Fixed:
- Price: €800K-€1.5M, Sunseeker added
- Agent 1: Joe Trader persona + actual sale ads research
- Ignored meilisearch binary + data/ (too large for GitHub)
- SESSION_DEBUG_BLOCKERS.md created
Ready for Session 1 launch.
🤖 Generated with Claude Code
16 KiB
Technical Decisions Summary
Project: Document Viewer Search Improvements Date: 2025-10-21 Status: Approved Design
Key Decisions Matrix
1. Component Architecture
| Decision | Chosen Approach | Rationale | Alternatives Considered |
|---|---|---|---|
| Search Results Component | Unified SearchResults.vue shared by DocumentView and SearchView |
DRY principle, consistent UX, easier maintenance | Separate components per view (rejected: code duplication) |
| Result Card | Extract to SearchResultCard.vue |
Reusable across dropdown and full-page views | Inline template (rejected: hard to test) |
| Dropdown Container | Separate SearchDropdown.vue with Teleport |
Clean separation of concerns, portal for z-index | Inline in DocumentView (rejected: messy code) |
| Navigation Controls | CompactNavControls.vue component |
Encapsulates nav logic, reusable | Mix with DocumentView (rejected: poor separation) |
Decision: Component-based architecture with clear responsibilities
2. State Management
| Decision | Chosen Approach | Rationale | Alternatives Considered |
|---|---|---|---|
| Global State | NO global store (Pinia/Vuex) | Search is ephemeral, component-scoped state sufficient | Pinia store (rejected: unnecessary complexity) |
| Composables | useDocumentSearch.js, useSearchResults.js |
Composition API pattern, code reuse without coupling | Mixin-based (rejected: Vue 3 deprecates mixins) |
| Search Caching | In-memory Map with 5-min TTL | Fast repeat queries, automatic cleanup | localStorage (rejected: not sensitive data) |
| Result Grouping | Computed property in composable | Reactive, declarative, no manual updates | Manual state management (rejected: error-prone) |
Decision: Composition API with reactive composables, no global store
3. API Design
| Decision | Chosen Approach | Rationale | Alternatives Considered |
|---|---|---|---|
| Document Scoping | Add currentDocumentId param to existing /api/search |
Backward compatible, single endpoint | New /api/search/scoped endpoint (rejected: fragmentation) |
| Grouping Metadata | Return grouping object in response |
Frontend can display counts without recalculation | Client-side grouping only (rejected: missed optimization) |
| Result Prioritization | Backend adds _isCurrentDoc flag |
Single source of truth, less client logic | Client-only sorting (rejected: duplicates logic) |
| Pagination | Existing limit/offset params |
Already implemented, works well | Cursor-based (rejected: overkill) |
Decision: Extend existing API with minimal changes, maintain backward compatibility
API Contract:
POST /api/search
Request: {
"q": "query",
"currentDocumentId": "doc-123", // NEW (optional)
"limit": 50
}
Response: {
"hits": [...],
"grouping": { // NEW
"currentDocument": { "hitCount": 8 },
"otherDocuments": { "hitCount": 12, "documentCount": 3 }
}
}
4. CSS/Positioning Strategy
| Decision | Chosen Approach | Rationale | Alternatives Considered |
|---|---|---|---|
| Navigation Bar | position: sticky; top: 0 |
Smooth scroll behavior, no JS needed | position: fixed (rejected: layout jumps) |
| Search Dropdown | position: fixed + Teleport to body |
Overlays content, no z-index issues | Absolute position (rejected: overflow issues) |
| Backdrop Blur | CSS backdrop-filter: blur(16px) |
Native browser support, GPU accelerated | JS blur library (rejected: performance cost) |
| Transitions | CSS transitions only | 60fps, hardware accelerated | JS animation libraries (rejected: bundle size) |
| Mobile Layout | Full-screen modal (< 768px), dropdown (> 768px) | Better touch UX, more space on mobile | Same dropdown everywhere (rejected: poor mobile UX) |
Decision: Modern CSS features (sticky, backdrop-filter) with progressive enhancement
Browser Support:
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- All features have 95%+ browser support as of 2025
5. Search UX Pattern
| Decision | Chosen Approach | Rationale | Alternatives Considered |
|---|---|---|---|
| DocumentView Search | Dropdown results | Non-intrusive, keeps context | Full-page (rejected: disrupts reading), Sidebar (rejected: screen space) |
| Result Grouping | "This Document" first, then "Other Documents" | Users prioritize current doc 80% of time | Flat list (rejected: poor scannability) |
| Debounce Delay | 300ms | Balance between responsiveness and API load | 500ms (rejected: feels slow), 100ms (rejected: too many requests) |
| Results Per Group | 5 current doc, 5 other docs, "Show more" button | Compact, shows variety, infinite scroll option | Show all (rejected: overwhelming), 3 per group (rejected: too few) |
| Keyboard Navigation | Arrow keys, Enter, Escape | Power user efficiency | Mouse-only (rejected: poor accessibility) |
Decision: Google-like dropdown with intelligent grouping
UX Flow:
User types → 300ms debounce → API call → Dropdown renders
↓
"This Document (8)" ← Always first
Page 12: ...
Page 15: ...
[5 results max]
"Other Documents (4)" ← Collapsed by default on mobile
Engine Manual p8: ...
[Show 8 more]
Click result → Navigate to page → Dropdown closes
6. Performance Optimizations
| Decision | Chosen Approach | Rationale | Alternatives Considered |
|---|---|---|---|
| Search Debouncing | useDebounceFn from @vueuse/core |
Battle-tested, TypeScript support | Custom debounce (rejected: reinventing wheel) |
| Result Caching | 5-minute in-memory cache | Repeat searches instant | IndexedDB (rejected: overkill), No cache (rejected: poor UX) |
| Virtual Scrolling | Only if > 100 results | Most searches return < 50 results | Always virtual scroll (rejected: complexity), Never (rejected: performance) |
| Image Lazy Loading | IntersectionObserver API |
Native, no library needed | Eager loading (rejected: slow), Library (rejected: bundle size) |
| Bundle Splitting | Code split search components | Faster initial load | Single bundle (rejected: larger initial load) |
Decision: Pragmatic optimizations based on real-world usage
Performance Targets:
- Search input → API response: < 100ms (p90)
- API response → Dropdown render: < 50ms
- Dropdown open/close: 60fps (16ms per frame)
- Total (type to see results): < 400ms
7. Accessibility
| Decision | Chosen Approach | Rationale | Alternatives Considered |
|---|---|---|---|
| ARIA Roles | role="combobox", role="listbox", role="option" |
WCAG 2.1 AA compliance | No ARIA (rejected: fails screen readers) |
| Keyboard Navigation | Full keyboard support (Tab, Arrow, Enter, Escape) | Power users, accessibility requirement | Mouse-only (rejected: excludes users) |
| Focus Management | Trap focus in dropdown, restore on close | Prevents keyboard users getting lost | No focus trap (rejected: poor UX) |
| Color Contrast | 7:1 for text, 4.5:1 for highlights (AAA) | Readable for low vision users | 4.5:1 everywhere (rejected: not AAA) |
| Screen Reader | Live regions for result count announcements | Blind users know search completed | Silent (rejected: no feedback) |
Decision: WCAG 2.1 AAA where feasible, AA minimum
ARIA Structure:
<div role="combobox" aria-expanded="true" aria-owns="results-listbox">
<input role="searchbox" aria-label="Search documents" />
</div>
<ul id="results-listbox" role="listbox" aria-label="Search results">
<li role="option" aria-selected="false">...</li>
</ul>
<div aria-live="polite">8 results found</div>
8. Testing Strategy
| Decision | Chosen Approach | Rationale | Alternatives Considered |
|---|---|---|---|
| Unit Tests | Vitest | Fast, Vue-native, Vite integration | Jest (rejected: slower setup) |
| Component Tests | @vue/test-utils | Official Vue testing library | Testing Library (rejected: different philosophy) |
| E2E Tests | Playwright | Modern, reliable, cross-browser | Cypress (rejected: fewer browsers), Selenium (rejected: slow) |
| Coverage Target | 80% for new code | Pragmatic balance | 100% (rejected: diminishing returns), None (rejected: risky) |
| CI Integration | GitHub Actions on PR | Automated, free for OSS | Manual testing (rejected: error-prone) |
Decision: Comprehensive testing with pragmatic coverage goals
Test Pyramid:
/\
/ \ E2E (10 tests)
/____\ - Search flow
/ \ - Navigation
/________\ - Mobile UX
/ \ Integration (30 tests)
/__________\ - Component interactions
- Composable logic
/ \ Unit (60 tests)
/______________\ - SearchResults.vue
- useDocumentSearch.js
- Result grouping logic
9. Security
| Decision | Chosen Approach | Rationale | Alternatives Considered |
|---|---|---|---|
| XSS Prevention | Vue auto-escaping + v-html only for Meilisearch responses |
Meilisearch sanitizes HTML | DOMPurify library (rejected: Meilisearch already safe) |
| Query Sanitization | 200 char limit, strip <> characters |
Prevent injection attacks | No sanitization (rejected: vulnerable) |
| Row-Level Security | Existing tenant tokens + org filtering | Already implemented | Client-side filtering only (rejected: data leak) |
| Rate Limiting | Debounce (300ms) + server-side rate limit | Prevent abuse | No limit (rejected: DoS vulnerable) |
Decision: Leverage existing security model, minimal additional code
10. Migration & Rollout
| Decision | Chosen Approach | Rationale | Alternatives Considered |
|---|---|---|---|
| Rollout Strategy | Phased: DocumentView → SearchView → HomeView | Test each integration point | Big bang (rejected: risky), Feature flag (rejected: complexity) |
| Backward Compatibility | Keep old components until full rollout | Safe rollback | Delete old code immediately (rejected: no rollback) |
| Database Migrations | None needed | No schema changes | Add search history table (rejected: Phase 2 feature) |
| Deployment | Standard CI/CD pipeline | No special process | Blue-green deployment (rejected: overkill for this change) |
Decision: Low-risk phased rollout with rollback capability
Timeline:
Week 1: Foundation components (can test in isolation)
Week 2: DocumentView integration (feature works end-to-end)
Week 3: SearchView refactor (remove duplication)
Week 4: HomeView polish + final QA
Decision Ownership
| Area | Owner | Reviewer | Status |
|---|---|---|---|
| Component Architecture | Tech Lead | Senior Engineer | ✅ Approved |
| API Design | Backend Lead | Tech Lead | ✅ Approved |
| UX Design | Product Designer | UX Lead | ✅ Approved |
| Performance | Tech Lead | DevOps | ✅ Approved |
| Accessibility | Frontend Lead | Accessibility Specialist | ✅ Approved |
| Testing | QA Lead | Tech Lead | ✅ Approved |
Trade-offs Acknowledged
1. No Pinia/Vuex Store
Trade-off: Each view has its own search instance Benefit: Simpler code, no global state pollution Cost: Can't share search results between views Mitigation: HTTP cache makes repeat queries fast anyway
2. Component-based vs. Renderless
Trade-off: SearchResults.vue has markup (not renderless)
Benefit: Easier to understand, faster to implement
Cost: Less flexible for advanced customization
Mitigation: Props allow sufficient customization for our needs
3. CSS Sticky vs. Fixed
Trade-off: Sticky doesn't overlay content Benefit: Smoother scroll behavior, no layout shift Cost: Scrolls out of view on long documents Mitigation: Users rarely scroll far with search open
4. Dropdown vs. Modal
Trade-off: Dropdown has limited space Benefit: Non-intrusive, keeps context Cost: May truncate results on small screens Mitigation: Full-screen modal on mobile (< 768px)
Success Metrics (KPIs)
| Metric | Baseline | Target | Measurement |
|---|---|---|---|
| Search response time | 150ms (p90) | < 100ms (p90) | Server logs |
| Time to first result | N/A (no search) | < 400ms | Lighthouse |
| Search success rate | 60% (estimate) | 80% | Analytics |
| Keyboard navigation usage | 0% | 15% | Event tracking |
| Accessibility violations | Unknown | 0 (axe-core) | CI pipeline |
| Bundle size increase | 0 KB | < 50 KB (gzipped) | Webpack stats |
| Test coverage | 0% (new code) | 80% | Vitest report |
Risk Assessment
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Performance degradation | Low | High | Benchmarks before/after, load testing |
| Accessibility issues | Medium | High | Automated testing (axe-core), manual audit |
| Cross-browser bugs | Medium | Medium | Playwright tests on Chrome, Firefox, Safari |
| Mobile UX problems | Low | Medium | Responsive design from day 1, mobile testing |
| API breaking changes | Low | High | Backward compatible API, versioning |
| Code duplication | Medium | Low | Shared component library, code review |
Overall Risk: LOW (well-defined scope, incremental rollout)
Open Questions (Deferred to Phase 2)
-
Search History: Store recent searches in localStorage?
- Decision: Phase 2 feature
- Reasoning: Nice-to-have, not critical for MVP
-
Search Suggestions: Auto-complete while typing?
- Decision: Phase 2 feature
- Reasoning: Requires additional Meilisearch config
-
Advanced Filters: Filter by document type, date, etc.?
- Decision: Phase 2 feature
- Reasoning: SearchView already has basic filters
-
Search Analytics: Track queries for insights?
- Decision: Phase 2 feature
- Reasoning: Privacy considerations, analytics setup
-
Offline Search: IndexedDB for offline mode?
- Decision: Phase 2 feature (PWA enhancement)
- Reasoning: Requires service worker, complex sync
Final Recommendation
APPROVED for implementation with the following conditions:
- ✅ Phased rollout: Start with DocumentView, validate before continuing
- ✅ Test coverage: 80% minimum for new code
- ✅ Performance: Maintain < 100ms search latency (p90)
- ✅ Accessibility: Zero violations in axe-core audit
- ✅ Rollback plan: Keep old components until full deployment
Next Steps:
- Create GitHub issues for each phase
- Assign to frontend engineer
- Schedule design review after Phase 1
- Schedule QA after Phase 3
- Deploy to production after Phase 4
Document Version: 1.0 Last Updated: 2025-10-21 Status: Ready for Implementation
Appendix: Technology Stack
| Layer | Technology | Version | Purpose |
|---|---|---|---|
| Frontend Framework | Vue 3 | 3.5.0 | UI components |
| Composition API | @vueuse/core | Latest | Utilities (debounce, etc.) |
| Search Engine | Meilisearch | 0.41.0 | Full-text search |
| HTTP Client | Fetch API | Native | API calls |
| Router | Vue Router | 4.4.0 | Navigation |
| Build Tool | Vite | 5.0.0 | Bundler |
| CSS Framework | Tailwind CSS | 3.4.0 | Styling |
| PDF Rendering | PDF.js | 4.0.0 | Document viewer |
| Testing | Vitest + Playwright | Latest | Unit + E2E tests |
| Backend | Express.js | Latest | API server |
| Database | SQLite | Latest | Metadata storage |