Commit Graph
148 Commits
Author SHA1 Message Date
Bill 7bccd5303a feat(workspace): 模式按钮双击收展侧栏
CI / typecheck + test + build (windows) (push) Waiting to run
图标栏的「终端工作区 / SSH 工作区」按钮新增双击手势:双击当前工作区
按钮展开/收起侧栏;单击(含双击另一个按钮)只切换工作区,侧栏不动。

判定逻辑抽成零 import 纯模块 modeRailGesture.ts,用浏览器原生
MouseEvent.detail 连击计数识别手势——detail=2 到达时 store 已被
第一击更新,故在 detail=1 时把「手势前模式」存入 ref 再比对。

新增 tests/mode-rail-gesture.mjs 表驱动测试(10 例),测试数 35→36。
2026-10-20 23:59:28 +08:00
Bill eac4704e9e docs(website): 更新站点至 v1.0.30
CI / typecheck + test + build (windows) (push) Waiting to run
Deploy website to GitHub Pages / deploy (push) Waiting to run
- hero 下载按钮/版本号、spec 版本号、下载表文件名与直链改为 1.0.30
- 更新日志新增 1.0.30、1.0.29 两节,按规则裁到最近 5 条
- i18n.js 四语言块补齐新键并移除被裁版本(1.0.24 / 1.0.25)
- README 保留列表同步为实际内容
2026-10-20 15:12:56 +08:00
Bill be243fd111 chore(release): v1.0.30 版本号与 CHANGELOG 四语同步
CI / typecheck + test + build (windows) (push) Canceled after 0s
v1.0.30
2026-10-18 10:17:36 +08:00
Bill fc0d1afdce feat(highlight): 主进程 ReDoS 校验闸门与 renderer 性能时间线 2026-10-18 10:09:07 +08:00
Bill 68fa46b2e3 fix(highlight): ReDoS 检测降级为编辑器提示(实测漏判,不可作拒绝闸门)
上一提交把 isReDoSProne 当作 compileRules 的拒绝闸门,声称"9/9 危险全抓"。
独立复查证伪了这个结论:

## 实测漏判 —— 危险规则仍会进热路径

| pattern | 静态检测 | 实测 |
|---|---|---|
| [(](a+)+$      | 放行 | 30 字符 4065ms |
| (?:a\|a)+$     | 放行 | 26 字符 7547ms |
| ((a\|a))+$     | 放行 | 24 字符 16212ms |
| (\w\|\d)+$     | 放行 | 26 字符 14451ms |

根因三条:外层扫描不跟踪字符类状态([(] 里的 ( 被当组起始)、遇到不闭合
组时 return false 让整个函数交白卷、分支比较只做字面相等抓不到语义重叠。

## 实测误判 —— 合法规则被拒

(?:a{2,3})+ 等把 {n,m} 范围重复当成 {n} 固定重复。

尝试修复后每修一处引入新问题(补字符类状态后又误杀 (?:\d{2})+ 与 ipv4
预设),证明手写正则语法扫描有本质缺陷。

## 三个替代方案均否决(有实测依据)

1. 运行时熔断(循环内查时钟):**原理上无效**。单次 exec 阻塞 28426ms,
   时钟检查根本执行不到
2. re2:不支持 lookbehind,perm/okstate/http 三条预设依赖它
3. Worker + 超时:**可靠**(6/6 危险全抓、806ms 内 tick 51 次主线程未阻塞)
   但往返 0.11ms/块 vs 直接 0.035ms,且终端高亮必须同步(WriteBatcher 消费
   同步染色结果)——只适合规则的异步校验,不适合热路径

## 本次改动

- compileRules 不再因 isReDoSProne 跳过规则:拒绝闸门会静默禁用合法规则
  (已证实会误杀)却仍放进危险规则(已证实会漏判),两头都不对
- isReDoSProne 保留给编辑器提示:漏判不比改动前差,误判只是多一个警告而非
  禁用功能
- 注释与 AGENTS.md 如实标注"只有提示没有根治",并记录三个方案的否决理由,
  避免后来者重复踩坑

热路径的实际边界仍是 MAX_LINE_LEN(>4KB 行不进用户正则)+ MAX_PER_RULE。
真正的解法(Worker 异步校验 + 存盘前把关)列为未完成项。
2026-10-17 23:19:19 +08:00
Bill c6aca15134 fix(highlight): ReDoS 改用静态结构分析根治;fit 加耗时埋点
## ReDoS:从「限制损害」改为「阻止发生」

原防线(MAX_PATTERN_LEN 限幅 + 编辑器实跑探测)都拦不住真正的灾难性回溯:
`(a+)+$` 配 30 个字符实测 8.2 秒,而 MAX_LINE_LEN=4KB 限的是长度不是回溯成本。

两个候选方案都被实测否决:
- re2:不支持 lookbehind,会废掉 perm/okstate/http 三条依赖它的预设——
  治了病废了功能
- Worker + 超时:往返开销 0.11ms/块(直接执行 0.035ms,4.1x),且终端高亮
  必须同步(WriteBatcher 消费同步染色结果),异步化会打乱 HighlightStream
  的 carry/flush 语义

采用静态结构分析 isReDoSProne():编译期扫描两种可证危险的形状——嵌套量词
((a+)+ / (.*)* / ([a-z]+)*)与同分支重复((a|a)*),命中即拒绝该规则,
不进编译集,永远到不了热路径。开销是纯字符串遍历。

关键细节:
- 有界量词不算危险:(?:\d{1,3}\.){3}(ipv4 预设)外层 {3} 固定重复,不会
  指数回溯。只对 *、+、{n,} 这类开放式重复判定。曾因把 {n} 也算进去误杀 ipv4
- 断言组((?= (?<= (?<!)不是重复单元,跳过

实测 9/9 危险全抓、6/6 安全无误判、22 条出厂预设全部通过。编辑器改用同一
函数,警告在用户输入时出现而不是等预览卡住。

## fit 耗时埋点

stall 日志(10-14 至 10-17 每天 23-67 次,平均 27.5 秒,34/35 是渲染线程)
经查证是 v1.0.29 之前就存在的既有问题,与本轮改动无关。高亮引擎已排除
(512KB 仅 0.3ms)。

真正候选是 fit 的全量 reflow(代价正比 scrollback 行数),但通用 stall 看门狗
只会说「渲染层卡了」,不可行动。故给 scheduleFit 加耗时记录:超 200ms 写诊断
日志,含 cols/rows/scrollback。正常路径只多一次 performance.now()。

不盲改 reflow 逻辑——先有数据再优化。
2026-10-17 22:43:49 +08:00
Bill cee616a23a feat: 字体与高亮系统改进(CJK 对齐、ReDoS 防护、链接规则、崩溃类设置修复)
CI / typecheck + test + build (windows) (push) Canceled after 0s
基于两轮专项调研(字体/高亮各两路,子代理实测探针交叉印证)实施的完整改进。

## 修复的真实 bug

**设置越界导致窗格永久死亡(用户报告)**
行高设 0.95 → xterm 构造/热更抛错 → 错误边界卸载整个 dockview 窗格;坏值已
落盘,每次重启都崩。根因是一整类缺陷:UI 允许值 / 配置能写值 与 xterm 硬约束
不一致,且主进程不校验。同类还有 scrollback<0(抛错)、cursorStyle 非法值
(更隐蔽:构造期被吞掉、只有热更期抛错,导致「重试」按钮永远无效、窗格死循环)。

修复:FONT_LIMITS/CURSOR_STYLES/RENDERER_MODES 收拢到 shared 层单一来源,
主进程加载时钳制 + 白名单化,healClampedFontSettings() 回写自愈;设置页
InputNumber、字体热键共用同一常量(照 LOCK_AUTO_DELAYS 先例,杜绝再次漂移)。

**perm 预设锚定失效**
^[-dlbcps]... 的 ^ 锚定的是「转义序列之间的 text run 起点」而非行首,ls -l
多行输出只有第一行权限位上色;带颜色的 ls(run 以转义切开)完全不触发。
改为 (?<=^|\s)[-dlbcps][rwxstST-]{9}(?![rwxstST-])——用负向前瞻而不是
(?=\s|$) 是因为后者会让带 ACL `+` / macOS `@` 后缀的真实 ls -l 输出不再染色。
旧 pattern 登记进 LEGACY_BUILTIN_PATTERNS,存量安装下次启动自动升级。

**高亮 span 吃掉应用自身 SGR**
wrap 固定追加 \x1b[0m,把应用设的颜色一并清掉。改为 SgrState 跟踪并重放。
四条护栏(每条都踩过坑,详见 AGENTS.md):
- reset 判定解析参数:ESC[00m/ESC[0;0m/ESC[0;32m(Go/Java 日志库常写)原先
  被追加进累积器,逐行增长直到撞上限,之后所有 span 恢复永久失效
- 回放闸门按序列条数而非字节数:同一个"单个前景色"长度差近 4 倍,按字节卡
  会砍掉真彩色却放过索引色
- 恢复串只插在可见间隙和最后一个 span 后:每 span 都重放会让 Kimi 这类全彩
  TUI(~19 span/行)把 9.5KB 输出膨胀到 40.8KB(4.30x),渲染线程实测卡
  18.4 秒。现为 3.05x 且长间隙着色正常
- 基准测试守门(膨胀 < 3.2x),改参数必须重跑

**CSI 私有参数导致整行失去高亮**
ESCAPE_RE 漏了 <>= 前缀与中间字节,\x1b[>4;1m(modifyOtherKeys)、
\x1b[<0;1;2M(SGR 鼠标)、\x1b[=1;1$y(DECRQM) 让整行成含裸 ESC 的文本,该行
之后所有关键词失去高亮(vim/tmux 常发)。

**超长行拖垮整段高亮**:一条 >4KB 的行让整个 text run 失去高亮。改为逐行降级。

**统计「最后写入者赢」**:模块级 totals 被各 pane 覆盖,设置页数据在窗格间
跳动。改为按 pane 实例 UUID 聚合(不能用 sessionId——SSH 分屏同一 session 挂
两个 pane,正好复制该 bug)。

## 新增能力

- **CJK 对齐三件套**:@xterm/addon-unicode11@0.10.0-beta.301(peer 恰好接受
  锁定的 6.1.0-beta.304,升 xterm 必须同步重配)、rescaleOverlappingGlyphs、
  cjkFontFamily 回退槽位。默认字体栈没有任何 CJK 字体,中文靠 Chromium 回退
  到比例字体导致列错位
- **字体名引号包裹**:quoteFontStack() 逐段判定(含反斜杠转义——只转义 " 会
  让尾部反斜杠把闭合引号转义掉,CSS 字符串不终止)
- **等宽徽标**:canvas measureText 逐字符采样,等宽排前
- **规则按行匹配**(lineAnchor → m flag)
- **命中提醒**(notify + onMatch 事件出口,60s 冷却,只显示规则名不显示原文)
- **可点击链接规则**(WezTerm hyperlink_rules 式,安全闸门在生成 span 时就
  丢弃非 http/https 目标)

## 其它

- 主题语义变量改为单一命名体系:semanticVars() 键就是最终 CSS 属性名,字面量
  键联合让拼错成为编译错误;删除 cssVarName()/semanticCssVars() 中间层
  (自定义属性名大小写敏感,转换被绕过会静默回落到深色兜底值)
- 删除死代码:automaticFontFallback 与顶层 overviewRuler 在 6.1.0-beta.304
  中均不存在,且移除了让任何伪造键都能通过 tsc 的 as 断言
- ReDoS 防护(MAX_PATTERN_LEN + 编辑器 backtracksBadly 实时探测)——注意这
  只是止血,(a+)+$ 配 30 字符实测仍卡 8.2 秒,根治需把正则执行挪进 Worker

测试 29 → 32(新增 font-stack、link-rules),全部通过;AGENTS.md 已同步。
2026-10-17 20:23:56 +08:00
Bill 619e4ac57f chore(release): package-lock 根版本对齐 1.0.29
Deploy website to GitHub Pages / deploy (push) Canceled after 0s
CI / typecheck + test + build (windows) (push) Canceled after 0s
v1.0.29
2026-10-16 23:57:42 +08:00
Bill 2aeea53da1 chore(release): v1.0.29 版本号与 CHANGELOG 四语同步 2026-10-16 23:44:53 +08:00
Bill 81748e4470 fix(theme): 浅色主题标签强调色改为按 WCAG 加深(实测 4.0:1 -> 4.9:1)
浅色分支此前固定 mix(accent, #000, 0.35),默认绿 #3fb950 得 #297834:在强调色
染过的激活标签底(≈#dae8df)上只有 4.0:1,低于 AA,用户自定义的浅色强调色还会
更差。改用 darkenToRatio(accent, chromeBg, 5.5)——对 chrome 底色保证 5.5:1,
落到更深的染色底上仍有 4.9:1。深色分支(mix 白 0.18)不动。

chrome.ts 新增 chromeBg 记录当前 chrome 底色(applyTabAccent 可能在
applyChromeTheme 之前被设置变更单独调用);#181818 是 pre-JS 深色默认值,
登记进 css-theme-vars 白名单(与 global.css 的 --chrome-bg 同值)。
2026-10-16 23:44:48 +08:00
Bill 3b86d62111 fix(theme): --ot-* 变量名 camelCase/kebab-case 不匹配导致浅色分支静默失效
CSS 自定义属性名大小写敏感,而 semanticVars() 的键是 camelCase、样式表读的是
kebab-case:chrome.ts 直接写 `--ot-${key}` 时,9 个多词变量(--ot-text-muted、
--ot-text-secondary、--ot-surface-raised、--ot-border-strong、--ot-success-fg、
--ot-danger-fg、--ot-warning-fg、--ot-info-fg、--ot-scrollbar-active、
--ot-focus-ring)从未真正生效,元素一律回落到 global.css :root 的深色兜底值——
浅色主题正是靠这些变量修可读性的,等于白改(--ot-text-muted 48 处引用仍是
#8a8a8a,白底 3.25:1;--ot-danger-fg 仍是 #f85149,3.15:1)。

- 键 `muted` 改名 `textMuted`(CSS 读的是 --ot-text-muted)
- 新增 semantic.ts 的 cssVarName()/semanticCssVars(),chrome.ts 只调用它们,
  命名逻辑单一来源
- 新增 tests/ot-var-names.mjs:把「CSS 引用名 = JS 写入名 = :root 声明名」
  三集合锁在一起(值测试看不出这类问题),已用「去掉转换」验证会失败
- AGENTS/STATE 同步该铁律;测试数 22/25 的陈旧计数修正为实际的 29
2026-10-16 23:29:03 +08:00
Bill 9e424138b5 Merge remote-tracking branch 'origin/Dev_20261001' 2026-10-16 23:10:29 +08:00
Bill 5bc91fd498 test(theme): 颜色字面量 fail-closed 守门(css-theme-vars)+ 基线清零;AGENTS/STATE 同步语义层与浅色主题铁律 2026-10-16 20:34:22 +08:00
Bill beaa6f1f04 refactor(theme): SFTP/监控/设置/高亮/锁屏面板颜色语义变量化;MonitorPanel 图表色随主题明暗派生(canvas 需实色) 2026-10-16 20:29:53 +08:00
Bill 83384b7233 refactor(theme): 工作区/连接侧栏/命令面板颜色语义变量化(徽章文本按 -fg 分流) 2026-10-16 20:27:09 +08:00
Bill 606ee0fa02 refactor(theme): global/terminal 硬编码颜色收敛进 --ot-* 语义变量(含滚动条/阴影/浮层,铁律例外保留) 2026-10-16 20:25:35 +08:00
Bill 79bea68c13 fix(theme): 浅色主题 ANSI 调色板重做到 WCAG AA + MCR/bold 动态开关 + antd 算法随亮度切换;github-light 底色柔化 #f6f8fa 2026-10-16 20:23:33 +08:00
Bill 2136221f2c feat(theme): 语义变量层基座 --ot-*(深色锁现值/浅色按 WCAG 派生)+ 表测试 2026-10-16 20:21:40 +08:00
Bill 0a7bdda2d3 docs(theme): 主题系统 A+B+C 改造设计文档与实施计划(含对抗性审查记录) 2026-10-16 20:20:04 +08:00
Bill 2b658520a4 fix(updater): 禁用注定失败的差量下载,修复更新下载两次;官网同步 v1.0.28(补 1.0.26/1.0.27)
CI / typecheck + test + build (windows) (push) Canceled after 0s
v1.0.28
2026-10-16 00:53:00 +08:00
Bill cff5454346 feat(workspace): 标签关闭二次确认(终端/SSH 双开关 + 键盘 Backspace/Delete 误关拦截),v1.0.27
CI / typecheck + test + build (windows) (push) Canceled after 0s
v1.0.27
2026-10-15 10:35:20 +08:00
Bill bca7033689 fix(terminal): 缩放/高频输出冻结修复(reflow 节流 + 写入批量化),新增诊断日志(7 天滚动 + 开关),v1.0.26
CI / typecheck + test + build (windows) (push) Canceled after 0s
v1.0.26
2026-10-11 09:52:39 +08:00
Bill fd4f1a3956 docs(roadmap): 补录通道重传缺口(exe/blockmap 409 无删除重试路径)
CI / typecheck + test + build (windows) (push) Canceled after 0s
2026-10-10 17:02:35 +08:00
Bill 2027fa3310 fix(updater,i18n): 对抗性审查修复(1中 + 3低)
CI / typecheck + test + build (windows) (push) Canceled after 0s
Deploy website to GitHub Pages / deploy (push) Canceled after 0s
- [中] 手动检查不再丢弃「已下载」事实:runCheck('manual') 原来无条件清
  抑制记录,导致下载完成后点「检查更新」会把关于页从「重启安装」退回
  「下载」(包已在磁盘)。改为保留 downloaded 记录,只清 announcement
  记账;测试 §19 补 manual-check 保持 downloaded 的口径(153 断言)
- website 首帧脚本:?lang= 的 decodeURIComponent 包 try/catch(畸形转义
  会在 IIFE 内抛 URIError,跳过 html[lang] 与防闪标记,指定语言被静默
  忽略);zh-Hant-TW/zh-Hant-HK/zh-Hant 按 BCP-47 语义判为繁体
  (normalize 与 i18n-apply.js 双处同步)
- ZmodemOffers 是 App 兄弟节点,语言切换时父级 children bailout 不重渲染:
  订阅 language 并以 tFor 显式翻译(事件回调内的 t() 保留)
- ROADMAP 补录对抗性审查推迟项(周期检查可被设置写入推迟、启动开关
  即时生效语义、下载无整体超时、--channel-only/--skip-gitea 矛盾)
v1.0.25
2026-10-10 16:51:10 +08:00
Bill 7c2044fdf4 fix(i18n): 崩溃兜底页不再停留旧语言
CI / typecheck + test + build (windows) (push) Canceled after 0s
Deploy website to GitHub Pages / deploy (push) Canceled after 0s
ErrorBoundary / PanelErrorBoundary 是 class 组件(拥有错误生命周期),
不能用 hook 订阅 settings store;根边界还是 App 的父级,语言切换时
App 的重渲染传导不到它——崩溃兜底页因此停留在启动时的语言,恰好是
用户最需要读得懂的一屏。

修法:导出改为一层薄函数包装(订阅 language 传 prop),class 内部经
tFor(language, …) 翻译。错误边界行为不变(边界仍是 class、仍在
children 之上)。使用方(main.tsx / TerminalPanel)无需改动。

ROADMAP 已知待办补录两项 i18n 质量专项:zh-TW 用语统一(命令/指令、
口令/密碼 等)、ja 片假名长音符号统一(フォルダ/フォルダー)。
2026-10-10 15:35:56 +08:00
Bill b344af4691 chore(release): v1.0.25
CI / typecheck + test + build (windows) (push) Canceled after 0s
2026-10-10 15:21:05 +08:00
Bill b89f9c6c95 website: v1.0.25 — 四语言站点 + 更新下载与日志
- 单页 + 语言层:assets/i18n.js(147 键 × zh-CN/zh-TW/en/ja)+
  i18n-apply.js(data-i18n / data-i18n-html / data-i18n-attr 三种挂键方式,
  首帧前定语言,?lang= → localStorage → navigator → zh-CN)
- 导航栏新增语言切换;html[lang]、title、meta description / og:* 同步
- 版本号/下载直链 → v1.0.25;近期更新补 1.0.25 / 1.0.24,裁到 5 条
- style.css 仅追加(语言选择器样式 + 首帧防闪),既有规则零改动
- README 补语言层说明与发版清单
2026-10-10 15:18:50 +08:00
Bill 7a1b11dad7 fix(i18n): 日文布局菜单的分割标签仍是中文
ja 的 workspace.layout.splitRight/splitBelow 值为「水平分割/垂直分割」
(中文句式),改为日语 UI 惯用的「左右に分割/上下に分割」。zh-TW 的
「水平分割/垂直分割」是通顺繁体且与 zh-TW 全文件用词(分割)一致,保留。
2026-10-10 15:18:49 +08:00
Bill 402bf368fc fix(i18n): 语言切换后 dockview/memo 组件不再停留旧语言
dockview 渲染的组件(面板内容/标签/header actions)不在 App→Workspace 的
重渲染路径上,React.memo 且 prop 恒定的组件也被挡住——它们的渲染期 t()
读模块级 current,不重渲染就停留旧值。日文界面下「+」按钮 tooltip、
SFTP 表头、搜索条等因此停留在中文。

修法沿用仓库既有惯例:组件自己订阅 settings.system.language,渲染文案改
tFor(language, …)(显式语言,不依赖模块级状态)。共 8 文件 145 处:

- TerminalTab / SshHostBadge / TerminalPanel / SshBottomPanel /
  TabActions(+按钮)/ BroadcastToggleButton / BroadcastPopover
- FilePanel(memo,60处) / PermissionModal / 传输面板行 / MonitorPanel
- TerminalView:搜索条、右键菜单、工具栏 tooltip、粘贴确认、结束遮罩;
  搜索命中提示改为「存标签、渲染期翻译」(消除 effect 闭包过期风险)

刻意不改:事件回调动作时构造的字符串(t() 在调用时刻已是新语言);
不给 DockviewReact 加 key={language}(会重挂载杀掉全部会话);
retitleAutoTitles 的存储标题机制保持不变。
2026-10-10 15:18:48 +08:00
Bill c94e3358ea test(updater): 覆盖调度、自动下载、同版本抑制与气泡
updater-fallback 从 82 扩到 150 断言(新增 11 节):周期节拍与关闭、重排、
启动检查只武装一次、并发检查共用同一 promise、自动下载开关、同版本去重、
换版本恢复、手动检查不被抑制、已下载状态保持(含不卡 checking 与
fetchHandled/downloaded 区分)、气泡的窗口可见性守卫。electron-stub 的
Tray 补 displayBalloon 记录、BrowserWindow.getAllWindows 可注入假窗口。

AGENTS.md 更新机制一节补录三条来源、注入式调度、自动下载、同版本抑制与
气泡守卫。
2026-10-10 13:36:55 +08:00
Bill bf79036599 feat(settings): 更新开关的界面与四语言文案
- 系统设置页加「定时检查更新」下拉(选项由 UPDATE_CHECK_INTERVALS 白名单
  生成,标签用 Record<UpdateCheckInterval, string> 保证漏项即编译错误)与
  「自动下载更新」开关
- 关于页:后台自动下载完成时用 statusDownloadedAuto 文案,与用户手动下载
  区分
- 四语言补齐 13 个键(含托盘气泡文案),已用 i18n parity 校验 617 键对齐
2026-10-10 13:36:54 +08:00
Bill 9cc856fc8f feat(updater): 定时检查更新与后台自动下载
设置 → 系统新增两个开关:定时检查更新(从不/4/12/24 小时,默认 4)与
自动下载更新(默认关)。安装时机仍交给用户——绝不因下载完成自动退出,
autoInstallOnAppQuit 只覆盖用户自己退出时装上。

- runCheck(source) 统一三条来源(启动/周期/手动),UpdateState.scheduled
  标记来源;已有在途检查返回同一 promise,避免 electron-updater 的去重把
  来源标记记到错的调用方
- 周期调度器经 settingsStore.setUpdateScheduleApplier 注入(updater 已
  import settingsStore,反向 import 成环);幂等重排,启动检查由
  startupArmed 保证每进程只武装一次;所有 timer 带 unref
- 自动下载由 settings.autoDownloadUpdate 驱动,autoUpdater.autoDownload
  保持 false(单一事实源在 settings);失败不重试,等下一周期
- 同版本抑制(actedRelease 单槽,actedFor 版本不同即替换):否则长开机
  的应用每 4 小时重复弹气泡、重复走一遍 downloadUpdate;手动检查无条件
  清空记录
- downloaded 与 fetchHandled 分开:前者仅在 update-downloaded 置位,用于
  阻止后续 update-available 把状态从 downloaded 翻回 available(否则「关于」
  页按钮会从「重启安装」退回「下载」)
- tray.notifyUpdate 弹 Windows 托盘气泡;非 win32 / 无 tray / 有可见窗口
  都不弹
2026-10-10 13:36:53 +08:00
Bill d3529bb9dc chore(release): v1.0.24
CI / typecheck + test + build (windows) (push) Canceled after 0s
v1.0.24
2026-10-09 15:51:12 +08:00
Bill 12ee779040 test(renderer): 新增 cwd/链接/broadcast 纯函数表驱动测试
CI / typecheck + test + build (windows) (push) Canceled after 0s
渲染层纯逻辑此前零测试覆盖。三个新测试直接 import .ts 源文件(Node
22.18+ 原生类型剥离,不经 esbuild bundle):
- terminal-cwd(69 断言):cdArgument 全部 shell 变体与 conemuCwd 载荷
- terminal-links(92 断言):findUrls 真实合同——尾随标点剥离、重叠
  收敛、裸 host 补 scheme、36 条不变式
- broadcast-store(39 断言):broadcastFanOut 扇出与 pruneOnTargetLoss
  (经 unregisterSession 间接覆盖)
2026-10-09 11:31:26 +08:00
Bill 64982993f3 build(scripts): 发布护栏 fail-closed 与 flag 语义;文档对齐
- release.cjs:assertNoDowngrade 的通道探测原先对网络错误/5xx/解析失败
  一律放行(fail-open),旧分支发旧版本号撞上探测失败会覆盖 latest.yml
  并让 pruneChannel 删掉线上更新版本的 exe。现仅通道真空(404)放行,
  其余抛错中止发布;prune 前的探测保持 best-effort
- 新增 --skip-gitea-release(只跳 Gitea release、通道照常上传);
  --skip-gitea 保持原行为但打印醒目警告;usage 补全 flag 语义
- 文档对齐:STATE.md(v1.0.23、19→22 离线测试、清除 MSI 描述、bundle
  清单补全)、ROADMAP 补 M12/M13、ci.yml 与 AGENTS.md 测试计数、
  AGENTS.md 补录本轮关键不变式(legacy 模板门控、SESSION_STATE/
  pendingOpens、zmodem 背压钩子、主机密钥 TTL);.gitignore 清重复与
  死条目
2026-10-09 11:31:25 +08:00
Bill 6a218ec6c0 fix(renderer): 界面与设置页一轮修补
- FilePanel 上传进度监听器组件卸载时清理(原先只在终态/reject 时 off)
- MonitorPanel:首采即失败时显示中断错误条,不再被 meta 门挡成永远
  '采集中'
- HighlightImportExport:blob 下载延迟 revokeObjectURL(原先同步回收
  会中止下载);FileReader 补 onerror
- HighlightTab 新建规则改 getState() 读法防 stale(对齐 replaceRule)
- ConnectionSidebar/CommandsPanel 三处删除/清空 handler 补 try/catch
  与 message.error(原先失败静默)
- highlightIO 导入信封补 version 校验、kind 缺失不再放行
- i18n:补 settings.highlight.builtin.percent 四语言键(原先唯一缺失
  的内置规则,非中文界面显示中文 note)
2026-10-09 11:31:10 +08:00
Bill 172cce1962 fix(stores): 配置文件形状不符先备份再回退;日志尾部同步落盘
- commands/connections/settings 三个 store 原先只对 JSON.parse 抛错做
  .bak 备份,合法 JSON 但形状不符(如 [1,2,3])会静默回退空/默认,下次
  写盘把命令库/书签凭据/自定义主题整体销毁。现统一为形状不符也先备份
  (与 knownHosts 的 fail-closed 策略对齐),三个 store 测试补对应用例
- sanitizeRules 回退分支返回预设副本而非模块级共享数组引用,并在回退时
  记录 warning(原先连警告都没有)
- 会话日志尾部:logStop 触发的最后一轮 flush 改同步落盘(在途异步写未
  落地时由 drain 循环做保序的最终同步轮),before-quit 不再丢日志尾巴
2026-10-09 11:31:06 +08:00
Bill e23010f950 fix(zmodem): 接收路径写背压;sftp 删除容忍 ENOENT;sysinfo 停止关流
- zmodem 接收:fs WriteStream 缓冲无上限,慢盘 + 快对端可把内存涨到接近
  文件大小。write() 返回 false 时暂停喂入 sentry 的 ssh 流、drain 恢复;
  touchActivity 只在写入被接受或 drain 恢复时计数,stall 看门狗不再把
  '堆在缓冲里'当进展;finalize/detach 解除悬挂暂停
- sftp 删除路径 ENOENT 一律视为已删成功(文件已被他人删除/重复删除/
  传输重跑后目标已消失),不再汇成假 delete failed;真错误仍报错
  (tests/sftp-timeout.mjs 新增 5 断言含防过度吞错用例)
- sysinfo:exec 回调里 state.stopped 分支先 close 到手的 stream
2026-10-09 11:30:51 +08:00
Bill 81ac112dac fix(main): PTY_EXIT 补偿查询、SSH 飞行连接登记与双窗口守卫
- 新增 SESSION_STATE 查询通道(契约先行):PTY_EXIT 只广播一次不重放,
  绑定晚于退出的窗格订阅后补查一次退出状态,快速退出的 shell 不再留下
  永远空白、无死亡遮罩的窗格(退出码登记表上限 512 条 FIFO)
- openSession 在 connectSsh 返回前登记 pendingOpens(owner + onClient
  最早引用),killPtysByOwner/killAllPtys 可中止飞行中的握手,renderer
  崩溃后不再留下无主孤儿 SSH 会话
- whenReady 的 createWindow 加已有窗口守卫,堵住与 second-instance
  showOrCreate 的双窗口竞态
- killAllPtys 补 replayBuffers.clear(),与 killSession 拆卸清单对齐
- promptUser 注释 30s 改为实际 120s;pty.ts 顺带注入 zmodem 背压钩子
  (见下一提交)
2026-10-09 11:30:51 +08:00
Bill 0289dcbd1d fix(workspace): 旧版布局模板不再误杀 SSH 会话
legacy(M6.1 前单 dockview)模板只喂 terminal dockview,成功路径却无条件
rebind 两个 dockview 并 kill 全部 previousSessions,SSH 活面板被降级为本地
终端、会话被静默销毁。成功路径改为按 sshTouched 门控 rebind、按 live 集合
过滤收尾 kill(与错误路径同一语义)。

另:handleApplyTemplate 加在途守卫(并发应用会互杀对方刚恢复的会话);
主机密钥队列加 120s TTL 清扫(主进程超时后不再广播,陈旧弹窗会永远占住
只渲染队头的队列),respondHostKey 移出 setState updater。
2026-10-09 11:30:34 +08:00
Bill 06cfc61392 chore: 对齐 package-lock 根版本号到 1.0.23
CI / typecheck + test + build (windows) (push) Canceled after 0s
2026-10-09 08:47:07 +08:00
Bill e9e0f7592f website: v1.0.23 — SFTP 列表改造、字体缩放与空白处右键菜单
CI / typecheck + test + build (windows) (push) Canceled after 0s
Deploy website to GitHub Pages / deploy (push) Canceled after 0s
2026-10-09 08:38:32 +08:00
Bill 3ab4b50afb chore(release): v1.0.23
CI / typecheck + test + build (windows) (push) Canceled after 0s
v1.0.23
2026-10-09 08:35:12 +08:00
Bill 6e7c9d5376 feat(sftp): 文件面板列头/排序、字体缩放与空白处右键菜单
- 列表头改为 sticky 并支持点击排序(名称/大小/修改时间/权限/用户/组,目录恒在前)
- 时间显示改为 YYYY/M/D HH:MM,大小显示改为 30.7 KB 风格
- 新增字体缩放 0.8–1.8(A-/A+ 与 Ctrl+滚轮),行高字号联动并记忆到 localStorage
- 行高/字号统一由 FilePanel.tsx 计算,经 CSS 变量下发,sftp.css 只消费变量
- 主进程新增 readdirDetailed + parseLongnameOwner,从 OpenSSH longname 解析真实的用户/组
- 列表空白处右键弹出菜单(行内右键仍走原菜单,两者不叠加)
- 修复:antd 把 ant-dropdown-trigger 克隆到行元素上,旧规则以更高特异性顶掉了 .sftp-row 的 display:grid,导致列此前并未按网格对齐
- 4 语言词典补充列名与缩放键
2026-10-09 08:33:06 +08:00
Bill 5602949a4f website: dark-first redesign + SFTP 面板截图
CI / typecheck + test + build (windows) (push) Canceled after 0s
Deploy website to GitHub Pages / deploy (push) Canceled after 0s
- 全站深色(#0b0e12 + 终端绿),右上角可切浅色;两套主题共用 CSS 令牌
- 自托管字体:Space Grotesk(展示)+ JetBrains Mono(等宽),不再依赖系统栈
- 「核心能力」改为横向滚动卡片轨道(箭头翻页 / 拖拽 / 键盘方向键)
- 新增 SFTP 文件面板截图(含空白处右键菜单),IP 已打码
- 补 og:image / theme-color / skip-link / 焦点环 / 导航当前位置高亮
2026-10-09 00:46:35 +08:00
Bill 9e5e959f80 docs(readme): add screenshots & advantages section; fix stale facts (11 themes, 19 tests, MSI dropped)
CI / typecheck + test + build (windows) (push) Canceled after 0s
2026-10-08 14:19:22 +08:00
Bill 309de439e8 website: v1.0.22 — drop MSI, refresh screenshots (agents/themes/lock)
CI / typecheck + test + build (windows) (push) Canceled after 0s
Deploy website to GitHub Pages / deploy (push) Canceled after 0s
- download: NSIS exe only, v1.0.22 links, update-source order GitHub-first
- features: 22 highlight rules, 11 builtin themes, bg dim layer
- new rows: AI Agent workspace, lock screen & security
- changelog: 1.0.22 / 1.0.21 / 1.0.20
2026-10-08 14:10:15 +08:00
Bill a381d7be03 docs: warn about auto-created wrong-target tags in release flow
CI / typecheck + test + build (windows) (push) Canceled after 0s
2026-10-08 13:31:54 +08:00
Bill 14f5963706 build: drop MSI installer, NSIS exe only
CI / typecheck + test + build (windows) (push) Canceled after 0s
electron-updater never supported MSI auto-updates; the exe is the only
installer from v1.0.22 on. release.cjs no longer expects/uploads the msi.
2026-10-08 13:25:01 +08:00
Bill 40336d014d release: v1.0.22
CI / typecheck + test + build (windows) (push) Canceled after 0s
fix(ime): actually ship @xterm/xterm 6.1.0-beta.304 (v1.0.21 built from stale
node_modules with 6.0.0); add predist dependency-consistency gate
(scripts/verify-deps.cjs) and rename-retry shim for the AV scan EPERM race
v1.0.22
2026-10-08 13:09:13 +08:00