Repository navigation
Releases: wudream813/super-termux
Release list
v2.3.6
v2.3.6
用户报「不支持传递图形协议」:img2sixel / kitty +kitten icat / imgcat 在本复用器里什么都不出现。原因是这一层把未知序列丢掉,而图形协议(sixel 的 DCS、kitty 的 APC、iTerm2 的 OSC 1337)正好是「未知」的三种。现在解析线程旁边挂一个小判定认出它们、整段原样收下,在主线程发完本帧差分之后按宿主坐标写出去 —— 实时直通,不进历史(滚动或缩放之后图像消失是可接受的口径)。Windows 侧另加 PSEUDOCONSOLE_PASSTHROUGH_MODE(0x8):不加它,in-box conhost 会自己把 sixel 与 kitty 剔掉(微软那边是 issue #1173,至今没放行),我们转发得再干净也到不了宿主;该位只在 Win11 22H2(build 22621)以上存在,所以「带位试建、失败立刻退回不带位」。
| # | 内容 |
|---|---|
| 1 | src/vt.c:图形序列采集状态机(三种协议、ST/BEL 两种收尾、File= 锚定判定、graphics_max 上限、中途出错整段丢弃)+ 每窗格 16 条的转发队列 |
| 2 | src/render.c:锁内取出队列并按「本帧该窗格的首行/首列」换算宿主坐标,帧尾统一 host_write;不在实时视图(正在回看)就不发 |
| 3 | [general] graphics / graphics_max / conpty_passthrough 三个 ini 键(读得进来、save_config() 也写回去),行为页第 7 个开关直接切 graphics |
| 4 | src/conpty_loader.c:RtlGetVersion 取 build 号 + conpty_passthrough_wanted() / conpty_passthrough_tri_from_text() 两个纯函数(进 make verify-loader 的单测),带位失败自动退回 |
| 5 | 判据:make gfx-posix(采集状态机 33 条定点断言,无外部依赖)+ verify_pane_palette.py T18a-T18f(真 pty 查宿主原始字节流;T18a 拿 v2.3.5 的发布档跑是红的) |
三个坑都是「静默什么都不发生」型的,靠定点单测才当场抓到:① kitty 的载荷态跳到一个 switch 里不存在的状态号(走 default 被清态);② File= 的模式串写成大写开头、与统一小写后的输入永远配不上(原来靠「4KB 内没认到才丢」兜住,改成锚定判定后立刻暴露);③ iTerm2 用 ESC \ 收尾时忘了把 \ 一起收进缓冲 ⇒ 发出去的是没有结尾的 OSC,宿主的解析器会停在半截上、把之后每行文字都当载荷吃掉。
v2.3.5
v2.3.5
拿 v2.3.4 上 Windows 真机试过的回报是「这次没有闪一下了,但是没有历史」+「没有记录终端」。顺着这两句查到:v2.3.4 为了「恢复了但还没显示就退出」在 session_flush_now() 最上面加了一句会无条件拿锁的落地调用,而那支函数正是 Windows CTRL_CLOSE_EVENT 处理器里调的 —— 那一刻 ConPTY 读线程多半正持着 g_mux.cs,处理器就此挂住,系统几秒钟后强杀进程 ⇒ 一个字都没写;于是下一次启动既没有历史可恢复(不闪了),也再攒不出快照。
| # | 内容 |
|---|---|
| 退出路径只限时等锁 | session_pump_pending_all() 从 session_flush_now() 的函数头挪进 if (locked) 里面(持锁调用,sess_apply 重入同一把可重入锁,安全);拿不到锁就照旧用上一次采集的那份落盘 —— 该函数自己的注释早就写着「死等等于把退出路径挂在锁上」,v2.3.4 亲手违反了它。 |
| 落地不再只认一条读取路径 | 挂起块原本只在「读线程收到该窗格第一帧」那一刻落地;Windows 的 ConPTY 起 shell 时连着来好几帧(首帧 + resize 后整屏重打),任何一帧没走那个调用点,快照就烂在挂起态里 = 「没有历史」。现在主循环每轮问一次 session_pump_due():武装着且过了 400ms 宽限期就地补上(与读线程那条同锁语义;两条都在,谁先到算谁)。 |
| 新判据:静态那条才是 catcher | tools/check_exit_path_locks.py(已挂进 make check-posix)抽出 session_flush_now 的函数体,要求 TryEnterCriticalSection 之前不出现无条件 EnterCriticalSection(、session_pump_pending_all() 只能出现在 if (locked) 内、并且这两件事都得还在(不许为了绕开锁把功能整个删掉),另加一条「主循环必须问 session_pump_due()」。拿 v2.3.4 那份 src/session.c 跑它是红的(session_pump_pending_all() 出现在 if (locked) 之外),本树绿。行为侧新增 T17:让 shell 正刷 400 行时发信号 ⇒ 进程须几秒内自退且档里有那些行;POSIX 上信号不跑在处理器里,多半挂不住 ⇒ T17 是护栏,catcher 是上面那条静态判据(这点写在这里,不含糊)。 |
| 顺带修的沙箱坑 | 判据依赖的 tools/vmini 丢了可执行位 ⇒ PermissionError,chmod +x 补回(与产品代码无关,记一句免得下次再查)。 |
v2.3.4
v2.3.4
按 Windows Terminal 的做法把「恢复的历史被首帧盖掉 / 必须滚动才看得到」这两条真正修掉:快照改排在窗格第一帧输出之后落地,视图按绝对行锚定到快照第一行。
| # | 内容 |
|---|---|
| 顺序(治「闪一下就没了」) | Windows 上 shell 起来那一帧,ConPTY 会重绘整个视口;快照若是先落进去的那批行,就被这一记原地盖掉 —— 这是 v2.3.0~2.3.3 反复没修好的那一半(Linux 的 pty 不重绘整屏,所以本地判据量不到)。现在 sess_feed 不再当场上屏,而是把这块文本挂起在该窗格上,等它收到第一帧真输出、并且这一帧已经落屏之后,才接上去(src/pane.c 读路径里的 session_pump_pending)⇒ 快照成了屏上最后被写入的一批行,没有任何东西再盖它。对标 microsoft/terminal:TermControl::_InitializeTerminal 是「有 _restorePath 就先把 buffer 恢复出来,否则才 Connection().Start()」(TermControl.cpp:1402-1408)—— 同一条不变量「恢复与首帧不撞车」;我们没把 create_pane_shell 拆成「建屏」+「起进程」(两平台各约 150 行启动路径,风险不对等),改排顺序拿到同样的效果,且对任意长度的历史都成立。兜底:session_save()/session_flush_now() 采集之前先把挂起块落地(否则「恢复了但还没显示就退出」下一轮会丢),窗格关闭时丢弃它。 |
| 绝对行锚(治「要能滚动才看得到」) | 说明行回到块首,视图锚定到它:Pane.rf_anchor 存的是reflow 显示行的绝对下标,render_split_pane 每帧用当前内容高度换算成 scroll_offset(= WT 的 UserScrollViewport(viewTop):_scrollOffset = realTop - viewTop,Terminal.cpp:1107;新输出到达时 _PreserveUserScrollOffset(:1096)按视口位移补偿 —— 我们原先把 vo 一次算死,shell 每出一行视图就被顶偏一行,说明行正好被推出屏外)。换算出的 vo 恒 ≤ screen_scroll_limit ⇒ 既不用放宽 render 的 vo 夹取,也不碰 screen_reflow_height()「首末内容之外的空白不落位」那条(v1.8.52 靠它避免「历史开头一片空白」);上一轮「垫一排空行凑 vo」就是撞在这两条上,已作废。 |
| 按键即让位 | 摊开的是视图不是模式:按任意键(write_to_pane)或滚动一次,就交还实时底部,键本身照常送进 shell,一个字节都不吞(= WT 的 Terminal::TrySnapOnInput,Terminal.cpp:495,默认开)。switch_pane 在锚定期间不再顺手把 vo 清零 —— 否则 session_restore() 末尾那一下落焦刚设好的视图就被抹掉。 |
| 这一版终于能在本地量到 | 新增 T16c:用 $ENV(dash 交互 shell 的启动脚本)让 shell 在起完提示符之后自己输出「ESC[2J ESC[H + 逐行 ESC[r;1H ESC[2K 重绘 24 行」—— 纯输出侧、不经按键,正是 ConPTY 那一帧的形状。反向对照:拿已发布的 v2.3.3 Linux 档跑,这条是红的(说明行与最早那行一起没了),新版是绿的。T16 同时翻成「说明行在第一屏第 0~1 行 + 最早那行当场可见」,新增 T16d 钉住「按键后让位、键不吞」。 |
| 没做的(写清楚) | ED(2) 语义仍然按 v1.8.52 的口径「只清视口、不动 scrollback」,没有改成 WT 的 _EraseAll(把整页推进 scrollback,adaptDispatch.cpp:735-753)—— 那会牵动「ConPTY 重绘 ⇒ 重复入历史」一整串,单独一轮再说。Windows 侧本轮只做到编译级验证:交叉链是临时从 apt 缓存里把 gcc-mingw-w64-x86-64 + binutils + mingw-w64-*-dev + mingw-w64-common 解到一个前缀目录里凑出来的(沙箱装不了系统包),make all(-O2 -Wall -Wextra)出 termux.exe 正常,make lint-o1 报 Windows 18 + POSIX 18 个文件 0 警告。本轮改动只碰已有的 C 代码路径(没有新 Win32 API),但「闪一下就没了」是否真被治好,最终仍然要你在真机上看一眼。SIGKILL / 崩溃仍然没有快照(退出时写一次)。 |
v2.3.3
v2.3.3
用户回报「运行后闪了一下历史,但马上没了」+「内容过长,能滚动时才看得到那行说明」。这一版把恢复的说明行挪到历史末尾(紧贴提示符),长历史第一屏就能看到它,更早的行照旧归 scrollback。
| # | 内容 |
|---|---|
| 说明行的位置 | v2.3.0~2.3.2 把「── 上次会话的历史(共 N 行…)──」写在恢复内容的最前面:32 行这种长度,光正文就把 24 行的视口填满,说明行被顶出屏幕 ⇒ 用户要能往上滚才看得到它(原话)。现在改成写在最后面:它是恢复出来的最后一行、正对着 shell 的提示符,第一屏必定看得见;屏上同时是最新那几行,更早的在滚动缓冲里(PgUp / 滚轮回看)。判据 T16/T16b 把「第一屏可见 + 老行没被丢」两头钉住。 |
| 没做的那条(写清楚) | 「闪一下就没了」的另外一半在 Windows:ConPTY 在 shell 起来那一帧会重绘整个视口,落在视口里的恢复内容会被原地盖掉(Linux 的 pty 不这么干,所以本仓库这套判据量不到)。真正的解法是把灌历史推迟到窗格第一帧真输出之后(需要主循环里加一个 pending 钩子),或把整块内容垫进 scrollback 再摊开 —— 后者实测要跨过 screen_scroll_limit 的 vo 夹取和 switch_pane 的复位,画面会停在半中间,已撤销。这一半留给带 Windows 现场的一轮,不拿「Linux 绿」冒充「Windows 好使」。 |
v2.3.2
v2.3.2
会话恢复的「看不到效果」这一类补三处:session 默认改成开、快照按最新那份发现(换 exe 文件名不再等于丢历史)、没读回来时在屏上说一行。
| # | 内容 |
|---|---|
| 默认开 | 用户连着两轮说「没有成功恢复」。现场里最常见的一种解释根本不是 bug:session 默认 off,没手动开过就一个字节都不写,而「没开」与「坏了」在屏上一模一样。v2.3.0 当时按「不碰任何人的现有行为」选了默认关,这一版反过来:功能默认生效,ini 里 session = off 照样能关(值写坏也当开,因为那行存在就说明想开)。 |
| 快照发现 | 写仍然只写 exe 同目录的 termux.session 一份(「退出只写一次盘」这条口径没变);读的时候改成拿「exe 目录里所有 *.session + 主目录 .termux.session」比最后修改时间取最新。动机:Release 的产物名带版本号(termux-2.3.1-windows-x64.exe),换版本 = 换文件名 = 快照落在上一个名字旁边,下一次启动按固定名字找不到 ⇒ 看着像「没存」。POSIX 侧用 opendir/stat,Windows 侧用 FindFirstFileW/GetFileAttributesExW,同一套判定。 |
| 说了再走 | 三种「什么都没恢复」以前都是静悄悄的:没有快照、快照读不认(版本不认 / 结构坏 / 空档)、目录写不动。现在前两种各打一行说明(── 会话:…──),并且这类说明行存盘时剔掉,不会被当成历史一轮轮攒下去。判据把「提示行只出现一次、且下一轮不会被当成历史」钉住。 |
| 默认开顺手暴露的 CI 互污 | session 默认开之后,凡是【直接跑仓库那支 exe】的判据都会在 exe 旁边留下/读到 termux.session:make check-posix 里的 lastcol(「满宽行必须原样发到宿主」)就因为多出来的两行把渲染分块位置挪了而红 —— 上一轮跑剩的快照被下一轮灌回屏上。加了个与 TERMUX_DUMP 同族的把手 TERMUX_NO_SESSION=1(src/config.c,只有显式设上才生效,产品路径不动),lastcol / smoke / 非会话那几组跑之前设它;会话那几组用自己的临时目录 + ini,不受影响。termux.session / *.session 同时进 .gitignore(判据跑在仓库根时它真的会出现)。 |
v2.3.1
v2.3.1
会话恢复补两刀:点 X 关窗口也要存盘(v2.3.0 只在退出钩子存,那种走法一次都没写 ⇒ 「没有成功恢复」),以及 快照带上 ANSI 代码(恢复出来是有颜色的终端,不是素色文本)。
| # | 内容 |
|---|---|
| 关窗口 / 注销也存盘 | 用户:「你这也没有成功恢复啊」。查出来的原因不在读、在写:Windows 的 ctrl_handler 只把 running 置 0 就 return TRUE,而系统在处理器返回之后立刻终止进程 —— 主循环还阻塞在 ReadConsoleInput 里,末尾那句 session_save() 永远到不了 ⇒ 点 X 关窗口时一个字节都没写下去,下次启动自然「没有上次会话」。现在存盘就在处理器里做完(session_flush_now(1)),Linux 的 SIGTERM/SIGHUP 也走同一支;for_exit_path 会记下「这次退出已存过」,退出钩子那一次自动跳过 ⇒ 仍然是一次写盘。另外多一个 POSIX 把手:kill -USR1 <pid> = 立刻存一份但不退出,判据靠它端到端验「有界拿锁 + 落盘 + 进程继续跑」这套机制(Windows 没有信号,那边用的就是同一支函数,运行期只有编译级验证)。拿锁只试 200 次 ≈400ms,等不到就用窗格关闭时采过的那份落盘 —— 死等等于把退出挂在别的线程持有的锁上。仍然救不了 SIGKILL 与崩溃:那不是「存盘晚一点」,是根本没机会。 |
| 快照带 ANSI 代码 | 用户:「恢复要保存 ANSI 代码」。只存文本的话恢复出来是素色一片,看着就不算恢复。一格的颜色有两处来源(cells[].Attributes 的 16 色 + 下划线,和平行的 fg_rgb/bg_rgb/rgb_valid 真彩),存的时候按格比这五元组、只在变化处发 SGR。刻意不借 render.c 的 emit_attr16:那条会把 16 色按 [theme] pane_* 换成真彩,焊进档里就换不了主题了 —— 存程序原本发的索引色(默认前景发 39、默认背景发 49),读回来由当前主题重新上色;默认属性一格一个字节都不发,否则满屏 \e[0;37;40m。文件格式仍是一行一条记录 ⇒ ESC 写成 \e、反斜杠写成 \,版本行升到 s 2;s 1 的老档照旧恢复且不做转义还原(否则用户路径里的 C:\e 会被当成 ESC)。单行 8000 字节的上限现在把颜色码也算进去。 |
| 长度口径:估大 == 抄到垃圾 | 新写的第一版 sess_row_text 返回「转义后的估算长度」(每字节 ×2),调用方拿它当实际长度 sbuf_add ⇒ 多出来的那几个字节是缓冲尾巴上的陈旧内容,实测档里存成 REDMARK77\0DMARK77。改成返回实际追加的字节数;判据 T10 用精确等式钉住(红行 = \e[0;31;49mREDSAVE61\e[m,一个多余字节都不许有)。 |
v2.3.0
v2.3.0
加了「退出时保存终端」:[general] session = on 之后,退出那一刻把每个窗格的文本历史和标签/分屏布局写进 termux.session,下次启动读回来并标一行「上次会话的历史」;顺手把行为页上被无谓挡掉的 A/B/K/W/M 换页键还回来。
| # | 内容 |
|---|---|
| 退出存盘 / 启动恢复 | 用户:「关掉终端,里面打过的东西就全没了,能不能后台运行、保存终端」。口径是先做存盘恢复(入口 = 设置 → 行为页第 6 个开关 + ini session,默认 off;真正的常驻后台进程另开版本,本仓库现在一个 socket / 命名管道都没有)。存的东西:每个标签页的锚点色 + 分屏树(v50pp 这种前缀式,数字是左/上侧占比)+ 每个窗格的逻辑行文本 + 当时聚焦在哪一格。三个不显然的做法:① 采集点不在退出钩子,而在 pane_mark_dead() / close_pane() 的开头 —— 窗格的 ScreenBuffer 就在 close_pane 里被 screen_free() 掉,等主循环退出来到退出钩子时历史早没了(第一版就是这样存出一个空文件),所以改成「窗格要被拆掉的那一刻先在内存里留一份,落盘仍只在退出时一次」;② 存逻辑行而不是屏幕物理行,读回时由引擎按新宽度重折 ⇒ 100 列存的历史在 40 列终端里读回来不会腰斩;③ 文本从 cells[] 取时要跳过宽字符的次格(BMP 宽字符次格是 0、emoji 是低代理),当成空格就会把「汉字」存成「汉 字」,中文历史每轮胀一倍。上限:每窗格 4000 行、单行 8000 字节、整档 6 MB,屏底的空行不进档,上一轮那句说明行也不会被当成历史再存一遍。恢复失败一律当「没有快照」:坏档 / 版本不对 / 树语法错都只影响这一个标签页,绝不让启动挂掉。 |
| 行为页的字母挡位 | v2.2.0 把 A/B/K/W/M 列进行为页的「本页已占用」名单,好让换页字母让路 —— 数错了:行为页只认 ESC/↑↓←→/Space/Enter,那五个字母在这页什么都不做,结果白挡掉一半换页键(A 换不到外观页)。名单删到只剩条目管理页真正占用的 S/A,并加判据钉住「行为页上按 a 必须换到外观页」。 |
v2.2.0
v2.2.0
启动页补上快捷键(F1 与 S,顺手修掉 [A] 那个假标签);条目管理页中间那段空缺填掉、动作条与提示行钉在列表底下;「预设方案」点名字改成打开列表;拖动分隔条改成即时重排;[+] 新建条目 失色修好。
| # | 内容 |
|---|---|
| 启动页有了快捷键 | 用户:「启动页连个快捷键都没有,[A] 外观 / 主题 还是假的」。三件事叠在一起:① 侧栏六行里只有五格写了记号,启动那行是 启动 (Startup),一个键都没绑;② [A] 那行写着记号却从没绑过字母(只有 F2 能用)⇒ 画着能按、按了没反应;③ M/A/K/B/W 这几个字母只在启动页认 ⇒ 进了别的页就按不回来,只能一路 ←/→ 或 Esc 退出去重进。现在 g_settings_nav_letters[] 把六个字母在所有设置页统一认(F1..F5 照旧,任何一页都能按),记号位与侧栏文字共用 sb_label[] 一份表 ⇒「画在一处、点在另一处」这一类从根上没有;字母被当前页自己用掉时让路(条目管理页的 S 仍是「设为启动默认」、A 仍是添加,行为页的 A/B/K/W/M 仍是单键开关,那两页换页请用 F1..F5),判定用 !is_ctrl && !is_alt,不动命中判定的输入时序。 |
| 条目管理页那 4 行空缺 | 用户:「条目管理中间有一段很大的空缺」。根因是这一页的行号在渲染、命中、滚动条、页脚读数四处各抄了一份常数(表头 / 首条 / 动作条 / 提示行都是写死的数字),首条从第 10 行提到第 6 行之后没人再对齐全套 ⇒ 说明行与表头之间空 4 行。现在 SETTINGS_MANAGE_HEAD=5 / ITEM0=HEAD+1 / BAR(n)=ITEM0+n+1 / HINT=BAR+1 / TAIL=2 一份宏,四处都从它推(settings_manage_row_view、settings_manage_natural_at、settings_page_band_raw、settings_page_mark),末条行号由条目数现算(settings_manage_last()),不再拿 26 硬夹。 |
| 动作条与提示行钉底 | 省行数是对的,但 [+] 新建条目 / [P] 预设库 / [设为默认] 与提示行原本算在可滚内容里 ⇒ 条目一多它们就滚不出屏(要点新建得先把列表滚到底,而滚到底又被「聚焦行必须可见」的夹取挡在中间)。现在这两行钉在可见带底下(SETTINGS_MANAGE_TAIL):列表短的时刻在末条下面一行,列表长的时刻在倒数第二、最后一行,一行的条目地方都不占。顺带把页脚那截 (3-9/12) 的行窗读数收进同一支 settings_page_vis() —— 以前只有滚动条那边减了 2 行、读数没减,报出来的带比画面宽两行(照着它去点第 10、11 行,点到的是按钮和提示,不是条目)。 |
| 「预设方案」点名字 | 用户:「点 GitHub Light 也可以打开预览」。以前名字那一段是「直接换到下一个方案」,而 ‹ › 两格才是上一个 / 下一个 —— 结果想挑个方案只能一遍遍点、看不见全集。现在点名字 = 打开方案列表浮层并把光标停在当前那条上(所见即所朝),Enter 应用;只有 ‹ › 两格仍算上一个 / 下一个。两格的列位由 settings_pane_scheme_arrow_col() 给(main_left+11 / +28),渲染与命中同出一处,漂了就红。 |
| 拖动分隔条即时重排 | 用户:「拖动分隔条时改成即时修改渲染」。v2.1 早期为了「一次拖动把 scrollback 从 93 行吃成 0」把两侧一起冻结到松手才 resize,代价是拖动途中画面纹丝不动。现在改成拖动中就重排,但每 90ms 最多提交一次(SPLIT_DRAG_LIVE_MS),松手时再无条件补最后一次 ⇒ 快速一划不会连着发几十次 ResizePseudoConsole / TIOCSWINSZ,慢速拖动能实时看到结果;历史不被吃这条仍由 hist_lines 与 scroll_offset 的原式子守。按用户要求不加 ini 开关。实测:12×100 分屏后按住分隔线往左拖 6 格,未松手右窗格 stty size 已经从 50 列变到 57 列,松手后仍是 57(不反弹);改前的二进制同样三个读数是 50 → 50 → 57。 |
[+] 新建条目 失色 |
用户:「没有前景色、背景色、也没有 hover 色」。不是没写样式:动作条那一行的样式段正好落在窗口左边界那一格上,hcut() 的 used > from 条件把「从第 0 列起笔的第一段 SGR」整段丢掉 ⇒ 后面所有文字都拿默认色画([P] 预设库、[设为默认] 跟着一起失色,hover 换底也看不见)。现在 hcut() 把「起点之前已经生效」的那截样式带到裁切结果里,实测未 hover 是 48;2;033;038;045 底 + 38;2;063;185;080 字,划过去底色换成 63;185;080。两处颜色都是已登记的主题字面量,没有新增用色。 |
| 回归判据 | tests/verify_pane_palette.py 新增 R20–R24 共 13 条(R20a 四条按键路径、R20b S 不被抢走、R20c 侧栏 [S] + 六名同列、R21 两档终端高度、R22 按钮上色与 hover、R23a/b/c、R24 拖动即时重排),全量 176 条全绿(4 条 SKIP 是 libvterm 相关的老组)。拿去跑 v2.1.9 的二进制(对照用的那份是从 de58160 现编的,与 Release 那版源码同源;tag 上多的 218d2f6 只动了判据没动 C)⇒ 恰好 12 条红:R20a×4、R20c、R21×2、R22、R23b、R24,加上新排版打红的既有两条(J1 文案、Q7 行窗);R20b / R23a / R23c 在两版都绿 —— 它们是「别改过头」那一侧的护栏(S 仍归本页、箭头列位与命中同源、箭头仍是换方案)。四条被新排版打红的既有判据是逐条量过屏幕才改的,不靠放宽不等号变绿:M3 详情页原先传的 ini=ini3 其实是 E3 用例剩下的「ini 文件内容」串(里面根本没有 [menu] 段 ⇒ 条目数 0),「能不能滚到第 3 个字段」只是碰巧成立,现在自造三条菜单的 fixture 并改判「基线可见标题 + 前三个字段而按钮在带外 / 滚五下标题出屏、按钮进来」;Q3 的「轨道下端」随可见带从第 11 行挪到第 9 行(点第 10、11 行现在点的是按钮与提示);Q7 改成量「▶[9] 停在可见带末行、它下面紧接的一行就是动作条」;J1 认新文案子串。R24 量的不是画面而是子 pty 的宽度(pane_resize_to 里 TIOCSWINSZ 与本地 screen_resize 同帧 ⇒ 窗格里 stty size 报的列数就是「模型有没有跟上」),拖动途中每一格用一次捕获边界隔开(间隔远大于 90ms 节流)。没装 libvterm-dev 的机器一律打 SKIP 而不是崩:TERMUX_NO_VTERM=1 本机复跑 50 ok / 55 SKIP / 0 FAIL。 |
v2.1.9
v2.1.9
淡入「只碰浮层自己那几格」;气泡换行重播一次;设置页左栏选中行常态带底色、划过的行也亮,名字一律同列。
| # | 内容 |
|---|---|
| 气泡不再挡旁边 | 用户:「气泡淡入把旁边不该挡的地方挡住了」。v2.1.8 的淡入是按行取增益的(anim_gain(row, …),作用域 anim_row_active 也只比行号),于是气泡框那一行的整行都被混向页面底色:框左边的侧栏正文、框右边的 [↑][↓][改][删] 尾巴一起变淡,看起来就像被框盖住。现在浮层登记自己的列窗口(气泡记 left … left+bw-1,toast 记 tcol … tcol+box_w-1),anim_fade 解析行定位时把列号也读出来,窗口外的 chunk 一律增益 1000(一格不动)。 |
| 换行要重播一次淡入 | 用户:「悬停提示没有淡入」。上一版为了「不一路闪」刻意让指针扫过多行不重播,结果是只有第一次出现看得见动画 —— 已经停在一行上再挪过去就一帧都不淡。现在状态键里带上气泡锚定的那一行(bit 12..16):从无到有、或换到别的行,都重起一段 110ms 淡入;同一行里鼠标再动不改键 ⇒ 不会一个事件闪一次。 |
| 侧栏选中行平时就有底色 | 用户:「设置面板左边要 hover 有颜色」。v2.1.6 曾把侧栏的 hover 底色整条撤掉(「不要一条一个颜色、不要 hover 背景」),做完之后侧栏连一点鼠标反馈都没有。现在按现在的要求改回来:当前页那一行常态用 TH_ACCENT→TH_BG1 45%(022;062;128,右表选中行同款),鼠标划过的行用 TH_BG1(022;027;034),两者同时命中时保留常态色(不让 hover 把「你在哪一页」冲掉)。底色铺满整行到分隔线前一格,分隔线那一列不碰。字色仍是「六个入口同色」—— v2.1.6 这一半保留。两处底色都是已登记的主题字面量,没有新增用色 ⇒ 改 [theme] 的人会跟着变。 |
| 「▶ 启动」的名字与别条同列 | 侧栏选中别的页时写成 " ▶" + label+2,▶ 直接和 [A] 挤在一格后面;而启动行拿 1 格的 %s 去顶别人 3 格的 [X] ⇒ 名字比其余条目靠左两格。现在六个入口一律「两格缩进 + 四格记号位 + 名字」,名字都从第 7 列起。 |
| 回归判据 | R17(淡入不得碰气泡列范围外的格子)、R13c(toast 同一条款,按横条实际宽度量列窗口)、R12c 判据反转成「换到别的行必须重播」、R18(一次触发一段:一次 / 同行再动 / 换行 三种鼠标序列的「混合帧数」必须分别是 一段 / 仍一段 / 两段)、R19a–e(六个入口名字同列;选中行不 hover 也有底色且铺到分隔线前一格;其余入口平时仍不涂底;划过的行才亮底、且不冲掉选中行底色、分隔线那列仍是 │;hover 的列范围与命中范围一致)。列窗口与宽度全部从字节流量出来(┌───┐ 的起止、行尾复位序列之前的可见宽度),不照抄 C 里的公式;宽度一律按显示列量 —— CJK 占两格,拿字符下标当列号会差一格(本机就是这么错过一次)。O 组「侧栏那一段只许是侧栏自己的串」原先是手写白名单 + startswith,文案一改就整组假红 ⇒ 改成从 src/render.c 的 sb_label[] 现抽现比,并把「被 sidebar_clip 裁过的每个前缀」都算合法(窄终端必然要裁),外来串照旧拦得住。整套判据拿去跑 v2.1.8 的二进制 ⇒ 恰好且只有 R12c / R13c / R17 / R18 / R19a / R19b / R19d / R19e 八条红,与本轮四件事一一对应(R17 越界 chunk 42 个、R13c 越界 7 个、R18 三种序列都是 21 帧、R19a 实测 [5, 7] 两列不齐);R19c「其余入口平时不涂底」在两版上都绿 —— 它是防止改过头的那一条。两条判据自身的毛病也一并记在这儿:① 新增的静态判据在 rA_ck 之外直接索引了屏幕行数据(side0[...]),没装 libvterm 的机器上那份数据是空的 ⇒ CI 的 macOS runner 上 KeyError 把整个作业带崩(本该打 SKIP),现在屏幕量取不到就走 ok_side 空值分支、五条 R19 一律 [SKIP],TERMUX_NO_VTERM=1 在本机复跑确认;② rA_span_cols 的切片起点带进了一个从 main() 漏出来的循环变量,本机不报错却把 toast 横条量成 35 列(真实 27)—— 修完才是第 37~63 列 ⇒ 「判据打出的数必须和另一侧几何对得上」,不然绿也没意义。 |
v2.1.8
v2.1.8
动画补上两处「看不见」的地方:切换标签页整页左右滑入,悬停气泡与底部提示条淡入;淡入的基准从「向黑压暗」改成「向页面底色混合」。
| # | 内容 |
|---|---|
| 切标签 = 整页横滑 | 用户:「切换标签也要有一个左右滑动的动画」。原先标签切换是瞬间换掉分屏树的根(split.h:切标签页只是换 root),0 帧过渡。现在按「新标签在旧标签的左边还是右边」定方向:往后的标签从右推进来、往前的从左进。做法是在交给帧差之前重写这一帧的正文:每条行定位(ESC<行>;<列>H)的列号加上本帧位移,落到屏外的格子直接不发 ⇒ 行宽永远不超过屏宽(不会折行溢出),命中的矩形、输入时序一个字节都没动。只有空格的铺底/清行 chunk 故意不参与位移,所以页面没推进来的那一截是干净的底色,不会露出上一页的残留。位移与亮度共用同一条缓动曲线、同一个 15ms 分片 ⇒ 收尾同帧,不会出现「亮到位了还在滑」。 |
| 淡入改成「向底色混合」 | 用户:「气泡的淡入完全没有看出来」。查下来是两件事叠一起:① 悬停气泡(render_settings_tooltip)与底部 toast 原先一帧动画都没有(状态键刻意排除 hover,它们按设计就是瞬现瞬隐),② 旧的淡入是「向黑压暗」,小浮层是实心底色画在页面上,压暗 25% 仍然是个完整的框。现在 c = 底色 + (c - 底色) × 进度:浮层/气泡第一帧几乎与页面同色,是真的「浮出来」。增益 1000 时该式是整数恒等 ⇒ 静止那一帧逐字节不变;只认 3 位十进制分量的写法,没有新增字面量颜色,g_theme_refs 不用登记。 |
| 气泡 / toast 只淡它自己那几行 | 状态键加两位「本帧画没画气泡 / toast」,于是从无到有的那一帧起一段 110ms 淡入;指针在行与行之间扫过去不重播(键不变 ⇒ 不会一路闪),消失仍是即时(淡出在终端里只会读成整屏闪一下)。动画期间只重写气泡/toast 占的那几行的颜色数字,其余行一格不动。 |
| 动画触发点上移到帧尾 | v2.1.7 的起动画挂在 render_settings_panel 出口,切标签时整帧可能根本没有面板 ⇒ 整段判断移到 render_screen 帧尾(theme_remap 之后,混合基准才是主题色)。分屏时也不会再出现「一个窗格的分支把状态冲掉 ⇒ 动画自我重启、屏幕永远留暗色」。[general] anim = off 时这些代码一帧都不执行。 |
| 回归判据 | tests/verify_pane_palette.py 的 R 组扩到 26 条判据(24×100 主档 + 12×100 / 24×40 两档,连多档一起跑出 28 行结果):滑动不数像素,而是拿「整屏文本套一条闭式位移公式」找唯一解(方向与列数一起出来);淡入用「off 与 normal 两次捕获的逐行颜色差集合」;「标签栏只跟着亮、不跟着滑」用「按行定位切段之后数可见字节」;再加一条「动画帧的字节流里不许出现 NUL」—— 动画会改写长度,长度与内容不吻合时缓冲尾巴会被当成光标段发给终端(画面看着正常,因为终端把 NUL 吞了,只有数字节才抓得住)。整套判据拿去跑 v2.1.7 的二进制 ⇒ 恰好且只有 R8 / R8b / R10b / R11 / R12 / R12b / R12c / R13 八条红;拿去跑本轮修 NUL 之前的那一版 ⇒ 只有 NUL 那条红。判据一律不读 C 的实现细节,也不放宽判据掩盖回归。没装 libvterm-dev 的机器(CI 的 macOS runner)上,要拿真实帧回放的屏幕级几条打 SKIP 而不是拿空帧假红、也不是拿 None == None 假绿;字节级的两条(CUP 列号、NUL)照常硬判 —— (TERMUX_NO_VTERM=1 在本机复现过一遍:SKIP 与硬判的分界正如预期)。anim 的毫秒数那条换了两次量法才站得住:① 原来数「最后 12 帧里出现过几种颜色」,慢机器上三档一起撞在窗口上限(CI macOS 实测 7 / 10 / 10);② 换成「一次翻页 + 整场窗口数压色帧数」,又多出一个假倒挂 —— 时长越长,越多收尾帧「淡到与真色四舍五入相同」就不计数了(macOS 实测 4 / 7 / 6,200ms 反而比 110ms 少);③ 定稿是分两头量:60 与 110 用压色帧数(两台机器都是 4 < 7),110 与 200 用整场帧数(本机 2 < 6 < 9 ≤ 15,只要求「不少于一秒里一直在重绘」)。两头都写进判据名里,日志上能看出哪一环是按什么量的 |