├── .gitattributes ├── .github └── workflows │ └── release-please.yml ├── .gitignore ├── .release-please-manifest.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── esbuild.config.mjs ├── esbuild.publish.mjs ├── icons ├── octicons │ ├── _gen.js │ └── icons.json └── rpg │ └── icons.json ├── manifest.json ├── package-lock.json ├── package.json ├── publish ├── gifs │ ├── all.gif │ ├── collapse.gif │ └── create-an-admonition.gif └── images │ ├── default.png │ ├── msdocs.png │ ├── nested-code.png │ ├── nested.png │ ├── no-title.png │ ├── pf2-note.png │ ├── rendered-title-markdown.png │ ├── title-markdown.png │ └── title.png ├── release-please-config.json ├── src ├── @types │ └── index.d.ts ├── assets │ ├── callout.scss │ ├── main.css │ └── main.scss ├── callout │ └── manager.ts ├── icons │ ├── manager.ts │ └── packs.ts ├── lang │ ├── README.md │ ├── helpers.ts │ └── locale │ │ ├── ar.ts │ │ ├── cz.ts │ │ ├── da.ts │ │ ├── de.ts │ │ ├── en-gb.ts │ │ ├── en.ts │ │ ├── es.ts │ │ ├── fr.ts │ │ ├── hi.ts │ │ ├── id.ts │ │ ├── it.ts │ │ ├── ja.ts │ │ ├── ko.ts │ │ ├── nl.ts │ │ ├── no.ts │ │ ├── pl.ts │ │ ├── pt-br.ts │ │ ├── pt.ts │ │ ├── ro.ts │ │ ├── ru.ts │ │ ├── tr.ts │ │ ├── zh-cn.ts │ │ └── zh-tw.ts ├── main.ts ├── modal │ ├── confirm.ts │ ├── export.ts │ └── index.ts ├── settings.ts ├── styles.css ├── suggest │ └── suggest.ts └── util │ ├── constants.ts │ ├── index.ts │ ├── util.ts │ └── validator.ts ├── svelte.config.js ├── tsconfig.json └── versions.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/release-please.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | 4 | push: 5 | branches: 6 | - "main" 7 | env: 8 | PLUGIN_NAME: obsidian-admonition 9 | 10 | permissions: 11 | contents: write 12 | pull-requests: write 13 | 14 | name: release-please 15 | 16 | jobs: 17 | release-please: 18 | runs-on: ubuntu-latest 19 | outputs: 20 | release_created: ${{ steps.release.outputs.release_created }} 21 | upload_url: ${{ steps.release.outputs.upload_url }} 22 | tag_name: ${{ steps.release.outputs.tag_name }} 23 | steps: 24 | - uses: google-github-actions/release-please-action@v3 25 | id: release 26 | with: 27 | command: manifest 28 | 29 | upload-build: 30 | runs-on: ubuntu-latest 31 | needs: release-please 32 | if: ${{ needs.release-please.outputs.release_created }} 33 | env: 34 | upload_url: ${{ needs.release-please.outputs.upload_url }} 35 | tag_name: ${{ needs.release-please.outputs.tag_name }} 36 | steps: 37 | - uses: actions/checkout@v2 38 | - name: Use Node.js 39 | uses: actions/setup-node@v1 40 | with: 41 | node-version: "18.x" # You might need to adjust this value to your own version 42 | - name: Build 43 | id: build 44 | run: | 45 | npm install 46 | npm run build --if-present 47 | mkdir ${{ env.PLUGIN_NAME }} 48 | cp main.js manifest.json styles.css ${{ env.PLUGIN_NAME }} 49 | zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }} 50 | - name: Upload zip file 51 | id: upload-zip 52 | uses: actions/upload-release-asset@v1 53 | env: 54 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 55 | with: 56 | upload_url: ${{ env.upload_url }} 57 | asset_path: ./${{ env.PLUGIN_NAME }}.zip 58 | asset_name: ${{ env.PLUGIN_NAME }}-${{ env.tag_name }}.zip 59 | asset_content_type: application/zip 60 | - name: Upload main.js 61 | id: upload-main 62 | uses: actions/upload-release-asset@v1 63 | env: 64 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 65 | with: 66 | upload_url: ${{ env.upload_url }} 67 | asset_path: ./main.js 68 | asset_name: main.js 69 | asset_content_type: text/javascript 70 | - name: Upload manifest.json 71 | id: upload-manifest 72 | uses: actions/upload-release-asset@v1 73 | env: 74 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 75 | with: 76 | upload_url: ${{ env.upload_url }} 77 | asset_path: ./manifest.json 78 | asset_name: manifest.json 79 | asset_content_type: application/json 80 | - name: Upload styles.css 81 | id: upload-css 82 | uses: actions/upload-release-asset@v1 83 | env: 84 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 85 | with: 86 | upload_url: ${{ env.upload_url }} 87 | asset_path: ./styles.css 88 | asset_name: styles.css 89 | asset_content_type: text/css 90 | notify: 91 | needs: upload-build 92 | uses: javalent/workflows/.github/workflows/notify.yml@main 93 | secrets: inherit 94 | with: 95 | name: Admonitions 96 | repo: admonitions 97 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Intellij 2 | *.iml 3 | .idea 4 | 5 | # vscode 6 | workspace.code-workspace 7 | 8 | # npm 9 | node_modules 10 | 11 | # build 12 | main.js 13 | *.js.map 14 | .DS_Store 15 | *build.js 16 | rollup.config-dev.js 17 | webpack.dev.js 18 | 19 | 20 | generated/* 21 | .env 22 | *.map 23 | -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | ".": "10.3.2" 3 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [10.3.2](https://github.com/javalent/admonitions/compare/10.3.1...10.3.2) (2024-06-01) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * Fixes issue where the admonition suggester crashed (close [#342](https://github.com/javalent/admonitions/issues/342), close [#343](https://github.com/javalent/admonitions/issues/343)) ([3356aaf](https://github.com/javalent/admonitions/commit/3356aafa89216077f41df5b52261afb6d0397e84)) 11 | 12 | ## [10.3.1](https://github.com/javalent/admonitions/compare/10.3.0...10.3.1) (2024-05-23) 13 | 14 | 15 | ### Bug Fixes 16 | 17 | * Fixes @javalent/utilities dependency ([1bb3398](https://github.com/javalent/admonitions/commit/1bb339816a4f1e91e82bee16bcb245f36359946d)) 18 | 19 | ## [10.3.0](https://github.com/javalent/admonitions/compare/10.2.0...10.3.0) (2024-05-23) 20 | 21 | 22 | ### Features 23 | 24 | * Adds support for HSL, HSB, HSV, HEX and RGB colors in Admonition definitions (thanks [@x](https://github.com/x)Ryul ) ([a872858](https://github.com/javalent/admonitions/commit/a8728585e65e853d16eebebf52f33aa84755e9e4)) 25 | 26 | 27 | ### Bug Fixes 28 | 29 | * Improves appearance of Editor suggester ([7652ed0](https://github.com/javalent/admonitions/commit/7652ed08cc13cfea9f8fad0bae6473706991f286)) 30 | * Improves appearance of suggesters (close [#338](https://github.com/javalent/admonitions/issues/338)) ([e36a4a8](https://github.com/javalent/admonitions/commit/e36a4a88fbb6590a4acbd78822ab5b6e7d203e36)) 31 | * Improves behavior of Admonition edit modal ([72c2bc3](https://github.com/javalent/admonitions/commit/72c2bc3024957315e8b6c3e1693b59379962ad07)) 32 | * Improves lifetime of Admonition rendered content ([a045776](https://github.com/javalent/admonitions/commit/a04577628731ced61b04e40d82afa86dd1c51c79)) 33 | 34 | ## [10.2.0](https://github.com/javalent/admonitions/compare/10.1.2...10.2.0) (2024-02-13) 35 | 36 | 37 | ### Features 38 | 39 | * Better Autocompletion for Callouts (thanks [@heinrich26](https://github.com/heinrich26)) ([d367e05](https://github.com/javalent/admonitions/commit/d367e05e650989c17977c2959935fb6705057cef)) 40 | 41 | ## [10.1.2](https://github.com/javalent/admonitions/compare/10.1.1...10.1.2) (2024-01-30) 42 | 43 | 44 | ### Bug Fixes 45 | 46 | * Fixes issue with Open/Collapse Admonition commands ([6d61851](https://github.com/javalent/admonitions/commit/6d618518d55fe47e47a66a7ad8bab7924dd205f4)) 47 | * remove timestamp from auto-generated css (close [#327](https://github.com/javalent/admonitions/issues/327)) ([8952e8e](https://github.com/javalent/admonitions/commit/8952e8ef70bae7500b1f70445a96949621897317)) 48 | 49 | ## [10.1.1](https://github.com/javalent/admonitions/compare/10.1.0...10.1.1) (2023-09-16) 50 | 51 | 52 | ### Bug Fixes 53 | 54 | * delay live preview focus to allow for click events ([8739ad8](https://github.com/javalent/admonitions/commit/8739ad8a8c6a07ac134048a68209e10521df7adc)) 55 | 56 | ## [10.1.0](https://github.com/javalent/admonitions/compare/10.0.2...10.1.0) (2023-09-14) 57 | 58 | 59 | ### Features 60 | 61 | * Enable `metadata` field on admonition codeblocks (close [#261](https://github.com/javalent/admonitions/issues/261)) ([9143f3f](https://github.com/javalent/admonitions/commit/9143f3f8741ab8c603cc2e88833681ad5c7daae2)) 62 | 63 | 64 | ### Bug Fixes 65 | 66 | * Click on Admonition codeblocks to edit in LP (close [#278](https://github.com/javalent/admonitions/issues/278)) ([4d993bb](https://github.com/javalent/admonitions/commit/4d993bb61b9042eb1cf2565d0205112f28dee508)) 67 | * Explicitly set titles will override the no title setting in callouts (close [#284](https://github.com/javalent/admonitions/issues/284)) ([22f32d4](https://github.com/javalent/admonitions/commit/22f32d410b94aed961b57dba44c565877edce393)) 68 | * Fixes incorrect error being displayed when using an image as an icon (close [#311](https://github.com/javalent/admonitions/issues/311)) ([099e84c](https://github.com/javalent/admonitions/commit/099e84cfb78e521d33044a005058b289fcc2d872)) 69 | 70 | ## [10.0.2](https://github.com/javalent/admonitions/compare/10.0.1...10.0.2) (2023-09-14) 71 | 72 | 73 | ### Bug Fixes 74 | 75 | * Fixes CSS escaping for custom callouts in Obsidian installer 1.4+ ([523ca6a](https://github.com/javalent/admonitions/commit/523ca6a2874e7825e2151ccd2c83885e45b225ce)) 76 | * fixes issue where trying to add a copy content button to a callout with no content ([10db45a](https://github.com/javalent/admonitions/commit/10db45aee7d13a926ba46ab91c1700e8dc120b17)) 77 | 78 | ## [10.0.1](https://github.com/javalent/admonitions/compare/10.0.0...10.0.1) (2023-08-11) 79 | 80 | 81 | ### Bug Fixes 82 | 83 | * remove publish build step ([fc9bbfc](https://github.com/javalent/admonitions/commit/fc9bbfcd09c9214b48df365552085128a6711a43)) 84 | 85 | ## [10.0.0](https://github.com/javalent/admonitions/compare/9.3.3...10.0.0) (2023-08-10) 86 | 87 | 88 | ### ⚠ BREAKING CHANGES 89 | 90 | * Publish.js creation removed from Admonition as this feature is depreciated. 91 | 92 | ### Features 93 | 94 | * Publish.js creation removed from Admonition as this feature is depreciated. ([17db9dd](https://github.com/javalent/admonitions/commit/17db9dda0035965d26b00f77c13c7514aff829bd)) 95 | 96 | ## [9.3.3](https://github.com/javalent/admonitions/compare/9.3.2...9.3.3) (2023-07-05) 97 | 98 | 99 | ### Bug Fixes 100 | 101 | * propagate source to admonition element (close [#314](https://github.com/javalent/admonitions/issues/314)) ([f5ec0bb](https://github.com/javalent/admonitions/commit/f5ec0bbe172f57a9bdf0ea78313b53e37ba66cb7)) 102 | 103 | ## [9.3.2](https://github.com/javalent/admonitions/compare/9.3.1...9.3.2) (2023-05-30) 104 | 105 | 106 | ### Bug Fixes 107 | 108 | * Pass component to renderMarkdown ([dfcf140](https://github.com/javalent/admonitions/commit/dfcf140e6f024ac8792292b771d9476f44fe3c20)) 109 | 110 | ## [9.3.1](https://github.com/javalent/admonitions/compare/9.3.0...9.3.1) (2023-05-07) 111 | 112 | 113 | ### Bug Fixes 114 | 115 | * update publish reference to scss ([bf949d7](https://github.com/javalent/admonitions/commit/bf949d7e8d2eaf8e4d1a78815197f78cb8d61bab)) 116 | 117 | ## [9.3.0](https://github.com/javalent/admonitions/compare/9.2.3...9.3.0) (2023-05-07) 118 | 119 | 120 | ### Features 121 | 122 | * **Readme:** Update Readme to New Iteration ([49c151b](https://github.com/javalent/admonitions/commit/49c151b3ef980dd4a24e249e20afa95c05d01d34)), closes [#302](https://github.com/javalent/admonitions/issues/302) 123 | 124 | 125 | ### Bug Fixes 126 | 127 | * fix several Live Preview / Reading discrepancies ([13dd599](https://github.com/javalent/admonitions/commit/13dd59939092bceae6c2d9500d11af32aee039aa)) 128 | * improves consistency across different screen sizes ([13dd599](https://github.com/javalent/admonitions/commit/13dd59939092bceae6c2d9500d11af32aee039aa)) 129 | * **issues:** Typo in bug-report.yml ([aceb2dc](https://github.com/javalent/admonitions/commit/aceb2dc1ecb9aced607b4e0ce3d0b901711f278b)) 130 | * **issues:** Typo in contibuting.md ([5a7bc32](https://github.com/javalent/admonitions/commit/5a7bc32da1faa60e907f317977784e4ffd534f11)) 131 | * settings ui fixes ([fd46a25](https://github.com/javalent/admonitions/commit/fd46a25e478cf85c9782934e60e21798418ef1a8)) 132 | 133 | ## [9.2.3](https://github.com/valentine195/obsidian-admonition/compare/8.13.12...9.2.3) (2023-04-19) 134 | 135 | 136 | ### Bug Fixes 137 | 138 | * version bump oops ([d4d0098](https://github.com/valentine195/obsidian-admonition/commit/d4d00985d6621d7e7d16caade8efa460aee42a26)) 139 | 140 | ## [8.13.12](https://github.com/valentine195/obsidian-admonition/compare/8.13.11...8.13.12) (2023-04-19) 141 | 142 | 143 | ### Bug Fixes 144 | 145 | * mixed case custom callout types are supported ([#295](https://github.com/valentine195/obsidian-admonition/issues/295)) ([628179e](https://github.com/valentine195/obsidian-admonition/commit/628179ee8f2fb5df20114ea33f4d548798ff8a51)) 146 | * remove webpack ([00a22aa](https://github.com/valentine195/obsidian-admonition/commit/00a22aa276b533838b3c398331138fc222571ba5)) 147 | * use custom title in InsertAdmonitionModal ([#296](https://github.com/valentine195/obsidian-admonition/issues/296)) ([3e17d60](https://github.com/valentine195/obsidian-admonition/commit/3e17d60f706bfd420d31f1659d8b42ee43ec88f0)) 148 | 149 | ## [8.13.11](https://github.com/valentine195/obsidian-admonition/compare/v8.13.10...8.13.11) (2023-04-19) 150 | 151 | 152 | ### Bug Fixes 153 | 154 | * switch to release-please ([5e0ee1a](https://github.com/valentine195/obsidian-admonition/commit/5e0ee1ad0522d7715d6b4c51d4b018f1eda8e5a4)) 155 | 156 | ### [9.2.2](https://github.com/valentine195/obsidian-admonition/compare/9.2.1...9.2.2) (2023-04-12) 157 | 158 | 159 | ### Bug Fixes 160 | 161 | * removes need for space to show callout suggester fixes [#293](https://github.com/valentine195/obsidian-admonition/issues/293) ([db50d92](https://github.com/valentine195/obsidian-admonition/commit/db50d922af79d66a27b188df93189db19b8bae8b)) 162 | * update to new obsidian customCss definition ([518b3d3](https://github.com/valentine195/obsidian-admonition/commit/518b3d396f76ca96e673f3f0c5610646f78e4188)) 163 | 164 | ### [9.2.1](https://github.com/valentine195/obsidian-admonition/compare/9.2.0...9.2.1) (2023-02-03) 165 | 166 | 167 | ### Bug Fixes 168 | 169 | * admonition and callout suggester displays immediately ([#249](https://github.com/valentine195/obsidian-admonition/issues/249)) ([6631412](https://github.com/valentine195/obsidian-admonition/commit/6631412671a2ea2ccc9f892969e2c8c94a7a0b84)) 170 | 171 | ## [9.2.0](https://github.com/valentine195/obsidian-admonition/compare/9.1.0...9.2.0) (2023-01-27) 172 | 173 | 174 | ### Features 175 | 176 | * adds admonition exporting ([0897e08](https://github.com/valentine195/obsidian-admonition/commit/0897e08647ac6158c31d99383dd65e21381c5f1e)) 177 | 178 | 179 | ### Bug Fixes 180 | 181 | * fixes obsidian icon definition ([dc05aba](https://github.com/valentine195/obsidian-admonition/commit/dc05abac372ce9256bf63aa0872cfa67e07b883d)) 182 | 183 | ## [9.1.0](https://github.com/valentine195/obsidian-admonition/compare/9.0.5...9.1.0) (2023-01-26) 184 | 185 | 186 | ### Features 187 | 188 | * adds admonition importing ([723f2fb](https://github.com/valentine195/obsidian-admonition/commit/723f2fb4fb88d372cb424dbb139fc1567d169c7d)) 189 | 190 | ### [9.0.4](https://github.com/valentine195/obsidian-admonition/compare/9.0.3...9.0.4) (2022-09-03) 191 | 192 | 193 | ### Bug Fixes 194 | 195 | * Fixes default Admonitions not respecting the injectColor setting. ([7cf16f0](https://github.com/valentine195/obsidian-admonition/commit/7cf16f063c2a03e7fa6aa33cf7820b2e02422d86)) 196 | 197 | ### [9.0.1](https://github.com/valentine195/obsidian-admonition/compare/9.0.0...9.0.1) (2022-04-04) 198 | 199 | 200 | ### Bug Fixes 201 | 202 | * Admonition titles now take all child nodes from paragraph parent, not just html elements (close [#239](https://github.com/valentine195/obsidian-admonition/issues/239)) ([4db9a01](https://github.com/valentine195/obsidian-admonition/commit/4db9a01e5dd1cb8dab8387fc14db34b448664a61)) 203 | 204 | ## [9.0.0](https://github.com/valentine195/obsidian-admonition/compare/8.3.7...9.0.0) (2022-03-31) 205 | 206 | 207 | ### ⚠ BREAKING CHANGES 208 | 209 | * Admonitions now mimics the structure of callouts 210 | 211 | ### Features 212 | 213 | * Admonitions now mimics the structure of callouts ([1ba0085](https://github.com/valentine195/obsidian-admonition/commit/1ba0085258fed225514632b6aefd5d1cb79f897a)) 214 | * Callouts now receive the "copy content" button if turned on in Admonition settings ([266efde](https://github.com/valentine195/obsidian-admonition/commit/266efde5395659a45aea124a20d3eb29e95ea457)) 215 | 216 | 217 | ### Bug Fixes 218 | 219 | * Fixes admonition copy button setting inheritance ([4776da3](https://github.com/valentine195/obsidian-admonition/commit/4776da3fe6243ebfaa6d66c3d2419271d01f70a8)) 220 | 221 | ### [8.3.7](https://github.com/valentine195/obsidian-admonition/compare/8.3.6...8.3.7) (2022-03-29) 222 | 223 | 224 | ### Bug Fixes 225 | 226 | * Properly handles the icon manager not finding an icon node ([4470170](https://github.com/valentine195/obsidian-admonition/commit/44701700e3209a82de836bb139c015f1896b2108)) 227 | 228 | ### [8.3.6](https://github.com/valentine195/obsidian-admonition/compare/8.3.5...8.3.6) (2022-03-29) 229 | 230 | 231 | ### Bug Fixes 232 | 233 | * Fixes issue where a type couldn't be renamed ([f8d37ac](https://github.com/valentine195/obsidian-admonition/commit/f8d37ace0c528e92e2dbd10908eb684b623974cf)) 234 | * Fixes issue where removing types from the callout manager was removing the wrong type ([a3435c7](https://github.com/valentine195/obsidian-admonition/commit/a3435c7fd47534bf43610f5c4f59085bb6a4450b)) 235 | * Fixes title display in admonition creation modal ([1a73421](https://github.com/valentine195/obsidian-admonition/commit/1a734212b7ecddeeb400ac4dce4cfc2bfc3493c5)) 236 | * Temporarily remove Export Custom Types button from mobile until it can be fixed ([f56ebb8](https://github.com/valentine195/obsidian-admonition/commit/f56ebb8a69a8aca41794058838ac993733d6a17e)) 237 | 238 | ### [8.3.5](https://github.com/valentine195/obsidian-admonition/compare/8.3.4...8.3.5) (2022-03-29) 239 | 240 | 241 | ### Bug Fixes 242 | 243 | * Fixes issue with maintaining a CSS snippet for callouts ([f7efbbc](https://github.com/valentine195/obsidian-admonition/commit/f7efbbc092b5b9dd9ae88ca3b62af7406a68ad17)) 244 | * Fixes issue with post processors map not getting reinitialized properly ([a7752d9](https://github.com/valentine195/obsidian-admonition/commit/a7752d9d4238407e48412d70161598925df21395)) 245 | 246 | ### [8.3.4](https://github.com/valentine195/obsidian-admonition/compare/8.3.3...8.3.4) (2022-03-28) 247 | 248 | 249 | ### Bug Fixes 250 | 251 | * Fixes several issues with converting code block admonitions to callouts (close [#235](https://github.com/valentine195/obsidian-admonition/issues/235)) ([457b388](https://github.com/valentine195/obsidian-admonition/commit/457b388f35b6d9dbe8d2bc44e61c36033ada844a)) 252 | 253 | ### [8.3.3](https://github.com/valentine195/obsidian-admonition/compare/8.3.2...8.3.3) (2022-03-27) 254 | 255 | 256 | ### Bug Fixes 257 | 258 | * fixes admonition conversion on mobile ([b4d29fc](https://github.com/valentine195/obsidian-admonition/commit/b4d29fc1dc850db6bc08d08156501728c5dba6a7)) 259 | 260 | ### [8.3.2](https://github.com/valentine195/obsidian-admonition/compare/8.3.1...8.3.2) (2022-03-23) 261 | 262 | 263 | ### Bug Fixes 264 | 265 | * Fixes issue where Insert Callout wouldn't insert a `>` on the first line ([c276afe](https://github.com/valentine195/obsidian-admonition/commit/c276afea945bda3bbeed9512c2f9eaedcda4834d)) 266 | 267 | ### [8.3.1](https://github.com/valentine195/obsidian-admonition/compare/8.3.0...8.3.1) (2022-03-22) 268 | 269 | 270 | ### Bug Fixes 271 | 272 | * fixes callout metadata surfacing not applying to default types ([25b7756](https://github.com/valentine195/obsidian-admonition/commit/25b7756884d0bcf0049a20c0fcaa84adb3a2ff1a)) 273 | 274 | ## [8.3.0](https://github.com/valentine195/obsidian-admonition/compare/8.2.4...8.3.0) (2022-03-22) 275 | 276 | 277 | ### Features 278 | 279 | * Surface callout metadata onto callout in post processor ([f814019](https://github.com/valentine195/obsidian-admonition/commit/f8140192dad90788609d4586c1bfb6e256703145)) 280 | 281 | ### [8.2.4](https://github.com/valentine195/obsidian-admonition/compare/8.2.3...8.2.4) (2022-03-21) 282 | 283 | 284 | ### Bug Fixes 285 | 286 | * Admonition drop shadow setting will now apply to callouts (close [#222](https://github.com/valentine195/obsidian-admonition/issues/222)) ([0457ced](https://github.com/valentine195/obsidian-admonition/commit/0457ced760384690928a243bc7e391c691ccbdcc)) 287 | 288 | ### [8.2.3](https://github.com/valentine195/obsidian-admonition/compare/8.2.2...8.2.3) (2022-03-19) 289 | 290 | 291 | ### Bug Fixes 292 | 293 | * Fixes issue with custom admonitions not working with callouts ([9c9c486](https://github.com/valentine195/obsidian-admonition/commit/9c9c486f192c82a3a64d12bc442f05eeb30df4e6)) 294 | 295 | ### [8.2.2](https://github.com/valentine195/obsidian-admonition/compare/8.2.1...8.2.2) (2022-03-16) 296 | 297 | 298 | ### Bug Fixes 299 | 300 | * Fixes casing display issue for custom titles in settings (close [#214](https://github.com/valentine195/obsidian-admonition/issues/214)) ([211b323](https://github.com/valentine195/obsidian-admonition/commit/211b3239c5faf897336a6f6439f38f237abaa48c)) 301 | * Fixes issue where admonition types were being set incorrectly ([06fa0bf](https://github.com/valentine195/obsidian-admonition/commit/06fa0bf54b90c68114223bcaf1ad3903cef9fc81)) 302 | 303 | ### [8.2.1](https://github.com/valentine195/obsidian-admonition/compare/8.2.0...8.2.1) (2022-03-15) 304 | 305 | 306 | ### Bug Fixes 307 | 308 | * Fixes issue preventing Settings Search from searching Admonition settings ([d020438](https://github.com/valentine195/obsidian-admonition/commit/d02043846a0ad08c678af3c995306c1ff2071682)) 309 | 310 | ## [8.2.0](https://github.com/valentine195/obsidian-admonition/compare/8.1.0...8.2.0) (2022-03-15) 311 | 312 | 313 | ### Features 314 | 315 | * Adds Obsidian's icon set to list of icons ([e17b75a](https://github.com/valentine195/obsidian-admonition/commit/e17b75a01a84426824dd447596da7e732991b824)) 316 | * Adds setting to use CSS snippet to manage custom callouts ([cdd5b85](https://github.com/valentine195/obsidian-admonition/commit/cdd5b8543a32e2cf0071db53f288d9938dbcb81f)) 317 | 318 | 319 | ### Bug Fixes 320 | 321 | * Adds publish script generation to build ([c47d5f0](https://github.com/valentine195/obsidian-admonition/commit/c47d5f0b19b3f36003046607381672b7b0e6a275)) 322 | * Admonitions updated in settings are no longer moved to the bottom of the list ([54ea584](https://github.com/valentine195/obsidian-admonition/commit/54ea584fb19e11a5cd3f0207809e8a231a1e6b47)) 323 | * removes console logs from publish script ([43cd171](https://github.com/valentine195/obsidian-admonition/commit/43cd171976d243cb00a1bfad03d4860509f6a58d)) 324 | 325 | ## [8.1.0](https://github.com/valentine195/obsidian-admonition/compare/8.0.1...8.1.0) (2022-03-14) 326 | 327 | 328 | ### Features 329 | 330 | * Adds ability to convert code block admonitions to callouts in settings. ([3f5c6f0](https://github.com/valentine195/obsidian-admonition/commit/3f5c6f0f93f059cf50009366f331edd335632afa)) 331 | 332 | ### [8.0.1](https://github.com/valentine195/obsidian-admonition/compare/8.0.0...8.0.1) (2022-03-14) 333 | 334 | 335 | ### Bug Fixes 336 | 337 | * Significant improvements to the publish feature for code blocks. ([ce644a5](https://github.com/valentine195/obsidian-admonition/commit/ce644a5dc22b56226479a4c8dd5fc4fd4a8d6a21)) 338 | 339 | ## [8.0.0](https://github.com/valentine195/obsidian-admonition/compare/7.0.4...8.0.0) (2022-03-14) 340 | 341 | 342 | ### Features 343 | 344 | * Admonitions has been updated to support the new Obsidian callouts feature! Check the Readme for more information on what this means. ([3697e4b](https://github.com/valentine195/obsidian-admonition/commit/3697e4b60ca34ad703555de19f0b21f628aea530)) 345 | 346 | ### [7.0.4](https://github.com/valentine195/obsidian-admonition/compare/7.0.3...7.0.4) (2022-03-03) 347 | 348 | 349 | ### Bug Fixes 350 | 351 | * Content element will no longer be added if no content is provided (close [#201](https://github.com/valentine195/obsidian-admonition/issues/201)) ([94d7a4b](https://github.com/valentine195/obsidian-admonition/commit/94d7a4b956c9c7c7a23480242e27e00c19ac97e0)) 352 | 353 | ### [7.0.3](https://github.com/valentine195/obsidian-admonition/compare/7.0.2...7.0.3) (2022-02-28) 354 | 355 | 356 | ### Bug Fixes 357 | 358 | * fixes issue with icon suggester not loading icons ([4a463d3](https://github.com/valentine195/obsidian-admonition/commit/4a463d340a1625daba7bae7739e331c5522d33c0)) 359 | 360 | ### [7.0.1](https://github.com/valentine195/obsidian-admonition/compare/7.0.0...7.0.1) (2022-02-25) 361 | 362 | 363 | ### Bug Fixes 364 | 365 | * Fixes MSDoc-syntax in Live Preview ([e4d1cdb](https://github.com/valentine195/obsidian-admonition/commit/e4d1cdb7dd0e15b5f1876d8d14dfeb850b85527b)) 366 | 367 | ## [7.0.0](https://github.com/valentine195/obsidian-admonition/compare/6.12.1...7.0.0) (2022-02-25) 368 | 369 | 370 | ### ⚠ BREAKING CHANGES 371 | 372 | * Removes support for Python-style non-codeblock Admonitions. 373 | * Removes generated `id` from Admonition elements. 374 | * Python Markdown nesting syntax is no longer supported. Use nesting code blocks instead. 375 | 376 | ### Features 377 | 378 | * Added Icon Packs (close [#181](https://github.com/valentine195/obsidian-admonition/issues/181)) ([e973cec](https://github.com/valentine195/obsidian-admonition/commit/e973cec353a656112f9310c64afb7780ba8590c3)) 379 | * Python Markdown nesting syntax is no longer supported. Use nesting code blocks instead. ([3e65292](https://github.com/valentine195/obsidian-admonition/commit/3e6529217ec54f2145d4eec5bb1ba1f687fef228)) 380 | * Removes generated `id` from Admonition elements. ([2f87101](https://github.com/valentine195/obsidian-admonition/commit/2f87101ed6ceaf7b7b4ebf5c07063efe476019dc)) 381 | * Removes support for Python-style non-codeblock Admonitions. ([b6ad0c4](https://github.com/valentine195/obsidian-admonition/commit/b6ad0c4ac146b9a946982b218aae599cf92ff7d4)) 382 | 383 | 384 | ### Bug Fixes 385 | 386 | * Admonition modal image button switched to icon ([02882e7](https://github.com/valentine195/obsidian-admonition/commit/02882e7adcb3a280508cf32d13a5f19db74bc729)) 387 | * Admonition suggestor is now the full width of the input element ([#190](https://github.com/valentine195/obsidian-admonition/issues/190)) ([a3b3c54](https://github.com/valentine195/obsidian-admonition/commit/a3b3c54703d4b8128ae1a16687c11dca40144908)) 388 | * Fixed checkboxes not working when in blockquotes or nested, including in the MS-Doc syntax (close [#150](https://github.com/valentine195/obsidian-admonition/issues/150)) ([202b826](https://github.com/valentine195/obsidian-admonition/commit/202b826b4d82e33c0524cde0da6f21f5d33ea0bf)) 389 | * Fixes pandoc and PDF export of blockquotes (close [#192](https://github.com/valentine195/obsidian-admonition/issues/192)) ([ba75de8](https://github.com/valentine195/obsidian-admonition/commit/ba75de802b10e12ed347d9dd05239e1c3905ca09)) 390 | * Removed old polyfill, fixed app layout ready call ([2ab15f1](https://github.com/valentine195/obsidian-admonition/commit/2ab15f101d193061cc13523465decbbf25527efa)) 391 | 392 | ### [6.12.1](https://github.com/valentine195/obsidian-admonition/compare/6.12.0...6.12.1) (2022-02-22) 393 | 394 | 395 | ### Bug Fixes 396 | 397 | * fixed mathjax rendering (close [#189](https://github.com/valentine195/obsidian-admonition/issues/189)) ([79acf7f](https://github.com/valentine195/obsidian-admonition/commit/79acf7f3bb426bfc6076a1d17ecaf59aad3af556)) 398 | 399 | ## [6.12.0](https://github.com/valentine195/obsidian-admonition/compare/6.11.1...6.12.0) (2022-02-18) 400 | 401 | 402 | ### Features 403 | 404 | * adds support for indented code blocks using MSDoc syntax (close [#176](https://github.com/valentine195/obsidian-admonition/issues/176)) ([9c9e6ac](https://github.com/valentine195/obsidian-admonition/commit/9c9e6ac6e1340a16eb498b1df321cd5fd2b3b9a6)) 405 | 406 | 407 | ### Bug Fixes 408 | 409 | * admonitions with custom titles now include them when using the inserted with title command (close [#145](https://github.com/valentine195/obsidian-admonition/issues/145)) ([7034477](https://github.com/valentine195/obsidian-admonition/commit/7034477ffbcd377a58f2c2308ceec6a7e3f0f314)) 410 | * fixed rpg awesome icons not showing on mobile (close [#122](https://github.com/valentine195/obsidian-admonition/issues/122)) ([903f18a](https://github.com/valentine195/obsidian-admonition/commit/903f18acca5b5fd49c5680b9d5d2bf2505aeb6d3)) 411 | * reduced margin on content inside admonitions without titles (close [#185](https://github.com/valentine195/obsidian-admonition/issues/185)) ([643ca8e](https://github.com/valentine195/obsidian-admonition/commit/643ca8e866f468358b0ca40e4064e68b30d588c9)) 412 | 413 | ### [6.11.1](https://github.com/valentine195/obsidian-admonition/compare/6.11.0...6.11.1) (2022-02-17) 414 | 415 | 416 | ### Bug Fixes 417 | 418 | * fixes bug where you couldn't have an empty title for the MSDoc syntax (close [#183](https://github.com/valentine195/obsidian-admonition/issues/183)) ([ca3de23](https://github.com/valentine195/obsidian-admonition/commit/ca3de23c3ba3e8e7130299ae2144f4b65522ba95)) 419 | 420 | ## [6.11.0](https://github.com/valentine195/obsidian-admonition/compare/6.10.1...6.11.0) (2022-02-08) 421 | 422 | 423 | ### Features 424 | 425 | * adds Hide Empty Admonitions setting (close [#171](https://github.com/valentine195/obsidian-admonition/issues/171)) ([bf379bf](https://github.com/valentine195/obsidian-admonition/commit/bf379bf5de32236dc0ca4bfa7b03a3768f3be090)) 426 | 427 | 428 | ### Bug Fixes 429 | 430 | * Disabled `!!!`-style syntax in settings. Legacy support will continue until version 7.0.0. ([14b662d](https://github.com/valentine195/obsidian-admonition/commit/14b662d8eb0a5bbc632d23e4d51ef8d639501cc2)) 431 | * Fixes several issues with Live Preview MSDoc syntax rendering (closes [#170](https://github.com/valentine195/obsidian-admonition/issues/170)) ([50562db](https://github.com/valentine195/obsidian-admonition/commit/50562db72329af76ed07d448af936a7347efac20)) 432 | * switch to Obsidian's Live Preview State Facet ([a9e43f9](https://github.com/valentine195/obsidian-admonition/commit/a9e43f93d8a0dfbddbeaf9a23d1f3df6e7513062)) 433 | * warned about future removal of `!!!`-style syntax ([cc183a8](https://github.com/valentine195/obsidian-admonition/commit/cc183a8cbae2c78bdc09d7b9a8d853534abed9a1)) 434 | 435 | ### [6.10.1](https://github.com/valentine195/obsidian-admonition/compare/6.10.0...6.10.1) (2022-02-02) 436 | 437 | 438 | ### Bug Fixes 439 | 440 | * admonitions render again when restarting app ([5945f02](https://github.com/valentine195/obsidian-admonition/commit/5945f022e8138847c1e8c167e6f2007152dbbc73)) 441 | 442 | ## [6.10.0](https://github.com/valentine195/obsidian-admonition/compare/6.9.10...6.10.0) (2022-02-02) 443 | 444 | 445 | ### Features 446 | 447 | * adds Drop Shadow setting to remove drop shadow ([d6d598e](https://github.com/valentine195/obsidian-admonition/commit/d6d598eee14a894c0ef06b6291a51f21f0c67967)) 448 | 449 | ### [6.9.10](https://github.com/valentine195/obsidian-admonition/compare/6.9.9...6.9.10) (2022-02-02) 450 | 451 | 452 | ### Bug Fixes 453 | 454 | * deferred onload to layout ready to improve plugin load times ([a9accc9](https://github.com/valentine195/obsidian-admonition/commit/a9accc9b44cdbc0531f7335b5c9d8c6d216c9422)) 455 | 456 | ### [6.9.9](https://github.com/valentine195/obsidian-admonition/compare/6.9.8...6.9.9) (2022-01-28) 457 | 458 | 459 | ### Bug Fixes 460 | 461 | * 🐛 ordered lists in live preview now render correctly ([72d03b6](https://github.com/valentine195/obsidian-admonition/commit/72d03b6d312f45ad1b097b9a67071ba3304de85e)) 462 | 463 | ### [6.9.8](https://github.com/valentine195/obsidian-admonition/compare/6.9.7...6.9.8) (2022-01-27) 464 | 465 | 466 | ### Bug Fixes 467 | 468 | * 🐛 MS Doc syntax is no longer case sensitive ([b2de6be](https://github.com/valentine195/obsidian-admonition/commit/b2de6be03c5885a2b6fd1c1935276a38a8d1d712)) 469 | 470 | ### [6.9.7](https://github.com/valentine195/obsidian-admonition/compare/6.9.6...6.9.7) (2022-01-23) 471 | 472 | 473 | ### Bug Fixes 474 | 475 | * fixes issue with an extra space in msdoc syntax not rendering ([0c08854](https://github.com/valentine195/obsidian-admonition/commit/0c0885461f7c410192d70bcf24253b878b129bdf)) 476 | 477 | ### [6.9.6](https://github.com/valentine195/obsidian-admonition/compare/6.9.5...6.9.6) (2022-01-18) 478 | 479 | 480 | ### Bug Fixes 481 | 482 | * improved live preview styling ([5a0c111](https://github.com/valentine195/obsidian-admonition/commit/5a0c11182c4935e491f52248123a98226e631660)) 483 | * improves live preview detection ([4a941fc](https://github.com/valentine195/obsidian-admonition/commit/4a941fc6e0af271bf1996183d19b6a1fd295d2b5)) 484 | * removed logs ([6a56b70](https://github.com/valentine195/obsidian-admonition/commit/6a56b706ed2248b4d142c37e7c314a4e5d6e68c9)) 485 | * Update release notes again ([e9ba91f](https://github.com/valentine195/obsidian-admonition/commit/e9ba91f727a4c61ab9249f668c18760679616b2c)) 486 | 487 | ### [6.9.5](https://github.com/valentine195/obsidian-admonition/compare/6.9.4...6.9.5) (2022-01-12) 488 | 489 | 490 | ### Bug Fixes 491 | 492 | * Remove Changelog boilerplate from release notes ([9ab1d18](https://github.com/valentine195/obsidian-admonition/commit/9ab1d18d4164e64ba74dcc151eff6cf1485ccb93)) 493 | 494 | ### [6.9.4](https://github.com/valentine195/obsidian-admonition/compare/6.9.3...6.9.4) (2022-01-12) 495 | 496 | 497 | ### Bug Fixes 498 | 499 | * Add release notes to releases ([ca4d88c](https://github.com/valentine195/obsidian-admonition/commit/ca4d88ca8f31078678bcbac3fa96139cc8a6e989)) 500 | 501 | ### [6.9.3](https://github.com/valentine195/obsidian-admonition/compare/6.9.2...6.9.3) (2022-01-12) 502 | 503 | 504 | ### Bug Fixes 505 | 506 | * fixes issue with long scrolling notes with new MS syntax in LP (closes [#149](https://github.com/valentine195/obsidian-admonition/issues/149)) ([03582c6](https://github.com/valentine195/obsidian-admonition/commit/03582c661d77f49a605ae6231681fdadb8a55e92)) 507 | 508 | ### [6.9.2](https://github.com/valentine195/obsidian-admonition/compare/6.9.1...6.9.2) (2022-01-11) 509 | 510 | 511 | ### Bug Fixes 512 | 513 | * add admonition copy button to MS syntax style admonitions ([7a1fc6c](https://github.com/valentine195/obsidian-admonition/commit/7a1fc6ca0ecca6370ac93496fba781a30a8a90f9)) 514 | 515 | ### [6.9.1](https://github.com/valentine195/obsidian-admonition/compare/6.9.0...6.9.1) (2022-01-11) 516 | 517 | ## [6.9.0](https://github.com/valentine195/obsidian-admonition/compare/6.8.0...6.9.0) (2022-01-11) 518 | 519 | 520 | ### Features 521 | 522 | * add a toggle for MS syntax in live preview ([8b73f47](https://github.com/valentine195/obsidian-admonition/commit/8b73f47b60d2984deee062ac396dc0562f5cb535)) 523 | * add live preview support to new blockquote admonitions ([936199c](https://github.com/valentine195/obsidian-admonition/commit/936199c182767d7563e60c03f414db2de1d18ab1)) 524 | 525 | 526 | ### Bug Fixes 527 | 528 | * clean up settings code ([b36e7e8](https://github.com/valentine195/obsidian-admonition/commit/b36e7e86d301024fc19a8e770ad594b67865e1d2)) 529 | * fixed webpack external modules ([f6c70a7](https://github.com/valentine195/obsidian-admonition/commit/f6c70a70df01924ff4c38d6972fa7d27102964ae)) 530 | 531 | ## [6.8.0](https://github.com/valentine195/obsidian-admonition/compare/6.7.5...6.8.0) (2022-01-09) 532 | 533 | 534 | ### Features 535 | 536 | * Adds MSDoc-style admonitions (close [#133](https://github.com/valentine195/obsidian-admonition/issues/133)) ([3af3922](https://github.com/valentine195/obsidian-admonition/commit/3af392265a6ec8f27002ece8a61d0f9d65626484)) 537 | 538 | ### [6.7.5](https://github.com/valentine195/obsidian-admonition/compare/6.7.4...6.7.5) (2022-01-07) 539 | 540 | 541 | ### Bug Fixes 542 | 543 | * improve live preview rendering ([#141](https://github.com/valentine195/obsidian-admonition/issues/141)) ([636eb83](https://github.com/valentine195/obsidian-admonition/commit/636eb83e18a4bbe484a2a2a9256d56e369204f5e)) 544 | 545 | ### [6.7.4](https://github.com/valentine195/obsidian-admonition/compare/6.7.3...6.7.4) (2022-01-06) 546 | 547 | 548 | ### Bug Fixes 549 | 550 | * partial fix for LP display issues ([85ca7b7](https://github.com/valentine195/obsidian-admonition/commit/85ca7b7d0c6553a4b01f4e327d8b86c2351530cf)) 551 | 552 | ### [6.7.3](https://github.com/valentine195/obsidian-admonition/compare/6.7.2...6.7.3) (2022-01-05) 553 | 554 | 555 | ### Bug Fixes 556 | 557 | * fixed checkbox behavior in admonitions (close [#135](https://github.com/valentine195/obsidian-admonition/issues/135)) ([db3f55e](https://github.com/valentine195/obsidian-admonition/commit/db3f55ed96ced410d3129faa38d7275b6613ccc7)) 558 | 559 | ### [6.7.2](https://github.com/valentine195/obsidian-admonition/compare/6.7.1...6.7.2) (2021-12-03) 560 | 561 | 562 | ### Bug Fixes 563 | 564 | * insert admonition commands now properly wrap selected text ([758ce51](https://github.com/valentine195/obsidian-admonition/commit/758ce516b1e639f0d6829aea866c9d0fa3101747)) 565 | 566 | ### [6.7.1](https://github.com/valentine195/obsidian-admonition/compare/6.7.0...6.7.1) (2021-11-29) 567 | 568 | 569 | ### Bug Fixes 570 | 571 | * admonitions without titles properly display again (close [#131](https://github.com/valentine195/obsidian-admonition/issues/131)) ([b3bd5c2](https://github.com/valentine195/obsidian-admonition/commit/b3bd5c2fe1010a85e79c5662dc83752de54f032f)) 572 | 573 | ## [6.7.0](https://github.com/valentine195/obsidian-admonition/compare/6.6.1...6.7.0) (2021-11-22) 574 | 575 | ### Features 576 | 577 | - added ability to default to no title ([0983790](https://github.com/valentine195/obsidian-admonition/commit/0983790cd930657d69c96c885c23eabcb884987e)) 578 | - added per-admonition copy button setting ([0983790](https://github.com/valentine195/obsidian-admonition/commit/0983790cd930657d69c96c885c23eabcb884987e)), closes [#118](https://github.com/valentine195/obsidian-admonition/issues/118) 579 | - added setting to turn off title parsing ([0983790](https://github.com/valentine195/obsidian-admonition/commit/0983790cd930657d69c96c885c23eabcb884987e)) (close [#123](https://github.com/valentine195/obsidian-admonition/issues/123)) 580 | 581 | ### Bug Fixes 582 | 583 | - fixed error that could happen when editing admonitions 584 | 585 | ### [6.6.1](https://github.com/valentine195/obsidian-admonition/compare/6.6.0...6.6.1) (2021-11-16) 586 | 587 | ### Bug Fixes 588 | 589 | - added tdlr admonition type (not sure how it got removed) ([c7d14eb](https://github.com/valentine195/obsidian-admonition/commit/c7d14eb50b693830d0e8f227777097e411b474f5)) 590 | 591 | ## [6.6.0](https://github.com/valentine195/obsidian-admonition/compare/6.5.1...6.6.0) (2021-11-16) 592 | 593 | ### Features 594 | 595 | - parent element now receives styles ([1297cc1](https://github.com/valentine195/obsidian-admonition/commit/1297cc1659f02b9510669b00034943515fef8365)) 596 | 597 | ### [6.5.1](https://github.com/valentine195/obsidian-admonition/compare/6.5.0...6.5.1) (2021-11-01) 598 | 599 | ### Bug Fixes 600 | 601 | - fixed admonition color issue ([b600bfb](https://github.com/valentine195/obsidian-admonition/commit/b600bfbac6c0a8077d91aa65717be81782cb4438)) 602 | 603 | ## [6.5.0](https://github.com/valentine195/obsidian-admonition/compare/6.4.1...6.5.0) (2021-11-01) 604 | 605 | ### Features 606 | 607 | - added ability to globally turn off Admonition Color being set directly on element ([afbcc23](https://github.com/valentine195/obsidian-admonition/commit/afbcc235dd3314ec779f44a25c4ffda74e0acaf1)) 608 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jeremy Valentine 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > 🥇 Our documentation has moved ***[here](https://plugins.javalent.com/admonitions)***. 2 | > 3 | > Buy Me a Coffee at ko-fi.com 4 | > 5 | > --- 6 | > 7 | > **Development Status**: Maintenance Mode 8 | > 9 | > Due to a glut of high priority Javalent plugin projects, this plugin is now entering maintenance mode for the time being. This is **not** a permanent status. 10 | > - PR's will be reviewed. 11 | > - *Yay* bugs will be reviewed and worked if able. 12 | > - Feature Requests **will not** be worked. 13 | 14 | --- 15 | 16 | The Admonition plugin for Obsidian is a tool that allows you to create attention-grabbing callouts, tips, warnings and other informative blocks within your notes. The plugin provides a range of pre-defined icons to pick from, as well as the ability to create your own custom styles using CSS. You can customize and style to fit your specific needs, and can target each iteration independently using the `.callout` and `.admonition` selectors. 17 | 18 | ## Features 19 | 20 | - Supports a variety of built-in admonition types such as tip, warning, caution, note, and more. 21 | - Customizable styles with the ability to use Markdown attributes in callouts, or stylize directly with CSS. 22 | - Supports nesting of blockquotes and code-blocks. 23 | - Admonitions can flourish in combination with other plugins such as Obsidian **[Templates](https://help.obsidian.md/Plugins/Templates)** or @SilentVoid13's **[Templater](https://github.com/SilentVoid13/Templater)**. 24 | - Supports import and export of custom admonitions via `.json` files. 25 | 26 | ### Quickstart 27 | 28 | 1. Install the Admonition plugin from the Community Plugins pane in Obsidian. 29 | 2. **Callout Version**: In the editor, type out the name of an admonition type (such as >[!tip]) followed by your content. 30 | 3. **Admonition Version**: In the editor, type out the name of an admonition type in a code block `(such as ```ad-tip)` followed by your content on the subsequent lines. 31 | 4. Ensure three backticks close your codeblock. 32 | 5. Preview your note to see the formatted admonition. 33 | 34 | 35 | ````yaml 36 | ```ad-tip 37 | title: This is a tip 38 | 39 | This is the content of the admonition tip. 40 | ``` 41 | ```` 42 | 43 | Check out the **[plugin documentation](https://plugins.javalent.com/admonitions)** for more detailed instructions and examples. 44 | 45 | ## Support 46 | 47 | If you encounter any issues, want to give back and help out, or have suggestions for new features, file an issue on the **[GitHub repository](https://github.com/javalent/admonitions/issues)**. 48 | 49 | ### Complementary Plugins by Javalent 50 | 51 | While we think all of our plugins are pretty awesome, we think these are especially awesome and work well with this plugin: 52 | 53 | - **[Obsidian Leaflet](https://github.com/valentine195/obsidian-leaflet-plugin)** Adds interactive maps to Obsidian notes 54 | - **[Dice Roller](https://github.com/valentine195/obsidian-dice-roller)** Inline dice rolling for Obsidian 55 | - **[Fantasy Statblocks](https://github.com/valentine195/obsidian-5e-statblocks)** Format Statblocks inside Obsidian 56 | - **[Initiative Tracker](https://github.com/valentine195/obsidian-initiative-tracker)** Track initiative and turns in Obsidian 57 | -------------------------------------------------------------------------------- /esbuild.config.mjs: -------------------------------------------------------------------------------- 1 | import esbuild from "esbuild"; 2 | import process from "process"; 3 | import builtins from "builtin-modules"; 4 | import { config } from "dotenv"; 5 | import { sassPlugin } from "esbuild-sass-plugin"; 6 | 7 | config(); 8 | 9 | const banner = `/* 10 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD 11 | if you want to view the source, please visit the github repository of this plugin 12 | */ 13 | `; 14 | 15 | const prod = process.argv[2] === "production"; 16 | 17 | const dir = prod ? "./" : process.env.OUTDIR; 18 | 19 | esbuild 20 | .build({ 21 | banner: { 22 | js: banner 23 | }, 24 | entryPoints: ["src/main.ts", "src/styles.css"], 25 | bundle: true, 26 | external: [ 27 | "obsidian", 28 | "electron", 29 | "codemirror", 30 | "@codemirror/autocomplete", 31 | "@codemirror/closebrackets", 32 | "@codemirror/collab", 33 | "@codemirror/commands", 34 | "@codemirror/comment", 35 | "@codemirror/fold", 36 | "@codemirror/gutter", 37 | "@codemirror/highlight", 38 | "@codemirror/history", 39 | "@codemirror/language", 40 | "@codemirror/lint", 41 | "@codemirror/matchbrackets", 42 | "@codemirror/panel", 43 | "@codemirror/rangeset", 44 | "@codemirror/rectangular-selection", 45 | "@codemirror/search", 46 | "@codemirror/state", 47 | "@codemirror/stream-parser", 48 | "@codemirror/text", 49 | "@codemirror/tooltip", 50 | "@codemirror/view", 51 | ...builtins 52 | ], 53 | format: "cjs", 54 | watch: !prod, 55 | minify: prod, 56 | target: "es2020", 57 | logLevel: "info", 58 | sourcemap: !prod ? "inline" : false, 59 | treeShaking: true, 60 | outdir: dir, 61 | 62 | plugins: [sassPlugin()] 63 | }) 64 | .catch(() => process.exit(1)); 65 | -------------------------------------------------------------------------------- /esbuild.publish.mjs: -------------------------------------------------------------------------------- 1 | import esbuild from "esbuild"; 2 | import process from "process"; 3 | import builtins from "builtin-modules"; 4 | import { config } from "dotenv"; 5 | import { sassPlugin } from "esbuild-sass-plugin"; 6 | 7 | config(); 8 | 9 | const banner = `/* 10 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD 11 | if you want to view the source, please visit the github repository of this plugin 12 | */ 13 | `; 14 | 15 | const prod = process.argv[2] === "production"; 16 | 17 | const dir = prod ? "./" : process.env.OUTDIR; 18 | 19 | esbuild 20 | .build({ 21 | banner: { 22 | js: banner 23 | }, 24 | entryPoints: ["src/publish/publish.admonition.ts"], 25 | bundle: true, 26 | external: ["obsidian", "electron"], 27 | format: "cjs", 28 | watch: !prod, 29 | minify: prod, 30 | target: "es2020", 31 | logLevel: "info", 32 | sourcemap: !prod ? "inline" : false, 33 | treeShaking: true, 34 | outfile: "./publish/publish.admonition.txt", 35 | define: { 36 | ADMONITION_ICON_MAP: "ADMONITION_ICON_MAP" 37 | }, 38 | tsconfig: "./src/publish/tsconfig.json", 39 | plugins: [sassPlugin()] 40 | }) 41 | .catch(() => process.exit(1)); 42 | -------------------------------------------------------------------------------- /icons/octicons/_gen.js: -------------------------------------------------------------------------------- 1 | const { writeFile } = require("fs"); 2 | 3 | const octicons = require("@primer/octicons"); 4 | 5 | const data = Object.fromEntries( 6 | Object.entries(octicons).map(([icon, definition]) => { 7 | return [icon, definition.toSVG()]; 8 | }) 9 | ); 10 | 11 | writeFile(`./icons/octicons/icons.json`, JSON.stringify(data), (err) => { 12 | if (err) console.error(err); 13 | }); 14 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian-admonition", 3 | "name": "Admonition", 4 | "version": "10.3.2", 5 | "minAppVersion": "1.1.0", 6 | "description": "Enhanced callouts for Obsidian.md", 7 | "author": "Jeremy Valentine", 8 | "authorUrl": "", 9 | "isDesktopOnly": false 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obsidian-admonition", 3 | "version": "10.3.2", 4 | "description": "Enhanced callouts for Obsidian.md", 5 | "main": "main.js", 6 | "scripts": { 7 | "dev": "node ./esbuild.config.mjs", 8 | "build": "node ./esbuild.config.mjs production", 9 | "publish": "node ./esbuild.publish.mjs production", 10 | "release": "standard-version" 11 | }, 12 | "standard-version": { 13 | "t": "", 14 | "releaseCommitMessageFormat": "Obsidian Admonitions Release: v{{currentTag}}" 15 | }, 16 | "keywords": [], 17 | "author": "Jeremy Valentine", 18 | "license": "MIT", 19 | "devDependencies": { 20 | "@fortawesome/fontawesome-svg-core": "^1.2.32", 21 | "@fortawesome/free-regular-svg-icons": "^5.15.3", 22 | "@fortawesome/free-solid-svg-icons": "^5.15.1", 23 | "@javalent/utilities": "^1.1.6", 24 | "@popperjs/core": "^2.9.2", 25 | "@primer/octicons": "^16.3.1", 26 | "@types/node": "^14.14.2", 27 | "@types/object.fromentries": "^2.0.0", 28 | "builtin-modules": "^3.3.0", 29 | "dotenv": "^10.0.0", 30 | "esbuild": "^0.13.15", 31 | "esbuild-sass-plugin": "^2.2.4", 32 | "monkey-around": "^2.3.0", 33 | "object.fromentries": "^2.0.4", 34 | "obsidian": "^1.5.7-1", 35 | "tslib": "^2.0.3", 36 | "typescript": "^4.0.3" 37 | }, 38 | "dependencies": { 39 | "@fortawesome/free-brands-svg-icons": "^5.15.4", 40 | "standard-version": "^9.3.2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /publish/gifs/all.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalent/admonitions/f19389940b0148b677e106f503980cbb9f767f63/publish/gifs/all.gif -------------------------------------------------------------------------------- /publish/gifs/collapse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalent/admonitions/f19389940b0148b677e106f503980cbb9f767f63/publish/gifs/collapse.gif -------------------------------------------------------------------------------- /publish/gifs/create-an-admonition.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalent/admonitions/f19389940b0148b677e106f503980cbb9f767f63/publish/gifs/create-an-admonition.gif -------------------------------------------------------------------------------- /publish/images/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalent/admonitions/f19389940b0148b677e106f503980cbb9f767f63/publish/images/default.png -------------------------------------------------------------------------------- /publish/images/msdocs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalent/admonitions/f19389940b0148b677e106f503980cbb9f767f63/publish/images/msdocs.png -------------------------------------------------------------------------------- /publish/images/nested-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalent/admonitions/f19389940b0148b677e106f503980cbb9f767f63/publish/images/nested-code.png -------------------------------------------------------------------------------- /publish/images/nested.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalent/admonitions/f19389940b0148b677e106f503980cbb9f767f63/publish/images/nested.png -------------------------------------------------------------------------------- /publish/images/no-title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalent/admonitions/f19389940b0148b677e106f503980cbb9f767f63/publish/images/no-title.png -------------------------------------------------------------------------------- /publish/images/pf2-note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalent/admonitions/f19389940b0148b677e106f503980cbb9f767f63/publish/images/pf2-note.png -------------------------------------------------------------------------------- /publish/images/rendered-title-markdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalent/admonitions/f19389940b0148b677e106f503980cbb9f767f63/publish/images/rendered-title-markdown.png -------------------------------------------------------------------------------- /publish/images/title-markdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalent/admonitions/f19389940b0148b677e106f503980cbb9f767f63/publish/images/title-markdown.png -------------------------------------------------------------------------------- /publish/images/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalent/admonitions/f19389940b0148b677e106f503980cbb9f767f63/publish/images/title.png -------------------------------------------------------------------------------- /release-please-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": { 3 | ".": { 4 | "changelog-path": "CHANGELOG.md", 5 | "release-type": "node" 6 | } 7 | }, 8 | "include-component-in-tag": false, 9 | "include-v-in-tag": false, 10 | "extra-files": [ 11 | { 12 | "type": "json", 13 | "path": "manifest.json", 14 | "jsonpath": "$.version" 15 | } 16 | ], 17 | "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json" 18 | } 19 | -------------------------------------------------------------------------------- /src/@types/index.d.ts: -------------------------------------------------------------------------------- 1 | import { IconName } from "@fortawesome/fontawesome-svg-core"; 2 | import { DownloadableIconPack } from "src/icons/manager"; 3 | import { ObsidianIconNames } from "src/icons/obsidian"; 4 | 5 | export interface Admonition { 6 | type: string; 7 | title?: string; 8 | icon: AdmonitionIconDefinition; 9 | color: string; 10 | command: boolean; 11 | injectColor?: boolean; 12 | noTitle: boolean; 13 | copy?: boolean; 14 | } 15 | 16 | export interface NestedAdmonition { 17 | type: string; 18 | start: number; 19 | end: number; 20 | src: string; 21 | } 22 | 23 | export interface AdmonitionSettings { 24 | userAdmonitions: Record; 25 | syntaxHighlight: boolean; 26 | copyButton: boolean; 27 | autoCollapse: boolean; 28 | defaultCollapseType: "open" | "closed"; 29 | version: string; 30 | injectColor: boolean; 31 | parseTitles: boolean; 32 | dropShadow: boolean; 33 | hideEmpty: boolean; 34 | icons: Array; 35 | useFontAwesome: boolean; 36 | rpgDownloadedOnce: boolean; 37 | open: { 38 | admonitions: boolean; 39 | icons: boolean; 40 | other: boolean; 41 | advanced: boolean; 42 | }; 43 | msDocConverted: boolean; 44 | useSnippet: boolean; 45 | snippetPath: string; 46 | } 47 | 48 | export type AdmonitionIconDefinition = { 49 | type?: IconType; 50 | name?: IconName | ObsidianIconNames | string; 51 | }; 52 | 53 | export type IconType = 54 | | "font-awesome" 55 | | "obsidian" 56 | | "image" 57 | | DownloadableIconPack; 58 | 59 | export type AdmonitionIconName = AdmonitionIconDefinition["name"]; 60 | export type AdmonitionIconType = AdmonitionIconDefinition["type"]; -------------------------------------------------------------------------------- /src/assets/callout.scss: -------------------------------------------------------------------------------- 1 | .callout:not(.admonition) { 2 | &.drop-shadow { 3 | box-shadow: 0 0.2rem 0.5rem var(--background-modifier-box-shadow); 4 | } 5 | .no-title { 6 | display: none; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/assets/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalent/admonitions/f19389940b0148b677e106f503980cbb9f767f63/src/assets/main.css -------------------------------------------------------------------------------- /src/assets/main.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | --admonition-details-icon: url("data:image/svg+xml;charset=utf-8,"); 3 | --admonition-margin-top: 1.5rem; 4 | --admonition-margin-bottom: var(--admonition-margin-top); 5 | --admonition-margin-top-lp: 0px; 6 | --admonition-margin-bottom-lp: 0.75rem; 7 | } 8 | 9 | .admonition { 10 | margin-top: var(--admonition-margin-top); 11 | margin-bottom: var(--admonition-margin-bottom); 12 | box-shadow: 0 0.2rem 0.5rem var(--background-modifier-box-shadow); 13 | 14 | &.no-title { 15 | .admonition-content { 16 | margin-top: 0; 17 | margin-bottom: 0; 18 | } 19 | } 20 | 21 | li { 22 | &.task-list-item { 23 | &.is-checked { 24 | p { 25 | text-decoration: line-through; 26 | } 27 | } 28 | } 29 | } 30 | 31 | &.no-drop { 32 | box-shadow: none; 33 | 34 | & > .admonition-title { 35 | &.no-title { 36 | & + .admonition-content { 37 | margin-top: 0; 38 | margin-bottom: 0; 39 | } 40 | } 41 | } 42 | 43 | .admonition { 44 | .admonition-content { 45 | border-right: 0.0625rem solid rgba(var(--admonition-color), 0.2); 46 | border-bottom: 0.0625rem solid rgba(var(--admonition-color), 0.2); 47 | } 48 | 49 | .admonition-title { 50 | &.no-title { 51 | & + .admonition-content { 52 | border-top: 0.0625rem solid rgba(var(--admonition-color), 0.2); 53 | margin-top: 0; 54 | margin-bottom: 0; 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | 62 | // Makes Mathjax blocks stop jumping around between views and 63 | // makes Mathjax act nicer in OL Blocks 64 | :is(.markdown-source-view.mod-cm6) .admonition .math-block > mjx-container { 65 | display: block; 66 | text-align: center; 67 | padding: 1rem; 68 | } 69 | 70 | :is(.markdown-reading-view) .admonition .math-block > mjx-container { 71 | display: block; 72 | text-align: center; 73 | padding: 0.0625rem; 74 | } 75 | 76 | *:not(.is-live-preview) { 77 | .admonition { 78 | &.no-content { 79 | display: none; 80 | } 81 | } 82 | } 83 | 84 | .is-live-preview { 85 | .admonition { 86 | margin-top: var(--admonition-margin-top-lp); 87 | margin-bottom: var(--admonition-margin-bottom-lp); 88 | 89 | &.no-content { 90 | opacity: 0.1; 91 | } 92 | } 93 | 94 | .admonition-content { 95 | & p { 96 | // This makes the P Layer consistent between views 97 | line-height: inherit; 98 | margin: revert; 99 | 100 | br { 101 | // This makes the BR Layer match the theme layer. 102 | display: initial; 103 | } 104 | 105 | & ul { 106 | > li { 107 | > ul { 108 | border-left: var(--blockquote-border-thickness); 109 | border-left-color: var(--list-marker-color); 110 | border-left-style: solid; 111 | } 112 | } 113 | } 114 | } 115 | 116 | &:first-child { 117 | // Reduced to 0.8 and made into rem units. 118 | margin-top: 0.8rem; 119 | } 120 | 121 | &:last-child { 122 | // Reduced to 0.8 and made into rem units. 123 | margin-bottom: 0.8rem; 124 | } 125 | } 126 | } 127 | 128 | .admonition-title { 129 | &.no-title { 130 | display: none; 131 | } 132 | 133 | &:hover { 134 | & + .admonition-content { 135 | .admonition-content-copy { 136 | opacity: 0.7; 137 | } 138 | } 139 | } 140 | } 141 | 142 | .admonition-content, 143 | .callout-content { 144 | position: relative; 145 | } 146 | 147 | .admonition-content-copy { 148 | color: var(--text-faint); 149 | cursor: pointer; 150 | opacity: 0; 151 | position: absolute; 152 | margin: 0.375rem; 153 | right: 0; 154 | top: 0; 155 | transition: 0.3s opacity ease-in; 156 | 157 | &:hover { 158 | color: var(--text-normal); 159 | } 160 | } 161 | 162 | .admonition:hover .admonition-content-copy, 163 | .callout:hover .admonition-content-copy, 164 | .admonition-content-copy:hover { 165 | opacity: 1; 166 | } 167 | 168 | .admonition-settings { 169 | .additional { 170 | margin: 0.375rem 0.75rem; 171 | 172 | & > .setting-item { 173 | border-top: 0; 174 | padding-top: 0.5625rem; 175 | } 176 | } 177 | 178 | .coffee { 179 | width: 60%; 180 | color: var(--text-faint); 181 | margin: 1rem auto; 182 | text-align: center; 183 | 184 | img { 185 | height: 30px; 186 | } 187 | } 188 | 189 | details { 190 | & > summary { 191 | outline: none; 192 | display: block !important; 193 | list-style: none !important; 194 | list-style-type: none !important; 195 | min-height: 1rem; 196 | border-top-left-radius: 0.1rem; 197 | border-top-right-radius: 0.1rem; 198 | cursor: pointer; 199 | position: relative; 200 | 201 | & > .collapser { 202 | position: absolute; 203 | top: 50%; 204 | right: 0.5rem; 205 | transform: translateY(-50%); 206 | content: ""; 207 | 208 | & > .handle { 209 | transform: rotate(0deg); 210 | transition: transform 0.25s; 211 | background-color: currentColor; 212 | -webkit-mask-repeat: no-repeat; 213 | mask-repeat: no-repeat; 214 | -webkit-mask-size: contain; 215 | mask-size: contain; 216 | -webkit-mask-image: var(--admonition-details-icon); 217 | mask-image: var(--admonition-details-icon); 218 | width: 20px; 219 | height: 20px; 220 | } 221 | } 222 | } 223 | } 224 | 225 | details[open] { 226 | & > summary { 227 | & > .collapser { 228 | & > .handle { 229 | transform: rotate(90deg); 230 | } 231 | } 232 | } 233 | } 234 | } 235 | 236 | .setting-item { 237 | & > .admonition { 238 | width: 50%; 239 | margin: 0; 240 | } 241 | } 242 | 243 | .unset-align-items { 244 | align-items: unset; 245 | } 246 | 247 | .admonition-settings-modal { 248 | .has-invalid-message { 249 | display: grid; 250 | grid-template-columns: 1fr auto; 251 | grid-template-rows: 1fr 1fr; 252 | grid-template-areas: "text image" 253 | "inv inv"; 254 | } 255 | 256 | input { 257 | &.is-invalid { 258 | border-color: #dc3545 !important; 259 | background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); 260 | background-repeat: no-repeat; 261 | background-position: right calc(0.375em + 0.1875rem) center; 262 | background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); 263 | } 264 | } 265 | 266 | .admonition-type-setting { 267 | input { 268 | grid-column: span 2; 269 | } 270 | } 271 | 272 | .invalid-feedback { 273 | display: block; 274 | grid-area: inv; 275 | width: 100%; 276 | margin-top: 0.25rem; 277 | font-size: 0.875em; 278 | color: #dc3545; 279 | } 280 | } 281 | 282 | .suggestion-content { 283 | &.admonition-icon { 284 | display: flex; 285 | align-items: center; 286 | justify-content: space-between; 287 | flex-flow: row wrap; 288 | 289 | & > .suggestion-text { 290 | &.admonition-text { 291 | width: fit-content; 292 | } 293 | } 294 | 295 | & > .suggestion-flair { 296 | &.admonition-suggester-icon { 297 | width: min-content; 298 | position: relative; 299 | top: unset; 300 | left: unset; 301 | right: unset; 302 | bottom: unset; 303 | display: flex; 304 | align-items: center; 305 | } 306 | } 307 | 308 | & > .suggestion-note { 309 | width: 100%; 310 | } 311 | } 312 | } 313 | 314 | .suggestion-container > .suggestion > .suggestion-item.admonition-suggester-item { 315 | color: rgb(var(--callout-color)); 316 | 317 | &.is-selected { 318 | background-color: rgba(var(--callout-color), 0.1); 319 | } 320 | 321 | .admonition-suggester-icon { 322 | &:not(:empty) { 323 | padding-right: var(--size-4-1); 324 | } 325 | 326 | display: inline-block; 327 | vertical-align: middle; 328 | } 329 | } 330 | 331 | .admonition-color-settings { 332 | .setting-item-control { 333 | gap: 1rem; 334 | } 335 | 336 | input[type="color"]:disabled { 337 | opacity: 0.75; 338 | cursor: not-allowed; 339 | } 340 | } 341 | 342 | .theme-dark { 343 | .admonition-color-settings { 344 | input[type=color]:disabled { 345 | opacity: 1; 346 | cursor: not-allowed; 347 | } 348 | } 349 | } 350 | 351 | .admonition-convert { 352 | display: flex; 353 | align-items: center; 354 | gap: 0.25rem; 355 | } 356 | 357 | .admonition-convert-icon { 358 | display: flex; 359 | align-items: center; 360 | 361 | .admonition-spin { 362 | animation: admonition-convert 1s ease-in-out infinite; 363 | fill: currentColor; 364 | } 365 | } 366 | 367 | @keyframes admonition-convert { 368 | from { 369 | transform: rotateZ(-45deg); 370 | } 371 | to { 372 | transform: rotateZ(315deg); 373 | } 374 | } 375 | 376 | .admonition-settings { 377 | .admonition-convert { 378 | color: var(--text-error); 379 | } 380 | } 381 | 382 | .notice-container { 383 | .admonition-convert { 384 | justify-content: space-between; 385 | gap: 1rem; 386 | } 387 | } 388 | 389 | .admonition-file-upload { 390 | margin-right: 0; 391 | margin-left: 12px; 392 | 393 | & > input[type="file"] { 394 | display: none; 395 | } 396 | } 397 | 398 | .insert-admonition-modal button:focus, 399 | .insert-admonition-modal .clickable-icon:focus { 400 | box-shadow: 0 0 5px rgb(0 0 0 / 50%); 401 | border-color: var(--background-modifier-border-focus); 402 | } 403 | 404 | .admonition-settings details > summary::-webkit-details-marker, 405 | .admonition-settings details > summary::marker { 406 | display: none !important; 407 | } 408 | 409 | .admonition-setting-warning { 410 | display: flex; 411 | gap: 0.25rem; 412 | align-items: center; 413 | 414 | &.text-warning { 415 | color: var(--text-error); 416 | } 417 | } 418 | 419 | .admonitions-nested-settings { 420 | padding-bottom: 18px; 421 | 422 | .setting-item { 423 | border: 0; 424 | padding-bottom: 0; 425 | } 426 | } 427 | 428 | .admonitions-nested-settings[open] .setting-item-heading, 429 | .admonitions-nested-settings:not(details) .setting-item-heading { 430 | border-top: 0; 431 | border-bottom: 1px solid var(--background-modifier-border); 432 | } 433 | 434 | .is-live-preview .admonition-content ul, 435 | .is-live-preview .admonition-content ol { 436 | white-space: normal; 437 | } 438 | -------------------------------------------------------------------------------- /src/callout/manager.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | MarkdownPostProcessorContext, 4 | Notice, 5 | setIcon 6 | } from "obsidian"; 7 | import { Admonition } from "src/@types"; 8 | import ObsidianAdmonition from "src/main"; 9 | import { CalloutSuggest } from "../suggest/suggest"; 10 | 11 | type Heights = Partial<{ 12 | height: string; 13 | "padding-top": string; 14 | "padding-bottom": string; 15 | "margin-top": string; 16 | "margin-bottom": string; 17 | }>; 18 | 19 | export default class CalloutManager extends Component { 20 | /* ruleMap: Map = new Map(); */ 21 | constructor(public plugin: ObsidianAdmonition) { 22 | super(); 23 | } 24 | 25 | onload() { 26 | //build sheet for custom admonitions 27 | 28 | document.head.appendChild(this.style); 29 | 30 | for (const admonition of Object.values( 31 | this.plugin.data.userAdmonitions 32 | )) { 33 | this.addAdmonition(admonition); 34 | } 35 | this.setUseSnippet(); 36 | 37 | this.plugin.registerEditorSuggest(new CalloutSuggest(this.plugin)); 38 | 39 | this.plugin.registerMarkdownPostProcessor( 40 | this.calloutProcessor.bind(this) 41 | ); 42 | } 43 | heights: Array = [ 44 | "height", 45 | "padding-top", 46 | "padding-bottom", 47 | "margin-top", 48 | "margin-bottom" 49 | ]; 50 | heightMap: WeakMap = new WeakMap(); 51 | calloutProcessor(el: HTMLElement, ctx: MarkdownPostProcessorContext) { 52 | const callout = el?.querySelector(".callout"); 53 | if (!callout) return; 54 | //apply metadata 55 | 56 | const type = callout.dataset.callout; 57 | const admonition = this.plugin.admonitions[type]; 58 | if (!admonition) return; 59 | 60 | const titleEl = callout.querySelector(".callout-title"); 61 | const content = 62 | callout.querySelector(".callout-content"); 63 | 64 | const section = ctx.getSectionInfo(el); 65 | if (section) { 66 | const { text, lineStart, lineEnd } = section; 67 | const definition = text.split("\n")[lineStart]; 68 | 69 | const [, metadata] = definition.match(/> \[!.+\|(.*)]/) ?? []; 70 | if (metadata) { 71 | callout.dataset.calloutMetadata = metadata; 72 | } 73 | 74 | if ( 75 | content && 76 | (this.plugin.admonitions[type].copy ?? 77 | this.plugin.data.copyButton) 78 | ) { 79 | let copy = content.createDiv("admonition-content-copy"); 80 | setIcon(copy, "copy"); 81 | copy.addEventListener("click", () => { 82 | navigator.clipboard 83 | .writeText( 84 | text 85 | .split("\n") 86 | .slice(lineStart + 1, lineEnd + 1) 87 | .join("\n") 88 | .replace(/^> /gm, "") 89 | ) 90 | .then(async () => { 91 | new Notice("Callout content copied to clipboard."); 92 | }); 93 | }); 94 | } 95 | } 96 | 97 | if (admonition.noTitle && !callout.dataset.calloutFold) { 98 | if ( 99 | titleEl 100 | .querySelector(".callout-title-inner") 101 | ?.textContent?.toLowerCase() === 102 | admonition.type.toLowerCase() 103 | ) { 104 | titleEl.addClass("no-title"); 105 | } 106 | } 107 | if ( 108 | !admonition.noTitle && 109 | this.plugin.data.autoCollapse && 110 | !callout.dataset.calloutFold 111 | ) { 112 | this.setCollapsible(callout); 113 | } 114 | 115 | if ( 116 | admonition.title && 117 | titleEl.textContent == 118 | type[0].toUpperCase() + type.slice(1).toLowerCase() 119 | ) { 120 | const titleContentEl = titleEl.querySelector( 121 | ".callout-title-inner" 122 | ); 123 | if (titleContentEl) { 124 | titleContentEl.setText(admonition.title); 125 | } 126 | } 127 | if (this.plugin.data.dropShadow) { 128 | callout.addClass("drop-shadow"); 129 | } 130 | } 131 | 132 | setCollapsible(callout: HTMLElement) { 133 | const titleEl = callout.querySelector(".callout-title"); 134 | const content = 135 | callout.querySelector(".callout-content"); 136 | 137 | if (!content) return; 138 | callout.addClass("is-collapsible"); 139 | if (this.plugin.data.defaultCollapseType == "closed") { 140 | callout.dataset.calloutFold = "-"; 141 | callout.addClass("is-collapsed"); 142 | } else { 143 | callout.dataset.calloutFold = "+"; 144 | } 145 | 146 | const iconEl = titleEl.createDiv("callout-fold"); 147 | 148 | setIcon(iconEl, "chevron-down"); 149 | 150 | let collapsed = callout.hasClass("is-collapsed"); 151 | 152 | this.getComputedHeights(content); 153 | 154 | if (collapsed) { 155 | for (const prop of this.heights) { 156 | content.style.setProperty(prop, "0px"); 157 | } 158 | } 159 | titleEl.onclick = (event: MouseEvent) => { 160 | this.collapse(callout, event); 161 | }; 162 | } 163 | 164 | collapse(callout: HTMLElement, event?: MouseEvent) { 165 | event?.preventDefault(); 166 | const content = 167 | callout.querySelector(".callout-content"); 168 | 169 | function transitionEnd(evt: TransitionEvent) { 170 | content.removeEventListener("transitionend", transitionEnd); 171 | content.style.removeProperty("transition"); 172 | } 173 | content.addEventListener("transitionend", transitionEnd); 174 | content.style.setProperty( 175 | "transition", 176 | "all 100ms cubic-bezier(.02, .01, .47, 1)" 177 | ); 178 | let collapsed = callout.hasClass("is-collapsed"); 179 | 180 | if (!event || event.button == 0) { 181 | const heights = this.getComputedHeights(content); 182 | for (const prop of this.heights) { 183 | content.style.setProperty( 184 | prop, 185 | collapsed ? heights[prop] : "0px" 186 | ); 187 | } 188 | 189 | callout.toggleClass("is-collapsed", !collapsed); 190 | } 191 | } 192 | 193 | getComputedHeights(el: HTMLDivElement): Heights { 194 | if (this.heightMap.has(el)) { 195 | return this.heightMap.get(el); 196 | } 197 | const style = getComputedStyle(el); 198 | const heights: Heights = {}; 199 | for (const key of this.heights) { 200 | heights[key] = style.getPropertyValue(key); 201 | } 202 | this.heightMap.set(el, heights); 203 | return heights; 204 | } 205 | generateCssString() { 206 | const sheet = [ 207 | `/* This snippet was auto-generated by the Admonitions plugin */\n\n` 208 | ]; 209 | for (const rule of Array.from(this.sheet.cssRules)) { 210 | sheet.push(rule.cssText); 211 | } 212 | return sheet.join("\n\n"); 213 | } 214 | addAdmonition(admonition: Admonition) { 215 | if (!admonition.icon) return; 216 | let rule: string; 217 | const color = 218 | admonition.injectColor ?? this.plugin.data.injectColor 219 | ? `--callout-color: ${admonition.color};` 220 | : ""; 221 | if (admonition.icon.type == "obsidian") { 222 | rule = `.callout[data-callout="${admonition.type.toLowerCase()}"] { 223 | ${color} 224 | --callout-icon: ${ 225 | admonition.icon.name 226 | }; /* Icon name from the Obsidian Icon Set */ 227 | }`; 228 | } else { 229 | rule = `.callout[data-callout="${admonition.type.toLowerCase()}"] { 230 | ${color} 231 | --callout-icon: "${( 232 | this.plugin.iconManager.getIconNode(admonition.icon)?.outerHTML ?? 233 | "" 234 | ) 235 | .replace(/(width|height)=(\\?"|')\d+(\\?"|')/g, "") 236 | .replace(/"/g, '\\"')}"; 237 | }`; 238 | } 239 | if (this.indexing.contains(admonition.type)) { 240 | this.sheet.deleteRule(this.indexing.indexOf(admonition.type)); 241 | } 242 | this.indexing = [ 243 | ...this.indexing.filter((type) => type != admonition.type), 244 | admonition.type 245 | ]; 246 | this.sheet.insertRule(rule, this.sheet.cssRules.length); 247 | this.updateSnippet(); 248 | } 249 | indexing: string[] = []; 250 | removeAdmonition(admonition: Admonition) { 251 | if (!this.indexing.contains(admonition.type)) return; 252 | const index = this.indexing.indexOf(admonition.type); 253 | this.sheet.deleteRule(index); 254 | this.indexing.splice(index, 1); 255 | this.updateSnippet(); 256 | } 257 | style = document.head.createEl("style", { 258 | attr: { id: "ADMONITIONS_CUSTOM_STYLE_SHEET" } 259 | }); 260 | get sheet() { 261 | return this.style.sheet; 262 | } 263 | 264 | unload() { 265 | this.style.detach(); 266 | } 267 | 268 | get snippetPath() { 269 | return this.plugin.app.customCss.getSnippetPath( 270 | this.plugin.data.snippetPath 271 | ); 272 | } 273 | setUseSnippet() { 274 | if (this.plugin.data.useSnippet) { 275 | this.updateSnippet(); 276 | } 277 | } 278 | async updateSnippet() { 279 | if (!this.plugin.data.useSnippet) return; 280 | if (await this.plugin.app.vault.adapter.exists(this.snippetPath)) { 281 | await this.plugin.app.vault.adapter.write( 282 | this.snippetPath, 283 | this.generateCssString() 284 | ); 285 | } else { 286 | await this.plugin.app.vault.create( 287 | this.snippetPath, 288 | this.generateCssString() 289 | ); 290 | } 291 | this.plugin.app.customCss.setCssEnabledStatus( 292 | this.plugin.data.snippetPath, 293 | true 294 | ); 295 | this.plugin.app.customCss.readSnippets(); 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /src/icons/manager.ts: -------------------------------------------------------------------------------- 1 | import { faCopy, far, IconPrefix } from "@fortawesome/free-regular-svg-icons"; 2 | import { fas } from "@fortawesome/free-solid-svg-icons"; 3 | import { fab } from "@fortawesome/free-brands-svg-icons"; 4 | import { 5 | IconDefinition, 6 | findIconDefinition, 7 | icon as getFAIcon, 8 | library 9 | } from "@fortawesome/fontawesome-svg-core"; 10 | 11 | import type { IconName } from "@fortawesome/fontawesome-svg-core"; 12 | 13 | /* import { RPG } from "./rpgawesome"; */ 14 | import type { AdmonitionIconDefinition, IconType } from "src/@types"; 15 | import type ObsidianAdmonition from "src/main"; 16 | import { getIconIds, Notice, setIcon } from "obsidian"; 17 | import { type DownloadableIconPack, DownloadableIcons } from "./packs"; 18 | 19 | export { type DownloadableIconPack, DownloadableIcons }; 20 | 21 | /** Load Font Awesome Library */ 22 | library.add(fas, far, fab, faCopy); 23 | 24 | export class IconManager { 25 | DOWNLOADED: { 26 | [key in DownloadableIconPack]?: Record; 27 | } = {}; 28 | FONT_AWESOME_MAP = new Map( 29 | [Object.values(fas), Object.values(far), Object.values(fab)] 30 | .flat() 31 | .map((i: IconDefinition) => { 32 | return [ 33 | i.iconName, 34 | { 35 | name: i.iconName, 36 | type: "font-awesome" as "font-awesome" 37 | } 38 | ]; 39 | }) 40 | ); 41 | constructor(public plugin: ObsidianAdmonition) {} 42 | async load() { 43 | for (const icon of this.plugin.data.icons) { 44 | const exists = await this.plugin.app.vault.adapter.exists( 45 | this.localIconPath(icon) 46 | ); 47 | if (!exists) { 48 | await this.downloadIcon(icon); 49 | } else { 50 | this.DOWNLOADED[icon] = JSON.parse( 51 | await this.plugin.app.vault.adapter.read( 52 | `${this.plugin.app.plugins.getPluginFolder()}/obsidian-admonition/${icon}.json` 53 | ) 54 | ); 55 | } 56 | } 57 | this.setIconDefinitions(); 58 | } 59 | iconDefinitions: AdmonitionIconDefinition[] = []; 60 | setIconDefinitions() { 61 | const downloaded: AdmonitionIconDefinition[] = []; 62 | for (const pack of this.plugin.data.icons) { 63 | if (!(pack in this.DOWNLOADED)) continue; 64 | const icons = this.DOWNLOADED[pack]; 65 | downloaded.push( 66 | ...Object.keys(icons).map((name) => { 67 | return { type: pack, name }; 68 | }) 69 | ); 70 | } 71 | this.iconDefinitions = [ 72 | ...(this.plugin.data.useFontAwesome 73 | ? this.FONT_AWESOME_MAP.values() 74 | : []), 75 | ...getIconIds().map((name) => { 76 | return { type: "obsidian" as IconType, name }; 77 | }), 78 | ...downloaded 79 | ]; 80 | } 81 | iconPath(pack: DownloadableIconPack) { 82 | return `https://raw.githubusercontent.com/valentine195/obsidian-admonition/master/icons/${pack}/icons.json`; 83 | } 84 | localIconPath(pack: DownloadableIconPack) { 85 | return `${this.plugin.app.plugins.getPluginFolder()}/obsidian-admonition/${pack}.json`; 86 | } 87 | async downloadIcon(pack: DownloadableIconPack) { 88 | try { 89 | const icons: Record = await ( 90 | await fetch(this.iconPath(pack)) 91 | ).json(); 92 | this.plugin.data.icons.push(pack); 93 | this.plugin.data.icons = [...new Set(this.plugin.data.icons)]; 94 | await this.plugin.app.vault.adapter.write( 95 | this.localIconPath(pack), 96 | JSON.stringify(icons) 97 | ); 98 | this.DOWNLOADED[pack] = icons; 99 | await this.plugin.saveSettings(); 100 | this.setIconDefinitions(); 101 | 102 | new Notice(`${DownloadableIcons[pack]} successfully downloaded.`); 103 | } catch (e) { 104 | console.error(e); 105 | new Notice("Could not download icon pack"); 106 | } 107 | } 108 | async removeIcon(pack: DownloadableIconPack) { 109 | await this.plugin.app.vault.adapter.remove(this.localIconPath(pack)); 110 | delete this.DOWNLOADED[pack]; 111 | this.plugin.data.icons.remove(pack); 112 | this.plugin.data.icons = [...new Set(this.plugin.data.icons)]; 113 | await this.plugin.saveSettings(); 114 | this.setIconDefinitions(); 115 | } 116 | getIconType(str: string): IconType { 117 | if (findIconDefinition({ iconName: str as IconName, prefix: "fas" })) 118 | return "font-awesome"; 119 | if (findIconDefinition({ iconName: str as IconName, prefix: "far" })) 120 | return "font-awesome"; 121 | if (findIconDefinition({ iconName: str as IconName, prefix: "fab" })) 122 | return "font-awesome"; 123 | if (getIconIds().includes(str)) { 124 | return "obsidian"; 125 | } 126 | for (const [pack, icons] of Object.entries(this.DOWNLOADED)) { 127 | if (str in icons) return pack as DownloadableIconPack; 128 | } 129 | } 130 | getIconModuleName(icon: AdmonitionIconDefinition) { 131 | if (icon.type === "font-awesome") return "Font Awesome"; 132 | if (icon.type === "obsidian") return "Obsidian Icon"; 133 | if (icon.type === "image") return; 134 | if (icon.type in DownloadableIcons) return DownloadableIcons[icon.type]; 135 | } 136 | getIconNode(icon: AdmonitionIconDefinition): Element { 137 | if (icon.type === "image") { 138 | const img = new Image(); 139 | img.src = icon.name; 140 | return img; 141 | } 142 | if (icon.type == "obsidian") { 143 | const el = createDiv(); 144 | setIcon(el, icon.name); 145 | return el; 146 | } 147 | if (this.DOWNLOADED[icon.type as DownloadableIconPack]?.[icon.name]) { 148 | const el = createDiv(); 149 | el.innerHTML = 150 | this.DOWNLOADED[icon.type as DownloadableIconPack]?.[icon.name]; 151 | return el.children[0]; 152 | } 153 | for (const prefix of ["fas", "far", "fab"] as IconPrefix[]) { 154 | const definition = findIconDefinition({ 155 | iconName: icon.name as IconName, 156 | prefix 157 | }); 158 | if (definition) return getFAIcon(definition).node[0]; 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/icons/packs.ts: -------------------------------------------------------------------------------- 1 | export type DownloadableIconPack = "octicons" | "rpg"; 2 | 3 | export const DownloadableIcons: Record = { 4 | octicons: "Octicons", 5 | rpg: "RPG Awesome" 6 | } as const; 7 | -------------------------------------------------------------------------------- /src/lang/README.md: -------------------------------------------------------------------------------- 1 | ## Localization 2 | 3 | The plugin has full localization support, and will attempt to load the current Obsidian locale. If one does not exist, it will fall back to English. 4 | 5 | ### Adding a new Locale 6 | 7 | New locales can be added by creating a pull request. Two things should be done in this pull request: 8 | 9 | 1. Create the locale in the `locales` folder by copying the `en.ts` file. This file should be given a name matching the string returned by `moment.locale()`. 10 | 2. Create the translation by editing the value of each property. 11 | 3. Add the import in `locales.ts`. 12 | 4. Add the language to the `localeMap` variable. 13 | 14 | -------------------------------------------------------------------------------- /src/lang/helpers.ts: -------------------------------------------------------------------------------- 1 | import { moment } from 'obsidian'; 2 | 3 | import ar from './locale/ar'; 4 | import cz from './locale/cz'; 5 | import da from './locale/da'; 6 | import de from './locale/de'; 7 | import en from './locale/en'; 8 | import enGB from './locale/en-gb'; 9 | import es from './locale/es'; 10 | import fr from './locale/fr'; 11 | import hi from './locale/hi'; 12 | import id from './locale/id'; 13 | import it from './locale/it'; 14 | import ja from './locale/ja'; 15 | import ko from './locale/ko'; 16 | import nl from './locale/nl'; 17 | import no from './locale/no'; 18 | import pl from './locale/pl'; 19 | import pt from './locale/pt'; 20 | import ptBR from './locale/pt-br'; 21 | import ro from './locale/ro'; 22 | import ru from './locale/ru'; 23 | import tr from './locale/tr'; 24 | import zhCN from './locale/zh-cn'; 25 | import zhTW from './locale/zh-tw'; 26 | 27 | const localeMap: { [k: string]: Partial } = { 28 | ar, 29 | cs: cz, 30 | da, 31 | de, 32 | en, 33 | 'en-gb': enGB, 34 | es, 35 | fr, 36 | hi, 37 | id, 38 | it, 39 | ja, 40 | ko, 41 | nl, 42 | nn: no, 43 | pl, 44 | pt, 45 | 'pt-br': ptBR, 46 | ro, 47 | ru, 48 | tr, 49 | 'zh-cn': zhCN, 50 | 'zh-tw': zhTW, 51 | }; 52 | 53 | const locale = localeMap[moment.locale()]; 54 | 55 | export function t(str: keyof typeof en): string { 56 | return (locale && locale[str]) || en[str]; 57 | } 58 | -------------------------------------------------------------------------------- /src/lang/locale/ar.ts: -------------------------------------------------------------------------------- 1 | // العربية 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/cz.ts: -------------------------------------------------------------------------------- 1 | // čeština 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/da.ts: -------------------------------------------------------------------------------- 1 | // Dansk 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/de.ts: -------------------------------------------------------------------------------- 1 | // Deutsch 2 | 3 | export default {}; -------------------------------------------------------------------------------- /src/lang/locale/en-gb.ts: -------------------------------------------------------------------------------- 1 | // British English 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/en.ts: -------------------------------------------------------------------------------- 1 | // English 2 | 3 | export default { 4 | // setting.ts 5 | "Admonition Settings": "Admonition Settings", 6 | "Use Obsidian's markdown syntax highlighter in admonition code blocks. This setting is experimental and could cause errors.": 7 | "Use Obsidian's markdown syntax highlighter in admonition code blocks. This setting is experimental and could cause errors.", 8 | "Markdown Syntax Highlighting": "Markdown Syntax Highlighting", 9 | "Try to sync internal links to the metadata cache to display in graph view. This setting could have unintended consequences. Use at your own risk.": 10 | "Try to sync internal links to the metadata cache to display in graph view. This setting could have unintended consequences. Use at your own risk.", 11 | " Sync Links to Metadata Cache": " Sync Links to Metadata Cache", 12 | "Allows admonitions to be created using ": 13 | "Allows admonitions to be created using ", 14 | " or ": " or ", 15 | ", instead of using a code block.": ", instead of using a code block.", 16 | " Enable Non-codeblock Admonitions": " Enable Non-codeblock Admonitions", 17 | "Collapsible by Default": "Collapsible by Default", 18 | "All admonitions will be collapsible by default. Use ": 19 | "All admonitions will be collapsible by default. Use ", 20 | " to prevent.": " to prevent.", 21 | "Default Collapse Type": "Default Collapse Type", 22 | "Collapsible admonitions will be either opened or closed.": 23 | "Collapsible admonitions will be either opened or closed.", 24 | "Add Copy Button": "Add Copy Button", 25 | "Add a 'copy content' button to admonitions.": 26 | "Add a 'copy content' button to admonitions.", 27 | "Add New": "Add New", 28 | "Add a new Admonition type.": "Add a new Admonition type.", 29 | "Add Additional": "Add Additional", 30 | "Register Commands": "Register Commands", 31 | "Unregister Commands": "Unregister Commands", 32 | Edit: "Edit", 33 | Delete: "Delete", 34 | "Admonition Type": "Admonition Type", 35 | "Admonition Title": "Admonition Title", 36 | "This will be the default title for this admonition type.": 37 | "This will be the default title for this admonition type.", 38 | "Admonition type cannot be empty.": "Admonition type cannot be empty.", 39 | "Admonition type cannot include spaces.": 40 | "Admonition type cannot include spaces.", 41 | "Types must be a valid CSS selector.": 42 | "Types must be a valid CSS selector.", 43 | "Invalid icon name.": "Invalid icon name.", 44 | "Icon cannot be empty.": "Icon cannot be empty.", 45 | "Upload Image": "Upload Image", 46 | "There was an error parsing the image.": 47 | "There was an error parsing the image.", 48 | "Admonition Icon": "Admonition Icon", 49 | Color: "Color", 50 | Save: "Save", 51 | "No Admonition Title by Default": "No Admonition Title by Default", 52 | "The admonition will have no title unless ": 53 | "The admonition will have no title unless ", 54 | " is explicitly provided.": " is explicitly provided.", 55 | "Show Copy Button": "Show Copy Button", 56 | "A copy button will be added to the admonition.": 57 | "A copy button will be added to the admonition.", 58 | "Parse Titles as Markdown": "Parse Titles as Markdown", 59 | "Admonition Titles will be rendered as markdown.": 60 | "Admonition Titles will be rendered as markdown." 61 | }; 62 | -------------------------------------------------------------------------------- /src/lang/locale/es.ts: -------------------------------------------------------------------------------- 1 | // Español 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/fr.ts: -------------------------------------------------------------------------------- 1 | // français 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/hi.ts: -------------------------------------------------------------------------------- 1 | // हिन्दी 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/id.ts: -------------------------------------------------------------------------------- 1 | // Bahasa Indonesia 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/it.ts: -------------------------------------------------------------------------------- 1 | // Italiano 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/ja.ts: -------------------------------------------------------------------------------- 1 | // 日本語 2 | 3 | export default {}; -------------------------------------------------------------------------------- /src/lang/locale/ko.ts: -------------------------------------------------------------------------------- 1 | // 한국어 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/nl.ts: -------------------------------------------------------------------------------- 1 | // Nederlands 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/no.ts: -------------------------------------------------------------------------------- 1 | // Norsk 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/pl.ts: -------------------------------------------------------------------------------- 1 | // język polski 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/pt-br.ts: -------------------------------------------------------------------------------- 1 | // Português do Brasil 2 | // Brazilian Portuguese 3 | 4 | export default {}; -------------------------------------------------------------------------------- /src/lang/locale/pt.ts: -------------------------------------------------------------------------------- 1 | // Português 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/ro.ts: -------------------------------------------------------------------------------- 1 | // Română 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/ru.ts: -------------------------------------------------------------------------------- 1 | // русский 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/tr.ts: -------------------------------------------------------------------------------- 1 | // Türkçe 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/lang/locale/zh-cn.ts: -------------------------------------------------------------------------------- 1 | // 简体中文 2 | 3 | export default { 4 | // setting.ts 5 | "Admonition Settings": "Admonition 设置", 6 | "Use Obsidian's markdown syntax highlighter in admonition code blocks. This setting is experimental and could cause errors.": 7 | "在 admonition 代码块中使用 Obsidian 的 markdown 语法高亮。该设置为实验性选项,可能会引发错误。", 8 | "Markdown Syntax Highlighting": "Markdown 语法高亮", 9 | "Try to sync internal links to the metadata cache to display in graph view. This setting could have unintended consequences. Use at your own risk.": 10 | "尝试将内部链接同步至 metadata 缓存,以便在图形视图中显示。这个设置可能会产生意想不到的后果。使用时请自行承担风险。", 11 | " Sync Links to Metadata Cache": " 同步链接至 metadata 缓存", 12 | "Allows admonitions to be created using ": 13 | "允许 admonitions 创建使用,", 14 | " or ": " 或 ", 15 | ", instead of using a code block.": ",而非使用代码块。", 16 | " Enable Non-codeblock Admonitions": " 允许非代码块的 Admonitions", 17 | "Collapsible by Default": "默认折叠", 18 | "All admonitions will be collapsible by default. Use ": 19 | "所有的 admonitions 默认可折叠。使用 ", 20 | " to prevent.": " 来阻止。", 21 | "Default Collapse Type": "默认可折叠类型", 22 | "Collapsible admonitions will be either opened or closed.": 23 | "可折叠的 admonitions 将是打开的或是关闭的。", 24 | "Add Copy Button": "添加复制按钮", 25 | "Add a 'copy content' button to admonitions.": 26 | "为 admonitions 新增一个“复制内容”按钮。", 27 | "Add New": "新增", 28 | "Add a new Admonition type.": "添加一个新的 Admonition 类型。", 29 | "Add Additional": "额外添加", 30 | "Register Commands": "注册命令", 31 | "Unregister Commands": "注销命令", 32 | Edit: "编辑", 33 | Delete: "删除", 34 | "Admonition Type": "Admonition 类型", 35 | "Admonition Title": "Admonition 名称", 36 | "This will be the default title for this admonition type.": 37 | "这将会成为该类型 admonition 的默认名称。", 38 | "Admonition type cannot be empty.": "Admonition 类型不能为空。", 39 | "Admonition type cannot include spaces.": 40 | "Admonition 类型不能包含空格", 41 | "Types must be a valid CSS selector.": 42 | "类型必须为合法的 CSS 选择器。", 43 | "Invalid icon name.": "非法图标名称。", 44 | "Icon cannot be empty.": "图标不能为空。", 45 | "Upload Image": "上传图像", 46 | "There was an error parsing the image.": 47 | "上传图像时出错了。", 48 | "Admonition Icon": "Admonition 图标", 49 | Color: "颜色", 50 | Save: "保存", 51 | "No Admonition Title by Default": "无默认 Admonition 名称", 52 | "The admonition will have no title unless ": 53 | "该 admonition 将没有名称,除非 ", 54 | " is explicitly provided.": " 是明确规定的。", 55 | "Show Copy Button": "显示复制按钮", 56 | "A copy button will be added to the admonition.": 57 | "一个复制按钮将被添加至该 admonition ", 58 | "Parse Titles as Markdown": "将名称转变为 Markdown", 59 | "Admonition Titles will be rendered as markdown.": 60 | "Admonition 名称将以 markdown 形式呈现。" 61 | }; 62 | -------------------------------------------------------------------------------- /src/lang/locale/zh-tw.ts: -------------------------------------------------------------------------------- 1 | // 繁體中文 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { 2 | addIcon, 3 | Component, 4 | editorLivePreviewField, 5 | MarkdownPostProcessor, 6 | MarkdownPostProcessorContext, 7 | MarkdownPreviewRenderer, 8 | MarkdownRenderChild, 9 | MarkdownRenderer, 10 | MarkdownView, 11 | Notice, 12 | Plugin, 13 | setIcon 14 | } from "obsidian"; 15 | 16 | import { 17 | Admonition, 18 | AdmonitionSettings, 19 | AdmonitionIconDefinition 20 | } from "./@types"; 21 | import { 22 | getParametersFromSource, 23 | SPIN_ICON, 24 | SPIN_ICON_NAME, 25 | WARNING_ICON, 26 | WARNING_ICON_NAME 27 | } from "./util"; 28 | import { 29 | ADMONITION_MAP, 30 | ADD_ADMONITION_COMMAND_ICON, 31 | REMOVE_ADMONITION_COMMAND_ICON, 32 | ADD_COMMAND_NAME, 33 | REMOVE_COMMAND_NAME 34 | } from "./util"; 35 | 36 | import type codemirror from "codemirror"; 37 | 38 | declare global { 39 | interface Window { 40 | CodeMirror: typeof codemirror; 41 | } 42 | } 43 | 44 | //add commands to app interface 45 | 46 | declare module "obsidian" { 47 | interface App { 48 | commands: { 49 | commands: { [id: string]: Command }; 50 | editorCommands: { [id: string]: Command }; 51 | findCommand(id: string): Command; 52 | executeCommandById(id: string): void; 53 | listCommands(): Command[]; 54 | executeCommandById(id: string): void; 55 | findCommand(id: string): Command; 56 | }; 57 | customCss: { 58 | getSnippetPath(file: string): string; 59 | readSnippets(): void; 60 | setCssEnabledStatus(snippet: string, enabled: boolean): void; 61 | }; 62 | plugins: { 63 | getPluginFolder(): string; 64 | }; 65 | } 66 | interface MarkdownPreviewView { 67 | renderer: MarkdownPreviewRenderer; 68 | } 69 | interface MarkdownPreviewRenderer { 70 | onCheckboxClick: (evt: MouseEvent, el: HTMLInputElement) => void; 71 | } 72 | namespace MarkdownPreviewRenderer { 73 | function unregisterCodeBlockPostProcessor(lang: string): void; 74 | } 75 | interface Editor { 76 | cm: { 77 | state: EditorState; 78 | focus: () => void; 79 | posAtDOM: (el: HTMLElement) => number; 80 | dispatch: (tr: TransactionSpec) => void; 81 | }; 82 | } 83 | interface Workspace { 84 | iterateCodeMirrors(callback: (cm: CodeMirror.Editor) => void): void; 85 | } 86 | } 87 | 88 | import AdmonitionSetting from "./settings"; 89 | import { DownloadableIconPack, IconManager } from "./icons/manager"; 90 | import { InsertAdmonitionModal } from "./modal"; 91 | import { IconName } from "@fortawesome/fontawesome-svg-core"; 92 | import CalloutManager from "./callout/manager"; 93 | import { AdmonitionSuggest } from "./suggest/suggest"; 94 | import { EditorState, TransactionSpec } from "@codemirror/state"; 95 | import CodeMirror from "codemirror"; 96 | 97 | const DEFAULT_APP_SETTINGS: AdmonitionSettings = { 98 | userAdmonitions: {}, 99 | syntaxHighlight: false, 100 | copyButton: false, 101 | version: "", 102 | autoCollapse: false, 103 | defaultCollapseType: "open", 104 | injectColor: true, 105 | parseTitles: true, 106 | dropShadow: true, 107 | hideEmpty: false, 108 | open: { 109 | admonitions: true, 110 | icons: true, 111 | other: true, 112 | advanced: false 113 | }, 114 | icons: [], 115 | useFontAwesome: true, 116 | rpgDownloadedOnce: false, 117 | msDocConverted: false, 118 | useSnippet: false, 119 | snippetPath: `custom-admonitions.${[...Array(6).keys()] 120 | .map(() => ((16 * Math.random()) | 0).toString(16)) 121 | .join("")}` 122 | }; 123 | 124 | export default class ObsidianAdmonition extends Plugin { 125 | data: AdmonitionSettings; 126 | 127 | postprocessors: Map = new Map(); 128 | 129 | iconManager = new IconManager(this); 130 | calloutManager: CalloutManager; 131 | 132 | get types() { 133 | return Object.keys(this.admonitions); 134 | } 135 | get admonitionArray() { 136 | return Object.keys(this.admonitions).map((key) => { 137 | return { 138 | ...this.admonitions[key], 139 | type: key 140 | }; 141 | }); 142 | } 143 | 144 | async onload(): Promise { 145 | console.log("Obsidian Admonition loaded"); 146 | 147 | this.postprocessors = new Map(); 148 | 149 | await this.loadSettings(); 150 | await this.iconManager.load(); 151 | this.app.workspace.onLayoutReady(async () => { 152 | this.addChild((this.calloutManager = new CalloutManager(this))); 153 | 154 | this.registerEditorSuggest(new AdmonitionSuggest(this)); 155 | 156 | Object.keys(this.admonitions).forEach((type) => { 157 | this.registerType(type); 158 | }); 159 | 160 | this.addSettingTab(new AdmonitionSetting(this.app, this)); 161 | 162 | addIcon(ADD_COMMAND_NAME, ADD_ADMONITION_COMMAND_ICON); 163 | addIcon(REMOVE_COMMAND_NAME, REMOVE_ADMONITION_COMMAND_ICON); 164 | addIcon(WARNING_ICON_NAME, WARNING_ICON); 165 | addIcon(SPIN_ICON_NAME, SPIN_ICON); 166 | 167 | /** Add generic commands. */ 168 | this.addCommand({ 169 | id: "collapse-admonitions", 170 | name: "Collapse Admonitions in Note", 171 | checkCallback: (checking) => { 172 | // checking if the command should appear in the Command Palette 173 | if (checking) { 174 | // make sure the active view is a MarkdownView. 175 | return !!this.app.workspace.getActiveViewOfType( 176 | MarkdownView 177 | ); 178 | } 179 | let view = 180 | this.app.workspace.getActiveViewOfType(MarkdownView); 181 | if (!view || !(view instanceof MarkdownView)) return; 182 | 183 | let admonitions = 184 | view.contentEl.querySelectorAll( 185 | ".callout.is-collapsible:not(.is-collapsed)" 186 | ); 187 | for (let i = 0; i < admonitions.length; i++) { 188 | let admonition = admonitions[i]; 189 | this.calloutManager.collapse(admonition); 190 | } 191 | } 192 | }); 193 | this.addCommand({ 194 | id: "open-admonitions", 195 | name: "Open Admonitions in Note", 196 | checkCallback: (checking) => { 197 | // checking if the command should appear in the Command Palette 198 | if (checking) { 199 | // make sure the active view is a MarkdownView. 200 | return !!this.app.workspace.getActiveViewOfType( 201 | MarkdownView 202 | ); 203 | } 204 | let view = 205 | this.app.workspace.getActiveViewOfType(MarkdownView); 206 | if (!view || !(view instanceof MarkdownView)) return; 207 | 208 | let admonitions = 209 | view.contentEl.querySelectorAll( 210 | ".callout.is-collapsible.is-collapsed" 211 | ); 212 | for (let i = 0; i < admonitions.length; i++) { 213 | let admonition = admonitions[i]; 214 | 215 | this.calloutManager.collapse(admonition); 216 | } 217 | } 218 | }); 219 | this.addCommand({ 220 | id: "insert-admonition", 221 | name: "Insert Admonition", 222 | editorCallback: (editor, view) => { 223 | let suggestor = new InsertAdmonitionModal(this); 224 | suggestor.onClose = () => { 225 | if (!suggestor.insert) return; 226 | let titleLine = "", 227 | collapseLine = ""; 228 | if ( 229 | suggestor.title.length && 230 | suggestor.title.toLowerCase() != 231 | suggestor.type.toLowerCase() 232 | ) { 233 | titleLine = `title: ${suggestor.title}\n`; 234 | } 235 | if ( 236 | (this.data.autoCollapse && 237 | suggestor.collapse != 238 | this.data.defaultCollapseType) || 239 | (!this.data.autoCollapse && 240 | suggestor.collapse != "none") 241 | ) { 242 | collapseLine = `collapse: ${suggestor.collapse}\n`; 243 | } 244 | editor.getDoc().replaceSelection( 245 | `\`\`\`ad-${ 246 | suggestor.type 247 | }\n${titleLine}${collapseLine} 248 | ${editor.getDoc().getSelection()} 249 | \`\`\`\n` 250 | ); 251 | const cursor = editor.getCursor(); 252 | editor.setCursor(cursor.line - 3); 253 | }; 254 | suggestor.open(); 255 | } 256 | }); 257 | this.addCommand({ 258 | id: "insert-callout", 259 | name: "Insert Callout", 260 | editorCallback: (editor, view) => { 261 | let suggestor = new InsertAdmonitionModal(this); 262 | suggestor.onClose = () => { 263 | if (!suggestor.insert) return; 264 | let title = "", 265 | collapse = ""; 266 | if ( 267 | (this.data.autoCollapse && 268 | suggestor.collapse != 269 | this.data.defaultCollapseType) || 270 | (!this.data.autoCollapse && 271 | suggestor.collapse != "none") 272 | ) { 273 | switch (suggestor.collapse) { 274 | case "open": { 275 | collapse = "+"; 276 | break; 277 | } 278 | case "closed": { 279 | collapse = "-"; 280 | break; 281 | } 282 | } 283 | } 284 | if ( 285 | suggestor.title.length && 286 | suggestor.title.toLowerCase() != 287 | suggestor.type.toLowerCase() 288 | ) { 289 | title = ` ${suggestor.title}`; 290 | } 291 | const selection = editor.getDoc().getSelection(); 292 | editor.getDoc().replaceSelection( 293 | `> [!${suggestor.type}]${collapse}${title} 294 | > ${selection.split("\n").join("\n> ")} 295 | ` 296 | ); 297 | }; 298 | suggestor.open(); 299 | } 300 | }); 301 | }); 302 | } 303 | async downloadIcon(pack: DownloadableIconPack) { 304 | this.iconManager.downloadIcon(pack); 305 | } 306 | 307 | async removeIcon(pack: DownloadableIconPack) { 308 | this.iconManager.removeIcon(pack); 309 | } 310 | 311 | async postprocessor( 312 | type: string, 313 | src: string, 314 | el: HTMLElement, 315 | ctx?: MarkdownPostProcessorContext 316 | ) { 317 | if (!this.admonitions[type]) { 318 | return; 319 | } 320 | try { 321 | const sourcePath = 322 | typeof ctx == "string" 323 | ? ctx 324 | : ctx?.sourcePath ?? 325 | this.app.workspace.getActiveFile()?.path ?? 326 | ""; 327 | let { title, collapse, content, icon, color, metadata } = 328 | getParametersFromSource(type, src, this.admonitions[type]); 329 | 330 | if (this.data.autoCollapse && !collapse) { 331 | collapse = this.data.defaultCollapseType ?? "open"; 332 | } else if (collapse && collapse.trim() === "none") { 333 | collapse = ""; 334 | } 335 | 336 | /* const iconNode = icon ? this.admonitions[type].icon; */ 337 | const admonition = this.admonitions[type]; 338 | let admonitionElement = this.getAdmonitionElement( 339 | type, 340 | title, 341 | this.iconManager.iconDefinitions.find( 342 | ({ name }) => icon === name 343 | ) ?? admonition.icon, 344 | color ?? 345 | (admonition.injectColor ?? this.data.injectColor 346 | ? admonition.color 347 | : null), 348 | collapse, 349 | sourcePath, 350 | metadata 351 | ); 352 | this.renderAdmonitionContent( 353 | admonitionElement, 354 | type, 355 | content, 356 | ctx, 357 | sourcePath, 358 | src 359 | ); 360 | if (collapse && collapse != "none") { 361 | this.calloutManager.setCollapsible(admonitionElement); 362 | } 363 | /** 364 | * Replace the
 tag with the new admonition.
365 |              */
366 |             const parent = el.parentElement;
367 |             if (parent) {
368 |                 parent.addClass(
369 |                     "admonition-parent",
370 |                     `admonition-${type}-parent`
371 |                 );
372 |             }
373 |             el.replaceWith(admonitionElement);
374 | 
375 |             const view = app.workspace.getActiveViewOfType(MarkdownView);
376 |             if (view?.editor?.cm?.state?.field(editorLivePreviewField)) {
377 |                 const editor = view.editor.cm;
378 |                 admonitionElement.onClickEvent((ev) => {
379 |                     if (ev.defaultPrevented || ev.detail > 1 || ev.shiftKey)
380 |                         return;
381 |                     try {
382 |                         setTimeout(() => {
383 |                             try {
384 |                                 const pos = editor.posAtDOM(admonitionElement);
385 |                                 editor.focus();
386 |                                 editor.dispatch({
387 |                                     selection: {
388 |                                         head: pos,
389 |                                         anchor: pos
390 |                                     }
391 |                                 });
392 |                             } catch (e) {}
393 |                         }, 10);
394 |                     } catch (e) {}
395 |                 });
396 |             }
397 | 
398 |             return admonitionElement;
399 |         } catch (e) {
400 |             console.error(e);
401 |             const pre = createEl("pre");
402 | 
403 |             pre.createEl("code", {
404 |                 attr: {
405 |                     style: `color: var(--text-error) !important`
406 |                 }
407 |             }).createSpan({
408 |                 text:
409 |                     "There was an error rendering the admonition:" +
410 |                     "\n\n" +
411 |                     src
412 |             });
413 | 
414 |             el.replaceWith(pre);
415 |         }
416 |     }
417 | 
418 |     /**
419 |      *  .callout.admonition.is-collapsible.is-collapsed
420 |      *      .callout-title
421 |      *          .callout-icon
422 |      *          .callout-title-inner
423 |      *          .callout-fold
424 |      *      .callout-content
425 |      */
426 |     getAdmonitionElement(
427 |         type: string,
428 |         title: string,
429 |         icon: AdmonitionIconDefinition,
430 |         color?: string,
431 |         collapse?: string,
432 |         source?: string,
433 |         metadata?: string
434 |     ): HTMLElement {
435 |         const admonition = createDiv({
436 |             cls: `callout admonition admonition-${type} admonition-plugin ${
437 |                 !title?.trim().length ? "no-title" : ""
438 |             }`,
439 |             attr: {
440 |                 style: color ? `--callout-color: ${color};` : "",
441 |                 "data-callout": type,
442 |                 "data-callout-fold": "",
443 |                 "data-callout-metadata": metadata ?? ""
444 |             }
445 |         });
446 |         const titleEl = admonition.createDiv({
447 |             cls: `callout-title admonition-title ${
448 |                 !title?.trim().length ? "no-title" : ""
449 |             }`
450 |         });
451 | 
452 |         if (title && title.trim().length) {
453 |             //build icon element
454 |             const iconEl = titleEl.createDiv(
455 |                 "callout-icon admonition-title-icon"
456 |             );
457 |             if (icon && icon.name && icon.type) {
458 |                 iconEl.appendChild(
459 |                     this.iconManager.getIconNode(icon) ?? createDiv()
460 |                 );
461 |             }
462 | 
463 |             //get markdown
464 |             const titleInnerEl = titleEl.createDiv(
465 |                 "callout-title-inner admonition-title-content"
466 |             );
467 |             MarkdownRenderer.render(
468 |                 this.app,
469 |                 title,
470 |                 titleInnerEl,
471 |                 source ?? "",
472 |                 this
473 |             );
474 |             if (
475 |                 titleInnerEl.firstElementChild &&
476 |                 titleInnerEl.firstElementChild instanceof HTMLParagraphElement
477 |             ) {
478 |                 titleInnerEl.setChildrenInPlace(
479 |                     Array.from(titleInnerEl.firstElementChild.childNodes)
480 |                 );
481 |             }
482 |         }
483 | 
484 |         //add them to title element
485 | 
486 |         if (collapse) {
487 |             admonition.addClass("is-collapsible");
488 |             if (collapse == "closed") {
489 |                 admonition.addClass("is-collapsed");
490 |             }
491 |         }
492 |         if (!this.data.dropShadow) {
493 |             admonition.addClass("no-drop");
494 |         }
495 |         return admonition;
496 |     }
497 | 
498 |     renderAdmonitionContent(
499 |         admonitionElement: HTMLElement,
500 |         type: string,
501 |         content: string,
502 |         ctx: MarkdownPostProcessorContext,
503 |         sourcePath: string,
504 |         src: string
505 |     ) {
506 |         let markdownRenderChild = new MarkdownRenderChild(admonitionElement);
507 |         markdownRenderChild.containerEl = admonitionElement;
508 |         if (ctx && !(typeof ctx == "string")) {
509 |             ctx.addChild(markdownRenderChild);
510 |         }
511 | 
512 |         if (content && content?.trim().length) {
513 |             /**
514 |              * Render the content as markdown and append it to the admonition.
515 |              */
516 |             const contentEl = this.getAdmonitionContentElement(
517 |                 type,
518 |                 admonitionElement,
519 |                 content
520 |             );
521 |             if (/^`{3,}mermaid/m.test(content)) {
522 |                 const wasCollapsed = !admonitionElement.hasAttribute("open");
523 |                 if (admonitionElement instanceof HTMLDetailsElement) {
524 |                     admonitionElement.setAttribute("open", "open");
525 |                 }
526 |                 setImmediate(() => {
527 |                     MarkdownRenderer.renderMarkdown(
528 |                         content,
529 |                         contentEl,
530 |                         sourcePath,
531 |                         markdownRenderChild
532 |                     );
533 |                     if (
534 |                         admonitionElement instanceof HTMLDetailsElement &&
535 |                         wasCollapsed
536 |                     ) {
537 |                         admonitionElement.removeAttribute("open");
538 |                     }
539 |                 });
540 |             } else {
541 |                 MarkdownRenderer.renderMarkdown(
542 |                     content,
543 |                     contentEl,
544 |                     sourcePath,
545 |                     markdownRenderChild
546 |                 );
547 |             }
548 | 
549 |             if (
550 |                 (!content.length || contentEl.textContent.trim() == "") &&
551 |                 this.data.hideEmpty
552 |             )
553 |                 admonitionElement.addClass("no-content");
554 | 
555 |             const taskLists = contentEl.querySelectorAll(
556 |                 ".task-list-item-checkbox"
557 |             );
558 |             if (taskLists?.length) {
559 |                 const split = src.split("\n");
560 |                 let slicer = 0;
561 |                 taskLists.forEach((task) => {
562 |                     const line = split
563 |                         .slice(slicer)
564 |                         .findIndex((l) => /^[ \t>]*\- \[.\]/.test(l));
565 | 
566 |                     if (line == -1) return;
567 |                     task.dataset.line = `${line + slicer + 1}`;
568 |                     slicer = line + slicer + 1;
569 |                 });
570 |             }
571 |         }
572 |     }
573 | 
574 |     getAdmonitionContentElement(
575 |         type: string,
576 |         admonitionElement: HTMLElement,
577 |         content: string
578 |     ) {
579 |         const contentEl = admonitionElement.createDiv(
580 |             "callout-content admonition-content"
581 |         );
582 |         if (this.admonitions[type].copy ?? this.data.copyButton) {
583 |             let copy = contentEl.createDiv("admonition-content-copy");
584 |             setIcon(copy, "copy");
585 |             copy.addEventListener("click", () => {
586 |                 navigator.clipboard.writeText(content.trim()).then(async () => {
587 |                     new Notice("Admonition content copied to clipboard.");
588 |                 });
589 |             });
590 |         }
591 |         return contentEl;
592 |     }
593 | 
594 |     registerType(type: string) {
595 |         /** Turn on CodeMirror syntax highlighting for this "language" */
596 |         if (this.data.syntaxHighlight) {
597 |             this.turnOnSyntaxHighlighting([type]);
598 |         }
599 | 
600 |         /** Register an admonition code-block post processor for legacy support. */
601 |         if (this.postprocessors.has(type)) {
602 |             MarkdownPreviewRenderer.unregisterCodeBlockPostProcessor(
603 |                 `ad-${type}`
604 |             );
605 |         }
606 |         this.postprocessors.set(
607 |             type,
608 |             this.registerMarkdownCodeBlockProcessor(
609 |                 `ad-${type}`,
610 |                 (src, el, ctx) => this.postprocessor(type, src, el, ctx)
611 |             )
612 |         );
613 |         const admonition = this.admonitions[type];
614 |         if (admonition.command) {
615 |             this.registerCommandsFor(admonition);
616 |         }
617 |     }
618 |     get admonitions() {
619 |         return { ...ADMONITION_MAP, ...this.data.userAdmonitions };
620 |     }
621 |     async addAdmonition(admonition: Admonition): Promise {
622 |         this.data.userAdmonitions = {
623 |             ...this.data.userAdmonitions,
624 |             [admonition.type]: admonition
625 |         };
626 | 
627 |         this.registerType(admonition.type);
628 | 
629 |         /** Create the admonition type in CSS */
630 |         this.calloutManager.addAdmonition(admonition);
631 | 
632 |         await this.saveSettings();
633 |     }
634 |     registerCommandsFor(admonition: Admonition) {
635 |         admonition.command = true;
636 |         this.addCommand({
637 |             id: `insert-${admonition.type}-callout`,
638 |             name: `Insert ${admonition.type} Callout`,
639 |             editorCheckCallback: (checking, editor, view) => {
640 |                 if (checking) return admonition.command;
641 |                 if (admonition.command) {
642 |                     try {
643 |                         const selection = editor.getDoc().getSelection();
644 |                         editor.getDoc().replaceSelection(
645 |                             `> [!${admonition.type}]
646 | > ${selection.split("\n").join("\n> ")}
647 | `
648 |                         );
649 |                         const cursor = editor.getCursor();
650 |                         editor.setCursor(cursor.line - 2);
651 |                     } catch (e) {
652 |                         new Notice(
653 |                             "There was an issue inserting the admonition."
654 |                         );
655 |                     }
656 |                 }
657 |             }
658 |         });
659 |         this.addCommand({
660 |             id: `insert-${admonition.type}`,
661 |             name: `Insert ${admonition.type}`,
662 |             editorCheckCallback: (checking, editor, view) => {
663 |                 if (checking) return admonition.command;
664 |                 if (admonition.command) {
665 |                     try {
666 |                         editor.getDoc().replaceSelection(
667 |                             `\`\`\`ad-${admonition.type}
668 | 
669 | ${editor.getDoc().getSelection()}
670 | 
671 | \`\`\`\n`
672 |                         );
673 |                         const cursor = editor.getCursor();
674 |                         editor.setCursor(cursor.line - 2);
675 |                     } catch (e) {
676 |                         new Notice(
677 |                             "There was an issue inserting the admonition."
678 |                         );
679 |                     }
680 |                 }
681 |             }
682 |         });
683 |         this.addCommand({
684 |             id: `insert-${admonition.type}-with-title`,
685 |             name: `Insert ${admonition.type} With Title`,
686 |             editorCheckCallback: (checking, editor, view) => {
687 |                 if (checking) return admonition.command;
688 |                 if (admonition.command) {
689 |                     try {
690 |                         const title = admonition.title ?? "";
691 |                         editor.getDoc().replaceSelection(
692 |                             `\`\`\`ad-${admonition.type}
693 | title: ${title}
694 | 
695 | ${editor.getDoc().getSelection()}
696 | 
697 | \`\`\`\n`
698 |                         );
699 |                         const cursor = editor.getCursor();
700 |                         editor.setCursor(cursor.line - 3);
701 |                     } catch (e) {
702 |                         new Notice(
703 |                             "There was an issue inserting the admonition."
704 |                         );
705 |                     }
706 |                 }
707 |             }
708 |         });
709 |     }
710 |     unregisterType(admonition: Admonition) {
711 |         if (this.data.syntaxHighlight) {
712 |             this.turnOffSyntaxHighlighting([admonition.type]);
713 |         }
714 | 
715 |         if (admonition.command) {
716 |             this.unregisterCommandsFor(admonition);
717 |         }
718 | 
719 |         if (this.postprocessors.has(admonition.type)) {
720 |             MarkdownPreviewRenderer.unregisterPostProcessor(
721 |                 this.postprocessors.get(admonition.type)
722 |             );
723 |             MarkdownPreviewRenderer.unregisterCodeBlockPostProcessor(
724 |                 `ad-${admonition.type}`
725 |             );
726 |             this.postprocessors.delete(admonition.type);
727 |         }
728 |     }
729 |     async removeAdmonition(admonition: Admonition) {
730 |         if (this.data.userAdmonitions[admonition.type]) {
731 |             delete this.data.userAdmonitions[admonition.type];
732 |         }
733 | 
734 |         this.unregisterType(admonition);
735 | 
736 |         /** Remove the admonition type in CSS */
737 |         this.calloutManager.removeAdmonition(admonition);
738 | 
739 |         await this.saveSettings();
740 |     }
741 |     unregisterCommandsFor(admonition: Admonition) {
742 |         admonition.command = false;
743 | 
744 |         if (
745 |             this.app.commands.findCommand(
746 |                 `obsidian-admonition:insert-${admonition.type}`
747 |             )
748 |         ) {
749 |             delete this.app.commands.editorCommands[
750 |                 `obsidian-admonition:insert-${admonition.type}`
751 |             ];
752 |             delete this.app.commands.editorCommands[
753 |                 `obsidian-admonition:insert-${admonition.type}-with-title`
754 |             ];
755 |             delete this.app.commands.commands[
756 |                 `obsidian-admonition:insert-${admonition.type}`
757 |             ];
758 |             delete this.app.commands.commands[
759 |                 `obsidian-admonition:insert-${admonition.type}-with-title`
760 |             ];
761 |         }
762 |     }
763 | 
764 |     async saveSettings() {
765 |         this.data.version = this.manifest.version;
766 | 
767 |         await this.saveData(this.data);
768 |     }
769 |     async loadSettings() {
770 |         const loaded: AdmonitionSettings = await this.loadData();
771 |         this.data = Object.assign({}, DEFAULT_APP_SETTINGS, loaded);
772 | 
773 |         if (this.data.userAdmonitions) {
774 |             if (
775 |                 !this.data.version ||
776 |                 Number(this.data.version.split(".")[0]) < 5
777 |             ) {
778 |                 for (let admonition in this.data.userAdmonitions) {
779 |                     if (
780 |                         Object.prototype.hasOwnProperty.call(
781 |                             this.data.userAdmonitions[admonition],
782 |                             "type"
783 |                         )
784 |                     )
785 |                         continue;
786 |                     this.data.userAdmonitions[admonition] = {
787 |                         ...this.data.userAdmonitions[admonition],
788 |                         icon: {
789 |                             type: "font-awesome",
790 |                             name: this.data.userAdmonitions[admonition]
791 |                                 .icon as unknown as IconName
792 |                         }
793 |                     };
794 |                 }
795 |             }
796 | 
797 |             if (
798 |                 !this.data.version ||
799 |                 Number(this.data.version.split(".")[0]) < 8
800 |             ) {
801 |                 new Notice(
802 |                     createFragment((e) => {
803 |                         e.createSpan({
804 |                             text: "Admonitions: Obsidian now has native support for callouts! Check out the "
805 |                         });
806 | 
807 |                         e.createEl("a", {
808 |                             text: "Admonitions ReadMe",
809 |                             href: "obsidian://show-plugin?id=obsidian-admonition"
810 |                         });
811 | 
812 |                         e.createSpan({
813 |                             text: " for what that means for Admonitions going forward."
814 |                         });
815 |                     }),
816 |                     0
817 |                 );
818 |             }
819 |         }
820 | 
821 |         if (
822 |             !this.data.rpgDownloadedOnce &&
823 |             this.data.userAdmonitions &&
824 |             Object.values(this.data.userAdmonitions).some((admonition) => {
825 |                 if (admonition.icon.type == "rpg") return true;
826 |             }) &&
827 |             !this.data.icons.includes("rpg")
828 |         ) {
829 |             try {
830 |                 await this.downloadIcon("rpg");
831 |                 this.data.rpgDownloadedOnce = true;
832 |             } catch (e) {}
833 |         }
834 | 
835 |         await this.saveSettings();
836 |     }
837 | 
838 |     turnOnSyntaxHighlighting(types: string[] = Object.keys(this.admonitions)) {
839 |         if (!this.data.syntaxHighlight) return;
840 |         types.forEach((type) => {
841 |             if (this.data.syntaxHighlight) {
842 |                 /** Process from @deathau's syntax highlight plugin */
843 |                 const [, cmPatchedType] = `${type}`.match(
844 |                     /^([\w+#-]*)[^\n`]*$/
845 |                 );
846 |                 window.CodeMirror.defineMode(
847 |                     `ad-${cmPatchedType}`,
848 |                     (config, options) => {
849 |                         return window.CodeMirror.getMode({}, "hypermd");
850 |                     }
851 |                 );
852 |             }
853 |         });
854 | 
855 |         this.app.workspace.onLayoutReady(() =>
856 |             this.app.workspace.iterateCodeMirrors((cm) =>
857 |                 cm.setOption("mode", cm.getOption("mode"))
858 |             )
859 |         );
860 |     }
861 |     turnOffSyntaxHighlighting(types: string[] = Object.keys(this.admonitions)) {
862 |         types.forEach((type) => {
863 |             if (window.CodeMirror.modes.hasOwnProperty(`ad-${type}`)) {
864 |                 delete window.CodeMirror.modes[`ad-${type}`];
865 |             }
866 |         });
867 |         this.app.workspace.onLayoutReady(() =>
868 |             this.app.workspace.iterateCodeMirrors((cm) =>
869 |                 cm.setOption("mode", cm.getOption("mode"))
870 |             )
871 |         );
872 |     }
873 | 
874 |     async onunload() {
875 |         console.log("Obsidian Admonition unloaded");
876 |         this.postprocessors = null;
877 |         this.turnOffSyntaxHighlighting();
878 |     }
879 | }
880 | 


--------------------------------------------------------------------------------
/src/modal/confirm.ts:
--------------------------------------------------------------------------------
 1 | import { App, ButtonComponent, Modal } from "obsidian";
 2 | 
 3 | export async function confirmWithModal(
 4 |     app: App,
 5 |     text: string,
 6 |     buttons: { cta: string; secondary: string } = {
 7 |         cta: "Yes",
 8 |         secondary: "No"
 9 |     }
10 | ): Promise {
11 |     return new Promise((resolve, reject) => {
12 |         try {
13 |             const modal = new ConfirmModal(app, text, buttons);
14 |             modal.onClose = () => {
15 |                 resolve(modal.confirmed);
16 |             };
17 |             modal.open();
18 |         } catch (e) {
19 |             reject();
20 |         }
21 |     });
22 | }
23 | 
24 | export class ConfirmModal extends Modal {
25 |     constructor(
26 |         app: App,
27 |         public text: string,
28 |         public buttons: { cta: string; secondary: string }
29 |     ) {
30 |         super(app);
31 |     }
32 |     confirmed: boolean = false;
33 |     async display() {
34 |         this.contentEl.empty();
35 |         this.contentEl.addClass("confirm-modal");
36 |         this.contentEl.createEl("p", {
37 |             text: this.text
38 |         });
39 |         const buttonEl = this.contentEl.createDiv(
40 |             "fantasy-calendar-confirm-buttons"
41 |         );
42 |         new ButtonComponent(buttonEl)
43 |             .setButtonText(this.buttons.cta)
44 |             .setCta()
45 |             .onClick(() => {
46 |                 this.confirmed = true;
47 |                 this.close();
48 |             });
49 |         new ButtonComponent(buttonEl)
50 |             .setButtonText(this.buttons.secondary)
51 |             .onClick(() => {
52 |                 this.close();
53 |             });
54 |     }
55 |     onOpen() {
56 |         this.display();
57 |     }
58 | }
59 | 


--------------------------------------------------------------------------------
/src/modal/export.ts:
--------------------------------------------------------------------------------
 1 | import { Modal, Setting } from "obsidian";
 2 | import ObsidianAdmonition from "src/main";
 3 | 
 4 | export default class Export extends Modal {
 5 |     constructor(public plugin: ObsidianAdmonition) {
 6 |         super(app);
 7 |     }
 8 |     admonitionDefinitions = Object.values(this.plugin.data.userAdmonitions);
 9 | 
10 |     admonitionNames = Object.keys(this.plugin.data.userAdmonitions);
11 | 
12 |     selectedAdmonitions = [...this.admonitionNames];
13 | 
14 |     export = false;
15 | 
16 |     onOpen() {
17 |         this.titleEl.setText("Export Admonitions");
18 |         this.containerEl.addClasses([
19 |             "admonition-settings",
20 |             "admonition-modal",
21 |             "admonition-export-modal"
22 |         ]);
23 |         new Setting(this.contentEl).addButton((b) =>
24 |             b.setButtonText("Export Selected").onClick(() => {
25 |                 this.export = true;
26 |                 this.close();
27 |             })
28 |         );
29 |         let toggleEl: HTMLDivElement;
30 |         new Setting(this.contentEl)
31 |             .addButton((b) =>
32 |                 b
33 |                     .setButtonText("Select All")
34 |                     .setCta()
35 |                     .onClick(() => {
36 |                         this.selectedAdmonitions = [...this.admonitionNames];
37 |                         this.generateToggles(toggleEl);
38 |                     })
39 |             )
40 |             .addButton((b) =>
41 |                 b.setButtonText("Deselect All").onClick(() => {
42 |                     this.selectedAdmonitions = [];
43 |                     this.generateToggles(toggleEl);
44 |                 })
45 |             );
46 |         toggleEl = this.contentEl.createDiv("additional");
47 |         this.generateToggles(toggleEl);
48 |     }
49 | 
50 |     generateToggles(toggleEl: HTMLDivElement) {
51 |         toggleEl.empty();
52 |         for (const name of this.admonitionNames) {
53 |             new Setting(toggleEl).setName(name).addToggle((t) => {
54 |                 t.setValue(this.selectedAdmonitions.includes(name)).onChange(
55 |                     (v) => {
56 |                         if (v) {
57 |                             this.selectedAdmonitions.push(name);
58 |                         } else {
59 |                             this.selectedAdmonitions.remove(name);
60 |                         }
61 |                     }
62 |                 );
63 |             });
64 |         }
65 |     }
66 | }
67 | 


--------------------------------------------------------------------------------
/src/modal/index.ts:
--------------------------------------------------------------------------------
  1 | import {
  2 |     FuzzyMatch,
  3 |     Modal,
  4 |     Notice,
  5 |     SearchComponent,
  6 |     Setting,
  7 |     TextComponent,
  8 |     renderMatches,
  9 |     setIcon
 10 | } from "obsidian";
 11 | 
 12 | import { FuzzyInputSuggest } from "@javalent/utilities";
 13 | 
 14 | import { Admonition, AdmonitionIconDefinition } from "src/@types";
 15 | import ObsidianAdmonition from "src/main";
 16 | 
 17 | export class IconSuggestionModal extends FuzzyInputSuggest {
 18 |     constructor(
 19 |         public plugin: ObsidianAdmonition,
 20 |         input: TextComponent | SearchComponent,
 21 |         items: AdmonitionIconDefinition[]
 22 |     ) {
 23 |         super(plugin.app, input, items);
 24 |     }
 25 |     renderNote(
 26 |         noteEL: HTMLElement,
 27 |         result: FuzzyMatch
 28 |     ): void {
 29 |         noteEL.setText(this.plugin.iconManager.getIconModuleName(result.item));
 30 |     }
 31 |     renderTitle(
 32 |         titleEl: HTMLElement,
 33 |         result: FuzzyMatch
 34 |     ): void {
 35 |         renderMatches(titleEl, result.item.name, result.match.matches);
 36 |     }
 37 |     renderFlair(
 38 |         flairEl: HTMLElement,
 39 |         result: FuzzyMatch
 40 |     ): void {
 41 |         const { item } = result;
 42 | 
 43 |         flairEl.appendChild(
 44 |             this.plugin.iconManager.getIconNode(item) ?? createDiv()
 45 |         );
 46 |     }
 47 | 
 48 |     getItemText(item: AdmonitionIconDefinition) {
 49 |         return item.name;
 50 |     }
 51 | }
 52 | class AdmonitionSuggestionModal extends FuzzyInputSuggest {
 53 |     constructor(
 54 |         public plugin: ObsidianAdmonition,
 55 |         input: TextComponent | SearchComponent,
 56 |         items: Admonition[]
 57 |     ) {
 58 |         super(plugin.app, input, items);
 59 |     }
 60 |     renderTitle(titleEl: HTMLElement, result: FuzzyMatch): void {
 61 |         renderMatches(titleEl, result.item.type, result.match.matches);
 62 |     }
 63 |     renderFlair(flairEl: HTMLElement, result: FuzzyMatch): void {
 64 |         const { item } = result;
 65 |         flairEl
 66 |             .appendChild(
 67 |                 this.plugin.iconManager.getIconNode(item.icon) ?? createDiv()
 68 |             )
 69 |             .setAttribute("color", `rgb(${item.color})`);
 70 |     }
 71 |     getItemText(item: Admonition) {
 72 |         return item.type;
 73 |     }
 74 | }
 75 | 
 76 | export class InsertAdmonitionModal extends Modal {
 77 |     public type: string;
 78 |     public title: string;
 79 |     public noTitle: boolean;
 80 |     public collapse: "open" | "closed" | "none" = this.plugin.data.autoCollapse
 81 |         ? this.plugin.data.defaultCollapseType
 82 |         : "none";
 83 |     private element: HTMLElement;
 84 |     admonitionEl: HTMLDivElement;
 85 |     insert: boolean;
 86 |     constructor(private plugin: ObsidianAdmonition) {
 87 |         super(plugin.app);
 88 | 
 89 |         this.containerEl.addClass("insert-admonition-modal");
 90 | 
 91 |         this.onOpen = () => this.display(true);
 92 |     }
 93 |     private async display(focus?: boolean) {
 94 |         const { contentEl } = this;
 95 | 
 96 |         contentEl.empty();
 97 | 
 98 |         const typeSetting = new Setting(contentEl);
 99 |         typeSetting.setName("Admonition Type").addText((t) => {
100 |             t.setPlaceholder("Admonition Type").setValue(this.type);
101 |             const modal = new AdmonitionSuggestionModal(
102 |                 this.plugin,
103 |                 t,
104 |                 this.plugin.admonitionArray
105 |             );
106 | 
107 |             const build = () => {
108 |                 if (
109 |                     t.inputEl.value &&
110 |                     this.plugin.admonitions[t.inputEl.value]
111 |                 ) {
112 |                     this.type = t.inputEl.value;
113 |                     this.title = this.plugin.admonitions[this.type].title;
114 |                     if (!this.title?.length) {
115 |                         this.title =
116 |                             this.type[0].toUpperCase() +
117 |                             this.type.slice(1).toLowerCase();
118 |                     }
119 |                     titleInput.setValue(this.title);
120 |                 } else {
121 |                     new Notice("No admonition type by that name exists.");
122 |                     t.inputEl.value = "";
123 |                 }
124 | 
125 |                 this.buildAdmonition();
126 |             };
127 | 
128 |             t.inputEl.onblur = build;
129 | 
130 |             modal.onSelect((item) => {
131 |                 t.inputEl.value = item.item.type;
132 |                 build();
133 |                 modal.close();
134 |             });
135 |         });
136 | 
137 |         let titleInput: TextComponent;
138 | 
139 |         const titleSetting = new Setting(contentEl);
140 |         titleSetting
141 |             .setName("Admonition Title")
142 |             .setDesc("Leave blank to render without a title.")
143 |             .addText((t) => {
144 |                 titleInput = t;
145 |                 t.setValue(this.title);
146 | 
147 |                 t.onChange((v) => {
148 |                     this.title = v;
149 |                     if (v.length == 0) {
150 |                         this.noTitle = true;
151 |                     } else {
152 |                         this.noTitle = false;
153 |                     }
154 |                     if (this.element) {
155 |                         const admonition = this.plugin.admonitions[this.type];
156 |                         const element = this.plugin.getAdmonitionElement(
157 |                             this.type,
158 |                             this.title,
159 |                             admonition.icon,
160 |                             admonition.injectColor ??
161 |                                 this.plugin.data.injectColor
162 |                                 ? admonition.color
163 |                                 : null,
164 |                             this.collapse
165 |                         );
166 |                         element.createDiv({
167 |                             cls: "admonition-content",
168 |                             text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod nulla."
169 |                         });
170 |                         this.element.replaceWith(element);
171 |                         this.element = element;
172 |                     }
173 |                 });
174 |             });
175 | 
176 |         const collapseSetting = new Setting(contentEl);
177 |         collapseSetting.setName("Make Collapsible").addDropdown((d) => {
178 |             d.addOption("open", "Open");
179 |             d.addOption("closed", "Closed");
180 |             d.addOption("none", "None");
181 |             d.setValue(this.collapse);
182 |             d.onChange((v: "open" | "closed" | "none") => {
183 |                 this.collapse = v;
184 |                 this.buildAdmonition();
185 |             });
186 |         });
187 | 
188 |         this.admonitionEl = this.contentEl.createDiv();
189 |         this.buildAdmonition();
190 | 
191 |         new Setting(contentEl)
192 |             .addButton((b) =>
193 |                 b
194 |                     .setButtonText("Insert")
195 |                     .setCta()
196 |                     .onClick(() => {
197 |                         this.insert = true;
198 |                         this.close();
199 |                     })
200 |             )
201 |             .addExtraButton((b) => {
202 |                 b.setIcon("cross")
203 |                     .setTooltip("Cancel")
204 |                     .onClick(() => this.close());
205 |                 b.extraSettingsEl.setAttr("tabindex", 0);
206 |                 b.extraSettingsEl.onkeydown = (evt) => {
207 |                     evt.key == "Enter" && this.close();
208 |                 };
209 |             });
210 |     }
211 |     buildAdmonition() {
212 |         this.admonitionEl.empty();
213 |         if (this.type && this.plugin.admonitions[this.type]) {
214 |             const admonition = this.plugin.admonitions[this.type];
215 |             this.element = this.plugin.getAdmonitionElement(
216 |                 this.type,
217 |                 this.title,
218 |                 admonition.icon,
219 |                 admonition.injectColor ?? this.plugin.data.injectColor
220 |                     ? admonition.color
221 |                     : null,
222 |                 this.collapse
223 |             );
224 |             this.element.createDiv({
225 |                 cls: "admonition-content",
226 |                 text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod nulla."
227 |             });
228 |             this.admonitionEl.appendChild(this.element);
229 |         }
230 |     }
231 | }
232 | 


--------------------------------------------------------------------------------
/src/styles.css:
--------------------------------------------------------------------------------
1 | @import "./assets/main.scss";
2 | @import "./assets/callout.scss";
3 | 


--------------------------------------------------------------------------------
/src/suggest/suggest.ts:
--------------------------------------------------------------------------------
  1 | import {
  2 |     Editor,
  3 |     EditorPosition,
  4 |     EditorSuggest,
  5 |     EditorSuggestContext,
  6 |     EditorSuggestTriggerInfo
  7 | } from "obsidian";
  8 | import { Admonition } from "src/@types";
  9 | import ObsidianAdmonition from "src/main";
 10 | 
 11 | abstract class AdmonitionOrCalloutSuggester extends EditorSuggest<
 12 |     [string, Admonition]
 13 | > {
 14 |     constructor(public plugin: ObsidianAdmonition) {
 15 |         super(plugin.app);
 16 |     }
 17 |     getSuggestions(ctx: EditorSuggestContext) {
 18 |         if (!ctx.query?.length) return Object.entries(this.plugin.admonitions);
 19 | 
 20 |         return Object.entries(this.plugin.admonitions).filter((p) =>
 21 |             p[0].toLowerCase().contains(ctx.query.toLowerCase())
 22 |         );
 23 |     }
 24 |     renderSuggestion(
 25 |         [text, item]: [text: string, item: Admonition],
 26 |         el: HTMLElement
 27 |     ) {
 28 |         el.addClasses(["admonition-suggester-item", "mod-complex"]);
 29 |         el.style.setProperty("--callout-color", item.color);
 30 |         el.createSpan({ text });
 31 |         const iconDiv = el.createDiv("suggestion-aux").createDiv({
 32 |             cls: "suggestion-flair",
 33 |             attr: {
 34 |                 style: `color: rgb(var(--callout-color))`
 35 |             }
 36 |         });
 37 |         let iconEl = this.plugin.iconManager.getIconNode(item.icon);
 38 |         // Unpack the icon if it's an Obsidian one, as they're wrapped with an extra 
39 | if (iconEl instanceof HTMLDivElement && iconEl.childElementCount == 1) 40 | iconEl = iconEl.firstElementChild; 41 | else if (iconEl !== null) { 42 | iconEl.removeClass("svg-inline--fa"); 43 | iconEl.addClass("svg-icon"); 44 | } 45 | iconDiv.appendChild(iconEl ?? createDiv()); 46 | } 47 | onTrigger( 48 | cursor: EditorPosition, 49 | editor: Editor 50 | ): EditorSuggestTriggerInfo { 51 | const line = editor.getLine(cursor.line); 52 | const match = this.testAndReturnQuery(line, cursor); 53 | if (!match) return null; 54 | const [_, query] = match; 55 | 56 | if ( 57 | Object.keys(this.plugin.admonitions).find( 58 | (p) => p.toLowerCase() == query.toLowerCase() 59 | ) 60 | ) { 61 | return null; 62 | } 63 | 64 | return { 65 | end: cursor, 66 | start: { 67 | ch: match.index + this.offset, 68 | line: cursor.line 69 | }, 70 | query 71 | }; 72 | } 73 | abstract offset: number; 74 | abstract selectSuggestion( 75 | value: [string, Admonition], 76 | evt: MouseEvent | KeyboardEvent 77 | ): void; 78 | abstract testAndReturnQuery( 79 | line: string, 80 | cursor: EditorPosition 81 | ): RegExpMatchArray | null; 82 | } 83 | 84 | export class CalloutSuggest extends AdmonitionOrCalloutSuggester { 85 | offset = 4; 86 | selectSuggestion( 87 | [text]: [text: string, item: Admonition], 88 | evt: MouseEvent | KeyboardEvent 89 | ): void { 90 | if (!this.context) return; 91 | 92 | const line = this.context.editor 93 | .getLine(this.context.end.line) 94 | .slice(this.context.end.ch); 95 | const [_, exists] = line.match(/^(\] ?)/) ?? []; 96 | 97 | this.context.editor.replaceRange( 98 | `${text}] `, 99 | this.context.start, 100 | { 101 | ...this.context.end, 102 | ch: 103 | this.context.start.ch + 104 | this.context.query.length + 105 | (exists?.length ?? 0) 106 | }, 107 | "admonitions" 108 | ); 109 | 110 | this.context.editor.setCursor( 111 | this.context.start.line, 112 | this.context.start.ch + text.length + 2 113 | ); 114 | 115 | this.close(); 116 | } 117 | testAndReturnQuery( 118 | line: string, 119 | cursor: EditorPosition 120 | ): RegExpMatchArray | null { 121 | if (/> ?\[!\w+\]/.test(line.slice(0, cursor.ch))) return null; 122 | if (!/> ?\[!\w*/.test(line)) return null; 123 | return line.match(/> ?\[!(\w*)\]?/); 124 | } 125 | } 126 | export class AdmonitionSuggest extends AdmonitionOrCalloutSuggester { 127 | offset = 6; 128 | selectSuggestion( 129 | [text]: [text: string, item: Admonition], 130 | evt: MouseEvent | KeyboardEvent 131 | ): void { 132 | if (!this.context) return; 133 | 134 | this.context.editor.replaceRange( 135 | `${text}`, 136 | this.context.start, 137 | this.context.end, 138 | "admonitions" 139 | ); 140 | 141 | this.close(); 142 | } 143 | testAndReturnQuery( 144 | line: string, 145 | cursor: EditorPosition 146 | ): RegExpMatchArray | null { 147 | if (!/```ad-\w*/.test(line)) return null; 148 | return line.match(/```ad-(\w*)/); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/util/constants.ts: -------------------------------------------------------------------------------- 1 | import { Admonition } from "../@types"; 2 | 3 | export const ADD_ADMONITION_COMMAND_ICON = ``; 4 | export const ADD_COMMAND_NAME = "admonition-add-command"; 5 | 6 | export const REMOVE_ADMONITION_COMMAND_ICON = ``; 7 | export const REMOVE_COMMAND_NAME = "admonition-remove-command"; 8 | 9 | export const WARNING_ICON = ``; 10 | export const WARNING_ICON_NAME = "admonition-warning"; 11 | 12 | export const SPIN_ICON = ``; 13 | export const SPIN_ICON_NAME = "admonition-spin"; 14 | 15 | export const ADMONITION_MAP: Record = { 16 | note: { 17 | type: "note", 18 | color: "68, 138, 255", 19 | icon: { 20 | type: "font-awesome", 21 | name: "pencil-alt" 22 | }, 23 | command: false, 24 | noTitle: false 25 | }, 26 | seealso: { 27 | type: "note", 28 | color: "68, 138, 255", 29 | icon: { 30 | type: "font-awesome", 31 | name: "pencil-alt" 32 | }, 33 | command: false, 34 | noTitle: false 35 | }, 36 | abstract: { 37 | type: "abstract", 38 | color: "0, 176, 255", 39 | icon: { 40 | type: "font-awesome", 41 | name: "book" 42 | }, 43 | command: false, 44 | noTitle: false 45 | }, 46 | summary: { 47 | type: "abstract", 48 | color: "0, 176, 255", 49 | icon: { 50 | type: "font-awesome", 51 | name: "book" 52 | }, 53 | command: false, 54 | noTitle: false 55 | }, 56 | tldr: { 57 | type: "abstract", 58 | color: "0, 176, 255", 59 | icon: { 60 | type: "font-awesome", 61 | name: "book" 62 | }, 63 | command: false, 64 | noTitle: false 65 | }, 66 | info: { 67 | type: "info", 68 | color: "0, 184, 212", 69 | icon: { 70 | type: "font-awesome", 71 | name: "info-circle" 72 | }, 73 | command: false, 74 | noTitle: false 75 | }, 76 | todo: { 77 | type: "info", 78 | color: "0, 184, 212", 79 | icon: { 80 | type: "font-awesome", 81 | name: "info-circle" 82 | }, 83 | command: false, 84 | noTitle: false 85 | }, 86 | tip: { 87 | type: "tip", 88 | color: "0, 191, 165", 89 | icon: { 90 | type: "font-awesome", 91 | name: "fire" 92 | }, 93 | command: false, 94 | noTitle: false 95 | }, 96 | hint: { 97 | type: "tip", 98 | color: "0, 191, 165", 99 | icon: { 100 | type: "font-awesome", 101 | name: "fire" 102 | }, 103 | command: false, 104 | noTitle: false 105 | }, 106 | important: { 107 | type: "tip", 108 | color: "0, 191, 165", 109 | icon: { 110 | type: "font-awesome", 111 | name: "fire" 112 | }, 113 | command: false, 114 | noTitle: false 115 | }, 116 | success: { 117 | type: "success", 118 | color: "0, 200, 83", 119 | icon: { 120 | type: "font-awesome", 121 | name: "check-circle" 122 | }, 123 | command: false, 124 | noTitle: false 125 | }, 126 | check: { 127 | type: "success", 128 | color: "0, 200, 83", 129 | icon: { 130 | type: "font-awesome", 131 | name: "check-circle" 132 | }, 133 | command: false, 134 | noTitle: false 135 | }, 136 | done: { 137 | type: "success", 138 | color: "0, 200, 83", 139 | icon: { 140 | type: "font-awesome", 141 | name: "check-circle" 142 | }, 143 | command: false, 144 | noTitle: false 145 | }, 146 | question: { 147 | type: "question", 148 | color: "100, 221, 23", 149 | icon: { 150 | type: "font-awesome", 151 | name: "question-circle" 152 | }, 153 | command: false, 154 | noTitle: false 155 | }, 156 | help: { 157 | type: "question", 158 | color: "100, 221, 23", 159 | icon: { 160 | type: "font-awesome", 161 | name: "question-circle" 162 | }, 163 | command: false, 164 | noTitle: false 165 | }, 166 | faq: { 167 | type: "question", 168 | color: "100, 221, 23", 169 | icon: { 170 | type: "font-awesome", 171 | name: "question-circle" 172 | }, 173 | command: false, 174 | noTitle: false 175 | }, 176 | warning: { 177 | type: "warning", 178 | color: "255, 145, 0", 179 | icon: { 180 | type: "font-awesome", 181 | name: "exclamation-triangle" 182 | }, 183 | command: false, 184 | noTitle: false 185 | }, 186 | caution: { 187 | type: "warning", 188 | color: "255, 145, 0", 189 | icon: { 190 | type: "font-awesome", 191 | name: "exclamation-triangle" 192 | }, 193 | command: false, 194 | noTitle: false 195 | }, 196 | attention: { 197 | type: "warning", 198 | color: "255, 145, 0", 199 | icon: { 200 | type: "font-awesome", 201 | name: "exclamation-triangle" 202 | }, 203 | command: false, 204 | noTitle: false 205 | }, 206 | failure: { 207 | type: "failure", 208 | color: "255, 82, 82", 209 | icon: { 210 | type: "font-awesome", 211 | name: "times-circle" 212 | }, 213 | command: false, 214 | noTitle: false 215 | }, 216 | fail: { 217 | type: "failure", 218 | color: "255, 82, 82", 219 | icon: { 220 | type: "font-awesome", 221 | name: "times-circle" 222 | }, 223 | command: false, 224 | noTitle: false 225 | }, 226 | missing: { 227 | type: "failure", 228 | color: "255, 82, 82", 229 | icon: { 230 | type: "font-awesome", 231 | name: "times-circle" 232 | }, 233 | command: false, 234 | noTitle: false 235 | }, 236 | danger: { 237 | type: "danger", 238 | color: "255, 23, 68", 239 | icon: { 240 | type: "font-awesome", 241 | name: "bolt" 242 | }, 243 | command: false, 244 | noTitle: false 245 | }, 246 | error: { 247 | type: "danger", 248 | color: "255, 23, 68", 249 | icon: { 250 | type: "font-awesome", 251 | name: "bolt" 252 | }, 253 | command: false, 254 | noTitle: false 255 | }, 256 | bug: { 257 | type: "bug", 258 | color: "245, 0, 87", 259 | icon: { 260 | type: "font-awesome", 261 | name: "bug" 262 | }, 263 | command: false, 264 | noTitle: false 265 | }, 266 | example: { 267 | type: "example", 268 | color: "124, 77, 255", 269 | icon: { 270 | type: "font-awesome", 271 | name: "list-ol" 272 | }, 273 | command: false, 274 | noTitle: false 275 | }, 276 | quote: { 277 | type: "quote", 278 | color: "158, 158, 158", 279 | icon: { 280 | type: "font-awesome", 281 | name: "quote-right" 282 | }, 283 | command: false, 284 | noTitle: false 285 | }, 286 | cite: { 287 | type: "quote", 288 | color: "158, 158, 158", 289 | icon: { 290 | type: "font-awesome", 291 | name: "quote-right" 292 | }, 293 | command: false, 294 | noTitle: false 295 | } 296 | }; 297 | -------------------------------------------------------------------------------- /src/util/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./util"; 2 | export * from "./constants"; 3 | -------------------------------------------------------------------------------- /src/util/util.ts: -------------------------------------------------------------------------------- 1 | import { Notice } from "obsidian"; 2 | import { Admonition } from "../@types"; 3 | 4 | function startsWithAny(str: string, needles: string[]) { 5 | for (let i = 0; i < needles.length; i++) { 6 | if (str.startsWith(needles[i])) { 7 | return i; 8 | } 9 | } 10 | 11 | return false; 12 | } 13 | 14 | export function getParametersFromSource( 15 | type: string, 16 | src: string, 17 | admonition: Admonition 18 | ) { 19 | const admonitionTitle = 20 | admonition.title ?? type[0].toUpperCase() + type.slice(1).toLowerCase(); 21 | const keywordTokens = ["title:", "collapse:", "icon:", "color:", "metadata:"]; 22 | 23 | const keywords = ["title", "collapse", "icon", "color", "metadata"]; 24 | 25 | let lines = src.split("\n"); 26 | 27 | let skipLines = 0; 28 | 29 | let params: { [k: string]: string } = {}; 30 | 31 | for (let i = 0; i < lines.length; i++) { 32 | let keywordIndex = startsWithAny(lines[i], keywordTokens); 33 | 34 | if (keywordIndex === false) { 35 | break; 36 | } 37 | 38 | let foundKeyword = keywords[keywordIndex]; 39 | 40 | if (params[foundKeyword] !== undefined) { 41 | break; 42 | } 43 | 44 | params[foundKeyword] = lines[i] 45 | .slice(keywordTokens[keywordIndex].length) 46 | .trim(); 47 | ++skipLines; 48 | } 49 | 50 | let { title, collapse, icon, color, metadata } = params; 51 | 52 | // If color is in RGB format 53 | if (color && color.startsWith('rgb')) { 54 | color = color.slice(4, -1); 55 | } 56 | 57 | // If color is in Hex format, convert it to RGB 58 | if (color && color.startsWith('#')) { 59 | const hex = color.slice(1); 60 | const bigint = parseInt(hex, 16); 61 | const r = (bigint >> 16) & 255; 62 | const g = (bigint >> 8) & 255; 63 | const b = bigint & 255; 64 | color = `${r}, ${g}, ${b}`; 65 | } 66 | 67 | // If color is in HSL format, convert it to RGB 68 | if (color && color.startsWith('hsl')) { 69 | const [h, s, l] = color.slice(4, -1).split(',').map(str => Number(str.replace('%', '').trim())); 70 | const [r, g, b] = hslToRgb(h, s, l); 71 | color = `${r}, ${g}, ${b}`; 72 | } 73 | 74 | // If color is in HSB format, convert it to RGB 75 | if (color && (color.startsWith('hsb') || color.startsWith('hsv'))) { 76 | const [h, s, v] = color.slice(4, -1).split(',').map(str => Number(str.replace('%', '').trim())); 77 | const [r, g, b] = hsbToRgb(h, s, v); 78 | color = `${r}, ${g}, ${b}`; 79 | } 80 | 81 | let content = lines.slice(skipLines).join("\n"); 82 | 83 | /** 84 | * If the admonition should collapse, but something other than open or closed was provided, set to closed. 85 | */ 86 | if ( 87 | collapse !== undefined && 88 | collapse !== "none" && 89 | collapse !== "open" && 90 | collapse !== "closed" 91 | ) { 92 | collapse = "closed"; 93 | } 94 | 95 | if (!("title" in params)) { 96 | if (!admonition.noTitle) { 97 | title = admonitionTitle; 98 | } 99 | } 100 | /** 101 | * If the admonition should collapse, but title was blanked, set the default title. 102 | */ 103 | if ( 104 | title && 105 | title.trim() === "" && 106 | collapse !== undefined && 107 | collapse !== "none" 108 | ) { 109 | title = admonitionTitle; 110 | new Notice("An admonition must have a title if it is collapsible."); 111 | } 112 | 113 | return { title, collapse, content, icon, color, metadata }; 114 | } 115 | 116 | function hslToRgb(h: number, s: number, l: number) { 117 | h /= 360; 118 | s /= 100; 119 | l /= 100; 120 | let r, g, b; 121 | 122 | if (s === 0) { 123 | r = g = b = l; // achromatic 124 | } else { 125 | const hue2rgb = (p: number, q: number, t: number) => { 126 | if (t < 0) t += 1; 127 | if (t > 1) t -= 1; 128 | if (t < 1 / 6) return p + (q - p) * 6 * t; 129 | if (t < 1 / 2) return q; 130 | if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; 131 | return p; 132 | }; 133 | const q = l < 0.5 ? l * (1 + s) : l + s - l * s; 134 | const p = 2 * l - q; 135 | r = hue2rgb(p, q, h + 1 / 3); 136 | g = hue2rgb(p, q, h); 137 | b = hue2rgb(p, q, h - 1 / 3); 138 | } 139 | 140 | return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]; 141 | } 142 | 143 | function hsbToRgb(h: number, s: number, b: number) { 144 | h /= 360; 145 | s /= 100; 146 | b /= 100; 147 | let r, g, bb; 148 | let i = Math.floor(h * 6); 149 | let f = h * 6 - i; 150 | let p = b * (1 - s); 151 | let q = b * (1 - f * s); 152 | let t = b * (1 - (1 - f) * s); 153 | switch (i % 6) { 154 | case 0: r = b, g = t, bb = p; break; 155 | case 1: r = q, g = b, bb = p; break; 156 | case 2: r = p, g = b, bb = t; break; 157 | case 3: r = p, g = q, bb = b; break; 158 | case 4: r = t, g = p, bb = b; break; 159 | case 5: r = b, g = p, bb = q; break; 160 | } 161 | return [Math.round(r * 255), Math.round(g * 255), Math.round(bb * 255)]; 162 | } -------------------------------------------------------------------------------- /src/util/validator.ts: -------------------------------------------------------------------------------- 1 | import { Admonition, AdmonitionIconDefinition } from "src/@types"; 2 | import { t } from "src/lang/helpers"; 3 | import ObsidianAdmonition from "src/main"; 4 | 5 | type ValidationSuccess = { 6 | success: true; 7 | messages?: string[]; 8 | }; 9 | type ValidationError = { 10 | success: false; 11 | failed: "type" | "icon" | "rgb" | "title" | "booleans"; 12 | message: string; 13 | }; 14 | type Result = ValidationSuccess | ValidationError; 15 | 16 | export const isSelectorValid = ((dummyElement) => (selector: string) => { 17 | try { 18 | dummyElement.querySelector(selector); 19 | } catch { 20 | return false; 21 | } 22 | return true; 23 | })(document.createDocumentFragment()); 24 | 25 | export class AdmonitionValidator { 26 | static validateImport( 27 | plugin: ObsidianAdmonition, 28 | admonition: Admonition 29 | ): Result { 30 | const result: Result = { 31 | success: true, 32 | messages: [] 33 | }; 34 | const validType = AdmonitionValidator.validateType( 35 | admonition.type, 36 | plugin 37 | ); 38 | if (validType.success == false) { 39 | return validType; 40 | } 41 | const iconName = 42 | typeof admonition.icon == "string" 43 | ? admonition.icon 44 | : typeof admonition.icon == "object" 45 | ? admonition.icon?.name 46 | : null; 47 | const validIcon = AdmonitionValidator.validateType(iconName, plugin); 48 | if (validIcon.success == false) { 49 | return validIcon; 50 | } 51 | 52 | const iconNode = plugin.iconManager.getIconNode(admonition.icon); 53 | if (!iconNode) { 54 | result.messages.push( 55 | "No installed icon found by the name " + 56 | iconName + 57 | ". Perhaps you need to install a new icon pack?" 58 | ); 59 | } 60 | if (admonition.title && typeof admonition.title != "string") { 61 | return { 62 | success: false, 63 | failed: "title", 64 | message: "Admonition titles can only be strings." 65 | }; 66 | } 67 | if ( 68 | !("color" in admonition) || 69 | !/(?:(?:2(?:[0-4]\d|5[0-5])|\d{1,2}|1\d\d)\s*,\s*){2}\s*(?:2(?:[0-4]\d|5[0-5])|\d{1,2}|1\d\d)/.test( 70 | admonition.color 71 | ) 72 | ) { 73 | console.warn( 74 | "No color provided for the import of " + 75 | admonition.type + 76 | ". Adding a random color." 77 | ); 78 | admonition.color = `${Math.floor( 79 | Math.random() * 255 80 | )}, ${Math.floor(Math.random() * 255)}, ${Math.floor( 81 | Math.random() * 255 82 | )}`; 83 | } 84 | const booleans: (keyof Admonition)[] = [ 85 | "command", 86 | "injectColor", 87 | "noTitle", 88 | "copy" 89 | ]; 90 | for (const key of booleans) { 91 | if ( 92 | key in admonition && 93 | typeof JSON.parse(JSON.stringify(admonition[key])) != "boolean" 94 | ) { 95 | return { 96 | success: false, 97 | failed: "booleans", 98 | message: `The "${key}" property must be a boolean if present.` 99 | }; 100 | } 101 | } 102 | return result; 103 | } 104 | static validate( 105 | plugin: ObsidianAdmonition, 106 | type: string, 107 | icon: AdmonitionIconDefinition, 108 | oldType?: string 109 | ): Result { 110 | const validType = AdmonitionValidator.validateType( 111 | type, 112 | plugin, 113 | oldType 114 | ); 115 | if (validType.success == false) { 116 | return validType; 117 | } 118 | 119 | return AdmonitionValidator.validateIcon(icon, plugin); 120 | } 121 | static validateType( 122 | type: string, 123 | plugin: ObsidianAdmonition, 124 | oldType?: string 125 | ): Result { 126 | if (!type.length) { 127 | return { 128 | success: false, 129 | message: t("Admonition type cannot be empty."), 130 | failed: "type" 131 | }; 132 | } 133 | 134 | if (type.includes(" ")) { 135 | return { 136 | success: false, 137 | message: t("Admonition type cannot include spaces."), 138 | failed: "type" 139 | }; 140 | } 141 | if (!isSelectorValid(type)) { 142 | return { 143 | success: false, 144 | message: t("Types must be a valid CSS selector."), 145 | failed: "type" 146 | }; 147 | } 148 | if (type != oldType && type in plugin.data.userAdmonitions) { 149 | return { 150 | success: false, 151 | message: "That Admonition type already exists.", 152 | failed: "type" 153 | }; 154 | } 155 | return { success: true }; 156 | } 157 | static validateIcon( 158 | definition: AdmonitionIconDefinition, 159 | plugin: ObsidianAdmonition 160 | ): Result { 161 | if (definition.type === "image") { 162 | return { 163 | success: true 164 | }; 165 | } 166 | if (!definition.name?.length) { 167 | return { 168 | success: false, 169 | message: t("Icon cannot be empty."), 170 | failed: "icon" 171 | }; 172 | } 173 | const icon = plugin.iconManager.getIconType(definition.name); 174 | if (!icon) { 175 | return { 176 | success: false, 177 | message: t("Invalid icon name."), 178 | failed: "icon" 179 | }; 180 | } 181 | return { 182 | success: true 183 | }; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | onwarn: (warning, handler) => { 3 | if (warning.code.toLowerCase().startsWith("a11y-")) { 4 | return; 5 | } 6 | handler(warning); 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "baseUrl": ".", 5 | "inlineSourceMap": true, 6 | "inlineSources": true, 7 | "module": "ESNext", 8 | "target": "es6", 9 | "allowJs": true, 10 | "noImplicitAny": true, 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "lib": ["dom", "es5", "scripthost", "es2019"] 14 | }, 15 | "include": ["**/*.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "0.2.3": "0.11.0", 3 | "1.0.1": "0.11.0", 4 | "2.0.1": "0.11.0", 5 | "3.1.2": "0.11.0", 6 | "3.2.2": "0.11.0", 7 | "3.3.4": "0.11.0", 8 | "4.0.1": "0.11.0", 9 | "4.1.7": "0.11.0", 10 | "4.2.1": "0.11.0", 11 | "4.3.1": "0.12.0", 12 | "4.4.2": "0.12.2", 13 | "5.0.3": "0.12.2", 14 | "6.2.10": "0.12.4", 15 | "6.4.1": "0.12.10", 16 | "7.0.4": "0.13.14", 17 | "8.0.0": "0.14.0", 18 | "9.2.0": "1.1.0" 19 | } 20 | --------------------------------------------------------------------------------