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:
co-authored by
Claude Opus 4.6
parent
b2fcc23623
commit
723dd6e4d3
+5
-5
@@ -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)"
|
||||
|
||||
Reference in New Issue
Block a user