Skip to content

Commit ffe4a64

Browse files
committed
wip scratchpad
1 parent e71ddbf commit ffe4a64

File tree

5 files changed

+185
-235
lines changed

5 files changed

+185
-235
lines changed

playground/app.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { Show, createSignal, JSX, on } from 'solid-js';
22
import { Routes, Route, useSearchParams } from 'solid-app-router';
33
import { eventBus } from './utils/serviceWorker';
44
import { Update } from './components/update';
5-
import { Header } from './components/header';
65
import { useZoom } from '../src/hooks/useZoom';
76
import { Edit } from './pages/edit';
87
import { Home } from './pages/home';
@@ -36,9 +35,8 @@ export const App = (): JSX.Element => {
3635

3736
return (
3837
<div class="relative flex bg-white dark:bg-solid-darkbg dark:text-white text-black h-screen text-slate-900 dark:text-slate-50 font-sans flex-col overflow-auto">
39-
<Header />
40-
4138
<Routes>
39+
<Route path="/scratchpad" element={<Edit horizontal={searchParams.isHorizontal != undefined} scratchpad />} />
4240
<Route path="/:user/:repl" element={<Edit horizontal={searchParams.isHorizontal != undefined} />} />
4341
<Route path="/:user" element={<Home />} />
4442
<Route path="/" element={<Home />} />

playground/components/header.tsx

Lines changed: 13 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,26 @@
11
import Dismiss from 'solid-dismiss';
22
import { Icon } from 'solid-heroicons';
3-
import { Component, onCleanup, createSignal, Show } from 'solid-js';
3+
import { onCleanup, createSignal, Show, ParentComponent } from 'solid-js';
44
import { share, link, download, xCircle, menu, moon, sun } from 'solid-heroicons/outline';
55
import { exportToZip } from '../utils/exportFiles';
66
import { ZoomDropdown } from './zoomDropdown';
77
import { API, useAppContext } from '../context';
8-
import { compressToURL } from '@amoutonbrady/lz-string';
98

109
import logo from '../assets/logo.svg?url';
1110

12-
export const Header: Component = () => {
11+
export const Header: ParentComponent<{ fork?: () => void; share: () => Promise<string> }> = (props) => {
1312
const [copy, setCopy] = createSignal(false);
1413
const context = useAppContext()!;
1514
const [showMenu, setShowMenu] = createSignal(false);
1615
let menuBtnEl!: HTMLButtonElement;
1716

1817
function shareLink() {
19-
let url = new URL(location.origin);
20-
url.hash = compressToURL(JSON.stringify(context.tabs()));
21-
console.log('Shareable url:', url.href);
22-
23-
fetch('/', { method: 'PUT', body: `{"url":"${url.href}"}` })
24-
.then((response) => {
25-
if (response.status >= 400) {
26-
throw new Error(response.statusText);
27-
}
28-
29-
return response.text();
30-
})
31-
.then((hash) => {
32-
const tinyUrl = new URL(location.origin);
33-
tinyUrl.searchParams.set('hash', hash);
34-
35-
navigator.clipboard.writeText(tinyUrl.toString()).then(() => {
36-
setCopy(true);
37-
setTimeout(setCopy, 750, false);
38-
});
39-
})
40-
.catch(() => {
41-
navigator.clipboard.writeText(url.href).then(() => {
42-
setCopy(true);
43-
setTimeout(setCopy, 750, false);
44-
});
18+
props.share().then((url) => {
19+
navigator.clipboard.writeText(url).then(() => {
20+
setCopy(true);
21+
setTimeout(setCopy, 750, false);
4522
});
23+
});
4624
}
4725

4826
window.addEventListener('resize', closeMobileMenu);
@@ -61,9 +39,11 @@ export const Header: Component = () => {
6139
<img src={logo} alt="solid-js logo" class="w-8" />
6240
</a>
6341
<div id="project-name">
64-
<span class="inline-block -mb-1">
65-
Solid<b>JS</b> Playground
66-
</span>
42+
{props.children || (
43+
<span class="inline-block">
44+
Solid<b>JS</b> Playground
45+
</span>
46+
)}
6747
</div>
6848
</h1>
6949
<div class="flex items-center gap-3 mr-2">
@@ -149,7 +129,7 @@ export const Header: Component = () => {
149129
when={context.user()?.avatar}
150130
fallback={
151131
<a
152-
class="mx-1 -mb-1"
132+
class="mx-1 bg-solid-default py-2 px-3 rounded text-lg"
153133
href={`${API}/auth/login?redirect=${window.location.origin}/login?auth=success`}
154134
rel="external"
155135
>

playground/pages/edit.tsx

Lines changed: 102 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,14 @@ import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
33
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
44
import CompilerWorker from '../../src/workers/compiler?worker';
55
import FormatterWorker from '../../src/workers/formatter?worker';
6-
import {
7-
batch,
8-
createEffect,
9-
createResource,
10-
createSignal,
11-
lazy,
12-
onCleanup,
13-
onMount,
14-
ParentComponent,
15-
Suspense,
16-
} from 'solid-js';
6+
import { batch, createEffect, createResource, createSignal, lazy, Suspense } from 'solid-js';
177
import { useParams } from 'solid-app-router';
188
import { API, useAppContext } from '../context';
199
import { debounce } from '@solid-primitives/scheduled';
20-
import { createTabList, Tab } from '../../src';
10+
import { createTabList, defaultTabs, Tab } from '../../src';
2111
import type { APIRepl } from './home';
12+
import { Header } from '../components/header';
13+
import { compressToURL } from '@amoutonbrady/lz-string';
2214

2315
const Repl = lazy(() => import('../../src/components/repl'));
2416

@@ -36,22 +28,7 @@ const Repl = lazy(() => import('../../src/components/repl'));
3628
},
3729
};
3830

39-
const RenderHeader: ParentComponent = (props) => {
40-
onMount(() => {
41-
const projectName = document.getElementById('project-name')!;
42-
const content = projectName.firstChild!;
43-
content.remove();
44-
const children = props.children as HTMLElement;
45-
projectName.appendChild(children);
46-
onCleanup(() => {
47-
projectName.appendChild(content);
48-
projectName.removeChild(children);
49-
});
50-
});
51-
return <></>;
52-
};
53-
54-
export const Edit = (props: { horizontal: boolean }) => {
31+
export const Edit = (props: { horizontal: boolean; scratchpad?: boolean }) => {
5532
const compiler = new CompilerWorker();
5633
const formatter = new FormatterWorker();
5734

@@ -63,10 +40,30 @@ export const Edit = (props: { horizontal: boolean }) => {
6340
const [tabs, setTabs] = createTabList([]);
6441
context.setTabs(tabs);
6542
const [current, setCurrent] = createSignal<string>();
66-
const [resource, { mutate }] = createResource<APIRepl, string>(params.repl, async (repl) => {
43+
const [resource, { mutate }] = createResource<APIRepl, string>(async () => {
44+
const repl = params.repl;
45+
6746
let output: APIRepl;
68-
if (params.user == 'local') {
69-
output = JSON.parse(localStorage.getItem(repl)!);
47+
if (props.scratchpad) {
48+
const scratchpad = localStorage.getItem('scratchpad');
49+
if (!scratchpad) {
50+
output = {
51+
id: 'scratchpad',
52+
title: 'Scratchpad',
53+
public: true,
54+
version: '1.0',
55+
labels: [],
56+
size: 0,
57+
created_at: new Date().toISOString(),
58+
files: defaultTabs.map((x) => ({
59+
name: x.name,
60+
content: x.source.split('\n'),
61+
})),
62+
};
63+
localStorage.setItem('scratchpad', JSON.stringify(output));
64+
} else {
65+
output = JSON.parse(scratchpad);
66+
}
7067
} else {
7168
output = await fetch(`${API}/repl/${repl}`, {
7269
headers: { authorization: context.token ? `Bearer ${context.token}` : '' },
@@ -91,20 +88,13 @@ export const Edit = (props: { horizontal: boolean }) => {
9188
() => {
9289
const repl = resource.latest;
9390
if (!repl) return;
91+
9492
const files = tabMapper(tabs());
95-
if (params.user == 'local') {
96-
localStorage.setItem(
97-
params.repl,
98-
JSON.stringify({
99-
title: repl.title,
100-
version: repl.version,
101-
public: repl.public,
102-
labels: repl.labels,
103-
files,
104-
}),
105-
);
93+
if (props.scratchpad) {
94+
localStorage.setItem('scratchpad', JSON.stringify({ ...repl, files }));
10695
return;
10796
}
97+
10898
if (!context.token || context.user()?.display != params.user) return;
10999
fetch(`${API}/repl/${params.repl}`, {
110100
method: 'PUT',
@@ -131,43 +121,76 @@ export const Edit = (props: { horizontal: boolean }) => {
131121
});
132122

133123
return (
134-
<Suspense
135-
fallback={
136-
<svg
137-
class="animate-spin h-12 w-12 text-white m-auto"
138-
xmlns="http://www.w3.org/2000/svg"
139-
fill="none"
140-
viewBox="0 0 24 24"
141-
>
142-
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
143-
<path
144-
class="opacity-75"
145-
fill="currentColor"
146-
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
147-
></path>
148-
</svg>
149-
}
150-
>
151-
<Repl
152-
compiler={compiler}
153-
formatter={formatter}
154-
isHorizontal={props.horizontal}
155-
dark={context.dark()}
156-
tabs={tabs()}
157-
setTabs={setTabs}
158-
current={current()}
159-
setCurrent={setCurrent}
160-
id="repl"
161-
/>
162-
<RenderHeader>
163-
<input
164-
class="bg-transparent"
165-
value={resource()?.title || ''}
166-
onChange={(e) => {
167-
mutate((x) => x && { ...x, title: e.currentTarget.value });
168-
}}
124+
<>
125+
<Header
126+
fork={() => {}}
127+
share={() => {
128+
if (props.scratchpad) {
129+
let url = new URL(location.origin);
130+
url.hash = compressToURL(JSON.stringify(context.tabs()));
131+
console.log('Shareable url:', url.href);
132+
133+
return fetch('/', { method: 'PUT', body: `{"url":"${url.href}"}` })
134+
.then((response) => {
135+
if (response.status >= 400) {
136+
throw new Error(response.statusText);
137+
}
138+
139+
return response.text();
140+
})
141+
.then((hash) => {
142+
const tinyUrl = new URL(location.origin);
143+
tinyUrl.searchParams.set('hash', hash);
144+
145+
return tinyUrl.toString();
146+
})
147+
.catch(() => {
148+
return url.href;
149+
});
150+
} else {
151+
return Promise.resolve(location.href);
152+
}
153+
}}
154+
>
155+
{resource()?.title && (
156+
<input
157+
class="bg-transparent"
158+
value={resource()?.title}
159+
onChange={(e) => {
160+
mutate((x) => x && { ...x, title: e.currentTarget.value });
161+
}}
162+
/>
163+
)}
164+
</Header>
165+
<Suspense
166+
fallback={
167+
<svg
168+
class="animate-spin h-12 w-12 text-white m-auto"
169+
xmlns="http://www.w3.org/2000/svg"
170+
fill="none"
171+
viewBox="0 0 24 24"
172+
>
173+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
174+
<path
175+
class="opacity-75"
176+
fill="currentColor"
177+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
178+
></path>
179+
</svg>
180+
}
181+
>
182+
<Repl
183+
compiler={compiler}
184+
formatter={formatter}
185+
isHorizontal={props.horizontal}
186+
dark={context.dark()}
187+
tabs={tabs()}
188+
setTabs={setTabs}
189+
current={current()}
190+
setCurrent={setCurrent}
191+
id="repl"
169192
/>
170-
</RenderHeader>
171-
</Suspense>
193+
</Suspense>
194+
</>
172195
);
173196
};

0 commit comments

Comments
 (0)