Repository: ebullient/obsidian-task-collector Branch: main Commit: 86d732ae0199 Files: 43 Total size: 297.3 KB Directory structure: gitextract_6ty4o8lg/ ├── .editorconfig ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report.yaml │ │ └── feature-request.yaml │ ├── changelog.hbs │ ├── dependabot.yml │ └── workflows/ │ ├── build.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── biome.json ├── docs/ │ ├── AppendingText.md │ ├── README.md │ └── TaskCollection.md ├── esbuild.config.mjs ├── eslint.config.mjs ├── jest.config.js ├── manifest-beta.json ├── manifest.json ├── package.json ├── src/ │ ├── @types/ │ │ ├── api.d.ts │ │ └── settings.d.ts │ ├── main.ts │ ├── moment.ts │ ├── styles.scss │ ├── taskcollector-Api.ts │ ├── taskcollector-Constants.ts │ ├── taskcollector-Data.ts │ ├── taskcollector-Plugin.ts │ ├── taskcollector-SettingsTab.ts │ ├── taskcollector-TaskCollector.ts │ └── taskcollector-TaskMarkModal.ts ├── test/ │ ├── dataMigration.test.ts │ ├── generatedRegExp.test.ts │ ├── markTasks.test.ts │ ├── mocks/ │ │ ├── moment-obsidian.d.ts │ │ └── obsidian.ts │ ├── moveCompletedItem.test.ts │ ├── resetAllTasks.test.ts │ └── setup.ts ├── tsconfig.json └── versions.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # EditorConfig helps developers define and maintain consistent # coding styles between different editors and IDEs # editorconfig.org root = true [*] # Change these settings to your own preference indent_style = space indent_size = 4 # We recommend you to keep these unchanged end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false [*.{yml,html,rb,css,xml,scss,json}] indent_size = 2 ================================================ FILE: .github/ISSUE_TEMPLATE/bug-report.yaml ================================================ name: Bug Report description: File a bug report title: "🐛 " labels: ["type: bug"] body: - type: markdown attributes: value: | ## Before you start - Check for plugin updates and make sure you're running the latest version of the plugin. - Restart Obsidian and see if the issue persists. - Look at existing bug reports to see if your issue has already been reported. - Is this actually a bug? If this is something that you wish the plugin could do, please submit a feature request instead. - type: textarea id: expected attributes: label: Expected Behavior description: What did you expect to happen? validations: required: true - type: textarea id: what-happened attributes: label: Current behaviour description: | Describe what actually happened. Screenshots or short recordings help, especially if the issue is visual. validations: required: true - type: textarea id: reproduce attributes: label: Steps to reproduce description: | Which exact steps can a developer take to reproduce the issue? The more detail you provide, the easier it will be to narrow down and fix the bug. placeholder: | 1. Launch Obsidian Sandbox via the `Open Sandbox Vault` command. 2. Install the plugin. 3. Create a note with the above markdown snippet 4. ... validations: required: true - type: checkboxes id: operating-systems attributes: label: Which Operating Systems are you using? description: You may select more than one. options: - label: Android - label: iPhone/iPad - label: Linux - label: macOS - label: Windows - type: input id: obsidian-version attributes: label: Obsidian Version description: Which Obsidian version are you using? placeholder: 0.15.9 validations: required: true - type: input id: plugin-version attributes: label: Plugin Version description: Which plugin version are you using? placeholder: 1.11.0 validations: required: true - type: checkboxes id: other-plugins-disabled attributes: label: Checks description: Please confirm options: - label: I have tried it in the sandbox vault with only this plugin enabled required: false - type: textarea id: possible-solution attributes: label: Possible solution description: | Not obligatory, but please suggest a fix or reason for the bug, if you have an idea. ================================================ FILE: .github/ISSUE_TEMPLATE/feature-request.yaml ================================================ name: Feature Request description: Request a new feature title: "✨ " labels: ["type: enhancement"] body: - type: textarea id: feature-description validations: required: true attributes: label: "🔖 Feature description" description: | A clear and concise description of what the feature request is. Include your use case. Examples: - I find it difficult to [...]. - I would like to be able to [...]. This would help me [...]. placeholder: "You should add ..." - type: textarea id: solution validations: required: true attributes: label: "✔️ Solution" description: "A clear and concise description of what you want to happen." placeholder: "In my use-case, ..." - type: textarea id: alternatives validations: required: false attributes: label: "❓ Alternatives" description: "A clear and concise description of any alternative solutions or features you've considered." placeholder: "I have considered ..." - type: textarea id: additional-context validations: required: false attributes: label: "📝 Additional Context" description: "Add any other context or screenshots about the feature request here." placeholder: "..." ================================================ FILE: .github/changelog.hbs ================================================ {{#each releases}} {{#if href}} ###{{#unless major}}#{{/unless}} [{{title}}]({{href}}) {{else}} #### {{title}} {{/if}} {{#if tag}} > {{niceDate}} {{/if}} {{#if summary}} {{summary}} {{/if}} {{#if fixes}} Fixes: {{#each fixes}} - {{#if commit.breaking}}**Breaking change:** {{/if}}{{commit.subject}} {{#each fixes}}#{{id}} {{/each}} {{/each}} {{/if}} {{#if commits}} Commits: {{#each commits}} - {{#if breaking}}**Breaking change:** {{/if}}{{subject}}{{#if href}} [`{{shorthash}}`]({{href}}){{/if}} {{/each}} {{/if}} {{#if merges}} PRs: {{#each merges}} - #{{id}} {{#if commit.breaking}}**Breaking change:** {{/if}}{{message}} {{/each}} {{/if}} {{/each}} ================================================ FILE: .github/dependabot.yml ================================================ # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "npm" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "weekly" cooldown: default-days: 30 - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" cooldown: default-days: 30 ================================================ FILE: .github/workflows/build.yml ================================================ name: CI Test on: workflow_dispatch: push: branches: [ main ] paths: - '**.ts' - '**.json' - '**.scss' - '**.css' pull_request: paths: - '**.ts' - '**.json' - '**.scss' - '**.css' permissions: read-all jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Use Node.js uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: '24' cache: 'npm' - name: Build and Test id: build run: | npm ci npm run build ================================================ FILE: .github/workflows/release.yml ================================================ name: Release Obsidian Plugin # Controls when the action will run. Workflow runs when manually triggered using the UI # or API. on: workflow_dispatch: inputs: version: description: 'New version or major, minor, patch' default: 'patch' required: true update_manifest: description: 'Update manifest.json' default: true required: true type: boolean update_brat: description: 'Update brat manifest' default: true required: true type: boolean env: GH_BOT_EMAIL: "41898282+github-actions[bot]@users.noreply.github.com" GH_BOT_NAME: "GitHub Action" jobs: build: permissions: contents: write id-token: write attestations: write runs-on: ubuntu-latest outputs: version: ${{ steps.build.outputs.version }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 # persist-credentials required: this step pushes commits and tags # zizmor: ignore[artipacked] with: fetch-depth: 0 # otherwise, you will failed to push refs to dest repo - name: Use Node.js uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: '20' cache: 'npm' # Build the plugin - name: Build and Tag id: build env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} INPUT_VERSION: ${{ github.event.inputs.version }} INPUT_UPDATE_MANIFEST: ${{ github.event.inputs.update_manifest }} INPUT_UPDATE_BRAT: ${{ github.event.inputs.update_brat }} run: | echo "version: $INPUT_VERSION" echo "update_manifest: $INPUT_UPDATE_MANIFEST" echo "update_brat: $INPUT_UPDATE_BRAT" git config user.name "$GH_BOT_NAME" git config user.email "$GH_BOT_EMAIL" npm ci npm version "$INPUT_VERSION" --no-git-tag-version VERSION=$(grep '^ "version"' package.json | cut -d'"' -f4) echo "$VERSION" if git rev-parse "refs/tags/$VERSION" > /dev/null 2>&1; then echo "🛑 Tag $VERSION already exists. Delete the release and tag manually, then re-run." exit 1 fi if [ "$INPUT_UPDATE_MANIFEST" = "true" ]; then # shellcheck disable=SC2086 sed -i 's|\(version":\) "[0-9\.]*"|\1 "'$VERSION'"|' manifest.json fi if [ "$INPUT_UPDATE_BRAT" = "true" ]; then # shellcheck disable=SC2086 sed -i 's|\(version":\) "[0-9\.]*"|\1 "'$VERSION'"|' manifest-beta.json fi git add . git status git commit -m "🔖 $VERSION" git push git tag "$VERSION" git push --tags npm run brat-notes -- "${VERSION}" echo "version=${VERSION}" >> "$GITHUB_OUTPUT" # Package the required files into a zip - name: Package env: REPO_NAME: ${{ github.event.repository.name }} RELEASE_VERSION: ${{ steps.build.outputs.version }} run: | mv build "$REPO_NAME" zip -r "${REPO_NAME}-${RELEASE_VERSION}.zip" "$REPO_NAME" unzip -j "${REPO_NAME}-${RELEASE_VERSION}.zip" \ "$REPO_NAME/main.js" \ "$REPO_NAME/styles.css" # Attest build provenance for release artifacts - name: Attest build artifacts uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4 with: subject-path: | ${{ github.event.repository.name }}-${{ steps.build.outputs.version }}.zip ./main.js ./styles.css # Create the release on github - name: Create Release id: create_release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} INPUT_UPDATE_MANIFEST: ${{ github.event.inputs.update_manifest }} REPO_NAME: ${{ github.event.repository.name }} RELEASE_VERSION: ${{ steps.build.outputs.version }} run: | prerelease=true if [ "$INPUT_UPDATE_MANIFEST" = "true" ]; then prerelease=false fi gh release create "$RELEASE_VERSION" \ -F ./release-notes.md \ --title "$RELEASE_VERSION" \ --verify-tag \ --prerelease=${prerelease} \ "${REPO_NAME}-${RELEASE_VERSION}.zip" \ './main.js' \ './styles.css' \ './manifest.json' \ './manifest-beta.json' ================================================ FILE: .gitignore ================================================ # Intellij *.iml .idea # VS Code .vscode .claude # npm node_modules .env # build build # obsidian data.json ================================================ FILE: CHANGELOG.md ================================================ ### Changelog All notable changes to this project will be documented in this file. Dates are displayed in UTC. Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). #### [1.2.2](https://github.com/ebullient/obsidian-task-collector/compare/1.2.1...1.2.2) - 🔧 🎨 eslint updates + fixes [`992e6ca`](https://github.com/ebullient/obsidian-task-collector/commit/992e6caabd1a665e630b9592d3acfaa945a7030b) - Bump typescript from 5.9.3 to 6.0.2 [`c2d2949`](https://github.com/ebullient/obsidian-task-collector/commit/c2d294937764b34907c6ba18603c40dad632e860) - 🏷️ Use @types/moment [`2b34c24`](https://github.com/ebullient/obsidian-task-collector/commit/2b34c243d52e8fa6204176186a615465e7a77a05) - Bump @biomejs/biome from 2.4.8 to 2.4.9 [`a6fc813`](https://github.com/ebullient/obsidian-task-collector/commit/a6fc813f198c2b7397704f145e1fd3d02b4bddee) - Bump @typescript-eslint/parser from 8.57.1 to 8.57.2 [`c07feac`](https://github.com/ebullient/obsidian-task-collector/commit/c07feac9755f028b1c156d7a2993e2307689429c) - ⬆️ reconcile dependency updates [`b1f4093`](https://github.com/ebullient/obsidian-task-collector/commit/b1f40937321a809baa990228376b2174b59b2434) - Bump @types/moment from 2.11.29 to 2.13.0 [`af50f29`](https://github.com/ebullient/obsidian-task-collector/commit/af50f290a12d1edf8c311ec45c13ed5401307588) - Bump @codemirror/view from 6.38.6 to 6.40.0 [`222f50c`](https://github.com/ebullient/obsidian-task-collector/commit/222f50cb3b333103bfd0230e1aeee43479701f92) - 🎨 Minor nits; resolve additional lint issues [`171220d`](https://github.com/ebullient/obsidian-task-collector/commit/171220d5b6e6504d81fb5872451a9d46812507fa) #### [1.2.1](https://github.com/ebullient/obsidian-task-collector/compare/1.2.0...1.2.1) > 23 April 2026 - 🎨 🔥 move plugin API; additional linting/cleanup [`89161a0`](https://github.com/ebullient/obsidian-task-collector/commit/89161a0d5be6cf96417a118507577adcd11421cd) - Bump @biomejs/biome from 2.4.7 to 2.4.8 [`4df9c3b`](https://github.com/ebullient/obsidian-task-collector/commit/4df9c3b2e7b130778ebc7f83279b9bcf64140fe0) #### [1.2.0](https://github.com/ebullient/obsidian-task-collector/compare/1.1.15...1.2.0) > 21 April 2026 - 🎨 cleanup unneeded dependencies [`0061f7c`](https://github.com/ebullient/obsidian-task-collector/commit/0061f7c286042ff5bd6d6a5660ee12e1717196ca) - 🎨 lint fixes: popout awareness, remove old deps [`d784052`](https://github.com/ebullient/obsidian-task-collector/commit/d78405226b76de685a3ed421e9f64ca00610946a) - Bump @typescript-eslint/parser from 8.56.1 to 8.57.1 [`b55464c`](https://github.com/ebullient/obsidian-task-collector/commit/b55464cd68828ae8f2694f8a7182a2a219aad4d2) - Bump esbuild-sass-plugin from 3.6.0 to 3.7.0 [`196449a`](https://github.com/ebullient/obsidian-task-collector/commit/196449a3caf20e15c951c8db2feacbbf53affc6c) #### [1.1.15](https://github.com/ebullient/obsidian-task-collector/compare/1.1.14...1.1.15) > 15 April 2026 - Bump jest from 30.2.0 to 30.3.0 [`f0e885d`](https://github.com/ebullient/obsidian-task-collector/commit/f0e885d6fb65a4e436084ad2eb6c457c013a98d6) - ➕ jest-util as direct dev dependency [`159a020`](https://github.com/ebullient/obsidian-task-collector/commit/159a020d27d1b2b2ab399dd0cfa9d4142d1706c4) - Bump jest-environment-jsdom from 30.2.0 to 30.3.0 [`4eaaa9b`](https://github.com/ebullient/obsidian-task-collector/commit/4eaaa9b54cd69c569c5a68d34951f88f58cb10c3) - Bump esbuild from 0.27.3 to 0.27.4 [`7aa90d3`](https://github.com/ebullient/obsidian-task-collector/commit/7aa90d334142ac6e0728839c663eee1fb2725864) - Bump @typescript-eslint/parser from 8.56.0 to 8.56.1 [`29c1cbe`](https://github.com/ebullient/obsidian-task-collector/commit/29c1cbe56ebb2cf4046a542cafcb5be426ccd5b3) - Bump @biomejs/biome from 2.4.6 to 2.4.7 [`f1d234e`](https://github.com/ebullient/obsidian-task-collector/commit/f1d234ec5fbdddcdb00345da72a3b43d3d3d10c1) - Bump @biomejs/biome from 2.3.15 to 2.4.4 [`a370762`](https://github.com/ebullient/obsidian-task-collector/commit/a37076236be493dc4d6a37fbcf27f3876f8f1251) - Bump @biomejs/biome from 2.3.15 to 2.4.4 [`b1a12df`](https://github.com/ebullient/obsidian-task-collector/commit/b1a12df061ee6f4c5b80702ab81e2ac78b4f1451) - ⬆️ bump dependencies; audit [`11fe2b5`](https://github.com/ebullient/obsidian-task-collector/commit/11fe2b5fa98c7dfd720c5108663e3fb88513881a) - Bump @types/node from 25.3.5 to 25.5.0 [`38059af`](https://github.com/ebullient/obsidian-task-collector/commit/38059af695198d5d2902d34a8f44a50ff7ece2bf) - Bump @types/node from 25.3.0 to 25.3.2 [`9578091`](https://github.com/ebullient/obsidian-task-collector/commit/9578091ad1d86de62d1f0466c7123961c9e62d41) - Bump obsidian from 1.12.2 to 1.12.3 [`4287435`](https://github.com/ebullient/obsidian-task-collector/commit/4287435baf00b8627dbacbf0d2461705851da39c) - Bump @types/node from 25.3.0 to 25.3.2 [`01dac7b`](https://github.com/ebullient/obsidian-task-collector/commit/01dac7bea8451c6d0547c5ebce4e65eb40a32643) - Bump flatted in the npm_and_yarn group across 1 directory [`117e341`](https://github.com/ebullient/obsidian-task-collector/commit/117e341b663f5a630bb1180aaf6a1abcc11b406d) - Bump actions/setup-node from 6.2.0 to 6.3.0 [`3425f21`](https://github.com/ebullient/obsidian-task-collector/commit/3425f217945b151cbca5ddfecd652a9c6a0c4200) #### [1.1.14](https://github.com/ebullient/obsidian-task-collector/compare/1.1.13...1.1.14) > 15 March 2026 #### [1.1.13](https://github.com/ebullient/obsidian-task-collector/compare/1.1.12...1.1.13) > 15 March 2026 - Bump @biomejs/biome from 2.3.12 to 2.3.14 [`d7e9caf`](https://github.com/ebullient/obsidian-task-collector/commit/d7e9caf825a778d348a5acaad02502a1d9830beb) - Bump @biomejs/biome from 2.3.12 to 2.3.14 [`1fae6fa`](https://github.com/ebullient/obsidian-task-collector/commit/1fae6fa383d9b5bd4a128ad6c7dccdc295a4068d) - Bump @biomejs/biome from 2.3.11 to 2.3.12 [`8d825cf`](https://github.com/ebullient/obsidian-task-collector/commit/8d825cfa5cf3fa06b6e6e03484bd7a3257b56abc) - 🐛 empty lines converted when option set [`8bbcfbe`](https://github.com/ebullient/obsidian-task-collector/commit/8bbcfbe04a3b00ebf26c7eb9c5ca00c224da6713) - ⬆️ update minimatch + flatted [`bcfe67a`](https://github.com/ebullient/obsidian-task-collector/commit/bcfe67a61863aa2df7417a2fd7dadbdfff3e66d5) - Bump immutable in the npm_and_yarn group across 1 directory [`342e841`](https://github.com/ebullient/obsidian-task-collector/commit/342e8411443833a228fe42633ab471171d60b03a) #### [1.1.12](https://github.com/ebullient/obsidian-task-collector/compare/1.1.11...1.1.12) > 20 February 2026 - ⬆️ override glob, minimatch ajv [`bfa5731`](https://github.com/ebullient/obsidian-task-collector/commit/bfa573185a9f3f26e2f320a20b168435a36e8130) - ⬆️ dependency bump [`d00ab9d`](https://github.com/ebullient/obsidian-task-collector/commit/d00ab9d4d2f170a5b81db2ac86551acc68bd7f60) - Bump esbuild-sass-plugin from 3.3.1 to 3.6.0 [`df2e9b8`](https://github.com/ebullient/obsidian-task-collector/commit/df2e9b8d5958e49c2b43d937b5fa2c3ba10bdf6c) - Bump esbuild from 0.25.12 to 0.27.0 [`f1ed266`](https://github.com/ebullient/obsidian-task-collector/commit/f1ed26639315eceaed2a850472efd325e18a83f2) - Bump @typescript-eslint/parser from 8.51.0 to 8.52.0 [`8c6dc67`](https://github.com/ebullient/obsidian-task-collector/commit/8c6dc671c6320d0f433b47a28b888e66abef8481) - Bump @biomejs/biome from 2.3.8 to 2.3.11 [`394b96a`](https://github.com/ebullient/obsidian-task-collector/commit/394b96ac6f09ba7205e990d60dc0581bca8048dc) - Bump @biomejs/biome from 2.3.6 to 2.3.8 [`e4971a1`](https://github.com/ebullient/obsidian-task-collector/commit/e4971a132c93f1b7d7d8f12170c49c43863ef637) - Bump @biomejs/biome from 2.3.4 to 2.3.5 [`e5ebef4`](https://github.com/ebullient/obsidian-task-collector/commit/e5ebef465cec79d7f67c0e7dd546046c4373b037) - Bump @biomejs/biome from 2.3.4 to 2.3.5 [`2cc6c46`](https://github.com/ebullient/obsidian-task-collector/commit/2cc6c46a0cdef89cae2d2c5818aa9e29801ba908) - Bump @biomejs/biome from 2.3.2 to 2.3.4 [`1a4f2f2`](https://github.com/ebullient/obsidian-task-collector/commit/1a4f2f2a552e7cd2ebce6472fb2554bee1cd2911) - Bump @types/node from 24.10.4 to 25.0.1 [`2e7a3c2`](https://github.com/ebullient/obsidian-task-collector/commit/2e7a3c266ef0e0aa72286eb6ff9357b353dc90db) - Bump obsidian from 1.10.2 to 1.10.3 [`869c4b2`](https://github.com/ebullient/obsidian-task-collector/commit/869c4b2a092c4d7f9593fbdd51ab52d69f87b2d0) - Bump @types/node from 25.0.1 to 25.0.5 [`bd49006`](https://github.com/ebullient/obsidian-task-collector/commit/bd490060a589f8f484d5939741881b3259db7c8f) - Bump obsidian from 1.11.0 to 1.11.4 [`a689efa`](https://github.com/ebullient/obsidian-task-collector/commit/a689efa656f00aaae20b7d6b4291ae471c02ce33) - Bump @types/node from 24.10.0 to 24.10.1 [`6a390b7`](https://github.com/ebullient/obsidian-task-collector/commit/6a390b7136361cda7d0bd3a7811a62e6b43676c5) - Bump actions/setup-node from 6.1.0 to 6.2.0 [`20324c3`](https://github.com/ebullient/obsidian-task-collector/commit/20324c312beb6f40b08d689ad9592e2ff221613e) - Bump actions/checkout from 6.0.1 to 6.0.2 [`5d14739`](https://github.com/ebullient/obsidian-task-collector/commit/5d147395b24018e88572541e2444b848968205db) - Bump actions/setup-node from 6.0.0 to 6.1.0 [`55097a5`](https://github.com/ebullient/obsidian-task-collector/commit/55097a5a9cbd2277869c9a557e5edc45bf0de06a) - Bump actions/checkout from 5.0.0 to 6.0.1 [`08b9b71`](https://github.com/ebullient/obsidian-task-collector/commit/08b9b71476eb48e91a652770337f9fe5fffd9035) - Bump @codemirror/language from `a9c3c7e` to `2d416d7` [`746e71b`](https://github.com/ebullient/obsidian-task-collector/commit/746e71baf308d8d6b4fefbcfacb0faeabf301964) - 🎨 settings icon [`dfc7dd3`](https://github.com/ebullient/obsidian-task-collector/commit/dfc7dd326f8b9d1d3e85265cf4cc4184ac506eb7) #### [1.1.11](https://github.com/ebullient/obsidian-task-collector/compare/1.1.10...1.1.11) > 2 December 2025 - ⚰️ ⬆️ remove unnecessary cache methods [`d59c45e`](https://github.com/ebullient/obsidian-task-collector/commit/d59c45eab25a7fa098c98748808869598f36919c) - 🔧 eslint for obsidian plugins [`566c9c0`](https://github.com/ebullient/obsidian-task-collector/commit/566c9c0f76e74ecb856a7645747abd835bbda460) - Bump jest from 30.0.2 to 30.0.5 [`7a79cab`](https://github.com/ebullient/obsidian-task-collector/commit/7a79cabc35c30762d64756514c9ec156145e0d8f) - ➖ remove direct codemirror dependencies [`e658f42`](https://github.com/ebullient/obsidian-task-collector/commit/e658f42753fdb8e5254b8e4a9182c7b69be1f3ad) - Bump jest from 30.0.5 to 30.1.3 [`4d6454c`](https://github.com/ebullient/obsidian-task-collector/commit/4d6454cd4ee45b3be678f3e50bd366556ea6e47b) - ⬆️ dependency bump [`cc9f98d`](https://github.com/ebullient/obsidian-task-collector/commit/cc9f98d28484e17f6f944f0ed81a7ad80bf2fd5e) - ⬆️ dependency bump [`b4adbdb`](https://github.com/ebullient/obsidian-task-collector/commit/b4adbdb410d179d4c775a186bc4052e96c8303eb) - Bump esbuild from 0.25.9 to 0.25.10 [`fc10c3a`](https://github.com/ebullient/obsidian-task-collector/commit/fc10c3a47e0e2d33d72a74cfa79e8621bcd3af4b) - Bump esbuild from 0.25.8 to 0.25.9 [`bd8b277`](https://github.com/ebullient/obsidian-task-collector/commit/bd8b277673c510469333fe30b509d00856eeac06) - ✨ regex test setting (when regex defined) [`1dc22a6`](https://github.com/ebullient/obsidian-task-collector/commit/1dc22a6a90d573aa82b60e02da61bdba4b15d0ae) - Bump jest-environment-jsdom from 30.0.5 to 30.1.2 [`8f50d49`](https://github.com/ebullient/obsidian-task-collector/commit/8f50d49b305d5b965b46a4beeae39141bd77e6c2) - dependency bump [`d2c171b`](https://github.com/ebullient/obsidian-task-collector/commit/d2c171bf88500b4bfef89e6baa47c881428fd387) - Bump @biomejs/biome from 2.2.4 to 2.2.6 [`722cc04`](https://github.com/ebullient/obsidian-task-collector/commit/722cc040a359178151f478968133d8cee9530120) - Bump @biomejs/biome from 2.2.4 to 2.3.2 [`e92e966`](https://github.com/ebullient/obsidian-task-collector/commit/e92e966c4003864af0f31f64a8de62106ac32f5f) - Bump @biomejs/biome from 2.2.3 to 2.2.4 [`bb2fe1c`](https://github.com/ebullient/obsidian-task-collector/commit/bb2fe1cc98f3dbaa7382488aa0afc2910fbc5724) - Bump @biomejs/biome from 2.2.2 to 2.2.3 [`c756bcd`](https://github.com/ebullient/obsidian-task-collector/commit/c756bcd707b3a13e1642c07a44bd7b17c1dabd88) - Bump @biomejs/biome from 2.2.0 to 2.2.2 [`5c7f1de`](https://github.com/ebullient/obsidian-task-collector/commit/5c7f1de52c1ffb8fac65f224ed09304c6a253caa) - Bump @biomejs/biome from 2.1.4 to 2.2.0 [`a440c76`](https://github.com/ebullient/obsidian-task-collector/commit/a440c76834ed0a591a595d917f094cfca0e7e19e) - Bump @biomejs/biome from 2.1.3 to 2.1.4 [`6c286b0`](https://github.com/ebullient/obsidian-task-collector/commit/6c286b043a23c490743352d2b05da28c056dc2d8) - Bump @biomejs/biome from 2.1.2 to 2.1.3 [`aab78e6`](https://github.com/ebullient/obsidian-task-collector/commit/aab78e6e08637e51969b5b10ba5df4ec120e6ed9) - Bump @biomejs/biome from 2.0.5 to 2.1.2 [`84c9f2b`](https://github.com/ebullient/obsidian-task-collector/commit/84c9f2b68b8ba721c5570f3b4cd86634807dcc98) - Bump @types/codemirror from 5.60.16 to 5.60.17 [`ab000ea`](https://github.com/ebullient/obsidian-task-collector/commit/ab000ea22264f8490d9cee8dc3b40d5029bab22f) - Resolve package dependencies [`131027b`](https://github.com/ebullient/obsidian-task-collector/commit/131027bed6a372698e2a3eeea93b29034aab2d03) - Bump js-yaml in the npm_and_yarn group across 1 directory [`c51fea3`](https://github.com/ebullient/obsidian-task-collector/commit/c51fea31e37344d02840f06e9da16a3df67ad41f) - 🧪 Add test for more complicated regular expression [`b96338f`](https://github.com/ebullient/obsidian-task-collector/commit/b96338f409b312461696e94799285d865e1fe033) - Bump @types/node from 24.3.3 to 24.6.2 [`5a8d5c0`](https://github.com/ebullient/obsidian-task-collector/commit/5a8d5c064bdcfa534c2b923c9e224fc417a5eb6b) - Bump @types/node from 24.1.0 to 24.2.0 [`badcb1f`](https://github.com/ebullient/obsidian-task-collector/commit/badcb1f8605c90b36ac66606417e67c2404692a5) - 👷 CI consistency [`e28cbb0`](https://github.com/ebullient/obsidian-task-collector/commit/e28cbb08c91efe2517358e446941c275866359d6) - 📌 pin dependencies; apply cooldown [`65c9a3d`](https://github.com/ebullient/obsidian-task-collector/commit/65c9a3de4695bb2b775b61ae0dd316474945d961) - Bump @codemirror/view from 6.37.2 to 6.38.0 [`dd83bbb`](https://github.com/ebullient/obsidian-task-collector/commit/dd83bbba36014bf2e96499c23e21e1d95ac26544) - Bump typescript from 5.9.2 to 5.9.3 [`c1672ad`](https://github.com/ebullient/obsidian-task-collector/commit/c1672ad54e2bdfe658c3f5dd1ac1a92073ca9c4e) - Bump ts-jest from 29.4.1 to 29.4.4 [`a530d3f`](https://github.com/ebullient/obsidian-task-collector/commit/a530d3fc6444df6d74a2ff22237d186fa93416f2) - Bump @types/node from 24.3.1 to 24.3.3 [`44b9416`](https://github.com/ebullient/obsidian-task-collector/commit/44b94167ecf50f1c7ac41ab6671d65906db7d238) - Bump @types/node from 24.3.0 to 24.3.1 [`6167aee`](https://github.com/ebullient/obsidian-task-collector/commit/6167aeeb6b0550e914501dfc608efefa021e3cc4) - Bump @codemirror/view from 6.38.1 to 6.38.2 [`dcdec6f`](https://github.com/ebullient/obsidian-task-collector/commit/dcdec6fe3a1c43286f3e5624a04d016666b3475f) - Bump dotenv from 17.2.1 to 17.2.2 [`aeaaa7c`](https://github.com/ebullient/obsidian-task-collector/commit/aeaaa7cb7c429cbf420b786f5528400f9dfed001) - Bump typescript from 5.8.3 to 5.9.2 [`6734e20`](https://github.com/ebullient/obsidian-task-collector/commit/6734e2076d0a458aa527b398eeacbb5e79fe3a34) - Bump @types/node from 24.0.4 to 24.1.0 [`6ddc5c0`](https://github.com/ebullient/obsidian-task-collector/commit/6ddc5c028dc3ebdab8e49f093c83f51d07e37560) - Bump dotenv from 16.5.0 to 17.2.1 [`6f62ebd`](https://github.com/ebullient/obsidian-task-collector/commit/6f62ebd30f4b5061f36a0587d0078dc605d8508b) - ⚒️ updates due to version updates [`e9594f5`](https://github.com/ebullient/obsidian-task-collector/commit/e9594f5ad85e87e4940469fbd7ed38b0d456673a) - Bump js-yaml in the npm_and_yarn group across 1 directory [`80e5696`](https://github.com/ebullient/obsidian-task-collector/commit/80e5696a5021f9cce2d6f409823240560afb93d8) - Bump actions/setup-node from 5.0.0 to 6.0.0 [`4546207`](https://github.com/ebullient/obsidian-task-collector/commit/454620700aec35570b34eb65560878f740a81756) - Bump actions/checkout from 4.3.0 to 5.0.0 [`04fc1da`](https://github.com/ebullient/obsidian-task-collector/commit/04fc1da98c8b7a496a4cd3eaf8b30123616f2c8a) - Bump @types/node from 24.2.1 to 24.3.0 [`d619ac6`](https://github.com/ebullient/obsidian-task-collector/commit/d619ac6449283f4a8bf9836928fcf4d5aa8b3085) - Bump @types/node from 24.2.0 to 24.2.1 [`3117ffc`](https://github.com/ebullient/obsidian-task-collector/commit/3117ffcc9920b8f87a301fe75720a9e3eb01f495) - 📌 Update minimum obsidian version [`e4da0a9`](https://github.com/ebullient/obsidian-task-collector/commit/e4da0a98b6df79f36633be2d7217464b962bfca2) - Bump actions/setup-node from 4.4.0 to 5.0.0 [`ca15a3c`](https://github.com/ebullient/obsidian-task-collector/commit/ca15a3cacd1359f92068521873950fa133e22937) - Bump actions/checkout from 4.3.0 to 5.0.0 [`c3bb951`](https://github.com/ebullient/obsidian-task-collector/commit/c3bb95128fb1ec16e947b68f16bd1b0e52796612) - Bump ts-jest from 29.4.0 to 29.4.1 [`8b833bd`](https://github.com/ebullient/obsidian-task-collector/commit/8b833bda4da4ce44c65f5bb9de287ee140c8e80a) - Bump @types/node from 24.0.4 to 24.1.0 [`9288f5a`](https://github.com/ebullient/obsidian-task-collector/commit/9288f5acd0949e7c8a349d23d8588e71099edece) #### [1.1.10](https://github.com/ebullient/obsidian-task-collector/compare/1.1.9...1.1.10) > 26 June 2025 - 🐛 Support + for unlinked lists; Resolves [`#657`](https://github.com/ebullient/obsidian-task-collector/issues/657) - Bump @types/node from 22.15.30 to 24.0.2 [`f7bfaf9`](https://github.com/ebullient/obsidian-task-collector/commit/f7bfaf92fbaf4f446db0d8bf1e75ebab1c6bee6b) - Bump jest and @types/jest [`3f6a0e9`](https://github.com/ebullient/obsidian-task-collector/commit/3f6a0e9174d5f695ab0319ea8da1130058f364a2) - ⬆️ dependency bump [`95322fa`](https://github.com/ebullient/obsidian-task-collector/commit/95322fa69543a4e35124d55f7aeaa39d792cae20) - Bump jest from 30.0.0 to 30.0.2 [`8ac23c3`](https://github.com/ebullient/obsidian-task-collector/commit/8ac23c3beb8d567faad8bf7ddbf67d66845edd78) - 🩹 biome formatting [`0bf9f27`](https://github.com/ebullient/obsidian-task-collector/commit/0bf9f27d884f4cb04dff3bbf286493f272f003f0) - Bump jest-environment-jsdom from 29.7.0 to 30.0.0 [`5f7a367`](https://github.com/ebullient/obsidian-task-collector/commit/5f7a367fab14fd815261c8d31a44ad1045c03d50) - Bump jest-environment-jsdom from 30.0.0 to 30.0.2 [`ab94b6b`](https://github.com/ebullient/obsidian-task-collector/commit/ab94b6b10a81cc15401e901cdc84d761e39fe414) - 🩹 biome upgrade [`9ecef24`](https://github.com/ebullient/obsidian-task-collector/commit/9ecef249519e28158e1a1d2368b3960fe14ab778) - Bump @biomejs/biome from 1.9.4 to 2.0.4 [`958091f`](https://github.com/ebullient/obsidian-task-collector/commit/958091fbec805ae1cf453d18ce41200213818f8a) - Bump @biomejs/biome from 1.9.4 to 2.0.4 [`f9e41d3`](https://github.com/ebullient/obsidian-task-collector/commit/f9e41d32811dcae55287823a8fcd0abde6fcc343) - Bump @codemirror/view from 6.37.1 to 6.37.2 [`b198c1a`](https://github.com/ebullient/obsidian-task-collector/commit/b198c1ac3dbd693276f4ab58fac5ac91dd94b68e) - Bump ts-jest from 29.3.4 to 29.4.0 [`d27271b`](https://github.com/ebullient/obsidian-task-collector/commit/d27271b943e65c9490ce4282b3974bee97d855ff) #### [1.1.9](https://github.com/ebullient/obsidian-task-collector/compare/1.1.8...1.1.9) > 6 June 2025 - ⬆️ dependency bump [`b269a35`](https://github.com/ebullient/obsidian-task-collector/commit/b269a35bf5ceedde6d0d9bc285b549830614722d) - Bump esbuild from 0.25.4 to 0.25.5 [`d4414cb`](https://github.com/ebullient/obsidian-task-collector/commit/d4414cb6de3f24ee3eb4bc20b8ca15f6e07367aa) - Bump @codemirror/view from 6.36.8 to 6.37.1 [`6920ff7`](https://github.com/ebullient/obsidian-task-collector/commit/6920ff71341fd20ab8da31fbfff2e40dc218e0a1) - Bump @types/node from 22.15.21 to 22.15.29 [`77c3f68`](https://github.com/ebullient/obsidian-task-collector/commit/77c3f680025bd20921be7fbf4f0b023b94355689) - Bump @types/codemirror from 5.60.15 to 5.60.16 [`ee49f2c`](https://github.com/ebullient/obsidian-task-collector/commit/ee49f2cb40cabe8febf3446827f4d42ab2838903) #### [1.1.8](https://github.com/ebullient/obsidian-task-collector/compare/1.1.7...1.1.8) > 23 May 2025 - 🐛 Ensure checkbox handlers are unique; fixes [`#641`](https://github.com/ebullient/obsidian-task-collector/issues/641) - ⬆️ dependency bump [`56f438f`](https://github.com/ebullient/obsidian-task-collector/commit/56f438ff27b4eddb55d841cde57600e1bc346ff3) - Bump esbuild from 0.25.3 to 0.25.4 [`f358589`](https://github.com/ebullient/obsidian-task-collector/commit/f358589dcc0c5b42550eacef1d6b9b4e015a6e38) - Bump esbuild from 0.25.2 to 0.25.3 [`5098148`](https://github.com/ebullient/obsidian-task-collector/commit/50981485fd72b5dd8ad92797c5f1ea408dcc58dc) - Bump esbuild from 0.25.1 to 0.25.2 [`51d4c3f`](https://github.com/ebullient/obsidian-task-collector/commit/51d4c3f574087a95faf81d2ba3d9dcdc8597a867) - Bump ts-jest from 29.3.2 to 29.3.4 [`1d8d60d`](https://github.com/ebullient/obsidian-task-collector/commit/1d8d60d290a84ff029be1229540b411decb75a63) - Bump ts-jest from 29.2.6 to 29.3.0 [`09842cd`](https://github.com/ebullient/obsidian-task-collector/commit/09842cd0b168c0753835eb218bafa30c6ab1dda7) - Bump ts-jest from 29.3.1 to 29.3.2 [`ef55b1f`](https://github.com/ebullient/obsidian-task-collector/commit/ef55b1f278518d5aa723b84e2bcc1e97d810d14b) - Bump @types/node from 22.13.14 to 22.14.1 [`dfb8d92`](https://github.com/ebullient/obsidian-task-collector/commit/dfb8d92980851afa611e1f0a8223a698f1139d30) - Bump ts-jest from 29.2.6 to 29.3.0 [`922bfba`](https://github.com/ebullient/obsidian-task-collector/commit/922bfba20bd7ed6380aba785e72085d2875f6195) - Bump @types/node from 22.15.3 to 22.15.19 [`78671a0`](https://github.com/ebullient/obsidian-task-collector/commit/78671a05da4f6ad1cf62fa445a0dba9b38355f54) - Bump @codemirror/view from 6.36.7 to 6.36.8 [`b66d066`](https://github.com/ebullient/obsidian-task-collector/commit/b66d0661cbdb6866b10c5943a36c6e0c8cbbf5e6) - Bump @types/node from 22.15.3 to 22.15.19 [`ef7568a`](https://github.com/ebullient/obsidian-task-collector/commit/ef7568a7ed81a755fe6f2025137dbc337749018f) - Bump @codemirror/view from 6.36.6 to 6.36.7 [`f61636b`](https://github.com/ebullient/obsidian-task-collector/commit/f61636b48e2a1ec7663bc8473d9c125162d36be8) - Bump @types/node from 22.14.1 to 22.15.3 [`db5f52e`](https://github.com/ebullient/obsidian-task-collector/commit/db5f52ecd1332fd0fd441c53edcbd1074b643e32) - Bump @codemirror/view from 6.36.5 to 6.36.6 [`6dd3c43`](https://github.com/ebullient/obsidian-task-collector/commit/6dd3c430914d1b39fb7a44afb1cef594c5952768) - Bump dotenv from 16.4.7 to 16.5.0 [`7e17a6b`](https://github.com/ebullient/obsidian-task-collector/commit/7e17a6bff276cfe958030b034ba5cde64bf93f29) - Bump typescript from 5.8.2 to 5.8.3 [`c5b5687`](https://github.com/ebullient/obsidian-task-collector/commit/c5b568704b214006ff9e70b98a5c60b2519b380f) - Bump @codemirror/view from 6.36.4 to 6.36.5 [`c57fdcd`](https://github.com/ebullient/obsidian-task-collector/commit/c57fdcdb06e1c7ea86c8e7ef170f1ec408010f14) - Bump @types/node from 22.13.10 to 22.13.14 [`c3816c7`](https://github.com/ebullient/obsidian-task-collector/commit/c3816c7d8d9871366920ed8dda5205759afd9b4c) - Bump builtin-modules from 4.0.0 to 5.0.0 [`8a3fe97`](https://github.com/ebullient/obsidian-task-collector/commit/8a3fe97d2ab12d15ef643f3df56ea0af0c572908) - Bump typescript from 5.7.3 to 5.8.2 [`fea9878`](https://github.com/ebullient/obsidian-task-collector/commit/fea9878b9f82fae45db7199e13f28634a7c2bd4e) - 🩹 nit fixes for esbuild [`dc34c89`](https://github.com/ebullient/obsidian-task-collector/commit/dc34c899feffde206c3a1f104e87e0b7b101367d) #### [1.1.7](https://github.com/ebullient/obsidian-task-collector/compare/1.1.6...1.1.7) > 25 February 2025 - ✨ Always add reset all command. Resolves [`#611`](https://github.com/ebullient/obsidian-task-collector/issues/611) - ⬆️ bump dependencies [`f72cb31`](https://github.com/ebullient/obsidian-task-collector/commit/f72cb319bc54aa3d53d8a273c89bf28403a92b4c) - Bump esbuild from 0.24.0 to 0.24.2 [`7034d38`](https://github.com/ebullient/obsidian-task-collector/commit/7034d38afd4b92371d18adc633cdb5fa27582e0f) - Bump esbuild from 0.24.2 to 0.25.0 [`06e2ca3`](https://github.com/ebullient/obsidian-task-collector/commit/06e2ca379dacfc56176a0ac087e53d502151223b) - Bump ts-jest from 29.2.5 to 29.2.6 [`41dbfa6`](https://github.com/ebullient/obsidian-task-collector/commit/41dbfa6f67b36d78adb56e5611f4b73ef5a8cb22) - Bump @codemirror/view from 6.36.2 to 6.36.3 [`6ff0037`](https://github.com/ebullient/obsidian-task-collector/commit/6ff0037acb273e11204ddce4501befbe3227bcbf) - Bump @codemirror/state from 6.5.1 to 6.5.2 [`f3b71e0`](https://github.com/ebullient/obsidian-task-collector/commit/f3b71e07b9d19b86d0a25ae165c0d5c6edccd4bb) - Bump @types/node from 22.10.7 to 22.10.10 [`7dd01ff`](https://github.com/ebullient/obsidian-task-collector/commit/7dd01ff2263cbf8180cfd107decb82c1843537af) - Bump @codemirror/state from 6.5.0 to 6.5.1 [`1b7622e`](https://github.com/ebullient/obsidian-task-collector/commit/1b7622ece4ae1b2caa92806933d1f25b7fcc94f5) - Bump typescript from 5.7.2 to 5.7.3 [`295062c`](https://github.com/ebullient/obsidian-task-collector/commit/295062c802945d7c4c8f7e36853cb101bd905077) - Bump @codemirror/view from 6.35.3 to 6.36.1 [`e42114a`](https://github.com/ebullient/obsidian-task-collector/commit/e42114a095387c452649001b60ec896a63ca08dc) - Bump obsidian from 1.7.2 to 1.8.7 [`bc54d7f`](https://github.com/ebullient/obsidian-task-collector/commit/bc54d7f5eb26214c6ef182ba4af625f341479425) - Bump @types/node from 22.13.4 to 22.13.5 [`4a2ca21`](https://github.com/ebullient/obsidian-task-collector/commit/4a2ca21c21d2e26dc00be3c94ef8a1d1f9985098) - Bump @types/node from 22.13.1 to 22.13.4 [`18138b7`](https://github.com/ebullient/obsidian-task-collector/commit/18138b795b1da04d61ea3ea6b155dfe8dc36d805) - Bump @types/node from 22.13.0 to 22.13.1 [`3921f0a`](https://github.com/ebullient/obsidian-task-collector/commit/3921f0aba8aab9e282c3e99775e17e4e7153b693) - Bump @types/node from 22.10.10 to 22.13.0 [`f670717`](https://github.com/ebullient/obsidian-task-collector/commit/f670717577bc616762a5d5b0d9f4146dbef99ec2) - Bump @types/node from 22.10.5 to 22.10.7 [`4837af0`](https://github.com/ebullient/obsidian-task-collector/commit/4837af0ea41fd7eb701dc17565bc99e8f47d995e) - Bump @codemirror/view from 6.36.1 to 6.36.2 [`9881a27`](https://github.com/ebullient/obsidian-task-collector/commit/9881a27702ac0a16d1ccd91ab78b67110f244b15) - Bump @types/node from 22.10.2 to 22.10.5 [`aecdedf`](https://github.com/ebullient/obsidian-task-collector/commit/aecdedff38d4ea73d7595dd2e7f76f1334f130cf) #### [1.1.6](https://github.com/ebullient/obsidian-task-collector/compare/1.1.5...1.1.6) > 19 December 2024 - ♻️ eslint/prettier -> biome [`b866018`](https://github.com/ebullient/obsidian-task-collector/commit/b8660180c067ea90c7bdbea9b5a71d58317c3284) - ⬆️ wrap-up bump [`b36f549`](https://github.com/ebullient/obsidian-task-collector/commit/b36f549a577d023be47d3288808a7e9ae3c7769e) - Bump @eslint/js from 9.13.0 to 9.14.0 [`7325cdb`](https://github.com/ebullient/obsidian-task-collector/commit/7325cdba9857b24c6f3e5691bda505e03697d0b0) - Bump @eslint/js from 9.14.0 to 9.15.0 [`b0b2219`](https://github.com/ebullient/obsidian-task-collector/commit/b0b2219283606c051980d363ac93eae0d0185220) - Bump eslint from 9.13.0 to 9.14.0 [`37407f3`](https://github.com/ebullient/obsidian-task-collector/commit/37407f3f997771a03f9d9ae83f1a0f15d4cea336) - Bump eslint from 9.14.0 to 9.15.0 [`cc02e95`](https://github.com/ebullient/obsidian-task-collector/commit/cc02e9504b3750308aecfab36502c028feb6b5f8) - Bump tslib from 2.8.0 to 2.8.1 [`9d9c66b`](https://github.com/ebullient/obsidian-task-collector/commit/9d9c66b0dcb32173752005e6c31230af6b4556eb) - Bump @types/node from 22.10.1 to 22.10.2 [`770a6de`](https://github.com/ebullient/obsidian-task-collector/commit/770a6de85c6779ef8ecf39539e34399d3f662c6c) - Bump @types/node from 22.9.1 to 22.9.3 [`62328b8`](https://github.com/ebullient/obsidian-task-collector/commit/62328b8fd4888bf4456639054cff14a367cad234) - Bump typescript from 5.6.3 to 5.7.2 [`875f8f1`](https://github.com/ebullient/obsidian-task-collector/commit/875f8f1292c47a6d996ed324ce81a3013d62910c) - Bump @eslint/eslintrc from 3.1.0 to 3.2.0 [`b8a0d99`](https://github.com/ebullient/obsidian-task-collector/commit/b8a0d99d7d098951df949ffed0ffe0240fb8d8f1) - Bump @types/node from 22.8.2 to 22.8.7 [`f6440b7`](https://github.com/ebullient/obsidian-task-collector/commit/f6440b743e623ce7fd05e18ba22026280518623d) - 💸 org-level funding [`f44c8e3`](https://github.com/ebullient/obsidian-task-collector/commit/f44c8e3243679b424422fc174e16a8e4abc556ec) #### [1.1.5](https://github.com/ebullient/obsidian-task-collector/compare/1.1.4...1.1.5) > 29 October 2024 - ⬆️ Resolve updated dependencies [`ff3a01c`](https://github.com/ebullient/obsidian-task-collector/commit/ff3a01cb3439eff9d11539697b1e76b00b1c5769) - Bump @types/node from 22.7.7 to 22.8.1 [`6a3ebfb`](https://github.com/ebullient/obsidian-task-collector/commit/6a3ebfb6ea97af362b37208ca3c44fa2bfafaa34) - MIT License [`e3b06d1`](https://github.com/ebullient/obsidian-task-collector/commit/e3b06d16f7ca9270ad840786129b460b3e9052a4) - ⬆️ fix eslint. See #557 [`e7b1987`](https://github.com/ebullient/obsidian-task-collector/commit/e7b1987398003964d3f16aa56f2814fa0a786c4a) - Bump @typescript-eslint/eslint-plugin from 8.10.0 to 8.11.0 [`382a51c`](https://github.com/ebullient/obsidian-task-collector/commit/382a51cae74bf38f5893603a0e95a611e769b25c) - Bump esbuild from 0.23.1 to 0.24.0 [`8421869`](https://github.com/ebullient/obsidian-task-collector/commit/84218690a7ca944c7fc70da5c118eba7654e738e) - Bump @typescript-eslint/eslint-plugin from 8.8.0 to 8.8.1 [`85144b3`](https://github.com/ebullient/obsidian-task-collector/commit/85144b33ca7cd7a4ec9e716bcf7ad050ed42f77a) - Bump @typescript-eslint/eslint-plugin from 8.6.0 to 8.8.0 [`a348f0e`](https://github.com/ebullient/obsidian-task-collector/commit/a348f0e0e8ef66d8bd2c50c67b531e83a58e82e5) - Bump @typescript-eslint/eslint-plugin from 8.3.0 to 8.4.0 [`301a5ff`](https://github.com/ebullient/obsidian-task-collector/commit/301a5ffeaa9325d4c4ee6ba1b040c433f06c9652) - Bump @typescript-eslint/parser from 8.6.0 to 8.8.0 [`d196a32`](https://github.com/ebullient/obsidian-task-collector/commit/d196a326484133d28d42372200683bd9bb28b50b) - Bump @typescript-eslint/eslint-plugin from 8.4.0 to 8.6.0 [`ec62381`](https://github.com/ebullient/obsidian-task-collector/commit/ec62381fae252be3a00a5d31f5eb1dde505ff591) - Bump @typescript-eslint/eslint-plugin from 8.8.1 to 8.10.0 [`15158b9`](https://github.com/ebullient/obsidian-task-collector/commit/15158b9173f11e24f112e16ee42997343f3a9d37) - Bump @typescript-eslint/parser from 8.4.0 to 8.6.0 [`54a1b3b`](https://github.com/ebullient/obsidian-task-collector/commit/54a1b3bdfded09d92a7f333786cc39d01a5f6b5d) - Bump eslint from 9.12.0 to 9.13.0 [`3dbbfbb`](https://github.com/ebullient/obsidian-task-collector/commit/3dbbfbb1f9f720a0b0d56eb257317e7cfd161633) - 🩹 fix issue template [`f058bc6`](https://github.com/ebullient/obsidian-task-collector/commit/f058bc660286d77949b89667afdc0373af2a2294) - Bump @eslint/js from 9.12.0 to 9.13.0 [`94e9c9e`](https://github.com/ebullient/obsidian-task-collector/commit/94e9c9e564234c02899f06792dd072c289aea458) - Bump @types/jest from 29.5.13 to 29.5.14 [`128d873`](https://github.com/ebullient/obsidian-task-collector/commit/128d8736af78dce039cf4cd5b117d947584ff818) - Bump obsidian from 1.6.6 to 1.7.2 [`6b0c29d`](https://github.com/ebullient/obsidian-task-collector/commit/6b0c29dcae30cbee4ae62f2bb10a1c2ad0e32fe2) - Bump typescript from 5.5.4 to 5.6.2 [`8ad87fb`](https://github.com/ebullient/obsidian-task-collector/commit/8ad87fb656ab3185a5d0025c6bb5b0f80f04c8ad) - Bump @types/jest from 29.5.12 to 29.5.13 [`80a9ee7`](https://github.com/ebullient/obsidian-task-collector/commit/80a9ee724fed033d71acce38ce2c499c0461f33d) - Bump tslib from 2.7.0 to 2.8.0 [`8ea0ad2`](https://github.com/ebullient/obsidian-task-collector/commit/8ea0ad2ae9d9e3de666f83b05ceb7de4fbb86899) - Bump @types/node from 22.7.5 to 22.7.7 [`747820b`](https://github.com/ebullient/obsidian-task-collector/commit/747820b5889ccfe7bb99e4d701e4d1949113f4a8) - Bump typescript from 5.6.2 to 5.6.3 [`1ab59b5`](https://github.com/ebullient/obsidian-task-collector/commit/1ab59b5dfc4fdde6cf476864ebb61c0efa456e54) - Bump @types/node from 22.7.4 to 22.7.5 [`eae1b56`](https://github.com/ebullient/obsidian-task-collector/commit/eae1b56559469e4cf38b20975279d35db6b3fcdb) - Bump @codemirror/view from 6.33.0 to 6.34.1 [`40923e5`](https://github.com/ebullient/obsidian-task-collector/commit/40923e55fd4711fd976e946deffd290de54b5629) - Bump @types/node from 22.5.5 to 22.7.4 [`2d78736`](https://github.com/ebullient/obsidian-task-collector/commit/2d787368e9e83b54c2d435e41de57c0c260862dd) - Bump @types/node from 22.5.4 to 22.5.5 [`45551fc`](https://github.com/ebullient/obsidian-task-collector/commit/45551fca79214e7f67f6d7eceb1db166b3a1bc57) - Bump @types/node from 22.5.2 to 22.5.4 [`c09d5a1`](https://github.com/ebullient/obsidian-task-collector/commit/c09d5a1ed72cde78f0af5ef6a2550e76e02683ed) #### [1.1.4](https://github.com/ebullient/obsidian-task-collector/compare/1.1.3...1.1.4) > 4 September 2024 - Bump @typescript-eslint/eslint-plugin from 8.2.0 to 8.3.0 [`7c6f55c`](https://github.com/ebullient/obsidian-task-collector/commit/7c6f55c419e6d5e0313789d006330ade7b77fd6d) - Bump @typescript-eslint/parser from 8.2.0 to 8.4.0 [`0feb45b`](https://github.com/ebullient/obsidian-task-collector/commit/0feb45b27f28e3a7a1bb5ddfe9d8ed55a374b91f) - ✨ support checking tasks in embedded sections [`a0b40b6`](https://github.com/ebullient/obsidian-task-collector/commit/a0b40b6bd3b9340ea95d90db4a82648403673635) - Bump @types/node from 22.5.0 to 22.5.2 [`7578441`](https://github.com/ebullient/obsidian-task-collector/commit/7578441ff6960225c91b4ee26d8224bc54e2a0e4) #### [1.1.3](https://github.com/ebullient/obsidian-task-collector/compare/1.1.2...1.1.3) > 29 August 2024 - ⬆️ bump eslint [`6c53709`](https://github.com/ebullient/obsidian-task-collector/commit/6c537099ed851d8ab1e15d3ef09fcd4e2d0c3194) - 🐛 modal supports notes embedded in canvas [`d9821cb`](https://github.com/ebullient/obsidian-task-collector/commit/d9821cb51a7eb1573436a4f05786d0d9952db5de) - Bump ts-jest from 29.2.4 to 29.2.5 [`1ecfbb3`](https://github.com/ebullient/obsidian-task-collector/commit/1ecfbb3fe6a6a16b59044ecbac53b2347b09e75d) - Bump tslib from 2.6.3 to 2.7.0 [`c6e96bf`](https://github.com/ebullient/obsidian-task-collector/commit/c6e96bfd454bd720eb4ab116645d2600bbd6cda6) - Bump @codemirror/view from 6.32.0 to 6.33.0 [`fbbebfe`](https://github.com/ebullient/obsidian-task-collector/commit/fbbebfeeb07c434a83845311f011604863563e41) - Bump @types/node from 22.4.0 to 22.4.1 [`e840e03`](https://github.com/ebullient/obsidian-task-collector/commit/e840e03ac0e96921373b60407dac1fd4f3a5a980) #### [1.1.2](https://github.com/ebullient/obsidian-task-collector/compare/1.1.1...1.1.2) > 19 August 2024 - ⬆️ bump eslint plugin [`7fb8179`](https://github.com/ebullient/obsidian-task-collector/commit/7fb817921b31d2ba6b06bc12281d51907f93265e) - Bump @typescript-eslint/eslint-plugin from 7.16.1 to 7.17.0 [`6ad3cb6`](https://github.com/ebullient/obsidian-task-collector/commit/6ad3cb6c0f43a7e1bc2db03e8d86526d6f799632) - Bump @typescript-eslint/parser from 7.16.1 to 7.17.0 [`c17ca23`](https://github.com/ebullient/obsidian-task-collector/commit/c17ca238f1480fa08015f3e32f44cbe717d422df) - Bump @typescript-eslint/parser from 7.17.0 to 7.18.0 [`7485675`](https://github.com/ebullient/obsidian-task-collector/commit/7485675413e82f94650bde69fc479cef0343c567) - Bump @types/node from 20.14.11 to 22.0.0 [`4c05384`](https://github.com/ebullient/obsidian-task-collector/commit/4c05384a8721aa06a8aa5ee7553a5932a511c4e0) - 🔇 re-align with debug setting [`7c7cf27`](https://github.com/ebullient/obsidian-task-collector/commit/7c7cf27a28bffea911f3d4947bf49e0d3af7cbbd) - Bump @types/node from 22.0.0 to 22.1.0 [`f894f8b`](https://github.com/ebullient/obsidian-task-collector/commit/f894f8bbc5f648755e35f8c4c4cc61d6e3036f94) - Bump ts-jest from 29.2.2 to 29.2.3 [`3d783ad`](https://github.com/ebullient/obsidian-task-collector/commit/3d783ad1e01f995ae33d2cb1fe5b5bf4f976180a) - Bump typescript from 5.5.3 to 5.5.4 [`90dd9aa`](https://github.com/ebullient/obsidian-task-collector/commit/90dd9aa8437370339136bc5b5f07bcd621fa01cf) - Bump @codemirror/view from 6.28.4 to 6.28.6 [`7b5c6b0`](https://github.com/ebullient/obsidian-task-collector/commit/7b5c6b065de02374e7b4e0602a0ef48813c5db43) - Bump @types/node from 20.14.10 to 20.14.11 [`78dd141`](https://github.com/ebullient/obsidian-task-collector/commit/78dd141cd534998f0b747cfc0d93ca27d4269793) - Bump @codemirror/view from 6.30.0 to 6.32.0 [`903d894`](https://github.com/ebullient/obsidian-task-collector/commit/903d89466e1a3cc9f688f90ef635b8240fb73f2e) - Bump @types/node from 22.1.0 to 22.2.0 [`823c5ad`](https://github.com/ebullient/obsidian-task-collector/commit/823c5ad4771c729f688cc7cecca068b3cc6122da) - Bump @codemirror/view from 6.29.1 to 6.30.0 [`2e6e559`](https://github.com/ebullient/obsidian-task-collector/commit/2e6e559817e81fad426c32db8db75f7daf5abffe) - Bump ts-jest from 29.2.3 to 29.2.4 [`f01ca0a`](https://github.com/ebullient/obsidian-task-collector/commit/f01ca0adde3a801b7ea88e6c1d33edc393a39ca3) - Bump @codemirror/view from 6.28.6 to 6.29.1 [`c9fa6eb`](https://github.com/ebullient/obsidian-task-collector/commit/c9fa6eb8173673456a83d0847a488e88b0a9bc50) - 🐛 Fix build issues [`da41212`](https://github.com/ebullient/obsidian-task-collector/commit/da41212e6e189e787ca6d4e3bcb76cc34ffe98e0) #### [1.1.1](https://github.com/ebullient/obsidian-task-collector/compare/1.1.0...1.1.1) > 16 July 2024 - ✨ allow task toggle in mark cycle; resolves [`#456`](https://github.com/ebullient/obsidian-task-collector/issues/456) - ⬆️ upgrade dependencies [`a76f791`](https://github.com/ebullient/obsidian-task-collector/commit/a76f791bc9904394b08acf4172f1148667ceea79) - Bump builtin-modules from 3.3.0 to 4.0.0 [`5563822`](https://github.com/ebullient/obsidian-task-collector/commit/556382296bd7ad5c792bf66dc641b86d74ed67b9) - ⬆️ workflow to node 20 [`edd9712`](https://github.com/ebullient/obsidian-task-collector/commit/edd97123af546cdf335cfa9c10d64dc3562025ea) #### [1.1.0](https://github.com/ebullient/obsidian-task-collector/compare/1.0.17...1.1.0) > 16 July 2024 - 🐛 Do not bind to metadata checkbox; fixes [`#435`](https://github.com/ebullient/obsidian-task-collector/issues/435) - ⬆️ upgrade vitest [`7763a89`](https://github.com/ebullient/obsidian-task-collector/commit/7763a898369eeae8c6a59782c9a73e61c0b61ad5) - Bump esbuild from 0.21.5 to 0.23.0 [`c642571`](https://github.com/ebullient/obsidian-task-collector/commit/c64257134cee0d0dcaadac0aaf260c55ec542248) - Bump @typescript-eslint/eslint-plugin from 7.8.0 to 7.9.0 [`1e515b4`](https://github.com/ebullient/obsidian-task-collector/commit/1e515b45c24333df264a53a493866fe317e3ace9) - Bump @typescript-eslint/eslint-plugin from 7.10.0 to 7.11.0 [`d10bb19`](https://github.com/ebullient/obsidian-task-collector/commit/d10bb19410a60406fcf646a317f0c503068e95d9) - Bump @typescript-eslint/eslint-plugin from 7.9.0 to 7.10.0 [`8863c42`](https://github.com/ebullient/obsidian-task-collector/commit/8863c4296b377e64a87ee681ee11784b05e9df23) - Bump @typescript-eslint/eslint-plugin from 7.7.1 to 7.8.0 [`9e5882f`](https://github.com/ebullient/obsidian-task-collector/commit/9e5882f548021f7fff2953760b640f0a9e7db14c) - Bump @typescript-eslint/eslint-plugin from 7.7.0 to 7.7.1 [`86bf26e`](https://github.com/ebullient/obsidian-task-collector/commit/86bf26ea5578dc931897074627026fc7788ce46d) - Bump @typescript-eslint/eslint-plugin from 7.3.1 to 7.4.0 [`450d603`](https://github.com/ebullient/obsidian-task-collector/commit/450d603101a9155b38f3c706395138892f6be1f5) - Bump @typescript-eslint/parser from 7.7.1 to 7.8.0 [`f1605fd`](https://github.com/ebullient/obsidian-task-collector/commit/f1605fdf6e1043a48ef9e2c58cb6e2caa9595d56) - Bump esbuild from 0.21.4 to 0.21.5 [`747c963`](https://github.com/ebullient/obsidian-task-collector/commit/747c963622c8ae514b194a8b776190a5da6cae18) - Bump esbuild from 0.20.2 to 0.21.4 [`11d9fbe`](https://github.com/ebullient/obsidian-task-collector/commit/11d9fbec0c6ae439b7dc8a9efe5ad85987a0d652) - Bump @typescript-eslint/eslint-plugin from 7.4.0 to 7.6.0 [`eca7863`](https://github.com/ebullient/obsidian-task-collector/commit/eca7863ff8ffd77e6ae14da1ed094699b01eeed9) - 📝 issue templates [`9370432`](https://github.com/ebullient/obsidian-task-collector/commit/9370432834bc0acb913a067083a353d0096d4865) - Bump @typescript-eslint/eslint-plugin from 7.13.1 to 7.15.0 [`b735172`](https://github.com/ebullient/obsidian-task-collector/commit/b73517244d9c78063c186dd2c68aa5b90d77a964) - Bump @typescript-eslint/eslint-plugin from 7.11.0 to 7.13.1 [`7b5e2e3`](https://github.com/ebullient/obsidian-task-collector/commit/7b5e2e3a87fc3cd94b7e57640352a86e978784c6) - Bump @typescript-eslint/eslint-plugin from 7.6.0 to 7.7.0 [`b42ddbf`](https://github.com/ebullient/obsidian-task-collector/commit/b42ddbfceb59aae9e411b90cbb39ad3fb6b6c3eb) - Bump @typescript-eslint/parser from 7.6.0 to 7.7.0 [`df1e928`](https://github.com/ebullient/obsidian-task-collector/commit/df1e928bfdac7436a8f226de4139784720649895) - Bump ts-jest from 29.2.0 to 29.2.2 [`bc713b6`](https://github.com/ebullient/obsidian-task-collector/commit/bc713b67bacc0f67cc8b8e58facc58b2803557ec) - Bump @typescript-eslint/parser from 7.3.1 to 7.6.0 [`f1b6922`](https://github.com/ebullient/obsidian-task-collector/commit/f1b692223c27735e5ee28173734ded38ad6367c1) - Bump @typescript-eslint/parser from 7.10.0 to 7.12.0 [`aec3bac`](https://github.com/ebullient/obsidian-task-collector/commit/aec3bacb65b77b64e2c32071232fce05e7a78268) - --- [`5e20eb2`](https://github.com/ebullient/obsidian-task-collector/commit/5e20eb2a9d1e624e012994f0d90caa679297635f) - Bump @typescript-eslint/parser from 7.7.0 to 7.7.1 [`ce5a487`](https://github.com/ebullient/obsidian-task-collector/commit/ce5a487df35d23d9e5a55c2172080f1f9a6140a1) - Bump esbuild-sass-plugin from 3.3.0 to 3.3.1 [`8974f0e`](https://github.com/ebullient/obsidian-task-collector/commit/8974f0e1d2e5ae826f89d69a229683ca93bcacf3) - Bump ts-jest from 29.1.2 to 29.1.3 [`42252b4`](https://github.com/ebullient/obsidian-task-collector/commit/42252b484e56f7dc2e8eb2cb6e580fefa363d1c5) - Bump braces from 3.0.2 to 3.0.3 in the npm_and_yarn group [`b1c592c`](https://github.com/ebullient/obsidian-task-collector/commit/b1c592cf3b58a3ac4e76a36dac8e0a07264b5781) - ✅ test for asterisk task list; see #491 [`5cd2d62`](https://github.com/ebullient/obsidian-task-collector/commit/5cd2d624df7a49ab2a03f3cb182301b3778838de) - Bump esbuild-sass-plugin from 3.2.0 to 3.3.0 [`815ac76`](https://github.com/ebullient/obsidian-task-collector/commit/815ac76655ad53d996d83dfdedc41a6da7011c94) - Bump typescript from 5.5.2 to 5.5.3 [`b8734e3`](https://github.com/ebullient/obsidian-task-collector/commit/b8734e3c0c184812397a4e24f66db706481e4266) - Bump prettier from 3.3.2 to 3.3.3 [`185e09e`](https://github.com/ebullient/obsidian-task-collector/commit/185e09e8b92e396460040654d83d6ef1c650b8bf) - Bump @types/node from 20.14.2 to 20.14.10 [`501462c`](https://github.com/ebullient/obsidian-task-collector/commit/501462c0b46d65b49cb9a5e2a4fe95518a02bef1) - Bump @codemirror/view from 6.28.2 to 6.28.4 [`41e4133`](https://github.com/ebullient/obsidian-task-collector/commit/41e4133ad4bd67197985d5ccb733c3cb4e253237) - Bump tslib from 2.6.2 to 2.6.3 [`98adcfc`](https://github.com/ebullient/obsidian-task-collector/commit/98adcfc709fbad328b5a5a4719b82357b4514ef4) - Bump @codemirror/view from 6.28.1 to 6.28.2 [`3f42cb6`](https://github.com/ebullient/obsidian-task-collector/commit/3f42cb613a0b01c7ccf07cc773c4b1a5828920d0) - Bump typescript from 5.4.5 to 5.5.2 [`940c237`](https://github.com/ebullient/obsidian-task-collector/commit/940c2377073740f3fe75b7cba94b21d00ab7bb36) - Bump ts-jest from 29.1.4 to 29.1.5 [`ae15f5c`](https://github.com/ebullient/obsidian-task-collector/commit/ae15f5c302e046be9503f472c4282b48b4259e6f) - Bump @types/node from 20.14.0 to 20.14.2 [`4bf81fa`](https://github.com/ebullient/obsidian-task-collector/commit/4bf81fae2e80ce5fdfd3bc9eba28160780820ffa) - Bump prettier from 3.3.0 to 3.3.2 [`3eb6dd7`](https://github.com/ebullient/obsidian-task-collector/commit/3eb6dd7a594e9a64adc4941304ad4e6269172326) - Bump @codemirror/view from 6.27.0 to 6.28.1 [`e826895`](https://github.com/ebullient/obsidian-task-collector/commit/e82689500a3d46e917260d374a58818206d4cda2) - Bump @codemirror/view from 6.26.3 to 6.27.0 [`c2134dc`](https://github.com/ebullient/obsidian-task-collector/commit/c2134dc684a148411383305549af99f2a836db6b) - Bump prettier from 3.2.5 to 3.3.0 [`ca6169a`](https://github.com/ebullient/obsidian-task-collector/commit/ca6169acfe18e924512d2de7942ef627b4bca1d4) - Bump ts-jest from 29.1.3 to 29.1.4 [`a4bf072`](https://github.com/ebullient/obsidian-task-collector/commit/a4bf07228251362516e6a57d5a3814fd73f2e9a8) - Bump @types/node from 20.12.12 to 20.14.0 [`10de9f2`](https://github.com/ebullient/obsidian-task-collector/commit/10de9f2d93a55bbf10f05ed98a998d0eeb89e50d) - Bump @types/node from 20.12.11 to 20.12.12 [`091636e`](https://github.com/ebullient/obsidian-task-collector/commit/091636eba26dfb087377d29b8b49bd5d5caa942a) - Bump @types/node from 20.12.8 to 20.12.11 [`a6c0152`](https://github.com/ebullient/obsidian-task-collector/commit/a6c015267af6ee77ad018c8b66a9e543582fc158) - Bump @types/node from 20.12.7 to 20.12.8 [`53c0dd4`](https://github.com/ebullient/obsidian-task-collector/commit/53c0dd461d1970cdeac465c1967bddf270fb8012) - Bump typescript from 5.4.4 to 5.4.5 [`f071b4b`](https://github.com/ebullient/obsidian-task-collector/commit/f071b4bb9b1ec164a72fc2483d4bc1629477f0cc) - Bump @types/node from 20.11.30 to 20.12.7 [`19f0b91`](https://github.com/ebullient/obsidian-task-collector/commit/19f0b91aa1eb2358a240150d4f2efcc9606cc917) - Bump @codemirror/view from 6.26.1 to 6.26.3 [`ab8d60e`](https://github.com/ebullient/obsidian-task-collector/commit/ab8d60ec515417ace0440c12cf928b21924497e4) - Bump typescript from 5.4.3 to 5.4.4 [`cbf172c`](https://github.com/ebullient/obsidian-task-collector/commit/cbf172cc52a6d1d08c6b90367897d610d8fda3d6) - Bump @codemirror/view from 6.26.0 to 6.26.1 [`f29c595`](https://github.com/ebullient/obsidian-task-collector/commit/f29c59570f3e223e6879c505059ece6d475cb169) - Bump typescript from 5.4.2 to 5.4.3 [`f95c735`](https://github.com/ebullient/obsidian-task-collector/commit/f95c73587ea16839d33e1ce5ac10b8558d5ee22a) - 🩹 app reference [`fba8830`](https://github.com/ebullient/obsidian-task-collector/commit/fba8830faaf70f7f79586e92a6a896f764a2bab6) - Bump ws from 8.16.0 to 8.17.1 in the npm_and_yarn group [`b190e1a`](https://github.com/ebullient/obsidian-task-collector/commit/b190e1a447fbe776d6372e50968c8bc4fc289538) - 🎨 formatting [`614d40a`](https://github.com/ebullient/obsidian-task-collector/commit/614d40a222c12e737407815dd931d95ec1a52476) - fixed 491 issue of staredd checklists [`6c3ecb2`](https://github.com/ebullient/obsidian-task-collector/commit/6c3ecb29e0a7b77856df1b21e5f766bf8314ce26) #### [1.0.17](https://github.com/ebullient/obsidian-task-collector/compare/1.0.16...1.0.17) > 21 March 2024 - 🐛 Add message to restart Obsidian; resolves [`#361`](https://github.com/ebullient/obsidian-task-collector/issues/361) - ⬆️ upgrade dependencies [`f530f76`](https://github.com/ebullient/obsidian-task-collector/commit/f530f76860605589a657b8bb77e622805f3ac683) - Bump eslint from 8.56.0 to 8.57.0 [`bc2ae75`](https://github.com/ebullient/obsidian-task-collector/commit/bc2ae75b01e366692074eb154032d61cd1b88715) - Bump @types/node from 20.11.24 to 20.11.28 [`691c147`](https://github.com/ebullient/obsidian-task-collector/commit/691c1479edcfa6fbf895321f6572b205f2b7d8c6) - Bump @codemirror/view from 6.25.1 to 6.26.0 [`6c2067c`](https://github.com/ebullient/obsidian-task-collector/commit/6c2067cc98476cc1d8b27407e99505f28ff80255) - Bump @codemirror/view from 6.24.1 to 6.25.1 [`8a6cdcd`](https://github.com/ebullient/obsidian-task-collector/commit/8a6cdcd14c2e0fdc6d66aa966e7f558d96eff4e9) - Bump typescript from 5.3.3 to 5.4.2 [`3eb8cc9`](https://github.com/ebullient/obsidian-task-collector/commit/3eb8cc9fbcce790be49fe6b0659a3ca527e6ceb8) - Bump @codemirror/state from 6.4.0 to 6.4.1 [`178035c`](https://github.com/ebullient/obsidian-task-collector/commit/178035c31ff2a76876c2b9e7d3dcdff5431e9fc9) - Bump @types/node from 20.11.19 to 20.11.24 [`52b5a26`](https://github.com/ebullient/obsidian-task-collector/commit/52b5a262c3dd2d234efae6a6031613600ef9ddc9) - Bump obsidian from 1.4.11 to 1.5.7 [`894efda`](https://github.com/ebullient/obsidian-task-collector/commit/894efda6be1f4b5863b594c8240ecd62996c94a1) - Bump @codemirror/view from 6.24.0 to 6.24.1 [`98d6305`](https://github.com/ebullient/obsidian-task-collector/commit/98d63059ff67ceb400b4d4c2ca9f03d2804acf67) - Bump dotenv from 16.4.4 to 16.4.5 [`79a00df`](https://github.com/ebullient/obsidian-task-collector/commit/79a00dff00cca373ce98d22aa3037d9f07e3fc3f) - 👷 update deps [`5bd8558`](https://github.com/ebullient/obsidian-task-collector/commit/5bd8558717bca022ca45ec9c0c64417099277ebe) - Update FUNDING.yml [`2f5878f`](https://github.com/ebullient/obsidian-task-collector/commit/2f5878f0b4ccf71edb3b822bc9e576f851811a45) #### [1.0.16](https://github.com/ebullient/obsidian-task-collector/compare/1.0.15...1.0.16) > 20 February 2024 - Bump esbuild and esbuild-sass-plugin [`405fb06`](https://github.com/ebullient/obsidian-task-collector/commit/405fb067c7527ff7ead56c02bd9ff0dffed69e12) - Bump @typescript-eslint/eslint-plugin from 6.20.0 to 6.21.0 [`a946b35`](https://github.com/ebullient/obsidian-task-collector/commit/a946b35c055ed8925559c8da4b0f63ca45e644d1) - Bump @typescript-eslint/eslint-plugin from 6.19.1 to 6.20.0 [`bf81f3d`](https://github.com/ebullient/obsidian-task-collector/commit/bf81f3dfd0fcae5812a86b0b82415b98aca2ff06) - Bump @typescript-eslint/eslint-plugin from 6.19.0 to 6.19.1 [`f43da78`](https://github.com/ebullient/obsidian-task-collector/commit/f43da78d516fd1b2e71b544988709d820b1c9c24) - Bump @typescript-eslint/eslint-plugin from 6.18.1 to 6.19.0 [`516d7e6`](https://github.com/ebullient/obsidian-task-collector/commit/516d7e6f0285caace5924736f917d15fb75324d6) - Bump @typescript-eslint/eslint-plugin from 6.18.0 to 6.18.1 [`52c5098`](https://github.com/ebullient/obsidian-task-collector/commit/52c50985a220d2726ac2c948fe38304a22b1eeb3) - Bump esbuild from 0.19.11 to 0.19.12 [`c48a596`](https://github.com/ebullient/obsidian-task-collector/commit/c48a596dde8a687e6ebd685f4be5e2489793cebc) - Bump @typescript-eslint/parser from 6.19.0 to 6.20.0 [`5e1d0b5`](https://github.com/ebullient/obsidian-task-collector/commit/5e1d0b5cbc15b542296443b86893f30884dca38c) - Bump @typescript-eslint/parser from 6.18.0 to 6.19.0 [`91984f6`](https://github.com/ebullient/obsidian-task-collector/commit/91984f683a4e2a2a5c73460e46a7540c84a1683e) - Bump dotenv from 16.4.1 to 16.4.4 [`e196a02`](https://github.com/ebullient/obsidian-task-collector/commit/e196a0281d30746cd94f57b75d34365353c638e3) - Bump ts-jest from 29.1.1 to 29.1.2 [`0f252e2`](https://github.com/ebullient/obsidian-task-collector/commit/0f252e2894d23eb2c8542cb95eadc1f961d255e8) - Bump @types/node from 20.11.17 to 20.11.19 [`fec08d4`](https://github.com/ebullient/obsidian-task-collector/commit/fec08d4aee3979d31fb5c5b6a13abb2a88ffc36d) - Bump @types/jest from 29.5.11 to 29.5.12 [`edb0e26`](https://github.com/ebullient/obsidian-task-collector/commit/edb0e26aa2d385b4871bf91dee06981dfbae1eed) - Bump @types/node from 20.11.16 to 20.11.17 [`2307c3f`](https://github.com/ebullient/obsidian-task-collector/commit/2307c3fd412e8b41665de3d3fe51e797d588f059) - Bump @codemirror/view from 6.23.1 to 6.24.0 [`24f6c6b`](https://github.com/ebullient/obsidian-task-collector/commit/24f6c6b3c37b3e8bbda414eada07c0e6b2a178c5) - Bump @types/node from 20.11.5 to 20.11.16 [`6474cef`](https://github.com/ebullient/obsidian-task-collector/commit/6474cefe91dd4264ea78eab4aae517362f7b920a) - Bump prettier from 3.2.4 to 3.2.5 [`5243296`](https://github.com/ebullient/obsidian-task-collector/commit/524329682f9360cb4a287bab6bb9c8d8c2cd3d4d) - Bump dotenv from 16.3.2 to 16.4.1 [`e021d55`](https://github.com/ebullient/obsidian-task-collector/commit/e021d556d764d2235d42e515fda6ea00cb5b83e8) - Bump @codemirror/view from 6.23.0 to 6.23.1 [`c378d88`](https://github.com/ebullient/obsidian-task-collector/commit/c378d8836ec0d3232d3afb6e21863fdf21336a75) - Bump @types/node from 20.11.3 to 20.11.5 [`9e5bf1c`](https://github.com/ebullient/obsidian-task-collector/commit/9e5bf1c6e13408428dccad575f70271bab62492e) - Bump prettier from 3.2.2 to 3.2.4 [`e3acccc`](https://github.com/ebullient/obsidian-task-collector/commit/e3acccc2c7ace0e7de52e634e72fb61d74731e1b) - Bump dotenv from 16.3.1 to 16.3.2 [`dcee115`](https://github.com/ebullient/obsidian-task-collector/commit/dcee115ef60674c94bac95c7044c7b68456ec284) - Bump @types/node from 20.10.7 to 20.11.3 [`bfaffc5`](https://github.com/ebullient/obsidian-task-collector/commit/bfaffc511eacf2ad9c09503b24d96f5a6492b4a2) - Bump prettier from 3.1.1 to 3.2.2 [`64d84fb`](https://github.com/ebullient/obsidian-task-collector/commit/64d84fb1653e1684f21832cceede0e5a2548ca74) #### [1.0.15](https://github.com/ebullient/obsidian-task-collector/compare/1.0.14...1.0.15) > 9 January 2024 - 🎨 Right click menu text. Resolves [`#387`](https://github.com/ebullient/obsidian-task-collector/issues/387) - Bump esbuild from 0.19.8 to 0.19.10 [`2d41b83`](https://github.com/ebullient/obsidian-task-collector/commit/2d41b83de98a40d2bea92e0845971d3506123bcd) - Bump esbuild from 0.19.10 to 0.19.11 [`8040ece`](https://github.com/ebullient/obsidian-task-collector/commit/8040ece4061a9cd74e9cc84ad2441409c5cf52fe) - Bump @typescript-eslint/eslint-plugin from 6.16.0 to 6.18.0 [`b6a334f`](https://github.com/ebullient/obsidian-task-collector/commit/b6a334f91b261a15e0ca45fe7867dc1e3e8c62ea) - Bump @typescript-eslint/eslint-plugin from 6.11.0 to 6.14.0 [`6fa3997`](https://github.com/ebullient/obsidian-task-collector/commit/6fa39970fec3fd2cc39a0376c859a5f7cf6088b8) - Bump @typescript-eslint/eslint-plugin from 6.14.0 to 6.16.0 [`fcd6601`](https://github.com/ebullient/obsidian-task-collector/commit/fcd66019906a608e994ca5984c80987f16cc2348) - Bump @typescript-eslint/parser from 6.14.0 to 6.15.0 [`ff60494`](https://github.com/ebullient/obsidian-task-collector/commit/ff6049476e500aa6b0df6377447f0c677cc413a6) - Bump @typescript-eslint/parser from 6.15.0 to 6.18.0 [`e63c1ab`](https://github.com/ebullient/obsidian-task-collector/commit/e63c1ab55a0bc04d702e4aeb92a67eacb5ee2b81) - Bump @typescript-eslint/parser from 6.13.2 to 6.14.0 [`9a9b0b8`](https://github.com/ebullient/obsidian-task-collector/commit/9a9b0b8786627cf44c0f214cd20bb3384ebdee12) - Bump @typescript-eslint/parser from 6.13.1 to 6.13.2 [`cffdd14`](https://github.com/ebullient/obsidian-task-collector/commit/cffdd148b5e911145ed12cb2ab2ef7a6c98b536f) - Bump @codemirror/view from 6.22.3 to 6.23.0 [`776275e`](https://github.com/ebullient/obsidian-task-collector/commit/776275e0e7da98a713a5bcb0ab30780d136456d6) - Bump eslint from 8.55.0 to 8.56.0 [`2ff05f7`](https://github.com/ebullient/obsidian-task-collector/commit/2ff05f779ec3bb45aa051685ccb8d0b8d6155f51) - Bump moment from 2.29.4 to 2.30.1 [`2e03235`](https://github.com/ebullient/obsidian-task-collector/commit/2e032358ab74448d0edab645d26284aaf1788ec4) - Bump @types/node from 20.10.6 to 20.10.7 [`2812fc4`](https://github.com/ebullient/obsidian-task-collector/commit/2812fc4aaf613cdf6412441f9921b506a78636a5) - Bump @types/node from 20.10.5 to 20.10.6 [`9e4fa54`](https://github.com/ebullient/obsidian-task-collector/commit/9e4fa54610b065b6e516cd8c320a3df8dc776104) - Bump esbuild-sass-plugin from 2.16.0 to 2.16.1 [`4aa995f`](https://github.com/ebullient/obsidian-task-collector/commit/4aa995fe67d2dfb7d5e7cead5968a50e9dda0fe4) - Bump @codemirror/state from 6.3.2 to 6.3.3 [`c976fbc`](https://github.com/ebullient/obsidian-task-collector/commit/c976fbc93c0ed39ccf8cda80c5bcce80c0acaa7a) - Bump @types/node from 20.10.0 to 20.10.5 [`01d4901`](https://github.com/ebullient/obsidian-task-collector/commit/01d4901fcc98278361e322199eabcbed903f61f6) - Bump @codemirror/view from 6.22.2 to 6.22.3 [`3c1f4b4`](https://github.com/ebullient/obsidian-task-collector/commit/3c1f4b42ae263313138a1e8edd6fdaea205875b2) - Bump @types/jest from 29.5.10 to 29.5.11 [`bca223e`](https://github.com/ebullient/obsidian-task-collector/commit/bca223ec0033238304c263c748f16808d1067c53) - Bump @codemirror/view from 6.22.1 to 6.22.2 [`4a2668b`](https://github.com/ebullient/obsidian-task-collector/commit/4a2668b02e55a0a37aef13bba18cbdebaa64a50d) - Bump prettier from 3.1.0 to 3.1.1 [`ef503fc`](https://github.com/ebullient/obsidian-task-collector/commit/ef503fc327d57d0dae4fde0f5a8427b5a47ed0a4) - Bump typescript from 5.3.2 to 5.3.3 [`673d55c`](https://github.com/ebullient/obsidian-task-collector/commit/673d55cb4a4948f33170886cc1d1c2cbae0d7ef9) #### [1.0.14](https://github.com/ebullient/obsidian-task-collector/compare/1.0.13...1.0.14) > 7 December 2023 - Bump esbuild from 0.19.6 to 0.19.8 [`3d4bd48`](https://github.com/ebullient/obsidian-task-collector/commit/3d4bd48c8ac26c03207aa48b930b4e556399b530) - Bump esbuild from 0.19.5 to 0.19.6 [`8a87f8e`](https://github.com/ebullient/obsidian-task-collector/commit/8a87f8e006b936773c15d0961c6635f1583ff3a3) - Bump @typescript-eslint/parser from 6.10.0 to 6.11.0 [`de07a5d`](https://github.com/ebullient/obsidian-task-collector/commit/de07a5d71936d08b6266cf0ac3fae38855eb6073) - Bump @typescript-eslint/eslint-plugin from 6.9.1 to 6.11.0 [`3a5d521`](https://github.com/ebullient/obsidian-task-collector/commit/3a5d521a9ffce95e4ae1808350451338e5ae1c9b) - Bump @typescript-eslint/parser from 6.11.0 to 6.12.0 [`40fea47`](https://github.com/ebullient/obsidian-task-collector/commit/40fea477bbf1113374e674d4291798e2138bf41f) - Bump @typescript-eslint/parser from 6.12.0 to 6.13.1 [`1ce9de9`](https://github.com/ebullient/obsidian-task-collector/commit/1ce9de9eabe15ae61f25b069c49853863f0e7903) - Bump eslint from 8.54.0 to 8.55.0 [`95060e1`](https://github.com/ebullient/obsidian-task-collector/commit/95060e1018ecdeb7edb6dbd908dcf70ac97ec0df) - Bump eslint from 8.53.0 to 8.54.0 [`3e1a4f3`](https://github.com/ebullient/obsidian-task-collector/commit/3e1a4f322d2bea385e0631f6d0ac2b2a23a0fb1c) - Bump typescript from 5.2.2 to 5.3.2 [`5d8b98d`](https://github.com/ebullient/obsidian-task-collector/commit/5d8b98d267fe0dd0075c52a0a06a9eff8e839f0a) - Bump @codemirror/state from 6.3.1 to 6.3.2 [`76deac9`](https://github.com/ebullient/obsidian-task-collector/commit/76deac9846b1c1ed96985d9825486df968adf64b) - Bump @types/jest from 29.5.8 to 29.5.10 [`10532ed`](https://github.com/ebullient/obsidian-task-collector/commit/10532edc8b7ba99b68923975d16e7d4e827059b3) - Bump @types/node from 20.9.2 to 20.10.0 [`642ef67`](https://github.com/ebullient/obsidian-task-collector/commit/642ef67653fb06789b8f8b0edd2334a21969bd7a) - Bump @types/codemirror from 5.60.13 to 5.60.15 [`f1c3183`](https://github.com/ebullient/obsidian-task-collector/commit/f1c3183c36beff94dc0cdb59d6d70ddf00666ab8) - Bump @codemirror/view from 6.22.0 to 6.22.1 [`6de53f3`](https://github.com/ebullient/obsidian-task-collector/commit/6de53f312725e02a4c787839d7d4e926b34f624f) - Bump @types/node from 20.9.0 to 20.9.2 [`438eb0a`](https://github.com/ebullient/obsidian-task-collector/commit/438eb0a5ff0b48d9b77b147320a2a207cb6f9480) - 🎨 use this.app consistently [`bc45c4f`](https://github.com/ebullient/obsidian-task-collector/commit/bc45c4f00f15ed6937db9d10d53df2e38031de6f) #### [1.0.13](https://github.com/ebullient/obsidian-task-collector/compare/1.0.12...1.0.13) > 18 November 2023 - 🎨 Add hideNotifications setting; Resolves [`#330`](https://github.com/ebullient/obsidian-task-collector/issues/330) - Bump jest and @types/jest [`6cc2201`](https://github.com/ebullient/obsidian-task-collector/commit/6cc22013f60e59e7a838f2b7032172992bad7cc8) - Bump jest and @types/jest [`c42e1dd`](https://github.com/ebullient/obsidian-task-collector/commit/c42e1dd7f42cc715645a26c5da89489365d10c95) - Bump esbuild and esbuild-sass-plugin [`8cd36eb`](https://github.com/ebullient/obsidian-task-collector/commit/8cd36eb2948c6bcfe9409f8a92000ab4d0413807) - Bump @typescript-eslint/eslint-plugin from 6.7.5 to 6.8.0 [`10ab304`](https://github.com/ebullient/obsidian-task-collector/commit/10ab30473fa389a1f3449d97fd9dc2074fde031b) - Bump esbuild from 0.19.4 to 0.19.5 [`f7ab869`](https://github.com/ebullient/obsidian-task-collector/commit/f7ab869eb9f153843fedfcf95d38bfcb61ee39d7) - Bump esbuild from 0.19.3 to 0.19.4 [`02f8d14`](https://github.com/ebullient/obsidian-task-collector/commit/02f8d147bd7ea68e33a24b5f7ef9dd37f8ea5bb3) - Bump esbuild from 0.19.2 to 0.19.3 [`a4c45fa`](https://github.com/ebullient/obsidian-task-collector/commit/a4c45fa449c618e07c17ae9434f85946439dbb0b) - Bump esbuild from 0.18.17 to 0.18.20 [`23f6470`](https://github.com/ebullient/obsidian-task-collector/commit/23f6470a4f6b782492606bb72f0709f7f352b8e6) - Bump @typescript-eslint/eslint-plugin from 6.7.2 to 6.7.3 [`0a9a8ba`](https://github.com/ebullient/obsidian-task-collector/commit/0a9a8ba21f16dba4a7e99a3137239135140e527c) - Bump @typescript-eslint/eslint-plugin from 6.7.4 to 6.7.5 [`36ceab7`](https://github.com/ebullient/obsidian-task-collector/commit/36ceab7895fb5f88e1cc125e6a5fb91e93048495) - Bump @typescript-eslint/eslint-plugin from 6.7.3 to 6.7.4 [`d3bad25`](https://github.com/ebullient/obsidian-task-collector/commit/d3bad2510ad76d83f5e82170af03b4c01f6ffd9b) - Bump @typescript-eslint/eslint-plugin from 6.4.0 to 6.4.1 [`4feae9b`](https://github.com/ebullient/obsidian-task-collector/commit/4feae9b0b5b29a7228ee88fa80d8d84c368f1f52) - Bump @babel/traverse from 7.22.5 to 7.23.2 [`23f40a9`](https://github.com/ebullient/obsidian-task-collector/commit/23f40a98428b1f91a5082345897630a139ab6a7a) - Bump @typescript-eslint/eslint-plugin from 6.3.0 to 6.4.0 [`7846487`](https://github.com/ebullient/obsidian-task-collector/commit/78464871e6580196c0972e8c69afd2a105323d9c) - Bump @typescript-eslint/eslint-plugin from 6.6.0 to 6.7.2 [`4259831`](https://github.com/ebullient/obsidian-task-collector/commit/4259831e26978b010098aa0bc35d8f433dd8eb00) - Bump @typescript-eslint/eslint-plugin from 6.4.1 to 6.6.0 [`16e503d`](https://github.com/ebullient/obsidian-task-collector/commit/16e503d91c8dcf3c57dff0ff57143ca1b43ca336) - Bump @typescript-eslint/eslint-plugin from 6.9.0 to 6.9.1 [`aa3b801`](https://github.com/ebullient/obsidian-task-collector/commit/aa3b801869b7b6249f9e8ce5f6dff538454ca338) - Bump @typescript-eslint/eslint-plugin from 6.8.0 to 6.9.0 [`1d1d27a`](https://github.com/ebullient/obsidian-task-collector/commit/1d1d27a69aadb17119f0e06447fbdac02cc6ff3a) - ✨ Skip section matching pattern [`4460a31`](https://github.com/ebullient/obsidian-task-collector/commit/4460a315335de22737a07a7ce7d07f77301e36ea) - Bump @typescript-eslint/parser from 6.3.0 to 6.4.0 [`8a8b3e5`](https://github.com/ebullient/obsidian-task-collector/commit/8a8b3e5b8befad597eccd72e6db7a01770a44535) - Bump jest-environment-jsdom from 29.6.4 to 29.7.0 [`e260598`](https://github.com/ebullient/obsidian-task-collector/commit/e26059854037c4b763af2e09b34d55ca645fba74) - Bump @typescript-eslint/parser from 6.7.5 to 6.10.0 [`b7eb9f7`](https://github.com/ebullient/obsidian-task-collector/commit/b7eb9f7e38f2b38a17925e0abc1b1cf4938ea050) - Bump @typescript-eslint/parser from 6.7.3 to 6.7.5 [`8fa7051`](https://github.com/ebullient/obsidian-task-collector/commit/8fa70517ce47a6db771665cc671f98cb055fb502) - Bump @typescript-eslint/parser from 6.4.0 to 6.7.3 [`d62687a`](https://github.com/ebullient/obsidian-task-collector/commit/d62687aca654a9091eed34d8e570c5b6bb033ecc) - Bump esbuild-sass-plugin from 2.15.0 to 2.16.0 [`18b583c`](https://github.com/ebullient/obsidian-task-collector/commit/18b583c265ed195efbaba3e44f2a6d1c0b9792d8) - Bump eslint from 8.51.0 to 8.52.0 [`bdd94b4`](https://github.com/ebullient/obsidian-task-collector/commit/bdd94b4f813d6557a432c9557e89c1ba9a673ea4) - Bump eslint from 8.52.0 to 8.53.0 [`01331f8`](https://github.com/ebullient/obsidian-task-collector/commit/01331f84d4ddfc4c4499d3837cee05e2fe97c658) - 👷 cleanup [`5072c8a`](https://github.com/ebullient/obsidian-task-collector/commit/5072c8a2e17ece9acfbcd3cd5438af3eaaf086d1) - Bump eslint from 8.48.0 to 8.49.0 [`ac45895`](https://github.com/ebullient/obsidian-task-collector/commit/ac4589517859261da2524aa6a4155ac87d19e21f) - Bump esbuild-sass-plugin from 2.10.0 to 2.11.0 [`9aeb4f9`](https://github.com/ebullient/obsidian-task-collector/commit/9aeb4f927c2834a0d7f7bc7d7cb04301c6c35889) - Bump @types/node from 20.8.3 to 20.8.6 [`b667e25`](https://github.com/ebullient/obsidian-task-collector/commit/b667e258159bbedcdae2795a463cd2836e79608f) - ⬆️ bump semver [`a067001`](https://github.com/ebullient/obsidian-task-collector/commit/a067001a1e6eec1de20b13360a1f3cf3e396bb6d) - Bump jest-environment-jsdom from 29.6.2 to 29.6.4 [`3ff21b0`](https://github.com/ebullient/obsidian-task-collector/commit/3ff21b05ec7e4df0bc6b45358154e3de58e19024) - Bump @types/codemirror from 5.60.8 to 5.60.9 [`720ac8b`](https://github.com/ebullient/obsidian-task-collector/commit/720ac8b1019cc05d448483a0e000dce0c931143b) - Bump @types/node from 20.8.7 to 20.8.9 [`0f8d0ee`](https://github.com/ebullient/obsidian-task-collector/commit/0f8d0eee0e67c2759bd1df76ac90f1ead11d0887) - Bump eslint from 8.50.0 to 8.51.0 [`930295e`](https://github.com/ebullient/obsidian-task-collector/commit/930295ed0b3a2ed02c3c6e2c045276a3e499684d) - Bump eslint from 8.49.0 to 8.50.0 [`229db3b`](https://github.com/ebullient/obsidian-task-collector/commit/229db3b7527e190a4e39c08038a27910d714431b) - Bump @codemirror/view from 6.16.0 to 6.17.1 [`d1f7b4f`](https://github.com/ebullient/obsidian-task-collector/commit/d1f7b4facd3aed5c3b7b6437c1a11f36d10cc2a0) - Bump eslint from 8.47.0 to 8.48.0 [`a7ea521`](https://github.com/ebullient/obsidian-task-collector/commit/a7ea521c640de1d5b39a8225022c0521f933a422) - Bump @types/codemirror from 5.60.12 to 5.60.13 [`06e3f72`](https://github.com/ebullient/obsidian-task-collector/commit/06e3f72e5bc885ebf25c1cc4a04c14650374a0d1) - Bump @types/node from 20.8.10 to 20.9.0 [`9695e14`](https://github.com/ebullient/obsidian-task-collector/commit/9695e14cefe63e50c27a156beb73586d57215dd1) - Bump @codemirror/view from 6.21.4 to 6.22.0 [`0aa87ce`](https://github.com/ebullient/obsidian-task-collector/commit/0aa87ce19094a6087f0f379f1425395b1bf081b8) - Bump prettier from 3.0.3 to 3.1.0 [`6a3ca0a`](https://github.com/ebullient/obsidian-task-collector/commit/6a3ca0adda8b3eee669f3c93d4a0cfc036a7e863) - Bump @types/jest from 29.5.7 to 29.5.8 [`193b180`](https://github.com/ebullient/obsidian-task-collector/commit/193b18051eb868527da3555a4525b4bbca46bab4) - Bump @types/node from 20.8.9 to 20.8.10 [`19045da`](https://github.com/ebullient/obsidian-task-collector/commit/19045da5f53d277454d3edd3688512af6d6219d1) - Bump @types/jest from 29.5.6 to 29.5.7 [`9b7f972`](https://github.com/ebullient/obsidian-task-collector/commit/9b7f972742621f8ca8c6b5a9d488a1c5229de010) - Bump @codemirror/state from 6.3.0 to 6.3.1 [`955d0fb`](https://github.com/ebullient/obsidian-task-collector/commit/955d0fb2163bd4b8123888879fde541965ed9917) - Bump @codemirror/view from 6.21.3 to 6.21.4 [`074dcec`](https://github.com/ebullient/obsidian-task-collector/commit/074dcec41d83824eaa269ffe064427b6b5bd87b8) - Bump @types/jest from 29.5.5 to 29.5.6 [`1f5a140`](https://github.com/ebullient/obsidian-task-collector/commit/1f5a14093ed7040f12d25723d089b85366233734) - Bump @types/node from 20.8.6 to 20.8.7 [`462e395`](https://github.com/ebullient/obsidian-task-collector/commit/462e39569945ef591e9e98a6f9975924b0a2f17a) - Bump @types/codemirror from 5.60.10 to 5.60.12 [`9791513`](https://github.com/ebullient/obsidian-task-collector/commit/97915139b1bbb48ddbe9521c521d887b67c7d382) - Bump @codemirror/state from 6.2.1 to 6.3.0 [`411ad51`](https://github.com/ebullient/obsidian-task-collector/commit/411ad516e42272c0957a2a25bcb415cfe2804b4c) - Bump @types/node from 20.6.2 to 20.8.3 [`a2880e9`](https://github.com/ebullient/obsidian-task-collector/commit/a2880e93d18320632a5bec1b1692413ea54e1e87) - Bump @codemirror/view from 6.21.1 to 6.21.3 [`e527d61`](https://github.com/ebullient/obsidian-task-collector/commit/e527d6144cba70b7707284e024b2904a719b1d3e) - Bump @codemirror/view from 6.20.2 to 6.21.1 [`c96b247`](https://github.com/ebullient/obsidian-task-collector/commit/c96b247fdeb8abc1d91c5ee3c450a40d8122d4b7) - Bump esbuild-sass-plugin from 2.14.0 to 2.15.0 [`471f3c9`](https://github.com/ebullient/obsidian-task-collector/commit/471f3c99e47fe1f996bc02de910036e8d8445794) - Bump @codemirror/view from 6.18.0 to 6.20.2 [`aef2079`](https://github.com/ebullient/obsidian-task-collector/commit/aef2079ba4d39e6ee1df1f16df62d617cda1128d) - Bump @types/codemirror from 5.60.9 to 5.60.10 [`cb3d6aa`](https://github.com/ebullient/obsidian-task-collector/commit/cb3d6aa7b7bcf86cd85121de85b31ed901106e38) - Bump obsidian from 1.4.4 to 1.4.11 [`7aee358`](https://github.com/ebullient/obsidian-task-collector/commit/7aee3584a5fcf6165632704182ce7ecf7b48d550) - Bump @types/node from 20.6.0 to 20.6.2 [`3aa2ac0`](https://github.com/ebullient/obsidian-task-collector/commit/3aa2ac0056918a17746e272ae9f56e2368aa8213) - Bump @types/node from 20.5.9 to 20.6.0 [`16b1b48`](https://github.com/ebullient/obsidian-task-collector/commit/16b1b485a822b02ff7f0832e63fec34584f8ef7a) - Bump @codemirror/view from 6.17.1 to 6.18.0 [`b9196f9`](https://github.com/ebullient/obsidian-task-collector/commit/b9196f9df4afd2fdbc8983b6e45c392f6f801be6) - Bump typescript from 5.1.6 to 5.2.2 [`4f22fba`](https://github.com/ebullient/obsidian-task-collector/commit/4f22fbab6d7a4d4464fccaf1c86501f23651f6f6) - Bump prettier from 3.0.2 to 3.0.3 [`9aab772`](https://github.com/ebullient/obsidian-task-collector/commit/9aab772ccb3e1068c3fd69a6911e8885338f57e3) - Bump @types/node from 20.5.0 to 20.5.9 [`6840f90`](https://github.com/ebullient/obsidian-task-collector/commit/6840f90653c4c75070b1f9a16db0d41b373623b6) - Bump prettier from 3.0.1 to 3.0.2 [`af282a2`](https://github.com/ebullient/obsidian-task-collector/commit/af282a2d80aa62ab1f8c45e7eb74deb5052fcb8c) - Bump obsidian from 1.4.0 to 1.4.4 [`30f6503`](https://github.com/ebullient/obsidian-task-collector/commit/30f65035e30d341d665e8f5e054e47a8f13a661e) - Bump tslib from 2.6.1 to 2.6.2 [`eb4005d`](https://github.com/ebullient/obsidian-task-collector/commit/eb4005d8f588066b69cfc7083c1dafdf3209922c) - 👷 sync manifest-beta [`f3a0a21`](https://github.com/ebullient/obsidian-task-collector/commit/f3a0a2112cc286301df0515eb36414d13894e82c) #### [1.0.12](https://github.com/ebullient/obsidian-task-collector/compare/1.0.11...1.0.12) > 16 August 2023 - 🐛 Ensure the last line is moved. Fixes [`#262`](https://github.com/ebullient/obsidian-task-collector/issues/262) - ⬆️ Bump jest version [`9a3460d`](https://github.com/ebullient/obsidian-task-collector/commit/9a3460d0c53a8bc29e03095d9f77c390c1920146) - Bump jest from 29.5.0 to 29.6.1 [`0a2dc56`](https://github.com/ebullient/obsidian-task-collector/commit/0a2dc56cec5b05c75d59f4a4b552974bd99bd5a7) - Bump jest from 29.6.1 to 29.6.2 [`2fbfad1`](https://github.com/ebullient/obsidian-task-collector/commit/2fbfad1ce289d0f0cf1347a4e9b5c490f46de807) - Bump esbuild and esbuild-sass-plugin [`a82994c`](https://github.com/ebullient/obsidian-task-collector/commit/a82994c52d0d7340e8d6fcc6ef41ec51987ec511) - Bump @typescript-eslint/parser from 5.59.8 to 5.59.9 [`fe81c4d`](https://github.com/ebullient/obsidian-task-collector/commit/fe81c4d5a7c73405a3910456a060d5600e4a8cb5) - Bump @typescript-eslint/eslint-plugin from 5.59.8 to 5.59.9 [`7fb8831`](https://github.com/ebullient/obsidian-task-collector/commit/7fb88312cf78a58502365fd3c0b5b2b91f1895fb) - ✏️ Prettier. [`ade8d42`](https://github.com/ebullient/obsidian-task-collector/commit/ade8d42b6e59ecbc95ad99af2b1d843d06af7a42) - Bump @typescript-eslint/eslint-plugin from 5.60.1 to 6.0.0 [`73ece21`](https://github.com/ebullient/obsidian-task-collector/commit/73ece21f22401483c8376ef4e2fc624a69b8553d) - Bump @typescript-eslint/eslint-plugin from 6.0.0 to 6.1.0 [`eeddc44`](https://github.com/ebullient/obsidian-task-collector/commit/eeddc4462c51937592ca8cf59039a0ce758b7bf6) - Bump @typescript-eslint/eslint-plugin from 5.59.11 to 5.60.0 [`09d271c`](https://github.com/ebullient/obsidian-task-collector/commit/09d271c52049d4c8f1afa333023cadf5d0c0d165) - Bump @typescript-eslint/eslint-plugin from 5.59.9 to 5.59.11 [`25d60cd`](https://github.com/ebullient/obsidian-task-collector/commit/25d60cd8bcabd4ad19fe18091aea3bc5982129a2) - Bump @typescript-eslint/eslint-plugin from 6.2.0 to 6.2.1 [`efeb2f0`](https://github.com/ebullient/obsidian-task-collector/commit/efeb2f057fe919066ae59d3900fb3dbdb63b70a3) - Bump @typescript-eslint/parser from 6.2.0 to 6.2.1 [`db8c145`](https://github.com/ebullient/obsidian-task-collector/commit/db8c1452b314eb479ef39c299f26e0e40c27fa27) - Bump esbuild from 0.18.16 to 0.18.17 [`98bb666`](https://github.com/ebullient/obsidian-task-collector/commit/98bb6667e55fa16e174bbf6cd3514bcb8043fd2d) - Bump esbuild from 0.18.11 to 0.18.16 [`ef64f12`](https://github.com/ebullient/obsidian-task-collector/commit/ef64f12022d261de556091cf829e4be7bd355a1c) - Bump esbuild from 0.18.9 to 0.18.11 [`ddafa5f`](https://github.com/ebullient/obsidian-task-collector/commit/ddafa5fbcc8b88574d5c9307671eccb153879137) - Bump @typescript-eslint/parser from 5.59.11 to 5.60.0 [`564b131`](https://github.com/ebullient/obsidian-task-collector/commit/564b1310af4e849d9266fb4c52c816bcd76481cc) - Bump @typescript-eslint/parser from 5.59.9 to 5.59.11 [`afa25b6`](https://github.com/ebullient/obsidian-task-collector/commit/afa25b6d92c2b984a90048bf69ad6e8d0d718adf) - Bump @typescript-eslint/eslint-plugin from 6.2.1 to 6.3.0 [`2610f76`](https://github.com/ebullient/obsidian-task-collector/commit/2610f76bf97f645306ef65fbae85601157726ab0) - Bump @typescript-eslint/eslint-plugin from 6.1.0 to 6.2.0 [`ea29016`](https://github.com/ebullient/obsidian-task-collector/commit/ea290169000e69f0058a523bde2cb5414ca4bdb9) - Bump eslint from 8.45.0 to 8.47.0 [`0be8ee4`](https://github.com/ebullient/obsidian-task-collector/commit/0be8ee45aa12b9858c78478fa0ebdaa2aa2d7bd5) - Bump jest-environment-jsdom from 29.5.0 to 29.6.1 [`944f6b6`](https://github.com/ebullient/obsidian-task-collector/commit/944f6b6da1b801d1b063db9ace3fe6f8a860fd78) - Bump @typescript-eslint/parser from 5.60.1 to 6.1.0 [`dacc120`](https://github.com/ebullient/obsidian-task-collector/commit/dacc120768a0b8eb746119197b4b76e81a65d8d2) - Bump @typescript-eslint/parser from 6.2.1 to 6.3.0 [`c6fda83`](https://github.com/ebullient/obsidian-task-collector/commit/c6fda83f3c0e8a8a41b3e0abd1832bacbd7c649f) - Bump @typescript-eslint/parser from 6.1.0 to 6.2.0 [`34558e2`](https://github.com/ebullient/obsidian-task-collector/commit/34558e298a90eb288597e0847f0915b8ef8d2de2) - Bump jest-environment-jsdom from 29.6.1 to 29.6.2 [`4cc7fb0`](https://github.com/ebullient/obsidian-task-collector/commit/4cc7fb0928447885bd3a56327c4c5f60c4842683) - Bump eslint from 8.43.0 to 8.44.0 [`fa46d44`](https://github.com/ebullient/obsidian-task-collector/commit/fa46d445719ee6fbec49b9490d820205c1901590) - ⬆️ 🐛 Fix references to moment [`c5c37ad`](https://github.com/ebullient/obsidian-task-collector/commit/c5c37adfca474d83bff1391b6dc3c4573673f1cc) - Bump eslint from 8.42.0 to 8.43.0 [`955bfa4`](https://github.com/ebullient/obsidian-task-collector/commit/955bfa409bccdad8202113a19ea7fc202199e247) - Bump obsidian from 1.2.8 to 1.4.0 [`b89569a`](https://github.com/ebullient/obsidian-task-collector/commit/b89569a093d01792c8fdf8d7240b3bc7f33a68e8) - Bump dotenv from 16.1.4 to 16.3.1 [`daefbb9`](https://github.com/ebullient/obsidian-task-collector/commit/daefbb9469a9c13d640269def1995dfc2d03fa85) - Bump @codemirror/view from 6.13.2 to 6.14.0 [`968bc27`](https://github.com/ebullient/obsidian-task-collector/commit/968bc27c4a4925e46267e2121b69aa7f5560e92c) - Bump @types/node from 20.3.0 to 20.3.1 [`5cbfa4f`](https://github.com/ebullient/obsidian-task-collector/commit/5cbfa4f8576514f325de0051dbdc0c2dbfa92f4c) - Bump @codemirror/view from 6.13.0 to 6.13.2 [`54a3422`](https://github.com/ebullient/obsidian-task-collector/commit/54a3422e386384535a2b5f2d4b0dba9feb18ae03) - Bump @types/codemirror from 5.60.7 to 5.60.8 [`84bcaaf`](https://github.com/ebullient/obsidian-task-collector/commit/84bcaafd7ac0ee480797c47a74bf77c298a1697a) - Bump @types/node from 20.2.5 to 20.3.0 [`b5b46ac`](https://github.com/ebullient/obsidian-task-collector/commit/b5b46acfcc24e1873e90a5e45391cf87596d50d1) - Bump prettier from 2.8.8 to 3.0.0 [`c4a2497`](https://github.com/ebullient/obsidian-task-collector/commit/c4a249795f456661f0a8a5ed1cc2bd8f28076a1f) - Bump eslint from 8.44.0 to 8.45.0 [`d8d1c39`](https://github.com/ebullient/obsidian-task-collector/commit/d8d1c39d7d8a4ec6097a450964671de3c8cb3980) - Bump typescript from 4.9.5 to 5.1.6 [`005e08c`](https://github.com/ebullient/obsidian-task-collector/commit/005e08c1fb937de4a134f402ce8fe3caa13d482e) - Bump ts-jest from 29.1.0 to 29.1.1 [`eac6860`](https://github.com/ebullient/obsidian-task-collector/commit/eac68604e8409a95135b522ed00b540909a08b0c) - Bump @types/node from 20.4.4 to 20.5.0 [`9cc4408`](https://github.com/ebullient/obsidian-task-collector/commit/9cc44086085c8a27ca0a8340c14e7704fe880acd) - Bump @codemirror/view from 6.15.3 to 6.16.0 [`068fb33`](https://github.com/ebullient/obsidian-task-collector/commit/068fb3382529dbc027451db7d45e810e9fc33039) - Bump prettier from 3.0.0 to 3.0.1 [`a56477d`](https://github.com/ebullient/obsidian-task-collector/commit/a56477d055a4ed6545fb40816688f975d1cce4bb) - Bump tslib from 2.6.0 to 2.6.1 [`3ed7007`](https://github.com/ebullient/obsidian-task-collector/commit/3ed70077d1e80afcad951b9557e9d83992a24ee8) - Bump @codemirror/view from 6.14.1 to 6.15.3 [`951e5cc`](https://github.com/ebullient/obsidian-task-collector/commit/951e5cc499c2289fe705ce0b1a48cd5ba77e5102) - Bump @types/node from 20.4.2 to 20.4.4 [`390094f`](https://github.com/ebullient/obsidian-task-collector/commit/390094f7b29626f5cb90efc4a2a112814fad0ae7) - Bump @types/node from 20.4.1 to 20.4.2 [`036b93d`](https://github.com/ebullient/obsidian-task-collector/commit/036b93d138d1888511c617834d4a8621901308e8) - Bump @types/jest from 29.5.2 to 29.5.3 [`da4c455`](https://github.com/ebullient/obsidian-task-collector/commit/da4c4559411c58de4d4a27f98e587cb6604a82a8) - Bump @types/node from 20.3.3 to 20.4.1 [`045ef91`](https://github.com/ebullient/obsidian-task-collector/commit/045ef910fff6085f53f0c1ec18c52dfea1a985c3) - Bump @codemirror/view from 6.14.0 to 6.14.1 [`5960823`](https://github.com/ebullient/obsidian-task-collector/commit/596082368067665272d75dd48a94b01239296d0d) - Bump @types/node from 20.3.2 to 20.3.3 [`dd1b3bd`](https://github.com/ebullient/obsidian-task-collector/commit/dd1b3bdfa0edaf4757f0555923afe4cb85304b9e) - Bump tslib from 2.5.3 to 2.6.0 [`2a536f0`](https://github.com/ebullient/obsidian-task-collector/commit/2a536f08972afa44392f298e289471e6f2f012db) - Bump word-wrap from 1.2.3 to 1.2.4 [`2f5dbce`](https://github.com/ebullient/obsidian-task-collector/commit/2f5dbcee7679e43a211e3f9bc4a4d3870c0f4393) #### [1.0.11](https://github.com/ebullient/obsidian-task-collector/compare/1.0.10...1.0.11) > 6 June 2023 - 🐛 Support numbered tasks. Fixes [`#212`](https://github.com/ebullient/obsidian-task-collector/issues/212) - Bump esbuild from 0.17.18 to 0.17.19 [`5fd1963`](https://github.com/ebullient/obsidian-task-collector/commit/5fd1963c6f113d840f5e00685f229b1e216f4e55) - Bump esbuild from 0.17.17 to 0.17.18 [`3b0e8fb`](https://github.com/ebullient/obsidian-task-collector/commit/3b0e8fb187f101e908062d3e3903a10b63c6959b) - Bump @typescript-eslint/eslint-plugin from 5.59.6 to 5.59.7 [`8d03001`](https://github.com/ebullient/obsidian-task-collector/commit/8d0300122e87a8925e7608d2876468df405e7ceb) - Bump @typescript-eslint/parser from 5.59.5 to 5.59.6 [`c2c8f4b`](https://github.com/ebullient/obsidian-task-collector/commit/c2c8f4b50c91692bf5f3d1694dc8f288527fd1a0) - Bump @typescript-eslint/eslint-plugin from 5.59.7 to 5.59.8 [`f3e096d`](https://github.com/ebullient/obsidian-task-collector/commit/f3e096dd4a6d4d1d2e40e6da5458bbe450fece49) - Bump @typescript-eslint/eslint-plugin from 5.59.1 to 5.59.2 [`9f44e17`](https://github.com/ebullient/obsidian-task-collector/commit/9f44e17befb316c772499c4f125ce7caadfe73d5) - Bump @typescript-eslint/eslint-plugin from 5.59.2 to 5.59.6 [`5cbb3bf`](https://github.com/ebullient/obsidian-task-collector/commit/5cbb3bf37e293fab199232d4ecf655b94600fbf1) - Bump @typescript-eslint/parser from 5.59.0 to 5.59.1 [`20a0eb8`](https://github.com/ebullient/obsidian-task-collector/commit/20a0eb834f957d43c787ab625edf71f9bbb5932a) - Bump @typescript-eslint/parser from 5.58.0 to 5.59.0 [`efdb28a`](https://github.com/ebullient/obsidian-task-collector/commit/efdb28a178f0dfb91e31a583a6c10ebfab979eb1) - Bump @typescript-eslint/eslint-plugin from 5.58.0 to 5.59.1 [`e329a87`](https://github.com/ebullient/obsidian-task-collector/commit/e329a876922dabad997f77da131e29020268d2b6) - Bump eslint from 8.39.0 to 8.41.0 [`b228df9`](https://github.com/ebullient/obsidian-task-collector/commit/b228df9fcba4e94a18fe934383400c6c19c76980) - Bump @typescript-eslint/parser from 5.59.6 to 5.59.8 [`8128162`](https://github.com/ebullient/obsidian-task-collector/commit/8128162dc118e601eaa7f46e59d8df3c09c6c350) - Bump @typescript-eslint/parser from 5.59.1 to 5.59.5 [`6f8bfa9`](https://github.com/ebullient/obsidian-task-collector/commit/6f8bfa968065afe96e849ce3d34ab9f75b290f4f) - Bump eslint from 8.41.0 to 8.42.0 [`bdaebd6`](https://github.com/ebullient/obsidian-task-collector/commit/bdaebd6da603cc3710e1170eea72cf891dee263f) - Bump eslint from 8.38.0 to 8.39.0 [`ddec5c6`](https://github.com/ebullient/obsidian-task-collector/commit/ddec5c6dbe51598b3024d1b4dab1905acb8f2a04) - Bump @types/codemirror from 0.0.108 to 5.60.7 [`3da028b`](https://github.com/ebullient/obsidian-task-collector/commit/3da028b68ed2b94934506c872b96ec917a85bb05) - Bump esbuild-sass-plugin from 2.8.0 to 2.9.0 [`7f3c440`](https://github.com/ebullient/obsidian-task-collector/commit/7f3c4408a8037f5b67362fdb3b3a6c5a7c1e3a34) - Bump dotenv from 16.0.3 to 16.1.4 [`2a74ba8`](https://github.com/ebullient/obsidian-task-collector/commit/2a74ba87c3bbf5ccc90c773f1372f0c71d96c2af) - Bump @codemirror/view from 6.12.0 to 6.13.0 [`d22404e`](https://github.com/ebullient/obsidian-task-collector/commit/d22404e3c43bcbd69ac1e73c82284240f2ad29c8) - Bump tslib from 2.5.2 to 2.5.3 [`21c1abc`](https://github.com/ebullient/obsidian-task-collector/commit/21c1abc6b85383fe45c087bce8e70a93c998af44) - Bump @types/node from 20.2.3 to 20.2.5 [`84126f8`](https://github.com/ebullient/obsidian-task-collector/commit/84126f889de78c29d66d8cf9ba1da542570c76d5) - Bump @codemirror/state from 6.2.0 to 6.2.1 [`08a18c0`](https://github.com/ebullient/obsidian-task-collector/commit/08a18c02aebb012cadfc6ed5b3df74f8a27c2df5) - Bump tslib from 2.5.0 to 2.5.2 [`624a5e7`](https://github.com/ebullient/obsidian-task-collector/commit/624a5e760ca7d6c73a7d973d49cecadf5cdf8c09) - Bump @types/node from 20.1.4 to 20.2.3 [`beab965`](https://github.com/ebullient/obsidian-task-collector/commit/beab9656f38d91d0b4f75a122fa542ac8d6c75c7) - Bump @codemirror/view from 6.11.2 to 6.12.0 [`22122a4`](https://github.com/ebullient/obsidian-task-collector/commit/22122a476be197aec42b44797162581a02b97441) - Bump @codemirror/view from 6.11.0 to 6.11.2 [`8e9bc76`](https://github.com/ebullient/obsidian-task-collector/commit/8e9bc7648d74823283165b638b91885785f30d7e) - Bump @types/node from 20.1.0 to 20.1.4 [`5e13965`](https://github.com/ebullient/obsidian-task-collector/commit/5e1396508d68f0ec3a55d926b8e41dd2fecd3d4f) - Bump obsidian from 1.2.5 to 1.2.8 [`7b901b0`](https://github.com/ebullient/obsidian-task-collector/commit/7b901b09496e07061eae868a0c0088e475387eea) - Bump @codemirror/view from 6.9.5 to 6.11.0 [`49b56ec`](https://github.com/ebullient/obsidian-task-collector/commit/49b56ec7c43891966b0aef686a4f41ab57b1ca87) - Bump @types/node from 18.16.3 to 20.1.0 [`2fb09aa`](https://github.com/ebullient/obsidian-task-collector/commit/2fb09aa298764b64dccf75949508fc12fee208aa) - Bump @types/node from 18.15.11 to 18.16.3 [`6629d38`](https://github.com/ebullient/obsidian-task-collector/commit/6629d386de60a9476cac6d66917050ae35cccb1d) - Bump obsidian from 1.2.3 to 1.2.5 [`f16d791`](https://github.com/ebullient/obsidian-task-collector/commit/f16d791ecade80efcdc246d3812a876a5e305f3d) - Bump prettier from 2.8.7 to 2.8.8 [`ccfec8e`](https://github.com/ebullient/obsidian-task-collector/commit/ccfec8ea68f6b659c49741f3b0b0a02cf50ecc0d) #### [1.0.10](https://github.com/ebullient/obsidian-task-collector/compare/1.0.9...1.0.10) > 19 April 2023 - ⚡️ Stabilize click handling in LP mode [`f5aa44f`](https://github.com/ebullient/obsidian-task-collector/commit/f5aa44f902510b0d869f27d5c471f003e1ae19ab) - 📝 Update readme. [`9f032c6`](https://github.com/ebullient/obsidian-task-collector/commit/9f032c62dce732ab81dbc7c9339c9ca777828cf3) #### [1.0.9](https://github.com/ebullient/obsidian-task-collector/compare/1.0.8...1.0.9) > 18 April 2023 - ✨ Modal pop-up on click in Live Preview [`f3d92a0`](https://github.com/ebullient/obsidian-task-collector/commit/f3d92a010a4c693c47552aa693e27ee1df80e186) #### [1.0.8](https://github.com/ebullient/obsidian-task-collector/compare/1.0.7...1.0.8) > 18 April 2023 - 🐛 Fix Mark with Prev/Next commands. Fixes [`#178`](https://github.com/ebullient/obsidian-task-collector/issues/178) - Bump esbuild from 0.17.16 to 0.17.17 [`36429d0`](https://github.com/ebullient/obsidian-task-collector/commit/36429d0ebe005b510178cd41f5df8d4105d49094) - Bump obsidian from 1.1.1 to 1.2.3 [`951e0a4`](https://github.com/ebullient/obsidian-task-collector/commit/951e0a4642b04b08092f45856396d5cffae47da5) #### [1.0.7](https://github.com/ebullient/obsidian-task-collector/compare/1.0.6...1.0.7) > 16 April 2023 - 🐛 ✨ Reset All command, Fix context menus and line continuation [`#115`](https://github.com/ebullient/obsidian-task-collector/issues/115) [`#114`](https://github.com/ebullient/obsidian-task-collector/issues/114) [`#162`](https://github.com/ebullient/obsidian-task-collector/issues/162) [`#161`](https://github.com/ebullient/obsidian-task-collector/issues/161) #### [1.0.6](https://github.com/ebullient/obsidian-task-collector/compare/1.0.5...1.0.6) > 16 April 2023 - Bump esbuild from 0.16.16 to 0.17.16 [`3abf377`](https://github.com/ebullient/obsidian-task-collector/commit/3abf377e0fade29e5373d4abddf1fd8ce025d604) - Bump @typescript-eslint/parser from 5.55.0 to 5.57.0 [`825d612`](https://github.com/ebullient/obsidian-task-collector/commit/825d612d8ac7ab0158e7f007c8f4f7accbf24b46) - Bump @typescript-eslint/parser from 5.54.0 to 5.54.1 [`7b1bab1`](https://github.com/ebullient/obsidian-task-collector/commit/7b1bab16729d216e1fb98e5c1f91e391d45c60e7) - Bump @typescript-eslint/parser from 5.49.0 to 5.50.0 [`7f38a41`](https://github.com/ebullient/obsidian-task-collector/commit/7f38a41e1dd2a7ef42f7ceea19900dda6245e237) - Bump @typescript-eslint/eslint-plugin from 5.49.0 to 5.50.0 [`c5c5107`](https://github.com/ebullient/obsidian-task-collector/commit/c5c5107b721236b04d9e5733646fc47d1e93a190) - Bump @typescript-eslint/eslint-plugin from 5.57.0 to 5.57.1 [`1f3bbaa`](https://github.com/ebullient/obsidian-task-collector/commit/1f3bbaad4528be5d53f69d6771e29ca5e07e0a56) - Bump @typescript-eslint/eslint-plugin from 5.55.0 to 5.57.0 [`37b68c8`](https://github.com/ebullient/obsidian-task-collector/commit/37b68c80506c1ec35e6b4a2191e9feef667ab12d) - Bump @typescript-eslint/eslint-plugin from 5.54.0 to 5.54.1 [`c91f1c5`](https://github.com/ebullient/obsidian-task-collector/commit/c91f1c545408918a8c4954cc4e739895fd228598) - Bump @typescript-eslint/eslint-plugin from 5.52.0 to 5.53.0 [`27b4ef6`](https://github.com/ebullient/obsidian-task-collector/commit/27b4ef6ff877ede7611563cbb61b4dacfbb5ac49) - Bump @typescript-eslint/eslint-plugin from 5.51.0 to 5.52.0 [`0e9e383`](https://github.com/ebullient/obsidian-task-collector/commit/0e9e383530be87e46e41f89a6137a73fb5ae37f8) - Bump @typescript-eslint/eslint-plugin from 5.54.1 to 5.55.0 [`d24ae1c`](https://github.com/ebullient/obsidian-task-collector/commit/d24ae1cffbf9b9d4ab81ef86985cdbe0d7b18f6a) - Bump @typescript-eslint/eslint-plugin from 5.53.0 to 5.54.0 [`a34f46c`](https://github.com/ebullient/obsidian-task-collector/commit/a34f46cc273bdc42f704cdd5fe9d54ae4e329bee) - Bump @typescript-eslint/eslint-plugin from 5.50.0 to 5.51.0 [`e2a935b`](https://github.com/ebullient/obsidian-task-collector/commit/e2a935be6a3cd01a050166eecabe01f0360d2fc7) - ✅ Test mark cycles; update task collection tests [`c5b6ba0`](https://github.com/ebullient/obsidian-task-collector/commit/c5b6ba00f7c42f7f11893a84b909bab45d3e041f) - Bump @typescript-eslint/parser from 5.54.1 to 5.55.0 [`14cfa73`](https://github.com/ebullient/obsidian-task-collector/commit/14cfa7397867ffc42e7f4a79110df96a64b66787) - Bump @typescript-eslint/parser from 5.53.0 to 5.54.0 [`a9f4a26`](https://github.com/ebullient/obsidian-task-collector/commit/a9f4a267b17e05bfe8f5b654984e29433b375609) - Bump @typescript-eslint/parser from 5.50.0 to 5.51.0 [`103026d`](https://github.com/ebullient/obsidian-task-collector/commit/103026d8e4b271ceb5f5094c46823c9eb0634d5d) - Bump eslint from 8.35.0 to 8.36.0 [`707c1da`](https://github.com/ebullient/obsidian-task-collector/commit/707c1daba48800c273129e1d0d137b3521dcd13b) - Bump @typescript-eslint/parser from 5.57.0 to 5.58.0 [`f1cc02b`](https://github.com/ebullient/obsidian-task-collector/commit/f1cc02b0660adaf59f6d14443245cf71ebc4f162) - Bump @typescript-eslint/parser from 5.51.0 to 5.53.0 [`43e4632`](https://github.com/ebullient/obsidian-task-collector/commit/43e4632392085851224ea79cb7b2412f51ec2f42) - Bump eslint from 8.36.0 to 8.37.0 [`ef351f2`](https://github.com/ebullient/obsidian-task-collector/commit/ef351f2e024ae65d69e221029c77b266c7dfd9a5) - Bump eslint from 8.34.0 to 8.35.0 [`c524eb4`](https://github.com/ebullient/obsidian-task-collector/commit/c524eb46bda59e0946a15e74d59c9830d6282dd7) - Bump eslint from 8.37.0 to 8.38.0 [`2123396`](https://github.com/ebullient/obsidian-task-collector/commit/2123396f9046cd0bb487e464cc9b764f2a3dda95) - Bump @types/node from 18.15.10 to 18.15.11 [`e36ab8e`](https://github.com/ebullient/obsidian-task-collector/commit/e36ab8e7be8637bcb51a43e8c53d479318f08572) - Bump prettier from 2.8.5 to 2.8.7 [`d48e64c`](https://github.com/ebullient/obsidian-task-collector/commit/d48e64c34f5f2d8cb4e1e9b99c09836861fceeb7) - Bump @types/node from 18.15.3 to 18.15.10 [`a5db799`](https://github.com/ebullient/obsidian-task-collector/commit/a5db7998753f794ba3060ff8ed72a8991f10ef00) - Bump @types/node from 18.15.1 to 18.15.3 [`28a9701`](https://github.com/ebullient/obsidian-task-collector/commit/28a97016ad02fc845b03e68cabcce86003785ebb) - Bump prettier from 2.8.4 to 2.8.5 [`007a76b`](https://github.com/ebullient/obsidian-task-collector/commit/007a76b4c03e70976a14a9a9326869720c709a33) - Bump @types/node from 18.14.6 to 18.15.1 [`a46f479`](https://github.com/ebullient/obsidian-task-collector/commit/a46f4797ea39a8e044c66f6becceb6d14f292c88) - Bump @types/node from 18.14.2 to 18.14.6 [`dd78ef3`](https://github.com/ebullient/obsidian-task-collector/commit/dd78ef3d2308c817ccf0287f8a986fe9e6fc656f) - Bump @types/node from 18.14.0 to 18.14.2 [`88314fb`](https://github.com/ebullient/obsidian-task-collector/commit/88314fb6f3061f94466be1e5874d123f2a864317) - Bump @types/node from 18.13.0 to 18.14.0 [`f8217e9`](https://github.com/ebullient/obsidian-task-collector/commit/f8217e9b8812bd7d0f2d346d702884e9bfccdc26) - Bump eslint from 8.33.0 to 8.34.0 [`98e1374`](https://github.com/ebullient/obsidian-task-collector/commit/98e1374342fb56efc4089df22190887744a0ed8e) - Bump prettier from 2.8.3 to 2.8.4 [`b6fd16a`](https://github.com/ebullient/obsidian-task-collector/commit/b6fd16a1e634b6057e32a370e97a9f1de1a771af) - Bump @types/node from 18.11.19 to 18.13.0 [`ab6ce69`](https://github.com/ebullient/obsidian-task-collector/commit/ab6ce6915909769778c79f30f66170dae018f08e) - Bump @types/node from 18.11.18 to 18.11.19 [`16b632e`](https://github.com/ebullient/obsidian-task-collector/commit/16b632ea08c5f70e7a384d2c53b143371f163beb) - Bump typescript from 4.9.4 to 4.9.5 [`31b88f8`](https://github.com/ebullient/obsidian-task-collector/commit/31b88f8348cc906aedb9c62af523cb098828985b) #### [1.0.5](https://github.com/ebullient/obsidian-task-collector/compare/1.0.4...1.0.5) > 3 February 2023 #### [1.0.4](https://github.com/ebullient/obsidian-task-collector/compare/1.0.3...1.0.4) > 3 February 2023 - ✨ Feat: Mark nonlist items via settings #113 [`#122`](https://github.com/ebullient/obsidian-task-collector/pull/122) - Bump @typescript-eslint/parser from 5.48.0 to 5.48.1 [`3789ce5`](https://github.com/ebullient/obsidian-task-collector/commit/3789ce5a24e755d0f7620ee3ea2fb1773e82b9dd) - Bump @typescript-eslint/eslint-plugin from 5.48.2 to 5.49.0 [`03ad9d2`](https://github.com/ebullient/obsidian-task-collector/commit/03ad9d2732f8d97f03be7a6adceb5662d29f5322) - Bump @typescript-eslint/eslint-plugin from 5.48.1 to 5.48.2 [`9b1dadb`](https://github.com/ebullient/obsidian-task-collector/commit/9b1dadb0f00728c5e441fe860f74eb54a8577ad7) - Bump @typescript-eslint/eslint-plugin from 5.48.0 to 5.48.1 [`6f00ee7`](https://github.com/ebullient/obsidian-task-collector/commit/6f00ee7edc432332e6763cab0159aad890f84f3f) - Bump @typescript-eslint/parser from 5.48.1 to 5.49.0 [`54e73d4`](https://github.com/ebullient/obsidian-task-collector/commit/54e73d473d8b40c66cf3643eacd4a2b38a855268) - 🐛 Fix iteration of task sections [`64e44c1`](https://github.com/ebullient/obsidian-task-collector/commit/64e44c10f201610213ffb70a549d8ac8c759f912) - Bump tslib from 2.4.1 to 2.5.0 [`ba503e9`](https://github.com/ebullient/obsidian-task-collector/commit/ba503e90efa32f3816d7c7f128d717f50513846f) - Bump eslint from 8.32.0 to 8.33.0 [`c35ef33`](https://github.com/ebullient/obsidian-task-collector/commit/c35ef33ba8ac0162def8351c3711f5ce56b83615) - Bump prettier from 2.8.2 to 2.8.3 [`9bcdc31`](https://github.com/ebullient/obsidian-task-collector/commit/9bcdc319f626af833d0983fb5ddbc4c70089c102) - Bump eslint from 8.31.0 to 8.32.0 [`37bc3b5`](https://github.com/ebullient/obsidian-task-collector/commit/37bc3b578b9e8fa933bc6c53bb0f90a829f2e7ac) - 📝 Update manifest description [`10c4be0`](https://github.com/ebullient/obsidian-task-collector/commit/10c4be02d1287c19ae4ddaad1d271ee2f56a5067) #### [1.0.3](https://github.com/ebullient/obsidian-task-collector/compare/1.0.2...1.0.3) > 15 January 2023 #### [1.0.2](https://github.com/ebullient/obsidian-task-collector/compare/1.0.1...1.0.2) > 13 January 2023 - 📝 Update docs and wording for settings [`f9e6702`](https://github.com/ebullient/obsidian-task-collector/commit/f9e6702149cb5bfb5a5557eb3732c2ff3710d8ad) - 🐛 Use `## Example` for Area heading placeholder [`e3c29b6`](https://github.com/ebullient/obsidian-task-collector/commit/e3c29b661041ebbef0107a01dc7eaa841cd3383a) #### [1.0.1](https://github.com/ebullient/obsidian-task-collector/compare/1.0.0...1.0.1) > 12 January 2023 - 🐛 Restore cursor/selection. Fixes [`#62`](https://github.com/ebullient/obsidian-task-collector/issues/62) - ♻️ 🐛 ✨ Cycle through marks; clean up icons [`2dd60e0`](https://github.com/ebullient/obsidian-task-collector/commit/2dd60e03e0e7143fee48ae0194813938e352dfe4) - ♻️ 🐛 Debounce settings; clean up menus and debug [`3566e05`](https://github.com/ebullient/obsidian-task-collector/commit/3566e054bea797650938696f28dfb0c80742ccc4) ### [1.0.0](https://github.com/ebullient/obsidian-task-collector/compare/0.8.2...1.0.0) > 11 January 2023 - 🔥 ♻️ ✨ Group-oriented append/remove settings [`fb91979`](https://github.com/ebullient/obsidian-task-collector/commit/fb91979b32596720131230d6ca3c4a64d9edfe90) - Bump esbuild from 0.16.12 to 0.16.16 [`af8e537`](https://github.com/ebullient/obsidian-task-collector/commit/af8e5374dc69eaba8ba3c414e5008f354b8d9bb2) - Bump prettier from 2.8.1 to 2.8.2 [`b1848cf`](https://github.com/ebullient/obsidian-task-collector/commit/b1848cfc1d933f6b5320360d6160a6bb3e87a0b2) #### [0.8.2](https://github.com/ebullient/obsidian-task-collector/compare/0.8.1...0.8.2) > 8 January 2023 - 🐛 Fix construction of completion expressions. Resolves [`#99`](https://github.com/ebullient/obsidian-task-collector/issues/99) - Bump esbuild from 0.15.18 to 0.16.4 [`3a007d8`](https://github.com/ebullient/obsidian-task-collector/commit/3a007d8ba8bb6614f03abdb0421474e92ec143a1) - Bump esbuild from 0.13.12 to 0.15.13 [`da1d98e`](https://github.com/ebullient/obsidian-task-collector/commit/da1d98e5c370118814b79d7a8c53a04dca94aa26) - Bump esbuild from 0.16.10 to 0.16.12 [`0bf9343`](https://github.com/ebullient/obsidian-task-collector/commit/0bf9343bdc81995e3060f0520de3274fe15a98c5) - Bump esbuild from 0.16.9 to 0.16.10 [`2bdbab5`](https://github.com/ebullient/obsidian-task-collector/commit/2bdbab55c52f7405b5c038892bd132e5412abcea) - Bump esbuild from 0.16.4 to 0.16.9 [`5a81ccb`](https://github.com/ebullient/obsidian-task-collector/commit/5a81ccb28b49dada1717aa2fb1a052c80a28f3fb) - Bump esbuild from 0.15.16 to 0.15.18 [`354d5b7`](https://github.com/ebullient/obsidian-task-collector/commit/354d5b71d928cf23d744ec7520113d7d9ccdd4e7) - Bump esbuild from 0.15.15 to 0.15.16 [`5659ace`](https://github.com/ebullient/obsidian-task-collector/commit/5659ace66511a7690ad9a1ca2d575635ce874ea8) - Bump esbuild from 0.15.13 to 0.15.15 [`2046bdd`](https://github.com/ebullient/obsidian-task-collector/commit/2046bdde108f4a5cf77c82e66c1f480662941f54) - Bump obsidian from 0.12.17 to 0.16.3 [`da46561`](https://github.com/ebullient/obsidian-task-collector/commit/da46561a6a080d4843f6ee2c8ac6f02ad2d56140) - Bump moment from 2.29.1 to 2.29.4 [`0458bb4`](https://github.com/ebullient/obsidian-task-collector/commit/0458bb4ba2a1496ae50463909cdb998c86507b20) - Bump json5 from 2.2.0 to 2.2.3 [`9d2d220`](https://github.com/ebullient/obsidian-task-collector/commit/9d2d220265d9eb56b7a5c2b9745ddaf9d3235718) - Bump node-fetch from 2.6.6 to 2.6.7 [`d06b5ac`](https://github.com/ebullient/obsidian-task-collector/commit/d06b5acf4a7780f9c7620b1399385bb508b1991d) - Bump prettier from 2.5.1 to 2.7.1 [`e761f60`](https://github.com/ebullient/obsidian-task-collector/commit/e761f60e2c89a96df99adbb21b0f0c15d4b8b2ad) - Bump minimist from 1.2.5 to 1.2.7 [`fab58c3`](https://github.com/ebullient/obsidian-task-collector/commit/fab58c3f21ba9214d702fb439053c1adafd57234) - Bump @types/node from 18.11.17 to 18.11.18 [`4abb8a9`](https://github.com/ebullient/obsidian-task-collector/commit/4abb8a95d43205f9f1ee68d90abd97095c2ed242) - Bump obsidian from 0.16.3 to 1.1.1 [`5ab8339`](https://github.com/ebullient/obsidian-task-collector/commit/5ab8339a231e34ff797effb6c0712387c4f2ea85) - Bump @types/node from 18.11.13 to 18.11.17 [`3604a06`](https://github.com/ebullient/obsidian-task-collector/commit/3604a0650ee3ff98deef8f2192dec798b4e523f7) - Bump typescript from 4.9.3 to 4.9.4 [`5ea8210`](https://github.com/ebullient/obsidian-task-collector/commit/5ea8210f0b9c867a6ccd2be5031c9f0d924ed1b2) - Bump @types/node from 18.11.10 to 18.11.13 [`23c76d3`](https://github.com/ebullient/obsidian-task-collector/commit/23c76d33190bd4a3f817d6710a80409726b2fbc5) - Bump prettier from 2.8.0 to 2.8.1 [`15f0306`](https://github.com/ebullient/obsidian-task-collector/commit/15f03066e61e01132a77d3b15f0d594ebd329867) - Bump @types/node from 18.11.9 to 18.11.10 [`a2ff35c`](https://github.com/ebullient/obsidian-task-collector/commit/a2ff35c6928eadcde4e05bc74b6f2197cada436b) - Bump prettier from 2.7.1 to 2.8.0 [`0374c1d`](https://github.com/ebullient/obsidian-task-collector/commit/0374c1dbd6c77f49c78bad857c39010b0041bed0) - Bump typescript from 4.8.4 to 4.9.3 [`ada95a5`](https://github.com/ebullient/obsidian-task-collector/commit/ada95a59e9d286e5379078e5f1fe17bf68d42b7a) - Bump builtin-modules from 3.2.0 to 3.3.0 [`7d4f4b2`](https://github.com/ebullient/obsidian-task-collector/commit/7d4f4b2ee0d5a27d219e8c405d565c964c8eea96) - Bump tslib from 2.3.1 to 2.4.1 [`9d40d36`](https://github.com/ebullient/obsidian-task-collector/commit/9d40d36b146edea96f4f47da23aa04d99c515c8b) - Bump @types/node from 14.17.21 to 18.11.9 [`b09b0f1`](https://github.com/ebullient/obsidian-task-collector/commit/b09b0f1b7995f9281e5df99e4f1fd0aaee8b903b) - Bump typescript from 4.4.4 to 4.8.4 [`e0b3533`](https://github.com/ebullient/obsidian-task-collector/commit/e0b353378505bfc8645c7fd5586ac11754f5001a) - 🔧 GH Actions dependencies [`f65120c`](https://github.com/ebullient/obsidian-task-collector/commit/f65120cfe0abcea7aeb772392ca266eabd251827) - Bump minimatch from 3.0.4 to 3.1.2 [`3b117c4`](https://github.com/ebullient/obsidian-task-collector/commit/3b117c43ee3bb296d80dec25f6bbd7b0df5f56b3) - 🔧 Dependabot updates [`2202b32`](https://github.com/ebullient/obsidian-task-collector/commit/2202b32e7ac9d3bf106bae4b398cbed53c8a889c) - 🐛 Fix checking sub-items in preview mode [`a55090f`](https://github.com/ebullient/obsidian-task-collector/commit/a55090f2907c2cb42857b91afd1f7084f8c40746) #### [0.8.1](https://github.com/ebullient/obsidian-task-collector/compare/0.8.0...0.8.1) > 9 September 2022 - 🎨 icon size for 0.16 [`a9f6c7d`](https://github.com/ebullient/obsidian-task-collector/commit/a9f6c7d4f7e40939661465eecf08b922039986e4) #### [0.8.0](https://github.com/ebullient/obsidian-task-collector/compare/0.7.8...0.8.0) > 27 April 2022 - ✨ ♻️ Support append/reset for all marks [`8631974`](https://github.com/ebullient/obsidian-task-collector/commit/8631974fd1de1c3e8663d3d4a8b940d6f121fdbf) - ✨ Add setting: use modal on left-click of checkbox [`1666223`](https://github.com/ebullient/obsidian-task-collector/commit/16662231a29f67e5388bb55cd7d6c65744e5d6e0) - 🎨 Define API: query task markers, invoke the modal. [`d8ae11a`](https://github.com/ebullient/obsidian-task-collector/commit/d8ae11ae42cccbe7449cc4867c74df064c562790) #### [0.7.8](https://github.com/ebullient/obsidian-task-collector/compare/0.7.7...0.7.8) > 23 April 2022 - 🐛 Preserve strict line-ending spaces [`1e26920`](https://github.com/ebullient/obsidian-task-collector/commit/1e26920bb118fdee0e5c92789cf72b9670c436d0) - ✨ Mark tasks in callouts [`319239e`](https://github.com/ebullient/obsidian-task-collector/commit/319239efc9f01f0770802003a416af3d4c09bb23) - ✨ Allow 'X' to be disabled/hidden [`0986d39`](https://github.com/ebullient/obsidian-task-collector/commit/0986d396032168a0970f93f97b9e6d0a3dec1977) - 🐛 Pop-up modal spacing (again) [`b1a436b`](https://github.com/ebullient/obsidian-task-collector/commit/b1a436b7bba7d1fbe63299d021943b05643c25fc) #### [0.7.7](https://github.com/ebullient/obsidian-task-collector/compare/0.7.6...0.7.7) > 23 March 2022 - 🐛 Errant selection of line. Resolves [`#40`](https://github.com/ebullient/obsidian-task-collector/issues/40) - 🎨 whitespace [`8ab9b11`](https://github.com/ebullient/obsidian-task-collector/commit/8ab9b11716ce5172e602b8439796d92253716029) - Update li style to match changes to default theme [`196d5f1`](https://github.com/ebullient/obsidian-task-collector/commit/196d5f116fa7020b69593fc3635d2d3c0d0905a1) #### [0.7.6](https://github.com/ebullient/obsidian-task-collector/compare/0.7.5...0.7.6) > 1 February 2022 - 🐛 Use additional specifiers in pop-up [`880fa10`](https://github.com/ebullient/obsidian-task-collector/commit/880fa10e29b0b3a4a1cdfe69d1d074cd95bce823) #### [0.7.5](https://github.com/ebullient/obsidian-task-collector/compare/0.7.4...0.7.5) > 17 January 2022 - 🐛 Use a minimum config string [`f899275`](https://github.com/ebullient/obsidian-task-collector/commit/f899275d7a150221d292885ee4441b04cbb7ed4a) #### [0.7.4](https://github.com/ebullient/obsidian-task-collector/compare/0.7.3...0.7.4) > 17 January 2022 - 🐛 Cope with empty incomplete tasks setting [`76d85ef`](https://github.com/ebullient/obsidian-task-collector/commit/76d85ef62a2591dadc3de873ad1f3bc04e9151a8) #### [0.7.3](https://github.com/ebullient/obsidian-task-collector/compare/0.7.2...0.7.3) > 14 January 2022 - ✨ Use bksp to remove checkbox [`04dcaf8`](https://github.com/ebullient/obsidian-task-collector/commit/04dcaf8b0d842228eea8941eef12d8bba1ba3d60) #### [0.7.2](https://github.com/ebullient/obsidian-task-collector/compare/0.7.1...0.7.2) > 14 January 2022 - 🐛 revert change (read instead of cachedRead) [`b7e11d6`](https://github.com/ebullient/obsidian-task-collector/commit/b7e11d6924f00aa764616db76cb9788e70446f2e) #### [0.7.1](https://github.com/ebullient/obsidian-task-collector/compare/0.7.0...0.7.1) > 14 January 2022 - ✨ Convert and Mark ordinary list items [`36c15a0`](https://github.com/ebullient/obsidian-task-collector/commit/36c15a0845cd36d96c27864cc131469064e83e5f) #### [0.7.0](https://github.com/ebullient/obsidian-task-collector/compare/0.6.6...0.7.0) > 13 January 2022 - ✨ Complete tasks in preview mode [`80f9575`](https://github.com/ebullient/obsidian-task-collector/commit/80f95751eb7c358cde5b14163c50b84ce1906e93) - add prettier to npm scripts [`11561eb`](https://github.com/ebullient/obsidian-task-collector/commit/11561eb95c83c74de6f6fd64073c8af19b3048e2) - run prettier on plugin files [`1a64a26`](https://github.com/ebullient/obsidian-task-collector/commit/1a64a26db5d6dfd61be7115f972d03e27534e229) - ✅ Update tests; Mark either complete/incomplete [`3c8e576`](https://github.com/ebullient/obsidian-task-collector/commit/3c8e576e7a1a2f80e20c14c9f759086983571a6d) - 🔧 json whitespace [`d4a31bf`](https://github.com/ebullient/obsidian-task-collector/commit/d4a31bffe592cdd490c350f2d567716b897a5d2b) #### [0.6.6](https://github.com/ebullient/obsidian-task-collector/compare/0.6.5...0.6.6) > 10 January 2022 - 🐛 support block-ids with numbers 🤦‍♀️ [`19da48c`](https://github.com/ebullient/obsidian-task-collector/commit/19da48c0f85599f23a9957bb66b75ccdc74babc9) #### [0.6.5](https://github.com/ebullient/obsidian-task-collector/compare/0.6.4...0.6.5) > 9 January 2022 - 🚸 Rename/Reorder settings [`04d3f0b`](https://github.com/ebullient/obsidian-task-collector/commit/04d3f0b64a3a9bcf9253a21373425425330e36e7) #### [0.6.4](https://github.com/ebullient/obsidian-task-collector/compare/0.6.3...0.6.4) > 9 January 2022 - ✨💥 Modal to select item mark [`a38d683`](https://github.com/ebullient/obsidian-task-collector/commit/a38d683ecdcddc80f3e21508fc83d109ff6f598c) - 🚸 Preserve trailing block ids with completion date [`ab17500`](https://github.com/ebullient/obsidian-task-collector/commit/ab17500db96e9656387d3d240cc161a0c87d5a22) #### [0.6.3](https://github.com/ebullient/obsidian-task-collector/compare/0.6.2...0.6.3) > 7 January 2022 - Fix processing of date formats containing square brackets [`#18`](https://github.com/ebullient/obsidian-task-collector/pull/18) - feat: optional remove checkbox when moving tasks [`#14`](https://github.com/ebullient/obsidian-task-collector/pull/14) - ✨ Run commands that mark all tasks in reader mode [`205e2a1`](https://github.com/ebullient/obsidian-task-collector/commit/205e2a19a227d61194fefcad0a114d93db685df6) - 🐛 Handle moment formats containing [] [`145468b`](https://github.com/ebullient/obsidian-task-collector/commit/145468bfe2c2b1f66d9da7c406d805b3e57e7cb0) - 🚸 Revise setting descriptions [`8093632`](https://github.com/ebullient/obsidian-task-collector/commit/80936322be42d81614749edde682e8182f57b777) - 🚸 quieter logs [`005f81d`](https://github.com/ebullient/obsidian-task-collector/commit/005f81d5def3f3b23e4fca670dc40387fc37e8d8) #### [0.6.2](https://github.com/ebullient/obsidian-task-collector/compare/0.6.1...0.6.2) > 7 January 2022 - fix: recognize tab indentation for sub-bullets [`#12`](https://github.com/ebullient/obsidian-task-collector/pull/12) - Rename files; right-click menu behavior for reset [`#9`](https://github.com/ebullient/obsidian-task-collector/pull/9) - I make ridiculous things. ;) [`d91c38c`](https://github.com/ebullient/obsidian-task-collector/commit/d91c38c3d2f610fa5555337a4c5e334bd6a367d8) - fix: recognise tabs identation for sub-notes [`d57c2bf`](https://github.com/ebullient/obsidian-task-collector/commit/d57c2bff29c2bcaccea1a583ab38d389f9f32818) - Update FUNDING.yml [`f3cb183`](https://github.com/ebullient/obsidian-task-collector/commit/f3cb183b4221a8890a4912c1c3867a9bfa177e5e) #### [0.6.1](https://github.com/ebullient/obsidian-task-collector/compare/0.6.0...0.6.1) > 9 October 2021 - Mark/Reset all in selection [`#6`](https://github.com/ebullient/obsidian-task-collector/issues/6) #### [0.6.0](https://github.com/ebullient/obsidian-task-collector/compare/0.5.0...0.6.0) > 9 October 2021 - Mobile does not like negative lookbehind [`3e86da4`](https://github.com/ebullient/obsidian-task-collector/commit/3e86da423cf3023fc4f1d9f6b50d6620a11f1b9a) - Additional notes re: versions [`6d83c78`](https://github.com/ebullient/obsidian-task-collector/commit/6d83c78d38f5ddfbf50a69fa4cf38f588360b8d2) #### [0.5.0](https://github.com/ebullient/obsidian-task-collector/compare/0.4.0...0.5.0) > 7 October 2021 - Reset single task. Resolves [`#4`](https://github.com/ebullient/obsidian-task-collector/issues/4) - Parse hours and minutes; test updates; resolves [`#2`](https://github.com/ebullient/obsidian-task-collector/issues/2) - Release 0.5.0 with reset single task support [`5e3cd4b`](https://github.com/ebullient/obsidian-task-collector/commit/5e3cd4be134d6ef6f9ee705fbf545840f4ffb75c) - Add a note about icons [`f89cd2e`](https://github.com/ebullient/obsidian-task-collector/commit/f89cd2e312fa44b711e1785715dc6e4ca67ff5aa) #### [0.4.0](https://github.com/ebullient/obsidian-task-collector/compare/0.3.0...0.4.0) > 7 October 2021 - Tests [`8138adb`](https://github.com/ebullient/obsidian-task-collector/commit/8138adb44bb24b35a22b7f21780df8ca1148d660) - eslint [`a822c60`](https://github.com/ebullient/obsidian-task-collector/commit/a822c60385a81b02c7d2188418f4c6a95a81d192) - mark/clear all; context-menu icons; refactoring [`55c75dc`](https://github.com/ebullient/obsidian-task-collector/commit/55c75dc43e6c0624f31191d098179f3fc2c45150) - Release 0.4.0 [`94b0755`](https://github.com/ebullient/obsidian-task-collector/commit/94b0755005c749ce61dc5061d394630c0903a10a) - Readme + menu fix [`2c1de83`](https://github.com/ebullient/obsidian-task-collector/commit/2c1de8382998cab60a6745701e4d1d4faa44c9b8) - Clarifications [`492a751`](https://github.com/ebullient/obsidian-task-collector/commit/492a751eaf8bc5a263089f443f6be7cf19871fd9) - find missing dep for ci [`8d8540f`](https://github.com/ebullient/obsidian-task-collector/commit/8d8540f096d591da5271a7281816d8a1e6616cb7) - Add shields.io badges [`290bf15`](https://github.com/ebullient/obsidian-task-collector/commit/290bf150a89bceb2cc1890929a763f98c26a5c35) #### [0.3.0](https://github.com/ebullient/obsidian-task-collector/compare/0.2...0.3.0) > 29 September 2021 - Rename plugin; Right-click menu items [`3ca0a4d`](https://github.com/ebullient/obsidian-task-collector/commit/3ca0a4d9ca95261c372e9461c016ca600db4b823) - Add support for canceled items [`bf6c39c`](https://github.com/ebullient/obsidian-task-collector/commit/bf6c39c98010cd02336b2f0f853c5f67137eea6d) #### [0.2](https://github.com/ebullient/obsidian-task-collector/compare/0.1...0.2) > 29 September 2021 - add repository; commit package-lock [`d88a0c7`](https://github.com/ebullient/obsidian-task-collector/commit/d88a0c7ec867aa89ad6022792ebeb96ac6547f53) - Rename plugin; Right-click menu items [`91c4a97`](https://github.com/ebullient/obsidian-task-collector/commit/91c4a972514702f3b595ff7accd4ed345281d790) #### 0.1 > 29 September 2021 - Functional, but no tests [`5bb74f1`](https://github.com/ebullient/obsidian-task-collector/commit/5bb74f12f74ce1c2b07e203376e2e041c555c714) - Initial commit [`300d072`](https://github.com/ebullient/obsidian-task-collector/commit/300d072bf8801f451e8992c836479863a42a2e29) - Create LICENSE [`b4209d2`](https://github.com/ebullient/obsidian-task-collector/commit/b4209d281ef8667b1cd135950b1630acb492068c) - CI [`0c9fb18`](https://github.com/ebullient/obsidian-task-collector/commit/0c9fb184a3e6ba62a46e5ffd8f61d0ed1f930d8b) - Add note about behavior of nested elements [`441f176`](https://github.com/ebullient/obsidian-task-collector/commit/441f176788d4fb770b7ee53d6b4543dce8587d6f) ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021 Erin Schnabel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Task Collector [![GitHub tag (Latest by date)](https://img.shields.io/github/v/tag/ebullient/obsidian-task-collector)](https://github.com/ebullient/obsidian-task-collector/releases) ![GitHub all releases](https://img.shields.io/github/downloads/ebullient/obsidian-task-collector/total?color=success) Yet another plugin to manage completed tasks, but this one has a task-completion modal to go with it! - [Configuration reference](docs/README.md) - [Commands](#commands) - [Marking tasks](#marking-tasks) - [Task mark selection](#task-mark-selection) ## Installation 1. Go to **Community Plugins** in your [Obsidian](https://www.obsidian.md) settings and **disable** Safe Mode 2. Click on **Browse** and search for "task collector" 3. Click install 4. Use the toggle on the community plugins tab to enable the plugin. ## TL;DR for marking tasks 1. Open Plugin settings 2. Edit the task marks for the `default` group to include any characters you use for incomplete tasks. 3. Edit the task marks for the `complete` group to include any characters you use for completed tasks. 4. _(optional)_ Scroll down to find **[Menus and modals](docs/README.md#menus-and-modals]**, and enable additional context menus. ![Task Completion](https://user-images.githubusercontent.com/808713/148706433-34d21845-a441-428d-a24c-380c6db457c7.gif) ## Commands Task Collector registers a few commands by default: ### (TC) Mark task This is a hot-key bindable command for edit-mode that opens a modal dialog for [task mark selection](#task-mark-selection). That snappy completion status you wanted is just a few taps away! For Reading and Live Preview modes, see [menus and modals](docs/README.md#menus-and-modals). ### (TC) Collect tasks Task Collector can gather and regroup different kinds of tasks into different areas within a note. 1. [Enable "Task collection"](docs/README.md#general-options) 2. For the group of tasks that you would like to gather: - Set an Area heading See [Task collection](docs/TaskCollection.md) for details. ### '(TC) Mark previous' and '(TC) Mark previous' If you configure a [Task mark cycle](docs/README.md#general-options), two commands will be registered that allow you to cycle forward or backward through the mark sequence. ## Marking tasks When Task Collector marks a task: 1. _(optional)_ Matched text appended by the previous mark will be removed 2. The task will be marked with the new mark 3. _(optional)_ The remove pattern configured for the new mark will be applied to remove text from the task. 4. If there is an append date format, a formatted time stamp will be appended to the task. ## Task mark selection Use the `(TC) Mark task` command or right-click context menu (if enabled) to select a task mark using a quick pop-up modal. The modal contains marks configured in [task groups](docs/README.md#task-groups). - The top row contains marks for [completed tasks](#completed-tasks). - All other configured marks appear in the next row (or rows, as the collection will wrap). **Notes**: - Select a value with your mouse, or the keyboard. - The selected value will determine follow-on actions, see [marking tasks](#marking-tasks). - Any character you choose will work. If it doesn't match a configured mark, the behavior from the `default` group will apply. > [!TIP] > Is the pop-up not showing what you expect? Review the marks defined in your [task groups](docs/README.md#task-groups). --- ## Why `mark`? There are not enough words. I chose _mark_, because it can mean the symbol itself (a mark), and also active action (you mark the task). The checkbox is the outer thing. Sometimes these characters indicate the status of a task, and some folks use these to mean something else entirely. So, `mark` it is. Naming things is hard. ### Completed tasks Why is there is a toggle for completed tasks if these are all just marks? The complete toggle is essentially an indicator. - In the mark selection dialog, those used to complete tasks appear in the top row. All others are (sorted) in the bottom row(s). - Task Collector has an API that other plugins or your own scripts can use to determine if a mark indicates a complete task or not: `app.plugins.plugins["obsidian-task-collector"] --- ## Credits - [Completed Area Plugin](https://github.com/DahaWong/obsidian-completed) -- general premise of moving completed tasks to a different area within the note (delimited by a heading). - [JeppeKlitgaard/ObsidianTweaks](https://github.com/JeppeKlitgaard/ObsidianTweaks/) -- simple/clear event triggers - [ivan-lednev/obsidian-task-archiver](https://github.com/ivan-lednev/obsidian-task-archiver) -- Treatment of sub-elements - [Darakah/obsidian-timelines](https://github.com/Darakah/obsidian-timelines) -- Editor select/replace - [Customizable Sidebar](https://github.com/phibr0/obsidian-customizable-sidebar) -- GH Action - [Dataview](https://github.com/blacksmithgu/obsidian-dataview) -- Jest/Testing Buy Me A Coffee ================================================ FILE: biome.json ================================================ { "$schema": "https://biomejs.dev/schemas/2.0.4/schema.json", "vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": true }, "files": { "ignoreUnknown": false, "includes": ["src/**", "test/**", "!test/mocks/**"] }, "formatter": { "enabled": true, "indentWidth": 4, "useEditorconfig": true }, "assist": { "actions": { "source": { "organizeImports": "on" } } }, "linter": { "enabled": true, "rules": { "recommended": true, "style": { "noParameterAssign": "error", "useAsConstAssertion": "error", "useDefaultParameterLast": "error", "useEnumInitializers": "error", "useSelfClosingElements": "error", "useSingleVarDeclarator": "error", "noUnusedTemplateLiteral": "error", "useNumberNamespace": "error", "noInferrableTypes": "error", "noUselessElse": "error" } } }, "javascript": { "formatter": { "quoteStyle": "double" } } } ================================================ FILE: docs/AppendingText.md ================================================ # Append text anywhere You can define a [task group](README.md#task-groups) named `text` to create a string you can append to any line of text (not just tasks). This can be useful if you want to configure a quick append string that matches the date format you use for your tasks. Appending a date and removing matching text work in the same way for both tasks and general text, however, the appended date text is not removed if that line of text is later converted into a task. ================================================ FILE: docs/README.md ================================================ # Task Collector Configuration This doc will give a high level overview of how Task Collector works, and its core configuration elements. - [General Options](#general-options) - [Task Groups](#task-groups) - [Menus and modals](#menus-and-modals) For readability, we've broken some things into their own docs: - [Task Collection](TaskCollection.md) - [Appending text to lines (not tasks)](AppendingText.md) Other references: - [Why marks?](../README.md#why-mark) - [Task mark selection](../README.md#task-mark-selection) - [Marking tasks](../README.md#marking-tasks) --- ## General options - **Toggle: Task collection** This option enables [Task collection](TaskCollection.md). Additional settings will be available if this is enabled. - *default*: disabled - **Define task mark cycle** _(optional)_ Use this option to specify a string of marks that can be iterated sequentially using `(TC) Mark previous` and `(TC) Mark next` commands. - *default*: (empty string, disabled) - **Notes**: - `(TC) Mark previous` and `(TC) Mark next` are not registered unless or until a task mark cycle is defined. - **Toggle: Convert non-list lines** (✨ 1.0.4) Convert lines that are not tasks into tasks when you mark them. - *default*: disabled - **Skip matching sections** _(optional)_ (✨ 1.0.13) When collecting tasks, skip any sections that match the specified pattern. - *default*: (empty string, disabled) - **Notes**: - *Be careful!* Test your expression before using it. There are several [online](https://www.regextester.com/) [tools](https://regex.observepoint.com/) that can help. --- ## Task groups One or more task marks can be configured together in a group. - `default` is a special group that can not be deleted. Any mark that is not included in any other group uses the default group settings. - `text` is a special name used for [appending text](AppendingText.md). ### Task group configuration - **Group name** _(required)_ Each group should have a unique name. The names are significant only as an identifier. - **Toggle: Complete** Enable this toggle if this group of marks indicate [completed tasks](../README.md#completed-tasks). - **Task marks** _(required)_ Tasks are specified as a contiguous string: ` !/?r` - **Notes**: - A mark can only belong to one group. - A `space` is a valid character. If a space is present, `⎵` is drawn (CSS) as an eye-catcher. - The string will sort itself as you add characters to it. - **Append date to selected task(s)** _(optional)_ - *default*: (empty string, disabled) - *example*: `[(]YYYY-MM-DD[)]`, results in `(2021-09-27)` - **Notes**: - When a [moment.js date format](https://momentjs.com/docs/#/displaying/format/) is specified, the current date/time will be appended to the task text. - Use square brackets to surround content that is not part of the format string. - When working with dataview-friendly annotations, for example, your format string should look something like this: `[[completion::]YYYY-MM-DD[]]`. - See [marking tasks](../README.md#marking-tasks) - **Remove text matching pattern** Remove text matching this [regular expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) from the task text. - *default*: (empty string, disabled) - *example*: `#(task|todo)` (remove #task or #todo tags) - **Notes**: - *Be careful!* Test your expression before using it. There are several [online](https://www.regextester.com/) [tools](https://regex.observepoint.com/) that can help. - Aside from an immediate "undo", this is not a reversible operation. - See [marking tasks](../README.md#marking-tasks) - **Toggle: Register '(TC) Mark with... ' command** Register a command _for each mark in the group_. - *default*: `false` - **Notes**: - Commands can be bound to hot-keys... - **Toggle: Add '(TC) Mark with... ' menu item** Register a right-click context menu item _for each mark in the group_. - *default*: `false` - **Area heading** _(if [task collection](TaskCollection.md) is enabled)_ Matching marked items will be inserted under the specified heading (most recent at the top). - *default*: (empty string, disabled) - **Notes**: - The area heading must be defined to enable collection for the group. - See [task collection](TaskCollection.md) for details. - **Toggle: Remove checkbox** _(if [task collection](TaskCollection.md) is enabled)_ Remove the checkbox marked tasks during the move to the configured area. - *default*: false --- ## Menus and modals - **Toggle: Prompt on checkbox click in Reading or Live preview**: When true, the [Task mark selection dialog](../README.md#task-mark-selection) will be opened when you click on a checkbox in Reading or Live Preview mode. - *default*: `false` - **Notes**: - This may conflict with a similar handler from the [Tasks](https://obsidian-tasks-group.github.io/obsidian-tasks/) plugin. Use one or the other, not both. - **Toggle: Add '(TC) Mark task' menu item** (✨ 0.6.4) Add an item to the right-click menu in edit mode to mark the task _on the current line (or within the current selection)_. It will open the [Task mark selection dialog](../README.md#task-mark-selection). - *default*: `false` - **Toggle: Add '(TC) Collect tasks' menu item** _(if [task collection](docs/../TaskCollection.md) is enabled)_ Add an item to the right-click menu to collect tasks in the current note. - *default*: `false` ================================================ FILE: docs/TaskCollection.md ================================================ # Task collection Task collection works on the tasks within a note. Tasks that match a mark configured for collection will be gathered into the designated area of the note. The `(TC) Collect Tasks` command or menu item triggers the following process: 1. Any missing Area headings will be added to the end of the document. 2. The document will be scanned from top to bottom to isolate discovered Area headings and their target sections. 3. General note content is scanned from top to bottom for tasks that should be collected. Each discovered task is moved to a block of pending tasks associated with the closest matching Area header ([where do tasks go](#where-do-tasks-go)?). 4. Collection target sections are then scanned (in order of appearance in the doc) for tasks that belong in a different section. Those are then added to the block of pending tasks for the closest matching Area header. 5. Finally, the document is reassembled. Area headings and their target sections are placed back into the document, with the pending block for each appearing immediately under the section header (most recent will be first). If you walk through the above with the following example, you should end up with the tasks in the same order:
BeforeAfter

- [ ] i1
- [x] one
- [>] two

## To Do

## Log
- [ ] i2
- [x] three
- [>] four

## Deferred
- [ ] i3
- [x] five
- [>] six

## To Do
- [ ] i1
- [ ] i2
- [ ] i3

## Log
- [x] one
- [x] five
- [x] three

## Deferred
- [>] two
- [>] four
- [>] six
To test out this example: 1. Enable task collection 2. Set the Area header for the `default` group to `## To Do` 3. Set the Area header for the `complete` group to `## Log` 4. Create a new group, `deferred`, that supports one mark (`>`) and uses `## Deferred` as an area header. Put the initial text in a note, and run `(TC) Collect Tasks`. ## Where do tasks go You can have more than one heading of each type in the note. If there are multiple sections of the same name, tasks that are discovered will be moved to the next (following) matching section. If a section is not found between the task and the end of the note, the search resumes from the top. **Notes**: - Marked tasks will carry along their sub items (nested lists, text, or quotes). - If a marked item has an unmarked child task, the child (and any text following) *will remain in the original location*. ## Required configuration - Task collection must be [enabled](README.md#general-options) - An **Area heading** must be defined for a [task group](README.md#task-groups) Specifying an area heading enables collection for tasks that use any of the marks in that group. - A new heading will be created at the end of the note if it does not exist. - Tasks with marks matching this group between this header and the next header or `---` will be ignored when collecting tasks (as they are already where they belong). ## Optional configuration - **Remove checkbox** for a [task group](README.md#task-groups) Remove the checkbox marked tasks during the move to the configured area. This transforms tasks into normal list items. Task Collector will not be able to reset these items. They also will not appear in task searches or queries. ================================================ FILE: esbuild.config.mjs ================================================ import esbuild from "esbuild"; import process from "node:process"; import { builtinModules } from "node:module"; import { sassPlugin } from 'esbuild-sass-plugin' const banner = `/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD if you want to view the source, please visit the github repository of this plugin */ `; const prod = (process.argv[2] === 'production'); const dir = prod || !process.env.OUTDIR ? "./build" : process.env.OUTDIR; const parameters = { banner: { js: banner, }, entryPoints: ['src/main.ts', 'src/styles.scss'], bundle: true, external: [ 'obsidian', 'electron', "codemirror", '@codemirror/language', '@codemirror/state', '@codemirror/view', 'moment', ...builtinModules ], format: 'cjs', logLevel: 'info', target: 'es2020', treeShaking: true, sourcemap: prod ? false : 'inline', minify: prod, outdir: dir, plugins: [ sassPlugin() ] }; if (prod) { await esbuild.build(parameters).catch((x) => { if (x.errors) { console.error(x.errors); } else { console.error(x); } process.exit(1) }); } else { const ctx = await esbuild.context(parameters); await ctx.watch() } ================================================ FILE: eslint.config.mjs ================================================ // eslint.config.mjs import globals from "globals"; import tsparser from "@typescript-eslint/parser"; import { defineConfig, globalIgnores } from "eslint/config"; import obsidianmd from "eslint-plugin-obsidianmd"; export default defineConfig([ ...obsidianmd.configs.recommended, globalIgnores([ "test/", "*.js", "*.mjs", "package.json" ]), { files: ["src/**/*.ts"], languageOptions: { parser: tsparser, parserOptions: { project: "./tsconfig.json" }, globals: { ...globals.node, ...globals.browser }, }, // Optional project overrides rules: { "obsidianmd/ui/sentence-case": [ "warn", { brands: ["Task Collector", "xX", "YYYY-MM-DD", "Live Preview", "Obsidian", "Reading", " #(todo|task)"], acronyms: ["TC"], enforceCamelCaseLower: true, }, ], }, }, ]); ================================================ FILE: jest.config.js ================================================ module.exports = { preset: "ts-jest", testEnvironment: 'jsdom', moduleDirectories: ['node_modules', 'src', 'test'], moduleNameMapper: { "^moment-obsidian$": "/node_modules/obsidian/node_modules/moment/moment.js", "^obsidian$": "/test/mocks/obsidian.ts" }, setupFiles: ['./test/setup.ts'], transform: { "^.+\\.tsx?$": ["ts-jest", { diagnostics: { ignoreCodes: [151002] } }], }, } ================================================ FILE: manifest-beta.json ================================================ { "id": "obsidian-task-collector", "name": "Task Collector (TC)", "version": "1.2.2", "minAppVersion": "1.13.0", "description": "Change task status and collect tasks within a document using hotkeys and context menus.", "author": "ebullient", "authorUrl": "https://github.com/ebullient", "fundingUrl": "https://buymeacoffee.com/ebullient", "isDesktopOnly": false } ================================================ FILE: manifest.json ================================================ { "id": "obsidian-task-collector", "name": "Task Collector (TC)", "version": "1.2.2", "minAppVersion": "1.13.0", "description": "Change task status and collect tasks within a document using hotkeys and context menus.", "author": "ebullient", "authorUrl": "https://github.com/ebullient", "fundingUrl": "https://buymeacoffee.com/ebullient", "isDesktopOnly": false } ================================================ FILE: package.json ================================================ { "name": "obsidian-task-collector", "version": "1.2.2", "private": true, "description": "Manage completed tasks within a document in Obsidian (https://obsidian.md)", "main": "main.js", "engines": { "node": ">=22" }, "scripts": { "dev": "node --env-file-if-exists=.env esbuild.config.mjs", "fix": "npx @biomejs/biome check --write ./src", "format": "npx @biomejs/biome format ./src", "eslint": "npx eslint", "lint": "npx @biomejs/biome lint ./src", "prebuild": "npx @biomejs/biome check ./src && npm run test", "build": "node esbuild.config.mjs production", "postbuild": "cp -v manifest.json README.md build", "test": "npx jest", "preversion": "npm run build", "version": "auto-changelog -p", "brat-notes": "run() { auto-changelog --stdout --hide-credit --hide-empty-releases --template .github/changelog.hbs -v $1 --starting-version $1 > release-notes.md; }; run" }, "keywords": [ "obsidian", "obsidian-md", "obsidian-plugin", "obsidian-md-plugin" ], "author": "ebullient", "repository": "github.com:ebullient/obsidian-task-collector", "license": "MIT", "devDependencies": { "@biomejs/biome": "2.4.9", "@types/jest": "^30.0.0", "@types/moment": "^2.13.0", "@types/node": "^25.5.0", "@typescript-eslint/parser": "^8.57.2", "auto-changelog": "^2.5.0", "esbuild": "^0.27.4", "esbuild-sass-plugin": "^3.7.0", "eslint-plugin-obsidianmd": "^0.2.9", "jest": "^30.3.0", "jest-environment-jsdom": "^30.3.0", "jest-util": "^30.3.0", "obsidian": "^1.12.3", "ts-jest": "^29.4.9", "tslib": "^2.8.1", "typescript": "6.0.2" }, "dependencies": { "@codemirror/language": "https://github.com/lishid/cm-language" }, "overrides": { "glob": "13.0.6", "minimatch": "10.2.3" }, "auto-changelog": { "backfillLimit": false, "commitLimit": false, "ignoreCommitPattern": "(🔖|🔨|🧹|changelog|release|Update README).*", "replaceText": { "([;.,] [rR]esolves) #\\d+\\.?\\s*$": "$1", "([;.,] [Ff]ixes) #\\d+\\.?\\s*$": "$1" } } } ================================================ FILE: src/@types/api.d.ts ================================================ export interface API { /** * Return completed task values as a string. * Will be "x", "xX", "x-", or "xX-" */ getCompletedTaskValues(): string; /** * Return incomplete task values as a string. * Minimally " ", but could be any other combination of characters, like " >?/123!" */ getIncompleteTaskValues(): string; /** * Return true if the provided value marks a completed (or canceled) item. */ isComplete(value: string): boolean; /** * Return true if the provided value marks a canceled item (-). */ isCanceled(value: string): boolean; /** * Async method that displays a modal menu containing potential task * completion candidates. * * Returns the selected "mark" (character) as a string. */ getMark(): Promise; } ================================================ FILE: src/@types/settings.d.ts ================================================ export type TaskCollectorSettings = { groups: Record; collectionEnabled: boolean; previewClickModal: boolean; markCycle: string; markCycleRemoveTask: boolean; skipSectionMatch: string; contextMenu: { markTask: boolean; resetTask: boolean; resetAllTasks: boolean; collectTasks: boolean; }; debug: boolean; convertEmptyLines: boolean; hideNotifications: boolean; version: TcVersion; }; export type ManipulationSettings = { name: string; marks: string; complete: boolean; removeExpr: string; appendDateFormat: string; collection?: CollectionSettings; registerCommand: boolean; useContextMenu: boolean; }; export type CollectionSettings = { areaHeading: string; removeCheckbox: boolean; }; export type TcVersion = { major: number; minor: number; patch: number; }; export type TaskCollectorCache = { useContextMenu: boolean; // task line context menu items marks: Record; // (char, settings) removeExpr: Record; // (settings name, removeTextRegex) undoExpr: Record; // (settings name, undoRegex) skipSectionExpr: RegExp | null; completedMarks: string; // marks that should be treated as "complete" incompleteMarks: string; // marks that should be treated as "incomplete" areaHeadings: string[]; // configured area headings headingToMark: Record; // heading to string of marks }; export type LegacySettings = { completedAreaHeader?: string; removeExpression?: string; appendDateFormat?: string; appendRemoveAllTasks?: boolean; incompleteTaskValues?: string; onlyLowercaseX?: boolean; supportCanceledTasks?: boolean; previewOnClick?: boolean; rightClickComplete?: boolean; rightClickMark?: boolean; rightClickMove?: boolean; rightClickResetTask?: boolean; rightClickResetAll?: boolean; rightClickToggleAll?: boolean; completedAreaRemoveCheckbox?: boolean; // Task Marker migration fields cycleTaskValues?: string; incompleteTaskValuesRow2?: string; appendTextFormatMarkRow2?: string; appendTextFormatMark?: string; appendTextFormatAppend?: string; rightClickAppend?: boolean; version?: TcVersion; }; export type TcSectionBlock = { existing: string[]; newTasks: string[]; }; export type TcSection = { blocks: TcSectionBlock[]; }; ================================================ FILE: src/main.ts ================================================ import { TaskCollectorPlugin } from "./taskcollector-Plugin"; export default TaskCollectorPlugin; ================================================ FILE: src/moment.ts ================================================ import { moment } from "obsidian"; type MomentFn = (...args: unknown[]) => moment.Moment; export const momentFn = "default" in moment ? (moment as unknown as { default: MomentFn }).default : (moment as unknown as MomentFn); ================================================ FILE: src/styles.scss ================================================ .task-collector-settings { dt, .setting-item.tc-create-task-group { padding: 0.75em 0 0 0; border-top: 1px solid var(--background-modifier-border); } .task-group-name { input[type="text"]:disabled { background-color: var(--background-primary); } } button.data-value-error { background-color: var(--background-modifier-error); color: var(--text-on-accent); } input[type="text"].data-value-error { border-color: var(--text-error); text-decoration: underline; } .task-marks { input[type="text"] { font-family: var(--font-monospace); } .setting-item-control[marks*=" "] { position: relative; } .setting-item-control[marks*=" "]::after { content: "␣"; position: absolute; left: 5px; } } .setting-item { border-top: none; } .regex-test-setting.regex-hidden { display: none; } } #taskcollector-modal { .modal-close-button { display: none; } .modal { padding: 10px; min-width: 200px; max-width: 300px; .markdown-preview-view { padding: 5px; ul { display: flex; flex-wrap: wrap; --gap: 3px; --square: 45px; margin: calc(-1 * var(--gap)) calc(1 * var(--gap)); margin-block-start: 0; margin-block-end: 0; padding-inline-start: 0; > li { margin: var(--gap); display: block; width: var(--square); height: var(--square); background-color: var(--background-primary); border: 1px solid var(--background-modifier-border); border-radius: 2px; text-indent: unset; line-height: var(--square); text-align: center; > span { font-family: var(--font-monospace); } } > li::before { display: none; } > li.task-list-item { text-decoration: none; input.task-list-item-checkbox { margin-right: 4px; margin-left: unset; } } } nav { display: flex; flex-wrap: wrap; justify-content: space-around; span { display: block; font-size: 0.8em; color: var(--text-muted); } } } } .modal-content { background-color: var(--background-secondary); margin-top: 0px; } } ================================================ FILE: src/taskcollector-Api.ts ================================================ import type { App } from "obsidian"; import type { API } from "./@types/api"; import type { TaskCollector } from "./taskcollector-TaskCollector"; import { promptForMark } from "./taskcollector-TaskMarkModal"; export class TaskCollectorApi implements API { app: App; taskCollector: TaskCollector; constructor(app: App, taskCollector: TaskCollector) { this.app = app; this.taskCollector = taskCollector; } getCompletedTaskValues(): string { return this.taskCollector.cache.completedMarks; } getIncompleteTaskValues(): string { return this.taskCollector.cache.incompleteMarks; } getMark(): Promise { return promptForMark(this.app, this.taskCollector); } isComplete(value: string): boolean { return this.getCompletedTaskValues().contains(value); } isCanceled(value: string): boolean { return value === "-"; } } ================================================ FILE: src/taskcollector-Constants.ts ================================================ import type { ManipulationSettings, TaskCollectorCache, TaskCollectorSettings, } from "./@types/settings"; export const TEXT_ONLY_MARK = "Ø"; export const TEXT_ONLY_NAME = "text"; export const DEFAULT_NAME = "default"; export const COMPLETE_NAME = "complete"; export const DEFAULT_SETTINGS: TaskCollectorSettings = { groups: { default: { name: DEFAULT_NAME, marks: " ", complete: false, removeExpr: "", appendDateFormat: "", registerCommand: false, useContextMenu: false, }, complete: { name: COMPLETE_NAME, marks: "xX", complete: true, removeExpr: "", appendDateFormat: "", registerCommand: false, useContextMenu: false, }, }, markCycle: "", markCycleRemoveTask: false, collectionEnabled: false, previewClickModal: true, contextMenu: { markTask: true, resetTask: false, resetAllTasks: false, collectTasks: true, }, debug: false, convertEmptyLines: false, hideNotifications: false, skipSectionMatch: "", version: { major: 0, minor: 0, patch: 0, }, }; export const GROUP_DEFAULT: ManipulationSettings = { name: DEFAULT_NAME, marks: "", complete: false, removeExpr: "", appendDateFormat: "", registerCommand: false, useContextMenu: false, }; export const GROUP_COMPLETE: ManipulationSettings = { name: COMPLETE_NAME, marks: "xX", complete: true, removeExpr: "", appendDateFormat: "", registerCommand: false, useContextMenu: false, }; export const DEFAULT_COLLECTION = { areaHeading: "## Log", removeCheckbox: false, }; export const CACHE_DEFAULT: TaskCollectorCache = { useContextMenu: false, completedMarks: "", incompleteMarks: "", skipSectionExpr: null, marks: {}, removeExpr: {}, undoExpr: {}, areaHeadings: [], headingToMark: {}, }; export const DEFAULT_SETTINGS_0 = { completedAreaHeader: "## Log", removeExpression: "", appendDateFormat: "", appendRemoveAllTasks: false, incompleteTaskValues: " ", onlyLowercaseX: false, supportCanceledTasks: true, previewOnClick: false, rightClickComplete: false, rightClickMark: false, rightClickMove: false, rightClickResetTask: false, rightClickResetAll: false, rightClickToggleAll: false, completedAreaRemoveCheckbox: false, }; ================================================ FILE: src/taskcollector-Data.ts ================================================ import type { LegacySettings, ManipulationSettings, TaskCollectorSettings, TcVersion, } from "./@types/settings"; import { COMPLETE_NAME, DEFAULT_NAME, DEFAULT_SETTINGS, DEFAULT_SETTINGS_0, GROUP_DEFAULT, TEXT_ONLY_MARK, TEXT_ONLY_NAME, } from "./taskcollector-Constants"; import type { TaskCollectorPlugin } from "./taskcollector-Plugin"; export const Data = { constructSettings, createSettingsGroup, sanitize, sanitizeMarks, moveGroup, }; /** * Sort and remove duplicate characters from string * @param marks * @returns */ function sanitizeMarks(marks: string): string { const tmp = Array.from(new Set(marks)); tmp.sort(); return tmp.join("").replace(TEXT_ONLY_MARK, ""); } function sanitize(tcp: TaskCollectorPlugin, settings: TaskCollectorSettings) { tcp.tc.logDebug("sanitize begin", settings); let dirty = false; // Make sure characters in cycle are unique if (settings.markCycle) { settings.markCycle = [...new Set(settings.markCycle)].join(""); settings.markCycle.replace("§", ""); if (settings.markCycleRemoveTask) { settings.markCycle += "§"; } } // resolve groups with a key / mts.name mismatch for (const [name, mts] of Object.entries(settings.groups)) { if (name !== mts.name) { dirty = true; // ensure name & mts.name agree if (settings.groups[mts.name]) { tcp.tc.logDebug( `Group named ${mts.name} already exists. Reverting group name to ${name}`, ); mts.name = name; } else { // move to the new name moveGroup(tcp, settings.groups, name, mts.name); } } } if (hasMark(settings.groups[TEXT_ONLY_NAME])) { dirty = true; // ensure text only group has no marks settings.groups[TEXT_ONLY_NAME].marks = TEXT_ONLY_MARK; } // check for multiple groups with empty marks const textOnlyGroups = Object.entries(settings.groups).filter( ([_, mts]) => !hasMark(mts), ); if (textOnlyGroups.length > 1) { dirty = true; tcp.tc.logDebug( `There can be only one group for text-only settings (${TEXT_ONLY_NAME}).`, ); if (!settings.groups[TEXT_ONLY_NAME]) { // There is no text only group. Use the first found tcp.tc.logDebug( `Configuration: renamed group ${textOnlyGroups[0][1].name} to ${TEXT_ONLY_NAME}.`, ); moveGroup( tcp, settings.groups, textOnlyGroups[0][1].name, TEXT_ONLY_NAME, ); } let used = ""; let nextMark: string; // filter from the top (post-move) for (const [name, mts] of Object.entries(settings.groups)) { if (!hasMark(mts) && name !== TEXT_ONLY_NAME) { [used, nextMark] = nextRandom(used); settings.groups[name].marks = nextMark; } } } else if ( textOnlyGroups.length === 1 && textOnlyGroups[0][1].name !== TEXT_ONLY_NAME ) { // Make sure the text only group has the required name tcp.tc.logDebug( `Configuration: renamed group ${textOnlyGroups[0][1].name} to ${TEXT_ONLY_NAME}.`, ); moveGroup( tcp, settings.groups, textOnlyGroups[0][1].name, TEXT_ONLY_NAME, ); } // The text-only group is not subject to task collection if (settings.groups[TEXT_ONLY_NAME]) { if (settings.groups[TEXT_ONLY_NAME].collection) { settings.groups[TEXT_ONLY_NAME].collection = undefined; } } if (dirty) { tcp.tc.notify( "(TC) Configuration settings were modified. See console for details.", ); } tcp.tc.logDebug("sanitize end", settings); } /** * Ensure loaded settings are valid for this version of the plugin * (will have trouble back-porting) * @param orig Original object (any) * @returns Properly formed TaskCollectorSettings */ async function constructSettings( tcp: TaskCollectorPlugin, orig: LegacySettings, ): Promise { return orig.version ? await adaptSettings(tcp, orig) : await migrateSettings(tcp, orig); } /** * Incremental update of 1.x TaskCollectorSettings * @param obj Partial TaskCollectorSettings * @returns Properly formed TaskCollectorSettings */ async function adaptSettings( tcp: TaskCollectorPlugin, obj: LegacySettings, ): Promise { const settings: TaskCollectorSettings = { ...DEFAULT_SETTINGS, ...(obj as Partial), }; sanitize(tcp, settings); const version = toVersion(tcp.manifest.version); if (compareVersion(version, settings.version) === 0) { return settings; } // Save the version and modified config settings.version = version; await tcp.saveData(settings); return settings; } async function migrateSettings( tcp: TaskCollectorPlugin, orig: LegacySettings, ): Promise { const old: Required = { ...DEFAULT_SETTINGS_0, version: { major: 0, minor: 0, patch: 0 }, cycleTaskValues: "", incompleteTaskValuesRow2: "", appendTextFormatMarkRow2: "", appendTextFormatMark: "", appendTextFormatAppend: "", rightClickAppend: false, ...orig, }; const settings = JSON.parse( JSON.stringify(DEFAULT_SETTINGS), ) as TaskCollectorSettings; // deep copy // Menus and Modals settings.previewClickModal = old.previewOnClick; settings.contextMenu.markTask = old.rightClickMark; settings.contextMenu.resetTask = old.rightClickResetTask; settings.groups[COMPLETE_NAME].useContextMenu = old.rightClickComplete; // Groups and marks let marks = "x"; if (!old.onlyLowercaseX) { marks += "X"; } if (old.supportCanceledTasks) { marks += "-"; } settings.groups[COMPLETE_NAME].marks = sanitizeMarks(marks); settings.groups[COMPLETE_NAME].appendDateFormat = old.appendDateFormat; settings.groups[COMPLETE_NAME].removeExpr = old.removeExpression; marks = old.incompleteTaskValues; settings.groups[DEFAULT_NAME].marks = sanitizeMarks(marks); if (old.appendRemoveAllTasks) { settings.groups[DEFAULT_NAME].appendDateFormat = old.appendDateFormat; settings.groups[DEFAULT_NAME].removeExpr = old.removeExpression; } // Task Collector default settings.collectionEnabled = true; settings.contextMenu.collectTasks = old.rightClickMove || false; // Task Marker if (orig.cycleTaskValues) { settings.markCycle = orig.cycleTaskValues; if (orig.incompleteTaskValuesRow2) { createSettingsGroup(settings.groups, "group-2", { marks: sanitizeMarks(orig.incompleteTaskValuesRow2), appendDateFormat: orig.appendTextFormatMarkRow2, }); } if (orig.appendTextFormatMark) { settings.groups[DEFAULT_NAME].appendDateFormat = orig.appendTextFormatMark; } if (orig.appendTextFormatAppend) { createSettingsGroup(settings.groups, TEXT_ONLY_NAME, { marks: TEXT_ONLY_MARK, appendDateFormat: orig.appendTextFormatAppend, useContextMenu: orig.rightClickAppend, }); } tcp.tc.logDebug("groups", settings.groups); // Task Marker default settings.collectionEnabled = false; } // Task collection if (settings.collectionEnabled) { settings.groups[COMPLETE_NAME].collection = { areaHeading: old.completedAreaHeader, removeCheckbox: old.completedAreaRemoveCheckbox, }; } // Save modified config settings.version = toVersion(tcp.manifest.version); tcp.tc.logDebug("migrated settings", settings); await tcp.saveData(settings); return settings; } function createSettingsGroup( groups: Record, name: string, data: Partial, ): void { groups[name] = { ...GROUP_DEFAULT, name, ...data, }; if (groups[name].marks === "") { groups[name].marks = TEXT_ONLY_MARK; } } function toVersion(version: string): TcVersion { const v = version.split("."); return { major: Number(v[0]), minor: Number(v[1]), patch: Number(v[2]), }; } function compareVersion(v1: TcVersion, v2: TcVersion): number { if (v1.major === v2.major) { if (v1.minor === v2.minor) { return v1.patch - v2.patch; } return v1.minor - v2.minor; } return v1.major - v2.major; } function hasMark(group: ManipulationSettings) { return group && group.marks !== "" && group.marks !== TEXT_ONLY_MARK; } function moveGroup( tcp: TaskCollectorPlugin, groups: Record, oldName: string, newName: string, ) { if (!groups || !oldName || !newName || newName === oldName) { return; } if (groups[newName]) { tcp.tc.logDebug(`Can not move group, ${newName} already exists`); } else { groups[oldName].name = newName; groups[newName] = groups[oldName]; delete groups[oldName]; } } function nextRandom(used: string): string[] { let next = used; let i = 0; do { const mark = String.fromCharCode( 0x2654 + Math.random() * (0x2667 - 0x2654 + 1), ); if (next.indexOf(mark) < 0) { next += mark; return [next, mark]; } i++; } while (i < 10); return [next, String.fromCharCode(0x24e7)]; } ================================================ FILE: src/taskcollector-Plugin.ts ================================================ import type { Extension } from "@codemirror/state"; import { type EditorView, ViewPlugin } from "@codemirror/view"; import { type Command, type Editor, type EditorPosition, type EventRef, type MarkdownFileInfo, type MarkdownPostProcessor, MarkdownPreviewRenderer, MarkdownView, Menu, Plugin, type TFile, } from "obsidian"; import type { API } from "./@types/api"; import type { LegacySettings } from "./@types/settings"; import { TaskCollectorApi } from "./taskcollector-Api"; import { TEXT_ONLY_MARK } from "./taskcollector-Constants"; import { Data } from "./taskcollector-Data"; import { TaskCollectorSettingsTab } from "./taskcollector-SettingsTab"; import { Direction, TaskCollector } from "./taskcollector-TaskCollector"; import { promptForMark } from "./taskcollector-TaskMarkModal"; declare module "obsidian" { interface App { commands: { commands: { [id: string]: Command; }; removeCommand(id: string): void; executeCommandById: (id: string) => void; }; plugins: { plugins: { "obsidian-task-collector": { api: API; }; }; }; } interface MarkdownPostProcessorContext { containerEl: HTMLElement; } } interface Selection { start: EditorPosition; end?: EditorPosition; lines: number[]; } export class TaskCollectorPlugin extends Plugin { tc: TaskCollector; handlersRegistered = false; commandsRegistered = false; editTaskContextMenu: EventRef | null; postProcessor: MarkdownPostProcessor | null; /** CodeMirror 6 extensions. Tracked via array to allow for dynamic updates. */ private cmExtension: Extension[] = []; /** External-facing plugin API. */ public api: API; async onload(): Promise { console.debug(`loading Task Collector (TC) v${this.manifest.version}`); this.tc = new TaskCollector(); this.addSettingTab( new TaskCollectorSettingsTab(this.app, this, this.tc), ); await this.loadSettings(); // Live Preview: register input handler if (this.tc.settings.previewClickModal) { this.cmExtension.push(inlinePlugin(this, this.tc)); this.registerEditorExtension(this.cmExtension); } this.registerCommands(); this.registerHandlers(); this.api = new TaskCollectorApi(this.app, this.tc); this.app.plugins.plugins["obsidian-task-collector"].api = this.api; } async markInCycle(direction: Direction, lines?: number[]): Promise { const activeFile = this.app.workspace.getActiveFile(); if (activeFile) { await this.app.vault.process(activeFile, (source): string => { return this.tc.markInCycle(source, direction, lines); }); } } async editLines(mark: string, lines?: number[]): Promise { const activeFile = this.app.workspace.getActiveFile(); if (activeFile) { await this.editLinesInFile(activeFile, mark, lines); } } async editLinesInFile( file: TFile, mark: string, lines?: number[], ): Promise { await this.app.vault.process(file, (source): string => { return this.tc.markSelectedTask(source, mark, lines); }); } async collectTasks(): Promise { const activeFile = this.app.workspace.getActiveFile(); if (activeFile) { await this.app.vault.process(activeFile, (source): string => { return this.tc.moveAllTasks(source); }); } } async resetAllTasks(): Promise { const activeFile = this.app.workspace.getActiveFile(); if (activeFile) { await this.app.vault.process(activeFile, (source): string => { return this.tc.resetAllTasks(source); }); } } getCurrentLinesFromEditor(editor: Editor): Selection { this.tc.logDebug( "from: %o, to: %o, anchor: %o, head: %o, general: %o", editor.getCursor("from"), editor.getCursor("to"), editor.getCursor("anchor"), editor.getCursor("head"), editor.getCursor(), ); let start: EditorPosition; let end: EditorPosition | undefined; const lines: number[] = []; if (editor.somethingSelected()) { start = editor.getCursor("from"); end = editor.getCursor("to"); for (let i = start.line; i <= end.line; i++) { lines.push(i); } } else { start = editor.getCursor(); lines.push(start.line); } return { start, end, lines, }; } buildContextMenu( menu: Menu, info: MarkdownView | MarkdownFileInfo, selection: Selection, ): void { if (this.tc.settings.contextMenu.markTask) { menu.addItem((item) => item .setTitle("(TC) Mark task") .setIcon("check-square") .onClick(async () => { this.tc.logDebug("Mark task", menu, info, selection); const mark = await promptForMark(this.app, this.tc); if (mark) { await this.editLines(mark, selection.lines); this.restoreCursor(selection, info.editor); } }), ); if (this.tc.settings.markCycle) { menu.addItem((item) => item .setTitle("(TC) Mark with next") .setIcon("forward") .onClick(async () => { this.tc.logDebug( "Mark with next", menu, info, selection, ); await this.markInCycle( Direction.NEXT, selection.lines, ); this.restoreCursor(selection, info.editor); }), ); menu.addItem((item) => item .setTitle("(TC) Mark with previous") .setIcon("reply") .onClick(async () => { this.tc.logDebug( "Mark with previous", menu, info, selection, ); await this.markInCycle( Direction.PREV, selection.lines, ); this.restoreCursor(selection, info.editor); }), ); } } // dynamic/optional menu items for (const [k, ms] of Object.entries(this.tc.cache.marks)) { if (ms.useContextMenu) { menu.addItem((item) => item .setTitle( k === TEXT_ONLY_MARK ? "(TC) Append text" : `(TC) Change to '[${k}]' (${ms.name})`, ) .setIcon("check-circle") .onClick(async () => { this.tc.logDebug( `Change to '${k}'`, menu, info, selection, ); await this.editLines(k, selection.lines); this.restoreCursor(selection, info.editor); }), ); } } if (this.tc.settings.contextMenu.resetAllTasks) { menu.addItem((item) => item .setTitle("(TC) Reset all tasks") .setIcon("blocks") .onClick(async () => { this.tc.logDebug("Reset all tasks", menu, info); await this.resetAllTasks(); this.restoreCursor(selection, info.editor); }), ); } if ( this.tc.settings.collectionEnabled && this.tc.settings.contextMenu.collectTasks ) { menu.addItem((item) => item .setTitle("(TC) Collect tasks") .setIcon("tornado") .onClick(async () => { await this.collectTasks(); this.restoreCursor(selection, info.editor); }), ); } } restoreCursor(selection: Selection, editor: Editor) { if (selection.lines.length > 1) { editor.setSelection(selection.start, selection.end); } else { editor.setCursor(selection.start); } } registerCommands(): void { if (!this.commandsRegistered) { this.tc.logDebug("register commands"); this.commandsRegistered = true; const markTaskCommand: Command = { id: "task-collector-mark", name: "Mark task", icon: "check-square", editorCallback: async ( editor: Editor, _view: MarkdownView | MarkdownFileInfo, ) => { const mark = await promptForMark(this.app, this.tc); if (mark) { const selection = this.getCurrentLinesFromEditor(editor); await this.editLines(mark, selection.lines); this.restoreCursor(selection, editor); } }, }; this.addCommand(markTaskCommand); const resetAllTaskCommand: Command = { id: "task-collector-reset-all-tasks", name: "Reset all tasks", icon: "blocks", callback: async () => { await this.resetAllTasks(); }, }; this.addCommand(resetAllTaskCommand); if (this.tc.settings.collectionEnabled) { const moveAllTaskCommand: Command = { id: "task-collector-move-completed-tasks", name: "Collect tasks", icon: "tornado", callback: async () => { await this.collectTasks(); }, }; this.addCommand(moveAllTaskCommand); } if (this.tc.settings.markCycle) { const markWithNextCommand: Command = { id: "task-collector-mark-next", name: "Mark with next", icon: "forward", editorCallback: async ( editor: Editor, view: MarkdownView | MarkdownFileInfo, ) => { this.tc.logDebug( `${markWithNextCommand.id}: callback`, editor, view, ); const selection = this.getCurrentLinesFromEditor(editor); await this.markInCycle(Direction.NEXT, selection.lines); this.restoreCursor(selection, editor); }, }; this.addCommand(markWithNextCommand); const markWithPrevCommand: Command = { id: "task-collector-mark-prev", name: "Mark with previous", icon: "reply", editorCallback: async ( editor: Editor, view: MarkdownView | MarkdownFileInfo, ) => { this.tc.logDebug( `${markWithPrevCommand.id}: callback`, editor, view, ); const selection = this.getCurrentLinesFromEditor(editor); await this.markInCycle(Direction.PREV, selection.lines); this.restoreCursor(selection, editor); }, }; this.addCommand(markWithPrevCommand); } // Per-group/mark commands for (const [k, ms] of Object.entries(this.tc.cache.marks)) { if (ms.registerCommand) { const command: Command = { id: `task-collector-mark-task-${k}`, name: k === TEXT_ONLY_MARK ? "Append text" : `Mark with '${k}'`, icon: k === TEXT_ONLY_MARK ? "list-plus" : "check-circle", editorCallback: async ( editor: Editor, view: MarkdownView | MarkdownFileInfo, ) => { const selection = this.getCurrentLinesFromEditor(editor); this.tc.logDebug( `${command.id}: callback`, selection, editor, view, ); await this.editLines(k, selection.lines); this.restoreCursor(selection, editor); }, }; this.addCommand(command); } } } } unregisterCommands(): void { this.tc.logDebug("unregister commands"); this.commandsRegistered = false; const oldCommands = Object.keys(this.app.commands.commands).filter( (p) => p.startsWith("task-collector-"), ); for (const command of oldCommands) { this.app.commands.removeCommand(command); } } registerHandlers(): void { if (!this.handlersRegistered) { this.tc.logDebug("register handlers"); this.handlersRegistered = true; // Source / Live Preview mode: register context menu if (this.tc.cache.useContextMenu) { this.editTaskContextMenu = this.app.workspace.on( "editor-menu", async (menu, editor, info) => { //get line selections here this.buildContextMenu( menu, info, this.getCurrentLinesFromEditor(editor), ); }, ); this.registerEvent(this.editTaskContextMenu); } // Reading mode: register post-processor if ( this.tc.cache.useContextMenu || this.tc.settings.previewClickModal ) { this.postProcessor = (el, ctx) => { const checkboxes = el.querySelectorAll( ".task-list-item-checkbox", ); const section = ctx.getSectionInfo(el); if (!checkboxes.length || !ctx.sourcePath || !section) { return; } const targetFile = this.app.vault.getFileByPath( ctx.sourcePath, ); this.tc.logDebug( "markdown postprocessor", el, ctx, section, checkboxes, targetFile, ); // Reset the parent element for embedded elements... let parent: HTMLElement = ctx.containerEl; while ( parent && !parent.classList.contains("markdown-reading-view") ) { if (parent.classList.contains("markdown-embed")) { break; } parent = parent.parentNode as HTMLElement; } let { lineStart } = section; if (parent.hasAttribute("src")) { // If the parent is an embedded element, we need to adjust the line number const src = parent.getAttribute("src"); const blockRef = src.split("#^")[1]; const header = src.split("#")[1]; const metadata = this.app.metadataCache.getFileCache(targetFile); if (blockRef) { const block = metadata.blocks[blockRef]; if (block) { lineStart += block.position.start.line; } } else if (header) { const heading = metadata.headings.find( (h) => h.heading === header, ); if (heading) { lineStart += heading.position.start.line; } } } for (const checkbox of Array.from(checkboxes)) { const line = Number(lineStart) + Number(checkbox.dataset.line); this.tc.logDebug("checkbox", checkbox, line); checkbox.setAttribute("data-tc-line", line.toString()); const parent = checkbox.parentElement; if (this.tc.cache.useContextMenu && parent) { this.registerDomEvent( parent, "contextmenu", (ev) => { const view = this.app.workspace.getActiveViewOfType( MarkdownView, ); if (view) { const menu = new Menu(); this.buildContextMenu(menu, view, { start: { line, ch: 0, }, lines: [line], }); menu.showAtMouseEvent(ev); } }, ); } if (this.tc.settings.previewClickModal) { // reading mode this.registerDomEvent( checkbox, "click", async (ev) => { ev.stopImmediatePropagation(); ev.preventDefault(); const mark = await promptForMark( this.app, this.tc, ); if (mark) { checkbox.checked = mark !== " "; checkbox.parentElement.dataset.task = mark; await this.editLinesInFile( targetFile, mark, [line], ); } }, ); } } }; this.registerMarkdownPostProcessor(this.postProcessor); } } } unregisterHandlers(): void { this.tc.logDebug("unregister handlers"); this.handlersRegistered = false; if (this.editTaskContextMenu) { this.app.workspace.offref(this.editTaskContextMenu); this.editTaskContextMenu = null; } if (this.postProcessor) { MarkdownPreviewRenderer.unregisterPostProcessor(this.postProcessor); this.postProcessor = null; } } onunload(): void { this.unregisterCommands(); this.unregisterHandlers(); } async loadSettings(): Promise { const obj = Object.assign( {}, (await this.loadData()) as LegacySettings, ); this.tc.init(await Data.constructSettings(this, obj)); } async saveSettings(): Promise { await this.saveData(this.tc.settings); if (this.handlersRegistered) { this.unregisterHandlers(); this.registerHandlers(); } if (this.commandsRegistered) { this.unregisterCommands(); this.registerCommands(); } } } export function inlinePlugin(tcp: TaskCollectorPlugin, tc: TaskCollector) { return ViewPlugin.fromClass( class { private readonly view: EditorView; private readonly eventHandler: (ev: MouseEvent) => void; private readonly tcp: TaskCollectorPlugin; constructor(view: EditorView) { this.view = view; this.tcp = tcp; this.eventHandler = (ev: MouseEvent) => { void (async () => { const { target } = ev; const activeFile = this.tcp.app.workspace.getActiveFile(); if ( !activeFile || !(target instanceof HTMLInputElement) || target.type !== "checkbox" || target.classList.contains("metadata-input-checkbox") ) { return; } tcp.tc.logDebug( "TC ViewPlugin: click", target, target.classList, ); ev.stopImmediatePropagation(); ev.preventDefault(); const mark = await promptForMark(this.tcp.app, tc); if (!mark) { return; } await this.tcp.app.vault.process( activeFile, (source): string => { const position = this.view.posAtDOM(target); const line = view.state.doc.lineAt(position); const i = source.split("\n").indexOf(line.text); tc.logDebug( "TC ViewPlugin: mark task", activeFile.path, mark, line, i, ); if (tcp.tc.anyTaskMark.test(line.text)) { return tc.markSelectedTask(source, mark, [ i, ]); } const offset = Number(target.dataset.line); return tc.markSelectedTask(source, mark, [ i + offset, ]); }, ); })(); }; this.view.dom.addEventListener("click", this.eventHandler); tcp.tc.logDebug("TC ViewPlugin: create click handler"); } destroy() { this.view.dom.removeEventListener("click", this.eventHandler); tcp.tc.logDebug("TC ViewPlugin: destroy click handler"); } }, ); } ================================================ FILE: src/taskcollector-SettingsTab.ts ================================================ import { type App, type ButtonComponent, debounce, Notice, PluginSettingTab, Setting, } from "obsidian"; import type { CollectionSettings, ManipulationSettings, TaskCollectorSettings, } from "./@types/settings"; import type TaskCollectorPlugin from "./main"; import { momentFn } from "./moment"; import { COMPLETE_NAME, DEFAULT_COLLECTION, DEFAULT_NAME, DEFAULT_SETTINGS, TEXT_ONLY_MARK, TEXT_ONLY_NAME, } from "./taskcollector-Constants"; import { Data } from "./taskcollector-Data"; import { _regex, type TaskCollector } from "./taskcollector-TaskCollector"; export class TaskCollectorSettingsTab extends PluginSettingTab { plugin: TaskCollectorPlugin; tc: TaskCollector; newSettings: TaskCollectorSettings; groupList: HTMLDListElement; markInputCache: Record> = {}; otherInputCache: Record = {}; saveButton: HTMLElement; constructor( app: App, plugin: TaskCollectorPlugin, taskCollector: TaskCollector, ) { super(app, plugin); this.plugin = plugin; this.tc = taskCollector; this.icon = "tornado"; this.newSettings = DEFAULT_SETTINGS; } async save() { Data.sanitize(this.plugin, this.newSettings); if (this.tc.isDirty(this.newSettings)) { if (this.tc.handlerChanged(this.newSettings)) { new Notice( "Updated Live Preview settings; restart Obsidian to apply changes.", ); } this.tc.init(this.newSettings); await this.plugin.saveSettings(); this.tc.notify("(TC) Configuration saved"); } } /** Save on exit */ hide(): void { void this.save(); } /** Show/validate setting changes */ display(): void { this.newSettings = JSON.parse( JSON.stringify(this.tc.settings), ) as TaskCollectorSettings; this.drawElements(); } drawElements(): void { this.containerEl.empty(); this.containerEl.addClass("task-collector-settings"); new Setting(this.containerEl).setHeading().setName("Task Collector"); new Setting(this.containerEl) .setName("Save settings") .setClass("task-collector-save-reset") .addButton((button) => button .setIcon("reset") .setTooltip( "Reset to previously saved (or generated) values", ) .onClick(() => { this.newSettings = JSON.parse( JSON.stringify(this.tc.settings), ) as TaskCollectorSettings; this.display(); const message = "(TC) Configuration reset"; this.tc.notify(message); }), ) .addButton((button) => { button .setIcon("save") .setTooltip("Save current values") .onClick(async () => { await this.save(); }); this.saveButton = button.buttonEl; }); new Setting(this.containerEl) .setName("Task collection") .setDesc( "Enable task collection (additional task group settings when enabled)", ) .addToggle((toggle) => toggle .setValue(this.newSettings.collectionEnabled) .onChange(async (value) => { const redraw = value !== this.newSettings.collectionEnabled; this.newSettings.collectionEnabled = value; if (redraw) { this.drawElements(); } }), ); new Setting(this.containerEl) .setName("Define task mark cycle") .setDesc( "Specify characters (as a string) for previous/next commands. Use the button to include checkbox removal in the cycle.", ) .addText((input) => input .setPlaceholder("") .setValue(this.newSettings.markCycle.replace("§", "")) .onChange(async (value) => { this.newSettings.markCycle = [...new Set(value)].join( "", ); }), ) .addExtraButton((button) => { const el = button .setTooltip( `Include checkbox removal in the cycle: ${this.newSettings.markCycleRemoveTask}`, ) .setIcon("cross-in-box") .onClick(() => { this.newSettings.markCycleRemoveTask = !this.newSettings.markCycleRemoveTask; el.classList.toggle( "is-active", this.newSettings.markCycleRemoveTask, ); button.setTooltip( `Include checkbox removal in the cycle: ${this.newSettings.markCycleRemoveTask}`, ); }).extraSettingsEl; el.classList.toggle( "is-active", this.newSettings.markCycleRemoveTask, ); }); new Setting(this.containerEl) .setName("Convert non-list lines") .setDesc("Converts non-list lines when marking tasks") .addToggle((toggle) => toggle .setValue(this.newSettings.convertEmptyLines) .onChange(async (value) => { this.newSettings.convertEmptyLines = value; }), ); new Setting(this.containerEl) .setName("Skip matching sections") .setDesc( "When collecting tasks, skip content of sections that match the specified pattern", ) .addText((input) => input .setPlaceholder("") .setValue(this.newSettings.skipSectionMatch) .onChange(async (value) => { this.newSettings.skipSectionMatch = value; }), ); new Setting(this.containerEl).setHeading().setName("Task groups"); this.containerEl.createEl("p", { text: "Task collector configures tasks in groups. " + "Each group can be associated with one or more task marks ('x' or '>'). " + "The default group configuration will apply to any mark not otherwise assigned to a group.", }); this.containerEl.createEl("p", { text: "Marks that you define within the following groups appear in the selection modal. " + "Those marks that 'complete' a task will appear in the top row.", }); this.groupList = this.containerEl.createEl("dl"); this.showTaskGroups(); new Setting(this.containerEl) .setClass("tc-create-task-group") .addButton((button: ButtonComponent) => button .setTooltip("Add a new task group") .setButtonText("+") .onClick(() => { const name = `group-${ Object.values(this.newSettings.groups).length }`; Data.createSettingsGroup( this.newSettings.groups, name, {}, ); this.showTaskGroups(); }), ); new Setting(this.containerEl).setHeading().setName("Menus and modals"); this.containerEl.createEl("p", { text: "Task Collector creates commands that can be bound to hotkeys or accessed using slash commands for marking tasks. " + "The following settings add right click context menu items for those commands.", }); new Setting(this.containerEl) .setName("Click handling: prompt when the checkbox is clicked") .setDesc( "When you click a checkbox, display a panel that allows you to select (with mouse or keyboard) the value to assign.", ) .addToggle((toggle) => toggle .setValue(this.newSettings.previewClickModal) .onChange(async (value) => { this.newSettings.previewClickModal = value; }), ); new Setting(this.containerEl) .setName("Add '(TC) Mark task' menu item") .setDesc( "Add an item to the right-click menu to mark the task on the current line (or within the current selection). This menu item will trigger a quick pop-up modal to select the desired mark value.", ) .addToggle((toggle) => toggle .setValue(this.newSettings.contextMenu.markTask) .onChange(async (value) => { this.newSettings.contextMenu.markTask = value; }), ); new Setting(this.containerEl) .setName("Add `(TC) Collect tasks` menu item") .setDesc( "Add an item to the right-click menu to collect tasks (based on task configuration).", ) .addToggle((toggle) => toggle .setValue(this.newSettings.contextMenu.collectTasks) .onChange(async (value) => { this.newSettings.contextMenu.collectTasks = value; }), ); new Setting(this.containerEl) .setName("Add '(TC) Reset all tasks' command and menu item") .setDesc( "Add a command and an item to the right-click menu to reset/clear all tasks in the current file.", ) .addToggle((toggle) => toggle .setValue(this.newSettings.contextMenu.resetAllTasks) .onChange(async (value) => { this.newSettings.contextMenu.resetAllTasks = value; }), ); new Setting(this.containerEl).setHeading().setName("Other settings"); new Setting(this.containerEl) .setName("Hide notifications") .setDesc( "Hide pop-up notification messages (messages will be logged in the developer console)", ) .addToggle((toggle) => toggle .setValue(this.newSettings.hideNotifications) .onChange(async (value) => { this.newSettings.hideNotifications = value; }), ); new Setting(this.containerEl) .setName("Debug") .setDesc("Enable debug messages") .addToggle((toggle) => toggle .setValue(this.newSettings.debug) .onChange(async (value) => { this.newSettings.debug = value; }), ); } showTaskGroups() { this.markInputCache = {}; this.otherInputCache = {}; this.groupList.empty(); this.clearButtonErrors(); // default always comes first this.createGroupItem(this.newSettings.groups[DEFAULT_NAME]); // any/everything else for (const mts of Object.values(this.newSettings.groups)) { if (mts.name !== DEFAULT_NAME) { this.createGroupItem(mts); } } } createGroupItem(mts: ManipulationSettings) { const dt = this.groupList.createEl("dt"); const itemEl = this.groupList.createEl("dd"); let testSetting: Setting; const nameSetting = new Setting(dt) .setName("Group name") .setDesc("Name for this group") .setClass("task-group-name"); if (mts.name === TEXT_ONLY_NAME) { nameSetting.addExtraButton((b) => { b.setIcon("info") .setTooltip( "This is a special group that supports appending text to arbitrary lines of text", ) .setDisabled(true); }); } nameSetting.addText((text) => { text.setPlaceholder(COMPLETE_NAME) .setValue(mts.name) .setDisabled(mts.name === DEFAULT_NAME) .onChange( debounce( (value) => { const target = this.newSettings.groups[value]; if (!value) { text.inputEl.addClass("data-value-error"); text.inputEl.setAttribute( "aria-label", "A group name is required.", ); } else if (target && target !== mts) { text.inputEl.addClass("data-value-error"); text.inputEl.setAttribute( "aria-label", "This name is already used by another group", ); } else { text.inputEl.removeClass("data-value-error"); text.inputEl.removeAttribute("aria-label"); Data.moveGroup( this.plugin, this.newSettings.groups, mts.name, value, ); if (value === TEXT_ONLY_NAME) { mts.marks = TEXT_ONLY_MARK; // we just created the text group, redraw / rebuild cache this.drawElements(); } } this.testForErrors(); }, 50, true, ), ); }); nameSetting.addExtraButton((b) => { b.setIcon(mts.name === DEFAULT_NAME ? "info" : "trash") .setTooltip( mts.name === DEFAULT_NAME ? "Default task settings" : "Delete this group", ) .setDisabled(mts.name === DEFAULT_NAME) .onClick(async () => { delete this.newSettings.groups[mts.name]; this.showTaskGroups(); }); }); if (mts.name === DEFAULT_NAME) { nameSetting.controlEl.addClass("default-group"); } else if (mts.name === TEXT_ONLY_NAME) { nameSetting.controlEl.addClass("text-only-group"); } if (mts.name !== TEXT_ONLY_NAME) { const taskMarks = new Setting(itemEl) .setName("Task marks") .setClass("task-marks"); if (mts.name !== DEFAULT_NAME) { taskMarks.addToggle((t) => { t.setValue(mts.complete); t.setTooltip( "If enabled, this group represents completed items. Completed items appear in the top row of the selection menu.", ).onChange(async (value) => { mts.complete = value; }); }); taskMarks.setDesc( "Set marks associated with this group as a string, for example: '>?!'. Use a space for unmarked tasks. " + "Enable the toggle if this group represents completed tasks.", ); } else { taskMarks.setDesc( "Set marks associated with this group as a string, for example: '>?!'. Use a space for unmarked tasks. ", ); } taskMarks.addText((input) => { input.setPlaceholder("xX").onChange( debounce( (value) => { const newMarks = Data.sanitizeMarks(value); if (newMarks !== value) { input.inputEl.value = newMarks; } if (newMarks !== mts.marks) { this.removeMarks(mts.marks, input.inputEl); mts.marks = newMarks; taskMarks.controlEl.setAttribute( "marks", mts.marks, ); this.findDuplicates(input.inputEl); } }, 50, true, ), ); // sanitize and display initial value mts.marks = Data.sanitizeMarks(mts.marks); input.setValue(mts.marks); taskMarks.controlEl.setAttribute("marks", mts.marks); this.findDuplicates(input.inputEl); }); } new Setting(itemEl) .setName(`Append date to ${this.getDescription(mts)}`) .setDesc( `Append today's date in the given moment.js format to the end of the ${this.getDescription( mts, )}`, ) .addMomentFormat((momentFormat) => { momentFormat .setPlaceholder("YYYY-MM-DD") .setValue(mts.appendDateFormat) .onChange( debounce( (value) => { try { // Try formatting "now" with the specified format string const now = momentFn().format(value); momentFormat.inputEl.removeClass( "data-value-error", ); momentFormat.inputEl.setAttribute( "aria-label", now, ); mts.appendDateFormat = value; } catch (e) { momentFormat.inputEl.addClass( "data-value-error", ); momentFormat.inputEl.setAttribute( "aria-label", "An error occurred parsing this moment string. See log for details.", ); console.error( `Error parsing specified date format for ${mts.name}: ${value}`, e, ); } this.testForErrors(); }, 200, true, ), ); }); new Setting(itemEl) .setName( `Remove text matching pattern from ${this.getDescription(mts)}`, ) .setDesc( `Text matching this regular expression will be removed from ${this.getDescription( mts, )}. Be careful! Test your expression first. The global flag ('g') is used for a per-line match.`, ) .addText((text) => text .setPlaceholder(" #(todo|task)") .setValue(mts.removeExpr) .onChange( debounce( (value) => { if (!value) { testSetting.settingEl.addClass( "regex-hidden", ); return; } testSetting.settingEl.removeClass( "regex-hidden", ); try { // try compiling the regular expression const regex = _regex.tryRemoveTextRegex(value); mts.removeExpr = value; // Visual feedback for valid regex text.inputEl.removeClass( "data-value-error", ); if (value) { // Check for likely over-escaping (e.g., \\\\d instead of \\d) const hasDoubleEscapes = /\\\\[dswDSW]|\\\\[{}[\]]/u.test( value, ); if (hasDoubleEscapes) { text.inputEl.addClass( "data-value-error", ); text.inputEl.setAttribute( "aria-label", `Warning: Pattern may be over-escaped. Use \\d not \\\\d, \\{ not \\\\{, etc. Current: /${regex?.source}/g`, ); } else { text.inputEl.setAttribute( "aria-label", `Valid regex: /${regex?.source || value}/g`, ); } } else { text.inputEl.removeAttribute( "aria-label", ); } this.tc.logDebug( "remove regex", mts.name, mts.removeExpr, ); // Update test field if it exists const testInput = this.otherInputCache[ `removeExpr-test-${mts.name}` ]; if (testInput && regex && testInput.value) { this.updateTestResult( testInput, regex, mts.name, ); } } catch (e) { // Visual feedback for invalid regex text.inputEl.addClass("data-value-error"); const msg = e instanceof Error ? e.message : String(e); text.inputEl.setAttribute( "aria-label", `Invalid regex: ${msg}`, ); console.error( `Error parsing specified text replacement regular expression for ${mts.name}: ${value}`, e, ); } this.testForErrors(); }, 50, true, ), ), ); // Add a test field below the regex input testSetting = new Setting(itemEl) .setClass("regex-test-setting") .setDesc( "Test your regex: Enter sample text to see what will be removed", ) .addText((testInput) => { testInput.setPlaceholder("- [ ] something #todo").onChange( debounce( (value) => { this.otherInputCache[ `removeExpr-test-${mts.name}` ] = testInput.inputEl; if (mts.removeExpr) { try { const regex = _regex.tryRemoveTextRegex( mts.removeExpr, ); if (regex && value) { this.updateTestResult( testInput.inputEl, regex, mts.name, ); } else { testInput.inputEl.removeAttribute( "aria-label", ); } } catch (e) { testInput.inputEl.setAttribute( "aria-label", "Cannot test: regex is invalid", ); console.debug("Invalid regex", e); } } }, 100, true, ), ); // Cache the input element for updates when regex changes this.otherInputCache[`removeExpr-test-${mts.name}`] = testInput.inputEl; }); if (!mts.removeExpr) { testSetting.settingEl.addClass("regex-hidden"); } new Setting(itemEl) .setName("Register '(TC) Mark with... ' command") .setDesc( mts.name === TEXT_ONLY_NAME ? "A command will be registered to append text to selected lines" : "A command will be registered for each mark in the group.", ) .addToggle((toggle) => toggle.setValue(mts.registerCommand).onChange((value) => { mts.registerCommand = value; }), ); new Setting(itemEl) .setName("Add '(TC) Mark with... ' menu item") .setDesc( "A right-click menu item will be added for each mark in the group.", ) .addToggle((toggle) => toggle.setValue(mts.useContextMenu).onChange(async (value) => { mts.useContextMenu = value; }), ); if (this.newSettings.collectionEnabled && mts.name !== TEXT_ONLY_NAME) { if (!mts.collection) { mts.collection = JSON.parse( JSON.stringify(DEFAULT_COLLECTION), ) as CollectionSettings; } new Setting(itemEl) .setName("Area heading") .setClass("area-heading") .setDesc( "Marked tasks will be collected and moved under the specified heading. Task collection for a group only occurs when an area heading is configured.", ) .addText((text) => text .setPlaceholder("## Example") .setValue(mts.collection.areaHeading) .onChange(async (value) => { mts.collection.areaHeading = value; }), ); new Setting(itemEl) .setName("Remove checkbox") .setClass("remove-checkbox") .setDesc("When a task is collected, remove the checkbox") .addToggle((toggle) => toggle .setValue(mts.collection.removeCheckbox) .onChange(async (value) => { mts.collection.removeCheckbox = value; }), ); } } private removeMarks(oldValue: string, input: HTMLInputElement) { const marks = oldValue ? oldValue.split("") : []; this.tc.logDebug( `removeMarks begin: '${oldValue}'`, this.markInputCache, ); if (input.hasClass("no-marks-defined")) { input.removeClass("no-marks-defined"); input.removeClass("data-value-error"); input.removeAttribute("aria-label"); } for (const x of marks) { this.tc.logDebug( `(TC): remove mark '${x}'`, this.markInputCache[x], ); if (this.markInputCache[x]) { const set = this.markInputCache[x]; set.delete(input); this.tryRemoveConflict(x, input); // if there is only one element left in the array, // remove the current character from the list of conflicts if (set.size === 1) { for (const i of set) { this.tryRemoveConflict(x, i); } } } } this.tc.logDebug(`removeMarks end: '${oldValue}'`, this.markInputCache); } private findDuplicates(input: HTMLInputElement) { const marks = input.value ? input.value.split("") : []; this.tc.logDebug( `findDuplicates begin: '${input.value}'`, marks, input, this.markInputCache, ); // add input element into the cache (new marks) for (const x of marks) { if (this.markInputCache[x]) { const set = this.markInputCache[x]; set.add(input); if (set.size > 1) { // we have a conflict over a defined task mark for (const i of set) { this.trySetConflict(x, i); } console.error( `(TC) More then one group uses task mark ${this.showMark( x, )}`, ); } } else { // no conflict, all is well. this.markInputCache[x] = new Set(); this.markInputCache[x].add(input); } } if (marks.length === 0) { input.addClass("no-marks-defined"); input.addClass("data-value-error"); input.setAttribute( "aria-label", this.newSettings.groups[TEXT_ONLY_NAME] ? "Must define one or more marks for this group." : `Must define one or more marks for this group. Change the name to '${TEXT_ONLY_NAME}' for special text-only behavior.`, ); this.tc.logDebug( `findDuplicates end (empty): '${input.value}'`, input, this.markInputCache, ); } this.tc.logDebug( `findDuplicates end: '${input.value}'`, input, this.markInputCache, ); this.testForErrors(); } private trySetConflict(mark: string, input: HTMLInputElement) { const existing = input.getAttribute("conflict") || ""; const conflict = Data.sanitizeMarks(existing + mark); input.setAttribute("conflict", conflict); input.addClass("data-value-error"); input.setAttribute( "aria-label", `More than one task group uses ${this.showMark(conflict)}`, ); this.tc.logDebug( `conflicts for '${input.value}': '${this.showMark(conflict)}'`, ); } private tryRemoveConflict(mark: string, input: HTMLInputElement) { if (!input.hasAttribute("conflict")) { return; } const remaining = input.getAttribute("conflict").replace(mark, ""); if (remaining.length === 0) { // all conflicting marks have been removed input.removeAttribute("conflict"); input.removeClass("data-value-error"); input.removeAttribute("aria-label"); } else { input.removeAttribute("conflict"); this.trySetConflict(remaining, input); } } private getDescription(mts: ManipulationSettings) { return mts.name === TEXT_ONLY_NAME ? "selected lines of text" : "selected task(s)"; } private showMark(x: string) { return x === TEXT_ONLY_MARK ? "(empty)" : x; } private clearButtonErrors() { // Modal create or reset this.saveButton.removeClass("data-value-error"); this.saveButton.removeAttribute("aria-label"); } private testForErrors() { const hasMarkErrors = Object.values(this.markInputCache) .flatMap((s) => Array.from(s.values())) .find((i) => i.hasClass("data-value-error")); const hasMomentErrors = Object.values(this.otherInputCache).find((i) => i.hasClass("data-value-error"), ); if (hasMarkErrors || hasMomentErrors) { this.saveButton.addClass("data-value-error"); this.saveButton.setAttribute( "aria-label", "There are configuration errors. Correct those before saving.", ); } else { this.saveButton.removeClass("data-value-error"); this.saveButton.removeAttribute("aria-label"); } } private updateTestResult( testInput: HTMLInputElement, regex: RegExp, groupName: string, ) { const testText = testInput.value; if (!testText) { testInput.removeAttribute("aria-label"); return; } // Test what will be removed by the regex const match = testText.match(regex); if (match) { const removed = match[0]; const result = testText.replace(new RegExp(regex.source, "g"), ""); testInput.setAttribute( "aria-label", `Will remove: "${removed}" → Result: "${result}"`, ); this.tc.logDebug( `removeExpr test for ${groupName}`, `Input: "${testText}"`, `Matched: "${removed}"`, `Result: "${result}"`, ); } else { testInput.setAttribute( "aria-label", "No match - nothing will be removed from this text", ); this.tc.logDebug( `removeExpr test for ${groupName}`, `Input: "${testText}"`, "No match", ); } } } ================================================ FILE: src/taskcollector-TaskCollector.ts ================================================ import { Notice } from "obsidian"; import type { ManipulationSettings, TaskCollectorCache, TaskCollectorSettings, TcSection, } from "./@types/settings"; import { momentFn } from "./moment"; import { CACHE_DEFAULT, DEFAULT_NAME, TEXT_ONLY_MARK, TEXT_ONLY_NAME, } from "./taskcollector-Constants"; import { Data } from "./taskcollector-Data"; const DATE_FORMATTING_TOKENS = /^(Y|D|M|H|h|m)+$/; const ALL_FORMATTING_TOKENS = /(\[[^[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; export enum Direction { PREV = "PREV", NEXT = "NEXT", } export class TaskCollector { settings: TaskCollectorSettings; cache: TaskCollectorCache; anyListItem = new RegExp(/^([\s>]*(?:-|\+|\*|\d+\.) )([^\\[].*)$/); anyTaskMark = new RegExp(/^([\s>]*(?:-|\+|\*|\d+\.) \[)(.)(\] .*)$/); anyText = new RegExp(/^([\s>]*)(.*)$/); blockQuote = new RegExp(/^(\s*>[\s>]*)(.*)$/); blockRef = new RegExp(/^(.*?)( \^[A-Za-z0-9-]+)?$/); continuation = new RegExp(/^( {2,}|\t)/); stripTask = new RegExp(/^([\s>]*(?:-|\+|\*|\d+\.)) \[.\] (.*)$/); init(settings: TaskCollectorSettings): void { this.settings = settings; this.cache = JSON.parse( JSON.stringify(CACHE_DEFAULT), ) as TaskCollectorCache; this.cache.useContextMenu = settings.contextMenu.markTask || settings.contextMenu.resetTask || settings.contextMenu.collectTasks || settings.contextMenu.resetAllTasks; for (const v of Object.values(settings.groups)) { this.cacheTaskSettings(v, this.cache); } // Store sorted unique list of completion area headings if (this.settings.collectionEnabled) { this.cache.areaHeadings = [ ...Object.keys(this.cache.headingToMark), ]; this.cache.areaHeadings.sort(); } this.cache.completedMarks = Data.sanitizeMarks( this.cache.completedMarks, ); this.cache.incompleteMarks = Data.sanitizeMarks( this.cache.incompleteMarks, ); this.cache.skipSectionExpr = trySkipSectionRegex( settings.skipSectionMatch, ); this.logDebug("configuration read", this.settings, this.cache); } handlerChanged(newSettings: TaskCollectorSettings) { return ( this.settings.previewClickModal !== newSettings.previewClickModal ); } isDirty(newSettings: TaskCollectorSettings) { return JSON.stringify(this.settings) !== JSON.stringify(newSettings); } logDebug(message: string, ...optionalParams: unknown[]): void { if (!this.settings || this.settings.debug) { console.debug(`(TC) ${message}`, ...optionalParams); } } notify(message: string) { if (this.settings?.hideNotifications) { console.warn(message); } else { new Notice(message); } } /** * Process task manipulation settings and populate cache * @param mts * @param cache */ private cacheTaskSettings( mts: ManipulationSettings, cache: TaskCollectorCache, ) { for (const x of mts.marks.split("")) { if (cache.marks[x]) { const name = cache.marks[x].name; console.warn( `Two groups of settings contain ${x}: ${name} and ${mts.name}. Using ${name}`, ); } else { // allow for lookup of this configuration per character cache.marks[x] = mts; // This specific configuration may want to add a context menu cache.useContextMenu = cache.useContextMenu || mts.useContextMenu; // store the regex for matching text to remove if (mts.removeExpr) { const regex = tryRemoveTextRegex(mts.removeExpr); cache.removeExpr[mts.name] = regex; } // store the undo string for this collection of marks if (mts.appendDateFormat) { const regex = tryUndoRegex(mts.appendDateFormat); cache.undoExpr[mts.name] = regex; } // store the area heading for this mark if (mts.collection?.areaHeading) { if (cache.headingToMark[mts.collection.areaHeading]) { cache.headingToMark[mts.collection.areaHeading] += x; } else { cache.headingToMark[mts.collection.areaHeading] = x; } } if (x !== TEXT_ONLY_MARK) { if (mts.complete) { cache.completedMarks += x; } else { cache.incompleteMarks += x; } } } } } // Mark tasks /** * Mark selected tasks * @param source * @param direction: -1 or 1 * @param lines */ markInCycle(source: string, d: Direction, lines: number[] = []): string { const split = source.split("\n"); const len = this.settings.markCycle.length; for (const n of lines) { const taskMatch = this.anyTaskMark.exec(split[n]); const listMatch = this.anyListItem.exec(split[n]); if (taskMatch) { // already a task: change from old to new const old = taskMatch[2]; const i = this.settings.markCycle.indexOf(old); const next = i < 0 ? d === Direction.NEXT // i < 0 ? 0 // NEXT : len - 1 // PREV : d === Direction.NEXT // i >= 0 ? (i + 1) % len // NEXT : (i + len - 1) % len; // PREV const chosenMark = this.settings.markCycle[next]; if (chosenMark === "§") { split[n] = this.doRemoveTask(split[n]); } else { split[n] = this.doMarkTask(split[n], old, chosenMark); } } else if (listMatch?.[2]) { const cycle = this.settings.markCycle.replace("§", ""); const chosenMark = cycle[d === Direction.NEXT ? 0 : cycle.length - 1]; // convert to a task, and then mark split[n] = this.updateLineText( `${listMatch[1]}[ ] ${listMatch[2]}`, chosenMark, ); this.logDebug("task marked", n, `|${split[n]}|`); } } return split.join("\n"); } /** * Mark selected tasks * @param source * @param mark * @param lines */ markSelectedTask( source: string, mark: string, lines: number[] = [], ): string { const split = source.split("\n"); for (const n of lines) { split[n] = this.updateLineText(split[n], mark); this.logDebug("task marked", n, `|${split[n]}|`); } return split.join("\n"); } /** * Update the task in the provided line text to use * the specified mark * @param lineText * @param mark */ updateLineText(lineText: string, existingMark: string): string { let mark = existingMark; if (mark === "Backspace") { return this.doRemoveTask(lineText); } if (mark === "") { mark = TEXT_ONLY_MARK; } if (mark === TEXT_ONLY_MARK && this.cache.marks[TEXT_ONLY_MARK]) { // append general text. Do not convert to or mess with the task-nature return this.doAppendText(lineText); } const taskMatch = this.anyTaskMark.exec(lineText); if (taskMatch) { // already a task: change from old to new const old = taskMatch[2]; return this.doMarkTask(lineText, old, mark); } const listMatch = this.anyListItem.exec(lineText); if (listMatch?.[2]) { // convert to a task, and then mark (recurse) return this.updateLineText( `${listMatch[1]}[ ] ${listMatch[2]}`, mark, ); } if (this.settings.convertEmptyLines) { const indentMatch = this.anyText.exec(lineText); if (indentMatch) { // split line on first character return this.updateLineText( `${indentMatch[1]}- [ ] ${indentMatch[2]}`, mark, ); } } this.logDebug("not a task or list item %s", `|${lineText}|`); return lineText; } private doAppendText(existingLine: string, append = true): string { let lineText = existingLine; // remember line ending: block id and strict line ending whitespace let blockid = ""; const strictLineEnding = lineText.endsWith(" "); const match = this.blockRef.exec(lineText); if (match?.[2]) { lineText = match[1]; blockid = match[2]; } // Apply text-only configuration const undoExpr = this.cache.undoExpr[TEXT_ONLY_NAME]; if (undoExpr) { lineText = lineText.replace(undoExpr, ""); } if (append) { const removeExpr = this.cache.removeExpr[TEXT_ONLY_NAME]; if (removeExpr) { lineText = lineText.replace(removeExpr, ""); } const appendExpr = this.settings.groups[TEXT_ONLY_NAME].appendDateFormat; if (appendExpr) { if (!lineText.endsWith(" ")) { lineText += " "; } lineText += momentFn().format(appendExpr); } } // restore block id & trailing whitespace lineText = lineText.replace(/\s*$/, blockid); if (strictLineEnding) { lineText += " "; } this.logDebug("text updated", `|${lineText}|`); return lineText; } private doMarkTask( existingLine: string, old: string, mark: string, ): string { let lineText = existingLine; if (old === mark) { this.logDebug("task already marked", `|${lineText}|`); return lineText; } const oldMarkName = this.cache.marks[old]?.name || DEFAULT_NAME; const newMarkName = this.cache.marks[mark]?.name || DEFAULT_NAME; // replace the task mark lineText = lineText.replace(this.anyTaskMark, `$1${mark}$3`); // remember line ending: block id and strict line ending whitespace let blockid = ""; const strictLineEnding = lineText.endsWith(" "); const match = this.blockRef.exec(lineText); if (match?.[2]) { lineText = match[1]; blockid = match[2]; } const undoExpr = this.cache.undoExpr[oldMarkName]; if (undoExpr) { lineText = lineText.replace(undoExpr, ""); } const removeExpr = this.cache.removeExpr[newMarkName]; if (removeExpr) { lineText = lineText.replace(removeExpr, ""); } const appendExpr = this.settings.groups[newMarkName].appendDateFormat; if (appendExpr) { if (!lineText.endsWith(" ")) { lineText += " "; } lineText += momentFn().format(appendExpr); } // append block id & replace ending whitespace lineText = lineText.replace(/\s*$/, blockid); if (strictLineEnding) { lineText += " "; } return lineText; } private doRemoveTask(lineText: string): string { return lineText.replace(this.stripTask, "$1 $2"); } // Reset all tasks not in a completion/skipped area resetAllTasks(source: string): string { const lines = source.split("\n"); const result: string[] = []; let inCompletedSection = false; let inSkippedSection = false; for (const line of lines) { const trimmed = line.trim(); if (inCompletedSection || inSkippedSection) { if (line.startsWith("#") || trimmed === "---") { inSkippedSection = this.isSkippedSection(line); inCompletedSection = contains(this.cache.areaHeadings, trimmed) !== undefined; } result.push(line); } else if (trimmed.startsWith("#") || trimmed === "---") { inCompletedSection = contains(this.cache.areaHeadings, trimmed) !== undefined; inSkippedSection = this.isSkippedSection(line); result.push(line); } else if (!(inCompletedSection || inSkippedSection)) { result.push(line.replace(this.anyTaskMark, "$1 $3")); } } return result.join("\n"); } // Task Collection / Move tasks /** * Move marked task to the appropriate heading * @param source */ moveAllTasks(source: string): string { if (this.cache.areaHeadings.length === 0) { return source; } const parsed: string[] = []; const headersInOrder: string[] = []; // split out content for named sections const sections = this.scan(source, parsed, headersInOrder); // move general tasks to appropriate sections const result = this.move(parsed, sections, headersInOrder, 0); // in order of appearance from top to bottom for (let i = 0; i < headersInOrder.length; i++) { const [heading, bi] = headersInOrder[i].split("%:%"); const bi2 = Number(bi); // move existing tasks in sections to other sections sections[heading].blocks[bi2].existing = this.move( sections[heading].blocks[bi2].existing, sections, headersInOrder, i, this.cache.headingToMark[heading], ); } return result .flatMap((l) => { const match = l.match(/%%--TC--(.*)--(\d+)--%%/); if (match) { const h = match[1]; const i = Number(match[2]); return sections[h].blocks[i].newTasks.concat( ...sections[h].blocks[i].existing, ); } return l; }) .join("\n"); } private scan( source: string, parsed: string[], headersInOrder: string[], ): Record { const split = source.split("\n"); this.ensureHeadings(split); const sections: Record = {}; let activeSection: string[] = null; // parse / analyze for (const line of split) { const trimmed = line.trim(); if ( line.startsWith("#") && contains(this.cache.areaHeadings, trimmed) ) { parsed.push(line); // push heading to parsed lines const index = this.createCompletionArea(trimmed, sections); activeSection = sections[trimmed].blocks[index].existing; parsed.push(`%%--TC--${trimmed}--${index}--%%`); headersInOrder.push(`${trimmed}%:%${index}`); } else if ( activeSection && (line.startsWith("#") || line.trim() === "---") ) { activeSection = null; parsed.push(line); } else if (activeSection) { activeSection.push(line); } else { parsed.push(line); } } return sections; } private move( source: string[], sections: Record, order: string[], prevOrder: number, excluded?: string, ): string[] { let orderIndex = prevOrder; const remaining: string[] = []; let markToMove = null; let taskToBeMoved = null; let inCallout = false; let inSkippedSection = false; let i = -1; for (let line of source) { i++; if (line.startsWith("#") || line.trim() === "---") { inSkippedSection = this.isSkippedSection(line); this.logDebug("TC: section", line, inSkippedSection); } if ( taskToBeMoved && !this.isTaskLine(line) && this.isContinuation(line, inCallout, source, i) ) { // keep task lines together taskToBeMoved.push(line); continue; } if (taskToBeMoved) { this.moveMark( markToMove, taskToBeMoved, sections, order, orderIndex, ); markToMove = null; taskToBeMoved = null; inCallout = false; } if (line.startsWith("%%--TC--")) { // only applies to general text, not completion sections // always preceded by a section heading orderIndex = indexFromLine(line); remaining.push(line); continue; } const taskMatch = this.anyTaskMark.exec(line); if (inSkippedSection) { remaining.push(line); } else if (taskMatch) { const mark = taskMatch[2]; if (excluded && excluded.indexOf(mark) >= 0) { // we are in the target section for this mark remaining.push(line); } else if (this.isCollected(mark)) { // start of task that should be moved to another section if (this.removeCheckbox(mark)) { line = this.doRemoveTask(line); } markToMove = mark; taskToBeMoved = []; taskToBeMoved.push(line); inCallout = this.isCallout(line); // is the task inside a callout } else { // mark not configured for collection remaining.push(line); } } else { remaining.push(line); } } if (taskToBeMoved && markToMove) { this.moveMark( markToMove, taskToBeMoved, sections, order, orderIndex, ); } return remaining; } private moveMark( markToMove: string, taskToBeMoved: string[], sections: Record, order: string[], orderIndex: number, ) { const heading = this.cache.marks[markToMove].collection.areaHeading; const index = this.findNextSection(heading, order, orderIndex); // add this task to the list of new tasks for the section for (const l of taskToBeMoved) { sections[heading].blocks[index].newTasks.push(l); } } /** * Find _next_ heading of the requested type (looping back to the beginning if necessary) * @param heading * @param order * @param start * @returns */ private findNextSection( heading: string, order: string[], start: number, ): number | undefined { let wrap = false; for (let i = start; !wrap || i !== start; i++) { if (i === order.length) { i = 0; wrap = true; } if (order[i].startsWith(heading)) { const split = order[i].split("%:%"); return Number(split[1]); } } return undefined; } private createCompletionArea( name: string, sections: Record, ): number { if (!sections[name]) { sections[name] = { blocks: [], }; } sections[name].blocks.push({ existing: [], newTasks: [], }); return sections[name].blocks.length - 1; } private ensureHeadings(split: string[]) { for (const h of this.cache.areaHeadings) { if (!contains(split, h)) { if (split[split.length - 1].trim() !== "") { split.push(""); } split.push(h); split.push(""); } } } private isCollected(mark: string) { return this.cache.marks[mark]?.collection?.areaHeading; } private removeCheckbox(mark: string) { return this.cache.marks[mark]?.collection?.removeCheckbox; } private isSkippedSection(lineText: string): boolean { return this.cache.skipSectionExpr?.test(lineText); } private isCallout(lineText: string): boolean { return this.blockQuote.test(lineText); } private isTaskLine(lineText: string): boolean { return this.anyTaskMark.test(lineText); } private isContinuation( lineText: string, inCallout: boolean, source: string[], i: number, ): boolean { if (inCallout) { const match = this.blockQuote.exec(lineText); if (match) { return ( match[1].endsWith(">") || // newline w/in callout match[1].endsWith(" ") || // leading whitespace match[1].endsWith("\t") // leading whitespace ); } } if (lineText.length === 0) { let j = i + 1; while (j < source.length) { if (source[j].length > 0) { return this.continuation.test(source[j]); } j++; } } return this.continuation.test(lineText); } } function contains(haystack: string[], needle: string) { return haystack.find((s) => s === needle); } function indexFromLine(lineText: string): number { const match = lineText.match(/%%--TC--(.*)--(\d+)--%%/); if (match) { return Number(match[2]); } return undefined; } export const _regex = { tryCompleteRegex, tryIncompleteRegex, tryUndoRegex, tryRemoveTextRegex, trySkipSectionRegex, }; function trySkipSectionRegex(param: string): RegExp { return param ? new RegExp(param) : null; } function tryCompleteRegex(param: string): RegExp { return new RegExp(`^([\\s>]*- \\[)[${param}](\\] .*)$`); } function tryIncompleteRegex(param: string): RegExp { return new RegExp(`^([\\s>]*- \\[)[${param}](\\] .*)$`); } function tryRemoveTextRegex(param: string): RegExp { return param ? new RegExp(param) : null; } function tryUndoRegex(appendDateFormat: string): RegExp { const array = appendDateFormat.match(ALL_FORMATTING_TOKENS); for (let i = 0, length = array.length; i < length; i++) { const segment = array[i]; if (DATE_FORMATTING_TOKENS.test(segment)) { array[i] = segment .replace(/YYYY/g, "\\d{4}") // 4-digit year .replace(/YY/g, "\\d{2}") // 2-digit year .replace(/DD/g, "\\d{2}") // day of month, padded .replace(/D/g, "\\d{1,2}") // day of month, not padded .replace(/MMM/g, "[A-Za-z]{3}") // month, abbrv .replace(/MM/g, "\\d{2}") // month, padded .replace(/M/g, "\\d{1,2}") // month, not padded .replace(/HH/g, "\\d{2}") // 24-hour, padded .replace(/H/g, "\\d{1,2}") // 24-hour, not padded .replace(/hh/g, "\\d{2}") // 12-hour, padded .replace(/h/g, "\\d{1,2}") // 12-hour, not padded .replace(/mm/g, "\\d{2}") // minute, padded .replace(/m/g, "\\d{1,2}"); // minute, not padded; } else if (segment.match(/\[[\s\S]/)) { array[i] = replaceLiterals(segment.replace(/^\[|\]$/g, "")); } else { array[i] = replaceLiterals(segment); } } // allow whitespace around the appended string const matchString = `\\s*${array.join("")}\\s*`; // allow a block reference at the end of the line return new RegExp(`${matchString}( \\^[A-Za-z0-9-]+)?$`); } function replaceLiterals(segment: string) { return segment .replace(/\(/g, "\\(") // escape literal ( .replace(/\)/g, "\\)") // escape literal ) .replace(/\[/g, "\\[") // escape literal [ .replace(/\]/g, "\\]"); // escape literal ] } ================================================ FILE: src/taskcollector-TaskMarkModal.ts ================================================ import { type App, Modal } from "obsidian"; import type { TaskCollector } from "./taskcollector-TaskCollector"; export function promptForMark( app: App, taskCollector: TaskCollector, ): Promise { return new Promise((resolve) => { const modal = new TaskMarkModal(app, taskCollector); modal.onClose = () => { resolve(modal.chosenMark); }; modal.open(); }); } export class TaskMarkModal extends Modal { taskCollector: TaskCollector; chosenMark: string; constructor(app: App, taskCollector: TaskCollector) { super(app); this.taskCollector = taskCollector; this.containerEl.id = "taskcollector-modal"; } onOpen(): void { const selector = this.contentEl.createDiv( "taskcollector-selector markdown-preview-view", ); const completedList = selector.createEl("ul"); completedList.addClass("contains-task-list"); this.addTaskValues( completedList, this.taskCollector.cache.completedMarks, true, ); const list = selector.createEl("ul"); list.addClass("contains-task-list"); this.addTaskValues( list, this.taskCollector.cache.incompleteMarks, false, ); const footer = selector.createEl("nav"); const esc = footer.createSpan(); esc.createEl("b", { text: "Esc" }); esc.appendText(" to dismiss"); const bksp = footer.createSpan(); bksp.createEl("b", { text: "Bksp" }); bksp.appendText(" to remove "); bksp.createEl("code", { text: "[]" }); const keyListener = (event: KeyboardEvent) => { switch (event.key) { case "ArrowLeft": case "ArrowRight": case "ArrowUp": case "ArrowDown": case "CapsLock": case "Tab": break; default: { this.chosenMark = event.key; event.preventDefault(); event.stopImmediatePropagation(); this.close(); } } }; this.scope.register([], null, keyListener); this.scope.register(["Shift"], null, keyListener); } addTaskValues( list: HTMLUListElement, choices: string, _markComplete: boolean, ): void { for (const character of choices) { const li = list.createEl("li", { cls: `task-list-item ${character === " " ? "" : " is-checked"}`, attr: { "data-task": character, }, }); li.addEventListener("click", (_event) => { this.chosenMark = character; this.close(); }); const input = li.createEl("input", { cls: "task-list-item-checkbox", attr: { id: `task-list-item-checkbox-${character}`, type: "checkbox", style: "pointer-events: none;", }, }); if (character !== " ") { input.setAttribute("checked", ""); } li.createSpan({ text: character === " " ? "␣" : character, attr: { style: "pointer-events: none;", }, }); } } onClose(): void { this.contentEl.empty(); } } ================================================ FILE: test/dataMigration.test.ts ================================================ import { App, type PluginManifest } from "obsidian"; import type { TaskCollectorSettings } from "../src/@types/settings"; import { COMPLETE_NAME, DEFAULT_NAME, DEFAULT_SETTINGS_0, GROUP_COMPLETE, GROUP_DEFAULT, TEXT_ONLY_MARK, } from "../src/taskcollector-Constants"; import { Data } from "../src/taskcollector-Data"; import { TaskCollectorPlugin } from "../src/taskcollector-Plugin"; import { TaskCollector } from "../src/taskcollector-TaskCollector"; const MANIFEST: PluginManifest = { id: "obsidian-task-collector", name: "Task Collector (TC)", author: "", version: "1.0.0", minAppVersion: "", description: "", }; jest.mock("obsidian", () => ({ App: jest.fn().mockImplementation(), Plugin: jest.fn().mockImplementation(() => { return { manifest: MANIFEST, saveData: () => Promise.resolve(), // debug: (message: string, ...optionalParams: any[]) => { // console.debug(message, ...optionalParams); // tests // } }; }), PluginSettingTab: jest.fn().mockImplementation(), Modal: jest.fn().mockImplementation(), moment: jest.requireActual("moment-obsidian"), })); const plugin = new TaskCollectorPlugin(new App(), MANIFEST); plugin.tc = new TaskCollector(); const DEFAULT_MIGRATION = { groups: { default: { name: DEFAULT_NAME, marks: " ", complete: false, removeExpr: "", appendDateFormat: "", registerCommand: false, useContextMenu: false, }, complete: { name: COMPLETE_NAME, marks: "-Xx", complete: true, removeExpr: "", appendDateFormat: "", registerCommand: false, useContextMenu: false, collection: { areaHeading: "## Log", removeCheckbox: false, }, }, }, markCycle: "", markCycleRemoveTask: false, collectionEnabled: true, previewClickModal: false, contextMenu: { markTask: false, resetTask: false, resetAllTasks: false, collectTasks: false, }, debug: false, convertEmptyLines: false, hideNotifications: false, skipSectionMatch: "", version: { major: 1, minor: 0, patch: 0, }, }; test("Migration: defaults", async () => { const settings = await Data.constructSettings(plugin, DEFAULT_SETTINGS_0); expect(settings).toEqual(DEFAULT_MIGRATION); }); test("Migration: appendReplace all", async () => { const initial = Object.assign({}, DEFAULT_SETTINGS_0, { appendDateFormat: "[(completed on ]D MMM, YYYY[)]", removeExpression: "#done", appendRemoveAllTasks: true, }); const expected: TaskCollectorSettings = JSON.parse( JSON.stringify(DEFAULT_MIGRATION), ); expected.groups[COMPLETE_NAME].appendDateFormat = "[(completed on ]D MMM, YYYY[)]"; expected.groups[DEFAULT_NAME].appendDateFormat = "[(completed on ]D MMM, YYYY[)]"; expected.groups[COMPLETE_NAME].removeExpr = "#done"; expected.groups[DEFAULT_NAME].removeExpr = "#done"; const settings = await Data.constructSettings(plugin, initial); expect(settings).toEqual(expected); }); test("Task Marker: User configuration", async () => { const initial = { removeExpression: "", appendDateFormat: "", appendTextFormatMark: "", appendTextFormatMarkRow2: "", appendTextFormatCreation: "", appendTextFormatAppend: " YYYY-MM-DD", appendRemoveAllTasks: false, incompleteTaskValues: " /ib?>", incompleteTaskValuesRow2: "I!", cycleTaskValues: " x/>-", onlyLowercaseX: false, supportCanceledTasks: true, previewOnClick: false, rightClickComplete: true, rightClickMark: true, rightClickCycle: true, rightClickCreate: true, rightClickAppend: true, rightClickResetTask: false, rightClickResetAll: false, rightClickToggleAll: false, }; const expected: TaskCollectorSettings = Object.assign( {}, JSON.parse(JSON.stringify(DEFAULT_MIGRATION)), { markCycle: " x/>-", markCycleRemoveTask: false, previewClickModal: false, collectionEnabled: false, contextMenu: { markTask: true, collectTasks: false, resetTask: false, resetAllTasks: false, }, groups: { default: { ...GROUP_DEFAULT, marks: " />?bi", }, complete: { ...GROUP_COMPLETE, marks: "-Xx", useContextMenu: true, }, "group-2": { ...GROUP_DEFAULT, name: "group-2", marks: "!I", }, text: { ...GROUP_DEFAULT, name: "text", marks: TEXT_ONLY_MARK, appendDateFormat: " YYYY-MM-DD", useContextMenu: true, }, }, }, ); console.log(expected); const settings = await Data.constructSettings(plugin, initial); expect(settings).toEqual(expected); }); ================================================ FILE: test/generatedRegExp.test.ts ================================================ import type { TaskCollectorSettings } from "../src/@types/settings"; import { COMPLETE_NAME, DEFAULT_NAME, DEFAULT_SETTINGS, TEXT_ONLY_MARK, TEXT_ONLY_NAME, } from "../src/taskcollector-Constants"; import { Data } from "../src/taskcollector-Data"; import { TaskCollector } from "../src/taskcollector-TaskCollector"; jest.mock("obsidian", () => ({ App: jest.fn().mockImplementation(), moment: jest.requireActual("moment-obsidian"), })); let tc = new TaskCollector(); let config: TaskCollectorSettings = JSON.parse( JSON.stringify(DEFAULT_SETTINGS), ); afterEach(() => { tc = new TaskCollector(); config = JSON.parse(JSON.stringify(DEFAULT_SETTINGS)); }); test("Match blockquotes / callouts", () => { tc.init(config); expect("> - [x] ").toMatch(tc.anyTaskMark); expect("> > - [x] ").toMatch(tc.anyTaskMark); expect("> - [ ] ").toMatch(tc.anyTaskMark); expect("> > - [ ] ").toMatch(tc.anyTaskMark); }); test("Match specified removal patterns", () => { config.groups[COMPLETE_NAME].removeExpr = "#(task|todo)"; tc.init(config); expect("- [ ] something #todo").toMatch(tc.cache.removeExpr[COMPLETE_NAME]); expect("- [ ] something #task").toMatch(tc.cache.removeExpr[COMPLETE_NAME]); expect("- [ ] something #task #todo").toMatch( tc.cache.removeExpr[COMPLETE_NAME], ); expect("- [ ] something else").not.toMatch( tc.cache.removeExpr[COMPLETE_NAME], ); // Remove text when transitioning to 'x' expect(tc.updateLineText("- [ ] something #todo", "x")).toEqual( "- [x] something", ); expect(tc.updateLineText("- [>] something #todo", "x")).toEqual( "- [x] something", ); // text not removed when transitioning to something else (default) expect(tc.updateLineText("- [x] something #todo", " ")).toEqual( "- [ ] something #todo", ); expect(tc.updateLineText("- [>] something #todo", "m")).toEqual( "- [m] something #todo", ); }); test("Match multiple string removal pattern (more complicated regex)", () => { config.groups[COMPLETE_NAME].removeExpr = "((#(next|waiting|someday)|\\\{\\d{4}-\\d{2}-\\d{2}\\\})\\s*)+"; tc.init(config); expect("- [ ] something #next").toMatch(tc.cache.removeExpr[COMPLETE_NAME]); expect("- [ ] something #waiting").toMatch(tc.cache.removeExpr[COMPLETE_NAME]); expect("- [ ] something #someday").toMatch(tc.cache.removeExpr[COMPLETE_NAME]); expect("- [ ] something {2025-10-25}").toMatch(tc.cache.removeExpr[COMPLETE_NAME]); expect("- [ ] something #next {2025-10-25}").toMatch(tc.cache.removeExpr[COMPLETE_NAME]); expect(tc.updateLineText("- [ ] something #next", "x")).toEqual( "- [x] something", ); expect(tc.updateLineText("- [ ] something {2025-10-25}", "x")).toEqual( "- [x] something", ); expect(tc.updateLineText("- [ ] something #next {2025-10-25}", "x")).toEqual( "- [x] something", ); }); describe("Set an append date", () => { test("YYYY-MM-DD append string", () => { config.groups[COMPLETE_NAME].appendDateFormat = "YYYY-MM-DD"; tc.init(config); // make sure various strings match the undo expression expect("- [x] something 2021-08-24").toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("- [x] something 2021-08-24 ").toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("- [x] something (2021-08-24)").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("- [x] 2021-08-24 something else").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); const completed = tc.updateLineText("- [ ] something #todo", "x"); expect(completed).toMatch(tc.cache.undoExpr[COMPLETE_NAME]); expect(tc.updateLineText(completed, " ")).toEqual( "- [ ] something #todo", ); }); test("(YYYY-MM-DD) append string", () => { config.groups[COMPLETE_NAME].appendDateFormat = "[(]YYYY-MM-DD[)]"; tc.init(config); expect("- [x] something (2021-08-24)").toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("- [x] something 2021-08-24").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("- [x] 2021-08-24 something else").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); const completed = tc.updateLineText("- [ ] something #todo", "x"); expect(completed).toMatch(tc.cache.undoExpr[COMPLETE_NAME]); expect(tc.updateLineText(completed, " ")).toEqual( "- [ ] something #todo", ); }); test("(D MMM, YYYY) append string", () => { config.groups[COMPLETE_NAME].appendDateFormat = "[(]D MMM, YYYY[)]"; tc.init(config); expect("- [x] something (6 Oct, 2021)").toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("- [x] something 6 Oct, 2021").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("- [x] 6 Oct, 2021, something else").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("- [x] 2021-10-06 something else").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); const completed = tc.updateLineText("- [ ] something #todo", "x"); expect(completed).toMatch(tc.cache.undoExpr[COMPLETE_NAME]); expect(tc.updateLineText(completed, " ")).toEqual( "- [ ] something #todo", ); }); test("DD MMM, YYYY append string", () => { config.groups[COMPLETE_NAME].appendDateFormat = "DD MMM, YYYY"; tc.init(config); expect("- [x] something 06 Oct, 2021").toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("- [x] something (6 Oct, 2021)").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("- [x] something 6 Oct, 2021").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("- [x] 6 Oct, 2021, something else").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("- [x] 2021-10-06 something else").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); const completed = tc.updateLineText("- [ ] something #todo", "x"); expect(completed).toMatch(tc.cache.undoExpr[COMPLETE_NAME]); expect(tc.updateLineText(completed, " ")).toEqual( "- [ ] something #todo", ); }); test("[(completed on ]D MMM, YYYY[)] append string", () => { config.groups[COMPLETE_NAME].appendDateFormat = "[(completed on ]D MMM, YYYY[)]"; tc.init(config); expect("+ [x] something (completed on 6 Oct, 2021)").toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("+ [x] something (6 Oct, 2021)").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("+ [x] something 6 Oct, 2021").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("+ [x] 6 Oct, 2021, something else").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("+ [x] 2021-10-06 something else").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); const completed = tc.updateLineText("+ [ ] something #todo", "x"); expect(completed).toMatch(tc.cache.undoExpr[COMPLETE_NAME]); expect(tc.updateLineText(completed, " ")).toEqual( "+ [ ] something #todo", ); }); test("[✅ ]YYYY-MM-DDTHH:mm append string", () => { config.groups[COMPLETE_NAME].appendDateFormat = "[✅ ]YYYY-MM-DDTHH:mm"; tc.init(config); expect("* [x] something ✅ 2021-10-07T13:55").toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("* [x] something (6 Oct, 2021)").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("* [x] something 6 Oct, 2021").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("* [x] 6 Oct, 2021, something else").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("* [x] 2021-10-06 something else").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); const completed = tc.updateLineText("* [ ] something #todo", "x"); expect(completed).toMatch(tc.cache.undoExpr[COMPLETE_NAME]); expect(tc.updateLineText(completed, " ")).toEqual( "* [ ] something #todo", ); }); test("Dataview annotated string [completion::2021-08-15]", () => { config.groups[COMPLETE_NAME].appendDateFormat = "[[completion::]YYYY-MM-DD[]]"; tc.init(config); expect("- [x] I finished this on [completion::2021-08-15]").toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("- [x] something (6 Oct, 2021)").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("- [x] something 6 Oct, 2021").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("- [x] 6 Oct, 2021, something else").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); expect("- [x] 2021-10-06 something else").not.toMatch( tc.cache.undoExpr[COMPLETE_NAME], ); const completed = tc.updateLineText("- [ ] something #todo", "x"); expect(completed).toMatch(tc.cache.undoExpr[COMPLETE_NAME]); expect(tc.updateLineText(completed, " ")).toEqual( "- [ ] something #todo", ); }); test("Correctly insert annotation ahead of block reference", () => { config.groups[COMPLETE_NAME].appendDateFormat = "[[completion::]YYYY-MM-DD[]]"; tc.init(config); expect("- [ ] something (6 Oct, 2021) ^your-ID-1").toMatch(tc.blockRef); expect( "- [x] I finished this on [completion::2021-08-15] ^your-ID-1", ).toMatch(tc.blockRef); expect( "- [x] I finished this on [completion::2021-08-15] ^your-ID-1", ).toMatch(tc.cache.undoExpr[COMPLETE_NAME]); const completed = tc.updateLineText( "- [ ] something #todo ^your-ID-1", "x", ); expect(completed).toMatch( /- \[x\] something #todo \[completion::\d+-\d+-\d+\] \^your-ID-1/, ); expect(completed).toMatch(tc.cache.undoExpr[COMPLETE_NAME]); expect(tc.updateLineText(completed, " ")).toEqual( "- [ ] something #todo ^your-ID-1", ); }); test("Preserve continuation with strict line-break", () => { config.groups[COMPLETE_NAME].appendDateFormat = "[(]YYYY-MM-DD[)]"; tc.init(config); const completed = tc.updateLineText("- [ ] something ", "x"); expect(completed).toMatch(/- \[x\] something \(\d+-\d+-\d+\) /); }); test("Preserve continuation with strict line-break across reset", () => { config.groups[DEFAULT_NAME].marks += ">"; config.groups[DEFAULT_NAME].appendDateFormat = "[(]YYYY-MM-DD[)]"; config.groups[COMPLETE_NAME].appendDateFormat = "[(]YYYY-MM-DD[)]"; tc.init(config); const completed = tc.updateLineText("- [ ] something ", "x"); expect(completed).toMatch(tc.cache.undoExpr[COMPLETE_NAME]); expect(completed).toMatch(/^- \[x\] something\s+\(\d+-\d+-\d+\) $/); const forwarded = tc.updateLineText(completed, ">"); expect(forwarded).toMatch(tc.cache.undoExpr[COMPLETE_NAME]); expect(forwarded).toMatch(/^- \[>\] something\s+\(\d+-\d+-\d+\) $/); expect(tc.updateLineText(forwarded, "x")).toMatch( /^- \[x\] something\s+\(\d+-\d+-\d+\) $/, ); }); test("Deal with lots of square brackets", () => { config.groups[DEFAULT_NAME].marks += ">"; config.groups[COMPLETE_NAME].appendDateFormat = "[[completion::]YYYY-MM-DD[] ✅ ]YYYY-MM-DD[T]HH:mm"; tc.init(config); const completed = tc.updateLineText("- [ ] something", "x"); expect(completed).toMatch(tc.cache.undoExpr[COMPLETE_NAME]); expect(completed).toMatch( /^- \[x\] something \[completion::\d+-\d+-\d+\] ✅ \d+-\d+-\d+T\d+:\d+$/, ); expect(tc.updateLineText(completed, " ")).toEqual("- [ ] something"); }); test("Mark plain text", () => { Data.createSettingsGroup(config.groups, TEXT_ONLY_NAME, { marks: TEXT_ONLY_MARK, removeExpr: "#(task|todo)", appendDateFormat: "[(]D MMM, YYYY[)]", }); tc.init(config); const marked = tc.updateLineText("something ", ""); expect(marked).toMatch(tc.cache.undoExpr[TEXT_ONLY_NAME]); }); }); test("Apply text stripping/reset rules between task groups", () => { config.groups[DEFAULT_NAME].marks += ">"; config.groups[DEFAULT_NAME].appendDateFormat = "[(]D MMM, YYYY[)]"; config.groups[COMPLETE_NAME].appendDateFormat = "[(]YYYY-MM-DD[)]"; config.groups[DEFAULT_NAME].removeExpr = "#done"; config.groups[COMPLETE_NAME].removeExpr = "#todo"; tc.init(config); const completed = tc.updateLineText("- [ ] something #todo", "x"); expect(completed).not.toMatch(tc.cache.removeExpr[COMPLETE_NAME]); expect(completed).toMatch(tc.cache.undoExpr[COMPLETE_NAME]); expect(completed).toMatch(/^- \[x\] something \(\d+-\d+-\d+\)$/); const changed = tc.updateLineText( "- [x] something #done (2022-04-26)", ">", ); expect(changed).not.toMatch(tc.cache.removeExpr[DEFAULT_NAME]); expect(changed).toMatch(tc.cache.undoExpr[DEFAULT_NAME]); expect(changed).toMatch(/^- \[>\] something\s+\(\d+ \S+, \d+\)$/); expect(tc.updateLineText(changed, "x")).toEqual(completed); expect(tc.updateLineText(changed, "-")).toMatch( /^- \[-\] something \(\d+ \S+, \d+\)$/, ); }); ================================================ FILE: test/markTasks.test.ts ================================================ import type { TaskCollectorSettings } from "../src/@types/settings"; import { COMPLETE_NAME, DEFAULT_COLLECTION, DEFAULT_NAME, DEFAULT_SETTINGS, } from "../src/taskcollector-Constants"; import { Direction, TaskCollector } from "../src/taskcollector-TaskCollector"; jest.mock("obsidian", () => ({ App: jest.fn().mockImplementation(), moment: jest.requireActual("moment-obsidian"), })); let tc = new TaskCollector(); let config: TaskCollectorSettings = JSON.parse( JSON.stringify(DEFAULT_SETTINGS), ); afterEach(() => { tc = new TaskCollector(); config = JSON.parse(JSON.stringify(DEFAULT_SETTINGS)); }); test("Test default settings", () => { tc.init(config); expect(tc.cache.removeExpr[COMPLETE_NAME]).toBeUndefined(); expect(tc.cache.undoExpr[COMPLETE_NAME]).toBeUndefined(); expect(tc.updateLineText("- [ ] something", "x")).toEqual( "- [x] something", ); expect(tc.updateLineText("- [x] something", "-")).toEqual( "- [-] something", ); expect(tc.updateLineText("- [-] something", ">")).toEqual( "- [>] something", ); expect(tc.updateLineText("- [>] something", " ")).toEqual( "- [ ] something", ); }); test("Test asterisk task list", () => { tc.init(config); expect(tc.cache.removeExpr[COMPLETE_NAME]).toBeUndefined(); expect(tc.cache.undoExpr[COMPLETE_NAME]).toBeUndefined(); expect(tc.updateLineText("* [ ] something", "x")).toEqual( "* [x] something", ); expect(tc.updateLineText("* [x] something", "-")).toEqual( "* [-] something", ); expect(tc.updateLineText("* [-] something", ">")).toEqual( "* [>] something", ); expect(tc.updateLineText("* [>] something", " ")).toEqual( "* [ ] something", ); }); test("Test plus task list", () => { tc.init(config); expect(tc.cache.removeExpr[COMPLETE_NAME]).toBeUndefined(); expect(tc.cache.undoExpr[COMPLETE_NAME]).toBeUndefined(); expect(tc.updateLineText("+ [ ] something", "x")).toEqual( "+ [x] something", ); expect(tc.updateLineText("+ [x] something", "-")).toEqual( "+ [-] something", ); expect(tc.updateLineText("+ [-] something", ">")).toEqual( "+ [>] something", ); expect(tc.updateLineText("+ [>] something", " ")).toEqual( "+ [ ] something", ); }); test("Test numbered tasks", () => { tc.init(config); expect(tc.cache.removeExpr[COMPLETE_NAME]).toBeUndefined(); expect(tc.cache.undoExpr[COMPLETE_NAME]).toBeUndefined(); expect(tc.updateLineText("1. [ ] something", "x")).toEqual( "1. [x] something", ); expect(tc.updateLineText("11. [x] something", "-")).toEqual( "11. [-] something", ); expect(tc.updateLineText("111. [-] something", ">")).toEqual( "111. [>] something", ); expect(tc.updateLineText("1111. [>] something", " ")).toEqual( "1111. [ ] something", ); }); test("Correctly mark items in a selection", () => { tc.init(config); const start = "- [ ] one\n- [>] two\n- [-] three\n- [x] four"; expect(tc.markSelectedTask(start, "x", [0, 1, 2, 3])).toEqual( "- [x] one\n- [x] two\n- [x] three\n- [x] four", ); expect(tc.markSelectedTask(start, "-", [0, 1, 2, 3])).toEqual( "- [-] one\n- [-] two\n- [-] three\n- [-] four", ); expect(tc.markSelectedTask(start, ">", [0, 1, 2, 3])).toEqual( "- [>] one\n- [>] two\n- [>] three\n- [>] four", ); expect(tc.markSelectedTask(start, " ", [0, 1, 2, 3])).toEqual( "- [ ] one\n- [ ] two\n- [ ] three\n- [ ] four", ); }); test("Remove checkbox from line with backspace from modal", () => { config.groups[COMPLETE_NAME].collection = Object.assign( {}, DEFAULT_COLLECTION, ); config.groups[COMPLETE_NAME].collection.removeCheckbox = true; tc.init(config); const completed = "- [x] something [x]"; const incomplete = "- [ ] something [x]"; const listItem = "- something [x]"; expect(tc.updateLineText(completed, "Backspace")).toEqual(listItem); expect(tc.updateLineText(incomplete, "Backspace")).toEqual(listItem); }); test("Create and Mark a normal list item", () => { config.groups[DEFAULT_NAME].marks += ">"; config.groups[COMPLETE_NAME].marks += "-"; config.groups[COMPLETE_NAME].removeExpr = "#(task|todo)"; tc.init(config); const start = "- one #task"; expect(tc.markSelectedTask(start, "x", [0])).toEqual("- [x] one"); expect(tc.markSelectedTask(start, "-", [0])).toEqual("- [-] one"); expect(tc.markSelectedTask(start, ">", [0])).toEqual("- [>] one #task"); expect(tc.markSelectedTask(start, " ", [0])).toEqual("- [ ] one #task"); }); test("Mark tasks within a callout", () => { tc.init(config); expect(tc.updateLineText("> - [ ] something", "-")).toEqual( "> - [-] something", ); expect(tc.updateLineText("> - [-] something", " ")).toEqual( "> - [ ] something", ); expect(tc.updateLineText("> 10. [-] something", " ")).toEqual( "> 10. [ ] something", ); expect(tc.updateLineText("> > - [x] something", "x")).toEqual( "> > - [x] something", ); expect(tc.updateLineText("> > - [x] something", " ")).toEqual( "> > - [ ] something", ); expect(tc.updateLineText("> > 1. [x] something", " ")).toEqual( "> > 1. [ ] something", ); }); describe("Mark lines that are not tasks", () => { test("Mark non-task/list lines when convert non-list lines is true", () => { config.convertEmptyLines = true; tc.init(config); expect(tc.updateLineText("something", "x")).toEqual("- [x] something"); }); test("Use indent for non-task/list lines when convertEmptyLines is true", () => { config.convertEmptyLines = true; tc.init(config); expect(tc.updateLineText("\tsomething", "x")).toEqual( "\t- [x] something", ); }); test("Do not mark non-task/list lines when convert non-list lines is false", () => { tc.init(config); expect(tc.updateLineText("something", "-")).toEqual("something"); }); test("Mark empty lines when convertEmptyLines is true", () => { config.convertEmptyLines = true; tc.init(config); expect(tc.updateLineText("", "x")).toEqual("- [x]"); }); test("Accomodate callouts for non-task/list lines when convertEmptyLines is true", () => { config.convertEmptyLines = true; tc.init(config); expect(tc.updateLineText("> something", "x")).toEqual( "> - [x] something", ); }); }); describe("Task mark cycle", () => { test("Mark tasks forward in a cycle (next)", () => { config.markCycle = "abc"; tc.init(config); expect(tc.markInCycle("- [ ] something", Direction.NEXT, [0])).toEqual( "- [a] something", ); expect(tc.markInCycle("- [a] something", Direction.NEXT, [0])).toEqual( "- [b] something", ); expect(tc.markInCycle("- [b] something", Direction.NEXT, [0])).toEqual( "- [c] something", ); expect(tc.markInCycle("- [c] something", Direction.NEXT, [0])).toEqual( "- [a] something", ); }); test("Mark tasks backward in a cycle (prev)", () => { config.markCycle = "abc"; tc.init(config); expect(tc.markInCycle("- [ ] something", Direction.PREV, [0])).toEqual( "- [c] something", ); expect(tc.markInCycle("- [a] something", Direction.PREV, [0])).toEqual( "- [c] something", ); expect(tc.markInCycle("- [b] something", Direction.PREV, [0])).toEqual( "- [a] something", ); expect(tc.markInCycle("- [c] something", Direction.PREV, [0])).toEqual( "- [b] something", ); }); test("Mark lines as tasks in a cycle", () => { config.markCycle = "abc"; tc.init(config); expect(tc.markInCycle("- something", Direction.NEXT, [0])).toEqual( "- [a] something", ); expect(tc.markInCycle("- something", Direction.PREV, [0])).toEqual( "- [c] something", ); }); test("Mark tasks forward in a cycle (next) include remove task", () => { config.markCycle = " ab§"; config.markCycleRemoveTask = true; tc.init(config); expect(tc.markInCycle("- something", Direction.NEXT, [0])).toEqual( "- [ ] something", ); expect(tc.markInCycle("- [ ] something", Direction.NEXT, [0])).toEqual( "- [a] something", ); expect(tc.markInCycle("- [a] something", Direction.NEXT, [0])).toEqual( "- [b] something", ); expect(tc.markInCycle("- [b] something", Direction.NEXT, [0])).toEqual( "- something", ); }); test("Mark tasks backward in a cycle (prev) include remove task", () => { config.markCycle = " ab§"; config.markCycleRemoveTask = true; tc.init(config); expect(tc.markInCycle("- [b] something", Direction.PREV, [0])).toEqual( "- [a] something", ); expect(tc.markInCycle("- [a] something", Direction.PREV, [0])).toEqual( "- [ ] something", ); expect(tc.markInCycle("- [ ] something", Direction.PREV, [0])).toEqual( "- something", ); expect(tc.markInCycle("- something", Direction.PREV, [0])).toEqual( "- [b] something", ); }); }); ================================================ FILE: test/mocks/moment-obsidian.d.ts ================================================ declare module "moment-obsidian" { import * as moment from "moment"; export = moment; } ================================================ FILE: test/mocks/obsidian.ts ================================================ import { Workspace, Vault, MetadataCache, FileManager, UserEvent } from "obsidian"; import moment from "moment-obsidian"; export { moment }; export const activeWindow = window; export class App { /** @public */ workspace: Workspace; /** @public */ vault: Vault; /** @public */ metadataCache: MetadataCache; /** @public */ fileManager: FileManager; /** * The last known user interaction event, to help commands find out what modifier keys are pressed. * @public */ lastEvent: UserEvent | null; } ================================================ FILE: test/moveCompletedItem.test.ts ================================================ import type { TaskCollectorSettings } from "../src/@types/settings"; import { COMPLETE_NAME, DEFAULT_COLLECTION, DEFAULT_NAME, DEFAULT_SETTINGS, GROUP_COMPLETE, } from "../src/taskcollector-Constants"; import { TaskCollector } from "../src/taskcollector-TaskCollector"; jest.mock("obsidian", () => ({ App: jest.fn().mockImplementation(), moment: jest.requireActual("moment-obsidian"), })); let tc = new TaskCollector(); const begin = JSON.parse(JSON.stringify(DEFAULT_SETTINGS)); begin.collectionEnabled = true; begin.groups[COMPLETE_NAME].collection = JSON.parse( JSON.stringify(DEFAULT_COLLECTION), ); let config: TaskCollectorSettings = JSON.parse(JSON.stringify(begin)); afterEach(() => { tc = new TaskCollector(); config = JSON.parse(JSON.stringify(begin)); // reset }); test("Test move with collection disabled (no change)", () => { config.collectionEnabled = false; tc.init(config); const text = "- [x] Complete"; const result = tc.moveAllTasks(text); expect(result).toEqual("- [x] Complete"); }); describe("Test move with collection enabled", () => { test("No completed items -> Log section created", () => { tc.init(config); const text = "- [ ] Incomplete"; const result = tc.moveAllTasks(text); expect(result).toEqual("- [ ] Incomplete\n\n## Log\n"); }); test("No completed items -> Log section created (preserve continuation)", () => { tc.init(config); const text = "a \n text continuation"; const result = tc.moveAllTasks(text); expect(result).toEqual("a \n text continuation\n\n## Log\n"); }); test("Move completed tasks", () => { config.groups[COMPLETE_NAME].marks += "-"; tc.init(config); const start = "" + "- [ ] one\n" + "- [>] two\n" + "- [-] three\n" + "- [x] four\n"; const result = "" + "- [ ] one\n" + "- [>] two\n" + "\n" + "## Log\n" + "- [-] three\n" + "- [x] four\n"; expect(tc.moveAllTasks(start)).toEqual(result); }); test("Move completed tasks with text continuation", () => { tc.init(config); const start = "\n" + "- [ ] An incomplete item\n" + "- [x] a \n" + " text continuation \n" + " \n" + " Including a longer paragraph in the same bullet\n" + "- [ ] An incomplete item\n"; const result = "\n" + "- [ ] An incomplete item\n" + "- [ ] An incomplete item\n" + "\n" + "## Log\n" + "- [x] a \n" + " text continuation \n" + " \n" + " Including a longer paragraph in the same bullet\n"; expect(tc.moveAllTasks(start)).toEqual(result); }); test("Move completed tasks within a callout", () => { tc.init(config); const start = "" + "> - [ ] Another item\n" + "> - [x] This line ends with two spaces \n" + "> which allows text to wrap using strict markdown line wrapping syntax. This line should move, too. \n" + ">\n" + "> This is also how you support list items with multiple paragraphs (leading whitespace indent), and it should travel with the previous list item.\n" + ">\n" + "> - [ ] Another item"; const result = "" + "> - [ ] Another item\n" + "> - [ ] Another item\n" + "\n" + "## Log\n" + "> - [x] This line ends with two spaces \n" + "> which allows text to wrap using strict markdown line wrapping syntax. This line should move, too. \n" + ">\n" + "> This is also how you support list items with multiple paragraphs (leading whitespace indent), and it should travel with the previous list item.\n" + ">\n"; expect(tc.moveAllTasks(start)).toEqual(result); }); test("Move completed tasks with associated callout", () => { tc.init(config); const start = "" + "- [ ] An incomplete item\n" + "- [x] The nested quote should move with the item\n" + " > [!note]\n" + " > Nested blockquotes associated with it would also be moved.\n" + "- [ ] A subsequent item should not be moved\n"; const result = "" + "- [ ] An incomplete item\n" + "- [ ] A subsequent item should not be moved\n" + "\n" + "## Log\n" + "- [x] The nested quote should move with the item\n" + " > [!note]\n" + " > Nested blockquotes associated with it would also be moved.\n"; expect(tc.moveAllTasks(start)).toEqual(result); }); }); test("Test move lists with mixed completion", () => { tc.init(config); const start = "" + "- [x] This line ends with two spaces \n" + " which allows text to wrap using strict markdown line wrapping syntax. This line should move, too. \n" + "\n" + " This is also how you support list items with multiple paragraphs (leading whitespace indent), and it should travel with the previous list item.\n" + "- [x] If this item is completed,\n" + " any wrapped text like this should also be moved, as it is indented\n" + " - [x] If there are nested bullets, it should all stay together. \n" + " - [ ] This is where things get messy. If this task remained incomplete, it would stay behind\n" + "- [x] If this task is completed,\n" + " > Nested blockquotes associated with it would also be moved.\n"; const result = "" + " - [ ] This is where things get messy. If this task remained incomplete, it would stay behind\n" + "\n" + "## Log\n" + "- [x] This line ends with two spaces \n" + " which allows text to wrap using strict markdown line wrapping syntax. This line should move, too. \n" + "\n" + " This is also how you support list items with multiple paragraphs (leading whitespace indent), and it should travel with the previous list item.\n" + "- [x] If this item is completed,\n" + " any wrapped text like this should also be moved, as it is indented\n" + " - [x] If there are nested bullets, it should all stay together. \n" + "- [x] If this task is completed,\n" + " > Nested blockquotes associated with it would also be moved.\n"; expect(tc.moveAllTasks(start)).toEqual(result); }); describe("Test move with multiple sections", () => { beforeEach(() => { config.groups["deferred"] = { ...JSON.parse(JSON.stringify(GROUP_COMPLETE)), marks: ">", collection: { areaHeading: "## Deferred", removeCheckbox: false, }, }; }); test("No completed or deferred items -> Log & Deferred sections created", () => { tc.init(config); const text = "- [ ] Incomplete"; const result = tc.moveAllTasks(text); expect(result).toEqual("- [ ] Incomplete\n\n## Deferred\n\n## Log\n"); }); test("Move completed tasks between sections", () => { config.groups[DEFAULT_NAME].collection = { areaHeading: "## To Do", removeCheckbox: false, }; tc.init(config); const start = "- [ ] i1\n" + "- [x] one\n" + "- [>] two\n" + "\n" + "## To Do\n" + "\n" + "## Log\n" + "- [ ] i2\n" + "- [x] three\n" + "- [>] four\n" + "\n" + "## Deferred\n" + "- [ ] i3\n" + "- [x] five\n" + "- [>] six\n" + ""; const result = "\n" + "## To Do\n" + "- [ ] i1\n" + "- [ ] i2\n" + "- [ ] i3\n" + "\n" + "## Log\n" + "- [x] one\n" + "- [x] five\n" + "- [x] three\n" + "\n" + "## Deferred\n" + "- [>] two\n" + "- [>] four\n" + "- [>] six\n" + ""; expect(tc.moveAllTasks(start)).toEqual(result); }); test("Move duplicate section", () => { tc.init(config); const start = "- [ ] i1\n" + "- [x] one\n" + "- [>] two\n" + "\n" + "## Deferred\n" + "\n" + "## Log\n" + "- [x] three\n" + "- [>] four\n" + "\n" + "## Deferred\n" + "- [x] five\n" + "- [>] six"; const result = "- [ ] i1\n" + "\n" + "## Deferred\n" + "- [>] two\n" + "\n" + "## Log\n" + "- [x] one\n" + "- [x] five\n" + "- [x] three\n" + "\n" + "## Deferred\n" + "- [>] four\n" + "- [>] six"; expect(tc.moveAllTasks(start)).toEqual(result); }); test("Move unmarked items with no loss", () => { // Ensure that unmarked items are not lost when moving. Issue #262 config.groups[DEFAULT_NAME].collection = { areaHeading: "## 1 - ToDos", removeCheckbox: false, }; config.groups[COMPLETE_NAME].collection = { areaHeading: "## 9 - Done √", removeCheckbox: false, }; config.groups["deferred"].collection = { areaHeading: "## 2 - Later >", removeCheckbox: false, }; tc.init(config); const start = "## 1 - ToDos\n" + "- [ ] 1\n" + "- [ ] 2\n" + "- [ ] 3\n" + "## 2 - Later >\n" + "\n" + "## 9 - Done √\n" + "- [ ] aa\n" + "- [ ] bb\n" + "- [ ] cc"; const result = "## 1 - ToDos\n" + "- [ ] aa\n" + "- [ ] bb\n" + "- [ ] cc\n" + "- [ ] 1\n" + "- [ ] 2\n" + "- [ ] 3\n" + "## 2 - Later >\n" + "\n" + "## 9 - Done √"; expect(tc.moveAllTasks(start)).toEqual(result); }); test("Move completed tasks around skipped section", () => { config.groups[COMPLETE_NAME].marks += "-"; config.skipSectionMatch = "# ❧ "; tc.init(config); const start = "" + "- [ ] one\n" + "- [>] two\n" + "## ❧ Ignore me\n" + "- [-] three\n" + "## ❧ Ignore me twice\n" + "- [x] four\n"; const result = "" + "- [ ] one\n" + "## ❧ Ignore me\n" + "- [-] three\n" + "## ❧ Ignore me twice\n" + "- [x] four\n" + "\n" + "## Deferred\n" + "- [>] two\n" + "\n" + "## Log\n"; expect(tc.moveAllTasks(start)).toEqual(result); }); }); ================================================ FILE: test/resetAllTasks.test.ts ================================================ import type { TaskCollectorSettings } from "../src/@types/settings"; import { COMPLETE_NAME, DEFAULT_COLLECTION, DEFAULT_SETTINGS, GROUP_COMPLETE, } from "../src/taskcollector-Constants"; import { TaskCollector } from "../src/taskcollector-TaskCollector"; jest.mock("obsidian", () => ({ App: jest.fn().mockImplementation(), moment: jest.requireActual("moment-obsidian"), })); let tc = new TaskCollector(); const begin = JSON.parse(JSON.stringify(DEFAULT_SETTINGS)); begin.collectionEnabled = true; begin.groups[COMPLETE_NAME].collection = JSON.parse( JSON.stringify(DEFAULT_COLLECTION), ); let config: TaskCollectorSettings = JSON.parse(JSON.stringify(begin)); const start = "" + "- [ ] i1\n" + "- [x] one\n" + "- [>] two\n" + "\n" + "## To Do\n" + "\n" + "## Log\n" + "- [ ] i2\n" + "- [x] three\n" + "- [>] four\n" + "\n" + "## Deferred\n" + "- [ ] i3\n" + "- [x] five\n" + "- [>] six\n" + ""; afterEach(() => { tc = new TaskCollector(); config = JSON.parse(JSON.stringify(begin)); // reset }); test("Reset with collection disabled", () => { config.collectionEnabled = false; tc.init(config); // all items are reset const expected = "" + "- [ ] i1\n" + "- [ ] one\n" + "- [ ] two\n" + "\n" + "## To Do\n" + "\n" + "## Log\n" + "- [ ] i2\n" + "- [ ] three\n" + "- [ ] four\n" + "\n" + "## Deferred\n" + "- [ ] i3\n" + "- [ ] five\n" + "- [ ] six\n" + ""; const result = tc.resetAllTasks(start); expect(result).toEqual(expected); }); test("Reset with collection enabled", () => { config.groups["deferred"] = { ...JSON.parse(JSON.stringify(GROUP_COMPLETE)), marks: ">", collection: { areaHeading: "## Deferred", removeCheckbox: false, }, }; tc.init(config); // items in "completion" areas are left alone const expected = "" + "- [ ] i1\n" + "- [ ] one\n" + "- [ ] two\n" + "\n" + "## To Do\n" + "\n" + "## Log\n" + "- [ ] i2\n" + "- [x] three\n" + "- [>] four\n" + "\n" + "## Deferred\n" + "- [ ] i3\n" + "- [x] five\n" + "- [>] six\n" + ""; const result = tc.resetAllTasks(start); expect(result).toEqual(expected); }); test("Reset with collection enabled and skipped section", () => { config.groups["deferred"] = { ...JSON.parse(JSON.stringify(GROUP_COMPLETE)), marks: ">", }; config.skipSectionMatch = "# ❧ "; tc.init(config); const startSkipped = "" + "- [ ] i1\n" + "- [x] one\n" + "- [>] two\n" + "\n" + "## To Do\n" + "\n" + "## Log\n" + "- [ ] i2\n" + "- [x] three\n" + "- [>] four\n" + "\n" + "## ❧ Deferred\n" + "- [ ] i3\n" + "- [x] five\n" + "- [>] six\n" + ""; // items in "completion" areas are left alone const expected = "" + "- [ ] i1\n" + "- [ ] one\n" + "- [ ] two\n" + "\n" + "## To Do\n" + "\n" + "## Log\n" + "- [ ] i2\n" + "- [x] three\n" + "- [>] four\n" + "\n" + "## ❧ Deferred\n" + "- [ ] i3\n" + "- [x] five\n" + "- [>] six\n" + ""; const result = tc.resetAllTasks(startSkipped); expect(result).toEqual(expected); }); ================================================ FILE: test/setup.ts ================================================ // jsdom doesn't provide activeWindow; point it at the test window (globalThis as typeof globalThis & { activeWindow: Window }).activeWindow = window; ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "target": "ES2022", "module": "nodenext", "allowJs": true, "noImplicitAny": true, "strictNullChecks": false, "strictPropertyInitialization": false, "moduleResolution": "nodenext", "esModuleInterop": true, "allowSyntheticDefaultImports": true, "importHelpers": true, "lib": [ "dom", "es5", "scripthost", "es2022" ], "types": [ "jest" ] }, "include": [ "**/*.ts" ] } ================================================ FILE: versions.json ================================================ { "0.1.0": "0.13.4", "1.2.0": "1.13.0" }