feat: Phase 2 — saved recipes, browser, accessibility, level UX #69

Merged
pyr0ball merged 17 commits from feature/orch-auto-lifecycle into main 2026-04-08 15:13:45 -07:00
Showing only changes of commit 74a7c068bc - Show all commits

View file

@ -40,23 +40,26 @@
<!-- Level Selector --> <!-- Level Selector -->
<div class="form-group"> <div class="form-group">
<label class="form-label">Creativity Level</label> <label class="form-label">How far should we stretch?</label>
<div class="flex flex-wrap gap-sm"> <div class="flex flex-wrap gap-sm">
<button <button
v-for="lvl in levels" v-for="lvl in levels"
:key="lvl.value" :key="lvl.value"
:class="['btn', 'btn-secondary', { active: recipesStore.level === lvl.value }]" :class="['btn', 'btn-secondary', { active: recipesStore.level === lvl.value }]"
@click="recipesStore.level = lvl.value" @click="recipesStore.level = lvl.value"
:title="lvl.description"
> >
{{ lvl.label }} {{ lvl.label }}
</button> </button>
</div> </div>
<p v-if="activeLevel" class="level-description text-sm text-secondary mt-xs">
{{ activeLevel.description }}
</p>
</div> </div>
<!-- Wildcard warning --> <!-- Surprise Me confirmation -->
<div v-if="recipesStore.level === 4" class="status-badge status-warning wildcard-warning"> <div v-if="recipesStore.level === 4" class="status-badge status-warning wildcard-warning">
Wildcard mode uses LLM to generate creative recipes with whatever you have. Results may be The AI will freestyle recipes from whatever you have. Results can be unusual that's part of the fun.
unusual.
<label class="flex-start gap-sm mt-xs"> <label class="flex-start gap-sm mt-xs">
<input type="checkbox" v-model="recipesStore.wildcardConfirmed" /> <input type="checkbox" v-model="recipesStore.wildcardConfirmed" />
<span>I understand, go for it</span> <span>I understand, go for it</span>
@ -644,12 +647,14 @@ function onCooked(recipe: RecipeSuggestion) {
} }
const levels = [ const levels = [
{ value: 1, label: '1 — From Pantry' }, { value: 1, label: 'Use What I Have', description: 'Finds recipes you can make right now using exactly what\'s in your pantry.' },
{ value: 2, label: '2 — Creative Swaps' }, { value: 2, label: 'Allow Swaps', description: 'Same as above, plus recipes where one or two ingredients can be substituted.' },
{ value: 3, label: '3 — AI Scaffold' }, { value: 3, label: 'Get Creative', description: 'AI builds recipes in your chosen cuisine style from what you have. Requires paid tier.' },
{ value: 4, label: '4 — Wildcard 🎲' }, { value: 4, label: 'Surprise Me 🎲', description: 'Fully AI-generated — open-ended and occasionally unexpected. Requires paid tier.' },
] ]
const activeLevel = computed(() => levels.find(l => l.value === recipesStore.level))
const cuisineStyles = [ const cuisineStyles = [
{ id: 'italian', label: 'Italian' }, { id: 'italian', label: 'Italian' },
{ id: 'mediterranean', label: 'Mediterranean' }, { id: 'mediterranean', label: 'Mediterranean' },
@ -796,6 +801,11 @@ onMounted(async () => {
margin-left: var(--spacing-xs); margin-left: var(--spacing-xs);
} }
.level-description {
font-style: italic;
line-height: 1.4;
}
.wildcard-warning { .wildcard-warning {
display: block; display: block;
margin-bottom: var(--spacing-md); margin-bottom: var(--spacing-md);