From cdfe973a1f870c758cae8e546ee5da9b39f5e276 Mon Sep 17 00:00:00 2001 From: Andrea Alberti Date: Mon, 19 Jan 2026 22:48:26 +0100 Subject: [PATCH 1/5] Updated gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 0640396..cd0e957 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ data.json # Exclude macOS Finder (System Explorer) View States .DS_Store + +# iCLoud +*.nosync \ No newline at end of file From 50f36810d3850f5d354f8953602304e2d118f187 Mon Sep 17 00:00:00 2001 From: Andrea Alberti Date: Sat, 24 Jan 2026 16:40:26 +0100 Subject: [PATCH 2/5] Added Sublime Text project file --- .gitignore | 8 +++++- obsidian-plugin-annotations.sublime-project | 29 +++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 obsidian-plugin-annotations.sublime-project diff --git a/.gitignore b/.gitignore index cd0e957..6168a07 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,10 @@ data.json .DS_Store # iCLoud -*.nosync \ No newline at end of file +*.nosync + +# Sublime +*sublime-workspace + +# Markdown +*.md \ No newline at end of file diff --git a/obsidian-plugin-annotations.sublime-project b/obsidian-plugin-annotations.sublime-project new file mode 100644 index 0000000..3ae30cd --- /dev/null +++ b/obsidian-plugin-annotations.sublime-project @@ -0,0 +1,29 @@ +{ + "folders": [ + { + "path": ".", + "folder_exclude_patterns": [".git", "node_modules", "dist"] + } + ], + "settings": { + "tab_size": 4, + "translate_tabs_to_spaces": true, + "typescript_tsdk": "./node_modules/typescript/lib" + }, + "build_systems": [ + { + "name": "TypeScript Build", + "shell_cmd": "npm run build", + "working_dir": "${folder}", // Use ${folder} to point to the project root + "file_regex": "^\\s*(.+?\\.ts)\\((\\d+),(\\d+)\\):\\s*(.*)$", + "selector": "source.ts" + }, + { + "name": "TypeScript Dev", + "shell_cmd": "npm run dev", + "working_dir": "${folder}", // Use ${folder} to point to the project root + "file_regex": "^\\s*(.+?\\.ts):(\\d+):(\\d+):\\s*(.*)$", + "selector": "source.ts" + } + ] +} From 5740615518ce9e8678d4b8aa1026f9007e8c1527 Mon Sep 17 00:00:00 2001 From: Andrea Alberti Date: Sat, 24 Jan 2026 17:48:08 +0100 Subject: [PATCH 3/5] Fix community plugins hook for Obsidian 1.11.5 (refs #26) --- manifest.json | 2 +- src/main.ts | 172 ++++++++++++++++---------------- src/types/obsidian-augment.d.ts | 15 ++- 3 files changed, 94 insertions(+), 95 deletions(-) diff --git a/manifest.json b/manifest.json index c59697c..7bba2cf 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "plugins-annotations", "name": "Plugins Annotations", - "version": "1.7.8", + "version": "1.7.9", "minAppVersion": "1.5.0", "description": "Allows adding personal comments to each installed plugin.", "author": "Andrea Alberti", diff --git a/src/main.ts b/src/main.ts index ba627a7..dde8aa5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -45,7 +45,7 @@ export default class PluginsAnnotations extends Plugin { debouncedSaveAnnotations: (callback?: () => void) => void; waitForSaveToComplete: () => Promise; - private communityPluginSettingTabPatched = false; + private communityPluginSettingTabPatched = false; private listGitHubIcons:HTMLDivElement[] = []; @@ -104,18 +104,22 @@ export default class PluginsAnnotations extends Plugin { } } - async onLayoutReady() { - // Load settings - const loadSettingsPromise = this.loadSettings(); - - // Load the big json file containing the GitHub address of all community plugins - const loadCommunityPluginsJsonPromise = this.loadCommunityPluginsJson(); + async onLayoutReady() { + // Load settings + const loadSettingsPromise = this.loadSettings(); + + // Load the big json file containing the GitHub address of all community plugins + const loadCommunityPluginsJsonPromise = this.loadCommunityPluginsJson(); - // Store a reference to the community plugin tab - this.communityPluginTab = this.app.setting.settingTabs.find((tab:SettingTab):boolean => tab.id === "community-plugins"); + // Store a reference to the community plugin tab + this.communityPluginTab = this.app.setting.settingTabs.find((tab:SettingTab):boolean => tab.id === "community-plugins"); - // Patch the rendering function of the community plugin preference pane - if(this.communityPluginTab) this.patchCommunityPluginSettingTab(this.communityPluginTab); + // Patch the rendering function of the community plugin preference pane + if(this.communityPluginTab) { + this.patchCommunityPluginSettingTab(this.communityPluginTab); + } else { + console.warn('[Plugins Annotations] Community plugins tab not found; hook not installed.'); + } // Monkey-patch functions to detect when community plugins are installed and uninstalled. this.hookOnInstallAndUninstallPlugins(); @@ -406,49 +410,46 @@ export default class PluginsAnnotations extends Plugin { return invertedMap; } - patchCommunityPluginSettingTab(tab:SettingTab) { - if(this.communityPluginSettingTabPatched) return; + patchCommunityPluginSettingTab(tab:SettingTab) { + if(this.communityPluginSettingTabPatched) return; - // eslint-disable-next-line @typescript-eslint/no-this-alias - const self = this; + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; // Monkey patch for uninstallPlugin - const removeMonkeyPatchForRender = around(tab, { - renderInstalledPlugin: (next: ( - pluginManifest: PluginManifest, - containerEl:HTMLElement, - nameMatch: boolean | null, - authorMatch: boolean | null, - descriptionMatch: boolean | null - ) => void ) => { + const removeMonkeyPatchForRender = around(tab, { + renderInstalledPlugin: (next: SettingTab['renderInstalledPlugin']) => { - return function (this: SettingTab, - pluginManifest: PluginManifest, - containerEl: HTMLElement, - nameMatch: boolean | null, - authorMatch: boolean | null, - descriptionMatch: boolean | null - ): void { - next.call(this, pluginManifest, containerEl, nameMatch, authorMatch, descriptionMatch); + return function (this: SettingTab, + pluginManifest: PluginManifest, + nameMatch: boolean | null, + authorMatch: boolean | null, + descriptionMatch: boolean | null + ): void { + next.call(this, pluginManifest, nameMatch, authorMatch, descriptionMatch); - // Custom code for personal annotations here - if(containerEl && containerEl.lastElementChild) self.addAnnotation(containerEl.lastElementChild); - }; - }, + // Custom code for personal annotations here + const listEl = (this as SettingTab & { installedPlugins?: { listEl?: HTMLElement } }).installedPlugins?.listEl; + const targetElement = listEl?.lastElementChild; + if (targetElement instanceof HTMLElement) { + self.addAnnotation(targetElement); + } + }; + }, // Patch for `render` method - render: (next: ( - isInitialRender: boolean - ) => void) => { + render: (next: ( + isInitialRender: boolean + ) => void) => { - return function (this: SettingTab, isInitialRender: boolean): void { - self.listGitHubIcons = []; + return function (this: SettingTab, isInitialRender: boolean): void { + self.listGitHubIcons = []; - // Call the original `render` function - next.call(this, isInitialRender); - self.addLockIcon(this.containerEl); - }; - } - }); + // Call the original `render` function + next.call(this, isInitialRender); + self.addLockIcon(this.containerEl); + }; + } + }); // Register the patch to ensure it gets cleaned up this.register(removeMonkeyPatchForRender); @@ -645,53 +646,53 @@ export default class PluginsAnnotations extends Plugin { } } - addAnnotation(pluginDOMElement: Element) { - const pluginNameDiv = pluginDOMElement.querySelector('.setting-item-name'); - const pluginName = pluginNameDiv ? pluginNameDiv.textContent : null; + addAnnotation(pluginDOMElement: Element) { + const pluginNameDiv = pluginDOMElement.querySelector('.setting-item-name'); + const pluginName = pluginNameDiv ? pluginNameDiv.textContent : null; - if (!pluginName) { - console.warn('Plugin name not found'); - return; - } + if (!pluginName) { + console.warn('Plugin name not found'); + return; + } - const pluginId = this.pluginNameToIdMap[pluginName]; - if (!pluginId) { - console.warn(`Plugin ID not found for plugin name: ${pluginName}`); - return; - } + const pluginId = this.pluginNameToIdMap[pluginName]; + if (!pluginId) { + console.warn(`Plugin ID not found for plugin name: ${pluginName}`); + return; + } - const settingItemInfo = pluginDOMElement.querySelector('.setting-item-info'); - if (settingItemInfo) { - const descriptionDiv = settingItemInfo.querySelector('.setting-item-description'); - if (descriptionDiv) { - const commentDiv = descriptionDiv.querySelector('.plugin-comment'); - if (!commentDiv) { - const annotation_container = document.createElement('div'); - annotation_container.className = 'plugin-comment'; + const settingItemInfo = pluginDOMElement.querySelector('.setting-item-info'); + if (settingItemInfo) { + const descriptionDiv = settingItemInfo.querySelector('.setting-item-description'); + if (descriptionDiv) { + const commentDiv = descriptionDiv.querySelector('.plugin-comment'); + if (!commentDiv) { + const annotation_container = document.createElement('div'); + annotation_container.className = 'plugin-comment'; const annotationControl = new AnnotationControl(this,annotation_container,pluginId,pluginName); descriptionDiv.appendChild(annotation_container); - if(this.settings.show_github_icons) { - // Get the repository of the plugin - const community_plugins = this.community_plugins[pluginId]; - const repo = community_plugins ? community_plugins.repo : undefined; - if (repo) { - const controlDiv = pluginDOMElement.querySelector('.setting-item-control'); - if(controlDiv) { - if(this.colorSchemeMedia) { - const isDarkMode = this.colorSchemeMedia.matches; - const gitHubIcon = annotationControl.addGitHubIcon(controlDiv,repo, isDarkMode); - if(gitHubIcon) this.listGitHubIcons.push(gitHubIcon); - } - } - } - } - } - } - } - } + if(this.settings.show_github_icons) { + // Get the repository of the plugin + const community_plugins = this.community_plugins[pluginId]; + const repo = community_plugins ? community_plugins.repo : undefined; + if (repo) { + const controlDiv = pluginDOMElement.querySelector('.setting-item-control'); + if(controlDiv) { + if(this.colorSchemeMedia) { + const isDarkMode = this.colorSchemeMedia.matches; + const gitHubIcon = annotationControl.addGitHubIcon(controlDiv,repo, isDarkMode); + if(gitHubIcon) this.listGitHubIcons.push(gitHubIcon); + } + } + } + } + } + } + } + } addAnnotations() { if(this.communityPluginTab===undefined) { @@ -760,4 +761,3 @@ export default class PluginsAnnotations extends Plugin { return uninstalledPlugins; } } - diff --git a/src/types/obsidian-augment.d.ts b/src/types/obsidian-augment.d.ts index 831079e..68ef034 100644 --- a/src/types/obsidian-augment.d.ts +++ b/src/types/obsidian-augment.d.ts @@ -20,13 +20,12 @@ declare module "obsidian" { navEl: HTMLElement; // updateSearch(e: string): void; render(isInitialRender:boolean):void; - renderInstalledPlugin( - pluginManifest: PluginManifest, - containerEl: HTMLElement, - nameMatch: boolean | null, - authorMatch: boolean | null, - descriptionMatch: boolean | null - ): void; + renderInstalledPlugin( + pluginManifest: PluginManifest, + nameMatch: boolean | null, + authorMatch: boolean | null, + descriptionMatch: boolean | null + ): void; } interface Setting { onOpen(): void; @@ -70,4 +69,4 @@ declare module "obsidian" { textInputEl: HTMLInputElement; } -} \ No newline at end of file +} From 48459054538dbbe94c5d76d145f30c62abd305ff Mon Sep 17 00:00:00 2001 From: Stef Nado Date: Mon, 13 Oct 2025 20:07:08 -0400 Subject: [PATCH 4/5] Fix mobile annotation focus and touch handling (refs #24, PR #25) --- src/annotation_control.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/annotation_control.ts b/src/annotation_control.ts index f1f4a92..7389cd6 100644 --- a/src/annotation_control.ts +++ b/src/annotation_control.ts @@ -55,13 +55,17 @@ export class AnnotationControl { } addEventListeners() { - this.annotation_div.addEventListener('mousedown', (event:MouseEvent) => { + const linkInteractionHandler = (event: MouseEvent | TouchEvent) => { if (event.target && (event.target as HTMLElement).tagName === 'A') { this.clickedLink = true; } else { this.clickedLink = false; } - }); + }; + + this.annotation_div.addEventListener('mousedown', linkInteractionHandler); + this.annotation_div.addEventListener('touchstart', linkInteractionHandler, { passive: true }); + // Prevent click event propagation to parent this.annotation_div.addEventListener('click', (event:MouseEvent) => { @@ -69,6 +73,8 @@ export class AnnotationControl { return; } else { event.stopPropagation(); + // Explicitly focus to help mobile keyboards appear, especially on Android. + this.annotation_div.focus(); } }); From 4a8f77760246f0607ef2d28b0f81d317a395cb2d Mon Sep 17 00:00:00 2001 From: Andrea Alberti Date: Sat, 24 Jan 2026 19:55:59 +0100 Subject: [PATCH 5/5] Handle nested link clicks in annotations --- manifest.json | 2 +- src/annotation_control.ts | 29 +++++++++++++++++------------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/manifest.json b/manifest.json index 7bba2cf..6d318bd 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "plugins-annotations", "name": "Plugins Annotations", - "version": "1.7.9", + "version": "1.7.10", "minAppVersion": "1.5.0", "description": "Allows adding personal comments to each installed plugin.", "author": "Andrea Alberti", diff --git a/src/annotation_control.ts b/src/annotation_control.ts index 7389cd6..f1e3001 100644 --- a/src/annotation_control.ts +++ b/src/annotation_control.ts @@ -55,13 +55,16 @@ export class AnnotationControl { } addEventListeners() { - const linkInteractionHandler = (event: MouseEvent | TouchEvent) => { - if (event.target && (event.target as HTMLElement).tagName === 'A') { - this.clickedLink = true; - } else { - this.clickedLink = false; - } - }; + const linkInteractionHandler = (event: MouseEvent | TouchEvent) => { + const target = event.target as HTMLElement | null; + // Use closest('a') so clicks on nested elements (e.g., spans inside links) still register as link clicks. + // This scenario is likely never occurring, but closest('a') keeps link detection reliable (just in case). + if (target && target.closest('a')) { + this.clickedLink = true; + } else { + this.clickedLink = false; + } + }; this.annotation_div.addEventListener('mousedown', linkInteractionHandler); this.annotation_div.addEventListener('touchstart', linkInteractionHandler, { passive: true }); @@ -71,11 +74,13 @@ export class AnnotationControl { this.annotation_div.addEventListener('click', (event:MouseEvent) => { if(!this.plugin.settings.editable) { return; - } else { - event.stopPropagation(); - // Explicitly focus to help mobile keyboards appear, especially on Android. - this.annotation_div.focus(); - } + } else { + event.stopPropagation(); + if (!this.clickedLink) { + // Explicitly focus to help mobile keyboards appear, especially on Android. + this.annotation_div.focus(); + } + } }); this.annotation_div.addEventListener('focus', async (event:FocusEvent) => {