-
{formatTaskStatus(task.status)}
-
{formatProfileDate(task.createdAt)}
+
+ {renderCardPreview(task.outputUrl, task.type === "video" ? "video" : "image", formatTaskType(task.type))}
+
+
+ {task.title}
+
+
{task.prompt}
+
+ {formatTaskStatus(task.status)}
+ {formatProfileDate(task.createdAt)}
+
))}
@@ -554,25 +598,27 @@ function ProfilePage({
return projects.length ? (
{projects.map((project) => (
-
-
- {project.name}
- {formatProfileDate(project.updatedAt)}
- {onDeleteProject ? (
-
- ) : null}
-
- {project.description || "最近更新的项目"}
-
-
{project.storyboardCount} 节点
-
{project.imageCount} 图 / {project.videoCount} 视频
+
+ {renderCardPreview(project.thumbnailUrl, "project", "项目")}
+
+
+ {project.name}
+ {onDeleteProject ? (
+
+ ) : null}
+
+
{project.description || "最近更新的项目"}
+
+ {project.storyboardCount} 节点
+ {formatProfileDate(project.updatedAt)}
+
))}
@@ -586,15 +632,18 @@ function ProfilePage({
return savedAssets.length ? (
{savedAssets.map((asset) => (
-
-
- {asset.name}
- {formatAssetStatus(asset.status)}
-
- {asset.description}
-
-
{asset.type}
-
{formatProfileDate(asset.updatedAt)}
+
+ {renderCardPreview(asset.imageUrl || asset.url, asset.type === "video" ? "video" : "asset", formatAssetType(asset.type))}
+
+
+ {asset.name}
+ {formatAssetStatus(asset.status)}
+
+
{asset.description}
+
+ {formatAssetType(asset.type)}
+ {formatProfileDate(asset.updatedAt)}
+
))}
@@ -708,6 +757,50 @@ function ProfilePage({
+
+
+
+
+
+
+ {accountPanel === "credits" ? (
+ <>
+
+ 当前账号
+ {displayName}
+
+
+ 积分剩余
+ {(usage.balanceCents / 100).toFixed(2)}
+
+ >
+ ) : (
+ <>
+
+ 任务总数
+ {tasks.length}
+
+
+ 已完成
+ {completedTasks.length}
+
+ >
+ )}
+
+
+
-
-
-
-
-
-
-
-
-
- {accountPanel === "credits" ? (
- <>
-
- 当前账号
- {displayName}
-
-
- 积分剩余
- {(usage.balanceCents / 100).toFixed(2)}
-
- >
- ) : (
- <>
-
- 任务总数
- {tasks.length}
-
-
- 已完成
- {completedTasks.length}
-
- >
- )}
-
-
diff --git a/src/styles/pages/script-tokens-v5.css b/src/styles/pages/script-tokens-v5.css
index 4114e1c..f8d25db 100644
--- a/src/styles/pages/script-tokens-v5.css
+++ b/src/styles/pages/script-tokens-v5.css
@@ -3715,3 +3715,217 @@
padding-top: 22px;
}
}
+
+/* Ecommerce-aligned tone pass: restrained dark SaaS surfaces, no depth shadows. */
+.script-eval-v5 {
+ --v5-bg: #0d0d0f;
+ --v5-bg2: #151719;
+ --v5-bg3: #181b1d;
+ --v5-bg4: #1d2022;
+ --v5-bg5: #222629;
+ --v5-border: rgba(255, 255, 255, 0.08);
+ --v5-border2: rgba(255, 255, 255, 0.12);
+ --v5-panel: #151719;
+ --v5-panel-2: #181b1d;
+ --v5-panel-3: #101214;
+ --v5-line: rgba(255, 255, 255, 0.08);
+ --v5-line-strong: rgba(0, 255, 136, 0.24);
+ --v5-green-deep: rgba(0, 255, 136, 0.055);
+ --v5-green-soft: rgba(0, 255, 136, 0.09);
+ --v5-green-border: rgba(0, 255, 136, 0.24);
+ --v5-shadow-soft: none;
+ --v5-shadow-tight: none;
+ background:
+ radial-gradient(circle at 24% 0%, rgba(0, 255, 136, 0.038), transparent 34%),
+ linear-gradient(180deg, rgba(255, 255, 255, 0.018), transparent 160px),
+ var(--v5-bg);
+}
+
+.script-eval-v5-page {
+ background:
+ linear-gradient(90deg, rgba(255, 255, 255, 0.014), transparent 24%, transparent 76%, rgba(255, 255, 255, 0.012)),
+ transparent;
+}
+
+.script-eval-v5-left,
+.script-eval-v5-right {
+ background: var(--v5-panel);
+ box-shadow: none;
+}
+
+.script-eval-v5-left {
+ border-right-color: var(--v5-line);
+}
+
+.script-eval-v5-left-main .script-eval-v5-lp-section,
+.script-eval-v5-left-main .script-eval-v5-lp-section.is-fill {
+ background: transparent;
+ border-bottom-color: var(--v5-line);
+ box-shadow: none;
+}
+
+.script-eval-v5-lp-label {
+ color: #a7b3af;
+ letter-spacing: 0.02em;
+}
+
+.script-eval-v5-lp-label::before {
+ background: var(--v5-green);
+ box-shadow: none;
+ opacity: 0.72;
+}
+
+.script-eval-v5-upload-zone,
+.script-eval-v5-info-empty,
+.script-eval-v5-history-empty,
+.script-eval-v5-info-item,
+.script-eval-v5-history-item,
+.script-eval-v5-loading,
+.script-eval-v5-illustration-hit,
+.script-eval-report__score-block,
+.script-eval-report__chart-card,
+.script-eval-report__path-card,
+.script-eval-report__finding-group p {
+ border-color: var(--v5-line);
+ background:
+ linear-gradient(180deg, rgba(255, 255, 255, 0.032), transparent 58%),
+ var(--v5-panel-2);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.025);
+}
+
+.script-eval-v5-upload-zone {
+ border-style: dashed;
+}
+
+.script-eval-v5-upload-zone::after {
+ display: none;
+}
+
+.script-eval-v5-upload-zone:hover,
+.script-eval-v5-upload-zone:focus-visible,
+.script-eval-v5.is-ready .script-eval-v5-upload-zone,
+.script-eval-v5.is-complete .script-eval-v5-upload-zone {
+ border-color: var(--v5-green-border);
+ background:
+ radial-gradient(circle at 50% 0%, rgba(0, 255, 136, 0.075), transparent 58%),
+ var(--v5-panel-3);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.028);
+}
+
+.script-eval-v5-upload-icon,
+.script-eval-v5-upload-card-icon {
+ border-color: rgba(0, 255, 136, 0.18);
+ border-radius: 10px;
+ background: rgba(0, 255, 136, 0.09);
+ box-shadow: none;
+}
+
+.script-eval-v5-upload-btn,
+.script-eval-v5-eval-btn {
+ background: var(--v5-green);
+ color: #061014;
+ box-shadow: none;
+}
+
+.script-eval-v5-upload-btn:hover,
+.script-eval-v5-eval-btn:hover:not(:disabled) {
+ background: var(--v5-green-dim);
+ transform: none;
+ box-shadow: none;
+}
+
+.script-eval-v5-upload-done,
+.script-eval-v5-history-item.is-active,
+.script-eval-v5-error,
+.script-eval-report__chart-note,
+.script-eval-report__grade {
+ box-shadow: none;
+}
+
+.script-eval-v5-upload-done {
+ border-color: var(--v5-green-border);
+ background:
+ linear-gradient(180deg, rgba(0, 255, 136, 0.085), rgba(0, 255, 136, 0.035)),
+ var(--v5-panel-2);
+}
+
+.script-eval-v5-history-item:hover {
+ border-color: rgba(255, 255, 255, 0.13);
+ background:
+ linear-gradient(180deg, rgba(255, 255, 255, 0.045), transparent 58%),
+ var(--v5-panel-2);
+ transform: none;
+ box-shadow: none;
+}
+
+.script-eval-v5-history-item.is-active {
+ border-color: var(--v5-green-border);
+ background:
+ linear-gradient(90deg, rgba(0, 255, 136, 0.08), rgba(0, 255, 136, 0.025)),
+ var(--v5-panel-2);
+}
+
+.script-eval-v5-lp-bottom,
+.script-eval-v5-right-topbar,
+.script-eval-v5-statusbar {
+ background: rgba(21, 23, 25, 0.96);
+ border-color: var(--v5-line);
+ box-shadow: none;
+ backdrop-filter: none;
+}
+
+.script-eval-v5-export-btn,
+.script-eval-v5-action-btn,
+.script-eval-v5-retry-btn {
+ border-color: var(--v5-line);
+ background: rgba(255, 255, 255, 0.035);
+ color: #aeb8b1;
+ box-shadow: none;
+}
+
+.script-eval-v5-export-btn:hover:not(:disabled),
+.script-eval-v5-action-btn:hover,
+.script-eval-v5-retry-btn:hover {
+ border-color: var(--v5-green-border);
+ background: rgba(0, 255, 136, 0.07);
+ color: #d9fff0;
+}
+
+.script-eval-v5-right-content:not(.is-report) {
+ background:
+ radial-gradient(circle at 50% 0%, rgba(0, 255, 136, 0.034), transparent 44%),
+ transparent;
+}
+
+.script-eval-v5-illustration-hit:hover,
+.script-eval-v5-illustration-hit:focus-visible {
+ background:
+ linear-gradient(180deg, rgba(0, 255, 136, 0.06), transparent 58%),
+ var(--v5-panel-2);
+ box-shadow: none;
+}
+
+.script-eval-report {
+ --report-bg: #0d0d0f;
+ --report-panel: #151719;
+ --report-panel-2: #101214;
+ --report-row: #181b1d;
+ --report-border: rgba(255, 255, 255, 0.08);
+ background:
+ linear-gradient(180deg, rgba(255, 255, 255, 0.018), transparent 180px),
+ var(--report-bg);
+}
+
+.script-eval-report::before,
+.script-eval-report::after {
+ opacity: 0.28;
+}
+
+.script-eval-report__bar-fill {
+ box-shadow: none;
+}
+
+.script-eval-v5.is-complete .script-eval-v5-status-dot,
+.script-eval-v5.is-ready .script-eval-v5-status-dot {
+ box-shadow: none;
+}
diff --git a/src/styles/themes/dark-green.css b/src/styles/themes/dark-green.css
index 84993ce..d8b7e40 100644
--- a/src/styles/themes/dark-green.css
+++ b/src/styles/themes/dark-green.css
@@ -6849,6 +6849,649 @@
}
}
+/* Profile center: responsive workspace refinement. */
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard {
+ background:
+ linear-gradient(180deg, rgba(var(--accent-rgb), 0.035), transparent 220px),
+ var(--dg-page);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__banner {
+ height: clamp(156px, 18vw, 214px);
+ background:
+ linear-gradient(135deg, rgba(var(--accent-rgb), 0.1), transparent 36%),
+ linear-gradient(180deg, rgba(255, 255, 255, 0.035), transparent),
+ var(--bg-surface);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__banner-overlay {
+ background:
+ linear-gradient(180deg, rgba(13, 13, 15, 0.12), rgba(13, 13, 15, 0.72)),
+ linear-gradient(90deg, rgba(13, 13, 15, 0.68), transparent 42%);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__body {
+ grid-template-columns: minmax(270px, 300px) minmax(0, 1fr);
+ gap: clamp(18px, 2.4vw, 30px);
+ width: min(1180px, calc(100% - 48px));
+ min-height: auto;
+ margin-top: -58px;
+ padding-bottom: clamp(36px, 5vw, 64px);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__sidebar,
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__account-card,
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__main-tabs,
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__section,
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card,
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__review-item,
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__empty-state,
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__upload-card {
+ border-color: rgba(255, 255, 255, 0.075);
+ background:
+ linear-gradient(180deg, rgba(255, 255, 255, 0.026), transparent),
+ rgba(21, 23, 25, 0.96);
+ box-shadow: none;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__sidebar {
+ gap: 16px;
+ padding: clamp(16px, 2vw, 22px);
+ border-radius: var(--radius-md);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__avatar-ring::before {
+ opacity: 0.45;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__avatar-ring .profile-page__avatar,
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__avatar {
+ width: 82px;
+ height: 82px;
+ border-width: 3px;
+ box-shadow: none;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__avatar-edit {
+ width: 82px;
+ height: 82px;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__avatar-badge {
+ box-shadow: none;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__username {
+ font-size: clamp(19px, 1.7vw, 22px);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__bio-display,
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__bio {
+ border-color: rgba(255, 255, 255, 0.07);
+ background: rgba(255, 255, 255, 0.024);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__bio-display span {
+ text-align: left;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__counts {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 8px;
+ padding: 0;
+ border: 0;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__count {
+ min-width: 0;
+ padding: 12px 8px;
+ border: 1px solid rgba(255, 255, 255, 0.065);
+ border-radius: var(--radius-sm);
+ background: rgba(255, 255, 255, 0.024);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__count strong {
+ font-size: clamp(18px, 1.5vw, 22px);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__account-card {
+ display: grid;
+ gap: 10px;
+ width: 100%;
+ padding: 11px;
+ border: 1px solid rgba(255, 255, 255, 0.075);
+ border-radius: var(--radius-sm);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__account-card .profile-page__list-tabs {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ width: 100%;
+ padding: 3px;
+ border-color: rgba(255, 255, 255, 0.06);
+ background: rgba(255, 255, 255, 0.02);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__account-card .profile-page__list-tabs button {
+ min-width: 0;
+ min-height: 30px;
+ padding: 0 8px;
+ overflow: hidden;
+ font-size: 12px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__account-card .profile-page__upload-card--meta {
+ grid-template-columns: 1fr;
+ gap: 8px;
+ padding: 0;
+ border: 0;
+ background: transparent;
+ box-shadow: none;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__account-card .profile-page__meta-item {
+ min-height: auto;
+ padding: 10px 11px;
+ background: rgba(255, 255, 255, 0.022);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__account-card .profile-page__meta-item strong {
+ font-size: 15px;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__share-btn {
+ min-height: 42px;
+ border-radius: 10px;
+ font-size: 13px;
+ font-weight: 650;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__share-btn:hover {
+ transform: none;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__share-btn--primary {
+ background: var(--accent);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__share-btn--secondary {
+ border-color: rgba(255, 255, 255, 0.08);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__main {
+ gap: 16px;
+ min-width: 0;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__main-tabs {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: 6px;
+ min-height: 50px;
+ margin: 0;
+ padding: 5px;
+ border-radius: var(--radius-md);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__main-tabs button {
+ min-width: 0;
+ min-height: 40px;
+ padding: 0 12px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__section {
+ display: grid;
+ gap: 14px;
+ padding: clamp(14px, 1.8vw, 18px);
+ border: 1px solid rgba(255, 255, 255, 0.075);
+ border-radius: var(--radius-md);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__section-label {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ color: var(--fg-body);
+ font-size: 15px;
+ font-weight: 700;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__section-label::before {
+ content: "";
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: var(--accent);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-grid,
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__review-list {
+ gap: 12px;
+ max-height: none;
+ overflow: visible;
+ padding-right: 0;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-grid {
+ grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__review-list {
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card {
+ min-height: 128px;
+ padding: 15px;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card {
+ grid-template-columns: 92px minmax(0, 1fr);
+ align-items: stretch;
+ gap: 12px;
+ min-height: 116px;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card-preview {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 0;
+ min-height: 88px;
+ overflow: hidden;
+ border: 1px solid rgba(255, 255, 255, 0.065);
+ border-radius: 10px;
+ background:
+ linear-gradient(135deg, rgba(var(--accent-rgb), 0.055), transparent 64%),
+ rgba(255, 255, 255, 0.024);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card-preview:not(.has-media) {
+ border-color: rgba(255, 255, 255, 0.07);
+ background:
+ linear-gradient(180deg, rgba(255, 255, 255, 0.035), transparent),
+ rgba(255, 255, 255, 0.018);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card-preview img,
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card-preview video {
+ width: 100%;
+ height: 100%;
+ min-height: 88px;
+ object-fit: cover;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card-placeholder {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 38px;
+ height: 38px;
+ border: 1px solid rgba(var(--accent-rgb), 0.18);
+ border-radius: 12px;
+ background: rgba(var(--accent-rgb), 0.07);
+ color: rgba(var(--accent-rgb), 0.92);
+ font-size: 16px;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-badge {
+ position: absolute;
+ top: 7px;
+ right: 7px;
+ max-width: calc(100% - 14px);
+ overflow: hidden;
+ padding: 3px 7px;
+ border-radius: 999px;
+ border: 1px solid rgba(var(--accent-rgb), 0.22);
+ background: rgba(8, 14, 12, 0.76);
+ color: var(--accent);
+ font-size: 10px;
+ font-weight: 700;
+ line-height: 1;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-head {
+ gap: 8px;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-head strong {
+ min-width: 0;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__delete-project {
+ flex: 0 0 26px;
+ width: 26px;
+ height: 26px;
+ margin: -3px -2px 0 0;
+ border: 1px solid rgba(255, 255, 255, 0.075);
+ border-radius: 8px;
+ background: rgba(255, 255, 255, 0.026);
+ color: var(--fg-soft);
+ opacity: 0.72;
+ transition: border-color var(--transition-fast), background var(--transition-fast), color var(--transition-fast), opacity var(--transition-fast);
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__delete-project:hover,
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__delete-project:focus-visible {
+ border-color: rgba(255, 118, 118, 0.28);
+ background: rgba(255, 118, 118, 0.08);
+ color: #ff9a9d;
+ opacity: 1;
+ outline: none;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card-body {
+ display: grid;
+ align-content: start;
+ gap: 8px;
+ min-width: 0;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card:hover,
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__review-item:hover {
+ border-color: rgba(var(--accent-rgb), 0.28);
+ transform: none;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__empty-state {
+ min-height: 240px;
+ padding: clamp(30px, 4vw, 46px) 24px;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-bar {
+ margin-bottom: 0;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-tabs {
+ width: fit-content;
+ max-width: 100%;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-tabs button {
+ white-space: nowrap;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__upload-card--meta {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ padding: 12px;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__meta-item {
+ min-height: 78px;
+}
+
+@media (max-width: 960px) {
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__body {
+ grid-template-columns: 1fr;
+ width: min(760px, calc(100% - 36px));
+ margin-top: -44px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__sidebar {
+ display: grid;
+ grid-template-columns: minmax(0, 1.2fr) minmax(220px, 0.8fr);
+ align-items: start;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__sidebar-head {
+ grid-row: span 5;
+ align-items: flex-start;
+ text-align: left;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__counts {
+ align-self: stretch;
+ }
+}
+
+@media (max-width: 640px) {
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__banner {
+ height: 140px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__body {
+ width: min(100% - 28px, 560px);
+ margin-top: -30px;
+ padding-bottom: 84px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__sidebar {
+ display: flex;
+ gap: 12px;
+ padding: 16px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__sidebar-head {
+ align-items: center;
+ text-align: center;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__avatar-ring .profile-page__avatar,
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__avatar {
+ width: 72px;
+ height: 72px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__avatar-edit {
+ width: 72px;
+ height: 72px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__counts {
+ gap: 6px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__count {
+ padding: 10px 6px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__main-tabs {
+ display: flex;
+ overflow-x: auto;
+ scrollbar-width: none;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__main-tabs::-webkit-scrollbar {
+ display: none;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__main-tabs button {
+ flex: 0 0 auto;
+ min-width: 88px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__section {
+ padding: 14px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-grid,
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__review-list {
+ grid-template-columns: 1fr;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card {
+ min-height: 118px;
+ padding: 13px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card {
+ grid-template-columns: 88px minmax(0, 1fr);
+ gap: 10px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card-preview,
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card-preview img,
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card-preview video {
+ min-height: 82px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card-head {
+ align-items: center;
+ flex-direction: row;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__upload-card--meta {
+ grid-template-columns: 1fr;
+ }
+}
+
+@media (max-width: 420px) {
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__body {
+ width: min(100% - 20px, 420px);
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__counts {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__share-btn {
+ min-height: 40px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card {
+ grid-template-columns: 78px minmax(0, 1fr);
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card-preview,
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card-preview img,
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__list-card-preview video {
+ min-height: 76px;
+ }
+}
+
+/* Profile center: lock media card rhythm for dense libraries. */
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card {
+ grid-template-columns: 92px minmax(0, 1fr);
+ height: 126px;
+ min-height: 126px;
+ max-height: 126px;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-preview {
+ width: 92px;
+ height: 96px;
+ min-height: 96px;
+ max-height: 96px;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-preview img,
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-preview video {
+ height: 96px;
+ min-height: 96px;
+ max-height: 96px;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-body {
+ display: grid;
+ grid-template-rows: 18px 36px 18px;
+ align-content: space-between;
+ gap: 8px;
+ height: 96px;
+ min-height: 96px;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-head {
+ min-height: 18px;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-head strong {
+ display: block;
+ width: 100%;
+ line-height: 1.25;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card p {
+ min-height: 36px;
+ max-height: 36px;
+ line-height: 1.5;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-meta {
+ align-self: end;
+ min-height: 18px;
+}
+
+.web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-meta span:last-child {
+ color: var(--fg-soft);
+}
+
+@media (max-width: 640px) {
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card {
+ grid-template-columns: 88px minmax(0, 1fr);
+ height: 112px;
+ min-height: 112px;
+ max-height: 112px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-preview {
+ width: 88px;
+ height: 86px;
+ min-height: 86px;
+ max-height: 86px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-preview img,
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-preview video {
+ height: 86px;
+ min-height: 86px;
+ max-height: 86px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-body {
+ grid-template-rows: 16px 32px 16px;
+ gap: 6px;
+ height: 86px;
+ min-height: 86px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card p {
+ min-height: 32px;
+ max-height: 32px;
+ line-height: 1.45;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-meta {
+ min-height: 16px;
+ }
+}
+
+@media (max-width: 420px) {
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card {
+ grid-template-columns: 78px minmax(0, 1fr);
+ height: 104px;
+ min-height: 104px;
+ max-height: 104px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-preview {
+ width: 78px;
+ height: 78px;
+ min-height: 78px;
+ max-height: 78px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-preview img,
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-preview video {
+ height: 78px;
+ min-height: 78px;
+ max-height: 78px;
+ }
+
+ .web-shell[data-ui-theme="dark-green"] .profile-page--dashboard .profile-page__media-card .profile-page__list-card-body {
+ height: 78px;
+ min-height: 78px;
+ }
+}
+
/* Ecommerce generation page: keep its carousel and composer independent from
the community carousel rules that share class names. */
.web-shell[data-ui-theme="dark-green"] .ecommerce-landing-page {
@@ -8948,18 +9591,23 @@
/* Canvas SaaS polish: refined production-tool surfaces without changing canvas behavior. */
.web-shell[data-ui-theme="dark-green"][data-view="canvas"] .canvas-page .studio-canvas {
- background:
+ background-image:
radial-gradient(circle at 18% 8%, rgba(var(--accent-rgb), 0.055), transparent 30%),
radial-gradient(circle at 72% 88%, rgba(255, 255, 255, 0.035), transparent 28%),
- linear-gradient(rgba(255, 255, 255, 0.026) 1px, transparent 1px),
- linear-gradient(90deg, rgba(255, 255, 255, 0.026) 1px, transparent 1px),
- var(--dg-page);
+ radial-gradient(
+ circle,
+ rgba(148, 163, 184, 0.34) 0 var(--canvas-bg-dot, 1.35px),
+ transparent calc(var(--canvas-bg-dot, 1.35px) + 0.55px)
+ );
+ background-color: var(--dg-page);
+ background-position:
+ center,
+ center,
+ var(--canvas-bg-x, 0px) var(--canvas-bg-y, 0px);
background-size:
auto,
auto,
- 24px 24px,
- 24px 24px,
- auto;
+ var(--canvas-bg-size, 24px) var(--canvas-bg-size, 24px);
color: var(--fg-body);
}
From c4ef9cc6bade39ec6727323d4c4b3fc68ad4a6bc Mon Sep 17 00:00:00 2001
From: ludan <251918489@qq.com>
Date: Thu, 4 Jun 2026 16:23:43 +0800
Subject: [PATCH 3/6] Merge origin/master: resolve CSS conflicts
script-tokens-v5.css: keep both SaaS polish rules and master's additions
dark-green.css: keep both profile/canvas polish and master's additions
---
src/styles/pages/script-tokens-v5.css | 2 --
src/styles/themes/dark-green.css | 4 ----
2 files changed, 6 deletions(-)
diff --git a/src/styles/pages/script-tokens-v5.css b/src/styles/pages/script-tokens-v5.css
index f13933a..f8d25db 100644
--- a/src/styles/pages/script-tokens-v5.css
+++ b/src/styles/pages/script-tokens-v5.css
@@ -2649,7 +2649,6 @@
min-height: 52px;
}
}
-<<<<<<< ours
/* ===== Commercial SaaS polish for script review page ===== */
.script-eval-v5 {
@@ -3930,4 +3929,3 @@
.script-eval-v5.is-ready .script-eval-v5-status-dot {
box-shadow: none;
}
->>>>>>> theirs
diff --git a/src/styles/themes/dark-green.css b/src/styles/themes/dark-green.css
index 8b743ba..ce421cd 100644
--- a/src/styles/themes/dark-green.css
+++ b/src/styles/themes/dark-green.css
@@ -6089,7 +6089,6 @@
box-shadow: none;
}
-<<<<<<< ours
.web-shell[data-ui-theme="dark-green"] .profile-page__meta-item small {
overflow: hidden;
color: var(--fg-soft);
@@ -8397,7 +8396,6 @@
.web-shell[data-ui-theme="dark-green"] .canvas-page .studio-canvas-node-resize-handle::before,
.web-shell[data-ui-theme="dark-green"] .canvas-page .studio-canvas-node-resize-handle::after {
background: var(--dg-button-text);
->>>>>>> theirs
}
.web-shell[data-ui-theme="dark-green"] .canvas-page .studio-canvas-save-asset {
@@ -8797,7 +8795,6 @@
}
}
-<<<<<<< ours
/* Ecommerce final polish: keep selected option states refined in the dark-green theme. */
.web-shell[data-ui-theme="dark-green"][data-view="ecommerce"] .product-clone-page[data-tool="clone"] :is(
.clone-ai-module-list button,
@@ -8917,7 +8914,6 @@
--commerce-panel: #151719;
--commerce-soft: #101214;
background: #0d0d0f;
->>>>>>> theirs
}
.web-shell[data-ui-theme="dark-green"] .omni-commerce-panel,
From e8a42dafde6eb5417b866769e1dc5fd29e37a568 Mon Sep 17 00:00:00 2001
From: ludan <251918489@qq.com>
Date: Thu, 4 Jun 2026 16:29:38 +0800
Subject: [PATCH 4/6] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=90=88=E5=B9=B6?=
=?UTF-8?q?=E5=86=B2=E7=AA=81=E5=AF=BC=E8=87=B4=E7=9A=84CSS=E6=9C=AA?=
=?UTF-8?q?=E9=97=AD=E5=90=88=E8=8A=B1=E6=8B=AC=E5=8F=B7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/styles/themes/dark-green.css | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/styles/themes/dark-green.css b/src/styles/themes/dark-green.css
index ce421cd..bcf25f0 100644
--- a/src/styles/themes/dark-green.css
+++ b/src/styles/themes/dark-green.css
@@ -8393,6 +8393,8 @@
.web-shell[data-ui-theme="dark-green"][data-view="workbench"] .ai-workbench-page.is-active .ai-chat-messages-surface {
padding-top: 86px;
}
+}
+
.web-shell[data-ui-theme="dark-green"] .canvas-page .studio-canvas-node-resize-handle::before,
.web-shell[data-ui-theme="dark-green"] .canvas-page .studio-canvas-node-resize-handle::after {
background: var(--dg-button-text);
@@ -8907,6 +8909,8 @@
.web-shell[data-ui-theme="dark-green"][data-view="canvas"] .studio-canvas-project-bar__status,
.web-shell[data-ui-theme="dark-green"][data-view="canvas"] .studio-canvas-project-bar__autosave-status {
color: var(--fg-muted);
+}
+
.web-shell[data-ui-theme="dark-green"] .omni-commerce-page {
--commerce-ink: #f3f5f2;
--commerce-muted: #aeb8b1;
From b81128d7cadef68649ae094efe3aaf19b66b39d5 Mon Sep 17 00:00:00 2001
From: Stringadmin
Date: Thu, 4 Jun 2026 18:27:12 +0800
Subject: [PATCH 5/6] fix: harden ecommerce media history for launch
---
scripts/smoke-generation-mocked.mjs | 72 +++++++
src/api/keyServerClient.ts | 4 +-
.../ecommerce/ecommerceVideoService.ts | 179 +++++++++++++++++-
3 files changed, 244 insertions(+), 11 deletions(-)
create mode 100644 scripts/smoke-generation-mocked.mjs
diff --git a/scripts/smoke-generation-mocked.mjs b/scripts/smoke-generation-mocked.mjs
new file mode 100644
index 0000000..f078568
--- /dev/null
+++ b/scripts/smoke-generation-mocked.mjs
@@ -0,0 +1,72 @@
+import fs from "node:fs";
+import path from "node:path";
+import process from "node:process";
+
+const repoRoot = process.cwd();
+const failures = [];
+
+function read(relativePath) {
+ return fs.readFileSync(path.join(repoRoot, relativePath), "utf8");
+}
+
+function assertMatch(label, content, pattern) {
+ if (!pattern.test(content)) {
+ failures.push(label);
+ }
+}
+
+function assertNoMatch(label, content, pattern) {
+ if (pattern.test(content)) {
+ failures.push(label);
+ }
+}
+
+const serverConnection = read("src/api/serverConnection.ts");
+const generationClient = read("src/api/aiGenerationClient.ts");
+const ecommerceVideoService = read("src/features/ecommerce/ecommerceVideoService.ts");
+const workbenchPersistence = read("src/features/workbench/workbenchResultPersistence.ts");
+
+assertMatch(
+ "serverConnection must build same-origin /api URLs",
+ serverConnection,
+ /return\s+`\/api\/\$\{cleanPath\}`;/,
+);
+assertNoMatch(
+ "frontend generation flow must not use fixed VITE environment config",
+ `${serverConnection}\n${generationClient}`,
+ /\b(?:import\.meta\.env|VITE_[A-Z0-9_]+)\b/,
+);
+assertNoMatch(
+ "frontend generation flow must not call provider hosts directly",
+ generationClient,
+ /dashscope\.aliyuncs\.com|\/dashscope-api\b|Bearer\s+sk-/i,
+);
+assertMatch("image generation must go through the app API", generationClient, /buildApiUrl\("ai\/image"\)/);
+assertMatch("video generation must go through the app API", generationClient, /buildApiUrl\("ai\/video"\)/);
+assertMatch("binary uploads must go through the app OSS API", generationClient, /buildApiUrl\("oss\/upload-binary"\)/);
+assertMatch("URL uploads must go through the app OSS API", generationClient, /buildApiUrl\("oss\/upload-by-url"\)/);
+assertMatch(
+ "ecommerce video history must durable-copy media before saving",
+ ecommerceVideoService,
+ /buildDurableVideoHistoryPayload\(payload\)/,
+);
+assertMatch(
+ "ecommerce video history must filter temporary provider URLs on read",
+ ecommerceVideoService,
+ /items:\s*history\.items\.map\(removeTemporaryHistoryUrls\)/,
+);
+assertMatch(
+ "workbench results must persist generated media through OSS",
+ workbenchPersistence,
+ /uploadAssetByUrl\(/,
+);
+
+if (failures.length) {
+ console.error("Mocked generation smoke check failed:");
+ for (const failure of failures) {
+ console.error(`- ${failure}`);
+ }
+ process.exit(1);
+}
+
+console.log("Mocked generation smoke check passed.");
diff --git a/src/api/keyServerClient.ts b/src/api/keyServerClient.ts
index 1acf077..151a551 100644
--- a/src/api/keyServerClient.ts
+++ b/src/api/keyServerClient.ts
@@ -913,7 +913,7 @@ export const keyServerClient = {
async getProjectContent(projectId: string): Promise {
const stored = readStoredSession();
if (!stored) {
- throw new Error("闇€瑕佸厛鐧诲綍");
+ throw new Error("需要先登录");
}
const safeProjectId = encodeURIComponent(projectId.trim());
@@ -1000,7 +1000,7 @@ export const keyServerClient = {
async deleteProject(projectId: string, options?: DeleteProjectOptions): Promise {
const stored = readStoredSession();
if (!stored) {
- throw new Error("闇€瑕佸厛鐧诲綍");
+ throw new Error("需要先登录");
}
const path = options?.cleanupUserData ? `projects/${encodeURIComponent(projectId)}?cleanupUserData=1` : `projects/${encodeURIComponent(projectId)}`;
diff --git a/src/features/ecommerce/ecommerceVideoService.ts b/src/features/ecommerce/ecommerceVideoService.ts
index 7f85cfa..d3d304a 100644
--- a/src/features/ecommerce/ecommerceVideoService.ts
+++ b/src/features/ecommerce/ecommerceVideoService.ts
@@ -19,6 +19,102 @@ import type {
PlanStep,
} from "./ecommerceVideoTypes";
+type UploadAssetByUrl = typeof aiGenerationClient.uploadAssetByUrl;
+
+interface DurableMediaUrl {
+ url: string | null;
+ originalUrl?: string | null;
+ ossKey?: string | null;
+}
+
+const TEMP_MEDIA_HOST_RE = /^file\d*\.aitohumanize\.com$/i;
+const OSS_MEDIA_HOST_RE = /\.oss-[^.]+\.aliyuncs\.com$/i;
+
+function isTemporaryProviderUrl(url: string): boolean {
+ try {
+ return TEMP_MEDIA_HOST_RE.test(new URL(url).hostname);
+ } catch {
+ return false;
+ }
+}
+
+function isDurableOssUrl(url: string): boolean {
+ try {
+ const parsed = new URL(url);
+ return parsed.protocol === "https:" && OSS_MEDIA_HOST_RE.test(parsed.hostname);
+ } catch {
+ return false;
+ }
+}
+
+function getMediaExtension(url: string, mimeType: string): string {
+ const normalizedMime = mimeType.split(";")[0]?.trim().toLowerCase();
+ if (normalizedMime === "image/jpeg") return "jpg";
+ if (normalizedMime === "image/png") return "png";
+ if (normalizedMime === "image/webp") return "webp";
+ if (normalizedMime === "image/gif") return "gif";
+ if (normalizedMime === "video/mp4") return "mp4";
+ if (normalizedMime === "video/webm") return "webm";
+ if (normalizedMime === "video/quicktime") return "mov";
+
+ try {
+ const matched = new URL(url).pathname.match(/\.([a-z0-9]{2,5})$/i);
+ if (matched?.[1]) return matched[1].toLowerCase();
+ } catch {
+ // Keep mime fallback below.
+ }
+
+ return mimeType.startsWith("video/") ? "mp4" : "png";
+}
+
+function buildDurableMediaName(prefix: string, url: string, mimeType: string): string {
+ const normalized = prefix
+ .trim()
+ .replace(/[\\/:*?"<>|]+/g, "-")
+ .replace(/\s+/g, " ")
+ .slice(0, 80)
+ .trim();
+ return `${normalized || "ecommerce-video-media"}.${getMediaExtension(url, mimeType)}`;
+}
+
+export async function resolveDurableMediaUrl(
+ url: string | null | undefined,
+ options: {
+ mediaType: "image" | "video";
+ namePrefix: string;
+ scope?: string;
+ uploadAssetByUrl?: UploadAssetByUrl;
+ },
+): Promise {
+ const sourceUrl = String(url || "").trim();
+ if (!sourceUrl) return { url: null };
+ if (isDurableOssUrl(sourceUrl)) return { url: sourceUrl };
+
+ const mimeType = options.mediaType === "video" ? "video/mp4" : "image/png";
+ const uploadAssetByUrl = options.uploadAssetByUrl || aiGenerationClient.uploadAssetByUrl.bind(aiGenerationClient);
+
+ try {
+ const uploaded = await uploadAssetByUrl({
+ sourceUrl,
+ name: buildDurableMediaName(options.namePrefix, sourceUrl, mimeType),
+ mimeType,
+ scope: options.scope || "ecommerce-video-history",
+ });
+ return {
+ url: uploaded.url || null,
+ originalUrl: sourceUrl,
+ ossKey: uploaded.ossKey || null,
+ };
+ } catch (error) {
+ const message = error instanceof Error ? error.message : String(error || "");
+ console.warn("[ecommerce-video] history media persistence failed:", message);
+ if (isTemporaryProviderUrl(sourceUrl)) {
+ return { url: null, originalUrl: sourceUrl };
+ }
+ return { url: sourceUrl };
+ }
+}
+
export interface PlanCallbacks {
onStepStart: (step: PlanStep) => void;
onStepDone: (step: PlanStep) => void;
@@ -268,6 +364,15 @@ export interface VideoHistoryScene {
videoUrl?: string | null;
}
+interface SaveVideoHistoryPayload {
+ title: string;
+ config: Record;
+ plan: Record;
+ scenes: VideoHistoryScene[];
+ sourceImageUrls: string[];
+ uploadAssetByUrl?: UploadAssetByUrl;
+}
+
export interface VideoHistoryItem {
id: number;
title: string;
@@ -293,22 +398,74 @@ function getAuthHeaders(): Record {
return token ? { Authorization: `Bearer ${token}` } : {};
}
-export async function saveVideoHistory(payload: {
- title: string;
- config: Record;
- plan: Record;
- scenes: VideoHistoryScene[];
- sourceImageUrls: string[];
-}): Promise<{ id: number; createdAt: string }> {
+export async function buildDurableVideoHistoryPayload(payload: SaveVideoHistoryPayload): Promise {
+ const uploadAssetByUrl = payload.uploadAssetByUrl;
+ const scenes = await Promise.all(
+ payload.scenes.map(async (scene) => {
+ const [image, video] = await Promise.all([
+ resolveDurableMediaUrl(scene.imageUrl, {
+ mediaType: "image",
+ namePrefix: `ecommerce-scene-${scene.sceneId}-image`,
+ uploadAssetByUrl,
+ }),
+ resolveDurableMediaUrl(scene.videoUrl, {
+ mediaType: "video",
+ namePrefix: `ecommerce-scene-${scene.sceneId}-video`,
+ uploadAssetByUrl,
+ }),
+ ]);
+ return {
+ ...scene,
+ imageUrl: image.url,
+ videoUrl: video.url,
+ };
+ }),
+ );
+
+ const sourceImageUrls = (
+ await Promise.all(
+ payload.sourceImageUrls.map((url, index) =>
+ resolveDurableMediaUrl(url, {
+ mediaType: "image",
+ namePrefix: `ecommerce-source-${index + 1}`,
+ uploadAssetByUrl,
+ }),
+ ),
+ )
+ )
+ .map((item) => item.url)
+ .filter((url): url is string => Boolean(url));
+
+ return {
+ ...payload,
+ scenes,
+ sourceImageUrls,
+ };
+}
+
+export async function saveVideoHistory(payload: SaveVideoHistoryPayload): Promise<{ id: number; createdAt: string }> {
+ const { uploadAssetByUrl: _uploadAssetByUrl, ...historyPayload } = await buildDurableVideoHistoryPayload(payload);
const res = await fetch(API_BASE, {
method: "POST",
headers: { "Content-Type": "application/json", ...getAuthHeaders() },
- body: JSON.stringify(payload),
+ body: JSON.stringify(historyPayload),
});
if (!res.ok) throw new Error("保存历史记录失败");
return res.json();
}
+function removeTemporaryHistoryUrls(item: VideoHistoryItem): VideoHistoryItem {
+ return {
+ ...item,
+ scenes: item.scenes.map((scene) => ({
+ ...scene,
+ imageUrl: scene.imageUrl && !isTemporaryProviderUrl(scene.imageUrl) ? scene.imageUrl : null,
+ videoUrl: scene.videoUrl && !isTemporaryProviderUrl(scene.videoUrl) ? scene.videoUrl : null,
+ })),
+ sourceImageUrls: item.sourceImageUrls.filter((url) => !isTemporaryProviderUrl(url)),
+ };
+}
+
export async function fetchVideoHistory(
limit = 20,
offset = 0,
@@ -318,7 +475,11 @@ export async function fetchVideoHistory(
{ headers: getAuthHeaders() },
);
if (!res.ok) throw new Error("获取历史记录失败");
- return res.json();
+ const history = (await res.json()) as VideoHistoryListResponse;
+ return {
+ ...history,
+ items: history.items.map(removeTemporaryHistoryUrls),
+ };
}
export async function deleteVideoHistory(id: number): Promise {
From 3493f169c02f2d1ba77dbd40b197f1d99da1ded4 Mon Sep 17 00:00:00 2001
From: Stringadmin
Date: Thu, 4 Jun 2026 19:00:50 +0800
Subject: [PATCH 6/6] fix: use public model config and disable source maps
---
src/api/modelCapabilitiesClient.ts | 2 +-
vite.config.ts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/api/modelCapabilitiesClient.ts b/src/api/modelCapabilitiesClient.ts
index 85b0f8b..33ce1f4 100644
--- a/src/api/modelCapabilitiesClient.ts
+++ b/src/api/modelCapabilitiesClient.ts
@@ -71,7 +71,7 @@ export const modelCapabilitiesClient = {
let payload: unknown;
try {
- payload = await serverRequest(`config/profile?name=${encodeURIComponent(name)}`);
+ payload = await serverRequest(`public/config/profile?name=${encodeURIComponent(name)}`);
} catch (error) {
if (isOptionalApiRouteMissing(error)) {
modelCapabilitiesRouteMissing = true;
diff --git a/vite.config.ts b/vite.config.ts
index 5dea423..49aaa45 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -25,7 +25,7 @@ export default defineConfig(() => ({
drop: ["console", "debugger"],
},
build: {
- sourcemap: "hidden",
+ sourcemap: false,
rollupOptions: {
output: {
manualChunks(id: string) {