Skip to content

v1.2.3

Latest

Choose a tag to compare

@github-actions github-actions released this 30 Oct 06:02
· 2 commits to master since this release

📢 重要通知

  • 外部代理模式下,请确认系统里安装的 CA 与 ca/ 目录中的那一张一致。若 ca/ 曾被删除重建,系统证书库里可能残留旧 CA,外部客户端会因证书不被信任而连不上;删掉旧的、重新安装 ca/akagi-ng-ca.cer 即可。内置窗口不受影响——它现在自己信任代理签发的证书

🎨 UI/UX 增强

  • 重做暗色模式背景:暗色此前沿用亮色的四角渐变,只是把不透明度从 0.2 提到 0.3——四角同时被点亮,中间反而是一片灰紫。现在四种色相改为带实心核心的模糊色块,底色降到 zinc-950,再加一层细噪点压掉大面积暗色渐变必然出现的色带。
  • 界面统一到一套尺度:按钮曾有四种高度、三种焦点环、六种圆角(其中三种在 token 之外),还有一批在 Tailwind v4 下根本不生效的动效时长(duration-* 只改过渡,不改 animate-in)。现在尺寸收成 1–4(24/32/36/40px)、圆角收成 --radius-1..6、.focus-ring 是唯一的焦点样式,IconButton 是独立组件且 aria-label 类型必填——纯图标按钮不填无障碍标签无法通过编译
  • 无障碍语义按 Radix 对齐:CapsuleSwitch 与主题开关改用 ToggleGroup,渲染成真正的单选组(role="radiogroup" + role="radio" + aria-checked),不再用 aria-pressed 冒充;Alert 不再在容器上挂 role="alert";SettingsItem 补上 aria-describedby
  • 退出动画在任何退出方式下都会播放:此前只挂在某一个按钮上,从托盘退出、从系统退出、或从启动错误页退出都会直接硬关闭
  • 设置面板只保留标题:描述行只是在重复标题
  • 四个语种的界面文案重写:在线设置里「开启后……」改成完整句子,密钥尾号、超时、套餐等标签补上冒号与量词;「在线服务暂时不可用 / 已回退到本地模型」改成能区分「本次回退」与「持续中断」的说法

🐛 问题修复

  • 加杠那一瞬间会多推一条无意义的推荐:决策点判定对任何加杠、拔北都会询问,不区分座次。你自己加杠之后引擎仍停在 WaitAct,decide() 照样返回一组非强制动作,于是先发出一条推荐、摸到岭上牌后又发出一条——同一个决策出现两行,而第一行针对的是已经交出回合的手牌。现在只在他家宣言时询问,那里抢杠才是真实选项
  • 模型指示器的黄灯状态永远不可达:熔断器在第一次请求失败时就布防,一次超时同时报告了「已回退」与「持续中断」,HUD 取更具体的那个,于是任何网络抖动都直接变成闪红五秒。现在两个标志重新对应两件不同的事:第一次失败只算临时回退,下一次决策立刻重试;从连续第二次超时起才按五秒起步、逐次翻倍退避
  • 启动游戏的错误提示在两种情况下都在说谎:一是页面还没加载完就关掉窗口,Electron 报 ERR_FAILED,被当成 URL 有问题,于是让你去检查一个根本没问题的设置;二是外部代理模式下内置窗口从不信任我们自己的 CA,代理重签的 TLS 被 Chromium 拒绝,每次启动都失败。现在关闭窗口被识别为无操作,证书信任只放行那张 CA 真正签过的叶子证书
  • 代理没运行时提示指错了方向:代理连接失败与 URL 错误此前共用一个提示。失败原因现在随结果返回(IPC 会把异常压成字符串,而只有渲染进程知道该说哪种语言),代理问题直接说明代理未启动

⚡ 改进与优化

  • 归档旁边不再有解包目录:.node 无法从 asar 内直接 dlopen,Electron 会先把它解到临时文件,asarUnpack 就是为此存在的,而它同时产生了 app.asar.unpacked。原生推理引擎现在直接随包放在 resources/engine/,改用 napi 生成 loader 本来就优先生效的 NAPI_RS_NATIVE_LIBRARY_PATH 指过去,于是整个目录消失。顺带把永不启用的 tls-impersonate 预编译文件(含 darwin / linux 各两份)一并排除
  • 图标按平台分发:Windows 包不再携带 318 KB 的 .icns 和 macOS 托盘模板,反之亦然
  • 安装后体积 339.6 MB(原 353.1 MB),压缩包 145.4 MB(原 146.3 MB)

🛠️ 维护与重构

  • 一个协调器接管所有退出路径:ShutdownCoordinator 经 before-quit 进入,幂等(第二次点击并入第一次),把两段时长下发给渲染进程并等待确认、以超时兜底;游戏窗口与 HUD 改用 setOpacity 原生淡出——此前那个事件发给了承载远程内容、根本没有监听者的游戏窗口
  • 渲染进程的每个事件都有了类型:主进程主动推送的 8 个通道此前在各调用点都是裸字符串,一侧改名另一侧毫无察觉;其中两个还用 as 强转,payload 掉字段会一路走到 HUD 才炸。现在 api/events.ts 统一定义,引擎 payload 在边界解析,渲染不了的值丢弃并记日志
  • 窗口的会话规则(分区、权限白名单、代理、证书信任)从 game-window 抽到 game-session,启动失败契约抽到 game-launch,game-window 因此比改动前还短
  • 主题、设置、尺寸各自只剩一个来源;Header 的 8 个 prop 收成 3 个,Dashboard 从 174 行拆到 88 行
  • 在线账户的几个操作补齐 try/catch/finally,按钮不会因为一次异常永久卡在加载态

📦 更新依赖

  • Electron 44 → 45(Chromium 156、Node 24.22.1),运行时体积减少 11.4 MB
  • i18next 26.5.11、react-i18next 17.1.9、react-router 8.5.1、lucide-react 1.64.0、react-toastify 11.2.0、typescript-eslint 8.72.1

🧪 测试与质量

  • 全仓 548 个单元测试、23 个集成测试、60 个 Rust 测试
  • 新增 game-launch-failure.test.ts,把「用户关窗」「重定向取消」「代理不可达」「URL 真的错了」四种情况分别钉住

📝 文档更新

  • README 的 AI 模型特性补上 MJOT 云端推理

📢 Important Notices

  • In external proxy mode, check that the certificate installed in your system is the same one in ca/. If that folder was ever deleted and regenerated, the old CA can linger in the OS store and external clients will fail to trust the proxy; delete it and install ca/akagi-ng-ca.cer again. The built-in window is unaffected — it trusts the proxy's certificates itself now

🎨 UI/UX Enhancements

  • *Refresh dark mode background: it used to reuse the light theme's four corner washes at a higher opacity, which lit every corner at once and left the middle a flat grey-purple. The four hues are blurred colour fields with a solid core now, over a darker base, with a fine grain layer that removes the banding a large dark gradient always shows.
  • The interface is on one scale: buttons had four heights, three focus rings, six corner radii (three of them outside the token set), and a batch of durations Tailwind v4 never applied, since duration-* sizes transitions rather than animate-in. Sizes are 1–4 (24/32/36/40px), radii are --radius-1..6, .focus-ring is the only focus treatment, and IconButton is its own component with aria-label required — an icon-only control cannot ship without a label
  • Semantics now match what Radix renders: CapsuleSwitch and the theme switch are ToggleGroups, which render as a real radio group (role="radiogroup" + role="radio" + aria-checked) instead of borrowing aria-pressed; Alert no longer puts role="alert" on a container; SettingsItem gained aria-describedby
  • The exit animation plays whichever way you quit: it was wired to one button, so quitting from the tray, from the OS, or from the startup error page skipped straight to a hard close
  • The settings panel is a title and nothing else: the description underneath only repeated the heading
  • Notices were rewritten in all four locales: hints read as full sentences, labels carry their colon and measure word, and the online-model states distinguish "stepped aside for this decision" from "sustained outage"

🐛 Bug Fixes

  • An added kan produced a recommendation nobody could act on: the decision-point test asked at every added kan and pulled north regardless of seat. After your own added kan the engine still reports itself as ready to act, so a row appeared for a hand that had already given up its turn, then a second row when the replacement tile arrived — two rows for one decision. It only asks when another seat declares now, where robbing the tile is a real option
  • The amber status of model status indicator light was unreachable: the circuit breaker armed itself on the first failed request, so a single timeout reported both "fell back" and "sustained outage". The HUD picks the more specific of the two, which meant every blip went straight to pulsing red for five seconds. A first failure is a hiccup again — the next decision retries at once — and the backoff only starts from the second consecutive failure
  • The launch error toast lied in two different ways: closing the window before the page arrived surfaced as ERR_FAILED and was reported as a broken URL, sending you to check a setting that was never wrong; and in external proxy mode the window never trusted our own certificate authority, so every launch failed on TLS. Closing a window you just opened is correctly a no-op now, and the trust only accepts leaf certificates that CA actually signed
  • An unavailable proxy was blamed on your settings: proxy failures and URL failures shared one message. The reason travels with the result now (IPC flattens an exception into a string, and only the renderer knows which language to say it in), so an absent proxy says so

⚡ Improvements & Refinements

  • Nothing is unpacked next to the archive any more: Windows will not dlopen a .node that only exists inside asar, so Electron extracts it to a temp file first — that is what asarUnpack is for, and it is what produced app.asar.unpacked. The native engine ships to resources/engine/ instead, reached through NAPI_RS_NATIVE_LIBRARY_PATH, which napi's generated loader already prefers over its own path guessing; the directory is gone. Unused prebuilds of a module we never enable went with it, including the darwin and linux builds
  • Icons ship per platform: a Windows install no longer carries a 318 KB .icns and the macOS tray templates, and vice versa
  • 339.6 MB installed (was 353.1 MB), 145.4 MB compressed (was 146.3 MB)

🛠️ Maintenance & Refactoring

  • One coordinator owns every quit path: ShutdownCoordinator is reached through before-quit and is idempotent, so a second click joins the first. It hands the renderer both durations and waits for an acknowledgement, with a timeout as the backstop, while the game and HUD windows fade through setOpacity — the event used to go to the game window, which hosts remote content and listens for nothing
  • Every event the renderer receives is typed: the eight channels the main process pushes on its own were raw strings at each call site, so a rename on one side stayed silently correct on the other; two were coerced with as, which let a payload that lost a field reach the HUD and throw there. They are named once in api/events.ts, and engine payloads are parsed at the boundary — a value that cannot be rendered is dropped and logged
  • The window's session rules (partition, permissions, proxy, certificate trust) moved out of game-window into game-session, and the launch contract into game-launch, which leaves game-window shorter than before these fixes
  • Theme, settings and sizes each have one source now; Header went from eight props to three, and Dashboard from 174 lines to 88
  • The online account actions gained their try/catch/finally, so a single exception can no longer leave a button spinning forever

📦 Dependency Updates

  • Electron 44 → 45 (Chromium 156, Node 24.22.1), 11.4 MB smaller at runtime
  • i18next 26.5.11, react-i18next 17.1.9, react-router 8.5.1, lucide-react 1.64.0, react-toastify 11.2.0, typescript-eslint 8.72.1

🧪 Testing & Quality

  • 548 unit tests, 23 integration tests and 60 Rust tests in total
  • game-launch-failure.test.ts pins the four cases apart: the user closing the window, a cancelled navigation, an unreachable proxy, and a URL that really is wrong

📝 Documentation

  • README's AI model section mentions MJOT online inference

Full Changelog: v1.2.2...v1.2.3