From 445bc6925d3a4de116ed01039e00f7f660ac920c Mon Sep 17 00:00:00 2001 From: Priec Date: Sun, 19 Jul 2026 18:40:44 +0200 Subject: [PATCH] email --- Cargo.lock | 1 + todo.md | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tui-canvas | 2 +- tui-pages | 2 +- 4 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 todo.md diff --git a/Cargo.lock b/Cargo.lock index 54e9f0c..a588bdc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7961,6 +7961,7 @@ dependencies = [ "async-trait", "crossterm", "derivative", + "email_address", "iban_validate", "jiff", "once_cell", diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..482213a --- /dev/null +++ b/todo.md @@ -0,0 +1,142 @@ +# TUI Pages TODO + +## Give form editors a dedicated page-level focus target + +### Current change + +Dynamic form-field visibility belongs to `tui-canvas`. A `FormEditor` knows its +stable logical field indexes and decides which fields are currently visible and +navigable through `DataProvider::field_visibility`. + +The phone-number example previously duplicated that state in `tui-pages`: + +```rust +PageFocusBuilder::new() + .canvas_field_indices(state.editor.navigable_fields()) +``` + +This rebuilt a second per-field list every time the page specification was +refreshed. When the phone extension appeared or disappeared, the page focus +targets changed. `TuiPages::sync_focus_to_spec` consequently called +`FocusManager::register_page`, which resets page-level focus state. Besides the +repeated visibility scan and allocation, this reconciliation caused observable +input lag and made focus behavior depend on two independently maintained lists. + +The immediate fix replaces that dynamic list with one stable page-level target: + +```rust +PageFocusBuilder::new().canvas_form_editor() +``` + +Internal field navigation and visibility are now handled only by `tui-canvas`. +`tui-pages` only needs to know whether focus is inside the form or outside it. + +### Concern with the immediate API + +`canvas_form_editor()` currently represents the complete form using +`FocusTarget::CanvasField(0)`. The zero is a marker, not the editor's current +field. This is correct for the current example because it contains one form +editor and the form-editor key hook only checks `FocusTarget::is_canvas()`. + +However, overloading `CanvasField(0)` is ambiguous and should not become the +finished public design: + +- Two form editors on the same page cannot be identified independently. +- A form editor and a standalone Canvas widget using field index `0` have the + same page-level identity. +- Every form-editor hook currently accepts any Canvas focus target, so multiple + registered form-editor hooks could compete for the same key event. +- Application code inspecting `FocusTarget::CanvasField(index)` may reasonably + assume that `index` is a real field index. For a form editor it is now only a + sentinel. +- Directly setting page focus cannot express which form editor should receive + focus. +- Future focus restoration cannot reliably distinguish multiple editors. + +### Recommended design + +Add a dedicated focus-target variant carrying the form-editor ID: + +```rust +pub enum FocusTarget { + CanvasFormEditor(usize), + CanvasField(usize), + InternalCanvasField(usize), + // ... +} +``` + +Change the builder API to require the same ID used when registering the Canvas +hook: + +```rust +PageFocusBuilder::new().canvas_form_editor(0) +``` + +The responsibilities would then be explicit: + +- `CanvasFormEditor(id)` identifies one complete form at the page level. +- `CanvasField(index)` remains for independently focusable Canvas widgets. +- `InternalCanvasField(index)` retains its existing internal/non-top-level + meaning where it is still needed. +- `FormEditor` remains the sole owner of its current field, internal navigation, + hidden fields, and computed/non-navigable fields. +- `tui-pages` owns navigation between the form and other page components such + as buttons, sections, and other form editors. + +The form-editor key hook must match its registered editor ID instead of merely +checking `FocusTarget::is_canvas()`: + +```rust +matches!( + ctx.focus.as_ref(), + Some(FocusTarget::CanvasFormEditor(focused_id)) if focused_id == id +) +``` + +Cursor behavior and input-layer selection should use the same ID-aware check. +`FocusTarget::is_canvas()` should include the new variant so generic canvas +boundary behavior continues to work. + +### Migration work + +1. Add `FocusTarget::CanvasFormEditor(usize)`. +2. Change `PageFocusBuilder::canvas_form_editor()` to + `canvas_form_editor(editor_id)`. +3. Update form-editor key dispatch, paste dispatch, cursor behavior, and input + context selection to require the matching editor ID. +4. Keep standalone text inputs and text areas matched through their existing + `CanvasField(index)` identities. +5. Update examples to pass the same ID to the page focus builder and + `TuiPagesBuilder::canvas_form_editor(id)`. +6. Search applications for per-field form focus lists and replace those lists + with one `CanvasFormEditor(id)` target at the correct position among the + other page targets. + +Do not retain `CanvasField(0)` as a compatibility sentinel unless backwards +compatibility is explicitly requested. + +### Required tests + +- One form editor keeps the same page focus target while internal field + visibility changes. +- Showing and hiding a dependent field does not call page focus registration or + reset the current page-level target. +- A page containing two form editors routes keys only to the focused editor ID. +- A form editor and standalone Canvas text input can coexist without identity + collisions. +- Exiting the first or last navigable form field moves to the correct adjacent + page component. +- Returning focus to a form preserves or deliberately restores the editor's + internal current field according to the chosen policy. +- Paste and cursor-style routing use the same editor-ID matching as key input. + +### Performance acceptance criteria + +- Page refresh must not enumerate a form editor's visible or navigable fields. +- Changing internal field visibility must not change the page-level focus + target vector. +- `sync_focus_to_spec` must not call `register_page` merely because a form row + appeared or disappeared. +- Typing into a visibility-controlling field must not allocate a second list of + form-field focus targets in `tui-pages`. diff --git a/tui-canvas b/tui-canvas index 55bd618..3f7df9f 160000 --- a/tui-canvas +++ b/tui-canvas @@ -1 +1 @@ -Subproject commit 55bd618804056d0e4758064f721116f395e6606e +Subproject commit 3f7df9f6fd4c6a26f7ab7533f19b012c300717d7 diff --git a/tui-pages b/tui-pages index c52f54d..f215b38 160000 --- a/tui-pages +++ b/tui-pages @@ -1 +1 @@ -Subproject commit c52f54dd41722a2078d3338f6ed1fda9c9fa6b90 +Subproject commit f215b383468e0a9e785eb37f8f838d278b5adb6a