From 3de7c5eed5ba262abf0d746211e33800db6d66df Mon Sep 17 00:00:00 2001 From: Tyler Hoang Date: Fri, 8 May 2026 03:24:36 -0700 Subject: Add recipe suggestions, chat tab, and major workflow improvements - Replace 7-day grid menu with browsable recipe suggestion cards (swap, remove, make this) - Add Chat tab: conversational AI with full pantry/menu/grocery context - Grocery list works without a menu (pantry-only mode) - Individual grocery checkboxes auto-add items to pantry - Swap modal with optional preference input - User notes textarea on menu and grocery generation - Clear button for menu and grocery list - LLM notes/summary displayed after generation, persisted to DB - Favicon linked in HTML - Category dropdown styled for dark theme - System prompt configurable via SYSTEM_PROMPT in .env - Fix startup error (ollama_timeout default), DB migration for menu_plans.notes - Simplify: batch N+1 queries, extract _current_monday(), merge chat sync fns, asyncio.get_running_loop(), fix currentGroceryId bug, cap chat history Co-Authored-By: Claude Sonnet 4.6 --- static/style.css | 286 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 223 insertions(+), 63 deletions(-) (limited to 'static/style.css') diff --git a/static/style.css b/static/style.css index 9d90898..4cc033b 100644 --- a/static/style.css +++ b/static/style.css @@ -270,6 +270,16 @@ main { border-color: #16a34a; } +.btn-secondary { + background-color: var(--surface2); + color: var(--text); + border: 1px solid var(--border); +} + +.btn-secondary:hover { + border-color: var(--text-muted); +} + .btn-sm { padding: 0.5rem 1rem; font-size: 0.8rem; @@ -354,97 +364,87 @@ tbody tr.expiry-warn { font-size: 0.9rem; } -/* ── Menu Grid ──────────────────────────────────────────────────────────── */ -.menu-grid { +/* ── Recipe List ───────────────────────────────────────────────────────── */ +.recipe-list { display: grid; - grid-template-columns: repeat(8, 1fr); - gap: 0.5rem; - margin-bottom: 2rem; + grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); + gap: 1rem; + padding: 1rem 0; } -.menu-grid-cell { +.recipe-card { background-color: var(--surface); border: 1px solid var(--border); border-radius: 0.5rem; - padding: 1rem; - min-height: 120px; + padding: 1.25rem; display: flex; flex-direction: column; - justify-content: space-between; - transition: all 0.2s ease; + gap: 0.5rem; + transition: border-color 0.2s ease; } -.menu-grid-cell:hover { +.recipe-card:hover { border-color: var(--accent); } -.menu-grid-cell.repeat-warning { - border: 2px solid var(--warning); +.recipe-card-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 0.5rem; } -.menu-grid-header { - background-color: var(--surface2); +.recipe-card-title { font-weight: 600; - text-align: center; - padding: 1rem; - border-radius: 0.5rem; - border: 1px solid var(--border); + font-size: 1rem; + color: var(--text); } -.menu-meal-type { - font-size: 0.75rem; - font-weight: 600; - text-transform: uppercase; - color: var(--text-muted); - margin-bottom: 0.5rem; +.recipe-type-badge { + font-size: 0.7rem; + padding: 0.2rem 0.6rem; + border-radius: 9999px; + font-weight: 500; + white-space: nowrap; + flex-shrink: 0; } -.menu-meal-name { - font-weight: 600; - margin-bottom: 0.5rem; - word-break: break-word; -} +.recipe-type-badge.breakfast { background-color: rgba(251, 191, 36, 0.2); color: #fbbf24; } +.recipe-type-badge.lunch { background-color: rgba(52, 211, 153, 0.2); color: #34d399; } +.recipe-type-badge.dinner { background-color: rgba(139, 92, 246, 0.2); color: #8b5cf6; } +.recipe-type-badge.snack { background-color: rgba(251, 146, 60, 0.2); color: #fb923c; } -.menu-time { - display: inline-block; - font-size: 0.75rem; - background-color: var(--surface2); - padding: 0.25rem 0.5rem; - border-radius: 0.25rem; - margin-bottom: 0.75rem; +.recipe-card-meta { + font-size: 0.8rem; color: var(--text-muted); } -.menu-actions { - display: flex; - gap: 0.5rem; - flex-wrap: wrap; +.recipe-card-ingredients { + font-size: 0.85rem; + color: var(--text-muted); + line-height: 1.5; } -@media (max-width: 768px) { - .menu-grid { - grid-template-columns: 1fr; - } - - .menu-grid-header { - display: none; - } +.recipe-card-instructions { + font-size: 0.85rem; + color: var(--text); + line-height: 1.6; + border-top: 1px solid var(--border); + padding-top: 0.5rem; + margin-top: 0.25rem; + white-space: pre-line; +} - .menu-grid-cell { - position: relative; - } +.recipe-card .btn { + align-self: flex-start; + margin-top: 0.5rem; +} - .menu-grid-cell::before { - content: attr(data-day); - position: absolute; - top: 0.5rem; - right: 0.5rem; - font-size: 0.75rem; - color: var(--text-muted); - background-color: var(--surface2); - padding: 0.25rem 0.5rem; - border-radius: 0.25rem; - } +.recipe-card-actions { + display: flex; + gap: 0.5rem; + flex-wrap: wrap; + margin-top: 0.5rem; } /* ── Grocery List ───────────────────────────────────────────────────────── */ @@ -826,3 +826,163 @@ tbody tr.expiry-warn { min-width: unset; } } + +.ai-notes-input { + width: 100%; + padding: 0.6rem 0.75rem; + background-color: var(--surface2); + border: 1px solid var(--border); + border-radius: 0.375rem; + color: var(--text); + font-size: 0.85rem; + resize: vertical; + font-family: inherit; + margin-bottom: 0.5rem; +} + +.ai-notes-input:focus { + outline: none; + border-color: var(--accent); +} + +.ai-notes-input::placeholder { + color: var(--text-muted); +} + +.grocery-item-checked { + opacity: 0.45; +} + +.grocery-item-checked .grocery-item-name { + text-decoration: line-through; +} + +.modal-overlay { + position: fixed; + inset: 0; + background-color: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.modal { + background-color: var(--surface); + border: 1px solid var(--border); + border-radius: 0.75rem; + padding: 1.5rem; + width: 100%; + max-width: 420px; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.modal-title { + font-size: 1.1rem; + font-weight: 600; + color: var(--text); +} + +.modal-subtitle { + font-size: 0.85rem; + color: var(--text-muted); + margin-top: -0.5rem; +} + +.modal-actions { + display: flex; + justify-content: flex-end; + gap: 0.75rem; +} + +.chat-container { + display: flex; + flex-direction: column; + height: calc(100vh - 180px); +} + +.chat-messages { + flex: 1; + overflow-y: auto; + padding: 1rem 0; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.chat-welcome { + color: var(--text-muted); + font-style: italic; + text-align: center; + padding: 2rem; +} + +.chat-message { + display: flex; +} + +.chat-message-user { + justify-content: flex-end; +} + +.chat-message-assistant { + justify-content: flex-start; +} + +.chat-bubble { + max-width: 75%; + padding: 0.75rem 1rem; + border-radius: 1rem; + font-size: 0.9rem; + line-height: 1.6; + word-break: break-word; +} + +.chat-message-user .chat-bubble { + background-color: var(--accent); + color: white; + border-bottom-right-radius: 0.25rem; +} + +.chat-message-assistant .chat-bubble { + background-color: var(--surface2); + color: var(--text); + border-bottom-left-radius: 0.25rem; +} + +.chat-typing-indicator { + color: var(--text-muted); + font-style: italic; +} + +.chat-input-area { + display: flex; + gap: 0.75rem; + align-items: flex-end; + padding-top: 1rem; + border-top: 1px solid var(--border); +} + +.chat-input { + flex: 1; + padding: 0.75rem; + background-color: var(--surface2); + border: 1px solid var(--border); + border-radius: 0.5rem; + color: var(--text); + font-size: 0.9rem; + resize: none; + font-family: inherit; + line-height: 1.5; +} + +.chat-input:focus { + outline: none; + border-color: var(--accent); +} + +.chat-input::placeholder { + color: var(--text-muted); +} -- cgit v1.3-2-g0d8e