Compare commits

..

2 Commits

Author SHA1 Message Date
R524809 4fb37bb45a feat: 修改AI规划出图 2026-08-27 16:12:56 +08:00
R524809 0e8fc4ebe0 feat: 采集插件样式修改 2026-08-26 17:38:18 +08:00
4 changed files with 63 additions and 21 deletions
+39 -2
View File
@@ -33,6 +33,32 @@ const STYLES = `
.fab:hover { transform: scale(1.08); }
.fab.hidden { display: none; }
/* 收起按钮:骑在面板左上边缘(一半在面板外)。必须在 iframe 外渲染——
iframe 裁剪内容,iframe 内的元素永远溢不出面板边界 */
.panel-close {
position: fixed;
top: 20px;
right: min(880px, 100vw); /* 面板左边缘 = 视口右沿 - 面板宽度 */
transform: translateX(50%);
width: 28px;
height: 28px;
border-radius: 50%;
border: 1px solid rgba(0, 0, 0, 0.12);
background: #fff;
color: #555;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
z-index: 4;
opacity: 0;
pointer-events: none;
transition: opacity 0.25s ease, transform 0.15s ease;
}
.panel-close.visible { opacity: 1; pointer-events: auto; }
.panel-close:hover { color: #6d28d9; border-color: #6d28d9; transform: translateX(50%) scale(1.08); }
.panel {
position: fixed;
top: 0;
@@ -92,23 +118,34 @@ export default defineContentScript({
const iframe = document.createElement('iframe');
iframe.title = '电商套图工作台';
panel.append(iframe);
container.append(fab, panel);
// 外置收起按钮(骑在面板左上边缘,一半在面板外)
const closeBtn = document.createElement('button');
closeBtn.className = 'panel-close';
closeBtn.title = '收起面板(Esc';
closeBtn.innerHTML =
'<svg width="12" height="12" viewBox="0 0 12 12" fill="none">' +
'<path d="M2 2l8 8M10 2l-8 8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>';
container.append(fab, panel, closeBtn);
const open = () => {
if (!iframe.src) iframe.src = chrome.runtime.getURL('/sidepanel.html');
panel.classList.add('open');
closeBtn.classList.add('visible');
fab.classList.add('hidden');
// 聚焦进面板,键盘操作(Esc 关闭 / 预览翻页)直接可用
iframe.focus();
};
const close = () => {
panel.classList.remove('open');
closeBtn.classList.remove('visible');
if (isProductPage()) fab.classList.remove('hidden');
};
fab.addEventListener('click', open);
closeBtn.addEventListener('click', close);
// 面板内 App✕ / Esc)→ 收起
// 面板内 AppEsc)→ 收起;✕ 按钮已外置到宿主层(closeBtn)
window.addEventListener('message', (e) => {
if (e.source === iframe.contentWindow && (e.data as any)?.type === PANEL_CLOSE_MSG) close();
});
+3 -5
View File
@@ -11,7 +11,7 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { createRoot } from 'react-dom/client';
import { App as AntApp, ConfigProvider, Popover, Progress, Select } from 'antd';
import { SettingOutlined, DownloadOutlined, ThunderboltOutlined, UploadOutlined, CloseOutlined } from '@ant-design/icons';
import { SettingOutlined, DownloadOutlined, ThunderboltOutlined, UploadOutlined } from '@ant-design/icons';
import type { ScanResult, ImageMaterial } from '../../src/collector/scan';
import { cleanFilename } from '../../src/collector/url';
import {
@@ -671,11 +671,9 @@ const App: React.FC = () => {
return (
<div className="page">
{/* ── 顶栏 ── */}
{/* ── 顶栏 ──(页内模式的收起 ✕ 按钮在宿主层渲染:要骑在面板边缘、一半在面板外,
iframe 会裁剪内容,按钮必须在 iframe 外;Esc 关闭仍走 closePanel */}
<div className="topbar">
{IN_PAGE && (
<button className="icon-btn" title="收起面板(Esc" onClick={closePanel}><CloseOutlined /></button>
)}
<div className="logo"></div>
<div>
<h1></h1>
+19 -12
View File
@@ -22,23 +22,27 @@ ALLOWED_KINDS = [
"size_chart", "sku_collection", "custom",
]
# 方案总张数硬上限(提示词预算 + 代码兜底裁减都用它)
MAX_PLAN_TOTAL = 8
SYSTEM_PROMPT = """你是一名资深电商视觉策划。根据商品信息规划一套电商套图的出图方案。
## 输出硬性约束(违反即失败)
1. 输出必须是**单行紧凑 JSON**:无换行、无缩进、无空格填充、无注释、无 markdown 围栏。
2. 顶层只有 summary 和 items 两个字段;每个 item 严格只有 kind/title/detail/prompt_hint/count/variant_name 六个字段,不得增删。
3. 文本长度上限(中文字符/英文单词数):summary ≤ 25 字;title ≤ 8 字;detail ≤ 20 字;prompt_hint ≤ 15 个英文词。超限必须删减,不得省略号截断。
4. count 默认 1,仅当该类图确需多个变体时才 >1,最大 3。总张数 8-15
4. count 默认 1,仅当该类图确需多个变体时才 >1,最大 3。总张数(所有 count 之和)不得超过 8,这是硬性上限
5. variant_name 只能从「SKU规格」列表原样照抄;没有绑定就输出 null。
## 规划规则
1. SKU 主图:每个带图 SKU 出 1 张独立主图(kind=white_bg),variant_name 填对应规格名;单 SKU 出 1 张(variant_name=null
2. 场景图(kind=lifestyle):按核心使用场景出 2-4 张,每张聚焦一个场景
3. 细节图(kind=material 或 custom):按关键细节/材质/结构2-3 张,每张聚焦一个卖点
4. 尺寸标注图(kind=size_chart):参数含长宽高/尺寸时出 1 张
5. SKU 合集图(kind=sku_collection):SKU >1 时出 1 张。
6. kind 枚举:white_bg / key_features / selling_pt / material / lifestyle / multi_scene / ecommerce_detail / size_chart / sku_collection / custom
7. title 用中文短语(如「主图·粉色」「浴室壁挂」);detail 中文说明这张图展示什么;prompt_hint 用英文描述构图要点
## 规划规则(总预算 8 张,按优先级分配)
1. 总预算:所有项 count 之和 ≤ 8。空间不足时砍低优先级项,优先级从低到高:尺寸标注图 / SKU 合集图 / 细节图 → 场景图;SKU 主图不砍
2. SKU 主图:每个带图 SKU 出 1 张独立主图(kind=white_bg),variant_name 填对应规格名;单 SKU 出 1 张(variant_name=null
3. 场景图(kind=lifestyle):预算内1-3 张,每张聚焦一个场景
4. 细节图(kind=material 或 custom):预算内出 1-2 张,每张聚焦一个卖点
5. 尺寸标注图(kind=size_chart):参数含长宽高/尺寸且预算富余时出 1 张。
6. SKU 合集图(kind=sku_collection):SKU >1 且预算富余时出 1 张
7. kind 枚举:white_bg / key_features / selling_pt / material / lifestyle / multi_scene / ecommerce_detail / size_chart / sku_collection / custom
8. title 用中文短语(如「主图·粉色」「浴室壁挂」);detail 中文说明这张图展示什么;prompt_hint 用英文描述构图要点。
## 输出示例(紧凑单行)
{"summary":"三色收纳盒全套图","items":[{"kind":"white_bg","title":"主图·粉色","detail":"粉色SKU白底主视觉","prompt_hint":"front view on white background","count":1,"variant_name":"粉色"}]}"""
@@ -214,14 +218,17 @@ async def generate_plan(
items = _normalize_items(data.get("items") or [], sku_variants)
if not items:
raise RuntimeError("规划器未返回有效方案项")
# 总量保护:超过 18 张时按比例截断
total = sum(i["count"] for i in items)
while total > 18 and items:
# 总量保护:超过上限时从尾部(低优先级项)裁减,SKU 主图排在前面不受影响
before_trim = sum(i["count"] for i in items)
total = before_trim
while total > MAX_PLAN_TOTAL and items:
last = items[-1]
if last["count"] > 1:
last["count"] -= 1
else:
items.pop()
total = sum(i["count"] for i in items)
if total < before_trim:
log.info("规划方案 %d 张超出上限 %d,已裁减至 %d 张(从尾部低优先级项开始)", before_trim, MAX_PLAN_TOTAL, total)
return {"summary": str(data.get("summary") or "").strip()[:100], "items": items}
+2 -2
View File
@@ -18,8 +18,8 @@ from config import get_settings
log = logging.getLogger("suite.watermark")
# 尺寸比例(与 ozonSeller app.js 常量一致
BADGE_SCALE = 0.15 # 图片水印直径 / 图宽
# 尺寸比例(BADGE_SCALE 在 ozonSeller 的 0.15 基础上缩小 30%:用户反馈偏大
BADGE_SCALE = 0.105 # 图片水印直径 / 图宽
BADGE_MARGIN = 0.01 # 图片水印边距 / 图宽(ozonSeller 固定 10px,按比例更稳)
TEXT_SCALE = 0.06 # 文字字号 / 图宽
TEXT_MIN_SIZE = 12