mirror of
https://github.com/cryptic0011/real-tasks.git
synced 2026-07-22 06:54:55 +00:00
Initial commit
This commit is contained in:
commit
fbcf455e0f
6 changed files with 234 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.DS_Store
|
||||
node_modules/
|
||||
data.json
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2026 Grayson Patterson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
71
README.md
Normal file
71
README.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Real Tasks
|
||||
|
||||
An Obsidian plugin that deletes task lines shortly after their checkbox is ticked. Your notes keep only the tasks that still need doing — no graveyard of strikethroughs, no manual cleanup.
|
||||
|
||||
## Why
|
||||
|
||||
Most task workflows leave completed tasks in the file as `- [x]` lines. That's great if you want a historical log; it's noise if you want your notes to function like a todo app. Real Tasks removes that noise automatically while preserving undo.
|
||||
|
||||
## How it works
|
||||
|
||||
- When you check off a task in the editor, Real Tasks waits **800 ms**, then removes the whole line via the editor API. Undo (`Cmd/Ctrl-Z`) restores it.
|
||||
- When a task gets completed programmatically — for example by Dataview checkbox clicks in an embedded query, or by a sync client — Real Tasks catches the `vault.modify` event, waits **400 ms**, and rewrites the file with the completed lines stripped out.
|
||||
- A ribbon button and command insert a new `- [ ] ` task line at the cursor, ready to type.
|
||||
|
||||
## Features
|
||||
|
||||
- Auto-delete completed task lines (`- [x]`, `- [X]`, `* [x]`, `+ [x]`).
|
||||
- Short delay after completion to allow for accidental clicks (`Cmd/Ctrl-Z` to recover).
|
||||
- Works from both the editor and external writes (Dataview, sync, external tools).
|
||||
- Ribbon icon and command **Insert new task** for quick task creation.
|
||||
- Command **Sweep completed tasks in this note now** for ad-hoc cleanup.
|
||||
- Desktop and mobile support.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Install and enable the plugin.
|
||||
2. Write tasks as normal: `- [ ] something to do`.
|
||||
3. Check the box. ~1 second later the line vanishes.
|
||||
4. Accidentally checked? Press `Cmd/Ctrl-Z` within the delay window, or immediately after, to restore the line.
|
||||
5. Tap the ribbon icon (a check-square) to insert a new task at your cursor. Optionally bind `Insert new task` to a hotkey in Settings → Hotkeys.
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
| ---------------------------------------------- | ----------------------------------------------------------- |
|
||||
| `Real Tasks: Insert new task` | Insert a `- [ ] ` line at the cursor. |
|
||||
| `Real Tasks: Sweep completed tasks in this note now` | Immediately remove all completed tasks from the active note. |
|
||||
|
||||
## Compatibility
|
||||
|
||||
- Tested on Obsidian `1.4.0` and above.
|
||||
- Desktop and mobile (iOS, Android).
|
||||
- Compatible with Dataview `TASK` queries — checking a task in an embedded Dataview result triggers the auto-delete.
|
||||
|
||||
## Installation
|
||||
|
||||
### Community plugins (once published)
|
||||
|
||||
1. Open **Settings → Community plugins**.
|
||||
2. Search for **Real Tasks**.
|
||||
3. Install and enable.
|
||||
|
||||
### Manual install
|
||||
|
||||
1. Download `main.js` and `manifest.json` from the latest [release](https://github.com/cryptic0011/real-tasks/releases).
|
||||
2. Create a folder `<VAULT>/.obsidian/plugins/real-tasks/`.
|
||||
3. Drop both files inside.
|
||||
4. Enable the plugin in **Settings → Community plugins**.
|
||||
|
||||
## Settings
|
||||
|
||||
No settings page in this release. Everything runs on sensible defaults. Future versions may expose the delete delay and checkbox filters.
|
||||
|
||||
## Development
|
||||
|
||||
- `main.js` is hand-written plain JavaScript — no build step, no bundler, no TypeScript.
|
||||
- To modify locally, edit `main.js` then reload the plugin via **Settings → Community plugins** toggle, or restart Obsidian.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
126
main.js
Normal file
126
main.js
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
"use strict";
|
||||
|
||||
const { Plugin, MarkdownView, Notice, TFile } = require("obsidian");
|
||||
|
||||
const TASK_PREFIX = "- [ ] ";
|
||||
|
||||
const EDITOR_DELAY_MS = 800;
|
||||
const VAULT_DELAY_MS = 400;
|
||||
const COMPLETED_TASK_RE = /^\s*[-*+]\s+\[[xX]\]\s/;
|
||||
|
||||
module.exports = class RealTasksPlugin extends Plugin {
|
||||
onload() {
|
||||
this._editorTimer = null;
|
||||
this._vaultTimers = new Map(); // path -> timeout handle
|
||||
this._suppressUntil = new Map(); // path -> timestamp we wrote the file
|
||||
|
||||
// Interactive typing path: preserves undo via the editor API.
|
||||
this.registerEvent(
|
||||
this.app.workspace.on("editor-change", (editor) => {
|
||||
if (this._editorTimer) clearTimeout(this._editorTimer);
|
||||
this._editorTimer = setTimeout(() => this.sweepEditor(editor), EDITOR_DELAY_MS);
|
||||
})
|
||||
);
|
||||
|
||||
// Programmatic writes (Dataview checkbox, sync, etc.) — use vault API.
|
||||
this.registerEvent(
|
||||
this.app.vault.on("modify", (file) => {
|
||||
if (!(file instanceof TFile) || file.extension !== "md") return;
|
||||
const until = this._suppressUntil.get(file.path) || 0;
|
||||
if (Date.now() < until) return; // our own write bouncing back
|
||||
|
||||
const existing = this._vaultTimers.get(file.path);
|
||||
if (existing) clearTimeout(existing);
|
||||
const handle = setTimeout(() => {
|
||||
this._vaultTimers.delete(file.path);
|
||||
this.sweepFile(file);
|
||||
}, VAULT_DELAY_MS);
|
||||
this._vaultTimers.set(file.path, handle);
|
||||
})
|
||||
);
|
||||
|
||||
this.addCommand({
|
||||
id: "sweep-now",
|
||||
name: "Sweep completed tasks in this note now",
|
||||
editorCallback: (editor) => this.sweepEditor(editor),
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "insert-task",
|
||||
name: "Insert new task",
|
||||
editorCallback: (editor) => this.insertTask(editor),
|
||||
});
|
||||
|
||||
this.addRibbonIcon("check-square", "Insert new task", () => {
|
||||
const view = this.app.workspace.getActiveViewOfType(MarkdownView);
|
||||
if (!view) {
|
||||
new Notice("Open a note first to insert a task.");
|
||||
return;
|
||||
}
|
||||
this.insertTask(view.editor);
|
||||
view.editor.focus();
|
||||
});
|
||||
}
|
||||
|
||||
onunload() {
|
||||
if (this._editorTimer) clearTimeout(this._editorTimer);
|
||||
for (const h of this._vaultTimers.values()) clearTimeout(h);
|
||||
this._vaultTimers.clear();
|
||||
}
|
||||
|
||||
insertTask(editor) {
|
||||
if (!editor) return;
|
||||
const cursor = editor.getCursor();
|
||||
const currentLine = editor.getLine(cursor.line) || "";
|
||||
const atLineStart = currentLine.trim() === "";
|
||||
const prefix = atLineStart ? TASK_PREFIX : "\n" + TASK_PREFIX;
|
||||
editor.replaceRange(prefix, cursor);
|
||||
const newLine = atLineStart ? cursor.line : cursor.line + 1;
|
||||
editor.setCursor({ line: newLine, ch: TASK_PREFIX.length });
|
||||
}
|
||||
|
||||
sweepEditor(editor) {
|
||||
if (!editor) return;
|
||||
for (let i = editor.lineCount() - 1; i >= 0; i--) {
|
||||
const line = editor.getLine(i);
|
||||
if (line == null) continue;
|
||||
if (!COMPLETED_TASK_RE.test(line)) continue;
|
||||
|
||||
const lastLine = i === editor.lineCount() - 1;
|
||||
const from = { line: i, ch: 0 };
|
||||
const to = lastLine
|
||||
? { line: i, ch: line.length }
|
||||
: { line: i + 1, ch: 0 };
|
||||
editor.replaceRange("", from, to);
|
||||
}
|
||||
}
|
||||
|
||||
async sweepFile(file) {
|
||||
const leaves = this.app.workspace.getLeavesOfType("markdown");
|
||||
for (const leaf of leaves) {
|
||||
const view = leaf.view;
|
||||
if (view instanceof MarkdownView && view.file && view.file.path === file.path) {
|
||||
this.sweepEditor(view.editor);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let content;
|
||||
try {
|
||||
content = await this.app.vault.read(file);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
const lines = content.split("\n");
|
||||
const kept = lines.filter((l) => !COMPLETED_TASK_RE.test(l));
|
||||
if (kept.length === lines.length) return;
|
||||
|
||||
const next = kept.join("\n");
|
||||
this._suppressUntil.set(file.path, Date.now() + 1500);
|
||||
try {
|
||||
await this.app.vault.modify(file, next);
|
||||
} catch (e) {
|
||||
console.warn("[real-tasks] vault write failed", file.path, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
10
manifest.json
Normal file
10
manifest.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"id": "real-tasks",
|
||||
"name": "Real Tasks",
|
||||
"version": "1.0.0",
|
||||
"minAppVersion": "1.4.0",
|
||||
"description": "Deletes task lines shortly after their checkbox is marked done so your notes only keep real, open work. Includes a ribbon button for quickly inserting a new task.",
|
||||
"author": "Grayson Patterson",
|
||||
"authorUrl": "https://github.com/YOUR-GITHUB-USERNAME",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
3
versions.json
Normal file
3
versions.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"1.0.0": "1.4.0"
|
||||
}
|
||||
Loading…
Reference in a new issue