@mordn/chat-widget 0.2.1 → 0.4.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/dist/index.d.mts CHANGED
@@ -57,6 +57,23 @@ interface ChatWidgetConfig {
57
57
  * Users can click these to quickly start a conversation
58
58
  */
59
59
  starterPrompts?: StarterPrompt[];
60
+ /**
61
+ * Called when the user dismisses the widget. The widget renders its own
62
+ * close X inside the header (correctly stacked) when this is provided —
63
+ * works in all layouts (popup, inline, page).
64
+ *
65
+ * In `popup` layout the widget also auto-closes its own panel; in
66
+ * `inline` and `page` layouts the consumer owns the close behaviour
67
+ * (e.g. setting their open state, navigating away).
68
+ */
69
+ onClose?: () => void;
70
+ /**
71
+ * Custom buttons rendered in the widget header next to the close X.
72
+ * Use this for "expand to full page", "settings", or any consumer-defined
73
+ * action that belongs in the widget's chrome — avoids absolute-positioning
74
+ * overlays from the outside that fight the widget's own z-index.
75
+ */
76
+ headerActions?: ReactNode;
60
77
  }
61
78
  interface StarterPrompt {
62
79
  /**
@@ -120,11 +137,35 @@ interface FeatureConfig {
120
137
  * - full: 100% - fills entire screen
121
138
  */
122
139
  type ChatWidgetSize = 'compact' | 'default' | 'large' | 'full';
140
+
141
+ /**
142
+ * Layout shape the widget renders in.
143
+ *
144
+ * - `popup` : floating side panel, opened by a toggle button (default).
145
+ * Best for ambient assistance available across pages.
146
+ * - `inline` : renders in place inside the parent element. No toggle button,
147
+ * no fixed positioning, fills its container. Best for dashboard
148
+ * cards or dedicated chat sections of a page.
149
+ * - `page` : full-viewport layout with a conversation list sidebar on the
150
+ * left and the active chat on the right. Best for a dedicated
151
+ * chat route (e.g. `/chat`).
152
+ *
153
+ * The `popup` value is the historical default and remains backward compatible.
154
+ */
155
+ type ChatWidgetLayout = 'popup' | 'inline' | 'page';
123
156
  interface DisplayConfig {
157
+ /**
158
+ * How the widget renders.
159
+ * Default: `'popup'` (backward compatible).
160
+ */
161
+ layout?: ChatWidgetLayout;
124
162
  /**
125
163
  * Preset size for the widget (recommended)
126
164
  * Uses clamp() for responsive sizing with min/max bounds
127
165
  * Default: 'default'
166
+ *
167
+ * Note: `size` only applies in `popup` layout. `inline` and `page` layouts
168
+ * fill their container regardless.
128
169
  */
129
170
  size?: ChatWidgetSize;
130
171
  /**
@@ -170,7 +211,7 @@ interface ChatWidgetProps extends ChatWidgetConfig {
170
211
  */
171
212
  widgetId?: string;
172
213
  }
173
- declare function ChatWidget({ userId, conversationId, initialMessages, className, model, systemPrompt, temperature, theme, features, display, starterPrompts, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
214
+ declare function ChatWidget({ userId, conversationId, initialMessages, className, model, systemPrompt, temperature, theme, features, display, starterPrompts, onClose, headerActions, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
174
215
 
175
216
  interface ChatTheme {
176
217
  lightPrimary: string;
package/dist/index.d.ts CHANGED
@@ -57,6 +57,23 @@ interface ChatWidgetConfig {
57
57
  * Users can click these to quickly start a conversation
58
58
  */
59
59
  starterPrompts?: StarterPrompt[];
60
+ /**
61
+ * Called when the user dismisses the widget. The widget renders its own
62
+ * close X inside the header (correctly stacked) when this is provided —
63
+ * works in all layouts (popup, inline, page).
64
+ *
65
+ * In `popup` layout the widget also auto-closes its own panel; in
66
+ * `inline` and `page` layouts the consumer owns the close behaviour
67
+ * (e.g. setting their open state, navigating away).
68
+ */
69
+ onClose?: () => void;
70
+ /**
71
+ * Custom buttons rendered in the widget header next to the close X.
72
+ * Use this for "expand to full page", "settings", or any consumer-defined
73
+ * action that belongs in the widget's chrome — avoids absolute-positioning
74
+ * overlays from the outside that fight the widget's own z-index.
75
+ */
76
+ headerActions?: ReactNode;
60
77
  }
61
78
  interface StarterPrompt {
62
79
  /**
@@ -120,11 +137,35 @@ interface FeatureConfig {
120
137
  * - full: 100% - fills entire screen
121
138
  */
122
139
  type ChatWidgetSize = 'compact' | 'default' | 'large' | 'full';
140
+
141
+ /**
142
+ * Layout shape the widget renders in.
143
+ *
144
+ * - `popup` : floating side panel, opened by a toggle button (default).
145
+ * Best for ambient assistance available across pages.
146
+ * - `inline` : renders in place inside the parent element. No toggle button,
147
+ * no fixed positioning, fills its container. Best for dashboard
148
+ * cards or dedicated chat sections of a page.
149
+ * - `page` : full-viewport layout with a conversation list sidebar on the
150
+ * left and the active chat on the right. Best for a dedicated
151
+ * chat route (e.g. `/chat`).
152
+ *
153
+ * The `popup` value is the historical default and remains backward compatible.
154
+ */
155
+ type ChatWidgetLayout = 'popup' | 'inline' | 'page';
123
156
  interface DisplayConfig {
157
+ /**
158
+ * How the widget renders.
159
+ * Default: `'popup'` (backward compatible).
160
+ */
161
+ layout?: ChatWidgetLayout;
124
162
  /**
125
163
  * Preset size for the widget (recommended)
126
164
  * Uses clamp() for responsive sizing with min/max bounds
127
165
  * Default: 'default'
166
+ *
167
+ * Note: `size` only applies in `popup` layout. `inline` and `page` layouts
168
+ * fill their container regardless.
128
169
  */
129
170
  size?: ChatWidgetSize;
130
171
  /**
@@ -170,7 +211,7 @@ interface ChatWidgetProps extends ChatWidgetConfig {
170
211
  */
171
212
  widgetId?: string;
172
213
  }
173
- declare function ChatWidget({ userId, conversationId, initialMessages, className, model, systemPrompt, temperature, theme, features, display, starterPrompts, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
214
+ declare function ChatWidget({ userId, conversationId, initialMessages, className, model, systemPrompt, temperature, theme, features, display, starterPrompts, onClose, headerActions, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
174
215
 
175
216
  interface ChatTheme {
176
217
  lightPrimary: string;
package/dist/index.js CHANGED
@@ -1431,7 +1431,7 @@ function useChatStorageKey() {
1431
1431
 
1432
1432
  // src/components/interface.tsx
1433
1433
  var import_jsx_runtime20 = require("react/jsx-runtime");
1434
- function ChatInterface({ id, initialMessages, config, onClose } = {}) {
1434
+ function ChatInterface({ id, initialMessages, config, onClose, headerActions } = {}) {
1435
1435
  const { storageKeyPrefix } = useChatStorageKey();
1436
1436
  const storageKey = (key) => storageKeyPrefix ? `chat-${storageKeyPrefix}-${key}` : `chat-${key}`;
1437
1437
  const themeMode = config?.theme?.mode || "light";
@@ -2102,6 +2102,7 @@ function ChatInterface({ id, initialMessages, config, onClose } = {}) {
2102
2102
  ] }, groupName)) }) })
2103
2103
  ] })
2104
2104
  ] }),
2105
+ headerActions,
2105
2106
  onClose && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2106
2107
  "button",
2107
2108
  {
@@ -2215,12 +2216,17 @@ function ChatWidget({
2215
2216
  theme,
2216
2217
  features,
2217
2218
  display,
2218
- starterPrompts
2219
+ starterPrompts,
2220
+ onClose,
2221
+ headerActions
2219
2222
  }) {
2223
+ const layout = display?.layout || "popup";
2220
2224
  const showToggleButton = display?.showToggleButton !== false;
2221
- const resizable = display?.resizable !== false;
2225
+ const resizable = layout === "popup" && display?.resizable !== false;
2222
2226
  const size = display?.size || "default";
2223
- const [isOpen, setIsOpen] = (0, import_react12.useState)(display?.defaultOpen || false);
2227
+ const [isOpen, setIsOpen] = (0, import_react12.useState)(
2228
+ layout !== "popup" ? true : display?.defaultOpen || false
2229
+ );
2224
2230
  const [isResizing, setIsResizing] = (0, import_react12.useState)(false);
2225
2231
  const containerRef = (0, import_react12.useRef)(null);
2226
2232
  const customStyles = (0, import_react12.useMemo)(() => {
@@ -2281,6 +2287,47 @@ function ChatWidget({
2281
2287
  starterPrompts
2282
2288
  }), [userId, model, systemPrompt, temperature, theme, features, starterPrompts]);
2283
2289
  const togglePosition = display?.toggleButtonPosition || { bottom: "24px", right: "24px" };
2290
+ const themeClass = theme?.mode === "dark" ? "dark" : "";
2291
+ if (layout === "inline") {
2292
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ChatStorageProvider, { userId, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2293
+ "div",
2294
+ {
2295
+ ref: containerRef,
2296
+ className: `chat-widget-container chat-widget-inline chat-widget-content ${themeClass} ${className || ""}`,
2297
+ style: customStyles,
2298
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2299
+ ChatInterface,
2300
+ {
2301
+ id: conversationId,
2302
+ initialMessages,
2303
+ config,
2304
+ onClose,
2305
+ headerActions
2306
+ }
2307
+ )
2308
+ }
2309
+ ) });
2310
+ }
2311
+ if (layout === "page") {
2312
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ChatStorageProvider, { userId, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2313
+ "div",
2314
+ {
2315
+ ref: containerRef,
2316
+ className: `chat-widget-container chat-widget-page chat-widget-content ${themeClass} ${className || ""}`,
2317
+ style: customStyles,
2318
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2319
+ ChatInterface,
2320
+ {
2321
+ id: conversationId,
2322
+ initialMessages,
2323
+ config,
2324
+ onClose,
2325
+ headerActions
2326
+ }
2327
+ )
2328
+ }
2329
+ ) });
2330
+ }
2284
2331
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(ChatStorageProvider, { userId, children: [
2285
2332
  showToggleButton && !isOpen && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2286
2333
  "button",
@@ -2296,7 +2343,7 @@ function ChatWidget({
2296
2343
  "div",
2297
2344
  {
2298
2345
  ref: containerRef,
2299
- className: `chat-widget-container chat-widget-popup chat-widget-content ${theme?.mode === "dark" ? "dark" : ""} ${className || ""}`,
2346
+ className: `chat-widget-container chat-widget-popup chat-widget-content ${themeClass} ${className || ""}`,
2300
2347
  "data-size": size,
2301
2348
  "data-resizing": isResizing,
2302
2349
  style: customStyles,
@@ -2315,7 +2362,11 @@ function ChatWidget({
2315
2362
  id: conversationId,
2316
2363
  initialMessages,
2317
2364
  config,
2318
- onClose: () => setIsOpen(false)
2365
+ onClose: () => {
2366
+ setIsOpen(false);
2367
+ onClose?.();
2368
+ },
2369
+ headerActions
2319
2370
  }
2320
2371
  ) })
2321
2372
  ]