# Obsidian Pomodoro Forest & Homestead: Technical Specification (PRD) ## 1. System Architecture ``` +-----------------------------+ | Obsidian App / Workspace | +--------------+--------------+ | +-----------v-----------+ | PomodoroTimerPlugin | (Plugin Lifecycle & Commands) +-----------+-----------+ | +----------------------+----------------------+ | | +----------v----------+ +----------v----------+ | StorageManager | | Timer Engine | | (Atomic data.json) | | (clock.worker.ts) | +----------+----------+ +----------+----------+ | | +----------v----------+ | | Forest Engine |<---------------------------------+ | (Gamification/Flora)| (On start: seed, tick: grow, complete: harvest, abort: wither) +----+------+----+----+ | | | | | +------------------------+ | | | +----v------+---v----+ +----v--------------------+ | SoundManager | | ConfettiEngine | | (Web Audio Chimes, | | (Canvas Petals, Stars & | | Rain, Stream, Wind)| | Sparkles Celebration) | +--------------------+ +-------------------------+ | +----------v---------------------------------------------+ | Svelte Component Layer | | - TimerViewComponent (Dial + Growth Preview + Canvas) | | - ForestComponent (Grove + Homestead + Nursery + Stats)| | - HomesteadView (Full Workspace Leaf Tab) | | - HtmlExportService (Web Snapshot Generator) | +--------------------------------------------------------+ ``` ### Key Principles 1. **Single Source of Truth:** `StorageManager` encapsulates reading and writing `data.json`. Neither `Settings` nor `Forest` touches `plugin.saveData()` directly. This prevents data wipes. 2. **Offline-First & Zero Dependencies:** Everything operates strictly inside the vault with pure vector SVGs and native Web Audio synthesis. No external network requests or heavy binary assets required. 3. **Decoupled Growth State:** The active timer drives a normalized progress metric `growthProgress` (0.0 to 1.0) derived by the `Timer` store, allowing visual components to render exact growth stages reactively. 4. **Export Readiness:** `HtmlExportService` and `generateExportPayload()` produce standardized JSON schemas and self-contained HTML bundles that can be rendered independently in any web browser or uploaded to an online dashboard. --- ## 2. Core Game Loop The loop is designed to pull you back daily without punishing you for resting. | Action | Sunlight โ˜€๏ธ | Coins ๐Ÿช™ | XP | Other | |---|---|---|---|---| | Finish a focus session (25m) | ~30 (1.2/min) | ~2 (1 per 10m) | 1/min | +1 sapling of the species grown, +12 vitality | | Hyperfocus landing (โ‰ฅ10m) | 1.2/min (โ‰ค90m) + 0.6/min (>90m) | 1 per 10m (โ‰ค90m) + 1 per 20m (>90m) + check-ins | 1/min (โ‰ค90m) + 0.5/min (>90m) | +1 sapling, +12 vitality, fair pomodoro equivalents | | Harvest early (focused task done, โ‰ฅ5m) | 75% of 1.2/min | 75% of 1 per 10m | 0.75/min | Young tree in the grove, +4 vitality, no sapling | | First tree of the day | | | +10 | | | Reach the daily goal (setting, default 4) | +40 | +5 | +20 | | | Streak bonus | +2% per streak day (max +20%) | | | | | Check off a markdown task `[ ]`โ†’`[x]` | | 3 (+2 if it had ๐Ÿ…) | 5 | +2 vitality, max 30 rewarded/day, each task once/day | | Finish a Kanban card (ticked, or moved into a done lane) | | 3 (+2 focus, +2 on time) | 6 (+2/๐Ÿ… up to 8, +4 estimate on target, +2 lanes within limits) | Counts as a task; shares the 30/day cap and once/day rule | | Board milestone (10, 25, 50, 100, 250, 500 cards on one board) | 2 ร— cards | cards / 5 | cards | Once per board | | Clear a board (every card done, โ‰ฅ5 cards) | 30 | 4 | 25 | Once per board per day | | Finish a break | Tea Gazebo perk | | 3 | | | Harvest a ripe crop | | 2โ€“15 by crop | 3โ€“18 by crop | Plot is re-sown; counts as a village action | | Daily quest (3/day, deterministic by date) | 20โ€“35 | 3โ€“4 | 15โ€“25 | Auto-claimed | | Daily chest (all 3 quests) | 60 | 10 | 40 | + random unlocked sapling | | Achievement (25) | varies | varies | varies | | | Level up | 25 ร— level | 3 ร— level | | Unlocks species, buildings, biomes, land | - **Sunlight** comes from focus; **Coins** mostly from tasks and quests. Tasks are worth ticking off. - **Levels**: total XP to reach level *L* is `50ยท(Lโˆ’1)ยทL` (L2 = 100, L5 = 1000, L10 = 4500). - **Vitality (0โ€“100)** fades 15 per day with zero sessions, never below 10, slowed by the Hearth Campfire. It changes how the village looks: villagers, chimney smoke, fireflies, butterflies, saturation and mist. **Nothing is ever destroyed.** - **Streak**: breaks after a missed day unless the River Watermill has shields left this month. - **Withering** (setting): giving up a work session after the first minute leaves a withered tree in today's grove. The UI asks for confirmation first. A session whose focused task was checked off never withers. - **Early harvest** (setting, on by default): when the task the timer is focused on goes `[ ]`โ†’`[x]` during a work session (matched by its block id), the session can end early. From `min(5, session/2)` minutes it grants a `young` tree with `round(1.2ยทmยท0.75)` sunlight, `floor(m/10ยท0.75)` coins and `round(0.75ยทm)` XP for `m` focused minutes, with the streak and building percentages applied. No sapling, no `completedPomodoros`, no first-tree, daily-goal or `sessions` / `tagged_session` quest progress; it does check in the streak, add focus minutes, progress `minutes` and `linked_task` quests, and add +4 vitality. Before the minimum it ends with no tree. Past halfway it adds a ๐Ÿ… to the task and moves on to the break. Per minute it always pays less than finishing, so it can't be farmed with short tasks. - **Hyperfocus mode** (setting, on by default): an open-ended count-up timer tailored for ADHD flow. Starts standalone via command or cycles from standard focus via the "Ride the wave" prompt. - **Growth stages**: absolute minutes (seed: 0โ€“15m, sprout: 15โ€“40m, sapling: 40โ€“75m, mature: 75m+). Dial ring fills up smoothly to 1.0 at 90 minutes. - **Rewards**: full rate up to 90 minutes (`1.2ยทm` sunlight, `floor(m/10)` coins, `m` XP); beyond 90 minutes, additional time accrues at half rate (`0.6ยทm` sunlight, `1 per 20m` coins, `0.5ยทm` XP) with building/streak multipliers applied. - **Body check-in bonus**: acknowledging check-in prompts awards +1 Coin per check-in (capped at 6 Coins). - **Pomodoro equivalents**: awards `floor(m / workLen)` pomodoros towards daily goals, `completedPomodoros`, and focused task actual counts. - **Immunity from withering**: early termination or reset never withers the tree; stopping at โ‰ฅ10m lands and harvests the tree and sapling, while stopping <10m cancels cleanly. - **Breadcrumb logging**: captures an optional re-entry note recorded to markdown task sub-bullets and session logs. - **Garden plots** (`garden_plot`, max 6): each holds one crop in `cropId` / `cropGrowth` (focus minutes) on its `PlacedHomesteadItem`. Growth comes only from focus: `minutes ร— (1 + scarecrow %)` per finished session or early harvest, and `TASK_WATERING_MINUTES` (5) per rewarded task. Nothing grows with wall-clock time and **crops never wilt**; a ripe crop waits. Harvesting grants the crop's Coins and XP and re-sows the same crop at 0. A new plot starts with carrots, changing crop keeps the minutes grown (capped at the new crop's total), and stowing a plot harvests a ripe crop first. Slower crops pay slightly more per minute (0.04โ€“0.06 Coins/min), so patience is rewarded, but a single plot never out-earns the session that grows it. - **Village building**: every finished session puts a sapling of the tree you grew into your inventory. Purchases also go into inventory and are then placed on a tile. Items can be moved, stowed back to inventory and upgraded. Paths, brooks and decorations can be bought repeatedly. Land expands from 5ร—5 to 8ร—8. ## 3. Flora Catalog | Species ID | Name | Category | Level | Cost (โ˜€๏ธ / ๐Ÿช™) | Charm | |---|---|---|---|---|---| | `classic_pine` | Classic Pine | Tree | 1 | Free | 2 | | `sunflower` | Sunflower Grove | Flower | 1 | 60 / 5 | 2 | | `ancient_oak` | Ancient Oak | Tree | 2 | 120 / 10 | 3 | | `lavender` | Lavender Field | Flower | 3 | 150 / 12 | 2 | | `autumn_maple`| Autumn Maple | Tree | 4 | 220 / 18 | 3 | | `sakura` | Cherry Blossom | Tree | 5 | 260 / 22 | 4 | | `willow` | Weeping Willow | Tree | 6 | 300 / 25 | 4 | | `bamboo` | Bamboo Grove | Zen | 7 | 350 / 30 | 3 | | `bonsai` | Zen Bonsai | Zen | 9 | 450 / 40 | 5 | | `mushroom_circle` | Fairy Ring | Rare | 11 | 600 / 55 | 6 | | `golden_tree` | Celestial Gold Tree | Rare | 14 | 1000 / 90 | 10 | Placed trees can be nurtured up to โ˜…โ˜…โ˜… (40ยทlvl โ˜€๏ธ / 4ยทlvl ๐Ÿช™) and render larger. ## 4. Homestead Structures, Perks & Biomes Perks scale with level: `base + perLevel ร— (level โˆ’ 1)`. Upgrade cost: `0.6 ร— price ร— currentLevel`. Repeat copies of the same building do not stack; the strongest one counts. | Structure | Lvl | Cost | Unique | Max โ˜… | Perk (base / per level) | |---|---|---|---|---|---| | Hearth Campfire (starter) | 1 | 120 / 10 | โœ“ | 5 | Village fades 20% (+10%) slower | | Cobblestone Path | 1 | 15 / 1 | | 1 | โ€” (ground tile) | | Stone Lantern | 1 | 60 / 6 | | 3 | +10% (+5%) Sunlight 19:00โ€“05:00 | | Garden Bench | 1 | 80 / 8 | | 3 | +1 (+1) Coin per task | | Babbling Brook | 2 | 25 / 2 | | 1 | โ€” (water tile) | | Village Well | 2 | 200 / 20 | โœ“ | 5 | +10% (+5%) Coins per session | | Wooden Footbridge | 3 | 120 / 12 | | 1 | โ€” (sits on water) | | Tea Gazebo | 4 | 320 / 30 | โœ“ | 5 | +5 (+3) Sunlight per finished break | | Cozy Cabin | 5 | 450 / 45 | โœ“ | 5 | +10% (+5%) Sunlight per session | | Torii Gate | 6 | 400 / 40 | โœ“ | 5 | +20% (+10%) quest rewards | | River Watermill | 8 | 700 / 65 | โœ“ | 3 | 1 (+1) streak shield per month | | Glass Conservatory | 10 | 850 / 80 | โœ“ | 5 | +15% (+10%) XP | | Dutch Windmill | 12 | 800 / 80 | โœ“ | 5 | +20% (+10%) Sunlight on 45m+ sessions | | Garden Plot | 2 | 60 / 6 | max 6 | 1 | โ€” (soil tile that grows a crop) | | Hay Bale | 2 | 30 / 3 | | 1 | โ€” | | Friendly Scarecrow | 3 | 90 / 9 | | 3 | Crops grow 15% (+10%) faster | | Chicken Coop | 4 | 260 / 26 | โœ“ | 3 | +3 (+2) Coins with the first tree of the day; 1 + level hens | ### Crops | Crop ID | Name | Level | Focus minutes | Harvest (๐Ÿช™ / XP) | |---|---|---|---|---| | `carrot` | Carrots | 1 | 50 | 2 / 3 | | `tomato` | Tomatoes | 3 | 100 | 5 / 6 | | `wheat` | Golden Wheat | 5 | 150 | 8 / 10 | | `pumpkin` | Pumpkins | 8 | 250 | 15 / 18 | Land: 6ร—6 at level 3 (300/30), 7ร—7 at level 7 (700/70), 8ร—8 at level 12 (1400/140). ### Biomes (unlocked by level) - ๐ŸŒฟ **Emerald Meadow** (1): lush green turf with wildflowers. - ๐ŸŒธ **Sakura Grove** (3): pink petals on soft grass. - ๐Ÿ‚ **Autumn Valley** (5): amber earth with fallen leaves. - โ„๏ธ **Alpine Frost** (8): snowy ground and rocks. - ๐ŸŒŒ **Twilight Sanctuary** (12): indigo moss that always glows. ### Visuals The village and the daily grove render as an isometric floating island (`src/render/VillageScene.ts`). The sky follows the real time of day: dawn, day, dusk and night, with sun/moon, stars and drifting clouds. After dusk, lit windows and lanterns cast glows. Windmill sails, the watermill wheel, chimney smoke, flames, water and trees are animated with CSS. All animation stops with *Low animation frame rate* or `prefers-reduced-motion`. ### Villagers and hens The renderer stays a pure SVG string; anything that walks is added on top by `src/render/VillageLife.ts`, which the village tab feeds each rendered scene. - `src/render/villageGrid.ts` classifies every tile (grass, path, bridge, water, soil, object) and finds routes. Actors walk between the centres of side-by-side tiles over grass, paths and bridges only, with paths and bridges preferred, so they never cross a tree, building, plot or open water. - Scene objects and actors share one draw list sorted by `x + y` (`data-depth`). As an actor moves it is re-slotted between the objects, so it passes behind anything nearer the viewer. - Villagers (count from vitality and buildings) stroll, visit buildings and ripe plots, and favour the campfire at dusk and night. Hens (1 + coop level) stay within two tiles of the coop. Clicking one makes it hop and speak; villagers comment on today's trees, streak and ripe crops, and cheer on harvests, quests, chests, achievements and level-ups. Their lines only ever encourage. - Actor state lives in `VillageLife`, not the markup, so they carry on when the scene re-renders. The loop pauses while the scene is off screen. - With motion off, and in HTML snapshots, the renderer draws everyone standing (`standingActors`), sorted into the same draw list. ## 5. Smart Tag Auto-Detection Rules When a focus session starts, the plugin collects hashtags from the tracked task, its text, and the note (frontmatter + inline tags). The first tag mapped to an unlocked species picks the seed (and counts toward the "#tagged" quest): - `#code`, `#dev`, `#programming` $\to$ `classic_pine` - `#write`, `#writing`, `#draft`, `#blog` $\to$ `sakura` - `#study`, `#reading`, `#research` $\to$ `ancient_oak` - `#zen`, `#meditate`, `#review` $\to$ `bonsai` - `#creative`, `#design`, `#art` $\to$ `autumn_maple` - `#sprint`, `#quick`, `#urgent` $\to$ `sunflower` --- ## 6. Dataview-Compatible Daily Note Format When enabled in settings, completing a Pomodoro appends the following structured inline-field markdown to the daily note: ```markdown - ๐ŸŒฒ **Pomodoro Forest** (14:35): [[Task Name]] ยท *Cherry Blossom* ยท [duration:: 25m] [tree:: sakura] [sunlight:: +30] [coins:: +2] [status:: completed] ``` Users can query their entire focus history across daily notes via Dataview: ```dataview TABLE duration, tree, sunlight, coins FROM "Daily Notes" WHERE tree SORT file.name DESC ``` --- ## 7. Web Dashboard Export Schema ```json { "$schema": "https://obsidian-pomodoro-forest.dev/schemas/v1/export.json", "exportTimestamp": 1727617000000, "pluginVersion": "2.0.0", "activeBiome": "sakura_garden", "streak": { "current": 14, "longest": 21, "lastCheckInDate": "2026-09-29" }, "lifetimeStats": { "totalFocusMinutes": 2450, "totalPomodoros": 98, "treesGrown": 94, "treesWithered": 4 }, "homestead": [ { "id": "b_1", "itemType": "building", "itemId": "cabin", "gridX": 2, "gridY": 2, "level": 2 }, { "id": "t_1", "itemType": "tree", "itemId": "sakura", "gridX": 1, "gridY": 2, "level": 1 } ], "dailyLogsSummary": [ { "date": "2026-09-29", "totalMinutes": 75, "completedPomodoros": 3, "trees": [ { "id": "tree_101", "speciesId": "sakura", "plantedAt": "2026-09-29T10:30:00.000Z", "durationMinutes": 25, "status": "mature", "taskText": "Draft Architecture RFC" } ] } ] } ``` ## 8. Kanban Boards Boards are notes in the [Kanban plugin](https://github.com/mgmeyers/obsidian-kanban)'s markdown format, so both plugins read and write the same files. | Part | Format | |---|---| | Board marker | `kanban-plugin: board` (or `basic`, `list`, `table`) in the frontmatter | | Lane | A heading (`## Title`), with an optional card limit: `## Doing (3)` | | Complete lane | `**Complete**` right under the heading (translated like the Kanban plugin: `**Fertiggestellt**`, `**ๅฎŒไบ†**`โ€ฆ); cards moved in are ticked, moved out are unticked | | Card | A top-level list item, `- [ ] text`, with its other lines indented under it (tab or 4 spaces, from the vault's *Use tabs* setting) | | Card id | A block id at the end of the first line, `^a1b2c3` | | Dates | `@{YYYY-MM-DD}` and `@@{HH:mm}` (triggers and date format from the board's settings), or Tasks / Dataview fields | | Archive | `***`, then `## Archive` and its cards | | Board settings | `%% kanban:settings` + a JSON code block at the end; `list-collapse`, `lane-width`, `date-trigger`, `time-trigger`, `date-format` and `new-card-insertion-method` are honoured | **Editing.** `src/board/BoardModel.ts` parses a note into line ranges and every edit returns the new text with only the touched lines changed. It has no Obsidian dependency and is unit-tested against the Kanban plugin's output. **Opening.** `BoardOpening` wraps `WorkspaceLeaf.setViewState` once the layout is ready (after the Kanban plugin's own wrapper, so it runs first). A board note opened as `markdown` (or as `kanban`, with *Open Kanban boards in* set to *Forest board*) opens as `pomodoro-forest-board` when the setting allows it; switching a leaf that already shows the note is never redirected. On *Automatic* nothing is redirected while the Kanban plugin is enabled. **Rewards.** `BoardWatcher` snapshots a board when it is opened (in any view) and diffs every `metadataCache` change: a card is finished when it was open and is now ticked or in a done lane, *and* it moved or its checkbox changed. Cards are matched by block id, else by their first line without tracking fields; a card whose text changed in place keeps its memory. `TaskRewardWatcher` skips a board's top-level cards (checklist items inside cards stay tasks). **Lane roles.** `done` for the Complete marker; otherwise from the title (multilingual word lists) or a per-board override in `gamification.boards[path].laneRoles`. **Memory** (`gamification.boards`, by note path): ```json { "Projects/Website relaunch.md": { "title": "Website relaunch", "cards": { "^onb1": { "lane": "In progress", "since": "2026-10-05", "seen": "2026-09-28", "started": "2026-10-05" } }, "shipped": 44, "history": [{ "date": "2026-10-06", "days": 2, "actual": 3, "expected": 3, "onTime": true }], "milestones": [10, 25], "laneRoles": { "Waiting on client": "active" }, "clearedOn": "2026-09-30" } } ``` `history` keeps the last 60 finished cards. `cards` only holds cards still on the board and is written only when a card appears, moves or disappears.