Skip to content

Commit de65a0c

Browse files
committed
frontend/llm history: merge upstream and use dstream and searchable selector
2 parents 32f002a + 3e752b9 commit de65a0c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+701
-614
lines changed

src/.claude/settings.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@
3737
"WebSearch",
3838
"mcp__cclsp__find_definition",
3939
"mcp__github__get_issue",
40+
"mcp__github__get_pull_request_comments",
41+
"mcp__github__get_pull_request",
42+
"mcp__github__get_issue_comments",
43+
"mcp__github__get_pull_request_comments",
4044
"mcp__cclsp__find_definition"
4145
],
4246
"deny": []

src/packages/frontend/chat/message.tsx

Lines changed: 115 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Badge, Button, Col, Popconfirm, Row, Space, Tooltip } from "antd";
99
import { List, Map } from "immutable";
1010
import { CSSProperties, useEffect, useLayoutEffect } from "react";
1111
import { useIntl } from "react-intl";
12+
1213
import { Avatar } from "@cocalc/frontend/account/avatar/avatar";
1314
import {
1415
CSS,
@@ -19,6 +20,7 @@ import {
1920
useTypedRedux,
2021
} from "@cocalc/frontend/app-framework";
2122
import { Gap, Icon, TimeAgo, Tip } from "@cocalc/frontend/components";
23+
import CopyButton from "@cocalc/frontend/components/copy-button";
2224
import MostlyStaticMarkdown from "@cocalc/frontend/editors/slate/mostly-static-markdown";
2325
import { IS_TOUCH } from "@cocalc/frontend/feature";
2426
import { modelToName } from "@cocalc/frontend/frame-editors/llm/llm-selector";
@@ -501,14 +503,122 @@ export default function Message({
501503
);
502504
}
503505

504-
function renderMessageBody({ lighten, message_class }) {
505-
const value = newest_content(message);
506+
function renderCopyMessageButton() {
507+
return (
508+
<Tip
509+
placement={"top"}
510+
title={intl.formatMessage({
511+
id: "chat.message.copy_markdown.tooltip",
512+
defaultMessage: "Copy message as markdown",
513+
description:
514+
"Tooltip for button to copy chat message as markdown text",
515+
})}
516+
>
517+
<CopyButton
518+
value={message_to_markdown(message)}
519+
size="small"
520+
noText={true}
521+
style={{
522+
color: is_viewers_message ? "white" : "#888",
523+
fontSize: "12px",
524+
marginTop: "-4px",
525+
}}
526+
/>
527+
</Tip>
528+
);
529+
}
530+
531+
function renderLinkMessageButton() {
532+
return (
533+
<Tip
534+
placement={"top"}
535+
title={intl.formatMessage({
536+
id: "chat.message.copy_link.tooltip",
537+
defaultMessage: "Select message. Copy URL to link to this message.",
538+
description:
539+
"Tooltip for button to copy URL link to specific chat message",
540+
})}
541+
>
542+
<Button
543+
onClick={() => {
544+
actions?.setFragment(message.get("date"));
545+
}}
546+
size="small"
547+
type={"text"}
548+
style={{
549+
color: is_viewers_message ? "white" : "#888",
550+
fontSize: "12px",
551+
marginTop: "-4px",
552+
}}
553+
>
554+
<Icon name="link" />
555+
</Button>
556+
</Tip>
557+
);
558+
}
559+
560+
function renderLLMFeedbackButtons() {
561+
if (isLLMThread) return;
506562

507563
const feedback = message.getIn(["feedback", account_id]);
508564
const otherFeedback =
509565
isLLMThread && msgWrittenByLLM ? 0 : (message.get("feedback")?.size ?? 0);
510566
const showOtherFeedback = otherFeedback > 0;
511567

568+
return (
569+
<Tip
570+
placement={"top"}
571+
title={
572+
!showOtherFeedback
573+
? "Like this"
574+
: () => {
575+
return (
576+
<div>
577+
{Object.keys(message.get("feedback")?.toJS() ?? {}).map(
578+
(account_id) => (
579+
<div key={account_id} style={{ marginBottom: "2px" }}>
580+
<Avatar size={24} account_id={account_id} />{" "}
581+
<User account_id={account_id} />
582+
</div>
583+
),
584+
)}
585+
</div>
586+
);
587+
}
588+
}
589+
>
590+
<Button
591+
style={{
592+
color: !feedback && is_viewers_message ? "white" : "#888",
593+
fontSize: "12px",
594+
marginTop: "-4px",
595+
...(feedback ? {} : { position: "relative", top: "-5px" }),
596+
}}
597+
size="small"
598+
type={feedback ? "dashed" : "text"}
599+
onClick={() => {
600+
actions?.feedback(message, feedback ? null : "positive");
601+
}}
602+
>
603+
{showOtherFeedback ? (
604+
<Badge count={otherFeedback} color="darkblue" size="small" />
605+
) : (
606+
""
607+
)}
608+
<Icon
609+
name="thumbs-up"
610+
style={{
611+
color: showOtherFeedback ? "darkblue" : undefined,
612+
}}
613+
/>
614+
</Button>
615+
</Tip>
616+
);
617+
}
618+
619+
function renderMessageBody({ lighten, message_class }) {
620+
const value = newest_content(message);
621+
512622
return (
513623
<>
514624
<span style={lighten}>
@@ -518,81 +628,9 @@ export default function Message({
518628
align="baseline"
519629
style={{ float: "right", marginRight: "10px" }}
520630
>
521-
{!isLLMThread && (
522-
<Tip
523-
placement={"top"}
524-
title={
525-
!showOtherFeedback
526-
? "Like this"
527-
: () => {
528-
return (
529-
<div>
530-
{Object.keys(
531-
message.get("feedback")?.toJS() ?? {},
532-
).map((account_id) => (
533-
<div
534-
key={account_id}
535-
style={{ marginBottom: "2px" }}
536-
>
537-
<Avatar size={24} account_id={account_id} />{" "}
538-
<User account_id={account_id} />
539-
</div>
540-
))}
541-
</div>
542-
);
543-
}
544-
}
545-
>
546-
<Button
547-
style={{
548-
color: !feedback && is_viewers_message ? "white" : "#888",
549-
fontSize: "12px",
550-
marginTop: "-4px",
551-
...(feedback ? {} : { position: "relative", top: "-5px" }),
552-
}}
553-
size="small"
554-
type={feedback ? "dashed" : "text"}
555-
onClick={() => {
556-
actions?.feedback(message, feedback ? null : "positive");
557-
}}
558-
>
559-
{showOtherFeedback ? (
560-
<Badge
561-
count={otherFeedback}
562-
color="darkblue"
563-
size="small"
564-
/>
565-
) : (
566-
""
567-
)}
568-
<Icon
569-
name="thumbs-up"
570-
style={{
571-
color: showOtherFeedback ? "darkblue" : undefined,
572-
}}
573-
/>
574-
</Button>
575-
</Tip>
576-
)}
577-
<Tip
578-
placement={"top"}
579-
title="Select message. Copy URL to link to this message."
580-
>
581-
<Button
582-
onClick={() => {
583-
actions?.setFragment(message.get("date"));
584-
}}
585-
size="small"
586-
type={"text"}
587-
style={{
588-
color: is_viewers_message ? "white" : "#888",
589-
fontSize: "12px",
590-
marginTop: "-4px",
591-
}}
592-
>
593-
<Icon name="link" />
594-
</Button>
595-
</Tip>
631+
{renderLLMFeedbackButtons()}
632+
{renderCopyMessageButton()}
633+
{renderLinkMessageButton()}
596634
</Space>
597635
</span>
598636
<MostlyStaticMarkdown

src/packages/frontend/codemirror/extensions/ai-formula.tsx

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
Dropdown,
1212
Input,
1313
Modal,
14+
Select,
1415
Space,
1516
Tooltip,
1617
} from "antd";
@@ -367,34 +368,41 @@ function AiGenFormula({ mode, text = "", project_id, locale, cb }: Props) {
367368
addonBefore={<Icon name="fx" />}
368369
/>
369370
{historyPrompts.length > 0 && (
370-
<Dropdown
371-
menu={{
372-
items: historyPrompts
373-
.slice(0, MAX_PROMPTS)
374-
.map((prompt, idx) => ({
375-
key: idx.toString(),
376-
label: (
377-
<Tooltip title={prompt} placement="left">
378-
<div
379-
style={{
380-
maxWidth: "300px",
381-
whiteSpace: "nowrap",
382-
overflow: "hidden",
383-
textOverflow: "ellipsis",
384-
}}
385-
>
386-
{prompt}
387-
</div>
388-
</Tooltip>
389-
),
390-
onClick: () => setInput(prompt),
391-
})) as MenuProps["items"],
392-
style: { maxHeight: "50vh", overflow: "auto" },
393-
}}
394-
trigger={["click"]}
395-
>
396-
<Button disabled={generating} icon={<Icon name="history" />} />
397-
</Dropdown>
371+
<Select
372+
style={{ width: 200 }}
373+
placeholder="Search history..."
374+
showSearch
375+
allowClear
376+
optionFilterProp="children"
377+
filterOption={(input, option) =>
378+
(option?.label ?? "")
379+
.toString()
380+
.toLowerCase()
381+
.includes(input.toLowerCase())
382+
}
383+
onSelect={(value) => setInput(value)}
384+
disabled={generating}
385+
dropdownStyle={{ maxHeight: 400, overflow: "auto" }}
386+
suffixIcon={<Icon name="history" />}
387+
options={historyPrompts.slice(0, MAX_PROMPTS).map((prompt, idx) => ({
388+
key: idx.toString(),
389+
value: prompt,
390+
label: (
391+
<Tooltip title={prompt} placement="left">
392+
<div
393+
style={{
394+
maxWidth: "300px",
395+
whiteSpace: "nowrap",
396+
overflow: "hidden",
397+
textOverflow: "ellipsis",
398+
}}
399+
>
400+
{prompt}
401+
</div>
402+
</Tooltip>
403+
),
404+
}))}
405+
/>
398406
)}
399407
<Button
400408
disabled={!input.trim() || generating}

0 commit comments

Comments
 (0)