// 大纲编辑器纯逻辑:按卷分组、伏笔窗口徽标文案、接近回收窗口提示(UX §6.7)。 // OutlineChapterView/ForeshadowWindowView 已由 OpenAPI 强类型,无需手动收窄。 import type { ForeshadowWindowView, OutlineChapterView } from "@/lib/api/types"; export interface VolumeGroup { volume: number; chapters: OutlineChapterView[]; } // 按 volume 分组(保持章号升序);卷号升序。 export function groupByVolume( chapters: readonly OutlineChapterView[] | undefined, ): VolumeGroup[] { const byVolume = new Map(); for (const ch of chapters ?? []) { const list = byVolume.get(ch.volume) ?? []; byVolume.set(ch.volume, [...list, ch]); } return [...byVolume.entries()] .sort(([a], [b]) => a - b) .map(([volume, list]) => ({ volume, chapters: [...list].sort((a, b) => a.no - b.no), })); } // 伏笔窗口徽标文案:`⚑ F-012 (40-60)` / `⚑ F-012 (≤60)` / `⚑ F-012`。 export function windowBadgeLabel(window: ForeshadowWindowView): string { const from = window.expected_close_from; const to = window.expected_close_to; if (typeof from === "number" && typeof to === "number") { return `⚑ ${window.code} (${from}-${to})`; } if (typeof to === "number") return `⚑ ${window.code} (≤${to})`; if (typeof from === "number") return `⚑ ${window.code} (≥${from})`; return `⚑ ${window.code}`; } // 接近回收:本章号落在窗口 [from,to] 内(提示「本章可回收」,UX §6.7 ⚑ 可回收)。 export function isCloseWindow( chapter: OutlineChapterView, window: ForeshadowWindowView, ): boolean { const to = window.expected_close_to; if (typeof to !== "number") return false; const from = window.expected_close_from ?? to; return chapter.no >= from && chapter.no <= to; }