@arbidocs/react
v0.4.93
Published
React hooks and provider for the ARBI SDK
Downloads
10,112
Readme
@arbidocs/react
React hooks and provider for building ARBI-powered apps. Includes @arbidocs/sdk and @tanstack/react-query — one install gets you everything.
Quickstart
npm install @arbidocs/react1. Set up providers
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ArbiProvider } from '@arbidocs/react'
const queryClient = new QueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<ArbiProvider url="https://www.arbidocs.com">
<Chat />
</ArbiProvider>
</QueryClientProvider>
)
}2. Build a streaming chat
import { useState, useRef } from 'react'
import { useArbi, useDocuments, useAssistantQuery } from '@arbidocs/react'
function Chat() {
const arbi = useArbi()
const [workspaceId, setWorkspaceId] = useState<string>()
if (!workspaceId) {
return (
<button onClick={async () => {
await arbi.login('[email protected]', 'password')
const ws = await arbi.workspaces.list()
await arbi.selectWorkspace(ws[0].external_id)
setWorkspaceId(ws[0].external_id)
}}>
Connect
</button>
)
}
return <ChatWindow workspaceId={workspaceId} />
}
function ChatWindow({ workspaceId }: { workspaceId: string }) {
const { data: docs } = useDocuments(workspaceId)
const mutation = useAssistantQuery()
const [input, setInput] = useState('')
const [response, setResponse] = useState('')
const lastId = useRef<string | null>(null)
return (
<div>
<div>{response}</div>
<form onSubmit={(e) => {
e.preventDefault()
setResponse('')
mutation.mutate({
question: input,
docIds: docs?.map((d) => d.external_id) ?? [],
previousResponseId: lastId.current,
onToken: (t) => setResponse((r) => r + t),
onStreamStart: ({ assistant_message_ext_id }) => {
lastId.current = assistant_message_ext_id
},
})
setInput('')
}}>
<input value={input} onChange={(e) => setInput(e.target.value)} />
<button type="submit" disabled={mutation.isPending}>Send</button>
</form>
</div>
)
}That's it — three hooks and you have a streaming RAG chat.
Hooks
| Hook | Returns | Auto-enabled when |
|------|---------|-------------------|
| useArbi() | Arbi instance | Always |
| useWorkspaces() | User's workspaces | Logged in |
| useArbiShellWorkspaces(options) | Workspaces as shell/gallery view models, the active one, open(workspace) (select, then onOpened) | Logged in |
| useShellWorkspaces(options) | Same, over a list and select you supply (no provider needed) | Always |
| useDocuments(workspaceId) | Workspace documents | Workspace selected |
| useConversations(workspaceId) | Workspace conversations | Workspace selected |
| useTags(workspaceId) | Workspace tags | Workspace selected |
| useTags(workspaceId, { kind, parentId, ids, include: ['links'], linksLimit }) | Tags filtered, with each one's documents, facts and threads (a table's rows) | Workspace selected |
| useTagKindSchema(kind) / useUpdateTagKindSchema() | A kind's fields in the workspace (a table's columns); set them | Workspace selected |
| useSetTagValues() | Merge-patch a tag's values for its kind's fields | Call .mutate() |
| useAssistantQuery() | Streaming mutation | Call .mutate() |
| useHealth() | Backend status | Pass { enabled: true } |
| useProfile() / useUpdateProfile() | The user's name, picture and email; save name or picture | Logged in |
| useRequestEmailChange() / useConfirmEmailChange() | Email change by verification code | Call .mutate() |
| usePasswordChange() | Change the master password (one request: the password only wraps the account key) | Call .mutate() |
| useNotificationPreferences(keys) | Email notification switches, optimistic save | Logged in |
| useSessions() / useTerminateSession() / useLogoutAllSessions() | Active sessions, end one, sign out everywhere | Logged in |
| useDeleteAccount() | Close the account (soft delete) | Call .mutate() |
The account hooks have SDK-free controllers for hosts without an ArbiProvider (useNameEditor,
useEmailChange, usePasswordChangeForm, useDeleteAccountFlow, useAvatarCrop,
toNotificationPreferences): they own the form state and take the save as a function. The blocks
/connect entry composes the lot into a settings page (see the Settings page recipe in
docs/design/blocks-package.md).
Artifact editing (@arbidocs/react/artifacts)
useArtifactSession({ workspaceId, docId }) loads an artifact, holds the edit buffer, saves it as a
NEW immutable version (a child document via parent_ext_id), lists the version lineage and compares
any two versions with track changes. It returns props for the @arbidocs/blocks/artifacts parts:
view (for ArtifactEditor), editorProps and redlineProps. The pieces underneath are
SDK-free and usable on their own: useArtifactEditSession (buffer, baseline, dirty, save,
compare), useRedline (diff with a pluggable, worker-capable RedlineEngine), buildVersionChain
and versionLabel. useArtifactText, useArtifactVersions and useSaveArtifactVersion are the
SDK bindings.
useArtifactText(docId) returns clean markdown and a status (loading, parsing, ready,
error): a fresh upload is parsed for a few seconds, during which the backend answers 404, 422 or
500, so it retries with a growing delay (bounded) and unescapes the backend's escaped markdown
(unescapeMarkdown; only for artifacts stored from markdown). useStreamedArtifact({ artifacts,
workspaceId, settled }) covers the stream, persist, edit life of an agent draft (phase:
streaming, persisting, parsing, ready, error), cleaning the draft with
cleanDraftMarkdown (unwrapCitationLinks, stripAgentPreamble, promoteBoldTitle) before it is
stored.
PDF document tools (@arbidocs/react/document-tools)
The data side of annotation, markup, freeze, e-signing, signature checking and production sets:
DocumentToolsProvider (the host's API client, user and callbacks; deploymentDomain defaults to
the enclosing ArbiProvider's, useDeploymentDomain()), fetchDocumentPdf (a version's /view
PDF), useDocumentMarkup, createMarkupStore (an AnnotationStore over the server markup, with
409 merge), fetchMarkupPdf, useDocumentSignatures, useSignDocument,
useSignatureVerification and buildProduction / saveProductionFiles. The UI is @arbidocs/blocks/document-tools; see
"Using the document tools in another frontend" in the @arbidocs/blocks README.
Streaming callbacks
Pass these to mutation.mutate() alongside question and docIds:
| Callback | Description |
|----------|-------------|
| onToken(token) | Each text chunk as it streams |
| onStreamStart({ assistant_message_ext_id }) | Stream started — save the ID for follow-up queries |
| onAgentStep(step) | Agent tool usage (searching, reading, etc.) |
| onError(message) | Error during streaming |
| onComplete() | Stream finished |
Cache invalidation
import { arbiQueryKeys } from '@arbidocs/react'
import { useQueryClient } from '@tanstack/react-query'
const queryClient = useQueryClient()
queryClient.invalidateQueries({ queryKey: arbiQueryKeys.documents(workspaceId) })Peer dependencies
react>= 18
Tasks, agents, skills, contacts and credits
Hooks for an "AI workforce" and a messages screen. Writes invalidate through arbiQueryKeys, so
useTasks, useAgents, useConfigs and useDirectMessages refresh by themselves.
const create = useCreateTask() // mutateAsync(CreateTaskRequest) -> TaskResponse
const run = useRunTask() // approves a run parked for access (temporary grant), then runs
const runs = useTaskRuns(taskId) // polls while a run is in flight
const save = useSaveAgentConfig() // { agentExtId, sections: { Agents: { PERSONA, ... } } }
const grant = useGrantAgentAccess() // { agentExtId, workspaceId, mode?: { kind: 'standing' | 'temporary' } }
const contacts = usePendingContacts() // + useAddContacts(), useRemoveContacts()Form-to-request mappings (taskComposerToCreateRequest, composePersona, ...) are in
@arbidocs/blocks/agents. Usage is reported in credits, not dollars: use
formatCredits(n) (fractional: 0.47 credits, 1.5 credits; whole from 100 up), creditsToUsd(n) and the metered flag on useProjectUsage /
useProjectsUsage (false = this deployment does not record usage). DirectMessages takes
listHeader, listLead, startByEmail and hideOwnContact. See docs/design/blocks-package.md
("Hooks and mappings a host should not rewrite").
