diff --git a/src/content/market/dom-sync.ts b/src/content/market/dom-sync.ts index d0540bf..1b8865f 100644 --- a/src/content/market/dom-sync.ts +++ b/src/content/market/dom-sync.ts @@ -431,7 +431,7 @@ function readDivGridAuthorIds(root: ParentNode): string[] | null { ) ?? null : null; const authorColumn = authorSection - ? getDirectContentColumns(authorSection)[0] ?? null + ? getNativeAuthorColumn(authorSection) : null; if (!authorColumn) { return null; @@ -497,7 +497,7 @@ function syncDivGridRoot(root: HTMLElement): MarketTableDom | null { type: "body" }); - const authorColumn = getDirectContentColumns(authorSection)[0] ?? null; + const authorColumn = getNativeAuthorColumn(authorSection); const actionColumn = getActionColumn(rightSection); if (!authorColumn || !actionColumn) { @@ -752,7 +752,11 @@ function ensureDivHeaderCell( nextCell.textContent = label; applyColumnWidth(nextCell, field); applyPluginHeaderCellStyles(nextCell); - container.appendChild(nextCell); + if (field === SELECTION_COLUMN_KEY) { + container.insertBefore(nextCell, templateCell); + } else { + container.appendChild(nextCell); + } return nextCell; } @@ -774,7 +778,11 @@ function ensureDivBodyColumn( nextColumn.dataset.marketColumnGroup = field; applyColumnWidth(nextColumn, field); syncDivColumnCells(nextColumn, templateColumn, field, rowCount); - container.appendChild(nextColumn); + if (field === SELECTION_COLUMN_KEY) { + container.insertBefore(nextColumn, templateColumn); + } else { + container.appendChild(nextColumn); + } return nextColumn; } @@ -802,7 +810,9 @@ function syncDivColumnCells( templateCells[index] ?? templateCells[templateCells.length - 1] ?? null; const nextCell = field === SELECTION_COLUMN_KEY - ? createBareContentCell(column.ownerDocument) + ? templateCell + ? createSelectionContentCell(templateCell) + : createBareContentCell(column.ownerDocument) : templateCell ? cloneElementShallow(templateCell) : createBareContentCell(column.ownerDocument); @@ -1106,6 +1116,21 @@ function getActionColumn(bodySection: HTMLElement): HTMLElement | null { return columns[columns.length - 1] ?? null; } +function getNativeAuthorColumn(authorSection: HTMLElement): HTMLElement | null { + return ( + getDirectContentColumns(authorSection).find( + (column) => + !column.dataset.marketColumnGroup && + getDirectContentCells(column).some( + (cell) => + cell.querySelector("a") || + cell.querySelector(".author-nickname") || + Boolean(cell.dataset.authorId) + ) + ) ?? null + ); +} + function getDirectHeaderCells(section: Element): HTMLElement[] { return Array.from(section.querySelectorAll(".header-cell")).filter( (cell): cell is HTMLElement => @@ -1162,6 +1187,13 @@ function createBareContentCell(document: Document): HTMLElement { return cell; } +function createSelectionContentCell(templateCell: HTMLElement): HTMLElement { + const cell = cloneElementShallow(templateCell); + cell.removeAttribute("data-testid"); + cell.removeAttribute("data-author-id"); + return cell; +} + function extractAuthorId(authorCell: HTMLElement): string { const explicitAuthorId = authorCell.dataset.authorId; if (explicitAuthorId) { diff --git a/tests/market-content-entry.test.ts b/tests/market-content-entry.test.ts index ff52123..98592f6 100644 --- a/tests/market-content-entry.test.ts +++ b/tests/market-content-entry.test.ts @@ -2682,9 +2682,7 @@ function installPaginationHarness( } const renderPage = () => { - const authorColumn = document.querySelector( - '[data-testid="author-section"] .content-column' - ) as HTMLElement | null; + const authorColumn = readAuthorContentColumn(); const middleColumn = document.querySelector( '.middle-columns .content-column' ) as HTMLElement | null; @@ -2777,9 +2775,7 @@ function installAsyncPaginationHarness( }; const renderPage = () => { - const authorColumn = document.querySelector( - '[data-testid="author-section"] .content-column' - ) as HTMLElement | null; + const authorColumn = readAuthorContentColumn(); const middleColumn = document.querySelector( '.middle-columns .content-column' ) as HTMLElement | null; @@ -2883,9 +2879,7 @@ function installLaggyPaginationHarness( }; const renderPage = () => { - const authorColumn = document.querySelector( - '[data-testid="author-section"] .content-column' - ) as HTMLElement | null; + const authorColumn = readAuthorContentColumn(); const middleColumn = document.querySelector( '.middle-columns .content-column' ) as HTMLElement | null; @@ -2940,9 +2934,7 @@ function installLaggyPaginationHarness( pageIndex += 1; updatePaginationState(pageIndex); - const authorColumn = document.querySelector( - '[data-testid="author-section"] .content-column' - ) as HTMLElement | null; + const authorColumn = readAuthorContentColumn(); const middleColumn = document.querySelector( '.middle-columns .content-column' ) as HTMLElement | null; @@ -3014,9 +3006,7 @@ function installProgressivePaginationHarness( price21To60s: string; }> ) => { - const authorColumn = document.querySelector( - '[data-testid="author-section"] .content-column' - ) as HTMLElement | null; + const authorColumn = readAuthorContentColumn(); const middleColumn = document.querySelector( '.middle-columns .content-column' ) as HTMLElement | null; @@ -3118,9 +3108,7 @@ function installLazyFieldHydrationHarness(options: { const rightColumns = document.querySelectorAll( '[data-testid="right-section"] > .content-column' ); - const authorCells = Array.from( - document.querySelectorAll('[data-testid="author-section"] .content-column .content-cell') - ) as HTMLElement[]; + const authorCells = readAuthorContentCells(); const middleCells = Array.from( document.querySelectorAll(".middle-columns .content-column .content-cell") ) as HTMLElement[]; @@ -3288,11 +3276,7 @@ function installPagedLazyFieldHydrationHarness(options: { const rightColumns = document.querySelectorAll( '[data-testid="right-section"] > .content-column' ); - const authorCells = Array.from( - document.querySelectorAll( - '[data-testid="author-section"] .content-column .content-cell' - ) - ) as HTMLElement[]; + const authorCells = readAuthorContentCells(); const middleCells = Array.from( document.querySelectorAll(".middle-columns .content-column .content-cell") ) as HTMLElement[]; @@ -3531,14 +3515,40 @@ function readRowOrder() { } function readDivAuthorOrder() { - const authorColumn = document.querySelector( - '[data-testid="author-section"] .content-column' - ); + const authorColumn = readAuthorContentColumn(); return readVisualCells(authorColumn).map( (cell) => cell.querySelector("a")?.textContent?.trim() ?? "" ); } +function readAuthorContentColumn(): HTMLElement | null { + const nativeColumns = Array.from( + document.querySelectorAll('[data-testid="author-section"] > .content-column') + ).filter( + (column): column is HTMLElement => + column instanceof HTMLElement && !column.dataset.marketColumnGroup + ); + if (nativeColumns.length === 1) { + return nativeColumns[0]; + } + + return ( + nativeColumns.find((column) => + Boolean( + column.querySelector('a[href*="/author-homepage/"]') || + column.querySelector(".author-nickname") || + column.querySelector("[data-testid^='author-cell-']") + ) + ) ?? null + ); +} + +function readAuthorContentCells(): HTMLElement[] { + return Array.from( + readAuthorContentColumn()?.querySelectorAll(":scope > .content-cell") ?? [] + ).filter((cell): cell is HTMLElement => cell instanceof HTMLElement); +} + function readDivRightRowTexts(rowIndex: number) { return Array.from( document.querySelectorAll('[data-testid="right-section"] > .content-column'), diff --git a/tests/market-dom-sync.test.ts b/tests/market-dom-sync.test.ts index 0a2309e..385f921 100644 --- a/tests/market-dom-sync.test.ts +++ b/tests/market-dom-sync.test.ts @@ -307,6 +307,28 @@ describe("market-dom-sync", () => { expect(readAuthorNames()).toEqual(["达人 A", "达人 B"]); }); + test("inserts the selection column before the native author column", () => { + document.body.innerHTML = buildRealMarketGridFixture(); + + const table = syncMarketTable(document); + if (!table) { + throw new Error("Expected market table"); + } + + expect(readAuthorSectionColumnKeys()).toEqual(["selection", "author"]); + }); + + test("keeps the selection cells aligned to the native author row heights", () => { + document.body.innerHTML = buildRealMarketGridFixture(); + + const table = syncMarketTable(document); + if (!table) { + throw new Error("Expected market table"); + } + + expect(readSelectionCellHeights()).toEqual(["120px", "120px"]); + }); + test("uses native-like alignment styles for plugin cells", () => { document.body.innerHTML = buildRealMarketGridFixtureWithScopedAttributes(); @@ -962,14 +984,37 @@ function readSelectionRowCheckboxCount() { } function readAuthorNames() { - const authorColumn = document.querySelector( - '[data-testid="author-section"] .content-column' - ); + const authorColumn = Array.from( + document.querySelectorAll('[data-testid="author-section"] > .content-column') + ).find((column) => (column as HTMLElement).querySelector("a")) as Element | null; return readVisualCells(authorColumn).map( (cell) => cell.querySelector("a")?.textContent?.trim() ?? "" ); } +function readAuthorSectionColumnKeys() { + return Array.from( + document.querySelectorAll('[data-testid="author-section"] > .content-column'), + (column) => { + const element = column as HTMLElement; + if (element.dataset.marketColumnGroup) { + return element.dataset.marketColumnGroup; + } + + return element.querySelector("a") ? "author" : "unknown"; + } + ); +} + +function readSelectionCellHeights() { + return Array.from( + document.querySelectorAll( + '[data-testid="author-section"] [data-market-column-group="selection"] > .content-cell' + ), + (cell) => (cell as HTMLElement).style.height + ); +} + function readAuthorRowHiddenStates() { return Array.from( document.querySelectorAll('[data-testid^="author-cell-"]'),