refactor: Naming (#27001)
This commit is contained in:
@@ -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 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<ChatFormFileInputInvisible bind:this={fileInputRef} onFileSelect={handleFileSelect} />
|
||||
<ChatFormInputFileInputInvisible bind:this={fileInputRef} onFileSelect={handleFileSelect} />
|
||||
|
||||
<form
|
||||
class="relative grid {className}"
|
||||
@@ -603,35 +602,20 @@
|
||||
<div
|
||||
class="flex-column relative min-h-12 items-center rounded-4xl md:rounded-3xl py-2 pb-2.25 shadow-sm transition-all focus-within:shadow-md md:py-3!"
|
||||
>
|
||||
{#if useContenteditable}
|
||||
<ChatFormContentEditable
|
||||
class="px-5 py-1.5 md:pt-0 mb-0.5"
|
||||
bind:this={inputRef}
|
||||
bind:value
|
||||
onKeydown={handleKeydown}
|
||||
onInput={() => {
|
||||
pickers.handleInput();
|
||||
onValueChange?.(value);
|
||||
}}
|
||||
onPaste={handlePaste}
|
||||
{disabled}
|
||||
{placeholder}
|
||||
/>
|
||||
{:else}
|
||||
<ChatFormTextarea
|
||||
class="px-5 py-1.5 md:pt-0"
|
||||
bind:this={inputRef}
|
||||
bind:value
|
||||
onKeydown={handleKeydown}
|
||||
onInput={() => {
|
||||
pickers.handleInput();
|
||||
onValueChange?.(value);
|
||||
}}
|
||||
onPaste={handlePaste}
|
||||
{disabled}
|
||||
{placeholder}
|
||||
/>
|
||||
{/if}
|
||||
<ChatFormInput
|
||||
class="px-5 py-1.5 md:pt-0"
|
||||
bind:this={inputRef}
|
||||
bind:value
|
||||
onKeydown={handleKeydown}
|
||||
onInput={() => {
|
||||
pickers.handleInput();
|
||||
onValueChange?.(value);
|
||||
}}
|
||||
onPaste={handlePaste}
|
||||
{disabled}
|
||||
{placeholder}
|
||||
{useContenteditable}
|
||||
/>
|
||||
|
||||
{#if mcpResourceStore.hasAttachments}
|
||||
<ChatFormMcpResourcesList
|
||||
@@ -667,7 +651,7 @@
|
||||
<ContextGaugePopup />
|
||||
|
||||
{#if toolsStore.hasEnabledCwdTools}
|
||||
<ChatFormWorkingDirectory
|
||||
<ChatFormCurrentWorkingDirectory
|
||||
directory={cwd}
|
||||
isOpen={pickers.isWorkingDirectoryPickerOpen}
|
||||
bind:query={pickers.workingDirectoryQuery}
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
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}
|
||||
>
|
||||
<ChatFormWorkingDirectoryChip
|
||||
<ChatFormCurrentWorkingDirectoryChip
|
||||
{directory}
|
||||
{homeBase}
|
||||
{disabled}
|
||||
@@ -372,7 +372,7 @@
|
||||
{#if !fileSearchEnabled}
|
||||
<div class="px-2 py-1.5 text-sm text-muted-foreground">{searchUnavailableMessage}</div>
|
||||
{:else if query.trim() && (search.isSearching || queryResults.length > 0 || searchError)}
|
||||
<ChatFormWorkingDirectoryResultsList
|
||||
<ChatFormCurrentWorkingDirectoryResultsList
|
||||
results={queryResults}
|
||||
hoveredIndex={nav.hoveredIndex}
|
||||
isSearching={search.isSearching}
|
||||
@@ -0,0 +1,80 @@
|
||||
<script lang="ts">
|
||||
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);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if useContenteditable}
|
||||
<ChatFormInputRich
|
||||
bind:this={richRef}
|
||||
class={className}
|
||||
{disabled}
|
||||
{onInput}
|
||||
{onKeydown}
|
||||
{onPaste}
|
||||
{placeholder}
|
||||
bind:value
|
||||
/>
|
||||
{:else}
|
||||
<ChatFormInputBasic
|
||||
bind:this={basicRef}
|
||||
class={className}
|
||||
{disabled}
|
||||
{onInput}
|
||||
{onKeydown}
|
||||
{onPaste}
|
||||
{placeholder}
|
||||
bind:value
|
||||
/>
|
||||
{/if}
|
||||
+13
-13
@@ -2,7 +2,7 @@
|
||||
import { CODE_BLOCK } from '$lib/constants';
|
||||
import { ColorMode } from '$lib/enums';
|
||||
import { isMobile } from '$lib/stores';
|
||||
import type { ContentEditableToken } from '$lib/types';
|
||||
import type { ChatFormInputRichToken } from '$lib/types';
|
||||
import type { SourceHistoryEntry } from '$lib/utils';
|
||||
import {
|
||||
badgeAwareWordJump,
|
||||
@@ -64,7 +64,7 @@
|
||||
rootElement.dataset.empty = source.length === 0 ? 'true' : 'false';
|
||||
}
|
||||
|
||||
function renderTokens(tokens: ContentEditableToken[]) {
|
||||
function renderTokens(tokens: ChatFormInputRichToken[]) {
|
||||
if (!rootElement) return;
|
||||
|
||||
const caret = rangeToTextOffset(rootElement, safeRange());
|
||||
@@ -127,7 +127,7 @@
|
||||
}
|
||||
|
||||
function highlightCodeBlocks(root: HTMLElement) {
|
||||
for (const el of root.querySelectorAll<HTMLElement>('code[data-code-token="block"]')) {
|
||||
for (const el of root.querySelectorAll<HTMLElement>('code[data-code-token="code_block"]')) {
|
||||
highlightCodeBlockElement(el);
|
||||
}
|
||||
}
|
||||
@@ -151,7 +151,7 @@
|
||||
}
|
||||
|
||||
while (node && node !== rootElement) {
|
||||
if (node instanceof HTMLElement && node.dataset.codeToken === 'block') {
|
||||
if (node instanceof HTMLElement && node.dataset.codeToken === 'code_block') {
|
||||
const caret = rangeToTextOffset(rootElement, range);
|
||||
|
||||
if (highlightCodeBlockElement(node)) {
|
||||
@@ -404,7 +404,7 @@
|
||||
let node: Node | null = container.parentNode;
|
||||
|
||||
while (node && node !== rootElement) {
|
||||
if (node instanceof HTMLElement && node.dataset.codeToken === 'block') {
|
||||
if (node instanceof HTMLElement && node.dataset.codeToken === 'code_block') {
|
||||
const tail = document.createRange();
|
||||
|
||||
tail.setStart(container, offset);
|
||||
@@ -462,7 +462,7 @@
|
||||
|
||||
const first = rootElement.firstChild;
|
||||
|
||||
if (!(first instanceof HTMLElement) || first.dataset.codeToken !== 'block') return false;
|
||||
if (!(first instanceof HTMLElement) || first.dataset.codeToken !== 'code_block') return false;
|
||||
|
||||
const range = safeRange();
|
||||
|
||||
@@ -507,7 +507,7 @@
|
||||
|
||||
const second = first.nextSibling;
|
||||
|
||||
if (!(second instanceof HTMLElement) || second.dataset.codeToken !== 'block') return;
|
||||
if (!(second instanceof HTMLElement) || second.dataset.codeToken !== 'code_block') return;
|
||||
|
||||
const range = safeRange();
|
||||
const onHatch =
|
||||
@@ -787,7 +787,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-1 {className}">
|
||||
<div class="flex-1 {className} mb-0.5">
|
||||
<div
|
||||
bind:this={rootElement}
|
||||
contenteditable={!disabled}
|
||||
@@ -798,7 +798,7 @@
|
||||
data-placeholder={placeholder}
|
||||
tabindex={disabled ? -1 : 0}
|
||||
class={[
|
||||
'chat-form-contenteditable text-md min-h-12 w-full overflow-y-auto whitespace-pre-wrap wrap-break-word border-0 bg-transparent p-0 leading-6 outline-none focus-visible:ring-0 focus-visible:ring-offset-0',
|
||||
'chat-form-input-rich text-md min-h-12 w-full overflow-y-auto whitespace-pre-wrap wrap-break-word border-0 bg-transparent p-0 leading-6 outline-none focus-visible:ring-0 focus-visible:ring-offset-0',
|
||||
disabled && 'cursor-not-allowed'
|
||||
]}
|
||||
style="max-height: var(--max-message-height);"
|
||||
@@ -815,18 +815,18 @@
|
||||
<style>
|
||||
/* pre-wrap is load-bearing: without it Chromium collapses \n in
|
||||
text nodes and converts them to spaces while typing */
|
||||
.chat-form-contenteditable {
|
||||
.chat-form-input-rich {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.chat-form-contenteditable:global([data-empty='true'])::before {
|
||||
.chat-form-input-rich:global([data-empty='true'])::before {
|
||||
content: attr(data-placeholder);
|
||||
color: var(--muted-foreground);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Inline code - mirrors markdown-content.css */
|
||||
.chat-form-contenteditable :global(code[data-code-token='inline']) {
|
||||
.chat-form-input-rich :global(code[data-code-token='code_inline']) {
|
||||
background: var(--muted);
|
||||
color: var(--muted-foreground);
|
||||
padding: 0.125rem 0.375rem;
|
||||
@@ -835,7 +835,7 @@
|
||||
}
|
||||
|
||||
/* Fenced code block - mirrors .code-block-wrapper in markdown-content.css */
|
||||
.chat-form-contenteditable :global(code[data-code-token='block']) {
|
||||
.chat-form-input-rich :global(code[data-code-token='code_block']) {
|
||||
display: block;
|
||||
margin: 0.25rem 0;
|
||||
padding: 0.75rem 1rem;
|
||||
+6
-6
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
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 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<ChatFormCommandPicker
|
||||
<ChatFormPickerCommand
|
||||
bind:this={commandPickerRef}
|
||||
isOpen={isCommandPickerOpen ?? false}
|
||||
query={commandQuery ?? ''}
|
||||
@@ -96,7 +96,7 @@
|
||||
{onPromptLoadError}
|
||||
/>
|
||||
|
||||
<ChatFormMentionPicker
|
||||
<ChatFormPickerMention
|
||||
bind:this={mentionPickerRef}
|
||||
isOpen={isMentionPickerOpen ?? false}
|
||||
query={mentionQuery ?? ''}
|
||||
|
||||
@@ -91,7 +91,7 @@ export { default as ChatAttachmentsListItemThumbnailImage } from './ChatAttachme
|
||||
* preview without carousel, or a gallery/carousel view when multiple items exist.
|
||||
* Uses ChatAttachmentPreviewSingle internally for each item's content.
|
||||
*/
|
||||
export { default as ChatAttachmentsPreview } from './ChatAttachments/ChatAttachmentsPreview.svelte';
|
||||
export { default as ChatAttachmentsPreview } from './ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreview.svelte';
|
||||
export { default as ChatAttachmentsPreviewNavButtons } from './ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewNavButtons.svelte';
|
||||
export { default as ChatAttachmentsPreviewFileInfo } from './ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewFileInfo.svelte';
|
||||
export { default as ChatAttachmentsPreviewThumbnailStrip } from './ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewThumbnailStrip.svelte';
|
||||
@@ -120,8 +120,8 @@ export { default as ChatAttachmentsPreviewCurrentItem } from './ChatAttachments/
|
||||
* Used by ChatScreenForm and ChatMessageEditForm for both new conversations and message editing.
|
||||
*
|
||||
* **Architecture:**
|
||||
* - Composes ChatFormTextarea (or ChatFormContentEditable for messages with
|
||||
* file mention links), ChatFormActions, and ChatFormPickerMcpPrompts
|
||||
* - Composes ChatFormInput (a plain textarea, or a contenteditable for
|
||||
* messages with file mention links), ChatFormActions, and ChatFormPickerMcpPrompts
|
||||
* - Manages file upload state via `uploadedFiles` bindable prop
|
||||
* - Integrates with ModelsSelectorDropdown for model selection in router mode
|
||||
* - Communicates with parent via callbacks (onSubmit, onFilesAdd, onStop, etc.)
|
||||
@@ -258,7 +258,7 @@ export { default as ChatFormContextGauge } from './ChatForm/ChatFormContextGauge
|
||||
/**
|
||||
* Hidden file input element for programmatic file selection.
|
||||
*/
|
||||
export { default as ChatFormFileInputInvisible } from './ChatForm/ChatFormFileInputInvisible.svelte';
|
||||
export { default as ChatFormInputFileInputInvisible } from './ChatForm/ChatFormInput/ChatFormInputFileInputInvisible.svelte';
|
||||
|
||||
/**
|
||||
* Displays MCP Resource attachments as a horizontal carousel.
|
||||
@@ -267,18 +267,13 @@ export { default as ChatFormFileInputInvisible } from './ChatForm/ChatFormFileIn
|
||||
export { default as ChatFormMcpResourcesList } from './ChatForm/ChatFormMcpResourcesList.svelte';
|
||||
|
||||
/**
|
||||
* Auto-resizing contenteditable input that renders `[name](file://...)`
|
||||
* mention links as inline chips while keeping the value as the markdown
|
||||
* source string. ChatForm swaps it in once a mention link lands in the
|
||||
* buffer. Shares the focus()/resetHeight()/caret handle with the textarea.
|
||||
* The message editor. Renders a plain auto-resizing textarea by default,
|
||||
* or a contenteditable that renders `[name](file://...)` mention links as
|
||||
* inline chips (keeping the value as the markdown source string) once a
|
||||
* mention link lands in the buffer. The variant is selected via the
|
||||
* `useContenteditable` prop; both share one imperative handle.
|
||||
*/
|
||||
export { default as ChatFormContentEditable } from './ChatForm/ChatFormContentEditable.svelte';
|
||||
|
||||
/**
|
||||
* Plain auto-resizing textarea with IME composition support. Default input
|
||||
* renderer inside ChatForm until a file mention lands.
|
||||
*/
|
||||
export { default as ChatFormTextarea } from './ChatForm/ChatFormTextarea.svelte';
|
||||
export { default as ChatFormInput } from './ChatForm/ChatFormInput/ChatFormInput.svelte';
|
||||
|
||||
/**
|
||||
* Working directory selector for agent mode. Renders a chip below the chat
|
||||
@@ -288,7 +283,7 @@ export { default as ChatFormTextarea } from './ChatForm/ChatFormTextarea.svelte'
|
||||
* synthetic "Set working directory to ..." user message into chat history
|
||||
* and is enforced on tool calls via the `x-tool-cwd` request header.
|
||||
*/
|
||||
export { default as ChatFormWorkingDirectory } from './ChatForm/ChatFormWorkingDirectory.svelte';
|
||||
export { default as ChatFormCurrentWorkingDirectory } from './ChatForm/ChatFormCurrentWorkingDirectory/ChatFormCurrentWorkingDirectory.svelte';
|
||||
|
||||
/**
|
||||
* **ChatFormPickerMcpPrompts** - MCP prompt selection interface
|
||||
@@ -359,14 +354,14 @@ export { default as ChatFormPickerPopover } from './ChatForm/ChatFormPickers/Cha
|
||||
* Generic scrollable list for picker popovers. Provides search input,
|
||||
* scroll-into-view for keyboard navigation, loading skeletons, empty state,
|
||||
* and optional footer. Uses Svelte 5 snippets for item/skeleton/footer rendering.
|
||||
* Shared by ChatFormPickerMcpPrompts and ChatFormMentionPicker.
|
||||
* Shared by ChatFormPickerMcpPrompts and ChatFormPickerMention.
|
||||
*/
|
||||
export { default as ChatFormPickerList } from './ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerList.svelte';
|
||||
|
||||
/**
|
||||
* Generic button wrapper for picker list items. Provides consistent styling,
|
||||
* hover/selected states, and data-picker-index attribute for scroll-into-view.
|
||||
* Shared by ChatFormPickerMcpPrompts and ChatFormMentionPicker.
|
||||
* Shared by ChatFormPickerMcpPrompts and ChatFormPickerMention.
|
||||
*/
|
||||
export { default as ChatFormPickerListItem } from './ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItem.svelte';
|
||||
|
||||
@@ -389,14 +384,14 @@ export { default as ChatFormPickerListItemSkeleton } from './ChatForm/ChatFormPi
|
||||
* tool, scoped to the conversation cwd (or server home when unset).
|
||||
* Selection splices a `[name](file:///<abs path>)` link into the input.
|
||||
*/
|
||||
export { default as ChatFormMentionPicker } from './ChatForm/ChatFormPickers/ChatFormMentionPicker.svelte';
|
||||
export { default as ChatFormMentionPicker } from './ChatForm/ChatFormPickers/ChatFormPickerMention.svelte';
|
||||
|
||||
/**
|
||||
* `/`-triggered slash-command picker. Lists the available slash commands
|
||||
* (`/prompt`, `/cwd`, `/model`) filtered by the typed query; selection
|
||||
* hands the command to the parent for dispatch.
|
||||
*/
|
||||
export { default as ChatFormCommandPicker } from './ChatForm/ChatFormPickers/ChatFormCommandPicker.svelte';
|
||||
export { default as ChatFormCommandPicker } from './ChatForm/ChatFormPickers/ChatFormPickerCommand.svelte';
|
||||
|
||||
/**
|
||||
* Hosts the chat-form pickers (slash-command, MCP prompt, file mention)
|
||||
|
||||
Reference in New Issue
Block a user