Skip to content

Conversation

@vredchenko
Copy link
Collaborator

@vredchenko vredchenko commented Jan 20, 2026

Summary

Implements full-text search for the developer dashboard (Ctrl+K):

  • Docs search: MiniSearch indexes 51 MDX documents at build time
  • GitHub search: Issues, PRs, and commits via GitHub Search API
  • SearchPalette: Dark-themed modal with filter chips, keyboard navigation
  • Configurable: core/webui-config.ts controls repos, limits, debounce

Closes #107

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
@github-actions github-actions bot added the smartem-devtools:webui Developer dashboard web interface label Jan 20, 2026
@vredchenko vredchenko added development New features or functionality implementation smartem-devtools Developer tooling, documentation, and workspace configuration labels Jan 20, 2026
@vredchenko
Copy link
Collaborator Author

vredchenko commented Jan 20, 2026

1. Configurable Keyboard Shortcut (Ctrl+K Issue) - COMPLETED

Problem

Current Ctrl+K shortcut conflicts with browser shortcuts:

  • Chrome: Opens address bar/omnibox
  • Firefox: Focuses search bar

Industry Patterns

Tool Mac Windows/Linux
VS Code Cmd+Shift+P Ctrl+Shift+P
GitHub Cmd+K Ctrl+K
Sublime Text Cmd+Shift+P Ctrl+Shift+P
Notion Cmd+K Ctrl+K

Recommendation

Use Cmd/Ctrl+P or Cmd/Ctrl+Shift+P for web apps to avoid browser conflicts. The / key (like GitHub's quick search) is another option.

Proposed Implementation

Make shortcut configurable in core/webui-config.ts:

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:

  • core/webui-config.ts
  • webui/src/hooks/useSearch.ts or SearchPalette.tsx
  • webui/src/components/header/OmniBoxBar.tsx (shortcut hint display)

Tasks:

  1. Add shortcut config to SearchConfig in core/webui-config.ts
  2. Update SearchPalette.tsx to read from config
  3. Update keyboard listener to check e.shiftKey
  4. Display correct shortcut in UI (detect platform for Cmd vs Ctrl symbol)

@vredchenko
Copy link
Collaborator Author

vredchenko commented Jan 20, 2026

2. GitHub API Rate Limit UX - COMPLETED

Problem

Current 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

  • Unauthenticated: 60 requests/hour
  • Authenticated: 30 req/min for code search, 5000/hour for issues/commits

Recommendation

Return error state to UI, show visual indicator.

Proposed Implementation

  1. Update useSearch.ts return type:
export interface UseSearchReturn {
  // ... existing
  githubRateLimited: boolean
  rateLimitResetTime?: Date
}
  1. Parse rate limit headers on 403:
if (res.status === 403) {
  const resetTime = res.headers.get('X-RateLimit-Reset')
  return { 
    rateLimited: true, 
    resetTime: resetTime ? new Date(Number(resetTime) * 1000) : undefined 
  }
}
  1. Show banner in SearchPalette.tsx:
{githubRateLimited && (
  <div className="search-palette__rate-limit-warning">
    GitHub search unavailable (rate limit). Showing docs only.
    {rateLimitResetTime && ` Resets ${formatRelativeTime(rateLimitResetTime)}`}
  </div>
)}

Files to modify:

  • webui/src/hooks/useSearch.ts
  • webui/src/components/widgets/SearchPalette/SearchPalette.tsx
  • webui/src/components/widgets/SearchPalette/SearchPalette.css

__

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)

@vredchenko
Copy link
Collaborator Author

vredchenko commented Jan 20, 2026

3. Gitignore search-index.json - COMPLETED

Problem

public/search-index.json is currently committed to git, but it's auto-generated during prebuild - same pattern as synced MDX docs.

Recommendation

Yes, add to .gitignore.

Proposed Implementation

Add to webui/.gitignore:

# Generated search index (regenerated by scripts/prebuild.ts)
public/search-index.json

Then remove from git tracking:

git rm --cached webui/public/search-index.json

Files to modify:

  • webui/.gitignore

@vredchenko
Copy link
Collaborator Author

vredchenko commented Jan 20, 2026

4. Update Icons to GitHub Octicons - COMPLETED

Change

Replace generic icons with official GitHub Octicons for PRs and Commits filter chips for consistency.

New Icons

PRs - 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:

  • webui/src/components/widgets/SearchPalette/SearchPalette.tsx

@vredchenko
Copy link
Collaborator Author

vredchenko commented Jan 20, 2026

5. Configurable GitHub Chip Grouping - COMPLETED

Feature

Make GitHub source chip grouping configurable with 3 display modes to communicate which chips search GitHub:

  1. divider - Vertical divider after Docs chip + tooltips
  2. label - "GitHub" text label before Issues/PRs/Commits
  3. icon - GitHub logo icon prefix on each chip (recommended default)

Proposed Implementation

  1. Add to SearchConfig:
export interface SearchConfig {
  // ... existing
  githubChipDisplay: 'divider' | 'label' | 'icon'
}
  1. Add GitHub mark 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>
)
  1. Render based on config:
{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>
  1. Add tooltips to all GitHub chips:
title={`Search GitHub ${SOURCE_LABELS[source].toLowerCase()}`}

Files to modify:

  • core/webui-config.ts
  • webui/src/components/widgets/SearchPalette/SearchPalette.tsx
  • webui/src/components/widgets/SearchPalette/SearchPalette.css

@vredchenko
Copy link
Collaborator Author

vredchenko commented Jan 20, 2026

6. Wider Modal with Sorting and Repository Filtering - COMPLETED

Changes

1. Increase modal width

Current: max-width: 680px

Recommendation: Increase to 800px or 900px for:

  • Better readability of long titles
  • Room for additional columns (date, repo)
  • Sort/filter controls
.search-palette {
  max-width: 800px; /* was 680px */
}

2. Add sort dropdown

Sort options:

  • Relevance (default for MiniSearch)
  • Date (newest first) - available in GitHub API response
  • Alphabetical
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 filter

Multi-select dropdown listing repos from searchConfig.githubRepos:

const [selectedRepos, setSelectedRepos] = useState<string[]>(
  searchConfig.githubRepos.map(r => `${r.owner}/${r.repo}`)
)

// Pass to search functions to filter API queries

Files to modify:

  • webui/src/components/widgets/SearchPalette/SearchPalette.tsx
  • webui/src/components/widgets/SearchPalette/SearchPalette.css
  • webui/src/hooks/useSearch.ts

@vredchenko
Copy link
Collaborator Author

vredchenko commented Jan 20, 2026

10. Docs Chip Icon Consistency - COMPLETED

Issue

The documentation filter chip in search results should use the same icon as the "Docs" button in the header for visual consistency.

Proposed Implementation

  1. Extract the docs icon used in the header into a shared component or constant
  2. Reuse it in the SearchPalette for the Docs filter chip

Files to modify:

  • Identify the icon used in header's Docs button
  • webui/src/components/widgets/SearchPalette/SearchPalette.tsx
  • Possibly create a shared icons file if not already present

@vredchenko
Copy link
Collaborator Author

vredchenko commented Jan 20, 2026

11. Search Results as Web Links - COMPLETED

Issue

Each search result item should be a proper web link (<a> tag) so users can:

  • Copy-paste the URL
  • Open in new tab (middle-click or Ctrl+click)
  • Copy link address via browser context menu
  • Share links directly

Current Behavior

Results may be rendered as clickable divs/buttons that navigate programmatically, which breaks standard browser link interactions.

Proposed Implementation

Wrap each result item in an <a> tag with the href attribute:

<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:

  • Standard link semantics for accessibility
  • Browser context menu with "Copy link address"
  • Middle-click / Ctrl+click for new tab
  • Programmatic navigation for same-tab clicks

Files to modify:

  • webui/src/components/widgets/SearchPalette/SearchPalette.tsx
  • webui/src/components/widgets/SearchPalette/SearchPalette.css (style a instead of div/button)

@vredchenko
Copy link
Collaborator Author

vredchenko commented Jan 20, 2026

14. Replace Placeholder Text with Magnifying Glass Icon - COMPLETED

Issue

The 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

  • OmniBoxBar search box: text placeholder "search..."
  • SearchPalette modal input: has magnifying glass icon

Proposed Implementation

Replace 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:

  • webui/src/components/header/OmniBoxBar.tsx
  • webui/src/components/header/OmniBoxBar.css (or equivalent)

@vredchenko
Copy link
Collaborator Author

vredchenko commented Jan 20, 2026

15. Make Search Text Strings Configurable - COMPLETED

Issue

Hardcoded text strings in the search UI should be configurable in webui-config.ts:

  • "Search docs, issues, PRs, commits..."
  • "Type to search documentation and GitHub"

Rationale

  • Different deployments may want different wording
  • Future features may add/remove search sources
  • Enables localization if needed later

Proposed Implementation

Add to core/webui-config.ts:

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:

  • core/webui-config.ts
  • webui/src/components/widgets/SearchPalette/SearchPalette.tsx

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.
@vredchenko vredchenko merged commit 0d3973b into main Jan 20, 2026
6 checks passed
@vredchenko vredchenko deleted the feature/search-palette branch January 20, 2026 18:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

development New features or functionality implementation smartem-devtools:webui Developer dashboard web interface smartem-devtools Developer tooling, documentation, and workspace configuration

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(webui): implement keyword search functionality

2 participants