# Tasks Companion

A first look at Workout Journal

A Plugin providing some convenience features for interacting with the Task plugin. I have been always uncomfortable with auto rollover, as it is easy to loose track and be unintentional about what is being rolled over. The other option manual rollover is intentional but unnecessarily tedious. I wanted the best of both worlds automatic collection and intentional rollover. Which not only feels natural but also looks appealing and intuitive. For the longest time this was solved by stupidly long dataview-js queries, but not anymore. To preserve my own sanity and maybe yours, if your brain is wired like mine, I present the plugin version of these stupidly long dataview-js queries. The main idea: Have rollover blocks you engage with intentionally from which you can directly edit the notes you query. I have furthermore added some convenience features I always wanted with Tasks. I mean if I am making a plugin anyway, way wouldn't I? So enter on a task not only autocompletes bullets but also the filter tag. And you have two commands, one for scheduling a task that is not yet scheduled and one for toggling task on a line or checkbox. Furthermore, commands to put dates on a task from a pop up. It works with [Periodic Notes](https://github.com/liamcain/obsidian-periodic-notes) when that's installed, reading folders and date formats from its settings, and works perfectly well on its own using its own settings. ## Tagging tasks The plugin tracks checklist items carrying a tag you choose — `#task` by default. Two commands make that tag cheap to apply. **Toggle task tag on the current line or selection.** Select a stretch of lines and run it: the tag is inserted between the checkbox and the content. - [ ] call the plumber → - [ ] #task call the plumber - [ ] book the MOT → - [ ] #task book the MOT Plain bullets and plain lines are turned into tasks on the way, so an ordinary list becomes a tracked one in a single step. Turn off *Tagging also creates checkboxes* if you'd rather only touch existing checklist items. - call the plumber → - [ ] #task call the plumber call the plumber → - [ ] #task call the plumber The command is a real toggle: if every line it would touch is already tagged, it strips the tag instead. A mixed selection tags everything. There's no default hotkey — bind one in Settings → Hotkeys. **Carrying the tag onto the next line.** With this on, pressing enter at the end of a tagged task starts the next item already tagged, so a run of tasks can be typed without repeating yourself. - [ ] #task call the plumber⏎ - [ ] #task ▌ It only fires on a tagged, non-empty checklist item with the cursor past the tag. Enter on an empty item still ends the list, and everything else keeps Obsidian's normal behaviour. Both features use the same tag as the rollover blocks, so tagging a line is what puts it in the inbox. ## Scheduling tasks **Schedule the task on the current line or selection.** Run it anywhere in the vault — the task doesn't have to be tagged, or in a periodic note — and a small date prompt opens with the field already focused, so a date is one short burst of typing and enter. The same prompt is a click away too: hover a tagged task anywhere in a note, in either editing or reading view, and a calendar icon appears beside it. Turn that off with *Calendar icon on tagged tasks* if you'd rather use the command alone. The field takes rather more than an ISO date (like in the natural language plugin): | You type | You get | | --- | --- | | `today`, `tomorrow`, `tmr`, `yesterday` | the obvious thing | | `fri`, `friday`, `next friday` | the next Friday — never today | | `next week`, `next month`, `next year` | a week, month or year out | | `+3`, `+3d`, `2w`, `+1m`, `-2d` | an offset in days, weeks, months or years | | `22` | the 22nd — this month, or next if it's gone | | `12-01`, `12 dec`, `dec 12` | that day this year, or next if it's gone | | `2026-12-01`, `2026/12/01` | exactly that | Anything ambiguous resolves forwards, since this only ever schedules work. What you've typed so far is spelled out beneath the field, and the month below follows along: click a day to schedule it straight away, or tab into the grid and walk it with the arrow keys (page up and down move by month). If the task already has a date, it's there when the prompt opens, and **Clear date** removes it. **Three dates, not one.** The buttons at the top choose which one you're setting: ⏳ scheduled (the default, and the one the blocks read), 📅 due, or 🛫 start. You can stay on the keyboard for that too — lead with the field name and the prompt follows: due friday → 📅 the coming Friday start +3d → 🛫 three days out fri → ⏳ the coming Friday Each field is written and cleared independently, so a task can carry all three. These are the Tasks plugin's own three planning dates; its record-keeping ones — created, done and cancelled — are left out, because they mark something that already happened and this prompt only ever resolves forwards. The date is written in whichever notation *Schedule format* is set to — `[due:: 2026-12-01]` or `📅 2026-12-01` — replacing any date already on the line for that field, in either notation. A selection spanning several tasks schedules them all in one step; lines that aren't checklist items are left alone. There's no default hotkey — bind one in Settings → Hotkeys. ## Parking a task **Move the task on the current line or selection to the collection note.** Run it on any task, anywhere, tagged or not — it's the in-note version of a periodic block's *put on hold* button. The task goes under the target heading in the collection note with its subtasks, and leaves its dates behind: scheduled, due and start come off the parent line, so a parked task shows up as unscheduled again. With several tasks selected, they all go in one step. There's no default hotkey — bind one in Settings → Hotkeys. ## Blocks Three modes, each placed as its own block so you can position them freely. **Unfinished tasks from earlier notes** — the classic rollover. Pulls from periodic notes of the same granularity dated strictly before the current one. Granularity is auto-detected from the note you're in, so the same block text works in daily, weekly, monthly, quarterly and yearly templates. ```rollover mode: periodic title: Rollover inbox ``` Force a granularity when you want a weekly note to show the day-level backlog: ```rollover mode: periodic granularity: day title: Unfinished this week ``` **Collection note** — everything parked in your long-term note. ```rollover mode: collection title: Unscheduled & long-term ``` **Unscheduled elsewhere** — open tasks anywhere in the vault with no scheduled or due date. Periodic note folders and the collection note are excluded automatically. ```rollover mode: unscheduled title: Unscheduled elsewhere in the vault exclude: [Archive, Meta/Templates] ``` ### Block options | Key | Values | Notes | | --- | --- | --- | | `mode` | `periodic`, `collection`, `unscheduled` | Defaults to `periodic`. | | `granularity` | `day`, `week`, `month`, `quarter`, `year` | `periodic` mode only. Defaults to the current note's own granularity. | | `title` | any text | Omit for no heading. | | `exclude` | `[A, B]` or `A, B` | Added to the folders excluded in settings. | | `limit` | number | Cap the number of rows. | | `show-source` | `true` / `false` | Whether to show the originating note. | ## Actions Each row carries the actions that make sense for it: - **checkbox** — writes `[x]` to the line in its source note. Right-click it (or long-press on a phone) for the other states: todo, in progress `[/]`, done and cancelled `[-]`. In-progress tasks count as unfinished, so they keep rolling over; done and cancelled ones drop out. - **open** — open the source note - **move here** — move the task and its subtasks under the target heading in the current note - **put on hold** — move the task and its subtasks to the collection note (`periodic` mode) - **schedule** — opens the date prompt above (`unscheduled` mode) Two looks for those buttons, set by **Row actions**: - **Minimal** (default) — icon buttons, appearing only when you hover the row, so the list stays quiet until you reach for it. - **Emoji** — 🔗 ➡️ ⏸️ ⏳, always visible. These are the emoji the [Tasks](https://github.com/obsidian-tasks-group/obsidian-tasks) plugin uses for the same ideas, so a vault built around it doesn't end up reading two visual languages at once. Subtasks render nested and always travel with their parent. A move inserts into the destination before cutting from the source, so an interrupted move leaves a duplicate rather than losing the task. ## Settings Task tag, whether the tag must start the line, target heading, collection note, excluded folders, schedule format and row action style. Under **Editing**, the two tagging behaviours above and whether tagged tasks show the calendar icon on hover. Under **Periodic notes**, per-granularity folder and format fallbacks, used only where Periodic Notes isn't supplying them. ## Performance Tasks are parsed once into an in-memory index, then patched per file from the metadata cache's `changed` event — which supplies the new content, so no re-read is needed. Only files the cache already reports as containing checklist items are read at startup. Blocks render from the index rather than rescanning the vault, so adding more blocks costs very little. ## Known limits - A blank line between a parent task and its subtasks ends the block; subtasks after a blank line will not travel with the parent. - Block detection is by indentation, so any deeper-indented line beneath a task moves with it, including non-task prose. - Line lookup matches on task text, so two byte-identical task lines in one file could resolve to the wrong one. - Scheduling stamps the parent line only; subtasks stay unannotated. - Tag continuation doesn't extend ordered lists — enter there behaves normally. ## Development ```sh npm install npm run dev # watch build npm run build # typecheck, then bundle to main.js npm test # run the test suites ``` `npm test` bundles each suite in `tests/` against a stub of the `obsidian` module and runs it in node, covering the pure logic: task parsing, tagging, tag continuation, path handling, periodic-note detection, date input and the line-edit layer that writes to files. Anything needing a live app — rendering, the settings tab, the date prompt, the metadata cache — has to be tried in Obsidian. Releases are cut by pushing a tag named for the version: ```sh git tag 0.1.1 && git push origin 0.1.1 ``` The workflow in `.github/workflows/main.yml` stamps the version into `manifest.json`, `versions.json` and `package.json`, commits that to the default branch, and moves the tag onto that commit, so the release comes from a commit whose manifest carries its version. It then builds, attests the build's provenance, and attaches `main.js`, `manifest.json` and `styles.css` to the release. Afterwards, `git pull` and `git fetch --tags --force` bring the bump commit and the moved tag back locally. ## License MIT