diff --git a/packages/frontend/editor-ui/src/components/FocusPanel.vue b/packages/frontend/editor-ui/src/components/FocusPanel.vue index fff230df15..3ac7df0b5e 100644 --- a/packages/frontend/editor-ui/src/components/FocusPanel.vue +++ b/packages/frontend/editor-ui/src/components/FocusPanel.vue @@ -116,7 +116,19 @@ const isExecutable = computed(() => { const node = computed(() => resolvedParameter.value?.node); -const { hasNodeRun } = useExecutionData({ node }); +const { workflowRunData } = useExecutionData({ node }); + +const hasNodeRun = computed(() => { + if (!node.value) return true; + const parentNode = workflowsStore + .getCurrentWorkflow() + .getParentNodes(node.value.name, 'main', 1)[0]; + return Boolean( + parentNode && + workflowRunData.value && + Object.prototype.hasOwnProperty.bind(workflowRunData.value)(parentNode), + ); +}); function getTypeOption(optionName: string): T | undefined { return resolvedParameter.value diff --git a/packages/frontend/editor-ui/src/components/ParameterInputFull.vue b/packages/frontend/editor-ui/src/components/ParameterInputFull.vue index 35317874d6..a43a159395 100644 --- a/packages/frontend/editor-ui/src/components/ParameterInputFull.vue +++ b/packages/frontend/editor-ui/src/components/ParameterInputFull.vue @@ -328,6 +328,7 @@ function removeOverride(clearField = false) { :is-read-only="isReadOnly" :show-options="displayOptions" :show-expression-selector="showExpressionSelector" + :is-content-overridden="isContentOverride" @update:model-value="optionSelected" @menu-expanded="onMenuExpanded" /> @@ -390,6 +391,7 @@ function removeOverride(clearField = false) { :is-read-only="isReadOnly" :show-options="displayOptions" :show-expression-selector="showExpressionSelector" + :is-content-overridden="isContentOverride" @update:model-value="optionSelected" @menu-expanded="onMenuExpanded" /> diff --git a/packages/frontend/editor-ui/src/components/ParameterOptions.vue b/packages/frontend/editor-ui/src/components/ParameterOptions.vue index d2999b2bd4..b7703644d3 100644 --- a/packages/frontend/editor-ui/src/components/ParameterOptions.vue +++ b/packages/frontend/editor-ui/src/components/ParameterOptions.vue @@ -22,6 +22,7 @@ interface Props { iconOrientation?: 'horizontal' | 'vertical'; loading?: boolean; loadingMessage?: string; + isContentOverridden?: boolean; } const props = withDefaults(defineProps(), { @@ -31,6 +32,7 @@ const props = withDefaults(defineProps(), { iconOrientation: 'vertical', loading: false, loadingMessage: () => useI18n().baseText('genericHelpers.loading'), + isContentOverridden: false, }); const emit = defineEmits<{ @@ -60,6 +62,7 @@ const canBeOpenedInFocusPanel = computed( isFocusPanelFeatureEnabled.value && !props.parameter.isNodeSetting && !props.isReadOnly && + !props.isContentOverridden && activeNode.value && // checking that it's inside ndv (props.parameter.type === 'string' || props.parameter.type === 'json'), ); diff --git a/packages/frontend/editor-ui/src/composables/useRunWorkflow.test.ts b/packages/frontend/editor-ui/src/composables/useRunWorkflow.test.ts index d565efe4a2..ed0d6238df 100644 --- a/packages/frontend/editor-ui/src/composables/useRunWorkflow.test.ts +++ b/packages/frontend/editor-ui/src/composables/useRunWorkflow.test.ts @@ -88,6 +88,7 @@ vi.mock('@/composables/useTelemetry', () => ({ })); vi.mock('@n8n/i18n', () => ({ + i18n: { baseText: vi.fn().mockImplementation((key) => key) }, useI18n: vi.fn().mockReturnValue({ baseText: vi.fn().mockImplementation((key) => key) }), })); diff --git a/packages/frontend/editor-ui/src/stores/focusPanel.store.ts b/packages/frontend/editor-ui/src/stores/focusPanel.store.ts index d73caa2fe4..6e3dff6ebe 100644 --- a/packages/frontend/editor-ui/src/stores/focusPanel.store.ts +++ b/packages/frontend/editor-ui/src/stores/focusPanel.store.ts @@ -13,6 +13,7 @@ import { useWorkflowsStore } from './workflows.store'; import { LOCAL_STORAGE_FOCUS_PANEL, PLACEHOLDER_EMPTY_WORKFLOW_ID } from '@/constants'; import { useStorage } from '@/composables/useStorage'; import { watchOnce } from '@vueuse/core'; +import { isFromAIOverrideValue } from '@/utils/fromAIOverrideUtils'; // matches NodeCreator to ensure they fully overlap by default when both are open const DEFAULT_PANEL_WIDTH = 385; @@ -63,17 +64,25 @@ export const useFocusPanelStore = defineStore(STORES.FOCUS_PANEL, () => { const focusPanelWidth = computed(() => currentFocusPanelData.value.width ?? DEFAULT_PANEL_WIDTH); const _focusedNodeParameters = computed(() => currentFocusPanelData.value.parameters); - // An unenriched parameter indicates a missing nodeId + // An unenriched parameter indicates a missing nodeId or an otherwise unfocusable parameter const focusedNodeParameters = computed>( () => _focusedNodeParameters.value.map((x) => { const node = workflowsStore.getNodeById(x.nodeId); if (!node) return x; + const value = get(node?.parameters ?? {}, x.parameterPath.replace(/parameters\./, '')); + + // For overridden parameters we pretend they are gone + // To avoid situations where we show the raw value of a newly overridden, previously focused parameter + if (typeof value === 'string' && isFromAIOverrideValue(value)) { + return x; + } + return { ...x, node, - value: get(node?.parameters ?? {}, x.parameterPath.replace(/parameters\./, '')), + value, } satisfies RichFocusedNodeParameter; }), ); diff --git a/packages/frontend/editor-ui/src/views/NodeView.vue b/packages/frontend/editor-ui/src/views/NodeView.vue index 47e6425250..964ef677d4 100644 --- a/packages/frontend/editor-ui/src/views/NodeView.vue +++ b/packages/frontend/editor-ui/src/views/NodeView.vue @@ -2172,7 +2172,7 @@ onBeforeUnmount(() => {