npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@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/libs

Prerequisites:

  • 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 | any from 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, ChatMessagepackages/korin-libs/types/index.ts
  • Room – exported from packages/korin-libs/hooks/useRooms.ts
  • MimeTypes – in packages/korin-libs/libs/mimeTypes.ts
  • File category types – packages/korin-libs/libs/fileCategories.ts

Troubleshooting

  • Missing or invalid token: Ensure you pass authToken or implement getAuthToken. Inspect network requests in your browser devtools; a 401 usually means your token is absent/expired.
  • Wrong baseUrl or chatApi: Double-check config.baseUrl and config.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/react that 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/getAuthToken and validate the Authorization header server-side.
  • Can I use my own UI? Yes. @korinai/libs is headless. Pair with korin-ui for ready-made components, or build your own.
  • How do I paginate rooms/gallery? Use the page and limit parameters on useRooms/useGallery and render load-more or infinite scroll.
  • How do I change language? Pass language and optional translations to KorinAIProvider.
  • Subpath imports? Yes. Import only what you use, e.g., @korinai/libs/hooks/useGallery.
  • File size/type limits for uploads? Validate before calling uploadFile. Use mimeTypes and getFileCategory helpers.

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