Skip to content

Commit d1808b3

Browse files
committed
refactor(cricket-surface): tighten degraded-host and row-repair helpers
1 parent dfdc6a6 commit d1808b3

7 files changed

Lines changed: 628 additions & 358 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ direkt zu einer versionierten Release-Sektion.
3636
- Nutzerwirkung: Keine beabsichtigte sichtbare Verhaltensänderung; die Cricket-Surface bleibt bei gemergten Reihen, Label-Markern und aktiver Spielerperspektive fachlich gleich, ist intern aber robuster gegen Sonar-bedingte Parser- und Row-Recovery-Bereinigungen abgesichert.
3737
Technik: `mark-parser`, `row-repair` und angrenzende `pipeline`-Helfer wurden lokal in kleinere Hilfsfunktionen zerlegt, nutzen `dataset` für Mark-/Spielerindex-Felder, entfernen einen fehlerhaften Extra-Argument-Aufruf im Grid-Row-Building und glätten einige eng benachbarte Pipeline-Klarheitsstellen; zusätzliche Parser-, Row-Repair- und Cricket-Render-State-Regressionen sichern Mark-Parsing, Shortfall-Recovery, explizite Spielerindizes und die Cricket-Board-Perspektive gegen Drift ab.
3838

39+
- Nutzerwirkung: Keine beabsichtigte sichtbare Verhaltensänderung; die Cricket-Surface behandelt Match-Routen, degradierte Host-Erkennung und Row-Recovery weiterhin fachlich gleich, ist in diesen Pfaden aber enger gegen Sonar-bedingte Strukturbereinigungen und benachbarte DOM-Sonderfälle abgesichert.
40+
Technik: `pipeline` und `row-repair` wurden in kleine Helper für Match-Route-Normalisierung, Degraded-Host-Pane-Auswahl, Active-Throw-Suppression und explizite Spaltenplanung zerlegt; ergänzende Runtime-Regressionen sichern dabei Query-/Hash-bereinigte Match-IDs, degradierte Hosts mit zusätzlichem Nebenknoten und Shortfall-ausgerichtete Cricket-Reihen ohne riskanten Snapshot-Breitumbau gegen Drift ab.
41+
3942
## [2.1.28] - 2026-04-18
4043

4144
### Added

dist/autodarts-xconfig.user.js

Lines changed: 331 additions & 240 deletions
Large diffs are not rendered by default.

src/features/cricket-surface/pipeline.js

Lines changed: 151 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ function normalizeHashValue(hashValue) {
125125
export function extractMatchRouteId(windowRef, documentRef) {
126126
const locationRef = windowRef?.location || documentRef?.defaultView?.location || null;
127127
const routePath = normalizeRoutePath(locationRef?.pathname || "");
128-
const match = routePath.match(MATCH_ROUTE_PATTERN);
128+
const match = MATCH_ROUTE_PATTERN.exec(routePath);
129129
return match?.[1] || "";
130130
}
131131

@@ -272,6 +272,51 @@ function hasDegradedControlPaneText(node) {
272272
return /(20|19|18|17|16|15|bull)/i.test(compactText) || /\d{6,}/.test(compactText);
273273
}
274274

275+
function selectDegradedHostPanePair(hostRect, childEntries) {
276+
if (!hostRect || childEntries.length < 2) {
277+
return null;
278+
}
279+
280+
const dominantChildren = childEntries
281+
.filter((entry) => entry.rect.height >= hostRect.height * DEGRADED_HOST_MIN_PANE_HEIGHT_RATIO)
282+
.sort((left, right) => {
283+
const leftArea = left.rect.width * left.rect.height;
284+
const rightArea = right.rect.width * right.rect.height;
285+
return rightArea - leftArea;
286+
})
287+
.slice(0, 2)
288+
.sort((left, right) => left.rect.left - right.rect.left);
289+
290+
return dominantChildren.length === 2 ? dominantChildren : null;
291+
}
292+
293+
function buildDegradedHostCandidate(current, gridRoot, hostRect, panePair) {
294+
if (!Array.isArray(panePair) || panePair.length !== 2) {
295+
return null;
296+
}
297+
298+
const [leftPane, rightPane] = panePair;
299+
const paneSpan = leftPane.rect.width + rightPane.rect.width;
300+
const leftWidthRatio = hostRect.width > 0 ? leftPane.rect.width / hostRect.width : 0;
301+
const rightWidthRatio = hostRect.width > 0 ? rightPane.rect.width / hostRect.width : 0;
302+
if (
303+
!containsNode(leftPane.node, gridRoot) ||
304+
paneSpan < hostRect.width * DEGRADED_HOST_MIN_PANE_SPAN_RATIO ||
305+
leftWidthRatio < DEGRADED_HOST_MIN_LEFT_PANE_WIDTH_RATIO ||
306+
rightWidthRatio < DEGRADED_HOST_MIN_RIGHT_PANE_WIDTH_RATIO ||
307+
!hasDegradedControlPaneText(rightPane.node)
308+
) {
309+
return null;
310+
}
311+
312+
return {
313+
hostNode: current,
314+
leftPaneNode: leftPane.node,
315+
rightPaneNode: rightPane.node,
316+
rightPaneText: readCompactNodeText(rightPane.node),
317+
};
318+
}
319+
275320
function findDegradedHostCandidate(extracted, options = {}) {
276321
const gridRoot = extracted?.gridSnapshot?.root || null;
277322
const documentRef = options.documentRef || extracted?.documentRef || null;
@@ -293,37 +338,10 @@ function findDegradedHostCandidate(extracted, options = {}) {
293338
while (current && current !== documentRef.body && current !== documentRef.documentElement) {
294339
const hostRect = readNodeRect(current);
295340
const childEntries = readVisibleChildEntries(current);
296-
if (hostRect && childEntries.length >= 2) {
297-
const dominantChildren = childEntries
298-
.filter((entry) => entry.rect.height >= hostRect.height * DEGRADED_HOST_MIN_PANE_HEIGHT_RATIO)
299-
.sort((left, right) => {
300-
const leftArea = left.rect.width * left.rect.height;
301-
const rightArea = right.rect.width * right.rect.height;
302-
return rightArea - leftArea;
303-
})
304-
.slice(0, 2)
305-
.sort((left, right) => left.rect.left - right.rect.left);
306-
307-
if (dominantChildren.length === 2) {
308-
const [leftPane, rightPane] = dominantChildren;
309-
const paneSpan = leftPane.rect.width + rightPane.rect.width;
310-
const leftWidthRatio = hostRect.width > 0 ? leftPane.rect.width / hostRect.width : 0;
311-
const rightWidthRatio = hostRect.width > 0 ? rightPane.rect.width / hostRect.width : 0;
312-
if (
313-
containsNode(leftPane.node, gridRoot) &&
314-
paneSpan >= hostRect.width * DEGRADED_HOST_MIN_PANE_SPAN_RATIO &&
315-
leftWidthRatio >= DEGRADED_HOST_MIN_LEFT_PANE_WIDTH_RATIO &&
316-
rightWidthRatio >= DEGRADED_HOST_MIN_RIGHT_PANE_WIDTH_RATIO &&
317-
hasDegradedControlPaneText(rightPane.node)
318-
) {
319-
return {
320-
hostNode: current,
321-
leftPaneNode: leftPane.node,
322-
rightPaneNode: rightPane.node,
323-
rightPaneText: readCompactNodeText(rightPane.node),
324-
};
325-
}
326-
}
341+
const panePair = selectDegradedHostPanePair(hostRect, childEntries);
342+
const candidate = buildDegradedHostCandidate(current, gridRoot, hostRect, panePair);
343+
if (candidate) {
344+
return candidate;
327345
}
328346

329347
current = current.parentElement || null;
@@ -631,7 +649,7 @@ function hasOwnMarkValue(node, options = {}) {
631649
}
632650
const cricketRules = options.cricketRules;
633651
const allowTextMarkValue = options.allowTextMarkValue !== false;
634-
if (typeof node.getAttribute === "function" && node.getAttribute("data-marks") !== null) {
652+
if (Object.hasOwn(node?.dataset || {}, "marks")) {
635653
return true;
636654
}
637655
if (typeof node.querySelectorAll === "function") {
@@ -963,17 +981,8 @@ function haveEquivalentThrowSignatures(leftThrows, rightThrows, cricketRules, ta
963981
return left.every((entry, index) => entry === right[index]);
964982
}
965983

966-
function shouldApplyActiveThrowPreview(options = {}) {
967-
const gameState = options.gameState;
968-
const cricketRules = options.cricketRules;
969-
const targetOrder = options.targetOrder;
970-
const activeThrows = Array.isArray(options.activeThrows) ? options.activeThrows : [];
971-
const activePlayerIndex = Number.isFinite(Number(options.activePlayerIndex))
972-
? Math.max(0, Math.round(Number(options.activePlayerIndex)))
973-
: 0;
974-
const snapshotMatch = options.snapshotMatch || null;
975-
976-
const debug = {
984+
function createActiveThrowPreviewDebug(activeThrows, cricketRules, targetOrder) {
985+
return {
977986
throwCount: activeThrows.length,
978987
applied: false,
979988
suppressionReason: "",
@@ -982,66 +991,120 @@ function shouldApplyActiveThrowPreview(options = {}) {
982991
activeThrowsSignature: toThrowSignatureParts(activeThrows, cricketRules, targetOrder).join(","),
983992
activeTurnSignature: "",
984993
};
994+
}
985995

986-
if (!activeThrows.length) {
987-
debug.suppressionReason = "no-active-throws";
988-
return debug;
989-
}
990-
996+
function readActiveTurnPreviewState(gameState, cricketRules, targetOrder) {
991997
const activeTurn =
992998
gameState && typeof gameState.getActiveTurn === "function"
993999
? gameState.getActiveTurn()
9941000
: null;
1001+
if (!activeTurn || typeof activeTurn !== "object") {
1002+
return {
1003+
finished: false,
1004+
signature: "",
1005+
};
1006+
}
9951007

996-
if (activeTurn && typeof activeTurn === "object") {
997-
debug.activeTurnSignature = toThrowSignatureParts(
1008+
return {
1009+
finished: Boolean(String(activeTurn.finishedAt || "").trim()),
1010+
signature: toThrowSignatureParts(
9981011
Array.isArray(activeTurn.throws) ? activeTurn.throws : [],
9991012
cricketRules,
10001013
targetOrder
1001-
).join(",");
1002-
debug.activeTurnFinished = Boolean(String(activeTurn.finishedAt || "").trim());
1014+
).join(","),
1015+
};
1016+
}
1017+
1018+
function listSnapshotTurns(snapshotMatch) {
1019+
return Array.isArray(snapshotMatch?.turns)
1020+
? snapshotMatch.turns.filter((turn) => turn && typeof turn === "object")
1021+
: [];
1022+
}
1023+
1024+
function isTurnOwnedByPlayer(turn, playerId) {
1025+
return String(turn?.playerId || "").trim() === playerId;
1026+
}
1027+
1028+
function isFinishedTurnWithThrows(turn) {
1029+
return Boolean(String(turn?.finishedAt || "").trim()) &&
1030+
Array.isArray(turn?.throws) &&
1031+
turn.throws.length > 0;
1032+
}
1033+
1034+
function shouldSuppressPreviewForFinishedSnapshotTurn(
1035+
snapshotMatch,
1036+
activePlayerIndex,
1037+
activeThrows,
1038+
cricketRules,
1039+
targetOrder
1040+
) {
1041+
const activePlayerId = getPlayerIdByIndex(snapshotMatch, activePlayerIndex);
1042+
if (!activePlayerId) {
1043+
return false;
1044+
}
1045+
1046+
const turns = listSnapshotTurns(snapshotMatch);
1047+
const unfinishedTurnsForActivePlayer = turns.filter((turn) => {
1048+
return isTurnOwnedByPlayer(turn, activePlayerId) && !String(turn.finishedAt || "").trim();
1049+
});
1050+
if (unfinishedTurnsForActivePlayer.length > 0) {
1051+
return false;
1052+
}
1053+
1054+
const newestFinishedTurn = selectNewestTurnCandidate(
1055+
turns.filter((turn) => {
1056+
return isTurnOwnedByPlayer(turn, activePlayerId) && isFinishedTurnWithThrows(turn);
1057+
})
1058+
);
1059+
return Boolean(
1060+
newestFinishedTurn &&
1061+
haveEquivalentThrowSignatures(
1062+
newestFinishedTurn.throws,
1063+
activeThrows,
1064+
cricketRules,
1065+
targetOrder
1066+
)
1067+
);
1068+
}
1069+
1070+
function shouldApplyActiveThrowPreview(options = {}) {
1071+
const gameState = options.gameState;
1072+
const cricketRules = options.cricketRules;
1073+
const targetOrder = options.targetOrder;
1074+
const activeThrows = Array.isArray(options.activeThrows) ? options.activeThrows : [];
1075+
const activePlayerIndex = Number.isFinite(Number(options.activePlayerIndex))
1076+
? Math.max(0, Math.round(Number(options.activePlayerIndex)))
1077+
: 0;
1078+
const snapshotMatch = options.snapshotMatch || null;
1079+
1080+
const debug = createActiveThrowPreviewDebug(activeThrows, cricketRules, targetOrder);
1081+
1082+
if (!activeThrows.length) {
1083+
debug.suppressionReason = "no-active-throws";
1084+
return debug;
10031085
}
10041086

1087+
const activeTurnState = readActiveTurnPreviewState(gameState, cricketRules, targetOrder);
1088+
debug.activeTurnSignature = activeTurnState.signature;
1089+
debug.activeTurnFinished = activeTurnState.finished;
1090+
10051091
if (debug.activeTurnFinished) {
10061092
debug.suppressionReason = "active-turn-finished";
10071093
return debug;
10081094
}
10091095

1010-
if (snapshotMatch && Array.isArray(snapshotMatch.turns)) {
1011-
const activePlayerId = getPlayerIdByIndex(snapshotMatch, activePlayerIndex);
1012-
const turns = snapshotMatch.turns.filter((turn) => turn && typeof turn === "object");
1013-
const unfinishedTurnsForActivePlayer = turns.filter((turn) => {
1014-
return (
1015-
String(turn.playerId || "").trim() === activePlayerId &&
1016-
!String(turn.finishedAt || "").trim()
1017-
);
1018-
});
1019-
1020-
if (activePlayerId && unfinishedTurnsForActivePlayer.length === 0) {
1021-
const finishedTurnsForActivePlayer = turns.filter((turn) => {
1022-
return (
1023-
String(turn.playerId || "").trim() === activePlayerId &&
1024-
String(turn.finishedAt || "").trim() &&
1025-
Array.isArray(turn.throws) &&
1026-
turn.throws.length > 0
1027-
);
1028-
});
1029-
1030-
const newestFinishedTurn = selectNewestTurnCandidate(finishedTurnsForActivePlayer);
1031-
if (
1032-
newestFinishedTurn &&
1033-
haveEquivalentThrowSignatures(
1034-
newestFinishedTurn.throws,
1035-
activeThrows,
1036-
cricketRules,
1037-
targetOrder
1038-
)
1039-
) {
1040-
debug.matchedFinishedTurn = true;
1041-
debug.suppressionReason = "matches-last-finished-turn";
1042-
return debug;
1043-
}
1044-
}
1096+
if (
1097+
shouldSuppressPreviewForFinishedSnapshotTurn(
1098+
snapshotMatch,
1099+
activePlayerIndex,
1100+
activeThrows,
1101+
cricketRules,
1102+
targetOrder
1103+
)
1104+
) {
1105+
debug.matchedFinishedTurn = true;
1106+
debug.suppressionReason = "matches-last-finished-turn";
1107+
return debug;
10451108
}
10461109

10471110
debug.applied = true;

0 commit comments

Comments
 (0)