2022-09-05 22:34:20 +02:00
|
|
|
|
// Copyright (c) 2022, Přemysl Eric Janouch <p@janouch.name>
|
|
|
|
|
// SPDX-License-Identifier: 0BSD
|
|
|
|
|
|
|
|
|
|
// --- Colours -----------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
let palette = [
|
|
|
|
|
'#000', '#800', '#080', '#880', '#008', '#808', '#088', '#ccc',
|
|
|
|
|
'#888', '#f00', '#0f0', '#ff0', '#00f', '#f0f', '#0ff', '#fff',
|
|
|
|
|
]
|
|
|
|
|
palette.length = 256
|
|
|
|
|
for (let i = 0; i < 216; i++) {
|
|
|
|
|
let r = i / 36 >> 0, g = (i / 6 >> 0) % 6, b = i % 6
|
|
|
|
|
r = !r ? '00' : (55 + 40 * r).toString(16)
|
|
|
|
|
g = !g ? '00' : (55 + 40 * g).toString(16)
|
|
|
|
|
b = !b ? '00' : (55 + 40 * b).toString(16)
|
|
|
|
|
palette[16 + i] = `#${r}${g}${b}`
|
|
|
|
|
}
|
|
|
|
|
for (let i = 0; i < 24; i++) {
|
|
|
|
|
let g = ('0' + (8 + i * 10).toString(16)).slice(-2)
|
|
|
|
|
palette[232 + i] = `#${g}${g}${g}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function applyColor(fg, bg, inverse) {
|
|
|
|
|
if (inverse)
|
|
|
|
|
[fg, bg] = [bg >= 0 ? bg : 15, fg >= 0 ? fg : 0]
|
|
|
|
|
|
|
|
|
|
let style = ''
|
|
|
|
|
if (fg >= 0)
|
|
|
|
|
style += `color: ${palette[fg]};`
|
|
|
|
|
if (bg >= 0)
|
|
|
|
|
style += `background-color: ${palette[bg]};`
|
|
|
|
|
if (style)
|
|
|
|
|
return style
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- Event processing -------------------------------------------------------
|
|
|
|
|
|
Start X11 and web frontends for xC
For this, we needed a wire protocol. After surveying available options,
it was decided to implement an XDR-like protocol code generator
in portable AWK. It now has two backends, per each of:
- xF, the X11 frontend, is in C, and is meant to be the primary
user interface in the future.
- xP, the web frontend, relies on a protocol proxy written in Go,
and is meant for use on-the-go (no pun intended).
They are very much work-in-progress proofs of concept right now,
and the relay protocol is certain to change.
2022-08-08 04:39:20 +02:00
|
|
|
|
// TODO: Probably reset state on disconnect, and indicate to user.
|
|
|
|
|
let socket = new WebSocket(proxy)
|
|
|
|
|
|
|
|
|
|
let commandSeq = 0
|
|
|
|
|
function send(command) {
|
|
|
|
|
socket.send(JSON.stringify({commandSeq: ++commandSeq, data: command}))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
socket.onopen = function(event) {
|
|
|
|
|
send({command: 'Hello', version: 1})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let buffers = new Map()
|
|
|
|
|
let bufferCurrent = undefined
|
|
|
|
|
|
|
|
|
|
socket.onmessage = function(event) {
|
|
|
|
|
console.log(event.data)
|
|
|
|
|
|
|
|
|
|
let e = JSON.parse(event.data).data
|
|
|
|
|
switch (e.event) {
|
|
|
|
|
case 'BufferUpdate':
|
|
|
|
|
{
|
|
|
|
|
let b = buffers.get(e.bufferName)
|
|
|
|
|
if (b === undefined) {
|
|
|
|
|
b = {lines: []}
|
|
|
|
|
buffers.set(e.bufferName, b)
|
|
|
|
|
}
|
|
|
|
|
// TODO: Update any buffer properties.
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
case 'BufferRename':
|
|
|
|
|
buffers.set(e.new, buffers.get(e.bufferName))
|
|
|
|
|
buffers.delete(e.bufferName)
|
|
|
|
|
break
|
|
|
|
|
case 'BufferRemove':
|
|
|
|
|
buffers.delete(e.bufferName)
|
|
|
|
|
break
|
|
|
|
|
case 'BufferActivate':
|
|
|
|
|
bufferCurrent = e.bufferName
|
|
|
|
|
// TODO: Somehow scroll to the end of it immediately.
|
|
|
|
|
// TODO: Focus the textarea.
|
|
|
|
|
break
|
|
|
|
|
case 'BufferLine':
|
|
|
|
|
{
|
|
|
|
|
let b = buffers.get(e.bufferName)
|
|
|
|
|
if (b !== undefined)
|
|
|
|
|
b.lines.push({when: e.when, rendition: e.rendition, items: e.items})
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
case 'BufferClear':
|
|
|
|
|
{
|
|
|
|
|
let b = buffers.get(e.bufferName)
|
|
|
|
|
if (b !== undefined)
|
|
|
|
|
b.lines.length = 0
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m.redraw()
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-05 22:34:20 +02:00
|
|
|
|
// ---- UI ---------------------------------------------------------------------
|
|
|
|
|
|
Start X11 and web frontends for xC
For this, we needed a wire protocol. After surveying available options,
it was decided to implement an XDR-like protocol code generator
in portable AWK. It now has two backends, per each of:
- xF, the X11 frontend, is in C, and is meant to be the primary
user interface in the future.
- xP, the web frontend, relies on a protocol proxy written in Go,
and is meant for use on-the-go (no pun intended).
They are very much work-in-progress proofs of concept right now,
and the relay protocol is certain to change.
2022-08-08 04:39:20 +02:00
|
|
|
|
let BufferList = {
|
|
|
|
|
view: vnode => {
|
|
|
|
|
let items = []
|
|
|
|
|
buffers.forEach((b, name) => {
|
|
|
|
|
let attrs = {
|
|
|
|
|
onclick: e => {
|
|
|
|
|
send({command: 'BufferActivate', bufferName: name})
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
if (name == bufferCurrent)
|
|
|
|
|
attrs.class = 'active'
|
|
|
|
|
items.push(m('.item', attrs, name))
|
|
|
|
|
})
|
|
|
|
|
return m('.list', {}, items)
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-06 14:35:42 +02:00
|
|
|
|
function linkify(text, attrs, a) {
|
|
|
|
|
let re = new RegExp([
|
|
|
|
|
/https?:\/\//,
|
|
|
|
|
/([^\[\](){}<>"'\s]|\([^\[\](){}<>"'\s]*\))+/,
|
|
|
|
|
/[^\[\](){}<>"'\s,.:]/,
|
|
|
|
|
].map(r => r.source).join(''), 'g')
|
|
|
|
|
|
|
|
|
|
let end = 0, match
|
|
|
|
|
while ((match = re.exec(text)) !== null) {
|
|
|
|
|
if (end < match.index)
|
|
|
|
|
a.push(m('span', attrs, text.substring(end, match.index)))
|
|
|
|
|
a.push(m('a', {href: match[0], ...attrs}, match[0]))
|
|
|
|
|
end = re.lastIndex
|
|
|
|
|
}
|
|
|
|
|
if (end < text.length)
|
|
|
|
|
a.push(m('span', attrs, text.substring(end)))
|
|
|
|
|
}
|
|
|
|
|
|
Start X11 and web frontends for xC
For this, we needed a wire protocol. After surveying available options,
it was decided to implement an XDR-like protocol code generator
in portable AWK. It now has two backends, per each of:
- xF, the X11 frontend, is in C, and is meant to be the primary
user interface in the future.
- xP, the web frontend, relies on a protocol proxy written in Go,
and is meant for use on-the-go (no pun intended).
They are very much work-in-progress proofs of concept right now,
and the relay protocol is certain to change.
2022-08-08 04:39:20 +02:00
|
|
|
|
let Content = {
|
|
|
|
|
view: vnode => {
|
|
|
|
|
let line = vnode.children[0]
|
|
|
|
|
let content = []
|
|
|
|
|
switch (line.rendition) {
|
2022-09-05 22:53:34 +02:00
|
|
|
|
case 'Indent': content.push(m('span.mark', {}, '')); break
|
|
|
|
|
case 'Status': content.push(m('span.mark', {}, '–')); break
|
|
|
|
|
case 'Error': content.push(m('span.mark.error', {}, '⚠')); break
|
|
|
|
|
case 'Join': content.push(m('span.mark.join', {}, '→')); break
|
|
|
|
|
case 'Part': content.push(m('span.mark.part', {}, '←')); break
|
|
|
|
|
case 'Action': content.push(m('span.mark.action', {}, '✶')); break
|
Start X11 and web frontends for xC
For this, we needed a wire protocol. After surveying available options,
it was decided to implement an XDR-like protocol code generator
in portable AWK. It now has two backends, per each of:
- xF, the X11 frontend, is in C, and is meant to be the primary
user interface in the future.
- xP, the web frontend, relies on a protocol proxy written in Go,
and is meant for use on-the-go (no pun intended).
They are very much work-in-progress proofs of concept right now,
and the relay protocol is certain to change.
2022-08-08 04:39:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let classes = new Set()
|
|
|
|
|
let flip = c => {
|
|
|
|
|
if (classes.has(c))
|
|
|
|
|
classes.delete(c)
|
|
|
|
|
else
|
|
|
|
|
classes.add(c)
|
|
|
|
|
}
|
2022-09-05 22:34:20 +02:00
|
|
|
|
let fg = -1, bg = -1, inverse = false
|
Start X11 and web frontends for xC
For this, we needed a wire protocol. After surveying available options,
it was decided to implement an XDR-like protocol code generator
in portable AWK. It now has two backends, per each of:
- xF, the X11 frontend, is in C, and is meant to be the primary
user interface in the future.
- xP, the web frontend, relies on a protocol proxy written in Go,
and is meant for use on-the-go (no pun intended).
They are very much work-in-progress proofs of concept right now,
and the relay protocol is certain to change.
2022-08-08 04:39:20 +02:00
|
|
|
|
line.items.forEach(item => {
|
|
|
|
|
switch (item.kind) {
|
|
|
|
|
case 'Text':
|
2022-09-06 14:35:42 +02:00
|
|
|
|
linkify(item.text, {
|
Start X11 and web frontends for xC
For this, we needed a wire protocol. After surveying available options,
it was decided to implement an XDR-like protocol code generator
in portable AWK. It now has two backends, per each of:
- xF, the X11 frontend, is in C, and is meant to be the primary
user interface in the future.
- xP, the web frontend, relies on a protocol proxy written in Go,
and is meant for use on-the-go (no pun intended).
They are very much work-in-progress proofs of concept right now,
and the relay protocol is certain to change.
2022-08-08 04:39:20 +02:00
|
|
|
|
class: Array.from(classes.keys()).join(' '),
|
2022-09-05 22:34:20 +02:00
|
|
|
|
style: applyColor(fg, bg, inverse),
|
2022-09-06 14:35:42 +02:00
|
|
|
|
}, content)
|
Start X11 and web frontends for xC
For this, we needed a wire protocol. After surveying available options,
it was decided to implement an XDR-like protocol code generator
in portable AWK. It now has two backends, per each of:
- xF, the X11 frontend, is in C, and is meant to be the primary
user interface in the future.
- xP, the web frontend, relies on a protocol proxy written in Go,
and is meant for use on-the-go (no pun intended).
They are very much work-in-progress proofs of concept right now,
and the relay protocol is certain to change.
2022-08-08 04:39:20 +02:00
|
|
|
|
break
|
|
|
|
|
case 'Reset':
|
|
|
|
|
classes.clear()
|
2022-09-05 22:34:20 +02:00
|
|
|
|
fg = bg = -1
|
|
|
|
|
inverse = false
|
|
|
|
|
break
|
|
|
|
|
case 'FgColor':
|
|
|
|
|
fg = item.color
|
|
|
|
|
break
|
|
|
|
|
case 'BgColor':
|
|
|
|
|
bg = item.color
|
|
|
|
|
break
|
|
|
|
|
case 'FlipInverse':
|
|
|
|
|
inverse = !inverse
|
Start X11 and web frontends for xC
For this, we needed a wire protocol. After surveying available options,
it was decided to implement an XDR-like protocol code generator
in portable AWK. It now has two backends, per each of:
- xF, the X11 frontend, is in C, and is meant to be the primary
user interface in the future.
- xP, the web frontend, relies on a protocol proxy written in Go,
and is meant for use on-the-go (no pun intended).
They are very much work-in-progress proofs of concept right now,
and the relay protocol is certain to change.
2022-08-08 04:39:20 +02:00
|
|
|
|
break
|
|
|
|
|
case 'FlipBold': flip('b'); break
|
|
|
|
|
case 'FlipItalic': flip('i'); break
|
|
|
|
|
case 'FlipUnderline': flip('u'); break
|
|
|
|
|
case 'FlipCrossedOut': flip('s'); break
|
|
|
|
|
case 'FlipMonospace': flip('m'); break
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
return m('.content', {}, content)
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let Buffer = {
|
|
|
|
|
view: vnode => {
|
|
|
|
|
let lines = []
|
|
|
|
|
let b = buffers.get(bufferCurrent)
|
|
|
|
|
if (b === undefined)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
let lastDateMark = undefined
|
|
|
|
|
b.lines.forEach(line => {
|
2022-09-06 14:38:09 +02:00
|
|
|
|
let date = new Date(line.when)
|
Start X11 and web frontends for xC
For this, we needed a wire protocol. After surveying available options,
it was decided to implement an XDR-like protocol code generator
in portable AWK. It now has two backends, per each of:
- xF, the X11 frontend, is in C, and is meant to be the primary
user interface in the future.
- xP, the web frontend, relies on a protocol proxy written in Go,
and is meant for use on-the-go (no pun intended).
They are very much work-in-progress proofs of concept right now,
and the relay protocol is certain to change.
2022-08-08 04:39:20 +02:00
|
|
|
|
let dateMark = date.toLocaleDateString()
|
|
|
|
|
if (dateMark !== lastDateMark) {
|
|
|
|
|
lines.push(m('.date', {}, dateMark))
|
|
|
|
|
lastDateMark = dateMark
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lines.push(m('.time', {}, date.toLocaleTimeString()))
|
|
|
|
|
lines.push(m(Content, {}, line))
|
|
|
|
|
})
|
|
|
|
|
return m('.buffer-container', {}, [
|
|
|
|
|
m('.filler'),
|
|
|
|
|
m('.buffer', {}, lines),
|
|
|
|
|
])
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: This should be remembered across buffer switches,
|
|
|
|
|
// and we'll probably have to intercept /all/ key presses.
|
|
|
|
|
let Input = {
|
|
|
|
|
view: vnode => {
|
|
|
|
|
return m('textarea', {
|
|
|
|
|
rows: 1,
|
|
|
|
|
onkeydown: e => {
|
|
|
|
|
// TODO: And perhaps on other actions, too.
|
|
|
|
|
send({command: 'Active'})
|
|
|
|
|
if (e.keyCode !== 13)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
send({
|
|
|
|
|
command: 'BufferInput',
|
|
|
|
|
bufferName: bufferCurrent,
|
|
|
|
|
text: e.currentTarget.value,
|
|
|
|
|
})
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
e.currentTarget.value = ''
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let Main = {
|
|
|
|
|
view: vnode => {
|
|
|
|
|
return m('.xP', {}, [
|
|
|
|
|
m('.title', {}, "xP"),
|
|
|
|
|
m('.middle', {}, [m(BufferList), m(Buffer)]),
|
|
|
|
|
m('.status', {}, bufferCurrent),
|
|
|
|
|
m(Input),
|
|
|
|
|
])
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Buffer names should work as routes.
|
|
|
|
|
window.addEventListener('load', () => {
|
|
|
|
|
m.route(document.body, '/', {
|
|
|
|
|
'/': Main,
|
|
|
|
|
})
|
|
|
|
|
})
|