@@ -17,6 +17,10 @@ function bareIrcCreateTuiApp(ctx, client) {
|
||||
height: size0.height || 24,
|
||||
help: false,
|
||||
quitConfirm: false,
|
||||
roomsOpen: false,
|
||||
roomsCursor: 0,
|
||||
roomsFilterEdit: false,
|
||||
openedRoomsOnce: false,
|
||||
filterOpen: false,
|
||||
status: 'connecting…',
|
||||
input: tui.textinput.create({ prompt: '', focused: true, charLimit: 450 }),
|
||||
@@ -38,10 +42,6 @@ function bareIrcCreateTuiApp(ctx, client) {
|
||||
if (tui && typeof tui.send === 'function')
|
||||
tui.send({ type: 'irc.paint' })
|
||||
})
|
||||
client.on('join', function () {
|
||||
if (tui && typeof tui.send === 'function')
|
||||
tui.send({ type: 'irc.paint' })
|
||||
})
|
||||
client.on('sasl', function (p) {
|
||||
self.status = p && p.ok ? 'sasl ok' : 'sasl failed'
|
||||
if (tui && typeof tui.send === 'function')
|
||||
@@ -59,9 +59,67 @@ function bareIrcCreateTuiApp(ctx, client) {
|
||||
if (tui && typeof tui.send === 'function')
|
||||
tui.send({ type: 'irc.paint' })
|
||||
})
|
||||
client.on('join', function () {
|
||||
self.roomsOpen = false
|
||||
self.roomsFilterEdit = false
|
||||
if (tui && typeof tui.send === 'function')
|
||||
tui.send({ type: 'irc.paint' })
|
||||
})
|
||||
client.on('list', function () {
|
||||
if (tui && typeof tui.send === 'function')
|
||||
tui.send({ type: 'irc.paint' })
|
||||
})
|
||||
client.on('listend', function (p) {
|
||||
self.status = 'rooms ' + ((p && p.count) || 0)
|
||||
if (tui && typeof tui.send === 'function')
|
||||
tui.send({ type: 'irc.paint' })
|
||||
})
|
||||
client.on('motd_end', function () {
|
||||
var joins = (client.opts && client.opts.autojoin) || []
|
||||
if (!self.openedRoomsOnce && (!joins || !joins.length)) {
|
||||
self.openedRoomsOnce = true
|
||||
self.roomsOpen = true
|
||||
self.roomsCursor = 0
|
||||
self.status = 'pick a room (Enter join / filter Esc close)'
|
||||
}
|
||||
var srv = bareIrcEnsureBuffer(client.state, 'server', '*server')
|
||||
bareIrcPushLine(client.state, srv, {
|
||||
atMs: Date.now(),
|
||||
kind: 'info',
|
||||
text:
|
||||
'You are ' +
|
||||
(client.state.nick || 'guest') +
|
||||
'. /rooms browse /join #libera help /query nick ? keys'
|
||||
})
|
||||
if (tui && typeof tui.send === 'function')
|
||||
tui.send({ type: 'irc.paint' })
|
||||
})
|
||||
this._sync()
|
||||
return null
|
||||
},
|
||||
_roomRows: function () {
|
||||
var listed = this.client.state.channelList || []
|
||||
if (listed.length) return listed
|
||||
return BARE_IRC_FEATURED_ROOMS
|
||||
},
|
||||
_openRooms: function (query) {
|
||||
this.roomsOpen = true
|
||||
this.roomsCursor = 0
|
||||
this.roomsFilterEdit = false
|
||||
if (query) this.client.list(query)
|
||||
this.status = query
|
||||
? 'listing ' + query
|
||||
: 'featured rooms (type /rooms linux to search)'
|
||||
},
|
||||
_joinRoomAtCursor: function () {
|
||||
var rows = this._roomRows()
|
||||
var hit = rows[this.roomsCursor]
|
||||
if (!hit || !hit.name) return
|
||||
this.roomsOpen = false
|
||||
this.roomsFilterEdit = false
|
||||
this.client.join(hit.name)
|
||||
this.status = 'joining ' + hit.name
|
||||
},
|
||||
_current: function () {
|
||||
var st = this.client.state
|
||||
return (
|
||||
@@ -73,9 +131,42 @@ function bareIrcCreateTuiApp(ctx, client) {
|
||||
buf.unread = 0
|
||||
buf.highlight = false
|
||||
var cols = Math.max(40, this.width || 80)
|
||||
var nickW = cols >= 100 ? 16 : 0
|
||||
var nickW = !this.roomsOpen && cols >= 100 ? 16 : 0
|
||||
this.viewport.width = Math.max(20, cols - nickW - 2)
|
||||
this.viewport.height = Math.max(4, (this.height || 24) - 6)
|
||||
if (this.roomsOpen) {
|
||||
var rows = this._roomRows()
|
||||
if (this.roomsCursor >= rows.length)
|
||||
this.roomsCursor = Math.max(0, rows.length - 1)
|
||||
var rlines = []
|
||||
var ri
|
||||
for (ri = 0; ri < rows.length; ri++) {
|
||||
var rr = rows[ri]
|
||||
var mark = ri === this.roomsCursor ? '>' : ' '
|
||||
var users =
|
||||
rr.users === '' || rr.users == null
|
||||
? ' '
|
||||
: String(rr.users).padStart
|
||||
? String(rr.users).padStart(4, ' ')
|
||||
: String(rr.users)
|
||||
rlines.push(
|
||||
mark +
|
||||
' ' +
|
||||
String(rr.name || '')
|
||||
.slice(0, 18)
|
||||
.padEnd(18, ' ') +
|
||||
' ' +
|
||||
users +
|
||||
' ' +
|
||||
String(rr.topic || '')
|
||||
)
|
||||
}
|
||||
if (!rlines.length) rlines.push('(no rooms — /rooms linux to search)')
|
||||
this.viewport.setContent(rlines.join('\n'))
|
||||
var mid = Math.floor((this.viewport.height || 8) / 2)
|
||||
this.viewport.setYOffset(Math.max(0, this.roomsCursor - mid))
|
||||
return
|
||||
}
|
||||
var lines = []
|
||||
for (var i = 0; i < buf.lines.length; i++) {
|
||||
var rec = buf.lines[i]
|
||||
@@ -128,86 +219,55 @@ function bareIrcCreateTuiApp(ctx, client) {
|
||||
if (!hit) return
|
||||
this.input.setValue(val.slice(0, val.length - prefix.length) + hit)
|
||||
},
|
||||
_runSlash: function (line) {
|
||||
var parts = line.slice(1).split(/\s+/)
|
||||
var cmd = (parts[0] || '').toLowerCase()
|
||||
var rest = line.slice(1 + (parts[0] || '').length).trim()
|
||||
_applySlash: function (res) {
|
||||
var buf = this._current()
|
||||
if (cmd === 'join' && parts[1]) this.client.join(parts[1], parts[2])
|
||||
else if (cmd === 'part')
|
||||
this.client.part(parts[1] || buf.name, rest.replace(/^\S+\s*/, ''))
|
||||
else if (cmd === 'query' && parts[1]) {
|
||||
var q = bareIrcEnsureBuffer(this.client.state, 'query', parts[1])
|
||||
this.client.state.currentId = q.id
|
||||
if (parts.slice(2).join(' ')) {
|
||||
this.client.sendPrivmsg(parts[1], parts.slice(2).join(' '))
|
||||
}
|
||||
} else if ((cmd === 'msg' || cmd === 'privmsg') && parts[1]) {
|
||||
this.client.sendPrivmsg(parts[1], parts.slice(2).join(' '))
|
||||
} else if (cmd === 'notice' && parts[1]) {
|
||||
this.client.sendRaw('NOTICE', [parts[1], parts.slice(2).join(' ')])
|
||||
} else if (cmd === 'nick' && parts[1])
|
||||
this.client.sendRaw('NICK', [parts[1]])
|
||||
else if (cmd === 'whois' && parts[1])
|
||||
this.client.sendRaw('WHOIS', [parts[1]])
|
||||
else if (cmd === 'who' && parts[1]) this.client.sendRaw('WHO', [parts[1]])
|
||||
else if (cmd === 'topic') {
|
||||
if (rest) this.client.sendRaw('TOPIC', [buf.name, rest])
|
||||
else this.client.sendRaw('TOPIC', [buf.name])
|
||||
} else if (cmd === 'me' && rest) {
|
||||
this.client.sendPrivmsg(buf.name, '\x01ACTION ' + rest + '\x01')
|
||||
} else if (cmd === 'away') this.client.sendRaw('AWAY', rest ? [rest] : [])
|
||||
else if (cmd === 'quit') {
|
||||
this.client.quit(rest || 'Leaving')
|
||||
return tui.quit
|
||||
} else if (cmd === 'quote' && rest) {
|
||||
var qparts = rest.split(/\s+/)
|
||||
this.client.sendRaw(qparts[0], qparts.slice(1))
|
||||
} else if (cmd === 'disconnect') this.client.quit('Disconnect')
|
||||
else if (cmd === 'reconnect') {
|
||||
try {
|
||||
if (this.client.opts) {
|
||||
this.client.attach(bareIrcDial(ctx, this.client.opts))
|
||||
this.status = 'reconnecting\u2026'
|
||||
}
|
||||
} catch (err) {
|
||||
this.status = 'reconnect failed'
|
||||
}
|
||||
} else if (cmd === 'win' || cmd === 'buffer') {
|
||||
var n = parseInt(parts[1], 10)
|
||||
var list = bareIrcListBuffers(this.client.state)
|
||||
if (n >= 1 && n <= list.length) {
|
||||
this.client.state.currentId = list[n - 1].id
|
||||
}
|
||||
} else if (cmd === 'p2p' && parts[1] === 'open' && parts[2]) {
|
||||
var name = String(parts[2])
|
||||
if (name.indexOf('[p2p]') < 0) name = name + ' [p2p]'
|
||||
var pb = bareIrcEnsureBuffer(this.client.state, 'p2p', name)
|
||||
this.client.state.currentId = pb.id
|
||||
bareIrcPushLine(this.client.state, pb, {
|
||||
atMs: Date.now(),
|
||||
kind: 'info',
|
||||
text: 'Shadow room — not Libera. Messages stay on the swarm sideband.'
|
||||
})
|
||||
if (typeof ctx.bareOsIrcShadowOpen === 'function') {
|
||||
try {
|
||||
ctx.bareOsIrcShadowOpen(parts[2])
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
} else if (cmd === 'help') {
|
||||
this.help = true
|
||||
} else {
|
||||
if (res.help) this.help = true
|
||||
if (res.rooms) this._openRooms(res.roomsQuery || '')
|
||||
if (res.note) {
|
||||
bareIrcPushLine(this.client.state, buf, {
|
||||
atMs: Date.now(),
|
||||
kind: 'info',
|
||||
text: 'unknown command /' + cmd
|
||||
text: res.note
|
||||
})
|
||||
}
|
||||
if (res.usage) {
|
||||
bareIrcPushLine(this.client.state, buf, {
|
||||
atMs: Date.now(),
|
||||
kind: 'info',
|
||||
text: res.usage
|
||||
})
|
||||
}
|
||||
if (res.clear && buf) buf.lines = []
|
||||
if (res.close) {
|
||||
if (buf.kind === 'channel') this.client.part(buf.name, '')
|
||||
var id = buf.id
|
||||
if (id !== 'server') {
|
||||
delete this.client.state.buffers[id]
|
||||
this.client.state.currentId = 'server'
|
||||
}
|
||||
}
|
||||
if (res.quit) return tui.quit
|
||||
this._sync()
|
||||
return null
|
||||
},
|
||||
_runSlash: function (line) {
|
||||
var self = this
|
||||
var res = bareIrcRunSlash(this.client, line, {
|
||||
buf: this._current(),
|
||||
ctx: ctx,
|
||||
reconnect: function () {
|
||||
try {
|
||||
if (self.client.opts) {
|
||||
self.client.attach(bareIrcDial(ctx, self.client.opts))
|
||||
self.status = 'reconnecting\u2026'
|
||||
}
|
||||
} catch (err) {
|
||||
self.status = 'reconnect failed'
|
||||
}
|
||||
}
|
||||
})
|
||||
return this._applySlash(res)
|
||||
},
|
||||
update: function (msg) {
|
||||
if (msg && msg.type === 'resize') {
|
||||
this.width = msg.width || this.width
|
||||
@@ -228,6 +288,60 @@ function bareIrcCreateTuiApp(ctx, client) {
|
||||
if (msg && msg.type === 'key') this.help = false
|
||||
return [this, null]
|
||||
}
|
||||
if (this.roomsOpen) {
|
||||
if (this.roomsFilterEdit) {
|
||||
if (tui.key.matches(msg, 'escape')) {
|
||||
this.roomsFilterEdit = false
|
||||
this.input.reset()
|
||||
return [this, null]
|
||||
}
|
||||
if (tui.key.matches(msg, 'enter')) {
|
||||
var q = String(this.input.value || '').trim()
|
||||
this.input.reset()
|
||||
this.roomsFilterEdit = false
|
||||
if (q) this.client.list(q)
|
||||
this.status = q ? 'listing ' + q : 'rooms'
|
||||
this._sync()
|
||||
return [this, null]
|
||||
}
|
||||
var rpair = this.input.update(msg)
|
||||
this.input = rpair[0]
|
||||
return [this, rpair[1]]
|
||||
}
|
||||
if (tui.key.matches(msg, 'escape', 'q')) {
|
||||
this.roomsOpen = false
|
||||
this._sync()
|
||||
return [this, null]
|
||||
}
|
||||
if (tui.key.matches(msg, '/', 'f')) {
|
||||
this.roomsFilterEdit = true
|
||||
this.input.reset()
|
||||
return [this, null]
|
||||
}
|
||||
if (tui.key.matches(msg, 'enter')) {
|
||||
this._joinRoomAtCursor()
|
||||
this._sync()
|
||||
return [this, null]
|
||||
}
|
||||
if (tui.key.matches(msg, 'j', 'down')) {
|
||||
this.roomsCursor++
|
||||
this._sync()
|
||||
return [this, null]
|
||||
}
|
||||
if (tui.key.matches(msg, 'k', 'up')) {
|
||||
this.roomsCursor = Math.max(0, this.roomsCursor - 1)
|
||||
this._sync()
|
||||
return [this, null]
|
||||
}
|
||||
if (tui.key.matches(msg, 'pageup', 'pagedown')) {
|
||||
var step = this.viewport.height || 10
|
||||
if (tui.key.matches(msg, 'pageup'))
|
||||
this.roomsCursor = Math.max(0, this.roomsCursor - step)
|
||||
else this.roomsCursor += step
|
||||
this._sync()
|
||||
return [this, null]
|
||||
}
|
||||
}
|
||||
if (this.quitConfirm) {
|
||||
if (tui.key.matches(msg, 'y', 'Y')) {
|
||||
this.client.quit('Quit')
|
||||
@@ -248,6 +362,11 @@ function bareIrcCreateTuiApp(ctx, client) {
|
||||
this.help = true
|
||||
return [this, null]
|
||||
}
|
||||
if (tui.key.matches(msg, 'ctrl+l')) {
|
||||
this._openRooms('')
|
||||
this._sync()
|
||||
return [this, null]
|
||||
}
|
||||
if (tui.key.matches(msg, 'ctrl+n', 'alt+right')) {
|
||||
this._nextBuffer(1)
|
||||
return [this, null]
|
||||
@@ -375,14 +494,24 @@ function bareIrcCreateTuiApp(ctx, client) {
|
||||
}
|
||||
body = joined.join('\n')
|
||||
}
|
||||
var prompt =
|
||||
(buf.kind === 'p2p' ? '#p2p ' : '') +
|
||||
'[' +
|
||||
buf.name +
|
||||
'] ' +
|
||||
(this.input.view ? this.input.view() : this.input.value || '')
|
||||
var footRaw =
|
||||
'Enter send /join /query /quit Ctrl+n/p buffers Tab nick ? help'
|
||||
var prompt
|
||||
if (this.roomsOpen && this.roomsFilterEdit) {
|
||||
prompt =
|
||||
'Filter: ' +
|
||||
(this.input.view ? this.input.view() : this.input.value || '')
|
||||
} else {
|
||||
prompt =
|
||||
(buf.kind === 'p2p' ? '#p2p ' : '') +
|
||||
'[' +
|
||||
(this.roomsOpen ? 'rooms' : buf.name) +
|
||||
'] ' +
|
||||
(this.input.view ? this.input.view() : this.input.value || '')
|
||||
}
|
||||
var footRaw = this.roomsOpen
|
||||
? this.roomsFilterEdit
|
||||
? 'Filter + Enter search LIST Esc back'
|
||||
: 'Enter join j/k move / filter Esc close featured or /rooms linux'
|
||||
: 'Enter send /rooms /join /query Ctrl+L rooms Ctrl+n/p Tab nick ?'
|
||||
var foot = st ? st().dim().width(cols).render(footRaw) : footRaw
|
||||
var rule = st
|
||||
? st()
|
||||
@@ -412,11 +541,13 @@ function bareIrcCreateTuiApp(ctx, client) {
|
||||
var body = this.quitConfirm
|
||||
? 'Disconnect and quit?\n\nY yes any other key cancel'
|
||||
: 'Bare OS irc\n\n' +
|
||||
'/join #chan /part /query nick /msg nick text\n' +
|
||||
'/nick /whois /me /away /quote /quit\n' +
|
||||
'/rooms [pat] browse/list /join #chan /part /query nick\n' +
|
||||
'/msg /notice /me /nick /whois /whowas /who /names /topic\n' +
|
||||
'/kick /invite /op /deop /voice /mode /ban /away /back\n' +
|
||||
'/list /motd /quote /ctcp /ping /ignore /clear /close /quit\n' +
|
||||
'/p2p open #chan shadow room (not Libera)\n' +
|
||||
'Ctrl+n / Ctrl+p next buffer Tab complete\n\n' +
|
||||
'Default network: irc.libera.chat:6697 TLS\n' +
|
||||
'Ctrl+L rooms Ctrl+n/p buffers Tab nick\n\n' +
|
||||
'Default: irc.libera.chat:6697 TLS random nick if unset\n' +
|
||||
'Press any key to close.'
|
||||
var boxed = st
|
||||
? st()
|
||||
|
||||
Reference in New Issue
Block a user