Codex/ecommerce history sync #29
@@ -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
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
// ESLint flat config(ESLint 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",
|
||||||
|
// 未使用 import:error 且可 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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
// lint-staged 配置 —— 配合 husky pre-commit 使用
|
// lint-staged:pre-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"],
|
||||||
};
|
};
|
||||||
|
|||||||
Generated
+1446
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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,
|
||||||
|
|||||||
@@ -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,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";
|
||||||
|
|
||||||
|
|||||||
@@ -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,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,4 +1,3 @@
|
|||||||
import type { ReactNode } from "react";
|
|
||||||
|
|
||||||
export type WebViewKey =
|
export type WebViewKey =
|
||||||
| "home"
|
| "home"
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user