Skip to content

Commit e6eb29f

Browse files
committed
fix(docs-site): playground should support full types + expose playground
1 parent 6eb1c48 commit e6eb29f

File tree

11 files changed

+897
-430
lines changed

11 files changed

+897
-430
lines changed

docs-site/docusaurus.config.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ const config: Config = {
6161
],
6262
CopyReadmeAndChangelogPlugin,
6363
ExamplesDocsPlugin,
64+
'docusaurus-plugin-sass',
6465
],
6566

6667
presets: [
@@ -103,10 +104,10 @@ const config: Config = {
103104
position: 'left',
104105
label: 'Tutorial',
105106
},
106-
// {
107-
// to: '/playground',
108-
// label: 'Playground',
109-
// },
107+
{
108+
to: '/playground',
109+
label: 'Playground',
110+
},
110111
{
111112
href: 'https://github.com/agentender/cli-forge',
112113
label: 'GitHub',

docs-site/package.json

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,28 @@
1010
"typecheck": "tsc"
1111
},
1212
"dependencies": {
13-
"@docusaurus/core": "3.4.0",
14-
"@docusaurus/preset-classic": "3.4.0",
13+
"@docusaurus/core": "3.5.2",
14+
"@docusaurus/preset-classic": "3.5.2",
1515
"@mdx-js/react": "^3.0.0",
1616
"clsx": "^2.0.0",
1717
"prism-react-renderer": "^2.3.0",
1818
"react": "^18.0.0",
1919
"react-dom": "^18.0.0"
2020
},
2121
"devDependencies": {
22-
"@docusaurus/module-type-aliases": "3.4.0",
23-
"@docusaurus/tsconfig": "3.4.0",
24-
"@docusaurus/types": "3.4.0",
22+
"@docusaurus/module-type-aliases": "3.5.2",
23+
"@docusaurus/tsconfig": "3.5.2",
24+
"@docusaurus/types": "3.5.2",
2525
"docusaurus-plugin-typedoc": "^1.0.3",
26+
"@docusaurus/remark-plugin-npm2yarn": "^3.5.2",
27+
"@typescript/sandbox": "^0.1.7",
28+
"docusaurus-plugin-sass": "^0.2.5",
29+
"monaco-editor": "^0.52.0",
30+
"monaco-editor-webpack-plugin": "^7.1.0",
31+
"sass": "^1.79.3",
2632
"remark-github": "^12.0.0",
2733
"typedoc": "^0.26.4",
28-
"typedoc-plugin-markdown": "^4.2.1",
29-
"typescript": "~5.2.2"
34+
"typedoc-plugin-markdown": "^4.2.1"
3035
},
3136
"browserslist": {
3237
"production": [

docs-site/pages/playground.tsx

Lines changed: 0 additions & 118 deletions
This file was deleted.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.editor {
2+
margin: 1.5rem;
3+
border: 2px solid #ccc;
4+
border-radius: 4px;
5+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
6+
padding: 0.5rem;
7+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
2+
import { useColorMode } from '@docusaurus/theme-common';
3+
4+
import {
5+
compressToEncodedURIComponent,
6+
decompressFromEncodedURIComponent,
7+
} from 'lz-string';
8+
9+
import { createTypeScriptSandbox } from '@typescript/sandbox';
10+
11+
import styles from './editor.module.scss';
12+
import toast from 'react-hot-toast';
13+
14+
export interface EditorRef {
15+
getText: () => string;
16+
setText: (text: string) => void;
17+
cleanup: () => void;
18+
}
19+
20+
export interface EditorProps {
21+
initialText: string;
22+
dts?: Record<string, string>;
23+
}
24+
25+
export const Editor = forwardRef<EditorRef, EditorProps>(
26+
({ initialText, dts }: EditorProps, ref) => {
27+
dts ??= {};
28+
29+
const [sandbox, setSandbox] =
30+
useState<ReturnType<typeof createTypeScriptSandbox>>(null);
31+
32+
const [ts, setTs] = useState<typeof import('typescript')>(null);
33+
const [monaco, setMonaco] = useState<typeof import('monaco-editor')>(null);
34+
35+
const { colorMode } = useColorMode();
36+
37+
useImperativeHandle(ref, () => ({
38+
getText: () => sandbox?.getText(),
39+
setText: (text: string) => {
40+
sandbox?.setText(text);
41+
42+
// clear the hash, as its not representative of the current state
43+
window.history.pushState(null, null, '#');
44+
},
45+
cleanup: () => {
46+
sandbox.getModel().dispose();
47+
sandbox.editor.dispose;
48+
},
49+
}));
50+
51+
useEffect(() => {
52+
(async () => {
53+
const [ts, monaco] = (await Promise.all([
54+
import('typescript'),
55+
import('monaco-editor'),
56+
])) as [typeof import('typescript'), typeof import('monaco-editor')];
57+
58+
setTs(ts);
59+
setMonaco(monaco);
60+
})();
61+
}, []);
62+
63+
useEffect(() => {
64+
(async () => {
65+
if (!ts || !monaco) {
66+
return;
67+
}
68+
69+
const contents = window.location.hash
70+
? decompressFromEncodedURIComponent(window.location.hash.slice(1))
71+
: initialText;
72+
73+
const sandbox = createTypeScriptSandbox(
74+
{
75+
domID: 'monaco-editor-embed',
76+
filetype: 'ts',
77+
text: contents,
78+
acquireTypes: false,
79+
monacoSettings: {
80+
automaticLayout: true,
81+
},
82+
},
83+
monaco,
84+
ts
85+
);
86+
87+
Object.keys(dts).forEach((path) => {
88+
// console.log('Adding', path, 'code:', dts[path]);
89+
sandbox.languageServiceDefaults.addExtraLib(dts[path], path);
90+
});
91+
92+
setSandbox(sandbox);
93+
94+
return () => {
95+
console.log('Disposing sandbox');
96+
sandbox.getModel().dispose();
97+
sandbox.editor.dispose();
98+
};
99+
})();
100+
}, [ts, monaco]);
101+
102+
useEffect(() => {
103+
if (colorMode === 'dark') {
104+
monaco?.editor?.setTheme('vs-dark');
105+
} else {
106+
monaco?.editor?.setTheme('vs-light');
107+
}
108+
}, [colorMode, monaco]);
109+
110+
useEffect(() => {
111+
if (!sandbox) {
112+
return;
113+
}
114+
115+
const listener = (event: React.KeyboardEvent) => {
116+
if ((event.ctrlKey || event.metaKey) && event.key === 's') {
117+
// Encode's the editor contents and sets it as the hash
118+
const uriComponent = compressToEncodedURIComponent(sandbox.getText());
119+
120+
if (!window.history?.pushState) {
121+
toast.error(
122+
'Your browser does not support this feature - unable to save.'
123+
);
124+
} else {
125+
window.history.pushState(null, null, `#${uriComponent}`);
126+
}
127+
128+
// Copies new URL to clipboard
129+
if (navigator.clipboard) {
130+
navigator.clipboard.writeText(window.location.href);
131+
toast.success('Saved and copied to clipboard');
132+
}
133+
134+
// Prevent the default save dialog
135+
event.preventDefault();
136+
}
137+
};
138+
139+
document.addEventListener('keydown', listener as any);
140+
141+
return () => {
142+
document.removeEventListener('keydown', listener as any);
143+
};
144+
}, [sandbox]);
145+
146+
return (
147+
<>
148+
{!sandbox ? <div id="loader">Loading...</div> : null}
149+
<div
150+
id="monaco-editor-embed"
151+
className={styles.editor}
152+
style={{ height: '800px' }}
153+
/>
154+
{/* TODO: Revisit this later. */}
155+
{/* <div style={{ position: 'absolute', bottom: '2rem', right: '2rem' }}>
156+
<button>View on TS Playground</button>
157+
</div> */}
158+
</>
159+
);
160+
}
161+
);
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
.sidebar {
2+
min-width: 200px;
3+
padding: 1.5rem;
4+
5+
h2 {
6+
font-size: 1.5rem;
7+
margin-bottom: 1rem;
8+
}
9+
10+
ul {
11+
list-style-type: none;
12+
padding: 0;
13+
margin: 0;
14+
15+
li {
16+
margin-bottom: 1rem;
17+
18+
button {
19+
width: 100%;
20+
padding: 0.5rem;
21+
border: 1px solid #ccc;
22+
border-radius: 0.25rem;
23+
background-color: #fff;
24+
cursor: pointer;
25+
transition: background-color 0.2s;
26+
27+
&:hover {
28+
background-color: #f5f5f5;
29+
}
30+
}
31+
}
32+
}
33+
}

0 commit comments

Comments
 (0)