Merge branch 'dhk-1' into 'master'
AI 写作四阶段内置智能体 Phase 1-4 落地 See merge request knowledge-base-bj/deerflow-server!116
This commit is contained in:
commit
c2f6d4fd6f
94 files changed
+6935
-1132
No files matched your search
@@ -320,4 +320,155 @@
|
||||
max-width: 64px;
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-contnet--top-submenu .item-nav:hover::before,
|
||||
.sidebar-contnet--top-submenu .item-nav-active::before,
|
||||
.sidebar-contnet--top-submenu .item-nav-hovered::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.top-submenu-bar {
|
||||
position: relative;
|
||||
z-index: 35;
|
||||
height: 64px;
|
||||
min-width: 0;
|
||||
flex: 1 1 auto;
|
||||
align-self: stretch;
|
||||
margin-left: 34px;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.top-submenu-list {
|
||||
min-width: max-content;
|
||||
height: 64px;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.top-submenu-item {
|
||||
position: relative;
|
||||
min-width: 122px;
|
||||
height: 64px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 0;
|
||||
padding: 0 18px;
|
||||
background: transparent;
|
||||
color: rgba(224, 242, 255, 0.9);
|
||||
font-family: Noto Sans S Chinese;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
transition: color 0.18s ease, text-shadow 0.18s ease, background 0.18s ease;
|
||||
}
|
||||
|
||||
.top-submenu-item:hover,
|
||||
.top-submenu-item--active {
|
||||
color: #ffffff;
|
||||
text-shadow: 0 0 10px rgba(91, 210, 255, 0.45);
|
||||
background:
|
||||
linear-gradient(135deg, rgba(15, 84, 169, 0.52) 0%, rgba(5, 43, 103, 0.34) 76%),
|
||||
linear-gradient(180deg, rgba(39, 126, 230, 0.2) 0%, rgba(0, 50, 122, 0.26) 100%);
|
||||
box-shadow: inset 0 1px 0 rgba(98, 180, 255, 0.16), inset 0 -18px 22px rgba(0, 31, 79, 0.24);
|
||||
}
|
||||
|
||||
.top-submenu-item--active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, rgba(59, 162, 255, 0) 0%, #35d7ff 50%, rgba(59, 162, 255, 0) 100%);
|
||||
box-shadow: 0 0 12px rgba(53, 215, 255, 0.9), 0 -6px 18px rgba(33, 143, 255, 0.36);
|
||||
}
|
||||
|
||||
.top-submenu-item--active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
right: 16px;
|
||||
bottom: 0;
|
||||
height: 16px;
|
||||
background: radial-gradient(ellipse at bottom, rgba(37, 189, 255, 0.34) 0%, rgba(37, 189, 255, 0) 68%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.third-submenu-bar {
|
||||
position: relative;
|
||||
z-index: 34;
|
||||
height: 42px;
|
||||
margin-left: 118px;
|
||||
flex-shrink: 0;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(4, 34, 78, 0.96) 0%, rgba(2, 24, 58, 0.96) 100%),
|
||||
radial-gradient(ellipse at top left, rgba(45, 149, 255, 0.18) 0%, rgba(45, 149, 255, 0) 42%);
|
||||
border-top: 1px solid rgba(79, 154, 255, 0.18);
|
||||
border-bottom: 1px solid rgba(43, 143, 254, 0.26);
|
||||
box-shadow: inset 0 1px 0 rgba(115, 194, 255, 0.08), inset 0 -10px 18px rgba(0, 26, 70, 0.22);
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.third-submenu-list {
|
||||
min-width: max-content;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.third-submenu-item {
|
||||
position: relative;
|
||||
height: 28px;
|
||||
min-width: 86px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid rgba(90, 169, 255, 0.2);
|
||||
border-radius: 2px;
|
||||
padding: 0 16px;
|
||||
background: rgba(2, 28, 65, 0.36);
|
||||
color: rgba(221, 242, 255, 0.78);
|
||||
font-family: Noto Sans S Chinese;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease, box-shadow 0.18s ease;
|
||||
}
|
||||
|
||||
.third-submenu-item:hover,
|
||||
.third-submenu-item--active {
|
||||
color: #ffffff;
|
||||
border-color: rgba(82, 205, 255, 0.55);
|
||||
background: linear-gradient(180deg, rgba(14, 84, 165, 0.54) 0%, rgba(4, 45, 104, 0.58) 100%);
|
||||
box-shadow: inset 0 0 12px rgba(0, 185, 255, 0.14), 0 0 10px rgba(35, 167, 255, 0.14);
|
||||
text-shadow: 0 0 8px rgba(87, 215, 255, 0.34);
|
||||
}
|
||||
|
||||
.third-submenu-item--active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
right: 12px;
|
||||
bottom: -1px;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, rgba(62, 178, 255, 0) 0%, #4ee6ff 50%, rgba(62, 178, 255, 0) 100%);
|
||||
box-shadow: 0 0 10px rgba(78, 230, 255, 0.72);
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import { PanelLeftOpen, PanelLeftClose } from "lucide-react";
|
||||
|
||||
import { getStoredAuth } from "@/core/auth";
|
||||
import { isWujieEnv } from "@/core/embed/wujie";
|
||||
import {
|
||||
defaultExpandedIds,
|
||||
filterMenuItemsByRole,
|
||||
sidebarMenuItems,
|
||||
type MenuItem,
|
||||
} from "@/core/page-layout/sidebar-menu";
|
||||
import { useSidebarMenuItems } from "@/core/page-layout/use-sidebar-menu";
|
||||
import './page-sdebar.css'
|
||||
|
||||
type PageSidebarDarkProps = {
|
||||
@@ -19,10 +19,8 @@ type PageSidebarDarkProps = {
|
||||
|
||||
export function PageSidebarDark({ collapsed, onToggleCollapsed }: PageSidebarDarkProps) {
|
||||
const isAdmin = getStoredAuth()?.system_role === "admin";
|
||||
const visibleMenuItems = useMemo(
|
||||
() => filterMenuItemsByRole(sidebarMenuItems, !!isAdmin),
|
||||
[isAdmin],
|
||||
);
|
||||
const visibleMenuItems = useSidebarMenuItems(!!isAdmin);
|
||||
const inWujieEnv = isWujieEnv();
|
||||
// defaultExpandedIds 是共享的 ReadonlySet,复制一份给 useState(避免外部突变)
|
||||
const [expandedIds, setExpandedIds] = useState<Set<string>>(() => new Set(defaultExpandedIds));
|
||||
const navigate = useNavigate();
|
||||
@@ -163,6 +161,7 @@ export function PageSidebarDark({ collapsed, onToggleCollapsed }: PageSidebarDar
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
||||
// collapsed 状态:sidebar 主体完全消失(包括占据的宽度),只渲染左下角浮动入口
|
||||
if (collapsed) {
|
||||
return (
|
||||
@@ -180,16 +179,16 @@ export function PageSidebarDark({ collapsed, onToggleCollapsed }: PageSidebarDar
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className="sidebar-contnet">
|
||||
<aside className={`sidebar-contnet ${inWujieEnv ? "" : "sidebar-contnet--top-submenu"}`}>
|
||||
<div className="sidebar-contents">
|
||||
{visibleMenuItems.map((item) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className="item-nav-group"
|
||||
onMouseEnter={() => handleNavHoverEnter(item.id)}
|
||||
onMouseLeave={scheduleNavPanelClose}
|
||||
onMouseEnter={inWujieEnv ? () => handleNavHoverEnter(item.id) : undefined}
|
||||
onMouseLeave={inWujieEnv ? scheduleNavPanelClose : undefined}
|
||||
>
|
||||
{item.children?.length ? (
|
||||
{inWujieEnv && item.children?.length ? (
|
||||
<div
|
||||
className={`item-nav-content ${hoveredTopId === item.id ? "item-nav-content--visible" : ""}`}
|
||||
>
|
||||
@@ -204,7 +203,7 @@ export function PageSidebarDark({ collapsed, onToggleCollapsed }: PageSidebarDar
|
||||
>
|
||||
<span className="item-nav__label">{item.label}</span>
|
||||
</button>
|
||||
{item.children?.length && hoveredTopId === item.id ? (
|
||||
{inWujieEnv && item.children?.length && hoveredTopId === item.id ? (
|
||||
<span className="item-nav-hover-bridge" aria-hidden="true" />
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -26,10 +26,9 @@ import { useI18n } from "@/core/i18n/hooks";
|
||||
import { useUnreadCount } from "@/core/notifications";
|
||||
import {
|
||||
defaultExpandedIds,
|
||||
filterMenuItemsByRole,
|
||||
sidebarMenuItems,
|
||||
type MenuItem,
|
||||
} from "@/core/page-layout/sidebar-menu";
|
||||
import { useSidebarMenuItems } from "@/core/page-layout/use-sidebar-menu";
|
||||
|
||||
type PageSidebarProps = {
|
||||
collapsed: boolean;
|
||||
@@ -45,7 +44,7 @@ export function PageSidebar({
|
||||
const { theme } = useTheme();
|
||||
const isDark = theme === "dark";
|
||||
const isAdmin = getStoredAuth()?.system_role === "admin";
|
||||
const visibleMenuItems = filterMenuItemsByRole(sidebarMenuItems, !!isAdmin);
|
||||
const visibleMenuItems = useSidebarMenuItems(!!isAdmin);
|
||||
const { t } = useI18n();
|
||||
const { data: unreadCount = 0 } = useUnreadCount();
|
||||
// defaultExpandedIds 是共享的 ReadonlySet,复制一份给 useState(避免外部突变)
|
||||
|
||||
@@ -23,7 +23,7 @@ function classifySpeed(tps: number): { color: string; label: string } {
|
||||
return { color: "#ef4444", label: "缓慢" };
|
||||
}
|
||||
|
||||
function formatDuration(ms: number): string {
|
||||
export function formatDuration(ms: number): string {
|
||||
if (ms < 1000) return `${Math.round(ms)} ms`;
|
||||
if (ms < 60_000) return `${(ms / 1000).toFixed(1)} s`;
|
||||
const mins = Math.floor(ms / 60_000);
|
||||
@@ -31,7 +31,7 @@ function formatDuration(ms: number): string {
|
||||
return `${mins}m ${secs}s`;
|
||||
}
|
||||
|
||||
function SpeedPill({ metric }: { metric: MessageMetric }) {
|
||||
export function SpeedPill({ metric }: { metric: MessageMetric }) {
|
||||
const fill = Math.max(0, Math.min(1, metric.tokensPerSec / FULL_SCALE_TOKENS_PER_SEC));
|
||||
const { color, label } = classifySpeed(metric.tokensPerSec);
|
||||
const display =
|
||||
|
||||
Reference in new issue
Block a user