feat(editor): Add "Working" overlay in the middle of the canvas (no-changelog) (#17744)

This commit is contained in:
Eugene
2025-07-29 13:32:00 +02:00
committed by GitHub
parent a0cf90b7d8
commit f730df398b
7 changed files with 177 additions and 3 deletions

View File

@@ -214,6 +214,16 @@ describe('CanvasNodeAIPrompt', () => {
});
});
it('should disable suggestion pills when builder is streaming', () => {
streaming.value = true;
const { container } = renderComponent();
const pills = container.querySelectorAll('[role="group"] button');
pills.forEach((pill) => {
expect(pill).toHaveAttribute('disabled');
});
});
it('should replace prompt when suggestion is clicked', async () => {
const { container } = renderComponent();
const firstPill = container.querySelector('[role="group"] button');
@@ -311,6 +321,26 @@ describe('CanvasNodeAIPrompt', () => {
NODE_CREATOR_OPEN_SOURCES.TRIGGER_PLACEHOLDER_BUTTON,
);
});
it('should disable "Add node manually" button when builder is streaming', () => {
streaming.value = true;
const { container } = renderComponent();
const addButton = container.querySelector('[aria-label="Add node manually"]');
expect(addButton).toHaveAttribute('disabled');
});
it('should not open node creator when streaming', async () => {
streaming.value = true;
const { container } = renderComponent();
const addButton = container.querySelector('[aria-label="Add node manually"]');
if (!addButton) throw new Error('Add button not found');
await fireEvent.click(addButton);
expect(openNodeCreatorForTriggerNodes).not.toHaveBeenCalled();
});
});
describe('event propagation', () => {

View File

@@ -91,6 +91,8 @@ async function onSuggestionClick(suggestion: WorkflowSuggestion) {
* Opens the node creator for adding trigger nodes manually
*/
function onAddNodeClick() {
if (builderStore.streaming) return;
nodeCreatorStore.openNodeCreatorForTriggerNodes(
NODE_CREATOR_OPEN_SOURCES.TRIGGER_PLACEHOLDER_BUTTON,
);
@@ -145,6 +147,7 @@ function onAddNodeClick() {
v-for="suggestion in suggestions"
:key="suggestion.id"
:class="$style.suggestionPill"
:disabled="builderStore.streaming"
type="button"
@click="onSuggestionClick(suggestion)"
>
@@ -159,7 +162,12 @@ function onAddNodeClick() {
<!-- Manual node creation section -->
<section :class="$style.startManually" @click.stop="onAddNodeClick">
<button :class="$style.addButton" type="button" aria-label="Add node manually">
<button
:class="$style.addButton"
:disabled="builderStore.streaming"
type="button"
aria-label="Add node manually"
>
<n8n-icon icon="plus" :size="40" />
</button>
<div :class="$style.startManuallyLabel">
@@ -284,7 +292,7 @@ function onAddNodeClick() {
color: var(--color-text-dark);
font-weight: var(--font-weight-regular);
&:hover {
&:hover:not(:disabled) {
color: var(--color-primary);
border-color: var(--color-primary);
background: var(--color-background-xlight);

View File

@@ -111,6 +111,7 @@ import { useNDVStore } from '@/stores/ndv.store';
import { getBounds, getNodesWithNormalizedPosition, getNodeViewTab } from '@/utils/nodeViewUtils';
import CanvasStopCurrentExecutionButton from '@/components/canvas/elements/buttons/CanvasStopCurrentExecutionButton.vue';
import CanvasStopWaitingForWebhookButton from '@/components/canvas/elements/buttons/CanvasStopWaitingForWebhookButton.vue';
import CanvasThinkingPill from '@n8n/design-system/components/CanvasThinkingPill/CanvasThinkingPill.vue';
import { nodeViewEventBus } from '@/event-bus';
import { tryToParseNumber } from '@/utils/typesUtils';
import { useTemplatesStore } from '@/stores/templates.store';
@@ -291,7 +292,8 @@ const isCanvasReadOnly = computed(() => {
isDemoRoute.value ||
isReadOnlyEnvironment.value ||
!(workflowPermissions.value.update ?? projectPermissions.value.workflow.update) ||
editableWorkflow.value.isArchived
editableWorkflow.value.isArchived ||
builderStore.streaming
);
});
@@ -2130,6 +2132,8 @@ onBeforeUnmount(() => {
{{ i18n.baseText('readOnlyEnv.cantEditOrRun') }}
</N8nCallout>
<CanvasThinkingPill v-if="builderStore.streaming" :class="$style.thinkingPill" />
<Suspense>
<LazyNodeCreation
v-if="!isCanvasReadOnly"
@@ -2236,4 +2240,12 @@ onBeforeUnmount(() => {
left: 50%;
transform: translateX(-50%);
}
.thinkingPill {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 10;
}
</style>