@docyrus/ui-pro-ai-assistant 0.7.2 → 0.7.4

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/README.md CHANGED
@@ -219,8 +219,9 @@ The main chat interface component.
219
219
  | Prop | Type | Default | Description |
220
220
  |------|------|---------|-------------|
221
221
  | `enableSidebar` | `boolean` | `true` | Show the session list sidebar |
222
- | `enableWelcomePage` | `boolean` | `true` | Show a welcome landing page on first open with agent greeting, inline prompt input, and recent sessions. Sidebar and header are hidden in this view. Dismissed on interaction (new chat, session click, or message send) |
223
- | `defaultFullscreen` | `boolean` | `false` | Start in fullscreen |
222
+ | `enableWelcomePage` | `boolean` | `true` | Show a welcome landing page on first open with agent greeting, inline prompt input, and recent sessions. The header (with agent tabs) stays visible so users can switch agents from the welcome screen; the sidebar is hidden. Dismissed on interaction (new chat, session click, or message send) |
223
+ | `defaultFullscreen` | `boolean` | `false` | Initial uncontrolled fullscreen state |
224
+ | `isFullscreen` | `boolean` | — | Controlled fullscreen state. When provided, the package no longer manages its own fullscreen state — pair with `onFullscreenChange` to fully drive the expand button from the host |
224
225
  | `hideExpand` | `boolean` | `false` | Hide the fullscreen toggle |
225
226
  | `hideCloseButton` | `boolean` | `false` | Hide the close (X) button in the header |
226
227
  | `hideBorder` | `boolean` | `false` | Hide the outer border and shadow of the assistant container |
@@ -249,6 +250,8 @@ The main chat interface component.
249
250
 
250
251
  The header renders a tab strip. The first tab is pinned to `tenantAiAgentId` and cannot be closed. The `+` button next to it opens a dropdown listing the available base agents (from `agentSelectorUrl`); selecting one opens a new closable tab with its own isolated thread and message history. Open tabs are persisted to `localStorage`.
251
252
 
253
+ Switching agents (either selecting an existing tab or adding a new one) resets navigation to the chat home — the previous agent's projects/works pane is dismissed and any selected project / project context is cleared, so the user lands in the new agent's chat view rather than a stale pane.
254
+
252
255
  | Prop | Type | Default | Description |
253
256
  |------|------|---------|-------------|
254
257
  | `agentSelectorUrl` | `string` | `"/ai/agent-deployments/base"` | Endpoint listing base agents shown in the "+" dropdown |
@@ -271,7 +274,7 @@ The header renders a tab strip. The first tab is pinned to `tenantAiAgentId` and
271
274
  | `onMessageSend` | `(message: string) => void` | Fires when a message is sent |
272
275
  | `onVoiceStart` | `() => void` | Fires when voice recording starts |
273
276
  | `onVoiceEnd` | `() => void` | Fires when voice recording ends |
274
- | `onFullscreenChange` | `(isFullscreen: boolean) => void` | Intercepts the expand button. When provided, replaces the package's built-in fullscreen toggle — the host receives the intended next state and decides what to do (toggle, redirect, etc.). When omitted, the package self-toggles its fullscreen state. |
277
+ | `onFullscreenChange` | `(isFullscreen: boolean) => void` | Intercepts the expand button. When provided, replaces the package's built-in fullscreen toggle — the host receives the intended next state and decides what to do (toggle, redirect, etc.). When omitted, the package self-toggles its fullscreen state. Pair with `isFullscreen` for a fully controlled expand/collapse. |
275
278
  | `onShare` | `(info: { dataSourceId: string; recordId: string }) => void` | Custom share handler. When provided, the thread header share button calls this instead of the built-in sharing editor |
276
279
 
277
280
  #### Initial prompt
@@ -1,5 +1,5 @@
1
1
  import { type RefObject } from 'react';
2
2
  import { type DocyAssistantProps } from './types';
3
- export declare const DocyAssistant: ({ ref, isOpen, onClose, supportWebSearch, supportThinking, supportFiles, supportDocumentSearch, supportDeepResearch, supportMultiModels, supportWorkCanvas, apiEndpoint, title: titleProp, description: descriptionProp, placeholder: placeholderProp, logo, footerText: footerTextProp, variant, renderMode, enableSidebar, enableVoice, enableMicrophone, enableWelcomePage, tenantAiAgentId, onMessageSend, onVoiceStart, onVoiceEnd, className, defaultFullscreen, hideExpand, hideCloseButton, hideBorder, showHeader, hideHeaderOnWelcome, agentSelectorUrl, onAgentChange, onFullscreenChange, enableSharing, onShare: onShareProp, initialPrompt, initialModelId, initialFeatures, initialFiles, hostEnvironment, appId, appName, appIcon, clientTools, ...props }: DocyAssistantProps & {
3
+ export declare const DocyAssistant: ({ ref, isOpen, onClose, supportWebSearch, supportThinking, supportFiles, supportDocumentSearch, supportDeepResearch, supportMultiModels, supportWorkCanvas, apiEndpoint, title: titleProp, description: descriptionProp, placeholder: placeholderProp, logo, footerText: footerTextProp, variant, renderMode, enableSidebar, enableVoice, enableMicrophone, enableWelcomePage, tenantAiAgentId, onMessageSend, onVoiceStart, onVoiceEnd, className, defaultFullscreen, isFullscreen: isFullscreenProp, hideExpand, hideCloseButton, hideBorder, showHeader, agentSelectorUrl, onAgentChange, onFullscreenChange, enableSharing, onShare: onShareProp, initialPrompt, initialModelId, initialFeatures, initialFiles, hostEnvironment, appId, appName, appIcon, clientTools, ...props }: DocyAssistantProps & {
4
4
  ref?: RefObject<HTMLDivElement | null>;
5
5
  }) => import("react/jsx-runtime").JSX.Element;
@@ -14,6 +14,7 @@ export declare function useSessionState(): {
14
14
  state: SessionState;
15
15
  actions: {
16
16
  setSessions: (sessions: AssistantSession[]) => void;
17
+ appendSessions: (sessions: AssistantSession[]) => void;
17
18
  selectSessionId: (id: string | null) => void;
18
19
  selectSession: (session: AssistantSession | null) => void;
19
20
  startEditing: () => void;
package/dist/index.js CHANGED
@@ -3108,13 +3108,19 @@ function processMessagesData(rawData) {
3108
3108
  }
3109
3109
 
3110
3110
  // src/lib/assistant-api-actions.ts
3111
- async function fetchAgentThreads(apiClient, tenantAiAgentId, onSuccess, userId, deploymentId) {
3111
+ async function fetchAgentThreads(apiClient, tenantAiAgentId, onSuccess, userId, deploymentId, limit, offset3) {
3112
3112
  try {
3113
3113
  const apiParams = {
3114
3114
  columns: "id,subject,created_on,last_modified_on,body_text,tenant_ai_agent_id,created_by",
3115
3115
  expand: "created_by",
3116
3116
  orderBy: JSON.stringify({ field: "created_on", direction: "desc" })
3117
3117
  };
3118
+ if (typeof limit === "number" && limit > 0) {
3119
+ apiParams.limit = limit;
3120
+ }
3121
+ if (typeof offset3 === "number" && offset3 > 0) {
3122
+ apiParams.offset = offset3;
3123
+ }
3118
3124
  const agentRule = deploymentId ? { field: "tenant_ai_agent_deployment_id", operator: "=", value: deploymentId } : { field: "tenant_ai_agent_id", operator: "=", value: tenantAiAgentId };
3119
3125
  const rules = [agentRule, { field: "archived", operator: "=", value: "false" }];
3120
3126
  if (userId) {
@@ -4751,6 +4757,12 @@ function sessionReducer(state, action) {
4751
4757
  switch (action.type) {
4752
4758
  case "SET_SESSIONS":
4753
4759
  return { ...state, sessions: action.payload };
4760
+ case "APPEND_SESSIONS": {
4761
+ const existing = new Set(state.sessions.map((s) => s.id));
4762
+ const fresh = action.payload.filter((s) => !existing.has(s.id));
4763
+ if (fresh.length === 0) return state;
4764
+ return { ...state, sessions: [...state.sessions, ...fresh] };
4765
+ }
4754
4766
  case "SELECT_SESSION_ID":
4755
4767
  return { ...state, selectedSessionId: action.payload };
4756
4768
  case "SELECT_SESSION":
@@ -4799,6 +4811,9 @@ function useSessionState() {
4799
4811
  const setSessions = useCallback((sessions) => {
4800
4812
  dispatch({ type: "SET_SESSIONS", payload: sessions });
4801
4813
  }, []);
4814
+ const appendSessions = useCallback((sessions) => {
4815
+ dispatch({ type: "APPEND_SESSIONS", payload: sessions });
4816
+ }, []);
4802
4817
  const selectSessionId = useCallback((id) => {
4803
4818
  dispatch({ type: "SELECT_SESSION_ID", payload: id });
4804
4819
  }, []);
@@ -4841,6 +4856,7 @@ function useSessionState() {
4841
4856
  const actions = useMemo(
4842
4857
  () => ({
4843
4858
  setSessions,
4859
+ appendSessions,
4844
4860
  selectSessionId,
4845
4861
  selectSession,
4846
4862
  startEditing,
@@ -4857,6 +4873,7 @@ function useSessionState() {
4857
4873
  }),
4858
4874
  [
4859
4875
  setSessions,
4876
+ appendSessions,
4860
4877
  selectSessionId,
4861
4878
  selectSession,
4862
4879
  startEditing,
@@ -19433,6 +19450,7 @@ var MessageItem = memo(({
19433
19450
  ), children: [
19434
19451
  /* @__PURE__ */ jsx(MessageContent, { className: cn(
19435
19452
  "group-[.is-user]:p-2 group-[.is-user]:mt-0.5",
19453
+ "group-[.is-user]:text-foreground",
19436
19454
  isEditing && isUserMessage && "flex-1 w-full max-w-full"
19437
19455
  ), children: renderMessageContent() }),
19438
19456
  isAssistantMessage && !isStreaming && textContent && /* @__PURE__ */ jsx(MessageActions, { className: "mt-1 opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ jsx(CopyButton, { text: textContent }) }),
@@ -34575,7 +34593,6 @@ var AssistantView = ({ ref, ...props }) => {
34575
34593
  hideCloseButton,
34576
34594
  hideBorder,
34577
34595
  showHeader = true,
34578
- hideHeaderOnWelcome = true,
34579
34596
  t,
34580
34597
  ...commonProps
34581
34598
  } = props;
@@ -34592,7 +34609,7 @@ var AssistantView = ({ ref, ...props }) => {
34592
34609
  "data-render-mode": "inline",
34593
34610
  ref,
34594
34611
  children: [
34595
- showHeader && (!hideHeaderOnWelcome || !(commonProps.showWelcome && commonProps.messages.length === 0)) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between h-12 px-3 border-b shrink-0 bg-background gap-2", children: [
34612
+ showHeader && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between h-12 px-3 border-b shrink-0 bg-background gap-2", children: [
34596
34613
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 min-w-0 flex-1", children: renderHeaderTabs ? renderHeaderTabs({ isFullscreen }) : null }),
34597
34614
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [
34598
34615
  onExpand && /* @__PURE__ */ jsx(
@@ -35696,12 +35713,19 @@ var SidebarContent = ({
35696
35713
  onShowMoreProjects,
35697
35714
  onNewProject,
35698
35715
  onToggleSidebar,
35716
+ onLoadMoreSessions,
35717
+ hasMoreSessions,
35699
35718
  supportWorkCanvas = true,
35700
35719
  t
35701
35720
  }) => {
35702
35721
  const [storedListType, setActiveListType] = useState("sessions");
35703
35722
  const activeListType = !supportWorkCanvas && storedListType === "works" ? "sessions" : storedListType;
35704
35723
  const [openMenuId, setOpenMenuId] = useState(null);
35724
+ const handleSessionsScroll = useCallback((e) => {
35725
+ if (activeListType !== "sessions" || !hasMoreSessions || !onLoadMoreSessions) return;
35726
+ const el = e.currentTarget;
35727
+ if (el.scrollHeight - el.scrollTop - el.clientHeight < 80) onLoadMoreSessions();
35728
+ }, [activeListType, hasMoreSessions, onLoadMoreSessions]);
35705
35729
  const navItems = [{ id: 1, label: t("tabs.sessions"), icon: MessageSquare }, ...supportWorkCanvas ? [{ id: 3, label: t("tabs.works"), icon: NotebookText }] : []];
35706
35730
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col flex-1 min-h-0", children: [
35707
35731
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-px", children: [
@@ -35823,6 +35847,7 @@ var SidebarContent = ({
35823
35847
  "div",
35824
35848
  {
35825
35849
  className: "flex-1 overflow-y-auto",
35850
+ onScroll: handleSessionsScroll,
35826
35851
  style: {
35827
35852
  scrollbarWidth: "thin",
35828
35853
  scrollbarColor: "var(--muted-foreground) transparent"
@@ -36103,6 +36128,7 @@ function dataUrlToFile(dataUrl, filename) {
36103
36128
  }
36104
36129
  return new File([u8arr], filename, { type: mime });
36105
36130
  }
36131
+ var THREADS_PAGE_SIZE = 20;
36106
36132
  var DocyAssistant = ({
36107
36133
  ref,
36108
36134
  isOpen = true,
@@ -36132,11 +36158,11 @@ var DocyAssistant = ({
36132
36158
  onVoiceEnd,
36133
36159
  className,
36134
36160
  defaultFullscreen = false,
36161
+ isFullscreen: isFullscreenProp,
36135
36162
  hideExpand = false,
36136
36163
  hideCloseButton = false,
36137
36164
  hideBorder = false,
36138
36165
  showHeader = true,
36139
- hideHeaderOnWelcome = true,
36140
36166
  agentSelectorUrl = "/ai/agent-deployments/base",
36141
36167
  onAgentChange,
36142
36168
  onFullscreenChange,
@@ -36211,7 +36237,9 @@ var DocyAssistant = ({
36211
36237
  const [showWelcome, setShowWelcome] = useState(() => enableWelcomePage && tabs.length <= 1);
36212
36238
  const currentUserId = configUser.id;
36213
36239
  const [projectSearchQuery, setProjectSearchQuery] = useState("");
36214
- const [isInlineFullscreen, setIsInlineFullscreen] = useState(defaultFullscreen);
36240
+ const [internalInlineFullscreen, setInternalInlineFullscreen] = useState(defaultFullscreen);
36241
+ const isInlineFullscreen = isFullscreenProp ?? internalInlineFullscreen;
36242
+ const setIsInlineFullscreen = setInternalInlineFullscreen;
36215
36243
  const [sharingEditorOpen, setSharingEditorOpen] = useState(false);
36216
36244
  const [appContext, setAppContext] = useState(
36217
36245
  appId ? { id: appId, name: appName, icon: appIcon } : null
@@ -36417,8 +36445,35 @@ var DocyAssistant = ({
36417
36445
  return changed ? next : prev;
36418
36446
  });
36419
36447
  }, [agentDetails, agentDetailsForId]);
36420
- const fetchThreads = useCallback(async () => {
36421
- await fetchAgentThreads(apiClient, activeAgentId, sessionActions.setSessions, currentUserId, deploymentId);
36448
+ const [hasMoreThreads, setHasMoreThreads] = useState(true);
36449
+ const isLoadingMoreThreadsRef = useRef(false);
36450
+ const sessionsRef = useRef(sessionState.sessions);
36451
+ useEffect(() => {
36452
+ sessionsRef.current = sessionState.sessions;
36453
+ }, [sessionState.sessions]);
36454
+ const fetchThreads = useCallback(async (mode = "initial") => {
36455
+ if (mode === "more") {
36456
+ if (isLoadingMoreThreadsRef.current) return;
36457
+ isLoadingMoreThreadsRef.current = true;
36458
+ }
36459
+ const offset3 = mode === "more" ? sessionsRef.current.length : 0;
36460
+ await fetchAgentThreads(
36461
+ apiClient,
36462
+ activeAgentId,
36463
+ (threads) => {
36464
+ if (mode === "initial") {
36465
+ sessionActions.setSessions(threads);
36466
+ } else {
36467
+ sessionActions.appendSessions(threads);
36468
+ }
36469
+ setHasMoreThreads(threads.length === THREADS_PAGE_SIZE);
36470
+ },
36471
+ currentUserId,
36472
+ deploymentId,
36473
+ THREADS_PAGE_SIZE,
36474
+ offset3
36475
+ );
36476
+ if (mode === "more") isLoadingMoreThreadsRef.current = false;
36422
36477
  }, [
36423
36478
  apiClient,
36424
36479
  activeAgentId,
@@ -36426,6 +36481,10 @@ var DocyAssistant = ({
36426
36481
  currentUserId,
36427
36482
  deploymentId
36428
36483
  ]);
36484
+ const loadMoreThreads = useCallback(() => {
36485
+ if (!hasMoreThreads) return;
36486
+ void fetchThreads("more");
36487
+ }, [fetchThreads, hasMoreThreads]);
36429
36488
  const fetchProjectThreads2 = useCallback(async (projectId) => {
36430
36489
  await fetchProjectThreads(apiClient, projectId, projectActions.setProjectThreads);
36431
36490
  }, [apiClient, projectActions]);
@@ -36463,7 +36522,8 @@ var DocyAssistant = ({
36463
36522
  ]);
36464
36523
  useEffect(() => {
36465
36524
  if (isOpen && enableSidebar) {
36466
- fetchThreads();
36525
+ setHasMoreThreads(true);
36526
+ void fetchThreads("initial");
36467
36527
  }
36468
36528
  }, [isOpen, enableSidebar, fetchThreads]);
36469
36529
  useEffect(() => {
@@ -36528,6 +36588,12 @@ var DocyAssistant = ({
36528
36588
  };
36529
36589
  const tabSnapshotsRef = useRef(/* @__PURE__ */ new Map());
36530
36590
  const previousTabIdRef = useRef(activeTabId);
36591
+ const resetNavigationForAgentSwitch = useCallback(() => {
36592
+ uiActions.setActiveTab(0);
36593
+ projectActions.setView("list");
36594
+ projectActions.selectProject(null);
36595
+ projectActions.setContext(null);
36596
+ }, [uiActions, projectActions]);
36531
36597
  const handleTabSelect = useCallback((nextTabId) => {
36532
36598
  if (nextTabId === activeTabId) return;
36533
36599
  tabSnapshotsRef.current.set(activeTabId, {
@@ -36535,7 +36601,8 @@ var DocyAssistant = ({
36535
36601
  messages
36536
36602
  });
36537
36603
  setActiveTabId(nextTabId);
36538
- }, [activeTabId, messages]);
36604
+ resetNavigationForAgentSwitch();
36605
+ }, [activeTabId, messages, resetNavigationForAgentSwitch]);
36539
36606
  const handleAddTab = useCallback((agent) => {
36540
36607
  const existing = tabs.find((t2) => t2.agentId === agent.id && t2.agentType === agent.type);
36541
36608
  if (existing) {
@@ -36556,13 +36623,15 @@ var DocyAssistant = ({
36556
36623
  });
36557
36624
  setTabs((prev) => [...prev, newTab]);
36558
36625
  setActiveTabId(newTab.id);
36626
+ resetNavigationForAgentSwitch();
36559
36627
  onAgentChange?.(agent.id, agent.type);
36560
36628
  }, [
36561
36629
  tabs,
36562
36630
  activeTabId,
36563
36631
  messages,
36564
36632
  handleTabSelect,
36565
- onAgentChange
36633
+ onAgentChange,
36634
+ resetNavigationForAgentSwitch
36566
36635
  ]);
36567
36636
  const pendingForwardRef = useRef(null);
36568
36637
  const handleForwardToAgent = useCallback(({ agentId: targetAgentId, prompt }) => {
@@ -36910,6 +36979,8 @@ var DocyAssistant = ({
36910
36979
  },
36911
36980
  onToggleSidebar: isFloating && !uiState.isExpanded ? () => uiActions.setSidebarOpen(false) : void 0,
36912
36981
  supportWorkCanvas: effectiveSupportWorkCanvas,
36982
+ onLoadMoreSessions: loadMoreThreads,
36983
+ hasMoreSessions: hasMoreThreads,
36913
36984
  t
36914
36985
  }
36915
36986
  );
@@ -37302,7 +37373,6 @@ var DocyAssistant = ({
37302
37373
  hideCloseButton,
37303
37374
  hideBorder,
37304
37375
  showHeader,
37305
- hideHeaderOnWelcome,
37306
37376
  renderHeaderTabs: ({ isFullscreen: fs }) => /* @__PURE__ */ jsx(
37307
37377
  AgentTabs,
37308
37378
  {