├── .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.
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 | 
72 |
73 | ## View mode
74 |
75 | 
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 |
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: `));
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: `
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 | }
--------------------------------------------------------------------------------