feat(contacts): revamp edit dialog with live preview + avatar picker

Rebuild the add/edit contact dialog on the portfolio-editor design language:

- A live preview card at the top shows the contact exactly as it renders in the
  list (avatar + name + address), updating as you type and pick an avatar.
- An avatar picker (Badge / Icon / Image segmented control) lets you keep the
  default Z/T type badge, choose a Material wallet icon from a searchable grid,
  or set a custom image. The picker area is fixed-height so the modal doesn't
  jump when switching modes.
- Custom images go through a new in-app ImagePicker (image_picker.h): a
  Material overlay that browses the filesystem starting at the user's Pictures
  folder, shows a thumbnail grid (decoded to raw pixels, box-downscaled to a
  small texture, cached per directory and freed on navigate/close, budgeted a
  few decodes per frame so large folders don't hitch), and returns the chosen
  path. The chosen image is copied into <config>/contact-avatars/ (named by an
  FNV hash of the source path, so re-picking is idempotent) and stored as
  "img:<path>". Like FolderPicker, it takes over the modal surface while open.
- Paste is now available on both add and edit; buttons are content-sized.

Adds three sweep surfaces (contacts-edit-{icon,badge,image}) that open the
dialog on a seeded contact in each avatar mode, plus i18n keys (+ 8-language
translations; reworded two zh/ja strings to stay within the existing CJK
subset, so no font rebuild).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-15 07:10:00 -05:00
parent 2249bc31d5
commit 2d4ba89c00
13 changed files with 809 additions and 25 deletions

View File

@@ -287,9 +287,20 @@
"console_welcome": "ObsidianDragonコンソールへようこそ",
"console_zoom_in": "拡大",
"console_zoom_out": "縮小",
"contact_avatar": "アバター",
"contact_avatar_badge": "バッジ",
"contact_avatar_badge_hint": "アドレス種別バッジを使用します — Z はシールド、T は透明。",
"contact_avatar_choose": "画像を選択…",
"contact_avatar_copy_failed": "その画像をコピーできませんでした。",
"contact_avatar_icon": "アイコン",
"contact_avatar_image": "画像",
"contact_avatar_image_hint": "画像はアプリ内にコピーされ、元のファイルが移動しても利用できます。",
"contact_avatar_remove": "削除",
"contact_global": "すべてのウォレットで表示(グローバル連絡先)",
"contact_global_badge_tt": "グローバル連絡先 — すべてのウォレットで表示",
"contact_global_tt": "オン:この連絡先はどのウォレットを読み込んでも表示されます。オフ:現在のウォレットにのみ属します。",
"contact_preview_addr": "ここにアドレスが表示されます",
"contact_preview_name": "連絡先名",
"contacts": "連絡先",
"contacts_search_no_match": "一致する連絡先がありません",
"contacts_search_placeholder": "連絡先を検索...",
@@ -465,6 +476,12 @@
"hide_qr": "QRを非表示",
"hide_zero_balances": "残高0を非表示",
"history": "履歴",
"img_picker_count": "このフォルダの画像:%d",
"img_picker_empty": "このフォルダにはサブフォルダも画像もありません。",
"img_picker_none": "このフォルダに画像はありません",
"img_picker_pictures": "画像フォルダ",
"img_picker_title": "画像を選択",
"img_picker_use": "この画像を使用",
"immature_type": "未成熟",
"import": "インポート",
"import_key_address": "アドレス:",