Merge branch 'dhk-1' of http://192.168.0.200/knowledge-base-bj/deerflow-server into dhk-1
This commit is contained in:
commit
001b04ac5a
16 files changed
+861
-619
No files matched your search
@@ -0,0 +1,28 @@
|
||||
# SIT 环境打包(pnpm build:sit / npm run build:sit)— 接口地址为 UAT 的 sit 版本
|
||||
# 打包后变量会写入 dist,运行时不可再改
|
||||
|
||||
# Gateway / DeerFlow API
|
||||
VITE_BACKEND_BASE_URL=https://ch1.b.sit.4.cn/deerflow
|
||||
VITE_LANGGRAPH_BASE_URL=https://ch1.b.sit.4.cn/deerflow/api
|
||||
|
||||
# 轻应用「新建笔记」系列接口
|
||||
VITE_NOTE_API_BASE_URL=https://your-intranet-host/note-api
|
||||
# 佳来模型对话接口
|
||||
VITE_AIPI_CHAT_URL=https://ch1.b.sit.4.cn/gpt
|
||||
# 多轮检索接口
|
||||
VITE_AIPI_GENERATE_CHAT_URL=https://ch1.b.sit.4.cn/jialai_b1_chat
|
||||
VITE_AIPI_TASK_CHAT_URL=https://ch1.b.sit.4.cn/qq_b1_chat
|
||||
VITE_AIPI_KNOWLEDGE_URL=https://chlib.b.sit.4.cn/knowlibrary
|
||||
VITE_AIPI_B1COH_URL=https://ch1.b.sit.4.cn/web
|
||||
VITE_AIPI_B2ACTOR_URL=https://ty.b.sit.4.cn
|
||||
VITE_AIPI_B2ACTOR_XWT_PATH=/fromB/toXWT?isFrame=1
|
||||
VITE_AIPI_KNOWLEDGE_IFRAME_API_URL=https://ch1.b.sit.4.cn/knowes/api/iframeKnowledgeUrl
|
||||
VITE_AIPI_KNOWLEDGE_IFRAME_SKIN=red
|
||||
VITE_AIPI_MOPECHAT_URL=https://ch1.b.sit.4.cn/likun_b1_mope
|
||||
VITE_AIPI_BACKENP_URL=https://knowledgetoolserver.b.sit.4.cn
|
||||
VITE_AIPI_CONSUMER_URL=https://ch1.b.sit.4.cn/consumer
|
||||
VITE_AIPI_PGLINKTO_URL=https://pg.b.sit.4.cn
|
||||
VITE_AIPI_XKLINKTO_URL=https://kz.b.sit.4.cn
|
||||
|
||||
# 可选:Canvas 独立 LangGraph 地址
|
||||
# VITE_CANVAS_LANGGRAPH_BASE_URL=
|
||||
@@ -6,6 +6,7 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "node --max-old-space-size=8192 ./node_modules/vite/bin/vite.js build",
|
||||
"build:sit": "node --max-old-space-size=8192 ./node_modules/vite/bin/vite.js build --mode sit",
|
||||
"build:network": "node --max-old-space-size=8192 ./node_modules/vite/bin/vite.js build --mode network",
|
||||
"preview": "vite preview",
|
||||
"typecheck": "tsc --noEmit"
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useMemo } from "react";
|
||||
import { Navigate, Route, Routes } from "react-router-dom";
|
||||
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
||||
|
||||
import { FontScaleApplier } from "@/components/font-scale";
|
||||
import { QueryClientProvider } from "@/components/query-client-provider";
|
||||
import { AuthSyncWatcher } from "@/core/auth/sync";
|
||||
import { I18nProvider } from "@/core/i18n/context";
|
||||
@@ -39,6 +40,7 @@ function AppProviders({ children }: { children: React.ReactNode }) {
|
||||
<ThemeProvider>
|
||||
<ChatProvider>
|
||||
<AuthSyncWatcher />
|
||||
<FontScaleApplier />
|
||||
{children}
|
||||
</ChatProvider>
|
||||
</ThemeProvider>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { useLocalSettings } from "@/core/settings";
|
||||
|
||||
/**
|
||||
* 基准字号 = 16px,对应《深蓝色 UI 规范》§2.1.2 后台系统正文字号(16px Regular)。
|
||||
* 用户在「外观 → 字号」里选择的倍数作用于 <html> 根元素的 font-size;由于 Tailwind
|
||||
* 的字号 / 间距基于 rem,整套界面会随之按比例放大 / 缩小。`fontScale === 1` 即默认 16px。
|
||||
*/
|
||||
export const BASE_FONT_PX = 16;
|
||||
|
||||
export const FONT_SCALE_OPTIONS: { value: number; label: string; px: number }[] =
|
||||
[
|
||||
{ value: 0.875, label: "小", px: 14 },
|
||||
{ value: 1, label: "标准", px: 16 },
|
||||
{ value: 1.125, label: "大", px: 18 },
|
||||
{ value: 1.25, label: "特大", px: 20 },
|
||||
];
|
||||
|
||||
/**
|
||||
* 将用户选择的字号倍数应用到 <html> 根元素。挂在应用根部,保证刷新后即时生效、全局统一。
|
||||
* 不渲染任何 DOM。
|
||||
*/
|
||||
export function FontScaleApplier() {
|
||||
const [settings] = useLocalSettings();
|
||||
const { fontScale } = settings.display;
|
||||
|
||||
useEffect(() => {
|
||||
const scale =
|
||||
typeof fontScale === "number" && Number.isFinite(fontScale) && fontScale > 0
|
||||
? fontScale
|
||||
: 1;
|
||||
document.documentElement.style.fontSize = `${BASE_FONT_PX * scale}px`;
|
||||
}, [fontScale]);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { toast } from "sonner";
|
||||
|
||||
import { useTheme } from "@/contexts/ThemeContext";
|
||||
|
||||
import { FONT_SCALE_OPTIONS } from "@/components/font-scale";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Select,
|
||||
@@ -21,12 +22,12 @@ import { getStoredAuth } from "@/core/auth";
|
||||
import { enUS, isLocale, zhCN, type Locale } from "@/core/i18n";
|
||||
import { useI18n } from "@/core/i18n/hooks";
|
||||
import { useLocalSettings } from "@/core/settings";
|
||||
import {
|
||||
useAppearanceSettings,
|
||||
useCitationDisplaySettings,
|
||||
useUpdateAppearanceSettings,
|
||||
useUpdateCitationDisplaySettings,
|
||||
} from "@/core/system-settings/hooks";
|
||||
import {
|
||||
useAppearanceSettings,
|
||||
useCitationDisplaySettings,
|
||||
useUpdateAppearanceSettings,
|
||||
useUpdateCitationDisplaySettings,
|
||||
} from "@/core/system-settings/hooks";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
import { SettingsSection } from "./settings-section";
|
||||
@@ -42,13 +43,13 @@ export function AppearanceSettingsPage() {
|
||||
const currentTheme = theme;
|
||||
const [settings, setSettings] = useLocalSettings();
|
||||
const isAdmin = getStoredAuth()?.system_role === "admin";
|
||||
const { data: appearance } = useAppearanceSettings();
|
||||
const updateAppearance = useUpdateAppearanceSettings();
|
||||
const { data: citationDisplay } = useCitationDisplaySettings();
|
||||
const updateCitationDisplay = useUpdateCitationDisplaySettings();
|
||||
const globalDefault = appearance?.default_theme ?? "light";
|
||||
const cleanupWords =
|
||||
citationDisplay?.cleanup_words ?? settings.display.citationCleanupWords ?? [];
|
||||
const { data: appearance } = useAppearanceSettings();
|
||||
const updateAppearance = useUpdateAppearanceSettings();
|
||||
const { data: citationDisplay } = useCitationDisplaySettings();
|
||||
const updateCitationDisplay = useUpdateCitationDisplaySettings();
|
||||
const globalDefault = appearance?.default_theme ?? "light";
|
||||
const cleanupWords =
|
||||
citationDisplay?.cleanup_words ?? settings.display.citationCleanupWords ?? [];
|
||||
|
||||
const handleSetGlobalDefault = async (value: "light" | "dark") => {
|
||||
if (value === globalDefault) return;
|
||||
@@ -147,6 +148,39 @@ export function AppearanceSettingsPage() {
|
||||
|
||||
<Separator />
|
||||
|
||||
<SettingsSection
|
||||
title="字号"
|
||||
description="调整界面整体字号,方便放大或缩小阅读。默认 16px(标准)。"
|
||||
>
|
||||
<div className="space-y-3">
|
||||
<Select
|
||||
value={String(settings.display.fontScale ?? 1)}
|
||||
onValueChange={(value) => {
|
||||
const scale = Number(value);
|
||||
if (Number.isFinite(scale) && scale > 0) {
|
||||
setSettings("display", { fontScale: scale });
|
||||
}
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-[220px]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{FONT_SCALE_OPTIONS.map((item) => (
|
||||
<SelectItem key={item.value} value={String(item.value)}>
|
||||
{item.label}({item.px}px)
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="text-muted-foreground rounded-lg border p-4 text-sm leading-relaxed">
|
||||
预览:多智能体圆桌研讨 · The quick brown fox · 0123456789
|
||||
</div>
|
||||
</div>
|
||||
</SettingsSection>
|
||||
|
||||
<Separator />
|
||||
|
||||
<SettingsSection
|
||||
title={t.settings.appearance.languageTitle}
|
||||
description={t.settings.appearance.languageDescription}
|
||||
@@ -217,16 +251,16 @@ export function AppearanceSettingsPage() {
|
||||
管理员配置。每行一个词或短句,参考文献卡片和详情弹框会自动移除这些广告词、导航词或免责声明。
|
||||
</p>
|
||||
</div>
|
||||
<Textarea
|
||||
value={cleanupWords.join("\n")}
|
||||
onChange={(event) => {
|
||||
const words = event.currentTarget.value
|
||||
.split(/\r?\n/)
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
setSettings("display", { citationCleanupWords: words });
|
||||
updateCitationDisplay.mutate({ cleanup_words: words });
|
||||
}}
|
||||
<Textarea
|
||||
value={cleanupWords.join("\n")}
|
||||
onChange={(event) => {
|
||||
const words = event.currentTarget.value
|
||||
.split(/\r?\n/)
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
setSettings("display", { citationCleanupWords: words });
|
||||
updateCitationDisplay.mutate({ cleanup_words: words });
|
||||
}}
|
||||
placeholder={"广告\n点击下载APP\n打开客户端\n免责声明\n责任编辑"}
|
||||
className="min-h-32 text-sm"
|
||||
/>
|
||||
|
||||
@@ -4,11 +4,12 @@ export const DEFAULT_LOCAL_SETTINGS: LocalSettings = {
|
||||
notification: {
|
||||
enabled: true,
|
||||
},
|
||||
display: {
|
||||
streamSpeedMeterEnabled: true,
|
||||
ragModeEnabled: false,
|
||||
citationCleanupWords: [],
|
||||
},
|
||||
display: {
|
||||
streamSpeedMeterEnabled: true,
|
||||
ragModeEnabled: false,
|
||||
citationCleanupWords: [],
|
||||
fontScale: 1,
|
||||
},
|
||||
context: {
|
||||
model_name: undefined,
|
||||
mode: undefined,
|
||||
@@ -17,9 +18,9 @@ export const DEFAULT_LOCAL_SETTINGS: LocalSettings = {
|
||||
},
|
||||
};
|
||||
|
||||
export const LOCAL_SETTINGS_KEY = "deerflow.local-settings";
|
||||
export const THREAD_MODEL_KEY_PREFIX = "deerflow.thread-model.";
|
||||
export const THREAD_RAG_MODE_KEY_PREFIX = "deerflow.thread-rag-mode.";
|
||||
export const LOCAL_SETTINGS_KEY = "deerflow.local-settings";
|
||||
export const THREAD_MODEL_KEY_PREFIX = "deerflow.thread-model.";
|
||||
export const THREAD_RAG_MODE_KEY_PREFIX = "deerflow.thread-rag-mode.";
|
||||
|
||||
function isBrowser(): boolean {
|
||||
return typeof window !== "undefined";
|
||||
@@ -40,13 +41,20 @@ export interface LocalSettings {
|
||||
* Global switch for skill result display adapters. When disabled, chat
|
||||
* messages render tool results with the legacy generic search UI only.
|
||||
*/
|
||||
ragModeEnabled: boolean;
|
||||
/**
|
||||
* Admin-maintained words or short phrases removed from reference snippets
|
||||
* and dialogs, one entry per line in the settings UI.
|
||||
*/
|
||||
citationCleanupWords: string[];
|
||||
};
|
||||
ragModeEnabled: boolean;
|
||||
/**
|
||||
* Admin-maintained words or short phrases removed from reference snippets
|
||||
* and dialogs, one entry per line in the settings UI.
|
||||
*/
|
||||
citationCleanupWords: string[];
|
||||
/**
|
||||
* Global UI font scale multiplier applied to the root <html> font-size.
|
||||
* Base is 16px (deep-blue UI spec §2.1.2 body text), so `1` === 16px.
|
||||
* The user picks a size from the appearance settings page; because the
|
||||
* app sizes text/spacing in rem, the whole UI scales proportionally.
|
||||
*/
|
||||
fontScale: number;
|
||||
};
|
||||
context: Omit<
|
||||
AgentThreadContext,
|
||||
| "thread_id"
|
||||
@@ -91,10 +99,10 @@ export function getThreadModelName(threadId: string): string | undefined {
|
||||
return localStorage.getItem(getThreadModelStorageKey(threadId)) ?? undefined;
|
||||
}
|
||||
|
||||
export function saveThreadModelName(
|
||||
threadId: string,
|
||||
modelName: string | undefined,
|
||||
) {
|
||||
export function saveThreadModelName(
|
||||
threadId: string,
|
||||
modelName: string | undefined,
|
||||
) {
|
||||
if (!isBrowser()) {
|
||||
return;
|
||||
}
|
||||
@@ -103,26 +111,26 @@ export function saveThreadModelName(
|
||||
localStorage.removeItem(key);
|
||||
return;
|
||||
}
|
||||
localStorage.setItem(key, modelName);
|
||||
}
|
||||
|
||||
function getThreadRagModeStorageKey(threadId: string): string {
|
||||
return `${THREAD_RAG_MODE_KEY_PREFIX}${threadId}`;
|
||||
}
|
||||
|
||||
export function isThreadRagModeLocked(threadId: string): boolean {
|
||||
if (!isBrowser() || !threadId) {
|
||||
return false;
|
||||
}
|
||||
return localStorage.getItem(getThreadRagModeStorageKey(threadId)) === "true";
|
||||
}
|
||||
|
||||
export function lockThreadRagMode(threadId: string) {
|
||||
if (!isBrowser() || !threadId) {
|
||||
return;
|
||||
}
|
||||
localStorage.setItem(getThreadRagModeStorageKey(threadId), "true");
|
||||
}
|
||||
localStorage.setItem(key, modelName);
|
||||
}
|
||||
|
||||
function getThreadRagModeStorageKey(threadId: string): string {
|
||||
return `${THREAD_RAG_MODE_KEY_PREFIX}${threadId}`;
|
||||
}
|
||||
|
||||
export function isThreadRagModeLocked(threadId: string): boolean {
|
||||
if (!isBrowser() || !threadId) {
|
||||
return false;
|
||||
}
|
||||
return localStorage.getItem(getThreadRagModeStorageKey(threadId)) === "true";
|
||||
}
|
||||
|
||||
export function lockThreadRagMode(threadId: string) {
|
||||
if (!isBrowser() || !threadId) {
|
||||
return;
|
||||
}
|
||||
localStorage.setItem(getThreadRagModeStorageKey(threadId), "true");
|
||||
}
|
||||
|
||||
export function applyThreadModelOverride(
|
||||
settings: LocalSettings,
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
} from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { listAgents, type Agent } from "@/core/agents";
|
||||
import { useTheme } from "@/contexts/ThemeContext";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
import {
|
||||
@@ -183,6 +184,11 @@ export function RecommendAgentsDialog({
|
||||
onCancel,
|
||||
onConfirm,
|
||||
}: RecommendAgentsDialogProps) {
|
||||
// 弹窗经 Radix portal 渲染到 .roundtable-planning 之外,页面的深蓝 scoped 样式
|
||||
// 够不着它。深色下手动挂上 .roundtable-planning--dark,让同一套深蓝 token + 兜底
|
||||
// 映射也作用于弹窗;浅色不挂,弹窗浅色外观不变。
|
||||
const { theme } = useTheme();
|
||||
const isDark = theme === "dark";
|
||||
const [state, setState] = useState<InternalState>(INITIAL_STATE);
|
||||
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
||||
|
||||
@@ -516,7 +522,12 @@ export function RecommendAgentsDialog({
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={handleOpenChange}>
|
||||
<DialogContent className="sm:max-w-3xl max-h-[85vh] flex flex-col overflow-hidden gap-3">
|
||||
<DialogContent
|
||||
className={cn(
|
||||
"sm:max-w-3xl max-h-[85vh] flex flex-col overflow-hidden gap-3",
|
||||
isDark && "roundtable-planning--dark",
|
||||
)}
|
||||
>
|
||||
<DialogHeader className="shrink-0">
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<SparklesIcon className="size-5 text-purple-500" />
|
||||
|
||||
@@ -55,6 +55,15 @@
|
||||
/* —— 功能语义色 —— */
|
||||
--destructive: #f5222d; /* zs-001-06 错误 */
|
||||
|
||||
/* —— brand-* 别名改深蓝(关键):globals.css 只在 :root 把 --brand-* 定为浅红/红,
|
||||
* .dark 未覆盖,导致组件里 var(--brand-200/900/950) 在深色下泛粉/泛红、与深蓝冲突。
|
||||
* 这里改为渐蓝系 ZS-002,所有 bg-[var(--brand-200)] / text-[var(--brand-900)] 等
|
||||
* 一并由红转蓝。 —— */
|
||||
--workspace-brand: #1677ff; /* 与 primary 对齐 */
|
||||
--brand-200: #16335f; /* 浅红 chip 底 → 深蓝软底(chip / 头像底) */
|
||||
--brand-900: #4096ff; /* 红强调 / 按钮 hover → 蓝(zs-002-05) */
|
||||
--brand-950: #91caff; /* 深红文字 → 浅蓝(zs-002-03,深底上可读) */
|
||||
|
||||
/* —— 圆桌局部别名(回落到上面已覆盖的 token) —— */
|
||||
--rt-page-bg: var(--background);
|
||||
--rt-primary: var(--primary);
|
||||
@@ -295,6 +304,33 @@
|
||||
background-color: color-mix(in oklch, #ec4899 18%, var(--card)) !important;
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
* 残留红色清理 —— 内联 SVG 红填充 + 红色发光阴影 → 深蓝系。
|
||||
* (这些是写死在组件里的红:头像 fill 属性、按钮 rgba(245,34,45) 阴影,
|
||||
* 不经 token,需在此单独改写;仅深色生效。)
|
||||
* ========================================================================== */
|
||||
|
||||
/* 头像等内联 SVG 的红色 fill(CSS 可覆盖 SVG presentation 属性) */
|
||||
:is(.dark .roundtable-planning, .roundtable-planning--dark) [fill="#f5222d"] {
|
||||
fill: #1677ff !important;
|
||||
}
|
||||
:is(.dark .roundtable-planning, .roundtable-planning--dark) [fill="#cf1322"] {
|
||||
fill: #0958d9 !important;
|
||||
}
|
||||
:is(.dark .roundtable-planning, .roundtable-planning--dark) [fill="#a8071a"] {
|
||||
fill: #003eb3 !important;
|
||||
}
|
||||
|
||||
/* 红色发光阴影 → 蓝色发光 */
|
||||
:is(.dark .roundtable-planning, .roundtable-planning--dark)
|
||||
.shadow-\[0_2px_8px_rgba\(245\2c 34\2c 45\2c 0\.15\)\] {
|
||||
box-shadow: 0 2px 8px rgba(22, 119, 255, 0.28) !important;
|
||||
}
|
||||
:is(.dark .roundtable-planning, .roundtable-planning--dark)
|
||||
.shadow-\[0_8px_30px_rgba\(245\2c 34\2c 45\2c 0\.06\)\] {
|
||||
box-shadow: 0 8px 30px rgba(22, 119, 255, 0.14) !important;
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
* 结构件覆盖(顶栏 / 步骤条 / 次要按钮)—— 保留原有针对性规则。
|
||||
* ========================================================================== */
|
||||
|
||||
Reference in new issue
Block a user