1. 27 Apr, 2026 2 commits
    • 第二阶段发布后用户日志显示: 启动 8s 后 5s 内连续 5 次
      load_history_index 全扫 513 条 + 513 次 stat, 主线程累计阻塞,
      随后日志戛然而止 (典型 jetsam SIGKILL 指纹).
      
      定位到两条阶段 1+2 没修的 hot path:
      
      1. get_history_item(timestamp): 点击历史项查看详情时触发,
         原走 load_history_index() + 线性查找. 改为直接调
         load_history_item_fast() (读单个 metadata.json), O(1).
      
      2. _update_history_index(history_item): 每次生成完图都调,
         原走 load_history_index() 全扫 + from_dict + 路径修正 + 整表写回.
         改为直接对 raw json list dict 操作, 无 stat / 无 from_dict.
      
      改完之后 hot path 完全不再触发 _fix_history_path 循环.
      柴进 committed
    • 阶段 1: 生成完成不再 refresh_history() 全量重建 360+ 个 widget,
      改走 prepend_history_item() O(1) 增量插入.
      
      阶段 2 Step 1: QListWidget -> QListView + HistoryListModel:
      - Model 仅持有 list[str] timestamps + OrderedDict LRU 缓存
      - 视口可见行才触发 data() 加载缩略图,与历史总条数解耦
      - delete/clear 走 model 增量,不再触发全量刷新
      
      修复 macOS 上长时间运行被 jetsam SIGKILL 的崩溃路径
      (2026-04-24 单日 14 次闪退,无 Traceback / faulthandler 栈).
      柴进 committed
  2. 21 Apr, 2026 5 commits
    • 自洽问题: 原先 REQUIRED_TABLES 里列了 nano_banana_app_config,
      但 _check_version 明明有"读不到就放行"的 fail-safe。
      同一张表一边说"必须存在",一边说"不存在也没事",逻辑矛盾。
      
      结果是: 如果 migration 还没在生产 DB 跑, 1.1.0 客户端启动就会在
      "表存在性校验"硬挂, 弹"应用启动失败,请联系 @柴进",
      完全绕不过 fail-safe。
      
      修正: app_config 作为"版本门禁"的后端存储不是启动必需品。
      - 没表/没记录 → fail-safe 放行, 版本门禁临时关闭
      - 有表+有记录 → 正常做版本校验
      - migration 可以任意时刻跑, 不阻塞发布节奏
      柴进 committed
    • 机制:
      config_util.sync_bundled_api_key(user_config_path) 启动时调用:
      - frozen 态才生效 (开发态不干涉)
      - 打包 config.json 的 api_key 和用户目录的比较
      - 不一致时只覆盖 api_key 这一个字段
      - saved_prompts / last_user / saved_password_hash / db_config 全部保留
      - 多重 fail-safe: 打包 config 缺失/解析失败/api_key 空/用户 config 解析失败 → 全都不动
      
      新 Gemini Key 已写入 repo 的 config.json。两周过渡计划:
      - D0 (本次发布): 新 Key 随 bundled config 分发,升级用户启动瞬间自动切到新 Key;
        老用户暂不升级,config.json 里还是老 Key,Google Cloud 两把 Key 并行
      - D14: Google Cloud Console 作废老 Key;
        已升级用户零感知,没升级用户此时才 401,被迫升级
      
      注: 企业内部软件,有用户名密码保护,Key 内置到 repo 可接受
      柴进 committed
    • 机制:
      - 新建 version.py (APP_VERSION = 1.1.0) 作为单一真相源
      - 新建 migration: nano_banana_app_config KV 表
        初始化 min_client_version=1.0.0, download_url=飞书文档
      - preflight 在 DB 表/字段校验通过后加一步版本校验:
        读 app_config -> 对比本地 APP_VERSION
        过旧返回 VERSION_TOO_OLD::<min>|<url> 前缀
        fail-safe: 读不到配置/解析失败 -> 放行 (避免 DBA 误操作全体挂掉)
      - 新增 handle_version_too_old: 明文弹窗 + "打开下载页" 按钮
        用 QDesktopServices.openUrl 调系统默认浏览器 (跨 Win/Mac)
      - image_generator 启动处按 is_version_error 分发:
        版本过旧走明文升级提示, 其他错误保留原脱敏路径
      
      Why:
      以后想淘汰任一老版本,只需:
        UPDATE nano_banana_app_config SET config_value='X.Y.Z'
         WHERE config_key='min_client_version'
      不再需要轮换 API Key (一次性核爆 -> 精细版本控制)
      柴进 committed
    • 根因: audit_logger.py / config_util.py / preflight.py 这三个启动
      必需模块从未被 git 追踪过. Windows 构建机上这些文件在本地磁盘,
      PyInstaller 能找到, 所以 Win 包正常; 但 Mac 拉代码后根目录缺这
      三个文件, PyInstaller 的 Analysis 找不到 import 链, 构建要么失败
      要么运行时 ImportError.
      
      本次补齐:
      - audit_logger.py — 审计日志单例 (NDJSON 本地队列 + 异步 MySQL 上传)
      - config_util.py  — 跨平台配置路径解析与安全加载
      - preflight.py    — 启动门禁 (config/DB/schema 校验)
      - database_schema.sql — 运维参考
      - migrations/2026-04-21_add_audit_log_columns.sql — 审计表迁移
      - .gitignore      — 屏蔽 .venv/build/dist/logs/__pycache__ 等,
                          防止以后再漏推业务代码时被大量噪声淹没
      柴进 committed
    • 本次改动分三块,合并一个提交:
      
      1. 运行中任务取消 (方案 A 软取消)
         - cancel_task 扩展支持 RUNNING: 标记 CANCELLED,脱钩 _current_worker,立刻 _process_next()
         - _on_task_completed/_on_task_failed 开头加 status == CANCELLED 自检,丢弃废 worker 回调
         - 右键菜单对 PENDING/RUNNING 都显示"取消任务"
         - _update_summary 新增"已取消"状态;_cleanup_old_tasks 纳入 CANCELLED 清理
      
      2. 任务栏点击回显修复
         - 根因: TaskQueueWidget 创建时没传 parent,self.parent_window 永远 None,回显全部静默失败
         - 根因: 两套重名方法互相覆盖,生效版用了 prompt_input / add_reference_image 等不存在的属性
         - 修复: 删除重复定义;回填改用主窗口真实属性 (prompt_text / uploaded_images + update_image_preview / aspect_ratio / image_size / display_image)
         - 款式设计 tab 也完整支持 prompt / 宽高比 / 尺寸 / 结果图回显
      
      3. Flash 独占宽高比 + 模式兼容校验
         - 新增 1:4 / 4:1 / 1:8 / 8:1 四个极速模式独占比例
         - 款式设计 tab 宽高比补齐到和图片生成 tab 一致
         - FLASH_ONLY_ASPECT_RATIOS 常量作为单一真相源
         - 双向实时校验:
           * 选 Flash-only 比例 + 慢速模式 → 问是否切到极速,拒绝则回滚比例
           * 极速 + Flash-only 比例 → 切慢速 → 问是否坚持切换,坚持则比例回落 1:1
         - 提交入口保留校验作为 defense in depth
      柴进 committed
  3. 15 Apr, 2026 6 commits
    • 之前只有两个图标并排, 用户看不出来要拖拽. 现在在 DMG 挂载打开时:
      - 中间一根灰箭头从 .app 指向 Applications
      - 底部中文提示 "将左侧应用拖到右侧 Applications 即完成安装"
      - 窗口/图标/箭头三者坐标对齐
      
      实现:
      - 用 PIL 在 staging 阶段生成 600x400 PNG, 放 .background/bg.png
        (dot 前缀让 Finder 默认隐藏)
      - AppleScript 里 set background picture 用 HFS path (冒号分隔)
      - 图标位置 {150,200} / {450,200} 与背景图箭头两端对齐
      
      PIL 字体查找: PingFang (默认中文) -> STHeiti -> Helvetica -> 兜底,
      在打包机上都会命中至少一个.
      
      Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
      柴进 committed
    • 双击挂载后, Finder 窗口显示两个图标并排, 用户直接把
      ZB100ImageGenerator.app 拖到 Applications 快捷方式上即可安装,
      不用讲解路径, 符合 macOS 标准分发体验.
      
      实现:
      - 先 staging: 把 .app 放入临时目录, 同级建 Applications 软链
      - hdiutil create UDRW 可写镜像
      - 挂载 -> AppleScript 设置窗口大小/图标位置/视图样式
      - 卸载 -> hdiutil convert UDZO 压缩成只读分发包
      - osascript 失败不中断 (防 Finder 自动化权限未授予), DMG 仍可用
      
      Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
      柴进 committed
    • 背景: lehe 机器启动崩是因为 SMB 传输吃掉了 PIL/.dylibs -> __dot__dylibs
      内层 symlink, 导致 libtiff dlopen 断链.
      
      修复: 构建脚本新增 hdiutil create 步骤打包 DMG (UDZO 压缩). DMG 是
      HFS+ 镜像, symlink 完整保留, 用户挂载 -> 拖拽到 Applications 即用.
      
      以后发 DMG, 不要直接拷贝 .app 目录.
      
      Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
      柴进 committed
    • lehe 那台 macOS 26 新包仍崩, 诊断发现不是打包问题:
        Frameworks/libtiff.6.dylib -> PIL/.dylibs/libtiff.6.dylib (broken)
      
      PyInstaller 6.x 实际结构:
        PIL/__dot__dylibs/  <真目录>
        PIL/.dylibs -> __dot__dylibs  <内层 symlink, 被 NAS 吃掉了>
        Frameworks/libtiff.6.dylib -> PIL/.dylibs/libtiff.6.dylib  <外层 symlink>
      
      内层 symlink 丢失 → 整条链断. 其他 macOS 26 机器能跑是因为挂载
      NAS 的协议/客户端不同, 保 symlink.
      
      记进 spec 注释, 下次再有人打包踩这坑直接看到结论.
      
      Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
      柴进 committed
    • 上一版 spec (4097b529) 在 macOS 26 构建出的包仍崩在同一位置:
        dlopen: Library not loaded: @rpath/libtiff.6.dylib
      
      修改:
      - 引入 PyInstaller 官方 collect_dynamic_libs('PIL', destdir='.')
        作为主策略, 显式枚举 .dylibs/.libs 作为兜底
      - 每步打印详细信息: PIL 安装路径、找到的文件列表、
        最终 binaries 合并结果
      - 最后如果 len == 0 直接打印警告, 免得构建成功但运行时才崩
      
      下次构建输出里找 [spec] 开头的行, 就能看清是哪步出了问题:
        - collect_dynamic_libs 返回空? -> PIL 没 bundled dylibs
        - .dylibs/ 不存在? -> Pillow 装的不是 wheel
        - 有文件但 bundle 里没有? -> PyInstaller 传入后续处理的问题
      
      Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
      柴进 committed
    • 构建流程 (修复 macOS 26 上 libtiff.6.dylib 未打包闪退):
      - ZB100ImageGenerator.spec 成为跨平台构建配置唯一真相源
      - 显式枚举 PIL/.dylibs/.libs 下的原生库并平铺到 bundle 根目录,
        匹配 PyInstaller 把 _imaging.so 的 @rpath 改写成 @loader_path/..
        (= Contents/Frameworks/) 的预期位置
      - build_mac_universal.sh / build_windows.bat 不再删除 *.spec,
        调用简化为 `pyinstaller ZB100ImageGenerator.spec`
      - 旧的 --collect-all PIL 方案保留了 .dylibs/ 目录结构, 跟 rpath
        预期位置不匹配, 治标不治本
      
      诊断日志 (定位 refresh_history 的 SIGKILL 位置):
      - 新增 _flush_logs() 模块级工具, 在可疑阶段边界强制刷盘
      - load_history_index: 每 20 条打一次路径修正进度
      - refresh_history: clear/load/loop 三段独立计时, 每 20 条打渲染进度
      - 下次 macOS 卡死被 SIGKILL 时能精确定位死亡行
      
      昨天 138ec9fa 的 thumb.jpg 缓存挡住了内存压力型 SIGKILL, 但今天
      115 条时 refresh_history 仍卡 8 秒后被 WindowServer 强杀 (日志
      只打了 "开始刷新" 就再无输出). 真正的架构修复 (取消 clear+rebuild
      全量重绘) 留待下次定位清楚后再做.
      
      Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
      柴进 committed
  4. 14 Apr, 2026 2 commits
    • 根因:
      - refresh_history 每次调用都 history_list.clear() + 重建全部条目
      - 每条加载 2K PNG 原图做 QPixmap (~16MB 解码) 再缩到 120x120
      - 106 条历史 × 16MB = ~1.7GB 瞬时内存峰值
      - macOS 内存压力触发 SIGKILL,不可被 faulthandler 捕获
      - 日志里只有 "开始刷新" 没有 "加载到 N 条",确认死在 clear()/load 之间
      
      修复:
      - HistoryManager 新增 thumb_path_for + get_or_create_thumbnail:
        用 PIL 生成 240x240 JPEG 缓存到 <record_dir>/thumb.jpg,
        基于 mtime 判断是否重新生成
      - save_generation 保存原图后顺便生成缩略图 (失败不影响主流程)
      - refresh_history 只加载 thumb.jpg, 内存峰值从 1.7GB → 6MB (280x 下降)
      - 缩略图生成失败用占位图兜底, 不回退加载原图 (防回到危险路径)
      - 首次升级会为 106 条存量一次性补生成 thumb.jpg (PIL 串行 ~32MB 峰值, 安全)
      
      附加:
      - refresh_history 增加 "刷新完成" 收尾日志, 便于下次若再崩定位死亡位置
      
      跨平台: 纯 PIL + pathlib + Qt 标准 API, Windows 零回归
      
      Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
      柴进 committed
    • 粘贴闪退 (macOS 26):
      - _safe_get_clipboard_image 在 Darwin 上禁用 mimeData.imageData() /
        clipboard.image() / application/x-qt-image 三条 native crash 路径,
        统一走 image/* 原始字节 + osascript PNGf 兜底
      - DragDropScrollArea.dropEvent 的拖入图像分支同步做平台分流
      - Windows/Linux 路径完全保留,零回归
      
      长时间运行闪退:
      - init_logging 改用 RotatingFileHandler (5MB × 5),避免日志无限增长
      - 启动时清理超过 24 小时的 clipboard_*.png 遗留临时文件
      
      Gemini 返回空图片:
      - response_modalities 加上 TEXT,允许模型回传拒绝理由
      - response.parts 增加 None 保护,修复日志里 20+ 次
        'NoneType object is not iterable' 异常
      - 错误上浮 finish_reason + 模型说明到 QMessageBox
      
      缩略图拖拽重排:
      - 新增 DraggableThumbnail + THUMB_REORDER_MIME 内部协议
      - 缩略图可拖动调整顺序,reorder_image 正确处理左右移动的索引
      
      Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
      柴进 committed
  5. 19 Mar, 2026 3 commits
    • 根因: PIL/_imaging.so 依赖 libtiff.6.dylib,但 PyInstaller 默认未收集。
      修复: 添加 --collect-all PIL 确保所有 Pillow 原生库(.dylib/.so)被打包。
      同步更新 Windows 打包脚本。
      
      Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
      柴进 committed
    • build_mac_universal.sh 是严格超集:自动检测架构、自动安装依赖、错误处理更完善。
      
      Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
      柴进 committed
    • - faulthandler: segfault 时自动输出 Python 调用栈到 crash_log.txt
      - 全局 sys.excepthook 捕获未处理异常
      - Qt 消息拦截器捕获 QtWarning/Critical/Fatal
      - 启动阶段打桩 [BOOT Phase 0~8]
      - 主窗口初始化打桩 [INIT 1/6~6/6]
      - QImage.save 崩溃高发点前后日志
      - 系统环境信息记录(OS/Python/Qt/Pillow 版本)
      
      Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
      柴进 committed
  6. 02 Mar, 2026 1 commit
  7. 28 Feb, 2026 9 commits
  8. 27 Feb, 2026 2 commits
  9. 15 Jan, 2026 1 commit
  10. 13 Jan, 2026 2 commits
  11. 18 Dec, 2025 2 commits
  12. 12 Dec, 2025 2 commits
  13. 11 Dec, 2025 3 commits