mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 08:01:35 +00:00
feat: relocate context picker, refine chat input + flow card radii
- Move the @ context-picker Popover from above the textarea (in
AIChatInput) to the bottom-left of the chat input controls row in
AIChatDisplay, immediately to the left of the Flow mode selector.
AIChatInput now exports addContextToSelection so the parent can drive
the same validated add path. The badges-only row above the textarea
only renders when there is at least one selected context (no empty
bar). AIChatInput drops its now-unused Popover and AvailableContextList
imports; AIChatDisplay picks them up plus AppAvailableContextList for
the APP-mode list.
- Replace the raw send/stop button with a Button variant="subtle"
iconOnly, keeping the rounded-full 28x28 hit area via btnClasses, the
ArrowUp/Square icon swap, and the disabled-when-empty / cancel-when-
loading behavior.
- Shrink the textarea: rows={3} -> rows={2} on all three textareas
(ContextTextarea, the APP-mode textarea, the default-mode textarea)
and bump min-height down from 3rem to 1.75rem so the textarea starts
as a single visible line and grows with content via autosize.
- Refine chat input box radius to rounded-xl, match the textarea via
border-radius: 0.75rem, and add pt-1 above the textarea for a touch
of breathing room from the card's top edge.
- /design-sandbox: switch the right pane's flow editor card from
rounded-xl to rounded-md so the flow card sits tighter while the chat
input keeps the softer rounded-xl.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,9 @@
|
||||
import ProviderModelSelector from './ProviderModelSelector.svelte'
|
||||
import ChatMode from './ChatMode.svelte'
|
||||
import DatatableCreationPolicy from './DatatableCreationPolicy.svelte'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import AvailableContextList from './AvailableContextList.svelte'
|
||||
import AppAvailableContextList from './AppAvailableContextList.svelte'
|
||||
import Markdown from 'svelte-exmarkdown'
|
||||
import { aiChatManager, AIMode } from './AIChatManager.svelte'
|
||||
import AIChatInput from './AIChatInput.svelte'
|
||||
@@ -173,9 +176,9 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
class="rounded-2xl border border-light bg-surface shadow-sm transition-all focus-within:shadow-md focus-within:border-gray-300 dark:focus-within:border-gray-600"
|
||||
class="rounded-xl border border-light bg-surface shadow-sm transition-all focus-within:shadow-md focus-within:border-gray-300 dark:focus-within:border-gray-600"
|
||||
>
|
||||
<div class="px-1 pt-0.5">
|
||||
<div class="px-1 pt-1">
|
||||
<AIChatInput
|
||||
bind:this={aiChatInput}
|
||||
bind:selectedContext
|
||||
@@ -194,6 +197,43 @@
|
||||
<Markdown md={disabledMessage} />
|
||||
</div>
|
||||
{:else}
|
||||
{#if aiChatManager.mode === AIMode.SCRIPT || aiChatManager.mode === AIMode.FLOW || aiChatManager.mode === AIMode.APP}
|
||||
<Popover>
|
||||
{#snippet trigger()}
|
||||
<div
|
||||
class="border rounded-md px-1 py-0.5 font-normal text-primary text-xs hover:bg-surface-hover bg-surface"
|
||||
title="Add context"
|
||||
>
|
||||
@
|
||||
</div>
|
||||
{/snippet}
|
||||
{#snippet content({ close })}
|
||||
{#if aiChatManager.mode === AIMode.APP}
|
||||
<AppAvailableContextList
|
||||
{availableContext}
|
||||
{selectedContext}
|
||||
onSelect={(element) => {
|
||||
aiChatInput?.addContextToSelection(element)
|
||||
close()
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<AvailableContextList
|
||||
{availableContext}
|
||||
{selectedContext}
|
||||
onSelect={(element) => {
|
||||
aiChatInput?.addContextToSelection(element)
|
||||
close()
|
||||
}}
|
||||
onSelectWorkspaceItem={(element) => {
|
||||
aiChatInput?.addContextToSelection(element)
|
||||
close()
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</Popover>
|
||||
{/if}
|
||||
<ChatMode />
|
||||
{#if aiChatManager.mode === AIMode.APP}
|
||||
<DatatableCreationPolicy />
|
||||
@@ -278,26 +318,22 @@
|
||||
</div>
|
||||
|
||||
{#if !disabled}
|
||||
<button
|
||||
type="button"
|
||||
aria-label={aiChatManager.loading ? 'Stop' : 'Send'}
|
||||
title={aiChatManager.loading ? 'Stop' : 'Send'}
|
||||
class="shrink-0 inline-flex items-center justify-center w-7 h-7 rounded-full bg-gray-900 dark:bg-gray-100 text-white dark:text-gray-900 hover:bg-gray-700 dark:hover:bg-gray-300 disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
|
||||
<Button
|
||||
variant="subtle"
|
||||
size="xs"
|
||||
iconOnly
|
||||
startIcon={{ icon: aiChatManager.loading ? Square : ArrowUp }}
|
||||
disabled={!aiChatManager.loading && !aiChatInput?.hasContent()}
|
||||
onclick={() => {
|
||||
title={aiChatManager.loading ? 'Stop' : 'Send'}
|
||||
btnClasses="!rounded-full !w-7 !h-7"
|
||||
on:click={() => {
|
||||
if (aiChatManager.loading) {
|
||||
cancel()
|
||||
} else {
|
||||
aiChatInput?.triggerSend()
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if aiChatManager.loading}
|
||||
<Square class="w-3 h-3" />
|
||||
{:else}
|
||||
<ArrowUp class="w-3.5 h-3.5" strokeWidth={2.5} />
|
||||
{/if}
|
||||
</button>
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<script lang="ts">
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import AvailableContextList from './AvailableContextList.svelte'
|
||||
import AppAvailableContextList from './AppAvailableContextList.svelte'
|
||||
import ContextElementBadge from './ContextElementBadge.svelte'
|
||||
import ContextTextarea from './ContextTextarea.svelte'
|
||||
@@ -109,7 +107,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
function addContextToSelection(contextElement: ContextElement) {
|
||||
export function addContextToSelection(contextElement: ContextElement) {
|
||||
if (!selectedContext || !availableContext) return
|
||||
|
||||
const alreadySelected = selectedContext.find(
|
||||
@@ -371,30 +369,8 @@
|
||||
|
||||
<div use:clickOutside class="relative">
|
||||
{#if aiChatManager.mode === AIMode.SCRIPT || aiChatManager.mode === AIMode.FLOW}
|
||||
{#if showContext}
|
||||
{#if showContext && selectedContext && selectedContext.length > 0}
|
||||
<div class="flex flex-row gap-1 mb-1 overflow-scroll pt-2 no-scrollbar">
|
||||
<Popover>
|
||||
{#snippet trigger()}
|
||||
<div
|
||||
class="border rounded-md px-1 py-0.5 font-normal text-primary text-xs hover:bg-surface-hover bg-surface"
|
||||
>@</div
|
||||
>
|
||||
{/snippet}
|
||||
{#snippet content({ close })}
|
||||
<AvailableContextList
|
||||
{availableContext}
|
||||
{selectedContext}
|
||||
onSelect={(element) => {
|
||||
addContextToSelection(element)
|
||||
close()
|
||||
}}
|
||||
onSelectWorkspaceItem={(element) => {
|
||||
addContextToSelection(element)
|
||||
close()
|
||||
}}
|
||||
/>
|
||||
{/snippet}
|
||||
</Popover>
|
||||
{#each selectedContext as element}
|
||||
<ContextElementBadge
|
||||
contextElement={element}
|
||||
@@ -426,26 +402,8 @@
|
||||
{onKeyDown}
|
||||
/>
|
||||
{:else if aiChatManager.mode === AIMode.APP}
|
||||
{#if showContext}
|
||||
{#if showContext && selectedContext && selectedContext.length > 0}
|
||||
<div class="flex flex-row gap-1 mb-1 overflow-scroll pt-2 no-scrollbar">
|
||||
<Popover>
|
||||
{#snippet trigger()}
|
||||
<div
|
||||
class="border rounded-md px-1 py-0.5 font-normal text-primary text-xs hover:bg-surface-hover bg-surface"
|
||||
>@</div
|
||||
>
|
||||
{/snippet}
|
||||
{#snippet content({ close })}
|
||||
<AppAvailableContextList
|
||||
{availableContext}
|
||||
{selectedContext}
|
||||
onSelect={(element) => {
|
||||
addContextToSelection(element)
|
||||
close()
|
||||
}}
|
||||
/>
|
||||
{/snippet}
|
||||
</Popover>
|
||||
{#each selectedContext as element (element.type + '-' + element.title)}
|
||||
<ContextElementBadge
|
||||
contextElement={element}
|
||||
@@ -486,7 +444,7 @@
|
||||
sendRequest()
|
||||
}
|
||||
}}
|
||||
rows={3}
|
||||
rows={2}
|
||||
placeholder={modePlaceholder}
|
||||
class="resize-none"
|
||||
{disabled}
|
||||
@@ -532,7 +490,7 @@
|
||||
sendRequest()
|
||||
}
|
||||
}}
|
||||
rows={3}
|
||||
rows={2}
|
||||
placeholder={modePlaceholder}
|
||||
class="resize-none"
|
||||
{disabled}
|
||||
|
||||
@@ -315,7 +315,7 @@
|
||||
onkeydown={handleKeyDown}
|
||||
bind:value
|
||||
use:autosize
|
||||
rows={3}
|
||||
rows={2}
|
||||
oninput={handleInput}
|
||||
onblur={() => {
|
||||
setTimeout(() => {
|
||||
@@ -373,12 +373,13 @@
|
||||
.textarea-input {
|
||||
padding: 0.25rem;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.75rem; /* matches rounded-xl */
|
||||
font-family: inherit;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.72;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-words;
|
||||
width: 100%;
|
||||
min-height: 3rem;
|
||||
min-height: 1.75rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -235,7 +235,7 @@
|
||||
than being its own root view. -->
|
||||
<Pane size={50} minSize={30} class="flex flex-col min-h-0 p-2 pl-0">
|
||||
<div
|
||||
class="flex-1 min-h-0 rounded-xl border border-light bg-surface overflow-hidden shadow-sm"
|
||||
class="flex-1 min-h-0 rounded-md border border-light bg-surface overflow-hidden shadow-sm"
|
||||
>
|
||||
<FlowWrapper
|
||||
disableAi
|
||||
|
||||
Reference in New Issue
Block a user