1
0
mirror of https://github.com/chinchang/web-maker.git synced 2025-05-05 18:15:58 +02:00

Merge pull request #354 from chinchang/monaco-new

Monaco
This commit is contained in:
Kushagra Gour 2018-12-10 23:20:13 +05:30 committed by GitHub
commit 5c6f93a927
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 4416 additions and 308 deletions

View File

@ -22,8 +22,8 @@ function minifyJs(fileName) {
).code;
fs.writeFileSync(fileName, minifiedContent);
console.log(
`[${fileName}]: ${content.length / 1024}M -> ${minifiedContent.length /
1024}M`
`[${fileName}]: ${content.length / 1024}K -> ${minifiedContent.length /
1024}K`
);
}
gulp.task('runWebpack', function() {
@ -41,6 +41,9 @@ gulp.task('copyFiles', function() {
gulp.src('src/lib/transpilers/*').pipe(gulp.dest('app/lib/transpilers')),
gulp.src('src/lib/prettier-worker.js').pipe(gulp.dest('app/lib/')),
gulp.src('src/lib/prettier/*').pipe(gulp.dest('app/lib/prettier')),
gulp
.src(['!src/lib/monaco/monaco.bundle.js', 'src/lib/monaco/**/*'])
.pipe(gulp.dest('app/lib/monaco')),
gulp.src('src/lib/screenlog.js').pipe(gulp.dest('app/lib')),
gulp.src('icons/*').pipe(gulp.dest('app/icons')),
gulp.src('src/assets/*').pipe(gulp.dest('app/assets')),
@ -63,6 +66,7 @@ gulp.task('copyFiles', function() {
.src('build/vendor.*.js')
.pipe(rename('vendor.js'))
.pipe(gulp.dest('app')),
gulp.src('build/monaco.*.js').pipe(gulp.dest('app')),
// Following CSS are copied to build/ folder where they'll be referenced by
// useRef plugin to concat into one.
@ -120,8 +124,11 @@ gulp.task('minify', function() {
debug: true
},
details => {
console.log(`${details.name}: ${details.stats.originalSize}`);
console.log(`${details.name}: ${details.stats.minifiedSize}`);
console.log(
`${details.name}: ${details.stats.originalSize} 👉🏼 ${
details.stats.minifiedSize
}`
);
}
)
)
@ -138,7 +145,7 @@ gulp.task('fixIndex', function() {
// vendor.hash.js gets created outside our markers, so remove it
contents = contents.replace(
/\<script src="\/vendor\.[\S\s]*?\<\/script\>/,
/\<script src="[\S\s]*?vendor\.[\S\s]*?\<\/script\>/,
''
);
@ -171,18 +178,12 @@ gulp.task('packageExtension', function() {
child_process.execSync('cp src/options.html extension');
child_process.execSync('cp src/eventPage.js extension');
child_process.execSync('cp src/icon-16.png extension');
child_process.execSync(
'rm -rf extension/service-worker.js extension/partials'
);
child_process.execSync('rm -rf extension/service-worker.js');
return merge(
gulp
.src('build/bundle.*.js')
.pipe(rename('script.js'))
.pipe(gulp.dest('extension')),
gulp
.src('build/vendor.*.js')
.pipe(rename('vendor.js'))
.pipe(gulp.dest('extension')),
gulp
.src('extension/**/*')

View File

@ -1,4 +1,3 @@
import CopyWebpackPlugin from 'copy-webpack-plugin';
var CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
/**
@ -29,6 +28,12 @@ export default function(config, env, helpers) {
if (env.isProd) {
config.devtool = false; // disable sourcemaps
// To support chunk loading in root and also /app path
config.output.publicPath = './';
// Remove the default hash append in chunk name
config.output.chunkFilename = '[name].chunk.js';
config.plugins.push(
new CommonsChunkPlugin({
name: 'vendor',

View File

@ -0,0 +1,390 @@
import { h, Component } from 'preact';
import CodeMirror from '../CodeMirror';
import 'codemirror/addon/edit/matchbrackets.js';
import 'codemirror/addon/edit/matchtags.js';
import 'codemirror/addon/edit/closebrackets.js';
import 'codemirror/addon/edit/closetag.js';
import 'codemirror/addon/comment/comment.js';
import 'codemirror/addon/fold/foldcode.js';
import 'codemirror/addon/fold/foldgutter.js';
import 'codemirror/addon/fold/xml-fold.js';
import 'codemirror/addon/fold/indent-fold.js';
import 'codemirror/addon/fold/comment-fold.js';
import 'codemirror/addon/fold/brace-fold.js';
import 'codemirror/addon/hint/show-hint.js';
import 'codemirror/addon/hint/javascript-hint.js';
import 'codemirror/addon/hint/xml-hint.js';
import 'codemirror/addon/hint/html-hint.js';
import 'codemirror/addon/hint/css-hint.js';
import 'codemirror/addon/selection/active-line.js';
import 'codemirror/addon/search/searchcursor.js';
import 'codemirror/addon/search/search.js';
import 'codemirror/addon/dialog/dialog.js';
import 'codemirror/addon/search/jump-to-line.js';
import 'codemirror/mode/xml/xml.js';
import 'codemirror/mode/css/css.js';
import 'codemirror/mode/javascript/javascript.js';
import 'codemirror/mode/htmlmixed/htmlmixed.js';
import 'codemirror/keymap/sublime.js';
import 'codemirror/keymap/vim.js';
import 'code-blast-codemirror/code-blast.js';
import emmet from '@emmetio/codemirror-plugin';
import { prettify, loadCss } from '../utils';
import { modes } from '../codeModes';
import { deferred } from '../deferred';
emmet(CodeMirror);
let monacoDepsDeferred;
window.MonacoEnvironment = {
getWorkerUrl(moduleId, label) {
switch (label) {
case 'html':
return 'lib/monaco/workers/html.worker.bundle.js';
case 'json':
return 'lib/monaco/workers/json.worker.bundle.js';
case 'css':
case 'scss':
case 'less':
return 'lib/monaco/workers/css.worker.bundle.js';
case 'typescript':
case 'javascript':
return 'lib/monaco/workers/ts.worker.bundle.js';
default:
return 'lib/monaco/workers/editor.worker.bundle.js';
}
}
};
export default class CodeEditor extends Component {
componentDidMount() {
this.initEditor();
}
shouldComponentUpdate(nextProps) {
if (nextProps.prefs !== this.props.prefs) {
const { prefs } = nextProps;
if (this.props.type === 'monaco') {
this.instance.updateOptions({ fontSize: prefs.fontSize });
} else {
this.instance.setOption(
'indentWithTabs',
prefs.indentWith !== 'spaces'
);
this.instance.setOption(
'blastCode',
prefs.isCodeBlastOn ? { effect: 2, shake: false } : false
);
this.instance.setOption('theme', prefs.editorTheme);
this.instance.setOption('indentUnit', +prefs.indentSize);
this.instance.setOption('tabSize', +prefs.indentSize);
this.instance.setOption('keyMap', prefs.keymap);
this.instance.setOption('lineWrapping', prefs.lineWrap);
this.instance.setOption('lineWrapping', prefs.autoCloseTags);
this.instance.refresh();
}
}
// Only condition when this component updates is when prop.type changes
if (nextProps.type !== this.props.type) {
if (
this.node.parentElement.querySelector('.monaco-editor, .CodeMirror')
) {
this.node.parentElement
.querySelector('.monaco-editor, .CodeMirror')
.remove();
}
return true;
}
return false;
}
componentDidUpdate(prevProps) {
// prop.type changed, reinit the editor
this.initEditor();
}
setModel(model) {
this.instance.swapDoc
? this.instance.swapDoc(model)
: this.instance.setModel(model);
}
setValue(value) {
// HACK: We set a flag on window for an ultra-short duration, which 'change'
// listener uses to set the change.origin to 'setValue', otherwise it's
// '+input'
if (this.props.type === 'monaco') {
window.monacoSetValTriggered = true;
setTimeout(() => {
window.monacoSetValTriggered = false;
}, 1);
}
this.instance.setValue(value);
// We save last set value so that when editor type changes, we can
// populate that last value
this.lastSetValue = value;
}
getValue() {
return this.instance.getValue();
}
saveViewState() {
if (this.props.type === 'monaco') {
return this.instance.saveViewState();
}
}
restoreViewState(state) {
if (this.props.type === 'monaco') {
this.instance.restoreViewState(state);
}
}
setOption(option, value) {
if (this.props.type === 'monaco') {
this.monacoEditorReadyDeferred.promise.then(() => {
this.instance.updateOptions({ [option]: value });
});
} else {
this.instance.setOption(option, value);
}
}
setLanguage(value) {
if (!window.monaco) return;
if (this.props.type === 'monaco') {
monaco.editor.setModelLanguage(
this.instance.getModel(),
this.getMonacoLanguageFromMode(modes[value].cmMode)
);
} else {
this.instance.setOption('mode', modes[value].cmMode);
CodeMirror.autoLoadMode(
this.instance,
modes[value].cmPath || modes[value].cmMode
);
}
}
clearGutter(gutterName) {
if (this.instance.clearGutter) {
this.instance.clearGutter(gutterName);
}
}
showErrors(errors) {
if (this.props.type === 'codemirror') {
errors.forEach(function(error) {
this.instance.operation(function() {
var n = document.createElement('div');
n.setAttribute('data-title', error.message);
n.classList.add('gutter-error-marker');
editor.setGutterMarker(error.lineNumber, 'error-gutter', n);
});
});
}
}
refresh() {
this.instance.refresh ? this.instance.refresh() : this.instance.layout();
}
focus() {
this.instance.focus();
}
/**
* Converts codemirror mode value to monaco language values.
* TODO: Refactor to not be codemirror related.
* @param {string} mode Codemirror mode value
*/
getMonacoLanguageFromMode(mode) {
if (['htmlmixed'].includes(mode)) {
return 'html';
}
if (['css', 'sass', 'scss', 'less', 'stylus'].includes(mode)) {
return 'css';
}
if (['javascript', 'text/typescript-jsx', 'jsx'].includes(mode)) {
return 'javascript';
}
return mode;
}
/**
* Loads the asynchronous deps according to the editor type.
*/
async loadDeps() {
if (this.props.type === 'monaco') {
if (!monacoDepsDeferred) {
monacoDepsDeferred = deferred();
loadCss({ url: 'lib/monaco/monaco.css', id: 'monaco-css' });
import(/* webpackChunkName: "monaco" */ '../lib/monaco/monaco.bundle.js').then(
() => {
monacoDepsDeferred.resolve();
}
);
}
return monacoDepsDeferred.promise;
}
return Promise.resolve();
}
async initEditor() {
this.monacoEditorReadyDeferred = deferred();
await this.loadDeps();
const { options, prefs } = this.props;
if (this.props.type === 'monaco') {
this.instance = monaco.editor.create(this.node, {
language: this.getMonacoLanguageFromMode(options.mode),
value: this.lastSetValue || '',
roundedSelection: false,
scrollBeyondLastLine: false,
theme: 'vs-dark',
fontSize: prefs.fontSize,
minimap: {
enabled: false
},
wordWrap: 'on',
renderWhitespace: 'all',
fontLigatures: true,
automaticLayout: true
});
window.monacoInstance = this.instance;
this.instance.onDidChangeModelContent(change => {
this.props.onChange(this.instance, {
...change,
origin: window.monacoSetValTriggered ? 'setValue' : '+input'
});
});
this.instance.addCommand(
monaco.KeyMod.WinCtrl | monaco.KeyMod.Shift | monaco.KeyCode.KEY_F,
() => {
if (options.prettier) {
prettify({
content: this.instance.getValue(),
type: options.prettierParser
}).then(formattedCode => this.instance.setValue(formattedCode));
}
}
);
this.monacoEditorReadyDeferred.resolve();
} else {
this.instance = CodeMirror.fromTextArea(this.node, {
mode: options.mode,
value: this.lastSetValue || '',
lineNumbers: true,
lineWrapping: !!prefs.lineWrap,
autofocus: options.autofocus || false,
autoCloseBrackets: true,
autoCloseTags: !!prefs.autoCloseTags,
matchBrackets: true,
matchTags: options.matchTags || false,
tabMode: 'indent',
keyMap: prefs.keyMap || 'sublime',
theme: prefs.editorTheme || 'monokai',
lint: !!options.lint,
tabSize: +prefs.indentSize || 2,
indentWithTabs: prefs.indentWith !== 'spaces',
indentUnit: +prefs.indentSize,
foldGutter: true,
styleActiveLine: true,
gutters: options.gutters || [],
// cursorScrollMargin: '20', has issue with scrolling
profile: options.profile || '',
extraKeys: {
Up: function(editor) {
// Stop up/down keys default behavior when saveditempane is open
// if (isSavedItemsPaneOpen) {
// return;
// }
CodeMirror.commands.goLineUp(editor);
},
Down: function(editor) {
// if (isSavedItemsPaneOpen) {
// return;
// }
CodeMirror.commands.goLineDown(editor);
},
'Shift-Tab': function(editor) {
CodeMirror.commands.indentAuto(editor);
},
'Shift-Ctrl-F': function(editor) {
if (options.prettier) {
prettify({
content: editor.getValue(),
type: options.prettierParser
}).then(formattedCode => editor.setValue(formattedCode));
}
},
Tab: function(editor) {
if (options.emmet) {
const didEmmetWork = editor.execCommand(
'emmetExpandAbbreviation'
);
if (didEmmetWork === true) {
return;
}
const input = $('[data-setting=indentWith]:checked');
if (
!editor.somethingSelected() &&
(!prefs.indentWith || prefs.indentWith === 'spaces')
) {
// softtabs adds spaces. This is required because by default tab key will put tab, but we want
// to indent with spaces if `spaces` is preferred mode of indentation.
// `somethingSelected` needs to be checked otherwise, all selected code is replaced with softtab.
CodeMirror.commands.insertSoftTab(editor);
} else {
CodeMirror.commands.defaultTab(editor);
}
}
},
Enter: 'emmetInsertLineBreak'
}
});
this.instance.on('focus', editor => {
if (typeof this.props.onFocus === 'function')
this.props.onFocus(editor);
});
this.instance.on('change', this.props.onChange);
this.instance.addKeyMap({
'Ctrl-Space': 'autocomplete'
});
this.instance.on('inputRead', (editor, input) => {
// Process further If this has autocompletition on and also the global
// autocomplete setting is on.
if (
!this.props.options.noAutocomplete &&
this.props.prefs.autoComplete
) {
if (
input.origin !== '+input' ||
input.text[0] === ';' ||
input.text[0] === ',' ||
input.text[0] === ' '
) {
return;
}
CodeMirror.commands.autocomplete(editor, null, {
completeSingle: false
});
}
});
}
// this.props.onCreation(this.instance);
}
render() {
const node =
this.props.type === 'monaco' ? (
<div ref={el => (this.node = el)} style="width:100%;height:100%;" />
) : (
<textarea ref={el => (this.node = el)} />
);
return node;
}
}

View File

@ -1,5 +1,5 @@
import { h, Component } from 'preact';
import UserCodeMirror from './UserCodeMirror.jsx';
import CodeEditor from './CodeEditor.jsx';
import { computeHtml, computeCss, computeJs } from '../computes';
import { modes, HtmlModes, CssModes, JsModes } from '../codeModes';
import { log, writeFile, loadJS, getCompleteHtml } from '../utils';
@ -13,7 +13,7 @@ import { PreviewDimension } from './PreviewDimension.jsx';
const minCodeWrapSize = 33;
/* global htmlCodeEl
*/
*/
export default class ContentWrap extends Component {
constructor(props) {
@ -173,15 +173,7 @@ export default class ContentWrap extends Component {
}
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);
});
});
this.cm[lang].showErrors(errors);
}
/**
@ -488,35 +480,23 @@ export default class ContentWrap extends Component {
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
);
this.cm.html.setLanguage(value);
return this.handleModeRequirements(value);
}
updateCssMode(value) {
this.props.onCodeModeChange('css', value);
this.props.currentItem.cssMode = value;
this.cm.css.setOption('mode', modes[value].cmMode);
this.cm.css.setOption('readOnly', modes[value].cmDisable);
window.cssSettingsBtn.classList[
modes[value].hasSettings ? 'remove' : 'add'
]('hide');
CodeMirror.autoLoadMode(
this.cm.css,
modes[value].cmPath || modes[value].cmMode
);
this.cm.css.setLanguage(value);
return this.handleModeRequirements(value);
}
updateJsMode(value) {
this.props.onCodeModeChange('js', value);
this.props.currentItem.jsMode = value;
this.cm.js.setOption('mode', modes[value].cmMode);
CodeMirror.autoLoadMode(
this.cm.js,
modes[value].cmPath || modes[value].cmMode
);
this.cm.js.setLanguage(value);
return this.handleModeRequirements(value);
}
codeModeChangeHandler(e) {
@ -708,7 +688,8 @@ export default class ContentWrap extends Component {
/>
</div>
</div>
<UserCodeMirror
<CodeEditor
type={this.props.prefs.isMonacoEditorOn ? 'monaco' : 'codemirror'}
options={{
mode: 'htmlmixed',
profile: 'xhtml',
@ -721,7 +702,7 @@ export default class ContentWrap extends Component {
}}
prefs={this.props.prefs}
onChange={this.onHtmlCodeChange.bind(this)}
onCreation={el => (this.cm.html = el)}
ref={editor => (this.cm.html = editor)}
onFocus={this.editorFocusHandler.bind(this)}
/>
</div>
@ -775,7 +756,8 @@ export default class ContentWrap extends Component {
/>
</div>
</div>
<UserCodeMirror
<CodeEditor
type={this.props.prefs.isMonacoEditorOn ? 'monaco' : 'codemirror'}
options={{
mode: 'css',
gutters: [
@ -789,7 +771,7 @@ export default class ContentWrap extends Component {
}}
prefs={this.props.prefs}
onChange={this.onCssCodeChange.bind(this)}
onCreation={el => (this.cm.css = el)}
ref={editor => (this.cm.css = editor)}
onFocus={this.editorFocusHandler.bind(this)}
/>
</div>
@ -830,7 +812,8 @@ export default class ContentWrap extends Component {
/>
</div>
</div>
<UserCodeMirror
<CodeEditor
type={this.props.prefs.isMonacoEditorOn ? 'monaco' : 'codemirror'}
options={{
mode: 'javascript',
gutters: [
@ -844,7 +827,7 @@ export default class ContentWrap extends Component {
prefs={this.props.prefs}
autoComplete={this.props.prefs.autoComplete}
onChange={this.onJsCodeChange.bind(this)}
onCreation={el => (this.cm.js = el)}
ref={editor => (this.cm.js = editor)}
onFocus={this.editorFocusHandler.bind(this)}
/>
{/* Inlet(scope.cm.js); */}

View File

@ -1,5 +1,5 @@
import { h, Component } from 'preact';
import UserCodeMirror from './UserCodeMirror';
import CodeEditor from './CodeEditor';
import { modes, HtmlModes, CssModes, JsModes } from '../codeModes';
import { log, loadJS } from '../utils';
@ -24,7 +24,7 @@ import { PreviewDimension } from './PreviewDimension';
const minCodeWrapSize = 33;
/* global htmlCodeEl
*/
*/
export default class ContentWrapFiles extends Component {
constructor(props) {
super(props);
@ -86,7 +86,7 @@ export default class ContentWrapFiles extends Component {
this.state.selectedFile &&
this.fileBuffers[this.state.selectedFile.path]
) {
this.fileBuffers[this.state.selectedFile.path].setValue(
this.fileBuffers[this.state.selectedFile.path].model.setValue(
getFileFromPath(
nextProps.currentItem.files,
this.state.selectedFile.path
@ -168,12 +168,23 @@ export default class ContentWrapFiles extends Component {
CodeMirror.autoLoadMode(this.cm, mode);
}
if (mime === 'application/json') {
mime = 'application/ld+json';
mime = this.props.prefs.isMonacoEditorOn ? 'json' : 'application/ld+json';
}
if (!this.props.prefs.isMonacoEditorOn) {
this.fileBuffers[file.path] = {
model: CodeMirror.Doc(
file.content || '',
detectedMode ? mime : 'text/plain'
)
};
} else {
this.fileBuffers[file.path] = {
model: monaco.editor.createModel(
file.content || '',
mime || 'javascript'
)
};
}
this.fileBuffers[file.path] = CodeMirror.Doc(
file.content || '',
detectedMode ? mime : 'text/plain'
);
}
onHtmlCodeChange(editor, change) {
@ -184,10 +195,10 @@ export default class ContentWrapFiles extends Component {
this.cmCodes.html,
change.origin !== 'setValue'
);
this.onCodeChange(editor, change);
this.onCodeChange(change);
}
onCodeChange(editor, change) {
onCodeChange(change) {
clearTimeout(this.updateTimer);
this.updateTimer = setTimeout(() => {
@ -249,19 +260,11 @@ export default class ContentWrapFiles extends Component {
}
}
cleanupErrors() {
this.cm.clearGutter('error-gutter');
this.editor.clearGutter('error-gutter');
}
showErrors(lang, errors) {
var editor = this.cm;
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);
});
});
showErrors(errors) {
this.editor.showErrors(errors);
}
/**
@ -305,9 +308,11 @@ export default class ContentWrapFiles extends Component {
}
refreshEditor() {
if (this.state.selectedFile) {
this.cm.setValue(this.state.selectedFile.content);
this.editor.setValue(this.state.selectedFile.content);
}
if (this.editor) {
this.editor.refresh();
}
this.cm.refresh();
window.cm = this.cm;
this.clearConsole();
@ -498,25 +503,26 @@ export default class ContentWrapFiles extends Component {
this.props.onFolderSelect(file);
return;
}
if (!this.fileBuffers[file.path]) {
this.createEditorDoc(file);
}
const currentState = this.editor.saveViewState();
if (currentState && this.state.selectedFile) {
this.fileBuffers[this.state.selectedFile.path].state = currentState;
}
this.setState({
editorOptions: this.getEditorOptions(file.name),
selectedFile: file
});
if (!this.fileBuffers[file.path]) {
this.createEditorDoc(file);
}
this.cm.swapDoc(this.fileBuffers[file.path]);
// 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.setValue(file.content || '');
this.cm.focus();
this.editor.setModel(this.fileBuffers[file.path].model);
if (this.fileBuffers[file.path].state) {
this.editor.restoreViewState(this.fileBuffers[file.path].state);
}
this.editor.focus();
}
onMessageFromConsole() {
@ -637,14 +643,15 @@ export default class ContentWrapFiles extends Component {
/>
</div>
</div>
<UserCodeMirror
<CodeEditor
type={this.props.prefs.isMonacoEditorOn ? 'monaco' : 'codemirror'}
value={
this.state.selectedFile ? this.state.selectedFile.content : ''
}
options={this.state.editorOptions}
prefs={this.props.prefs}
onChange={this.onHtmlCodeChange.bind(this)}
onCreation={editor => (this.cm = editor)}
ref={editor => (this.editor = editor)}
onFocus={this.editorFocusHandler.bind(this)}
/>
</div>

View File

@ -11,6 +11,10 @@ function CheckboxSetting({ label, onChange, pref }) {
</Switch>
);
}
function HelpText({ children }) {
return <p class="help-text">{children}</p>;
}
export default class Settings extends Component {
updateSetting(e, settingName) {
const value =
@ -34,47 +38,45 @@ export default class Settings extends Component {
pref={prefs.preserveLastCode}
onChange={e => this.updateSetting(e, 'preserveLastCode')}
/>
<p class="help-text">
Loads the last open creation when app starts
</p>
<HelpText>Loads the last open creation when app starts</HelpText>
<Divider />
<CheckboxSetting
label="Fast/light version"
pref={prefs.lightVersion}
onChange={e => this.updateSetting(e, 'lightVersion')}
/>
<p class="help-text">
<HelpText>
Switch to lighter version for better performance. Removes things
like blur etc.
</p>
</HelpText>
<Divider />
<CheckboxSetting
label="Auto-preview"
pref={prefs.autoPreview}
onChange={e => this.updateSetting(e, 'autoPreview')}
/>
<p class="help-text">
<HelpText>
Refreshes the preview as you code. Otherwise use the 'Run' button
</p>
</HelpText>
<Divider />
<CheckboxSetting
label="Auto-save"
pref={prefs.autoSave}
onChange={e => this.updateSetting(e, 'autoSave')}
/>
<p class="help-text">
<HelpText>
Auto-save keeps saving your code at regular intervals after you
hit save manually the first time
</p>
</HelpText>
<Divider />
<CheckboxSetting
label="Refresh preview on resize"
pref={prefs.refreshOnResize}
onChange={e => this.updateSetting(e, 'refreshOnResize')}
/>
<p class="help-text">
<HelpText>
Preview will refresh when you resize the preview pane
</p>
</HelpText>
<div class="show-when-extension">
<Divider />
<CheckboxSetting
@ -83,10 +85,10 @@ export default class Settings extends Component {
onChange={e => this.updateSetting(e, 'replaceNewTab')}
showWhenExtension
/>
<p class="help-text">
<HelpText>
Turning this on will start showing Web Maker in every new tab
you open
</p>
</HelpText>
</div>
<Divider />
<CheckboxSetting
@ -94,9 +96,9 @@ export default class Settings extends Component {
pref={prefs.preserveConsoleLogs}
onChange={e => this.updateSetting(e, 'preserveConsoleLogs')}
/>
<p class="help-text">
<HelpText>
Preserves the console logs across your preview refreshes
</p>
</HelpText>
</TabPanel>
<TabPanel label="Indentation">
<div class="line">
@ -151,6 +153,17 @@ export default class Settings extends Component {
<TabPanel label="Editor">
<div>
<div>
<CheckboxSetting
title="Use experimental Monaco editor"
label="Use Monaco Editor"
pref={prefs.isMonacoEditorOn}
onChange={e => this.updateSetting(e, 'isMonacoEditorOn')}
/>
<HelpText>
(Experimental) Switches from CodeMirror to Monaco. Many other
settings might not be available in Monaco.
</HelpText>
<Divider />
<div class="line">
<span>Default Preprocessors</span>
<div class="flex">
@ -308,21 +321,20 @@ export default class Settings extends Component {
pref={prefs.isCodeBlastOn}
onChange={e => this.updateSetting(e, 'isCodeBlastOn')}
/>
<p class="help-text">
Enjoy wonderful particle blasts while you type
</p>
<HelpText>Enjoy wonderful particle blasts while you type</HelpText>
<Divider />
<CheckboxSetting
label="Js13kGames Mode"
pref={prefs.isJs13kModeOn}
onChange={e => this.updateSetting(e, 'isJs13kModeOn')}
/>
<p class="help-text">
<HelpText>
Make the app ready to build some games for{' '}
<a href="https://js13kgames.com/" target="_blank" rel="noopener">
Js13kGames
</a>.
</p>
</a>
.
</HelpText>
</TabPanel>
<TabPanel label="Advanced">
<div>
@ -338,11 +350,11 @@ export default class Settings extends Component {
ms
</div>
</label>
<p class="help-text">
<HelpText>
If any loop iteration takes more than the defined time, it is
detected as a potential infinite loop and further iterations are
stopped.
</p>
</HelpText>
</div>
<Divider />

View File

@ -5,7 +5,11 @@ export class SplitPane extends Component {
componentDidMount() {
this.updateSplit();
}
componentWillUpdate() {
if (this.splitInstance) {
this.splitInstance.destroy();
}
}
componentDidUpdate(prevProps) {
if (this.hasGutter() && !this.hasPropsChanged(prevProps, this.props)) {
return;
@ -33,13 +37,8 @@ export class SplitPane extends Component {
const { children, ...options } = this.props;
options.gutterSize = 6;
if (this.splitInstance && this.hasGutter()) {
this.splitInstance.destroy();
}
/* eslint-disable new-cap */
this.splitInstance = Split([...this.parent.children], options);
console.log('recreating split');
/* eslint-enable new-cap */

View File

@ -1,176 +0,0 @@
import { h, Component } from 'preact';
import CodeMirror from '../CodeMirror';
import 'codemirror/addon/edit/matchbrackets.js';
import 'codemirror/addon/edit/matchtags.js';
import 'codemirror/addon/edit/closebrackets.js';
import 'codemirror/addon/edit/closetag.js';
import 'codemirror/addon/comment/comment.js';
import 'codemirror/addon/fold/foldcode.js';
import 'codemirror/addon/fold/foldgutter.js';
import 'codemirror/addon/fold/xml-fold.js';
import 'codemirror/addon/fold/indent-fold.js';
import 'codemirror/addon/fold/comment-fold.js';
import 'codemirror/addon/fold/brace-fold.js';
import 'codemirror/addon/hint/show-hint.js';
import 'codemirror/addon/hint/javascript-hint.js';
import 'codemirror/addon/hint/xml-hint.js';
import 'codemirror/addon/hint/html-hint.js';
import 'codemirror/addon/hint/css-hint.js';
import 'codemirror/addon/selection/active-line.js';
import 'codemirror/addon/search/searchcursor.js';
import 'codemirror/addon/search/search.js';
import 'codemirror/addon/dialog/dialog.js';
import 'codemirror/addon/search/jump-to-line.js';
import 'codemirror/mode/xml/xml.js';
import 'codemirror/mode/css/css.js';
import 'codemirror/mode/javascript/javascript.js';
import 'codemirror/mode/htmlmixed/htmlmixed.js';
import 'codemirror/keymap/sublime.js';
import 'codemirror/keymap/vim.js';
import 'code-blast-codemirror/code-blast.js';
import emmet from '@emmetio/codemirror-plugin';
import { prettify } from '../utils';
emmet(CodeMirror);
export default class UserCodeMirror extends Component {
componentDidMount() {
this.initEditor();
}
shouldComponentUpdate(nextProps) {
if (nextProps.prefs !== this.props.prefs) {
const { prefs } = nextProps;
this.cm.setOption('indentWithTabs', prefs.indentWith !== 'spaces');
this.cm.setOption(
'blastCode',
prefs.isCodeBlastOn ? { effect: 2, shake: false } : false
);
this.cm.setOption('theme', prefs.editorTheme);
this.cm.setOption('indentUnit', +prefs.indentSize);
this.cm.setOption('tabSize', +prefs.indentSize);
this.cm.setOption('keyMap', prefs.keymap);
this.cm.setOption('lineWrapping', prefs.lineWrap);
this.cm.setOption('lineWrapping', prefs.autoCloseTags);
this.cm.refresh();
}
return false;
}
initEditor() {
const { options, prefs } = this.props;
this.cm = CodeMirror.fromTextArea(this.textarea, {
mode: options.mode,
lineNumbers: true,
lineWrapping: !!prefs.lineWrap,
autofocus: options.autofocus || false,
autoCloseBrackets: true,
autoCloseTags: !!prefs.autoCloseTags,
matchBrackets: true,
matchTags: options.matchTags || false,
tabMode: 'indent',
keyMap: prefs.keyMap || 'sublime',
theme: prefs.editorTheme || 'monokai',
lint: !!options.lint,
tabSize: +prefs.indentSize || 2,
indentWithTabs: prefs.indentWith !== 'spaces',
indentUnit: +prefs.indentSize,
foldGutter: true,
styleActiveLine: true,
gutters: options.gutters || [],
// cursorScrollMargin: '20', has issue with scrolling
profile: options.profile || '',
extraKeys: {
Up: function(editor) {
// Stop up/down keys default behavior when saveditempane is open
// if (isSavedItemsPaneOpen) {
// return;
// }
CodeMirror.commands.goLineUp(editor);
},
Down: function(editor) {
// if (isSavedItemsPaneOpen) {
// return;
// }
CodeMirror.commands.goLineDown(editor);
},
'Shift-Tab': function(editor) {
CodeMirror.commands.indentAuto(editor);
},
'Shift-Ctrl-F': function(editor) {
if (options.prettier) {
prettify({
content: editor.getValue(),
type: options.prettierParser
}).then(formattedCode => editor.setValue(formattedCode));
}
},
Tab: function(editor) {
if (options.emmet) {
const didEmmetWork = editor.execCommand('emmetExpandAbbreviation');
if (didEmmetWork === true) {
return;
}
}
const input = $('[data-setting=indentWith]:checked');
if (
!editor.somethingSelected() &&
(!prefs.indentWith || prefs.indentWith === 'spaces')
) {
// softtabs adds spaces. This is required because by default tab key will put tab, but we want
// to indent with spaces if `spaces` is preferred mode of indentation.
// `somethingSelected` needs to be checked otherwise, all selected code is replaced with softtab.
CodeMirror.commands.insertSoftTab(editor);
} else {
CodeMirror.commands.defaultTab(editor);
}
},
Enter: 'emmetInsertLineBreak'
}
});
this.cm.on('focus', editor => {
if (typeof this.props.onFocus === 'function') this.props.onFocus(editor);
});
this.cm.on('change', this.props.onChange);
this.cm.addKeyMap({
'Ctrl-Space': 'autocomplete'
});
this.cm.on('inputRead', (editor, input) => {
// Process further If this has autocompletition on and also the global
// autocomplete setting is on.
if (!this.props.options.noAutocomplete && this.props.prefs.autoComplete) {
if (
input.origin !== '+input' ||
input.text[0] === ';' ||
input.text[0] === ',' ||
input.text[0] === ' '
) {
return;
}
CodeMirror.commands.autocomplete(this.cm, null, {
completeSingle: false
});
}
});
this.props.onCreation(this.cm);
}
render() {
return (
<textarea
ref={el => (this.textarea = el)}
name=""
id=""
cols="30"
rows="10"
/>
);
}
}

View File

@ -133,7 +133,8 @@ export default class App extends Component {
layoutMode: 2,
isJs13kModeOn: false,
autoCloseTags: true,
lang: 'en'
lang: 'en',
isMonacoEditorOn: false
};
this.prefs = {};
@ -1527,20 +1528,6 @@ export default class App extends Component {
<Alerts />
<form
style="display:none;"
action="https://codepen.io/pen/define"
method="POST"
target="_blank"
id="js-codepen-form"
>
<input
type="hidden"
name="data"
value="{&quot;title&quot;: &quot;New Pen!&quot;, &quot;html&quot;: &quot;<div>Hello, World!</div>&quot;}"
/>
</form>
<Modal
show={this.state.isAddLibraryModalOpen}
closeHandler={() => this.setState({ isAddLibraryModalOpen: false })}
@ -1674,7 +1661,7 @@ export default class App extends Component {
<input
type="hidden"
name="data"
value="{&quot;title&quot;: &quot;New Pen!&quot;, &quot;html&quot;: &quot;<div>Hello, World!</div>&quot;}"
value='{"title": "New Pen!", "html": "<div>Hello, World!</div>"}'
/>
</form>
</div>

View File

@ -55,9 +55,9 @@ import { log } from './utils';
if (db) {
return resolve(db);
}
const _firestore = firebase.firestore();
_firestore.settings({ timestampsInSnapshots: true });
return _firestore
const firestoreInstance = firebase.firestore();
firestoreInstance.settings({ timestampsInSnapshots: true });
return firestoreInstance
.enablePersistence({ experimentalTabSynchronization: true })
.then(function() {
// Initialize Cloud Firestore through firebase

File diff suppressed because one or more lines are too long

3843
src/lib/monaco/monaco.css Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -49,6 +49,11 @@ if (
// It's the perfect time to display a "New content is available; please refresh."
// message in the page's interface.
console.log('New or updated content is available.');
if (window.alertsService) {
window.alertsService.add(
'New version available. Please refresh the page.'
);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a "Content is cached for offline use." message.

View File

@ -305,6 +305,21 @@ export function loadJS(src) {
return d.promise;
}
export function loadCss({ url, id }) {
var d = deferred();
var style = window.document.createElement('link');
style.setAttribute('href', url);
style.setAttribute('rel', 'stylesheet');
if (id) {
style.setAttribute('id', id);
}
document.head.appendChild(style);
style.onload = function() {
d.resolve();
};
return d.promise;
}
/* eslint-disable max-params */
export function getCompleteHtml(html, css, js, item, isForExport) {
/* eslint-enable max-params */