Skip to content

Commit 16d6f06

Browse files
committed
frontend/chat: if "search" is set, temporarily unfold all threads to search through all messages
1 parent ecddb43 commit 16d6f06

File tree

4 files changed

+49
-29
lines changed

4 files changed

+49
-29
lines changed

src/packages/frontend/chat/chat-log.tsx

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,10 @@ export function ChatLog(props: Readonly<Props>) {
170170
}
171171

172172
const is_thread = isThread(messages, message);
173-
const is_folded = isFolded(messages, message, account_id);
173+
// if we search for a message, we treat all threads as unfolded
174+
const force_unfold = !!search;
175+
const is_folded =
176+
!force_unfold && isFolded(messages, message, account_id);
174177
const is_thread_body = message.get("reply_to") != null;
175178

176179
return (
@@ -188,6 +191,7 @@ export function ChatLog(props: Readonly<Props>) {
188191
actions={actions}
189192
is_thread={is_thread}
190193
is_folded={is_folded}
194+
force_unfold={force_unfold}
191195
is_thread_body={is_thread_body}
192196
is_prev_sender={isPrevMessageSender(
193197
index,
@@ -331,13 +335,16 @@ export function getSortedDates(
331335
for (const [date, message] of m) {
332336
if (message == null) continue;
333337

334-
const is_thread = isThread(messages, message);
335-
const is_folded = isFolded(messages, message, account_id);
336-
const is_thread_body = message.get("reply_to") != null;
337-
const folded = is_thread && is_folded && is_thread_body;
338-
if (folded) {
339-
numFolded++;
340-
continue;
338+
// If we search for a message, we treat all threads as unfolded
339+
if (!search) {
340+
const is_thread = isThread(messages, message);
341+
const is_folded = isFolded(messages, message, account_id);
342+
const is_thread_body = message.get("reply_to") != null;
343+
const folded = is_thread && is_folded && is_thread_body;
344+
if (folded) {
345+
numFolded++;
346+
continue;
347+
}
341348
}
342349

343350
const reply_to = message.get("reply_to");

src/packages/frontend/chat/chatroom.tsx

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,10 @@ export const ChatRoom: React.FC<Props> = ({ project_id, path }) => {
299299
default_value={search}
300300
on_change={debounce(
301301
(value) => actions.setState({ search: value }),
302-
250,
302+
150,
303+
{ leading: false, trailing: true },
303304
)}
305+
status={!search ? undefined : "warning"}
304306
style={{
305307
margin: 0,
306308
width: "100%",
@@ -374,16 +376,22 @@ export const ChatRoom: React.FC<Props> = ({ project_id, path }) => {
374376
? undefined
375377
: "error"
376378
}
377-
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
378-
const v = e.target.value;
379-
setFilterRecentHCustom(v);
380-
const val = parseFloat(v);
381-
if (isFinite(val) && val >= 0) {
382-
actions.setState({ filterRecentH: val });
383-
} else if (v == "") {
384-
actions.setState({ filterRecentH: FILTER_RECENT_NONE.value });
385-
}
386-
}}
379+
onChange={debounce(
380+
(e: React.ChangeEvent<HTMLInputElement>) => {
381+
const v = e.target.value;
382+
setFilterRecentHCustom(v);
383+
const val = parseFloat(v);
384+
if (isFinite(val) && val >= 0) {
385+
actions.setState({ filterRecentH: val });
386+
} else if (v == "") {
387+
actions.setState({
388+
filterRecentH: FILTER_RECENT_NONE.value,
389+
});
390+
}
391+
},
392+
150,
393+
{ leading: true, trailing: true },
394+
)}
387395
onKeyDown={(e) => e.stopPropagation()}
388396
onPressEnter={() => setFilterRecentOpen(false)}
389397
addonAfter={<span style={{ paddingLeft: "5px" }}>hours</span>}

src/packages/frontend/chat/message.tsx

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,15 @@ interface Props {
122122
is_thread?: boolean; // if true, there is a thread starting in a reply_to message
123123
is_folded?: boolean; // if true, only show the reply_to root message
124124
is_thread_body: boolean;
125+
force_unfold?: boolean; // if true, all threads are temporarily forced to be unfolded
125126

126127
llm_cost_reply?: [number, number] | null;
127128
}
128129

129130
export default function Message(props: Readonly<Props>) {
130131
const {
131132
is_folded,
133+
force_unfold,
132134
is_thread_body,
133135
is_thread,
134136
llm_cost_reply,
@@ -179,10 +181,6 @@ export default function Message(props: Readonly<Props>) {
179181
);
180182
}, [message]);
181183

182-
const isFolded: boolean = useMemo(() => {
183-
return message.get("folding")?.includes(props.account_id) ?? false;
184-
}, [message]);
185-
186184
const reverseRowOrdering =
187185
!is_thread_body && sender_is_viewer(props.account_id, message);
188186

@@ -527,7 +525,11 @@ export default function Message(props: Readonly<Props>) {
527525
) : undefined}
528526
{isLLMThread && msgWrittenByLLM ? (
529527
<>
530-
<RegenerateLLM actions={props.actions} date={date} model={isLLMThread} />
528+
<RegenerateLLM
529+
actions={props.actions}
530+
date={date}
531+
model={isLLMThread}
532+
/>
531533
<FeedbackLLM actions={props.actions} message={message} />
532534
</>
533535
) : undefined}
@@ -651,8 +653,8 @@ export default function Message(props: Readonly<Props>) {
651653
onChange={(value) => {
652654
replyMessageRef.current = value;
653655
// replyMentionsRef does not submit mentions, only gives us the value
654-
const reply = replyMentionsRef.current?.(undefined, true) ?? value;
655-
props.actions?.llm_estimate_cost(reply, "reply", message.toJS());
656+
const reply = replyMentionsRef.current?.(undefined, true) ?? value;
657+
props.actions?.llm_estimate_cost(reply, "reply", message.toJS());
656658
}}
657659
placeholder={"Reply to the above message..."}
658660
/>
@@ -684,7 +686,7 @@ export default function Message(props: Readonly<Props>) {
684686
function getStyleBase(): CSS {
685687
if (!is_thread_body) {
686688
if (is_thread) {
687-
if (isFolded) {
689+
if (is_folded) {
688690
return THREAD_STYLE_FOLDED;
689691
} else {
690692
return THREAD_STYLE_TOP;
@@ -791,7 +793,7 @@ export default function Message(props: Readonly<Props>) {
791793
marginRight: "5px",
792794
}
793795
: { marginTop: "5px", width: "100%", textAlign: "center" };
794-
const iconname = isFolded
796+
const iconname = is_folded
795797
? mode === "standalone"
796798
? reverseRowOrdering
797799
? "right-circle-o"
@@ -802,6 +804,7 @@ export default function Message(props: Readonly<Props>) {
802804
<Button
803805
type="text"
804806
style={style}
807+
disabled={force_unfold}
805808
onClick={() =>
806809
props.actions?.foldThread(message.get("date"), props.index)
807810
}
@@ -824,7 +827,7 @@ export default function Message(props: Readonly<Props>) {
824827
) : (
825828
<Tooltip
826829
title={
827-
isFolded
830+
is_folded
828831
? "Unfold this thread"
829832
: "Fold this thread to hide replies"
830833
}

src/packages/frontend/components/search-input.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ interface Props {
3737
autoSelect?: boolean;
3838
placeholder?: string;
3939
focus?: number; // if this changes, focus the search box.
40+
status?: "warning" | "error";
4041
}
4142

4243
export const SearchInput: React.FC<Props> = React.memo((props) => {
@@ -147,6 +148,7 @@ export const SearchInput: React.FC<Props> = React.memo((props) => {
147148
onKeyUp={key_up}
148149
disabled={props.disabled}
149150
enterButton={props.buttonAfter}
151+
status={props.status}
150152
/>
151153
);
152154
});

0 commit comments

Comments
 (0)