Skip to content

Commit

Permalink
feat(demo): convert in browser
Browse files Browse the repository at this point in the history
and persist config in url
  • Loading branch information
davidenke committed Oct 21, 2024
1 parent 4aeaa7f commit eeda560
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 10 deletions.
10 changes: 7 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<script type="module" src="./src/demo/demo.ts"></script>
<script type="module" src="./src/index.ts"></script>
</head>
<body>
<body onload="window.handleLoad()">
<header>
<h1>Astro Decap Collection Demo</h1>
<p>
Expand Down Expand Up @@ -66,8 +66,12 @@ <h1>Astro Decap Collection Demo</h1>
></textarea>
</div>
<nav>
<button onclick="window.loadExample('./examples/blog.yml')">Blog example</button>
<button onclick="window.loadExample('./examples/relation.yml')">Relation example</button>
<button data-example="./examples/blog.yml" onclick="window.handleClick(event)">
Blog example
</button>
<button data-example="./examples/relation.yml" onclick="window.handleClick(event)">
Relation example
</button>
</nav>
</fieldset>
<fieldset id="config">
Expand Down
62 changes: 55 additions & 7 deletions src/demo/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,26 @@ import zod from 'zod';
import { parseConfig } from '../utils/decap.utils.js';
import { formatCode } from '../utils/format.utils.js';
import { transformCollection } from '../utils/transform.utils.js';
import { compress } from './utils/compress.utils.js';
import { decompress } from './utils/decompress.utils.js';

declare global {
interface Window {
global: Window;
loadExample(path: string): void;
handleLoad(event: Event): void;
handleClick(event: PointerEvent): void;
handleInput(event: InputEvent): Promise<void>;
handleScroll(event: Event): void;

loadExample(path: string): void;
updateExample(data: string): void;
updateInput(from: InputEvent): string;

updatePreview(config: string, schemas: Record<string, string>): void;
clearPreview(): void;

reflectToUrl(config: string, param?: string): Promise<void>;
initFromUrl(param?: string): Promise<void>;
}
}

Expand All @@ -28,12 +38,13 @@ hljs.registerLanguage('yaml', yaml);
// prevent errors from libs using ancient `global` instead of `globalThis`
window.global ||= window;

// loads an example from the `examples` folder
window.loadExample = async (path: string) => {
const input = document.querySelector('textarea')!;
const example = await fetch(path);
input.value = await example.text();
input.dispatchEvent(new InputEvent('input'));
window.handleLoad = () => window.initFromUrl();

window.handleClick = event => {
event.preventDefault();
const { dataset } = event.target as HTMLElement;
if (!dataset.example) return;
window.loadExample(dataset.example);
};

// handle textarea input event
Expand Down Expand Up @@ -62,10 +73,26 @@ window.updateInput = event => {
preview.innerHTML = hljs.highlight(input.value, { language: 'yaml' }).value;
preview.style.height = `${input.scrollHeight}px`;
preview.style.width = `${input.scrollWidth}px`;

window.handleScroll(event);
window.reflectToUrl(input.value);

return input.value;
};

// loads an example from the `examples` folder
window.loadExample = async (path: string) => {
const example = await fetch(path);
const config = await example.text();
window.updateExample(config);
};

window.updateExample = data => {
const input = document.querySelector('textarea')!;
input.value = data;
input.dispatchEvent(new InputEvent('input'));
};

// update the preview code with the config and schemas
window.updatePreview = (config, schemas) => {
const configPreview = document.querySelector<HTMLElement>('#config code')!;
Expand All @@ -90,3 +117,24 @@ window.clearPreview = () => {
const schemaPreview = document.querySelector('#schemas pre')!;
schemaPreview.innerHTML = '<div><code></code></div>';
};

// stores the given string gzip compressed in the URL
window.reflectToUrl = async (config, param = 'c') => {
const url = new URL(location.href);
if (config.trim() === '') {
url.searchParams.delete(param);
} else {
const compressed = await compress(config, 'gzip');
url.searchParams.set(param, compressed);
}
history.replaceState({}, '', url.toString());
};

window.initFromUrl = async (param = 'c') => {
const url = new URL(location.href);
const compressed = url.searchParams.get(param);
if (!compressed) return;

const config = await decompress(compressed, 'gzip');
window.updateExample(config);
};
28 changes: 28 additions & 0 deletions src/demo/utils/compress.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Compress a string with browser native APIs into a binary string representation
*
* @param data - Input string that should be compressed
* @param encoding - Compression algorithm to use
* @returns The compressed binary string
*/
export async function compressRaw(data: string, encoding: CompressionFormat): Promise<string> {
// stream the string through the compressor
const stream = new Blob([new TextEncoder().encode(data)])
.stream()
.pipeThrough(new CompressionStream(encoding));
// convert the stream to an array buffer
const buffer = await new Response(stream).arrayBuffer();
// convert the array buffer to a binary string
return Array.from(new Uint8Array(buffer), x => String.fromCodePoint(x)).join('');
}

/**
* Compress a string with browser native APIs into a string representation
*
* @param data - Input string that should be compressed
* @param encoding - Compression algorithm to use
* @returns The compressed string
*/
export async function compress(data: string, encoding: CompressionFormat): Promise<string> {
return btoa(await compressRaw(data, encoding));
}
26 changes: 26 additions & 0 deletions src/demo/utils/decompress.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Decompress a binary string representation with browser native APIs in to a normal js string
*
* @param binary - Binary string that should be decompressed, e.g. the output from `atob`
* @param encoding - Decompression algorithm to use
* @returns The decompressed string
*/
export async function decompressRaw(binary: string, encoding: CompressionFormat): Promise<string> {
// stream the string through the decompressor
const stream = new Blob([Uint8Array.from(binary, m => m.codePointAt(0)!)])
.stream()
.pipeThrough(new DecompressionStream(encoding));
// convert the stream to a string
return new Response(stream).text();
}

/**
* Decompress a string representation with browser native APIs in to a normal js string
*
* @param data - String that should be decompressed
* @param encoding - Decompression algorithm to use
* @returns The decompressed string
*/
export async function decompress(data: string, encoding: CompressionFormat): Promise<string> {
return decompressRaw(atob(data), encoding);
}

0 comments on commit eeda560

Please sign in to comment.