@sybilion/uilib 1.3.87 → 1.3.89

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.
Files changed (41) hide show
  1. package/dist/esm/components/ui/Chat/ChatMessage/AgentMessageContent.helpers.js +29 -21
  2. package/dist/esm/components/ui/Chat/ChatMessage/ChatMessage.js +2 -1
  3. package/dist/esm/components/ui/Chat/ChatSheet/ChatSheet.js +1 -0
  4. package/dist/esm/components/ui/Chat/ChatSheet/useChatPanelChromeModel.js +36 -10
  5. package/dist/esm/components/ui/Sidebar/Sidebar.js +23 -7
  6. package/dist/esm/contexts/chat-context.js +180 -205
  7. package/dist/esm/contexts/chatPersistence.js +6 -18
  8. package/dist/esm/contexts/chatSessionStorage.js +245 -0
  9. package/dist/esm/lib/dashboard-spec/jsonDashboardFence.js +80 -0
  10. package/dist/esm/types/src/components/ui/Chat/ChatEmptyState/ChatEmptyState.types.d.ts +5 -1
  11. package/dist/esm/types/src/components/ui/Chat/ChatMessage/AgentMessageContent.helpers.d.ts +1 -5
  12. package/dist/esm/types/src/components/ui/Chat/ChatMessage/AgentMessageContent.helpers.test.d.ts +1 -0
  13. package/dist/esm/types/src/components/ui/Chat/ChatSheet/ChatSheet.d.ts +2 -0
  14. package/dist/esm/types/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.d.ts +8 -1
  15. package/dist/esm/types/src/components/ui/Chat/index.d.ts +1 -1
  16. package/dist/esm/types/src/components/ui/Sidebar/Sidebar.d.ts +2 -0
  17. package/dist/esm/types/src/contexts/chat-context.d.ts +6 -0
  18. package/dist/esm/types/src/contexts/chatPersistence.d.ts +4 -1
  19. package/dist/esm/types/src/contexts/chatSessionStorage.d.ts +32 -0
  20. package/dist/esm/types/src/contexts/chatSessionStorage.test.d.ts +1 -0
  21. package/dist/esm/types/src/lib/dashboard-spec/jsonDashboardFence.d.ts +9 -0
  22. package/dist/esm/types/src/lib/dashboard-spec/stripJsonDashboardFences.d.ts +1 -2
  23. package/dist/esm/types/src/lib/dashboard-spec/stripJsonDashboardFences.test.d.ts +1 -0
  24. package/package.json +1 -1
  25. package/src/components/ui/Chat/ChatEmptyState/ChatEmptyState.types.ts +9 -1
  26. package/src/components/ui/Chat/ChatMessage/AgentMessageContent.helpers.test.tsx +25 -0
  27. package/src/components/ui/Chat/ChatMessage/AgentMessageContent.helpers.tsx +35 -26
  28. package/src/components/ui/Chat/ChatMessage/ChatMessage.tsx +2 -1
  29. package/src/components/ui/Chat/ChatSheet/ChatSheet.tsx +3 -0
  30. package/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.tsx +55 -10
  31. package/src/components/ui/Chat/index.ts +1 -0
  32. package/src/components/ui/Sidebar/Sidebar.tsx +27 -8
  33. package/src/contexts/chat-context.tsx +253 -220
  34. package/src/contexts/chatPersistence.test.ts +11 -0
  35. package/src/contexts/chatPersistence.ts +22 -6
  36. package/src/contexts/chatSessionStorage.test.ts +125 -0
  37. package/src/contexts/chatSessionStorage.ts +321 -0
  38. package/src/lib/dashboard-spec/jsonDashboardFence.ts +98 -0
  39. package/src/lib/dashboard-spec/stripJsonDashboardFences.test.ts +84 -0
  40. package/src/lib/dashboard-spec/stripJsonDashboardFences.ts +5 -6
  41. package/dist/esm/lib/dashboard-spec/stripJsonDashboardFences.js +0 -7
@@ -13,6 +13,8 @@ export type ChatSheetActions = {
13
13
  openNewChat: () => void;
14
14
  /** Starts a new chat, opens the panel, and pre-fills the composer (user sends manually). */
15
15
  openNewChatWithPrefill: (prompt: string) => void;
16
+ /** Starts a new chat, opens panel, and sends prompt immediately. */
17
+ openNewChatAndSubmit: (prompt: string) => void;
16
18
  };
17
19
 
18
20
  export interface ChatSheetProps extends Omit<
@@ -79,6 +81,7 @@ export function ChatSheet({
79
81
  model.onOpenChange(true);
80
82
  },
81
83
  openNewChatWithPrefill: model.openNewChatWithPrefill,
84
+ openNewChatAndSubmit: model.openNewChatAndSubmit,
82
85
  };
83
86
  }
84
87
 
@@ -51,7 +51,10 @@ import {
51
51
  } from '../../Sidebar/Sidebar';
52
52
  import { Chat } from '../Chat';
53
53
  import type { ChatChromeProps } from '../ChatChrome';
54
- import type { ChatAttachmentDropItem, ChatPresetsRenderOptions } from '../ChatChrome/ChatChrome.types';
54
+ import type {
55
+ ChatAttachmentDropItem,
56
+ ChatPresetsRenderOptions,
57
+ } from '../ChatChrome/ChatChrome.types';
55
58
  import type { ChatEmptyStateConfig } from '../ChatEmptyState/ChatEmptyState.types';
56
59
  import type { ChatEmptyStateProps } from '../ChatEmptyState/ChatEmptyState.types';
57
60
  import {
@@ -108,6 +111,11 @@ export type UseChatPanelChromeModelInput = {
108
111
  | string
109
112
  | ChatSendMessagePayload
110
113
  | Promise<string | ChatSendMessagePayload>;
114
+ /**
115
+ * When true, preset clicks always use the API send path (skip local `answer` /
116
+ * script demo). Use on /reports/new where first message must hit transformSendPayload.
117
+ */
118
+ submitPresetsViaApi?: boolean;
111
119
  };
112
120
 
113
121
  export type UseChatPanelChromeModelResult = {
@@ -118,6 +126,8 @@ export type UseChatPanelChromeModelResult = {
118
126
  newChat: () => void;
119
127
  /** New session + open panel + one-shot composer pre-fill (does not send). */
120
128
  openNewChatWithPrefill: (prompt: string) => void;
129
+ /** New session + open panel + immediate send. */
130
+ openNewChatAndSubmit: (prompt: string) => void;
121
131
  chatPanelContainer: HTMLElement | null;
122
132
  };
123
133
 
@@ -160,6 +170,7 @@ export function useChatPanelChromeModel({
160
170
  onSlashItemCommand,
161
171
  copyHistoryOnNewChat = false,
162
172
  transformSendPayload,
173
+ submitPresetsViaApi = false,
163
174
  }: UseChatPanelChromeModelInput): UseChatPanelChromeModelResult {
164
175
  const effectiveScopeId = scopeId ?? NO_SCOPE_FALLBACK;
165
176
  const isMobile = useIsMobile();
@@ -209,6 +220,10 @@ export function useChatPanelChromeModel({
209
220
  );
210
221
  /** Deduplicate Strict Mode double `useEffect` on the same mount. */
211
222
  const promptParamHandledInEffectRef = useRef(false);
223
+ const pendingOpenChatAndSubmitRef = useRef<{
224
+ sessionId: string;
225
+ prompt: string;
226
+ } | null>(null);
212
227
  const prevShellChatPanelOpenRef = useRef(shellChatPanelOpen);
213
228
  /** This instance requested shell chat width (ignore other ChatSheet instances on the page). */
214
229
  const openedShellChatRef = useRef(false);
@@ -405,6 +420,23 @@ export function useChatPanelChromeModel({
405
420
  [startEmptyNewChat, onOpenChange],
406
421
  );
407
422
 
423
+ const openNewChatAndSubmit = useCallback(
424
+ (prompt: string) => {
425
+ const sessionId = startEmptyNewChat();
426
+ if (sessionId == null) {
427
+ logger.warn('Chat submit: sign in to use the assistant.');
428
+ return;
429
+ }
430
+
431
+ const trimmed = prompt.trim();
432
+ if (!trimmed) return;
433
+
434
+ pendingOpenChatAndSubmitRef.current = { sessionId, prompt: trimmed };
435
+ onOpenChange(true);
436
+ },
437
+ [startEmptyNewChat, onOpenChange],
438
+ );
439
+
408
440
  /**
409
441
  * App link: `?prompt=…` — open panel, pre-fill composer, strip param (read once on mount
410
442
  * from `location.search`). If the selected session already has messages, `newChat()` first.
@@ -567,7 +599,7 @@ export function useChatPanelChromeModel({
567
599
  try {
568
600
  const { response: assistantResponse, sessionId } =
569
601
  await sendMessage(payload);
570
- onMessage?.(
602
+ await onMessage?.(
571
603
  displayTextFromSendPayload(payload),
572
604
  assistantResponse,
573
605
  sessionId,
@@ -735,7 +767,7 @@ export function useChatPanelChromeModel({
735
767
  try {
736
768
  const { response: assistantResponse, sessionId } =
737
769
  await sendMessage(payload);
738
- onMessage?.(
770
+ await onMessage?.(
739
771
  displayTextFromSendPayload(payload),
740
772
  assistantResponse,
741
773
  sessionId,
@@ -763,23 +795,31 @@ export function useChatPanelChromeModel({
763
795
  ],
764
796
  );
765
797
 
798
+ useEffect(() => {
799
+ const pending = pendingOpenChatAndSubmitRef.current;
800
+ if (!pending || currentChatId !== pending.sessionId) return;
801
+ pendingOpenChatAndSubmitRef.current = null;
802
+ void handlePromptSubmit(pending.prompt);
803
+ }, [currentChatId, handlePromptSubmit]);
804
+
766
805
  const submitPreset = useCallback(
767
- async (preset: ChatPreset) => {
806
+ async (preset: ChatPreset, options?: { message?: string }) => {
768
807
  const script = preset.script;
769
808
  const scriptGraph = isPresetScriptGraph(script);
770
809
  const hasLinearScript = Array.isArray(script) && script.length > 0;
771
810
  const hasReplies =
772
811
  preset.replies && Object.keys(preset.replies).length > 0;
773
812
  const isLocalDemo =
774
- hasLinearScript ||
775
- scriptGraph ||
776
- Boolean(preset.answer?.trim()) ||
777
- Boolean(hasReplies);
813
+ !submitPresetsViaApi &&
814
+ (hasLinearScript ||
815
+ scriptGraph ||
816
+ Boolean(preset.answer?.trim()) ||
817
+ Boolean(hasReplies));
778
818
 
779
819
  if (!isLocalDemo) {
780
820
  if (!currentChatId) return;
781
821
  endLocalDemoFlow(currentChatId);
782
- await handlePromptSubmit(preset.text);
822
+ await handlePromptSubmit(options?.message ?? preset.text);
783
823
  return;
784
824
  }
785
825
 
@@ -862,6 +902,7 @@ export function useChatPanelChromeModel({
862
902
  handlePromptSubmit,
863
903
  addMessage,
864
904
  presetsWithFreeform,
905
+ submitPresetsViaApi,
865
906
  ],
866
907
  );
867
908
 
@@ -1089,7 +1130,10 @@ export function useChatPanelChromeModel({
1089
1130
  })
1090
1131
  ) {
1091
1132
  // Chat open uses `startViewTransition`; nested transition here aborts first → AbortError overlay.
1092
- setSidebarNavOpen(false, { viewTransition: false });
1133
+ setSidebarNavOpen(false, {
1134
+ viewTransition: false,
1135
+ layoutAutoClose: true,
1136
+ });
1093
1137
  }
1094
1138
  };
1095
1139
 
@@ -1301,6 +1345,7 @@ export function useChatPanelChromeModel({
1301
1345
  toggleOpen,
1302
1346
  newChat: startEmptyNewChat,
1303
1347
  openNewChatWithPrefill,
1348
+ openNewChatAndSubmit,
1304
1349
  chatPanelContainer,
1305
1350
  };
1306
1351
  }
@@ -44,6 +44,7 @@ export type {
44
44
  ChatEmptyStateConfig,
45
45
  ChatEmptyStateContext,
46
46
  ChatEmptyStateProps,
47
+ SubmitPresetOptions,
47
48
  } from './ChatEmptyState/ChatEmptyState.types';
48
49
  export type {
49
50
  Chat as ChatType,
@@ -93,7 +93,11 @@ function readInitialChatWidthPx(): number {
93
93
  type SetPanelWidthOptions = { persist?: boolean };
94
94
 
95
95
  /** Pass `viewTransition: false` to avoid nesting `document.startViewTransition` (e.g. chat open already animating). */
96
- export type SetSidebarOpenOptions = { viewTransition?: boolean };
96
+ export type SetSidebarOpenOptions = {
97
+ viewTransition?: boolean;
98
+ /** Nav closed for layout (chat/width); restore when chat closes — do not persist closed cookie. */
99
+ layoutAutoClose?: boolean;
100
+ };
97
101
 
98
102
  type SidebarContextProps = {
99
103
  isOpen: boolean;
@@ -178,6 +182,8 @@ function SidebarProvider({
178
182
  const [chatPanelOpen, _setChatPanelOpen] = useState(false);
179
183
  const sidebarWidthRef = useRef(sidebarWidthPx);
180
184
  const chatWidthRef = useRef(chatWidthPx);
185
+ /** Left nav was auto-closed because chat opened on a narrow shell; reopen when chat closes. */
186
+ const sidebarAutoClosedForChatRef = useRef(false);
181
187
  sidebarWidthRef.current = sidebarWidthPx;
182
188
  chatWidthRef.current = chatWidthPx;
183
189
 
@@ -332,10 +338,8 @@ function SidebarProvider({
332
338
  window.dispatchEvent(new CustomEvent(DISMISS_CHAT_FOR_LAYOUT_EVENT));
333
339
  }
334
340
  } else if (openingChat && isOpen) {
341
+ sidebarAutoClosedForChatRef.current = true;
335
342
  setIsOpen(false);
336
- if (getCookiePreferences(userId)?.functional) {
337
- setCookie('isSidebarOpen', 'false', 60 * 60 * 24 * 7);
338
- }
339
343
  }
340
344
  },
341
345
  [
@@ -345,7 +349,6 @@ function SidebarProvider({
345
349
  chatPanelOpen,
346
350
  sidebarWidthPx,
347
351
  chatWidthPx,
348
- userId,
349
352
  ],
350
353
  );
351
354
 
@@ -353,16 +356,32 @@ function SidebarProvider({
353
356
  (open: boolean) => {
354
357
  if (open) {
355
358
  closeOppositeSidebarIfNoSpace(false, true);
359
+ _setChatPanelOpen(true);
360
+ return;
361
+ }
362
+ _setChatPanelOpen(false);
363
+ if (sidebarAutoClosedForChatRef.current) {
364
+ sidebarAutoClosedForChatRef.current = false;
365
+ setIsOpen(true);
366
+ if (getCookiePreferences(userId)?.functional) {
367
+ setCookie('isSidebarOpen', 'true', 60 * 60 * 24 * 7);
368
+ }
356
369
  }
357
- _setChatPanelOpen(open);
358
370
  },
359
- [closeOppositeSidebarIfNoSpace],
371
+ [closeOppositeSidebarIfNoSpace, userId],
360
372
  );
361
373
 
362
374
  const setOpen = useCallback(
363
375
  (value: boolean, options?: SetSidebarOpenOptions) => {
376
+ const layoutAutoClose = options?.layoutAutoClose === true;
377
+
364
378
  if (value) {
379
+ sidebarAutoClosedForChatRef.current = false;
365
380
  closeOppositeSidebarIfNoSpace(true, false);
381
+ } else if (layoutAutoClose) {
382
+ sidebarAutoClosedForChatRef.current = true;
383
+ } else {
384
+ sidebarAutoClosedForChatRef.current = false;
366
385
  }
367
386
 
368
387
  const useViewTransition =
@@ -379,7 +398,7 @@ function SidebarProvider({
379
398
  setIsOpen(value);
380
399
  }
381
400
 
382
- if (getCookiePreferences(userId)?.functional) {
401
+ if (!layoutAutoClose && getCookiePreferences(userId)?.functional) {
383
402
  setCookie('isSidebarOpen', value.toString(), 60 * 60 * 24 * 7);
384
403
  }
385
404
  },