Pro

Knowledge base

Pro block

The screen where a team gives its AI agent documents to answer from: upload files, add a site or sitemap, or connect Google Drive, Notion or Dropbox; watch each source move through reading, chunking and indexing with counts that never shift; retry failures, re-sync, remove, see usage against the plan, and test which passages a question retrieves.

Knowledge base

What Copper answers guests and hosts from. Copper only uses what’s here, and says so when a question isn’t covered.

Upload files

Add a website

Help pages, a FAQ, your menus online.

Connect an app

  • Google DriveConnected as mara@saturdaysupper.co
  • NotionConnected as Acme HQ
  • DropboxConnected as Saturday Supper Club

Sources

10 ready · 848 chunks to search

All 10In progress 0Failed 0
  • Autumn tasting menu.pdfReady
    6 pages38 chunks9,420 tokensSynced 2 hours ago
  • Allergen matrix, autumn.pdfReady
    4 pages52 chunks11.9k tokensSynced 2 hours ago
  • Venue FAQReady
    Acme HQ › Guests › Venue FAQ, and 9 sub-pages12 pages64 chunks15.2k tokensSynced 42 minutes ago
  • acme.co/helpReady
    Every page in acme.co/sitemap.xml38 pages211 chunks48.9k tokensSynced yesterday
  • Supplier list.csvReady
    1 page26 chunks4,120 tokensSynced 3 days ago
  • Granary Hall: access, parking and step-free entrance.docxReady
    3 pages14 chunks3,380 tokensSynced 5 days ago
  • MenusReady
    Shared drive › Copper › Menus, 14 files41 pages260 chunks61.4k tokensSynced 18 minutes ago
  • Copper house style.mdReady
    2 pages9 chunks2,110 tokensSynced on Oct 25
  • Wine pairings 2026.pdfReady
    8 pages44 chunks10.3k tokensSynced on Oct 22
  • Event contractsReady
    Acme › Contracts23 pages130 chunks31.2k tokensSynced 7 hours ago

Uploads, crawls and searches take about a second, like a real backend. Drag files from your computer onto the list, or use “Drop 3 sample files” to see a duplicate and a type it can’t read. Tick “Fail the next request” to see how each action recovers.

Installation

This is a Pro block. UI Polish Pro costs $149 one time and includes every Pro component, block and template, now and later. Add your license key to your MCP connection or components.json, then install it either way below.

With UI Polish connected, ask your agent:

Add the UI Polish knowledge base to this project.

Or with the shadcn CLI:

npx shadcn@latest add @uipolish/knowledge-base
Package@base-ui/react
Brings inAlert, Batch progress, Brand marks, Button, Checkbox, Dialog, Dropdown menu, Empty state, Icons, Search field, Segmented control, Text, Text field

The screen where a team gives its AI agent something to answer from: the documents, sites and app folders behind retrieval (RAG). There are three ways in: upload files by drop or browse, add a page or sitemap, or connect Google Drive, Notion or Dropbox. Each source then moves through reading → chunking → indexing → ready, or failed with a reason and Retry. A list of 40 files shows on one batch sheet, and a test panel shows which passages a question retrieves.

Over six sources, the filters (a segmented control) and the search share a row at the field's height, 40px (--ui-control-md): controls in one row share one height.

A source in progress is the accent, ready is green and failed red, and that color runs through the row: the pill, the turning glyph, and a line along the row's foot that travels as the source moves forward. Pages, chunks and tokens tick up in boxes that hold their width, so nothing beside them moves.

TSX
<KnowledgeBase  agentName="Copper"  sources={sources}            // [{ id, name, kind, status, progress?, error?, pages?, chunks?, tokens?, syncedAt? }]  usage={{ used: 370, limit: 500, unit: "pages" }}  batch={batch}                // { sourceIds, startedAt, finishedAt? } while a set of files is read  connected={{ googledrive: "mara@saturdaysupper.co" }}  onAddFiles={(files) => api.upload(files).then(refresh)}  onReplaceFile={(id, file) => api.replace(id, file).then(refresh)}  onAddWebsite={(url, { crawl }) => api.post("/sources/web", { url, crawl }).then(refresh)}  onConnect={(service) => oauth.open(service).then(picker.choose).then(refresh)}  onResync={(id) => api.post(`/sources/${id}/sync`).then(refresh)}  onRetry={(ids) => api.post("/sources/retry", { ids }).then(refresh)}  onRemove={(id) => api.delete(`/sources/${id}`).then(refresh)}  onTest={(question) => api.post("/retrieve", { question, k: 4 })}  testSuggestions={["Is the tasting menu nut-free?", "Where can guests park?"]}  onUpgrade={() => router.push("/settings/billing")}/>

When to use it

  • An agent or assistant that answers only from material the customer gives it, and the people who set it up need to see what it knows.
  • Ingest that takes seconds to minutes per source, where people watch it happen and need to know what failed and why.
  • A plan limit on pages, chunks or tokens.

When not to

  • Files attached to one message or task. That's prompt-input with attachments, or file-upload in a form.
  • Managing the agent's tools and servers: connections.
  • Search analytics, evaluation runs or chunking settings. Link to them; this screen answers "what does it know, and does it find the right thing".

States

StateWhat shows
First run (sources is [])"Give Copper something to answer from", one line, and the three ways in as cards: a drop zone, a website form, and the apps. No list, no test panel
ReadySources with their counts and "Synced 2 hours ago", the plan meter, the test panel. Over 6 sources: filters (All, In progress, Failed) and a search
A source queuedGrey pill "Queued", "Waiting its turn"
reading / chunking / indexingThe pill in the accent, naming the stage, with a turning arc; the line along the row's foot travels through the three stages (progress moves it within one); counts tick up in held boxes
readyGreen pill with a check that lands with a small snap; the line fills and fades
failedRed pill, and under the name, the error in a tinted box with Retry
Many files at once (batch)BatchProgress above the list: "Reading 40 files", 14 of 40, time left, current file, Stop (onStopBatch)
Some failed (2 of 40)The sheet reads "Added 38 of 40 files", lists the two with their reasons and Retry 2; the rows say the same, and the Failed filter shows 2
Unsupported typeWarning above the list: "Seating plan.pages wasn't added. .pages files can't be read. Save it as PDF, DOCX, Markdown, or CSV and add it again."
Duplicate fileSame notice: "Already here, synced 2 hours ago. Replace it if this copy is newer." with Replace (onReplaceFile)
Too large / empty"Too large at 64 MB. Up to 50 MB: split it, or export just the pages that matter." / "This file is empty."
Near the limit (80%)Amber meter, "62 pages left. A long PDF may not fit."
Over the limitA warning banner "Copper's knowledge base is full" with Upgrade plan; the meter is red; the add panel says what to remove; drops are refused. Sources already here keep working
Viewer (readOnly)Info banner with your reason; no Add, Retry, Re-sync or Remove. Testing still works
Upload pending / failedThe drop zone shimmers "Uploading 3 files…" after 300ms; a failure shows an error notice with Try again for the same files
Test: idle / searching / results / nothing / failedA line on what it shows / "Searching 10 sources…" in the reserved space / the answer and the passages / "Nothing matched … Copper would say it doesn't know" / the error with Try again
Test, nothing ready yet"Testing opens once a source is ready."
Loading / didn't load"Loading sources…" in a card the list's height / an empty state with Try again (onReload)
Long namesWrap; nothing is cut. Passages show in full

Props

PropWhat it does
sources{ id, name, kind, location?, href?, status, progress?, error?, pages?, chunks?, tokens?, syncedAt? }[]. kind is "file", "website", "googledrive", "notion" or "dropbox". status is "queued", "reading", "chunking", "indexing", "ready" or "failed"
usage{ used, limit, unit? }. Warns at 80%, blocks adding at 100%
batch{ sourceIds, startedAt?, finishedAt? }: files added together, for the sheet. Clear it when you like; keep it while some failed
services, connectedApps offered (default all three) and the ones already connected, with the account to show
accept, acceptLabel, maxFileSizeDefault ".pdf,.docx,.md,.csv", "PDF, DOCX, Markdown or CSV", 50 MB
onAddFiles(files)Only files that passed type, size and duplicate checks. Resolve once they're in sources
onReplaceFile(sourceId, file)From a duplicate's Replace. Leave out and duplicates are only skipped
onAddWebsite(url, { crawl })url normalized to https. A sitemap*.xml always crawls
onConnect(service)Open sign-in and the folder picker. Also "Add a folder" once connected
onResync(id), onRetry(ids), onRemove(id)Row actions. Remove asks first
onStopBatch()Shows Stop on the sheet
onTest(question)Resolve { answer?, chunks: [{ id, sourceId, location?, text, highlights?, score }] }, best first. highlights are [start, end) ranges in text
testSuggestionsQuestions to try, one tap each
onUpgrade()Shows Upgrade plan when full
readOnlyA sentence: why, and who can change it
loading, error, onReloadWhole-screen loading and failure
agentNameFor sentences. Default "Your agent"
now, localeFor "Synced 2 hours ago"; pass now from the server so both render alike
pageSizeRows before "Show n more". Default 12
title, description, headingLevelDefault "Knowledge base", none, 1

Every callback can return a promise: its control shows busy until it settles, and a rejection's message shows where it was pressed. The block never changes data itself; resolve once sources reflects the change.

Customizing

  • Stages. Use the four your pipeline has, mapped onto reading, chunking and indexing; send progress within a stage if you know it, so the line moves smoothly. The stage colors are --kb-reading, --kb-chunking and --kb-indexing on .root, all the accent by default.
  • Errors. Write error as what happened and what fixes it: "Password protected. Upload a copy without the password.", not "Parse error".
  • The limit. Set usage.unit to what you bill: pages, chunks or tokens.
  • File types. Change accept and acceptLabel together; the rejection says to save as acceptLabel.
  • Test results. answer is optional. Showing only the passages is often more honest while people tune their sources.
  • Widths. The test panel sits beside the list once the block is 900px wide; website and apps sit side by side at 620px. Both are container queries, so the block lays out right in a narrow settings column.
  • Spacing. The title sits --ui-gap-block above the content, and the sources and the test panel sit --ui-gap-block apart (16 narrow, 32 from 760px).

Layout

On a phone (the screen under 420px) a source's status pill wraps under its name and lines up with it, and the filter's labels stay on one line, scrolling inside the track if the screen is narrower still.

Accessibility

  • One heading at headingLevel; Sources and Test it one below, each add card one below that.
  • Add sources is a disclosure button (aria-expanded) for the panel; the plus turns into a close as it opens. The closed panel is inert.
  • The drop zone is a real button that opens the file picker; its prompt and constraints are its name and description. Dropping is never the only way.
  • Each row's status is text ("Indexing"), not color alone; failed rows describe the pill with their error. Row menus are named "Actions for Autumn tasting menu.pdf".
  • Remove is an alert dialog naming the source and what stops, with focus on Cancel; afterwards focus goes to the Sources heading.
  • A polite status region announces what lands: "3 files added. Copper starts reading them now.", "2 files weren't added…", "4 passages from 3 sources. Best match first." The batch sheet announces its own milestones, not every file.
  • The usage bar is role="meter" with "370 of 500 pages used".
  • Passages are an ordered list; matched words are <mark>.
  • Reduced motion: the arc stops turning and breathes, the line jumps to its place, and rows fade instead of opening.
  • Buttons are the standard 40px; only a source's “…” menu is small, and it reaches 40px under a finger.

Before / after

Before: an upload input, a table of file names with a "processing" string, and nothing when a file fails. People find out from a wrong answer.

TSX
<input type="file" multiple onChange={upload} /><table>{docs.map((d) => <tr key={d.id}><td>{d.name}</td><td>{d.status}</td></tr>)}</table>

After: three ways in, each source's stage in words and color, failures that say what to do with Retry, a plan meter, and a test panel that shows what a question retrieves.

TSX
<KnowledgeBase  agentName="Copper"  sources={sources}  usage={usage}  batch={batch}  onAddFiles={upload}  onAddWebsite={addSite}  onConnect={connect}  onRetry={retry}  onRemove={remove}  onTest={retrieve}/>