Custom template slash commands
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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) => ({
|
||||
|
||||
@@ -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!,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user