解决 artifact 预览组件冲突:mermaid 渲染(dhk-1) 与 markdown 锚点跳转(master) 取并集;package-lock.json 按 master 删除(项目用 pnpm)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
董宏凯andClaude Opus 4.8 committed 2026-06-05 19:43:02 +08:00
commit b2132dd668
104 files changed
+8775 -12038

No files matched your search

+2
View File
@@ -9,6 +9,8 @@ export interface UserListItem {
system_role: "admin" | "user";
skill_count: number;
agent_count: number;
/** 问答次数:用户提问的次数(一次提问算一次)。 */
qa_count: number;
}
export async function listAllUsers(): Promise<UserListItem[]> {
+7 -35
View File
@@ -6,13 +6,7 @@ import { toast } from "sonner";
import { getStoredAuth } from "@/core/auth";
const AUTH_STORAGE_KEY = "deerflow.auth";
// 防抖窗口:跨标签页登录/重写 auth 常常在极短时间内连发多个 storage 事件
// (例如先写 token、再写用户信息)。等它"落定"再判断,避免抖动期的中间态
// (短暂 null / 半写状态)误触发动作。
const SETTLE_DELAY_MS = 1200;
// 提示 toast 的稳定 id —— 同一次身份变化只保留一条,避免连发事件刷屏。
const IDENTITY_TOAST_ID = "auth-identity-changed";
function readUserId(): string | null {
@@ -20,23 +14,8 @@ function readUserId(): string | null {
}
/**
* Mount once at the app root. Watches `localStorage["deerflow.auth"]` for
* cross-tab changes.
*
* 背景:`deerflow.auth` 在同源所有标签页之间共享。某个标签页登录/重写它时,
* 其它标签页仍持有旧用户的内存态(会话列表、thread id、以及多智能体会商这类
* 纯内存的页面状态),与新 token 不一致,可能在"新对话"等操作上 404。
*
* 旧实现:只要检测到 user_id 变化就 `window.location.reload()`。这会把**正在
* 使用的标签页**(例如正在跑多智能体会商第二步的那个)整页刷新,内存态全丢、
* 步骤回到第一步 —— 用户复制链接到新标签页打开(新标签页可能以不同/默认用户
* 重新登录并改写共享 auth)就会触发,体验很差。
*
* 现实现(方案 1,收敛粗暴刷新):
* - **真·退出登录**(auth 变为 null)→ 仍然跳登录页并刷新(本就该重新登录)。
* - **出现不同的非空用户**(另一个标签页登录成别的身份)→ **不再自动整页刷新**,
* 改为弹一条常驻提示,把"是否刷新同步"的决定权交给用户,避免冲掉正在进行的工作。
* - 防抖 + 落定后二次校验,过滤掉同用户重写 / 抖动中间态带来的误触发。
* Mount once at the app root. Watches localStorage auth changes from other
* tabs and lets the user decide when to refresh on account switches.
*/
export function AuthSyncWatcher() {
const lastUserIdRef = useRef<string | null>(readUserId());
@@ -44,26 +23,18 @@ export function AuthSyncWatcher() {
useEffect(() => {
let settleTimer: ReturnType<typeof setTimeout> | null = null;
// storage 事件落定后再判断当前的真实身份并决定动作。
function evaluateSettledIdentity() {
const nextUserId = readUserId();
if (nextUserId === lastUserIdRef.current) {
// 同用户重写,或抖动后又回到原身份 —— 无需任何动作。
return;
}
lastUserIdRef.current = nextUserId;
if (nextUserId === null) {
// 另一个标签页退出登录(或清空了 auth)→ 本标签页也该回到登录页。
lastUserIdRef.current = null;
window.location.replace("#/login");
window.location.reload();
return;
}
// 出现了不同的非空用户。共享 auth 已被改成别的身份,继续在本标签页发请求
// 可能 404 / 鉴权失败,但**不**强制刷新 —— 用户可能正在本标签页干活
// (如多智能体会商),由用户自己决定何时刷新同步。
lastUserIdRef.current = nextUserId;
toast.warning("检测到账号在其它标签页发生变化", {
id: IDENTITY_TOAST_ID,
description: "当前标签页仍是原会话。如需与新账号同步,请刷新页面。",
@@ -76,8 +47,9 @@ export function AuthSyncWatcher() {
}
function handleStorage(event: StorageEvent) {
// 只关心 auth 键的变化;event.key === null 表示 localStorage 被整体清空。
if (event.key !== null && event.key !== AUTH_STORAGE_KEY) {
// Ignore unrelated keys and localStorage.clear(). Backend session loss is
// handled centrally through 401 responses.
if (event.key !== AUTH_STORAGE_KEY) {
return;
}
if (settleTimer) {