├── test-vault ├── Change.md ├── Delete.md ├── .obsidian │ ├── community-plugins.json │ └── plugins │ │ └── hot-reload │ │ ├── manifest.json │ │ └── main.js ├── Changelog.md └── Home.md ├── .github ├── FUNDING.yml ├── dependabot.yml ├── pull_request_template.md ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── release.yml │ └── codeql.yml ├── versions.json ├── .editorconfig ├── CODE_OF_CONDUCT.md ├── manifest.json ├── bunfig.toml ├── package.json ├── styles.css ├── eslint.config.mjs ├── tsconfig.json ├── .gitignore ├── LICENSE ├── CHANGELOG.md ├── CONTRIBUTING.md ├── .claude.md ├── src ├── suggest.ts ├── main.ts └── settings.ts ├── README.md └── bun.lock /test-vault/Change.md: -------------------------------------------------------------------------------- 1 | change me 2 | -------------------------------------------------------------------------------- /test-vault/Delete.md: -------------------------------------------------------------------------------- 1 | delete me 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [philoserf] 2 | buy_me_a_coffee: philoserf 3 | -------------------------------------------------------------------------------- /test-vault/.obsidian/community-plugins.json: -------------------------------------------------------------------------------- 1 | ["obsidian-vault-changelog", "hot-reload"] 2 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.2.0": "1.0.0", 3 | "1.1.0": "1.0.0", 4 | "1.0.0": "1.0.0", 5 | "0.1.0": "0.9.7" 6 | } 7 | -------------------------------------------------------------------------------- /test-vault/Changelog.md: -------------------------------------------------------------------------------- 1 | - 2025-03-05T1736 · [[Home]] 2 | - 2025-03-05T1736 · [[Delete]] 3 | - 2025-03-05T1736 · [[Change]] 4 | -------------------------------------------------------------------------------- /test-vault/Home.md: -------------------------------------------------------------------------------- 1 | ![[Changelog]] 2 | 3 | How to test. 4 | 5 | [[Add]] a file. See the result. 6 | [[Change]] a file. See the results. 7 | [[Delete]] a file. See the results. 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = tab 8 | indent_size = 2 9 | tab_width = 2 10 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | We are all human beings, being human. 4 | Treat each other with respect and decorum. 5 | Assume good intentions. 6 | Practice a **"Yes, and"** worldview. 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian-vault-changelog", 3 | "name": "Vault Changelog", 4 | "description": "Maintain a changelog of recently edited notes", 5 | "author": "Mark Ayers", 6 | "authorUrl": "https://philoserf.com", 7 | "fundingUrl": "https://buymeacoffee.com/philoserf", 8 | "minAppVersion": "1.0.0", 9 | "version": "1.2.0" 10 | } 11 | -------------------------------------------------------------------------------- /test-vault/.obsidian/plugins/hot-reload/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "hot-reload", 3 | "name": "Hot Reload", 4 | "author": "PJ Eby", 5 | "authorUrl": "https://github.com/pjeby", 6 | "version": "0.1.10", 7 | "minAppVersion": "0.15.9", 8 | "description": "Automatically reload in-development plugins when their files are changed", 9 | "isDesktopOnly": true 10 | } 11 | -------------------------------------------------------------------------------- /bunfig.toml: -------------------------------------------------------------------------------- 1 | [install] 2 | # Use the frozen-lockfile flag by default to avoid accidental lockfile updates 3 | frozen = true 4 | 5 | [test] 6 | # Configuration for bun test if you add tests later 7 | coverage = true 8 | 9 | [build] 10 | # Default build settings 11 | minify = true 12 | sourcemap = "external" 13 | target = "browser" 14 | format = "cjs" 15 | external = ["obsidian"] -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest a new feature 4 | title: "" 5 | labels: enhancement 6 | assignees: "" 7 | --- 8 | 9 | 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "bun build.mjs production", 4 | "dev": "bun build.mjs", 5 | "format": "bun prettier --write --ignore-unknown .", 6 | "lint": "bun run eslint" 7 | }, 8 | "devDependencies": { 9 | "@typescript-eslint/eslint-plugin": "^8.46.0", 10 | "@typescript-eslint/parser": "^8.46.0", 11 | "eslint": "^9.37.0", 12 | "eslint-config-prettier": "^10.1.8", 13 | "obsidian": "^1.10.0", 14 | "prettier": "^3.6.2", 15 | "typescript": "^5.9.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | .excluded-folders-list { 2 | margin-bottom: 1em; 3 | } 4 | 5 | .excluded-folder-item { 6 | display: flex; 7 | justify-content: space-between; 8 | align-items: center; 9 | background-color: var(--background-secondary); 10 | border-radius: 4px; 11 | padding: 4px 8px; 12 | margin-bottom: 6px; 13 | } 14 | 15 | .excluded-folder-remove { 16 | cursor: pointer; 17 | border: none; 18 | background: transparent; 19 | color: var(--text-muted); 20 | padding: 0 4px; 21 | font-size: 14px; 22 | } 23 | 24 | .excluded-folder-remove:hover { 25 | color: var(--text-error); 26 | } 27 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import typescriptEslint from "@typescript-eslint/eslint-plugin"; 2 | import tsParser from "@typescript-eslint/parser"; 3 | import prettier from "eslint-config-prettier"; 4 | 5 | export default [ 6 | { 7 | ignores: ["node_modules", "dist", "main.js"], 8 | }, 9 | { 10 | files: ["**/*.ts"], 11 | plugins: { 12 | "@typescript-eslint": typescriptEslint, 13 | }, 14 | languageOptions: { 15 | parser: tsParser, 16 | parserOptions: { 17 | project: "./tsconfig.json", 18 | }, 19 | }, 20 | rules: { 21 | ...typescriptEslint.configs.recommended.rules, 22 | ...prettier.rules, 23 | }, 24 | }, 25 | ]; 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // Language and Environment 4 | "lib": ["DOM", "ESNext"], 5 | "target": "ESNext", 6 | 7 | // Modules 8 | "module": "ESNext", 9 | "moduleResolution": "bundler", 10 | "verbatimModuleSyntax": true, 11 | "esModuleInterop": true, 12 | 13 | // Emit 14 | "noEmit": true, 15 | "declaration": false, 16 | 17 | // Type Checking 18 | "strict": true, 19 | "noFallthroughCasesInSwitch": true, 20 | "noUncheckedIndexedAccess": true, 21 | 22 | // Completeness 23 | "skipLibCheck": true, 24 | "forceConsistentCasingInFileNames": true 25 | }, 26 | "include": ["src/**/*.ts"] 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: ["*"] 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v6 11 | - uses: oven-sh/setup-bun@v2 12 | with: 13 | bun-version: latest 14 | 15 | - run: | 16 | bun install 17 | bun run build 18 | 19 | - name: Create Release and Upload Assets 20 | uses: softprops/action-gh-release@v2 21 | with: 22 | name: ${{ github.ref }} 23 | files: | 24 | main.js 25 | manifest.json 26 | styles.css 27 | fail_on_unmatched_files: true 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build artifacts 2 | **/*.js 3 | main.js.map 4 | 5 | # Dependencies 6 | node_modules/ 7 | 8 | # Bun 9 | .bun/ 10 | # bun.lock - We want to track the lock file 11 | 12 | # Test vault - plugins 13 | test-vault/.obsidian/plugins/* 14 | !test-vault/.obsidian/plugins/journals/.hotreload 15 | !test-vault/.obsidian/plugins/hot-reload/main.js 16 | !test-vault/.obsidian/plugins/hot-reload/manifest.json 17 | 18 | # Test vault - config files 19 | test-vault/.obsidian/app.json 20 | test-vault/.obsidian/appearance.json 21 | test-vault/.obsidian/core-plugins-migration.json 22 | test-vault/.obsidian/core-plugins.json 23 | test-vault/.obsidian/workspace.json 24 | 25 | # Test vault - markdown files 26 | test-vault/**/*.md 27 | !test-vault/Change.md 28 | !test-vault/Changelog.md 29 | !test-vault/Delete.md 30 | !test-vault/Home.md 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a bug 4 | title: "" 5 | labels: bug 6 | assignees: "" 7 | --- 8 | 9 | 28 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | schedule: 9 | - cron: "16 15 * * 1" 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze (${{ matrix.language }}) 14 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 15 | permissions: 16 | security-events: write 17 | 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | include: 22 | - language: javascript-typescript 23 | build-mode: none 24 | steps: 25 | - name: Checkout repository 26 | uses: actions/checkout@v6 27 | 28 | - name: Initialize CodeQL 29 | uses: github/codeql-action/init@v4 30 | with: 31 | languages: ${{ matrix.language }} 32 | build-mode: ${{ matrix.build-mode }} 33 | 34 | - name: Perform CodeQL Analysis 35 | uses: github/codeql-action/analyze@v4 36 | with: 37 | category: "/language:${{matrix.language}}" 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright 2025 by Mark Ayers 4 | Copyright 2020–2024 by Badr Bouslikhin 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Unreleased 4 | 5 | ## 1.2.0 6 | 7 | ### Added 8 | 9 | - Test vault for easier development and testing 10 | - Hot-reload plugin to test vault for seamless development 11 | - "Use wiki-links" setting to optionally disable wiki-link formatting (addresses #9) 12 | - "Changelog heading" setting to optionally prepend a heading to the changelog (addresses #4) 13 | 14 | ### Changed 15 | 16 | - Moved styles to external CSS file 17 | - Restructured documentation into separate files following GitHub standards 18 | - Improved README with use cases, intent description, and alternatives section (addresses #46) 19 | 20 | ### Maintenance 21 | 22 | - Improved internal documentation 23 | - Added Dependabot configuration for GitHub Actions 24 | - Updated all development dependencies to latest versions: 25 | - TypeScript 5.7.3 → 5.9.3 26 | - TypeScript ESLint parser 8.25.0 → 8.46.0 27 | - TypeScript ESLint plugin 8.25.0 → 8.46.0 28 | - ESLint 9.22.0 → 9.37.0 29 | - Prettier 3.5.3 → 3.6.2 30 | - eslint-config-prettier 10.1.1 → 10.1.8 31 | - Obsidian API 1.8.7 → 1.10.0 32 | 33 | ## 1.1.0 34 | 35 | - Added folder suggestion for the "Changelog path" 36 | - Added "Excluded folders" 37 | - Added input validation for "Datetime format" and "Max recent files" 38 | 39 | ## 1.0.0 40 | 41 | - Transferred to a new maintainer. 42 | - Fixed file creation bug. 43 | - Improved error messages for file creation failures. 44 | - Added date format customization. 45 | - Refactored code to align with Obsidian community guidelines. 46 | - Updated README with revised installation and usage instructions. 47 | - Added LICENSE file. 48 | 49 | ## 0.1.0 50 | 51 | - Initial release by Badr Bouslikhin. 52 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | 💡 Want to improve the plugin? Here's how you can help: 4 | 5 | - **Discussions**: [GitHub Discussions](https://github.com/philoserf/obsidian-vault-changelog/discussions) 6 | - **Bug Reports**: [Open an Issue](https://github.com/philoserf/obsidian-vault-changelog/issues) 7 | - **Feature Requests**: [Open a Pull Request](https://github.com/philoserf/obsidian-vault-changelog/pulls) 8 | - **Community**: [Obsidian Forum](https://forum.obsidian.md) | [Obsidian Discord](https://discord.gg/obsidianmd) 9 | 10 | ## Development 11 | 12 | This project uses Bun as the build tool: 13 | 14 | 1. Clone this repository `gh repo clone philoserf/obsidian-vault-changelog`. 15 | 2. Install Bun: [https://bun.sh/docs/installation](https://bun.sh/docs/installation) 16 | 3. Install dependencies: `bun install` 17 | 4. Format: `bun run format` 18 | 5. Lint: `bun run lint` 19 | 6. Development build with auto-refresh: `bun run dev` 20 | 7. Production build: `bun run build` 21 | 22 | ### Using the test-vault 23 | 24 | The project includes a test-vault for development: 25 | 26 | 1. Open the test-vault in Obsidian (`File > Open another vault > Open folder as vault` and select the `test-vault` directory) 27 | 2. Build the plugin with hot-reload: `bun run dev` 28 | 3. Changes will automatically be applied to the plugin in the test-vault 29 | 30 | ### Manual installation 31 | 32 | Alternatively, you can manually install the plugin in your own vault: 33 | 34 | 1. Copy `manifest.json` and `main.js` into your **Obsidian plugins folder** (`.obsidian/plugins/obsidian-vault-changelog`). 35 | 2. Reload Obsidian and enable the plugin. 36 | 37 | ## Test release 38 | 39 | ```shell 40 | act \ 41 | --container-architecture linux/amd64 \ 42 | -W .github/workflows/release.yml \ 43 | -P ubuntu-latest=catthehacker/ubuntu:act-latest \ 44 | --pull=false \ 45 | -e <(echo '{"ref": "refs/tags/v1.0.0", "ref_name": "v1.0.0"}') 46 | ``` 47 | -------------------------------------------------------------------------------- /.claude.md: -------------------------------------------------------------------------------- 1 | # CLAUDE.md 2 | 3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 | 5 | ## Development Commands 6 | 7 | - **Build for production**: `bun build.mjs production` 8 | - **Development build with watch**: `bun dev` (builds to test-vault/.obsidian/plugins/ for testing) 9 | - **Format code**: `bun format` 10 | - **Lint code**: `bun lint` 11 | - **Build**: `bun build` 12 | 13 | Always run format, lint, and build commands after making changes. 14 | 15 | ## Architecture Overview 16 | 17 | This is an Obsidian plugin that tracks file modifications and maintains a chronological changelog. The architecture consists of: 18 | 19 | ### Core Components 20 | 21 | - **ChangelogPlugin** (`src/main.ts`): Main plugin class handling file system events, debounced change detection, and changelog generation 22 | - **ChangelogSettingsTab** (`src/settings.ts`): Settings UI with path autocompletion and folder exclusion management 23 | - **PathSuggest** (`src/suggest.ts`): File/folder path autocompletion component 24 | 25 | ### Key Plugin Behaviors 26 | 27 | - Uses debounced file system event listeners (modify, delete, rename) to trigger changelog updates 28 | - Generates markdown-formatted entries with timestamps using Moment.js 29 | - Filters files by exclusion rules and limits results to configurable maximum 30 | - Completely overwrites the changelog file on each update 31 | - Supports both manual commands and automatic updates 32 | 33 | ### Development Setup 34 | 35 | - Built with Bun as the build tool and package manager 36 | - Uses custom `build.mjs` script that handles both production and development builds 37 | - Development builds copy files to `test-vault/.obsidian/plugins/` for local testing 38 | - TypeScript with strict mode enabled and modern ESNext target 39 | 40 | ### Testing Environment 41 | 42 | The `test-vault/` directory serves as a local Obsidian vault for plugin development and testing. 43 | -------------------------------------------------------------------------------- /src/suggest.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * suggest.ts - Path suggestion functionality for the Vault Changelog plugin 3 | * 4 | * This file implements autocompletion for file and folder paths in the settings UI. 5 | * It extends Obsidian's AbstractInputSuggest to provide context-aware path 6 | * suggestions, helping users select valid locations for their changelog file 7 | * and excluded folders. 8 | */ 9 | 10 | import { AbstractInputSuggest, App } from "obsidian"; 11 | 12 | /** 13 | * Provides autocomplete suggestions for file and folder paths 14 | * Used in settings UI to help users select valid paths 15 | * 16 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L1708|AbstractInputSuggest} 17 | */ 18 | export class PathSuggest extends AbstractInputSuggest { 19 | /** The input element to attach suggestions to */ 20 | inputEl: HTMLInputElement; 21 | 22 | /** 23 | * Creates a new path suggestion provider 24 | * @param app - The Obsidian app instance 25 | * @param inputEl - The input element to enhance with suggestions 26 | */ 27 | constructor(app: App, inputEl: HTMLInputElement) { 28 | super(app, inputEl); 29 | this.inputEl = inputEl; 30 | } 31 | 32 | /** 33 | * Gets path suggestions based on input string 34 | * Searches for both folders and markdown files that match the input 35 | * @param inputStr - The current input string to match against 36 | * @returns Array of matching path strings 37 | * 38 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L3316|Vault.getAllFolders} 39 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L3327|Vault.getFiles} 40 | */ 41 | getSuggestions(inputStr: string): string[] { 42 | const lowerCaseInputStr = inputStr.toLowerCase(); 43 | 44 | // Get all folders 45 | const folders = this.app.vault.getAllFolders(); 46 | const files = this.app.vault 47 | .getFiles() 48 | .filter((file) => file.extension === "md"); 49 | 50 | const suggestions: string[] = []; 51 | 52 | // Add folder suggestions with trailing slash 53 | folders.forEach((folder) => { 54 | const folderPath = folder.path; 55 | if (folderPath.toLowerCase().contains(lowerCaseInputStr)) { 56 | suggestions.push(folderPath + "/"); 57 | } 58 | }); 59 | 60 | // Add markdown files 61 | files.forEach((file) => { 62 | const filePath = file.path; 63 | if (filePath.toLowerCase().contains(lowerCaseInputStr)) { 64 | suggestions.push(filePath); 65 | } 66 | }); 67 | 68 | return suggestions; 69 | } 70 | 71 | /** 72 | * Renders a suggestion in the dropdown 73 | * @param path - The path string to render 74 | * @param el - The HTML element to render into 75 | */ 76 | renderSuggestion(path: string, el: HTMLElement): void { 77 | el.setText(path); 78 | } 79 | 80 | /** 81 | * Handles selection of a suggestion 82 | * Updates the input value and triggers input event 83 | * @param path - The selected path 84 | */ 85 | selectSuggestion(path: string): void { 86 | this.inputEl.value = path; 87 | this.inputEl.trigger("input"); 88 | this.close(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /test-vault/.obsidian/plugins/hot-reload/main.js: -------------------------------------------------------------------------------- 1 | const {Plugin, Notice, debounce} = require("obsidian"); 2 | const fs = require("fs"); 3 | 4 | const watchNeeded = window.process.platform !== "darwin" && window.process.platform !== "win32"; 5 | 6 | module.exports = class HotReload extends Plugin { 7 | 8 | statCache = new Map(); // path -> Stat 9 | queue = Promise.resolve(); 10 | 11 | run(val, err) { 12 | return this.queue = this.queue.then(val, err); 13 | } 14 | 15 | reindexPlugins = debounce(() => this.run(() => this.getPluginNames()), 500, true); 16 | requestScan = debounce(() => this.run(() => this.checkVersions()), 250, true); 17 | 18 | onload() { 19 | app.workspace.onLayoutReady(async ()=> { 20 | this.pluginReloaders = {}; 21 | this.inProgress = null; 22 | await this.getPluginNames(); 23 | this.registerEvent( this.app.vault.on("raw", this.requestScan)); 24 | this.watch(".obsidian/plugins"); 25 | this.requestScan(); 26 | this.addCommand({ 27 | id: "scan-for-changes", 28 | name: "Check plugins for changes and reload them", 29 | callback: () => this.requestScan() 30 | }) 31 | }); 32 | } 33 | 34 | watch(path) { 35 | if (this.app.vault.adapter.watchers.hasOwnProperty(path)) return; 36 | const realPath = [this.app.vault.adapter.basePath, path].join("/"); 37 | const lstat = fs.lstatSync(realPath); 38 | if (lstat && (watchNeeded || lstat.isSymbolicLink()) && fs.statSync(realPath).isDirectory()) { 39 | this.app.vault.adapter.startWatchPath(path, false); 40 | } 41 | } 42 | 43 | async checkVersions() { 44 | const base = this.app.plugins.getPluginFolder(); 45 | for (const dir of Object.keys(this.pluginNames)) { 46 | for (const file of ["manifest.json", "main.js", "styles.css", ".hotreload"]) { 47 | const path = `${base}/${dir}/${file}`; 48 | const stat = await app.vault.adapter.stat(path); 49 | if (stat) { 50 | if (this.statCache.has(path) && stat.mtime !== this.statCache.get(path).mtime) { 51 | this.onFileChange(path); 52 | } 53 | this.statCache.set(path, stat); 54 | } 55 | } 56 | } 57 | } 58 | 59 | async getPluginNames() { 60 | const plugins = {}, enabled = new Set(); 61 | for (const {id, dir} of Object.values(app.plugins.manifests)) { 62 | this.watch(dir); 63 | plugins[dir.split("/").pop()] = id; 64 | if ( 65 | await this.app.vault.exists(dir+"/.git") || 66 | await this.app.vault.exists(dir+"/.hotreload") 67 | ) enabled.add(id); 68 | } 69 | this.pluginNames = plugins; 70 | this.enabledPlugins = enabled; 71 | } 72 | 73 | onFileChange(filename) { 74 | if (!filename.startsWith(this.app.plugins.getPluginFolder()+"/")) return; 75 | const path = filename.split("/"); 76 | const base = path.pop(), dir = path.pop(); 77 | if (path.length === 1 && dir === "plugins") return this.watch(filename); 78 | if (path.length != 2) return; 79 | const plugin = dir && this.pluginNames[dir]; 80 | if (base === "manifest.json" || base === ".hotreload" || base === ".git" || !plugin) return this.reindexPlugins(); 81 | if (base !== "main.js" && base !== "styles.css") return; 82 | if (!this.enabledPlugins.has(plugin)) return; 83 | const reloader = this.pluginReloaders[plugin] || ( 84 | this.pluginReloaders[plugin] = debounce(() => this.run(() => this.reload(plugin), console.error), 750, true) 85 | ); 86 | reloader(); 87 | } 88 | 89 | async reload(plugin) { 90 | const plugins = app.plugins; 91 | 92 | // Don't reload disabled plugins 93 | if (!plugins.enabledPlugins.has(plugin)) return; 94 | 95 | await plugins.disablePlugin(plugin); 96 | console.debug("disabled", plugin); 97 | 98 | // Ensure sourcemaps are loaded (Obsidian 14+) 99 | const oldDebug = localStorage.getItem("debug-plugin"); 100 | localStorage.setItem("debug-plugin", "1"); 101 | try { 102 | await plugins.enablePlugin(plugin); 103 | } finally { 104 | // Restore previous setting 105 | if (oldDebug === null) localStorage.removeItem("debug-plugin"); else localStorage.setItem("debug-plugin", oldDebug); 106 | } 107 | console.debug("enabled", plugin); 108 | new Notice(`Plugin "${plugin}" has been reloaded`); 109 | } 110 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Obsidian Vault Changelog Plugin 2 | 3 | A plugin to maintain a change log of recently edited files in your Obsidian vault. Updates can be triggered manually or automatically. 4 | 5 | ## Why Use This Plugin? 6 | 7 | **Vault Changelog** is ideal if you want: 8 | 9 | - **A persistent, text-based record** of recently edited files that survives vault moves and syncing 10 | - **Simple configuration** with a single command to update your changelog 11 | - **Minimal overhead** without needing to learn query syntax or manage complex workflows 12 | - **Embeddable output** that can be referenced from other notes (e.g., a dashboard or index) 13 | - **Clean graph view option** by disabling wiki-links to avoid cluttering your graph 14 | 15 | ### Use Cases 16 | 17 | - **Personal knowledge base**: Track your daily writing and review what you've been working on 18 | - **Team collaboration**: Share a changelog of recent edits with collaborators 19 | - **Obsidian Publish**: Display a nicely formatted list of recent updates on your published site 20 | - **Daily/weekly reviews**: Embed the changelog in periodic notes to reflect on your work 21 | - **Project tracking**: Monitor activity in specific folders (using excluded folders feature) 22 | 23 | ## Features 24 | 25 | - Tracks recently edited notes in a centralized changelog. 26 | - Supports both manual and automatic updates. 27 | - Customizable file paths, timestamps, and entry limits. 28 | - Optional wiki-links and heading configuration. 29 | 30 | ## Important 31 | 32 | ⚠️ **The change log note is entirely overwritten at each update.** 33 | Use a dedicated change log note and embed it elsewhere if you need historical tracking. 34 | 35 | ## Project History 36 | 37 | This project was originally created by **Badr Bouslikhin (2020-2024)**. 38 | In January 2025, Badr transferred the repository to **Mark Ayers**. 39 | On behalf of the Obsidian community, we extend our gratitude to Badr for this valuable contribution. 40 | 41 | ## Installation 42 | 43 | 1. Open **Settings** in Obsidian. 44 | 2. Navigate to **Community plugins**. 45 | 3. Select **Browse**. 46 | 4. Search for **Changelog**. 47 | 5. Install and enable the plugin. 48 | 49 | 🔗 **[Plugin Page](https://obsidian.md/plugins?id=obsidian-vault-changelog#)** 50 | 51 | ## Usage 52 | 53 | - **Manual Update**: Use the command palette and run `Vault Changelog: Update`. 54 | - **Automatic Update**: If enabled, the changelog updates whenever a file is modified. 55 | 56 | ## Example Output 57 | 58 | With wiki-links enabled (default): 59 | 60 | ```markdown 61 | - 2024-01-28T14:30 · [[Note Title]] 62 | - 2024-01-28T14:25 · [[Another Note]] 63 | ``` 64 | 65 | With wiki-links disabled: 66 | 67 | ```markdown 68 | - 2024-01-28T14:30 · Note Title 69 | - 2024-01-28T14:25 · Another Note 70 | ``` 71 | 72 | With a heading configured: 73 | 74 | ```markdown 75 | # Changelog 76 | 77 | - 2024-01-28T14:30 · [[Note Title]] 78 | - 2024-01-28T14:25 · [[Another Note]] 79 | ``` 80 | 81 | ## Settings 82 | 83 | - **Auto update**: Enable automatic updates (`false` by default). 84 | - **Changelog path**: File location for the changelog (`Changelog.md` by default). 85 | - **Datetime format**: Moment.js format string (`YYYY-MM-DD[T]HHmm` by default). 86 | - **Max recent files**: Number of tracked files (`25` by default). 87 | - **Use wiki-links**: Format filenames as wiki-links `[[note]]` instead of plain text (`true` by default). 88 | - **Changelog heading**: Optional heading to prepend to the changelog (empty by default). Example: `# Changelog` 89 | - **Excluded folders**: Folders to exclude from the changelog (empty by default). 90 | 91 | ## Alternatives 92 | 93 | While **Vault Changelog** provides a simple, persistent changelog, you might prefer alternatives depending on your needs: 94 | 95 | ### Other Plugins 96 | 97 | - **[List Modified](https://github.com/franciskafieh/obsidian-list-modified)**: A more advanced changelog plugin that links modified files to daily, weekly, or monthly notes. Best for users who want changelog entries integrated into periodic notes rather than a single standalone file. 98 | 99 | - **[Recent Files](https://github.com/tgrosinger/recent-files-obsidian)**: Adds a sidebar pane showing recently opened (not edited) files. Great for quick navigation but doesn't create a persistent text record. 100 | 101 | - **[Obsidian Git](https://github.com/denolehov/obsidian-git)**: For detailed version control and change history. Overkill if you only need a simple list of recently edited files, but essential for tracking actual content changes and collaboration. 102 | 103 | ### Dataview Alternative 104 | 105 | If you already use the **[Dataview](https://github.com/blacksmithgu/obsidian-dataview)** plugin, you can achieve similar results with a query: 106 | 107 | ````markdown 108 | ```dataview 109 | TABLE dateformat(file.mtime, "yyyy-MM-dd HH:mm") AS "Last Modified" 110 | FROM "" 111 | SORT file.mtime DESC 112 | LIMIT 25 113 | ``` 114 | ```` 115 | 116 | **Dataview pros**: Dynamic queries, no plugin needed if you already use Dataview, can filter by folders/tags 117 | **Dataview cons**: Requires learning query syntax, queries don't work in all contexts (e.g., mobile widgets, some themes) 118 | 119 | **Vault Changelog pros**: Simple one-command update, works everywhere (embeds, Obsidian Publish, mobile), persistent text output, no query syntax needed 120 | 121 | ## Documentation 122 | 123 | - [Changelog](CHANGELOG.md) 124 | - [Code of Conduct](CODE_OF_CONDUCT.md) 125 | - [Contributing Guide](CONTRIBUTING.md) 126 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * main.ts - Core implementation of the Vault Changelog Obsidian plugin 3 | * 4 | * This file contains the primary plugin class that tracks file modifications 5 | * in an Obsidian vault and maintains a chronological changelog of recently 6 | * edited notes. It handles file system events, changelog generation, and 7 | * implements the plugin lifecycle methods. 8 | */ 9 | 10 | import { Notice, Plugin, TAbstractFile, TFile, debounce } from "obsidian"; 11 | 12 | import { 13 | type ChangelogSettings, 14 | ChangelogSettingsTab, 15 | DEFAULT_SETTINGS, 16 | } from "./settings"; 17 | 18 | /** 19 | * Main plugin class that handles tracking file changes and updating the changelog 20 | * Extends Obsidian's Plugin class and implements all core functionality 21 | */ 22 | export default class ChangelogPlugin extends Plugin { 23 | /** Plugin settings configuration */ 24 | settings: ChangelogSettings = DEFAULT_SETTINGS; 25 | 26 | /** 27 | * Initializes the plugin when Obsidian loads it 28 | * Sets up settings, commands, styles, and event listeners 29 | * 30 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L2188|Plugin.loadData} 31 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L2207|Plugin.addSettingTab} 32 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L2149|Plugin.addCommand} 33 | */ 34 | async onload() { 35 | await this.loadSettings(); 36 | this.addSettingTab(new ChangelogSettingsTab(this.app, this)); 37 | 38 | // Register the manual update command 39 | this.addCommand({ 40 | id: "update-changelog", 41 | name: "Update Changelog", 42 | callback: () => this.updateChangelog(), 43 | }); 44 | 45 | this.loadStyles(); 46 | 47 | // Debounce vault change handler to prevent excessive updates 48 | this.onVaultChange = debounce(this.onVaultChange.bind(this), 200); 49 | this.enableAutoUpdate(); 50 | } 51 | 52 | /** 53 | * Handles cleanup when plugin is disabled 54 | * No manual cleanup needed as Obsidian handles event listener cleanup 55 | * 56 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L2140|Plugin.unload} 57 | */ 58 | onunload() { 59 | // Cleanup happens automatically 60 | } 61 | 62 | /** 63 | * Loads the plugin's CSS styles from the styles.css file 64 | */ 65 | async loadStyles() { 66 | const cssFile = await this.app.vault.adapter.read( 67 | this.manifest.dir + "/styles.css", 68 | ); 69 | this.registerStyles(cssFile); 70 | } 71 | 72 | /** 73 | * Registers CSS styles by creating a style element and appending it to the document 74 | * @param cssText - The CSS content to add to the document 75 | */ 76 | registerStyles(cssText: string) { 77 | const styleEl = document.createElement("style"); 78 | styleEl.textContent = cssText; 79 | this.register(() => styleEl.remove()); 80 | document.head.appendChild(styleEl); 81 | } 82 | 83 | /** 84 | * Enables automatic changelog updates by registering file system event listeners 85 | * Only registers events if autoUpdate is enabled in settings 86 | * 87 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L2175|Plugin.registerEvent} 88 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L3250|Vault.on} 89 | */ 90 | enableAutoUpdate() { 91 | if (this.settings.autoUpdate) { 92 | // Handler for modify events 93 | this.registerEvent( 94 | this.app.vault.on("modify", (file: TAbstractFile) => { 95 | if (file instanceof TFile) { 96 | this.onVaultChange(file); 97 | } 98 | }), 99 | ); 100 | 101 | // Handler for delete events 102 | this.registerEvent( 103 | this.app.vault.on("delete", (file: TAbstractFile) => { 104 | if (file instanceof TFile) { 105 | this.onVaultChange(file); 106 | } 107 | }), 108 | ); 109 | 110 | // Handler for rename events (has different signature with oldPath parameter) 111 | this.registerEvent( 112 | this.app.vault.on("rename", (file: TAbstractFile) => { 113 | if (file instanceof TFile) { 114 | this.onVaultChange(file); 115 | } 116 | }), 117 | ); 118 | } 119 | } 120 | 121 | /** 122 | * Event handler for vault file changes 123 | * Triggers changelog update when any file except the changelog itself changes 124 | * @param file - The file that changed 125 | */ 126 | onVaultChange(file: TFile) { 127 | if (file.path !== this.settings.changelogPath) { 128 | this.updateChangelog(); 129 | } 130 | } 131 | 132 | /** 133 | * Updates the changelog file with the latest list of recently edited files 134 | * Generates changelog content and writes it to the configured file path 135 | * 136 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts|Obsidian API} 137 | */ 138 | async updateChangelog() { 139 | const changelog = await this.generateChangelog(); 140 | await this.writeToFile(this.settings.changelogPath, changelog); 141 | } 142 | 143 | /** 144 | * Generates the changelog content by formatting a list of recently edited files 145 | * @returns A formatted string containing the changelog content 146 | */ 147 | async generateChangelog() { 148 | const recentFiles = this.getRecentlyEditedFiles(); 149 | 150 | let changelogContent = ""; 151 | 152 | // Add optional heading if configured 153 | if (this.settings.changelogHeading) { 154 | changelogContent += `${this.settings.changelogHeading}\n\n`; 155 | } 156 | 157 | recentFiles.forEach((file) => { 158 | // Use window.moment to prevent TypeScript error with the imported moment 159 | const m = window.moment(file.stat.mtime); 160 | const formattedTime = m.format(this.settings.datetimeFormat); 161 | 162 | // Format filename based on useWikiLinks setting 163 | const fileName = this.settings.useWikiLinks 164 | ? `[[${file.basename}]]` 165 | : file.basename; 166 | 167 | changelogContent += `- ${formattedTime} · ${fileName}\n`; 168 | }); 169 | 170 | return changelogContent; 171 | } 172 | 173 | /** 174 | * Gets the list of recently edited markdown files in the vault 175 | * Excludes the changelog file itself and any files in excluded folders 176 | * @returns A sorted array of TFile objects, limited to maxRecentFiles 177 | * 178 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L3326|Vault.getMarkdownFiles} 179 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L797|TFile} 180 | */ 181 | getRecentlyEditedFiles() { 182 | return this.app.vault 183 | .getMarkdownFiles() 184 | .filter((file) => { 185 | // Exclude the changelog file itself 186 | if (file.path === this.settings.changelogPath) { 187 | return false; 188 | } 189 | 190 | // Exclude files in excluded folders 191 | for (const folder of this.settings.excludedFolders) { 192 | if (file.path.startsWith(folder)) { 193 | return false; 194 | } 195 | } 196 | 197 | return true; 198 | }) 199 | .sort((a, b) => b.stat.mtime - a.stat.mtime) 200 | .slice(0, this.settings.maxRecentFiles); 201 | } 202 | 203 | /** 204 | * Writes content to a file at the specified path 205 | * Creates the file if it doesn't exist, otherwise modifies it 206 | * @param path - The path to write to 207 | * @param content - The content to write 208 | * 209 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L3340|Vault.getAbstractFileByPath} 210 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L3284|Vault.create} 211 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L3296|Vault.modify} 212 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L2869|Notice} 213 | */ 214 | async writeToFile(path: string, content: string) { 215 | let file = this.app.vault.getAbstractFileByPath(path); 216 | if (!file) { 217 | file = await this.app.vault.create(path, ""); 218 | } 219 | if (file instanceof TFile) { 220 | await this.app.vault.modify(file, content); 221 | } else { 222 | new Notice(`Could not update changelog at path: ${path}`); 223 | } 224 | } 225 | 226 | /** 227 | * Loads saved settings from disk and merges them with default settings 228 | */ 229 | async loadSettings() { 230 | const loadedSettings = await this.loadData(); 231 | this.settings = { 232 | ...DEFAULT_SETTINGS, 233 | ...loadedSettings, 234 | }; 235 | } 236 | 237 | /** 238 | * Saves current settings to disk 239 | */ 240 | async saveSettings() { 241 | await this.saveData(this.settings); 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /src/settings.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * settings.ts - Settings management for the Vault Changelog plugin 3 | * 4 | * This file defines the plugin's configuration interface, default values, 5 | * and the settings UI tab. It handles user preferences for: 6 | * - Auto-updating the changelog 7 | * - Setting the changelog file path 8 | * - Configuring datetime formats 9 | * - Setting maximum number of files to track 10 | * - Managing excluded folders 11 | */ 12 | 13 | import { 14 | App, 15 | Notice, 16 | PluginSettingTab, 17 | Setting, 18 | normalizePath, 19 | } from "obsidian"; 20 | 21 | import ChangelogPlugin from "./main"; 22 | import { PathSuggest } from "./suggest"; 23 | 24 | /** 25 | * Interface defining all configurable settings for the Vault Changelog plugin 26 | */ 27 | export interface ChangelogSettings { 28 | /** Whether to automatically update the changelog when files are modified */ 29 | autoUpdate: boolean; 30 | 31 | /** Path to the changelog file */ 32 | changelogPath: string; 33 | 34 | /** Format string for timestamps (using Moment.js syntax) */ 35 | datetimeFormat: string; 36 | 37 | /** Maximum number of files to include in the changelog */ 38 | maxRecentFiles: number; 39 | 40 | /** Array of folder paths to exclude from the changelog */ 41 | excludedFolders: string[]; 42 | 43 | /** Whether to use wiki-links ([[note]]) or plain text in changelog entries */ 44 | useWikiLinks: boolean; 45 | 46 | /** Optional heading to prepend to the changelog (empty string means no heading) */ 47 | changelogHeading: string; 48 | } 49 | 50 | /** 51 | * Default configuration values for the plugin 52 | */ 53 | export const DEFAULT_SETTINGS: ChangelogSettings = { 54 | autoUpdate: false, 55 | changelogPath: "Changelog.md", 56 | datetimeFormat: "YYYY-MM-DD[T]HHmm", 57 | maxRecentFiles: 25, 58 | excludedFolders: [], 59 | useWikiLinks: true, 60 | changelogHeading: "", 61 | }; 62 | 63 | /** 64 | * Settings tab implementation for the Vault Changelog plugin 65 | * Provides the UI for configuring all plugin settings 66 | * 67 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L2596|PluginSettingTab} 68 | */ 69 | export class ChangelogSettingsTab extends PluginSettingTab { 70 | /** Reference to the parent plugin instance */ 71 | plugin: ChangelogPlugin; 72 | 73 | /** 74 | * Creates a new settings tab instance 75 | * @param app - The Obsidian app instance 76 | * @param plugin - The plugin instance 77 | */ 78 | constructor(app: App, plugin: ChangelogPlugin) { 79 | super(app, plugin); 80 | this.plugin = plugin; 81 | } 82 | 83 | /** 84 | * Renders the list of excluded folders with remove buttons 85 | * @param container - The HTML element to render the list into 86 | */ 87 | renderExcludedFolders(container: HTMLElement) { 88 | container.empty(); 89 | 90 | if (this.plugin.settings.excludedFolders.length === 0) { 91 | container.createEl("div", { text: "No excluded folders" }); 92 | return; 93 | } 94 | 95 | this.plugin.settings.excludedFolders.forEach((folder) => { 96 | const folderDiv = container.createDiv("excluded-folder-item"); 97 | folderDiv.createSpan({ text: folder }); 98 | 99 | const removeButton = folderDiv.createEl("button", { 100 | text: "✕", 101 | cls: "excluded-folder-remove", 102 | }); 103 | 104 | removeButton.addEventListener("click", async () => { 105 | const index = this.plugin.settings.excludedFolders.indexOf(folder); 106 | if (index > -1) { 107 | this.plugin.settings.excludedFolders.splice(index, 1); 108 | await this.plugin.saveSettings(); 109 | this.renderExcludedFolders(container); 110 | } 111 | }); 112 | }); 113 | } 114 | 115 | /** 116 | * Renders the settings UI in the Obsidian settings tab 117 | * Creates all input elements and handles their change events 118 | * 119 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L2601|PluginSettingTab.display} 120 | * @see {@link https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts#L2521|Setting} 121 | */ 122 | display() { 123 | const { containerEl } = this; 124 | const { settings } = this.plugin; 125 | 126 | containerEl.empty(); 127 | 128 | new Setting(containerEl) 129 | .setName("Auto update") 130 | .setDesc("Automatically update changelog on vault changes") 131 | .addToggle((toggle) => 132 | toggle.setValue(settings.autoUpdate).onChange(async (value) => { 133 | settings.autoUpdate = value; 134 | await this.plugin.saveSettings(); 135 | if (value) { 136 | this.plugin.enableAutoUpdate(); 137 | } 138 | }), 139 | ); 140 | 141 | new Setting(containerEl) 142 | .setName("Changelog path") 143 | .setDesc("Relative path including filename and extension") 144 | .addText((text) => { 145 | text 146 | .setPlaceholder("Folder/Changelog.md") 147 | .setValue(settings.changelogPath) 148 | .onChange(async (path) => { 149 | settings.changelogPath = normalizePath(path); 150 | await this.plugin.saveSettings(); 151 | }); 152 | 153 | // Add path autocompletion 154 | new PathSuggest(this.app, text.inputEl); 155 | }); 156 | 157 | new Setting(containerEl) 158 | .setName("Datetime format") 159 | .setDesc("Moment.js datetime format string") 160 | .addText((text) => 161 | text 162 | .setPlaceholder("YYYY-MM-DD[T]HHmm") 163 | .setValue(settings.datetimeFormat) 164 | .onChange(async (format) => { 165 | // Attempt to format current date with the new format string 166 | // Returns "Invalid date" if the format is invalid 167 | // Use window.moment to prevent TypeScript error 168 | const m = window.moment(); 169 | const isValid = m.format(format) !== "Invalid date"; 170 | 171 | if (!isValid) { 172 | // Revert to previous valid format and notify user 173 | text.setValue(settings.datetimeFormat); 174 | new Notice("Invalid datetime format"); 175 | return; 176 | } 177 | 178 | // Save valid format and persist settings 179 | settings.datetimeFormat = format; 180 | await this.plugin.saveSettings(); 181 | }), 182 | ); 183 | 184 | new Setting(containerEl) 185 | .setName("Max recent files") 186 | .setDesc("Maximum number of recently edited files to include") 187 | .addText((text) => 188 | text 189 | .setValue(settings.maxRecentFiles.toString()) 190 | .onChange(async (value) => { 191 | // Ensure the value is a positive number 192 | const numValue = Number(value); 193 | if (isNaN(numValue) || numValue < 1) { 194 | text.setValue(settings.maxRecentFiles.toString()); 195 | return; 196 | } 197 | settings.maxRecentFiles = numValue; 198 | await this.plugin.saveSettings(); 199 | }), 200 | ); 201 | 202 | new Setting(containerEl) 203 | .setName("Use wiki-links") 204 | .setDesc("Format filenames as wiki-links [[note]] instead of plain text") 205 | .addToggle((toggle) => 206 | toggle.setValue(settings.useWikiLinks).onChange(async (value) => { 207 | settings.useWikiLinks = value; 208 | await this.plugin.saveSettings(); 209 | }), 210 | ); 211 | 212 | new Setting(containerEl) 213 | .setName("Changelog heading") 214 | .setDesc( 215 | "Optional heading to prepend to the changelog (e.g., # Changelog). Leave empty for no heading.", 216 | ) 217 | .addText((text) => 218 | text 219 | .setPlaceholder("# Changelog") 220 | .setValue(settings.changelogHeading) 221 | .onChange(async (value) => { 222 | settings.changelogHeading = value; 223 | await this.plugin.saveSettings(); 224 | }), 225 | ); 226 | 227 | // Excluded folders section header 228 | containerEl.createEl("h3", { text: "Excluded folders" }); 229 | 230 | // Create a list of currently excluded folders with delete buttons 231 | const excludedFoldersList = containerEl.createDiv("excluded-folders-list"); 232 | this.renderExcludedFolders(excludedFoldersList); 233 | 234 | // Add a new excluded folder with path suggestions 235 | new Setting(containerEl) 236 | .setName("Add excluded folder") 237 | .setDesc("Folders to exclude from the changelog") 238 | .addText((text) => { 239 | text.setPlaceholder("folder/path/"); 240 | 241 | // Add path autocompletion 242 | new PathSuggest(this.app, text.inputEl); 243 | }) 244 | .addButton((button) => { 245 | button.setButtonText("Add").onClick(async () => { 246 | const input = button.buttonEl.parentElement?.querySelector("input"); 247 | if (input) { 248 | const folderPath = input.value; 249 | if (folderPath && !settings.excludedFolders.includes(folderPath)) { 250 | settings.excludedFolders.push(folderPath); 251 | await this.plugin.saveSettings(); 252 | input.value = ""; 253 | this.renderExcludedFolders(excludedFoldersList); 254 | } 255 | } 256 | }); 257 | }); 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /bun.lock: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1, 3 | "workspaces": { 4 | "": { 5 | "devDependencies": { 6 | "@typescript-eslint/eslint-plugin": "^8.26.1", 7 | "@typescript-eslint/parser": "^8.26.1", 8 | "eslint": "^9.22.0", 9 | "eslint-config-prettier": "^10.1.1", 10 | "obsidian": "^1.8.7", 11 | "prettier": "^3.5.3", 12 | "typescript": "^5.8.2", 13 | }, 14 | }, 15 | }, 16 | "packages": { 17 | "@codemirror/state": ["@codemirror/state@6.5.2", "", { "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } }, "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA=="], 18 | 19 | "@codemirror/view": ["@codemirror/view@6.36.4", "", { "dependencies": { "@codemirror/state": "^6.5.0", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "sha512-ZQ0V5ovw/miKEXTvjgzRyjnrk9TwriUB1k4R5p7uNnHR9Hus+D1SXHGdJshijEzPFjU25xea/7nhIeSqYFKdbA=="], 20 | 21 | "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], 22 | 23 | "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], 24 | 25 | "@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="], 26 | 27 | "@eslint/config-helpers": ["@eslint/config-helpers@0.4.0", "", { "dependencies": { "@eslint/core": "^0.16.0" } }, "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog=="], 28 | 29 | "@eslint/core": ["@eslint/core@0.16.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q=="], 30 | 31 | "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], 32 | 33 | "@eslint/js": ["@eslint/js@9.37.0", "", {}, "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg=="], 34 | 35 | "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="], 36 | 37 | "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.0", "", { "dependencies": { "@eslint/core": "^0.16.0", "levn": "^0.4.1" } }, "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A=="], 38 | 39 | "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], 40 | 41 | "@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="], 42 | 43 | "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], 44 | 45 | "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.2", "", {}, "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ=="], 46 | 47 | "@marijn/find-cluster-break": ["@marijn/find-cluster-break@1.0.2", "", {}, "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="], 48 | 49 | "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], 50 | 51 | "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], 52 | 53 | "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], 54 | 55 | "@types/codemirror": ["@types/codemirror@5.60.8", "", { "dependencies": { "@types/tern": "*" } }, "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw=="], 56 | 57 | "@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="], 58 | 59 | "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], 60 | 61 | "@types/tern": ["@types/tern@0.23.9", "", { "dependencies": { "@types/estree": "*" } }, "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw=="], 62 | 63 | "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.46.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.46.0", "@typescript-eslint/type-utils": "8.46.0", "@typescript-eslint/utils": "8.46.0", "@typescript-eslint/visitor-keys": "8.46.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.46.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA=="], 64 | 65 | "@typescript-eslint/parser": ["@typescript-eslint/parser@8.46.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.46.0", "@typescript-eslint/types": "8.46.0", "@typescript-eslint/typescript-estree": "8.46.0", "@typescript-eslint/visitor-keys": "8.46.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ=="], 66 | 67 | "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.46.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.46.0", "@typescript-eslint/types": "^8.46.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ=="], 68 | 69 | "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.46.0", "", { "dependencies": { "@typescript-eslint/types": "8.46.0", "@typescript-eslint/visitor-keys": "8.46.0" } }, "sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw=="], 70 | 71 | "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.46.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw=="], 72 | 73 | "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.46.0", "", { "dependencies": { "@typescript-eslint/types": "8.46.0", "@typescript-eslint/typescript-estree": "8.46.0", "@typescript-eslint/utils": "8.46.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg=="], 74 | 75 | "@typescript-eslint/types": ["@typescript-eslint/types@8.46.0", "", {}, "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA=="], 76 | 77 | "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.46.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.46.0", "@typescript-eslint/tsconfig-utils": "8.46.0", "@typescript-eslint/types": "8.46.0", "@typescript-eslint/visitor-keys": "8.46.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg=="], 78 | 79 | "@typescript-eslint/utils": ["@typescript-eslint/utils@8.46.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.46.0", "@typescript-eslint/types": "8.46.0", "@typescript-eslint/typescript-estree": "8.46.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g=="], 80 | 81 | "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.46.0", "", { "dependencies": { "@typescript-eslint/types": "8.46.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q=="], 82 | 83 | "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], 84 | 85 | "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], 86 | 87 | "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], 88 | 89 | "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], 90 | 91 | "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], 92 | 93 | "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], 94 | 95 | "brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], 96 | 97 | "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], 98 | 99 | "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], 100 | 101 | "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], 102 | 103 | "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], 104 | 105 | "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], 106 | 107 | "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], 108 | 109 | "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], 110 | 111 | "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], 112 | 113 | "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], 114 | 115 | "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], 116 | 117 | "eslint": ["eslint@9.37.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.4.0", "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.37.0", "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig=="], 118 | 119 | "eslint-config-prettier": ["eslint-config-prettier@10.1.8", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="], 120 | 121 | "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], 122 | 123 | "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], 124 | 125 | "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], 126 | 127 | "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], 128 | 129 | "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], 130 | 131 | "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], 132 | 133 | "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], 134 | 135 | "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], 136 | 137 | "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], 138 | 139 | "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], 140 | 141 | "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], 142 | 143 | "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], 144 | 145 | "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], 146 | 147 | "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], 148 | 149 | "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], 150 | 151 | "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], 152 | 153 | "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], 154 | 155 | "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], 156 | 157 | "globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], 158 | 159 | "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], 160 | 161 | "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], 162 | 163 | "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], 164 | 165 | "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], 166 | 167 | "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], 168 | 169 | "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], 170 | 171 | "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], 172 | 173 | "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], 174 | 175 | "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], 176 | 177 | "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], 178 | 179 | "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], 180 | 181 | "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], 182 | 183 | "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], 184 | 185 | "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], 186 | 187 | "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], 188 | 189 | "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], 190 | 191 | "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], 192 | 193 | "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], 194 | 195 | "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], 196 | 197 | "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], 198 | 199 | "moment": ["moment@2.29.4", "", {}, "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="], 200 | 201 | "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 202 | 203 | "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], 204 | 205 | "obsidian": ["obsidian@1.10.0", "", { "dependencies": { "@types/codemirror": "5.60.8", "moment": "2.29.4" }, "peerDependencies": { "@codemirror/state": "6.5.0", "@codemirror/view": "6.38.1" } }, "sha512-F7hhnmGRQD1TanDPFT//LD3iKNUVd7N8sKL7flCCHRszfTxpDJ39j3T7LHbcGpyid906i6lD5oO+cnfLBzJMKw=="], 206 | 207 | "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], 208 | 209 | "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], 210 | 211 | "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], 212 | 213 | "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], 214 | 215 | "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], 216 | 217 | "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], 218 | 219 | "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 220 | 221 | "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], 222 | 223 | "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], 224 | 225 | "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], 226 | 227 | "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], 228 | 229 | "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], 230 | 231 | "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], 232 | 233 | "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], 234 | 235 | "semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], 236 | 237 | "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], 238 | 239 | "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], 240 | 241 | "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], 242 | 243 | "style-mod": ["style-mod@4.1.2", "", {}, "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="], 244 | 245 | "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], 246 | 247 | "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], 248 | 249 | "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], 250 | 251 | "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], 252 | 253 | "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], 254 | 255 | "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], 256 | 257 | "w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="], 258 | 259 | "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], 260 | 261 | "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], 262 | 263 | "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], 264 | 265 | "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], 266 | 267 | "@eslint/eslintrc/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], 268 | 269 | "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], 270 | 271 | "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], 272 | 273 | "eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], 274 | 275 | "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], 276 | 277 | "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], 278 | } 279 | } 280 | --------------------------------------------------------------------------------