Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(playground): compress shared example #3501

Merged
merged 1 commit into from
Sep 21, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 61 additions & 28 deletions packages/brick-playground/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ registerTypeScript(monaco);
registerYaml(monaco);
registerHtml(monaco);

const GZIP_HASH_PREFIX = "#gzip,";

interface Example extends Sources {
key: string;
mode: "html" | "yaml";
Expand Down Expand Up @@ -179,9 +181,23 @@ async function main() {
if (matchedExample) {
initEditorsWith(matchedExample);
} else if (codeFromHash) {
let decompressedExampleString: string | undefined;
if (location.hash.startsWith(GZIP_HASH_PREFIX)) {
try {
decompressedExampleString = await decompress(
location.hash.substring(GZIP_HASH_PREFIX.length)
);
} catch (e) {
// eslint-disable-next-line no-console
console.error("Decompress shared example failed:", e);
}
}

let pastedSources: Example;
try {
pastedSources = JSON.parse(b64DecodeUnicode(location.hash.slice(1)));
pastedSources = JSON.parse(
decompressedExampleString ?? b64DecodeUnicode(location.hash.slice(1))
);
} catch (error) {
// eslint-disable-next-line no-console
console.error("Parse pasted sources failed:", error);
Expand Down Expand Up @@ -238,6 +254,9 @@ async function main() {
previewWin = iframe.contentWindow as unknown as PreviewWindow;
resolve();
});
iframe.addEventListener("error", (reason) => {
reject(reason);
});
});

iframe.src = "./preview/";
Expand Down Expand Up @@ -283,46 +302,60 @@ async function main() {
const shareButton = document.querySelector("#brick-playground-button-share");
const shareResult = document.querySelector("#brick-playground-share-result");
let shareButtonResetTimeout = -1;
shareButton.addEventListener("click", () => {
shareButton.addEventListener("click", async () => {
if (shareButtonResetTimeout !== -1) {
shareResult.textContent = "";
clearTimeout(shareButtonResetTimeout);
}
history.replaceState(
null,
"",
`?mode=${mode}#${b64EncodeUnicode(
JSON.stringify({ [mode]: sources[mode] })
)}`
);
const result = copy(location.href);
shareResult.textContent = result ? "URL copied" : "Failed to copy URL";
let ok = false;
try {
history.replaceState(
null,
"",
`?mode=${mode}${GZIP_HASH_PREFIX}${await compress(
JSON.stringify({ [mode]: sources[mode] })
)}`
);
ok = copy(location.href);
} catch (e) {
// eslint-disable-next-line no-console
console.error("Compress shared example failed:", e);
}
shareResult.textContent = ok ? "URL copied" : "Failed to copy URL";
shareButtonResetTimeout = setTimeout(() => {
shareButtonResetTimeout = -1;
shareResult.textContent = "";
}, 2000) as unknown as number;
});
}

function b64EncodeUnicode(str: string) {
// first we use encodeURIComponent to get percent-encoded UTF-8,
// then we convert the percent encodings into raw bytes which
// can be fed into btoa.
return btoa(
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode(parseInt(p1, 16));
})
);
function base64ToBytes(base64: string): Uint8Array {
const binString = atob(base64);
return Uint8Array.from(binString, (m) => m.codePointAt(0));
}

function b64DecodeUnicode(str: string) {
return decodeURIComponent(
[...atob(str)]
.map(function (c) {
return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
})
.join("")
);
function bytesToBase64(bytes: Uint8Array): string {
const binString = Array.from(bytes, (x) => String.fromCodePoint(x)).join("");
return btoa(binString);
}

async function compress(str: string) {
const blob = new Blob([str], { type: "text/plain" });
const stream = blob.stream().pipeThrough(new CompressionStream("gzip"));
const compressedBlob = await new Response(stream).blob();
return bytesToBase64(new Uint8Array(await compressedBlob.arrayBuffer()));
}

async function decompress(str: string) {
const bytes = base64ToBytes(str);
const blob = new Blob([bytes], { type: "text/plain" });
const stream = blob.stream().pipeThrough(new DecompressionStream("gzip"));
const decompressedBlob = await new Response(stream).blob();
return decompressedBlob.text();
}

function b64DecodeUnicode(str: string): string {
return new TextDecoder().decode(base64ToBytes(str));
}

function decorateAltCode(code: string, mode: string, altMode: string): string {
Expand Down