Skip to content

Commit

Permalink
Merge pull request #93 from milafrerichs/feat/upgrade-console
Browse files Browse the repository at this point in the history
Feat/upgrade console
  • Loading branch information
milafrerichs authored Jan 3, 2020
2 parents a6b825d + 6716dd8 commit 16d0154
Show file tree
Hide file tree
Showing 14 changed files with 641 additions and 396 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[*.{js,svelte}]
[*.{js,svelte,html}]
indent_style = space
indent_size = 2

Expand Down
8 changes: 8 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# v0.6.0
## Changed
Internal changes to how the iframes work and how messages are posted, should improve performance and future use

# v0.5.1
## Changed
Use Svelte JSON tree directly inside the component, and remove the extra iframe for the console

# v0.5.0
## Added
Added Svelte JSON Tree to the project to pretifx the console output and allow to show more values than just objects
Expand Down
5 changes: 2 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "javascript-repl",
"svelte": "src/Repl.svelte",
"module": "index.mjs",
"version": "0.5.0",
"version": "0.6.0",
"description": "",
"main": "index.js",
"author": "Mila Frerichs <[email protected]>",
Expand Down Expand Up @@ -33,7 +33,8 @@
"now-build": "npm run copy && npm run srcdoc && rollup -c example/rollup.config.js"
},
"dependencies": {
"codemirror": "^5.47.0"
"codemirror": "^5.47.0",
"svelte-json-tree": "^0.1.0"
},
"files": [
"src",
Expand Down
223 changes: 223 additions & 0 deletions src/CodeMirror.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
<script context="module">
let codemirror_promise;
</script>

<script>
import { onMount, beforeUpdate, createEventDispatcher, getContext } from 'svelte';
const dispatch = createEventDispatcher();
export let readonly = false;
export let errorLoc = null;
export let flex = false;
export let lineNumbers = true;
export let tab = true;
let w;
let h;
let code = '';
let mode;
// We have to expose set and update methods, rather
// than making this state-driven through props,
// because it's difficult to update an editor
// without resetting scroll otherwise
export async function set(new_code, new_mode) {
if (new_mode !== mode) {
await createEditor(mode = new_mode);
}
code = new_code;
updating_externally = true;
if (editor) editor.setValue(code);
updating_externally = false;
}
export function update(new_code) {
code = new_code;
if (editor) {
const { left, top } = editor.getScrollInfo();
editor.setValue(code = new_code);
editor.scrollTo(left, top);
}
}
export function resize() {
editor.refresh();
}
export function focus() {
editor.focus();
}
const modes = {
js: {
name: 'javascript',
json: false
},
json: {
name: 'javascript',
json: true
},
};
const refs = {};
let editor;
let updating_externally = false;
let marker;
let error_line;
let destroyed = false;
let CodeMirror;
$: if (editor && w && h) {
editor.refresh();
}
$: {
if (marker) marker.clear();
if (errorLoc) {
const line = errorLoc.line - 1;
const ch = errorLoc.column;
marker = editor.markText({ line, ch }, { line, ch: ch + 1 }, {
className: 'error-loc'
});
error_line = line;
} else {
error_line = null;
}
}
let previous_error_line;
$: if (editor) {
if (previous_error_line != null) {
editor.removeLineClass(previous_error_line, 'wrap', 'error-line')
}
if (error_line && (error_line !== previous_error_line)) {
editor.addLineClass(error_line, 'wrap', 'error-line');
previous_error_line = error_line;
}
}
onMount(() => {
if (window.CodeMirror) {
CodeMirror = window.CodeMirror;
createEditor(mode || 'js').then(() => {
updating_externally = true;
if (editor) editor.setValue(code || '');
updating_externally = false;
});
} else {
codemirror_promise.then(async mod => {
CodeMirror = mod.default;
await createEditor(mode || 'js');
if (editor) editor.setValue(code || '');
});
}
return () => {
destroyed = true;
if (editor) editor.toTextArea();
}
});
let first = true;
async function createEditor(mode) {
if (destroyed || !CodeMirror) return;
if (editor) editor.toTextArea();
const opts = {
lineNumbers,
lineWrapping: true,
indentWithTabs: true,
indentUnit: 2,
tabSize: 2,
value: '',
mode: modes[mode] || {
name: mode
},
readOnly: readonly,
autoCloseBrackets: true,
autoCloseTags: true
};
if (!tab) opts.extraKeys = {
Tab: tab,
'Shift-Tab': tab
};
// Creating a text editor is a lot of work, so we yield
// the main thread for a moment. This helps reduce jank
if (first) await sleep(50);
if (destroyed) return;
editor = CodeMirror.fromTextArea(refs.editor, opts);
editor.on('change', instance => {
if (!updating_externally) {
const value = instance.getValue();
dispatch('change', { value });
}
});
if (first) await sleep(50);
editor.refresh();
first = false;
}
function sleep(ms) {
return new Promise(fulfil => setTimeout(fulfil, ms));
}
</script>

<style>
.codemirror-container {
position: relative;
width: 100%;
height: 100%;
border: none;
line-height: 1.5;
overflow: hidden;
}
.codemirror-container :global(.CodeMirror) {
height: 100%;
background: transparent;
font: 400 14px/1.7 var(--font-mono);
color: var(--base);
}
.codemirror-container.flex :global(.CodeMirror) {
height: auto;
}
.codemirror-container.flex :global(.CodeMirror-lines) {
padding: 0;
}
.codemirror-container :global(.CodeMirror-gutters) {
padding: 0 16px 0 8px;
border: none;
}
.codemirror-container :global(.error-loc) {
position: relative;
border-bottom: 2px solid #da106e;
}
.codemirror-container :global(.error-line) {
background-color: rgba(200, 0, 0, .05);
}
textarea {
visibility: hidden;
}
pre {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
border: none;
padding: 4px 4px 4px 60px;
resize: none;
font-family: var(--font-mono);
font-size: 13px;
line-height: 1.7;
user-select: none;
pointer-events: none;
color: #ccc;
tab-size: 2;
-moz-tab-size: 2;
}
.flex pre {
padding: 0 0 0 4px;
height: auto;
}
</style>

<div class='codemirror-container' class:flex bind:offsetWidth={w} bind:offsetHeight={h}>
<textarea
tabindex='0'
bind:this={refs.editor}
readonly
value={code}
></textarea>

{#if !CodeMirror}
<pre style="position: absolute; left: 0; top: 0"
>{code}</pre>

<div style="position: absolute; width: 100%; bottom: 0">
</div>
{/if}
</div>
77 changes: 52 additions & 25 deletions src/Console.svelte
Original file line number Diff line number Diff line change
@@ -1,32 +1,59 @@
<script>
import Result from './Result.svelte';
import { onMount } from 'svelte';
import JSONNode from 'svelte-json-tree';
import { onMount } from 'svelte';
import { code } from './stores.js'
import { logs } from './stores.js'
export let width;
export let height;
let message = '';
$: if($code) {
message = `
document.body.innerHTML = '';
var consoleOutput = '';
var old = console.log;
const script = document.createElement('script');
script.type= 'text/javascript';
script.src = 'https://cdn.jsdelivr.net/gh/milafrerichs/svelte-json-tree@098ffbf4bcd7aa982ce17c5899195e1a6396c7dd/index.js';
document.head.appendChild(script);
console.log = function (message) {
new JsonTree({
target: document.body,
props: {
value: message
}
});
};
${$code}
`
}
</script>
<Result name={'console'} {width} {height} html={''} code={message} />
<style>
.log {
border-bottom: 1px solid #eee;
padding: 5px 10px;
display: flex;
}
.log > :global(*) {
margin-right: 10px;
font-family: var(--font-mono);
}
.console-warn {
background: #fffbe6;
border-color: #fff4c4;
}
.console-error {
background: #fff0f0;
border-color: #fed6d7;
}
.count {
color: #999;
font-size: 12px;
line-height: 1.2;
}
.info {
color: #666;
font-family: var(--font) !important;
font-size: 12px;
}
.error {
color: #da106e; /* todo make this a var */
}
</style>
{#each $logs as log}
<div class="log console-{log.level}">
{#if log.count > 1}
<span class="count">{log.count}x</span>
{/if}

{#if log.level === 'clear'}
<span class="info">Console was cleared</span>
{:else if log.level === 'unclonable'}
<span class="info error">Message could not be cloned. Open devtools to see it</span>
{:else}
{#each log.args as arg}
<JSONNode value={arg} />
{/each}
{/if}
</div>
{/each}
Loading

0 comments on commit 16d0154

Please sign in to comment.