Add grouping of pending tasks into Overdue/Today/Tomorrow/This
Week/Later/No Date buckets when the active list has group_by_due_date
enabled. This provides a grouped view in TasksScreen (section headers,
no drag-and-drop) and a new derived store groupedPendingTasks exported
from the app store to supply the UI. Dated groups are sorted by due_date
while the No Date group preserves the custom order.
Few changes: lets make the date chip red when in the overdue section. In all other sections, lets hide the date chip for individual tasks. Then, for the Later section, lets break that up into individual days.
- Replaced single "Later" group with per-day groups (e.g. "Mon, Apr 20"), each sorted by due_date
- Added `dateChipStyle` prop to TaskItem ("normal" | "overdue" | "hidden"), default "normal"
- Date chip is red (border-red-400 text-red-400) in "overdue" style, hidden in "hidden" style
- TasksScreen passes dateChipStyle="overdue" for Overdue group, "hidden" for all others
Fix date chips and include year in future group labels
Replace dynamic ternary class strings with explicit {#if} blocks for the
date chip so Tailwind v4's scanner picks up the classes and theme tokens
are respected. Use text-danger and border-danger for overdue chips and
proper border/token classes for non-overdue chips to fix the overdue
chip appearance and prevent chips from showing in sections that should
be hidden.
Also include the year in per-day group labels when the date is not in
the current year (e.g. "Mon, Apr 20, 2027") by passing the appropriate
Intl.DateTimeFormat options, so tasks scheduled in future years display
an unambiguous header.
yes (remove This Week, extend per-day grouping to everything beyond tomorrow)
- Removed "This Week" group entirely
- Everything beyond tomorrow now gets a per-day group label (same as what "Later" was doing), giving a consistent Overdue → Today → Tomorrow → [per-day] → No Date structure
Respect manual order within same-day groups
Ensure tasks that fall on the same day keep the user's manual ordering
by using a task_order index as a tiebreaker when sorting. Also add
drag-and-drop group tracking so drags and drops are confined to their
originating group and wire full DnD handlers into the grouped view.
- Use a taskOrderIndex map in sortByDue to break ties when due dates are equal so same-day tasks respect manual order.
- Add dragGroup state and record it on handleDragStart; ignore dragOver/drop events when the event group differs from the originating dragGroup to prevent cross-group drops.
- Reset dragGroup on drag end.
- Wrap grouped TaskItem entries with draggable divs and hook up dragstart/dragover/dragend/drop to match the flat view's DnD behavior.
Update drag-and-drop to set task due dates
Support dragging tasks between date-grouped columns by updating the
task's due_date to the target group's date (or clearing it when dropped
into No Date). Preserve the task's time-of-day when has_time is set,
block dropping into the computed Overdue group, and allow dragging out
of Overdue. Also add TaskGroup.date to the grouping data and expose a
showSubtaskCount prop and optional subtask rendering in the task list
UI.
fix null taskId error when reordering after cross-group drag
- Capture dragId and dragGroup into local variables at the top of handleDrop before any awaits — prevents the race where dragend fires during updateTask and nulls out dragId before reorderTask is called
Allow dragging tasks to update date groups
Add date handling to task groups so dragging a task into another group
updates its due_date. Introduce date: Date | null for TaskGroup (null
represents Overdue/No Date), update cross-group drops to set or clear a
task's due_date based on the target group while preserving time-of-day
for tasks with has_time, and keep within-group drops as reorders. Also
ensure Overdue remains a computed-only target (cannot drop into it)
while allowing dragging out.
Additionally, add GoogleTasks workspace support fields
(WorkspaceMode::GoogleTasks and an optional google_account) and include
the new field in WorkspaceConfig::new to initialize google_account to
None.
|
||
|---|---|---|
| .claude | ||
| .idea | ||
| apps/tauri | ||
| crates | ||
| docs | ||
| .gitattributes | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CLAUDE.md | ||
| LICENSE | ||
| PLAN.md | ||
| README.md | ||
Onyx
A local-first, cross-platform tasks application built with Rust. Inspired by Google Tasks, designed for speed and flexibility.
Core Principles
- Local-First: Your data, your folder, your control
- Fast: Sub-second startup, instant response
- Cross-Platform: Single codebase, all platforms
- Flexible: Multiple workspaces for different contexts
Project Structure
onyx/
├── Cargo.toml # Workspace definition
├── PLAN.md # Detailed project plan
├── README.md # This file
├── crates/
│ ├── onyx-core/ # Core library (backend)
│ └── onyx-cli/ # CLI frontend
├── apps/
│ └── tauri/ # Tauri v2 GUI (Svelte 5 + Tailwind CSS 4)
└── docs/
Project Status
- Phase 1 (Core + CLI): Complete
- Phase 2 (WebDAV Sync): Complete — backend, CLI, and GUI all wired
- Phase 3 (GUI MVP): Complete
- Phase 4 (Mobile): Tauri Android cfg-gated, needs
tauri android init+ build
Core Library (onyx-core)
- Data models (Task, TaskList, AppConfig, WorkspaceConfig)
- Markdown file I/O with YAML frontmatter
- Local storage with repository pattern
- Multiple workspace support
- Task ordering and grouping
- Subtask hierarchy (parent_id)
- WebDAV sync with three-way diff and offline queue
- Platform keychain credential storage (feature-gated for Android)
- Checksum-based conflict resolution (remote wins, local recovered as duplicate)
CLI (onyx-cli)
- Workspace management (init, add, list, switch, remove, retarget, migrate)
- Task list management (create, show, delete)
- Task operations (add, complete, delete, edit)
- Group-by-due-date toggle
- WebDAV sync (setup, push, pull, status)
GUI (apps/tauri/)
- Tauri v2 + Svelte 5 + Tailwind CSS 4
- Task CRUD with animated transitions
- Drag-and-drop reordering
- Sliding lists drawer, settings popup
- Workspace switcher with add/remove
- Dark mode (GNOME-style neutral grays, cyan-blue accent)
- Due date picker/editor with optional time
- Subtask hierarchy with three-panel slide navigation
- Move tasks between lists
- List rename, group-by-due-date toggle, delete completed tasks
- Keyboard shortcuts (Escape priority chain)
- WebDAV setup flow with credential auto-population
- File watcher (auto-reloads on external changes)
- Auto-sync with configurable interval, status indicators
- Custom confirmation dialogs
- Desktop packaging (Linux: AppImage + .deb; Windows: MSI)
Development Setup
Prerequisites
- Rust 1.70+ (install from rustup.rs)
- Git
- Node.js 18+ (for Tauri GUI)
Build
# Clone and build
git clone https://github.com/SteelDynamite/onyx.git
cd onyx
cargo build
# Run tests
cargo test -p onyx-core
# Run CLI
cargo run -p onyx-cli -- --help
# Run Tauri GUI
cd apps/tauri && npm install
npm run tauri dev
Quick Start
Initialize your first workspace
# Initialize a new workspace
cargo run -p onyx-cli -- init ~/Documents/Tasks --name personal
# This creates:
# - A workspace named "personal" at ~/Documents/Tasks
# - A default list called "My Tasks"
# - Sets "personal" as the current workspace
Add and manage tasks
# Add a task
cargo run -p onyx-cli -- add "Buy groceries"
# Add a task with due date
cargo run -p onyx-cli -- add "Review PR #123" --list "Work" --due "2026-11-15"
# List all tasks
cargo run -p onyx-cli -- list show
# Complete a task
cargo run -p onyx-cli -- complete <task-id>
# Edit a task (opens in $EDITOR)
cargo run -p onyx-cli -- edit <task-id>
# Delete a task
cargo run -p onyx-cli -- delete <task-id>
Manage workspaces
# Add another workspace
cargo run -p onyx-cli -- workspace add shared ~/Dropbox/TeamTasks
# List workspaces
cargo run -p onyx-cli -- workspace list
# Switch workspace
cargo run -p onyx-cli -- workspace switch shared
# Use specific workspace for a command
cargo run -p onyx-cli -- add "Team meeting" --workspace shared
Manage task lists
# Create a new list
cargo run -p onyx-cli -- list create "Work"
# Show tasks in a specific list
cargo run -p onyx-cli -- list show --list "Work"
# Delete a list
cargo run -p onyx-cli -- list delete "Work"
Data Format
Tasks are stored as markdown files with YAML frontmatter (Obsidian-compatible):
---
id: 550e8400-e29b-41d4-a716-446655440000
status: backlog
version: 3
due: 2026-11-15T14:00:00Z
---
Task description and notes go here in **markdown** format.
- Can include lists
- Rich formatting
- Links, etc.
File System Structure
~/Documents/Tasks/ # User-selected folder
├── .onyx-workspace.json # Workspace metadata: list ordering, detection
├── My Tasks/ # Task list folder
│ ├── .listdata.json # List metadata: task order, id, timestamps
│ ├── Buy groceries.md # Individual task files
│ └── Call dentist.md
└── Work/
├── .listdata.json
├── Review PRs.md
└── Team meeting prep.md
Testing
Run the test suite:
# Run all tests
cargo test
# Run tests for specific crate
cargo test -p onyx-core
# Run tests with output
cargo test -- --nocapture
What's Next?
- Phase 4: Mobile support (iOS & Android via Tauri v2 mobile)
- Phase 5: GUI advanced features (rich markdown editor, search/filter)
- Phase 6: Mobile polish and platform-specific integrations
- Phase 7: Google Tasks importer and unique features
See PLAN.md for detailed roadmap.
License
GNU General Public License v3.0 (GPL-3.0)
This project is free and open-source software.