mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-01 18:22:41 -08:00
fix: address claude review feedback on overlay refactor
This commit is contained in:
@@ -7,7 +7,7 @@ import type { MergedToken } from '../types';
|
||||
import { PartOfSpeech } from '../types.js';
|
||||
import {
|
||||
alignTokensToSourceText,
|
||||
buildInvisibleTokenHoverRanges,
|
||||
buildSubtitleTokenHoverRanges,
|
||||
computeWordClass,
|
||||
normalizeSubtitle,
|
||||
sanitizeSubtitleHoverTokenColor,
|
||||
@@ -266,26 +266,26 @@ test('alignTokensToSourceText avoids duplicate tail when later token surface doe
|
||||
);
|
||||
});
|
||||
|
||||
test('buildInvisibleTokenHoverRanges tracks token offsets across text separators', () => {
|
||||
test('buildSubtitleTokenHoverRanges tracks token offsets across text separators', () => {
|
||||
const tokens = [
|
||||
createToken({ surface: 'キリキリと' }),
|
||||
createToken({ surface: 'かかってこい' }),
|
||||
];
|
||||
|
||||
const ranges = buildInvisibleTokenHoverRanges(tokens, 'キリキリと\nかかってこい');
|
||||
const ranges = buildSubtitleTokenHoverRanges(tokens, 'キリキリと\nかかってこい');
|
||||
assert.deepEqual(ranges, [
|
||||
{ start: 0, end: 5, tokenIndex: 0 },
|
||||
{ start: 6, end: 12, tokenIndex: 1 },
|
||||
]);
|
||||
});
|
||||
|
||||
test('buildInvisibleTokenHoverRanges ignores unmatched token surfaces', () => {
|
||||
test('buildSubtitleTokenHoverRanges ignores unmatched token surfaces', () => {
|
||||
const tokens = [
|
||||
createToken({ surface: '君たちが潰した拠点に' }),
|
||||
createToken({ surface: '教団の主力は1人もいない' }),
|
||||
];
|
||||
|
||||
const ranges = buildInvisibleTokenHoverRanges(tokens, '君たちが潰した拠点に\n教団の主力は1人もいない');
|
||||
const ranges = buildSubtitleTokenHoverRanges(tokens, '君たちが潰した拠点に\n教団の主力は1人もいない');
|
||||
assert.deepEqual(ranges, [{ start: 0, end: 10, tokenIndex: 0 }]);
|
||||
});
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ type FrequencyRenderSettings = {
|
||||
bandedColors: [string, string, string, string, string];
|
||||
};
|
||||
|
||||
export type InvisibleTokenHoverRange = {
|
||||
export type SubtitleTokenHoverRange = {
|
||||
start: number;
|
||||
end: number;
|
||||
tokenIndex: number;
|
||||
@@ -37,6 +37,8 @@ export function normalizeSubtitle(text: string, trim = true, collapseLineBreaks
|
||||
}
|
||||
|
||||
const HEX_COLOR_PATTERN = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
|
||||
const SAFE_CSS_COLOR_PATTERN =
|
||||
/^(?:#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})|(?:rgba?|hsla?)\([^)]*\)|var\([^)]*\)|[a-zA-Z]+)$/;
|
||||
|
||||
function sanitizeHexColor(value: unknown, fallback: string): string {
|
||||
return typeof value === 'string' && HEX_COLOR_PATTERN.test(value.trim())
|
||||
@@ -58,7 +60,9 @@ function sanitizeSubtitleHoverTokenBackgroundColor(value: unknown): string {
|
||||
return 'rgba(54, 58, 79, 0.84)';
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
return trimmed.length > 0 ? trimmed : 'rgba(54, 58, 79, 0.84)';
|
||||
return trimmed.length > 0 && SAFE_CSS_COLOR_PATTERN.test(trimmed)
|
||||
? trimmed
|
||||
: 'rgba(54, 58, 79, 0.84)';
|
||||
}
|
||||
|
||||
const DEFAULT_FREQUENCY_RENDER_SETTINGS: FrequencyRenderSettings = {
|
||||
@@ -293,16 +297,16 @@ export function alignTokensToSourceText(
|
||||
return segments;
|
||||
}
|
||||
|
||||
export function buildInvisibleTokenHoverRanges(
|
||||
export function buildSubtitleTokenHoverRanges(
|
||||
tokens: MergedToken[],
|
||||
sourceText: string,
|
||||
): InvisibleTokenHoverRange[] {
|
||||
): SubtitleTokenHoverRange[] {
|
||||
if (tokens.length === 0 || sourceText.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const segments = alignTokensToSourceText(tokens, sourceText);
|
||||
const ranges: InvisibleTokenHoverRange[] = [];
|
||||
const ranges: SubtitleTokenHoverRange[] = [];
|
||||
let cursor = 0;
|
||||
|
||||
for (const segment of segments) {
|
||||
|
||||
Reference in New Issue
Block a user