summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-01-13 08:38:39 -1000
committerPeter Stone <thepeterstone@gmail.com>2026-01-13 08:38:39 -1000
commit043f48c12eb4dfc410e8724b430166000d7cb905 (patch)
tree2d67af6a531bc1ad76bcf54641d1f49b36ab094b
parent06c7485a7d05de86f9898e388161e8d932d5f3e6 (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.md114
-rw-r--r--SESSION_STATE.md13
-rw-r--r--internal/models/atom.go135
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,
+ }
+}