Build a web interface for the tars personal search engine using:
- Backend: FastAPI (Python)
- Frontend: HTMX + TailwindCSS (via CDN)
- Caching: PostgreSQL-based query caching for hybrid search
- Command:
tars webto start the server
┌─────────────────────────────────────────────────────────────────────┐
│ tars web (FastAPI) │
├─────────────────────────────────────────────────────────────────────┤
│ Templates (Jinja2 + HTMX + TailwindCSS) │
│ ├── base.html (layout, nav, scripts) │
│ ├── index.html (search homepage) │
│ ├── links/list.html (paginated link list) │
│ ├── links/detail.html (single link view with content) │
│ ├── links/add.html (add link form) │
│ ├── search/results.html (search results) │
│ ├── crawl/status.html (crawl management) │
│ └── partials/ (HTMX partial templates) │
├─────────────────────────────────────────────────────────────────────┤
│ API Routes │
│ ├── / GET - Search homepage │
│ ├── /search GET - Hybrid search with caching │
│ ├── /links GET - List links (paginated) │
│ ├── /links/add POST - Add new link │
│ ├── /links/{id} GET - View link detail + content │
│ ├── /links/{id}/hide POST - Toggle hidden status │
│ ├── /links/{id}/delete DELETE - Remove link │
│ ├── /crawl GET - Crawl management page │
│ ├── /crawl/start POST - Start crawl job │
│ ├── /db/status GET - Database status │
│ └── /db/vector/status GET - Vector/embedding status │
├─────────────────────────────────────────────────────────────────────┤
│ Database (PostgreSQL) │
│ ├── links table (+ hidden column) │
│ └── search_cache table (query caching) │
└─────────────────────────────────────────────────────────────────────┘
1. Add hidden column to links table
ALTER TABLE links ADD COLUMN hidden BOOLEAN NOT NULL DEFAULT FALSE;- Hidden links are excluded from search results
- Hidden links are skipped during crawl operations
- Can be toggled via web UI
CREATE TABLE search_cache (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
query_hash TEXT NOT NULL, -- SHA256 of normalized query
query_text TEXT NOT NULL, -- Original query for debugging
keyword_weight NUMERIC(3,2) NOT NULL,
vector_weight NUMERIC(3,2) NOT NULL,
results JSONB NOT NULL, -- Cached search results
total_count INTEGER NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ NOT NULL, -- Cache expiration
UNIQUE(query_hash, keyword_weight, vector_weight)
);
CREATE INDEX search_cache_expires_idx ON search_cache(expires_at);
CREATE INDEX search_cache_query_idx ON search_cache(query_hash);- Invalidate cache when links are added/updated/deleted
- Invalidate cache when embeddings are regenerated
- Default TTL: 1 hour (configurable)
Files: src/tars/db.py
Tasks:
- Add
hiddencolumn to schema indb_init() - Add migration for existing tables to add
hiddencolumn - Create
search_cachetable schema - Implement cache functions:
db_cache_search(query_hash, results, ttl)- Store cached resultsdb_get_cached_search(query_hash)- Retrieve if not expireddb_invalidate_search_cache()- Clear all cachedb_cleanup_expired_cache()- Remove expired entries
- Modify
db_hybrid_search()to check cache first - Modify all search functions to exclude
hidden = TRUE - Add
db_toggle_hidden(url)function - Add
db_get_link_by_id(id)function for detail view - Modify
db_get_links_to_crawl()to exclude hidden links
Dependencies: None (can start immediately)
Files: src/tars/web/__init__.py, src/tars/web/app.py
Tasks:
- Create
src/tars/web/package directory - Set up FastAPI app with:
- Jinja2 templates configuration
- Static files serving (for any local assets)
- CORS middleware (for development)
- Request/response logging
- Create base configuration:
- Host/port settings (default: 127.0.0.1:8000)
- Debug mode toggle
- Template directory path
- Implement health check endpoint
/health - Implement database status endpoint
/db/status - Set up error handlers (404, 500)
Dependencies: None (can start immediately)
Files: src/tars/web/routes/links.py
Tasks:
- Create router for link management
- Implement endpoints:
GET /links- List all links (paginated, HTMX partial support)GET /links/{id}- View single link with full contentPOST /links/add- Add new link (form submission)POST /links/{id}/hide- Toggle hidden status (HTMX)DELETE /links/{id}- Remove link (HTMX)POST /links/{id}/crawl- Trigger crawl for single link
- Support both full page and HTMX partial responses
- Implement pagination with HTMX infinite scroll or page buttons
Dependencies: Agent 1 (for db_get_link_by_id, db_toggle_hidden)
Files: src/tars/web/routes/search.py
Tasks:
- Create router for search functionality
- Implement endpoints:
GET /- Homepage with search formGET /search- Hybrid search with resultsGET /search/text- BM25-only searchGET /search/vector- Vector-only search
- Implement search form with:
- Query input
- Search type selector (hybrid/text/vector)
- Weight sliders for hybrid search
- Results per page selector
- Cache integration for hybrid search
- Result highlighting (bold matching terms)
- "Open in new tab" links for all results
Dependencies: Agent 1 (for cache functions)
Files: src/tars/web/routes/crawl.py, src/tars/web/routes/db.py
Tasks:
- Create router for crawl management
- Implement endpoints:
GET /crawl- Crawl management pagePOST /crawl/start- Start crawl (with mode: all/missing/old)GET /crawl/status- Get current crawl progress (SSE or polling)
- Create router for database management
- Implement endpoints:
GET /db- Database management pagePOST /db/init- Initialize databaseGET /db/vector/status- Vector search statusPOST /db/vector/embed- Generate embeddings
- Background task support for long-running operations
- Progress reporting via HTMX polling
Dependencies: Agent 2 (for app setup)
Files: src/tars/web/templates/base.html, src/tars/web/templates/nav.html
Tasks:
- Create templates directory structure
- Implement
base.htmlwith:- TailwindCSS CDN link
- HTMX CDN link
- Dark mode support
- Responsive layout
- Navigation include
- Implement
nav.htmlnavigation component:- Logo/brand
- Search (highlighted if on search page)
- Links (list all links)
- Add Link
- Crawl
- Database Status
- Implement
_flash.htmlpartial for flash messages - Set up Tailwind color scheme (professional, readable)
Dependencies: None (can start immediately)
Files: src/tars/web/templates/search/, src/tars/web/templates/partials/
Tasks:
- Create
search/index.html- Search homepage:- Large centered search input
- Search type tabs (Hybrid/Text/Vector)
- Quick stats (total links, embedded count)
- Create
search/results.html- Search results page:- Search form at top
- Results list with:
- Title (linked, opens in new tab)
- URL (truncated, linked)
- Description snippet
- Search scores (RRF, keyword rank, vector rank)
- Quick actions (hide, crawl, view detail)
- Pagination controls
- Create
partials/search_result.html- Single result item (for HTMX) - Create
partials/pagination.html- Reusable pagination
Dependencies: Agent 6 (for base template)
Files: src/tars/web/templates/links/
Tasks:
- Create
links/list.html- All links list:- Table/card view with columns:
- Title/URL
- Added date
- Crawl status (icon: crawled/pending/error)
- Hidden status (strikethrough if hidden)
- Actions (view, hide, delete, crawl)
- Filter controls (show hidden, crawl status)
- Bulk actions
- Table/card view with columns:
- Create
links/detail.html- Single link view:- Full metadata display
- Rendered content (scrollable, formatted)
- Open original link button (new tab)
- Crawl history/status
- Hide/unhide toggle
- Delete with confirmation
- Create
links/add.html- Add link form:- URL input with validation
- Optional notes field
- "Add and crawl" checkbox
- Create
partials/link_row.html- Table row (for HTMX updates)
Dependencies: Agent 6 (for base template)
Files: src/tars/web/templates/crawl/, src/tars/web/templates/db/
Tasks:
- Create
crawl/index.html- Crawl management:- Stats cards (total, crawled, pending, errors)
- Crawl mode selector (missing/all/old N days)
- Start crawl button
- Progress display (during crawl)
- Recent crawl log
- Create
db/index.html- Database management:- Connection status card
- Schema status
- Initialize button (if needed)
- Create
db/vector.html- Vector search status:- Embedding stats (total, embedded, pending)
- Generate embeddings button
- Progress display
- Create confirmation modals (delete, bulk actions)
Dependencies: Agent 6 (for base template)
Files: src/tars/__init__.py
Tasks:
- Add
tars webcommand to CLI:@main.command() @click.option('--host', default='127.0.0.1', help='Host to bind') @click.option('--port', default=8000, help='Port to bind') @click.option('--reload', is_flag=True, help='Enable auto-reload') def web(host, port, reload): """Start the web interface"""
- Add uvicorn as dependency in pyproject.toml
- Add fastapi and jinja2 dependencies
- Import and run the FastAPI app with uvicorn
- Add
--openflag to auto-open browser
Dependencies: Agent 2 (for app module)
src/tars/
├── __init__.py (add web command)
├── db.py (add hidden, cache)
├── crawl.py (no changes)
└── web/
├── __init__.py (package init)
├── app.py (FastAPI app setup)
├── routes/
│ ├── __init__.py
│ ├── links.py (link management)
│ ├── search.py (search endpoints)
│ ├── crawl.py (crawl management)
│ └── db.py (database management)
└── templates/
├── base.html
├── nav.html
├── search/
│ ├── index.html
│ └── results.html
├── links/
│ ├── list.html
│ ├── detail.html
│ └── add.html
├── crawl/
│ └── index.html
├── db/
│ ├── index.html
│ └── vector.html
└── partials/
├── flash.html
├── pagination.html
├── search_result.html
└── link_row.html
# pyproject.toml additions
dependencies = [
# ... existing ...
"fastapi>=0.109.0",
"uvicorn[standard]>=0.27.0",
"jinja2>=3.1.0",
"python-multipart>=0.0.6", # For form handling
]Phase 1 (Parallel - No Dependencies):
├── Agent 1: Database Schema & Caching
├── Agent 2: FastAPI App Setup
└── Agent 6: Base Templates & Layout
Phase 2 (After Phase 1):
├── Agent 3: Link Routes (needs Agent 1)
├── Agent 4: Search Routes (needs Agent 1)
├── Agent 5: Crawl/DB Routes (needs Agent 2)
├── Agent 7: Search Templates (needs Agent 6)
├── Agent 8: Link Templates (needs Agent 6)
└── Agent 9: Crawl/DB Templates (needs Agent 6)
Phase 3 (After Phase 2):
└── Agent 10: CLI Integration (needs Agent 2)
- Large, prominent search bar on homepage
- Real-time search suggestions (optional, future)
- Three search modes: Hybrid (default), Text-only, Vector-only
- Adjustable weights for hybrid search
- Cached results for faster repeat searches
- Score display showing why results ranked
- Clean list view with sorting and filtering
- Detail view showing rendered crawled content
- One-click hide/unhide from results
- One-click delete with confirmation
- Quick crawl trigger per link
- All links open in new tab (target="_blank")
- Visual progress indicator
- Mode selection (missing/all/old)
- Per-link crawl status indicators
- Error reporting and retry
- PostgreSQL query caching (1 hour TTL)
- Cache invalidation on data changes
- HTMX partial updates (no full page reloads)
- Pagination for large result sets
- TailwindCSS utility classes
- Dark mode support
- Responsive (mobile-friendly)
- Clean, minimal interface
- Consistent spacing and typography
- Partial page updates:
hx-get="/links" hx-target="#content" - Inline editing:
hx-post="/links/{id}/hide" hx-swap="outerHTML" - Delete with confirmation:
hx-confirm="Are you sure?" - Form submission:
hx-post="/links/add" hx-target="#link-list" - Infinite scroll:
hx-trigger="revealed" hx-get="/links?page=2" - Search as you type (optional):
hx-trigger="keyup changed delay:500ms" - Progress polling:
hx-trigger="every 2s" hx-get="/crawl/status"
- Hybrid search results (most expensive operation)
- Query + weights combination as cache key
cache_key = hashlib.sha256(
f"{query.lower().strip()}:{keyword_weight}:{vector_weight}".encode()
).hexdigest()- New link added → invalidate all
- Link updated/deleted → invalidate all
- Embeddings regenerated → invalidate all
- Link hidden/unhidden → invalidate all
- Default: 1 hour
- Configurable via environment variable
TARS_CACHE_TTL
- Search returns results with correct ranking
- Hidden links don't appear in search results
- Cache speeds up repeat searches
- All CLI features work via web interface
- Links open in new tabs
- Content renders correctly in detail view
- Crawl progress updates in real-time
- Pagination works correctly
- Forms validate input
- Error states display properly
- Mobile responsive layout