Skip to content

Commit

Permalink
fix: cleanup and improve perf
Browse files Browse the repository at this point in the history
  • Loading branch information
nmn committed Dec 16, 2024
1 parent bbc0460 commit 72fb564
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 96 deletions.
25 changes: 13 additions & 12 deletions apps/docs/components/LogoDownloadModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ const styles = stylex.create({
position: 'fixed',
top: 60,
left: 20,
padding: '16px',
borderRadius: '8px',
padding: 16,
borderRadius: 8,
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
maxWidth: '16.25rem',
maxHeight: '80vh',
Expand All @@ -39,12 +39,12 @@ const styles = stylex.create({
color: '#FFFFFF',
},
section: {
marginBottom: '16px',
marginBottom: 16,
},
sectionTitle: {
fontSize: '14px',
fontSize: 14,
fontWeight: 'bold',
marginBottom: '8px',
marginBottom: 8,
},
buttonGroup: {
display: 'flex',
Expand All @@ -54,7 +54,8 @@ const styles = stylex.create({
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-start',
padding: '12px 16px',
paddingBlock: 12,
paddingInline: 16,
cursor: 'pointer',
backgroundColor: {
default: 'var(--ifm-background-color)',
Expand All @@ -64,17 +65,17 @@ const styles = stylex.create({
default: 'var(--ifm-font-color-base)',
':hover': '#FFFFFF',
},
borderRadius: '4px',
fontSize: '14px',
borderRadius: 4,
fontSize: 14,
transition: 'background-color 0.2s, color 0.2s',
borderStyle: 'none',
width: '100%',
marginBottom: '8px',
marginBottom: 8,
},
icon: {
marginRight: '8px',
width: '16px',
height: '16px',
marginRight: 8,
width: 16,
height: 16,
fill: 'currentColor',
},
});
Expand Down
146 changes: 90 additions & 56 deletions apps/docs/components/Playground.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* @format
*/

// Import necessary components and libraries
import BrowserOnly from '@docusaurus/BrowserOnly';
import { library } from '@fortawesome/fontawesome-svg-core';
import { faBars, faRotateRight } from '@fortawesome/free-solid-svg-icons';
Expand All @@ -18,9 +19,17 @@ import { useEffect, useRef, useState } from 'react';
import { UnControlled as CodeMirror } from 'react-codemirror2';
import 'codemirror/mode/javascript/javascript';
import { files } from './playground-utils/files';
import useDebounced from './hooks/useDebounced';

// Add FontAwesome icons to the library
library.add(faBars, faRotateRight);

/**
* Function to spawn a command in the WebContainer instance.
* @param {WebContainer} instance - The WebContainer instance.
* @param {...string} args - Command arguments to be executed.
* @returns {Promise} - Promise that resolves when the command execution is successful.
*/
async function wcSpawn(instance, ...args) {
console.log('Running:', args.join(' '));
const process = await instance.spawn(...args);
Expand All @@ -41,6 +50,10 @@ async function wcSpawn(instance, ...args) {
return process;
}

/**
* Function to initialize and configure the WebContainer.
* @returns {Promise} - Promise that resolves with the configured WebContainer instance.
*/
async function makeWebcontainer() {
console.log('Booting WebContainer...');
const instance = await WebContainer.boot();
Expand All @@ -57,17 +70,22 @@ async function makeWebcontainer() {
return instance;
}

/**
* Main component for the Playground.
* @returns {JSX.Element} - The rendered JSX element.
*/
export default function Playground() {
const instance = useRef(null);
const [url, setUrl] = useState(null);
const debounceTimeout = useRef(null);
const [code, setCode] = useState(
const [code, _setCode] = useState(
files.src.directory['App.jsx'].file.contents,
);
const [error, setError] = useState(null);
const loadingTimeout = useRef(null);
const urlRef = useRef(null);

/**
* Function to build the WebContainer and start the development server.
*/
const build = async () => {
const containerInstance = instance.current;
if (!containerInstance) {
Expand Down Expand Up @@ -96,32 +114,31 @@ export default function Playground() {
});
};

const updateFiles = async () => {
/**
* Function to update files in the WebContainer.
*/
const updateFiles = async (updatedCode) => {
const containerInstance = instance.current;
const filePath = './src/App.jsx';
const updatedCode = code;
await containerInstance.fs.writeFile(filePath, updatedCode);
};

const handleCodeChange = (newCode) => {
if (debounceTimeout.current) {
clearTimeout(debounceTimeout.current);
debounceTimeout.current = null;
}
const debouncedUpdateFiles = useDebounced(async (newCode) => {
await updateFiles(newCode);
}, 1000);

debounceTimeout.current = setTimeout(async () => {
setCode(newCode);
if (url) {
try {
await updateFiles();
console.log('Successfully applied changes.');
} catch (err) {
console.error(err);
}
}
}, 2000);
/**
* Function to handle code changes in the CodeMirror editor.
* @param {string} newCode - The new code content from the editor.
*/
const handleCodeChange = (newCode) => {
// setCode(newCode);
debouncedUpdateFiles(newCode);
};

/**
* Function to reload the WebContainer preview.
*/
const reloadWebContainer = async () => {
if (!url) return;
const iframe = document.querySelector('iframe');
Expand All @@ -130,7 +147,6 @@ export default function Playground() {
if (error) {
setError(null);
}
console.log('Reloading container preview...');
await reloadPreview(iframe);
} catch (err) {
console.error(`Error reloading preview: ${err.message}`);
Expand All @@ -140,45 +156,46 @@ export default function Playground() {
}
};

// useEffect to initialize the WebContainer and build it
useEffect(() => {
require('codemirror/mode/javascript/javascript');
let loadingTimeout;
makeWebcontainer().then((i) => {
instance.current = i;
build().then(() => {
loadingTimeout.current = setTimeout(() => {
console.log('running loading timeout');
console.log('instance: ', instance.current);
console.log('url ref: ', urlRef.current);
loadingTimeout = setTimeout(() => {
if (!urlRef.current) {
console.log('error due to timeout...');
setError(
'WebContainer failed to load. Please try reloading or use a different browser.',
);
}
loadingTimeout = null;
}, 10000);
});
});

() => {
// Cleanup function to unmount the WebContainer and clear timeouts
return () => {
instance.current.unmount();
if (debounceTimeout.current) {
clearTimeout(debounceTimeout.current);
}
if (loadingTimeout.current) {
clearTimeout(loadingTimeout.current);
if (loadingTimeout != null) {
clearTimeout(loadingTimeout);
}
};
}, []);

// Render the Playground component
return (
<div {...stylex.props(styles.root)}>
<header {...stylex.props(styles.header)}>
<button>
{/* <button>
<FontAwesomeIcon icon="fa-solid fa-bars" />
</button>
<button {...stylex.props(styles.reloadButton)}>
</button> */}
<button
{...stylex.props(styles.reloadButton)}
onClick={reloadWebContainer}
>
<FontAwesomeIcon
onClick={reloadWebContainer}
aria-label="reload"
icon="fa-solid fa-rotate-right"
/>
</button>
Expand All @@ -189,20 +206,24 @@ export default function Playground() {
<>
<CodeMirror
{...stylex.props(styles.textarea)}
onChange={(editor, data, newCode) => handleCodeChange(newCode)}
options={{
mode: 'javascript',
theme: 'material-darker',
lineNumbers: true,
}}
value={code}
onChange={(editor, data, newCode) => handleCodeChange(newCode)}
/>
{error ? (
<div {...stylex.props(styles.textarea)}>{error}</div>
<div {...stylex.props(styles.textarea, styles.centered)}>
{error}
</div>
) : url ? (
<iframe {...stylex.props(styles.textarea)} src={url} />
) : (
<div {...stylex.props(styles.textarea)}>Loading...</div>
<div {...stylex.props(styles.textarea, styles.centered)}>
Loading...
</div>
)}
</>
)}
Expand All @@ -212,47 +233,56 @@ export default function Playground() {
);
}

// Style definitions for the Playground component
const styles = stylex.create({
root: {
minHeight: '100vh',
backgroundColor: 'var(--bg1)',
},
header: {
height: '40px',
height: 40,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
paddingLeft: '20px',
paddingRight: '20px',
justifyContent: 'flex-end',
paddingInline: 4,
backgroundColor: 'var(--playground-container-bg)',
borderBottom: '1px solid rgba(255, 255, 255, 0.1)',
borderBottomWidth: 1,
borderBottomStyle: 'solid',
borderBottomColor: 'rgba(255, 255, 255, 0.1)',
boxShadow: '0 1px 4px rgba(0, 0, 0, 0.1)',
zIndex: 20,
},
reloadButton: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'transparent',
backgroundColor: {
default: 'transparent',
':hover': 'var(--ifm-color-primary-light)',
},
color: 'var(--fg1)',
width: '32px',
height: '32px',
borderRadius: '4px',
border: 'none',
width: 32,
height: 32,
borderRadius: 4,
borderStyle: 'none',
cursor: 'pointer',
zIndex: 20,
padding: '4px',
transition: 'background-color 200ms, transform 150ms',
':hover': {
backgroundColor: 'var(--ifm-color-primary-light)',
transform: 'scale(1.05)',
padding: 4,
transitionProperty: 'background-color, transform',
transitionDuration: '200ms, 150ms',
transform: {
default: null,
':hover': 'scale(1.05)',
},
},
container: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: stylex.firstThatWorks('calc(100dvh - 60px)', 'calc(100vh - 60px)'),
height: stylex.firstThatWorks(
'calc(100dvh - 100px)',
'calc(100vh - 100px)',
),
borderBottomWidth: 2,
borderBottomStyle: 'solid',
borderBottomColor: 'var(--cyan)',
Expand All @@ -267,4 +297,8 @@ const styles = stylex.create({
borderWidth: 0,
borderStyle: 'none',
},
centered: {
alignItems: 'center',
justifyContent: 'center',
},
});
32 changes: 32 additions & 0 deletions apps/docs/components/hooks/useDebounced.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/

import { useRef, useEffect } from 'react';

export default function useDebounced(fn, delay) {
const timeoutRef = useRef(null);

useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);

const debouncedFn = (...args) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
fn(...args);
}, delay);
};
return debouncedFn;
}
Loading

0 comments on commit 72fb564

Please sign in to comment.