From f274168c5917cca62624c76221d02a14b4864052 Mon Sep 17 00:00:00 2001 From: Priec Date: Sun, 19 Jul 2026 20:36:27 +0200 Subject: [PATCH] gtin and credit card in the canvas --- Cargo.lock | 2 + server | 2 +- todo.md | 142 ------------------------ tui-canvas | 2 +- tui-pages | 2 +- tui-pages_migration_for_client_guide.md | 33 ------ 6 files changed, 5 insertions(+), 178 deletions(-) delete mode 100644 todo.md delete mode 100644 tui-pages_migration_for_client_guide.md diff --git a/Cargo.lock b/Cargo.lock index a588bdc..84defcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7959,9 +7959,11 @@ dependencies = [ "anyhow", "arboard", "async-trait", + "card-validate", "crossterm", "derivative", "email_address", + "gtin-validate", "iban_validate", "jiff", "once_cell", diff --git a/server b/server index 125410b..324c20b 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit 125410b9c68315819eb64ac40302af8bd5665186 +Subproject commit 324c20b39ce8275cb2b02d4d6108eb425e69c601 diff --git a/todo.md b/todo.md deleted file mode 100644 index 482213a..0000000 --- a/todo.md +++ /dev/null @@ -1,142 +0,0 @@ -# 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 3f7df9f..562769c 160000 --- a/tui-canvas +++ b/tui-canvas @@ -1 +1 @@ -Subproject commit 3f7df9f6fd4c6a26f7ab7533f19b012c300717d7 +Subproject commit 562769c3d239b4681126176ab33ba49bde1d38e3 diff --git a/tui-pages b/tui-pages index f215b38..981a77c 160000 --- a/tui-pages +++ b/tui-pages @@ -1 +1 @@ -Subproject commit f215b383468e0a9e785eb37f8f838d278b5adb6a +Subproject commit 981a77c86377b28f9297551f22ed99004e9e4a99 diff --git a/tui-pages_migration_for_client_guide.md b/tui-pages_migration_for_client_guide.md deleted file mode 100644 index 563f328..0000000 --- a/tui-pages_migration_for_client_guide.md +++ /dev/null @@ -1,33 +0,0 @@ - - 1. Action System - tui-pages intentionally does not provide ActionResolution like client/src/action_engine/action_decider/ - handler.rs:23. Replace it in the client adapter/handler, not in tui-pages. - - Best path: keep the routing logic as app code inside TuiActionHandler::handle_action, or keep a private helper - equivalent to ActionDecider::resolve. ctx.current_view and ctx.focus from tui-pages/src/runtime/mod.rs:181 give - you enough data to route page/canvas/global actions. This is a client philosophy change: routing becomes part of - the app handler, while tui-pages only provides focus/view/input context. - 2. Overlay Types - No tui-pages change needed. Your old OverlayKind maps naturally to the generic O parameter. - - Define something like client-side enum ClientOverlay { CommandBar, SearchPalette, FindFilePalette, Sidebar, - Picker }, then use FocusTarget::Overlay(ClientOverlay::CommandBar) etc. DialogButton(usize) should probably - become FocusTarget::ModalItem(usize) if you use the modal path, because tui-pages separates simple named overlays - from modal item focus in tui-pages/src/focus/target.rs:19. - 3. String vs Type-Based Modes - This is not a real incompatibility. ModeId is just a typed wrapper over strings and implements AsRef/ - From in tui-pages/src/runtime/mod.rs:13. Your current KeyMode::as_str() values already match the tui- - pages::modes constants. - - The actual migration is moving mode calculation from FocusTarget::mode_hint() in client/src/focus_manager/ - target.rs:50 into PageSpec::modes(...). Since page_spec(view, state, focus) receives focus, you can preserve the - same behavior there. Watch one behavior: the old client converts plain insert-mode chars into - CanvasAction::InsertChar in client/src/input_pipeline/pipeline.rs:90; tui-pages returns PipelineResponse::Type - instead. Preserve that in TuiActionHandler::handle_text. - 4. Page Identification - PageIdentifier should likely disappear. It duplicates AppView. tui-pages already gives ctx.current_view, so - checks like PageIdentifier::Form(_) become matches!(ctx.current_view, AppView::Form(_)). - - This is a natural cleanup, not a tui-pages gap. The only case to keep a separate identifier is if you - intentionally want to collapse several AppView variants into one routing bucket. Even then, make it a client- - local helper derived from AppView, not a runtime concept.