Skip to main content

max / mountaineer

Add 'sysop/' from commit '0612c9431dee54b56e9d7a3c7bbc2d14f083d94b'
git-subtree-dir
sysop
git-subtree-mainline
2b96943159debad8446549bff833e94c4f89003a
git-subtree-split
0612c9431dee54b56e9d7a3c7bbc2d14f083d94b
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-05-18 03:11 UTC
Commit: 5dc41a4d0ff0941e31ebd86d837bc8e0a20c0200
Parents: 2b96943, 0612c94
24 files changed, +2548 insertions, -0 deletions
@@ -1,0 +1,4 @@
1 + /target
2 + **/*.rs.bk
3 + Cargo.lock.bak
4 + .DS_Store
@@ -1,0 +1,865 @@
1 + # This file is automatically @generated by Cargo.
2 + # It is not intended for manual editing.
3 + version = 4
4 +
5 + [[package]]
6 + name = "allocator-api2"
7 + version = "0.2.21"
8 + source = "registry+https://github.com/rust-lang/crates.io-index"
9 + checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
10 +
11 + [[package]]
12 + name = "anstream"
13 + version = "1.0.0"
14 + source = "registry+https://github.com/rust-lang/crates.io-index"
15 + checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
16 + dependencies = [
17 + "anstyle",
18 + "anstyle-parse",
19 + "anstyle-query",
20 + "anstyle-wincon",
21 + "colorchoice",
22 + "is_terminal_polyfill",
23 + "utf8parse",
24 + ]
25 +
26 + [[package]]
27 + name = "anstyle"
28 + version = "1.0.14"
29 + source = "registry+https://github.com/rust-lang/crates.io-index"
30 + checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
31 +
32 + [[package]]
33 + name = "anstyle-parse"
34 + version = "1.0.0"
35 + source = "registry+https://github.com/rust-lang/crates.io-index"
36 + checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
37 + dependencies = [
38 + "utf8parse",
39 + ]
40 +
41 + [[package]]
42 + name = "anstyle-query"
43 + version = "1.1.5"
44 + source = "registry+https://github.com/rust-lang/crates.io-index"
45 + checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
46 + dependencies = [
47 + "windows-sys 0.61.2",
48 + ]
49 +
50 + [[package]]
51 + name = "anstyle-wincon"
52 + version = "3.0.11"
53 + source = "registry+https://github.com/rust-lang/crates.io-index"
54 + checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
55 + dependencies = [
56 + "anstyle",
57 + "once_cell_polyfill",
58 + "windows-sys 0.61.2",
59 + ]
60 +
61 + [[package]]
62 + name = "anyhow"
63 + version = "1.0.102"
64 + source = "registry+https://github.com/rust-lang/crates.io-index"
65 + checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
66 +
67 + [[package]]
68 + name = "bitflags"
69 + version = "2.11.1"
70 + source = "registry+https://github.com/rust-lang/crates.io-index"
71 + checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
72 +
73 + [[package]]
74 + name = "cassowary"
75 + version = "0.3.0"
76 + source = "registry+https://github.com/rust-lang/crates.io-index"
77 + checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
78 +
79 + [[package]]
80 + name = "castaway"
81 + version = "0.2.4"
82 + source = "registry+https://github.com/rust-lang/crates.io-index"
83 + checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
84 + dependencies = [
85 + "rustversion",
86 + ]
87 +
88 + [[package]]
89 + name = "cfg-if"
90 + version = "1.0.4"
91 + source = "registry+https://github.com/rust-lang/crates.io-index"
92 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
93 +
94 + [[package]]
95 + name = "clap"
96 + version = "4.6.1"
97 + source = "registry+https://github.com/rust-lang/crates.io-index"
98 + checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
99 + dependencies = [
100 + "clap_builder",
101 + "clap_derive",
102 + ]
103 +
104 + [[package]]
105 + name = "clap_builder"
106 + version = "4.6.0"
107 + source = "registry+https://github.com/rust-lang/crates.io-index"
108 + checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
109 + dependencies = [
110 + "anstream",
111 + "anstyle",
112 + "clap_lex",
113 + "strsim",
114 + ]
115 +
116 + [[package]]
117 + name = "clap_derive"
118 + version = "4.6.1"
119 + source = "registry+https://github.com/rust-lang/crates.io-index"
120 + checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
121 + dependencies = [
122 + "heck",
123 + "proc-macro2",
124 + "quote",
125 + "syn",
126 + ]
127 +
128 + [[package]]
129 + name = "clap_lex"
130 + version = "1.1.0"
131 + source = "registry+https://github.com/rust-lang/crates.io-index"
132 + checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
133 +
134 + [[package]]
135 + name = "colorchoice"
136 + version = "1.0.5"
137 + source = "registry+https://github.com/rust-lang/crates.io-index"
138 + checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
139 +
140 + [[package]]
141 + name = "compact_str"
142 + version = "0.8.1"
143 + source = "registry+https://github.com/rust-lang/crates.io-index"
144 + checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32"
145 + dependencies = [
146 + "castaway",
147 + "cfg-if",
148 + "itoa",
149 + "rustversion",
150 + "ryu",
151 + "static_assertions",
152 + ]
153 +
154 + [[package]]
155 + name = "crossterm"
156 + version = "0.28.1"
157 + source = "registry+https://github.com/rust-lang/crates.io-index"
158 + checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
159 + dependencies = [
160 + "bitflags",
161 + "crossterm_winapi",
162 + "mio",
163 + "parking_lot",
164 + "rustix",
165 + "signal-hook",
166 + "signal-hook-mio",
167 + "winapi",
168 + ]
169 +
170 + [[package]]
171 + name = "crossterm_winapi"
172 + version = "0.9.1"
173 + source = "registry+https://github.com/rust-lang/crates.io-index"
174 + checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
175 + dependencies = [
176 + "winapi",
177 + ]
178 +
179 + [[package]]
180 + name = "darling"
181 + version = "0.20.11"
182 + source = "registry+https://github.com/rust-lang/crates.io-index"
183 + checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
184 + dependencies = [
185 + "darling_core",
186 + "darling_macro",
187 + ]
188 +
189 + [[package]]
190 + name = "darling_core"
191 + version = "0.20.11"
192 + source = "registry+https://github.com/rust-lang/crates.io-index"
193 + checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
194 + dependencies = [
195 + "fnv",
196 + "ident_case",
197 + "proc-macro2",
198 + "quote",
199 + "strsim",
200 + "syn",
201 + ]
202 +
203 + [[package]]
204 + name = "darling_macro"
205 + version = "0.20.11"
206 + source = "registry+https://github.com/rust-lang/crates.io-index"
207 + checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
208 + dependencies = [
209 + "darling_core",
210 + "quote",
211 + "syn",
212 + ]
213 +
214 + [[package]]
215 + name = "either"
216 + version = "1.15.0"
217 + source = "registry+https://github.com/rust-lang/crates.io-index"
218 + checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
219 +
220 + [[package]]
221 + name = "equivalent"
222 + version = "1.0.2"
223 + source = "registry+https://github.com/rust-lang/crates.io-index"
224 + checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
225 +
226 + [[package]]
227 + name = "errno"
228 + version = "0.3.14"
229 + source = "registry+https://github.com/rust-lang/crates.io-index"
230 + checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
231 + dependencies = [
232 + "libc",
233 + "windows-sys 0.61.2",
234 + ]
235 +
236 + [[package]]
237 + name = "fnv"
238 + version = "1.0.7"
239 + source = "registry+https://github.com/rust-lang/crates.io-index"
240 + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
241 +
242 + [[package]]
243 + name = "foldhash"
244 + version = "0.1.5"
245 + source = "registry+https://github.com/rust-lang/crates.io-index"
246 + checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
247 +
248 + [[package]]
249 + name = "hashbrown"
250 + version = "0.15.5"
251 + source = "registry+https://github.com/rust-lang/crates.io-index"
252 + checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
253 + dependencies = [
254 + "allocator-api2",
255 + "equivalent",
256 + "foldhash",
257 + ]
258 +
259 + [[package]]
260 + name = "hashbrown"
261 + version = "0.17.1"
262 + source = "registry+https://github.com/rust-lang/crates.io-index"
263 + checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
264 +
265 + [[package]]
266 + name = "heck"
267 + version = "0.5.0"
268 + source = "registry+https://github.com/rust-lang/crates.io-index"
269 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
270 +
271 + [[package]]
272 + name = "ident_case"
273 + version = "1.0.1"
274 + source = "registry+https://github.com/rust-lang/crates.io-index"
275 + checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
276 +
277 + [[package]]
278 + name = "indexmap"
279 + version = "2.14.0"
280 + source = "registry+https://github.com/rust-lang/crates.io-index"
281 + checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
282 + dependencies = [
283 + "equivalent",
284 + "hashbrown 0.17.1",
285 + ]
286 +
287 + [[package]]
288 + name = "indoc"
289 + version = "2.0.7"
290 + source = "registry+https://github.com/rust-lang/crates.io-index"
291 + checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
292 + dependencies = [
293 + "rustversion",
294 + ]
295 +
296 + [[package]]
297 + name = "instability"
298 + version = "0.3.10"
299 + source = "registry+https://github.com/rust-lang/crates.io-index"
300 + checksum = "6778b0196eefee7df739db78758e5cf9b37412268bfa5650bfeed028aed20d9c"
301 + dependencies = [
302 + "darling",
303 + "indoc",
304 + "proc-macro2",
305 + "quote",
306 + "syn",
307 + ]
308 +
309 + [[package]]
310 + name = "is_terminal_polyfill"
311 + version = "1.70.2"
312 + source = "registry+https://github.com/rust-lang/crates.io-index"
313 + checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
314 +
315 + [[package]]
316 + name = "itertools"
317 + version = "0.13.0"
318 + source = "registry+https://github.com/rust-lang/crates.io-index"
319 + checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
320 + dependencies = [
321 + "either",
322 + ]
323 +
324 + [[package]]
325 + name = "itoa"
326 + version = "1.0.18"
327 + source = "registry+https://github.com/rust-lang/crates.io-index"
328 + checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
329 +
330 + [[package]]
331 + name = "libc"
332 + version = "0.2.186"
333 + source = "registry+https://github.com/rust-lang/crates.io-index"
334 + checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
335 +
336 + [[package]]
337 + name = "linux-raw-sys"
338 + version = "0.4.15"
339 + source = "registry+https://github.com/rust-lang/crates.io-index"
340 + checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
341 +
342 + [[package]]
343 + name = "lock_api"
344 + version = "0.4.14"
345 + source = "registry+https://github.com/rust-lang/crates.io-index"
346 + checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
347 + dependencies = [
348 + "scopeguard",
349 + ]
350 +
351 + [[package]]
352 + name = "log"
353 + version = "0.4.29"
354 + source = "registry+https://github.com/rust-lang/crates.io-index"
355 + checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
356 +
357 + [[package]]
358 + name = "lru"
359 + version = "0.12.5"
360 + source = "registry+https://github.com/rust-lang/crates.io-index"
361 + checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
362 + dependencies = [
363 + "hashbrown 0.15.5",
364 + ]
365 +
366 + [[package]]
367 + name = "memchr"
368 + version = "2.8.0"
369 + source = "registry+https://github.com/rust-lang/crates.io-index"
370 + checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
371 +
372 + [[package]]
373 + name = "mio"
374 + version = "1.2.0"
375 + source = "registry+https://github.com/rust-lang/crates.io-index"
376 + checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
377 + dependencies = [
378 + "libc",
379 + "log",
380 + "wasi",
381 + "windows-sys 0.61.2",
382 + ]
383 +
384 + [[package]]
385 + name = "once_cell_polyfill"
386 + version = "1.70.2"
387 + source = "registry+https://github.com/rust-lang/crates.io-index"
388 + checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
389 +
390 + [[package]]
391 + name = "parking_lot"
392 + version = "0.12.5"
393 + source = "registry+https://github.com/rust-lang/crates.io-index"
394 + checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
395 + dependencies = [
396 + "lock_api",
397 + "parking_lot_core",
398 + ]
399 +
400 + [[package]]
401 + name = "parking_lot_core"
402 + version = "0.9.12"
403 + source = "registry+https://github.com/rust-lang/crates.io-index"
404 + checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
405 + dependencies = [
406 + "cfg-if",
407 + "libc",
408 + "redox_syscall",
409 + "smallvec",
410 + "windows-link",
411 + ]
412 +
413 + [[package]]
414 + name = "paste"
415 + version = "1.0.15"
416 + source = "registry+https://github.com/rust-lang/crates.io-index"
417 + checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
418 +
419 + [[package]]
420 + name = "proc-macro2"
421 + version = "1.0.106"
422 + source = "registry+https://github.com/rust-lang/crates.io-index"
423 + checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
424 + dependencies = [
425 + "unicode-ident",
426 + ]
427 +
428 + [[package]]
429 + name = "quote"
430 + version = "1.0.45"
431 + source = "registry+https://github.com/rust-lang/crates.io-index"
432 + checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
433 + dependencies = [
434 + "proc-macro2",
435 + ]
436 +
437 + [[package]]
438 + name = "ratatui"
439 + version = "0.29.0"
440 + source = "registry+https://github.com/rust-lang/crates.io-index"
441 + checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
442 + dependencies = [
443 + "bitflags",
444 + "cassowary",
445 + "compact_str",
446 + "crossterm",
447 + "indoc",
448 + "instability",
449 + "itertools",
450 + "lru",
451 + "paste",
452 + "strum",
453 + "unicode-segmentation",
454 + "unicode-truncate",
455 + "unicode-width 0.2.0",
456 + ]
457 +
458 + [[package]]
459 + name = "redox_syscall"
460 + version = "0.5.18"
461 + source = "registry+https://github.com/rust-lang/crates.io-index"
462 + checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
463 + dependencies = [
464 + "bitflags",
465 + ]
466 +
467 + [[package]]
468 + name = "rustix"
469 + version = "0.38.44"
470 + source = "registry+https://github.com/rust-lang/crates.io-index"
471 + checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
472 + dependencies = [
473 + "bitflags",
474 + "errno",
475 + "libc",
476 + "linux-raw-sys",
477 + "windows-sys 0.59.0",
478 + ]
479 +
480 + [[package]]
481 + name = "rustversion"
482 + version = "1.0.22"
483 + source = "registry+https://github.com/rust-lang/crates.io-index"
484 + checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
485 +
486 + [[package]]
487 + name = "ryu"
488 + version = "1.0.23"
489 + source = "registry+https://github.com/rust-lang/crates.io-index"
490 + checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
491 +
492 + [[package]]
493 + name = "scopeguard"
494 + version = "1.2.0"
495 + source = "registry+https://github.com/rust-lang/crates.io-index"
496 + checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
497 +
498 + [[package]]
499 + name = "serde"
500 + version = "1.0.228"
Lines truncated
@@ -1,0 +1,18 @@
1 + [workspace]
2 + resolver = "3"
3 + members = ["crates/sysop", "crates/sysop-tui"]
4 +
5 + [workspace.package]
6 + edition = "2024"
7 + rust-version = "1.85"
8 + license = "MIT OR Apache-2.0"
9 +
10 + [workspace.dependencies]
11 + sysop-tui = { path = "crates/sysop-tui" }
12 + ratatui = "0.29"
13 + crossterm = "0.28"
14 + anyhow = "1"
15 + clap = { version = "4", features = ["derive"] }
16 + serde = { version = "1", features = ["derive"] }
17 + serde_json = "1"
18 + toml = "0.8"
@@ -1,0 +1,10 @@
1 + [package]
2 + name = "sysop-tui"
3 + version = "0.1.0"
4 + edition.workspace = true
5 + rust-version.workspace = true
6 + license.workspace = true
7 +
8 + [dependencies]
9 + ratatui.workspace = true
10 + crossterm.workspace = true
@@ -1,0 +1,106 @@
1 + use crossterm::event::{self, Event, KeyCode};
2 + use ratatui::style::Style;
3 + use ratatui::text::{Line, Span};
4 + use ratatui::widgets::Paragraph;
5 + use std::io;
6 + use sysop_tui::{Footer, GlobalAction, classify, hint, layout, palette, selection, style};
7 +
8 + enum State {
9 + Ok,
10 + Warn,
11 + Fault,
12 + Inactive,
13 + }
14 +
15 + struct Service {
16 + name: &'static str,
17 + state: State,
18 + }
19 +
20 + fn state_span(s: &State) -> Span<'static> {
21 + match s {
22 + State::Ok => style::ok("ok"),
23 + State::Warn => style::warn("syncing"),
24 + State::Fault => style::fault("down"),
25 + State::Inactive => style::inactive("stopped"),
26 + }
27 + }
28 +
29 + fn services() -> Vec<Service> {
30 + vec![
31 + Service { name: "sshd", state: State::Ok },
32 + Service { name: "unbound", state: State::Ok },
33 + Service { name: "chronyd", state: State::Ok },
34 + Service { name: "caddy", state: State::Warn },
35 + Service { name: "nebula", state: State::Fault },
36 + Service { name: "tlp", state: State::Inactive },
37 + ]
38 + }
39 +
40 + fn body(items: &[Service], selected: usize) -> Paragraph<'_> {
41 + let lines: Vec<Line> = items
42 + .iter()
43 + .enumerate()
44 + .map(|(i, s)| {
45 + let is_sel = i == selected;
46 + let marker = if is_sel { selection::MARKER } else { " " };
47 + let line = Line::from(vec![
48 + Span::raw(format!(" {} ", marker)),
49 + Span::raw(format!("{:<12}", s.name)),
50 + Span::raw(" "),
51 + state_span(&s.state),
52 + ]);
53 + if is_sel {
54 + line.style(selection::selected_style())
55 + } else {
56 + line
57 + }
58 + })
59 + .collect();
60 + Paragraph::new(lines).style(Style::default().bg(palette::BG).fg(palette::FG))
61 + }
62 +
63 + fn main() -> io::Result<()> {
64 + let mut terminal = ratatui::init();
65 + let services = services();
66 + let mut idx: usize = 0;
67 +
68 + let footer = Footer::new([
69 + hint("j/k", "move"),
70 + hint("Enter", "details"),
71 + hint("?", "help"),
72 + hint("q", "quit"),
73 + ]);
74 +
75 + let result: io::Result<()> = loop {
76 + let draw = terminal.draw(|frame| {
77 + let [body_area, foot_area] = layout::split(frame.area());
78 + frame.render_widget(body(&services, idx), body_area);
79 + frame.render_widget(&footer, foot_area);
80 + });
81 + if let Err(e) = draw {
82 + break Err(e);
83 + }
84 +
85 + match event::read() {
86 + Ok(Event::Key(key)) => match classify(key) {
87 + GlobalAction::Quit => break Ok(()),
88 + GlobalAction::Passthrough => match key.code {
89 + KeyCode::Char('j') | KeyCode::Down if idx + 1 < services.len() => {
90 + idx += 1;
91 + }
92 + KeyCode::Char('k') | KeyCode::Up => {
93 + idx = idx.saturating_sub(1);
94 + }
95 + _ => {}
96 + },
97 + _ => {}
98 + },
99 + Ok(_) => {}
100 + Err(e) => break Err(e),
101 + }
102 + };
103 +
104 + ratatui::restore();
105 + result
106 + }
@@ -1,0 +1,44 @@
1 + use crate::palette;
2 + use ratatui::buffer::Buffer;
3 + use ratatui::layout::Rect;
4 + use ratatui::style::Style;
5 + use ratatui::text::{Line, Span};
6 + use ratatui::widgets::{Paragraph, Widget};
7 +
8 + pub struct Hint {
9 + pub key: &'static str,
10 + pub label: &'static str,
11 + }
12 +
13 + pub fn hint(key: &'static str, label: &'static str) -> Hint {
14 + Hint { key, label }
15 + }
16 +
17 + pub struct Footer {
18 + hints: Vec<Hint>,
19 + }
20 +
21 + impl Footer {
22 + pub fn new(hints: impl IntoIterator<Item = Hint>) -> Self {
23 + Self {
24 + hints: hints.into_iter().collect(),
25 + }
26 + }
27 + }
28 +
29 + impl Widget for &Footer {
30 + fn render(self, area: Rect, buf: &mut Buffer) {
31 + let mut spans: Vec<Span> = Vec::with_capacity(self.hints.len() * 4);
32 + for (i, h) in self.hints.iter().enumerate() {
33 + if i > 0 {
34 + spans.push(Span::raw(" "));
35 + }
36 + spans.push(Span::styled(h.key, Style::default().fg(palette::ACCENT)));
37 + spans.push(Span::raw(" "));
38 + spans.push(Span::styled(h.label, Style::default().fg(palette::DIM)));
39 + }
40 + Paragraph::new(Line::from(spans))
41 + .style(Style::default().bg(palette::BG))
42 + .render(area, buf);
43 + }
44 + }
@@ -1,0 +1,22 @@
1 + use crossterm::event::{KeyCode, KeyEvent};
2 +
3 + #[derive(Debug, Clone, Copy, PartialEq, Eq)]
4 + pub enum GlobalAction {
5 + Help,
6 + Quit,
7 + Back,
8 + Filter,
9 + Command,
10 + Passthrough,
11 + }
12 +
13 + pub fn classify(key: KeyEvent) -> GlobalAction {
14 + match key.code {
15 + KeyCode::Char('?') => GlobalAction::Help,
16 + KeyCode::Char('q') => GlobalAction::Quit,
17 + KeyCode::Esc => GlobalAction::Back,
18 + KeyCode::Char('/') => GlobalAction::Filter,
19 + KeyCode::Char(':') => GlobalAction::Command,
20 + _ => GlobalAction::Passthrough,
21 + }
22 + }
@@ -1,0 +1,5 @@
1 + use ratatui::layout::{Constraint, Layout, Rect};
2 +
3 + pub fn split(area: Rect) -> [Rect; 2] {
4 + Layout::vertical([Constraint::Min(0), Constraint::Length(1)]).areas(area)
5 + }
@@ -1,0 +1,10 @@
1 + pub mod footer;
2 + pub mod keys;
3 + pub mod layout;
4 + pub mod palette;
5 + pub mod selection;
6 + pub mod style;
7 +
8 + pub use footer::{Footer, Hint, hint};
9 + pub use keys::{GlobalAction, classify};
10 + pub use selection::{MARKER, selected_style};
@@ -1,0 +1,10 @@
1 + use ratatui::style::Color;
2 +
3 + pub const BG: Color = Color::Rgb(0xf7, 0xf3, 0xee);
4 + pub const FG: Color = Color::Rgb(0x60, 0x5a, 0x52);
5 + pub const DIM: Color = Color::Rgb(0x8c, 0x86, 0x7e);
6 + pub const SELECTION_BG: Color = Color::Rgb(0xdd, 0xd2, 0xbc);
7 + pub const ACCENT: Color = Color::Rgb(0x65, 0x87, 0xbf);
8 + pub const STATE_OK: Color = Color::Rgb(0x5d, 0x7a, 0x55);
9 + pub const STATE_WARN: Color = Color::Rgb(0xa7, 0x8a, 0x5e);
10 + pub const STATE_FAULT: Color = Color::Rgb(0xc4, 0x6a, 0x6a);
@@ -1,0 +1,11 @@
1 + use crate::palette;
2 + use ratatui::style::{Modifier, Style};
3 +
4 + pub const MARKER: &str = "▶";
5 +
6 + pub fn selected_style() -> Style {
7 + Style::default()
8 + .fg(palette::FG)
9 + .bg(palette::SELECTION_BG)
10 + .add_modifier(Modifier::BOLD)
11 + }
@@ -1,0 +1,34 @@
1 + use crate::palette;
2 + use ratatui::style::{Modifier, Style};
3 + use ratatui::text::Span;
4 +
5 + pub fn fg(s: impl Into<String>) -> Span<'static> {
6 + Span::styled(s.into(), Style::default().fg(palette::FG))
7 + }
8 +
9 + pub fn dim(s: impl Into<String>) -> Span<'static> {
10 + Span::styled(s.into(), Style::default().fg(palette::DIM))
11 + }
12 +
13 + pub fn bold(s: impl Into<String>) -> Span<'static> {
14 + Span::styled(
15 + s.into(),
16 + Style::default().fg(palette::FG).add_modifier(Modifier::BOLD),
17 + )
18 + }
19 +
20 + pub fn ok(s: impl Into<String>) -> Span<'static> {
21 + Span::styled(s.into(), Style::default().fg(palette::STATE_OK))
22 + }
23 +
24 + pub fn warn(s: impl Into<String>) -> Span<'static> {
25 + Span::styled(s.into(), Style::default().fg(palette::STATE_WARN))
26 + }
27 +
28 + pub fn fault(s: impl Into<String>) -> Span<'static> {
29 + Span::styled(s.into(), Style::default().fg(palette::STATE_FAULT))
30 + }
31 +
32 + pub fn inactive(s: impl Into<String>) -> Span<'static> {
33 + dim(s)
34 + }
@@ -1,0 +1,20 @@
1 + [package]
2 + name = "sysop"
3 + version = "0.1.0"
4 + edition.workspace = true
5 + rust-version.workspace = true
6 + license.workspace = true
7 +
8 + [[bin]]
9 + name = "sysop"
10 + path = "src/main.rs"
11 +
12 + [dependencies]
13 + sysop-tui.workspace = true
14 + ratatui.workspace = true
15 + crossterm.workspace = true
16 + anyhow.workspace = true
17 + clap.workspace = true
18 + serde.workspace = true
19 + serde_json.workspace = true
20 + toml.workspace = true