Compare commits

..

No commits in common. "b4ee523628b5e91ec06c6c8c61fe03507e1f412e" and "62773acaa099d7633a6d83d44b790f4f53fb274e" have entirely different histories.

4 changed files with 55 additions and 307 deletions

View File

@ -19,8 +19,8 @@ struct CommandMessage {
case HELLO: case HELLO:
u32 version; u32 version;
// If the version check succeeds, the client will receive // If the version check succeeds, the client will receive
// an initial stream of SERVER_UPDATE, BUFFER_UPDATE, BUFFER_STATS, // an initial stream of BUFFER_UPDATE, BUFFER_STATS, BUFFER_LINE,
// BUFFER_LINE, and finally a BUFFER_ACTIVATE message. // and finally a BUFFER_ACTIVATE message.
case ACTIVE: case ACTIVE:
void; void;
case BUFFER_INPUT: case BUFFER_INPUT:
@ -56,33 +56,14 @@ struct EventMessage {
BUFFER_ACTIVATE, BUFFER_ACTIVATE,
BUFFER_LINE, BUFFER_LINE,
BUFFER_CLEAR, BUFFER_CLEAR,
SERVER_UPDATE,
SERVER_RENAME,
SERVER_REMOVE,
ERROR, ERROR,
RESPONSE, RESPONSE,
} event) { } event) {
case PING: case PING:
void; void;
case BUFFER_UPDATE: case BUFFER_UPDATE:
string buffer_name; string buffer_name;
bool hide_unimportant; bool hide_unimportant;
union BufferContext switch (enum BufferKind {
GLOBAL,
SERVER,
CHANNEL,
PRIVATE_MESSAGE,
} kind) {
case GLOBAL:
void;
case SERVER:
string server_name;
case CHANNEL:
string server_name;
case PRIVATE_MESSAGE:
string server_name;
} context;
case BUFFER_STATS: case BUFFER_STATS:
string buffer_name; string buffer_name;
// These are cumulative, even for lines flushed out from buffers. // These are cumulative, even for lines flushed out from buffers.
@ -149,23 +130,6 @@ struct EventMessage {
case BUFFER_CLEAR: case BUFFER_CLEAR:
string buffer_name; string buffer_name;
case SERVER_UPDATE:
string server_name;
enum ServerState {
DISCONNECTED,
CONNECTING,
CONNECTED,
REGISTERED,
DISCONNECTING,
} state;
case SERVER_RENAME:
// Buffers aren't sent updates for in this circumstance,
// as that wouldn't be sufficiently atomic anyway.
string server_name;
string new;
case SERVER_REMOVE:
string server_name;
// Restriction: command_seq strictly follows the sequence received // Restriction: command_seq strictly follows the sequence received
// by the relay, across both of these replies. // by the relay, across both of these replies.
case ERROR: case ERROR:

161
xC.c
View File

@ -3109,28 +3109,6 @@ relay_prepare_buffer_update (struct app_context *ctx, struct buffer *buffer)
e->event = RELAY_EVENT_BUFFER_UPDATE; e->event = RELAY_EVENT_BUFFER_UPDATE;
e->buffer_name = str_from_cstr (buffer->name); e->buffer_name = str_from_cstr (buffer->name);
e->hide_unimportant = buffer->hide_unimportant; e->hide_unimportant = buffer->hide_unimportant;
struct str *server_name = NULL;
switch (buffer->type)
{
case BUFFER_GLOBAL:
e->context.kind = RELAY_BUFFER_KIND_GLOBAL;
break;
case BUFFER_SERVER:
e->context.kind = RELAY_BUFFER_KIND_SERVER;
server_name = &e->context.server.server_name;
break;
case BUFFER_CHANNEL:
e->context.kind = RELAY_BUFFER_KIND_CHANNEL;
server_name = &e->context.channel.server_name;
break;
case BUFFER_PM:
e->context.kind = RELAY_BUFFER_KIND_PRIVATE_MESSAGE;
server_name = &e->context.private_message.server_name;
break;
}
if (server_name)
*server_name = str_from_cstr (buffer->server->name);
} }
static void static void
@ -3270,51 +3248,6 @@ relay_prepare_buffer_clear (struct app_context *ctx,
e->buffer_name = str_from_cstr (buffer->name); e->buffer_name = str_from_cstr (buffer->name);
} }
enum relay_server_state
relay_server_state_for_server (struct server *s)
{
switch (s->state)
{
case IRC_DISCONNECTED: return RELAY_SERVER_STATE_DISCONNECTED;
case IRC_CONNECTING: return RELAY_SERVER_STATE_CONNECTING;
case IRC_CONNECTED: return RELAY_SERVER_STATE_CONNECTED;
case IRC_REGISTERED: return RELAY_SERVER_STATE_REGISTERED;
case IRC_CLOSING:
case IRC_HALF_CLOSED: return RELAY_SERVER_STATE_DISCONNECTING;
}
return 0;
}
static void
relay_prepare_server_update (struct app_context *ctx, struct server *s)
{
struct relay_event_message *m = relay_prepare (ctx);
struct relay_event_data_server_update *e = &m->data.server_update;
e->event = RELAY_EVENT_SERVER_UPDATE;
e->server_name = str_from_cstr (s->name);
e->state = relay_server_state_for_server (s);
}
static void
relay_prepare_server_rename (struct app_context *ctx, struct server *s,
const char *new_name)
{
struct relay_event_message *m = relay_prepare (ctx);
struct relay_event_data_server_rename *e = &m->data.server_rename;
e->event = RELAY_EVENT_SERVER_RENAME;
e->server_name = str_from_cstr (s->name);
e->new = str_from_cstr (new_name);
}
static void
relay_prepare_server_remove (struct app_context *ctx, struct server *s)
{
struct relay_event_message *m = relay_prepare (ctx);
struct relay_event_data_server_remove *e = &m->data.server_remove;
e->event = RELAY_EVENT_SERVER_REMOVE;
e->server_name = str_from_cstr (s->name);
}
static void static void
relay_prepare_error (struct app_context *ctx, uint32_t seq, const char *message) relay_prepare_error (struct app_context *ctx, uint32_t seq, const char *message)
{ {
@ -4957,14 +4890,14 @@ buffer_add (struct app_context *ctx, struct buffer *buffer)
{ {
hard_assert (!buffer_by_name (ctx, buffer->name)); hard_assert (!buffer_by_name (ctx, buffer->name));
relay_prepare_buffer_update (ctx, buffer);
relay_broadcast (ctx);
str_map_set (&ctx->buffers_by_name, buffer->name, buffer); str_map_set (&ctx->buffers_by_name, buffer->name, buffer);
LIST_APPEND_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer); LIST_APPEND_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
buffer_open_log_file (ctx, buffer); buffer_open_log_file (ctx, buffer);
relay_prepare_buffer_update (ctx, buffer);
relay_broadcast (ctx);
// Normally this doesn't cause changes in the prompt but a prompt hook // Normally this doesn't cause changes in the prompt but a prompt hook
// could decide to show some information for all buffers nonetheless // could decide to show some information for all buffers nonetheless
refresh_prompt (ctx); refresh_prompt (ctx);
@ -4976,9 +4909,6 @@ buffer_remove (struct app_context *ctx, struct buffer *buffer)
hard_assert (buffer != ctx->current_buffer); hard_assert (buffer != ctx->current_buffer);
hard_assert (buffer != ctx->global_buffer); hard_assert (buffer != ctx->global_buffer);
relay_prepare_buffer_remove (ctx, buffer);
relay_broadcast (ctx);
CALL_ (ctx->input, buffer_destroy, buffer->input_data); CALL_ (ctx->input, buffer_destroy, buffer->input_data);
buffer->input_data = NULL; buffer->input_data = NULL;
@ -4994,6 +4924,9 @@ buffer_remove (struct app_context *ctx, struct buffer *buffer)
if (buffer->type == BUFFER_SERVER) if (buffer->type == BUFFER_SERVER)
buffer->server->buffer = NULL; buffer->server->buffer = NULL;
relay_prepare_buffer_remove (ctx, buffer);
relay_broadcast (ctx);
str_map_set (&ctx->buffers_by_name, buffer->name, NULL); str_map_set (&ctx->buffers_by_name, buffer->name, NULL);
LIST_UNLINK_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer); LIST_UNLINK_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
buffer_unref (buffer); buffer_unref (buffer);
@ -5107,9 +5040,6 @@ buffer_activate (struct app_context *ctx, struct buffer *buffer)
if (ctx->current_buffer == buffer) if (ctx->current_buffer == buffer)
return; return;
relay_prepare_buffer_activate (ctx, buffer);
relay_broadcast (ctx);
// This is the only place where the unread messages marker // This is the only place where the unread messages marker
// and highlight indicator are reset // and highlight indicator are reset
if (ctx->current_buffer) if (ctx->current_buffer)
@ -5126,6 +5056,9 @@ buffer_activate (struct app_context *ctx, struct buffer *buffer)
ctx->last_buffer = ctx->current_buffer; ctx->last_buffer = ctx->current_buffer;
ctx->current_buffer = buffer; ctx->current_buffer = buffer;
relay_prepare_buffer_activate (ctx, buffer);
relay_broadcast (ctx);
refresh_prompt (ctx); refresh_prompt (ctx);
} }
@ -5213,14 +5146,14 @@ buffer_rename (struct app_context *ctx,
static void static void
buffer_clear (struct app_context *ctx, struct buffer *buffer) buffer_clear (struct app_context *ctx, struct buffer *buffer)
{ {
relay_prepare_buffer_clear (ctx, buffer);
relay_broadcast (ctx);
LIST_FOR_EACH (struct buffer_line, iter, buffer->lines) LIST_FOR_EACH (struct buffer_line, iter, buffer->lines)
buffer_line_destroy (iter); buffer_line_destroy (iter);
buffer->lines = buffer->lines_tail = NULL; buffer->lines = buffer->lines_tail = NULL;
buffer->lines_count = 0; buffer->lines_count = 0;
relay_prepare_buffer_clear (ctx, buffer);
relay_broadcast (ctx);
} }
static struct buffer * static struct buffer *
@ -5732,17 +5665,6 @@ irc_send (struct server *s, const char *format, ...)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
irc_set_state (struct server *s, enum server_state state)
{
s->state = state;
relay_prepare_server_update (s->ctx, s);
relay_broadcast (s->ctx);
refresh_prompt (s->ctx);
}
static void static void
irc_real_shutdown (struct server *s) irc_real_shutdown (struct server *s)
{ {
@ -5758,7 +5680,7 @@ irc_real_shutdown (struct server *s)
if (!soft_assert (errno == EINTR)) if (!soft_assert (errno == EINTR))
break; break;
irc_set_state (s, IRC_HALF_CLOSED); s->state = IRC_HALF_CLOSED;
} }
static void static void
@ -5769,7 +5691,7 @@ irc_shutdown (struct server *s)
return; return;
// TODO: set a timer to cut the connection if we don't receive an EOF // TODO: set a timer to cut the connection if we don't receive an EOF
irc_set_state (s, IRC_CLOSING); s->state = IRC_CLOSING;
// Either there's still some data in the write buffer and we wait // Either there's still some data in the write buffer and we wait
// until they're sent, or we send an EOF to the server right away // until they're sent, or we send an EOF to the server right away
@ -5791,7 +5713,7 @@ irc_destroy_connector (struct server *s)
s->socks_conn = NULL; s->socks_conn = NULL;
// Not connecting anymore // Not connecting anymore
irc_set_state (s, IRC_DISCONNECTED); s->state = IRC_DISCONNECTED;
} }
static void static void
@ -5822,7 +5744,7 @@ irc_destroy_transport (struct server *s)
poller_fd_reset (&s->socket_event); poller_fd_reset (&s->socket_event);
xclose (s->socket); xclose (s->socket);
s->socket = -1; s->socket = -1;
irc_set_state (s, IRC_DISCONNECTED); s->state = IRC_DISCONNECTED;
str_reset (&s->read_buffer); str_reset (&s->read_buffer);
str_reset (&s->write_buffer); str_reset (&s->write_buffer);
@ -5883,6 +5805,8 @@ irc_disconnect (struct server *s)
s->reconnect_attempt = 0; s->reconnect_attempt = 0;
irc_queue_reconnect (s); irc_queue_reconnect (s);
} }
refresh_prompt (s->ctx);
} }
static void static void
@ -6634,7 +6558,7 @@ irc_finish_connection (struct server *s, int socket, const char *hostname)
} }
log_server_status (s, s->buffer, "Connection established"); log_server_status (s, s->buffer, "Connection established");
irc_set_state (s, IRC_CONNECTED); s->state = IRC_CONNECTED;
s->socket_event = poller_fd_make (&ctx->poller, s->socket); s->socket_event = poller_fd_make (&ctx->poller, s->socket);
s->socket_event.dispatcher = (poller_fd_fn) on_irc_ready; s->socket_event.dispatcher = (poller_fd_fn) on_irc_ready;
@ -6643,6 +6567,8 @@ irc_finish_connection (struct server *s, int socket, const char *hostname)
irc_update_poller (s, NULL); irc_update_poller (s, NULL);
irc_reset_connection_timeouts (s); irc_reset_connection_timeouts (s);
irc_register (s); irc_register (s);
refresh_prompt (s->ctx);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -6789,7 +6715,7 @@ irc_initiate_connect (struct server *s)
irc_queue_reconnect (s); irc_queue_reconnect (s);
} }
else if (s->state != IRC_CONNECTED) else if (s->state != IRC_CONNECTED)
irc_set_state (s, IRC_CONNECTING); s->state = IRC_CONNECTING;
} }
// --- Input prompt ------------------------------------------------------------ // --- Input prompt ------------------------------------------------------------
@ -8337,7 +8263,8 @@ irc_on_registered (struct server *s, const char *nickname)
str_reset (&s->irc_user_mode); str_reset (&s->irc_user_mode);
cstr_set (&s->irc_user_host, NULL); cstr_set (&s->irc_user_host, NULL);
irc_set_state (s, IRC_REGISTERED); s->state = IRC_REGISTERED;
refresh_prompt (s->ctx);
// XXX: we can also use WHOIS if it's not supported (optional by RFC 2812) // XXX: we can also use WHOIS if it's not supported (optional by RFC 2812)
// TODO: maybe rather always use RPL_ISUPPORT NICKLEN & USERLEN & HOSTLEN // TODO: maybe rather always use RPL_ISUPPORT NICKLEN & USERLEN & HOSTLEN
@ -9495,9 +9422,6 @@ server_remove (struct app_context *ctx, struct server *s)
if (s->buffer) if (s->buffer)
buffer_remove_safe (ctx, s->buffer); buffer_remove_safe (ctx, s->buffer);
relay_prepare_server_remove (ctx, s);
relay_broadcast (ctx);
struct str_map_unset_iter iter = struct str_map_unset_iter iter =
str_map_unset_iter_make (&s->irc_buffer_map); str_map_unset_iter_make (&s->irc_buffer_map);
struct buffer *buffer; struct buffer *buffer;
@ -9521,10 +9445,6 @@ static void
server_rename (struct app_context *ctx, struct server *s, const char *new_name) server_rename (struct app_context *ctx, struct server *s, const char *new_name)
{ {
hard_assert (!str_map_find (&ctx->servers, new_name)); hard_assert (!str_map_find (&ctx->servers, new_name));
relay_prepare_server_rename (ctx, s, new_name);
relay_broadcast (ctx);
str_map_set (&ctx->servers, new_name, str_map_set (&ctx->servers, new_name,
str_map_steal (&ctx->servers, s->name)); str_map_steal (&ctx->servers, s->name));
@ -10020,27 +9940,20 @@ lua_server_gc (lua_State *L)
return lua_weak_gc (L, &lua_server_info); return lua_weak_gc (L, &lua_server_info);
} }
static const char *
lua_server_state_to_string (enum server_state state)
{
switch (state)
{
case IRC_DISCONNECTED: return "disconnected";
case IRC_CONNECTING: return "connecting";
case IRC_CONNECTED: return "connected";
case IRC_REGISTERED: return "registered";
case IRC_CLOSING: return "closing";
case IRC_HALF_CLOSED: return "half-closed";
}
return "?";
}
static int static int
lua_server_get_state (lua_State *L) lua_server_get_state (lua_State *L)
{ {
struct lua_weak *wrapper = lua_weak_deref (L, &lua_server_info); struct lua_weak *wrapper = lua_weak_deref (L, &lua_server_info);
struct server *server = wrapper->object; struct server *server = wrapper->object;
lua_pushstring (L, lua_server_state_to_string (server->state)); switch (server->state)
{
case IRC_DISCONNECTED: lua_pushstring (L, "disconnected"); break;
case IRC_CONNECTING: lua_pushstring (L, "connecting"); break;
case IRC_CONNECTED: lua_pushstring (L, "connected"); break;
case IRC_REGISTERED: lua_pushstring (L, "registered"); break;
case IRC_CLOSING: lua_pushstring (L, "closing"); break;
case IRC_HALF_CLOSED: lua_pushstring (L, "half_closed"); break;
}
return 1; return 1;
} }
@ -15399,14 +15312,6 @@ init_poller_events (struct app_context *ctx)
static void static void
client_resync (struct client *c) client_resync (struct client *c)
{ {
struct str_map_iter iter = str_map_iter_make (&c->ctx->servers);
struct server *s;
while ((s = str_map_iter_next (&iter)))
{
relay_prepare_server_update (c->ctx, s);
relay_send (c);
}
LIST_FOR_EACH (struct buffer, buffer, c->ctx->buffers) LIST_FOR_EACH (struct buffer, buffer, c->ctx->buffers)
{ {
relay_prepare_buffer_update (c->ctx, buffer); relay_prepare_buffer_update (c->ctx, buffer);

View File

@ -17,10 +17,6 @@ body {
padding: .05em .3em; padding: .05em .3em;
background: #eee; background: #eee;
display: flex;
justify-content: space-between;
align-items: baseline;
position: relative; position: relative;
border-top: 3px solid #ccc; border-top: 3px solid #ccc;
border-bottom: 2px solid #888; border-bottom: 2px solid #888;
@ -43,8 +39,10 @@ body {
bottom: -1px; bottom: -1px;
background: #ccc; background: #ccc;
} }
button { .title {
font: inherit; display: flex;
justify-content: space-between;
align-items: baseline;
} }
.middle { .middle {
@ -114,8 +112,9 @@ button {
font-weight: bold; font-weight: bold;
} }
.unread { .unread {
height: 1px;
grid-column: span 2; grid-column: span 2;
border-top: 1px solid #ff5f00; background: #ff5f00;
} }
.time { .time {
padding: .1em .3em; padding: .1em .3em;
@ -123,13 +122,6 @@ button {
color: #bbb; color: #bbb;
border-right: 1px solid #ccc; border-right: 1px solid #ccc;
} }
.time.hidden:after {
border-top: .2em dotted #ccc;
display: block;
width: 50%;
margin: 0 auto;
content: "";
}
.mark { .mark {
padding-right: .3em; padding-right: .3em;
text-align: center; text-align: center;

View File

@ -129,9 +129,6 @@ class RelayRpc extends EventTarget {
// ---- Utilities -------------------------------------------------------------- // ---- Utilities --------------------------------------------------------------
function utf8Encode(s) { return new TextEncoder().encode(s) }
function utf8Decode(s) { return new TextDecoder().decode(s) }
// On macOS, the Alt/Option key transforms characters, which basically breaks // On macOS, the Alt/Option key transforms characters, which basically breaks
// all event.altKey shortcuts, so require combining them with Control as well // all event.altKey shortcuts, so require combining them with Control as well
// on that system. // on that system.
@ -156,33 +153,6 @@ function beep() {
oscillator.stop(audioContext.currentTime + 0.1) oscillator.stop(audioContext.currentTime + 0.1)
} }
let iconLink = undefined
let iconState = undefined
function updateIcon(highlighted) {
if (iconState === highlighted)
return
iconState = highlighted
let canvas = document.createElement('canvas')
canvas.width = 32
canvas.height = 32
let ctx = canvas.getContext('2d')
ctx.arc(16, 16, 12, 0, 2 * Math.PI)
ctx.fillStyle = highlighted ? '#ff5f00' : '#ccc'
ctx.fill()
if (iconLink === undefined) {
iconLink = document.createElement('link')
iconLink.type = 'image/png'
iconLink.rel = 'icon'
document.getElementsByTagName('head')[0].appendChild(iconLink)
}
iconLink.href = canvas.toDataURL();
}
// ---- Event processing ------------------------------------------------------- // ---- Event processing -------------------------------------------------------
let rpc = new RelayRpc(proxy) let rpc = new RelayRpc(proxy)
@ -193,8 +163,6 @@ let bufferCurrent = undefined
let bufferLog = undefined let bufferLog = undefined
let bufferAutoscroll = true let bufferAutoscroll = true
let servers = new Map()
function bufferResetStats(b) { function bufferResetStats(b) {
b.newMessages = 0 b.newMessages = 0
b.newUnimportantMessages = 0 b.newUnimportantMessages = 0
@ -236,8 +204,6 @@ rpc.connect().then(result => {
bufferLog = undefined bufferLog = undefined
bufferAutoscroll = true bufferAutoscroll = true
servers.clear()
rpc.send({command: 'Hello', version: 1}) rpc.send({command: 'Hello', version: 1})
connecting = false connecting = false
m.redraw() m.redraw()
@ -254,8 +220,6 @@ rpc.addEventListener('Ping', event => {
rpc.send({command: 'PingResponse', eventSeq: event.detail.eventSeq}) rpc.send({command: 'PingResponse', eventSeq: event.detail.eventSeq})
}) })
// ~~~ Buffer events ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rpc.addEventListener('BufferUpdate', event => { rpc.addEventListener('BufferUpdate', event => {
let e = event.detail, b = buffers.get(e.bufferName) let e = event.detail, b = buffers.get(e.bufferName)
if (b === undefined) { if (b === undefined) {
@ -266,10 +230,7 @@ rpc.addEventListener('BufferUpdate', event => {
})) }))
bufferResetStats(b) bufferResetStats(b)
} }
b.hideUnimportant = e.hideUnimportant b.hideUnimportant = e.hideUnimportant
b.kind = e.context.kind
b.server = servers.get(e.context.serverName)
}) })
rpc.addEventListener('BufferStats', event => { rpc.addEventListener('BufferStats', event => {
@ -363,8 +324,9 @@ rpc.addEventListener('BufferLine', event => {
} }
} }
if (line.isHighlight || // TODO: Find some way of highlighting the tab in a browser.
(!visible && b.kind === 'PrivateMessage' && !line.isUnimportant)) { // TODO: Also highlight on unseen private messages, like xC does.
if (line.isHighlight) {
beep() beep()
if (!visible) if (!visible)
b.highlighted = true b.highlighted = true
@ -377,26 +339,6 @@ rpc.addEventListener('BufferClear', event => {
b.lines.length = 0 b.lines.length = 0
}) })
// ~~~ Server events ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rpc.addEventListener('ServerUpdate', event => {
let e = event.detail, s = servers.get(e.serverName)
if (s === undefined)
servers.set(e.serverName, (s = {}))
s.state = e.state
})
rpc.addEventListener('ServerRename', event => {
let e = event.detail
servers.set(e.new, servers.get(e.serverName))
servers.delete(e.serverName)
})
rpc.addEventListener('ServerRemove', event => {
let e = event.detail
servers.delete(e.serverName)
})
// --- Colours ----------------------------------------------------------------- // --- Colours -----------------------------------------------------------------
let palette = [ let palette = [
@ -441,16 +383,13 @@ let Toolbar = {
let BufferList = { let BufferList = {
view: vnode => { view: vnode => {
let highlighted = false
let items = Array.from(buffers, ([name, b]) => { let items = Array.from(buffers, ([name, b]) => {
let classes = [], displayName = name let classes = [], displayName = name
if (name == bufferCurrent) { if (name == bufferCurrent) {
classes.push('current') classes.push('current')
} else { } else {
if (b.highlighted) { if (b.highlighted)
classes.push('highlighted') classes.push('highlighted')
highlighted = true
}
if (b.newMessages) { if (b.newMessages) {
classes.push('activity') classes.push('activity')
displayName += ` (${b.newMessages})` displayName += ` (${b.newMessages})`
@ -461,8 +400,6 @@ let BufferList = {
class: classes.join(' '), class: classes.join(' '),
}, displayName) }, displayName)
}) })
updateIcon(highlighted)
return m('.list', {}, items) return m('.list', {}, items)
}, },
} }
@ -580,20 +517,13 @@ let Buffer = {
return m('.buffer') return m('.buffer')
let lastDateMark = undefined let lastDateMark = undefined
let squashing = false
let markBefore = b.lines.length let markBefore = b.lines.length
- b.newMessages - b.newUnimportantMessages - b.newMessages - b.newUnimportantMessages
b.lines.forEach((line, i) => { b.lines.forEach((line, i) => {
if (i == markBefore) if (i == markBefore)
lines.push(m('.unread')) lines.push(m('.unread'))
if (line.isUnimportant && b.hideUnimportant)
if (!line.isUnimportant || !b.hideUnimportant) {
squashing = false
} else if (squashing) {
return return
} else {
squashing = true
}
let date = new Date(line.when) let date = new Date(line.when)
let dateMark = date.toLocaleDateString() let dateMark = date.toLocaleDateString()
@ -601,11 +531,6 @@ let Buffer = {
lines.push(m('.date', {}, dateMark)) lines.push(m('.date', {}, dateMark))
lastDateMark = dateMark lastDateMark = dateMark
} }
if (squashing) {
lines.push(m('.time.hidden'))
lines.push(m('.content'))
return
}
let attrs = {} let attrs = {}
if (line.leaked) if (line.leaked)
@ -655,21 +580,6 @@ let BufferContainer = {
}, },
} }
let Status = {
view: vnode => {
let b = buffers.get(bufferCurrent)
if (b === undefined)
return m('.status', {}, 'Synchronizing...')
let status = `${bufferCurrent}`
if (b.hideUnimportant)
status += `<H>`
if (b.server !== undefined)
status += ` (${b.server.state})`
return m('.status', {}, status)
},
}
let Input = { let Input = {
counter: 0, counter: 0,
stamp: textarea => { stamp: textarea => {
@ -688,22 +598,15 @@ let Input = {
command: 'BufferComplete', command: 'BufferComplete',
bufferName: bufferCurrent, bufferName: bufferCurrent,
text: textarea.value, text: textarea.value,
position: utf8Encode( position: textarea.selectionEnd,
textarea.value.slice(0, textarea.selectionEnd)).length,
}).then(resp => { }).then(resp => {
if (!Input.stamp(textarea).every((v, k) => v === state[k])) if (!Input.stamp(textarea).every((v, k) => v === state[k]))
return return
let preceding = utf8Encode(textarea.value).slice(0, resp.start)
let start = utf8Decode(preceding).length
// TODO: Somehow display remaining options, or cycle through. // TODO: Somehow display remaining options, or cycle through.
if (resp.completions.length) { if (resp.completions.length)
textarea.setRangeText(resp.completions[0], textarea.setRangeText(resp.completions[0],
start, textarea.selectionEnd, 'end') resp.start, textarea.selectionEnd, 'end')
} else {
beep()
}
if (resp.completions.length === 1) if (resp.completions.length === 1)
textarea.setRangeText(' ', textarea.setRangeText(' ',
textarea.selectionStart, textarea.selectionEnd, 'end') textarea.selectionStart, textarea.selectionEnd, 'end')
@ -805,7 +708,8 @@ let Main = {
return m('.xP', {}, [ return m('.xP', {}, [
m('.title', {}, [`xP (${state})`, m(Toolbar)]), m('.title', {}, [`xP (${state})`, m(Toolbar)]),
m('.middle', {}, [m(BufferList), m(BufferContainer)]), m('.middle', {}, [m(BufferList), m(BufferContainer)]),
m(Status), // TODO: Indicate hideUnimportant.
m('.status', {}, bufferCurrent),
m(Input), m(Input),
]) ])
}, },
@ -817,8 +721,11 @@ document.addEventListener('keydown', event => {
if (rpc.ws == undefined || !hasShortcutModifiers(event)) if (rpc.ws == undefined || !hasShortcutModifiers(event))
return return
let names = undefined
switch (event.key) { switch (event.key) {
case 'Tab':
if (bufferLast !== undefined)
bufferActivate(bufferLast)
break
case 'h': case 'h':
bufferToggleLog() bufferToggleLog()
break break
@ -836,26 +743,6 @@ document.addEventListener('keydown', event => {
break break
} }
break break
case 'Tab':
if (bufferLast !== undefined)
bufferActivate(bufferLast)
break
case 'PageUp':
names = [...buffers.keys()]
for (let i = 0; i < names.length; i++)
if (names[i] === bufferCurrent) {
bufferActivate(names.at(--i))
break
}
break
case 'PageDown':
names = [...buffers.keys()]
for (let i = 0; i < names.length; i++)
if (names[i] === bufferCurrent) {
bufferActivate(names.at(++i) || names[0])
break
}
break
default: default:
return return
} }