From ed15acff3dcb0ac0f28e2da2b399a537868054e5 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Thu, 8 Jan 2026 19:34:06 +0000 Subject: [PATCH 01/29] fix: somehow this existing fix was reverted at some point, oops --- editor.v | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/editor.v b/editor.v index 6c398492..1b645ef8 100644 --- a/editor.v +++ b/editor.v @@ -206,10 +206,9 @@ fn (m EditorModel) view(mut ctx tea.Context) { ctx.draw_text(0, y, '│') } ctx.reset_color() + ctx.push_offset(tea.Offset{ x: 1 }) } - ctx.push_offset(tea.Offset{ x: 1 }) - for y, l in m.lines { ctx.draw_text(0, y, l.replace('\t', ' ')) } From a93f4583092c30a134c91811a5fa88f99b7d4b86 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Thu, 8 Jan 2026 19:34:33 +0000 Subject: [PATCH 02/29] feat: pass in relevant options/external settings into splash model --- petal.v | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/petal.v b/petal.v index a972e59d..4118df7b 100644 --- a/petal.v +++ b/petal.v @@ -2,8 +2,8 @@ module main import os import tauraamui.bobatea as tea -import cfg import theme +import cfg import palette const dot = '•' @@ -26,7 +26,7 @@ fn PetalModel.new(config cfg.Config) PetalModel { config: config theme: config.theme first_frame: true - active_screen: SplashScreenModel.new() + active_screen: SplashScreenModel.new(leader_key: config.leader_key, theme: config.theme) } } From af4fafd3ef283308268a0cbef71e77846bad28fc Mon Sep 17 00:00:00 2001 From: tauraamui Date: Thu, 8 Jan 2026 19:35:11 +0000 Subject: [PATCH 03/29] feat: pass in theme set to splash screen model --- splash_screen.v | 97 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 21 deletions(-) diff --git a/splash_screen.v b/splash_screen.v index f0e910a8..549a63a9 100644 --- a/splash_screen.v +++ b/splash_screen.v @@ -4,6 +4,7 @@ import math import os import tauraamui.bobatea as tea import palette +import theme const gitcommit_hash = $embed_file('.githash').to_string() @@ -18,9 +19,16 @@ mut: width int } +@[params] +struct SplashScreenOptions { + leader_key string + theme theme.Theme +} + struct SplashScreenModel { leader_key string logo SplashLogo + theme theme.Theme mut: tmux_wrapped bool leader_mode bool @@ -28,9 +36,10 @@ mut: dialog_model ?DebuggableModel } -fn SplashScreenModel.new() SplashScreenModel { +fn SplashScreenModel.new(opts SplashScreenOptions) SplashScreenModel { return SplashScreenModel{ - leader_key: ';' + leader_key: opts.leader_key + theme: opts.theme logo: SplashLogo{ data: logo_contents.to_string().split_into_lines() } @@ -43,7 +52,7 @@ fn (mut m SplashScreenModel) init() ?tea.Cmd { fn (mut m SplashScreenModel) handle_escape() (tea.Model, ?tea.Cmd) { if !m.leader_mode { - return SplashScreenModel{}, tea.quit + return m.clone(), tea.quit } m.leader_mode = false m.leader_data = '' @@ -115,7 +124,7 @@ fn (mut m SplashScreenModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { else { match msg.string() { 'q' { - return SplashScreenModel{}, tea.quit + return m.clone(), tea.quit } m.leader_key { if !m.leader_mode { @@ -173,14 +182,22 @@ fn (mut m SplashScreenModel) reset_leader_mode() { fn (m SplashScreenModel) view(mut ctx tea.Context) { render_version_label(mut ctx, '${version} - (#${build_id})') - render_logo_and_help_centered_and_stacked(mut ctx, m.logo, m.leader_mode, m.leader_data) + render_logo_and_help_centered_and_stacked( + mut ctx, + logo: m.logo, + in_leader_mode: m.leader_mode, + leader_data: m.leader_data + petal_pink: palette.petal_pink_color + petal_green: palette.petal_green_color + backdrop_color: if m.theme.name == "dark" { none } else { theme.dark_theme.bg_color } + ) render_help_keybinds(mut ctx) offset_from_id := ctx.push_offset(tea.Offset{ y: ctx.window_height() - 1 }) defer { ctx.clear_offsets_from(offset_from_id) } if m.leader_mode { ctx.set_color(palette.subtle_text_fg_color) - leader_data := ";" + m.leader_data + leader_data := m.leader_key + m.leader_data ctx.draw_text(ctx.window_width() - tea.visible_len(leader_data) - 1, 0, leader_data) ctx.reset_color() } @@ -214,7 +231,17 @@ fn render_help_keybinds(mut ctx tea.Context) { ctx.reset_color() } -fn render_logo_and_help_centered_and_stacked(mut ctx tea.Context, logo SplashLogo, in_leader_mode bool, leader_data string) { +@[params] +struct RenderLogoAndHelpParams { + RenderLogoParams + in_leader_mode bool + leader_data string +} + +fn render_logo_and_help_centered_and_stacked( + mut ctx tea.Context, + opts RenderLogoAndHelpParams +) { // NOTE(tauraamui) [25/10/2025]: all following contents to be padded from top of window base_offset_y := f64(ctx.window_height()) * 0.1 offset_from_id := ctx.push_offset(tea.Offset{ @@ -223,22 +250,22 @@ fn render_logo_and_help_centered_and_stacked(mut ctx tea.Context, logo SplashLog }) defer { ctx.clear_offsets_from(offset_from_id) } - ctx.push_offset(render_logo(mut ctx, logo)) + ctx.push_offset(render_logo(mut ctx, opts.RenderLogoParams)) ctx.push_offset(render_lilly_name(mut ctx)) - ctx.push_offset(render_keybinds_list(mut ctx, in_leader_mode, leader_data)) - render_copyright_footer(mut ctx) + ctx.push_offset(render_keybinds_list(mut ctx, opts.in_leader_mode, opts.leader_data)) + render_copyright_footer(mut ctx, opts.petal_pink) } const copyright_footer_label = 'the lilly editor authors © (made with ${[u8(0xf0), 0x9f, 0x92, 0x95].bytestr()})' -fn render_copyright_footer(mut ctx tea.Context) { +fn render_copyright_footer(mut ctx tea.Context, petal_pink tea.Color) { offset_from_id := ctx.push_offset(tea.Offset{ x: -(tea.visible_len(copyright_footer_label) / 2) y: 1 }) defer { ctx.clear_offsets_from(offset_from_id) } - ctx.set_color(palette.petal_pink_color) + ctx.set_color(petal_pink) ctx.draw_text(0, 0, copyright_footer_label) ctx.reset_color() } @@ -307,7 +334,13 @@ fn render_lilly_name(mut ctx tea.Context) tea.Offset { return ctx.compact_offsets_from(offset_from_id) } -fn render_logo(mut ctx tea.Context, logo SplashLogo) tea.Offset { +@[params] +struct RenderLogoParams { + RenderLogoLineParams + logo SplashLogo +} + +fn render_logo(mut ctx tea.Context, opts RenderLogoParams) tea.Offset { // NOTE(tauraamui) [25/10/25]: this can be reduced to a style container which basically // makes the y offset be down by 10% of the parent. in this // case the parent is just the window itself, but could be anything @@ -316,41 +349,63 @@ fn render_logo(mut ctx tea.Context, logo SplashLogo) tea.Offset { // line at once with the light pink color set, but some lines of the logo // contain both green and pink, so they need to be rendered per character // with the correct palette option/fg set - ctx.set_color(palette.petal_pink_color) + // ctx.set_color(palette.petal_pink_color) + ctx.set_color(opts.petal_pink) offset_from_id := ctx.push_offset(tea.Offset{}) defer { ctx.clear_offsets_from(offset_from_id) } - for _, l in logo.data { + for _, l in opts.logo.data { // NOTE(tauraamui) [26/10/25] by splitting these offset pushes into two separate calls // we're only continuously removing the offset for the X position // each loop iter, so by the end `compact_offsets` is a combination of // the full height of the logo once its been completely rendered ctx.push_offset(tea.Offset{ y: 1 }) ctx.push_offset(tea.Offset{ x: -(tea.visible_len(l) / 2) }) - render_logo_line(mut ctx, l) + render_logo_line(mut ctx, l, opts.RenderLogoLineParams) ctx.pop_offset() } ctx.reset_color() return ctx.compact_offsets_from(offset_from_id) } -fn render_logo_line(mut ctx tea.Context, line string) { +@[params] +struct RenderLogoLineParams { + petal_pink tea.Color + petal_green tea.Color + backdrop_color ?tea.Color +} + +fn render_logo_line(mut ctx tea.Context, line string, opts RenderLogoLineParams) { if has_colouring_directives(line) { - render_logo_line_char_by_char(mut ctx, line) + render_logo_line_char_by_char(mut ctx, line, opts.petal_pink, opts.petal_green, opts.backdrop_color) return } + if bg_color := opts.backdrop_color { + ctx.set_bg_color(bg_color) + defer { ctx.reset_bg_color() } + } ctx.draw_text(0, 0, line) } -fn render_logo_line_char_by_char(mut ctx tea.Context, line string) { +fn render_logo_line_char_by_char( + mut ctx tea.Context, + line string, + petal_pink tea.Color, + petal_green tea.Color, + backdrop_color ?tea.Color +) { + if bg_color := backdrop_color { + ctx.set_bg_color(bg_color) + defer { ctx.reset_bg_color() } + } for j, c in line.runes() { mut to_draw := '${c}' if to_draw == 'g' { to_draw = ' ' - ctx.set_color(palette.petal_green_color) + ctx.set_color(petal_green) } if to_draw == 'p' { to_draw = ' ' - ctx.set_color(palette.petal_pink_color) + ctx.set_color(petal_pink) } ctx.draw_text(j, 0, to_draw) } From 386b5ea7692570a8d6aceb2e10c9baf68fff11ae Mon Sep 17 00:00:00 2001 From: tauraamui Date: Thu, 8 Jan 2026 19:35:43 +0000 Subject: [PATCH 04/29] misc: temp swap theme to light for now --- main.v | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.v b/main.v index 314eee5f..1c6f78e1 100644 --- a/main.v +++ b/main.v @@ -4,8 +4,8 @@ import tauraamui.bobatea as tea import cfg fn main() { - // config := cfg.Config.new(load_from_path: none).set_theme(cfg.light_theme_name) - config := cfg.Config.new(load_from_path: none) + config := cfg.Config.new(load_from_path: none).set_theme(cfg.light_theme_name) + // config := cfg.Config.new(load_from_path: none) mut petal_model := PetalModel.new(config) mut app := tea.new_program(mut petal_model) From f5876c9e6eb0c4f271cb76de3f4114bf32602a46 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Thu, 8 Jan 2026 19:40:05 +0000 Subject: [PATCH 05/29] feat: set backdrop color from fg --- main.v | 4 ++-- splash_screen.v | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main.v b/main.v index 1c6f78e1..314eee5f 100644 --- a/main.v +++ b/main.v @@ -4,8 +4,8 @@ import tauraamui.bobatea as tea import cfg fn main() { - config := cfg.Config.new(load_from_path: none).set_theme(cfg.light_theme_name) - // config := cfg.Config.new(load_from_path: none) + // config := cfg.Config.new(load_from_path: none).set_theme(cfg.light_theme_name) + config := cfg.Config.new(load_from_path: none) mut petal_model := PetalModel.new(config) mut app := tea.new_program(mut petal_model) diff --git a/splash_screen.v b/splash_screen.v index 549a63a9..72a24115 100644 --- a/splash_screen.v +++ b/splash_screen.v @@ -189,7 +189,7 @@ fn (m SplashScreenModel) view(mut ctx tea.Context) { leader_data: m.leader_data petal_pink: palette.petal_pink_color petal_green: palette.petal_green_color - backdrop_color: if m.theme.name == "dark" { none } else { theme.dark_theme.bg_color } + backdrop_color: m.theme.fg_color ) render_help_keybinds(mut ctx) From 25df5139ef2cfb6fe4ab8cede3e9b542787506d8 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Thu, 8 Jan 2026 19:58:26 +0000 Subject: [PATCH 06/29] chore: move petal shades to theme from palette collection --- main.v | 4 ++-- splash_screen.v | 17 +++-------------- theme.v | 14 +++++++++++++- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/main.v b/main.v index 314eee5f..1c6f78e1 100644 --- a/main.v +++ b/main.v @@ -4,8 +4,8 @@ import tauraamui.bobatea as tea import cfg fn main() { - // config := cfg.Config.new(load_from_path: none).set_theme(cfg.light_theme_name) - config := cfg.Config.new(load_from_path: none) + config := cfg.Config.new(load_from_path: none).set_theme(cfg.light_theme_name) + // config := cfg.Config.new(load_from_path: none) mut petal_model := PetalModel.new(config) mut app := tea.new_program(mut petal_model) diff --git a/splash_screen.v b/splash_screen.v index 72a24115..811a86f9 100644 --- a/splash_screen.v +++ b/splash_screen.v @@ -187,9 +187,8 @@ fn (m SplashScreenModel) view(mut ctx tea.Context) { logo: m.logo, in_leader_mode: m.leader_mode, leader_data: m.leader_data - petal_pink: palette.petal_pink_color - petal_green: palette.petal_green_color - backdrop_color: m.theme.fg_color + petal_pink: m.theme.petal_pink + petal_green: m.theme.petal_green ) render_help_keybinds(mut ctx) @@ -371,18 +370,13 @@ fn render_logo(mut ctx tea.Context, opts RenderLogoParams) tea.Offset { struct RenderLogoLineParams { petal_pink tea.Color petal_green tea.Color - backdrop_color ?tea.Color } fn render_logo_line(mut ctx tea.Context, line string, opts RenderLogoLineParams) { if has_colouring_directives(line) { - render_logo_line_char_by_char(mut ctx, line, opts.petal_pink, opts.petal_green, opts.backdrop_color) + render_logo_line_char_by_char(mut ctx, line, opts.petal_pink, opts.petal_green) return } - if bg_color := opts.backdrop_color { - ctx.set_bg_color(bg_color) - defer { ctx.reset_bg_color() } - } ctx.draw_text(0, 0, line) } @@ -391,12 +385,7 @@ fn render_logo_line_char_by_char( line string, petal_pink tea.Color, petal_green tea.Color, - backdrop_color ?tea.Color ) { - if bg_color := backdrop_color { - ctx.set_bg_color(bg_color) - defer { ctx.reset_bg_color() } - } for j, c in line.runes() { mut to_draw := '${c}' if to_draw == 'g' { diff --git a/theme.v b/theme.v index a6d2cd4c..47b49aba 100644 --- a/theme.v +++ b/theme.v @@ -6,26 +6,38 @@ import palette pub const dark_theme_name = "dark" pub const light_theme_name = "light" -pub struct Theme{ +pub struct Theme { pub: name string @[required] bg_color tea.Color @[required] fg_color tea.Color + active_split_divider_color tea.Color @[required] inactive_split_divider_color tea.Color @[required] + + petal_pink tea.Color + petal_green tea.Color } pub const dark_theme = Theme{ name: "dark" bg_color: palette.matte_black_bg_color + active_split_divider_color: palette.petal_pink_color inactive_split_divider_color: palette.status_bar_bg_color + + petal_pink: tea.Color.ansi(219) + petal_green: tea.Color.ansi(84) } pub const light_theme = Theme{ name: "light" bg_color: palette.matte_white_fg_color + active_split_divider_color: palette.petal_pink_color inactive_split_divider_color: palette.status_bar_bg_color + + petal_pink: tea.Color.ansi(206) + petal_green: tea.Color.ansi(76) } From 30d522ad42856b1666b72198e6800c2ecfdc49f3 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Fri, 9 Jan 2026 17:21:47 +0000 Subject: [PATCH 07/29] fix: set fg color from inverse of selected file path bg color --- file_picker.v | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/file_picker.v b/file_picker.v index a2c3abd1..ead780d9 100644 --- a/file_picker.v +++ b/file_picker.v @@ -287,11 +287,14 @@ fn render_file_path_line(mut ctx tea.Context, file_path string, width int, heigh mut prefix := ' ' if is_selected { prefix = '» ' - ctx.set_bg_color(palette.selected_highlight_bg_color) + selected_path_highlight_bg_color := palette.selected_highlight_bg_color + ctx.set_color(palette.fg_color(selected_path_highlight_bg_color)) + ctx.set_bg_color(selected_path_highlight_bg_color) } ctx.draw_rect(0, height - 3, width - 2, 1) ctx.draw_text(0, height - 3, prefix + file_path.replace(os.getwd(), '.')) if is_selected { + ctx.reset_color() ctx.reset_bg_color() } ctx.push_offset(tea.Offset{ y: -1 }) From c075a57d8222b0f7879a5e91b397ae43e24d4b98 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Fri, 9 Jan 2026 17:22:09 +0000 Subject: [PATCH 08/29] chore(ui): adjust light theme's petal pink and background shades --- editor_workspace.v | 1 - theme.v | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/editor_workspace.v b/editor_workspace.v index a0b28f30..1f53e9b9 100644 --- a/editor_workspace.v +++ b/editor_workspace.v @@ -365,7 +365,6 @@ fn (mut m EditorWorkspaceModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { } CheckIfTMUXWrappedMsg { m.tmux_wrapped = os.getenv("TMUX").len > 0 - assert m.tmux_wrapped } OpenDialogMsg { mut d_model := msg.model diff --git a/theme.v b/theme.v index 47b49aba..c0ba70c9 100644 --- a/theme.v +++ b/theme.v @@ -32,12 +32,12 @@ pub const dark_theme = Theme{ pub const light_theme = Theme{ name: "light" - bg_color: palette.matte_white_fg_color + bg_color: tea.Color.ansi(231) active_split_divider_color: palette.petal_pink_color inactive_split_divider_color: palette.status_bar_bg_color - petal_pink: tea.Color.ansi(206) + petal_pink: tea.Color.ansi(200) petal_green: tea.Color.ansi(76) } From 6e8aad3ac53fcf0d55c4910e5303ecbad932a9f4 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Fri, 9 Jan 2026 17:44:17 +0000 Subject: [PATCH 09/29] chore(theme): move green choice for keybinds tow theme's petal green --- splash_screen.v | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/splash_screen.v b/splash_screen.v index 811a86f9..ae600d0e 100644 --- a/splash_screen.v +++ b/splash_screen.v @@ -251,7 +251,7 @@ fn render_logo_and_help_centered_and_stacked( ctx.push_offset(render_logo(mut ctx, opts.RenderLogoParams)) ctx.push_offset(render_lilly_name(mut ctx)) - ctx.push_offset(render_keybinds_list(mut ctx, opts.in_leader_mode, opts.leader_data)) + ctx.push_offset(render_keybinds_list(mut ctx, opts.in_leader_mode, opts.leader_data, opts.petal_green)) render_copyright_footer(mut ctx, opts.petal_pink) } @@ -282,9 +282,9 @@ const disabled_command_help = [ ] const pending_match_color = tea.Color.ansi(244) -const closest_match_color = palette.petal_green_color +// const closest_match_color = palette.petal_green_color -fn render_keybinds_list(mut ctx tea.Context, in_leader_mode bool, leader_data string) tea.Offset { +fn render_keybinds_list(mut ctx tea.Context, in_leader_mode bool, leader_data string, closest_match_color tea.Color) tea.Offset { offset_from_id := ctx.push_offset(tea.Offset{ y: 1 }) defer { ctx.clear_offsets_from(offset_from_id) } From 24b751ae780d8d66876f9dc9e22a6d72df775228 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Sat, 10 Jan 2026 00:13:02 +0000 Subject: [PATCH 10/29] chore(theme): pass in light grey to use for help fg use --- splash_screen.v | 6 +++--- theme.v | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/splash_screen.v b/splash_screen.v index ae600d0e..44c3af55 100644 --- a/splash_screen.v +++ b/splash_screen.v @@ -181,7 +181,7 @@ fn (mut m SplashScreenModel) reset_leader_mode() { } fn (m SplashScreenModel) view(mut ctx tea.Context) { - render_version_label(mut ctx, '${version} - (#${build_id})') + render_version_label(mut ctx, '${version} - (#${build_id})', m.theme.light_grey) render_logo_and_help_centered_and_stacked( mut ctx, logo: m.logo, @@ -215,8 +215,8 @@ fn (m SplashScreenModel) view(mut ctx tea.Context) { } } -fn render_version_label(mut ctx tea.Context, version_label string) { - ctx.set_color(palette.help_fg_color) +fn render_version_label(mut ctx tea.Context, version_label string, help_fg_color tea.Color) { + ctx.set_color(help_fg_color) ctx.draw_text(1, 0, version_label) ctx.reset_color() } diff --git a/theme.v b/theme.v index c0ba70c9..e5767b34 100644 --- a/theme.v +++ b/theme.v @@ -17,6 +17,8 @@ pub: petal_pink tea.Color petal_green tea.Color + + light_grey tea.Color } pub const dark_theme = Theme{ @@ -28,6 +30,8 @@ pub const dark_theme = Theme{ petal_pink: tea.Color.ansi(219) petal_green: tea.Color.ansi(84) + + light_grey: tea.Color.ansi(241) } pub const light_theme = Theme{ @@ -39,5 +43,7 @@ pub const light_theme = Theme{ petal_pink: tea.Color.ansi(200) petal_green: tea.Color.ansi(76) + + light_grey: tea.Color.ansi(241) } From 235ff6a3762d63e41e12e76bb30c75840796f038 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Sat, 10 Jan 2026 01:29:52 +0000 Subject: [PATCH 11/29] chore: provide better name for theme matte grey option --- theme.v | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/theme.v b/theme.v index e5767b34..bb592fcc 100644 --- a/theme.v +++ b/theme.v @@ -18,7 +18,7 @@ pub: petal_pink tea.Color petal_green tea.Color - light_grey tea.Color + subtle_light_grey tea.Color } pub const dark_theme = Theme{ @@ -31,7 +31,7 @@ pub const dark_theme = Theme{ petal_pink: tea.Color.ansi(219) petal_green: tea.Color.ansi(84) - light_grey: tea.Color.ansi(241) + subtle_light_grey: tea.Color.ansi(241) } pub const light_theme = Theme{ @@ -44,6 +44,6 @@ pub const light_theme = Theme{ petal_pink: tea.Color.ansi(200) petal_green: tea.Color.ansi(76) - light_grey: tea.Color.ansi(241) + subtle_light_grey: tea.Color.ansi(248) } From fb7befd5554ffc3c05c5cff6c088f0b461de3ccd Mon Sep 17 00:00:00 2001 From: tauraamui Date: Sat, 10 Jan 2026 01:30:12 +0000 Subject: [PATCH 12/29] chore: pass in shared theme def for subtle light grey --- splash_screen.v | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/splash_screen.v b/splash_screen.v index 44c3af55..0e5a4670 100644 --- a/splash_screen.v +++ b/splash_screen.v @@ -181,7 +181,7 @@ fn (mut m SplashScreenModel) reset_leader_mode() { } fn (m SplashScreenModel) view(mut ctx tea.Context) { - render_version_label(mut ctx, '${version} - (#${build_id})', m.theme.light_grey) + render_version_label(mut ctx, '${version} - (#${build_id})', m.theme.subtle_light_grey) render_logo_and_help_centered_and_stacked( mut ctx, logo: m.logo, @@ -189,8 +189,9 @@ fn (m SplashScreenModel) view(mut ctx tea.Context) { leader_data: m.leader_data petal_pink: m.theme.petal_pink petal_green: m.theme.petal_green + disabled_help_fg_color: m.theme.subtle_light_grey ) - render_help_keybinds(mut ctx) + render_help_keybinds(mut ctx, m.theme.subtle_light_grey) offset_from_id := ctx.push_offset(tea.Offset{ y: ctx.window_height() - 1 }) defer { ctx.clear_offsets_from(offset_from_id) } @@ -221,11 +222,11 @@ fn render_version_label(mut ctx tea.Context, version_label string, help_fg_color ctx.reset_color() } -fn render_help_keybinds(mut ctx tea.Context) { +fn render_help_keybinds(mut ctx tea.Context, help_fg_color tea.Color) { offset_from_id := ctx.push_offset(tea.Offset{ x: 1, y: ctx.window_height() - 1 }) defer { ctx.clear_offsets_from(offset_from_id) } - ctx.set_color(palette.help_fg_color) + ctx.set_color(help_fg_color) ctx.draw_text(0, 0, 'q: quit ${dot} esc: exit') ctx.reset_color() } @@ -235,6 +236,7 @@ struct RenderLogoAndHelpParams { RenderLogoParams in_leader_mode bool leader_data string + disabled_help_fg_color tea.Color } fn render_logo_and_help_centered_and_stacked( @@ -251,7 +253,7 @@ fn render_logo_and_help_centered_and_stacked( ctx.push_offset(render_logo(mut ctx, opts.RenderLogoParams)) ctx.push_offset(render_lilly_name(mut ctx)) - ctx.push_offset(render_keybinds_list(mut ctx, opts.in_leader_mode, opts.leader_data, opts.petal_green)) + ctx.push_offset(render_keybinds_list(mut ctx, opts.in_leader_mode, opts.leader_data, opts.petal_green, opts.disabled_help_fg_color)) render_copyright_footer(mut ctx, opts.petal_pink) } @@ -282,9 +284,14 @@ const disabled_command_help = [ ] const pending_match_color = tea.Color.ansi(244) -// const closest_match_color = palette.petal_green_color -fn render_keybinds_list(mut ctx tea.Context, in_leader_mode bool, leader_data string, closest_match_color tea.Color) tea.Offset { +fn render_keybinds_list( + mut ctx tea.Context, + in_leader_mode bool, + leader_data string, + closest_match_color tea.Color, + disabled_help_fg_color tea.Color +) tea.Offset { offset_from_id := ctx.push_offset(tea.Offset{ y: 1 }) defer { ctx.clear_offsets_from(offset_from_id) } @@ -307,7 +314,7 @@ fn render_keybinds_list(mut ctx tea.Context, in_leader_mode bool, leader_data st ctx.push_offset(tea.Offset{ y: 1 }) ctx.push_offset(tea.Offset{ x: -(tea.visible_len(l) / 2) }) ctx.set_style(.strikethrough) - ctx.set_color(palette.help_fg_color) + ctx.set_color(disabled_help_fg_color) ctx.draw_text(0, 0, l) ctx.reset_color() ctx.clear_style() From 32798e6d43c57d5a69fe54808f991f50d1fa5816 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Sat, 10 Jan 2026 01:37:57 +0000 Subject: [PATCH 13/29] refactor: condense excessively long args into params struct for both --- splash_screen.v | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/splash_screen.v b/splash_screen.v index 0e5a4670..71861312 100644 --- a/splash_screen.v +++ b/splash_screen.v @@ -189,6 +189,7 @@ fn (m SplashScreenModel) view(mut ctx tea.Context) { leader_data: m.leader_data petal_pink: m.theme.petal_pink petal_green: m.theme.petal_green + closest_match_color: m.theme.petal_green disabled_help_fg_color: m.theme.subtle_light_grey ) render_help_keybinds(mut ctx, m.theme.subtle_light_grey) @@ -234,9 +235,7 @@ fn render_help_keybinds(mut ctx tea.Context, help_fg_color tea.Color) { @[params] struct RenderLogoAndHelpParams { RenderLogoParams - in_leader_mode bool - leader_data string - disabled_help_fg_color tea.Color + RenderKeybindsListParams } fn render_logo_and_help_centered_and_stacked( @@ -253,7 +252,7 @@ fn render_logo_and_help_centered_and_stacked( ctx.push_offset(render_logo(mut ctx, opts.RenderLogoParams)) ctx.push_offset(render_lilly_name(mut ctx)) - ctx.push_offset(render_keybinds_list(mut ctx, opts.in_leader_mode, opts.leader_data, opts.petal_green, opts.disabled_help_fg_color)) + ctx.push_offset(render_keybinds_list(mut ctx, opts.RenderKeybindsListParams)) render_copyright_footer(mut ctx, opts.petal_pink) } @@ -285,12 +284,17 @@ const disabled_command_help = [ const pending_match_color = tea.Color.ansi(244) +@[params] +struct RenderKeybindsListParams { + in_leader_mode bool + leader_data string + closest_match_color tea.Color + disabled_help_fg_color tea.Color +} + fn render_keybinds_list( mut ctx tea.Context, - in_leader_mode bool, - leader_data string, - closest_match_color tea.Color, - disabled_help_fg_color tea.Color + opts RenderKeybindsListParams ) tea.Offset { offset_from_id := ctx.push_offset(tea.Offset{ y: 1 }) defer { ctx.clear_offsets_from(offset_from_id) } @@ -298,12 +302,12 @@ fn render_keybinds_list( for l in basic_command_help { ctx.push_offset(tea.Offset{ y: 1 }) ctx.push_offset(tea.Offset{ x: -(tea.visible_len(l) / 2) }) - if in_leader_mode { - fg_color := if leader_data == 'f' { closest_match_color } else { pending_match_color } + if opts.in_leader_mode { + fg_color := if opts.leader_data == 'f' { opts.closest_match_color } else { pending_match_color } ctx.set_color(fg_color) } ctx.draw_text(0, 0, l) - if in_leader_mode { + if opts.in_leader_mode { ctx.reset_color() } ctx.pop_offset() @@ -314,7 +318,7 @@ fn render_keybinds_list( ctx.push_offset(tea.Offset{ y: 1 }) ctx.push_offset(tea.Offset{ x: -(tea.visible_len(l) / 2) }) ctx.set_style(.strikethrough) - ctx.set_color(disabled_help_fg_color) + ctx.set_color(opts.disabled_help_fg_color) ctx.draw_text(0, 0, l) ctx.reset_color() ctx.clear_style() From e4b7f7f09e7c55447dd69f9b38ac719861187581 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Sat, 10 Jan 2026 03:21:11 +0000 Subject: [PATCH 14/29] chore: pass in selection bg color from theme for picker --- editor_workspace.v | 7 +++++-- file_picker.v | 40 ++++++++++++++++++++++++++++------------ splash_screen.v | 4 ++-- theme.v | 7 +++++++ 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/editor_workspace.v b/editor_workspace.v index 1f53e9b9..d5db6e08 100644 --- a/editor_workspace.v +++ b/editor_workspace.v @@ -5,6 +5,7 @@ import time import math import tauraamui.bobatea as tea import boba +import theme import palette import glyphs @@ -14,6 +15,7 @@ struct EditorWorkspaceModel { // accidentally set the mode state without accounting for necessary checks and state changes, // the only way we can change the mode is by exiting the current scope with a command to do so mode Mode + theme theme.Theme mut: tmux_wrapped bool dialog_model ?DebuggableModel @@ -51,8 +53,9 @@ fn open_editor_workspace(initial_file_path string) tea.Cmd { } } -fn EditorWorkspaceModel.new(initial_file_path string) EditorWorkspaceModel { +fn EditorWorkspaceModel.new(ttheme theme.Theme, initial_file_path string) EditorWorkspaceModel { return EditorWorkspaceModel{ + theme: ttheme initial_file_path: initial_file_path split_tree: boba.SplitTree.new() } @@ -263,7 +266,7 @@ fn (mut m EditorWorkspaceModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { match m.leader_suffix { 'ff' { cmds << switch_mode(.normal) - cmds << open_file_picker + cmds << open_file_picker(m.theme) } else {} } diff --git a/file_picker.v b/file_picker.v index ead780d9..b3657877 100644 --- a/file_picker.v +++ b/file_picker.v @@ -6,9 +6,11 @@ import strings import lib.files import tauraamui.bobatea as tea import palette +import theme import boba struct FilePickerModel { + theme theme.Theme mut: width int height int @@ -38,10 +40,13 @@ pub: pub struct CloseDialogMsg {} -pub fn open_file_picker() tea.Msg { - return OpenDialogMsg{ - model: FilePickerModel{ - finder: files.new_finder() +pub fn open_file_picker(ttheme theme.Theme) tea.Cmd { + return fn [ttheme] () tea.Msg { + return OpenDialogMsg{ + model: FilePickerModel{ + theme: ttheme + finder: files.new_finder() + } } } } @@ -283,17 +288,28 @@ fn calculate_cursor_color(blink_frame int) tea.Color { return tea.Color.ansi(color_value) } -fn render_file_path_line(mut ctx tea.Context, file_path string, width int, height int, is_selected bool) { +@[params] +struct RenderFilePathLineParams { + file_path string + width int + height int + is_selected bool + selection_bg_color tea.Color +} + +fn render_file_path_line( + mut ctx tea.Context, opts RenderFilePathLineParams +) { mut prefix := ' ' - if is_selected { + if opts.is_selected { prefix = '» ' - selected_path_highlight_bg_color := palette.selected_highlight_bg_color + selected_path_highlight_bg_color := opts.selection_bg_color ctx.set_color(palette.fg_color(selected_path_highlight_bg_color)) ctx.set_bg_color(selected_path_highlight_bg_color) } - ctx.draw_rect(0, height - 3, width - 2, 1) - ctx.draw_text(0, height - 3, prefix + file_path.replace(os.getwd(), '.')) - if is_selected { + ctx.draw_rect(0, opts.height - 3, opts.width - 2, 1) + ctx.draw_text(0, opts.height - 3, prefix + opts.file_path.replace(os.getwd(), '.')) + if opts.is_selected { ctx.reset_color() ctx.reset_bg_color() } @@ -319,7 +335,7 @@ fn (m FilePickerModel) render_file_results_pane(mut r_ctx tea.Context, width int ctx.draw_rect(0, 0, max_width, max_height) if m.loading { - ctx.set_color(palette.subtle_text_fg_color) + ctx.set_color(m.theme.subtle_light_grey) loading_label := 'Loading files…' ctx.draw_text((width / 2) - tea.visible_len(loading_label) / 2, height / 2, loading_label) @@ -331,7 +347,7 @@ fn (m FilePickerModel) render_file_results_pane(mut r_ctx tea.Context, width int list_offset_id := ctx.push_offset(tea.Offset{}) for i, file_path in clamp_files_list_to_scrolled(m.start_index, max_items, m.filtered_files) { is_selected := (i + m.start_index) == m.selected_index - render_file_path_line(mut ctx, file_path, width, height, is_selected) + render_file_path_line(mut ctx, file_path: file_path, width: width, height: height, is_selected: is_selected, selection_bg_color: m.theme.selection_bg_color) } ctx.clear_offsets_from(list_offset_id) }) diff --git a/splash_screen.v b/splash_screen.v index 71861312..ed1c9594 100644 --- a/splash_screen.v +++ b/splash_screen.v @@ -150,7 +150,7 @@ fn (mut m SplashScreenModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { cmds << open_editor_workspace(msg.file_path) } OpenEditorWorkspaceMsg { - mut workspace := EditorWorkspaceModel.new(msg.initial_file_path) + mut workspace := EditorWorkspaceModel.new(m.theme, msg.initial_file_path) cmd := workspace.init() if u_cmd := cmd { cmds << u_cmd @@ -163,7 +163,7 @@ fn (mut m SplashScreenModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { match m.leader_data { 'ff' { m.reset_leader_mode() - cmds << open_file_picker + cmds << open_file_picker(m.theme) } 'xx' { m.reset_leader_mode() diff --git a/theme.v b/theme.v index bb592fcc..921fe627 100644 --- a/theme.v +++ b/theme.v @@ -17,8 +17,11 @@ pub: petal_pink tea.Color petal_green tea.Color + petal_red tea.Color subtle_light_grey tea.Color + + selection_bg_color tea.Color } pub const dark_theme = Theme{ @@ -32,6 +35,8 @@ pub const dark_theme = Theme{ petal_green: tea.Color.ansi(84) subtle_light_grey: tea.Color.ansi(241) + + selection_bg_color: tea.Color.ansi(239) } pub const light_theme = Theme{ @@ -45,5 +50,7 @@ pub const light_theme = Theme{ petal_green: tea.Color.ansi(76) subtle_light_grey: tea.Color.ansi(248) + + selection_bg_color: tea.Color.ansi(239) } From 58793baa8000542494b8e5e99c98ef7f917ea63f Mon Sep 17 00:00:00 2001 From: tauraamui Date: Sat, 10 Jan 2026 11:43:51 +0000 Subject: [PATCH 15/29] chore: streamline theme structure fields --- file_picker.v | 2 +- theme.v | 43 ++++++++++++++++--------------------------- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/file_picker.v b/file_picker.v index b3657877..e144c648 100644 --- a/file_picker.v +++ b/file_picker.v @@ -347,7 +347,7 @@ fn (m FilePickerModel) render_file_results_pane(mut r_ctx tea.Context, width int list_offset_id := ctx.push_offset(tea.Offset{}) for i, file_path in clamp_files_list_to_scrolled(m.start_index, max_items, m.filtered_files) { is_selected := (i + m.start_index) == m.selected_index - render_file_path_line(mut ctx, file_path: file_path, width: width, height: height, is_selected: is_selected, selection_bg_color: m.theme.selection_bg_color) + render_file_path_line(mut ctx, file_path: file_path, width: width, height: height, is_selected: is_selected, selection_bg_color: m.theme.highlight_bg_color) } ctx.clear_offsets_from(list_offset_id) }) diff --git a/theme.v b/theme.v index 921fe627..1978afe5 100644 --- a/theme.v +++ b/theme.v @@ -11,46 +11,35 @@ pub: name string @[required] bg_color tea.Color @[required] fg_color tea.Color + highlight_bg_color tea.Color @[required] + petal_pink tea.Color @[required] + petal_green tea.Color @[required] + petal_red tea.Color @[required] + subtle_light_grey tea.Color @[required] - active_split_divider_color tea.Color @[required] - inactive_split_divider_color tea.Color @[required] - - petal_pink tea.Color - petal_green tea.Color - petal_red tea.Color - - subtle_light_grey tea.Color - - selection_bg_color tea.Color } pub const dark_theme = Theme{ name: "dark" bg_color: palette.matte_black_bg_color - - active_split_divider_color: palette.petal_pink_color - inactive_split_divider_color: palette.status_bar_bg_color - + highlight_bg_color: tea.Color.ansi(239) petal_pink: tea.Color.ansi(219) petal_green: tea.Color.ansi(84) - + petal_red: tea.Color.ansi(196) subtle_light_grey: tea.Color.ansi(241) - - selection_bg_color: tea.Color.ansi(239) } +const light_petal_pink = tea.Color.ansi(200) +const light_petal_green = tea.Color.ansi(76) +const light_subtle_light_grey = tea.Color.ansi(248) + pub const light_theme = Theme{ name: "light" bg_color: tea.Color.ansi(231) - - active_split_divider_color: palette.petal_pink_color - inactive_split_divider_color: palette.status_bar_bg_color - - petal_pink: tea.Color.ansi(200) - petal_green: tea.Color.ansi(76) - - subtle_light_grey: tea.Color.ansi(248) - - selection_bg_color: tea.Color.ansi(239) + highlight_bg_color: tea.Color.ansi(239) + petal_pink: light_petal_pink + petal_green: light_petal_green + petal_red: tea.Color.ansi(196) + subtle_light_grey: light_subtle_light_grey } From 13a0a50965ad4d02a6cd528e558e1f5fedaefca4 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Sat, 10 Jan 2026 11:47:20 +0000 Subject: [PATCH 16/29] chore: set formatting --- colors.v | 35 ++++++------ colors_test.v | 5 +- config.v | 11 ++-- debug_screen.v | 28 +++++---- editor.v | 89 +++++++++++++++++++---------- editor_workspace.v | 139 +++++++++++++++++++-------------------------- file_picker.v | 30 ++++++---- input_field.v | 24 ++++---- mode.v | 25 ++++---- petal.v | 6 +- splash_screen.v | 85 +++++++++++++-------------- split_tree.v | 42 ++++++++------ theme.v | 46 +++++++-------- version_dialog.v | 14 +++-- 14 files changed, 303 insertions(+), 276 deletions(-) diff --git a/colors.v b/colors.v index d5c3d441..17a4dc62 100644 --- a/colors.v +++ b/colors.v @@ -5,26 +5,26 @@ import tauraamui.bobatea as tea // TODO(tauraamui): consolidate all used colors into a core palette/swatch set // and alias reference off of that only in the places/models they are used with // alternative names, instead of here. This file is getting messy and out of hand. -pub const matte_black_bg_color = tea.Color{ 20, 20, 20 } +pub const matte_black_bg_color = tea.Color{20, 20, 20} -pub const petal_pink_color = tea.Color{ 245, 191, 243 } -pub const petal_green_color = tea.Color{ 97, 242, 136 } -pub const petal_red_color = tea.Color{ 245, 42, 42 } +pub const petal_pink_color = tea.Color{245, 191, 243} +pub const petal_green_color = tea.Color{97, 242, 136} +pub const petal_red_color = tea.Color{245, 42, 42} -pub const status_green = tea.Color{ 145, 237, 145 } -pub const status_orange = tea.Color{ 237, 207, 123 } -pub const status_lilac = tea.Color{ 194, 110, 230 } -pub const status_dark_lilac = tea.Color{ 154, 119, 209 } -pub const status_cyan = tea.Color{ 138, 222, 237 } -pub const status_purple = tea.Color{ 130, 144, 250 } +pub const status_green = tea.Color{145, 237, 145} +pub const status_orange = tea.Color{237, 207, 123} +pub const status_lilac = tea.Color{194, 110, 230} +pub const status_dark_lilac = tea.Color{154, 119, 209} +pub const status_cyan = tea.Color{138, 222, 237} +pub const status_purple = tea.Color{130, 144, 250} pub const error_color = petal_red_color // -pub const matte_black_fg_color = tea.Color.ansi(232) -pub const matte_white_fg_color = tea.Color{ 230, 230, 230 } -pub const bright_off_white_fg_color = tea.Color{ 255, 255, 255 } +pub const matte_black_fg_color = tea.Color.ansi(232) +pub const matte_white_fg_color = tea.Color{230, 230, 230} +pub const bright_off_white_fg_color = tea.Color{255, 255, 255} pub const subtle_text_fg_color = tea.Color.ansi(249) pub const help_fg_color = tea.Color.ansi(241) @@ -33,10 +33,10 @@ pub const selected_highlight_bg_color = tea.Color.ansi(239) pub const subtle_border_fg_color = petal_pink_color -pub const status_bar_bg_color = tea.Color.ansi(234) -pub const status_file_name_bg_color = tea.Color{ 86, 86, 86 } -pub const status_branch_name_bg_color = tea.Color{ 154, 119, 209 } -pub const status_cursor_pos_bg_color = tea.Color{ 245, 42, 42 } +pub const status_bar_bg_color = tea.Color.ansi(234) +pub const status_file_name_bg_color = tea.Color{86, 86, 86} +pub const status_branch_name_bg_color = tea.Color{154, 119, 209} +pub const status_cursor_pos_bg_color = tea.Color{245, 42, 42} pub fn fg_color(background_color tea.Color) tea.Color { s_r := f32(background_color.r) / 255 @@ -47,4 +47,3 @@ pub fn fg_color(background_color tea.Color) tea.Color { return if luminance > 0.5 { matte_black_fg_color } else { matte_white_fg_color } } - diff --git a/colors_test.v b/colors_test.v index 1b93179a..522928bf 100644 --- a/colors_test.v +++ b/colors_test.v @@ -3,7 +3,6 @@ module palette import tauraamui.bobatea as tea fn test_fg_color_from_shades_of_bg() { - assert fg_color(tea.Color{ 10, 10, 10 }) == matte_white_fg_color - assert fg_color(tea.Color{ 200, 200, 200 }) == matte_black_fg_color + assert fg_color(tea.Color{10, 10, 10}) == matte_white_fg_color + assert fg_color(tea.Color{200, 200, 200}) == matte_black_fg_color } - diff --git a/config.v b/config.v index a72d0a06..33fcceeb 100644 --- a/config.v +++ b/config.v @@ -5,7 +5,7 @@ import theme pub const light_theme_name = theme.light_theme_name pub const dark_theme_name = theme.dark_theme_name -pub struct Config{ +pub struct Config { pub: theme theme.Theme leader_key string @@ -14,7 +14,7 @@ pub: @[params] pub struct ConfigOptions { pub: - load_from_path ?string = "~/.config/petal/petal.cfg" + load_from_path ?string = '~/.config/petal/petal.cfg' } pub fn Config.new(opts ConfigOptions) Config { @@ -25,15 +25,14 @@ pub fn Config.new(opts ConfigOptions) Config { } return Config{ - theme: theme.dark_theme - leader_key: ";" + theme: theme.dark_theme + leader_key: ';' } } pub fn (c Config) set_theme(name string) Config { return Config{ ...c - theme: if name == "dark" { theme.dark_theme } else { theme.light_theme } + theme: if name == 'dark' { theme.dark_theme } else { theme.light_theme } } } - diff --git a/debug_screen.v b/debug_screen.v index 5e7e5e7d..5d17e2df 100644 --- a/debug_screen.v +++ b/debug_screen.v @@ -9,7 +9,7 @@ import palette interface DebuggableModel { tea.Model Debuggable - width() int + width() int height() int } @@ -58,18 +58,18 @@ enum LogLevel { fn (l LogLevel) str() string { return match l { - .debug { "DEBU" } - .info { "INFO" } - .warn { "WARN" } - .error { "ERRO" } + .debug { 'DEBU' } + .info { 'INFO' } + .warn { 'WARN' } + .error { 'ERRO' } } } fn (l LogLevel) fg() tea.Color { return match l { .debug { tea.Color.ansi(62) } - .info { tea.Color.ansi(183) } - .warn { tea.Color.ansi(220) } + .info { tea.Color.ansi(183) } + .warn { tea.Color.ansi(220) } .error { tea.Color.ansi(162) } } } @@ -80,7 +80,7 @@ struct LogMsg { } fn log(message string, level LogLevel) tea.Msg { - return LogMsg{ level, message } + return LogMsg{level, message} } fn debug_log(message string) tea.Cmd { @@ -253,11 +253,11 @@ fn render_logs(mut ctx tea.Context, x int, y int, logs []LogMsg) { ctx.set_color(l.level.fg()) mut label_x_offset := 0 - ctx.draw_text(x + label_x_offset, y + i, "[") + ctx.draw_text(x + label_x_offset, y + i, '[') label_x_offset += 1 ctx.draw_text(x + label_x_offset, y + i, level_label) label_x_offset += tea.visible_len(level_label) - ctx.draw_text(x + label_x_offset, y + i, "]") + ctx.draw_text(x + label_x_offset, y + i, ']') label_x_offset += 1 ctx.reset_color() label_x_offset += 1 // single space padding @@ -301,9 +301,13 @@ fn (m DebugScreenModel) debug_data() DebugData { } } -fn (m DebugScreenModel) width() int { return m.window_width } +fn (m DebugScreenModel) width() int { + return m.window_width +} -fn (m DebugScreenModel) height() int { return m.window_height } +fn (m DebugScreenModel) height() int { + return m.window_height +} fn (m DebugScreenModel) clone() tea.Model { return DebugScreenModel{ diff --git a/editor.v b/editor.v index 1b645ef8..23728675 100644 --- a/editor.v +++ b/editor.v @@ -11,26 +11,46 @@ struct ModelCursorPos { fn (c ModelCursorPos) up() ModelCursorPos { yy := c.y - 1 - if yy < 0 { return c } - return ModelCursorPos{ y: yy, x: 0 } + if yy < 0 { + return c + } + return ModelCursorPos{ + y: yy + x: 0 + } } fn (c ModelCursorPos) down(max int) ModelCursorPos { yy := c.y + 1 - if yy >= max { return c } - return ModelCursorPos{ y: yy, x: 0 } + if yy >= max { + return c + } + return ModelCursorPos{ + y: yy + x: 0 + } } fn (c ModelCursorPos) left() ModelCursorPos { xx := c.x - 1 - if xx < 0 { return c } - return ModelCursorPos{ y: c.y, x: xx } + if xx < 0 { + return c + } + return ModelCursorPos{ + y: c.y + x: xx + } } fn (c ModelCursorPos) right(max int) ModelCursorPos { yy := c.y + 1 - if yy >= max { return c } - return ModelCursorPos{ y: yy, x: 0 } + if yy >= max { + return c + } + return ModelCursorPos{ + y: yy + x: 0 + } } struct EditorData { @@ -60,7 +80,7 @@ struct OpenEditorMsg { fn open_editor(file_path string) tea.Cmd { return fn [file_path] () tea.Msg { - return OpenEditorMsg{ file_path } + return OpenEditorMsg{file_path} } } @@ -68,8 +88,8 @@ struct QueryEditorDataMsg {} fn query_editor_data(id int) tea.Cmd { return fn [id] () tea.Msg { - return EditorModelMsg { - id: id + return EditorModelMsg{ + id: id msg: QueryEditorDataMsg{} } } @@ -81,16 +101,20 @@ struct EditorDataResultMsg { fn editor_data(data EditorData) tea.Cmd { return fn [data] () tea.Msg { - return EditorDataResultMsg{ data } + return EditorDataResultMsg{data} } } fn EditorModel.new(id int, file_path string) EditorModel { assert file_path.len != 0 return EditorModel{ - id: id, - file_path: file_path, - lines: if content := os.read_lines(file_path) { content } else { []string{ len: 150, init: "This is a line of random text" } } + id: id + file_path: file_path + lines: if content := os.read_lines(file_path) { + content + } else { + []string{len: 150, init: 'This is a line of random text'} + } } } @@ -121,7 +145,6 @@ fn move_cursor_up() tea.Msg { return EditorCursorUpMsg{} } - fn (mut m EditorModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { mut cmds := []tea.Cmd{} @@ -129,14 +152,13 @@ fn (mut m EditorModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { match msg.k_type { .runes { match msg.string() { - "j" { + 'j' { cmds << move_cursor_down return m.clone(), tea.batch_array(cmds) } - "k" { + 'k' { cmds << move_cursor_up return m.clone(), tea.batch_array(cmds) - } else {} } @@ -147,7 +169,7 @@ fn (mut m EditorModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { match msg { tea.ResizedMsg { - m.width = msg.window_width + m.width = msg.window_width m.height = msg.window_height } EditorModelMsg { @@ -196,13 +218,17 @@ const active_editor_border_color = palette.petal_pink_color const inactive_editor_border_color = palette.status_dark_lilac fn (m EditorModel) view(mut ctx tea.Context) { - ctx.set_clip_area(tea.ClipArea{ 0, 0, m.width, m.height }) + ctx.set_clip_area(tea.ClipArea{0, 0, m.width, m.height}) defer { ctx.clear_clip_area() } if m.show_border { - border_color := if m.focused { active_editor_border_color } else { inactive_editor_border_color } + border_color := if m.focused { + active_editor_border_color + } else { + inactive_editor_border_color + } ctx.set_color(border_color) - for y in 0..m.height { + for y in 0 .. m.height { ctx.draw_text(0, y, '│') } ctx.reset_color() @@ -213,7 +239,9 @@ fn (m EditorModel) view(mut ctx tea.Context) { ctx.draw_text(0, y, l.replace('\t', ' ')) } - if m.focused { m.render_cursor(mut ctx) } + if m.focused { + m.render_cursor(mut ctx) + } } fn (m EditorModel) render_cursor(mut ctx tea.Context) { @@ -227,8 +255,8 @@ fn (m EditorModel) debug_data() DebugData { return DebugData{ name: 'active editor data' data: { - 'id': '${m.id}' - 'file path': m.file_path + 'id': '${m.id}' + 'file path': m.file_path 'cursor_row': '${m.cursor_pos.y}' 'cursor_col': '${m.cursor_pos.x}' } @@ -244,9 +272,13 @@ fn (m EditorModel) data() EditorData { } } -fn (m EditorModel) width() int { return m.width } +fn (m EditorModel) width() int { + return m.width +} -fn (m EditorModel) height() int { return m.height } +fn (m EditorModel) height() int { + return m.height +} fn (m EditorModel) clone() tea.Model { assert m.file_path.len != 0 @@ -254,4 +286,3 @@ fn (m EditorModel) clone() tea.Model { ...m } } - diff --git a/editor_workspace.v b/editor_workspace.v index d5db6e08..21d31e49 100644 --- a/editor_workspace.v +++ b/editor_workspace.v @@ -14,16 +14,16 @@ struct EditorWorkspaceModel { // NOTE(tauraamui): forced mode to be immutable, this ensures we cannot randomly // accidentally set the mode state without accounting for necessary checks and state changes, // the only way we can change the mode is by exiting the current scope with a command to do so - mode Mode - theme theme.Theme + mode Mode + theme theme.Theme mut: - tmux_wrapped bool - dialog_model ?DebuggableModel + tmux_wrapped bool + dialog_model ?DebuggableModel - active_editor_id int + active_editor_id int - split_tree boba.SplitTree - editors map[int]DebuggableModel + split_tree boba.SplitTree + editors map[int]DebuggableModel active_editor_data ?EditorData branch_name string @@ -55,14 +55,14 @@ fn open_editor_workspace(initial_file_path string) tea.Cmd { fn EditorWorkspaceModel.new(ttheme theme.Theme, initial_file_path string) EditorWorkspaceModel { return EditorWorkspaceModel{ - theme: ttheme + theme: ttheme initial_file_path: initial_file_path - split_tree: boba.SplitTree.new() + split_tree: boba.SplitTree.new() } } fn (mut m EditorWorkspaceModel) init() ?tea.Cmd { - m.input_field = boba.InputField.new_with_prefix(":", 0) + m.input_field = boba.InputField.new_with_prefix(':', 0) return tea.batch(open_editor(m.initial_file_path), check_if_tmux_wrapped) } @@ -72,7 +72,7 @@ struct SwitchModeMsg { fn switch_mode(mode Mode) tea.Cmd { return fn [mode] () tea.Msg { - return SwitchModeMsg{ mode } + return SwitchModeMsg{mode} } } @@ -82,14 +82,14 @@ struct CommandMsg { fn run_command(command string) tea.Cmd { return fn [command] () tea.Msg { - return CommandMsg{ command } + return CommandMsg{command} } } fn focus_editor(editor_id int) tea.Cmd { return fn [editor_id] () tea.Msg { return EditorModelMsg{ - id: editor_id + id: editor_id msg: tea.FocusedMsg{} } } @@ -98,7 +98,7 @@ fn focus_editor(editor_id int) tea.Cmd { fn unfocus_editor(editor_id int) tea.Cmd { return fn [editor_id] () tea.Msg { return EditorModelMsg{ - id: editor_id + id: editor_id msg: tea.BlurredMsg{} } } @@ -128,7 +128,7 @@ struct DisplayErrorMsg { fn display_error(error string) tea.Cmd { return fn [error] () tea.Msg { - return DisplayErrorMsg { error } + return DisplayErrorMsg{error} } } @@ -166,7 +166,7 @@ fn pwd_git_branch_name(branch_name string) tea.Cmd { fn resolve_git_branch_name(execute fn (cmd string) os.Result) string { $if darwin { - return "(not supported on macos)" + return '(not supported on macos)' } prefix := '\uE0A0' wt := spawn currently_in_worktree(execute) @@ -224,13 +224,11 @@ fn (mut m EditorWorkspaceModel) update_dialog(msg tea.Msg) (?tea.Model, ?tea.Cmd } if mut open_model := m.dialog_model { - intercepted_msg := if msg is tea.ResizedMsg && mut open_model is FilePickerModel { tea.Msg( - // force forward a 80% of the actual window size down to moddal model - tea.ResizedMsg{ - window_width: int(f64(msg.window_width) * 0.8) + // force forward a 80% of the actual window size down to moddal model + intercepted_msg := if msg is tea.ResizedMsg && mut open_model is FilePickerModel { tea.Msg(tea.ResizedMsg{ + window_width: int(f64(msg.window_width) * 0.8) window_height: int(f64(msg.window_height) * 0.8) - } - ) } else { msg } + }) } else { msg } d, cmd := open_model.update(intercepted_msg) if d is DebuggableModel { @@ -306,16 +304,16 @@ fn (mut m EditorWorkspaceModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { match msg.k_type { .special { match msg.string() { - "escape" { + 'escape' { cmds << hide_error } - "ctrl+b" { + 'ctrl+b' { cmds << switch_mode(.navigation) } - "ctrl+w+h" { + 'ctrl+w+h' { cmds << switch_active_split(.left) } - "ctrl+w+l" { + 'ctrl+w+l' { cmds << switch_active_split(.right) } else {} @@ -367,7 +365,7 @@ fn (mut m EditorWorkspaceModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { cmds << query_pwd_git_branch } CheckIfTMUXWrappedMsg { - m.tmux_wrapped = os.getenv("TMUX").len > 0 + m.tmux_wrapped = os.getenv('TMUX').len > 0 } OpenDialogMsg { mut d_model := msg.model @@ -400,17 +398,13 @@ fn (mut m EditorWorkspaceModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { cmds << u_cmd } - cmds << tea.sequence( - focus_editor(editor_id), - toggle_editor_show_border(editor_id, false), - query_editor_data(editor_id), - query_pwd_git_branch - ) - cmds << debug_log("opened file ${msg.file_path} into model of id ${editor_id}") + cmds << tea.sequence(focus_editor(editor_id), toggle_editor_show_border(editor_id, + false), query_editor_data(editor_id), query_pwd_git_branch) + cmds << debug_log('opened file ${msg.file_path} into model of id ${editor_id}') } VerticalSplitMsg { if info := m.split_tree.get_active_editor() { - old_id := info.id // get the old ID before inserting + old_id := info.id // get the old ID before inserting new_id := m.next_editor_id() mut new_editor := EditorModel.new(new_id, info.file_path) if init_cmd := new_editor.init() { @@ -423,14 +417,9 @@ fn (mut m EditorWorkspaceModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { // sync active_editor_id with split_tree m.active_editor_id = m.split_tree.active_editor_id - cmds << tea.sequence( - unfocus_editor(old_id), - toggle_editor_show_border(m.split_tree.get_leftmost_id(), false), - focus_editor(new_id), - query_editor_data(new_id), - query_pwd_git_branch, - tea.emit_resize - ) + cmds << tea.sequence(unfocus_editor(old_id), toggle_editor_show_border(m.split_tree.get_leftmost_id(), + false), focus_editor(new_id), query_editor_data(new_id), query_pwd_git_branch, + tea.emit_resize) } } CloseActiveSplitMsg { @@ -443,13 +432,9 @@ fn (mut m EditorWorkspaceModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { cmds << tea.quit } else { // focus the new active editor - cmds << tea.sequence( - focus_editor(m.active_editor_id), - toggle_editor_show_border(m.split_tree.get_leftmost_id(), false), - query_editor_data(m.active_editor_id), - query_pwd_git_branch, - tea.emit_resize - ) + cmds << tea.sequence(focus_editor(m.active_editor_id), toggle_editor_show_border(m.split_tree.get_leftmost_id(), + false), query_editor_data(m.active_editor_id), query_pwd_git_branch, + tea.emit_resize) } } } @@ -467,12 +452,8 @@ fn (mut m EditorWorkspaceModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { new_id := m.split_tree.active_editor_id m.active_editor_id = new_id - cmds << tea.sequence( - unfocus_editor(old_id), - focus_editor(new_id), - query_editor_data(new_id), - query_pwd_git_branch - ) + cmds << tea.sequence(unfocus_editor(old_id), focus_editor(new_id), + query_editor_data(new_id), query_pwd_git_branch) } } else { if m.tmux_wrapped { @@ -491,12 +472,8 @@ fn (mut m EditorWorkspaceModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { new_id := m.split_tree.active_editor_id m.active_editor_id = new_id - cmds << tea.sequence( - unfocus_editor(old_id), - focus_editor(new_id), - query_editor_data(new_id), - query_pwd_git_branch - ) + cmds << tea.sequence(unfocus_editor(old_id), focus_editor(new_id), + query_editor_data(new_id), query_pwd_git_branch) } } else { if m.tmux_wrapped { @@ -514,11 +491,11 @@ fn (mut m EditorWorkspaceModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { } CommandMsg { match msg.command { - "q" { cmds << close_active_split } - "qa" { cmds << tea.quit } - "debug" { cmds << toggle_debug_screen } - "version" { cmds << open_version_dialog } - "vs" { cmds << split_vertically } + 'q' { cmds << close_active_split } + 'qa' { cmds << tea.quit } + 'debug' { cmds << toggle_debug_screen } + 'version' { cmds << open_version_dialog } + 'vs' { cmds << split_vertically } else { cmds << raise_error("unknown command '${msg.command}'") } } } @@ -574,17 +551,17 @@ fn (mut m EditorWorkspaceModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { fn (m EditorWorkspaceModel) view(mut ctx tea.Context) { editor_area_height := ctx.window_height() - 2 - ctx.set_clip_area(tea.ClipArea{ 0, 0, ctx.window_width(), editor_area_height }) + ctx.set_clip_area(tea.ClipArea{0, 0, ctx.window_width(), editor_area_height}) layout := m.split_tree.get_layout(ctx.window_width(), editor_area_height) for rect in layout { if mut editor := m.editors[rect.editor_id] { // set clip area for this specific split - ctx.set_clip_area(tea.ClipArea{ rect.x, rect.y, rect.width, rect.height }) + ctx.set_clip_area(tea.ClipArea{rect.x, rect.y, rect.width, rect.height}) offset_id := ctx.push_offset(tea.Offset{ x: rect.x, y: rect.y }) resized, _ := editor.update(tea.ResizedMsg{ - window_width: rect.width + window_width: rect.width window_height: rect.height }) if resized is DebuggableModel { @@ -593,19 +570,17 @@ fn (m EditorWorkspaceModel) view(mut ctx tea.Context) { } ctx.clear_offsets_from(offset_id) - ctx.clear_clip_area() // clear after each split + ctx.clear_clip_area() // clear after each split } } m.render_status_bar(mut ctx) if mut open_model := m.dialog_model { - id := ctx.push_offset( - tea.Offset{ - x: int(f64(ctx.window_width() / 2)) - int(f64(open_model.width() / 2)) - y: int (f64(ctx.window_height() / 2)) - int(f64(open_model.height() / 2)) - } - ) + id := ctx.push_offset(tea.Offset{ + x: int(f64(ctx.window_width() / 2)) - int(f64(open_model.width() / 2)) + y: int(f64(ctx.window_height() / 2)) - int(f64(open_model.height() / 2)) + }) defer { ctx.clear_offsets_from(id) } open_model.view(mut ctx) @@ -766,14 +741,18 @@ fn (m EditorWorkspaceModel) debug_data() DebugData { return DebugData{ name: 'editor_workspace data' data: { - 'initial file path': m.initial_file_path + 'initial file path': m.initial_file_path } } } -fn (m EditorWorkspaceModel) width() int { return 0 } +fn (m EditorWorkspaceModel) width() int { + return 0 +} -fn (m EditorWorkspaceModel) height() int { return 0 } +fn (m EditorWorkspaceModel) height() int { + return 0 +} fn (mut m EditorWorkspaceModel) clone() tea.Model { return EditorWorkspaceModel{ diff --git a/file_picker.v b/file_picker.v index e144c648..a344ee1c 100644 --- a/file_picker.v +++ b/file_picker.v @@ -10,7 +10,7 @@ import theme import boba struct FilePickerModel { - theme theme.Theme + theme theme.Theme mut: width int height int @@ -44,7 +44,7 @@ pub fn open_file_picker(ttheme theme.Theme) tea.Cmd { return fn [ttheme] () tea.Msg { return OpenDialogMsg{ model: FilePickerModel{ - theme: ttheme + theme: ttheme finder: files.new_finder() } } @@ -176,7 +176,7 @@ fn (mut m FilePickerModel) on_cancel() (tea.Model, ?tea.Cmd) { return m.clone(), cmd } -const filter_trigger_special_keys = ["backspace", "delete"] +const filter_trigger_special_keys = ['backspace', 'delete'] fn (mut m FilePickerModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { mut cmds := []tea.Cmd{} @@ -297,9 +297,7 @@ struct RenderFilePathLineParams { selection_bg_color tea.Color } -fn render_file_path_line( - mut ctx tea.Context, opts RenderFilePathLineParams -) { +fn render_file_path_line(mut ctx tea.Context, opts RenderFilePathLineParams) { mut prefix := ' ' if opts.is_selected { prefix = '» ' @@ -347,7 +345,13 @@ fn (m FilePickerModel) render_file_results_pane(mut r_ctx tea.Context, width int list_offset_id := ctx.push_offset(tea.Offset{}) for i, file_path in clamp_files_list_to_scrolled(m.start_index, max_items, m.filtered_files) { is_selected := (i + m.start_index) == m.selected_index - render_file_path_line(mut ctx, file_path: file_path, width: width, height: height, is_selected: is_selected, selection_bg_color: m.theme.highlight_bg_color) + render_file_path_line(mut ctx, + file_path: file_path + width: width + height: height + is_selected: is_selected + selection_bg_color: m.theme.highlight_bg_color + ) } ctx.clear_offsets_from(list_offset_id) }) @@ -380,7 +384,9 @@ fn clamp_files_list_to_scrolled(start int, max_items int, initial_files_list []s } fn (m FilePickerModel) view(mut ctx tea.Context) { - if m.width == 0 || m.height == 0 { return } + if m.width == 0 || m.height == 0 { + return + } // wipe existing rendered cells "behind" the modal ctx.draw_rect(0, 0, m.width, m.height) @@ -416,9 +422,13 @@ fn (m FilePickerModel) debug_data() DebugData { } } -fn (m FilePickerModel) width() int { return m.width } +fn (m FilePickerModel) width() int { + return m.width +} -fn (m FilePickerModel) height() int { return m.height } +fn (m FilePickerModel) height() int { + return m.height +} fn (m FilePickerModel) clone() tea.Model { return FilePickerModel{ diff --git a/input_field.v b/input_field.v index 13da0ca9..66d33840 100644 --- a/input_field.v +++ b/input_field.v @@ -14,8 +14,8 @@ mut: value string width int layout tea.Layout = no_bordered_layout - input_prefix string = ">" - prefix_padding int = 1 + input_prefix string = '>' + prefix_padding int = 1 cursor_pos int cursor_blink_frame int focused bool @@ -39,7 +39,10 @@ pub fn InputField.new() InputField { } pub fn InputField.new_with_prefix(input_prefix string, prefix_padding int) InputField { - return InputField{ input_prefix: input_prefix, prefix_padding: prefix_padding } + return InputField{ + input_prefix: input_prefix + prefix_padding: prefix_padding + } } pub fn (mut i InputField) init() ?tea.Cmd { @@ -141,7 +144,9 @@ pub fn (m InputField) view(mut r_ctx tea.Context) { // let's also make sure we notice if width is ever negative // because that's a bug signifier assert !(width < 0) - if width <= 0 { return } + if width <= 0 { + return + } cursor_pos := m.cursor_pos cursor_color := calculate_cursor_color(m.cursor_blink_frame) @@ -150,15 +155,15 @@ pub fn (m InputField) view(mut r_ctx tea.Context) { prefix_padding := m.prefix_padding height := if m.layout.border == .none { 1 } else { 3 } - m.layout.size(width, height).render(mut r_ctx, fn [ - cursor_pos, cursor_color, width, value_runes, input_prefix, prefix_padding - ] (mut l_ctx tea.Context) { + m.layout.size(width, height).render(mut r_ctx, fn [cursor_pos, cursor_color, width, value_runes, input_prefix, prefix_padding] (mut l_ctx tea.Context) { l_ctx.set_clip_area(tea.ClipArea{0, 0, width - 3, 1}) defer { l_ctx.clear_clip_area() } l_ctx.draw_rect(0, 0, width - 2, 1) l_ctx.draw_text(0, 0, input_prefix) - input_text_offset := l_ctx.push_offset(tea.Offset{ x: prefix_padding + tea.visible_len(input_prefix) }) + input_text_offset := l_ctx.push_offset(tea.Offset{ + x: prefix_padding + tea.visible_len(input_prefix) + }) cursor_within_content := cursor_pos < value_runes.len for i, r in value_runes { r_str := r.str() @@ -200,7 +205,7 @@ pub fn (m &InputField) rune_len() int { } pub fn (mut m InputField) reset() { - m.value = '' + m.value = '' m.cursor_pos = 0 } @@ -221,4 +226,3 @@ fn (m InputField) clone() InputField { ...m } } - diff --git a/mode.v b/mode.v index 280c7f9a..c9e47cb7 100644 --- a/mode.v +++ b/mode.v @@ -15,25 +15,24 @@ enum Mode as u8 { fn (m Mode) color() tea.Color { return match m { - .normal { palette.status_green } - .leader { palette.status_purple } - .command { palette.status_cyan } - .insert { palette.status_orange } - .visual { palette.status_lilac } + .normal { palette.status_green } + .leader { palette.status_purple } + .command { palette.status_cyan } + .insert { palette.status_orange } + .visual { palette.status_lilac } .visual_line { palette.status_lilac } - .navigation { palette.status_cyan } + .navigation { palette.status_cyan } } } fn (m Mode) str() string { return match m { - .normal { 'NORMAL' } - .leader { 'LEADER' } - .command { 'COMMAND' } - .insert { 'INSERT' } - .visual { 'VISUAL' } + .normal { 'NORMAL' } + .leader { 'LEADER' } + .command { 'COMMAND' } + .insert { 'INSERT' } + .visual { 'VISUAL' } .visual_line { 'VISUAL LINE' } - .navigation { 'NAVIGATION' } + .navigation { 'NAVIGATION' } } } - diff --git a/petal.v b/petal.v index 4118df7b..3bf4c451 100644 --- a/petal.v +++ b/petal.v @@ -23,9 +23,9 @@ mut: fn PetalModel.new(config cfg.Config) PetalModel { return PetalModel{ - config: config - theme: config.theme - first_frame: true + config: config + theme: config.theme + first_frame: true active_screen: SplashScreenModel.new(leader_key: config.leader_key, theme: config.theme) } } diff --git a/splash_screen.v b/splash_screen.v index ed1c9594..3c873b0b 100644 --- a/splash_screen.v +++ b/splash_screen.v @@ -70,13 +70,11 @@ fn (mut m SplashScreenModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { } if mut open_model := m.dialog_model { - intercepted_msg := if msg is tea.ResizedMsg { tea.Msg( - // force forward a 80% of the actual window size down to moddal model - tea.ResizedMsg{ - window_width: int(f64(msg.window_width) * 0.8) + // force forward a 80% of the actual window size down to moddal model + intercepted_msg := if msg is tea.ResizedMsg { tea.Msg(tea.ResizedMsg{ + window_width: int(f64(msg.window_width) * 0.8) window_height: int(f64(msg.window_height) * 0.8) - } - ) } else { msg } + }) } else { msg } d, cmd := open_model.update(intercepted_msg) if d is DebuggableModel { @@ -87,7 +85,7 @@ fn (mut m SplashScreenModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { match msg { CheckIfTMUXWrappedMsg { - m.tmux_wrapped = os.getenv("TMUX").len > 0 + m.tmux_wrapped = os.getenv('TMUX').len > 0 } tea.KeyMsg { match msg.k_type { @@ -182,14 +180,13 @@ fn (mut m SplashScreenModel) reset_leader_mode() { fn (m SplashScreenModel) view(mut ctx tea.Context) { render_version_label(mut ctx, '${version} - (#${build_id})', m.theme.subtle_light_grey) - render_logo_and_help_centered_and_stacked( - mut ctx, - logo: m.logo, - in_leader_mode: m.leader_mode, - leader_data: m.leader_data - petal_pink: m.theme.petal_pink - petal_green: m.theme.petal_green - closest_match_color: m.theme.petal_green + render_logo_and_help_centered_and_stacked(mut ctx, + logo: m.logo + in_leader_mode: m.leader_mode + leader_data: m.leader_data + petal_pink: m.theme.petal_pink + petal_green: m.theme.petal_green + closest_match_color: m.theme.petal_green disabled_help_fg_color: m.theme.subtle_light_grey ) render_help_keybinds(mut ctx, m.theme.subtle_light_grey) @@ -205,12 +202,10 @@ fn (m SplashScreenModel) view(mut ctx tea.Context) { ctx.clear_all_offsets() if mut open_model := m.dialog_model { - id := ctx.push_offset( - tea.Offset{ - x: int(f64(ctx.window_width() / 2)) - int(f64(open_model.width() / 2)) - y: int (f64(ctx.window_height() / 2)) - int(f64(open_model.height() / 2)) - } - ) + id := ctx.push_offset(tea.Offset{ + x: int(f64(ctx.window_width() / 2)) - int(f64(open_model.width() / 2)) + y: int(f64(ctx.window_height() / 2)) - int(f64(open_model.height() / 2)) + }) defer { ctx.clear_offsets_from(id) } open_model.view(mut ctx) @@ -238,10 +233,8 @@ struct RenderLogoAndHelpParams { RenderKeybindsListParams } -fn render_logo_and_help_centered_and_stacked( - mut ctx tea.Context, - opts RenderLogoAndHelpParams -) { +fn render_logo_and_help_centered_and_stacked(mut ctx tea.Context, + opts RenderLogoAndHelpParams) { // NOTE(tauraamui) [25/10/2025]: all following contents to be padded from top of window base_offset_y := f64(ctx.window_height()) * 0.1 offset_from_id := ctx.push_offset(tea.Offset{ @@ -286,16 +279,14 @@ const pending_match_color = tea.Color.ansi(244) @[params] struct RenderKeybindsListParams { - in_leader_mode bool - leader_data string - closest_match_color tea.Color + in_leader_mode bool + leader_data string + closest_match_color tea.Color disabled_help_fg_color tea.Color } -fn render_keybinds_list( - mut ctx tea.Context, - opts RenderKeybindsListParams -) tea.Offset { +fn render_keybinds_list(mut ctx tea.Context, + opts RenderKeybindsListParams) tea.Offset { offset_from_id := ctx.push_offset(tea.Offset{ y: 1 }) defer { ctx.clear_offsets_from(offset_from_id) } @@ -303,7 +294,11 @@ fn render_keybinds_list( ctx.push_offset(tea.Offset{ y: 1 }) ctx.push_offset(tea.Offset{ x: -(tea.visible_len(l) / 2) }) if opts.in_leader_mode { - fg_color := if opts.leader_data == 'f' { opts.closest_match_color } else { pending_match_color } + fg_color := if opts.leader_data == 'f' { + opts.closest_match_color + } else { + pending_match_color + } ctx.set_color(fg_color) } ctx.draw_text(0, 0, l) @@ -379,8 +374,8 @@ fn render_logo(mut ctx tea.Context, opts RenderLogoParams) tea.Offset { @[params] struct RenderLogoLineParams { - petal_pink tea.Color - petal_green tea.Color + petal_pink tea.Color + petal_green tea.Color } fn render_logo_line(mut ctx tea.Context, line string, opts RenderLogoLineParams) { @@ -391,12 +386,10 @@ fn render_logo_line(mut ctx tea.Context, line string, opts RenderLogoLineParams) ctx.draw_text(0, 0, line) } -fn render_logo_line_char_by_char( - mut ctx tea.Context, +fn render_logo_line_char_by_char(mut ctx tea.Context, line string, petal_pink tea.Color, - petal_green tea.Color, -) { + petal_green tea.Color) { for j, c in line.runes() { mut to_draw := '${c}' if to_draw == 'g' { @@ -424,17 +417,21 @@ fn (m SplashScreenModel) debug_data() DebugData { return DebugData{ name: 'splash_screen data' data: { - 'leader key': m.leader_key + 'leader key': m.leader_key 'tmux wrapped': '${m.tmux_wrapped}' - '': if d := m.dialog_model { d.debug_data() } else { 'null' } - 'version': '${version} - (${build_id})' + '': if d := m.dialog_model { d.debug_data() } else { 'null' } + 'version': '${version} - (${build_id})' } } } -fn (m SplashScreenModel) width() int { return 0 } +fn (m SplashScreenModel) width() int { + return 0 +} -fn (m SplashScreenModel) height() int { return 0 } +fn (m SplashScreenModel) height() int { + return 0 +} fn (m SplashScreenModel) clone() tea.Model { return SplashScreenModel{ diff --git a/split_tree.v b/split_tree.v index 13fe7c08..540e9593 100644 --- a/split_tree.v +++ b/split_tree.v @@ -65,7 +65,7 @@ pub fn (t SplitTree) find_editor_by_id(node SplitNode, target_id int) ?EditorInf EditorLeaf { if node.editor_id == target_id { return EditorInfo{ - id: node.editor_id + id: node.editor_id file_path: node.file_path } } @@ -145,8 +145,8 @@ fn (t SplitTree) insert_split_at(node SplitNode, target_id int, new_id int, new_ // Found target - wrap both in container return SplitContainer{ direction: direction - children: [SplitNode(node), SplitNode(new_leaf)] - ratios: [0.5, 0.5] + children: [SplitNode(node), SplitNode(new_leaf)] + ratios: [0.5, 0.5] } } return node @@ -169,14 +169,14 @@ fn (t SplitTree) insert_split_at(node SplitNode, target_id int, new_id int, new_ return SplitContainer{ ...node children: new_children - ratios: new_ratios + ratios: new_ratios } } else { // Different direction - wrap target and new in container new_container := SplitContainer{ direction: direction - children: [child, new_leaf] - ratios: [0.5, 0.5] + children: [child, new_leaf] + ratios: [0.5, 0.5] } mut new_children := node.children.clone() new_children[i] = new_container @@ -192,7 +192,8 @@ fn (t SplitTree) insert_split_at(node SplitNode, target_id int, new_id int, new_ // Not a direct child - recurse mut new_children := []SplitNode{} for child in node.children { - new_children << t.insert_split_at(child, target_id, new_id, new_file_path, direction) + new_children << t.insert_split_at(child, target_id, new_id, new_file_path, + direction) } return SplitContainer{ ...node @@ -264,7 +265,11 @@ pub fn (mut t SplitTree) navigate_prev(do_not_wrap_around bool) bool { return false } - prev_idx := if do_not_wrap_around { current_idx - 1 } else { (current_idx - 1 + all_ids.len) % all_ids.len } + prev_idx := if do_not_wrap_around { + current_idx - 1 + } else { + (current_idx - 1 + all_ids.len) % all_ids.len + } t.active_editor_id = all_ids[prev_idx] return true } @@ -311,10 +316,10 @@ fn (t SplitTree) calculate_layout(node SplitNode, x int, y int, width int, heigh EditorLeaf { rects << LayoutRect{ editor_id: node.editor_id - x: x - y: y - width: width - height: height + x: x + y: y + width: width + height: height } } SplitContainer { @@ -330,7 +335,8 @@ fn (t SplitTree) calculate_layout(node SplitNode, x int, y int, width int, heigh int(f64(width) * node.ratios[i]) } - t.calculate_layout(child, current_x, y, child_width, height, mut rects) + t.calculate_layout(child, current_x, y, child_width, height, mut + rects) current_x += child_width remaining_width -= child_width } @@ -348,7 +354,8 @@ fn (t SplitTree) calculate_layout(node SplitNode, x int, y int, width int, heigh int(f64(height) * node.ratios[i]) } - t.calculate_layout(child, x, current_y, width, child_height, mut rects) + t.calculate_layout(child, x, current_y, width, child_height, mut + rects) current_y += child_height remaining_height -= child_height } @@ -415,9 +422,9 @@ fn (t SplitTree) remove_editor_from_node(node SplitNode, target_id int) ?SplitNo } if new_children.len == 0 { - return none // container is empty + return none // container is empty } else if new_children.len == 1 { - return new_children[0] // collapse container with single child + return new_children[0] // collapse container with single child } else { // recalculate ratios mut new_ratios := []f64{} @@ -428,7 +435,7 @@ fn (t SplitTree) remove_editor_from_node(node SplitNode, target_id int) ?SplitNo return SplitContainer{ ...node children: new_children - ratios: new_ratios + ratios: new_ratios } } } @@ -438,4 +445,3 @@ fn (t SplitTree) remove_editor_from_node(node SplitNode, target_id int) ?SplitNo pub fn (t SplitTree) count() int { return t.get_all_editor_ids().len } - diff --git a/theme.v b/theme.v index 1978afe5..42670b63 100644 --- a/theme.v +++ b/theme.v @@ -3,30 +3,29 @@ module theme import tauraamui.bobatea as tea import palette -pub const dark_theme_name = "dark" -pub const light_theme_name = "light" +pub const dark_theme_name = 'dark' +pub const light_theme_name = 'light' pub struct Theme { pub: - name string @[required] - bg_color tea.Color @[required] - fg_color tea.Color - highlight_bg_color tea.Color @[required] - petal_pink tea.Color @[required] - petal_green tea.Color @[required] - petal_red tea.Color @[required] - subtle_light_grey tea.Color @[required] - + name string @[required] + bg_color tea.Color @[required] + fg_color tea.Color + highlight_bg_color tea.Color @[required] + petal_pink tea.Color @[required] + petal_green tea.Color @[required] + petal_red tea.Color @[required] + subtle_light_grey tea.Color @[required] } pub const dark_theme = Theme{ - name: "dark" - bg_color: palette.matte_black_bg_color + name: 'dark' + bg_color: palette.matte_black_bg_color highlight_bg_color: tea.Color.ansi(239) - petal_pink: tea.Color.ansi(219) - petal_green: tea.Color.ansi(84) - petal_red: tea.Color.ansi(196) - subtle_light_grey: tea.Color.ansi(241) + petal_pink: tea.Color.ansi(219) + petal_green: tea.Color.ansi(84) + petal_red: tea.Color.ansi(196) + subtle_light_grey: tea.Color.ansi(241) } const light_petal_pink = tea.Color.ansi(200) @@ -34,12 +33,11 @@ const light_petal_green = tea.Color.ansi(76) const light_subtle_light_grey = tea.Color.ansi(248) pub const light_theme = Theme{ - name: "light" - bg_color: tea.Color.ansi(231) + name: 'light' + bg_color: tea.Color.ansi(231) highlight_bg_color: tea.Color.ansi(239) - petal_pink: light_petal_pink - petal_green: light_petal_green - petal_red: tea.Color.ansi(196) - subtle_light_grey: light_subtle_light_grey + petal_pink: light_petal_pink + petal_green: light_petal_green + petal_red: tea.Color.ansi(196) + subtle_light_grey: light_subtle_light_grey } - diff --git a/version_dialog.v b/version_dialog.v index 0fd6676d..c448a69e 100644 --- a/version_dialog.v +++ b/version_dialog.v @@ -51,19 +51,23 @@ fn (mut m VersionModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { fn (m VersionModel) view(mut r_ctx tea.Context) { r_ctx.draw_rect(0, 0, m.width, m.height) - width := m.width - 2 + width := m.width - 2 height := m.height - 2 subtle_bordered_layout.size(m.width, m.height).render(mut r_ctx, fn [width, height] (mut ctx tea.Context) { ctx.reset_color() - version_label := "project petal version (${version})" + version_label := 'project petal version (${version})' ctx.draw_text((width / 2) - tea.visible_len(version_label) / 2, height / 2, version_label) }) } -fn (m VersionModel) width() int { return m.width } +fn (m VersionModel) width() int { + return m.width +} -fn (m VersionModel) height() int { return m.height } +fn (m VersionModel) height() int { + return m.height +} fn (m VersionModel) debug_data() DebugData { return DebugData{} @@ -74,5 +78,3 @@ fn (m VersionModel) clone() tea.Model { ...m } } - - From 4d3e3dbcee748d5ca05dc870f302a1eb63d0fd11 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Mon, 12 Jan 2026 11:00:08 +0000 Subject: [PATCH 17/29] chore: pass in ui elements colors from theme or use theme directly --- editor_workspace.v | 2 +- file_picker.v | 12 ++++-------- input_field.v | 9 ++------- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/editor_workspace.v b/editor_workspace.v index 21d31e49..f2ddc5f5 100644 --- a/editor_workspace.v +++ b/editor_workspace.v @@ -494,7 +494,7 @@ fn (mut m EditorWorkspaceModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { 'q' { cmds << close_active_split } 'qa' { cmds << tea.quit } 'debug' { cmds << toggle_debug_screen } - 'version' { cmds << open_version_dialog } + 'version' { cmds << open_version_dialog(m.theme) } 'vs' { cmds << split_vertically } else { cmds << raise_error("unknown command '${msg.command}'") } } diff --git a/file_picker.v b/file_picker.v index a344ee1c..1783c092 100644 --- a/file_picker.v +++ b/file_picker.v @@ -57,7 +57,7 @@ pub fn close_file_picker() tea.Msg { pub fn (mut m FilePickerModel) init() ?tea.Cmd { m.loading = true - m.input_field = boba.BorderedInputField.new() + m.input_field = boba.BorderedInputField.new(m.theme.petal_pink) m.input_field.focus() mut cmds := []tea.Cmd{} if input_init_cmd := m.input_field.init() { @@ -320,12 +320,8 @@ fn (m FilePickerModel) max_visible_items() int { return if max_height > 0 { max_height } else { 0 } } -const subtle_bordered_layout = tea.new_layout() - .border(.normal) - .border_color(palette.subtle_border_fg_color) - -fn (m FilePickerModel) render_file_results_pane(mut r_ctx tea.Context, width int, height int) { - subtle_bordered_layout.size(width, height).render(mut r_ctx, fn [m, width, height] (mut ctx tea.Context) { +fn (m FilePickerModel) render_file_results_pane(mut r_ctx tea.Context, width int, height int, border_color tea.Color) { + tea.new_layout().border(.normal).border_color(border_color).size(width, height).render(mut r_ctx, fn [m, width, height] (mut ctx tea.Context) { max_width := width - 2 max_height := height - 2 ctx.set_clip_area(tea.ClipArea{0, 0, max_width - 1, max_height}) @@ -391,7 +387,7 @@ fn (m FilePickerModel) view(mut ctx tea.Context) { ctx.draw_rect(0, 0, m.width, m.height) max_results_height := m.height - 3 - m.render_file_results_pane(mut ctx, m.width, max_results_height) + m.render_file_results_pane(mut ctx, m.width, max_results_height, m.theme.petal_pink) ctx.push_offset(tea.Offset{ y: max_results_height }) m.input_field.view(mut ctx) diff --git a/input_field.v b/input_field.v index 66d33840..418fbe3e 100644 --- a/input_field.v +++ b/input_field.v @@ -3,7 +3,6 @@ module boba import math import time import tauraamui.bobatea as tea -import palette const frames_per_cycle = 50.0 @@ -21,16 +20,12 @@ mut: focused bool } -const subtle_bordered_layout = tea.new_layout() - .border(.normal) - .border_color(palette.subtle_border_fg_color) - const no_bordered_layout = tea.new_layout() .border(.none) -pub fn BorderedInputField.new() InputField { +pub fn BorderedInputField.new(border_color tea.Color) InputField { return InputField{ - layout: subtle_bordered_layout + layout: tea.new_layout().border(.normal).border_color(border_color) } } From 7282b64635bcfab64b0ccfae57f36b3aaccd4173 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Mon, 12 Jan 2026 11:00:28 +0000 Subject: [PATCH 18/29] chore(ui): tidy up the color palette shade defs --- theme.v | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/theme.v b/theme.v index 42670b63..9086e324 100644 --- a/theme.v +++ b/theme.v @@ -3,29 +3,31 @@ module theme import tauraamui.bobatea as tea import palette -pub const dark_theme_name = 'dark' -pub const light_theme_name = 'light' +pub const dark_theme_name = "dark" +pub const light_theme_name = "light" pub struct Theme { pub: - name string @[required] - bg_color tea.Color @[required] - fg_color tea.Color - highlight_bg_color tea.Color @[required] - petal_pink tea.Color @[required] - petal_green tea.Color @[required] - petal_red tea.Color @[required] - subtle_light_grey tea.Color @[required] + name string @[required] + bg_color tea.Color @[required] + fg_color tea.Color + highlight_bg_color tea.Color @[required] + petal_pink tea.Color @[required] + petal_green tea.Color @[required] + petal_red tea.Color @[required] + subtle_light_grey tea.Color @[required] } +const dark_petal_red = tea.Color.ansi(196) + pub const dark_theme = Theme{ - name: 'dark' - bg_color: palette.matte_black_bg_color + name: "dark" + bg_color: palette.matte_black_bg_color highlight_bg_color: tea.Color.ansi(239) - petal_pink: tea.Color.ansi(219) - petal_green: tea.Color.ansi(84) - petal_red: tea.Color.ansi(196) - subtle_light_grey: tea.Color.ansi(241) + petal_pink: tea.Color.ansi(219) + petal_green: tea.Color.ansi(84) + petal_red: dark_petal_red + subtle_light_grey: tea.Color.ansi(241) } const light_petal_pink = tea.Color.ansi(200) @@ -33,11 +35,12 @@ const light_petal_green = tea.Color.ansi(76) const light_subtle_light_grey = tea.Color.ansi(248) pub const light_theme = Theme{ - name: 'light' - bg_color: tea.Color.ansi(231) + name: "light" + bg_color: tea.Color.ansi(231) highlight_bg_color: tea.Color.ansi(239) - petal_pink: light_petal_pink - petal_green: light_petal_green - petal_red: tea.Color.ansi(196) - subtle_light_grey: light_subtle_light_grey + petal_pink: light_petal_pink + petal_green: light_petal_green + petal_red: dark_theme.petal_red + subtle_light_grey: light_subtle_light_grey } + From 379b81fc589fd0907ab15d118e5bf6deb4993169 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Mon, 12 Jan 2026 11:01:01 +0000 Subject: [PATCH 19/29] feat: pass in consistent (used elsewhere) shade for dialog border --- version_dialog.v | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/version_dialog.v b/version_dialog.v index c448a69e..7ae77938 100644 --- a/version_dialog.v +++ b/version_dialog.v @@ -1,18 +1,23 @@ module main import tauraamui.bobatea as tea +import theme struct VersionModel { + theme theme.Theme width int height int version string } -fn open_version_dialog() tea.Msg { - return OpenDialogMsg{ - model: VersionModel{ - width: 52 - height: 5 +fn open_version_dialog(ttheme theme.Theme) tea.Cmd { + return fn [ttheme] () tea.Msg { + return OpenDialogMsg{ + model: VersionModel{ + theme: ttheme + width: 52 + height: 5 + } } } } @@ -54,7 +59,7 @@ fn (m VersionModel) view(mut r_ctx tea.Context) { width := m.width - 2 height := m.height - 2 - subtle_bordered_layout.size(m.width, m.height).render(mut r_ctx, fn [width, height] (mut ctx tea.Context) { + tea.new_layout().border(.normal).border_color(m.theme.petal_pink).size(m.width, m.height).render(mut r_ctx, fn [width, height] (mut ctx tea.Context) { ctx.reset_color() version_label := 'project petal version (${version})' ctx.draw_text((width / 2) - tea.visible_len(version_label) / 2, height / 2, version_label) From eda6d49612cf9847d078152374fa9991a5652d0e Mon Sep 17 00:00:00 2001 From: tauraamui Date: Tue, 13 Jan 2026 00:39:57 +0000 Subject: [PATCH 20/29] chore: better label magic number to deduct from clip area width --- input_field.v | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/input_field.v b/input_field.v index 418fbe3e..24485076 100644 --- a/input_field.v +++ b/input_field.v @@ -153,7 +153,9 @@ pub fn (m InputField) view(mut r_ctx tea.Context) { m.layout.size(width, height).render(mut r_ctx, fn [cursor_pos, cursor_color, width, value_runes, input_prefix, prefix_padding] (mut l_ctx tea.Context) { l_ctx.set_clip_area(tea.ClipArea{0, 0, width - 3, 1}) defer { l_ctx.clear_clip_area() } - l_ctx.draw_rect(0, 0, width - 2, 1) + + left_right_border_cells_to_deduct_from_rects_full_width := 2 + l_ctx.draw_rect(0, 0, width - left_right_border_cells_to_deduct_from_rects_full_width, 1) l_ctx.draw_text(0, 0, input_prefix) input_text_offset := l_ctx.push_offset(tea.Offset{ From 9c81fa8ef3ad910882252c512222661a5d8f4519 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Tue, 13 Jan 2026 18:54:17 +0000 Subject: [PATCH 21/29] feat: enable easy switching of light/dark themes --- main.v | 5 +++-- make.vsh | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/main.v b/main.v index 1c6f78e1..07f494ed 100644 --- a/main.v +++ b/main.v @@ -1,11 +1,12 @@ module main +import os import tauraamui.bobatea as tea import cfg fn main() { - config := cfg.Config.new(load_from_path: none).set_theme(cfg.light_theme_name) - // config := cfg.Config.new(load_from_path: none) + theme_name := os.getenv("PETAL_THEME") + config := cfg.Config.new(load_from_path: none).set_theme(theme_name) mut petal_model := PetalModel.new(config) mut app := tea.new_program(mut petal_model) diff --git a/make.vsh b/make.vsh index 12ff13b1..b6842173 100755 --- a/make.vsh +++ b/make.vsh @@ -17,6 +17,8 @@ context.task( run: |self| system('v . -o ${app_name}') ) context.task(name: 'run', depends: ['_generate-git-hash'], run: |self| system('v -g run .')) +context.task(name: 'run-d', depends: ['_generate-git-hash'], run: |self| system('export PETAL_THEME=dark && v -g run .')) +context.task(name: 'run-l', depends: ['_generate-git-hash'], run: |self| system('export PETAL_THEME=light && v -g run .')) context.task(name: 'compile-make', run: |self| system('v -prod -skip-running make.vsh')) // TEST TASKS From aacc4d592ef0054b5cb91f9ff87ad987c99b1d84 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Tue, 13 Jan 2026 22:53:59 +0000 Subject: [PATCH 22/29] chore(ui): improve highlight shade for file picker --- theme.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theme.v b/theme.v index 9086e324..775e52c8 100644 --- a/theme.v +++ b/theme.v @@ -37,7 +37,7 @@ const light_subtle_light_grey = tea.Color.ansi(248) pub const light_theme = Theme{ name: "light" bg_color: tea.Color.ansi(231) - highlight_bg_color: tea.Color.ansi(239) + highlight_bg_color: tea.Color.ansi(218) petal_pink: light_petal_pink petal_green: light_petal_green petal_red: dark_theme.petal_red From 987697e220a34112dbe90600147c6a9fc1b0ee81 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Wed, 14 Jan 2026 16:52:52 +0000 Subject: [PATCH 23/29] feat: add task alias for displaying ansi color codes table --- make.vsh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/make.vsh b/make.vsh index b6842173..ff279aa2 100755 --- a/make.vsh +++ b/make.vsh @@ -63,6 +63,13 @@ context.task( } ) +context.task( + name: 'ansi-color-codes', + run: fn [mut context] (self build.Task) ! { + context.exec('ansi-colour-codes') + } +) + context.task( name: 'ansi-to-rgb' help: 'prompts for single ansi colour code and outputs the RGB components' From 50495b882db3ea658f27183230bbe9e8d7d6dc91 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Thu, 15 Jan 2026 00:27:40 +0000 Subject: [PATCH 24/29] chore(ui): adjust spacer bg color for each theme type --- editor_workspace.v | 6 +++--- theme.v | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/editor_workspace.v b/editor_workspace.v index f2ddc5f5..7b60f06a 100644 --- a/editor_workspace.v +++ b/editor_workspace.v @@ -588,7 +588,7 @@ fn (m EditorWorkspaceModel) view(mut ctx tea.Context) { } fn (m EditorWorkspaceModel) render_status_bar(mut ctx tea.Context) { - ctx.set_bg_color(palette.status_bar_bg_color) + ctx.set_bg_color(m.theme.status_bar_spacer) ctx.draw_rect(0, ctx.window_height() - 2, ctx.window_width(), 1) ctx.reset_bg_color() @@ -657,7 +657,7 @@ fn (m EditorWorkspaceModel) render_status_blocks(mut ctx tea.Context) { ctx.reset_color() // status bar spacer left end cap - ctx.set_color(palette.status_bar_bg_color) + ctx.set_color(m.theme.status_bar_spacer) ctx.draw_text(1, 0, glyphs.slant_left_flat_top) ctx.reset_color() // @@ -669,7 +669,7 @@ fn (m EditorWorkspaceModel) render_status_blocks(mut ctx tea.Context) { ctx.push_offset(tea.Offset{ x: cursor_pos_segment_start }) // status bar spacer right end cap - ctx.set_color(palette.status_bar_bg_color) + ctx.set_color(m.theme.status_bar_spacer) ctx.draw_text(-1, 0, glyphs.slant_right_flat_top) ctx.reset_color() // diff --git a/theme.v b/theme.v index 775e52c8..d117031d 100644 --- a/theme.v +++ b/theme.v @@ -1,7 +1,6 @@ module theme import tauraamui.bobatea as tea -import palette pub const dark_theme_name = "dark" pub const light_theme_name = "light" @@ -16,18 +15,20 @@ pub: petal_green tea.Color @[required] petal_red tea.Color @[required] subtle_light_grey tea.Color @[required] + status_bar_spacer tea.Color @[required] } const dark_petal_red = tea.Color.ansi(196) pub const dark_theme = Theme{ name: "dark" - bg_color: palette.matte_black_bg_color - highlight_bg_color: tea.Color.ansi(239) + bg_color: tea.Color.ansi(233) + highlight_bg_color: tea.Color.ansi(139) petal_pink: tea.Color.ansi(219) petal_green: tea.Color.ansi(84) petal_red: dark_petal_red subtle_light_grey: tea.Color.ansi(241) + status_bar_spacer: tea.Color.ansi(234) } const light_petal_pink = tea.Color.ansi(200) @@ -42,5 +43,6 @@ pub const light_theme = Theme{ petal_green: light_petal_green petal_red: dark_theme.petal_red subtle_light_grey: light_subtle_light_grey + status_bar_spacer: tea.Color.ansi(255) } From 4c51f2a370b0966c26849f5f5faa71aa448ca6ba Mon Sep 17 00:00:00 2001 From: tauraamui Date: Thu, 15 Jan 2026 03:52:34 +0000 Subject: [PATCH 25/29] chore(ui): adjust shade used for status bar branch name --- editor_workspace.v | 9 +++++---- theme.v | 7 ++++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/editor_workspace.v b/editor_workspace.v index 7b60f06a..7f8f7055 100644 --- a/editor_workspace.v +++ b/editor_workspace.v @@ -638,21 +638,22 @@ fn (m EditorWorkspaceModel) render_status_blocks(mut ctx tea.Context) { ctx.reset_color() ctx.push_offset(tea.Offset{ x: 2 }) - ctx.set_color(palette.status_branch_name_bg_color) + branch_name_bg_color := m.theme.status_branch_name + ctx.set_color(branch_name_bg_color) ctx.draw_text(0, 0, '${glyphs.slant_left_flat_top}${glyphs.block}') ctx.reset_color() ctx.push_offset(tea.Offset{ x: 2 }) branch_name_label := m.active_branch_name() - ctx.set_color(palette.matte_white_fg_color) - ctx.set_bg_color(palette.status_branch_name_bg_color) + ctx.set_color(palette.fg_color(branch_name_bg_color)) + ctx.set_bg_color(branch_name_bg_color) ctx.draw_text(0, 0, branch_name_label) ctx.reset_bg_color() ctx.reset_color() ctx.push_offset(tea.Offset{ x: tea.visible_len(branch_name_label) }) - ctx.set_color(palette.status_branch_name_bg_color) + ctx.set_color(branch_name_bg_color) ctx.draw_text(-1, 0, '${glyphs.block}${glyphs.slant_right_flat_bottom}') ctx.reset_color() diff --git a/theme.v b/theme.v index d117031d..e7880001 100644 --- a/theme.v +++ b/theme.v @@ -15,20 +15,24 @@ pub: petal_green tea.Color @[required] petal_red tea.Color @[required] subtle_light_grey tea.Color @[required] + status_bar_spacer tea.Color @[required] + status_branch_name tea.Color @[required] } +const dark_petal_pink = tea.Color.ansi(219) const dark_petal_red = tea.Color.ansi(196) pub const dark_theme = Theme{ name: "dark" bg_color: tea.Color.ansi(233) highlight_bg_color: tea.Color.ansi(139) - petal_pink: tea.Color.ansi(219) + petal_pink: dark_petal_pink petal_green: tea.Color.ansi(84) petal_red: dark_petal_red subtle_light_grey: tea.Color.ansi(241) status_bar_spacer: tea.Color.ansi(234) + status_branch_name: dark_petal_pink } const light_petal_pink = tea.Color.ansi(200) @@ -44,5 +48,6 @@ pub const light_theme = Theme{ petal_red: dark_theme.petal_red subtle_light_grey: light_subtle_light_grey status_bar_spacer: tea.Color.ansi(255) + status_branch_name: tea.Color.ansi(219) } From 15bdb6a3930fb3134a495b34120081bdcc314be9 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Thu, 15 Jan 2026 14:45:26 +0000 Subject: [PATCH 26/29] chore(ui): swap theme color usage for status file name on bar --- editor_workspace.v | 9 +++++---- theme.v | 28 +++++++++++++++++----------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/editor_workspace.v b/editor_workspace.v index 7f8f7055..ab4c8736 100644 --- a/editor_workspace.v +++ b/editor_workspace.v @@ -619,21 +619,22 @@ fn (m EditorWorkspaceModel) render_status_blocks(mut ctx tea.Context) { ctx.reset_color() ctx.push_offset(tea.Offset{ x: 2 }) - ctx.set_color(palette.status_file_name_bg_color) + file_name_bg_color := m.theme.status_file_name + ctx.set_color(file_name_bg_color) ctx.draw_text(0, 0, '${glyphs.slant_left_flat_top}${glyphs.block}') ctx.reset_color() ctx.push_offset(tea.Offset{ x: 2 }) file_name_label := m.active_file_name() - ctx.set_color(palette.fg_color(palette.status_file_name_bg_color)) - ctx.set_bg_color(palette.status_file_name_bg_color) + ctx.set_color(palette.fg_color(file_name_bg_color)) + ctx.set_bg_color(file_name_bg_color) ctx.draw_text(0, 0, file_name_label) ctx.reset_bg_color() ctx.reset_color() ctx.push_offset(tea.Offset{ x: tea.visible_len(file_name_label) }) - ctx.set_color(palette.status_file_name_bg_color) + ctx.set_color(file_name_bg_color) ctx.draw_text(0, 0, '${glyphs.block}${glyphs.slant_right_flat_bottom}') ctx.reset_color() ctx.push_offset(tea.Offset{ x: 2 }) diff --git a/theme.v b/theme.v index e7880001..7844dca0 100644 --- a/theme.v +++ b/theme.v @@ -7,17 +7,19 @@ pub const light_theme_name = "light" pub struct Theme { pub: - name string @[required] - bg_color tea.Color @[required] - fg_color tea.Color - highlight_bg_color tea.Color @[required] - petal_pink tea.Color @[required] - petal_green tea.Color @[required] - petal_red tea.Color @[required] - subtle_light_grey tea.Color @[required] - - status_bar_spacer tea.Color @[required] - status_branch_name tea.Color @[required] + name string @[required] + bg_color tea.Color @[required] + fg_color tea.Color + highlight_bg_color tea.Color @[required] + petal_pink tea.Color @[required] + petal_green tea.Color @[required] + petal_red tea.Color @[required] + subtle_light_grey tea.Color @[required] + + status_file_name tea.Color @[required] + status_branch_name tea.Color @[required] + status_bar_spacer tea.Color @[required] + } const dark_petal_pink = tea.Color.ansi(219) @@ -31,6 +33,8 @@ pub const dark_theme = Theme{ petal_green: tea.Color.ansi(84) petal_red: dark_petal_red subtle_light_grey: tea.Color.ansi(241) + + status_file_name: tea.Color.ansi(239) status_bar_spacer: tea.Color.ansi(234) status_branch_name: dark_petal_pink } @@ -47,6 +51,8 @@ pub const light_theme = Theme{ petal_green: light_petal_green petal_red: dark_theme.petal_red subtle_light_grey: light_subtle_light_grey + + status_file_name: tea.Color.ansi(242) status_bar_spacer: tea.Color.ansi(255) status_branch_name: tea.Color.ansi(219) } From 45f580dddd82ba771b94acae4d0c13ef527e34ec Mon Sep 17 00:00:00 2001 From: tauraamui Date: Sat, 17 Jan 2026 14:52:13 +0000 Subject: [PATCH 27/29] fix: enable test to run successfully due to intialising theme --- file_picker_test.v | 2 ++ 1 file changed, 2 insertions(+) diff --git a/file_picker_test.v b/file_picker_test.v index 299158fb..ca2ae4b4 100644 --- a/file_picker_test.v +++ b/file_picker_test.v @@ -2,6 +2,7 @@ module main import tauraamui.bobatea.lib.draw import tauraamui.bobatea as tea +import theme import lib.files struct MockFilesFinder { @@ -20,6 +21,7 @@ fn (mut f MockFilesFinder) search(root string) { fn test_file_list_loads_files() { mut fp := FilePickerModel{ + theme: theme.light_theme finder: MockFilesFinder{} } From d3321d09ed20856e2a5f778fbc7ef0c02307986f Mon Sep 17 00:00:00 2001 From: tauraamui Date: Wed, 21 Jan 2026 11:54:51 +0000 Subject: [PATCH 28/29] chore: define status bar mode color segment defs into theme type --- theme.v | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/theme.v b/theme.v index 7844dca0..f8334dee 100644 --- a/theme.v +++ b/theme.v @@ -20,6 +20,11 @@ pub: status_branch_name tea.Color @[required] status_bar_spacer tea.Color @[required] + status_green tea.Color @[required] + status_purple tea.Color @[required] + status_cyan tea.Color @[required] + status_orange tea.Color @[required] + status_lilac tea.Color @[required] } const dark_petal_pink = tea.Color.ansi(219) @@ -37,6 +42,12 @@ pub const dark_theme = Theme{ status_file_name: tea.Color.ansi(239) status_bar_spacer: tea.Color.ansi(234) status_branch_name: dark_petal_pink + + status_green: tea.Color.ansi(120) + status_purple: tea.Color.ansi(105) + status_cyan: tea.Color.ansi(117) + status_orange: tea.Color.ansi(222) + status_lilac: tea.Color.ansi(134) } const light_petal_pink = tea.Color.ansi(200) @@ -55,5 +66,11 @@ pub const light_theme = Theme{ status_file_name: tea.Color.ansi(242) status_bar_spacer: tea.Color.ansi(255) status_branch_name: tea.Color.ansi(219) + + status_green: tea.Color.ansi(120) + status_purple: tea.Color.ansi(105) + status_cyan: tea.Color.ansi(117) + status_orange: tea.Color.ansi(222) + status_lilac: tea.Color.ansi(134) } From 7b4248a62f78e4ec28e9aa52711c1a45cd39e5a6 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Wed, 21 Jan 2026 11:55:03 +0000 Subject: [PATCH 29/29] chore: resolve mode segment colors with palette derived from theme --- editor_workspace.v | 7 ++++--- mode.v | 17 +++++++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/editor_workspace.v b/editor_workspace.v index ab4c8736..93abcf0c 100644 --- a/editor_workspace.v +++ b/editor_workspace.v @@ -600,21 +600,22 @@ fn (m EditorWorkspaceModel) render_status_blocks(mut ctx tea.Context) { status_bar_offset := ctx.push_offset(tea.Offset{ y: ctx.window_height() - 2 }) defer { ctx.clear_offsets_from(status_bar_offset) } - ctx.set_color(m.mode.color()) + mode_color := m.mode.color(m.theme) + ctx.set_color(mode_color) ctx.draw_text(0, 0, '${glyphs.left_rounded}${glyphs.block}') ctx.reset_color() blocks_offset := ctx.push_offset(tea.Offset{ x: 2 }) mode_label := m.mode.str() ctx.set_color(palette.matte_black_fg_color) - ctx.set_bg_color(m.mode.color()) + ctx.set_bg_color(mode_color) ctx.draw_text(0, 0, mode_label) ctx.reset_bg_color() ctx.reset_color() ctx.push_offset(tea.Offset{ x: tea.visible_len(mode_label) }) - ctx.set_color(m.mode.color()) + ctx.set_color(mode_color) ctx.draw_text(0, 0, '${glyphs.block}${glyphs.slant_right_flat_bottom}') ctx.reset_color() ctx.push_offset(tea.Offset{ x: 2 }) diff --git a/mode.v b/mode.v index c9e47cb7..59ff401f 100644 --- a/mode.v +++ b/mode.v @@ -2,6 +2,7 @@ module main import tauraamui.bobatea as tea import palette +import theme enum Mode as u8 { normal @@ -13,15 +14,15 @@ enum Mode as u8 { navigation } -fn (m Mode) color() tea.Color { +fn (m Mode) color(ttheme theme.Theme) tea.Color { return match m { - .normal { palette.status_green } - .leader { palette.status_purple } - .command { palette.status_cyan } - .insert { palette.status_orange } - .visual { palette.status_lilac } - .visual_line { palette.status_lilac } - .navigation { palette.status_cyan } + .normal { ttheme.status_green } + .leader { ttheme.status_purple } + .command { ttheme.status_cyan } + .insert { ttheme.status_orange } + .visual { ttheme.status_lilac } + .visual_line { ttheme.status_lilac } + .navigation { ttheme.status_cyan } } }