-
Notifications
You must be signed in to change notification settings - Fork 0
feat(webui): unified search with MiniSearch and GitHub API #124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Implements full-text search for documentation and GitHub content: - MiniSearch for client-side docs search (51 documents indexed) - GitHub Search API for issues, PRs, and commits - SearchPalette component with dark theme styling - Filter chips for toggling search sources - Debounced search with configurable delay - Search config in core/webui-config.ts Closes #107
1. Configurable Keyboard Shortcut (Ctrl+K Issue) - COMPLETEDProblemCurrent
Industry Patterns
RecommendationUse Cmd/Ctrl+P or Cmd/Ctrl+Shift+P for web apps to avoid browser conflicts. The Proposed ImplementationMake shortcut configurable in export interface SearchConfig {
// ... existing fields
shortcut: {
key: string // default: 'p'
requireMeta: boolean // default: true (Cmd on Mac, Ctrl on Win/Linux)
requireShift: boolean // default: true
}
}Files to modify:
Tasks:
|
2. GitHub API Rate Limit UX - COMPLETEDProblemCurrent behavior silently returns empty array on 403 (rate limit), logs warning to console only: if (res.status === 403) {
console.warn('GitHub API rate limit exceeded')
return []
}This is misleading UX - users think there are no results when we actually couldn't query. GitHub Rate Limits
RecommendationReturn error state to UI, show visual indicator. Proposed Implementation
export interface UseSearchReturn {
// ... existing
githubRateLimited: boolean
rateLimitResetTime?: Date
}
if (res.status === 403) {
const resetTime = res.headers.get('X-RateLimit-Reset')
return {
rateLimited: true,
resetTime: resetTime ? new Date(Number(resetTime) * 1000) : undefined
}
}
{githubRateLimited && (
<div className="search-palette__rate-limit-warning">
GitHub search unavailable (rate limit). Showing docs only.
{rateLimitResetTime && ` Resets ${formatRelativeTime(rateLimitResetTime)}`}
</div>
)}Files to modify:
__ EDIT: text "GitHub search unavailable (rate limit). Showing docs only." change to "GitHub search unavailable (rate limit). Showing limited results", make string configurable in webui-config, keep console warning and inform when limit resets and disable github chips (re-enable on new search if limit reset) |
3. Gitignore search-index.json - COMPLETEDProblem
RecommendationYes, add to Proposed ImplementationAdd to # Generated search index (regenerated by scripts/prebuild.ts)
public/search-index.jsonThen remove from git tracking: git rm --cached webui/public/search-index.jsonFiles to modify:
|
4. Update Icons to GitHub Octicons - COMPLETEDChangeReplace generic icons with official GitHub Octicons for PRs and Commits filter chips for consistency. New IconsPRs - git-pull-request octicon: <svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
<path d="M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z" />
</svg>Commits - git-commit octicon: <svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
<path d="M11.93 8.5a4.002 4.002 0 0 1-7.86 0H.75a.75.75 0 0 1 0-1.5h3.32a4.002 4.002 0 0 1 7.86 0h3.32a.75.75 0 0 1 0 1.5Zm-1.43-.75a2.5 2.5 0 1 0-5 0 2.5 2.5 0 0 0 5 0Z" />
</svg>Files to modify:
|
5. Configurable GitHub Chip Grouping - COMPLETEDFeatureMake GitHub source chip grouping configurable with 3 display modes to communicate which chips search GitHub:
Proposed Implementation
export interface SearchConfig {
// ... existing
githubChipDisplay: 'divider' | 'label' | 'icon'
}
const GitHubMarkIcon = () => (
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" />
</svg>
)
{githubChipDisplay === 'divider' && <div className="search-palette__chip-divider" />}
{githubChipDisplay === 'label' && <span className="search-palette__github-label">GitHub</span>}
{/* For each GitHub chip when mode is 'icon' */}
<button className="search-palette__filter">
{githubChipDisplay === 'icon' && <GitHubMarkIcon />}
{/* ... rest of chip */}
</button>
title={`Search GitHub ${SOURCE_LABELS[source].toLowerCase()}`}Files to modify:
|
6. Wider Modal with Sorting and Repository Filtering - COMPLETEDChanges1. Increase modal widthCurrent: Recommendation: Increase to
.search-palette {
max-width: 800px; /* was 680px */
}2. Add sort dropdownSort options:
const [sortBy, setSortBy] = useState<'relevance' | 'date' | 'alpha'>('relevance')
// Sort results before display
const sortedResults = useMemo(() => {
switch (sortBy) {
case 'date':
return [...results].sort((a, b) =>
new Date(b.metadata?.date || 0).getTime() - new Date(a.metadata?.date || 0).getTime()
)
case 'alpha':
return [...results].sort((a, b) => a.title.localeCompare(b.title))
default:
return results // already sorted by relevance
}
}, [results, sortBy])3. Add repository filterMulti-select dropdown listing repos from const [selectedRepos, setSelectedRepos] = useState<string[]>(
searchConfig.githubRepos.map(r => `${r.owner}/${r.repo}`)
)
// Pass to search functions to filter API queriesFiles to modify:
|
10. Docs Chip Icon Consistency - COMPLETEDIssueThe documentation filter chip in search results should use the same icon as the "Docs" button in the header for visual consistency. Proposed Implementation
Files to modify:
|
11. Search Results as Web Links - COMPLETEDIssueEach search result item should be a proper web link (
Current BehaviorResults may be rendered as clickable divs/buttons that navigate programmatically, which breaks standard browser link interactions. Proposed ImplementationWrap each result item in an <a
href={result.href}
className="search-palette__result-item"
onClick={(e) => {
// Handle internal navigation if needed
if (!e.metaKey && !e.ctrlKey && !e.shiftKey) {
e.preventDefault()
// internal navigation logic
onClose()
}
// Otherwise let browser handle (new tab, etc.)
}}
>
{/* result content */}
</a>This preserves:
Files to modify:
|
14. Replace Placeholder Text with Magnifying Glass Icon - COMPLETEDIssueThe search box in OmniBoxBar uses placeholder text "search..." - should use a magnifying glass icon instead, matching the icon used in the search results modal input. Current State
Proposed ImplementationReplace the placeholder text with an icon prefix in the input: <div className="omnibox-search-wrapper">
<SearchIcon className="omnibox-search-icon" />
<input
type="text"
className="omnibox-search-input"
placeholder="" // or keep minimal text if needed for accessibility
// ...
/>
</div>.omnibox-search-wrapper {
display: flex;
align-items: center;
gap: 8px;
}
.omnibox-search-icon {
width: 16px;
height: 16px;
color: var(--text-secondary);
}Accessibility note: May want to keep a visually-hidden label or aria-label for screen readers. Files to modify:
|
15. Make Search Text Strings Configurable - COMPLETEDIssueHardcoded text strings in the search UI should be configurable in
Rationale
Proposed ImplementationAdd to export interface SearchConfig {
// ... existing fields
strings: {
placeholder: string // "Search docs, issues, PRs, commits..."
emptyStateHint: string // "Type to search documentation and GitHub"
noResultsText: string // "No results found"
rateLimitWarning: string // "GitHub search unavailable (rate limit)"
}
}With defaults: const defaultSearchStrings = {
placeholder: 'Search docs, issues, PRs, commits...',
emptyStateHint: 'Type to search documentation and GitHub',
noResultsText: 'No results found',
rateLimitWarning: 'GitHub search unavailable (rate limit). Showing docs only.'
}Usage in component: const { strings } = searchConfig
// ...
<input placeholder={strings.placeholder} />
// ...
{!query && <p>{strings.emptyStateHint}</p>}Files to modify:
|
Add ShortcutConfig with enabled, key, requireMeta, and requireShift options. Default shortcut is "/" (like GitHub) but currently disabled due to Firefox compatibility issue. Shortcut hint in header now displays dynamically based on config and platform (Cmd vs Ctrl).
Previously, when GitHub API hit 403 rate limit, the search returned empty results with only a console warning. Users had no way to know the search temporarily failed vs no results existing. Now displays a warning banner with reset time, disables GitHub filter buttons, and allows docs search to continue working independently.
The search index is auto-generated during prebuild by scripts/prebuild.ts, same pattern as synced MDX docs - no need to track in version control.
Replace custom SVG icons with official GitHub octicons for visual consistency: - docs: book octicon (matches header DocsButton style) - prs: git-pull-request octicon - commits: git-commit octicon
Replace text-only placeholder with icon + text for visual consistency with SearchPalette modal input. Added role="button" and aria-label for accessibility.
Move hardcoded UI text strings to webui-config.ts: - placeholder: search input placeholder - emptyStateHint: hint shown when input is empty - noResultsText: message when no results found Enables customization across deployments and potential localization.
Add githubChipDisplay config with three modes: - 'divider': vertical separator after Docs chip - 'label': "GitHub" text label before GitHub chips - 'icon': GitHub mark icon prefix on each chip (default) Also adds descriptive tooltips to all filter chips.
- Increase modal width from 680px to 800px - Add sort dropdown: Relevance (default), Date, Alphabetical - Add repository filter dropdown for GitHub sources - Store date and repo metadata in search results for sorting/filtering
Replace div-based result items with semantic <a> tags to enable: - Copy link address via browser context menu - Middle-click / Ctrl+click to open in new tab - Standard link semantics for accessibility Internal docs links use client-side navigation while external GitHub links open in new tabs with proper rel attributes.
Summary
Implements full-text search for the developer dashboard (Ctrl+K):
core/webui-config.tscontrols repos, limits, debounceCloses #107