From 735706ead47cfc4dca550e06259ff4ce61d4b521 Mon Sep 17 00:00:00 2001 From: callumalpass Date: Sat, 21 Feb 2026 13:41:32 +1100 Subject: [PATCH] docs: tighten accuracy, fix cross-links, and consolidate unreleased notes --- PRIVACY.md | 79 +- README.md | 2 +- docs/HTTP_API.md | 1342 ++++++--------------- docs/calendar-setup.md | 17 +- docs/core-concepts.md | 5 +- docs/features.md | 6 +- docs/features/calendar-integration.md | 13 +- docs/features/ics-integration.md | 11 +- docs/features/inline-tasks.md | 16 +- docs/features/integrations.md | 6 +- docs/features/reminders.md | 4 +- docs/features/task-management.md | 2 +- docs/features/time-management.md | 4 +- docs/nlp-api.md | 251 ++-- docs/privacy.md | 79 +- docs/releases/unreleased.md | 2 + docs/settings.md | 4 +- docs/settings/advanced-settings.md | 181 ++- docs/settings/calendar-settings.md | 61 +- docs/settings/misc-settings.md | 46 +- docs/settings/property-types-reference.md | 2 +- docs/settings/task-defaults.md | 4 +- docs/troubleshooting.md | 8 +- docs/views.md | 2 +- docs/views/notes-view.md | 65 +- docs/views/task-list.md | 8 +- docs/webhooks.md | 988 +++------------ mkdocs.yml | 4 +- 28 files changed, 930 insertions(+), 2282 deletions(-) diff --git a/PRIVACY.md b/PRIVACY.md index be7615f4..d8d85991 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -1,56 +1,67 @@ # TaskNotes Privacy Policy -Last updated: February 8, 2026 +Last updated: February 21, 2026 ## Overview -TaskNotes is an Obsidian plugin that helps you manage tasks within your notes. +TaskNotes is an Obsidian plugin. It stores task data in your local vault files. ## Data Collection and Usage -TaskNotes does not collect, transmit, or share any of your data. +TaskNotes does not include telemetry or analytics collection. -## Data Storage +## Local Storage -Task and note content stays in your local Obsidian vault, and plugin settings are stored in Obsidian’s local configuration. By default, TaskNotes does not send vault data to external services. +Task and note content stays in your local Obsidian vault. +Plugin settings are stored in Obsidian's local plugin configuration. + +## Optional Network Features + +TaskNotes is local-first. Network requests occur only when you enable features that require them. + +Optional network features: + +- OAuth calendar integration (Google/Microsoft): fetches and updates calendar events through provider APIs. +- ICS subscriptions: fetches events from configured ICS URLs. +- Webhooks: sends event payloads to webhook endpoints you configure. +- API docs UI (`/api/docs/ui`): loads Swagger UI assets from `unpkg.com` in your browser. + +## OAuth Credentials and Tokens + +- OAuth client credentials are configured by you in TaskNotes settings. +- Access and refresh tokens are stored locally on your device. +- You can disconnect providers at any time to revoke TaskNotes access. + +## Third-Party Services (When Enabled) + +- Google APIs: https://policies.google.com/privacy +- Microsoft APIs: https://privacy.microsoft.com/privacystatement +- Any ICS host or webhook endpoint you configure + +## What TaskNotes Does Not Do + +- No TaskNotes-hosted cloud sync service +- No remote storage of your vault content by TaskNotes +- No telemetry pipeline sending usage metrics ## Data Deletion -You can stop TaskNotes processing by disabling the plugin, and remove plugin configuration by uninstalling it. Your notes remain in your vault under your control. +You can stop TaskNotes processing by disabling the plugin. +You can remove plugin settings by uninstalling the plugin. +Your notes remain in your vault. -## Network Requests (Optional Features) +## Changes to This Policy -TaskNotes operates locally by default, but includes optional features that make network requests when you enable them: - -**Optional network features:** - -OAuth calendar integration (Google or Microsoft) uses OAuth 2.0 (with PKCE) to authenticate and stores access/refresh tokens locally on your device. If enabled, TaskNotes fetches calendar events and can update external events when you choose sync actions. - -ICS subscriptions fetch event data from URLs you configure. Webhooks send task event payloads to endpoints you configure. License validation sends your license key to Lemon Squeezy for validity checks, caches results locally for 24 hours, and applies a 7-day grace period when validation is temporarily unavailable. - -**OAuth Credentials:** - -TaskNotes includes bundled OAuth client credentials for quick setup. These app credentials are public identifiers and do not expose your account. Authentication and calendar access are controlled by your user tokens. You can also use your own OAuth credentials in advanced setup. - -**Third-Party Services:** - -- **Lemon Squeezy**: License validation only (https://www.lemonsqueezy.com/privacy) -- **Google**: OAuth authentication and Calendar API access (https://policies.google.com/privacy) -- **Microsoft**: OAuth authentication and Calendar API access (https://privacy.microsoft.com/privacystatement) - -**What we never do:** -TaskNotes does not run analytics/telemetry collection, does not read your notes remotely, and does not store your calendar data on TaskNotes servers. Calendar requests go directly between your device and the provider APIs you connect. - -## Changes to Privacy Policy - -We may update this policy. Changes will be posted in this file with an updated date. +This policy may be updated. Changes are published in this file with a new date. ## Contact -For questions or concerns about privacy, please open an issue on GitHub: +For privacy questions, open an issue: https://github.com/callumalpass/tasknotes/issues -## Open Source +## Open Source Verification -TaskNotes is open source software. You can review the code at https://github.com/callumalpass/tasknotes to verify these privacy practices. +TaskNotes is open source. You can inspect the implementation: + +https://github.com/callumalpass/tasknotes diff --git a/README.md b/README.md index 575482be..9ca96759 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Open a view with commands like **TaskNotes: Open tasks view** or **TaskNotes: Op ## How it works with Bases -TaskNotes registers as a Bases data source and provides custom view types: `tasknotesTaskList`, `tasknotesKanban`, `tasknotesCalendar`, `tasknotesAgenda`, and `tasknotesMinicalendar`. Your task notes become rows; frontmatter properties become columns. +TaskNotes registers as a Bases data source and provides custom view types: `tasknotesTaskList`, `tasknotesKanban`, `tasknotesCalendar`, and `tasknotesMiniCalendar`. The default Agenda file is a preconfigured `tasknotesCalendar` list view (`listWeek`). Your task notes become rows; frontmatter properties become columns. The default `.base` files include formula properties for computed values: diff --git a/docs/HTTP_API.md b/docs/HTTP_API.md index adb74227..2cf1951a 100644 --- a/docs/HTTP_API.md +++ b/docs/HTTP_API.md @@ -1,1078 +1,482 @@ # TaskNotes HTTP API -The TaskNotes HTTP API allows external applications to interact with your TaskNotes data. This enables powerful integrations with browsers, automation tools, mobile apps, and custom scripts. -The API is local-first and designed for automation against files in your vault. +The TaskNotes HTTP API provides local HTTP access to tasks, time tracking, pomodoro, calendars, webhooks, and NLP parsing. -## Quick Start +## Availability -1. **Enable API**: Go to TaskNotes Settings → Integrations → HTTP API (desktop only) -2. **Configure**: Set port (default 8080) and optional auth token -3. **Restart**: Restart Obsidian to apply API enable/port changes and start the server -4. **Test**: `curl http://localhost:8080/api/health` -5. **Explore**: Visit `http://localhost:8080/api/docs/ui` for interactive documentation -After setup, validate `/api/health` first, then test one read endpoint and one write endpoint. +- Desktop only +- Disabled by default +- Started when Obsidian starts and TaskNotes API is enabled +- Not available on mobile -## Interactive Documentation +Enable it in `Settings -> TaskNotes -> Integrations -> HTTP API`. -TaskNotes provides comprehensive API documentation through Swagger UI: +## Base URL -- **OpenAPI Specification**: `GET /api/docs` - Machine-readable API spec in OpenAPI 3.0 format -- **Interactive Docs**: `GET /api/docs/ui` - Swagger UI for exploring and testing endpoints +`http://localhost:{PORT}` -The interactive documentation includes: -- Complete endpoint documentation with examples -- Request/response schemas -- Try-it-out functionality for testing endpoints -- Authentication setup for protected endpoints +Default port is `8080`. ## Authentication -### Optional Bearer Token +Authentication is optional. + +- If `apiAuthToken` is empty, all API requests are accepted. +- If `apiAuthToken` is set, send `Authorization: Bearer `. + +Example: + ```bash -# Set token in settings, then use in requests: -curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8080/api/tasks +curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8080/api/health ``` -### No Authentication -If no token is configured, all API requests are allowed without authentication. - -### Security Warning -The HTTP API is intended for local use. Any client that can reach your configured API port can send requests if authentication is disabled. Always set an authentication token unless you are in a fully trusted environment. -When exposing the port beyond localhost, token authentication should be treated as required. - -## Base URL -``` -http://localhost:{PORT}/api -``` -Default port is 8080 (configurable in settings). - ## Response Format -All endpoints return JSON in this format: +Success: + ```json { "success": true, - "data": { /* response data */ }, - "message": "optional success message" + "data": {} } ``` -Error responses: +Error: + ```json { "success": false, - "error": "Error description" + "error": "Error message" } ``` -## Endpoints -Endpoints are grouped by resource and follow the same response envelope. Read operations return current file-derived state; write operations update vault files. +## Endpoint Index -### Health Check -``` -GET /api/health -``` +### System -**Response:** -```json -{ - "success": true, - "data": { - "status": "ok", - "timestamp": "2025-08-12T10:30:00.000Z" - } -} -``` +- `GET /api/health` +- `GET /api/docs` +- `GET /api/docs/ui` +- `POST /api/nlp/parse` +- `POST /api/nlp/create` ### Tasks -#### List Tasks -``` -GET /api/tasks -``` - -**Query Parameters:** - -- `status` - Filter by status (e.g., "open", "completed") -- `priority` - Filter by priority (e.g., "High", "Normal") -- `project` - Filter by project name (partial match) -- `tag` - Filter by tag (partial match) -- `overdue` - "true" for overdue tasks only -- `completed` - "true" or "false" -- `archived` - "true" or "false" -- `due_before` - ISO date (e.g., "2025-08-15") -- `due_after` - ISO date -- `sort` - Field to sort by (e.g., "due:asc", "priority:desc") -- `limit` - Max number of results -- `offset` - Skip this many results - -**Examples:** -```bash -# All active tasks -curl "http://localhost:8080/api/tasks?completed=false&archived=false" - -# High priority overdue tasks -curl "http://localhost:8080/api/tasks?priority=High&overdue=true" - -# Tasks due this week, sorted by due date -curl "http://localhost:8080/api/tasks?due_before=2025-08-19&sort=due:asc" -``` - -**Response:** - -```json -{ - "success": true, - "data": { - "tasks": [ - { - "path": "TaskNotes/Tasks/sample-task.md", - "title": "Review quarterly budget", - "status": "open", - "priority": "High", - "due": "2025-08-15", - "scheduled": "2025-08-14", - "tags": ["work", "finance"], - "projects": ["[[Q3 Planning]]"], - "contexts": ["@office"], - "dateCreated": "2025-08-10T09:00:00.000Z", - "dateModified": "2025-08-10T09:00:00.000Z" - } - ], - "total": 150, - "filtered": 1 - } -} -``` - -#### Create Task - -``` -POST /api/tasks -``` - -**Request Body:** - -```json -{ - "title": "New task title", - "priority": "High", - "status": "open", - "due": "2025-08-15", - "scheduled": "2025-08-14", - "tags": ["email", "urgent"], - "projects": ["[[Work Project]]"], - "contexts": ["@computer"], - "details": "Additional task description", - "timeEstimate": 60 -} -``` - -**Required Fields:** - -- `title` - Task title (max 200 characters) - -**Optional Fields:** - -- `priority` - Task priority -- `status` - Task status -- `due` - Due date (ISO format) -- `scheduled` - Scheduled date (ISO format) -- `tags` - Array of tag strings -- `projects` - Array of project links -- `contexts` - Array of context strings -- `details` - Task description/details -- `timeEstimate` - Estimated time in minutes - -#### Get Single Task - -``` -GET /api/tasks/{id} -``` - -Where `{id}` is the task file path (URL-encoded). - -#### Update Task - -``` -PUT /api/tasks/{id} -``` - -**Request Body:** Same format as create task, with partial updates supported. - -#### Delete Task - -``` -DELETE /api/tasks/{id} -``` +- `GET /api/tasks` +- `POST /api/tasks` +- `GET /api/tasks/:id` +- `PUT /api/tasks/:id` +- `DELETE /api/tasks/:id` +- `POST /api/tasks/:id/toggle-status` +- `POST /api/tasks/:id/archive` +- `POST /api/tasks/:id/complete-instance` +- `POST /api/tasks/query` +- `GET /api/filter-options` +- `GET /api/stats` ### Time Tracking -Control and query time tracking data for tasks with comprehensive analytics and reporting capabilities. - -#### Start Time Tracking - -``` -POST /api/tasks/{id}/time/start -``` - -Start time tracking for a specific task. - -**Response:** -```json -{ - "success": true, - "data": { - "id": "path/to/task.md", - "title": "Work on API integration", - "status": "in-progress", - "timeEntries": [ - { - "startTime": "2025-08-14T10:00:00.000Z", - "description": null - } - ] - } -} -``` - -#### Start Time Tracking with Description - -``` -POST /api/tasks/{id}/time/start-with-description -``` - -Start time tracking with an optional description of the work being done. - -**Request Body:** -```json -{ - "description": "Working on API endpoint implementation" -} -``` - -**Response:** -```json -{ - "success": true, - "data": { - "task": { - "id": "path/to/task.md", - "title": "Work on API integration" - }, - "message": "Time tracking started with description: Working on API endpoint implementation" - } -} -``` - -#### Stop Time Tracking - -``` -POST /api/tasks/{id}/time/stop -``` - -Stop the currently active time tracking session for a task. - -**Response:** -```json -{ - "success": true, - "data": { - "id": "path/to/task.md", - "title": "Work on API integration", - "timeEntries": [ - { - "startTime": "2025-08-14T10:00:00.000Z", - "endTime": "2025-08-14T11:30:00.000Z", - "description": "Working on API endpoint implementation" - } - ] - } -} -``` - -#### Get Task Time Data - -``` -GET /api/tasks/{id}/time -``` - -Get comprehensive time tracking data for a specific task. - -**Response:** -```json -{ - "success": true, - "data": { - "task": { - "id": "path/to/task.md", - "title": "Work on API integration", - "status": "in-progress", - "priority": "high" - }, - "summary": { - "totalMinutes": 180, - "totalHours": 3.0, - "totalSessions": 3, - "completedSessions": 2, - "activeSessions": 1, - "averageSessionMinutes": 60 - }, - "activeSession": { - "startTime": "2025-08-14T14:00:00.000Z", - "description": "Final testing phase", - "elapsedMinutes": 15 - }, - "timeEntries": [ - { - "startTime": "2025-08-14T10:00:00.000Z", - "endTime": "2025-08-14T11:30:00.000Z", - "description": "Initial implementation", - "duration": 90, - "isActive": false - }, - { - "startTime": "2025-08-14T13:00:00.000Z", - "endTime": "2025-08-14T13:45:00.000Z", - "description": "Code review and fixes", - "duration": 45, - "isActive": false - }, - { - "startTime": "2025-08-14T14:00:00.000Z", - "endTime": null, - "description": "Final testing phase", - "duration": 15, - "isActive": true - } - ] - } -} -``` - -#### Get Active Time Sessions - -``` -GET /api/time/active -``` - -Get all currently active time tracking sessions across all tasks. - -**Response:** -```json -{ - "success": true, - "data": { - "activeSessions": [ - { - "task": { - "id": "path/to/task1.md", - "title": "API Integration", - "status": "in-progress", - "priority": "high", - "tags": ["development", "api"], - "projects": ["[[Project Alpha]]"] - }, - "session": { - "startTime": "2025-08-14T14:00:00.000Z", - "description": "Final testing phase", - "elapsedMinutes": 25 - }, - "elapsedMinutes": 25 - }, - { - "task": { - "id": "path/to/task2.md", - "title": "Documentation Update", - "status": "open", - "priority": "normal", - "tags": ["documentation"], - "projects": ["[[Project Beta]]"] - }, - "session": { - "startTime": "2025-08-14T13:45:00.000Z", - "description": "Writing API examples", - "elapsedMinutes": 40 - }, - "elapsedMinutes": 40 - } - ], - "totalActiveSessions": 2, - "totalElapsedMinutes": 65 - } -} -``` - -#### Get Time Summary - -``` -GET /api/time/summary -``` - -Get time tracking statistics and summaries with flexible date filtering. - -**Query Parameters:** -- `period` - Time period: `today`, `week`, `month`, `all` (default: `today`) -- `from` - Start date for custom period (ISO format: `2025-08-01`) -- `to` - End date for custom period (ISO format: `2025-08-15`) - -**Examples:** -```bash -# Today's time summary -curl "http://localhost:8080/api/time/summary" - -# This week's summary -curl "http://localhost:8080/api/time/summary?period=week" - -# Custom date range -curl "http://localhost:8080/api/time/summary?from=2025-08-01&to=2025-08-15" -``` - -**Response:** -```json -{ - "success": true, - "data": { - "period": "today", - "dateRange": { - "from": "2025-08-14T00:00:00.000Z", - "to": "2025-08-14T23:59:59.999Z" - }, - "summary": { - "totalMinutes": 320, - "totalHours": 5.33, - "tasksWithTime": 8, - "activeTasks": 2, - "completedTasks": 3 - }, - "topTasks": [ - { - "task": "projects/api-integration.md", - "title": "API Integration", - "minutes": 120 - }, - { - "task": "projects/documentation.md", - "title": "Documentation Update", - "minutes": 95 - } - ], - "topProjects": [ - { - "project": "[[Project Alpha]]", - "minutes": 180 - }, - { - "project": "[[Project Beta]]", - "minutes": 140 - } - ], - "topTags": [ - { - "tag": "development", - "minutes": 200 - }, - { - "tag": "documentation", - "minutes": 120 - } - ] - } -} -``` - -### Task Actions - -#### Toggle Status - -``` -POST /api/tasks/{id}/toggle-status -``` - -Toggles between open/completed status. - -#### Toggle Archive - -``` -POST /api/tasks/{id}/archive -``` - -Archives or unarchives the task. - -#### Complete Recurring Instance - -``` -POST /api/tasks/{id}/complete-instance -``` - - -**Request Body:** - -```json -{ - "date": "2025-08-12" -} -``` - -### Advanced Queries - -#### Query Tasks - -``` -POST /api/tasks/query -``` - - -**Request Body:** Advanced FilterQuery object (see TaskNotes FilterQuery documentation). - -#### Get Filter Options - -``` -GET /api/filter-options -``` - -Returns available tags, projects, statuses, and priorities for building filter UIs. - -### Statistics - -#### Get Task Statistics - -``` -GET /api/stats -``` - -**Response:** - -```json -{ - "success": true, - "data": { - "total": 245, - "completed": 189, - "active": 45, - "overdue": 8, - "archived": 11, - "withTimeTracking": 67 - } -} -``` - -#### Get Aggregated Time Estimates - -``` -GET /api/time-stats -``` - -Aggregates the `timeEstimate` for tasks within a given date range. The range can be a predefined period or a custom start/end date. - -**Query Parameters:** - -- `range` - A predefined range. Can be one of `daily`, `weekly`, `monthly`, `yearly`. -- `start` - A start date for a custom range, in `YYYY-MM-DD` format. Must be used with `end`. -- `end` - An end date for a custom range, in `YYYY-MM-DD` format. Must be used with `start`. - -**Examples:** -```bash -# Get total estimated time for tasks this week -curl "http://localhost:8080/api/time-stats?range=weekly" - -# Get total estimated time for a custom range -curl "http://localhost:8080/api/time-stats?start=2025-01-01&end=2025-01-31" -``` - -**Response:** - -```json -{ - "success": true, - "data": { - "totalMinutes": 750 - } -} -``` +- `POST /api/tasks/:id/time/start` +- `POST /api/tasks/:id/time/start-with-description` +- `POST /api/tasks/:id/time/stop` +- `GET /api/tasks/:id/time` +- `GET /api/time/active` +- `GET /api/time/summary` ### Pomodoro -Control pomodoro sessions programmatically through the API. +- `POST /api/pomodoro/start` +- `POST /api/pomodoro/stop` +- `POST /api/pomodoro/pause` +- `POST /api/pomodoro/resume` +- `GET /api/pomodoro/status` +- `GET /api/pomodoro/sessions` +- `GET /api/pomodoro/stats` -#### Start Pomodoro Session +### Calendars -``` -POST /api/pomodoro/start -``` +- `GET /api/calendars` +- `GET /api/calendars/google` +- `GET /api/calendars/microsoft` +- `GET /api/calendars/subscriptions` +- `GET /api/calendars/events` -**Request Body (Optional):** +### Webhooks -```json -{ - "taskId": "path/to/task.md" -} -``` +- `POST /api/webhooks` +- `GET /api/webhooks` +- `DELETE /api/webhooks/:id` +- `GET /api/webhooks/deliveries` -**Response:** +See `docs/webhooks.md` for event and transform details. -```json -{ - "success": true, - "data": { - "session": { - "id": "pomo_123", - "type": "work", - "duration": 1500, - "startTime": "2025-08-13T10:00:00.000Z" - }, - "task": { - "id": "path/to/task.md", - "title": "Work on API integration" - }, - "message": "Pomodoro session started" - } -} -``` +## Route Details -#### Stop Pomodoro Session +## Health -``` -POST /api/pomodoro/stop -``` +### `GET /api/health` -#### Pause Pomodoro Session - -``` -POST /api/pomodoro/pause -``` - -#### Resume Pomodoro Session - -``` -POST /api/pomodoro/resume -``` - -#### Get Pomodoro Status - -``` -GET /api/pomodoro/status -``` - -**Response:** - -```json -{ - "success": true, - "data": { - "isRunning": true, - "timeRemaining": 900, - "currentSession": { - "id": "pomo_123", - "type": "work", - "duration": 1500, - "startTime": "2025-08-13T10:00:00.000Z" - }, - "totalPomodoros": 42, - "currentStreak": 3, - "totalMinutesToday": 180 - } -} -``` - -#### Get Pomodoro Session History - -``` -GET /api/pomodoro/sessions -``` - -**Query Parameters:** - -- `limit` - Maximum number of sessions to return -- `date` - Filter sessions by date (YYYY-MM-DD) - -**Examples:** +Returns service state plus vault metadata. ```bash -# Get last 10 sessions -curl "http://localhost:8080/api/pomodoro/sessions?limit=10" - -# Get sessions for specific date -curl "http://localhost:8080/api/pomodoro/sessions?date=2025-08-13" +curl http://localhost:8080/api/health ``` -#### Get Pomodoro Statistics +## Tasks -``` -GET /api/pomodoro/stats -``` +### `GET /api/tasks` -**Query Parameters:** +Basic task listing with pagination only. -- `date` - Get stats for specific date (YYYY-MM-DD), defaults to today +Query params: -**Response:** +- `limit` (default `50`, max `200`) +- `offset` (default `0`) -```json -{ - "success": true, - "data": { - "totalSessions": 15, - "completedSessions": 12, - "interruptedSessions": 3, - "totalFocusTime": 300, - "workSessions": 10, - "breakSessions": 5, - "longestStreak": 8, - "averageSessionLength": 24.5 - } -} -``` +Important: -## Integration Examples -Examples below show typical patterns for browser extensions, local scripts, and service bridges. +- Filtering params such as `status`, `priority`, `tag`, `project`, `due_before`, `due_after`, `overdue`, `completed`, `archived`, and `sort` are rejected on this endpoint with HTTP `400`. +- Use `POST /api/tasks/query` for filtering. -### Browser Bookmarklet - -```javascript -javascript:(function(){ - const title = document.title; - const url = window.location.href; - - fetch('http://localhost:8080/api/tasks', { - method: 'POST', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify({ - title: `Review: ${title}`, - tags: ['web'], - details: `Source: ${url}` - }) - }).then(r => r.json()).then(d => { - alert(d.success ? 'Task created!' : 'Error: ' + d.error); - }); -})(); -``` - -### Python Script - -```python -import requests - -def create_task(title, **kwargs): - response = requests.post('http://localhost:8080/api/tasks', - json={'title': title, **kwargs}) - return response.json() - -# Create task from command line -task = create_task("Call dentist", priority="High", due="2025-08-15") -print(f"Created task: {task['data']['title']}") -``` - -### Automation (Zapier/IFTTT) +Example: + +```bash +curl "http://localhost:8080/api/tasks?limit=25&offset=0" +``` + +Response fields: + +- `data.tasks` +- `data.pagination` with `total`, `offset`, `limit`, `hasMore` +- `data.vault` +- `data.note` + +### `POST /api/tasks` + +Create one task. + +Required: + +- `title` + +Common optional fields: + +- `details`, `status`, `priority`, `due`, `scheduled` +- `tags`, `contexts`, `projects` +- `recurrence`, `timeEstimate` ```bash -# Webhook URL for automation services curl -X POST http://localhost:8080/api/tasks \ -H "Content-Type: application/json" \ - -d '{"title":"{{trigger.subject}}", "tags":["email"], "details":"{{trigger.body}}"}' + -d '{"title":"Review docs","priority":"high"}' ``` -### Pomodoro Timer Integration +Returns HTTP `201` with created task data. -```javascript -// Simple Pomodoro timer controller -class PomodoroController { - constructor(apiUrl = 'http://localhost:8080') { - this.apiUrl = apiUrl; - } +### `GET /api/tasks/:id` - async startSession(taskId = null) { - const body = taskId ? { taskId } : {}; - const response = await fetch(`${this.apiUrl}/api/pomodoro/start`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body) - }); - return response.json(); - } +Get one task by path id. - async getStatus() { - const response = await fetch(`${this.apiUrl}/api/pomodoro/status`); - return response.json(); - } +- `:id` must be URL-encoded task path. - async pause() { - const response = await fetch(`${this.apiUrl}/api/pomodoro/pause`, { - method: 'POST' - }); - return response.json(); - } - - async resume() { - const response = await fetch(`${this.apiUrl}/api/pomodoro/resume`, { - method: 'POST' - }); - return response.json(); - } - - async stop() { - const response = await fetch(`${this.apiUrl}/api/pomodoro/stop`, { - method: 'POST' - }); - return response.json(); - } -} - -// Usage -const pomodoro = new PomodoroController(); - -// Start a session for a specific task -await pomodoro.startSession('Projects/MyProject.md'); - -// Check current status -const status = await pomodoro.getStatus(); -console.log(`Time remaining: ${Math.floor(status.data.timeRemaining / 60)} minutes`); +```bash +curl "http://localhost:8080/api/tasks/TaskNotes%2FTasks%2FReview%20docs.md" ``` -### Time Tracking Integration +### `PUT /api/tasks/:id` -```javascript -// Comprehensive time tracking controller -class TimeTracker { - constructor(apiUrl = 'http://localhost:8080') { - this.apiUrl = apiUrl; - } +Update task with partial payload. - // Start time tracking with description - async startTracking(taskId, description = null) { - const endpoint = description - ? `/api/tasks/${encodeURIComponent(taskId)}/time/start-with-description` - : `/api/tasks/${encodeURIComponent(taskId)}/time/start`; - - const body = description ? { description } : {}; - - const response = await fetch(`${this.apiUrl}${endpoint}`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body) - }); - return response.json(); - } +```bash +curl -X PUT "http://localhost:8080/api/tasks/TaskNotes%2FTasks%2FReview%20docs.md" \ + -H "Content-Type: application/json" \ + -d '{"status":"in-progress"}' +``` - // Stop time tracking - async stopTracking(taskId) { - const response = await fetch(`${this.apiUrl}/api/tasks/${encodeURIComponent(taskId)}/time/stop`, { - method: 'POST' - }); - return response.json(); - } +### `DELETE /api/tasks/:id` - // Get active sessions - async getActiveSessions() { - const response = await fetch(`${this.apiUrl}/api/time/active`); - return response.json(); - } +Delete task file. - // Get task time data - async getTaskTimeData(taskId) { - const response = await fetch(`${this.apiUrl}/api/tasks/${encodeURIComponent(taskId)}/time`); - return response.json(); - } +### `POST /api/tasks/:id/toggle-status` - // Get time summary - async getTimeSummary(period = 'today', fromDate = null, toDate = null) { - let url = `${this.apiUrl}/api/time/summary?period=${period}`; - if (fromDate) url += `&from=${fromDate}`; - if (toDate) url += `&to=${toDate}`; - - const response = await fetch(url); - return response.json(); - } +Toggle task status via configured workflow. - // Get daily dashboard data - async getDashboard() { - const [activeSessions, todaySummary, weekSummary] = await Promise.all([ - this.getActiveSessions(), - this.getTimeSummary('today'), - this.getTimeSummary('week') - ]); +### `POST /api/tasks/:id/archive` - return { - active: activeSessions.data, - today: todaySummary.data, - week: weekSummary.data - }; - } +Toggle archive state. - // Toggle time tracking for a task - async toggleTracking(taskId, description = null) { - const activeSessions = await this.getActiveSessions(); - const isCurrentlyTracking = activeSessions.data.activeSessions.some( - session => session.task.id === taskId - ); +### `POST /api/tasks/:id/complete-instance` - if (isCurrentlyTracking) { - return await this.stopTracking(taskId); - } else { - return await this.startTracking(taskId, description); +Complete recurring instance. + +Request body: + +- Optional `date` (ISO string). If omitted, uses current date context. + +### `POST /api/tasks/query` + +Advanced filtering. + +Request body is a `FilterQuery` object. Root object is a group with: + +- `type: "group"` +- `id` +- `conjunction: "and" | "or"` +- `children` (conditions or groups) + +Optional top-level query options: + +- `sortKey`, `sortDirection` +- `groupKey`, `subgroupKey` + +Example: + +```json +{ + "type": "group", + "id": "root", + "conjunction": "and", + "children": [ + { + "type": "condition", + "id": "c1", + "property": "status", + "operator": "is", + "value": "open" } - } + ], + "sortKey": "due", + "sortDirection": "asc" } - -// Usage examples -const tracker = new TimeTracker(); - -// Start tracking with description -await tracker.startTracking('projects/api-work.md', 'Implementing time tracking endpoints'); - -// Get active sessions -const active = await tracker.getActiveSessions(); -console.log(`Currently tracking ${active.data.totalActiveSessions} tasks`); - -// Get today's summary -const today = await tracker.getTimeSummary('today'); -console.log(`Today: ${today.data.summary.totalHours} hours across ${today.data.summary.tasksWithTime} tasks`); - -// Get weekly breakdown -const week = await tracker.getTimeSummary('week'); -console.log('Top projects this week:'); -week.data.topProjects.forEach(project => { - console.log(`- ${project.project}: ${Math.round(project.minutes / 60 * 100) / 100} hours`); -}); - -// Toggle tracking (start if stopped, stop if running) -await tracker.toggleTracking('projects/documentation.md', 'Writing API examples'); - -// Get comprehensive dashboard -const dashboard = await tracker.getDashboard(); -console.log('Time Tracking Dashboard:', { - activeNow: dashboard.active.totalActiveSessions, - todayHours: dashboard.today.summary.totalHours, - weekHours: dashboard.week.summary.totalHours, - topTaskToday: dashboard.today.topTasks[0]?.title || 'None' -}); ``` -### Time Analytics Dashboard +Response: -```python -import requests -from datetime import datetime, timedelta -import json +- `data.tasks` +- `data.total` +- `data.filtered` +- `data.vault` -class TimeAnalytics: - def __init__(self, api_url='http://localhost:8080'): - self.api_url = api_url - - def get_time_summary(self, period='today', from_date=None, to_date=None): - params = {'period': period} - if from_date: - params['from'] = from_date - if to_date: - params['to'] = to_date - - response = requests.get(f'{self.api_url}/api/time/summary', params=params) - return response.json() - - def generate_weekly_report(self): - """Generate a comprehensive weekly time tracking report""" - week_data = self.get_time_summary('week') - - if not week_data['success']: - return None - - data = week_data['data'] - summary = data['summary'] - - report = { - 'period': f"{data['dateRange']['from'][:10]} to {data['dateRange']['to'][:10]}", - 'total_hours': summary['totalHours'], - 'avg_hours_per_day': round(summary['totalHours'] / 7, 2), - 'tasks_worked_on': summary['tasksWithTime'], - 'productivity_score': min(100, round((summary['totalHours'] / 40) * 100, 1)), - 'top_focus_areas': { - 'projects': data['topProjects'][:3], - 'tags': data['topTags'][:3], - 'tasks': data['topTasks'][:5] - } - } - - return report - - def get_project_breakdown(self, days=30): - """Get time breakdown by project for the last N days""" - end_date = datetime.now().isoformat()[:10] - start_date = (datetime.now() - timedelta(days=days)).isoformat()[:10] - - data = self.get_time_summary('custom', start_date, end_date) - - if data['success']: - return { - 'period_days': days, - 'total_hours': data['data']['summary']['totalHours'], - 'projects': data['data']['topProjects'] - } - return None +### `GET /api/filter-options` -# Usage -analytics = TimeAnalytics() +Returns filter options for UI builders. -# Weekly report -report = analytics.generate_weekly_report() -print(f"Weekly Report ({report['period']}):") -print(f"- Total: {report['total_hours']} hours") -print(f"- Daily average: {report['avg_hours_per_day']} hours") -print(f"- Productivity score: {report['productivity_score']}%") -print(f"- Top project: {report['top_focus_areas']['projects'][0]['project']}") +### `GET /api/stats` -# Project breakdown -projects = analytics.get_project_breakdown(30) -print(f"\nLast 30 days project breakdown:") -for project in projects['projects']: - percentage = round((project['minutes'] / (projects['total_hours'] * 60)) * 100, 1) - print(f"- {project['project']}: {round(project['minutes']/60, 1)}h ({percentage}%)") +Returns summary counts: + +- `total`, `completed`, `active`, `overdue`, `archived`, `withTimeTracking` + +## Time Tracking + +### `POST /api/tasks/:id/time/start` + +Starts a new active time entry for that task. + +### `POST /api/tasks/:id/time/start-with-description` + +Starts time tracking and writes `description` on the new active entry. + +Request body: + +```json +{ + "description": "Implementation" +} ``` -## Error Handling -Errors return `success: false` with an `error` message. For client code, handle non-2xx status codes and response-body errors as separate checks. +### `POST /api/tasks/:id/time/stop` -### Common Errors +Stops active time entry for that task. -- `400 Bad Request` - Invalid request data -- `401 Unauthorized` - Invalid or missing auth token -- `404 Not Found` - Task not found -- `500 Internal Server Error` - Server error +### `GET /api/tasks/:id/time` -### Rate Limiting +Returns per-task time summary and entries. -No rate limiting currently implemented. Use responsibly. +### `GET /api/time/active` -### CORS +Returns currently active sessions across tasks. -CORS is enabled for all origins (`*`). API is intended for localhost use only. +Important: + +- Multiple active sessions can exist across different tasks. + +### `GET /api/time/summary` + +Returns aggregate time summary. + +Query params: + +- `period` (for example `today`, `week`, `month`, `all`) +- `from` (ISO date) +- `to` (ISO date) + +Example: + +```bash +curl "http://localhost:8080/api/time/summary?period=week" +``` + +## Pomodoro + +### `POST /api/pomodoro/start` + +Starts a session. + +Optional request fields: + +- `taskId` (URL path of task) +- `duration` (number) + +### `POST /api/pomodoro/stop` + +Stops and resets current session. + +### `POST /api/pomodoro/pause` + +Pauses running session. + +### `POST /api/pomodoro/resume` + +Resumes paused session. + +### `GET /api/pomodoro/status` + +Returns current state plus computed totals (`totalPomodoros`, `currentStreak`, `totalMinutesToday`). + +### `GET /api/pomodoro/sessions` + +Returns history. + +Query params: + +- `limit` +- `date` (`YYYY-MM-DD`) + +### `GET /api/pomodoro/stats` + +Returns stats for today or provided date. + +Query params: + +- `date` (`YYYY-MM-DD`) + +## Calendars + +### `GET /api/calendars` + +Returns provider connectivity overview and subscription counts. + +### `GET /api/calendars/google` + +Returns Google provider details. + +- If disconnected, returns `{ "connected": false }`. + +### `GET /api/calendars/microsoft` + +Returns Microsoft provider details. + +- If disconnected, returns `{ "connected": false }`. + +### `GET /api/calendars/subscriptions` + +Returns ICS subscriptions with runtime fields such as `lastFetched` and `lastError`. + +### `GET /api/calendars/events` + +Returns merged event list from connected providers and ICS subscriptions. + +Query params: + +- `start` (ISO date/datetime) +- `end` (ISO date/datetime) + +Response includes: + +- `events` +- `total` +- `sources` (counts by provider) + +## Webhooks + +### `POST /api/webhooks` + +Registers webhook. + +Required fields: + +- `url` +- `events` (non-empty array) + +Optional fields: + +- `id` +- `secret` +- `active` +- `transformFile` +- `corsHeaders` + +### `GET /api/webhooks` + +Lists registered webhooks. Stored secrets are not returned. + +### `DELETE /api/webhooks/:id` + +Deletes webhook. + +### `GET /api/webhooks/deliveries` + +Returns last 100 delivery records. + +## OpenAPI Docs + +### `GET /api/docs` + +Returns OpenAPI JSON generated from registered controllers. + +### `GET /api/docs/ui` + +Returns Swagger UI. + +## Errors + +Common status codes: + +- `400` invalid request or invalid state +- `401` missing/invalid bearer token (when auth token is configured) +- `404` missing task/webhook/resource +- `500` internal error ## Security Notes -If you store tokens in scripts, load them from environment variables rather than hardcoding in source files. -- **Localhost Only**: API server only accepts connections from localhost -- **Desktop Only**: API is not available on mobile platforms -- **Optional Auth**: Bearer token authentication is optional but recommended -- **No HTTPS**: Traffic is unencrypted (localhost only) +Current behavior: + +- CORS allows all origins (`*`). +- Transport is HTTP only (no TLS). +- Node server is started with `server.listen(port)` and does not explicitly bind to `127.0.0.1`. + +Practical guidance: + +- Set an auth token. +- Treat API port as sensitive and keep it firewalled. +- If you expose this port outside localhost, route through a trusted reverse proxy and TLS. ## Troubleshooting -Most connection issues are caused by API disabled state, port mismatch, or stale Obsidian process state after settings changes. -### API Not Starting +### API unavailable -1. Check that API is enabled in settings -2. Ensure port is not in use by another application -3. Try different port (1024-65535) -4. Check Obsidian console for errors +1. Confirm API is enabled in settings. +2. Confirm Obsidian is running. +3. Confirm selected port is free. +4. Reload plugin or restart Obsidian after changing API enable/port. -### Connection Refused +### `401 Authentication required` -1. Verify API is enabled and Obsidian is running -2. Check correct port number -3. Ensure using `http://` not `https://` -4. Try `127.0.0.1` instead of `localhost` +1. Check token value. +2. Check `Bearer ` prefix. +3. Remove whitespace around token. -### Authentication Errors +### Unexpected task list behavior -1. Verify token matches exactly (case-sensitive) -2. Include `Bearer ` prefix in Authorization header -3. Check for trailing spaces in token +If you pass filters to `GET /api/tasks`, the endpoint returns `400` by design. Use `POST /api/tasks/query`. diff --git a/docs/calendar-setup.md b/docs/calendar-setup.md index 863227dd..13ddf96b 100644 --- a/docs/calendar-setup.md +++ b/docs/calendar-setup.md @@ -2,19 +2,6 @@ TaskNotes supports Google Calendar and Microsoft Calendar integration via OAuth 2.0. - - ## Setup (Your Own OAuth Credentials) To connect calendars, create OAuth credentials with Google and/or Microsoft, then paste them into TaskNotes Integrations settings. The sections below walk through each provider. @@ -27,7 +14,7 @@ To connect calendars, create OAuth credentials with Google and/or Microsoft, the *Video by [@antoneheyward](https://www.youtube.com/@antoneheyward)* -In [Google Cloud Console](https://console.cloud.google.com), create or select a project, enable the Google Calendar API, and create OAuth 2.0 credentials using the Desktop application type. Then copy the Client ID and Client Secret into TaskNotes (Settings → Integrations → Calendar) and click **Connect Google Calendar**. +In [Google Cloud Console](https://console.cloud.google.com), create or select a project, enable the Google Calendar API, and create OAuth 2.0 credentials using the Desktop application type. Then copy the Client ID and Client Secret into TaskNotes (`Settings -> TaskNotes -> Integrations`) and click **Connect Google Calendar**. ### Microsoft Calendar @@ -50,7 +37,7 @@ Credentials and tokens are stored locally in your Obsidian data. Tokens refresh **"Failed to connect"** -Verify credentials first, then confirm redirect URI setup. For Microsoft, use `http://127.0.0.1` via the manifest editor (not `localhost`). For Google, verify `http://localhost:8080` is configured. Also confirm API permissions were granted. +Verify credentials first, then confirm loopback redirect configuration. TaskNotes uses a local callback on `127.0.0.1` with a dynamically selected port. For Microsoft, ensure loopback redirect support is configured in the app registration. Also confirm required permissions/scopes are granted. **"Failed to fetch events"** diff --git a/docs/core-concepts.md b/docs/core-concepts.md index ad365c68..0d425bf1 100644 --- a/docs/core-concepts.md +++ b/docs/core-concepts.md @@ -75,7 +75,7 @@ Property keys are configurable. If your vault uses `deadline` instead of `due`, ### Custom Fields -Add any frontmatter property to your tasks. User-defined fields work in filtering, sorting, and templates. Define custom fields in Settings → Task Properties to include them in task modals and views. +Add any frontmatter property to your tasks. User-defined fields work in filtering, sorting, and templates. Define custom fields in `Settings -> TaskNotes -> Task Properties` to include them in task modals and views. ## Bases Integration @@ -93,7 +93,6 @@ This makes view behavior inspectable and predictable. If a task appears in the w Bases is a core plugin included with Obsidian 1.10.1+: -1. Open Settings → Core Plugins +1. Open `Settings -> Core Plugins` 2. Enable "Bases" 3. TaskNotes views will now function - diff --git a/docs/features.md b/docs/features.md index c592c3c5..2965eec6 100644 --- a/docs/features.md +++ b/docs/features.md @@ -1,6 +1,6 @@ # Features -TaskNotes covers the full spectrum of task management, from basic organization to advanced workflows with time tracking and calendar integration. +TaskNotes includes task organization, time tracking, and calendar integration features. ## Task Management @@ -26,7 +26,7 @@ For details on how Bases integration works, see [Core Concepts](core-concepts.md Inline task features let you work from normal notes without context switching. Task links can display interactive cards, checkboxes can be converted into full task notes, and project notes can surface subtasks and dependency relationships in place. -Natural language parsing supports date, priority, and context extraction across multiple languages, which helps keep capture friction low while preserving structured data. +Natural language parsing supports date, priority, and context extraction across multiple languages, which helps keep fast capture while preserving structured data. See [Inline Task Integration](features/inline-tasks.md) for details. @@ -42,7 +42,7 @@ See [Time Management](features/time-management.md) for details. TaskNotes supports bidirectional OAuth sync with Google Calendar and Microsoft Outlook, plus read-only ICS subscriptions for external feeds. Calendar views include month, week, day, year, and list modes, and drag-and-drop scheduling can update tasks directly. -For planning workflows, time-blocking and calendar-linked task updates make it possible to move from backlog management to real schedule execution in one workspace. +For planning workflows, time-blocking and calendar-linked task updates connect backlog management with schedule execution in the same workspace. ![Calendar month view](assets/views-calendar-month.png) diff --git a/docs/features/calendar-integration.md b/docs/features/calendar-integration.md index c0b94cd5..de2d6e7a 100644 --- a/docs/features/calendar-integration.md +++ b/docs/features/calendar-integration.md @@ -19,7 +19,7 @@ OAuth calendar integration requires creating an OAuth application with your cale 1. Create an OAuth application in Google Cloud Console or Microsoft Azure Portal 2. Configure redirect URIs and scopes 3. Obtain client ID and client secret -4. Enter credentials in TaskNotes settings (**Settings → TaskNotes → Integrations → OAuth Calendar Integration**) +4. Enter credentials in TaskNotes settings (`Settings -> TaskNotes -> Integrations`, OAuth calendar section) ### Synchronization Behavior @@ -49,9 +49,10 @@ To create a time entry on the Calendar View: 1. Click and drag on a time slot in the calendar to select a time range 2. When the selection menu appears, choose **Create time entry** (timeblock appears only if the feature is enabled) -3. The time entry editor opens with start/end times pre-filled for the selected range +3. Choose a task in the task selector modal +4. A time entry is created for that task with the selected start/end range -Time entries are associated with tasks and stored in the task's frontmatter. Multiple time entries can exist for a single task. +Time entries are always associated with a specific task and stored in that task's frontmatter. Multiple time entries can exist for one task. ### Managing Time Entries @@ -62,13 +63,13 @@ The time entry editor modal provides functions to: - Delete time entries - See the total time tracked across all entries for a task -Access the time entry editor by clicking on an existing time entry in the calendar or through the task's context menu. +Access the time entry editor by clicking an existing time entry in the calendar. ## ICS Calendar Subscriptions TaskNotes can subscribe to external calendar feeds using the iCalendar (ICS) format. This provides read-only access to events from calendar services. ICS subscriptions differ from OAuth calendar integration in that they are read-only—dragging ICS events to new dates does not update the source calendar. -Add and manage ICS subscriptions from **Settings → TaskNotes → Integrations → Calendar subscriptions**. +Add and manage ICS subscriptions from `Settings -> TaskNotes -> Integrations` (Calendar Subscriptions section). For details on creating notes and tasks from calendar events, see [ICS Integration](ics-integration.md). @@ -82,4 +83,4 @@ The Calendar View supports time blocking for scheduling dedicated work periods. Time blocks are stored in the frontmatter of daily notes and can be linked to specific tasks. This differs from time entries, which track actual time spent and are stored in task frontmatter rather than daily notes. -Enable time blocking under **Settings → TaskNotes → Features → Timeblocking**. +Enable time blocking under `Settings -> TaskNotes -> Features` (Timeblocking section). diff --git a/docs/features/ics-integration.md b/docs/features/ics-integration.md index ad928d8f..981ef6aa 100644 --- a/docs/features/ics-integration.md +++ b/docs/features/ics-integration.md @@ -1,6 +1,6 @@ # ICS Calendar Event Integration -TaskNotes provides integration with ICS calendar events, allowing you to create notes and tasks directly from calendar entries. This feature bridges the gap between scheduled events and actionable content in your vault. +TaskNotes can use ICS calendar events to create notes and tasks directly from calendar entries. ICS integration creates linked notes and tasks from event data without manual re-entry. ## Overview @@ -12,7 +12,7 @@ The ICS integration allows you to: - Generate tasks from events with proper scheduling and context - Link existing vault content to calendar events - Maintain relationships between events and created content -Event context, notes, and follow-up tasks remain connected through frontmatter links rather than manual cross-linking. +Event context, notes, and follow-up tasks remain connected through frontmatter links. ## Event Information Modal @@ -44,7 +44,6 @@ The relationship is maintained through the ICS Event ID field in the content's f **Link Note** - Opens a file selection dialog to link an existing note to the calendar event. **Refresh** - Reloads the list of related content to reflect recent changes. -For meeting workflows, the modal provides a single place to inspect event data, create content, and link existing files. ## Note Creation from Events @@ -93,7 +92,7 @@ Tasks are scheduled using the event's start time as an ISO timestamp, preserving - **Contexts** - Event location is added as a context (if provided) - **Time Estimate** - Calculated from event duration (if start and end times are available) - **Tags** - Includes the ICS event tag and any default task tags -After creation, priority, status, and schedule can be edited when the event data does not match the final task plan. +After creation, priority, status, and schedule can be edited if event data does not match the final task plan. ### Content @@ -109,7 +108,6 @@ You can establish connections between existing vault content and calendar events 2. The file's frontmatter is updated to include the event ID 3. The modification date is updated to reflect the change 4. The connection appears in the related content list -Linking existing files supports notes created before calendar integration or outside the modal flow. ### Bidirectional References @@ -160,7 +158,7 @@ All standard TaskNotes template variables remain available: ## Configuration -ICS integration settings are located under **Settings → TaskNotes → Integrations**: +ICS integration settings are located under `Settings -> TaskNotes -> Integrations`: ### Default Templates @@ -175,7 +173,6 @@ Folder paths support template variables for dynamic organization: - Date variables: `{{year}}`, `{{month}}`, `{{day}}`, `{{monthName}}`, etc. - ICS event variables: `{{icsEventTitle}}`, `{{icsEventLocation}}`, `{{icsEventDescription}}` - Other variables: See template variables section below -Define folder conventions before large imports to keep event-generated files in predictable locations. Example folder paths: diff --git a/docs/features/inline-tasks.md b/docs/features/inline-tasks.md index 9729651a..ee9ffb10 100644 --- a/docs/features/inline-tasks.md +++ b/docs/features/inline-tasks.md @@ -6,7 +6,7 @@ Inline features support capture and task updates without leaving the current not ## Task Link Overlays -When a wikilink to a task note is created, TaskNotes can replace it with an interactive **Task Link Overlay**. Enable or disable overlays from **Settings → TaskNotes → General → Task link overlay**. The widget displays information about the task, such as its status, priority, and due date, and allows actions like changing the status or priority or opening the task for editing directly from the note. +When a wikilink to a task note is created, TaskNotes can replace it with an interactive **Task Link Overlay**. Enable or disable overlays from `Settings -> TaskNotes -> Features` (`Task link overlay`). The widget displays information about the task, such as status, priority, and due date, and allows actions like status/priority changes or opening the edit modal. ![Task Link Overlays in Live Preview mode](../assets/2025-07-17_21-03-55.png) @@ -45,12 +45,12 @@ When you run the command, the current line is used as the title of the new task. ## Instant Task Conversion -The **Instant Task Conversion** feature transforms lines in your notes into TaskNotes files. This works with both checkbox tasks and regular lines of text. Turn the feature on or off from **Settings → TaskNotes → Features → Show convert button next to checkboxes**. When enabled, a "convert" button appears next to the content in edit mode. Clicking this button creates a new task note using the line's text as the title and replaces the original line with a link to the new task file. +The **Instant Task Conversion** feature transforms lines in your notes into TaskNotes files. This works with both checkbox tasks and regular lines of text. Turn the feature on or off from `Settings -> TaskNotes -> Features` (`Show convert button next to checkboxes`). When enabled, a "convert" button appears next to content in edit mode. Clicking this button creates a new task note using the line text as the title and replaces the original line with a link to the new task file. This supports progressive conversion from draft notes to dedicated task files. ### Folder Configuration -By default, converted tasks are placed in the same folder as the current note (`{{currentNotePath}}`). You can change this behavior in **Settings → TaskNotes → General → Folder for converted tasks**: +By default, converted tasks are placed in the same folder as the current note (`{{currentNotePath}}`). You can change this behavior in `Settings -> TaskNotes -> General` (`Folder for converted tasks`): - **Leave empty**: Uses your default tasks folder (configured in the same section) - **`{{currentNotePath}}`**: Places tasks in the same folder as the note you're editing (default) @@ -106,7 +106,7 @@ To use bulk conversion: 3. Search for "Convert all tasks in note to TaskNotes" 4. Execute the command -The command will display progress and show a summary when complete (e.g., "✅ Successfully converted 5 tasks to TaskNotes!"). +The command displays progress and shows a summary when complete (for example, "Successfully converted 5 tasks to TaskNotes."). !!! warning "Important Considerations" @@ -171,7 +171,7 @@ Additional behavior: ### Configuration -Enable or disable the widget in **Settings → TaskNotes → Misc Settings → Show Relationships Widget**. +Enable or disable the widget in `Settings -> TaskNotes -> Appearance` (`Show relationships widget`). Position the widget at the top (after frontmatter) or bottom of the note using the **Relationships Position** setting. @@ -180,7 +180,7 @@ Position the widget at the top (after frontmatter) or bottom of the note using t Tasks with subtasks can display an expand/collapse chevron that toggles subtask visibility. - The chevron can be positioned on the Right (default, hover to show) or on the Left (always visible, matches group chevrons). -- Configure this in Settings → TaskNotes → Misc Settings → Subtask chevron position. +- Configure this in `Settings -> TaskNotes -> Appearance` (`Subtask chevron position`). ![Left subtask chevron](../assets/left-task-subtask-chevron.gif) @@ -196,7 +196,7 @@ These settings are replaced in v4 by: - `showRelationships` and `relationshipsPosition` -If you had project subtasks enabled in v3, the relationships widget will be enabled automatically after upgrading to v4. The underlying Bases file changed from `TaskNotes/Views/project-subtasks.base` to `TaskNotes/Views/relationships.base`. Run the **Create Default Files** command in Settings → General to create the new relationships.base file. +If you had project subtasks enabled in v3, the relationships widget is enabled automatically after upgrading to v4. The underlying Bases file changed from `TaskNotes/Views/project-subtasks.base` to `TaskNotes/Views/relationships.base`. Run the **Create default files** action in `Settings -> TaskNotes -> Integrations` if `relationships.base` is missing. ## Natural Language Processing @@ -234,7 +234,7 @@ Features include: ### Customizable Triggers -**New in v4**: Triggers for NLP properties can be customized in **Settings → Features → NLP Triggers**. +**New in v4**: Triggers for NLP properties can be customized in `Settings -> TaskNotes -> Features` (`NLP Triggers`). You can configure trigger characters or strings for: diff --git a/docs/features/integrations.md b/docs/features/integrations.md index 4e672af8..cf104a4f 100644 --- a/docs/features/integrations.md +++ b/docs/features/integrations.md @@ -5,7 +5,7 @@ TaskNotes integrates with external services and Obsidian's core plugins. ## Bases Core Plugin -TaskNotes v4 uses Obsidian's Bases core plugin for its main views (Task List, Kanban, Calendar, Agenda). Bases must be enabled from Settings → Core Plugins. +TaskNotes v4 uses Obsidian's Bases core plugin for its main views (Task List, Kanban, Calendar, Agenda). Bases must be enabled in `Settings -> Core Plugins`. For details on Bases integration, see [Core Concepts](../core-concepts.md#bases-integration). @@ -17,12 +17,12 @@ For setup instructions, see [Calendar Integration](calendar-integration.md). ## HTTP API -A REST API enables external applications to create, update, and query tasks. Use this for automation, browser extensions, mobile apps, or custom integrations. +A REST API enables external applications to create, update, and query tasks. Use it for automation, browser extensions, or custom integrations. For API documentation, see [HTTP API](../HTTP_API.md). ## Webhooks -Send task data to external services when tasks are created, updated, or completed. Supports custom payload transformations for services like Slack, Discord, and Microsoft Teams. +Webhooks send task event payloads to external services when subscribed events occur. Optional payload transformations support service-specific formats. For configuration, see [Webhooks](../webhooks.md). diff --git a/docs/features/reminders.md b/docs/features/reminders.md index 80d157f1..17b6adf7 100644 --- a/docs/features/reminders.md +++ b/docs/features/reminders.md @@ -112,7 +112,7 @@ These indicators are intended to make reminders discoverable in list-heavy views Default reminders can be configured in: -**Settings → TaskNotes → Task Properties → Reminders** +`Settings -> TaskNotes -> Task Properties -> Reminders` Defaults are applied to: @@ -120,7 +120,7 @@ Defaults are applied to: - Instant conversion - Natural language task creation -Default reminders are best for recurring operating habits, such as pre-deadline checks or day-before planning prompts. +Default reminders are useful for recurring habits, such as pre-deadline checks or day-before planning prompts. ![Task properties settings](../assets/settings-task-properties.png) diff --git a/docs/features/task-management.md b/docs/features/task-management.md index 97cc39ff..5e899e47 100644 --- a/docs/features/task-management.md +++ b/docs/features/task-management.md @@ -94,7 +94,7 @@ Additionally, you can convert any line type in your notes to TaskNotes using the Tasks store their data in YAML frontmatter with properties for status, priority, dates, contexts, projects, tags, time estimates, recurrence, and reminders. Custom fields can extend this structure. -This frontmatter-first design keeps task data editable and portable while still supporting rich UI interactions across views and widgets. +This frontmatter-first design keeps task data editable and portable while supporting consistent behavior across views and widgets. For property types and examples, see [Core Concepts](../core-concepts.md#yaml-frontmatter). For configuration options, see [Task Properties Settings](../settings/task-properties.md). diff --git a/docs/features/time-management.md b/docs/features/time-management.md index fe2b3dff..546d4431 100644 --- a/docs/features/time-management.md +++ b/docs/features/time-management.md @@ -6,7 +6,7 @@ TaskNotes includes features for time tracking and productivity, such as a time t TaskNotes has a time tracker to record the time spent on each task. Time tracking information is stored in the `timeEntries` array within each task's YAML frontmatter. Each time entry includes a start time and an end time. -The time tracking interface includes controls to start and stop tracking, which appear in task views and on task cards. Only one task can be actively tracked at a time; starting a new timer will automatically stop the previous one. The total time spent on a task is calculated from all completed sessions. +The time tracking interface includes controls to start and stop tracking in task views and task cards. TaskNotes prevents duplicate active sessions on the same task. Active sessions on different tasks can exist at the same time, and total time spent on each task is calculated from completed sessions. ### Auto-Stop Time Tracking @@ -14,7 +14,7 @@ TaskNotes can automatically stop time tracking when a task is marked as complete The auto-stop feature works by monitoring task status changes across all views and interfaces. When a task's status changes from any non-completed state to a completed state (as defined by the custom status configuration), any active time tracking session for that task is automatically terminated. -**Configuration Options:** Configure these under **Settings → TaskNotes → Features → Time Tracking**. +**Configuration Options:** Configure these under `Settings -> TaskNotes -> Features` (Time Tracking section). - **Auto-stop tracking** - Enable or disable the automatic stopping behavior (enabled by default) - **Completion notification** - Show a notice when auto-stop occurs (disabled by default) diff --git a/docs/nlp-api.md b/docs/nlp-api.md index 9486d25d..ecb6ab5d 100644 --- a/docs/nlp-api.md +++ b/docs/nlp-api.md @@ -1,23 +1,37 @@ - # TaskNotes NLP API -TaskNotes now exposes its natural language parsing capabilities through API endpoints, allowing external clients to parse task descriptions and create tasks using natural language input. +The NLP API parses natural-language task text and optionally creates a task. ## Endpoints -### POST /api/nlp/parse +- `POST /api/nlp/parse` +- `POST /api/nlp/create` -Parses natural language input and returns structured task data without creating a task. +Both endpoints require a JSON body with `text`. + +## Request Format -**Request:** ```json { - "text": "Review PR #123 tomorrow high priority @work", - "locale": "en" + "text": "Review PR #123 tomorrow high priority @work" } ``` -**Response:** +Notes: + +- `text` is required and must be a string. +- Per-request `locale` is not currently supported. +- Parser language comes from TaskNotes settings (`nlpLanguage`). + +## `POST /api/nlp/parse` + +Parses text and returns two objects: + +- `parsed`: direct parser output +- `taskData`: normalized task payload that would be used for creation + +Example response (shape): + ```json { "success": true, @@ -28,8 +42,8 @@ Parses natural language input and returns structured task data without creating "contexts": ["work"], "projects": [], "priority": "high", - "status": "todo", - "dueDate": "2024-08-13", + "status": null, + "dueDate": "2026-02-22", "scheduledDate": null, "dueTime": null, "scheduledTime": null, @@ -40,11 +54,11 @@ Parses natural language input and returns structured task data without creating "taskData": { "title": "Review PR", "priority": "high", - "status": "todo", + "status": "open", "tags": ["123"], "contexts": ["work"], "projects": [], - "due": "2024-08-13", + "due": "2026-02-22", "scheduled": null, "recurrence": null, "timeEstimate": null @@ -53,123 +67,51 @@ Parses natural language input and returns structured task data without creating } ``` -### POST /api/nlp/create +If parser does not detect status, `taskData.status` falls back to your default status workflow. -Parses natural language input and creates a task in one step. +## `POST /api/nlp/create` -**Request:** -```json -{ - "text": "Call mom due friday 2pm #personal", - "locale": "en" -} +Parses text and creates a task in one call. + +Returns HTTP `201` on success. + +Example: + +```bash +curl -X POST http://localhost:8080/api/nlp/create \ + -H "Content-Type: application/json" \ + -d '{"text":"Call mom due friday 2pm #personal"}' ``` -**Response:** -```json -{ - "success": true, - "data": { - "task": { - "id": "TaskNotes/Tasks/Call mom.md", - "title": "Call mom", - "filePath": "TaskNotes/Tasks/Call mom.md", - "priority": "medium", - "status": "todo", - "tags": ["personal"], - "contexts": [], - "projects": [], - "due": "2024-08-16 14:00" - }, - "parsed": { - "title": "Call mom", - "tags": ["personal"], - "contexts": [], - "projects": [], - "priority": null, - "status": "todo", - "dueDate": "2024-08-16", - "dueTime": "14:00", - "scheduledDate": null, - "scheduledTime": null, - "recurrence": null, - "estimate": null, - "isCompleted": false - } - } -} -``` +Response fields: -## Natural Language Parsing Features +- `data.task` (created task) +- `data.parsed` (parser output) -The NLP parser can extract the following from text input: +## What the Parser Extracts -### Dates and Times -- **Absolute**: "tomorrow", "friday", "next week", "2024-08-15" -- **Relative**: "today", "tomorrow", "next monday" -- **Times**: "2pm", "14:00", "at 6pm" -- **Keywords**: "due friday", "scheduled tomorrow" +The parser can extract: -### Priority Levels -- **Keywords**: "high priority", "low priority", "urgent" -- **Symbols**: "!!!" (high), "!!" (medium), "!" (low) +- Dates and times (`tomorrow`, `friday 2pm`, ISO dates) +- Priority terms (`high priority`, `urgent`, symbol patterns) +- Status terms (mapped to your configured statuses) +- Tags (`#tag`) +- Contexts (`@context`) +- Projects (`+project`) +- Time estimates (`2h`, `30min`, `estimate 45m`) +- Recurrence (`daily`, `weekly`, `every monday`) -### Tags -- **Format**: "#tag", "#personal", "#work" -- **Extracted**: Becomes task tags +Exact parsing behavior depends on your TaskNotes NLP settings and trigger configuration. -### Contexts -- **Format**: "@context", "@work", "@home" -- **Extracted**: Becomes task contexts +## Error Responses -### Projects -- **Format**: "+project", "+website-redesign" -- **Extracted**: Becomes task projects +Common errors: -### Time Estimates -- **Format**: "2h", "30min", "estimate 45m" -- **Extracted**: Converted to minutes +- `400`: missing/invalid `text` +- `401`: auth required (if API token is enabled) +- `500`: parse/processing error -### Recurrence -- **Keywords**: "daily", "weekly", "monthly", "every monday" -- **Format**: Converted to RRule format - -### Status -- **Keywords**: "done", "completed", "todo", "in-progress" -- **Custom**: Uses your configured status types - -### Multi-language Support - -The NLP parser supports multiple languages. You can specify the language to use for parsing by providing a `locale` parameter in the request body. The following locales are supported: - -- `en` (English) -- `de` (German) -- `es` (Spanish) -- `fr` (French) -- `it` (Italian) -- `ja` (Japanese) -- `nl` (Dutch) -- `pt` (Portuguese) -- `ru` (Russian) -- `sv` (Swedish) -- `uk` (Ukrainian) -- `zh` (Chinese) - -If no locale is provided, the parser will default to English. - -## Example Inputs - -| Input | Extracted | -|-------|-----------| -| `Review PR #123 tomorrow high priority @work` | Title: "Review PR", Tags: ["123"], Contexts: ["work"], Priority: "high", Due: tomorrow | -| `Buy groceries today at 6pm` | Title: "Buy groceries", Scheduled: today 18:00 | -| `Weekly team meeting every monday 10am` | Title: "Weekly team meeting", Recurrence: "weekly", Scheduled: mondays 10:00 | -| `Fix bug estimate 2h high priority @urgent` | Title: "Fix bug", Estimate: 120 min, Priority: "high", Contexts: ["urgent"] | -| `Call mom due friday 2pm #personal` | Title: "Call mom", Due: friday 14:00, Tags: ["personal"] | - -## Error Handling - -Both endpoints return standard error responses: +Error shape: ```json { @@ -178,75 +120,56 @@ Both endpoints return standard error responses: } ``` -Common errors: -- **400**: Missing or invalid `text` field -- **401**: Authentication required (if API token configured) -- **500**: Internal server error during parsing or task creation +## Client Examples -## Client Usage +### JavaScript -### JavaScript Example ```javascript -// Parse text only -async function parseTask(text, locale = 'en') { - const response = await fetch('http://localhost:8080/api/nlp/parse', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ text, locale }) +async function parseTask(text) { + const response = await fetch("http://localhost:8080/api/nlp/parse", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ text }) }); return response.json(); } -// Parse and create task -async function createTaskFromText(text, locale = 'en') { - const response = await fetch('http://localhost:8080/api/nlp/create', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ text, locale }) +async function createTaskFromText(text) { + const response = await fetch("http://localhost:8080/api/nlp/create", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ text }) }); return response.json(); } ``` -### Python Example +### Python + ```python import requests -def parse_task(text, locale='en'): - response = requests.post('http://localhost:8080/api/nlp/parse', - json={'text': text, 'locale': locale}) + +def parse_task(text): + response = requests.post( + "http://localhost:8080/api/nlp/parse", + json={"text": text}, + ) return response.json() -def create_task_from_text(text, locale='en'): - response = requests.post('http://localhost:8080/api/nlp/create', - json={'text': text, 'locale': locale}) + +def create_task_from_text(text): + response = requests.post( + "http://localhost:8080/api/nlp/create", + json={"text": text}, + ) return response.json() ``` -## Integration Benefits - -- **Mobile Apps**: Parse voice-to-text input -- **CLI Tools**: Natural language task creation -- **Chat Bots**: Process user messages -- **Email Integration**: Parse forwarded emails -- **Browser Extensions**: Smart task creation -- **Third-party Apps**: Integrate with existing workflows - ## Authentication -If you have an API authentication token configured in TaskNotes settings, include it in requests: +If API auth token is enabled, include bearer token: -```javascript -headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer your-api-token' -} +```http +Authorization: Bearer YOUR_TOKEN ``` - -## User Settings Integration - -The NLP parser uses your TaskNotes configuration: -- **Custom Status Types**: Your configured statuses -- **Custom Priorities**: Your priority levels -- **Default Behavior**: Scheduled vs due date preferences -- **Task Creation Defaults**: Applied to created tasks diff --git a/docs/privacy.md b/docs/privacy.md index be7615f4..d8d85991 100644 --- a/docs/privacy.md +++ b/docs/privacy.md @@ -1,56 +1,67 @@ # TaskNotes Privacy Policy -Last updated: February 8, 2026 +Last updated: February 21, 2026 ## Overview -TaskNotes is an Obsidian plugin that helps you manage tasks within your notes. +TaskNotes is an Obsidian plugin. It stores task data in your local vault files. ## Data Collection and Usage -TaskNotes does not collect, transmit, or share any of your data. +TaskNotes does not include telemetry or analytics collection. -## Data Storage +## Local Storage -Task and note content stays in your local Obsidian vault, and plugin settings are stored in Obsidian’s local configuration. By default, TaskNotes does not send vault data to external services. +Task and note content stays in your local Obsidian vault. +Plugin settings are stored in Obsidian's local plugin configuration. + +## Optional Network Features + +TaskNotes is local-first. Network requests occur only when you enable features that require them. + +Optional network features: + +- OAuth calendar integration (Google/Microsoft): fetches and updates calendar events through provider APIs. +- ICS subscriptions: fetches events from configured ICS URLs. +- Webhooks: sends event payloads to webhook endpoints you configure. +- API docs UI (`/api/docs/ui`): loads Swagger UI assets from `unpkg.com` in your browser. + +## OAuth Credentials and Tokens + +- OAuth client credentials are configured by you in TaskNotes settings. +- Access and refresh tokens are stored locally on your device. +- You can disconnect providers at any time to revoke TaskNotes access. + +## Third-Party Services (When Enabled) + +- Google APIs: https://policies.google.com/privacy +- Microsoft APIs: https://privacy.microsoft.com/privacystatement +- Any ICS host or webhook endpoint you configure + +## What TaskNotes Does Not Do + +- No TaskNotes-hosted cloud sync service +- No remote storage of your vault content by TaskNotes +- No telemetry pipeline sending usage metrics ## Data Deletion -You can stop TaskNotes processing by disabling the plugin, and remove plugin configuration by uninstalling it. Your notes remain in your vault under your control. +You can stop TaskNotes processing by disabling the plugin. +You can remove plugin settings by uninstalling the plugin. +Your notes remain in your vault. -## Network Requests (Optional Features) +## Changes to This Policy -TaskNotes operates locally by default, but includes optional features that make network requests when you enable them: - -**Optional network features:** - -OAuth calendar integration (Google or Microsoft) uses OAuth 2.0 (with PKCE) to authenticate and stores access/refresh tokens locally on your device. If enabled, TaskNotes fetches calendar events and can update external events when you choose sync actions. - -ICS subscriptions fetch event data from URLs you configure. Webhooks send task event payloads to endpoints you configure. License validation sends your license key to Lemon Squeezy for validity checks, caches results locally for 24 hours, and applies a 7-day grace period when validation is temporarily unavailable. - -**OAuth Credentials:** - -TaskNotes includes bundled OAuth client credentials for quick setup. These app credentials are public identifiers and do not expose your account. Authentication and calendar access are controlled by your user tokens. You can also use your own OAuth credentials in advanced setup. - -**Third-Party Services:** - -- **Lemon Squeezy**: License validation only (https://www.lemonsqueezy.com/privacy) -- **Google**: OAuth authentication and Calendar API access (https://policies.google.com/privacy) -- **Microsoft**: OAuth authentication and Calendar API access (https://privacy.microsoft.com/privacystatement) - -**What we never do:** -TaskNotes does not run analytics/telemetry collection, does not read your notes remotely, and does not store your calendar data on TaskNotes servers. Calendar requests go directly between your device and the provider APIs you connect. - -## Changes to Privacy Policy - -We may update this policy. Changes will be posted in this file with an updated date. +This policy may be updated. Changes are published in this file with a new date. ## Contact -For questions or concerns about privacy, please open an issue on GitHub: +For privacy questions, open an issue: https://github.com/callumalpass/tasknotes/issues -## Open Source +## Open Source Verification -TaskNotes is open source software. You can review the code at https://github.com/callumalpass/tasknotes to verify these privacy practices. +TaskNotes is open source. You can inspect the implementation: + +https://github.com/callumalpass/tasknotes diff --git a/docs/releases/unreleased.md b/docs/releases/unreleased.md index 15a1a085..3d3d4024 100644 --- a/docs/releases/unreleased.md +++ b/docs/releases/unreleased.md @@ -33,3 +33,5 @@ Example: - Fixed a settings Integrations listener lifecycle issue that could accumulate calendar update callbacks while the settings UI is repeatedly opened/re-rendered - (#1630) Fixed TaskNote inline task cards ignoring centered "Readable line length" layout in Minimal theme by constraining and centering the widget in readable mode - Thanks to @martin-forge for reporting +- Consolidated documentation cleanup for accuracy and clarity across API, webhook, NLP, privacy, settings, and view docs (corrected outdated endpoint/behavior details, normalized current settings paths, and tightened non-release prose) +- Fixed a broken docs cross-reference from Property Types Reference to Task Properties settings diff --git a/docs/settings.md b/docs/settings.md index 13c9aeaa..902494bc 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -1,11 +1,11 @@ # Settings -TaskNotes provides settings to customize its behavior and appearance. The settings are organized into tabs, each controlling a different aspect of the plugin. +TaskNotes settings are organized into tabs. Each tab controls a different part of plugin behavior. ## General -The General tab controls how TaskNotes recognizes tasks, where files are stored, and how task cards respond to clicks. This is the best place to start if your vault already has conventions you want TaskNotes to follow, such as custom task tags, property-based identification, or specific folder structures. +The General tab controls task identification, storage locations, and task-card click behavior. Use this tab when your vault already has conventions such as custom tags, property-based identification, or specific folder structures. For more information, see the [General Settings](settings/general.md) documentation. diff --git a/docs/settings/advanced-settings.md b/docs/settings/advanced-settings.md index 457714f6..a32e1bde 100644 --- a/docs/settings/advanced-settings.md +++ b/docs/settings/advanced-settings.md @@ -1,95 +1,86 @@ -# Advanced Settings - -These settings provide customization options, such as field mapping, custom status and priority workflows. - -## Field Mapping - -**Field Mapping** allows you to customize the YAML property names that TaskNotes uses for its internal properties. This can be used for integrating with other plugins or for matching your existing vault structure. You can map all of the core task properties, including title, status, priority, due date, contexts, and projects, as well as other properties like time estimates and recurrence patterns. - -## User Fields - -**User Fields** allow you to define custom frontmatter properties that appear as filterable options in all TaskNotes views. This feature enables you to extend TaskNotes with your own custom metadata while maintaining full filtering capabilities. - -### Creating User Fields - -1. Navigate to **Settings → Advanced → Field Mapping** -2. Scroll to the **User Fields** section -3. Click **Add field** to create a new custom property -4. Configure each field with: - - **Property Name**: The frontmatter key (e.g., `effort`, `assignee`) - - **Display Name**: How it appears in filter dropdowns (e.g., "Effort Level", "Assigned To") - - **Type**: Data type that determines available filter operators - -### Field Types - -**Text**: For string values like names, descriptions, or categories -- Filter operators: is, is not, contains, does not contain, is empty, is not empty -- Example: `assignee: "John Smith"` - -**Number**: For numeric values including mixed text-number formats -- Filter operators: is, is not, greater than, less than, equal or greater than, equal or less than, is empty, is not empty -- Example: `effort: 5` or `priority: "2-Medium"` (evaluates as number 2) - -**Date**: For date values with natural language support -- Filter operators: is, is not, before, after, on or before, on or after, is empty, is not empty -- Example: `deadline: 2024-12-25` or `review_date: "next week"` - -**Boolean**: For true/false values -- Filter operators: is checked, is not checked -- Example: `urgent: true` - -**List**: For comma-separated values including wikilinks -- Filter operators: contains, does not contain, is empty, is not empty -- Example: `tags: "work, urgent"` or `related: "[[Project A]], [[Project B]]"` - -### Smart List Filtering - -User fields of type **List** support comma splitting that preserves: -- **Wikilinks**: `[[Health, Fitness & Mindset]], [[Nutrition]]` correctly splits into two items -- **Quoted text**: `"Focus, Deep Work", Notes` treats quoted content as single items -- **Mixed content**: Handles combinations of wikilinks, quotes, and plain text - -![Custom User Fields - List Filtering](../assets/custom-user-fields-list-filter.gif) - -### Numeric Field Intelligence - -User fields of type **Number** can handle mixed text-number formats, making them perfect for priority systems: -- `"1-Low"` evaluates as number `1` -- `"2-Medium"` evaluates as number `2` -- `"3-High"` evaluates as number `3` - -This allows filtering like "Priority greater than 1" to match "2-Medium" and "3-High" while excluding "1-Low". - -![Custom User Fields - Numeric Filtering](../assets/custom-user-fields-numeric-filter.gif) - -### Instant Availability - -User fields become available in the Bases filter/sort/group controls immediately after configuration—no plugin reload required. The fields appear in all TaskNotes views (Task List, Agenda, Kanban, Calendar View) as soon as you save the settings. - -## Custom Status System - -TaskNotes allows you to define your own **Custom Status Workflows**. You can create as many statuses as you need, and you can customize their names, colors, and completion behavior. You can also set the order in which the statuses appear, which determines the progression of the workflow. - -## Custom Priority System - -You can also create a **Custom Priority System**. You can define as many priority levels as you need, and you can customize their names, colors, and weights. - -> **Note:** In TaskNotes v4.0+, Obsidian's Bases plugin handles priority sorting alphabetically by the priority value. To ensure priorities sort in your desired order, name them to sort alphabetically (e.g., `1-urgent`, `2-high`, `3-medium`, `4-normal`, `5-low`). The weight field is preserved for potential future use but does not affect Bases sorting. - -## Pomodoro Timer Settings - -You can configure the **Pomodoro Timer** from this section, including the duration of work and break sessions, as well as the notification settings. You can also choose where to store the Pomodoro session data. - -### Time Tracking Auto-Stop - -**Auto-stop time tracking on task completion** - Automatically stop active time tracking when any task is marked as completed. This ensures that time tracking accurately reflects the work done on tasks without requiring manual intervention. Enabled by default. - -**Show notification when auto-stopping time tracking** - Display a notification when time tracking is automatically stopped due to task completion. This provides feedback when the auto-stop feature activates. Disabled by default to avoid notification clutter. - -## Field Mapping - -The Field Mapping section includes two additional fields for ICS integration: - -**ICS Event ID** - Frontmatter field name for storing calendar event identifiers. Default is `icsEventId`. This field maintains the connection between vault content and calendar events. - -**ICS Event Tag** - Tag used to identify content created from ICS events. Default is `ics_event`. This tag is automatically added to notes and tasks generated from calendar events. +# Advanced Settings + +This page documents advanced configuration patterns that are spread across multiple TaskNotes settings tabs. + +## Where Advanced Configuration Lives + +TaskNotes v4 uses a 6-tab settings layout: + +- `General` +- `Task Properties` +- `Modal Fields` +- `Appearance` +- `Features` +- `Integrations` + +There is no separate `Advanced` tab in the current UI. + +## Field Mapping + +Field mapping controls which frontmatter keys TaskNotes reads and writes for core properties. + +Location: + +- `Settings -> TaskNotes -> Task Properties` + +Use field mapping when: + +- You already use different frontmatter key names in your vault. +- You are integrating with other plugins that expect specific keys. + +## User Fields + +User fields add custom properties that become available in filters, grouping, sorting, and modal forms. + +Location: + +- `Settings -> TaskNotes -> Task Properties` + +Typical fields: + +- Text (`client`, `assignee`) +- Number (`effort`, `score`) +- Date (`reviewDate`) +- Boolean (`urgent`) +- List (`labels`, `stakeholders`) + +## Status and Priority Workflows + +Custom statuses and priorities are configured in Task Properties. + +Location: + +- `Settings -> TaskNotes -> Task Properties` + +Notes: + +- Statuses define workflow progression and completion semantics. +- Priority names sort lexicographically in Bases unless your view uses explicit formulas. + +## Modal Field Layout + +The create/edit modal can be reconfigured to show only relevant fields. + +Location: + +- `Settings -> TaskNotes -> Modal Fields` + +This is useful when your team standardizes a minimal required schema. + +## Time Tracking and Pomodoro Controls + +Time tracking auto-stop and pomodoro behavior are configured in Features. + +Location: + +- `Settings -> TaskNotes -> Features` + +## ICS-Specific Field Mapping + +ICS-related identifiers (`icsEventId`, ICS tag field) are part of field mapping. + +Location: + +- `Settings -> TaskNotes -> Task Properties` + +These fields are used to maintain links between imported calendar events and created notes/tasks. diff --git a/docs/settings/calendar-settings.md b/docs/settings/calendar-settings.md index fa0401ff..98e6f2b1 100644 --- a/docs/settings/calendar-settings.md +++ b/docs/settings/calendar-settings.md @@ -1,47 +1,58 @@ # Calendar Settings -These settings control the appearance and behavior of the calendar views, as well as the integration with external calendar systems. +Calendar behavior is configured across multiple tabs. -## Default Calendar View +## Settings Map -You can set the **Default View** for the Calendar View, choosing from month, week, day, year, or custom days. You can also configure the **Time Slot Duration** and the **Time Range** that is displayed in the week, day, and custom days views. +### Appearance tab -**Custom view day count** - When using the Custom Days view, this slider controls how many days (2-10) are displayed simultaneously. The default is 3 days, which provides optimal screen space utilization while maintaining detailed scheduling capabilities. +Location: -## Week and Date Settings +- `Settings -> TaskNotes -> Appearance` -You can set the **First Day of the Week** and choose whether to **Show Weekends** in the calendar views. +Controls include: -You can also configure the **Calendar Locale** to change the calendar's language and date formatting. This supports different calendar systems like the Jalali (Persian) calendar by setting the locale to "fa". Leave this field empty to automatically detect your browser's locale. +- Default calendar view mode (month/week/day/year/custom days) +- First day of week and weekend visibility +- Locale and date formatting +- Time slot window (`slotMinTime`, `slotMaxTime`, `slotDuration`) +- Default event visibility toggles (due, scheduled, recurring, time entries, ICS) +- Event stacking and overlap display options -## Event Type Visibility +### Features tab -You can control which types of events are shown by default in the calendar views, including scheduled tasks, tasks with due dates, recurring tasks, time entries, and events from external calendars. +Location: -## Event Display and Stacking +- `Settings -> TaskNotes -> Features` -These settings control how events are displayed when multiple events occur at the same time or on the same day. +Controls include: -**Allow events to overlap** - When enabled, timed events in week and day views can visually overlap each other. When disabled, events are displayed side-by-side with no overlapping. +- Timeblocking enable/disable +- Timeblocking behavior options -**Max stacked events (week/day view)** - Limits how many events can stack horizontally in the week and day views. When exceeded, a "+X more" link appears. Set to 0 for unlimited stacking. +### Integrations tab -**Max events per day (month view)** - Limits how many events are shown per day cell in month view. When exceeded, a "+X more" link appears. Set to 0 for automatic limiting based on cell height. +Location: -**Max event rows per day (month view)** - Limits how many rows of events are shown per day cell in month view. Set to 0 for unlimited rows. +- `Settings -> TaskNotes -> Integrations` -## Timeblocking Features +Controls include: -You can enable or disable the **Timeblocking** feature in the Features tab, which allows you to create and manage timeblocks in the calendar views. When enabled, dragging on a time slot in the calendar view will display a context menu that includes the "Create timeblock" option. +- Google/Microsoft OAuth calendar connections +- ICS calendar subscriptions +- ICS import behavior and note/task creation options +- Automatic ICS export settings +- Google Calendar task export settings -## External Calendar Integration +## Practical Setup Order -You can manage your **ICS Calendar Subscriptions** from this section, including adding, editing, and removing subscriptions. You can also set the refresh interval for remote calendars. +1. Configure baseline calendar display in `Appearance`. +2. Enable timeblocking in `Features` if you schedule focused blocks. +3. Add external calendars in `Integrations`. +4. Validate event visibility in your calendar `.base` view options. -## Content Creation from Events +## Related Docs -These settings control how notes are created from calendar events. - -**Default note template** - Template file for notes created from ICS events (leave empty for default format). Specify the path to a markdown template file. - -**Default note folder** - Folder for notes created from ICS events (leave empty for vault root). The folder structure will be created if it doesn't exist. +- `docs/features/calendar-integration.md` +- `docs/calendar-setup.md` +- `docs/views/calendar-views.md` diff --git a/docs/settings/misc-settings.md b/docs/settings/misc-settings.md index b904adb8..db34d3b5 100644 --- a/docs/settings/misc-settings.md +++ b/docs/settings/misc-settings.md @@ -1,43 +1,49 @@ # Misc Settings -These settings control various plugin features and display options that don't fit into other categories. +This page documents settings that do not fit one single workflow category. -## Saved Views button position +## Saved Views Button Position -Choose where the Saved Views button appears in the view header for Task List, Agenda, Kanban, and Calendar Bases views: +Controls where the Saved Views button appears in view headers. -- **Right (default)**: Saved Views sits after the other header controls -- **Left**: Saved Views is placed before the other controls +- `Right` (default) +- `Left` -This setting only changes alignment—the button still opens the same Saved Views picker. +Affects Task List, Agenda, Kanban, and Calendar Bases views. +## Status Bar: Tracked Tasks -## Status Bar +Shows currently tracked tasks in the Obsidian status bar. -**Show tracked tasks in status bar** - Display currently tracked tasks (with active time tracking) in the status bar at the bottom of the app. This provides a quick visual indicator of which tasks are currently being tracked without needing to open the TaskNotes views. +Use this when you want active timer visibility without opening a task view. ## Relationships Widget -**Show relationships widget** - Display the unified widget that surfaces subtasks, parent projects, and blocking relationships in the editor. Disable this if you prefer to open the `relationships.base` file in a separate pane instead of embedding it in notes. +Shows or hides the inline relationships widget in notes. -## Task Display +The widget surfaces: -**Hide completed tasks from overdue** - Control whether completed tasks appear as overdue in the agenda view. When enabled (default), completed tasks will not appear in the "Overdue" section of the agenda view, even if their due or scheduled date has passed. This setting affects task grouping and overdue detection throughout the plugin, helping keep overdue lists focused on actionable items. +- Subtasks +- Parent project links +- Blocking and blocked-by relationships +## Hide Completed from Overdue + +When enabled, completed tasks are not shown in overdue sections even if their dates are in the past. ## Subtask Chevron Position -Configure where the expand/collapse chevron appears on project task cards and inside the Relationships widget. +Controls chevron placement for expandable subtasks. -- Right (default): The chevron appears on the right-hand side and shows on hover. -- Left (match group chevrons): The chevron appears on the left-hand side, always visible, matching group chevrons. +- Right (default) +- Left (matches group chevrons) -Demo: +## Disable Note Indexing -![Left subtask chevron](../assets/left-task-subtask-chevron.gif) +Disables indexing of non-task notes to reduce indexing overhead in large vaults. -## Performance Settings +Trade-off: -**Disable note indexing** - Disable indexing and caching of non-task notes to improve performance in large vaults. Note: This will disable the Notes view and notes display in the Agenda view. Requires plugin restart to take effect. - -This setting is useful for large vaults where performance may be impacted by indexing many notes. When enabled, TaskNotes will only index and track task files, which can significantly improve performance but will disable some features that depend on note content. +- Task workflows remain available. +- Features that rely on non-task note indexing (for example note/date lookups used by note-oriented UI elements) will have reduced functionality. +- Plugin reload/restart is recommended after changing this setting. diff --git a/docs/settings/property-types-reference.md b/docs/settings/property-types-reference.md index ed7e0f18..2bac17d8 100644 --- a/docs/settings/property-types-reference.md +++ b/docs/settings/property-types-reference.md @@ -337,7 +337,7 @@ For example, if you map `due` to `dueDate`, TaskNotes will expect: dueDate: "2025-01-15" ``` -See [Field Mapping](task-properties.md#field-mapping) for configuration details. +See [Task Properties settings](task-properties.md) for configuration details. --- diff --git a/docs/settings/task-defaults.md b/docs/settings/task-defaults.md index b7fe8cda..d7e99f54 100644 --- a/docs/settings/task-defaults.md +++ b/docs/settings/task-defaults.md @@ -258,7 +258,7 @@ Storing title in filename favors path-readable tasks and external tooling that k ## Default Reminders -Configure default reminders in **Settings → Task Properties → Reminders**. These reminders automatically apply to new tasks. +Configure default reminders in `Settings -> TaskNotes -> Task Properties` (Reminders card). These reminders automatically apply to new tasks. Default reminders apply to all new tasks and can be supplemented with per-task reminders. ### Reminder Types @@ -272,7 +272,7 @@ Default reminders apply to all new tasks and can be supplemented with per-task r ### Configuration -1. Navigate to Settings → TaskNotes → Task Properties +1. Navigate to `Settings -> TaskNotes -> Task Properties` 2. Expand the Reminders card 3. In the Default Reminders section, select type (Relative or Absolute) 4. Configure timing and optional description diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 0eabdc28..82152fe4 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -10,13 +10,13 @@ When debugging, start with the smallest reproducible scenario: one affected task **Symptoms**: TaskNotes views show errors or don't display tasks -First confirm Bases is enabled (Settings → Core Plugins → Bases), then restart Obsidian once. If views are still missing, verify `.base` files exist in `TaskNotes/Views/`. If needed, regenerate defaults from Settings → TaskNotes → Integrations → **Create default files**. +First confirm Bases is enabled (`Settings -> Core Plugins -> Bases`), then restart Obsidian once. If views are still missing, verify `.base` files exist in `TaskNotes/Views/`. If needed, regenerate defaults from `Settings -> TaskNotes -> Integrations` (`Create default files`). ### Commands Open Wrong Files **Symptoms**: Ribbon icons or commands open unexpected files -Check command mappings in Settings → TaskNotes → Integrations → View Commands. Reset mappings that were changed unintentionally, then verify each referenced `.base` file exists at the configured path. +Check command mappings in `Settings -> TaskNotes -> General` (`View Commands`). Reset mappings that were changed unintentionally, then verify each referenced `.base` file exists at the configured path. ## Common Issues @@ -48,7 +48,7 @@ Reduce visible event layers first (scheduled/due/recurring/time entries), then i **Symptoms**: Natural language input doesn't extract expected task properties -Enable NLP in Settings → TaskNotes → Features, then verify your trigger characters (`@`, `#`, `!` by default) and any custom status/priority mappings. If parsing still seems inconsistent, compare input against the syntax in [NLP API](nlp-api.md). +Enable NLP in `Settings -> TaskNotes -> Features`, then verify your trigger characters (`@`, `#`, `!` by default) and any custom status/priority mappings. If parsing still seems inconsistent, compare input against the syntax in [NLP API](nlp-api.md). ### Time Tracking Issues @@ -88,7 +88,7 @@ To improve loading times, reduce external calendar subscriptions, increase ICS r **Symptoms**: Google Calendar or Microsoft Outlook won't connect -Verify credentials and redirect URI (`http://localhost:42813/callback`) exactly, ensure app publication/test-user access is correct, and retry after disconnecting. Also check popup blockers. For provider-specific setup details, use [Calendar Setup](calendar-setup.md). +Verify credentials and loopback redirect configuration (`127.0.0.1` with dynamic local port), ensure app publication/test-user access is correct, and retry after disconnecting. Also check popup blockers. For provider-specific setup details, use [Calendar Setup](calendar-setup.md). ### OAuth Calendar Not Syncing diff --git a/docs/views.md b/docs/views.md index 4490d673..c16832bd 100644 --- a/docs/views.md +++ b/docs/views.md @@ -7,7 +7,7 @@ For details on Bases integration and how to enable it, see [Core Concepts](core- ## Task-Focused Views -Task-focused views are different entry points into the same underlying task notes. The [Task List View](views/task-list.md) is usually the best default for day-to-day planning because it exposes filters, sorting, and grouping in a straightforward list format. +Task-focused views are different entry points into the same underlying task notes. The [Task List View](views/task-list.md) is a common starting view for day-to-day planning because it exposes filters, sorting, and grouping in list format. When you want workflow by status, [Kanban View](views/kanban-view.md) organizes cards into columns and can optionally add swimlanes for an extra organizational layer. [Calendar Views](views/calendar-views.md) are useful when schedule and timing matter more than backlog shape, with month/week/day/year/list modes plus drag-and-drop scheduling and time-block support. diff --git a/docs/views/notes-view.md b/docs/views/notes-view.md index edcc3a3c..9041bd9d 100644 --- a/docs/views/notes-view.md +++ b/docs/views/notes-view.md @@ -1,61 +1,18 @@ -# Notes View +# Notes View (Legacy) -The Notes View displays your vault's non-task notes organized by date, providing a chronological view of your notes for the selected day. +This page documents the legacy Notes View from earlier TaskNotes versions. -## What Notes Are Included +## Current Status -The Notes View shows **non-task notes** that meet specific criteria: +In current v4 Bases-first workflows, Notes View is not part of the default view command/file set. -### Inclusion Criteria +Use these v4 alternatives instead: -- **Markdown files** (`.md` extension) -- **Non-task notes** (don't contain the task tag in frontmatter) -- **Date-matched notes** for the selected day -- **Not in excluded folders** (configurable in settings) -- **Note indexing enabled** (performance setting) +- `views/calendar-views.md` for schedule-centric planning +- `views/agenda-view.md` for list-style near-term planning +- `views/default-base-templates.md` to customize `.base` files -### Date Detection +## Legacy Behavior Reference -Notes are associated with dates through: -1. **Frontmatter fields**: `dateCreated` or `date` -2. **Filename patterns**: YYYY-MM-DD format in the filename -3. Only notes matching the selected date appear - -### Exclusions - -Notes are excluded if they: - -- Are task files (contain the configured task tag) -- Are in excluded folders (Templates, Archive, etc.) -- Have note indexing disabled (performance optimization) -- Don't have a date or the date doesn't match the selected day - -## Interface Layout - -The Notes View consists of: - -- **Date navigation header** - Navigate between dates to view notes for different days -- **Note cards list** - Displays all qualifying notes for the selected date -- Each note card shows the note's title, path, and relevant metadata - -## Note Actions - -**Click to open**: Click on any note card to open the note in the current pane. - -## Settings That Affect Notes View - -Several settings control what appears in the Notes View: - -- **Note indexing** (Settings > Performance > "Disable note indexing") - - When disabled, no notes appear in this view -- **Excluded folders** (Settings > General > "Excluded folders") - - Comma-separated folder paths to exclude from indexing -- **Task tag** (Settings > General) - - Notes with this tag are treated as tasks and excluded from Notes view - -## Performance Considerations - -For large vaults, you can: -- Disable note indexing entirely to improve performance -- Add frequently-changing folders (like Templates) to excluded folders -- Note that disabling note indexing will hide the Notes view entirely +Historically, Notes View listed non-task notes by date using note indexing. +If you are migrating older docs or saved workflows, verify behavior in your current plugin version before relying on this page. diff --git a/docs/views/task-list.md b/docs/views/task-list.md index 7dc4b66a..d5dbb2d9 100644 --- a/docs/views/task-list.md +++ b/docs/views/task-list.md @@ -15,14 +15,14 @@ Task List is implemented as a `.base` file located in `TaskNotes/Views/tasks-def Bases is an official Obsidian core plugin built directly into Obsidian (not a community plugin). It provides a framework for creating database views of notes and tasks. To enable Bases: -1. Open Settings → Core Plugins +1. Open `Settings -> Core Plugins` 2. Enable "Bases" 3. TaskNotes view commands will now open `.base` files from `TaskNotes/Views/` If commands open empty or unexpected views, first confirm Bases is enabled and the command path points to the intended `.base` file. ### View File Location -When you use the "Open Tasks View" command or ribbon icon, TaskNotes opens the `.base` file configured under **Settings → TaskNotes → General → View Commands** (initially `TaskNotes/Views/tasks-default.base`). The default file is created automatically the first time you use the command, and you can point the command to any other `.base` file if you maintain multiple task-list layouts. +When you use the "Open Tasks View" command or ribbon icon, TaskNotes opens the `.base` file configured under `Settings -> TaskNotes -> General` (`View Commands`) (initially `TaskNotes/Views/tasks-default.base`). The default file is created automatically the first time you use the command, and you can point the command to any other `.base` file if you maintain multiple task-list layouts. ## Configuration @@ -96,7 +96,7 @@ TaskNotes properties are accessed in Bases YAML using these paths: | Created | `file.ctime` | File creation date | | Modified | `file.mtime` | File modification date | -The exact property names depend on your TaskNotes field mapping settings (Settings → Advanced → Field Mapping). The table above shows the default mappings. +The exact property names depend on your TaskNotes field mapping settings (`Settings -> TaskNotes -> Task Properties`). The table above shows default mappings. ## Filtering and Sorting @@ -233,7 +233,7 @@ The v3 FilterBar UI component no longer exists - all configuration is done throu The Task List View provides interaction with tasks through clicking and context menus: -- **Click on a task**: Opens the task for editing or navigates to the task note (behavior configured in Settings → General → Click Actions) +- **Click on a task**: Opens the task for editing or navigates to the task note (behavior configured in `Settings -> TaskNotes -> General` in click-action controls) - **Right-click on a task**: Opens a context menu with actions: - Mark as complete - Change priority diff --git a/docs/webhooks.md b/docs/webhooks.md index b2286917..f8400d2b 100644 --- a/docs/webhooks.md +++ b/docs/webhooks.md @@ -1,930 +1,276 @@ # TaskNotes Webhooks -TaskNotes webhooks enable real-time integrations by sending HTTP POST requests to your configured endpoints whenever specific events occur. This allows you to build automation, sync with external services, and create custom workflows. -Webhooks provide push-based event delivery. For periodic sync, HTTP API polling is an alternative. +TaskNotes webhooks send HTTP POST requests when selected events occur. -## Quick Start +## Prerequisites -1. **Enable HTTP API** in TaskNotes Settings → Integrations → HTTP API -2. **Add a webhook** by clicking "Add Webhook" in the webhook settings -3. **Select events** you want to receive notifications for -4. **Configure transformation** (optional) for custom payload formats -5. **Test your integration** using the included test server or your endpoint -A minimal first setup is one event (for example `task.completed`) and a logging endpoint, then incremental event expansion. +1. Enable HTTP API in `Settings -> TaskNotes -> Integrations -> HTTP API`. +2. Create at least one webhook (settings UI or API). +3. Subscribe it to one or more events. -## Webhook Management Interface +## Event Types -TaskNotes provides a modern, intuitive interface for managing webhooks: +Task events: -### Settings Interface +- `task.created` +- `task.updated` +- `task.deleted` +- `task.completed` +- `task.archived` +- `task.unarchived` -- **Card-based layout** - Each webhook displayed as a clean card with clear status indicators -- **Visual status indicators** - Active webhooks show green checkmarks, inactive show red X -- **Real-time statistics** - Success and failure counts with color-coded icons -- **Action buttons** - Enable/disable and delete webhooks with confirmation dialogs +Time events: -### Adding Webhooks +- `time.started` +- `time.stopped` -The webhook creation modal provides: +Pomodoro events: -- **Event selection** - Choose from all available events with descriptions -- **Transform configuration** - Optional JavaScript/JSON file specification -- **Headers control** - Toggle custom headers for strict CORS services -- **Validation** - Real-time URL and event validation +- `pomodoro.started` +- `pomodoro.completed` +- `pomodoro.interrupted` -### Status Monitoring +Recurring events: -Each webhook card displays: +- `recurring.instance.completed` +- `recurring.instance.skipped` -- **Connection status** - Visual indicators for active/inactive state -- **Success metrics** - Green checkmark with success count -- **Failure metrics** - Red X with failure count -- **Transform info** - Shows configured transformation file -- **CORS status** - Warning when custom headers are disabled +Reminder events: -### Accessibility Features +- `reminder.triggered` -The webhook interface is designed for accessibility: +## Payload Shape -- **Semantic icons** - Uses Obsidian's icon system instead of emoji characters -- **Descriptive labels** - Clear ARIA labels for screen readers -- **Keyboard navigation** - Full keyboard support for all interactions -- **High contrast** - Status indicators use color-coded backgrounds with icons -- **Tooltips** - Helpful hover text for all action buttons -- **Focus states** - Clear focus indicators for keyboard users - -## Webhook Events - -TaskNotes triggers webhooks for the following events: -Event selection controls downstream load and payload volume. - -### Task Events - -- `task.created` - When a new task is created -- `task.updated` - When a task is modified -- `task.deleted` - When a task is removed -- `task.completed` - When a task status changes to completed -- `task.archived` - When a task is archived -- `task.unarchived` - When a task is unarchived - -### Time Tracking Events - -- `time.started` - When time tracking starts on a task -- `time.stopped` - When time tracking stops on a task - -### Pomodoro Events - -- `pomodoro.started` - When a pomodoro session begins -- `pomodoro.completed` - When a pomodoro session finishes successfully -- `pomodoro.interrupted` - When a pomodoro session is interrupted - -### Recurring Task Events - -- `recurring.instance.completed` - When a recurring task instance is marked complete - -### Reminder Events - -- `reminder.triggered` - When a task reminder fires and displays a notification - -## Payload Structure - -All webhook payloads follow this structure: +All webhook payloads use the same top-level envelope: ```json { "event": "task.created", - "timestamp": "2024-03-15T14:30:00.000Z", + "timestamp": "2026-02-21T10:30:00.000Z", "vault": { "name": "My Vault", - "path": "/Users/username/Documents/MyVault" + "path": "/path/to/vault" }, - "data": { - "task": { - "id": "path/to/task.md", - "title": "Review PR #123", - "status": "todo", - "priority": "high", - "due": "2024-03-16", - "scheduled": null, - "path": "path/to/task.md", - "archived": false, - "tags": ["123"], - "contexts": ["work"], - "projects": ["[[Project Name]]"], - "timeEstimate": 60 - } - } -} -``` -The stable envelope (`event`, `timestamp`, `vault`, `data`) supports event-based routing with selective field parsing. - -## Event-Specific Payloads - -### Task Created/Updated/Deleted - -```json -{ - "event": "task.created", - "data": { - "task": { /* TaskInfo object */ } - } + "data": {} } ``` -### Task Updated (includes previous state) +`data` is event-specific. -```json -{ - "event": "task.updated", - "data": { - "task": { /* Current TaskInfo */ }, - "previous": { /* Previous TaskInfo */ } - } -} -``` +## Register and Manage Webhooks via API -### Time Tracking Events +### Create -```json -{ - "event": "time.started", - "data": { - "task": { /* TaskInfo object */ }, - "session": { - "startTime": "2024-03-15T14:30:00.000Z", - "endTime": null, - "description": null - } - } -} -``` +`POST /api/webhooks` -### NLP Task Creation +Required fields: -```json -{ - "event": "task.created", - "data": { - "task": { /* TaskInfo object */ }, - "source": "nlp", - "originalText": "Review PR #123 tomorrow high priority @work" - } -} -``` +- `url` (string) +- `events` (non-empty array) -### Reminder Events +Optional fields: -```json -{ - "event": "reminder.triggered", - "data": { - "task": { /* TaskInfo object */ }, - "reminder": { - "id": "rem_1234", - "type": "relative", - "relatedTo": "due", - "offset": "-PT15M", - "description": "Don't forget this important task!" - }, - "notificationTime": "2024-03-15T14:15:00.000Z", - "message": "Don't forget this important task!", - "notificationType": "system" - } -} -``` +- `id` +- `secret` (auto-generated if omitted) +- `active` (default `true`) +- `transformFile` +- `corsHeaders` (default `true`) -## Security - -### Webhook Signatures - -TaskNotes signs all webhook payloads using HMAC-SHA256. Verify signatures to ensure authenticity: -Validate signatures before business logic or database writes. - -**Headers:** - -- `X-TaskNotes-Event`: Event type (e.g., "task.created") -- `X-TaskNotes-Signature`: HMAC signature (hex-encoded) -- `X-TaskNotes-Delivery-ID`: Unique delivery ID - -**Verification (Node.js):** - -```javascript -const crypto = require('crypto'); - -function verifyWebhook(payload, signature, secret) { - const expectedSignature = crypto - .createHmac('sha256', secret) - .update(JSON.stringify(payload)) - .digest('hex'); - - return signature === expectedSignature; -} - -// Express.js example -app.post('/webhook', express.json(), (req, res) => { - const signature = req.headers['x-tasknotes-signature']; - const isValid = verifyWebhook(req.body, signature, process.env.WEBHOOK_SECRET); - - if (!isValid) { - return res.status(401).send('Invalid signature'); - } - - // Process webhook... - console.log('Event:', req.body.event); - res.status(200).send('OK'); -}); -``` - -**Verification (Python):** - -```python -import hmac -import hashlib -import json - -def verify_webhook(payload, signature, secret): - expected = hmac.new( - secret.encode('utf-8'), - json.dumps(payload).encode('utf-8'), - hashlib.sha256 - ).hexdigest() - return signature == expected - -# Flask example -from flask import Flask, request - -@app.route('/webhook', methods=['POST']) -def handle_webhook(): - signature = request.headers.get('X-TaskNotes-Signature') - if not verify_webhook(request.json, signature, WEBHOOK_SECRET): - return 'Invalid signature', 401 - - event = request.json['event'] - # Process webhook... - return 'OK' -``` - -## Integration Examples - -### Todoist Sync - -```javascript -app.post('/webhook/tasknotes', (req, res) => { - const { event, data } = req.body; - - if (event === 'task.created') { - // Create task in Todoist - await todoist.createTask({ - content: data.task.title, - due_date: data.task.due, - priority: mapPriority(data.task.priority), - project_id: getProjectId(data.task.projects[0]) - }); - } - - res.status(200).send('OK'); -}); -``` - -### Slack Notifications - -```javascript -app.post('/webhook/tasknotes', (req, res) => { - const { event, data } = req.body; - - if (event === 'task.completed') { - slack.chat.postMessage({ - channel: '#productivity', - text: `✅ Task completed: ${data.task.title}` - }); - } - - res.status(200).send('OK'); -}); -``` - -### Time Tracking Integration - -```javascript -app.post('/webhook/tasknotes', (req, res) => { - const { event, data } = req.body; - - if (event === 'time.started') { - // Start timer in external service - await toggl.startTimer({ - description: data.task.title, - project: data.task.projects[0] - }); - } else if (event === 'time.stopped') { - await toggl.stopTimer(); - } - - res.status(200).send('OK'); -}); -``` - -### Reminder Notifications - -```javascript -app.post('/webhook/tasknotes', (req, res) => { - const { event, data } = req.body; - - if (event === 'reminder.triggered') { - // Forward reminder to mobile app via push notification - await pushNotification.send({ - title: 'TaskNotes Reminder', - body: data.message, - data: { - taskId: data.task.id, - taskTitle: data.task.title, - reminderType: data.reminder.type - } - }); - - // Or send to smart home system - await homeAssistant.notify({ - message: data.message, - service: 'mobile_app_phone' - }); - } - - res.status(200).send('OK'); -}); -``` - -### Analytics Dashboard - -```javascript -app.post('/webhook/tasknotes', (req, res) => { - const { event, data, vault } = req.body; - - // Store event in database - await db.events.create({ - vault_name: vault.name, - event_type: event, - task_id: data.task?.id, - timestamp: new Date(), - metadata: data - }); - - // Emit to real-time dashboard - io.emit('task-update', { event, data }); - - res.status(200).send('OK'); -}); -``` - -## Webhook Management - -### Register Webhook (via API) +Example: ```bash curl -X POST http://localhost:8080/api/webhooks \ -H "Content-Type: application/json" \ - -H "Authorization: Bearer YOUR_API_TOKEN" \ -d '{ - "url": "https://your-service.com/webhook", - "events": ["task.created", "task.completed"] + "url": "https://example.com/tasknotes", + "events": ["task.completed", "task.created"], + "transformFile": "TaskNotes/webhooks/slack.json" }' ``` -### List Webhooks +### List -```bash -curl http://localhost:8080/api/webhooks \ - -H "Authorization: Bearer YOUR_API_TOKEN" +`GET /api/webhooks` + +Returns registered hooks. Secrets are not returned. + +### Delete + +`DELETE /api/webhooks/:id` + +### Delivery History + +`GET /api/webhooks/deliveries` + +Returns last 100 in-memory deliveries. + +## Delivery Behavior + +Current implementation behavior: + +- Webhook processing is asynchronous (fire-and-forget from task operation). +- Each delivery starts with one attempt. +- Failed deliveries retry with exponential backoff: `1s`, `2s`, `4s`. +- Maximum retries: `3` retries after initial attempt (up to 4 attempts total). +- If cumulative `failureCount` for a webhook exceeds `10`, webhook is auto-disabled. +- No explicit request timeout is set in delivery fetch. + +Because retries and duplicates are possible, handlers should be idempotent. + +## Signature Verification + +When `corsHeaders` is enabled (default), delivery includes: + +- `X-TaskNotes-Event` +- `X-TaskNotes-Signature` +- `X-TaskNotes-Delivery-ID` + +Signature is HMAC-SHA256 over `JSON.stringify(payload)` using webhook `secret`. + +Node.js example: + +```javascript +const crypto = require("crypto"); + +function verifyWebhook(payload, signature, secret) { + const expected = crypto + .createHmac("sha256", secret) + .update(JSON.stringify(payload)) + .digest("hex"); + return signature === expected; +} ``` -### Delete Webhook - -```bash -curl -X DELETE http://localhost:8080/api/webhooks/WEBHOOK_ID \ - -H "Authorization: Bearer YOUR_API_TOKEN" -``` - -### View Delivery History - -```bash -curl http://localhost:8080/api/webhooks/deliveries \ - -H "Authorization: Bearer YOUR_API_TOKEN" -``` - -## Reliability Features - -### Automatic Retries - -- Failed deliveries retry 3 times with exponential backoff (1s, 2s, 4s) -- Webhooks automatically disabled after 10+ consecutive failures -Handlers should be idempotent because retries and duplicate deliveries can occur. - -### Delivery Tracking - -- Each delivery gets a unique ID for tracking -- Success/failure counts maintained per webhook -- Recent delivery history available via API - -### Error Handling - -- 10-second timeout per delivery attempt -- HTTP status codes tracked for debugging -- Failed webhooks don't block TaskNotes operations - ## Payload Transformations -### Transform Files +A webhook can specify `transformFile` pointing to a file in your vault. -TaskNotes supports custom payload transformations using JavaScript or JSON template files stored in your vault. This allows you to adapt webhook payloads to match the specific format required by different services (Discord, Slack, custom APIs, etc.). -JSON templates support direct field mapping; JavaScript transforms support conditional logic and event-specific formatting. +Supported file types: -📁 **[View Transform Examples](examples/webhook-transforms/)** - Ready-to-use transform files for Discord, Slack, Teams, and more! +- `.js`: JavaScript transform function +- `.json`: event template map with variable interpolation -#### How Transform Files Work +If transform execution fails, TaskNotes falls back to original payload. -1. **File Location**: Transform files must be stored in your Obsidian vault -2. **File Types**: Supports `.js` (JavaScript) and `.json` (JSON template) files -3. **Execution**: Files are read and executed when a webhook is triggered -4. **Safety**: JavaScript files run in a controlled context for security -5. **Error Handling**: Failed transformations fall back to original payload +### JavaScript Transform Files -#### JavaScript Transformations +Expected format: -JavaScript files provide maximum flexibility for complex transformations. The file must define a `transform` function that receives the webhook payload and returns the transformed data. - -**Basic Structure:** ```javascript function transform(payload) { - // Your transformation logic here - return transformedPayload; + return payload; } ``` -**Complete Discord Example:** +Execution model: + +- File is read from the vault. +- Code is executed with `new Function(...)`. +- Returned value becomes the outgoing request body. + +Important behavior: + +- Returning `null` does not skip delivery. Payload body becomes JSON `null`. +- Returning an array does not fan out to multiple URLs. Array is posted to the configured single URL. +- `console.log` is available because code runs in plugin runtime context. + +Example: + ```javascript -// discord-webhook.js - Transform for Discord webhook format function transform(payload) { - const { event, data, timestamp, vault } = payload; - - // Handle different event types - if (event === 'task.completed') { + if (payload.event === "task.completed") { return { - embeds: [{ - title: "✅ Task Completed", - description: data.task.title, - color: 5763719, // Green color - fields: [ - { - name: "Priority", - value: data.task.priority || "Normal", - inline: true - }, - { - name: "Project", - value: data.task.projects?.[0] || "None", - inline: true - }, - { - name: "Due Date", - value: data.task.due || "Not set", - inline: true - } - ], - footer: { - text: `From ${vault.name}`, - icon_url: "https://obsidian.md/favicon.ico" - }, - timestamp: timestamp - }] - }; - } else if (event === 'task.created') { - return { - embeds: [{ - title: "📝 New Task Created", - description: data.task.title, - color: 3447003, // Blue color - fields: [ - { - name: "Status", - value: data.task.status, - inline: true - }, - { - name: "Priority", - value: data.task.priority || "Normal", - inline: true - } - ], - timestamp: timestamp - }] - }; - } else if (event === 'pomodoro.completed') { - return { - embeds: [{ - title: "🍅 Pomodoro Completed", - description: `Finished working on: ${data.task.title}`, - color: 15158332, // Red color - timestamp: timestamp - }] + text: `Completed: ${payload.data.task.title}`, + event: payload.event, + at: payload.timestamp, }; } - - // For unhandled events, return a generic message - return { - content: `TaskNotes: ${event} event triggered` - }; + + return payload; } ``` -**Slack Example:** -```javascript -// slack-webhook.js - Transform for Slack webhook format -function transform(payload) { - const { event, data, vault } = payload; - - if (event === 'task.completed') { - return { - text: `Task completed: ${data.task.title}`, - blocks: [ - { - type: "section", - text: { - type: "mrkdwn", - text: `✅ *Task Completed*\n${data.task.title}` - } - }, - { - type: "context", - elements: [ - { - type: "mrkdwn", - text: `Priority: ${data.task.priority || 'Normal'} | Vault: ${vault.name}` - } - ] - } - ] - }; - } - - return { - text: `TaskNotes: ${event} in ${vault.name}` - }; -} -``` +### JSON Transform Files -#### JSON Templates +Structure: -JSON templates provide a simpler way to transform payloads using variable substitution. Templates can define different formats for different events. - -**Template Structure:** -```json -{ - "event-name": { /* Template for specific event */ }, - "default": { /* Fallback template for all other events */ } -} -``` - -**Variable Syntax:** -- Use `${path.to.value}` to insert values from the payload -- Supports nested object access (e.g., `${data.task.title}`) -- Variables that don't exist remain as literal text - -**Slack Template Example:** ```json { "task.completed": { "text": "Task completed: ${data.task.title}", - "channel": "#tasks", - "username": "TaskNotes", - "icon_emoji": ":white_check_mark:", - "attachments": [ - { - "color": "good", - "fields": [ - { - "title": "Priority", - "value": "${data.task.priority}", - "short": true - }, - { - "title": "Project", - "value": "${data.task.projects.0}", - "short": true - } - ] - } - ] - }, - "task.created": { - "text": "New task: ${data.task.title}", - "channel": "#tasks", - "username": "TaskNotes", - "icon_emoji": ":memo:" + "vault": "${vault.name}" }, "default": { - "text": "TaskNotes event: ${event}", - "channel": "#general", - "username": "TaskNotes" + "text": "TaskNotes event: ${event}" } } ``` -**Teams Template Example:** -```json -{ - "task.completed": { - "@type": "MessageCard", - "@context": "http://schema.org/extensions", - "themeColor": "28a745", - "summary": "Task Completed", - "sections": [ - { - "activityTitle": "✅ Task Completed", - "activitySubtitle": "${data.task.title}", - "facts": [ - { - "name": "Priority:", - "value": "${data.task.priority}" - }, - { - "name": "Vault:", - "value": "${vault.name}" - } - ] - } - ] - }, - "default": { - "@type": "MessageCard", - "@context": "http://schema.org/extensions", - "summary": "TaskNotes Event", - "text": "Event ${event} triggered in ${vault.name}" - } -} -``` +Rules: -#### Advanced JavaScript Examples +- Matching order: exact event key, then `default`. +- Variables use `${path.to.value}`. +- Missing variables are left unchanged. -**Conditional Logic:** -```javascript -function transform(payload) { - const { event, data } = payload; - - // Only process high priority tasks - if (data.task && data.task.priority !== 'high') { - return null; // Return null to skip webhook delivery - } - - // Custom logic based on task properties - if (data.task.tags && data.task.tags.includes('urgent')) { - return { - priority: "high", - message: `🚨 URGENT: ${data.task.title}`, - event: event - }; - } - - return payload; // Return original -} -``` +## CORS and Headers -**Data Enrichment:** -```javascript -function transform(payload) { - const { event, data } = payload; - - // Add computed fields - const enrichedPayload = { - ...payload, - computed: { - isOverdue: data.task.due && new Date(data.task.due) < new Date(), - hasProject: data.task.projects && data.task.projects.length > 0, - estimatedMinutes: data.task.timeEstimate || 0, - daysSinceDue: data.task.due ? - Math.floor((new Date() - new Date(data.task.due)) / (1000 * 60 * 60 * 24)) : null - } - }; - - return enrichedPayload; -} -``` +Webhook delivery requests are outbound server-side requests from TaskNotes. -**Multi-Service Routing:** -```javascript -function transform(payload) { - const { event, data } = payload; - - // Return array to send to multiple endpoints - const results = []; - - // Always log to analytics service - results.push({ - service: "analytics", - event: event, - task_id: data.task.id, - timestamp: new Date().toISOString() - }); - - // Send high priority tasks to alert channel - if (data.task.priority === 'high') { - results.push({ - service: "alerts", - text: `High priority task: ${data.task.title}`, - urgency: "high" - }); - } - - return results; -} -``` +`corsHeaders` behavior: -#### Security Considerations +- `true` (default): sends TaskNotes custom headers including signature. +- `false`: only sends `Content-Type: application/json`. -- **Sandboxed Execution**: JavaScript files run in a controlled context -- **No Node.js APIs**: Transform functions cannot access file system, network, or other Node.js APIs -- **Error Isolation**: Transform errors don't affect TaskNotes or other webhooks -- **Input Validation**: Always validate payload structure in your transform function +Disable custom headers for endpoints that reject non-standard headers. -#### Debugging Transform Files +## Testing -**Console Logging:** -Transform functions cannot use `console.log()`, but you can return debug information: +### Local Test Server -```javascript -function transform(payload) { - try { - // Your transformation logic - const result = { /* transformed data */ }; - - return { - ...result, - _debug: { - originalEvent: payload.event, - transformedAt: new Date().toISOString() - } - }; - } catch (error) { - // Return error info in payload for debugging - return { - error: error.message, - originalPayload: payload - }; - } -} -``` - -**Testing Strategy:** -1. Start with a simple transform that returns the original payload -2. Add small changes incrementally -3. Use webhook.site or the built-in test server to inspect outputs -4. Check TaskNotes console for transformation errors - -### Headers Configuration - -TaskNotes includes custom headers by default: - -- `X-TaskNotes-Event`: Event type -- `X-TaskNotes-Signature`: HMAC signature -- `X-TaskNotes-Delivery-ID`: Unique delivery ID - -For services with strict CORS policies (Discord, Slack), disable custom headers in webhook settings. - -## Testing Webhooks - -### Built-in Test Server - -TaskNotes includes a comprehensive test server for webhook development: +Use repository script: ```bash -# Navigate to TaskNotes directory node test-webhook.js +``` -# Or specify custom port +Optional custom port: + +```bash node test-webhook.js 8080 ``` -The test server provides: +Default server values: -- **Real-time payload inspection** with formatted output -- **Signature verification** using configurable test secret -- **Event-specific processing** with detailed logging -- **CORS support** for browser-based testing -- **Health check endpoint** at `/health` +- URL: `http://localhost:3000/webhook` +- Test secret: `test-secret-key-for-tasknotes-webhooks` -#### Configuration +### External Inspection -Use the test secret when adding the webhook: - -``` -URL: http://localhost:3000/webhook -Secret: test-secret-key-for-tasknotes-webhooks -``` - -### Local Testing with ngrok - -```bash -# Install ngrok -npm install -g ngrok - -# Expose local server -ngrok http 3000 - -# Use the ngrok URL in webhook settings -# https://abc123.ngrok.io/webhook -``` - -### Webhook Testing Service - -Use services like [webhook.site](https://webhook.site) for quick testing: - -1. Go to webhook.site and copy your unique URL -2. Add it as a webhook in TaskNotes -3. Perform actions to trigger events -4. View payloads in real-time on webhook.site - -## Best Practices - -### Security - -- Always verify webhook signatures -- Use HTTPS endpoints in production -- Store webhook secrets securely -- Validate payload structure before processing - -### Performance - -- Process webhooks asynchronously -- Return 200 status quickly to avoid retries -- Use queuing for heavy processing -- Implement idempotency using delivery IDs - -### Reliability - -- Handle duplicate deliveries gracefully -- Log webhook events for debugging -- Monitor webhook failures -- Set up alerts for disabled webhooks - -### Integration - -- Subscribe only to needed events -- Filter events in your handler -- Batch related operations when possible -- Use webhook data to trigger workflows, not as primary data source +For quick inspection, use a request-bin tool such as `webhook.site`. ## Troubleshooting -### Common Issues +### No deliveries -**Webhook not triggered:** +1. Confirm HTTP API is enabled. +2. Confirm webhook is active. +3. Confirm event is subscribed. -- Verify webhook is active in settings -- Check event subscription includes the triggered event -- Ensure HTTP API is enabled and running +### Signature mismatch -**Signature verification fails:** +1. Confirm secret matches webhook config. +2. Verify your verifier hashes the exact JSON body. +3. Verify hex digest comparison. -- Confirm secret matches webhook configuration -- Check payload serialization (use exact JSON string) -- Verify HMAC calculation implementation +### Webhook disabled automatically -**Timeouts:** +If failures continue and `failureCount` exceeds 10, webhook is disabled. -- Optimize endpoint response time -- Return 200 status before heavy processing -- Use async processing for complex operations +1. Fix endpoint availability or response handling. +2. Re-enable webhook in TaskNotes settings, or recreate it via `POST /api/webhooks`. -**High failure count:** +### Transform errors -- Check endpoint availability -- Verify URL and network connectivity -- Review server logs for error details - -### Debug Mode - -Enable debug logging by setting webhook events to verbose: - -```javascript -// In your webhook handler -console.log('Headers:', req.headers); -console.log('Body:', req.body); -console.log('Signature verification:', isValidSignature); -``` - -### Monitoring and Debugging - -The improved webhook interface provides better debugging tools: - -- **Visual status indicators** - Quickly identify inactive or failing webhooks -- **Success/failure counts** - Monitor webhook health at a glance -- **Card-based layout** - Easy scanning of multiple webhook configurations -- **Transform file status** - Clear indication when payload transformations are active -- **CORS warnings** - Visual alerts when custom headers are disabled - -### Support - -For webhook-related issues: -1. Check webhook status indicators in the settings interface -2. Monitor success/failure counts for each webhook -3. Verify endpoint accessibility with the built-in test server -4. Test with webhook.site for quick validation -5. Review TaskNotes console logs for detailed error information -6. Use the included test server for local development and debugging +1. Confirm `transformFile` exists in vault. +2. For JS, ensure `transform` function is defined. +3. Check Obsidian console logs for transform exceptions. diff --git a/mkdocs.yml b/mkdocs.yml index b071435f..81f5c744 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -62,7 +62,6 @@ nav: - Views: - Overview: views.md - Task List View: views/task-list.md - - Notes View: views/notes-view.md - Agenda View: views/agenda-view.md - Kanban View: views/kanban-view.md - Calendar Views: views/calendar-views.md @@ -73,12 +72,15 @@ nav: - General: settings/general.md - Features: settings/features.md - Task Properties: settings/task-properties.md + - Modal Fields: settings/modal-fields.md + - Property Identification: settings/property-identification.md - Property Types Reference: settings/property-types-reference.md - Defaults & Templates: settings/defaults.md - Task Defaults: settings/task-defaults.md - Appearance & UI: settings/appearance.md - Inline Task Settings: settings/inline-task-settings.md - Calendar Settings: settings/calendar-settings.md + - Misc Settings: settings/misc-settings.md - Integrations: settings/integrations.md - Advanced Settings: settings/advanced-settings.md - APIs & Integration: