chore(loop): iOS HIG compliance plan — 24 tasks across 7 phases

Font sizes (text-[10px]→text-xs, text-[8-9px]→text-[11px]),
touch targets (44×44px minimum for all buttons), input zoom
prevention (16px minimum), gap compliance (8px between targets),
glass button system updates, and comprehensive verification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-04 12:43:44 +00:00
co-authored by Claude Opus 4.6
parent bda670d8d9
commit dde3859a2e
2 changed files with 124 additions and 81 deletions
+53 -29
View File
@@ -1,60 +1,84 @@
# Overnight Plan — Archy Integration, Content Hardening & Feature Completion
# Overnight Plan — iOS HIG Compliance & Continued Hardening
> **Format**: `- [ ]` = pending, `- [x]` = done.
> Make at least 30 attempts on any difficult task before moving on. Loop reads this file.
> After each phase, commit and push.
## Phase 1: Critical ContentPanel Wiring Gaps
## Phase 1: Input & Text Foundation (Prevent iOS Zoom + Readability)
- [x] **T1**Wire ImageGrid + ImageDetail into ContentPanel.vue. Import components, destructure `panelImages`, `selectedImage`, `openImageDetail`, `closeImageDetail` from `useContentPanel()`. Add ImageGrid render block following BookGrid/TVSeriesGrid pattern. Add ImageDetail overlay following BookDetail pattern. Add `selectedImage` to `hasDetailOpen` computed. Reference ContentGridView.vue for how ImageGrid is already used there.
- [ ] **T1**Fix all input/textarea font sizes to prevent iOS auto-zoom. Every `<input>`, `<textarea>`, and `<select>` must use `text-base` (16px) minimum. Files to fix: `ChatInput.vue` (main message textarea uses `text-sm`), `ChatSearch.vue` (search input), `SettingsModal.vue` (memory inputs use `text-xs`), `PersonaSelector.vue`, `MemoryPanel.vue`, `PluginSettingsForm.vue`, `ShareToNostr.vue`, `AdvancedSettings.vue`, `PromptPalette.vue`. Search for all `<input` and `<textarea` tags across `packages/app/src/` and verify each has at least `text-base`. Do NOT change the visual design — only increase font size on the form elements themselves.
- [x] **T2**Wire PlaceGrid + PlaceDetail into ContentPanel.vue. Import components, destructure `panelPlaces`, `selectedPlace`, `openPlaceDetail`, `closePlaceDetail` from `useContentPanel()`. Add PlaceGrid render block + PlaceDetail overlay. Add `selectedPlace` to `hasDetailOpen` computed. Follow same patterns as T1.
- [ ] **T2**Replace all `text-[10px]` with `text-xs` (12px) across the entire app. There are ~301 instances. Use find-and-replace but verify each file still looks correct. The main offenders: `ChatMessage.vue` (timestamps, token counts, edit labels), `SettingsModal.vue` (section headers), `ContentPanel.vue` (tab labels), all `*Detail.vue` and `*Grid.vue` files. Run `grep -r 'text-\[10px\]' packages/app/src/` to find all instances. Replace with `text-xs`. This is the single biggest compliance fix.
- [x] **T3**Verify code tab rendering in ContentPanel.vue. Ensure `panelCodeBlocks` is destructured and code content renders when the code tab is active. If missing, add a code block display component or inline rendering with syntax highlighting. The code tab should show extracted code blocks from AI responses.
- [ ] **T3**Replace all `text-[9px]` and `text-[8px]` with `text-[11px]` (iOS absolute minimum). There are ~100 instances. These are used in badges, version numbers, metadata labels. Files: `PluginMarketplace.vue`, `NostrGrid.vue`, `DesignSystemDetail.vue`, `DesignSystemGrid.vue`, settings components. Search with `grep -r 'text-\[9px\]\|text-\[8px\]' packages/app/src/`. Replace with `text-[11px]`.
- [x] **TEST:P1** — Run `cd packages/app && npx vitest run` and `pnpm typecheck && pnpm lint`. Fix all failures before proceeding.
- [ ] **T4** — Replace all `text-[11px]` with `text-xs` (12px). There are ~76 instances plus the new ones from T3. These are in `SettingsModal.vue`, `BranchSwitcher.vue`, `DesignSystemGrid.vue`. After T3 creates new `text-[11px]` instances, this task bumps them all to `text-xs`. Search with `grep -r 'text-\[11px\]' packages/app/src/`. Replace with `text-xs`.
## Phase 2: Archy Service Integration into AIUI
- [ ] **TEST:P1** — Run `pnpm typecheck && pnpm lint && pnpm test`. Fix all failures. Commit and push.
- [x] **T4** — Extend useArchy composable to support richer context categories. Add new reactive refs and fetch methods for: `wallet` (Lightning balance, channel count, node pubkey), `files` (Nextcloud file listings — name, path, size, modified date), `media` (photo count, recent photos, music files). Update `buildArchyContext()` to include wallet balance, file counts, and media summary in the AI system prompt. Update `fetchPermittedContext()` to request these new categories from the bridge. The data format should match what Archy's contextBroker will return (see archyBridge.ts categories: 'apps' | 'system' | 'network' | 'wallet' | 'files'). File: `packages/app/src/composables/useArchy.ts`
## Phase 2: Touch Targets — Chat Interface (44×44px Minimum)
- [x] **T5**Create Archy app cards for AIUI. Build a data file at `packages/app/src/data/archy-apps.ts` that maps Archy service IDs to AIUI-friendly metadata: display name, description, icon (use emoji for now), category, default port, and deep-link URL pattern (e.g., `/app/nextcloud`, `/app/mempool`). Include all major services: bitcoin-core, lnd, core-lightning, btcpay-server, mempool, nextcloud, immich, nostr-rs-relay, home-assistant, grafana, searxng, ollama, penpot, onlyoffice, fedimint, meshtastic. When running in Archy, merge this with live `installedApps` data from useArchy to show actual status. File: `packages/app/src/data/archy-apps.ts`
- [ ] **T5**Fix ChatMessage.vue action buttons. The edit/reply/branch/feedback buttons use `w-7 h-7` (28px). Change the button wrapper to `min-w-[44px] min-h-[44px]` with `flex items-center justify-center` while keeping the inner SVG icon at `w-4 h-4` or `w-5 h-5`. This makes the tap target 44px but the visual icon stays the same size. Also fix the `gap-0.5` (2px) between action buttons to `gap-2` (8px). Apply to all button rows in this file.
- [x] **T6**Create an ArchyAppsGrid component. Build `packages/app/src/components/content/ArchyAppsGrid.vue` that displays Archy node apps in a grid. Each card shows: app name, status (running/stopped/not installed), description snippet, and an action button (Open if running, Install if not). Use glass-card styling. On click, call `useArchy().requestAction('open-app', { appId })` for running apps. When not embedded in Archy, show the apps as informational cards without action buttons. Wire this into ContentPanel.vue under a new conditional: when the 'app' tab is active AND `useArchy().isEmbedded`, show ArchyAppsGrid above or instead of the generic AppsGrid.
- [ ] **T6**Fix ChatHeader.vue toolbar buttons. All 9 buttons use `w-8 h-8` or `w-9 h-9` (32-36px). Wrap each in a 44px touch target: `min-w-[44px] min-h-[44px] flex items-center justify-center`. Keep the visual icon sizes unchanged. Fix any `gap-1` between toolbar buttons to `gap-2`.
- [x] **T7**Wire Archy wallet context into AI responses. When the AI mentions Bitcoin/Lightning and Archy wallet data is available, the system prompt should include: wallet balance (sats), number of channels, total capacity. This lets the AI say things like "Your node has 500,000 sats across 3 channels" when relevant. In `useArchy.ts`, add `walletInfo` ref with `{ balanceSats?: number, channelCount?: number, totalCapacitySats?: number, nodePubkey?: string }`. In `buildArchyContext()`, append wallet section when data exists.
- [ ] **T7**Fix ChatInput.vue buttons. The image attach button (`w-8 h-8`), close reply button (`w-5 h-5`), and send button need 44px minimum touch targets. Also fix the send button row gap to 8px minimum between targets.
- [x] **T8**Wire Archy file context for AI awareness. When files permission is granted, the AI should know what files the user has. In `useArchy.ts`, add `fileList` ref as `{ name: string, path: string, size?: number, modified?: string, type: 'file' | 'folder' }[]`. In `buildArchyContext()`, summarize: "You have X files in Nextcloud. Recent files: file1.pdf, file2.jpg, notes/todo.md". Limit to 20 most recent files to keep prompt concise. The AI can then reference user's files conversationally.
- [ ] **T8**Fix ChatSearch.vue navigation buttons. Previous/Next/Close buttons use `w-6 h-6` (24px). Expand to 44px touch targets with padding.
- [x] **TEST:P2**Run `pnpm test && pnpm typecheck && pnpm lint`. Fix all failures before proceeding.
- [ ] **T9**Fix ArticleReader.vue and PdfViewer.vue controls. Close/navigation buttons use `w-8 h-8` (32px). Expand to 44px. Fix button gaps.
## Phase 3: Content Extraction Hardening
- [ ] **TEST:P2** — Run `pnpm typecheck && pnpm lint && pnpm test`. Commit and push.
- [x] **T9** — Expand extraction quality test suite to 100+ tests. Add tests for: podcast extraction (library `[[podcast:p1]]` tags + `[[podcast_ext:]]` tags), app extraction (`extractApps`), film extraction (library `[[film:f1]]` tags + `[[film_ext:]]` tags), magazine section extraction, websites/news detection from markdown links and bare domain extraction, edge cases (empty strings, very long text, unicode characters, special chars in titles), mixed content with 3+ types in one response, `filterTabsByContext` with every specialized path (news+TV, nostr+apps, app+code, etc). Each test should use realistic AI response text patterns. File: `packages/app/src/__tests__/extractionQuality.test.ts`
## Phase 3: Touch Targets — Content & Settings Components
- [x] **T10** — Fix any extraction bugs found during T9 testing. Run the expanded test suite, fix all failures in `contentExtraction.ts` and/or `contentFiltering.ts`. Iterate until all tests pass.
- [ ] **T10** — Fix all content detail view buttons. Search for `w-7 h-7`, `w-6 h-6`, `w-5 h-5` button patterns in: `BookDetail.vue`, `FilmDetail.vue`, `TVSeriesDetail.vue`, `SongDetail.vue`, `PodcastDetail.vue`, `PlaceDetail.vue`, `ImageDetail.vue`, `AppDetail.vue`, `MagazineSectionDetail.vue`, `NostrGrid.vue`. Expand all interactive buttons to 44px minimum touch targets. Keep icon sizes unchanged.
- [x] **T11**Verify filterTabsByContext prioritization for all paths. Write specific tests ensuring: news queries show news/magazine first but include TV/books/etc if present, nostr queries show nostr first, app queries show apps first, preferred tab from `preferredFirstTab()` is always first in general case, no content type is ever silently dropped. File: `packages/app/src/__tests__/extractionQuality.test.ts`
- [ ] **T11**Fix SettingsModal.vue and SettingsPanel.vue buttons. Close button (`w-7 h-7`), color picker buttons (`w-8 h-8`), edit/delete buttons (`w-5 h-5`). All need 44px. Fix all `gap-0.5` to `gap-2`.
- [x] **TEST:P3**Run `cd packages/app && npx vitest run src/__tests__/extractionQuality.test.ts` — must have 100+ tests all passing. Then run `pnpm typecheck && pnpm lint`.
- [ ] **T12**Fix MemoryPanel.vue, PersonaSelector.vue, and PluginMarketplace.vue buttons. Edit/delete memory buttons (`w-5 h-5`), persona buttons, plugin action buttons. All need 44px touch targets with 8px gaps.
## Phase 4: Archy Integration Tests
- [ ] **T13** — Fix MapRenderer.vue fullscreen button, BranchSwitcher.vue buttons, and any remaining small buttons found by searching for `w-5 h-5`, `w-6 h-6`, `w-7 h-7`, `w-8 h-8` on clickable elements across all `.vue` files. Each interactive element needs 44px minimum.
- [x] **T12**Create Archy integration test suite. File: `packages/app/src/__tests__/archyIntegration.test.ts`. Test: archyBridge message handling (mock postMessage for context:response, theme:response, permissions:update), useArchy composable (init when embedded, buildArchyContext output format with various data combinations, fetchPermittedContext with mocked bridge, requestAction delegation). Mock `window.parent` and `window.addEventListener`. Test wallet/file context formatting. Test base-aware API paths resolve correctly for both `/` and `/aiui/` base paths.
- [ ] **TEST:P3**Run `pnpm typecheck && pnpm lint && pnpm test`. Commit and push.
- [x] **T13** — Test ArchyAppsGrid component. Write tests verifying: renders app cards from archy-apps.ts data, shows correct status when merged with live data, action buttons work (open-app calls requestAction), graceful fallback when not embedded (shows informational cards only), glass-card styling applied.
## Phase 4: Touch Target Gaps & ContentPanel Tab Bar
- [x] **TEST:P4**Run `pnpm test && pnpm typecheck && pnpm lint`. All tests must pass.
- [ ] **T14**Fix ContentPanel.vue tab bar. Tab buttons likely have `gap-1` (4px) between them. Increase to `gap-2` (8px). Ensure each tab button has at least 44px height. The tab labels should use `text-xs` minimum (from Phase 1 fixes). The tab bar should be horizontally scrollable on mobile with sufficient padding.
## Phase 5: Code Mode UX Completion
- [ ] **T15** — Audit and fix ALL remaining `gap-0.5` and `gap-1` between clickable elements. Search all `.vue` files for `gap-0.5` and `gap-1`. For each result, check if the container holds clickable elements (buttons, links, tabs). If so, increase to `gap-2` (8px). Leave non-clickable layout gaps (like text spacing) unchanged.
- [x] **T14**Add Esc key to exit code mode on desktop. In ChatWindow.vue or ChatPage.vue, add a `keydown` event listener for Escape that calls `exitCodeMode()` from `useCodeContext` when code mode is active (`codeMode.value === true`). Only handle on desktop (not mobile). Add/cleanup in onMounted/onUnmounted.
- [ ] **T16**Fix grid card touch targets. In all `*Grid.vue` components, verify that cards have sufficient tap area. Cards themselves are usually fine (full-width), but action buttons within cards (favorite, play, expand) need 44px targets. Check: `FilmGrid.vue`, `SongGrid.vue`, `BookGrid.vue`, `TVSeriesGrid.vue`, `PodcastGrid.vue`, `AppsGrid.vue`, `ArchyAppsGrid.vue`, `ImageGrid.vue`, `PlaceGrid.vue`.
- [x] **T15**Show selected project indicator near chat input when in code mode. When `codeMode` is true and a project is selected (`currentProject` from `useCodeContext`), display the project name as a small pill/badge near the chat input area. Use accent/orange styling (bg-accent/15, text-accent).
- [ ] **TEST:P4**Run `pnpm typecheck && pnpm lint && pnpm test`. Commit and push.
- [x] **T16** — Wire selected design tokens + files into AI context. When sending a message in code mode, include selected design tokens and file contents as context in the system prompt. Read from `useCodeContext()`. Keep concise.
## Phase 5: Glass Button System & Utility Classes
- [x] **TEST:P5**Run `pnpm typecheck && pnpm lint`. Fix all failures before proceeding.
- [ ] **T17**Update `.glass-button` and `.glass-button-sm` in `packages/app/src/styles/main.css` to enforce 44px minimum height on mobile. Add a responsive rule: on screens ≤ 768px, `.glass-button` gets `min-height: 48px` and `.glass-button-sm` gets `min-height: 44px`. This catches all uses of these classes automatically. On desktop they stay as-is.
## Phase 6: Final Verification & Cleanup
- [ ] **T18** — Audit all `!h-7 !min-h-0` overrides. These force 28px height, defeating the glass button minimum. Search all `.vue` files for `!h-7` and `!min-h-0`. Replace with mobile-responsive approach: keep desktop size but ensure 44px minimum on mobile using `min-h-[44px] md:min-h-0`.
- [x] **T17**Final verification pass. Run `pnpm test` (all tests pass), `pnpm typecheck` (no type errors), `pnpm lint` (0 errors). Fix any issues. Commit all remaining changes with a summary commit message. Push to remote.
- [ ] **T19**Add a `.touch-target` utility class to `main.css`: `.touch-target { min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }`. Then refactor at least 15 icon buttons across the app to use this class instead of manual min-w/min-h. This creates a reusable pattern for future development.
- [ ] **TEST:P5** — Run `pnpm typecheck && pnpm lint && pnpm test`. Commit and push.
## Phase 6: Modals, Dialogs & Overlays
- [ ] **T20** — Fix PassphraseDialog.vue. Submit button uses `h-10` (40px, 4px short). Change to `h-11` (44px). Verify input field is `text-base`. Ensure the dialog has proper padding for mobile.
- [ ] **T21** — Audit all modal/overlay/dialog components for mobile compliance. Check: `ArticleOverlay.vue`, `ShareToNostr.vue`, `ConversationExport.vue`, `ComparisonMode.vue`, `NostrProfileEditor.vue`, `NostrRelayManager.vue`. Each should have: 44px button touch targets, 16px input text, 8px button gaps, proper padding on mobile.
- [ ] **T22** — Fix the sidebar/conversation list touch targets. `ConversationList.vue` or sidebar entries should have at least 44px height per item. Delete/rename buttons within list items need 44px targets.
- [ ] **TEST:P6** — Run `pnpm typecheck && pnpm lint && pnpm test`. Commit and push.
## Phase 7: Comprehensive Verification
- [ ] **T23** — Run a final grep audit to verify NO remaining violations:
- `text-[10px]`, `text-[9px]`, `text-[8px]` — should return 0 results in `packages/app/src/`
- `text-[11px]` — should return 0 results (all bumped to text-xs)
- `gap-0.5` between clickable elements — should be 0
- All inputs have `text-base` minimum
Fix any remaining violations found.
- [ ] **T24** — Run `pnpm test` (all tests pass), `pnpm typecheck` (no errors), `pnpm lint` (0 errors). Final commit with summary message. Push to remote.
+71 -52
View File
@@ -1,83 +1,102 @@
You are hardening the AIUI content extraction pipeline, integrating Archy services, and completing incomplete features. Read these files first:
You are making the AIUI app compliant with iOS Human Interface Guidelines (HIG) for font sizes, touch targets, and button hitboxes — WITHOUT changing the visual design. Read these files first:
1. `loop/plan.md` — Your task checklist (mark items `- [x]` as you complete them)
2. `CLAUDE.md` — Project conventions, design system rules, and coding standards
## Key Context
## Key Principle
### Content Extraction Pipeline
- `packages/app/src/composables/contentExtraction.ts` — Extracts books, films, TV, songs, podcasts, places, images, code blocks, apps from AI response text
- `packages/app/src/composables/contentFiltering.ts` — Query/response classifiers (isBookQuery, isTVQuery, etc.) and tab routing (filterTabsByContext)
- `packages/app/src/composables/useContentPanel.ts` — Wires extraction functions to Vue reactive state and tab system
- `packages/app/src/components/content/ContentPanel.vue` — Main content panel rendering grids and detail views
- `packages/app/src/components/content/ContentGridView.vue` — Grid view (already has ImageGrid/PlaceGrid wired)
- `packages/app/src/__tests__/extractionQuality.test.ts` — Extraction quality test suite (currently 64 tests)
**Same design, better accessibility.** The app should look identical but be properly tappable and readable on mobile. All changes are about sizing minimums, not visual redesign.
Content flows: AI response text → extraction functions → useContentPanel refs → filterTabsByContext → ContentPanel/ContentGridView rendering
## iOS HIG Requirements
### Archy Integration (CRITICAL — this is the main new work)
- `packages/app/src/services/archyBridge.ts` — PostMessage client for AIUI ↔ Archipelago IPC (already implemented)
- `packages/app/src/composables/useArchy.ts` — Reactive Vue wrapper (just created, needs extension)
- `packages/app/src/composables/useAI.ts` — Already injects Archy context via `buildArchyContext()` in `buildSystemPrompt()`
1. **Font Sizes:**
- Body text: 17px (but we use 14px base — OK, it's a dense app)
- Footnote: 13px minimum
- Caption: 11px ABSOLUTE minimum (nothing smaller than 11px ever)
- Form inputs: 16px minimum (prevents iOS auto-zoom on focus)
Archy is a self-hosted Linux node OS with 23+ apps. AIUI runs as an iframe inside Archy at `/aiui/`. Communication is via postMessage with 5 context categories: apps, system, network, wallet, files. The bridge is initialized when `?embedded=true` is in the URL.
2. **Touch Targets:**
- All interactive elements: 44×44px minimum tappable area
- The VISUAL icon can be smaller (e.g., 20px SVG), but the tappable wrapper must be 44×44px
- Pattern: `<button class="min-w-[44px] min-h-[44px] flex items-center justify-center"><svg class="w-5 h-5" ...></button>`
**Archy Services Available** (that AIUI should know about):
- Bitcoin Core (blockchain data, UTXO, fee estimates)
- LND / Core Lightning / Lightning Stack (channels, payments, invoices)
- BTCPay Server (payment processing)
- Mempool (blockchain explorer)
- Nextcloud (files, notes, contacts, calendar via WebDAV)
- Immich (photos, ML-tagging)
- Nostr relays (nostr-rs-relay, strfry)
- Home Assistant (smart home)
- Grafana (monitoring)
- SearXNG (privacy search)
- Ollama (local LLM)
- Penpot (design)
- OnlyOffice (documents)
- Fedimint (federated custody)
- Meshtastic (mesh networking)
- DID Wallet (Web5 identity)
3. **Gaps Between Targets:**
- Minimum 8px (gap-2) between any two tappable elements
- `gap-0.5` (2px) and `gap-1` (4px) between buttons is a violation
**Data Handshake**: AIUI requests context via `archyBridge.requestContext(category)`. Archy's contextBroker responds with sanitized data (no secrets, no IPs, no keys). Currently `wallet` and `files` categories are stubs in Archy — build the AIUI consumer side anyway so it's ready when Archy implements them. Use realistic mock shapes.
## Current Violations Found (from audit)
**API Path Note**: All API paths use `import.meta.env.BASE_URL` prefix so they work at both `/` (dev) and `/aiui/` (production). This was just implemented — don't change it.
- **301× `text-[10px]`** — timestamps, labels, metadata throughout app
- **76× `text-[11px]`** — settings, branch switcher, design system
- **100× `text-[9px]` / `text-[8px]`** — badges, version numbers, plugin metadata
- **40+ buttons below 44×44px** — `w-7 h-7` (28px), `w-8 h-8` (32px), `w-6 h-6` (24px), `w-5 h-5` (20px)
- **20+ locations with gap < 8px** between clickable elements
- **9+ inputs below 16px** — ChatInput textarea, settings inputs, search
## Key Files
- `packages/app/src/styles/main.css` — Glass button system, utility classes
- `packages/app/src/components/chat/ChatMessage.vue` — Message actions, timestamps
- `packages/app/src/components/chat/ChatInput.vue` — Main input, send button
- `packages/app/src/components/chat/ChatHeader.vue` — Toolbar buttons
- `packages/app/src/components/chat/ChatSearch.vue` — Search nav buttons
- `packages/app/src/components/chat/SettingsModal.vue` — Settings UI
- `packages/app/src/components/content/ContentPanel.vue` — Tab bar
- `packages/app/src/components/content/*Grid.vue` — All content grids
- `packages/app/src/components/content/*Detail.vue` — All detail views
- `packages/app/src/components/settings/` — Settings components
- `packages/app/src/components/ui/` — Shared UI components
## Design System Rules (must follow for every file you touch)
- **Glass morphism only** — use `.glass`, `.glass-card`, `.glass-button`, `.glass-button-sm` from `src/styles/main.css`
- **No light-mode conditionals** — never use `isDark ?` pattern. The app is dark-only. Remove any existing light-mode code you encounter.
- **No light-mode conditionals** — never use `isDark ?` pattern. The app is dark-only.
- **No `bg-gray-*` or `bg-white`** — use `bg-white/5`, `bg-white/10`, `bg-black/35` etc.
- **Text opacity scale** — `text-white/25``/40``/60``/70``/80``/90``/96``text-white`
- **Accent colour** — `text-accent` (`#F7931A`), `bg-accent/15`, `border-accent/30` for Bitcoin-orange highlights
- **Touch targets** — min 44×44px, 8px gaps between targets
- **Font size** — min `16px` on all inputs (prevents iOS zoom)
- **Accent colour** — `text-accent` (`#F7931A`), `bg-accent/15`, `border-accent/30`
- **Animations** — `animate-scale-in`, `animate-fade-up`, `animate-fade-in` from design system only
## How to Fix Touch Targets (Pattern)
**Before (violation):**
```html
<button class="w-7 h-7 rounded-full" @click="doThing">
<svg class="w-4 h-4" ...></svg>
</button>
```
**After (compliant):**
```html
<button class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-full" @click="doThing">
<svg class="w-4 h-4" ...></svg>
</button>
```
Or use the utility class (after T19):
```html
<button class="touch-target rounded-full" @click="doThing">
<svg class="w-4 h-4" ...></svg>
</button>
```
## For each task in loop/plan.md:
1. Find the first unchecked `- [ ]` item
2. Read the task description carefully
3. Read the relevant source files before making changes
4. Implement the feature/fix following conventions and design system rules
5. Run `pnpm typecheck && pnpm lint` — fix all errors before committing
4. Make the change — keep visual design identical, only adjust sizing
5. Run `pnpm typecheck && pnpm lint` — fix all errors
6. Run `pnpm test` — fix any failing tests
7. If the task involves tests, run `cd packages/app && npx vitest run` specifically
8. Commit: `type(scope): description` (conventional commits)
9. Mark it done `- [x]` in `loop/plan.md`
10. Move to the next unchecked task immediately
7. Commit: `type(scope): description` (conventional commits)
8. Mark it done `- [x]` in `loop/plan.md`
9. Move to the next unchecked task immediately
## Rules
- Never skip a testing gate — if tests fail, fix them before moving on
- If a task is proving difficult, keep trying different approaches. Make at least 30 genuine attempts before giving up and moving on.
- If a task is proving difficult, keep trying different approaches. Make at least 30 genuine attempts.
- Always run typecheck + lint after every code change
- Keep initial bundle < 250 KB gzipped — lazy-load anything heavy
- Bitcoin only — never altcoins, never fiat payment flows
- Privacy first — no external analytics calls, no telemetry
- The visual design must NOT change — same colors, same layout, same glass morphism
- Only sizes of tappable areas, font minimums, and gaps change
- Do not stop until all tasks are checked or you are rate limited
- When adding tests, aim for realistic AI response patterns, not trivial examples
- When wiring components into ContentPanel.vue, follow the exact same pattern used for existing content types (BookGrid/BookDetail, TVSeriesGrid/TVSeriesDetail, etc.)
- When building Archy integration, the AIUI side should be resilient — always handle the case where Archy doesn't respond or data is empty
- Commit and push after each completed phase (TEST: tasks)
- Commit and push after each TEST: task