@prmichaelsen/acp-visualizer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/README.md +68 -0
  2. package/agent/commands/acp.clarification-address.md +417 -0
  3. package/agent/commands/acp.clarification-capture.md +386 -0
  4. package/agent/commands/acp.clarification-create.md +437 -0
  5. package/agent/commands/acp.clarifications-research.md +326 -0
  6. package/agent/commands/acp.command-create.md +432 -0
  7. package/agent/commands/acp.design-create.md +286 -0
  8. package/agent/commands/acp.design-reference.md +355 -0
  9. package/agent/commands/acp.handoff.md +270 -0
  10. package/agent/commands/acp.index.md +423 -0
  11. package/agent/commands/acp.init.md +546 -0
  12. package/agent/commands/acp.package-create.md +895 -0
  13. package/agent/commands/acp.package-info.md +212 -0
  14. package/agent/commands/acp.package-install.md +539 -0
  15. package/agent/commands/acp.package-list.md +280 -0
  16. package/agent/commands/acp.package-publish.md +541 -0
  17. package/agent/commands/acp.package-remove.md +293 -0
  18. package/agent/commands/acp.package-search.md +307 -0
  19. package/agent/commands/acp.package-update.md +361 -0
  20. package/agent/commands/acp.package-validate.md +540 -0
  21. package/agent/commands/acp.pattern-create.md +386 -0
  22. package/agent/commands/acp.plan.md +587 -0
  23. package/agent/commands/acp.proceed.md +882 -0
  24. package/agent/commands/acp.project-create.md +675 -0
  25. package/agent/commands/acp.project-info.md +312 -0
  26. package/agent/commands/acp.project-list.md +226 -0
  27. package/agent/commands/acp.project-remove.md +379 -0
  28. package/agent/commands/acp.project-set.md +227 -0
  29. package/agent/commands/acp.project-update.md +307 -0
  30. package/agent/commands/acp.projects-restore.md +228 -0
  31. package/agent/commands/acp.projects-sync.md +347 -0
  32. package/agent/commands/acp.report.md +407 -0
  33. package/agent/commands/acp.resume.md +239 -0
  34. package/agent/commands/acp.sessions.md +301 -0
  35. package/agent/commands/acp.status.md +293 -0
  36. package/agent/commands/acp.sync.md +364 -0
  37. package/agent/commands/acp.task-create.md +500 -0
  38. package/agent/commands/acp.update.md +302 -0
  39. package/agent/commands/acp.validate.md +466 -0
  40. package/agent/commands/acp.version-check-for-updates.md +276 -0
  41. package/agent/commands/acp.version-check.md +191 -0
  42. package/agent/commands/acp.version-update.md +289 -0
  43. package/agent/commands/command.template.md +339 -0
  44. package/agent/commands/git.commit.md +526 -0
  45. package/agent/commands/git.init.md +514 -0
  46. package/agent/commands/tanstack-cloudflare.deploy.md +272 -0
  47. package/agent/commands/tanstack-cloudflare.tail.md +275 -0
  48. package/agent/design/.gitkeep +0 -0
  49. package/agent/design/design.template.md +154 -0
  50. package/agent/design/local.dashboard-layout-routing.md +288 -0
  51. package/agent/design/local.data-model-yaml-parsing.md +310 -0
  52. package/agent/design/local.search-filtering.md +331 -0
  53. package/agent/design/local.server-api-auto-refresh.md +235 -0
  54. package/agent/design/local.table-tree-views.md +299 -0
  55. package/agent/design/local.visualizer-requirements.md +349 -0
  56. package/agent/design/requirements.template.md +387 -0
  57. package/agent/index/.gitkeep +0 -0
  58. package/agent/index/acp.core.yaml +137 -0
  59. package/agent/index/local.main.template.yaml +37 -0
  60. package/agent/manifest.template.yaml +13 -0
  61. package/agent/manifest.yaml +302 -0
  62. package/agent/milestones/.gitkeep +0 -0
  63. package/agent/milestones/milestone-1-project-scaffold-data-pipeline.md +67 -0
  64. package/agent/milestones/milestone-1-{title}.template.md +206 -0
  65. package/agent/milestones/milestone-2-dashboard-views-interaction.md +79 -0
  66. package/agent/package.template.yaml +86 -0
  67. package/agent/patterns/.gitkeep +0 -0
  68. package/agent/patterns/bootstrap.template.md +1237 -0
  69. package/agent/patterns/pattern.template.md +382 -0
  70. package/agent/patterns/tanstack-cloudflare.acl-permissions.md +332 -0
  71. package/agent/patterns/tanstack-cloudflare.action-bar-item.md +416 -0
  72. package/agent/patterns/tanstack-cloudflare.api-route-handlers.md +401 -0
  73. package/agent/patterns/tanstack-cloudflare.auth-session-management.md +387 -0
  74. package/agent/patterns/tanstack-cloudflare.card-and-list.md +271 -0
  75. package/agent/patterns/tanstack-cloudflare.chat-engine.md +353 -0
  76. package/agent/patterns/tanstack-cloudflare.confirmation-tokens.md +346 -0
  77. package/agent/patterns/tanstack-cloudflare.durable-objects-websocket.md +516 -0
  78. package/agent/patterns/tanstack-cloudflare.email-service.md +431 -0
  79. package/agent/patterns/tanstack-cloudflare.expander.md +98 -0
  80. package/agent/patterns/tanstack-cloudflare.fcm-push.md +115 -0
  81. package/agent/patterns/tanstack-cloudflare.firebase-anonymous-sessions.md +441 -0
  82. package/agent/patterns/tanstack-cloudflare.firebase-auth.md +348 -0
  83. package/agent/patterns/tanstack-cloudflare.firebase-firestore.md +550 -0
  84. package/agent/patterns/tanstack-cloudflare.firebase-storage.md +369 -0
  85. package/agent/patterns/tanstack-cloudflare.form-controls.md +145 -0
  86. package/agent/patterns/tanstack-cloudflare.global-search-context.md +93 -0
  87. package/agent/patterns/tanstack-cloudflare.image-carousel.md +126 -0
  88. package/agent/patterns/tanstack-cloudflare.library-services.md +553 -0
  89. package/agent/patterns/tanstack-cloudflare.lightbox.md +169 -0
  90. package/agent/patterns/tanstack-cloudflare.markdown-content.md +115 -0
  91. package/agent/patterns/tanstack-cloudflare.mention-suggestions.md +98 -0
  92. package/agent/patterns/tanstack-cloudflare.modal.md +156 -0
  93. package/agent/patterns/tanstack-cloudflare.nextjs-to-tanstack-routing.md +461 -0
  94. package/agent/patterns/tanstack-cloudflare.notifications-engine.md +151 -0
  95. package/agent/patterns/tanstack-cloudflare.oauth-token-refresh.md +90 -0
  96. package/agent/patterns/tanstack-cloudflare.og-metadata.md +296 -0
  97. package/agent/patterns/tanstack-cloudflare.pagination.md +442 -0
  98. package/agent/patterns/tanstack-cloudflare.pill-input.md +220 -0
  99. package/agent/patterns/tanstack-cloudflare.provider-adapter.md +401 -0
  100. package/agent/patterns/tanstack-cloudflare.rate-limiting.md +323 -0
  101. package/agent/patterns/tanstack-cloudflare.scheduled-tasks.md +338 -0
  102. package/agent/patterns/tanstack-cloudflare.searchable-settings.md +375 -0
  103. package/agent/patterns/tanstack-cloudflare.slide-over.md +129 -0
  104. package/agent/patterns/tanstack-cloudflare.ssr-preload.md +571 -0
  105. package/agent/patterns/tanstack-cloudflare.third-party-api-integration.md +508 -0
  106. package/agent/patterns/tanstack-cloudflare.toast-system.md +142 -0
  107. package/agent/patterns/tanstack-cloudflare.unified-header.md +280 -0
  108. package/agent/patterns/tanstack-cloudflare.user-scoped-collections.md +628 -0
  109. package/agent/patterns/tanstack-cloudflare.websocket-manager.md +237 -0
  110. package/agent/patterns/tanstack-cloudflare.wrangler-configuration.md +358 -0
  111. package/agent/patterns/tanstack-cloudflare.zod-schema-validation.md +336 -0
  112. package/agent/progress.template.yaml +161 -0
  113. package/agent/progress.yaml +145 -0
  114. package/agent/schemas/package.schema.yaml +276 -0
  115. package/agent/scripts/acp.common.sh +1781 -0
  116. package/agent/scripts/acp.install.sh +333 -0
  117. package/agent/scripts/acp.package-create.sh +924 -0
  118. package/agent/scripts/acp.package-info.sh +288 -0
  119. package/agent/scripts/acp.package-install.sh +893 -0
  120. package/agent/scripts/acp.package-list.sh +311 -0
  121. package/agent/scripts/acp.package-publish.sh +420 -0
  122. package/agent/scripts/acp.package-remove.sh +348 -0
  123. package/agent/scripts/acp.package-search.sh +156 -0
  124. package/agent/scripts/acp.package-update.sh +517 -0
  125. package/agent/scripts/acp.package-validate.sh +1018 -0
  126. package/agent/scripts/acp.uninstall.sh +85 -0
  127. package/agent/scripts/acp.version-check-for-updates.sh +98 -0
  128. package/agent/scripts/acp.version-check.sh +47 -0
  129. package/agent/scripts/acp.version-update.sh +176 -0
  130. package/agent/scripts/acp.yaml-parser.sh +985 -0
  131. package/agent/scripts/acp.yaml-validate.sh +205 -0
  132. package/agent/tasks/.gitkeep +0 -0
  133. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-1-initialize-tanstack-start-project.md +210 -0
  134. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-2-implement-data-model-yaml-parser.md +294 -0
  135. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-3-build-server-api-data-loading.md +193 -0
  136. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-4-add-auto-refresh-sse.md +262 -0
  137. package/agent/tasks/milestone-2-dashboard-views-interaction/task-10-polish-integration-testing.md +156 -0
  138. package/agent/tasks/milestone-2-dashboard-views-interaction/task-5-build-dashboard-layout-routing.md +178 -0
  139. package/agent/tasks/milestone-2-dashboard-views-interaction/task-6-build-overview-page.md +141 -0
  140. package/agent/tasks/milestone-2-dashboard-views-interaction/task-7-implement-milestone-table-view.md +153 -0
  141. package/agent/tasks/milestone-2-dashboard-views-interaction/task-8-implement-milestone-tree-view.md +174 -0
  142. package/agent/tasks/milestone-2-dashboard-views-interaction/task-9-implement-search-filtering.md +233 -0
  143. package/agent/tasks/task-1-{title}.template.md +244 -0
  144. package/bin/visualize.mjs +84 -0
  145. package/package.json +48 -0
  146. package/src/components/ExtraFieldsBadge.tsx +15 -0
  147. package/src/components/FilterBar.tsx +33 -0
  148. package/src/components/Header.tsx +23 -0
  149. package/src/components/MilestoneTable.tsx +167 -0
  150. package/src/components/MilestoneTree.tsx +84 -0
  151. package/src/components/ProgressBar.tsx +20 -0
  152. package/src/components/SearchInput.tsx +22 -0
  153. package/src/components/Sidebar.tsx +54 -0
  154. package/src/components/StatusBadge.tsx +23 -0
  155. package/src/components/StatusDot.tsx +12 -0
  156. package/src/components/TaskList.tsx +36 -0
  157. package/src/components/ViewToggle.tsx +31 -0
  158. package/src/lib/config.ts +8 -0
  159. package/src/lib/file-watcher.ts +43 -0
  160. package/src/lib/search.ts +48 -0
  161. package/src/lib/types.ts +73 -0
  162. package/src/lib/useAutoRefresh.ts +31 -0
  163. package/src/lib/useCollapse.ts +31 -0
  164. package/src/lib/useFilteredData.ts +55 -0
  165. package/src/lib/yaml-loader-real.spec.ts +47 -0
  166. package/src/lib/yaml-loader.spec.ts +201 -0
  167. package/src/lib/yaml-loader.ts +265 -0
  168. package/src/routeTree.gen.ts +140 -0
  169. package/src/router.tsx +10 -0
  170. package/src/routes/__root.tsx +75 -0
  171. package/src/routes/api/watch.ts +29 -0
  172. package/src/routes/index.tsx +115 -0
  173. package/src/routes/milestones.tsx +50 -0
  174. package/src/routes/search.tsx +84 -0
  175. package/src/routes/tasks.tsx +63 -0
  176. package/src/services/progress-database.service.ts +46 -0
  177. package/src/styles.css +25 -0
  178. package/tsconfig.json +24 -0
  179. package/vite.config.ts +16 -0
  180. package/vitest.config.ts +27 -0
@@ -0,0 +1,375 @@
1
+ # Searchable Settings Page
2
+
3
+ **Category**: Design
4
+ **Applicable To**: Settings pages with grouped sections, search-to-scroll, hash-based navigation, and a registry of searchable items
5
+ **Status**: Stable
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ The Settings page uses a registry-based architecture: all settings are defined in a central `SettingsItem[]` array with name, description, category, hidden keywords, and a hash-fragment path. A search input filters the registry using AND-logic word matching, and selecting a result navigates to the route + scrolls to the section via its `id` attribute. This makes settings discoverable without browsing every section.
12
+
13
+ ---
14
+
15
+ ## When to Use This Pattern
16
+
17
+ **Use this pattern when:**
18
+ - Building a settings page with many sections that benefit from search
19
+ - Any page with hash-based scroll-to-section navigation
20
+ - Settings that span multiple sub-pages (main settings, ghost settings, delete account)
21
+
22
+ **Don't use this pattern when:**
23
+ - The page has fewer than 5 settings (just list them, no search needed)
24
+ - Settings are entirely form-based with no sections (use a simple form)
25
+
26
+ ---
27
+
28
+ ## Core Principles
29
+
30
+ 1. **Registry as Source of Truth**: All searchable settings defined in one `SettingsItem[]` array
31
+ 2. **Hidden Keywords**: Extra search terms (`keywords[]`) improve discoverability without cluttering the UI
32
+ 3. **AND-Logic Search**: All query words must match in the concatenated haystack
33
+ 4. **Hash-Fragment Navigation**: Search results link to `path#section-id`, scrolling to the section
34
+ 5. **Optimistic Updates**: Toggle/slider changes apply immediately, revert on API failure
35
+
36
+ ---
37
+
38
+ ## Implementation
39
+
40
+ ### Settings Registry
41
+
42
+ **File**: `src/constant/settings-registry.ts`
43
+
44
+ ```typescript
45
+ export interface SettingsItem {
46
+ /** Unique identifier, also used as Algolia objectID */
47
+ id: string
48
+ /** Display name shown in search results */
49
+ name: string
50
+ /** Short description shown under the name */
51
+ description: string
52
+ /** Category grouping (e.g. "Ghost Mode", "Privacy", "Display") */
53
+ category: string
54
+ /** Extra search terms not visible in UI (e.g. "telemetry" for analytics) */
55
+ keywords: string[]
56
+ /** Route path + optional hash fragment (e.g. "/settings#privacy") */
57
+ path: string
58
+ /** Sub-items listed for context in search results */
59
+ sub_items: string[]
60
+ }
61
+
62
+ export const SETTINGS_REGISTRY: SettingsItem[] = [
63
+ {
64
+ id: 'ghost-mode',
65
+ name: 'Ghost Mode',
66
+ description: 'Configure your ghost persona and conversation behavior',
67
+ category: 'Ghost Mode',
68
+ keywords: ['persona', 'alter ego', 'anonymous', 'identity'],
69
+ path: '/settings/ghost#ghost-mode',
70
+ sub_items: ['Enable ghost mode', 'Ghost name', 'Ghost persona'],
71
+ },
72
+ {
73
+ id: 'privacy-analytics',
74
+ name: 'Analytics',
75
+ description: 'Control whether usage data is collected',
76
+ category: 'Privacy',
77
+ keywords: ['tracking', 'telemetry', 'data collection', 'opt out'],
78
+ path: '/settings#privacy',
79
+ sub_items: ['Toggle analytics on/off'],
80
+ },
81
+ // ... 25 total items
82
+ ]
83
+ ```
84
+
85
+ ### Search Algorithm
86
+
87
+ **File**: `src/routes/settings/index.tsx`
88
+
89
+ ```typescript
90
+ function searchSettings(query: string): SettingsItem[] {
91
+ if (!query.trim()) return []
92
+ const q = query.toLowerCase()
93
+ return SETTINGS_REGISTRY.filter((item) => {
94
+ const haystack = [
95
+ item.name,
96
+ item.description,
97
+ item.category,
98
+ ...item.sub_items,
99
+ ...item.keywords,
100
+ ].join(' ').toLowerCase()
101
+ return q.split(/\s+/).every((word) => haystack.includes(word))
102
+ })
103
+ }
104
+ ```
105
+
106
+ **How it works**:
107
+ - Concatenates all searchable fields into one string ("haystack")
108
+ - Splits query into words
109
+ - Every word must appear in the haystack (AND logic)
110
+ - Example: `"toggle privacy"` matches items containing both "toggle" AND "privacy"
111
+
112
+ ### Search UI
113
+
114
+ ```typescript
115
+ const [searchQuery, setSearchQuery] = useState('')
116
+ const [searchFocused, setSearchFocused] = useState(false)
117
+ const searchRef = useRef<HTMLDivElement>(null)
118
+
119
+ const searchResults = useMemo(() => searchSettings(searchQuery), [searchQuery])
120
+ const showResults = searchFocused && searchQuery.trim().length > 0
121
+ ```
122
+
123
+ **Rendered structure**:
124
+
125
+ ```
126
+ ┌─ Search Input ────────────────────────────────┐
127
+ │ 🔍 Search settings... ✕ │
128
+ ├───────────────────────────────────────────────┤
129
+ │ Ghost Mode [Ghost Mode] │ ← category badge
130
+ │ Configure your ghost persona... │
131
+ │ • Enable ghost mode • Ghost name │ ← sub_items
132
+ ├───────────────────────────────────────────────┤
133
+ │ Analytics [Privacy] │
134
+ │ Control whether usage data is collected │
135
+ │ • Toggle analytics on/off │
136
+ └───────────────────────────────────────────────┘
137
+ ```
138
+
139
+ **Result click handler**:
140
+
141
+ ```typescript
142
+ onClick={() => {
143
+ setSearchQuery('')
144
+ setSearchFocused(false)
145
+ const [to, hash] = item.path.split('#')
146
+ navigate({ to, hash })
147
+ if (hash) {
148
+ setTimeout(() => {
149
+ document.getElementById(hash)?.scrollIntoView({
150
+ behavior: 'smooth',
151
+ block: 'start',
152
+ })
153
+ }, 300) // Delay allows DOM to settle after navigation
154
+ }
155
+ }
156
+ ```
157
+
158
+ ### Section Anchors
159
+
160
+ Each settings section uses an `id` attribute matching the hash fragment:
161
+
162
+ ```typescript
163
+ <div id="privacy" className="bg-gray-900/50 backdrop-blur-sm border border-gray-800 rounded-xl p-6">
164
+ <div className="flex items-center gap-3 mb-4">
165
+ <Shield className="w-5 h-5 text-blue-400" />
166
+ <h2 className="text-lg font-semibold text-white">Privacy</h2>
167
+ </div>
168
+ {/* Setting controls */}
169
+ </div>
170
+ ```
171
+
172
+ ### Hash Scroll on Page Load
173
+
174
+ ```typescript
175
+ useEffect(() => {
176
+ const hash = window.location.hash.slice(1)
177
+ if (!hash) return
178
+ const timer = setTimeout(() => {
179
+ document.getElementById(hash)?.scrollIntoView({
180
+ behavior: 'smooth',
181
+ block: 'start',
182
+ })
183
+ }, 300)
184
+ return () => clearTimeout(timer)
185
+ }, [])
186
+ ```
187
+
188
+ This handles both direct links (`/settings#privacy`) and search-driven navigation.
189
+
190
+ ### Settings Controls Within Sections
191
+
192
+ **Toggle setting**:
193
+
194
+ ```typescript
195
+ <div className="flex items-center justify-between py-3">
196
+ <div>
197
+ <p className="text-sm font-medium text-white">Analytics</p>
198
+ <p className="text-xs text-gray-500">Help improve the app by sharing usage data</p>
199
+ </div>
200
+ <ToggleSwitch
201
+ checked={analyticsEnabled}
202
+ onChange={handleAnalyticsToggle}
203
+ disabled={toggling}
204
+ />
205
+ </div>
206
+ ```
207
+
208
+ **Slider setting**:
209
+
210
+ ```typescript
211
+ <div className="py-3">
212
+ <p className="text-sm font-medium text-white mb-2">Search Relevance</p>
213
+ <p className="text-xs text-gray-500 mb-3">Higher values return fewer but more relevant results</p>
214
+ <Slider min={0} max={0.8} step={0.05} value={threshold} onChange={handleChange} />
215
+ </div>
216
+ ```
217
+
218
+ **Link to sub-page**:
219
+
220
+ ```typescript
221
+ <Link to="/settings/ghost"
222
+ className="flex items-center justify-between p-4 bg-gray-900/50 border border-gray-800 rounded-xl hover:border-purple-500/50 transition-colors">
223
+ <div className="flex items-center gap-3">
224
+ <Ghost className="w-5 h-5 text-purple-400" />
225
+ <div>
226
+ <p className="text-sm font-medium text-white">Ghost Mode</p>
227
+ <p className="text-xs text-gray-500">Configure persona and trust levels</p>
228
+ </div>
229
+ </div>
230
+ <ChevronRight className="w-4 h-4 text-gray-500" />
231
+ </Link>
232
+ ```
233
+
234
+ ### State Management
235
+
236
+ **Server-synced preferences** (UIPreferencesContext):
237
+ ```typescript
238
+ const { preferences, updatePreference } = useUIPreferences()
239
+
240
+ // Optimistic update + API call
241
+ const handleToggle = async (value: boolean) => {
242
+ const success = await updatePreference({ memory_card_overflow: value ? 'scroll' : 'clip' })
243
+ if (!success) toast.error({ title: 'Failed to save' })
244
+ }
245
+ ```
246
+
247
+ **Device-local preferences** (UIPreferencesLocalContext):
248
+ ```typescript
249
+ const { contentFontSize, setContentFontSize } = useUIPreferencesLocal()
250
+ // Stored in localStorage, varies per device (mobile vs desktop)
251
+ ```
252
+
253
+ ---
254
+
255
+ ## Examples
256
+
257
+ ### Adding a New Setting
258
+
259
+ 1. **Add to registry** (`src/constant/settings-registry.ts`):
260
+ ```typescript
261
+ {
262
+ id: 'theme-mode',
263
+ name: 'Theme',
264
+ description: 'Choose light or dark color scheme',
265
+ category: 'Display',
266
+ keywords: ['dark mode', 'light mode', 'appearance', 'color'],
267
+ path: '/settings#ui-preferences',
268
+ sub_items: ['Light', 'Dark', 'System'],
269
+ }
270
+ ```
271
+
272
+ 2. **Add section anchor** (in settings page):
273
+ ```typescript
274
+ <div id="ui-preferences" className="bg-gray-900/50 ...">
275
+ {/* existing controls + new theme toggle */}
276
+ </div>
277
+ ```
278
+
279
+ 3. **Done** — the new setting is immediately searchable.
280
+
281
+ ### Adding a New Sub-Page
282
+
283
+ 1. Create route: `src/routes/settings/new-page.tsx`
284
+ 2. Add registry entries with `path: '/settings/new-page#section-id'`
285
+ 3. Add link in main settings page with ChevronRight arrow
286
+
287
+ ---
288
+
289
+ ## Anti-Patterns
290
+
291
+ ### Hardcoding Settings Without Registry
292
+
293
+ ```typescript
294
+ // Bad: Settings not searchable, no central definition
295
+ <div>
296
+ <h2>Privacy</h2>
297
+ <ToggleSwitch checked={analytics} onChange={toggleAnalytics} />
298
+ </div>
299
+
300
+ // Good: Define in registry, render from section id, searchable automatically
301
+ // settings-registry.ts:
302
+ { id: 'privacy-analytics', name: 'Analytics', path: '/settings#privacy', ... }
303
+ // settings page:
304
+ <div id="privacy">...</div>
305
+ ```
306
+
307
+ ### OR-Logic Search
308
+
309
+ ```typescript
310
+ // Bad: Returns too many results (any word matches)
311
+ return q.split(/\s+/).some((word) => haystack.includes(word))
312
+
313
+ // Good: AND logic — all words must match
314
+ return q.split(/\s+/).every((word) => haystack.includes(word))
315
+ ```
316
+
317
+ ### Scrolling Without Delay
318
+
319
+ ```typescript
320
+ // Bad: Element might not be in DOM yet after navigation
321
+ document.getElementById(hash)?.scrollIntoView({ behavior: 'smooth' })
322
+
323
+ // Good: Wait for DOM to settle
324
+ setTimeout(() => {
325
+ document.getElementById(hash)?.scrollIntoView({ behavior: 'smooth', block: 'start' })
326
+ }, 300)
327
+ ```
328
+
329
+ ---
330
+
331
+ ## Key Design Decisions
332
+
333
+ ### Search Architecture
334
+
335
+ | Decision | Choice | Rationale |
336
+ |---|---|---|
337
+ | Search data source | Static registry array | Fast, no API call, works offline |
338
+ | Search algorithm | AND-logic word matching | More precise than OR; reduces noise |
339
+ | Hidden keywords | `keywords[]` field | Improves discoverability without UI clutter |
340
+ | Result navigation | Route + hash scroll | Deep-links to exact section |
341
+ | Scroll delay | 300ms setTimeout | Allows DOM render after route navigation |
342
+
343
+ ### State Management
344
+
345
+ | Decision | Choice | Rationale |
346
+ |---|---|---|
347
+ | Server preferences | UIPreferencesContext with optimistic update | Syncs across devices; instant UI response |
348
+ | Device preferences | localStorage via UIPreferencesLocalContext | Font size varies per device (mobile vs desktop) |
349
+ | Update pattern | Optimistic + rollback on failure | Feels instant; reverts if API fails |
350
+
351
+ ---
352
+
353
+ ## Checklist
354
+
355
+ - [ ] New settings added to `SETTINGS_REGISTRY` with id, name, description, category, keywords, path, sub_items
356
+ - [ ] Section div has `id` attribute matching the hash fragment in the registry path
357
+ - [ ] Keywords include synonyms users might search for (e.g., "telemetry" for analytics)
358
+ - [ ] Sub-items list the specific controls within the section
359
+ - [ ] Path uses hash fragment for same-page sections, full route for sub-pages
360
+ - [ ] Settings controls use ToggleSwitch, Slider, or link-with-chevron patterns
361
+ - [ ] Server-synced settings use optimistic update via UIPreferencesContext
362
+ - [ ] Device-local settings use localStorage via UIPreferencesLocalContext
363
+
364
+ ---
365
+
366
+ ## Related Patterns
367
+
368
+ - **[Form Controls](./tanstack-cloudflare.form-controls.md)**: ToggleSwitch and Slider used within settings sections
369
+ - **[Unified Header](./tanstack-cloudflare.unified-header.md)**: Settings page uses UnifiedHeader with back navigation
370
+
371
+ ---
372
+
373
+ **Status**: Stable
374
+ **Last Updated**: 2026-03-14
375
+ **Contributors**: Community
@@ -0,0 +1,129 @@
1
+ # SlideOverPanel & MessageSearchSlideover
2
+
3
+ **Category**: Design
4
+ **Applicable To**: Right-side slide panels, search slideouts, and configuration drawers
5
+ **Status**: Stable
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ SlideOverPanel is a lightweight right-side drawer (w-72) that slides in with a translate-x animation and backdrop. MessageSearchSlideover is a specialized full-width search panel with debounced API search and result navigation. Both render inline (not portaled) with fixed positioning.
12
+
13
+ ---
14
+
15
+ ## Implementation
16
+
17
+ ### SlideOverPanel (Generic Drawer)
18
+
19
+ **File**: `src/components/SlideOverPanel.tsx`
20
+
21
+ ```typescript
22
+ interface SlideOverPanelProps {
23
+ open: boolean
24
+ onClose: () => void
25
+ children: ReactNode
26
+ }
27
+ ```
28
+
29
+ **Behavior**:
30
+ - Fixed positioning: `top-14` (below header), `right-0`, `bottom-0`
31
+ - Width: `w-72` (288px)
32
+ - **Not** a portal — renders inline from parent component
33
+ - **Animation** (200ms):
34
+ - Backdrop: `opacity-0` → `opacity-100`
35
+ - Panel: `translate-x-full` → `translate-x-0`
36
+ - Mounted/visible state tracking for exit animation before unmount
37
+ - Backdrop click closes
38
+ - z-index: backdrop 20, panel 30
39
+ - Dark theme: `bg-gray-900` with `border-l border-gray-800`
40
+
41
+ **Usage**:
42
+
43
+ ```typescript
44
+ <SlideOverPanel open={panelOpen} onClose={() => setPanelOpen(false)}>
45
+ <div className="p-4">
46
+ <h3>Panel Content</h3>
47
+ {/* configuration, details, etc. */}
48
+ </div>
49
+ </SlideOverPanel>
50
+ ```
51
+
52
+ ---
53
+
54
+ ### MessageSearchSlideover (Search Panel)
55
+
56
+ **File**: `src/components/chat/MessageSearchSlideover.tsx`
57
+
58
+ ```typescript
59
+ interface MessageSearchSlideoverProps {
60
+ conversationId: string
61
+ isOpen: boolean
62
+ onClose: () => void
63
+ onSelectMessage: (messageId: string) => void
64
+ }
65
+ ```
66
+
67
+ **Behavior**:
68
+ - Full-screen fixed panel (not constrained to right side)
69
+ - Max-width: `md` on desktop
70
+ - **Header**: Search icon + auto-focused input + X close button
71
+ - **Debounced search**: 300ms delay, calls `/api/search/messages`
72
+ - **Results**: Message snippet with fade mask, role label, relative time
73
+ - Click result → calls `onSelectMessage(messageId)` and closes panel
74
+ - Escape key closes
75
+ - Backdrop click closes
76
+ - Safe-area-inset-top handling
77
+
78
+ **Usage**:
79
+
80
+ ```typescript
81
+ <MessageSearchSlideover
82
+ conversationId={conversationId}
83
+ isOpen={searchOpen}
84
+ onClose={() => setSearchOpen(false)}
85
+ onSelectMessage={(id) => scrollToMessage(id)}
86
+ />
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Anti-Patterns
92
+
93
+ ### Using Portal for Simple Slide Panels
94
+
95
+ ```typescript
96
+ // Bad: Unnecessary portal when parent layout supports fixed children
97
+ {createPortal(<div className="fixed right-0">...</div>, document.body)}
98
+
99
+ // Good: Render inline — simpler and avoids portal context issues
100
+ <SlideOverPanel open={open} onClose={close}>{content}</SlideOverPanel>
101
+ ```
102
+
103
+ ### Not Auto-Focusing Search Input
104
+
105
+ ```typescript
106
+ // Bad: User must click into input after opening
107
+ <input type="text" />
108
+
109
+ // Good: Auto-focus on open
110
+ const inputRef = useRef<HTMLInputElement>(null)
111
+ useEffect(() => { if (isOpen) inputRef.current?.focus() }, [isOpen])
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Checklist
117
+
118
+ - [ ] Use `SlideOverPanel` for generic right-side drawers
119
+ - [ ] Use `MessageSearchSlideover` for search-within-conversation
120
+ - [ ] Auto-focus input when panel opens
121
+ - [ ] Debounce search input (300ms)
122
+ - [ ] Panel renders below header (`top-14`) to avoid overlapping
123
+ - [ ] Exit animation completes before unmount (mounted/visible state tracking)
124
+
125
+ ---
126
+
127
+ **Status**: Stable
128
+ **Last Updated**: 2026-03-14
129
+ **Contributors**: Community