diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 5705e41..0000000 --- a/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -*.js -*.mjs diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index 37a0ec5..0000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,14 +0,0 @@ -// eslint-disable-next-line no-undef -module.exports = { - env: { browser: true }, - extends: [ "eslint:recommended", "plugin:@typescript-eslint/recommended" ], - parser: "@typescript-eslint/parser", - plugins: [ "@typescript-eslint" ], - rules: { - "indent": [ "error", "tab" ], - "linebreak-style": [ "error", "unix" ], - "semi": [ "error", "always" ], - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": 1, - }, -}; diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..a02ede5 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,45 @@ +import eslint from "@eslint/js"; +import tseslint from "typescript-eslint"; + +export default [ + ...tseslint.config({ + files: [ "**/*.ts", "**/*.mts" ], + extends: [ + eslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + ...tseslint.configs.stylisticTypeChecked, + { + languageOptions: { + parserOptions: { + project: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + ], + }), + { + files: [ "**/*.ts", "**/*.mts" ], + plugins: { + tseslint: tseslint.plugin, + }, + rules: { + "indent": [ "error", "tab" ], + "semi": [ "error", "always" ], + "linebreak-style": [ "error", "unix" ], + "@typescript-eslint/array-type": [ "error", { default: "generic" } ], + "@typescript-eslint/no-empty-object-type": [ "error", { allowInterfaces: "always" } ], + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-unused-vars": "warn", + "@typescript-eslint/prefer-optional-chain": "off", + "@typescript-eslint/dot-notation": "off", + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-misused-promises": "off", + "@typescript-eslint/require-await": "off", + "@typescript-eslint/no-unnecessary-type-assertion": "off", + }, + }, + { + ignores: [ "dist/**/*" ], + }, +]; diff --git a/package.json b/package.json index 0882372..9439187 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "mark-my-search", "displayName": "Mark My Search", + "type": "module", "scripts": { "icons-convert": "bash -c 'mkdir -p icons/dist; SIZES=(16 32 48 64 96 128 240 300); for SIZE in ${SIZES[@]}; do inkscape icons/mms.svg -w $SIZE -h $SIZE -o icons/dist/mms-${SIZE}.png; done; SIZES=(32); for SIZE in ${SIZES[@]}; do inkscape icons/mms-off.svg -w $SIZE -h $SIZE -o icons/dist/mms-off-${SIZE}.png; done'", "scripts-build": "rm --recursive dist; tsc --project tsconfig.json", @@ -24,12 +25,12 @@ }, "homepage": "https://github.com/searchmarkers/mark-my-search#readme", "devDependencies": { - "inkscape": "3.1.1", - "@types/firefox-webext-browser": "120.0.0", "@types/chrome": "0.0.253", - "@typescript-eslint/eslint-plugin": "6.13.1", - "@typescript-eslint/parser": "6.13.1", - "eslint": "8.54.0", + "@types/firefox-webext-browser": "120.0.0", + "typescript-eslint": "8.0.0-alpha.30", + "eslint": "9.5.0", + "globals": "^15.6.0", + "inkscape": "3.1.1", "typescript": "5.3.2" } } diff --git a/src/background.mts b/src/background.mts index b7b0633..ae66247 100644 --- a/src/background.mts +++ b/src/background.mts @@ -528,7 +528,7 @@ chrome.commands.onCommand.addListener(async commandString => { (chrome.action["openPopup"] ?? (() => undefined))(); } const [ tab ] = await chrome.tabs.query({ active: true, lastFocusedWindow: true }); - const tabId = tab.id as number; // `tab.id` is always defined for this case. + const tabId = tab.id!; // `tab.id` is always defined for this case. const commandInfo = parseCommand(commandString); switch (commandInfo.type) { case "openPopup": { @@ -666,8 +666,14 @@ const handleMessage = async (message: Message.Background): Promise { (async () => { - message.tabId ??= sender.tab?.id ?? (await chrome.tabs.query({ active: true, lastFocusedWindow: true }))[0].id; - handleMessage(message as Message.Background).then(sendResponse); + const messageWithTabId: Message.Background = ("tabId" in message + ? message + : { + ...message, + tabId: sender.tab?.id ?? (await chrome.tabs.query({ active: true, lastFocusedWindow: true }))[0].id ?? NaN, + } + ); + handleMessage(messageWithTabId).then(sendResponse); })(); return true; }); diff --git a/src/content.mts b/src/content.mts index 641a930..a52520e 100644 --- a/src/content.mts +++ b/src/content.mts @@ -39,8 +39,8 @@ interface ControlsInfo { */ const focusReturnToDocument = (): boolean => { const activeElement = document.activeElement; - if (activeElement && activeElement.tagName === "INPUT" && activeElement.closest(`#${EleID.BAR}`)) { - (activeElement as HTMLInputElement).blur(); + if (activeElement instanceof HTMLInputElement && activeElement.closest(`#${EleID.BAR}`)) { + activeElement.blur(); return true; } return false; @@ -67,7 +67,7 @@ const getTermsFromSelection = (termTokens: TermTokens): ReadonlyArray })() .map(phrase => phrase.replace(/\p{Other}/gu, "")) .filter(phrase => phrase !== "").map(phrase => new MatchTerm(phrase)); - const termSelectors: Set = new Set(); + const termSelectors = new Set(); for (const term of termsAll) { const token = termTokens.get(term); if (!termSelectors.has(token)) { @@ -150,8 +150,8 @@ const styleElementsCleanup = () => { if (style && style.textContent !== "") { style.textContent = ""; } - const stylePaint = document.getElementById(EleID.STYLE_PAINT) as HTMLStyleElement | null; - if (stylePaint && stylePaint.sheet) { + const stylePaint = document.getElementById(EleID.STYLE_PAINT); + if (stylePaint instanceof HTMLStyleElement && stylePaint.sheet) { while (stylePaint.sheet.cssRules.length) { stylePaint.sheet.deleteRule(0); } @@ -216,16 +216,16 @@ const respondToCommand_factory = ( }; }; -interface TermSetter extends TermReplacer, TermAppender { - setTerms: (termsNew: ReadonlyArray) => void; +interface TermSetter extends TermReplacer, TermAppender { + setTerms: (termsNew: ReadonlyArray) => Async extends true ? Promise : void; } -interface TermReplacer { - replaceTerm: (term: MatchTerm | null, index: number) => void; +interface TermReplacer { + replaceTerm: (term: MatchTerm | null, index: number) => Async extends true ? Promise : void; } -interface TermAppender { - appendTerm: (term: MatchTerm) => void; +interface TermAppender { + appendTerm: (term: MatchTerm) => Async extends true ? Promise : void; } (() => { @@ -267,7 +267,7 @@ interface TermAppender { }; const updateTermStatus = (term: MatchTerm) => getToolbar(false)?.updateTermStatus(term); const highlighter: AbstractEngineManager = new EngineManager(updateTermStatus, termTokens, termPatterns); - const termSetterInternal: TermSetter = { + const termSetterInternal: TermSetter = { setTerms: termsNew => { if (itemsMatch(terms, termsNew, termEquals)) { return; @@ -284,7 +284,7 @@ interface TermAppender { replaceTerm: (term, termIndex) => { const termsOld: ReadonlyArray = [ ...terms ]; if (term) { - const termsNew = terms as Array; + const termsNew = [ ...terms ]; termsNew[termIndex] = term; terms = termsNew; } else { diff --git a/src/modules/highlight/container-blocks.mts b/src/modules/highlight/container-blocks.mts index a4f8a1f..46cfdcd 100644 --- a/src/modules/highlight/container-blocks.mts +++ b/src/modules/highlight/container-blocks.mts @@ -13,7 +13,7 @@ const containerBlockSelector = `:not(${Array.from(highlightTags.flow).join(", ") */ const getContainerBlock = (element: HTMLElement): HTMLElement => // Always returns an element since "body" is not a flow tag. - element.closest(containerBlockSelector) as HTMLElement + element.closest(containerBlockSelector)! ; export { containerBlockSelector, getContainerBlock }; diff --git a/src/modules/highlight/engines/element.mts b/src/modules/highlight/engines/element.mts index 656ab1f..72bfcb7 100644 --- a/src/modules/highlight/engines/element.mts +++ b/src/modules/highlight/engines/element.mts @@ -16,7 +16,7 @@ const canHighlightElement = (rejectSelector: string, element: Element): boolean !element.closest(rejectSelector) && element.tagName !== HIGHLIGHT_TAG_UPPER ; -type Properties = { [ELEMENT_JUST_HIGHLIGHTED]: boolean } +interface Properties { [ELEMENT_JUST_HIGHLIGHTED]: boolean } type PropertiesElement = BasePropertiesElement @@ -78,9 +78,9 @@ class FlowNodeList { let text = ""; let current = this.first; do { - text += (current as FlowNodeListItem).value.textContent; + text += current!.value.textContent; // eslint-disable-next-line no-cond-assign - } while (current = (current as FlowNodeListItem).next); + } while (current = current!.next); return text; } @@ -90,11 +90,11 @@ class FlowNodeList { } *[Symbol.iterator] () { - let current = this.first; + let current = this.first!; do { - yield current as FlowNodeListItem; + yield current; // eslint-disable-next-line no-cond-assign - } while (current = (current as FlowNodeListItem).next); + } while (current = current.next!); } } @@ -242,7 +242,7 @@ ${HIGHLIGHT_TAG} { const text = node.textContent ?? ""; if (text.length === 0) { node.remove(); - return (nodeItemPrevious ? nodeItemPrevious.next : nodeItems.first) as FlowNodeListItem; + return (nodeItemPrevious ? nodeItemPrevious.next : nodeItems.first)!; } const parent = node.parentElement as Element; (parent as PropertiesElement)[ELEMENT_JUST_HIGHLIGHTED] = true; @@ -281,7 +281,7 @@ ${HIGHLIGHT_TAG} { const text = textAfterNode.textContent ?? ""; if (text.length === 0) { textAfterNode.parentElement?.removeChild(textAfterNode); - return (nodeItemPrevious ? nodeItemPrevious.next : nodeItems.first) as FlowNodeListItem; + return (nodeItemPrevious ? nodeItemPrevious.next : nodeItems.first)!; } const parent = textAfterNode.parentNode as Node; const textEndNode = document.createTextNode(text.substring(start, end)); @@ -309,19 +309,18 @@ ${HIGHLIGHT_TAG} { const textFlow = nodeItems.getText(); for (const term of terms) { let nodeItemPrevious: FlowNodeListItem | null = null; - let nodeItem = nodeItems.first as FlowNodeListItem; + let nodeItem = nodeItems.first!; let textStart = 0; let textEnd = nodeItem.value.length; for (const match of textFlow.matchAll(this.termPatterns.get(term))) { - let highlightStart = match.index as number; + let highlightStart = match.index!; const highlightEnd = highlightStart + match[0].length; while (textEnd <= highlightStart) { nodeItemPrevious = nodeItem; - nodeItem = nodeItem.next as FlowNodeListItem; + nodeItem = nodeItem.next!; textStart = textEnd; textEnd += nodeItem.value.length; } - // eslint-disable-next-line no-constant-condition while (true) { // TODO join together nodes where possible // TODO investigate why, under some circumstances, new empty highlight elements keep being produced @@ -340,7 +339,7 @@ ${HIGHLIGHT_TAG} { break; } nodeItemPrevious = nodeItem; - nodeItem = nodeItem.next as FlowNodeListItem; + nodeItem = nodeItem.next!; textStart = textEnd; textEnd += nodeItem.value.length; } @@ -386,7 +385,7 @@ ${HIGHLIGHT_TAG} { nodeItems.push(node as Text); break; }} - node = node.nextSibling as ChildNode; // May be null (checked by loop condition) + node = node.nextSibling!; // May be null (checked by loop condition) } while (node && visitSiblings); }; @@ -410,7 +409,7 @@ ${HIGHLIGHT_TAG} { getMutationUpdatesObserver () { const rejectSelector = Array.from(highlightTags.reject).join(", "); - const elements: Set = new Set(); + const elements = new Set(); let periodDateLast = 0; let periodHighlightCount = 0; let throttling = false; @@ -444,10 +443,10 @@ ${HIGHLIGHT_TAG} { }; return new MutationObserver(mutations => { //mutationUpdates.disconnect(); - const elementsJustHighlighted: Set = new Set(); + const elementsJustHighlighted = new Set(); for (const mutation of mutations) { const element = mutation.target.nodeType === Node.TEXT_NODE - ? mutation.target.parentElement as HTMLElement + ? mutation.target.parentElement! : mutation.target as HTMLElement; if (element) { if ((element as PropertiesHTMLElement)[ELEMENT_JUST_HIGHLIGHTED]) { diff --git a/src/modules/highlight/engines/highlight.mts b/src/modules/highlight/engines/highlight.mts index ad10b85..73a9122 100644 --- a/src/modules/highlight/engines/highlight.mts +++ b/src/modules/highlight/engines/highlight.mts @@ -13,13 +13,13 @@ type Flow = BaseFlow type BoxInfo = BaseBoxInfo -type BoxInfoRange = { range: AbstractRange } +interface BoxInfoRange { range: AbstractRange } const getName = (termToken: string) => "markmysearch-" + termToken; class ExtendedHighlight { readonly highlight: Highlight; - readonly boxInfoRanges: Map = new Map(); + readonly boxInfoRanges = new Map(); constructor (...initialRanges: Array) { this.highlight = new Highlight(...initialRanges); @@ -61,8 +61,8 @@ class ExtendedHighlight { } class ExtendedHighlightRegistry { - readonly registry = CSS.highlights as HighlightRegistry; - readonly map: Map = new Map(); + readonly registry = CSS.highlights!; + readonly map = new Map(); get size () { return this.map.size; @@ -94,7 +94,7 @@ class ExtendedHighlightRegistry { } } -type HighlightStyle = { +interface HighlightStyle { opacity: number lineThickness: number lineStyle: "dotted" | "dashed" | "solid" | "double" | "wavy" @@ -113,7 +113,7 @@ class HighlightEngine implements AbstractTreeCacheEngine { readonly mutationUpdates: ReturnType; readonly highlights = new ExtendedHighlightRegistry(); - readonly highlightedElements: Set> = new Set(); + readonly highlightedElements = new Set>(); readonly terms = createContainer>([]); readonly hues = createContainer>([]); diff --git a/src/modules/highlight/engines/paint.mts b/src/modules/highlight/engines/paint.mts index b4a9ccc..d5a0257 100644 --- a/src/modules/highlight/engines/paint.mts +++ b/src/modules/highlight/engines/paint.mts @@ -23,13 +23,13 @@ type Flow = BaseFlow type BoxInfo = BaseBoxInfo -type BoxInfoBoxes = { boxes: Array } +interface BoxInfoBoxes { boxes: Array } type CachingElement = Cache.BaseCachingElement type CachingHTMLElement = Cache.BaseCachingHTMLElement -type Box = { +interface Box { token: string x: number y: number @@ -37,7 +37,7 @@ type Box = { height: number } -type StyleRuleInfo = { +interface StyleRuleInfo { rule: string element: CachingElement } @@ -55,7 +55,7 @@ class PaintEngine implements AbstractTreeCacheEngine { readonly mutationUpdates: ReturnType; - readonly elementsVisible: Set = new Set(); + readonly elementsVisible = new Set(); readonly shiftObserver: ResizeObserver; readonly visibilityObserver: IntersectionObserver; readonly styleUpdates: ReturnType; @@ -218,7 +218,7 @@ class PaintEngine implements AbstractTreeCacheEngine { } styleUpdate (styleRules: ReadonlyArray) { - const styleSheet = (document.getElementById(EleID.STYLE_PAINT) as HTMLStyleElement).sheet as CSSStyleSheet; + const styleSheet = (document.getElementById(EleID.STYLE_PAINT) as HTMLStyleElement).sheet!; for (const { rule, element } of styleRules) { if (element[CACHE].styleRuleIdx === undefined) { element[CACHE].styleRuleIdx = styleSheet.cssRules.length; diff --git a/src/modules/highlight/engines/paint/boxes.mts b/src/modules/highlight/engines/paint/boxes.mts index fd918b3..0fffaea 100644 --- a/src/modules/highlight/engines/paint/boxes.mts +++ b/src/modules/highlight/engines/paint/boxes.mts @@ -52,10 +52,10 @@ const elementPopulateBoxes = ( range.setEnd(boxInfo.node, boxInfo.end); const textRects = range.getClientRects(); for (let i = 0; i < textRects.length; i++) { - const textRect = textRects.item(i) as DOMRect; + const textRect = textRects.item(i)!; if (i !== 0 - && textRect.x === (textRects.item(i - 1) as DOMRect).x - && textRect.y === (textRects.item(i - 1) as DOMRect).y + && textRect.x === textRects.item(i - 1)!.x + && textRect.y === textRects.item(i - 1)!.y ) { continue; } diff --git a/src/modules/highlight/engines/paint/methods/paint.mts b/src/modules/highlight/engines/paint/methods/paint.mts index 7b0cd49..e56175c 100644 --- a/src/modules/highlight/engines/paint/methods/paint.mts +++ b/src/modules/highlight/engines/paint/methods/paint.mts @@ -28,7 +28,6 @@ class PaintMethod implements AbstractMethod { findHighlightableAncestor (element: CachingElement): CachingElement { let ancestor = element; - // eslint-disable-next-line no-constant-condition while (true) { // Anchors cannot (yet) be highlighted directly inside, due to security concerns with CSS Paint. const ancestorUnhighlightable = ancestor.closest("a"); diff --git a/src/modules/highlight/highlight-tags.mts b/src/modules/highlight/highlight-tags.mts index eae5ac3..77e8458 100644 --- a/src/modules/highlight/highlight-tags.mts +++ b/src/modules/highlight/highlight-tags.mts @@ -3,7 +3,7 @@ import type { HighlightTagName } from "/dist/modules/highlight/models/tree-edit/ const HIGHLIGHT_TAG: HighlightTagName = "mms-h"; -type HighlightTags = { +interface HighlightTags { reject: ReadonlySet, flow: ReadonlySet, } diff --git a/src/modules/highlight/matcher.mts b/src/modules/highlight/matcher.mts index 5b2807b..f81f6ee 100644 --- a/src/modules/highlight/matcher.mts +++ b/src/modules/highlight/matcher.mts @@ -1,17 +1,21 @@ import type { MatchTerm, TermPatterns } from "/dist/modules/match-term.mjs"; -type BaseFlow> = { +interface BaseFlow> { text: string boxesInfo: Array> } -type BaseBoxInfo> = { +interface MainBaseBoxInfo { term: MatchTerm start: number end: number -} & (WithNode extends true ? { node: Text } : Record) & Partial +} -type BoxInfoExtension = Record +type BaseBoxInfo> = ( + MainBaseBoxInfo + & (WithNode extends true ? { node: Text } : Record) + & Partial +) const matchInText = ( terms: ReadonlyArray, @@ -44,14 +48,13 @@ const matchInTextFlow = >( let textStart = 0; let textEnd = node.length; for (const match of text.matchAll(termPatterns.get(term))) { - const highlightStart = match.index as number; + const highlightStart = match.index!; const highlightEnd = highlightStart + match[0].length; while (textEnd <= highlightStart) { node = textFlow[++i]; textStart = textEnd; textEnd += node.length; } - // eslint-disable-next-line no-constant-condition while (true) { // Register as much of this highlight that fits into this node. boxesInfo.push({ diff --git a/src/modules/highlight/models/tree-cache/flow-monitors/flow-monitor.mts b/src/modules/highlight/models/tree-cache/flow-monitors/flow-monitor.mts index f85e5b0..9c071ed 100644 --- a/src/modules/highlight/models/tree-cache/flow-monitors/flow-monitor.mts +++ b/src/modules/highlight/models/tree-cache/flow-monitors/flow-monitor.mts @@ -10,11 +10,9 @@ import { type BaseFlow, type BaseBoxInfo, matchInTextFlow } from "/dist/modules/ import { MatchTerm, type TermPatterns } from "/dist/modules/match-term.mjs"; import { type RContainer } from "/dist/modules/common.mjs"; -type Flow = BaseFlow +type Flow = BaseFlow -type BoxInfoExtension = Record - -class FlowMonitor>> +class FlowMonitor>> implements AbstractFlowMonitor { readonly mutationObserver: MutationObserver; @@ -50,7 +48,7 @@ implements AbstractFlowMonitor { const rejectSelector = Array.from(highlightTags.reject).join(", "); return new MutationObserver(mutations => { // TODO optimise - const elementsAffected: Set = new Set(); + const elementsAffected = new Set(); //const elementsAdded: Set = new Set(); for (const mutation of mutations) { if (mutation.type === "characterData" @@ -177,7 +175,7 @@ implements AbstractFlowMonitor { return; } if (CACHE in ancestor) { - this.onBoxesInfoRemoved && this.onBoxesInfoRemoved(ancestor); + if (this.onBoxesInfoRemoved) this.onBoxesInfoRemoved(ancestor); delete (ancestor as UnknownCachingHTMLElement)[CACHE]; } const walker = document.createTreeWalker(ancestor, NodeFilter.SHOW_ELEMENT, element => @@ -187,7 +185,7 @@ implements AbstractFlowMonitor { // eslint-disable-next-line no-cond-assign while (element = walker.nextNode() as BCachingHTMLElement) { if (CACHE in element) { - this.onBoxesInfoRemoved && this.onBoxesInfoRemoved(element); + if (this.onBoxesInfoRemoved) this.onBoxesInfoRemoved(element); delete (element as UnknownCachingHTMLElement)[CACHE]; } } @@ -250,7 +248,7 @@ implements AbstractFlowMonitor { }; const ancestor = getAncestorCommon( textFlow[0].parentElement as BCachingHTMLElement, - textFlow.at(-1) as Text, + textFlow.at(-1)!, ) as BCachingHTMLElement; // This will be enforced in a moment by assigning the element's cache. if (ancestor === null) { console.warn("Unexpected condition: Common ancestor not found.", textFlow); @@ -263,9 +261,9 @@ implements AbstractFlowMonitor { }; ancestor[CACHE] ??= this.createElementCache(ancestor); ancestor[CACHE].flows.push(flow); - this.onBoxesInfoPopulated && this.onBoxesInfoPopulated(ancestor); + if (this.onBoxesInfoPopulated) this.onBoxesInfoPopulated(ancestor); if (flow.boxesInfo.length > 0) { - this.onNewHighlightedAncestor && this.onNewHighlightedAncestor(ancestor); + if (this.onNewHighlightedAncestor) this.onNewHighlightedAncestor(ancestor); } } } @@ -314,7 +312,7 @@ const populateTextFlows = (node: Node, textFlows: Array>, textFlow: } } } - node = node.nextSibling as ChildNode; // May be null (checked by loop condition). + node = node.nextSibling!; // May be null (checked by loop condition). } while (node); }; diff --git a/src/modules/highlight/models/tree-cache/term-counters/term-counter.mts b/src/modules/highlight/models/tree-cache/term-counters/term-counter.mts index 508cef4..74a44d6 100644 --- a/src/modules/highlight/models/tree-cache/term-counters/term-counter.mts +++ b/src/modules/highlight/models/tree-cache/term-counters/term-counter.mts @@ -7,7 +7,11 @@ import type { MatchTerm } from "/dist/modules/match-term.mjs"; type Flow = BaseFlow class TermCounter implements AbstractTermCounter { - countBetter (term: MatchTerm) { + countBetter (term: MatchTerm): number { + return this.countFaster(term); + } + + countFaster (term: MatchTerm): number { const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, element => highlightTags.reject.has((element as Element).tagName) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT ); @@ -26,9 +30,7 @@ class TermCounter implements AbstractTermCounter { return count; } - readonly countFaster = this.countBetter; - - exists (term: MatchTerm) { + exists (term: MatchTerm): boolean { const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, element => highlightTags.reject.has((element as Element).tagName) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT ); diff --git a/src/modules/highlight/models/tree-cache/term-markers/term-marker.mts b/src/modules/highlight/models/tree-cache/term-markers/term-marker.mts index 43176ed..5710cff 100644 --- a/src/modules/highlight/models/tree-cache/term-markers/term-marker.mts +++ b/src/modules/highlight/models/tree-cache/term-markers/term-marker.mts @@ -19,7 +19,7 @@ class TermMarker implements AbstractTermMarker { highlightedElements: Iterable>, ) { const termsSet = new Set(terms); - const gutter = document.getElementById(EleID.MARKER_GUTTER) as HTMLElement; + const gutter = document.getElementById(EleID.MARKER_GUTTER)!; let markersHtml = ""; for (const element of highlightedElements) if (CACHE in element) { const highlightedTerms = new Set(element[CACHE].flows.flatMap(flow => @@ -27,9 +27,11 @@ class TermMarker implements AbstractTermMarker { )); const yRelative = getElementYRelative(element); // TODO use single marker with custom style - markersHtml += Array.from(highlightedTerms).map((term, i) => `
`); + markersHtml += Array.from(highlightedTerms) + .map((term, i) => `
`) + .join(""); } gutter.replaceChildren(); // Removes children, since inner HTML replacement does not for some reason gutter.innerHTML = markersHtml; diff --git a/src/modules/highlight/models/tree-cache/term-walkers/term-walker.mts b/src/modules/highlight/models/tree-cache/term-walkers/term-walker.mts index e5809d4..f388b12 100644 --- a/src/modules/highlight/models/tree-cache/term-walkers/term-walker.mts +++ b/src/modules/highlight/models/tree-cache/term-walkers/term-walker.mts @@ -14,12 +14,10 @@ class TermWalker implements AbstractTermWalker { nodeStart?: Node, ): CachingHTMLElement | null { elementsPurgeClass(EleClass.FOCUS_CONTAINER); - const selection = document.getSelection() as Selection; - const bar = document.getElementById(EleID.BAR) as HTMLElement; const nodeBegin = reverse ? getNodeFinal(document.body) : document.body; - const nodeSelected = selection ? selection.anchorNode : null; + const nodeSelected = getSelection()?.anchorNode; const nodeFocused = document.activeElement - ? (document.activeElement === document.body || bar.contains(document.activeElement)) + ? (document.activeElement === document.body || document.activeElement.closest(`${EleID.BAR}`)) ? null : document.activeElement as HTMLElement : null; @@ -60,7 +58,7 @@ class TermWalker implements AbstractTermWalker { element.classList.add(EleClass.FOCUS_CONTAINER); } focusClosest(element, element => CACHE in element && element[CACHE].flows.length > 0); - selection.setBaseAndExtent(element, 0, element, 0); + getSelection()?.setBaseAndExtent(element, 0, element, 0); element.scrollIntoView({ behavior: stepNotJump ? "auto" : "smooth", block: "center" }); return element; } diff --git a/src/modules/highlight/models/tree-cache/tree-cache.mts b/src/modules/highlight/models/tree-cache/tree-cache.mts index 15da079..b5d8ffd 100644 --- a/src/modules/highlight/models/tree-cache/tree-cache.mts +++ b/src/modules/highlight/models/tree-cache/tree-cache.mts @@ -1,6 +1,6 @@ import type { BaseFlow } from "/dist/modules/highlight/matcher.mjs"; -type TreeCache> = { +interface TreeCache> { flows: Array } diff --git a/src/modules/highlight/models/tree-edit/term-counters/term-counter.mts b/src/modules/highlight/models/tree-edit/term-counters/term-counter.mts index 6e2d899..f352728 100644 --- a/src/modules/highlight/models/tree-edit/term-counters/term-counter.mts +++ b/src/modules/highlight/models/tree-edit/term-counters/term-counter.mts @@ -9,9 +9,11 @@ class TermCounter implements AbstractTermCounter { this.#termTokens = termTokens; } - readonly countBetter = this.countFaster; + countBetter (term: MatchTerm): number { + return this.countFaster(term); + } - countFaster (term: MatchTerm) { + countFaster (term: MatchTerm): number { // This is an unstable heuristic: the more highlight elements are split, the more it overpredicts. const occurrences = document.body.getElementsByClassName(getTermClass(term, this.#termTokens)); //const matches = occurrences.map(occurrence => occurrence.textContent).join("").match(term.pattern); @@ -19,7 +21,7 @@ class TermCounter implements AbstractTermCounter { return occurrences.length; } - exists (term: MatchTerm) { + exists (term: MatchTerm): boolean { return document.body.getElementsByClassName(getTermClass(term, this.#termTokens)).length > 0; } } diff --git a/src/modules/highlight/models/tree-edit/term-markers/term-marker.mts b/src/modules/highlight/models/tree-edit/term-markers/term-marker.mts index b0bfd82..984ba82 100644 --- a/src/modules/highlight/models/tree-edit/term-markers/term-marker.mts +++ b/src/modules/highlight/models/tree-edit/term-markers/term-marker.mts @@ -20,7 +20,7 @@ class TermMarker implements AbstractTermMarker { highlightedElements: Iterable, ) { const regexMatchTermSelector = new RegExp(`\\b${EleClass.TERM}(?:-\\w+)+\\b`); - const gutter = document.getElementById(EleID.MARKER_GUTTER) as HTMLElement; + const gutter = document.getElementById(EleID.MARKER_GUTTER)!; const containersInfo: Array<{ container: HTMLElement termsAdded: Set @@ -29,7 +29,7 @@ class TermMarker implements AbstractTermMarker { for (const highlight of highlightedElements) { const container = getContainerBlock(highlight); const containerIdx = containersInfo.findIndex(containerInfo => container.contains(containerInfo.container)); - const className = (highlight.className.match(regexMatchTermSelector) as RegExpMatchArray)[0]; + const className = highlight.className.match(regexMatchTermSelector)![0]; const yRelative = getElementYRelative(container); let markerCss = `top: ${yRelative * 100}%;`; if (containerIdx !== -1) { @@ -55,7 +55,7 @@ class TermMarker implements AbstractTermMarker { } raise (term: MatchTerm | null, container: HTMLElement) { - const scrollMarkerGutter = document.getElementById(EleID.MARKER_GUTTER) as HTMLElement; + const scrollMarkerGutter = document.getElementById(EleID.MARKER_GUTTER)!; elementsPurgeClass(EleClass.FOCUS, scrollMarkerGutter); [6, 5, 4, 3, 2].some(precisionFactor => { const precision = 10**precisionFactor; diff --git a/src/modules/highlight/models/tree-edit/term-walkers/term-walker.mts b/src/modules/highlight/models/tree-edit/term-walkers/term-walker.mts index da3cb45..341ef89 100644 --- a/src/modules/highlight/models/tree-edit/term-walkers/term-walker.mts +++ b/src/modules/highlight/models/tree-edit/term-walkers/term-walker.mts @@ -70,11 +70,11 @@ class TermWalker implements AbstractTermWalker { } } } - const container = getContainerBlock(elementTerm.parentElement as HTMLElement); + const container = getContainerBlock(elementTerm.parentElement!); container.classList.add(EleClass.FOCUS_CONTAINER); elementTerm.classList.add(EleClass.FOCUS); elementToSelect = Array.from(container.getElementsByTagName(HIGHLIGHT_TAG)) - .every(thisElement => getContainerBlock(thisElement.parentElement as HTMLElement) === container) + .every(thisElement => getContainerBlock(thisElement.parentElement!) === container) ? container : elementTerm; if (elementToSelect.tabIndex === -1) { @@ -109,8 +109,8 @@ class TermWalker implements AbstractTermWalker { .getElementsByClassName(EleClass.FOCUS_CONTAINER)[0] as HTMLElement; const selection = document.getSelection(); const activeElement = document.activeElement; - if (activeElement && activeElement.tagName === "INPUT" && activeElement.closest(`#${EleID.BAR}`)) { - (activeElement as HTMLInputElement).blur(); + if (activeElement instanceof HTMLInputElement && activeElement.closest(`#${EleID.BAR}`)) { + activeElement.blur(); } const selectionFocus = selection && (!activeElement || activeElement === document.body || !document.body.contains(activeElement) @@ -161,31 +161,35 @@ class TermWalker implements AbstractTermWalker { highlight: HTMLElement, node: Node, nextSiblingMethod: "nextSibling" | "previousSibling" - ) { - return node[nextSiblingMethod] - ? (node[nextSiblingMethod] as Node).nodeType === Node.ELEMENT_NODE - ? (node[nextSiblingMethod] as HTMLElement).tagName === HIGHLIGHT_TAG_UPPER - ? this.getSiblingHighlightFinal(node[nextSiblingMethod] as HTMLElement, node[nextSiblingMethod] as HTMLElement, - nextSiblingMethod) - : highlight - : (node[nextSiblingMethod] as Node).nodeType === Node.TEXT_NODE - ? (node[nextSiblingMethod] as Text).textContent === "" - ? this.getSiblingHighlightFinal(highlight, node[nextSiblingMethod] as Text, nextSiblingMethod) - : highlight - : highlight - : highlight; + ): HTMLElement { + const nextSibling = node[nextSiblingMethod]; + if (!nextSibling) { + return highlight; + } + if (nextSibling instanceof HTMLElement) { + if (nextSibling.tagName === HIGHLIGHT_TAG_UPPER) { + return this.getSiblingHighlightFinal(nextSibling, nextSibling, nextSiblingMethod); + } + return highlight; + } else if (nextSibling instanceof Text) { + if (nextSibling.textContent === "") { + return this.getSiblingHighlightFinal(highlight, nextSibling, nextSiblingMethod); + } + return highlight; + } + return highlight; } - getTopLevelHighlight (element: Element) { + getTopLevelHighlight (element: Element): Element { const closestHighlight = (element.parentElement as Element).closest(HIGHLIGHT_TAG); return closestHighlight ? this.getTopLevelHighlight(closestHighlight) : element; } stepToElement (element: HTMLElement) { - element = this.getTopLevelHighlight(element); + element = this.getTopLevelHighlight(element) as HTMLElement; const elementFirst = this.getSiblingHighlightFinal(element, element, "previousSibling"); const elementLast = this.getSiblingHighlightFinal(element, element, "nextSibling"); - (getSelection() as Selection).setBaseAndExtent(elementFirst, 0, elementLast, elementLast.childNodes.length); + getSelection()?.setBaseAndExtent(elementFirst, 0, elementLast, elementLast.childNodes.length); element.scrollIntoView({ block: "center" }); } @@ -222,7 +226,7 @@ class TermWalker implements AbstractTermWalker { document.body, NodeFilter.SHOW_ELEMENT, (element => - (element.parentElement as Element).closest(HIGHLIGHT_TAG) + element.parentElement!.closest(HIGHLIGHT_TAG) ? NodeFilter.FILTER_REJECT : (element.tagName === HIGHLIGHT_TAG_UPPER && isVisible(element)) ? NodeFilter.FILTER_ACCEPT diff --git a/src/modules/highlight/special-engines/paint.mts b/src/modules/highlight/special-engines/paint.mts index ba5f60c..74eac34 100644 --- a/src/modules/highlight/special-engines/paint.mts +++ b/src/modules/highlight/special-engines/paint.mts @@ -24,9 +24,9 @@ class PaintSpecialEngine implements AbstractSpecialEngine { hues: ReadonlyArray = []; readonly styleRules: StyleRulesInfo = { hovered: "", focused: "" }; - readonly elementsInfo: Map unknown, set: (value: unknown) => unknown }> - }> = new Map(); + }>(); constructor (termTokens: TermTokens, termPatterns: TermPatterns) { this.termTokens = termTokens; @@ -130,11 +130,10 @@ class PaintSpecialEngine implements AbstractSpecialEngine { highlight (highlightCtx: HighlightContext, terms: ReadonlyArray, hues: ReadonlyArray) { const element = document.querySelector(contextCSS[highlightCtx]); - const value = (element as HTMLInputElement | null)?.value; - if (value === undefined) { + if (!(element instanceof HTMLInputElement)) { return; } - this.styleUpdate({ [highlightCtx]: this.constructHighlightStyleRule(highlightCtx, terms, hues, value) }); + this.styleUpdate({ [highlightCtx]: this.constructHighlightStyleRule(highlightCtx, terms, hues, element.value) }); } unhighlight (highlightCtx: HighlightContext) { diff --git a/src/modules/highlight/term-css.mts b/src/modules/highlight/term-css.mts index ee85eeb..34689d9 100644 --- a/src/modules/highlight/term-css.mts +++ b/src/modules/highlight/term-css.mts @@ -12,9 +12,14 @@ const getDiagonalStyle = (colorA: string, colorB: string, cycle: number) => { const getHorizontalStyle = (colorA: string, colorB: string, cycle: number) => { const isAboveStyleLevel = (level: number) => cycle >= level; return isAboveStyleLevel(1) - ? `linear-gradient(${Array(Math.floor(cycle/2 + 1.5) * 2).fill("").map((v, i) => - (Math.floor(i / 2) % 2 == cycle % 2 ? colorB : colorA) + `${Math.floor((i + 1) / 2)/(Math.floor((cycle + 1) / 2) + 1) * 100}%` - )})` + ? `linear-gradient(${Array(Math.floor(cycle/2 + 1.5) * 2) + .fill("") + .map((v, i) => ( + (Math.floor(i / 2) % 2 == cycle % 2 ? colorB : colorA) + + (Math.floor((i + 1) / 2)/(Math.floor((cycle + 1) / 2) + 1) * 100) + "%" + )) + .join(", ") + })` : colorA; }; diff --git a/src/modules/interface/toolbar/control.mts b/src/modules/interface/toolbar/control.mts index 787863b..e65a78f 100644 --- a/src/modules/interface/toolbar/control.mts +++ b/src/modules/interface/toolbar/control.mts @@ -3,7 +3,7 @@ import { getControlClass } from "/dist/modules/interface/toolbar/common.mjs"; import { EleClass } from "/dist/modules/common.mjs"; import type { DoPhrasesMatchTerms, ControlsInfo } from "/dist/content.mjs"; -type ControlButtonInfo = { +interface ControlButtonInfo { controlClasses?: Array buttonClasses?: Array path?: string @@ -21,7 +21,7 @@ class Control { // TODO do not expose this; remove attribute readonly button: HTMLButtonElement; - readonly #name: string; + readonly #name: ControlButtonName; /** * Creates a control. diff --git a/src/modules/interface/toolbar/term-control/term-input.mts b/src/modules/interface/toolbar/term-control/term-input.mts index d476d97..9ebada1 100644 --- a/src/modules/interface/toolbar/term-control/term-input.mts +++ b/src/modules/interface/toolbar/term-control/term-input.mts @@ -188,9 +188,9 @@ class TermInput { } focus (): SelectionReturnTarget { - const selection = getSelection() as Selection; const activeElementOriginal = document.activeElement as HTMLElement; - const selectionRangesOriginal = Array(selection.rangeCount).fill(null).map((v, i) => selection.getRangeAt(i)); + const selection = getSelection(); + const selectionRangesOriginal = selection && Array(selection.rangeCount).fill(null).map((v, i) => selection.getRangeAt(i)); this.#input.focus(); return { element: activeElementOriginal, diff --git a/src/modules/interface/toolbar/term-control/term-option-list.mts b/src/modules/interface/toolbar/term-control/term-option-list.mts index fe2b298..2da9cb5 100644 --- a/src/modules/interface/toolbar/term-control/term-option-list.mts +++ b/src/modules/interface/toolbar/term-control/term-option-list.mts @@ -90,7 +90,7 @@ class TermOptionList { } } else if (event.key.length === 1 && /\w/.test(event.key)) { const toggledOption = options.some(option => { - if (option.title.toLowerCase()[0] === event.key.toLowerCase()) { + if (option.title.toLowerCase().startsWith(event.key.toLowerCase())) { option.toggle(); return true; } diff --git a/src/modules/interface/toolbar/term-controls/append.mts b/src/modules/interface/toolbar/term-controls/append.mts index a531233..53f0f07 100644 --- a/src/modules/interface/toolbar/term-controls/append.mts +++ b/src/modules/interface/toolbar/term-controls/append.mts @@ -30,7 +30,7 @@ class TermAppendControl implements TermAbstractControl { this.matchMode = Object.assign({}, controlsInfo.matchMode); let controlContainerTemp: HTMLElement | undefined = undefined; const setUpControl = (container: HTMLElement) => { - const pad = container.querySelector(`.${EleClass.CONTROL_PAD}`) as HTMLElement; + const pad = container.querySelector(`.${EleClass.CONTROL_PAD}`)! as HTMLElement; this.#input.appendTo(pad); const revealButton = this.#optionList.createFullTriggerButton(); pad.appendChild(revealButton); diff --git a/src/modules/match-term.mts b/src/modules/match-term.mts index 8097edd..9e1d775 100644 --- a/src/modules/match-term.mts +++ b/src/modules/match-term.mts @@ -90,7 +90,7 @@ const generatePattern = (term: MatchTerm): RegExp => { }; class TermTokens { - readonly #termTokens: WeakMap = new WeakMap(); + readonly #termTokens = new WeakMap(); readonly #tokenBaseCounts: Record = {}; get (term: MatchTerm): string { @@ -105,7 +105,7 @@ class TermTokens { } class TermPatterns { - readonly #termPatterns: WeakMap = new WeakMap(); + readonly #termPatterns = new WeakMap(); get (term: MatchTerm): RegExp { const pattern = this.#termPatterns.get(term); diff --git a/src/modules/messaging.mts b/src/modules/messaging.mts index e9a9a15..929d608 100644 --- a/src/modules/messaging.mts +++ b/src/modules/messaging.mts @@ -2,7 +2,7 @@ import type { CommandInfo } from "/dist/modules/commands.mjs"; import type { ConfigValues } from "/dist/modules/privileged/storage.mjs"; import type { MatchTerm } from "/dist/modules/match-term.mjs"; -type Tab = { +interface Tab { getDetails?: TabDetailsRequest commands?: ReadonlyArray extensionCommands?: ReadonlyArray @@ -19,12 +19,12 @@ type Tab = { matchMode?: ConfigValues["matchModeDefaults"] } -type TabDetailsRequest = { +interface TabDetailsRequest { termsFromSelection?: true highlightsShown?: true } -type TabResponse = { +interface TabResponse { terms?: ReadonlyArray highlightsShown?: boolean } @@ -40,9 +40,10 @@ type Background = { highlightsShownOn?: boolean barCollapsedOn?: boolean } -} & (WithId extends true - ? { tabId: number } - : { tabId?: number } +} & ( + (WithId extends true ? never : Record) | { + tabId: number + } ) type BackgroundResponse = Tab | null diff --git a/src/modules/messaging/tab.mts b/src/modules/messaging/tab.mts index 1ccd290..72f0c1b 100644 --- a/src/modules/messaging/tab.mts +++ b/src/modules/messaging/tab.mts @@ -5,7 +5,7 @@ import { log } from "/dist/modules/common.mjs"; const sendTabMessage = (tabId: number, message: Message.Tab): Promise => chrome.tabs.sendMessage(tabId, message).catch(reason => { log("messaging fail", "scripts may not be injected", { reason }); - }) + }) as Promise ; export { sendTabMessage }; diff --git a/src/modules/page/build.mts b/src/modules/page/build.mts index 8e8dafa..68639b1 100644 --- a/src/modules/page/build.mts +++ b/src/modules/page/build.mts @@ -6,7 +6,7 @@ import * as EmailJS from "/lib/email.min.js"; // eslint-disable-next-line @typescript-eslint/no-namespace namespace Page { - export type InteractionInfo = { + export interface InteractionInfo { className: string list?: { getLength: () => Promise @@ -66,19 +66,19 @@ namespace Page { } // eslint-disable-next-line @typescript-eslint/no-namespace export namespace Interaction { - export type ObjectRowInfo = { + export interface ObjectRowInfo { className: string key: string label?: InteractionInfo["label"] textbox?: InteractionInfo["textbox"] input?: InteractionInfo["input"] } - export type ObjectColumnInfo = { + export interface ObjectColumnInfo { className: string rows: Array } export type SubmitterLoad = (setEnabled: (enabled: boolean) => void) => void - export type SubmitterInfo = { + export interface SubmitterInfo { text: string id?: string onLoad?: SubmitterLoad @@ -110,14 +110,14 @@ namespace Page { containerIndex: number, store: boolean, ) => void - export type InputInfo = { + export interface InputInfo { autoId?: string getType?: InputFetch onLoad?: InputLoad onChange?: InputToggle } } - export type SectionInfo = { + export interface SectionInfo { className?: string title?: { text: string @@ -125,7 +125,7 @@ namespace Page { } interactions: Array } - export type PanelInfo = { + export interface PanelInfo { className: string name: { text: string @@ -138,7 +138,7 @@ namespace Page { | "textArray" | "textNumber" ) - export type AlertInfo = { + export interface AlertInfo { text: string timeout?: number } @@ -149,7 +149,7 @@ namespace Page { ) } -type FormField = { +interface FormField { question: string response: string } @@ -170,8 +170,8 @@ export const isWindowInFrame = () => ( export const sendProblemReport = async (userMessage = "", formFields: Array) => { const [ tab ] = await chrome.tabs.query({ active: true, lastFocusedWindow: true }); const bank = await Bank.get([ "researchInstances" ]); - const phrases = bank.researchInstances[tab.id as number] - ? bank.researchInstances[tab.id as number].terms.map((term: MatchTerm) => term.phrase).join(" ∣ ") + const phrases = bank.researchInstances[tab.id!] + ? bank.researchInstances[tab.id!].terms.map((term: MatchTerm) => term.phrase).join(" ∣ ") : ""; const message = { addon_version: Manifest.getVersion(), @@ -211,7 +211,7 @@ export const forInput = (input: HTMLInputElement, getText: (() => Promise (Object.entries({ " ": "Space", @@ -260,7 +260,7 @@ export const forInput = (input: HTMLInputElement, getText: (() => Promise { Object.keys(modifiers).forEach(mod => { - modifiers[mod] = event[`${mod}Key`]; + modifiers[mod] = event[`${mod}Key`] as boolean; }); }; const inputUpdate = () => { @@ -308,7 +308,7 @@ export const forInput = (input: HTMLInputElement, getText: (() => Promise { }; export const pageFocusScrollContainer = () => { - (document.querySelector(".container.panel") as HTMLElement).focus(); + (document.querySelector(".container.panel")! as HTMLElement).focus(); }; export const pageReload = () => { @@ -720,10 +720,10 @@ textarea // eslint-disable-next-line @typescript-eslint/no-unused-vars const focusActivePanel = () => { - const frame = document.querySelector("#frame") as HTMLElement; + const frame = document.querySelector("#frame")!; const className = getPanelClassName(Array.from(frame.classList)); - const inputFirst = document.querySelector(`.panel.${className} input`) as HTMLInputElement | null; - if (inputFirst) { + const inputFirst = document.querySelector(`.panel.${className} input`); + if (inputFirst instanceof HTMLInputElement) { inputFirst.focus(); if (inputFirst.type === "text") { inputFirst.select(); @@ -743,7 +743,7 @@ textarea if (tabNext) { return tabNext; } if (cycle) { - return (tab.parentElement as HTMLElement)[toRight ? "firstElementChild" : "lastElementChild"] as HTMLElement; + return tab.parentElement![toRight ? "firstElementChild" : "lastElementChild"] as HTMLElement; } else { return null; } @@ -752,14 +752,14 @@ textarea }; const handleTabs = () => { - const frame = document.getElementById("frame") as HTMLElement; + const frame = document.getElementById("frame")!; document.addEventListener("keydown", event => { if (event.ctrlKey || event.metaKey) { return; } const shiftTab = (toRight: boolean, cycle: boolean) => { const currentTab = document - .querySelector(`.container.tab .${getPanelClassName(Array.from(frame.classList))}`) as HTMLButtonElement; + .querySelector(`.container.tab .${getPanelClassName(Array.from(frame.classList))}`)! as HTMLButtonElement; shiftTabFromTab(currentTab, toRight, cycle); }; if (event.key === "PageDown") { @@ -874,7 +874,7 @@ textarea } label.classList.add("label"); const onChangeInternal = () => { - labelInfo.setText ? labelInfo.setText((label as HTMLInputElement).value, containerIndex) : undefined; + if (labelInfo.setText) labelInfo.setText((label as HTMLInputElement).value, containerIndex); }; if (labelInfo.setText) { const labelTextbox = label as HTMLInputElement; @@ -1042,10 +1042,10 @@ textarea } else { objectInfo.columns.forEach(columnInfo => insertColumn(columnInfo)); } - const inputMain = objectElement.querySelector("input") as HTMLInputElement; + const inputMain = objectElement.querySelector("input")!; let newElementQueued = false; inputMain.addEventListener("input", () => { - if (inputMain.value && ((container.lastElementChild as HTMLInputElement).querySelector("input") as HTMLInputElement).value && !newElementQueued) { + if (inputMain.value && ((container.lastElementChild as HTMLInputElement).querySelector("input")!).value && !newElementQueued) { newElementQueued = true; getArray().then(async array => { array.push(objectInfo.list.getNew(inputMain.value)); @@ -1138,7 +1138,7 @@ textarea } container.appendChild(button); let getMessageText = () => ""; - // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function + // eslint-disable-next-line @typescript-eslint/no-unused-vars let allowInputs = (allowed = true) => {}; button.addEventListener("click", () => { button.disabled = true; @@ -1279,12 +1279,12 @@ textarea insertNote(interaction, interactionInfo.note); insertInput(interaction, interactionInfo.input, inputId, () => index, 0); }; - const labelTextbox = interaction.querySelector("input") as HTMLInputElement; + const labelTextbox = interaction.querySelector("input")!; if (interactionInfo.list) { const listInfo = interactionInfo.list; const onChangeInternal = (commitIfEmpty = false) => { index = Array.from(container.children).indexOf(interaction); - if (labelTextbox.value && ((container.lastElementChild as HTMLElement).querySelector("input") as HTMLInputElement).value) { + if (labelTextbox.value && ((container.lastElementChild as HTMLElement).querySelector("input")!).value) { listInfo.pushWithName(labelTextbox.value).then(() => { insertBody(); insertInteraction(container, interactionInfo); @@ -1415,8 +1415,8 @@ textarea const insertAndManageContent = (panelsInfo: Array) => { document.body.appendChild(createFrameStructure()); - const panelContainer = document.querySelector(".container.panel") as HTMLElement; - const tabContainer = document.querySelector(".container.tab") as HTMLElement; + const panelContainer = document.querySelector(".container.panel")!; + const tabContainer = document.querySelector(".container.tab")!; panelsInfo.forEach(panelInfo => { const panel = document.createElement("div"); panel.classList.add("panel", panelInfo.className); @@ -1439,7 +1439,7 @@ textarea }); // TODO handle multiple tabs correctly // TODO visual indication of letter - const lettersTaken: Set = new Set(); + const lettersTaken = new Set(); const info: Array<{ letter: string, inputInfo?: Page.Interaction.InputInfo }> = panelsInfo.flatMap(panelInfo => panelInfo.sections.flatMap(sectionInfo => sectionInfo.interactions .map(interactionInfo => { @@ -1520,7 +1520,7 @@ body { const hash = (new URL(location.href)).hash; const tabButton = hash.length ? document.getElementById(hash.slice(1)) : getTabs()[0]; if (tabButton) { - const frame = document.getElementById("frame") as HTMLElement; + const frame = document.getElementById("frame")!; frame.classList.forEach(className => { if (classNameIsPanel(className)) { frame.classList.remove(className); diff --git a/src/modules/privileged/storage.mts b/src/modules/privileged/storage.mts index 08fc622..066f037 100644 --- a/src/modules/privileged/storage.mts +++ b/src/modules/privileged/storage.mts @@ -17,7 +17,7 @@ enum ConfigContext { type ResearchInstances = Record type SearchSites = Record -type BankValues = { +interface BankValues { researchInstances: ResearchInstances engines: SearchSites } @@ -90,7 +90,7 @@ export class StoreListInterface { } } -type ConfigBarControlsShown = { +interface ConfigBarControlsShown { toggleBarCollapsed: StoreImmediate disableTabResearch: StoreImmediate performSearch: StoreImmediate @@ -98,7 +98,7 @@ type ConfigBarControlsShown = { appendTerm: StoreImmediate replaceTerms: StoreImmediate } -type ConfigBarLook = { +interface ConfigBarLook { showEditIcon: StoreImmediate showRevealIcon: StoreImmediate fontSize: StoreImmediate @@ -106,20 +106,20 @@ type ConfigBarLook = { opacityTerm: StoreImmediate borderRadius: StoreImmediate } -type ConfigHighlightLook = { +interface ConfigHighlightLook { hues: StoreImmediate, Context> } -type ConfigHighlighter = { +interface ConfigHighlighter { engine: StoreImmediate paintEngine: StoreImmediate } -type ConfigURLFilters = { +interface ConfigURLFilters { noPageModify: StoreImmediate noHighlight: StoreImmediate nonSearch: StoreImmediate } -type ConfigValues = { +interface ConfigValues { theme: { edition: StoreImmediate variant: StoreImmediate @@ -168,7 +168,7 @@ type URLFilter = Array<{ pathname: string, }> -type TermList = { +interface TermList { name: string terms: ReadonlyArray urlFilter: URLFilter @@ -479,6 +479,7 @@ export abstract class Bank { chrome.storage.session.onChanged.addListener(changes => { // TODO check that the change was not initiated from the same script? for (const [ key, value ] of (Object.entries as Entries)(changes as Record)) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment bankCache[key] = value.newValue; } }); @@ -510,6 +511,7 @@ export abstract class Config { const storageAreaValues: Record> = { local: {}, sync: {} }; for (const [ configKey, group ] of Object.entries(config)) { for (const [ groupKey, value ] of Object.entries(group)) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access const valueSchema: Store = configSchema[configKey][groupKey]; const storageValues = valueSchema.sync ? storageAreaValues.sync : storageAreaValues.local; const key = configKey + "." + groupKey; @@ -542,6 +544,7 @@ export abstract class Config { for (const [ configKey, groupInfo ] of keyObjectEntries) { const groupKeys = typeof groupInfo === "object" ? groupInfo : Object.keys(configSchema[configKey]); for (const groupKey of groupKeys) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const valueSchema: Store = configSchema[configKey][groupKey]; const storageKeys = valueSchema.sync ? storageAreaKeys.sync : storageAreaKeys.local; storageKeys.push(configKey + "." + groupKey); @@ -563,6 +566,7 @@ export abstract class Config { for (const [ configKey, groupInfo ] of keyObjectEntries) { const groupKeys = typeof groupInfo === "object" ? groupInfo : Object.keys(configSchema[configKey]); for (const groupKey of groupKeys) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const valueSchema: Store = configSchema[configKey][groupKey]; storageAreaKeys[valueSchema.sync ? "sync" : "local"].push(configKey + "." + groupKey); } @@ -583,6 +587,7 @@ export abstract class Config { pendingCount++; (config[configKey] as unknown) ??= {} as StoreGroup; const key = configKey + "." + groupKey; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const valueSchema: Store = configSchema[configKey][groupKey]; const value = (await (valueSchema.sync ? storageAreaPromises.sync : storageAreaPromises.local))[key]; switch (valueSchema.type) { @@ -625,6 +630,7 @@ export abstract class Config { (config[configKey] as unknown) = {} as StoreGroup; const groupKeys = typeof groupInfo === "object" ? groupInfo : Object.keys(configSchema[configKey]); for (const groupKey of groupKeys) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const valueSchema: Store = configSchema[configKey][groupKey]; switch (valueSchema.type) { case StoreType.IMMEDIATE: { @@ -646,6 +652,7 @@ export abstract class Config { (configTypes[configKey] as unknown) = {} as StoreGroup; const groupKeys = typeof groupInfo === "object" ? groupInfo : Object.keys(configSchema[configKey]); for (const groupKey of groupKeys) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const valueSchema: Store = configSchema[configKey][groupKey]; configTypes[configKey][groupKey] = valueSchema.type; } @@ -703,6 +710,7 @@ const migrations: Record { const storageArea: chrome.storage.StorageArea = chrome.storage[areaName]; const version1Key = KEYS.reservedFor[areaName].schemaVersion1; const storageSpecial = await storageArea.get([ "schemaVersion", KEYS.special.schemaVersion ]); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const versionValue = storageSpecial[KEYS.special.schemaVersion] ?? (storageSpecial[version1Key] ? 1 : undefined); const schemaVersion = (typeof versionValue === "number") ? versionValue : 0; if (schemaVersion === SCHEMA_VERSION) { diff --git a/src/pages/options-new.mts b/src/pages/options-new.mts index a9d2858..13e082e 100644 --- a/src/pages/options-new.mts +++ b/src/pages/options-new.mts @@ -30,7 +30,7 @@ const getControlOptionTemp = ( .find(commandOther => commandOther.name === details?.command?.name)?.shortcut?.split("+") ?? [] ).join("+") : undefined, forInput: details?.command?.name && (chrome.commands["update"] || (globalThis["browser"] && browser.commands["update"])) - ? (input, getText, setFloatingText) => forInput(input, getText, setFloatingText, details?.command?.name as string) + ? (input, getText, setFloatingText) => forInput(input, getText, setFloatingText, details!.command!.name!) : undefined, }, input: { diff --git a/src/pages/options.mts b/src/pages/options.mts index 4c5cae3..8cfe395 100644 --- a/src/pages/options.mts +++ b/src/pages/options.mts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ //import { isWindowInFrame } from "/dist/modules/page/build.mjs"; import type { StoreImmediate, StoreList, ConfigValues, ConfigKey } from "/dist/modules/privileged/storage.mjs"; import { StoreType, StoreListInterface, Config } from "/dist/modules/privileged/storage.mjs"; @@ -19,7 +20,7 @@ type OptionsInfo = Array<{ }}> }> -type Preference = { +interface Preference { label: string tooltip?: string type: PreferenceType @@ -62,7 +63,7 @@ enum PreferenceType { ARRAY_NUMBER, } -type OptionsConfig = { +interface OptionsConfig { height?: number width?: number } @@ -497,7 +498,7 @@ label[for]:hover (value) => { valuesCurrent[optionKey][preferenceKey] = value; }, () => valuesCurrent[optionKey][preferenceKey], Config.getDefault({ [optionKey]: [ preferenceKey ] })[optionKey][preferenceKey], - Config.getType({ [optionKey]: [ preferenceKey ] })[optionKey][preferenceKey], + Config.getType({ [optionKey]: [ preferenceKey ] })[optionKey][preferenceKey] as StoreType, () => config[optionKey][preferenceKey], ); } diff --git a/src/paint.ts b/src/paint.ts index ebc029a..b9a4155 100644 --- a/src/paint.ts +++ b/src/paint.ts @@ -14,8 +14,8 @@ registerPaint("markmysearch-highlights", class { geom: PaintSize, properties: StylePropertyMapReadOnly, ) { - const selectorStyles = JSON.parse(properties.get("--markmysearch-styles")?.toString() || "{}") as TermSelectorStyles; - const boxes = JSON.parse(properties.get("--markmysearch-boxes")?.toString() || "[]") as Array; + const selectorStyles = JSON.parse(properties.get("--markmysearch-styles")?.toString() ?? "{}") as TermSelectorStyles; + const boxes = JSON.parse(properties.get("--markmysearch-boxes")?.toString() ?? "[]") as Array; for (const box of boxes) { const style = selectorStyles[box.token]; if (!style) { diff --git a/src/types/custom-highlight.d.ts b/src/types/custom-highlight.d.ts index ba85201..eee2592 100644 --- a/src/types/custom-highlight.d.ts +++ b/src/types/custom-highlight.d.ts @@ -5,12 +5,11 @@ type HighlightType = ( ) declare class Highlight extends Set { - constructor(...initialRanges: AbstractRange[]); + constructor (...initialRanges: Array) priority: number; type: HighlightType; } -// eslint-disable-next-line @typescript-eslint/no-namespace declare namespace CSS { const highlights: HighlightRegistry | undefined; } diff --git a/src/types/worklets/paint-worklet.d.ts b/src/types/worklets/paint-worklet.d.ts index 6192075..4cb1761 100644 --- a/src/types/worklets/paint-worklet.d.ts +++ b/src/types/worklets/paint-worklet.d.ts @@ -1,12 +1,10 @@ -interface WorkletGlobalScope { } +//interface WorkletGlobalScope {} -interface PaintInstanceConstructor { - new(): { - paint(ctx: PaintRenderingContext2D, geom: PaintSize, properties: StylePropertyMapReadOnly): void; - }; +type PaintInstanceConstructor = new () => { + paint (ctx: PaintRenderingContext2D, geom: PaintSize, properties: StylePropertyMapReadOnly): void } -declare class PaintWorkletGlobalScope implements WorkletGlobalScope { +declare class PaintWorkletGlobalScope /*implements WorkletGlobalScope*/ { registerPaint(name: string, paintCtor: PaintInstanceConstructor): void readonly devicePixelRatio: number; } @@ -17,29 +15,18 @@ interface PaintRenderingContext2DSettings { alpha?: boolean; } -declare interface PaintRenderingContext2D { } - -interface PaintRenderingContext2D extends CanvasState { } - -interface PaintRenderingContext2D extends CanvasTransform { } - -interface PaintRenderingContext2D extends CanvasCompositing { } - -interface PaintRenderingContext2D extends CanvasImageSmoothing { } - -interface PaintRenderingContext2D extends CanvasFillStrokeStyles { } - -interface PaintRenderingContext2D extends CanvasShadowStyles { } - -interface PaintRenderingContext2D extends CanvasRect { } - -interface PaintRenderingContext2D extends CanvasDrawPath { } - -interface PaintRenderingContext2D extends CanvasDrawImage { } - -interface PaintRenderingContext2D extends CanvasPathDrawingStyles { } - -interface PaintRenderingContext2D extends CanvasPath { } +declare interface PaintRenderingContext2D extends +CanvasState, +CanvasTransform, +CanvasCompositing, +CanvasImageSmoothing, +CanvasFillStrokeStyles, +CanvasShadowStyles, +CanvasRect, +CanvasDrawPath, +CanvasDrawImage, +CanvasPathDrawingStyles, +CanvasPath {} declare class PaintSize { readonly width: number; diff --git a/src/types/worklets/paint.d.ts b/src/types/worklets/paint.d.ts index 76752df..f3fc6e8 100644 --- a/src/types/worklets/paint.d.ts +++ b/src/types/worklets/paint.d.ts @@ -1,4 +1,3 @@ -// eslint-disable-next-line @typescript-eslint/no-namespace declare namespace CSS { const paintWorklet: Worklet | undefined; }