@spscommerce/max 0.3.0 → 0.5.0

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/lib/MaxPanel.d.ts CHANGED
@@ -2,6 +2,7 @@ import * as React from "react";
2
2
  import { FeatureFlagsTitles } from "./models/FeatureFlagsTitles";
3
3
  import { CurrentApp } from "./models/CurrentApp";
4
4
  import { CurrentUser } from "./models/CurrentUser";
5
+ import type { AdditionalContextProvider } from "./models/AdditionalContext";
5
6
  export declare function MaxPanel({ token, currentUser, hideAgentPanel, isAgentPanelOpen, isInCustomerView, flags, env, currentApp, context, currentConversationId, locale, }: {
6
7
  token: string | null;
7
8
  currentUser: CurrentUser | undefined;
@@ -11,7 +12,7 @@ export declare function MaxPanel({ token, currentUser, hideAgentPanel, isAgentPa
11
12
  flags: FeatureFlagsTitles;
12
13
  env: string;
13
14
  currentApp: CurrentApp;
14
- context: () => Record<any, any>;
15
+ context: AdditionalContextProvider;
15
16
  currentConversationId?: string | null;
16
17
  locale?: string;
17
18
  }): React.ReactNode;
@@ -1,4 +1,5 @@
1
1
  import { ConversationList, ConversationMessages } from "./models/Conversation";
2
+ import type { AdditionalContext } from "./models/AdditionalContext";
2
3
  import type { UsageStatus } from "./models/UsageStatus";
3
4
  export declare class AgentApiService {
4
5
  private readonly url;
@@ -19,5 +20,5 @@ export declare class AgentApiService {
19
20
  deleteConversation: (conversationId: string) => Promise<void>;
20
21
  postFeedback: (messageId: string, score: string, comment: string) => Promise<void>;
21
22
  fetchUsageStatus: (userId: string) => Promise<UsageStatus>;
22
- postChat(message: string, currentAppName: string, currentConversationId: string | null, agentContext: object, currentUrl: string, appInitiated: boolean, userTimezone: string | null): Promise<unknown>;
23
+ postChat(message: string, currentAppName: string, currentConversationId: string | null, agentContext: AdditionalContext, currentUrl: string, appInitiated: boolean, userTimezone: string | null): Promise<unknown>;
23
24
  }
@@ -0,0 +1,7 @@
1
+ import * as React from "react";
2
+ type LoadingStatusProps = {
3
+ label: string;
4
+ };
5
+ export declare function OlderMessagesLoadingStatus({ label }: LoadingStatusProps): React.ReactNode;
6
+ export declare function ConversationSwitchLoadingStatus({ label }: LoadingStatusProps): React.ReactNode;
7
+ export {};
@@ -0,0 +1,14 @@
1
+ import * as React from "react";
2
+ type MaxPanelHeaderActionsProps = {
3
+ isCompact: boolean;
4
+ showConversationList: boolean;
5
+ conversationToggleLabel: string;
6
+ newConversationLabel: string;
7
+ newConversationCompactTitle: string;
8
+ closePanelLabel: string;
9
+ onToggleConversationList: () => void;
10
+ onStartNewConversation: () => void;
11
+ onClosePanel: () => void;
12
+ };
13
+ export declare function MaxPanelHeaderActions({ isCompact, showConversationList, conversationToggleLabel, newConversationLabel, newConversationCompactTitle, closePanelLabel, onToggleConversationList, onStartNewConversation, onClosePanel, }: MaxPanelHeaderActionsProps): React.ReactNode;
14
+ export {};
@@ -0,0 +1,2 @@
1
+ export { ConversationSwitchLoadingStatus, OlderMessagesLoadingStatus } from "./ChatLoadingStatus";
2
+ export { MaxPanelHeaderActions } from "./MaxPanelHeaderActions";
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Primary phase. Mutually exclusive — exactly one is active at any time.
3
+ *
4
+ * - `idle` — No active conversation; landing surface.
5
+ * - `switch-pending` — A conversation switch (or initial load with an
6
+ * existing conversation id) was requested. Waiting
7
+ * for the messages fetch to resolve.
8
+ * - `switch-settling` — Fetch resolved. Layout is settling; the controller
9
+ * is actively snapping the viewport to the bottom and
10
+ * waiting for late-arriving content (images, async
11
+ * markdown rendering, feedback widgets) to commit.
12
+ * - `ready` — Steady state: pin, streaming follow, scroll-to-
13
+ * bottom button, and pagination all behave per their
14
+ * own side-channel state.
15
+ */
16
+ export type ScrollPhase = "idle" | "switch-pending" | "switch-settling" | "ready";
17
+ /**
18
+ * Active user-message anchor. Set when the user sends a message, cleared
19
+ * when a conversation switch begins or the message is replaced. The
20
+ * controller pins the referenced message near the top of the viewport.
21
+ *
22
+ * `expansionTargetTop` is the synthetic min-height target applied to
23
+ * `.conversation-section` when natural content does not provide enough
24
+ * scroll room to honor the pin. `null` means no expansion is active.
25
+ */
26
+ export type UserPinAnchor = {
27
+ messageId: string;
28
+ expansionTargetTop: number | null;
29
+ };
30
+ /**
31
+ * Scroll position captured at the start of an older-messages load. Used
32
+ * after the prepend lands to restore the user's viewport so the visible
33
+ * messages do not appear to jump.
34
+ */
35
+ export type OlderLoadCapture = {
36
+ savedScrollTop: number;
37
+ savedScrollHeight: number;
38
+ };
39
+ export type ScrollState = {
40
+ readonly phase: ScrollPhase;
41
+ readonly streamingFollowEngaged: boolean;
42
+ readonly userPin: UserPinAnchor | null;
43
+ readonly userPinApplied: boolean;
44
+ readonly olderLoad: OlderLoadCapture | null;
45
+ readonly programmaticLockActive: boolean;
46
+ };
47
+ export declare const initialScrollState: ScrollState;
48
+ export type ScrollAction = {
49
+ type: "CONVERSATION_SWITCH_REQUESTED";
50
+ } | {
51
+ type: "CONVERSATION_LOAD_RESOLVED";
52
+ } | {
53
+ type: "SWITCH_SETTLE_COMPLETE";
54
+ } | {
55
+ type: "USER_MESSAGE_ANCHORED";
56
+ messageId: string;
57
+ } | {
58
+ type: "USER_PIN_APPLIED";
59
+ expansionTargetTop: number | null;
60
+ } | {
61
+ type: "USER_PIN_EXPANSION_CLEARED";
62
+ } | {
63
+ type: "USER_PIN_CLEARED";
64
+ } | {
65
+ type: "STREAMING_FOLLOW_ENGAGED";
66
+ } | {
67
+ type: "STREAMING_FOLLOW_DISENGAGED";
68
+ } | {
69
+ type: "STREAMING_ENDED";
70
+ } | {
71
+ type: "PROGRAMMATIC_LOCK_ENGAGED";
72
+ } | {
73
+ type: "PROGRAMMATIC_LOCK_RELEASED";
74
+ } | {
75
+ type: "OLDER_LOAD_STARTED";
76
+ savedScrollTop: number;
77
+ savedScrollHeight: number;
78
+ } | {
79
+ type: "OLDER_LOAD_RESOLVED";
80
+ };
81
+ /**
82
+ * Apply an action to the state. Pure function — no I/O. Every transition is
83
+ * named; unreachable transitions return state unchanged.
84
+ *
85
+ * INVARIANT: `userPinApplied` only becomes true via `USER_PIN_APPLIED`, and
86
+ * is reset to false on every `USER_MESSAGE_ANCHORED` and switch lifecycle
87
+ * action. The controller relies on this for the "pin only once per
88
+ * message" guarantee.
89
+ */
90
+ export declare function scrollReducer(state: ScrollState, action: ScrollAction): ScrollState;
91
+ export type ChatSurfaceState = "initial-loading" | "switch-loading" | "ready";
92
+ /**
93
+ * Externally observable scroll state value, emitted as `data-scroll-state`
94
+ * on the scroll wrapper. Order of precedence: switch lifecycle > interaction
95
+ * mode > steady. Stable string set documented for downstream consumers
96
+ * (tests, Playwright selectors, Clarity recordings).
97
+ */
98
+ export type ScrollStateAttribute = "idle" | "switch-pending" | "switch-settling" | "streaming-follow" | "user-pin-active" | "ready";
99
+ export declare function selectScrollStateAttribute(state: ScrollState): ScrollStateAttribute;
100
+ export declare function selectChatSurfaceState(args: {
101
+ state: ScrollState;
102
+ loading: boolean;
103
+ isActiveChat: boolean;
104
+ showConversationList: boolean;
105
+ }): ChatSurfaceState;
106
+ /**
107
+ * Whether the scroll-to-bottom button should be visible. Pure: caller
108
+ * measures the DOM and passes the values in.
109
+ *
110
+ * Hidden during switch lifecycle (the user is transitioning conversations,
111
+ * showing the button would flash). Hidden while the only "distance to
112
+ * bottom" comes from synthetic pin expansion (not real new content).
113
+ * Otherwise visible iff `distanceToBottom > threshold`.
114
+ */
115
+ export declare function selectShowScrollToBottom(args: {
116
+ state: ScrollState;
117
+ scrollHeight: number;
118
+ clientHeight: number;
119
+ scrollTop: number;
120
+ isStreaming: boolean;
121
+ threshold: number;
122
+ }): boolean;
123
+ /**
124
+ * Whether the controller is in the user-visible "transitioning" window
125
+ * (switch-pending or switch-settling). Used by the loading UI and to
126
+ * gate IntersectionObserver pagination triggers.
127
+ */
128
+ export declare function selectIsTransitioning(state: ScrollState): boolean;
@@ -1,12 +1,12 @@
1
1
  import { Chat } from "../models/Chat";
2
2
  import { Conversation, Message } from "../models/Conversation";
3
3
  import { AgentApiService } from "../api-service";
4
- export declare function useMaxChat(agentApi: AgentApiService, context: () => Record<any, any>): {
4
+ import type { AdditionalContextProvider } from "../models/AdditionalContext";
5
+ export declare function useMaxChat(agentApi: AgentApiService, context: AdditionalContextProvider): {
5
6
  error: boolean;
6
7
  loading: boolean;
7
8
  isAgentThinking: boolean;
8
9
  messages: Chat[];
9
- displayedMessages: Chat[];
10
10
  hasMoreMessages: boolean;
11
11
  loadOlderMessages: () => Promise<void>;
12
12
  isLoadingOlderMessages: boolean;
@@ -0,0 +1,30 @@
1
+ import * as React from "react";
2
+ import { Chat } from "../models/Chat";
3
+ import { ChatSurfaceState, ScrollAction, ScrollStateAttribute } from "./scrollStateMachine";
4
+ type UseMaxPanelScrollControllerArgs = {
5
+ isAgentPanelOpen?: boolean;
6
+ currentConversationId: string | null | undefined;
7
+ showConversationList: boolean;
8
+ loading: boolean;
9
+ messages: Chat[];
10
+ messageHasBeenSent: boolean;
11
+ isAgentThinking: boolean;
12
+ streamingResponse?: string;
13
+ hasMoreMessages: boolean;
14
+ isLoadingOlderMessages: boolean;
15
+ loadOlderMessages: () => Promise<void>;
16
+ };
17
+ type UseMaxPanelScrollControllerResult = {
18
+ scrollWrapperRef: React.MutableRefObject<HTMLDivElement | null>;
19
+ sentinelRef: React.MutableRefObject<HTMLDivElement | null>;
20
+ showScrollToBottom: boolean;
21
+ isConversationTransitioning: boolean;
22
+ isStreaming: boolean;
23
+ isActiveChat: boolean;
24
+ chatSurfaceState: ChatSurfaceState;
25
+ scrollStateAttribute: ScrollStateAttribute;
26
+ prepareConversationSwitchSnap: () => void;
27
+ scrollToLatest: () => void;
28
+ };
29
+ export declare function useMaxPanelScrollController(args: UseMaxPanelScrollControllerArgs): UseMaxPanelScrollControllerResult;
30
+ export type { ScrollAction };
package/lib/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
1
  import "./max.scss";
2
2
  export * from "./icons";
3
3
  export * from "./MaxPanel";
4
+ export type { AdditionalContext, AdditionalContextProvider } from "./models/AdditionalContext";
5
+ export type { CurrentApp } from "./models/CurrentApp";
6
+ export type { FeatureFlagsTitles } from "./models/FeatureFlagsTitles";