diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-01-13 08:38:39 -1000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-01-13 08:38:39 -1000 |
| commit | 043f48c12eb4dfc410e8724b430166000d7cb905 (patch) | |
| tree | 2d67af6a531bc1ad76bcf54641d1f49b36ab094b | |
| parent | 06c7485a7d05de86f9898e388161e8d932d5f3e6 (diff) | |
Implement unified Atom model for multi-source abstraction
- Create internal/models/atom.go with Atom struct and enums
- Add mapper functions for Task, Card, Note, and Meal types
- Normalize priority scales (1-4) and assign brand colors
- Update Phase 2 plan with Atom architecture as Step 1
- Document architectural decision in SESSION_STATE.md
This abstraction enables consistent handling, sorting, and rendering
of items from Trello, Todoist, Obsidian, and PlanToEat sources.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
| -rw-r--r-- | PHASE_2_SURGICAL_PLAN.md | 114 | ||||
| -rw-r--r-- | SESSION_STATE.md | 13 | ||||
| -rw-r--r-- | internal/models/atom.go | 135 |
3 files changed, 261 insertions, 1 deletions
diff --git a/PHASE_2_SURGICAL_PLAN.md b/PHASE_2_SURGICAL_PLAN.md new file mode 100644 index 0000000..dbac3a6 --- /dev/null +++ b/PHASE_2_SURGICAL_PLAN.md @@ -0,0 +1,114 @@ +# Phase 2: The Productivity OS (Interactive & Refined) + +This phase transforms the dashboard into a primary interface with write capabilities, smart sorting, and a refined "Glassmorphism" UI. + +## 1. The Unified Atom Model +**Status:** [ ] Pending + +```text +Define `models.Atom` to abstract over Trello, Todoist, Obsidian, and PlanToEat. +Implement mapper functions. + +1. **Model:** Create `internal/models/atom.go` with: + - AtomSource enum (trello, todoist, obsidian, plantoeat) + - AtomType enum (task, note, meal) + - Atom struct with normalized fields (ID, Title, Description, DueDate, Priority, etc.) + - UI helper fields (SourceIcon, ColorClass) + - Raw field for preserving original data +2. **Mappers:** Implement converter functions: + - TaskToAtom(Task) -> Atom + - CardToAtom(Card) -> Atom + - NoteToAtom(Note) -> Atom + - MealToAtom(Meal) -> Atom +3. **Priority Mapping:** Normalize priority scales to 1-4 (Low to Urgent) +4. **Color Mapping:** Assign brand colors (Trello=Blue, Todoist=Red, Obsidian=Purple, PlanToEat=Green) +``` + +## 2. Information Architecture: The 4-Tab Split +**Status:** [ ] Pending + +```text +Refactor the frontend and router to support 4 distinct tabs. + +1. **Templates:** Update `web/templates/index.html` navigation to: + - Tasks (Todoist + Due Trello) + - Planning (Trello Boards) + - Notes (Obsidian) + - Meals (PlanToEat) +2. **Handlers:** Create/Update handlers in `internal/handlers/tabs.go` (new file): + - `HandleTasksTab`: Aggregates Todoist + Trello cards with due dates. + - `HandlePlanningTab`: Returns Trello boards (excluding cards shown in Tasks?). + - `HandleMealsTab`: Returns PlanToEat data. +3. **Router:** Register new routes `/tabs/tasks`, `/tabs/planning`, `/tabs/meals`. +``` + +## 3. Trello: Smart Sorting & Logic +**Status:** [ ] Pending + +```text +Enhance Trello sorting in `internal/api/trello.go` and `internal/store/sqlite.go`. + +1. **Backend:** Update `GetBoardsWithCards` to sort by: + - Primary: Has active cards? + - Secondary: Newest card modification date (requires parsing Trello ID hex timestamp). + - Tertiary: Number of cards. +2. **Store:** Update SQL query in `GetBoards` to reflect this sort order. +``` + +## 4. Todoist: "Due First" Sorting +**Status:** [ ] Pending + +```text +Ensure Todoist tasks are sorted by urgency. + +1. **Store:** Edit `internal/store/sqlite.go` -> `GetTasks`. +2. **Query:** Update ORDER BY clause: + - `CASE WHEN due_date IS NULL THEN 1 ELSE 0 END` (Due dates first) + - `due_date ASC` (Earliest first) + - `priority DESC` (High priority next) +``` + +## 5. Obsidian: Search & Categorization +**Status:** [ ] Pending + +```text +Enhance Obsidian to support search and categorization. + +1. **Backend:** Edit `internal/api/obsidian.go`. + - Add `SearchNotes(query string)`. + - Add logic to group notes by tags (e.g., #planning, #task, #meal). +2. **Frontend:** Add a Search Bar to the Notes tab. + - HTMX trigger: `hx-get="/tabs/notes/search" hx-trigger="keyup changed delay:500ms"`. +``` + +## 6. Visual Overhaul: "Muted Landscape" +**Status:** [ ] Pending + +```text +Apply the new visual design. + +1. **CSS:** Update Tailwind config for "muted" palette. +2. **Layout:** Add a full-screen background image container. +3. **Components:** Update cards to use `bg-white/80 backdrop-blur-md` (Glassmorphism). +``` + +## 7. Write Operations (The "Primary Interface" Goal) +**Status:** [ ] Pending + +```text +Implement the interactive features. + +1. **Trello:** Implement `UpdateCard` (Archive) and "Mark Complete" button. +2. **Obsidian:** Implement `CreateNote` (Quick Capture) and "New Note" form. +``` + +## 8. Mobile PWA +**Status:** [ ] Pending + +```text +Make it installable. + +1. Create `web/static/manifest.json`. +2. Add icons. +3. Link manifest in `index.html`. +``` diff --git a/SESSION_STATE.md b/SESSION_STATE.md index f4b76f0..e3a498e 100644 --- a/SESSION_STATE.md +++ b/SESSION_STATE.md @@ -32,6 +32,13 @@ Frontend modernization with tabs, HTMX, and Tailwind build pipeline complete. - Empty boards now pushed to bottom, active boards at top - **Commit:** 9ef5b7f "Sort Trello boards with active boards first" - **Frontend Modernization:** Complete UI overhaul with tabs, HTMX, and Tailwind build pipeline + - **Commit:** 06c7485 "Modernize frontend with tabs, HTMX, and Tailwind build pipeline" +- **Unified Atom Model:** Created abstraction layer for all data sources + - internal/models/atom.go: New Atom struct with AtomSource and AtomType enums + - Mapper functions: TaskToAtom, CardToAtom, NoteToAtom, MealToAtom + - Priority normalization (1-4 scale), brand color mapping (Trello=Blue, Todoist=Red, Obsidian=Purple, PlanToEat=Green) + - Preserves raw data for future write operations + - All tests passing after implementation - **Build Pipeline:** npm + PostCSS + Tailwind configuration (replaced CDN) - package.json, tailwind.config.js, postcss.config.js, Makefile - Custom design system with brand colors (Trello, Todoist, Obsidian, PlanToEat) @@ -72,9 +79,13 @@ Frontend modernization with tabs, HTMX, and Tailwind build pipeline complete. - **Decision:** Compiled Tailwind over CDN for 99% smaller CSS and custom design tokens. - **Decision:** Template partials for HTMX-friendly swap targets and reusability. - **Decision:** Native `<details>` element for empty board collapsible (no JS required). +- **Decision:** Unified Atom Model - Abstract all data sources (Trello, Todoist, Obsidian, PlanToEat) into a single `models.Atom` type for consistent handling, sorting, and rendering across the UI. ## 📋 Next Steps -1. **Future:** Consider Phase 2 features (write operations, user management). +1. **Phase 2 Step 2:** Implement 4-Tab Split (Tasks, Planning, Notes, Meals) using the Atom model. +2. **Phase 2 Step 3:** Trello smart sorting (activity-based, modification date). +3. **Phase 2 Step 4:** Todoist "due first" sorting. +4. **Phase 2 Remaining:** Search, visual overhaul, write operations, PWA. ## ⚠️ Known Blockers / Debt - None currently. diff --git a/internal/models/atom.go b/internal/models/atom.go new file mode 100644 index 0000000..47695d9 --- /dev/null +++ b/internal/models/atom.go @@ -0,0 +1,135 @@ +package models + +import "time" + +type AtomSource string + +const ( + SourceTrello AtomSource = "trello" + SourceTodoist AtomSource = "todoist" + SourceObsidian AtomSource = "obsidian" + SourceMeal AtomSource = "plantoeat" +) + +type AtomType string + +const ( + TypeTask AtomType = "task" + TypeNote AtomType = "note" + TypeMeal AtomType = "meal" +) + +// Atom represents a unified unit of work or information +type Atom struct { + ID string + Title string + Description string + Source AtomSource + Type AtomType + + // Metadata + URL string + DueDate *time.Time + CreatedAt time.Time + + Priority int // Normalized: 1 (Low) to 4 (Urgent) + + // UI Helpers (to be populated by mappers) + SourceIcon string // e.g., "trello-icon.svg" or emoji + ColorClass string // e.g., "border-blue-500" + + // Original Data (for write operations) + Raw interface{} +} + +// TaskToAtom converts a Todoist Task to an Atom +func TaskToAtom(t Task) Atom { + // Todoist priority: 1 (normal) to 4 (urgent) + // Keep as-is since it already matches our 1-4 scale + priority := t.Priority + if priority < 1 { + priority = 1 + } + if priority > 4 { + priority = 4 + } + + return Atom{ + ID: t.ID, + Title: t.Content, + Description: t.Description, + Source: SourceTodoist, + Type: TypeTask, + URL: t.URL, + DueDate: t.DueDate, + CreatedAt: t.CreatedAt, + Priority: priority, + SourceIcon: "✓", // Checkmark emoji for tasks + ColorClass: "border-red-500", + Raw: t, + } +} + +// CardToAtom converts a Trello Card to an Atom +func CardToAtom(c Card) Atom { + // Trello doesn't have explicit priority, default to medium (2) + // Can be enhanced later with label-based priority detection + priority := 2 + + return Atom{ + ID: c.ID, + Title: c.Name, + Description: c.ListName, // Use list name as description + Source: SourceTrello, + Type: TypeTask, + URL: c.URL, + DueDate: c.DueDate, + CreatedAt: time.Time{}, // Trello ID contains timestamp, can be parsed later + Priority: priority, + SourceIcon: "📋", // Clipboard emoji for boards + ColorClass: "border-blue-500", + Raw: c, + } +} + +// NoteToAtom converts an Obsidian Note to an Atom +func NoteToAtom(n Note) Atom { + // Notes don't have priority, default to low (1) + priority := 1 + + return Atom{ + ID: n.Path, // Use path as unique ID + Title: n.Title, + Description: n.Content, + Source: SourceObsidian, + Type: TypeNote, + URL: "", // Obsidian notes don't have URLs + DueDate: nil, // Notes typically don't have due dates + CreatedAt: n.Modified, // Use modified time as created time + Priority: priority, + SourceIcon: "📝", // Memo emoji for notes + ColorClass: "border-purple-500", + Raw: n, + } +} + +// MealToAtom converts a PlanToEat Meal to an Atom +func MealToAtom(m Meal) Atom { + // Meals don't have priority, default to low (1) + priority := 1 + + return Atom{ + ID: m.ID, + Title: m.RecipeName, + Description: m.MealType, // breakfast, lunch, dinner + Source: SourceMeal, + Type: TypeMeal, + URL: m.RecipeURL, + DueDate: &m.Date, // Meal date is effectively the "due date" + CreatedAt: time.Time{}, + Priority: priority, + SourceIcon: "🍽️", // Fork and knife emoji for meals + ColorClass: "border-green-500", + Raw: m, + } +} |
