Skip to content

Commit c81ea79

Browse files
authored
Encode code to / decode code from the URL hash. (#2096)
1 parent d1136cd commit c81ea79

File tree

4 files changed

+93
-35
lines changed

4 files changed

+93
-35
lines changed

website/src/app/play/Playground.tsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
'use client';
2+
3+
import Repl from '../../repl/Repl';
4+
import { stringToBytes, bytesToString } from './encoder';
5+
6+
export default function Playground() {
7+
{
8+
/*
9+
Debug with:
10+
11+
List([
12+
'apple',
13+
'banana',
14+
'coconut',
15+
123,
16+
null,
17+
undefined,
18+
new Date()
19+
])
20+
.push('dragonfruit')
21+
.map((fruit) => upperFirst(fruit))
22+
23+
*/
24+
}
25+
26+
let decodedHash: string | null = null;
27+
28+
try {
29+
decodedHash = window.location.hash
30+
? bytesToString(window.location.hash.slice(1))
31+
: null;
32+
} catch (e) {
33+
console.warn('Error decoding hash', e);
34+
}
35+
36+
const defaultValue =
37+
decodedHash ??
38+
`const upperFirst = (str) => typeof str === 'string'
39+
? str.charAt(0).toUpperCase() + str.slice(1)
40+
: str;
41+
42+
List([
43+
'apple',
44+
'banana',
45+
'coconut',
46+
])
47+
.push('dragonfruit')
48+
.map((fruit) => upperFirst(fruit))
49+
`;
50+
51+
return (
52+
<Repl
53+
defaultValue={defaultValue}
54+
onRun={(code) => {
55+
const bytes = stringToBytes(code);
56+
57+
// adds bytes as url hash
58+
window.location.hash = bytes;
59+
}}
60+
/>
61+
);
62+
}

website/src/app/play/encoder.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// taken from https://developer.mozilla.org/en-US/docs/Web/API/Window/btoa#unicode_strings
2+
function base64ToBytes(base64: string): Uint8Array {
3+
const binString = atob(base64);
4+
return Uint8Array.from(binString, (m: string) => m.codePointAt(0) ?? 0);
5+
}
6+
7+
function bytesToBase64(bytes: Uint8Array): string {
8+
const binString = Array.from(bytes, (byte) =>
9+
String.fromCodePoint(byte)
10+
).join('');
11+
return btoa(binString);
12+
}
13+
14+
export function stringToBytes(str: string): string {
15+
return bytesToBase64(new TextEncoder().encode(str));
16+
}
17+
18+
export function bytesToString(bytes: string): string {
19+
return new TextDecoder().decode(base64ToBytes(bytes));
20+
}

website/src/app/play/page.tsx

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { getTypeDefs } from '../../static/getTypeDefs';
44
import { DocSearch } from '../../DocSearch';
55
import { SideBar } from '../../Sidebar';
66
import { getSidebarLinks } from '../../getSidebarLinks';
7-
import Repl from '../../repl/Repl';
7+
import Playground from './Playground';
88

99
export async function generateStaticParams() {
1010
return [...getVersions().map((version) => ({ version }))];
@@ -29,38 +29,8 @@ export default function OverviewDocPage() {
2929
<div key="Overview" className="docContents">
3030
<DocSearch />
3131
<h1>Playgroud ({version})</h1>
32-
33-
{/*
34-
Debug with:
35-
36-
List([
37-
'apple',
38-
'banana',
39-
'coconut',
40-
123,
41-
null,
42-
undefined,
43-
new Date()
44-
])
45-
.push('dragonfruit')
46-
.map((fruit) => upperFirst(fruit))
47-
48-
*/}
49-
50-
<Repl
51-
defaultValue={`const upperFirst = (str) => typeof str === 'string'
52-
? str.charAt(0).toUpperCase() + str.slice(1)
53-
: str;
54-
55-
List([
56-
'apple',
57-
'banana',
58-
'coconut',
59-
])
60-
.push('dragonfruit')
61-
.map((fruit) => upperFirst(fruit))
62-
`}
63-
/>
32+
You can share or bookmark the url to get access to this playground.
33+
<Playground />
6434
</div>
6535
</>
6636
);

website/src/repl/Repl.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { Editor } from './Editor';
55
import FormatterOutput from './FormatterOutput';
66
import './repl.css';
77

8-
type Props = { defaultValue: string };
8+
type Props = { defaultValue: string; onRun?: (code: string) => void };
99

10-
function Repl({ defaultValue }: Props): JSX.Element {
10+
function Repl({ defaultValue, onRun }: Props): JSX.Element {
1111
const [code, setCode] = useState<string>(defaultValue);
1212
const [output, setOutput] = useState<{
1313
header: Array<unknown>;
@@ -125,6 +125,12 @@ function Repl({ defaultValue }: Props): JSX.Element {
125125

126126
const runCode = () => {
127127
if (workerRef.current) {
128+
// notify parent
129+
if (onRun) {
130+
onRun(code);
131+
}
132+
133+
// send message to worker
128134
workerRef.current.postMessage(code);
129135
workerRef.current.onmessage = (event) => {
130136
if (event.data.error) {

0 commit comments

Comments
 (0)