diff --git a/frontend-web/src/app/(dashboard)/workspace/create/page.tsx b/frontend-web/src/app/(dashboard)/workspace/create/page.tsx index 4c6a7dc..514895a 100644 --- a/frontend-web/src/app/(dashboard)/workspace/create/page.tsx +++ b/frontend-web/src/app/(dashboard)/workspace/create/page.tsx @@ -355,10 +355,9 @@ export default function CreateTaskPage() {
AI Video Studio -

工作台

+

生成工作台

- 按模型选型、提示词、素材引用的顺序完成一次提交。用户侧不暴露 - URL 之类的平台配置,只保留创作需要的参数。 + 以“模型选型 → 提示词 → 素材引用 → 任务提交”的顺序完成一次创作。平台连接信息被隐藏,只保留真正影响出片的参数。

@@ -376,6 +375,27 @@ export default function CreateTaskPage() {
+
+
+ 当前模型 + {selectedModel?.frontendTitle ?? "等待加载"} +
+
+ 出片规格 + + {activeDuration}s · {activeResolution} · {activeRatio} + +
+
+ 任务预估 + {estimatedPoints} 积分 +
+
+ 素材引用 + {selectedAssetCount} 项 +
+
+
可用积分 @@ -644,7 +664,7 @@ export default function CreateTaskPage() {
Asset Dock

素材库

-

上传后即时入库,右侧挑素材,左侧直接组成任务。

+

上传后即时入库,边看缩略图边做引用,创作参数和素材选择留在同一画面里。

素材库 diff --git a/frontend-web/src/app/(dashboard)/workspace/tasks/page.tsx b/frontend-web/src/app/(dashboard)/workspace/tasks/page.tsx index c07fa5f..fd71136 100644 --- a/frontend-web/src/app/(dashboard)/workspace/tasks/page.tsx +++ b/frontend-web/src/app/(dashboard)/workspace/tasks/page.tsx @@ -15,6 +15,13 @@ export default function TasksPage() { refetchInterval: 4_000, }); + const tasks = tasksQuery.data ?? []; + const runningCount = tasks.filter((task) => + ["queued", "submitted", "running"].includes(task.taskStatus), + ).length; + const succeededCount = tasks.filter((task) => task.taskStatus === "succeeded").length; + const failedCount = tasks.filter((task) => task.taskStatus === "failed").length; + return (
@@ -27,13 +34,36 @@ export default function TasksPage() {
+
+
+ 任务总数 + {tasks.length} + 当前账号已提交的所有视频任务 +
+
+ 处理中 + {runningCount} + 包含排队、已提交和运行中的任务 +
+
+ 已完成 + {succeededCount} + 完成后可直接打开结果视频 +
+
+ 失败/异常 + {failedCount} + 建议进入详情页查看供应商回执与报错 +
+
+
- {tasksQuery.data?.map((task) => ( -
+ {tasks.map((task) => ( +
{task.taskNo} -
+
{dayjs(task.createdAt).format("YYYY-MM-DD HH:mm:ss")} · {task.durationSeconds} s
@@ -59,15 +89,19 @@ export default function TasksPage() { 查看详情 {task.resultVideoUrl ? ( - + 打开视频 ) : null}
-
+
))}
); } - diff --git a/frontend-web/src/app/admin/(secure)/dashboard/page.tsx b/frontend-web/src/app/admin/(secure)/dashboard/page.tsx index 531601f..48e2aef 100644 --- a/frontend-web/src/app/admin/(secure)/dashboard/page.tsx +++ b/frontend-web/src/app/admin/(secure)/dashboard/page.tsx @@ -17,26 +17,118 @@ export default function DashboardPage() { }); const data = dashboardQuery.data; + const summaryCards = [ + { + label: "用户总数", + value: data?.users ?? 0, + copy: "平台已创建账户数", + }, + { + label: "已支付订单", + value: data?.paidOrders ?? 0, + copy: "已完成支付的充值订单", + }, + { + label: "任务总数", + value: data?.tasks ?? 0, + copy: "全链路视频任务累计数量", + }, + { + label: "成功率", + value: `${data?.successRate ?? 0}%`, + copy: "基于任务完成状态的成功占比", + }, + ]; return ( -
-
-

用户总数

-
{data?.users ?? 0}
-
-
-

已支付订单

-
{data?.paidOrders ?? 0}
-
-
-

任务总数

-
{data?.tasks ?? 0}
-
-
-

成功率

-
{data?.successRate ?? 0}%
-
-
+
+
+
+ Control overview +

统一观察用户经济、模型路由和任务健康度

+

+ 当前后台适合快速联调 MVP:前台创作流、积分系统、邀请奖励、模型供应商和任务轮询都能在这里找到对应控制面。 +

+
+
+
+ 业务主线 + 用户 + 任务 + 钱包 + 先看活跃链路是否闭环,再处理局部异常。 +
+
+ 路由主线 + 平台模型 -> 供应商模型 + 优先保证模型绑定、价格和回调链路一致。 +
+
+ 运行主线 + 本地 mock / 源码模式 + 适合先打磨前后台交互和管理体验。 +
+
+
+ +
+ {summaryCards.map((card) => ( +
+

{card.label}

+
{card.value}
+

{card.copy}

+
+ ))} +
+ +
+
+
+
+

今日关注

+

建议按下面的顺序巡检后台核心链路。

+
+
+
+
+ 1. 模型绑定和价格规则 + 保证前台展示模型和供应商实际路由保持一致,避免定价与能力错位。 +
+
+ 2. 任务状态与回调日志 + 遇到异常任务优先看回调载荷、最终状态和结果转存是否成功。 +
+
+ 3. 充值、兑换码和奖励关系 + 用户经济系统最好作为统一链路检查,而不是拆散在多个页面里看。 +
+
+
+ +
+
+
+

运维视角

+

用三张小卡片快速定位应该先进哪个模块。

+
+
+
+
+ 增长 + 用户 / 奖励 / 邀请 + 去用户管理、增长奖励、邀请关系。 +
+
+ 供应商 + 账号 / 模型 / 绑定 + 去供应商账号、供应商模型、模型绑定。 +
+
+ 资金与产出 + 订单 / 价格 / 任务 + 去充值订单、价格规则、视频任务。 +
+
+
+
+
); } - diff --git a/frontend-web/src/app/admin/login/page.tsx b/frontend-web/src/app/admin/login/page.tsx index 36ba4d7..896e2db 100644 --- a/frontend-web/src/app/admin/login/page.tsx +++ b/frontend-web/src/app/admin/login/page.tsx @@ -1,6 +1,8 @@ "use client"; import { startTransition, useState } from "react"; +import { ArrowRight, Blocks, Coins, Settings2, ShieldCheck } from "lucide-react"; +import Link from "next/link"; import { useRouter } from "next/navigation"; import { api, ApiError } from "@/lib/api"; @@ -29,23 +31,58 @@ export default function AdminLoginPage() { } return ( -
-
-
+
+
+
Ops Console
-

- 管好模型、价格、奖励、订单和任务链路。 -

-

- 后台聚焦两件事:配置业务规则,以及处理异常链路。默认已创建管理员账号,适合直接联调整条 MVP。 +

把模型、价格、奖励、订单和任务链路收进一块控制台。

+

+ 视觉上参考 `Linear/Vercel` 式精密控制台,信息密度更高,但仍保留足够留白,让配置、审计和异常排查更好读。

+
+ Provider routing + Pricing control + Audit trail +
+
+
+
+
+ Admin access + Operational overview +
+

后台只做关键决策,不做噪音堆叠

+

+ 当前后台围绕四个维度组织:用户经济系统、模型与供应商、任务状态以及公共配置。适合直接联调 MVP。 +

+
+
+
+ + 模型控制面 + 平台模型、供应商模型和路由关系都在一个壳层里完成。 +
+
+ + 积分与价格系统 + 订单、兑换码、价格规则和奖励规则统一维护。 +
+
+ + 配置与审计 + 系统配置、任务回调和异常日志便于排查问题。 +
+
-
-
AIVideo Admin
-

管理员登录

+ +
+
AIVideo Admin
+

管理员登录

+

进入运营控制台,处理规则配置和异常链路。

+
- {error ?
{error}
: null} + {error ?
{error}
: null} +
+ + 返回用户端 + +
+ + 默认管理员账号已由后端初始化脚本自动创建。 +
+
); } - diff --git a/frontend-web/src/app/fonts.ts b/frontend-web/src/app/fonts.ts new file mode 100644 index 0000000..bc10ee2 --- /dev/null +++ b/frontend-web/src/app/fonts.ts @@ -0,0 +1,17 @@ +import { IBM_Plex_Mono, Manrope, Syne } from "next/font/google"; + +export const displayFont = Syne({ + subsets: ["latin"], + variable: "--font-display", +}); + +export const bodyFont = Manrope({ + subsets: ["latin"], + variable: "--font-body", +}); + +export const monoFont = IBM_Plex_Mono({ + subsets: ["latin"], + variable: "--font-mono", + weight: ["400", "500", "600"], +}); diff --git a/frontend-web/src/app/globals.css b/frontend-web/src/app/globals.css index 42d59a7..736a7e5 100644 --- a/frontend-web/src/app/globals.css +++ b/frontend-web/src/app/globals.css @@ -1,29 +1,33 @@ @import "tailwindcss"; :root { - --font-display: "Bahnschrift", "Segoe UI", "PingFang SC", sans-serif; - --font-body: "Microsoft YaHei UI", "PingFang SC", "Noto Sans SC", sans-serif; - --bg: #090b12; - --bg-soft: #101521; - --bg-elevated: #171d2d; - --surface: rgba(17, 22, 35, 0.88); - --surface-strong: rgba(24, 31, 48, 0.98); - --surface-muted: rgba(13, 17, 27, 0.92); - --ink: #f5f7fb; - --muted: #97a3bb; - --line: rgba(151, 163, 187, 0.16); - --line-strong: rgba(135, 196, 255, 0.32); - --accent: #4ea9ff; - --accent-strong: #7fd6ff; - --accent-soft: rgba(78, 169, 255, 0.14); - --success: #68d391; - --warn: #f0b354; - --danger: #f07f7f; - --shadow: 0 28px 90px rgba(0, 0, 0, 0.4); + --bg: #06070a; + --bg-soft: #0d1015; + --bg-elevated: #141821; + --surface: rgba(12, 16, 22, 0.82); + --surface-strong: rgba(16, 20, 27, 0.92); + --surface-soft: rgba(255, 255, 255, 0.04); + --surface-highlight: rgba(255, 255, 255, 0.08); + --ink: #f5f0e6; + --ink-soft: rgba(245, 240, 230, 0.84); + --muted: #98a1aa; + --line: rgba(255, 255, 255, 0.08); + --line-strong: rgba(255, 255, 255, 0.18); + --accent: #ff8d5d; + --accent-strong: #ffd764; + --accent-cool: #7ed3ff; + --accent-soft: rgba(255, 141, 93, 0.14); + --success: #75e0a7; + --warn: #ffca78; + --danger: #ff9696; + --shadow: 0 36px 80px rgba(0, 0, 0, 0.42); + --shadow-soft: 0 18px 44px rgba(0, 0, 0, 0.26); } .app-fonts { - font-family: var(--font-body); + font-family: var(--font-body), "PingFang SC", "Microsoft YaHei UI", sans-serif; + font-synthesis: none; + text-rendering: optimizeLegibility; } * { @@ -35,15 +39,43 @@ body { min-height: 100%; } +html { + background: var(--bg); +} + body { margin: 0; color: var(--ink); - font-family: var(--font-body), sans-serif; background: - radial-gradient(circle at top left, rgba(53, 112, 255, 0.18), transparent 26%), - radial-gradient(circle at top right, rgba(12, 132, 92, 0.12), transparent 24%), - radial-gradient(circle at bottom center, rgba(92, 51, 174, 0.12), transparent 30%), - linear-gradient(180deg, #0a0d15 0%, #080a10 100%); + radial-gradient(circle at 14% 16%, rgba(255, 141, 93, 0.18), transparent 28%), + radial-gradient(circle at 84% 12%, rgba(126, 211, 255, 0.16), transparent 24%), + radial-gradient(circle at 50% 100%, rgba(255, 215, 100, 0.12), transparent 22%), + linear-gradient(180deg, #07080b 0%, #090b10 48%, #050608 100%); + overflow-x: hidden; +} + +body::before, +body::after { + content: ""; + position: fixed; + inset: 0; + pointer-events: none; +} + +body::before { + background: + radial-gradient(circle at 10% 20%, rgba(255, 141, 93, 0.08), transparent 18%), + radial-gradient(circle at 80% 16%, rgba(126, 211, 255, 0.08), transparent 18%), + linear-gradient(90deg, rgba(255, 255, 255, 0.018) 1px, transparent 1px), + linear-gradient(rgba(255, 255, 255, 0.018) 1px, transparent 1px); + background-size: auto, auto, 80px 80px, 80px 80px; + opacity: 0.6; +} + +body::after { + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.04), transparent 18%), + radial-gradient(circle at center, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.45)); } a { @@ -65,7 +97,7 @@ button:disabled { input::placeholder, textarea::placeholder { - color: rgba(151, 163, 187, 0.74); + color: rgba(152, 161, 170, 0.68); } .spin { @@ -89,63 +121,219 @@ textarea::placeholder { padding: 24px; } -.auth-grid { - min-height: 100vh; - display: grid; - grid-template-columns: 1.12fr 0.88fr; - background: - linear-gradient(160deg, rgba(10, 13, 21, 0.98) 0%, rgba(8, 10, 16, 0.98) 100%); -} - +.auth-grid, .login-grid { min-height: 100vh; display: grid; - grid-template-columns: 1fr 440px; + position: relative; + isolation: isolate; +} + +.auth-grid { + grid-template-columns: minmax(0, 1.08fr) minmax(420px, 0.92fr); +} + +.login-grid { + grid-template-columns: minmax(0, 1fr) 480px; +} + +.auth-grid-admin { + background: + radial-gradient(circle at 16% 16%, rgba(126, 211, 255, 0.12), transparent 24%), + linear-gradient(135deg, rgba(7, 9, 12, 0.94), rgba(10, 13, 18, 0.98)); } .auth-hero { - padding: 56px; + padding: clamp(28px, 5vw, 56px); display: flex; flex-direction: column; justify-content: space-between; + gap: 28px; + position: relative; + overflow: hidden; +} + +.auth-hero::before { + content: ""; + position: absolute; + inset: 26px; + border-radius: 36px; + border: 1px solid rgba(255, 255, 255, 0.06); + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.015)); + backdrop-filter: blur(18px); + box-shadow: var(--shadow-soft); +} + +.auth-hero-studio::after, +.auth-hero-admin::after { + content: ""; + position: absolute; + width: 260px; + height: 260px; + border-radius: 999px; + right: -40px; + bottom: -60px; + filter: blur(10px); +} + +.auth-hero-studio::after { + background: radial-gradient(circle at 30% 30%, rgba(255, 141, 93, 0.34), transparent 68%); +} + +.auth-hero-admin::after { + background: radial-gradient(circle at 30% 30%, rgba(126, 211, 255, 0.28), transparent 68%); +} + +.auth-copy, +.showcase-stack { + position: relative; + z-index: 1; +} + +.auth-copy { + display: grid; + gap: 18px; + max-width: 680px; } .auth-card { - width: min(460px, calc(100vw - 32px)); - margin: auto; + width: min(500px, calc(100vw - 32px)); padding: 32px; - border: 1px solid var(--line); - border-radius: 28px; - background: rgba(16, 21, 33, 0.94); + border-radius: 32px; + border: 1px solid rgba(255, 255, 255, 0.08); + background: + linear-gradient(180deg, rgba(18, 22, 29, 0.9), rgba(11, 14, 19, 0.96)); box-shadow: var(--shadow); + backdrop-filter: blur(20px); + position: relative; + overflow: hidden; +} + +.auth-card::before, +.panel::before, +.profile-card::before, +.stat-card::before, +.pulse-card::before { + content: ""; + position: absolute; + inset: 0 0 auto; + height: 1px; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.24), transparent); +} + +.auth-card-strong { + background: + radial-gradient(circle at top right, rgba(126, 211, 255, 0.08), transparent 26%), + linear-gradient(180deg, rgba(18, 22, 29, 0.96), rgba(11, 14, 19, 0.98)); +} + +.auth-card-head { + display: grid; + gap: 8px; + margin-bottom: 24px; +} + +.auth-card-head h2, +.auth-card-head h3 { + margin: 0; + font-size: clamp(28px, 4vw, 38px); + line-height: 0.98; + font-family: var(--font-display); } .eyebrow, .brand-kicker, -.header-kicker { +.header-kicker, +.sidebar-note-label { letter-spacing: 0.18em; text-transform: uppercase; - color: var(--accent-strong); - font-size: 12px; + color: var(--accent-cool); + font-size: 11px; + font-family: var(--font-mono); } .headline { - margin: 10px 0 14px; - font-size: clamp(36px, 5vw, 70px); - line-height: 0.96; - font-family: var(--font-display), sans-serif; + margin: 0; + font-size: clamp(40px, 5vw, 80px); + line-height: 0.92; + letter-spacing: -0.04em; + font-family: var(--font-display); + max-width: 11ch; } .subcopy { - max-width: 560px; - line-height: 1.7; + max-width: 620px; + line-height: 1.72; color: var(--muted); + font-size: 15px; +} + +.auth-chip-row, +.brand-pill-row { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.auth-chip, +.brand-pill { + padding: 9px 12px; + border-radius: 999px; + border: 1px solid rgba(255, 255, 255, 0.08); + background: rgba(255, 255, 255, 0.05); + color: var(--ink-soft); + font-size: 11px; + letter-spacing: 0.08em; + text-transform: uppercase; + font-family: var(--font-mono); +} + +.showcase-stack { + display: grid; + gap: 16px; +} + +.showcase-card-grid { + display: grid; + gap: 14px; + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.showcase-card { + padding: 20px; + border-radius: 26px; + border: 1px solid rgba(255, 255, 255, 0.08); + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02)); + box-shadow: var(--shadow-soft); + display: grid; + gap: 12px; +} + +.showcase-card svg { + color: var(--accent-cool); +} + +.showcase-card strong { + display: block; + font-size: 15px; +} + +.showcase-card span { + color: var(--muted); + line-height: 1.65; + font-size: 13px; +} + +.showcase-card-hero { + min-height: 190px; + align-content: start; } .form-stack { display: grid; gap: 16px; - margin-top: 20px; } .field-label { @@ -162,12 +350,15 @@ textarea::placeholder { .library-search input { width: 100%; padding: 14px 16px; - border: 1px solid var(--line); - border-radius: 16px; + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 18px; color: var(--ink); - background: rgba(10, 14, 23, 0.88); + background: rgba(7, 9, 13, 0.72); outline: none; - transition: border-color 0.2s ease, background 0.2s ease; + transition: + border-color 0.2s ease, + background 0.2s ease, + transform 0.2s ease; } .field-label input:focus, @@ -175,8 +366,9 @@ textarea::placeholder { .field-label textarea:focus, .prompt-editor:focus, .library-search input:focus { - border-color: var(--line-strong); - background: rgba(16, 21, 33, 0.96); + border-color: rgba(126, 211, 255, 0.28); + background: rgba(12, 15, 20, 0.88); + transform: translateY(-1px); } .field-label textarea, @@ -189,14 +381,20 @@ textarea::placeholder { .ghost-button, .danger-button { border: 0; - border-radius: 16px; + border-radius: 18px; + min-height: 50px; padding: 14px 18px; display: inline-flex; align-items: center; justify-content: center; gap: 10px; cursor: pointer; - transition: transform 0.18s ease, opacity 0.18s ease, background 0.18s ease; + font-weight: 700; + transition: + transform 0.18s ease, + opacity 0.18s ease, + background 0.18s ease, + border-color 0.18s ease; } .primary-button:hover, @@ -206,67 +404,148 @@ textarea::placeholder { } .primary-button { - background: linear-gradient(135deg, #2f86ff 0%, #59c7ff 100%); - color: white; - box-shadow: 0 16px 40px rgba(47, 134, 255, 0.28); + color: #17120a; + background: linear-gradient(135deg, var(--accent) 0%, var(--accent-strong) 100%); + box-shadow: 0 18px 36px rgba(255, 141, 93, 0.24); } .ghost-button { - background: rgba(255, 255, 255, 0.05); - border: 1px solid var(--line); color: var(--ink); + background: rgba(255, 255, 255, 0.04); + border: 1px solid rgba(255, 255, 255, 0.08); } .danger-button { - background: rgba(240, 127, 127, 0.12); - border: 1px solid rgba(240, 127, 127, 0.22); color: var(--danger); + background: rgba(255, 150, 150, 0.1); + border: 1px solid rgba(255, 150, 150, 0.16); } .compact-button { min-height: 40px; padding: 10px 14px; + border-radius: 14px; font-size: 13px; } +.field-note { + display: inline-flex; + align-items: center; + gap: 10px; + padding: 12px 14px; + border-radius: 16px; + border: 1px solid rgba(255, 255, 255, 0.08); + background: rgba(255, 255, 255, 0.03); + color: var(--muted); + font-size: 13px; + line-height: 1.5; +} + +.auth-action-row { + display: grid; + gap: 14px; +} + .shell-grid { min-height: 100vh; display: grid; - grid-template-columns: 320px 1fr; + grid-template-columns: 320px minmax(0, 1fr); +} + +.shell-grid-admin { + grid-template-columns: 340px minmax(0, 1fr); } .shell-sidebar { - padding: 28px 22px; - border-right: 1px solid var(--line); - background: - linear-gradient(180deg, rgba(14, 18, 29, 0.98) 0%, rgba(9, 12, 20, 0.98) 100%); - display: flex; - flex-direction: column; - gap: 22px; position: sticky; top: 0; min-height: 100vh; + align-self: start; + padding: 28px 22px; + border-right: 1px solid rgba(255, 255, 255, 0.06); + background: + linear-gradient(180deg, rgba(10, 12, 16, 0.96), rgba(8, 10, 14, 0.92)); + backdrop-filter: blur(18px); + display: flex; + flex-direction: column; + gap: 20px; } .brand-block { - padding: 18px; - border-radius: 28px; - border: 1px solid rgba(127, 214, 255, 0.1); + position: relative; + overflow: hidden; + padding: 24px; + border-radius: 30px; + border: 1px solid rgba(255, 255, 255, 0.08); background: - linear-gradient(145deg, rgba(27, 38, 61, 0.96) 0%, rgba(12, 17, 28, 0.98) 100%); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04); + linear-gradient(150deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.02)); + box-shadow: var(--shadow-soft); +} + +.brand-block-studio { + background: + radial-gradient(circle at top right, rgba(255, 141, 93, 0.18), transparent 30%), + linear-gradient(150deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.02)); +} + +.brand-block-admin { + background: + radial-gradient(circle at top right, rgba(126, 211, 255, 0.16), transparent 32%), + linear-gradient(150deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.02)); +} + +.brand-visual { + position: absolute; + inset: -34px -26px auto auto; + width: 190px; + height: 190px; + pointer-events: none; +} + +.brand-orb { + position: absolute; + border-radius: 999px; +} + +.brand-orb-primary { + right: 8px; + top: 0; + width: 150px; + height: 150px; + background: radial-gradient(circle at 30% 30%, rgba(255, 215, 100, 0.9), rgba(255, 141, 93, 0.24) 54%, transparent 72%); + opacity: 0.6; +} + +.brand-orb-secondary { + left: 12px; + top: 56px; + width: 108px; + height: 108px; + background: radial-gradient(circle at 30% 30%, rgba(126, 211, 255, 0.82), rgba(126, 211, 255, 0.12) 58%, transparent 74%); + opacity: 0.72; +} + +.brand-block-admin .brand-orb-primary { + background: radial-gradient(circle at 30% 30%, rgba(126, 211, 255, 0.92), rgba(126, 211, 255, 0.18) 56%, transparent 72%); +} + +.brand-block-admin .brand-orb-secondary { + background: radial-gradient(circle at 30% 30%, rgba(255, 141, 93, 0.74), rgba(255, 141, 93, 0.12) 56%, transparent 72%); } .brand-block h1 { - margin: 8px 0 10px; - font-size: 34px; - font-family: var(--font-display), sans-serif; + margin: 12px 0 10px; + font-size: clamp(36px, 4vw, 46px); + line-height: 0.94; + letter-spacing: -0.03em; + font-family: var(--font-display); } .brand-block p { margin: 0; line-height: 1.7; color: var(--muted); + max-width: 24ch; } .brand-status { @@ -276,8 +555,9 @@ textarea::placeholder { gap: 10px; padding: 10px 14px; border-radius: 999px; + border: 1px solid rgba(255, 255, 255, 0.08); background: rgba(255, 255, 255, 0.04); - color: var(--ink); + color: var(--ink-soft); font-size: 13px; } @@ -286,7 +566,36 @@ textarea::placeholder { height: 8px; border-radius: 999px; background: var(--success); - box-shadow: 0 0 0 6px rgba(104, 211, 145, 0.12); + box-shadow: 0 0 0 6px rgba(117, 224, 167, 0.12); +} + +.sidebar-note { + padding: 18px; + border-radius: 24px; + border: 1px solid rgba(255, 255, 255, 0.08); + background: rgba(255, 255, 255, 0.03); + display: grid; + gap: 14px; +} + +.sidebar-metrics { + display: grid; + gap: 12px; +} + +.sidebar-metric { + display: grid; + gap: 4px; +} + +.sidebar-metric strong { + font-size: 13px; +} + +.sidebar-metric span { + color: var(--muted); + font-size: 13px; + line-height: 1.55; } .sidebar-nav { @@ -295,18 +604,26 @@ textarea::placeholder { } .nav-item { - display: flex; - align-items: flex-start; + display: grid; + grid-template-columns: 18px minmax(0, 1fr) auto; gap: 12px; - padding: 14px 16px; - border-radius: 18px; + align-items: start; + padding: 15px 16px; + border-radius: 22px; color: var(--muted); border: 1px solid transparent; - transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease; + background: rgba(255, 255, 255, 0.02); + transition: + background 0.18s ease, + border-color 0.18s ease, + color 0.18s ease, + transform 0.18s ease; } -.nav-item svg { - margin-top: 2px; +.nav-item:hover { + transform: translateX(3px); + border-color: rgba(255, 255, 255, 0.08); + background: rgba(255, 255, 255, 0.04); } .nav-item span { @@ -318,23 +635,35 @@ textarea::placeholder { .nav-item small { display: block; margin-top: 4px; - color: var(--muted); + color: rgba(245, 240, 230, 0.56); line-height: 1.45; + font-size: 12px; } .nav-item.active { - background: linear-gradient(135deg, rgba(47, 134, 255, 0.16), rgba(89, 199, 255, 0.08)); - border-color: rgba(127, 214, 255, 0.18); + background: linear-gradient(135deg, rgba(255, 141, 93, 0.16), rgba(126, 211, 255, 0.14)); + border-color: rgba(255, 255, 255, 0.16); +} + +.nav-index { + padding-top: 4px; + color: rgba(245, 240, 230, 0.42); + font-size: 11px; + font-family: var(--font-mono); + letter-spacing: 0.08em; } .profile-card, .panel, .stat-card, .pulse-card { - border: 1px solid var(--line); - border-radius: 26px; - background: var(--surface); - backdrop-filter: blur(16px); + position: relative; + overflow: hidden; + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 28px; + background: + linear-gradient(180deg, rgba(13, 16, 22, 0.88), rgba(9, 12, 17, 0.95)); + backdrop-filter: blur(18px); box-shadow: var(--shadow); } @@ -351,20 +680,20 @@ textarea::placeholder { } .profile-avatar { - width: 48px; - height: 48px; + width: 54px; + height: 54px; display: grid; place-items: center; - border-radius: 18px; - background: linear-gradient(135deg, rgba(47, 134, 255, 0.28), rgba(89, 199, 255, 0.16)); - border: 1px solid rgba(127, 214, 255, 0.16); + border-radius: 20px; + background: linear-gradient(135deg, rgba(255, 141, 93, 0.22), rgba(126, 211, 255, 0.16)); + border: 1px solid rgba(255, 255, 255, 0.1); font-weight: 800; letter-spacing: 0.06em; } .profile-avatar-large { - width: 64px; - height: 64px; + width: 68px; + height: 68px; } .profile-name { @@ -376,28 +705,61 @@ textarea::placeholder { font-size: 14px; } +.profile-card-stats { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.profile-stat { + padding: 12px 14px; + border-radius: 18px; + border: 1px solid rgba(255, 255, 255, 0.06); + background: rgba(255, 255, 255, 0.03); + display: grid; + gap: 4px; +} + +.profile-stat span { + color: var(--muted); + font-size: 12px; +} + +.profile-stat strong { + font-family: var(--font-mono); + font-size: 14px; +} + .shell-main { - padding: 24px; + padding: 30px 32px 42px; } .shell-header { display: flex; justify-content: space-between; align-items: flex-start; - gap: 16px; + gap: 18px; +} + +.shell-header-content { + display: grid; + gap: 6px; + max-width: 760px; } .shell-header h2 { - margin: 6px 0 8px; - font-size: 34px; - font-family: var(--font-display), sans-serif; + margin: 0; + font-size: clamp(36px, 4vw, 58px); + line-height: 0.92; + letter-spacing: -0.04em; + font-family: var(--font-display); } .shell-header-copy { margin: 0; - max-width: 620px; + max-width: 680px; color: var(--muted); - line-height: 1.65; + line-height: 1.68; } .shell-header-meta { @@ -405,6 +767,8 @@ textarea::placeholder { align-items: center; gap: 10px; flex-wrap: wrap; + justify-content: flex-end; + max-width: 460px; } .header-chip { @@ -413,20 +777,21 @@ textarea::placeholder { display: inline-flex; align-items: center; gap: 8px; - background: rgba(255, 255, 255, 0.05); - border: 1px solid var(--line); - color: var(--muted); - font-size: 13px; + background: rgba(255, 255, 255, 0.04); + border: 1px solid rgba(255, 255, 255, 0.08); + color: var(--ink-soft); + font-size: 12px; + font-family: var(--font-mono); } .header-chip-success { - color: #baf5cf; - background: rgba(104, 211, 145, 0.1); - border-color: rgba(104, 211, 145, 0.16); + color: #d7fbea; + background: rgba(117, 224, 167, 0.08); + border-color: rgba(117, 224, 167, 0.18); } .shell-content { - margin-top: 22px; + margin-top: 24px; display: grid; gap: 18px; } @@ -443,7 +808,9 @@ textarea::placeholder { .workbench-grid, .studio-summary-grid, .studio-control-grid, -.media-slot-grid { +.media-slot-grid, +.task-stat-band, +.dashboard-stage-grid { display: grid; gap: 18px; } @@ -453,7 +820,7 @@ textarea::placeholder { } .stats-grid { - grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); } .two-col-grid { @@ -461,7 +828,7 @@ textarea::placeholder { } .workbench-grid { - grid-template-columns: minmax(0, 1.08fr) minmax(360px, 0.92fr); + grid-template-columns: minmax(0, 1.06fr) minmax(360px, 0.94fr); align-items: start; } @@ -480,13 +847,14 @@ textarea::placeholder { .workbench-header h3, .auth-card h3 { margin: 0 0 12px; - font-size: 24px; - font-family: var(--font-display), sans-serif; + font-size: 25px; + font-family: var(--font-display); } .stat-card .value { - font-size: 34px; - font-family: var(--font-display), sans-serif; + font-size: 36px; + line-height: 1; + font-family: var(--font-display); } .muted { @@ -505,16 +873,19 @@ textarea::placeholder { justify-content: space-between; } -.list-grid { +.list-grid, +.ops-feed, +.admin-dashboard-stack { display: grid; gap: 12px; } -.list-item { - padding: 16px; - border-radius: 18px; - border: 1px solid var(--line); - background: rgba(255, 255, 255, 0.04); +.list-item, +.ops-feed-item { + padding: 18px; + border-radius: 22px; + border: 1px solid rgba(255, 255, 255, 0.06); + background: linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.025)); } .list-item strong { @@ -531,54 +902,58 @@ textarea::placeholder { .status-badge { display: inline-flex; align-items: center; - padding: 6px 10px; + padding: 7px 10px; border-radius: 999px; - font-size: 12px; + font-size: 11px; font-weight: 700; text-transform: uppercase; + font-family: var(--font-mono); + letter-spacing: 0.08em; } .tone-soft { - background: rgba(151, 163, 187, 0.12); - color: #ced8ea; + background: rgba(255, 255, 255, 0.08); + color: #ddd5c8; } .tone-success { - background: rgba(104, 211, 145, 0.14); - color: var(--success); + background: rgba(117, 224, 167, 0.14); + color: #cbf6de; } .tone-warn { - background: rgba(240, 179, 84, 0.14); - color: var(--warn); + background: rgba(255, 202, 120, 0.14); + color: #ffe2a6; } .tone-danger { - background: rgba(240, 127, 127, 0.14); - color: var(--danger); + background: rgba(255, 150, 150, 0.14); + color: #ffd1d1; } .tone-ghost { - background: rgba(151, 163, 187, 0.08); + background: rgba(152, 161, 170, 0.12); color: var(--muted); } .media-preview { width: 100%; - border-radius: 18px; - border: 1px solid var(--line); - background: #06080c; + border-radius: 22px; + border: 1px solid rgba(255, 255, 255, 0.08); + background: #040507; } .code-block { margin: 0; padding: 16px; - border-radius: 18px; - background: #080b12; - border: 1px solid var(--line); - color: #d7e1ff; + border-radius: 20px; + background: #0a0d12; + border: 1px solid rgba(255, 255, 255, 0.08); + color: #e8dfd0; overflow: auto; font-size: 13px; + font-family: var(--font-mono); + line-height: 1.7; } .studio-panel, @@ -587,7 +962,8 @@ textarea::placeholder { overflow: hidden; } -.workbench-header { +.workbench-header, +.library-header { display: flex; justify-content: space-between; align-items: flex-start; @@ -595,20 +971,48 @@ textarea::placeholder { margin-bottom: 20px; } +.studio-command-bar { + display: grid; + gap: 12px; + grid-template-columns: repeat(4, minmax(0, 1fr)); + margin-bottom: 18px; +} + +.studio-command-pill { + padding: 16px 18px; + border-radius: 20px; + border: 1px solid rgba(255, 255, 255, 0.08); + background: rgba(255, 255, 255, 0.04); + color: var(--muted); + display: grid; + gap: 8px; + font-size: 11px; + letter-spacing: 0.12em; + text-transform: uppercase; + font-family: var(--font-mono); +} + +.studio-command-pill strong { + color: var(--ink); + font-size: 15px; + line-height: 1.45; + letter-spacing: 0; + text-transform: none; + font-family: var(--font-body); +} + .studio-stat { padding: 18px; - border-radius: 22px; - border: 1px solid var(--line); - background: linear-gradient(160deg, rgba(31, 41, 64, 0.88), rgba(15, 20, 32, 0.94)); -} - -.studio-stat span, -.studio-stat small { - display: block; + border-radius: 24px; + border: 1px solid rgba(255, 255, 255, 0.08); + background: + linear-gradient(160deg, rgba(32, 39, 50, 0.86), rgba(14, 17, 24, 0.94)); } .studio-stat span, +.studio-stat small, .mini-note { + display: block; color: var(--muted); font-size: 13px; } @@ -617,13 +1021,14 @@ textarea::placeholder { display: block; margin: 14px 0 10px; font-size: 30px; - font-family: var(--font-display), sans-serif; + line-height: 1; + font-family: var(--font-display); } .control-block { padding: 18px; - border-radius: 22px; - border: 1px solid var(--line); + border-radius: 24px; + border: 1px solid rgba(255, 255, 255, 0.08); background: rgba(255, 255, 255, 0.03); } @@ -647,19 +1052,25 @@ textarea::placeholder { .model-card { width: 100%; - border: 1px solid var(--line); - border-radius: 20px; + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 22px; padding: 16px; - background: linear-gradient(160deg, rgba(12, 17, 28, 0.96), rgba(25, 34, 53, 0.88)); + background: linear-gradient(160deg, rgba(10, 13, 18, 0.94), rgba(23, 28, 36, 0.88)); color: var(--ink); text-align: left; cursor: pointer; + transition: transform 0.18s ease, border-color 0.18s ease, background 0.18s ease; +} + +.model-card:hover { + transform: translateY(-1px); } .model-card.active { - border-color: rgba(127, 214, 255, 0.32); - background: linear-gradient(160deg, rgba(29, 63, 116, 0.92), rgba(18, 29, 52, 0.96)); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06); + border-color: rgba(255, 255, 255, 0.18); + background: + linear-gradient(160deg, rgba(64, 42, 22, 0.92), rgba(23, 28, 36, 0.96)); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07); } .model-card-head { @@ -698,12 +1109,13 @@ textarea::placeholder { padding: 6px 10px; border-radius: 999px; background: rgba(255, 255, 255, 0.06); - color: #d4ebff; + color: var(--ink-soft); font-size: 12px; + font-family: var(--font-mono); } .prompt-editor { - min-height: 180px; + min-height: 200px; } .segmented-group { @@ -713,26 +1125,27 @@ textarea::placeholder { } .segmented-button { - min-height: 40px; - border: 1px solid var(--line); - border-radius: 14px; + min-height: 42px; + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 16px; padding: 10px 14px; background: rgba(255, 255, 255, 0.03); color: var(--muted); cursor: pointer; + transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease; } .segmented-button.active { - background: linear-gradient(135deg, rgba(47, 134, 255, 0.18), rgba(89, 199, 255, 0.12)); - border-color: rgba(127, 214, 255, 0.22); - color: white; + background: linear-gradient(135deg, rgba(255, 141, 93, 0.18), rgba(126, 211, 255, 0.12)); + border-color: rgba(255, 255, 255, 0.14); + color: var(--ink); } .media-slot { padding: 16px; - border-radius: 20px; - border: 1px solid var(--line); - background: rgba(8, 11, 18, 0.7); + border-radius: 22px; + border: 1px solid rgba(255, 255, 255, 0.08); + background: rgba(255, 255, 255, 0.03); } .media-slot-head { @@ -759,10 +1172,10 @@ textarea::placeholder { display: grid; gap: 4px; min-width: 0; - border: 1px solid rgba(127, 214, 255, 0.16); - border-radius: 14px; + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 16px; padding: 10px 12px; - background: rgba(47, 134, 255, 0.08); + background: rgba(255, 141, 93, 0.08); color: var(--ink); cursor: pointer; text-align: left; @@ -784,7 +1197,7 @@ textarea::placeholder { border: 0; padding: 0; background: none; - color: var(--accent-strong); + color: var(--accent-cool); cursor: pointer; } @@ -794,7 +1207,7 @@ textarea::placeholder { gap: 14px; padding: 18px; border-radius: 22px; - border: 1px solid var(--line); + border: 1px solid rgba(255, 255, 255, 0.08); background: rgba(255, 255, 255, 0.03); } @@ -819,20 +1232,20 @@ textarea::placeholder { padding: 14px 16px; border-radius: 16px; background: rgba(255, 255, 255, 0.04); - border: 1px solid var(--line); + border: 1px solid rgba(255, 255, 255, 0.08); color: var(--muted); } .inline-feedback.is-success { - border-color: rgba(104, 211, 145, 0.18); - background: rgba(104, 211, 145, 0.08); - color: #c8f3d5; + border-color: rgba(117, 224, 167, 0.18); + background: rgba(117, 224, 167, 0.08); + color: #d7fbea; } .inline-feedback.is-error { - border-color: rgba(240, 127, 127, 0.18); - background: rgba(240, 127, 127, 0.08); - color: #ffd7d7; + border-color: rgba(255, 150, 150, 0.18); + background: rgba(255, 150, 150, 0.08); + color: #ffd8d8; } .composer-footer { @@ -845,10 +1258,6 @@ textarea::placeholder { } .library-header { - display: flex; - justify-content: space-between; - align-items: flex-start; - gap: 16px; margin-bottom: 20px; } @@ -860,25 +1269,25 @@ textarea::placeholder { .tab-chip { padding: 10px 14px; - border-radius: 14px; - border: 1px solid var(--line); + border-radius: 16px; + border: 1px solid rgba(255, 255, 255, 0.08); background: rgba(255, 255, 255, 0.03); color: var(--muted); font-size: 13px; } .tab-chip.active { - background: rgba(47, 134, 255, 0.16); - color: white; - border-color: rgba(127, 214, 255, 0.2); + background: rgba(255, 141, 93, 0.12); + color: var(--ink); + border-color: rgba(255, 255, 255, 0.14); } .asset-upload-panel, .recent-task-strip { margin-bottom: 18px; padding: 18px; - border-radius: 22px; - border: 1px solid var(--line); + border-radius: 24px; + border: 1px solid rgba(255, 255, 255, 0.08); background: rgba(255, 255, 255, 0.03); } @@ -890,14 +1299,14 @@ textarea::placeholder { } .file-picker { - min-height: 52px; + min-height: 54px; display: flex; align-items: center; gap: 12px; padding: 0 16px; - border-radius: 16px; - border: 1px dashed rgba(127, 214, 255, 0.22); - background: rgba(10, 14, 23, 0.7); + border-radius: 18px; + border: 1px dashed rgba(126, 211, 255, 0.22); + background: rgba(7, 9, 13, 0.72); color: var(--muted); cursor: pointer; } @@ -928,9 +1337,9 @@ textarea::placeholder { align-items: center; gap: 10px; padding-left: 14px; - border-radius: 16px; - border: 1px solid var(--line); - background: rgba(10, 14, 23, 0.8); + border-radius: 18px; + border: 1px solid rgba(255, 255, 255, 0.08); + background: rgba(7, 9, 13, 0.72); } .library-search input { @@ -946,13 +1355,13 @@ textarea::placeholder { .asset-library-card { overflow: hidden; - border-radius: 22px; - border: 1px solid var(--line); - background: linear-gradient(160deg, rgba(12, 17, 28, 0.96), rgba(18, 24, 36, 0.92)); + border-radius: 24px; + border: 1px solid rgba(255, 255, 255, 0.08); + background: linear-gradient(160deg, rgba(10, 13, 18, 0.94), rgba(18, 22, 30, 0.9)); } .asset-library-card.selected { - border-color: rgba(127, 214, 255, 0.26); + border-color: rgba(255, 255, 255, 0.14); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04); } @@ -979,9 +1388,9 @@ textarea::placeholder { .asset-thumb-audio { gap: 10px; - color: var(--accent-strong); + color: var(--accent-cool); background: - radial-gradient(circle at top, rgba(47, 134, 255, 0.2), transparent 52%), + radial-gradient(circle at top, rgba(126, 211, 255, 0.18), transparent 52%), rgba(8, 11, 18, 0.96); } @@ -991,9 +1400,10 @@ textarea::placeholder { left: 12px; padding: 6px 10px; border-radius: 999px; - background: rgba(9, 12, 20, 0.8); - border: 1px solid rgba(127, 214, 255, 0.18); + background: rgba(9, 12, 20, 0.82); + border: 1px solid rgba(255, 255, 255, 0.12); font-size: 12px; + font-family: var(--font-mono); } .asset-card-body { @@ -1023,7 +1433,7 @@ textarea::placeholder { display: inline-flex; align-items: center; gap: 4px; - color: #c7eeff; + color: #ffe3be; font-size: 12px; white-space: nowrap; } @@ -1041,8 +1451,8 @@ textarea::placeholder { display: grid; gap: 12px; padding: 16px; - border-radius: 18px; - border: 1px solid var(--line); + border-radius: 20px; + border: 1px solid rgba(255, 255, 255, 0.06); background: rgba(255, 255, 255, 0.04); } @@ -1061,9 +1471,9 @@ textarea::placeholder { } .empty-state { - padding: 24px; - border-radius: 18px; - border: 1px dashed rgba(151, 163, 187, 0.2); + padding: 26px; + border-radius: 20px; + border: 1px dashed rgba(255, 255, 255, 0.12); background: rgba(255, 255, 255, 0.03); color: var(--muted); text-align: center; @@ -1075,11 +1485,119 @@ textarea::placeholder { gap: 14px; } +.task-stat-band { + grid-template-columns: repeat(4, minmax(0, 1fr)); + margin-top: 20px; +} + +.task-stat-card { + padding: 18px; + border-radius: 22px; + border: 1px solid rgba(255, 255, 255, 0.08); + background: rgba(255, 255, 255, 0.03); +} + +.task-stat-card span { + display: block; + color: var(--muted); + font-size: 11px; + letter-spacing: 0.1em; + text-transform: uppercase; + font-family: var(--font-mono); +} + +.task-stat-card strong { + display: block; + margin: 14px 0 10px; + font-size: 30px; + line-height: 1; + font-family: var(--font-display); +} + +.task-stat-card small { + color: var(--muted); + line-height: 1.6; +} + +.task-stream-card { + background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.025)); +} + +.task-stream-meta { + font-family: var(--font-mono); + font-size: 12px; +} + +.dashboard-stage { + display: grid; + gap: 18px; + grid-template-columns: 1.1fr 0.9fr; + align-items: stretch; +} + +.dashboard-stage-copy { + display: grid; + gap: 10px; +} + +.dashboard-stage-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.dashboard-mini-card { + padding: 18px; + border-radius: 24px; + border: 1px solid rgba(255, 255, 255, 0.08); + background: rgba(255, 255, 255, 0.03); + display: grid; + gap: 8px; +} + +.dashboard-mini-card span { + color: var(--muted); + font-size: 11px; + letter-spacing: 0.1em; + text-transform: uppercase; + font-family: var(--font-mono); +} + +.dashboard-mini-card strong { + font-size: 18px; +} + +.dashboard-mini-card small { + color: var(--muted); + line-height: 1.55; +} + +.ops-feed-item { + display: grid; + gap: 6px; +} + +.ops-feed-item strong { + font-size: 15px; +} + +.ops-feed-item span { + color: var(--muted); + line-height: 1.65; +} + +@media (max-width: 1400px) { + .studio-command-bar, + .task-stat-band, + .dashboard-stage-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + @media (max-width: 1320px) { .workbench-grid, .two-col-grid, .studio-control-grid, - .media-slot-grid { + .media-slot-grid, + .dashboard-stage { grid-template-columns: 1fr; } @@ -1088,10 +1606,11 @@ textarea::placeholder { } } -@media (max-width: 1100px) { +@media (max-width: 1120px) { .auth-grid, .login-grid, - .shell-grid { + .shell-grid, + .shell-grid-admin { grid-template-columns: 1fr; } @@ -1099,11 +1618,15 @@ textarea::placeholder { position: static; min-height: auto; border-right: 0; - border-bottom: 1px solid var(--line); + border-bottom: 1px solid rgba(255, 255, 255, 0.06); } .shell-main { - padding: 18px; + padding: 20px 18px 34px; + } + + .showcase-card-grid { + grid-template-columns: 1fr; } } @@ -1113,7 +1636,8 @@ textarea::placeholder { .shell-main, .panel, .stat-card, - .pulse-card { + .pulse-card, + .auth-card { padding: 18px; } @@ -1123,11 +1647,20 @@ textarea::placeholder { flex-direction: column; } - .upload-row { + .upload-row, + .task-stat-band, + .studio-command-bar, + .dashboard-stage-grid, + .profile-card-stats { grid-template-columns: 1fr; } .asset-gallery { grid-template-columns: 1fr; } + + .headline { + max-width: none; + font-size: clamp(34px, 12vw, 58px); + } } diff --git a/frontend-web/src/app/layout.tsx b/frontend-web/src/app/layout.tsx index ad039f0..55ccd69 100644 --- a/frontend-web/src/app/layout.tsx +++ b/frontend-web/src/app/layout.tsx @@ -1,12 +1,13 @@ import type { Metadata } from "next"; +import { bodyFont, displayFont, monoFont } from "@/app/fonts"; import { Providers } from "@/components/providers"; import "./globals.css"; export const metadata: Metadata = { title: "AIVideo", - description: "AI 视频生成平台用户前台", + description: "AIVideo AI 视频创作与运营控制台", }; export default function RootLayout({ @@ -15,7 +16,10 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - + {children} diff --git a/frontend-web/src/app/login/page.tsx b/frontend-web/src/app/login/page.tsx index da4c48e..9c6496f 100644 --- a/frontend-web/src/app/login/page.tsx +++ b/frontend-web/src/app/login/page.tsx @@ -1,6 +1,7 @@ "use client"; import { startTransition, useState } from "react"; +import { ArrowRight, Clapperboard, Coins, FolderKanban, Sparkles } from "lucide-react"; import Link from "next/link"; import { useRouter } from "next/navigation"; @@ -28,27 +29,57 @@ export default function LoginPage() { return (
-
-
+
+
Creative Workbench
-

从提示词到成片,完整跑通你的 AI 视频生产线。

+

从一句提示词到成片交付,把 AI 视频生产线收进一块工作台。

- 支持充值积分、兑换密钥、素材上传、供应商模型路由和任务轮询。当前默认已接入本地 mock - OpenAI / Seedance 流程,开箱即可联调。 + 参考 `RunwayML` 式的电影感操作界面,把模型切换、素材引用、积分消耗和任务轮询收敛为一个连续动作。

+
+ Prompt to video + Asset-aware workflow + Realtime task polling +
-
-

默认体验方式

-

- 如果还没有账号,直接去注册。注册成功后会自动发放注册奖励积分。 -

+
+
+
+ Studio ready + Source mode +
+

一次登录,接管创作链路

+

+ 默认即可走本地 mock 供应商、SQLite 和本地存储,适合先把完整交互、任务状态和后台规则跑通。 +

+
+
+
+ + 工作台直达成片 + 模型、时长、比例与素材引用在同一视图完成。 +
+
+ + 任务状态持续刷新 + 提交后自动轮询,成功后直接打开结果视频。 +
+
+ + 积分链路完整 + 注册奖励、充值、兑换码和冻结扣费全部可见。 +
+
-
-
Welcome Back
-

登录 AIVideo

+ +
+
Welcome Back
+

登录 AIVideo

+

继续你的创作队列、素材库和积分操作。

+
- {error ?
{error}
: null} + {error ?
{error}
: null} - - 去注册 - +
+ + 创建账号 + +
+ + 新用户注册后可按后台规则领取初始积分。 +
+
); } - diff --git a/frontend-web/src/components/admin-shell.tsx b/frontend-web/src/components/admin-shell.tsx index b041720..8f4f84e 100644 --- a/frontend-web/src/components/admin-shell.tsx +++ b/frontend-web/src/components/admin-shell.tsx @@ -16,25 +16,25 @@ import { } from "lucide-react"; import Link from "next/link"; import { usePathname, useRouter } from "next/navigation"; -import { useEffect } from "react"; +import { useEffect, useMemo } from "react"; import { api } from "@/lib/api"; const navigation = [ - { href: "/admin/dashboard", label: "仪表盘", icon: ChartColumnBig }, - { href: "/admin/users", label: "用户管理", icon: Users }, - { href: "/admin/recharge-orders", label: "充值订单", icon: Coins }, - { href: "/admin/redeem-codes", label: "兑换密钥", icon: KeySquare }, - { href: "/admin/growth-rules", label: "增长奖励", icon: Link2 }, - { href: "/admin/invite-relations", label: "邀请关系", icon: Link2 }, - { href: "/admin/provider-accounts", label: "供应商账号", icon: Workflow }, - { href: "/admin/provider-models", label: "供应商模型", icon: Blocks }, - { href: "/admin/video-models", label: "平台模型", icon: Package2 }, - { href: "/admin/video-model-bindings", label: "模型绑定", icon: Workflow }, - { href: "/admin/pricing-rules", label: "价格规则", icon: Coins }, - { href: "/admin/video-tasks", label: "视频任务", icon: Workflow }, - { href: "/admin/callback-logs", label: "回调日志", icon: ChartColumnBig }, - { href: "/admin/system-config", label: "系统配置", icon: Settings2 }, + { href: "/admin/dashboard", label: "仪表盘", description: "查看系统总览、支付与任务健康度", icon: ChartColumnBig }, + { href: "/admin/users", label: "用户管理", description: "维护用户状态、余额与基本资料", icon: Users }, + { href: "/admin/recharge-orders", label: "充值订单", description: "跟踪充值流转与支付状态", icon: Coins }, + { href: "/admin/redeem-codes", label: "兑换密钥", description: "批量生成、停用与审计兑换码", icon: KeySquare }, + { href: "/admin/growth-rules", label: "增长奖励", description: "配置注册奖励与邀请返利规则", icon: Link2 }, + { href: "/admin/invite-relations", label: "邀请关系", description: "查看邀请码链路与归因结果", icon: Link2 }, + { href: "/admin/provider-accounts", label: "供应商账号", description: "管理各供应商 baseUrl 与鉴权信息", icon: Workflow }, + { href: "/admin/provider-models", label: "供应商模型", description: "维护供应商模型能力与协议", icon: Blocks }, + { href: "/admin/video-models", label: "平台模型", description: "定义前台可见的统一模型层", icon: Package2 }, + { href: "/admin/video-model-bindings", label: "模型绑定", description: "决定平台模型到供应商模型的路由", icon: Workflow }, + { href: "/admin/pricing-rules", label: "价格规则", description: "按模型与版本维护积分定价", icon: Coins }, + { href: "/admin/video-tasks", label: "视频任务", description: "处理异常任务与追踪回调结果", icon: Workflow }, + { href: "/admin/callback-logs", label: "回调日志", description: "查看供应商回调负载与重放线索", icon: ChartColumnBig }, + { href: "/admin/system-config", label: "系统配置", description: "维护站点公告、策略与公开配置", icon: Settings2 }, ]; export function AdminShell({ children }: { children: React.ReactNode }) { @@ -51,6 +51,14 @@ export function AdminShell({ children }: { children: React.ReactNode }) { } }, [meQuery.error, router]); + const current = useMemo( + () => + [...navigation] + .sort((left, right) => right.href.length - left.href.length) + .find((item) => pathname.startsWith(item.href)) ?? navigation[0], + [pathname], + ); + if (meQuery.isLoading || !meQuery.data) { return (
@@ -60,16 +68,43 @@ export function AdminShell({ children }: { children: React.ReactNode }) { } return ( -
+
+
+
+
Operations Console
+

{current.label}

+

{current.description}

+
+
+
管理员会话已建立
+
统一配置 / 审计 / 处置
+
+
{children}
); } - diff --git a/frontend-web/src/components/register-form.tsx b/frontend-web/src/components/register-form.tsx index dc9c30c..0ca6052 100644 --- a/frontend-web/src/components/register-form.tsx +++ b/frontend-web/src/components/register-form.tsx @@ -1,6 +1,7 @@ "use client"; import { startTransition, useState } from "react"; +import { ArrowRight, Coins, Gift, Link2, Sparkles } from "lucide-react"; import Link from "next/link"; import { useRouter } from "next/navigation"; @@ -36,24 +37,57 @@ export function RegisterForm({ inviteCode = "" }: { inviteCode?: string }) { return (
-
-
+
+
New Account
-

用一个账号串起充值、任务、邀请奖励和素材管理。

+

用一个账号,串起创作、积分、邀请奖励和素材管理。

- 注册后会读取后台增长奖励规则,若已开启则自动发放注册积分;如果通过邀请码注册,也会建立邀请关系。 + 注册后会读取后台增长规则发放奖励;如果带邀请码进入,则会同步建立邀请关系,后续消费可自动回传奖励。

+
+ Signup rewards + Invite tracking + Wallet ready +
-
-

邀请奖励说明

-

被邀请用户首次有效消费成功后,邀请人可自动拿到奖励积分。

+
+
+
+ Growth loop + Reward rules active +
+

把增长规则自然接到用户首登体验里

+

+ 注册成功后直接进入工作台,不需要在多个页面之间跳转,就能继续上传素材、创建任务和消费积分。 +

+
+
+
+ + 注册奖励自动发放 + 遵循后台增长配置,不在前台写死业务规则。 +
+
+ + 邀请码自动建链 + 注册时携带邀请码即可完成邀请归因。 +
+
+ + 积分钱包即刻可用 + 注册完成后可直接进入充值、兑换和视频生成流程。 +
+
-
-
Create Account
-

注册用户

+ +
+
Create Account
+

注册用户

+

创建账号后会直接进入创作端工作台。

+
- {error ?
{error}
: null} + {error ?
{error}
: null} - - 返回登录 - +
+ + 返回登录 + +
+ + 若通过邀请链接进入,邀请码会自动带入当前表单。 +
+
); } - diff --git a/frontend-web/src/components/site-shell.tsx b/frontend-web/src/components/site-shell.tsx index b570cfc..3df80b7 100644 --- a/frontend-web/src/components/site-shell.tsx +++ b/frontend-web/src/components/site-shell.tsx @@ -105,20 +105,47 @@ export function SiteShell({ children }: { children: React.ReactNode }) { } return ( -
+
+
+
+ 身份 + Creator +
+
+ ID + {(data.publicId || "AIVIDEO").slice(-8)} +
+