From 9780d95223dad5ceb4250e81ccac918ed0f92929 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Fri, 3 Jul 2026 14:19:20 +0000 Subject: [PATCH] Docs: bring SPEC/README/CHANGELOG current through v0.5.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SPEC.md: retitled from 'v1 Draft' to a living-document status line; added missing Schema Reference sections for gtd-kanban (2.3) and gtd-matrix (2.4, both modes) — previously undocumented outside the roadmap; added unscheduled-todos/unscheduled-notes to the calendar table; rewrote Rendering (§4) and Architecture (§5) to reflect the three-block pipeline, shared collectors, and tag-fetch machinery; corrected v0.2.0 roadmap heading (recurrence shipped, strip horizon was not built — previously overclaimed) - New CHANGELOG.md: one entry per shipped version, 0.1.0 through 0.5.0 - README.md: fixed stale test count (47 -> 88), linked CHANGELOG, added a 'Cutting a release' checklist including the npm whoami / E404 auth-expiry gotcha hit across several publishes - No source changes; 88 tests still passing --- CHANGELOG.md | 51 ++++++++++++++++++ README.md | 12 ++++- SPEC.md | 130 ++++++++++++++++++++++++++++++++++------------ package-lock.json | 4 +- 4 files changed, 159 insertions(+), 38 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..05c5681 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,51 @@ +# Changelog + +All notable changes to GTD Calendar are documented here. Versions follow the plugin's `manifest.json` / `package.json` version, which also matches the published npm releases. + +## 0.5.0 — The Skeleton Matrix + +- Added `mode: skeleton | eisenhower` to `gtd-matrix` (default: `skeleton`). +- **Skeleton mode** replaces Eisenhower's opinion-based axes with derived ones: rows = active (`in-progress-tag`, shared with the kanban), columns = due soon (`urgent-window` days, default 3, overdue included; `urgent-tag` as a manual override for dateless items). Quadrants: **Do Next**, **Scheduled**, **On Deck**, **Backlog** — nothing is ever "Eliminate". +- Classic Eisenhower preserved via `mode: eisenhower`. +- `MatrixBoard` is now positional internally (top-left…bottom-right); labels are supplied per mode, and rendering is fully shared between the two modes. + +## 0.4.0 — Eisenhower Matrix + +- New `gtd-matrix` block: a 2×2 prioritisation grid with labelled axes. +- Quadrants from two tags (`urgent-tag`, `important-tag`): Do First / Schedule / Delegate / Eliminate. +- Completed to-dos excluded entirely (a prioritisation view, not a tracking view). +- Cards reuse the kanban's rendering (compact, recurrence mark, drilldown, `card-detail`). + +## 0.3.2 — Unscheduled visibility toggles + +- Added `unscheduled-todos` and `unscheduled-notes` options to `gtd-calendar` (default: `yes` for both; also accepts `true`/`false`, `show`/`hide`). +- Lets a dashboard with multiple stacked calendar blocks show each Unscheduled sub-section under only one of them, instead of duplicating the list. + +## 0.3.1 — Split Unscheduled section + +- The Unscheduled area now shows two labelled sub-sections — **Unscheduled to-dos** (first) and **Unscheduled notes** — instead of one mixed list. +- A sub-section is omitted entirely when it has no items. + +## 0.3.0 — Kanban board + +- New `gtd-kanban` block: three columns (Backlog / In Progress / Done) built from to-dos only. +- Bucketing: `todo_completed` wins → Done; else the `in-progress-tag` → In Progress; else → Backlog. +- Done column limited to a configurable `done-window` (default 7 days; `all` for full history). +- Per-column sort (`due-date` / `title` / `modified-date`); dateless cards sort last under `due-date`. +- Compact cards with a configurable detail panel (`card-detail: hover | always | none`). +- Read-only. + +## 0.2.0 — Recurring to-do awareness + +- Recurring to-dos (as managed by the *Repeating To-Dos* plugin, v2) are marked with a ↻ symbol across every view. +- Detected via the `recurring` tag the Repeating To-Dos plugin maintains as its own query index — no coupling to its note-body format, no code duplication of its recurrence logic. + +## 0.1.0 — Initial release + +- Forked from [WeMakeMachines/joplin-plugin-event-calendar](https://github.com/WeMakeMachines/joplin-plugin-event-calendar). +- `gtd-calendar` block populated by real notes and to-dos (instead of hand-written YAML) in the calendar note's folder and, optionally, its descendants (`scope`). +- Views: `day`, `week`, `month` (compact tile strip, upstream-style) and `month-grid` (full current-month 7-column grid with weekday headers). +- Notes and to-dos opt in via a `gtd` fenced block (empty block valid); to-dos schedule automatically from their Joplin due date. +- Unscheduled items (no resolvable date) collect below the grid. +- Drilldown: every tile opens its source note. +- Fixed an upstream bug where the week view silently grouped by month. diff --git a/README.md b/README.md index 49cc484..c702516 100644 --- a/README.md +++ b/README.md @@ -189,11 +189,19 @@ Issues and ideas: [the repository](https://gitea.skeletonworks.online/vwiebe/jop ```bash npm install -npm test # 47 unit tests +npm test # 88 unit tests npm run dist # builds publish/*.jpl ``` -Design notes and the full v1 specification live in [SPEC.md](SPEC.md). +Design notes and the full specification live in [SPEC.md](SPEC.md). Release history is in [CHANGELOG.md](CHANGELOG.md). + +### Cutting a release + +1. Bump `version` in **both** `src/manifest.json` and `package.json` (they must match — the Joplin plugin repo reads the manifest, npm reads package.json). +2. Add an entry to `CHANGELOG.md`. +3. `npm publish` (the `prepare` script rebuilds the `.jpl` fresh from source, so you can't accidentally publish a stale build). + +If `npm publish` fails with `E404` on the `PUT`, check `npm whoami` first — a 401 there means your login session has expired, which npm surfaces as a misleading 404 on publish. Fix with `npm login --auth-type=legacy`, or avoid it entirely with a [granular access token](https://docs.npmjs.com/creating-and-viewing-access-tokens) so publishing never needs an interactive session. ## Credits & license diff --git a/SPEC.md b/SPEC.md index f797282..ebba3e6 100644 --- a/SPEC.md +++ b/SPEC.md @@ -1,9 +1,11 @@ -# GTD Calendar — Project Specification (v1 Draft) +# GTD Calendar — Project Specification -A fork of [WeMakeMachines/joplin-plugin-event-calendar](https://github.com/WeMakeMachines/joplin-plugin-event-calendar) (MIT) that inverts the plugin's data model: instead of events living as YAML inside the calendar note, the calendar is **populated by real Joplin notes and todos** in the surrounding folder tree, with clickable drilldown to the source note. +*Status: living document, current through v0.5.0. Sections 1–5 describe the shipped design; §9 tracks what's still ahead.* -**Working name:** `joplin-plugin-gtd-calendar` -**Proposed manifest id:** `com.victorwiebe.joplin.plugin.gtd-calendar` (must differ from upstream's id so both plugins can coexist during development) +A fork of [WeMakeMachines/joplin-plugin-event-calendar](https://github.com/WeMakeMachines/joplin-plugin-event-calendar) (MIT) that inverts the plugin's data model: instead of events living as YAML inside the calendar note, the plugin's views are **populated by real Joplin notes and todos** in the surrounding folder tree, with clickable drilldown to the source note. What started as a single calendar view has grown into three composable views — calendar, kanban, and a priorities matrix — sharing one data layer and one tag/date vocabulary. + +**Package name:** `joplin-plugin-gtd-calendar` (npm) +**Manifest id:** `com.victorwiebe.joplin.plugin.gtd-calendar` --- @@ -39,8 +41,12 @@ sort-type: title # title | modified_date (default: title) | `todos` | `gtd-only`, `all`, `none` | `gtd-only` | Same semantics for todos. | | `sort` | `asc`, `desc` | `asc` | Order of events **within** each day/week/month grouping and within Unscheduled. Groupings themselves always run chronologically. | | `sort-type` | `title`, `modified_date` | `title` | Field used by `sort`. `modified_date` maps to Joplin's `updated_time`. | +| `unscheduled-todos` | `yes`/`no` (also `true`/`false`, `show`/`hide`) | `yes` | Whether the Unscheduled **to-dos** sub-section is shown. | +| `unscheduled-notes` | `yes`/`no` (also `true`/`false`, `show`/`hide`) | `yes` | Whether the Unscheduled **notes** sub-section is shown. Independent of `unscheduled-todos` — lets a dashboard split the two lists across stacked calendar blocks instead of duplicating both. | -Unknown keys are ignored with a console warning. A malformed block renders an inline error box rather than failing silently (improvement over upstream, which swallows parse errors into the console). +Unscheduled items (no resolvable date) are split into two labelled sub-sections — to-dos first, then notes — each hidden when empty or toggled off. + +Unknown keys are ignored with a warning. A malformed block renders an inline warning above the calendar rather than failing silently. ### 2.2 The `gtd` block (lives in participating notes/todos) @@ -66,6 +72,47 @@ text: Hover text # optional; tooltip detail, as upstream An **empty** ` ```gtd ` block is valid and meaningful: it is the opt-in signal with all defaults. +### 2.3 The `gtd-kanban` block (lives in the board note) + +``` +```gtd-kanban +title: Editorial Board +scope: this-folder # same semantics as gtd-calendar +todos: gtd-only # gtd-only | all | none (default: gtd-only) +sort-type: due-date # due-date | title | modified-date (default: due-date) +sort: asc # asc | desc (default: asc) +in-progress-tag: in-progress # default: "in-progress" +card-detail: hover # hover | always | none (default: hover) +done-window: 7 # days, or "all" (default: 7) +``` + +Three columns, always shown: **Backlog**, **In Progress**, **Done**. To-dos only — plain notes never appear. Bucketing: `todo_completed > 0` → Done (wins regardless of tags); else the `in-progress-tag` → In Progress; else → Backlog. Backlog shows everything uncompleted and untagged, dated or not. Done is filtered to to-dos completed within `done-window` days (`all` for full history), preventing unbounded growth. Sorting is per-column; under `due-date`, cards without a due date sort after all dated ones regardless of direction. Read-only in the current version — reflects to-do state, does not change it. + +### 2.4 The `gtd-matrix` block (lives in the matrix note) + +``` +```gtd-matrix +mode: skeleton # skeleton | eisenhower (default: skeleton) +title: Priorities +scope: this-folder +todos: gtd-only +sort-type: due-date +sort: asc +urgent-tag: urgent # default: "urgent" +important-tag: important # Eisenhower mode only; default: "important" +in-progress-tag: in-progress # Skeleton mode only; default: "in-progress" +urgent-window: 3 # Skeleton mode only, days; default: 3 +card-detail: hover +``` + +A 2×2 grid with labelled axes. To-dos only; **completed to-dos are excluded entirely** in both modes (a prioritisation view, not a tracking view — the kanban's Done column is where completions live). The board is internally positional (top-left / top-right / bottom-left / bottom-right); each mode supplies its own axis and quadrant labels. + +**`mode: skeleton`** (default) — rows = active (`in-progress-tag`, the same tag the kanban uses); columns = due soon (due date within `urgent-window` days, overdue included, or the `urgent-tag` as a manual override for dateless items). Quadrants: **Do Next** (active + due soon), **Scheduled** (active, not due soon — includes dateless in-progress work), **On Deck** (not active + due soon — the early-warning quadrant), **Backlog** (neither). Designed to complement the calendar and kanban directly: same in-progress tag, dates drive urgency instead of a second opinion-tag, and nothing is ever condemned as "Eliminate" — a Backlog item is simply low priority. In-progress + `urgent-tag` + no date resolves to Do Next (the urgent tag is an explicit override, so it wins over the absence of a date). + +**`mode: eisenhower`** — the classic: rows = `important-tag`; columns = `urgent-tag`. Quadrants: Do First (both), Schedule (important only), Delegate (urgent only), Eliminate (neither). + +A warning is raised if `urgent-tag` and `important-tag` resolve to the same tag (Eisenhower mode only — the axes would collapse). Read-only. + --- ## 3. Data Model Rules @@ -96,59 +143,74 @@ The calendar note itself is always excluded from results, as is any note whose o ## 4. Rendering -- Reuses upstream's grouping/renderer architecture (`DayGrouping`/`WeekGrouping`/`MonthGrouping` + DOM renderers + CSS asset), including the empty-grouping placeholders and current-date highlight. -- **Fixes upstream bug:** `Calendar/index.ts` imports `WeekGrouping` from `Month/MonthGrouping`, so the week view silently groups by month. Corrected in the fork. -- **Type distinction:** todos render with a checkbox glyph (☐ / ☑ when completed); notes render with a document glyph. Glyphs are suppressed if the item supplies its own `icon`. A subtle border-style difference (solid vs. dashed, TBD in CSS) keeps the distinction visible under custom colours. -- **Unscheduled section:** pinned below the grid, with its own header, honoring `sort` / `sort-type`. -- **Drilldown:** each tile is clickable and opens the source note. +- **Calendar** reuses upstream's grouping/renderer architecture (`DayGrouping`/`WeekGrouping`/`MonthGrouping` + DOM renderers + CSS asset), including the empty-grouping placeholders and current-date highlight, plus the fork's own `month-grid` (a 7-column grid of the entire current month). +- **Fixed upstream bug:** `Calendar/index.ts` imported `WeekGrouping` from `Month/MonthGrouping`, so the week view silently grouped by month. Corrected in the fork. +- **Type distinction:** todos render with a checkbox glyph (☐ / ☑ when completed); notes render with a document glyph. Glyphs are suppressed if the item supplies its own `icon`. Recurring to-dos (per the `recurring` tag) get a ↻ suffix to the right of any icon, across every view. +- **Unscheduled section:** below the calendar grid, split into two labelled sub-sections — to-dos first, then notes — each hidden when empty or switched off (`unscheduled-todos` / `unscheduled-notes`). +- **Kanban** renders three fixed columns (Backlog / In Progress / Done) of compact cards; a "hover" mode shows due date and hover text only on mouseover (`card-detail`). +- **Matrix** renders a 2×2 grid with axis and quadrant labels supplied per-mode by the plugin process; cards are visually and behaviourally identical to kanban cards. +- **Drilldown:** every tile, card, hover-card row, and chip across all three block types is clickable and opens the source note. --- ## 5. Architecture -Upstream is a single synchronous markdown-it content script with no data API access. The fork becomes a three-part plugin: +Upstream is a single synchronous markdown-it content script with no data API access. The fork is a three-part plugin, now serving three fence types (`gtd-calendar`, `gtd-kanban`, `gtd-matrix`) through the same pipeline: ``` ┌─────────────────────────────┐ -│ 1. Markdown-it content │ Intercepts ```gtd-calendar fences. -│ script (renderer) │ Parses YAML config, emits a placeholder -│ │
carrying the config as data-attrs. +│ 1. Markdown-it content │ Intercepts ```gtd-calendar / ```gtd-kanban / +│ script (renderer) │ ```gtd-matrix fences. Parses YAML config, +│ │ emits a placeholder
tagged with +│ │ data-block-type and the config as a +│ │ data attribute. └──────────────┬──────────────┘ │ (rendered HTML) ┌──────────────▼──────────────┐ │ 2. Webview asset script │ Finds placeholders, calls -│ (runs in rendered note) │ webviewApi.postMessage(config). -│ │ Receives event data, builds the -│ │ calendar DOM via the (reused) -│ │ grouping/renderer classes. -│ │ Click → postMessage({open: noteId}). +│ (runs in rendered note) │ webviewApi.postMessage(config) with +│ │ getEvents / getKanban / getMatrix +│ │ depending on block type. Builds the +│ │ calendar / board / matrix DOM from the +│ │ returned payload. Click → postMessage +│ │ ({type: "openNote", noteId}). └──────────────┬──────────────┘ │ postMessage / onMessage ┌──────────────▼──────────────┐ -│ 3. Main plugin process │ joplin.contentScripts.onMessage: -│ (index.ts) │ • resolve current note's folder +│ 3. Main plugin process │ joplin.contentScripts.onMessage routes +│ (index.ts) │ by message type to a handler per block +│ │ type, each of which: +│ │ • resolves current note's folder │ │ (joplin.workspace.selectedNote) -│ │ • build folder tree, apply scope -│ │ • fetch notes/todos (id, title, -│ │ is_todo, todo_due, todo_completed, -│ │ updated_time, body) -│ │ • parse ```gtd blocks from bodies -│ │ • apply inclusion matrix, return -│ │ structured event list -│ │ • handle open: joplin.commands -│ │ .execute('openNote', noteId) +│ │ • builds folder tree, applies scope +│ │ (src/Gtd/folderScope.ts) +│ │ • fetches notes/todos, paginated +│ │ • parses ```gtd blocks from bodies +│ │ (src/Gtd/gtdBlock.ts) +│ │ • fetches tag titles per to-do +│ │ (getNoteTagTitles) for recurrence / +│ │ in-progress / urgent / important +│ │ detection +│ │ • applies the inclusion matrix, date +│ │ resolution, and view-specific +│ │ bucketing (collectEvents.ts / +│ │ collectKanban.ts / collectMatrix.ts) +│ │ • handles openNote via +│ │ joplin.commands.execute └─────────────────────────────┘ ``` +All three collectors (`collectEvents`, `collectKanban`, `collectMatrix`) share `folderScope.ts`, `gtdBlock.ts`, and `resolveDate.ts` — the inclusion-matrix and date-resolution logic is written once and reused by every view. + ### Performance notes - Folder tree and note list fetched per render request; paginated API calls (`page`/`has_more`). -- Body scanning is limited to items in scope. For large scopes, pre-filter with Joplin's search API (`body:"```gtd"`-style query) before fetching full bodies. Cache by `updated_time` if needed; not a v1 requirement. -- Refresh model: re-render occurs on note switch (Joplin behavior). `joplin.workspace.onNoteChange` live refresh is a stretch goal, not v1. +- Body scanning is limited to items in scope. Kanban and matrix additionally fetch tag titles per to-do (one call each) for recurrence/state detection. For large scopes, pre-filter with Joplin's search API before fetching full bodies; cache by `updated_time` if needed — not yet required at observed real-world scales (largest tested: ~285 notes, single folder scope), but worth revisiting if a `children`-scope board on a large tree feels slow, especially with Persistent Layout keeping several boards rendered at once. +- Refresh model: re-render occurs on note switch (Joplin behavior); no live refresh on background data changes. Fine for a read-only plugin; becomes a sharper question once the authoring axis (§9) lands, where a write on one rendered board should ideally refresh others. ### Platform caveats -- Desktop is the target. Mobile plugin support is partial; `openNote` behavior there is unverified. The "embed search" pairing remains a documented fallback for mobile users. +- Desktop is the target. Mobile plugin support is partial and this plugin's core mechanisms (custom fence rendering, webview↔plugin messaging) are not expected to work there; documented explicitly in the README. Underlying to-dos still sync and appear in Joplin's native mobile views. The "Embed Search" plugin pairing remains a documented pattern for cross-scope aggregation generally, not specifically a mobile workaround. --- @@ -184,7 +246,7 @@ Upstream is MIT (© Franco Speziali / WeMakeMachines). The fork retains the MIT Recorded direction, not commitments. Each gets a focused design pass before work begins; real-world use of the shipped plugin may reorder this. -### v0.2.0 — Repeating to-dos (visualisation) + configurable strip horizon +### v0.2.0 — Repeating to-dos (visualisation) — SHIPPED · strip horizon — NOT YET BUILT Recurrence itself is delegated to an existing community plugin (open-environment approach; we don't reimplement it). Candidates evaluated: BeatLink's original *Repeating To-Dos* (unmaintained), the *[Revived]* fork (maintained, GitHub issues only), and *Repeating To-dos v2* by TheScriptingGuy (`com.github.TheScriptingGuy.joplin-plugin-repeating-todos`, best-documented). All share the same mechanic: on completion the to-do's alarm date is reset to the next occurrence and the to-do is unmarked — so at any moment a recurring to-do is a **single** to-do at its next due date, with the recurrence rule stored in the note body. diff --git a/package-lock.json b/package-lock.json index 7577ab6..5a0dd3c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "joplin-plugin-gtd-calendar", - "version": "0.4.0", + "version": "0.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "joplin-plugin-gtd-calendar", - "version": "0.4.0", + "version": "0.5.0", "license": "MIT", "dependencies": { "date-fns": "^2.29.3",