|
7 | 7 |
|
8 | 8 | // Composables |
9 | 9 | import { useAsk } from '@/composables/useAsk' |
| 10 | + import { useDiscovery } from '@/composables/useDiscovery' |
10 | 11 | import { useSearch } from '@/composables/useSearch' |
11 | 12 | import { useSettings } from '@/composables/useSettings' |
12 | 13 |
|
|
18 | 19 | import type { SavedResult, SearchResult } from '@/composables/useSearch' |
19 | 20 |
|
20 | 21 | const search = useSearch() |
| 22 | + const discovery = useDiscovery() |
21 | 23 |
|
22 | 24 | const router = useRouter() |
23 | 25 | const inputRef = useTemplateRef<HTMLInputElement>('input') |
|
155 | 157 | <div :class="['rounded-lg shadow-xl border border-divider overflow-hidden', settings.showBgGlass.value ? 'bg-glass-surface' : 'bg-surface']"> |
156 | 158 | <Discovery.Activator |
157 | 159 | class="flex-1 bg-transparent flex border-b border-divider outline-none text-on-surface rounded-lg rounded-b-0 items-center gap-3 px-4 py-3" |
158 | | - step="search-tabs" |
| 160 | + step="search-input" |
159 | 161 | > |
160 | 162 | <AppIcon |
161 | 163 | aria-hidden="true" |
|
221 | 223 | <!-- Empty query state: show favorites and recents --> |
222 | 224 | <template v-else-if="!search.text.value.trim()"> |
223 | 225 | <!-- Favorites section --> |
224 | | - <div v-if="search.favorites.value.length > 0" aria-label="Favorites" role="group"> |
| 226 | + <Discovery.Activator |
| 227 | + v-if="search.favorites.value.length > 0" |
| 228 | + aria-label="Favorites" |
| 229 | + as="div" |
| 230 | + class="rounded-lg" |
| 231 | + role="group" |
| 232 | + step="search-favorited" |
| 233 | + > |
225 | 234 | <div class="px-4 py-2 text-xs font-medium text-on-surface-variant uppercase tracking-wide bg-surface-variant/50 flex items-center justify-between"> |
226 | 235 | <span>Favorites</span> |
227 | 236 | </div> |
|
264 | 273 | <AppIcon aria-hidden="true" icon="close" size="16" /> |
265 | 274 | </span> |
266 | 275 | </div> |
267 | | - </div> |
| 276 | + </Discovery.Activator> |
268 | 277 |
|
269 | 278 | <!-- Recents section --> |
270 | | - <div v-if="search.recents.value.length > 0" aria-label="Recent searches" role="group"> |
| 279 | + <Discovery.Activator |
| 280 | + v-if="search.recents.value.length > 0" |
| 281 | + aria-label="Recent searches" |
| 282 | + as="div" |
| 283 | + class="rounded-lg" |
| 284 | + role="group" |
| 285 | + step="search-history" |
| 286 | + > |
271 | 287 | <div class="px-4 py-2 text-xs font-medium text-on-surface-variant uppercase tracking-wide bg-surface-variant/50"> |
272 | 288 | Recent |
273 | 289 | </div> |
|
326 | 342 | </span> |
327 | 343 | </div> |
328 | 344 | </div> |
329 | | - </div> |
| 345 | + </Discovery.Activator> |
330 | 346 |
|
331 | 347 | <!-- No content placeholder --> |
332 | 348 | <div |
|
358 | 374 | <div class="px-4 py-2 text-xs font-medium text-on-surface-variant uppercase tracking-wide bg-surface-variant/50"> |
359 | 375 | {{ group.category }} |
360 | 376 | </div> |
| 377 | + <!-- First result gets Discovery.Activator for tour --> |
| 378 | + <Discovery.Activator |
| 379 | + v-if="groupIndex === 0 && group.items.length > 0" |
| 380 | + class="rounded-lg" |
| 381 | + step="search-results" |
| 382 | + > |
| 383 | + <div |
| 384 | + :id="`search-result-0`" |
| 385 | + :aria-selected="0 === search.selection.index.value" |
| 386 | + :class="[ |
| 387 | + 'group w-full px-4 py-2 flex items-center gap-2 text-left transition-colors cursor-pointer', |
| 388 | + 0 === search.selection.index.value |
| 389 | + ? 'bg-primary/10 text-primary' |
| 390 | + : 'hover:bg-surface-variant text-on-surface', |
| 391 | + ]" |
| 392 | + :data-selected="0 === search.selection.index.value" |
| 393 | + role="option" |
| 394 | + tabindex="-1" |
| 395 | + @click="navigate(group.items[0])" |
| 396 | + @mouseenter="onHover(0)" |
| 397 | + > |
| 398 | + <div class="flex-1 min-w-0 flex flex-col gap-0.5"> |
| 399 | + <span class="font-medium">{{ group.items[0].title }}</span> |
| 400 | + <span |
| 401 | + v-if="group.items[0].headings.length > 0" |
| 402 | + class="text-xs text-on-surface-variant truncate" |
| 403 | + > |
| 404 | + {{ group.items[0].headings.slice(0, 3).join(' → ') }} |
| 405 | + </span> |
| 406 | + </div> |
| 407 | + <div class="flex items-center gap-1 shrink-0"> |
| 408 | + <!-- Favorite toggle --> |
| 409 | + <Discovery.Activator |
| 410 | + class="rounded-lg" |
| 411 | + :padding="4" |
| 412 | + step="search-favorite" |
| 413 | + > |
| 414 | + <span |
| 415 | + :aria-label="search.isFavorite(group.items[0].id) ? 'Remove from favorites' : 'Add to favorites'" |
| 416 | + :class="[ |
| 417 | + 'inline-flex p-1.5 rounded-lg hover:bg-surface-variant focus-visible:bg-surface-variant transition-colors cursor-pointer', |
| 418 | + search.isFavorite(group.items[0].id) || discovery.isActive.value ? 'opacity-100 text-warning' : 'opacity-0 group-hover:opacity-100 focus-visible:opacity-100 text-on-surface/60 hover:text-warning focus-visible:text-warning', |
| 419 | + ]" |
| 420 | + role="button" |
| 421 | + tabindex="0" |
| 422 | + :title="search.isFavorite(group.items[0].id) ? 'Remove from favorites' : 'Add to favorites'" |
| 423 | + @click="toggleFavorite($event, group.items[0].id)" |
| 424 | + @keydown.enter.stop="toggleFavorite($event, group.items[0].id)" |
| 425 | + @keydown.space.stop.prevent="toggleFavorite($event, group.items[0].id)" |
| 426 | + > |
| 427 | + <AppIcon |
| 428 | + aria-hidden="true" |
| 429 | + :icon="search.isFavorite(group.items[0].id) ? 'star' : 'star-outline'" |
| 430 | + size="16" |
| 431 | + /> |
| 432 | + </span> |
| 433 | + </Discovery.Activator> |
| 434 | + <!-- Ask AI button --> |
| 435 | + <Discovery.Activator |
| 436 | + class="rounded-lg" |
| 437 | + :padding="4" |
| 438 | + step="search-ask-ai" |
| 439 | + > |
| 440 | + <span |
| 441 | + aria-label="Ask AI about this page" |
| 442 | + :class="[ |
| 443 | + 'inline-flex p-1.5 rounded-lg hover:bg-surface-variant focus-visible:bg-surface-variant transition-colors text-on-surface/60 hover:text-primary focus-visible:text-primary cursor-pointer', |
| 444 | + discovery.isActive.value ? 'opacity-100' : 'opacity-0 group-hover:opacity-100 focus-visible:opacity-100', |
| 445 | + ]" |
| 446 | + role="button" |
| 447 | + tabindex="0" |
| 448 | + title="Ask AI" |
| 449 | + @click="askAbout($event, group.items[0])" |
| 450 | + @keydown.enter.stop="askAbout($event, group.items[0])" |
| 451 | + @keydown.space.stop.prevent="askAbout($event, group.items[0])" |
| 452 | + > |
| 453 | + <AppIcon aria-hidden="true" icon="create" size="16" /> |
| 454 | + </span> |
| 455 | + </Discovery.Activator> |
| 456 | + <!-- Dismiss button --> |
| 457 | + <Discovery.Activator |
| 458 | + class="rounded-lg" |
| 459 | + :padding="4" |
| 460 | + step="search-dismiss" |
| 461 | + > |
| 462 | + <span |
| 463 | + aria-label="Dismiss result" |
| 464 | + :class="[ |
| 465 | + 'inline-flex p-1.5 rounded-lg hover:bg-surface-variant focus-visible:bg-surface-variant transition-colors text-on-surface/60 hover:text-on-surface-variant focus-visible:text-on-surface-variant cursor-pointer', |
| 466 | + discovery.isActive.value ? 'opacity-100' : 'opacity-0 group-hover:opacity-100 focus-visible:opacity-100', |
| 467 | + ]" |
| 468 | + role="button" |
| 469 | + tabindex="0" |
| 470 | + title="Dismiss result" |
| 471 | + @click="dismissResult($event, group.items[0].id)" |
| 472 | + @keydown.enter.stop="dismissResult($event, group.items[0].id)" |
| 473 | + @keydown.space.stop.prevent="dismissResult($event, group.items[0].id)" |
| 474 | + > |
| 475 | + <AppIcon aria-hidden="true" icon="close" size="16" /> |
| 476 | + </span> |
| 477 | + </Discovery.Activator> |
| 478 | + </div> |
| 479 | + </div> |
| 480 | + </Discovery.Activator> |
| 481 | + <!-- Remaining results in first group (skip first) --> |
361 | 482 | <div |
362 | | - v-for="(result, itemIndex) in group.items" |
363 | | - :id="`search-result-${getFlatIndex(groupIndex, itemIndex)}`" |
| 483 | + v-for="(result, itemIndex) in groupIndex === 0 ? group.items.slice(1) : group.items" |
| 484 | + :id="`search-result-${getFlatIndex(groupIndex, groupIndex === 0 ? itemIndex + 1 : itemIndex)}`" |
364 | 485 | :key="result.id" |
365 | | - :aria-selected="getFlatIndex(groupIndex, itemIndex) === search.selection.index.value" |
| 486 | + :aria-selected="getFlatIndex(groupIndex, groupIndex === 0 ? itemIndex + 1 : itemIndex) === search.selection.index.value" |
366 | 487 | :class="[ |
367 | 488 | 'group w-full px-4 py-2 flex items-center gap-2 text-left transition-colors cursor-pointer', |
368 | | - getFlatIndex(groupIndex, itemIndex) === search.selection.index.value |
| 489 | + getFlatIndex(groupIndex, groupIndex === 0 ? itemIndex + 1 : itemIndex) === search.selection.index.value |
369 | 490 | ? 'bg-primary/10 text-primary' |
370 | 491 | : 'hover:bg-surface-variant text-on-surface', |
371 | 492 | ]" |
372 | | - :data-selected="getFlatIndex(groupIndex, itemIndex) === search.selection.index.value" |
| 493 | + :data-selected="getFlatIndex(groupIndex, groupIndex === 0 ? itemIndex + 1 : itemIndex) === search.selection.index.value" |
373 | 494 | role="option" |
374 | 495 | tabindex="-1" |
375 | 496 | @click="navigate(result)" |
376 | | - @mouseenter="onHover(getFlatIndex(groupIndex, itemIndex))" |
| 497 | + @mouseenter="onHover(getFlatIndex(groupIndex, groupIndex === 0 ? itemIndex + 1 : itemIndex))" |
377 | 498 | > |
378 | 499 | <div class="flex-1 min-w-0 flex flex-col gap-0.5"> |
379 | 500 | <span class="font-medium">{{ result.title }}</span> |
|
0 commit comments