Skip to content

Commit ff302ca

Browse files
authored
docs: Multiple files support (#989)
* Reading multiple files into dictionaries * Editor now has tabs * Type example holds now all possible data regarding examples * Introduced TS bundler * Removed legacy live preview code * Working static imports * Fixed StackBlitz, added onCleanup * Astro tgpu quick fix * Dynamic import fix endeavour * No cache import implemented * Types fixes * Minor type fixes, biome suggested fixes * Moved .html control files to the file tabs * Naming changes * Moved filtering out of context for readability * Fixed mobile version * Tidying * Biome formatting fixes * Pull request fixes * Creating URL instead of plain string for ViteImport * Fixed relative pathing in vite's build * PR final fixes * Formatting changes * Monaco css endeavour * Fixed wrong default file in monaco bug * Fixed css monaco issue * Naming tsSources -> tsImports * CSS Monaco final fix block h-[calc(100%-6rem)] md:h-[calc(100%-2rem)] was the correct answer * Vite ignore fixes * Run babel * Biome fix * Block is default expression * Not checking whether index.ts exists anymore
1 parent be8a643 commit ff302ca

File tree

11 files changed

+188
-321
lines changed

11 files changed

+188
-321
lines changed

apps/typegpu-docs/astro.config.mjs

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,38 +18,12 @@ const stripFalsy = (items) =>
1818

1919
const DEV = import.meta.env.DEV;
2020

21-
/**
22-
* Plugin that converts code transformed by the `typegpu` plugin to a raw string.
23-
* @returns {{
24-
* name: string;
25-
* enforce: 'post';
26-
* transform(code: string, id: string): { code: string } | undefined;
27-
* }}
28-
*/
29-
function toRawPlugin() {
30-
return {
31-
name: 'to-raw',
32-
enforce: 'post',
33-
34-
transform(code, id) {
35-
if (id.endsWith('?tgpu=true')) {
36-
return {
37-
code: `export default ${JSON.stringify(code)
38-
.replace(/\u2028/g, '\\u2028')
39-
.replace(/\u2029/g, '\\u2029')};`,
40-
};
41-
}
42-
return undefined;
43-
},
44-
};
45-
}
46-
4721
// https://astro.build/config
4822
export default defineConfig({
4923
site: 'https://docs.swmansion.com',
5024
base: 'TypeGPU',
5125
vite: {
52-
plugins: [typegpu({ include: [/tgpu=true/] }), toRawPlugin()],
26+
plugins: [typegpu({ include: [/tgpu=true/] })],
5327
},
5428
integrations: [
5529
starlight({

apps/typegpu-docs/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
"@astrojs/starlight": "^0.32.1",
1616
"@astrojs/starlight-tailwind": "^3.0.0",
1717
"@astrojs/tailwind": "^6.0.0",
18-
"@babel/standalone": "^7.26.6",
1918
"@monaco-editor/react": "^4.6.0",
2019
"@radix-ui/react-select": "^2.1.1",
2120
"@radix-ui/react-slider": "^1.2.0",

apps/typegpu-docs/src/components/CodeEditor.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,11 @@ const createCodeEditorComponent =
9090
const { code, shown } = props;
9191

9292
return (
93-
<div className={shown ? 'contents' : 'hidden'}>
93+
<div
94+
className={
95+
shown ? 'h-[calc(100%-6rem)] md:h-[calc(100%-2rem)]' : 'hidden'
96+
}
97+
>
9498
<Editor
9599
defaultLanguage={language}
96100
value={code}
@@ -102,7 +106,7 @@ const createCodeEditorComponent =
102106
},
103107
readOnly: true,
104108
}}
105-
className="pt-16 md:pt-0"
109+
className="rounded-tl-none"
106110
/>
107111
</div>
108112
);

apps/typegpu-docs/src/components/ExampleView.tsx

Lines changed: 40 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type Props = {
2222
};
2323

2424
function useExample(
25-
exampleCode: string,
25+
tsImports: Record<string, () => Promise<unknown>>,
2626
htmlCode: string,
2727
setSnackbarText: (text: string | undefined) => void,
2828
) {
@@ -34,14 +34,13 @@ function useExample(
3434
let cancelled = false;
3535
setSnackbarText(undefined);
3636

37-
executeExample(exampleCode)
37+
executeExample(tsImports)
3838
.then((example) => {
3939
if (cancelled) {
4040
// Another instance was started in the meantime.
4141
example.dispose();
4242
return;
4343
}
44-
4544
// Success
4645
setExampleControlParams(example.controlParams);
4746
exampleRef.current = example;
@@ -63,32 +62,35 @@ function useExample(
6362
exampleRef.current?.dispose();
6463
cancelled = true;
6564
};
66-
}, [exampleCode, htmlCode, setSnackbarText, setExampleControlParams]);
65+
}, [htmlCode, setSnackbarText, setExampleControlParams]);
6766
}
6867

69-
type EditorTab = 'ts' | 'html';
70-
7168
export function ExampleView({ example }: Props) {
72-
const { tsCode, htmlCode, execTsCode } = example;
69+
const { tsCodes, tsImports, htmlCode } = example;
7370

7471
const [snackbarText, setSnackbarText] = useAtom(currentSnackbarAtom);
75-
const [currentEditorTab, setCurrentEditorTab] = useState<EditorTab>('ts');
72+
const [currentFile, setCurrentFile] = useState<string>('index.ts');
7673

7774
const codeEditorShowing = useAtomValue(codeEditorShownAtom);
7875
const codeEditorMobileShowing = useAtomValue(codeEditorShownMobileAtom);
79-
8076
const exampleHtmlRef = useRef<HTMLDivElement>(null);
8177

82-
// biome-ignore lint/correctness/useExhaustiveDependencies: reset embedded html on code change
78+
const codeFiles = Object.keys(tsCodes);
79+
const editorTabsList = [
80+
'index.ts',
81+
...codeFiles.filter((name) => name !== 'index.ts'),
82+
'index.html',
83+
];
84+
8385
useEffect(() => {
8486
if (!exampleHtmlRef.current) {
8587
return;
8688
}
8789
exampleHtmlRef.current.innerHTML = htmlCode;
88-
}, [tsCode, htmlCode]);
90+
}, [htmlCode]);
8991

90-
useExample(execTsCode, htmlCode, setSnackbarText);
91-
useResizableCanvas(exampleHtmlRef, tsCode, htmlCode);
92+
useExample(tsImports, htmlCode, setSnackbarText); // live example
93+
useResizableCanvas(exampleHtmlRef, htmlCode);
9294

9395
return (
9496
<>
@@ -97,7 +99,7 @@ export function ExampleView({ example }: Props) {
9799
<div className="flex flex-col md:grid gap-4 md:grid-cols-[1fr_18.75rem] h-full">
98100
<div
99101
className={cs(
100-
'flex-1 grid gap-4 overflow-auto',
102+
'flex-1 grid gap-4',
101103
codeEditorShowing ? 'md:grid-rows-2' : '',
102104
)}
103105
>
@@ -131,18 +133,29 @@ export function ExampleView({ example }: Props) {
131133
'absolute bg-tameplum-50 z-20 md:relative h-[calc(100%-2rem)] w-[calc(100%-2rem)] md:w-full md:h-full',
132134
)}
133135
>
134-
<div className="absolute inset-0">
135-
<EditorTabButtonPanel
136-
currentEditorTab={currentEditorTab}
137-
setCurrentEditorTab={setCurrentEditorTab}
138-
/>
139-
140-
<TsCodeEditor shown={currentEditorTab === 'ts'} code={tsCode} />
141-
142-
<HtmlCodeEditor
143-
shown={currentEditorTab === 'html'}
144-
code={htmlCode}
145-
/>
136+
<div className="absolute inset-1">
137+
<div className="flex overflow-auto border-gray-300 pt-16 md:pt-0">
138+
{editorTabsList.map((fileName) => (
139+
<button
140+
key={fileName}
141+
type="button"
142+
onClick={() => setCurrentFile(fileName)}
143+
className={cs(
144+
'px-4 py-2',
145+
currentFile === fileName
146+
? 'rounded-t-lg rounded-bl-none rounded-br-none bg-gradient-to-br from-gradient-purple to-gradient-blue text-white hover:from-gradient-purple-dark hover:to-gradient-blue-dark'
147+
: 'rounded-t-lg rounded-bl-none rounded-br-none bg-white border-tameplum-100 border-2 hover:bg-tameplum-20',
148+
)}
149+
>
150+
{fileName}
151+
</button>
152+
))}
153+
</div>
154+
{currentFile === 'index.html' ? (
155+
<HtmlCodeEditor shown code={htmlCode} />
156+
) : (
157+
<TsCodeEditor shown code={tsCodes[currentFile]} />
158+
)}
146159
</div>
147160
</div>
148161
) : null}
@@ -153,48 +166,6 @@ export function ExampleView({ example }: Props) {
153166
);
154167
}
155168

156-
function EditorTabButtonPanel({
157-
currentEditorTab,
158-
setCurrentEditorTab,
159-
}: {
160-
currentEditorTab: EditorTab;
161-
setCurrentEditorTab: (tab: EditorTab) => void;
162-
}) {
163-
const commonStyle =
164-
'inline-flex justify-center items-center box-border text-sm px-5 py-1';
165-
const activeStyle =
166-
'bg-gradient-to-br from-gradient-purple to-gradient-blue text-white hover:from-gradient-purple-dark hover:to-gradient-blue-dark';
167-
const inactiveStyle =
168-
'bg-white border-tameplum-100 border-2 hover:bg-tameplum-20';
169-
170-
return (
171-
<div className="absolute right-0 md:right-6 top-2 z-10 flex">
172-
<button
173-
className={cs(
174-
commonStyle,
175-
'rounded-l-lg',
176-
currentEditorTab === 'ts' ? activeStyle : inactiveStyle,
177-
)}
178-
type="button"
179-
onClick={() => setCurrentEditorTab('ts')}
180-
>
181-
TS
182-
</button>
183-
<button
184-
className={cs(
185-
commonStyle,
186-
'rounded-r-lg',
187-
currentEditorTab === 'html' ? activeStyle : inactiveStyle,
188-
)}
189-
type="button"
190-
onClick={() => setCurrentEditorTab('html')}
191-
>
192-
HTML
193-
</button>
194-
</div>
195-
);
196-
}
197-
198169
function GPUUnsupportedPanel() {
199170
return (
200171
<div className="grid gap-6 text-xl leading-tight text-center place-content-center">
@@ -215,7 +186,6 @@ function GPUUnsupportedPanel() {
215186

216187
function useResizableCanvas(
217188
exampleHtmlRef: RefObject<HTMLDivElement | null>,
218-
tsCode: string,
219189
htmlCode: string,
220190
) {
221191
// biome-ignore lint/correctness/useExhaustiveDependencies: should be run on every htmlCode and tsCode change
@@ -276,5 +246,5 @@ function useResizableCanvas(
276246
observer.disconnect();
277247
}
278248
};
279-
}, [exampleHtmlRef, tsCode, htmlCode]);
249+
}, [exampleHtmlRef, htmlCode]);
280250
}

apps/typegpu-docs/src/components/stackblitz/openInStackBlitz.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,24 @@ if (pnpmWorkspaceYaml instanceof type.errors) {
1717
}
1818

1919
export const openInStackBlitz = (example: Example) => {
20+
const tsFiles = Object.entries(example.tsCodes).reduce(
21+
(acc, [fileName, code]) => {
22+
acc[`src/${fileName}`] = code.replaceAll(
23+
'/TypeGPU',
24+
'https://docs.swmansion.com/TypeGPU',
25+
);
26+
return acc;
27+
},
28+
{} as Record<string, string>,
29+
);
30+
2031
StackBlitzSDK.openProject(
2132
{
2233
template: 'node',
2334
title: example.metadata.title,
2435
files: {
2536
'index.ts': index.slice('// @ts-ignore\n'.length),
26-
'src/example.ts': example.tsCode.replaceAll(
27-
'/TypeGPU',
28-
'https://docs.swmansion.com/TypeGPU',
29-
),
37+
...tsFiles,
3038
'index.html': `\
3139
<!DOCTYPE html>
3240
<html lang="en">
@@ -55,7 +63,7 @@ ${example.htmlCode}
5563
"noEmit": true,
5664
"strict": true,
5765
"noUnusedLocals": true,
58-
"noUnusedParameters": true,
66+
"noUnusedParameters": true
5967
},
6068
"include": ["src", "index.ts"]
6169
}`,
@@ -91,7 +99,7 @@ export default defineConfig({
9199
},
92100
},
93101
{
94-
openFile: 'src/example.ts',
102+
openFile: 'src/index.ts',
95103
newWindow: true,
96104
theme: 'light',
97105
},

apps/typegpu-docs/src/components/stackblitz/stackBlitzIndex.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// @ts-ignore
2-
import * as example from './src/example.ts';
2+
import * as example from './src/index.ts';
33

44
const body = document.querySelector('body') as HTMLBodyElement;
55
body.style.display = 'flex';

apps/typegpu-docs/src/content/examples/simple/triangle/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
55
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
66
const context = canvas.getContext('webgpu') as GPUCanvasContext;
77

8+
const purple = d.vec4f(0.769, 0.392, 1.0, 1);
9+
const blue = d.vec4f(0.114, 0.447, 0.941, 1);
10+
811
const root = await tgpu.init();
912

1013
context.configure({
@@ -13,9 +16,6 @@ context.configure({
1316
alphaMode: 'premultiplied',
1417
});
1518

16-
const purple = d.vec4f(0.769, 0.392, 1.0, 1);
17-
const blue = d.vec4f(0.114, 0.447, 0.941, 1);
18-
1919
const getGradientColor = tgpu['~unstable']
2020
.fn([d.f32], d.vec4f)
2121
.does(/* wgsl */ `(ratio: f32) -> vec4f {

0 commit comments

Comments
 (0)