diff --git a/.gitignore b/.gitignore index 0640396..6168a07 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,12 @@ data.json # Exclude macOS Finder (System Explorer) View States .DS_Store + +# iCLoud +*.nosync + +# Sublime +*sublime-workspace + +# Markdown +*.md \ No newline at end of file diff --git a/manifest.json b/manifest.json index c59697c..6d318bd 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "plugins-annotations", "name": "Plugins Annotations", - "version": "1.7.8", + "version": "1.7.10", "minAppVersion": "1.5.0", "description": "Allows adding personal comments to each installed plugin.", "author": "Andrea Alberti", 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" + } + ] +} diff --git a/src/annotation_control.ts b/src/annotation_control.ts index 7389cd6..7f1835f 100644 --- a/src/annotation_control.ts +++ b/src/annotation_control.ts @@ -56,7 +56,10 @@ export class AnnotationControl { addEventListeners() { const linkInteractionHandler = (event: MouseEvent | TouchEvent) => { - if (event.target && (event.target as HTMLElement).tagName === 'A') { + 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; @@ -73,8 +76,10 @@ export class AnnotationControl { return; } else { event.stopPropagation(); - // Explicitly focus to help mobile keyboards appear, especially on Android. - this.annotation_div.focus(); + if (!this.clickedLink) { + // Explicitly focus to help mobile keyboards appear, especially on Android. + this.annotation_div.focus(); + } } }); 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 +}