mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-28 06:22:45 -08:00
feat: integrate n+1 target highlighting
- Merge feature branch changes for n+1 target-only highlight flow - Extend merged token model and token-merger to mark exactly-one unknown targets - Thread n+1 candidate metadata through tokenizer and config systems - Update subtitle renderer/state to route configured colors and new token class - Resolve merge conflicts in core service tests, including subtitle and subsync behavior
This commit is contained in:
@@ -69,6 +69,9 @@ export type RendererState = {
|
||||
lastHoverSelectionKey: string;
|
||||
lastHoverSelectionNode: Text | null;
|
||||
|
||||
knownWordColor: string;
|
||||
nPlusOneColor: string;
|
||||
|
||||
keybindingsMap: Map<string, (string | number)[]>;
|
||||
chordPending: boolean;
|
||||
chordTimeout: ReturnType<typeof setTimeout> | null;
|
||||
@@ -125,6 +128,9 @@ export function createRendererState(): RendererState {
|
||||
lastHoverSelectionKey: "",
|
||||
lastHoverSelectionNode: null,
|
||||
|
||||
knownWordColor: "#a6da95",
|
||||
nPlusOneColor: "#c6a0f6",
|
||||
|
||||
keybindingsMap: new Map(),
|
||||
chordPending: false,
|
||||
chordTimeout: null,
|
||||
|
||||
@@ -248,6 +248,8 @@ body {
|
||||
font-size: 35px;
|
||||
line-height: 1.5;
|
||||
color: #cad3f5;
|
||||
--subtitle-known-word-color: #a6da95;
|
||||
--subtitle-n-plus-one-color: #c6a0f6;
|
||||
text-shadow:
|
||||
2px 2px 4px rgba(0, 0, 0, 0.8),
|
||||
-1px -1px 2px rgba(0, 0, 0, 0.5);
|
||||
@@ -285,10 +287,15 @@ body.settings-modal-open #subtitleContainer {
|
||||
}
|
||||
|
||||
#subtitleRoot .word.word-known {
|
||||
color: #a6da95;
|
||||
color: var(--subtitle-known-word-color, #a6da95);
|
||||
text-shadow: 0 0 6px rgba(166, 218, 149, 0.35);
|
||||
}
|
||||
|
||||
#subtitleRoot .word.word-n-plus-one {
|
||||
color: var(--subtitle-n-plus-one-color, #c6a0f6);
|
||||
text-shadow: 0 0 6px rgba(198, 160, 246, 0.35);
|
||||
}
|
||||
|
||||
#subtitleRoot .word:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 3px;
|
||||
|
||||
@@ -23,13 +23,13 @@ function renderWithTokens(root: HTMLElement, tokens: MergedToken[]): void {
|
||||
|
||||
if (surface.includes("\n")) {
|
||||
const parts = surface.split("\n");
|
||||
for (let i = 0; i < parts.length; i += 1) {
|
||||
if (parts[i]) {
|
||||
const span = document.createElement("span");
|
||||
span.className = token.isKnown ? "word word-known" : "word";
|
||||
span.textContent = parts[i];
|
||||
if (token.reading) span.dataset.reading = token.reading;
|
||||
if (token.headword) span.dataset.headword = token.headword;
|
||||
for (let i = 0; i < parts.length; i += 1) {
|
||||
if (parts[i]) {
|
||||
const span = document.createElement("span");
|
||||
span.className = computeWordClass(token);
|
||||
span.textContent = parts[i];
|
||||
if (token.reading) span.dataset.reading = token.reading;
|
||||
if (token.headword) span.dataset.headword = token.headword;
|
||||
fragment.appendChild(span);
|
||||
}
|
||||
if (i < parts.length - 1) {
|
||||
@@ -40,7 +40,7 @@ function renderWithTokens(root: HTMLElement, tokens: MergedToken[]): void {
|
||||
}
|
||||
|
||||
const span = document.createElement("span");
|
||||
span.className = token.isKnown ? "word word-known" : "word";
|
||||
span.className = computeWordClass(token);
|
||||
span.textContent = surface;
|
||||
if (token.reading) span.dataset.reading = token.reading;
|
||||
if (token.headword) span.dataset.headword = token.headword;
|
||||
@@ -50,6 +50,18 @@ function renderWithTokens(root: HTMLElement, tokens: MergedToken[]): void {
|
||||
root.appendChild(fragment);
|
||||
}
|
||||
|
||||
function computeWordClass(token: MergedToken): string {
|
||||
if (token.isNPlusOneTarget) {
|
||||
return "word word-n-plus-one";
|
||||
}
|
||||
|
||||
if (token.isKnown) {
|
||||
return "word word-known";
|
||||
}
|
||||
|
||||
return "word";
|
||||
}
|
||||
|
||||
function renderCharacterLevel(root: HTMLElement, text: string): void {
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
@@ -173,6 +185,19 @@ export function createSubtitleRenderer(ctx: RendererContext) {
|
||||
ctx.dom.subtitleContainer.style.background = style.backgroundColor;
|
||||
}
|
||||
|
||||
const knownWordColor =
|
||||
style.knownWordColor ?? ctx.state.knownWordColor ?? "#a6da95";
|
||||
const nPlusOneColor =
|
||||
style.nPlusOneColor ?? ctx.state.nPlusOneColor ?? "#c6a0f6";
|
||||
|
||||
ctx.state.knownWordColor = knownWordColor;
|
||||
ctx.state.nPlusOneColor = nPlusOneColor;
|
||||
ctx.dom.subtitleRoot.style.setProperty(
|
||||
"--subtitle-known-word-color",
|
||||
knownWordColor,
|
||||
);
|
||||
ctx.dom.subtitleRoot.style.setProperty("--subtitle-n-plus-one-color", nPlusOneColor);
|
||||
|
||||
const secondaryStyle = style.secondary;
|
||||
if (!secondaryStyle) return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user