From afa2f604506d42876c95814d45e1cf7bc20d7416 Mon Sep 17 00:00:00 2001 From: Mustache Games Date: Mon, 17 Nov 2025 20:55:11 +0200 Subject: [PATCH 1/2] docs(plugin-dev): document simple counters API - Add "Simple Counters" subsection with basic/full model tables, validation/examples (regex, dates) - Integrate counter demo in Quick Start (`plugin.js`: set/inc on button, snack update) - Tie-in Hooks ACTION example: increment counter on task add - List community plugins in Resources (`counter-tester-plugin`, `sp-reporter`) - Clean Data Operations formatting --- docs/plugin-development.md | 74 +++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/docs/plugin-development.md b/docs/plugin-development.md index 4969834c5..be05c2929 100644 --- a/docs/plugin-development.md +++ b/docs/plugin-development.md @@ -57,20 +57,20 @@ my-plugin/ ```javascript console.log('Hello World plugin loaded!'); - // Show a notification PluginAPI.showSnack({ msg: 'Hello from my plugin!', type: 'SUCCESS', }); - -// Register a header button +// Demo a simple counter +await PluginAPI.setCounter('hello-count', 0); PluginAPI.registerHeaderButton({ - label: 'Hello', + label: 'Hello (Count: 0)', icon: 'waving_hand', - onClick: () => { + onClick: async () => { + const newCount = await PluginAPI.incrementCounter('hello-count'); PluginAPI.showSnack({ - msg: 'Button clicked!', + msg: `Button clicked! Count: ${newCount}`, type: 'INFO', }); }, @@ -250,26 +250,70 @@ Plugins that render custom UI in a sandboxed iframe. ## Available API Methods ### Data Operations - #### Tasks - - `getTasks()` - Get all active tasks - `getArchivedTasks()` - Get archived tasks - `getCurrentContextTasks()` - Get tasks in current context - `addTask(task)` - Create a new task - `updateTask(taskId, updates)` - Update existing task - #### Projects - - `getAllProjects()` - Get all projects - `addProject(project)` - Create new project - `updateProject(projectId, updates)` - Update project - #### Tags - - `getAllTags()` - Get all tags - `addTag(tag)` - Create new tag - `updateTag(tagId, updates)` - Update tag +#### Simple Counters +Simple counters let you track lightweight metrics (e.g., daily clicks or habits) that persist and sync with your data. There are two levels: **basic** (key-value pairs for today's count) and **full model** (full CRUD on `SimpleCounter` entities with date-specific values). + +##### Basic Counters +These treat counters as a simple `{ [id: string]: number }` map for today's values (auto-upserts via NgRx). + +| Method | Description | Example | +|--------|-------------|---------| +| `getAllCounters()` | Get all counters as `{ [id: string]: number }` | `const counters = await PluginAPI.getAllCounters(); console.log(counters['my-key']);` | +| `getCounter(id)` | Get today's value for a counter (returns `null` if unset) | `const val = await PluginAPI.getCounter('daily-commits');` | +| `setCounter(id, value)` | Set today's value (non-negative number; validates id regex `/^[A-Za-z0-9_-]+$/`) | `await PluginAPI.setCounter('daily-commits', 5);` | +| `incrementCounter(id, incrementBy = 1)` | Increment and return new value (floors at 0) | `const newVal = await PluginAPI.incrementCounter('daily-commits', 2);` | +| `decrementCounter(id, decrementBy = 1)` | Decrement and return new value (floors at 0) | `const newVal = await PluginAPI.decrementCounter('daily-commits');` | +| `deleteCounter(id)` | Delete the counter | `await PluginAPI.deleteCounter('daily-commits');` | + +**Example:** +```javascript +// Track daily commits +let commits = await PluginAPI.getCounter('daily-commits') ?? 0; +await PluginAPI.incrementCounter('daily-commits'); +PluginAPI.showSnack({ msg: `Commits today: ${await PluginAPI.getCounter('daily-commits')}`, type: 'INFO' }); +``` + +##### Full SimpleCounter Model +For advanced use: Full CRUD on counters with metadata (title, enabled state, date-specific values via `countOnDay: { [date: string]: number }`). + +| Method | Description | Example | +|--------|-------------|---------| +| `getAllSimpleCounters()` | Get all as `SimpleCounter[]` | `const all = await PluginAPI.getAllSimpleCounters();` | +| `getSimpleCounter(id)` | Get one by id (returns `undefined` if not found) | `const counter = await PluginAPI.getSimpleCounter('my-id');` | +| `updateSimpleCounter(id, updates)` | Partial update (e.g., `{ title: 'New Title', countOnDay: { '2025-11-17': 10 } }`) | `await PluginAPI.updateSimpleCounter('my-id', { isEnabled: false });` | +| `toggleSimpleCounter(id)` | Toggle `isOn` state (throws if not found) | `await PluginAPI.toggleSimpleCounter('my-id');` | +| `setSimpleCounterEnabled(id, isEnabled)` | Set enabled state | `await PluginAPI.setSimpleCounterEnabled('my-id', true);` | +| `deleteSimpleCounter(id)` | Delete by id | `await PluginAPI.deleteSimpleCounter('my-id');` | +| `setSimpleCounterToday(id, value)` | Set today's value (YYYY-MM-DD) | `await PluginAPI.setSimpleCounterToday('my-id', 10);` | +| `setSimpleCounterDate(id, date, value)` | Set value for specific date (validates YYYY-MM-DD) | `await PluginAPI.setSimpleCounterDate('my-id', '2025-11-16', 5);` | + +**Example:** +```javascript +// Create/update a habit counter +await PluginAPI.updateSimpleCounter('habit-streak', { + title: 'Daily Streak', + type: 'ClickCounter', + isEnabled: true, + countOnDay: { '2025-11-17': 1 } // Today's count +}); +await PluginAPI.toggleSimpleCounter('habit-streak'); +const counter = await PluginAPI.getSimpleCounter('habit-streak'); +console.log(`Streak on: ${counter.isOn}`); +``` ### UI Operations @@ -380,6 +424,8 @@ PluginAPI.registerHook(PluginAPI.Hooks.TASK_COMPLETE, (taskId) => { PluginAPI.registerHook(PluginAPI.Hooks.ACTION, (action) => { if (action.type === 'ADD_TASK_SUCCESS') { console.log('New task added:', action.payload); + // Bonus: Increment a counter on task add + PluginAPI.incrementCounter('tasks-added-today'); } }); ``` @@ -523,7 +569,9 @@ async function testAPI() { - **Plugin API Types**: [@super-productivity/plugin-api](https://www.npmjs.com/package/@super-productivity/plugin-api) - **Plugin Boilerplate**: [boilerplate-solid-js](../packages/plugin-dev/boilerplate-solid-js) - **Example Plugins**: [plugin-dev](../packages/plugin-dev) -- **Community Plugins**: Coming Soon! +- **Community Plugins**: + - [counter-tester-plugin](https://github.com/Mustache-Games/counter-tester-plugin) by [Mustache Dev](https://github.com/Mustache-Games) + - [sp-reporter](https://github.com/dougcooper/sp-reporter) by [dougcooper](https://github.com/dougcooper) ## Contributing From 273a1077d74b2862f07ef9be6da3c0dc124e4c7e Mon Sep 17 00:00:00 2001 From: Mustache Games Date: Mon, 17 Nov 2025 21:08:02 +0200 Subject: [PATCH 2/2] docs(plugin-dev): improve whitespaces - Blank lines in Quick Start JS (after log/notification/counter) - Spacing in Data Operations subsections (Tasks/Projects/Tags/Simple Counters) --- docs/plugin-development.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/plugin-development.md b/docs/plugin-development.md index be05c2929..1c6eb7d99 100644 --- a/docs/plugin-development.md +++ b/docs/plugin-development.md @@ -57,11 +57,13 @@ my-plugin/ ```javascript console.log('Hello World plugin loaded!'); + // Show a notification PluginAPI.showSnack({ msg: 'Hello from my plugin!', type: 'SUCCESS', }); + // Demo a simple counter await PluginAPI.setCounter('hello-count', 0); PluginAPI.registerHeaderButton({ @@ -250,20 +252,27 @@ Plugins that render custom UI in a sandboxed iframe. ## Available API Methods ### Data Operations + #### Tasks + - `getTasks()` - Get all active tasks - `getArchivedTasks()` - Get archived tasks - `getCurrentContextTasks()` - Get tasks in current context - `addTask(task)` - Create a new task - `updateTask(taskId, updates)` - Update existing task + #### Projects + - `getAllProjects()` - Get all projects - `addProject(project)` - Create new project - `updateProject(projectId, updates)` - Update project + #### Tags + - `getAllTags()` - Get all tags - `addTag(tag)` - Create new tag - `updateTag(tagId, updates)` - Update tag + #### Simple Counters Simple counters let you track lightweight metrics (e.g., daily clicks or habits) that persist and sync with your data. There are two levels: **basic** (key-value pairs for today's count) and **full model** (full CRUD on `SimpleCounter` entities with date-specific values).