@desktalk/miniapp-note 0.1.0-alpha.1
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/LICENSE +21 -0
- package/dist/backend.d.ts +5 -0
- package/dist/backend.d.ts.map +1 -0
- package/dist/backend.js +235 -0
- package/dist/backend.js.map +7 -0
- package/dist/components/NoteActions.d.ts +19 -0
- package/dist/components/NoteActions.d.ts.map +1 -0
- package/dist/components/NoteEditor.d.ts +29 -0
- package/dist/components/NoteEditor.d.ts.map +1 -0
- package/dist/components/NoteList.d.ts +12 -0
- package/dist/components/NoteList.d.ts.map +1 -0
- package/dist/frontend.d.ts +3 -0
- package/dist/frontend.d.ts.map +1 -0
- package/dist/frontend.js +3795 -0
- package/dist/frontend.js.map +7 -0
- package/dist/i18n/manifest.json +6 -0
- package/dist/lib/frontmatter.d.ts +13 -0
- package/dist/lib/frontmatter.d.ts.map +1 -0
- package/dist/lib/helpers.d.ts +8 -0
- package/dist/lib/helpers.d.ts.map +1 -0
- package/dist/meta.json +3 -0
- package/dist/types.d.ts +23 -0
- package/dist/types.d.ts.map +1 -0
- package/icons/miniapp-note-icon.png +0 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DeskTalk contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { MiniAppManifest, MiniAppContext, MiniAppBackendActivation } from '@desktalk/sdk';
|
|
2
|
+
export declare const manifest: MiniAppManifest;
|
|
3
|
+
export declare function activate(ctx: MiniAppContext): MiniAppBackendActivation;
|
|
4
|
+
export declare function deactivate(): void;
|
|
5
|
+
//# sourceMappingURL=backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AAO/F,eAAO,MAAM,QAAQ,EAAE,eAMtB,CAAC;AAIF,wBAAgB,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,wBAAwB,CAqOtE;AAED,wBAAgB,UAAU,IAAI,IAAI,CAEjC"}
|
package/dist/backend.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// src/lib/frontmatter.ts
|
|
2
|
+
var FM_REGEX = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
3
|
+
function parseFrontMatter(raw) {
|
|
4
|
+
const match = raw.match(FM_REGEX);
|
|
5
|
+
if (!match) {
|
|
6
|
+
const heading = raw.match(/^#\s+(.+)$/m);
|
|
7
|
+
return { title: heading ? heading[1].trim() : "Untitled", tags: [], created: null, body: raw };
|
|
8
|
+
}
|
|
9
|
+
const yaml = match[1];
|
|
10
|
+
const body = match[2];
|
|
11
|
+
let title = "Untitled";
|
|
12
|
+
let tags = [];
|
|
13
|
+
let created = null;
|
|
14
|
+
let inTags = false;
|
|
15
|
+
for (const line of yaml.split("\n")) {
|
|
16
|
+
const trimmed = line.trim();
|
|
17
|
+
if (trimmed.startsWith("title:")) {
|
|
18
|
+
title = trimmed.slice(6).trim().replace(/^["']|["']$/g, "");
|
|
19
|
+
inTags = false;
|
|
20
|
+
} else if (trimmed.startsWith("created:")) {
|
|
21
|
+
created = trimmed.slice(8).trim();
|
|
22
|
+
inTags = false;
|
|
23
|
+
} else if (trimmed.startsWith("tags:")) {
|
|
24
|
+
inTags = true;
|
|
25
|
+
const inline = trimmed.slice(5).trim();
|
|
26
|
+
if (inline.startsWith("[") && inline.endsWith("]")) {
|
|
27
|
+
tags = inline.slice(1, -1).split(",").map((t) => t.trim().replace(/^["']|["']$/g, "")).filter(Boolean);
|
|
28
|
+
inTags = false;
|
|
29
|
+
}
|
|
30
|
+
} else if (inTags && trimmed.startsWith("- ")) {
|
|
31
|
+
tags.push(
|
|
32
|
+
trimmed.slice(2).trim().replace(/^["']|["']$/g, "")
|
|
33
|
+
);
|
|
34
|
+
} else {
|
|
35
|
+
inTags = false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return { title, tags, created, body };
|
|
39
|
+
}
|
|
40
|
+
function serializeFrontMatter(title, tags, created, body) {
|
|
41
|
+
const lines = ["---", `title: "${title.replace(/"/g, '\\"')}"`];
|
|
42
|
+
if (tags.length > 0) {
|
|
43
|
+
lines.push("tags:");
|
|
44
|
+
for (const tag of tags) lines.push(` - ${tag}`);
|
|
45
|
+
}
|
|
46
|
+
lines.push(`created: ${created}`, "---", "", body);
|
|
47
|
+
return lines.join("\n");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/lib/helpers.ts
|
|
51
|
+
function slugify(text) {
|
|
52
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "untitled";
|
|
53
|
+
}
|
|
54
|
+
function preview(body, maxLen = 100) {
|
|
55
|
+
const text = body.replace(/^#+\s+/gm, "").replace(/[*_`~\[\]()>]/g, "").trim();
|
|
56
|
+
return text.length > maxLen ? text.slice(0, maxLen) + "\u2026" : text;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/backend.ts
|
|
60
|
+
var manifest = {
|
|
61
|
+
id: "note",
|
|
62
|
+
name: "Note",
|
|
63
|
+
icon: "\u{1F5D2}\uFE0F",
|
|
64
|
+
version: "0.1.0",
|
|
65
|
+
description: "Markdown note-taking with tags and YAML front matter"
|
|
66
|
+
};
|
|
67
|
+
function activate(ctx) {
|
|
68
|
+
ctx.logger.info("Note MiniApp activated");
|
|
69
|
+
function joinPath(...parts) {
|
|
70
|
+
return parts.filter(Boolean).join("/").replace(/\/+/g, "/");
|
|
71
|
+
}
|
|
72
|
+
function appPath(relPath = ".") {
|
|
73
|
+
return relPath === "." ? joinPath(".data", manifest.id) : joinPath(".data", manifest.id, relPath);
|
|
74
|
+
}
|
|
75
|
+
async function walkMarkdown(dir) {
|
|
76
|
+
const exists = await ctx.fs.exists(appPath(dir));
|
|
77
|
+
if (!exists) return [];
|
|
78
|
+
const entries = await ctx.fs.readDir(appPath(dir));
|
|
79
|
+
const paths = [];
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
const entryPath = dir === "." ? entry.name : joinPath(dir, entry.name);
|
|
82
|
+
if (entry.type === "directory") {
|
|
83
|
+
paths.push(...await walkMarkdown(entryPath));
|
|
84
|
+
} else if (entry.type === "file" && entry.name.endsWith(".md")) {
|
|
85
|
+
paths.push(entryPath);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return paths;
|
|
89
|
+
}
|
|
90
|
+
function pathToId(relPath) {
|
|
91
|
+
return relPath.replace(/\.md$/, "");
|
|
92
|
+
}
|
|
93
|
+
async function scanNotes(tagFilter) {
|
|
94
|
+
const dirExists = await ctx.fs.exists(appPath("."));
|
|
95
|
+
if (!dirExists) {
|
|
96
|
+
await ctx.fs.mkdir(appPath("."));
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
const mdPaths = await walkMarkdown(".");
|
|
100
|
+
const metas = [];
|
|
101
|
+
for (const relPath of mdPaths) {
|
|
102
|
+
try {
|
|
103
|
+
const raw = await ctx.fs.readFile(appPath(relPath));
|
|
104
|
+
const stat = await ctx.fs.stat(appPath(relPath));
|
|
105
|
+
const fm = parseFrontMatter(raw);
|
|
106
|
+
if (tagFilter && !fm.tags.includes(tagFilter)) continue;
|
|
107
|
+
metas.push({
|
|
108
|
+
id: pathToId(relPath),
|
|
109
|
+
title: fm.title,
|
|
110
|
+
tags: fm.tags,
|
|
111
|
+
createdAt: fm.created ?? stat.createdAt,
|
|
112
|
+
updatedAt: stat.modifiedAt,
|
|
113
|
+
preview: preview(fm.body)
|
|
114
|
+
});
|
|
115
|
+
} catch {
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
metas.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
119
|
+
return metas;
|
|
120
|
+
}
|
|
121
|
+
ctx.messaging.onCommand(
|
|
122
|
+
"notes.list",
|
|
123
|
+
async (req) => scanNotes(req?.tag)
|
|
124
|
+
);
|
|
125
|
+
ctx.messaging.onCommand("notes.get", async (req) => {
|
|
126
|
+
const filename = `${req.id}.md`;
|
|
127
|
+
const raw = await ctx.fs.readFile(appPath(filename));
|
|
128
|
+
const stat = await ctx.fs.stat(appPath(filename));
|
|
129
|
+
const fm = parseFrontMatter(raw);
|
|
130
|
+
return {
|
|
131
|
+
id: req.id,
|
|
132
|
+
title: fm.title,
|
|
133
|
+
tags: fm.tags,
|
|
134
|
+
content: raw,
|
|
135
|
+
createdAt: fm.created ?? stat.createdAt,
|
|
136
|
+
updatedAt: stat.modifiedAt
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
ctx.messaging.onCommand("notes.create", async (req) => {
|
|
140
|
+
const title = req?.title || "Untitled";
|
|
141
|
+
const tags = req?.tags || [];
|
|
142
|
+
const body = req?.content || "";
|
|
143
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
144
|
+
const id = req?.path || slugify(title) + "-" + Date.now().toString(36);
|
|
145
|
+
const filename = `${id}.md`;
|
|
146
|
+
const content = serializeFrontMatter(title, tags, now, body);
|
|
147
|
+
await ctx.fs.writeFile(appPath(filename), content);
|
|
148
|
+
const stat = await ctx.fs.stat(appPath(filename));
|
|
149
|
+
ctx.logger.info(`Created note: ${id}`);
|
|
150
|
+
return { id, title, tags, content, createdAt: now, updatedAt: stat.modifiedAt };
|
|
151
|
+
});
|
|
152
|
+
ctx.messaging.onCommand(
|
|
153
|
+
"notes.update",
|
|
154
|
+
async (req) => {
|
|
155
|
+
const filename = `${req.id}.md`;
|
|
156
|
+
const existing = await ctx.fs.readFile(appPath(filename));
|
|
157
|
+
const oldFm = parseFrontMatter(existing);
|
|
158
|
+
const stat = await ctx.fs.stat(appPath(filename));
|
|
159
|
+
let title;
|
|
160
|
+
let tags;
|
|
161
|
+
let body;
|
|
162
|
+
if (req.content !== void 0) {
|
|
163
|
+
const newFm = parseFrontMatter(req.content);
|
|
164
|
+
title = newFm.title;
|
|
165
|
+
tags = req.tags ?? newFm.tags;
|
|
166
|
+
body = newFm.body;
|
|
167
|
+
} else {
|
|
168
|
+
title = oldFm.title;
|
|
169
|
+
tags = req.tags ?? oldFm.tags;
|
|
170
|
+
body = oldFm.body;
|
|
171
|
+
}
|
|
172
|
+
const created = oldFm.created ?? stat.createdAt;
|
|
173
|
+
const content = serializeFrontMatter(title, tags, created, body);
|
|
174
|
+
await ctx.fs.writeFile(appPath(filename), content);
|
|
175
|
+
const newStat = await ctx.fs.stat(appPath(filename));
|
|
176
|
+
ctx.logger.info(`Updated note: ${req.id}`);
|
|
177
|
+
return {
|
|
178
|
+
id: req.id,
|
|
179
|
+
title,
|
|
180
|
+
tags,
|
|
181
|
+
content,
|
|
182
|
+
createdAt: created,
|
|
183
|
+
updatedAt: newStat.modifiedAt
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
ctx.messaging.onCommand("notes.delete", async (req) => {
|
|
188
|
+
await ctx.fs.deleteFile(appPath(`${req.id}.md`));
|
|
189
|
+
ctx.logger.info(`Deleted note: ${req.id}`);
|
|
190
|
+
});
|
|
191
|
+
ctx.messaging.onCommand("notes.search", async (req) => {
|
|
192
|
+
const q = (req.query || "").toLowerCase();
|
|
193
|
+
if (!q) return scanNotes();
|
|
194
|
+
const mdPaths = await walkMarkdown(".");
|
|
195
|
+
const results = [];
|
|
196
|
+
for (const relPath of mdPaths) {
|
|
197
|
+
try {
|
|
198
|
+
const raw = await ctx.fs.readFile(appPath(relPath));
|
|
199
|
+
if (!raw.toLowerCase().includes(q)) continue;
|
|
200
|
+
const stat = await ctx.fs.stat(appPath(relPath));
|
|
201
|
+
const fm = parseFrontMatter(raw);
|
|
202
|
+
results.push({
|
|
203
|
+
id: pathToId(relPath),
|
|
204
|
+
title: fm.title,
|
|
205
|
+
tags: fm.tags,
|
|
206
|
+
createdAt: fm.created ?? stat.createdAt,
|
|
207
|
+
updatedAt: stat.modifiedAt,
|
|
208
|
+
preview: preview(fm.body)
|
|
209
|
+
});
|
|
210
|
+
} catch {
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
results.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
214
|
+
return results;
|
|
215
|
+
});
|
|
216
|
+
ctx.messaging.onCommand("notes.tags", async () => {
|
|
217
|
+
const all = await scanNotes();
|
|
218
|
+
const counts = /* @__PURE__ */ new Map();
|
|
219
|
+
for (const m of all) {
|
|
220
|
+
for (const tag of m.tags) {
|
|
221
|
+
counts.set(tag, (counts.get(tag) ?? 0) + 1);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return Array.from(counts.entries()).map(([tag, count]) => ({ tag, count })).sort((a, b) => a.tag.localeCompare(b.tag));
|
|
225
|
+
});
|
|
226
|
+
return {};
|
|
227
|
+
}
|
|
228
|
+
function deactivate() {
|
|
229
|
+
}
|
|
230
|
+
export {
|
|
231
|
+
activate,
|
|
232
|
+
deactivate,
|
|
233
|
+
manifest
|
|
234
|
+
};
|
|
235
|
+
//# sourceMappingURL=backend.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/lib/frontmatter.ts", "../src/lib/helpers.ts", "../src/backend.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Hand-rolled YAML front matter parser and serializer.\n * Handles title, tags (inline [a,b] and block - item syntax), and created date.\n */\n\nconst FM_REGEX = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/;\n\nexport interface FrontMatter {\n title: string;\n tags: string[];\n created: string | null;\n body: string;\n}\n\nexport function parseFrontMatter(raw: string): FrontMatter {\n const match = raw.match(FM_REGEX);\n if (!match) {\n const heading = raw.match(/^#\\s+(.+)$/m);\n return { title: heading ? heading[1].trim() : 'Untitled', tags: [], created: null, body: raw };\n }\n\n const yaml = match[1];\n const body = match[2];\n let title = 'Untitled';\n let tags: string[] = [];\n let created: string | null = null;\n let inTags = false;\n\n for (const line of yaml.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed.startsWith('title:')) {\n title = trimmed\n .slice(6)\n .trim()\n .replace(/^[\"']|[\"']$/g, '');\n inTags = false;\n } else if (trimmed.startsWith('created:')) {\n created = trimmed.slice(8).trim();\n inTags = false;\n } else if (trimmed.startsWith('tags:')) {\n inTags = true;\n const inline = trimmed.slice(5).trim();\n if (inline.startsWith('[') && inline.endsWith(']')) {\n tags = inline\n .slice(1, -1)\n .split(',')\n .map((t) => t.trim().replace(/^[\"']|[\"']$/g, ''))\n .filter(Boolean);\n inTags = false;\n }\n } else if (inTags && trimmed.startsWith('- ')) {\n tags.push(\n trimmed\n .slice(2)\n .trim()\n .replace(/^[\"']|[\"']$/g, ''),\n );\n } else {\n inTags = false;\n }\n }\n\n return { title, tags, created, body };\n}\n\nexport function serializeFrontMatter(\n title: string,\n tags: string[],\n created: string,\n body: string,\n): string {\n const lines = ['---', `title: \"${title.replace(/\"/g, '\\\\\"')}\"`];\n if (tags.length > 0) {\n lines.push('tags:');\n for (const tag of tags) lines.push(` - ${tag}`);\n }\n lines.push(`created: ${created}`, '---', '', body);\n return lines.join('\\n');\n}\n", "/**\n * Utility functions for the Note MiniApp backend.\n */\n\n/** Convert a title to a URL-safe slug, max 64 chars. */\nexport function slugify(text: string): string {\n return (\n text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 64) || 'untitled'\n );\n}\n\n/** Extract a plain-text preview from Markdown body, stripping formatting. */\nexport function preview(body: string, maxLen = 100): string {\n const text = body\n .replace(/^#+\\s+/gm, '')\n .replace(/[*_`~\\[\\]()>]/g, '')\n .trim();\n return text.length > maxLen ? text.slice(0, maxLen) + '\u2026' : text;\n}\n", "import type { MiniAppManifest, MiniAppContext, MiniAppBackendActivation } from '@desktalk/sdk';\nimport type { Note, NoteMeta, TagCount } from './types';\nimport { parseFrontMatter, serializeFrontMatter } from './lib/frontmatter';\nimport { slugify, preview } from './lib/helpers';\n\n// \u2500\u2500\u2500 Manifest \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const manifest: MiniAppManifest = {\n id: 'note',\n name: 'Note',\n icon: '\\uD83D\\uDDD2\\uFE0F',\n version: '0.1.0',\n description: 'Markdown note-taking with tags and YAML front matter',\n};\n\n// \u2500\u2500\u2500 Activate \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function activate(ctx: MiniAppContext): MiniAppBackendActivation {\n ctx.logger.info('Note MiniApp activated');\n\n function joinPath(...parts: string[]): string {\n return parts.filter(Boolean).join('/').replace(/\\/+/g, '/');\n }\n\n function appPath(relPath = '.'): string {\n return relPath === '.'\n ? joinPath('.data', manifest.id)\n : joinPath('.data', manifest.id, relPath);\n }\n\n /**\n * Recursively collect all .md files under the given directory.\n * Returns relative paths (e.g. \"work/meeting.md\", \"readme.md\").\n */\n async function walkMarkdown(dir: string): Promise<string[]> {\n const exists = await ctx.fs.exists(appPath(dir));\n if (!exists) return [];\n\n const entries = await ctx.fs.readDir(appPath(dir));\n const paths: string[] = [];\n for (const entry of entries) {\n const entryPath = dir === '.' ? entry.name : joinPath(dir, entry.name);\n if (entry.type === 'directory') {\n paths.push(...(await walkMarkdown(entryPath)));\n } else if (entry.type === 'file' && entry.name.endsWith('.md')) {\n paths.push(entryPath);\n }\n }\n return paths;\n }\n\n /**\n * Derive a note ID from a relative .md path.\n * The ID is the path with the .md extension stripped.\n * Example: \"work/meeting.md\" \u2192 \"work/meeting\"\n */\n function pathToId(relPath: string): string {\n return relPath.replace(/\\.md$/, '');\n }\n\n /**\n * Read all .md files recursively and build metadata from front matter + stat.\n * The filesystem is the single source of truth \u2014 no separate index.\n * Note IDs are relative paths without the .md extension (e.g. \"work/meeting\").\n */\n async function scanNotes(tagFilter?: string): Promise<NoteMeta[]> {\n const dirExists = await ctx.fs.exists(appPath('.'));\n if (!dirExists) {\n await ctx.fs.mkdir(appPath('.'));\n return [];\n }\n\n const mdPaths = await walkMarkdown('.');\n const metas: NoteMeta[] = [];\n\n for (const relPath of mdPaths) {\n try {\n const raw = await ctx.fs.readFile(appPath(relPath));\n const stat = await ctx.fs.stat(appPath(relPath));\n const fm = parseFrontMatter(raw);\n\n if (tagFilter && !fm.tags.includes(tagFilter)) continue;\n\n metas.push({\n id: pathToId(relPath),\n title: fm.title,\n tags: fm.tags,\n createdAt: fm.created ?? stat.createdAt,\n updatedAt: stat.modifiedAt,\n preview: preview(fm.body),\n });\n } catch {\n // Corrupted file \u2014 skip\n }\n }\n\n metas.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));\n return metas;\n }\n\n // \u2500\u2500\u2500 notes.list \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n ctx.messaging.onCommand<{ tag?: string }, NoteMeta[]>('notes.list', async (req) =>\n scanNotes(req?.tag),\n );\n\n // \u2500\u2500\u2500 notes.get \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n ctx.messaging.onCommand<{ id: string }, Note>('notes.get', async (req) => {\n const filename = `${req.id}.md`;\n const raw = await ctx.fs.readFile(appPath(filename));\n const stat = await ctx.fs.stat(appPath(filename));\n const fm = parseFrontMatter(raw);\n return {\n id: req.id,\n title: fm.title,\n tags: fm.tags,\n content: raw,\n createdAt: fm.created ?? stat.createdAt,\n updatedAt: stat.modifiedAt,\n };\n });\n\n // \u2500\u2500\u2500 notes.create \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n ctx.messaging.onCommand<\n { title?: string; content?: string; tags?: string[]; path?: string },\n Note\n >('notes.create', async (req) => {\n const title = req?.title || 'Untitled';\n const tags = req?.tags || [];\n const body = req?.content || '';\n const now = new Date().toISOString();\n\n // Use explicit path if provided, otherwise fall back to slug + timestamp.\n const id = req?.path || slugify(title) + '-' + Date.now().toString(36);\n const filename = `${id}.md`;\n\n const content = serializeFrontMatter(title, tags, now, body);\n await ctx.fs.writeFile(appPath(filename), content);\n\n const stat = await ctx.fs.stat(appPath(filename));\n ctx.logger.info(`Created note: ${id}`);\n return { id, title, tags, content, createdAt: now, updatedAt: stat.modifiedAt };\n });\n\n // \u2500\u2500\u2500 notes.update \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n ctx.messaging.onCommand<{ id: string; content?: string; tags?: string[] }, Note>(\n 'notes.update',\n async (req) => {\n const filename = `${req.id}.md`;\n const existing = await ctx.fs.readFile(appPath(filename));\n const oldFm = parseFrontMatter(existing);\n const stat = await ctx.fs.stat(appPath(filename));\n\n let title: string;\n let tags: string[];\n let body: string;\n\n if (req.content !== undefined) {\n // Full content provided \u2014 parse its front matter\n const newFm = parseFrontMatter(req.content);\n title = newFm.title;\n tags = req.tags ?? newFm.tags;\n body = newFm.body;\n } else {\n // Only tags changed\n title = oldFm.title;\n tags = req.tags ?? oldFm.tags;\n body = oldFm.body;\n }\n\n const created = oldFm.created ?? stat.createdAt;\n const content = serializeFrontMatter(title, tags, created, body);\n await ctx.fs.writeFile(appPath(filename), content);\n\n const newStat = await ctx.fs.stat(appPath(filename));\n ctx.logger.info(`Updated note: ${req.id}`);\n return {\n id: req.id,\n title,\n tags,\n content,\n createdAt: created,\n updatedAt: newStat.modifiedAt,\n };\n },\n );\n\n // \u2500\u2500\u2500 notes.delete \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n ctx.messaging.onCommand<{ id: string }, void>('notes.delete', async (req) => {\n await ctx.fs.deleteFile(appPath(`${req.id}.md`));\n ctx.logger.info(`Deleted note: ${req.id}`);\n });\n\n // \u2500\u2500\u2500 notes.search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n ctx.messaging.onCommand<{ query: string }, NoteMeta[]>('notes.search', async (req) => {\n const q = (req.query || '').toLowerCase();\n if (!q) return scanNotes();\n\n const mdPaths = await walkMarkdown('.');\n const results: NoteMeta[] = [];\n\n for (const relPath of mdPaths) {\n try {\n const raw = await ctx.fs.readFile(appPath(relPath));\n if (!raw.toLowerCase().includes(q)) continue;\n\n const stat = await ctx.fs.stat(appPath(relPath));\n const fm = parseFrontMatter(raw);\n results.push({\n id: pathToId(relPath),\n title: fm.title,\n tags: fm.tags,\n createdAt: fm.created ?? stat.createdAt,\n updatedAt: stat.modifiedAt,\n preview: preview(fm.body),\n });\n } catch {\n // skip unreadable files\n }\n }\n\n results.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));\n return results;\n });\n\n // \u2500\u2500\u2500 notes.tags \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n ctx.messaging.onCommand<void, TagCount[]>('notes.tags', async () => {\n const all = await scanNotes();\n const counts = new Map<string, number>();\n for (const m of all) {\n for (const tag of m.tags) {\n counts.set(tag, (counts.get(tag) ?? 0) + 1);\n }\n }\n return Array.from(counts.entries())\n .map(([tag, count]) => ({ tag, count }))\n .sort((a, b) => a.tag.localeCompare(b.tag));\n });\n\n return {};\n}\n\nexport function deactivate(): void {\n // cleanup\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React, { type RefObject } from 'react';
|
|
2
|
+
import type { Note, NoteMeta } from '../types';
|
|
3
|
+
import type { NoteEditorHandle } from './NoteEditor';
|
|
4
|
+
interface NoteActionsProps {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
selectedNoteId: string | null;
|
|
7
|
+
selectedNote: Note | null;
|
|
8
|
+
notes: NoteMeta[];
|
|
9
|
+
editorRef: RefObject<NoteEditorHandle | null>;
|
|
10
|
+
onNoteCreated: (note: Note) => void;
|
|
11
|
+
onNoteDeleted: (id: string) => void;
|
|
12
|
+
onSearch: (query: string) => void;
|
|
13
|
+
onNoteUpdated: (note: Note) => void;
|
|
14
|
+
onSelectNote: (id: string) => Promise<void>;
|
|
15
|
+
onRefresh: () => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function NoteActions({ children, selectedNoteId, selectedNote, notes, editorRef, onNoteCreated, onNoteDeleted, onSearch, onNoteUpdated, onSelectNote, onRefresh: _onRefresh, }: NoteActionsProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=NoteActions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NoteActions.d.ts","sourceRoot":"","sources":["../../src/components/NoteActions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAe,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE3D,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAsDrD,UAAU,gBAAgB;IACxB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC9C,aAAa,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACpC,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,aAAa,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACpC,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,KAAK,EACL,SAAS,EACT,aAAa,EACb,aAAa,EACb,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,SAAS,EAAE,UAAU,GACtB,EAAE,gBAAgB,2CAoRlB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Note } from '../types';
|
|
3
|
+
import '@desktalk/ui';
|
|
4
|
+
/**
|
|
5
|
+
* Public handle exposed by NoteEditor via ref.
|
|
6
|
+
* Used by NoteActions for AI-driven editing.
|
|
7
|
+
*/
|
|
8
|
+
export interface NoteEditorHandle {
|
|
9
|
+
/** Get the current markdown content (front matter stripped). */
|
|
10
|
+
getMarkdown(): string | null;
|
|
11
|
+
/** Replace all editor content with new markdown. */
|
|
12
|
+
setMarkdown(markdown: string): void;
|
|
13
|
+
/** Get the 1-indexed line number of the cursor. */
|
|
14
|
+
getCursorLine(): number;
|
|
15
|
+
/** Get the currently selected text (empty string if none). */
|
|
16
|
+
getSelectedText(): string;
|
|
17
|
+
}
|
|
18
|
+
interface NoteEditorProps {
|
|
19
|
+
note: Note | null;
|
|
20
|
+
loading: boolean;
|
|
21
|
+
onSave: (content: string) => void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* NoteEditor uses <dt-markdown-editor> custom element.
|
|
25
|
+
* Shows a placeholder when no note is selected.
|
|
26
|
+
*/
|
|
27
|
+
export declare const NoteEditor: React.ForwardRefExoticComponent<NoteEditorProps & React.RefAttributes<NoteEditorHandle>>;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=NoteEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NoteEditor.d.ts","sourceRoot":"","sources":["../../src/components/NoteEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuE,MAAM,OAAO,CAAC;AAC5F,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAErC,OAAO,cAAc,CAAC;AAEtB;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gEAAgE;IAChE,WAAW,IAAI,MAAM,GAAG,IAAI,CAAC;IAC7B,oDAAoD;IACpD,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,mDAAmD;IACnD,aAAa,IAAI,MAAM,CAAC;IACxB,8DAA8D;IAC9D,eAAe,IAAI,MAAM,CAAC;CAC3B;AAED,UAAU,eAAe;IACvB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAUD;;;GAGG;AACH,eAAO,MAAM,UAAU,0FA+GrB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NoteMeta } from '../types';
|
|
2
|
+
interface NoteListProps {
|
|
3
|
+
notes: NoteMeta[];
|
|
4
|
+
selectedId: string | null;
|
|
5
|
+
onSelect: (id: string) => void;
|
|
6
|
+
onCreate: () => void;
|
|
7
|
+
searchQuery: string;
|
|
8
|
+
onSearchChange: (query: string) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function NoteList({ notes, selectedId, onSelect, onCreate, searchQuery, onSearchChange, }: NoteListProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=NoteList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NoteList.d.ts","sourceRoot":"","sources":["../../src/components/NoteList.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGzC,UAAU,aAAa;IACrB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAoBD,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,cAAc,GACf,EAAE,aAAa,2CAuCf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontend.d.ts","sourceRoot":"","sources":["../src/frontend.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAkNvF,wBAAgB,QAAQ,CAAC,GAAG,EAAE,sBAAsB,GAAG,yBAAyB,CAe/E"}
|