chore(RECIPE-0009): update FINDINGS.md for iteration 1 planning

This commit is contained in:
Giancarmine Salucci
2026-02-18 10:15:43 +01:00
parent dfca35bde2
commit c98a2407a7

View File

@@ -2835,6 +2835,313 @@ Current filter tabs take significant horizontal space (5 buttons). Consolidating
---
**Document Version:** 2.0
**Last Updated by:** Planner Agent (RECIPE-0009 Iteration 0)
### [Planner] Research Notes - RECIPE-0009 Iteration 1 (2026-02-18)
**Task:** UI enhancements - footer status bar, icon-only buttons, toggle Add Recipe visibility
#### Current Homepage UI Structure Analysis
**Research Date:** 2026-02-18
**Source:** Analysis of [src/routes/+page.svelte](src/routes/+page.svelte), iteration 0 implementation
**Current Implementation (Iteration 0)**:
1. **Connection Status Widget** (lines 369-383):
- Fixed position: bottom-right (`fixed bottom-4 right-4`)
- Shows connection status with colored dot + text label
- Shows last ping timestamp
- Will be REMOVED and replaced with footer bar
2. **Action Bar** (lines 263-297):
- Filter dropdown (lines 266-276)
- Refresh button with icon + text (lines 277-285)
- Add Recipe button with icon + text (lines 288-297)
- Currently: Add Recipe button ALWAYS visible (iteration 0 requirement)
3. **Empty State** (lines 310-342):
- Shows when `!loading && filteredItems.length === 0`
- Contains "Add Recipe URL" link
**Changes Required for Iteration 1**:
1. Remove floating connection status widget
2. Add footer status bar (icons only)
3. Convert refresh button to icon-only
4. Convert Add Recipe button to icon-only
5. Toggle Add Recipe button visibility (hide when empty, show when has items)
---
#### Footer Status Bar Design - RECIPE-0009 Iteration 1
**Research Date:** 2026-02-18
**Source:** Web PWA patterns, existing codebase styling patterns
**Design Requirements**:
- **Position**: Fixed at bottom (`fixed bottom-0 left-0 right-0`)
- **Layout**: Full width with max-width container matching page layout (`max-w-6xl`)
- **Content**: Two sections (notification status left, live updates right)
- **Display**: Icons only, no text labels
- **Accessibility**: title and aria-label attributes on interactive elements
- **Z-index**: `z-50` to ensure visibility above all content
- **Visual**: White background, top border, shadow for lift effect
**State Integration**:
Footer needs access to two state sources:
1. **Notification Status**: Via `pushNotificationManager.getState()`
- Need to add `notificationViewModel` state variable in +page.svelte
- Subscribe to state changes in `onMount`
- Cleanup subscription in `onDestroy`
2. **Connection Status**: Already exists as `connectionStatus` state
- Reuse existing variable
- States: 'connecting' | 'connected' | 'disconnected'
**Notification Icon Logic**:
```typescript
if (!supported || permission === 'denied') {
// Show bell with slash (not supported/denied)
icon = 'bell-slash';
color = 'text-gray-400';
} else if (subscribed) {
// Show bell icon (enabled)
icon = 'bell';
color = 'text-green-600';
} else {
// Show bell icon (available but not enabled)
icon = 'bell';
color = 'text-gray-400';
}
```
**Live Update Indicator Logic**:
```typescript
if (connectionStatus === 'connected') {
dotColor = 'bg-green-400';
title = 'Live updates active';
} else if (connectionStatus === 'connecting') {
dotColor = 'bg-yellow-400';
title = 'Connecting to live updates...';
} else {
dotColor = 'bg-red-400';
title = 'Live updates disconnected';
}
```
**Click Behavior**:
Clicking notification icon scrolls to NotificationSettings component:
```typescript
onclick={() => {
document.querySelector('[data-notification-settings]')?.scrollIntoView({ behavior: 'smooth' });
}}
```
Requires adding `data-notification-settings` attribute to NotificationSettings wrapper.
---
#### Icon-Only Button Patterns - RECIPE-0009 Iteration 1
**Research Date:** 2026-02-18
**Source:** Existing codebase button styles, Tailwind CSS documentation, WCAG 2.1 guidelines
**Current Button Pattern (with text)**:
```svelte
<button class="flex items-center space-x-2 px-4 py-2 ...">
<svg class="w-4 h-4" ... />
<span>Button Text</span>
</button>
```
- Padding: `px-4 py-2` (horizontal + vertical)
- Icon size: `w-4 h-4` (16x16px)
- Spacing: `space-x-2` (gap between icon and text)
**Icon-Only Button Pattern**:
```svelte
<button
title="Button description"
aria-label="Button description"
class="p-2 ..."
>
<svg class="w-5 h-5" ... />
</button>
```
**Changes**:
- Padding: `p-2` (square/circular button)
- Icon size: `w-5 h-5` (20x20px - slightly larger for better visibility)
- Remove: `space-x-2` class (no text to space from)
- Add: `title` attribute (tooltip on hover)
- Add: `aria-label` attribute (screen reader accessibility)
**Accessibility Requirements** (WCAG 2.1):
1. **Title Attribute**: Provides tooltip text for sighted users on hover
2. **Aria-label Attribute**: Provides accessible name for screen readers
3. **Minimum Touch Target**: 24x24px recommended (20x20px icon + 8px padding = 36x36px total ✓)
4. **Color Contrast**: Must meet 3:1 ratio for non-text (icons)
**Examples**:
Refresh button:
```svelte
<button
title="Refresh queue"
aria-label="Refresh queue"
class="p-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 ..."
>
<svg class="w-5 h-5" ... />
</button>
```
Add Recipe button:
```svelte
<a
href="/share"
title="Add recipe URL"
aria-label="Add recipe URL"
class="p-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 ..."
>
<svg class="w-5 h-5" ... />
</a>
```
---
#### Add Recipe Button Visibility Logic - RECIPE-0009 Iteration 1
**Research Date:** 2026-02-18
**Source:** context_compact.yaml requirement analysis, UX patterns
**Iteration 0 Implementation**:
- Add Recipe button ALWAYS visible in controls bar
- Rationale: User complained "do not hide the add recipe component when there are items in the queue"
**Iteration 1 Requirement**:
> "Toggle "Add Recipe" button visibility in controls bar (hide when queue empty, show when items exist - opposite of placeholder rule)"
**Interpretation**:
"Opposite of placeholder rule":
- Placeholder (empty state) shows when: `items.length === 0`
- Add Recipe button in controls shows when: `items.length > 0` (opposite condition)
**Logic**:
```svelte
{#if items.length > 0}
<a href="/share" title="Add recipe URL" aria-label="Add recipe URL" ...>
<svg ... />
</a>
{/if}
```
**Rationale**:
1. **Empty State**: When queue is empty, user sees empty state with centered "Add Recipe URL" link
2. **Non-Empty State**: When queue has items, controls bar shows Add Recipe button (icon-only)
3. **No Redundancy**: Button doesn't appear when empty state link is already visible
4. **Consistent Access**: User always has access to "Add Recipe" via either empty state link OR controls bar button
**UX Benefits**:
- Cleaner UI when queue is empty (no redundant button)
- Convenient access when queue has items (quick add more recipes)
- Fulfills opposite condition of empty state placeholder
---
#### Svelte 5 Notification State Management
**Research Date:** 2026-02-18
**Source:** Existing iteration 0 implementation, [PushNotificationManager.ts](src/lib/client/PushNotificationManager.ts)
**NotificationState Type**:
```typescript
interface NotificationState {
supported: boolean;
permission: NotificationPermission; // 'default' | 'granted' | 'denied'
subscribed: boolean;
loading: boolean;
error: string | null;
}
```
**State Subscription Pattern**:
```typescript
// Import type
import type { NotificationState } from '$lib/client/PushNotificationManager';
// Declare state
let notificationViewModel = $state<NotificationState | null>(null);
// Subscribe in onMount
onMount(() => {
// ... existing code ...
const unsubscribeNotifications = pushNotificationManager.onStateChange((newState) => {
notificationViewModel = newState;
});
return () => {
unsubscribeNotifications?.();
};
});
```
**Cleanup in onDestroy**:
Current onDestroy only cleans up `eventSource`. Need to also cleanup notification subscription:
```typescript
onDestroy(() => {
if (eventSource) {
console.log('[SSE] Closing connection on component destroy');
eventSource.close();
connectionStatus = 'disconnected';
}
// No cleanup needed - handled by onMount return callback
});
```
**Note**: Svelte 5's `onMount` return function handles cleanup automatically when component unmounts.
**State Access in Footer**:
Footer component needs null-safe access since initial state is `null`:
```svelte
{#if notificationViewModel}
{#if !notificationViewModel.supported || notificationViewModel.permission === 'denied'}
<!-- Show disabled icon -->
{:else if notificationViewModel.subscribed}
<!-- Show enabled icon -->
{:else}
<!-- Show available icon -->
{/if}
{:else}
<!-- Loading state - show gray icon -->
<svg class="w-5 h-5 text-gray-400" ... />
{/if}
```
**Initial State Handling**:
`pushNotificationManager.onStateChange()` sends initial state immediately on subscription, so `notificationViewModel` will be populated almost instantly after component mount.
---
**Document Version:** 3.0
**Last Updated by:** Planner Agent (RECIPE-0009 Iteration 1)
**Next Update:** Developer Agent