UI/UX Part 11 — Search & Local Knowledge Retrieval UX Architecture
Reusable P2P Communication Platform
Status: UI/UX architecture specification
UI Series: Part 11
Desktop UI: Dioxus
Android UI: Kotlin + Jetpack Compose
Core runtime: Rust
Primary purpose: define global search, conversation search, file/contact/link discovery, local knowledge retrieval, optional semantic search, index/rebuild states, privacy, source navigation, pagination, and the Rust presentation contracts for both UIs.
1. Governing Principle
Search should feel global and immediate to the user while remaining local-first, privacy-preserving, source-grounded, and reconstructable from authoritative Rust state.
Search must never become a hidden cloud copy of the user's private communication history.
2. Architectural Position
Rust Authoritative Data
│
├── Messages
├── Files
├── Contacts
├── Calls
├── Links
└── Plugin Data
│
▼
Rust Search / Index Layer
│
├── lexical index
├── metadata index
├── optional semantic index
└── result ranking
│
▼
Search Presentation Service
│
┌────┴─────┐
│ │
Dioxus Compose
Desktop Android
3. Search Modes
#![allow(unused)] fn main() { pub enum SearchMode { Global, Conversation(ConversationId), Contacts, Files, Links, } }
Future optional modes:
Calls
Plugins
Knowledge
4. Search Domains
#![allow(unused)] fn main() { pub enum SearchDomain { Messages, Files, Contacts, Links, Calls, Plugins, } }
5. Global Search
Searches all locally available, authorized content:
Messages
Files
Contacts
Links
Calls
Plugin data
The result set is filtered by Rust authorization rules before it reaches either UI.
6. Conversation Search
Scoped to one conversation.
Prioritizes:
messages
attachments
links
and supports jump-to-message.
7. Inbox Filter Is Not Full Search
Part 04 inbox filtering only filters conversation summaries.
Part 11 full search queries durable indexed content.
8. Search Entry Points
Desktop:
Ctrl/Cmd+K Command palette / navigation
Ctrl/Cmd+Shift+F Global content search
Ctrl/Cmd+F Current conversation search
Android:
Search icon
Dedicated Search destination
Conversation app-bar search
9. Query Model
#![allow(unused)] fn main() { pub struct SearchQuery { pub text: String, pub scope: SearchScope, pub filters: SearchFilters, pub mode: SearchQueryMode, } }
10. Query Modes
#![allow(unused)] fn main() { pub enum SearchQueryMode { Lexical, Semantic, Hybrid, } }
Recommended v1 default:
Lexical
Semantic/hybrid can be added later behind the same presentation contract.
11. Search Scope
#![allow(unused)] fn main() { pub enum SearchScope { Global, Conversation(ConversationId), Contact(AccountId), Group(GroupId), Domain(SearchDomain), } }
12. Filters
Recommended:
Sender
Conversation
Date
File type
Has attachment
Unread
Message type
Advanced later:
Exact phrase
Before
After
Mentions me
From me
Has link
13. Desktop Global Search Layout
+-----------------------------------------------------------+
| Search [____________________________________] Filters |
+-----------------------+-----------------------------------+
| Categories | Results |
| All | |
| Messages | |
| Files | |
| Contacts | |
| Links | |
| Calls | |
+-----------------------+-----------------------------------+
| Optional Preview / Source Context |
+-----------------------------------------------------------+
14. Android Global Search Layout
Search App Bar
Filter Chips
Result Sections
LazyColumn
On tablets/foldables:
Result List
+
Source Preview
when space allows.
15. Search Debounce
Recommended:
Desktop: 100–200 ms
Android: 150–300 ms
Tune through measurement.
16. Cancellation
Each request has a stable identity.
#![allow(unused)] fn main() { pub struct SearchRequestId(pub u64); }
When the query changes:
cancel/ignore stale request
Only the latest request may update visible results.
17. Search Result Types
#![allow(unused)] fn main() { pub enum SearchResultView { Message(MessageSearchResultView), File(FileSearchResultView), Contact(ContactSearchResultView), Link(LinkSearchResultView), Call(CallSearchResultView), Plugin(PluginSearchResultView), } }
18. Message Search Result
#![allow(unused)] fn main() { pub struct MessageSearchResultView { pub message: MessageId, pub conversation: ConversationId, pub sender: SenderView, pub snippet: SearchSnippet, pub sent_at: Timestamp, pub rank: SearchRank, } }
19. Result Snippets
Show a concise local snippet with matching terms emphasized.
Do not render an entire long message just because it matched.
20. Result Highlighting
Use visual emphasis plus normal readable text.
Do not rely on color alone.
21. File Search Result
Show:
filename
file type
conversation
sender
date
content snippet if indexed
availability state if useful
22. Contact Search Result
Show:
name
relationship
verification state
23. Link Search Result
Show:
domain
title if known
conversation
sender
date
24. Call Search Result
If call history is indexed:
contact/group
date
duration
missed/answered
25. Plugin Search Result
Always identify source provider:
From Notes plugin
Plugin search remains permissioned and namespaced.
26. Result Grouping
Global search can show:
Messages
Files
Contacts
Links
Calls
with a few best results from each.
Selecting a category opens the complete paged view.
27. Ranking
Rust search engine owns relevance and ranking.
UI must not invent a second ranking algorithm.
28. Sort Options
Default:
Relevance
Optional:
Newest
Oldest
29. Jump to Source Message
Selecting a message result:
SearchResult
→ MessageId
→ request page around MessageId
→ typed conversation navigation
→ temporary highlight
30. Search Outside Loaded Timeline
Use the Part 05 centered page API:
#![allow(unused)] fn main() { pub struct MessageAroundRequest { pub conversation: ConversationId, pub anchor: MessageId, pub before: usize, pub after: usize, } }
31. Search Navigation Preservation
Returning to search should restore:
query
filters
scroll position
selected result
32. Desktop Preview Pane
Wide desktop may preview:
message context
file metadata
contact profile summary
without leaving search.
Preview should not accidentally mark a message read unless actual Part 30 read semantics are satisfied.
33. Android Search Navigation
Phone usually navigates to the source.
Tablet/foldable may use list/detail preview.
34. Search History
Optional local-only feature.
Store recent query strings:
device-local
with:
Clear history
Disable history
35. Strict Privacy Mode
Allow:
Do not save recent searches
36. No Remote Autocomplete
Hard default:
query text does not go to a remote autocomplete/search server
37. Local Suggestions
Safe local suggestions may include:
recent searches
recent contacts
recent conversations
38. Lexical Search
Baseline search capabilities:
word match
phrase match
prefix
filters
Unicode normalization
39. Fuzzy Search
Useful for:
contact names
filenames
Use bounded fuzziness.
40. Advanced Query Syntax
Desktop power users may later support:
from:alice
in:project
has:file
before:2026-08-01
"exact phrase"
Android should expose equivalent filters visually rather than requiring syntax.
41. Query Parsing
Rust parses and validates query syntax.
UI can expose a builder/chips over the same semantics.
42. Invalid Query
Show inline:
Invalid date filter
Unknown search filter
Do not show a modal.
43. Semantic Search
Optional later.
Must remain local by default.
44. Hybrid Search
Combines:
lexical
metadata
semantic similarity
Rust owns fusion/ranking.
45. Semantic UX
Do not force users to understand embeddings.
A future product may simply label:
Search by meaning
or transparently use hybrid search.
46. Embedding Privacy
Embeddings are derived private data.
Store locally by default.
47. External AI
If a user explicitly configures an external provider:
provider
data scope
retrieved snippets
must be explicit.
Never silently upload the local search corpus.
48. Local Knowledge Retrieval
Search can power:
local assistant
question answering
summarization
context retrieval
but retrieval must be source-grounded.
49. Knowledge Chunk
#![allow(unused)] fn main() { pub struct KnowledgeChunkView { pub source_id: SearchSourceId, pub source_type: SearchDomain, pub snippet: String, pub conversation: Option<ConversationId>, pub message: Option<MessageId>, pub file: Option<BlobId>, } }
50. Retrieval Principle
A local assistant only receives actual retrieved source chunks.
No fabricated "memory" documents.
51. Source-Linked Answers
Assistant answers should expose source links to:
message
file
conversation
contact
where relevant.
52. Bounded Retrieval
Never retrieve entire history blindly.
Use:
max chunks
max tokens/bytes
scope limits
53. Search Index States
#![allow(unused)] fn main() { pub enum SearchIndexState { Ready, Building, Updating, Stale, Corrupt, Disabled, } }
54. Ready
Normal state.
55. Building
Initial/rebuild state.
Messaging remains fully usable.
56. Updating
Normal background incremental indexing.
Usually invisible.
57. Stale
Results may temporarily be incomplete.
Only surface if user needs to know.
58. Corrupt
Rust quarantines/rebuilds index.
UI:
Search index is being repaired
59. Disabled
Search screen explains:
Search indexing is disabled
with Enable action if policy allows.
60. Search During Index Build
If partial querying is supported:
show available results
+
Indexing…
Otherwise show progress without blocking rest of app.
61. Index Rebuild
Advanced/settings action:
Rebuild Search Index
62. Rebuild Progress
#![allow(unused)] fn main() { pub struct SearchIndexProgress { pub completed: u64, pub total_estimate: Option<u64>, } }
63. Search Empty State
Empty query:
Search messages, files, contacts, and links
64. No Results
No results for "..."
Potential suggestions:
Remove filters
Search all conversations
Check spelling
65. No Results During Build
Show both facts:
No results yet
Search indexing is still in progress
66. Offline Search
Works against local indexes without Internet.
67. Incomplete Local History
If a device does not have all historical content, search may be incomplete.
Do not claim complete coverage.
68. Coverage Warning
When relevant:
Some older content is not available on this device
69. Per-Device Search
Each device maintains its own index.
Indexes are not synced by default.
70. Different Results Across Devices
This can be normal if devices have different local history/file availability.
71. Trusted Archive Search
Future optional feature:
Search trusted archive
must be explicit.
72. Federated Search
Future search may query:
local core
plugins
trusted archive
with per-provider budgets/timeouts.
73. Provider Failure
A failing plugin provider does not fail whole search.
UI can show:
Some results are unavailable
74. Provider Permissions
Plugins search only their own namespace unless granted broader permission.
75. Pagination
Use cursor-based paging.
#![allow(unused)] fn main() { pub struct SearchResultPage { pub request_id: SearchRequestId, pub results: Vec<SearchResultView>, pub next_cursor: Option<SearchCursor>, pub total_estimate: Option<u64>, } }
76. Exact Result Count
Optional.
Do not block expensive queries to compute exact counts for huge datasets.
77. Stable Search Source Identity
#![allow(unused)] fn main() { pub enum SearchSourceId { Message(MessageId), File(BlobId), Contact(AccountId), Link(LinkId), Call(CallId), Plugin { plugin: PluginId, item: PluginItemId, }, } }
78. Virtualization
Required for large result sets.
Desktop:
virtualized result list
Android:
LazyColumn / LazyGrid
79. Result Stability
Avoid continuous result movement while user tries to click.
Batch/rerank carefully.
80. v1 Result Strategy
Recommended:
short debounce
→ stable first page
rather than streaming multiple ranking reorder phases.
81. Conversation Search — Desktop
Can open:
right-side search inspector
while timeline remains visible.
82. Conversation Search — Android
Top app bar enters search mode.
Results can show:
list of matching messages
or next/previous controls over timeline.
83. Result Navigation
After jumping:
Previous match
Next match
3 of 18
if total available.
84. Files Search Integration
Part 10 Files destination can scope:
SearchDomain::Files
85. Contacts Search Integration
Part 08 can scope:
SearchDomain::Contacts
86. Group Search Integration
Part 09 can scope:
Conversation(GroupId)
or group member search using dedicated group member API.
87. Unicode & Multilingual Search
Must handle:
English
Hindi
Urdu
Arabic
mixed scripts
emoji
without UI assumptions.
88. Bidi Rendering
Snippets with mixed RTL/LTR must render safely.
89. Stemming/Language Processing
Backend feature.
UI should not promise language-specific behavior unless supported.
90. Search Accessibility
Search field label:
Search messages, files, contacts, and links
91. Message Result Accessibility
Example:
Message from Alice in Project Group, yesterday, matching deadline
92. File Result Accessibility
Example:
PDF timetable.pdf from School Group, 2.4 megabytes
93. Result Count Accessibility
Announce once when useful:
18 results
not after every minor update.
94. Search Loading Accessibility
Only announce Searching if latency is noticeable.
95. Keyboard Focus — Desktop
Preserve selected SearchSourceId when results update if still present.
96. Android Keyboard Behavior
Returning from a result should not automatically reopen IME unless search field was active.
97. Filter Accessibility
All chips/buttons have explicit labels and selected state.
98. Large Font
Search chips/result rows wrap without clipping.
99. RTL
Search controls mirror correctly.
Identifiers/fingerprints keep canonical direction where necessary.
100. Reduced Motion
Search does not need large transitions.
101. Search Privacy Settings
Potential:
Index message text
Index file contents
Enable semantic search
Keep search history
102. Recommended Defaults
Enable local lexical indexing for:
messages
metadata
contacts
filenames
links
Optional:
file content extraction
semantic index
103. File Content Indexing
Supported safe parsers may extract:
PDF text
Markdown
plain text
office text
with strict limits.
104. OCR
Future optional.
Do not imply image-text search until OCR exists.
105. Audio Transcription Search
Future optional.
Must be explicit/local-first where practical.
106. Index Storage
Storage manager may show:
Search index — 420 MB
Semantic index — 1.3 GB
107. Clear Index
Because index is derived:
Clear Search Index
is safe.
Search can rebuild later.
108. Low Storage
Rust may discard semantic index first.
UI can explain if user opens storage management.
109. Search Locking
If app is locked, search results are hidden.
110. Logging
Never log raw search queries/snippets by default.
111. Crash Reports
Redact:
query
snippet
file content
112. Safe Metrics
Possible:
search latency
result-count bucket
index size
rebuild duration
query cancellation rate
No query content.
113. Performance Goal
Local lexical search should feel near-instant on typical hardware.
A useful target:
first useful page within ~150 ms
where hardware/corpus permit.
114. Semantic Search Latency
May be slower.
UI may show:
Refining results…
if hybrid processing is deliberately staged.
115. Background Indexing Priority
Indexing yields to:
calls
message send/receive
critical file work
emergency traffic
116. Android Background Indexing
Respect:
battery
thermal
background execution
through Rust scheduler/platform constraints.
117. Desktop Background Indexing
Low-priority worker.
118. Search Errors
#![allow(unused)] fn main() { pub enum SearchUiErrorKind { QueryInvalid, IndexUnavailable, IndexBuilding, ProviderUnavailable, PermissionDenied, Internal, } }
119. Error UX
Query invalid:
inline correction
Index unavailable:
Search unavailable
Rebuild Index
Provider unavailable:
partial results remain
120. Permission Enforcement
Rust filters unauthorized content before producing UI results.
Never send secret result then rely on UI hiding.
121. Deleted Result
If result disappears before open:
This result is no longer available
122. Edited Message
Timeline opens current version.
Search snippet refreshes asynchronously.
123. Removed Local File
Metadata result may remain if source message exists.
Opening can trigger download if available.
124. Left Group
Results disappear or become inaccessible according to retention/security policy.
125. Search Screen Snapshot
#![allow(unused)] fn main() { pub struct SearchScreenSnapshot { pub index_state: SearchIndexState, pub recent_queries: Vec<RecentSearchView>, pub enabled_domains: Vec<SearchDomain>, pub semantic_available: bool, } }
126. Search Presentation API
#![allow(unused)] fn main() { pub trait SearchPresentation { async fn snapshot( &self, ) -> Result<SearchScreenSnapshot, UiError>; async fn search( &self, request: SearchRequest, ) -> Result<SearchResultPage, UiError>; async fn next_page( &self, cursor: SearchCursor, ) -> Result<SearchResultPage, UiError>; async fn cancel( &self, request: SearchRequestId, ) -> Result<(), UiError>; } }
127. Search Request
#![allow(unused)] fn main() { pub struct SearchRequest { pub request_id: SearchRequestId, pub query: SearchQuery, pub page_size: usize, } }
128. Search Events
#![allow(unused)] fn main() { pub enum SearchUiEvent { IndexStateChanged(SearchIndexState), IndexProgressChanged(SearchIndexProgress), ProviderStateChanged(SearchProviderState), } }
129. Knowledge Retrieval API
#![allow(unused)] fn main() { pub trait KnowledgeRetrievalPresentation { async fn retrieve( &self, request: KnowledgeRetrievalRequest, ) -> Result<Vec<KnowledgeChunkView>, UiError>; } }
130. Retrieval Request
#![allow(unused)] fn main() { pub struct KnowledgeRetrievalRequest { pub query: String, pub scope: SearchScope, pub max_chunks: usize, } }
131. Android ViewModel
Owns:
query field
active filters
selected category
search mode
scroll restoration
UiEffects
Rust owns:
index
query execution
ranking
permissions
pagination
132. Dioxus Presenter
Owns:
query signal
filter panel
preview pane
selection
keyboard focus
133. Hard UI Boundaries
Do not:
query DB directly from UI
parse index files in UI
implement independent search ranking in Kotlin
upload search text remotely by default
134. Testing Matrix
Required:
empty query
global query
conversation query
messages
files
contacts
links
filters
no results
index building
index rebuild
offline
provider failure
large corpus
deleted result
rapid query changes
135. Desktop Tests
Verify:
keyboard shortcuts
result navigation
preview pane
jump to source
back to search
filters
pagination
136. Android Tests
Verify:
search app bar
IME
filter chips
back navigation
source jump
process death
TalkBack
large font
RTL
137. Search Cancellation Test
Type rapidly:
a
al
ali
alic
alice
Only the latest result set is rendered.
138. Index Rebuild Test
Search degrades safely while app remains usable.
139. Privacy Test
No query/snippet leaves device without an explicit configured external-provider action.
140. Plugin Permission Test
Plugin search never crosses unauthorized namespace.
141. Large Corpus Test
Test:
100k messages
1M messages
many files
with bounded memory and paging.
142. Search Jump Test
Result deep in history correctly loads around stable MessageId.
143. Search History Test
Clearing history does not clear index.
144. Multi-Device Test
Different devices can legitimately show different search coverage while maintaining the same source IDs for shared content.
145. Recommended v1 Scope
Ship:
global lexical search
conversation search
message results
file results
contact results
link results
sender/date/type filters
jump to source
recent searches local-only
index rebuild state
offline search
desktop keyboard search
Android search app bar/chips
Defer:
semantic/vector search
local assistant
OCR
audio transcription
trusted archive search
complex query language
cross-device federated search
unless already implemented safely.
146. Definition of Done
UI/UX Part 11 is complete when:
- global and conversation search are distinct
- search is local-first and private by default
- Rust owns indexing, parsing, ranking, permissions, and paging
- the UIs never query databases or index formats directly
- result types for messages/files/contacts/links/calls/plugins are defined
- every result can navigate using stable source identities
- stale queries cannot overwrite newer results
- Building/Stale/Corrupt/Disabled index states have clear UX
- index rebuild never blocks core messaging
- large result sets are paged/virtualized
- desktop is keyboard-first and can use a preview pane
- Android uses native search app bar/chips and lifecycle-safe ViewModels
- optional semantic/local-knowledge retrieval remains local-first, bounded, and source-grounded
- external AI use is explicit
- accessibility, RTL, large font, reduced motion, and screen-reader semantics are defined
- query/snippet telemetry is prohibited by default
- Rust search and knowledge retrieval presentation APIs are specified
- offline, incomplete history, provider failure, corruption, deletion races, and large-corpus scenarios are tested
147. Final Architecture
RUST SEARCH CORE
│
┌───────────────┼───────────────┐
│ │ │
Lexical Index Metadata Optional Semantic
│ │ │
└───────────────┼───────────────┘
│
Search Presentation
┌─────┴─────┐
│ │
Dioxus Compose
│ │
Desktop Search Android Search
Optional local knowledge path:
Search/Retrieval
│
▼
Bounded Source Chunks
│
▼
Local Assistant
│
▼
Source-linked Answer
There is no default path:
private communication history
→ central search service
148. Final Principle
The search experience should make a large private communication history feel small and navigable without giving up privacy.
The correct model is:
local indexing
+
stable source identities
+
fast lexical retrieval
+
clear filters
+
source navigation
+
optional bounded semantic retrieval
not:
upload the user's communication history so search feels convenient
This gives both Dioxus desktop and Android Compose a fast, useful, trustworthy search experience while the Rust search/index layer remains authoritative and rebuildable.