No longer serialize binary blobs as data URLs

pull/417/head
Zef Hemel 2023-05-26 14:04:32 +02:00
parent 25c789538d
commit 1a1b942f92
10 changed files with 34 additions and 94 deletions

View File

@ -42,23 +42,21 @@ export function getAttachmentMeta(name: string): Promise<AttachmentMeta> {
*/ */
export function readAttachment( export function readAttachment(
name: string, name: string,
): Promise<string> { ): Promise<{ data: Uint8Array; meta: AttachmentMeta }> {
return syscall("space.readAttachment", name); return syscall("space.readAttachment", name);
} }
/** /**
* Writes an attachment to the space * Writes an attachment to the space
* @param name path of the attachment to write * @param name path of the attachment to write
* @param encoding encoding of the data ("utf8" or "dataurl)
* @param data data itself * @param data data itself
* @returns * @returns
*/ */
export function writeAttachment( export function writeAttachment(
name: string, name: string,
encoding: "utf8" | "dataurl", data: Uint8Array,
data: string,
): Promise<AttachmentMeta> { ): Promise<AttachmentMeta> {
return syscall("space.writeAttachment", name, encoding, data); return syscall("space.writeAttachment", name, data);
} }
/** /**

View File

@ -77,9 +77,6 @@ async function actionClickOrActionEnter(
} }
if (url.indexOf("://") === -1 && !url.startsWith("mailto:")) { if (url.indexOf("://") === -1 && !url.startsWith("mailto:")) {
url = decodeURIComponent(url); url = decodeURIComponent(url);
// // attachment URL, let's fetch as a data url
// const dataUrl = await space.readAttachment(url);
// return editor.downloadFile(url, dataUrl);
return editor.openUrl(`/.fs/${url}`); return editor.openUrl(`/.fs/${url}`);
} else { } else {
await editor.openUrl(url); await editor.openUrl(url);

View File

@ -57,8 +57,7 @@ export async function updatePlugsCommand() {
// console.log("Writing", `_plug/${plugName}.plug.js`, workerCode); // console.log("Writing", `_plug/${plugName}.plug.js`, workerCode);
await space.writeAttachment( await space.writeAttachment(
`_plug/${plugName}.plug.js`, `_plug/${plugName}.plug.js`,
"utf8", new TextEncoder().encode(workerCode),
workerCode,
); );
} }

View File

@ -12,7 +12,7 @@ type MarkdownRenderOptions = {
annotationPositions?: true; annotationPositions?: true;
attachmentUrlPrefix?: string; attachmentUrlPrefix?: string;
// When defined, use to inline images as data: urls // When defined, use to inline images as data: urls
inlineAttachments?: (url: string) => Promise<string>; inlineAttachments?: (url: string) => string;
}; };
function cleanTags(values: (Tag | null)[]): Tag[] { function cleanTags(values: (Tag | null)[]): Tag[] {
@ -364,34 +364,34 @@ function render(
} }
} }
async function traverseTag( function traverseTag(
t: Tag, t: Tag,
fn: (t: Tag) => Promise<void>, fn: (t: Tag) => void,
): Promise<void> { ) {
await fn(t); fn(t);
if (typeof t === "string") { if (typeof t === "string") {
return; return;
} }
if (t.body) { if (t.body) {
for (const child of t.body) { for (const child of t.body) {
await traverseTag(child, fn); traverseTag(child, fn);
} }
} }
} }
export async function renderMarkdownToHtml( export function renderMarkdownToHtml(
t: ParseTree, t: ParseTree,
options: MarkdownRenderOptions = {}, options: MarkdownRenderOptions = {},
) { ) {
preprocess(t, options); preprocess(t, options);
const htmlTree = posPreservingRender(t, options); const htmlTree = posPreservingRender(t, options);
if (htmlTree && options.inlineAttachments) { if (htmlTree && options.inlineAttachments) {
await traverseTag(htmlTree, async (t) => { traverseTag(htmlTree, (t) => {
if (typeof t === "string") { if (typeof t === "string") {
return; return;
} }
if (t.name === "img") { if (t.name === "img") {
t.attrs!.src = await options.inlineAttachments!(t.attrs!.src!); t.attrs!.src = options.inlineAttachments!(t.attrs!.src!);
} }
}); });
} }

View File

@ -12,17 +12,12 @@ export async function updateMarkdownPreview() {
// const cleanMd = await cleanMarkdown(text); // const cleanMd = await cleanMarkdown(text);
const css = await asset.readAsset("assets/styles.css"); const css = await asset.readAsset("assets/styles.css");
const js = await asset.readAsset("assets/handler.js"); const js = await asset.readAsset("assets/handler.js");
const html = await renderMarkdownToHtml(mdTree, { const html = renderMarkdownToHtml(mdTree, {
smartHardBreak: true, smartHardBreak: true,
annotationPositions: true, annotationPositions: true,
inlineAttachments: async (url): Promise<string> => { inlineAttachments: (url) => {
if (!url.includes("://")) { if (!url.includes("://")) {
try { return `/.fs/${url}`;
return await space.readAttachment(url);
} catch (e: any) {
console.error(e);
return url;
}
} }
return url; return url;
}, },

View File

@ -180,7 +180,7 @@ export function attachmentExtension(editor: Editor) {
if (!finalFileName) { if (!finalFileName) {
return; return;
} }
await editor.space.writeAttachment(finalFileName, "arraybuffer", data!); await editor.space.writeAttachment(finalFileName, new Uint8Array(data));
let attachmentMarkdown = `[${finalFileName}](${ let attachmentMarkdown = `[${finalFileName}](${
encodeURIComponent(finalFileName) encodeURIComponent(finalFileName)
})`; })`;

View File

@ -33,24 +33,16 @@ class TableViewWidget extends WidgetType {
}); });
}); });
renderMarkdownToHtml(this.t, { dom.innerHTML = renderMarkdownToHtml(this.t, {
// Annotate every element with its position so we can use it to put // Annotate every element with its position so we can use it to put
// the cursor there when the user clicks on the table. // the cursor there when the user clicks on the table.
annotationPositions: true, annotationPositions: true,
inlineAttachments: async (url): Promise<string> => { inlineAttachments: (url) => {
if (!url.includes("://")) { if (!url.includes("://")) {
try { return `/.fs/${url}`;
const d = await this.editor.space.readAttachment(url, "dataurl");
return d.data as string;
} catch (e: any) {
console.error(e);
return url;
}
} }
return url; return url;
}, },
}).then((html) => {
dom.innerHTML = html;
}); });
return dom; return dom;
} }

View File

@ -3,7 +3,6 @@ import { mime } from "https://deno.land/x/mimetypes@v1.0.0/mod.ts";
import type { FileContent } from "../common/spaces/indexeddb_space_primitives.ts"; import type { FileContent } from "../common/spaces/indexeddb_space_primitives.ts";
import { simpleHash } from "../common/crypto.ts"; import { simpleHash } from "../common/crypto.ts";
import { clientStore } from "../plug-api/silverbullet-syscall/mod.ts";
const CACHE_NAME = "{{CACHE_NAME}}"; const CACHE_NAME = "{{CACHE_NAME}}";

View File

@ -10,9 +10,6 @@ import {
import { mime } from "./deps.ts"; import { mime } from "./deps.ts";
import { AttachmentMeta, PageMeta } from "./types.ts"; import { AttachmentMeta, PageMeta } from "./types.ts";
export type FileEncoding = "utf8" | "arraybuffer" | "dataurl";
export type FileData = ArrayBuffer | string;
export type SpaceEvents = { export type SpaceEvents = {
pageCreated: (meta: PageMeta) => void; pageCreated: (meta: PageMeta) => void;
pageChanged: (meta: PageMeta) => void; pageChanged: (meta: PageMeta) => void;
@ -190,31 +187,12 @@ export class Space extends EventEmitter<SpaceEvents> {
/** /**
* Reads an attachment * Reads an attachment
* @param name path of the attachment * @param name path of the attachment
* @param encoding how the return value is expected to be encoded
* @returns * @returns
*/ */
async readAttachment( readAttachment(
name: string, name: string,
encoding: FileEncoding, ): Promise<{ data: Uint8Array; meta: AttachmentMeta }> {
): Promise<{ data: FileData; meta: AttachmentMeta }> { return this.spacePrimitives.readFile(name);
const { data, meta } = await this.spacePrimitives.readFile(name);
switch (encoding) {
case "arraybuffer":
return { data, meta };
case "dataurl":
return {
data: base64EncodedDataUrl(
mime.getType(name) || "application/octet-stream",
data,
),
meta,
};
case "utf8":
return {
data: new TextDecoder().decode(data),
meta,
};
}
} }
getAttachmentMeta(name: string): Promise<AttachmentMeta> { getAttachmentMeta(name: string): Promise<AttachmentMeta> {
@ -223,30 +201,14 @@ export class Space extends EventEmitter<SpaceEvents> {
writeAttachment( writeAttachment(
name: string, name: string,
encoding: FileEncoding, data: Uint8Array,
data: FileData,
selfUpdate?: boolean | undefined, selfUpdate?: boolean | undefined,
): Promise<AttachmentMeta> { ): Promise<AttachmentMeta> {
switch (encoding) { return this.spacePrimitives.writeFile(
case "arraybuffer": name,
return this.spacePrimitives.writeFile( data as Uint8Array,
name, selfUpdate,
data as Uint8Array, );
selfUpdate,
);
case "dataurl":
return this.spacePrimitives.writeFile(
name,
base64DecodeDataUrl(data as string),
selfUpdate,
);
case "utf8":
return this.spacePrimitives.writeFile(
name,
new TextEncoder().encode(data as string),
selfUpdate,
);
}
} }
deleteAttachment(name: string): Promise<void> { deleteAttachment(name: string): Promise<void> {

View File

@ -1,7 +1,6 @@
import { Editor } from "../editor.tsx"; import { Editor } from "../editor.tsx";
import { SysCallMapping } from "../../plugos/system.ts"; import { SysCallMapping } from "../../plugos/system.ts";
import { AttachmentMeta, PageMeta } from "../types.ts"; import { AttachmentMeta, PageMeta } from "../types.ts";
import { FileData, FileEncoding } from "../space.ts";
export function spaceSyscalls(editor: Editor): SysCallMapping { export function spaceSyscalls(editor: Editor): SysCallMapping {
const space = editor.space; const space = editor.space;
@ -44,8 +43,8 @@ export function spaceSyscalls(editor: Editor): SysCallMapping {
"space.readAttachment": async ( "space.readAttachment": async (
_ctx, _ctx,
name: string, name: string,
): Promise<FileData> => { ): Promise<Uint8Array> => {
return (await space.readAttachment(name, "dataurl")).data; return (await space.readAttachment(name)).data;
}, },
"space.getAttachmentMeta": async ( "space.getAttachmentMeta": async (
_ctx, _ctx,
@ -53,13 +52,12 @@ export function spaceSyscalls(editor: Editor): SysCallMapping {
): Promise<AttachmentMeta> => { ): Promise<AttachmentMeta> => {
return await space.getAttachmentMeta(name); return await space.getAttachmentMeta(name);
}, },
"space.writeAttachment": async ( "space.writeAttachment": (
_ctx, _ctx,
name: string, name: string,
encoding: FileEncoding, data: Uint8Array,
data: string,
): Promise<AttachmentMeta> => { ): Promise<AttachmentMeta> => {
return await space.writeAttachment(name, encoding, data); return space.writeAttachment(name, data);
}, },
"space.deleteAttachment": async (_ctx, name: string) => { "space.deleteAttachment": async (_ctx, name: string) => {
await space.deleteAttachment(name); await space.deleteAttachment(name);