Skip to main content

max / goingson

got: render with makeover-tui, drop alloy_tui got took Alloy's design system for two things: the makeover-intents-to-ratatui colour mapping, and a footer widget. The mapping is family-level work every terminal consumer does, so it moved to makeover-tui 0.6.0 behind its theme feature and got takes it from there. The footer is this app's opinion about its own chrome, so it moved into src/statusbar.rs. Only the hint half of the footer came across; got never set the transient status the original also carried. alloy_tui is now absent from the lockfile entirely.
Author: Max Johnson <me@maxj.phd> · 2026-08-01 02:20 UTC
Signed with PGP, not checked
Commit: fe87638714653d96b8ef860a8284bc7436757069
Parent: 0ae049d
5 files changed, +114 insertions, -51 deletions
M Cargo.lock +18 -29
@@ -59,18 +59,6 @@
59 59 source = "registry+https://github.com/rust-lang/crates.io-index"
60 60 checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
61 61
62 - [[package]]
63 - name = "alloy_tui"
64 - version = "4.1.1"
65 - source = "registry+https://github.com/rust-lang/crates.io-index"
66 - checksum = "0fc8efbc6697c89aa62ba9ff338f140e2a2821b0ba11138acbb9f1f2f55fa2bc"
67 - dependencies = [
68 - "makeover",
69 - "makeover-geometry",
70 - "makeover-tui",
71 - "ratatui",
72 - ]
73 -
74 62 [[package]]
75 63 name = "ammonia"
76 64 version = "4.1.4"
@@ -1336,7 +1324,7 @@
1336 1324 "libc",
1337 1325 "option-ext",
1338 1326 "redox_users",
1339 - "windows-sys 0.59.0",
1327 + "windows-sys 0.61.2",
1340 1328 ]
1341 1329
1342 1330 [[package]]
@@ -1586,7 +1574,7 @@
1586 1574 checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
1587 1575 dependencies = [
1588 1576 "libc",
1589 - "windows-sys 0.52.0",
1577 + "windows-sys 0.61.2",
1590 1578 ]
1591 1579
1592 1580 [[package]]
@@ -2292,12 +2280,12 @@
2292 2280 name = "got"
2293 2281 version = "0.5.0"
2294 2282 dependencies = [
2295 - "alloy_tui",
2296 2283 "anyhow",
2297 2284 "chrono",
2298 2285 "goingson-core",
2299 2286 "goingson-db-sqlite",
2300 2287 "makeover",
2288 + "makeover-tui",
2301 2289 "ratatui",
2302 2290 "sqlx",
2303 2291 "tokio",
@@ -3402,10 +3390,11 @@
3402 3390
3403 3391 [[package]]
3404 3392 name = "makeover-tui"
3405 - version = "0.5.1"
3393 + version = "0.6.0"
3406 3394 source = "registry+https://github.com/rust-lang/crates.io-index"
3407 - checksum = "70b1561cdd6db865a4a427d49c4752d4bac53a0e2c6038e869ea95ab808469ff"
3395 + checksum = "64f203bbbfb41450908603551bf23ff39d79d68b5be7b77ce51f3de3e459b397"
3408 3396 dependencies = [
3397 + "makeover",
3409 3398 "makeover-layout",
3410 3399 "ratatui",
3411 3400 ]
@@ -3571,7 +3560,7 @@
3571 3560 "png 0.18.1",
3572 3561 "serde",
3573 3562 "thiserror 2.0.19",
3574 - "windows-sys 0.60.2",
3563 + "windows-sys 0.61.2",
3575 3564 ]
3576 3565
3577 3566 [[package]]
@@ -3704,7 +3693,7 @@
3704 3693 source = "registry+https://github.com/rust-lang/crates.io-index"
3705 3694 checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
3706 3695 dependencies = [
3707 - "windows-sys 0.59.0",
3696 + "windows-sys 0.61.2",
3708 3697 ]
3709 3698
3710 3699 [[package]]
@@ -5017,7 +5006,7 @@
5017 5006 "errno",
5018 5007 "libc",
5019 5008 "linux-raw-sys",
5020 - "windows-sys 0.52.0",
5009 + "windows-sys 0.61.2",
5021 5010 ]
5022 5011
5023 5012 [[package]]
@@ -5074,7 +5063,7 @@
5074 5063 "security-framework",
5075 5064 "security-framework-sys",
5076 5065 "webpki-root-certs",
5077 - "windows-sys 0.52.0",
5066 + "windows-sys 0.61.2",
5078 5067 ]
5079 5068
5080 5069 [[package]]
@@ -5095,7 +5084,7 @@
5095 5084 "security-framework",
5096 5085 "security-framework-sys",
5097 5086 "webpki-root-certs",
5098 - "windows-sys 0.52.0",
5087 + "windows-sys 0.61.2",
5099 5088 ]
5100 5089
5101 5090 [[package]]
@@ -5610,7 +5599,7 @@
5610 5599 checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
5611 5600 dependencies = [
5612 5601 "libc",
5613 - "windows-sys 0.60.2",
5602 + "windows-sys 0.61.2",
5614 5603 ]
5615 5604
5616 5605 [[package]]
@@ -6516,7 +6505,7 @@
6516 6505 "serde_with",
6517 6506 "swift-rs",
6518 6507 "thiserror 2.0.19",
6519 - "toml 0.9.12+spec-1.1.0",
6508 + "toml 1.1.3+spec-1.1.0",
6520 6509 "url",
6521 6510 "urlpattern",
6522 6511 "uuid",
@@ -6555,7 +6544,7 @@
6555 6544 "getrandom 0.4.3",
6556 6545 "once_cell",
6557 6546 "rustix",
6558 - "windows-sys 0.52.0",
6547 + "windows-sys 0.61.2",
6559 6548 ]
6560 6549
6561 6550 [[package]]
@@ -6577,7 +6566,7 @@
6577 6566 "parking_lot",
6578 6567 "rustix",
6579 6568 "signal-hook",
6580 - "windows-sys 0.60.2",
6569 + "windows-sys 0.61.2",
6581 6570 ]
6582 6571
6583 6572 [[package]]
@@ -7076,7 +7065,7 @@
7076 7065 "png 0.18.1",
7077 7066 "serde",
7078 7067 "thiserror 2.0.19",
7079 - "windows-sys 0.60.2",
7068 + "windows-sys 0.61.2",
7080 7069 ]
7081 7070
7082 7071 [[package]]
@@ -7111,7 +7100,7 @@
7111 7100 dependencies = [
7112 7101 "memoffset",
7113 7102 "tempfile",
7114 - "windows-sys 0.60.2",
7103 + "windows-sys 0.61.2",
7115 7104 ]
7116 7105
7117 7106 [[package]]
@@ -7650,7 +7639,7 @@
7650 7639 source = "registry+https://github.com/rust-lang/crates.io-index"
7651 7640 checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
7652 7641 dependencies = [
7653 - "windows-sys 0.52.0",
7642 + "windows-sys 0.61.2",
7654 7643 ]
7655 7644
7656 7645 [[package]]
M Cargo.toml +1 -1
@@ -83,7 +83,7 @@
83 83 notify = "8.2"
84 84 notify-debouncer-mini = "0.7"
85 85 makeover = "2.4.1"
86 - alloy_tui = "4.1.0"
86 + makeover-tui = "0.6.0"
87 87 toml = "1.1"
88 88
89 89 # Enums
@@ -11,9 +11,10 @@
11 11 goingson-core = { workspace = true, features = ["sqlx-sqlite"] }
12 12 goingson-db-sqlite = { workspace = true }
13 13
14 - # Alloy's themed-ratatui layer, from crates.io. MIT, carved out of the
15 - # otherwise GPL alloy repo so it can be linked from here.
16 - alloy_tui.workspace = true
14 + # The family's terminal renderer, from crates.io. MIT. The `theme` feature is
15 + # the makeover-intents-to-ratatui-colours mapping; the footer this app draws
16 + # with them is its own, in src/statusbar.rs.
17 + makeover-tui = { workspace = true, features = ["theme"] }
17 18
18 19 tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
19 20 sqlx = { workspace = true, features = ["sqlite"] }
@@ -1,6 +1,6 @@
1 1 //! got, a read-first terminal cockpit over GoingsOn's local `goingson.db`.
2 2 //!
3 - //! First consumer of `alloy_tui` (Alloy's themed-ratatui layer). Opens the same
3 + //! Rendered with `makeover-tui`, the family's terminal renderer. Opens the same
4 4 //! SQLite file the desktop app uses as a peer reader, exactly like `go-mcp`
5 5 //! (see `crates/go-mcp`): links the domain crates directly, resolves the same
6 6 //! default DB path, and reads through the normal repository layer.
@@ -19,7 +19,9 @@
19 19 use std::io::{self, Stdout};
20 20 use std::path::PathBuf;
21 21
22 - use alloy_tui::{AlloyStatusBar, Theme, hint};
22 + mod statusbar;
23 +
24 + use crate::statusbar::{StatusBar, hint};
23 25 use anyhow::{Context, Result};
24 26 use chrono::{Datelike, Duration, Local, NaiveDate, TimeZone, Utc};
25 27 use goingson_core::{
@@ -31,6 +33,7 @@
31 33 SqliteContactRepository, SqliteEventRepository, SqliteProjectRepository, SqliteTaskRepository,
32 34 init_pool,
33 35 };
36 + use makeover_tui::Theme;
34 37 use ratatui::Terminal;
35 38 use ratatui::crossterm::event::{self, Event as CtEvent, KeyCode, KeyEventKind};
36 39 use ratatui::crossterm::{execute, terminal};
@@ -49,7 +52,7 @@
49 52
50 53 /// The theme `got` renders in.
51 54 ///
52 - /// `alloy_tui` ships no built-in palette on purpose: a missing or malformed
55 + /// `makeover-tui` ships no built-in palette on purpose: a missing or malformed
53 56 /// theme is an error the user sees, not something papered over by rendering in
54 57 /// colors that exist nowhere in the theme files. So load one. A theme dropped
55 58 /// into `~/.config/goingson/themes/` wins; otherwise use the set `makeover`
@@ -667,7 +670,7 @@
667 670 draw_pane(f, app, p, body[2]);
668 671
669 672 f.render_widget(
670 - AlloyStatusBar::new(
673 + StatusBar::new(
671 674 &p,
672 675 [
673 676 hint("Tab", "group"),
@@ -682,7 +685,7 @@
682 685 }
683 686
684 687 /// The Work / Time / Messages tab strip, active group in accent.
685 - fn group_tabs(p: alloy_tui::Theme, active: Group) -> Line<'static> {
688 + fn group_tabs(p: Theme, active: Group) -> Line<'static> {
686 689 let mut spans = Vec::new();
687 690 for (i, g) in Group::ALL.iter().enumerate() {
688 691 if i > 0 {
@@ -701,7 +704,7 @@
701 704 }
702 705
703 706 /// The sub-tab strip of panes within a group; the active pane is bracketed.
704 - fn pane_tabs(p: alloy_tui::Theme, group: Group, active: usize) -> Line<'static> {
707 + fn pane_tabs(p: Theme, group: Group, active: usize) -> Line<'static> {
705 708 let mut spans = Vec::new();
706 709 for (i, pane) in group.panes().iter().enumerate() {
707 710 if i > 0 {
@@ -725,12 +728,7 @@
725 728 }
726 729
727 730 /// Render the active pane's body into `area`.
728 - fn draw_pane(
729 - f: &mut ratatui::Frame,
730 - app: &mut App,
731 - p: alloy_tui::Theme,
732 - area: ratatui::layout::Rect,
733 - ) {
731 + fn draw_pane(f: &mut ratatui::Frame, app: &mut App, p: Theme, area: ratatui::layout::Rect) {
734 732 match app.current_pane() {
735 733 Pane::Tasks => {
736 734 let items: Vec<ListItem> = app.rows.iter().map(|r| row_item(p, r)).collect();
@@ -788,7 +786,7 @@
788 786 }
789 787 }
790 788
791 - fn row_item<'a>(p: alloy_tui::Theme, row: &'a Row) -> ListItem<'a> {
789 + fn row_item<'a>(p: Theme, row: &'a Row) -> ListItem<'a> {
792 790 match row {
793 791 Row::Header { name, open } => {
794 792 let line = Line::from(vec![
@@ -828,7 +826,7 @@
828 826 }
829 827 }
830 828
831 - fn project_item<'a>(p: alloy_tui::Theme, row: &'a ProjectRow) -> ListItem<'a> {
829 + fn project_item<'a>(p: Theme, row: &'a ProjectRow) -> ListItem<'a> {
832 830 let status_color = match row.status {
833 831 ProjectStatus::Active => p.status_success,
834 832 ProjectStatus::OnHold => p.status_warning,
@@ -863,7 +861,7 @@
863 861 ListItem::new(line)
864 862 }
865 863
866 - fn event_item<'a>(p: alloy_tui::Theme, row: &'a EventRow) -> ListItem<'a> {
864 + fn event_item<'a>(p: Theme, row: &'a EventRow) -> ListItem<'a> {
867 865 match row {
868 866 EventRow::DayHeader { label, count } => {
869 867 let line = Line::from(vec![
@@ -909,7 +907,7 @@
909 907 }
910 908 }
911 909
912 - fn contact_item<'a>(p: alloy_tui::Theme, c: &'a Contact) -> ListItem<'a> {
910 + fn contact_item<'a>(p: Theme, c: &'a Contact) -> ListItem<'a> {
913 911 let mut spans = vec![
914 912 Span::raw(" "),
915 913 Span::styled(
@@ -941,7 +939,7 @@
941 939 }
942 940
943 941 /// A bold section header line for the Timer pane.
944 - fn section(p: alloy_tui::Theme, title: &str) -> Line<'static> {
942 + fn section(p: Theme, title: &str) -> Line<'static> {
945 943 Line::from(Span::styled(
946 944 format!(" {title}"),
947 945 Style::default()
@@ -952,7 +950,7 @@
952 950
953 951 /// Render the Timer pane as a compact dashboard: active timer, this week's
954 952 /// tracked breakdown, and recent sessions.
955 - fn timer_lines(p: alloy_tui::Theme, tv: &TimerView) -> Vec<Line<'static>> {
953 + fn timer_lines(p: Theme, tv: &TimerView) -> Vec<Line<'static>> {
956 954 let mut lines = Vec::new();
957 955
958 956 match &tv.active {
@@ -1,0 +1,75 @@
1 + //! The one-row footer: key hints along the bottom.
2 + //!
3 + //! Was `alloy_tui::AlloyStatusBar`. It moved here when `got` stopped depending
4 + //! on Alloy's design system: the colour mapping it needs is family-level and
5 + //! now lives in `makeover-tui`, but a footer's layout is this app's opinion
6 + //! about its own chrome, not something two unrelated programs should share.
7 + //!
8 + //! Only the hint half came across. The original also carried a right-aligned
9 + //! transient status slot; `got` never set one, and an unused builder method is
10 + //! a worse thing to keep than fifteen lines are to rewrite if it is ever
11 + //! wanted.
12 +
13 + use makeover_tui::Theme;
14 + use ratatui::buffer::Buffer;
15 + use ratatui::layout::Rect;
16 + use ratatui::style::Style;
17 + use ratatui::text::{Line, Span};
18 + use ratatui::widgets::{Paragraph, Widget};
19 +
20 + /// A footer key hint: the key, and what it does.
21 + pub(crate) struct Hint {
22 + pub(crate) key: &'static str,
23 + pub(crate) label: &'static str,
24 + }
25 +
26 + /// Terse constructor, so hint lists read as data at the call site:
27 + /// `[hint("Tab", "group"), hint("q", "quit")]`.
28 + pub(crate) const fn hint(key: &'static str, label: &'static str) -> Hint {
29 + Hint { key, label }
30 + }
31 +
32 + pub(crate) struct StatusBar<'a> {
33 + theme: &'a Theme,
34 + hints: Vec<Hint>,
35 + }
36 +
37 + impl<'a> StatusBar<'a> {
38 + pub(crate) fn new(theme: &'a Theme, hints: impl IntoIterator<Item = Hint>) -> Self {
39 + Self {
40 + theme,
41 + hints: hints.into_iter().collect(),
42 + }
43 + }
44 + }
45 +
46 + impl Widget for StatusBar<'_> {
47 + fn render(self, area: Rect, buf: &mut Buffer) {
48 + // The bar reads as its own band, so the whole row takes the sunken
49 + // surface first and every span below is drawn onto it.
50 + let base = Style::default().bg(self.theme.surface_sunken);
51 + Paragraph::new("").style(base).render(area, buf);
52 +
53 + let mut spans: Vec<Span> = Vec::with_capacity(self.hints.len() * 3);
54 + for (i, h) in self.hints.iter().enumerate() {
55 + if i > 0 {
56 + spans.push(Span::raw(" "));
57 + }
58 + // The key takes the action colour and the label is muted: a key
59 + // hint IS the actionable element, which is the one place accent on
60 + // text is not decoration.
61 + spans.push(Span::styled(
62 + h.key,
63 + Style::default().fg(self.theme.action_primary),
64 + ));
65 + spans.push(Span::raw(" "));
66 + spans.push(Span::styled(
67 + h.label,
68 + Style::default().fg(self.theme.content_muted),
69 + ));
70 + }
71 + Paragraph::new(Line::from(spans))
72 + .style(base)
73 + .render(area, buf);
74 + }
75 + }