✨ feat(config): 更新模型配置与LLM提示词指南

- 将默认LLM模型从gemini-2.0-flash升级为gemini-3-flash-preview
- 将博主人设从"性感福利主播"更改为"二次元coser"
- 优化LLM生成SD提示词的指南,新增中国审美人物描述规则
- 为各SD模型添加颜值核心词、示范prompt和禁止使用的关键词
- 新增三维人物描述法(眼睛/肤色/气质)和专属光线词指导

📦 build(openspec): 归档旧规范并创建新规范

- 将improve-maintainability规范归档至2026-02-25目录
- 新增2026-02-26-improve-ui-layout规范,包含UI布局优化设计
- 新增2026-02-26-optimize-image-generation规范,包含图片生成优化设计
- 在根目录openspec/specs下新增图片质量、后处理、中国审美和LLM提示词规范

♻️ refactor(sd_service): 优化SD模型配置和图片后处理

- 为各SD模型添加中国审美特征词和欧美面孔排除词
- 新增高画质预设档,SDXL模型启用Hires Fix参数
- 将后处理拆分为beauty_enhance和anti_detect_postprocess两个独立函数
- 新增美化增强功能,支持通过enhance_level参数控制强度

♻️ refactor(services): 更新内容生成服务以支持美化增强

- 在generate_images函数中新增enhance_level参数
- 将美化强度参数传递至SDService.txt2img调用

♻️ refactor(ui): 优化UI布局和添加美化强度控件

- 注入自定义CSS主题层,优化字体、按钮和卡片样式
- 将全局设置迁移至独立的"⚙️ 配置"Tab,优化Tab顺序
- 在内容创作Tab的高级设置中添加美化强度滑块控件
- 优化自动运营Tab布局,改为2列卡片网格展示
This commit is contained in:
2026-02-26 22:58:05 +08:00
parent b635108b89
commit b5deafa2cc
53 changed files with 1578 additions and 387 deletions
@@ -0,0 +1,35 @@
## ADDED Requirements
### Requirement: 各 SD 模型 prompt_prefix 包含中国审美面孔特征词
系统 SHALL 在 `SD_MODEL_PROFILES` 每个模型的 `prompt_prefix` 中包含以下中国审美特征词(具体权重值由各模型风格微调,但不得省略):
- 眼型:`(almond eyes:1.2)` 或 `(delicate almond-shaped eyes:1.2)`
- 肤色:`(porcelain skin:1.2)` 或 `(fair porcelain skin:1.2)` 或 `(luminous fair skin:1.2)`
- 五官精致感:`(delicate facial features:1.2)` 或 `(refined features:1.2)`
- 气质:`(youthful appearance:1.1)` 或 `(elegant temperament:1.1)`
#### Scenario: 生成的人物图像呈现中国大众偏好的精致感
- **WHEN** 使用任意 SD 模型生成含人物的图片
- **THEN** `prompt_prefix` 中包含杏眼、白皙肤色、精致五官三类词汇中各至少一个
### Requirement: 各 SD 模型 negative_prompt 补充欧美面孔排除词
系统 SHALL 在 `SD_MODEL_PROFILES` 每个模型的 `negative_prompt` 中包含以下欧美面孔特征排除词(在现有基础上补充):
- `strong jawline`、`prominent brow ridge`、`deep-set eyes`(如已存在则保留权重)
- `angular facial structure`、`square jaw`、`heavy brow`
#### Scenario: negative_prompt 明确排除欧美面孔结构特征
- **WHEN** 任意模型进行人物图片生成
- **THEN** negative_prompt 中包含 `strong jawline`、`prominent brow ridge`、`deep-set eyes` 至少两个排除词
### Requirement: PERSONA_SD_PROFILES 各人设包含中式气质差异化增强词
系统 SHALL 在 `PERSONA_SD_PROFILES` 中,每个人设的 `prompt_boost` 包含至少 1 个与中国审美相关的气质词,且各人设之间的气质词须有差异化区分(如:甜妹-减龄感、知性-优雅气质、赛博博主-精致感 + 未来感)。
#### Scenario: 不同人设生成的人物具有可感知的气质差异
- **WHEN** 分别以"甜妹"和"知性"人设各生成一批图片
- **THEN** 两批图片中 `prompt_boost` 的核心气质词不重复,体现差异化风格
### Requirement: 美化增强对肤色偏向做修正
`beauty_enhance()` 函数 SHALL 在饱和度提升时对色调偏暖方向微调(暖白/自然肤色),避免饱和度提升导致肤色偏黄或偏红。具体实现通过调整 PIL 色调(Hue)向暖白方向 ±5° 以内微调。
#### Scenario: 美化后肤色不发黄不发红
- **WHEN** 调用 `beauty_enhance(img, level=1.0)` 对含人物的图片处理
- **THEN** 输出图片肤色区域饱和度提升的同时,色调保持在自然暖白范围内(目视无偏黄/偏红现象)
@@ -0,0 +1,38 @@
## ADDED Requirements
### Requirement: beauty_enhance 作为独立美化增强函数
系统 SHALL 在 `sd_service.py` 中提供 `beauty_enhance(img: Image.Image, level: float = 1.0) -> Image.Image` 函数,支持以下增强操作(所有操作的强度随 `level` 线性缩放):
- 智能锐化(基于 `ImageFilter.UnsharpMask`,强调五官轮廓与发丝细节)
- 亮度与对比度微增(`level=1.0` 时各 +2-3%,`level=2.0` 时各 +4-6%)
- 饱和度提升(`level=1.0` 时 +5%,`level=2.0` 时 +10%,令肤色更均匀饱满)
- `level=0` 时 SHALL 直接返回原图,跳过所有处理
#### Scenario: 正常调用增强管线
- **WHEN** 调用 `beauty_enhance(img, level=1.0)`
- **THEN** 返回经过锐化、亮度微调、饱和度提升处理的 PIL Image,图片尺寸不变
#### Scenario: level=0 时跳过处理
- **WHEN** 调用 `beauty_enhance(img, level=0)`
- **THEN** 直接返回原始 img 对象,不执行任何增强操作
#### Scenario: level=2 时增强效果加倍
- **WHEN** 调用 `beauty_enhance(img, level=2.0)`
- **THEN** 锐化、亮度、饱和度的调整幅度均为 level=1.0 时的 2 倍
### Requirement: 后处理管线顺序为美化先于反 AI 扰动
系统 SHALL 在 `txt2img` 和 `img2img` 生成流程中,对每张输出图片依次执行:`beauty_enhance(img, level) → anti_detect_postprocess(img)`,确保美化增强在扰动引入之前完成。
#### Scenario: 生成图片经过完整两阶段后处理
- **WHEN** `txt2img` 成功生成图片
- **THEN** 每张图片先经过 `beauty_enhance`,再经过 `anti_detect_postprocess`,最终返回给调用方
### Requirement: enhance_level 参数从 UI 传递至后处理管线
系统 SHALL 支持 `enhance_level: float` 参数从 Gradio UI 经 `services/content.py` 的 `generate_images()` 函数传递至 `SDService.txt2img()`,最终传入 `beauty_enhance()`。新参数默认值为 `1.0`,向后兼容。
#### Scenario: UI 美化强度滑块值传递到生成结果
- **WHEN** 用户在"高级设置"中将美化强度滑块调整为 2.0 并点击生成
- **THEN** 生成图片经过 `beauty_enhance(img, level=2.0)` 处理
#### Scenario: 旧调用方不传 enhance_level 时行为不变
- **WHEN** `generate_images()` 未传入 `enhance_level` 参数
- **THEN** 默认使用 `level=1.0`,行为与优化前相同
@@ -0,0 +1,23 @@
## ADDED Requirements
### Requirement: 各 SD 模型新增高画质预设档
系统 SHALL 在 `SD_MODEL_PROFILES` 的每个模型 `presets` 字典中新增 `"高画质 (约5分钟)"` 档位,参数须满足:SD 1.5 模型步数 ≥ 50、CFG 6.5-7.5、采样器为 `DPM++ SDE`;SDXL 模型步数 ≥ 40、CFG 5.5-6.5、采样器为 `DPM++ 2M SDE`,并启用 Hires Fix 参数(`enable_hr: true`)。
#### Scenario: 用户选择高画质档后生成请求包含 Hires Fix
- **WHEN** 用户将质量模式切换为"高画质 (约5分钟)"且当前模型为 SDXL 架构
- **THEN** `txt2img` API 请求 payload 中包含 `enable_hr: true`、`hr_scale: 1.5`、`hr_upscaler` 字段
#### Scenario: SD 1.5 模型高画质档不启用 Hires Fix
- **WHEN** 用户将质量模式切换为"高画质 (约5分钟)"且当前模型 `arch == "sd15"`
- **THEN** payload 中不含 `enable_hr` 字段,以避免 OOM
#### Scenario: Hires Fix upscaler 不存在时降级
- **WHEN** `hr_upscaler` 指定的放大器模型(如 "4x-UltraSharp")在 SD WebUI 中不可用
- **THEN** 系统 SHALL 自动降级为 `"Latent"` 作为 fallback,并通过日志记录警告
### Requirement: 预设名称在 UI 中按模型动态更新
系统 SHALL 在用户切换 SD 模型后,更新 UI 质量模式单选按钮的选项列表,使其始终反映当前模型的可用预设档名称。
#### Scenario: 切换模型后预设列表刷新
- **WHEN** 用户在连接设置中切换 SD 模型
- **THEN** 创作页"生成模式"单选按钮的选项 SHALL 更新为该模型 `presets` 中的键名列表
@@ -0,0 +1,29 @@
## ADDED Requirements
### Requirement: LLM Prompt 指南包含人物真实感描述规则
`get_sd_prompt_guide()` 返回的指南 SHALL 包含"人物描述规则"段落,内容要求:
- 明确禁止使用 `beautiful girl`、`pretty woman`、`good looking` 等无导向泛化词
- 要求从五官(眼型、鼻型、唇型)、肤色(白皙、透亮、均匀)、气质(减龄、温柔、知性)三个维度分别提供具体描述词
- 对于有人物的 Prompt,要求至少包含 2 个五官/肤色词和 1 个气质词
#### Scenario: LLM 生成含人物的 Prompt 时使用规范词汇
- **WHEN** LLM 根据指南生成包含人物描述的 SD Prompt
- **THEN** Prompt 中不含 `beautiful`、`pretty`、`good looking` 等泛化词,而是包含如 `almond eyes`、`porcelain skin`、`youthful appearance` 等具体描述词
### Requirement: LLM Prompt 指南区分人物图与纯场景图的写作策略
`get_sd_prompt_guide()` 返回的指南 SHALL 明确区分两种写作模式:人物主体图(需详细描述人物特征)和纯场景/物品图(无需人物词,优先描述氛围、色彩、光线)。
#### Scenario: 生成场景图 Prompt 时不误加人物词
- **WHEN** 文案主题为室内布置或产品展示(无人物)
- **THEN** LLM 生成的 Prompt 中不包含人物特征描述词
#### Scenario: 生成人物图 Prompt 时包含完整人物描述
- **WHEN** 文案主题为穿搭、美妆或生活场景(含人物)
- **THEN** LLM 生成的 Prompt 中包含五官、肤色、气质三个维度的描述词各至少 1 个
### Requirement: LLM Prompt 指南包含光影与摄影感描述规范
`get_sd_prompt_guide()` 返回的指南 SHALL 包含光影描述规则,要求生成 Prompt 时指定具体光源类型(如 `soft window light`、`golden hour`、`studio lighting`)而非泛化词(如 `good lighting`)。
#### Scenario: Prompt 中包含具体光影词而非泛化词
- **WHEN** LLM 根据指南生成 SD Prompt
- **THEN** Prompt 中包含 `soft window light`、`golden hour`、`studio lighting`、`diffused natural light` 等具体光影词之一
+16
View File
@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 开机自启管理函数迁移至独立模块
系统 SHALL 将开机自启相关常量和函数从 `main.py` 提取至 `services/autostart.py`,包括:`_APP_NAME`、`_STARTUP_REG_KEY`、`_get_startup_script_path`、`_get_startup_bat_path`、`_create_startup_scripts`、`is_autostart_enabled`、`enable_autostart`、`disable_autostart`、`toggle_autostart`。
#### Scenario: 模块导入成功
- **WHEN** `main.py` 执行 `from services.autostart import toggle_autostart, is_autostart_enabled`
- **THEN** 函数可正常调用
#### Scenario: Windows 注册表操作行为不变
- **WHEN** `enable_autostart()` 在 Windows 系统上被调用
- **THEN** SHALL 向注册表 `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` 写入启动项,行为与迁移前完全一致
#### Scenario: 非 Windows 平台处理不变
- **WHEN** `enable_autostart()` 在非 Windows 系统上被调用
- **THEN** SHALL 返回与迁移前相同的平台不支持提示信息
@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 连接管理函数迁移至独立模块
系统 SHALL 将所有 LLM / SD / MCP 连接管理及认证相关函数从 `main.py` 提取至 `services/connection.py`,包括:`_get_llm_config`、`connect_llm`、`add_llm_provider`、`remove_llm_provider`、`on_provider_selected`、`connect_sd`、`on_sd_model_change`、`check_mcp_status`、`get_login_qrcode`、`logout_xhs`、`_auto_fetch_xsec_token`、`check_login`、`save_my_user_id`、`upload_face_image`、`load_saved_face_image`。
#### Scenario: 模块导入成功
- **WHEN** `main.py` 执行 `from services.connection import connect_llm, connect_sd` 等导入
- **THEN** 所有函数可正常调用,行为与迁移前完全一致
#### Scenario: 外部依赖通过参数传入
- **WHEN** `services/connection.py` 中的函数需要访问 `cfg`、`llm`(`LLMService`)、`sd`(`SDService`)、`mcp`(`MCPClient`)
- **THEN** 这些依赖 SHALL 通过函数参数接收,`services/connection.py` 模块顶层不创建单例实例
#### Scenario: 无循环导入
- **WHEN** Python 解释器加载 `services/connection.py`
- **THEN** 不产生 `ImportError` 或循环导入错误
+16
View File
@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 内容生成函数迁移至独立模块
系统 SHALL 将内容生成、图片生成、发布及导出相关函数从 `main.py` 提取至 `services/content.py`,包括:`generate_copy`、`generate_images`、`one_click_export`、`publish_to_xhs`。
#### Scenario: 模块导入成功
- **WHEN** `main.py` 执行 `from services.content import generate_copy, generate_images, publish_to_xhs, one_click_export`
- **THEN** 所有函数可正常调用,行为与迁移前完全一致
#### Scenario: 内容生成保留现有验证逻辑
- **WHEN** 调用 `publish_to_xhs` 时标题超过 20 字或图片数量不合法
- **THEN** 函数 SHALL 返回与迁移前相同的错误提示,不改变验证行为
#### Scenario: 临时文件清理逻辑保留
- **WHEN** `publish_to_xhs` 执行完毕(成功或失败)
- **THEN** `finally` 块中的 AI 临时文件清理逻辑 SHALL 正常执行
@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 互动自动化函数迁移至独立模块
系统 SHALL 将评论、点赞、收藏、回复等互动自动化函数从 `main.py` 提取至 `services/engagement.py`,包括:`load_note_for_comment`、`ai_generate_comment`、`send_comment`、`fetch_my_notes`、`on_my_note_selected`、`fetch_my_note_comments`、`ai_reply_comment`、`send_reply`、`auto_comment_once`、`_auto_comment_with_log`、`auto_like_once`、`_auto_like_with_log`、`auto_favorite_once`、`_auto_favorite_with_log`、`auto_reply_once`、`_auto_reply_with_log`、`_auto_publish_with_log`。
#### Scenario: 模块导入成功
- **WHEN** `main.py` 执行 `from services.engagement import auto_comment_once, auto_like_once` 等导入
- **THEN** 所有函数可正常调用
#### Scenario: 日志回调参数化
- **WHEN** `engagement.py` 中的 `_with_log` 函数需要追加日志时
- **THEN** 函数 SHALL 接收 `log_fn` 参数(callable)用于写入日志,不直接依赖外部 `_auto_log` 列表
#### Scenario: 频率限制集成
- **WHEN** `auto_comment_once` 等函数执行前需要检查每日限额和冷却状态
- **THEN** 通过调用 `rate_limiter` 模块中的函数实现,不在 `engagement.py` 内复制限流逻辑
+12
View File
@@ -0,0 +1,12 @@
## ADDED Requirements
### Requirement: 热点探测函数迁移至独立模块
系统 SHALL 将热点搜索与分析相关函数从 `main.py` 提取至 `services/hotspot.py`,包括:`search_hotspots`、`analyze_and_suggest`、`generate_from_hotspot`、`_set_cache`、`_get_cache`、`_fetch_and_cache`、`_pick_from_cache`、`fetch_proactive_notes`、`on_proactive_note_selected`。
#### Scenario: 模块导入成功
- **WHEN** `main.py` 执行 `from services.hotspot import search_hotspots, analyze_and_suggest` 等导入
- **THEN** 所有函数可正常调用
#### Scenario: 线程安全缓存随模块迁移
- **WHEN** `_cache_lock`(`threading.RLock`)随函数一起迁移至 `services/hotspot.py`
- **THEN** `_set_cache` / `_get_cache` 的线程安全行为保持不变
+16
View File
@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 人设管理函数及常量迁移至独立模块
系统 SHALL 将人设相关的常量和函数从 `main.py` 提取至 `services/persona.py`,包括:`DEFAULT_PERSONAS`、`RANDOM_PERSONA_LABEL`、`PERSONA_POOL_MAP`、`DEFAULT_TOPICS`、`DEFAULT_STYLES`、`DEFAULT_COMMENT_KEYWORDS`、`_match_persona_pools`、`get_persona_topics`、`get_persona_keywords`、`on_persona_changed`、`_resolve_persona`。
#### Scenario: 常量可从模块导入
- **WHEN** `main.py` 执行 `from services.persona import DEFAULT_PERSONAS, PERSONA_POOL_MAP`
- **THEN** 常量值 SHALL 与迁移前完全一致
#### Scenario: 人设解析正确处理随机人设标签
- **WHEN** `_resolve_persona(RANDOM_PERSONA_LABEL)` 被调用
- **THEN** SHALL 返回从人设池中随机选取的人设文本,行为与迁移前一致
#### Scenario: 人设变更回调正常触发
- **WHEN** `on_persona_changed(persona_text)` 被调用
- **THEN** SHALL 返回更新后的话题列表和关键词列表,供 Gradio UI 使用
+12
View File
@@ -0,0 +1,12 @@
## ADDED Requirements
### Requirement: 用户主页解析函数迁移至独立模块
系统 SHALL 将用户主页数据获取与解析相关函数从 `main.py` 提取至 `services/profile.py`,包括:`_parse_profile_json`、`_parse_count`、`fetch_my_profile`。
#### Scenario: 模块导入成功
- **WHEN** `main.py` 执行 `from services.profile import fetch_my_profile`
- **THEN** 函数可正常调用,行为与迁移前一致
#### Scenario: 解析容错性保留
- **WHEN** `_parse_count` 接收到格式异常的数值字符串(如 "1.2万"、"--")
- **THEN** SHALL 返回与迁移前相同的浮点数或 0,不抛出异常
+16
View File
@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 排期队列操作函数迁移至独立模块
系统 SHALL 将内容排期队列相关函数从 `main.py` 提取至 `services/queue_ops.py`,包括:`generate_to_queue`、`_queue_publish_callback`、`queue_refresh_table`、`queue_refresh_calendar`、`queue_preview_item`、`queue_approve_item`、`queue_reject_item`、`queue_delete_item`、`queue_retry_item`、`queue_publish_now`、`queue_start_processor`、`queue_stop_processor`、`queue_get_status`、`queue_batch_approve`、`queue_generate_and_refresh`。
#### Scenario: 模块导入成功
- **WHEN** `main.py` 执行 `from services.queue_ops import queue_generate_and_refresh, queue_refresh_table` 等导入
- **THEN** 所有函数可正常调用
#### Scenario: publish callback 在 main.py 完成注册
- **WHEN** 应用启动时 `main.py` 调用 `pub_queue.set_publish_callback(_queue_publish_callback)`(`_queue_publish_callback` 已迁移至 `queue_ops.py`)
- **THEN** 队列发布回调 SHALL 正常注册并在队列处理时触发
#### Scenario: 队列操作读写 pub_queue 单例
- **WHEN** `queue_ops.py` 中的函数需要访问 `pub_queue` 或 `queue_publisher`
- **THEN** 这些单例 SHALL 通过函数参数传入,不在 `queue_ops.py` 模块顶层初始化
@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 频率控制与每日限额函数迁移至独立模块
系统 SHALL 将频率控制、每日限额及冷却相关的所有状态变量和函数从 `main.py` 提取至 `services/rate_limiter.py`,包括:`_auto_running`、`_op_history`、`_daily_stats`、`DAILY_LIMITS`、`_consecutive_errors`、`_error_cooldown_until`、`_reset_daily_stats_if_needed`、`_check_daily_limit`、`_increment_stat`、`_record_error`、`_clear_error_streak`、`_is_in_cooldown`、`_is_in_operating_hours`、`_get_stats_summary`。
#### Scenario: 模块级状态初始化一次
- **WHEN** Python 首次导入 `services/rate_limiter.py`
- **THEN** `_daily_stats`、`_op_history` 等模块级变量 SHALL 仅初始化一次(Python 模块单例语义)
#### Scenario: 每日限额检查正常工作
- **WHEN** `_check_daily_limit("comment")` 被调用
- **THEN** 返回值 SHALL 与迁移前行为完全一致
#### Scenario: 运营时段限制正常工作
- **WHEN** 当前时间不在 `start_hour` 至 `end_hour` 范围内时调用 `_is_in_operating_hours`
- **THEN** 返回 `False`,阻止自动化操作执行
+16
View File
@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 自动调度器函数迁移至独立模块
系统 SHALL 将调度器相关的状态变量和函数从 `main.py` 提取至 `services/scheduler.py`,包括:`_scheduler_next_times`、`_auto_log`(列表)、`_auto_log_append`、`_scheduler_loop`、`start_scheduler`、`stop_scheduler`、`get_auto_log`、`get_scheduler_status`、`_learn_running`、`_learn_scheduler_loop`、`start_learn_scheduler`、`stop_learn_scheduler`。
#### Scenario: 调度器启停正常工作
- **WHEN** `start_scheduler(...)` 被调用并传入合法参数
- **THEN** 调度器线程 SHALL 正常启动,`get_scheduler_status()` 返回运行中状态
#### Scenario: 日志追加线程安全
- **WHEN** 多个自动化任务并发调用 `_auto_log_append(msg)`
- **THEN** 日志条目 SHALL 正确追加,不丢失和乱序
#### Scenario: engagement 通过回调写日志
- **WHEN** `services/engagement.py` 中的函数需要写日志时
- **THEN** SHALL 通过 `log_fn` 参数(由 `scheduler.py` 传入 `_auto_log_append`)写入,不直接导入 `scheduler.py`
@@ -0,0 +1,20 @@
## ADDED Requirements
### Requirement: 独立配置 Tab 承载全局设置
系统 SHALL 在主 Tab 列表末尾提供「⚙️ 配置」Tab,将 LLM 提供商配置、SD WebUI 配置、ReActor 换脸设置、系统设置(开机自启等)全部组件声明移入该 Tab,从主界面顶部移除 `gr.Accordion("⚙️ 全局设置")` 折叠块。
#### Scenario: 首屏无全局设置折叠块
- **WHEN** 用户打开应用
- **THEN** 主界面顶部 SHALL 不再显示任何折叠区块,直接呈现 Tab 导航栏
#### Scenario: 配置 Tab 包含所有原折叠区内容
- **WHEN** 用户切换到「⚙️ 配置」Tab
- **THEN** Tab 内 SHALL 包含 LLM 提供商 Dropdown、LLM 模型 Dropdown、添加/删除提供商面板、MCP Server URL、SD WebUI URL、连接/检查按钮、SD 模型、博主人设、AI 换脸(头像上传 + 开关)、开机自启开关,功能与原折叠区完全一致
#### Scenario: 跨 Tab 共享组件可正常访问
- **WHEN** 「内容创作」等其他 Tab 需要使用 `llm_model`、`sd_model`、`persona`、`status_bar`、`face_swap_toggle` 等组件
- **THEN** 这些组件 SHALL 在 `gr.Blocks` 上下文中声明,并作为参数传递给各 `build_tab()` 函数,功能不受 Tab 物理位置影响
#### Scenario: 连接状态实时反馈
- **WHEN** 用户在配置 Tab 点击「🔗 连接 LLM」或「🎨 连接 SD」
- **THEN** `status_bar` Markdown 组件 SHALL 实时更新显示连接结果,与原折叠区行为一致
+36 -6
View File
@@ -1,19 +1,49 @@
## ADDED Requirements
### Requirement: 内容创作 Tab 的 UI 代码迁移至独立模块
`ui/tab_create.py` SHALL 包含原 `main.py` 中「内容创作 Tab」的全部 Gradio 组件定义和事件绑定,并导出 `build_tab() -> None` 函数,该函数接受一个 `gr.Blocks` 上下文,在其中构建 Tab 内容。`main.py` SHALL 通过 `from ui.tab_create import build_tab` 调用该函数,不在主文件中保留重复的组件代码。
`ui/tab_create.py` SHALL 包含「内容创作 Tab」的全部 Gradio 组件定义和事件绑定,并导出 `build_tab(...) -> dict` 函数,返回跨 Tab 共享组件的字典(key 集合不得变更)。
#### Scenario: main.py 正常启动并显示内容创作 Tab
- **WHEN** 运行 `python main.py` 启动 Gradio 应用
- **THEN** 内容创作 Tab 正常显示,所有组件与迁移前功能一致
内容创作 Tab 内部 SHALL 采用**三栏布局**:
- **左栏(scale=3)**:人设选择、话题/风格、文案生成参数(高级设置 Accordion)
- **中栏(scale=4)**:文案输出区(标题、正文、标签、提示词)+ 文案操作按钮组
- **右栏(scale=3)**:图片预览 Gallery + 图片操作按钮组(含美化强度滑块)
三栏通过 `gr.Row()` 包裹三个 `gr.Column(scale=...)` 实现,所有高频操作 SHALL 在无需垂直滚动的情况下可见(≥1280px 宽度分辨率)。
#### Scenario: main.py/app.py 正常启动并显示内容创作 Tab
- **WHEN** 运行应用启动 Gradio
- **THEN** 内容创作 Tab 正常显示三栏布局,所有组件与迁移前功能一致
#### Scenario: tab_create 模块可独立导入
- **WHEN** 在 Python 中执行 `from ui.tab_create import build_tab`
- **THEN** 不抛出任何导入错误,`build_tab` 为可调用对象
#### Scenario: 三栏布局在宽屏下无需滚动
- **WHEN** 用户在 ≥1280px 宽度的浏览器中打开内容创作 Tab
- **THEN** 左栏参数、中栏文案输出、右栏图片预览 SHALL 同时可见,无需垂直滚动
#### Scenario: build_tab 返回字典 key 保持不变
- **WHEN** `build_tab(...)` 被调用并返回字典
- **THEN** 返回字典 SHALL 至少包含 `res_title`、`res_content`、`res_prompt`、`res_tags`、`quality_mode`、`steps`、`cfg_scale`、`neg_prompt` 等原有 key
### Requirement: ui/ 目录结构规范
`ui/` 目录 SHALL 包含 `__init__.py`,每个 Tab 模块文件命名约定为 `tab_<name>.py`,不在 Tab 模块中直接调用全局服务初始化代码(如 `ConfigManager()`、`LLMService()` 等单例初始化应由 `main.py` 完成并通过参数或模块级引用传入)。
`ui/` 目录 SHALL 包含 `__init__.py`,每个 Tab 模块文件命名约定为 `tab_<name>.py`,不在 Tab 模块中直接调用全局服务初始化代码。
#### Scenario: 新增 Tab 模块的标准结构
- **WHEN** 开发者创建新的 `ui/tab_*.py` 文件
- **THEN** 该文件导出 `build_tab(...)` 函数,且顶层不包含副作用代码(不在 import 时触发服务连接)
- **THEN** 该文件导出 `build_tab(...)` 函数,且顶层不包含副作用代码
### Requirement: 自动运营 Tab 采用卡片式两列网格布局
「🤖 自动运营」Tab 内的各自动化任务 SHALL 以 **2 列 × N 行** 的卡片网格展示,每个任务使用 `gr.Group()` 包裹,卡片内 SHALL 包含:任务名称标题、启用开关(`gr.Checkbox`)、执行间隔(`gr.Number` 或 `gr.Slider`)、上次执行时间(`gr.Markdown`)。
#### Scenario: 自动运营任务以两列网格显示
- **WHEN** 用户切换到「🤖 自动运营」Tab
- **THEN** 各自动化任务(自动评论、自动点赞、自动收藏、自动发布、自动回复等)SHALL 以两列卡片网格排列,每列宽度相等
#### Scenario: 每张卡片包含完整任务控制
- **WHEN** 用户查看某个任务卡片
- **THEN** 卡片内 SHALL 显示任务开关、执行间隔设置项,功能与原单列布局完全一致
#### Scenario: 自定义 CSS 增强视觉效果
- **WHEN** 应用加载完成
- **THEN** `gr.Blocks(css=...)` SHALL 注入自定义样式,包含:字体层级优化、按钮圆角(≥6px)、`gr.Group` 卡片轻微阴影(`box-shadow`),不破坏 Gradio 内部组件样式
+38
View File
@@ -0,0 +1,38 @@
## ADDED Requirements
### Requirement: 剩余 Gradio Tab 提取为独立 UI 模块
系统 SHALL 将 `ui/app.py` 中所有 Gradio Tab 按如下顺序排列,且顶部 SHALL 不存在任何全局设置折叠块(`gr.Accordion`):
| 序号 | Tab 名称 | 模块/说明 |
|------|----------|-----------|
| 0 | ✍️ 内容创作 | `ui/tab_create.py` |
| 1 | 📅 内容排期 | 内联或 `ui/tab_queue.py` |
| 2 | 🔥 热点探测 | 内联或 `ui/tab_hotspot.py` |
| 3 | 💬 评论管家 | 内联或 `ui/tab_engage.py` |
| 4 | 📊 数据看板 | 内联或 `ui/tab_analytics.py` |
| 5 | 🧠 智能学习 | 内联或 `ui/tab_learn.py` |
| 6 | 🤖 自动运营 | 内联或 `ui/tab_auto.py` |
| 7 | 🔐 账号登录 | 内联或 `ui/tab_profile.py` |
| 8 | ⚙️ 配置 | 内联(含原全局设置所有组件) |
每个 Tab 模块 SHALL 暴露 `build_tab(...)` 函数,接受所需组件引用和回调函数作为参数。
#### Scenario: 每个 Tab 模块暴露 build_tab 函数
- **WHEN** `ui/app.py` 执行 `from ui.tab_create import build_tab`
- **THEN** 调用 `build_tab(...)` 后 SHALL 返回包含需跨 Tab 共享组件的 dict
#### Scenario: build_tab 接收回调而非直接导入 services
- **WHEN** `build_tab(...)` 内部需要调用业务函数时
- **THEN** 业务函数 SHALL 通过 `fn_*` 参数传入,不在 `ui/tab_*.py` 内直接 `import services.*`
#### Scenario: 事件绑定在 build_tab 内完成
- **WHEN** `build_tab(...)` 被调用
- **THEN** 本 Tab 所有 Gradio 组件的 `.click()`、`.change()` 等事件绑定 SHALL 在函数内完成
#### Scenario: 内容创作 Tab 为首个 Tab(索引 0)
- **WHEN** 用户打开应用
- **THEN** 默认激活的 Tab SHALL 为「✍️ 内容创作」,用户无需额外点击即可开始创作工作流
#### Scenario: 配置和账号 Tab 位于末尾
- **WHEN** 用户查看 Tab 导航栏
- **THEN** 「🔐 账号登录」SHALL 位于倒数第二位,「⚙️ 配置」SHALL 位于最末位,低频操作不干扰主工作区