- {consoleEvents.map((consoleEvent) => {
- const { method, times } = consoleEvent;
- return (
-
);
diff --git a/client/modules/IDE/components/ConsoleInput.jsx b/client/modules/IDE/components/ConsoleInput.jsx
deleted file mode 100644
index 274845192f..0000000000
--- a/client/modules/IDE/components/ConsoleInput.jsx
+++ /dev/null
@@ -1,132 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import CodeMirror from 'codemirror';
-import { Encode } from 'console-feed';
-
-import RightArrowIcon from '../../../images/right-arrow.svg';
-import { dispatch } from '../../../utils/dispatcher';
-
-// heavily inspired by
-// https://github.com/codesandbox/codesandbox-client/blob/92a1131f4ded6f7d9c16945dc7c18aa97c8ada27/packages/app/src/app/components/Preview/DevTools/Console/Input/index.tsx
-
-class ConsoleInput extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- commandHistory: [],
- commandCursor: -1
- };
- }
-
- componentDidMount() {
- this._cm = CodeMirror(this.codemirrorContainer, { // eslint-disable-line
- theme: `p5-${this.props.theme}`,
- scrollbarStyle: null,
- keymap: 'sublime',
- mode: 'javascript',
- inputStyle: 'contenteditable'
- });
-
- this._cm.on('keydown', (cm, e) => {
- if (e.key === 'Enter' && !e.shiftKey) {
- e.preventDefault();
- e.stopPropagation();
- const value = cm.getValue();
- if (value.trim(' ') === '') {
- return false;
- }
- const messages = [{ log: Encode({ method: 'command', data: [value] }) }];
- const consoleEvent = [{ method: 'command', data: [value] }];
- dispatch({
- source: 'console',
- messages
- });
- this.props.dispatchConsoleEvent(consoleEvent);
- cm.setValue('');
- this.setState(state => ({
- commandCursor: -1,
- commandHistory: [value, ...state.commandHistory],
- }));
- } else if (e.key === 'ArrowUp') {
- const lineNumber = this._cm.getDoc().getCursor().line;
- if (lineNumber !== 0) {
- return false;
- }
-
- this.setState((state) => {
- const newCursor = Math.min(
- state.commandCursor + 1,
- state.commandHistory.length - 1
- );
- this._cm
- .getDoc()
- .setValue(state.commandHistory[newCursor] || '');
- const cursorPos = this._cm.getDoc().getLine(0).length - 1;
- this._cm.getDoc().setCursor({ line: 0, ch: cursorPos });
- return { commandCursor: newCursor };
- });
- } else if (e.key === 'ArrowDown') {
- const lineNumber = this._cm.getDoc().getCursor().line;
- const lineCount = this._cm.getValue().split('\n').length;
- if (lineNumber + 1 !== lineCount) {
- return false;
- }
-
- this.setState((state) => {
- const newCursor = Math.max(state.commandCursor - 1, -1);
- this._cm
- .getDoc()
- .setValue(state.commandHistory[newCursor] || '');
- const newLineCount = this._cm.getValue().split('\n').length;
- const newLine = this._cm.getDoc().getLine(newLineCount);
- const cursorPos = newLine ? newLine.length - 1 : 1;
- this._cm.getDoc().setCursor({ line: lineCount, ch: cursorPos });
- return { commandCursor: newCursor };
- });
- }
- return true;
- });
-
- this._cm.getWrapperElement().style['font-size'] = `${this.props.fontSize}px`;
- }
-
- componentDidUpdate(prevProps) {
- this._cm.setOption('theme', `p5-${this.props.theme}`);
- this._cm.getWrapperElement().style['font-size'] = `${this.props.fontSize}px`;
- this._cm.refresh();
- }
-
- componentWillUnmount() {
- this._cm = null;
- }
-
- render() {
- return (
-
-
-
-
-
{ this.codemirrorContainer = element; }} className="console__editor" />
-
- );
- }
-}
-
-ConsoleInput.propTypes = {
- theme: PropTypes.string.isRequired,
- dispatchConsoleEvent: PropTypes.func.isRequired,
- fontSize: PropTypes.number.isRequired
-};
-
-
-export default ConsoleInput;
diff --git a/client/modules/IDE/components/Editor.jsx b/client/modules/IDE/components/Editor.jsx
index ae379334b7..4b6ed053e8 100644
--- a/client/modules/IDE/components/Editor.jsx
+++ b/client/modules/IDE/components/Editor.jsx
@@ -191,7 +191,6 @@ class Editor extends React.Component {
const oldDoc = this._cm.swapDoc(this._docs[this.props.file.id]);
this._docs[prevProps.file.id] = oldDoc;
this._cm.focus();
-
if (!prevProps.unsavedChanges) {
setTimeout(() => this.props.setUnsavedChanges(false), 400);
}
diff --git a/client/modules/IDE/components/PreviewFrame.jsx b/client/modules/IDE/components/PreviewFrame.jsx
index 142cb5ece7..ef33bb3f64 100644
--- a/client/modules/IDE/components/PreviewFrame.jsx
+++ b/client/modules/IDE/components/PreviewFrame.jsx
@@ -2,11 +2,13 @@ import PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';
// import escapeStringRegexp from 'escape-string-regexp';
+import { isEqual } from 'lodash';
import srcDoc from 'srcdoc-polyfill';
import loopProtect from 'loop-protect';
import { JSHINT } from 'jshint';
import decomment from 'decomment';
import classNames from 'classnames';
+import { Decode } from 'console-feed';
import { getBlobUrl } from '../actions/files';
import { resolvePathToFile } from '../../../../server/utils/filePath';
import {
@@ -19,7 +21,6 @@ import {
} from '../../../../server/utils/fileUtils';
import { hijackConsoleErrorsScript, startTag, getAllScriptOffsets }
from '../../../utils/consoleUtils';
-import { registerFrame } from '../../../utils/dispatcher';
const shouldRenderSketch = (props, prevProps = undefined) => {
@@ -41,18 +42,18 @@ const shouldRenderSketch = (props, prevProps = undefined) => {
class PreviewFrame extends React.Component {
constructor(props) {
super(props);
-
- this.iframe = React.createRef();
+ this.handleConsoleEvent = this.handleConsoleEvent.bind(this);
}
componentDidMount() {
+ window.addEventListener('message', this.handleConsoleEvent);
+
const props = {
...this.props,
previewIsRefreshing: this.props.previewIsRefreshing,
isAccessibleOutputPlaying: this.props.isAccessibleOutputPlaying
};
if (shouldRenderSketch(props)) this.renderSketch();
- registerFrame(this.iframe.current.contentWindow);
}
componentDidUpdate(prevProps) {
@@ -62,10 +63,51 @@ class PreviewFrame extends React.Component {
}
componentWillUnmount() {
- const iframeBody = this.iframe.current.contentDocument.body;
+ window.removeEventListener('message', this.handleConsoleEvent);
+ const iframeBody = this.iframeElement.contentDocument.body;
if (iframeBody) { ReactDOM.unmountComponentAtNode(iframeBody); }
}
+ handleConsoleEvent(messageEvent) {
+ if (Array.isArray(messageEvent.data)) {
+ const decodedMessages = messageEvent.data.map(message =>
+ Object.assign(Decode(message.log), {
+ source: message.source
+ }));
+
+ decodedMessages.every((message, index, arr) => {
+ const { data: args } = message;
+ let hasInfiniteLoop = false;
+ Object.keys(args).forEach((key) => {
+ if (typeof args[key] === 'string' && args[key].includes('Exiting potential infinite loop')) {
+ this.props.stopSketch();
+ this.props.expandConsole();
+ hasInfiniteLoop = true;
+ }
+ });
+ if (hasInfiniteLoop) {
+ return false;
+ }
+ if (index === arr.length - 1) {
+ Object.assign(message, { times: 1 });
+ return false;
+ }
+ const cur = Object.assign(message, { times: 1 });
+ const nextIndex = index + 1;
+ while (isEqual(cur.data, arr[nextIndex].data) && cur.method === arr[nextIndex].method) {
+ cur.times += 1;
+ arr.splice(nextIndex, 1);
+ if (nextIndex === arr.length) {
+ return false;
+ }
+ }
+ return true;
+ });
+
+ this.props.dispatchConsoleEvent(decodedMessages);
+ }
+ }
+
addLoopProtect(sketchDoc) {
const scriptsInHTML = sketchDoc.getElementsByTagName('script');
const scriptsInHTMLArray = Array.prototype.slice.call(scriptsInHTML);
@@ -284,7 +326,7 @@ class PreviewFrame extends React.Component {
}
renderSketch() {
- const doc = this.iframe.current;
+ const doc = this.iframeElement;
const localFiles = this.injectLocalFiles();
if (this.props.isPlaying) {
this.props.clearConsole();
@@ -313,7 +355,7 @@ class PreviewFrame extends React.Component {
role="main"
frameBorder="0"
title="sketch preview"
- ref={this.iframe}
+ ref={(element) => { this.iframeElement = element; }}
sandbox={sandboxAttributes}
/>
);
@@ -335,14 +377,17 @@ PreviewFrame.propTypes = {
url: PropTypes.string,
id: PropTypes.string.isRequired
})).isRequired,
+ dispatchConsoleEvent: PropTypes.func.isRequired,
endSketchRefresh: PropTypes.func.isRequired,
previewIsRefreshing: PropTypes.bool.isRequired,
fullView: PropTypes.bool,
setBlobUrl: PropTypes.func.isRequired,
+ stopSketch: PropTypes.func.isRequired,
+ expandConsole: PropTypes.func.isRequired,
clearConsole: PropTypes.func.isRequired,
cmController: PropTypes.shape({
getContent: PropTypes.func
- })
+ }),
};
PreviewFrame.defaultProps = {
diff --git a/client/modules/IDE/hooks/useHandleMessageEvent.js b/client/modules/IDE/hooks/useHandleMessageEvent.js
deleted file mode 100644
index 2bd22168b7..0000000000
--- a/client/modules/IDE/hooks/useHandleMessageEvent.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import { useDispatch } from 'react-redux';
-import { Decode } from 'console-feed';
-import { isEqual } from 'lodash';
-import { dispatchConsoleEvent } from '../actions/console';
-import { stopSketch, expandConsole } from '../actions/console';
-
-export default function useHandleMessageEvent() {
- const dispatch = useDispatch();
-
- const handleMessageEvent = (data) => {
- const { source, messages } = data;
- if (source === 'sketch' && Array.isArray(messages)) {
- const decodedMessages = messages.map(message => Decode(message.log));
- decodedMessages.every((message, index, arr) => {
- const { data: args } = message;
- let hasInfiniteLoop = false;
- Object.keys(args).forEach((key) => {
- if (typeof args[key] === 'string' && args[key].includes('Exiting potential infinite loop')) {
- dispatch(stopSketch());
- dispatch(expandConsole());
- hasInfiniteLoop = true;
- }
- });
- if (hasInfiniteLoop) {
- return false;
- }
- if (index === arr.length - 1) {
- Object.assign(message, { times: 1 });
- return false;
- }
- // this should be done in the reducer probs
- const cur = Object.assign(message, { times: 1 });
- const nextIndex = index + 1;
- while (isEqual(cur.data, arr[nextIndex].data) && cur.method === arr[nextIndex].method) {
- cur.times += 1;
- arr.splice(nextIndex, 1);
- if (nextIndex === arr.length) {
- return false;
- }
- }
- return true;
- });
- dispatch(dispatchConsoleEvent(decodedMessages));
- }
- };
- return handleMessageEvent;
-}
diff --git a/client/modules/IDE/pages/MobileIDEView.jsx b/client/modules/IDE/pages/MobileIDEView.jsx
index 963b4fbafa..0d58887e04 100644
--- a/client/modules/IDE/pages/MobileIDEView.jsx
+++ b/client/modules/IDE/pages/MobileIDEView.jsx
@@ -37,7 +37,7 @@ import Dropdown from '../../../components/Dropdown';
import { getIsUserOwner } from '../selectors/users';
-import { useEffectWithComparison, useEventListener } from '../hooks/custom-hooks';
+import { useEffectWithComparison, useEventListener } from '../../../utils/custom-hooks';
import * as device from '../../../utils/device';
diff --git a/client/styles/abstracts/_variables.scss b/client/styles/abstracts/_variables.scss
index d356c68168..d0f62b27b4 100644
--- a/client/styles/abstracts/_variables.scss
+++ b/client/styles/abstracts/_variables.scss
@@ -64,11 +64,7 @@ $themes: (
icon-toast-hover-color: $lightest,
shadow-color: rgba(0, 0, 0, 0.16),
console-background-color: $light,
- console-input-background-color: $lightest,
- console-color: $darker,
- console-logged-times-color: $lightest,
- console-arrow-color: $middle-gray,
- console-active-arrow-color: #0071AD,
+ console-color: $lightest,
console-header-background-color: $medium-light,
console-header-color: $darker,
console-info-background-color: #5276B7,
@@ -144,11 +140,7 @@ $themes: (
icon-toast-hover-color: $lightest,
shadow-color: rgba(0, 0, 0, 0.16),
console-background-color: $dark,
- console-input-background-color: $darker,
console-color: $lightest,
- console-logged-times-color: $dark,
- console-arrow-color: $medium-light,
- console-active-arrow-color: #097BB3,
console-header-background-color: $medium-dark,
console-header-color: $lightest,
console-info-background-color: #5276B7,
@@ -222,11 +214,7 @@ $themes: (
icon-toast-hover-color: $yellow,
shadow-color: rgba(0, 0, 0, 0.16),
console-background-color: $dark,
- console-input-background-color: $darker,
- console-color: $lightest,
- console-logged-times-color: $darker,
- console-arrow-color: $lightest,
- console-active-arrow-color: $dodgerblue,
+ console-color: $black,
console-header-background-color: $medium-dark,
console-header-color: $lightest,
console-info-background-color: $lightsteelblue,
diff --git a/client/styles/components/_console-input.scss b/client/styles/components/_console-input.scss
deleted file mode 100644
index 84db1f3387..0000000000
--- a/client/styles/components/_console-input.scss
+++ /dev/null
@@ -1,52 +0,0 @@
-.console__input {
- width: 100%;
- display: flex;
- align-items: start;
- @include themify() {
- background-color: getThemifyVariable('console-input-background-color');
- }
-}
-
-.console__input .console-active__arrow {
- width: auto;
- height: 38%;
- & path {
- @include themify() {
- fill: getThemifyVariable('console-active-arrow-color');
- opacity: 1;
- }
- }
-}
-
-.console-active__arrow-container {
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- margin-left: #{10 / $base-font-size}rem;
-}
-
-.console__editor {
- margin-left: #{15 / $base-font-size}rem;
- flex: 1;
- & .CodeMirror {
- height: auto;
- }
- & .CodeMirror-lines {
- padding-top: #{2 / $base-font-size}rem;
- }
-}
-
-.console__editor .CodeMirror {
- border: none;
- font-family: Inconsolata,monospace;
- @include themify() {
- background-color: getThemifyVariable('console-input-background-color');
- }
-
- .CodeMirror-line {
- @include themify() {
- color: getThemifyVariable('console-color');
- }
- }
-}
\ No newline at end of file
diff --git a/client/styles/components/_console.scss b/client/styles/components/_console.scss
index e3854a6764..29e26f62c5 100644
--- a/client/styles/components/_console.scss
+++ b/client/styles/components/_console.scss
@@ -3,11 +3,12 @@
background: getThemifyVariable('console-background-color');
border-color: getThemifyVariable('ide-border-color');
}
- border-left: #{1 / $base-font-size}rem solid;
- border-right: #{1 / $base-font-size}rem solid;
+ border-left: 1px solid;
+ border-right: 1px solid;
width: 100%;
height: 100%;
z-index: 1000;
+ overflow: hidden;
display: flex;
flex-direction: column;
@@ -15,6 +16,14 @@
position:relative;
text-align:left;
}
+
+ .preview-console__message {
+ @include themify() {
+ color: getThemifyVariable('console-color');
+ }
+ flex: 1 0 auto;
+ position: relative;
+ }
}
.preview-console__header {
@@ -40,9 +49,8 @@
.preview-console__messages {
display: flex;
- flex: 1;
flex-direction: column;
- overflow-y: scroll;
+ overflow-y: auto;
}
.preview-console__collapse {
@@ -101,7 +109,6 @@
font-weight: bold;
margin: #{2 / $base-font-size}rem 0 0 #{8 / $base-font-size}rem;
- border-radius: #{10 / $base-font-size}rem;
padding: #{1 / $base-font-size}rem #{4 / $base-font-size}rem;
z-index: 100;
left: 0;
@@ -128,16 +135,3 @@
}
}
}
-
-.preview-console__body {
- display: flex;
- flex-direction: column;
- height: calc(100% - #{30 / $base-font-size}rem);
-
- .preview-console__message {
- position: relative;
- @include themify() {
- color: getThemifyVariable('console-logged-times-color');
- }
- }
-}
diff --git a/client/styles/components/_p5-contrast-codemirror-theme.scss b/client/styles/components/_p5-contrast-codemirror-theme.scss
index b9f837cc15..0ec609b834 100644
--- a/client/styles/components/_p5-contrast-codemirror-theme.scss
+++ b/client/styles/components/_p5-contrast-codemirror-theme.scss
@@ -131,7 +131,3 @@ $p5-contrast-activeline: #999999;
background-color: white;
color: #333;
}
-
-.cm-s-p5-contrast .CodeMirror-cursor {
- border-left: 1px solid $p5-contrast-white;
-}
diff --git a/client/styles/components/_p5-dark-codemirror-theme.scss b/client/styles/components/_p5-dark-codemirror-theme.scss
index 51879fdfd9..9e16951432 100644
--- a/client/styles/components/_p5-dark-codemirror-theme.scss
+++ b/client/styles/components/_p5-dark-codemirror-theme.scss
@@ -138,7 +138,3 @@ $p5-dark-error: #df3a3d;
background-color: white;
color: #333;
}
-
-.cm-s-p5-dark .CodeMirror-cursor {
- border-left: 1px solid $p5-dark-white;
-}
diff --git a/client/styles/components/_p5-light-codemirror-theme.scss b/client/styles/components/_p5-light-codemirror-theme.scss
index 28345a34e0..485aa7beef 100644
--- a/client/styles/components/_p5-light-codemirror-theme.scss
+++ b/client/styles/components/_p5-light-codemirror-theme.scss
@@ -131,7 +131,3 @@ $p5-light-activeline: rgb(207, 207, 207);
background-color: #333;
color: white;
}
-
-.cm-s-p5-light .CodeMirror-cursor {
- border-left: 1px solid $p5-light-black;
-}
diff --git a/client/styles/main.scss b/client/styles/main.scss
index b9e4fa53c4..8ba66af71e 100644
--- a/client/styles/main.scss
+++ b/client/styles/main.scss
@@ -44,7 +44,6 @@
@import 'components/keyboard-shortcuts';
@import 'components/copyable-input';
@import 'components/feedback';
-@import 'components/console-input';
@import 'components/loader';
@import 'components/uploader';
@import 'components/tabs';
diff --git a/client/modules/IDE/hooks/custom-hooks.js b/client/utils/custom-hooks.js
similarity index 100%
rename from client/modules/IDE/hooks/custom-hooks.js
rename to client/utils/custom-hooks.js
diff --git a/client/utils/dispatcher.js b/client/utils/dispatcher.js
deleted file mode 100644
index 6745840999..0000000000
--- a/client/utils/dispatcher.js
+++ /dev/null
@@ -1,48 +0,0 @@
-// Inspired by
-// https://github.com/codesandbox/codesandbox-client/blob/master/packages/codesandbox-api/src/dispatcher/index.ts
-
-let frame = null;
-let listener = null;
-const { origin } = window;
-
-export function registerFrame(newFrame) {
- frame = newFrame;
-}
-
-function notifyListener(message) {
- if (listener) listener(message);
-}
-
-function notifyFrame(message) {
- const rawMessage = JSON.parse(JSON.stringify(message));
- if (frame && frame.postMessage) {
- frame.postMessage(rawMessage, origin);
- }
-}
-
-export function dispatch(message) {
- if (!message) return;
-
- notifyListener(message);
- notifyFrame(message);
-}
-
-/**
- * Call callback to remove listener
- */
-export function listen(callback) {
- listener = callback;
- return () => {
- listener = null;
- };
-}
-
-function eventListener(e) {
- const { data } = e;
-
- if (data && e.origin === origin) {
- notifyListener(data);
- }
-}
-
-window.addEventListener('message', eventListener);
diff --git a/client/utils/evaluateExpression.js b/client/utils/evaluateExpression.js
deleted file mode 100644
index 570adf6fa2..0000000000
--- a/client/utils/evaluateExpression.js
+++ /dev/null
@@ -1,28 +0,0 @@
-function __makeEvaluateExpression(evalInClosure) {
- return expr => evalInClosure(`
- ${expr}`);
-}
-
-function evaluateExpression() {
- return __makeEvaluateExpression((expr) => {
- let newExpr = expr;
- let result = null;
- let error = false;
- try {
- try {
- const wrapped = `(${expr})`;
- const validate = new Function(wrapped); // eslint-disable-line
- newExpr = wrapped; // eslint-disable-line
- } catch (e) {
- // We shouldn't wrap the expression
- }
- result = (0, eval)(newExpr); // eslint-disable-line
- } catch (e) {
- result = `${e.name}: ${e.message}`;
- error = true;
- }
- return { result, error };
- });
-}
-
-export default evaluateExpression();
diff --git a/client/utils/previewEntry.js b/client/utils/previewEntry.js
index 7db7fb478d..59858938b5 100644
--- a/client/utils/previewEntry.js
+++ b/client/utils/previewEntry.js
@@ -1,6 +1,5 @@
import loopProtect from 'loop-protect';
-import { Hook, Decode, Encode } from 'console-feed';
-import evaluateExpression from './evaluateExpression';
+import { Hook } from 'console-feed';
window.loopProtect = loopProtect;
@@ -8,36 +7,13 @@ const consoleBuffer = [];
const LOGWAIT = 500;
Hook(window.console, (log) => {
consoleBuffer.push({
- log
+ log,
+ source: 'sketch'
});
});
setInterval(() => {
if (consoleBuffer.length > 0) {
- const message = {
- messages: consoleBuffer,
- source: 'sketch'
- };
- window.parent.postMessage(message, window.origin);
+ window.parent.postMessage(consoleBuffer, '*');
consoleBuffer.length = 0;
}
}, LOGWAIT);
-
-function handleMessageEvent(e) {
- if (window.origin !== e.origin) return;
- const { data } = e;
- const { source, messages } = data;
- if (source === 'console' && Array.isArray(messages)) {
- const decodedMessages = messages.map(message => Decode(message.log));
- decodedMessages.forEach((message) => {
- const { data: args } = message;
- const { result, error } = evaluateExpression(args);
- const resultMessages = [{ log: Encode({ method: error ? 'error' : 'result', data: [result] }) }];
- window.parent.postMessage({
- messages: resultMessages,
- source: 'sketch'
- }, window.origin);
- });
- }
-}
-
-window.addEventListener('message', handleMessageEvent);