Skip to content

Feature/task list keyboard project management - #2176

Open
thisisthedave wants to merge 58 commits into
callumalpass:mainfrom
thisisthedave:feature/task-list-keyboard-project-management
Open

Feature/task list keyboard project management#2176
thisisthedave wants to merge 58 commits into
callumalpass:mainfrom
thisisthedave:feature/task-list-keyboard-project-management

Conversation

@thisisthedave

Copy link
Copy Markdown

Summary

This PR improves Task List interaction through configurable
keyboard navigation, persistent focus and selection, batch editing, and richer
grouped-property drag-and-drop behavior.

It also adds project-assignment editing, normalizes list-valued group headings,
and introduces settings for controlling whether drops move or copy values
between groups.

New features

Keyboard navigation and focus

  • Added keyboard navigation through Task List cards.

  • Default navigation shortcuts now include:

    • Next task: ArrowDown or j
    • Previous task: ArrowUp or k
    • First task: Home
    • Last task: End
  • Moving the mouse over a task transfers focus to that card.

  • Keyboard focus is restored when:

    • The Task List is first opened.
    • Its tab is brought back to the foreground.
    • The active tab is clicked again.
    • Search is submitted or cancelled.
    • A task edit causes the Bases view to rerender.
  • Logical task focus is tracked independently from transient DOM elements,
    preventing rerenders from leaving focus on document.body.

  • Focus and selection have distinct, consistent styling:

    • Focused tasks use a gray border.
    • Selected tasks use a purple background and dim purple border.
    • The first selected task receives the brighter selection border.
    • Selection styling takes precedence over the focus background, while the
      focus border remains visible.

Task selection and batch actions

  • Added configurable shortcuts for:
    • Toggling selection: Space or x
    • Selecting all visible tasks: Ctrl+A / Cmd+A
    • Clearing focus and selection: Escape or Backspace
    • Copying selected task titles: Ctrl+C / Cmd+C
    • Toggling archive state: y
task-keyboard-nav
  • Existing task-editing shortcuts now operate on the active selection.

  • Batch operations only affect tasks that are both selected and currently
    visible. Tasks hidden by search or filtering remain selected but are not
    edited.

  • Selection is preserved across:

    • Task edits
    • Bases rerenders
    • Tab changes
    • Search activation and cancellation
    • Context-popup cancellation
  • Pressing Escape inside a contextual popup or search field no longer
    unintentionally clears the Task List selection.

  • Clearing selection leaves the Task List ready for continued keyboard
    navigation.

Configurable Task List shortcuts

  • Added a dedicated settings interface for Task List shortcuts.
  • Shortcuts support arbitrary modifier combinations and multiple bindings per
    action.
image
  • Task List shortcuts use an Obsidian child scope while the view is active,
    allowing them to work even when the same chord is assigned to a global editor
    command.

  • Added duplicate detection with cancel and replace options.

  • Shortcut capture now includes an explicit confirm/cancel step.

  • Escape can be assigned as a shortcut without dismissing the underlying
    Settings dialog.

  • Shortcut bindings can be individually removed using a circle-x action.

  • New bindings are added using a circle-plus action.

  • Each action can be reset to its defaults.

Search integration

  • / focuses the Task List search field.
  • Pressing Enter in search returns keyboard focus to the task list.
  • Pressing Escape exits search without clearing the current selection.
  • Task navigation and edit shortcuts continue to work after leaving search.

Project assignment editing

  • The project-selection dialog now supports removing existing project
    assignments as well as adding new ones.
image
  • The project selection dialog and drag-and-drop code now use shared code to assign projects so that both write wikilink-style values into the projects property.

Grouped-property drag and drop

Drag and drop between groups now works generically for list-valued properties,
including projects, tags, contexts, and compatible custom list properties.

image task-drag-drop

Three configurable behaviors are available under Appearance & UI → Task
Interaction:

  • Move between groups — replace the source group values with the destination
    values.

  • Copy between groups — preserve existing values and add the destination values.

  • Move; Ctrl/Option to copy — move by default, or preserve existing values when
    the modifier is held. This is the default.

Additional behavior includes:

  • Dropping a selected task applies the operation to all selected, visible tasks.
  • Each task’s source values are evaluated independently during a batch drop.
  • Dropping into an ungrouped section clears the applicable property when using
    move behavior.

Stable grouping for list properties

List-valued group keys are normalized before being submitted to the Bases
renderer.

For example, these project assignments now appear in the same group:

  • [[Kitchen]], [[Baking]]
  • [[Baking]], [[Kitchen]]
image

Normalization affects grouping identity only and does not rewrite the order
stored in task frontmatter.

Implementation hotspots

Changes Source Location
Main Task List integration, lifecycle, keyboard dispatch, batch action targeting, and drag/drop TaskListView.ts
Logical task focus and focus restoration across rerender TaskListFocusController.ts
Determining when the Task List owns keyboard input TaskListInputOwnershipController.ts
Shortcut definitions, defaults, matching, and action metadata taskListKeyboardActions.ts
Generic list-property drop behavior and batch drop planning taskListDropPlanning.ts
Order-insensitive normalization of list-valued groups taskListGrouping.ts
Conversion of Bases list group key basesValueConversion.ts
Shortcut settings UI, capture scope, confirmation, and duplicate handling keyboardShortcutsTab.ts
Group drag/drop behavior setting appearanceTab.ts
Default settings and shortcut bindings defaults.ts
Settings types and drop-behavior definitions settings.ts
Settings loading and shortcut normalization settingsPersistence.ts
Adding and removing project assignments ProjectSelectModal.ts
Canonical project-reference generation linkUtils.ts
Focus, selection, shortcut, grouping, and drop styling styles.css

Testing

The PR adds focused unit coverage for:

  • Keyboard shortcut matching and configurable bindings
  • Focus and input ownership
  • Selection persistence and visible-only batch targeting
  • Shortcut settings persistence
  • List-property drop planning
  • Project-reference generation
  • List-valued group normalization

- Task-list shortcuts pause while an Obsidian menu or modal is open.
- Menu/modal Escape and Backspace events remain owned by the overlay.
- Focus returns to the originating task after an overlay closes.
- Focus is not stolen if the user intentionally moves to another control.
- Escape clears task selection without moving focus to the Bases root.
- Escape dismisses search; Backspace dismisses an already-empty search and returns to the focused task.
- IME composition and editable controls suppress task-list shortcuts.
- All listeners and pending restoration timers are cleaned up with the view.
  - Selection/focus visuals now have deterministic precedence:
      - Purple selection background overrides gray focus background.
      - Gray focus border overrides the primary-selection border.
      - Every selected task gets a subdued purple border.
      - The primary selected task retains the brighter purple border
        when not focused.

  - The first rendered task receives keyboard focus when the task view
    initially loads, so shortcuts work immediately without clicking a
    task first.
  - Selection state is rehydrated whenever Bases rebuilds the view
    after an edit.

  - Batch property, tag, and archive edits no longer explicitly clear
    selection. Delete and “Clear selection” still do.

  - Task-list views listen for workspace leaf activation and restore
    the previously focused card when their tab returns to the
    foreground.
  - Keyboard shortcuts now remain available when focus is on a non-
    interactive part of the foreground task view. Actions use the
    remembered focused card as their target.

  - Cancelling search restores card focus, and subsequent rerenders
    continue restoring it.

  - / now creates and opens the search controls when search was
    disabled for the Bases view, then focuses the input.

  - Existing search controls are simply focused.
  Keyboard multi-edit actions now:

  - Target only selected tasks present in the current filtered view.
  - Preserve hidden tasks in the selection state.
  - Do nothing if every selected task is filtered out.
  - Continue using the focused task when there is no active selection.
  - Enter in search preserves the query and returns focus to the remembered task.
  - Added customizable actions:
      - Clear focus/selection: Escape, Backspace
      - Toggle selection: Space
      - Copy selected/focused visible task titles: Ctrl/Cmd+C
      - Select all visible tasks: Ctrl/Cmd+A
      - Toggle archive: Y

  - Legacy shortcut settings migrate to the new action names.
  - Mixed archived/unarchived selections show a warning instead of toggling unpredictably.
  - Shortcut conflict detection and replace/cancel behavior automatically covers the new actions.
  - Fixed literal Space key normalization.
  The active Task List now pushes a child keyboard scope containing its configured shortcuts. This gives  bindings such as Ctrl+B and Ctrl+D precedence over global Bold/Delete Paragraph commands while:

  - The Task List leaf is active.
  - Keyboard focus belongs to a non-editable part of that view.
  - No menu, modal, search input, or editor owns input.

  The scope is popped when the leaf deactivates, focus enters an editable control, or the view unloads.
- Hotkey recording now pushes a temporary Obsidian Scope that consumes Escape, cancels recording, and
    prevents the underlying Settings dialog from closing. Cleanup is idempotent and also runs during
    rerenders.
  - Escape is now captured as a normal shortcut and forwarded to the confirmation flow without closing
    Settings.

  - New shortcuts require confirmation before being saved.
  - “Add shortcut” is now a circle-plus icon.
  - Reset appears before Add.
  - Existing bindings display a circle-x icon beside the value; it turns red on hover.
  - Added scoped styling and updated regression coverage.
  - Moving the mouse over a task updates its visual/remembered focus without stealing DOM focus.
  - Moving over empty list space leaves focus unchanged.
  - Selection toggle targets the mouse-focused task.
  - Navigation shortcuts continue from the hovered task, then switch back to normal keyboard/DOM focus.
  - Uses one delegated mousemove listener, compatible with rerenders and virtualized cards.
  - Mouse hover now moves actual card focus, clearing stale gray focus styling from the previous card.
  - Keyboard navigation switches the list to keyboard-cursor mode and suppresses lingering :hover
    backgrounds on non-focused cards.

  - Selected-task purple styling remains unaffected.
  - Edit overlays now schedule focus restoration when opened and continue monitoring until closed.
  - After task rerenders, focus returns to the replacement card, reactivating shortcut ownership while
    preserving selection.

  - Active interactive controls inside cards are protected from hover stealing focus.
- Assigned projects appear in the selector.
- Projects can be removed from the active task selection.
  - Dragging a task between project groups now replaces all existing project
    assignments with the destination project.

  - Other list grouping behavior remains unchanged.
  - File-link grouping is unaffected.
  - Added tests for frontmatter mutation and post-drop task state.
  - drag between project groups replaces all projects.
  - shift+drag adds the destination project while preserving every existing project.

  - drag between tag groups replaces all tags.
  - shift-drag adds the destination tag while preserving every existing tag.

  The Appearance & UI → Task Interaction group now includes “Project and tag drop
  behavior” with:
  - Dropping replaces values
  - Dropping adds values
  - Dropping replaces; Shift-drop adds — default
 - The setting now applies to every list-valued grouping property, including
    contexts, aliases, and custom Obsidian list properties.

  - Renamed the setting to “List-property drop behavior” and generalized its help
    text.

  - Replacement mode now replaces the entire list for any list property.
  - Additive mode preserves all existing values and adds the destination.
  - Changed native drag effects from move-only to allow all effects. Additive
    drops advertise copy; replacement drops advertise move. This permits modifier
    keys during drag initiation as intended by the HTML drag API. MDN drag
    operations
- Project-group drops now resolve the project file and generate the same canonical project reference used by the task modal.
- Respects the useFrontmatterMarkdownLinks setting; otherwise writes wikilinks
    such as "[[Project Name]]".
  - Dragging a selected task moves all selected tasks that are currently visible.
  - Filtered-out selected tasks are not modified.
  - Dragging an unselected task moves only that task.
  - Every affected task receives the destination group mutation and property-
    change side effects.

  - The card under the pointer determines the insertion position.
  - Selection remains intact after the drop.
  - Lexically sorts values when forming list-valued group identities.
  - Merges equivalent groups before rendering.
  - Handles projects, tags, contexts, and custom list properties.
  - Does not modify the order stored in task frontmatter.
  - Leaves scalar grouping unchanged.
…tkey to mark complete

Selecting a completed status for a recurring task now records an occurrence completion instead of writing status: done.
Applies to Task List keyboard status editing and the task context-menu status submenu.
Recurring completion repairs an already-completed parent back to the configured default status.
Completion results now return the resolved recurrence date and final state.
Toasts report the actual completed occurrence, e.g. “completed for Aug 2,” rather than the projected Aug 4 date.
Added configurable Mark complete Task List action.
Default shortcut: e (like todoist). Edit task context menu moved to shift+e
Added regression tests for shifted recurrence dates, parent-status repair, status routing, and shortcut resolution.
The Task List keyboard/focus/selection system is now shared across all four Bases task views (kanban, calendar, task list, agenda). What changed:

New shared modules (src/bases/):

basesTaskCardActions.ts — the view-agnostic action executor, extracted from TaskListView.executeTaskListAction (navigate, select, edit due/priority/status/recurrence, mark complete, archive, tags/context/project, delete, etc.)
BasesTaskCardKeyboardController.ts — owns roving card focus, keyboard dispatch, and Obsidian Scope activation for any card-based view; replaces the duplicate focus-controller wiring that had crept into TaskListView alongside BasesViewBase's shared hover tracking
Per-view wiring:

Task List — refactored onto the shared controller with no intended behavior change (verified via the existing test suite, adapted to the new module boundaries)
Kanban — full parity: navigation, multi-select, and all edit actions on real .task-card elements; keyboard nav shares the same virtualized-column limitation mouse selection already had
Agenda / Calendar list mode — full parity, same as Kanban, since it renders real task cards
Calendar grid modes (Month/Week/Day/Year/Custom) — no roving-focus model since these render FullCalendar's own elements, so hotkeys act on whichever task is currently hovered; navigation/selection/search/create are excluded there
- selected/keyboard-focused card styling now survives scrolling in virtualized Kanban columns and the Task List. Added an onRenderedElementsChanged hook to VirtualScroller that fires on every mount/unmount, wired to re-run selection and focus styling regardless of what triggered the recycle (scroll included).
- Ctrl+A, Shift+Arrow-range, and Shift+click-range now select across the entire matching task list in Kanban and Task List, not just currently-rendered cards. Kanban's fix computes true column-major/swimlane-major visual order (not just an approximate pre-grouping order) without disturbing the separate subtask-expansion bookkeeping.
- Task List's keyboard next/previous/first/last can now reach cards the virtual scroller hasn't mounted yet, via a new ensureIndexRendered primitive. Kanban's version of this was scoped out as a separate follow-up (real redesign of column-aware navigation, not an additive fix) - flagged clearly in memory for next time.
Calendar's Agenda/list mode creates its .task-card elements via FullCalendar's eventDidMount callback (mountCalendarListEventCard, called from CalendarView.handleEventDidMount), which runs on FullCalendar's own async render cycle - completely decoupled from CalendarView.render()'s synchronous finally-block where selection styling normally gets reapplied. On first load, that sync pass ran before FullCalendar had actually mounted any cards, so it found nothing to style. Kanban didn't have this problem because its virtualization hook already covers it.
Default key: t
Available in Keyboard Shortcuts settings as the existing localized “Time estimate (minutes)” label.
Opens the existing numeric user-field modal, supports bulk updates, clears non-positive/blank values, and restores card focus afterward.
@bradmccormick

Copy link
Copy Markdown

I just migrated ~30 tasks into TaskNotes from another app and this is the main friction point keeping me from doing quick triage sessions in the list view. Being able to j/k through tasks and use Space to build a selection, then bulk-complete, would make this feel much more like a proper task manager. Hope this gets merged — and if there's appetite for it, a single-key 'mark done' shortcut on the focused item would be icing on the cake.

Normal Task List cards now preserve the native mousedown default action, so browser drag/drop can start.
Embedded CodeMirror Live Preview cards still stop press-event propagation, preventing editor selection behavior without canceling dragging.
Selected visible tasks now receive distinct manual-order ranks and are inserted together as a contiguous block at the drop target, preserving their selection order. Previously only the dragged task received a new rank, so the others stayed in place unless the drop also changed their group.
@thisisthedave

Copy link
Copy Markdown
Author

... if there's appetite for it, a single-key 'mark done' shortcut on the focused item would be icing on the cake.

Hi @bradmccormick! If you'd like I can publish hotkey support as an independent plugin until it's merged into the official tasknotes plugin. I'd love to get feedback and testing from somebody other than myself. There is already a hotkey for completing a task. The default shortcut is "e" to mimic task completion in Todoist.

@bradmccormick

Copy link
Copy Markdown

Thanks for the quick response! I went ahead and built your branch locally and have been running it in my vault. Confirmed: j/k navigation, Space multi-select, and e to complete all work exactly as described — this is a genuine quality-of-life upgrade for task triage.

Happy to keep dogfooding the branch as you iterate. If you ever publish a BRAT-installable version I'll switch to that, but for now I'll just rebuild from the branch when there are updates. Either way I'm rooting for this to land in the main plugin.

@thisisthedave

Copy link
Copy Markdown
Author

@bradmccormick that's great! Please let me know if you find any bugs or have any feature ideas. I created a release from the branch, so you should now be able to install the repo with BRAT: https://github.com/thisisthedave/tasknotes

FWIW if you're coming from a project management app like Linear or Jira, you might be interested in the project progress graph plugin I just released. It is also raw and could use some additional scrutiny: https://community.obsidian.md/plugins/tasknotes-views

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