├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── FR.yml │ └── bug.yml └── workflows │ ├── auto_assign.yml │ └── release.yml ├── .gitignore ├── .npmrc ├── CHANGELOG-beta.md ├── CHANGELOG.md ├── LICENSE ├── README.md ├── _assets ├── settings.png └── view_mode.png ├── biome.json ├── bun.lockb ├── commit-and-tag-version.mjs ├── esbuild.config.mjs ├── hooks └── _changelog.mjs ├── manifest-beta.json ├── manifest.json ├── package.json ├── pnpm-lock.yaml ├── src ├── cmPlugin.ts ├── interface.ts ├── main.ts ├── markdownProcessor.ts ├── settings │ ├── change_pattern.ts │ ├── import_export.ts │ ├── index.ts │ └── viewModal.ts ├── styles.css └── utils.ts ├── styles.css ├── tsconfig.json └── versions.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 2 10 | tab_width = 2 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FR.yml: -------------------------------------------------------------------------------- 1 | name: "Feature request" 2 | description: "Suggest an idea for this project" 3 | title: "[FR]: " 4 | labels: ["enhancement"] 5 | assignees: 6 | - Mara-Li 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | Thanks for taking the time to fill out this Feature request! 12 | - type: checkboxes 13 | attributes: 14 | label: Issue validation 15 | description: | 16 | - Thanks to check if your issue is relative to the repository. Any non relative or duplicate issue will be closed. 17 | - Please, check the documentation and the configuration files before submitting your request. 18 | - Issue not in English will be closed. 19 | options: 20 | - label: "I checked the issue to prevent duplicate" 21 | required: true 22 | - label: "I checked my configurations files and the documentation" 23 | required: true 24 | - type: textarea 25 | id: describe-request 26 | attributes: 27 | label: Is your feature related to a problem ? 28 | description: If you found a solution with the inherent limit I had with Obsidian, please, add it here! 29 | placeholder: "Tell me the original problem" 30 | - type: textarea 31 | id: describe-solution 32 | attributes: 33 | label: What solution do you want to see ? 34 | description: Describe your idea here! 35 | validations: 36 | required: true 37 | - type: textarea 38 | id: alternative 39 | attributes: 40 | label: Describe the alternative you've considered 41 | - type: textarea 42 | attributes: 43 | label: Anything else? 44 | description: | 45 | Links? References? Anything that will give us more context about the issue you are encountering! 46 | Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. 47 | validations: 48 | required: false 49 | - type: markdown 50 | attributes: 51 | value: | 52 | ## Environment 53 | Please fill out the following information about your environment. If you are unsure about any of them, just leave it blank. 54 | - type: dropdown 55 | id: version 56 | attributes: 57 | label: OS 58 | description: Check your OS 59 | multiple: true 60 | options: 61 | - IOS 62 | - Android 63 | - MacOS 64 | - Windows 65 | - Linux 66 | - type: textarea 67 | attributes: 68 | label: Obsidian information 69 | description: | 70 | Please copy and paste the information about your Obsidian version using the command "show debug info" in the obsidian's commands palette. 71 | render: bash session 72 | validations: 73 | required: true 74 | - type: input 75 | id: plugin-version 76 | attributes: 77 | label: Plugin version 78 | description: Please copy and paste the version of the plugin you are using. 79 | placeholder: "1.0.0" 80 | validations: 81 | required: true 82 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: "Bug report" 2 | description: Fill a bug report 3 | title: "[Bug]: " 4 | labels: ["bug"] 5 | assignees: 6 | - Mara-Li 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | Thanks for taking the time to fill out this bug report. 12 | - type: checkboxes 13 | attributes: 14 | label: Issue validation 15 | description: | 16 | - Thanks to check if your issue is relative to the repository. Any non relative or duplicate issue will be closed. 17 | - Please, check the documentation and the configuration files before submitting your request. 18 | - Issue not in English will be closed. 19 | options: 20 | - label: "I checked the issue to prevent duplicate" 21 | required: true 22 | - label: "I checked my configurations files and the documentation" 23 | required: true 24 | - type: textarea 25 | id: describe-bug 26 | attributes: 27 | label: Describe the bug 28 | description: A clear and concise description of what the bug is. 29 | placeholder: "Tell us what you see! And don't forget the error" 30 | validations: 31 | required: true 32 | - type: textarea 33 | id: repro-bug 34 | attributes: 35 | label: How to reproduce ? 36 | description: Step to reproduce the behavior 37 | placeholder: | 38 | 1. Go to '...' 39 | 2. Click on '....' 40 | 3. Scroll down to '....' 41 | 4. See error 42 | validations: 43 | required: false 44 | - type: textarea 45 | id: minimal-repro 46 | attributes: 47 | label: Minimal Reproducible Example 48 | description: Please provide a minimal reproducible example. 49 | validations: 50 | required: true 51 | - type: textarea 52 | attributes: 53 | label: Configuration 54 | description: | 55 | Open the configuration settings with any text editor. The settings are located in `.obsidian/plugins/regex-mark` 56 | render: JSON 57 | validations: 58 | required: true 59 | - type: textarea 60 | id: logs 61 | attributes: 62 | label: Relevant log output 63 | description: | 64 | Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. You can open the Obsidian's console with "CTRL+MAJ+I." 65 | render: bash session 66 | - type: textarea 67 | attributes: 68 | label: Anything else? 69 | description: | 70 | Links? References? Anything that will give us more context about the issue you are encountering! 71 | Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. 72 | validations: 73 | required: false 74 | - type: markdown 75 | attributes: 76 | value: | 77 | ## Environment 78 | Please fill out the following information about your environment. If you are unsure about any of them, just leave it blank. 79 | - type: dropdown 80 | id: version 81 | attributes: 82 | label: OS 83 | description: Check your OS 84 | multiple: true 85 | options: 86 | - IOS 87 | - Android 88 | - MacOS 89 | - Windows 90 | - Linux 91 | - type: textarea 92 | attributes: 93 | label: Obsidian information 94 | description: | 95 | Please copy and paste the information about your Obsidian version using the command "show debug info" in the obsidian's commands palette. 96 | render: bash session 97 | validations: 98 | required: true 99 | - type: input 100 | id: plugin-version 101 | attributes: 102 | label: Plugin version 103 | description: Please copy and paste the version of the plugin you are using. 104 | placeholder: "1.0.0" 105 | validations: 106 | required: true 107 | -------------------------------------------------------------------------------- /.github/workflows/auto_assign.yml: -------------------------------------------------------------------------------- 1 | name: Auto Assign 2 | on: 3 | issues: 4 | types: [opened, edited, labeled, unlabeled] 5 | pull_request: 6 | types: [opened, edited, labeled, unlabeled] 7 | jobs: 8 | auto-assign: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: wow-actions/auto-assign@v3 12 | with: 13 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 14 | reviewers: ${{github.repository_owner}} 15 | assignees: ${{github.repository_owner}} 16 | skipKeywords: wip, draft 17 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release obsidian plugin 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | bump: 7 | default: false 8 | description: "Bump version based on semantic release" 9 | type: boolean 10 | required: false 11 | beta: 12 | default: false 13 | description: "Make a beta release" 14 | type: boolean 15 | required: false 16 | push: 17 | tags: 18 | - "*" 19 | permissions: 20 | contents: write 21 | 22 | jobs: 23 | release: 24 | if: (github.event_name == 'push') || (github.event_name == 'workflow_dispatch' && !inputs.bump) 25 | uses: mara-li/reusable-workflows/.github/workflows/obsidian-plugin-release.yaml@main 26 | 27 | with: 28 | PLUGIN_NAME: regex-mark 29 | CACHE: "bun" 30 | secrets: 31 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | 33 | bump-version-and-release: 34 | if: ${{ inputs.bump }} 35 | uses: mara-li/reusable-workflows/.github/workflows/obsidian-plugin-bump-version.yaml@main 36 | with: 37 | PLUGIN_NAME: regex-mark 38 | BETA: ${{ inputs.beta }} 39 | CACHE: "bun" 40 | secrets: 41 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # Intellij 5 | *.iml 6 | .idea 7 | 8 | # npm 9 | node_modules 10 | 11 | # Don't include the compiled main.js file in the repo. 12 | # They should be uploaded to GitHub releases instead. 13 | main.js 14 | styles.css 15 | !src/styles.css 16 | dist/* 17 | .env 18 | 19 | # Exclude sourcemaps 20 | *.map 21 | 22 | # obsidian 23 | data.json 24 | 25 | # Exclude macOS Finder (System Explorer) View States 26 | .DS_Store 27 | /.hotreload 28 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | tag-version-prefix="" -------------------------------------------------------------------------------- /CHANGELOG-beta.md: -------------------------------------------------------------------------------- 1 | ## [1.10.0-0](https://github.com/Mara-Li/obsidian-regex-mark/compare/1.9.3...1.10.0-0) (2024-12-27) 2 | ### Features 3 | 4 | * integrate "subgroup" with named group for more granular CSS ([95351da](https://github.com/Mara-Li/obsidian-regex-mark/commit/95351da33891efe5dee805f89a5d0012b40713fe)) 5 | 6 | ## [1.7.3-1](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.7.3-0...1.7.3-1) (2024-08-11) 7 | 8 | ## [1.7.3-0](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.7.2...1.7.3-0) (2024-08-11) 9 | 10 | ## [1.6.0-0](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.5.3...1.6.0-0) (2024-06-04) 11 | ### Features 12 | 13 | * allow to disable a class momentally ([8c82f8d](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/8c82f8dcbf5d999049bd3b4da47212cbb4999aa5)), closes [#3](https://github.com/Lisandra-dev/obsidian-regex-mark/issues/3) -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.10.0](https://github.com/Mara-Li/obsidian-regex-mark/compare/1.10.0-0...1.10.0) (2025-04-30) 2 | ### Bug Fixes 3 | 4 | * incorrect set of hide for non applicable regex ([#26](https://github.com/Mara-Li/obsidian-regex-mark/issues/26)) ([6f00efd](https://github.com/Mara-Li/obsidian-regex-mark/commit/6f00efdac63124351c71389fd9423a7178db0c60)) 5 | 6 | ## [1.9.3](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.9.2...1.9.3) (2024-11-16) 7 | ### Bug Fixes 8 | 9 | * **LP:** forgot to update lp mode with open/close setting change ([55f28ac](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/55f28ac3b75c50e5d02d103ed86c3bd188e8f625)), closes [#19](https://github.com/Lisandra-dev/obsidian-regex-mark/issues/19) 10 | 11 | ## [1.9.2](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.9.1...1.9.2) (2024-09-07) 12 | 13 | ## [1.9.1](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.9.0...1.9.1) (2024-09-07) 14 | ### Bug Fixes 15 | 16 | * **pattern:** DEFAULT_PATTERN not escaped properly ([e6088f5](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/e6088f5ef395f01bc448a9215462f010a4396657)) 17 | 18 | ## [1.9.0](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.8.0...1.9.0) (2024-09-07) 19 | ### Features 20 | 21 | * allow changing open/close pattern ([a1bc5e8](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/a1bc5e81dbd813fd3d19778131a7a1bdb73ecd4e)), closes [#14](https://github.com/Lisandra-dev/obsidian-regex-mark/issues/14) 22 | 23 | ## [1.8.0](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.7.3-1...1.8.0) (2024-09-01) 24 | ### Features 25 | 26 | * add support for flags ([7b0deea](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/7b0deeae17656bc4222168909e9c9c23b55fe55f)), closes [#12](https://github.com/Lisandra-dev/obsidian-regex-mark/issues/12) 27 | * **code:** allow to enable/disable mark on code (inlines+block) ([2ee8f8e](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/2ee8f8eb6582614864466097b49374aa4518318a)), closes [#13](https://github.com/Lisandra-dev/obsidian-regex-mark/issues/13) 28 | 29 | ### Bug Fixes 30 | 31 | * prevent duplicate flags ([96d7980](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/96d79806728d66c948c5afbb992113a840ab79de)), closes [#13](https://github.com/Lisandra-dev/obsidian-regex-mark/issues/13) 32 | 33 | ## [1.7.2](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.7.0...1.7.2) (2024-07-02) 34 | ### Bug Fixes 35 | 36 | * in table class doesn't apply ([07c80fb](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/07c80fb1cbfb7f888658ddef8ee7dafe5724662d)), closes [#8](https://github.com/Lisandra-dev/obsidian-regex-mark/issues/8) 37 | 38 | ## [1.7.1](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.7.0...1.7.1) (2024-06-26) 39 | ### Bug Fixes 40 | 41 | * in table class doesn't apply ([07c80fb](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/07c80fb1cbfb7f888658ddef8ee7dafe5724662d)), closes [#8](https://github.com/Lisandra-dev/obsidian-regex-mark/issues/8) 42 | 43 | ## [1.7.0](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.6.4...1.7.0) (2024-06-23) 44 | ### Features 45 | 46 | * allow to hide per view mode (reading, source & LP) ([01f3da3](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/01f3da397d9be556981421a618d7def2f8b6c7bf)) 47 | 48 | ### Bug Fixes 49 | 50 | * new regex disabled ([927a306](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/927a30616ae67ef605319670c015d87b331cde17)) 51 | * structured clone + styles ([a6f7536](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/a6f7536de58bbcf95b8bb4b5504cfd4e5e5a7a59)) 52 | 53 | ## [1.6.3](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.6.2...1.6.3) (2024-06-08) 54 | ### Bug Fixes 55 | 56 | * temp fix to exclude regex that can match newline ([9b37f64](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/9b37f64202a780321a61843dcea5651035e714d5)) 57 | 58 | ## [1.6.2](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.6.1...1.6.2) (2024-06-04) 59 | ### Bug Fixes 60 | 61 | * regex placement ([3ac4363](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/3ac4363643558525f89380bc36444a27c571f6d9)) 62 | 63 | ## [1.6.1](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.6.0...1.6.1) (2024-06-04) 64 | ### Bug Fixes 65 | 66 | * stuck on disabled even regex are valid ([06c9213](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/06c9213a3dce50a7763910471d18da7e7cf8e50f)) 67 | 68 | ## [1.6.0](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.6.0-0...1.6.0) (2024-06-04) 69 | 70 | ## [1.5.3](https://github.com/Mara-Li/obsidian-regex-mark/compare/1.5.2...1.5.3) (2024-05-28) 71 | ### Bug Fixes 72 | 73 | * remove unused css-class ([e3a5a94](https://github.com/Mara-Li/obsidian-regex-mark/commit/e3a5a948ab11df47918360ebb8d6f3d35848f837)) 74 | * set a tooltip when hide is disabled ([787fe8b](https://github.com/Mara-Li/obsidian-regex-mark/commit/787fe8b14456d4f85ec379f13d19a191ce86a5d5)) 75 | 76 | ## [1.5.2](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.5.1...1.5.2) (2024-05-24) 77 | ### Bug Fixes 78 | 79 | * avoid innerHTML and use `sanitizeHTMLToDom` instead ([fd50ce0](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/fd50ce0818beaa28e2da04ad42c2aecb3f69603e)) 80 | 81 | ## [1.5.1](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.5.0...1.5.1) (2023-12-24) 82 | ### Bug Fixes 83 | 84 | * forgot to remove logs ([bc30cb6](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/bc30cb63717a714b320274b2cfcc7ac054848e64)) 85 | 86 | ## [1.5.0](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.4.2...1.5.0) (2023-12-22) 87 | ### Features 88 | 89 | * allow table and callout title to be rendered too ([c01456c](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/c01456cb74542ad92438771e21ebb202671b3ac8)) 90 | 91 | ## [1.4.2](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.4.1...1.4.2) (2023-12-22) 92 | ### Bug Fixes 93 | 94 | * allow table ([d012425](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/d012425758007362d8c1570d6235f21f9f46431a)) 95 | 96 | ## [1.4.1](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.4.0...1.4.1) (2023-12-03) 97 | ### Bug Fixes 98 | 99 | * all regex without close/open return null ([4364939](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/436493969e9e37f66e3417871892f059ea342f17)) 100 | * allow to have markup in heading ([5a544db](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/5a544db8a34bcf423998f43998f44b657d5eb0dc)) 101 | 102 | ## [1.4.0](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.3.0...1.4.0) (2023-12-03) 103 | ### Features 104 | 105 | * prevent regex with \} as they are wrongly parsed ([162493a](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/162493a88ec7ae7717afd3d133462df48f23edbd)) 106 | 107 | ## [1.3.0](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.2.2...1.3.0) (2023-12-02) 108 | ### Features 109 | 110 | * add support for "li" ! ([7f36774](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/7f36774068a39f1a3dbd1e7f3245ce38ba4fbc7c)) 111 | 112 | ## [1.2.2](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.2.1...1.2.2) (2023-11-30) 113 | ### Bug Fixes 114 | 115 | * better message ([c4efca2](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/c4efca27e2dd5f2479e0e8b00e7be85772df03e8)) 116 | * prevent duplicate regex ([4763186](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/47631861dbe66eabddb514d56ed1668970c31b8b)) 117 | 118 | ## [1.2.1](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.2.0...1.2.1) (2023-11-30) 119 | ### Bug Fixes 120 | 121 | * disable toggle if regex is invalid ([d90cb4c](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/d90cb4c434318a15ad6b9cfbc358588e0ed0d990)) 122 | 123 | ## [1.2.0](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.1.6...1.2.0) (2023-11-29) 124 | ### Features 125 | 126 | * reload extension when settings change ([f35c5b9](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/f35c5b95a8c62e9aa86fd92723b6699d34ca910c)) 127 | 128 | ## [1.1.6](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.1.5...1.1.6) (2023-11-29) 129 | 130 | ## [1.1.5](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.1.4...1.1.5) (2023-11-29) 131 | ### Bug Fixes 132 | 133 | * cursor bug (again) ([add93b2](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/add93b2d5b98b89e75ed5680d1dbec5a1c16e920)) 134 | 135 | ## [1.1.4](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.1.3...1.1.4) (2023-11-29) 136 | 137 | ## [1.1.3](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.1.2...1.1.3) (2023-11-29) 138 | 139 | ## [1.1.2](https://github.com/Lisandra-dev/obsidian-regex-mark/compare/1.1.1...1.1.2) (2023-11-29) 140 | ### Bug Fixes 141 | 142 | * error in cursor position ([1671349](https://github.com/Lisandra-dev/obsidian-regex-mark/commit/167134943d270906e9de900a2b84252dedc32271)) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Mara-Li 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Obsidian Regex Mark is a plugin for [Obsidian](https://obsidian.md/) that allows to add custom CSS classes to text based on regular expressions. 2 | 3 | # Usage 4 | 5 | Add a regular expression and a CSS class to the plugin settings. The plugin will then add the CSS class to any text that matches the regular expression. 6 | 7 | The contents of the regex will be added to the span tag with the `data-content` attributes for a more granular styling. 8 | 9 | # How it works 10 | 11 | The following regular expression will add the CSS class `comment` to any text that matches the regular expression. 12 | 13 | ``` 14 | regex: //.*$ 15 | class: comment 16 | ``` 17 | 18 | And the following text: 19 | 20 | ```markdown 21 | This is a normal line of text. //This is a comment. 22 | ``` 23 | 24 | will be converted to: 25 | 26 | ```html 27 |

This is a normal line of text. //This is a comment.

28 | ``` 29 | 30 | It is also possible to create "custom" Markdown tags using the `{{open:regex}}` and `{{close:regex}}` syntax. You need to toggle the option `hide` to enable it. 31 | 32 | > [!NOTE] 33 | > The usage of `__` for underline (ie `__text__`) is not supported in reading 34 | > mode, but works well in Live Preview! 35 | 36 | The plugin will mimic the behavior of Obsidian with transforming: 37 | 38 | ```markdown 39 | __text__ 40 | ``` 41 | 42 | to: 43 | 44 | ```html 45 | 46 | 47 | __ 48 | this is a normal text with underline (in LP) 49 | __ 50 | 51 | 52 | ``` 53 | 54 | The css will after hide the `.cm-hide` class, unless you select it (only in livePreview for the selection). 55 | 56 | Selecting the text will show everything, like with other markup. 57 | 58 | ## Named group 59 | You can also encapsulate a sub-css into a regex mark, using named group. The group name will be used as the CSS class. 60 | For example : 61 | - Regex : `{{open:^-# }}((@(?.*)@)?(?.*))` 62 | - Text : `^-# @bold@ hello world` 63 | - Result : `bold hello world!` 64 | 65 | > [!important] 66 | > If you want to keep the rest of the text, you need to add another **named group** at the end of the regex, like `(?.*)` in the example above. 67 | > This mean that is not possible to match multiple time the same group in the same text: `-# @bold@ hello world @bold@` will not work as expected. 68 | 69 | # Settings 70 | 71 | ![img.png](_assets/settings.png) 72 | 73 | ## View mode 74 | 75 | ![img.png](_assets/view_mode.png) 76 | You can disable "per view" the regex, aka, disabling for: 77 | 78 | - Reading mode 79 | - Live Preview mode 80 | - Source mode 81 | 82 | Each toggle are independent, so you can disable for reading mode, but enable for live preview mode. 83 | 84 | You can also disable/enable a snippet within code (code-block or inline, between backticks), with toggle "Code". 85 | 86 | ## Change open/close tags (advanced user only!) 87 | 88 | This setting allow to change the default `{{open:}}` and `{{close:}}` tags to something else, allowing to use the `}` (for example) as an opening/closing (ie `{regex}` can be recognized and stylized). When the tags are changed, the regex are automatically updated to use the new tags. If a regex is broken, it will be disabled in all view, and a warning will be displayed in a Notice. 89 | 90 | ## Import / export 91 | 92 | You can share your regexes with other people without manually sharing the `data.json` using the import / export feature. You can also use it to back up your regexes. 93 | 94 | The settings also port the open/close tags, so you can share the settings with the tags you use. Manual regex in object format can also be imported, and they will be added to the list. 95 | 96 | # Next steps 97 | 98 | You can then use the CSS class to style the text in your CSS snippet or any other usages. You can even import or export settings from others! 99 | 100 | ## Examples 101 | 102 | - Underline : `{{open:__}}(.*){{close:__}}` 103 | Note: To make it works in Reading mode, read [this](#additional-notes) 104 | - SuperScript : `{{open:\^}}(.)` (Note: It works for "one" character only here, but you can use 105 | `^x^` syntax if you want to use for more text: 106 | `{{open:\^}}(.*){{close:\^}}`) 107 | - SubScript : `{{open:_}}([a-zA-Z\d]\b))` (As before, it works for only one element) 108 | 109 | The CSS for the above example is: 110 | 111 | ```css 112 | .superscript { 113 | vertical-align: super; 114 | font-size: 90%; 115 | } 116 | 117 | .subscript { 118 | vertical-align: sub; 119 | font-size: 90%; 120 | } 121 | ``` 122 | 123 | ## Additional notes 124 | 125 | In **reading mode** the plugin can't override the default mark behavior. For example, `**` and `__` will be always bold, and render without the tag. The plugin won't recognize the opening and closing pattern, and ignore it. 126 | 127 | To prevent this, you can escape the pattern using a backslash (`\`): 128 | 129 | ```md 130 | \__hello world\__ 131 | lorem ipsum (won't be in italic) 132 | ``` 133 | 134 | In reading view, it will render as: 135 | 136 | ```text 137 | __hello world__ 138 | ``` 139 | 140 | And the regex should be: `{{open:\\?_\\?_}}(.*){{close:\\?_\\?_}}` 141 | You **need** to make the backslash optional, because they don't render in the reading view. 142 | 143 | --- 144 | > [!WARNING] 145 | > If your Obsidian refuse to open a file, and you get this error in the console: 146 | > 147 | > ```shell 148 | > RangeError: Decorations that replace line breaks may not be specified via plugins 149 | > ``` 150 | > That's mean that one of your regex doesn't work inside Obsidian. You need to check every regex you have added to find the one that cause the issue and delete it, and reload Obsidian to make it work again. 151 | > I tried to find a way to catch the error and display it in the console, but I didn't find a way to do it... So, if you have any idea, I'm open to suggestions! 152 | 153 | --- 154 | 155 | # Credits 156 | 157 | - [rien7](https://github.com/rien7/obsidian-regex-mark): Original work 158 | -------------------------------------------------------------------------------- /_assets/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mara-Li/obsidian-regex-mark/58b25656351c6928c5e5ffce2b0122cc26f406d8/_assets/settings.png -------------------------------------------------------------------------------- /_assets/view_mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mara-Li/obsidian-regex-mark/58b25656351c6928c5e5ffce2b0122cc26f406d8/_assets/view_mode.png -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", 3 | "assist": { 4 | "actions": { 5 | "source": { 6 | "organizeImports": "on" 7 | } 8 | } 9 | }, 10 | "linter": { 11 | "enabled": true, 12 | "rules": { 13 | "recommended": false, 14 | "complexity": { 15 | "noExtraBooleanCast": "error", 16 | "noAdjacentSpacesInRegex": "error", 17 | "noUselessCatch": "error", 18 | "noWith": "error" 19 | }, 20 | "correctness": { 21 | "noConstAssign": "error", 22 | "noConstantCondition": "error", 23 | "noEmptyCharacterClassInRegex": "error", 24 | "noEmptyPattern": "error", 25 | "noGlobalObjectCalls": "error", 26 | "noInnerDeclarations": "error", 27 | "noInvalidConstructorSuper": "error", 28 | "noInvalidBuiltinInstantiation": { 29 | "level": "error" 30 | }, 31 | "noNonoctalDecimalEscape": "error", 32 | "noPrecisionLoss": "error", 33 | "noSelfAssign": "error", 34 | "noSetterReturn": "error", 35 | "noSwitchDeclarations": "error", 36 | "noUndeclaredVariables": "error", 37 | "noUnreachable": "error", 38 | "noUnreachableSuper": "error", 39 | "noUnsafeFinally": "error", 40 | "noUnsafeOptionalChaining": "error", 41 | "noUnusedLabels": "error", 42 | "noUnusedVariables": "warn", 43 | "noUnusedImports": "warn", 44 | "useIsNan": "error", 45 | "useValidForDirection": "error", 46 | "useYield": "error" 47 | }, 48 | "suspicious": { 49 | "noAssignInExpressions": "error", 50 | "noAsyncPromiseExecutor": "error", 51 | "noCatchAssign": "error", 52 | "noClassAssign": "error", 53 | "noCompareNegZero": "error", 54 | "noControlCharactersInRegex": "error", 55 | "noDebugger": "error", 56 | "noDuplicateCase": "error", 57 | "noDuplicateClassMembers": "error", 58 | "noDuplicateObjectKeys": "error", 59 | "noDuplicateParameters": "error", 60 | "noEmptyBlockStatements": "error", 61 | "noFallthroughSwitchClause": "error", 62 | "noFunctionAssign": "error", 63 | "noGlobalAssign": "error", 64 | "noImportAssign": "error", 65 | "noMisleadingCharacterClass": "error", 66 | "noPrototypeBuiltins": "error", 67 | "noRedeclare": "error", 68 | "noShadowRestrictedNames": "error", 69 | "noUnsafeNegation": "error", 70 | "useGetterReturn": "error", 71 | "useValidTypeof": "error", 72 | "noVar": "error" 73 | }, 74 | "style": { 75 | "useFilenamingConvention": "error", 76 | "useImportType": "error", 77 | "useNamingConvention": { 78 | "level": "warn", 79 | "options": { 80 | "strictCase": false 81 | } 82 | }, 83 | "useTemplate": "warn", 84 | "useConst": "error" 85 | } 86 | }, 87 | "includes": [ 88 | "**", 89 | "!**/npm node_modules", 90 | "!**/build", 91 | "!**/dist", 92 | "!**/src/i18n/locales" 93 | ] 94 | }, 95 | "overrides": [ 96 | { 97 | "includes": [ 98 | "**/*.ts", 99 | "**/*.tsx", 100 | "!**/*.js", 101 | "!**/node_modules/**" 102 | ] 103 | }, 104 | { 105 | "includes": [ 106 | "**/*.js", 107 | "!**/node_modules/**" 108 | ] 109 | }, 110 | { 111 | "includes": [ 112 | "**/*.json", 113 | "!**/node_modules/**" 114 | ] 115 | } 116 | ], 117 | "formatter": { 118 | "enabled": true, 119 | "indentStyle": "tab", 120 | "indentWidth": 2, 121 | "lineWidth": 120 122 | }, 123 | "javascript": { 124 | "formatter": { 125 | "quoteStyle": "double", 126 | "semicolons": "always", 127 | "trailingCommas": "es5" 128 | } 129 | }, 130 | "json": { 131 | "formatter": { 132 | "trailingCommas": "none" 133 | } 134 | } 135 | } -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mara-Li/obsidian-regex-mark/58b25656351c6928c5e5ffce2b0122cc26f406d8/bun.lockb -------------------------------------------------------------------------------- /commit-and-tag-version.mjs: -------------------------------------------------------------------------------- 1 | import { Command, Option } from "commander"; 2 | import commitAndTagVersion from "commit-and-tag-version"; 3 | import dedent from "dedent"; 4 | import pkg from "ansi-colors"; 5 | const { red, dim, gray, italic, bold, cyan, blue, green, underline, yellow, theme } = pkg; 6 | 7 | const program = new Command(); 8 | 9 | theme({ 10 | danger: red, 11 | dark: dim.gray, 12 | disabled: gray, 13 | em: italic, 14 | heading: bold.underline, 15 | info: cyan, 16 | muted: dim, 17 | primary: blue, 18 | strong: bold, 19 | success: green.bold, 20 | warning: yellow.underline, 21 | }); 22 | 23 | const info = (msg) => pkg.info(msg); 24 | const heading = (msg) => pkg.heading(msg); 25 | const em = (msg) => pkg.em(msg); 26 | 27 | program 28 | .description("Bump version and create a new tag") 29 | .option("-b, --beta", "Pre-release version") 30 | .option("--dry-run", "Dry run") 31 | .addOption( 32 | new Option("-r, --release-as ", "release type version").choices([ 33 | "major", 34 | "minor", 35 | "patch", 36 | ]) 37 | ); 38 | 39 | program.parse(); 40 | const opt = program.opts(); 41 | 42 | const betaMsg = opt.beta ? em("- Pre-release\n\t") : ""; 43 | const dryRunMsg = opt.dryRun ? em("- Dry run\n\t") : ""; 44 | const releaseAsMsg = opt.releaseAs ? em(`- Release as ${underline(opt.releaseAs)}`) : ""; 45 | 46 | const msg = dedent(` 47 | ${heading("Options :")} 48 | ${betaMsg}${dryRunMsg}${releaseAsMsg} 49 | `); 50 | 51 | console.log(msg); 52 | console.log(); 53 | 54 | if (opt.beta) { 55 | console.log(`${bold.green(">")} ${info(underline("Bumping beta version..."))}`); 56 | console.log(); 57 | const bumpFiles = [ 58 | { 59 | filename: "manifest-beta.json", 60 | type: "json", 61 | }, 62 | { 63 | filename: "package.json", 64 | type: "json", 65 | }, 66 | { 67 | filename: "package-lock.json", 68 | type: "json", 69 | }, 70 | ]; 71 | commitAndTagVersion({ 72 | infile: "CHANGELOG-beta.md", 73 | bumpFiles, 74 | prerelease: "", 75 | dryRun: opt.dryRun, 76 | tagPrefix: "", 77 | scripts: { 78 | postchangelog: "node hooks/_changelog.mjs -b", 79 | }, 80 | }) 81 | .then(() => { 82 | console.log("Done"); 83 | }) 84 | .catch((err) => { 85 | console.error(err); 86 | }); 87 | } else { 88 | const versionBumped = opt.releaseAs 89 | ? info(`Release as ${underline(opt.releaseAs)}`) 90 | : info("Release"); 91 | console.log(`${bold.green(">")} ${underline(versionBumped)}`); 92 | console.log(); 93 | 94 | const bumpFiles = [ 95 | { 96 | filename: "manifest-beta.json", 97 | type: "json", 98 | }, 99 | { 100 | filename: "package.json", 101 | type: "json", 102 | }, 103 | { 104 | filename: "package-lock.json", 105 | type: "json", 106 | }, 107 | { 108 | filename: "manifest.json", 109 | type: "json", 110 | }, 111 | ]; 112 | 113 | commitAndTagVersion({ 114 | infile: "CHANGELOG.md", 115 | bumpFiles, 116 | dryRun: opt.dryRun, 117 | tagPrefix: "", 118 | releaseAs: opt.releaseAs, 119 | scripts: { 120 | postchangelog: "node hooks/_changelog.mjs", 121 | }, 122 | }) 123 | .then(() => { 124 | console.log("Done"); 125 | }) 126 | .catch((err) => { 127 | console.error(err); 128 | }); 129 | } 130 | -------------------------------------------------------------------------------- /esbuild.config.mjs: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import * as path from "path"; 3 | import builtins from "builtin-modules"; 4 | import { Command } from "commander"; 5 | import dotenv from "dotenv"; 6 | import esbuild from "esbuild"; 7 | import manifest from "./manifest.json" with { type: "json" }; 8 | import packageJson from "./package.json" with { type: "json" }; 9 | 10 | // Initial configuration 11 | dotenv.config({ path: [".env"] }); 12 | 13 | // Parsing command line arguments 14 | const program = new Command(); 15 | program 16 | .option("-p, --production", "Production build") 17 | .option("-v, --vault [vault]", "Use vault path", false) 18 | .option("-o, --output-dir ", "Output path") 19 | .option("-b, --beta", "Pre-release version") 20 | .parse(); 21 | 22 | // Get options 23 | const options = program.opts(); 24 | const isProd = !!options.production; 25 | const isBeta = !!options.beta; 26 | const isStyled = fs.existsSync("src/styles.css"); 27 | const pluginID = manifest.id; 28 | 29 | // Banner for the output file 30 | const banner = `/* 31 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD 32 | if you want to view the source, please visit the github repository of this plugin: ${packageJson.repository} 33 | */`; 34 | 35 | // Determine the output directory based on options 36 | function resolveOutputDir() { 37 | if (options.outputDir) return options.outputDir; 38 | 39 | if (options.vault) { 40 | const vaultPath = typeof options.vault === "string" 41 | ? options.vault 42 | : process.env.VAULT; 43 | 44 | if (!vaultPath) throw new Error("VAULT environment variable not set"); 45 | 46 | const folderPath = path.join(vaultPath, ".obsidian", "plugins", pluginID); 47 | if (!fs.existsSync(folderPath)) fs.mkdirSync(folderPath, { recursive: true }); 48 | 49 | if (!isProd) fs.writeFileSync(path.join(folderPath, ".hotreload"), ""); 50 | 51 | return folderPath; 52 | } 53 | 54 | return isProd ? "./dist" : "./"; 55 | } 56 | 57 | // Prepare the output directory 58 | function prepareOutputDir(dir) { 59 | if ((isProd || options.outputDir) && fs.existsSync(dir)) { 60 | fs.rmSync(dir, { recursive: true }); 61 | } 62 | 63 | if (isBeta && !fs.existsSync("manifest-beta.json")) { 64 | fs.copyFileSync("manifest.json", "manifest-beta.json"); 65 | } 66 | } 67 | 68 | // Get plugins for esbuild 69 | function getPlugins(outDir) { 70 | const plugins = []; 71 | 72 | // Plugin pour déplacer les styles 73 | if (isStyled) { 74 | plugins.push({ 75 | name: "move-styles", 76 | setup(build) { 77 | build.onEnd(() => { 78 | fs.copyFileSync("src/styles.css", path.join(outDir, "styles.css")); 79 | }); 80 | } 81 | }); 82 | } 83 | 84 | // Copy manifest file 85 | plugins.push({ 86 | name: "copy-manifest", 87 | setup(build) { 88 | build.onEnd(() => { 89 | const manifestSource = isBeta ? "manifest-beta.json" : "manifest.json"; 90 | fs.copyFileSync(manifestSource, path.join(outDir, "manifest.json")); 91 | }); 92 | } 93 | }); 94 | 95 | return plugins; 96 | } 97 | 98 | // Principal configuration 99 | async function buildPlugin() { 100 | const outDir = resolveOutputDir(); 101 | prepareOutputDir(outDir); 102 | 103 | const entryPoints = ["src/main.ts"]; 104 | if (isStyled) entryPoints.push("src/styles.css"); 105 | 106 | // Créer le contexte esbuild 107 | const context = await esbuild.context({ 108 | banner: { js: banner }, 109 | entryPoints, 110 | bundle: true, 111 | external: [ 112 | "obsidian", 113 | "electron", 114 | "@codemirror/autocomplete", 115 | "@codemirror/collab", 116 | "@codemirror/commands", 117 | "@codemirror/language", 118 | "@codemirror/lint", 119 | "@codemirror/search", 120 | "@codemirror/state", 121 | "@codemirror/view", 122 | "@lezer/common", 123 | "@lezer/highlight", 124 | "@lezer/lr", 125 | ...builtins, 126 | ], 127 | format: "cjs", 128 | target: "esnext", 129 | logLevel: "info", 130 | sourcemap: isProd ? false : "inline", 131 | treeShaking: true, 132 | minify: isProd, 133 | minifySyntax: isProd, 134 | minifyWhitespace: isProd, 135 | outdir: outDir, 136 | plugins: getPlugins(outDir) 137 | }); 138 | 139 | console.log(`🚀 ${isProd ? 'Production' : 'Development'} build`); 140 | console.log(`📤 Output directory: ${outDir}`); 141 | 142 | if (isProd) { 143 | await context.rebuild(); 144 | console.log("✅ Build successful"); 145 | process.exit(0); 146 | } else { 147 | await context.watch(); 148 | } 149 | } 150 | 151 | buildPlugin().catch(err => { 152 | console.error("Build failed:", err); 153 | process.exit(1); 154 | }); -------------------------------------------------------------------------------- /hooks/_changelog.mjs: -------------------------------------------------------------------------------- 1 | import { readFileSync, writeFile } from "fs"; 2 | 3 | import { Command } from "commander"; 4 | const program = new Command(); 5 | 6 | program.option("-b, --beta", "Pre-release version"); 7 | 8 | program.parse(); 9 | const opt = program.opts(); 10 | 11 | /** 12 | * Remove text from the file 13 | * @param {string} path 14 | */ 15 | function removeText(path) { 16 | const toRemove = [ 17 | "# Changelog", 18 | "All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.", 19 | ]; 20 | let changelog = readFileSync(path, "utf8"); 21 | for (const remove of toRemove) changelog = changelog.replace(remove, "").trim(); 22 | changelog = changelog.replaceAll(/[\n\r]{3,}/gm, "\n\n").trim(); 23 | changelog = changelog.replaceAll(/## (.*)[\n\r]{2}### /gm, "## $1\n### ").trim(); 24 | writeFile(path, changelog.trim(), "utf8", (err) => { 25 | if (err) return console.error(err); 26 | }); 27 | } 28 | 29 | if (!opt.beta) removeText("CHANGELOG.md"); 30 | else removeText("CHANGELOG-beta.md"); 31 | -------------------------------------------------------------------------------- /manifest-beta.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "regex-mark", 3 | "name": "Regex Mark", 4 | "version": "1.10.0", 5 | "minAppVersion": "1.5.12", 6 | "description": "Add custom CSS classes to text based on regular expressions.", 7 | "author": "rien7, Mara-Li", 8 | "authorUrl": "https://github.com/mara-li", 9 | "isDesktopOnly": false, 10 | "fundingUrl": "https://ko-fi.com/mara__li" 11 | } 12 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "regex-mark", 3 | "name": "Regex Mark", 4 | "version": "1.10.0", 5 | "minAppVersion": "1.5.12", 6 | "description": "Add custom CSS classes to text based on regular expressions.", 7 | "author": "rien7, Mara-Li", 8 | "authorUrl": "https://github.com/mara-li", 9 | "isDesktopOnly": false, 10 | "fundingUrl": "https://ko-fi.com/mara__li" 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obsidian-regex-mark", 3 | "version": "1.10.0", 4 | "description": "Add custom CSS classes to text based on regular expressions.", 5 | "main": "main.js", 6 | "private": "true", 7 | "scripts": { 8 | "prebuild": "tsc --noEmit --skipLibCheck", 9 | "build": "node esbuild.config.mjs --production", 10 | "dev:prod": "node esbuild.config.mjs --vault", 11 | "dev": "node esbuild.config.mjs", 12 | "export": "node esbuild.config.mjs --production --vault", 13 | "prerelease": "tsc --noEmit --skipLibCheck", 14 | "release": "node commit-and-tag-version.mjs", 15 | "postrelease": "git push --follow-tags origin master", 16 | "lint": "bun biome format --write src/" 17 | }, 18 | "commit-and-tag-version": { 19 | "t": "" 20 | }, 21 | "keywords": [], 22 | "author": "", 23 | "license": "MIT", 24 | "devDependencies": { 25 | "@biomejs/biome": "^2.0.0-beta.2", 26 | "@codemirror/search": "^6.5.10", 27 | "@codemirror/state": "^6.5.2", 28 | "@codemirror/view": "^6.36.6", 29 | "@types/dompurify": "^3.2.0", 30 | "@types/lodash": "^4.17.16", 31 | "@types/node": "^22.15.3", 32 | "builtin-modules": "5.0.0", 33 | "esbuild": "0.25.3", 34 | "lodash": "^4.17.21", 35 | "obsidian": "^1.8.7", 36 | "ts-dedent": "^2.2.0", 37 | "tslib": "2.8.1", 38 | "typescript": "5.8.3" 39 | }, 40 | "dependencies": { 41 | "@codemirror/language": "^6.11.0", 42 | "ansi-colors": "^4.1.3", 43 | "commander": "^13.1.0", 44 | "commit-and-tag-version": "^12.5.1", 45 | "dedent": "^1.5.3", 46 | "dotenv": "^16.5.0" 47 | }, 48 | "trustedDependencies": [ 49 | "@biomejs/biome", 50 | "esbuild" 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@codemirror/language': 12 | specifier: ^6.9.3 13 | version: 6.10.1 14 | ansi-colors: 15 | specifier: ^4.1.3 16 | version: 4.1.3 17 | commander: 18 | specifier: ^12.1.0 19 | version: 12.1.0 20 | commit-and-tag-version: 21 | specifier: ^12.0.0 22 | version: 12.0.0 23 | dedent: 24 | specifier: ^1.5.1 25 | version: 1.5.3 26 | dotenv: 27 | specifier: ^16.4.5 28 | version: 16.4.5 29 | devDependencies: 30 | '@biomejs/biome': 31 | specifier: 1.8.3 32 | version: 1.8.3 33 | '@codemirror/search': 34 | specifier: ^6.5.4 35 | version: 6.5.4 36 | '@codemirror/state': 37 | specifier: ^6.3.1 38 | version: 6.3.1 39 | '@codemirror/view': 40 | specifier: ^6.22.0 41 | version: 6.22.0 42 | '@types/dompurify': 43 | specifier: ^3.0.5 44 | version: 3.0.5 45 | '@types/lodash': 46 | specifier: ^4.14.201 47 | version: 4.14.201 48 | '@types/node': 49 | specifier: ^16.11.6 50 | version: 16.11.6 51 | builtin-modules: 52 | specifier: 3.3.0 53 | version: 3.3.0 54 | esbuild: 55 | specifier: 0.17.3 56 | version: 0.17.3 57 | lodash: 58 | specifier: ^4.17.21 59 | version: 4.17.21 60 | obsidian: 61 | specifier: latest 62 | version: 1.4.11(@codemirror/state@6.3.1)(@codemirror/view@6.22.0) 63 | ts-dedent: 64 | specifier: ^2.2.0 65 | version: 2.2.0 66 | tslib: 67 | specifier: 2.4.0 68 | version: 2.4.0 69 | typescript: 70 | specifier: 5.5.2 71 | version: 5.5.2 72 | 73 | packages: 74 | 75 | '@babel/code-frame@7.23.4': 76 | resolution: {integrity: sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA==} 77 | engines: {node: '>=6.9.0'} 78 | 79 | '@babel/helper-validator-identifier@7.22.20': 80 | resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} 81 | engines: {node: '>=6.9.0'} 82 | 83 | '@babel/highlight@7.23.4': 84 | resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} 85 | engines: {node: '>=6.9.0'} 86 | 87 | '@biomejs/biome@1.8.3': 88 | resolution: {integrity: sha512-/uUV3MV+vyAczO+vKrPdOW0Iaet7UnJMU4bNMinggGJTAnBPjCoLEYcyYtYHNnUNYlv4xZMH6hVIQCAozq8d5w==} 89 | engines: {node: '>=14.21.3'} 90 | hasBin: true 91 | 92 | '@biomejs/cli-darwin-arm64@1.8.3': 93 | resolution: {integrity: sha512-9DYOjclFpKrH/m1Oz75SSExR8VKvNSSsLnVIqdnKexj6NwmiMlKk94Wa1kZEdv6MCOHGHgyyoV57Cw8WzL5n3A==} 94 | engines: {node: '>=14.21.3'} 95 | cpu: [arm64] 96 | os: [darwin] 97 | 98 | '@biomejs/cli-darwin-x64@1.8.3': 99 | resolution: {integrity: sha512-UeW44L/AtbmOF7KXLCoM+9PSgPo0IDcyEUfIoOXYeANaNXXf9mLUwV1GeF2OWjyic5zj6CnAJ9uzk2LT3v/wAw==} 100 | engines: {node: '>=14.21.3'} 101 | cpu: [x64] 102 | os: [darwin] 103 | 104 | '@biomejs/cli-linux-arm64-musl@1.8.3': 105 | resolution: {integrity: sha512-9yjUfOFN7wrYsXt/T/gEWfvVxKlnh3yBpnScw98IF+oOeCYb5/b/+K7YNqKROV2i1DlMjg9g/EcN9wvj+NkMuQ==} 106 | engines: {node: '>=14.21.3'} 107 | cpu: [arm64] 108 | os: [linux] 109 | 110 | '@biomejs/cli-linux-arm64@1.8.3': 111 | resolution: {integrity: sha512-fed2ji8s+I/m8upWpTJGanqiJ0rnlHOK3DdxsyVLZQ8ClY6qLuPc9uehCREBifRJLl/iJyQpHIRufLDeotsPtw==} 112 | engines: {node: '>=14.21.3'} 113 | cpu: [arm64] 114 | os: [linux] 115 | 116 | '@biomejs/cli-linux-x64-musl@1.8.3': 117 | resolution: {integrity: sha512-UHrGJX7PrKMKzPGoEsooKC9jXJMa28TUSMjcIlbDnIO4EAavCoVmNQaIuUSH0Ls2mpGMwUIf+aZJv657zfWWjA==} 118 | engines: {node: '>=14.21.3'} 119 | cpu: [x64] 120 | os: [linux] 121 | 122 | '@biomejs/cli-linux-x64@1.8.3': 123 | resolution: {integrity: sha512-I8G2QmuE1teISyT8ie1HXsjFRz9L1m5n83U1O6m30Kw+kPMPSKjag6QGUn+sXT8V+XWIZxFFBoTDEDZW2KPDDw==} 124 | engines: {node: '>=14.21.3'} 125 | cpu: [x64] 126 | os: [linux] 127 | 128 | '@biomejs/cli-win32-arm64@1.8.3': 129 | resolution: {integrity: sha512-J+Hu9WvrBevfy06eU1Na0lpc7uR9tibm9maHynLIoAjLZpQU3IW+OKHUtyL8p6/3pT2Ju5t5emReeIS2SAxhkQ==} 130 | engines: {node: '>=14.21.3'} 131 | cpu: [arm64] 132 | os: [win32] 133 | 134 | '@biomejs/cli-win32-x64@1.8.3': 135 | resolution: {integrity: sha512-/PJ59vA1pnQeKahemaQf4Nyj7IKUvGQSc3Ze1uIGi+Wvr1xF7rGobSrAAG01T/gUDG21vkDsZYM03NAmPiVkqg==} 136 | engines: {node: '>=14.21.3'} 137 | cpu: [x64] 138 | os: [win32] 139 | 140 | '@codemirror/language@6.10.1': 141 | resolution: {integrity: sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==} 142 | 143 | '@codemirror/search@6.5.4': 144 | resolution: {integrity: sha512-YoTrvjv9e8EbPs58opjZKyJ3ewFrVSUzQ/4WXlULQLSDDr1nGPJ67mMXFNNVYwdFhybzhrzrtqgHmtpJwIF+8g==} 145 | 146 | '@codemirror/state@6.3.1': 147 | resolution: {integrity: sha512-88e4HhMtKJyw6fKprGaN/yZfiaoGYOi2nM45YCUC6R/kex9sxFWBDGatS1vk4lMgnWmdIIB9tk8Gj1LmL8YfvA==} 148 | 149 | '@codemirror/state@6.4.1': 150 | resolution: {integrity: sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==} 151 | 152 | '@codemirror/view@6.22.0': 153 | resolution: {integrity: sha512-6zLj4YIoIpfTGKrDMTbeZRpa8ih4EymMCKmddEDcJWrCdp/N1D46B38YEz4creTb4T177AVS9EyXkLeC/HL2jA==} 154 | 155 | '@codemirror/view@6.26.3': 156 | resolution: {integrity: sha512-gmqxkPALZjkgSxIeeweY/wGQXBfwTUaLs8h7OKtSwfbj9Ct3L11lD+u1sS7XHppxFQoMDiMDp07P9f3I2jWOHw==} 157 | 158 | '@esbuild/android-arm64@0.17.3': 159 | resolution: {integrity: sha512-XvJsYo3dO3Pi4kpalkyMvfQsjxPWHYjoX4MDiB/FUM4YMfWcXa5l4VCwFWVYI1+92yxqjuqrhNg0CZg3gSouyQ==} 160 | engines: {node: '>=12'} 161 | cpu: [arm64] 162 | os: [android] 163 | 164 | '@esbuild/android-arm@0.17.3': 165 | resolution: {integrity: sha512-1Mlz934GvbgdDmt26rTLmf03cAgLg5HyOgJN+ZGCeP3Q9ynYTNMn2/LQxIl7Uy+o4K6Rfi2OuLsr12JQQR8gNg==} 166 | engines: {node: '>=12'} 167 | cpu: [arm] 168 | os: [android] 169 | 170 | '@esbuild/android-x64@0.17.3': 171 | resolution: {integrity: sha512-nuV2CmLS07Gqh5/GrZLuqkU9Bm6H6vcCspM+zjp9TdQlxJtIe+qqEXQChmfc7nWdyr/yz3h45Utk1tUn8Cz5+A==} 172 | engines: {node: '>=12'} 173 | cpu: [x64] 174 | os: [android] 175 | 176 | '@esbuild/darwin-arm64@0.17.3': 177 | resolution: {integrity: sha512-01Hxaaat6m0Xp9AXGM8mjFtqqwDjzlMP0eQq9zll9U85ttVALGCGDuEvra5Feu/NbP5AEP1MaopPwzsTcUq1cw==} 178 | engines: {node: '>=12'} 179 | cpu: [arm64] 180 | os: [darwin] 181 | 182 | '@esbuild/darwin-x64@0.17.3': 183 | resolution: {integrity: sha512-Eo2gq0Q/er2muf8Z83X21UFoB7EU6/m3GNKvrhACJkjVThd0uA+8RfKpfNhuMCl1bKRfBzKOk6xaYKQZ4lZqvA==} 184 | engines: {node: '>=12'} 185 | cpu: [x64] 186 | os: [darwin] 187 | 188 | '@esbuild/freebsd-arm64@0.17.3': 189 | resolution: {integrity: sha512-CN62ESxaquP61n1ZjQP/jZte8CE09M6kNn3baos2SeUfdVBkWN5n6vGp2iKyb/bm/x4JQzEvJgRHLGd5F5b81w==} 190 | engines: {node: '>=12'} 191 | cpu: [arm64] 192 | os: [freebsd] 193 | 194 | '@esbuild/freebsd-x64@0.17.3': 195 | resolution: {integrity: sha512-feq+K8TxIznZE+zhdVurF3WNJ/Sa35dQNYbaqM/wsCbWdzXr5lyq+AaTUSER2cUR+SXPnd/EY75EPRjf4s1SLg==} 196 | engines: {node: '>=12'} 197 | cpu: [x64] 198 | os: [freebsd] 199 | 200 | '@esbuild/linux-arm64@0.17.3': 201 | resolution: {integrity: sha512-JHeZXD4auLYBnrKn6JYJ0o5nWJI9PhChA/Nt0G4MvLaMrvXuWnY93R3a7PiXeJQphpL1nYsaMcoV2QtuvRnF/g==} 202 | engines: {node: '>=12'} 203 | cpu: [arm64] 204 | os: [linux] 205 | 206 | '@esbuild/linux-arm@0.17.3': 207 | resolution: {integrity: sha512-CLP3EgyNuPcg2cshbwkqYy5bbAgK+VhyfMU7oIYyn+x4Y67xb5C5ylxsNUjRmr8BX+MW3YhVNm6Lq6FKtRTWHQ==} 208 | engines: {node: '>=12'} 209 | cpu: [arm] 210 | os: [linux] 211 | 212 | '@esbuild/linux-ia32@0.17.3': 213 | resolution: {integrity: sha512-FyXlD2ZjZqTFh0sOQxFDiWG1uQUEOLbEh9gKN/7pFxck5Vw0qjWSDqbn6C10GAa1rXJpwsntHcmLqydY9ST9ZA==} 214 | engines: {node: '>=12'} 215 | cpu: [ia32] 216 | os: [linux] 217 | 218 | '@esbuild/linux-loong64@0.17.3': 219 | resolution: {integrity: sha512-OrDGMvDBI2g7s04J8dh8/I7eSO+/E7nMDT2Z5IruBfUO/RiigF1OF6xoH33Dn4W/OwAWSUf1s2nXamb28ZklTA==} 220 | engines: {node: '>=12'} 221 | cpu: [loong64] 222 | os: [linux] 223 | 224 | '@esbuild/linux-mips64el@0.17.3': 225 | resolution: {integrity: sha512-DcnUpXnVCJvmv0TzuLwKBC2nsQHle8EIiAJiJ+PipEVC16wHXaPEKP0EqN8WnBe0TPvMITOUlP2aiL5YMld+CQ==} 226 | engines: {node: '>=12'} 227 | cpu: [mips64el] 228 | os: [linux] 229 | 230 | '@esbuild/linux-ppc64@0.17.3': 231 | resolution: {integrity: sha512-BDYf/l1WVhWE+FHAW3FzZPtVlk9QsrwsxGzABmN4g8bTjmhazsId3h127pliDRRu5674k1Y2RWejbpN46N9ZhQ==} 232 | engines: {node: '>=12'} 233 | cpu: [ppc64] 234 | os: [linux] 235 | 236 | '@esbuild/linux-riscv64@0.17.3': 237 | resolution: {integrity: sha512-WViAxWYMRIi+prTJTyV1wnqd2mS2cPqJlN85oscVhXdb/ZTFJdrpaqm/uDsZPGKHtbg5TuRX/ymKdOSk41YZow==} 238 | engines: {node: '>=12'} 239 | cpu: [riscv64] 240 | os: [linux] 241 | 242 | '@esbuild/linux-s390x@0.17.3': 243 | resolution: {integrity: sha512-Iw8lkNHUC4oGP1O/KhumcVy77u2s6+KUjieUqzEU3XuWJqZ+AY7uVMrrCbAiwWTkpQHkr00BuXH5RpC6Sb/7Ug==} 244 | engines: {node: '>=12'} 245 | cpu: [s390x] 246 | os: [linux] 247 | 248 | '@esbuild/linux-x64@0.17.3': 249 | resolution: {integrity: sha512-0AGkWQMzeoeAtXQRNB3s4J1/T2XbigM2/Mn2yU1tQSmQRmHIZdkGbVq2A3aDdNslPyhb9/lH0S5GMTZ4xsjBqg==} 250 | engines: {node: '>=12'} 251 | cpu: [x64] 252 | os: [linux] 253 | 254 | '@esbuild/netbsd-x64@0.17.3': 255 | resolution: {integrity: sha512-4+rR/WHOxIVh53UIQIICryjdoKdHsFZFD4zLSonJ9RRw7bhKzVyXbnRPsWSfwybYqw9sB7ots/SYyufL1mBpEg==} 256 | engines: {node: '>=12'} 257 | cpu: [x64] 258 | os: [netbsd] 259 | 260 | '@esbuild/openbsd-x64@0.17.3': 261 | resolution: {integrity: sha512-cVpWnkx9IYg99EjGxa5Gc0XmqumtAwK3aoz7O4Dii2vko+qXbkHoujWA68cqXjhh6TsLaQelfDO4MVnyr+ODeA==} 262 | engines: {node: '>=12'} 263 | cpu: [x64] 264 | os: [openbsd] 265 | 266 | '@esbuild/sunos-x64@0.17.3': 267 | resolution: {integrity: sha512-RxmhKLbTCDAY2xOfrww6ieIZkZF+KBqG7S2Ako2SljKXRFi+0863PspK74QQ7JpmWwncChY25JTJSbVBYGQk2Q==} 268 | engines: {node: '>=12'} 269 | cpu: [x64] 270 | os: [sunos] 271 | 272 | '@esbuild/win32-arm64@0.17.3': 273 | resolution: {integrity: sha512-0r36VeEJ4efwmofxVJRXDjVRP2jTmv877zc+i+Pc7MNsIr38NfsjkQj23AfF7l0WbB+RQ7VUb+LDiqC/KY/M/A==} 274 | engines: {node: '>=12'} 275 | cpu: [arm64] 276 | os: [win32] 277 | 278 | '@esbuild/win32-ia32@0.17.3': 279 | resolution: {integrity: sha512-wgO6rc7uGStH22nur4aLFcq7Wh86bE9cOFmfTr/yxN3BXvDEdCSXyKkO+U5JIt53eTOgC47v9k/C1bITWL/Teg==} 280 | engines: {node: '>=12'} 281 | cpu: [ia32] 282 | os: [win32] 283 | 284 | '@esbuild/win32-x64@0.17.3': 285 | resolution: {integrity: sha512-FdVl64OIuiKjgXBjwZaJLKp0eaEckifbhn10dXWhysMJkWblg3OEEGKSIyhiD5RSgAya8WzP3DNkngtIg3Nt7g==} 286 | engines: {node: '>=12'} 287 | cpu: [x64] 288 | os: [win32] 289 | 290 | '@hutson/parse-repository-url@3.0.2': 291 | resolution: {integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==} 292 | engines: {node: '>=6.9.0'} 293 | 294 | '@lezer/common@1.2.1': 295 | resolution: {integrity: sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==} 296 | 297 | '@lezer/highlight@1.2.0': 298 | resolution: {integrity: sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==} 299 | 300 | '@lezer/lr@1.4.0': 301 | resolution: {integrity: sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==} 302 | 303 | '@types/codemirror@5.60.8': 304 | resolution: {integrity: sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==} 305 | 306 | '@types/dompurify@3.0.5': 307 | resolution: {integrity: sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==} 308 | 309 | '@types/estree@1.0.5': 310 | resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} 311 | 312 | '@types/lodash@4.14.201': 313 | resolution: {integrity: sha512-y9euML0cim1JrykNxADLfaG0FgD1g/yTHwUs/Jg9ZIU7WKj2/4IW9Lbb1WZbvck78W/lfGXFfe+u2EGfIJXdLQ==} 314 | 315 | '@types/minimist@1.2.5': 316 | resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} 317 | 318 | '@types/node@16.11.6': 319 | resolution: {integrity: sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==} 320 | 321 | '@types/normalize-package-data@2.4.4': 322 | resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} 323 | 324 | '@types/tern@0.23.7': 325 | resolution: {integrity: sha512-0YS9XCZ0LAhlP11HV9SqncUYyz9Ggsgc7Om/AmchKvoeFyj0qPaJmX6rJ93mJVExizWDzUMb49gAtVpI1uHd8Q==} 326 | 327 | '@types/trusted-types@2.0.7': 328 | resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} 329 | 330 | JSONStream@1.3.5: 331 | resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} 332 | hasBin: true 333 | 334 | add-stream@1.0.0: 335 | resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==} 336 | 337 | ansi-colors@4.1.3: 338 | resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} 339 | engines: {node: '>=6'} 340 | 341 | ansi-regex@5.0.1: 342 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 343 | engines: {node: '>=8'} 344 | 345 | ansi-styles@3.2.1: 346 | resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} 347 | engines: {node: '>=4'} 348 | 349 | ansi-styles@4.3.0: 350 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 351 | engines: {node: '>=8'} 352 | 353 | array-ify@1.0.0: 354 | resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} 355 | 356 | arrify@1.0.1: 357 | resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} 358 | engines: {node: '>=0.10.0'} 359 | 360 | balanced-match@1.0.2: 361 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 362 | 363 | brace-expansion@1.1.11: 364 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 365 | 366 | buffer-from@1.1.2: 367 | resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} 368 | 369 | builtin-modules@3.3.0: 370 | resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} 371 | engines: {node: '>=6'} 372 | 373 | camelcase-keys@6.2.2: 374 | resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} 375 | engines: {node: '>=8'} 376 | 377 | camelcase@5.3.1: 378 | resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} 379 | engines: {node: '>=6'} 380 | 381 | chalk@2.4.2: 382 | resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} 383 | engines: {node: '>=4'} 384 | 385 | cliui@7.0.4: 386 | resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} 387 | 388 | cliui@8.0.1: 389 | resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} 390 | engines: {node: '>=12'} 391 | 392 | color-convert@1.9.3: 393 | resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} 394 | 395 | color-convert@2.0.1: 396 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 397 | engines: {node: '>=7.0.0'} 398 | 399 | color-name@1.1.3: 400 | resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} 401 | 402 | color-name@1.1.4: 403 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 404 | 405 | commander@12.1.0: 406 | resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} 407 | engines: {node: '>=18'} 408 | 409 | commit-and-tag-version@12.0.0: 410 | resolution: {integrity: sha512-ynzs3Zojw3Z0hyBuA4prkDUvfODRUoqbsk7RFpFc28I12vXxhrAv+N5/9W4O0htdi9sxL6xvzxozXUapBeGXTQ==} 411 | engines: {node: '>=14'} 412 | hasBin: true 413 | 414 | compare-func@2.0.0: 415 | resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} 416 | 417 | concat-map@0.0.1: 418 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 419 | 420 | concat-stream@2.0.0: 421 | resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} 422 | engines: {'0': node >= 6.0} 423 | 424 | conventional-changelog-angular@5.0.13: 425 | resolution: {integrity: sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==} 426 | engines: {node: '>=10'} 427 | 428 | conventional-changelog-atom@2.0.8: 429 | resolution: {integrity: sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==} 430 | engines: {node: '>=10'} 431 | 432 | conventional-changelog-codemirror@2.0.8: 433 | resolution: {integrity: sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==} 434 | engines: {node: '>=10'} 435 | 436 | conventional-changelog-config-spec@2.1.0: 437 | resolution: {integrity: sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ==} 438 | 439 | conventional-changelog-conventionalcommits@4.6.3: 440 | resolution: {integrity: sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==} 441 | engines: {node: '>=10'} 442 | 443 | conventional-changelog-conventionalcommits@6.1.0: 444 | resolution: {integrity: sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw==} 445 | engines: {node: '>=14'} 446 | 447 | conventional-changelog-core@4.2.4: 448 | resolution: {integrity: sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg==} 449 | engines: {node: '>=10'} 450 | 451 | conventional-changelog-ember@2.0.9: 452 | resolution: {integrity: sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==} 453 | engines: {node: '>=10'} 454 | 455 | conventional-changelog-eslint@3.0.9: 456 | resolution: {integrity: sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==} 457 | engines: {node: '>=10'} 458 | 459 | conventional-changelog-express@2.0.6: 460 | resolution: {integrity: sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==} 461 | engines: {node: '>=10'} 462 | 463 | conventional-changelog-jquery@3.0.11: 464 | resolution: {integrity: sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==} 465 | engines: {node: '>=10'} 466 | 467 | conventional-changelog-jshint@2.0.9: 468 | resolution: {integrity: sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==} 469 | engines: {node: '>=10'} 470 | 471 | conventional-changelog-preset-loader@2.3.4: 472 | resolution: {integrity: sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==} 473 | engines: {node: '>=10'} 474 | 475 | conventional-changelog-preset-loader@3.0.0: 476 | resolution: {integrity: sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA==} 477 | engines: {node: '>=14'} 478 | 479 | conventional-changelog-writer@5.0.1: 480 | resolution: {integrity: sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==} 481 | engines: {node: '>=10'} 482 | hasBin: true 483 | 484 | conventional-changelog@3.1.25: 485 | resolution: {integrity: sha512-ryhi3fd1mKf3fSjbLXOfK2D06YwKNic1nC9mWqybBHdObPd8KJ2vjaXZfYj1U23t+V8T8n0d7gwnc9XbIdFbyQ==} 486 | engines: {node: '>=10'} 487 | 488 | conventional-commits-filter@2.0.7: 489 | resolution: {integrity: sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==} 490 | engines: {node: '>=10'} 491 | 492 | conventional-commits-filter@3.0.0: 493 | resolution: {integrity: sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q==} 494 | engines: {node: '>=14'} 495 | 496 | conventional-commits-parser@3.2.4: 497 | resolution: {integrity: sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==} 498 | engines: {node: '>=10'} 499 | hasBin: true 500 | 501 | conventional-commits-parser@4.0.0: 502 | resolution: {integrity: sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==} 503 | engines: {node: '>=14'} 504 | hasBin: true 505 | 506 | conventional-recommended-bump@7.0.1: 507 | resolution: {integrity: sha512-Ft79FF4SlOFvX4PkwFDRnaNiIVX7YbmqGU0RwccUaiGvgp3S0a8ipR2/Qxk31vclDNM+GSdJOVs2KrsUCjblVA==} 508 | engines: {node: '>=14'} 509 | hasBin: true 510 | 511 | core-util-is@1.0.3: 512 | resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} 513 | 514 | crelt@1.0.6: 515 | resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} 516 | 517 | dargs@7.0.0: 518 | resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} 519 | engines: {node: '>=8'} 520 | 521 | dateformat@3.0.3: 522 | resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} 523 | 524 | decamelize-keys@1.1.1: 525 | resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} 526 | engines: {node: '>=0.10.0'} 527 | 528 | decamelize@1.2.0: 529 | resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} 530 | engines: {node: '>=0.10.0'} 531 | 532 | dedent@1.5.3: 533 | resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} 534 | peerDependencies: 535 | babel-plugin-macros: ^3.1.0 536 | peerDependenciesMeta: 537 | babel-plugin-macros: 538 | optional: true 539 | 540 | detect-indent@6.1.0: 541 | resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} 542 | engines: {node: '>=8'} 543 | 544 | detect-newline@3.1.0: 545 | resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} 546 | engines: {node: '>=8'} 547 | 548 | dot-prop@5.3.0: 549 | resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} 550 | engines: {node: '>=8'} 551 | 552 | dotenv@16.4.5: 553 | resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} 554 | engines: {node: '>=12'} 555 | 556 | dotgitignore@2.1.0: 557 | resolution: {integrity: sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==} 558 | engines: {node: '>=6'} 559 | 560 | emoji-regex@8.0.0: 561 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 562 | 563 | error-ex@1.3.2: 564 | resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} 565 | 566 | esbuild@0.17.3: 567 | resolution: {integrity: sha512-9n3AsBRe6sIyOc6kmoXg2ypCLgf3eZSraWFRpnkto+svt8cZNuKTkb1bhQcitBcvIqjNiK7K0J3KPmwGSfkA8g==} 568 | engines: {node: '>=12'} 569 | hasBin: true 570 | 571 | escalade@3.1.1: 572 | resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} 573 | engines: {node: '>=6'} 574 | 575 | escape-string-regexp@1.0.5: 576 | resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} 577 | engines: {node: '>=0.8.0'} 578 | 579 | figures@3.2.0: 580 | resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} 581 | engines: {node: '>=8'} 582 | 583 | find-up@2.1.0: 584 | resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} 585 | engines: {node: '>=4'} 586 | 587 | find-up@3.0.0: 588 | resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} 589 | engines: {node: '>=6'} 590 | 591 | find-up@4.1.0: 592 | resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} 593 | engines: {node: '>=8'} 594 | 595 | find-up@5.0.0: 596 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 597 | engines: {node: '>=10'} 598 | 599 | function-bind@1.1.2: 600 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 601 | 602 | get-caller-file@2.0.5: 603 | resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} 604 | engines: {node: 6.* || 8.* || >= 10.*} 605 | 606 | get-pkg-repo@4.2.1: 607 | resolution: {integrity: sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==} 608 | engines: {node: '>=6.9.0'} 609 | hasBin: true 610 | 611 | git-raw-commits@2.0.11: 612 | resolution: {integrity: sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==} 613 | engines: {node: '>=10'} 614 | hasBin: true 615 | 616 | git-raw-commits@3.0.0: 617 | resolution: {integrity: sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw==} 618 | engines: {node: '>=14'} 619 | hasBin: true 620 | 621 | git-remote-origin-url@2.0.0: 622 | resolution: {integrity: sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==} 623 | engines: {node: '>=4'} 624 | 625 | git-semver-tags@4.1.1: 626 | resolution: {integrity: sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==} 627 | engines: {node: '>=10'} 628 | hasBin: true 629 | 630 | git-semver-tags@5.0.1: 631 | resolution: {integrity: sha512-hIvOeZwRbQ+7YEUmCkHqo8FOLQZCEn18yevLHADlFPZY02KJGsu5FZt9YW/lybfK2uhWFI7Qg/07LekJiTv7iA==} 632 | engines: {node: '>=14'} 633 | hasBin: true 634 | 635 | gitconfiglocal@1.0.0: 636 | resolution: {integrity: sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==} 637 | 638 | graceful-fs@4.2.11: 639 | resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 640 | 641 | handlebars@4.7.8: 642 | resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} 643 | engines: {node: '>=0.4.7'} 644 | hasBin: true 645 | 646 | hard-rejection@2.1.0: 647 | resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} 648 | engines: {node: '>=6'} 649 | 650 | has-flag@3.0.0: 651 | resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} 652 | engines: {node: '>=4'} 653 | 654 | hasown@2.0.0: 655 | resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} 656 | engines: {node: '>= 0.4'} 657 | 658 | hosted-git-info@2.8.9: 659 | resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} 660 | 661 | hosted-git-info@4.1.0: 662 | resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} 663 | engines: {node: '>=10'} 664 | 665 | indent-string@4.0.0: 666 | resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} 667 | engines: {node: '>=8'} 668 | 669 | inherits@2.0.4: 670 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 671 | 672 | ini@1.3.8: 673 | resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} 674 | 675 | is-arrayish@0.2.1: 676 | resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} 677 | 678 | is-core-module@2.13.1: 679 | resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} 680 | 681 | is-fullwidth-code-point@3.0.0: 682 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 683 | engines: {node: '>=8'} 684 | 685 | is-obj@2.0.0: 686 | resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} 687 | engines: {node: '>=8'} 688 | 689 | is-plain-obj@1.1.0: 690 | resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} 691 | engines: {node: '>=0.10.0'} 692 | 693 | is-text-path@1.0.1: 694 | resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} 695 | engines: {node: '>=0.10.0'} 696 | 697 | isarray@1.0.0: 698 | resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} 699 | 700 | js-tokens@4.0.0: 701 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 702 | 703 | json-parse-better-errors@1.0.2: 704 | resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} 705 | 706 | json-parse-even-better-errors@2.3.1: 707 | resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} 708 | 709 | json-stringify-safe@5.0.1: 710 | resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} 711 | 712 | jsonparse@1.3.1: 713 | resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} 714 | engines: {'0': node >= 0.2.0} 715 | 716 | kind-of@6.0.3: 717 | resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} 718 | engines: {node: '>=0.10.0'} 719 | 720 | lines-and-columns@1.2.4: 721 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} 722 | 723 | load-json-file@4.0.0: 724 | resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} 725 | engines: {node: '>=4'} 726 | 727 | locate-path@2.0.0: 728 | resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} 729 | engines: {node: '>=4'} 730 | 731 | locate-path@3.0.0: 732 | resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} 733 | engines: {node: '>=6'} 734 | 735 | locate-path@5.0.0: 736 | resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} 737 | engines: {node: '>=8'} 738 | 739 | locate-path@6.0.0: 740 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 741 | engines: {node: '>=10'} 742 | 743 | lodash.ismatch@4.4.0: 744 | resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==} 745 | 746 | lodash@4.17.21: 747 | resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} 748 | 749 | lru-cache@6.0.0: 750 | resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} 751 | engines: {node: '>=10'} 752 | 753 | map-obj@1.0.1: 754 | resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} 755 | engines: {node: '>=0.10.0'} 756 | 757 | map-obj@4.3.0: 758 | resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} 759 | engines: {node: '>=8'} 760 | 761 | meow@8.1.2: 762 | resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==} 763 | engines: {node: '>=10'} 764 | 765 | min-indent@1.0.1: 766 | resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} 767 | engines: {node: '>=4'} 768 | 769 | minimatch@3.1.2: 770 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 771 | 772 | minimist-options@4.1.0: 773 | resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} 774 | engines: {node: '>= 6'} 775 | 776 | minimist@1.2.8: 777 | resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} 778 | 779 | modify-values@1.0.1: 780 | resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==} 781 | engines: {node: '>=0.10.0'} 782 | 783 | moment@2.29.4: 784 | resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} 785 | 786 | neo-async@2.6.2: 787 | resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} 788 | 789 | normalize-package-data@2.5.0: 790 | resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} 791 | 792 | normalize-package-data@3.0.3: 793 | resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} 794 | engines: {node: '>=10'} 795 | 796 | obsidian@1.4.11: 797 | resolution: {integrity: sha512-BCVYTvaXxElJMl6MMbDdY/CGK+aq18SdtDY/7vH8v6BxCBQ6KF4kKxL0vG9UZ0o5qh139KpUoJHNm+6O5dllKA==} 798 | peerDependencies: 799 | '@codemirror/state': ^6.0.0 800 | '@codemirror/view': ^6.0.0 801 | 802 | p-limit@1.3.0: 803 | resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} 804 | engines: {node: '>=4'} 805 | 806 | p-limit@2.3.0: 807 | resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} 808 | engines: {node: '>=6'} 809 | 810 | p-limit@3.1.0: 811 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 812 | engines: {node: '>=10'} 813 | 814 | p-locate@2.0.0: 815 | resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} 816 | engines: {node: '>=4'} 817 | 818 | p-locate@3.0.0: 819 | resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} 820 | engines: {node: '>=6'} 821 | 822 | p-locate@4.1.0: 823 | resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} 824 | engines: {node: '>=8'} 825 | 826 | p-locate@5.0.0: 827 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 828 | engines: {node: '>=10'} 829 | 830 | p-try@1.0.0: 831 | resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} 832 | engines: {node: '>=4'} 833 | 834 | p-try@2.2.0: 835 | resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} 836 | engines: {node: '>=6'} 837 | 838 | parse-json@4.0.0: 839 | resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} 840 | engines: {node: '>=4'} 841 | 842 | parse-json@5.2.0: 843 | resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} 844 | engines: {node: '>=8'} 845 | 846 | path-exists@3.0.0: 847 | resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} 848 | engines: {node: '>=4'} 849 | 850 | path-exists@4.0.0: 851 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 852 | engines: {node: '>=8'} 853 | 854 | path-parse@1.0.7: 855 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 856 | 857 | path-type@3.0.0: 858 | resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} 859 | engines: {node: '>=4'} 860 | 861 | pify@2.3.0: 862 | resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} 863 | engines: {node: '>=0.10.0'} 864 | 865 | pify@3.0.0: 866 | resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} 867 | engines: {node: '>=4'} 868 | 869 | process-nextick-args@2.0.1: 870 | resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} 871 | 872 | q@1.5.1: 873 | resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} 874 | engines: {node: '>=0.6.0', teleport: '>=0.2.0'} 875 | 876 | quick-lru@4.0.1: 877 | resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} 878 | engines: {node: '>=8'} 879 | 880 | read-pkg-up@3.0.0: 881 | resolution: {integrity: sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==} 882 | engines: {node: '>=4'} 883 | 884 | read-pkg-up@7.0.1: 885 | resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} 886 | engines: {node: '>=8'} 887 | 888 | read-pkg@3.0.0: 889 | resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} 890 | engines: {node: '>=4'} 891 | 892 | read-pkg@5.2.0: 893 | resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} 894 | engines: {node: '>=8'} 895 | 896 | readable-stream@2.3.8: 897 | resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} 898 | 899 | readable-stream@3.6.2: 900 | resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} 901 | engines: {node: '>= 6'} 902 | 903 | redent@3.0.0: 904 | resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} 905 | engines: {node: '>=8'} 906 | 907 | require-directory@2.1.1: 908 | resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} 909 | engines: {node: '>=0.10.0'} 910 | 911 | resolve@1.22.8: 912 | resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} 913 | hasBin: true 914 | 915 | safe-buffer@5.1.2: 916 | resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} 917 | 918 | safe-buffer@5.2.1: 919 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 920 | 921 | semver@5.7.2: 922 | resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} 923 | hasBin: true 924 | 925 | semver@6.3.1: 926 | resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} 927 | hasBin: true 928 | 929 | semver@7.5.4: 930 | resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} 931 | engines: {node: '>=10'} 932 | hasBin: true 933 | 934 | source-map@0.6.1: 935 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 936 | engines: {node: '>=0.10.0'} 937 | 938 | spdx-correct@3.2.0: 939 | resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} 940 | 941 | spdx-exceptions@2.3.0: 942 | resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} 943 | 944 | spdx-expression-parse@3.0.1: 945 | resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} 946 | 947 | spdx-license-ids@3.0.16: 948 | resolution: {integrity: sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==} 949 | 950 | split2@3.2.2: 951 | resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} 952 | 953 | split@1.0.1: 954 | resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} 955 | 956 | string-width@4.2.3: 957 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 958 | engines: {node: '>=8'} 959 | 960 | string_decoder@1.1.1: 961 | resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} 962 | 963 | string_decoder@1.3.0: 964 | resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} 965 | 966 | strip-ansi@6.0.1: 967 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 968 | engines: {node: '>=8'} 969 | 970 | strip-bom@3.0.0: 971 | resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} 972 | engines: {node: '>=4'} 973 | 974 | strip-indent@3.0.0: 975 | resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} 976 | engines: {node: '>=8'} 977 | 978 | style-mod@4.1.0: 979 | resolution: {integrity: sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==} 980 | 981 | supports-color@5.5.0: 982 | resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} 983 | engines: {node: '>=4'} 984 | 985 | supports-preserve-symlinks-flag@1.0.0: 986 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 987 | engines: {node: '>= 0.4'} 988 | 989 | text-extensions@1.9.0: 990 | resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==} 991 | engines: {node: '>=0.10'} 992 | 993 | through2@2.0.5: 994 | resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} 995 | 996 | through2@4.0.2: 997 | resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} 998 | 999 | through@2.3.8: 1000 | resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} 1001 | 1002 | trim-newlines@3.0.1: 1003 | resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} 1004 | engines: {node: '>=8'} 1005 | 1006 | ts-dedent@2.2.0: 1007 | resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} 1008 | engines: {node: '>=6.10'} 1009 | 1010 | tslib@2.4.0: 1011 | resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} 1012 | 1013 | type-fest@0.18.1: 1014 | resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} 1015 | engines: {node: '>=10'} 1016 | 1017 | type-fest@0.6.0: 1018 | resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} 1019 | engines: {node: '>=8'} 1020 | 1021 | type-fest@0.8.1: 1022 | resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} 1023 | engines: {node: '>=8'} 1024 | 1025 | typedarray@0.0.6: 1026 | resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} 1027 | 1028 | typescript@5.5.2: 1029 | resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==} 1030 | engines: {node: '>=14.17'} 1031 | hasBin: true 1032 | 1033 | uglify-js@3.17.4: 1034 | resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} 1035 | engines: {node: '>=0.8.0'} 1036 | hasBin: true 1037 | 1038 | util-deprecate@1.0.2: 1039 | resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 1040 | 1041 | validate-npm-package-license@3.0.4: 1042 | resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} 1043 | 1044 | w3c-keyname@2.2.8: 1045 | resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} 1046 | 1047 | wordwrap@1.0.0: 1048 | resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} 1049 | 1050 | wrap-ansi@7.0.0: 1051 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 1052 | engines: {node: '>=10'} 1053 | 1054 | xtend@4.0.2: 1055 | resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} 1056 | engines: {node: '>=0.4'} 1057 | 1058 | y18n@5.0.8: 1059 | resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} 1060 | engines: {node: '>=10'} 1061 | 1062 | yallist@4.0.0: 1063 | resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} 1064 | 1065 | yargs-parser@20.2.9: 1066 | resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} 1067 | engines: {node: '>=10'} 1068 | 1069 | yargs-parser@21.1.1: 1070 | resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} 1071 | engines: {node: '>=12'} 1072 | 1073 | yargs@16.2.0: 1074 | resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} 1075 | engines: {node: '>=10'} 1076 | 1077 | yargs@17.7.2: 1078 | resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} 1079 | engines: {node: '>=12'} 1080 | 1081 | yocto-queue@0.1.0: 1082 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 1083 | engines: {node: '>=10'} 1084 | 1085 | snapshots: 1086 | 1087 | '@babel/code-frame@7.23.4': 1088 | dependencies: 1089 | '@babel/highlight': 7.23.4 1090 | chalk: 2.4.2 1091 | 1092 | '@babel/helper-validator-identifier@7.22.20': {} 1093 | 1094 | '@babel/highlight@7.23.4': 1095 | dependencies: 1096 | '@babel/helper-validator-identifier': 7.22.20 1097 | chalk: 2.4.2 1098 | js-tokens: 4.0.0 1099 | 1100 | '@biomejs/biome@1.8.3': 1101 | optionalDependencies: 1102 | '@biomejs/cli-darwin-arm64': 1.8.3 1103 | '@biomejs/cli-darwin-x64': 1.8.3 1104 | '@biomejs/cli-linux-arm64': 1.8.3 1105 | '@biomejs/cli-linux-arm64-musl': 1.8.3 1106 | '@biomejs/cli-linux-x64': 1.8.3 1107 | '@biomejs/cli-linux-x64-musl': 1.8.3 1108 | '@biomejs/cli-win32-arm64': 1.8.3 1109 | '@biomejs/cli-win32-x64': 1.8.3 1110 | 1111 | '@biomejs/cli-darwin-arm64@1.8.3': 1112 | optional: true 1113 | 1114 | '@biomejs/cli-darwin-x64@1.8.3': 1115 | optional: true 1116 | 1117 | '@biomejs/cli-linux-arm64-musl@1.8.3': 1118 | optional: true 1119 | 1120 | '@biomejs/cli-linux-arm64@1.8.3': 1121 | optional: true 1122 | 1123 | '@biomejs/cli-linux-x64-musl@1.8.3': 1124 | optional: true 1125 | 1126 | '@biomejs/cli-linux-x64@1.8.3': 1127 | optional: true 1128 | 1129 | '@biomejs/cli-win32-arm64@1.8.3': 1130 | optional: true 1131 | 1132 | '@biomejs/cli-win32-x64@1.8.3': 1133 | optional: true 1134 | 1135 | '@codemirror/language@6.10.1': 1136 | dependencies: 1137 | '@codemirror/state': 6.3.1 1138 | '@codemirror/view': 6.26.3 1139 | '@lezer/common': 1.2.1 1140 | '@lezer/highlight': 1.2.0 1141 | '@lezer/lr': 1.4.0 1142 | style-mod: 4.1.0 1143 | 1144 | '@codemirror/search@6.5.4': 1145 | dependencies: 1146 | '@codemirror/state': 6.3.1 1147 | '@codemirror/view': 6.22.0 1148 | crelt: 1.0.6 1149 | 1150 | '@codemirror/state@6.3.1': {} 1151 | 1152 | '@codemirror/state@6.4.1': {} 1153 | 1154 | '@codemirror/view@6.22.0': 1155 | dependencies: 1156 | '@codemirror/state': 6.3.1 1157 | style-mod: 4.1.0 1158 | w3c-keyname: 2.2.8 1159 | 1160 | '@codemirror/view@6.26.3': 1161 | dependencies: 1162 | '@codemirror/state': 6.4.1 1163 | style-mod: 4.1.0 1164 | w3c-keyname: 2.2.8 1165 | 1166 | '@esbuild/android-arm64@0.17.3': 1167 | optional: true 1168 | 1169 | '@esbuild/android-arm@0.17.3': 1170 | optional: true 1171 | 1172 | '@esbuild/android-x64@0.17.3': 1173 | optional: true 1174 | 1175 | '@esbuild/darwin-arm64@0.17.3': 1176 | optional: true 1177 | 1178 | '@esbuild/darwin-x64@0.17.3': 1179 | optional: true 1180 | 1181 | '@esbuild/freebsd-arm64@0.17.3': 1182 | optional: true 1183 | 1184 | '@esbuild/freebsd-x64@0.17.3': 1185 | optional: true 1186 | 1187 | '@esbuild/linux-arm64@0.17.3': 1188 | optional: true 1189 | 1190 | '@esbuild/linux-arm@0.17.3': 1191 | optional: true 1192 | 1193 | '@esbuild/linux-ia32@0.17.3': 1194 | optional: true 1195 | 1196 | '@esbuild/linux-loong64@0.17.3': 1197 | optional: true 1198 | 1199 | '@esbuild/linux-mips64el@0.17.3': 1200 | optional: true 1201 | 1202 | '@esbuild/linux-ppc64@0.17.3': 1203 | optional: true 1204 | 1205 | '@esbuild/linux-riscv64@0.17.3': 1206 | optional: true 1207 | 1208 | '@esbuild/linux-s390x@0.17.3': 1209 | optional: true 1210 | 1211 | '@esbuild/linux-x64@0.17.3': 1212 | optional: true 1213 | 1214 | '@esbuild/netbsd-x64@0.17.3': 1215 | optional: true 1216 | 1217 | '@esbuild/openbsd-x64@0.17.3': 1218 | optional: true 1219 | 1220 | '@esbuild/sunos-x64@0.17.3': 1221 | optional: true 1222 | 1223 | '@esbuild/win32-arm64@0.17.3': 1224 | optional: true 1225 | 1226 | '@esbuild/win32-ia32@0.17.3': 1227 | optional: true 1228 | 1229 | '@esbuild/win32-x64@0.17.3': 1230 | optional: true 1231 | 1232 | '@hutson/parse-repository-url@3.0.2': {} 1233 | 1234 | '@lezer/common@1.2.1': {} 1235 | 1236 | '@lezer/highlight@1.2.0': 1237 | dependencies: 1238 | '@lezer/common': 1.2.1 1239 | 1240 | '@lezer/lr@1.4.0': 1241 | dependencies: 1242 | '@lezer/common': 1.2.1 1243 | 1244 | '@types/codemirror@5.60.8': 1245 | dependencies: 1246 | '@types/tern': 0.23.7 1247 | 1248 | '@types/dompurify@3.0.5': 1249 | dependencies: 1250 | '@types/trusted-types': 2.0.7 1251 | 1252 | '@types/estree@1.0.5': {} 1253 | 1254 | '@types/lodash@4.14.201': {} 1255 | 1256 | '@types/minimist@1.2.5': {} 1257 | 1258 | '@types/node@16.11.6': {} 1259 | 1260 | '@types/normalize-package-data@2.4.4': {} 1261 | 1262 | '@types/tern@0.23.7': 1263 | dependencies: 1264 | '@types/estree': 1.0.5 1265 | 1266 | '@types/trusted-types@2.0.7': {} 1267 | 1268 | JSONStream@1.3.5: 1269 | dependencies: 1270 | jsonparse: 1.3.1 1271 | through: 2.3.8 1272 | 1273 | add-stream@1.0.0: {} 1274 | 1275 | ansi-colors@4.1.3: {} 1276 | 1277 | ansi-regex@5.0.1: {} 1278 | 1279 | ansi-styles@3.2.1: 1280 | dependencies: 1281 | color-convert: 1.9.3 1282 | 1283 | ansi-styles@4.3.0: 1284 | dependencies: 1285 | color-convert: 2.0.1 1286 | 1287 | array-ify@1.0.0: {} 1288 | 1289 | arrify@1.0.1: {} 1290 | 1291 | balanced-match@1.0.2: {} 1292 | 1293 | brace-expansion@1.1.11: 1294 | dependencies: 1295 | balanced-match: 1.0.2 1296 | concat-map: 0.0.1 1297 | 1298 | buffer-from@1.1.2: {} 1299 | 1300 | builtin-modules@3.3.0: {} 1301 | 1302 | camelcase-keys@6.2.2: 1303 | dependencies: 1304 | camelcase: 5.3.1 1305 | map-obj: 4.3.0 1306 | quick-lru: 4.0.1 1307 | 1308 | camelcase@5.3.1: {} 1309 | 1310 | chalk@2.4.2: 1311 | dependencies: 1312 | ansi-styles: 3.2.1 1313 | escape-string-regexp: 1.0.5 1314 | supports-color: 5.5.0 1315 | 1316 | cliui@7.0.4: 1317 | dependencies: 1318 | string-width: 4.2.3 1319 | strip-ansi: 6.0.1 1320 | wrap-ansi: 7.0.0 1321 | 1322 | cliui@8.0.1: 1323 | dependencies: 1324 | string-width: 4.2.3 1325 | strip-ansi: 6.0.1 1326 | wrap-ansi: 7.0.0 1327 | 1328 | color-convert@1.9.3: 1329 | dependencies: 1330 | color-name: 1.1.3 1331 | 1332 | color-convert@2.0.1: 1333 | dependencies: 1334 | color-name: 1.1.4 1335 | 1336 | color-name@1.1.3: {} 1337 | 1338 | color-name@1.1.4: {} 1339 | 1340 | commander@12.1.0: {} 1341 | 1342 | commit-and-tag-version@12.0.0: 1343 | dependencies: 1344 | chalk: 2.4.2 1345 | conventional-changelog: 3.1.25 1346 | conventional-changelog-config-spec: 2.1.0 1347 | conventional-changelog-conventionalcommits: 6.1.0 1348 | conventional-recommended-bump: 7.0.1 1349 | detect-indent: 6.1.0 1350 | detect-newline: 3.1.0 1351 | dotgitignore: 2.1.0 1352 | figures: 3.2.0 1353 | find-up: 5.0.0 1354 | git-semver-tags: 5.0.1 1355 | semver: 7.5.4 1356 | yargs: 17.7.2 1357 | 1358 | compare-func@2.0.0: 1359 | dependencies: 1360 | array-ify: 1.0.0 1361 | dot-prop: 5.3.0 1362 | 1363 | concat-map@0.0.1: {} 1364 | 1365 | concat-stream@2.0.0: 1366 | dependencies: 1367 | buffer-from: 1.1.2 1368 | inherits: 2.0.4 1369 | readable-stream: 3.6.2 1370 | typedarray: 0.0.6 1371 | 1372 | conventional-changelog-angular@5.0.13: 1373 | dependencies: 1374 | compare-func: 2.0.0 1375 | q: 1.5.1 1376 | 1377 | conventional-changelog-atom@2.0.8: 1378 | dependencies: 1379 | q: 1.5.1 1380 | 1381 | conventional-changelog-codemirror@2.0.8: 1382 | dependencies: 1383 | q: 1.5.1 1384 | 1385 | conventional-changelog-config-spec@2.1.0: {} 1386 | 1387 | conventional-changelog-conventionalcommits@4.6.3: 1388 | dependencies: 1389 | compare-func: 2.0.0 1390 | lodash: 4.17.21 1391 | q: 1.5.1 1392 | 1393 | conventional-changelog-conventionalcommits@6.1.0: 1394 | dependencies: 1395 | compare-func: 2.0.0 1396 | 1397 | conventional-changelog-core@4.2.4: 1398 | dependencies: 1399 | add-stream: 1.0.0 1400 | conventional-changelog-writer: 5.0.1 1401 | conventional-commits-parser: 3.2.4 1402 | dateformat: 3.0.3 1403 | get-pkg-repo: 4.2.1 1404 | git-raw-commits: 2.0.11 1405 | git-remote-origin-url: 2.0.0 1406 | git-semver-tags: 4.1.1 1407 | lodash: 4.17.21 1408 | normalize-package-data: 3.0.3 1409 | q: 1.5.1 1410 | read-pkg: 3.0.0 1411 | read-pkg-up: 3.0.0 1412 | through2: 4.0.2 1413 | 1414 | conventional-changelog-ember@2.0.9: 1415 | dependencies: 1416 | q: 1.5.1 1417 | 1418 | conventional-changelog-eslint@3.0.9: 1419 | dependencies: 1420 | q: 1.5.1 1421 | 1422 | conventional-changelog-express@2.0.6: 1423 | dependencies: 1424 | q: 1.5.1 1425 | 1426 | conventional-changelog-jquery@3.0.11: 1427 | dependencies: 1428 | q: 1.5.1 1429 | 1430 | conventional-changelog-jshint@2.0.9: 1431 | dependencies: 1432 | compare-func: 2.0.0 1433 | q: 1.5.1 1434 | 1435 | conventional-changelog-preset-loader@2.3.4: {} 1436 | 1437 | conventional-changelog-preset-loader@3.0.0: {} 1438 | 1439 | conventional-changelog-writer@5.0.1: 1440 | dependencies: 1441 | conventional-commits-filter: 2.0.7 1442 | dateformat: 3.0.3 1443 | handlebars: 4.7.8 1444 | json-stringify-safe: 5.0.1 1445 | lodash: 4.17.21 1446 | meow: 8.1.2 1447 | semver: 6.3.1 1448 | split: 1.0.1 1449 | through2: 4.0.2 1450 | 1451 | conventional-changelog@3.1.25: 1452 | dependencies: 1453 | conventional-changelog-angular: 5.0.13 1454 | conventional-changelog-atom: 2.0.8 1455 | conventional-changelog-codemirror: 2.0.8 1456 | conventional-changelog-conventionalcommits: 4.6.3 1457 | conventional-changelog-core: 4.2.4 1458 | conventional-changelog-ember: 2.0.9 1459 | conventional-changelog-eslint: 3.0.9 1460 | conventional-changelog-express: 2.0.6 1461 | conventional-changelog-jquery: 3.0.11 1462 | conventional-changelog-jshint: 2.0.9 1463 | conventional-changelog-preset-loader: 2.3.4 1464 | 1465 | conventional-commits-filter@2.0.7: 1466 | dependencies: 1467 | lodash.ismatch: 4.4.0 1468 | modify-values: 1.0.1 1469 | 1470 | conventional-commits-filter@3.0.0: 1471 | dependencies: 1472 | lodash.ismatch: 4.4.0 1473 | modify-values: 1.0.1 1474 | 1475 | conventional-commits-parser@3.2.4: 1476 | dependencies: 1477 | JSONStream: 1.3.5 1478 | is-text-path: 1.0.1 1479 | lodash: 4.17.21 1480 | meow: 8.1.2 1481 | split2: 3.2.2 1482 | through2: 4.0.2 1483 | 1484 | conventional-commits-parser@4.0.0: 1485 | dependencies: 1486 | JSONStream: 1.3.5 1487 | is-text-path: 1.0.1 1488 | meow: 8.1.2 1489 | split2: 3.2.2 1490 | 1491 | conventional-recommended-bump@7.0.1: 1492 | dependencies: 1493 | concat-stream: 2.0.0 1494 | conventional-changelog-preset-loader: 3.0.0 1495 | conventional-commits-filter: 3.0.0 1496 | conventional-commits-parser: 4.0.0 1497 | git-raw-commits: 3.0.0 1498 | git-semver-tags: 5.0.1 1499 | meow: 8.1.2 1500 | 1501 | core-util-is@1.0.3: {} 1502 | 1503 | crelt@1.0.6: {} 1504 | 1505 | dargs@7.0.0: {} 1506 | 1507 | dateformat@3.0.3: {} 1508 | 1509 | decamelize-keys@1.1.1: 1510 | dependencies: 1511 | decamelize: 1.2.0 1512 | map-obj: 1.0.1 1513 | 1514 | decamelize@1.2.0: {} 1515 | 1516 | dedent@1.5.3: {} 1517 | 1518 | detect-indent@6.1.0: {} 1519 | 1520 | detect-newline@3.1.0: {} 1521 | 1522 | dot-prop@5.3.0: 1523 | dependencies: 1524 | is-obj: 2.0.0 1525 | 1526 | dotenv@16.4.5: {} 1527 | 1528 | dotgitignore@2.1.0: 1529 | dependencies: 1530 | find-up: 3.0.0 1531 | minimatch: 3.1.2 1532 | 1533 | emoji-regex@8.0.0: {} 1534 | 1535 | error-ex@1.3.2: 1536 | dependencies: 1537 | is-arrayish: 0.2.1 1538 | 1539 | esbuild@0.17.3: 1540 | optionalDependencies: 1541 | '@esbuild/android-arm': 0.17.3 1542 | '@esbuild/android-arm64': 0.17.3 1543 | '@esbuild/android-x64': 0.17.3 1544 | '@esbuild/darwin-arm64': 0.17.3 1545 | '@esbuild/darwin-x64': 0.17.3 1546 | '@esbuild/freebsd-arm64': 0.17.3 1547 | '@esbuild/freebsd-x64': 0.17.3 1548 | '@esbuild/linux-arm': 0.17.3 1549 | '@esbuild/linux-arm64': 0.17.3 1550 | '@esbuild/linux-ia32': 0.17.3 1551 | '@esbuild/linux-loong64': 0.17.3 1552 | '@esbuild/linux-mips64el': 0.17.3 1553 | '@esbuild/linux-ppc64': 0.17.3 1554 | '@esbuild/linux-riscv64': 0.17.3 1555 | '@esbuild/linux-s390x': 0.17.3 1556 | '@esbuild/linux-x64': 0.17.3 1557 | '@esbuild/netbsd-x64': 0.17.3 1558 | '@esbuild/openbsd-x64': 0.17.3 1559 | '@esbuild/sunos-x64': 0.17.3 1560 | '@esbuild/win32-arm64': 0.17.3 1561 | '@esbuild/win32-ia32': 0.17.3 1562 | '@esbuild/win32-x64': 0.17.3 1563 | 1564 | escalade@3.1.1: {} 1565 | 1566 | escape-string-regexp@1.0.5: {} 1567 | 1568 | figures@3.2.0: 1569 | dependencies: 1570 | escape-string-regexp: 1.0.5 1571 | 1572 | find-up@2.1.0: 1573 | dependencies: 1574 | locate-path: 2.0.0 1575 | 1576 | find-up@3.0.0: 1577 | dependencies: 1578 | locate-path: 3.0.0 1579 | 1580 | find-up@4.1.0: 1581 | dependencies: 1582 | locate-path: 5.0.0 1583 | path-exists: 4.0.0 1584 | 1585 | find-up@5.0.0: 1586 | dependencies: 1587 | locate-path: 6.0.0 1588 | path-exists: 4.0.0 1589 | 1590 | function-bind@1.1.2: {} 1591 | 1592 | get-caller-file@2.0.5: {} 1593 | 1594 | get-pkg-repo@4.2.1: 1595 | dependencies: 1596 | '@hutson/parse-repository-url': 3.0.2 1597 | hosted-git-info: 4.1.0 1598 | through2: 2.0.5 1599 | yargs: 16.2.0 1600 | 1601 | git-raw-commits@2.0.11: 1602 | dependencies: 1603 | dargs: 7.0.0 1604 | lodash: 4.17.21 1605 | meow: 8.1.2 1606 | split2: 3.2.2 1607 | through2: 4.0.2 1608 | 1609 | git-raw-commits@3.0.0: 1610 | dependencies: 1611 | dargs: 7.0.0 1612 | meow: 8.1.2 1613 | split2: 3.2.2 1614 | 1615 | git-remote-origin-url@2.0.0: 1616 | dependencies: 1617 | gitconfiglocal: 1.0.0 1618 | pify: 2.3.0 1619 | 1620 | git-semver-tags@4.1.1: 1621 | dependencies: 1622 | meow: 8.1.2 1623 | semver: 6.3.1 1624 | 1625 | git-semver-tags@5.0.1: 1626 | dependencies: 1627 | meow: 8.1.2 1628 | semver: 7.5.4 1629 | 1630 | gitconfiglocal@1.0.0: 1631 | dependencies: 1632 | ini: 1.3.8 1633 | 1634 | graceful-fs@4.2.11: {} 1635 | 1636 | handlebars@4.7.8: 1637 | dependencies: 1638 | minimist: 1.2.8 1639 | neo-async: 2.6.2 1640 | source-map: 0.6.1 1641 | wordwrap: 1.0.0 1642 | optionalDependencies: 1643 | uglify-js: 3.17.4 1644 | 1645 | hard-rejection@2.1.0: {} 1646 | 1647 | has-flag@3.0.0: {} 1648 | 1649 | hasown@2.0.0: 1650 | dependencies: 1651 | function-bind: 1.1.2 1652 | 1653 | hosted-git-info@2.8.9: {} 1654 | 1655 | hosted-git-info@4.1.0: 1656 | dependencies: 1657 | lru-cache: 6.0.0 1658 | 1659 | indent-string@4.0.0: {} 1660 | 1661 | inherits@2.0.4: {} 1662 | 1663 | ini@1.3.8: {} 1664 | 1665 | is-arrayish@0.2.1: {} 1666 | 1667 | is-core-module@2.13.1: 1668 | dependencies: 1669 | hasown: 2.0.0 1670 | 1671 | is-fullwidth-code-point@3.0.0: {} 1672 | 1673 | is-obj@2.0.0: {} 1674 | 1675 | is-plain-obj@1.1.0: {} 1676 | 1677 | is-text-path@1.0.1: 1678 | dependencies: 1679 | text-extensions: 1.9.0 1680 | 1681 | isarray@1.0.0: {} 1682 | 1683 | js-tokens@4.0.0: {} 1684 | 1685 | json-parse-better-errors@1.0.2: {} 1686 | 1687 | json-parse-even-better-errors@2.3.1: {} 1688 | 1689 | json-stringify-safe@5.0.1: {} 1690 | 1691 | jsonparse@1.3.1: {} 1692 | 1693 | kind-of@6.0.3: {} 1694 | 1695 | lines-and-columns@1.2.4: {} 1696 | 1697 | load-json-file@4.0.0: 1698 | dependencies: 1699 | graceful-fs: 4.2.11 1700 | parse-json: 4.0.0 1701 | pify: 3.0.0 1702 | strip-bom: 3.0.0 1703 | 1704 | locate-path@2.0.0: 1705 | dependencies: 1706 | p-locate: 2.0.0 1707 | path-exists: 3.0.0 1708 | 1709 | locate-path@3.0.0: 1710 | dependencies: 1711 | p-locate: 3.0.0 1712 | path-exists: 3.0.0 1713 | 1714 | locate-path@5.0.0: 1715 | dependencies: 1716 | p-locate: 4.1.0 1717 | 1718 | locate-path@6.0.0: 1719 | dependencies: 1720 | p-locate: 5.0.0 1721 | 1722 | lodash.ismatch@4.4.0: {} 1723 | 1724 | lodash@4.17.21: {} 1725 | 1726 | lru-cache@6.0.0: 1727 | dependencies: 1728 | yallist: 4.0.0 1729 | 1730 | map-obj@1.0.1: {} 1731 | 1732 | map-obj@4.3.0: {} 1733 | 1734 | meow@8.1.2: 1735 | dependencies: 1736 | '@types/minimist': 1.2.5 1737 | camelcase-keys: 6.2.2 1738 | decamelize-keys: 1.1.1 1739 | hard-rejection: 2.1.0 1740 | minimist-options: 4.1.0 1741 | normalize-package-data: 3.0.3 1742 | read-pkg-up: 7.0.1 1743 | redent: 3.0.0 1744 | trim-newlines: 3.0.1 1745 | type-fest: 0.18.1 1746 | yargs-parser: 20.2.9 1747 | 1748 | min-indent@1.0.1: {} 1749 | 1750 | minimatch@3.1.2: 1751 | dependencies: 1752 | brace-expansion: 1.1.11 1753 | 1754 | minimist-options@4.1.0: 1755 | dependencies: 1756 | arrify: 1.0.1 1757 | is-plain-obj: 1.1.0 1758 | kind-of: 6.0.3 1759 | 1760 | minimist@1.2.8: {} 1761 | 1762 | modify-values@1.0.1: {} 1763 | 1764 | moment@2.29.4: {} 1765 | 1766 | neo-async@2.6.2: {} 1767 | 1768 | normalize-package-data@2.5.0: 1769 | dependencies: 1770 | hosted-git-info: 2.8.9 1771 | resolve: 1.22.8 1772 | semver: 5.7.2 1773 | validate-npm-package-license: 3.0.4 1774 | 1775 | normalize-package-data@3.0.3: 1776 | dependencies: 1777 | hosted-git-info: 4.1.0 1778 | is-core-module: 2.13.1 1779 | semver: 7.5.4 1780 | validate-npm-package-license: 3.0.4 1781 | 1782 | obsidian@1.4.11(@codemirror/state@6.3.1)(@codemirror/view@6.22.0): 1783 | dependencies: 1784 | '@codemirror/state': 6.3.1 1785 | '@codemirror/view': 6.22.0 1786 | '@types/codemirror': 5.60.8 1787 | moment: 2.29.4 1788 | 1789 | p-limit@1.3.0: 1790 | dependencies: 1791 | p-try: 1.0.0 1792 | 1793 | p-limit@2.3.0: 1794 | dependencies: 1795 | p-try: 2.2.0 1796 | 1797 | p-limit@3.1.0: 1798 | dependencies: 1799 | yocto-queue: 0.1.0 1800 | 1801 | p-locate@2.0.0: 1802 | dependencies: 1803 | p-limit: 1.3.0 1804 | 1805 | p-locate@3.0.0: 1806 | dependencies: 1807 | p-limit: 2.3.0 1808 | 1809 | p-locate@4.1.0: 1810 | dependencies: 1811 | p-limit: 2.3.0 1812 | 1813 | p-locate@5.0.0: 1814 | dependencies: 1815 | p-limit: 3.1.0 1816 | 1817 | p-try@1.0.0: {} 1818 | 1819 | p-try@2.2.0: {} 1820 | 1821 | parse-json@4.0.0: 1822 | dependencies: 1823 | error-ex: 1.3.2 1824 | json-parse-better-errors: 1.0.2 1825 | 1826 | parse-json@5.2.0: 1827 | dependencies: 1828 | '@babel/code-frame': 7.23.4 1829 | error-ex: 1.3.2 1830 | json-parse-even-better-errors: 2.3.1 1831 | lines-and-columns: 1.2.4 1832 | 1833 | path-exists@3.0.0: {} 1834 | 1835 | path-exists@4.0.0: {} 1836 | 1837 | path-parse@1.0.7: {} 1838 | 1839 | path-type@3.0.0: 1840 | dependencies: 1841 | pify: 3.0.0 1842 | 1843 | pify@2.3.0: {} 1844 | 1845 | pify@3.0.0: {} 1846 | 1847 | process-nextick-args@2.0.1: {} 1848 | 1849 | q@1.5.1: {} 1850 | 1851 | quick-lru@4.0.1: {} 1852 | 1853 | read-pkg-up@3.0.0: 1854 | dependencies: 1855 | find-up: 2.1.0 1856 | read-pkg: 3.0.0 1857 | 1858 | read-pkg-up@7.0.1: 1859 | dependencies: 1860 | find-up: 4.1.0 1861 | read-pkg: 5.2.0 1862 | type-fest: 0.8.1 1863 | 1864 | read-pkg@3.0.0: 1865 | dependencies: 1866 | load-json-file: 4.0.0 1867 | normalize-package-data: 2.5.0 1868 | path-type: 3.0.0 1869 | 1870 | read-pkg@5.2.0: 1871 | dependencies: 1872 | '@types/normalize-package-data': 2.4.4 1873 | normalize-package-data: 2.5.0 1874 | parse-json: 5.2.0 1875 | type-fest: 0.6.0 1876 | 1877 | readable-stream@2.3.8: 1878 | dependencies: 1879 | core-util-is: 1.0.3 1880 | inherits: 2.0.4 1881 | isarray: 1.0.0 1882 | process-nextick-args: 2.0.1 1883 | safe-buffer: 5.1.2 1884 | string_decoder: 1.1.1 1885 | util-deprecate: 1.0.2 1886 | 1887 | readable-stream@3.6.2: 1888 | dependencies: 1889 | inherits: 2.0.4 1890 | string_decoder: 1.3.0 1891 | util-deprecate: 1.0.2 1892 | 1893 | redent@3.0.0: 1894 | dependencies: 1895 | indent-string: 4.0.0 1896 | strip-indent: 3.0.0 1897 | 1898 | require-directory@2.1.1: {} 1899 | 1900 | resolve@1.22.8: 1901 | dependencies: 1902 | is-core-module: 2.13.1 1903 | path-parse: 1.0.7 1904 | supports-preserve-symlinks-flag: 1.0.0 1905 | 1906 | safe-buffer@5.1.2: {} 1907 | 1908 | safe-buffer@5.2.1: {} 1909 | 1910 | semver@5.7.2: {} 1911 | 1912 | semver@6.3.1: {} 1913 | 1914 | semver@7.5.4: 1915 | dependencies: 1916 | lru-cache: 6.0.0 1917 | 1918 | source-map@0.6.1: {} 1919 | 1920 | spdx-correct@3.2.0: 1921 | dependencies: 1922 | spdx-expression-parse: 3.0.1 1923 | spdx-license-ids: 3.0.16 1924 | 1925 | spdx-exceptions@2.3.0: {} 1926 | 1927 | spdx-expression-parse@3.0.1: 1928 | dependencies: 1929 | spdx-exceptions: 2.3.0 1930 | spdx-license-ids: 3.0.16 1931 | 1932 | spdx-license-ids@3.0.16: {} 1933 | 1934 | split2@3.2.2: 1935 | dependencies: 1936 | readable-stream: 3.6.2 1937 | 1938 | split@1.0.1: 1939 | dependencies: 1940 | through: 2.3.8 1941 | 1942 | string-width@4.2.3: 1943 | dependencies: 1944 | emoji-regex: 8.0.0 1945 | is-fullwidth-code-point: 3.0.0 1946 | strip-ansi: 6.0.1 1947 | 1948 | string_decoder@1.1.1: 1949 | dependencies: 1950 | safe-buffer: 5.1.2 1951 | 1952 | string_decoder@1.3.0: 1953 | dependencies: 1954 | safe-buffer: 5.2.1 1955 | 1956 | strip-ansi@6.0.1: 1957 | dependencies: 1958 | ansi-regex: 5.0.1 1959 | 1960 | strip-bom@3.0.0: {} 1961 | 1962 | strip-indent@3.0.0: 1963 | dependencies: 1964 | min-indent: 1.0.1 1965 | 1966 | style-mod@4.1.0: {} 1967 | 1968 | supports-color@5.5.0: 1969 | dependencies: 1970 | has-flag: 3.0.0 1971 | 1972 | supports-preserve-symlinks-flag@1.0.0: {} 1973 | 1974 | text-extensions@1.9.0: {} 1975 | 1976 | through2@2.0.5: 1977 | dependencies: 1978 | readable-stream: 2.3.8 1979 | xtend: 4.0.2 1980 | 1981 | through2@4.0.2: 1982 | dependencies: 1983 | readable-stream: 3.6.2 1984 | 1985 | through@2.3.8: {} 1986 | 1987 | trim-newlines@3.0.1: {} 1988 | 1989 | ts-dedent@2.2.0: {} 1990 | 1991 | tslib@2.4.0: {} 1992 | 1993 | type-fest@0.18.1: {} 1994 | 1995 | type-fest@0.6.0: {} 1996 | 1997 | type-fest@0.8.1: {} 1998 | 1999 | typedarray@0.0.6: {} 2000 | 2001 | typescript@5.5.2: {} 2002 | 2003 | uglify-js@3.17.4: 2004 | optional: true 2005 | 2006 | util-deprecate@1.0.2: {} 2007 | 2008 | validate-npm-package-license@3.0.4: 2009 | dependencies: 2010 | spdx-correct: 3.2.0 2011 | spdx-expression-parse: 3.0.1 2012 | 2013 | w3c-keyname@2.2.8: {} 2014 | 2015 | wordwrap@1.0.0: {} 2016 | 2017 | wrap-ansi@7.0.0: 2018 | dependencies: 2019 | ansi-styles: 4.3.0 2020 | string-width: 4.2.3 2021 | strip-ansi: 6.0.1 2022 | 2023 | xtend@4.0.2: {} 2024 | 2025 | y18n@5.0.8: {} 2026 | 2027 | yallist@4.0.0: {} 2028 | 2029 | yargs-parser@20.2.9: {} 2030 | 2031 | yargs-parser@21.1.1: {} 2032 | 2033 | yargs@16.2.0: 2034 | dependencies: 2035 | cliui: 7.0.4 2036 | escalade: 3.1.1 2037 | get-caller-file: 2.0.5 2038 | require-directory: 2.1.1 2039 | string-width: 4.2.3 2040 | y18n: 5.0.8 2041 | yargs-parser: 20.2.9 2042 | 2043 | yargs@17.7.2: 2044 | dependencies: 2045 | cliui: 8.0.1 2046 | escalade: 3.1.1 2047 | get-caller-file: 2.0.5 2048 | require-directory: 2.1.1 2049 | string-width: 4.2.3 2050 | y18n: 5.0.8 2051 | yargs-parser: 21.1.1 2052 | 2053 | yocto-queue@0.1.0: {} 2054 | -------------------------------------------------------------------------------- /src/cmPlugin.ts: -------------------------------------------------------------------------------- 1 | import { RegExpCursor } from "@codemirror/search"; 2 | import { type EditorSelection, type Extension, Facet, combineConfig } from "@codemirror/state"; 3 | import { 4 | Decoration, 5 | type DecorationSet, 6 | type EditorView, 7 | type PluginSpec, 8 | type PluginValue, 9 | ViewPlugin, 10 | type ViewUpdate, 11 | WidgetType, 12 | } from "@codemirror/view"; 13 | import { cloneDeep } from "lodash"; 14 | 15 | import { Notice, sanitizeHTMLToDom } from "obsidian"; 16 | import { DEFAULT_PATTERN, type Mark, type Pattern, type SettingOption, type SettingOptions } from "./interface"; 17 | import type RegexMark from "./main"; 18 | import { isValidRegex, matchGroups, removeTags } from "./utils"; 19 | 20 | const Config = Facet.define>({ 21 | combine(options) { 22 | return combineConfig(options, {}); 23 | }, 24 | }); 25 | 26 | export function cmExtension(plugin: RegexMark) { 27 | const extensions: Extension[] = [cmPlugin]; 28 | const options = plugin.settings; 29 | extensions.push(Config.of(cloneDeep(options))); 30 | return extensions; 31 | } 32 | 33 | class CMPlugin implements PluginValue { 34 | decorations: DecorationSet; 35 | 36 | constructor(view: EditorView) { 37 | this.decorations = this.buildDecorations(view); 38 | } 39 | 40 | update(update: ViewUpdate) { 41 | if (update) { 42 | this.decorations = this.buildDecorations(update.view); 43 | } 44 | } 45 | 46 | viewMode(view: EditorView) { 47 | const parent = view.dom.parentElement; 48 | if (parent?.classList.contains("is-live-preview")) return "Live"; 49 | else return "Source"; 50 | } 51 | 52 | buildDecorations(view: EditorView) { 53 | const decorations = []; 54 | const data: Mark = Object.values(view.state.facet(Config).mark); 55 | const pattern: Pattern = view.state.facet(Config).pattern ?? cloneDeep(DEFAULT_PATTERN); 56 | 57 | const mode = this.viewMode(view); 58 | for (const part of view.visibleRanges) { 59 | for (const d of data) { 60 | const displayMode = mode === "Live" ? d.viewMode?.live : d.viewMode?.source; 61 | if ( 62 | !d.regex || 63 | !d.class || 64 | d.regex === "" || 65 | d.class === "" || 66 | !isValidRegex(d.regex, true, pattern) || 67 | displayMode === false 68 | ) 69 | continue; 70 | try { 71 | const cursor = new RegExpCursor(view.state.doc, removeTags(d.regex, pattern), {}, part.from, part.to); 72 | while (!cursor.next().done) { 73 | const { from, to } = cursor.value; 74 | const insideBlock = disableInBlock(d, view, cursor, part, from, to); 75 | if (insideBlock) continue; 76 | 77 | //don't add the decoration if the cursor (selection in the editor) is inside the decoration 78 | if (checkSelectionOverlap(view.state.selection, from, to)) { 79 | //just apply the decoration to the whole line 80 | const markup = Decoration.mark({ class: d.class }); 81 | decorations.push(markup.range(from, to)); 82 | continue; 83 | } 84 | const string = view.state.sliceDoc(from, to).trim(); 85 | const markDeco = Decoration.replace({ 86 | widget: new LivePreviewWidget(string, d, view, pattern), 87 | }); 88 | decorations.push(markDeco.range(from, to)); 89 | } 90 | } catch (e) { 91 | console.error(e); 92 | new Notice(sanitizeHTMLToDom(`${d.regex}: ${e}`)); 93 | } 94 | } 95 | } 96 | return Decoration.set(decorations.sort((a, b) => a.from - b.from)); 97 | } 98 | } 99 | 100 | const pluginSpec: PluginSpec = { 101 | decorations: (value: CMPlugin) => value.decorations, 102 | }; 103 | 104 | const cmPlugin = ViewPlugin.fromClass(CMPlugin, pluginSpec); 105 | 106 | class LivePreviewWidget extends WidgetType { 107 | data: SettingOption; 108 | view: EditorView; 109 | pattern: Pattern; 110 | 111 | constructor( 112 | readonly value: string, 113 | data: SettingOption, 114 | view: EditorView, 115 | pattern?: Pattern 116 | ) { 117 | super(); 118 | this.data = data; 119 | this.view = view; 120 | this.pattern = pattern ?? cloneDeep(DEFAULT_PATTERN); 121 | } 122 | 123 | //Widget is only updated when the raw text is changed / the elements get focus and loses it 124 | 125 | eq(other: LivePreviewWidget) { 126 | //return false if the regex is edited 127 | const regex = new RegExp(removeTags(this.data.regex, this.pattern)); 128 | if (this.value.match(regex) === null) return false; 129 | 130 | return other.value == this.value; 131 | } 132 | 133 | constructTag(pattern: string) { 134 | const regex = new RegExp(pattern); 135 | return this.data.regex.match(regex)?.[1] ?? null; 136 | } 137 | 138 | toDOM() { 139 | let wrap = document.createElement("span"); 140 | wrap.addClass(this.data.class); 141 | const text = this.value; 142 | if (this.data.hide) { 143 | const newContent = wrap.createEl("span"); 144 | const res = this.subGroup(this.data.regex, text, newContent); 145 | if (res) wrap = res; 146 | } else wrap.innerText = text; 147 | 148 | return wrap; 149 | } 150 | 151 | ignoreEvent(_event: Event) { 152 | return false; 153 | } 154 | 155 | destroy(_dom: HTMLElement): void { 156 | //do nothing 157 | } 158 | 159 | /** 160 | * If they are (?) syntax in the regex, create a different html element for each group 161 | * for example: 162 | * (.*)(?.*)(?.*) => 163 | * 164 | * text 165 | * text 166 | * 167 | * @param regex 168 | * @param text 169 | * @param newContent 170 | */ 171 | subGroup(regex: string, text: string, newContent: HTMLSpanElement) { 172 | const openTag = this.constructTag(this.pattern.open); 173 | const closeTag = this.constructTag(this.pattern.close); 174 | if ( 175 | (openTag && !isValidRegex(openTag as string, true, this.pattern)) || 176 | (closeTag && !isValidRegex(closeTag as string, true, this.pattern)) 177 | ) { 178 | return newContent; 179 | } 180 | const openRegex = new RegExp(openTag as string, "g"); 181 | const closeRegex = new RegExp(closeTag as string, "g"); 182 | const matchSub = matchGroups(removeTags(regex, this.pattern), text); 183 | if (!matchSub) { 184 | newContent.createEl("span", { cls: "cm-hide" }).setText(text.match(openRegex)?.[1] || ""); 185 | newContent 186 | .createEl("span", { cls: this.data.class }) 187 | .setText(text.replace(openRegex, "").replace(closeRegex, "") || ""); 188 | newContent.createEl("span", { cls: "cm-hide" }).setText(text.match(closeRegex)?.[1] || ""); 189 | return newContent; 190 | } 191 | newContent.addClass(this.data.class); 192 | for (const [css, items] of Object.entries(matchSub)) { 193 | newContent.createEl("span", { cls: css }).setText(items.text); 194 | } 195 | return newContent; 196 | } 197 | } 198 | 199 | function checkSelectionOverlap(selection: EditorSelection | undefined, from: number, to: number): boolean { 200 | if (!selection) { 201 | return false; 202 | } 203 | 204 | for (const range of selection.ranges) { 205 | if (range.to >= from && range.from <= to) { 206 | return true; 207 | } //if text is not undefined, check if the selection is inside the text 208 | } 209 | 210 | return false; 211 | } 212 | 213 | function disableInBlock( 214 | data: SettingOption, 215 | view: EditorView, 216 | blockMatch: any, 217 | part: { from: number; to: number }, 218 | from: number, 219 | to: number 220 | ) { 221 | if (data.viewMode?.codeBlock || data.viewMode?.codeBlock === undefined) return false; 222 | const blockRegex = /(```[\s\S]*?```|`[^`]*`)/g; 223 | let insideBlock = false; 224 | blockRegex.lastIndex = 0; 225 | // biome-ignore lint/suspicious/noAssignInExpressions: 226 | while ((blockMatch = blockRegex.exec(view.state.doc.sliceString(part.from, part.to))) !== null) { 227 | const blockFrom = blockMatch.index + part.from; 228 | const blockTo = blockRegex.lastIndex + part.from; 229 | if (from >= blockFrom && to <= blockTo) { 230 | insideBlock = true; 231 | break; 232 | } 233 | } 234 | return insideBlock; 235 | } 236 | -------------------------------------------------------------------------------- /src/interface.ts: -------------------------------------------------------------------------------- 1 | export interface SettingOption { 2 | /** 3 | * Regex to match the text 4 | */ 5 | regex: string; 6 | /** 7 | * Regex flags 8 | * @default ['g', 'i'] 9 | */ 10 | flags?: RegexFlags[]; 11 | /** 12 | * The associated css class 13 | */ 14 | class: string; 15 | /** 16 | * If the regex have a group {{open}} and {{close}} and the open/close should be hidden 17 | */ 18 | hide?: boolean; 19 | /** 20 | * @deprecated 21 | * Now disable is handled by the view mode 22 | */ 23 | disable?: boolean; 24 | /** 25 | * Application view of the regex 26 | * Include the disable option 27 | */ 28 | viewMode?: ViewMode; 29 | } 30 | 31 | export type SettingOptions = { 32 | mark: Mark; 33 | pattern?: Pattern; 34 | }; 35 | 36 | export type Pattern = { 37 | open: string; 38 | close: string; 39 | }; 40 | 41 | export const DEFAULT_PATTERN: Pattern = { 42 | open: `{{open:(.*?)}}`, 43 | close: `{{close:(.*?)}}`, 44 | }; 45 | 46 | export const DEFAULT_SETTINGS: SettingOptions = { 47 | mark: [], 48 | pattern: DEFAULT_PATTERN, 49 | }; 50 | 51 | export type Mark = SettingOption[]; 52 | 53 | export type ViewMode = { 54 | reading: boolean; 55 | source: boolean; 56 | live: boolean; 57 | codeBlock?: boolean; 58 | }; 59 | 60 | export const DEFAULT_VIEW_MODE: ViewMode = { 61 | reading: true, 62 | source: true, 63 | live: true, 64 | }; 65 | 66 | export type RegexFlags = "g" | "i" | "m" | "s" | "u" | "y"; 67 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import type { Extension } from "@codemirror/state"; 2 | import { Plugin } from "obsidian"; 3 | 4 | import { cmExtension } from "./cmPlugin"; 5 | import { DEFAULT_SETTINGS, type SettingOptions } from "./interface"; 6 | import { MarkdownProcessor } from "./markdownProcessor"; 7 | import { RemarkRegexSettingTab } from "./settings"; 8 | 9 | export default class RegexMark extends Plugin { 10 | settings: SettingOptions; 11 | extensions: Extension[]; 12 | cmExtension: Extension; 13 | 14 | async onload() { 15 | console.log("loading plugin RegexMark"); 16 | await this.loadSettings(); 17 | const hasDisable = this.settings.mark.filter((data) => data.disable); 18 | for (const data of hasDisable) { 19 | if (data.disable) { 20 | console.warn(`Deprecated disable option found for ${data.class}, removing it and adjust the viewMode option.`); 21 | data.viewMode = { 22 | reading: false, 23 | source: false, 24 | live: false, 25 | }; 26 | delete data.disable; 27 | await this.saveSettings(); 28 | } 29 | } 30 | this.addSettingTab(new RemarkRegexSettingTab(this.app, this)); 31 | this.registerMarkdownPostProcessor((element: HTMLElement) => { 32 | MarkdownProcessor(this.settings.mark, element, this.app, this.settings.pattern); 33 | }); 34 | this.cmExtension = cmExtension(this); 35 | this.extensions = []; 36 | this.updateCmExtension(); 37 | this.registerEditorExtension(this.extensions); 38 | } 39 | 40 | onunload() { 41 | console.log("unloading plugin RegexMark"); 42 | } 43 | 44 | async loadSettings() { 45 | const oldSettings = await this.loadData(); 46 | if (Array.isArray(oldSettings)) { 47 | this.settings = { 48 | mark: oldSettings, 49 | pattern: DEFAULT_SETTINGS.pattern, 50 | }; 51 | await this.saveSettings(); 52 | } else { 53 | this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); 54 | } 55 | } 56 | 57 | async saveSettings() { 58 | await this.saveData(this.settings); 59 | } 60 | 61 | async overrideSettings(settings: SettingOptions) { 62 | this.settings = settings; 63 | await this.saveSettings(); 64 | this.updateCmExtension(); 65 | } 66 | 67 | updateCmExtension() { 68 | this.extensions.remove(this.cmExtension); 69 | this.cmExtension = cmExtension(this); 70 | this.extensions.push(this.cmExtension); 71 | this.app.workspace.updateOptions(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/markdownProcessor.ts: -------------------------------------------------------------------------------- 1 | import { type App, MarkdownView, sanitizeHTMLToDom } from "obsidian"; 2 | 3 | import type { Mark, Pattern } from "./interface"; 4 | import { isValidRegex, matchGroups, removeTags } from "./utils"; 5 | 6 | export function MarkdownProcessor(data: Mark, element: HTMLElement, app: App, pattern?: Pattern) { 7 | const paragraph = element.findAll("p, li, h1, h2, h3, h4, h5, h6, td, .callout-title-inner, th, code"); 8 | paragraph.push(...element.findAllSelf(".table-cell-wrapper")); 9 | const activeMode = app.workspace.getActiveViewOfType(MarkdownView)?.getMode() === "source"; 10 | for (const p of paragraph) { 11 | let ignore = true; 12 | for (const d of data) { 13 | if ( 14 | !d.regex || 15 | !d.class || 16 | d.regex === "" || 17 | d.class === "" || 18 | !isValidRegex(d.regex, true, pattern) || 19 | d.viewMode?.reading === false 20 | ) 21 | continue; 22 | const regex = new RegExp(removeTags(d.regex, pattern), d.flags?.join("") ?? "gi"); 23 | if (regex.test(p.textContent || "")) { 24 | ignore = false; 25 | break; 26 | } 27 | } 28 | if (ignore) continue; 29 | 30 | const treeWalker = document.createTreeWalker(p, NodeFilter.SHOW_TEXT); 31 | const textNodes = []; 32 | while (treeWalker.nextNode()) { 33 | textNodes.push(treeWalker.currentNode); 34 | } 35 | for (const node of textNodes) { 36 | let text = node.textContent; 37 | if (text) { 38 | for (const d of data) { 39 | if (!d.viewMode) d.viewMode = { reading: true, source: true, live: true, codeBlock: true }; 40 | if (node.parentNode?.nodeName === "CODE" && d.viewMode?.codeBlock === false) continue; 41 | const enabled = activeMode ? d.viewMode?.live : d.viewMode?.reading; 42 | if (!d.regex || !d.class || d.regex === "" || d.class === "" || enabled === false) continue; 43 | const regex = new RegExp(removeTags(d.regex, pattern), d.flags?.join("") ?? "gi"); 44 | if (d.hide) { 45 | const group = removeTags(d.regex, pattern) 46 | .match(/\((.*?)\)/) 47 | ?.filter((x) => x != null); 48 | const dataText = regex.exec(text); 49 | //remove undefined in dataText 50 | if (!group || !dataText || dataText.length < 2) continue; 51 | const subgroup = matchGroups(regex.source, text); 52 | if (!subgroup) text = text.replace(regex, `$1`); 53 | else { 54 | let html = ``; 55 | for (const [css, subtxt] of Object.entries(subgroup)) { 56 | html += text.replace(subtxt.input, `${subtxt.text}`); 57 | } 58 | html += ""; 59 | text = html; 60 | } 61 | } else { 62 | text = text.replace(regex, `$&`); 63 | } 64 | } 65 | const dom = sanitizeHTMLToDom(text); 66 | if (node.parentNode) node.parentNode.replaceChild(dom, node); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/settings/change_pattern.ts: -------------------------------------------------------------------------------- 1 | import { type App, Modal, Notice, Setting, sanitizeHTMLToDom } from "obsidian"; 2 | import { dedent } from "ts-dedent"; 3 | import { DEFAULT_PATTERN, type Pattern } from "../interface"; 4 | 5 | enum ErrorCode { 6 | NotOpen = "Pattern doesn't contain 'open:'", 7 | NotClose = "Pattern doesn't contain 'close:'", 8 | Empty = "Pattern is empty", 9 | Invalid = "Pattern is invalid", 10 | WithoutGroup = "Pattern doesn't contain a group", 11 | NeedChar = "Pattern need to contain a character for enclosing", 12 | } 13 | 14 | export class RemarkPatternTab extends Modal { 15 | result: Pattern; 16 | oldPattern: Pattern | undefined; 17 | onSubmit: (result: Pattern) => void; 18 | 19 | constructor(app: App, oldPattern: Pattern | undefined, onSubmit: (result: Pattern) => void) { 20 | super(app); 21 | this.onSubmit = onSubmit; 22 | this.oldPattern = oldPattern; 23 | } 24 | 25 | exampleRegex(pattern: string) { 26 | return pattern.replaceAll(/\\/g, ""); 27 | } 28 | 29 | onOpen(): void { 30 | const { contentEl } = this; 31 | this.contentEl.addClasses(["RegexMark", "pattern-change"]); 32 | this.result = this.oldPattern ?? DEFAULT_PATTERN; 33 | 34 | new Setting(contentEl).setHeading().setName("Change open/close tags"); 35 | 36 | const desc = dedent(` 37 |

Allow to change the {{open:}} and {{close:}} tags.

38 |
39 |
40 |
41 | 42 | 43 | 44 | 45 | 46 |
47 |
Warning
48 |
49 |
50 |

Any character used in the tag will disable the character in it.
51 | For example: [[open:]]]] can't work if open is set on [[open:]].

52 |
53 |
54 |

Your regex will be manually ported using the new settings. If an error is found during the conversion, the regex will be disabled in all view, and you will need to fix it manually.

55 |
56 |
57 |
58 | 59 | 60 | 61 | 62 |
63 |
Note
64 |
65 |
66 |

The open/close needs to be registered in the regex format, so don't forget to escape the characters!
67 | For example: [[open:(.*)]] needs to be saved as \\[\\[open:(.*)\\]\\]
Also, you can't use \\ for the tag (reserved for escape only)!

68 |
69 |
70 |

For the moment, based on your settings:

71 |
    72 |
  • Open: ${this.exampleRegex(this.oldPattern?.open ?? DEFAULT_PATTERN.open)}
  • 73 |
  • Close: ${this.exampleRegex(this.oldPattern?.close ?? DEFAULT_PATTERN.close)}
  • 74 |
75 | `); 76 | contentEl.appendChild(sanitizeHTMLToDom(desc)); 77 | 78 | new Setting(contentEl) 79 | .setName("Pattern") 80 | .setDesc("Define the pattern to be used") 81 | .addText((text) => { 82 | text.inputEl.addClass("pattern"); 83 | text.inputEl.setAttribute("data-type", "open"); 84 | text.inputEl.setAttribute("data-value", this.result.open); 85 | text.setValue(this.result.open).onChange((value) => { 86 | this.result.open = value; 87 | text.inputEl.setAttribute("data-value", value); 88 | }); 89 | }); 90 | 91 | new Setting(contentEl) 92 | .setName("Close pattern") 93 | .setDesc("Define the close pattern to be used") 94 | .addText((text) => { 95 | text.inputEl.addClass("pattern"); 96 | text.inputEl.setAttribute("data-type", "close"); 97 | text.inputEl.setAttribute("data-value", this.result.close); 98 | text.setValue(this.result.close).onChange((value) => { 99 | this.result.close = value; 100 | text.inputEl.setAttribute("data-value", value); 101 | }); 102 | }); 103 | 104 | new Setting(contentEl) 105 | .addButton((button) => { 106 | button 107 | .setButtonText("Save") 108 | .setCta() 109 | .onClick(() => { 110 | this.result.open = this.result.open.replace("(.*)", "(.*?)"); 111 | this.result.close = this.result.close.replace("(.*)", "(.*?)"); 112 | if (!this.verifyAllPattern()) return; 113 | this.onSubmit(this.result); 114 | this.close(); 115 | }); 116 | }) 117 | .addButton((button) => { 118 | button 119 | .setButtonText("Cancel") 120 | .setWarning() 121 | .onClick(() => { 122 | this.close(); 123 | }); 124 | }); 125 | } 126 | 127 | verifyRegexPattern(pattern: string, which: "open" | "close"): ErrorCode | true { 128 | //verify if the pattern is valid 129 | if (pattern.trim().length === 0) return ErrorCode.Empty; 130 | if (which === "open" && !pattern.includes("open:")) return ErrorCode.NotOpen; 131 | if (which === "close" && !pattern.includes("close:")) return ErrorCode.NotClose; 132 | if (pattern === `${which}:(.*?)`) return ErrorCode.NeedChar; 133 | if (!pattern.match(/\(\.\*\??\)/)) return ErrorCode.WithoutGroup; 134 | try { 135 | new RegExp(pattern); 136 | return true; 137 | } catch (_e) { 138 | return ErrorCode.Invalid; 139 | } 140 | } 141 | 142 | verifyAllPattern(): boolean { 143 | const errors: string[] = []; 144 | this.contentEl.querySelectorAll("input.pattern").forEach((el) => { 145 | const which = el.getAttribute("data-type") as "open" | "close"; 146 | const value = el.getAttribute("data-value") ?? ""; 147 | const result = this.verifyRegexPattern(value, which); 148 | if (result !== true) { 149 | el.addClass("error"); 150 | errors.push(`${value}: ${result}`); 151 | } else { 152 | el.removeClass("error"); 153 | } 154 | }); 155 | if (errors.length > 0) { 156 | const html = errors.map((d) => `
  • ${d}
  • `).join(""); 157 | new Notice(sanitizeHTMLToDom(`Errors found:
      ${html}
    `)); 158 | return false; 159 | } 160 | return true; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/settings/import_export.ts: -------------------------------------------------------------------------------- 1 | import { cloneDeep } from "lodash"; 2 | import { ButtonComponent, Modal, Platform, Setting, TextAreaComponent } from "obsidian"; 3 | import type { RemarkRegexSettingTab } from "."; 4 | import type { Mark, SettingOption, SettingOptions } from "../interface"; 5 | import type RegexMark from "../main"; 6 | 7 | export class ImportSettings extends Modal { 8 | plugin: RegexMark; 9 | settings: SettingOptions; 10 | settingTab: RemarkRegexSettingTab; 11 | 12 | constructor(plugin: RegexMark, settings: SettingOptions, tab: RemarkRegexSettingTab) { 13 | super(plugin.app); 14 | this.plugin = plugin; 15 | this.settings = settings; 16 | this.settingTab = tab; 17 | } 18 | 19 | onOpen() { 20 | const { contentEl } = this; 21 | this.contentEl.addClass("RegexMark"); 22 | 23 | new Setting(contentEl).setName("Import settings").setDesc("Allow to import regex from other users.").setHeading(); 24 | 25 | new Setting(contentEl).then((setting) => { 26 | // biome-ignore lint/correctness/noUndeclaredVariables: createSpan is a function builded with the plugin 27 | const errorSpan = createSpan({ 28 | cls: "import-error", 29 | text: "Error during the importation: ", 30 | }); 31 | setting.nameEl.appendChild(errorSpan); 32 | const importAndClose = async (str: string) => { 33 | const oldSettings = cloneDeep(this.settings); 34 | if (str) { 35 | try { 36 | const importSettings = JSON.parse(str) as unknown; 37 | if (importSettings) { 38 | if (Object.hasOwn(importSettings, "pattern")) { 39 | oldSettings.pattern = (importSettings as SettingOptions).pattern; 40 | delete (importSettings as SettingOptions).pattern; 41 | } 42 | const marks: Mark = []; 43 | //import the list of regex only 44 | if (Object.hasOwn(importSettings, "mark") || importSettings instanceof Array) { 45 | if (Object.hasOwn(importSettings, "mark")) { 46 | marks.push(...(importSettings as SettingOptions).mark); 47 | } else if (importSettings instanceof Array) { 48 | marks.push(...(importSettings as Mark)); 49 | } 50 | for (const setting of marks) { 51 | if (!setting.regex || !setting.class) { 52 | throw new Error("Invalid importation"); 53 | } 54 | } 55 | //import only if not in the old settings 56 | const imported = marks.filter((setting: SettingOption) => { 57 | return !oldSettings.mark.find((oldSetting: SettingOption) => oldSetting.regex === setting.regex); 58 | }); 59 | oldSettings.mark.push(...imported); 60 | this.settings = oldSettings; 61 | } else if (importSettings instanceof Object && !Object.hasOwn(importSettings, "mark")) { 62 | if (!Object.hasOwn(importSettings, "regex") || !Object.hasOwn(importSettings, "class")) { 63 | throw new Error("Invalid importation"); 64 | } 65 | const imported = importSettings as SettingOption; 66 | if (!oldSettings.mark.find((oldSetting: SettingOption) => oldSetting.regex === imported.regex)) { 67 | oldSettings.mark.push(importSettings as SettingOption); 68 | this.settings = oldSettings; 69 | } else { 70 | throw new Error("Already in the settings"); 71 | } 72 | } 73 | } 74 | await this.plugin.overrideSettings(oldSettings); 75 | this.close(); 76 | this.settingTab.display(); 77 | } catch (e) { 78 | errorSpan.addClass("active"); 79 | errorSpan.setText(`Error during importation: ${e}`); 80 | } 81 | } else { 82 | errorSpan.addClass("active"); 83 | errorSpan.setText("No importation detected"); 84 | } 85 | }; 86 | setting.controlEl.createEl( 87 | "input", 88 | { 89 | cls: "import-input", 90 | attr: { 91 | id: "import-input", 92 | name: "import-input", 93 | type: "file", 94 | accept: ".json", 95 | }, 96 | }, 97 | (importInput) => { 98 | importInput.addEventListener("change", async (e) => { 99 | const reader = new FileReader(); 100 | reader.onload = async (e: ProgressEvent) => { 101 | await importAndClose(e!.target!.result?.toString().trim() ?? ""); 102 | }; 103 | reader.readAsText((e.target as HTMLInputElement).files![0]); 104 | }); 105 | } 106 | ); 107 | setting.controlEl.createEl("label", { 108 | cls: "import-label", 109 | text: "Import from a file", 110 | attr: { 111 | for: "import-input", 112 | }, 113 | }); 114 | 115 | const textArea = new TextAreaComponent(contentEl).setPlaceholder("Paste your settings here").then((textArea) => { 116 | const saveButton = new ButtonComponent(contentEl).setButtonText("Save").onClick(async () => { 117 | await importAndClose(textArea.getValue()); 118 | }); 119 | saveButton.buttonEl.addClass("import-save"); 120 | }); 121 | textArea.inputEl.addClass("import-textarea"); 122 | }); 123 | } 124 | 125 | onClose(): void { 126 | const { contentEl } = this; 127 | contentEl.empty(); 128 | } 129 | } 130 | 131 | export class ExportSettings extends Modal { 132 | plugin: RegexMark; 133 | settings: SettingOptions; 134 | settingTab: RemarkRegexSettingTab; 135 | 136 | constructor(plugin: RegexMark, settings: SettingOptions, tab: RemarkRegexSettingTab) { 137 | super(plugin.app); 138 | this.plugin = plugin; 139 | this.settings = settings; 140 | this.settingTab = tab; 141 | } 142 | 143 | onOpen() { 144 | const { contentEl } = this; 145 | this.contentEl.addClass("RegexMark"); 146 | 147 | new Setting(contentEl) 148 | .setName("Export settings") 149 | .setDesc("Allow to export regex to share it with other users.") 150 | .then((setting) => { 151 | const copied = cloneDeep(this.settings); 152 | const output = JSON.stringify(copied, null, 2); 153 | setting.controlEl.createEl( 154 | "a", 155 | { 156 | cls: "copy", 157 | text: "Copy to clipboard", 158 | href: "#", 159 | }, 160 | (copyButton) => { 161 | const textArea = new TextAreaComponent(contentEl).setValue(output).then((textArea) => { 162 | copyButton.addEventListener("click", (e) => { 163 | e.preventDefault(); 164 | textArea.inputEl.select(); 165 | textArea.inputEl.setSelectionRange(0, 99999); 166 | //use clipboard API 167 | navigator.clipboard.writeText(textArea.inputEl.value); 168 | copyButton.addClass("success"); 169 | setTimeout(() => { 170 | if (copyButton.parentNode) copyButton.removeClass("success"); 171 | }, 2000); 172 | }); 173 | }); 174 | textArea.inputEl.addClass("export-textarea"); 175 | } 176 | ); 177 | if (Platform.isDesktop) { 178 | setting.controlEl.createEl("a", { 179 | cls: "download", 180 | text: "Download", 181 | attr: { 182 | download: "regexmark.json", 183 | href: `data:text/json;charset=utf-8,${encodeURIComponent(output)}`, 184 | }, 185 | }); 186 | } else if (Platform.isMobile) { 187 | setting.addButton((button) => { 188 | button 189 | .setClass("download") 190 | .setButtonText("Download") 191 | .onClick(() => { 192 | const blob = new Blob([output], { type: "application/json" }); 193 | const url = URL.createObjectURL(blob); 194 | const a = document.createElement("a"); 195 | a.href = url; 196 | a.download = "regexmark.json"; 197 | a.click(); 198 | URL.revokeObjectURL(url); 199 | }); 200 | }); 201 | } 202 | }); 203 | } 204 | 205 | onClose(): void { 206 | const { contentEl } = this; 207 | contentEl.empty(); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/settings/index.ts: -------------------------------------------------------------------------------- 1 | import { cloneDeep } from "lodash"; 2 | import { 3 | type App, 4 | Notice, 5 | PluginSettingTab, 6 | Setting, 7 | type ToggleComponent, 8 | sanitizeHTMLToDom, 9 | MarkdownRenderer, 10 | } from "obsidian"; 11 | import { dedent } from "ts-dedent"; 12 | import { 13 | DEFAULT_PATTERN, 14 | DEFAULT_VIEW_MODE, 15 | type Pattern, 16 | type RegexFlags, 17 | type SettingOption, 18 | type SettingOptions, 19 | type ViewMode, 20 | } from "../interface"; 21 | import type RegexMark from "../main"; 22 | import { hasToHide, isInvalid, isValidRegex, removeTags } from "../utils"; 23 | import { RemarkPatternTab } from "./change_pattern"; 24 | import { ExportSettings, ImportSettings } from "./import_export"; 25 | import { RemarkRegexOptions } from "./viewModal"; 26 | 27 | export class RemarkRegexSettingTab extends PluginSettingTab { 28 | plugin: RegexMark; 29 | settings: SettingOptions; 30 | toggles: Map = new Map(); 31 | 32 | constructor(app: App, plugin: RegexMark) { 33 | super(app, plugin); 34 | this.plugin = plugin; 35 | this.settings = plugin.settings; 36 | } 37 | 38 | /** 39 | * Disable the toggle option if the regex doesn't contain any group 40 | * @param data - The setting option containing regex information 41 | */ 42 | disableToggle(data: SettingOption) { 43 | const isRegexInvalid = this.verifyRegexFromInput(data); 44 | const toggleComponent = this.toggles.get(data); 45 | 46 | if (toggleComponent) { 47 | toggleComponent.toggleEl.toggleClass("is-disabled-manually", isRegexInvalid); 48 | toggleComponent.setDisabled(isRegexInvalid); 49 | 50 | const tooltip = isRegexInvalid 51 | ? "Can't hide the regex if no group is found in it." 52 | : "Hide the regex in Live-Preview, only keeping the content."; 53 | 54 | toggleComponent.setTooltip(tooltip); 55 | } 56 | } 57 | 58 | /** 59 | * Create a deep copy of the view mode 60 | */ 61 | cloneViewMode(mode: SettingOption): ViewMode { 62 | return cloneDeep(mode.viewMode ?? DEFAULT_VIEW_MODE); 63 | } 64 | 65 | /** 66 | * Create a deep copy of the pattern configuration 67 | */ 68 | clonePattern(pattern: SettingOptions): Pattern { 69 | return cloneDeep(pattern.pattern ?? { open: "{{open:}}", close: "{{close:}}" }); 70 | } 71 | 72 | /** 73 | * Updates all regex patterns when the open/close tags are changed 74 | */ 75 | updateRegex(newPattern: Pattern) { 76 | const oldPattern = this.settings.pattern ?? DEFAULT_PATTERN; 77 | const notValid = []; 78 | 79 | // Create a simplified pattern without escaping characters 80 | const simplifiedPattern: Pattern = { 81 | open: newPattern.open.replace("(.*?)", "$1").replaceAll(/\\/g, ""), 82 | close: newPattern.close.replace("(.*?)", "$1").replaceAll(/\\/g, ""), 83 | }; 84 | 85 | // Update each regex with the new pattern 86 | for (const data of this.settings.mark) { 87 | const updatedRegex = data.regex 88 | .replace(new RegExp(oldPattern.open), simplifiedPattern.open) 89 | .replace(new RegExp(oldPattern.close), simplifiedPattern.close); 90 | 91 | // Apply changes to the data object 92 | Object.assign(data, { regex: updatedRegex }); 93 | 94 | // Verify if the new regex is valid 95 | const isValid = this.verifyRegex(data, newPattern); 96 | if (!isValid) { 97 | data.viewMode = { 98 | reading: false, 99 | source: false, 100 | live: false, 101 | }; 102 | notValid.push(data.regex); 103 | } 104 | } 105 | 106 | // Show notification for invalid regexes 107 | if (notValid.length > 0) { 108 | const htmlList = notValid.map((d) => `
  • ${d}
  • `).join(""); 109 | new Notice( 110 | sanitizeHTMLToDom( 111 | `The following regexes are invalid:
      ${htmlList}
    ` 112 | ), 113 | 0 114 | ); 115 | } 116 | } 117 | 118 | /** 119 | * Creates a user-friendly representation of pattern 120 | */ 121 | stringifyPattern(pattern: Pattern) { 122 | return { 123 | open: pattern.open.replace("(.*?)", "regex").replaceAll(/\\/g, ""), 124 | close: pattern.close.replace("(.*?)", "regex").replaceAll(/\\/g, ""), 125 | }; 126 | } 127 | 128 | /** 129 | * Renders the settings interface 130 | */ 131 | async display(): Promise { 132 | const { containerEl } = this; 133 | this.toggles.clear(); 134 | containerEl.addClass("RegexMark"); 135 | containerEl.empty(); 136 | 137 | // Render header with import/export and pattern change buttons 138 | this.renderHeaderButtons(containerEl); 139 | 140 | // Display documentation markdown 141 | await this.renderDocumentation(containerEl); 142 | 143 | // Render each regex setting 144 | for (const data of this.plugin.settings.mark) { 145 | this.renderRegexSetting(containerEl, data); 146 | } 147 | 148 | // Render add and verify buttons 149 | this.renderActionButtons(containerEl); 150 | } 151 | 152 | /** 153 | * Creates the header buttons for import/export and pattern changes 154 | */ 155 | private renderHeaderButtons(containerEl: HTMLElement) { 156 | new Setting(containerEl) 157 | .setClass("import-export") 158 | .addButton((button) => { 159 | button.setButtonText("Import").onClick(() => { 160 | new ImportSettings(this.plugin, this.plugin.settings, this).open(); 161 | }); 162 | }) 163 | .addButton((button) => { 164 | button.setButtonText("Export").onClick(() => { 165 | new ExportSettings(this.plugin, this.plugin.settings, this).open(); 166 | }); 167 | }) 168 | .addButton((button) => { 169 | button 170 | .setButtonText("Change open/close tags") 171 | .setTooltip("Advanced user only! Allow to change the tags for hiding element") 172 | .onClick(async () => { 173 | new RemarkPatternTab(this.app, this.clonePattern(this.settings), async (result) => { 174 | this.updateRegex(result); 175 | this.plugin.settings.pattern = result; 176 | await this.plugin.saveSettings(); 177 | await this.display(); 178 | }).open(); 179 | }); 180 | }); 181 | } 182 | 183 | /** 184 | * Renders the documentation markdown for the plugin 185 | */ 186 | private async renderDocumentation(containerEl: HTMLElement) { 187 | const pattern = this.stringifyPattern(this.plugin.settings.pattern ?? DEFAULT_PATTERN); 188 | 189 | await MarkdownRenderer.render( 190 | this.app, 191 | dedent(`Regex Mark allows to add custom CSS class to text that matches a regex. 192 | 193 | If you are not familiar with regex, you can use this tool to help you building regex: [https://regex101.com/](https://regex101.com/). Don't forget to set to **ECMAScript** in the left panel. 194 | 195 | You can create custom Markdown Markup with using \`${pattern.open}\` and \`${pattern.close}\`. The open and close regex will be hidden in Live-Preview. You need to use the \`hide\` toggle to make it works. 196 | 197 | To activate the toggle, you need to use a **regex group**. See [here for more information](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions/Groups_and_backreferences). 198 | 199 | > [!tip] Named group 200 | > Named group allows to "granular" match regex. The name will be used as a CSS classes. 201 | > **Note** : It's pretty experimental so don't expect much! 202 | > Also, you need to use a named group for **each group** in the regex. 203 | 204 | > [!note] 205 | > - "Overwriting" markdown (for example in \`__underline__\`) won't work in reading mode. But, you can escape the pattern with a backslash: \`\\\` before the mark to disable it! Don't forget it in the regex too. 206 | > - Using a group in the opening and closing pattern is not supported. 207 | `), 208 | containerEl, 209 | "", 210 | this.plugin 211 | ); 212 | } 213 | 214 | /** 215 | * Renders a single regex setting item 216 | */ 217 | private renderRegexSetting(containerEl: HTMLElement, data: SettingOption) { 218 | new Setting(containerEl) 219 | .setClass("regex-setting") 220 | .addExtraButton((button) => { 221 | button 222 | .setIcon("eye") 223 | .setTooltip("Edit the view mode") 224 | .onClick(async () => { 225 | new RemarkRegexOptions(this.app, this.cloneViewMode(data), async (result) => { 226 | data.viewMode = result; 227 | await this.plugin.saveSettings(); 228 | this.plugin.updateCmExtension(); 229 | }).open(); 230 | }); 231 | }) 232 | .addText((text) => this.createRegexInput(text, data)) 233 | .addText((text) => this.createFlagsInput(text, data)) 234 | .addText((text) => this.createClassInput(text, data)) 235 | .addToggle((toggle) => this.createHideToggle(toggle, data)) 236 | .addExtraButton((button) => this.createDeleteButton(button, data)) 237 | .addExtraButton((button) => this.createMoveUpButton(button, data)) 238 | .addExtraButton((button) => this.createMoveDownButton(button, data)); 239 | 240 | this.disableToggle(data); 241 | } 242 | 243 | /** 244 | * Creates the regex text input field 245 | */ 246 | private createRegexInput(text: any, data: SettingOption) { 247 | text.inputEl.setAttribute("regex-value", data.regex); 248 | 249 | text.setValue(data.regex).onChange(async (value: string) => { 250 | data.regex = value; 251 | await this.plugin.saveSettings(); 252 | text.inputEl.setAttribute("regex-value", data.regex); 253 | this.disableToggle(data); 254 | }); 255 | 256 | text.inputEl.addClasses(["extra-width", "regex-input"]); 257 | this.addTooltip("Regex", text.inputEl); 258 | } 259 | 260 | /** 261 | * Creates the flags text input field 262 | */ 263 | private createFlagsInput(text: any, data: SettingOption) { 264 | text.setValue(data.flags?.join("").toLowerCase() ?? "gi").onChange(async (value: string) => { 265 | text.inputEl.removeClass("is-invalid"); 266 | this.addTooltip("Regex flags", text.inputEl); 267 | 268 | // Filter valid flags and ensure uniqueness 269 | data.flags = value 270 | .split("") 271 | .map((d) => d.toLowerCase()) 272 | .filter( 273 | (d, index, self) => ["g", "i", "m", "s", "u", "y"].includes(d) && self.indexOf(d) === index 274 | ) as RegexFlags[]; 275 | 276 | // Highlight invalid flags 277 | const invalidFlags = value 278 | .split("") 279 | .filter((d, index, self) => !["g", "i", "m", "s", "u", "y"].includes(d) || self.indexOf(d) !== index); 280 | 281 | if (invalidFlags.length > 0) { 282 | text.inputEl.addClass("is-invalid"); 283 | this.addTooltip("Invalid flags ; they are automatically fixed at save", text.inputEl); 284 | } 285 | 286 | await this.plugin.saveSettings(); 287 | }); 288 | 289 | text.inputEl.addClasses(["min-width", "flags-input"]); 290 | this.addTooltip("Regex flags", text.inputEl); 291 | } 292 | 293 | /** 294 | * Creates the CSS class input field 295 | */ 296 | private createClassInput(text: any, data: SettingOption) { 297 | text.setValue(data.class).onChange(async (value: string) => { 298 | data.class = value; 299 | await this.plugin.saveSettings(); 300 | text.inputEl.setAttribute("css-value", data.class); 301 | }); 302 | 303 | text.inputEl.addClasses(["extra-width", "css-input"]); 304 | this.addTooltip("Class", text.inputEl); 305 | } 306 | 307 | /** 308 | * Creates the hide toggle button 309 | */ 310 | private createHideToggle(toggle: ToggleComponent, data: SettingOption) { 311 | toggle.toggleEl.addClass("group-toggle"); 312 | this.disableToggle(data); 313 | 314 | toggle.setValue(data.hide ?? false).onChange(async (value: boolean) => { 315 | data.hide = value; 316 | await this.plugin.saveSettings(); 317 | }); 318 | 319 | this.toggles.set(data, toggle); 320 | } 321 | 322 | /** 323 | * Creates the delete button 324 | */ 325 | private createDeleteButton(button: any, data: SettingOption) { 326 | button 327 | .setIcon("trash") 328 | .setTooltip("Delete this regex") 329 | .onClick(async () => { 330 | this.plugin.settings.mark = this.plugin.settings.mark.filter((d) => d !== data); 331 | await this.plugin.saveSettings(); 332 | await this.display(); 333 | }); 334 | } 335 | 336 | /** 337 | * Creates the move up button 338 | */ 339 | private createMoveUpButton(button: any, data: SettingOption) { 340 | button 341 | .setIcon("arrow-up") 342 | .setTooltip("Move this regex up") 343 | .onClick(async () => { 344 | const index = this.plugin.settings.mark.indexOf(data); 345 | if (index <= 0) return; 346 | 347 | this.plugin.settings.mark.splice(index - 1, 0, this.plugin.settings.mark.splice(index, 1)[0]); 348 | await this.plugin.saveSettings(); 349 | await this.display(); 350 | }); 351 | } 352 | 353 | /** 354 | * Creates the move down button 355 | */ 356 | private createMoveDownButton(button: any, data: SettingOption) { 357 | button 358 | .setIcon("arrow-down") 359 | .setTooltip("Move this regex down") 360 | .onClick(async () => { 361 | const index = this.plugin.settings.mark.indexOf(data); 362 | if (index >= this.plugin.settings.mark.length - 1) return; 363 | 364 | this.plugin.settings.mark.splice(index + 1, 0, this.plugin.settings.mark.splice(index, 1)[0]); 365 | await this.plugin.saveSettings(); 366 | await this.display(); 367 | }); 368 | } 369 | 370 | /** 371 | * Renders the add and verify buttons at the bottom of settings 372 | */ 373 | private renderActionButtons(containerEl: HTMLElement) { 374 | new Setting(containerEl) 375 | .addButton((button) => { 376 | button 377 | .setButtonText("Add Regex") 378 | .setTooltip("Add a new regex") 379 | .onClick(async () => { 380 | this.plugin.settings.mark.push({ 381 | regex: "", 382 | class: "", 383 | hide: false, 384 | }); 385 | await this.plugin.saveSettings(); 386 | await this.display(); 387 | }); 388 | }) 389 | .addButton((button) => { 390 | button 391 | .setButtonText("Verify & apply") 392 | .setTooltip("Verify and apply the regexes") 393 | .onClick(async () => this.verifyAndApplySettings()); 394 | }); 395 | } 396 | 397 | /** 398 | * Verifies and applies all regex settings 399 | */ 400 | private async verifyAndApplySettings() { 401 | if (this.findDuplicate()) { 402 | const validRegex = this.plugin.settings.mark.every((d) => this.verifyRegex(d, this.plugin.settings.pattern)); 403 | const validCss = this.plugin.settings.mark.every((d) => this.verifyClass(d)); 404 | 405 | if (validRegex && validCss) { 406 | try { 407 | this.plugin.updateCmExtension(); 408 | } catch (e) { 409 | console.error(e); 410 | return; 411 | } 412 | await this.display(); 413 | new Notice(sanitizeHTMLToDom(`🎉 Regexes applied successfully`), 0); 414 | return; 415 | } 416 | 417 | // Construct error message 418 | let msg = "Found "; 419 | if (!validRegex) msg += "invalid regexes"; 420 | if (!validRegex && !validCss) msg += " and "; 421 | if (!validCss) msg += "empty css "; 422 | msg += ". Please fix them before applying."; 423 | 424 | new Notice(sanitizeHTMLToDom(`${msg}`)); 425 | } else { 426 | new Notice("Duplicate regexes found, please fix them before applying."); 427 | } 428 | } 429 | 430 | /** 431 | * Adds a tooltip to an HTML element 432 | */ 433 | addTooltip(text: string, cb: HTMLElement) { 434 | cb.onfocus = () => { 435 | const tooltip = document.body.createEl("div", { text, cls: "tooltip" }); 436 | if (!tooltip) return; 437 | 438 | tooltip.createEl("div", { cls: "tooltip-arrow" }); 439 | const rec = cb.getBoundingClientRect(); 440 | tooltip.style.top = `${rec.top + rec.height + 5}px`; 441 | tooltip.style.left = `${rec.left + rec.width / 2}px`; 442 | tooltip.style.right = `${rec.right}px`; 443 | tooltip.style.width = `max-content`; 444 | tooltip.style.height = `max-content`; 445 | }; 446 | 447 | cb.onblur = () => { 448 | // biome-ignore lint/correctness/noUndeclaredVariables: activeDocument is declared in the Obsidian API 449 | activeDocument.querySelector(".tooltip")?.remove(); 450 | }; 451 | } 452 | 453 | /** 454 | * Verifies if a regex is valid 455 | */ 456 | verifyRegex(data: SettingOption, pattern?: Pattern) { 457 | const index = this.plugin.settings.mark.indexOf(data); 458 | const regex = data.regex; 459 | const inputElement = document.querySelectorAll(".regex-input")[index]; 460 | 461 | // Check if regex is empty 462 | if (regex.trim().length === 0) { 463 | inputElement?.addClass("is-invalid"); 464 | return false; 465 | } 466 | 467 | // Use default pattern if not provided 468 | if (!pattern) pattern = this.plugin.settings.pattern ?? DEFAULT_PATTERN; 469 | 470 | // Validate hide functionality 471 | if (data.hide && !isValidRegex(removeTags(regex, pattern))) { 472 | new Notice(sanitizeHTMLToDom(`The open/close pattern is not recognized`)); 473 | inputElement?.addClass("is-invalid"); 474 | return false; 475 | } 476 | 477 | // Check for newline after [^] regex 478 | if (isInvalid(data.regex)) { 479 | new Notice( 480 | sanitizeHTMLToDom(`You need to add a new line after the [^] regex`) 481 | ); 482 | inputElement?.addClass("is-invalid"); 483 | return false; 484 | } 485 | 486 | // Verify if regex has groups for hiding 487 | if (data.hide && !hasToHide(data.regex, this.plugin.settings.pattern)) { 488 | new Notice( 489 | sanitizeHTMLToDom(`You need to use a group in the regex to hide it`) 490 | ); 491 | data.hide = false; 492 | this.plugin.saveSettings(); 493 | this.disableToggle(data); 494 | inputElement?.addClass("is-invalid"); 495 | } 496 | 497 | // Try to create a RegExp object to validate syntax 498 | try { 499 | new RegExp(regex); 500 | inputElement?.removeClass("is-invalid"); 501 | return true; 502 | } catch (_e) { 503 | console.warn("Invalid regex", regex); 504 | inputElement?.addClass("is-invalid"); 505 | return false; 506 | } 507 | } 508 | 509 | /** 510 | * Verifies if a CSS class is not empty 511 | */ 512 | verifyClass(data: SettingOption) { 513 | const css = data.class; 514 | const inputElement = document.querySelectorAll(".css-input")[this.plugin.settings.mark.indexOf(data)]; 515 | 516 | if (css.trim().length === 0) { 517 | inputElement?.addClass("is-invalid"); 518 | return false; 519 | } 520 | 521 | inputElement?.removeClass("is-invalid"); 522 | return true; 523 | } 524 | 525 | /** 526 | * Gets the regex value from input element 527 | */ 528 | private getRegexFromInput(data: SettingOption) { 529 | const index = this.plugin.settings.mark.indexOf(data); 530 | const input = document.querySelectorAll(".regex-input")[index]; 531 | 532 | if (input) { 533 | const regex = input.getAttribute("regex-value"); 534 | if (regex) return regex; 535 | } 536 | 537 | return data.regex; 538 | } 539 | 540 | /** 541 | * Checks if the regex is valid for the hide toggle 542 | */ 543 | private verifyRegexFromInput(data: SettingOption) { 544 | const regex = this.getRegexFromInput(data); 545 | 546 | if (regex.trim().length === 0) { 547 | return true; // Consider empty regex as invalid for hiding 548 | } 549 | 550 | return !hasToHide(regex, this.settings.pattern) || !isValidRegex(regex, false, this.settings.pattern); 551 | } 552 | 553 | /** 554 | * Finds duplicate regexes in settings 555 | * @returns true if no duplicates found, false otherwise 556 | */ 557 | findDuplicate() { 558 | const duplicateIndex: { 559 | regex: string; 560 | index: number[]; 561 | }[] = []; 562 | 563 | // Remove all invalid markers 564 | document.querySelectorAll(".is-invalid").forEach((d) => d.removeClass("is-invalid")); 565 | 566 | // Find duplicates 567 | for (const data of this.plugin.settings.mark) { 568 | const index = duplicateIndex.findIndex((d) => d.regex === data.regex); 569 | 570 | if (index >= 0) { 571 | duplicateIndex[index].index.push(this.plugin.settings.mark.indexOf(data)); 572 | } else { 573 | duplicateIndex.push({ 574 | regex: data.regex, 575 | index: [this.plugin.settings.mark.indexOf(data)], 576 | }); 577 | } 578 | } 579 | 580 | // Mark duplicates as invalid 581 | const allDuplicateIndex = duplicateIndex.flatMap((d) => (d.index.length > 1 ? d.index : [])); 582 | 583 | if (allDuplicateIndex.length === 0) return true; 584 | 585 | for (const duplicate of allDuplicateIndex) { 586 | const element = document.querySelectorAll(".regex-input")[duplicate]; 587 | element?.addClass("is-invalid"); 588 | 589 | const regex = this.plugin.settings.mark[duplicate].regex; 590 | new Notice(`Duplicate regex: ${regex}.`); 591 | } 592 | 593 | return false; 594 | } 595 | } 596 | -------------------------------------------------------------------------------- /src/settings/viewModal.ts: -------------------------------------------------------------------------------- 1 | import { type App, Modal, Setting } from "obsidian"; 2 | import { DEFAULT_VIEW_MODE, type ViewMode } from "../interface"; 3 | 4 | export class RemarkRegexOptions extends Modal { 5 | result: ViewMode; 6 | regexMark: ViewMode | undefined; 7 | onSubmit: (result: ViewMode) => void; 8 | 9 | constructor(app: App, regexMark: ViewMode | undefined, onSubmit: (result: ViewMode) => void) { 10 | super(app); 11 | this.onSubmit = onSubmit; 12 | this.regexMark = regexMark; 13 | } 14 | 15 | onOpen(): void { 16 | const { contentEl } = this; 17 | this.result = this.regexMark || DEFAULT_VIEW_MODE; 18 | 19 | new Setting(contentEl) 20 | .setName("View mode") 21 | .setHeading() 22 | .setDesc("Allow to choose where the regex should be applied. Each toggle are independent."); 23 | 24 | new Setting(contentEl) 25 | .setName("Reading mode") 26 | .setDesc("Apply the regex to the reading mode") 27 | .addToggle((toggle) => { 28 | toggle.setValue(this.result.reading).onChange((value) => { 29 | this.result.reading = value; 30 | }); 31 | }); 32 | 33 | new Setting(contentEl) 34 | .setName("Source mode") 35 | .setDesc("Note: In source mode, open and close tags are not hidden. It will just enable the css class.") 36 | .addToggle((toggle) => { 37 | toggle.setValue(this.result.source).onChange((value) => { 38 | this.result.source = value; 39 | }); 40 | }); 41 | 42 | new Setting(contentEl) 43 | .setName("Live mode") 44 | .setDesc("Apply the regex to the live mode") 45 | .addToggle((toggle) => { 46 | toggle.setValue(this.result.live).onChange((value) => { 47 | this.result.live = value; 48 | }); 49 | }); 50 | 51 | new Setting(contentEl) 52 | .setHeading() 53 | .setName("Code") 54 | .setDesc("Apply the regex when the text is within code (inline or block).") 55 | .addToggle((toggle) => { 56 | toggle.setValue(this.result.codeBlock ?? true).onChange((value) => { 57 | this.result.codeBlock = value; 58 | }); 59 | }); 60 | 61 | new Setting(contentEl) 62 | .addButton((button) => { 63 | button 64 | .setButtonText("Save") 65 | .setCta() 66 | .onClick(() => { 67 | this.onSubmit(this.result); 68 | this.close(); 69 | }); 70 | }) 71 | .addButton((button) => { 72 | button 73 | .setButtonText("Cancel") 74 | .setWarning() 75 | .onClick(() => { 76 | this.close(); 77 | }); 78 | }); 79 | } 80 | 81 | onClose(): void { 82 | const { contentEl } = this; 83 | contentEl.empty(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | .regex-setting-secondary { 2 | font-size: 0.8em; 3 | color: #666; 4 | } 5 | 6 | .is-live-preview .cm-hide { 7 | display: none; 8 | } 9 | 10 | .is-live-preview .cm-active .cm-hide { 11 | display: inline; 12 | } 13 | 14 | .RegexMark { 15 | -webkit-user-select: text; 16 | user-select: text; 17 | } 18 | 19 | .RegexMark li.error::marker { 20 | color: #fafafa; 21 | } 22 | 23 | .regex-setting .setting-item-info { 24 | display: none; 25 | } 26 | 27 | .regex-setting input.min-width { 28 | min-width: 7ch; 29 | } 30 | 31 | .regex-setting input.extra-width { 32 | width: 100%; 33 | } 34 | 35 | .regex-setting input.is-invalid { 36 | border-color: var(--text-error); 37 | } 38 | 39 | .regex-setting .is-disabled-manually { 40 | opacity: 0.5; 41 | } 42 | 43 | .regex-mark-callout { 44 | border-left: 2px solid var(--color-blue); 45 | padding-left: 1em; 46 | background-color: rgba(var(--color-blue-rgb), 0.1); 47 | } 48 | 49 | .notice:has(.RegexMark.success) { 50 | background-color: rgba(var(--color-green-rgb), 0.6); 51 | } 52 | 53 | .notice:has(.RegexMark.error) { 54 | background-color: rgba(var(--color-red-rgb), 0.6); 55 | } 56 | 57 | .RegexMark input, 58 | .regex-setting input { 59 | font-family: var(--font-monospace); 60 | } 61 | 62 | .setting-item.import-export { 63 | padding-bottom: 10px; 64 | } 65 | 66 | .RegexMark .export-import>.setting-item-info { 67 | display: flex; 68 | flex-direction: row; 69 | gap: 4px; 70 | overflow: hidden; 71 | padding: 0; 72 | } 73 | 74 | .RegexMark .setting-item.export-import>.setting-item-control { 75 | padding-bottom: 10px; 76 | } 77 | 78 | .RegexMark .import-error { 79 | display: none; 80 | color: var(--text-error); 81 | } 82 | 83 | .RegexMark input.error { 84 | border-color: var(--text-error); 85 | } 86 | 87 | .RegexMark .import-error.active { 88 | display: block; 89 | } 90 | 91 | .RegexMark .import-input { 92 | width: 0.1px; 93 | height: 0.1px; 94 | opacity: 0; 95 | overflow: hidden; 96 | position: absolute; 97 | z-index: -1; 98 | } 99 | 100 | .RegexMark .copy, 101 | .RegexMark .download, 102 | .RegexMark .import-label { 103 | color: var(--text-normal); 104 | font-size: var(--font-ui-small); 105 | border-radius: var(--button-radius); 106 | border: 0; 107 | padding: var(--size-4-1) var(--size-4-3); 108 | font-weight: var(--input-font-weight); 109 | font-family: inherit; 110 | cursor: pointer; 111 | background-color: var(--interactive-normal); 112 | box-shadow: var(--input-shadow); 113 | text-align: center; 114 | white-space: nowrap; 115 | text-decoration: none; 116 | transition: background-color 0.1s ease, box-shadow 0.1s ease; 117 | } 118 | 119 | .RegexMark .copy:hover, 120 | .RegexMark .download:hover, 121 | .RegexMark .import-label:hover { 122 | background-color: var(--interactive-hover); 123 | box-shadow: var(--input-shadow-hover); 124 | } 125 | 126 | .RegexMark .import-save { 127 | text-align: center; 128 | width: 100%; 129 | } 130 | 131 | .RegexMark .import-textarea, 132 | .RegexMark .export-textarea { 133 | width: 100%; 134 | height: 200px; 135 | font-family: var(--font-monospace); 136 | font-size: 14px; 137 | } 138 | 139 | .RegexMark .copy.success::before { 140 | opacity: 1; 141 | } 142 | 143 | .RegexMark .copy::before { 144 | color: var(--color-green); 145 | content: "✓"; 146 | position: absolute; 147 | left: -18px; 148 | font-weight: bold; 149 | opacity: 0; 150 | transition: 150ms opacity ease-in-out; 151 | } 152 | 153 | .RegexMark .copy, 154 | .RegexMark .download { 155 | position: relative; 156 | display: inline-block; 157 | margin-left: 10px; 158 | } 159 | 160 | .is-mobile .RegexMark .copy, 161 | .RegexMark .copy, 162 | .RegexMark .download { 163 | padding: 9px !important; 164 | } 165 | 166 | .RegexMark .min-width { 167 | min-width: 7ch; 168 | } -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import type { Pattern } from "./interface"; 2 | 3 | export function removeTags(regex: string, pattern?: Pattern) { 4 | if (!pattern) return regex.replace(/{{open:(.*?)}}/, "$1").replace(/{{close:(.*?)}}/, "$1"); 5 | const open = new RegExp(pattern.open); 6 | const close = new RegExp(pattern.close); 7 | return regex.replace(open, "$1").replace(close, "$1"); 8 | } 9 | 10 | export const isInvalid = (regex: string) => { 11 | return regex.match(/(.*)\[\^(.*)\](.*)/) && !regex.match(/(.*)\[\^(.*)\\n(.*)\](.*)/); 12 | }; 13 | 14 | export function isValidRegex(regex: string, warn = true, pattern?: Pattern) { 15 | if (isInvalid(regex)) { 16 | return false; 17 | } 18 | try { 19 | new RegExp(removeTags(regex, pattern), "gmu"); 20 | return true; 21 | } catch (_e) { 22 | if (warn) console.warn(`Invalid regex: ${regex}`); 23 | return false; 24 | } 25 | } 26 | 27 | export function hasToHide(regex: string, pattern?: Pattern) { 28 | return removeTags(regex, pattern).match(/\((.*?)\)/); 29 | } 30 | 31 | export function extractGroups(regex: string): string[] { 32 | const groupPattern = /\(\?<([a-zA-Z_][a-zA-Z0-9_]*)>/g; 33 | const groups: string[] = []; 34 | 35 | let match; 36 | //biome-ignore lint/suspicious/noAssignInExpressions: 37 | while ((match = groupPattern.exec(regex)) !== null) { 38 | // match[1] contient le nom du groupe 39 | groups.push(match[1]); 40 | } 41 | 42 | return groups; 43 | } 44 | 45 | export function matchGroups(regex: string, text: string): Record | null { 46 | const groupPattern = new RegExp(regex); 47 | const match = groupPattern.exec(text); 48 | 49 | if (!match) return null; 50 | 51 | const groupNames = extractGroups(regex); 52 | const result: Record = {}; 53 | 54 | groupNames.forEach((groupName) => { 55 | if (match.groups && match.groups[groupName] !== undefined) { 56 | result[groupName] = { 57 | text: match.groups[groupName], 58 | input: match[0], 59 | }; 60 | } 61 | }); 62 | if (Object.keys(result).length === 0) return null; 63 | return result; 64 | } 65 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | .regex-setting-secondary { 2 | font-size: 0.8em; 3 | color: #666; 4 | } 5 | 6 | .is-live-preview .cm-hide { 7 | display: none; 8 | } 9 | 10 | .is-live-preview .cm-active .cm-hide { 11 | display: inline; 12 | } 13 | 14 | .RegexMark { 15 | -webkit-user-select: text; 16 | user-select: text; 17 | } 18 | 19 | .RegexMark li.error::marker { 20 | color: #fafafa; 21 | } 22 | 23 | .regex-setting .setting-item-info { 24 | display: none; 25 | } 26 | 27 | .regex-setting input.min-width { 28 | min-width: 7ch; 29 | } 30 | 31 | .regex-setting input.extra-width { 32 | width: 100%; 33 | } 34 | 35 | .regex-setting input.is-invalid { 36 | border-color: var(--text-error); 37 | } 38 | 39 | .regex-setting .is-disabled-manually { 40 | opacity: 0.5; 41 | } 42 | 43 | .regex-mark-callout { 44 | border-left: 2px solid var(--color-blue); 45 | padding-left: 1em; 46 | background-color: rgba(var(--color-blue-rgb), 0.1); 47 | } 48 | 49 | .notice:has(.RegexMark.success) { 50 | background-color: rgba(var(--color-green-rgb), 0.6); 51 | } 52 | 53 | .notice:has(.RegexMark.error) { 54 | background-color: rgba(var(--color-red-rgb), 0.6); 55 | } 56 | 57 | .RegexMark input, 58 | .regex-setting input { 59 | font-family: var(--font-monospace); 60 | } 61 | 62 | .setting-item.import-export { 63 | padding-bottom: 10px; 64 | } 65 | 66 | .RegexMark .export-import>.setting-item-info { 67 | display: flex; 68 | flex-direction: row; 69 | gap: 4px; 70 | overflow: hidden; 71 | padding: 0; 72 | } 73 | 74 | .RegexMark .setting-item.export-import>.setting-item-control { 75 | padding-bottom: 10px; 76 | } 77 | 78 | .RegexMark .import-error { 79 | display: none; 80 | color: var(--text-error); 81 | } 82 | 83 | .RegexMark input.error { 84 | border-color: var(--text-error); 85 | } 86 | 87 | .RegexMark .import-error.active { 88 | display: block; 89 | } 90 | 91 | .RegexMark .import-input { 92 | width: 0.1px; 93 | height: 0.1px; 94 | opacity: 0; 95 | overflow: hidden; 96 | position: absolute; 97 | z-index: -1; 98 | } 99 | 100 | .RegexMark .copy, 101 | .RegexMark .download, 102 | .RegexMark .import-label { 103 | color: var(--text-normal); 104 | font-size: var(--font-ui-small); 105 | border-radius: var(--button-radius); 106 | border: 0; 107 | padding: var(--size-4-1) var(--size-4-3); 108 | font-weight: var(--input-font-weight); 109 | font-family: inherit; 110 | cursor: pointer; 111 | background-color: var(--interactive-normal); 112 | box-shadow: var(--input-shadow); 113 | text-align: center; 114 | white-space: nowrap; 115 | text-decoration: none; 116 | transition: background-color 0.1s ease, box-shadow 0.1s ease; 117 | } 118 | 119 | .RegexMark .copy:hover, 120 | .RegexMark .download:hover, 121 | .RegexMark .import-label:hover { 122 | background-color: var(--interactive-hover); 123 | box-shadow: var(--input-shadow-hover); 124 | } 125 | 126 | .RegexMark .import-save { 127 | text-align: center; 128 | width: 100%; 129 | } 130 | 131 | .RegexMark .import-textarea, 132 | .RegexMark .export-textarea { 133 | width: 100%; 134 | height: 200px; 135 | font-family: var(--font-monospace); 136 | font-size: 14px; 137 | } 138 | 139 | .RegexMark .copy.success::before { 140 | opacity: 1; 141 | } 142 | 143 | .RegexMark .copy::before { 144 | color: var(--color-green); 145 | content: "✓"; 146 | position: absolute; 147 | left: -18px; 148 | font-weight: bold; 149 | opacity: 0; 150 | transition: 150ms opacity ease-in-out; 151 | } 152 | 153 | .RegexMark .copy, 154 | .RegexMark .download { 155 | position: relative; 156 | display: inline-block; 157 | margin-left: 10px; 158 | } 159 | 160 | .is-mobile .RegexMark .copy, 161 | .RegexMark .copy, 162 | .RegexMark .download { 163 | padding: 9px !important; 164 | } 165 | 166 | .RegexMark .min-width { 167 | min-width: 7ch; 168 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "inlineSourceMap": true, 5 | "inlineSources": true, 6 | "module": "ESNext", 7 | "target": "ESNext", 8 | "allowJs": true, 9 | "noImplicitAny": true, 10 | "moduleResolution": "node", 11 | "importHelpers": true, 12 | "isolatedModules": true, 13 | "strictNullChecks": true, 14 | "lib": [ 15 | "DOM", 16 | "ES5", 17 | "ES6", 18 | "ES7", 19 | "ES2018", 20 | "DOM.Iterable", 21 | "ESNext", 22 | "ES2022" 23 | ] 24 | }, 25 | "include": ["**/*.ts"] 26 | } 27 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.0.0": "0.15.0", 3 | "1.0.3": "0.15.0", 4 | "1.1.0": "0.15.0", 5 | "1.1.1": "0.15.0", 6 | "1.1.3": "0.15.0" 7 | } --------------------------------------------------------------------------------