Skip to content

feat: unify widget hiding into a single hideable-state system#430

Open
zachthedev wants to merge 1 commit into
sirmalloc:mainfrom
zachthedev:unified-hideable-states
Open

feat: unify widget hiding into a single hideable-state system#430
zachthedev wants to merge 1 commit into
sirmalloc:mainfrom
zachthedev:unified-hideable-states

Conversation

@zachthedev

@zachthedev zachthedev commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Closes #428 (my proposal from earlier today; happy to rework or split).

Nine per-widget hide keys (hideNoGit, hideNoJj, hideNoRemote, hideZero, hideWhenEmpty, hideIfDisabled, hideStatus, hideTitle, hideWhenNotFork) become one mechanism: widgets declare conditions via an optional getHideableStates(), enabled states live in metadata.hide as a comma-separated list, and a shared (h)ide… keybind opens a checklist for the selected widget.

Diff guide (116 files): ~55 widget files are the same mechanical conversion; most test changes are one-line key swaps.

Group Files What changed
Core shared/hideable.ts (new), types/Widget.ts state declarations + isHidden/get/set (~95 lines)
Migration utils/migrations.ts, types/Settings.ts v3 -> v4 converts legacy keys per widget type (hideNoGit also covered (no upstream) on Ahead/Behind and the no-PR placeholder on Git PR; on-disk git-pr alias handled); one test per mapping
TUI HideStatesEditor.tsx (new), items-editor wiring one injected h keybind opens the checklist
Renderer utils/renderer.ts merge-target-hidden pass: decorative custom-text/symbol items collapse with their merge target
Widgets ~55 files drop the bespoke toggle, declare states, call isHidden()
Tests ~42 files new: hideable/migration/renderer suites; existing: one-line key swaps
Docs USAGE.md, AGENTS.md new "Hiding Widgets Conditionally" section

Behavior:

  • Existing configs render identically after migration
  • Ahead/Behind's hardcoded 0/0 auto-hide becomes a default-enabled zero state, now opt-out-able
  • New opt-in states: zero (token widgets, session cost/clock, git counters), no-data (speed widgets, Block Timer, usage widgets' [No credentials]-style errors), default-value (Output Style)
  • Reset timers are excluded from no-data: they bind h for the hour toggle; a registry test enforces the h reservation
  • Instant h toggles become a checklist (Space toggle, Enter save)
  • Downgrade: older versions ignore metadata.hide, so hidden placeholders reappear until upgrading back (in USAGE.md)
  • Dropped the dead hide: z.boolean() from WidgetItemSchema; no updatemessage added since rendering is unchanged

Open question: stock Ahead/Behind items now show (hide: zero) in the editor since defaults are surfaced honestly. Want default-enabled states suppressed from that annotation?

Happy to split (core conversion / new states / renderer pass) in whatever order you prefer.

Rebased onto current main. Since this PR opened, #431 (symbol override) and #417/#418 (extra-usage currency) merged, so the shared Git/JJ widgets now carry both their symbol-override changes and this PR's hideable conversion. The conversion was also extended to widgets that landed on main after this PR opened, so the unification stays complete:

  • Cache Hit Rate / Cache Read / Cache Write: the hideWhenEmpty option becomes the empty hideable state (the separate turn/session scope toggle is unchanged).
  • Extra Usage Used: the hideIfDisabled option becomes the disabled hideable state, matching Extra Usage Remaining / Utilization.
  • migrations.ts gains matching cache-read / cache-write / cache-hit-rate / extra-usage-used rules so existing configs migrate cleanly.

Tested: bun run lint clean; bun test green except the same Windows-environment failures main has; exercised against my real config inside and outside repos.

Note: #347 (rules engine) reuses the hide: z.boolean() field this PR removes and adds rule-based hiding; if that lands first I'll rebase the schema change and align the hide semantics with it.

@sirmalloc

Copy link
Copy Markdown
Owner

@zachthedev Can you go ahead and rebase this on main and resolve any issues?

@zachthedev

Copy link
Copy Markdown
Contributor Author

@zachthedev Can you go ahead and rebase this on main and resolve any issues?

Will do sometime in the next 24 hours

@zachthedev zachthedev force-pushed the unified-hideable-states branch from 4d51f8c to 9b4424d Compare June 15, 2026 18:10
@zachthedev

zachthedev commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto main, conflicts resolved. #431 had merged into the same Git/JJ widgets, so they now carry both symbol-override and hideable; also extended the conversion to the Cache and Extra Usage Used widgets that landed since, with v3 -> v4 migration. lint clean; bun test green bar the pre-existing Windows-env failures. Ready for review.

@zachthedev

zachthedev commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

Merge-order note: #457 (Closes #450) also touches CompactionCounter hide-zero. It adds per-metric hide-zero on hideZero; this PR migrates hideZero to the unified system. Whichever merges second needs a small reconcile; if #457 lands first, this PR's conversion must also cover the metric mode.

@zachthedev zachthedev force-pushed the unified-hideable-states branch from 9b4424d to 592db94 Compare June 16, 2026 04:00
@zachthedev

Copy link
Copy Markdown
Contributor Author

@zachthedev Can you go ahead and rebase this on main and resolve any issues?

Ready for review. Thanks much!

Implements the unified hideable-state system proposed in sirmalloc#428.

- Add src/widgets/shared/hideable.ts: widgets declare hideable conditions
  via getHideableStates(); a single metadata.hide key stores enabled state
  keys as a comma-separated list; isHidden() replaces the nine per-widget
  accessors (hideNoGit, hideNoJj, hideNoRemote, hideZero, hideWhenEmpty,
  hideIfDisabled, hideStatus/hideTitle, hideWhenNotFork)
- Convert existing configs through the standard settings migration
  (v3 -> v4): legacy boolean flags map to the states each widget type
  actually hid, so existing configs render identically; default-enabled
  states are folded into written lists to preserve behavior
- Replace per-widget hide toggles with one (h)ide… keybind that opens a
  shared checklist editor (HideStatesEditor); the items editor injects the
  keybind for any widget declaring states and shows enabled states as
  (hide: no-git, zero); 'h' is reserved for this purpose, enforced by a
  registry-wide test
- Add previously missing hide options: zero for token/session-cost/
  session-clock widgets and for the git count widgets (changes,
  insertions, deletions, staged/unstaged/untracked files, conflicts),
  no-data for speed widgets, Block Timer, and the usage widgets' error
  placeholders, default-value for Output Style, and a separate no-data
  state for Git PR
- Git Ahead/Behind's hardcoded 0/0 auto-hide becomes a declared
  default-enabled zero state, so it can now be opted out to show ↑0↓0
- Decorative custom-text/custom-symbol items can opt into
  merge-target-hidden: the renderer collapses them when the widget they
  are merged with renders nothing, so icon prefixes no longer orphan
- Remove the dead top-level hide field from WidgetItemSchema
- Remove shared/git-no-git.ts; slim shared/git-remote.ts and
  shared/extra-usage-disabled.ts to their remaining responsibilities
- Update docs/USAGE.md and tests; add coverage for the hideable module,
  the v3 -> v4 migration, the renderer merge-target pass, and all newly
  hideable states

Co-authored-by: Claude <noreply@anthropic.com>
@zachthedev zachthedev force-pushed the unified-hideable-states branch from 592db94 to 36a0a27 Compare June 18, 2026 00:18
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.

feat: unified hideable-state system for widgets

2 participants