git.sagev.space/public/assets/pebblisp-console.js

185 lines
4.8 KiB
JavaScript

let pl
function importPebblisp() {
if (pl) {
return
}
pl = () => {}
return import('./pebblisp.js')
.then(async mod => await mod.default())
.then(async module =>
pl = function pl(code) {
return module.ccall('run', 'string', ['string'], [code])
}
)
}
class PlConsole extends HTMLElement {
constructor() {
super()
this.codeHistory = `
(if (= 4 (+ 2 2))
"Math still works!"
"Uh-oh...")
> Math still works!`
}
input(e) {
e.preventDefault()
}
connectedCallback() {
const shadow = this.attachShadow({ mode: 'closed' })
shadow.innerHTML = `
<script>
window.pl = function pl(code) {
return Module.ccall('run', 'string', ['string'], [code])
}
</script>
<style>
.loadable {
transition: opacity, display;
transition-duration: 2s;
transition-timing-function: ease-in;
opacity: 1;
}
#pebblisp-console.unloaded .loadable {
opacity: 0;
cursor: pointer;
user-select: none;
pointer-events: none;
}
#pebblisp-console {
width: calc(min(500px, 90%));
height: 400px;
display: flex;
flex-direction: column;
justify-content: end;
background-color: black;
color: white;
padding: 1em;
font-size: 115%;
border-radius: 6px;
position: relative;
}
@media only screen and (max-width: 600px) {
#pebblisp-console {
width: calc(min(500px, 105%));
margin-left: -20px;
margin-right: -20px;
font-size: 75%;
}
}
#pebblisp-output {
text-align: left;
overflow-y: scroll;
white-space: pre-wrap;
word-wrap: break-word;
margin-top: 0;
}
.pebblisp-repl-input {
font-family: monospace;
margin: 0;
padding: 3px;
border-width: 0;
outline: #555 solid 1px;
width: 98%;
background-color: #2b2a33;
color: #eee;
}
.pebblisp-repl-input:focus {
outline: #aaa solid 1px;
}
#pebblisp-console-preload {
opacity: 0;
height: 100%;
width: 100%;
text-align: center;
user-select: none;
cursor: pointer;
display: flex;
flex-direction: column;
justify-content: center;
position: absolute;
top: 0;
left: 0;
transition: opacity 1s;
text-shadow: -1px 2px 2px #FFFFFF77;
}
#pebblisp-console-preload:hover {
/*text-shadow: -1px 2px 2px white;*/
animation: 1s infinite jumpy-text steps(5, end);
}
#pebblisp-console.unloaded #pebblisp-console-preload {
opacity: 1;
}
@keyframes jumpy-text {
0% {
text-shadow: -1px 2px 2px #FFFFFF77;
}
20% {
text-shadow: 1px 1px 2px #FFFFFF77;
}
40% {
text-shadow: 0px 3px 2px #FFFFFF77;
}
60% {
text-shadow: -2px 0px 2px #FFFFFF77;
}
80% {
text-shadow: 0px -3px 2px #FFFFFF77;
}
}
</style>
<div id="pebblisp-console" class="unloaded">
<div id="pebblisp-console-preload">
<div>Click to load the Pebblisp REPL (&lt; 0.5mB)</div>
</div>
<pre id="pebblisp-output" class="loadable">${this.codeHistory}</pre>
<form id='repl-form' class="loadable">
<input name="input" type="text" class="pebblisp-repl-input loadable">
</form>
</div>
`
const consoleNode = shadow.getElementById('pebblisp-console')
const preloadMessage = shadow.getElementById('pebblisp-console-preload')
const replForm = shadow.getElementById('repl-form')
preloadMessage.addEventListener('click', e => {
setTimeout(() => preloadMessage.remove(), 1000)
importPebblisp().then(() => {
consoleNode.classList.remove('unloaded')
})
})
replForm.addEventListener('submit', e => {
e.preventDefault()
const input = e.target.input.value
e.target.input.value = ''
const result = pl(input)
this.codeHistory += `
${input}
> ${result}`
const output = shadow.getElementById('pebblisp-output')
output.innerText = this.codeHistory
output.scrollTop = output.scrollHeight
})
}
}
customElements.define("pebblisp-console", PlConsole);
document.addEventListener('DOMContentLoaded', () => {
const element = document.getElementById('user-content-pebblisp-console-placeholder')
element?.replaceWith(document.createElement('pebblisp-console'))
})