mirror of
https://github.com/alberti42/obsidian-plugins-annotations.git
synced 2026-07-22 10:10:24 +00:00
Compare commits
19 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
565e5cb37b | ||
|
|
6ba7c001be | ||
|
|
4c77b281b6 | ||
|
|
5e72862016 | ||
|
|
4acac3602b | ||
|
|
d6c6c468ad | ||
|
|
a2c0bc8a1e | ||
|
|
83e84bfa15 | ||
|
|
6ffeebb771 | ||
|
|
c076b8175b | ||
|
|
3ba82f30c8 | ||
|
|
4a8f777602 | ||
|
|
4845905453 | ||
|
|
5740615518 | ||
|
|
50f36810d3 | ||
|
|
cdfe973a1f | ||
|
|
6974fd0c3d | ||
|
|
84ecaaeaa2 | ||
|
|
1ef188de8c |
56 changed files with 560 additions and 165 deletions
|
|
@ -1,3 +0,0 @@
|
|||
node_modules/
|
||||
|
||||
main.js
|
||||
50
.github/workflows/release.yml
vendored
Normal file
50
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+.[0-9]+'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- run: npm ci
|
||||
|
||||
- run: npm run build
|
||||
|
||||
- name: Prepare release notes
|
||||
run: |
|
||||
VERSION="${GITHUB_REF_NAME}"
|
||||
NOTES="release-notes/release-${VERSION}.md"
|
||||
if [ ! -f "$NOTES" ]; then
|
||||
echo "No release notes provided for version ${VERSION}." > "$NOTES"
|
||||
fi
|
||||
PREV_TAG=$(git tag --sort=-version:refname | grep -A1 "^${VERSION}$" | tail -1)
|
||||
if [ -n "$PREV_TAG" ] && [ "$PREV_TAG" != "$VERSION" ]; then
|
||||
echo "" >> "$NOTES"
|
||||
echo "**Full changelog**: https://github.com/${{ github.repository }}/compare/${PREV_TAG}...${VERSION}" >> "$NOTES"
|
||||
fi
|
||||
|
||||
- uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
name: ${{ github.ref_name }}
|
||||
body_path: release-notes/release-${{ github.ref_name }}.md
|
||||
make_latest: true
|
||||
draft: true
|
||||
prerelease: false
|
||||
files: |
|
||||
dist/main.js
|
||||
dist/manifest.json
|
||||
dist/styles.css
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -18,3 +18,9 @@ data.json
|
|||
|
||||
# Exclude macOS Finder (System Explorer) View States
|
||||
.DS_Store
|
||||
|
||||
# iCLoud
|
||||
*.nosync
|
||||
|
||||
# Sublime
|
||||
*sublime-workspace
|
||||
|
|
|
|||
3
.npmrc
3
.npmrc
|
|
@ -1 +1,2 @@
|
|||
tag-version-prefix=""
|
||||
tag-version-prefix=""
|
||||
message=build: version bump to %s
|
||||
43
eslint.config.js
Normal file
43
eslint.config.js
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
node: true, // Add this line to define Node.js global variables like `module`
|
||||
es6: true
|
||||
},
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended'
|
||||
],
|
||||
ignorePatterns: ['node_modules', 'dist', 'coverage', '**/*.d.ts', 'tests'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
sourceType: 'module'
|
||||
},
|
||||
plugins: ['@typescript-eslint'],
|
||||
rules: {
|
||||
'@typescript-eslint/naming-convention': [
|
||||
'error',
|
||||
{
|
||||
selector: 'interface',
|
||||
format: ['PascalCase'],
|
||||
custom: {
|
||||
regex: '^I[A-Z]',
|
||||
match: true
|
||||
}
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-unused-vars': ['warn', { args: 'none' }],
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-namespace': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/quotes': [
|
||||
'error',
|
||||
'single',
|
||||
{ avoidEscape: true, allowTemplateLiterals: false }
|
||||
],
|
||||
curly: ['error', 'all'],
|
||||
eqeqeq: 'error',
|
||||
'prefer-arrow-callback': 'error'
|
||||
}
|
||||
};
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"id": "plugins-annotations",
|
||||
"name": "Plugins Annotations",
|
||||
"version": "1.7.7",
|
||||
"minAppVersion": "1.5.0",
|
||||
"version": "1.7.12",
|
||||
"minAppVersion": "1.13.0",
|
||||
"description": "Allows adding personal comments to each installed plugin.",
|
||||
"author": "Andrea Alberti",
|
||||
"authorUrl": "https://www.linkedin.com/in/dr-andrea-alberti/",
|
||||
"fundingUrl": "https://buymeacoffee.com/alberti",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
}
|
||||
29
obsidian-plugin-annotations.sublime-project
Normal file
29
obsidian-plugin-annotations.sublime-project
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
11
package-lock.json
generated
11
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "plugins-annotations",
|
||||
"version": "1.0.0",
|
||||
"version": "1.7.12",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "plugins-annotations",
|
||||
"version": "1.0.0",
|
||||
"version": "1.7.12",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"monkey-around": "^3.0.0"
|
||||
|
|
@ -1871,10 +1871,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "plugins-annotations",
|
||||
"version": "1.0.0",
|
||||
"version": "1.7.12",
|
||||
"description": "Allows adding personal comments to each installed plugin.",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
1
release-notes/release-1.0.0.md
Normal file
1
release-notes/release-1.0.0.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
1
release-notes/release-1.0.1.md
Normal file
1
release-notes/release-1.0.1.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
1
release-notes/release-1.0.10.md
Normal file
1
release-notes/release-1.0.10.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
1
release-notes/release-1.0.11.md
Normal file
1
release-notes/release-1.0.11.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
12
release-notes/release-1.0.12.md
Normal file
12
release-notes/release-1.0.12.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Changelog
|
||||
|
||||
## [1.0.12] - 2024-06-23
|
||||
|
||||
### Bug fixes
|
||||
- **Mobile Optimization**:
|
||||
- Resolved the issue where the caret and keyboard were not shown when clicking on placeholder text in mobile devices.
|
||||
- Clicking on a placeholder text now selects the entire text, allowing users to easily start typing.
|
||||
|
||||
### Bug Fixes
|
||||
- **Annotation Deletion**:
|
||||
- Fixed an issue where annotations were not correctly deleted when the text was removed.
|
||||
1
release-notes/release-1.0.2.md
Normal file
1
release-notes/release-1.0.2.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
1
release-notes/release-1.0.3.md
Normal file
1
release-notes/release-1.0.3.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
1
release-notes/release-1.0.4.md
Normal file
1
release-notes/release-1.0.4.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
1
release-notes/release-1.0.5.md
Normal file
1
release-notes/release-1.0.5.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
1
release-notes/release-1.0.6.md
Normal file
1
release-notes/release-1.0.6.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
1
release-notes/release-1.0.7.md
Normal file
1
release-notes/release-1.0.7.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
1
release-notes/release-1.0.8.md
Normal file
1
release-notes/release-1.0.8.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
1
release-notes/release-1.0.9.md
Normal file
1
release-notes/release-1.0.9.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
7
release-notes/release-1.1.0.md
Normal file
7
release-notes/release-1.1.0.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Changelog version 1.1.0
|
||||
|
||||
## Added features
|
||||
|
||||
- Setting panels for configuring plugin annotations.
|
||||
- Option to hide annotations when they are empty, improving the cleanliness of the interface.
|
||||
- Enhanced the user experience by allowing the removal of annotations for uninstalled plugins from memory.
|
||||
1
release-notes/release-1.1.1.md
Normal file
1
release-notes/release-1.1.1.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Small fix correcting a typo.
|
||||
5
release-notes/release-1.2.0.md
Normal file
5
release-notes/release-1.2.0.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Change log
|
||||
|
||||
## Added:
|
||||
|
||||
- Editing notes as simple text, Markdown, and HTML.
|
||||
5
release-notes/release-1.2.1.md
Normal file
5
release-notes/release-1.2.1.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Change log
|
||||
|
||||
## Fixes
|
||||
|
||||
- Trim annotations to remove empty lines and spaces. If the user wants to enforce empty spaces, HTML-formatted code can be used.
|
||||
5
release-notes/release-1.3.0.md
Normal file
5
release-notes/release-1.3.0.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Change log
|
||||
|
||||
## New features
|
||||
|
||||
- Added a lock button to lock annotations and prevent empty annotation fields from showing up when hovering over with the mouse.
|
||||
5
release-notes/release-1.3.2.md
Normal file
5
release-notes/release-1.3.2.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Change log
|
||||
|
||||
# New minor feature
|
||||
|
||||
- Added customization of the placeholder label appearing when no user annotation is provided. It supports HTML-formatted text.
|
||||
14
release-notes/release-1.4.0.md
Normal file
14
release-notes/release-1.4.0.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Changelog for Version 1.4.0
|
||||
|
||||
## New Features
|
||||
- Added an option to automatically remove annotations when a plugin is uninstalled.
|
||||
- Displaying the names of uninstalled plugins, allowing users to manually remove stored annotations if desired.
|
||||
|
||||
## Bug Fixes
|
||||
- Fixed an issue where personal annotations were not displayed when filtering installed plugins using the search field.
|
||||
|
||||
## Under the Hood Improvements
|
||||
- Enhanced data format to store the names of plugins.
|
||||
- Refactored code for better performance and maintainability.
|
||||
|
||||
This release includes significant improvements, bug fixes, and new features to enhance your experience with the plugin. Enjoy the updates!
|
||||
12
release-notes/release-1.5.1.md
Normal file
12
release-notes/release-1.5.1.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Change log
|
||||
|
||||
## New features
|
||||
|
||||
- Possible to provide a Markdown file (for example in `00 Meta/Misc/Plugins annotations.md`), where the annotations are stored. This allows editing the annotations directly from the Obsidian editor. Most importantly, it allows one to use Obsidian links and having the links automatically updated when the linked files in the vault are renamed.
|
||||
- Added to the preference panes a place to manage backups of the annotations.
|
||||
- Added possibility to export and import annotations as json file.
|
||||
- Many other improvements under the hood and refactored the code.
|
||||
|
||||
## Changes
|
||||
|
||||
- The default type for the plugin annotations has now changed from `text` to `markdown`.
|
||||
12
release-notes/release-1.5.2.md
Normal file
12
release-notes/release-1.5.2.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Change log
|
||||
|
||||
## New features
|
||||
|
||||
- Possible to provide a Markdown file (for example in `00 Meta/Misc/Plugins annotations.md`), where the annotations are stored. This allows editing the annotations directly from the Obsidian editor. Most importantly, it allows one to use Obsidian links and having the links automatically updated when the linked files in the vault are renamed.
|
||||
- Added to the preference panes a place to manage backups of the annotations.
|
||||
- Added possibility to export and import annotations as json file.
|
||||
- Many other improvements under the hood and refactored the code.
|
||||
|
||||
## Changes
|
||||
|
||||
- The default type for the plugin annotations has now changed from `text` to `markdown`.
|
||||
12
release-notes/release-1.5.3.md
Normal file
12
release-notes/release-1.5.3.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Change log
|
||||
|
||||
## New features
|
||||
|
||||
- Possible to provide a Markdown file (for example in `00 Meta/Misc/Plugins annotations.md`), where the annotations are stored. This allows editing the annotations directly from the Obsidian editor. Most importantly, it allows one to use Obsidian links and having the links automatically updated when the linked files in the vault are renamed.
|
||||
- Added to the preference panes a place to manage backups of the annotations.
|
||||
- Added possibility to export and import annotations as json file.
|
||||
- Many other improvements under the hood and refactored the code.
|
||||
|
||||
## Changes
|
||||
|
||||
- The default type for the plugin annotations has now changed from `text` to `markdown`.
|
||||
6
release-notes/release-1.5.4.md
Normal file
6
release-notes/release-1.5.4.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Change log
|
||||
|
||||
## Minor improvements
|
||||
|
||||
- Improved explanation of backup functionality in the preference pane
|
||||
- Improve visualization of the preference pane for mobile devices (optimized for with small screens).
|
||||
16
release-notes/release-1.6.0.md
Normal file
16
release-notes/release-1.6.0.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Change log
|
||||
|
||||
## New features
|
||||
|
||||
- Added backup of annotations
|
||||
|
||||
## Bugs and improvements
|
||||
|
||||
- Corrected a bug where the links were not clickable if the notes were not editable.
|
||||
- Corrected a bug where the lock icon was not correctly displayed the first time the pane was opened.
|
||||
- Refactoring the code, which improves clarity and allows more extensions in the future.
|
||||
- Improved explanations in the preference pane.
|
||||
|
||||
## Removed features
|
||||
|
||||
- HTML and text annotations have been discontinued. It was too much of an effort to maintain them, while there is no advantage over using Markdown. You can still use HTML in Markdown.
|
||||
17
release-notes/release-1.6.1.md
Normal file
17
release-notes/release-1.6.1.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Change log
|
||||
|
||||
## New features
|
||||
|
||||
- Added backup of annotations
|
||||
- Notes exported to the markdown file are sorted out alphabetically.
|
||||
|
||||
## Bugs and improvements
|
||||
|
||||
- Corrected a bug where the links were not clickable if the notes were not editable.
|
||||
- Corrected a bug where the lock icon was not correctly displayed the first time the pane was opened.
|
||||
- Refactoring the code, which improves clarity and allows more extensions in the future.
|
||||
- Improved explanations in the preference pane.
|
||||
|
||||
## Removed features
|
||||
|
||||
- HTML and text annotations have been discontinued. It was too much of an effort to maintain them, while there is no advantage over using Markdown. You can still use HTML in Markdown.
|
||||
11
release-notes/release-1.6.2.md
Normal file
11
release-notes/release-1.6.2.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Change log
|
||||
|
||||
## New features
|
||||
|
||||
- Added restore buttons to the settings for default configurations
|
||||
- Added the possibility to edit annotations of uninstalled plugins in the preference pane.
|
||||
- Improved explanation in the preference pane.
|
||||
|
||||
## Bug fixes
|
||||
|
||||
- Many bugs corrected (still testing import / export / backup functions).
|
||||
6
release-notes/release-1.6.3.md
Normal file
6
release-notes/release-1.6.3.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Change log
|
||||
|
||||
## Bug fixes and performance improvements
|
||||
|
||||
- Improved debounced saving functions.
|
||||
- Fixed a bug when switching between the preference pane and the community plugins pane, where the latest changes in the settings were lost.
|
||||
5
release-notes/release-1.6.4.md
Normal file
5
release-notes/release-1.6.4.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Change log
|
||||
|
||||
## Bug fixes
|
||||
|
||||
- Fixed bug where buttons resetting to default conditions did not properly save the new setting.
|
||||
6
release-notes/release-1.6.5.md
Normal file
6
release-notes/release-1.6.5.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Change log
|
||||
|
||||
## Bug fixes
|
||||
|
||||
- When disabling the plugin, the lock icon is now removed.
|
||||
- This should fix the bug of multiple lock icons being created when the plugin `Divide & Conquer` was used. The plugin disables and re-enables plugins. Every time this plugin was reloaded, a new icon was created because the previous one was never removed.
|
||||
5
release-notes/release-1.6.6.md
Normal file
5
release-notes/release-1.6.6.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Change log
|
||||
|
||||
## Bug fixes
|
||||
|
||||
- Fixed bug where annotations could not be added when some text is provided in the search field to restrict the range of annotations displayed.
|
||||
6
release-notes/release-1.7.0.md
Normal file
6
release-notes/release-1.7.0.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Change log
|
||||
|
||||
## New features
|
||||
|
||||
- Added GitHub icons linking to the plugin's GitHub page. A configuration setting allows one to decide whether to show or not these icons.
|
||||
- Various improvements under the hood.
|
||||
7
release-notes/release-1.7.1.md
Normal file
7
release-notes/release-1.7.1.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Change log
|
||||
|
||||
## New features
|
||||
|
||||
- Added GitHub icons linking to the plugin's GitHub page. A configuration setting allows one to decide whether to show or not these icons.
|
||||
- Various improvements under the hood.
|
||||
- Some fixes resolving minor bugs in 1.7.0
|
||||
5
release-notes/release-1.7.10.md
Normal file
5
release-notes/release-1.7.10.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Change log
|
||||
|
||||
## Bug fix
|
||||
|
||||
- Fix mobile annotation focus and touch handling (refs #24, PR #25)
|
||||
5
release-notes/release-1.7.11.md
Normal file
5
release-notes/release-1.7.11.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Change log
|
||||
|
||||
## Bug fix
|
||||
|
||||
- Correctly identify Obsidian appearance (light/dark theme) for displaying GitHub icon matched to the theme
|
||||
9
release-notes/release-1.7.12.md
Normal file
9
release-notes/release-1.7.12.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# Change log
|
||||
|
||||
## Bug fix
|
||||
|
||||
- Restore personal annotations in the **Community plugins** settings tab. Obsidian 1.13 redesigned that tab, which stopped annotations (and the lock icon and GitHub icons) from appearing. The plugin now hooks into the new settings UI.
|
||||
|
||||
## Compatibility
|
||||
|
||||
- Requires Obsidian **1.13.0** or newer. Users on older Obsidian versions keep receiving 1.7.11.
|
||||
7
release-notes/release-1.7.2.md
Normal file
7
release-notes/release-1.7.2.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Change log
|
||||
|
||||
## New features
|
||||
|
||||
- Added GitHub icons linking to the plugin's GitHub page. A configuration setting allows one to decide whether to show or not these icons.
|
||||
- Various improvements under the hood.
|
||||
- Some fixes resolving minor bugs in 1.7.1
|
||||
7
release-notes/release-1.7.3.md
Normal file
7
release-notes/release-1.7.3.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Change log
|
||||
|
||||
## Bug fixes
|
||||
|
||||
- Fixed bug where a warning was shown when a new plugin was installed.
|
||||
- Fixed small bugs where the code is now more efficient, i.e., avoids repeating unnecessary operations
|
||||
- Corrected a bug where, when enabling this plugin, the personal annotations were not immediately shown in the preference pane. Instead, it required to exit the preference pane and enter it again. Now, this is no longer necessary.
|
||||
5
release-notes/release-1.7.5.md
Normal file
5
release-notes/release-1.7.5.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Change log
|
||||
|
||||
## Bug fixes
|
||||
|
||||
- Fixed a bug that caused a conflict with [Hotkey Helper plugin](https://github.com/pjeby/hotkey-helper) and possibly could have conflicted with other plugins that make use of `openTab` Obsidian function for opening preference panes.
|
||||
5
release-notes/release-1.7.6.md
Normal file
5
release-notes/release-1.7.6.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Change log
|
||||
|
||||
## Bug fixes
|
||||
|
||||
- Streamlined code and remove potential weak spot where `openTab` function was called with an asynchronous mechanism. See discussion on the GitHub [page](https://github.com/alberti42/obsidian-plugins-annotations/issues/14).
|
||||
5
release-notes/release-1.7.7.md
Normal file
5
release-notes/release-1.7.7.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Change log
|
||||
|
||||
## New features
|
||||
|
||||
- Detect changes to `data.json` made by the synchronisation engine (Obsidian or Google Drive or Dropbox etc) or any external editor. If the Community Preferences window is active, the changes will be shown immediately. Similarly, if changes are made using an external editor to the MarkDown file that stores the personal annotations, these changes will be detected and the preferences pane will be updated immediately to reflect the new changes.
|
||||
5
release-notes/release-1.7.8.md
Normal file
5
release-notes/release-1.7.8.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Change log
|
||||
|
||||
## New features
|
||||
|
||||
- Detect changes to data.json which can be caused by the synchronization engine or an external editor and update the setting pane automatically.
|
||||
5
release-notes/release-1.7.9.md
Normal file
5
release-notes/release-1.7.9.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Change log
|
||||
|
||||
## Bug fix
|
||||
|
||||
- Fix community plugins hook for Obsidian 1.11.5 (refs #26)
|
||||
|
|
@ -55,13 +55,20 @@ export class AnnotationControl {
|
|||
}
|
||||
|
||||
addEventListeners() {
|
||||
this.annotation_div.addEventListener('mousedown', (event:MouseEvent) => {
|
||||
if (event.target && (event.target as HTMLElement).tagName === 'A') {
|
||||
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 });
|
||||
|
||||
|
||||
// Prevent click event propagation to parent
|
||||
this.annotation_div.addEventListener('click', (event:MouseEvent) => {
|
||||
|
|
@ -69,6 +76,10 @@ export class AnnotationControl {
|
|||
return;
|
||||
} else {
|
||||
event.stopPropagation();
|
||||
if (!this.clickedLink) {
|
||||
// Explicitly focus to help mobile keyboards appear, especially on Android.
|
||||
this.annotation_div.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
289
src/main.ts
289
src/main.ts
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import {
|
||||
Plugin,
|
||||
Setting,
|
||||
SettingTab,
|
||||
Platform,
|
||||
Plugins,
|
||||
|
|
@ -38,14 +39,14 @@ export default class PluginsAnnotations extends Plugin {
|
|||
|
||||
private community_plugins = {} as CommunityPluginInfoDict;
|
||||
|
||||
private handleThemeChange: ((event: MediaQueryListEvent) => void) | null = null;
|
||||
private colorSchemeMedia: MediaQueryList | null = null;
|
||||
// MutationObserver watching document.body class changes to detect Obsidian theme switches
|
||||
private themeObserver: MutationObserver | null = null;
|
||||
|
||||
// Declare class methods that will be initialized in the constructor
|
||||
debouncedSaveAnnotations: (callback?: () => void) => void;
|
||||
waitForSaveToComplete: () => Promise<void>;
|
||||
|
||||
private communityPluginSettingTabPatched = false;
|
||||
private communityPluginSettingTabPatched = false;
|
||||
|
||||
private listGitHubIcons:HTMLDivElement[] = [];
|
||||
|
||||
|
|
@ -104,25 +105,26 @@ 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();
|
||||
|
||||
// Detect color scheme
|
||||
this.colorSchemeMedia = matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
// Install listener to theme changes
|
||||
if(this.communityPluginTab && this.communityPluginTab.containerEl) this.listenForThemeChange(this.communityPluginTab.containerEl);
|
||||
|
||||
|
|
@ -406,49 +408,32 @@ 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 ) => {
|
||||
// Monkey patch the per-plugin renderer of the community-plugins tab.
|
||||
// Obsidian's declarative settings tab calls `renderInstalledPlugin(setting, manifest)`
|
||||
// for every plugin row, on both the initial render and every later re-render, so this
|
||||
// is the single hook we need for injecting annotations and the lock icon.
|
||||
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, setting: Setting, manifest: PluginManifest): void {
|
||||
// Render the plugin row using Obsidian's own implementation first.
|
||||
next.call(this, setting, manifest);
|
||||
|
||||
// Custom code for personal annotations here
|
||||
if(containerEl && containerEl.lastElementChild) self.addAnnotation(containerEl.lastElementChild);
|
||||
};
|
||||
},
|
||||
// Patch for `render` method
|
||||
render: (next: (
|
||||
isInitialRender: boolean
|
||||
) => void) => {
|
||||
|
||||
return function (this: SettingTab, isInitialRender: boolean): void {
|
||||
self.listGitHubIcons = [];
|
||||
|
||||
// Call the original `render` function
|
||||
next.call(this, isInitialRender);
|
||||
self.addLockIcon(this.containerEl);
|
||||
};
|
||||
}
|
||||
});
|
||||
const settingEl = setting.settingEl;
|
||||
if (settingEl instanceof HTMLElement) {
|
||||
self.addAnnotation(settingEl, manifest.id);
|
||||
// (Re)add the lock icon to the group heading. It is idempotent.
|
||||
self.addLockIcon(this.containerEl);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Register the patch to ensure it gets cleaned up
|
||||
this.register(removeMonkeyPatchForRender);
|
||||
|
|
@ -501,46 +486,44 @@ export default class PluginsAnnotations extends Plugin {
|
|||
}
|
||||
|
||||
listenForThemeChange(tabContainer: HTMLElement) {
|
||||
// If listener is already install, we can directly return
|
||||
if(this.handleThemeChange) return;
|
||||
// If observer is already installed, return immediately to avoid duplicates
|
||||
if (this.themeObserver) return;
|
||||
|
||||
// Check if the color scheme was detected
|
||||
if (this.colorSchemeMedia===null) {
|
||||
console.warn("Color scheme could not be determined.");
|
||||
return;
|
||||
}
|
||||
// Obsidian sets 'theme-dark' or 'theme-light' on document.body; observe class
|
||||
// attribute changes so GitHub icons update whenever the user switches themes,
|
||||
// regardless of the OS-level color scheme.
|
||||
this.themeObserver = new MutationObserver(() => {
|
||||
const isDarkMode = document.body.classList.contains('theme-dark');
|
||||
const githubIcons = tabContainer.querySelectorAll(
|
||||
'div.setting-item > div.setting-item-control > div.github-icon'
|
||||
);
|
||||
// Update each icon to match the new theme
|
||||
githubIcons.forEach((icon) => {
|
||||
icon.innerHTML = isDarkMode ? svg_github_dark : svg_github_light;
|
||||
});
|
||||
});
|
||||
|
||||
// Create the event listener with the correct signature
|
||||
this.handleThemeChange = (event: MediaQueryListEvent): void => {
|
||||
const pluginsContainer = tabContainer.querySelector('.installed-plugins-container');
|
||||
// Watch only the 'class' attribute to minimise observer overhead
|
||||
this.themeObserver.observe(document.body, { attributes: true, attributeFilter: ['class'] });
|
||||
}
|
||||
|
||||
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
|
||||
this.colorSchemeMedia.addEventListener("change", this.handleThemeChange);
|
||||
// Locate the control area of the "Installed plugins" group heading, where the lock
|
||||
// icon lives. This tab renders several group headings, so we target the installed-plugins
|
||||
// group (`.setting-group.mod-list`) specifically rather than the first heading found.
|
||||
private findInstalledPluginsHeadingControl(containerEl: HTMLElement): Element | null {
|
||||
const group = containerEl.querySelector('.setting-group.mod-list');
|
||||
return group ? group.querySelector('.setting-item-heading .setting-item-control') : null;
|
||||
}
|
||||
|
||||
async addLockIcon(containerEl: HTMLElement) {
|
||||
// Add new icon to the existing icons container
|
||||
const headingContainer = containerEl.querySelector('.setting-item-heading .setting-item-control');
|
||||
const headingContainer = this.findInstalledPluginsHeadingControl(containerEl);
|
||||
if (headingContainer) {
|
||||
|
||||
// Idempotent: skip if a lock icon is already present in this heading.
|
||||
if (this.lockIcon && this.lockIcon.isConnected && headingContainer.contains(this.lockIcon)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.lockIcon = document.createElement('div');
|
||||
|
||||
const lockIcon = this.lockIcon;
|
||||
|
|
@ -610,9 +593,18 @@ export default class PluginsAnnotations extends Plugin {
|
|||
}
|
||||
|
||||
removeGitHubIcons() {
|
||||
// Remove any GitHub icons currently in the tab. We query the DOM (rather than rely
|
||||
// solely on the tracked list) so icons re-created by Obsidian's render reconciliation
|
||||
// are also cleaned up.
|
||||
if (this.communityPluginTab) {
|
||||
this.communityPluginTab.containerEl.querySelectorAll('.github-icon').forEach((iconEl:Element) => {
|
||||
iconEl.remove();
|
||||
});
|
||||
}
|
||||
this.listGitHubIcons.forEach((iconEl:Element) => {
|
||||
iconEl.remove();
|
||||
})
|
||||
});
|
||||
this.listGitHubIcons = [];
|
||||
}
|
||||
|
||||
async loadCommunityPluginsJson() {
|
||||
|
|
@ -645,53 +637,74 @@ export default class PluginsAnnotations extends Plugin {
|
|||
}
|
||||
}
|
||||
|
||||
addAnnotation(pluginDOMElement: Element) {
|
||||
const pluginNameDiv = pluginDOMElement.querySelector('.setting-item-name');
|
||||
const pluginName = pluginNameDiv ? pluginNameDiv.textContent : null;
|
||||
// Extract the plugin name from a `.setting-item-name` element. Newer Obsidian appends
|
||||
// version/author <span>s to this element, so we read only the leading text node(s)
|
||||
// rather than `textContent` (which would include the version and author).
|
||||
private extractPluginName(nameEl: Element): string {
|
||||
let text = '';
|
||||
nameEl.childNodes.forEach((node: ChildNode) => {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
text += node.textContent ?? '';
|
||||
}
|
||||
});
|
||||
return text.trim();
|
||||
}
|
||||
|
||||
if (!pluginName) {
|
||||
console.warn('Plugin name not found');
|
||||
return;
|
||||
}
|
||||
addAnnotation(pluginDOMElement: Element, pluginId?: string) {
|
||||
if (!pluginId) {
|
||||
// No id supplied (e.g. bulk re-scan): derive it from the displayed plugin name.
|
||||
const pluginNameDiv = pluginDOMElement.querySelector('.setting-item-name');
|
||||
const pluginName = pluginNameDiv ? this.extractPluginName(pluginNameDiv) : null;
|
||||
|
||||
const pluginId = this.pluginNameToIdMap[pluginName];
|
||||
if (!pluginId) {
|
||||
console.warn(`Plugin ID not found for plugin name: ${pluginName}`);
|
||||
return;
|
||||
}
|
||||
if (!pluginName) {
|
||||
console.warn('Plugin name not found');
|
||||
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';
|
||||
pluginId = this.pluginNameToIdMap[pluginName];
|
||||
}
|
||||
|
||||
if (!pluginId) {
|
||||
console.warn('Plugin ID not found for the plugin row.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the canonical name from the installed manifests (falls back to the id).
|
||||
const pluginName = this.pluginIdToNameMap[pluginId] ?? pluginId;
|
||||
|
||||
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) {
|
||||
{
|
||||
// Read Obsidian's active theme from document.body at render time
|
||||
const isDarkMode = document.body.classList.contains('theme-dark');
|
||||
const gitHubIcon = annotationControl.addGitHubIcon(controlDiv,repo, isDarkMode);
|
||||
if(gitHubIcon) this.listGitHubIcons.push(gitHubIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addAnnotations() {
|
||||
if(this.communityPluginTab===undefined) {
|
||||
|
|
@ -699,13 +712,17 @@ export default class PluginsAnnotations extends Plugin {
|
|||
return;
|
||||
}
|
||||
|
||||
const pluginsContainer = this.communityPluginTab.containerEl.querySelector('.installed-plugins-container');
|
||||
const containerEl = this.communityPluginTab.containerEl;
|
||||
// The installed plugins are rendered inside `.setting-group.mod-list`; its
|
||||
// `.setting-items` element holds the individual plugin rows.
|
||||
const pluginsContainer = containerEl.querySelector('.setting-group.mod-list .setting-items');
|
||||
if (!pluginsContainer) {
|
||||
console.warn("Annotations could not be added because installed-plugins-container was not detected.")
|
||||
console.warn("Annotations could not be added because the installed-plugins list was not detected.")
|
||||
return;
|
||||
}
|
||||
|
||||
const pluginDOMElements = pluginsContainer.querySelectorAll('.setting-item');
|
||||
|
||||
// Only iterate direct plugin rows, skipping the group heading (also a `.setting-item`).
|
||||
const pluginDOMElements = pluginsContainer.querySelectorAll(':scope > .setting-item:not(.setting-item-heading)');
|
||||
pluginDOMElements.forEach(pluginDOMElement => {
|
||||
this.addAnnotation(pluginDOMElement);
|
||||
});
|
||||
|
|
@ -721,11 +738,10 @@ export default class PluginsAnnotations extends Plugin {
|
|||
}
|
||||
|
||||
removeHandleThemeChangeListener() {
|
||||
// Remove listeners
|
||||
if (this.handleThemeChange) {
|
||||
if (this.colorSchemeMedia) {
|
||||
this.colorSchemeMedia.removeEventListener("change", this.handleThemeChange);
|
||||
}
|
||||
// Disconnect and release the MutationObserver used for theme detection
|
||||
if (this.themeObserver) {
|
||||
this.themeObserver.disconnect();
|
||||
this.themeObserver = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -760,4 +776,3 @@ export default class PluginsAnnotations extends Plugin {
|
|||
return uninstalledPlugins;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
14
src/types/obsidian-augment.d.ts
vendored
14
src/types/obsidian-augment.d.ts
vendored
|
|
@ -18,15 +18,9 @@ declare module "obsidian" {
|
|||
id: string;
|
||||
name: string;
|
||||
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;
|
||||
// Per-plugin row renderer of the community-plugins tab (Obsidian >= 1.13):
|
||||
// `setting` is the row's Setting component (exposes `settingEl`), `manifest` its plugin manifest.
|
||||
renderInstalledPlugin(setting: Setting, manifest: PluginManifest): void;
|
||||
}
|
||||
interface Setting {
|
||||
onOpen(): void;
|
||||
|
|
@ -70,4 +64,4 @@ declare module "obsidian" {
|
|||
textInputEl: HTMLInputElement;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
{
|
||||
"1.0.0": "0.15.0"
|
||||
}
|
||||
"1.0.0": "0.15.0",
|
||||
"1.7.10": "1.5.0",
|
||||
"1.7.11": "1.5.0",
|
||||
"1.7.12": "1.13.0"
|
||||
}
|
||||
Loading…
Reference in a new issue