1
0
silverbullet/plugs/collab/collab.ts
Zef Hemel 7c825348b2
Big refactors and fixes
* Query regen
* Fix anchor completion
* Dependency fixes
* Changelog update
2023-07-02 11:25:32 +02:00

159 lines
3.9 KiB
TypeScript

import {
findNodeOfType,
removeParentPointers,
renderToText,
} from "$sb/lib/tree.ts";
import { getText } from "$sb/silverbullet-syscall/editor.ts";
import { parseMarkdown } from "$sb/silverbullet-syscall/markdown.ts";
import {
extractFrontmatter,
prepareFrontmatterDispatch,
} from "$sb/lib/frontmatter.ts";
import { store, YAML } from "$sb/plugos-syscall/mod.ts";
import { collab, editor, markdown } from "$sb/silverbullet-syscall/mod.ts";
import { nanoid } from "https://esm.sh/nanoid@4.0.0";
import { FileMeta } from "../../common/types.ts";
const defaultServer = "wss://collab.silverbullet.md";
async function ensureUsername(): Promise<string> {
let username = await store.get("collabUsername");
if (!username) {
username = await editor.prompt(
"Please enter a publicly visible user name (or cancel for 'anonymous'):",
);
if (!username) {
return "anonymous";
} else {
await store.set("collabUsername", username);
}
}
return username;
}
export async function joinCommand() {
let collabUri = await editor.prompt(
"Collab share URI:",
);
if (!collabUri) {
return;
}
if (!collabUri.startsWith("collab:")) {
collabUri = "collab:" + collabUri;
}
await editor.navigate(collabUri);
}
export async function shareCommand() {
const serverUrl = await editor.prompt(
"Please enter the URL of the collab server to use:",
defaultServer,
);
if (!serverUrl) {
return;
}
const roomId = nanoid().replaceAll("_", "-");
await editor.save();
const text = await editor.getText();
const tree = await markdown.parseMarkdown(text);
let { $share } = await extractFrontmatter(tree);
if (!$share) {
$share = [];
}
if (!Array.isArray($share)) {
$share = [$share];
}
removeParentPointers(tree);
const dispatchData = await prepareFrontmatterDispatch(tree, {
$share: [...$share, `collab:${serverUrl}/${roomId}`],
});
await editor.dispatch(dispatchData);
collab.start(
serverUrl,
roomId,
await ensureUsername(),
);
}
export async function detectPage() {
const tree = await parseMarkdown(await getText());
const frontMatter = findNodeOfType(tree, "FrontMatter");
if (frontMatter) {
const yamlText = renderToText(frontMatter.children![1].children![0]);
try {
let { $share } = await YAML.parse(yamlText) as any;
if (!$share) {
return;
}
if (!Array.isArray($share)) {
$share = [$share];
}
for (const uri of $share) {
if (uri.startsWith("collab:")) {
console.log("Going to enable collab");
const uriPieces = uri.substring("collab:".length).split("/");
await collab.start(
// All parts except the last one
uriPieces.slice(0, uriPieces.length - 1).join("/"),
// because the last one is the room ID
uriPieces[uriPieces.length - 1],
await ensureUsername(),
);
}
}
} catch (e) {
console.error("Error parsing YAML", e);
}
}
}
export function shareNoop() {
return true;
}
export function readFileCollab(
name: string,
): { data: Uint8Array; meta: FileMeta } {
if (!name.endsWith(".md")) {
throw new Error("Not found");
}
const collabUri = name.substring(0, name.length - ".md".length);
const text = `---\n$share: ${collabUri}\n---\n`;
return {
// encoding === "arraybuffer" is not an option, so either it's "utf8" or "dataurl"
data: new TextEncoder().encode(text),
meta: {
name,
contentType: "text/markdown",
size: text.length,
lastModified: 0,
perm: "rw",
},
};
}
export function getFileMetaCollab(name: string): FileMeta {
return {
name,
contentType: "text/markdown",
size: -1,
lastModified: 0,
perm: "rw",
};
}
export function writeFileCollab(name: string): FileMeta {
return {
name,
contentType: "text/markdown",
size: -1,
lastModified: 0,
perm: "rw",
};
}