diff --git a/HandTranscriptMd/eslint.config.mjs b/HandTranscriptMd/eslint.config.mjs index e9f95db..48c178c 100644 --- a/HandTranscriptMd/eslint.config.mjs +++ b/HandTranscriptMd/eslint.config.mjs @@ -1,24 +1,19 @@ import { defineConfig } from "eslint/config"; import obsidianmd from "eslint-plugin-obsidianmd"; -import tsparser from "@typescript-eslint/parser"; +import tseslint from "typescript-eslint"; export default defineConfig([ ...obsidianmd.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, { files: ["**/*.ts"], languageOptions: { - parser: tsparser, parserOptions: { project: "./tsconfig.json", tsconfigRootDir: import.meta.dirname, }, globals: { window: "readonly", - document: "readonly", - setTimeout: "readonly", - clearTimeout: "readonly", - requestAnimationFrame: "readonly", - cancelAnimationFrame: "readonly", performance: "readonly", console: "readonly", process: "readonly", diff --git a/HandTranscriptMd/manifest.json b/HandTranscriptMd/manifest.json index 50fb2e1..8dcc82b 100644 --- a/HandTranscriptMd/manifest.json +++ b/HandTranscriptMd/manifest.json @@ -2,7 +2,7 @@ "id": "handwriting-to-markdown", "name": "HandTranscriptMd", "version": "1.0.2", - "minAppVersion": "0.15.0", + "minAppVersion": "1.7.2", "description": "Inline handwriting canvas with OCR conversion to structured markdown. Requires a free Gemini API key.", "author": "GabrieleC", "authorUrl": "https://github.com/gabriele-cusato", diff --git a/HandTranscriptMd/package-lock.json b/HandTranscriptMd/package-lock.json index 4ed971c..f97baa4 100644 --- a/HandTranscriptMd/package-lock.json +++ b/HandTranscriptMd/package-lock.json @@ -1,12 +1,12 @@ { "name": "handwriting-to-markdown", - "version": "1.0.0", + "version": "1.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "handwriting-to-markdown", - "version": "1.0.0", + "version": "1.0.2", "license": "MIT", "dependencies": { "obsidian": "latest" @@ -17,7 +17,7 @@ "@typescript-eslint/parser": "^8.57.2", "esbuild": "0.25.5", "eslint": "^9.39.4", - "eslint-plugin-obsidianmd": "^0.1.9", + "eslint-plugin-obsidianmd": "^0.3.0", "globals": "^17.4.0", "jiti": "^2.6.1", "tslib": "2.4.0", @@ -612,7 +612,6 @@ "integrity": "sha512-rvR/EZtvUG3p9uqrSmcDJPYSH7atmWr0RnFWN6m917MAPx82+zQgPUmDu0whPFG6XTyM0vB/hR6c1Q63OaYtCQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@eslint/core": "^0.17.0", "@eslint/plugin-kit": "^0.4.1", @@ -691,7 +690,6 @@ "integrity": "sha512-KWiFQpSAqEIyrTXko3hFNLeQvSK8zXlJQzhhxsyVn58WFRYXST99b3Nqnu+ttOtjds2Pl2grUHGpe2NzhPynuQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=18" } @@ -785,9 +783,9 @@ } }, "node_modules/@types/eslint": { - "version": "8.56.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz", - "integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "license": "MIT", "dependencies": { @@ -2263,24 +2261,42 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/eslint-plugin-no-unsanitized": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.1.5.tgz", + "integrity": "sha512-MSB4hXPVFQrI8weqzs6gzl7reP2k/qSjtCoL2vUMSDejIIq9YL1ZKvq5/ORBXab/PvfBBrWO2jWviYpL+4Ghfg==", + "dev": true, + "license": "MPL-2.0", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, "node_modules/eslint-plugin-obsidianmd": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/eslint-plugin-obsidianmd/-/eslint-plugin-obsidianmd-0.1.9.tgz", - "integrity": "sha512-/gyo5vky3Y7re4BtT/8MQbHU5Wes4o6VRqas3YmXE7aTCnMsdV0kfzV1GDXJN9Hrsc9UQPoeKUMiapKL0aGE4g==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-obsidianmd/-/eslint-plugin-obsidianmd-0.3.0.tgz", + "integrity": "sha512-QvGDI6B2nxJBrsZKGTg31da2A/fEJNlnwN+fRZkaoPIu1QL3fYXUdpP7ThyMdr/0iTYQxifb9lt2X9cpydQx1w==", "dev": true, "license": "MIT", "dependencies": { + "@eslint/config-helpers": "^0.4.2", + "@eslint/js": "^9.30.1", + "@eslint/json": "0.14.0", "@microsoft/eslint-plugin-sdl": "^1.1.0", - "@types/eslint": "8.56.2", + "@types/eslint": "9.6.1", "@types/node": "20.12.12", - "eslint": ">=9.0.0 <10.0.0", + "@typescript-eslint/types": "^8.33.1", + "@typescript-eslint/utils": "^8.33.1", + "eslint": ">=9.0.0", "eslint-plugin-depend": "1.3.1", "eslint-plugin-import": "^2.31.0", "eslint-plugin-json-schema-validator": "5.1.0", + "eslint-plugin-no-unsanitized": "^4.1.5", "eslint-plugin-security": "2.1.1", "globals": "14.0.0", - "obsidian": "1.8.7", - "typescript": "5.4.5" + "obsidian": "1.12.3", + "semver": "^7.7.4", + "typescript": "5.4.5", + "typescript-eslint": "^8.35.1" }, "bin": { "eslint-plugin-obsidian": "dist/lib/index.js" @@ -2291,7 +2307,7 @@ "peerDependencies": { "@eslint/js": "^9.30.1", "@eslint/json": "0.14.0", - "eslint": ">=9.0.0 <10.0.0", + "eslint": ">=9.0.0", "obsidian": "1.8.7", "typescript-eslint": "^8.35.1" } @@ -2319,21 +2335,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-obsidianmd/node_modules/obsidian": { - "version": "1.8.7", - "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.8.7.tgz", - "integrity": "sha512-h4bWwNFAGRXlMlMAzdEiIM2ppTGlrh7uGOJS6w4gClrsjc+ei/3YAtU2VdFUlCiPuTHpY4aBpFJJW75S1Tl/JA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/codemirror": "5.60.8", - "moment": "2.29.4" - }, - "peerDependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" - } - }, "node_modules/eslint-plugin-obsidianmd/node_modules/typescript": { "version": "5.4.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", @@ -3826,9 +3827,9 @@ } }, "node_modules/obsidian": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.10.3.tgz", - "integrity": "sha512-VP+ZSxNMG7y6Z+sU9WqLvJAskCfkFrTz2kFHWmmzis+C+4+ELjk/sazwcTHrHXNZlgCeo8YOlM6SOrAFCynNew==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.12.3.tgz", + "integrity": "sha512-HxWqe763dOqzXjnNiHmAJTRERN8KILBSqxDSEqbeSr7W8R8Jxezzbca+nz1LiiqXnMpM8lV2jzAezw3CZ4xNUw==", "license": "MIT", "dependencies": { "@types/codemirror": "5.60.8", diff --git a/HandTranscriptMd/package.json b/HandTranscriptMd/package.json index 6e8a319..7998831 100644 --- a/HandTranscriptMd/package.json +++ b/HandTranscriptMd/package.json @@ -18,7 +18,7 @@ "@typescript-eslint/parser": "^8.57.2", "esbuild": "0.25.5", "eslint": "^9.39.4", - "eslint-plugin-obsidianmd": "^0.1.9", + "eslint-plugin-obsidianmd": "^0.3.0", "globals": "^17.4.0", "jiti": "^2.6.1", "tslib": "2.4.0", diff --git a/HandTranscriptMd/src/drawing-canvas.ts b/HandTranscriptMd/src/drawing-canvas.ts index 906b2c9..7208275 100644 --- a/HandTranscriptMd/src/drawing-canvas.ts +++ b/HandTranscriptMd/src/drawing-canvas.ts @@ -100,7 +100,7 @@ export class DrawingCanvas { this.mobileMode = mobileMode; this.debugFn = debugFn; - this.canvas = document.createElement('canvas'); + this.canvas = activeDocument.createElement('canvas'); // Dimensione CSS: pixel logici → il browser mostra il canvas a questa dimensione this.canvas.style.width = width + 'px'; this.canvas.style.height = height + 'px'; @@ -268,7 +268,7 @@ export class DrawingCanvas { destroy() { if (this.animFrameId !== null) { - cancelAnimationFrame(this.animFrameId); + window.cancelAnimationFrame(this.animFrameId); } this.canvas.removeEventListener('pointerdown', this.boundDown); this.canvas.removeEventListener('pointermove', this.boundMove); @@ -379,7 +379,7 @@ export class DrawingCanvas { if (startLogicalH === targetLogicalH) return; if (this.animFrameId !== null) { - cancelAnimationFrame(this.animFrameId); + window.cancelAnimationFrame(this.animFrameId); this.animFrameId = null; } @@ -406,13 +406,13 @@ export class DrawingCanvas { this.resizeCb?.(); if (progress < 1) { - this.animFrameId = requestAnimationFrame(step); + this.animFrameId = window.requestAnimationFrame(step); } else { this.animFrameId = null; } }; - this.animFrameId = requestAnimationFrame(step); + this.animFrameId = window.requestAnimationFrame(step); } /* --- Coordinate --- */ diff --git a/HandTranscriptMd/src/editor-view.ts b/HandTranscriptMd/src/editor-view.ts index e5e10c6..fbb231c 100644 --- a/HandTranscriptMd/src/editor-view.ts +++ b/HandTranscriptMd/src/editor-view.ts @@ -216,10 +216,7 @@ async function buildEditorUI(opts: { opts.afterCanvas(canvas, scrollWrap, canvasWidth); // Resize handle (visibile ma non interattivo) - // NOTA: handle è dichiarato dopo colorBtns ma catturato dal bgModeListener per closure: - // la closure legge il valore corrente di 'handle' quando viene invocata (non quando è definita). - let handle!: HTMLElement; - handle = scrollWrap.createDiv({ cls: 'hwm_resize-handle hwm_resize-handle--disabled' }); + const handle = scrollWrap.createDiv({ cls: 'hwm_resize-handle hwm_resize-handle--disabled' }); handle.createEl('span', { text: '⋯' }); handle.classList.toggle('hwm_resize-handle--dark', isDark); @@ -331,7 +328,7 @@ export class DrawingEditorView extends ItemView { this.canvas.destroy(); this.canvas = null; } - if (this.saveTimer) clearTimeout(this.saveTimer); + if (this.saveTimer) window.clearTimeout(this.saveTimer); // Deregistra il listener bgMode if (this.bgModeListener) { this.plugin.bgModeListeners.delete(this.bgModeListener); @@ -379,8 +376,8 @@ export class DrawingEditorView extends ItemView { // Auto-save debounced (2s dopo l'ultimo cambiamento) canvas.onChange(() => { - if (this.saveTimer) clearTimeout(this.saveTimer); - this.saveTimer = setTimeout(() => { void this.saveSvg(); }, 2000); + if (this.saveTimer) window.clearTimeout(this.saveTimer); + this.saveTimer = window.setTimeout(() => { void this.saveSvg(); }, 2000); }); } @@ -485,7 +482,7 @@ export class DrawingModal extends Modal { this.canvas.destroy(); this.canvas = null; } - if (this.saveTimer) clearTimeout(this.saveTimer); + if (this.saveTimer) window.clearTimeout(this.saveTimer); // Deregistra il listener bgMode if (this.bgModeListener) { this.plugin.bgModeListeners.delete(this.bgModeListener); @@ -510,7 +507,7 @@ export class DrawingModal extends Modal { // Espande il canvas a tutta la larghezza del modal eliminando le bande laterali. // requestAnimationFrame garantisce che il layout del modal sia pronto prima di misurarlo. afterCanvas: (cv, scrollWrap, canvasWidth) => { - requestAnimationFrame(() => { + window.requestAnimationFrame(() => { const displayW = scrollWrap.clientWidth; if (displayW > canvasWidth) cv.setDisplayWidth(displayW); }); @@ -525,8 +522,8 @@ export class DrawingModal extends Modal { // Auto-save debounced (2s dopo l'ultimo cambiamento) canvas.onChange(() => { - if (this.saveTimer) clearTimeout(this.saveTimer); - this.saveTimer = setTimeout(() => { void this.saveSvg(); }, 2000); + if (this.saveTimer) window.clearTimeout(this.saveTimer); + this.saveTimer = window.setTimeout(() => { void this.saveSvg(); }, 2000); }); } @@ -591,7 +588,7 @@ export class DrawingModal extends Modal { const doFocus = () => { if (focusDone) return; focusDone = true; - setTimeout(() => { + window.setTimeout(() => { let mdView = ws.getActiveViewOfType(MarkdownView); if (!mdView || mdView.file?.path !== srcPath) { const leaf = ws.getLeavesOfType('markdown') @@ -618,7 +615,7 @@ export class DrawingModal extends Modal { if (svgFile instanceof TFile) await this.app.fileManager.trashFile(svgFile); // Fallback: se vault.modify non spara entro 3s (caso anomalo), forza comunque il focus - setTimeout(() => { this.app.vault.offref(ref); doFocus(); }, 3000); + window.setTimeout(() => { this.app.vault.offref(ref); doFocus(); }, 3000); this.close(); new Notice(t('notice_deleted')); diff --git a/HandTranscriptMd/src/embed.ts b/HandTranscriptMd/src/embed.ts index 52460c3..943a312 100644 --- a/HandTranscriptMd/src/embed.ts +++ b/HandTranscriptMd/src/embed.ts @@ -116,7 +116,7 @@ function setupMutationObserver(plugin: HandwritingPlugin) { // Se Obsidian non ha ancora caricato l'immagine (classe image-embed // assente), riprova tra 150 ms — il caricamento è asincrono. if (!span.classList.contains('image-embed')) { - setTimeout(() => tryDecorate(span), 150); + window.setTimeout(() => tryDecorate(span), 150); return; } @@ -153,7 +153,7 @@ function setupMutationObserver(plugin: HandwritingPlugin) { // Disable transition temporarily so the height sync is instant (no visual flash). wrapper.classList.add('hwm_no-transition'); wrapper.style.setProperty('height', newH + 'px'); - requestAnimationFrame(() => { + window.requestAnimationFrame(() => { wrapper.style.removeProperty('height'); wrapper.classList.remove('hwm_no-transition'); }); @@ -200,7 +200,7 @@ function setupMutationObserver(plugin: HandwritingPlugin) { } }); - observer.observe(document.body, { childList: true, subtree: true }); + observer.observe(activeDocument.body, { childList: true, subtree: true }); // Disconnette l'observer quando il plugin viene disabilitato plugin.register(() => observer.disconnect()); } @@ -615,7 +615,7 @@ function createPortalPanel( // position: relative sullo span è gestita dalla regola CSS // .internal-embed[data-hwm-decorated="1"] — non serve inline style. - const panel = document.createElement('div'); + const panel = activeDocument.createElement('div'); panel.className = 'hwm_portal-panel'; if (isDark) panel.classList.add('hwm_portal-panel--dark'); container.appendChild(panel); @@ -660,7 +660,7 @@ function createPortalPanel( })(); }); // Separatore visivo - const sep = document.createElement('div'); + const sep = activeDocument.createElement('div'); sep.className = 'hwm_separator'; panel.appendChild(sep); @@ -694,7 +694,7 @@ function createPortalPanel( if (!img || !img.parentElement) return null; // Crea il wrapper e sposta l'img dentro: nessuna classe/altezza iniziale. // La transizione CSS (height 0.3s ease) è già definita in styles.css su .hwm_clip-wrapper. - wrapper = document.createElement('div'); + wrapper = activeDocument.createElement('div'); wrapper.className = 'hwm_clip-wrapper'; img.parentElement.insertBefore(wrapper, img); wrapper.appendChild(img); @@ -717,7 +717,7 @@ function createPortalPanel( wrapper.classList.remove('hwm_overflow-hidden'); }; wrapper.addEventListener('transitionend', cleanup, { once: true }); - setTimeout(cleanup, 400); // 300ms transizione + 100ms buffer + window.setTimeout(cleanup, 400); // 300ms transizione + 100ms buffer } container.classList.remove('hwm_is-collapsed'); collapseBtn.classList.remove('hwm_rotated'); @@ -768,9 +768,9 @@ function createPortalPanel( const showConvertOverlay = (): HTMLElement => { // Nasconde il pannello durante OCR: evita click sui bottoni panel.classList.add('hwm_hidden'); - const overlay = document.createElement('div'); + const overlay = activeDocument.createElement('div'); overlay.className = 'hwm_convert-overlay'; - const spinner = document.createElement('div'); + const spinner = activeDocument.createElement('div'); spinner.className = 'hwm_spinner'; overlay.appendChild(spinner); container.appendChild(overlay); @@ -861,7 +861,7 @@ function showInlineConfirm(anchorEl: HTMLElement, msg: string): Promise // Crea un bottone div nel pannello portale. // key: chiave i18n — usata sia per il title che per data-hwm-key (aggiornamento live al cambio lingua) function createPanelBtn(parent: HTMLElement, icon: string, key: I18nKey): HTMLElement { - const btn = document.createElement('div'); + const btn = activeDocument.createElement('div'); btn.className = 'hwm_btn'; btn.setAttribute('title', t(key)); btn.setAttribute('data-hwm-key', key); @@ -886,12 +886,12 @@ function createLegacyPortalButton( svgPath: string, sourcePath: string ) { - const btn = document.createElement('button'); + const btn = activeDocument.createElement('button'); btn.className = 'hwm_portal-btn'; // setIcon: inserisce l'SVG Lucide in modo sicuro (no innerHTML) setIcon(btn, 'pencil'); btn.title = t('btn_open_editor'); - document.body.appendChild(btn); + activeDocument.body.appendChild(btn); // Apre la tab editor al click btn.addEventListener('click', () => { void (async () => { @@ -927,9 +927,9 @@ function createLegacyPortalButton( .some(l => (l.view as DrawingEditorView).getEmbedId() === embedId); const inViewport = rect.width > 0 && rect.top < window.innerHeight && rect.bottom > 0; btn.classList.toggle('hwm_hidden', !(inViewport && !editorOpen)); - requestAnimationFrame(update); + window.requestAnimationFrame(update); }; - requestAnimationFrame(update); + window.requestAnimationFrame(update); } // Usa
invece di