@xbrain-swe/nova-chat-widget 0.1.10 → 0.1.11

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.cts CHANGED
@@ -80,6 +80,12 @@ type XbNovaClientAction = {
80
80
  type XbNovaClientActions = Record<string, XbNovaClientAction>;
81
81
 
82
82
  type FactValue = unknown | (() => unknown);
83
+ /** `useXbNovaContext`로 호스트가 AI에게 알려주는 상품·화면 부가 설명 한 건. 내용은 전부
84
+ * 호스트 자율이다 — 가입 됨/안됨 같은 판단도 호스트가 `description`에 문장으로 쓴다. */
85
+ type XbNovaContextEntry = {
86
+ title: string;
87
+ description: string;
88
+ };
83
89
  /** 위젯 아바타·pill 배경 그라디언트에 쓰는 두 색상 — 기본값은 SDK 자체 톤(인디고→바이올렛)
84
90
  * 이고, 소비자가 `<XbNovaProvider theme={{accentFrom, accentTo}}>`로 자기 브랜드 컬러를
85
91
  * 넘기면 그걸 쓴다("카통령 브랜드 컬러를 따르게 해달라" — 파랑/보라가 사이트 톤과 안
@@ -158,6 +164,10 @@ type UseXbNovaChatResult = {
158
164
  declare function useXbNovaChat(options?: {
159
165
  botId?: string;
160
166
  nudgeNodeId?: string;
167
+ /** 위젯이 첫 말풍선으로 보여준 문구(넛지 문구). 첫 메시지를 보낼 때 서버에 같이 보내 AI가
168
+ * "방금 이런 말을 건넸다"는 걸 알게 한다 — 없으면 봇 인사말을 쓴다. 서버가 설정과 일치하는지
169
+ * 검증해서 맞는 것만 이력에 저장한다. */
170
+ openingMessage?: string;
161
171
  onDismissSheet?: () => void;
162
172
  onFeedback?: (e: XbNovaFeedbackEvent) => void;
163
173
  }): UseXbNovaChatResult;
@@ -249,6 +259,11 @@ declare function useXbNovaFact(key: string, value: FactValue): void;
249
259
  /** 필드가 여러 개인 객체 하나를 한 번에 등록한다 — 매물 데이터처럼. `useXbNovaFact`를
250
260
  * 필드마다 반복하는 것과 같다. */
251
261
  declare function useXbNovaFacts(values: Record<string, FactValue>): void;
262
+ /** AI에게 알려줄 상품·화면 부가 설명을 등록한다. 넛지 조건용인 facts와 달리 **챗 요청의
263
+ * `context`로 전송**되어 답변의 근거가 된다. 내용(가입 가능 여부 같은 판단 포함)은 호스트가
264
+ * `description`에 문장으로 쓴다 — SDK는 형식 검증과 전달만 한다. 배열 또는 함수(pull) 둘 다
265
+ * 된다. 컴포넌트가 언마운트되면 자동으로 빠진다. */
266
+ declare function useXbNovaContext(entries: XbNovaContextEntry[] | (() => XbNovaContextEntry[])): void;
252
267
  /** 방문자 식별자 — 없으면 SDK가 즉시 만들어 localStorage에 고정한 뒤 그 값을 반환한다. */
253
268
  declare function useXbNovaVisitor(): string;
254
269
  /** 선언형 — 서버 컴포넌트 트리에서 훅을 못 쓸 때. `<XbNovaFact id="xbnova_summary" value="..." />`. */
@@ -274,4 +289,4 @@ declare function useXbNovaSettings(): {
274
289
  showBar: () => void;
275
290
  };
276
291
 
277
- export { type ChatMessage, type FactValue, type MessagePart, type ProductItem, type TextPart, type ToolPart, type UseXbNovaChatResult, type VerdictAction, type VerdictOutput, type ViewKind, type ViewRenderer, type ViewRendererMap, XbNovaChat, type XbNovaClientAction, type XbNovaClientActionResult, type XbNovaClientActions, XbNovaFact, XbNovaFacts, type XbNovaFeedbackEvent, type XbNovaFeedbackRating, type XbNovaFeedbackReason, type XbNovaOnboarding, XbNovaProvider, type XbNovaTheme, XbNovaTrack, trackXbNovaEvent, useXbNovaChat, useXbNovaFact, useXbNovaFacts, useXbNovaSettings, useXbNovaTrack, useXbNovaVisitor };
292
+ export { type ChatMessage, type FactValue, type MessagePart, type ProductItem, type TextPart, type ToolPart, type UseXbNovaChatResult, type VerdictAction, type VerdictOutput, type ViewKind, type ViewRenderer, type ViewRendererMap, XbNovaChat, type XbNovaClientAction, type XbNovaClientActionResult, type XbNovaClientActions, type XbNovaContextEntry, XbNovaFact, XbNovaFacts, type XbNovaFeedbackEvent, type XbNovaFeedbackRating, type XbNovaFeedbackReason, type XbNovaOnboarding, XbNovaProvider, type XbNovaTheme, XbNovaTrack, trackXbNovaEvent, useXbNovaChat, useXbNovaContext, useXbNovaFact, useXbNovaFacts, useXbNovaSettings, useXbNovaTrack, useXbNovaVisitor };
package/dist/index.d.ts CHANGED
@@ -80,6 +80,12 @@ type XbNovaClientAction = {
80
80
  type XbNovaClientActions = Record<string, XbNovaClientAction>;
81
81
 
82
82
  type FactValue = unknown | (() => unknown);
83
+ /** `useXbNovaContext`로 호스트가 AI에게 알려주는 상품·화면 부가 설명 한 건. 내용은 전부
84
+ * 호스트 자율이다 — 가입 됨/안됨 같은 판단도 호스트가 `description`에 문장으로 쓴다. */
85
+ type XbNovaContextEntry = {
86
+ title: string;
87
+ description: string;
88
+ };
83
89
  /** 위젯 아바타·pill 배경 그라디언트에 쓰는 두 색상 — 기본값은 SDK 자체 톤(인디고→바이올렛)
84
90
  * 이고, 소비자가 `<XbNovaProvider theme={{accentFrom, accentTo}}>`로 자기 브랜드 컬러를
85
91
  * 넘기면 그걸 쓴다("카통령 브랜드 컬러를 따르게 해달라" — 파랑/보라가 사이트 톤과 안
@@ -158,6 +164,10 @@ type UseXbNovaChatResult = {
158
164
  declare function useXbNovaChat(options?: {
159
165
  botId?: string;
160
166
  nudgeNodeId?: string;
167
+ /** 위젯이 첫 말풍선으로 보여준 문구(넛지 문구). 첫 메시지를 보낼 때 서버에 같이 보내 AI가
168
+ * "방금 이런 말을 건넸다"는 걸 알게 한다 — 없으면 봇 인사말을 쓴다. 서버가 설정과 일치하는지
169
+ * 검증해서 맞는 것만 이력에 저장한다. */
170
+ openingMessage?: string;
161
171
  onDismissSheet?: () => void;
162
172
  onFeedback?: (e: XbNovaFeedbackEvent) => void;
163
173
  }): UseXbNovaChatResult;
@@ -249,6 +259,11 @@ declare function useXbNovaFact(key: string, value: FactValue): void;
249
259
  /** 필드가 여러 개인 객체 하나를 한 번에 등록한다 — 매물 데이터처럼. `useXbNovaFact`를
250
260
  * 필드마다 반복하는 것과 같다. */
251
261
  declare function useXbNovaFacts(values: Record<string, FactValue>): void;
262
+ /** AI에게 알려줄 상품·화면 부가 설명을 등록한다. 넛지 조건용인 facts와 달리 **챗 요청의
263
+ * `context`로 전송**되어 답변의 근거가 된다. 내용(가입 가능 여부 같은 판단 포함)은 호스트가
264
+ * `description`에 문장으로 쓴다 — SDK는 형식 검증과 전달만 한다. 배열 또는 함수(pull) 둘 다
265
+ * 된다. 컴포넌트가 언마운트되면 자동으로 빠진다. */
266
+ declare function useXbNovaContext(entries: XbNovaContextEntry[] | (() => XbNovaContextEntry[])): void;
252
267
  /** 방문자 식별자 — 없으면 SDK가 즉시 만들어 localStorage에 고정한 뒤 그 값을 반환한다. */
253
268
  declare function useXbNovaVisitor(): string;
254
269
  /** 선언형 — 서버 컴포넌트 트리에서 훅을 못 쓸 때. `<XbNovaFact id="xbnova_summary" value="..." />`. */
@@ -274,4 +289,4 @@ declare function useXbNovaSettings(): {
274
289
  showBar: () => void;
275
290
  };
276
291
 
277
- export { type ChatMessage, type FactValue, type MessagePart, type ProductItem, type TextPart, type ToolPart, type UseXbNovaChatResult, type VerdictAction, type VerdictOutput, type ViewKind, type ViewRenderer, type ViewRendererMap, XbNovaChat, type XbNovaClientAction, type XbNovaClientActionResult, type XbNovaClientActions, XbNovaFact, XbNovaFacts, type XbNovaFeedbackEvent, type XbNovaFeedbackRating, type XbNovaFeedbackReason, type XbNovaOnboarding, XbNovaProvider, type XbNovaTheme, XbNovaTrack, trackXbNovaEvent, useXbNovaChat, useXbNovaFact, useXbNovaFacts, useXbNovaSettings, useXbNovaTrack, useXbNovaVisitor };
292
+ export { type ChatMessage, type FactValue, type MessagePart, type ProductItem, type TextPart, type ToolPart, type UseXbNovaChatResult, type VerdictAction, type VerdictOutput, type ViewKind, type ViewRenderer, type ViewRendererMap, XbNovaChat, type XbNovaClientAction, type XbNovaClientActionResult, type XbNovaClientActions, type XbNovaContextEntry, XbNovaFact, XbNovaFacts, type XbNovaFeedbackEvent, type XbNovaFeedbackRating, type XbNovaFeedbackReason, type XbNovaOnboarding, XbNovaProvider, type XbNovaTheme, XbNovaTrack, trackXbNovaEvent, useXbNovaChat, useXbNovaContext, useXbNovaFact, useXbNovaFacts, useXbNovaSettings, useXbNovaTrack, useXbNovaVisitor };
package/dist/index.js CHANGED
@@ -40,6 +40,17 @@ function resolveVisitorId() {
40
40
  function resolveFactValue(value) {
41
41
  return typeof value === "function" ? value() : value;
42
42
  }
43
+ function collectContextEntries(registry) {
44
+ const out = [];
45
+ for (const source of registry.values()) {
46
+ const entries = typeof source === "function" ? source() : source;
47
+ if (!Array.isArray(entries)) continue;
48
+ for (const e of entries) {
49
+ if (e && typeof e.title === "string" && typeof e.description === "string") out.push(e);
50
+ }
51
+ }
52
+ return out;
53
+ }
43
54
  function InternalProvider({
44
55
  auth,
45
56
  clientActionHandlers,
@@ -48,6 +59,7 @@ function InternalProvider({
48
59
  }) {
49
60
  const trackRegistry = useRef(/* @__PURE__ */ new Map()).current;
50
61
  const factsRegistry = useRef(/* @__PURE__ */ new Map()).current;
62
+ const contextRegistry = useRef(/* @__PURE__ */ new Map()).current;
51
63
  const emptyHandlers = useRef(/* @__PURE__ */ new Map()).current;
52
64
  const visitorIdRef = useRef(null);
53
65
  if (visitorIdRef.current === null) {
@@ -58,11 +70,12 @@ function InternalProvider({
58
70
  auth,
59
71
  trackRegistry,
60
72
  factsRegistry,
73
+ contextRegistry,
61
74
  clientActionHandlers: clientActionHandlers ?? emptyHandlers,
62
75
  visitorId: visitorIdRef.current,
63
76
  theme
64
77
  }),
65
- [auth, trackRegistry, factsRegistry, clientActionHandlers, emptyHandlers, theme]
78
+ [auth, trackRegistry, factsRegistry, contextRegistry, clientActionHandlers, emptyHandlers, theme]
66
79
  );
67
80
  return /* @__PURE__ */ jsx(InternalContext.Provider, { value, children });
68
81
  }
@@ -1089,7 +1102,7 @@ async function resumeToolResult(apiBaseUrl, apiKey, body, handlers) {
1089
1102
  // src/use-chat.ts
1090
1103
  var DEFAULT_CLIENT_ACTION_TIMEOUT_MS = 15e3;
1091
1104
  function useXbNovaChat(options) {
1092
- const { auth, trackRegistry, clientActionHandlers, visitorId } = useXbNovaInternal();
1105
+ const { auth, trackRegistry, contextRegistry, clientActionHandlers, visitorId } = useXbNovaInternal();
1093
1106
  const botId = options?.botId ?? auth.botId;
1094
1107
  const nudgeNodeId = options?.nudgeNodeId;
1095
1108
  const { apiKey, apiBaseUrl } = auth;
@@ -1241,7 +1254,9 @@ function useXbNovaChat(options) {
1241
1254
  return next;
1242
1255
  });
1243
1256
  const focused = getFocusedTrackEntry(trackRegistry);
1244
- const context = focused ? { region: focused.region, data: resolveFactValue(focused.data) } : void 0;
1257
+ const notes = collectContextEntries(contextRegistry);
1258
+ const screen = focused ? { region: focused.region, data: resolveFactValue(focused.data) } : {};
1259
+ const context = focused || notes.length ? { ...screen, ...notes.length ? { notes } : {} } : void 0;
1245
1260
  try {
1246
1261
  await streamChat(
1247
1262
  apiBaseUrl,
@@ -1251,7 +1266,8 @@ function useXbNovaChat(options) {
1251
1266
  message: trimmed,
1252
1267
  conversation_id: onboarding?.conversation_id ?? null,
1253
1268
  context,
1254
- nudge_node_id: nudgeNodeId ?? null
1269
+ nudge_node_id: nudgeNodeId ?? null,
1270
+ opening_message: messages.length === 0 ? (options?.openingMessage ?? onboarding?.greeting) || null : null
1255
1271
  },
1256
1272
  { onPart: replaceLast, onMessageId: setLastAssistantId, onError: setError }
1257
1273
  );
@@ -1550,6 +1566,7 @@ function XbNovaChat({
1550
1566
  onFeedback,
1551
1567
  botId: activeBotId ?? botId,
1552
1568
  nudgeNodeId: activeNudgeNodeId,
1569
+ openingMessage: openingLine ?? void 0,
1553
1570
  onDismissSheet: () => setOpen(false)
1554
1571
  });
1555
1572
  const effectiveBotId = activeBotId ?? botId;
@@ -2124,7 +2141,7 @@ function AnswerActionsFrame({
2124
2141
  }
2125
2142
 
2126
2143
  // src/facts.ts
2127
- import { useEffect as useEffect9, useRef as useRef9 } from "react";
2144
+ import { useEffect as useEffect9, useId, useRef as useRef9 } from "react";
2128
2145
  function useXbNovaFact(key, value) {
2129
2146
  const { factsRegistry } = useXbNovaInternal();
2130
2147
  useEffect9(() => {
@@ -2153,6 +2170,18 @@ function useXbNovaFacts(values) {
2153
2170
  };
2154
2171
  }, [factsRegistry]);
2155
2172
  }
2173
+ function useXbNovaContext(entries) {
2174
+ const { contextRegistry } = useXbNovaInternal();
2175
+ const id = useId();
2176
+ useEffect9(() => {
2177
+ contextRegistry.set(id, entries);
2178
+ });
2179
+ useEffect9(() => {
2180
+ return () => {
2181
+ contextRegistry.delete(id);
2182
+ };
2183
+ }, [id, contextRegistry]);
2184
+ }
2156
2185
  function useXbNovaVisitor() {
2157
2186
  const { visitorId } = useXbNovaInternal();
2158
2187
  return visitorId;
@@ -2173,6 +2202,7 @@ export {
2173
2202
  XbNovaTrack,
2174
2203
  trackXbNovaEvent,
2175
2204
  useXbNovaChat,
2205
+ useXbNovaContext,
2176
2206
  useXbNovaFact,
2177
2207
  useXbNovaFacts,
2178
2208
  useXbNovaSettings,