Commit graph

19 commits

Author SHA1 Message Date
a523cb094e perf(browser): replace LIKE scans with FTS5; cache category counts
- Add migration 021: recipe_browser_fts FTS5 table on category + keywords
  columns, eliminating LIKE '%keyword%' full sequential scans on 3.1M rows
- _count_recipes_for_keywords now uses FTS5 MATCH (O(log N) vs O(N))
- browse_recipes reuses cached count, eliminating the second COUNT(*) scan
  per page request; ORDER BY r.id replaces the unindexed ORDER BY title sort
- Module-level _COUNT_CACHE keyed by (db_path, keywords) means domain-switch
  category counts are computed once per process lifetime

feat(find): dietary preset grid, Big 9 allergen pills, Hard Day Mode surface

- Dietary constraints replaced with toggle-button preset grid (8 options)
  + free-text "Other" field; removes dense freeform text input
- Allergies replaced with Big 9 pill picker (peanuts, tree nuts, shellfish,
  fish, milk, eggs, wheat, soy, sesame) + "Other" for custom entries
- Hard Day Mode surfaced as a standalone aria-pressed button above the
  dietary collapsible; no longer buried inside a collapsed section
- Active-state dot indicators on both collapsibles show filter engagement
  at a glance without expanding

fix(a11y): aria-describedby wiring for wildcard checkbox and tag inputs (#40)

- Persistent hint spans replace placeholder-only instructions for constraint
  and allergy fields (WCAG 3.3.2)

fix(browse): auto-select highest-count category on domain switch (#41)

- Eliminates the 3-decision cold start (domain → category → content)
- Surprise Me button added for zero-decision random navigation
2026-04-08 23:10:48 -07:00
4bb93b78d1 feat(ux/nd): collapse Find tab into level picker + two filter sections with active indicators 2026-04-08 22:51:43 -07:00
8c1443d156 fix(ux/nd): Browse tab auto-selects top category on mount; add Surprise Me button (closes #41) 2026-04-08 22:30:49 -07:00
001e46ba9a fix(a11y): add aria-describedby and persistent hints for wildcard checkbox and tag inputs (closes #40) 2026-04-08 22:27:28 -07:00
74a7c068bc feat(ux): rename creativity levels with descriptive labels and inline descriptions
- "Use What I Have" / "Allow Swaps" / "Get Creative" / "Surprise Me 🎲"
- Active level shows a one-liner description beneath the selector
- Hover tooltips repeat the description for mouse users
- Label changed from "Creativity Level" to "How far should we stretch?"
- Wildcard confirmation copy updated to match new framing
2026-04-08 15:03:51 -07:00
e203ad4bdc fix(a11y): WCAG accessibility improvements across frontend
- Global :focus-visible ring in theme.css — covers all interactive elements
  with keyboard-nav focus ring without affecting mouse/touch users
- Removed pulse-urgent animation (safety policy violation — infinite animation)
- Global prefers-reduced-motion guard suppresses all animations system-wide
- Added .sr-only utility class for screen-reader-only content
- Tab bar (RecipesView): role=tablist/tab/tabpanel + aria-selected + arrow key nav
- Modal focus management: trap focus on open, restore on close, Escape to dismiss
  (SaveRecipeModal, RecipeDetailPanel, SavedRecipesPanel new-collection dialog)
- aria-modal=true on all modal dialogs
- Icon-only buttons now have contextual aria-labels:
  chip-remove: "Remove constraint: vegetarian" / "Remove allergy: peanuts"
  bookmark: "Bookmark: {title}" / "Remove bookmark: {title}"
  dismiss: "Hide recipe: {title}"
  browser save toggle: "Save recipe: {title}" / "Edit saved recipe: {title}"
- InventoryList qty +/- buttons: aria-label="Increase/Decrease quantity"
- Quantity number inputs: aria-label="Quantity"
- Select elements (SavedRecipesPanel): labelled via .sr-only for-id pattern
- Star rating group: role=group + aria-labelledby; each star: aria-pressed
- Ingredient checkboxes: label wraps input + span (label association fix)
- aria-live="polite" announcer for dynamic recipe results count
- Dynamic aria-labels on status messages (role=alert/status + aria-live)
2026-04-08 14:35:18 -07:00
793df1b5cf feat: saved recipes, recipe browser, and recipe detail panel
- Saved recipes: save/unsave, star rating, notes, tags, collections (migrations 018-020)
- Recipe browser: domain/category browsing with pantry match badges, pagination
- Recipe detail panel: full directions, ingredient checklist, swap candidates, prep notes
- Grocery links: affiliate links for missing ingredients
- Nutrition filters and display chips on recipe cards
- Bookmark toggle persisted to saved_recipes table
- Tier gates on saved recipes (paid) and collections (premium)
- Browser telemetry for domain/category click tracking
- Cloud compose: CLOUD_DATA_ROOT volume mount for per-user SQLite trees
- manage.sh: cf-orch agent sidecar in local stack
- README: updated feature list and stack description
2026-04-08 14:35:02 -07:00
11a0d1f3a6 feat: handle household invite accept on app load via URL hash 2026-04-04 22:53:55 -07:00
7cce05b95a feat: household management UI in Settings (Premium-gated) 2026-04-04 22:51:03 -07:00
70b1319b60 feat: add householdAPI typed wrappers to api.ts 2026-04-04 22:48:25 -07:00
f63defa883 fix: mobile overflow — inv-row-right shrink + filter-chip-row width 2026-04-03 18:55:33 -07:00
6791ea22b2 feat: complete feedback button — status probe, requests dep, tests
- feedback.py: add GET /feedback/status endpoint (returns {enabled: bool})
  so frontend can probe on mount instead of optimistic-enable; remove
  unused get_db import
- FeedbackButton.vue: probe /feedback/status on mount, start hidden;
  drop redundant 503-hide path (status probe makes it redundant)
- pyproject.toml: declare requests>=2.31 (used by feedback.py Forgejo calls)
- tests/api/test_feedback.py: 7 tests — status endpoint (no-token, token,
  demo mode), POST 503/403, happy path with mocked Forgejo, 502 on error
2026-04-03 16:52:40 -07:00
61c16af754 feat: in-app feedback FAB + mobile safe-area-inset fixes
- FeedbackButton.vue — floating action button; fires POST /feedback if
  FORGEJO_API_TOKEN is configured, silently hidden otherwise
- feedback.py endpoint — creates Forgejo issues with beta-feedback +
  needs-triage labels; includes version, OS, device info
- App.vue: wire FeedbackButton; use env(safe-area-inset-bottom) for
  bottom-nav clearance (fixes clipped content on iPhone home-bar)
- style.css: overflow-x: hidden prevents stray elements expanding
  mobile viewport
2026-04-03 16:47:50 -07:00
6ff5a5709e fix: mobile nav FOUC + Pantry scan toggle responsive layout
- index.html: inline anti-FOUC CSS so sidebar stays hidden on mobile
  before the JS bundle hydrates (eliminates ~100ms flash of sidebar)
- InventoryList: scan-mode-toggle fills card width on <=480px so buttons
  don't overflow; mode button labels hidden on <=360px (icons only)
- Very narrow phones get icon-only mode toggle, all three buttons still
  accessible via aria-label
2026-04-03 11:10:39 -07:00
1f819c4ee0 feat(frontend): recipe UI — filters, dismissal, load more, prep notes, nutrition chips
- Style/category filter panel with active chip display
- Dismiss (excluded_ids) support — recipes don't reappear until next fresh search
- Load more appends next batch without full re-fetch
- Prep notes 'Before you start:' section above directions
- Nutrition macro chips (kcal, fat, protein, carbs, fiber, sugar, sodium)
- Composables extracted for reuse
2026-04-02 22:12:45 -07:00
b9eadcdf0e feat(frontend): warm organic design overhaul — Fraunces/DM fonts, saffron accent, compact inventory shelf view
- EditItemModal: replace all hardcoded colors (#eee, #f5f5f5, #2196F3, etc.) with CSS variable tokens; restyle modal header with display font, blur backdrop, and theme-aware form elements
- ReceiptsView: replace emoji headings, hardcoded spinner, and non-theme .button class with themed equivalents; all colors through var(--color-*) tokens
- RecipesView: fix broken --color-warning-rgb / --color-primary-rgb references (not defined in theme); use --color-warning-bg and --color-info-bg instead; apply section-title to heading
- SettingsView: apply section-title display-font class to heading for consistency
- InventoryList: remove three dead functions (formatDate, getDaysUntilExpiry, getExpiryClass) that caused TS6133 build errors
2026-04-01 22:29:55 -07:00
828efede42 fix: align frontend InventoryItem type with actual API response
InventoryItemResponse returns flat fields (product_name, barcode, category)
not a nested product object. Frontend interface and templates were using
item.product.name / item.product.brand which threw TypeError on render,
blanking the inventory tab.

- InventoryItem: remove product:Product, add product_name/barcode/category
- InventoryStats: fix total_products→available_items, expired→expired_items,
  items_by_location→locations
- item IDs are int not string — update deleteItem/updateItem/consumeItem sigs
- EditItemModal, RecipesView, InventoryList: fix all item.product.xxx refs
2026-04-01 17:30:21 -07:00
1e70b4b1f6 feat: recipe + settings frontend — Recipes and Settings tabs
- RecipesView: level selector (1-4), constraints/allergies tag inputs,
  hard day mode toggle, max missing input, expiry-first pantry extraction,
  recipe cards with collapsible swaps/directions, grocery links, rate
  limit banner
- SettingsView: cooking equipment tag input with quick-add chips, save
  with confirmation feedback
- stores/recipes.ts: Pinia store for recipe state + suggest() action
- stores/settings.ts: Pinia store for cooking_equipment persistence
- api.ts: RecipeRequest/Result/Suggestion types + recipesAPI + settingsAPI
- App.vue: two new tabs (Recipes, Settings), lazy inventory load on tab switch
2026-03-31 19:20:13 -07:00
8cbde774e5 chore: initial commit — kiwi Phase 2 complete
Pantry tracker app with:
- FastAPI backend + Vue 3 SPA frontend
- SQLite via circuitforge-core (migrations 001-005)
- Inventory CRUD, barcode scan, receipt OCR pipeline
- Expiry prediction (deterministic + LLM fallback)
- CF-core tier system integration
- Cloud session support (menagerie)
2026-03-30 22:20:48 -07:00