mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-27 18:22:41 -08:00
- add subtitleStyle.hoverTokenColor config default + validation - normalize hover color payloads and propagate configured color to mpv runtime - refresh invisible overlay tokenization with current subtitle text and tighten hover overlay cleanup hooks - record TASK-98 and subagent coordination updates
162 lines
4.5 KiB
TypeScript
162 lines
4.5 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
|
|
import { PartOfSpeech, type SubtitleData } from '../../types';
|
|
import {
|
|
HOVER_TOKEN_MESSAGE,
|
|
HOVER_SCRIPT_NAME,
|
|
buildHoveredTokenMessageCommand,
|
|
buildHoveredTokenPayload,
|
|
createApplyHoveredTokenOverlayHandler,
|
|
} from './mpv-hover-highlight';
|
|
|
|
const SUBTITLE: SubtitleData = {
|
|
text: '昨日は雨だった。',
|
|
tokens: [
|
|
{
|
|
surface: '昨日',
|
|
reading: 'きのう',
|
|
headword: '昨日',
|
|
startPos: 0,
|
|
endPos: 2,
|
|
partOfSpeech: PartOfSpeech.noun,
|
|
isMerged: false,
|
|
isKnown: false,
|
|
isNPlusOneTarget: false,
|
|
},
|
|
{
|
|
surface: 'は',
|
|
reading: 'は',
|
|
headword: 'は',
|
|
startPos: 2,
|
|
endPos: 3,
|
|
partOfSpeech: PartOfSpeech.particle,
|
|
isMerged: false,
|
|
isKnown: true,
|
|
isNPlusOneTarget: false,
|
|
},
|
|
{
|
|
surface: '雨',
|
|
reading: 'あめ',
|
|
headword: '雨',
|
|
startPos: 3,
|
|
endPos: 4,
|
|
partOfSpeech: PartOfSpeech.noun,
|
|
isMerged: false,
|
|
isKnown: false,
|
|
isNPlusOneTarget: true,
|
|
},
|
|
{
|
|
surface: 'だった。',
|
|
reading: 'だった。',
|
|
headword: 'だ',
|
|
startPos: 4,
|
|
endPos: 8,
|
|
partOfSpeech: PartOfSpeech.other,
|
|
isMerged: false,
|
|
isKnown: false,
|
|
isNPlusOneTarget: false,
|
|
},
|
|
],
|
|
};
|
|
|
|
test('buildHoveredTokenPayload normalizes metadata and strips empty tokens', () => {
|
|
const payload = buildHoveredTokenPayload({
|
|
subtitle: SUBTITLE,
|
|
hoveredTokenIndex: 2,
|
|
revision: 5,
|
|
});
|
|
|
|
assert.equal(payload.revision, 5);
|
|
assert.equal(payload.subtitle, '昨日は雨だった。');
|
|
assert.equal(payload.hoveredTokenIndex, 2);
|
|
assert.equal(payload.tokens.length, 4);
|
|
assert.equal(payload.tokens[0]?.text, '昨日');
|
|
assert.equal(payload.tokens[0]?.index, 0);
|
|
assert.equal(payload.tokens[1]?.index, 1);
|
|
assert.equal(payload.colors.hover, 'C6A0F6');
|
|
});
|
|
|
|
test('buildHoveredTokenPayload normalizes hover color override', () => {
|
|
const payload = buildHoveredTokenPayload({
|
|
subtitle: SUBTITLE,
|
|
hoveredTokenIndex: 1,
|
|
revision: 7,
|
|
hoverColor: '#c6a0f6',
|
|
});
|
|
|
|
assert.equal(payload.colors.hover, 'C6A0F6');
|
|
});
|
|
|
|
test('buildHoveredTokenMessageCommand sends script-message-to subminer payload', () => {
|
|
const payload = buildHoveredTokenPayload({
|
|
subtitle: SUBTITLE,
|
|
hoveredTokenIndex: 0,
|
|
revision: 1,
|
|
});
|
|
|
|
const command = buildHoveredTokenMessageCommand(payload);
|
|
|
|
assert.equal(command[0], 'script-message-to');
|
|
assert.equal(command[1], HOVER_SCRIPT_NAME);
|
|
assert.equal(command[2], HOVER_TOKEN_MESSAGE);
|
|
|
|
const raw = command[3] as string;
|
|
const parsed = JSON.parse(raw);
|
|
assert.equal(parsed.revision, 1);
|
|
assert.equal(parsed.hoveredTokenIndex, 0);
|
|
assert.equal(parsed.subtitle, '昨日は雨だった。');
|
|
assert.equal(parsed.tokens.length, 4);
|
|
});
|
|
|
|
test('createApplyHoveredTokenOverlayHandler sends clear payload when hovered token is missing', () => {
|
|
const commands: Array<(string | number)[]> = [];
|
|
const apply = createApplyHoveredTokenOverlayHandler({
|
|
getMpvClient: () => ({
|
|
connected: true,
|
|
send: ({ command }: { command: (string | number)[] }) => {
|
|
commands.push(command);
|
|
return true;
|
|
},
|
|
}),
|
|
getCurrentSubtitleData: () => SUBTITLE,
|
|
getHoveredTokenIndex: () => null,
|
|
getHoveredSubtitleRevision: () => 3,
|
|
getHoverTokenColor: () => null,
|
|
});
|
|
|
|
apply();
|
|
|
|
const parsed = JSON.parse(commands[0]?.[3] as string);
|
|
assert.equal(parsed.hoveredTokenIndex, null);
|
|
assert.equal(parsed.subtitle, null);
|
|
assert.equal(parsed.tokens.length, 0);
|
|
});
|
|
|
|
test('createApplyHoveredTokenOverlayHandler sends highlight payload when hover is active', () => {
|
|
const commands: Array<(string | number)[]> = [];
|
|
const apply = createApplyHoveredTokenOverlayHandler({
|
|
getMpvClient: () => ({
|
|
connected: true,
|
|
send: ({ command }: { command: (string | number)[] }) => {
|
|
commands.push(command);
|
|
return true;
|
|
},
|
|
}),
|
|
getCurrentSubtitleData: () => SUBTITLE,
|
|
getHoveredTokenIndex: () => 0,
|
|
getHoveredSubtitleRevision: () => 3,
|
|
getHoverTokenColor: () => '#c6a0f6',
|
|
});
|
|
|
|
apply();
|
|
|
|
const parsed = JSON.parse(commands[0]?.[3] as string);
|
|
assert.equal(parsed.hoveredTokenIndex, 0);
|
|
assert.equal(parsed.subtitle, '昨日は雨だった。');
|
|
assert.equal(parsed.tokens.length, 4);
|
|
assert.equal(parsed.colors.hover, 'C6A0F6');
|
|
assert.equal(commands[0]?.[0], 'script-message-to');
|
|
assert.equal(commands[0]?.[1], HOVER_SCRIPT_NAME);
|
|
});
|