Panel had no stylesheet -> all buttons rendered native gray. Add
setObjectName('aiOutfitPanel') + _apply_styles() with QSS scoped to
#aiOutfitPanel (so dialogs keep native buttons): primary blue for 开始生成
(aiStartBtn, mirrors 开始批量导出/打开文件夹), quiet red for 停止生成 and the 话术
删除 (aiStopBtn/aiPromptDeleteBtn, mirrors 删除), secondary gray for the rest.
docs/11 §10.5 + tasks §19.11. Offscreen grab verified; full suite (12) green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- _on_finished(total==0) now shows _show_no_pending_message: counts 完成/失败
via read_all_rows and tells the user how to redo (clear E column / retry
failed), instead of the silent '完成 0,失败 0'.
- _on_tasks_loaded no longer calls _fill_sample_combo(tasks), so generating on
an all-完成 sheet no longer empties the preview dropdown (read_all_rows owns it).
- Offscreen-verified both paths; full suite (12 files) green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
After a whole sheet is 完成, clicking 开始生成 silently shows '0 待处理 /
完成 0' and also re-empties the preview sample dropdown. Document two fixes:
(1) a clear 'no pending rows' message (N done skipped / M failed retryable /
clear E column to redo), (2) _on_tasks_loaded must not reset the preview combo
(read_all_rows owns it). Add §10.4 + §19.10.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add excel_service.read_all_rows (every valid data row, status-independent) and
use it in _reload_sample_rows for the preview dropdown, so previewing still
works after the whole sheet is 完成. Generation still uses load_outfit_tasks.
+1 excel test (read_all_rows includes 完成/失败, skips incomplete); offscreen
all-完成 sheet now yields sample rows; full suite (12) green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bug: after a whole Excel finishes, re-selecting it shows an empty preview
sample-row dropdown because _reload_sample_rows uses load_outfit_tasks (which
skips 完成 rows). Preview should list all valid data rows regardless of status.
Document the cause + fix (new excel_service.read_all_rows for preview; generate
still uses load_outfit_tasks). Update §7 + add §10.3 / §19.9.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Upgrade the single prompt to multiple named templates (switch/new/save-as/
rename/delete/save, remember last). Decisions: all-custom (seed one 默认),
dirty-check confirm on switch, include rename. Storage: outfit_prompts.json +
outfit_prompt_name in app_config, migrate the old outfit_prompt.txt. Add §7.2
design + §19.8 task (not yet implemented).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add last_tab to DEFAULT_CONFIG; main_window restores it in _restore_preferences
(only enabled/implemented tabs 0/1; setCurrentIndex before connecting the persist
slot to avoid an echo write) and persists via _on_tab_persist on currentChanged.
Mirrors the existing last_template/last_batch_mode pattern. Document the key in
docs/02 §9. Offscreen-verified (restore 0/1, disabled 2 -> 0, persist); suite green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Connect QComboBox.activated (user-only) on the 分辨率 and AI 模型 dropdowns to
an info-only QMessageBox: resolution shows the per-resolution timeout +
next-run-only; model shows its api_type + billing/effect caveat + next-run-only.
Track last value so re-selecting the same item doesn't pop; programmatic sets
(apply_config/_fill_sample_combo) never pop. Offscreen-verified; suite (12) green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
User switching the 分辨率 or AI 模型 dropdown shows an info-only QMessageBox
(no block, no revert) about the impact: resolution -> per-resolution timeout +
next-run-only; model -> api_type + billing/effect may differ + next-run-only.
Trigger via QComboBox.activated (user-only) so programmatic sets don't pop.
Add §10.2 design + §19.7 task (not yet implemented).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 数据源/输出 become inline label+path+browse rows (_inline_path_row);
removed the left group boxes to free vertical space.
- 通用话术 + 最终提示词预览 enlarged and stretch with window height.
- 生成设置 grid -> 3 columns (2 rows); AI 模型 dropdown moved under it in the
right column. Splitter sizes left360/center/right400; right minWidth 360.
- Verified offscreen against the mockup (real Qt grab); full suite (12) green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 生成设置 -> 3 cols x 2 rows; AI 模型 dropdown moved under it on the right.
- 数据源/输出 become inline label+path rows; bigger 通用话术 + 最终提示词预览
(preview now shows the appended output requirements). Right column widened
to ~400 (left ~360). Update §10 and re-render ui-ai-outfit.png.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- core/ai_outfit: OUTPUT_REQUIREMENTS constant + build_output_requirements();
render_prompt(template, task, resolution=None) appends the block when a
resolution is given; generate_outfit_image passes the current resolution.
- ai_outfit_panel: inline preview now uses render_prompt/build_output_requirements
with the selected resolution and refreshes when the resolution combo changes,
so the preview equals what is actually sent.
- tests: +3 cases (tail with resolution, none without, helper empty/filled);
updated the success test for the appended tail. Full suite (12 files) green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Final prompt = user 话术 + an auto-appended 「批量生成输出要求」block ported
from the old tool (参考解析度 dynamic = resolution setting; 固定 1:1 / 结合标题与
参考图 / 不可跑版 fixed). Simplified wording, always appended, preview must
include it. Add §7.1 design + §19.6 task (not yet implemented).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Template covering all four api_type variants (chat/gemini/images/images_edits).
Copy to ~/.cmbot/config/ai_models.json and fill real values; the real file
(with keys) stays out of git.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Constrain the 样本行 and AI 模型 combos (AdjustToMinimumContentsLengthWithIcon
+ minimumContentsLength + Ignored size policy) so long items elide instead of
inflating the column's min width. Left scroll h-policy AlwaysOff -> AsNeeded as
a safety net; trim splitter initial sizes to fit the default window.
Measured: left inner minimumSizeHint 829px -> 200px (viewport ~398), no clip.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Left column content is clipped on the right (looks covered by the center).
Measured offscreen: the left QScrollArea has h-scroll AlwaysOff while its
inner content demands 829px min width vs a ~428px viewport. The driver is the
样本行 QComboBox (minimumSizeHint ~743px from its long '第N行·货号·标题' item),
which inflates the preview group. Documented root cause + fix direction
(constrain combo size policy; left scroll AsNeeded).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the one-per-row QFormLayout with a 2-column QGridLayout (3x2:
并发/间隔 · 冷却/重试 · 分辨率/质量), matching the mockup's 2-col 生成设置.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Drop the prompt-preview dialog; add a live inline preview pinned at the
bottom of the left column: sample-row dropdown + read-only rendered text,
refreshing on prompt edit / sample change, with a missing-{title} hint.
- Sample rows load from the chosen Excel (on browse, on startup from the
remembered path, and from the run's loaded tasks).
- Move 生成设置 out of the left column into the right run column
(_create_settings_group), so left = 数据源 + 话术 + 预览.
- Matches docs d79f881. Full suite green; offscreen window smoke passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Replace the prompt-preview dialog with an inline, live preview pinned at
the bottom of the left column (sample-row dropdown, title substituted).
- Move 生成设置 from the left into the right run column (2-col), so left
becomes a focused 数据源 + 话术 + 预览 authoring column.
- Update §7 (inline preview) and §10 (left/right column contents) + re-render
the mockup.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Match the doc change (8a4ebe6): remove the 插入货号 button (keep only
插入标题) and drop 货号 {product_id} from DEFAULT_OUTFIT_PROMPT. product_id
still drives Excel column B and the output filename, not the AI prompt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Prompt offers only {title}; product_id is used for Excel column B and the
output filename (货号.jpg), not for AI generation. Update §7/§10 and the
mockup (remove the 插入货号 chip + {product_id} from the sample prompt).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- main_window: wrap workflow in a QStackedWidget (page 0 = print, page 1 =
AI outfit); enable tab 2「AI 穿搭」, switch pages on tab change.
- app/widgets/ai_outfit_panel.py: three-column page per docs/11 §10 —
left settings (Excel/output/model/prompt editor+save+insert+preview
dialog/batch options), center (recent-results thumbnails + detail
table), right (progress/stats/start/stop/export failures/log).
- Threading: QThread + _OutfitWorker(QObject) wraps OutfitBatchRunner;
queued signals refresh UI, each row written back to Excel on the worker
thread; finish summary + failure-list CSV export.
- config_service: load_ai_models()/load_outfit_prompt()/save_outfit_prompt()
+ outfit_* keys in app_config; panel persists via config_changed signal.
- tests/test_config_service.py: 8 cases for the new helpers. Full suite
(12 files) green on Python 3.7; offscreen MainWindow smoke test passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- §8 dynamic timeout: add resolution_timeout() (512/1K/2K/4K ->
180/240/360/600s); generate() uses it for the POST and image download.
timeout_seconds now defaults to 0 = auto-by-resolution; an explicit
positive value overrides. Validation allows 0, rejects negatives.
- _to_int now returns the supplied default on parse failure (was 0), so a
bad connect_timeout_seconds falls back to 30 instead of failing as 0.
- Tests: +5 (resolution map, auto vs override timeout, connect fallback).
- tasks.md §19.1/§19.2: tick unit-test item, note runner is pure-logic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>