fix(app): iOS HIG Phase 2 — chat interface touch targets (44px minimum)

- ChatMessage: action buttons w-7→44px, gap-0.5→gap-2
- ChatHeader: toolbar buttons w-8/w-9→44px
- ChatInput: attach button w-8→44px, reply close w-5→44px
- ChatSearch: prev/next/close buttons w-6→44px
- ArticleReader: all toolbar buttons w-8→44px
- PdfViewer: all nav/zoom buttons w-8→44px

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-04 12:56:24 +00:00
co-authored by Claude Opus 4.6
parent b2fcc23623
commit 723dd6e4d3
7 changed files with 36 additions and 36 deletions
+5 -5
View File
@@ -6,15 +6,15 @@
## Phase 1: Input & Text Foundation (Prevent iOS Zoom + Readability)
- [ ] **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] **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.
- [ ] **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] **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.
- [ ] **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] **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]`.
- [ ] **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`.
- [x] **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`.
- [ ] **TEST:P1** — Run `pnpm typecheck && pnpm lint && pnpm test`. Fix all failures. Commit and push.
- [x] **TEST:P1** — Run `pnpm typecheck && pnpm lint && pnpm test`. Fix all failures. Commit and push.
## Phase 2: Touch Targets — Chat Interface (44×44px Minimum)
@@ -6,7 +6,7 @@
<div class="flex items-center justify-between gap-2">
<button
ref="modelPickerTriggerRef"
class="w-8 h-8 rounded-xl path-glass-icon flex items-center justify-center shrink-0 transition-colors cursor-pointer text-[#fafafa] hover:text-white"
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center shrink-0 transition-colors cursor-pointer text-[#fafafa] hover:text-white"
:title="`AI model: ${modelDisplayName}`"
aria-label="Select AI model"
@click="showModelPicker = !showModelPicker"
@@ -15,7 +15,7 @@
</button>
<div class="flex items-center gap-2 shrink-0">
<button
class="w-9 h-9 rounded-xl path-glass-icon flex items-center justify-center transition-colors"
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center transition-colors"
:class="webSearchEnabled
? 'text-accent'
: 'text-white/70 hover:text-white'"
@@ -29,7 +29,7 @@
</button>
<button
class="w-9 h-9 rounded-xl path-glass-icon flex items-center justify-center transition-colors"
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center transition-colors"
:class="chatStore.showHistory
? 'text-accent'
: 'text-white/70 hover:text-white'"
@@ -43,7 +43,7 @@
</button>
<button
class="w-9 h-9 rounded-xl path-glass-icon flex items-center justify-center transition-colors"
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center transition-colors"
:class="chatStore.chatCollapsed
? 'text-accent'
: 'text-white/70 hover:text-white'"
@@ -60,7 +60,7 @@
</button>
<button
class="w-9 h-9 rounded-xl path-glass-icon flex items-center justify-center transition-colors"
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center transition-colors"
:class="comparison.isComparing.value
? 'text-accent'
: 'text-white/70 hover:text-white'"
@@ -74,7 +74,7 @@
</button>
<button
class="w-9 h-9 rounded-xl path-glass-icon flex items-center justify-center transition-colors text-white/70 hover:text-white"
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center transition-colors text-white/70 hover:text-white"
aria-label="Settings"
title="Settings"
@click="$emit('openSettings')"
@@ -87,7 +87,7 @@
<button
ref="menuTriggerRef"
class="w-9 h-9 rounded-xl path-glass-icon flex items-center justify-center transition-colors text-white/70 hover:text-white"
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center transition-colors text-white/70 hover:text-white"
aria-label="Conversation menu"
@click="showMenu = !showMenu"
>
@@ -97,7 +97,7 @@
</button>
<button
class="w-9 h-9 rounded-xl path-glass-icon flex items-center justify-center transition-colors text-white/70 hover:text-white"
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center transition-colors text-white/70 hover:text-white"
aria-label="New conversation"
@click="$emit('newChat')"
>
@@ -108,7 +108,7 @@
<button
v-if="showClose"
class="w-9 h-9 rounded-xl path-glass-icon flex items-center justify-center transition-colors text-white/70 hover:text-white"
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center transition-colors text-white/70 hover:text-white"
aria-label="Close"
@click="$emit('close')"
>
@@ -22,7 +22,7 @@
<p class="text-xs text-white/50 truncate">{{ replyTo.excerpt }}</p>
</div>
<button
class="shrink-0 w-5 h-5 flex items-center justify-center rounded-md hover:bg-white/10 transition-colors text-white/40 hover:text-white/70"
class="shrink-0 min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md hover:bg-white/10 transition-colors text-white/40 hover:text-white/70"
aria-label="Cancel reply"
@click="$emit('clearReply')"
>
@@ -88,7 +88,7 @@
<!-- Image attach button -->
<button
v-if="!streaming && images.length < MAX_IMAGES"
class="shrink-0 w-8 h-8 flex items-center justify-center rounded-lg text-white/40 hover:text-white/70 hover:bg-white/10 transition-all"
class="shrink-0 min-w-[44px] min-h-[44px] flex items-center justify-center rounded-lg text-white/40 hover:text-white/70 hover:bg-white/10 transition-all"
aria-label="Attach image"
@click="openFilePicker"
>
@@ -12,10 +12,10 @@
class="absolute -top-4 opacity-0 group-hover/msg:opacity-100 transition-opacity duration-200 z-10"
:class="isUser ? 'right-1' : 'left-1'"
>
<div class="flex items-center gap-0.5 px-1 py-0.5 rounded-lg bg-black/60 backdrop-blur-md border border-white/10 shadow-lg">
<div class="flex items-center gap-2 px-1 py-0.5 rounded-lg bg-black/60 backdrop-blur-md border border-white/10 shadow-lg">
<button
v-if="isUser"
class="w-7 h-7 flex items-center justify-center rounded-md text-white/50 hover:text-white/90 hover:bg-white/10 transition-colors"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md text-white/50 hover:text-white/90 hover:bg-white/10 transition-colors"
title="Edit message"
@click.stop="startEditing"
>
@@ -23,14 +23,14 @@
</button>
<button
v-if="!isUser"
class="w-7 h-7 flex items-center justify-center rounded-md text-white/50 hover:text-white/90 hover:bg-white/10 transition-colors"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md text-white/50 hover:text-white/90 hover:bg-white/10 transition-colors"
title="Regenerate response"
@click.stop="$emit('regenerate')"
>
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z" clip-rule="evenodd" /></svg>
</button>
<button
class="w-7 h-7 flex items-center justify-center rounded-md text-white/50 hover:text-white/90 hover:bg-white/10 transition-colors"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md text-white/50 hover:text-white/90 hover:bg-white/10 transition-colors"
title="Reply"
@click.stop="$emit('reply', message.id, message.content)"
>
@@ -38,7 +38,7 @@
</button>
<button
v-if="!isUser"
class="w-7 h-7 flex items-center justify-center rounded-md text-white/50 hover:text-white/90 hover:bg-white/10 transition-colors"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md text-white/50 hover:text-white/90 hover:bg-white/10 transition-colors"
title="Branch from here"
@click.stop="$emit('branch', message.id)"
>
@@ -47,7 +47,7 @@
<div v-if="!isUser" class="w-px h-4 bg-white/10 mx-0.5" />
<button
v-if="!isUser"
class="w-7 h-7 flex items-center justify-center rounded-md transition-colors"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md transition-colors"
:class="message.feedback === 'up' ? 'text-green-400' : 'text-white/50 hover:text-green-400/80 hover:bg-white/10'"
title="Good response"
@click.stop="toggleFeedback('up')"
@@ -56,7 +56,7 @@
</button>
<button
v-if="!isUser"
class="w-7 h-7 flex items-center justify-center rounded-md transition-colors"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md transition-colors"
:class="message.feedback === 'down' ? 'text-red-400' : 'text-white/50 hover:text-red-400/80 hover:bg-white/10'"
title="Poor response"
@click.stop="toggleFeedback('down')"
@@ -22,7 +22,7 @@
{{ matchCount > 0 ? `${currentMatchIndex + 1}/${matchCount}` : 'No results' }}
</span>
<button
class="w-6 h-6 flex items-center justify-center rounded-md hover:bg-white/10 transition-colors text-white/50 hover:text-white/80 disabled:opacity-30"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md hover:bg-white/10 transition-colors text-white/50 hover:text-white/80 disabled:opacity-30"
:disabled="matchCount === 0"
aria-label="Previous match"
@click="prevMatch"
@@ -32,7 +32,7 @@
</svg>
</button>
<button
class="w-6 h-6 flex items-center justify-center rounded-md hover:bg-white/10 transition-colors text-white/50 hover:text-white/80 disabled:opacity-30"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md hover:bg-white/10 transition-colors text-white/50 hover:text-white/80 disabled:opacity-30"
:disabled="matchCount === 0"
aria-label="Next match"
@click="nextMatch"
@@ -42,7 +42,7 @@
</svg>
</button>
<button
class="w-6 h-6 flex items-center justify-center rounded-md hover:bg-white/10 transition-colors text-white/50 hover:text-white/80"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md hover:bg-white/10 transition-colors text-white/50 hover:text-white/80"
aria-label="Close search"
@click="close"
>
@@ -25,7 +25,7 @@
<!-- Header bar -->
<div class="sticky top-0 z-10 flex items-center gap-2 px-4 py-2 bg-black/60 backdrop-blur-md border-b border-white/5">
<button
class="w-8 h-8 flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors"
title="Back"
@click="$emit('back')"
>
@@ -39,7 +39,7 @@
<!-- TOC toggle (mobile) -->
<button
v-if="headings.length > 1"
class="lg:hidden w-8 h-8 flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors"
class="lg:hidden min-w-[44px] min-h-[44px] flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors"
title="Table of contents"
@click="showMobileToc = !showMobileToc"
>
@@ -50,7 +50,7 @@
<!-- Font size -->
<button
class="w-8 h-8 flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors"
title="Decrease font size"
:disabled="fontSizeIdx <= 0"
@click="fontSizeIdx = Math.max(0, fontSizeIdx - 1)"
@@ -58,7 +58,7 @@
<span class="text-xs font-bold">A-</span>
</button>
<button
class="w-8 h-8 flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors"
title="Increase font size"
:disabled="fontSizeIdx >= fontSizes.length - 1"
@click="fontSizeIdx = Math.min(fontSizes.length - 1, fontSizeIdx + 1)"
@@ -68,7 +68,7 @@
<!-- Print -->
<button
class="w-8 h-8 flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors"
title="Print"
@click="printArticle"
>
@@ -3,7 +3,7 @@
<!-- Toolbar -->
<div class="flex items-center gap-2 px-4 py-2 bg-black/60 backdrop-blur-md border-b border-white/5 shrink-0">
<button
class="w-8 h-8 flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors"
title="Back"
@click="$emit('back')"
>
@@ -16,7 +16,7 @@
<!-- Page nav -->
<button
class="w-8 h-8 flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors disabled:opacity-30"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors disabled:opacity-30"
:disabled="currentPage <= 1"
title="Previous page"
@click="goToPage(currentPage - 1)"
@@ -27,7 +27,7 @@
</button>
<span class="text-xs text-white/50 tabular-nums min-w-[60px] text-center">{{ currentPage }} / {{ totalPages }}</span>
<button
class="w-8 h-8 flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors disabled:opacity-30"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors disabled:opacity-30"
:disabled="currentPage >= totalPages"
title="Next page"
@click="goToPage(currentPage + 1)"
@@ -39,7 +39,7 @@
<!-- Zoom -->
<button
class="w-8 h-8 flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors disabled:opacity-30"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors disabled:opacity-30"
:disabled="scale <= 0.5"
title="Zoom out"
@click="scale = Math.max(0.5, scale - 0.25)"
@@ -48,7 +48,7 @@
</button>
<span class="text-xs text-white/40 tabular-nums w-10 text-center">{{ Math.round(scale * 100) }}%</span>
<button
class="w-8 h-8 flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors disabled:opacity-30"
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-lg text-white/60 hover:text-white/80 hover:bg-white/10 transition-colors disabled:opacity-30"
:disabled="scale >= 2"
title="Zoom in"
@click="scale = Math.min(2, scale + 0.25)"