Skip to content

feat: support custom task sorting - #54

Closed
Rizhiy wants to merge 1 commit into
tintinweb:masterfrom
Rizhiy:feat/active-first-task-widget
Closed

Rizhiy wants to merge 1 commit into
tintinweb:masterfrom
Rizhiy:feat/active-first-task-widget

Conversation

@Rizhiy

@Rizhiy Rizhiy commented Aug 14, 2026

Copy link
Copy Markdown

Allows sortOrder to be a comparator function loaded from tasks-config.cjs, and adds optional completed-task collapsing.

Existing defaults, built-in sort behavior, and tests are unchanged. Three tests cover the new options.

Tests: lint, typecheck, 194 tests, build.

@Rizhiy
Rizhiy force-pushed the feat/active-first-task-widget branch from 0f4692f to 66797be Compare August 14, 2026 01:57
@Rizhiy Rizhiy changed the title feat: show active tasks before collapsed completed tasks feat: add active-first collapsed task display Aug 14, 2026
@Rizhiy
Rizhiy force-pushed the feat/active-first-task-widget branch from 66797be to 60b8dff Compare August 14, 2026 02:07
@Rizhiy Rizhiy changed the title feat: add active-first collapsed task display feat: support custom task sorting Aug 14, 2026
tintinweb added a commit that referenced this pull request Aug 16, 2026
The built-in `status` order is completed-first, the reverse of the TaskList
tool, so finished work sits on top of active work. Two PRs reached for a fix a
month apart: #35 added a sortDirection flag that reverses the result, #54 made
sortOrder a comparator function loaded by require() from a tasks-config.cjs —
including one inside the project's .pi/.

Neither shape survives. Reversing an order also flips its tie-breaks, so
`status` descending gives pending-first with ids descending inside each group,
which is not what either author wanted. And executing config code from a path
that lives inside every cloned repository trades a display setting for
arbitrary code execution.

sortOrder now also accepts a sort spec: an ordered list of comparison keys
(id / status / updatedAt), each with an optional direction and, on status, a
custom rank. Pure data, no code. The presets are themselves specs, and a new
`active` preset — in-progress → pending → completed, ties by id ascending — is
exactly the order both PRs were chasing, selectable from the settings menu
without editing a file. Specs are validated where they are read: an unknown
field, direction or status falls back to id order rather than breaking the
widget. collapseCompleted folds finished tasks into one `✔ N completed` line,
composing with the visible limit instead of overriding it — collapsing decides
what is in the list, then maxVisible/showAll/hiddenAt apply to what is left, so
a long backlog stays bounded. The header still counts every task.

The four existing presets reproduce their original comparators exactly,
tie-breaks included; a differential fuzz over 20k random task sets with
duplicate ids and forced timestamp ties found no divergence across 80k
orderings. Deliberately not ported from #35: a memoized sort cache that builds
a JSON.stringify signature over every task per frame, which for a ≤100-task
widget costs more than the sort it skips.

Two behaviour changes fall out of validating what was previously unchecked. An
unrecognised hiddenAt indexed the truncation table to undefined and threw on
every render — caught by the render guard, so the widget silently showed
nothing at all — and now narrows to bottom. An unrecognised sortOrder name
selected no comparator and left tasks in storage order; it now sorts by id.
CUSTOMIZING.md covers config scopes, presets, specs, recipes and failure modes,
linked from the README.

Converges #35 (thanks @Minh-Ng) and #54 (thanks @Rizhiy).
@tintinweb

Copy link
Copy Markdown
Owner

Landed this in bce4b88.

collapseCompleted is in, with one change: it composes with maxVisible
rather than overriding it, so the visible limit still applies to whatever's
left after collapsing.

For sorting I went with a JSON sort spec instead of a comparator loaded from
tasks-config.cjs — .pi/ sits inside cloned repos, so executable config there
runs on clone. Your comparator is the new active preset, or as a spec:

  "sortOrder": [
    { "field": "status", "rank": ["in_progress", "pending", "completed"] },
    { "field": "id" }
  ]

Docs: https://github.com/tintinweb/pi-tasks/blob/master/CUSTOMIZING.md

Closing since it's on master. Thanks for the PR.

@tintinweb tintinweb closed this Aug 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants