87 lines
3.9 KiB
TypeScript
87 lines
3.9 KiB
TypeScript
import type { Token } from '../core/types';
|
||
import { joinTokens } from '../core/joinTokens';
|
||
|
||
import { applyEllipsisToLastLine } from './ellipsis';
|
||
import type { ApplyOverflowFallbackInput, ApplyOverflowFallbackResult, PartialLayoutInput, PartialLayoutLine } from './types';
|
||
|
||
function nTokens(tokens: Token[]): number {
|
||
return Array.isArray(tokens) ? tokens.length : 0;
|
||
}
|
||
|
||
function coversToEnd(partial: PartialLayoutInput | null | undefined, n: number): boolean {
|
||
if (!partial || !Array.isArray(partial.lines) || partial.lines.length === 0) return false;
|
||
const last = partial.lines[partial.lines.length - 1];
|
||
return (last?.end ?? -1) === n;
|
||
}
|
||
|
||
function buildLinesFromPartial(args: {
|
||
tokens: Token[];
|
||
partial?: PartialLayoutInput | null;
|
||
maxLines: number;
|
||
lang: 'TC' | 'EN';
|
||
}): Array<{ start: number; end: number; text: string }> {
|
||
const N = nTokens(args.tokens);
|
||
const maxLines = Math.max(1, args.maxLines | 0);
|
||
const rawSeparators = args.lang === 'TC' ? Array.from({ length: Math.max(0, N) }, () => '') : undefined;
|
||
|
||
if (args.partial && Array.isArray(args.partial.lines) && args.partial.lines.length > 0) {
|
||
const out: Array<{ start: number; end: number; text: string }> = [];
|
||
for (const line of args.partial.lines.slice(0, maxLines)) {
|
||
const start = Math.max(0, line.start | 0);
|
||
const end = Math.min(N, Math.max(start, line.end | 0));
|
||
const text = line.text ?? joinTokens(args.tokens, start, end, rawSeparators);
|
||
out.push({ start, end, text });
|
||
}
|
||
return out;
|
||
}
|
||
|
||
// 若没有 partial:把全文当单行 best-effort
|
||
return [{ start: 0, end: N, text: joinTokens(args.tokens, 0, N, rawSeparators) }];
|
||
}
|
||
|
||
function toWrapped(lines: string[]): { lines: string[]; wrappedText: string } {
|
||
const outLines = lines.filter((s) => s !== undefined) as string[];
|
||
return { lines: outLines, wrappedText: outLines.join('\n') };
|
||
}
|
||
|
||
function toSingleLine(tokens: Token[]): { lines: string[]; wrappedText: string } {
|
||
const N = nTokens(tokens);
|
||
// 注意:这里不知道语言口径,因此 SYSTEM_DEFAULT 的单行文本在上层保证传入“原文”更稳妥。
|
||
// 为保持最小可用,这里仍使用默认 joinTokens(EN=空格 join,TC=grapheme 之间可能会有空格)。
|
||
const text = joinTokens(tokens, 0, N);
|
||
return { lines: [text], wrappedText: text };
|
||
}
|
||
|
||
export async function applyOverflowFallback(input: ApplyOverflowFallbackInput): Promise<ApplyOverflowFallbackResult> {
|
||
const tokens = input.tokens ?? [];
|
||
const N = nTokens(tokens);
|
||
const maxLines = Math.max(1, input.maxLines | 0);
|
||
|
||
const isOverflow = !coversToEnd(input.partialLayout, N);
|
||
|
||
// 若其实没有 overflow:直接返回(NONE)
|
||
if (!isOverflow) {
|
||
const base = buildLinesFromPartial({ tokens, partial: input.partialLayout, maxLines, lang: input.lang });
|
||
const { lines, wrappedText } = toWrapped(base.map((l) => l.text));
|
||
return { lines, wrappedText, meta: { fallback_type: 'NONE', overflow_type: 'NONE', reason: input.reason } };
|
||
}
|
||
|
||
if (input.overflowMode === 'SYSTEM_DEFAULT') {
|
||
const { lines, wrappedText } = toSingleLine(tokens);
|
||
return { lines, wrappedText, meta: { fallback_type: 'SYSTEM_DEFAULT', overflow_type: 'NONE', reason: input.reason } };
|
||
}
|
||
|
||
if (input.overflowMode === 'CLIP') {
|
||
const base = buildLinesFromPartial({ tokens, partial: input.partialLayout, maxLines, lang: input.lang });
|
||
const { lines, wrappedText } = toWrapped(base.slice(0, maxLines).map((l) => l.text));
|
||
return { lines, wrappedText, meta: { fallback_type: 'NONE', overflow_type: 'CLIP', reason: input.reason } };
|
||
}
|
||
|
||
// ELLIPSIS
|
||
const base = buildLinesFromPartial({ tokens, partial: input.partialLayout, maxLines, lang: input.lang });
|
||
const { lines } = await applyEllipsisToLastLine({ input, baseLines: base.slice(0, maxLines) });
|
||
const wrapped = toWrapped(lines);
|
||
return { ...wrapped, meta: { fallback_type: 'NONE', overflow_type: 'ELLIPSIS', reason: input.reason } };
|
||
}
|
||
|