commit 03ab70cb97a3c53f6123d6bdbd9975a1cfb52973
Author: MrJensK <jens.se@icloud.com>
AuthorDate: Sun Aug 23 21:56:58 2026 +0200
Commit: MrJensK <jens.se@icloud.com>
CommitDate: Sun Aug 23 21:56:58 2026 +0200
Fix the search field's white background and remove the stray section dividers
FTXUI's default InputOption transform inverts the *whole* field while
focused (white foreground + inverted = a solid white bar) -- the
original only ever inverted the single cursor character, relying on
Input's own native blinking-bar cursor for the rest. Added
plainInputTransform() (color + dim-if-placeholder only, no inverted/
bgcolor) and wired it into all three Input instances (search, folder,
trackers).
Also matched the original's actual divider layout (App.tsx has exactly
one grey Rule, below the logo/notice bar, and nothing between the
sidebar and content or above the footer -- just spacing): removed the
two extra plain-white separator()s and recolored the remaining one
(and the folder/trackers prompts' own copy of it) to palette::rule.
---
apps/tui/main.cpp | 26 +++++++++++++++++++++-----
1 file changed, 21 insertions(+), 5 deletions(-)
diff --git a/apps/tui/main.cpp b/apps/tui/main.cpp
index 19123ea..2b6d024 100644
--- a/apps/tui/main.cpp
+++ b/apps/tui/main.cpp
@@ -95,6 +95,17 @@ std::string sourcesStatusLine(const AppState& state) {
return line;
}
+// FTXUI's default InputOption::transform inverts the *entire* field while
+// focused (white-on-default becomes a solid white bar) -- TextField.tsx only
+// ever inverted the single cursor character. Input's own Render() already
+// draws a native blinking bar cursor at the right position regardless of
+// transform, so this just needs to stop painting the rest of the field.
+Element plainInputTransform(InputState state) {
+ Element e = state.element | color(palette::text);
+ if (state.is_placeholder) e = e | dim;
+ return e;
+}
+
std::string filterBarText(const AppState& state) {
std::string s = "[" + sectionLabel(state.section) + "]";
s += state.hideDead ? " hide-dead:on" : " hide-dead:off";
@@ -266,6 +277,7 @@ int main() {
InputOption searchOptions;
searchOptions.multiline = false;
+ searchOptions.transform = plainInputTransform;
searchOptions.on_enter = [&] {
aggregator.search(state.query);
state.view = View::Browser;
@@ -516,6 +528,7 @@ int main() {
InputOption folderOptions;
folderOptions.multiline = false;
+ folderOptions.transform = plainInputTransform;
folderOptions.on_enter = [&] {
state.editingFolder = false;
syncFocus();
@@ -543,6 +556,7 @@ int main() {
InputOption trackersOptions;
trackersOptions.multiline = false;
+ trackersOptions.transform = plainInputTransform;
trackersOptions.on_enter = [&] {
state.editingTrackers = false;
syncFocus();
@@ -627,7 +641,7 @@ int main() {
if (state.editingFolder) {
return vbox({
renderLogo(),
- separator(),
+ separator() | color(palette::rule),
coloredWindow(text("default download folder"), folderInput->Render(), palette::accent),
text("enter: save esc: cancel") | dim,
});
@@ -635,7 +649,7 @@ int main() {
if (state.editingTrackers) {
return vbox({
renderLogo(),
- separator(),
+ separator() | color(palette::rule),
coloredWindow(text("extra trackers"),
vbox({
text(trackersStatus(config.trackers, state.trackersPromptText)) | dim,
@@ -704,15 +718,17 @@ int main() {
if (!state.notice.empty()) top.push_back(filler());
if (!state.notice.empty()) top.push_back(text(state.notice) | color(palette::good));
+ // Only one divider, below the logo/notice bar -- matches the original
+ // (App.tsx's single `showTopRule` Rule); nothing separates the sidebar
+ // from the content or sits above the footer there, just spacing.
return vbox({
hbox(std::move(top)),
- separator(),
+ separator() | color(palette::rule),
hbox({
sidebarMenu->Render() | size(WIDTH, EQUAL, 16),
- separator(),
+ text(" "),
content | flex,
}) | flex,
- separator(),
renderFooter(state),
}) |
flex;