diff --git a/packages/frontend/editor-ui/src/components/FocusPanel.vue b/packages/frontend/editor-ui/src/components/FocusPanel.vue
index 6f545343259..9bd1f833b0e 100644
--- a/packages/frontend/editor-ui/src/components/FocusPanel.vue
+++ b/packages/frontend/editor-ui/src/components/FocusPanel.vue
@@ -4,29 +4,54 @@ import { N8nText, N8nInput } from '@n8n/design-system';
import { computed } from 'vue';
import { useI18n } from '@n8n/i18n';
import { isValueExpression } from '@/utils/nodeTypesUtils';
+import { useNodeSettingsParameters } from '@/composables/useNodeSettingsParameters';
+import { useWorkflowsStore } from '@/stores/workflows.store';
+import { useNodeTypesStore } from '@/stores/nodeTypes.store';
defineOptions({ name: 'FocusPanel' });
const locale = useI18n();
const focusPanelStore = useFocusPanelStore();
+const nodeTypesStore = useNodeTypesStore();
+const nodeSettingsParameters = useNodeSettingsParameters();
const focusedNodeParameter = computed(() => focusPanelStore.focusedNodeParameters[0]);
+const resolvedParameter = computed(() =>
+ focusPanelStore.isRichParameter(focusedNodeParameter.value)
+ ? focusedNodeParameter.value
+ : undefined,
+);
const focusPanelActive = computed(() => focusPanelStore.focusPanelActive);
+const isCodeBlock = computed(() => resolvedParameter.value?.parameterPath === 'parameters.jsCode');
+
const expressionModeEnabled = computed(
() =>
- focusedNodeParameter.value &&
- isValueExpression(focusedNodeParameter.value.parameter, focusedNodeParameter.value.value),
+ resolvedParameter.value &&
+ isValueExpression(resolvedParameter.value.parameter, resolvedParameter.value.value),
+);
+
+const isToolNode = computed(() =>
+ resolvedParameter.value ? nodeTypesStore.isToolNode(resolvedParameter.value?.node.type) : false,
);
function optionSelected() {
// TODO: Handle the option selected (command: string) from the dropdown
}
-function valueChanged() {
- // TODO: Update parameter value
+function valueChanged(value: string) {
+ if (resolvedParameter.value === undefined) {
+ return;
+ }
+
+ nodeSettingsParameters.updateNodeParameter(
+ { value, name: `parameters.${focusedNodeParameter.value.parameter.name}` },
+ value,
+ resolvedParameter.value.node,
+ isToolNode.value,
+ );
}
function executeFocusedNode() {
@@ -44,13 +69,13 @@ function executeFocusedNode() {
-
+
{{
- focusedNodeParameter.parameter.displayName
+ resolvedParameter.parameter.displayName
}}
- {{ focusedNodeParameter.nodeName }}
+ {{ resolvedParameter.node.name }}
-
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/utils.ts b/packages/frontend/editor-ui/src/components/Node/NodeCreator/utils.ts
index a362b12014b..bc1dcab1c54 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/utils.ts
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/utils.ts
@@ -121,6 +121,7 @@ export function searchNodes(searchFilter: string, items: INodeCreateElement[]) {
}
const trimmedFilter = removeTrailingTrigger(searchFilter).toLowerCase();
+ debugger;
// We have a snapshot of this call in sublimeSearch.test.ts to assert practical order for some cases
// Please update the snapshots per the README next to the the snapshots if you modify items significantly.
diff --git a/packages/frontend/editor-ui/src/components/ParameterInput.vue b/packages/frontend/editor-ui/src/components/ParameterInput.vue
index b92e9313eea..52ad88e9ea3 100644
--- a/packages/frontend/editor-ui/src/components/ParameterInput.vue
+++ b/packages/frontend/editor-ui/src/components/ParameterInput.vue
@@ -1048,10 +1048,9 @@ async function optionSelected(command: string) {
if (node.value && command === 'focus') {
focusPanelStore.setFocusedNodeParameter({
- nodeName: node.value.name,
+ nodeId: node.value.id,
parameterPath: props.path,
parameter: props.parameter,
- value: modelValueString.value,
});
if (ndvStore.activeNode) {
diff --git a/packages/frontend/editor-ui/src/components/ParameterInputList.vue b/packages/frontend/editor-ui/src/components/ParameterInputList.vue
index f4aae2b43e3..1060f3195aa 100644
--- a/packages/frontend/editor-ui/src/components/ParameterInputList.vue
+++ b/packages/frontend/editor-ui/src/components/ParameterInputList.vue
@@ -6,7 +6,7 @@ import type {
NodeParameterValue,
NodeParameterValueType,
} from 'n8n-workflow';
-import { ADD_FORM_NOTICE, deepCopy, NodeHelpers } from 'n8n-workflow';
+import { ADD_FORM_NOTICE, deepCopy, getParameterValueByPath, NodeHelpers } from 'n8n-workflow';
import { computed, defineAsyncComponent, onErrorCaptured, ref, watch, type WatchSource } from 'vue';
import type { IUpdateInformation } from '@/Interface';
@@ -538,7 +538,7 @@ function getDependentParametersValues(parameter: INodeProperties): string | null
function getParameterValue
(
name: string,
): T {
- return nodeHelpers.getParameterValue(props.nodeValues, name, props.path) as T;
+ return getParameterValueByPath(props.nodeValues, name, props.path) as T;
}
function isRagStarterCallout(parameter: INodeProperties): boolean {
diff --git a/packages/frontend/editor-ui/src/components/ParameterOptions.vue b/packages/frontend/editor-ui/src/components/ParameterOptions.vue
index 416fb9e40ea..8955811785c 100644
--- a/packages/frontend/editor-ui/src/components/ParameterOptions.vue
+++ b/packages/frontend/editor-ui/src/components/ParameterOptions.vue
@@ -61,7 +61,7 @@ const shouldShowOptions = computed(() => {
}
if (['codeNodeEditor', 'sqlEditor'].includes(props.parameter.typeOptions?.editor ?? '')) {
- return false;
+ // return false;
}
if (props.showOptions) {
diff --git a/packages/frontend/editor-ui/src/composables/useNodeHelpers.ts b/packages/frontend/editor-ui/src/composables/useNodeHelpers.ts
index 163e42523f4..f26b1c3b546 100644
--- a/packages/frontend/editor-ui/src/composables/useNodeHelpers.ts
+++ b/packages/frontend/editor-ui/src/composables/useNodeHelpers.ts
@@ -41,7 +41,6 @@ import { isObject } from '@/utils/objectUtils';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { useCredentialsStore } from '@/stores/credentials.store';
-import get from 'lodash/get';
import { useI18n } from '@n8n/i18n';
import { EnableNodeToggleCommand } from '@/models/history';
import { useTelemetry } from './useTelemetry';
@@ -92,10 +91,6 @@ export function useNodeHelpers() {
return false;
}
- function getParameterValue(nodeValues: INodeParameters, parameterName: string, path: string) {
- return get(nodeValues, path ? path + '.' + parameterName : parameterName);
- }
-
// Returns if the given parameter should be displayed or not
function displayParameter(
nodeValues: INodeParameters,
@@ -1005,7 +1000,6 @@ export function useNodeHelpers() {
return {
hasProxyAuth,
isCustomApiCallSelected,
- getParameterValue,
displayParameter,
getNodeIssues,
updateNodesInputIssues,
diff --git a/packages/frontend/editor-ui/src/stores/focusPanel.store.ts b/packages/frontend/editor-ui/src/stores/focusPanel.store.ts
index 5ce64c1f94d..87ebc227ca6 100644
--- a/packages/frontend/editor-ui/src/stores/focusPanel.store.ts
+++ b/packages/frontend/editor-ui/src/stores/focusPanel.store.ts
@@ -1,22 +1,48 @@
import { STORES } from '@n8n/stores';
import { defineStore } from 'pinia';
-import { ref } from 'vue';
+import { computed, ref } from 'vue';
-import type { INodeProperties } from 'n8n-workflow';
+import {
+ getParameterValueByPath,
+ type NodeParameterValueType,
+ type INode,
+ type INodeProperties,
+} from 'n8n-workflow';
+import { useWorkflowsStore } from './workflows.store';
type FocusedNodeParameter = {
- nodeName: string;
+ nodeId: string;
parameter: INodeProperties;
parameterPath: string;
- value: string;
+};
+
+export type RichFocusedNodeParameter = FocusedNodeParameter & {
+ node: INode;
+ value: NodeParameterValueType;
};
export const useFocusPanelStore = defineStore(STORES.FOCUS_PANEL, () => {
+ const workflowsStore = useWorkflowsStore();
+
const focusPanelActive = ref(false);
- const focusedNodeParameters = ref([]);
+ const _focusedNodeParameters = ref([]);
+
+ // An unenriched parameter indicates a missing nodeId
+ const focusedNodeParameters = computed>(
+ () =>
+ _focusedNodeParameters.value.map((x) => {
+ const node = workflowsStore.getNodeById(x.nodeId);
+ if (!node) return x;
+ return {
+ ...x,
+ node,
+ value: getParameterValueByPath(node?.parameters ?? {}, x.parameter.name, x.parameterPath),
+ } satisfies RichFocusedNodeParameter;
+ }),
+ );
const setFocusedNodeParameter = (nodeParameter: FocusedNodeParameter) => {
- focusedNodeParameters.value = [
+ _focusedNodeParameters.value = [
nodeParameter,
// Uncomment when tabs are implemented
// ...focusedNodeParameters.value.filter((p) => p.parameterPath !== nodeParameter.parameterPath),
@@ -31,10 +57,17 @@ export const useFocusPanelStore = defineStore(STORES.FOCUS_PANEL, () => {
focusPanelActive.value = !focusPanelActive.value;
};
+ function isRichParameter(
+ p: RichFocusedNodeParameter | FocusedNodeParameter,
+ ): p is RichFocusedNodeParameter {
+ return 'value' in p && 'node' in p;
+ }
+
return {
focusPanelActive,
focusedNodeParameters,
setFocusedNodeParameter,
+ isRichParameter,
closeFocusPanel,
toggleFocusPanel,
};
diff --git a/packages/workflow/src/node-helpers.ts b/packages/workflow/src/node-helpers.ts
index 0fc0f59cf3c..d6f0250c42b 100644
--- a/packages/workflow/src/node-helpers.ts
+++ b/packages/workflow/src/node-helpers.ts
@@ -1758,3 +1758,11 @@ export function getSubworkflowId(node: INode): string | undefined {
}
return;
}
+
+export function getParameterValue(
+ nodeValues: INodeParameters,
+ parameterName: string,
+ path: string,
+) {
+ return get(nodeValues, path ? path + '.' + parameterName : parameterName);
+}