Skip to main content
vibld

Template

Seekroom

A search modal in an AI chat assistant that lets people find past conversations and projects by title or content. With an empty query it doubles as a quick switcher showing the most recent items.

AI chat app search modal for past chats and projects · App screen: search · Small tools and apps · full-stack app (auth + DB)

A mock-up of the screen, drawn from its layout, palette and typefaces. A build follows the full prompt below.

Start from this screenRead the build prompt

Typefaces

The catalog's own faces. A screen composed into a template is drawn in that template's typefaces.

  • InterHeadings: Inter 400, 16px input text
  • InterBody: Inter 400 16px row titles; 13px relative times (muted)

Patterns

  • centered search modal on warm off-white canvas
  • list of recent chats with relative time on the right
  • highlighted first row with Enter key hint
  • minimal icon rail (new, search, chats, projects, artifacts, code)
  • close x in input row
  • type icon per row (chat bubble vs project box)
  • avatar at rail bottom

States it is designed for

  • Empty query: recents
  • Searching (subtle spinner in input)
  • Results with snippets
  • No results ("No chats match. Try fewer words.")
  • Search unavailable (error line with retry)
  • Very long titles truncated with ellipsis
  • Brand-new account (no chats yet)

Who it is for

  • Heavy users of an AI assistant with many chats
  • Knowledge workers organising work into projects
  • Developers returning to earlier coding sessions

Layout

  1. Thin icon rail (~28-56px): sidebar toggle, new chat (+), search, chats, projects, artifacts, code; download and avatar at bottom
  2. Canvas: warm off-white, empty chat behind
  3. Modal (~415px wide, vertically centered): search icon + input "Search chats and projects" + close x; list of up to 8 rows (icon, title, relative time or ↵ on active)
  4. Mobile: full-screen sheet with input at top

Palette

warm, quiet, literate, unhurried. Like flipping through a notebook rather than a database.

  • app background#edece8
  • modal background#ffffff
  • highlighted row#f5f0e6
  • row titles#1f1e1b
  • relative times, placeholder#6b6a64
  • rail icon colour#3d3c38
  • focus ring, active bar#b8572f
  • modal hairline (decorative)#dedcd4
  • avatar circle#3d3c38

Every checked pair, measured again

SampleWhereRatioNeeds
Aarow title on modal16.67:14.5:1
Aamuted time on modal5.43:14.5:1
Aamuted time on active row4.78:14.5:1
Aatitle on active row14.68:14.5:1
rail icons on canvas9.34:13:1
focus ring on modal4.73:13:1
Aatext on canvas14.10:14.5:1

As vibld’s tokens

The palette on the fifteen colour tokens vibld styles a project with, each text colour on the fill it is read on. Marked tokens are solved from the palette, because no swatch held that role at 4.5:1.

  • background
  • card
  • muted
  • primary
  • secondary
  • accent
  • destructive *

Type scale

Display
Inter 400, 16px input text
Body
Inter 400 16px row titles; 13px relative times (muted)

Similar to the observed soft grotesk. No headings in the modal; hierarchy comes from weight and colour only.

Spacing and imagery

Compact but airy: 4px base; input row 48px; list rows 36px with 12px side padding and 4px inset radius; modal radius 12px, padding 8px; shadow 0 12px 40px rgba(40,30,10,.12).

None. 16px outline icons: chat bubble for chats, box for projects.

Components

  • Icon rail
  • Search modal
  • Search input with close
  • Result row (icon, title, time)
  • Active row with Enter hint
  • Match highlight in titles and snippets
  • Section divider for Projects vs Chats (on query)

Interactions

  • Cmd/Ctrl+K opens; Esc or x closes
  • Empty query shows recent items; typing searches titles and message content (debounced 200ms)
  • Arrow keys move the active row; Enter opens; mouse hover sets active
  • When content matches, a one-line snippet appears under the title with the match bolded
  • Modal fades and scales 0.98→1 in 120ms

Data

  • Conversation{id, user_id, title, project_id?, updated_at}
  • Message{id, conversation_id, role, content, created_at}
  • Project{id, user_id, name, updated_at}
  • SearchHit{kind (chat|project), id, title, snippet?, updated_at}

Guardrails

Experience

  • With no query, act as a quick switcher of recent items
  • Show relative times in plain words (Just now, Past hour, Past week, Past month)
  • Distinguish chats and projects with icons and, when searching, section labels
  • Keep the modal compact (max 8 visible rows, scroll the rest)
  • Enter hint shown only on the active row

Accessibility

  • Combobox + listbox; active row via aria-activedescendant
  • Row accessible name includes type and time ("Chat: Offsite ideas, past month")
  • Close button labelled; Esc works
  • Matches highlighted with <mark> styled bold, not colour only
  • Focus ring visible on the warm canvas

Security

  • Search strictly scoped to the signed-in user (RLS on conversations, messages, projects)
  • Snippets generated server-side and HTML-escaped
  • Do not index or return deleted chats; honour retention settings

Build prompt

The baseline every prompt in the catalog assumes, then this design’s own ten sections, from goal to guardrails.

The baseline
### How to use these prompts
Paste an entry's build prompt into your coding agent as the first message. Each prompt names its own stack, tokens and acceptance criteria; the rules below apply to all of them and can be prepended once per project.

### Engineering baseline
- TypeScript strict mode, no `any`, small typed components, feature folders, and one source of truth for design tokens (CSS variables consumed by Tailwind).
- Validate every input with a shared zod schema on the client and again on the server or edge function. Never trust client-side checks alone.
- Show loading, empty and error states for every async view. Surface errors in plain language with a retry, and log details to the console in development only.
- Keep secrets out of the bundle. Only publishable keys (for example a Supabase anon key) belong in client code; service-role keys, API keys and webhooks live in server or edge-function environment variables.

### Data and auth baseline (full-stack entries)
- Enable Row Level Security on every table before inserting data. Default-deny, then add owner-scoped policies (`auth.uid() = user_id`) and explicit role checks for admin views.
- Store roles in a separate table checked by a security-definer function, never in a user-editable profile field.
- Upload files to private storage buckets with size and MIME limits, and serve them through signed URLs.
- Rate-limit public endpoints (forms, auth, AI calls) and add a honeypot field or captcha to anonymous forms.
- Take payments through a hosted checkout and verify webhooks by signature. Never handle raw card data.

### Accessibility and UX baseline
- Target WCAG 2.2 AA: 4.5:1 contrast for normal text and 3:1 for large text, input borders, focus rings and meaningful icons or chart lines. Every palette in this catalog lists its verified pairs; re-check with a contrast tool after any colour change.
- Keep body text at 16px or larger with 1.5 line height, nothing below 12px, no light weights under 24px, and uppercase only for short labels.
- Give every interactive element a visible focus ring, full keyboard support, semantic landmarks, labelled form fields, and alt text on meaningful images.
- Respect `prefers-reduced-motion` for every animation. Give drag-and-drop and carousels keyboard and button alternatives.
- Build mobile-first and test at 375px, 768px and 1280px.

### Content guardrails
- Use original copy, fictional sample data and placeholder or licensed imagery. Do not reuse another product's name, logo, screenshots or marketing text.
- Label demo testimonials and metrics as samples. Collect the minimum personal data the feature needs.

### SaaS screen baseline
- Design every screen for its full set of states: first-run empty, loading skeleton, partial data, error with retry, permission-denied, and success feedback. Each entry lists the states its screen needs.
- Keep destructive actions (delete, revoke, downgrade, remove member) behind a confirmation that names the object, and prefer undo over a second dialog where the action is reversible.
- Enforce authorisation on the server for every action a screen exposes. Hiding a button is not access control; check the role again in the API or RLS policy.
- Never show secrets (API keys, tokens) in full after creation. Show them once, then mask them, and offer rotate and revoke.
- Keep the app shell (navigation, workspace switcher, account menu) consistent across screens, and preserve filters, sort and scroll position when the user navigates back.
### Goal
Build **Seekroom**, the search modal of an AI chat assistant. From anywhere, Cmd/Ctrl+K opens a compact, centred modal on a warm off-white canvas. With an empty query it lists recent chats and projects with relative times; typing searches titles and message text and shows snippets.

### Stack
React + TypeScript + Vite, Tailwind CSS, shadcn/ui Command (cmdk) and Dialog on Radix, lucide-react, TanStack Query, date-fns for relative times. Supabase Postgres full-text search on conversations and messages through an RPC.

### Pages & layout
1. **Icon rail** (56px): sidebar toggle, new chat (+), search, chats, projects, artifacts, code; download and avatar at the bottom.
2. **Canvas**: warm off-white with an empty chat composer behind the modal.
3. **Search modal** (440px, centred): search icon, input "Search chats and projects", close x; rows with a type icon, title and relative time on the right; the active row has a warm tint and a ↵ hint in place of the time. Example rows: "Getting started guide" (project), "Quarterly planning notes", "Image-to-text automation", "Spreadsheet integration setup", "Accessibility audit fixes", "Plan a memorable team offsite".
4. With a query: two small sections ("Projects", "Chats"), and snippets under titles when the match is in message content.
5. Mobile: full-screen sheet.

### Design system
- Colors: `--canvas: #edece8` (app background), `--bg: #ffffff` (modal background), `--active: #f5f0e6` (highlighted row), `--fg: #1f1e1b` (row titles), `--muted: #6b6a64` (relative times, placeholder), `--rail-icon: #3d3c38` (rail icon colour), `--accent: #b8572f` (focus ring, active bar), `--border: #dedcd4` (modal hairline (decorative)), `--avatar: #3d3c38` (avatar circle).
- Fonts: Inter (similar to the observed soft grotesk): input 16px, row titles 16px/400, times 13px muted; line-height 1.5.
- Spacing: 4px base; input row 48px; rows 36px with 12px padding and 4px inset from the modal edge.
- Radius: modal 12px; rows 6px.
- Shadows: `0 12px 40px rgba(40,30,10,.12)`; scrim 20% warm grey.
- Motion: 120ms fade and scale; none under reduced motion.

### Components & interactions
IconRail, SearchDialog, SearchInput (spinner while fetching, close button), ResultList (virtualised after 50), ResultRow (icon, title, optional snippet with bold matches, time or ↵), SectionLabel, EmptyRecents, NoResults, ErrorLine with Retry. Hover and arrows share the active state; Enter opens the item and closes the modal.

### Data & state
Tables: `conversations(id, user_id, title, project_id, updated_at)`, `messages(id, conversation_id, role, content, created_at, tsv)`, `projects(id, user_id, name, updated_at)`. RPC `search_history(q)` returns `{kind, id, title, snippet, updated_at}`. Seed 20 invented chats and 3 projects. Handle states: recents, searching, results with snippets, no results, error, long titles, new account.

### Accessibility
Use combobox and listbox semantics with aria-activedescendant. Row names include the type and time. Use `<mark>` for matches, styled bold. The close button has a label; Esc closes; focus returns to the trigger. Focus ring: 2px terracotta.
Verified contrast: row title on modal: #1f1e1b on #ffffff = 16.67:1; muted time on modal: #6b6a64 on #ffffff = 5.43:1; muted time on active row: #6b6a64 on #f5f0e6 = 4.78:1; title on active row: #1f1e1b on #f5f0e6 = 14.68:1; rail icons on canvas: #3d3c38 on #edece8 = 9.34:1; focus ring on modal: #b8572f on #ffffff = 4.73:1; text on canvas: #1f1e1b on #edece8 = 14.10:1.

### Security
RLS on conversations, messages and projects: `user_id = auth.uid()`. The RPC is `security invoker` so RLS applies. Generate snippets server-side with `ts_headline` and escape them before rendering. Exclude soft-deleted chats and honour the user's retention setting.

### Performance & SEO
Recents come from a cached query and open instantly. Debounce search to 200ms and cancel stale requests. Use a GIN index on the tsvector. The app is noindex.

### Guardrails
- Invented chat titles; no real product names in sample data.
- Keep the modal compact and never cover the whole canvas on desktop.
- Acceptance criteria: (1) Cmd/Ctrl+K opens and Esc closes; (2) the empty query shows recents; (3) content matches show snippets; (4) keyboard navigation is complete; (5) all states render; (6) RLS prevents cross-user results.

Open the builderAll templatesThis palette on its own