From a7b9535ff97ef7b15f31785bd98774f7bb3a7b72 Mon Sep 17 00:00:00 2001
From: Kushagra Gour
Date: Sun, 12 Aug 2018 15:18:00 +0530
Subject: [PATCH 01/29] WIP
---
src/components/ContentWrap2.jsx | 547 +++++++++++++++++++++++++++++
src/components/app.jsx | 7 +-
src/service-worker-registration.js | 1 -
src/service-worker.js | 40 +++
src/style.css | 10 +
5 files changed, 600 insertions(+), 5 deletions(-)
create mode 100644 src/components/ContentWrap2.jsx
create mode 100644 src/service-worker.js
diff --git a/src/components/ContentWrap2.jsx b/src/components/ContentWrap2.jsx
new file mode 100644
index 0000000..0cd9216
--- /dev/null
+++ b/src/components/ContentWrap2.jsx
@@ -0,0 +1,547 @@
+import { h, Component } from 'preact';
+import UserCodeMirror from './UserCodeMirror.jsx';
+import { computeHtml, computeCss, computeJs } from '../computes';
+import { modes, HtmlModes, CssModes, JsModes } from '../codeModes';
+import { log, writeFile, loadJS, getCompleteHtml } from '../utils';
+import { SplitPane } from './SplitPane.jsx';
+import { trackEvent } from '../analytics';
+import CodeMirror from '../CodeMirror';
+import CodeMirrorBox from './CodeMirrorBox';
+import { deferred } from '../deferred';
+const minCodeWrapSize = 33;
+
+/* global htmlCodeEl, jsCodeEl, cssCodeEl, logCountEl
+*/
+class Sidebar extends Component {
+ render() {
+ return (
+
+ );
+ }
+}
+
+export default class ContentWrap2 extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ isConsoleOpen: false,
+ isCssSettingsModalOpen: false,
+ files: [
+ { name: 'index.html' },
+ { name: 'style.css' },
+ { name: 'script.js' }
+ ]
+ };
+ this.state.selectedFile = this.state.files[0];
+ this.updateTimer = null;
+ this.updateDelay = 500;
+ this.htmlMode = HtmlModes.HTML;
+ this.prefs = {};
+ this.codeInPreview = { html: null, css: null, js: null };
+ this.cmCodes = { html: props.currentItem.html, css: '', js: '' };
+ this.cm = {};
+ this.logCount = 0;
+ }
+ shouldComponentUpdate(nextProps, nextState) {
+ return (
+ this.state.isConsoleOpen !== nextState.isConsoleOpen ||
+ this.state.isCssSettingsModalOpen !== nextState.isCssSettingsModalOpen ||
+ this.state.mainSplitSizes !== nextState.mainSplitSizes ||
+ this.state.selectedFile !== nextState.selectedFile ||
+ this.props.currentLayoutMode !== nextProps.currentLayoutMode ||
+ this.props.currentItem !== nextProps.currentItem ||
+ this.props.prefs !== nextProps.prefs
+ );
+ }
+ componentDidUpdate() {
+ // HACK: becuase its a DOM manipulation
+ // window.logCountEl.textContent = this.logCount;
+ // log('🚀', 'didupdate', this.props.currentItem);
+ // if (this.isValidItem(this.props.currentItem)) {
+ // this.refreshEditor();
+ // }
+ }
+ componentDidMount() {
+ this.props.onRef(this);
+ }
+
+ onHtmlCodeChange(editor, change) {
+ this.cmCodes.html = editor.getValue();
+ this.state.selectedFile.content = editor.getValue();
+ this.props.onCodeChange(
+ 'html',
+ this.cmCodes.html,
+ change.origin !== 'setValue'
+ );
+ this.onCodeChange(editor, change);
+ }
+
+ onCodeChange(editor, change) {
+ clearTimeout(this.updateTimer);
+
+ this.updateTimer = setTimeout(() => {
+ // This is done so that multiple simultaneous setValue don't trigger too many preview refreshes
+ // and in turn too many file writes on a single file (eg. preview.html).
+ if (change.origin !== 'setValue') {
+ // Specifically checking for false so that the condition doesn't get true even
+ // on absent key - possible when the setting key hasn't been fetched yet.
+ if (this.prefs.autoPreview !== false) {
+ this.setPreviewContent();
+ }
+
+ // Track when people actually are working.
+ trackEvent.previewCount = (trackEvent.previewCount || 0) + 1;
+ if (trackEvent.previewCount === 4) {
+ trackEvent('fn', 'usingPreview');
+ }
+ }
+ }, this.updateDelay);
+ }
+
+ createPreviewFile(html, css, js) {
+ const shouldInlineJs =
+ !window.webkitRequestFileSystem || !window.IS_EXTENSION;
+ var contents = getCompleteHtml(
+ html,
+ css,
+ shouldInlineJs ? js : null,
+ this.props.currentItem
+ );
+ var blob = new Blob([contents], { type: 'text/plain;charset=UTF-8' });
+ var blobjs = new Blob([js], { type: 'text/plain;charset=UTF-8' });
+
+ // Track if people have written code.
+ if (!trackEvent.hasTrackedCode && (html || css || js)) {
+ trackEvent('fn', 'hasCode');
+ trackEvent.hasTrackedCode = true;
+ }
+
+ if (shouldInlineJs) {
+ if (this.detachedWindow) {
+ log('✉️ Sending message to detached window');
+ this.detachedWindow.postMessage({ contents }, '*');
+ } else {
+ var obj = {};
+ this.state.files.forEach(file => {
+ obj[`/user/${file.name}`] = file.content || '';
+ });
+ // obj[`/user/index.html`] = this.cmCodes.html;
+
+ navigator.serviceWorker.controller.postMessage(obj);
+ this.frame.src = '/user/index.html';
+ }
+ } else {
+ // we need to store user script in external JS file to prevent inline-script
+ // CSP from affecting it.
+ writeFile('script.js', blobjs, () => {
+ writeFile('preview.html', blob, () => {
+ var origin = chrome.i18n.getMessage()
+ ? `chrome-extension://${chrome.i18n.getMessage('@@extension_id')}`
+ : `${location.origin}`;
+ var src = `filesystem:${origin}/temporary/preview.html`;
+ if (this.detachedWindow) {
+ this.detachedWindow.postMessage(src, '*');
+ } else {
+ this.frame.src = src;
+ }
+ });
+ });
+ }
+ }
+ cleanupErrors(lang) {
+ this.cm[lang].clearGutter('error-gutter');
+ }
+
+ showErrors(lang, errors) {
+ var editor = this.cm[lang];
+ errors.forEach(function(e) {
+ editor.operation(function() {
+ var n = document.createElement('div');
+ n.setAttribute('data-title', e.message);
+ n.classList.add('gutter-error-marker');
+ editor.setGutterMarker(e.lineNumber, 'error-gutter', n);
+ });
+ });
+ }
+
+ /**
+ * Generates the preview from the current code.
+ * @param {boolean} isForced Should refresh everything without any check or not
+ * @param {boolean} isManual Is this a manual preview request from user?
+ */
+ setPreviewContent(isForced, isManual) {
+ if (!this.props.prefs.autoPreview && !isManual) {
+ return;
+ }
+
+ if (!this.props.prefs.preserveConsoleLogs) {
+ // this.clearConsole();
+ }
+ this.cleanupErrors('html');
+ // this.cleanupErrors('css');
+ // this.cleanupErrors('js');
+
+ var currentCode = {
+ html: this.cmCodes.html,
+ css: this.cmCodes.css,
+ js: this.cmCodes.js
+ };
+ log('🔎 setPreviewContent', isForced);
+ const targetFrame = this.detachedWindow
+ ? this.detachedWindow.document.querySelector('iframe')
+ : this.frame;
+
+ const cssMode = this.props.currentItem.cssMode;
+ var htmlPromise = computeHtml(
+ currentCode.html,
+ this.props.currentItem.htmlMode
+ );
+ var cssPromise = computeCss(
+ cssMode === CssModes.ACSS ? currentCode.html : currentCode.css,
+ cssMode,
+ this.props.currentItem.cssSettings
+ );
+ var jsPromise = computeJs(
+ currentCode.js,
+ this.props.currentItem.jsMode,
+ true,
+ this.props.prefs.infiniteLoopTimeout
+ );
+ Promise.all([htmlPromise, cssPromise, jsPromise]).then(result => {
+ if (cssMode === CssModes.ACSS) {
+ this.cm.css.setValue(result[1].code || '');
+ }
+
+ this.createPreviewFile(
+ result[0].code || '',
+ result[1].code || '',
+ result[2].code || ''
+ );
+ result.forEach(resultItem => {
+ if (resultItem.errors) {
+ this.showErrors(resultItem.errors.lang, resultItem.errors.data);
+ }
+ });
+ });
+
+ this.codeInPreview.html = currentCode.html;
+ this.codeInPreview.css = currentCode.css;
+ this.codeInPreview.js = currentCode.js;
+ }
+ isValidItem(item) {
+ return !!item.title;
+ }
+ refreshEditor() {
+ this.cmCodes.html = this.props.currentItem.html;
+ this.cm.html.setValue(this.cmCodes.html || '');
+ this.cm.html.refresh();
+
+ // this.clearConsole();
+
+ // Set preview only when all modes are updated so that preview doesn't generate on partially
+ // correct modes and also doesn't happen 3 times.
+ Promise.all([this.updateHtmlMode(this.props.currentItem.htmlMode)]).then(
+ () => this.setPreviewContent(true)
+ );
+ }
+ applyCodemirrorSettings(prefs) {
+ if (!this.cm) {
+ return;
+ }
+ htmlCodeEl.querySelector('.CodeMirror').style.fontSize = `${parseInt(
+ prefs.fontSize,
+ 10
+ )}px`;
+
+ // Replace correct css file in LINK tags's href
+ window.editorThemeLinkTag.href = `lib/codemirror/theme/${
+ prefs.editorTheme
+ }.css`;
+ window.fontStyleTag.textContent = window.fontStyleTemplate.textContent.replace(
+ /fontname/g,
+ (prefs.editorFont === 'other'
+ ? prefs.editorCustomFont
+ : prefs.editorFont) || 'FiraCode'
+ );
+ // window.customEditorFontInput.classList[
+ // prefs.editorFont === 'other' ? 'remove' : 'add'
+ // ]('hide');
+ // this.consoleCm.setOption('theme', prefs.editorTheme);
+
+ ['html'].forEach(type => {
+ this.cm[type].setOption('indentWithTabs', prefs.indentWith !== 'spaces');
+ this.cm[type].setOption(
+ 'blastCode',
+ prefs.isCodeBlastOn ? { effect: 2, shake: false } : false
+ );
+ this.cm[type].setOption('indentUnit', +prefs.indentSize);
+ this.cm[type].setOption('tabSize', +prefs.indentSize);
+ this.cm[type].setOption('theme', prefs.editorTheme);
+
+ this.cm[type].setOption('keyMap', prefs.keymap);
+ this.cm[type].setOption('lineWrapping', prefs.lineWrap);
+ this.cm[type].refresh();
+ });
+ }
+
+ // Check all the code wrap if they are minimized or maximized
+ updateCodeWrapCollapseStates() {
+ // This is debounced!
+ clearTimeout(this.updateCodeWrapCollapseStates.timeout);
+ this.updateCodeWrapCollapseStates.timeout = setTimeout(() => {
+ const { currentLayoutMode } = this.props;
+ const prop =
+ currentLayoutMode === 2 || currentLayoutMode === 5 ? 'width' : 'height';
+ [htmlCodeEl].forEach(function(el) {
+ const bounds = el.getBoundingClientRect();
+ const size = bounds[prop];
+ if (size < 100) {
+ el.classList.add('is-minimized');
+ } else {
+ el.classList.remove('is-minimized');
+ }
+ if (el.style[prop].indexOf(`100% - ${minCodeWrapSize * 2}px`) !== -1) {
+ el.classList.add('is-maximized');
+ } else {
+ el.classList.remove('is-maximized');
+ }
+ });
+ }, 50);
+ }
+
+ resetSplitting() {
+ this.setState({
+ mainSplitSizes: this.getMainSplitSizesToApply()
+ });
+ }
+ updateSplits() {
+ this.props.onSplitUpdate();
+ // Not using setState to avoid re-render
+ this.state.mainSplitSizes = this.props.currentItem.mainSizes;
+ }
+
+ // Returns the sizes of main code & preview panes.
+ getMainSplitSizesToApply() {
+ var mainSplitSizes;
+ const sidebarWidth = 160;
+ const { currentItem, currentLayoutMode } = this.props;
+ if (false && currentItem && currentItem.mainSizes) {
+ // For layout mode 3, main panes are reversed using flex-direction.
+ // So we need to apply the saved sizes in reverse order.
+ mainSplitSizes =
+ currentLayoutMode === 3
+ ? [currentItem.mainSizes[1], currentItem.mainSizes[0]]
+ : currentItem.mainSizes;
+ } else {
+ mainSplitSizes = [
+ `${sidebarWidth}px`,
+ `calc(50% - ${sidebarWidth / 2}px)`,
+ `calc(50% - ${sidebarWidth / 2}px)`
+ ];
+ }
+ return mainSplitSizes;
+ }
+
+ mainSplitDragEndHandler() {
+ if (this.props.prefs.refreshOnResize) {
+ // Running preview updation in next call stack, so that error there
+ // doesn't affect this dragend listener.
+ setTimeout(() => {
+ this.setPreviewContent(true);
+ }, 1);
+ }
+ this.updateSplits();
+ }
+
+ /**
+ * Loaded the code comiler based on the mode selected
+ */
+ handleModeRequirements(mode) {
+ const baseTranspilerPath = 'lib/transpilers';
+ // Exit if already loaded
+ var d = deferred();
+ if (modes[mode].hasLoaded) {
+ d.resolve();
+ return d.promise;
+ }
+
+ function setLoadedFlag() {
+ modes[mode].hasLoaded = true;
+ d.resolve();
+ }
+
+ if (mode === HtmlModes.JADE) {
+ loadJS(`${baseTranspilerPath}/jade.js`).then(setLoadedFlag);
+ } else if (mode === HtmlModes.MARKDOWN) {
+ loadJS(`${baseTranspilerPath}/marked.js`).then(setLoadedFlag);
+ } else if (mode === CssModes.LESS) {
+ loadJS(`${baseTranspilerPath}/less.min.js`).then(setLoadedFlag);
+ } else if (mode === CssModes.SCSS || mode === CssModes.SASS) {
+ loadJS(`${baseTranspilerPath}/sass.js`).then(function() {
+ window.sass = new Sass(`${baseTranspilerPath}/sass.worker.js`);
+ setLoadedFlag();
+ });
+ } else if (mode === CssModes.STYLUS) {
+ loadJS(`${baseTranspilerPath}/stylus.min.js`).then(setLoadedFlag);
+ } else if (mode === CssModes.ACSS) {
+ loadJS(`${baseTranspilerPath}/atomizer.browser.js`).then(setLoadedFlag);
+ } else if (mode === JsModes.COFFEESCRIPT) {
+ loadJS(`${baseTranspilerPath}/coffee-script.js`).then(setLoadedFlag);
+ } else if (mode === JsModes.ES6) {
+ loadJS(`${baseTranspilerPath}/babel.min.js`).then(setLoadedFlag);
+ } else if (mode === JsModes.TS) {
+ loadJS(`${baseTranspilerPath}/typescript.js`).then(setLoadedFlag);
+ } else {
+ d.resolve();
+ }
+
+ return d.promise;
+ }
+
+ updateHtmlMode(value) {
+ // this.props.onCodeModeChange('html', value);
+ // this.props.currentItem.htmlMode = value;
+ this.cm.html.setOption('mode', modes[value].cmMode);
+ CodeMirror.autoLoadMode(
+ this.cm.html,
+ modes[value].cmPath || modes[value].cmMode
+ );
+ return this.handleModeRequirements(value);
+ }
+ updateCssMode(value) {
+ // this.props.onCodeModeChange('css', value);
+ // this.props.currentItem.cssMode = value;
+ this.cm.html.setOption('mode', modes[value].cmMode);
+ this.cm.html.setOption('readOnly', modes[value].cmDisable);
+ /* window.cssSettingsBtn.classList[
+ modes[value].hasSettings ? 'remove' : 'add'
+ ]('hide'); */
+ CodeMirror.autoLoadMode(
+ this.cm.html,
+ modes[value].cmPath || modes[value].cmMode
+ );
+ return this.handleModeRequirements(value);
+ }
+ updateJsMode(value) {
+ this.cm.html.setOption('mode', modes[value].cmMode);
+ CodeMirror.autoLoadMode(
+ this.cm.html,
+ modes[value].cmPath || modes[value].cmMode
+ );
+ return this.handleModeRequirements(value);
+ }
+ codeModeChangeHandler(e) {
+ var mode = e.target.value;
+ var type = e.target.dataset.type;
+ var currentMode = this.props.currentItem[
+ type === 'html' ? 'htmlMode' : type === 'css' ? 'cssMode' : 'jsMode'
+ ];
+ if (currentMode !== mode) {
+ if (type === 'html') {
+ this.updateHtmlMode(mode).then(() => this.setPreviewContent(true));
+ }
+ trackEvent('ui', 'updateCodeMode', mode);
+ }
+ }
+
+ getDemoFrame(callback) {
+ callback(this.frame);
+ }
+ editorFocusHandler(editor) {
+ this.props.onEditorFocus(editor);
+ }
+ fileSelectHandler(file) {
+ this.setState({ selectedFile: file });
+ var cmMode = 'html';
+ if (file.name.match(/\.css$/)) {
+ this.updateCssMode('css');
+ } else if (file.name.match(/\.js$/)) {
+ this.updateCssMode('js');
+ } else {
+ this.updateCssMode('html');
+ }
+ this.cm.html.setValue(file.content || '');
+ }
+
+ render() {
+ return (
+
+
+
+
+
+ (this.cm.html = el)}
+ onFocus={this.editorFocusHandler.bind(this)}
+ />
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/components/app.jsx b/src/components/app.jsx
index b378cd3..85cb482 100644
--- a/src/components/app.jsx
+++ b/src/components/app.jsx
@@ -2,9 +2,9 @@
*/
import { h, Component } from 'preact';
-
+import '../service-worker-registration';
import { MainHeader } from './MainHeader.jsx';
-import ContentWrap from './ContentWrap.jsx';
+import ContentWrap2 from './ContentWrap2.jsx';
import Footer from './Footer.jsx';
import SavedItemPane from './SavedItemPane.jsx';
import AddLibrary from './AddLibrary.jsx';
@@ -1170,8 +1170,7 @@ export default class App extends Component {
user={this.state.user}
unsavedEditCount={this.state.unsavedEditCount}
/>
-
Date: Sun, 30 Sep 2018 15:28:47 +0530
Subject: [PATCH 02/29] =?UTF-8?q?add=20working=20POC=20for=20files=20?=
=?UTF-8?q?=F0=9F=94=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/ContentWrap2.jsx | 24 +++++++++++-----------
src/components/app.jsx | 35 +++++++++++++++++++++++----------
src/index.html | 2 +-
src/style.css | 7 +++++++
4 files changed, 46 insertions(+), 22 deletions(-)
diff --git a/src/components/ContentWrap2.jsx b/src/components/ContentWrap2.jsx
index 0cd9216..6bee0d9 100644
--- a/src/components/ContentWrap2.jsx
+++ b/src/components/ContentWrap2.jsx
@@ -24,6 +24,10 @@ class Sidebar extends Component {
type="button"
onClick={this.props.onFileSelect.bind(null, file)}
>
+
{file.name}
@@ -38,14 +42,12 @@ export default class ContentWrap2 extends Component {
super(props);
this.state = {
isConsoleOpen: false,
- isCssSettingsModalOpen: false,
- files: [
- { name: 'index.html' },
- { name: 'style.css' },
- { name: 'script.js' }
- ]
+ isCssSettingsModalOpen: false
};
- this.state.selectedFile = this.state.files[0];
+ this.state.selectedFile =
+ this.currentItem && this.currentItem.files
+ ? this.currentItem.files[0]
+ : null;
this.updateTimer = null;
this.updateDelay = 500;
this.htmlMode = HtmlModes.HTML;
@@ -135,10 +137,9 @@ export default class ContentWrap2 extends Component {
this.detachedWindow.postMessage({ contents }, '*');
} else {
var obj = {};
- this.state.files.forEach(file => {
+ this.props.currentItem.files.forEach(file => {
obj[`/user/${file.name}`] = file.content || '';
});
- // obj[`/user/index.html`] = this.cmCodes.html;
navigator.serviceWorker.controller.postMessage(obj);
this.frame.src = '/user/index.html';
@@ -474,6 +475,7 @@ export default class ContentWrap2 extends Component {
this.updateCssMode('html');
}
this.cm.html.setValue(file.content || '');
+ this.cm.html.focus();
}
render() {
@@ -490,7 +492,7 @@ export default class ContentWrap2 extends Component {
>
@@ -507,7 +509,7 @@ export default class ContentWrap2 extends Component {
title="Double click to toggle code pane"
>
diff --git a/src/components/app.jsx b/src/components/app.jsx
index 0bc7ce2..22432be 100644
--- a/src/components/app.jsx
+++ b/src/components/app.jsx
@@ -332,10 +332,12 @@ export default class App extends Component {
setCurrentItem(item) {
const d = deferred();
// TODO: remove later
- item.htmlMode =
- item.htmlMode || this.state.prefs.htmlMode || HtmlModes.HTML;
- item.cssMode = item.cssMode || this.state.prefs.cssMode || CssModes.CSS;
- item.jsMode = item.jsMode || this.state.prefs.jsMode || JsModes.JS;
+ if (!item.files) {
+ item.htmlMode =
+ item.htmlMode || this.state.prefs.htmlMode || HtmlModes.HTML;
+ item.cssMode = item.cssMode || this.state.prefs.cssMode || CssModes.CSS;
+ item.jsMode = item.jsMode || this.state.prefs.jsMode || JsModes.JS;
+ }
this.setState({ currentItem: item }, d.resolve);
@@ -699,7 +701,15 @@ export default class App extends Component {
this.setState({ currentItem: item });
}
onCodeChange(type, code, isUserChange) {
- this.state.currentItem[type] = code;
+ if (this.state.currentItem.files) {
+ this.state.currentItem.files.map(file => {
+ if (file.name === type.name) {
+ file.content = code;
+ }
+ });
+ } else {
+ this.state.currentItem[type] = code;
+ }
if (isUserChange) {
this.setState({ unsavedEditCount: this.state.unsavedEditCount + 1 });
From 799ae55abd2cfee5f3b8049b7384b2cfa294169b Mon Sep 17 00:00:00 2001
From: Kushagra Gour
Date: Thu, 4 Oct 2018 01:18:54 +0530
Subject: [PATCH 04/29] new file template n add file support
---
src/components/ContentWrap2.jsx | 91 +++++++----------------
src/components/CreateNewModal.jsx | 4 ++
src/components/SidePane.jsx | 116 ++++++++++++++++++++++++++++++
src/components/app.jsx | 33 +++++++--
src/style.css | 20 +++++-
5 files changed, 192 insertions(+), 72 deletions(-)
create mode 100644 src/components/SidePane.jsx
diff --git a/src/components/ContentWrap2.jsx b/src/components/ContentWrap2.jsx
index 6b694e0..40df847 100644
--- a/src/components/ContentWrap2.jsx
+++ b/src/components/ContentWrap2.jsx
@@ -8,35 +8,11 @@ import { trackEvent } from '../analytics';
import CodeMirror from '../CodeMirror';
import CodeMirrorBox from './CodeMirrorBox';
import { deferred } from '../deferred';
+import { SidePane } from './SidePane';
const minCodeWrapSize = 33;
/* global htmlCodeEl, jsCodeEl, cssCodeEl, logCountEl
*/
-class Sidebar extends Component {
- render() {
- return (
-
- );
- }
-}
-
export default class ContentWrap2 extends Component {
constructor(props) {
super(props);
@@ -66,6 +42,15 @@ export default class ContentWrap2 extends Component {
this.props.prefs !== nextProps.prefs
);
}
+ componentWillUpdate(nextProps) {
+ if (
+ this.props.currentItem.createdOn !== nextProps.currentItem.createdOn ||
+ this.props.currentItem.id !== nextProps.currentItem.id
+ ) {
+ this.fileBuffers = {};
+ this.state.selectedFile = null;
+ }
+ }
componentDidUpdate() {
if (
this.props.currentItem &&
@@ -97,7 +82,7 @@ export default class ContentWrap2 extends Component {
mode = modes[HtmlModes.HTML].cmMode;
}
console.log('mode', mode);
- this.fileBuffers[file.name] = CodeMirror.Doc(file.content, mode);
+ this.fileBuffers[file.name] = CodeMirror.Doc(file.content || '', mode);
}
onHtmlCodeChange(editor, change) {
@@ -195,38 +180,12 @@ export default class ContentWrap2 extends Component {
? this.detachedWindow.document.querySelector('iframe')
: this.frame;
- const cssMode = this.props.currentItem.cssMode;
- var htmlPromise = computeHtml(
- currentCode.html,
- this.props.currentItem.htmlMode
- );
- var cssPromise = computeCss(
- cssMode === CssModes.ACSS ? currentCode.html : currentCode.css,
- cssMode,
- this.props.currentItem.cssSettings
- );
- var jsPromise = computeJs(
- currentCode.js,
- this.props.currentItem.jsMode,
- true,
- this.props.prefs.infiniteLoopTimeout
- );
- Promise.all([htmlPromise, cssPromise, jsPromise]).then(result => {
- /* if (cssMode === CssModes.ACSS) {
- this.cm.css.setValue(result[1].code || '');
- } */
-
- this.createPreviewFile(
- result[0].code || '',
- result[1].code || '',
- result[2].code || ''
- );
- result.forEach(resultItem => {
- if (resultItem.errors) {
- this.showErrors(resultItem.errors.lang, resultItem.errors.data);
- }
- });
- });
+ this.createPreviewFile();
+ // result.forEach(resultItem => {
+ // if (resultItem.errors) {
+ // this.showErrors(resultItem.errors.lang, resultItem.errors.data);
+ // }
+ // });
this.codeInPreview.html = currentCode.html;
this.codeInPreview.css = currentCode.css;
@@ -406,12 +365,12 @@ export default class ContentWrap2 extends Component {
updateHtmlMode(value) {
// this.props.onCodeModeChange('html', value);
// this.props.currentItem.htmlMode = value;
- this.cm.setOption('mode', modes[value].cmMode);
- CodeMirror.autoLoadMode(
- this.cm,
- modes[value].cmPath || modes[value].cmMode
- );
- return this.handleModeRequirements(value);
+ // this.cm.setOption('mode', modes[value].cmMode);
+ // CodeMirror.autoLoadMode(
+ // this.cm,
+ // modes[value].cmPath || modes[value].cmMode
+ // );
+ // return this.handleModeRequirements(value);
}
updateCssMode(value) {
// this.props.onCodeModeChange('css', value);
@@ -474,9 +433,11 @@ export default class ContentWrap2 extends Component {
onDragEnd={this.mainSplitDragEndHandler.bind(this)}
>
diff --git a/src/components/CreateNewModal.jsx b/src/components/CreateNewModal.jsx
index 7145860..8dc1699 100644
--- a/src/components/CreateNewModal.jsx
+++ b/src/components/CreateNewModal.jsx
@@ -7,6 +7,7 @@ export function CreateNewModal({
show,
closeHandler,
onBlankTemplateSelect,
+ onBlankFileTemplateSelect,
onTemplateSelect
}) {
return (
@@ -15,6 +16,9 @@ export function CreateNewModal({
+
Or choose from a template:
diff --git a/src/components/SidePane.jsx b/src/components/SidePane.jsx
new file mode 100644
index 0000000..4d92b2c
--- /dev/null
+++ b/src/components/SidePane.jsx
@@ -0,0 +1,116 @@
+import { h, Component } from 'preact';
+const ENTER_KEY = 13;
+const ESCAPE_KEY = 27;
+
+function FileIcon({ fileName }) {
+ if (!fileName) fileName = 'sd.sd';
+
+ const type = fileName.match(/.(\w+)$/)[1];
+ console.log(type);
+ switch (type) {
+ case 'html':
+ return (
+
+ );
+ case 'js':
+ return (
+
+ );
+ case 'css':
+ return (
+
+ );
+ }
+}
+
+export class SidePane extends Component {
+ addFileButtonClickHandler() {
+ this.setState({ isEditing: true });
+ setTimeout(() => {
+ this.newFileNameInput.focus();
+ }, 1);
+ }
+
+ addFile() {
+ // This gets called twice when enter is pressed, because blur also fires.
+ if (!this.newFileNameInput) {
+ return;
+ }
+ const fileName = this.newFileNameInput.value;
+ if (fileName) {
+ this.props.onAddFile(fileName);
+ }
+ this.setState({ isEditing: false });
+ }
+ newFileNameInputKeyDownHandler(e) {
+ if (e.which === ENTER_KEY) {
+ this.addFile();
+ } else if (e.which === ESCAPE_KEY) {
+ this.setState({ isEditing: false });
+ }
+ }
+ render() {
+ const { files, onFileSelect, selectedFile } = this.props;
+
+ return (
+
+ );
+ }
+}
diff --git a/src/components/app.jsx b/src/components/app.jsx
index 22432be..a7419d1 100644
--- a/src/components/app.jsx
+++ b/src/components/app.jsx
@@ -264,7 +264,7 @@ export default class App extends Component {
alertsService.add(`"${sourceItem.title}" was forked`);
trackEvent('fn', 'itemForked');
}
- createNewItem(isProject = true) {
+ createNewItem(isFileMode = false) {
const d = new Date();
let item = {
title:
@@ -275,15 +275,17 @@ export default class App extends Component {
'-' +
d.getHours() +
':' +
- d.getMinutes()
+ d.getMinutes(),
+ createdOn: +d,
+ content: ''
};
- if (isProject) {
+ if (isFileMode) {
item = {
...item,
files: [
- { name: 'index.html' },
- { name: 'style.css' },
- { name: 'script.js' }
+ { name: 'index.html', content: '' },
+ { name: 'style.css', content: '' },
+ { name: 'script.js', content: '' }
]
};
} else {
@@ -1165,6 +1167,10 @@ export default class App extends Component {
this.createNewItem();
this.setState({ isCreateNewModalOpen: false });
}
+ blankFileTemplateSelectHandler() {
+ this.createNewItem(true);
+ this.setState({ isCreateNewModalOpen: false });
+ }
templateSelectHandler(template) {
fetch(`templates/template-${template.id}.json`)
@@ -1174,6 +1180,17 @@ export default class App extends Component {
});
this.setState({ isCreateNewModalOpen: false });
}
+ addFileHandler(fileName) {
+ this.setState({
+ currentItem: {
+ ...this.state.currentItem,
+ files: [
+ ...this.state.currentItem.files,
+ { name: fileName, content: '' }
+ ]
+ }
+ });
+ }
render() {
return (
@@ -1204,6 +1221,7 @@ export default class App extends Component {
prefs={this.state.prefs}
onEditorFocus={this.editorFocusHandler.bind(this)}
onSplitUpdate={this.splitUpdateHandler.bind(this)}
+ onAddFile={this.addFileHandler.bind(this)}
/>
@@ -283,7 +284,7 @@ export default class Settings extends Component {
title="Enjoy wonderful particle blasts while you type"
label="Code blast!"
name="isCodeBlastOn"
- pref={this.props.prefs.isCodeBlastOn}
+ pref={prefs.isCodeBlastOn}
onChange={this.updateSetting.bind(this)}
/>
@@ -291,7 +292,7 @@ export default class Settings extends Component {
title="Get ready to build some games at JS13KGames"
label="Js13kGames Mode"
name="isJs13kModeOn"
- pref={this.props.prefs.isJs13kModeOn}
+ pref={prefs.isJs13kModeOn}
onChange={this.updateSetting.bind(this)}
/>
@@ -307,7 +308,7 @@ export default class Settings extends Component {
Maximum time allowed in a loop iteration{' '}
{' '}
From 2d09be94116857570df981c2902a4f244f207abf Mon Sep 17 00:00:00 2001
From: Kushagra Gour
Date: Sun, 7 Oct 2018 18:03:46 +0530
Subject: [PATCH 10/29] hide useless things in file mode
---
src/components/Footer.jsx | 12 ++++++------
src/components/MainHeader.jsx | 32 +++++++++++++++++---------------
src/components/app.jsx | 1 +
src/style.css | 4 ++++
4 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx
index 9d6d76a..ba76fa7 100644
--- a/src/components/Footer.jsx
+++ b/src/components/Footer.jsx
@@ -196,7 +196,7 @@ export default class Footer extends Component {