diff --git a/PRD.md b/PRD.md index 7d14cde..f466383 100644 --- a/PRD.md +++ b/PRD.md @@ -1,11 +1,11 @@ -# Minabot — Habit Tracking PRD +# Mina Habits — Habit Tracking PRD Status: Initial product requirements Date: 2026-09-04 ## Application intent -Minabot helps people record daily habit progress and understand their consistency through GitHub-style calendar heatmaps. Users can inspect each habit independently and combine selected habits into shared charts. +Mina Habits helps people record daily habit progress and understand their consistency through GitHub-style calendar heatmaps. Users can inspect each habit independently and combine selected habits into shared charts. Individual charts answer, “How much progress did I make on this habit?” Combined charts answer, “How many of the habits due that day did I fully complete?” Partial progress is visible and useful, but does not count as a completed habit in a combined chart. diff --git a/README.md b/README.md index 58dd21c..5abc22a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Minabot +# Mina Habits Bun + Hono habit-tracking REST API with Drizzle ORM and local SQLite. The React Router client includes a homepage for signed-out visitors and a personal habit dashboard. @@ -67,7 +67,9 @@ its own recurrence. Both habit and task schedules support daily, selected weekda day intervals, and week intervals with an editable start date and weekday. Archive asks for confirmation in a modal and preserves earlier history. **Archived habits** opens the archive modal, with history inspection and restoration -starting today. Task deletion uses the same modal confirmation. Tasks +starting today. **Delete permanently** removes an archived habit and all its history +after confirmation; it also removes the habit from combined charts and deletes +charts left empty. Task deletion uses the same modal confirmation. Tasks remain manageable on days off, while their checkboxes stay disabled. Settings, sharing, new habits, and historical check-ins also open in native dialogs. Dialogs keep the background inactive, contain keyboard focus, support Escape and @@ -85,16 +87,18 @@ Combined-chart management is not exposed on this homepage. The dashboard opens with a compact greeting and a Today filter. All habits includes routines on days off; creating a habit or editing one out of today’s schedule keeps -it reachable in All habits. Saved calendars expand through **View history**, with -historical corrections available inside the expanded view. Selecting a square +it reachable in All habits. Every habit shows its calendar immediately, starting with three months and offering +longer ranges. Historical corrections are available by selecting a day. Selecting a square only changes the inspected day; it preserves the date range and number of weeks, including on untracked, unscheduled, and future dates. An external editor date outside a preset window can still bring that date into view. -Each saved habit uses its own action layout: count goals have a prominent counter -and unit inside a tinted panel, simple check-ins use a compact checkbox-and-heading -row, and routines use a bordered checklist with a progress header. Daily schedule labels and -duplicate progress copy are omitted; days off stay explicit. +Each saved habit shares a serif heading, type label, color marker, schedule, and +explicit completion status. A visible graph sits beside a neutral completion panel: +count goals show their target, unit, and editable counter; check-ins show a labeled +checkbox; routines show an open task list and progress. On narrow screens, completion +controls stack above the graph. Spacing, typography, and neutral surfaces use the +shared design-system tokens; habit color marks identity and progress. Count totals can be typed directly and saved with **Save** or Enter. Escape restores the recorded total; plus and minus remain available. Counts may exceed the target. diff --git a/bun.lock b/bun.lock index 1b73201..bcfd0c4 100644 --- a/bun.lock +++ b/bun.lock @@ -3,7 +3,7 @@ "configVersion": 1, "workspaces": { "": { - "name": "bun-react-template", + "name": "mina-habits", "dependencies": { "@nivo/calendar": "^0.99.0", "bun-plugin-tailwind": "^0.1.2", diff --git a/docs/API.md b/docs/API.md index a245d3e..5845449 100644 --- a/docs/API.md +++ b/docs/API.md @@ -20,6 +20,7 @@ Run `bun dev` to migrate the local database and serve the API. Run `bun run test | GET | `/habits/:id` | Current habit configuration and revision ID | | PATCH | `/habits/:id` | Change name, method, method settings, schedule or archived flag | | DELETE | `/habits/:id` | Archive from today, preserving history; 204 | +| DELETE | `/habits/:id/permanent` | Permanently delete an owned archived habit and all its history; 204. Active habits return 409. Remove it from combined charts and delete charts left empty. | | GET | `/habits/:id/history` | `{revisions: [...]}` newest first, including superseded same-day revisions | | GET | `/habits/:id/tasks` | `{tasks: [...]}` for current task method | | POST | `/habits/:id/tasks` | Add task; 201 | diff --git a/docs/design/imagegen-prompts.md b/docs/design/imagegen-prompts.md index a583bb7..fea921a 100644 --- a/docs/design/imagegen-prompts.md +++ b/docs/design/imagegen-prompts.md @@ -1,4 +1,4 @@ -# Minabot visual studies +# Mina Habits visual studies Generated with the built-in imagegen tool on 2026-09-04 from PRD.md. These are visual references, not data-accurate calendar renders. Two independent generations; no input images. The no-card, monochromatic editorial layouts informed the scoped design-system stylesheet and reusable components. Fonts are bundled locally under src/assets/fonts with their OFL license. @@ -6,13 +6,13 @@ Generated with the built-in imagegen tool on 2026-09-04 from PRD.md. These are v Saved asset: `src/assets/design/today.png` -Use case: ui-mockup. Asset type: high-fidelity desktop web app design reference for Minabot, a habit tracker. Ultra-minimalistic flat design, pure white background, black typography, only neutral grays for secondary labels and calendar intensities. Instrument Serif regular and italic for large editorial headings, restrained sans-serif for controls and metadata. NO CARDS, no bordered panels, no rounded containers, no shadows, no gradients, no illustrations, no streaks, no rewards. Very generous whitespace, thin horizontal hairline dividers, tiny square checkbox controls, sharp rectangular black primary buttons. Wide landscape screenshot, clean browser-free canvas. Simple header with serif wordmark "minabot" and links "Today", "Habits", "Charts". Primary request: Today view. Large heading "A little, every day." and small date "Friday, September 4". Below a horizontal rule, title "Today" with right aligned "1 of 3 complete". Three spacious open habit rows: "Drink water" with caption "Daily · 8 glasses" and minus 7 / 8 plus stepper; "Read a little" with caption "Daily · Mark as done" and checked black checkbox; "Evening reset" with caption "Daily · 2 of 3 tasks" and nested task checkboxes "Clear desk" checked, "Plan tomorrow" checked, "Stretch" unchecked. Lower open section "Your rhythm" with small GitHub-style calendar grid of square cells, neutral grayscale intensity and month labels. Caption "Only fully completed habits count. Today: 1 of 3." No cards anywhere. Crisp elegant readable interface. +Use case: ui-mockup. Asset type: high-fidelity desktop web app design reference for Mina Habits, a habit tracker. Ultra-minimalistic flat design, pure white background, black typography, only neutral grays for secondary labels and calendar intensities. Instrument Serif regular and italic for large editorial headings, restrained sans-serif for controls and metadata. NO CARDS, no bordered panels, no rounded containers, no shadows, no gradients, no illustrations, no streaks, no rewards. Very generous whitespace, thin horizontal hairline dividers, tiny square checkbox controls, sharp rectangular black primary buttons. Wide landscape screenshot, clean browser-free canvas. Simple header with serif wordmark "Mina Habits" and links "Today", "Habits", "Charts". Primary request: Today view. Large heading "A little, every day." and small date "Friday, September 4". Below a horizontal rule, title "Today" with right aligned "1 of 3 complete". Three spacious open habit rows: "Drink water" with caption "Daily · 8 glasses" and minus 7 / 8 plus stepper; "Read a little" with caption "Daily · Mark as done" and checked black checkbox; "Evening reset" with caption "Daily · 2 of 3 tasks" and nested task checkboxes "Clear desk" checked, "Plan tomorrow" checked, "Stretch" unchecked. Lower open section "Your rhythm" with small GitHub-style calendar grid of square cells, neutral grayscale intensity and month labels. Caption "Only fully completed habits count. Today: 1 of 3." No cards anywhere. Crisp elegant readable interface. ## Habit detail Saved asset: `src/assets/design/habit-detail.png` -Use case: ui-mockup. Asset type: high-fidelity desktop web app design reference for Minabot, a habit tracker. Ultra-minimalistic flat design, pure white background, black typography, only neutral grays for secondary labels and calendar intensities. Instrument Serif regular and italic for large editorial headings, restrained sans-serif for controls and metadata. NO CARDS, no bordered panels, no rounded containers, no shadows, no gradients, no illustrations, no streaks, no rewards. Very generous whitespace, thin horizontal hairline dividers, tiny square checkbox controls, sharp rectangular black primary buttons. Wide landscape screenshot, clean browser-free canvas. Simple header with serif wordmark "minabot" and links "Today", "Habits", "Charts". Primary request: Individual habit detail view. Breadcrumb "Habits / Drink water". Large Instrument Serif heading "Drink water" and small subtitle "Eight glasses. One day at a time." Right aligned text action "Edit habit". Open ruled section with label "Friday, September 4", huge serif "7 / 8" with unit "glasses", minus and plus buttons, text "In progress · 1 glass to go". Next open section "Progress" with GitHub-style 7-row square-cell calendar across Jan–Sep 2026. 8 distinct positive gray steps plus empty due state; not scheduled cells are outlined, future cells faint. Below calendar an inline selected-date inspector "September 4, 2026 · 7 of 8 glasses · Incomplete" and explanatory caption "Partial progress is visible here. This habit counts in a combined chart only at 8 of 8." Bottom thin-rule section "Schedule" with "Every day" and "Changes apply from today. Earlier requirements stay the same." NO cards or boxed panels. Crisp elegant readable interface. +Use case: ui-mockup. Asset type: high-fidelity desktop web app design reference for Mina Habits, a habit tracker. Ultra-minimalistic flat design, pure white background, black typography, only neutral grays for secondary labels and calendar intensities. Instrument Serif regular and italic for large editorial headings, restrained sans-serif for controls and metadata. NO CARDS, no bordered panels, no rounded containers, no shadows, no gradients, no illustrations, no streaks, no rewards. Very generous whitespace, thin horizontal hairline dividers, tiny square checkbox controls, sharp rectangular black primary buttons. Wide landscape screenshot, clean browser-free canvas. Simple header with serif wordmark "Mina Habits" and links "Today", "Habits", "Charts". Primary request: Individual habit detail view. Breadcrumb "Habits / Drink water". Large Instrument Serif heading "Drink water" and small subtitle "Eight glasses. One day at a time." Right aligned text action "Edit habit". Open ruled section with label "Friday, September 4", huge serif "7 / 8" with unit "glasses", minus and plus buttons, text "In progress · 1 glass to go". Next open section "Progress" with GitHub-style 7-row square-cell calendar across Jan–Sep 2026. 8 distinct positive gray steps plus empty due state; not scheduled cells are outlined, future cells faint. Below calendar an inline selected-date inspector "September 4, 2026 · 7 of 8 glasses · Incomplete" and explanatory caption "Partial progress is visible here. This habit counts in a combined chart only at 8 of 8." Bottom thin-rule section "Schedule" with "Every day" and "Changes apply from today. Earlier requirements stay the same." NO cards or boxed panels. Crisp elegant readable interface. ## Interactive implementation @@ -42,4 +42,4 @@ The updated Today preview uses four independent habit charts in a two-column, mu New saved asset: `src/assets/design/today-v2.png`. Original `today.png` is preserved; the gallery now uses v2. Built-in imagegen prompt: -Use case: ui-mockup. Asset type: high fidelity desktop Minabot habit tracker Today view. Ultra minimalistic flat white website. Instrument Serif regular and italic headings with small system sans labels. Thin horizontal rules, ample whitespace. NO cards, no outer borders around chart groups, no shadows, no rounded panels. Header serif wordmark "minabot", links "Today", "Habits", "Charts". Large title "A little, every day." Date "Friday, September 4". Below show FOUR INDIVIDUAL HABIT CALENDARS in a TWO COLUMN, TWO ROW GRID. Each habit owns its separate GitHub-style 7-row calendar with 13 weeks of square cells, Jun/Jul/Aug/Sep labels. Each calendar has ONE DISTINCT COLOR with intensity shades: Drink water muted blue, Read a little warm ochre, Evening reset muted violet, Move a little muted green. All remaining UI black and white. Top left: Drink water, "7 of 8 glasses", minus/plus logging, blue calendar. Top right: Read a little, "Complete", checked checkbox, ochre calendar. Bottom left: Evening reset, "2 of 3 tasks", violet calendar, accordion labeled "Tasks for today" expanded underneath revealing checked "Clear desk", checked "Plan tomorrow", unchecked "Stretch". Bottom right: Move a little, "10 of 20 minutes", minus/plus logging, green calendar. Separate chart groups with whitespace and top horizontal rules only; absolutely no card containers. Precise editorial elegant layout. At bottom small note "Each chart shows its own progress. Only fully completed habits count in combined charts." Render clean landscape screenshot, no browser frame. +Use case: ui-mockup. Asset type: high fidelity desktop Mina Habits habit tracker Today view. Ultra minimalistic flat white website. Instrument Serif regular and italic headings with small system sans labels. Thin horizontal rules, ample whitespace. NO cards, no outer borders around chart groups, no shadows, no rounded panels. Header serif wordmark "Mina Habits", links "Today", "Habits", "Charts". Large title "A little, every day." Date "Friday, September 4". Below show FOUR INDIVIDUAL HABIT CALENDARS in a TWO COLUMN, TWO ROW GRID. Each habit owns its separate GitHub-style 7-row calendar with 13 weeks of square cells, Jun/Jul/Aug/Sep labels. Each calendar has ONE DISTINCT COLOR with intensity shades: Drink water muted blue, Read a little warm ochre, Evening reset muted violet, Move a little muted green. All remaining UI black and white. Top left: Drink water, "7 of 8 glasses", minus/plus logging, blue calendar. Top right: Read a little, "Complete", checked checkbox, ochre calendar. Bottom left: Evening reset, "2 of 3 tasks", violet calendar, accordion labeled "Tasks for today" expanded underneath revealing checked "Clear desk", checked "Plan tomorrow", unchecked "Stretch". Bottom right: Move a little, "10 of 20 minutes", minus/plus logging, green calendar. Separate chart groups with whitespace and top horizontal rules only; absolutely no card containers. Precise editorial elegant layout. At bottom small note "Each chart shows its own progress. Only fully completed habits count in combined charts." Render clean landscape screenshot, no browser frame. diff --git a/docs/design/mina-habits-icon.md b/docs/design/mina-habits-icon.md new file mode 100644 index 0000000..b1d3c4f --- /dev/null +++ b/docs/design/mina-habits-icon.md @@ -0,0 +1,13 @@ +# Mina Habits favicon + +Generated with the built-in imagegen tool. Master: `mina-habits-icon.png`. +The application uses 32px and 180px PNG derivatives in `src/assets/`. + +## Prompt + +Use case: logo-brand +Asset type: production square favicon for Mina Habits, a minimal editorial habit tracker. +Primary request: a single bold, elegant uppercase M monogram, warm white on a solid near-black square background. Match a quiet serif wordmark and minimalist black-and-white interface. Strong thick strokes and subtle sturdy serifs, highly readable at 16x16 pixels. +Composition: centered M fills about 75% of square width and height, balanced narrow padding. Flat crisp 2D artwork, square canvas, no mockup. +Text (verbatim): "M" +Constraints: one icon only; no other letters, no caption, no gradients, shadows, texture, borders or decorative objects. diff --git a/docs/design/mina-habits-icon.png b/docs/design/mina-habits-icon.png new file mode 100644 index 0000000..3d5c656 Binary files /dev/null and b/docs/design/mina-habits-icon.png differ diff --git a/docs/discord-sharing-design.md b/docs/discord-sharing-design.md new file mode 100644 index 0000000..47fdf25 --- /dev/null +++ b/docs/discord-sharing-design.md @@ -0,0 +1,21 @@ +# Discord sharing design + +The shared image uses a separate, vertically stacked GitHub-style calendar for each selected habit. Calendar geometry comes from the dashboard's Sunday-first timeline helper; colors and partial progress come directly from the sharing API. Each total describes the entire selected date range. Days off remain dots and do not enter the denominator. + +Short ranges pair a large period total with the calendar. Long ranges use the full image width for the uninterrupted timeline. Titles, calendars and legends occupy separate vertical regions, including at the tallest seven-row grid size. Text truncates within explicit bounds instead of colliding with neighboring counts. Preview, download and Discord use the same PNG, preserving the server's 1200px export contract. + +The modal puts the image first, shows the Discord destination before sending, exposes quick period choices and keeps privacy details in an expandable section. The footer remains available during scrolling. Its native dialog retains keyboard focus containment, including the privacy disclosure. The thumbnail view uses a 360px image width; actual Discord sizing varies with the client and attachment aspect ratio. Cards containing many stacked calendars will require opening the image to inspect fine daily details. + +## Research + +- [GitHub: Introducing Contributions](https://github.blog/news-insights/product-news/introducing-contributions/): the calendar itself provides the recognizable visual history. +- [NN/g: Progressive Disclosure](https://www.nngroup.com/articles/progressive-disclosure/): defer optional settings while keeping the primary task clear. +- [Strava: Sharing Your Activities](https://support.strava.com/en-us/articles/15401840-sharing-your-strava-activities): a purpose-built image preview with a focused sharing action. + +## Verification + +Run `bun run typecheck`, `bun test` and `bun run build`. + +For disposable browser testing, run `SHARE_PREVIEW=1 bun scripts/dashboard-preview.ts` and open http://127.0.0.1:3107/__preview/login. This uses a temporary database and simulated Discord delivery, with no external posts. + +Run `bun scripts/share-card-preview.ts` for the visual fixtures at http://127.0.0.1:3108. These cover long names, six calendars, privacy and no scheduled days at 320px, with a 390px iframe for mobile modal checks. Rendering tests cover date geometry, leap days, original shades, days off and whole-period totals. diff --git a/package.json b/package.json index 351601f..55a5bc1 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "minabot", + "name": "mina-habits", "version": "0.1.0", "private": true, "type": "module", diff --git a/scripts/dashboard-preview.ts b/scripts/dashboard-preview.ts index b23f80f..3a8fe4b 100644 --- a/scripts/dashboard-preview.ts +++ b/scripts/dashboard-preview.ts @@ -6,7 +6,8 @@ import { createApi } from "../src/api"; import index from "../src/index.html"; const f = fixture(); -const origin = "http://127.0.0.1:3107"; +const port = Number(process.env.PREVIEW_PORT ?? 3107); +const origin = `http://127.0.0.1:${port}`; const previewCookie = "minabot_dashboard_preview_session"; const sharingPreview = process.env.SHARE_PREVIEW === "1"; let sharedImage: Blob | null = null; @@ -52,7 +53,7 @@ if (sharingPreview) setInterval(async () => { }, 1000).unref(); const server = Bun.serve({ hostname: "127.0.0.1", - port: 3107, + port, routes: { "/__preview/reminder": () => new Response(reminderMessage ?? "No reminder sent"), "/__preview/shared-image": () => sharingPreview && sharedImage ? new Response(sharedImage) : new Response(null, { status: 404 }), diff --git a/scripts/share-card-preview.html b/scripts/share-card-preview.html new file mode 100644 index 0000000..2b82cb3 --- /dev/null +++ b/scripts/share-card-preview.html @@ -0,0 +1 @@ +Sharing visual QA
diff --git a/scripts/share-card-preview.ts b/scripts/share-card-preview.ts new file mode 100644 index 0000000..bb92766 --- /dev/null +++ b/scripts/share-card-preview.ts @@ -0,0 +1,5 @@ +// Run with: bun scripts/share-card-preview.ts +// Optional mobile iframe: SHARE_PREVIEW=1 bun scripts/dashboard-preview.ts +import page from "./share-card-preview.html"; +const server = Bun.serve({ hostname: "127.0.0.1", port: 3108, routes: { "/*": page }, development: true }); +console.log(`Sharing visual fixtures: ${server.url}`); diff --git a/scripts/share-card-preview.tsx b/scripts/share-card-preview.tsx new file mode 100644 index 0000000..8f1a3b2 --- /dev/null +++ b/scripts/share-card-preview.tsx @@ -0,0 +1,30 @@ +// Disposable visual fixtures: no user data and no outbound Discord calls. +import { ThemeProvider, ThemeControl, useTheme } from "../src/components/ThemeProvider"; +import { createRoot } from "react-dom/client"; +import { useEffect, useState } from "react"; +import { addDays } from "../src/habits/calendar"; +import { renderProgressCard } from "../src/lib/progress-card"; +import type { ShareData } from "../src/sharing/contracts"; +import "../styles/globals.css"; + +const names = ["A few pages, every day", "An afternoon walk", "A very long habit name that should truncate gracefully without hitting the count", "Drink enough water", "练习中文 · 日本語", "Make time to unwind"]; +const colors = ["#90647e", "#397f76", "#ba793c", "#4b70b4", "#834f9e", "#bd5863"]; +function Preview({ count, empty = false, privateCard = false }: { count: number; empty?: boolean; privateCard?: boolean }) { + const { theme } = useTheme(); + const [url, setUrl] = useState(""); + useEffect(() => { + let disposed = false, objectUrl = ""; + const total = count === 6 ? 365 : 30; + const from = addDays("2026-09-04", 1 - total); + const data: ShareData = { from, to: "2026-09-04", timezone: "America/Argentina/Buenos_Aires", habits: names.slice(0, count).map((name, i) => { + const days = Array.from({length: total}, (_, n) => { const due = !empty && (n + i) % 7 !== 0; const ratio = due ? (n % 4 === 0 ? 0 : n % 3 === 0 ? .5 : 1) : null; return { date: addDays(from, n), due, complete: ratio === 1, ratio, color: ratio === 1 ? colors[i]! : ratio ? "#c7b0bf" : "#e4e7e0" }; }); + return { name, color: colors[i]!, completed: days.filter(d => d.complete).length, scheduled: days.filter(d => d.due).length, days, legend: { empty: "#e4e7e0", partial: ["#c7b0bf"], complete: colors[i]! } }; + }) }; + void renderProgressCard(data, { id: "qa", discordId: "qa", username: "alice", displayName: "Alexandra with a very long display name", avatarUrl: null, timezone: data.timezone }, { name: !privateCard, avatar: !privateCard, habitNames: !privateCard, timezone: privateCard }, theme).then(result => { + if (disposed) return; objectUrl = URL.createObjectURL(result.blob); setUrl(objectUrl); + }); + return () => { disposed = true; if (objectUrl) URL.revokeObjectURL(objectUrl); }; + }, [theme]); + return

{count} {count === 1 ? "habit" : "habits"}{empty ? " · no schedule" : ""}{privateCard ? " · private" : ""}

{url && QA progress card at 320 pixels}
; +} +createRoot(document.getElementById("root")!).render(

Card readability at 320px

390 × 844 mobile composer