@lapis-notes/lapis-plugin-docs 0.0.0-stage → 0.2.0

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.
Files changed (197) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +188 -2
  3. package/dist/docs/DocsBlockMarkdownEditor.svelte +37 -0
  4. package/dist/docs/DocsBlockMarkdownEditor.svelte.d.ts +13 -0
  5. package/dist/docs/DocsBlockRefPicker.svelte +75 -0
  6. package/dist/docs/DocsBlockRefPicker.svelte.d.ts +11 -0
  7. package/dist/docs/DocsBookEditor.svelte +141 -0
  8. package/dist/docs/DocsBookEditor.svelte.d.ts +10 -0
  9. package/dist/docs/DocsBookNavigation.svelte +838 -0
  10. package/dist/docs/DocsBookNavigation.svelte.d.ts +11 -0
  11. package/dist/docs/DocsBooksColumn.svelte +82 -0
  12. package/dist/docs/DocsBooksColumn.svelte.d.ts +10 -0
  13. package/dist/docs/DocsCardBackMatter.svelte +32 -0
  14. package/dist/docs/DocsCardBackMatter.svelte.d.ts +10 -0
  15. package/dist/docs/DocsCardBlockExtra.svelte +43 -0
  16. package/dist/docs/DocsCardBlockExtra.svelte.d.ts +9 -0
  17. package/dist/docs/DocsCardPreviewFlip.svelte +143 -0
  18. package/dist/docs/DocsCardPreviewFlip.svelte.d.ts +17 -0
  19. package/dist/docs/DocsCardPreviewStack.svelte +139 -0
  20. package/dist/docs/DocsCardPreviewStack.svelte.d.ts +13 -0
  21. package/dist/docs/DocsCardPrompt.svelte +33 -0
  22. package/dist/docs/DocsCardPrompt.svelte.d.ts +8 -0
  23. package/dist/docs/DocsCardReviewAnswer.svelte +45 -0
  24. package/dist/docs/DocsCardReviewAnswer.svelte.d.ts +8 -0
  25. package/dist/docs/DocsCardTagChips.svelte +19 -0
  26. package/dist/docs/DocsCardTagChips.svelte.d.ts +7 -0
  27. package/dist/docs/DocsCardsPanel.svelte +232 -0
  28. package/dist/docs/DocsCardsPanel.svelte.d.ts +17 -0
  29. package/dist/docs/DocsCardsReview.css +1050 -0
  30. package/dist/docs/DocsChapterFooter.svelte +248 -0
  31. package/dist/docs/DocsChapterFooter.svelte.d.ts +11 -0
  32. package/dist/docs/DocsChapterScrollRing.svelte +101 -0
  33. package/dist/docs/DocsChapterScrollRing.svelte.d.ts +6 -0
  34. package/dist/docs/DocsDisplayPanel.css +627 -0
  35. package/dist/docs/DocsDisplayPanel.svelte +78 -0
  36. package/dist/docs/DocsDisplayPanel.svelte.d.ts +16 -0
  37. package/dist/docs/DocsEditorPanel.css +325 -0
  38. package/dist/docs/DocsEditorPanel.svelte +570 -0
  39. package/dist/docs/DocsEditorPanel.svelte.d.ts +22 -0
  40. package/dist/docs/DocsFlipCard.svelte +68 -0
  41. package/dist/docs/DocsFlipCard.svelte.d.ts +8 -0
  42. package/dist/docs/DocsFolderDirectoryList.svelte +206 -0
  43. package/dist/docs/DocsFolderDirectoryList.svelte.d.ts +14 -0
  44. package/dist/docs/DocsFolderPanel.css +357 -0
  45. package/dist/docs/DocsFolderPanel.svelte +102 -0
  46. package/dist/docs/DocsFolderPanel.svelte.d.ts +17 -0
  47. package/dist/docs/DocsLinksPanel.css +61 -0
  48. package/dist/docs/DocsLinksPanel.svelte +55 -0
  49. package/dist/docs/DocsLinksPanel.svelte.d.ts +11 -0
  50. package/dist/docs/DocsMarkdownField.svelte +93 -0
  51. package/dist/docs/DocsMarkdownField.svelte.d.ts +11 -0
  52. package/dist/docs/DocsMarkdownPreview.svelte +278 -0
  53. package/dist/docs/DocsMarkdownPreview.svelte.d.ts +17 -0
  54. package/dist/docs/DocsReviewPanel.svelte +63 -0
  55. package/dist/docs/DocsReviewPanel.svelte.d.ts +11 -0
  56. package/dist/docs/DocsRichBlock.svelte +291 -0
  57. package/dist/docs/DocsRichBlock.svelte.d.ts +13 -0
  58. package/dist/docs/DocsRichDisplay.svelte +316 -0
  59. package/dist/docs/DocsRichDisplay.svelte.d.ts +14 -0
  60. package/dist/docs/DocsSlidesPreview.css +243 -0
  61. package/dist/docs/DocsSlidesPreview.svelte +206 -0
  62. package/dist/docs/DocsSlidesPreview.svelte.d.ts +10 -0
  63. package/dist/docs/DocsWorkspace.css +570 -0
  64. package/dist/docs/DocsWorkspace.svelte +831 -0
  65. package/dist/docs/DocsWorkspace.svelte.d.ts +48 -0
  66. package/dist/docs/DocsWorkspaceToolbar.svelte +211 -0
  67. package/dist/docs/DocsWorkspaceToolbar.svelte.d.ts +27 -0
  68. package/dist/docs/candidates/ChoiceMenu.svelte +114 -0
  69. package/dist/docs/candidates/ChoiceMenu.svelte.d.ts +10 -0
  70. package/dist/docs/candidates/FlipCardDeck.svelte +817 -0
  71. package/dist/docs/candidates/FlipCardDeck.svelte.d.ts +37 -0
  72. package/dist/docs/candidates/MarkdownFloatingToc.svelte +313 -0
  73. package/dist/docs/candidates/MarkdownFloatingToc.svelte.d.ts +14 -0
  74. package/dist/docs/candidates/SearchableChoicePicker.css +161 -0
  75. package/dist/docs/candidates/SearchableChoicePicker.svelte +208 -0
  76. package/dist/docs/candidates/SearchableChoicePicker.svelte.d.ts +19 -0
  77. package/dist/docs/candidates/StructuredTable.svelte +98 -0
  78. package/dist/docs/candidates/StructuredTable.svelte.d.ts +8 -0
  79. package/dist/docs/candidates/StructuredTableEditor.svelte +374 -0
  80. package/dist/docs/candidates/StructuredTableEditor.svelte.d.ts +12 -0
  81. package/dist/docs/candidates/TagEditor.svelte +31 -0
  82. package/dist/docs/candidates/TagEditor.svelte.d.ts +14 -0
  83. package/dist/docs/candidates/WorkspaceHistoryControls.svelte +112 -0
  84. package/dist/docs/candidates/WorkspaceHistoryControls.svelte.d.ts +8 -0
  85. package/dist/docs/candidates/markdown-floating-toc.css +151 -0
  86. package/dist/docs/candidates/markdown-floating-toc.d.ts +1 -0
  87. package/dist/docs/candidates/markdown-floating-toc.js +1 -0
  88. package/dist/docs/docs-anki-import.d.ts +34 -0
  89. package/dist/docs/docs-anki-import.js +516 -0
  90. package/dist/docs/docs-book-view-model.d.ts +9 -0
  91. package/dist/docs/docs-book-view-model.js +71 -0
  92. package/dist/docs/docs-card-answer-markdown.d.ts +1 -0
  93. package/dist/docs/docs-card-answer-markdown.js +63 -0
  94. package/dist/docs/docs-card-display.d.ts +10 -0
  95. package/dist/docs/docs-card-display.js +18 -0
  96. package/dist/docs/docs-display-focus.d.ts +7 -0
  97. package/dist/docs/docs-display-focus.js +42 -0
  98. package/dist/docs/docs-document-surface.d.ts +7 -0
  99. package/dist/docs/docs-document-surface.js +25 -0
  100. package/dist/docs/docs-folder-directory.d.ts +21 -0
  101. package/dist/docs/docs-folder-directory.js +56 -0
  102. package/dist/docs/docs-link-mode.d.ts +16 -0
  103. package/dist/docs/docs-link-mode.js +94 -0
  104. package/dist/docs/docs-workspace-helpers.d.ts +25 -0
  105. package/dist/docs/docs-workspace-helpers.js +195 -0
  106. package/dist/docs/docs.tokens.css +13 -0
  107. package/dist/docs/import-anki-package.d.ts +10 -0
  108. package/dist/docs/import-anki-package.js +30 -0
  109. package/dist/docs/lapis-markdown-extensions.d.ts +9 -0
  110. package/dist/docs/lapis-markdown-extensions.js +128 -0
  111. package/dist/docs/pin-reading-chrome.d.ts +4 -0
  112. package/dist/docs/pin-reading-chrome.js +32 -0
  113. package/dist/docs/sidebar/DocsFoldersSidebar.svelte +134 -0
  114. package/dist/docs/sidebar/DocsFoldersSidebar.svelte.d.ts +15 -0
  115. package/dist/docs/sidebar/DocsLinksSidebar.svelte +105 -0
  116. package/dist/docs/sidebar/DocsLinksSidebar.svelte.d.ts +11 -0
  117. package/dist/docs/sidebar/DocsSidebar.css +346 -0
  118. package/dist/docs/sidebar/DocsSidebar.svelte +430 -0
  119. package/dist/docs/sidebar/DocsSidebar.svelte.d.ts +18 -0
  120. package/dist/docs/sidebar/DocsSidebarHost.svelte +139 -0
  121. package/dist/docs/sidebar/DocsSidebarHost.svelte.d.ts +12 -0
  122. package/dist/docs/sidebar/DocsTagsSidebar.svelte +176 -0
  123. package/dist/docs/sidebar/DocsTagsSidebar.svelte.d.ts +12 -0
  124. package/dist/docs/sidebar/docs-book-note-tree.d.ts +23 -0
  125. package/dist/docs/sidebar/docs-book-note-tree.js +88 -0
  126. package/dist/docs/sidebar/docs-note-sort.d.ts +26 -0
  127. package/dist/docs/sidebar/docs-note-sort.js +54 -0
  128. package/dist/docs/sidebar/docs-sidebar-filters.d.ts +17 -0
  129. package/dist/docs/sidebar/docs-sidebar-filters.js +74 -0
  130. package/dist/docs/sidebar/docs-sidebar-ui-storage.d.ts +24 -0
  131. package/dist/docs/sidebar/docs-sidebar-ui-storage.js +87 -0
  132. package/dist/docs/sidebar/docs-source-folder-tree.d.ts +16 -0
  133. package/dist/docs/sidebar/docs-source-folder-tree.js +143 -0
  134. package/dist/docs/sidebar/docs-tag-tree.d.ts +17 -0
  135. package/dist/docs/sidebar/docs-tag-tree.js +84 -0
  136. package/dist/docs-cards-view.d.ts +18 -0
  137. package/dist/docs-cards-view.js +52 -0
  138. package/dist/docs-document-view.d.ts +24 -0
  139. package/dist/docs-document-view.js +127 -0
  140. package/dist/docs-document-workspace.svelte +215 -0
  141. package/dist/docs-document-workspace.svelte.d.ts +16 -0
  142. package/dist/docs-index.d.ts +5 -0
  143. package/dist/docs-index.js +49 -0
  144. package/dist/docs-path.d.ts +6 -0
  145. package/dist/docs-path.js +10 -0
  146. package/dist/docs-plugin.d.ts +21 -0
  147. package/dist/docs-plugin.js +140 -0
  148. package/dist/docs-repository.d.ts +15 -0
  149. package/dist/docs-repository.js +75 -0
  150. package/dist/docs-review-view.d.ts +18 -0
  151. package/dist/docs-review-view.js +52 -0
  152. package/dist/docs-settings-tab.d.ts +8 -0
  153. package/dist/docs-settings-tab.js +25 -0
  154. package/dist/docs-settings.d.ts +17 -0
  155. package/dist/docs-settings.js +38 -0
  156. package/dist/docs-view.d.ts +22 -0
  157. package/dist/docs-view.js +51 -0
  158. package/dist/docs-workspace.svelte +394 -0
  159. package/dist/docs-workspace.svelte.d.ts +15 -0
  160. package/dist/index.d.ts +12 -0
  161. package/dist/index.js +12 -0
  162. package/dist/model/book.d.ts +6 -0
  163. package/dist/model/book.js +101 -0
  164. package/dist/model/coding-runtime.d.ts +4 -0
  165. package/dist/model/coding-runtime.js +9 -0
  166. package/dist/model/docs-card-context.d.ts +32 -0
  167. package/dist/model/docs-card-context.js +89 -0
  168. package/dist/model/docs-card-preview-stack-motion.d.ts +51 -0
  169. package/dist/model/docs-card-preview-stack-motion.js +94 -0
  170. package/dist/model/docs-media-context.d.ts +9 -0
  171. package/dist/model/docs-media-context.js +18 -0
  172. package/dist/model/docs-note-list.d.ts +8 -0
  173. package/dist/model/docs-note-list.js +25 -0
  174. package/dist/model/docs-relative-media.d.ts +30 -0
  175. package/dist/model/docs-relative-media.js +116 -0
  176. package/dist/model/docs-review-constants.d.ts +4 -0
  177. package/dist/model/docs-review-constants.js +13 -0
  178. package/dist/model/fsrs.d.ts +14 -0
  179. package/dist/model/fsrs.js +153 -0
  180. package/dist/model/graph.d.ts +12 -0
  181. package/dist/model/graph.js +169 -0
  182. package/dist/model/index.d.ts +14 -0
  183. package/dist/model/index.js +14 -0
  184. package/dist/model/markdown-normalize.d.ts +11 -0
  185. package/dist/model/markdown-normalize.js +755 -0
  186. package/dist/model/markdown.d.ts +167 -0
  187. package/dist/model/markdown.js +365 -0
  188. package/dist/model/sample.d.ts +7 -0
  189. package/dist/model/sample.js +524 -0
  190. package/dist/model/types.d.ts +328 -0
  191. package/dist/model/types.js +1 -0
  192. package/dist/model/workspace.d.ts +6 -0
  193. package/dist/model/workspace.js +81 -0
  194. package/dist/storybook.css +52 -0
  195. package/dist/styles.css +1 -0
  196. package/manifest.json +11 -0
  197. package/package.json +147 -4
@@ -0,0 +1,5 @@
1
+ import type { IndexFieldDefinition, IndexProjectionRegistration } from "@lapis-notes/api/app-database";
2
+ export declare const DOCS_NOTE_PROJECTION_ID = "note";
3
+ export declare const DOCS_NOTE_PROJECTION_VERSION = 1;
4
+ export declare const DOCS_NOTE_PROJECTION_FIELDS: Record<string, IndexFieldDefinition>;
5
+ export declare function createDocsProjectionRegistration(docsRoot: string): IndexProjectionRegistration;
@@ -0,0 +1,49 @@
1
+ import { parseDocsNoteMarkdownFile } from "./model";
2
+ import { normalizeDocsRoot } from "./docs-settings";
3
+ export const DOCS_NOTE_PROJECTION_ID = "note";
4
+ export const DOCS_NOTE_PROJECTION_VERSION = 1;
5
+ export const DOCS_NOTE_PROJECTION_FIELDS = {
6
+ id: { type: "string", indexed: true },
7
+ title: { type: "string", indexed: true },
8
+ docType: { type: "string", indexed: true },
9
+ path: { type: "path", indexed: true },
10
+ tags: { type: "string", indexed: true, cardinality: "many" },
11
+ cardCount: { type: "number", indexed: false },
12
+ bookId: { type: "string", indexed: true },
13
+ };
14
+ export function createDocsProjectionRegistration(docsRoot) {
15
+ const root = normalizeDocsRoot(docsRoot);
16
+ return {
17
+ id: DOCS_NOTE_PROJECTION_ID,
18
+ version: DOCS_NOTE_PROJECTION_VERSION,
19
+ visibility: "public",
20
+ fields: DOCS_NOTE_PROJECTION_FIELDS,
21
+ source: {
22
+ extensions: ["doc.md"],
23
+ pathPrefix: root,
24
+ },
25
+ project({ file, content }) {
26
+ const note = parseDocsNoteMarkdownFile({ path: file.path, content });
27
+ if (!note)
28
+ return { rows: [], edges: [] };
29
+ return {
30
+ rows: [
31
+ {
32
+ id: note.id,
33
+ kind: "note",
34
+ data: {
35
+ id: note.id,
36
+ title: note.title,
37
+ docType: note.docType,
38
+ path: note.sourcePath,
39
+ tags: note.tags,
40
+ cardCount: note.cards.length,
41
+ bookId: note.bookId ?? "",
42
+ },
43
+ },
44
+ ],
45
+ edges: [],
46
+ };
47
+ },
48
+ };
49
+ }
@@ -0,0 +1,6 @@
1
+ export declare const DOCS_DOCUMENT_VIEW_TYPE = "docs-document";
2
+ export declare const DOCS_FILENAME_PATTERNS: readonly ["*.doc.md"];
3
+ export declare const DOCS_EXTENSIONS: readonly ["doc.md"];
4
+ export declare const DOCS_FILE_GLOB = "**/*.doc.md";
5
+ export declare function isDocsDocumentPath(path: string): boolean;
6
+ export declare function docsDocumentExtension(path: string): string;
@@ -0,0 +1,10 @@
1
+ export const DOCS_DOCUMENT_VIEW_TYPE = "docs-document";
2
+ export const DOCS_FILENAME_PATTERNS = ["*.doc.md"];
3
+ export const DOCS_EXTENSIONS = ["doc.md"];
4
+ export const DOCS_FILE_GLOB = "**/*.doc.md";
5
+ export function isDocsDocumentPath(path) {
6
+ return /\.doc\.md$/i.test(path.replaceAll("\\", "/"));
7
+ }
8
+ export function docsDocumentExtension(path) {
9
+ return isDocsDocumentPath(path) ? "doc.md" : "";
10
+ }
@@ -0,0 +1,21 @@
1
+ import { Plugin, type App, type PluginManifest } from "@lapis-notes/api";
2
+ import { DocsRepository } from "./docs-repository";
3
+ import { type DocsPluginSettings, type DocsPluginSettingsPatch } from "./docs-settings";
4
+ export declare const DOCS_PLUGIN_ID = "docs";
5
+ export declare class DocsPlugin extends Plugin {
6
+ readonly repository: DocsRepository;
7
+ clock: () => Date;
8
+ private data;
9
+ private saveQueue;
10
+ private readonly settingsListeners;
11
+ constructor(app: App, pluginManifest?: PluginManifest);
12
+ onload(): Promise<void>;
13
+ getSettings(): DocsPluginSettings;
14
+ updateSettings(patch: DocsPluginSettingsPatch): Promise<void>;
15
+ subscribeSettings(listener: (settings: DocsPluginSettings) => void): () => void;
16
+ openDocs(): Promise<void>;
17
+ openCards(): Promise<void>;
18
+ openReview(scope: "note" | "workspace"): Promise<void>;
19
+ private focusDocumentPanel;
20
+ }
21
+ export default DocsPlugin;
@@ -0,0 +1,140 @@
1
+ import { Plugin, } from "@lapis-notes/api";
2
+ import { requestDocsDocumentSurface } from "./docs/docs-document-surface";
3
+ import { createDocsProjectionRegistration } from "./docs-index";
4
+ import { DocsDocumentView } from "./docs-document-view";
5
+ import { DOCS_DOCUMENT_VIEW_TYPE, DOCS_EXTENSIONS, DOCS_FILENAME_PATTERNS, isDocsDocumentPath, } from "./docs-path";
6
+ import { DocsRepository, createVaultDocsFileStore } from "./docs-repository";
7
+ import { cloneDocsPluginSettings, migrateDocsPluginData, patchDocsPluginSettings, } from "./docs-settings";
8
+ import { DocsSettingsTab } from "./docs-settings-tab";
9
+ import { DOCS_CARDS_VIEW_TYPE, DocsCardsView } from "./docs-cards-view";
10
+ import { DOCS_REVIEW_VIEW_TYPE, DocsReviewView } from "./docs-review-view";
11
+ import { DOCS_VIEW_TYPE, DocsView } from "./docs-view";
12
+ export const DOCS_PLUGIN_ID = "docs";
13
+ const MANIFEST = {
14
+ id: DOCS_PLUGIN_ID,
15
+ name: "Docs",
16
+ version: "0.1.0",
17
+ minAppVersion: "0.0.1",
18
+ description: "Vault-native Docs notes, books, cards, and FSRS review.",
19
+ author: "Lapis Notes",
20
+ };
21
+ export class DocsPlugin extends Plugin {
22
+ repository;
23
+ clock = () => new Date();
24
+ data = migrateDocsPluginData(null);
25
+ saveQueue = Promise.resolve();
26
+ settingsListeners = new Set();
27
+ constructor(app, pluginManifest = MANIFEST) {
28
+ super(app, pluginManifest);
29
+ this.repository = new DocsRepository(createVaultDocsFileStore(app));
30
+ }
31
+ async onload() {
32
+ this.data = migrateDocsPluginData(await this.loadData());
33
+ this.registerIndexProjection(createDocsProjectionRegistration(this.getSettings().docsRoot));
34
+ this.registerSidebarView(DOCS_VIEW_TYPE, (leaf) => new DocsView(leaf, this.repository, () => this.clock(), {
35
+ get: () => this.getSettings(),
36
+ subscribe: (listener) => this.subscribeSettings(listener),
37
+ }), { side: "left", title: "Docs", icon: "book-open" });
38
+ this.registerView(DOCS_DOCUMENT_VIEW_TYPE, (leaf) => new DocsDocumentView(leaf, this.repository, () => this.clock()));
39
+ this.registerView(DOCS_CARDS_VIEW_TYPE, (leaf) => new DocsCardsView(leaf, this.repository, () => this.clock(), {
40
+ get: () => this.getSettings(),
41
+ subscribe: (listener) => this.subscribeSettings(listener),
42
+ }));
43
+ this.registerView(DOCS_REVIEW_VIEW_TYPE, (leaf) => new DocsReviewView(leaf, this.repository, () => this.clock(), {
44
+ get: () => this.getSettings(),
45
+ subscribe: (listener) => this.subscribeSettings(listener),
46
+ }));
47
+ this.registerEditorView({
48
+ id: DOCS_DOCUMENT_VIEW_TYPE,
49
+ viewType: DOCS_DOCUMENT_VIEW_TYPE,
50
+ label: "Docs",
51
+ filenamePatterns: [...DOCS_FILENAME_PATTERNS],
52
+ priority: "exclusive",
53
+ });
54
+ this.registerExtensions([...DOCS_EXTENSIONS], DOCS_DOCUMENT_VIEW_TYPE);
55
+ this.addCommand({
56
+ id: "open",
57
+ name: "Open docs",
58
+ callback: () => void this.openDocs(),
59
+ });
60
+ this.addCommand({
61
+ id: "open-cards",
62
+ name: "Open cards",
63
+ callback: () => void this.openCards(),
64
+ });
65
+ this.addCommand({
66
+ id: "review-note",
67
+ name: "Review due cards (note)",
68
+ callback: () => void this.openReview("note"),
69
+ });
70
+ this.addCommand({
71
+ id: "review-workspace",
72
+ name: "Review due cards (workspace)",
73
+ callback: () => void this.openReview("workspace"),
74
+ });
75
+ this.addRibbonIcon("book-open", "Open Docs", () => void this.openDocs());
76
+ this.addSettingTab(new DocsSettingsTab(this.app, this));
77
+ }
78
+ getSettings() {
79
+ return cloneDocsPluginSettings(this.data.settings);
80
+ }
81
+ async updateSettings(patch) {
82
+ this.data = {
83
+ ...this.data,
84
+ settings: patchDocsPluginSettings(this.data.settings, patch),
85
+ };
86
+ const snapshot = {
87
+ version: this.data.version,
88
+ settings: this.getSettings(),
89
+ };
90
+ this.saveQueue = this.saveQueue
91
+ .catch(() => undefined)
92
+ .then(() => this.saveData(snapshot));
93
+ await this.saveQueue;
94
+ for (const listener of this.settingsListeners) {
95
+ listener(cloneDocsPluginSettings(snapshot.settings));
96
+ }
97
+ }
98
+ subscribeSettings(listener) {
99
+ this.settingsListeners.add(listener);
100
+ return () => this.settingsListeners.delete(listener);
101
+ }
102
+ async openDocs() {
103
+ const existing = this.app.workspace.getLeavesOfType(DOCS_VIEW_TYPE)[0];
104
+ const leaf = existing ?? this.app.workspace.ensureSideLeaf(DOCS_VIEW_TYPE, "left");
105
+ if (leaf.view.getViewType() !== DOCS_VIEW_TYPE) {
106
+ await leaf.setViewState({ type: DOCS_VIEW_TYPE, active: true });
107
+ }
108
+ this.app.workspace.activateLeaf(leaf, {
109
+ focusRootHost: false,
110
+ source: "api",
111
+ operation: "open-docs",
112
+ });
113
+ await this.app.workspace.revealLeaf(leaf);
114
+ }
115
+ async openCards() {
116
+ await this.focusDocumentPanel("cards");
117
+ }
118
+ async openReview(scope) {
119
+ await this.focusDocumentPanel("review", scope);
120
+ }
121
+ async focusDocumentPanel(panel, reviewScope) {
122
+ const file = this.app.workspace.getActiveFile();
123
+ const documentLeaf = this.app.workspace
124
+ .getLeavesOfType(DOCS_DOCUMENT_VIEW_TYPE)
125
+ .find((leaf) => leaf.view instanceof DocsDocumentView && leaf.view.file);
126
+ const path = file && isDocsDocumentPath(file.path)
127
+ ? file.path
128
+ : documentLeaf?.view instanceof DocsDocumentView
129
+ ? documentLeaf.view.file?.path
130
+ : undefined;
131
+ if (!path)
132
+ return;
133
+ requestDocsDocumentSurface(path, {
134
+ panel,
135
+ ...(reviewScope ? { reviewScope } : {}),
136
+ });
137
+ await this.app.workspace.openLinkText(path, "", false);
138
+ }
139
+ }
140
+ export default DocsPlugin;
@@ -0,0 +1,15 @@
1
+ import type { App } from "@lapis-notes/api";
2
+ import { type DocsNote } from "./model";
3
+ export type DocsFileStore = {
4
+ read(path: string): Promise<string>;
5
+ write(path: string, content: string): Promise<void>;
6
+ listMarkdown(root: string): Promise<string[]>;
7
+ };
8
+ export declare function createVaultDocsFileStore(app: App): DocsFileStore;
9
+ export declare class DocsRepository {
10
+ private readonly store;
11
+ constructor(store: DocsFileStore);
12
+ listNotes(docsRoot: string): Promise<DocsNote[]>;
13
+ readNote(path: string): Promise<DocsNote | null>;
14
+ writeNote(note: DocsNote): Promise<void>;
15
+ }
@@ -0,0 +1,75 @@
1
+ import { isDocsDocumentPath } from "./docs-path";
2
+ import { docsNoteToMarkdown, parseDocsNoteMarkdownFile, } from "./model";
3
+ function parentFolder(path) {
4
+ const index = path.lastIndexOf("/");
5
+ return index > 0 ? path.slice(0, index) : null;
6
+ }
7
+ export function createVaultDocsFileStore(app) {
8
+ return {
9
+ async read(path) {
10
+ const file = app.vault.getFileByPath(path);
11
+ if (!file)
12
+ throw new Error(`Missing docs file: ${path}`);
13
+ return app.vault.cachedRead(file);
14
+ },
15
+ async write(path, content) {
16
+ const file = app.vault.getFileByPath(path);
17
+ if (file) {
18
+ await app.vault.modify(file, content);
19
+ return;
20
+ }
21
+ const folder = parentFolder(path);
22
+ if (folder)
23
+ await app.vault.mkpath(folder);
24
+ await app.vault.create(path, content);
25
+ },
26
+ async listMarkdown(root) {
27
+ const prefix = root.replace(/^\/+|\/+$/g, "");
28
+ return app.vault
29
+ .getFiles()
30
+ .map((file) => file.path)
31
+ .filter((path) => {
32
+ if (!isDocsDocumentPath(path))
33
+ return false;
34
+ return (path === `${prefix}.doc.md` ||
35
+ path.startsWith(`${prefix}/`) ||
36
+ !prefix);
37
+ })
38
+ .sort();
39
+ },
40
+ };
41
+ }
42
+ export class DocsRepository {
43
+ store;
44
+ constructor(store) {
45
+ this.store = store;
46
+ }
47
+ async listNotes(docsRoot) {
48
+ const paths = await this.store.listMarkdown(docsRoot);
49
+ const notes = [];
50
+ for (const path of paths) {
51
+ try {
52
+ const content = await this.store.read(path);
53
+ const parsed = parseDocsNoteMarkdownFile({ path, content });
54
+ if (parsed)
55
+ notes.push(parsed);
56
+ }
57
+ catch {
58
+ // Skip unreadable or non-docs markdown.
59
+ }
60
+ }
61
+ return notes;
62
+ }
63
+ async readNote(path) {
64
+ try {
65
+ const content = await this.store.read(path);
66
+ return parseDocsNoteMarkdownFile({ path, content });
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ }
72
+ async writeNote(note) {
73
+ await this.store.write(note.sourcePath, docsNoteToMarkdown(note));
74
+ }
75
+ }
@@ -0,0 +1,18 @@
1
+ import { View, type WorkspaceLeaf } from "@lapis-notes/api";
2
+ import type { DocsRepository } from "./docs-repository";
3
+ import type { DocsSettingsSource } from "./docs-view";
4
+ export declare const DOCS_REVIEW_VIEW_TYPE = "docs-review";
5
+ export declare class DocsReviewView extends View {
6
+ private readonly repository;
7
+ private readonly clock;
8
+ private readonly settings?;
9
+ private component;
10
+ constructor(leaf: WorkspaceLeaf, repository: DocsRepository, clock?: () => Date, settings?: DocsSettingsSource | undefined);
11
+ getViewType(): string;
12
+ getDisplayText(): string;
13
+ getIcon(): string;
14
+ protected onOpen(): Promise<void>;
15
+ protected onClose(): Promise<void>;
16
+ onload(): void;
17
+ onunload(): void;
18
+ }
@@ -0,0 +1,52 @@
1
+ import { View } from "@lapis-notes/api";
2
+ import { mount, unmount } from "svelte";
3
+ import DocsWorkspace from "./docs-workspace.svelte";
4
+ export const DOCS_REVIEW_VIEW_TYPE = "docs-review";
5
+ export class DocsReviewView extends View {
6
+ repository;
7
+ clock;
8
+ settings;
9
+ component = null;
10
+ constructor(leaf, repository, clock = () => new Date(), settings) {
11
+ super(leaf);
12
+ this.repository = repository;
13
+ this.clock = clock;
14
+ this.settings = settings;
15
+ }
16
+ getViewType() {
17
+ return DOCS_REVIEW_VIEW_TYPE;
18
+ }
19
+ getDisplayText() {
20
+ return "Docs Review";
21
+ }
22
+ getIcon() {
23
+ return "brain";
24
+ }
25
+ onOpen() {
26
+ return Promise.resolve();
27
+ }
28
+ onClose() {
29
+ return Promise.resolve();
30
+ }
31
+ onload() {
32
+ this.containerEl.style.width = "100%";
33
+ this.containerEl.style.height = "100%";
34
+ this.containerEl.style.minHeight = "0";
35
+ this.containerEl.style.overflow = "hidden";
36
+ this.component = mount(DocsWorkspace, {
37
+ target: this.containerEl,
38
+ props: {
39
+ app: this.app,
40
+ repository: this.repository,
41
+ clock: this.clock,
42
+ settingsSource: this.settings,
43
+ mode: "review",
44
+ },
45
+ });
46
+ }
47
+ onunload() {
48
+ if (this.component)
49
+ void unmount(this.component);
50
+ this.component = null;
51
+ }
52
+ }
@@ -0,0 +1,8 @@
1
+ import { PluginSettingTab, type App } from "@lapis-notes/api";
2
+ import type { DocsPlugin } from "./docs-plugin";
3
+ export declare class DocsSettingsTab extends PluginSettingTab {
4
+ private readonly docsPlugin;
5
+ constructor(app: App, docsPlugin: DocsPlugin);
6
+ display(): void;
7
+ private update;
8
+ }
@@ -0,0 +1,25 @@
1
+ import { Notice, PluginSettingTab, Setting } from "@lapis-notes/api";
2
+ export class DocsSettingsTab extends PluginSettingTab {
3
+ docsPlugin;
4
+ constructor(app, docsPlugin) {
5
+ super(app, docsPlugin);
6
+ this.docsPlugin = docsPlugin;
7
+ }
8
+ display() {
9
+ const settings = this.docsPlugin.getSettings();
10
+ this.containerEl.empty();
11
+ new Setting(this.containerEl)
12
+ .setName("Docs root")
13
+ .setDesc("Vault folder that holds Docs notes.")
14
+ .addText((text) => text.setValue(settings.docsRoot).onChange((docsRoot) => void this.update({ docsRoot })));
15
+ }
16
+ async update(patch) {
17
+ try {
18
+ await this.docsPlugin.updateSettings(patch);
19
+ this.display();
20
+ }
21
+ catch (error) {
22
+ new Notice(error instanceof Error ? error.message : String(error));
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,17 @@
1
+ export declare const DOCS_PLUGIN_DATA_VERSION: 1;
2
+ export type DocsPluginSettings = {
3
+ docsRoot: string;
4
+ };
5
+ export type DocsPluginSettingsPatch = {
6
+ docsRoot?: string;
7
+ };
8
+ export type DocsPluginDataV1 = {
9
+ version: typeof DOCS_PLUGIN_DATA_VERSION;
10
+ settings: DocsPluginSettings;
11
+ };
12
+ export declare const DEFAULT_DOCS_PLUGIN_SETTINGS: DocsPluginSettings;
13
+ export declare function normalizeDocsRoot(value: unknown): string;
14
+ export declare function cloneDocsPluginSettings(settings: DocsPluginSettings): DocsPluginSettings;
15
+ export declare function mergeDocsPluginSettings(value: unknown): DocsPluginSettings;
16
+ export declare function patchDocsPluginSettings(settings: DocsPluginSettings, patch: DocsPluginSettingsPatch): DocsPluginSettings;
17
+ export declare function migrateDocsPluginData(value: unknown): DocsPluginDataV1;
@@ -0,0 +1,38 @@
1
+ export const DOCS_PLUGIN_DATA_VERSION = 1;
2
+ export const DEFAULT_DOCS_PLUGIN_SETTINGS = Object.freeze({
3
+ docsRoot: "docs",
4
+ });
5
+ function recordValue(value) {
6
+ return value && typeof value === "object" && !Array.isArray(value)
7
+ ? value
8
+ : {};
9
+ }
10
+ export function normalizeDocsRoot(value) {
11
+ if (typeof value !== "string")
12
+ return DEFAULT_DOCS_PLUGIN_SETTINGS.docsRoot;
13
+ const trimmed = value.trim().replace(/^\/+|\/+$/g, "");
14
+ return trimmed || DEFAULT_DOCS_PLUGIN_SETTINGS.docsRoot;
15
+ }
16
+ export function cloneDocsPluginSettings(settings) {
17
+ return { docsRoot: settings.docsRoot };
18
+ }
19
+ export function mergeDocsPluginSettings(value) {
20
+ const stored = recordValue(value);
21
+ return {
22
+ docsRoot: normalizeDocsRoot(stored.docsRoot),
23
+ };
24
+ }
25
+ export function patchDocsPluginSettings(settings, patch) {
26
+ return {
27
+ docsRoot: patch.docsRoot !== undefined
28
+ ? normalizeDocsRoot(patch.docsRoot)
29
+ : settings.docsRoot,
30
+ };
31
+ }
32
+ export function migrateDocsPluginData(value) {
33
+ const stored = recordValue(value);
34
+ return {
35
+ version: DOCS_PLUGIN_DATA_VERSION,
36
+ settings: mergeDocsPluginSettings(stored.settings ?? stored),
37
+ };
38
+ }
@@ -0,0 +1,22 @@
1
+ import { View, type WorkspaceLeaf } from "@lapis-notes/api";
2
+ import type { DocsRepository } from "./docs-repository";
3
+ import type { DocsPluginSettings } from "./docs-settings";
4
+ export declare const DOCS_VIEW_TYPE = "docs";
5
+ export type DocsSettingsSource = {
6
+ get(): DocsPluginSettings;
7
+ subscribe(listener: (settings: DocsPluginSettings) => void): () => void;
8
+ };
9
+ export declare class DocsView extends View {
10
+ private readonly repository;
11
+ private readonly clock;
12
+ private readonly settings?;
13
+ private component;
14
+ constructor(leaf: WorkspaceLeaf, repository: DocsRepository, clock?: () => Date, settings?: DocsSettingsSource | undefined);
15
+ getViewType(): string;
16
+ getDisplayText(): string;
17
+ getIcon(): string;
18
+ protected onOpen(): Promise<void>;
19
+ protected onClose(): Promise<void>;
20
+ onload(): void;
21
+ onunload(): void;
22
+ }
@@ -0,0 +1,51 @@
1
+ import { View } from "@lapis-notes/api";
2
+ import { mount, unmount } from "svelte";
3
+ import DocsSidebarHost from "./docs/sidebar/DocsSidebarHost.svelte";
4
+ export const DOCS_VIEW_TYPE = "docs";
5
+ export class DocsView extends View {
6
+ repository;
7
+ clock;
8
+ settings;
9
+ component = null;
10
+ constructor(leaf, repository, clock = () => new Date(), settings) {
11
+ super(leaf);
12
+ this.repository = repository;
13
+ this.clock = clock;
14
+ this.settings = settings;
15
+ }
16
+ getViewType() {
17
+ return DOCS_VIEW_TYPE;
18
+ }
19
+ getDisplayText() {
20
+ return "Docs";
21
+ }
22
+ getIcon() {
23
+ return "book-open";
24
+ }
25
+ onOpen() {
26
+ return Promise.resolve();
27
+ }
28
+ onClose() {
29
+ return Promise.resolve();
30
+ }
31
+ onload() {
32
+ this.containerEl.style.width = "100%";
33
+ this.containerEl.style.height = "100%";
34
+ this.containerEl.style.minHeight = "0";
35
+ this.containerEl.style.overflow = "hidden";
36
+ this.component = mount(DocsSidebarHost, {
37
+ target: this.containerEl,
38
+ props: {
39
+ app: this.app,
40
+ repository: this.repository,
41
+ clock: this.clock,
42
+ docsRoot: this.settings?.get().docsRoot ?? "docs",
43
+ },
44
+ });
45
+ }
46
+ onunload() {
47
+ if (this.component)
48
+ void unmount(this.component);
49
+ this.component = null;
50
+ }
51
+ }