Files
deerflow-code/frontend-web/src/core/page-layout/compact-menu-overrides.ts
T
董宏凯andCursor acf538ac01 feat: 简介模式菜单按类型 A/B 分套,兼容未配置的旧部署
两套网页可通过 runtime-config.js 的 VITE_COMPACT_MENU_TYPE 加载各自左侧菜单;缺省或旧页面不传参数时仍读取原来的类型 A 数据。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-19 16:59:34 +08:00

107 lines
3.4 KiB
TypeScript

import type { MenuItem } from "./sidebar-menu";
import { readRuntimeEnv } from "@/env";
export type CompactMenuPlacement = "sidebar" | "settings";
export const COMPACT_MENU_TYPES = ["A", "B"] as const;
export type CompactMenuType = (typeof COMPACT_MENU_TYPES)[number];
export const DEFAULT_COMPACT_MENU_TYPE: CompactMenuType = "A";
const TYPE_ALIASES: Record<string, CompactMenuType> = {
A: "A",
"1": "A",
DEFAULT: "A",
B: "B",
"2": "B",
ALT: "B",
};
export function normalizeCompactMenuType(value?: string | null): CompactMenuType {
const raw = (value ?? "").trim().toUpperCase();
return TYPE_ALIASES[raw] ?? DEFAULT_COMPACT_MENU_TYPE;
}
export function isDefaultCompactMenuType(value?: string | null): boolean {
return normalizeCompactMenuType(value) === DEFAULT_COMPACT_MENU_TYPE;
}
/** Compact-menu variant for this website, from runtime-config.js.
* Missing / empty config is type A, which loads the original compact menu. */
export function getRuntimeCompactMenuType(): CompactMenuType {
return normalizeCompactMenuType(readRuntimeEnv("VITE_COMPACT_MENU_TYPE"));
}
export const COMPACT_MENU_TYPE_LABELS: Record<CompactMenuType, string> = {
A: "类型 A",
B: "类型 B",
};
export interface CompactMenuOverride {
nodeId: string;
placement: CompactMenuPlacement;
sortOrder: number;
label?: string | null;
}
export interface CompactMenuItem extends MenuItem {
compactPlacement: CompactMenuPlacement;
compactOrder: number;
}
function firstRoutePath(items?: MenuItem[]): string | undefined {
for (const item of items ?? []) {
if (item.path) return item.path;
const nested = firstRoutePath(item.children);
if (nested) return nested;
}
return undefined;
}
function isNavigable(item: MenuItem): boolean {
return Boolean(item.path ?? firstRoutePath(item.children));
}
export function applyCompactMenuOverrides(
items: MenuItem[],
overrides: CompactMenuOverride[],
): { sidebarItems: CompactMenuItem[]; settingsItems: CompactMenuItem[] } {
const byId = new Map(overrides.map((override) => [override.nodeId, override]));
const enhanced = items.filter(isNavigable).map((item, index) => {
const override = byId.get(item.id);
return {
...item,
label: override?.label != null && override.label !== "" ? override.label : item.label,
compactPlacement: override?.placement ?? "sidebar",
compactOrder: override?.sortOrder ?? index,
};
});
const sortItems = (list: CompactMenuItem[]) =>
list.slice().sort((a, b) => a.compactOrder - b.compactOrder || a.id.localeCompare(b.id));
return {
sidebarItems: sortItems(enhanced.filter((item) => item.compactPlacement === "sidebar")),
settingsItems: sortItems(enhanced.filter((item) => item.compactPlacement === "settings")),
};
}
export function diffCompactMenuOverrides(items: CompactMenuEditorItem[]): CompactMenuOverride[] {
return items.flatMap((item, index) => {
const renamed = item.label !== item.defaultLabel;
const moved = item.compactPlacement !== "sidebar";
const reordered = index !== item.defaultIndex || item.compactOrder !== item.defaultIndex;
if (!renamed && !moved && !reordered) return [];
return {
nodeId: item.id,
placement: item.compactPlacement,
sortOrder: index,
label: renamed ? item.label : null,
};
});
}
export interface CompactMenuEditorItem extends CompactMenuItem {
defaultLabel: string;
defaultIndex: number;
}