Commit Graph
94 Commits
Author SHA1 Message Date
adminandClaude Opus 4.8 093e8f7f90 feat(ai-outfit): apply approved relayout to the panel (§19.3)
- 数据源/输出 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>
2026-06-22 11:08:25 +08:00
adminandClaude Opus 4.8 8e6c347d91 docs(ai-outfit): relayout AI panel mockup (3-col settings, model on right)
- 生成设置 -> 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>
2026-06-22 11:01:23 +08:00
adminandClaude Opus 4.8 d4082ec477 feat(ai-outfit): auto-append output requirements to prompt (§19.6)
- 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>
2026-06-22 10:40:28 +08:00
adminandClaude Opus 4.8 031c420f33 docs(ai-outfit): spec auto-appended output requirements (§7.1 / §19.6)
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>
2026-06-22 10:35:11 +08:00
adminandClaude Opus 4.8 b122bd20b1 docs(ai-outfit): add ai_models.json sample (placeholder keys)
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>
2026-06-22 10:13:49 +08:00
adminandClaude Opus 4.8 d75b228303 fix(ai-outfit): stop combos from clipping the left column (§10.1)
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>
2026-06-22 10:08:36 +08:00
adminandClaude Opus 4.8 7dc907eb78 docs(ai-outfit): record left-column clipping bug + analysis (§10.1)
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>
2026-06-22 10:04:24 +08:00
adminandClaude Opus 4.8 11dd062f45 feat(ai-outfit): two settings per row in the right column
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>
2026-06-22 09:53:15 +08:00
adminandClaude Opus 4.8 db28a39ebc feat(ai-outfit): inline prompt preview + move settings to right (layout A)
- 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>
2026-06-22 09:49:21 +08:00
adminandClaude Opus 4.8 d79f881ec7 docs(ai-outfit): inline prompt preview + settings on right (layout A)
- 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>
2026-06-22 09:35:52 +08:00
adminandClaude Opus 4.8 572c2cf6dd feat(ai-outfit): drop product_id from prompt UI + default template
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>
2026-06-22 09:17:28 +08:00
adminandClaude Opus 4.8 8a4ebe685b docs(ai-outfit): drop product_id from prompt, keep only insert-title
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>
2026-06-22 09:16:04 +08:00
adminandClaude Opus 4.8 1526254486 feat(ai-outfit): AI 穿搭 UI tab (§19.3) + config plumbing (§19.4)
- 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>
2026-06-22 08:52:37 +08:00
adminandClaude Opus 4.8 2d408d9593 docs: replace 使用说明.docx with cmbot自动化工具.docx
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 08:38:05 +08:00
adminandClaude Opus 4.8 5ca615f7ae fix(ai-outfit): per-resolution timeout + _to_int default fallback
- §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>
2026-06-18 18:03:52 +08:00
admin 5b57a4d39a feat: add outfit batch runner 2026-06-18 17:52:05 +08:00
admin 28fcc018bc feat: add outfit generation core 2026-06-18 17:43:35 +08:00
admin fe1dc7778e feat: add ai image service 2026-06-18 17:40:56 +08:00
admin 10238d3da3 feat: add outfit excel service 2026-06-18 17:35:00 +08:00
admin 382797cfea feat: add outfit task models 2026-06-18 17:30:18 +08:00
admin 5fb2f1c8e0 chore: add ai outfit dependencies 2026-06-18 17:26:16 +08:00
adminandClaude Opus 4.8 a7db9444bd docs: track AI outfit module in tasks.md (§19)
Promote AI 穿搭 from the deferred list (§18) to an active §19: design
+ mockup done (19.0), implementation staged per docs/11 §14 (19.1-19.5).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 17:13:34 +08:00
adminandClaude Opus 4.8 31733ed9c9 docs: add AI outfit UI mockup, align style with print module
- New docs/ui-ai-outfit.html + ui-ai-outfit.png: 「2 AI 穿搭」page
  mockup, same chrome/palette/components as docs/ui-v1 (blue-only).
- Center = recent-results thumbnail strip (finished outputs only) +
  detail table; dropped the live single-image preview to avoid the
  concurrent "which row" ambiguity.
- §7: 保存话术 persists template via config_service; 预览最终提示词 is a
  button opening a read-only QDialog rendered from a sample row.
- §8: concurrency is Python threads (ThreadPoolExecutor); Qt only
  signals back to the main thread to refresh UI; default 1 for API limit.
- §10: three-column layout, results-strip rationale, settings 3-col.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 17:11:56 +08:00
adminandClaude Opus 4.8 c82f870910 docs: add AI outfit module design (docs/11-ai-outfit.md)
Design for the "2 AI 穿搭" tab: Excel as the (fixed) data source → convert rows
to OutfitTask → garment image + prompt → AI image API → person-wearing-clothes
JPG → write result path back to Excel. Ports the old project's API client /
batch / retry into cmbot's app/core/services layering, reusing config/log/
~/.cmbot/output. Also fixes the 旧ai穿搭项目.md §6 recommendation (keep Excel).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 16:30:26 +08:00
admin 0afceba79c feat: add vision prompt script 2026-06-18 15:21:49 +08:00
adminandClaude Opus 4.8 83d0f37d3e docs: add user manual (使用说明.docx) + generator script
scripts/gen_user_manual.py builds docs/使用说明.docx with python-docx (re-run to
regenerate). Covers install/launch, UI overview, workflow, materials, preview
fine-tuning, templates, batch modes/queue, export structure, settings + online
update, data locations, and FAQ.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 14:39:29 +08:00
adminandClaude Opus 4.8 421ae78035 feat: default export dir to <install root>\合并后的图片 when packaged
get_output_dir() now returns <install root>\合并后的图片 (parent of app\) for
frozen builds — next to Launcher.exe, easy to find, not replaced on update.
Falls back to <data_dir>/output if the install root is read-only; dev mode
unchanged. The export panel already uses get_output_dir(), so its default
follows automatically.

- tests/test_file_service.py: +3 (dev, frozen install-root, fallback)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 14:29:09 +08:00
adminandClaude Opus 4.8 3c4617a27b docs: default export dir to <install root>\合并后的图片
Default output moves from ~/.cmbot/output (buried in the user profile) to a
合并后的图片\ folder next to Launcher.exe — easy to find and not replaced on
update (NOT under app\, which is swapped each update). Falls back to
~/.cmbot/output if the install root is read-only; user can still pick any dir.
docs/10 §4/§5, docs/02 config, docs/07 §7.5; tasks 17.22.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 14:26:04 +08:00
adminandClaude Opus 4.8 0795e270b4 feat: new-version hint text left of the gear (replaces red button)
Show a low-key blue, clickable "发现新版本 vX.Y.Z" label just left of the
⚙ 配置 button when an update is found (hidden otherwise), instead of recoloring
the button red. Matches docs/07 §4.3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 14:11:30 +08:00
adminandClaude Opus 4.8 954914a8af docs: update indicator is a hint text, not a red button
Replace the red-dot-on-the-gear treatment with a low-key blue "发现新版本
vX.Y.Z" label to the left of the ⚙ 配置 button (red reads as error; the text
is clearer and shows the version). docs/07 §4.3, docs/10 §12 and related
wording updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 14:02:14 +08:00
adminandClaude Opus 4.8 dc2cd123fc docs: align update design with non-blocking two-phase model
Launcher only applies a staged update + launches (no download at startup);
downloading happens in the app's settings dialog. docs/10 §3/§6/§8/§12/§16/§18
rewritten for the two-phase flow (installer.download_and_stage / apply_staged);
docs/07 §4.3/§4.4 cover the gear dot + "检查并更新" button; tasks 17.21.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 12:03:23 +08:00
adminandClaude Opus 4.8 5a1a004daf feat: non-blocking updates — download in app, apply at launch
Startup no longer blocks on a download. New split:
- services/installer.py: download_and_stage() (runs while the app is open →
  staging\app.new, verified) and apply_staged() (launcher swaps it into app\
  at next launch, when the exe isn't locked).
- launcher.py: slimmed to seed + apply_staged + launch; no network at startup.
- main_window: a new version lights a dot on the ⚙ 配置 button instead of a
  blocking banner; the old open-folder banner is removed.
- settings_dialog: "检查并更新" downloads + stages, then "下次启动生效".

Verified end-to-end over a local HTTP server (download→stage→apply); 101 tests
pass (test_installer +11, test_launcher rewritten).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 11:58:06 +08:00
adminandClaude Opus 4.8 30814b6bd6 docs: reconcile tasks.md with actual state
Tick §17.1–17.6 (persistence, independent resets, button dedup, relocate/rename,
template delete, compact layout — all implemented earlier). Clean up §18: move
delivered online-update/distribution out of the backlog; add stage-4 (mandatory/
retention) as the remaining update work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 11:37:17 +08:00
adminandClaude Opus 4.8 56f2767ca0 chore: preset factory update source in default config (internal use)
Ship update_source=http://cm.xiapi.com with the internal read account so a
fresh install is preconfigured (seeded into ~/.cmbot on first run). Plaintext
credentials are acceptable for internal LAN use per project decision.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 11:12:30 +08:00
adminandClaude Opus 4.8 2d2c6c839d feat: settings dialog for update config (gear entry)
A low-key "⚙ 配置" button at the right end of the flow-tab row opens a settings
dialog to edit update_source / update_user / update_pass, with a test-connection
button that distinguishes "reachable & up to date" from "cannot connect".

- settings_dialog.py: pure-UI QDialog; password mask + show; test runs off the
  UI thread via load_manifest and reports the result
- update_service: add load_manifest() (raises on failure, unlike check_for_update)
- main_window: gear button, _open_settings persists via save_config and re-runs
  the update check; _update_found connected once to avoid duplicate banners
- tests: +2 for load_manifest

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 11:09:29 +08:00
adminandClaude Opus 4.8 295dc803c6 chore: retire PowerShell update.ps1 and install_local.ps1
Superseded by src/launcher.py (Launcher.exe) + the portable layout from
build.ps1. The PS prototype validated the flow; removed to avoid confusion
with the old %LOCALAPPDATA% installer. docs/10 §16 and tasks 17.19 updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 11:05:06 +08:00
adminandClaude Opus 4.8 3327276c9a docs: settings dialog for update config (gear entry, not a numbered tab)
A low-key "⚙ 配置" button at the right end of the flow-tab row (separated by
stretch, not a numbered step) opens a settings dialog to edit update_source /
update_user / update_pass, with a test-connection check. Dialog is pure UI;
main_window owns save_config. docs/07 §3/§4.1/§4.3/§4.4; tasks 17.20.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 11:03:36 +08:00
adminandClaude Opus 4.8 ac428fb5ec feat: build.ps1 produces Launcher.exe + portable layout
- build a lean onefile Launcher.exe from src/launcher.py (stdlib-only, no
  PySide6/Pillow) alongside the onedir app build
- release dir is now the portable layout: Launcher.exe + app\ (CMBot.exe +
  config + version.txt)
- emit two zips: CMBot-<ver>.zip (self-update payload = app\ contents, what
  manifest.url points to) and CMBot-<ver>-portable.zip (initial install)
- launcher: harden logging setup for --windowed builds / read-only roots

Actual PyInstaller build must run on Windows; script is syntax-verified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 10:56:53 +08:00
adminandClaude Opus 4.8 1d8e4fc240 feat: src/launcher.py — Python update launcher (HTTP, app/app.old swap)
Launcher core that reuses services/update_service: seed default config into
~/.cmbot on first run, check the manifest, download the release zip, verify
SHA-256, extract, validate version, swap into app/ (keeping app.old/ for
rollback), then launch app/CMBot.exe. Degrades to the local version on any
failure; checks install-root writability before updating.

- update_service: UpdateInfo gains sha256/size/min_supported; release URL
  resolved to absolute; extract make_auth_header + add download() helper
- tests/test_launcher.py: 11 tests (seed, find-root, swap, sha mismatch,
  no-update); also verified end-to-end against a local HTTP server

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 10:49:45 +08:00
adminandClaude Opus 4.8 3d01bed98c feat: get_data_dir() three-tier fallback (env -> ~/.cmbot -> dev root)
Packaged builds now resolve the data root to ~/.cmbot (%USERPROFILE%\.cmbot)
instead of the program dir, so user config/templates/output stay writable and
per-user regardless of where the program is installed or whether it is launched
via Launcher.exe (docs/10-lan-update.md §5). CMBOT_DATA_DIR still overrides.

- tests/test_file_service.py: 5 tests covering env override, frozen->~/.cmbot,
  dev->project root, blank-env handling

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 10:39:52 +08:00
adminandClaude Opus 4.8 0c9415fbcd docs: pivot launcher to Launcher.exe + portable layout + ~/.cmbot data
- Launcher is a compiled Python/PyInstaller Launcher.exe (retires update.ps1
  and install_local.ps1)
- portable: extract to any writable dir; InstallRoot = Launcher.exe's folder,
  no longer tied to %LOCALAPPDATA%
- user data moves to ~/.cmbot (%USERPROFILE%\.cmbot): always writable, per-user,
  survives program updates; get_data_dir() three-tier (env -> ~/.cmbot -> dev root)
- writability check now applies to the program dir only; first-run seeds default
  templates from app\config into ~/.cmbot
- docs/10 §3/§4/§5/§8/§9/§11/§16 updated; tasks 17.19 added (docs done, code TODO)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 10:37:48 +08:00
adminandClaude Opus 4.8 54e42670f7 fix: tolerate UTF-8 BOM in manifest; sync docs to app/app.old model
#1 BOM: PowerShell Set-Content -Encoding UTF8 writes a BOM that made the in-app
json.loads silently fail (banner never showed even with an update available).
- update_service: decode manifest with utf-8-sig (HTTP + local)
- build.ps1: write manifest.json without BOM (UTF8Encoding $false)
- tests: +2 covering BOM manifests (HTTP + local)

#2 docs: docs/10 §16 status synced to the implemented app/app.old/version.txt +
SHA-256 model (stages 2/3 done + e2e verified); tasks 17.18 updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 10:19:03 +08:00
admin ba8daaccdd feat: add local launcher installer 2026-06-18 10:04:29 +08:00
admin 757056b462 docs: mark update release script complete 2026-06-18 09:45:35 +08:00
admin cdfd684894 feat: generate update release manifest 2026-06-18 09:45:08 +08:00
admin 8944676ad6 docs: update http update task status 2026-06-18 09:43:23 +08:00
admin 6aae8d1bd6 feat: update launcher over http 2026-06-18 09:42:45 +08:00
admin 813e569639 feat: support http update checks 2026-06-18 09:38:44 +08:00
admin 6bc27f5180 chore: add update auth config defaults 2026-06-18 09:36:19 +08:00
admin 622159badd docs: update online update strategy 2026-06-18 09:25:42 +08:00