@informedai/react 0.4.12 → 0.4.14

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
@@ -109,6 +109,12 @@ interface InformedAssistantConfig {
109
109
  onSessionChange?: (session: Session) => void;
110
110
  /** Callback when an error occurs */
111
111
  onError?: (error: Error) => void;
112
+ /**
113
+ * Callback to get current field values from your form.
114
+ * Called before each message is sent so the AI knows about unsaved changes.
115
+ * Return the current values of all fields as they appear in your UI.
116
+ */
117
+ getCurrentFieldValues?: () => Record<string, unknown>;
112
118
  /** Custom theme overrides */
113
119
  theme?: Partial<WidgetTheme>;
114
120
  /** Position of the widget (for floating mode) */
@@ -267,14 +273,24 @@ declare class InformedAIClient {
267
273
  getSession(id: string): Promise<GetSessionResponse>;
268
274
  /**
269
275
  * Send a message to the session with SSE streaming.
276
+ *
277
+ * @param sessionId - The session ID
278
+ * @param message - The user's message
279
+ * @param onEvent - SSE event handler
280
+ * @param currentFieldValues - Current values from the UI form (for unsaved changes context)
270
281
  */
271
- sendMessage(sessionId: string, message: string, onEvent: (event: SSEEvent) => void): Promise<void>;
282
+ sendMessage(sessionId: string, message: string, onEvent: (event: SSEEvent) => void, currentFieldValues?: Record<string, unknown>): Promise<void>;
272
283
  /**
273
284
  * Send a quick action to the session.
274
285
  * For actions that trigger AI (task_action:*), returns SSE stream.
275
286
  * For other actions (select_task:*, resume_task:*), returns session directly.
287
+ *
288
+ * @param sessionId - The session ID
289
+ * @param action - The quick action string
290
+ * @param onEvent - Optional SSE event handler
291
+ * @param currentFieldValues - Current values from the UI form (for task_action context)
276
292
  */
277
- sendQuickAction(sessionId: string, action: string, onEvent?: (event: SSEEvent) => void): Promise<Session>;
293
+ sendQuickAction(sessionId: string, action: string, onEvent?: (event: SSEEvent) => void, currentFieldValues?: Record<string, unknown>): Promise<Session>;
278
294
  /**
279
295
  * Apply the pending value for the active task.
280
296
  */
package/dist/index.d.ts CHANGED
@@ -109,6 +109,12 @@ interface InformedAssistantConfig {
109
109
  onSessionChange?: (session: Session) => void;
110
110
  /** Callback when an error occurs */
111
111
  onError?: (error: Error) => void;
112
+ /**
113
+ * Callback to get current field values from your form.
114
+ * Called before each message is sent so the AI knows about unsaved changes.
115
+ * Return the current values of all fields as they appear in your UI.
116
+ */
117
+ getCurrentFieldValues?: () => Record<string, unknown>;
112
118
  /** Custom theme overrides */
113
119
  theme?: Partial<WidgetTheme>;
114
120
  /** Position of the widget (for floating mode) */
@@ -267,14 +273,24 @@ declare class InformedAIClient {
267
273
  getSession(id: string): Promise<GetSessionResponse>;
268
274
  /**
269
275
  * Send a message to the session with SSE streaming.
276
+ *
277
+ * @param sessionId - The session ID
278
+ * @param message - The user's message
279
+ * @param onEvent - SSE event handler
280
+ * @param currentFieldValues - Current values from the UI form (for unsaved changes context)
270
281
  */
271
- sendMessage(sessionId: string, message: string, onEvent: (event: SSEEvent) => void): Promise<void>;
282
+ sendMessage(sessionId: string, message: string, onEvent: (event: SSEEvent) => void, currentFieldValues?: Record<string, unknown>): Promise<void>;
272
283
  /**
273
284
  * Send a quick action to the session.
274
285
  * For actions that trigger AI (task_action:*), returns SSE stream.
275
286
  * For other actions (select_task:*, resume_task:*), returns session directly.
287
+ *
288
+ * @param sessionId - The session ID
289
+ * @param action - The quick action string
290
+ * @param onEvent - Optional SSE event handler
291
+ * @param currentFieldValues - Current values from the UI form (for task_action context)
276
292
  */
277
- sendQuickAction(sessionId: string, action: string, onEvent?: (event: SSEEvent) => void): Promise<Session>;
293
+ sendQuickAction(sessionId: string, action: string, onEvent?: (event: SSEEvent) => void, currentFieldValues?: Record<string, unknown>): Promise<Session>;
278
294
  /**
279
295
  * Apply the pending value for the active task.
280
296
  */
package/dist/index.js CHANGED
@@ -91,12 +91,17 @@ var InformedAIClient = class {
91
91
  }
92
92
  /**
93
93
  * Send a message to the session with SSE streaming.
94
+ *
95
+ * @param sessionId - The session ID
96
+ * @param message - The user's message
97
+ * @param onEvent - SSE event handler
98
+ * @param currentFieldValues - Current values from the UI form (for unsaved changes context)
94
99
  */
95
- async sendMessage(sessionId, message, onEvent) {
100
+ async sendMessage(sessionId, message, onEvent, currentFieldValues) {
96
101
  const response = await fetch(`${this.apiUrl}/widget/sessions/${sessionId}/message`, {
97
102
  method: "POST",
98
103
  headers: this.getHeaders(),
99
- body: JSON.stringify({ message })
104
+ body: JSON.stringify({ message, currentFieldValues })
100
105
  });
101
106
  if (!response.ok) {
102
107
  const error = await response.json().catch(() => ({ error: "Request failed" }));
@@ -108,12 +113,17 @@ var InformedAIClient = class {
108
113
  * Send a quick action to the session.
109
114
  * For actions that trigger AI (task_action:*), returns SSE stream.
110
115
  * For other actions (select_task:*, resume_task:*), returns session directly.
116
+ *
117
+ * @param sessionId - The session ID
118
+ * @param action - The quick action string
119
+ * @param onEvent - Optional SSE event handler
120
+ * @param currentFieldValues - Current values from the UI form (for task_action context)
111
121
  */
112
- async sendQuickAction(sessionId, action, onEvent) {
122
+ async sendQuickAction(sessionId, action, onEvent, currentFieldValues) {
113
123
  const response = await fetch(`${this.apiUrl}/widget/sessions/${sessionId}/quick-action`, {
114
124
  method: "POST",
115
125
  headers: this.getHeaders(),
116
- body: JSON.stringify({ action })
126
+ body: JSON.stringify({ action, currentFieldValues })
117
127
  });
118
128
  if (!response.ok) {
119
129
  const error = await response.json().catch(() => ({ error: "Request failed" }));
@@ -575,7 +585,8 @@ function InformedAIProvider({ config, children }) {
575
585
  setIsStreaming(true);
576
586
  setStreamingContent("");
577
587
  setError(null);
578
- await clientRef.current.sendMessage(session.id, message, handleSSEEvent);
588
+ const currentFieldValues = config.getCurrentFieldValues?.();
589
+ await clientRef.current.sendMessage(session.id, message, handleSSEEvent, currentFieldValues);
579
590
  } catch (err) {
580
591
  if (isSessionNotFoundError(err)) {
581
592
  await handleSessionDeleted();
@@ -594,10 +605,12 @@ function InformedAIProvider({ config, children }) {
594
605
  setIsStreaming(true);
595
606
  setStreamingContent("");
596
607
  setError(null);
608
+ const currentFieldValues = action.startsWith("task_action:") ? config.getCurrentFieldValues?.() : void 0;
597
609
  const newSession = await clientRef.current.sendQuickAction(
598
610
  session.id,
599
611
  action,
600
- handleSSEEvent
612
+ handleSSEEvent,
613
+ currentFieldValues
601
614
  );
602
615
  setSession(newSession);
603
616
  config.onSessionChange?.(newSession);
@@ -742,14 +755,15 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
742
755
  streamingContent,
743
756
  sendMessage,
744
757
  sendQuickAction,
745
- applyPendingValue,
746
- skipTask
758
+ applyPendingValue
747
759
  } = useInformedAI();
748
760
  const [isCollapsed, setIsCollapsed] = (0, import_react2.useState)(defaultCollapsed);
749
761
  const [inputValue, setInputValue] = (0, import_react2.useState)("");
762
+ const [showPendingActions, setShowPendingActions] = (0, import_react2.useState)(true);
750
763
  const messagesEndRef = (0, import_react2.useRef)(null);
751
764
  const inputRef = (0, import_react2.useRef)(null);
752
765
  const wasStreamingRef = (0, import_react2.useRef)(false);
766
+ const lastPendingValueRef = (0, import_react2.useRef)(void 0);
753
767
  (0, import_react2.useEffect)(() => {
754
768
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
755
769
  }, [session?.widgetMessages, streamingContent]);
@@ -759,6 +773,15 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
759
773
  }
760
774
  wasStreamingRef.current = isStreaming;
761
775
  }, [isStreaming]);
776
+ const activeTask = session?.activeTask;
777
+ const pendingValue = activeTask ? session?.taskStates[activeTask]?.pendingValue : void 0;
778
+ const hasPendingValue = pendingValue !== void 0 && pendingValue !== null;
779
+ (0, import_react2.useEffect)(() => {
780
+ if (hasPendingValue && pendingValue !== lastPendingValueRef.current) {
781
+ setShowPendingActions(true);
782
+ }
783
+ lastPendingValueRef.current = pendingValue;
784
+ }, [hasPendingValue, pendingValue]);
762
785
  const handleSubmit = async (e) => {
763
786
  e.preventDefault();
764
787
  if (!inputValue.trim() || isStreaming) return;
@@ -769,9 +792,6 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
769
792
  const handleQuickAction = async (action) => {
770
793
  await sendQuickAction(action.action, action.payload);
771
794
  };
772
- const activeTask = session?.activeTask;
773
- const pendingValue = activeTask ? session?.taskStates[activeTask]?.pendingValue : void 0;
774
- const hasPendingValue = pendingValue !== void 0 && pendingValue !== null;
775
795
  const cssVars = {
776
796
  "--ia-primary": theme.primaryColor,
777
797
  "--ia-bg": theme.backgroundColor,
@@ -968,7 +988,7 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
968
988
  ]
969
989
  }
970
990
  ),
971
- hasPendingValue && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
991
+ hasPendingValue && showPendingActions ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
972
992
  "div",
973
993
  {
974
994
  style: {
@@ -1002,7 +1022,7 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
1002
1022
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1003
1023
  "button",
1004
1024
  {
1005
- onClick: skipTask,
1025
+ onClick: () => setShowPendingActions(false),
1006
1026
  disabled: isStreaming,
1007
1027
  style: {
1008
1028
  padding: "10px 16px",
@@ -1015,13 +1035,12 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
1015
1035
  fontSize: "14px",
1016
1036
  opacity: isStreaming ? 0.5 : 1
1017
1037
  },
1018
- children: "Skip"
1038
+ children: "Not yet"
1019
1039
  }
1020
1040
  )
1021
1041
  ]
1022
1042
  }
1023
- ),
1024
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1043
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1025
1044
  "form",
1026
1045
  {
1027
1046
  onSubmit: handleSubmit,
package/dist/index.mjs CHANGED
@@ -61,12 +61,17 @@ var InformedAIClient = class {
61
61
  }
62
62
  /**
63
63
  * Send a message to the session with SSE streaming.
64
+ *
65
+ * @param sessionId - The session ID
66
+ * @param message - The user's message
67
+ * @param onEvent - SSE event handler
68
+ * @param currentFieldValues - Current values from the UI form (for unsaved changes context)
64
69
  */
65
- async sendMessage(sessionId, message, onEvent) {
70
+ async sendMessage(sessionId, message, onEvent, currentFieldValues) {
66
71
  const response = await fetch(`${this.apiUrl}/widget/sessions/${sessionId}/message`, {
67
72
  method: "POST",
68
73
  headers: this.getHeaders(),
69
- body: JSON.stringify({ message })
74
+ body: JSON.stringify({ message, currentFieldValues })
70
75
  });
71
76
  if (!response.ok) {
72
77
  const error = await response.json().catch(() => ({ error: "Request failed" }));
@@ -78,12 +83,17 @@ var InformedAIClient = class {
78
83
  * Send a quick action to the session.
79
84
  * For actions that trigger AI (task_action:*), returns SSE stream.
80
85
  * For other actions (select_task:*, resume_task:*), returns session directly.
86
+ *
87
+ * @param sessionId - The session ID
88
+ * @param action - The quick action string
89
+ * @param onEvent - Optional SSE event handler
90
+ * @param currentFieldValues - Current values from the UI form (for task_action context)
81
91
  */
82
- async sendQuickAction(sessionId, action, onEvent) {
92
+ async sendQuickAction(sessionId, action, onEvent, currentFieldValues) {
83
93
  const response = await fetch(`${this.apiUrl}/widget/sessions/${sessionId}/quick-action`, {
84
94
  method: "POST",
85
95
  headers: this.getHeaders(),
86
- body: JSON.stringify({ action })
96
+ body: JSON.stringify({ action, currentFieldValues })
87
97
  });
88
98
  if (!response.ok) {
89
99
  const error = await response.json().catch(() => ({ error: "Request failed" }));
@@ -545,7 +555,8 @@ function InformedAIProvider({ config, children }) {
545
555
  setIsStreaming(true);
546
556
  setStreamingContent("");
547
557
  setError(null);
548
- await clientRef.current.sendMessage(session.id, message, handleSSEEvent);
558
+ const currentFieldValues = config.getCurrentFieldValues?.();
559
+ await clientRef.current.sendMessage(session.id, message, handleSSEEvent, currentFieldValues);
549
560
  } catch (err) {
550
561
  if (isSessionNotFoundError(err)) {
551
562
  await handleSessionDeleted();
@@ -564,10 +575,12 @@ function InformedAIProvider({ config, children }) {
564
575
  setIsStreaming(true);
565
576
  setStreamingContent("");
566
577
  setError(null);
578
+ const currentFieldValues = action.startsWith("task_action:") ? config.getCurrentFieldValues?.() : void 0;
567
579
  const newSession = await clientRef.current.sendQuickAction(
568
580
  session.id,
569
581
  action,
570
- handleSSEEvent
582
+ handleSSEEvent,
583
+ currentFieldValues
571
584
  );
572
585
  setSession(newSession);
573
586
  config.onSessionChange?.(newSession);
@@ -712,14 +725,15 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
712
725
  streamingContent,
713
726
  sendMessage,
714
727
  sendQuickAction,
715
- applyPendingValue,
716
- skipTask
728
+ applyPendingValue
717
729
  } = useInformedAI();
718
730
  const [isCollapsed, setIsCollapsed] = useState2(defaultCollapsed);
719
731
  const [inputValue, setInputValue] = useState2("");
732
+ const [showPendingActions, setShowPendingActions] = useState2(true);
720
733
  const messagesEndRef = useRef2(null);
721
734
  const inputRef = useRef2(null);
722
735
  const wasStreamingRef = useRef2(false);
736
+ const lastPendingValueRef = useRef2(void 0);
723
737
  useEffect2(() => {
724
738
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
725
739
  }, [session?.widgetMessages, streamingContent]);
@@ -729,6 +743,15 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
729
743
  }
730
744
  wasStreamingRef.current = isStreaming;
731
745
  }, [isStreaming]);
746
+ const activeTask = session?.activeTask;
747
+ const pendingValue = activeTask ? session?.taskStates[activeTask]?.pendingValue : void 0;
748
+ const hasPendingValue = pendingValue !== void 0 && pendingValue !== null;
749
+ useEffect2(() => {
750
+ if (hasPendingValue && pendingValue !== lastPendingValueRef.current) {
751
+ setShowPendingActions(true);
752
+ }
753
+ lastPendingValueRef.current = pendingValue;
754
+ }, [hasPendingValue, pendingValue]);
732
755
  const handleSubmit = async (e) => {
733
756
  e.preventDefault();
734
757
  if (!inputValue.trim() || isStreaming) return;
@@ -739,9 +762,6 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
739
762
  const handleQuickAction = async (action) => {
740
763
  await sendQuickAction(action.action, action.payload);
741
764
  };
742
- const activeTask = session?.activeTask;
743
- const pendingValue = activeTask ? session?.taskStates[activeTask]?.pendingValue : void 0;
744
- const hasPendingValue = pendingValue !== void 0 && pendingValue !== null;
745
765
  const cssVars = {
746
766
  "--ia-primary": theme.primaryColor,
747
767
  "--ia-bg": theme.backgroundColor,
@@ -938,7 +958,7 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
938
958
  ]
939
959
  }
940
960
  ),
941
- hasPendingValue && /* @__PURE__ */ jsxs(
961
+ hasPendingValue && showPendingActions ? /* @__PURE__ */ jsxs(
942
962
  "div",
943
963
  {
944
964
  style: {
@@ -972,7 +992,7 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
972
992
  /* @__PURE__ */ jsx2(
973
993
  "button",
974
994
  {
975
- onClick: skipTask,
995
+ onClick: () => setShowPendingActions(false),
976
996
  disabled: isStreaming,
977
997
  style: {
978
998
  padding: "10px 16px",
@@ -985,13 +1005,12 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
985
1005
  fontSize: "14px",
986
1006
  opacity: isStreaming ? 0.5 : 1
987
1007
  },
988
- children: "Skip"
1008
+ children: "Not yet"
989
1009
  }
990
1010
  )
991
1011
  ]
992
1012
  }
993
- ),
994
- /* @__PURE__ */ jsxs(
1013
+ ) : /* @__PURE__ */ jsxs(
995
1014
  "form",
996
1015
  {
997
1016
  onSubmit: handleSubmit,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@informedai/react",
3
- "version": "0.4.12",
3
+ "version": "0.4.14",
4
4
  "description": "React SDK for InformedAI Assistant - AI-powered content creation widget",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",