Monorepo with yarn workspaces requires yarn 3.2
This commit is contained in:
@@ -1,105 +0,0 @@
|
||||
syntax:
|
||||
HashTag:
|
||||
firstCharacters:
|
||||
- "#"
|
||||
regex: "#[A-Za-z\\.]+"
|
||||
styles:
|
||||
color: blue
|
||||
AtMention:
|
||||
firstCharacters:
|
||||
- "@"
|
||||
regex: "@[A-Za-z\\.]+"
|
||||
styles:
|
||||
color: blue
|
||||
NakedURL:
|
||||
firstCharacters:
|
||||
- "h"
|
||||
regex: "https?:\\/\\/[-a-zA-Z0-9@:%._\\+~#=]{1,256}([-a-zA-Z0-9()@:%_\\+.~#?&=\\/]*)"
|
||||
styles:
|
||||
color: "#0330cb"
|
||||
textDecoration: underline
|
||||
functions:
|
||||
clearPageIndex:
|
||||
path: "./page.ts:clearPageIndex"
|
||||
env: server
|
||||
events:
|
||||
- page:saved
|
||||
- page:deleted
|
||||
pageQueryProvider:
|
||||
path: ./page.ts:pageQueryProvider
|
||||
events:
|
||||
- query:page
|
||||
parseIndexTextRepublish:
|
||||
path: "./page.ts:parseIndexTextRepublish"
|
||||
events:
|
||||
- page:index_text
|
||||
indexLinks:
|
||||
path: "./page.ts:indexLinks"
|
||||
events:
|
||||
- page:index
|
||||
linkQueryProvider:
|
||||
path: ./page.ts:linkQueryProvider
|
||||
events:
|
||||
- query:link
|
||||
indexItems:
|
||||
path: "./item.ts:indexItems"
|
||||
events:
|
||||
- page:index
|
||||
itemQueryProvider:
|
||||
path: ./item.ts:queryProvider
|
||||
events:
|
||||
- query:item
|
||||
deletePage:
|
||||
path: "./page.ts:deletePage"
|
||||
command:
|
||||
name: "Page: Delete"
|
||||
reindexSpaceCommand:
|
||||
path: "./page.ts:reindexCommand"
|
||||
command:
|
||||
name: "Space: Reindex"
|
||||
reindexSpace:
|
||||
path: "./page.ts:reindexSpace"
|
||||
env: server
|
||||
renamePage:
|
||||
path: "./page.ts:renamePage"
|
||||
command:
|
||||
name: "Page: Rename"
|
||||
mac: Cmd-Alt-r
|
||||
key: Ctrl-Alt-r
|
||||
pageComplete:
|
||||
path: "./page.ts:pageComplete"
|
||||
events:
|
||||
- page:complete
|
||||
linkNavigate:
|
||||
path: "./navigate.ts:linkNavigate"
|
||||
command:
|
||||
name: Navigate To page
|
||||
key: Ctrl-Enter
|
||||
mac: Cmd-Enter
|
||||
clickNavigate:
|
||||
path: "./navigate.ts:clickNavigate"
|
||||
events:
|
||||
- page:click
|
||||
insertToday:
|
||||
path: "./dates.ts:insertToday"
|
||||
slashCommand:
|
||||
name: today
|
||||
insertTomorrow:
|
||||
path: "./dates.ts:insertTomorrow"
|
||||
slashCommand:
|
||||
name: tomorrow
|
||||
parseServerCommand:
|
||||
path: ./page.ts:parseServerPageCommand
|
||||
command:
|
||||
name: "Debug: Parse Document on Server"
|
||||
parsePage:
|
||||
path: ./page.ts:parsePage
|
||||
parseCommand:
|
||||
path: ./page.ts:parsePageCommand
|
||||
command:
|
||||
name: "Debug: Parse Document"
|
||||
|
||||
instantiateTemplateCommand:
|
||||
path: ./template.ts:instantiateTemplateCommand
|
||||
command:
|
||||
name: "Template: Instantiate for Page"
|
||||
@@ -1,17 +0,0 @@
|
||||
import { insertAtCursor } from "plugos-silverbullet-syscall/editor";
|
||||
|
||||
const dateMatchRegex = /(\d{4}\-\d{2}\-\d{2})/g;
|
||||
|
||||
export function niceDate(d: Date): string {
|
||||
return d.toISOString().split("T")[0];
|
||||
}
|
||||
|
||||
export async function insertToday() {
|
||||
await insertAtCursor(niceDate(new Date()));
|
||||
}
|
||||
|
||||
export async function insertTomorrow() {
|
||||
let d = new Date();
|
||||
d.setDate(d.getDate() + 1);
|
||||
await insertAtCursor(niceDate(d));
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
import { IndexTreeEvent } from "../../webapp/app_event";
|
||||
|
||||
import { batchSet, scanPrefixGlobal } from "plugos-silverbullet-syscall/index";
|
||||
import { collectNodesOfType, ParseTree, renderToText } from "../../common/tree";
|
||||
import { removeQueries } from "../query/util";
|
||||
import { applyQuery, QueryProviderEvent } from "../query/engine";
|
||||
|
||||
export type Item = {
|
||||
name: string;
|
||||
nested?: string;
|
||||
// Not stored in DB
|
||||
page?: string;
|
||||
pos?: number;
|
||||
};
|
||||
|
||||
export async function indexItems({ name, tree }: IndexTreeEvent) {
|
||||
let items: { key: string; value: Item }[] = [];
|
||||
removeQueries(tree);
|
||||
|
||||
console.log("Indexing items", name);
|
||||
|
||||
let coll = collectNodesOfType(tree, "ListItem");
|
||||
|
||||
coll.forEach((n) => {
|
||||
if (!n.children) {
|
||||
return;
|
||||
}
|
||||
let textNodes: ParseTree[] = [];
|
||||
let nested: string | undefined;
|
||||
for (let child of n.children!.slice(1)) {
|
||||
if (child.type === "OrderedList" || child.type === "BulletList") {
|
||||
nested = renderToText(child);
|
||||
break;
|
||||
}
|
||||
textNodes.push(child);
|
||||
}
|
||||
let item = textNodes.map(renderToText).join("").trim();
|
||||
let value: Item = {
|
||||
name: item,
|
||||
};
|
||||
if (nested) {
|
||||
value.nested = nested;
|
||||
}
|
||||
items.push({
|
||||
key: `it:${n.from}`,
|
||||
value,
|
||||
});
|
||||
});
|
||||
console.log("Found", items.length, "item(s)");
|
||||
await batchSet(name, items);
|
||||
}
|
||||
|
||||
export async function queryProvider({
|
||||
query,
|
||||
}: QueryProviderEvent): Promise<string> {
|
||||
let allItems: Item[] = [];
|
||||
for (let { key, page, value } of await scanPrefixGlobal("it:")) {
|
||||
let [, pos] = key.split(":");
|
||||
allItems.push({
|
||||
...value,
|
||||
page: page,
|
||||
pos: +pos,
|
||||
});
|
||||
}
|
||||
let markdownItems = applyQuery(query, allItems).map(
|
||||
(item) =>
|
||||
`* [[${item.page}@${item.pos}]] ${item.name}` +
|
||||
(item.nested ? "\n " + item.nested : "")
|
||||
);
|
||||
return markdownItems.join("\n");
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import {
|
||||
getCursor,
|
||||
getText,
|
||||
insertAtPos,
|
||||
replaceRange,
|
||||
} from "plugos-silverbullet-syscall/editor";
|
||||
|
||||
export async function toggleH1() {
|
||||
await togglePrefix("# ");
|
||||
}
|
||||
|
||||
export async function toggleH2() {
|
||||
await togglePrefix("## ");
|
||||
}
|
||||
|
||||
function lookBack(s: string, pos: number, backString: string): boolean {
|
||||
return s.substring(pos - backString.length, pos) === backString;
|
||||
}
|
||||
|
||||
async function togglePrefix(prefix: string) {
|
||||
let text = await getText();
|
||||
let pos = await getCursor();
|
||||
if (text[pos] === "\n") {
|
||||
pos--;
|
||||
}
|
||||
while (pos > 0 && text[pos] !== "\n") {
|
||||
if (lookBack(text, pos, prefix)) {
|
||||
// Already has this prefix, let's flip it
|
||||
await replaceRange(pos - prefix.length, pos, "");
|
||||
return;
|
||||
}
|
||||
pos--;
|
||||
}
|
||||
if (pos) {
|
||||
pos++;
|
||||
}
|
||||
await insertAtPos(prefix, pos);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import { ClickEvent } from "../../webapp/app_event";
|
||||
import { getCursor, getText, navigate as navigateTo, openUrl } from "plugos-silverbullet-syscall/editor";
|
||||
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
|
||||
import { nodeAtPos, ParseTree } from "../../common/tree";
|
||||
|
||||
const materializedQueryPrefix = /<!--\s*#query\s+/;
|
||||
|
||||
async function actionClickOrActionEnter(mdTree: ParseTree | null) {
|
||||
if (!mdTree) {
|
||||
return;
|
||||
}
|
||||
console.log("Attempting to navigate based on syntax node", mdTree);
|
||||
switch (mdTree.type) {
|
||||
case "WikiLinkPage":
|
||||
let pageLink = mdTree.children![0].text!;
|
||||
let pos = "0";
|
||||
if (pageLink.includes("@")) {
|
||||
[pageLink, pos] = pageLink.split("@");
|
||||
}
|
||||
await navigateTo(pageLink, +pos);
|
||||
break;
|
||||
case "URL":
|
||||
case "NakedURL":
|
||||
await openUrl(mdTree.children![0].text!);
|
||||
break;
|
||||
case "Link":
|
||||
await openUrl(mdTree.children![4].children![0].text!);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export async function linkNavigate() {
|
||||
let mdTree = await parseMarkdown(await getText());
|
||||
let newNode = nodeAtPos(mdTree, await getCursor());
|
||||
await actionClickOrActionEnter(newNode);
|
||||
}
|
||||
|
||||
export async function clickNavigate(event: ClickEvent) {
|
||||
// Navigate by default, don't navigate when Ctrl or Cmd is held
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
return;
|
||||
}
|
||||
let mdTree = await parseMarkdown(await getText());
|
||||
let newNode = nodeAtPos(mdTree, event.pos);
|
||||
await actionClickOrActionEnter(newNode);
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
import { IndexEvent, IndexTreeEvent } from "../../webapp/app_event";
|
||||
import {
|
||||
batchSet,
|
||||
clearPageIndex as clearPageIndexSyscall,
|
||||
clearPageIndexForPage,
|
||||
scanPrefixGlobal,
|
||||
set
|
||||
} from "plugos-silverbullet-syscall/index";
|
||||
import {
|
||||
flashNotification,
|
||||
getCurrentPage,
|
||||
getText,
|
||||
matchBefore,
|
||||
navigate,
|
||||
prompt
|
||||
} from "plugos-silverbullet-syscall/editor";
|
||||
|
||||
import { dispatch } from "plugos-syscall/event";
|
||||
import { deletePage as deletePageSyscall, listPages, readPage, writePage } from "plugos-silverbullet-syscall/space";
|
||||
import { invokeFunction } from "plugos-silverbullet-syscall/system";
|
||||
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
|
||||
import {
|
||||
addParentPointers,
|
||||
collectNodesMatching,
|
||||
ParseTree,
|
||||
renderToText,
|
||||
replaceNodesMatching
|
||||
} from "../../common/tree";
|
||||
import { applyQuery, QueryProviderEvent } from "../query/engine";
|
||||
import { PageMeta } from "../../common/types";
|
||||
import { extractMeta } from "../query/data";
|
||||
import { jsonToMDTable } from "../query/util";
|
||||
|
||||
// Key space:
|
||||
// pl:toPage:pos => pageName
|
||||
// meta => metaJson
|
||||
|
||||
export async function indexLinks({ name, tree }: IndexTreeEvent) {
|
||||
let backLinks: { key: string; value: string }[] = [];
|
||||
// [[Style Links]]
|
||||
console.log("Now indexing", name);
|
||||
let pageMeta = extractMeta(tree);
|
||||
if (Object.keys(pageMeta).length > 0) {
|
||||
await set(name, "meta:", pageMeta);
|
||||
}
|
||||
|
||||
collectNodesMatching(tree, (n) => n.type === "WikiLinkPage").forEach((n) => {
|
||||
let toPage = n.children![0].text!;
|
||||
if (toPage.includes("@")) {
|
||||
toPage = toPage.split("@")[0];
|
||||
}
|
||||
backLinks.push({
|
||||
key: `pl:${toPage}:${n.from}`,
|
||||
value: name,
|
||||
});
|
||||
});
|
||||
console.log("Found", backLinks.length, "wiki link(s)");
|
||||
await batchSet(name, backLinks);
|
||||
}
|
||||
|
||||
export async function pageQueryProvider({
|
||||
query,
|
||||
}: QueryProviderEvent): Promise<string> {
|
||||
let allPages = await listPages();
|
||||
if (query.select) {
|
||||
let allPageMap: Map<string, any> = new Map(
|
||||
allPages.map((pm) => [pm.name, pm])
|
||||
);
|
||||
for (let { page, value } of await scanPrefixGlobal("meta:")) {
|
||||
let p = allPageMap.get(page);
|
||||
if (p) {
|
||||
for (let [k, v] of Object.entries(value)) {
|
||||
p[k] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
allPages = [...allPageMap.values()];
|
||||
return jsonToMDTable(applyQuery(query, allPages), (k, v) =>
|
||||
k === "name" ? `[[${v}]]` : v
|
||||
);
|
||||
} else {
|
||||
return applyQuery(query, allPages)
|
||||
.map((pageMeta: PageMeta) => `* [[${pageMeta.name}]]`)
|
||||
.join("\n");
|
||||
}
|
||||
}
|
||||
|
||||
export async function linkQueryProvider({
|
||||
query,
|
||||
pageName,
|
||||
}: QueryProviderEvent): Promise<string> {
|
||||
let uniqueLinks = new Set<string>();
|
||||
for (let { value: name } of await scanPrefixGlobal(`pl:${pageName}:`)) {
|
||||
uniqueLinks.add(name);
|
||||
}
|
||||
let markdownLinks = applyQuery(
|
||||
query,
|
||||
[...uniqueLinks].map((l) => ({ name: l }))
|
||||
).map((pageMeta) => `* [[${pageMeta.name}]]`);
|
||||
return markdownLinks.join("\n");
|
||||
}
|
||||
|
||||
export async function deletePage() {
|
||||
let pageName = await getCurrentPage();
|
||||
console.log("Navigating to start page");
|
||||
await navigate("start");
|
||||
console.log("Deleting page from space");
|
||||
await deletePageSyscall(pageName);
|
||||
}
|
||||
|
||||
export async function renamePage() {
|
||||
const oldName = await getCurrentPage();
|
||||
console.log("Old name is", oldName);
|
||||
const newName = await prompt(`Rename ${oldName} to:`, oldName);
|
||||
if (!newName) {
|
||||
return;
|
||||
}
|
||||
console.log("New name", newName);
|
||||
|
||||
let pagesToUpdate = await getBackLinks(oldName);
|
||||
console.log("All pages containing backlinks", pagesToUpdate);
|
||||
|
||||
let text = await getText();
|
||||
console.log("Writing new page to space");
|
||||
await writePage(newName, text);
|
||||
console.log("Navigating to new page");
|
||||
await navigate(newName);
|
||||
console.log("Deleting page from space");
|
||||
await deletePageSyscall(oldName);
|
||||
|
||||
let pageToUpdateSet = new Set<string>();
|
||||
for (let pageToUpdate of pagesToUpdate) {
|
||||
pageToUpdateSet.add(pageToUpdate.page);
|
||||
}
|
||||
|
||||
for (let pageToUpdate of pageToUpdateSet) {
|
||||
if (pageToUpdate === oldName) {
|
||||
continue;
|
||||
}
|
||||
console.log("Now going to update links in", pageToUpdate);
|
||||
let { text } = await readPage(pageToUpdate);
|
||||
// console.log("Received text", text);
|
||||
if (!text) {
|
||||
// Page likely does not exist, but at least we can skip it
|
||||
continue;
|
||||
}
|
||||
let mdTree = await parseMarkdown(text);
|
||||
addParentPointers(mdTree);
|
||||
replaceNodesMatching(mdTree, (n): ParseTree | undefined | null => {
|
||||
if (n.type === "WikiLinkPage") {
|
||||
let pageName = n.children![0].text!;
|
||||
if (pageName === oldName) {
|
||||
n.children![0].text = newName;
|
||||
return n;
|
||||
}
|
||||
// page name with @pos position
|
||||
if (pageName.startsWith(`${oldName}@`)) {
|
||||
let [, pos] = pageName.split("@");
|
||||
n.children![0].text = `${newName}@${pos}`;
|
||||
return n;
|
||||
}
|
||||
}
|
||||
return;
|
||||
});
|
||||
// let newText = text.replaceAll(`[[${oldName}]]`, `[[${newName}]]`);
|
||||
let newText = renderToText(mdTree);
|
||||
if (text !== newText) {
|
||||
console.log("Changes made, saving...");
|
||||
await writePage(pageToUpdate, newText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type BackLink = {
|
||||
page: string;
|
||||
pos: number;
|
||||
};
|
||||
|
||||
async function getBackLinks(pageName: string): Promise<BackLink[]> {
|
||||
let allBackLinks = await scanPrefixGlobal(`pl:${pageName}:`);
|
||||
let pagesToUpdate: BackLink[] = [];
|
||||
for (let { key, value } of allBackLinks) {
|
||||
let keyParts = key.split(":");
|
||||
pagesToUpdate.push({
|
||||
page: value,
|
||||
pos: +keyParts[keyParts.length - 1],
|
||||
});
|
||||
}
|
||||
return pagesToUpdate;
|
||||
}
|
||||
|
||||
export async function reindexCommand() {
|
||||
await flashNotification("Reindexing...");
|
||||
await invokeFunction("server", "reindexSpace");
|
||||
await flashNotification("Reindexing done");
|
||||
}
|
||||
|
||||
// Completion
|
||||
export async function pageComplete() {
|
||||
let prefix = await matchBefore("\\[\\[[\\w\\s]*");
|
||||
if (!prefix) {
|
||||
return null;
|
||||
}
|
||||
let allPages = await listPages();
|
||||
return {
|
||||
from: prefix.from + 2,
|
||||
options: allPages.map((pageMeta) => ({
|
||||
label: pageMeta.name,
|
||||
type: "page",
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
// Server functions
|
||||
export async function reindexSpace() {
|
||||
console.log("Clearing page index...");
|
||||
await clearPageIndexSyscall();
|
||||
console.log("Listing all pages");
|
||||
let pages = await listPages();
|
||||
for (let { name } of pages) {
|
||||
console.log("Indexing", name);
|
||||
const { text } = await readPage(name);
|
||||
let parsed = await parseMarkdown(text);
|
||||
await dispatch("page:index", {
|
||||
name,
|
||||
tree: parsed,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function clearPageIndex(page: string) {
|
||||
console.log("Clearing page index for page", page);
|
||||
await clearPageIndexForPage(page);
|
||||
}
|
||||
|
||||
export async function parseIndexTextRepublish({ name, text }: IndexEvent) {
|
||||
await dispatch("page:index", {
|
||||
name,
|
||||
tree: await parseMarkdown(text),
|
||||
});
|
||||
}
|
||||
|
||||
export async function parseServerPageCommand() {
|
||||
console.log(await invokeFunction("server", "parsePage", await getText()));
|
||||
}
|
||||
|
||||
export async function parsePageCommand() {
|
||||
parsePage(await getText());
|
||||
}
|
||||
|
||||
export async function parsePage(text: string) {
|
||||
console.log("AST", JSON.stringify(await parseMarkdown(text), null, 2));
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { EndpointRequest, EndpointResponse } from "../../plugos/hooks/endpoint";
|
||||
|
||||
export function endpointTest(req: EndpointRequest): EndpointResponse {
|
||||
console.log("I'm running on the server!", req);
|
||||
return {
|
||||
status: 200,
|
||||
body: "Hello world!",
|
||||
};
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import { listPages, readPage, writePage } from "plugos-silverbullet-syscall/space";
|
||||
import { filterBox, navigate, prompt } from "plugos-silverbullet-syscall/editor";
|
||||
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
|
||||
import { extractMeta } from "../query/data";
|
||||
import { renderToText } from "../../common/tree";
|
||||
import { niceDate } from "./dates";
|
||||
|
||||
const pageTemplatePrefix = `template/page/`;
|
||||
|
||||
export async function instantiateTemplateCommand() {
|
||||
let allPages = await listPages();
|
||||
let allPageTemplates = allPages.filter((pageMeta) =>
|
||||
pageMeta.name.startsWith(pageTemplatePrefix)
|
||||
);
|
||||
|
||||
let selectedTemplate = await filterBox(
|
||||
"Template",
|
||||
allPageTemplates,
|
||||
"Select the template to create a new page from"
|
||||
);
|
||||
|
||||
if (!selectedTemplate) {
|
||||
return;
|
||||
}
|
||||
console.log("Selected template", selectedTemplate);
|
||||
|
||||
let { text } = await readPage(selectedTemplate.name);
|
||||
|
||||
let parseTree = await parseMarkdown(text);
|
||||
let additionalPageMeta = extractMeta(parseTree, true);
|
||||
console.log("Page meta", additionalPageMeta);
|
||||
|
||||
let pageName = await prompt("Name of new page", additionalPageMeta.name);
|
||||
if (!pageName) {
|
||||
return;
|
||||
}
|
||||
let pageText = replaceTemplateVars(renderToText(parseTree), pageName);
|
||||
await writePage(pageName, pageText);
|
||||
await navigate(pageName);
|
||||
}
|
||||
|
||||
export function replaceTemplateVars(s: string, pageName: string): string {
|
||||
return s.replaceAll(/\{\{([^\}]+)\}\}/g, (match, v) => {
|
||||
switch (v) {
|
||||
case "today":
|
||||
return niceDate(new Date());
|
||||
case "yesterday":
|
||||
let yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
return niceDate(yesterday);
|
||||
case "lastWeek":
|
||||
let lastWeek = new Date();
|
||||
lastWeek.setDate(lastWeek.getDate() - 7);
|
||||
return niceDate(lastWeek);
|
||||
}
|
||||
return match;
|
||||
});
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
function countWords(str: string): number {
|
||||
const matches = str.match(/[\w\d\'-]+/gi);
|
||||
return matches ? matches.length : 0;
|
||||
}
|
||||
|
||||
function readingTime(wordCount: number): number {
|
||||
// 225 is average word reading speed for adults
|
||||
return Math.ceil(wordCount / 225);
|
||||
}
|
||||
Reference in New Issue
Block a user