diff --git a/__mocks__/obsidian.ts b/__mocks__/obsidian.ts index 0734b25..4a3b6c4 100644 --- a/__mocks__/obsidian.ts +++ b/__mocks__/obsidian.ts @@ -1,5 +1,9 @@ import { StateField } from '@codemirror/state'; +if (typeof window !== 'undefined' && !('require' in window)) { + (window as unknown as { require: unknown }).require = require; +} + const toForwardSlashes = (value: string): string => value.replace(/\\/g, '/'); const joinPath = (...parts: string[]): string => { const normalizedParts = parts.map(toForwardSlashes).filter(part => part !== '' && part !== '.'); diff --git a/src/core/equations/live-preview-equations.ts b/src/core/equations/live-preview-equations.ts index 4ceef93..3ac220b 100644 --- a/src/core/equations/live-preview-equations.ts +++ b/src/core/equations/live-preview-equations.ts @@ -184,7 +184,7 @@ function createTagManagerPlugin( ): ViewPlugin { return ViewPlugin.fromClass( class { - timeout: any = null; + timeout: number | null = null; blockedBySelection = false; constructor(view: EditorView) { diff --git a/src/core/equations/provider-equation.ts b/src/core/equations/provider-equation.ts index 27cd448..623da73 100644 --- a/src/core/equations/provider-equation.ts +++ b/src/core/equations/provider-equation.ts @@ -1,4 +1,4 @@ -import { TFile, App, CachedMetadata } from 'obsidian'; +import { TFile, App, CachedMetadata, Pos } from 'obsidian'; import { EquationBlock } from 'types'; import { trimMathText, parseMarkdownComment, parseYamlLike } from 'utils/parse'; @@ -53,7 +53,7 @@ export class ActiveNoteEquationProvider { $file: file.path, $type: 'equation' as const, $blockId: blockId, - $pos: position as any, + $pos: position as unknown as Pos, $position: { start: position.start.line, end: position.end.line }, $mathText: trimmedMathText, $manualTag: manualTag, diff --git a/src/core/equations/reading-view-equations.ts b/src/core/equations/reading-view-equations.ts index e4ada49..ce8d141 100644 --- a/src/core/equations/reading-view-equations.ts +++ b/src/core/equations/reading-view-equations.ts @@ -14,7 +14,7 @@ export const createEquationNumberProcessor = (plugin: LatexReferencer): Markdown // In reading view, we need to read the file content directly. // We cannot rely on an active editor being open. - plugin.app.vault.cachedRead(file).then(content => { + void plugin.app.vault.cachedRead(file).then(content => { const equations = processActiveNoteEquations(plugin, file, content); if (equations.size === 0) return; diff --git a/src/core/linker/dom-observer.ts b/src/core/linker/dom-observer.ts index 6d30d28..4cb081d 100644 --- a/src/core/linker/dom-observer.ts +++ b/src/core/linker/dom-observer.ts @@ -75,7 +75,7 @@ function processEquationLinksInElement(node: HTMLElement, plugin: LatexReference * (e.g., on initial page load). */ function scanExistingCallouts(plugin: LatexReferencer): void { - const calloutBlocks = document.querySelectorAll('.cm-embed-block.cm-callout'); + const calloutBlocks = activeDocument.querySelectorAll('.cm-embed-block.cm-callout'); for (const block of calloutBlocks) { if (block.instanceOf(HTMLElement)) { processEquationLinksInElement(block, plugin); @@ -84,7 +84,7 @@ function scanExistingCallouts(plugin: LatexReferencer): void { } /** - * Sets up a MutationObserver on document.body to detect callout rendering + * Sets up a MutationObserver on activeDocument.body to detect callout rendering * and reprocess equation links within them. Also performs an initial scan * after a delay to catch callouts rendered before the cache was ready. * @@ -97,7 +97,6 @@ export function setupDOMObserver(plugin: LatexReferencer): () => void { if (mutation.type === 'childList') { for (const node of mutation.addedNodes) { if (node.instanceOf(HTMLElement)) { - if (!node.querySelector && !node.matches) continue; processEquationLinksInElement(node, plugin); } } @@ -105,7 +104,7 @@ export function setupDOMObserver(plugin: LatexReferencer): () => void { } }); - observer.observe(document.body, { childList: true, subtree: true }); + observer.observe(activeDocument.body, { childList: true, subtree: true }); // Initial scan: process callouts that were rendered before the observer // started or before the equation cache was ready. We use onLayoutReady diff --git a/src/core/linker/helper.ts b/src/core/linker/helper.ts index e799487..a48c488 100644 --- a/src/core/linker/helper.ts +++ b/src/core/linker/helper.ts @@ -18,7 +18,7 @@ export function getMathLink( const subpathResult = cache ? resolveSubpath(cache, subpath) : null; for (const provider of plugin.internalProviders) { - const provided = provider.provide({ path, subpath }, targetFile, subpathResult as any); + const provided = provider.provide({ path, subpath }, targetFile, subpathResult as unknown); if (provided) { return provided; } diff --git a/src/core/linker/live-preview-link-renderer.ts b/src/core/linker/live-preview-link-renderer.ts index 93463e4..4ce807f 100644 --- a/src/core/linker/live-preview-link-renderer.ts +++ b/src/core/linker/live-preview-link-renderer.ts @@ -88,16 +88,20 @@ export const createLivePreviewLinkRendererPlugin = (plugin: LatexReferencer): Ex mathLinkWrapper.onclick = (evt: MouseEvent) => { evt.preventDefault(); - app.workspace.openLinkText(this.outLinkText, this.sourcePath, evt.ctrlKey || evt.metaKey); + void app.workspace.openLinkText( + this.outLinkText, + this.sourcePath, + evt.ctrlKey || evt.metaKey + ); }; mathLinkWrapper.onmousedown = (evt: MouseEvent) => { - if (evt.button == 1) evt.preventDefault(); + if (evt.button === 1) evt.preventDefault(); }; mathLinkWrapper.onauxclick = (evt: MouseEvent) => { - if (evt.button == 1) { - app.workspace.openLinkText(this.outLinkText, this.sourcePath, true); + if (evt.button === 1) { + void app.workspace.openLinkText(this.outLinkText, this.sourcePath, true); } }; @@ -182,7 +186,7 @@ export const createLivePreviewLinkRendererPlugin = (plugin: LatexReferencer): Ex this.decorations = builder.finish(); if (this.decorations.size > 0) { - finishRenderMath(); + void finishRenderMath(); } } }, diff --git a/src/core/linker/reading-view-linker.ts b/src/core/linker/reading-view-linker.ts index c0b1261..f456914 100644 --- a/src/core/linker/reading-view-linker.ts +++ b/src/core/linker/reading-view-linker.ts @@ -61,7 +61,7 @@ export class LatexRenderChild extends MarkdownRenderChild { const linkEl = this.containerEl; setMathLink(mathLink, linkEl); } - finishRenderMath(); + void finishRenderMath(); } } diff --git a/src/declarations.d.ts b/src/declarations.d.ts index 0d46872..85f1a96 100644 --- a/src/declarations.d.ts +++ b/src/declarations.d.ts @@ -1,4 +1,4 @@ -import { PaneType, SplitDirection, UserEvent } from 'obsidian'; +import { PaneType, SplitDirection, UserEvent, Scope, View, MarkdownFileInfo } from 'obsidian'; import { EditorView } from '@codemirror/view'; declare module 'obsidian' { @@ -12,16 +12,26 @@ declare module 'obsidian' { setSelectedItem(index: number, event: UserEvent | null): void; } + interface Workspace { + getActiveFileView(): View | null; + activeEditor?: MarkdownFileInfo | null; + } + interface App { + workspace: Workspace; + commands: { + commands: Record; + removeCommand(id: string): void; + }; plugins: { enabledPlugins: Set; plugins: { - [id: string]: any; + [id: string]: unknown; }; getPlugin: (id: string) => Plugin | null; }; internalPlugins: { - getPluginById(id: string): Plugin & { instance: any }; + getPluginById(id: string): Plugin & { instance: unknown }; }; } interface Editor { @@ -39,6 +49,12 @@ declare module 'obsidian' { // 3. USE the complete interface here as well chooser: Suggestions; } + interface Vault { + getConfig(key: string): unknown; + } + namespace MarkdownRenderer { + function postProcess(app: App, context: unknown): Promise; + } } export type LeafArgs = diff --git a/src/features/custom-notes/manager.ts b/src/features/custom-notes/manager.ts index 4c38a60..ec73876 100644 --- a/src/features/custom-notes/manager.ts +++ b/src/features/custom-notes/manager.ts @@ -1,5 +1,6 @@ -import { Notice, TFile } from 'obsidian'; +import { Command, TFile } from 'obsidian'; import type LatexReferencer from '../../main'; +import { showNotice } from 'utils/obsidian'; export class CustomNoteManager { private registeredCommandIds: string[] = []; @@ -12,7 +13,7 @@ export class CustomNoteManager { registerCommands() { // 1. Unregister all previously registered commands - const appCommands = (this.plugin.app as any).commands; + const appCommands = this.plugin.app.commands; if (appCommands && typeof appCommands.removeCommand === 'function') { for (const cmdId of this.registeredCommandIds) { try { @@ -34,11 +35,11 @@ export class CustomNoteManager { const commandId = `open-custom-note-${item.id}`; const fullCommandId = `${this.plugin.manifest.id}:${commandId}`; - const commandConfig: any = { + const commandConfig: Command = { id: commandId, name: `Open custom note: ${displayName}`, callback: () => { - this.openCustomNote(item.notePath); + void this.openCustomNote(item.notePath); } }; @@ -66,7 +67,7 @@ export class CustomNoteManager { const leaf = this.plugin.app.workspace.getLeaf('tab'); await leaf.openFile(file); } else { - new Notice( + showNotice( `Custom note not found at path: ${notePath}. Please verify your configuration in settings.` ); } diff --git a/src/features/export-pdf/pdf.ts b/src/features/export-pdf/pdf.ts index a43eca5..00c48f0 100644 --- a/src/features/export-pdf/pdf.ts +++ b/src/features/export-pdf/pdf.ts @@ -1,6 +1,36 @@ -import * as electron from 'electron'; -import * as fs from 'fs/promises'; -import { type FrontMatterCache, Notice } from 'obsidian'; +import { type FrontMatterCache } from 'obsidian'; +import { showNotice } from 'utils/obsidian'; + +interface ElectronModule { + remote: { + shell: { + openPath(path: string): Promise; + }; + dialog: { + showSaveDialog( + options: Record + ): Promise<{ canceled: boolean; filePath?: string }>; + showOpenDialog( + options: Record + ): Promise<{ canceled: boolean; filePaths: string[] }>; + }; + }; +} + +interface FsModule { + writeFile(path: string, data: Uint8Array): Promise; +} + +const getRequire = (): ((id: string) => unknown) | null => { + if (typeof window !== 'undefined' && 'require' in window) { + return (window as unknown as { require: (id: string) => unknown }).require; + } + return null; +}; + +const req = getRequire(); +const fs = req ? (req('fs/promises') as FsModule) : null; +const electron = req ? (req('electron') as ElectronModule) : null; import { PDFArray, PDFDict, @@ -337,11 +367,11 @@ export async function editPDF( const pdfDoc = await PDFDocument.load(data); const posistions = await getDestPosition(pdfDoc); - setAnchors(pdfDoc, posistions); + await setAnchors(pdfDoc, posistions); const outlines = generateOutlines(headings, posistions, maxLevel); - setOutline(pdfDoc, outlines); + await setOutline(pdfDoc, outlines); if (displayMetadata) { setMetadata(pdfDoc, frontMatter ?? {}); } @@ -354,37 +384,44 @@ export function setMetadata( pdfDoc: PDFDocument, { title, author, keywords, subject, creator, created_at, updated_at }: FrontMatterCache ) { - if (title) { + if (title && typeof title === 'string') { pdfDoc.setTitle(title, { showInWindowTitleBar: true }); } if (author) { if (Array.isArray(author)) { - pdfDoc.setAuthor(author.join(', ')); + pdfDoc.setAuthor(author.map(a => String(a)).join(', ')); } else { - pdfDoc.setAuthor(author.toString()); + pdfDoc.setAuthor(String(author)); } } if (keywords) { - pdfDoc.setKeywords(typeof keywords == 'string' ? [keywords] : keywords); + const kwList = Array.isArray(keywords) + ? keywords.map(k => String(k)) + : typeof keywords === 'string' + ? [keywords] + : [String(keywords)]; + pdfDoc.setKeywords(kwList); } - if (subject) { + if (subject && typeof subject === 'string') { pdfDoc.setSubject(subject); } - pdfDoc.setCreator(creator ?? 'Obsidian'); + const creatorStr = typeof creator === 'string' ? creator : 'Obsidian'; + pdfDoc.setCreator(creatorStr); pdfDoc.setProducer('Obsidian'); - pdfDoc.setCreationDate(new Date(created_at ?? new Date())); - pdfDoc.setModificationDate(new Date(updated_at ?? new Date())); + const creationDate = created_at ? new Date(created_at as string | number | Date) : new Date(); + pdfDoc.setCreationDate(creationDate); + const modDate = updated_at ? new Date(updated_at as string | number | Date) : new Date(); + pdfDoc.setModificationDate(modDate); } export async function exportToPDF( outputFile: string, config: TConfig & PluginSettings, // Changed from BetterExportPdfPluginSettings - w: any, + w: { printToPDF(options: Record): Promise }, { doc, frontMatter }: DocType ) { - console.log('output pdf:', outputFile); let pageSize = config['pageSize'] as PageSizeType; - if (config['pageSize'] == 'Custom' && config['pageWidth'] && config['pageHeight']) { + if (config['pageSize'] === 'Custom' && config['pageWidth'] && config['pageHeight']) { pageSize = { width: safeParseFloat(config['pageWidth'], 210) / 25.4, height: safeParseFloat(config['pageHeight'], 297) / 25.4 @@ -395,7 +432,7 @@ export async function exportToPDF( if (scale > 200 || scale < 10) { scale = 100; } - const printOptions: any = { + const printOptions: Record = { landscape: config?.['landscape'], printBackground: config?.['printBackground'], generateTaggedPDF: config?.['generateTaggedPDF'], @@ -413,7 +450,7 @@ export async function exportToPDF( : '' }; - if (config.marginType == '0') { + if (config.marginType === '0') { printOptions['margins'] = { marginType: 'custom', top: 0, @@ -421,11 +458,11 @@ export async function exportToPDF( left: 0, right: 0 }; - } else if (config.marginType == '1') { + } else if (config.marginType === '1') { printOptions['margins'] = { marginType: 'default' }; - } else if (config.marginType == '2') { + } else if (config.marginType === '2') { printOptions['margins'] = { marginType: 'custom', top: 0.1, @@ -433,7 +470,7 @@ export async function exportToPDF( left: 0.1, right: 0.1 }; - } else if (config.marginType == '3') { + } else if (config.marginType === '3') { // Custom Margin printOptions['margins'] = { marginType: 'custom', @@ -455,21 +492,22 @@ export async function exportToPDF( maxLevel: safeParseInt(config?.maxLevel, 6) }); - await fs.writeFile(outputFile, data); + if (fs) { + await fs.writeFile(outputFile, data); + } - if (config.open) { - // @ts-ignore - electron.remote.shell.openPath(outputFile); + if (config.open && electron) { + void electron.remote.shell.openPath(outputFile); } } catch (error) { console.error(error); - new Notice(`Export to PDF failed: ${error}`); + showNotice(`Export to PDF failed: ${String(error)}`); } } export async function getOutputFile(filename: string, isTimestamp?: boolean) { - // @ts-ignore - const result: Electron.SaveDialogReturnValue = await electron.remote.dialog.showSaveDialog({ + if (!electron) return; + const result = await electron.remote.dialog.showSaveDialog({ title: 'Export to PDF', defaultPath: `${filename + (isTimestamp ? `-${Date.now()}` : '')}.pdf`, filters: [ @@ -477,7 +515,7 @@ export async function getOutputFile(filename: string, isTimestamp?: boolean) { { name: 'PDF', extensions: ['pdf'] } ], properties: ['showOverwriteConfirmation', 'createDirectory'] - } as any); + }); if (result.canceled) { return; @@ -486,8 +524,8 @@ export async function getOutputFile(filename: string, isTimestamp?: boolean) { } export async function getOutputPath(filename: string, isTimestamp?: boolean) { - // @ts-ignore - const result: Electron.OpenDialogReturnValue = await electron.remote.dialog.showOpenDialog({ + if (!electron) return; + const result = await electron.remote.dialog.showOpenDialog({ title: 'Export to PDF', defaultPath: filename, properties: ['openDirectory'] diff --git a/src/features/export-pdf/render.ts b/src/features/export-pdf/render.ts index 2989eef..bf2223a 100644 --- a/src/features/export-pdf/render.ts +++ b/src/features/export-pdf/render.ts @@ -1,29 +1,28 @@ -import { - App, - Component, - type FrontMatterCache, - MarkdownRenderer, - MarkdownView, - Notice, - TFile -} from 'obsidian'; +import { App, Component, MarkdownRenderer, MarkdownView, TFile } from 'obsidian'; import type { TConfig } from '../../ui/export-pdf/modal'; import { copyAttributes, fixAnchors, modifyDest } from './utils'; import { checkAndFixCalloutMath } from '../../utils/fixer'; +import { showNotice } from 'utils/obsidian'; export function getAllStyles() { const cssTexts: string[] = []; - Array.from(document.styleSheets).forEach(sheet => { - // @ts-ignore - const id = sheet.ownerNode?.id; + Array.from(activeDocument.styleSheets).forEach(sheet => { + let id: string | undefined = undefined; + let href: string | undefined = undefined; + if (sheet.ownerNode) { + if (sheet.ownerNode.instanceOf(HTMLElement)) { + id = sheet.ownerNode.id; + } + if (sheet.ownerNode.instanceOf(HTMLLinkElement)) { + href = sheet.ownerNode.href; + } + } //