Skip to content

feat(core): replace the OS keyboard in favor of an in-app keyboard on seed entry - #2564

Open
wired-pasteque wants to merge 1 commit into
developfrom
feat/custom-mnemonic-keyboard
Open

feat(core): replace the OS keyboard in favor of an in-app keyboard on seed entry#2564
wired-pasteque wants to merge 1 commit into
developfrom
feat/custom-mnemonic-keyboard

Conversation

@wired-pasteque

Copy link
Copy Markdown
Collaborator

Seed words were typed into ordinary text fields, so every keystroke passed through the platform IME - a third-party keyboard, the autocorrect/prediction cache, or a malicious accessibility service could read the recovery phrase as it was typed.

Changes:

  • Word fields on import/onboarding are now read-only displays: no platform keyboard, no paste, no selection toolbar.
  • New mnemonic keyboard - letters-only, docked at the bottom with suggestions above.
  • Only letters that keep at least one wordlist word possible are enabled, so a typo can't be typed.
  • Paranoid mode - optional shuffle randomises the layout and reshuffles on every tap (Random.secure(), rejecting arrangements where >6 of 26 keys stay put).
Screen.Recording.2026-08-06.at.6.26.05.PM.mov

@claude

This comment was marked as outdated.

@wired-pasteque wired-pasteque self-assigned this Aug 6, 2026
@wired-pasteque
wired-pasteque force-pushed the feat/custom-mnemonic-keyboard branch from e59680a to 6820556 Compare August 7, 2026 00:39
@wired-pasteque

Copy link
Copy Markdown
Collaborator Author

@claude, check the fixes

@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown

Claude finished @wired-pasteque's task in 3m 33s —— View job


Fix verification — all previous findings addressed ✅

  • Identify new commits since the previous review (branch was force-pushed with fixes folded into 6820556c8)
  • Verify fix: _ensureFieldVisible RangeError guard
  • Verify fix: semantics exposure of word fields / suggestion chips
  • Verify fix: dead onEditingComplete / auto-advance
  • Verify fix: passphrase field autocorrect/suggestions
  • Verify nits: 26-letter layout assert, localization files
  • Post findings

I re-checked every finding from the previous review against the current branch. All the substantive ones are fixed, each with test coverage. I found no new issues introduced by the fixes.

🐛 RangeError after shrinking the length — fixed

lib/core/widgets/mnemonic_widget.dart:562-580 now captures the FocusNode instead of the index and bails in the post-frame callback when the node is no longer in _focusNodes (!_focusNodes.contains(node)). I traced the callback orderings: whether the scroll callback runs before or after the deferred node disposal, the contains identity check runs before any member access on the retired node, so both interleavings are safe. The requested regression test was added too — test/core_test/widgets/mnemonic_widget_test.dart:373-405 deterministically reproduces the exact focus-then-shrink interleaving and asserts no exception.

🔒 Semantics exposure — fixed

  • The whole word grid is wrapped in ExcludeSemantics (mnemonic_widget.dart:931), with a comment explaining that a TextField publishes its text as its semantics value.
  • The suggestion strip is wrapped too (mnemonic_widget.dart:776).
  • Three semantics tests now cover keys, field values, and chips (mnemonic_widget_test.dart:458-507) — including the key one: the typed word is visible on screen but not readable via find.bySemanticsLabel.

Dead code / auto-advance — resolved

onEditingComplete/onComplete is gone from the mnemonic widgets entirely. The behavior is now coherent: a completed last word (via auto-fill or chip) dismisses the keyboard, a chip tap on any other field advances focus, and a fully-typed non-last word keeps focus put by design (documented at mnemonic_widget.dart:645-651 — a uniquely-determined word leaves no enabled letters, so it can't be broken).

Passphrase field — fixed

enableSuggestions: false / autocorrect: false are now threaded through LabeledTextInputBBInputText and applied to the passphrase input (mnemonic_widget.dart:216-219), with a widget test asserting both flags (mnemonic_widget_test.dart:509-525). BBInputText already sets enableIMEPersonalizedLearning: false, which rounds this out. (obscureText wasn't added — that was a "consider", and it's a defensible UX call for an optional passphrase.)

Nits

  • Layout assert — fixed. MnemonicKeyboard now asserts layout.length == 26 in its constructor (mnemonic_keyboard.dart:62-66).
  • Localization — intentionally left as-is, and that's fine. mnemonicShuffleKeyboardHint exists only in app_en.arb. On closer inspection my earlier claim that the locale files are "otherwise kept in sync" was wrong — e.g. the pre-existing mnemonicPossibleLastWords exists only in en and fr — so English-fallback for new keys appears to be the repo's normal translation workflow. Withdrawn as a concern.

Still standing (deliberate trade-offs, not bugs)

These are documented by-design decisions that deserve an explicit product ack, not code changes: screen-reader users cannot enter a seed at all (the exclusion is now consistent, which is the point), and hardware-keyboard/paste entry no longer works for seed words.

Verification caveat

Flutter isn't available in this runner, so I couldn't execute the test suite locally; verification is by reading the code and tests. The PR's Analyze and Test CI run is in progress on the latest push — worth confirming it goes green.


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant