Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 39 additions & 17 deletions userscript/source/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,25 +53,16 @@ export async function RunNamuLinkUserscript(BrowserWindow: typeof window, Usersc

const OCRInstance = CreateOcrWorkerClient(BrowserWindow, new Worker(URL.createObjectURL(new Blob([__OCR_WORKER_CODE__], { type: 'application/javascript' }))))

ArticleHTMLElement.addEventListener('vue:settled', async () => {
let Targeted = [...document.querySelectorAll('#app div[class] div[class] ~ div[class]')].filter(Ele => Ele instanceof HTMLElement)
Targeted = Targeted.filter(Ele =>
parseFloat(getComputedStyle(Ele).getPropertyValue('padding-top')) >= 20 ||
parseFloat(getComputedStyle(Ele).getPropertyValue('margin-top')) >= 20 ||
parseFloat(getComputedStyle(Ele).getPropertyValue('margin-bottom')) >= 12.5
)
Targeted = Targeted.filter(Ele => [...Ele.querySelectorAll('*')].filter(Child =>
parseFloat(getComputedStyle(Child).getPropertyValue('padding-top')) >= 5 && parseFloat(getComputedStyle(Child).getPropertyValue('border-bottom-width')) >= 0.1
).length === 1)
Targeted = await (async () => {
const NextTargeted = []
async function ExecuteOCR(Targeted: HTMLElement[]) {
const NextTargeted = []
Comment on lines +56 to +57
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ExecuteOCR returns an untyped empty array (const NextTargeted = []), so TypeScript infers any[] and you lose type-safety for Targeted after await ExecuteOCR(Targeted). Please type NextTargeted (e.g., HTMLElement[]) and/or add an explicit return type like Promise<HTMLElement[]>.

Suggested change
async function ExecuteOCR(Targeted: HTMLElement[]) {
const NextTargeted = []
async function ExecuteOCR(Targeted: HTMLElement[]): Promise<HTMLElement[]> {
const NextTargeted: HTMLElement[] = []

Copilot uses AI. Check for mistakes.
for (const Parent of Targeted) {
const CandidateChildren = [...Parent.querySelectorAll('*')]
.filter(Child => Child instanceof HTMLElement)
.filter(Child =>
Child instanceof HTMLImageElement ||
getComputedStyle(Child).backgroundImage !== 'none'
)
).filter(Child => parseFloat(getComputedStyle(Child).getPropertyValue('width')) >= 5 && parseFloat(getComputedStyle(Child).getPropertyValue('height')) >= 5)
.filter(Child => parseFloat(getComputedStyle(Child).getPropertyValue('width')) <= 50 && parseFloat(getComputedStyle(Child).getPropertyValue('height')) <= 50)
Comment on lines 61 to +65
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This chain calls getComputedStyle(Child) multiple times per element for width/height checks, which can be expensive and can trigger repeated style calculations. Consider caching the computed style (or width/height) per Child before filtering to reduce overhead in the vue:settled handler.

Copilot uses AI. Check for mistakes.
let MatchedCount = 0
for (const Child of CandidateChildren) {
const Result = await OCRInstance.DetectFromElement(Child, {
Expand All @@ -87,17 +78,48 @@ export async function RunNamuLinkUserscript(BrowserWindow: typeof window, Usersc
}
}
return NextTargeted
})()
}

ArticleHTMLElement.addEventListener('vue:settled', async () => {
let Targeted = [...document.querySelectorAll('#app div[class] div[class] ~ div[class]')].filter(Ele => Ele instanceof HTMLElement)
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This handler uses the global document for queries, while the rest of the userscript consistently uses the injected BrowserWindow.document (e.g., WaitForElement('#app', BrowserWindow.document) at line 47 and DOM creation at lines 132–135). For consistency and to avoid userscript sandbox/unsafeWindow mismatches, prefer querying via BrowserWindow.document (or ArticleHTMLElement.ownerDocument).

Suggested change
let Targeted = [...document.querySelectorAll('#app div[class] div[class] ~ div[class]')].filter(Ele => Ele instanceof HTMLElement)
let Targeted = [...BrowserWindow.document.querySelectorAll('#app div[class] div[class] ~ div[class]')].filter(Ele => Ele instanceof HTMLElement)

Copilot uses AI. Check for mistakes.
Targeted = Targeted.filter(Ele =>
parseFloat(getComputedStyle(Ele).getPropertyValue('padding-top')) >= 20 ||
parseFloat(getComputedStyle(Ele).getPropertyValue('margin-top')) >= 20 ||
parseFloat(getComputedStyle(Ele).getPropertyValue('margin-bottom')) >= 12.5
)
Targeted = Targeted.filter(Ele => {
let Children = [...Ele.querySelectorAll('*')].filter(Child => Child instanceof HTMLElement)
// non-HTMLTableElement
if (Children.filter(Child =>
parseFloat(getComputedStyle(Child).getPropertyValue('padding-top')) >= 5 &&
parseFloat(getComputedStyle(Child).getPropertyValue('border-bottom-width')) >= 0.1
).length === 1) return true
Comment on lines +92 to +96
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment // non-HTMLTableElement is misleading here: the predicate does not check that the child is not a table element, so table-related DOM could still satisfy this branch. Please either enforce the non-table condition or reword the comment to match the actual logic.

Copilot uses AI. Check for mistakes.
// HTMLTableElement
return Children.filter(Child => (Child instanceof HTMLTableElement || Child instanceof HTMLTableCellElement) &&
parseFloat(getComputedStyle(Child).getPropertyValue('padding-top')) >= 5 && parseFloat(getComputedStyle(Child).getPropertyValue('padding-bottom')) >= 5).length >= 2
})
Targeted = Targeted.filter(Ele => {
let Children = [...Ele.querySelectorAll('*')].filter(Child => Child instanceof HTMLElement)
Comment on lines +93 to +102
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ele.querySelectorAll('*') is executed again in subsequent Targeted = Targeted.filter(...) steps (e.g., the next filter starting at line 101). Consider collecting Children once per Ele (or combining these predicates) to avoid repeated full DOM traversals for the same element.

Suggested change
if (Children.filter(Child =>
parseFloat(getComputedStyle(Child).getPropertyValue('padding-top')) >= 5 &&
parseFloat(getComputedStyle(Child).getPropertyValue('border-bottom-width')) >= 0.1
).length === 1) return true
// HTMLTableElement
return Children.filter(Child => (Child instanceof HTMLTableElement || Child instanceof HTMLTableCellElement) &&
parseFloat(getComputedStyle(Child).getPropertyValue('padding-top')) >= 5 && parseFloat(getComputedStyle(Child).getPropertyValue('padding-bottom')) >= 5).length >= 2
})
Targeted = Targeted.filter(Ele => {
let Children = [...Ele.querySelectorAll('*')].filter(Child => Child instanceof HTMLElement)
const MatchesStructure = Children.filter(Child =>
parseFloat(getComputedStyle(Child).getPropertyValue('padding-top')) >= 5 &&
parseFloat(getComputedStyle(Child).getPropertyValue('border-bottom-width')) >= 0.1
).length === 1 ||
// HTMLTableElement
Children.filter(Child => (Child instanceof HTMLTableElement || Child instanceof HTMLTableCellElement) &&
parseFloat(getComputedStyle(Child).getPropertyValue('padding-top')) >= 5 && parseFloat(getComputedStyle(Child).getPropertyValue('padding-bottom')) >= 5).length >= 2
if (!MatchesStructure) return false

Copilot uses AI. Check for mistakes.
return !Children.some(Child => {
return parseFloat(getComputedStyle(Child).getPropertyValue('margin-bottom')) >= 10 && parseFloat(getComputedStyle(Child).getPropertyValue('padding-bottom')) >= 1 && parseFloat(getComputedStyle(Child).getPropertyValue('padding-top')) >= 1 &&
parseFloat(getComputedStyle(Child).getPropertyValue('border-top-width')) >= 0.25 && parseFloat(getComputedStyle(Child).getPropertyValue('border-bottom-width')) >= 0.25
Comment on lines +104 to +105
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inside this some(...) predicate, getComputedStyle(Child) is effectively invoked multiple times per element (once per property). Consider caching const Style = getComputedStyle(Child) and reading all needed properties from it to reduce repeated style lookups.

Suggested change
return parseFloat(getComputedStyle(Child).getPropertyValue('margin-bottom')) >= 10 && parseFloat(getComputedStyle(Child).getPropertyValue('padding-bottom')) >= 1 && parseFloat(getComputedStyle(Child).getPropertyValue('padding-top')) >= 1 &&
parseFloat(getComputedStyle(Child).getPropertyValue('border-top-width')) >= 0.25 && parseFloat(getComputedStyle(Child).getPropertyValue('border-bottom-width')) >= 0.25
const Style = getComputedStyle(Child)
return parseFloat(Style.getPropertyValue('margin-bottom')) >= 10 && parseFloat(Style.getPropertyValue('padding-bottom')) >= 1 && parseFloat(Style.getPropertyValue('padding-top')) >= 1 &&
parseFloat(Style.getPropertyValue('border-top-width')) >= 0.25 && parseFloat(Style.getPropertyValue('border-bottom-width')) >= 0.25

Copilot uses AI. Check for mistakes.
})
})
Targeted = await ExecuteOCR(Targeted)
Targeted.forEach(Ele => Targeted.push(...new Set([...Ele.querySelectorAll('*')].filter(Child => Child instanceof HTMLElement))))
Targeted = [...new Set(Targeted)]
let RealTargeted = Targeted.filter(Ele => parseFloat(getComputedStyle(Ele).getPropertyValue('padding-left')) >= 5 && parseFloat(getComputedStyle(Ele).getPropertyValue('border-right-width')) >= 0.1)
console.debug(`[${UserscriptName}] vue:settled RealTargeted`, RealTargeted)
RealTargeted.forEach(Ele => {
Ele.style.setProperty('display', 'none', 'important')
})
let FrameTargeted = Targeted.filter(Ele => Ele instanceof HTMLElement && Ele.innerText.trim().length === 0)
console.debug(`[${UserscriptName}] vue:settled FrameTargeted`, FrameTargeted)
FrameTargeted.forEach(Ele => {
let RealTabletTargeted = Targeted.filter(Ele => {
if (!(Ele instanceof HTMLElement) || !(Ele instanceof HTMLTableElement)) return false
let Children = [...Ele.querySelectorAll('*')].filter(Child => Child instanceof HTMLElement)
return Children.some(Child => parseFloat(getComputedStyle(Child).getPropertyValue('padding-top')) >= 5 && parseFloat(getComputedStyle(Child).getPropertyValue('padding-bottom')) >= 5)
})
console.debug(`[${UserscriptName}] vue:settled RealTabletTargeted`, RealTabletTargeted)
RealTabletTargeted.forEach(Ele => {
Comment on lines +116 to +122
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable name RealTabletTargeted looks like a typo given the surrounding table-related logic (HTMLTableElement). Consider renaming to RealTableTargeted (or similar) to avoid confusion and to keep log messages consistent.

Suggested change
let RealTabletTargeted = Targeted.filter(Ele => {
if (!(Ele instanceof HTMLElement) || !(Ele instanceof HTMLTableElement)) return false
let Children = [...Ele.querySelectorAll('*')].filter(Child => Child instanceof HTMLElement)
return Children.some(Child => parseFloat(getComputedStyle(Child).getPropertyValue('padding-top')) >= 5 && parseFloat(getComputedStyle(Child).getPropertyValue('padding-bottom')) >= 5)
})
console.debug(`[${UserscriptName}] vue:settled RealTabletTargeted`, RealTabletTargeted)
RealTabletTargeted.forEach(Ele => {
let RealTableTargeted = Targeted.filter(Ele => {
if (!(Ele instanceof HTMLElement) || !(Ele instanceof HTMLTableElement)) return false
let Children = [...Ele.querySelectorAll('*')].filter(Child => Child instanceof HTMLElement)
return Children.some(Child => parseFloat(getComputedStyle(Child).getPropertyValue('padding-top')) >= 5 && parseFloat(getComputedStyle(Child).getPropertyValue('padding-bottom')) >= 5)
})
console.debug(`[${UserscriptName}] vue:settled RealTableTargeted`, RealTableTargeted)
RealTableTargeted.forEach(Ele => {

Copilot uses AI. Check for mistakes.
Ele.style.setProperty('display', 'none', 'important')
})
})
Expand Down
Loading