Complete all open beads tasks (hm-68a, hm-isi, hm-3w5, hm-3rr + close hm-6if/hm-wqb/hm-6qj)#16
Conversation
Closes hm-68a. Introduces a shared `crate::suggest` module backed by a small Levenshtein DP and used in two surfaces: - `himitsu search <query>` now prints `did you mean <path>?` to stderr when the result list is empty, the query is non-tag-only, and the closest existing secret is within the heuristic distance threshold. - A reusable `SecretRefAutocomplete` widget in `tui::widgets` opens beneath the search bar on Ctrl+Space, surfacing up to five closest paths via Up/Down/Enter. The popup is non-modal — the query field keeps consuming printable keys. Also folds in `cargo fmt --all` reformatting and a couple of `clippy --fix` cleanups (unused imports, manual_contains) from the same toolchain pass.
Closes hm-3w5 and hm-3rr. * hm-3w5 — every field in `new_secret.rs` now renders a muted placeholder example when empty + unfocused (path: prod/api/STRIPE_KEY, url: https://example.com, expires_at: never | 30d | 6mo | …, etc.). Generalised the rendering through a single `field_paragraph` helper so the multi-line value field and every single-line field follow the same focus/empty/placeholder rules. Added the same support to the generic `forms::FormView` (used by `remote_add` and any future `ProtoForm`) via a `placeholder` builder on `Field`. * hm-3rr — added a final `[ submit ]` button row at the end of the tab cycle. Enter on the submit step runs the existing `submit()` path. Pressing Esc with any populated field opens a centered unsaved-changes modal with `[ keep editing ] [ save ] [ discard ]` (Left/Right cycle, Enter activates, Esc dismisses to keep editing). An empty form still bails immediately so the prompt only fires when the user actually has something to lose. Tests cover: cycle includes Submit + wraps; Shift-Tab wraps to Submit; Enter on Submit submits; Esc-on-empty cancels; Esc with content opens modal; Right/Left cycle the focus; Enter on Discard emits Cancel; Esc inside modal restores the form. forms::FormView gains 3 placeholder rendering tests.
Closes hm-isi. Adds a `Hint` surface for ambient, persistent guidance text in the bottom-left of the frame — distinct from the toast lane, which is transient and full-width. * `tui::hint` — new module with a `Hint` struct, `MAX_LEN = 60`, multi- byte-safe truncation, and a `render` that paints only the left third of the bottom strip so a coexisting toast can claim the right portion. * `tui::app` — `set_hint`/`clear_hint` accessors plus action wiring on `NewSecretAction::SetHint(_)` / `ClearHint`. Toast suppresses the hint visually (3s TTL means the hint reappears as soon as the toast clears); cancel/created/failed routes also clear the hint state explicitly. * `tui::views::new_secret` — `on_key` snapshots the focused step, detects step transitions caused by navigation keys, and translates them into `SetHint`/`ClearHint` via `hint_for_step`. Tipped steps are Path, Tags, ExpiresAt; everything else clears the strip. Submit/save outcomes are forwarded untouched so an internal validation refocus doesn't churn the action enum. * `tui::harness` integration test (`ambient_hint_tracks_focus_and_clears _on_cancel`) drives the new-secret flow via real key events and asserts the hint is seeded on open, swaps with focus, and clears on cancel.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c7a40ce17e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
| // possibly land on: every secret path search_core just returned for | ||
| // an unfiltered scan. This is cheap (already in memory) and dodges | ||
| // having to re-walk the store when the popup wants to open. | ||
| let corpus: Vec<String> = self.results.iter().map(|r| r.path.clone()).collect(); |
There was a problem hiding this comment.
Build autocomplete corpus from unfiltered results
When the user types a typo that produces zero search hits, refresh_results sets the autocomplete corpus from self.results, which has already been filtered by that typo, so the corpus is empty and Ctrl+Space cannot show the closest existing secret. This defeats the new typo-recovery path in the TUI; the corpus needs to come from an unfiltered scan (as the CLI suggestion path does with search_core(ctx, "", &[])) rather than the current filtered results.
Useful? React with 👍 / 👎.
Summary
Closes every open beads issue in one branch. Three were already implemented on
mainand just needed closing; four required new code.Implemented
crate::suggestmodule (inline DP, no extra crate).himitsu searchprintsdid you mean <path>?to stderr on a zero-result query. ReusableSecretRefAutocompletewidget intui::widgetsis wired into the search bar; Ctrl+Space toggles it, Up/Down navigate, Enter accepts.tui::hintsurface, distinct from the toast lane. Persistent muted text in the left third of the bottom row; auto-suppressed when a toast is on screen. The new-secret form publishes step-aware tips forpath(slashes),tags(comma-separated), andexpires_at(duration grammar) viaNewSecretAction::SetHint/ClearHint.placeholderbuilder onforms::Fieldplus a sharedfield_paragraphhelper innew_secret.rsso every input renders a muted example when empty + unfocused. Concrete placeholders for path/value/description/tags/url/totp/env_key/expires_at.Step::Submittab stop renders a[ submit ]button row; Enter on it runs the existingsubmit(). Esc with any populated field opens a centeredunsaved changesmodal with[ keep editing ] [ save ] [ discard ](Left/Right cycle, Enter activates, Esc dismisses).Already implemented; closed in
.beads/issues.jsonlset/write/search/ls/tag), env DSL (tag:selectors), TUI (Tags step, viewer chips, search row chips). Verified by 90+ tag-related tests.path:in the secret-viewer edit doc renames the secret while preservingcreated_at/history (finish_edit_renames_secret_and_preserves_history).Test results
cargo fmt --all -- --check— clean.cargo clippy --workspace --all-targets -- -D warnings— clean.cargo test --workspace— 602/602 binary tests pass; 94/97 integration tests pass. The 3 failing tests (test_check_*) are pre-existing onmain(a git env quirk in the test setup) and unrelated to this work.Notes for reviewers
Tabis already bound to fold-toggle in the search view. Documented at the call site.cargo fmt/clippy --fixswept up two pre-existing lint nits (unused_importsincli/join.rstests andmanual_containsincommand_palette.rstests). Folded into the suggest commit because the gate requires them.Test plan
cargo buildsucceeds locally.cargo fmt --all -- --checkis clean.cargo clippy --workspace --all-targets -- -D warningsis clean.cargo test --workspace— all binary tests pass; the only integration failures are the 3 pre-existingtest_check_*.n→ see the placeholder examples + bottom-left hint.unsaved changesmodal appears.himitsu search <typo>from the CLI to seedid you mean …?.https://claude.ai/code/session_01XGkUiFcnS4UX8ZHLGE7p2C
Generated by Claude Code