Custom template slash commands

This commit is contained in:
Zef Hemel
2023-11-06 09:14:16 +01:00
parent 0e2a802bbd
commit 1afac0274e
16 changed files with 214 additions and 79 deletions
-1
View File
@@ -1,7 +1,6 @@
import { handlebars, space } from "$sb/syscalls.ts";
import { handlebarHelpers } from "../../common/syscalls/handlebar_helpers.ts";
import { PageMeta } from "$sb/types.ts";
import { render } from "preact";
export function defaultJsonTransformer(_k: string, v: any) {
if (v === undefined) {
+14 -5
View File
@@ -1,6 +1,6 @@
import { CompleteEvent } from "$sb/app_event.ts";
import { space } from "$sb/syscalls.ts";
import { PageMeta } from "$sb/types.ts";
import { FileMeta, PageMeta } from "$sb/types.ts";
import { cacheFileListing } from "../federation/federation.ts";
// Completion
@@ -24,10 +24,7 @@ export async function pageComplete(completeEvent: CompleteEvent) {
// Cached listing
const federationPages = (await cacheFileListing(domain)).filter((fm) =>
fm.name.endsWith(".md")
).map((fm) => ({
...fm,
name: fm.name.slice(0, -3),
}));
).map(fileMetaToPageMeta);
if (federationPages.length > 0) {
allPages = allPages.concat(federationPages);
}
@@ -45,3 +42,15 @@ export async function pageComplete(completeEvent: CompleteEvent) {
}),
};
}
function fileMetaToPageMeta(fileMeta: FileMeta): PageMeta {
const name = fileMeta.name.substring(0, fileMeta.name.length - 3);
return {
...fileMeta,
ref: fileMeta.name,
tags: ["page"],
name,
created: new Date(fileMeta.created).toISOString(),
lastModified: new Date(fileMeta.lastModified).toISOString(),
} as PageMeta;
}
+6 -21
View File
@@ -1,43 +1,28 @@
import type { IndexTreeEvent } from "$sb/app_event.ts";
import { space } from "$sb/syscalls.ts";
import type { ObjectValue, PageMeta } from "$sb/types.ts";
import type { PageMeta } from "$sb/types.ts";
import { extractFrontmatter } from "$sb/lib/frontmatter.ts";
import { extractAttributes } from "$sb/lib/attribute.ts";
import { indexObjects } from "./api.ts";
export type PageObject = ObjectValue<
// The base is PageMeta, but we override lastModified to be a string
Omit<Omit<PageMeta, "lastModified">, "created"> & {
created: string; // indexing it as a string
lastModified: string; // indexing it as a string
} & Record<string, any>
>;
export async function indexPage({ name, tree }: IndexTreeEvent) {
if (name.startsWith("_")) {
// Don't index pages starting with _
return;
}
const pageMeta = await space.getPageMeta(name);
let pageObj: PageObject = {
ref: name,
tags: [], // will be overridden in a bit
...pageMeta,
created: new Date(pageMeta.created).toISOString(),
lastModified: new Date(pageMeta.lastModified).toISOString(),
};
let pageMeta = await space.getPageMeta(name);
const frontmatter: Record<string, any> = await extractFrontmatter(tree);
const frontmatter = await extractFrontmatter(tree);
const toplevelAttributes = await extractAttributes(tree, false);
// Push them all into the page object
pageObj = { ...pageObj, ...frontmatter, ...toplevelAttributes };
pageMeta = { ...pageMeta, ...frontmatter, ...toplevelAttributes };
pageObj.tags = ["page", ...pageObj.tags || []];
pageMeta.tags = [...new Set(["page", ...pageMeta.tags || []])];
// console.log("Page object", pageObj);
// console.log("Extracted page meta data", pageMeta);
await indexObjects<PageObject>(name, [pageObj]);
await indexObjects<PageMeta>(name, [pageMeta]);
}
+1 -1
View File
@@ -29,6 +29,6 @@ export function getObjectByRef<T>(
page: string,
tag: string,
ref: string,
): Promise<ObjectValue<T>[]> {
): Promise<T | undefined> {
return invokeFunction("index.getObjectByRef", page, tag, ref);
}
+7 -5
View File
@@ -7,20 +7,19 @@ import {
collectNodesOfType,
findParentMatching,
} from "$sb/lib/tree.ts";
import type { ObjectValue } from "$sb/types.ts";
export type TagObject = {
ref: string;
tags: string[];
export type TagObject = ObjectValue<{
name: string;
page: string;
parent: string;
};
}>;
export async function indexTags({ name, tree }: IndexTreeEvent) {
removeQueries(tree);
const tags = new Set<string>(); // name:parent
addParentPointers(tree);
const pageTags: string[] = (await extractFrontmatter(tree)).tags || [];
const pageTags: string[] = (await extractFrontmatter(tree)).tags;
for (const pageTag of pageTags) {
tags.add(`${pageTag}:page`);
}
@@ -68,9 +67,12 @@ export async function tagComplete(completeEvent: CompleteEvent) {
} else if (itemPrefixRegex.test(completeEvent.linePrefix)) {
parent = "item";
}
// Query all tags
const allTags = await queryObjects<TagObject>("tag", {
filter: ["=", ["attr", "parent"], ["string", parent]],
});
return {
from: completeEvent.pos - tagPrefix.length,
options: allTags.map((tag) => ({
+5 -5
View File
@@ -3,19 +3,19 @@ import { events, language, space, system } from "$sb/syscalls.ts";
import { parseTreeToAST } from "$sb/lib/tree.ts";
import { astToKvQuery } from "$sb/lib/parse-query.ts";
import { jsonToMDTable, renderTemplate } from "../directive/util.ts";
import { replaceTemplateVars } from "../template/template.ts";
import { loadPageObject, replaceTemplateVars } from "../template/template.ts";
export async function widget(
bodyText: string,
pageName: string,
): Promise<WidgetContent> {
const pageMeta = await space.getPageMeta(pageName);
const pageObject = await loadPageObject(pageName);
try {
const queryAST = parseTreeToAST(
await language.parseLanguage(
"query",
await replaceTemplateVars(bodyText, pageMeta),
await replaceTemplateVars(bodyText, pageObject),
),
);
const parsedQuery = astToKvQuery(queryAST[1]);
@@ -28,7 +28,7 @@ export async function widget(
// Let's dispatch an event and see what happens
const results = await events.dispatchEvent(
eventName,
{ query: parsedQuery, pageName: pageMeta.name },
{ query: parsedQuery, pageName: pageObject.name },
30 * 1000,
);
if (results.length === 0) {
@@ -45,7 +45,7 @@ export async function widget(
if (parsedQuery.render) {
// Configured a custom rendering template, let's use it!
const rendered = await renderTemplate(
pageMeta,
pageObject,
parsedQuery.render,
allResults,
parsedQuery.renderAll!,
+3 -2
View File
@@ -1,8 +1,9 @@
import { WidgetContent } from "$sb/app_event.ts";
import { handlebars, markdown, space, system, YAML } from "$sb/syscalls.ts";
import { rewritePageRefs } from "$sb/lib/resolve.ts";
import { replaceTemplateVars } from "../template/template.ts";
import { loadPageObject, replaceTemplateVars } from "../template/template.ts";
import { renderToText } from "$sb/lib/tree.ts";
import { PageMeta } from "$sb/types.ts";
type TemplateConfig = {
// Pull the template from a page
@@ -19,7 +20,7 @@ export async function widget(
bodyText: string,
pageName: string,
): Promise<WidgetContent> {
const pageMeta = await space.getPageMeta(pageName);
const pageMeta: PageMeta = await loadPageObject(pageName);
try {
const config: TemplateConfig = await YAML.parse(bodyText);
+9
View File
@@ -1,5 +1,14 @@
name: template
functions:
templateSlashCommand:
path: ./template.ts:templateSlashComplete
events:
- slash:complete
insertSlashTemplate:
path: ./template.ts:insertSlashTemplate
# Template commands
insertTemplateText:
path: "./template.ts:insertTemplateText"
+73 -21
View File
@@ -1,10 +1,53 @@
import { editor, handlebars, markdown, space } from "$sb/syscalls.ts";
import { editor, handlebars, markdown, space, YAML } from "$sb/syscalls.ts";
import { extractFrontmatter } from "$sb/lib/frontmatter.ts";
import { renderToText } from "$sb/lib/tree.ts";
import { niceDate, niceTime } from "$sb/lib/dates.ts";
import { readSettings } from "$sb/lib/settings_page.ts";
import { cleanPageRef } from "$sb/lib/resolve.ts";
import { PageMeta } from "$sb/types.ts";
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);
}
}
export async function instantiateTemplateCommand() {
const allPages = await space.listPages();
@@ -39,9 +82,11 @@ export async function instantiateTemplateCommand() {
]);
const tempPageMeta: PageMeta = {
tags: ["page"],
ref: "",
name: "",
created: 0,
lastModified: 0,
created: "",
lastModified: "",
perm: "rw",
};
@@ -159,6 +204,20 @@ export async function applyPageTemplateCommand() {
}
}
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;
}
export function replaceTemplateVars(
s: string,
pageMeta: PageMeta,
@@ -206,9 +265,11 @@ export async function dailyNoteCommand() {
await space.writePage(
pageName,
await replaceTemplateVars(dailyNoteTemplateText, {
tags: ["page"],
ref: pageName,
name: pageName,
created: 0,
lastModified: 0,
created: "",
lastModified: "",
perm: "rw",
}),
);
@@ -252,8 +313,10 @@ export async function weeklyNoteCommand() {
pageName,
await replaceTemplateVars(weeklyNoteTemplateText, {
name: pageName,
created: 0,
lastModified: 0,
ref: pageName,
tags: ["page"],
created: "",
lastModified: "",
perm: "rw",
}),
);
@@ -267,22 +330,11 @@ export async function weeklyNoteCommand() {
export async function insertTemplateText(cmdDef: any) {
const cursorPos = await editor.getCursor();
const page = await editor.getCurrentPage();
let pageMeta: PageMeta | undefined;
try {
pageMeta = await space.getPageMeta(page);
} catch {
// Likely page not yet created
pageMeta = {
name: page,
created: 0,
lastModified: 0,
perm: "rw",
};
}
const pageMeta = await loadPageObject(page);
let templateText: string = cmdDef.value;
const carretPos = templateText.indexOf("|^|");
templateText = templateText.replace("|^|", "");
templateText = await replaceTemplateVars(templateText, pageMeta!);
templateText = await replaceTemplateVars(templateText, pageMeta);
await editor.insertAtCursor(templateText);
if (carretPos !== -1) {
await editor.moveCursor(cursorPos + carretPos);