From d7d21b2c55cd512b7f5a3011e4fa07ef11d9facb Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Tue, 8 Sep 2026 14:00:58 -0700 Subject: [PATCH] Show picker-selected native chat skills as pills (#19616) * Render picker-selected native chat skills as inline pills * Use cube icon for native chat skill pills * Update skill pill label assertion * Use cube icon for every native chat skill pill * Use neutral cube icon for native chat skill pills * Match native chat skill icon to selector --------- Co-authored-by: Merge Sim --- .../native-chat/NativeChatComposer.tsx | 5 +- .../native-chat/NativeChatComposerField.tsx | 51 ++-- .../NativeChatPromptEditor.test.tsx | 142 +++++++++++ .../native-chat/NativeChatPromptEditor.tsx | 233 ++++++++++++++++++ .../native-chat/NativeChatSkillPill.tsx | 28 +++ .../native-chat-composer-autogrow.test.tsx | 4 +- .../native-chat-composer-composition.test.tsx | 70 +++--- .../native-chat/native-chat-composer-input.ts | 14 ++ .../native-chat/native-chat-draft-cache.ts | 31 ++- .../native-chat-prompt-document.ts | 68 +++++ .../native-chat-prompt-editor.test-support.ts | 17 ++ ...structured-send-composition-clear.test.tsx | 27 +- ...-chat-composer-app-menu-selection.test.tsx | 4 +- ...native-chat-composer-app-menu-selection.ts | 5 +- .../use-native-chat-composer-attachments.ts | 3 +- .../use-native-chat-composer-keydown.ts | 2 +- .../use-native-chat-dictation-actions.ts | 3 +- .../use-native-chat-picker-state.ts | 7 +- .../use-native-chat-typed-insertion.ts | 3 +- 19 files changed, 637 insertions(+), 80 deletions(-) create mode 100644 src/renderer/src/components/native-chat/NativeChatPromptEditor.test.tsx create mode 100644 src/renderer/src/components/native-chat/NativeChatPromptEditor.tsx create mode 100644 src/renderer/src/components/native-chat/NativeChatSkillPill.tsx create mode 100644 src/renderer/src/components/native-chat/native-chat-composer-input.ts create mode 100644 src/renderer/src/components/native-chat/native-chat-prompt-document.ts create mode 100644 src/renderer/src/components/native-chat/native-chat-prompt-editor.test-support.ts diff --git a/src/renderer/src/components/native-chat/NativeChatComposer.tsx b/src/renderer/src/components/native-chat/NativeChatComposer.tsx index 79ca7cbd851..4833f89fc94 100644 --- a/src/renderer/src/components/native-chat/NativeChatComposer.tsx +++ b/src/renderer/src/components/native-chat/NativeChatComposer.tsx @@ -1,3 +1,4 @@ +import type { NativeChatComposerInput } from './native-chat-composer-input' import { forwardRef, useCallback, useImperativeHandle, useState } from 'react' import { useAppStore } from '../../store' import { sendRuntimePtyInput } from '@/runtime/runtime-terminal-inspection' @@ -147,7 +148,7 @@ const NativeChatComposerPane = forwardRef { + const syncCaret = useCallback((el: NativeChatComposerInput) => { setCaret(el.selectionStart ?? el.value.length) }, []) @@ -353,7 +354,7 @@ const NativeChatComposerPane = forwardRef { + (value: string, element: NativeChatComposerInput) => { setDraft(value) setHistory((prev) => ({ entries: prev.entries, index: null })) syncCaret(element) diff --git a/src/renderer/src/components/native-chat/NativeChatComposerField.tsx b/src/renderer/src/components/native-chat/NativeChatComposerField.tsx index 25fff0267be..6b474a2f267 100644 --- a/src/renderer/src/components/native-chat/NativeChatComposerField.tsx +++ b/src/renderer/src/components/native-chat/NativeChatComposerField.tsx @@ -1,3 +1,5 @@ +import { NativeChatPromptEditor } from './NativeChatPromptEditor' +import type { NativeChatComposerInput } from './native-chat-composer-input' import type { ClipboardEventHandler, KeyboardEventHandler, RefObject } from 'react' import { useLayoutEffect, useRef } from 'react' import { ImageOff } from 'lucide-react' @@ -19,7 +21,7 @@ export type NativeChatComposerFieldProps = { /** Pane identity published to the drop pipeline so a native file drop lands * only in the composer it was dropped on. */ composerScopeKey: string - textareaRef: RefObject + textareaRef: RefObject draft: string disabled: boolean hasPty: boolean @@ -35,11 +37,11 @@ export type NativeChatComposerFieldProps = { isDictating: boolean isDictationHoldMode: boolean imeEnterGesture: ReturnType - onDraftChange: (value: string, element: HTMLTextAreaElement) => void - onTextareaSelect: (element: HTMLTextAreaElement) => void - onKeyDown: KeyboardEventHandler - onImeSettled: (element: HTMLTextAreaElement) => void - onPaste: ClipboardEventHandler + onDraftChange: (value: string, element: NativeChatComposerInput) => void + onTextareaSelect: (element: NativeChatComposerInput) => void + onKeyDown: KeyboardEventHandler + onImeSettled: (element: NativeChatComposerInput) => void + onPaste: ClipboardEventHandler pickerListboxId: string onChoosePickerItem: (item: NativeChatPickerItem) => void onRetrySkills: () => void @@ -151,7 +153,7 @@ export function NativeChatComposerField({ textarea.value = draft }, [draft, imeEnterGesture, textareaRef]) - const settleImeValue = (element: HTMLTextAreaElement): void => { + const settleImeValue = (element: NativeChatComposerInput): void => { if (droppedDraftClearRef.current) { droppedDraftClearRef.current = false element.value = imeComposedSegment(compositionBaseRef.current, element.value) @@ -204,38 +206,39 @@ export function NativeChatComposerField({ ))} ) : null} -