2023-11-06 08:14:16 +00:00
|
|
|
import { editor, handlebars, markdown, space, YAML } from "$sb/syscalls.ts";
|
2022-11-24 11:04:00 +00:00
|
|
|
import { extractFrontmatter } from "$sb/lib/frontmatter.ts";
|
2022-10-14 13:11:33 +00:00
|
|
|
import { renderToText } from "$sb/lib/tree.ts";
|
2023-11-03 11:04:51 +00:00
|
|
|
import { niceDate, niceTime } from "$sb/lib/dates.ts";
|
2022-10-14 13:11:33 +00:00
|
|
|
import { readSettings } from "$sb/lib/settings_page.ts";
|
2023-08-17 18:27:05 +00:00
|
|
|
import { cleanPageRef } from "$sb/lib/resolve.ts";
|
2023-11-06 08:14:16 +00:00
|
|
|
import { ObjectValue, PageMeta } from "$sb/types.ts";
|
|
|
|
import { CompleteEvent, SlashCompletion } from "$sb/app_event.ts";
|
|
|
|
import { getObjectByRef, queryObjects } from "../index/plug_api.ts";
|
|
|
|
|
|
|
|
export type TemplateObject = ObjectValue<{
|
|
|
|
trigger?: string; // has to start with # for now
|
|
|
|
scope?: string;
|
|
|
|
frontmatter?: Record<string, any> | string;
|
|
|
|
}>;
|
|
|
|
|
|
|
|
export async function templateSlashComplete(
|
|
|
|
completeEvent: CompleteEvent,
|
|
|
|
): Promise<SlashCompletion[]> {
|
|
|
|
const allTemplates = await queryObjects<TemplateObject>("template", {});
|
|
|
|
return allTemplates.map((template) => ({
|
|
|
|
label: template.trigger!,
|
|
|
|
detail: "template",
|
|
|
|
templatePage: template.ref,
|
|
|
|
pageName: completeEvent.pageName,
|
|
|
|
invoke: "template.insertSlashTemplate",
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function insertSlashTemplate(slashCompletion: SlashCompletion) {
|
|
|
|
const pageObject = await loadPageObject(slashCompletion.pageName);
|
|
|
|
|
|
|
|
let templateText = await space.readPage(slashCompletion.templatePage);
|
|
|
|
templateText = await replaceTemplateVars(templateText, pageObject);
|
|
|
|
const parseTree = await markdown.parseMarkdown(templateText);
|
|
|
|
const frontmatter = await extractFrontmatter(parseTree, [], true);
|
|
|
|
templateText = renderToText(parseTree).trim();
|
|
|
|
if (frontmatter.frontmatter) {
|
|
|
|
templateText = "---\n" + (await YAML.stringify(frontmatter.frontmatter)) +
|
|
|
|
"---\n" + templateText;
|
|
|
|
}
|
|
|
|
|
|
|
|
const cursorPos = await editor.getCursor();
|
|
|
|
const carretPos = templateText.indexOf("|^|");
|
|
|
|
templateText = templateText.replace("|^|", "");
|
|
|
|
await editor.insertAtCursor(templateText);
|
|
|
|
if (carretPos !== -1) {
|
|
|
|
await editor.moveCursor(cursorPos + carretPos);
|
|
|
|
}
|
|
|
|
}
|
2022-04-13 12:46:52 +00:00
|
|
|
|
|
|
|
export async function instantiateTemplateCommand() {
|
2022-10-14 13:11:33 +00:00
|
|
|
const allPages = await space.listPages();
|
2022-10-10 12:50:21 +00:00
|
|
|
const { pageTemplatePrefix } = await readSettings({
|
2022-07-15 09:17:02 +00:00
|
|
|
pageTemplatePrefix: "template/page/",
|
|
|
|
});
|
|
|
|
|
2022-10-14 13:11:33 +00:00
|
|
|
const selectedTemplate = await editor.filterBox(
|
2022-04-13 12:46:52 +00:00
|
|
|
"Template",
|
2022-08-08 11:56:04 +00:00
|
|
|
allPages
|
|
|
|
.filter((pageMeta) => pageMeta.name.startsWith(pageTemplatePrefix))
|
|
|
|
.map((pageMeta) => ({
|
|
|
|
...pageMeta,
|
|
|
|
name: pageMeta.name.slice(pageTemplatePrefix.length),
|
|
|
|
})),
|
2022-10-10 12:50:21 +00:00
|
|
|
`Select the template to create a new page from (listing any page starting with <tt>${pageTemplatePrefix}</tt>)`,
|
2022-04-13 12:46:52 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (!selectedTemplate) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
console.log("Selected template", selectedTemplate);
|
|
|
|
|
2022-10-14 13:11:33 +00:00
|
|
|
const text = await space.readPage(
|
2022-10-10 12:50:21 +00:00
|
|
|
`${pageTemplatePrefix}${selectedTemplate.name}`,
|
2022-08-08 11:56:04 +00:00
|
|
|
);
|
2022-04-13 12:46:52 +00:00
|
|
|
|
2022-10-14 13:11:33 +00:00
|
|
|
const parseTree = await markdown.parseMarkdown(text);
|
2023-05-23 18:53:53 +00:00
|
|
|
const additionalPageMeta = await extractFrontmatter(parseTree, [
|
2022-08-09 13:37:47 +00:00
|
|
|
"$name",
|
|
|
|
"$disableDirectives",
|
|
|
|
]);
|
2022-04-13 12:46:52 +00:00
|
|
|
|
2023-07-02 09:25:32 +00:00
|
|
|
const tempPageMeta: PageMeta = {
|
2023-11-06 08:14:16 +00:00
|
|
|
tags: ["page"],
|
|
|
|
ref: "",
|
2023-07-02 09:25:32 +00:00
|
|
|
name: "",
|
2023-11-06 08:14:16 +00:00
|
|
|
created: "",
|
|
|
|
lastModified: "",
|
2023-07-02 09:25:32 +00:00
|
|
|
perm: "rw",
|
|
|
|
};
|
|
|
|
|
2023-02-27 14:20:16 +00:00
|
|
|
if (additionalPageMeta.$name) {
|
2023-10-03 12:16:33 +00:00
|
|
|
additionalPageMeta.$name = await replaceTemplateVars(
|
2023-02-27 14:20:16 +00:00
|
|
|
additionalPageMeta.$name,
|
2023-07-02 09:25:32 +00:00
|
|
|
tempPageMeta,
|
2023-02-27 14:20:16 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-10-14 13:11:33 +00:00
|
|
|
const pageName = await editor.prompt(
|
|
|
|
"Name of new page",
|
|
|
|
additionalPageMeta.$name,
|
|
|
|
);
|
2022-04-13 12:46:52 +00:00
|
|
|
if (!pageName) {
|
|
|
|
return;
|
|
|
|
}
|
2023-07-02 09:25:32 +00:00
|
|
|
tempPageMeta.name = pageName;
|
2023-02-27 14:51:54 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
// Fails if doesn't exist
|
2023-03-06 13:17:03 +00:00
|
|
|
await space.getPageMeta(pageName);
|
|
|
|
|
2023-02-27 14:51:54 +00:00
|
|
|
// So, page exists, let's warn
|
|
|
|
if (
|
|
|
|
!await editor.confirm(
|
|
|
|
`Page ${pageName} already exists, are you sure you want to override it?`,
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} catch {
|
|
|
|
// The preferred scenario, let's keep going
|
|
|
|
}
|
|
|
|
|
2023-10-03 12:16:33 +00:00
|
|
|
const pageText = await replaceTemplateVars(
|
|
|
|
renderToText(parseTree),
|
|
|
|
tempPageMeta,
|
|
|
|
);
|
2022-10-14 13:11:33 +00:00
|
|
|
await space.writePage(pageName, pageText);
|
|
|
|
await editor.navigate(pageName);
|
2022-04-19 14:54:47 +00:00
|
|
|
}
|
|
|
|
|
2022-07-04 13:07:27 +00:00
|
|
|
export async function insertSnippet() {
|
2022-10-14 13:11:33 +00:00
|
|
|
const allPages = await space.listPages();
|
|
|
|
const { snippetPrefix } = await readSettings({
|
2022-07-15 09:17:02 +00:00
|
|
|
snippetPrefix: "snippet/",
|
|
|
|
});
|
2022-10-14 13:11:33 +00:00
|
|
|
const cursorPos = await editor.getCursor();
|
|
|
|
const page = await editor.getCurrentPage();
|
2023-07-02 09:25:32 +00:00
|
|
|
const pageMeta = await space.getPageMeta(page);
|
2022-10-14 13:11:33 +00:00
|
|
|
const allSnippets = allPages
|
2022-07-15 09:17:02 +00:00
|
|
|
.filter((pageMeta) => pageMeta.name.startsWith(snippetPrefix))
|
|
|
|
.map((pageMeta) => ({
|
|
|
|
...pageMeta,
|
|
|
|
name: pageMeta.name.slice(snippetPrefix.length),
|
|
|
|
}));
|
2022-07-04 13:07:27 +00:00
|
|
|
|
2022-10-14 13:11:33 +00:00
|
|
|
const selectedSnippet = await editor.filterBox(
|
2022-07-04 13:07:27 +00:00
|
|
|
"Snippet",
|
|
|
|
allSnippets,
|
2022-10-10 12:50:21 +00:00
|
|
|
`Select the snippet to insert (listing any page starting with <tt>${snippetPrefix}</tt>)`,
|
2022-07-04 13:07:27 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (!selectedSnippet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-10-14 13:11:33 +00:00
|
|
|
const text = await space.readPage(`${snippetPrefix}${selectedSnippet.name}`);
|
2023-10-03 12:16:33 +00:00
|
|
|
let templateText = await replaceTemplateVars(text, pageMeta);
|
2022-10-14 13:11:33 +00:00
|
|
|
const carretPos = templateText.indexOf("|^|");
|
2022-07-04 13:07:27 +00:00
|
|
|
templateText = templateText.replace("|^|", "");
|
2023-10-03 12:16:33 +00:00
|
|
|
templateText = await replaceTemplateVars(templateText, pageMeta);
|
2022-10-14 13:11:33 +00:00
|
|
|
await editor.insertAtCursor(templateText);
|
2022-07-04 13:07:27 +00:00
|
|
|
if (carretPos !== -1) {
|
2022-10-14 13:11:33 +00:00
|
|
|
await editor.moveCursor(cursorPos + carretPos);
|
2022-07-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-09 08:55:35 +00:00
|
|
|
export async function applyPageTemplateCommand() {
|
|
|
|
const allPages = await space.listPages();
|
|
|
|
const { pageTemplatePrefix } = await readSettings({
|
|
|
|
pageTemplatePrefix: "template/page/",
|
|
|
|
});
|
|
|
|
const cursorPos = await editor.getCursor();
|
|
|
|
const page = await editor.getCurrentPage();
|
|
|
|
const pageMeta = await space.getPageMeta(page);
|
|
|
|
const allSnippets = allPages
|
|
|
|
.filter((pageMeta) => pageMeta.name.startsWith(pageTemplatePrefix))
|
|
|
|
.map((pageMeta) => ({
|
|
|
|
...pageMeta,
|
|
|
|
name: pageMeta.name.slice(pageTemplatePrefix.length),
|
|
|
|
}));
|
|
|
|
|
|
|
|
const selectedPage = await editor.filterBox(
|
|
|
|
"Page template",
|
|
|
|
allSnippets,
|
|
|
|
`Select the page template to apply (listing any page starting with <tt>${pageTemplatePrefix}</tt>)`,
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!selectedPage) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const text = await space.readPage(
|
|
|
|
`${pageTemplatePrefix}${selectedPage.name}`,
|
|
|
|
);
|
2023-10-03 12:16:33 +00:00
|
|
|
let templateText = await replaceTemplateVars(text, pageMeta);
|
2023-08-09 08:55:35 +00:00
|
|
|
const carretPos = templateText.indexOf("|^|");
|
|
|
|
templateText = templateText.replace("|^|", "");
|
2023-10-03 12:16:33 +00:00
|
|
|
templateText = await replaceTemplateVars(templateText, pageMeta);
|
2023-08-09 08:55:35 +00:00
|
|
|
await editor.insertAtCursor(templateText);
|
|
|
|
if (carretPos !== -1) {
|
|
|
|
await editor.moveCursor(cursorPos + carretPos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-06 08:14:16 +00:00
|
|
|
export async function loadPageObject(pageName: string): Promise<PageMeta> {
|
|
|
|
return (await getObjectByRef<PageMeta>(
|
|
|
|
pageName,
|
|
|
|
"page",
|
|
|
|
pageName,
|
|
|
|
)) || {
|
|
|
|
ref: pageName,
|
|
|
|
name: pageName,
|
|
|
|
tags: ["page"],
|
|
|
|
lastModified: "",
|
|
|
|
created: "",
|
|
|
|
} as PageMeta;
|
|
|
|
}
|
|
|
|
|
2023-10-03 12:16:33 +00:00
|
|
|
export function replaceTemplateVars(
|
|
|
|
s: string,
|
|
|
|
pageMeta: PageMeta,
|
|
|
|
): Promise<string> {
|
|
|
|
return handlebars.renderTemplate(s, {}, { page: pageMeta });
|
2022-04-13 12:46:52 +00:00
|
|
|
}
|
2022-05-06 16:55:04 +00:00
|
|
|
|
|
|
|
export async function quickNoteCommand() {
|
2022-10-14 13:11:33 +00:00
|
|
|
const { quickNotePrefix } = await readSettings({
|
2022-08-08 11:56:04 +00:00
|
|
|
quickNotePrefix: "📥 ",
|
|
|
|
});
|
2023-11-03 11:04:51 +00:00
|
|
|
const date = niceDate(new Date());
|
|
|
|
const time = niceTime(new Date());
|
2022-10-14 13:11:33 +00:00
|
|
|
const pageName = `${quickNotePrefix}${date} ${time}`;
|
|
|
|
await editor.navigate(pageName);
|
2022-05-06 16:55:04 +00:00
|
|
|
}
|
|
|
|
|
2022-07-06 10:18:47 +00:00
|
|
|
export async function dailyNoteCommand() {
|
2022-10-14 13:11:33 +00:00
|
|
|
const { dailyNoteTemplate, dailyNotePrefix } = await readSettings({
|
2023-08-17 18:27:05 +00:00
|
|
|
dailyNoteTemplate: "[[template/page/Daily Note]]",
|
2022-08-08 11:56:04 +00:00
|
|
|
dailyNotePrefix: "📅 ",
|
|
|
|
});
|
2022-10-14 13:11:33 +00:00
|
|
|
const date = niceDate(new Date());
|
|
|
|
const pageName = `${dailyNotePrefix}${date}`;
|
2023-07-02 09:25:32 +00:00
|
|
|
let carretPos = 0;
|
2023-05-23 18:53:53 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
await space.getPageMeta(pageName);
|
|
|
|
} catch {
|
|
|
|
// Doesn't exist, let's create
|
|
|
|
let dailyNoteTemplateText = "";
|
2022-08-08 11:56:04 +00:00
|
|
|
try {
|
2023-08-17 18:27:05 +00:00
|
|
|
dailyNoteTemplateText = await space.readPage(
|
|
|
|
cleanPageRef(dailyNoteTemplate),
|
|
|
|
);
|
2023-07-02 09:25:32 +00:00
|
|
|
carretPos = dailyNoteTemplateText.indexOf("|^|");
|
|
|
|
if (carretPos === -1) {
|
|
|
|
carretPos = 0;
|
|
|
|
}
|
|
|
|
dailyNoteTemplateText = dailyNoteTemplateText.replace("|^|", "");
|
2022-08-08 11:56:04 +00:00
|
|
|
} catch {
|
2023-05-23 18:53:53 +00:00
|
|
|
console.warn(`No daily note template found at ${dailyNoteTemplate}`);
|
2022-08-08 11:56:04 +00:00
|
|
|
}
|
2023-07-02 09:25:32 +00:00
|
|
|
|
2023-05-23 18:53:53 +00:00
|
|
|
await space.writePage(
|
|
|
|
pageName,
|
2023-10-03 12:16:33 +00:00
|
|
|
await replaceTemplateVars(dailyNoteTemplateText, {
|
2023-11-06 08:14:16 +00:00
|
|
|
tags: ["page"],
|
|
|
|
ref: pageName,
|
2023-07-02 09:25:32 +00:00
|
|
|
name: pageName,
|
2023-11-06 08:14:16 +00:00
|
|
|
created: "",
|
|
|
|
lastModified: "",
|
2023-07-02 09:25:32 +00:00
|
|
|
perm: "rw",
|
|
|
|
}),
|
2023-05-23 18:53:53 +00:00
|
|
|
);
|
2022-08-08 11:56:04 +00:00
|
|
|
}
|
2023-07-02 09:25:32 +00:00
|
|
|
await editor.navigate(pageName, carretPos);
|
2022-07-06 10:18:47 +00:00
|
|
|
}
|
|
|
|
|
2022-11-02 08:06:30 +00:00
|
|
|
function getWeekStartDate(monday = false) {
|
|
|
|
const d = new Date();
|
|
|
|
const day = d.getDay();
|
|
|
|
let diff = d.getDate() - day;
|
|
|
|
if (monday) {
|
|
|
|
diff += day == 0 ? -6 : 1;
|
|
|
|
}
|
|
|
|
return new Date(d.setDate(diff));
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function weeklyNoteCommand() {
|
|
|
|
const { weeklyNoteTemplate, weeklyNotePrefix, weeklyNoteMonday } =
|
|
|
|
await readSettings({
|
2023-08-17 18:27:05 +00:00
|
|
|
weeklyNoteTemplate: "[[template/page/Weekly Note]]",
|
2022-11-02 08:06:30 +00:00
|
|
|
weeklyNotePrefix: "🗓️ ",
|
|
|
|
weeklyNoteMonday: false,
|
|
|
|
});
|
|
|
|
let weeklyNoteTemplateText = "";
|
|
|
|
try {
|
2023-08-17 18:27:05 +00:00
|
|
|
weeklyNoteTemplateText = await space.readPage(
|
|
|
|
cleanPageRef(weeklyNoteTemplate),
|
|
|
|
);
|
2022-11-02 08:06:30 +00:00
|
|
|
} catch {
|
|
|
|
console.warn(`No weekly note template found at ${weeklyNoteTemplate}`);
|
|
|
|
}
|
|
|
|
const date = niceDate(getWeekStartDate(weeklyNoteMonday));
|
|
|
|
const pageName = `${weeklyNotePrefix}${date}`;
|
|
|
|
if (weeklyNoteTemplateText) {
|
|
|
|
try {
|
|
|
|
await space.getPageMeta(pageName);
|
|
|
|
} catch {
|
|
|
|
// Doesn't exist, let's create
|
|
|
|
await space.writePage(
|
|
|
|
pageName,
|
2023-10-03 12:16:33 +00:00
|
|
|
await replaceTemplateVars(weeklyNoteTemplateText, {
|
2023-07-02 09:25:32 +00:00
|
|
|
name: pageName,
|
2023-11-06 08:14:16 +00:00
|
|
|
ref: pageName,
|
|
|
|
tags: ["page"],
|
|
|
|
created: "",
|
|
|
|
lastModified: "",
|
2023-07-02 09:25:32 +00:00
|
|
|
perm: "rw",
|
|
|
|
}),
|
2022-11-02 08:06:30 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
await editor.navigate(pageName);
|
|
|
|
} else {
|
|
|
|
await editor.navigate(pageName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-04 13:07:27 +00:00
|
|
|
export async function insertTemplateText(cmdDef: any) {
|
2022-10-14 13:11:33 +00:00
|
|
|
const cursorPos = await editor.getCursor();
|
|
|
|
const page = await editor.getCurrentPage();
|
2023-11-06 08:14:16 +00:00
|
|
|
const pageMeta = await loadPageObject(page);
|
2022-07-04 13:07:27 +00:00
|
|
|
let templateText: string = cmdDef.value;
|
2022-10-14 13:11:33 +00:00
|
|
|
const carretPos = templateText.indexOf("|^|");
|
2022-07-04 13:07:27 +00:00
|
|
|
templateText = templateText.replace("|^|", "");
|
2023-11-06 08:14:16 +00:00
|
|
|
templateText = await replaceTemplateVars(templateText, pageMeta);
|
2022-10-14 13:11:33 +00:00
|
|
|
await editor.insertAtCursor(templateText);
|
2022-07-04 13:07:27 +00:00
|
|
|
if (carretPos !== -1) {
|
2022-10-14 13:11:33 +00:00
|
|
|
await editor.moveCursor(cursorPos + carretPos);
|
2022-07-04 13:07:27 +00:00
|
|
|
}
|
2022-05-06 16:55:04 +00:00
|
|
|
}
|
2022-11-18 15:04:37 +00:00
|
|
|
|
|
|
|
export async function applyLineReplace(cmdDef: any) {
|
|
|
|
const cursorPos = await editor.getCursor();
|
|
|
|
const text = await editor.getText();
|
|
|
|
const matchRegex = new RegExp(cmdDef.match);
|
|
|
|
let startOfLine = cursorPos;
|
|
|
|
while (startOfLine > 0 && text[startOfLine - 1] !== "\n") {
|
|
|
|
startOfLine--;
|
|
|
|
}
|
|
|
|
let currentLine = text.slice(startOfLine, cursorPos);
|
|
|
|
|
|
|
|
const emptyLine = !currentLine;
|
|
|
|
|
|
|
|
currentLine = currentLine.replace(matchRegex, cmdDef.replace);
|
|
|
|
|
|
|
|
await editor.dispatch({
|
|
|
|
changes: {
|
|
|
|
from: startOfLine,
|
|
|
|
to: cursorPos,
|
|
|
|
insert: currentLine,
|
|
|
|
},
|
|
|
|
selection: emptyLine
|
|
|
|
? {
|
|
|
|
anchor: startOfLine + currentLine.length,
|
|
|
|
}
|
|
|
|
: undefined,
|
|
|
|
});
|
|
|
|
}
|