From 293da1d4efa6fc8bbcdd3618c7a13bd4d6d3e3f1 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 29 Mar 2026 21:29:27 +0300 Subject: [PATCH] feat(llm): display cost next to the title --- .../src/widgets/react/FormDropdownList.tsx | 19 ++++++++++--- .../type_widgets/llm_chat/ChatInputBar.tsx | 27 ++++++++++++------- .../widgets/type_widgets/llm_chat/LlmChat.css | 5 ++++ .../type_widgets/llm_chat/useLlmChat.ts | 2 +- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/apps/client/src/widgets/react/FormDropdownList.tsx b/apps/client/src/widgets/react/FormDropdownList.tsx index 08d607a8c2..150fc4a724 100644 --- a/apps/client/src/widgets/react/FormDropdownList.tsx +++ b/apps/client/src/widgets/react/FormDropdownList.tsx @@ -5,16 +5,27 @@ interface FormDropdownList extends Omit { values: T[]; keyProperty: keyof T; titleProperty: keyof T; + /** Property to show as a small suffix next to the title */ + titleSuffixProperty?: keyof T; descriptionProperty?: keyof T; currentValue: string; onChange(newValue: string): void; } -export default function FormDropdownList({ values, keyProperty, titleProperty, descriptionProperty, currentValue, onChange, ...restProps }: FormDropdownList) { +export default function FormDropdownList({ values, keyProperty, titleProperty, titleSuffixProperty, descriptionProperty, currentValue, onChange, ...restProps }: FormDropdownList) { const currentValueData = values.find(value => value[keyProperty] === currentValue); + const renderTitle = (item: T) => { + const title = item[titleProperty] as string; + const suffix = titleSuffixProperty ? item[titleSuffixProperty] as string : null; + if (suffix) { + return <>{title} {suffix}; + } + return title; + }; + return ( - + {values.map(item => ( onChange(item[keyProperty] as string)} @@ -22,9 +33,9 @@ export default function FormDropdownList({ values, keyProperty, titleProperty description={descriptionProperty && item[descriptionProperty] as string} selected={currentValue === item[keyProperty]} > - {item[titleProperty] as string} + {renderTitle(item)} ))} ) -} \ No newline at end of file +} diff --git a/apps/client/src/widgets/type_widgets/llm_chat/ChatInputBar.tsx b/apps/client/src/widgets/type_widgets/llm_chat/ChatInputBar.tsx index 3bb9a6d405..54a15105f1 100644 --- a/apps/client/src/widgets/type_widgets/llm_chat/ChatInputBar.tsx +++ b/apps/client/src/widgets/type_widgets/llm_chat/ChatInputBar.tsx @@ -1,7 +1,9 @@ import type { RefObject } from "preact"; -import type { UseLlmChatReturn } from "./useLlmChat.js"; + import { t } from "../../../services/i18n.js"; -import FormDropdownList from "../../react/FormDropdownList.js"; +import Dropdown from "../../react/Dropdown.js"; +import { FormListItem } from "../../react/FormList.js"; +import type { UseLlmChatReturn } from "./useLlmChat.js"; /** Format token count with thousands separators */ function formatTokenCount(tokens: number): string { @@ -88,16 +90,21 @@ export default function ChatInputBar({
- {currentModel?.name}} disabled={chat.isStreaming} buttonClassName="llm-chat-model-select" - /> + > + {chat.availableModels.map(model => ( + handleModelSelect(model.id)} + checked={chat.selectedModel === model.id} + > + {model.name}({model.costDescription}) + + ))} +