From 4604211ffad67f53f6aae145e92faec52a189acc Mon Sep 17 00:00:00 2001 From: Will Li Date: Thu, 10 Jul 2025 20:26:35 -0700 Subject: [PATCH] ui fix --- .../src/components/chat/ChatTextArea.tsx | 674 ++++++++++-------- webview-ui/src/i18n/locales/ca/chat.json | 2 +- webview-ui/src/i18n/locales/de/chat.json | 2 +- webview-ui/src/i18n/locales/en/chat.json | 2 +- webview-ui/src/i18n/locales/es/chat.json | 2 +- webview-ui/src/i18n/locales/fr/chat.json | 2 +- webview-ui/src/i18n/locales/hi/chat.json | 2 +- webview-ui/src/i18n/locales/id/chat.json | 2 +- webview-ui/src/i18n/locales/it/chat.json | 2 +- webview-ui/src/i18n/locales/ja/chat.json | 2 +- webview-ui/src/i18n/locales/ko/chat.json | 2 +- webview-ui/src/i18n/locales/nl/chat.json | 2 +- webview-ui/src/i18n/locales/pl/chat.json | 2 +- webview-ui/src/i18n/locales/pt-BR/chat.json | 2 +- webview-ui/src/i18n/locales/ru/chat.json | 2 +- webview-ui/src/i18n/locales/tr/chat.json | 2 +- webview-ui/src/i18n/locales/vi/chat.json | 2 +- webview-ui/src/i18n/locales/zh-CN/chat.json | 2 +- webview-ui/src/i18n/locales/zh-TW/chat.json | 2 +- 19 files changed, 385 insertions(+), 325 deletions(-) diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index b7da0a6ce1..66c42e20b3 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -785,6 +785,369 @@ const ChatTextArea = forwardRef( 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 = () => ( + + ) + + // Helper function to render edit mode controls + const renderEditModeControls = () => ( +
+
+
{renderModeSelector()}
+
+
+ + + +
+
+ ) + + // 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 ( +
+
+ {label} +
+
+
+ +
+ + + +
+
+ ) + }, + [listApiConfigMeta, currentApiConfigName, t, togglePinnedApiConfig], + ) + + // Helper function to render non-edit mode controls + const renderNonEditModeControls = () => ( +
+
+
{renderModeSelector()}
+ +
+ +
+
+ +
+ + +
+
+ ) + + // Helper function to render the text area section + const renderTextAreaSection = () => ( +
+
+ { + 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 && ( + + )} + +
+ +
+ + {!isEditMode && ( +
+ +
+ )} + + {!inputValue && !isEditMode && ( +
+ {placeholderBottomText} +
+ )} +
+ ) + return (
( />
)} -
-
- { - 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 && ( - - )} - -
- -
- - {isEditMode ? ( -
- -
- ) : ( -
- -
- )} - - {!inputValue && ( -
- {placeholderBottomText} -
- )} -
+ {renderTextAreaSection()}
+ + {isEditMode && renderEditModeControls()}
{selectedImages.length > 0 && ( @@ -1027,161 +1241,7 @@ const ChatTextArea = forwardRef( /> )} -
-
-
- { - setMode(value) - vscode.postMessage({ type: "mode", text: value }) - }} - triggerClassName="w-full" - modeShortcutText={modeShortcutText} - customModes={customModes} - customModePrompts={customModePrompts} - /> -
- -
- 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 ( -
-
- {label} -
-
-
- -
- - - -
-
- ) - }} - /> -
-
- -
- {isEditMode && ( - - )} - - -
-
+ {!isEditMode && renderNonEditModeControls()} ) }, diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index 29989cefb5..ccc83c4387 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -44,7 +44,7 @@ }, "save": { "title": "Desar", - "tooltip": "Desa els canvis del fitxer" + "tooltip": "Desa els canvis del missatge" }, "reject": { "title": "Rebutjar", diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index fa9b7e02ae..ee8054beda 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -44,7 +44,7 @@ }, "save": { "title": "Speichern", - "tooltip": "Dateiänderungen speichern" + "tooltip": "Nachrichtenänderungen speichern" }, "reject": { "title": "Ablehnen", diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index 0c4a430576..fd30f776eb 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -39,7 +39,7 @@ }, "save": { "title": "Save", - "tooltip": "Save the file changes" + "tooltip": "Save the message changes" }, "tokenProgress": { "availableSpace": "Available space: {{amount}} tokens", diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index 864d074c3b..044409fe15 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -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", diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index 9c0805c737..4a69fd91a1 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -44,7 +44,7 @@ }, "save": { "title": "Enregistrer", - "tooltip": "Sauvegarder les modifications du fichier" + "tooltip": "Enregistrer les modifications du message" }, "reject": { "title": "Rejeter", diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index 54f18ae6df..c717b4fadf 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -44,7 +44,7 @@ }, "save": { "title": "सहेजें", - "tooltip": "फ़ाइल परिवर्तन सहेजें" + "tooltip": "संदेश के बदलाव सहेजें" }, "reject": { "title": "अस्वीकार करें", diff --git a/webview-ui/src/i18n/locales/id/chat.json b/webview-ui/src/i18n/locales/id/chat.json index 8401a5777c..95b18d5431 100644 --- a/webview-ui/src/i18n/locales/id/chat.json +++ b/webview-ui/src/i18n/locales/id/chat.json @@ -45,7 +45,7 @@ }, "save": { "title": "Simpan", - "tooltip": "Simpan perubahan file" + "tooltip": "Simpan perubahan pesan" }, "tokenProgress": { "availableSpace": "Ruang tersedia: {{amount}} token", diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index 259e4ef2b3..7fecf646db 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -44,7 +44,7 @@ }, "save": { "title": "Salva", - "tooltip": "Salva le modifiche al file" + "tooltip": "Salva le modifiche del messaggio" }, "reject": { "title": "Rifiuta", diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index 20aad74477..303aefc83a 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -44,7 +44,7 @@ }, "save": { "title": "保存", - "tooltip": "ファイル変更を保存" + "tooltip": "メッセージの変更を保存" }, "reject": { "title": "拒否", diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index 4134d7e8f6..c7327d475c 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -44,7 +44,7 @@ }, "save": { "title": "저장", - "tooltip": "파일 변경사항 저장" + "tooltip": "메시지 변경사항 저장" }, "reject": { "title": "거부", diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index 3d77a767fe..e34b9f0521 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -39,7 +39,7 @@ }, "save": { "title": "Opslaan", - "tooltip": "Bestandswijzigingen opslaan" + "tooltip": "Berichtwijzigingen opslaan" }, "tokenProgress": { "availableSpace": "Beschikbare ruimte: {{amount}} tokens", diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index eed1749b9b..3c01cba1a0 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -44,7 +44,7 @@ }, "save": { "title": "Zapisz", - "tooltip": "Zapisz zmiany w pliku" + "tooltip": "Zapisz zmiany wiadomości" }, "reject": { "title": "Odrzuć", diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index 03dfa468fc..85a28e8a7c 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -44,7 +44,7 @@ }, "save": { "title": "Salvar", - "tooltip": "Salvar as alterações do arquivo" + "tooltip": "Salvar as alterações da mensagem" }, "reject": { "title": "Rejeitar", diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index 6d31e3ed10..b33db4159f 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -39,7 +39,7 @@ }, "save": { "title": "Сохранить", - "tooltip": "Сохранить изменения в файле" + "tooltip": "Сохранить изменения сообщения" }, "tokenProgress": { "availableSpace": "Доступно места: {{amount}} токенов", diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index 6aca7b9a80..815bf5bae8 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -44,7 +44,7 @@ }, "save": { "title": "Kaydet", - "tooltip": "Dosya değişikliklerini kaydet" + "tooltip": "Mesaj değişikliklerini kaydet" }, "reject": { "title": "Reddet", diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index 4522cd7917..952e5721d4 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -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", diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index 3f5d5ba48a..ff67e6aaa6 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -44,7 +44,7 @@ }, "save": { "title": "保存", - "tooltip": "保存文件更改" + "tooltip": "保存消息更改" }, "reject": { "title": "拒绝", diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index 1dc3b19f7a..e9cc813ad4 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -44,7 +44,7 @@ }, "save": { "title": "儲存", - "tooltip": "儲存檔案變更" + "tooltip": "儲存訊息變更" }, "reject": { "title": "拒絕",