@blocksdiy/react-common 1.70.2 → 1.71.1
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/comments/anchor.d.ts +96 -0
- package/dist/comments/anchor.d.ts.map +1 -0
- package/dist/comments/anchor.js +346 -0
- package/dist/comments/api.d.ts +38 -0
- package/dist/comments/api.d.ts.map +1 -0
- package/dist/comments/api.js +62 -0
- package/dist/comments/comment-agents.d.ts +28 -0
- package/dist/comments/comment-agents.d.ts.map +1 -0
- package/dist/comments/comment-agents.js +32 -0
- package/dist/comments/comment-mode.d.ts +115 -0
- package/dist/comments/comment-mode.d.ts.map +1 -0
- package/dist/comments/comment-mode.js +176 -0
- package/dist/comments/composer.d.ts +133 -0
- package/dist/comments/composer.d.ts.map +1 -0
- package/dist/comments/composer.js +273 -0
- package/dist/comments/index.d.ts +19 -0
- package/dist/comments/index.d.ts.map +1 -0
- package/dist/comments/index.js +23 -0
- package/dist/comments/layout.d.ts +25 -0
- package/dist/comments/layout.d.ts.map +1 -0
- package/dist/comments/layout.js +83 -0
- package/dist/comments/mentions.d.ts +32 -0
- package/dist/comments/mentions.d.ts.map +1 -0
- package/dist/comments/mentions.js +30 -0
- package/dist/comments/menu.d.ts +33 -0
- package/dist/comments/menu.d.ts.map +1 -0
- package/dist/comments/menu.js +47 -0
- package/dist/comments/open-comment.d.ts +28 -0
- package/dist/comments/open-comment.d.ts.map +1 -0
- package/dist/comments/open-comment.js +9 -0
- package/dist/comments/page-pick.d.ts +76 -0
- package/dist/comments/page-pick.d.ts.map +1 -0
- package/dist/comments/page-pick.js +283 -0
- package/dist/comments/pick-message.d.ts +11 -0
- package/dist/comments/pick-message.d.ts.map +1 -0
- package/dist/comments/pick-message.js +33 -0
- package/dist/comments/pin-view.d.ts +107 -0
- package/dist/comments/pin-view.d.ts.map +1 -0
- package/dist/comments/pin-view.js +248 -0
- package/dist/comments/pins.d.ts +126 -0
- package/dist/comments/pins.d.ts.map +1 -0
- package/dist/comments/pins.js +218 -0
- package/dist/comments/text-pick.d.ts +69 -0
- package/dist/comments/text-pick.d.ts.map +1 -0
- package/dist/comments/text-pick.js +307 -0
- package/dist/comments/threads-store.d.ts +119 -0
- package/dist/comments/threads-store.d.ts.map +1 -0
- package/dist/comments/threads-store.js +286 -0
- package/dist/comments/types.d.ts +235 -0
- package/dist/comments/types.d.ts.map +1 -0
- package/dist/comments/types.js +109 -0
- package/dist/comments/use-comment-menu.d.ts +69 -0
- package/dist/comments/use-comment-menu.d.ts.map +1 -0
- package/dist/comments/use-comment-menu.js +140 -0
- package/dist/comments/use-held.d.ts +3 -0
- package/dist/comments/use-held.d.ts.map +1 -0
- package/dist/comments/use-held.js +16 -0
- package/dist/comments/use-thread-scroll.d.ts +23 -0
- package/dist/comments/use-thread-scroll.d.ts.map +1 -0
- package/dist/comments/use-thread-scroll.js +89 -0
- package/dist/hooks/agent-conversation-context.d.ts +12 -1
- package/dist/hooks/agent-conversation-context.d.ts.map +1 -1
- package/dist/hooks/agent-conversation-context.js +12 -5
- package/dist/hooks/agent-thread-live-update.d.ts +5 -2
- package/dist/hooks/agent-thread-live-update.d.ts.map +1 -1
- package/dist/hooks/agent-thread-live-update.js +14 -0
- package/dist/hooks/use-agent-conversation.d.ts +8 -0
- package/dist/hooks/use-agent-conversation.d.ts.map +1 -1
- package/dist/hooks/use-agent-conversation.js +10 -0
- package/dist/hooks/use-agent-threads.d.ts +1 -0
- package/dist/hooks/use-agent-threads.d.ts.map +1 -1
- package/dist/hooks/use-agent-threads.js +4 -1
- package/dist/iframe-communication/types.d.ts +25 -0
- package/dist/iframe-communication/types.d.ts.map +1 -1
- package/package.json +5 -2
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type FocusEvent, type KeyboardEvent, type MouseEvent } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* A comment menu's open content carries this attribute: the layer asks at a press on the page
|
|
4
|
+
* whether one is open, so that click only closes it.
|
|
5
|
+
*/
|
|
6
|
+
export declare const COMMENT_MENU_ATTRIBUTE = "data-blocks-comment-menu";
|
|
7
|
+
export interface UseCommentMenuOptions {
|
|
8
|
+
/** Which edge of the trigger the menu lines up with: its start (a picker) or its end (a ⋯). */
|
|
9
|
+
align: "start" | "end";
|
|
10
|
+
/** The height the menu asks for, to open below its trigger or, without room there, above. */
|
|
11
|
+
height: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A menu button of the comments, headless (the registry draws it): the menu is fixed in the window
|
|
15
|
+
* beside its trigger (`menuPosition`), portalled to the comments' layer (`container`) so no
|
|
16
|
+
* panel's overflow clips it and no app menu stack hides it. Opening it (a click, or ↓ on the
|
|
17
|
+
* trigger) moves focus to its checked item, else its first, and ↑ on the trigger to its last
|
|
18
|
+
* (WAI-ARIA's menu button); ↓ ↑ Home End move between items (they
|
|
19
|
+
* take focus by keys only: give them `tabIndex={-1}`); Esc, Tab or Shift+Tab close it with focus
|
|
20
|
+
* back on the trigger, and Esc goes no further (not the thread, not comment mode); focus leaving
|
|
21
|
+
* it, the page scrolling or the window resizing closes it (it stands where its trigger was). A
|
|
22
|
+
* press in it keeps focus where it is, so a browser that doesn't focus buttons on click doesn't
|
|
23
|
+
* blur it shut before the click lands.
|
|
24
|
+
*
|
|
25
|
+
* Spread `rootProps` on an element around the trigger (events from the portalled menu reach it as
|
|
26
|
+
* React bubbles them), `triggerProps` on the button and `menuProps` on the menu's root.
|
|
27
|
+
*/
|
|
28
|
+
export declare const useCommentMenu: ({ align, height }: UseCommentMenuOptions) => {
|
|
29
|
+
isOpen: boolean;
|
|
30
|
+
/** Closes it with focus back on its trigger, as a pick does (WAI-ARIA's menu button). */
|
|
31
|
+
close: () => void;
|
|
32
|
+
/** Where to portal the menu while it is open: the comments' layer the trigger is in, else the body. */
|
|
33
|
+
container: Element | null;
|
|
34
|
+
rootProps: {
|
|
35
|
+
ref: import("react").RefObject<HTMLDivElement | null>;
|
|
36
|
+
onBlur: (event: FocusEvent<HTMLElement>) => void;
|
|
37
|
+
onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;
|
|
38
|
+
};
|
|
39
|
+
triggerProps: {
|
|
40
|
+
ref: import("react").RefObject<HTMLButtonElement | null>;
|
|
41
|
+
id: string;
|
|
42
|
+
"aria-haspopup": "menu";
|
|
43
|
+
"aria-expanded": boolean;
|
|
44
|
+
"aria-controls": string | undefined;
|
|
45
|
+
"data-state": "open" | "closed";
|
|
46
|
+
onClick: () => void;
|
|
47
|
+
onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;
|
|
48
|
+
};
|
|
49
|
+
menuProps: {
|
|
50
|
+
ref: import("react").RefObject<HTMLDivElement | null>;
|
|
51
|
+
id: string;
|
|
52
|
+
role: "menu";
|
|
53
|
+
"aria-labelledby": string;
|
|
54
|
+
"data-blocks-comment-menu": string;
|
|
55
|
+
"data-state": "open";
|
|
56
|
+
"data-side": "top" | "bottom" | undefined;
|
|
57
|
+
style: {
|
|
58
|
+
top?: number | undefined;
|
|
59
|
+
bottom?: number | undefined;
|
|
60
|
+
left?: number | undefined;
|
|
61
|
+
right?: number | undefined;
|
|
62
|
+
maxHeight?: number | undefined;
|
|
63
|
+
};
|
|
64
|
+
onMouseDown: (event: MouseEvent<HTMLElement>) => void;
|
|
65
|
+
onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
export type CommentMenuState = ReturnType<typeof useCommentMenu>;
|
|
69
|
+
//# sourceMappingURL=use-comment-menu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-comment-menu.d.ts","sourceRoot":"","sources":["../../src/comments/use-comment-menu.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,UAAU,EAMhB,MAAM,OAAO,CAAC;AAMf;;;GAGG;AACH,eAAO,MAAM,sBAAsB,6BAA6B,CAAC;AAMjE,MAAM,WAAW,qBAAqB;IACpC,+FAA+F;IAC/F,KAAK,EAAE,OAAO,GAAG,KAAK,CAAC;IACvB,6FAA6F;IAC7F,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,cAAc,GAAI,mBAAmB,qBAAqB;;IAsEnE,yFAAyF;;IAEzF,uGAAuG;;;;wBAIrF,UAAU,CAAC,WAAW,CAAC;2BAKpB,aAAa,CAAC,WAAW,CAAC;;;;;;;;;;2BAgB1B,aAAa,CAAC,WAAW,CAAC;;;;;;;;;;;;;;;;;6BAgBxB,UAAU,CAAC,WAAW,CAAC;2BACzB,aAAa,CAAC,WAAW,CAAC;;CAUlD,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { useCallback, useEffect, useId, useRef, useState, } from "react";
|
|
2
|
+
import { toRect } from "./layout.js";
|
|
3
|
+
import { menuKeyTarget, menuPosition } from "./menu.js";
|
|
4
|
+
import { COMMENTS_LAYER_ATTRIBUTE } from "./pins.js";
|
|
5
|
+
/**
|
|
6
|
+
* A comment menu's open content carries this attribute: the layer asks at a press on the page
|
|
7
|
+
* whether one is open, so that click only closes it.
|
|
8
|
+
*/
|
|
9
|
+
export const COMMENT_MENU_ATTRIBUTE = "data-blocks-comment-menu";
|
|
10
|
+
const ITEM_SELECTOR = '[role="menuitem"], [role="menuitemradio"], [role="menuitemcheckbox"]';
|
|
11
|
+
const itemsIn = (menu) => Array.from(menu?.querySelectorAll(ITEM_SELECTOR) ?? []);
|
|
12
|
+
/**
|
|
13
|
+
* A menu button of the comments, headless (the registry draws it): the menu is fixed in the window
|
|
14
|
+
* beside its trigger (`menuPosition`), portalled to the comments' layer (`container`) so no
|
|
15
|
+
* panel's overflow clips it and no app menu stack hides it. Opening it (a click, or ↓ on the
|
|
16
|
+
* trigger) moves focus to its checked item, else its first, and ↑ on the trigger to its last
|
|
17
|
+
* (WAI-ARIA's menu button); ↓ ↑ Home End move between items (they
|
|
18
|
+
* take focus by keys only: give them `tabIndex={-1}`); Esc, Tab or Shift+Tab close it with focus
|
|
19
|
+
* back on the trigger, and Esc goes no further (not the thread, not comment mode); focus leaving
|
|
20
|
+
* it, the page scrolling or the window resizing closes it (it stands where its trigger was). A
|
|
21
|
+
* press in it keeps focus where it is, so a browser that doesn't focus buttons on click doesn't
|
|
22
|
+
* blur it shut before the click lands.
|
|
23
|
+
*
|
|
24
|
+
* Spread `rootProps` on an element around the trigger (events from the portalled menu reach it as
|
|
25
|
+
* React bubbles them), `triggerProps` on the button and `menuProps` on the menu's root.
|
|
26
|
+
*/
|
|
27
|
+
export const useCommentMenu = ({ align, height }) => {
|
|
28
|
+
// Where it stands and where it is portalled, both read from the trigger as it opens.
|
|
29
|
+
const [opened, setOpened] = useState(null);
|
|
30
|
+
const position = opened?.position ?? null;
|
|
31
|
+
const isOpen = opened !== null;
|
|
32
|
+
const rootRef = useRef(null);
|
|
33
|
+
const triggerRef = useRef(null);
|
|
34
|
+
const menuRef = useRef(null);
|
|
35
|
+
const triggerId = useId();
|
|
36
|
+
const menuId = useId();
|
|
37
|
+
const open = (focus = "checked") => {
|
|
38
|
+
const trigger = triggerRef.current;
|
|
39
|
+
if (!trigger) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
setOpened({
|
|
43
|
+
position: menuPosition(toRect(trigger.getBoundingClientRect()), {
|
|
44
|
+
height,
|
|
45
|
+
align,
|
|
46
|
+
isRtl: getComputedStyle(trigger).direction === "rtl",
|
|
47
|
+
viewportWidth: document.documentElement.clientWidth,
|
|
48
|
+
viewportHeight: window.innerHeight,
|
|
49
|
+
}),
|
|
50
|
+
// The comments' layer the trigger is in, else the body.
|
|
51
|
+
container: trigger.closest(`[${COMMENTS_LAYER_ATTRIBUTE}]`) ?? document.body,
|
|
52
|
+
focus,
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
const close = useCallback(() => setOpened(null), []);
|
|
56
|
+
const closeToTrigger = () => {
|
|
57
|
+
close();
|
|
58
|
+
triggerRef.current?.focus();
|
|
59
|
+
};
|
|
60
|
+
const focusFrom = opened?.focus;
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (!focusFrom) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const all = itemsIn(menuRef.current);
|
|
66
|
+
(focusFrom === "last"
|
|
67
|
+
? all.at(-1)
|
|
68
|
+
: (all.find((item) => item.getAttribute("aria-checked") === "true") ?? all[0]))?.focus();
|
|
69
|
+
// A scroll inside its own list doesn't move it.
|
|
70
|
+
const onScroll = (event) => {
|
|
71
|
+
if (!menuRef.current?.contains(event.target)) {
|
|
72
|
+
close();
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
window.addEventListener("scroll", onScroll, true);
|
|
76
|
+
window.addEventListener("resize", close);
|
|
77
|
+
return () => {
|
|
78
|
+
window.removeEventListener("scroll", onScroll, true);
|
|
79
|
+
window.removeEventListener("resize", close);
|
|
80
|
+
};
|
|
81
|
+
}, [focusFrom, close]);
|
|
82
|
+
const isInside = (node) => !!node && (!!rootRef.current?.contains(node) || !!menuRef.current?.contains(node));
|
|
83
|
+
const { side, ...style } = position ?? {};
|
|
84
|
+
return {
|
|
85
|
+
isOpen,
|
|
86
|
+
/** Closes it with focus back on its trigger, as a pick does (WAI-ARIA's menu button). */
|
|
87
|
+
close: closeToTrigger,
|
|
88
|
+
/** Where to portal the menu while it is open: the comments' layer the trigger is in, else the body. */
|
|
89
|
+
container: opened?.container ?? null,
|
|
90
|
+
rootProps: {
|
|
91
|
+
ref: rootRef,
|
|
92
|
+
onBlur: (event) => {
|
|
93
|
+
if (!isInside(event.relatedTarget)) {
|
|
94
|
+
close();
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
onKeyDown: (event) => {
|
|
98
|
+
if (isOpen && (event.key === "Escape" || event.key === "Tab")) {
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
event.stopPropagation();
|
|
101
|
+
closeToTrigger();
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
triggerProps: {
|
|
106
|
+
ref: triggerRef,
|
|
107
|
+
id: triggerId,
|
|
108
|
+
"aria-haspopup": "menu",
|
|
109
|
+
"aria-expanded": isOpen,
|
|
110
|
+
"aria-controls": isOpen ? menuId : undefined,
|
|
111
|
+
"data-state": isOpen ? "open" : "closed",
|
|
112
|
+
onClick: () => (isOpen ? close() : open()),
|
|
113
|
+
onKeyDown: (event) => {
|
|
114
|
+
if (!isOpen && (event.key === "ArrowDown" || event.key === "ArrowUp")) {
|
|
115
|
+
event.preventDefault();
|
|
116
|
+
open(event.key === "ArrowUp" ? "last" : "checked");
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
menuProps: {
|
|
121
|
+
ref: menuRef,
|
|
122
|
+
id: menuId,
|
|
123
|
+
role: "menu",
|
|
124
|
+
"aria-labelledby": triggerId,
|
|
125
|
+
[COMMENT_MENU_ATTRIBUTE]: "",
|
|
126
|
+
"data-state": "open",
|
|
127
|
+
"data-side": side,
|
|
128
|
+
style,
|
|
129
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
130
|
+
onKeyDown: (event) => {
|
|
131
|
+
const all = itemsIn(menuRef.current);
|
|
132
|
+
const next = menuKeyTarget(event.key, all.indexOf(document.activeElement), all.length);
|
|
133
|
+
if (next !== null) {
|
|
134
|
+
event.preventDefault();
|
|
135
|
+
all[next]?.focus();
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-held.d.ts","sourceRoot":"","sources":["../../src/comments/use-held.ts"],"names":[],"mappings":"AAEA,kGAAkG;AAClG,eAAO,MAAM,OAAO,GAAI,MAAM,OAAO,EAAE,IAAI,MAAM,EAAE,KAAK,OAAO,KAAG,OAajE,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/** True once `isOn` has held for `ms` under the same `key`; false again the moment it doesn't. */
|
|
3
|
+
export const useHeld = (isOn, ms, key) => {
|
|
4
|
+
const [heldKey, setHeldKey] = React.useState(null);
|
|
5
|
+
React.useEffect(() => {
|
|
6
|
+
if (!isOn) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const timer = window.setTimeout(() => setHeldKey(key), ms);
|
|
10
|
+
return () => {
|
|
11
|
+
window.clearTimeout(timer);
|
|
12
|
+
setHeldKey(null);
|
|
13
|
+
};
|
|
14
|
+
}, [isOn, ms, key]);
|
|
15
|
+
return isOn && heldKey === key;
|
|
16
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* The comments' scroll area. It opens at the newest; when comments come in or change (a new
|
|
4
|
+
* reply, an agent's progress line, its answer) it scrolls down to them — smoothly, at once under
|
|
5
|
+
* reduced motion — if the reader was at or near the bottom, and leaves them where they are if they
|
|
6
|
+
* scrolled up to read. Sending a comment of one's own always goes down to it. `overflow` says
|
|
7
|
+
* which edges have more to scroll, for the fades.
|
|
8
|
+
*/
|
|
9
|
+
export declare function useThreadScroll({ contentKey, newestId, isNewestMine, }: {
|
|
10
|
+
/** Changes whenever what the comments show changes: their ids, bodies and states. */
|
|
11
|
+
contentKey: string;
|
|
12
|
+
newestId: number | undefined;
|
|
13
|
+
isNewestMine: boolean;
|
|
14
|
+
}): {
|
|
15
|
+
scrollRef: React.RefObject<HTMLDivElement | null>;
|
|
16
|
+
contentRef: React.RefObject<HTMLDivElement | null>;
|
|
17
|
+
overflow: {
|
|
18
|
+
top: boolean;
|
|
19
|
+
bottom: boolean;
|
|
20
|
+
};
|
|
21
|
+
onScroll: () => void;
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=use-thread-scroll.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-thread-scroll.d.ts","sourceRoot":"","sources":["../../src/comments/use-thread-scroll.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAQ/B;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAC9B,UAAU,EACV,QAAQ,EACR,YAAY,GACb,EAAE;IACD,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,YAAY,EAAE,OAAO,CAAC;CACvB;;;;;;;;EAiFA"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/** How near the bottom (px) counts as reading the newest: the thread follows new comments then. */
|
|
3
|
+
const STICK_TO_BOTTOM_PX = 48;
|
|
4
|
+
const prefersReducedMotion = () => typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
5
|
+
/**
|
|
6
|
+
* The comments' scroll area. It opens at the newest; when comments come in or change (a new
|
|
7
|
+
* reply, an agent's progress line, its answer) it scrolls down to them — smoothly, at once under
|
|
8
|
+
* reduced motion — if the reader was at or near the bottom, and leaves them where they are if they
|
|
9
|
+
* scrolled up to read. Sending a comment of one's own always goes down to it. `overflow` says
|
|
10
|
+
* which edges have more to scroll, for the fades.
|
|
11
|
+
*/
|
|
12
|
+
export function useThreadScroll({ contentKey, newestId, isNewestMine, }) {
|
|
13
|
+
const scrollRef = React.useRef(null);
|
|
14
|
+
const contentRef = React.useRef(null);
|
|
15
|
+
// Whether the reader is at the bottom, kept as they scroll — before the content changes, when
|
|
16
|
+
// the distance would already count the new comment. Only scrolling up lets go of it, so the
|
|
17
|
+
// area's own smooth scroll down never does.
|
|
18
|
+
const isStuckRef = React.useRef(true);
|
|
19
|
+
const lastTopRef = React.useRef(0);
|
|
20
|
+
const lastHeightRef = React.useRef(0);
|
|
21
|
+
const newestIdRef = React.useRef(newestId);
|
|
22
|
+
const hasOpenedRef = React.useRef(false);
|
|
23
|
+
const [overflow, setOverflow] = React.useState({ top: false, bottom: false });
|
|
24
|
+
const measure = React.useCallback(() => {
|
|
25
|
+
const element = scrollRef.current;
|
|
26
|
+
if (!element) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const top = element.scrollTop > 1;
|
|
30
|
+
const bottom = element.scrollHeight - element.scrollTop - element.clientHeight > 1;
|
|
31
|
+
setOverflow((current) => (current.top === top && current.bottom === bottom ? current : { top, bottom }));
|
|
32
|
+
}, []);
|
|
33
|
+
const onScroll = React.useCallback(() => {
|
|
34
|
+
const element = scrollRef.current;
|
|
35
|
+
if (!element) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const distance = element.scrollHeight - element.scrollTop - element.clientHeight;
|
|
39
|
+
if (distance <= STICK_TO_BOTTOM_PX) {
|
|
40
|
+
isStuckRef.current = true;
|
|
41
|
+
}
|
|
42
|
+
else if (element.scrollTop < lastTopRef.current) {
|
|
43
|
+
isStuckRef.current = false;
|
|
44
|
+
}
|
|
45
|
+
lastTopRef.current = element.scrollTop;
|
|
46
|
+
measure();
|
|
47
|
+
}, [measure]);
|
|
48
|
+
React.useLayoutEffect(() => {
|
|
49
|
+
const element = scrollRef.current;
|
|
50
|
+
if (!element || !contentKey) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (newestId !== newestIdRef.current && isNewestMine) {
|
|
54
|
+
isStuckRef.current = true;
|
|
55
|
+
}
|
|
56
|
+
newestIdRef.current = newestId;
|
|
57
|
+
if (isStuckRef.current) {
|
|
58
|
+
const isOpening = !hasOpenedRef.current;
|
|
59
|
+
element.scrollTo({
|
|
60
|
+
top: element.scrollHeight,
|
|
61
|
+
behavior: isOpening || prefersReducedMotion() ? "auto" : "smooth",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
hasOpenedRef.current = true;
|
|
65
|
+
measure();
|
|
66
|
+
}, [contentKey, newestId, isNewestMine, measure]);
|
|
67
|
+
// The area resizing (the panel, the composer growing a line) keeps a reader at the bottom there,
|
|
68
|
+
// and the fades follow the area and its content.
|
|
69
|
+
React.useEffect(() => {
|
|
70
|
+
const element = scrollRef.current;
|
|
71
|
+
if (!element || typeof ResizeObserver === "undefined") {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
lastHeightRef.current = element.clientHeight;
|
|
75
|
+
const observer = new ResizeObserver(() => {
|
|
76
|
+
if (element.clientHeight !== lastHeightRef.current && isStuckRef.current) {
|
|
77
|
+
element.scrollTop = element.scrollHeight;
|
|
78
|
+
}
|
|
79
|
+
lastHeightRef.current = element.clientHeight;
|
|
80
|
+
measure();
|
|
81
|
+
});
|
|
82
|
+
observer.observe(element);
|
|
83
|
+
if (contentRef.current) {
|
|
84
|
+
observer.observe(contentRef.current);
|
|
85
|
+
}
|
|
86
|
+
return () => observer.disconnect();
|
|
87
|
+
}, [measure]);
|
|
88
|
+
return { scrollRef, contentRef, overflow, onScroll };
|
|
89
|
+
}
|
|
@@ -10,9 +10,20 @@ export interface AgentConversationContextValue {
|
|
|
10
10
|
isDraft: boolean;
|
|
11
11
|
/** The index under `filters`; its `renameThread` also keeps the open conversation's remembered title current. */
|
|
12
12
|
threads: UseAgentThreadsResult;
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* The open conversation's row: listed, else the last listed, else a stand-in (`isStandIn`), which
|
|
15
|
+
* shows the conversation's own title and status once they are read (`shownConversationRow`).
|
|
16
|
+
*/
|
|
14
17
|
row: AgentThreadItem;
|
|
15
18
|
isStandIn: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* The open conversation's own read is done and the server has no row for it that this reader
|
|
21
|
+
* may see (a 404): someone else's comment conversation the reader can't see, or — briefly — a
|
|
22
|
+
* conversation just started whose row hasn't landed. A host tells the two apart (react-common's
|
|
23
|
+
* `parseCommentAgentChatThreadId`) before saying the conversation isn't available. False for a
|
|
24
|
+
* draft and while the read is in flight.
|
|
25
|
+
*/
|
|
26
|
+
isConversationMissing: boolean;
|
|
16
27
|
filters: AgentThreadFilters;
|
|
17
28
|
setFilters: (filters: AgentThreadFilters) => void;
|
|
18
29
|
hasActiveFilters: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-conversation-context.d.ts","sourceRoot":"","sources":["../../src/hooks/agent-conversation-context.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAiB,KAAK,SAAS,EAAwB,MAAM,OAAO,CAAC;AAE5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,
|
|
1
|
+
{"version":3,"file":"agent-conversation-context.d.ts","sourceRoot":"","sources":["../../src/hooks/agent-conversation-context.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAiB,KAAK,SAAS,EAAwB,MAAM,OAAO,CAAC;AAE5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAGzB,KAAK,0BAA0B,EAChC,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAmB,KAAK,sBAAsB,EAAE,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE/G,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,sBAAsB,EAAE,QAAQ,GAAG,QAAQ,CAAC,CAAC;AAMnF,MAAM,WAAW,6BAA6B;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,oBAAoB,CAAC;IACnC,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,iHAAiH;IACjH,OAAO,EAAE,qBAAqB,CAAC;IAC/B;;;OAGG;IACH,GAAG,EAAE,eAAe,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,qBAAqB,EAAE,OAAO,CAAC;IAC/B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,UAAU,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAClD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,uFAAuF;IACvF,iBAAiB,EAAE,0BAA0B,CAAC,mBAAmB,CAAC,CAAC;CACpE;AAID,MAAM,WAAW,8BAA+B,SAAQ,IAAI,CAC1D,sBAAsB,EACtB,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CACvD;IACC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,oBAAoB,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,IAAI,EAAE,qBAAqB,CAAC;IAC5B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,OAAO,EACP,YAAY,EACZ,OAAO,EACP,IAAI,EACJ,KAAK,EACL,OAAO,EACP,OAAO,EACP,OAAO,EAAE,WAAW,EACpB,MAAM,EACN,QAAQ,GACT,EAAE,8BAA8B,+BA+BhC;AAED,MAAM,MAAM,oCAAoC,GAAG,8BAA8B,CAAC;AAKlF;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAAC,EAC9C,OAAO,EACP,YAAY,EACZ,OAAO,EACP,IAAI,EACJ,KAAK,EACL,OAAO,EACP,OAAO,EACP,QAAQ,GACT,EAAE,oCAAoC,+BAgBtC;AAiDD,eAAO,MAAM,2BAA2B,QAAO,6BAQ9C,CAAC"}
|
|
@@ -4,9 +4,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
4
4
|
// chat (useAgentConversation), for parts to read through useAgentConversationContext. The
|
|
5
5
|
// agent-layout registry item renders it; a custom UI over the same behaviour can too.
|
|
6
6
|
import { createContext, useContext, useState } from "react";
|
|
7
|
-
import { useAgentConversation, } from "./use-agent-conversation.js";
|
|
7
|
+
import { shownConversationRow, useAgentConversation, } from "./use-agent-conversation.js";
|
|
8
8
|
import { useAgentThread } from "./use-agent-thread.js";
|
|
9
9
|
import { useAgentThreads } from "./use-agent-threads.js";
|
|
10
|
+
/** A single-conversation read that finished with no row (a 404): see `isConversationMissing`. */
|
|
11
|
+
const isReadMissing = (read, isDraft) => !isDraft && !read.isLoading && read.error === null && read.threads.length === 0;
|
|
10
12
|
const AgentConversationContext = createContext(null);
|
|
11
13
|
/**
|
|
12
14
|
* The Root over the agent's whole index: the sidebar's list under its filters, and the open
|
|
@@ -24,7 +26,11 @@ export function AgentConversationProvider({ agentId, conversation, isDraft, chat
|
|
|
24
26
|
headers,
|
|
25
27
|
baseUrl,
|
|
26
28
|
});
|
|
27
|
-
|
|
29
|
+
// The open conversation read on its own, for when the index doesn't list it (someone else's
|
|
30
|
+
// comment conversation, for a person who neither builds nor manages the app): its header shows
|
|
31
|
+
// its title. Free when the index lists it, and a draft reads nothing.
|
|
32
|
+
const read = useAgentThread({ agentId, conversation, isDraft, token, headers, baseUrl });
|
|
33
|
+
return (_jsx(AgentConversationRoot, { agentId: agentId, conversation: conversation, isDraft: isDraft, chat: chat, threads: threads, filters: filters, setFilters: setFilters, read: read.threads[0], isConversationMissing: isReadMissing(read, isDraft), children: children }));
|
|
28
34
|
}
|
|
29
35
|
const NO_FILTERS = {};
|
|
30
36
|
const noFilters = () => undefined;
|
|
@@ -37,9 +43,9 @@ const noFilters = () => undefined;
|
|
|
37
43
|
*/
|
|
38
44
|
export function AgentThreadConversationProvider({ agentId, conversation, isDraft, chat, token, headers, baseUrl, children, }) {
|
|
39
45
|
const threads = useAgentThread({ agentId, conversation, isDraft, token, headers, baseUrl });
|
|
40
|
-
return (_jsx(AgentConversationRoot, { agentId: agentId, conversation: conversation, isDraft: isDraft, chat: chat, threads: threads, filters: NO_FILTERS, setFilters: noFilters, children: children }));
|
|
46
|
+
return (_jsx(AgentConversationRoot, { agentId: agentId, conversation: conversation, isDraft: isDraft, chat: chat, threads: threads, filters: NO_FILTERS, setFilters: noFilters, isConversationMissing: isReadMissing(threads, isDraft), children: children }));
|
|
41
47
|
}
|
|
42
|
-
function AgentConversationRoot({ agentId, conversation, isDraft, chat, threads, filters, setFilters, children, }) {
|
|
48
|
+
function AgentConversationRoot({ agentId, conversation, isDraft, chat, threads, filters, setFilters, read, isConversationMissing, children, }) {
|
|
43
49
|
const { row, isStandIn, renameThread, notifyMessageSent } = useAgentConversation({
|
|
44
50
|
threads,
|
|
45
51
|
agentId,
|
|
@@ -52,8 +58,9 @@ function AgentConversationRoot({ agentId, conversation, isDraft, chat, threads,
|
|
|
52
58
|
conversation,
|
|
53
59
|
isDraft,
|
|
54
60
|
threads: { ...threads, renameThread },
|
|
55
|
-
row,
|
|
61
|
+
row: shownConversationRow(row, isStandIn, read),
|
|
56
62
|
isStandIn,
|
|
63
|
+
isConversationMissing,
|
|
57
64
|
filters,
|
|
58
65
|
setFilters,
|
|
59
66
|
hasActiveFilters: Boolean(filters.status ?? filters.source),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
declare const THREAD_STATUSES: readonly ["idle", "interrupted", "waiting_on_agent", "busy", "error"];
|
|
2
2
|
export type AgentThreadStatus = (typeof THREAD_STATUSES)[number];
|
|
3
|
-
declare const THREAD_SOURCES: readonly ["direct_chat", "send_message", "feedback", "self_config_resume", "scheduled_reminder", "unknown"];
|
|
3
|
+
declare const THREAD_SOURCES: readonly ["direct_chat", "send_message", "feedback", "self_config_resume", "scheduled_reminder", "comment", "unknown"];
|
|
4
4
|
export type AgentThreadSource = (typeof THREAD_SOURCES)[number] | "agent_call";
|
|
5
5
|
export interface AgentThreadItem {
|
|
6
6
|
agentId: string | null;
|
|
@@ -42,7 +42,10 @@ export interface AgentThreadLiveUpdateOptions {
|
|
|
42
42
|
* Without `userId` a live update only refreshes rows already listed; it never adds one.
|
|
43
43
|
*/
|
|
44
44
|
private?: boolean;
|
|
45
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* The signed-in user's id: for `private`, and to tell whose a comment conversation is — a live
|
|
47
|
+
* update adds one only when this user asked (see `applyAgentThreadUpdate`).
|
|
48
|
+
*/
|
|
46
49
|
userId?: string;
|
|
47
50
|
sort?: "lastActivity" | "firstSeen";
|
|
48
51
|
order?: "asc" | "desc";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-thread-live-update.d.ts","sourceRoot":"","sources":["../../src/hooks/agent-thread-live-update.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agent-thread-live-update.d.ts","sourceRoot":"","sources":["../../src/hooks/agent-thread-live-update.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Wire types and pure merge logic for the agent-threads index; dependency-free for tests.
|
|
2
|
+
import { parseCommentAgentChatThreadId } from "../comments/types.js";
|
|
2
3
|
const THREAD_STATUSES = ["idle", "interrupted", "waiting_on_agent", "busy", "error"];
|
|
3
4
|
const THREAD_SOURCES = [
|
|
4
5
|
"direct_chat",
|
|
@@ -7,6 +8,8 @@ const THREAD_SOURCES = [
|
|
|
7
8
|
"self_config_resume",
|
|
8
9
|
// A thread a fired reminder or schedule started (wf-actions-py scheduled_reminder_runner.py).
|
|
9
10
|
"scheduled_reminder",
|
|
11
|
+
// A conversation a comment's @mention started (wf-actions-py agent_comment_mention_runner.py).
|
|
12
|
+
"comment",
|
|
10
13
|
"unknown",
|
|
11
14
|
];
|
|
12
15
|
/** A thread's identity within its agent: the thread id alone. */
|
|
@@ -75,6 +78,10 @@ const sortTime = (item, sort) => {
|
|
|
75
78
|
export const matchesAgentThreadFilters = (item, options = {}) => (!options.status || item.status === options.status) &&
|
|
76
79
|
(!options.source || item.source === options.source) &&
|
|
77
80
|
(!options.private || options.userId === undefined || item.userId == null || item.userId === options.userId);
|
|
81
|
+
const isOwnOrNotCommentConversation = (item, userId) => {
|
|
82
|
+
const comment = parseCommentAgentChatThreadId(item.chatThreadId);
|
|
83
|
+
return comment === null || (userId !== undefined && String(comment.askerUserId) === userId);
|
|
84
|
+
};
|
|
78
85
|
export const applyAgentThreadUpdate = (threads, incoming, options = {}) => {
|
|
79
86
|
const key = agentThreadKey(incoming);
|
|
80
87
|
const existing = threads.find((thread) => agentThreadKey(thread) === key);
|
|
@@ -89,6 +96,12 @@ export const applyAgentThreadUpdate = (threads, incoming, options = {}) => {
|
|
|
89
96
|
if (!existing && options.private && (options.userId === undefined || incoming.userId == null)) {
|
|
90
97
|
return threads;
|
|
91
98
|
}
|
|
99
|
+
// Every member who may read a comment conversation hears its events, but the server lists
|
|
100
|
+
// someone else's only for the app's builders and managers — and their list already holds it.
|
|
101
|
+
// So a live update adds a comment conversation only when the signed-in user asked.
|
|
102
|
+
if (!existing && !isOwnOrNotCommentConversation(incoming, options.userId)) {
|
|
103
|
+
return threads;
|
|
104
|
+
}
|
|
92
105
|
const sort = options.sort ?? "lastActivity";
|
|
93
106
|
const direction = options.order === "asc" ? 1 : -1;
|
|
94
107
|
const next = existing ? threads.map((thread) => (thread === existing ? incoming : thread)) : [...threads, incoming];
|
|
@@ -143,6 +156,7 @@ const GROUP_OF_SOURCE = {
|
|
|
143
156
|
direct_chat: "chats",
|
|
144
157
|
self_config_resume: "chats",
|
|
145
158
|
scheduled_reminder: "chats",
|
|
159
|
+
comment: "chats",
|
|
146
160
|
unknown: "chats",
|
|
147
161
|
send_message: "workflows",
|
|
148
162
|
feedback: "feedback",
|
|
@@ -45,6 +45,14 @@ export declare const useAgentConversationSelection: ({ urlTarget, }: UseAgentCon
|
|
|
45
45
|
export declare const RUN_STALE_MS: number;
|
|
46
46
|
/** A row for a conversation the index lists nothing for (yet): the draft, or a thread whose row has not landed. */
|
|
47
47
|
export declare const agentThreadStandIn: (agentId: string, conversation: AgentConversationRef, isDraft: boolean) => AgentThreadItem;
|
|
48
|
+
/**
|
|
49
|
+
* The open conversation's row as a surface shows it: `row` as it is, unless it is a stand-in and
|
|
50
|
+
* the conversation's own read (`read`, the server's row for it) is in, whose title and status it
|
|
51
|
+
* then shows. For a conversation the index doesn't list: someone else's comment conversation, which
|
|
52
|
+
* the index leaves out for a person who neither builds nor manages the app. Only what it shows: the
|
|
53
|
+
* row stays a stand-in for the rest (no rename, no turn accounting).
|
|
54
|
+
*/
|
|
55
|
+
export declare const shownConversationRow: (row: AgentThreadItem, isStandIn: boolean, read: AgentThreadItem | undefined) => AgentThreadItem;
|
|
48
56
|
/** When the oldest run mark among `rows` reaches the stale ceiling (epoch ms); null with no mark. */
|
|
49
57
|
export declare const nextRunStaleAt: (rows: readonly AgentThreadItem[]) => number | null;
|
|
50
58
|
/** The turns this client has accounted for on one conversation (`key`: agentThreadKey). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-agent-conversation.d.ts","sourceRoot":"","sources":["../../src/hooks/use-agent-conversation.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-agent-conversation.d.ts","sourceRoot":"","sources":["../../src/hooks/use-agent-conversation.ts"],"names":[],"mappings":"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"}
|
|
@@ -71,6 +71,16 @@ export const agentThreadStandIn = (agentId, conversation, isDraft) => {
|
|
|
71
71
|
lastActivityAt: now,
|
|
72
72
|
};
|
|
73
73
|
};
|
|
74
|
+
/**
|
|
75
|
+
* The open conversation's row as a surface shows it: `row` as it is, unless it is a stand-in and
|
|
76
|
+
* the conversation's own read (`read`, the server's row for it) is in, whose title and status it
|
|
77
|
+
* then shows. For a conversation the index doesn't list: someone else's comment conversation, which
|
|
78
|
+
* the index leaves out for a person who neither builds nor manages the app. Only what it shows: the
|
|
79
|
+
* row stays a stand-in for the rest (no rename, no turn accounting).
|
|
80
|
+
*/
|
|
81
|
+
export const shownConversationRow = (row, isStandIn, read) => isStandIn && read && agentThreadKey(read) === agentThreadKey(row)
|
|
82
|
+
? { ...row, title: read.title, status: read.status }
|
|
83
|
+
: row;
|
|
74
84
|
/** When the oldest run mark among `rows` reaches the stale ceiling (epoch ms); null with no mark. */
|
|
75
85
|
export const nextRunStaleAt = (rows) => {
|
|
76
86
|
let next = null;
|
|
@@ -79,6 +79,7 @@ export interface UseAgentThreadsResult {
|
|
|
79
79
|
*/
|
|
80
80
|
export declare const agentInboxChatId: (agentId: string) => string;
|
|
81
81
|
export declare const agentThreadShortLabel: (thread: Pick<AgentThreadItem, "chatThreadId">) => string;
|
|
82
|
+
/** An untitled comment conversation reads "Comment": its id starts with a word, not a short code. */
|
|
82
83
|
export declare const agentThreadDisplayTitle: (thread: Pick<AgentThreadItem, "chatThreadId" | "title">) => string;
|
|
83
84
|
/** Agent-threads index with pagination, realtime upserts, and optimistic send listing. */
|
|
84
85
|
export declare const useAgentThreads: (options: UseAgentThreadsOptions) => UseAgentThreadsResult;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-agent-threads.d.ts","sourceRoot":"","sources":["../../src/hooks/use-agent-threads.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-agent-threads.d.ts","sourceRoot":"","sources":["../../src/hooks/use-agent-threads.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getApiHost } from "@blocksdiy/blocks-client-api/envService";
|
|
2
2
|
import { websocketsService } from "@blocksdiy/blocks-client-api/websocketService";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { parseCommentAgentChatThreadId } from "../comments/types.js";
|
|
4
5
|
import { agentThreadKey, applyAgentThreadUpdate, matchesAgentThreadFilters, optimisticThreadAfterSend, overlayOptimisticThreads, reconcileWalkWithLive, removeAgentThread, retireOptimisticThreadsTakenOver, } from "./agent-thread-live-update.js";
|
|
5
6
|
import { acquireAgentThreadStore } from "./agent-thread-store.js";
|
|
6
7
|
export * from "./agent-thread-live-update.js";
|
|
@@ -84,7 +85,9 @@ export const postAgentThreadRename = async (fetchJson, agentId, thread, title, s
|
|
|
84
85
|
*/
|
|
85
86
|
export const agentInboxChatId = (agentId) => `agent-inbox-${agentId}`;
|
|
86
87
|
export const agentThreadShortLabel = (thread) => thread.chatThreadId.slice(0, 8);
|
|
87
|
-
|
|
88
|
+
/** An untitled comment conversation reads "Comment": its id starts with a word, not a short code. */
|
|
89
|
+
export const agentThreadDisplayTitle = (thread) => thread.title ??
|
|
90
|
+
(parseCommentAgentChatThreadId(thread.chatThreadId) ? "Comment" : `Chat ${agentThreadShortLabel(thread)}`);
|
|
88
91
|
const buildQuery = (filters, cursor) => {
|
|
89
92
|
const params = new URLSearchParams();
|
|
90
93
|
if (filters.status) {
|