Commit graph

175 commits

Author SHA1 Message Date
6aa63cf2f0 chore: bump version to 0.3.0
Some checks failed
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Has been cancelled
Release / release (push) Has been cancelled
2026-04-16 14:24:16 -07:00
e745ce4375 feat: wire meal planner slot editor and meal type picker
Slot click now opens an inline editor panel:
- Pick from saved recipes via dropdown (pre-loaded on mount)
- Or type a custom label
- Clear slot button when a slot is already filled
- Save/Cancel with loading state

Add meal type opens a chip picker showing the types not yet active
(breakfast / lunch / snack minus whatever is already on the plan).
Selecting one calls the new PATCH /meal-plans/{plan_id} endpoint.

Backend:
- PATCH /meal-plans/{plan_id} with UpdatePlanRequest(meal_types)
- store.update_meal_plan_types() UPDATE ... RETURNING *
- 409 on IntegrityError in create_plan (already in place)
2026-04-16 14:23:38 -07:00
de0008f5c7 fix: meal planner auto-selects current week on load, + New week idempotent
- Add autoSelectPlan() to the store: after loadPlans() resolves, set
  activePlan to the current week's plan (or most recent) without a second
  API round-trip -- list already returns full PlanSummary with slots
- Call autoSelectPlan(mondayOfCurrentWeek()) in onMounted so the grid
  populates immediately without the user touching the dropdown
- Make onNewPlan idempotent: if a 409 comes back, activate the existing
  plan for that week instead of surfacing an error to the user
2026-04-16 10:50:34 -07:00
dbaf2b6ac8 fix: meal planner week add button crashing on r.name / add duplicate guard
- Fix sqlite3.OperationalError: the recipes table uses `title` not `name`;
  get_plan_slots JOIN was crashing every list_plans call with a 500,
  making the + New week button appear broken (plans were being created
  silently but the selector refresh always failed)
- Add migration 032 to add UNIQUE INDEX on meal_plans(week_start)
  to prevent duplicate plans accumulating while the button was broken
- Raise HTTP 409 on IntegrityError in create_plan so duplicates produce
  a clear error instead of a 500
- Fix mondayOfCurrentWeek to build the date string from local date parts
  instead of toISOString(), which converts through UTC and can produce the
  wrong calendar day for UTC+ timezones
- Add planCreating/planError state to MealPlanView so button shows
  "Creating..." during the request and displays errors inline
2026-04-16 10:46:28 -07:00
9a277f9b42 fix: barcode scan performance + timeout + success message
- Refactor _lookup_in_database to accept a shared httpx.AsyncClient so
  all three Open*Facts database attempts reuse one TLS connection instead
  of opening a new one per call; restores pre-fallback scan speed
- Increase recipe suggest timeout to 120s (was 30s) to survive cf-orch
  model cold-start on first request of a session
- Include product brand in barcode scan success message so the user can
  clearly see what was found (e.g. "Added: Cheerios (General Mills) to pantry")
2026-04-16 09:57:53 -07:00
200a6ef87b feat(recipes): complexity badges, time hints, Surprise Me, Just Pick One
#55 — Complexity rating on recipe cards:
  - Derived from direction text via _classify_method_complexity()
  - Badge displayed on every card: easy (green), moderate (amber), involved (red)
  - Filterable via complexity filter chips in the results bar

#58 — Cooking time + difficulty as filter domains:
  - estimated_time_min derived from step count + complexity
  - Time hint (~Nm) shown on every card
  - complexity_filter and max_time_min fields in RecipeRequest
  - Both applied in the engine before suggestions are built

#53 — Surprise Me: picks a random suggestion from the filtered pool,
  avoids repeating the last pick. Shown in a spotlight card.

#57 — Just Pick One: surfaces the top-matched suggestion in the same
  spotlight card. One tap to commit to cooking it.

Closes #55, #58, #53, #57
2026-04-16 09:27:34 -07:00
c8fdc21c29 feat(export): JSON full-backup download (pantry + saved recipes)
Adds GET /export/json that bundles inventory and saved recipes into a
single timestamped JSON file for data portability. The export envelope
includes schema version and export timestamp so future import logic can
handle version differences.

Frontend: new primary-styled JSON download button in the Export card with
a short description of what is included.

Closes #62
2026-04-16 09:16:33 -07:00
2ad71f2636 feat(recipes): pantry match floor filter — 'can make now' toggle
Adds pantry_match_only flag to RecipeRequest. When enabled, any recipe
with one or more missing ingredients (after swaps) is excluded from
results. Swapped ingredients count as covered.

Frontend: toggle checkbox in recipe settings panel, disabled when
shopping mode is active (the two modes are mutually exclusive). Hides
the max-missing input when pantry-match-only is on (irrelevant there).

Closes #63
2026-04-16 09:12:24 -07:00
0de6182f48 feat(scan): barcode miss fallback chain — Open Beauty Facts + Open Products Facts
When a barcode is not found in Open Food Facts, the service now tries
Open Beauty Facts and Open Products Facts before giving up. All three
share the same API format; only the host URL differs.

When all databases miss, the scan endpoint sets needs_manual_entry=true
in the result. The frontend detects this, shows a calm informational
message, and switches to manual entry mode automatically.

Also fixes a latent bug where not-found scans showed 'Added: item to
pantry' due to the success condition checking barcodes_found (always 1)
instead of added_to_inventory.

Closes #65
2026-04-16 08:30:49 -07:00
fb18a9c78c feat: partial consumption tracking and waste/disposal logging (#12 #60)
#12 — partial consume:
- POST /inventory/items/{id}/consume now accepts optional {quantity}
  body; decrements by that amount and only marks status=consumed when
  quantity reaches zero (store.partial_consume_item)
- OFFs barcode scan pre-fills sub-unit quantity when product data
  includes a pack size (number_of_units or 'N x ...' quantity string)
- Consume button shows quantity-aware label and opens ActionDialog
  with number input for multi-unit items ('use some or all')
- consumeItem() in api.ts now returns InventoryItem and accepts
  optional quantity param

#60 — disposal logging:
- Migration 031: adds disposal_reason TEXT column to inventory_items
  (status='discarded' was already in the CHECK constraint)
- POST /inventory/items/{id}/discard endpoint with optional DiscardRequest
  body (free text or preset reason)
- Calm framing: 'item not used' not 'wasted'; reason presets avoid
  blame language ('went bad before I could use it', 'too much — had excess')
- Muted discard button (X icon, tertiary color) — not alarming

Shared:
- New ActionDialog.vue component for dialogs with inline inputs
  (quantity stepper or reason dropdown); keeps ConfirmDialog simple
- disposal_reason field added to InventoryItemResponse

Closes #12
Closes #60
2026-04-16 07:28:21 -07:00
443e68ba3f fix: wire recipe engine to cf-text service instead of vllm
Aligns llm_recipe.py with the pattern already used by the meal plan
service. cf-text routes through a lighter GGUF/llama.cpp path and
shares VRAM budget with other products via cf-orch, rather than
requiring a dedicated vLLM process. Also drops model_candidates
(not applicable to cf-text allocation).

Closes #70
2026-04-16 06:25:46 -07:00
64a0abebe3 feat: pantry intel cluster — #61 expiry display, #64 cook log, #66 scaling, #59 open-package tracking
#61: expiry badge now shows relative + calendar date ("5d · Apr 15") with
tooltip "Expires in 5 days (Apr 15)"; traffic-light colors already in place

#64: RecipeDetailPanel.handleCook() calls recipesStore.logCook(); SavedRecipesPanel
shows "Last made: X ago" below each card using cookLog entries

#66: Serving multiplier (1x/2x/3x/4x) in RecipeDetailPanel scales ingredient
quantities using regex; handles integers, decimals, fractions (1/2, 3/4),
mixed numbers (1 1/2), and ranges (2-3); leaves unrecognised strings unchanged

#59: migration 030 adds opened_date column; ExpirationPredictor gains
SHELF_LIFE_AFTER_OPENING table + days_after_opening(); POST /inventory/items/{id}/open
sets opened_date=today and returns computed opened_expiry_date; InventoryList
shows lock-open button for unopened items and an "📂 5d · Apr 15" badge once opened
2026-04-16 06:01:25 -07:00
4423373750 feat: screenshot attachment in feedback form (#82)
Some checks are pending
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Waiting to run
- Backend: new /api/v1/feedback/attach endpoint uploads image to
  Forgejo as an issue asset, then pins it as a comment so the
  screenshot is visible inline on the issue
- Frontend: file input in feedback form (all types, max 5 MB)
  with inline thumbnail preview and remove button
- Attach call is non-fatal: if upload fails after issue creation,
  the issue is still filed and the user sees success
- Screenshot state clears on modal reset

Closes #82
2026-04-15 23:08:02 -07:00
76516abd62 feat: metric/imperial unit preference (#81)
Some checks are pending
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Waiting to run
- Settings: add unit_system key (metric | imperial, default metric)
- Recipe LLM prompts: inject unit instruction into L3 and L4 prompts
  so generated recipes use the user's preferred units throughout
- Frontend: new utils/units.ts converter (mirrors Python units.py)
- Inventory list: display quantities converted to preferred units
- Settings view: metric/imperial toggle with save button
- Settings store: load/save unit_system alongside cooking_equipment

Closes #81
2026-04-15 23:04:29 -07:00
757f779030 ci: add GitHub Actions CI for public credibility badge
Some checks are pending
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Waiting to run
Lean self-contained workflow — no Forgejo-specific secrets.
circuitforge-core installs from Forgejo git (public repo).
Forgejo (.forgejo/workflows/ci.yml) remains the canonical CI.

Backend: ruff + pytest | Frontend: vue-tsc only (no vitest yet)
2026-04-15 20:20:13 -07:00
c984e6162e ci: wire Forgejo Actions CI, mirror, and release workflows
Some checks are pending
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Waiting to run
- .forgejo/workflows/ci.yml — backend (ruff + pytest) + frontend (vue-tsc)
- .forgejo/workflows/mirror.yml — push to GitHub + Codeberg on main/tags
- .forgejo/workflows/release.yml — git-cliff changelog + Forgejo release on v* tags
- .cliff.toml — conventional commits changelog config

Frontend CI runs typecheck only (no vitest yet; tracked separately).
circuitforge-core installed from Forgejo git (public; not on PyPI).
Docker push disabled pending BSL registry policy (cf-agents#3).

Closes #23
2026-04-15 20:10:57 -07:00
ab97af73f7 Merge pull request 'fix(a11y): accessibility and ND-design audit fixes (#42-#48, #54, #80)' (#85) from fix/a11y-audit into main 2026-04-15 10:21:13 -07:00
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
99e9cbb8c1 refactor(kiwi): remove unused LIFETIME_SOURCES import from recipes.py 2026-04-14 15:44:42 -07:00
006582b179 feat(kiwi): add /orch-usage proxy endpoint for frontend budget display 2026-04-14 15:42:58 -07:00
1a6898324c feat(kiwi): merge meal planner feature into main
Adds full meal planning workflow to Kiwi:
- Weekly meal plan creation with configurable meal types (Paid gate)
- Drag-and-assign recipe slots per day
- Prep session generation with sequenced task lists and time estimates
- LLM-assisted full-week plan and timing fill-in (BYOK-unlockable)
- Community feed (local ActivityPub-compat + cloud federation)
- Build Your Own recipe tab with assembly templates
- Save/bookmark any recipe with star rating, notes, and style tags
- Shopping list export from built recipes
- Tab reorder: Saved > Build > Community > Find > Browse
- Auto-redirect from empty Saved tab to Build
- Custom ingredient injection persists in candidate list
- z-index fix: save modal above recipe detail panel
- Route ordering fix: /recipes/saved before /{recipe_id} catch-all
2026-04-14 15:37:57 -07:00
01216b82c3 feat(kiwi): gate L3/L4 recipes behind orch budget check; fallback to L2 on exhaustion 2026-04-14 15:24:57 -07:00
2071540a56 feat(kiwi): add Heimdall orch budget client with fail-open semantics 2026-04-14 15:15:43 -07:00
bd73ca0b6d fix(tests): correct build endpoint test fixture
- Use monkeypatch.setattr to patch cloud_session._LOCAL_KIWI_DB
  instead of wrong KIWI_DB_PATH env var (module-level singleton
  computed at import time; env var had no effect)
- Assert id > 0 (real persisted DB id) instead of -1 (old
  pre-persistence sentinel value)
2026-04-14 14:57:16 -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
b4f031e87d feat(kiwi): add orch_fallback field to RecipeResult 2026-04-14 14:38:37 -07:00
fbae9ced72 feat(kiwi): add LIFETIME_ORCH_CAPS and LIFETIME_SOURCES constants 2026-04-14 14:38:36 -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
8c4965123f feat: add GET /templates, GET /template-candidates, POST /build endpoints
Wires the three Build Your Own API routes into the recipes router,
registered before the catch-all /{recipe_id} route to avoid shadowing.
Adds 5 endpoint tests covering template list count/shape, candidate
response structure, successful recipe build, and 404 on unknown template.
2026-04-14 11:45:43 -07:00
c02e538cb2 feat: remove assembly results from suggest() -- moved to Build Your Own tab 2026-04-14 11:39:57 -07:00
da940ebaec feat: add get_role_candidates() and build_from_selection() to assembly engine
Both functions are DB-free public API additions to assembly_recipes.py.
get_role_candidates() scores pantry candidates against a wizard step using
element-profile overlap with prior picks; build_from_selection() builds a
RecipeSuggestion from explicit role overrides with required-role validation.
2026-04-14 11:06:08 -07:00