@sybilion/uilib 1.2.17 → 1.2.18

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.
@@ -1,6 +1,6 @@
1
1
  import styleInject from 'style-inject';
2
2
 
3
- var css_248z = ".ChatEmptyState_root__j1n-C{align-items:center;display:flex;flex:1;flex-direction:column;gap:var(--p-10);justify-content:center;padding:var(--p-6);text-align:center;text-size:var(--text-sm);color:var(--text-secondary)}.ChatEmptyState_icon__YSDgv,.ChatEmptyState_icon__YSDgv>svg{height:32px;width:32px}";
3
+ var css_248z = ".ChatEmptyState_root__j1n-C{align-items:center;color:var(--text-secondary);display:flex;flex:1;flex-direction:column;font-size:var(--text-sm);gap:var(--p-10);justify-content:center;padding:var(--p-6);text-align:center;text-wrap:balance}.ChatEmptyState_icon__YSDgv,.ChatEmptyState_icon__YSDgv>svg{height:32px;width:32px}";
4
4
  var S = {"root":"ChatEmptyState_root__j1n-C","icon":"ChatEmptyState_icon__YSDgv"};
5
5
  styleInject(css_248z);
6
6
 
@@ -3,7 +3,7 @@ import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
3
3
  import { MessageRole, GENERATING_DASHBOARD_SYSTEM_TEXT } from '../Chat.types.js';
4
4
  import { isGraphIntakeAssistantStepComplete, matchUserTextToQuickReply, parseScriptLine, textHasQuickReplyMarkers, branchKeysUsedFromChatHistory, branchKeysUsedByUserMessages, extractQuickReplyLabelKeyPairsFromText, entryBranchKeyBeforeLastAssistant, isPresetScriptGraph, branchesFromPresetScriptGraph } from '../ChatMessage/presetScript.js';
5
5
  import { usedPresetIdsFromMessages, formatChatTranscript } from '../chat-preset-utils.js';
6
- import { useChatsForScopeId, useChat } from '../../../../contexts/chat-context.js';
6
+ import { useChatsForScopeId, useChat, isChatEmpty } from '../../../../contexts/chat-context.js';
7
7
  import useEvent from '../../../../hooks/useEvent.js';
8
8
  import { useIsMobile } from '../../../../hooks/useIsMobile.js';
9
9
  import { useQueryParams } from '../../../../hooks/useQueryParams.js';
@@ -145,6 +145,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
145
145
  removeSearchParams(CHAT_QUERY_PARAM);
146
146
  }
147
147
  };
148
+ const isEmpty = isChatEmpty(chat) && !isLoading;
148
149
  /**
149
150
  * App link: `?prompt=…` — open panel, pre-fill composer, strip param (read once on mount
150
151
  * from `location.search`). If the selected session already has messages, `newChat()` first.
@@ -168,12 +169,8 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
168
169
  return;
169
170
  }
170
171
  promptParamHandledInEffectRef.current = true;
171
- const selected = currentChatId
172
- ? chats.find(c => c.session_id === currentChatId)
173
- : undefined;
174
- const selectedHasMessages = (selected?.messages?.length ?? 0) > 0;
175
172
  const needsFirstSession = chats.length === 0;
176
- if (selectedHasMessages || needsFirstSession) {
173
+ if (!isEmpty || needsFirstSession) {
177
174
  const sessionId = newChat();
178
175
  if (sessionId == null) {
179
176
  logger.warn('Chat prompt link: sign in to use the assistant.');
@@ -641,7 +638,6 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
641
638
  };
642
639
  const isLastMessageFromUser = chat?.messages.length > 0 &&
643
640
  chat.messages[chat.messages.length - 1]?.role === MessageRole.USER;
644
- const isEmpty = !chat?.messages?.length && !isLoading;
645
641
  const linearScriptActive = Boolean(currentChatId && scriptByChatId[currentChatId]);
646
642
  const quickBranches = currentChatId
647
643
  ? quickReplyBranchesByChat[currentChatId]
@@ -1,5 +1,5 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { createContext, useState, useCallback, useEffect, useContext } from 'react';
2
+ import { createContext, useState, useCallback, useEffect, useContext, useMemo } from 'react';
3
3
  import { MessageRole } from '../components/ui/Chat/Chat.types.js';
4
4
  import { stripJsonDashboardFences } from '../lib/dashboard-spec/stripJsonDashboardFences.js';
5
5
  import { LS } from '@homecode/ui';
@@ -255,6 +255,7 @@ function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessag
255
255
  deleteChat,
256
256
  }, children: children }));
257
257
  }
258
+ const isChatEmpty = (chat) => chat?.messages.length === 0;
258
259
  function useChats() {
259
260
  const context = useContext(ChatContext);
260
261
  if (context === undefined) {
@@ -264,17 +265,21 @@ function useChats() {
264
265
  }
265
266
  function useChat(scopeId, chatId) {
266
267
  const { getChatsForScopeId } = useChats();
267
- if (!scopeId || !chatId)
268
- return null;
269
- const list = getChatsForScopeId(scopeId);
270
- return list.find(chat => chat.session_id === chatId) ?? null;
268
+ return useMemo(() => {
269
+ if (!scopeId || !chatId)
270
+ return null;
271
+ return (getChatsForScopeId(scopeId)?.find(chat => chat.session_id === chatId) ??
272
+ null);
273
+ }, [scopeId, chatId, getChatsForScopeId]);
271
274
  }
272
275
  function useChatsForScopeId(scopeId) {
273
276
  const { getChatsForScopeId, getCurrentChatId, setCurrentChatId, newChat, addMessage, removeMessageById, sendMessage, deleteChat, } = useChats();
274
277
  const chats = getChatsForScopeId(scopeId);
275
278
  const currentChatId = getCurrentChatId(scopeId);
279
+ const currentChat = useChat(scopeId, currentChatId ?? undefined);
276
280
  return {
277
281
  chats,
282
+ currentChat,
278
283
  currentChatId,
279
284
  setCurrentChatId: (targetId) => setCurrentChatId(scopeId, targetId),
280
285
  newChat: () => newChat(scopeId),
@@ -294,4 +299,4 @@ function useCurrentChat(scopeId) {
294
299
  return useChat(scopeId, chatId ?? undefined);
295
300
  }
296
301
 
297
- export { ChatContext, ChatProvider, useChat, useChats, useChatsForDataset, useChatsForScopeId, useCurrentChat };
302
+ export { ChatContext, ChatProvider, isChatEmpty, useChat, useChats, useChatsForDataset, useChatsForScopeId, useCurrentChat };
package/dist/esm/index.js CHANGED
@@ -4,7 +4,7 @@ export { DEFAULT_THEME_ACTIVE_COLOR } from './docs/lib/theme.js';
4
4
  export { SybilionAuthProvider, createSybilionApiFetch, getSybilionApiOriginFromSdk, sybilionApiFetch, useSybilionApiFetch, useSybilionAuth } from './sybilion-auth/SybilionAuthProvider.js';
5
5
  export { SYBILION_AUTH_LOGIN_PATH, normalizeApiBaseUrl } from './sybilion-auth/authPaths.js';
6
6
  export { exchangeAuth0AccessTokenForSybilionJwt } from './sybilion-auth/exchangeSybilionToken.js';
7
- export { ChatContext, ChatProvider, useChat, useChats, useChatsForDataset, useChatsForScopeId, useCurrentChat } from './contexts/chat-context.js';
7
+ export { ChatContext, ChatProvider, isChatEmpty, useChat, useChats, useChatsForDataset, useChatsForScopeId, useCurrentChat } from './contexts/chat-context.js';
8
8
  export { AnalysesSelector } from './components/ui/AnalysesSelector/AnalysesSelector.js';
9
9
  export { AnalysisLineIcon } from './components/ui/AnalysisLineIcon/AnalysisLineIcon.js';
10
10
  export { AppHeaderHost, AppHeaderPortal } from './components/ui/AppHeader/AppHeader.js';
@@ -21,10 +21,12 @@ export interface ChatProviderProps {
21
21
  sendChatMessage: SendChatMessageFn;
22
22
  }
23
23
  export declare function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessageFn, }: ChatProviderProps): import("react/jsx-runtime").JSX.Element;
24
+ export declare const isChatEmpty: (chat: Chat | null) => boolean;
24
25
  export declare function useChats(): ChatContextType;
25
26
  export declare function useChat(scopeId: string | undefined, chatId: string | undefined): Chat | null;
26
27
  export declare function useChatsForScopeId(scopeId: string): {
27
28
  chats: Chat[];
29
+ currentChat: Chat;
28
30
  currentChatId: string;
29
31
  setCurrentChatId: (targetId: string) => void;
30
32
  newChat: () => string;
@@ -36,6 +38,7 @@ export declare function useChatsForScopeId(scopeId: string): {
36
38
  /** @deprecated Use useChatsForScopeId */
37
39
  export declare function useChatsForDataset(scopeId: string): {
38
40
  chats: Chat[];
41
+ currentChat: Chat;
39
42
  currentChatId: string;
40
43
  setCurrentChatId: (targetId: string) => void;
41
44
  newChat: () => string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sybilion/uilib",
3
- "version": "1.2.17",
3
+ "version": "1.2.18",
4
4
  "description": "Sybilion Design System — React UI components (Webpack + Stylus)",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -10,7 +10,8 @@
10
10
  padding var(--p-6)
11
11
 
12
12
  text-align center
13
- text-size var(--text-sm)
13
+ text-wrap balance
14
+ font-size var(--text-sm)
14
15
  color var(--text-secondary)
15
16
 
16
17
  .icon
@@ -23,7 +23,11 @@ import {
23
23
  formatChatTranscript,
24
24
  usedPresetIdsFromMessages,
25
25
  } from '#uilib/components/ui/Chat/chat-preset-utils';
26
- import { useChat, useChatsForScopeId } from '#uilib/contexts/chat-context';
26
+ import {
27
+ isChatEmpty,
28
+ useChat,
29
+ useChatsForScopeId,
30
+ } from '#uilib/contexts/chat-context';
27
31
  import useEvent from '#uilib/hooks/useEvent';
28
32
  import { useIsMobile } from '#uilib/hooks/useIsMobile';
29
33
  import { useQueryParams } from '#uilib/hooks/useQueryParams';
@@ -268,6 +272,8 @@ export function useChatPanelChromeModel({
268
272
  }
269
273
  };
270
274
 
275
+ const isEmpty = isChatEmpty(chat) && !isLoading;
276
+
271
277
  /**
272
278
  * App link: `?prompt=…` — open panel, pre-fill composer, strip param (read once on mount
273
279
  * from `location.search`). If the selected session already has messages, `newChat()` first.
@@ -294,13 +300,9 @@ export function useChatPanelChromeModel({
294
300
  }
295
301
  promptParamHandledInEffectRef.current = true;
296
302
 
297
- const selected = currentChatId
298
- ? chats.find(c => c.session_id === currentChatId)
299
- : undefined;
300
- const selectedHasMessages = (selected?.messages?.length ?? 0) > 0;
301
303
  const needsFirstSession = chats.length === 0;
302
304
 
303
- if (selectedHasMessages || needsFirstSession) {
305
+ if (!isEmpty || needsFirstSession) {
304
306
  const sessionId = newChat();
305
307
  if (sessionId == null) {
306
308
  logger.warn('Chat prompt link: sign in to use the assistant.');
@@ -820,8 +822,6 @@ export function useChatPanelChromeModel({
820
822
  chat?.messages.length > 0 &&
821
823
  chat.messages[chat.messages.length - 1]?.role === MessageRole.USER;
822
824
 
823
- const isEmpty = !chat?.messages?.length && !isLoading;
824
-
825
825
  const linearScriptActive = Boolean(
826
826
  currentChatId && scriptByChatId[currentChatId],
827
827
  );
@@ -4,6 +4,7 @@ import {
4
4
  useCallback,
5
5
  useContext,
6
6
  useEffect,
7
+ useMemo,
7
8
  useState,
8
9
  } from 'react';
9
10
 
@@ -383,6 +384,9 @@ export function ChatProvider({
383
384
  );
384
385
  }
385
386
 
387
+ export const isChatEmpty = (chat: Chat | null): boolean =>
388
+ chat?.messages.length === 0;
389
+
386
390
  export function useChats() {
387
391
  const context = useContext(ChatContext);
388
392
  if (context === undefined) {
@@ -396,9 +400,13 @@ export function useChat(
396
400
  chatId: string | undefined,
397
401
  ): Chat | null {
398
402
  const { getChatsForScopeId } = useChats();
399
- if (!scopeId || !chatId) return null;
400
- const list = getChatsForScopeId(scopeId);
401
- return list.find(chat => chat.session_id === chatId) ?? null;
403
+ return useMemo(() => {
404
+ if (!scopeId || !chatId) return null;
405
+ return (
406
+ getChatsForScopeId(scopeId)?.find(chat => chat.session_id === chatId) ??
407
+ null
408
+ );
409
+ }, [scopeId, chatId, getChatsForScopeId]);
402
410
  }
403
411
 
404
412
  export function useChatsForScopeId(scopeId: string) {
@@ -414,9 +422,11 @@ export function useChatsForScopeId(scopeId: string) {
414
422
  } = useChats();
415
423
  const chats = getChatsForScopeId(scopeId);
416
424
  const currentChatId = getCurrentChatId(scopeId);
425
+ const currentChat = useChat(scopeId, currentChatId ?? undefined);
417
426
 
418
427
  return {
419
428
  chats,
429
+ currentChat,
420
430
  currentChatId,
421
431
  setCurrentChatId: (targetId: string) => setCurrentChatId(scopeId, targetId),
422
432
  newChat: () => newChat(scopeId),
@@ -22,7 +22,7 @@ export default function CardPage() {
22
22
  actions={<DocsHeaderActions />}
23
23
  />
24
24
  <PageContentSection>
25
- <Card style={{ maxWidth: 360 }}>
25
+ <Card paddingSize="s" style={{ maxWidth: 360 }}>
26
26
  <CardHeader>
27
27
  <CardTitle>Card title</CardTitle>
28
28
  <CardDescription>Supporting description text.</CardDescription>