Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .beads/issues.jsonl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{"_type":"issue","id":"hm-68a","title":"introduce an element that is to be used anywhere where i am typing a reference to a secret. it should autocomplete as i type, showing me the closest match based on levenshtein distance. the same code should be used to also add a 'did you mean X' when himitsu search returns no results","status":"open","priority":2,"issue_type":"task","owner":"demo@himitsu.dev","created_at":"2026-05-07T06:22:32Z","created_by":"Cooper Maruyama","updated_at":"2026-05-07T06:22:32Z","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"hm-7ob","title":"Update command palette layout","status":"closed","priority":2,"issue_type":"task","owner":"demo@himitsu.dev","created_at":"2026-05-07T06:06:18Z","created_by":"Cooper Maruyama","updated_at":"2026-05-07T06:10:27Z","started_at":"2026-05-07T06:06:18Z","closed_at":"2026-05-07T06:10:27Z","close_reason":"Updated command palette layout and copy","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"hm-isi","title":"add support for TUI hints - a small bit of text that can render in subtle text on the bottom left corner, sort of like a floating element","status":"open","priority":2,"issue_type":"task","owner":"demo@himitsu.dev","created_at":"2026-05-06T23:21:58Z","created_by":"Cooper Maruyama","updated_at":"2026-05-06T23:21:58Z","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"hm-3w5","title":"all form fields should have placeholders containing an example","status":"open","priority":2,"issue_type":"task","owner":"demo@himitsu.dev","created_at":"2026-05-06T23:20:58Z","created_by":"Cooper Maruyama","updated_at":"2026-05-06T23:20:58Z","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"hm-3rr","title":"add a submit button to the new secret tui, and add a confirm dialog when clicking esc that lets you save or discard","status":"open","priority":2,"issue_type":"task","owner":"demo@himitsu.dev","created_at":"2026-05-06T23:20:25Z","created_by":"Cooper Maruyama","updated_at":"2026-05-06T23:20:25Z","dependency_count":0,"dependent_count":0,"comment_count":0}
Expand Down
50 changes: 44 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ Search is the **root view** -- the app opens straight into a fuzzy filter over e
| `ctrl-n` | new secret |
| `ctrl-s` | switch store |
| `ctrl-y` | copy selected value |
| `Y` (`shift-y`) | copy `himitsu read <ref>` for the selected row |
| `shift-e` | browse env presets |
| `?` | help |
| `esc` / `ctrl-c` | quit |
Expand All @@ -191,13 +192,20 @@ Search is the **root view** -- the app opens straight into a fuzzy filter over e
| Key | Action |
|-----|--------|
| `r` | reveal / hide value |
| `y` | copy to clipboard |
| `y` | copy decrypted value to clipboard |
| `Y` (`shift-y`) | copy `himitsu read <ref>` (the *command*, not the value) |
| `e` | edit in `$EDITOR` |
| `R` | rekey for current recipients |
| `d` | delete (confirms with `y`) |
| `?` | help |
| `esc` | back |

`Y` lets you share *how to fetch* a secret in a PR comment, chat message,
or runbook without putting the plaintext on your clipboard. The clipboard
gets `himitsu read prod/API_KEY` (or `himitsu -r org/repo read …` when the
row lives in a different store from the active one), ready to paste into
a terminal that has the right age key.

![secret viewer](demo/tui-us-012.gif)

### New-secret form
Expand Down Expand Up @@ -502,10 +510,12 @@ tui:

# Per-action keybindings. Each action takes a list, so multiple keys can
# trigger the same action. Unspecified actions fall back to the
# hardcoded defaults documented in [TUI](#tui).
# hardcoded defaults documented in [TUI](#tui). Leader-key chords are
# whitespace-separated (see "Leader-key chords" below).
keys:
new_secret: ["F2", "ctrl+n"]
quit: ["esc", "ctrl+q"]
new_secret: ["F2", "ctrl+n"]
save_secret: ["ctrl+x s"]
quit: ["esc", "ctrl+q"]
```

Binding strings are `<mod>+<mod>+<code>`, lowercased, modifiers first --
Expand All @@ -514,11 +524,39 @@ characters imply `shift` (`"Y"` == `"shift+y"`). Bare letters match
case-insensitively, so `"y"` matches both `y` and `Y`. Malformed bindings
surface as a clear config error at startup.

#### Leader-key chords

Multi-step chord bindings are written as whitespace-separated steps:

```yaml
tui:
keys:
# Press Ctrl+X, then s. Useful when terminal/tmux ate Ctrl+S (XOFF).
save_secret: ["ctrl+x s"]
# Mix and match — multiple bindings per action; chord and single-step
# entries can coexist.
new_secret: ["F2", "ctrl+x ctrl+n"]
```

When you press the first step of a chord, the dashboard shows a `ctrl+x …`
breadcrumb at the bottom of the screen and waits for the continuation.
A non-continuation key aborts the chord (no spurious action fires; the
breadcrumb flips to `chord aborted: …`). Single-step bindings still flow
through each view's normal key handling, so you can keep typing into the
search box without your letters getting eaten.

There is no chord timeout — the dispatcher resolves on the next key,
not on a wall clock. If you bind both `ctrl+x` (single-step) and
`ctrl+x s` (chord) to different actions, the chord wins: pressing
`ctrl+x` enters the pending state instead of firing the single-step
binding immediately.

The full action list (with defaults) is in
[`rust/src/tui/keymap.rs`](rust/src/tui/keymap.rs):
`quit`, `help`, `command_palette`, `new_secret`, `switch_store`,
`copy_selected`, `envs`, `reveal`, `copy_value`, `rekey`, `edit`, `delete`,
`back`, `save_secret`, `next_field`, `prev_field`, `cancel`.
`copy_selected`, `copy_ref_selected`, `envs`, `reveal`, `copy_value`,
`copy_ref`, `rekey`, `edit`, `delete`, `back`, `save_secret`,
`next_field`, `prev_field`, `cancel`.

## Sync & Store Health

Expand Down
2 changes: 1 addition & 1 deletion rust/src/cli/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ fn collect_stores(ctx: &Context) -> Result<Vec<(String, PathBuf)>> {
///
/// If the path is under `stores_dir`, returns the `org/repo` slug.
/// Otherwise falls back to the full path string.
fn store_label(store: &std::path::Path, ctx: &Context) -> String {
pub(crate) fn store_label(store: &std::path::Path, ctx: &Context) -> String {
if let Ok(rel) = store.strip_prefix(ctx.stores_dir()) {
let s = rel.to_string_lossy().replace('\\', "/");
if !s.is_empty() {
Expand Down
Loading
Loading