@directivsys/react-sdk
v0.1.10
Published
React bindings and UI components for DirectivSys built on top of @directivsys/core-sdk.
Maintainers
Readme
@directivsys/react-sdk
React SDK for DirectivSys — the AI-powered directive and analytics platform. This package provides the React context layer, hooks, pre-built UI components, theming system, analytics dashboards, and reporting components needed to embed DirectivSys into any React application.
Version: 0.1.6 | React: ≥ 18 | TypeScript: ≥ 5.0
Table of Contents
- How it fits together
- Prerequisites
- Installation
- Quick Start
- Provider Setup
- Core Hook —
useDirectivSys - Components
- Analytics
- Reports
- Theming
- TypeScript Types
- Context & Session Handling
- Voice Input / Output
- Custom Storage Adapter
- Architecture Overview
How it fits together
This package is the React adapter layer over @directivsys/core-sdk. The split keeps all transport, orchestration, and session logic in the framework-agnostic core while exposing an idiomatic React API here.
| Concern | Package |
|---|---|
| Transport clients, conversation controller, analytics/report contracts | @directivsys/core-sdk |
| React provider, hooks, UI components, themes | @directivsys/react-sdk |
All types exported by the core SDK are re-exported from this package, so you only need to import from @directivsys/react-sdk in your application code.
Prerequisites
- React 18 or later
- A DirectivSys platform API key (obtain from your DirectivSys account dashboard)
Installation
npm install @directivsys/react-sdk
# or
yarn add @directivsys/react-sdk
# or
pnpm add @directivsys/react-sdk@directivsys/core-sdk is installed automatically as a peer dependency.
Quick Start
The minimal integration requires three steps: wrap your app in DirectivSysProvider, call useDirectivSys in a component, and handle tool calls from the AI via onIntentDetected.
// 1. Wrap your app
import { DirectivSysProvider } from "@directivsys/react-sdk";
export function App() {
return (
<DirectivSysProvider apiKey={process.env.DIRECTIVSYS_API_KEY!}>
<MyApp />
</DirectivSysProvider>
);
}
// 2. Use the hook in any child component
import { DirectivSysChatbox } from "@directivsys/react-sdk";
import type { ToolCall, ToolResult } from "@directivsys/react-sdk";
export function MyApp() {
const handleAction = async (toolCall: ToolCall): Promise<ToolResult> => {
// Execute the AI-requested action in your application and return a result.
// The summary is shown to the user; detailed_data is available to the AI.
return {
status: "success",
summary: `Handled: ${toolCall.toolName}`,
detailed_data: {},
};
};
return (
<DirectivSysChatbox
onIntentDetected={handleAction}
currentContext={{ userId: "user-123" }}
renderMode="standard" // floating chat widget
/>
);
}Provider Setup
DirectivSysProvider must wrap any component that uses SDK hooks or components. Place it as high as possible in your component tree (e.g., alongside other global providers).
import { DirectivSysProvider, darkTheme } from "@directivsys/react-sdk";
export function AppProviders({ children }: { children: React.ReactNode }) {
return (
<DirectivSysProvider
apiKey={process.env.NEXT_PUBLIC_DIRECTIVSYS_API_KEY!}
theme={darkTheme}
config={{ timeout: 30_000 }}
>
{children}
</DirectivSysProvider>
);
}DirectivSysProviderProps
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| apiKey | string | ✅ | — | Your DirectivSys platform API key. |
| config | Partial<DirectivSysConfig> | | {} | Transport-level overrides. See below. |
| theme | Theme | | lightTheme | Active theme applied to all SDK components. |
| sttProvider | STTProvider | | WebSpeechSTTProvider | Speech-to-text provider. |
| ttsProvider | TTSProvider | | WebSpeechTTSProvider | Text-to-speech provider. |
| children | ReactNode | ✅ | — | |
DirectivSysConfig options
| Field | Type | Default | Description |
|---|---|---|---|
| baseURL | string | SDK default | Override the DirectivSys API base URL (e.g., for self-hosted deployments). |
| timeout | number | 30000 | Request timeout in milliseconds. |
| headers | Record<string, string> | {} | Additional HTTP headers added to every API request. |
Core Hook — useDirectivSys
useDirectivSys is the primary hook for building custom chat or assistant UI. It manages conversation state and exposes actions.
import { useDirectivSys } from "@directivsys/react-sdk";
import type { ToolCall, ToolResult, CurrentContext } from "@directivsys/react-sdk";
export function AssistantPanel() {
const context: CurrentContext = {
userId: "user-123",
userName: "Jane Smith",
currentContext: {
currentPageName: "Inventory Dashboard",
currentPageUrl: "/inventory",
viewingItems: [{ id: "item-1", name: "Widget A" }],
},
};
const {
messages,
isLoading,
error,
sendChatQuery,
clearMessages,
isListening,
startVoiceInput,
stopVoiceInput,
hasMoreMessages,
isLoadingHistory,
loadMoreMessages,
} = useDirectivSys({
currentContext: context,
onIntentDetected: async (toolCall: ToolCall): Promise<ToolResult> => {
// Dispatch the tool call to your application logic
const result = await myApp.execute(toolCall.toolName, toolCall.parameters);
return { status: "success", summary: result.message, detailed_data: result.data };
},
onError: (err) => console.error("SDK error:", err),
onTranscript: (text) => console.log("Voice transcript:", text),
onAgentResponse: (response) => console.log("Agent said:", response),
});
return (
<div>
{hasMoreMessages && (
<button onClick={() => void loadMoreMessages()} disabled={isLoadingHistory}>
Load earlier messages
</button>
)}
{messages.map((msg) => (
<div key={msg.id} className={msg.role === "user" ? "user-msg" : "ai-msg"}>
{msg.content}
</div>
))}
{isLoading && <span>Thinking…</span>}
{error && <span className="error">{error.message}</span>}
<button onClick={() => void sendChatQuery("Show me today's summary")}>
Ask
</button>
</div>
);
}UseDirectivSysOptions
| Option | Type | Required | Description |
|---|---|---|---|
| currentContext | CurrentContext | ✅ | User and page context sent with every message. Changing this value causes the controller to propagate the update before the next request. |
| onIntentDetected | OnIntentDetected | ✅ | Called whenever the AI requests an action (tool call). Must return a ToolResult. |
| onError | (error: Error) => void | | Called on transport or orchestration errors. |
| onSpeechStart | () => void | | Fired when the microphone opens. |
| onSpeechEnd | () => void | | Fired when voice recording stops. |
| onTranscript | (text: string) => void | | Fired when speech-to-text produces a transcript. |
| onAgentResponse | (response: string) => void | | Fired when the AI produces a text response. |
| speechStopElement | HTMLElement \| null | | Element (or window) whose pointerdown / keydown events stop TTS playback. Defaults to window. |
| storageAdapter | StorageAdapter | | Custom storage for conversation history persistence. Defaults to localStorage. |
UseDirectivSysReturn
| Field | Type | Description |
|---|---|---|
| messages | ChatMessage[] | The full conversation history in chronological order. |
| isLoading | boolean | true while a request is in flight. |
| error | Error \| null | The last error, or null. |
| sendChatQuery | (query: SendChatQueryInput) => Promise<void> | Send a text or multipart message to the AI. |
| clearMessages | () => void | Clear the local message history and reset the session. |
| isListening | boolean | true while the microphone is recording. |
| startVoiceInput | () => void | Start recording voice input. |
| stopVoiceInput | () => void | Stop recording voice input. |
| stopSpeechPlayback | () => void | Stop any active TTS playback immediately. |
| hasMoreMessages | boolean | true when older messages are available to page in. |
| isLoadingHistory | boolean | true while loadMoreMessages is in flight. |
| loadMoreMessages | () => Promise<void> | Fetch the next page of older conversation history. |
CurrentContext
The CurrentContext object is the primary signal that tells the AI who the user is and what they are looking at. Pass an up-to-date object on every render — the SDK will efficiently diff it and only propagate genuine changes.
interface CurrentContext {
userId?: string;
userName?: string;
userPreferences?: string;
userRecentActivity?: string;
currentContext?: {
currentPageName?: string;
currentPageUrl?: string;
currentPageDescription?: string;
viewingItems?: ViewingItem[];
};
}Important: Always include
userIdwhen the user is authenticated. Omitting it may cause directives and analytics to be attributed to the wrong session. See the known bug report indocs/SDK_CONTEXT_UPDATE_BUG_REPORT.mdfor details on context-update edge cases.
Components
DirectivSysChatbox
A full-featured conversational chat UI. Supports two render modes: "basic" (an inline panel) and "standard" (a floating bottom-corner widget).
import { DirectivSysChatbox } from "@directivsys/react-sdk";
<DirectivSysChatbox
onIntentDetected={handleAction}
currentContext={currentContext}
renderMode="standard"
defaultOpen={false}
boxLocation="bottom-right"
titleText="AI Assistant"
placeholder="Ask me anything…"
height="600px"
width="400px"
/>DirectivSysChatboxProps
| Prop | Type | Default | Description |
|---|---|---|---|
| onIntentDetected | OnIntentDetected | — | Required. Tool call handler. |
| currentContext | CurrentContext | — | Required. User/page context. |
| renderMode | "standard" \| "basic" | "basic" | "standard" renders a toggleable floating widget; "basic" renders an inline panel. |
| defaultOpen | boolean | false | Initial open/closed state (only applies to "standard" mode). |
| boxLocation | "bottom-right" \| "bottom-left" | "bottom-right" | Corner position for the floating widget. |
| placeholder | string | "Type your message…" | Input placeholder text. |
| height | string | "500px" | Height of the chat panel. |
| width | string | "100%" | Width of the chat panel. |
| theme | Partial<Theme> | — | Per-instance theme overrides (merged with the provider theme). |
| renderMessage | (message: ChatMessage) => ReactNode | — | Custom message renderer. If omitted, built-in bubble rendering is used. |
| titleText | string | "AI Assistant" | Header title (standard mode). |
| titleIcon | ReactNode | — | Icon rendered before the title (standard mode). |
| titleTextColor | string | — | CSS colour for the title text. |
| headerBgColor | string | theme.colors.primary | Header background colour. |
| toggleIcon | ReactNode | speech-bubble SVG | Icon inside the floating toggle button. |
| toggleButtonClass | string | Tailwind utility classes | CSS class(es) for the toggle button. |
| toggleButtonTitle | string | "Open chat assistant" | title attribute on the toggle button. |
| storageAdapter | StorageAdapter | localStorage | Persistent storage for conversation history. |
| onError | (error: Error) => void | — | Error callback. |
| onSpeechStart | () => void | — | Microphone opened. |
| onSpeechEnd | () => void | — | Microphone closed. |
| onTranscript | (text: string) => void | — | STT transcript available. |
| onAgentResponse | (response: string) => void | — | AI text response ready. |
DirectivSysSearch
A compact search bar with voice support. Results from the AI are displayed in a dropdown list below the input.
import { DirectivSysSearch } from "@directivsys/react-sdk";
<DirectivSysSearch
onIntentDetected={handleAction}
currentContext={currentContext}
placeholder="Search with AI…"
maxResults={5}
width="100%"
/>DirectivSysSearchProps
| Prop | Type | Default | Description |
|---|---|---|---|
| onIntentDetected | OnIntentDetected | — | Required. Tool call handler. |
| currentContext | CurrentContext | — | Required. User/page context. |
| placeholder | string | "Search…" | Input placeholder text. |
| width | string | "100%" | CSS width of the search container. |
| maxResults | number | 5 | Maximum number of AI responses to display. |
| theme | Partial<Theme> | — | Per-instance theme overrides. |
| onError | (error: Error) => void | — | Error callback. |
| onSpeechStart | () => void | — | |
| onSpeechEnd | () => void | — | |
| onTranscript | (text: string) => void | — | |
| onAgentResponse | (response: string) => void | — | |
Analytics
AnalyticsDashboard
A full analytics management UI. Fetches the available analytics configurations from the platform, lets the user switch between them, displays the latest result (metrics, observations, directives), and lets the user execute or decline proposed directives.
import { AnalyticsDashboard } from "@directivsys/react-sdk";
import type { OnDirectiveAction } from "@directivsys/react-sdk";
const handleDirective: OnDirectiveAction = async (directive, action) => {
if (action === "execute") {
await mySystem.applyDirective(directive);
return { success: true };
}
return { success: true }; // decline is a no-op in the application layer
};
<AnalyticsDashboard
onDirectiveAction={handleDirective}
showRefresh
autoRefreshInterval={60} // seconds
/>AnalyticsDashboardProps
| Prop | Type | Default | Description |
|---|---|---|---|
| onDirectiveAction | OnDirectiveAction | — | Required. Called when the user executes or declines a directive. |
| showRefresh | boolean | true | Show a manual refresh button. |
| autoRefreshInterval | number | 0 | Auto-refresh interval in seconds. 0 disables auto-refresh. |
| theme | Theme | — | Theme override. Falls back to the provider theme. |
| className | string | "" | CSS class applied to the root element. |
AnalyticsWidget
A compact, embeddable analytics widget for a single analytics type.
import { AnalyticsWidget } from "@directivsys/react-sdk";
<AnalyticsWidget analyticsType="inventory-health" />AnalyticsWidgetProps
| Prop | Type | Default | Description |
|---|---|---|---|
| analyticsType | string | — | Required. Analytics type identifier as configured on the platform. |
| theme | Theme | — | Theme override. |
| className | string | "" | CSS class applied to the root element. |
Analytics Hooks
useAnalyticsConfigs
Fetches the list of analytics configurations available to the current API key.
const { configs, isLoading, error, refetch } = useAnalyticsConfigs();| Return field | Type | Description |
|---|---|---|
| configs | AnalyticsConfig[] | Available analytics configurations. |
| isLoading | boolean | |
| error | Error \| null | |
| refetch | () => Promise<void> | Manually re-fetch. |
useAnalyticsResult
Fetches the latest result for a given analytics type.
const { result, isLoading, error, refetch } = useAnalyticsResult("inventory-health");Pass null to skip the fetch (useful when no analytics type is selected yet).
| Return field | Type | Description |
|---|---|---|
| result | AnalyticsResult \| null | Latest result including metrics, observations, and directives. |
| isLoading | boolean | |
| error | Error \| null | |
| refetch | () => Promise<void> | Manually re-fetch. |
useDirectiveAction
Handles the lifecycle of executing or declining a directive, including API status updates.
const { handleAction, isProcessing, error } = useDirectiveAction(onDirectiveAction);
await handleAction(directive, "execute"); // or "decline"| Parameter | Type | Description |
|---|---|---|
| onDirectiveAction | OnDirectiveAction \| undefined | Optional application callback. If provided, it runs first; the SDK then synchronises the directive status on the platform. |
| Return field | Type | Description |
|---|---|---|
| handleAction | (directive, action) => Promise<DirectiveActionResult> | Call with a directive and "execute" or "decline". |
| isProcessing | boolean | true while the action is in flight. |
| error | Error \| null | Last error, if any. |
Reports
ReportsDashboard
A tabbed shell that renders all five built-in report views with a navigation bar.
import { ReportsDashboard } from "@directivsys/react-sdk";
<ReportsDashboard initialReport="executive" theme={myTheme} />ReportsDashboardProps
| Prop | Type | Default | Description |
|---|---|---|---|
| initialReport | ReportType | "executive" | Which report tab to open on mount. |
| theme | Theme | — | Theme applied to all nested report components. |
ReportType is "executive" | "risk" | "audit" | "health" | "quality".
Individual Report Components
All five report components can be imported and rendered independently without the ReportsDashboard shell.
| Export | Description |
|---|---|
| ExecutiveDashboard | High-level directive execution metrics. |
| SupplyChainRisk | Risk analysis and supply chain health indicators. |
| ExecutionAuditTrail | Paginated directive execution history with filtering. |
| PortfolioHealth | Portfolio metric health scores and critical alerts. |
| DataQualityAutomation | Data quality and automation assessment scores. |
Each component accepts theme?: Theme and any data-specific props — see the TypeScript signatures in the component source files for full details.
Report Hooks
useReportData
Generic data-fetching hook with built-in caching and request deduplication. Used internally by all report components — expose it when building custom report views.
import { useReportData } from "@directivsys/react-sdk";
import { myReportsApi } from "./api";
function MyCustomReport() {
const { data, loading, error, refetch } = useReportData(
() => myReportsApi.getExecutiveSummary({ startDate, endDate }),
[startDate, endDate], // re-fetch when these change
{ ttl: 2 * 60 * 1000 }, // cache for 2 minutes
);
// …
}| Parameter | Type | Default | Description |
|---|---|---|---|
| fetchFn | () => Promise<T> | — | Required. Async data fetcher. |
| dependencies | React.DependencyList | [] | Values that trigger a re-fetch when changed. |
| options.enabled | boolean | true | Set false to pause fetching. |
| options.ttl | number | 300000 | Cache TTL in milliseconds (default: 5 minutes). |
| Return field | Type | Description |
|---|---|---|
| data | T \| null | Fetched data, or null while loading or on error. |
| loading | boolean | |
| error | Error \| null | |
| refetch | () => Promise<void> | Bypass cache and re-fetch immediately. |
useDateRange
Manages a date range selection backed by predefined options.
import { useDateRange } from "@directivsys/react-sdk";
const { selectedOption, setSelectedOption, startDate, endDate, label } =
useDateRange("past-month");| Parameter | Type | Default | Description |
|---|---|---|---|
| defaultOption | DateRangeOption | "past-month" | Initial selection. |
| Return field | Type | Description |
|---|---|---|
| selectedOption | DateRangeOption | Currently selected option key. |
| setSelectedOption | (option: DateRangeOption) => void | Update the selection. |
| startDate | string | ISO 8601 date string for range start. |
| endDate | string | ISO 8601 date string for range end. |
| label | string | Human-readable label for the selected range. |
Theming
The SDK ships with lightTheme and darkTheme. Pass a theme to DirectivSysProvider to apply it globally; individual components accept a theme prop for per-instance overrides.
import { DirectivSysProvider, darkTheme } from "@directivsys/react-sdk";
<DirectivSysProvider apiKey="…" theme={darkTheme}>
…
</DirectivSysProvider>Creating a custom theme
Use createCustomTheme to extend an existing theme with your brand colours:
import { createCustomTheme, lightTheme } from "@directivsys/react-sdk";
const brandTheme = createCustomTheme(lightTheme, {
colors: {
primary: "#6200ea",
secondary: "#03dac6",
},
borderRadius: "4px",
});Theme shape
interface Theme {
name: string;
colors: {
primary: string; // Interactive elements, CTAs
secondary: string; // Accents
background: string; // Page background
surface: string; // Card / panel background
text: string; // Primary text
textSecondary: string; // Labels, captions
border: string; // Dividers and outlines
error: string;
success: string;
};
spacing: { xs: string; sm: string; md: string; lg: string; xl: string };
fontSizes: { xs: string; sm: string; md: string; lg: string; xl: string };
borderRadius: string;
boxShadow: string;
}Selecting a theme at runtime
import { getTheme } from "@directivsys/react-sdk";
const theme = getTheme("dark"); // lightTheme | darkTheme
const custom = getTheme("custom", myTheme); // returns myThemeTypeScript Types
All types are exported from the package root:
import type {
// Conversation
ChatMessage,
ToolCall,
ToolResult,
OnIntentDetected,
CurrentContext,
InterfaceState,
ViewingItem,
SendChatQueryInput,
UseDirectivSysOptions,
UseDirectivSysReturn,
// Config
DirectivSysConfig,
StorageAdapter,
// Voice
STTProvider,
TTSProvider,
// Theme
Theme,
ThemeColors,
ThemeSpacing,
ThemeFontSizes,
ThemeMode,
// Analytics
AnalyticsConfig,
AnalyticsResult,
AnalyticsMetric,
AnalyticsDirective,
DirectiveParameter,
AnalyticsObservation,
DirectiveActionResult,
OnDirectiveAction,
} from "@directivsys/react-sdk";Context & Session Handling
Every conversation is tied to a server-side session. The SDK stores the sessionId in localStorage by default so that conversation history persists across page reloads.
Chat history pagination
When a session has more history than the initial load, hasMoreMessages is true. Call loadMoreMessages() to page in older messages:
const { messages, hasMoreMessages, isLoadingHistory, loadMoreMessages } = useDirectivSys(…);
return (
<>
{hasMoreMessages && (
<button onClick={() => void loadMoreMessages()} disabled={isLoadingHistory}>
{isLoadingHistory ? "Loading…" : "Load earlier messages"}
</button>
)}
{messages.map(…)}
</>
);Custom storage adapter
Replace localStorage with any object that satisfies the StorageAdapter interface:
interface StorageAdapter {
getItem(key: string): string | null;
setItem(key: string, value: string): void;
removeItem(key: string): void;
}Pass it to DirectivSysChatbox, DirectivSysSearch, or useDirectivSys via the storageAdapter prop / option.
// In-memory adapter (no persistence — useful in tests or SSR)
const memoryAdapter: StorageAdapter = (() => {
const store: Record<string, string> = {};
return {
getItem: (k) => store[k] ?? null,
setItem: (k, v) => { store[k] = v; },
removeItem: (k) => { delete store[k]; },
};
})();Voice Input / Output
Voice is powered by the Web Speech API (WebSpeechSTTProvider / WebSpeechTTSProvider) out of the box. Both providers are injected via the DirectivSysProvider, so components share the same STT/TTS instances.
To replace one or both providers with a custom implementation (e.g., a cloud STT service), implement the STTProvider / TTSProvider interfaces and pass instances to the provider:
import type { STTProvider } from "@directivsys/react-sdk";
class MyCloudSTTProvider implements STTProvider {
// … implement the interface
}
<DirectivSysProvider
apiKey="…"
sttProvider={new MyCloudSTTProvider()}
>Architecture Overview
See docs/ARCHITECTURE.md for the full layering diagram and design rationale.
@directivsys/react-sdk
├── DirectivSysProvider → React context: API client, theme, STT/TTS providers
├── useDirectivSys → Stateful adapter over DirectivSysConversationController
├── DirectivSysChatbox → Full-featured chat UI (inline or floating widget)
├── DirectivSysSearch → Compact AI-powered search bar
├── AnalyticsDashboard → Analytics config selector + result viewer + directive actions
├── AnalyticsWidget → Single-type analytics embed
└── ReportsDashboard → Tabbed shell for five built-in report viewsData flow for a chat message:
User types message
→ sendChatQuery()
→ DirectivSysConversationController (core-sdk)
→ POST /api/chat (with CurrentContext)
→ AI responds, may call tools
→ onIntentDetected() fires → your app executes the action
→ ToolResult returned to controller
→ Controller appends messages to state
→ React component re-renders