commit 7128ad8
delthas
·
2025-11-12 15:02:23 +0000 UTC
parent 8a399a9
Support STATUSMSG-prefixed messages Parse STATUSMSG prefixes in incoming messages, and push those messages to the correct channel with the prefix indicated in the interface. This also marks channel-context messages with a prefix. Changes to ui.Line were added in order to support multiple styles in the head. Implements: https://todo.sr.ht/~delthas/senpai/183 Co-Authored-By: Taavi Väänänen <taavi@majava.org>
M
app.go
+84,
-99
1@@ -484,7 +484,7 @@ func (app *App) ircLoop(netID string) {
2 if app.cfg.Debug {
3 app.queueStatusLine(netID, ui.Line{
4 At: time.Now(),
5- Head: "IN --",
6+ Head: ui.PlainString("IN --"),
7 Body: ui.PlainString(msg.String()),
8 })
9 }
10@@ -498,16 +498,15 @@ func (app *App) ircLoop(netID string) {
11 content: nil,
12 })
13 app.queueStatusLine(netID, ui.Line{
14- Head: "!!",
15- HeadColor: ui.ColorRed,
16- Body: ui.PlainString("Connection lost"),
17+ Head: ui.ColorString("!!", ui.ColorRed),
18+ Body: ui.PlainString("Connection lost"),
19 })
20 }
21 }
22
23 func (app *App) connect(netID string) net.Conn {
24 app.queueStatusLine(netID, ui.Line{
25- Head: "--",
26+ Head: ui.PlainString("--"),
27 Body: ui.PlainSprintf("Connecting to %s...", app.cfg.Addr),
28 })
29 conn, err := app.tryConnect()
30@@ -515,9 +514,8 @@ func (app *App) connect(netID string) net.Conn {
31 return conn
32 }
33 app.queueStatusLine(netID, ui.Line{
34- Head: "!!",
35- HeadColor: ui.ColorRed,
36- Body: ui.PlainSprintf("Connection failed: %v", err),
37+ Head: ui.ColorString("!!", ui.ColorRed),
38+ Body: ui.PlainSprintf("Connection failed: %v", err),
39 })
40 return nil
41 }
42@@ -582,7 +580,7 @@ func (app *App) debugOutputMessages(netID string, out chan<- irc.Message) chan<-
43 }
44 app.queueStatusLine(netID, ui.Line{
45 At: time.Now(),
46- Head: "OUT --",
47+ Head: ui.PlainString("OUT --"),
48 Body: ui.PlainString(d.String()),
49 })
50 out <- msg
51@@ -639,11 +637,10 @@ func (app *App) handleUIEvent(ev interface{}) bool {
52 if err := commandDoUpload(app, []string{ev.Path}); err != nil {
53 netID, buffer := app.win.CurrentBuffer()
54 app.win.AddLine(netID, buffer, ui.Line{
55- At: time.Now(),
56- Head: "!!",
57- HeadColor: ui.ColorRed,
58- Notify: ui.NotifyUnread,
59- Body: ui.PlainSprintf("SCREENSHOT: %s", err),
60+ At: time.Now(),
61+ Head: ui.ColorString("!!", ui.ColorRed),
62+ Notify: ui.NotifyUnread,
63+ Body: ui.PlainSprintf("SCREENSHOT: %s", err),
64 })
65 break
66 }
67@@ -669,7 +666,7 @@ func (app *App) handleUIEvent(ev interface{}) bool {
68 netID, buffer := app.win.CurrentBuffer()
69 app.win.AddLine(netID, buffer, ui.Line{
70 At: time.Now(),
71- Head: "--",
72+ Head: ui.PlainString("--"),
73 Body: ui.PlainString(fmt.Sprintf("File uploaded at: %v", ev.Location)),
74 })
75 }
76@@ -677,10 +674,9 @@ func (app *App) handleUIEvent(ev interface{}) bool {
77 app.uploadingProgress = nil
78 netID, buffer := app.win.CurrentBuffer()
79 app.win.AddLine(netID, buffer, ui.Line{
80- At: time.Now(),
81- Head: "!!",
82- HeadColor: ui.ColorRed,
83- Body: ui.PlainString(fmt.Sprintf("File upload failed: %v", ev.Error)),
84+ At: time.Now(),
85+ Head: ui.ColorString("!!", ui.ColorRed),
86+ Body: ui.PlainString(fmt.Sprintf("File upload failed: %v", ev.Error)),
87 })
88 } else {
89 app.uploadingProgress = &ev.Progress
90@@ -699,7 +695,7 @@ func (app *App) handleUIEvent(ev interface{}) bool {
91 cNetID, cTarget := app.win.CurrentBuffer()
92 app.win.AddLine(cNetID, cTarget, ui.Line{
93 At: time.Now(),
94- Head: "--",
95+ Head: ui.PlainString("--"),
96 Body: ui.PlainString("An IRC link of a new channel was opened. Enter to add and join that channel."),
97 })
98 app.win.InputSet(fmt.Sprintf("/join %v", target))
99@@ -712,7 +708,7 @@ func (app *App) handleUIEvent(ev interface{}) bool {
100 netID, target := app.win.CurrentBuffer()
101 app.win.AddLine(netID, target, ui.Line{
102 At: time.Now(),
103- Head: "--",
104+ Head: ui.PlainString("--"),
105 Body: ui.PlainString("An IRC link of a new network was opened. Enter to add and join that network."),
106 })
107 app.win.InputSet(fmt.Sprintf("/bouncer network create -addr %q", host))
108@@ -807,10 +803,9 @@ func (app *App) handleMouseEvent(ev vaxis.Mouse) {
109 if failed {
110 netID, buffer := app.win.CurrentBuffer()
111 app.win.AddLine(netID, buffer, ui.Line{
112- At: time.Now(),
113- Head: "!!",
114- HeadColor: ui.ColorRed,
115- Body: ui.PlainString(errNotSupported.Error()),
116+ At: time.Now(),
117+ Head: ui.ColorString("!!", ui.ColorRed),
118+ Body: ui.PlainString(errNotSupported.Error()),
119 })
120 }
121 } else {
122@@ -857,20 +852,19 @@ func (app *App) handleMouseEvent(ev vaxis.Mouse) {
123 case 2:
124 if _, err := getBouncerService(app); err != nil {
125 app.win.AddLine(netID, target, ui.Line{
126- At: time.Now(),
127- Head: "--",
128- HeadColor: ui.ColorRed,
129- Body: ui.PlainSprintf("Adding networks is not available: %v", err),
130+ At: time.Now(),
131+ Head: ui.ColorString("--", ui.ColorRed),
132+ Body: ui.PlainSprintf("Adding networks is not available: %v", err),
133 })
134 } else {
135 app.win.AddLine(netID, target, ui.Line{
136 At: time.Now(),
137- Head: "--",
138+ Head: ui.PlainString("--"),
139 Body: ui.PlainString("To join a network/server, use /bouncer network create -addr <address> [-name <name>]"),
140 })
141 app.win.AddLine(netID, target, ui.Line{
142 At: time.Now(),
143- Head: "--",
144+ Head: ui.PlainString("--"),
145 Body: ui.PlainString("For details, see /bouncer help network create"),
146 })
147 app.win.InputSet("/bouncer network create -addr ")
148@@ -878,14 +872,14 @@ func (app *App) handleMouseEvent(ev vaxis.Mouse) {
149 case 4:
150 app.win.AddLine(netID, target, ui.Line{
151 At: time.Now(),
152- Head: "--",
153+ Head: ui.PlainString("--"),
154 Body: ui.PlainString("To join a channel, use /join <#channel> [<password>]"),
155 })
156 app.win.InputSet("/join ")
157 case 6:
158 app.win.AddLine(netID, target, ui.Line{
159 At: time.Now(),
160- Head: "--",
161+ Head: ui.PlainString("--"),
162 Body: ui.PlainString("To message a user, use /query <user> [<message>]"),
163 })
164 app.win.InputSet("/query ")
165@@ -1000,11 +994,10 @@ func (app *App) handleAction(action string, args ...string) {
166 for _, part := range strings.Split(input, "\n") {
167 if err = app.handleInput(buffer, part); err != nil {
168 app.win.AddLine(netID, buffer, ui.Line{
169- At: time.Now(),
170- Head: "!!",
171- HeadColor: ui.ColorRed,
172- Notify: ui.NotifyUnread,
173- Body: ui.PlainSprintf("%q: %s", input, err),
174+ At: time.Now(),
175+ Head: ui.ColorString("!!", ui.ColorRed),
176+ Notify: ui.NotifyUnread,
177+ Body: ui.PlainSprintf("%q: %s", input, err),
178 })
179 break
180 }
181@@ -1030,11 +1023,10 @@ func (app *App) handleAction(action string, args ...string) {
182 default:
183 netID, buffer := app.win.CurrentBuffer()
184 app.win.AddLine(netID, buffer, ui.Line{
185- At: time.Now(),
186- Head: "!!",
187- HeadColor: ui.ColorRed,
188- Notify: ui.NotifyUnread,
189- Body: ui.PlainSprintf("shortcut: action %q does not exist", action),
190+ At: time.Now(),
191+ Head: ui.ColorString("!!", ui.ColorRed),
192+ Notify: ui.NotifyUnread,
193+ Body: ui.PlainSprintf("shortcut: action %q does not exist", action),
194 })
195 }
196 }
197@@ -1453,10 +1445,9 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
198 ev, err := s.HandleMessage(msg)
199 if err != nil {
200 app.win.AddLine(netID, "", ui.Line{
201- Head: "!!",
202- HeadColor: ui.ColorRed,
203- Notify: ui.NotifyUnread,
204- Body: ui.PlainSprintf("Received corrupt message %q: %s", msg.String(), err),
205+ Head: ui.ColorString("!!", ui.ColorRed),
206+ Notify: ui.NotifyUnread,
207+ Body: ui.PlainSprintf("Received corrupt message %q: %s", msg.String(), err),
208 })
209 return
210 }
211@@ -1505,7 +1496,7 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
212 }
213 app.addStatusLine(netID, ui.Line{
214 At: msg.TimeOrNow(),
215- Head: "--",
216+ Head: ui.PlainString("--"),
217 Body: ui.PlainString(body),
218 })
219 if !app.shownBouncerNotice && !s.IsBouncer() {
220@@ -1519,7 +1510,7 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
221 } {
222 app.addStatusLine(netID, ui.Line{
223 At: msg.TimeOrNow(),
224- Head: "Bouncer --",
225+ Head: ui.PlainString("Bouncer --"),
226 Body: ui.IRCString(line),
227 })
228 }
229@@ -1550,7 +1541,7 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
230 cNetID, cTarget := app.win.CurrentBuffer()
231 app.win.AddLine(cNetID, cTarget, ui.Line{
232 At: time.Now(),
233- Head: "--",
234+ Head: ui.PlainString("--"),
235 Body: ui.PlainString("An IRC link of a new channel was opened. Enter to add and join that channel."),
236 })
237 app.win.InputSet(fmt.Sprintf("/join %v", target))
238@@ -1574,8 +1565,7 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
239 body.AddStyle(body.Len()-len(s.Nick()), textStyle)
240 app.addStatusLine(netID, ui.Line{
241 At: msg.TimeOrNow(),
242- Head: "--",
243- HeadColor: app.cfg.Colors.Status,
244+ Head: ui.ColorString("--", app.cfg.Colors.Status),
245 Body: body.StyledString(),
246 Highlight: true,
247 Readable: true,
248@@ -1679,10 +1669,9 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
249 body = fmt.Sprintf("%s invited %s to join this channel", ev.Inviter, ev.Invitee)
250 }
251 app.win.AddLine(netID, buffer, ui.Line{
252- At: msg.TimeOrNow(),
253- Head: "--",
254- HeadColor: app.cfg.Colors.Status,
255- Notify: notify,
256+ At: msg.TimeOrNow(),
257+ Head: ui.ColorString("--", app.cfg.Colors.Status),
258+ Notify: notify,
259 Body: ui.Styled(body, vaxis.Style{
260 Foreground: app.cfg.Colors.Status,
261 }),
262@@ -1865,7 +1854,7 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
263 netID, target := app.win.CurrentBuffer()
264 app.win.AddLine(netID, target, ui.Line{
265 At: time.Now(),
266- Head: "--",
267+ Head: ui.PlainString("--"),
268 Body: ui.PlainString("An IRC link of a new network was opened. Enter to add and join that network."),
269 })
270 app.win.InputSet(fmt.Sprintf("/bouncer network create -addr %q", host))
271@@ -1876,9 +1865,8 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
272 text += " -- " + item.Topic
273 }
274 app.addStatusLine(netID, ui.Line{
275- At: msg.TimeOrNow(),
276- Head: "List --",
277- HeadColor: app.cfg.Colors.Status,
278+ At: msg.TimeOrNow(),
279+ Head: ui.ColorString("List --", app.cfg.Colors.Status),
280 Body: ui.Styled(text, vaxis.Style{
281 Foreground: app.cfg.Colors.Status,
282 }),
283@@ -1892,9 +1880,8 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
284 head = "--"
285 }
286 app.addStatusLine(netID, ui.Line{
287- At: msg.TimeOrNow(),
288- Head: head,
289- HeadColor: app.cfg.Colors.Status,
290+ At: msg.TimeOrNow(),
291+ Head: ui.ColorString(head, app.cfg.Colors.Status),
292 Body: ui.Styled(ev.Message, vaxis.Style{
293 Foreground: app.cfg.Colors.Status,
294 }),
295@@ -1905,9 +1892,8 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
296 switch ev.Severity {
297 case irc.SeverityNote:
298 app.addStatusLine(netID, ui.Line{
299- At: msg.TimeOrNow(),
300- Head: fmt.Sprintf("(%s) --", ev.Code),
301- HeadColor: app.cfg.Colors.Status,
302+ At: msg.TimeOrNow(),
303+ Head: ui.ColorString(fmt.Sprintf("(%s) --", ev.Code), app.cfg.Colors.Status),
304 Body: ui.Styled(ev.Message, vaxis.Style{
305 Foreground: app.cfg.Colors.Status,
306 }),
307@@ -1924,7 +1910,7 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
308 }
309 app.addStatusLine(netID, ui.Line{
310 At: msg.TimeOrNow(),
311- Head: head,
312+ Head: ui.PlainString(head),
313 Body: ui.PlainString(body),
314 })
315 }
316@@ -1997,10 +1983,9 @@ func (app *App) notifyHighlight(buffer, nick, content string, current bool) {
317 if app.cfg.OnHighlightPath != "" {
318 body := fmt.Sprintf("Unable to find on-highlight command at path: %q", path)
319 app.addStatusLine(netID, ui.Line{
320- At: time.Now(),
321- Head: "!!",
322- HeadColor: ui.ColorRed,
323- Body: ui.PlainString(body),
324+ At: time.Now(),
325+ Head: ui.ColorString("!!", ui.ColorRed),
326+ Body: ui.PlainString(body),
327 })
328 }
329 return
330@@ -2020,10 +2005,9 @@ func (app *App) notifyHighlight(buffer, nick, content string, current bool) {
331 if err != nil {
332 body := fmt.Sprintf("Failed to invoke on-highlight command at path: %v. Output: %q", err, string(output))
333 app.addStatusLine(netID, ui.Line{
334- At: time.Now(),
335- Head: "!!",
336- HeadColor: ui.ColorRed,
337- Body: ui.PlainString(body),
338+ At: time.Now(),
339+ Head: ui.ColorString("!!", ui.ColorRed),
340+ Body: ui.PlainString(body),
341 })
342 }
343 }
344@@ -2113,8 +2097,7 @@ func (app *App) formatEvent(ev irc.Event) ui.Line {
345 body.AddStyle(body.Len()-len(ev.User), textStyle)
346 return ui.Line{
347 At: ev.Time,
348- Head: "--",
349- HeadColor: app.cfg.Colors.Status,
350+ Head: ui.ColorString("--", app.cfg.Colors.Status),
351 Body: body.StyledString(),
352 Mergeable: true,
353 Data: []irc.Event{ev},
354@@ -2133,8 +2116,7 @@ func (app *App) formatEvent(ev irc.Event) ui.Line {
355 body.WriteString(ev.User)
356 return ui.Line{
357 At: ev.Time,
358- Head: "--",
359- HeadColor: app.cfg.Colors.Status,
360+ Head: ui.ColorString("--", app.cfg.Colors.Status),
361 Body: body.StyledString(),
362 Mergeable: true,
363 Data: []irc.Event{ev},
364@@ -2153,8 +2135,7 @@ func (app *App) formatEvent(ev irc.Event) ui.Line {
365 body.WriteString(ev.User)
366 return ui.Line{
367 At: ev.Time,
368- Head: "--",
369- HeadColor: app.cfg.Colors.Status,
370+ Head: ui.ColorString("--", app.cfg.Colors.Status),
371 Body: body.StyledString(),
372 Mergeable: true,
373 Data: []irc.Event{ev},
374@@ -2173,8 +2154,7 @@ func (app *App) formatEvent(ev irc.Event) ui.Line {
375 body.WriteString(ev.User)
376 return ui.Line{
377 At: ev.Time,
378- Head: "--",
379- HeadColor: app.cfg.Colors.Status,
380+ Head: ui.ColorString("--", app.cfg.Colors.Status),
381 Body: body.StyledString(),
382 Mergeable: true,
383 Data: []irc.Event{ev},
384@@ -2185,10 +2165,9 @@ func (app *App) formatEvent(ev irc.Event) ui.Line {
385 who := ui.IRCString(ev.Who).String()
386 body := fmt.Sprintf("Topic changed by %s to: %s", who, topic)
387 return ui.Line{
388- At: ev.Time,
389- Head: "--",
390- HeadColor: app.cfg.Colors.Status,
391- Notify: ui.NotifyUnread,
392+ At: ev.Time,
393+ Head: ui.ColorString("--", app.cfg.Colors.Status),
394+ Notify: ui.NotifyUnread,
395 Body: ui.Styled(body, vaxis.Style{
396 Foreground: app.cfg.Colors.Status,
397 }),
398@@ -2197,9 +2176,8 @@ func (app *App) formatEvent(ev irc.Event) ui.Line {
399 case irc.ModeChangeEvent:
400 body := fmt.Sprintf("[%s]", ev.Mode)
401 return ui.Line{
402- At: ev.Time,
403- Head: "--",
404- HeadColor: app.cfg.Colors.Status,
405+ At: ev.Time,
406+ Head: ui.ColorString("--", app.cfg.Colors.Status),
407 Body: ui.Styled(body, vaxis.Style{
408 Foreground: app.cfg.Colors.Status,
409 }),
410@@ -2318,12 +2296,21 @@ func (app *App) formatMessage(s *irc.Session, ev irc.MessageEvent) (buffer strin
411 notification = ui.NotifyUnread
412 }
413
414- head := ev.User
415- var headColor vaxis.Color
416+ var head ui.StyledStringBuilder
417+ if ev.TargetPrefix != "" {
418+ head.WriteStyledString(ui.ColorString(ev.TargetPrefix, ui.ColorGreen))
419+ }
420+
421 if isAction || isNotice {
422- head = "*"
423+ if head.Len() == 0 {
424+ head.WriteStyledString(ui.PlainString("*"))
425+ }
426 } else {
427- headColor = app.win.IdentColor(app.cfg.Colors.Nicks, head, isFromSelf)
428+ if head.Len() > 0 {
429+ head.WriteStyledString(ui.PlainString(" "))
430+ }
431+ c := app.win.IdentColor(app.cfg.Colors.Nicks, ev.User, isFromSelf)
432+ head.WriteStyledString(ui.ColorString(ev.User, c))
433 }
434
435 var body ui.StyledStringBuilder
436@@ -2351,8 +2338,7 @@ func (app *App) formatMessage(s *irc.Session, ev irc.MessageEvent) (buffer strin
437
438 line = ui.Line{
439 At: ev.Time,
440- Head: head,
441- HeadColor: headColor,
442+ Head: head.StyledString(),
443 Notify: notification,
444 Body: body.StyledString(),
445 Highlight: hlLine,
446@@ -2549,9 +2535,8 @@ func (app *App) printTopic(netID, buffer string) (ok bool) {
447 body = fmt.Sprintf("Topic (set by %s on %s): %s", who.Name, at.Local().Format("January 2 2006 at 15:04:05"), topic)
448 }
449 app.win.AddLine(netID, buffer, ui.Line{
450- At: time.Now(),
451- Head: "--",
452- HeadColor: app.cfg.Colors.Status,
453+ At: time.Now(),
454+ Head: ui.ColorString("--", app.cfg.Colors.Status),
455 Body: ui.Styled(body, vaxis.Style{
456 Foreground: app.cfg.Colors.Status,
457 }),
+5,
-6
1@@ -429,7 +429,7 @@ func commandDoHelp(app *App, args []string) (err error) {
2 if len(args) == 0 {
3 app.win.AddLine(netID, buffer, ui.Line{
4 At: t,
5- Head: "--",
6+ Head: ui.PlainString("--"),
7 Body: ui.PlainString("Available commands:"),
8 })
9
10@@ -442,7 +442,7 @@ func commandDoHelp(app *App, args []string) (err error) {
11 search := strings.ToUpper(args[0])
12 app.win.AddLine(netID, buffer, ui.Line{
13 At: t,
14- Head: "--",
15+ Head: ui.PlainString("--"),
16 Body: ui.PlainSprintf("Commands that match \"%s\":", search),
17 })
18
19@@ -589,10 +589,9 @@ func commandDoNames(app *App, args []string) (err error) {
20 body := sb.StyledString()
21 // TODO remove last space
22 app.win.AddLine(netID, buffer, ui.Line{
23- At: time.Now(),
24- Head: "--",
25- HeadColor: app.cfg.Colors.Status,
26- Body: body,
27+ At: time.Now(),
28+ Head: ui.ColorString("--", app.cfg.Colors.Status),
29+ Body: body,
30 })
31 return nil
32 }
+1,
-0
1@@ -87,6 +87,7 @@ type MessageEvent struct {
2 User string
3 Target string
4 TargetIsChannel bool
5+ TargetPrefix string
6 Command string
7 Content string
8 Time time.Time
+23,
-8
1@@ -145,6 +145,7 @@ type Session struct {
2
3 serverName string
4 defaultPrefix *Prefix
5+
6 // ISUPPORT features
7 casemap func(string) string
8 chanmodes [4]string
9@@ -153,6 +154,7 @@ type Session struct {
10 historyLimit int
11 prefixSymbols string
12 prefixModes string
13+ statusmsg string
14 monitor bool
15 whox bool
16 listMask bool
17@@ -1378,7 +1380,7 @@ func (s *Session) handleMessageRegistered(msg Message, playback bool) (Event, er
18 return nil, err
19 }
20
21- targetCf := s.casemap(target)
22+ targetCf := strings.TrimLeft(s.casemap(target), s.statusmsg)
23 nickCf := s.casemap(msg.Prefix.Name)
24 if !playback {
25 s.typings.Done(targetCf, nickCf)
26@@ -2123,22 +2125,33 @@ func (s *Session) handleMessageRegistered(msg Message, playback bool) (Event, er
27 }
28
29 func (s *Session) newMessageEvent(msg Message) (ev MessageEvent, err error) {
30- var target, content string
31- if err := msg.ParseParams(&target, &content); err != nil {
32+ var rawTarget, content string
33+ if err := msg.ParseParams(&rawTarget, &content); err != nil {
34 return ev, err
35 }
36
37+ i := strings.IndexFunc(rawTarget, func(r rune) bool {
38+ return !strings.ContainsRune(s.statusmsg, r)
39+ })
40+ if i < 0 {
41+ i = len(rawTarget)
42+ }
43+ prefix := rawTarget[:i]
44+ target := rawTarget[i:]
45+
46 ev = MessageEvent{
47- User: msg.Prefix.Name, // TODO correctly casemap
48- Target: target, // TODO correctly casemap
49- Command: msg.Command,
50- Content: content,
51- Time: msg.TimeOrNow(),
52+ User: msg.Prefix.Name, // TODO correctly casemap
53+ Target: target, // TODO correctly casemap
54+ TargetPrefix: prefix,
55+ Command: msg.Command,
56+ Content: content,
57+ Time: msg.TimeOrNow(),
58 }
59
60 if s.IsMe(target) {
61 if context := msg.Tags["+draft/channel-context"]; context != "" {
62 target = context
63+ ev.TargetPrefix = "*"
64 }
65 }
66 targetCf := s.Casemap(target)
67@@ -2246,6 +2259,8 @@ func (s *Session) updateFeatures(features []string) {
68 numPrefixes := len(value)/2 - 1
69 s.prefixModes = value[1 : numPrefixes+1]
70 s.prefixSymbols = value[numPrefixes+2:]
71+ case "STATUSMSG":
72+ s.statusmsg = value
73 case "WHOX":
74 s.whox = true
75 case "SOJU.IM/FILEHOST":
+18,
-9
1@@ -42,9 +42,8 @@ const (
2
3 type Line struct {
4 At time.Time
5- Head string
6+ Head StyledString
7 Body StyledString
8- HeadColor vaxis.Color
9 Notify NotifyType
10 Highlight bool
11 Readable bool
12@@ -1239,15 +1238,25 @@ func (bs *BufferList) DrawTimeline(ui *UI, x0, y0, nickColWidth int) {
13 }
14
15 if yi >= y0 {
16- identSt := vaxis.Style{
17- Foreground: line.HeadColor,
18+ head := line.Head
19+ if line.Highlight && len(line.Head.styles) > 0 {
20+ var sb StyledStringBuilder
21+ sb.WriteString(head.string)
22+ for _, st := range line.Head.styles {
23+ s := st.Style
24+ s.Attribute |= vaxis.AttrReverse
25+ sb.AddStyle(st.Start, s)
26+ }
27+ head = sb.StyledString()
28 }
29- if line.Highlight {
30- identSt.Attribute |= vaxis.AttrReverse
31+ xb, xe := printIdent(vx, x0+7, yi, nickColWidth, head)
32+
33+ lastHead := line.Head.string
34+ if len(line.Head.styles) > 0 {
35+ lastHead = lastHead[line.Head.styles[len(line.Head.styles)-1].Start:]
36 }
37- xb, xe := printIdent(vx, x0+7, yi, nickColWidth, Styled(line.Head, identSt))
38
39- if !strings.HasSuffix(line.Head, "--") && !strings.HasSuffix(line.Head, "!!") {
40+ if !strings.HasSuffix(lastHead, "--") && !strings.HasSuffix(lastHead, "!!") {
41 ui.clickEvents = append(ui.clickEvents, clickEvent{
42 xb: xb,
43 xe: xe,
44@@ -1257,7 +1266,7 @@ func (bs *BufferList) DrawTimeline(ui *UI, x0, y0, nickColWidth int) {
45 NetID: b.netID,
46 Buffer: b.title,
47 },
48- Nick: line.Head,
49+ Nick: lastHead,
50 },
51 })
52 }
+4,
-0
1@@ -74,6 +74,10 @@ func PlainSprintf(format string, a ...interface{}) StyledString {
2 return PlainString(fmt.Sprintf(format, a...))
3 }
4
5+func ColorString(s string, fg vaxis.Color) StyledString {
6+ return Styled(s, vaxis.Style{Foreground: fg})
7+}
8+
9 func Styled(s string, style vaxis.Style) StyledString {
10 rStyle := rangedStyle{
11 Start: 0,
M
ui/ui.go
+3,
-3
1@@ -491,10 +491,10 @@ func (ui *UI) AddLine(netID, buffer string, line Line) {
2 focused := ui.bs.focused && curNetID == netID && curBuffer == buffer
3 if b != nil && line.Notify == NotifyHighlight && !focused {
4 var header string
5- if buffer != line.Head {
6- header = fmt.Sprintf("%s — %s", buffer, line.Head)
7+ if buffer != line.Head.String() {
8+ header = fmt.Sprintf("%s — %s", buffer, line.Head.String())
9 } else {
10- header = line.Head
11+ header = line.Head.String()
12 }
13 id := ui.notify(NotifyEvent{
14 NetID: netID,
+1,
-1
1@@ -16,7 +16,7 @@ func (app *App) initWindow() {
2 }
3 app.win.AddBuffer("", "(home)", "")
4 app.win.AddLine("", "", ui.Line{
5- Head: "--",
6+ Head: ui.PlainString("--"),
7 Body: ui.PlainString(fmt.Sprintf("Welcome to senpai %v! To get started, use the Help buttons, or enter /help for a list of commands.", version)),
8 At: time.Now(),
9 })