No longer serialize binary blobs as data URLs
parent
25c789538d
commit
1a1b942f92
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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!);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
},
|
},
|
||||||
|
|
|
@ -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)
|
||||||
})`;
|
})`;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}}";
|
||||||
|
|
||||||
|
|
56
web/space.ts
56
web/space.ts
|
@ -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> {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue