diff --git a/components/buttons/fullscreenbutton.tsx b/components/buttons/fullscreenbutton.tsx
index 45b6882f..08615f90 100644
--- a/components/buttons/fullscreenbutton.tsx
+++ b/components/buttons/fullscreenbutton.tsx
@@ -12,10 +12,12 @@ export default function FullscreenButton() {
return (
diff --git a/components/buttons/scalebutton.tsx b/components/buttons/resolutionbutton.tsx
similarity index 55%
rename from components/buttons/scalebutton.tsx
rename to components/buttons/resolutionbutton.tsx
index d6414434..19d1958c 100644
--- a/components/buttons/scalebutton.tsx
+++ b/components/buttons/resolutionbutton.tsx
@@ -3,6 +3,7 @@ import Hd from '@mui/icons-material/Hd';
import Sd from '@mui/icons-material/Sd';
import Button from '@mui/material/Button';
import { useTheme } from '@mui/material/styles';
+import Resolution from 'components/resolution';
import { useAtom, useAtomValue } from 'jotai';
import { halfResolutionAtom } from 'lib/atoms/atoms';
@@ -11,18 +12,27 @@ const ScaleIcon = () => {
return halfResolution ? : ;
};
-export default function ScaleButton() {
+export default function ResolutionButton() {
const [halfResolution, setHalfResolution] = useAtom(halfResolutionAtom);
const theme = useTheme();
+
+ // Define styles outside the return for better readability
+ const buttonStyles = {
+ padding: '2px',
+ minWidth: 0,
+ color: halfResolution ? theme.palette.primary.contrastText : theme.palette.primary.light,
+ '&:hover': {
+ backgroundColor: 'rgba(255, 255, 255, 0.08)'
+ }
+ };
+
return (
);
diff --git a/components/editor/editor.tsx b/components/editor/editor.tsx
index 2a7c8205..1e7f92a4 100644
--- a/components/editor/editor.tsx
+++ b/components/editor/editor.tsx
@@ -6,6 +6,7 @@ import Giscus from '@giscus/react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Grid from '@mui/material/Grid';
+import Stack from '@mui/material/Stack';
import { useTheme } from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery';
import { User } from '@supabase/supabase-js';
@@ -15,7 +16,7 @@ import PlayPauseButton from 'components/buttons/playpausebutton';
import RecordButton from 'components/buttons/recordbutton';
import ReloadButton from 'components/buttons/reloadbutton';
import ResetButton from 'components/buttons/resetbutton';
-import ScaleButton from 'components/buttons/scalebutton';
+import ResolutionButton from 'components/buttons/resolutionbutton';
import VimButton from 'components/buttons/vimbutton';
import EntryPointDisplay from 'components/editor/entrypointdisplay';
import { MetadataEditor } from 'components/editor/metadataeditor';
@@ -31,6 +32,7 @@ import { createClient } from 'lib/supabase/client';
import dynamic from 'next/dynamic';
import { useCallback } from 'react';
import { ItemWithTransitionSignal } from 'theme/itemwithtransition';
+import { MonacoTheme } from 'theme/monacotheme';
import { Frame } from 'theme/theme';
import ConfigurationPicker from './configurationpicker';
import Explainer from './explainer';
@@ -43,21 +45,23 @@ interface EditorProps {
function Comments() {
return (
-
+
+
+
);
}
@@ -74,9 +78,6 @@ export default function Editor(props: EditorProps) {
}, []);
const Timer = dynamic(() => import('components/timer'), { ssr: false });
- const Resolution = dynamic(() => import('components/resolution'), {
- ssr: false
- });
let metadataEditor: JSX.Element | null = null;
if (supabase && !props.standalone) {
@@ -102,6 +103,43 @@ export default function Editor(props: EditorProps) {
};
}
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down('md'));
+
+ const monacoOptions = (isMobile: boolean) => ({
+ stopRenderingLineAfter: isMobile ? 500 : 1000,
+ fontSize: isMobile ? 12 : 12,
+ lineHeight: isMobile ? 16 : 18,
+ fontFamily: "'Fira Code', monospace",
+ 'bracketPairColorization.enabled': true,
+ mouseWheelZoom: true,
+ minimap: { enabled: !isMobile },
+ scrollBeyondLastLine: !isMobile,
+ automaticLayout: true,
+ lineNumbersMinChars: isMobile ? 3 : 4,
+ useShadowDOM: false // https://github.com/microsoft/monaco-editor/issues/3602
+ });
+
+ const monacoEditorWithButtons = (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+
const leftPanel = (
@@ -117,61 +155,63 @@ export default function Editor(props: EditorProps) {
embed={props.embed}
/>
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
{metadataEditor}
- {shaderID ?
: null}
+
+ {/* Show code right after shader metadata on mobile */}
+ {isMobile && monacoEditorWithButtons}
+
+ {/* Don't show comments on mobile */}
+ {!isMobile && (shaderID ?
: null)}
);
- const theme = useTheme();
-
const rightPanel = (
-
-
-
-
-
- {' '}
- {/* invisible button, used only for centering */}
-
-
-
-
-
-
-
-
-
+ {!isMobile && monacoEditorWithButtons}
.MuiGrid-item': {
+ minHeight: 'none',
+ maxHeight: '400px' // Prevents stretch on mobiles
+ }
+ }
}}
>
@@ -184,6 +224,7 @@ export default function Editor(props: EditorProps) {
+ {isMobile && (shaderID ?
: null)}
);
diff --git a/components/editor/explainer.tsx b/components/editor/explainer.tsx
index ab8403a7..9d8dc162 100644
--- a/components/editor/explainer.tsx
+++ b/components/editor/explainer.tsx
@@ -11,8 +11,6 @@ import DraggableWindow from '../global/draggable-window';
import { HiLite } from '../global/hilite';
import Logo from '../global/logo';
-const EXPLAINER_INNER_HEIGHT = '570';
-
const ExplainerBody = () => {
const theme = useTheme();
const prelude = useAtomValue(wgputoyPreludeAtom);
@@ -20,11 +18,11 @@ const ExplainerBody = () => {
return (
@@ -265,7 +263,21 @@ export default function Explainer() {
diff --git a/components/editor/monaco.tsx b/components/editor/monaco.tsx
index 02416455..96d3107e 100644
--- a/components/editor/monaco.tsx
+++ b/components/editor/monaco.tsx
@@ -280,6 +280,39 @@ const Monaco = props => {
);
// https://github.com/microsoft/monaco-editor/issues/392
document.fonts.ready.then(() => monaco.editor.remeasureFonts());
+
+ // https://github.com/suren-atoyan/monaco-react/issues/733
+ const handleMouseEvent = (e: MonacoEditor.IEditorMouseEvent) => {
+ const type = e['changedTouches']
+ ? MonacoEditor.MouseTargetType.UNKNOWN
+ : MonacoEditor.MouseTargetType.CONTENT_EMPTY;
+ if (e?.target?.type === type) {
+ setTimeout(() => {
+ const position = _editor.getPosition();
+ const line = position?.lineNumber || 1;
+ const column =
+ (_editor.getModel()?.getLineContent(line).length || 0) + 1;
+ _editor.setSelection(new monaco.Selection(line, column, line, column));
+ }, 12);
+ }
+ };
+ const handleTouchEvent = e => {
+ e.preventDefault();
+ const touch = e.changedTouches[0];
+ const mouseEvent = new MouseEvent('mouseup', {
+ bubbles: true,
+ cancelable: true,
+ clientX: touch.clientX,
+ clientY: touch.clientY,
+ buttons: 1
+ });
+ domNode!.dispatchEvent(mouseEvent);
+ };
+ const domNode = _editor.getDomNode();
+ _editor.onMouseDown(handleMouseEvent);
+ _editor.onMouseUp(handleMouseEvent);
+ domNode!.addEventListener('touchstart', handleTouchEvent, { passive: false });
+ domNode!.addEventListener('touchend', handleTouchEvent, { passive: false });
}}
options={props.editorOptions}
theme="global" // preference
diff --git a/components/resolution.tsx b/components/resolution.tsx
index b11bd042..a8a0af2e 100644
--- a/components/resolution.tsx
+++ b/components/resolution.tsx
@@ -8,14 +8,11 @@ export default function Resolution() {
const width = useAtomValue(widthAtom);
const height = useAtomValue(heightAtom);
- if (width > 0 && height > 0) {
- return (
-
-
- {width}x{height}
-
-
- );
- }
- return null;
+ return (
+
+
+ {width ? `${width}x${height}` : ''}
+
+
+ );
}
diff --git a/components/timer.tsx b/components/timer.tsx
index 6ef24b89..36a7aea6 100644
--- a/components/timer.tsx
+++ b/components/timer.tsx
@@ -3,7 +3,7 @@ import Box from '@mui/material/Box';
import { useAtomValue } from 'jotai';
import { useTransientAtom } from 'jotai-game';
import { isPlayingAtom, timerAtom } from 'lib/atoms/atoms';
-import { Fragment, useRef, useState } from 'react';
+import { useRef, useState } from 'react';
import { theme } from 'theme/theme';
import useAnimationFrame from 'use-animation-frame';
@@ -26,22 +26,17 @@ export default function Timer() {
}
});
- if (timer > 0 && fps > 0) {
- return (
-
-
-
- {timer.toFixed(1)}s / {fps.toFixed(1)} FPS
-
-
-
- );
- }
- return null;
+ return (
+
+ {fps.toFixed(1)} FPS
+ {timer.toFixed(1)}s
+
+ );
}
diff --git a/components/wgputoycontroller.tsx b/components/wgputoycontroller.tsx
index 17f6c1bc..19cdedad 100644
--- a/components/wgputoycontroller.tsx
+++ b/components/wgputoycontroller.tsx
@@ -27,7 +27,12 @@ import {
titleAtom,
widthAtom
} from 'lib/atoms/atoms';
-import { canvasElAtom, canvasParentElAtom, wgputoyPreludeAtom } from 'lib/atoms/wgputoyatoms';
+import {
+ canvasElAtom,
+ canvasParentElAtom,
+ wgpuAvailabilityAtom,
+ wgputoyPreludeAtom
+} from 'lib/atoms/wgputoyatoms';
import { ComputeEngine } from 'lib/engine';
import { getCompiler, TextureDimensions } from 'lib/slang/compiler';
import { useCallback, useEffect } from 'react';
@@ -86,7 +91,8 @@ const WgpuToyController = props => {
const setSaveColorTransitionSignal = useSetAtom(saveColorTransitionSignalAtom);
const canvas = useAtomValue(canvasElAtom);
- const [, setPrelude] = useAtom(wgputoyPreludeAtom);
+ const setPrelude = useSetAtom(wgputoyPreludeAtom);
+ const wgpuAvailability = useAtomValue(wgpuAvailabilityAtom);
const parentRef = useAtomValue(canvasParentElAtom);
@@ -544,12 +550,12 @@ const WgpuToyController = props => {
}, []);
useEffect(() => {
- if (!isPlaying()) {
+ if (!isPlaying() && wgpuAvailability === 'available') {
setPlay(true);
setNeedsInitialReset(true);
playCallback();
}
- }, []);
+ }, [wgpuAvailability]);
// Return a pauseCallback for the cleanup lifecycle
useEffect(() => pauseCallback, []);
diff --git a/theme/monacotheme.ts b/theme/monacotheme.ts
index 57766dd7..d18c4590 100644
--- a/theme/monacotheme.ts
+++ b/theme/monacotheme.ts
@@ -1,6 +1,15 @@
+import Box from '@mui/material/Box';
+import { styled } from '@mui/material/styles';
import { theme } from 'theme/theme';
import 'theme/themeModule';
+// fix: https://github.com/microsoft/monaco-editor/issues/3602
+export const MonacoTheme = styled(Box)(({ theme }) => ({
+ '.monaco-action-bar': {
+ backgroundColor: `${theme.palette.background.paper} !important`
+ }
+}));
+
export const defineMonacoTheme = (monaco, name: string) => {
return monaco.editor.defineTheme(name, {
base: 'vs-dark', // can also be vs-dark or hc-black