From c23b21eed8469af3ec2d762f3430ccbc20c1f8d8 Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Sun, 25 Oct 2026 18:02:32 +0800 Subject: [PATCH] =?UTF-8?q?docs(UI=E8=87=AA=E5=8A=A8=E5=8C=96):=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AC=AC=E4=B8=83=E8=8A=82=E3=80=8C=E6=9D=BF?= =?UTF-8?q?=E7=AB=AF=E6=9C=89=E7=8A=B6=E6=80=81=E5=AF=BC=E8=88=AA=E5=88=A4?= =?UTF-8?q?=E6=8D=AE=E3=80=8D+=20=E8=BF=87=E5=AF=86=E7=A0=81=E5=8F=A3?= =?UTF-8?q?=E5=BE=84=E6=8C=87=E9=92=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 票 = task-25 第二半(配套上一笔的 scripts/board_ui_nav.py)。 docs/UI设计/UI_自动化测试流程.md 新增第七节: - 7.1 为什么必须闭环:KEY 空帧 + 盲走 9 帧仅 3 帧名副其实的实测表(含每帧落点与墨量), 三张证据 PNG 落 docs/测试说明/boot_log_data/(弹框裁切现场、盲走落密码页、盲走停在级联)。 「512 bits 神秘空白态」在表里定名为**密码输入页**——走带 #P 注记的叶子时第一屏是要密码。 - 7.2 认屏三判据(表格:判据 + 为什么这样判)与两条实测纪律(l 收列、#P 注记顺序)。 - 7.3 四种用法与钩子档位:钩子只跑快检档,完整档(含闭环)合入前 / 上板前手工跑;「没跑」 与「跑过且通过」在脚本输出里是两种打印,不互相顶替。 - 7.4 四条已知限度:末两行叶子弹框被裁(真机定位留台架窗口取证,是取证题不是编码题)、 同标题多态不区分、与黄金文案的有意耦合、快检不证导航。 - 7.5 过密码口径:查看 000 / 修改 123,键序与黄金回归编辑态完全一致;只读判据不自动升格。 docs/UI设计/UI_数据供给设计说明.md 追加落地注·五(指针级):写路径(批 5)必须把「过密码」 当显式状态——快照层不提供「曾否进过某页」,可写性只由 UI 权限态与 par.locked 决定。 纯文档与取证图,无行为改动。取证 PNG 由 simulator/i1_to_png.py 从留档帧转出(4 倍放大,与 其余 UI 证据图同规格);本笔钩子按各自触发面执行,其中 doc-links 会核对本节新增的两处 相对链接与三张图的存在性。 --- docs/UI设计/UI_数据供给设计说明.md | 8 ++ docs/UI设计/UI_自动化测试流程.md | 85 ++++++++++++++++++ .../20261025_uinav_blindwalk_cascade.png | Bin 0 -> 4643 bytes .../20261025_uinav_blindwalk_pwd512.png | Bin 0 -> 1031 bytes .../20261025_uinav_popup_clipped.png | Bin 0 -> 1544 bytes 5 files changed, 93 insertions(+) create mode 100644 docs/测试说明/boot_log_data/20261025_uinav_blindwalk_cascade.png create mode 100644 docs/测试说明/boot_log_data/20261025_uinav_blindwalk_pwd512.png create mode 100644 docs/测试说明/boot_log_data/20261025_uinav_popup_clipped.png diff --git a/docs/UI设计/UI_数据供给设计说明.md b/docs/UI设计/UI_数据供给设计说明.md index 2488cad6..d9e0d382 100644 --- a/docs/UI设计/UI_数据供给设计说明.md +++ b/docs/UI设计/UI_数据供给设计说明.md @@ -526,6 +526,14 @@ typedef struct { > 「合位/分位」这条词映射当前**没有任何自动化证据覆盖**(像素恒 IV、页面依赖 LVGL 也进不了 > 主机单测),它已登记为开入落地后的台架判据,而不是假装测过。 +> 【落地注·五(写路径判据的前置口径,指针级,不重述细节)】带 `#P` 注记的叶子**第一屏是密码 +> 页、不是内容页**:真机实测里三帧「深页盲走」的落点都是密码输入页(墨量 512,与黄金参考 +> 的两张密码态逐字对得上)。所以批 5 的写路径判据必须把「过密码」当**显式状态**处理——快照 +> 层不提供「曾否进过某页」这种隐式状态,可写性只由 UI 侧权限态与 `par.locked` 共同决定, +> 「当前是否已过密码」既不真实也不该被推断。判据侧的过法只有一套(查看密码 = 出厂 000 / +> 修改密码 = 123,键序与黄金回归的网口设置编辑态完全一致),口径与纪律见 +> `docs/UI设计/UI_自动化测试流程.md` §7.5;本节只登记这条事实,避免两份文档各说各话。 + --- ## 12. 验收判据 diff --git a/docs/UI设计/UI_自动化测试流程.md b/docs/UI设计/UI_自动化测试流程.md index ff876961..9d837cab 100644 --- a/docs/UI设计/UI_自动化测试流程.md +++ b/docs/UI设计/UI_自动化测试流程.md @@ -102,6 +102,88 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警 - `菜单页面设计.md` §9:内容页对象树 / 几何表 / 内存预算(本文数字的来源) - `simulator/README.md` §3.5/§3.6:无头模式(`--keys`/`--screenshot`)与回归脚本说明 +## 七、板端有状态导航判据(真机侧,`scripts/board_ui_nav.py`) + +> 前五节都在模拟器里跑;模拟器与固件同一份 UI 源码,像素判据在 PC 上就成立。本节管另外 +> 一半:**真机上把页面走到、并证明走对了**——这一步只有真机需要(模拟器侧把键序直接喂给 +> 无头进程,状态是确定的)。 + +### 7.1 为什么必须闭环(实测 2026-10-25 大窗) + +- RemoDispBus 的 **KEY 应答是空帧、不携带任何状态**:发了 5 个键不等于到了第 5 层。 +- 靠预置键序盲走,9 个「应当到达」的态里只有 3 个真是那个态: + +| 留档帧 | 文件名声称 | 实际是 | 墨量 | 证据 | +|---|---|---|---|---| +| 00 / 01 / 02 | 根菜单 / 装置信息 / 交流量 | 名副其实 | 3226 / 3476 / 3346 | 认屏一致 | +| 03_dc | 直流量 | 菜单级联(整屏无页标题) | 4961 | [blindwalk_cascade](../测试说明/boot_log_data/20261025_uinav_blindwalk_cascade.png) | +| 04_yx / 05_yxp2 / 06_soe | 遥信量两页 / SOE记录 | **密码输入页** | 512 / 515 / 512 | [blindwalk_pwd512](../测试说明/boot_log_data/20261025_uinav_blindwalk_pwd512.png) | +| 07_vparam | 参数查看页 | 网口设置页 | 3774 | 标题带命中「网口设置」 | +| 08_vset | 定值查看页 | 装置信息页 | 3474 | 标题带命中「装置信息」 | + + 那三帧的 512 墨量曾被记成「神秘空白态」;与黄金参考 `shot_pwd.bin`、`shot_pwe.bin`(墨量 + 同为 512)逐字对得上之后可以定性:**走带 `#P` 注记的叶子时,第一屏是要密码,不是内容页** + ——写路径判据(批 5)因此必须把「过密码」当显式状态处理,口径见 §7.5。 +- 结论:判据脚本只能信画面。脚本因此**每按一键重新认屏**再决定下一个键,且「到达」只由 + **页面身份**裁定;中途任何假设都写进轨迹,绝不当成结论。 + +### 7.2 认屏的三条判据(精确字形匹配,复用同一份 `ui_font_cn_12`) + +| 态 | 判据 | 为什么这样判 | +|---|---|---| +| 内容页 | 标题带命中 + 该页黄金行文本至少一条 + 页码行「第 N 页,共 M 页」 | 只认标题会把两屏认成一屏(「参数」既是菜单项也是页名);页码让「遥信量第 2 页 / 共 5 页」成为可比对的事实 | +| 菜单 | 在白底横条(反白选中行)覆盖的行范围内、**整幅宽度**找反白标签;长标签优先,命中框被包住者丢 | 选中行是黑字白底,只按正常极性永远读不出选中项;「参数定值」那行必然同时落进子串「参数」「定值」,不去重就凭空多两列 | +| 密码页 | 「用户密码」四个字里中三个 | 整串匹配实测差约 1 px 打不中;数字格随输入变,不参与判定 | + +另两条实测纪律(理由写死在代码注释里,此处不重述): + +- **收级联列按 `l`、不按 `s`**:菜单态按 `s` 不收列,用 `ssssssss` 回根正是深页错位的来源之一; +- 菜单树的 `#P` 注记可能写在 `#page=` **之前**(`网口设置 #P #page=net_cfg`),解析顺序错了就 + 剥不掉,那一叶的页面 id 也永远认不出来。 +### 7.3 用法与门禁档位 + +```powershell +# 认屏快检:18 张黄金参考(+ 若有台架留档帧),秒级,不需要构建产物 +& '.venv\Scripts\python.exe' scripts/board_ui_nav.py --selftest-quick +# 完整自检:再加全绑定页闭环导航(需模拟器可执行文件,约 30 s) +& '.venv\Scripts\python.exe' scripts/board_ui_nav.py --selftest +# 台架窗口:一条命令走完全部绑定页并逐页存帧(须持 bench 锁) +& '.venv\Scripts\python.exe' scripts/board_ui_nav.py --walk +# 只走一页并抓图;--trace 落步迹、--dump-fail 落「认错的那一屏」 +& '.venv\Scripts\python.exe' scripts/board_ui_nav.py --board --to yx --grab D:/tmp/yx +``` + +- 提交钩子 `uinav-selftest` 跑的是**快检档**(`--selftest-quick`)。为什么不挂完整档:闭环 + 那一段要模拟器可执行文件——它是构建产物,新工作树里没有,正是同一天 `mem-numbers` 缺 + `.map` 那课的形状。脚本对「缺可执行文件」报 FAIL 并给可执行提示,不「跳过当通过」; + 合入前 / 上板前请手工跑一次完整档(快检 ≠ 闭环,两者结论不同,别互相顶替)。 +- 板端后端只连 7003 取图注键,**不含串口、烧录、复位**;跑真机必须自己持 `bench` 锁。 +- 台架留档帧目录在 gitignore 内、不入库:缺就整段跳过,不算失败——但脚本把「没跑」与 + 「跑过且通过」分开打印,不把前者写成后者。 + +### 7.4 已知限度(别把它当万能闸) + +1. **根列选中落在最后两行时,子菜单弹框会被屏幕裁掉**:选带不在可扫窗口里,列数会漏计。 + 脚本对策是「枚举 + 验终点」——末列先盲按 N 次下移再进(第 N 次尝试),成败仍只由页面 + 身份裁定,所以它会大声报错而不是静默走错页。实测 `自检记录` / `运行记录` 两页在模拟器 + 上 5 次枚举内没吃到,现场帧见 + [popup_clipped](../测试说明/boot_log_data/20261025_uinav_popup_clipped.png); + **真机上那层弹框究竟画在哪,留台架窗口取证后再定修法**——这是取证题,不是编码题。 +2. 同一标题的多个显示态(网口设置的 只读 / 编辑 / 保存)在分类里都是同一个页面 id + `net_cfg`;要区分得看行内文本,写路径判据需要时再加。 +3. 认屏复用黄金参考的文案:改了页面文案而没同步 `simulator/tests/` 的文本断言表,本节脚本 + 会一起红。那是**有意的耦合**(两处必须同时改),不是脚本脆弱。 +4. 快检档只证「认屏认得对」,不证「导航走得对」;后者只有完整档的闭环那一段会响。 + +### 7.5 过密码的口径(与黄金回归同源,只有一套走法) + +查看密码 = 出厂 `000`(直接 `e` 确认);修改密码 = `123`,键序 `u,r,u,r,u,r,e`,与 +`simulator/tests/check_content_pages.py` 的 `netcfg_edit` 态**完全一致**——刻意不另发明一套走法 +(两套走法迟早分叉,而分叉的那一边往往是真机)。导航器**默认只过查看密码**:判据需要写 +路径权限时必须显式传 `modify=True`,**不因「能进去」就自动升格成可写**——只读判据偷偷拿到 +修改权限,等于把判据的边界让给了实现。 + +--- ## 变更记录 | 日期 | 变更 | @@ -114,4 +196,7 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警 | 2026-09-13 | 管线同步:`UI_界面制作流程.md` 移除 HTML 样机阶段(五阶段 → 四阶段 P0~P4),本文定位改为"P2/P3 门禁的可执行版本";§六 关系描述更新为 P0 规格 → P1 编码 → P2 本文回归 → P3 上机目检 → P4 归档;移除 `tools/ui_mock/` 引用 | | 2026-10-11 | **裁剪 LVGL 死代码(F-1/F-2/F-3)时的门禁记录**:先用**未裁剪**配置 `--update` 生成 11 页黄金参考,再切到裁剪配置重编重跑 → **逐字节全部一致**(菜单 8 状态像素一致),证明裁剪零视觉影响。池水位门禁随之 5440/5520 → **5368/5448**(−72 B;一次性残留仍 80 B,故非泄漏变化,见 §三 水位历史)。顺带揪出并修掉 `Lib/lv_conf.h` 与 `simulator/lv_conf.h` 的**长期漂移**(模拟器早已 I1-only、MCU 端未裁)→ 规则见 `UI_设计架构.md` §1.4 第 10 条,量化见 `../内存管理/RAM使用分析与优化方案.md` §2.6.4 | | 2026-10-15 | **回归改为自洽运行(修 `netcfg_save` 假失败)**:`check_menu_rows.py` / `check_content_pages.py` 一律以**空临时目录当工作目录**跑 `lvgl_sim`(模拟器按 CWD 读写 `sim_config.ini`,残留档会改变页面内容);`simulator/main.c` 的 `--cfgtest` 加外壳,**无论成败一律删除伪造的 4000 B 配置档**——旧行为连 PASS 路径也会留雷,之后每次 UI 回归的保存行都从 `OK` 变 `ERR`(引擎拒写超长文档,属正确行为)。脚本里"删 `sim_netcfg.bin` 残留"的旧兼容清理随之作废(旧档迁移 2026-10-10 已删),改为**只提示不删除**。修后 11 页 + 往返池水位 5,448 B 全过,**未刷任何黄金参考**;排查口径见 §五 | +| 2026-10-25 | 新增第七节「板端有状态导航判据」(`scripts/board_ui_nav.py`):真机侧闭环导航、 + 盲走 9 帧仅 3 帧名副其实的实测表、认屏三判据、`uinav-selftest` 钩子档位、四条已知限度与 + 过密码口径;三张证据 PNG 落 `boot_log_data/` | | 2026-10-22 | **`ref/*.bin` 像素黄金参考改为入库**(撤销 2026-09-10 的"整目录不入库"):`.gitignore` 由忽略整目录收窄为只忽略 `ref/*.png`。动因是这条门禁不可移植——新克隆/新工作树里基准必然缺失,`ui-golden` 报「缺黄金参考」而不是回归结论;本仓实测两次同类踩坑(并行工作树里第一次跑必失败,只能现场 `--update` 造基准,而"现场造的基准"恰好失去意义)。入库前做了可复现性验证:主树(`ref/` 生成于 2026-10-11)与新工作树各自独立构建 `lvgl_sim`,11 个 `.bin` SHA-256 全部一致。§四 与"何时不该更新"随之改写:`--update` = 改基准,会进 `git diff`,须逐页过目并在提交正文说明 | diff --git a/docs/测试说明/boot_log_data/20261025_uinav_blindwalk_cascade.png b/docs/测试说明/boot_log_data/20261025_uinav_blindwalk_cascade.png new file mode 100644 index 0000000000000000000000000000000000000000..fb54e0d3a14c2ffe9592402f4d8b24eeab68ed9a GIT binary patch literal 4643 zcmY*ceO!{~{??px%~jj_l||FqYO@_nSBB2_R!wS|&P+{Bv^m9iLM&1e5Z|=vBee5@c z+5M*X#XVHtSo*9?`aluCz3ROS8|^os4-QQ@KdZ|%#*XiH-u)lDPruCDwXZN+zMUe6 z$ip`r7uJ2cz3N7ff7Uu$s76|U#D4Meg|#=Y{~qqzb-s8={hf8)<|3SYJ0V8S!SpZs z^sBsGi`PY-`N1!19h~;=x+wU_6X#5iv1Tui;)jbC_FI^@w^2aAzDz{?dChUd53jZi z$B))J`Yr!r^Sa1Imc$mlsj!GJcvm9*?OX$;IM+CB=wV3fP=rhasePqGG{4{j6;CBW@czqGc+Ak+k8`J6iur!eb(%YD2@e4g~m}J zF@KHpQrr+|VmhxOBy3-#UY+ayrhregCJtuu!??P+26#o_?2tP;mj|`7vGc^(^mkHP zPZ`+8GPMn{LgBKrOk+BTvX7O8a}@6eI+Y#$Y7YX!?t+{>YlJ z=21=?qIX~;O)*voj{?9#@5&?3!i*XfM^}FL)v+zwec%K#Q^Ag8@27B0|Ht6IxWs09 zqM@VA$|u7n$%;2Dd+%@T{M$ob>HPeEgMMBfYH*!}W%s-}dlRNqk8mxkwsPSWRbSJ> zq$JOaO+xoZbCcF!(p;RmHwXw-8lhp6I@C%;W6k?72Psfos`#MbcW)VAaD}C)b>DtD?YEdKdgtR0Ek(vsE55}@Xl!(bOjs*;Lp2d{qA$c)`Sn>7>%8h zb?Xs^XQXq6&uV4b5Sk2bVRDEF8IBOCpFi}_crlSp-yN0&@&u;QYrUZy09Zim}Cf!ObJ@?SN=SUd=^-Y9k; zlhGJAHf@oGWVDMlzto0%OGH<%0wrfcEM(01ABQ5{FY_1k0)3wk{t36vkx%5@uU6dY z9r(qA)kj)7eNnG^4tvDbEJc|s)*GaWIg`FN3?htq9=QZId!$=OK1U|Tvv_^1!!ZU} z4;&yhm<7pT7F`!gEd5WX_;zclt)SDy4voGUtuO|W7{+^BLq*NHzSa(iRoBB8C2;g- z;h=hm?^|W9p9-|!eUL>Qy#xB+VtIA#rd3S3CXndGiUx|8*<@5O9)~pP;^?hv_qTba zw6RKPB3iYkPLfXPoD95F;mu#A4Mxn9fUw2}*>qPolkCw+_O^k#ZKa5c?Up_5zC8lT zj1=BrP?QT-#1u#c!+ceAZZ`Rsj(7nz??T^X@CJJ&GB`!?skTHDHgvNSiVuCwNnn9wL!u~1Z z!bN0xOsU6lZ3+2b)~Qtbl35qma#?`ft4=)n0nmQm`GW;{4{$IoD9r6D*2`8PK^f=JaV!*^+Vn#s!`M3 z`eSL86pzZvfUUA;yEhwLY+vHqoVFm4aI2wHRm{@HXxIDQk;N+>OGIOZO#@?azTh-X zm<~dhY^Uy->y~-9LDdJSToW1dU8FBSP7R(ndM^&8i!B!aI^X{e#98{B`xu~rC=gsC zrROgeYS(3m@z?`+P`g;5l2RQKQY0(`LxeqXN+Dq>G^T_NwzMjl#uH=A9I$CEh~rsi z9Et#tKVbQnBwN4~R7ktzB5GMd-QI%+PsXZJq>aH;V;(~B+l;8MnON<&4jkpx$$b55 zprmi9t5I}L{2Cr9q?&k{90fAqaGyFGk>MjNvY;@sxI1_aLY8=*-skN-9vHRLwU$BX zY)}mnGme=hqcVv%Mg10r^5u`H27+d!a27$JtGw!5@g1n736G;1bs;NJIe7nsQfF%? zo>KQ~zRRu#((CEHy&A?rMd|~~_avP+)r~p`nhAVQ)5;LFG@(gIv(?`tJneUU(nD|0 zXynO7m%L{WQ9cVPso77gpYDy{#;}DZ#T)muFK=LN=@^7xF5pErHgm8LWNBX_EH*Xh zek^@OI`W&O?_aw*#uA(p!Y~uVBO*A%=^6X5lXF!6AD48+e)8IsRXV-QMkxNk7l3YY zK+UAnrpjeuy~}MZ)Y~`kb9_`wUpUM}zj6ig5Mv%YH<+MkJ%3)X^fT__BTCeE0`wfbT$WT zaS(;`SJ|zt`+*E}1kES#X?X502#zk2TW-Uy1h+%WC=AhQJC)E1(BDGqjzZObXS(X} zBZBd4ZaB|okt1+BLPLbT!M<}R8TGOZ1$!t5B|JXwtdW`;k>(MB-?*#w(=rehlFSWT zvW1xu=4Vd1UOx$4V`_RDgrsFO90-i6D(`$%W$gp#S1m)_8tskC)oGp_>@LZ-@R zx2O&oSao%?Ro1~0n6rcj8xRk^d)nxH% zG}o;!Z;l6l4&H7nH2#wq!>1{~e0fCkk-~;df7u0N;0R`o@LyxiTUffipdMt!iZ2dD z3@% z7ZZ}NM=)5NIPgJWONPev#rWdN`K5G`#|;}LN`PHzGe)E23X)l)L`D!jR>IWe#HeyR z<8L*%WX9nvqfjkNE{;JYNrS`HbzjWNV|B6vZwmU#e=xiXmZxnUGl;biFs5RajZI$lpQjO($(6`V6OhJVO3ffAZg)NGoR zeY@A{o3nuXh0TXU{m6|1mv2i-PeIIomjfVfEXdImHxGpI9hTXbD&@12zoSt2N-5ZWzt{6Fp^XRjMfKdM)k>M>nJ0BEl zYbJa74l}C>z>Xr0P(D5gzlu;N+Ag!T_MC^sq=yj7JxSb2wH}5=(vYl`NB0d3s62UM zlXQ<2x*#aeo0!`hEcJykC)r54OHgwtHh*=n&D&c9!UzuVG4vk9Vv71;Ue%^QB?>&~ zm?VzypDj;VbNWqHMS;ssUh`R_1R?`SsfopXMpMc9EQ0zJvE!KYD=lj729LBK#lVSS z04QV6T#*DHXX+Ta6l^TL;jtl{Lhp%6$=#lTc-3yH3I~edglB z*l|ZbZ#L25mzcd3#Lgkc4WFf_u3@LT=i5E3G8h-W Q3uAk}58p-Kk^Ia506pUl`~Uy| literal 0 HcmV?d00001 diff --git a/docs/测试说明/boot_log_data/20261025_uinav_blindwalk_pwd512.png b/docs/测试说明/boot_log_data/20261025_uinav_blindwalk_pwd512.png new file mode 100644 index 0000000000000000000000000000000000000000..5e5b50ad9255169e0b24b25dcda0841a202177cf GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0y~yU}^wi4xj*o=CW!385o$)dAc};RLpsM=Vsm^0}+;h z<*)w#H>!1VndmGQ(^hc4@}*LNvehoBn=RKsn&=1`jF)|3jXAhhwtC?yQX&h5+Q?$rPB;`#11d+iI(Z~bzv{rj4) zygnfR05z^UWB1_2<_Bww?`-||?ae)b^n)MLm_RNKUB)c;@!a{Ab#?PMz?}ZF!SU+v z_-r+WM6i;tUu8esoca3JbBFmaQ`7CLmzC?qJmvWTb1gY>W|83)f1rNH+?sp0x<9^r z9edON7t;z~u;1%0ix-Hx|Gpk}UV87nP`LkA@>#r(RkpfX(<@kAviSOLCZzCK{_RD? z;$OE+e*e3CZ8sN^#PfHx*&)|=tvAhjpZBF|{ux;Kf`bGS)C8Gc_k@i;)lIl|?Ee11 zr$E}r;SeN5eb{C0u6Fx=;^OPrR#?aZBYkb}`-sKgU#)uc_bklukOTotH^7)*S$iP7 p=brPLig&kR$%&i*91uOue;A{CBHkA!_a}ht_jL7hS?83{1OQFHJTU+O literal 0 HcmV?d00001 diff --git a/docs/测试说明/boot_log_data/20261025_uinav_popup_clipped.png b/docs/测试说明/boot_log_data/20261025_uinav_popup_clipped.png new file mode 100644 index 0000000000000000000000000000000000000000..00bda64e1ea1684328c9f058058d485032fb8d27 GIT binary patch literal 1544 zcmeAS@N?(olHy`uVBq!ia0y~yU}^wi4xj*o=CW!385mg4d%8G=RLpsMH@1I~0RwBm z-8cXLpME!4`tpTKOhvkLK0oO>ar}&LF|&v~KS&o4ta~=|USrGk@!KmWJ(7DT%y~gI(`Mp!upT2kfRipcx^LKYGgh^(V z9VnOIdHHYV{OiTpweKVM#MfeXtN+y892T*FtXg%GG5c@2TlBNUJ-csCtJ}71ukHKu%SzW>;qaKkAf(#h zvUVPO&G!SVzfPKU%k)9fz4!C>AHB5o$yQ7C%~89<@>oD_6>?a#YaMI+LiTU-&abf* z-mrasZ_e^JwZ{Kzr+>Mloe5X-ob~)Y=IwTq^OwxlJu9qvfAwFRU2EgbjQo$mEaSD6 zPWvXe<9d|4^i1(PQOD1GZgPL_Da{D7f@Q)AU(@7meV2-Bx0Uf$l=S6i&)#ZzF#Gka zqZ$9JZF(A7fME^{*L8e5Dp_N^ZkD?nCBm#W3*WKY#<=Zq4(DW8@Z74E@)u9>?|{Y! zI59wC7DPH+{(rox;uv>&75n+gf*la+7ry6yxAEKGEbr6XUo5+x1~WX&I^q21`uL-- z&&%ET)pzI9*J-)N=E@+q07G!=Eyn5lxXa$&iCO(v_p4Rx`+e{P@bG%m_7$aGcR&1s z3oVrWuzB;>JIAkI{&?^74*d_aU@{?_nSn{=t=#^l)eO8e#6*9)x{;^Ym z^wu5x&iYvxl&)A1K{>)%J2s@hK70KTllOk>OW6rG_x=u)J8jE za@Qka$>YjO(3Q(QK@TjGcxM6?9T1$w!Sld&#tq5Z_4|^ zvnOEDYij-AR?P(KGO@l?Sc+~t&#YDzfBxsrwcnqr?o_|P;V}bJ7M$a@`yKUi`&8L& zUq8P&Zp92s<)&Y9W-OgA-+%vw*Za!Twx>T@!(0{;t{PQk=JWI3i7L06yx$jp{Fzt! zKJxgz-18N@zdc}%%-UK7EI2+yUvJO8`(nkpklXJ!?_+-&ecV(UW<%&&8SlAE-ZY;T y&AGDso^5RI+s*f1G^Wd3?}6Hc$iN5m{xgVeyq+(fbU_f5_&i