-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #93 from milafrerichs/feat/upgrade-console
Feat/upgrade console
- Loading branch information
Showing
14 changed files
with
641 additions
and
396 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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]>", | ||
|
@@ -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", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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} |
Oops, something went wrong.