Work on $share
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
name: collab
|
||||
imports:
|
||||
- https://get.silverbullet.md/global.plug.json
|
||||
functions:
|
||||
detectCollabPage:
|
||||
path: "./collab.ts:detectPage"
|
||||
events:
|
||||
- editor:pageLoaded
|
||||
- plugs:loaded
|
||||
joinCommand:
|
||||
path: "./collab.ts:joinCommand"
|
||||
command:
|
||||
name: "Share: Join Collab"
|
||||
shareCommand:
|
||||
path: "./collab.ts:shareCommand"
|
||||
command:
|
||||
name: "Share: Collab"
|
||||
readPageCollab:
|
||||
path: ./collab.ts:readFileCollab
|
||||
env: client
|
||||
pageNamespace:
|
||||
pattern: "collab:.+"
|
||||
operation: readFile
|
||||
writePageCollab:
|
||||
path: ./collab.ts:writeFileCollab
|
||||
env: client
|
||||
pageNamespace:
|
||||
pattern: "collab:.+"
|
||||
operation: writeFile
|
||||
getPageMetaCollab:
|
||||
path: ./collab.ts:getFileMetaCollab
|
||||
env: client
|
||||
pageNamespace:
|
||||
pattern: "collab:.+"
|
||||
operation: getFileMeta
|
||||
@@ -0,0 +1,169 @@
|
||||
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 * as YAML from "yaml";
|
||||
import {
|
||||
clientStore,
|
||||
collab,
|
||||
editor,
|
||||
markdown,
|
||||
space,
|
||||
} from "$sb/silverbullet-syscall/mod.ts";
|
||||
|
||||
import { nanoid } from "https://esm.sh/nanoid@4.0.0";
|
||||
import {
|
||||
FileData,
|
||||
FileEncoding,
|
||||
} from "../../common/spaces/space_primitives.ts";
|
||||
import { FileMeta } from "../../common/types.ts";
|
||||
import { base64EncodedDataUrl } from "../../plugos/asset_bundle/base64.ts";
|
||||
|
||||
const defaultServer = "wss://collab.silverbullet.md";
|
||||
|
||||
async function ensureUsername(): Promise<string> {
|
||||
let username = await clientStore.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 clientStore.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();
|
||||
await editor.save();
|
||||
const text = await editor.getText();
|
||||
const tree = await markdown.parseMarkdown(text);
|
||||
let { $share } = extractFrontmatter(tree);
|
||||
if (!$share) {
|
||||
$share = [];
|
||||
}
|
||||
if (!Array.isArray($share)) {
|
||||
$share = [$share];
|
||||
}
|
||||
|
||||
removeParentPointers(tree);
|
||||
const dispatchData = 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 } = 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 async function readFileCollab(
|
||||
name: string,
|
||||
encoding: FileEncoding,
|
||||
): Promise<{ data: FileData; meta: FileMeta }> {
|
||||
if (!name.endsWith(".md")) {
|
||||
throw new Error("File 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 "string" or "dataurl"
|
||||
data: encoding === "string" ? text : base64EncodedDataUrl(
|
||||
"text/markdown",
|
||||
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",
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user