@@ -11,13 +11,13 @@ export const builtins: Record<string, Record<string, string>> = {
|
||||
ref: "!string",
|
||||
name: "!string",
|
||||
displayName: "string",
|
||||
aliases: "array",
|
||||
aliases: "string[]",
|
||||
created: "!date",
|
||||
lastModified: "!date",
|
||||
perm: "!rw|ro",
|
||||
contentType: "!string",
|
||||
size: "!number",
|
||||
tags: "array",
|
||||
tags: "string[]",
|
||||
},
|
||||
task: {
|
||||
ref: "!string",
|
||||
@@ -27,11 +27,11 @@ export const builtins: Record<string, Record<string, string>> = {
|
||||
state: "!string",
|
||||
deadline: "string",
|
||||
pos: "!number",
|
||||
tags: "array",
|
||||
tags: "string[]",
|
||||
},
|
||||
taskstate: {
|
||||
ref: "!string",
|
||||
tags: "!array",
|
||||
tags: "!string[]",
|
||||
state: "!string",
|
||||
count: "!number",
|
||||
page: "!string",
|
||||
@@ -76,6 +76,7 @@ export const builtins: Record<string, Record<string, string>> = {
|
||||
pos: "!number",
|
||||
type: "string",
|
||||
trigger: "string",
|
||||
forTags: "string[]",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { CodeWidgetContent } from "$sb/types.ts";
|
||||
import { editor, language, markdown, space } from "$sb/syscalls.ts";
|
||||
import { extractFrontmatter } from "$sb/lib/frontmatter.ts";
|
||||
import { queryObjects } from "./api.ts";
|
||||
import { TemplateObject } from "../template/types.ts";
|
||||
import { renderTemplate } from "../template/plug_api.ts";
|
||||
import { loadPageObject } from "../template/template.ts";
|
||||
import { expressionToKvQueryExpression } from "$sb/lib/parse-query.ts";
|
||||
import { evalQueryExpression } from "$sb/lib/query.ts";
|
||||
import { parseTreeToAST } from "$sb/lib/tree.ts";
|
||||
|
||||
// Somewhat decent looking default template
|
||||
const fallbackTemplate = `{{#each .}}
|
||||
{{#ifEq @key "tags"}}{{else}}**{{@key}}**: {{.}}
|
||||
{{/ifEq}}
|
||||
{{/each}}
|
||||
{{#if tags}}_Tagged with_ {{#each tags}}#{{.}} {{/each}}{{/if}}`;
|
||||
|
||||
export async function renderFrontmatterWidget(): Promise<
|
||||
CodeWidgetContent | null
|
||||
> {
|
||||
const text = await editor.getText();
|
||||
const pageMeta = await loadPageObject(await editor.getCurrentPage());
|
||||
const parsedMd = await markdown.parseMarkdown(text);
|
||||
const frontmatter = await extractFrontmatter(parsedMd);
|
||||
|
||||
const allFrontMatterTemplates = await queryObjects<TemplateObject>(
|
||||
"template",
|
||||
{
|
||||
filter: ["=", ["attr", "type"], ["string", "frontmatter"]],
|
||||
orderBy: [{ expr: ["attr", "priority"], desc: false }],
|
||||
},
|
||||
);
|
||||
let templateText = fallbackTemplate;
|
||||
// Strategy: walk through all matching templates, evaluate the 'where' expression, and pick the first one that matches
|
||||
for (const template of allFrontMatterTemplates) {
|
||||
const exprAST = parseTreeToAST(
|
||||
await language.parseLanguage("expression", template.where!),
|
||||
);
|
||||
const parsedExpression = expressionToKvQueryExpression(exprAST[1]);
|
||||
if (evalQueryExpression(parsedExpression, pageMeta)) {
|
||||
// Match! We're happy
|
||||
templateText = await space.readPage(template.ref);
|
||||
break;
|
||||
}
|
||||
}
|
||||
const summaryText = await renderTemplate(
|
||||
templateText,
|
||||
pageMeta,
|
||||
frontmatter,
|
||||
);
|
||||
// console.log("Rendered", summaryText);
|
||||
return {
|
||||
markdown: summaryText.text,
|
||||
banner: "frontmatter",
|
||||
buttons: [
|
||||
{
|
||||
description: "Reload",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-refresh-cw"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>`,
|
||||
invokeFunction: "index.refreshWidgets",
|
||||
},
|
||||
{
|
||||
description: "Edit",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-edit"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>`,
|
||||
invokeFunction: "index.editFrontmatter",
|
||||
},
|
||||
{
|
||||
description: "",
|
||||
svg: "",
|
||||
widgetTarget: true,
|
||||
invokeFunction: "index.editFrontmatter",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export async function editFrontmatter() {
|
||||
// 4 = after the frontmatter (--- + newline)
|
||||
await editor.moveCursor(4, true);
|
||||
}
|
||||
@@ -186,4 +186,13 @@ functions:
|
||||
lintYAML:
|
||||
path: lint.ts:lintYAML
|
||||
events:
|
||||
- editor:lint
|
||||
- editor:lint
|
||||
|
||||
renderFrontmatterWidget:
|
||||
path: frontmatter.ts:renderFrontmatterWidget
|
||||
env: client
|
||||
panelWidget: frontmatter
|
||||
|
||||
editFrontmatter:
|
||||
path: frontmatter.ts:editFrontmatter
|
||||
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ export async function renderTOC(): Promise<CodeWidgetContent | null> {
|
||||
return false;
|
||||
});
|
||||
if (headers.length < headerThreshold) {
|
||||
console.log("Not enough headers, not showing TOC", headers.length);
|
||||
// Not enough headers, not showing TOC
|
||||
return null;
|
||||
}
|
||||
// console.log("Headers", headers);
|
||||
|
||||
@@ -267,7 +267,10 @@ function render(
|
||||
}
|
||||
case "Hashtag":
|
||||
return {
|
||||
name: "strong",
|
||||
name: "span",
|
||||
attrs: {
|
||||
class: "hashtag",
|
||||
},
|
||||
body: t.children![0].text!,
|
||||
};
|
||||
|
||||
@@ -414,6 +417,16 @@ function render(
|
||||
case "Entity":
|
||||
return t.children![0].text!;
|
||||
|
||||
case "TemplateDirective": {
|
||||
return {
|
||||
name: "span",
|
||||
attrs: {
|
||||
class: "template-directive",
|
||||
},
|
||||
body: renderToText(t),
|
||||
};
|
||||
}
|
||||
|
||||
// Text
|
||||
case undefined:
|
||||
return t.text!;
|
||||
|
||||
+9
-20
@@ -1,11 +1,7 @@
|
||||
import type { LintEvent } from "$sb/app_event.ts";
|
||||
import { events, language, space } from "$sb/syscalls.ts";
|
||||
import {
|
||||
findNodeOfType,
|
||||
parseTreeToAST,
|
||||
traverseTreeAsync,
|
||||
} from "$sb/lib/tree.ts";
|
||||
import { astToKvQuery } from "$sb/lib/parse-query.ts";
|
||||
import { events, space } from "$sb/syscalls.ts";
|
||||
import { findNodeOfType, traverseTreeAsync } from "$sb/lib/tree.ts";
|
||||
import { parseQuery } from "$sb/lib/parse-query.ts";
|
||||
import { loadPageObject, replaceTemplateVars } from "../template/template.ts";
|
||||
import { cleanPageRef, resolvePath } from "$sb/lib/resolve.ts";
|
||||
import { CodeWidgetContent, LintDiagnostic } from "$sb/types.ts";
|
||||
@@ -18,13 +14,9 @@ export async function widget(
|
||||
const pageObject = await loadPageObject(pageName);
|
||||
|
||||
try {
|
||||
const queryAST = parseTreeToAST(
|
||||
await language.parseLanguage(
|
||||
"query",
|
||||
await replaceTemplateVars(bodyText, pageObject),
|
||||
),
|
||||
const parsedQuery = await parseQuery(
|
||||
await replaceTemplateVars(bodyText, pageObject),
|
||||
);
|
||||
const parsedQuery = astToKvQuery(queryAST[1]);
|
||||
|
||||
if (!parsedQuery.limit) {
|
||||
parsedQuery.limit = ["number", 1000];
|
||||
@@ -115,13 +107,10 @@ export async function lintQuery(
|
||||
}
|
||||
const bodyText = codeText.children![0].text!;
|
||||
try {
|
||||
const queryAST = parseTreeToAST(
|
||||
await language.parseLanguage(
|
||||
"query",
|
||||
await replaceTemplateVars(bodyText, pageObject),
|
||||
),
|
||||
const parsedQuery = await parseQuery(
|
||||
await replaceTemplateVars(bodyText, pageObject),
|
||||
);
|
||||
const parsedQuery = astToKvQuery(queryAST[1]);
|
||||
|
||||
const allSources = await allQuerySources();
|
||||
if (
|
||||
parsedQuery.querySource &&
|
||||
@@ -141,7 +130,7 @@ export async function lintQuery(
|
||||
);
|
||||
try {
|
||||
await space.getPageMeta(templatePage);
|
||||
} catch (e: any) {
|
||||
} catch {
|
||||
diagnostics.push({
|
||||
from: codeText.from!,
|
||||
to: codeText.to!,
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import { ObjectValue } from "$sb/types.ts";
|
||||
|
||||
export type TemplateFrontmatter = {
|
||||
trigger?: string; // slash command name
|
||||
displayName?: string;
|
||||
type?: "page";
|
||||
// Frontmatter can be encoded as an object (in which case we'll serialize it) or as a string
|
||||
frontmatter?: Record<string, any> | string;
|
||||
|
||||
// Specific for slash templates
|
||||
trigger?: string;
|
||||
|
||||
// Specific for frontmatter templates
|
||||
where?: string; // expression (SB query style)
|
||||
priority?: number; // When multiple templates match, the one with the highest priority is used
|
||||
};
|
||||
|
||||
export type TemplateObject = ObjectValue<TemplateFrontmatter>;
|
||||
|
||||
+10
-3
@@ -36,20 +36,27 @@ export function buildHandebarOptions(pageMeta: PageMeta) {
|
||||
};
|
||||
}
|
||||
|
||||
export function defaultJsonTransformer(_k: string, v: any) {
|
||||
export function defaultJsonTransformer(v: any): string {
|
||||
if (v === undefined) {
|
||||
return "";
|
||||
}
|
||||
if (typeof v === "string") {
|
||||
return v.replaceAll("\n", " ").replaceAll("|", "\\|");
|
||||
}
|
||||
if (Array.isArray(v)) {
|
||||
return v.map(defaultJsonTransformer).join(", ");
|
||||
} else if (typeof v === "object") {
|
||||
return Object.entries(v).map(([k, v]: [string, any]) =>
|
||||
`${k}: ${defaultJsonTransformer(v)}`
|
||||
).join(", ");
|
||||
}
|
||||
return "" + v;
|
||||
}
|
||||
|
||||
// Nicely format an array of JSON objects as a Markdown table
|
||||
export function jsonToMDTable(
|
||||
jsonArray: any[],
|
||||
valueTransformer: (k: string, v: any) => string = defaultJsonTransformer,
|
||||
valueTransformer: (v: any) => string = defaultJsonTransformer,
|
||||
): string {
|
||||
const headers = new Set<string>();
|
||||
for (const entry of jsonArray) {
|
||||
@@ -79,7 +86,7 @@ export function jsonToMDTable(
|
||||
for (const val of jsonArray) {
|
||||
const el = [];
|
||||
for (const prop of headerList) {
|
||||
const s = valueTransformer(prop, val[prop]);
|
||||
const s = valueTransformer(val[prop]);
|
||||
el.push(s);
|
||||
}
|
||||
lines.push("|" + el.join("|") + "|");
|
||||
|
||||
Reference in New Issue
Block a user