diff --git a/README.md b/README.md index 6b8fd80..319451c 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,112 @@ -# Progress Tracker Plugin for Obsidian +# Progress Tracker -A plugin for Obsidian (https://obsidian.md) that helps you track progress and manage tasks in your notes. +Progress Tracker is an Obsidian plugin that visualizes your task completion progress and helps you stay on top of your work. ## Features -- Track progress of tasks and projects -- Add progress bars to your notes -- Monitor completion status -- Generate progress reports -- Customize tracking settings +- **Smooth Progress Bar**: Visualizes your task completion progress with an animated, color-coded progress bar +- **Task Tracking**: Automatically counts completed and incomplete tasks in your notes +- **Status Management**: Automatically updates status in the YAML frontmatter based on task progress +- **Kanban Integration**: Moves cards between columns in Kanban boards when tasks are completed +- **Metadata Updates**: Automatically adds completion dates and updates metadata when tasks reach 100% + +## Screenshots + +![Progress Tracker Main View](Screenshot.png) + +## How It Works + +The plugin tracks Markdown tasks (`- [ ]` and `- [x]`) in your notes and displays: + +- A visual progress bar showing completion percentage +- Task count (complete/total) +- Color-coded indicators for different progress levels ## Installation -1. Open Obsidian Settings -2. Go to Community Plugins and disable Safe Mode -3. Install and enable the Dataview plugin (required dependency) -4. Click Browse and search for "Progress Tracker" -5. Install the plugin -6. Enable the plugin in your settings +1. In Obsidian, go to Settings → Community plugins +2. Disable Safe mode +3. Click "Browse" and search for "Progress Tracker" +4. Install the plugin and enable it -## Dependencies +## Requirements -This plugin requires the [Dataview plugin](https://github.com/blacksmithgu/obsidian-dataview) to be installed and enabled. Dataview is used for: -- Querying and tracking progress data -- Generating progress reports -- Analyzing task completion statistics +- [Dataview plugin](https://github.com/blacksmithgu/obsidian-dataview) (optional but recommended) -Please make sure to install Dataview before using Progress Tracker. +## Configuration -## Usage +### General Settings +- Toggle debug information +- Customize update animations -### Basic Progress Tracking -- Add progress indicators to any note -- Update progress status with simple commands -- View completion percentages +### Performance +- Adjust the delay for various operations to balance responsiveness and performance +- Configure keyboard input and checkbox click update delays -### Progress Reports -- Generate summary reports -- Track progress over time -- Export progress data +### Progress Bar Colors +- Choose from preset color schemes or create your own +- Customize thresholds for low/medium/high progress states -### Settings -- Customize progress bar appearance -- Configure tracking preferences -- Set up notification options +### Metadata Auto-Update +- Automatically update YAML frontmatter when tasks are completed +- Change status fields and add completion dates +- Customize status labels + +### Kanban Integration +- Automatically move cards in Kanban boards based on task status +- Match column names to status values +- Automatically detect Kanban boards in your vault + +## Usage Tips + +### Task Format +The plugin recognizes standard Markdown task formats: + +- [] Incomplete task +- [x] Completed task + + +### YAML Frontmatter +For status tracking, use YAML frontmatter at the top of your notes: +```yaml +--- +status: In Progress +--- +``` + +The plugin will automatically update this to "Completed" when all tasks are done. + +Kanban Integration +For the best Kanban integration: + +- Name your columns to match your status labels (Todo, In Progress, Completed) +- Enable automatic synchronization in settings + +### Keyboard Shortcuts +You can assign keyboard shortcuts to the following commands: + +- Clear completed files cache - Reset the list of files that have already triggered completion notifications + +## Troubleshooting +- Progress bar not updating: Check that your tasks use the proper Markdown format +- Kanban integration not working: Ensure your Kanban columns match your status values +- Slow updates: Adjust the performance settings to balance responsiveness with performance ## Support +If you encounter issues or have feature suggestions: -If you encounter any issues or have suggestions for improvements, please: -- Open an issue on GitHub -- Join our community discussions +- Check the GitHub Issues -## Contributing +## Changelog +v1.1.0 +- Added smooth transitions for progress bar updates +- Fixed Kanban board detection and integration +- Improved metadata handling for completion status +- Enhanced UI responsiveness -Contributions are welcome! Please feel free to submit a Pull Request. - -1. Fork the repository -2. Create your feature branch -3. Commit your changes -4. Push to the branch -5. Create a Pull Request - -## License - -This project is licensed under the MIT License - see the LICENSE file for details. +v1.0.0 +- Initial release +- Credits +- Developed by Van Nam +- Thanks to the Obsidian community for inspiration and feedback diff --git a/Screenshot.png b/Screenshot.png new file mode 100644 index 0000000..6341622 Binary files /dev/null and b/Screenshot.png differ diff --git a/main.ts b/main.ts index 3c6775c..e86a858 100644 --- a/main.ts +++ b/main.ts @@ -13,7 +13,7 @@ import { Notice, } from "obsidian"; -// Định nghĩa interface cho DataviewAPI +// Define DataviewAPI interface interface DataviewApi { executeJs( code: string, @@ -22,32 +22,32 @@ interface DataviewApi { ): Promise; page(path: string): any; pages(source: string): any[]; - // Không sử dụng eval vì có thể không tồn tại trong một số phiên bản Dataview + // Not using eval as it might not exist in some Dataview versions } -// Định nghĩa interface cho window object để truy cập Dataview plugin +// Define window object interface to access Dataview plugin declare global { interface Window { DataviewAPI?: DataviewApi; } } -// Hàm helper để lấy Dataview API +// Helper function to get Dataview API function getDataviewAPI(app: App): DataviewApi | null { - // Cách 1: Thông qua window object + // Method 1: Through window object // @ts-ignore if (window.DataviewAPI) { return window.DataviewAPI; } - // Cách 2: Thông qua app.plugins + // Method 2: Through app.plugins // @ts-ignore const dataviewPlugin = app.plugins?.plugins?.dataview; if (dataviewPlugin && dataviewPlugin.api) { return dataviewPlugin.api; } - // Cách 3: Kiểm tra xem plugin có được enable không + // Method 3: Check if plugin is enabled // @ts-ignore if (app.plugins.enabledPlugins.has("dataview")) { console.log("Dataview plugin is enabled but API is not available yet"); @@ -59,7 +59,7 @@ function getDataviewAPI(app: App): DataviewApi | null { } interface TaskProgressBarSettings { - mySetting: string; // Có vẻ không được sử dụng - có thể loại bỏ trong tương lai + mySetting: string; // Appears unused - could be removed in the future showDebugInfo: boolean; progressColorScheme: "default" | "red-orange-green" | "custom"; lowProgressColor: string; @@ -148,7 +148,7 @@ export default class TaskProgressBarPlugin extends Plugin { // Add settings tab this.addSettingTab(new TaskProgressBarSettingTab(this.app, this)); - // Kiểm tra Dataview API và thiết lập interval để kiểm tra lại nếu chưa có + // Check Dataview API and set up interval to check again if not found this.checkDataviewAPI(); // Register event to update progress bar when file changes @@ -175,10 +175,10 @@ export default class TaskProgressBarPlugin extends Plugin { "editor-change", debounce(async (editor, view) => { if (view instanceof MarkdownView && this.sidebarView) { - // Lấy nội dung hiện tại của editor + // Get current editor content const content = editor.getValue(); - // Kiểm tra nếu nội dung thay đổi và có chứa task hoặc nội dung cũ có chứa task + // Check if content has changed and contains tasks or old content contains tasks if ( content.includes("- [") || content.includes("- [ ]") || @@ -187,18 +187,18 @@ export default class TaskProgressBarPlugin extends Plugin { this.lastFileContent.includes("- [ ]") || this.lastFileContent.includes("- [x]") ) { - // Cập nhật ngay lập tức + // Update immediately if (this.lastActiveFile) { - // Cập nhật nội dung file cuối cùng trước khi kiểm tra thay đổi + // Update last file content before checking changes this.lastActiveFile = view.file; - // Cập nhật thanh tiến trình ngay lập tức + // Update progress bar immediately this.sidebarView.updateProgressBar( view.file, content ); - // Sau đó mới cập nhật nội dung file cuối cùng + // Then update last file content this.lastFileContent = content; } } @@ -207,13 +207,13 @@ export default class TaskProgressBarPlugin extends Plugin { ) // Use configurable delay ); - // Lắng nghe sự kiện keydown để phát hiện khi người dùng nhập task mới hoặc check/uncheck task + // Listen for keydown events to detect when user enters new tasks or checks/unchecks tasks this.registerDomEvent(document, "keydown", (evt: KeyboardEvent) => { - // Kiểm tra xem có đang trong editor không + // Check if we're in the editor const activeView = this.app.workspace.getActiveViewOfType(MarkdownView); if (activeView && activeView.getMode() === "source") { - // Cập nhật ngay lập tức khi nhấn các phím liên quan đến task + // Update immediately when pressing keys related to tasks if ( [ "Enter", @@ -225,7 +225,7 @@ export default class TaskProgressBarPlugin extends Plugin { "Delete", ].includes(evt.key) ) { - // Cập nhật ngay lập tức + // Update immediately setTimeout(() => { const content = activeView.editor.getValue(); if ( @@ -235,7 +235,7 @@ export default class TaskProgressBarPlugin extends Plugin { ) { this.lastActiveFile = activeView.file; - // Cập nhật thanh tiến trình ngay lập tức + // Update progress bar immediately if (this.sidebarView) { this.sidebarView.updateProgressBar( activeView.file, @@ -243,7 +243,7 @@ export default class TaskProgressBarPlugin extends Plugin { ); } - // Sau đó mới cập nhật nội dung file cuối cùng + // Then update last file content this.lastFileContent = content; } }, this.settings.keyboardInputDelay); // Use configurable delay @@ -251,35 +251,35 @@ export default class TaskProgressBarPlugin extends Plugin { } }); - // Lắng nghe sự kiện click trong editor để phát hiện khi task được check/uncheck + // Listen for click events in the editor to detect when tasks are checked/unchecked this.registerDomEvent(document, "click", (evt: MouseEvent) => { const target = evt.target as HTMLElement; - // Kiểm tra xem click có phải vào checkbox của task không + // Check if click is on a task checkbox if ( target && target.tagName === "INPUT" && target.classList.contains("task-list-item-checkbox") ) { - // Đợi một chút để Obsidian cập nhật trạng thái task trong file + // Wait a bit for Obsidian to update the task state in the file setTimeout(async () => { const activeFile = this.app.workspace.getActiveFile(); if (activeFile && this.sidebarView) { - // Đọc nội dung file hiện tại + // Read current file content const content = await this.app.vault.read(activeFile); - // Cập nhật thanh tiến trình ngay lập tức + // Update progress bar immediately this.lastActiveFile = activeFile; this.sidebarView.updateProgressBar(activeFile, content); - // Sau đó mới cập nhật nội dung file cuối cùng + // Then update last file content this.lastFileContent = content; } }, this.settings.checkboxClickDelay); // Use configurable delay } }); - // Activate view when plugin loads - đợi một chút để Obsidian khởi động hoàn toàn + // Activate view when plugin loads - wait a bit for Obsidian to fully start setTimeout(() => { this.activateView(); @@ -320,33 +320,33 @@ export default class TaskProgressBarPlugin extends Plugin { }); } - // Kiểm tra Dataview API và thiết lập interval để kiểm tra lại nếu chưa có + // Check Dataview API and set up interval to check again if not found checkDataviewAPI() { - // Kiểm tra ngay lập tức + // Check immediately this.dvAPI = getDataviewAPI(this.app); - // Nếu không tìm thấy, thiết lập interval để kiểm tra lại + // If not found, set up interval to check again if (!this.dvAPI) { this.dataviewCheckInterval = window.setInterval(() => { this.dvAPI = getDataviewAPI(this.app); if (this.dvAPI) { console.log("Dataview API found"); - // Nếu tìm thấy, xóa interval + // If found, clear interval if (this.dataviewCheckInterval) { clearInterval(this.dataviewCheckInterval); this.dataviewCheckInterval = null; } - // Cập nhật sidebar nếu đang mở + // Update sidebar if open if (this.sidebarView && this.lastActiveFile) { this.sidebarView.updateProgressBar(this.lastActiveFile); } } - }, 2000); // Kiểm tra mỗi 2 giây + }, 2000); // Check every 2 seconds } } - // Cập nhật nội dung file cuối cùng + // Update last file content async updateLastFileContent(file: TFile) { if (file) { this.lastFileContent = await this.app.vault.read(file); @@ -365,14 +365,14 @@ export default class TaskProgressBarPlugin extends Plugin { } // Otherwise, create a new leaf in the left sidebar - // Kiểm tra xem workspace đã sẵn sàng chưa + // Check if workspace is ready if (!workspace.leftSplit) { console.log("Workspace not ready yet, retrying in 500ms"); setTimeout(() => this.activateView(), 500); return; } - // Sử dụng getLeaf thay vì createLeaf + // Use getLeaf instead of createLeaf const leaf = workspace.getLeftLeaf(false); if (leaf) { await leaf.setViewState({ @@ -392,7 +392,7 @@ export default class TaskProgressBarPlugin extends Plugin { } onunload() { - // Xóa interval nếu có + // Clear interval if it exists if (this.dataviewCheckInterval) { clearInterval(this.dataviewCheckInterval); this.dataviewCheckInterval = null; @@ -521,7 +521,7 @@ class TaskProgressBarView extends ItemView { // Track the update time for this file this.lastFileUpdateMap.set(file.path, Date.now()); - // Tránh cập nhật quá nhanh + // Avoid updating too quickly const now = Date.now(); if (!isInitialLoad && now - this.lastUpdateTime < 100) return; this.lastUpdateTime = now; @@ -541,13 +541,13 @@ class TaskProgressBarView extends ItemView { this.initialLoadComplete = true; } - // Thêm class để hiển thị animation chỉ khi cài đặt showUpdateAnimation được bật + // Add class to show animation only when showUpdateAnimation setting is enabled if (this.plugin.settings.showUpdateAnimation) { progressContainer.classList.add("updating"); } try { - // Cập nhật ngay lập tức nếu có nội dung được cung cấp + // Update immediately if content is provided if (content) { if (!this.hasTasksInContent(content)) { // Only clear if not already showing "no tasks" message @@ -567,7 +567,7 @@ class TaskProgressBarView extends ItemView { ); } } else { - // Đọc nội dung file + // Read file content const fileContent = await this.plugin.app.vault.read(file); if (!this.hasTasksInContent(fileContent)) { @@ -598,7 +598,7 @@ class TaskProgressBarView extends ItemView { text: `Error updating progress bar: ${error.message}`, }); } finally { - // Xóa class sau khi cập nhật xong, chỉ khi animation được bật + // Remove class after update completes, only if animation is enabled if (this.plugin.settings.showUpdateAnimation) { setTimeout(() => { progressContainer.classList.remove("updating"); @@ -619,7 +619,7 @@ class TaskProgressBarView extends ItemView { ); } - // Phương thức tạo thanh tiến trình từ nội dung string + // Method to create progress bar from string content async createProgressBarFromString( container: HTMLElement, content: string, @@ -649,7 +649,7 @@ class TaskProgressBarView extends ItemView { return; } - // Sử dụng regex chính xác hơn để đếm task + // Use more accurate regex to count tasks const incompleteTasks = (content.match(/- \[ \]/g) || []).length; const completedTasks = (content.match(/- \[x\]/gi) || []).length; let totalTasks = incompleteTasks + completedTasks; @@ -841,7 +841,7 @@ class TaskProgressBarView extends ItemView { // If using default color scheme, let CSS handle it if (settings.progressColorScheme === "default") { - // Xóa bất kỳ màu inline nào đã được đặt trước đó + // Remove any previously set inline colors progressElement.style.backgroundColor = ""; return; } @@ -849,16 +849,16 @@ class TaskProgressBarView extends ItemView { // Apply custom color based on percentage let newColor = ""; if (percentage === 100) { - // Hoàn thành - màu xanh lá + // Complete - green newColor = settings.completeProgressColor; } else if (percentage >= settings.mediumProgressThreshold) { - // Tiến độ cao (66-99%) - màu xanh dương + // High progress (66-99%) - blue newColor = settings.highProgressColor; } else if (percentage >= settings.lowProgressThreshold) { - // Tiến độ trung bình (34-65%) - màu cam/vàng + // Medium progress (34-65%) - orange/yellow newColor = settings.mediumProgressColor; } else { - // Tiến độ thấp (0-33%) - màu đỏ + // Low progress (0-33%) - red newColor = settings.lowProgressColor; } @@ -867,7 +867,7 @@ class TaskProgressBarView extends ItemView { progressElement.style.backgroundColor = newColor; } - // Thêm debug log nếu cần + // Add debug log if needed if (this.plugin.settings.showDebugInfo) { console.log(`Applied color for ${percentage}%: Color scheme: ${settings.progressColorScheme}, @@ -1904,7 +1904,7 @@ class TaskProgressBarSettingTab extends PluginSettingTab { containerEl.createEl("h2", { text: "Task Progress Bar Settings" }); - // Thêm thông tin về trạng thái Dataview + // Add Dataview status information const dataviewStatus = containerEl.createDiv({ cls: "dataview-status", }); @@ -1919,7 +1919,7 @@ class TaskProgressBarSettingTab extends PluginSettingTab { cls: "dataview-unavailable", }); - // Thêm nút để kiểm tra lại Dataview + // Add button to check for Dataview again const checkButton = dataviewStatus.createEl("button", { text: "Check for Dataview", cls: "mod-cta", @@ -1965,10 +1965,10 @@ class TaskProgressBarSettingTab extends PluginSettingTab { this.plugin.settings.showDebugInfo = value; await this.plugin.saveSettings(); - // Cập nhật sidebar nếu đang mở - sử dụng phương thức public + // Update sidebar if open - use public method const currentFile = this.app.workspace.getActiveFile(); if (currentFile) { - // Sử dụng phương thức public để cập nhật UI + // Use public method to update UI this.plugin.checkDataviewAPI(); } }) @@ -2100,7 +2100,7 @@ class TaskProgressBarSettingTab extends PluginSettingTab { // Set preset colors if red-orange-green is selected if (value === "red-orange-green") { - // Đặt lại các giá trị màu sắc và ngưỡng + // Reset color values and thresholds this.plugin.settings.lowProgressColor = "#e06c75"; // Red this.plugin.settings.mediumProgressColor = @@ -2110,12 +2110,12 @@ class TaskProgressBarSettingTab extends PluginSettingTab { this.plugin.settings.completeProgressColor = "#98c379"; // Green - // Đặt lại các ngưỡng + // Reset thresholds this.plugin.settings.lowProgressThreshold = 30; this.plugin.settings.mediumProgressThreshold = 60; this.plugin.settings.highProgressThreshold = 99; - // Hiển thị thông báo để xác nhận thay đổi + // Show notice to confirm changes new Notice( "Applied Red-Orange-Blue-Green color scheme" ); diff --git a/manifest.json b/manifest.json index b3e6ab7..c3cf040 100644 --- a/manifest.json +++ b/manifest.json @@ -1,11 +1,11 @@ { "id": "progress-tracker", "name": "ProgressTracker", - "version": "2.0.1", + "version": "1.1.0", "minAppVersion": "0.15.0", "description": "Track task completion with a visual progress bar in your sidebar", "author": "VN", - "authorUrl": "https://wpmasterynow.com", - "fundingUrl": "https://wpmasterynow.com", + "authorUrl": "https://github.com/vannamhh/", + "fundingUrl": "https://github.com/vannamhh/", "isDesktopOnly": false }