Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/typegpu-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
"lodash": "^4.17.21",
"lucide-react": "^0.536.0",
"lz-string": "^1.5.0",
"monaco-editor": "^0.53.0",
"morphcharts": "^1.3.2",
"motion": "^12.23.24",
"onnxruntime-web": "1.23.0-dev.20250917-21fbad8a65",
Expand All @@ -61,6 +60,7 @@
"starlight-typedoc": "^0.19.0",
"three": "catalog:example",
"tinybench": "^3.1.0",
"tsover-monaco-editor": "^0.55.1",
"typedoc": "^0.27.9",
"typedoc-plugin-markdown": "4.3.0",
"typegpu": "workspace:*",
Expand Down
113 changes: 71 additions & 42 deletions apps/typegpu-docs/src/components/CodeEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,50 @@
import Editor, { type BeforeMount, type Monaco, type OnMount } from '@monaco-editor/react';
import type { editor } from 'monaco-editor';
import Editor, { type Monaco, type OnMount } from '@monaco-editor/react';
import type { editor } from 'tsover-monaco-editor';
import { entries, filter, fromEntries, isTruthy, map, pipe } from 'remeda';
import { SANDBOX_MODULES } from '../utils/examples/sandboxModules.ts';
import type { ExampleCommonFile, ExampleSrcFile } from '../utils/examples/types.ts';
import { tsCompilerOptions } from '../utils/liveEditor/embeddedTypeScript.ts';
import {
tsnotoverCompilerOptions,
tsoverCompilerOptions,
} from '../utils/liveEditor/embeddedTypeScript.ts';

function handleEditorWillMount(monaco: Monaco) {
// This setup is required for tsover to work, because monaco-react won't use custom monaco without `loader.config()`
// Config docs: https://www.npmjs.com/package/@monaco-editor/react?activeTab=readme#loader-config
// Integration docs: https://github.com/microsoft/monaco-editor/blob/main/docs/integrate-esm.md
import { loader } from '@monaco-editor/react';
import * as monaco from 'tsover-monaco-editor';
// oxlint-disable-next-line import/default
import editorWorker from 'tsover-monaco-editor/esm/vs/editor/editor.worker?worker';
// oxlint-disable-next-line import/default
import jsonWorker from 'tsover-monaco-editor/esm/vs/language/json/json.worker?worker';
// oxlint-disable-next-line import/default
import cssWorker from 'tsover-monaco-editor/esm/vs/language/css/css.worker?worker';
// oxlint-disable-next-line import/default
import htmlWorker from 'tsover-monaco-editor/esm/vs/language/html/html.worker?worker';
// oxlint-disable-next-line import/default
import tsWorker from 'tsover-monaco-editor/esm/vs/language/typescript/ts.worker?worker';

self.MonacoEnvironment = {
getWorker(_, label) {
switch (label) {
case 'json':
return new jsonWorker();
case 'css':
return new cssWorker();
case 'html':
return new htmlWorker();
case 'typescript':
case 'javascript':
return new tsWorker();
default:
return new editorWorker();
}
},
};

loader.config({ monaco });

const handleEditorWillMount = (tsover: boolean) => (monaco: Monaco) => {
const tsDefaults = monaco?.languages.typescript.typescriptDefaults;

const reroutes = pipe(
Expand Down Expand Up @@ -34,10 +73,10 @@ function handleEditorWillMount(monaco: Monaco) {
}

tsDefaults.setCompilerOptions({
...tsCompilerOptions,
...(tsover ? tsoverCompilerOptions : tsnotoverCompilerOptions),
paths: reroutes,
});
}
};

function handleEditorOnMount(editor: editor.IStandaloneCodeEditor) {
// Folding regions in code automatically. Useful for code not strictly
Expand All @@ -46,45 +85,35 @@ function handleEditorOnMount(editor: editor.IStandaloneCodeEditor) {
}

type Props = {
language: 'typescript' | 'html';
tsoverEnabled: boolean;
file: ExampleSrcFile | ExampleCommonFile;
shown: boolean;
};

const createCodeEditorComponent =
(language: 'typescript' | 'html', beforeMount?: BeforeMount, onMount?: OnMount) =>
(props: Props) => {
const { file, shown } = props;

// Monaco needs relative paths to work correctly and '../../common/file.ts' will not do
const path =
'common' in file
? `common/${file.path}`
: `${file.exampleKey.replace('--', '/')}/${file.path}`;

return (
<div className={shown ? 'h-[calc(100%-7rem)] md:h-[calc(100%-3rem)]' : 'hidden'}>
<Editor
defaultLanguage={language}
value={file.tsnotoverContent ?? file.content}
path={path}
beforeMount={beforeMount}
onMount={onMount}
options={{
minimap: {
enabled: false,
},
readOnly: true,
domReadOnly: true,
}}
/>
</div>
);
};
export function CodeEditor(props: Props) {
const { language, tsoverEnabled, file, shown } = props;

export const TsCodeEditor = createCodeEditorComponent(
'typescript',
handleEditorWillMount,
handleEditorOnMount,
);
// Monaco needs relative paths to work correctly and '../../common/file.ts' will not do
const path =
'common' in file ? `common/${file.path}` : `${file.exampleKey.replace('--', '/')}/${file.path}`;

export const HtmlCodeEditor = createCodeEditorComponent('html');
return (
<div className={shown ? 'h-[calc(100%-7rem)] md:h-[calc(100%-3rem)]' : 'hidden'}>
<Editor
defaultLanguage={language}
value={tsoverEnabled ? file.content : (file.tsnotoverContent ?? file.content)}
path={path}
beforeMount={language === 'typescript' ? handleEditorWillMount(tsoverEnabled) : undefined}
onMount={language === 'typescript' ? (handleEditorOnMount as OnMount) : undefined}
options={{
minimap: {
enabled: false,
},
readOnly: true,
domReadOnly: true,
}}
/>
</div>
);
}
19 changes: 18 additions & 1 deletion apps/typegpu-docs/src/components/ControlPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import {
type ExampleControlParam,
exampleControlsAtom,
} from '../utils/examples/exampleControlAtom.ts';
import { codeEditorShownAtom, menuShownAtom } from '../utils/examples/exampleViewStateAtoms.ts';
import {
codeEditorShownAtom,
menuShownAtom,
tsoverUsedAtom,
} from '../utils/examples/exampleViewStateAtoms.ts';
import { isGPUSupported } from '../utils/isGPUSupported.ts';
import { Button } from './design/Button.tsx';
import { ColorPicker } from './design/ColorPicker.tsx';
Expand Down Expand Up @@ -280,10 +284,12 @@ const unreachable = (_: never) => null;
export function ControlPanel() {
const [menuShowing, setMenuShowing] = useAtom(menuShownAtom);
const [codeEditorShowing, setCodeEditorShowing] = useAtom(codeEditorShownAtom);
const [tsoverUsed, setTsoverUsed] = useAtom(tsoverUsedAtom);
const exampleControlParams = useAtomValue(exampleControlsAtom);

const showLeftMenuId = useId();
const showCodeEditorId = useId();
const tsoverUsedId = useId();

return (
<div
Expand Down Expand Up @@ -318,6 +324,17 @@ export function ControlPanel() {
onChange={(e) => setCodeEditorShowing(e.target.checked)}
/>
</label>
<label
htmlFor={tsoverUsedId}
className="flex cursor-pointer items-center justify-between gap-3 text-sm"
>
<span>Use operator overloads</span>
<Toggle
id={tsoverUsedId}
checked={tsoverUsed}
onChange={(e) => setTsoverUsed(e.target.checked)}
/>
</label>

<hr className="my-0 box-border w-full border-tameplum-100 border-t" />
</div>
Expand Down
20 changes: 16 additions & 4 deletions apps/typegpu-docs/src/components/ExampleView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import cs from 'classnames';
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import { type RefObject, useEffect, useRef, useState } from 'react';
import { currentSnackbarAtom } from '../utils/examples/currentSnackbarAtom.ts';
import { codeEditorShownAtom } from '../utils/examples/exampleViewStateAtoms.ts';
import { codeEditorShownAtom, tsoverUsedAtom } from '../utils/examples/exampleViewStateAtoms.ts';
import { ExecutionCancelledError } from '../utils/examples/errors.ts';
import { exampleControlsAtom } from '../utils/examples/exampleControlAtom.ts';
import { executeExample } from '../utils/examples/exampleRunner.ts';
import type { ExampleState } from '../utils/examples/exampleState.ts';
import type { Example, ExampleCommonFile, ExampleSrcFile } from '../utils/examples/types.ts';
import { isGPUSupported } from '../utils/isGPUSupported.ts';
import { HtmlCodeEditor, TsCodeEditor } from './CodeEditor.tsx';
import { CodeEditor } from './CodeEditor.tsx';
import { ControlPanel } from './ControlPanel.tsx';
import { Button } from './design/Button.tsx';
import { Snackbar } from './design/Snackbar.tsx';
Expand Down Expand Up @@ -70,6 +70,7 @@ export function ExampleView({ example, common }: Props) {
const [currentFilePath, setCurrentFilePath] = useState<string>('index.ts');

const codeEditorShown = useAtomValue(codeEditorShownAtom);
const tsoverUsed = useAtomValue(tsoverUsedAtom);
const exampleHtmlRef = useRef<HTMLDivElement>(null);

const tsFiles = filterRelevantTsFiles(srcFiles, common);
Expand Down Expand Up @@ -140,10 +141,21 @@ export function ExampleView({ example, common }: Props) {
</div>
</div>

<HtmlCodeEditor shown={currentFilePath === 'index.html'} file={htmlFile} />
<CodeEditor
shown={currentFilePath === 'index.html'}
file={htmlFile}
language={'html'}
tsoverEnabled={false}
/>

{tsFiles.map((file) => (
<TsCodeEditor key={file.path} shown={file.path === currentFilePath} file={file} />
<CodeEditor
key={file.path}
shown={file.path === currentFilePath}
language={'typescript'}
tsoverEnabled={tsoverUsed}
file={file}
/>
))}
</div>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { entries, filter, fromEntries, isTruthy, map, pipe } from 'remeda';
import type { Monaco } from '@monaco-editor/react';
import { SANDBOX_MODULES } from '../../../utils/examples/sandboxModules.ts';
import { tsCompilerOptions } from '../../../utils/liveEditor/embeddedTypeScript.ts';
import { tsnotoverCompilerOptions } from '../../../utils/liveEditor/embeddedTypeScript.ts';

export const LANGUAGE_MAP: Record<string, string> = {
wgsl: 'wgsl',
Expand Down Expand Up @@ -63,7 +63,7 @@ export function setupMonacoEditor(monaco: Monaco) {
}

tsDefaults.setCompilerOptions({
...tsCompilerOptions,
...tsnotoverCompilerOptions,
paths: reroutes,
});
}
2 changes: 2 additions & 0 deletions apps/typegpu-docs/src/utils/examples/exampleViewStateAtoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export const codeEditorShownAtom = atomWithStorage(
storageOptions,
);

export const tsoverUsedAtom = atomWithStorage('tsover-used', true, undefined, storageOptions);

export const experimentalExamplesShownAtom = atomWithStorage(
'experimental-examples-shown',
true,
Expand Down
10 changes: 8 additions & 2 deletions apps/typegpu-docs/src/utils/liveEditor/embeddedTypeScript.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { languages } from 'monaco-editor';
// @ts-nocheck -- language.typescript is deprecated, but its replacement is not yet exported
import { languages } from 'tsover-monaco-editor';

export const tsCompilerOptions: languages.typescript.CompilerOptions = {
export const tsnotoverCompilerOptions: languages.typescript.CompilerOptions = {
target: languages.typescript.ScriptTarget.ESNext,
allowNonTsExtensions: true,
strict: true,
Expand All @@ -12,3 +13,8 @@ export const tsCompilerOptions: languages.typescript.CompilerOptions = {
baseUrl: '.',
lib: ['dom', 'es2021'],
};

export const tsoverCompilerOptions: languages.typescript.CompilerOptions = {
...tsnotoverCompilerOptions,
lib: [...tsnotoverCompilerOptions.lib, 'tsover'],
};
Loading
Loading