Repository navigation
Built in Extensions
Four extensions ship inside the app, in
builtin-extensions/.
They use the same API as anyone else's, so they are the best worked examples.
A devlog enables one with "<name>": "builtin" in devlog.json; it still
asks before it starts, and keeps its grant when the app updates it.
| Extension | Id | Version | API | Runs | Read it for |
|---|---|---|---|---|---|
| devlog-time | builtin.devlog-time |
1.1.0 | ^1.7.0 |
sandboxed | Almost everything: node types, commands in menus, keys and the note box, every view placement, a managed canvas, providing time, sending through destinations. TypeScript + @devlog/ui. |
| devlog-focus | builtin.devlog-focus |
1.1.0 | ^1.2.0 |
unrestricted | A small unrestricted extension with a platform helper, per-machine JSON-lines files, providing focus. |
| devlog-jira | builtin.devlog-jira |
1.1.0 | ^1.3.0 |
sandboxed | A complete destination: settings, a secret, a canvas field, a check command, a ledger, fetch. |
| devlog-cms | builtin.devlog-cms |
1.0.1 | ^1.3.0 |
sandboxed | A destination that drives a web app with no API (form login, HTML parsing). |
A built-in written in plain JavaScript is a folder with
devlog-extension.json and main.js. One written in TypeScript keeps its
sources in src/, and scripts/build-builtins.mjs (run by npm run build,
dev, package, smoke and screens) bundles:
-
src/main.ts→dist/main.js(CommonJS for Node 20, Node built-ins only); - each
src/views/<view>.tsx→dist/views/<view>.{html,js,css}(one IIFE script and one stylesheet, React and@devlog/uiincluded).
dist/ is not committed and src/ is not packaged. The same esbuild
settings work for an extension of your own.
Everything about time: without it there is no task type, no Start button, no Timesheet or Summary, and nothing about time is recorded.
| Piece | API used |
|---|---|
The task node type (◉, "posting here makes it the active task") |
contributes.nodeTypes |
The clock: one active task, paused while locked, idle or asleep; start/task/stop and a heartbeat every five minutes in extensions/builtin.devlog-time/<machine>/… (or on the machine only) |
activity.on, activity.idleAfter, files.repo / files.local, provide.activity
|
| Posting on a task starts it | devlog.onBlockAdded |
| Status bar item, task picker popover, Start/Stop in task headers |
views (statusbar, popover, canvasHeader with nodeType: task), views.handle / post
|
Stop (Mod+Shift+., tray), Start a task… (tray), Start this task (canvas menu on tasks) |
commands with keybinding, menus, nodeType
|
Post as task (Mod+Shift+Enter, #task), Make task on a block |
commands with post/tag and menus: ["block"]; devlog.promote
|
| New task from the picker |
devlog.createCanvas with type
|
| Tray label, sidebar highlight, keep running in the tray |
app.setTrayLabel, ui.highlight, app.keepRunning
|
Timesheet and Summary pages (Mod+Shift+H) |
views (page), ui.openPage, devlog.activity, devlog.range
|
The Timesheets canvas: one kind=timesheet block per week |
devlog.managedCanvas, addBlock with kind and date, editBlock
|
| Sending to Jira or CMS |
permissions.send, destinations.list / preview / send
|
| Hour targets per canvas (weekly, monthly) |
canvasFields with inherited: false
|
Settings: idle_minutes (pause after this many minutes without input) and
in_repo (keep time in the devlog, synced, or on this machine only).
Sources: src/main.ts (activation, commands, view handlers), src/tracker.ts
(the clock), src/timesheets.ts (the Timesheets canvas and sending),
src/targets.ts, src/views/*.tsx, src/ui/*.tsx. Background:
docs/TIME-EXTENSION.md,
docs/TIMESHEETS.md.
Records which window is in front (app and title) while the machine is unlocked, for screen time on the timeline and in the review.
- Runs unrestricted (asks to be trusted) to start a platform helper:
PowerShell with
GetForegroundWindowon Windows, anosascriptloop on macOS (titles need the Accessibility permission),xdotoolon Linux. - Appends
{"t","app","title"}lines toextensions/builtin.devlog-focus/<machine>/YYYY/MM/YYYY-MM-DD.jsonl(appendOnly: ["**/*.jsonl"]), nothing while paused. - Gives the events back through
provide.focus.
A destination (worklogs, "Jira"): one worklog per timesheet entry on the
entry's Jira issue.
- Canvas field
issue(e.g.ACME-123), usually on task canvases, inherited down the tree. - Settings
baseurl,email; secrettoken. Basic auth with email and API token (Cloud), or a Bearer personal access token with no email (Data Center). REST API v2. -
checkcommand tests the connection from the settings page. - Ledger in
extensions/builtin.devlog-jira/sent/<week>.json(entry id → issue, worklog id and what was sent), so sending again sends only creates, updates and deletes. -
permissions.network: ["*.atlassian.net"], no read or write grant needed.
A destination (hours, "CMS") for a consultancy timesheet web app that has
no API, so it drives the web pages:
- form login (
j_security_check; cookies kept in memory for the send), the week page parsed for assignment rows and each day'stimesheet_id, and each day's form submitted withhrs_workedin decimal hours; - hours per assignment (canvas field
assignment, usually on the client) per day; the preview compares with what CMS shows so only differing days are sent; - ledger
sent/<week>.json(assignment and day → hours) so days it filled that no longer have time go back to 0; - settings
username,baseurl; secretpassword; commandscheckandassignments.
Devlog 0.18.0 · extension API 1.7.0 · storage format 4 · Repository · Design notes
Using Devlog
Writing extensions
- Overview
- Quickstart
- Manifest
- API Reference
- Types
- Views and UI
- UI Kit Reference
- Timesheet Destinations
- Activity and Time Data
- Sandbox and Permissions
- Testing
- Built-in Extensions
- API Versions
- Troubleshooting
Devlog internals