add password field

This commit is contained in:
2026-11-01 17:26:30 +07:00
parent b2ce60f0e6
commit acf907461b
9 changed files with 430 additions and 56 deletions
+1 -1
View File
@@ -265,7 +265,7 @@ Wave M1 — unblocks the port; each through the review gate:
| M3 | core | Segmented control ✅ Landed (2026-11-01) | All/Popular/Recent explore filters | bezel's toggle-group pill; single-select only, multiple waits. Shipped as a controlled single-select (`Segmented::new(id, selected)` + `options((value, label))` + `on_change(T)` — exactly one segment always active); muted pill track, the selected segment on a raised `surface` plate with border; left/right/home/end move and activate when the strip has focus (no wrap), `RadioGroup`/`RadioButton` roles with `aria_toggled`; renders hugging via a wrapper so it works in block and flex parents alike. Plain `segmented` feature, no deps. **Follow-ups (2026-11-01):** the selected plate dropped its border for a small raised shadow — a third `Elevations.control` class, profiled per platform like popover/dialog (D24); `Size::Small` became the default and medium/large resolve through the size table — taller tracks (`control_height × 1.15 / 1.35`), a scaled track inset (`0.125 / 0.25 / 0.375 rem`) and segment padding (`0.5× / 0.75× / 0.75×` of `padding_x`), with the large label staying at the medium size; options take optional leading icons (`SegmentedOption::new(value, label).icon(role)`, tuples still accepted through `From`); and the selected plate is now a measured, absolutely-positioned layer that springs between segments (per-segment measure canvases feed keyed geometry; the plate springs on `left` and `width` separately, Switch-style — mounts at its target so the first frame never animates, retargets mid-flight, and snaps under reduce-motion). **Follow-up 2 (2026-11-01):** the track inset also scales per size (`0.125 / 0.25 / 0.375 rem`), and a settled-plate regression test guards the plate's existence and slot (`debug_bounds("segmented-plate")`) |
| M4 | core | Context menu ✅ Landed (2026-11-01) | repo rows, file-tree rows, tab bar | gpuikit attaches the menu to an existing element; bezel keys one-open-at-a-time. Shipped as a self-contained `ContextMenu::new(id).target(el).items(...)` — open state and pointer position are namespaced keyed state, right-click opens at the pointer, left clicks pass through to the target untouched, and any click outside, escape, or activation closes. Composes `Menu`, which gained `trigger_button(MouseButton)` and `open_at(Point)` for pointer-anchored opening (outside-click dismissal now closes on any button). Plain `context_menu` feature, depends on `menu` |
| M5 | core | Empty state ✅ Landed (2026-11-01) | inbox, issues, search misses | bezel: icon + headline + one line of hint. Shipped as `EmptyState::new(headline)` with optional `.icon(role)`, `.hint(text)`, and `.action(element)` — no id (nothing interactive), no size classes (content, always medium metrics); the illustration is the component's own `icon_size × 2` decoration, muted like the hint, `line_height 1.3` on the headline, everything centered. Plain `empty_state` feature, no deps |
| M6 | input | Password input | identity unlock + `nsec` import passphrase | lifts §6.2's "password masking on first need" — the need arrived |
| M6 | core | Password input ✅ Landed (2026-11-01) | identity unlock + `nsec` import passphrase | Lifts §6.2's "password masking on first need" — the need arrived. `InputState::set_password(bool)` masks the painted line one bullet per grapheme (paint-only: the buffer, undo, and caret all stay real; a mask map of grapheme byte offsets converts between the buffer and the painted display for hit-testing, the caret, selection, the IME underline, and `bounds_for_range`). Masked fields refuse copy and cut so the secret never reaches the clipboard; paste, editing, and reveal stay available. No element builder — the flag is state, like read-only. **Follow-up (2026-11-01):** a built-in reveal toggle — an eye button (`IconRole::Eye`/`EyeOff`, native glyphs per platform) pinned inside the field's right edge, shown on every password field. The state now tracks the field kind (`set_password`/`is_password`) and its visibility (`set_revealed`/`is_revealed`) separately, so toggling reveal never changes the field's kind; the paint reserves the button's corner (`right_padding` threaded through `paint`/`store_layout`, with a content mask so text never runs under the icon) |
| M7 | input | Multiline textarea | discussion composer, patch + commit messages | bezel: fixed rows or min/max auto-grow; gpuikit: rows on InputState. Reuses Input's buffer; D12's editor crate stays separate |
| M8 | core | Avatar image | profile pictures in rows and panel tabs | lifts the deferred image slot: gpui `img` + initials fallback (signed adds its pixel-art fallback app-side) |