What your AI assistant reads before it answers: files, websites and connected apps, each indexing live with its chunks counting up, a source opened with its chunks, and a retrieval test that shows what a question would find. Drop files on it to add them. On motion.
Installation
When to use
- The screen where a team manages what its AI assistant knows: files, websites and connected apps, and how far each is indexed.
- Products with retrieval (RAG), where people add sources, fix one that failed and check what a question would find.
When not to use
- Plain file storage with nothing indexed: a file list or Data Table fits better.
- One upload in a form: File Dropzone on its own.
Usage
Knowledge sources with your own data: every prop has the sample as its default, so pass only what you have.
"use client"
import { useRef, useState } from "react"
import { KnowledgeSources, type KnowledgeSource, type KnowledgeSourcesHandle } from "@/components/dashral/blocks/knowledge-sources"
export default function Knowledge() {
const knowledge = useRef<KnowledgeSourcesHandle>(null)
// Kept in state: a new array replaces what's shown.
const [sources] = useState<KnowledgeSource[]>([
{ id: "doc-1", name: "Refund policy.pdf", kind: "file", format: "pdf", size: 412_000, chunks: 38, tokens: 21_400, status: "ready", syncedAt: "2 hours ago", enabled: true },
{ id: "web-1", name: "docs.acme.com", kind: "website", url: "docs.acme.com", status: "indexing", progress: 0.4, chunks: 120, enabled: true },
])
// As your indexer reports: knowledge.current?.update("web-1", { progress: 0.8, chunks: 240 })
return (
<KnowledgeSources
ref={knowledge}
sources={sources}
// Return the rows to show (with your ids); while the promise is pending the button shows it.
onAdd={async (input) => console.log("add", input.files?.length ?? 0, "files", input.url ?? "", input.app ?? "")}
onResync={async (source) => console.log("sync", source.id)}
onToggle={async (source, enabled) => console.log(source.id, enabled ? "in use" : "left out")}
onRemove={async (source) => console.log("remove", source.id)}
/>
)
}API reference
KnowledgeSources
What an AI assistant knows: its sources (files, websites, connected apps), each with its indexing status and progress, the totals in chunks and tokens, a source opened with its chunks, and a retrieval test that shows what a question would find. Drop files on the list to add them. Every change calls your handler first.
| Prop | Type | Default |
|---|---|---|
title | React.ReactNode | "Knowledge" |
sources | KnowledgeSource[]The sources at first. A new array replaces what's shown (and what was added here), so keep yours in state. | sample |
onAdd | (input: AddSourceInput) => Promise<KnowledgeSource[] | void> | KnowledgeSource[] | voidFiles dropped or picked, a website, or an app to connect. Return the sources to show (their ids are yours); left out, or returning nothing, a queued row is added for each file or the website. While the promise is pending the menu's button shows it; a thrown Error's message shows above the list. | — |
onRemove | (source: KnowledgeSource) => Promise<void> | voidRemoved after its confirm: the dialog waits for the promise; a throw keeps it and says why. | — |
onResync | (source: KnowledgeSource) => Promise<void> | voidSync again (or retry a failed one): it goes back to Queued at once, and back as it was if this throws. | — |
onToggle | (source: KnowledgeSource, enabled: boolean) => Promise<void> | voidUse it in answers or not: shown at once, put back if this throws. | — |
onRetrieve | (query: string) => Promise<RetrievalMatch[]> | RetrievalMatch[]The retrieval test, with your own search; left out, a keyword match over the sources' preview chunks. | — |
ref | React.Ref<KnowledgeSourcesHandle> | — |
className | string | — |
SourceDetail
One source opened: what it is, its status (progress, the error), whether answers use it, its chunks, and its actions.
| Prop | Type | Default |
|---|---|---|
source | KnowledgeSourceRequired. | — |
onResync | () => voidRequired. | — |
onToggle | (enabled: boolean) => voidRequired. | — |
onRemove | () => Promise<void> | voidRequired. | — |
RetrievalTest
A question, and the chunks it would find, best first with how close each is; one picked opens its source.
| Prop | Type | Default |
|---|---|---|
sources | KnowledgeSource[]Required. | — |
onRetrieve | KnowledgeSourcesProps["onRetrieve"] | — |
onPick | (sourceId: string) => void | — |
type SourceKind
= "file" | "website" | "app"type SourceStatus
= "queued" | "uploading" | "indexing" | "ready" | "failed"type SourceApp
A connected app, by the brand catalog's role: docs (Notion), files (Dropbox), code (GitHub), support (Intercom).
= "docs" | "files" | "code" | "support"type SourceChunk
| Prop | Type | Default |
|---|---|---|
id | string | — |
text | string | — |
tokens | number | — |
page | number | — |
type KnowledgeSource
| Prop | Type | Default |
|---|---|---|
id | string | — |
name | string | — |
kind | SourceKind | — |
format | stringA file's type, for its icon: "pdf", "docx", "csv", "md"… | — |
app | SourceApp | — |
url | string | — |
size | numberIn bytes. | — |
pages | number | — |
chunks | number | — |
tokens | number | — |
status | SourceStatus | — |
progress | number0–1 while uploading or indexing. | — |
error | stringWhy it failed. | — |
syncedAt | stringWhen it was last synced, as shown: "2 hours ago". | — |
enabled | booleanWhether the assistant uses it in its answers. | — |
preview | SourceChunk[]A few of its chunks, for the preview and the retrieval test. | — |
type RetrievalMatch
| Prop | Type | Default |
|---|---|---|
sourceId | string | — |
chunk | SourceChunk | — |
score | number | — |
type AddSourceInput
| Prop | Type | Default |
|---|---|---|
files | File[] | — |
url | string | — |
app | SourceApp | — |
type KnowledgeSourcesHandle
| Prop | Type | Default |
|---|---|---|
update | (id: string, patch: Partial<KnowledgeSource>) => voidChange a source as your indexer reports (status, progress, chunks, error…). | — |
add | (source: KnowledgeSource) => voidA source added elsewhere: it grows in at the top. | — |