mirror of
https://github.com/n8n-io/n8n.git
synced 2026-05-31 00:37:10 +02:00
fix(editor): Polish Instance AI visuals (no-changelog) (#30987)
Some checks are pending
Build: Benchmark Image / build (push) Waiting to run
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (22.22.3) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.15.0) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Util: Sync API Docs / sync-public-api (push) Waiting to run
Some checks are pending
Build: Benchmark Image / build (push) Waiting to run
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (22.22.3) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.15.0) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Util: Sync API Docs / sync-public-api (push) Waiting to run
This commit is contained in:
parent
40ecd5ea33
commit
ca949e15c5
|
|
@ -428,20 +428,52 @@ watch(
|
|||
|
||||
// --- Floating input dynamic padding ---
|
||||
const inputContainerRef = useTemplateRef<HTMLElement>('inputContainer');
|
||||
const inputSwapRef = useTemplateRef<HTMLElement>('inputSwap');
|
||||
const inputAreaHeight = ref(120);
|
||||
let resizeObserver: ResizeObserver | null = null;
|
||||
const scrollButtonBottomOffset = ref(144);
|
||||
let inputContainerResizeObserver: ResizeObserver | null = null;
|
||||
let inputSwapResizeObserver: ResizeObserver | null = null;
|
||||
|
||||
function updateScrollButtonBottomOffset() {
|
||||
const container = inputContainerRef.value;
|
||||
const inputSwap = inputSwapRef.value;
|
||||
if (!container || !inputSwap) {
|
||||
scrollButtonBottomOffset.value = inputAreaHeight.value + 24;
|
||||
return;
|
||||
}
|
||||
|
||||
const containerBottom = container.getBoundingClientRect().bottom;
|
||||
const inputSwapTop = inputSwap.getBoundingClientRect().top;
|
||||
scrollButtonBottomOffset.value = Math.max(24, containerBottom - inputSwapTop + 24);
|
||||
}
|
||||
|
||||
watch(
|
||||
inputContainerRef,
|
||||
(el) => {
|
||||
resizeObserver?.disconnect();
|
||||
inputContainerResizeObserver?.disconnect();
|
||||
if (el) {
|
||||
resizeObserver = new ResizeObserver((entries) => {
|
||||
inputContainerResizeObserver = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
inputAreaHeight.value = entry.borderBoxSize[0]?.blockSize ?? entry.contentRect.height;
|
||||
}
|
||||
updateScrollButtonBottomOffset();
|
||||
});
|
||||
resizeObserver.observe(el);
|
||||
inputContainerResizeObserver.observe(el);
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
inputSwapRef,
|
||||
(el) => {
|
||||
inputSwapResizeObserver?.disconnect();
|
||||
if (el) {
|
||||
inputSwapResizeObserver = new ResizeObserver(() => {
|
||||
updateScrollButtonBottomOffset();
|
||||
});
|
||||
inputSwapResizeObserver.observe(el);
|
||||
updateScrollButtonBottomOffset();
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
|
|
@ -483,7 +515,8 @@ onMounted(() => {
|
|||
onUnmounted(() => {
|
||||
thread.closeSSE();
|
||||
contentResizeObserver?.disconnect();
|
||||
resizeObserver?.disconnect();
|
||||
inputContainerResizeObserver?.disconnect();
|
||||
inputSwapResizeObserver?.disconnect();
|
||||
executionTracking.cleanup();
|
||||
});
|
||||
|
||||
|
|
@ -645,13 +678,15 @@ function handleWorkflowFailures(report: WorkflowFailuresReport) {
|
|||
<!-- Scroll to bottom button -->
|
||||
<div
|
||||
:class="$style.scrollButtonContainer"
|
||||
:style="{ bottom: `${inputAreaHeight + 8}px` }"
|
||||
:style="{ bottom: `${scrollButtonBottomOffset}px` }"
|
||||
>
|
||||
<Transition name="fade">
|
||||
<Transition name="scroll-button-fade">
|
||||
<N8nIconButton
|
||||
v-if="userScrolledUp && thread.hasMessages"
|
||||
variant="outline"
|
||||
icon="arrow-down"
|
||||
size="large"
|
||||
icon-size="large"
|
||||
:class="$style.scrollToBottomButton"
|
||||
@click="
|
||||
scrollToBottom(true);
|
||||
|
|
@ -677,7 +712,7 @@ function handleWorkflowFailures(report: WorkflowFailuresReport) {
|
|||
@upgrade-click="goToUpgrade('instance-ai', 'upgrade-instance-ai')"
|
||||
@dismiss="creditBanner.dismiss()"
|
||||
/>
|
||||
<div :class="$style.inputSwap">
|
||||
<div ref="inputSwap" :class="$style.inputSwap">
|
||||
<Transition name="input-swap">
|
||||
<InstanceAiConfirmationPanel
|
||||
v-if="hasFloatingConfirmation"
|
||||
|
|
@ -1002,14 +1037,34 @@ function handleWorkflowFailures(report: WorkflowFailuresReport) {
|
|||
}
|
||||
|
||||
.scrollToBottomButton {
|
||||
pointer-events: auto;
|
||||
background: var(--color--background--light-2);
|
||||
border: var(--border);
|
||||
border-radius: var(--radius);
|
||||
color: var(--color--text--tint-1);
|
||||
--button--color: var(--icon-color--strong);
|
||||
--button--color--background: var(--background--surface);
|
||||
--button--color--background-hover: var(--color--foreground--tint-2);
|
||||
--button--color--background-active: var(--color--foreground--tint-2);
|
||||
--button--shadow: var(--shadow--xs);
|
||||
--button--shadow--hover: var(--shadow--xs);
|
||||
--button--shadow--active: var(--shadow--xs);
|
||||
--button--border-color: var(--border-color);
|
||||
--button--border-color--hover: var(--border-color);
|
||||
--button--border-color--active: var(--border-color);
|
||||
--button--border--shadow: 0 0 0 1px var(--button--border-color);
|
||||
--button--border--shadow--hover: 0 0 0 1px var(--button--border-color--hover);
|
||||
--button--border--shadow--active: 0 0 0 1px var(--button--border-color--active);
|
||||
--button--radius: var(--radius--full);
|
||||
|
||||
&:hover {
|
||||
background: var(--color--foreground--tint-2);
|
||||
pointer-events: auto;
|
||||
|
||||
&.scrollToBottomButton {
|
||||
background-color: var(--background--surface);
|
||||
border: var(--border);
|
||||
border-radius: var(--radius--full);
|
||||
box-shadow: var(--shadow--xs);
|
||||
color: var(--icon-color--strong);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color--foreground--tint-2);
|
||||
box-shadow: var(--shadow--xs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1096,6 +1151,16 @@ function handleWorkflowFailures(report: WorkflowFailuresReport) {
|
|||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.scroll-button-fade-enter-from,
|
||||
.scroll-button-fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.scroll-button-fade-enter-active,
|
||||
.scroll-button-fade-leave-active {
|
||||
transition: opacity 0.12s ease;
|
||||
}
|
||||
|
||||
.preview-panel-slide-enter-active,
|
||||
.preview-panel-slide-leave-active {
|
||||
--preview-panel-slide-easing: var(--easing--ease-in-out);
|
||||
|
|
|
|||
|
|
@ -109,6 +109,11 @@ function resolveArtifactName(artifact: ArtifactInfo): string {
|
|||
|
||||
<style lang="scss" module>
|
||||
.reasoningTrigger {
|
||||
--button--padding: 0;
|
||||
--button--font-size: var(--font-size--sm);
|
||||
|
||||
padding-inline: 0;
|
||||
font-size: var(--font-size--sm);
|
||||
color: var(--color--text--tint-2);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
<script lang="ts" setup>
|
||||
import type { InstanceAiAgentNode } from '@n8n/api-types';
|
||||
import { N8nCallout, N8nIcon } from '@n8n/design-system';
|
||||
import { N8nCallout } from '@n8n/design-system';
|
||||
import { CollapsibleRoot, CollapsibleTrigger } from 'reka-ui';
|
||||
import AnimatedCollapsibleContent from './AnimatedCollapsibleContent.vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import SubagentStepTimeline from './SubagentStepTimeline.vue';
|
||||
import TimelineStepChevron from './TimelineStepChevron.vue';
|
||||
import TimelineStepButton from './TimelineStepButton.vue';
|
||||
import { useSettingsStore } from '@/app/stores/settings.store';
|
||||
|
||||
|
|
@ -51,7 +52,7 @@ watch(
|
|||
<CollapsibleTrigger as-child>
|
||||
<TimelineStepButton :loading="isActive" size="medium">
|
||||
<template #icon>
|
||||
<N8nIcon :icon="isOpen ? 'chevron-down' : 'chevron-right'" size="small" />
|
||||
<TimelineStepChevron :open="isOpen" />
|
||||
</template>
|
||||
{{ sectionTitle }}
|
||||
</TimelineStepButton>
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ function getAnswers(): DisplayAnswer[] {
|
|||
}
|
||||
|
||||
.userBubble {
|
||||
background: var(--color--background);
|
||||
background: var(--assistant--color--background--user-bubble);
|
||||
padding: var(--spacing--xs) var(--spacing--sm) var(--spacing--sm);
|
||||
border-radius: var(--radius--xl);
|
||||
white-space: pre-wrap;
|
||||
|
|
@ -92,7 +92,7 @@ function getAnswers(): DisplayAnswer[] {
|
|||
}
|
||||
|
||||
.skipped {
|
||||
color: var(--color--text--tint-2);
|
||||
color: var(--text-color);
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ function onKeydown(event: KeyboardEvent) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<ConfirmationFooter layout="column">
|
||||
<ConfirmationFooter layout="column" :class="$style.footer">
|
||||
<div
|
||||
ref="container"
|
||||
:class="$style.list"
|
||||
|
|
@ -89,16 +89,16 @@ function onKeydown(event: KeyboardEvent) {
|
|||
@click="emit('select', option.key)"
|
||||
@mouseenter="highlightedIndex = idx"
|
||||
>
|
||||
<N8nIcon :class="$style.leadingIcon" :icon="option.icon" size="small" />
|
||||
<N8nIcon :class="$style.leadingIcon" :icon="option.icon" size="large" />
|
||||
<span :class="$style.label">
|
||||
<span :class="option.suffix ? $style.labelStrong : undefined">{{ option.label }}</span>
|
||||
<span :class="$style.labelStrong">{{ option.label }}</span>
|
||||
<span v-if="option.suffix" :class="$style.labelMuted">{{ option.suffix }}</span>
|
||||
</span>
|
||||
<N8nIcon
|
||||
v-if="option.withArrow !== false"
|
||||
:class="$style.trailingIcon"
|
||||
icon="arrow-right"
|
||||
size="small"
|
||||
size="large"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -112,20 +112,24 @@ function onKeydown(event: KeyboardEvent) {
|
|||
outline: none;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing--2xs);
|
||||
width: 100%;
|
||||
padding: var(--spacing--3xs) var(--spacing--2xs);
|
||||
min-height: 36px;
|
||||
padding: var(--spacing--3xs) var(--spacing--2xs) var(--spacing--3xs) var(--spacing--xs);
|
||||
border: none;
|
||||
border-radius: var(--radius--lg);
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
font-size: var(--font-size--sm);
|
||||
color: var(--color--text);
|
||||
transition: background-color 0.15s ease;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
// Highlight: applied when the row is the current selection (keyboard or
|
||||
|
|
@ -135,24 +139,29 @@ function onKeydown(event: KeyboardEvent) {
|
|||
background-color: light-dark(var(--color--neutral-100), var(--color--neutral-800));
|
||||
|
||||
.trailingIcon {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
// Destructive variant only changes the highlight colour, so the cost of
|
||||
// confirming becomes obvious the moment the user lands on the row.
|
||||
.rowDestructive.highlighted {
|
||||
background-color: var(--callout--color--background--danger);
|
||||
color: var(--callout--color--text--danger);
|
||||
background-color: light-dark(var(--color--red-100), var(--callout--color--background--danger));
|
||||
color: light-dark(var(--color--red-800), var(--color--red-250));
|
||||
|
||||
.leadingIcon {
|
||||
color: var(--callout--color--text--danger);
|
||||
color: light-dark(var(--color--red-800), var(--color--red-250));
|
||||
}
|
||||
|
||||
.trailingIcon {
|
||||
color: light-dark(var(--color--red-800), var(--color--red-250));
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.leadingIcon {
|
||||
flex-shrink: 0;
|
||||
color: var(--color--text--tint-1);
|
||||
color: var(--icon-color--strong);
|
||||
}
|
||||
|
||||
.label {
|
||||
|
|
@ -165,19 +174,19 @@ function onKeydown(event: KeyboardEvent) {
|
|||
}
|
||||
|
||||
.labelStrong {
|
||||
font-weight: var(--font-weight--bold);
|
||||
font-weight: var(--font-weight--medium);
|
||||
}
|
||||
|
||||
.labelMuted {
|
||||
color: var(--color--text--tint-1);
|
||||
color: var(--text-color--subtle);
|
||||
font-weight: var(--font-weight--regular);
|
||||
}
|
||||
|
||||
.trailingIcon {
|
||||
margin-left: auto;
|
||||
opacity: 0;
|
||||
color: var(--color--text--tint-1);
|
||||
visibility: hidden;
|
||||
color: var(--icon--color);
|
||||
opacity: 0.7;
|
||||
flex-shrink: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@
|
|||
font-size: var(--font-size--sm);
|
||||
color: var(--color--text);
|
||||
word-break: break-all;
|
||||
padding: var(--spacing--2xs);
|
||||
padding: var(--spacing--2xs) 0;
|
||||
border-radius: var(--radius);
|
||||
border: var(--border);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -9,11 +9,15 @@
|
|||
font-size: var(--font-size--sm);
|
||||
color: var(--color--text--tint-2);
|
||||
background: var(--color--foreground--tint-2);
|
||||
border-radius: var(--radius);
|
||||
border-radius: var(--radius--xs);
|
||||
padding: var(--spacing--2xs);
|
||||
margin-top: var(--spacing--2xs);
|
||||
border: var(--border);
|
||||
max-width: 90%;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: light-dark(var(--color--neutral-300), var(--color--neutral-700)) transparent;
|
||||
|
||||
:global(pre) {
|
||||
background: transparent;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
<script lang="ts" setup>
|
||||
import { N8nBadge, N8nCard, N8nIcon, N8nText } from '@n8n/design-system';
|
||||
import { N8nBadge, N8nCard, N8nText } from '@n8n/design-system';
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import { CollapsibleRoot, CollapsibleTrigger } from 'reka-ui';
|
||||
import AnimatedCollapsibleContent from './AnimatedCollapsibleContent.vue';
|
||||
import { computed } from 'vue';
|
||||
import { useToolLabel } from '../toolLabels';
|
||||
import TimelineStepChevron from './TimelineStepChevron.vue';
|
||||
import TimelineStepButton from './TimelineStepButton.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
|
|
@ -36,7 +37,7 @@ const briefing = computed(() => {
|
|||
<CollapsibleTrigger as-child>
|
||||
<TimelineStepButton :loading="props.isLoading" size="medium">
|
||||
<template #icon>
|
||||
<N8nIcon :icon="isOpen ? 'chevron-down' : 'chevron-right'" size="small" />
|
||||
<TimelineStepChevron :open="isOpen" />
|
||||
</template>
|
||||
{{ i18n.baseText('instanceAi.delegateCard.delegatingTo') }}:
|
||||
<N8nText bold>{{ role }}</N8nText>
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ function buildApprovalOptions(item: PendingConfirmationItem): ApprovalOption[] {
|
|||
if (!destructive) {
|
||||
options.push({
|
||||
key: 'always-allow',
|
||||
icon: 'check',
|
||||
icon: 'check-check',
|
||||
label: i18n.baseText('instanceAi.confirmation.alwaysAllow'),
|
||||
suffix: i18n.baseText('instanceAi.confirmation.alwaysAllowSuffix'),
|
||||
testId: 'instance-ai-panel-confirm-always-allow',
|
||||
|
|
@ -185,7 +185,6 @@ function buildApprovalOptions(item: PendingConfirmationItem): ApprovalOption[] {
|
|||
key: 'deny',
|
||||
icon: 'ban',
|
||||
label: i18n.baseText('instanceAi.confirmation.deny'),
|
||||
withArrow: false,
|
||||
testId: 'instance-ai-panel-confirm-deny',
|
||||
});
|
||||
return options;
|
||||
|
|
@ -563,7 +562,7 @@ function handlePlanRequestChanges(
|
|||
<div v-else>
|
||||
<div :class="$style.approvalRow">
|
||||
<div :class="$style.approvalRowBody">
|
||||
<N8nText size="medium" bold>
|
||||
<N8nText size="large" bold>
|
||||
{{ buildApprovalTitle(chunk.item) }}
|
||||
</N8nText>
|
||||
<ConfirmationPreview>{{ buildApprovalSubtitle(chunk.item) }}</ConfirmationPreview>
|
||||
|
|
@ -589,9 +588,12 @@ function handlePlanRequestChanges(
|
|||
}
|
||||
|
||||
.root {
|
||||
border: var(--border);
|
||||
border-radius: var(--radius--lg);
|
||||
background-color: var(--color--background--light-3);
|
||||
border: none;
|
||||
border-radius: var(--radius--xl);
|
||||
box-shadow:
|
||||
var(--shadow--xs),
|
||||
inset 0 0 0 1px light-dark(var(--color--black-alpha-100), var(--color--white-alpha-100));
|
||||
background-color: var(--background--surface);
|
||||
}
|
||||
|
||||
.floatingRoot {
|
||||
|
|
@ -615,12 +617,12 @@ function handlePlanRequestChanges(
|
|||
.approvalRow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: var(--spacing--4xs) 0;
|
||||
gap: var(--spacing--2xs);
|
||||
padding: var(--spacing--sm);
|
||||
font-size: var(--font-size--2xs);
|
||||
}
|
||||
|
||||
.approvalRowBody {
|
||||
padding: var(--spacing--sm) var(--spacing--sm) 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing--2xs);
|
||||
|
|
|
|||
|
|
@ -183,6 +183,7 @@ function formatJson(value: unknown): string {
|
|||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
margin-block: var(--spacing--md);
|
||||
}
|
||||
|
||||
.userAttachments {
|
||||
|
|
@ -193,7 +194,7 @@ function formatJson(value: unknown): string {
|
|||
}
|
||||
|
||||
.userBubble {
|
||||
background: var(--color--background);
|
||||
background: var(--assistant--color--background--user-bubble);
|
||||
padding: var(--spacing--xs) var(--spacing--sm);
|
||||
border-radius: var(--radius--xl);
|
||||
white-space: pre-wrap;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="ts" setup>
|
||||
import type { InstanceAiAgentNode } from '@n8n/api-types';
|
||||
import { N8nIcon } from '@n8n/design-system';
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import { CollapsibleRoot, CollapsibleTrigger } from 'reka-ui';
|
||||
import AnimatedCollapsibleContent from './AnimatedCollapsibleContent.vue';
|
||||
|
|
@ -8,6 +7,7 @@ import { computed } from 'vue';
|
|||
import type { ResponseGroupSegment } from '../useTimelineGrouping';
|
||||
import AgentTimeline from './AgentTimeline.vue';
|
||||
import TimelineStepButton from './TimelineStepButton.vue';
|
||||
import TimelineStepChevron from './TimelineStepChevron.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
group: ResponseGroupSegment;
|
||||
|
|
@ -89,7 +89,7 @@ const isCollapsible = computed(
|
|||
<CollapsibleTrigger as-child>
|
||||
<TimelineStepButton size="medium">
|
||||
<template #icon>
|
||||
<N8nIcon :icon="isOpen ? 'chevron-down' : 'chevron-right'" size="small" />
|
||||
<TimelineStepChevron :open="isOpen" />
|
||||
</template>
|
||||
{{ summaryText }}
|
||||
</TimelineStepButton>
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ const steps = computed((): TimelineStep[] => {
|
|||
<template v-else-if="step.type === 'text'">
|
||||
<CollapsibleRoot v-if="step.isLongText" v-slot="{ open }">
|
||||
<CollapsibleTrigger as-child>
|
||||
<N8nButton ref="triggerRef" variant="ghost" size="small">
|
||||
<N8nButton ref="triggerRef" variant="ghost" size="small" :class="$style.toggleTrigger">
|
||||
<template #icon>
|
||||
<template v-if="step.isLoading">
|
||||
<N8nIcon icon="spinner" size="small" color="primary" spin />
|
||||
|
|
@ -166,6 +166,14 @@ const steps = computed((): TimelineStep[] => {
|
|||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.toggleTrigger {
|
||||
--button--padding: 0;
|
||||
--button--font-size: var(--font-size--sm);
|
||||
|
||||
padding-inline: 0;
|
||||
font-size: var(--font-size--sm);
|
||||
}
|
||||
|
||||
.streamingMarkdown {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
|
|
|
|||
|
|
@ -38,8 +38,12 @@ defineSlots<{
|
|||
max-width: 90%;
|
||||
justify-content: flex-start;
|
||||
color: var(--color--text--tint-1);
|
||||
font-size: var(--font-size--sm);
|
||||
position: relative;
|
||||
padding-inline: 0;
|
||||
|
||||
--button--padding: 0;
|
||||
--button--font-size: var(--font-size--sm);
|
||||
--button--color--background-active: transparent;
|
||||
--button--color--background-hover: transparent;
|
||||
&:hover {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
<script lang="ts" setup>
|
||||
import { N8nIcon } from '@n8n/design-system';
|
||||
|
||||
defineProps<{
|
||||
open: boolean;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<N8nIcon icon="chevron-right" size="large" :class="[$style.chevron, open && $style.open]" />
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.chevron {
|
||||
transition: transform var(--duration--snappy) var(--easing--ease-out);
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.open {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.chevron {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
<script lang="ts" setup>
|
||||
import type { InstanceAiToolCallState } from '@n8n/api-types';
|
||||
import { N8nCallout, N8nIcon } from '@n8n/design-system';
|
||||
import { N8nCallout } from '@n8n/design-system';
|
||||
import { CollapsibleRoot, CollapsibleTrigger } from 'reka-ui';
|
||||
import AnimatedCollapsibleContent from './AnimatedCollapsibleContent.vue';
|
||||
import { useToolLabel } from '../toolLabels';
|
||||
import DataSection from './DataSection.vue';
|
||||
import TimelineStepChevron from './TimelineStepChevron.vue';
|
||||
import TimelineStepButton from './TimelineStepButton.vue';
|
||||
import ToolResultJson from './ToolResultJson.vue';
|
||||
import ToolResultRenderer from './ToolResultRenderer.vue';
|
||||
|
|
@ -50,7 +51,7 @@ function getDisplayLabel(tc: InstanceAiToolCallState): string {
|
|||
<CollapsibleTrigger as-child>
|
||||
<TimelineStepButton :loading="props.toolCall.isLoading">
|
||||
<template #icon>
|
||||
<N8nIcon :icon="isOpen ? 'chevron-down' : 'chevron-right'" size="small" />
|
||||
<TimelineStepChevron :open="isOpen" />
|
||||
</template>
|
||||
{{ props.label ?? getDisplayLabel(props.toolCall) }}
|
||||
</TimelineStepButton>
|
||||
|
|
|
|||
|
|
@ -100,13 +100,11 @@ function downloadFullJson() {
|
|||
<style lang="scss" module>
|
||||
.json {
|
||||
font-family: monospace;
|
||||
font-size: var(--font-size--sm);
|
||||
font-size: var(--font-size--xs);
|
||||
line-height: var(--line-height--xl);
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
margin: 0;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
color: var(--color--text--tint-1);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user