@assistant-ui/react 0.15.24 → 0.15.26
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/hooks/useToolCallElapsed.d.ts +3 -1
- package/dist/hooks/useToolCallElapsed.d.ts.map +1 -1
- package/dist/hooks/useToolCallElapsed.js +8 -8
- package/dist/hooks/useToolCallElapsed.js.map +1 -1
- package/dist/index.d.ts +39 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/mcp-apps/McpAppRenderer.js +3 -2
- package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/mcp-apps/app-frame.d.ts.map +1 -1
- package/dist/mcp-apps/app-frame.js +56 -46
- package/dist/mcp-apps/app-frame.js.map +1 -1
- package/dist/mcp-apps/csp.d.ts +4 -0
- package/dist/mcp-apps/csp.d.ts.map +1 -0
- package/dist/mcp-apps/csp.js +93 -0
- package/dist/mcp-apps/csp.js.map +1 -0
- package/dist/mcp-apps/types.d.ts +1 -0
- package/dist/mcp-apps/types.d.ts.map +1 -1
- package/dist/mcp-apps/types.js.map +1 -1
- package/dist/model-context/makeAssistantVisible.d.ts +1 -0
- package/dist/model-context/makeAssistantVisible.d.ts.map +1 -1
- package/dist/model-context/makeAssistantVisible.js +31 -12
- package/dist/model-context/makeAssistantVisible.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.d.ts +1 -0
- package/dist/primitives/assistantModal/AssistantModalRoot.d.ts.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.js.map +1 -1
- package/dist/primitives/attachment.d.ts +3 -1
- package/dist/primitives/attachment.d.ts.map +1 -1
- package/dist/primitives/attachment.js.map +1 -1
- package/dist/primitives/composer/ComposerCancel.d.ts +5 -1
- package/dist/primitives/composer/ComposerCancel.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerCancel.js +14 -1
- package/dist/primitives/composer/ComposerCancel.js.map +1 -1
- package/dist/primitives/composer/ComposerInput.d.ts +15 -1
- package/dist/primitives/composer/ComposerInput.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerInput.js +5 -4
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/dist/primitives/composer/ComposerRoot.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerRoot.js +24 -19
- package/dist/primitives/composer/ComposerRoot.js.map +1 -1
- package/dist/primitives/composer/composerInputSelector.d.ts +3 -0
- package/dist/primitives/composer/composerInputSelector.d.ts.map +1 -0
- package/dist/primitives/composer/composerInputSelector.js +7 -0
- package/dist/primitives/composer/composerInputSelector.js.map +1 -0
- package/dist/primitives/composer/trigger/TriggerPopover.d.ts +11 -11
- package/dist/primitives/composer/trigger/TriggerPopover.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopover.js +62 -60
- package/dist/primitives/composer/trigger/TriggerPopover.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverAction.d.ts +8 -8
- package/dist/primitives/composer/trigger/TriggerPopoverAction.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverAction.js +6 -6
- package/dist/primitives/composer/trigger/TriggerPopoverAction.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts +3 -3
- package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverCategories.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverDirective.d.ts +9 -9
- package/dist/primitives/composer/trigger/TriggerPopoverDirective.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverDirective.js +7 -7
- package/dist/primitives/composer/trigger/TriggerPopoverDirective.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts +5 -5
- package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverItems.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverRootContext.d.ts +8 -8
- package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js +8 -8
- package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js.map +1 -1
- package/dist/primitives/composer/trigger/index.d.ts +1 -1
- package/dist/primitives/composer/useComposerCancelWithFocus.d.ts +2 -0
- package/dist/primitives/composer/useComposerCancelWithFocus.d.ts.map +1 -0
- package/dist/primitives/composer/useComposerCancelWithFocus.js +42 -0
- package/dist/primitives/composer/useComposerCancelWithFocus.js.map +1 -0
- package/dist/primitives/composer.d.ts +55 -15
- package/dist/primitives/composer.d.ts.map +1 -1
- package/dist/primitives/composer.js +8 -1
- package/dist/primitives/composer.js.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.d.ts +8 -1
- package/dist/primitives/message/MessagePartsGrouped.d.ts.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.js +58 -278
- package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.d.ts.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js +5 -2
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -1
- package/dist/primitives/thread/ThreadLoadEarlier.d.ts +19 -0
- package/dist/primitives/thread/ThreadLoadEarlier.d.ts.map +1 -0
- package/dist/primitives/thread/ThreadLoadEarlier.js +36 -0
- package/dist/primitives/thread/ThreadLoadEarlier.js.map +1 -0
- package/dist/primitives/thread/ThreadMessages.d.ts +1 -1
- package/dist/primitives/thread/ThreadMessages.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadMessages.js +2 -2
- package/dist/primitives/thread/ThreadViewport.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadViewport.js +4 -3
- package/dist/primitives/thread/ThreadViewport.js.map +1 -1
- package/dist/primitives/thread/topAnchor/useTopAnchorReserve.js +2 -2
- package/dist/primitives/thread/topAnchor/useTopAnchorReserve.js.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.js +195 -17
- package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
- package/dist/primitives/thread.d.ts +2 -0
- package/dist/primitives/thread.d.ts.map +1 -1
- package/dist/primitives/thread.js +5 -2
- package/dist/primitives/thread.js.map +1 -1
- package/dist/sandbox-host/SandboxHost.d.ts.map +1 -1
- package/dist/sandbox-host/SandboxHost.js +9 -3
- package/dist/sandbox-host/SandboxHost.js.map +1 -1
- package/dist/unstable/useComposerInput.d.ts +7 -4
- package/dist/unstable/useComposerInput.d.ts.map +1 -1
- package/dist/unstable/useComposerInput.js +4 -4
- package/dist/unstable/useComposerInput.js.map +1 -1
- package/dist/unstable/useComposerInputHistory.d.ts +3 -2
- package/dist/unstable/useComposerInputHistory.d.ts.map +1 -1
- package/dist/unstable/useComposerInputHistory.js +2 -2
- package/dist/unstable/useComposerInputHistory.js.map +1 -1
- package/dist/unstable/useLiveCompletionAdapter.d.ts +3 -2
- package/dist/unstable/useLiveCompletionAdapter.d.ts.map +1 -1
- package/dist/unstable/useLiveCompletionAdapter.js +8 -7
- package/dist/unstable/useLiveCompletionAdapter.js.map +1 -1
- package/dist/unstable/useMentionAdapter.d.ts +12 -3
- package/dist/unstable/useMentionAdapter.d.ts.map +1 -1
- package/dist/unstable/useMentionAdapter.js +2 -2
- package/dist/unstable/useMentionAdapter.js.map +1 -1
- package/dist/unstable/useMessageStallDetection.d.ts +4 -2
- package/dist/unstable/useMessageStallDetection.d.ts.map +1 -1
- package/dist/unstable/useMessageStallDetection.js +12 -8
- package/dist/unstable/useMessageStallDetection.js.map +1 -1
- package/dist/unstable/useSlashCommandAdapter.d.ts +5 -2
- package/dist/unstable/useSlashCommandAdapter.d.ts.map +1 -1
- package/dist/unstable/useSlashCommandAdapter.js +5 -4
- package/dist/unstable/useSlashCommandAdapter.js.map +1 -1
- package/dist/unstable/webmcp/useWebMcpProvider.d.ts +4 -0
- package/dist/unstable/webmcp/useWebMcpProvider.d.ts.map +1 -1
- package/dist/unstable/webmcp/useWebMcpProvider.js +2 -0
- package/dist/unstable/webmcp/useWebMcpProvider.js.map +1 -1
- package/dist/utils/getSelectionMessageId.d.ts.map +1 -1
- package/dist/utils/getSelectionMessageId.js +31 -8
- package/dist/utils/getSelectionMessageId.js.map +1 -1
- package/dist/utils/hooks/useOnResizeContent.d.ts.map +1 -1
- package/dist/utils/hooks/useOnResizeContent.js +2 -22
- package/dist/utils/hooks/useOnResizeContent.js.map +1 -1
- package/dist/utils/smooth/useSmooth.js +6 -6
- package/dist/utils/smooth/useSmooth.js.map +1 -1
- package/dist/utils/useIsomorphicLayoutEffect.d.ts +3 -0
- package/dist/utils/useIsomorphicLayoutEffect.d.ts.map +1 -0
- package/dist/utils/useIsomorphicLayoutEffect.js +6 -0
- package/dist/utils/useIsomorphicLayoutEffect.js.map +1 -0
- package/package.json +12 -11
- package/src/hooks/useToolCallElapsed.test.tsx +37 -0
- package/src/hooks/useToolCallElapsed.ts +10 -7
- package/src/index.ts +47 -5
- package/src/mcp-apps/McpAppRenderer.tsx +3 -9
- package/src/mcp-apps/app-frame.test.tsx +38 -0
- package/src/mcp-apps/app-frame.tsx +5 -8
- package/src/mcp-apps/csp.test.ts +112 -0
- package/src/mcp-apps/csp.ts +124 -0
- package/src/mcp-apps/types.ts +1 -0
- package/src/model-context/frame/AssistantFrame.test.ts +45 -0
- package/src/model-context/frame/SPEC_AssistantFrame.md +1 -0
- package/src/model-context/makeAssistantVisible.test.tsx +315 -5
- package/src/model-context/makeAssistantVisible.tsx +43 -7
- package/src/primitives/assistantModal/AssistantModalRoot.tsx +1 -0
- package/src/primitives/attachment.ts +4 -1
- package/src/primitives/composer/ComposerCancel.focus.test.tsx +307 -0
- package/src/primitives/composer/ComposerCancel.ts +8 -1
- package/src/primitives/composer/ComposerInput.tsx +20 -2
- package/src/primitives/composer/ComposerRoot.tsx +6 -5
- package/src/primitives/composer/composerInputSelector.ts +4 -0
- package/src/primitives/composer/trigger/TriggerPopover.tsx +11 -11
- package/src/primitives/composer/trigger/TriggerPopoverAction.tsx +10 -13
- package/src/primitives/composer/trigger/TriggerPopoverCategories.tsx +2 -2
- package/src/primitives/composer/trigger/TriggerPopoverDirective.tsx +11 -14
- package/src/primitives/composer/trigger/TriggerPopoverExports.test.ts +61 -0
- package/src/primitives/composer/trigger/TriggerPopoverItems.tsx +3 -3
- package/src/primitives/composer/trigger/TriggerPopoverRootContext.tsx +8 -8
- package/src/primitives/composer/useComposerCancelWithFocus.ts +57 -0
- package/src/primitives/composer.ts +70 -15
- package/src/primitives/message/MessagePartsGrouped.test.tsx +106 -2
- package/src/primitives/message/MessagePartsGrouped.tsx +36 -134
- package/src/primitives/message/MessageRoot.test.tsx +125 -2
- package/src/primitives/selectionToolbar/SelectionToolbarRoot.test.tsx +20 -12
- package/src/primitives/selectionToolbar/SelectionToolbarRoot.tsx +6 -2
- package/src/primitives/thread/ThreadLoadEarlier.tsx +35 -0
- package/src/primitives/thread/ThreadMessages.tsx +1 -0
- package/src/primitives/thread/ThreadViewport.tsx +3 -3
- package/src/primitives/thread/topAnchor/useTopAnchorReserve.ts +2 -2
- package/src/primitives/thread/useThreadViewportAutoScroll.test.tsx +922 -68
- package/src/primitives/thread/useThreadViewportAutoScroll.ts +330 -56
- package/src/primitives/thread.ts +2 -0
- package/src/sandbox-host/SandboxHost.test.tsx +34 -0
- package/src/sandbox-host/SandboxHost.tsx +11 -12
- package/src/tests/toolCallTiming.test.tsx +19 -2
- package/src/unstable/useComposerInput.ts +7 -4
- package/src/unstable/useComposerInputHistory.ts +3 -2
- package/src/unstable/useLiveCompletionAdapter.ts +9 -14
- package/src/unstable/useMentionAdapter.ts +12 -3
- package/src/unstable/useMessageStallDetection.test.tsx +40 -0
- package/src/unstable/useMessageStallDetection.ts +14 -8
- package/src/unstable/useSlashCommandAdapter.ts +8 -4
- package/src/unstable/webmcp/useWebMcpProvider.ts +4 -0
- package/src/utils/getSelectionMessageId.test.ts +231 -0
- package/src/utils/getSelectionMessageId.ts +45 -7
- package/src/utils/hooks/useOnResizeContent.ts +2 -29
- package/src/utils/smooth/useSmooth.test.tsx +16 -5
- package/src/utils/smooth/useSmooth.ts +7 -7
- package/src/utils/useIsomorphicLayoutEffect.test.tsx +21 -0
- package/src/utils/useIsomorphicLayoutEffect.ts +9 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ActionButtonElement, type ActionButtonProps } from "../../utils/createActionButton.js";
|
|
2
|
+
declare const useThreadLoadEarlier: () => (() => void) | null;
|
|
3
|
+
export declare namespace ThreadPrimitiveLoadEarlier {
|
|
4
|
+
type Element = ActionButtonElement;
|
|
5
|
+
type Props = ActionButtonProps<typeof useThreadLoadEarlier>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A button that loads the page before the first loaded message. Disabled
|
|
9
|
+
* while the runtime reports no earlier messages. A press while a page is
|
|
10
|
+
* loading joins that load, so the button stays enabled and keeps keyboard
|
|
11
|
+
* focus until the page lands; read `thread.isLoadingEarlier` to show progress.
|
|
12
|
+
*/
|
|
13
|
+
export declare const ThreadPrimitiveLoadEarlier: import("react").ForwardRefExoticComponent<Omit<Omit<import("react").ClassAttributes<HTMLButtonElement> & import("react").ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
14
|
+
asChild?: boolean;
|
|
15
|
+
}, "ref"> & {
|
|
16
|
+
render?: import("react").ReactElement | undefined;
|
|
17
|
+
} & import("react").RefAttributes<HTMLButtonElement>, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=ThreadLoadEarlier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ThreadLoadEarlier.d.ts","sourceRoot":"","sources":["../../../src/primitives/thread/ThreadLoadEarlier.tsx"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EAEvB,0CAAuC;AAExC,QAAA,MAAM,oBAAoB,2BAQzB,CAAC;AAEF,yBAAiB,0BAA0B,CAAC;IAC1C,KAAY,OAAO,GAAG,mBAAmB,CAAC;IAC1C,KAAY,KAAK,GAAG,iBAAiB,CAAC,OAAO,oBAAoB,CAAC,CAAC;CACpE;AAED;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B;;;;gHAGtC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createActionButton } from "../../utils/createActionButton.js";
|
|
3
|
+
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
|
|
4
|
+
import "@assistant-ui/tap/react-shim";
|
|
5
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
6
|
+
//#region src/primitives/thread/ThreadLoadEarlier.tsx
|
|
7
|
+
const useThreadLoadEarlier = () => {
|
|
8
|
+
const $ = c(2);
|
|
9
|
+
const aui = useAui();
|
|
10
|
+
const hasEarlier = useAuiState(_temp);
|
|
11
|
+
let t0;
|
|
12
|
+
if ($[0] !== aui.thread) {
|
|
13
|
+
t0 = () => {
|
|
14
|
+
aui.thread.loadEarlier();
|
|
15
|
+
};
|
|
16
|
+
$[0] = aui.thread;
|
|
17
|
+
$[1] = t0;
|
|
18
|
+
} else t0 = $[1];
|
|
19
|
+
const loadEarlier = t0;
|
|
20
|
+
if (!hasEarlier) return null;
|
|
21
|
+
return loadEarlier;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* A button that loads the page before the first loaded message. Disabled
|
|
25
|
+
* while the runtime reports no earlier messages. A press while a page is
|
|
26
|
+
* loading joins that load, so the button stays enabled and keeps keyboard
|
|
27
|
+
* focus until the page lands; read `thread.isLoadingEarlier` to show progress.
|
|
28
|
+
*/
|
|
29
|
+
const ThreadPrimitiveLoadEarlier = createActionButton("ThreadPrimitive.LoadEarlier", useThreadLoadEarlier);
|
|
30
|
+
function _temp(s) {
|
|
31
|
+
return s.thread.hasEarlier;
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { ThreadPrimitiveLoadEarlier };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=ThreadLoadEarlier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ThreadLoadEarlier.js","names":["c","_c","useCallback","useAui","useAuiState","ActionButtonElement","ActionButtonProps","createActionButton","useThreadLoadEarlier","$","aui","hasEarlier","_temp","t0","thread","loadEarlier","ThreadPrimitiveLoadEarlier","Element","Props","s","_c"],"sources":["../../../src/primitives/thread/ThreadLoadEarlier.tsx"],"sourcesContent":["\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport {\n type ActionButtonElement,\n type ActionButtonProps,\n createActionButton,\n} from \"../../utils/createActionButton\";\n\nconst useThreadLoadEarlier = () => {\n const aui = useAui();\n const hasEarlier = useAuiState((s) => s.thread.hasEarlier);\n const loadEarlier = useCallback(() => {\n void aui.thread.loadEarlier();\n }, [aui]);\n if (!hasEarlier) return null;\n return loadEarlier;\n};\n\nexport namespace ThreadPrimitiveLoadEarlier {\n export type Element = ActionButtonElement;\n export type Props = ActionButtonProps<typeof useThreadLoadEarlier>;\n}\n\n/**\n * A button that loads the page before the first loaded message. Disabled\n * while the runtime reports no earlier messages. A press while a page is\n * loading joins that load, so the button stays enabled and keeps keyboard\n * focus until the page lands; read `thread.isLoadingEarlier` to show progress.\n */\nexport const ThreadPrimitiveLoadEarlier = createActionButton(\n \"ThreadPrimitive.LoadEarlier\",\n useThreadLoadEarlier,\n);\n"],"mappings":";;;;;;AAUA,MAAMQ,6BAAuB;CAAA,MAAAC,IAAAR,EAAA,CAAA;CAC3B,MAAAS,MAAYP,OAAO;CACnB,MAAAQ,aAAmBP,YAAYQ,KAA0B;CAAE,IAAAC;CAAA,IAAAJ,EAAA,OAAAC,IAAAI,QAAA;EAC3BD,WAAA;GACzBH,IAAGI,OAAOC,YAAa;EAAC;EAC9BN,EAAA,KAAAC,IAAAI;EAAAL,EAAA,KAAAI;CAAA,OAAAA,KAAAJ,EAAA;CAFD,MAAAM,cAAoBF;CAGpB,IAAI,CAACF,YAAU,OAAS;CAAK,OACtBI;AAAW;;;;;;;AAcpB,MAAaC,6BAA6BT,mBACxC,+BACAC,oBACF;AAxB6B,SAAAI,MAAAO,GAAA;CAAA,OAEWA,EAACL,OAAOH;AAAW"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { ThreadPrimitiveMessages, ThreadPrimitiveMessagesImpl, ThreadPrimitiveMessageByIndex, ThreadPrimitiveUnstable_MessageById, } from "@assistant-ui/core/react";
|
|
1
|
+
export { ThreadPrimitiveMessages, ThreadPrimitiveMessagesImpl, ThreadPrimitiveMessageByIndex, ThreadPrimitiveUnstable_MessageById, ThreadPrimitiveRow, } from "@assistant-ui/core/react";
|
|
2
2
|
//# sourceMappingURL=ThreadMessages.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThreadMessages.d.ts","sourceRoot":"","sources":["../../../src/primitives/thread/ThreadMessages.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,uBAAuB,EACvB,2BAA2B,EAC3B,6BAA6B,EAC7B,mCAAmC,
|
|
1
|
+
{"version":3,"file":"ThreadMessages.d.ts","sourceRoot":"","sources":["../../../src/primitives/thread/ThreadMessages.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,uBAAuB,EACvB,2BAA2B,EAC3B,6BAA6B,EAC7B,mCAAmC,EACnC,kBAAkB,GACnB,MAAM,0BAA0B,CAAC"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { ThreadPrimitiveMessageByIndex, ThreadPrimitiveMessages, ThreadPrimitiveMessagesImpl, ThreadPrimitiveUnstable_MessageById } from "@assistant-ui/core/react";
|
|
3
|
-
export { ThreadPrimitiveMessageByIndex, ThreadPrimitiveMessages, ThreadPrimitiveMessagesImpl, ThreadPrimitiveUnstable_MessageById };
|
|
2
|
+
import { ThreadPrimitiveMessageByIndex, ThreadPrimitiveMessages, ThreadPrimitiveMessagesImpl, ThreadPrimitiveRow, ThreadPrimitiveUnstable_MessageById } from "@assistant-ui/core/react";
|
|
3
|
+
export { ThreadPrimitiveMessageByIndex, ThreadPrimitiveMessages, ThreadPrimitiveMessagesImpl, ThreadPrimitiveRow, ThreadPrimitiveUnstable_MessageById };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThreadViewport.d.ts","sourceRoot":"","sources":["../../../src/primitives/thread/ThreadViewport.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,iCAA8B;AAClD,OAAO,EACL,KAAK,YAAY,EAEjB,KAAK,wBAAwB,
|
|
1
|
+
{"version":3,"file":"ThreadViewport.d.ts","sourceRoot":"","sources":["../../../src/primitives/thread/ThreadViewport.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,iCAA8B;AAClD,OAAO,EACL,KAAK,YAAY,EAEjB,KAAK,wBAAwB,EAG9B,MAAM,OAAO,CAAC;AAkBf,yBAAiB,uBAAuB,CAAC;IACvC,KAAY,OAAO,GAAG,YAAY,CAAC,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC;IACzD,KAAY,KAAK,GAAG,wBAAwB,CAAC,OAAO,SAAS,CAAC,GAAG,CAAC,GAAG;QACnE;;;;;WAKG;QACH,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAEjC;;;;WAIG;QACH,UAAU,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC;QAE1C;;;;WAIG;QACH,qBAAqB,CAAC,EAAE;YACtB;;;;eAIG;YACH,UAAU,CAAC,EAAE,MAAM,CAAC;YACpB;;;;eAIG;YACH,aAAa,CAAC,EAAE,MAAM,CAAC;SACxB,CAAC;QAEF;;;;WAIG;QACH,wBAAwB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAE/C;;;;WAIG;QACH,0BAA0B,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAEjD;;;;WAIG;QACH,4BAA4B,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;KACpD,CAAC;CACH;AA2GD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,uBAAuB;;;;;IAnLhC;;;;;OAKG;iBACU,OAAO,GAAG,SAAS;IAEhC;;;;OAIG;iBACU,KAAK,GAAG,QAAQ,GAAG,SAAS;IAEzC;;;;OAIG;4BACqB;QACtB;;;;WAIG;QACH,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB;;;;WAIG;QACH,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB;IAED;;;;OAIG;+BACwB,OAAO,GAAG,SAAS;IAE9C;;;;OAIG;iCAC0B,OAAO,GAAG,SAAS;IAEhD;;;;OAIG;mCAC4B,OAAO,GAAG,SAAS;kDAwIpD,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useThreadViewport, useThreadViewportStore } from "../../context/react/ThreadViewportContext.js";
|
|
3
3
|
import { ThreadPrimitiveViewportProvider } from "../../context/providers/ThreadViewportProvider.js";
|
|
4
|
+
import { useIsomorphicLayoutEffect } from "../../utils/useIsomorphicLayoutEffect.js";
|
|
4
5
|
import { Primitive as Primitive$1 } from "../../utils/Primitive.js";
|
|
5
6
|
import { useManagedRef } from "../../utils/hooks/useManagedRef.js";
|
|
6
7
|
import { useThreadViewportAutoScroll } from "./useThreadViewportAutoScroll.js";
|
|
@@ -8,7 +9,7 @@ import { useSizeHandle } from "../../utils/hooks/useSizeHandle.js";
|
|
|
8
9
|
import { useTopAnchorReserve } from "./topAnchor/useTopAnchorReserve.js";
|
|
9
10
|
import { getActiveTopAnchorAnchorId, getActiveTopAnchorTargetId, isTopAnchorTurnValid } from "./topAnchor/topAnchorTurn.js";
|
|
10
11
|
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
|
|
11
|
-
import { forwardRef
|
|
12
|
+
import { forwardRef } from "@assistant-ui/tap/react-shim";
|
|
12
13
|
import { useAuiState } from "@assistant-ui/store";
|
|
13
14
|
import { jsx } from "react/jsx-runtime";
|
|
14
15
|
import { useComposedRefs } from "radix-ui/internal";
|
|
@@ -93,7 +94,7 @@ const useTopAnchorTurn = (enabled) => {
|
|
|
93
94
|
t4 = $[13];
|
|
94
95
|
t5 = $[14];
|
|
95
96
|
}
|
|
96
|
-
|
|
97
|
+
useIsomorphicLayoutEffect(t4, t5);
|
|
97
98
|
let t6;
|
|
98
99
|
let t7;
|
|
99
100
|
if ($[15] !== activeTurn || $[16] !== threadViewportStore) {
|
|
@@ -113,7 +114,7 @@ const useTopAnchorTurn = (enabled) => {
|
|
|
113
114
|
t6 = $[17];
|
|
114
115
|
t7 = $[18];
|
|
115
116
|
}
|
|
116
|
-
|
|
117
|
+
useIsomorphicLayoutEffect(t6, t7);
|
|
117
118
|
};
|
|
118
119
|
const ThreadPrimitiveViewportScrollable = forwardRef((t0, forwardedRef) => {
|
|
119
120
|
const $ = c(18);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThreadViewport.js","names":["c","_c","useComposedRefs","Primitive","ComponentRef","forwardRef","ComponentPropsWithoutRef","useCallback","useLayoutEffect","useMemo","useAuiState","useManagedRef","useThreadViewportAutoScroll","ThreadPrimitiveViewportProvider","useSizeHandle","useThreadViewport","useThreadViewportStore","useTopAnchorReserve","getActiveTopAnchorAnchorId","getActiveTopAnchorTargetId","isTopAnchorTurnValid","ThreadPrimitiveViewport","Element","div","Props","autoScroll","turnAnchor","topAnchorMessageClamp","tallerThan","visibleHeight","scrollToBottomOnRunStart","scrollToBottomOnInitialize","scrollToBottomOnThreadSwitch","useViewportSizeRef","register","_temp","getHeight","_temp2","useViewportElementRef","registerViewportElement","_temp3","useTopAnchorTurn","enabled","$","threadViewportStore","t0","s","thread","activeAnchorId","t1","s_0","activeTargetId","topAnchorTurn","_temp4","t2","bb0","t3","anchorId","targetId","activeTurn","s_2","messages","topAnchorTurnIsValid","t4","t5","getState","setTopAnchorTurn","t6","t7","state","current","ThreadPrimitiveViewportScrollable","forwardedRef","children","rest","autoScrollRef","viewportSizeRef","viewportElementRef","topAnchorEnabled","ref","displayName","props","registerViewport","el","clientHeight","s_1","getHeight","_c","Primitive"],"sources":["../../../src/primitives/thread/ThreadViewport.tsx"],"sourcesContent":["\"use client\";\n\nimport { useComposedRefs } from \"radix-ui/internal\";\nimport { Primitive } from \"../../utils/Primitive\";\nimport {\n type ComponentRef,\n forwardRef,\n type ComponentPropsWithoutRef,\n useCallback,\n useLayoutEffect,\n useMemo,\n} from \"react\";\nimport { useAuiState } from \"@assistant-ui/store\";\nimport { useManagedRef } from \"../../utils/hooks/useManagedRef\";\nimport { useThreadViewportAutoScroll } from \"./useThreadViewportAutoScroll\";\nimport { ThreadPrimitiveViewportProvider } from \"../../context/providers/ThreadViewportProvider\";\nimport { useSizeHandle } from \"../../utils/hooks/useSizeHandle\";\nimport {\n useThreadViewport,\n useThreadViewportStore,\n} from \"../../context/react/ThreadViewportContext\";\nimport { useTopAnchorReserve } from \"./topAnchor/useTopAnchorReserve\";\nimport {\n getActiveTopAnchorAnchorId,\n getActiveTopAnchorTargetId,\n isTopAnchorTurnValid,\n} from \"./topAnchor/topAnchorTurn\";\n\nexport namespace ThreadPrimitiveViewport {\n export type Element = ComponentRef<typeof Primitive.div>;\n export type Props = ComponentPropsWithoutRef<typeof Primitive.div> & {\n /**\n * Whether to automatically scroll to the bottom when new messages are added.\n * When enabled, the viewport will automatically scroll to show the latest content.\n *\n * Default false if `turnAnchor` is \"top\", otherwise defaults to true.\n */\n autoScroll?: boolean | undefined;\n\n /**\n * Controls scroll anchoring behavior for new messages.\n * - \"bottom\" (default): Messages anchor at the bottom, classic chat behavior.\n * - \"top\": New user messages anchor at the top of the viewport for a focused reading experience.\n */\n turnAnchor?: \"top\" | \"bottom\" | undefined;\n\n /**\n * Clamps tall user messages so the assistant response stays in view.\n *\n * @default { tallerThan: \"10em\", visibleHeight: \"6em\" }\n */\n topAnchorMessageClamp?: {\n /**\n * Clamp messages taller than this. Supports `px`, `em`, and `rem`.\n *\n * @default \"10em\"\n */\n tallerThan?: string;\n /**\n * Visible portion of clamped messages. Supports `px`, `em`, and `rem`.\n *\n * @default \"6em\"\n */\n visibleHeight?: string;\n };\n\n /**\n * Whether to scroll to bottom when a new run starts.\n *\n * Defaults to true.\n */\n scrollToBottomOnRunStart?: boolean | undefined;\n\n /**\n * Whether to scroll to bottom when thread history is first loaded.\n *\n * Defaults to true.\n */\n scrollToBottomOnInitialize?: boolean | undefined;\n\n /**\n * Whether to scroll to bottom when switching to a different thread.\n *\n * Defaults to true.\n */\n scrollToBottomOnThreadSwitch?: boolean | undefined;\n };\n}\n\nconst useViewportSizeRef = () => {\n const register = useThreadViewport((s) => s.registerViewport);\n const getHeight = useCallback((el: HTMLElement) => el.clientHeight, []);\n return useSizeHandle(register, getHeight);\n};\n\nconst useViewportElementRef = () => {\n const registerViewportElement = useThreadViewport(\n (s) => s.registerViewportElement,\n );\n\n return useManagedRef(registerViewportElement);\n};\n\nconst useTopAnchorTurn = (enabled: boolean) => {\n const threadViewportStore = useThreadViewportStore();\n const activeAnchorId = useAuiState((s) => {\n if (!enabled) return undefined;\n return getActiveTopAnchorAnchorId(s.thread);\n });\n const activeTargetId = useAuiState((s) => {\n if (!enabled) return undefined;\n return getActiveTopAnchorTargetId(s.thread);\n });\n const topAnchorTurn = useThreadViewport((s) => s.topAnchorTurn);\n const activeTurn = useMemo(() => {\n if (!activeAnchorId || !activeTargetId) return null;\n return { anchorId: activeAnchorId, targetId: activeTargetId };\n }, [activeAnchorId, activeTargetId]);\n\n const topAnchorTurnIsValid = useAuiState(\n (s) =>\n enabled &&\n !!topAnchorTurn &&\n isTopAnchorTurnValid(topAnchorTurn, s.thread.messages),\n );\n\n useLayoutEffect(() => {\n if (!topAnchorTurn || topAnchorTurnIsValid) return;\n\n threadViewportStore.getState().setTopAnchorTurn(null);\n }, [threadViewportStore, topAnchorTurn, topAnchorTurnIsValid]);\n\n useLayoutEffect(() => {\n if (!activeTurn) return;\n\n const state = threadViewportStore.getState();\n const current = state.topAnchorTurn;\n if (\n current?.anchorId === activeTurn.anchorId &&\n current.targetId === activeTurn.targetId\n ) {\n return;\n }\n\n state.setTopAnchorTurn(activeTurn);\n }, [activeTurn, threadViewportStore]);\n};\n\nconst ThreadPrimitiveViewportScrollable = forwardRef<\n ThreadPrimitiveViewport.Element,\n ThreadPrimitiveViewport.Props\n>(\n (\n {\n autoScroll,\n scrollToBottomOnRunStart,\n scrollToBottomOnInitialize,\n scrollToBottomOnThreadSwitch,\n children,\n ...rest\n },\n forwardedRef,\n ) => {\n const autoScrollRef = useThreadViewportAutoScroll<HTMLDivElement>({\n autoScroll,\n scrollToBottomOnRunStart,\n scrollToBottomOnInitialize,\n scrollToBottomOnThreadSwitch,\n });\n const viewportSizeRef = useViewportSizeRef();\n const viewportElementRef = useViewportElementRef();\n const threadViewportStore = useThreadViewportStore();\n const turnAnchor = threadViewportStore.getState().turnAnchor;\n const topAnchorEnabled = turnAnchor === \"top\";\n useTopAnchorTurn(topAnchorEnabled);\n useTopAnchorReserve(topAnchorEnabled);\n const ref = useComposedRefs(\n forwardedRef,\n autoScrollRef,\n viewportSizeRef,\n viewportElementRef,\n );\n\n return (\n <Primitive.div {...rest} ref={ref}>\n {children}\n </Primitive.div>\n );\n },\n);\n\nThreadPrimitiveViewportScrollable.displayName =\n \"ThreadPrimitive.ViewportScrollable\";\n\n/**\n * A scrollable viewport container for thread messages.\n *\n * This component provides a scrollable area for displaying thread messages with\n * automatic scrolling capabilities. It manages the viewport state and provides\n * context for child components to access viewport-related functionality.\n *\n * @example\n * ```tsx\n * <ThreadPrimitive.Viewport turnAnchor=\"top\">\n * <ThreadPrimitive.Messages>\n * {() => <MyMessage />}\n * </ThreadPrimitive.Messages>\n * </ThreadPrimitive.Viewport>\n * ```\n */\nexport const ThreadPrimitiveViewport = forwardRef<\n ThreadPrimitiveViewport.Element,\n ThreadPrimitiveViewport.Props\n>(({ turnAnchor, topAnchorMessageClamp, ...props }, ref) => {\n return (\n <ThreadPrimitiveViewportProvider\n options={{ turnAnchor, topAnchorMessageClamp }}\n >\n <ThreadPrimitiveViewportScrollable {...props} ref={ref} />\n </ThreadPrimitiveViewportProvider>\n );\n});\n\nThreadPrimitiveViewport.displayName = \"ThreadPrimitive.Viewport\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ThreadViewport.js","names":["c","_c","useComposedRefs","Primitive","ComponentRef","forwardRef","ComponentPropsWithoutRef","useCallback","useMemo","useAuiState","useManagedRef","useThreadViewportAutoScroll","ThreadPrimitiveViewportProvider","useSizeHandle","useThreadViewport","useThreadViewportStore","useTopAnchorReserve","useIsomorphicLayoutEffect","getActiveTopAnchorAnchorId","getActiveTopAnchorTargetId","isTopAnchorTurnValid","ThreadPrimitiveViewport","Element","div","Props","autoScroll","turnAnchor","topAnchorMessageClamp","tallerThan","visibleHeight","scrollToBottomOnRunStart","scrollToBottomOnInitialize","scrollToBottomOnThreadSwitch","useViewportSizeRef","register","_temp","getHeight","_temp2","useViewportElementRef","registerViewportElement","_temp3","useTopAnchorTurn","enabled","$","threadViewportStore","t0","s","thread","activeAnchorId","t1","s_0","activeTargetId","topAnchorTurn","_temp4","t2","bb0","t3","anchorId","targetId","activeTurn","s_2","messages","topAnchorTurnIsValid","t4","t5","getState","setTopAnchorTurn","t6","t7","state","current","ThreadPrimitiveViewportScrollable","forwardedRef","children","rest","autoScrollRef","viewportSizeRef","viewportElementRef","topAnchorEnabled","ref","displayName","props","registerViewport","el","clientHeight","s_1","getHeight","_c","Primitive"],"sources":["../../../src/primitives/thread/ThreadViewport.tsx"],"sourcesContent":["\"use client\";\n\nimport { useComposedRefs } from \"radix-ui/internal\";\nimport { Primitive } from \"../../utils/Primitive\";\nimport {\n type ComponentRef,\n forwardRef,\n type ComponentPropsWithoutRef,\n useCallback,\n useMemo,\n} from \"react\";\nimport { useAuiState } from \"@assistant-ui/store\";\nimport { useManagedRef } from \"../../utils/hooks/useManagedRef\";\nimport { useThreadViewportAutoScroll } from \"./useThreadViewportAutoScroll\";\nimport { ThreadPrimitiveViewportProvider } from \"../../context/providers/ThreadViewportProvider\";\nimport { useSizeHandle } from \"../../utils/hooks/useSizeHandle\";\nimport {\n useThreadViewport,\n useThreadViewportStore,\n} from \"../../context/react/ThreadViewportContext\";\nimport { useTopAnchorReserve } from \"./topAnchor/useTopAnchorReserve\";\nimport { useIsomorphicLayoutEffect } from \"../../utils/useIsomorphicLayoutEffect\";\nimport {\n getActiveTopAnchorAnchorId,\n getActiveTopAnchorTargetId,\n isTopAnchorTurnValid,\n} from \"./topAnchor/topAnchorTurn\";\n\nexport namespace ThreadPrimitiveViewport {\n export type Element = ComponentRef<typeof Primitive.div>;\n export type Props = ComponentPropsWithoutRef<typeof Primitive.div> & {\n /**\n * Whether to automatically scroll to the bottom when new messages are added.\n * When enabled, the viewport will automatically scroll to show the latest content.\n *\n * Default false if `turnAnchor` is \"top\", otherwise defaults to true.\n */\n autoScroll?: boolean | undefined;\n\n /**\n * Controls scroll anchoring behavior for new messages.\n * - \"bottom\" (default): Messages anchor at the bottom, classic chat behavior.\n * - \"top\": New user messages anchor at the top of the viewport for a focused reading experience.\n */\n turnAnchor?: \"top\" | \"bottom\" | undefined;\n\n /**\n * Clamps tall user messages so the assistant response stays in view.\n *\n * @default { tallerThan: \"10em\", visibleHeight: \"6em\" }\n */\n topAnchorMessageClamp?: {\n /**\n * Clamp messages taller than this. Supports `px`, `em`, and `rem`.\n *\n * @default \"10em\"\n */\n tallerThan?: string;\n /**\n * Visible portion of clamped messages. Supports `px`, `em`, and `rem`.\n *\n * @default \"6em\"\n */\n visibleHeight?: string;\n };\n\n /**\n * Whether to scroll to bottom when a new run starts.\n *\n * Defaults to true.\n */\n scrollToBottomOnRunStart?: boolean | undefined;\n\n /**\n * Whether to scroll to bottom when thread history is first loaded.\n *\n * Defaults to true.\n */\n scrollToBottomOnInitialize?: boolean | undefined;\n\n /**\n * Whether to scroll to bottom when switching to a different thread.\n *\n * Defaults to true.\n */\n scrollToBottomOnThreadSwitch?: boolean | undefined;\n };\n}\n\nconst useViewportSizeRef = () => {\n const register = useThreadViewport((s) => s.registerViewport);\n const getHeight = useCallback((el: HTMLElement) => el.clientHeight, []);\n return useSizeHandle(register, getHeight);\n};\n\nconst useViewportElementRef = () => {\n const registerViewportElement = useThreadViewport(\n (s) => s.registerViewportElement,\n );\n\n return useManagedRef(registerViewportElement);\n};\n\nconst useTopAnchorTurn = (enabled: boolean) => {\n const threadViewportStore = useThreadViewportStore();\n const activeAnchorId = useAuiState((s) => {\n if (!enabled) return undefined;\n return getActiveTopAnchorAnchorId(s.thread);\n });\n const activeTargetId = useAuiState((s) => {\n if (!enabled) return undefined;\n return getActiveTopAnchorTargetId(s.thread);\n });\n const topAnchorTurn = useThreadViewport((s) => s.topAnchorTurn);\n const activeTurn = useMemo(() => {\n if (!activeAnchorId || !activeTargetId) return null;\n return { anchorId: activeAnchorId, targetId: activeTargetId };\n }, [activeAnchorId, activeTargetId]);\n\n const topAnchorTurnIsValid = useAuiState(\n (s) =>\n enabled &&\n !!topAnchorTurn &&\n isTopAnchorTurnValid(topAnchorTurn, s.thread.messages),\n );\n\n useIsomorphicLayoutEffect(() => {\n if (!topAnchorTurn || topAnchorTurnIsValid) return;\n\n threadViewportStore.getState().setTopAnchorTurn(null);\n }, [threadViewportStore, topAnchorTurn, topAnchorTurnIsValid]);\n\n useIsomorphicLayoutEffect(() => {\n if (!activeTurn) return;\n\n const state = threadViewportStore.getState();\n const current = state.topAnchorTurn;\n if (\n current?.anchorId === activeTurn.anchorId &&\n current.targetId === activeTurn.targetId\n ) {\n return;\n }\n\n state.setTopAnchorTurn(activeTurn);\n }, [activeTurn, threadViewportStore]);\n};\n\nconst ThreadPrimitiveViewportScrollable = forwardRef<\n ThreadPrimitiveViewport.Element,\n ThreadPrimitiveViewport.Props\n>(\n (\n {\n autoScroll,\n scrollToBottomOnRunStart,\n scrollToBottomOnInitialize,\n scrollToBottomOnThreadSwitch,\n children,\n ...rest\n },\n forwardedRef,\n ) => {\n const autoScrollRef = useThreadViewportAutoScroll<HTMLDivElement>({\n autoScroll,\n scrollToBottomOnRunStart,\n scrollToBottomOnInitialize,\n scrollToBottomOnThreadSwitch,\n });\n const viewportSizeRef = useViewportSizeRef();\n const viewportElementRef = useViewportElementRef();\n const threadViewportStore = useThreadViewportStore();\n const turnAnchor = threadViewportStore.getState().turnAnchor;\n const topAnchorEnabled = turnAnchor === \"top\";\n useTopAnchorTurn(topAnchorEnabled);\n useTopAnchorReserve(topAnchorEnabled);\n const ref = useComposedRefs(\n forwardedRef,\n autoScrollRef,\n viewportSizeRef,\n viewportElementRef,\n );\n\n return (\n <Primitive.div {...rest} ref={ref}>\n {children}\n </Primitive.div>\n );\n },\n);\n\nThreadPrimitiveViewportScrollable.displayName =\n \"ThreadPrimitive.ViewportScrollable\";\n\n/**\n * A scrollable viewport container for thread messages.\n *\n * This component provides a scrollable area for displaying thread messages with\n * automatic scrolling capabilities. It manages the viewport state and provides\n * context for child components to access viewport-related functionality.\n *\n * @example\n * ```tsx\n * <ThreadPrimitive.Viewport turnAnchor=\"top\">\n * <ThreadPrimitive.Messages>\n * {() => <MyMessage />}\n * </ThreadPrimitive.Messages>\n * </ThreadPrimitive.Viewport>\n * ```\n */\nexport const ThreadPrimitiveViewport = forwardRef<\n ThreadPrimitiveViewport.Element,\n ThreadPrimitiveViewport.Props\n>(({ turnAnchor, topAnchorMessageClamp, ...props }, ref) => {\n return (\n <ThreadPrimitiveViewportProvider\n options={{ turnAnchor, topAnchorMessageClamp }}\n >\n <ThreadPrimitiveViewportScrollable {...props} ref={ref} />\n </ThreadPrimitiveViewportProvider>\n );\n});\n\nThreadPrimitiveViewport.displayName = \"ThreadPrimitive.Viewport\";\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useThreadViewportStore } from "../../../context/react/ThreadViewportContext.js";
|
|
3
|
+
import { useIsomorphicLayoutEffect } from "../../../utils/useIsomorphicLayoutEffect.js";
|
|
3
4
|
import { mountTopAnchorReserve } from "./mountTopAnchorReserve.js";
|
|
4
5
|
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
|
|
5
|
-
import { useLayoutEffect } from "@assistant-ui/tap/react-shim";
|
|
6
6
|
//#region src/primitives/thread/topAnchor/useTopAnchorReserve.ts
|
|
7
7
|
/**
|
|
8
8
|
* Mounts the top-turn-anchor reserve element against the active
|
|
@@ -28,7 +28,7 @@ const useTopAnchorReserve = (enabled) => {
|
|
|
28
28
|
t0 = $[2];
|
|
29
29
|
t1 = $[3];
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
useIsomorphicLayoutEffect(t0, t1);
|
|
32
32
|
};
|
|
33
33
|
//#endregion
|
|
34
34
|
export { useTopAnchorReserve };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTopAnchorReserve.js","names":["c","_c","
|
|
1
|
+
{"version":3,"file":"useTopAnchorReserve.js","names":["c","_c","useIsomorphicLayoutEffect","useThreadViewportStore","mountTopAnchorReserve","useTopAnchorReserve","enabled","$","threadViewportStore","t0","t1","_c"],"sources":["../../../../src/primitives/thread/topAnchor/useTopAnchorReserve.ts"],"sourcesContent":["\"use client\";\n\nimport { useIsomorphicLayoutEffect } from \"../../../utils/useIsomorphicLayoutEffect\";\nimport { useThreadViewportStore } from \"../../../context/react/ThreadViewportContext\";\nimport { mountTopAnchorReserve } from \"./mountTopAnchorReserve\";\n\n/**\n * Mounts the top-turn-anchor reserve element against the active\n * `ThreadViewport` store. Call this from inside the scrollable viewport so\n * the reserve `<div>` is appended next to the streaming assistant message.\n */\nexport const useTopAnchorReserve = (enabled: boolean) => {\n const threadViewportStore = useThreadViewportStore();\n\n useIsomorphicLayoutEffect(() => {\n if (!enabled) return;\n return mountTopAnchorReserve(threadViewportStore);\n }, [enabled, threadViewportStore]);\n};\n"],"mappings":";;;;;;;;;;;AAWA,MAAaK,uBAAsBC,YAAA;CAAA,MAAAC,IAAAN,EAAA,CAAA;CACjC,MAAAO,sBAA4BL,uBAAuB;CAAE,IAAAM;CAAA,IAAAC;CAAA,IAAAH,EAAA,OAAAD,WAAAC,EAAA,OAAAC,qBAAA;EAE3BC,WAAA;GACxB,IAAI,CAACH,SAAO;GAAS,OACdF,sBAAsBI,mBAAmB;EAAC;EAChDE,KAAA,CAACJ,SAASE,mBAAmB;EAACD,EAAA,KAAAD;EAAAC,EAAA,KAAAC;EAAAD,EAAA,KAAAE;EAAAF,EAAA,KAAAG;CAAA,OAAA;EAAAD,KAAAF,EAAA;EAAAG,KAAAH,EAAA;CAAA;CAHjCL,0BAA0BO,IAGvBC,EAA8B;AAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useThreadViewportAutoScroll.d.ts","sourceRoot":"","sources":["../../../src/primitives/thread/useThreadViewportAutoScroll.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"useThreadViewportAutoScroll.d.ts","sourceRoot":"","sources":["../../../src/primitives/thread/useThreadViewportAutoScroll.ts"],"names":[],"mappings":"AAGA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,OAAO,CAAC;AAwH9D,yBAAiB,2BAA2B,CAAC;IAC3C,KAAY,OAAO,GAAG;QACpB;;;;;WAKG;QACH,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAEjC;;;;WAIG;QACH,wBAAwB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAE/C;;;;WAIG;QACH,0BAA0B,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAEjD;;;;WAIG;QACH,4BAA4B,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;KACpD,CAAC;CACH;AAED,eAAO,MAAM,2BAA2B,GAAI,QAAQ,SAAS,WAAW,EAAE,qGAKvE,2BAA2B,CAAC,OAAO,KAAG,WAAW,CAAC,QAAQ,CA+Y5D,CAAC"}
|
|
@@ -1,25 +1,97 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useThreadViewportStore } from "../../context/react/ThreadViewportContext.js";
|
|
3
3
|
import { writableStore } from "../../context/ReadonlyStore.js";
|
|
4
|
+
import { useIsomorphicLayoutEffect } from "../../utils/useIsomorphicLayoutEffect.js";
|
|
4
5
|
import { useOnScrollToBottom } from "../../utils/hooks/useOnScrollToBottom.js";
|
|
5
6
|
import { useManagedRef } from "../../utils/hooks/useManagedRef.js";
|
|
6
7
|
import { useOnResizeContent } from "../../utils/hooks/useOnResizeContent.js";
|
|
7
|
-
import { useCallback,
|
|
8
|
-
import { useAuiEvent, useAuiState } from "@assistant-ui/store";
|
|
8
|
+
import { useCallback, useRef } from "@assistant-ui/tap/react-shim";
|
|
9
|
+
import { useAui, useAuiEvent, useAuiState } from "@assistant-ui/store";
|
|
9
10
|
import { useComposedRefs } from "radix-ui/internal";
|
|
10
11
|
import { isUserScrollUp, isViewportAtBottom, viewportOverflows } from "@assistant-ui/store/client";
|
|
11
12
|
//#region src/primitives/thread/useThreadViewportAutoScroll.ts
|
|
12
|
-
const
|
|
13
|
+
const SCROLL_KEYS = /* @__PURE__ */ new Set([
|
|
14
|
+
" ",
|
|
15
|
+
"ArrowUp",
|
|
16
|
+
"ArrowDown",
|
|
17
|
+
"PageUp",
|
|
18
|
+
"PageDown",
|
|
19
|
+
"Home",
|
|
20
|
+
"End"
|
|
21
|
+
]);
|
|
22
|
+
const INTENT_CANCEL_KEYS = /* @__PURE__ */ new Set(["Enter", ...SCROLL_KEYS]);
|
|
13
23
|
const TEXT_ENTRY_SELECTOR = [
|
|
14
24
|
"textarea",
|
|
15
25
|
"select",
|
|
16
26
|
"[contenteditable]:not([contenteditable='false'])",
|
|
17
27
|
"input:not([type='checkbox']):not([type='radio']):not([type='button']):not([type='submit']):not([type='reset']):not([type='image']):not([type='range']):not([type='file']):not([type='color'])"
|
|
18
28
|
].join(", ");
|
|
29
|
+
const MESSAGE_SELECTOR = "[data-message-id]";
|
|
30
|
+
const DISCLOSURE_SELECTOR = "[aria-expanded], summary";
|
|
31
|
+
const isOpeningDisclosure = (target) => {
|
|
32
|
+
const control = target.closest(DISCLOSURE_SELECTOR);
|
|
33
|
+
if (!control) return false;
|
|
34
|
+
if (control.tagName === "SUMMARY") return control.parentElement?.hasAttribute("open") === false;
|
|
35
|
+
return control.getAttribute("aria-expanded") === "false" && (control.getAttribute("aria-haspopup") ?? "false") === "false" && control.getAttribute("role") !== "combobox";
|
|
36
|
+
};
|
|
37
|
+
const offsetInViewport = (element, viewport) => element.getBoundingClientRect().top - viewport.getBoundingClientRect().top;
|
|
38
|
+
const measureFirstMessage = (viewport) => {
|
|
39
|
+
const element = viewport.querySelector(MESSAGE_SELECTOR);
|
|
40
|
+
const id = element?.dataset["messageId"];
|
|
41
|
+
return element && id ? {
|
|
42
|
+
id,
|
|
43
|
+
offset: offsetInViewport(element, viewport)
|
|
44
|
+
} : null;
|
|
45
|
+
};
|
|
46
|
+
/** The first message that starts in view, else the one spanning its top edge. */
|
|
47
|
+
const measureReaderMessage = (viewport) => {
|
|
48
|
+
const { top, bottom } = viewport.getBoundingClientRect();
|
|
49
|
+
let spanning = null;
|
|
50
|
+
for (const element of viewport.querySelectorAll(MESSAGE_SELECTOR)) {
|
|
51
|
+
const rect = element.getBoundingClientRect();
|
|
52
|
+
if (rect.bottom <= top) continue;
|
|
53
|
+
if (rect.top >= bottom) break;
|
|
54
|
+
const id = element.dataset["messageId"];
|
|
55
|
+
if (!id) continue;
|
|
56
|
+
if (rect.top >= top) return {
|
|
57
|
+
id,
|
|
58
|
+
offset: rect.top - top
|
|
59
|
+
};
|
|
60
|
+
spanning ??= {
|
|
61
|
+
id,
|
|
62
|
+
offset: rect.top - top
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return spanning;
|
|
66
|
+
};
|
|
67
|
+
const findMessage = (viewport, id) => {
|
|
68
|
+
for (const element of viewport.querySelectorAll(MESSAGE_SELECTOR)) if (element.dataset["messageId"] === id) return element;
|
|
69
|
+
return null;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Scrolls so the message sits at `anchor.offset` again, recording the
|
|
73
|
+
* position it scrolls to in `heldScrollTop` before the scroll event can
|
|
74
|
+
* report it; false when the message is gone.
|
|
75
|
+
*/
|
|
76
|
+
const keepMessageAt = (viewport, anchor, heldScrollTop) => {
|
|
77
|
+
const element = findMessage(viewport, anchor.id);
|
|
78
|
+
if (!element) return false;
|
|
79
|
+
const delta = offsetInViewport(element, viewport) - anchor.offset;
|
|
80
|
+
heldScrollTop.current = viewport.scrollTop + delta;
|
|
81
|
+
if (Math.abs(delta) >= 1) viewport.scrollTo({
|
|
82
|
+
top: heldScrollTop.current,
|
|
83
|
+
behavior: "instant"
|
|
84
|
+
});
|
|
85
|
+
heldScrollTop.current = viewport.scrollTop;
|
|
86
|
+
return true;
|
|
87
|
+
};
|
|
19
88
|
const useThreadViewportAutoScroll = ({ autoScroll, scrollToBottomOnRunStart = true, scrollToBottomOnInitialize = true, scrollToBottomOnThreadSwitch = true }) => {
|
|
89
|
+
const aui = useAui();
|
|
20
90
|
const divRef = useRef(null);
|
|
21
91
|
const hasMessages = useAuiState((s) => s.thread.messages.length > 0);
|
|
22
|
-
const
|
|
92
|
+
const firstMessageId = useAuiState((s_0) => s_0.thread.messages[0]?.id);
|
|
93
|
+
const threadId = useAuiState((s_1) => s_1.threadListItem.id);
|
|
94
|
+
const isRunning = useAuiState((s_2) => s_2.thread.isRunning);
|
|
23
95
|
const initializeScrollRequestedRef = useRef(false);
|
|
24
96
|
const scheduledFrameRef = useRef(null);
|
|
25
97
|
const threadViewportStore = useThreadViewportStore();
|
|
@@ -28,10 +100,15 @@ const useThreadViewportAutoScroll = ({ autoScroll, scrollToBottomOnRunStart = tr
|
|
|
28
100
|
const lastScrollHeight = useRef(0);
|
|
29
101
|
const lastObservedScrollHeight = useRef(0);
|
|
30
102
|
const lastObservedClientHeight = useRef(0);
|
|
103
|
+
const firstMessageRef = useRef(null);
|
|
104
|
+
const prependAnchorRef = useRef(null);
|
|
105
|
+
const heldScrollTopRef = useRef(0);
|
|
31
106
|
const scrollingToBottomBehaviorRef = useRef(null);
|
|
32
107
|
const followBottomRef = useRef(autoScroll);
|
|
108
|
+
const followPausedRef = useRef(false);
|
|
109
|
+
const scrolledSincePauseRef = useRef(false);
|
|
33
110
|
const previousAutoScrollRef = useRef(autoScroll);
|
|
34
|
-
|
|
111
|
+
useIsomorphicLayoutEffect(() => {
|
|
35
112
|
const previousAutoScroll = previousAutoScrollRef.current;
|
|
36
113
|
previousAutoScrollRef.current = autoScroll;
|
|
37
114
|
if (previousAutoScroll || !autoScroll) return;
|
|
@@ -42,11 +119,15 @@ const useThreadViewportAutoScroll = ({ autoScroll, scrollToBottomOnRunStart = tr
|
|
|
42
119
|
const div_0 = divRef.current;
|
|
43
120
|
if (!div_0) return;
|
|
44
121
|
followBottomRef.current = true;
|
|
122
|
+
followPausedRef.current = false;
|
|
45
123
|
scrollingToBottomBehaviorRef.current = behavior;
|
|
124
|
+
prependAnchorRef.current = null;
|
|
46
125
|
div_0.scrollTo({
|
|
47
126
|
top: div_0.scrollHeight,
|
|
48
127
|
behavior
|
|
49
128
|
});
|
|
129
|
+
lastScrollTop.current = div_0.scrollTop;
|
|
130
|
+
lastScrollHeight.current = div_0.scrollHeight;
|
|
50
131
|
}, []);
|
|
51
132
|
const cancelScheduledFrame = useCallback(() => {
|
|
52
133
|
if (scheduledFrameRef.current === null) return;
|
|
@@ -61,23 +142,27 @@ const useThreadViewportAutoScroll = ({ autoScroll, scrollToBottomOnRunStart = tr
|
|
|
61
142
|
scrollToBottom(behavior_0);
|
|
62
143
|
});
|
|
63
144
|
}, [cancelScheduledFrame, scrollToBottom]);
|
|
64
|
-
|
|
145
|
+
useIsomorphicLayoutEffect(() => () => cancelScheduledFrame(), [cancelScheduledFrame]);
|
|
65
146
|
const hasActiveTopAnchor = useCallback(() => {
|
|
66
147
|
const state = threadViewportStore.getState();
|
|
67
148
|
return state.turnAnchor === "top" && state.element.viewport === divRef.current && state.element.anchor !== null;
|
|
68
149
|
}, [threadViewportStore]);
|
|
69
|
-
const
|
|
150
|
+
const onScroll = () => {
|
|
70
151
|
const div_1 = divRef.current;
|
|
71
152
|
if (!div_1) return;
|
|
153
|
+
if (prependAnchorRef.current && Math.abs(div_1.scrollTop - heldScrollTopRef.current) >= 1) prependAnchorRef.current = null;
|
|
72
154
|
const isAtBottom = threadViewportStore.getState().isAtBottom;
|
|
73
155
|
const newIsAtBottom = isViewportAtBottom(div_1);
|
|
74
|
-
if (!newIsAtBottom && lastScrollTop.current < div_1.scrollTop) {} else {
|
|
156
|
+
if (scrollingToBottomBehaviorRef.current !== null && !newIsAtBottom && lastScrollTop.current < div_1.scrollTop) {} else {
|
|
75
157
|
const userScrolledUp = isUserScrollUp({
|
|
76
158
|
scrollTop: lastScrollTop.current,
|
|
77
159
|
scrollHeight: lastScrollHeight.current
|
|
78
160
|
}, div_1);
|
|
79
161
|
if (newIsAtBottom) {
|
|
80
|
-
if (viewportOverflows(div_1))
|
|
162
|
+
if (viewportOverflows(div_1)) {
|
|
163
|
+
scrollingToBottomBehaviorRef.current = null;
|
|
164
|
+
if (scrolledSincePauseRef.current) followPausedRef.current = false;
|
|
165
|
+
}
|
|
81
166
|
if (autoScroll) followBottomRef.current = true;
|
|
82
167
|
} else if (userScrolledUp) {
|
|
83
168
|
cancelScheduledFrame();
|
|
@@ -88,40 +173,130 @@ const useThreadViewportAutoScroll = ({ autoScroll, scrollToBottomOnRunStart = tr
|
|
|
88
173
|
}
|
|
89
174
|
lastScrollTop.current = div_1.scrollTop;
|
|
90
175
|
lastScrollHeight.current = div_1.scrollHeight;
|
|
176
|
+
firstMessageRef.current = measureFirstMessage(div_1);
|
|
91
177
|
};
|
|
92
|
-
const
|
|
178
|
+
const onScrollRef = useRef(onScroll);
|
|
179
|
+
useIsomorphicLayoutEffect(() => {
|
|
180
|
+
onScrollRef.current = onScroll;
|
|
181
|
+
});
|
|
182
|
+
const handleScroll = useCallback(() => onScrollRef.current(), []);
|
|
183
|
+
const onResize = () => {
|
|
93
184
|
const div_2 = divRef.current;
|
|
94
185
|
if (!div_2) return;
|
|
95
186
|
const { scrollHeight, clientHeight } = div_2;
|
|
96
187
|
if (scrollHeight === lastObservedScrollHeight.current && clientHeight === lastObservedClientHeight.current) return;
|
|
97
188
|
lastObservedScrollHeight.current = scrollHeight;
|
|
98
189
|
lastObservedClientHeight.current = clientHeight;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
190
|
+
if (scheduledFrameRef.current === null) {
|
|
191
|
+
const scrollBehavior = scrollingToBottomBehaviorRef.current;
|
|
192
|
+
if (scrollBehavior && hasActiveTopAnchor()) scrollingToBottomBehaviorRef.current = null;
|
|
193
|
+
else if (scrollBehavior) scrollToBottom(scrollBehavior);
|
|
194
|
+
else if (autoScroll && !(isRunning && hasActiveTopAnchor()) && followBottomRef.current && !followPausedRef.current) scrollToBottom("instant");
|
|
195
|
+
else if (prependAnchorRef.current && !keepMessageAt(div_2, prependAnchorRef.current, heldScrollTopRef)) prependAnchorRef.current = null;
|
|
196
|
+
}
|
|
103
197
|
handleScroll();
|
|
198
|
+
};
|
|
199
|
+
const onResizeRef = useRef(onResize);
|
|
200
|
+
useIsomorphicLayoutEffect(() => {
|
|
201
|
+
onResizeRef.current = onResize;
|
|
104
202
|
});
|
|
105
|
-
const
|
|
203
|
+
const resizeRef = useOnResizeContent(() => onResizeRef.current());
|
|
204
|
+
const scrollRef = useManagedRef(useCallback((el) => {
|
|
106
205
|
const cancelPendingScrollToBottom = () => {
|
|
206
|
+
if (scrollingToBottomBehaviorRef.current === null && scheduledFrameRef.current === null) return;
|
|
107
207
|
cancelScheduledFrame();
|
|
108
208
|
scrollingToBottomBehaviorRef.current = null;
|
|
209
|
+
handleScroll();
|
|
109
210
|
};
|
|
110
211
|
const cancelOnKeyDown = (event) => {
|
|
111
|
-
if (!
|
|
212
|
+
if (!INTENT_CANCEL_KEYS.has(event.key)) return;
|
|
112
213
|
if (event.target?.closest?.(TEXT_ENTRY_SELECTOR)) return;
|
|
113
214
|
cancelPendingScrollToBottom();
|
|
114
215
|
};
|
|
216
|
+
const pauseFollowOnExpand = (event_0) => {
|
|
217
|
+
const target_0 = event_0.target;
|
|
218
|
+
if (!target_0?.closest?.(MESSAGE_SELECTOR) || target_0.closest(TEXT_ENTRY_SELECTOR) || !isOpeningDisclosure(target_0)) return;
|
|
219
|
+
followPausedRef.current = true;
|
|
220
|
+
scrolledSincePauseRef.current = false;
|
|
221
|
+
cancelPendingScrollToBottom();
|
|
222
|
+
};
|
|
223
|
+
const noteScrollGesture = (event_1) => {
|
|
224
|
+
if (event_1 instanceof KeyboardEvent) {
|
|
225
|
+
if (!SCROLL_KEYS.has(event_1.key)) return;
|
|
226
|
+
if (event_1.target?.closest?.(TEXT_ENTRY_SELECTOR)) return;
|
|
227
|
+
} else if (event_1.type === "pointerdown" && event_1.target !== el) return;
|
|
228
|
+
scrolledSincePauseRef.current = true;
|
|
229
|
+
};
|
|
230
|
+
const releasePrependAnchor = () => {
|
|
231
|
+
prependAnchorRef.current = null;
|
|
232
|
+
};
|
|
233
|
+
const gestures = [
|
|
234
|
+
"pointerdown",
|
|
235
|
+
"wheel",
|
|
236
|
+
"touchstart",
|
|
237
|
+
"keydown"
|
|
238
|
+
];
|
|
239
|
+
const resumeGestures = [
|
|
240
|
+
"pointerdown",
|
|
241
|
+
"wheel",
|
|
242
|
+
"touchmove",
|
|
243
|
+
"keydown"
|
|
244
|
+
];
|
|
115
245
|
el.addEventListener("scroll", handleScroll);
|
|
116
246
|
el.addEventListener("pointerdown", cancelPendingScrollToBottom);
|
|
247
|
+
el.addEventListener("wheel", cancelPendingScrollToBottom, { passive: true });
|
|
248
|
+
el.addEventListener("touchstart", cancelPendingScrollToBottom, { passive: true });
|
|
117
249
|
el.addEventListener("keydown", cancelOnKeyDown);
|
|
250
|
+
el.addEventListener("click", pauseFollowOnExpand, { capture: true });
|
|
251
|
+
for (const gesture of resumeGestures) el.addEventListener(gesture, noteScrollGesture, { passive: true });
|
|
252
|
+
for (const gesture_0 of gestures) el.addEventListener(gesture_0, releasePrependAnchor, { passive: true });
|
|
118
253
|
return () => {
|
|
119
254
|
el.removeEventListener("scroll", handleScroll);
|
|
120
255
|
el.removeEventListener("pointerdown", cancelPendingScrollToBottom);
|
|
256
|
+
el.removeEventListener("wheel", cancelPendingScrollToBottom);
|
|
257
|
+
el.removeEventListener("touchstart", cancelPendingScrollToBottom);
|
|
121
258
|
el.removeEventListener("keydown", cancelOnKeyDown);
|
|
259
|
+
el.removeEventListener("click", pauseFollowOnExpand, { capture: true });
|
|
260
|
+
for (const gesture_1 of resumeGestures) el.removeEventListener(gesture_1, noteScrollGesture);
|
|
261
|
+
for (const gesture_2 of gestures) el.removeEventListener(gesture_2, releasePrependAnchor);
|
|
122
262
|
};
|
|
263
|
+
}, [cancelScheduledFrame, handleScroll]));
|
|
264
|
+
const previousRef = useRef({
|
|
265
|
+
threadId,
|
|
266
|
+
firstMessageId
|
|
123
267
|
});
|
|
124
|
-
|
|
268
|
+
useIsomorphicLayoutEffect(() => {
|
|
269
|
+
const previous = previousRef.current;
|
|
270
|
+
previousRef.current = {
|
|
271
|
+
threadId,
|
|
272
|
+
firstMessageId
|
|
273
|
+
};
|
|
274
|
+
if (previous.threadId !== threadId) {
|
|
275
|
+
prependAnchorRef.current = null;
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const div_3 = divRef.current;
|
|
279
|
+
const previousFirstMessageId = previous.firstMessageId;
|
|
280
|
+
if (!div_3 || previousFirstMessageId === void 0 || previousFirstMessageId === firstMessageId || autoScroll && followBottomRef.current && !followPausedRef.current || scrollingToBottomBehaviorRef.current !== null) return;
|
|
281
|
+
if (!aui.thread.getState().messages.some((message, index) => index > 0 && message.id === previousFirstMessageId)) return;
|
|
282
|
+
const anchor = firstMessageRef.current;
|
|
283
|
+
if (anchor?.id === previousFirstMessageId && keepMessageAt(div_3, anchor, heldScrollTopRef)) prependAnchorRef.current = measureReaderMessage(div_3) ?? anchor;
|
|
284
|
+
else {
|
|
285
|
+
const top = div_3.scrollHeight - (lastScrollHeight.current - lastScrollTop.current);
|
|
286
|
+
if (Math.abs(div_3.scrollTop - top) >= 1) div_3.scrollTo({
|
|
287
|
+
top,
|
|
288
|
+
behavior: "instant"
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
lastScrollTop.current = div_3.scrollTop;
|
|
292
|
+
lastScrollHeight.current = div_3.scrollHeight;
|
|
293
|
+
}, [
|
|
294
|
+
aui,
|
|
295
|
+
autoScroll,
|
|
296
|
+
firstMessageId,
|
|
297
|
+
threadId
|
|
298
|
+
]);
|
|
299
|
+
useIsomorphicLayoutEffect(() => {
|
|
125
300
|
if (!scrollToBottomOnInitialize) return;
|
|
126
301
|
if (!hasMessages) {
|
|
127
302
|
initializeScrollRequestedRef.current = false;
|
|
@@ -140,11 +315,14 @@ const useThreadViewportAutoScroll = ({ autoScroll, scrollToBottomOnRunStart = tr
|
|
|
140
315
|
scrollToBottom(behavior_1);
|
|
141
316
|
});
|
|
142
317
|
useAuiEvent("thread.runStart", () => {
|
|
318
|
+
prependAnchorRef.current = null;
|
|
319
|
+
followPausedRef.current = false;
|
|
143
320
|
if (!scrollToBottomOnRunStart) return;
|
|
144
321
|
if (threadViewportStore.getState().turnAnchor === "top") return;
|
|
145
322
|
scheduleScrollToBottom("auto");
|
|
146
323
|
});
|
|
147
324
|
useAuiEvent("threads.selectionChanged", () => {
|
|
325
|
+
followPausedRef.current = false;
|
|
148
326
|
if (!scrollToBottomOnThreadSwitch) return;
|
|
149
327
|
scheduleScrollToBottom("instant");
|
|
150
328
|
});
|