- 09 May, 2026 15 commits
-
-
回顾旧 StyleDesignerTab,task #15a 漏了一批按钮,现在补全: 顶部工具行(左侧卡片): -
柴进 committed
随机:跳过 lockedFields,每个非锁定字段随机取一个 option
-
恢复默认词库:调 jewelry.resetAll()
- 重置字段:清空 formData
字段行(每个 ComboBox 后跟 3 个 36px 按钮):
-
添加词条:弹 Dialog 输入新值 → jewelry.addItem(category, value)
-
️ 删除当前词条:删 ComboBox 当前选中项
-
/
字段锁:locked 字段 ComboBox disabled,标签变 textTertiary,
跳过
操作行加
下载图片(复用 ImageGenTab 的 SaveFileDialog 逻辑 + imageGen.saveFile)
ComboBox 宽度修复:
Layout.preferredWidth: 240 + Layout.maximumWidth: 240 + Layout.fillWidth: true
锁住宽度,避免随机后内容长度(如"小爪层戒臂(如莲花夹层设计)")撑大 RowLayout
造成视觉跳动。displayText 由 ThemedComboBox.contentItem 的 elide 处理。
视觉验证:QML_DEBUG_TAB=1 进入款式设计 tab,三排顶部按钮 + 8 行字段
(每行 ComboBox +
+
️ +
)+ 操作行(生成 + 下载)全就位,UI 无回归。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> -
qml_poc/qml/StyleDesignerTab.qml (新): - 左侧 360px 卡片:8 字段 ComboBox 表单(jewelry.categories Repeater) 每个字段头插 "(不选)" 让用户能清空 onActivated → updateField(category, value) → 触发 reassemble - 右侧上半 Prompt 预览:实时调 jewelry.previewPrompt(formData) Text 只读展示 + 字数统计 - 右侧操作行:生成图片 PrimaryButton (调 imageGen.submitTask 默认 1:1 / 2K / 慢速模式 Pro 模型) + 重置字段 + 状态指示器 - 右侧下半预览:lastResultPath Image,双击系统查看器打开 - 监听 imageGen 4 个信号但只对 currentTaskId 匹配的做反应(taskId 唯一可区分图片生成 tab vs 款式设计 tab,互不冲突) - 监听 jewelry.libraryChanged → optionsRepeater.reloadAll (task #15b 词库管理对话框触发时刷新 ComboBox model) MainWindow.qml: 款式设计占位 Item → StyleDesignerTab {} 未做(task #15b): - 词库管理子对话框(每个类别能增删词条) - jewelry.addItem / removeItem / resetAll Slot 都已就位,缺 UI 视觉验证:QML_DEBUG_TAB=1 直接进款式设计 tab,8 字段 ComboBox + Prompt 预览(PromptAssembler 在空表单时给 fallback "一款高端精品珠宝戒指设计… 高端珠宝渲染" 52 字)+ 生成按钮全就位,UI 无回归。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>柴进 committed -
core/history.py: HistoryListModel.roleNames() 暴露 4 个 Qt 内置 role 给 QML: display / decoration / toolTip / timestamp (UserRole) 旧 QListView 用 int role 不受影响 bridges/history.py: Slot thumbnailPath(timestamp) → str 返回 thumb.jpg 本地路径(按需用 PIL 生成 240px JPEG),缩略图缺失回退原图 qml_poc/qml/HistoryTab.qml (新): - 左侧 340px 卡片:列表 (history.model) * delegate 76px 高:60×60 缩略图 + timestamp + prompt 双行摘要 * 选中态 accent 蓝边框 + accentSubtle 浅蓝底 * hover 提示完整 toolTip - 右侧详情卡片: * timestamp + createdAt + "在文件管理器中打开" + "删除" * 360px 大图(双击系统查看器打开) * GridLayout 4 列元信息(宽高比/尺寸/模型) * prompt ScrollView(长文本可滚) - Component.onCompleted + onCountChanged 自动选中第一条 - itemRemoved 信号 → 选中失效时回到空态 qml_poc/main_qml.py: AppState 加 QML_DEBUG_TAB env var (0/1/2) 控制启动 tab 索引,方便 PoC 调试 MainWindow.qml: 历史记录占位 Item → HistoryTab {} 视觉验证:QML_DEBUG_TAB=2 直接进历史 tab,2 条已存历史正确渲染(缩略图 + 详情大图 + 元信息 + prompt 都对),UI 无回归。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>柴进 committed -
MainWindow.qml sidebar 改造: - header: 加 "N 进行中" 计数(绑定 taskQueue.runningCount + pendingCount,0 时隐藏) - ListView model: 硬编码 ListModel → taskQueue.model(_TaskListModel QAbstractListModel) - 空态占位: "暂无任务"(visible: count === 0) - delegate 改造,56px 高 ColumnLayout 三行: 1. prompt 摘要(>14 字省略) 2. 状态彩色文字(pending=橙 / running=蓝 / completed=绿 / failed=红) + 耗时 3. 进行中任务的细进度条(高 2px,绑 progress 0-1) - delegate 用 required property 声明 6 个 role:taskId / prompt / status / progress / statusText / elapsed - 右键点击 pending/running 任务 → taskQueue.cancelTask(taskId) 至此 生成 → sidebar → 历史 闭环完整: - 用户在图片生成 tab 点 "生成图片" → ImageGenBridge.submitTask - TaskQueueManager 信号 → TaskQueueBridge._on_task_added → upsert 进 model - sidebar ListView 自动渲染新行,状态文字 + 进度条同步 - 完成后 ImageGenBridge → HistoryBridge.addNew,历史 tab 也会增量 视觉验证:QML_AUTO_LOGIN=1 启动主窗口,空态正确显示"暂无任务",UI 无回归。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
柴进 committed -
ImageGenBridge.saveFile(src, dest) → bool shutil.copy2 wrap,源文件不存在 / 写盘失败时记日志返回 False ImageGenTab.qml: - SaveFileDialog (FileDialog SaveFile mode + defaultSuffix png) - "下载图片" enabled by lastResultPath !== "",onClicked 弹保存对话框 - onAccepted 调 imageGen.saveFile,成功状态绿色 "● 已保存到 <path>" - 预览 Image 包 MouseArea,onDoubleClicked Qt.openUrlExternally("file:///" + path) 至此 task #14 完整闭环: #14a 核心生成(prompt → submit → 进度 → 预览) #14b 参考图录入(添加 / 粘贴 / 拖拽 + 缩略图删除) #14c 提示词收藏 / 删除(持久化 config.json) #14d 下载图片 + 双击预览打开系统查看器 视觉验证:QML_AUTO_LOGIN=1 启动主窗口,UI 完整无回归,下载按钮在无生成图时正确灰。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>柴进 committed -
config_util.py 加 save_config(path, config) → bool 原子写:tmp 文件 + replace,失败记日志返回 False(不抛异常) ImageGenBridge: - 构造函数加 saved_prompts / config_path 参数 - Property savedPrompts: list[str] - Slot addSavedPrompt(prompt): 去重 + 插入头部 + 持久化 + 信号 - Slot removeSavedPrompt(prompt): 移除 + 持久化 + 信号 - _persist_saved_prompts: load_config_safe → 改 saved_prompts → save_config main_qml.py: 装 saved_prompts + config_path 给 ImageGenBridge ImageGenTab.qml: - "
柴进 committed
收藏" enabled by promptArea.text.trim().length > 0,onClicked add
- "删除" enabled by savedPrompts.length > 0,删除当前 ComboBox 选中项
- 快速选择 ComboBox model: imageGen.savedPrompts (响应 savedPromptsChanged 自动刷)
- onActivated 仅在 currentText 非空时填到 promptArea
视觉验证:QML_AUTO_LOGIN=1 启动主窗口,ComboBox 已显示从 config.json 读到的
"主石换成闪耀的祖母绿",收藏按钮在 prompt 空时正确灰。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> -
ImageGenBridge 加 2 个 Slot: - pasteFromClipboard() → str 从 QGuiApplication.clipboard() 拿 image,存到 tempdir/nano_banana_app/clipboard_*.png (与旧 _cleanup_clipboard_tempfiles 同目录,启动期统一 24h 清理) 失败返回 "" - normalizeFileUrls(urls list) → list 把 QML DropArea 给的 file:/// QUrl 列表转本地路径,过滤非图扩展名 (.png/.jpg/.jpeg/.webp/.bmp) ImageGenTab.qml: - import QtQuick.Dialogs 引入 FileDialog (现代 QML6 module) - "添加图片" → addImageDialog.open() (multi-select 图片) - "
柴进 committed
粘贴图片" → imageGen.pasteFromClipboard(),无图时状态变橙色"剪贴板没有图片"
- 拖拽区 DropArea 接 text/uri-list,containsDrag 时边框变 accent + 底色变 accentSubtle
drop 后调 imageGen.normalizeFileUrls(drop.urls) 拿本地路径
- 已选图缩略图 Flow:
ScrollView + Repeater 96×96 圆角缩略图 + 右上角红色 × 删除按钮
- addRefPath/addRefPaths/removeRefAt 三个辅助函数处理状态去重和增量
未做(task #14c/d):
- 提示词收藏 / 删除 saved_prompts 持久化
- 下载图片 + 双击预览打开系统查看器
视觉验证:QML_AUTO_LOGIN=1 启动主窗口,参考图区按钮全 enabled,UI 无回归。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> -
ImageGenTab.qml wiring 改造: - 加状态 properties: refImages / currentTaskId / lastResultPath / statusText / statusColor - TextArea 取 placeholder,删硬编码 "风景画" 占位 - 模式/宽高比/尺寸 三个 ComboBox 给 id,submit() 时取 currentText - 操作行 "生成图片" PrimaryButton onClicked → tab.submit() - submit 内部空 prompt 阻断 + 重复点击防护 + 调 imageGen.submitTask - 任务进行中按钮文字 "生成中…",结束自动恢复 - Connections 监听 imageGen 4 个信号: - taskSubmitted → "● 已提交" - taskProgress → "● 正在生成…" - taskCompleted → 拿 resultPath 更新 lastResultPath,"● 已完成" - taskFailed → "● <error>",红色 - 预览 Rectangle 内嵌 Image (file:/// + lastResultPath, cache:false 强制重读) - 无图片时显示占位文字,有图时直接展示 未做(task #14b/c/d 后续 commit): - 添加图片 / 粘贴图片 按钮(QFileDialog + QClipboard,桥要补 Slot) -
柴进 committed
收藏 + 删除 saved_prompts 持久化
- 下载图片 按钮 / 双击预览打开系统查看器
视觉验证:QML_AUTO_LOGIN=1 启动主窗口,所有控件渲染无回归。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> -
LoginScreen.qml wiring 改造: - appState.login → auth.login (走 DatabaseManager.authenticate / pymysql) - 删 || "demo" 兜底,密码空时 UI 阻断 - submitting 状态:登录中 username/password/checkbox/button 全 disabled,按钮文字 "登录中…" - 监听 auth.loginFailed → errorLabel 红字展示后端返回的具体原因 ("用户名或密码错误" / "无法连接到服务器" 等,DatabaseManager 已统一中文化) - usernameField 不再硬编码 "chaijin",改 placeholder 冒烟测试(直接调 Python 测真 db): - chaijin/wrongpwd → False,loginFailed 信号"用户名或密码错误" - chaijin/a160827 → True,loggedIn=True,currentUser='chaijin' QML 启动登录页渲染正常(task13_login.png 已验证)。 注: - "记住用户名/密码" checkbox UI 保留但暂未持久化,task #18 系统集成时接 config_util 落盘 - pymysql 同步阻塞主线程最多 5s(connect_timeout),暂不影响 UX; 若 db 慢成痛点 task #18 时再改 worker 异步 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>柴进 committed -
bridges/ auth.py AuthBridge login/logout + currentUser/loggedIn (PoC 模式接受任意非空) imagegen.py ImageGenBridge submitTask + 信号转发 (TaskQueueManager → QML) history.py HistoryBridge 暴露 HistoryListModel + refresh/getItem/deleteItem taskqueue.py TaskQueueBridge 自带 _TaskListModel (QAbstractListModel + roleNames) jewelry.py JewelryBridge 词库增删 + previewPrompt _icons.py build_placeholder_icon (旧 ImageGeneratorWindow.create_placeholder_icon 是实例方法,桥层独立一份) main_qml.py 改造: - 顶部加载 config.json + 实例化 core 业务 (HistoryManager / JewelryLibraryManager / TaskQueueManager) + 启 audit_logger(有 db_config 时) - 5 个桥通过 setContextProperty 注入:appState / auth / imageGen / history / taskQueue / jewelry - imageGen.taskCompleted → history.addNew(timestamp) 串起新生成图自动入历史 - AppState 保留作 currentTab 等 UI 状态(task #13 后 loggedIn 字段删,QML 改用 auth.loggedIn) 冒烟测试: - 5 桥全 import OK,依赖注入构造 OK - PoC 模式 login('test','x') 通过 / login('','') 拒绝 - history.refresh() 加载 2 条历史成功 - jewelry.previewPrompt({...}) 正确组装中文 prompt - QML PoC 启动,登录页 + 主窗口(QML_AUTO_LOGIN=1)渲染完整无视觉回归 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>柴进 committed -
5082 行的怪兽塞了 12 个类(数据库/历史/Worker/词库/UI),现在按职责拆开: core/paths.py get_app_data_path + save_png_with_validation + 启动迁移 core/database.py DatabaseManager + hash_password core/history.py HistoryItem + HistoryListModel + HistoryManager core/generation.py ImageGenerationWorker + Gemini 模型常量 core/jewelry.py DEFAULT_JEWELRY_LIBRARY + JewelryLibraryManager + PromptAssembler image_generator.py 5082 → 3781 行,剩下全是 QWidget UI 类 (LoginDialog / DraggableThumbnail / DragDropScrollArea / ImageGeneratorWindow / StyleDesignerTab + utils + main),task #19 QML 全量切换后整体删除。 外部消费者改 import: task_queue.py / temp_clean.py: from image_generator → from core.generation image_generator.py 顶部 from core.* 引入,LoginDialog 等内部代码无感知 冒烟测试:image_generator/task_queue/core 各自 import 通过,类身份正确。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
柴进 committed -
- Main.qml 登录态 height 880 → 940, minimumHeight 760 → 820 - 参考图 Card preferredHeight 170 → 230, minimum 160 → 200 - 视觉:参考图 / 提示词+设置 / 预览 ≈ 230 / 290 / 240,比例协调 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
柴进 committed -
- 参考图 Card preferredHeight 200 → 170 - 提示词+设置行 preferredHeight 320 → 290 - 预览 Card minimumHeight 200 → 240(拿剩余空间,视觉更舒展) - AppState: QML_AUTO_LOGIN=1 时默认登录态,调试时不必每次重启都过登录页 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
柴进 committed -
- 参考图 Card preferredHeight 240 → 200 - 提示词+生成设置行 preferredHeight 360 → 320 - 生成设置卡内 spacing 16 → 12, group 内 6 → 4 - caption "生成模式/宽高比/图片尺寸" 完整显示, 预览卡占满底部 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
柴进 committed -
新分支 feat/ui-qml-poc 从 master 起,独立于 feat/ui-redesign-apple-theme。 PySide6 + QtQuick 路线,业务后端不动,只换 UI 层。 文件: - qml_poc/main_qml.py 入口(QQmlApplicationEngine + AppState QObject) - qml_poc/qml/Main.qml ApplicationWindow,按 loggedIn 切换尺寸 + 子页 - qml_poc/qml/Theme.qml Singleton 设计令牌(24 色 + 尺寸 + 字号 + 跨平台字体栈) - qml_poc/qml/qmldir 模块声明(singleton Theme) - qml_poc/qml/LoginScreen.qml 登录页(标题 + 副标题 + 输入 + 复选 + pill 按钮 + 回车提交) - qml_poc/qml/MainWindow.qml 主窗口(下划线式 TabBar + 任务队列 sidebar + StackLayout) - qml_poc/qml/ImageGenTab.qml 图片生成 tab UI(参考图卡 + 提示词卡 + 生成设置卡 + 操作 + 预览) - qml_poc/qml/components/ Card / PrimaryButton / SecondaryButton / ThemedTextField / ThemedComboBox / CaptionLabel UI 改进: - caption + combo 用 ColumnLayout 6px 间距;Card 内 12px spacing - TabBar 改下划线式(不是凸起 tab) - 主按钮 pill 圆角 980 + Apple Blue + ColorAnimation 120ms hover/pressed - 输入框焦点 2px 蓝边动效 - 复选框 Apple Blue 实色 + 白色 ✓ - 卡片圆角 12px + 1px 极淡边框 跨平台:QtQuick 跨 Mac/Windows/Linux;字体栈 Qt.platform.os 自动选 SF Pro/Segoe UI。 后续:业务桥层 + 4 个 tab 业务接入 + 打包 + 切主入口(任务清单 #12-#19)。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
柴进 committed
-
- 27 Apr, 2026 3 commits
-
-
继上次只修 hot path 之后, 把残留的所有冷热路径都改成 raw json 直接操作, 彻底消灭 load_history_index 内的 stat 循环. 修改: 1. _migrate_paths_once: 启动时一次性路径归一化, 抽样 5 条 60% 阈值 决定是否需要全量迁移, 之后所有路径都是当前 base_path 下的有效绝对路径 2. load_history_index: 简化为 raw read + from_dict + sort, 0 stat N=513 时只需几 ms (旧版 ~60ms) 3. delete_history_item: 改 raw json filter 4. _cleanup_old_records: 改 raw json sort + slice 5. 删除已无调用方的 _save_history_index / _fix_history_path 至此 hot path (生成完成, 点击历史项) 与 cold path (删除, 启动清理) 都不再触发 N 次 stat. 唯一保留全量读的 load_history_index 也只有 refresh_history 一处真实调用方.
柴进 committed -
第二阶段发布后用户日志显示: 启动 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
-
- 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
-
- 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
-
- 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
-
- 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
-
- 02 Mar, 2026 1 commit
-
-
柴进 committed
-
- 28 Feb, 2026 5 commits