Merge branch 'main' into testing_annotation_control

This commit is contained in:
Andrea Alberti 2026-01-24 20:57:13 +01:00 committed by GitHub
commit 3ba82f30c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 140 additions and 98 deletions

9
.gitignore vendored
View file

@ -18,3 +18,12 @@ data.json
# Exclude macOS Finder (System Explorer) View States
.DS_Store
# iCLoud
*.nosync
# Sublime
*sublime-workspace
# Markdown
*.md

View file

@ -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",

View file

@ -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"
}
]
}

View file

@ -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();
}
}
});

View file

@ -45,7 +45,7 @@ export default class PluginsAnnotations extends Plugin {
debouncedSaveAnnotations: (callback?: () => void) => void;
waitForSaveToComplete: () => Promise<void>;
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;
}
}

View file

@ -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;
}
}
}