@korinai/libs
v1.0.11
Published
Shared hooks, contexts, utils, and UI helpers for KorinAI apps
Readme
@korinai/libs
Shared hooks, contexts, utils, types, and small UI helpers used across KorinAI apps.
This package provides a thin client layer (SWR-based hooks and utility helpers) to interact with a KorinAI backend and wire up common app state via React contexts.
Installation
Peer dependencies (not installed automatically):
- react >= 18
- react-dom >= 18
- swr >= 2
- ai >= 5 (only for types used in
ChatMessage)
Install in your app:
pnpm add @korinai/libs
# or
npm i @korinai/libs
# or
yarn add @korinai/libsPrerequisites:
- Node.js 18+ (LTS recommended)
- A React 18+ app (Next.js is fine)
- Basic familiarity with SWR (we use it behind the scenes)
Quick start
Wrap your app with KorinAIProvider and then use the hooks you need.
{% raw %}
import { KorinAIProvider, useKorinAI, useGallery, useMessages } from "@korinai/libs";
export default function AppRoot({ children }: { children: React.ReactNode }) {
return (
<KorinAIProvider
config={{ baseUrl: "https://api.example.com" }}
authToken={"<JWT or session token>"}
language="en"
// Optionally provide async token retriever
// getAuthToken={async () => myTokenGetter()}
>
{children}
</KorinAIProvider>
);
}{% endraw %}
function GalleryExample() { const { config, authToken } = useKorinAI(); const { items, isLoading, isError } = useGallery({ page: 1, limit: 10 }); if (isLoading) return Loading…; if (isError) return Error loading gallery; return ( {items.map((it) => ( {it.caption} ))} ); }
Checklist (common gotchas):
- Provide a valid `config.baseUrl` (and `chatApi` if different).
- Pass `authToken` or `getAuthToken` to authenticate requests.
- Ensure your backend exposes the endpoints used by the hooks (see hook names for intent).
## Beginner roadmap (5 minutes)
1. Install and wrap your app with `KorinAIProvider`.
2. Set `config.baseUrl` (and optionally `config.chatApi`).
3. Provide an `authToken` or `getAuthToken()` that returns a valid token.
4. Try your first hook: `useGallery()` or `useRooms()` and render the data.
5. Add actions: upload with `useGalleryUpload()`, or send chat messages with your own API using the Vercel AI SDK.
## Concepts in 2 minutes
- `KorinAIProvider`: a React context that stores your API config, language, and auth token.
- Hooks like `useGallery`, `useRooms`, `useMessages`: SWR-based data hooks that call your backend.
- Utilities like `getFileName` or `getFileCategory`: small helpers you can call anywhere.
- UI helper `getFileIcon`: returns an SVG icon based on file type.
## Common recipes
### 1) Display a simple gallery grid
```tsx
import { useGallery } from "@korinai/libs";
export function GalleryGrid() {
const { items, isLoading, isError } = useGallery({ page: 1, limit: 12 });
if (isLoading) return <p>Loading…</p>;
if (isError) return <p>Failed to load</p>;
return (
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
{items.map((it) => (
<figure key={it.id} className="border rounded p-2">
<img src={it.file_url} alt={it.caption || "item"} />
<figcaption className="text-sm mt-1">{it.caption}</figcaption>
</figure>
))}
</div>
);
}2) Upload a file and refresh the gallery
import { useGallery, useGalleryUpload } from "@korinai/libs";
export function Uploader() {
const { mutate } = useGallery({ page: 1, limit: 12 });
const { uploadFile, isUploading, uploadProgress } = useGalleryUpload();
async function onSelect(file: File) {
const res = await uploadFile(file, false, []);
if (res.success) await mutate();
}
return (
<div>
<input type="file" onChange={(e) => e.target.files && onSelect(e.target.files[0]!)} />
{isUploading && (
<p>
{uploadProgress.status}: {Math.round(uploadProgress.progress)}%
</p>
)}
</div>
);
}3) Render a file icon for a URL
import { getFileIcon } from "@korinai/libs/ui/getFileIcon";
export function FileRow({ url }: { url: string }) {
return (
<div className="flex items-center gap-2">
{getFileIcon(url)}
<span className="text-sm">{url.split("/").pop()}</span>
</div>
);
}Auth & configuration (beginner-safe)
The minimum config you need is set on KorinAIProvider:
{% raw %}
<KorinAIProvider
config={{
baseUrl: "https://api.example.com", // your API base for gallery/rooms/etc.
chatApi: "/api/chat", // your chat streaming endpoint (optional here)
}}
// Either pass a token…
authToken={"<jwt-or-session>"}
// …or provide an async getter (recommended):
// getAuthToken={async () => fetch('/api/token').then(r => r.text())}
>
{children}
</KorinAIProvider>{% endraw %}
Tips:
- Use `getAuthToken` if your token changes or you need to refresh it.
- For chat UIs, see the minimal `chatApi` endpoint example in the Korin UI README.
---
## Exports overview
All exports are available from the package root `@korinai/libs`.
### Contexts
- `KorinAIProvider(props)` – React provider for global config/auth/language.
- `useKorinAI()` – Access KorinAI context values.
- `KORIN_TRANSLATIONS` – Base translations map.
- Types: `KorinAIConfig`, `AuthToken`, `KorinAIContextType`, `ChatTranslations`.
- `AgentProvider(props)` – Provides current agent state + list from API.
- `useAgent()` – Access selected agent and agent list.
- Type: `Agent`.
### Hooks
- `useAgents(page?: number, limit?: number, search?: string)`
- `useDebouncedValue<T>(value: T, delay: number): T`
- `useGallery(options?: { page?: number; limit?: number; sortBy?: "created_at"|"updated_at"|"file_url"|"caption"; sortOrder?: "asc"|"desc"; showAll?: boolean })`
- `useGalleryDetail(fileId: string | null)`
- `useGalleryUpload()`
- `useIsMobile(): boolean`
- `useMessages(roomId?: string, agentId?: string, agentName?: string)`
- `useRooms(page?: number, limit?: number, participantId?: string)`
- `useSingleRoom(roomId: string, enableSWR?: boolean)`
- `useUser()`
### Utils
- `buildUrl(baseUrl: string, path: string): string`
- `getFileName(path: string): string`
- `getFileCategory(url: string): "image" | "video" | "audio" | "document"`
- `getMimeType(fileUrl: string): string`
- `mimeTypes` – record of file extensions to mime type
- `getLanguageFromFilename(filename: string): string | undefined` (also the file's default export, re-exposed by name)
### UI helpers
- `getFileIcon(fileName: string): ReactElement` – returns an inline SVG element based on file extension
### Types
- `PromptTemplate`
- `FileAttachment`
- `MessageMetadata`
- `ChatMessage` – `UIMessage<MessageMetadata>` from `ai`
- `MimeTypes` – type of `mimeTypes` record
- `FileCategory`
- `Room` (from `useRooms`)
---
## API reference (concise)
### Contexts
#### `KorinAIProvider`
Props:
- `config: KorinAIConfig` – `{ baseUrl?: string; chatApi?: string; minimumCreditsWarning?: string; language?: string }`
- `authToken: string | undefined | null`
- `language?: string`
- `getAuthToken?: () => Promise<string | undefined | null>`
- `translations?: ChatTranslations`
#### `useKorinAI()`
Returns `{ config, setConfig, authToken, setAuthToken, getAuthToken, language, setLanguage, translations }`.
#### `AgentProvider`
Props:
- `initialAgentId?: string` (default: `"fin-advisor"`)
- `onAgentSwitch?: (agent: Agent) => void`
#### `useAgent()`
Returns `{ currentAgent, agents, switchAgent, isLoading, isError }`.
### Hooks
#### `useAgents(page?: number, limit?: number, search?: string)`
Returns `{ agents, currentPage, totalPages, totalItems, isLoading, isError, mutate }`.
Example:
```tsx
import { useAgents } from "@korinai/libs";
export function AgentsList() {
const { agents, isLoading, isError } = useAgents(1, 20, "designer");
if (isLoading) return <p>Loading agents…</p>;
if (isError) return <p>Failed to load agents</p>;
return (
<ul>
{agents.map((a) => (
<li key={a.id}>{a.name}</li>
))}
</ul>
);
}Loading and error states:
isLoading: boolean, true while request in-flight or initial SWR state.isError:Error | anyfrom SWR; treat as truthy if present.
useDebouncedValue<T>(value: T, delay: number): T
Returns a debounced value after delay ms.
useGallery(options?)
Options: { page?, limit?, sortBy?, sortOrder?, showAll? }.
Returns { items, total, page, totalPages, isLoading, isError, mutate }.
useGalleryDetail(fileId)
Returns { detail, isLoading, isError, mutate } for a single gallery item.
useGalleryUpload()
Returns { uploadFile(file: File, isPublic: boolean, accessEmails: string[], isKnowledge?: boolean): Promise<{ success: boolean; fileUrl?: string; galleryId?: string; caption?: string; error?: string }>, isUploading, uploadProgress }.
useIsMobile()
Heuristic combining viewport width and UA data; returns boolean.
useMessages(roomId?, agentId?, agentName?)
Returns { messages, isLoading, isError, mutate } from the messages endpoint.
Example:
import { useMessages } from "@korinai/libs";
export function MessagesView({ roomId }: { roomId: string }) {
const { messages, isLoading, isError } = useMessages(roomId);
if (isLoading) return <p>Loading messages…</p>;
if (isError) return <p>Failed to load messages</p>;
return (
<div>
{messages.map((m) => (
<div key={m.id}>{m.content}</div>
))}
</div>
);
}Loading and error states:
isLoading: boolean, true while fetching.isError:Error | any.
useRooms(page?, limit?, participantId?)
Exports type Room. Returns { rooms, currentPage, totalPages, totalItems, isLoading, isError, mutate }.
Example:
import { useRooms } from "@korinai/libs";
export function RoomsGrid() {
const { rooms, isLoading, isError } = useRooms(1, 12);
if (isLoading) return <p>Loading rooms…</p>;
if (isError) return <p>Failed to load rooms</p>;
return (
<ul>
{rooms.map((r) => (
<li key={r.id}>{r.name}</li>
))}
</ul>
);
}Loading and error states:
isLoading: boolean, true until initial list is loaded.isError:Error | any.
useSingleRoom(roomId, enableSWR = true)
Fetch one room by id. Returns { room, isLoading, isError, mutate }.
useUser()
Returns { user, isLoading, isError, mutate }.
Utils
See the list in Exports; each function is a thin, tree-shakeable helper without side effects.
UI helpers
getFileIcon(fileName) returns a sized inline SVG for common file types.
Module format and bundling
This package ships ESM and CJS builds with type declarations:
- ESM:
dist/index.mjs - CJS:
dist/index.cjs - Types:
dist/index.d.ts
It also supports subpath imports for deeper paths, e.g. @korinai/libs/hooks/useGallery.
Tree-shaking is supported; all modules are side-effect free ("sideEffects": false).
Subpath imports
You can import specific modules directly for optimal tree-shaking:
// Hooks
import { useGallery } from "@korinai/libs/hooks/useGallery";
import { useAgents } from "@korinai/libs/hooks/useAgents";
import { useRooms } from "@korinai/libs/hooks/useRooms";
// Contexts
import { KorinAIProvider } from "@korinai/libs/contexts/korinai-context";
// Utils and UI
import { buildUrl } from "@korinai/libs/libs/build-url";
import { getFileIcon } from "@korinai/libs/ui/getFileIcon";Type definitions location
To keep this README concise (no duplicated schemas), refer to the source files for exact field shapes:
PromptTemplate,FileAttachment,MessageMetadata,ChatMessage–packages/korin-libs/types/index.tsRoom– exported frompackages/korin-libs/hooks/useRooms.tsMimeTypes– inpackages/korin-libs/libs/mimeTypes.ts- File category types –
packages/korin-libs/libs/fileCategories.ts
Troubleshooting
- Missing or invalid token: Ensure you pass
authTokenor implementgetAuthToken. Inspect network requests in your browser devtools; a 401 usually means your token is absent/expired. - Wrong
baseUrlorchatApi: Double-checkconfig.baseUrlandconfig.chatApi. Try opening the URL directly in the browser to verify it responds. - CORS errors: Configure your backend to allow your app’s origin. In development, enable permissive CORS or use a proxy.
- SWR not updating: Call
mutate()from the relevant hook after an action (e.g., upload) to revalidate data. - Type errors: Make sure your project uses a compatible TypeScript version and the
@types/reactthat matches React 18.
FAQ
- Do I need Next.js? No. Any React 18 app works. Examples use Next.js because it’s common.
- Can I secure my APIs? Yes. Use
authToken/getAuthTokenand validate theAuthorizationheader server-side. - Can I use my own UI? Yes.
@korinai/libsis headless. Pair withkorin-uifor ready-made components, or build your own. - How do I paginate rooms/gallery? Use the
pageandlimitparameters onuseRooms/useGalleryand render load-more or infinite scroll. - How do I change language? Pass
languageand optionaltranslationstoKorinAIProvider. - Subpath imports? Yes. Import only what you use, e.g.,
@korinai/libs/hooks/useGallery. - File size/type limits for uploads? Validate before calling
uploadFile. UsemimeTypesandgetFileCategoryhelpers.
Contributing
- Keep APIs small and composable.
- Prefer named exports.
- Add minimal JSDoc/TSdoc above public functions.
- Update this README when adding or changing exports.
License
ISC © KorinAI
