Links
Project Idea
GitHub Profile
Forum Introduction Post
Pull Requests Submitted to Joplin
- CLI: Add `clear` command to clear console output by querysmith-sys · Pull Request #14844 · laurent22/joplin · GitHub
- Desktop: fix layout overlap when resizing the editor to the left by querysmith-sys · Pull Request #14916 · laurent22/joplin · GitHub
Other Relevant Development Experience
1. Introduction
I am Dipok, from North-East India currently pursuing a Bachelor's degree in Computer Science.
I have programming experience in Python, GO, TypeScript, JavaScript, SQL, and NoSQL, HTML, CSS.
I actively read and study open-source codebases and related documentation to understand how real-world software systems are designed and implemented.
I am interested in productivity tools, which led me to explore open-source projects in this space and contribute to Joplin. my contributions to Joplin (#14844, #14916) gave me hands-on familiarity with the codebase, its structure, and contribution workflow. while these contributions are not directly related to this project idea, they gave me a clear understanding of how the codebase is organized, how plugins are structured, and how contributions are expected to be written. additionally, i studied the source code of the outline plugin, which shares architectural similarities with what i am building — particularly around sidebar panel creation and webview communication — and this has directly informed my technical approach for this project.
2. Project Summary
many users write tasks directly inside notes using markdown checkboxes, for example:
- [ ] solve assignment
- [ ] submit report
when notes are spread across different notebooks, these tasks become difficult to track because users must open each note individually to see what is still pending. joplin provides to-do notes, but tasks written inside regular notes are not aggregated in one place.
this project proposes a task overview plugin for joplin that scans notes, extracts checkbox tasks, and displays them in a centralized dashboard panel.
plugin features:
- extract checkbox tasks from notes
- display all tasks in a sidebar panel similar to the existing outline plugin ui
- separate pending and completed tasks
- detect simple deadlines written inside task text
- show a daily productivity summary (due today, overdue, upcoming)
- allow users to click a task and open the note where it was written
the expected outcome is a lightweight task dashboard that enables users to quickly view and manage tasks without manually searching through multiple notes.
out of scope for this project includes:
- advanced natural language task detection
- reminder or notification systems
3. Technical Approach
System Flow:
the plugin will follow a pipeline/orchestrator pattern where a central entry file manages the execution flow between modules.
the plugin entry point (index.ts) acts as the main orchestrator. It initializes the plugin and coordinates communication between the different components responsible for task extraction, deadline detection, and UI rendering.
The overall execution flow:
Plugin Start
↓
Note Retrieval
↓
Task Extraction
↓
Deadline Detection
↓
Task Storage
↓
Task Aggregation (Completion + Deadline-based grouping)
↓
Dashboard Rendering
the orchestrator coordinates this pipeline. each module processes its input and returns the result to the orchestrator , which then passes the processed data to the next module.
example flow:
- Note Retrieval module fetches notes using the Joplin Plugin API and returns the list of notes to the orchestrator .
- the orchestrator passes the note data to the Task Extraction module, which parses the notes and returns a list of detected tasks.
- the tasks are then passed to the Deadline Detection module, which analyzes task text and attaches deadline information where applicable.
- the processed task objects are stored and grouped before being displayed in the Task Overview panel.
This modular flow will keep the system easy to maintain and allows each component to focus on a single responsibility.
Architecture / Components
The project will be implemented as an external plugin using the Joplin Plugin API. The plugin will scan notes, extract checkbox tasks, detect deadlines, and display them in a centralized sidebar panel.
The system is organized into the following modules:
3.1. Plugin initialization (orchestrator)
the plugin entry file (src/index.ts) acts as the orchestrator of the system. it initializes the plugin when joplin loads it and coordinates how data flows between modules.
the plugin is registered using the joplin plugin entry pattern:
typescript
joplin.plugins.register({
onStart: async function() {
// initialize panel, register events, trigger initial scan
}
});
responsibilities:
- register the plugin using
joplin.plugins.register({ onStart }) - create the task overview sidebar panel
- register workspace event listeners
- trigger the initial task scan
the orchestrator receives outputs from each module and passes them to the next stage in the pipeline.
3.2. Note Retrieval module
this module retrieves notes using the joplin plugin data api.
implementation approach:
let page = 1;
let allNotes = [];
while (true) {
const result = await joplin.data.get(['notes'], {
fields: ['id', 'title', 'body'],
page: page,
});
allNotes = allNotes.concat(result.items);
if (!result.has_more) break;
page++;
}
the Joplin data API returns paginated results. the loop continues until has_more is false, ensuring all notes are retrieved regardless of vault size.
each note contains markdown content which will be analyzed for checkbox tasks.
the retrieved notes are returned to the orchestrator and passed to the task extraction module.
3.3 Task Extraction module
this module scans note content to detect markdown checkbox tasks.
implementation approach:
- split the note body into lines using
note.body.split('\n') - enumerate each line with its index to capture
line_number - track fenced code blocks using an
insideCodeBlockboolean flag - detect checkbox tasks using the regex pattern
/^- \[[ xX]\] (.+)/
the character class [ xX] handles pending ([ ]), completed lowercase ([x]), and completed uppercase ([X]) states. line_number is the 0-indexed position of the matched line in the split array, used later for task navigation in section 3.8.
fenced code block exclusion:
typescript
let insideCodeBlock = false;
const lines = note.body.split('\n');
lines.forEach((line, index) => {
if (line.startsWith('```')) {
insideCodeBlock = !insideCodeBlock;
return;
}
if (insideCodeBlock) return;
const match = line.match(/^- \[[ xX]\] (.+)/);
if (match) {
// construct task object
}
});
the insideCodeBlock flag toggles each time a ``` fence is encountered. lines between fences are skipped entirely, ensuring tasks written as code examples are not extracted as actionable tasks.
supported patterns:
- [ ] task -> pending
- [x] task -> completed
- [X] task -> completed
for each detected task the system extracts:
- task text
- completion status
- source note id
- source note title
- line number
these values are used to construct a task object.
example structure:
task {
text
completed
deadline
note_id
note_title
line_number
}
3.4 Deadline detection module
after tasks are extracted, the plugin analyzes the task text to detect deadline expressions.
deadline detection operates on tasks returned by the task extraction module and enriches each task object with optional deadline information.
implementation approach:
deadline detection uses chrono-node, a natural language date parsing library, instead of raw regex. this correctly handles natural language expressions and is locale-aware — avoiding the pitfalls of raw JS Date parsing.
supported formats include expressions such as by june 25th, due tomorrow, due next friday, and ISO format 2026-06-25 — handled automatically by chrono-node.
example: - [ ] submit report by june 25
typescript
const results = chrono.parse(taskText, new Date());
const deadline = results.length > 0 ? results[0].start.date() : null;
chrono.parse() returns an array of parsed date results. the first valid result is used as the deadline. if no date expression is found, the array is empty and deadline is set to null.
handling edge cases:
- if multiple date expressions are present in a task, the first valid match is used as the deadline.
- if the detected date does not include a year (e.g., "june 25"), the current year is assumed.
- if the detected date is in the past, it will be treated as overdue during summary computation.
- if the task text contains unrecognizable date expressions, they are ignored and no deadline is assigned.
detected deadlines are not used to update note titles, create tags, or trigger alarms — they are used solely to categorize tasks in the daily productivity summary as due today, upcoming, or overdue.
3.5 Task storage
extracted tasks are stored in an in-memory collection inside the plugin.
example:
tasks: task[]
this dataset is used for task aggregation and dashboard rendering.
3.6 Task aggregation
tasks are grouped based on completion state (completed field of the task object) for the task overview, and based on the deadline field of the task object for the daily summary.
the aggregation module groups tasks based on:
- completion state:
- pending
- completed
- deadline categories (for tasks with deadlines):
- due today
- upcoming
- overdue
implementation approach:
typescript
pendingTasks -> tasks.filter(t => t.completed === false)
completedTasks -> tasks.filter(t => t.completed === true)
for deadline-based grouping, deadlines are compared against the current date with time normalized to midnight to ensure day-level accuracy:
typescript
const today = new Date();
today.setHours(0, 0, 0, 0);
const deadlineDate = new Date(t.deadline);
deadlineDate.setHours(0, 0, 0, 0);
if (deadlineDate < today) -> overdue
if (deadlineDate.getTime() === today.getTime()) -> due today
if (deadlineDate > today) -> upcoming
time is normalized to midnight on both sides so that a task due "today" is not accidentally classified as overdue due to time-of-day differences. these counts are used to generate the daily productivity summary displayed in the dashboard.
3.7 Dashboard ui
the plugin creates a sidebar panel using the joplin views api:
typescript
const panelHandle = await joplin.views.panels.create('taskOverviewPanel');
the panel runs inside a sandboxed webview context, which is a separate process from the plugin's main thread (index.ts). because of this isolation, direct function calls between the panel ui and the plugin are not possible — communication is handled through a message bridge.
script injection:
a dedicated file src/webview.js is injected into the panel using:
typescript
await joplin.views.panels.addScript(panelHandle, './webview.js');
webview.js handles all dom manipulation and user interaction inside the panel — including rendering the task list and attaching click listeners to each task item. it runs entirely inside the webview and has no direct access to joplin apis.
message bridge (webview → plugin):
when a user clicks a task in the panel, webview.js sends a message to the main plugin thread using the webviewApi global object injected by joplin:
javascript
// inside webview.js
document.addEventListener('click', (event) => {
const taskItem = event.target.closest('[data-note-id]');
if (taskItem) {
webviewApi.postMessage({
type: 'openNote',
noteId: taskItem.dataset.noteId,
lineNumber: parseInt(taskItem.dataset.lineNumber),
});
}
});
index.ts registers a listener to receive these messages:
typescript
// inside index.ts
joplin.views.panels.onMessage(panelHandle, async (message) => {
if (message.type === 'openNote') {
await joplin.commands.execute('openNote', message.noteId);
}
});
panel updates (plugin → webview):
when the task list changes — due to note edits, creation, or checkbox toggles — the plugin regenerates the panel html and pushes it using:
typescript
await joplin.views.panels.setHtml(panelHandle, generatePanelHtml(tasks));
generatePanelHtml() is a typescript function that builds the html string from the current task dataset, embedding data-note-id and data-line-number attributes on each task element so webview.js can read them on click.
generatePanelHtml() structure:
function generatePanelHtml(tasks: Task[]): string {
const pending = tasks.filter(t => !t.completed);
const completed = tasks.filter(t => t.completed);
const taskRow = (t: Task) => `
<div class="task-item" data-note-id="${t.note_id}" data-line-number="${t.line_number}">
<span class="task-text">${t.text}</span>
<span class="task-source">${t.note_title}</span>
</div>`;
return `
<div id="task-overview">
<div class="summary">...</div>
<div class="section">
<h3>Pending</h3>
${pending.map(taskRow).join('')}
</div>
<div class="section">
<h3>Completed</h3>
${completed.map(taskRow).join('')}
</div>
</div>`;
}
responsibility split:
| thread | file | responsibility |
|---|---|---|
| plugin main thread | index.ts |
panel creation, onMessage handler, task data, setHtml updates |
| webview thread | webview.js |
dom rendering, click listeners, webviewApi.postMessage calls |
the panel displays:
- daily productivity summary
- pending tasks
- completed tasks
UI Example:
figure 1 — task overview panel integrated into the joplin layout
figure 2 — detailed view of the task overview panel
figure 3 — task navigation interaction
user interaction flow:
- the plugin automatically updates the sidebar when notes are created, edited, or checkbox states change
- users can view all extracted tasks in one place without opening individual notes
- clicking a task sends a message via
webviewApi.postMessageto the main thread, which opens the corresponding note usingjoplin.commands.execute('openNote', noteId)
3.8 Task navigation
each task object retains its source note_id and line_number (0-indexed), computed during extraction in section 3.3.
when a user clicks a task in the panel, webview.js sends the note_id and line_number via webviewApi.postMessage to the main thread. the onMessage handler in index.ts then opens the note and scrolls the editor to the exact task line:
typescript
await joplin.commands.execute('openNote', message.noteId);
await joplin.commands.execute('editor.execCommand', {
name: 'scrollToLine',
args: [message.lineNumber],
});
this enables users to jump directly to the task location inside the note without manually searching through its content.
3.9 Synchronization with note events
the plugin maintains a note-task cache — a Map<noteId, Task[]> — that stores the extracted tasks for each note. this allows the plugin to update only the affected note's tasks when a change occurs, without re-scanning the entire vault.
the following events trigger an update:
- plugin starts — full scan, populates the cache for all notes
- note is edited — only the changed note is re-parsed
- new note is created — the new note is scanned and added to the cache
- checkbox state is toggled — treated as a note body change, handled by
onNoteChange
to avoid redundant re-parses when a user is actively typing, the onNoteChange handler is wrapped in a 300ms debounce:
typescript
const debouncedUpdate = debounce(async (event) => {
const noteId = event.id;
const note = await joplin.data.get(['notes', noteId], {
fields: ['id', 'title', 'body'],
});
cache.delete(noteId);
const updatedTasks = extractTasks(note);
cache.set(noteId, updatedTasks);
await joplin.views.panels.setHtml(panelHandle, generatePanelHtml([...cache.values()].flat()));
}, 300);
await joplin.workspace.onNoteChange(debouncedUpdate);
when onNoteChange fires, the orchestrator:
- retrieves the updated note using its
id - removes the old task entries for that
noteIdfrom the cache - re-runs task extraction and deadline detection on the updated note
- reinserts the new task entries into the cache
- refreshes the dashboard panel
this keeps the task overview synchronized with the user's notes without requiring a full re-scan of all notes each time.
Changes to the Joplin codebase
this project will be implemented as an external plugin using the Joplin Plugin API, therefore it does not require modifications to the core Joplin codebase.
the plugin will interact with Joplin through the official plugin interfaces, including:
joplin.datafor retrieving note contentjoplin.views.panelsfor creating the task overview sidebar paneljoplin.workspaceevents for detecting note updates
because the feature is implemented as a plugin, it can be installed, updated, or removed without affecting the core application.
Libraries or Technologies
i will be using the standard joplin plugin development stack:
- typescript for plugin implementation
- joplin plugin api for interacting with notes and ui
- regular expressions for detecting checkbox tasks
- chrono-node for natural language deadline parsing
- html/css for rendering the task overview panel
- webpack for bundling the plugin and its dependencies (including chrono-node) into a single distributable package, as required by the joplin plugin build system
Potential Challenges
- efficient note scanning scanning all notes repeatedly can affect performance for users with many notes. the plugin will perform a full scan only during initialization and rely on event-driven updates afterwards.
- detecting note changes efficiently the plugin must detect when notes change without reprocessing everything.
joplin.workspace.onNoteChangewill be used to detect when a note is edited, created, or a checkbox state changes, and only the affected note will be re-parsed using the note-task cache described in section 3.9. - deadline parsing reliability users may write deadlines in different formats. the plugin uses chrono-node to handle natural language date expressions such as
june 25th,next friday, and2026-06-25, making parsing reliable and locale-aware without manually maintaining regex patterns. - ui performance during updates when tasks change frequently, the dashboard should remain responsive. the implementation will update only affected tasks and refresh the panel dynamically instead of rebuilding the entire dataset.
Testing Strategy
- unit tests for core modules
individual modules such as task extraction, deadline detection, and task aggregation will be tested independently to ensure their outputs are correct for different inputs. - integration tests for the processing pipeline
integration tests will verify the full flow from note retrieval → task extraction → deadline detection → task aggregation to ensure modules work correctly together. - test cases for markdown task parsing
tests will include different checkbox patterns such as- [ ]and- [x], as well as notes containing multiple tasks. - deadline detection tests
test cases will validate that chrono-node correctly parses natural language deadline expressions such asby june 25th,due tomorrow,due next friday, and ISO format2026-06-25. - manual testing for plugin behavior
manual tests will verify the plugin inside the Joplin UI, including dashboard rendering, task updates when notes change, and navigation from tasks to source notes. - event-driven update testing
tests will confirm that when a note is edited, created, or a checkbox state changes, only the affected note is re-parsed and the dashboard updates correctly.
Documentation plan
- user documentation
documentation will explain how the plugin works and how users can write tasks that the system can detect. this includes the supported checkbox formats (- [ ],- [x]) and examples of deadline expressions such asdue tomorrow,by june 25th, or2026-06-25. - usage examples
examples will be provided showing how tasks written inside notes appear in the task overview panel and how users can navigate back to the source note. - developer documentation
documentation will describe the plugin architecture, the orchestrator/pipeline execution flow, and the responsibilities of each module. - contribution guide
guidelines will explain how developers can extend the plugin, for example by adding new deadline detection patterns, improving task parsing logic, or extending UI features. - setup instructions
steps for building and running the plugin locally will be provided so contributors can easily understand the codebase and start contributing.
4. Implementation Plan
Week 1–2 — Plugin Setup + Note Retrieval
- plugin skeleton (
index.ts) - sidebar panel creation
- fetch all notes using
joplin.data, paginated viahasMoreloop - debounce note change handlers (300ms) to avoid redundant re-parses during active editing
Result: plugin loads, all notes retrieved correctly regardless of vault size
Week 3–4 — Task Extraction
- detect markdown checkbox tasks
- create task objects
- write unit tests for parsing
Result: tasks extracted accurately from all notes
Week 5–6 — Deadline Detection + Basic Dashboard
- deadline detection using chrono-node (handles
June 25th,next Friday, locale-aware) - task grouping (pending / completed)
- render tasks in sidebar panel
Result: panel renders grouped tasks with detected deadlines
Milestone: A working panel displays extracted tasks from all notes with pending/completed grouping and basic deadline detection.
Week 7–8 — Event-Driven Updates
- workspace event listeners using
onNoteChange - maintain a note-task cache keyed by note ID — only re-parse the changed note
- update dashboard dynamically without full rescans
Result: dashboard updates in real-time on note edits
Week 9–10 — Task Navigation + Summary
- clicking a task opens the source note and scrolls to it
- daily productivity summary (due today / overdue / upcoming counts)
Result: clicking a task opens the source note; summary shows due/overdue counts
Week 11–12 — Polish + Stretch Goals
- integration tests
- performance improvements
- documentation + bug fixes
- Stretch: tag-based filtering, settings UI, keyboard shortcuts
Note: timeline may be adjusted slightly based on mentor feedback and development progress.
5. Deliverables
Implemented Features
- a task overview plugin for Joplin
- a sidebar task panel integrated into the right side of the joplin interface
- automatic extraction of markdown checkbox tasks from notes
- global task overview showing tasks across all notes
- grouping of tasks into pending and completed
- deadline detection from task text using supported patterns
- daily productivity summary (due today, overdue, upcoming)
- task navigation, allowing users to open the source note directly from the task panel
- event-based updates so the task panel refreshes when notes change
Tests
- unit tests for task extraction and deadline detection modules
- integration tests for the full pipeline (note retrieval → task extraction → dashboard rendering)
Documentation
- user documentation describing how to use the task overview panel and supported task formats
- developer documentation explaining plugin architecture and module responsibilities
- contribution guidelines for extending parsing rules or adding new features
6. Availability
I will dedicate 30–35 hours/week to GSoC. While I have university coursework during parts of the program, I am fully committed to meeting every weekly milestone regardless — I will adjust my schedule as needed to ensure no deliverable is delayed. I have no internship or part-time job during this period. I will respond within 24 hours on GitHub and the Joplin forum, and will proactively post weekly progress updates.
My time zone is IST (UTC+5:30).


