Codex/ecommerce history sync #29

Merged
stringadmin merged 14 commits from codex/ecommerce-history-sync into main 2026-06-18 02:20:06 +00:00
15 changed files with 1580 additions and 55 deletions
Showing only changes of commit 1adcda08b3 - Show all commits
+39
View File
@@ -0,0 +1,39 @@
# Gitea Actions CI —— 防回潮检查。
#
# 注意:本文件需 Gitea 服务端【启用 Actions】并【配置 act_runner】后才会执行。
# 未配置 runner 时本文件无副作用(不影响本地开发与 husky 钩子)。
# 启用方式:Gitea 站点管理 → 启用 Actions;在 runner 主机注册 act_runner 并打 label。
#
# 本地已有 husky 钩子兜底:pre-commit 跑 tsc+eslint(增量)pre-push 跑 css:audit。
name: CI
on:
push:
branches: [main, "main-merge-work"]
pull_request:
branches: [main]
jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Type check
run: npm run type-check
- name: Tests
run: npm run test:run
- name: Lint (error 阻断,warning 放行)
run: npm run lint
+75
View File
@@ -0,0 +1,75 @@
// ESLint flat configESLint 9)。防回潮基建:锁定去重/抽取/合规成果,约束新代码。
// 策略:warn 基线——历史问题(如 exhaustive-deps)设 warn 不阻断提交,
// 新代码的 error 类问题(unused vars 等)强制清零。CI/pre-commit 只拦 error。
import js from "@eslint/js";
import tseslint from "typescript-eslint";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import unusedImports from "eslint-plugin-unused-imports";
import globals from "globals";
export default tseslint.config(
{
// 忽略构建产物、依赖、配置脚本、JS shim。
ignores: [
"dist/**",
"node_modules/**",
"coverage/**",
"**/*.config.{js,ts,mjs,cjs}",
"scripts/**",
"src/data/ossAssets.js",
],
},
js.configs.recommended,
...tseslint.configs.recommended,
{
files: ["**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: 2022,
sourceType: "module",
globals: {
...globals.browser,
...globals.es2022,
},
},
plugins: {
"react-hooks": reactHooks,
"react-refresh": reactRefresh,
"unused-imports": unusedImports,
},
rules: {
...reactHooks.configs.recommended.rules,
// 历史问题:warn 不阻断,渐进清理。
"react-hooks/exhaustive-deps": "warn",
"@typescript-eslint/no-explicit-any": "warn",
// 未使用 importerror 且可 autofix 自动删除(unused-imports 插件专长)。
"@typescript-eslint/no-unused-vars": "off",
"unused-imports/no-unused-imports": "error",
// 未使用局部变量:warn 基线(不自动删,避免误删 dead code 有副作用的赋值)。
"unused-imports/no-unused-vars": [
"warn",
{ vars: "all", varsIgnorePattern: "^_", args: "after-used", argsIgnorePattern: "^_" },
],
// 允许 warn/error(现有 console.warn/error 是有意的诊断输出)。
"no-console": ["warn", { allow: ["warn", "error"] }],
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
},
},
{
// 测试文件:补 vitest 全局,避免 describe/it/expect 误报未定义。
files: ["**/*.{test,spec}.{ts,tsx}"],
languageOptions: {
globals: {
...globals.node,
describe: "readonly",
it: "readonly",
expect: "readonly",
vi: "readonly",
beforeEach: "readonly",
afterEach: "readonly",
beforeAll: "readonly",
afterAll: "readonly",
},
},
},
);
+5 -8
View File
@@ -1,10 +1,7 @@
// lint-staged 配置 —— 配合 husky pre-commit 使用 // lint-stagedpre-commit 时对暂存文件运行检查。
// // - tsc --noEmit:全量类型检查(函数语法返回命令,不追加文件名)。
// 当前只运行 tsc 全量类型检查(tsc 不接受单文件增量检查) // - eslint --fix:仅对暂存的改动文件增量检查(新代码强制 error=0
// 未来可扩展 ESLint / Prettier / stylelint 等按文件的检查 // warning 不阻断提交)。存量历史问题不会因此被卡住
//
// 函数语法返回原始命令字符串,lint-staged 不会追加文件名。
export default { export default {
"*.{ts,tsx}": () => "tsc --noEmit", "*.{ts,tsx}": [() => "tsc --noEmit", "eslint --fix"],
}; };
+1446
View File
File diff suppressed because it is too large Load Diff
+10
View File
@@ -8,6 +8,9 @@
"build": "vite build", "build": "vite build",
"preview": "vite preview --host 127.0.0.1", "preview": "vite preview --host 127.0.0.1",
"type-check": "tsc -p tsconfig.json --noEmit", "type-check": "tsc -p tsconfig.json --noEmit",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"lint:strict": "eslint . --max-warnings=0",
"test": "vitest", "test": "vitest",
"test:run": "vitest run", "test:run": "vitest run",
"test:coverage": "vitest run --coverage", "test:coverage": "vitest run --coverage",
@@ -23,13 +26,20 @@
"zustand": "5.0.13" "zustand": "5.0.13"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.18.0",
"@types/react": "18.2.55", "@types/react": "18.2.55",
"@types/react-dom": "18.2.18", "@types/react-dom": "18.2.18",
"@vitejs/plugin-react": "4.2.1", "@vitejs/plugin-react": "4.2.1",
"@vitest/coverage-v8": "^1.6.0", "@vitest/coverage-v8": "^1.6.0",
"eslint": "^9.18.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.18",
"eslint-plugin-unused-imports": "^4.1.4",
"globals": "^15.14.0",
"husky": "^9.1.7", "husky": "^9.1.7",
"lint-staged": "^17.0.7", "lint-staged": "^17.0.7",
"typescript": "5.3.3", "typescript": "5.3.3",
"typescript-eslint": "^8.20.0",
"vite": "5.1.0", "vite": "5.1.0",
"vite-plugin-compression2": "2.5.3", "vite-plugin-compression2": "2.5.3",
"vitest": "^1.6.0" "vitest": "^1.6.0"
+1 -1
View File
@@ -1,4 +1,4 @@
import { lazy, Suspense, useCallback, useEffect, useMemo, useState } from "react"; import { lazy, Suspense, useCallback, useEffect, useState } from "react";
import { import {
BugOutlined, BugOutlined,
CheckCircleFilled, CheckCircleFilled,
+1 -1
View File
@@ -108,7 +108,7 @@ export interface ComplianceCheck {
} }
function findJsonSlice(raw: string): string { function findJsonSlice(raw: string): string {
const start = raw.search(/[\[{]/); const start = raw.search(/[{[]/);
if (start < 0) return raw; if (start < 0) return raw;
const stack: string[] = []; const stack: string[] = [];
+1 -1
View File
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useRef, useState } from "react"; import { useEffect, useState } from "react";
export type ToastType = "success" | "error" | "info"; export type ToastType = "success" | "error" | "info";
-36
View File
@@ -26,15 +26,8 @@ import {
} from "@ant-design/icons"; } from "@ant-design/icons";
import { import {
ArrowsCounterClockwise, ArrowsCounterClockwise,
Fire,
FrameCorners,
Gift,
MagicWand, MagicWand,
Mountains,
PaperPlaneRight, PaperPlaneRight,
ShoppingBag,
User,
VideoCamera,
} from "@phosphor-icons/react"; } from "@phosphor-icons/react";
import { Fragment, useEffect, useMemo, useRef, useState, type CSSProperties, type ChangeEvent, type DragEvent, type KeyboardEvent as ReactKeyboardEvent, type MouseEvent as ReactMouseEvent, type PointerEvent as ReactPointerEvent, type ReactNode } from "react"; import { Fragment, useEffect, useMemo, useRef, useState, type CSSProperties, type ChangeEvent, type DragEvent, type KeyboardEvent as ReactKeyboardEvent, type MouseEvent as ReactMouseEvent, type PointerEvent as ReactPointerEvent, type ReactNode } from "react";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
@@ -58,43 +51,25 @@ import EcommerceCopywritingPanel from "./panels/EcommerceCopywritingPanel";
import { ecommerceOssScopes, saveUnifiedEcommerceGenerationRecord, deleteEcommerceGenerationRecord } from "./ecommerceGenerationPersistence"; import { ecommerceOssScopes, saveUnifiedEcommerceGenerationRecord, deleteEcommerceGenerationRecord } from "./ecommerceGenerationPersistence";
import { downloadResultAsset } from "../workbench/workbenchDownload"; import { downloadResultAsset } from "../workbench/workbenchDownload";
import { import {
appendEnglish,
defaultCloneOutput, defaultCloneOutput,
defaultEcommercePlatform, defaultEcommercePlatform,
defaultMarketLanguageOption,
defaultPlatformSpec,
defaultProductSetOutput, defaultProductSetOutput,
domesticPlatformLabels,
domesticPlatformLanguages,
formatUploadedImageRatio,
getMarketLanguageOptions,
getPlatformDefaultLanguage, getPlatformDefaultLanguage,
getPlatformDefaultRatio, getPlatformDefaultRatio,
getPlatformLanguageOptions, getPlatformLanguageOptions,
getPlatformRatioGroup,
getPlatformRatioOptions, getPlatformRatioOptions,
getPlatformSpec,
getUniqueRatioOptions,
isDomesticPlatform,
languageAliases,
legacyPlatformAliases,
marketLanguageOptions, marketLanguageOptions,
marketOptions, marketOptions,
normalizeLanguage,
normalizeLanguageForPlatform, normalizeLanguageForPlatform,
normalizeMarket, normalizeMarket,
normalizePlatform, normalizePlatform,
normalizeRatioForPlatform, normalizeRatioForPlatform,
platformOptions, platformOptions,
platformSpecOptions,
uniqueLanguages,
} from "./utils/platformRules"; } from "./utils/platformRules";
import type { import type {
CloneOutputKey, CloneOutputKey,
PlatformRatioModeKey,
ProductSetOutputKey, ProductSetOutputKey,
} from "./utils/platformRules"; } from "./utils/platformRules";
import type { EcommercePlatformSpec, PlatformRatioGroup } from "./utils/platformRules";
import { import {
buildHistoryTurnFromRecord, buildHistoryTurnFromRecord,
cloneLatestSettingStorageKey, cloneLatestSettingStorageKey,
@@ -102,15 +77,9 @@ import {
defaultCloneSetCounts, defaultCloneSetCounts,
ecommerceHistoryStorageKey, ecommerceHistoryStorageKey,
getTurnResults, getTurnResults,
isCloneImageItem,
isCloneResult,
isCloneSavedSetting,
isEcommerceHistoryRecord,
normalizeEcommerceHistoryRecord, normalizeEcommerceHistoryRecord,
normalizeEcommerceHistoryTurn, normalizeEcommerceHistoryTurn,
readCloneLatestSetting,
readEcommerceHistoryRecords, readEcommerceHistoryRecords,
removeFilePayloadFromImages,
writeCloneLatestSetting, writeCloneLatestSetting,
writeEcommerceHistoryRecords, writeEcommerceHistoryRecords,
} from "./utils/clonePersistence"; } from "./utils/clonePersistence";
@@ -275,18 +244,13 @@ import {
rgbToHex, rgbToHex,
} from "./utils/colorUtils"; } from "./utils/colorUtils";
import { import {
formatAspectRatio,
formatRatioDisplayValue, formatRatioDisplayValue,
getQuickSetRatioValue, getQuickSetRatioValue,
getRatioDisplayParts, getRatioDisplayParts,
greatestCommonDivisor,
normalizeRatioForApi, normalizeRatioForApi,
normalizeRatioToken, normalizeRatioToken,
parseRatioToAspectCss, parseRatioToAspectCss,
quickSetRatioOptions, quickSetRatioOptions,
supportedImageApiRatios,
toSupportedImageApiRatio,
type SupportedImageApiRatio,
} from "./utils/ratioUtils"; } from "./utils/ratioUtils";
@@ -1,4 +1,4 @@
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import "../../styles/pages/ecommerce-video.css"; import "../../styles/pages/ecommerce-video.css";
import { import {
CloseOutlined, CloseOutlined,
@@ -33,7 +33,6 @@ import { useGenerationTasks } from "../../hooks/useGenerationTasks";
import { import {
saveEcommerceVideoState, saveEcommerceVideoState,
loadEcommerceVideoState, loadEcommerceVideoState,
clearEcommerceVideoState,
} from "./ecommerceVideoKeepalive"; } from "./ecommerceVideoKeepalive";
import { saveUnifiedEcommerceGenerationRecord } from "./ecommerceGenerationPersistence"; import { saveUnifiedEcommerceGenerationRecord } from "./ecommerceGenerationPersistence";
import { useVideoSceneRunner } from "./useVideoSceneRunner"; import { useVideoSceneRunner } from "./useVideoSceneRunner";
@@ -4,7 +4,6 @@ import {
CopyOutlined, CopyOutlined,
EditOutlined, EditOutlined,
FileTextOutlined, FileTextOutlined,
FireOutlined,
GlobalOutlined, GlobalOutlined,
MessageOutlined, MessageOutlined,
SmileOutlined, SmileOutlined,
@@ -13,8 +13,6 @@ import {
} from "./ecommerceVideoService"; } from "./ecommerceVideoService";
import { waitForTask } from "../../api/taskSubscription"; import { waitForTask } from "../../api/taskSubscription";
import { ServerRequestError } from "../../api/serverConnection"; import { ServerRequestError } from "../../api/serverConnection";
import { ENTERPRISE_DEFAULT_VIDEO_MODEL } from "../../utils/enterpriseVideoPolicy";
import { resolveVideoRequestModel } from "../../utils/resolveVideoModel";
import { import {
saveEcommerceVideoState, saveEcommerceVideoState,
} from "./ecommerceVideoKeepalive"; } from "./ecommerceVideoKeepalive";
-1
View File
@@ -1,5 +1,4 @@
import { create } from "zustand"; import { create } from "zustand";
import type { WebGenerationPreviewTask } from "../types";
export type QueueItemStatus = "pending" | "running" | "completed" | "failed" | "cancelled"; export type QueueItemStatus = "pending" | "running" | "completed" | "failed" | "cancelled";
-1
View File
@@ -1,4 +1,3 @@
import type { ReactNode } from "react";
export type WebViewKey = export type WebViewKey =
| "home" | "home"
+1 -1
View File
@@ -10,7 +10,7 @@ interface ErrorReport {
sessionId?: string; sessionId?: string;
} }
let reportQueue: ErrorReport[] = []; const reportQueue: ErrorReport[] = [];
let flushTimer: ReturnType<typeof setTimeout> | null = null; let flushTimer: ReturnType<typeof setTimeout> | null = null;
function getSessionId(): string | undefined { function getSessionId(): string | undefined {