Skip to content

Commit 40bec5e

Browse files
committed
make colours user-configurable
1 parent 634c673 commit 40bec5e

8 files changed

Lines changed: 172 additions & 36 deletions

File tree

README.md

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ a = away, d = do not disturb, x = extended away, _ = offline).
114114

115115
A single contact is active, which can be modified by `PgUp/PdDown`.
116116
The active contact is shown in reversed foreground and background
117-
color. Its chat content is displayed in the chat window. Certain
117+
colour. Its chat content is displayed in the chat window. Certain
118118
commands and operations (such as sending a message) require an active
119119
contact.
120120

@@ -170,20 +170,49 @@ Active keys:
170170

171171
`/help` prints the available commands, `/help command` more detailed help of the given command.
172172

173-
#### Colors
173+
#### Colours
174+
175+
default foreground colours are:
176+
- Chat empty
177+
- GroupChat empty
178+
- Presence gray 18
179+
- Info gray 18
180+
- Warning yellow
181+
- Error red
182+
- Success green
183+
184+
To customise, create a file `colours.sexp` in your config folder
185+
(`~/.config/ocaml-xmpp-client` unless `-f` is provided). Sample content:
186+
```
187+
((Chat "empty")
188+
(GroupChat "empty")
189+
(Presence "gray 18")
190+
(Info "gray 18")
191+
(Warning "yellow")
192+
(Error "red")
193+
(Success "green"))
194+
```
195+
196+
Available colours:
197+
- "empty",
198+
- "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white",
199+
- "lightblack", "lightred", "lightgreen", "lightyellow", "lightblue", "lightmagenta", "lightcyan", "lightwhite",
200+
- "gray <n>" (where n is in the range 0 and 23,
201+
- "rgb <r> <g> <b>" (where r, g, b in the range 0 and 5)
202+
203+
frame: red means the active session is unencrypted, green encrypted
174204

175205
contact list:
176-
- green frame and contact: OTR session established
177-
- red frame and contact: no OTR session
178-
- black: no active session exists
206+
- green contact: OTR session established
207+
- red contact: no OTR session
208+
- black/white: groupchat, offline, self
179209

180210
horizontal line
181211
- red OTR fingerprint: not verified (use a second channel)
182212
- green OTR: key is verified
183213

184-
status line
185-
- own jabber id in reverse colors: logging to disk is enabled for this contact
186-
- own jabber id in usual colors: no logging to disk for this contact
214+
logging (`/log on|off`):
215+
- jabber id in status line in reverse: logging is turned on
187216

188217
### FAQ
189218

_tags

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ true : package(sexplib astring)
1313
<src/xmpp_callbacks.ml>: package(erm_xmpp lwt tls tls.lwt ptime)
1414
<src/xmpp_connection.ml>: package(erm_xmpp lwt tls tls.lwt)
1515

16-
<cli/cli_support.ml>: package(uutf notty astring)
17-
<cli/cli_client.ml>: package(lwt otr notty erm_xmpp ptime ptime.clock.os)
18-
<cli/cli_input.ml>: package(notty lwt erm_xmpp otr)
19-
<cli/cli_commands.ml>: package(lwt otr erm_xmpp)
20-
<cli/cli_config.ml>: package(lwt nocrypto otr notty tls.lwt x509)
21-
<cli/cli_state.ml>: package(hex lwt nocrypto erm_xmpp tls.lwt x509)
16+
<cli/*>: package(notty lwt)
17+
<cli/cli_colour.ml>: package(astring)
18+
<cli/cli_support.ml>: package(uutf astring)
19+
<cli/cli_client.ml>: package(otr erm_xmpp ptime ptime.clock.os)
20+
<cli/cli_input.ml>: package(erm_xmpp otr)
21+
<cli/cli_commands.ml>: package(otr erm_xmpp)
22+
<cli/cli_config.ml>: package(nocrypto otr tls.lwt x509)
23+
<cli/cli_state.ml>: package(hex nocrypto erm_xmpp tls.lwt x509)
2224

2325
<bin/jackline.{ml,byte,native}>: package(erm_xmpp hex lwt notty notty.lwt nocrypto otr sexplib tls tls.lwt ptime ptime.clock.os)

bin/jackline.ml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ let start_client cfgdir debug unicode fd_gui fd_nfy () =
5959
config
6060
| Some cfg -> Lwt.return cfg) >>= fun config ->
6161

62+
Cli_colour.init () ;
63+
(Persistency.load_colours cfgdir >|= function
64+
| None -> ()
65+
| Some colours -> Cli_colour.load colours) >>= fun () ->
66+
6267
(match config.Xconfig.password with
6368
| None -> Persistency.load_password cfgdir
6469
| Some x -> Lwt.return (Some x)) >>= (function
@@ -113,11 +118,14 @@ let start_client cfgdir debug unicode fd_gui fd_nfy () =
113118
let connect_mvar = Cli_state.Connect.connect_me config ui_mvar state_mvar users in
114119
let state = Cli_state.empty_state cfgdir config users connect_mvar state_mvar in
115120

116-
let greeting =
117-
"multi user chat support: see you at /join jackline@conference.jabber.ccc.de (use ArrowUp key); \
118-
type /help for help"
119-
and sender = `Local (`Full myjid, "welcome to jackline " ^ Utils.version) in
120-
Cli_state.add_status ~kind:`Info state sender greeting ;
121+
let greetings = [
122+
"welcome to jackline r" ^ Utils.version ;
123+
"type /help for command list" ;
124+
"* configurable colours! '((Presence \"cyan\") (Success \"lightgreen\"))' in your ~/.config/ocaml-xmpp-client/colours.sexp (see https://github.com/hannesm/jackline for details)" ;
125+
"* improved MUC support: join&leave messages; autojoin on (re)connect; /rooms discovery" ]
126+
and sender = `Local (`Full myjid,"")
127+
in
128+
List.iter (Cli_state.add_status ~kind:`Info state sender) greetings ;
121129

122130
let us = Contact.fold (fun _ v acc -> v :: acc) users [] in
123131

cli/cli_client.ml

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,6 @@ let print_time ~now ~tz_offset_s timestamp =
1313
else
1414
Printf.sprintf "%02d-%02d %02d:%02d " m d hh mm
1515

16-
let st_to_a = function
17-
| `Chat | `GroupChat -> A.empty
18-
| `Presence | `Info -> A.(fg (gray 18))
19-
| `Warning -> A.(fg yellow)
20-
| `Error -> A.(fg red)
21-
| `Success -> A.(fg green)
22-
2316
let format_log tz_offset_s now log =
2417
let { User.direction ; timestamp ; message ; kind ; _ } = log in
2518
let time = print_time ~now ~tz_offset_s timestamp in
@@ -29,7 +22,7 @@ let format_log tz_offset_s now log =
2922
| `Local (_, x) -> "* " ^ x ^ " *"
3023
| `To _ -> ">>>"
3124
in
32-
(st_to_a kind, time ^ from ^ " " ^ message)
25+
(Cli_colour.kind kind, time ^ from ^ " " ^ message)
3326

3427
let format_message tz_offset_s now self buddy resource { User.direction ; encrypted ; received ; timestamp ; message ; kind ; _ } =
3528
let time = print_time ~now ~tz_offset_s timestamp
@@ -75,11 +68,11 @@ let format_message tz_offset_s now self buddy resource { User.direction ; encryp
7568
(style, r ^ pre)
7669
and to_style st =
7770
match st, kind with
78-
| `Default, x -> st_to_a x
71+
| `Default, x -> Cli_colour.kind x
7972
| `Highlight, `Chat | `Highlight, `GroupChat -> A.(st bold)
80-
| `Highlight, x -> st_to_a x
73+
| `Highlight, x -> Cli_colour.kind x
8174
| `Underline, `Chat | `Underline, `GroupChat -> A.(st underline)
82-
| `Underline, x -> A.(st underline ++ st_to_a x)
75+
| `Underline, x -> A.(st underline ++ Cli_colour.kind x)
8376
in
8477
let p, msg =
8578
if String.length message >= 3 && String.sub message 0 3 = "/me" then
@@ -103,8 +96,8 @@ let format_message tz_offset_s now self buddy resource { User.direction ; encryp
10396

10497
let buddy_to_color = function
10598
| `Default -> A.empty
106-
| `Good -> A.(fg green)
107-
| `Bad -> A.(fg red)
99+
| `Good -> Cli_colour.kind `Success
100+
| `Bad -> Cli_colour.kind `Error
108101

109102
let format_buddy state width s contact resource =
110103
let jid = Contact.jid contact resource in
@@ -211,7 +204,7 @@ let horizontal_line buddy resource a scrollback width =
211204

212205
let status_line self mysession notify log a width =
213206
let a = A.(a ++ st bold) in
214-
let notify = if notify then I.string A.(a ++ st blink ++ fg cyan) "##" else Char.hdash a 2
207+
let notify = if notify then I.string A.(a ++ st blink ++ Cli_colour.kind `Warning) "##" else Char.hdash a 2
215208
and jid =
216209
let data = User.userid self mysession
217210
and a' = if log then A.(st reverse) else a
@@ -287,8 +280,8 @@ let render_state (width, height) state =
287280
let input = char_list_to_str pre in
288281
( match Cli_commands.completion state input with
289282
| [] -> I.empty
290-
| [x] -> I.string A.(fg (gray 18)) x
291-
| xs -> I.string A.(fg (gray 18)) (String.concat "|" xs) )
283+
| [x] -> I.string (Cli_colour.kind `Info) x
284+
| xs -> I.string (Cli_colour.kind `Info) (String.concat "|" xs) )
292285
| _ -> iinp2
293286
in
294287
v_center iinp r width
@@ -386,7 +379,7 @@ let rec loop term size redrawer mvar input_mvar state =
386379
try
387380
render_state size state
388381
with e ->
389-
let e = A.(fg red), (Printexc.to_string e)
382+
let e = Cli_colour.kind `Error, (Printexc.to_string e)
390383
and note =
391384
"While trying to render the UI. Try to scroll to another buddy \
392385
(Page Up/Down), switch rendering of buddy list (F12), or clear \

cli/cli_colour.ml

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
open Notty
2+
open Astring
3+
4+
module OrderedKind = struct
5+
type t = User.chatkind
6+
7+
let compare (a : [< t]) (b : [< t]) = match a, b with
8+
| `Chat, `Chat -> 0
9+
| `GroupChat, `GroupChat -> 0
10+
| `Presence, `Presence -> 0
11+
| `Info, `Info -> 0
12+
| `Warning, `Warning -> 0
13+
| `Error, `Error -> 0
14+
| `Success, `Success -> 0
15+
| `Chat, _ -> 1
16+
| _, `Chat -> -1
17+
| `GroupChat, _ -> 1
18+
| _, `GroupChat -> -1
19+
| `Presence, _ -> 1
20+
| _, `Presence -> -1
21+
| `Info, _ -> 1
22+
| _, `Info -> -1
23+
| `Warning, _ -> 1
24+
| _, `Warning -> -1
25+
| `Error, _ -> 1
26+
| _, `Error -> -1
27+
end
28+
29+
module M = Map.Make(OrderedKind)
30+
31+
let c = ref M.empty
32+
33+
let init () =
34+
let m =
35+
M.add `Presence A.(gray 18)
36+
(M.add `Info A.(gray 18)
37+
(M.add `Warning A.yellow
38+
(M.add `Error A.red
39+
(M.add `Success A.green M.empty))))
40+
in
41+
c := m
42+
43+
let parse_value =
44+
let open A in
45+
function
46+
| "empty" -> None
47+
| x -> Some (match x with
48+
| "black" -> Some black
49+
| "red" -> Some red
50+
| "green" -> Some green
51+
| "yellow" -> Some yellow
52+
| "blue" -> Some blue
53+
| "magenta" -> Some magenta
54+
| "cyan" -> Some cyan
55+
| "white" -> Some white
56+
| "lightblack" -> Some lightblack
57+
| "lightred" -> Some lightred
58+
| "lightgreen" -> Some lightgreen
59+
| "lightyellow" -> Some lightyellow
60+
| "lightblue" -> Some lightblue
61+
| "lightmagenta" -> Some lightmagenta
62+
| "lightcyan" -> Some lightcyan
63+
| "lightwhite" -> Some lightwhite
64+
| x when String.is_prefix ~affix:"gray" x ->
65+
(match String.cut ~sep:" " x with
66+
| None -> None
67+
| Some (_, i) -> match String.to_int i with
68+
| Some x when x >= 0 && x <= 23 -> Some (gray x)
69+
| _ -> None)
70+
| x when String.is_prefix ~affix:"rgb" x ->
71+
(match String.cuts ~sep:" " x with
72+
| _::xs when List.length xs = 3 ->
73+
(match
74+
List.filter
75+
(function None -> false | Some _ -> true)
76+
(List.map String.to_int xs)
77+
with
78+
| Some r::Some g::Some b::[] when r >= 0 && r <= 5 && g >= 0 && g <= 5 && b >= 0 && b <= 5 ->
79+
Some (rgb ~r ~g ~b)
80+
| _ -> None)
81+
| _ -> None)
82+
| _ -> None)
83+
84+
let load_c (kind, value) =
85+
match parse_value value with
86+
| None -> c := (M.remove kind !c)
87+
| Some None -> invalid_arg ("couldn't parse your colour for " ^ User.chatkind_to_string kind ^ ": " ^ value)
88+
| Some (Some col) -> c := (M.add kind col !c)
89+
90+
let load = List.iter load_c
91+
92+
let kind k = try A.fg (M.find k !c) with Not_found -> A.empty
93+

src/persistency.ml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ let write dir filename buf =
7777
Lwt.return ()
7878

7979
let config = "config.sexp"
80+
let colours = "colours.sexp"
8081
let users = "users.sexp"
8182

8283
let maybe_create_dir dir =
@@ -103,6 +104,12 @@ let load_config load_dsa cfg =
103104
Some (Xconfig.load_config (Some dsa) x)
104105
| None -> Lwt.return_none
105106

107+
108+
let load_colours cfg =
109+
read cfg colours >|= function
110+
| Some x -> Some (Sexplib.Conv.(list_of_sexp (pair_of_sexp User.chatkind_of_sexp string_of_sexp)) (Sexplib.Sexp.of_string x))
111+
| None -> None
112+
106113
let dump_user cfgdir user =
107114
user_dir cfgdir >>= fun userdir ->
108115
let out = Xjid.bare_jid_to_string (Contact.bare user) in

src/user.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ type chatkind = [
216216
| `Success
217217
] [@@deriving sexp]
218218

219+
let chatkind_to_string k =
220+
Sexplib.Sexp.to_string (sexp_of_chatkind k)
221+
219222
type message = {
220223
direction : direction ;
221224
encrypted : bool ;

src/user.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ type chatkind = [
9999

100100
val chatkind_of_sexp : Sexplib.Type.t -> chatkind
101101
val sexp_of_chatkind : chatkind -> Sexplib.Type.t
102+
val chatkind_to_string : chatkind -> string
102103

103104
type message = {
104105
direction : direction ;

0 commit comments

Comments
 (0)