Skip to content

fix: right-align config values, and drop the search screen the host already provides - #104

Merged
serkanalgur merged 1 commit into
mainfrom
fix/config-row-layout
Sep 30, 2026
Merged

serkanalgur merged 1 commit into
mainfrom
fix/config-row-layout

Conversation

@serkanalgur

Copy link
Copy Markdown
Owner

Two corrections after reading OpenCode's dialog-select source

I checked the host implementation (packages/tui/src/ui/dialog-select.tsx) rather than only the plugin type definitions, and found I had reasoned from the wrong file last release.

1. The search screen was redundant, and worse than what the host does

Every ui.dialog.select already renders a live fuzzy filter over title and category, weighted 2:1 toward title, with a Search input drawn at the top of each dialog. The installed plugin API exposes neither skipFilter nor renderFilter, so it cannot be turned off.

The search screen added in 2.14.0 was therefore redundant — and worse than the host's: it required pressing Enter into a prompt before a single character could be typed, and its matching was not live. Removed, with no replacement affordance, since the filter is already on every list.

The config flow search describe (8 tests) is deleted, not weakened: there is no remaining our behaviour to test. A guard now asserts the adapter never passes skipFilter/renderFilter.

2. Values now right-align in footer

The host renders footer right-aligned against a flexing title. Values used to sit in the description immediately after the label, so every row's value began at a different column and comparing two settings meant reading nine rows.

One rule across all three screens: title names the row, description disambiguates it, footer reports its current state.

maxTotalCost                                    25  *
maxCostPerTask                                  1
alertThreshold                                  0.2

Three things verified rather than assumed

  • current also moves the cursor — it draws ● and calls setStore("selected", currentIndex). On a per-block field list it would park the cursor on whichever field happened to be current and skip the first row. Set on the hub's scope row only.
  • category grouping is left unused. A field list is per-block and its title already reads Nexus configuration — budget, so a bold accent header would repeat it. Left documented at fieldOptions rather than added as a redundant header.
  • disabled rows are filtered out, not dimmed. filtered() drops x.disabled !== true before anything is drawn, so a field with no editor is absent. A block claiming "3 settings" while showing two was a lie; the uneditable count now moves to the block's description, the one row that survives the filter. Zero for every block shipped today.

A bug the restyle exposed

The dirty marker was driven by the adapter's whole-draft configIsDirty, so staging one field marked every row in the block. In a description that was merely redundant; in a right-aligned column it is actively harmful — that column is exactly what a user scans to find their own edit, and it lied on every untouched row. configFieldDirty now marks the row and only the row, pinned by a test asserting exactly one of three siblings is marked.

Verified

bun test 1385 pass / 0 fail, bunx tsc --noEmit clean, bun run lint clean (3 pre-existing infos), bun run build clean.

I also rendered the real rows against the real config and read them: staging budget.maxTotalCost marks that row and its block, leaves selfHealing untouched, and flips Save from No changes to write to Write the staged changes to disk.

Note

PR is a fix: — it removes a feature added in 2.14.0 and corrects a bug it introduced. The line count is large because the search screen's removal and the row-routing rewrite overlap in the same functions.

…lready provides

Two corrections after reading OpenCode's own dialog-select.

Every ui.dialog.select already ships a live fuzzy filter over title and
category, weighted 2:1 toward title, with a Search input drawn at the
top of each dialog. The installed plugin API exposes neither skipFilter
nor renderFilter, so the filter cannot be turned off — it is always
there. The search screen added in 2.14.0 was therefore redundant, and
worse than the host's: it required pressing Enter into a prompt before
a single character could be typed, and its matching was not live.
Removed it, with no replacement affordance, since the filter is already
on every list.

Values move to `footer`, which the host renders right-aligned against a
flexing title. They used to sit in the description immediately after
the label, so every row's value began at a different column and the eye
had to read nine rows to compare two settings. The same nine values
now line up in a column of their own. One rule across all three
screens: title names the row, description disambiguates it, footer
reports its current state.

The restyle exposed a bug. The dirty marker was driven by the
adapter's whole-draft configIsDirty, so staging one field marked
every row in the block. Harmless in a description, actively harmful in
a right-aligned column — that column is precisely what a user scans to
find their own edit, and it would have lied on every untouched row.
configFieldDirty marks the row and only the row.

`disabled` rows are filtered out of the host's list rather than
dimmed, so a field with no editor is absent, not faint, and a block
claiming "3 settings" while showing two was a lie. The uneditable
count moves to the block's description, the one row that survives the
filter. Zero for every block shipped today.

`current` also moves the cursor, not just the marker, so it is set on
the hub's scope row only; on a per-block field list it would park the
cursor on whichever field happened to be current and skip the first.

`category` grouping is left unused: a field list is per-block and its
title already names the block, so a category header would repeat it.
@serkanalgur
serkanalgur merged commit e5f37c2 into main Sep 30, 2026
4 checks passed
@serkanalgur
serkanalgur deleted the fix/config-row-layout branch September 30, 2026 09:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant