diff --git a/xP/public/xP.css b/xP/public/xP.css index 8b67844..77e89fc 100644 --- a/xP/public/xP.css +++ b/xP/public/xP.css @@ -16,6 +16,10 @@ body { border-bottom: 1px solid #ccc; padding: .05rem .3rem; } +.title { + display: flex; + justify-content: space-between; +} .middle { flex: auto; @@ -52,6 +56,12 @@ body { grid-template-columns: max-content auto; overflow-y: auto; } +.log { + padding: .1rem .3rem; + font-family: monospace; + white-space: pre-wrap; + overflow-y: auto; +} .date { padding: .3rem; diff --git a/xP/public/xP.js b/xP/public/xP.js index 19a769a..b02d2af 100644 --- a/xP/public/xP.js +++ b/xP/public/xP.js @@ -118,6 +118,11 @@ class RelayRpc extends EventTarget { this.promised[seq] = {resolve, reject} }) } + + base64decode(str) { + return decodeURIComponent(atob(str).split('').map(c => + '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join('')) + } } // ---- Event processing ------------------------------------------------------- @@ -126,10 +131,14 @@ let rpc = new RelayRpc(proxy) let buffers = new Map() let bufferCurrent = undefined +let bufferLog = undefined + let connecting = true rpc.connect().then(result => { buffers.clear() bufferCurrent = undefined + bufferLog = undefined + rpc.send({command: 'Hello', version: 1}) connecting = false m.redraw() @@ -166,6 +175,7 @@ rpc.addEventListener('BufferActivate', event => { let e = event.detail, b = buffers.get(e.bufferName) let old = buffers.get(bufferCurrent) bufferCurrent = e.bufferName + bufferLog = undefined let textarea = document.getElementById('input') if (textarea === null) @@ -230,16 +240,14 @@ function applyColor(fg, bg, inverse) { let BufferList = { view: vnode => { - let items = [] - buffers.forEach((b, name) => { + let items = Array.from(buffers, ([name, b]) => { let attrs = { - onclick: event => { - rpc.send({command: 'BufferActivate', bufferName: name}) - }, + onclick: event => + rpc.send({command: 'BufferActivate', bufferName: name}), } if (name == bufferCurrent) attrs.class = 'active' - items.push(m('.item', attrs, name)) + return m('.item', attrs, name) }) return m('.list', {}, items) }, @@ -319,7 +327,7 @@ let Content = { let Buffer = { oncreate: vnode => { - if (vnode.dom === undefined) + if (vnode.dom === undefined || bufferLog !== undefined) return let el = vnode.dom.children[1] @@ -349,9 +357,36 @@ let Buffer = { lines.push(m('.time', {}, date.toLocaleTimeString())) lines.push(m(Content, {}, line)) }) + return m('.buffer-container', {}, [ m('.filler'), - m('.buffer', {}, lines), + bufferLog !== undefined + ? m(".log", {}, bufferLog) + : m('.buffer', {}, lines), + ]) + }, +} + +let Toolbar = { + toggleLog: () => { + if (bufferLog) { + bufferLog = undefined + return + } + + rpc.send({ + command: 'BufferLog', + bufferName: bufferCurrent, + }).then(resp => { + bufferLog = rpc.base64decode(resp.log) + m.redraw() + }) + }, + + view: vnode => { + return m('.toolbar', {}, [ + m('button', {onclick: Toolbar.toggleLog}, + bufferLog === undefined ? 'Show log' : 'Hide log'), ]) }, } @@ -372,12 +407,12 @@ function onKeyDown(event) { bufferName: bufferCurrent, text: textarea.value, position: textarea.selectionEnd, - }).then(response => { + }).then(resp => { // TODO: Somehow display remaining options, or cycle through. - if (response.completions.length) - textarea.setRangeText(response.completions[0], - response.start, textarea.selectionEnd, 'end') - if (response.completions.length === 1) + if (resp.completions.length) + textarea.setRangeText(resp.completions[0], + resp.start, textarea.selectionEnd, 'end') + if (resp.completions.length === 1) textarea.setRangeText(' ', textarea.selectionStart, textarea.selectionEnd, 'end') }) @@ -415,7 +450,7 @@ let Main = { state = "Disconnected" return m('.xP', {}, [ - m('.title', {}, `xP (${state})`), + m('.title', {}, [`xP (${state})`, m(Toolbar)]), m('.middle', {}, [m(BufferList), m(Buffer)]), m('.status', {}, bufferCurrent), m(Input),