diff --git a/frontend/src/lib/components/copilot/chat/AIChat.svelte b/frontend/src/lib/components/copilot/chat/AIChat.svelte index 301a8dba54..129faa9f01 100644 --- a/frontend/src/lib/components/copilot/chat/AIChat.svelte +++ b/frontend/src/lib/components/copilot/chat/AIChat.svelte @@ -10,6 +10,7 @@ import HideButton from '$lib/components/apps/editor/settingsPanel/HideButton.svelte' import { SUPPORTED_CHAT_SCRIPT_LANGUAGES } from './script/core' import { copilotInfo } from '$lib/aiStore' + import type { ComposerSelection } from './composerBox' let { hideHeader = false, @@ -20,7 +21,8 @@ emptyHint, inputPreface, initialInstructions = undefined, - onDraftChange = undefined + onDraftChange = undefined, + placeholder = undefined }: { hideHeader?: boolean hideModeSelector?: boolean @@ -39,6 +41,8 @@ // Seed / observe the composer's draft text (forwarded to AIChatDisplay). initialInstructions?: string onDraftChange?: (text: string) => void + /** Composer placeholder for the first message. Falls back to the per-AI-mode wording. */ + placeholder?: string } = $props() const isAdmin = $derived($userStore?.is_admin || $userStore?.is_super_admin) @@ -106,8 +110,8 @@ aiChatManager.sendRequest(options) } - export function focusInput() { - aiChatDisplay?.focusInput() + export function focusInput(selection?: ComposerSelection) { + aiChatDisplay?.focusInput(selection) } const historyManager = aiChatManager.historyManager @@ -201,4 +205,5 @@ {inputPreface} {initialInstructions} {onDraftChange} + {placeholder} > diff --git a/frontend/src/lib/components/copilot/chat/AIChatDisplay.svelte b/frontend/src/lib/components/copilot/chat/AIChatDisplay.svelte index b233728a19..845126b4ef 100644 --- a/frontend/src/lib/components/copilot/chat/AIChatDisplay.svelte +++ b/frontend/src/lib/components/copilot/chat/AIChatDisplay.svelte @@ -53,6 +53,7 @@ import ToolGroupDisplay from './ToolGroupDisplay.svelte' import { chatItemKey, groupToolRuns } from './toolGroups' import AIChatInput from './AIChatInput.svelte' + import type { ComposerSelection } from './composerBox' import AttachedFilesBar from './files/AttachedFilesBar.svelte' import QueuedMessageChip from './QueuedMessageChip.svelte' import JobsSegment from './JobsSegment.svelte' @@ -357,8 +358,8 @@ chatHost.sendRequest({ instructions: suggestion }) } - export function focusInput() { - aiChatInput?.focusInput() + export function focusInput(selection?: ComposerSelection) { + aiChatInput?.focusInput(selection) } $effect(() => { diff --git a/frontend/src/lib/components/copilot/chat/AIChatInput.svelte b/frontend/src/lib/components/copilot/chat/AIChatInput.svelte index 41ea788fac..cd95da5a88 100644 --- a/frontend/src/lib/components/copilot/chat/AIChatInput.svelte +++ b/frontend/src/lib/components/copilot/chat/AIChatInput.svelte @@ -13,7 +13,12 @@ import { AIMode } from './AIChatManager.svelte' import { CHAT_INPUT_PADDING, getAiChatManager } from './aiChatManagerContext' import { getChatViewHost } from './chatViewHost' - import { composerBoxClass, COMPOSER_FIELD_RESET } from './composerBox' + import { + composerBoxClass, + COMPOSER_FIELD_RESET, + globalComposerSuggestion, + type ComposerSelection + } from './composerBox' import { formatMention, hasMention, removeMentionFromText } from './mention' import { twMerge } from 'tailwind-merge' import { tick, untrack, type Snippet } from 'svelte' @@ -119,44 +124,12 @@ pendingQuestionToolCallId = undefined }: Props = $props() - // GLOBAL-mode suggestion pool. We pick one at mount-time so each new - // session lands on a different prompt; the choice stays stable for - // the lifetime of this input so the placeholder doesn't shuffle as - // the user is reading it. - const GLOBAL_PLACEHOLDER_SUGGESTIONS = [ - 'Write a hello-world flow', - 'Create a script that lists files in an S3 bucket', - 'Build a CRUD app for a customer table', - 'Schedule a daily cleanup of old runs', - 'Wrap an existing script into a flow with retries', - 'Add an HTTP trigger to an existing script', - 'Generate a report from a SQL query and email it', - 'Create a Postgres resource and a script that queries it', - 'Refactor a script to add error handling', - 'List my workspace flows and scripts' - ] - // An operator authors nothing, so nothing above is theirs to ask for; one with the - // workspace's builder right can compose flows out of deployed scripts as well. - const OPERATOR_PLACEHOLDER_SUGGESTIONS = [ - 'List my workspace flows and scripts', - 'Show the runs that failed today and why', - 'Find the script that sends a report and run it', - 'Rerun the last failed run of a flow' - ] - const BUILDER_PLACEHOLDER_SUGGESTIONS = [ - ...OPERATOR_PLACEHOLDER_SUGGESTIONS, - 'Compose a flow that chains two deployed scripts' - ] + // Picked at mount-time so each new session lands on a different prompt. const permissions = useSessionPermissions(() => chatHost.operatingWorkspace) const suggestionPick = Math.random() - const globalSuggestion = $derived.by(() => { - const pool = permissions.canOpenEditor('script') - ? GLOBAL_PLACEHOLDER_SUGGESTIONS - : permissions.canOpenEditor('flow') - ? BUILDER_PLACEHOLDER_SUGGESTIONS - : OPERATOR_PLACEHOLDER_SUGGESTIONS - return pool[Math.floor(suggestionPick * pool.length)] - }) + const globalSuggestion = $derived( + globalComposerSuggestion(suggestionPick, permissions.canOpenEditor) + ) // Generate mode-specific placeholder const modePlaceholder = $derived.by(() => { @@ -636,11 +609,14 @@ draft.text = removeMentionFromText(draft.text, title) } - export function focusInput() { + export function focusInput(selection?: ComposerSelection) { if (isContextEnabledMode) { - contextTextareaComponent?.focus() + contextTextareaComponent?.focus(selection) } else { instructionsTextareaComponent?.focus() + if (selection) { + instructionsTextareaComponent?.setSelectionRange(selection.start, selection.end) + } } } diff --git a/frontend/src/lib/components/copilot/chat/ComposerPlaceholder.svelte b/frontend/src/lib/components/copilot/chat/ComposerPlaceholder.svelte new file mode 100644 index 0000000000..17c95086ca --- /dev/null +++ b/frontend/src/lib/components/copilot/chat/ComposerPlaceholder.svelte @@ -0,0 +1,93 @@ + + +
+
+
+ + +
+
+
+
+
diff --git a/frontend/src/lib/components/copilot/chat/ContextTextarea.svelte b/frontend/src/lib/components/copilot/chat/ContextTextarea.svelte index dd3a011da2..c80e9113df 100644 --- a/frontend/src/lib/components/copilot/chat/ContextTextarea.svelte +++ b/frontend/src/lib/components/copilot/chat/ContextTextarea.svelte @@ -1,5 +1,5 @@ + +