diff --git a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreview.svelte similarity index 100% rename from tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreview.svelte diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatForm.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatForm.svelte index bf56e91083..49c3dfe205 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatForm.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatForm.svelte @@ -3,12 +3,11 @@ import { ChatAttachmentsList, ChatFormActions, - ChatFormContentEditable, - ChatFormFileInputInvisible, + ChatFormCurrentWorkingDirectory, + ChatFormInput, + ChatFormInputFileInputInvisible, ChatFormMcpResourcesList, ChatFormPickers, - ChatFormTextarea, - ChatFormWorkingDirectory, DialogMcpResourcesBrowser } from '$lib/components/app'; import { @@ -121,7 +120,7 @@ let audioRecorder: AudioRecorder | undefined; let chatFormActionsRef: ChatFormActions | undefined = $state(undefined); - let fileInputRef: ChatFormFileInputInvisible | undefined = $state(undefined); + let fileInputRef: ChatFormInputFileInputInvisible | undefined = $state(undefined); let pickersRef: { handleKeydown: (event: KeyboardEvent) => boolean } | undefined = $state(undefined); let inputRef: ChatInputHandle | undefined = $state(undefined); @@ -544,7 +543,7 @@ } - +
- {#if useContenteditable} - { - pickers.handleInput(); - onValueChange?.(value); - }} - onPaste={handlePaste} - {disabled} - {placeholder} - /> - {:else} - { - pickers.handleInput(); - onValueChange?.(value); - }} - onPaste={handlePaste} - {disabled} - {placeholder} - /> - {/if} + { + pickers.handleInput(); + onValueChange?.(value); + }} + onPaste={handlePaste} + {disabled} + {placeholder} + {useContenteditable} + /> {#if mcpResourceStore.hasAttachments} {#if toolsStore.hasEnabledCwdTools} - - import ChatFormWorkingDirectoryChip from './ChatFormWorkingDirectoryChip.svelte'; - import ChatFormWorkingDirectoryResultsList from './ChatFormWorkingDirectoryResultsList.svelte'; + import ChatFormCurrentWorkingDirectoryChip from './ChatFormCurrentWorkingDirectoryChip.svelte'; + import ChatFormCurrentWorkingDirectoryResultsList from './ChatFormCurrentWorkingDirectoryResultsList.svelte'; import { FolderOpen } from '@lucide/svelte'; import SearchInput from '$lib/components/app/forms/SearchInput.svelte'; import * as Popover from '$lib/components/ui/popover'; @@ -250,7 +250,7 @@ // user cancelled - silently ignore; other errors are logged if (err instanceof DOMException && err.name === 'AbortError') return; - console.error('[ChatFormWorkingDirectory] showDirectoryPicker failed:', err); + console.error('[ChatFormCurrentWorkingDirectory] showDirectoryPicker failed:', err); } } @@ -331,7 +331,7 @@ onclick={onOpen} {disabled} > - {searchUnavailableMessage} {:else if query.trim() && (search.isSearching || queryResults.length > 0 || searchError)} - + import ChatFormInputBasic from './ChatFormInputBasic.svelte'; + import ChatFormInputRich from './ChatFormInputRich.svelte'; + + interface Props { + class?: string; + disabled?: boolean; + onInput?: () => void; + onKeydown?: (event: KeyboardEvent) => void; + onPaste?: (event: ClipboardEvent) => void; + placeholder?: string; + value?: string; + useContenteditable?: boolean; + } + + let { + class: className = '', + disabled = false, + onInput, + onKeydown, + onPaste, + placeholder = 'Ask anything...', + useContenteditable = false, + value = $bindable('') + }: Props = $props(); + + let basicRef: ChatFormInputBasic | undefined = $state(); + let richRef: ChatFormInputRich | undefined = $state(); + + // The two renderers share one imperative handle (focus/caret/height), so + // the parent can drive whichever variant is mounted through this one. + export function getElement() { + return useContenteditable ? richRef?.getElement() : basicRef?.getElement(); + } + + export function focus() { + if (useContenteditable) richRef?.focus(); + else basicRef?.focus(); + } + + export function resetHeight() { + if (useContenteditable) richRef?.resetHeight(); + else basicRef?.resetHeight(); + } + + export function getCaretOffset(): number { + return useContenteditable + ? (richRef?.getCaretOffset() ?? 0) + : (basicRef?.getCaretOffset() ?? 0); + } + + export function setCaretOffset(offset: number) { + if (useContenteditable) richRef?.setCaretOffset(offset); + else basicRef?.setCaretOffset(offset); + } + + +{#if useContenteditable} + +{:else} + +{/if} diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormTextarea.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormInput/ChatFormInputBasic.svelte similarity index 100% rename from tools/ui/src/lib/components/app/chat/ChatForm/ChatFormTextarea.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormInput/ChatFormInputBasic.svelte diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormFileInputInvisible.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormInput/ChatFormInputFileInputInvisible.svelte similarity index 100% rename from tools/ui/src/lib/components/app/chat/ChatForm/ChatFormFileInputInvisible.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormInput/ChatFormInputFileInputInvisible.svelte diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContentEditable.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormInput/ChatFormInputRich.svelte similarity index 99% rename from tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContentEditable.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormInput/ChatFormInputRich.svelte index ba44304011..c0a8f9d1cb 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContentEditable.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormInput/ChatFormInputRich.svelte @@ -787,7 +787,7 @@ } -
+
- import ChatFormCommandPicker from './ChatFormCommandPicker.svelte'; - import ChatFormMentionPicker from './ChatFormMentionPicker.svelte'; + import ChatFormPickerCommand from './ChatFormPickerCommand.svelte'; import ChatFormPickerMcpPrompts from './ChatFormPickerMcpPrompts/ChatFormPickerMcpPrompts.svelte'; + import ChatFormPickerMention from './ChatFormPickerMention.svelte'; import type { ChatFormCommand, FileMentionEntry, @@ -55,9 +55,9 @@ scopePath }: Props = $props(); - let commandPickerRef: ChatFormCommandPicker | undefined = $state(undefined); + let commandPickerRef: ChatFormPickerCommand | undefined = $state(undefined); let promptPickerRef: ChatFormPickerMcpPrompts | undefined = $state(undefined); - let mentionPickerRef: ChatFormMentionPicker | undefined = $state(undefined); + let mentionPickerRef: ChatFormPickerMention | undefined = $state(undefined); /** Delegate keyboard events to the active picker child; true if handled. */ export function handleKeydown(event: KeyboardEvent): boolean { @@ -77,7 +77,7 @@ } - -