Skip to content

Commit 66a6402

Browse files
docs: Common code between examples (#1995)
1 parent d2e6cfc commit 66a6402

11 files changed

Lines changed: 111 additions & 245 deletions

File tree

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import Editor, {
66
import type { editor } from 'monaco-editor';
77
import { entries, filter, fromEntries, isTruthy, map, pipe } from 'remeda';
88
import { SANDBOX_MODULES } from '../utils/examples/sandboxModules.ts';
9-
import type { ExampleSrcFile } from '../utils/examples/types.ts';
9+
import type {
10+
ExampleCommonFile,
11+
ExampleSrcFile,
12+
} from '../utils/examples/types.ts';
1013
import { tsCompilerOptions } from '../utils/liveEditor/embeddedTypeScript.ts';
1114

1215
function handleEditorWillMount(monaco: Monaco) {
@@ -56,7 +59,7 @@ function handleEditorOnMount(editor: editor.IStandaloneCodeEditor) {
5659
}
5760

5861
type Props = {
59-
file: ExampleSrcFile;
62+
file: ExampleSrcFile | ExampleCommonFile;
6063
shown: boolean;
6164
};
6265

@@ -68,6 +71,11 @@ const createCodeEditorComponent = (
6871
(props: Props) => {
6972
const { file, shown } = props;
7073

74+
// Monaco needs relative paths to work correctly and '../../common/file.ts' will not do
75+
const path = 'common' in file
76+
? `common/${file.path}`
77+
: `${file.exampleKey.replace('--', '/')}/${file.path}`;
78+
7179
return (
7280
<div
7381
className={shown
@@ -77,7 +85,7 @@ const createCodeEditorComponent = (
7785
<Editor
7886
defaultLanguage={language}
7987
value={file.content}
80-
path={file.path}
88+
path={path}
8189
beforeMount={beforeMount}
8290
onMount={onMount}
8391
options={{

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useAtomValue, useSetAtom } from 'jotai';
22
import { Suspense, useEffect, useRef } from 'react';
33
import { currentExampleAtom } from '../utils/examples/currentExampleAtom.ts';
4-
import { examples } from '../examples/exampleContent.ts';
4+
import { common, examples } from '../examples/exampleContent.ts';
55
import { ExampleNotFound } from './ExampleNotFound.tsx';
66
import { ExampleView } from './ExampleView.tsx';
77

@@ -68,7 +68,11 @@ function ExamplePage() {
6868

6969
if (currentExample in examples) {
7070
return (
71-
<ExampleView key={currentExample} example={examples[currentExample]} />
71+
<ExampleView
72+
key={currentExample}
73+
example={examples[currentExample]}
74+
common={common}
75+
/>
7276
);
7377
}
7478

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

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ import { ExecutionCancelledError } from '../utils/examples/errors.ts';
1010
import { exampleControlsAtom } from '../utils/examples/exampleControlAtom.ts';
1111
import { executeExample } from '../utils/examples/exampleRunner.ts';
1212
import type { ExampleState } from '../utils/examples/exampleState.ts';
13-
import type { Example } from '../utils/examples/types.ts';
13+
import type {
14+
Example,
15+
ExampleCommonFile,
16+
ExampleSrcFile,
17+
} from '../utils/examples/types.ts';
1418
import { isGPUSupported } from '../utils/isGPUSupported.ts';
1519
import { HtmlCodeEditor, TsCodeEditor } from './CodeEditor.tsx';
1620
import { ControlPanel } from './ControlPanel.tsx';
@@ -20,6 +24,7 @@ import { openInStackBlitz } from './stackblitz/openInStackBlitz.ts';
2024

2125
type Props = {
2226
example: Example;
27+
common: ExampleCommonFile[];
2328
isPlayground?: boolean;
2429
};
2530

@@ -66,8 +71,8 @@ function useExample(
6671
}, [setSnackbarText, setExampleControlParams]);
6772
}
6873

69-
export function ExampleView({ example }: Props) {
70-
const { tsFiles, tsImport, htmlFile } = example;
74+
export function ExampleView({ example, common }: Props) {
75+
const { tsFiles: srcFiles, tsImport, htmlFile } = example;
7176

7277
const [snackbarText, setSnackbarText] = useAtom(currentSnackbarAtom);
7378
const [currentFilePath, setCurrentFilePath] = useState<string>('index.ts');
@@ -76,6 +81,7 @@ export function ExampleView({ example }: Props) {
7681
const codeEditorMobileShowing = useAtomValue(codeEditorShownMobileAtom);
7782
const exampleHtmlRef = useRef<HTMLDivElement>(null);
7883

84+
const tsFiles = filterRelevantTsFiles(srcFiles, common);
7985
const filePaths = tsFiles.map((file) => file.path);
8086
const editorTabsList = [
8187
'index.ts',
@@ -171,7 +177,7 @@ export function ExampleView({ example }: Props) {
171177
</div>
172178

173179
<div className='absolute right-0 z-5 md:top-15 md:right-8'>
174-
<Button onClick={() => openInStackBlitz(example)}>
180+
<Button onClick={() => openInStackBlitz(example, common)}>
175181
<span className='font-bold'>Edit on</span>
176182
<img
177183
src='https://developer.stackblitz.com/img/logo/stackblitz-logo-black_blue.svg'
@@ -290,3 +296,27 @@ function useResizableCanvas(exampleHtmlRef: RefObject<HTMLDivElement | null>) {
290296
};
291297
}, [exampleHtmlRef]);
292298
}
299+
300+
/**
301+
* NOTE: this function only filters common files used in src files.
302+
* Common files used in other common files will not be included.
303+
*/
304+
function filterRelevantTsFiles(
305+
srcFiles: ExampleSrcFile[],
306+
commonFiles: ExampleCommonFile[],
307+
) {
308+
const tsFiles: (ExampleSrcFile | ExampleCommonFile)[] = [
309+
...srcFiles,
310+
];
311+
312+
for (const common of commonFiles) {
313+
for (const src of srcFiles) {
314+
if (src.content.includes(`common/${common.path}`)) {
315+
tsFiles.push(common);
316+
break;
317+
}
318+
}
319+
}
320+
321+
return tsFiles;
322+
}

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

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import pnpmWorkspace from '../../../../../pnpm-workspace.yaml?raw';
1919
import typegpuDocsPackageJson from '../../../package.json' with {
2020
type: 'json',
2121
};
22-
import type { Example } from '../../utils/examples/types.ts';
22+
import type { Example, ExampleCommonFile } from '../../utils/examples/types.ts';
2323
// biome-ignore lint/correctness/useImportExtensions: dude it's there
2424
import index from './stackBlitzIndex.ts?raw';
2525

@@ -51,17 +51,29 @@ if (pnpmWorkspaceYaml instanceof type.errors) {
5151
throw new Error(pnpmWorkspaceYaml.summary);
5252
}
5353

54-
export const openInStackBlitz = (example: Example) => {
55-
const tsFiles = example.tsFiles.reduce(
56-
(acc, file) => {
57-
acc[`src/${file.path}`] = file.content.replaceAll(
58-
'/TypeGPU',
59-
'https://docs.swmansion.com/TypeGPU',
60-
);
61-
return acc;
62-
},
63-
{} as Record<string, string>,
64-
);
54+
export const openInStackBlitz = (
55+
example: Example,
56+
common: ExampleCommonFile[],
57+
) => {
58+
const tsFiles: Record<string, string> = {};
59+
60+
for (const file of example.tsFiles) {
61+
tsFiles[`src/${file.path}`] = file.content;
62+
}
63+
for (const file of common) {
64+
tsFiles[`src/common/${file.path}`] = file.content;
65+
}
66+
67+
for (const key of Object.keys(tsFiles)) {
68+
const content = tsFiles[key];
69+
tsFiles[key] = content.replaceAll(
70+
'/TypeGPU',
71+
'https://docs.swmansion.com/TypeGPU',
72+
).replaceAll(
73+
'../../common',
74+
'./common',
75+
);
76+
}
6577

6678
StackBlitzSDK.openProject(
6779
{

apps/typegpu-docs/src/examples/rendering/phong-reflection/setup-orbit-camera.ts renamed to apps/typegpu-docs/src/examples/common/setup-orbit-camera.ts

File renamed without changes.

apps/typegpu-docs/src/examples/exampleContent.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import pathe from 'pathe';
22
import * as R from 'remeda';
33
import type {
44
Example,
5+
ExampleCommonFile,
56
ExampleMetadata,
67
ExampleSrcFile,
78
ThumbnailPair,
@@ -75,8 +76,9 @@ const metaFiles = R.pipe(
7576
R.mapKeys(pathToExampleKey),
7677
);
7778

78-
const readonlyTsFiles = R.pipe(
79-
import.meta.glob('./**/*.ts', {
79+
const exampleTsFiles = R.pipe(
80+
// './<category>/<example>/<file>.ts'
81+
import.meta.glob('./*/*/*.ts', {
8082
query: 'raw',
8183
eager: true,
8284
import: 'default',
@@ -127,7 +129,7 @@ export const examples = R.pipe(
127129
({
128130
key,
129131
metadata: value,
130-
tsFiles: readonlyTsFiles[key] ?? [],
132+
tsFiles: exampleTsFiles[key] ?? [],
131133
tsImport: () => noCacheImport(tsFilesImportFunctions[key]),
132134
htmlFile: htmlFiles[key]?.[0] ?? '',
133135
thumbnails: thumbnailFiles[key],
@@ -140,4 +142,18 @@ export const examplesByCategory = R.groupBy(
140142
(example) => example.metadata.category,
141143
);
142144

145+
export const common = R.pipe(
146+
import.meta.glob('./common/*.ts', {
147+
query: 'raw',
148+
eager: true,
149+
import: 'default',
150+
}) as Record<string, string>,
151+
R.mapValues((content: string, key: string): ExampleCommonFile => ({
152+
common: true,
153+
path: pathe.basename(key),
154+
content,
155+
})),
156+
R.values(),
157+
);
158+
143159
export const PLAYGROUND_KEY = 'playground__';

apps/typegpu-docs/src/examples/rendering/phong-reflection/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
ModelVertexOutput,
1010
} from './schemas.ts';
1111
import { loadModel } from './load-model.ts';
12-
import { Camera, setupOrbitCamera } from './setup-orbit-camera.ts';
12+
import { Camera, setupOrbitCamera } from '../../common/setup-orbit-camera.ts';
1313

1414
// setup
1515
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
@@ -46,7 +46,7 @@ const exampleControlsUniform = root.createUniform(
4646
p.initialControls,
4747
);
4848

49-
export const vertexShader = tgpu['~unstable'].vertexFn({
49+
const vertexShader = tgpu['~unstable'].vertexFn({
5050
in: { ...ModelVertexInput.propTypes, instanceIndex: d.builtin.instanceIndex },
5151
out: ModelVertexOutput,
5252
})((input) => {
@@ -63,7 +63,7 @@ export const vertexShader = tgpu['~unstable'].vertexFn({
6363
});
6464

6565
// see https://gist.github.com/chicio/d983fff6ff304bd55bebd6ff05a2f9dd
66-
export const fragmentShader = tgpu['~unstable'].fragmentFn({
66+
const fragmentShader = tgpu['~unstable'].fragmentFn({
6767
in: ModelVertexOutput,
6868
out: d.vec4f,
6969
})((input) => {

apps/typegpu-docs/src/examples/simulation/gravity/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import {
3939
Time,
4040
timeAccess,
4141
} from './schemas.ts';
42-
import { Camera, setupOrbitCamera } from './setup-orbit-camera.ts';
42+
import { Camera, setupOrbitCamera } from '../../common/setup-orbit-camera.ts';
4343

4444
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
4545
const canvas = document.querySelector('canvas') as HTMLCanvasElement;

apps/typegpu-docs/src/examples/simulation/gravity/schemas.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import tgpu, { type TgpuSampler } from 'typegpu';
22
import * as d from 'typegpu/data';
3-
import { Camera } from './setup-orbit-camera.ts';
3+
import { Camera } from '../../common/setup-orbit-camera.ts';
44

55
export type CelestialBody = d.Infer<typeof CelestialBody>;
66
export const CelestialBody = d.struct({

0 commit comments

Comments
 (0)