gtin and credit card in the canvas
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -7959,9 +7959,11 @@ dependencies = [
|
||||
"anyhow",
|
||||
"arboard",
|
||||
"async-trait",
|
||||
"card-validate",
|
||||
"crossterm",
|
||||
"derivative",
|
||||
"email_address",
|
||||
"gtin-validate",
|
||||
"iban_validate",
|
||||
"jiff",
|
||||
"once_cell",
|
||||
|
||||
2
server
2
server
Submodule server updated: 125410b9c6...324c20b39c
142
todo.md
142
todo.md
@@ -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<O = ()> {
|
||||
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`.
|
||||
Submodule tui-canvas updated: 3f7df9f6fd...562769c3d2
Submodule tui-pages updated: f215b38346...981a77c863
@@ -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<str>/
|
||||
From<String> 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.
|
||||
Reference in New Issue
Block a user