feat: 公开页背景色支持 iframe 外部配置 + CM助手旁排行榜入口

- 新增 usePublicBackground:公开页(排行榜/分享/会商分享/计划任务)支持
  嵌套方通过 ?bg=/?text= 查询参数或 postMessage 覆盖背景/文字色
- Header 左上角 CM助手 旁新增「排行榜」按钮,新标签打开公开排行榜页

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
AdministratorandClaude Opus 4.8 committed 2026-06-12 12:21:25 +08:00
1 parent 28098b3155
commit da677f87d5
6 files changed
+136 -14

No files matched your search

@@ -44,6 +44,7 @@ import type {
AdminLeaderboardUser,
} from "@/core/admin/api";
import { getBackendBaseURL } from "@/core/config";
import { usePublicBackground } from "@/core/embed/background";
import { cn } from "@/lib/utils";
type UserRankTab = "activity" | "questions" | "tokens" | "skills" | "efficiency";
@@ -151,6 +152,8 @@ export default function PublicLeaderboardPage() {
const [query, setQuery] = useState("");
const [userPage, setUserPage] = useState(1);
const days = RANGE_DAYS;
// 被第三方 iframe 嵌套时,允许嵌套方通过 ?bg=/?text= 或 postMessage 改背景/文字色。
const pageBg = usePublicBackground();
useEffect(() => {
let cancelled = false;
@@ -222,7 +225,7 @@ export default function PublicLeaderboardPage() {
}, [userPageCount]);
return (
<div className="min-h-screen w-full bg-[#FFF7F7] text-[#231815] dark:bg-[#011226] dark:text-white">
<div className="min-h-screen w-full bg-[#FFF7F7] text-[#231815] dark:bg-[#011226] dark:text-white" style={pageBg}>
<div className="mx-auto w-full max-w-6xl px-4 py-8 sm:px-6 sm:py-10">
{/* 标题区:仅展示,不含任何操作按钮 */}
<header className="mb-6">
@@ -19,6 +19,7 @@ import {
getPublicDraftShareSnapshot,
type PublicDraftSeatDelivery,
} from "@/core/roundtable-draft-shares/api";
import { usePublicBackground } from "@/core/embed/background";
import { markdownComponents } from "@/roundtable-planning/components/markdown-components";
/** 各席位最终交付 —— 折叠手风琴(仿 HighFidelityReport 第二区块)。 */
@@ -96,6 +97,8 @@ export default function PublicRoundtableSharePage({
report?: boolean;
}) {
const { shareCode } = useParams<{ shareCode: string }>();
// 嵌套方可用 ?bg=/?text= 或 postMessage 覆盖背景/文字色。
const pageBg = usePublicBackground();
const { data, isLoading, isError } = useQuery({
queryKey: ["public-roundtable-share", shareCode],
@@ -107,7 +110,7 @@ export default function PublicRoundtableSharePage({
if (isLoading) {
return (
<div className="bg-background text-muted-foreground flex h-screen items-center justify-center gap-2 text-sm">
<div className="bg-background text-muted-foreground flex h-screen items-center justify-center gap-2 text-sm" style={pageBg}>
<Loader2 className="size-4 animate-spin" />
正在加载分享内容…
</div>
@@ -116,7 +119,7 @@ export default function PublicRoundtableSharePage({
if (isError || !data) {
return (
<div className="bg-background flex h-screen flex-col items-center justify-center gap-2 px-6 text-center">
<div className="bg-background flex h-screen flex-col items-center justify-center gap-2 px-6 text-center" style={pageBg}>
<p className="text-lg font-medium">分享不可用</p>
<p className="text-muted-foreground text-sm">
该分享链接不存在、已被撤销,或不是只读分享。
@@ -128,7 +131,7 @@ export default function PublicRoundtableSharePage({
// ── 报告页:整页沙箱 iframe 展示第 3 步结果绘制 ──────────────────────────────
if (report) {
return (
<div className="bg-background flex h-screen flex-col">
<div className="bg-background flex h-screen flex-col" style={pageBg}>
<header className="flex shrink-0 items-center gap-2 border-b px-6 py-3">
<h1 className="truncate text-base font-semibold">
{data.title || "未命名会商"}
@@ -162,7 +165,7 @@ export default function PublicRoundtableSharePage({
const reportUrl = `${window.location.origin}${window.location.pathname}#/roundtable-share/${shareCode}/report`;
return (
<div className="bg-[#fef6f6] flex h-screen flex-col text-slate-800">
<div className="bg-[#fef6f6] flex h-screen flex-col text-slate-800" style={pageBg}>
<header className="flex shrink-0 flex-col gap-1 border-b border-slate-100 bg-white px-6 py-3">
<div className="flex items-center gap-2">
<h1 className="truncate text-base font-semibold">
@@ -43,6 +43,7 @@ import {
import { MarkdownContent } from "@/components/workspace/messages/markdown-content";
import { HtmlPreviewFrame } from "@/components/html-preview-frame";
import { getBackendBaseURL } from "@/core/config";
import { usePublicBackground } from "@/core/embed/background";
import { downloadMarkdownAsDocx } from "@/core/scheduled-tasks/markdown-docx";
import { reasoningPlugins } from "@/core/streamdown";
import { cn } from "@/lib/utils";
@@ -289,6 +290,8 @@ export default function PublicScheduledTaskPage() {
const [runListWidth, setRunListWidth] = useState(RUN_LIST_DEFAULT_WIDTH);
const dragStateRef = useRef<{ startX: number; startWidth: number } | null>(null);
const queryClient = useQueryClient();
// 嵌套方可用 ?bg=/?text= 或 postMessage 覆盖背景/文字色。
const pageBg = usePublicBackground();
const { data, isLoading, isError, error } = useQuery({
queryKey: ["public-scheduled-task", taskId],
@@ -405,7 +408,7 @@ export default function PublicScheduledTaskPage() {
if (!taskId) {
return (
<div className="text-muted-foreground flex h-screen items-center justify-center text-sm">
<div className="text-muted-foreground flex h-screen items-center justify-center text-sm" style={pageBg}>
缺少任务 ID。请使用 <code className="mx-1">/public/scheduled-tasks/&lt;task_id&gt;</code>。
</div>
);
@@ -413,7 +416,7 @@ export default function PublicScheduledTaskPage() {
if (isLoading) {
return (
<div className="text-muted-foreground flex h-screen items-center justify-center gap-2 text-sm">
<div className="text-muted-foreground flex h-screen items-center justify-center gap-2 text-sm" style={pageBg}>
<Loader2 className="size-4 animate-spin" />
正在加载任务记录...
</div>
@@ -422,7 +425,7 @@ export default function PublicScheduledTaskPage() {
if (isError) {
return (
<div className="text-destructive flex h-screen items-center justify-center text-sm">
<div className="text-destructive flex h-screen items-center justify-center text-sm" style={pageBg}>
加载失败:{error instanceof Error ? error.message : "未知错误"}
</div>
);
@@ -430,7 +433,7 @@ export default function PublicScheduledTaskPage() {
return (
<TooltipProvider delayDuration={150}>
<div className="bg-muted/20 flex h-screen min-h-0 overflow-hidden p-4">
<div className="bg-muted/20 flex h-screen min-h-0 overflow-hidden p-4" style={pageBg}>
<div
className="relative flex min-h-0 shrink-0 transition-[width] duration-200 ease-out"
style={{ width: visibleRunListWidth }}
@@ -1139,6 +1142,8 @@ function RunDetailPane({ run, taskId }: { run: PublicRun; taskId: string }) {
export function PublicScheduledTaskFullscreenPage() {
const { taskId, runId } = useParams<{ taskId: string; runId: string }>();
// 嵌套方可用 ?bg=/?text= 或 postMessage 覆盖背景/文字色。
const pageBg = usePublicBackground();
const [searchParams] = useSearchParams();
const requestedTab = searchParams.get("tab");
const tabKind: ContentTab["kind"] =
@@ -1194,7 +1199,7 @@ export function PublicScheduledTaskFullscreenPage() {
return (
<TooltipProvider delayDuration={150}>
<div className="bg-background flex h-screen min-h-0 flex-col">
<div className="bg-background flex h-screen min-h-0 flex-col" style={pageBg}>
<div className="flex h-12 shrink-0 items-center gap-2 border-b px-3">
<Button asChild variant="ghost" size="sm" className="h-8 px-2 text-xs">
<a href={backHref}>
+6 -3
View File
@@ -14,6 +14,7 @@ import {
import { MessageList } from "@/components/workspace/messages";
import { ThreadContext } from "@/components/workspace/messages/context";
import { SubtasksProvider } from "@/core/tasks/context";
import { usePublicBackground } from "@/core/embed/background";
import { getPublicShareSnapshot } from "@/core/thread-shares/api";
import type { AgentThreadState } from "@/core/threads";
@@ -71,6 +72,8 @@ function PublicShareBody({
*/
export default function PublicSharePage() {
const { shareCode } = useParams<{ shareCode: string }>();
// 嵌套方可用 ?bg=/?text= 或 postMessage 覆盖背景/文字色。
const pageBg = usePublicBackground();
const { data, isLoading, isError } = useQuery({
queryKey: ["public-share", shareCode],
@@ -101,7 +104,7 @@ export default function PublicSharePage() {
if (isLoading) {
return (
<div className="bg-background text-muted-foreground flex h-screen items-center justify-center gap-2 text-sm">
<div className="bg-background text-muted-foreground flex h-screen items-center justify-center gap-2 text-sm" style={pageBg}>
<Loader2 className="size-4 animate-spin" />
正在加载分享内容…
</div>
@@ -110,7 +113,7 @@ export default function PublicSharePage() {
if (isError || !data) {
return (
<div className="bg-background flex h-screen flex-col items-center justify-center gap-2 px-6 text-center">
<div className="bg-background flex h-screen flex-col items-center justify-center gap-2 px-6 text-center" style={pageBg}>
<p className="text-lg font-medium">分享不可用</p>
<p className="text-muted-foreground text-sm">
该分享链接不存在、已被撤销,或不是只读分享。
@@ -120,7 +123,7 @@ export default function PublicSharePage() {
}
return (
<div className="bg-background flex h-screen flex-col">
<div className="bg-background flex h-screen flex-col" style={pageBg}>
<header className="flex shrink-0 flex-col gap-1 border-b px-6 py-3">
<div className="flex items-center gap-2">
<h1 className="truncate text-base font-semibold">