diff --git a/README.md b/README.md index dfdb09a..b033cad 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Obsidian Plugins Annotations is a plugin for [Obsidian](https://obsidian.md) tha - Detailed instructions how to edit the annotations are provided in the _setting pane_ of the plugin. - Possible to lock the annotations with the lock button next to the title "Installed plugins". This feature is useful in combination with the toggle hiding the annotations fields where there is no user annotation yet. -## Installation +## Installation The preferred method to install the plugin is to use the Community Plugins Preference pane in Obsidian. @@ -32,7 +32,7 @@ The preferred method to install the plugin is to use the Community Plugins Prefe ## Screenshot -![Screenshot of the setting pane showing installed plugins with annotations by the user](docs/images/screenshot.jpg) +![Screenshot of the setting pane showing installed plugins with annotations by the user](docs/images/screenshot_dark.jpg) ## Development diff --git a/docs/images/screenshot_dark.jpg b/docs/images/screenshot_dark.jpg new file mode 100644 index 0000000..574807a Binary files /dev/null and b/docs/images/screenshot_dark.jpg differ diff --git a/manifest.json b/manifest.json index 51300a0..73b38ee 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "plugins-annotations", "name": "Plugins Annotations", - "version": "1.6.6", + "version": "1.7.0", "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 ef81b58..8146b7b 100644 --- a/src/annotation_control.ts +++ b/src/annotation_control.ts @@ -4,14 +4,21 @@ import PluginsAnnotations from "main"; import { MarkdownRenderer, Platform } from "obsidian"; import { isPluginAnnotation } from "types"; -export class annotationControl { +const github_prefix = "https://github.com/"; + +import { svg_github_dark, svg_github_light } from "graphics"; + +export class AnnotationControl { + static addGitHubIcon(controlDiv: Element) { + throw new Error('Method not implemented.'); + } private clickedLink: boolean; private isPlaceholder: boolean; private annotationDesc:string; private placeholder:string; private label:string; private annotation_div: HTMLDivElement; - + constructor(private plugin: PluginsAnnotations, private annotation_container:HTMLElement, private pluginId:string, private pluginName:string) { this.clickedLink = false; @@ -183,4 +190,31 @@ export class annotationControl { }); }); } + + addGitHubIcon(controlDiv:Element, repo:string, isDarkMode:boolean) { + if (controlDiv) { + const GitHubDiv = document.createElement('div'); + GitHubDiv.classList.add('clickable-icon', 'extra-setting-button', 'github-icon'); + GitHubDiv.setAttribute('aria-label', 'Open plugin\'s GitHub page'); + GitHubDiv.innerHTML = isDarkMode ? svg_github_dark : svg_github_light; + + // Add click listener to open the repo URL + GitHubDiv.addEventListener('click', () => { + // Use Obsidian's native method to open external links + window.open(github_prefix + repo, '_blank'); + }); + + // Get all elements with the class .clickable-icon inside controlDiv + const clickableIcons = controlDiv.querySelectorAll('.clickable-icon'); + + // Insert the new icon as the second last of all clickable icons + if (clickableIcons.length > 0) { + const lastIcon = clickableIcons[clickableIcons.length - 1]; + controlDiv.insertBefore(GitHubDiv, lastIcon); + } else { + // If no clickable icons are found, append it as the first child + controlDiv.insertBefore(GitHubDiv, controlDiv.firstChild); + } + } + } } diff --git a/src/defaults.ts b/src/defaults.ts index 76e722a..0b036e8 100644 --- a/src/defaults.ts +++ b/src/defaults.ts @@ -18,6 +18,7 @@ export const DEFAULT_SETTINGS: PluginsAnnotationsSettings = { editable: true, automatic_remove: false, markdown_file_path: '', + show_github_icons: true, backups: [], - compatibility: '1.6.0', + compatibility: '1.7.0', } diff --git a/src/defaults_legacy.ts b/src/defaults_legacy.ts index d56efd8..abdcc53 100644 --- a/src/defaults_legacy.ts +++ b/src/defaults_legacy.ts @@ -1,8 +1,29 @@ // defaults.ts -import { AnnotationType_1_5_0, PluginsAnnotationsSettings_1_3_0, PluginsAnnotationsSettings_1_4_0, PluginsAnnotationsSettings_1_5_0 } from './types_legacy'; +import { AnnotationType_1_5_0, PluginsAnnotationsSettings_1_3_0, PluginsAnnotationsSettings_1_4_0, PluginsAnnotationsSettings_1_5_0, PluginsAnnotationsSettings_1_6_0 } from './types_legacy'; + +export const DEFAULT_SETTINGS_1_6_0: PluginsAnnotationsSettings_1_6_0 = { + annotations: { + "plugins-annotations": { + "name": "Plugins Annotations", + "desc": "Allows writing annotations (just like this one) about the community plugins installed in the vault.", + } + }, + plugins_annotations_uuid: 'BC56AB7B-A46F-4ACF-9BA1-3A4461F74C79', + hide_placeholders: false, + delete_placeholder_string_on_insertion: false, + label_mobile: 'Annotation: ', + label_desktop: 'Personal annotation: ', + label_placeholder : "Add your personal comment about ${plugin_name} here...", + editable: true, + automatic_remove: false, + markdown_file_path: '', + backups: [], + compatibility: '1.6.0', +} /* ===== Version 1.5.0 ===== */ + export const DEFAULT_SETTINGS_1_5_0: PluginsAnnotationsSettings_1_5_0 = { "annotations": { "plugins-annotations": { diff --git a/src/graphics.ts b/src/graphics.ts new file mode 100644 index 0000000..a102a53 --- /dev/null +++ b/src/graphics.ts @@ -0,0 +1,7 @@ +// graphics.ts + +export const svg_unlocked = atob('PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgdmlld0JveD0iMCAwIDI0IDI0IiBcCiAgICAgICAgICAgICAgICAgICAgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS1sb2NrLW9wZW4iPlwKICAgICAgICAgICAgICAgICAgICA8cmVjdCB3aWR0aD0iMTgiIGhlaWdodD0iMTEiIHg9IjMiIHk9IjExIiByeD0iMiIgcnk9IjIiLz5cCiAgICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik03IDExdi00YzAtMi44IDIuMi01IDUtNSAxLjYgMCAzLjEuOCA0IDIiLz4gXAogICAgICAgICAgICAgICAgPC9zdmc+'); +export const svg_locked = atob('PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgdmlld0JveD0iMCAwIDI0IDI0IiBcCiAgICAgICAgICAgICAgICAgICAgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS1sb2NrIj5cCiAgICAgICAgICAgICAgICAgICAgPHJlY3Qgd2lkdGg9IjE4IiBoZWlnaHQ9IjExIiB4PSIzIiB5PSIxMSIgcng9IjIiIHJ5PSIyIi8+XAogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik03IDExVjdhNSA1IDAgMCAxIDEwIDB2NCIvPlwKICAgICAgICAgICAgICAgIDwvc3ZnPg=='); + +export const svg_github_light=atob('PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgY2xhc3M9InN2Zy1pY29uIGx1Y2lkZS1zZXR0aW5ncyI+CiAgPGRlZnM+CiAgICA8c3R5bGU+CiAgICAgIC5jbHMtMSB7CiAgICAgICAgZmlsbDogIzI0MjkyZjsKICAgICAgICBmaWxsLXJ1bGU6IGV2ZW5vZGQ7CiAgICAgICAgc3Ryb2tlLXdpZHRoOiAwcHg7CiAgICAgIH0KICAgIDwvc3R5bGU+CiAgPC9kZWZzPgogIDxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTEyLC4zQzUuNC4zLjEsNS43LjEsMTIuM3MzLjQsOS44LDguMSwxMS40Yy42LjEuOC0uMy44LS42czAtMS4yLDAtMi4yYy0zLjMuNy00LTEuNC00LTEuNC0uNS0xLjQtMS4zLTEuNy0xLjMtMS43LTEuMS0uNywwLS43LDAtLjcsMS4yLDAsMS44LDEuMiwxLjgsMS4yLDEuMSwxLjgsMi44LDEuMywzLjUsMSwwLS44LjQtMS4zLjctMS42LTIuNi0uMy01LjQtMS4zLTUuNC01LjlzLjUtMi40LDEuMi0zLjJjLS4xLS4zLS41LTEuNS4xLTMuMiwwLDAsMS0uMywzLjMsMS4yLDEtLjMsMi0uNCwzLS40LDEsMCwyLC4xLDMsLjQsMi4zLTEuNSwzLjMtMS4yLDMuMy0xLjIuNywxLjYuMiwyLjkuMSwzLjIuOC44LDEuMiwxLjksMS4yLDMuMiwwLDQuNi0yLjgsNS42LTUuNCw1LjkuNC40LjgsMS4xLjgsMi4yLDAsMS42LDAsMi45LDAsMy4zcy4yLjcuOC42YzQuNy0xLjYsOC4xLTYuMSw4LjEtMTEuNCwwLTYuNi01LjMtMTItMTEuOS0xMloiLz4KPC9zdmc+'); +export const svg_github_dark=atob('PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgY2xhc3M9InN2Zy1pY29uIGx1Y2lkZS1zZXR0aW5ncyI+CiAgPGRlZnM+CiAgICA8c3R5bGU+CiAgICAgIC5jbHMtMSB7CiAgICAgICAgZmlsbDogI2ZmZjsKICAgICAgICBmaWxsLXJ1bGU6IGV2ZW5vZGQ7CiAgICAgICAgc3Ryb2tlLXdpZHRoOiAwcHg7CiAgICAgIH0KICAgIDwvc3R5bGU+CiAgPC9kZWZzPgogIDxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTEyLC4zQzUuNC4zLjEsNS43LjEsMTIuM3MzLjQsOS44LDguMSwxMS40Yy42LjEuOC0uMy44LS42czAtMS4yLDAtMi4yYy0zLjMuNy00LTEuNC00LTEuNC0uNS0xLjQtMS4zLTEuNy0xLjMtMS43LTEuMS0uNywwLS43LDAtLjcsMS4yLDAsMS44LDEuMiwxLjgsMS4yLDEuMSwxLjgsMi44LDEuMywzLjUsMSwwLS44LjQtMS4zLjctMS42LTIuNi0uMy01LjQtMS4zLTUuNC01LjlzLjUtMi40LDEuMi0zLjJjLS4xLS4zLS41LTEuNS4xLTMuMiwwLDAsMS0uMywzLjMsMS4yLDEtLjMsMi0uNCwzLS40LDEsMCwyLC4xLDMsLjQsMi4zLTEuNSwzLjMtMS4yLDMuMy0xLjIuNiwxLjYuMiwyLjkuMSwzLjIuOC44LDEuMiwxLjksMS4yLDMuMiwwLDQuNi0yLjgsNS42LTUuNCw1LjkuNC40LjgsMS4xLjgsMi4yLDAsMS42LDAsMi45LDAsMy4zcy4yLjcuOC42YzQuNy0xLjYsOC4xLTYuMSw4LjEtMTEuNCwwLTYuNi01LjMtMTItMTEuOS0xMloiLz4KPC9zdmc+'); diff --git a/src/main.ts b/src/main.ts index 286f1b6..cf3adca 100644 --- a/src/main.ts +++ b/src/main.ts @@ -14,36 +14,24 @@ import { // App, } from 'obsidian'; import { around } from 'monkey-around'; -import { isPluginAnnotation, isPluginsAnnotationsSettings, PluginAnnotation, PluginAnnotationDict, PluginBackup, PluginsAnnotationsSettings } from './types'; -import { PluginAnnotationDict_1_4_0, PluginsAnnotationsSettings_1_4_0, PluginsAnnotationsSettings_1_3_0, isPluginAnnotationDictFormat_1_3_0, isSettingsFormat_1_3_0, isSettingsFormat_1_4_0, parseAnnotation_1_4_0, PluginsAnnotationsSettings_1_5_0, PluginAnnotationDict_1_5_0, isPluginsAnnotationsSettings_1_5_0, } from 'types_legacy' -import { DEFAULT_SETTINGS_1_3_0, DEFAULT_SETTINGS_1_4_0, DEFAULT_SETTINGS_1_5_0 } from './defaults_legacy'; +import { CommunityPluginInfo, CommunityPluginInfoDict, isPluginAnnotation, isPluginsAnnotationsSettings, PluginAnnotation, PluginAnnotationDict, PluginBackup, PluginsAnnotationsSettings } from './types'; +import { PluginAnnotationDict_1_4_0, PluginsAnnotationsSettings_1_4_0, PluginsAnnotationsSettings_1_3_0, isPluginAnnotationDictFormat_1_3_0, isSettingsFormat_1_3_0, isSettingsFormat_1_4_0, parseAnnotation_1_4_0, PluginsAnnotationsSettings_1_5_0, PluginAnnotationDict_1_5_0, isPluginsAnnotationsSettings_1_5_0, PluginsAnnotationsSettings_1_6_0, isPluginsAnnotationsSettings_1_6_0, } from 'types_legacy' +import { DEFAULT_SETTINGS_1_3_0, DEFAULT_SETTINGS_1_4_0, DEFAULT_SETTINGS_1_5_0, DEFAULT_SETTINGS_1_6_0 } from './defaults_legacy'; import { DEFAULT_SETTINGS } from 'defaults'; import { PluginsAnnotationsSettingTab } from 'settings_tab' import * as path from 'path'; import { readAnnotationsFromMdFile, writeAnnotationsToMdFile } from 'manageAnnotations'; import { backupSettings, debounceFactoryWithWaitMechanism, delay, sortAnnotations } from 'utils'; -import { annotationControl } from 'annotation_control'; +import { AnnotationControl } from 'annotation_control'; +import { svg_locked, svg_unlocked, svg_github_dark, svg_github_light } from "graphics"; + export default class PluginsAnnotations extends Plugin { settings: PluginsAnnotationsSettings = structuredClone(DEFAULT_SETTINGS); pluginNameToIdMap: Record = {}; pluginIdToNameMap: Record = {}; sortedPluginIds: string[] = []; - svg_unlocked ='\ - \ - \ - '; - svg_locked ='\ - \ - \ - '; - - svg_github_light=atob('PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgY2xhc3M9InN2Zy1pY29uIGx1Y2lkZS1zZXR0aW5ncyI+CiAgPGRlZnM+CiAgICA8c3R5bGU+CiAgICAgIC5jbHMtMSB7CiAgICAgICAgZmlsbDogIzI0MjkyZjsKICAgICAgICBmaWxsLXJ1bGU6IGV2ZW5vZGQ7CiAgICAgICAgc3Ryb2tlLXdpZHRoOiAwcHg7CiAgICAgIH0KICAgIDwvc3R5bGU+CiAgPC9kZWZzPgogIDxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTEyLC4zQzUuNC4zLjEsNS43LjEsMTIuM3MzLjQsOS44LDguMSwxMS40Yy42LjEuOC0uMy44LS42czAtMS4yLDAtMi4yYy0zLjMuNy00LTEuNC00LTEuNC0uNS0xLjQtMS4zLTEuNy0xLjMtMS43LTEuMS0uNywwLS43LDAtLjcsMS4yLDAsMS44LDEuMiwxLjgsMS4yLDEuMSwxLjgsMi44LDEuMywzLjUsMSwwLS44LjQtMS4zLjctMS42LTIuNi0uMy01LjQtMS4zLTUuNC01LjlzLjUtMi40LDEuMi0zLjJjLS4xLS4zLS41LTEuNS4xLTMuMiwwLDAsMS0uMywzLjMsMS4yLDEtLjMsMi0uNCwzLS40LDEsMCwyLC4xLDMsLjQsMi4zLTEuNSwzLjMtMS4yLDMuMy0xLjIuNywxLjYuMiwyLjkuMSwzLjIuOC44LDEuMiwxLjksMS4yLDMuMiwwLDQuNi0yLjgsNS42LTUuNCw1LjkuNC40LjgsMS4xLjgsMi4yLDAsMS42LDAsMi45LDAsMy4zcy4yLjcuOC42YzQuNy0xLjYsOC4xLTYuMSw4LjEtMTEuNCwwLTYuNi01LjMtMTItMTEuOS0xMloiLz4KPC9zdmc+'); - svg_github_dark=atob('PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgY2xhc3M9InN2Zy1pY29uIGx1Y2lkZS1zZXR0aW5ncyI+CiAgPGRlZnM+CiAgICA8c3R5bGU+CiAgICAgIC5jbHMtMSB7CiAgICAgICAgZmlsbDogI2ZmZjsKICAgICAgICBmaWxsLXJ1bGU6IGV2ZW5vZGQ7CiAgICAgICAgc3Ryb2tlLXdpZHRoOiAwcHg7CiAgICAgIH0KICAgIDwvc3R5bGU+CiAgPC9kZWZzPgogIDxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTEyLC4zQzUuNC4zLjEsNS43LjEsMTIuM3MzLjQsOS44LDguMSwxMS40Yy42LjEuOC0uMy44LS42czAtMS4yLDAtMi4yYy0zLjMuNy00LTEuNC00LTEuNC0uNS0xLjQtMS4zLTEuNy0xLjMtMS43LTEuMS0uNywwLS43LDAtLjcsMS4yLDAsMS44LDEuMiwxLjgsMS4yLDEuMSwxLjgsMi44LDEuMywzLjUsMSwwLS44LjQtMS4zLjctMS42LTIuNi0uMy01LjQtMS4zLTUuNC01LjlzLjUtMi40LDEuMi0zLjJjLS4xLS4zLS41LTEuNS4xLTMuMiwwLDAsMS0uMywzLjMsMS4yLDEtLjMsMi0uNCwzLS40LDEsMCwyLC4xLDMsLjQsMi4zLTEuNSwzLjMtMS4yLDMuMy0xLjIuNiwxLjYuMiwyLjkuMSwzLjIuOC44LDEuMiwxLjksMS4yLDMuMiwwLDQuNi0yLjgsNS42LTUuNCw1LjkuNC40LjgsMS4xLjgsMi4yLDAsMS42LDAsMi45LDAsMy4zcy4yLjcuOC42YzQuNy0xLjYsOC4xLTYuMSw4LjEtMTEuNCwwLTYuNi01LjMtMTItMTEuOS0xMloiLz4KPC9zdmc+'); - private lockIcon: HTMLDivElement | null = null; private mutationObserver: MutationObserver | null = null; @@ -55,6 +43,11 @@ export default class PluginsAnnotations extends Plugin { private savePromise: Promise | null = null; private resolveSavePromise: (() => void) | null = null; + private community_plugins = {} as CommunityPluginInfoDict; + + private handleThemeChange: ((event: MediaQueryListEvent) => void) | null = null; + private colorSchemeMedia: MediaQueryList | null = null; + // Declare class methods that will be initialized in the constructor debouncedSaveAnnotations: (callback?: () => void) => void; waitForSaveToComplete: () => Promise; @@ -69,7 +62,6 @@ export default class PluginsAnnotations extends Plugin { async (callback: () => void = (): void => {}) => { await this.saveSettings(); if(callback) callback(); - console.log("FINISHED SAVING ANNOTATIONS"); }, timeout_debounced_saving_ms); this.debouncedSaveAnnotations = debouncedFct; this.waitForSaveToComplete = waitFnc; @@ -77,8 +69,6 @@ export default class PluginsAnnotations extends Plugin { async onload() { - // console.clear(); - // console.log('Loading Plugins Annotations'); // Add settings tab. It avoids loading the setting at this stage @@ -101,6 +91,14 @@ export default class PluginsAnnotations extends Plugin { } } }); + + this.app.workspace.onLayoutReady(async () => { + // Wait for all plugins to be loaded + this.colorSchemeMedia = matchMedia('(prefers-color-scheme: dark)'); + }); + + // Call this function in your plugin initialization or where appropriate + this.loadCommunityPluginsJson(); } /* Load settings for different versions */ @@ -114,7 +112,17 @@ export default class PluginsAnnotations extends Plugin { // Nested function to handle different versions of settings const getSettingsFromData = async (data: unknown): Promise => { if (isPluginsAnnotationsSettings(data)) { - const settings: PluginsAnnotationsSettings = data; + return data as PluginsAnnotationsSettings; + } else if (isPluginsAnnotationsSettings_1_6_0(data)) { + // Make a backup + await backupSettings('Settings before upgrade from 1.5 to 1.6',data,importBackups); + await delay(10); // add a small delay to shift the timestamp of the backup + + const settings: PluginsAnnotationsSettings = { + ...data, + "compatibility":"1.7.0", + show_github_icons:DEFAULT_SETTINGS.show_github_icons + }; return settings as PluginsAnnotationsSettings; } else if (isPluginsAnnotationsSettings_1_5_0(data)) { // Make a backup @@ -132,8 +140,8 @@ export default class PluginsAnnotations extends Plugin { const oldSettings: PluginsAnnotationsSettings_1_5_0 = data; // Update the data with the new format - const default_new_settings = DEFAULT_SETTINGS; - const newSettings: PluginsAnnotationsSettings = { + const default_new_settings = DEFAULT_SETTINGS_1_6_0; + const newSettings: PluginsAnnotationsSettings_1_6_0 = { ...oldSettings, annotations: upgradedAnnotations, plugins_annotations_uuid: default_new_settings.plugins_annotations_uuid, @@ -393,8 +401,48 @@ export default class PluginsAnnotations extends Plugin { this.sortedPluginIds = sortAnnotations(this.settings.annotations); } + listenForThemeChange(tabContainer: HTMLElement) { + const pluginsContainer = tabContainer.querySelector('.installed-plugins-container'); + + // Remove previous event listener if it exists + if (this.handleThemeChange) { + if (this.colorSchemeMedia) { + this.colorSchemeMedia.removeEventListener("change", this.handleThemeChange); + } + } + + // Create the event listener with the correct signature + this.handleThemeChange = (event: MediaQueryListEvent): void => { + const isDarkMode = event.matches; // true means dark mode is active + + if (pluginsContainer) { + const githubIcons = pluginsContainer.querySelectorAll( + 'div.setting-item > div.setting-item-control > div.github-icon' + ); + + // Iterate over each github icon and set the appropriate SVG + githubIcons.forEach((icon) => { + if (isDarkMode) { + icon.innerHTML = svg_github_dark; + } else { + icon.innerHTML = svg_github_light; + } + }); + } + } + + // Add an event listener for changes to the appearance mode + if (this.colorSchemeMedia) { + this.colorSchemeMedia.addEventListener("change", this.handleThemeChange); + } + } + async observeTab(tab: SettingTab) { + + // just in case, remove previous observers if there are any + this.disconnectObservers(); + // eslint-disable-next-line @typescript-eslint/no-this-alias const self = this; @@ -437,13 +485,16 @@ export default class PluginsAnnotations extends Plugin { // there could be changes in the settings due to synchronization among devices // which only happens after the plugin is loaded await this.loadSettings(); - + await this.loadCommunityPluginsJson(); + this.addIcon(tab); this.addAnnotations(tab); }); observer.observe(tab.containerEl, { childList: true, subtree: false }); this.mutationObserver = observer; + + this.listenForThemeChange(tab.containerEl); } // force reload - this is convenient because since the loading of the plugin @@ -479,10 +530,10 @@ export default class PluginsAnnotations extends Plugin { if(this.settings.editable) { lockIcon.setAttribute('aria-label', 'Click to lock personal annotations'); - lockIcon.innerHTML = this.svg_unlocked; + lockIcon.innerHTML = svg_unlocked; } else { lockIcon.setAttribute('aria-label', 'Click to be able to edit personal annotations'); - lockIcon.innerHTML = this.svg_locked; + lockIcon.innerHTML = svg_locked; } lockIcon.addEventListener('click', (event:MouseEvent) => { @@ -491,10 +542,10 @@ export default class PluginsAnnotations extends Plugin { this.debouncedSaveAnnotations(); if(this.settings.editable) { lockIcon.setAttribute('aria-label', 'Click to lock personal annotations'); - lockIcon.innerHTML = this.svg_unlocked; + lockIcon.innerHTML = svg_unlocked; } else { lockIcon.setAttribute('aria-label', 'Click to unlock personal annotations'); - lockIcon.innerHTML = this.svg_locked; + lockIcon.innerHTML = svg_locked; } const plugins = tab.containerEl.querySelectorAll('.plugin-comment-annotation'); @@ -526,7 +577,6 @@ export default class PluginsAnnotations extends Plugin { // Remove the 'plugin-comment-placeholder' class el.classList.remove('plugin-comment-placeholder'); } - }); }); @@ -541,11 +591,38 @@ export default class PluginsAnnotations extends Plugin { } } + async loadCommunityPluginsJson() { + const url = 'https://raw.githubusercontent.com/obsidianmd/obsidian-releases/master/community-plugins.json'; + + try { + // Fetch the JSON data from the URL + const response = await fetch(url); + + // Check if the response is OK (status code 200-299) + if (!response.ok) { + // throw new Error(`Failed to fetch: ${response.status} ${response.statusText}`); + return; + } + + // Parse the JSON data + const pluginsData = (await response.json()) as CommunityPluginInfo[]; + + this.community_plugins = pluginsData.reduce((acc:CommunityPluginInfoDict, plugin:CommunityPluginInfo) => { + acc[plugin.id] = plugin; + return acc; + }, {} as CommunityPluginInfoDict); + + } catch (error) { + console.error('Error loading community plugins JSON:', error); + } + } + addAnnotation(pluginDOMElement: Element) { const pluginNameDiv = pluginDOMElement.querySelector('.setting-item-name'); const pluginName = pluginNameDiv ? pluginNameDiv.textContent : null; if (!pluginName) { + console.log(pluginNameDiv); console.warn('Plugin name not found'); return; } @@ -556,8 +633,6 @@ export default class PluginsAnnotations extends Plugin { return; } - const manifest = this.app.plugins.manifests[pluginId]; - const settingItemInfo = pluginDOMElement.querySelector('.setting-item-info'); if (settingItemInfo) { const descriptionDiv = settingItemInfo.querySelector('.setting-item-description'); @@ -567,29 +642,24 @@ export default class PluginsAnnotations extends Plugin { const annotation_container = document.createElement('div'); annotation_container.className = 'plugin-comment'; - new annotationControl(this,annotation_container,pluginId,pluginName); + const annotationControl = new AnnotationControl(this,annotation_container,pluginId,pluginName); descriptionDiv.appendChild(annotation_container); - } - } - } - const controlDiv = pluginDOMElement.querySelector('.setting-item-control'); - if (controlDiv) { - const GitHubDiv = document.createElement('div'); - GitHubDiv.classList.add('clickable-icon', 'extra-setting-button'); - GitHubDiv.setAttribute('aria-label', 'Open plugin\'s GitHub page'); - GitHubDiv.innerHTML = this.svg_github_light; - // Get all elements with the class .clickable-icon inside controlDiv - const clickableIcons = controlDiv.querySelectorAll('.clickable-icon'); - console.log(manifest); - // Insert the new icon as the second last of all clickable icons - if (clickableIcons.length > 0) { - const lastIcon = clickableIcons[clickableIcons.length - 1]; - controlDiv.insertBefore(GitHubDiv, lastIcon); - } else { - // If no clickable icons are found, append it as the first child - controlDiv.insertBefore(GitHubDiv, controlDiv.firstChild); + 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) { + annotationControl.addGitHubIcon(controlDiv,repo, this.colorSchemeMedia.matches); + } + } + } + } + } } } } diff --git a/src/settings_tab.ts b/src/settings_tab.ts index f93c8c4..6a7660d 100644 --- a/src/settings_tab.ts +++ b/src/settings_tab.ts @@ -6,7 +6,9 @@ import { App, normalizePath, Notice, Platform, PluginSettingTab, Setting, TextCo import { PluginAnnotationDict } from "types"; import { parseFilePath, FileSuggestion, downloadJson, showConfirmationDialog, backupSettings, sortAnnotations } from "utils"; import { DEFAULT_SETTINGS } from 'defaults'; -import { annotationControl } from "annotation_control"; +import { AnnotationControl } from "annotation_control"; + +import { svg_locked, svg_unlocked } from "graphics"; declare const moment: typeof import('moment'); @@ -110,13 +112,13 @@ export class PluginsAnnotationsSettingTab extends PluginSettingTab { div.classList.add('plugin-comment-icon-container') const unlock_icon = document.createElement('div'); unlock_icon.classList.add('clickable-icon'); - unlock_icon.innerHTML = this.plugin.svg_unlocked; + unlock_icon.innerHTML = svg_unlocked; unlock_icon.addEventListener('click', (event:MouseEvent) => { editable_toggle.setValue(true); }); const lock_icon = document.createElement('div'); lock_icon.classList.add('clickable-icon'); - lock_icon.innerHTML = this.plugin.svg_locked; + lock_icon.innerHTML = svg_locked; lock_icon.addEventListener('click', (event:MouseEvent) => { editable_toggle.setValue(false); }); @@ -446,6 +448,32 @@ export class PluginsAnnotationsSettingTab extends PluginSettingTab { }); }); + const show_github_icons_setting = new Setting(containerEl) + .setName('Show GitHub links') + .setDesc("If this option is enabled, a clickable icon linking to the plugin's GitHub page will be displayed."); + + let show_github_icons_toggle: ToggleComponent; + show_github_icons_setting.addToggle(toggle => { + show_github_icons_toggle = toggle; + toggle + .setValue(this.plugin.settings.show_github_icons) + .onChange(async (value: boolean) => { + this.plugin.settings.show_github_icons = value; + this.plugin.debouncedSaveAnnotations(); + }); + }); + + show_github_icons_setting.addExtraButton((button) => { + button + .setIcon("reset") + .setTooltip("Reset to default value") + .onClick(() => { + const value = DEFAULT_SETTINGS.show_github_icons + show_github_icons_toggle.setValue(value); + }); + }); + + /* ====== Backups ====== */ this.backupManager = new BackupManager(this.plugin, containerEl); @@ -722,7 +750,7 @@ class UninstalledPluginsManager { pluginSetting.descEl.dataset.plugin=pluginId; // Render the annotation - new annotationControl(this.plugin,pluginSetting.descEl,pluginId,this.uninstalledPlugins[pluginId].name); + new AnnotationControl(this.plugin,pluginSetting.descEl,pluginId,this.uninstalledPlugins[pluginId].name); // Set the attributes by applying the correct classes pluginSetting.descEl.classList.add('plugin-comment-annotation'); @@ -737,7 +765,7 @@ class UninstalledPluginsManager { const pluginId = descEl.dataset.plugin; if(pluginId) { descEl.innerHTML = ''; - new annotationControl(this.plugin,descEl,pluginId,this.uninstalledPlugins[pluginId].name); + new AnnotationControl(this.plugin,descEl,pluginId,this.uninstalledPlugins[pluginId].name); } } }); diff --git a/src/types.ts b/src/types.ts index 52d6ff9..5de578b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -28,7 +28,8 @@ export interface PluginsAnnotationsSettings { editable: boolean; automatic_remove: boolean; markdown_file_path: string; - compatibility: string; + show_github_icons: boolean; + compatibility: "1.7.0"; backups: PluginBackup[]; } @@ -37,11 +38,12 @@ export function isPluginsAnnotationsSettings(s:unknown): s is PluginsAnnotations return false; } return 'annotations' in s - && 'compatibility' in s && (s as PluginsAnnotationsSettings).compatibility === '1.6.0' + && 'compatibility' in s && (s as PluginsAnnotationsSettings).compatibility === '1.7.0' && 'plugins_annotations_uuid' in s && (s as PluginsAnnotationsSettings).plugins_annotations_uuid === DEFAULT_SETTINGS.plugins_annotations_uuid; } + export function isPluginAnnotation(anno:unknown): anno is PluginAnnotation { if (typeof anno !== 'object' || anno === null) { return false; @@ -61,3 +63,13 @@ export interface ParsedPath { ext: string, path: string } + +export type CommunityPluginInfoDict = {[key:string]:CommunityPluginInfo}; + +export interface CommunityPluginInfo { + author: string, + description: string, + id: string, + name: string, + repo: string, +} diff --git a/src/types_legacy.ts b/src/types_legacy.ts index 873e452..1625d3d 100644 --- a/src/types_legacy.ts +++ b/src/types_legacy.ts @@ -1,7 +1,34 @@ // types_legacy.ts -import { DEFAULT_SETTINGS_1_3_0, DEFAULT_SETTINGS_1_4_0, DEFAULT_SETTINGS_1_5_0 } from "defaults_legacy"; -import { PluginAnnotation, PluginsAnnotationsSettings } from "types"; +import { DEFAULT_SETTINGS_1_3_0, DEFAULT_SETTINGS_1_4_0, DEFAULT_SETTINGS_1_5_0, DEFAULT_SETTINGS_1_6_0 } from "defaults_legacy"; +import { PluginAnnotation, PluginAnnotationDict, PluginBackup } from "types"; + +/* VERSION 1.6 */ + +export interface PluginsAnnotationsSettings_1_6_0 { + annotations: PluginAnnotationDict; + plugins_annotations_uuid: string; + hide_placeholders: boolean; + delete_placeholder_string_on_insertion: boolean; + label_mobile: string; + label_desktop: string; + label_placeholder: string; + editable: boolean; + automatic_remove: boolean; + markdown_file_path: string; + compatibility: "1.6.0"; + backups: PluginBackup[]; +} + +export function isPluginsAnnotationsSettings_1_6_0(s:unknown): s is PluginsAnnotationsSettings_1_6_0 { + if (typeof s !== 'object' || s === null) { + return false; + } + return 'annotations' in s + && 'compatibility' in s && (s as PluginsAnnotationsSettings_1_6_0).compatibility === '1.6.0' + && 'plugins_annotations_uuid' in s + && (s as PluginsAnnotationsSettings_1_6_0).plugins_annotations_uuid === DEFAULT_SETTINGS_1_6_0.plugins_annotations_uuid; +} /* VERSION 1.5 */ @@ -13,8 +40,9 @@ export interface PluginAnnotationDict_1_5_0 { [pluginId: string]: PluginAnnotation_1_5_0; } -export interface PluginsAnnotationsSettings_1_5_0 extends Omit{ +export interface PluginsAnnotationsSettings_1_5_0 extends Omit{ annotations: PluginAnnotationDict_1_5_0; + compatibility: "1.5.0"; } export function isPluginsAnnotationsSettings_1_5_0(s:unknown): s is PluginsAnnotationsSettings_1_5_0 {