Commit graph

56 commits

Author SHA1 Message Date
6741c6981d fix(kiwi-a11y): chip-remove touch targets, btn-link undo target, star rating label, notes aria-expanded 2026-04-15 10:12:17 -07:00
5c135d0860 fix(kiwi-a11y): undo toast for 'I cooked this' dismiss action (#45) 2026-04-15 10:06:31 -07:00
bc04739447 fix(kiwi-a11y): SavedRecipesPanel empty state, remove confirmation, notes expand (#43 #44 #48) 2026-04-15 10:01:41 -07:00
ceb03f8b5b fix(kiwi-a11y): ND/calm-UX policy fixes — deficit language, wildcard styling, depletion framing (#42 #46 #47 #80-M)
- #42: Replace deficit framing — "You'd need:" → "To complete this recipe:", element_gaps
  card-warning → card-secondary, missing/gap chips status-warning → status-info,
  "Your pantry is missing..." → "These would expand your options:"
- #46: Add activeNutritionFilterCount computed; show count in Advanced filters summary
  when filters are active so it's visible while collapsed
- #47: Wildcard confirmation status-warning → status-info, copy updated to calm framing;
  wildcard recipe card badge status-warning → status-info
- M1: Add re-search hint below Hard Day Mode toggle when results are already showing
- M8: Move swap candidates collapsible to after directions/steps section
- L2: Add autocomplete="off" to filter search, constraint, and allergy text inputs
- L5: Add title="This is an affiliate link" disclosure to grocery affiliate links

Items already correct (no change needed):
- M2: Level description already always visible via activeLevel computed
- M3: Rate limit copy already using calm framing
- M5: No-results copy already calm
- M6: levelLabels already uses full names
- M7: "that's part of the fun" was part of the wildcard copy fixed under #47
- L1: Neon/konami handler not present in this file
2026-04-15 09:57:48 -07:00
9de42c3088 fix(kiwi-a11y): tab focus, silent fail, emoji labels, form for/id pairs (H3-H8, #80) 2026-04-15 09:53:15 -07:00
41837f348c fix(kiwi-a11y): darken light-mode muted text to #7a5c2e for WCAG 1.4.3 AA (H1, #80) 2026-04-15 09:51:26 -07:00
4de4f63614 fix(kiwi-a11y): btn-icon touch targets; aria-busy loading; role=alert on error (C4-C6, #80) 2026-04-15 09:48:19 -07:00
391e79ac86 fix(kiwi-a11y): deep watchers for constraint/allergy persistence (#54) 2026-04-15 09:43:54 -07:00
91724caf96 fix(kiwi-a11y): persist constraint and allergy preferences to localStorage (#54) 2026-04-15 09:42:13 -07:00
fdc477b395 fix(kiwi-fe): MealPlanView strict TS split index type narrowing 2026-04-14 15:55:38 -07:00
33c619b6b5 feat(kiwi-fe): wire OrchUsagePill into RecipesView and Settings opt-in toggle
- Import and mount OrchUsagePill near the recipe level selector in RecipesView;
  pill is self-hiding when not enabled or no lifetime key is present
- Add useOrchUsage composable to SettingsView with a Display section checkbox
  so users can opt in to seeing the cloud recipe call budget pill
- Add @/ path alias to vite.config.ts and tsconfig.app.json to resolve the
  existing @/services/api import in useOrchUsage.ts (fixes vite build error)
- tsc --noEmit and vite build both pass clean
2026-04-14 15:51:34 -07:00
1ae54c370d feat(kiwi-fe): add OrchUsagePill component with calm low-budget state 2026-04-14 15:46:58 -07:00
b4f8bde952 feat(kiwi-fe): add useOrchUsage composable with opt-in localStorage persistence 2026-04-14 15:46:12 -07:00
bdfbc963b7 feat(kiwi-fe): add getOrchUsage API call and OrchUsage type 2026-04-14 15:45:22 -07:00
9941227fae chore: merge main into feature/meal-planner
Resolves three conflicts:
- app/api/routes.py: fixed saved_recipes-before-recipes ordering from main;
  meal_plans and community_router from feature branch
- app/db/store.py: meal plan/prep session methods (feature) + community
  pseudonym methods (main) -- both additive
- app/tiers.py: KIWI_BYOK_UNLOCKABLE includes meal_plan_llm,
  meal_plan_llm_timing (feature) and community_fork_adapt (main)
2026-04-14 14:53:52 -07:00
3933136666 fix: save, shopping list, and route ordering for Build Your Own
- Persist built recipes to recipes table on /build so they get real DB IDs
  and can be bookmarked via saved_recipes (FK was pointing at negative IDs)
- Populate missing_ingredients in build_from_selection() from role_overrides
  vs pantry diff -- backend now owns shopping list computation
- Remove client-side cartItems tracking; shopping list derived from
  builtRecipe.missing_ingredients instead
- Fix saved_recipes 422: mount saved_recipes router before recipes router in
  routes.py so /recipes/saved isn't captured by /recipes/{recipe_id}
- Bump SaveRecipeModal z-index to 500 (above detail-overlay at 400)
- Replace "Add to pantry" primary action with "Grocery list" clipboard copy;
  "Add to pantry" demoted to compact secondary button
2026-04-14 14:48:30 -07:00
1882116235 feat: UX polish for Build Your Own tab and default landing
- Default app landing changed from Pantry to Recipes tab
- Pre-fetch inventory on app mount so Find tab has data immediately
- Reorder recipe sub-tabs: Saved > Build Your Own > Community > Find > Browse
- Default active sub-tab changed to Saved
- Auto-redirect from Saved to Build Your Own when saved list is empty
- Add freeform custom ingredient input: typing a non-pantry item now shows
  "Use X anyway" button so users aren't blocked on unknown ingredients
2026-04-14 13:53:54 -07:00
144d1dc6c4 chore: commit in-progress work -- tag inferrer, imitate endpoint, hall-of-chaos easter egg, migration files, Dockerfile .env defense
- app/services/recipe/tag_inferrer.py: infer tags from recipe ingredient text
- app/db/migrations/022_recipe_generic_flag.sql, 029_inferred_tags.sql: schema migrations
- app/api/endpoints/imitate.py: recipe imitation endpoint stub
- app/api/endpoints/community.py: hall-of-chaos easter egg endpoint
- scripts/pipeline/infer_recipe_tags.py, backfill_keywords.py: pipeline scripts
- scripts/pipeline/build_recipe_index.py: extended index builder
- Dockerfile: explicit .env removal as defense-in-depth
- frontend/src/components/FeedbackButton.vue: feedback UX improvements
- frontend/src/style.css: minor style tweaks
- app/cloud_session.py: cloud session improvements
- tests/api/test_community_endpoints.py: additional test coverage
2026-04-14 13:23:15 -07:00
fe18fb48c0 feat: wire Build Your Own tab into RecipesView and add sparse-result nudge 2026-04-14 12:26:32 -07:00
40a12764c4 feat: add BuildYourOwnTab wizard component (template grid + picker + result) 2026-04-14 12:08:20 -07:00
9a42cdd4ae feat: add missingIngredientMode and builderFilterMode to recipes store 2026-04-14 11:53:29 -07:00
77ab6fb94a feat: add getTemplates, getRoleCandidates, buildRecipe to recipesAPI client 2026-04-14 11:48:33 -07:00
878a9a268c fix: community module integration fixes -- slots payload + ForkResult type
- PublishPayload gains optional slots field; PublishPlanModal maps
  plan.slots into the payload so the backend can compute element
  snapshot scores (seasoning, richness, etc.) from actual recipes
- plan-forked emit type updated to ForkResult across CommunityFeedPanel
  and RecipesView so forked_from is preserved for future navigation
2026-04-13 14:21:33 -07:00
d7bfc083e7 fix: Hall of Chaos quality fixes -- timer cleanup, aria-live, reduced-motion
- onUnmounted clears blooperHoldTimer to prevent stale callback after
  component teardown
- HallOfChaosView loading state gains aria-live="polite" so state
  transitions are announced to screen readers
- CommunityFeedPanel reduced-motion block resets toast translateY offset
  to avoid flash-of-offset-position on slow paint cycles
2026-04-13 12:34:23 -07:00
9246935fd7 feat: Hall of Chaos easter egg -- HallOfChaosView + long-press trigger
Adds the Hall of Chaos overlay component (recipe blooper gallery with
static CSS tilts, chaos level counter, panel-local overlay) and wires
the 800ms long-press trigger on the Bloopers filter tab in
CommunityFeedPanel. Pairs with the backend /community/hall-of-chaos
endpoint and test added in Task 10.
2026-04-13 12:30:48 -07:00
f92ac7a509 fix: publish modal a11y -- dialog-scoped Tab guard + correct first-focus in OutcomeModal 2026-04-13 11:48:57 -07:00
9603d421b6 feat: community publish modals -- focus traps, aria-live, plan + outcome forms 2026-04-13 11:45:32 -07:00
730445e479 fix: community feed a11y -- reduced-motion guards + tablist focus management 2026-04-13 11:38:17 -07:00
8731cad854 feat: community feed Vue frontend -- Pinia store + feed panel + RecipesView tab 2026-04-13 11:34:54 -07:00
19c0664637 fix(review): address code review findings before merge
- update_prep_task: move whitelist guard above filter so invalid column
  check runs on raw kwargs (was dead code — set(filtered) - allowed is
  always empty); fixes latent SQL injection path for future callers
- main.py: move register_kiwi_programs() into lifespan context manager
  so it runs once at startup, not at module import time
- MealPlanView.vue: remove debug console.log stubs from onSlotClick and
  onAddMealType (follow-up issue handlers, not ready for production)
2026-04-12 14:16:24 -07:00
2baa8c49a9 feat(frontend): add MealPlan tab with grid, shopping list, and prep schedule
closes kiwi#68, kiwi#71
2026-04-12 13:57:55 -07:00
faaa6fbf86 feat(frontend): add PrepSessionView with editable task durations 2026-04-12 13:57:48 -07:00
67b521559e feat(frontend): add ShoppingListPanel with pantry diff and affiliate links 2026-04-12 13:57:48 -07:00
a7fc441105 feat(frontend): add MealPlanGrid compact-expandable week grid component 2026-04-12 13:57:47 -07:00
543c64ea30 feat(frontend): add mealPlan Pinia store with immutable slot updates 2026-04-12 13:57:40 -07:00
4865498db9 feat(frontend): add mealPlanAPI client with TypeScript types 2026-04-12 13:55:14 -07:00
0b9e3553a7 fix(frontend): remove unused removeConstraint function (TS6133) 2026-04-09 12:31:18 -07:00
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