mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
ui fix
This commit is contained in:
parent
5edab52884
commit
4604211ffa
19 changed files with 385 additions and 325 deletions
|
|
@ -785,6 +785,369 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
|
||||
const placeholderBottomText = `\n(${t("chat:addContext")}${shouldDisableImages ? `, ${t("chat:dragFiles")}` : `, ${t("chat:dragFilesImages")}`})`
|
||||
|
||||
// Common mode selector handler
|
||||
const handleModeChange = useCallback(
|
||||
(value: Mode) => {
|
||||
setMode(value)
|
||||
vscode.postMessage({ type: "mode", text: value })
|
||||
},
|
||||
[setMode],
|
||||
)
|
||||
|
||||
// Helper function to render mode selector
|
||||
const renderModeSelector = () => (
|
||||
<ModeSelector
|
||||
value={mode}
|
||||
title={t("chat:selectMode")}
|
||||
onChange={handleModeChange}
|
||||
triggerClassName="w-full"
|
||||
modeShortcutText={modeShortcutText}
|
||||
customModes={customModes}
|
||||
customModePrompts={customModePrompts}
|
||||
/>
|
||||
)
|
||||
|
||||
// Helper function to render edit mode controls
|
||||
const renderEditModeControls = () => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex",
|
||||
"items-center",
|
||||
"justify-between",
|
||||
"absolute",
|
||||
"bottom-2",
|
||||
"left-2",
|
||||
"right-2",
|
||||
"z-30",
|
||||
)}>
|
||||
<div className={cn("flex", "items-center", "gap-1", "flex-1", "min-w-0")}>
|
||||
<div className="shrink-0">{renderModeSelector()}</div>
|
||||
</div>
|
||||
<div className={cn("flex", "items-center", "gap-0.5", "shrink-0", "ml-2")}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={onCancel}
|
||||
disabled={sendingDisabled}
|
||||
className="text-xs bg-vscode-toolbar-hoverBackground hover:bg-vscode-button-secondaryBackground text-vscode-button-secondaryForeground">
|
||||
Cancel
|
||||
</Button>
|
||||
<IconButton
|
||||
iconClass="codicon-device-camera"
|
||||
title={t("chat:addImages")}
|
||||
disabled={shouldDisableImages}
|
||||
onClick={onSelectImages}
|
||||
className="opacity-60 hover:opacity-100 text-vscode-descriptionForeground hover:text-vscode-foreground"
|
||||
/>
|
||||
<IconButton
|
||||
iconClass="codicon-edit"
|
||||
title={t("chat:save.tooltip")}
|
||||
disabled={sendingDisabled}
|
||||
onClick={onSend}
|
||||
className="opacity-60 hover:opacity-100 text-vscode-descriptionForeground hover:text-vscode-foreground"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
// Helper function to get API config dropdown options
|
||||
const getApiConfigOptions = useMemo(() => {
|
||||
const pinnedConfigs = (listApiConfigMeta || [])
|
||||
.filter((config) => pinnedApiConfigs && pinnedApiConfigs[config.id])
|
||||
.map((config) => ({
|
||||
value: config.id,
|
||||
label: config.name,
|
||||
name: config.name,
|
||||
type: DropdownOptionType.ITEM,
|
||||
pinned: true,
|
||||
}))
|
||||
.sort((a, b) => a.label.localeCompare(b.label))
|
||||
|
||||
const unpinnedConfigs = (listApiConfigMeta || [])
|
||||
.filter((config) => !pinnedApiConfigs || !pinnedApiConfigs[config.id])
|
||||
.map((config) => ({
|
||||
value: config.id,
|
||||
label: config.name,
|
||||
name: config.name,
|
||||
type: DropdownOptionType.ITEM,
|
||||
pinned: false,
|
||||
}))
|
||||
.sort((a, b) => a.label.localeCompare(b.label))
|
||||
|
||||
const hasPinnedAndUnpinned = pinnedConfigs.length > 0 && unpinnedConfigs.length > 0
|
||||
|
||||
return [
|
||||
...pinnedConfigs,
|
||||
...(hasPinnedAndUnpinned
|
||||
? [
|
||||
{
|
||||
value: "sep-pinned",
|
||||
label: t("chat:separator"),
|
||||
type: DropdownOptionType.SEPARATOR,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...unpinnedConfigs,
|
||||
{
|
||||
value: "sep-2",
|
||||
label: t("chat:separator"),
|
||||
type: DropdownOptionType.SEPARATOR,
|
||||
},
|
||||
{
|
||||
value: "settingsButtonClicked",
|
||||
label: t("chat:edit"),
|
||||
type: DropdownOptionType.ACTION,
|
||||
},
|
||||
]
|
||||
}, [listApiConfigMeta, pinnedApiConfigs, t])
|
||||
|
||||
// Helper function to handle API config change
|
||||
const handleApiConfigChange = useCallback((value: string) => {
|
||||
if (value === "settingsButtonClicked") {
|
||||
vscode.postMessage({
|
||||
type: "loadApiConfiguration",
|
||||
text: value,
|
||||
values: { section: "providers" },
|
||||
})
|
||||
} else {
|
||||
vscode.postMessage({ type: "loadApiConfigurationById", text: value })
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Helper function to render API config item
|
||||
const renderApiConfigItem = useCallback(
|
||||
({ type, value, label, pinned }: any) => {
|
||||
if (type !== DropdownOptionType.ITEM) {
|
||||
return label
|
||||
}
|
||||
|
||||
const config = listApiConfigMeta?.find((c) => c.id === value)
|
||||
const isCurrentConfig = config?.name === currentApiConfigName
|
||||
|
||||
return (
|
||||
<div className="flex justify-between gap-2 w-full h-5">
|
||||
<div
|
||||
className={cn("truncate min-w-0 overflow-hidden", {
|
||||
"font-medium": isCurrentConfig,
|
||||
})}>
|
||||
{label}
|
||||
</div>
|
||||
<div className="flex justify-end w-10 flex-shrink-0">
|
||||
<div
|
||||
className={cn("size-5 p-1", {
|
||||
"block group-hover:hidden": !pinned,
|
||||
hidden: !isCurrentConfig,
|
||||
})}>
|
||||
<Check className="size-3" />
|
||||
</div>
|
||||
<StandardTooltip content={pinned ? t("chat:unpin") : t("chat:pin")}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
togglePinnedApiConfig(value)
|
||||
vscode.postMessage({
|
||||
type: "toggleApiConfigPin",
|
||||
text: value,
|
||||
})
|
||||
}}
|
||||
className={cn("size-5", {
|
||||
"hidden group-hover:flex": !pinned,
|
||||
"bg-accent": pinned,
|
||||
})}>
|
||||
<Pin className="size-3 p-0.5 opacity-50" />
|
||||
</Button>
|
||||
</StandardTooltip>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
[listApiConfigMeta, currentApiConfigName, t, togglePinnedApiConfig],
|
||||
)
|
||||
|
||||
// Helper function to render non-edit mode controls
|
||||
const renderNonEditModeControls = () => (
|
||||
<div className={cn("flex", "justify-between", "items-center", "mt-auto")}>
|
||||
<div className={cn("flex", "items-center", "gap-1", "min-w-0")}>
|
||||
<div className="shrink-0">{renderModeSelector()}</div>
|
||||
|
||||
<div className={cn("flex-1", "min-w-0", "overflow-hidden")}>
|
||||
<SelectDropdown
|
||||
value={currentConfigId}
|
||||
disabled={selectApiConfigDisabled}
|
||||
title={t("chat:selectApiConfig")}
|
||||
disableSearch={false}
|
||||
placeholder={displayName}
|
||||
options={getApiConfigOptions}
|
||||
onChange={handleApiConfigChange}
|
||||
triggerClassName="w-full text-ellipsis overflow-hidden"
|
||||
itemClassName="group"
|
||||
renderItem={renderApiConfigItem}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={cn("flex", "items-center", "gap-0.5", "shrink-0")}>
|
||||
<IndexingStatusDot />
|
||||
<IconButton
|
||||
iconClass="codicon-device-camera"
|
||||
title={t("chat:addImages")}
|
||||
disabled={shouldDisableImages}
|
||||
onClick={onSelectImages}
|
||||
className="mr-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
// Helper function to render the text area section
|
||||
const renderTextAreaSection = () => (
|
||||
<div
|
||||
className={cn(
|
||||
"relative",
|
||||
"flex-1",
|
||||
"flex",
|
||||
"flex-col-reverse",
|
||||
"min-h-0",
|
||||
"overflow-hidden",
|
||||
"rounded",
|
||||
)}>
|
||||
<div
|
||||
ref={highlightLayerRef}
|
||||
className={cn(
|
||||
"absolute",
|
||||
"inset-0",
|
||||
"pointer-events-none",
|
||||
"whitespace-pre-wrap",
|
||||
"break-words",
|
||||
"text-transparent",
|
||||
"overflow-hidden",
|
||||
"font-vscode-font-family",
|
||||
"text-vscode-editor-font-size",
|
||||
"leading-vscode-editor-line-height",
|
||||
"py-2",
|
||||
"px-[9px]",
|
||||
"z-10",
|
||||
"forced-color-adjust-none",
|
||||
)}
|
||||
style={{
|
||||
color: "transparent",
|
||||
}}
|
||||
/>
|
||||
<DynamicTextArea
|
||||
ref={(el) => {
|
||||
if (typeof ref === "function") {
|
||||
ref(el)
|
||||
} else if (ref) {
|
||||
ref.current = el
|
||||
}
|
||||
textAreaRef.current = el
|
||||
}}
|
||||
value={inputValue}
|
||||
onChange={(e) => {
|
||||
handleInputChange(e)
|
||||
updateHighlights()
|
||||
}}
|
||||
onFocus={() => setIsFocused(true)}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={handleKeyUp}
|
||||
onBlur={handleBlur}
|
||||
onPaste={handlePaste}
|
||||
onSelect={updateCursorPosition}
|
||||
onMouseUp={updateCursorPosition}
|
||||
onHeightChange={(height) => {
|
||||
if (textAreaBaseHeight === undefined || height < textAreaBaseHeight) {
|
||||
setTextAreaBaseHeight(height)
|
||||
}
|
||||
|
||||
onHeightChange?.(height)
|
||||
}}
|
||||
placeholder={placeholderText}
|
||||
minRows={3}
|
||||
maxRows={15}
|
||||
autoFocus={true}
|
||||
className={cn(
|
||||
"w-full",
|
||||
"text-vscode-input-foreground",
|
||||
"font-vscode-font-family",
|
||||
"text-vscode-editor-font-size",
|
||||
"leading-vscode-editor-line-height",
|
||||
"cursor-text",
|
||||
isEditMode ? "pt-1.5 pb-10 px-2" : "py-1.5 px-2",
|
||||
isFocused
|
||||
? "border border-vscode-focusBorder outline outline-vscode-focusBorder"
|
||||
: isDraggingOver
|
||||
? "border-2 border-dashed border-vscode-focusBorder"
|
||||
: "border border-transparent",
|
||||
isDraggingOver
|
||||
? "bg-[color-mix(in_srgb,var(--vscode-input-background)_95%,var(--vscode-focusBorder))]"
|
||||
: "bg-vscode-input-background",
|
||||
"transition-background-color duration-150 ease-in-out",
|
||||
"will-change-background-color",
|
||||
"min-h-[90px]",
|
||||
"box-border",
|
||||
"rounded",
|
||||
"resize-none",
|
||||
"overflow-x-hidden",
|
||||
"overflow-y-auto",
|
||||
"pr-9",
|
||||
"flex-none flex-grow",
|
||||
"z-[2]",
|
||||
"scrollbar-none",
|
||||
"scrollbar-hide",
|
||||
)}
|
||||
onScroll={() => updateHighlights()}
|
||||
/>
|
||||
|
||||
{isTtsPlaying && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="absolute top-0 right-0 opacity-25 hover:opacity-100 z-10"
|
||||
onClick={() => vscode.postMessage({ type: "stopTts" })}>
|
||||
<VolumeX className="size-4" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<div className="absolute top-1 right-1 z-30">
|
||||
<IconButton
|
||||
iconClass={isEnhancingPrompt ? "codicon-loading" : "codicon-sparkle"}
|
||||
title={t("chat:enhancePrompt")}
|
||||
disabled={sendingDisabled}
|
||||
isLoading={isEnhancingPrompt}
|
||||
onClick={handleEnhancePrompt}
|
||||
className="opacity-60 hover:opacity-100 text-vscode-descriptionForeground hover:text-vscode-foreground"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{!isEditMode && (
|
||||
<div className="absolute bottom-1 right-1 z-30">
|
||||
<IconButton
|
||||
iconClass="codicon-send"
|
||||
title={t("chat:sendMessage")}
|
||||
disabled={sendingDisabled}
|
||||
onClick={onSend}
|
||||
className="opacity-60 hover:opacity-100 text-vscode-descriptionForeground hover:text-vscode-foreground"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!inputValue && !isEditMode && (
|
||||
<div
|
||||
className="absolute left-2 z-30 pr-9 flex items-center h-8"
|
||||
style={{
|
||||
bottom: "0.25rem",
|
||||
color: "var(--vscode-tab-inactiveForeground)",
|
||||
userSelect: "none",
|
||||
pointerEvents: "none",
|
||||
}}>
|
||||
{placeholderBottomText}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
|
|
@ -859,160 +1222,11 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
/>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={cn(
|
||||
"relative",
|
||||
"flex-1",
|
||||
"flex",
|
||||
"flex-col-reverse",
|
||||
"min-h-0",
|
||||
"overflow-hidden",
|
||||
"rounded",
|
||||
)}>
|
||||
<div
|
||||
ref={highlightLayerRef}
|
||||
className={cn(
|
||||
"absolute",
|
||||
"inset-0",
|
||||
"pointer-events-none",
|
||||
"whitespace-pre-wrap",
|
||||
"break-words",
|
||||
"text-transparent",
|
||||
"overflow-hidden",
|
||||
"font-vscode-font-family",
|
||||
"text-vscode-editor-font-size",
|
||||
"leading-vscode-editor-line-height",
|
||||
"py-2",
|
||||
"px-[9px]",
|
||||
"z-10",
|
||||
"forced-color-adjust-none",
|
||||
)}
|
||||
style={{
|
||||
color: "transparent",
|
||||
}}
|
||||
/>
|
||||
<DynamicTextArea
|
||||
ref={(el) => {
|
||||
if (typeof ref === "function") {
|
||||
ref(el)
|
||||
} else if (ref) {
|
||||
ref.current = el
|
||||
}
|
||||
textAreaRef.current = el
|
||||
}}
|
||||
value={inputValue}
|
||||
onChange={(e) => {
|
||||
handleInputChange(e)
|
||||
updateHighlights()
|
||||
}}
|
||||
onFocus={() => setIsFocused(true)}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={handleKeyUp}
|
||||
onBlur={handleBlur}
|
||||
onPaste={handlePaste}
|
||||
onSelect={updateCursorPosition}
|
||||
onMouseUp={updateCursorPosition}
|
||||
onHeightChange={(height) => {
|
||||
if (textAreaBaseHeight === undefined || height < textAreaBaseHeight) {
|
||||
setTextAreaBaseHeight(height)
|
||||
}
|
||||
|
||||
onHeightChange?.(height)
|
||||
}}
|
||||
placeholder={placeholderText}
|
||||
minRows={3}
|
||||
maxRows={15}
|
||||
autoFocus={true}
|
||||
className={cn(
|
||||
"w-full",
|
||||
"text-vscode-input-foreground",
|
||||
"font-vscode-font-family",
|
||||
"text-vscode-editor-font-size",
|
||||
"leading-vscode-editor-line-height",
|
||||
"cursor-text",
|
||||
"py-1.5 px-2",
|
||||
isFocused
|
||||
? "border border-vscode-focusBorder outline outline-vscode-focusBorder"
|
||||
: isDraggingOver
|
||||
? "border-2 border-dashed border-vscode-focusBorder"
|
||||
: "border border-transparent",
|
||||
isDraggingOver
|
||||
? "bg-[color-mix(in_srgb,var(--vscode-input-background)_95%,var(--vscode-focusBorder))]"
|
||||
: "bg-vscode-input-background",
|
||||
"transition-background-color duration-150 ease-in-out",
|
||||
"will-change-background-color",
|
||||
"min-h-[90px]",
|
||||
"box-border",
|
||||
"rounded",
|
||||
"resize-none",
|
||||
"overflow-x-hidden",
|
||||
"overflow-y-auto",
|
||||
"pr-9",
|
||||
"flex-none flex-grow",
|
||||
"z-[2]",
|
||||
"scrollbar-none",
|
||||
"scrollbar-hide",
|
||||
)}
|
||||
onScroll={() => updateHighlights()}
|
||||
/>
|
||||
|
||||
{isTtsPlaying && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="absolute top-0 right-0 opacity-25 hover:opacity-100 z-10"
|
||||
onClick={() => vscode.postMessage({ type: "stopTts" })}>
|
||||
<VolumeX className="size-4" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<div className="absolute top-1 right-1 z-30">
|
||||
<IconButton
|
||||
iconClass={isEnhancingPrompt ? "codicon-loading" : "codicon-sparkle"}
|
||||
title={t("chat:enhancePrompt")}
|
||||
disabled={sendingDisabled}
|
||||
isLoading={isEnhancingPrompt}
|
||||
onClick={handleEnhancePrompt}
|
||||
className="opacity-60 hover:opacity-100 text-vscode-descriptionForeground hover:text-vscode-foreground"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{isEditMode ? (
|
||||
<div className="absolute bottom-1 right-1 z-30">
|
||||
<IconButton
|
||||
iconClass="codicon-edit"
|
||||
title={t("chat:save.tooltip")}
|
||||
disabled={sendingDisabled}
|
||||
onClick={onSend}
|
||||
className="opacity-60 hover:opacity-100 text-vscode-descriptionForeground hover:text-vscode-foreground"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="absolute bottom-1 right-1 z-30">
|
||||
<IconButton
|
||||
iconClass="codicon-send"
|
||||
title={t("chat:sendMessage")}
|
||||
disabled={sendingDisabled}
|
||||
onClick={onSend}
|
||||
className="opacity-60 hover:opacity-100 text-vscode-descriptionForeground hover:text-vscode-foreground"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!inputValue && (
|
||||
<div
|
||||
className="absolute left-2 z-30 pr-9 flex items-center h-8"
|
||||
style={{
|
||||
bottom: "0.25rem",
|
||||
color: "var(--vscode-tab-inactiveForeground)",
|
||||
userSelect: "none",
|
||||
pointerEvents: "none",
|
||||
}}>
|
||||
{placeholderBottomText}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{renderTextAreaSection()}
|
||||
</div>
|
||||
|
||||
{isEditMode && renderEditModeControls()}
|
||||
</div>
|
||||
|
||||
{selectedImages.length > 0 && (
|
||||
|
|
@ -1027,161 +1241,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
/>
|
||||
)}
|
||||
|
||||
<div className={cn("flex", "justify-between", "items-center", "mt-auto")}>
|
||||
<div className={cn("flex", "items-center", "gap-1", "min-w-0")}>
|
||||
<div className="shrink-0">
|
||||
<ModeSelector
|
||||
value={mode}
|
||||
title={t("chat:selectMode")}
|
||||
onChange={(value) => {
|
||||
setMode(value)
|
||||
vscode.postMessage({ type: "mode", text: value })
|
||||
}}
|
||||
triggerClassName="w-full"
|
||||
modeShortcutText={modeShortcutText}
|
||||
customModes={customModes}
|
||||
customModePrompts={customModePrompts}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={cn("flex-1", "min-w-0", "overflow-hidden")}>
|
||||
<SelectDropdown
|
||||
value={currentConfigId}
|
||||
disabled={selectApiConfigDisabled}
|
||||
title={t("chat:selectApiConfig")}
|
||||
disableSearch={false}
|
||||
placeholder={displayName}
|
||||
options={[
|
||||
// Pinned items first.
|
||||
...(listApiConfigMeta || [])
|
||||
.filter((config) => pinnedApiConfigs && pinnedApiConfigs[config.id])
|
||||
.map((config) => ({
|
||||
value: config.id,
|
||||
label: config.name,
|
||||
name: config.name, // Keep name for comparison with currentApiConfigName.
|
||||
type: DropdownOptionType.ITEM,
|
||||
pinned: true,
|
||||
}))
|
||||
.sort((a, b) => a.label.localeCompare(b.label)),
|
||||
// If we have pinned items and unpinned items, add a separator.
|
||||
...(pinnedApiConfigs &&
|
||||
Object.keys(pinnedApiConfigs).length > 0 &&
|
||||
(listApiConfigMeta || []).some((config) => !pinnedApiConfigs[config.id])
|
||||
? [
|
||||
{
|
||||
value: "sep-pinned",
|
||||
label: t("chat:separator"),
|
||||
type: DropdownOptionType.SEPARATOR,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
// Unpinned items sorted alphabetically.
|
||||
...(listApiConfigMeta || [])
|
||||
.filter((config) => !pinnedApiConfigs || !pinnedApiConfigs[config.id])
|
||||
.map((config) => ({
|
||||
value: config.id,
|
||||
label: config.name,
|
||||
name: config.name, // Keep name for comparison with currentApiConfigName.
|
||||
type: DropdownOptionType.ITEM,
|
||||
pinned: false,
|
||||
}))
|
||||
.sort((a, b) => a.label.localeCompare(b.label)),
|
||||
{
|
||||
value: "sep-2",
|
||||
label: t("chat:separator"),
|
||||
type: DropdownOptionType.SEPARATOR,
|
||||
},
|
||||
{
|
||||
value: "settingsButtonClicked",
|
||||
label: t("chat:edit"),
|
||||
type: DropdownOptionType.ACTION,
|
||||
},
|
||||
]}
|
||||
onChange={(value) => {
|
||||
if (value === "settingsButtonClicked") {
|
||||
vscode.postMessage({
|
||||
type: "loadApiConfiguration",
|
||||
text: value,
|
||||
values: { section: "providers" },
|
||||
})
|
||||
} else {
|
||||
vscode.postMessage({ type: "loadApiConfigurationById", text: value })
|
||||
}
|
||||
}}
|
||||
triggerClassName="w-full text-ellipsis overflow-hidden"
|
||||
itemClassName="group"
|
||||
renderItem={({ type, value, label, pinned }) => {
|
||||
if (type !== DropdownOptionType.ITEM) {
|
||||
return label
|
||||
}
|
||||
|
||||
const config = listApiConfigMeta?.find((c) => c.id === value)
|
||||
const isCurrentConfig = config?.name === currentApiConfigName
|
||||
|
||||
return (
|
||||
<div className="flex justify-between gap-2 w-full h-5">
|
||||
<div
|
||||
className={cn("truncate min-w-0 overflow-hidden", {
|
||||
"font-medium": isCurrentConfig,
|
||||
})}>
|
||||
{label}
|
||||
</div>
|
||||
<div className="flex justify-end w-10 flex-shrink-0">
|
||||
<div
|
||||
className={cn("size-5 p-1", {
|
||||
"block group-hover:hidden": !pinned,
|
||||
hidden: !isCurrentConfig,
|
||||
})}>
|
||||
<Check className="size-3" />
|
||||
</div>
|
||||
<StandardTooltip content={pinned ? t("chat:unpin") : t("chat:pin")}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
togglePinnedApiConfig(value)
|
||||
vscode.postMessage({
|
||||
type: "toggleApiConfigPin",
|
||||
text: value,
|
||||
})
|
||||
}}
|
||||
className={cn("size-5", {
|
||||
"hidden group-hover:flex": !pinned,
|
||||
"bg-accent": pinned,
|
||||
})}>
|
||||
<Pin className="size-3 p-0.5 opacity-50" />
|
||||
</Button>
|
||||
</StandardTooltip>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={cn("flex", "items-center", "gap-0.5", "shrink-0")}>
|
||||
{isEditMode && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={onCancel}
|
||||
disabled={sendingDisabled}
|
||||
className="mr-2 text-xs">
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
<IndexingStatusDot />
|
||||
<IconButton
|
||||
iconClass="codicon-device-camera"
|
||||
title={t("chat:addImages")}
|
||||
disabled={shouldDisableImages}
|
||||
onClick={onSelectImages}
|
||||
className="mr-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{!isEditMode && renderNonEditModeControls()}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "Desar",
|
||||
"tooltip": "Desa els canvis del fitxer"
|
||||
"tooltip": "Desa els canvis del missatge"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Rebutjar",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "Speichern",
|
||||
"tooltip": "Dateiänderungen speichern"
|
||||
"tooltip": "Nachrichtenänderungen speichern"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Ablehnen",
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "Save",
|
||||
"tooltip": "Save the file changes"
|
||||
"tooltip": "Save the message changes"
|
||||
},
|
||||
"tokenProgress": {
|
||||
"availableSpace": "Available space: {{amount}} tokens",
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "Guardar",
|
||||
"tooltip": "Guardar los cambios del archivo"
|
||||
"tooltip": "Guardar los cambios del mensaje"
|
||||
},
|
||||
"tokenProgress": {
|
||||
"availableSpace": "Espacio disponible: {{amount}} tokens",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "Enregistrer",
|
||||
"tooltip": "Sauvegarder les modifications du fichier"
|
||||
"tooltip": "Enregistrer les modifications du message"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Rejeter",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "सहेजें",
|
||||
"tooltip": "फ़ाइल परिवर्तन सहेजें"
|
||||
"tooltip": "संदेश के बदलाव सहेजें"
|
||||
},
|
||||
"reject": {
|
||||
"title": "अस्वीकार करें",
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "Simpan",
|
||||
"tooltip": "Simpan perubahan file"
|
||||
"tooltip": "Simpan perubahan pesan"
|
||||
},
|
||||
"tokenProgress": {
|
||||
"availableSpace": "Ruang tersedia: {{amount}} token",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "Salva",
|
||||
"tooltip": "Salva le modifiche al file"
|
||||
"tooltip": "Salva le modifiche del messaggio"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Rifiuta",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "保存",
|
||||
"tooltip": "ファイル変更を保存"
|
||||
"tooltip": "メッセージの変更を保存"
|
||||
},
|
||||
"reject": {
|
||||
"title": "拒否",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "저장",
|
||||
"tooltip": "파일 변경사항 저장"
|
||||
"tooltip": "메시지 변경사항 저장"
|
||||
},
|
||||
"reject": {
|
||||
"title": "거부",
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "Opslaan",
|
||||
"tooltip": "Bestandswijzigingen opslaan"
|
||||
"tooltip": "Berichtwijzigingen opslaan"
|
||||
},
|
||||
"tokenProgress": {
|
||||
"availableSpace": "Beschikbare ruimte: {{amount}} tokens",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "Zapisz",
|
||||
"tooltip": "Zapisz zmiany w pliku"
|
||||
"tooltip": "Zapisz zmiany wiadomości"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Odrzuć",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "Salvar",
|
||||
"tooltip": "Salvar as alterações do arquivo"
|
||||
"tooltip": "Salvar as alterações da mensagem"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Rejeitar",
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "Сохранить",
|
||||
"tooltip": "Сохранить изменения в файле"
|
||||
"tooltip": "Сохранить изменения сообщения"
|
||||
},
|
||||
"tokenProgress": {
|
||||
"availableSpace": "Доступно места: {{amount}} токенов",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "Kaydet",
|
||||
"tooltip": "Dosya değişikliklerini kaydet"
|
||||
"tooltip": "Mesaj değişikliklerini kaydet"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Reddet",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "Lưu",
|
||||
"tooltip": "Lưu các thay đổi tệp"
|
||||
"tooltip": "Lưu các thay đổi tin nhắn"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Từ chối",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "保存",
|
||||
"tooltip": "保存文件更改"
|
||||
"tooltip": "保存消息更改"
|
||||
},
|
||||
"reject": {
|
||||
"title": "拒绝",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
},
|
||||
"save": {
|
||||
"title": "儲存",
|
||||
"tooltip": "儲存檔案變更"
|
||||
"tooltip": "儲存訊息變更"
|
||||
},
|
||||
"reject": {
|
||||
"title": "拒絕",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue