| 334 | } |
| 335 | |
| 336 | function SectionHeader({ |
| 337 | title, |
| 338 | totalRaw, |
| 339 | aggregated, |
| 340 | }: { |
| 341 | title: string; |
| 342 | totalRaw: number; |
| 343 | aggregated: number; |
| 344 | }) { |
| 345 | const t = useT(); |
| 346 | // aggregated = 唯一路径数;totalRaw = 原始 [[link]] 总出现次数 |
| 347 | // 仅当不一致时才同时展示"unique / total",否则单数 |
| 348 | const countText = |
| 349 | aggregated === totalRaw |
| 350 | ? String(totalRaw) |
| 351 | : t("panel.unique_of_total", { unique: aggregated, total: totalRaw }); |
| 352 | return ( |
| 353 | <div className="flex items-baseline gap-2"> |
| 354 | <span className="text-[10px] uppercase tracking-wide text-muted-foreground font-semibold"> |
| 355 | {title} |
| 356 | </span> |
| 357 | <span className="text-[10px] text-muted-foreground tabular-nums"> |
| 358 | {countText} |
| 359 | </span> |
| 360 | </div> |
| 361 | ); |
| 362 | } |
| 363 | |
| 364 | function orderedBuckets<T>(grouped: Record<string, T[]>): [string, T[]][] { |
| 365 | const known = BUCKET_ORDER.filter((k) => grouped[k]?.length).map( |