@helpway/react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # @helpway/react
2
+
3
+ Official React SDK for [Helpway](https://helpway.ai) — AI-powered customer support in a single component.
4
+
5
+ Two integration levels in one package:
6
+
7
+ - **Drop-in**: mount `<HelpwayWidget apiKey="..." />` anywhere in your tree and you're done.
8
+ - **Headless**: use `SupportProvider` + hooks (`useWidget`, `useConversations`, `useTimeline`, `useComposer`) to build your own chat UI against the same realtime backend.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install @helpway/react
14
+ # or
15
+ bun add @helpway/react
16
+ # or
17
+ pnpm add @helpway/react
18
+ ```
19
+
20
+ Peer dependencies: `react` ≥ 18 and `react-dom` ≥ 18.
21
+
22
+ ## Drop-in
23
+
24
+ Grab your public API key from the Helpway dashboard → Settings → API keys (format: `pk_live_…`).
25
+
26
+ ```tsx
27
+ // app/layout.tsx (Next.js App Router)
28
+ import { HelpwayWidget } from "@helpway/react";
29
+
30
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
31
+ return (
32
+ <html>
33
+ <body>
34
+ {children}
35
+ <HelpwayWidget apiKey="pk_live_XXXXXXXX" />
36
+ </body>
37
+ </html>
38
+ );
39
+ }
40
+ ```
41
+
42
+ ```tsx
43
+ // main.tsx (Vite / CRA)
44
+ import { HelpwayWidget } from "@helpway/react";
45
+ import { StrictMode } from "react";
46
+ import { createRoot } from "react-dom/client";
47
+ import App from "./App";
48
+
49
+ createRoot(document.getElementById("root")!).render(
50
+ <StrictMode>
51
+ <App />
52
+ <HelpwayWidget apiKey="pk_live_XXXXXXXX" />
53
+ </StrictMode>,
54
+ );
55
+ ```
56
+
57
+ The component injects the Helpway widget loader script into `document.body`, handles StrictMode double-mounts, and removes the script on unmount.
58
+
59
+ ### Props
60
+
61
+ | Prop | Type | Default | Description |
62
+ | -------- | -------- | ------------------------------------ | --------------------------------------------- |
63
+ | `apiKey` | `string` | — | Public key from the dashboard. Required. |
64
+ | `src` | `string` | `https://api.helpway.ai/widget.js` | Override for staging / self-hosted setups. |
65
+
66
+ ### Identifying logged-in users
67
+
68
+ After your user signs in, call the global `Helpway` identify function so the dashboard sees their real name and email instead of `Anonymous`:
69
+
70
+ ```ts
71
+ window.Helpway("identify", {
72
+ uniqueId: user.id,
73
+ name: user.name,
74
+ email: user.email,
75
+ });
76
+ ```
77
+
78
+ ## Headless
79
+
80
+ If you want to render your own chat UI (custom bubbles, different layout, embedded in your app instead of a floating button), use the provider + hooks:
81
+
82
+ ```tsx
83
+ import { SupportProvider, useConversations, useTimeline, useComposer } from "@helpway/react";
84
+
85
+ function ChatSurface() {
86
+ const { conversations, activeConversationId, setActive } = useConversations();
87
+ const { items } = useTimeline();
88
+ const { draft, setDraft, send } = useComposer();
89
+
90
+ return (
91
+ <div>
92
+ {/* render conversations, messages, composer — full control */}
93
+ </div>
94
+ );
95
+ }
96
+
97
+ export default function App() {
98
+ return (
99
+ <SupportProvider apiKey="pk_live_XXXXXXXX">
100
+ <ChatSurface />
101
+ </SupportProvider>
102
+ );
103
+ }
104
+ ```
105
+
106
+ Exported hooks:
107
+
108
+ - `useWidget()` — global state, openers/closers
109
+ - `useConversations()` — list + active conversation
110
+ - `useTimeline()` — messages + events for the active conversation
111
+ - `useComposer()` — draft state + send/attach
112
+ - `usePresence()` — online team members
113
+ - `useUnread()` — unread counts
114
+ - `useRealtime()` — raw WebSocket event stream
115
+
116
+ Full API docs: [helpway.ai/docs/react](https://helpway.ai/docs/react)
117
+
118
+ ## Styling
119
+
120
+ The default themed widget ships opinionated styles. If you use `DefaultWidget`, pull in the stylesheet once:
121
+
122
+ ```ts
123
+ import "@helpway/react/styles.css";
124
+ ```
125
+
126
+ The headless hooks have no styles — bring your own.
127
+
128
+ ## License
129
+
130
+ MIT © Helpway
@@ -0,0 +1,257 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as _helpway_widget_core from '@helpway/widget-core';
3
+ import { WidgetStore } from '@helpway/widget-core';
4
+ export { ConversationSummary, RealtimeEvent, TimelineItem, WidgetState, WidgetStore, clearVisitorId, formatRelativeTime, formatTime, getInitials, getOrCreateVisitorId } from '@helpway/widget-core';
5
+ import * as react from 'react';
6
+ import react__default, { ReactNode } from 'react';
7
+ import * as _helpway_types from '@helpway/types';
8
+ import { TimelineItem, RealtimeEvent, TimelineItemPart } from '@helpway/types';
9
+
10
+ /**
11
+ * `<HelpwayWidget />` — one-line drop-in React component that loads
12
+ * the Helpway widget script and tears it down on unmount.
13
+ *
14
+ * This is the lowest-friction integration path for React / Next.js /
15
+ * Vite apps that don't need to render custom chat UI themselves. Under
16
+ * the hood it just injects `https://api.helpway.ai/widget.js` with the
17
+ * provided public key, exactly like the plain `<script>` snippet — but
18
+ * wrapped in a React lifecycle so it participates in StrictMode double
19
+ * mounts, SSR boundaries, and component cleanup without footguns.
20
+ *
21
+ * Power-user alternative: import `{ SupportProvider, useWidget, … }`
22
+ * from this same package for full programmatic control.
23
+ */
24
+ interface HelpwayWidgetProps {
25
+ /**
26
+ * Public API key from the Helpway dashboard. Required.
27
+ * Format: `pk_live_…` or `pk_test_…`.
28
+ */
29
+ apiKey: string;
30
+ /**
31
+ * Override the default widget loader URL. Almost no one needs this —
32
+ * exposed for staging / self-hosted setups.
33
+ * @default "https://api.helpway.ai/widget.js"
34
+ */
35
+ src?: string;
36
+ }
37
+ /**
38
+ * Render this once at the root of your app (e.g. in `_app.tsx`,
39
+ * `layout.tsx`, or `main.tsx`). Unmounting removes the script and
40
+ * collapses the widget iframe — useful for route-scoped mounts.
41
+ */
42
+ declare function HelpwayWidget({ apiKey, src, }: HelpwayWidgetProps): null;
43
+
44
+ interface IdentifyData {
45
+ externalId: string;
46
+ email?: string;
47
+ name?: string;
48
+ image?: string;
49
+ metadata?: Record<string, unknown>;
50
+ }
51
+ /** Use a slice of the widget store with auto re-render */
52
+ declare function useWidgetStore<T>(selector: (state: WidgetStore) => T): T;
53
+ /** Hook to identify the current visitor (link to customer contact) */
54
+ declare function useIdentify(): (data: IdentifyData) => Promise<void>;
55
+ interface SupportProviderProps {
56
+ /** Public API key (pk_xxx) */
57
+ apiKey: string;
58
+ /** API base URL (default: auto-detect from script tag or window) */
59
+ apiUrl?: string;
60
+ /** Widget locale (e.g., "fr", "de"). Falls back to navigator.language → "en" */
61
+ locale?: string;
62
+ /** Deep link: auto-open this conversation after bootstrap (from ?hw_conv= URL param) */
63
+ initialConversationId?: string;
64
+ /** Callback to expose widget API to parent (for embed loader) */
65
+ onReady?: (api: {
66
+ identify: (data: IdentifyData) => Promise<void>;
67
+ openConversation: (id: string) => void;
68
+ }) => void;
69
+ children: ReactNode;
70
+ }
71
+ /**
72
+ * Root provider for the Helpway widget.
73
+ * Creates tRPC client, WebSocket connection, and state store.
74
+ *
75
+ * Visitor identity is managed via server-side cookies (hw_visitor).
76
+ * No localStorage or client-side ID management needed.
77
+ */
78
+ declare function SupportProvider({ apiKey, apiUrl, locale, initialConversationId, onReady, children, }: SupportProviderProps): react_jsx_runtime.JSX.Element;
79
+
80
+ /**
81
+ * Core widget state — connection status, visitor identity, website info.
82
+ *
83
+ * Usage:
84
+ * ```tsx
85
+ * const { isReady, visitor, website, identify } = useWidget()
86
+ * ```
87
+ */
88
+ declare function useWidget(): {
89
+ isReady: boolean;
90
+ isConnecting: boolean;
91
+ visitor: _helpway_types.PublicVisitor | null;
92
+ website: {
93
+ id: string;
94
+ name: string;
95
+ domain: string | null;
96
+ logoUrl: string | null;
97
+ } | null;
98
+ agentConfig: _helpway_widget_core.AgentConfig | null;
99
+ identify: (data: {
100
+ externalId: string;
101
+ email?: string;
102
+ name?: string;
103
+ image?: string;
104
+ metadata?: Record<string, unknown>;
105
+ }) => Promise<any>;
106
+ };
107
+
108
+ /**
109
+ * Conversation list + create new conversation.
110
+ */
111
+ declare function useConversations(): {
112
+ conversations: _helpway_widget_core.ConversationSummary[];
113
+ create: (text: string) => Promise<any>;
114
+ isCreating: boolean;
115
+ setActiveConversation: (id: string | null) => void;
116
+ };
117
+
118
+ /**
119
+ * Timeline items for a conversation + real-time updates.
120
+ */
121
+ declare function useTimeline(conversationId: string | null): {
122
+ items: TimelineItem[];
123
+ isLoading: boolean;
124
+ hasMore: boolean;
125
+ loadMore: () => Promise<void>;
126
+ };
127
+
128
+ interface PendingAttachment {
129
+ id: string;
130
+ file: File;
131
+ previewUrl: string | null;
132
+ isUploading: boolean;
133
+ error: string | null;
134
+ /** Resolved after upload — the part to include in sendMessage */
135
+ part: {
136
+ type: "image";
137
+ url: string;
138
+ mimeType: string;
139
+ } | {
140
+ type: "file";
141
+ url: string;
142
+ fileName: string;
143
+ fileSize: number;
144
+ mimeType: string;
145
+ } | null;
146
+ }
147
+ /**
148
+ * Composer state — text input + attachments + submit for a conversation.
149
+ */
150
+ declare function useComposer(conversationId: string | null): {
151
+ text: string;
152
+ setText: react.Dispatch<react.SetStateAction<string>>;
153
+ submit: () => Promise<void>;
154
+ isSubmitting: boolean;
155
+ attachments: PendingAttachment[];
156
+ addFiles: (files: File[]) => Promise<void>;
157
+ removeAttachment: (id: string) => void;
158
+ sendError: string | null;
159
+ clearSendError: () => void;
160
+ };
161
+
162
+ /**
163
+ * Typing presence — show "is typing" + send own typing status.
164
+ *
165
+ * Debounced: only sends WS event when typing state *changes*.
166
+ * While typing, auto-stops after 3 seconds of inactivity.
167
+ *
168
+ * Usage:
169
+ * ```tsx
170
+ * const { isTyping, setTyping } = usePresence(conversationId)
171
+ * ```
172
+ */
173
+ declare function usePresence(conversationId: string | null): {
174
+ isTyping: boolean;
175
+ isAiProcessing: boolean;
176
+ setTyping: (typing: boolean) => void;
177
+ };
178
+
179
+ /**
180
+ * Total unread count across all conversations.
181
+ *
182
+ * Usage:
183
+ * ```tsx
184
+ * const { totalUnread } = useUnread()
185
+ * ```
186
+ */
187
+ declare function useUnread(): {
188
+ totalUnread: number;
189
+ };
190
+
191
+ /**
192
+ * Subscribe to raw WebSocket events.
193
+ * For advanced usage — most apps should use useTimeline/usePresence instead.
194
+ *
195
+ * Usage:
196
+ * ```tsx
197
+ * useRealtime((event) => {
198
+ * if (event.type === 'timelineItemCreated') { ... }
199
+ * })
200
+ * ```
201
+ */
202
+ declare function useRealtime(handler: (event: RealtimeEvent) => void): void;
203
+
204
+ /**
205
+ * Unified support widget — Intercom-style with tab navigation.
206
+ * Two tabs: Home + Messages. Sub-views: conversation + new conversation.
207
+ */
208
+ declare function DefaultWidget({ initialOpen }?: {
209
+ initialOpen?: boolean;
210
+ }): react_jsx_runtime.JSX.Element;
211
+
212
+ interface MessageBubbleProps {
213
+ text: string;
214
+ sender: "visitor" | "team" | "ai";
215
+ senderName?: string;
216
+ time: string;
217
+ parts?: TimelineItemPart[] | null;
218
+ }
219
+ /** Single message bubble — Intercom-style, renders text + image/file parts */
220
+ declare function MessageBubble({ text, sender, senderName, time, parts }: MessageBubbleProps): react_jsx_runtime.JSX.Element;
221
+
222
+ interface MessageListProps {
223
+ items: TimelineItem[];
224
+ visitorName?: string;
225
+ isTyping?: boolean;
226
+ isAiProcessing?: boolean;
227
+ hasMore?: boolean;
228
+ isLoadingMore?: boolean;
229
+ onLoadMore?: () => void;
230
+ /** Slot rendered after all messages but before typing indicators (e.g. email prompt) */
231
+ renderAfterMessages?: react__default.ReactNode;
232
+ }
233
+ /** Timeline with date separators, typing indicator, infinite scroll, and AI processing state */
234
+ declare function MessageList({ items, visitorName, isTyping, isAiProcessing, hasMore, isLoadingMore, onLoadMore, renderAfterMessages, }: MessageListProps): react_jsx_runtime.JSX.Element;
235
+
236
+ interface ComposerProps {
237
+ text: string;
238
+ onTextChange: (text: string) => void;
239
+ onSubmit: () => void;
240
+ onTyping?: (isTyping: boolean) => void;
241
+ isSubmitting?: boolean;
242
+ placeholder?: string;
243
+ /** Compact mode — single-line input without footer row */
244
+ compact?: boolean;
245
+ /** Pending attachments to preview */
246
+ attachments?: PendingAttachment[];
247
+ /** Called when files are added (paste, drop, or file picker) */
248
+ onAddFiles?: (files: File[]) => void;
249
+ /** Called to remove a pending attachment */
250
+ onRemoveAttachment?: (id: string) => void;
251
+ /** Disable entire composer (e.g. email required before sending) */
252
+ disabled?: boolean;
253
+ }
254
+ /** Widget composer — auto-resize, Enter-to-send, paste/drop attachments */
255
+ declare function Composer({ text, onTextChange, onSubmit, onTyping, isSubmitting, placeholder, compact, attachments, onAddFiles, onRemoveAttachment, disabled, }: ComposerProps): react_jsx_runtime.JSX.Element;
256
+
257
+ export { Composer, DefaultWidget, HelpwayWidget, type HelpwayWidgetProps, type IdentifyData, MessageBubble, MessageList, SupportProvider, useComposer, useConversations, useIdentify, usePresence, useRealtime, useTimeline, useUnread, useWidget, useWidgetStore };