@wallavi/widget 1.3.8 → 1.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
@@ -119,8 +119,32 @@ interface BubbleWidgetProps extends ChatWidgetConfig {
119
119
  * @default true
120
120
  */
121
121
  autoConfig?: boolean;
122
+ /**
123
+ * Hide the floating bubble button. The panel can still be opened via:
124
+ * - Keyboard shortcut (keyboardShortcut + shortcutKey)
125
+ * - Controlled mode (isOpen + onOpenChange) — connect your own button or trigger
126
+ * Useful when you want a custom-placed button or a purely keyboard-driven widget.
127
+ * @default false
128
+ */
129
+ hideBubble?: boolean;
130
+ /**
131
+ * Controlled open state. When provided the widget becomes fully controlled —
132
+ * the parent is responsible for managing open/close. Pair with onOpenChange.
133
+ *
134
+ * @example
135
+ * const [chatOpen, setChatOpen] = useState(false);
136
+ * <button onClick={() => setChatOpen(v => !v)}>Support</button>
137
+ * <BubbleWidget isOpen={chatOpen} onOpenChange={setChatOpen} hideBubble ... />
138
+ */
139
+ isOpen?: boolean;
140
+ /**
141
+ * Called whenever the widget wants to change its open state
142
+ * (bubble click, header close button, Cmd+K, autoOpen).
143
+ * Use together with isOpen to fully control open/close from outside.
144
+ */
145
+ onOpenChange?: (open: boolean) => void;
122
146
  }
123
- declare function BubbleWidget({ position: positionProp, width, height, expandedWidth, expandedHeight, keyboardShortcut: keyboardShortcutProp, shortcutKey, autoOpen: autoOpenProp, bubbleIconUrl: bubbleIconUrlProp, bubbleSize, panelClassName, autoConfig, ...chatProps }: BubbleWidgetProps): react_jsx_runtime.JSX.Element;
147
+ declare function BubbleWidget({ position: positionProp, width, height, expandedWidth, expandedHeight, keyboardShortcut: keyboardShortcutProp, shortcutKey, autoOpen: autoOpenProp, bubbleIconUrl: bubbleIconUrlProp, bubbleSize, panelClassName, autoConfig, hideBubble, isOpen: isOpenProp, onOpenChange, ...chatProps }: BubbleWidgetProps): react_jsx_runtime.JSX.Element;
124
148
 
125
149
  declare function ChatWidget({ agentId, workspaceId, agentName, displayName, profilePicture, userMessageColor, initialMessages, suggestedMessages, messagePlaceholder, watermark, watermarkLogoUrl, footer, theme, showThinking, regenerateMessage, persist, onNavigate, hideCloseButton, source, userContext, playgroundOverrides, className, onClose, onReset, onExpand, expanded, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
126
150
 
package/dist/index.d.ts CHANGED
@@ -119,8 +119,32 @@ interface BubbleWidgetProps extends ChatWidgetConfig {
119
119
  * @default true
120
120
  */
121
121
  autoConfig?: boolean;
122
+ /**
123
+ * Hide the floating bubble button. The panel can still be opened via:
124
+ * - Keyboard shortcut (keyboardShortcut + shortcutKey)
125
+ * - Controlled mode (isOpen + onOpenChange) — connect your own button or trigger
126
+ * Useful when you want a custom-placed button or a purely keyboard-driven widget.
127
+ * @default false
128
+ */
129
+ hideBubble?: boolean;
130
+ /**
131
+ * Controlled open state. When provided the widget becomes fully controlled —
132
+ * the parent is responsible for managing open/close. Pair with onOpenChange.
133
+ *
134
+ * @example
135
+ * const [chatOpen, setChatOpen] = useState(false);
136
+ * <button onClick={() => setChatOpen(v => !v)}>Support</button>
137
+ * <BubbleWidget isOpen={chatOpen} onOpenChange={setChatOpen} hideBubble ... />
138
+ */
139
+ isOpen?: boolean;
140
+ /**
141
+ * Called whenever the widget wants to change its open state
142
+ * (bubble click, header close button, Cmd+K, autoOpen).
143
+ * Use together with isOpen to fully control open/close from outside.
144
+ */
145
+ onOpenChange?: (open: boolean) => void;
122
146
  }
123
- declare function BubbleWidget({ position: positionProp, width, height, expandedWidth, expandedHeight, keyboardShortcut: keyboardShortcutProp, shortcutKey, autoOpen: autoOpenProp, bubbleIconUrl: bubbleIconUrlProp, bubbleSize, panelClassName, autoConfig, ...chatProps }: BubbleWidgetProps): react_jsx_runtime.JSX.Element;
147
+ declare function BubbleWidget({ position: positionProp, width, height, expandedWidth, expandedHeight, keyboardShortcut: keyboardShortcutProp, shortcutKey, autoOpen: autoOpenProp, bubbleIconUrl: bubbleIconUrlProp, bubbleSize, panelClassName, autoConfig, hideBubble, isOpen: isOpenProp, onOpenChange, ...chatProps }: BubbleWidgetProps): react_jsx_runtime.JSX.Element;
124
148
 
125
149
  declare function ChatWidget({ agentId, workspaceId, agentName, displayName, profilePicture, userMessageColor, initialMessages, suggestedMessages, messagePlaceholder, watermark, watermarkLogoUrl, footer, theme, showThinking, regenerateMessage, persist, onNavigate, hideCloseButton, source, userContext, playgroundOverrides, className, onClose, onReset, onExpand, expanded, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
126
150
 
package/dist/index.js CHANGED
@@ -148,8 +148,10 @@ function useChat({
148
148
  { id: assistantMsgId, role: "assistant", parts: [] }
149
149
  ]);
150
150
  try {
151
- const token = typeof window !== "undefined" ? await window.Clerk?.session?.getToken() : null;
152
- const res = await fetch(`${API_URL}/api/threads/${threadId}/stream`, {
151
+ const isPrivate = Boolean(workspaceId);
152
+ const token = isPrivate && typeof window !== "undefined" ? await window.Clerk?.session?.getToken() : null;
153
+ const url = isPrivate ? `${API_URL}/api/threads/${threadId}/stream` : `${API_URL}/api/public/chat/stream`;
154
+ const res = await fetch(url, {
153
155
  method: "POST",
154
156
  headers: {
155
157
  "Content-Type": "application/json",
@@ -158,14 +160,17 @@ function useChat({
158
160
  body: JSON.stringify({
159
161
  input: userInput,
160
162
  agentId,
161
- workspaceId,
163
+ // Private endpoint: workspaceId + threadId in URL, playgroundOverrides allowed.
164
+ // Public endpoint: threadId in body (no workspaceId, no playgroundOverrides).
165
+ ...isPrivate ? { workspaceId, ...playgroundOverrides ? { playgroundOverrides } : {} } : { threadId },
162
166
  source,
163
- ...playgroundOverrides ? { playgroundOverrides } : {},
164
167
  ...userContext?.userName ? { userName: userContext.userName } : {},
165
168
  ...userContext?.userEmail ? { userEmail: userContext.userEmail } : {},
166
169
  userMetadata: {
167
170
  ...userContext?.metadata ?? {},
168
171
  ...userContext?.pageContext ? { pageContext: userContext.pageContext } : {},
172
+ // Forward client auth headers to the pipeline (for action/tool execution).
173
+ // On the private path, also include the Wallavi Clerk token.
169
174
  headers: {
170
175
  ...token ? { Authorization: `Bearer ${token}` } : {},
171
176
  ...userContext?.headers ?? {}
@@ -830,9 +835,19 @@ function BubbleWidget({
830
835
  bubbleSize = 52,
831
836
  panelClassName,
832
837
  autoConfig = true,
838
+ hideBubble = false,
839
+ isOpen: isOpenProp,
840
+ onOpenChange,
833
841
  ...chatProps
834
842
  }) {
835
- const [open, setOpen] = react.useState(false);
843
+ const isControlled = isOpenProp !== void 0;
844
+ const [internalOpen, setInternalOpen] = react.useState(false);
845
+ const open = isControlled ? isOpenProp : internalOpen;
846
+ const setOpen = react.useCallback((valueOrUpdater) => {
847
+ const next = typeof valueOrUpdater === "function" ? valueOrUpdater(open) : valueOrUpdater;
848
+ if (!isControlled) setInternalOpen(next);
849
+ onOpenChange?.(next);
850
+ }, [isControlled, open, onOpenChange]);
836
851
  const [expanded, setExpanded] = react.useState(false);
837
852
  const panelRef = react.useRef(null);
838
853
  const autoOpenedRef = react.useRef(false);
@@ -901,7 +916,9 @@ function BubbleWidget({
901
916
  }, [open]);
902
917
  const handleClose = () => {
903
918
  setOpen(false);
904
- localStorage.setItem(KEY_DISMISSED, String(Date.now() + 24 * 60 * 60 * 1e3));
919
+ if (!isControlled) {
920
+ localStorage.setItem(KEY_DISMISSED, String(Date.now() + 24 * 60 * 60 * 1e3));
921
+ }
905
922
  };
906
923
  const toggleExpanded = () => setExpanded((v) => {
907
924
  const next = !v;
@@ -948,7 +965,7 @@ function BubbleWidget({
948
965
  )
949
966
  }
950
967
  ),
951
- /* @__PURE__ */ jsxRuntime.jsx(
968
+ !hideBubble && /* @__PURE__ */ jsxRuntime.jsx(
952
969
  "button",
953
970
  {
954
971
  onClick: () => setOpen((v) => !v),
package/dist/index.mjs CHANGED
@@ -122,8 +122,10 @@ function useChat({
122
122
  { id: assistantMsgId, role: "assistant", parts: [] }
123
123
  ]);
124
124
  try {
125
- const token = typeof window !== "undefined" ? await window.Clerk?.session?.getToken() : null;
126
- const res = await fetch(`${API_URL}/api/threads/${threadId}/stream`, {
125
+ const isPrivate = Boolean(workspaceId);
126
+ const token = isPrivate && typeof window !== "undefined" ? await window.Clerk?.session?.getToken() : null;
127
+ const url = isPrivate ? `${API_URL}/api/threads/${threadId}/stream` : `${API_URL}/api/public/chat/stream`;
128
+ const res = await fetch(url, {
127
129
  method: "POST",
128
130
  headers: {
129
131
  "Content-Type": "application/json",
@@ -132,14 +134,17 @@ function useChat({
132
134
  body: JSON.stringify({
133
135
  input: userInput,
134
136
  agentId,
135
- workspaceId,
137
+ // Private endpoint: workspaceId + threadId in URL, playgroundOverrides allowed.
138
+ // Public endpoint: threadId in body (no workspaceId, no playgroundOverrides).
139
+ ...isPrivate ? { workspaceId, ...playgroundOverrides ? { playgroundOverrides } : {} } : { threadId },
136
140
  source,
137
- ...playgroundOverrides ? { playgroundOverrides } : {},
138
141
  ...userContext?.userName ? { userName: userContext.userName } : {},
139
142
  ...userContext?.userEmail ? { userEmail: userContext.userEmail } : {},
140
143
  userMetadata: {
141
144
  ...userContext?.metadata ?? {},
142
145
  ...userContext?.pageContext ? { pageContext: userContext.pageContext } : {},
146
+ // Forward client auth headers to the pipeline (for action/tool execution).
147
+ // On the private path, also include the Wallavi Clerk token.
143
148
  headers: {
144
149
  ...token ? { Authorization: `Bearer ${token}` } : {},
145
150
  ...userContext?.headers ?? {}
@@ -804,9 +809,19 @@ function BubbleWidget({
804
809
  bubbleSize = 52,
805
810
  panelClassName,
806
811
  autoConfig = true,
812
+ hideBubble = false,
813
+ isOpen: isOpenProp,
814
+ onOpenChange,
807
815
  ...chatProps
808
816
  }) {
809
- const [open, setOpen] = useState(false);
817
+ const isControlled = isOpenProp !== void 0;
818
+ const [internalOpen, setInternalOpen] = useState(false);
819
+ const open = isControlled ? isOpenProp : internalOpen;
820
+ const setOpen = useCallback((valueOrUpdater) => {
821
+ const next = typeof valueOrUpdater === "function" ? valueOrUpdater(open) : valueOrUpdater;
822
+ if (!isControlled) setInternalOpen(next);
823
+ onOpenChange?.(next);
824
+ }, [isControlled, open, onOpenChange]);
810
825
  const [expanded, setExpanded] = useState(false);
811
826
  const panelRef = useRef(null);
812
827
  const autoOpenedRef = useRef(false);
@@ -875,7 +890,9 @@ function BubbleWidget({
875
890
  }, [open]);
876
891
  const handleClose = () => {
877
892
  setOpen(false);
878
- localStorage.setItem(KEY_DISMISSED, String(Date.now() + 24 * 60 * 60 * 1e3));
893
+ if (!isControlled) {
894
+ localStorage.setItem(KEY_DISMISSED, String(Date.now() + 24 * 60 * 60 * 1e3));
895
+ }
879
896
  };
880
897
  const toggleExpanded = () => setExpanded((v) => {
881
898
  const next = !v;
@@ -922,7 +939,7 @@ function BubbleWidget({
922
939
  )
923
940
  }
924
941
  ),
925
- /* @__PURE__ */ jsx(
942
+ !hideBubble && /* @__PURE__ */ jsx(
926
943
  "button",
927
944
  {
928
945
  onClick: () => setOpen((v) => !v),
package/package.json CHANGED
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "private": false,
35
35
  "types": "./dist/index.d.ts",
36
- "version": "1.3.8",
36
+ "version": "1.4.0",
37
37
  "scripts": {
38
38
  "build": "tsup",
39
39
  "typecheck": "tsc --noEmit"