Custom template slash commands
This commit is contained in:
parent
0e2a802bbd
commit
1afac0274e
@ -7,6 +7,9 @@ export function handlebarHelpers() {
|
||||
escapeRegexp: (ts: any) => {
|
||||
return ts.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
|
||||
},
|
||||
escape: (handlebarsExpr: string) => {
|
||||
return `{{${handlebarsExpr}}}`;
|
||||
},
|
||||
replaceRegexp: (s: string, regexp: string, replacement: string) => {
|
||||
return s.replace(new RegExp(regexp, "g"), replacement);
|
||||
},
|
||||
|
@ -6,6 +6,7 @@ export type AppEvent =
|
||||
| "page:click"
|
||||
| "editor:complete"
|
||||
| "minieditor:complete"
|
||||
| "slash:complete"
|
||||
| "page:load"
|
||||
| "editor:init"
|
||||
| "editor:pageLoaded" // args: pageName, previousPage, isSynced
|
||||
@ -51,6 +52,12 @@ export type CompleteEvent = {
|
||||
parentNodes: string[];
|
||||
};
|
||||
|
||||
export type SlashCompletion = {
|
||||
label: string;
|
||||
detail?: string;
|
||||
invoke: string;
|
||||
} & Record<string, any>;
|
||||
|
||||
export type WidgetContent = {
|
||||
html?: string;
|
||||
script?: string;
|
||||
|
@ -9,18 +9,23 @@ import {
|
||||
traverseTreeAsync,
|
||||
} from "$sb/lib/tree.ts";
|
||||
|
||||
export type FrontMatter = { tags: string[] } & Record<string, any>;
|
||||
|
||||
// Extracts front matter (or legacy "meta" code blocks) from a markdown document
|
||||
// optionally removes certain keys from the front matter
|
||||
export async function extractFrontmatter(
|
||||
tree: ParseTree,
|
||||
removeKeys: string[] = [],
|
||||
removeFrontmatterSection = false,
|
||||
): Promise<any> {
|
||||
let data: any = {};
|
||||
): Promise<FrontMatter> {
|
||||
let data: FrontMatter = {
|
||||
tags: [],
|
||||
};
|
||||
addParentPointers(tree);
|
||||
let paragraphCounter = 0;
|
||||
|
||||
await replaceNodesMatchingAsync(tree, async (t) => {
|
||||
// Find tags in the first paragraph to attach to the page
|
||||
if (t.type === "Paragraph") {
|
||||
paragraphCounter++;
|
||||
// Only attach hashtags in the first paragraph to the page
|
||||
@ -28,11 +33,8 @@ export async function extractFrontmatter(
|
||||
return;
|
||||
}
|
||||
collectNodesOfType(t, "Hashtag").forEach((h) => {
|
||||
if (!data.tags) {
|
||||
data.tags = [];
|
||||
}
|
||||
const tagname = h.children![0].text!.substring(1);
|
||||
if (Array.isArray(data.tags) && !data.tags.includes(tagname)) {
|
||||
if (!data.tags.includes(tagname)) {
|
||||
data.tags.push(tagname);
|
||||
}
|
||||
});
|
||||
@ -45,6 +47,14 @@ export async function extractFrontmatter(
|
||||
const parsedData: any = await YAML.parse(yamlText);
|
||||
const newData = { ...parsedData };
|
||||
data = { ...data, ...parsedData };
|
||||
// Make sure we have a tags array
|
||||
if (!data.tags) {
|
||||
data.tags = [];
|
||||
}
|
||||
// Normalize tags to an array and support a "tag1, tag2" notation
|
||||
if (typeof data.tags === "string") {
|
||||
data.tags = (data.tags as string).split(/,\s*/);
|
||||
}
|
||||
if (removeKeys.length > 0) {
|
||||
let removedOne = false;
|
||||
|
||||
|
@ -8,13 +8,15 @@ export type FileMeta = {
|
||||
noSync?: boolean;
|
||||
};
|
||||
|
||||
export type PageMeta = {
|
||||
name: string;
|
||||
created: number;
|
||||
lastModified: number;
|
||||
lastOpened?: number;
|
||||
perm: "ro" | "rw";
|
||||
};
|
||||
export type PageMeta = ObjectValue<
|
||||
{
|
||||
name: string;
|
||||
created: string; // indexing it as a string
|
||||
lastModified: string; // indexing it as a string
|
||||
lastOpened?: number;
|
||||
perm: "ro" | "rw";
|
||||
} & Record<string, any>
|
||||
>;
|
||||
|
||||
export type AttachmentMeta = {
|
||||
name: string;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,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"
|
||||
|
@ -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);
|
||||
|
@ -624,7 +624,7 @@ export class Client {
|
||||
}
|
||||
|
||||
// Code completion support
|
||||
private async completeWithEvent(
|
||||
async completeWithEvent(
|
||||
context: CompletionContext,
|
||||
eventName: AppEvent,
|
||||
): Promise<CompletionResult | null> {
|
||||
@ -761,7 +761,14 @@ export class Client {
|
||||
console.log("Page doesn't exist, creating new page:", pageName);
|
||||
doc = {
|
||||
text: "",
|
||||
meta: { name: pageName, lastModified: 0, perm: "rw" } as PageMeta,
|
||||
meta: {
|
||||
ref: pageName,
|
||||
tags: ["page"],
|
||||
name: pageName,
|
||||
lastModified: "",
|
||||
created: "",
|
||||
perm: "rw",
|
||||
} as PageMeta,
|
||||
};
|
||||
} else {
|
||||
this.flashNotification(
|
||||
|
@ -4,6 +4,7 @@ import { Completion, CompletionContext, CompletionResult } from "../deps.ts";
|
||||
import { safeRun } from "../../common/util.ts";
|
||||
import { Client } from "../client.ts";
|
||||
import { syntaxTree } from "../deps.ts";
|
||||
import { SlashCompletion } from "$sb/app_event.ts";
|
||||
|
||||
export type SlashCommandDef = {
|
||||
name: string;
|
||||
@ -53,9 +54,9 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
|
||||
}
|
||||
|
||||
// Completer for CodeMirror
|
||||
public slashCommandCompleter(
|
||||
public async slashCommandCompleter(
|
||||
ctx: CompletionContext,
|
||||
): CompletionResult | null {
|
||||
): Promise<CompletionResult | null> {
|
||||
const prefix = ctx.matchBefore(slashCommandRegexp);
|
||||
if (!prefix) {
|
||||
return null;
|
||||
@ -68,6 +69,7 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
|
||||
if (currentNode.type.name === "CommentBlock") {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (const def of this.slashCommands.values()) {
|
||||
options.push({
|
||||
label: def.slashCommand.name,
|
||||
@ -90,6 +92,48 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const slashCompletions: SlashCompletion[] | null = await this.editor
|
||||
.completeWithEvent(
|
||||
ctx,
|
||||
"slash:complete",
|
||||
) as any;
|
||||
|
||||
if (slashCompletions) {
|
||||
for (const slashCompletion of slashCompletions) {
|
||||
options.push({
|
||||
label: slashCompletion.label,
|
||||
detail: slashCompletion.detail,
|
||||
apply: () => {
|
||||
// Delete slash command part
|
||||
this.editor.editorView.dispatch({
|
||||
changes: {
|
||||
from: prefix!.from + prefixText.indexOf("/"),
|
||||
to: ctx.pos,
|
||||
insert: "",
|
||||
},
|
||||
});
|
||||
// Replace with whatever the completion is
|
||||
safeRun(async () => {
|
||||
const [plugName, functionName] = slashCompletion.invoke.split(
|
||||
".",
|
||||
);
|
||||
const plug = this.editor.system.system.loadedPlugs.get(plugName);
|
||||
if (!plug) {
|
||||
this.editor.flashNotification(
|
||||
`Plug ${plugName} not found`,
|
||||
"error",
|
||||
);
|
||||
return;
|
||||
}
|
||||
await plug.invoke(functionName, [slashCompletion]);
|
||||
this.editor.focus();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
// + 1 because of the '/'
|
||||
from: prefix.from + prefixText.indexOf("/") + 1,
|
||||
|
@ -221,8 +221,13 @@ export class Space {
|
||||
}
|
||||
|
||||
export function fileMetaToPageMeta(fileMeta: FileMeta): PageMeta {
|
||||
const name = fileMeta.name.substring(0, fileMeta.name.length - 3);
|
||||
return {
|
||||
...fileMeta,
|
||||
name: fileMeta.name.substring(0, fileMeta.name.length - 3),
|
||||
ref: name,
|
||||
tags: ["page"],
|
||||
name,
|
||||
created: new Date(fileMeta.created).toISOString(),
|
||||
lastModified: new Date(fileMeta.lastModified).toISOString(),
|
||||
} as PageMeta;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user