n8n-editor-ui
Version:
Workflow Editor UI for n8n
2,403 lines • 85.2 kB
JavaScript
import { o as __toESM } from "./chunk-6z4oVpB-.js";
import { C as computed, Cn as toDisplayString, Ct as withScopeId, D as createElementBlock, E as createCommentVNode, G as nextTick, Gt as unref, I as guardReactiveProps, It as ref, L as h, Lt as shallowReactive, M as createVNode, Mt as markRaw, P as defineComponent, T as createBlock, W as mergeProps, _ as Fragment, _t as watch, at as renderSlot, bn as normalizeStyle, bt as withCtx, ct as resolveDynamicComponent, et as openBlock, h as withModifiers, it as renderList, j as createTextVNode, l as useCssVars, lt as toHandlers, ot as resolveComponent, pt as useSlots, rt as pushScopeId, st as resolveDirective, tt as popScopeId, vn as normalizeClass, w as createBaseVNode, xt as withDirectives, yn as normalizeProps } from "./vue.runtime.esm-bundler-tP5dCd7J.js";
import { _t as useI18n, h as computedAsync, vt as I18nT } from "./_MapCache-ficiegRb.js";
import { Sn as N8nButton_default, ht as N8nTooltip_default, mt as N8nLink_default, wn as N8nIcon_default, xn as N8nText_default } from "./src-DgvumQTi.js";
import { t as __plugin_vue_export_helper_default } from "./_plugin-vue_export-helper-BwBpWJRZ.js";
import { y as useRouter } from "./truncate-B1HVeveJ.js";
import { $i as nodeViewEventBus, Gn as useNodeHelpers, Kn as injectWorkflowState, M as useNDVStore, N as useWorkflowsStore, Nt as useNodeTypesStore, O as useTelemetry, Sn as useFlattenSchema, Ui as isEmpty, Un as environments_store_default, b as useExternalHooks, fi as executionDataToJson, gs as useSettingsStore, m as useMessage, oa as useUIStore, v as useToast, vn as generateCodeForPrompt, xn as useDataSchema } from "./builder.store-sBTWwxRU.js";
import { Ha as DateTime, Ja as AI_TRANSFORM_JS_CODE, N as PLACEHOLDER_FILLED_AT_EXECUTION_TIME, S as SCHEMA_PREVIEW_DOCS_URL, Tn as MANUAL_TRIGGER_NODE_TYPE, Ua as NodeConnectionTypes, Xo as FROM_AI_PARAMETERS_MODAL_KEY, Ya as AI_TRANSFORM_NODE_TYPE, ai as require_pick, bc as EnterpriseEditionFeature, hr as WEBHOOK_NODE_TYPE, qa as AI_TRANSFORM_CODE_GENERATED_FOR_PROMPT, rs as MODAL_CONFIRM, un as FORM_TRIGGER_NODE_TYPE, wi as createResultError, xo as ApplicationError, zt as CHAT_TRIGGER_NODE_TYPE } from "./constants-BbpucWL4.js";
import { r as useRootStore } from "./_baseOrderBy-B_6CV8x6.js";
import { t as useRunWorkflow } from "./useRunWorkflow-BQJbxiOd.js";
import { t as usePinnedData } from "./usePinnedData-CL2qWhwp.js";
import { t as NodeIcon_default } from "./NodeIcon-BVhrZNYI.js";
import { i as Ln, o as gu, r as estree_exports, t as NDVEmptyState_default } from "./NDVEmptyState-CpUQ-969.js";
import { t as Draggable_default } from "./Draggable-04Tfmp_H.js";
import { t as TextWithHighlights_default } from "./TextWithHighlights-DjzuDrgO.js";
import { n as MappingPill_default, t as useTelemetryContext } from "./useTelemetryContext-5Wb7vBCv.js";
import { t as useSchemaPreviewStore } from "./schemaPreview.store-BFprwUEe.js";
import { a as needsAgentInput } from "./nodeTransforms-Lk_uXh7p.js";
var _hoisted_1$3 = { class: "schema-header-wrapper" };
var _hoisted_2$2 = {
class: "schema-header",
"data-test-id": "run-data-schema-header"
};
var _hoisted_3 = { class: "title" };
var _hoisted_4 = {
key: 0,
class: "info"
};
var _hoisted_5 = {
key: 2,
class: "extra-info",
"data-test-id": "run-data-schema-node-item-count"
};
var _hoisted_6 = {
key: 3,
class: "extra-info"
};
var VirtualSchemaHeader_default = /* @__PURE__ */ __plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
__name: "VirtualSchemaHeader",
props: {
title: {},
collapsable: { type: Boolean },
collapsed: { type: Boolean },
itemCount: {},
info: {},
nodeType: {},
preview: { type: Boolean },
lastSuccessfulPreview: { type: Boolean }
},
emits: ["click:toggle", "execute"],
setup(__props, { emit: __emit }) {
const props$1 = __props;
const i18n = useI18n();
const isTrigger = computed(() => Boolean(props$1.nodeType?.group.includes("trigger")));
const emit = __emit;
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", _hoisted_1$3, [createBaseVNode("div", _hoisted_2$2, [
createBaseVNode("div", {
class: "toggle",
onClickCapture: _cache[0] || (_cache[0] = withModifiers(($event) => emit("click:toggle"), ["stop"]))
}, [createVNode(unref(N8nIcon_default), {
size: "medium",
icon: "chevron-down",
class: normalizeClass({
"collapse-icon": true,
collapsed: _ctx.collapsed
})
}, null, 8, ["class"])], 32),
_ctx.nodeType ? (openBlock(), createBlock(NodeIcon_default, {
key: 0,
class: normalizeClass(["icon", { "icon-trigger": isTrigger.value }]),
"node-type": _ctx.nodeType,
size: 12
}, null, 8, ["class", "node-type"])) : createCommentVNode("", true),
createBaseVNode("div", _hoisted_3, [createTextVNode(toDisplayString(_ctx.title) + " ", 1), _ctx.info ? (openBlock(), createElementBlock("span", _hoisted_4, toDisplayString(_ctx.info), 1)) : createCommentVNode("", true)]),
isTrigger.value ? (openBlock(), createBlock(unref(N8nIcon_default), {
key: 1,
class: "trigger-icon",
icon: "bolt-filled",
size: "xsmall"
})) : createCommentVNode("", true),
_ctx.itemCount && !_ctx.lastSuccessfulPreview ? (openBlock(), createElementBlock("div", _hoisted_5, toDisplayString(unref(i18n).baseText("ndv.output.items", { interpolate: { count: _ctx.itemCount } })), 1)) : _ctx.preview ? (openBlock(), createElementBlock("div", _hoisted_6, toDisplayString(unref(i18n).baseText("dataMapping.schemaView.previewNode")), 1)) : createCommentVNode("", true)
]), _ctx.preview && !_ctx.collapsed ? (openBlock(), createElementBlock("div", {
key: 0,
class: "notice",
"data-test-id": "schema-preview-warning",
onClick: _cache[3] || (_cache[3] = withModifiers(() => {}, ["stop"]))
}, [_ctx.lastSuccessfulPreview ? (openBlock(), createBlock(unref(I18nT), {
key: 0,
keypath: "dataMapping.schemaView.previewLastExecution",
scope: "global"
}, {
execute: withCtx(() => [createVNode(unref(N8nLink_default), {
onClick: _cache[1] || (_cache[1] = () => emit("execute", _ctx.title)),
size: "small",
bold: ""
}, {
default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("dataMapping.schemaView.previewLastExecution.executePreviousNodes")), 1)]),
_: 1
})]),
_: 1
})) : (openBlock(), createBlock(unref(I18nT), {
key: 1,
keypath: "dataMapping.schemaView.preview",
scope: "global"
}, {
execute: withCtx(() => [createVNode(unref(N8nLink_default), {
onClick: _cache[2] || (_cache[2] = () => emit("execute", _ctx.title)),
size: "small",
bold: ""
}, {
default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("dataMapping.schemaView.preview.executeNode")), 1)]),
_: 1
})]),
link: withCtx(() => [createVNode(unref(N8nLink_default), {
to: unref(SCHEMA_PREVIEW_DOCS_URL),
size: "small",
bold: ""
}, {
default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("generic.learnMore")), 1)]),
_: 1
}, 8, ["to"])]),
_: 1
}))])) : createCommentVNode("", true)]);
};
}
}), [["__scopeId", "data-v-81c3a2d5"]]);
var _hoisted_1$2 = { class: "toggle-container" };
var _hoisted_2$1 = [
"data-name",
"data-path",
"data-depth",
"data-nest-level",
"data-value",
"data-node-type",
"data-target",
"data-node-name",
"data-run-index"
];
var VirtualSchemaItem_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "VirtualSchemaItem",
props: {
title: {},
path: {},
level: {},
depth: {},
expression: {},
value: {},
id: {},
icon: {},
collapsable: { type: Boolean },
nodeName: {},
nodeType: {},
highlight: { type: Boolean },
draggable: { type: Boolean },
collapsed: { type: Boolean },
search: {},
preview: { type: Boolean },
locked: { type: Boolean },
lockedTooltip: {},
runIndex: {}
},
emits: ["click"],
setup(__props, { emit: __emit }) {
useCssVars((_ctx) => ({ "8de62a40": _ctx.level }));
const props$1 = __props;
const emit = __emit;
return (_ctx, _cache) => {
const _directive_n8n_html = resolveDirective("n8n-html");
return openBlock(), createElementBlock("div", {
class: normalizeClass(["schema-item", { draggable: _ctx.draggable }]),
"data-test-id": "run-data-schema-item"
}, [
createBaseVNode("div", _hoisted_1$2, [_ctx.collapsable ? (openBlock(), createElementBlock("div", {
key: 0,
class: "toggle",
onClick: _cache[0] || (_cache[0] = ($event) => emit("click"))
}, [createVNode(unref(N8nIcon_default), {
icon: "chevron-down",
class: normalizeClass({
"collapse-icon": true,
collapsed: _ctx.collapsed
})
}, null, 8, ["class"])])) : createCommentVNode("", true)]),
_ctx.title ? (openBlock(), createElementBlock("div", {
key: 0,
"data-name": _ctx.title,
"data-path": _ctx.path,
"data-depth": _ctx.depth,
"data-nest-level": _ctx.level,
"data-value": _ctx.expression,
"data-node-type": _ctx.nodeType,
"data-target": !_ctx.locked && "mappable",
"data-node-name": _ctx.nodeName,
"data-run-index": _ctx.runIndex,
class: normalizeClass(["pill", {
"pill--highlight": _ctx.highlight,
"pill--preview": _ctx.preview,
"pill--locked": _ctx.locked
}]),
"data-test-id": "run-data-schema-node-name"
}, [createVNode(unref(N8nIcon_default), {
class: "type-icon",
icon: _ctx.icon,
size: "small"
}, null, 8, ["icon"]), createVNode(TextWithHighlights_default, {
class: "title",
content: _ctx.title,
search: props$1.search
}, null, 8, ["content", "search"])], 10, _hoisted_2$1)) : createCommentVNode("", true),
_ctx.locked ? (openBlock(), createBlock(unref(N8nTooltip_default), {
key: 1,
disabled: !_ctx.lockedTooltip,
"popper-class": _ctx.$style.tooltip
}, {
content: withCtx(() => [withDirectives(createBaseVNode("span", null, null, 512), [[_directive_n8n_html, _ctx.lockedTooltip]])]),
default: withCtx(() => [createVNode(unref(N8nIcon_default), {
class: "locked-icon",
icon: "lock",
size: "small"
})]),
_: 1
}, 8, ["disabled", "popper-class"])) : createCommentVNode("", true),
createVNode(TextWithHighlights_default, {
"data-test-id": "run-data-schema-item-value",
class: "text",
content: _ctx.value,
search: props$1.search
}, null, 8, ["content", "search"])
], 2);
};
}
});
var VirtualSchemaItem_vue_vue_type_style_index_1_lang_module_default = { tooltip: "_tooltip_1e5yq_2" };
var VirtualSchemaItem_default = /* @__PURE__ */ __plugin_vue_export_helper_default(VirtualSchemaItem_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": VirtualSchemaItem_vue_vue_type_style_index_1_lang_module_default }], ["__scopeId", "data-v-6a0fee4a"]]);
function getInternetExplorerVersion() {
var ua = window.navigator.userAgent;
var msie = ua.indexOf("MSIE ");
if (msie > 0) return parseInt(ua.substring(msie + 5, ua.indexOf(".", msie)), 10);
if (ua.indexOf("Trident/") > 0) {
var rv = ua.indexOf("rv:");
return parseInt(ua.substring(rv + 3, ua.indexOf(".", rv)), 10);
}
var edge = ua.indexOf("Edge/");
if (edge > 0) return parseInt(ua.substring(edge + 5, ua.indexOf(".", edge)), 10);
return -1;
}
var isIE;
function initCompat() {
if (!initCompat.init) {
initCompat.init = true;
isIE = getInternetExplorerVersion() !== -1;
}
}
var script$2 = {
name: "ResizeObserver",
props: {
emitOnMount: {
type: Boolean,
default: false
},
ignoreWidth: {
type: Boolean,
default: false
},
ignoreHeight: {
type: Boolean,
default: false
}
},
emits: ["notify"],
mounted() {
initCompat();
nextTick(() => {
this._w = this.$el.offsetWidth;
this._h = this.$el.offsetHeight;
if (this.emitOnMount) this.emitSize();
});
const object = document.createElement("object");
this._resizeObject = object;
object.setAttribute("aria-hidden", "true");
object.setAttribute("tabindex", -1);
object.onload = this.addResizeHandlers;
object.type = "text/html";
if (isIE) this.$el.appendChild(object);
object.data = "about:blank";
if (!isIE) this.$el.appendChild(object);
},
beforeUnmount() {
this.removeResizeHandlers();
},
methods: {
compareAndNotify() {
if (!this.ignoreWidth && this._w !== this.$el.offsetWidth || !this.ignoreHeight && this._h !== this.$el.offsetHeight) {
this._w = this.$el.offsetWidth;
this._h = this.$el.offsetHeight;
this.emitSize();
}
},
emitSize() {
this.$emit("notify", {
width: this._w,
height: this._h
});
},
addResizeHandlers() {
this._resizeObject.contentDocument.defaultView.addEventListener("resize", this.compareAndNotify);
this.compareAndNotify();
},
removeResizeHandlers() {
if (this._resizeObject && this._resizeObject.onload) {
if (!isIE && this._resizeObject.contentDocument) this._resizeObject.contentDocument.defaultView.removeEventListener("resize", this.compareAndNotify);
this.$el.removeChild(this._resizeObject);
this._resizeObject.onload = null;
this._resizeObject = null;
}
}
}
};
var _withId = /* @__PURE__ */ withScopeId("data-v-b329ee4c");
pushScopeId("data-v-b329ee4c");
var _hoisted_1$1 = {
class: "resize-observer",
tabindex: "-1"
};
popScopeId();
script$2.render = /* @__PURE__ */ _withId((_ctx, _cache, $props, $setup, $data, $options) => {
return openBlock(), createBlock("div", _hoisted_1$1);
});
script$2.__scopeId = "data-v-b329ee4c";
script$2.__file = "src/components/ResizeObserver.vue";
function _typeof$1(obj) {
"@babel/helpers - typeof";
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") _typeof$1 = function(obj$1) {
return typeof obj$1;
};
else _typeof$1 = function(obj$1) {
return obj$1 && typeof Symbol === "function" && obj$1.constructor === Symbol && obj$1 !== Symbol.prototype ? "symbol" : typeof obj$1;
};
return _typeof$1(obj);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
}
function _defineProperties(target, props$1) {
for (var i = 0; i < props$1.length; i++) {
var descriptor = props$1[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
}
function _iterableToArray(iter) {
if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function processOptions(value) {
var options;
if (typeof value === "function") options = { callback: value };
else options = value;
return options;
}
function throttle(callback, delay) {
var options = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {};
var timeout;
var lastState;
var currentArgs;
var throttled = function throttled$1(state) {
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) args[_key - 1] = arguments[_key];
currentArgs = args;
if (timeout && state === lastState) return;
var leading = options.leading;
if (typeof leading === "function") leading = leading(state, lastState);
if ((!timeout || state !== lastState) && leading) callback.apply(void 0, [state].concat(_toConsumableArray(currentArgs)));
lastState = state;
clearTimeout(timeout);
timeout = setTimeout(function() {
callback.apply(void 0, [state].concat(_toConsumableArray(currentArgs)));
timeout = 0;
}, delay);
};
throttled._clear = function() {
clearTimeout(timeout);
timeout = null;
};
return throttled;
}
function deepEqual(val1, val2) {
if (val1 === val2) return true;
if (_typeof$1(val1) === "object") {
for (var key in val1) if (!deepEqual(val1[key], val2[key])) return false;
return true;
}
return false;
}
var VisibilityState = /* @__PURE__ */ function() {
function VisibilityState$1(el, options, vnode) {
_classCallCheck(this, VisibilityState$1);
this.el = el;
this.observer = null;
this.frozen = false;
this.createObserver(options, vnode);
}
_createClass(VisibilityState$1, [
{
key: "createObserver",
value: function createObserver(options, vnode) {
var _this = this;
if (this.observer) this.destroyObserver();
if (this.frozen) return;
this.options = processOptions(options);
this.callback = function(result, entry) {
_this.options.callback(result, entry);
if (result && _this.options.once) {
_this.frozen = true;
_this.destroyObserver();
}
};
if (this.callback && this.options.throttle) {
var _leading = (this.options.throttleOptions || {}).leading;
this.callback = throttle(this.callback, this.options.throttle, { leading: function leading(state) {
return _leading === "both" || _leading === "visible" && state || _leading === "hidden" && !state;
} });
}
this.oldResult = void 0;
this.observer = new IntersectionObserver(function(entries) {
var entry = entries[0];
if (entries.length > 1) {
var intersectingEntry = entries.find(function(e) {
return e.isIntersecting;
});
if (intersectingEntry) entry = intersectingEntry;
}
if (_this.callback) {
var result = entry.isIntersecting && entry.intersectionRatio >= _this.threshold;
if (result === _this.oldResult) return;
_this.oldResult = result;
_this.callback(result, entry);
}
}, this.options.intersection);
nextTick(function() {
if (_this.observer) _this.observer.observe(_this.el);
});
}
},
{
key: "destroyObserver",
value: function destroyObserver() {
if (this.observer) {
this.observer.disconnect();
this.observer = null;
}
if (this.callback && this.callback._clear) {
this.callback._clear();
this.callback = null;
}
}
},
{
key: "threshold",
get: function get() {
return this.options.intersection && typeof this.options.intersection.threshold === "number" ? this.options.intersection.threshold : 0;
}
}
]);
return VisibilityState$1;
}();
function beforeMount(el, _ref2, vnode) {
var value = _ref2.value;
if (!value) return;
if (typeof IntersectionObserver === "undefined") console.warn("[vue-observe-visibility] IntersectionObserver API is not available in your browser. Please install this polyfill: https://github.com/w3c/IntersectionObserver/tree/master/polyfill");
else el._vue_visibilityState = new VisibilityState(el, value, vnode);
}
function updated(el, _ref3, vnode) {
var value = _ref3.value, oldValue = _ref3.oldValue;
if (deepEqual(value, oldValue)) return;
var state = el._vue_visibilityState;
if (!value) {
unmounted(el);
return;
}
if (state) state.createObserver(value, vnode);
else beforeMount(el, { value }, vnode);
}
function unmounted(el) {
var state = el._vue_visibilityState;
if (state) {
state.destroyObserver();
delete el._vue_visibilityState;
}
}
var ObserveVisibility = {
beforeMount,
updated,
unmounted
};
function mitt_es_default(n) {
return {
all: n = n || /* @__PURE__ */ new Map(),
on: function(t, e) {
var i = n.get(t);
i && i.push(e) || n.set(t, [e]);
},
off: function(t, e) {
var i = n.get(t);
i && i.splice(i.indexOf(e) >>> 0, 1);
},
emit: function(t, e) {
(n.get(t) || []).slice().map(function(n$1) {
n$1(e);
}), (n.get("*") || []).slice().map(function(n$1) {
n$1(t, e);
});
}
};
}
var config = { itemsLimit: 1e3 };
var regex = /(auto|scroll)/;
function parents(node, ps) {
if (node.parentNode === null) return ps;
return parents(node.parentNode, ps.concat([node]));
}
var style = function style$1(node, prop) {
return getComputedStyle(node, null).getPropertyValue(prop);
};
var overflow = function overflow$1(node) {
return style(node, "overflow") + style(node, "overflow-y") + style(node, "overflow-x");
};
var scroll = function scroll$1(node) {
return regex.test(overflow(node));
};
function getScrollParent(node) {
if (!(node instanceof HTMLElement || node instanceof SVGElement)) return;
var ps = parents(node.parentNode, []);
for (var i = 0; i < ps.length; i += 1) if (scroll(ps[i])) return ps[i];
return document.scrollingElement || document.documentElement;
}
function _typeof(obj) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(obj$1) {
return typeof obj$1;
} : function(obj$1) {
return obj$1 && "function" == typeof Symbol && obj$1.constructor === Symbol && obj$1 !== Symbol.prototype ? "symbol" : typeof obj$1;
}, _typeof(obj);
}
var props = {
items: {
type: Array,
required: true
},
keyField: {
type: String,
default: "id"
},
direction: {
type: String,
default: "vertical",
validator: function validator(value) {
return ["vertical", "horizontal"].includes(value);
}
},
listTag: {
type: String,
default: "div"
},
itemTag: {
type: String,
default: "div"
}
};
function simpleArray() {
return this.items.length && _typeof(this.items[0]) !== "object";
}
var supportsPassive = false;
if (typeof window !== "undefined") {
supportsPassive = false;
try {
var opts = Object.defineProperty({}, "passive", { get: function get() {
supportsPassive = true;
} });
window.addEventListener("test", null, opts);
} catch (e) {}
}
var uid = 0;
var script$2$1 = {
name: "RecycleScroller",
components: { ResizeObserver: script$2 },
directives: { ObserveVisibility },
props: {
...props,
itemSize: {
type: Number,
default: null
},
gridItems: {
type: Number,
default: void 0
},
itemSecondarySize: {
type: Number,
default: void 0
},
minItemSize: {
type: [Number, String],
default: null
},
sizeField: {
type: String,
default: "size"
},
typeField: {
type: String,
default: "type"
},
buffer: {
type: Number,
default: 200
},
pageMode: {
type: Boolean,
default: false
},
prerender: {
type: Number,
default: 0
},
emitUpdate: {
type: Boolean,
default: false
},
updateInterval: {
type: Number,
default: 0
},
skipHover: {
type: Boolean,
default: false
},
listTag: {
type: String,
default: "div"
},
itemTag: {
type: String,
default: "div"
},
listClass: {
type: [
String,
Object,
Array
],
default: ""
},
itemClass: {
type: [
String,
Object,
Array
],
default: ""
}
},
emits: [
"resize",
"visible",
"hidden",
"update",
"scroll-start",
"scroll-end"
],
data() {
return {
pool: [],
totalSize: 0,
ready: false,
hoverKey: null
};
},
computed: {
sizes() {
if (this.itemSize === null) {
const sizes = { "-1": { accumulator: 0 } };
const items = this.items;
const field = this.sizeField;
const minItemSize = this.minItemSize;
let computedMinSize = 1e4;
let accumulator = 0;
let current;
for (let i = 0, l = items.length; i < l; i++) {
current = items[i][field] || minItemSize;
if (current < computedMinSize) computedMinSize = current;
accumulator += current;
sizes[i] = {
accumulator,
size: current
};
}
this.$_computedMinItemSize = computedMinSize;
return sizes;
}
return [];
},
simpleArray,
itemIndexByKey() {
const { keyField, items } = this;
const result = {};
for (let i = 0, l = items.length; i < l; i++) result[items[i][keyField]] = i;
return result;
}
},
watch: {
items() {
this.updateVisibleItems(true);
},
pageMode() {
this.applyPageMode();
this.updateVisibleItems(false);
},
sizes: {
handler() {
this.updateVisibleItems(false);
},
deep: true
},
gridItems() {
this.updateVisibleItems(true);
},
itemSecondarySize() {
this.updateVisibleItems(true);
}
},
created() {
this.$_startIndex = 0;
this.$_endIndex = 0;
this.$_views = /* @__PURE__ */ new Map();
this.$_unusedViews = /* @__PURE__ */ new Map();
this.$_scrollDirty = false;
this.$_lastUpdateScrollPosition = 0;
if (this.prerender) {
this.$_prerender = true;
this.updateVisibleItems(false);
}
if (this.gridItems && !this.itemSize) console.error("[vue-recycle-scroller] You must provide an itemSize when using gridItems");
},
mounted() {
this.applyPageMode();
this.$nextTick(() => {
this.$_prerender = false;
this.updateVisibleItems(true);
this.ready = true;
});
},
activated() {
const lastPosition = this.$_lastUpdateScrollPosition;
if (typeof lastPosition === "number") this.$nextTick(() => {
this.scrollToPosition(lastPosition);
});
},
beforeUnmount() {
this.removeListeners();
},
methods: {
addView(pool, index, item, key, type) {
const view = shallowReactive({
item,
position: 0,
nr: markRaw({
id: uid++,
index,
used: true,
key,
type
})
});
pool.push(view);
return view;
},
unuseView(view, fake = false) {
const unusedViews = this.$_unusedViews;
const type = view.nr.type;
let unusedPool = unusedViews.get(type);
if (!unusedPool) {
unusedPool = [];
unusedViews.set(type, unusedPool);
}
unusedPool.push(view);
if (!fake) {
view.nr.used = false;
view.position = -9999;
}
},
handleResize() {
this.$emit("resize");
if (this.ready) this.updateVisibleItems(false);
},
handleScroll(event) {
if (!this.$_scrollDirty) {
this.$_scrollDirty = true;
if (this.$_updateTimeout) return;
const requestUpdate = () => requestAnimationFrame(() => {
this.$_scrollDirty = false;
const { continuous } = this.updateVisibleItems(false, true);
if (!continuous) {
clearTimeout(this.$_refreshTimout);
this.$_refreshTimout = setTimeout(this.handleScroll, this.updateInterval + 100);
}
});
requestUpdate();
if (this.updateInterval) this.$_updateTimeout = setTimeout(() => {
this.$_updateTimeout = 0;
if (this.$_scrollDirty) requestUpdate();
}, this.updateInterval);
}
},
handleVisibilityChange(isVisible, entry) {
if (this.ready) if (isVisible || entry.boundingClientRect.width !== 0 || entry.boundingClientRect.height !== 0) {
this.$emit("visible");
requestAnimationFrame(() => {
this.updateVisibleItems(false);
});
} else this.$emit("hidden");
},
updateVisibleItems(checkItem, checkPositionDiff = false) {
const itemSize = this.itemSize;
const gridItems = this.gridItems || 1;
const itemSecondarySize = this.itemSecondarySize || itemSize;
const minItemSize = this.$_computedMinItemSize;
const typeField = this.typeField;
const keyField = this.simpleArray ? null : this.keyField;
const items = this.items;
const count = items.length;
const sizes = this.sizes;
const views = this.$_views;
const unusedViews = this.$_unusedViews;
const pool = this.pool;
const itemIndexByKey = this.itemIndexByKey;
let startIndex, endIndex;
let totalSize;
let visibleStartIndex, visibleEndIndex;
if (!count) startIndex = endIndex = visibleStartIndex = visibleEndIndex = totalSize = 0;
else if (this.$_prerender) {
startIndex = visibleStartIndex = 0;
endIndex = visibleEndIndex = Math.min(this.prerender, items.length);
totalSize = null;
} else {
const scroll$1 = this.getScroll();
if (checkPositionDiff) {
let positionDiff = scroll$1.start - this.$_lastUpdateScrollPosition;
if (positionDiff < 0) positionDiff = -positionDiff;
if (itemSize === null && positionDiff < minItemSize || positionDiff < itemSize) return { continuous: true };
}
this.$_lastUpdateScrollPosition = scroll$1.start;
const buffer = this.buffer;
scroll$1.start -= buffer;
scroll$1.end += buffer;
let beforeSize = 0;
if (this.$refs.before) {
beforeSize = this.$refs.before.scrollHeight;
scroll$1.start -= beforeSize;
}
if (this.$refs.after) {
const afterSize = this.$refs.after.scrollHeight;
scroll$1.end += afterSize;
}
if (itemSize === null) {
let h$1;
let a = 0;
let b = count - 1;
let i = ~~(count / 2);
let oldI;
do {
oldI = i;
h$1 = sizes[i].accumulator;
if (h$1 < scroll$1.start) a = i;
else if (i < count - 1 && sizes[i + 1].accumulator > scroll$1.start) b = i;
i = ~~((a + b) / 2);
} while (i !== oldI);
i < 0 && (i = 0);
startIndex = i;
totalSize = sizes[count - 1].accumulator;
for (endIndex = i; endIndex < count && sizes[endIndex].accumulator < scroll$1.end; endIndex++);
if (endIndex === -1) endIndex = items.length - 1;
else {
endIndex++;
endIndex > count && (endIndex = count);
}
for (visibleStartIndex = startIndex; visibleStartIndex < count && beforeSize + sizes[visibleStartIndex].accumulator < scroll$1.start; visibleStartIndex++);
for (visibleEndIndex = visibleStartIndex; visibleEndIndex < count && beforeSize + sizes[visibleEndIndex].accumulator < scroll$1.end; visibleEndIndex++);
} else {
startIndex = ~~(scroll$1.start / itemSize * gridItems);
const remainer = startIndex % gridItems;
startIndex -= remainer;
endIndex = Math.ceil(scroll$1.end / itemSize * gridItems);
visibleStartIndex = Math.max(0, Math.floor((scroll$1.start - beforeSize) / itemSize * gridItems));
visibleEndIndex = Math.floor((scroll$1.end - beforeSize) / itemSize * gridItems);
startIndex < 0 && (startIndex = 0);
endIndex > count && (endIndex = count);
visibleStartIndex < 0 && (visibleStartIndex = 0);
visibleEndIndex > count && (visibleEndIndex = count);
totalSize = Math.ceil(count / gridItems) * itemSize;
}
}
if (endIndex - startIndex > config.itemsLimit) this.itemsLimitError();
this.totalSize = totalSize;
let view;
const continuous = startIndex <= this.$_endIndex && endIndex >= this.$_startIndex;
if (continuous) for (let i = 0, l = pool.length; i < l; i++) {
view = pool[i];
if (view.nr.used) {
if (checkItem) view.nr.index = itemIndexByKey[view.item[keyField]];
if (view.nr.index == null || view.nr.index < startIndex || view.nr.index >= endIndex) this.unuseView(view);
}
}
const unusedIndex = continuous ? null : /* @__PURE__ */ new Map();
let item, type;
let v;
for (let i = startIndex; i < endIndex; i++) {
item = items[i];
const key = keyField ? item[keyField] : item;
if (key == null) throw new Error(`Key is ${key} on item (keyField is '${keyField}')`);
view = views.get(key);
if (!itemSize && !sizes[i].size) {
if (view) this.unuseView(view);
continue;
}
type = item[typeField];
let unusedPool = unusedViews.get(type);
let newlyUsedView = false;
if (!view) {
if (continuous) if (unusedPool && unusedPool.length) view = unusedPool.pop();
else view = this.addView(pool, i, item, key, type);
else {
v = unusedIndex.get(type) || 0;
if (!unusedPool || v >= unusedPool.length) {
view = this.addView(pool, i, item, key, type);
this.unuseView(view, true);
unusedPool = unusedViews.get(type);
}
view = unusedPool[v];
unusedIndex.set(type, v + 1);
}
views.delete(view.nr.key);
view.nr.used = true;
view.nr.index = i;
view.nr.key = key;
view.nr.type = type;
views.set(key, view);
newlyUsedView = true;
} else if (!view.nr.used) {
view.nr.used = true;
newlyUsedView = true;
if (unusedPool) {
const index = unusedPool.indexOf(view);
if (index !== -1) unusedPool.splice(index, 1);
}
}
view.item = item;
if (newlyUsedView) {
if (i === items.length - 1) this.$emit("scroll-end");
if (i === 0) this.$emit("scroll-start");
}
if (itemSize === null) {
view.position = sizes[i - 1].accumulator;
view.offset = 0;
} else {
view.position = Math.floor(i / gridItems) * itemSize;
view.offset = i % gridItems * itemSecondarySize;
}
}
this.$_startIndex = startIndex;
this.$_endIndex = endIndex;
if (this.emitUpdate) this.$emit("update", startIndex, endIndex, visibleStartIndex, visibleEndIndex);
clearTimeout(this.$_sortTimer);
this.$_sortTimer = setTimeout(this.sortViews, this.updateInterval + 300);
return { continuous };
},
getListenerTarget() {
let target = getScrollParent(this.$el);
if (window.document && (target === window.document.documentElement || target === window.document.body)) target = window;
return target;
},
getScroll() {
const { $el: el, direction } = this;
const isVertical = direction === "vertical";
let scrollState;
if (this.pageMode) {
const bounds = el.getBoundingClientRect();
const boundsSize = isVertical ? bounds.height : bounds.width;
let start = -(isVertical ? bounds.top : bounds.left);
let size = isVertical ? window.innerHeight : window.innerWidth;
if (start < 0) {
size += start;
start = 0;
}
if (start + size > boundsSize) size = boundsSize - start;
scrollState = {
start,
end: start + size
};
} else if (isVertical) scrollState = {
start: el.scrollTop,
end: el.scrollTop + el.clientHeight
};
else scrollState = {
start: el.scrollLeft,
end: el.scrollLeft + el.clientWidth
};
return scrollState;
},
applyPageMode() {
if (this.pageMode) this.addListeners();
else this.removeListeners();
},
addListeners() {
this.listenerTarget = this.getListenerTarget();
this.listenerTarget.addEventListener("scroll", this.handleScroll, supportsPassive ? { passive: true } : false);
this.listenerTarget.addEventListener("resize", this.handleResize);
},
removeListeners() {
if (!this.listenerTarget) return;
this.listenerTarget.removeEventListener("scroll", this.handleScroll);
this.listenerTarget.removeEventListener("resize", this.handleResize);
this.listenerTarget = null;
},
scrollToItem(index) {
let scroll$1;
const gridItems = this.gridItems || 1;
if (this.itemSize === null) scroll$1 = index > 0 ? this.sizes[index - 1].accumulator : 0;
else scroll$1 = Math.floor(index / gridItems) * this.itemSize;
this.scrollToPosition(scroll$1);
},
scrollToPosition(position) {
const direction = this.direction === "vertical" ? {
scroll: "scrollTop",
start: "top"
} : {
scroll: "scrollLeft",
start: "left"
};
let viewport;
let scrollDirection;
let scrollDistance;
if (this.pageMode) {
const viewportEl = getScrollParent(this.$el);
const scrollTop = viewportEl.tagName === "HTML" ? 0 : viewportEl[direction.scroll];
const bounds = viewportEl.getBoundingClientRect();
const scrollerPosition = this.$el.getBoundingClientRect()[direction.start] - bounds[direction.start];
viewport = viewportEl;
scrollDirection = direction.scroll;
scrollDistance = position + scrollTop + scrollerPosition;
} else {
viewport = this.$el;
scrollDirection = direction.scroll;
scrollDistance = position;
}
viewport[scrollDirection] = scrollDistance;
},
itemsLimitError() {
setTimeout(() => {
console.log("It seems the scroller element isn't scrolling, so it tries to render all the items at once.", "Scroller:", this.$el);
console.log("Make sure the scroller has a fixed height (or width) and 'overflow-y' (or 'overflow-x') set to 'auto' so it can scroll correctly and only render the items visible in the scroll viewport.");
});
throw new Error("Rendered items limit reached");
},
sortViews() {
this.pool.sort((viewA, viewB) => viewA.nr.index - viewB.nr.index);
}
}
};
var _hoisted_1 = {
key: 0,
ref: "before",
class: "vue-recycle-scroller__slot"
};
var _hoisted_2 = {
key: 1,
ref: "after",
class: "vue-recycle-scroller__slot"
};
function render$1(_ctx, _cache, $props, $setup, $data, $options) {
const _component_ResizeObserver = resolveComponent("ResizeObserver");
const _directive_observe_visibility = resolveDirective("observe-visibility");
return withDirectives((openBlock(), createElementBlock("div", {
class: normalizeClass(["vue-recycle-scroller", {
ready: $data.ready,
"page-mode": $props.pageMode,
[`direction-${_ctx.direction}`]: true
}]),
onScrollPassive: _cache[0] || (_cache[0] = (...args) => $options.handleScroll && $options.handleScroll(...args))
}, [
_ctx.$slots.before ? (openBlock(), createElementBlock("div", _hoisted_1, [renderSlot(_ctx.$slots, "before")], 512)) : createCommentVNode("v-if", true),
(openBlock(), createBlock(resolveDynamicComponent($props.listTag), {
ref: "wrapper",
style: normalizeStyle({ [_ctx.direction === "vertical" ? "minHeight" : "minWidth"]: $data.totalSize + "px" }),
class: normalizeClass(["vue-recycle-scroller__item-wrapper", $props.listClass])
}, {
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList($data.pool, (view) => {
return openBlock(), createBlock(resolveDynamicComponent($props.itemTag), mergeProps({
key: view.nr.id,
style: $data.ready ? {
transform: `translate${_ctx.direction === "vertical" ? "Y" : "X"}(${view.position}px) translate${_ctx.direction === "vertical" ? "X" : "Y"}(${view.offset}px)`,
width: $props.gridItems ? `${_ctx.direction === "vertical" ? $props.itemSecondarySize || $props.itemSize : $props.itemSize}px` : void 0,
height: $props.gridItems ? `${_ctx.direction === "horizontal" ? $props.itemSecondarySize || $props.itemSize : $props.itemSize}px` : void 0
} : null,
class: ["vue-recycle-scroller__item-view", [$props.itemClass, { hover: !$props.skipHover && $data.hoverKey === view.nr.key }]]
}, toHandlers($props.skipHover ? {} : {
mouseenter: () => {
$data.hoverKey = view.nr.key;
},
mouseleave: () => {
$data.hoverKey = null;
}
})), {
default: withCtx(() => [renderSlot(_ctx.$slots, "default", {
item: view.item,
index: view.nr.index,
active: view.nr.used
})]),
_: 2
}, 1040, ["style", "class"]);
}), 128)), renderSlot(_ctx.$slots, "empty")]),
_: 3
}, 8, ["style", "class"])),
_ctx.$slots.after ? (openBlock(), createElementBlock("div", _hoisted_2, [renderSlot(_ctx.$slots, "after")], 512)) : createCommentVNode("v-if", true),
createVNode(_component_ResizeObserver, { onNotify: $options.handleResize }, null, 8, ["onNotify"])
], 34)), [[_directive_observe_visibility, $options.handleVisibilityChange]]);
}
script$2$1.render = render$1;
script$2$1.__file = "src/components/RecycleScroller.vue";
var script$1 = {
name: "DynamicScroller",
components: { RecycleScroller: script$2$1 },
provide() {
if (typeof ResizeObserver !== "undefined") this.$_resizeObserver = new ResizeObserver((entries) => {
requestAnimationFrame(() => {
if (!Array.isArray(entries)) return;
for (const entry of entries) if (entry.target && entry.target.$_vs_onResize) {
let width, height;
if (entry.borderBoxSize) {
const resizeObserverSize = entry.borderBoxSize[0];
width = resizeObserverSize.inlineSize;
height = resizeObserverSize.blockSize;
} else {
width = entry.contentRect.width;
height = entry.contentRect.height;
}
entry.target.$_vs_onResize(entry.target.$_vs_id, width, height);
}
});
});
return {
vscrollData: this.vscrollData,
vscrollParent: this,
vscrollResizeObserver: this.$_resizeObserver
};
},
inheritAttrs: false,
props: {
...props,
minItemSize: {
type: [Number, String],
required: true
}
},
emits: ["resize", "visible"],
data() {
return { vscrollData: {
active: true,
sizes: {},
keyField: this.keyField,
simpleArray: false
} };
},
computed: {
simpleArray,
itemsWithSize() {
const result = [];
const { items, keyField, simpleArray: simpleArray$1 } = this;
const sizes = this.vscrollData.sizes;
const l = items.length;
for (let i = 0; i < l; i++) {
const item = items[i];
const id = simpleArray$1 ? i : item[keyField];
let size = sizes[id];
if (typeof size === "undefined" && !this.$_undefinedMap[id]) size = 0;
result.push({
item,
id,
size
});
}
return result;
}
},
watch: {
items() {
this.forceUpdate();
},
simpleArray: {
handler(value) {
this.vscrollData.simpleArray = value;
},
immediate: true
},
direction(value) {
this.forceUpdate(true);
},
itemsWithSize(next, prev) {
const scrollTop = this.$el.scrollTop;
let prevActiveTop = 0;
let activeTop = 0;
const length = Math.min(next.length, prev.length);
for (let i = 0; i < length; i++) {
if (prevActiveTop >= scrollTop) break;
prevActiveTop += prev[i].size || this.minItemSize;
activeTop += next[i].size || this.minItemSize;
}
const offset = activeTop - prevActiveTop;
if (offset === 0) return;
this.$el.scrollTop += offset;
}
},
beforeCreate() {
this.$_updates = [];
this.$_undefinedSizes = 0;
this.$_undefinedMap = {};
this.$_events = mitt_es_default();
},
activated() {
this.vscrollData.active = true;
},
deactivated() {
this.vscrollData.active = false;
},
unmounted() {
this.$_events.all.clear();
},
methods: {
onScrollerResize() {
if (this.$refs.scroller) this.forceUpdate();
this.$emit("resize");
},
onScrollerVisible() {
this.$_events.emit("vscroll:update", { force: false });
this.$emit("visible");
},
forceUpdate(clear = false) {
if (clear || this.simpleArray) this.vscrollData.sizes = {};
this.$_events.emit("vscroll:update", { force: true });
},
scrollToItem(index) {
const scroller = this.$refs.scroller;
if (scroller) scroller.scrollToItem(index);
},
getItemSize(item, index = void 0) {
const id = this.simpleArray ? index != null ? index : this.items.indexOf(item) : item[this.keyField];
return this.vscrollData.sizes[id] || 0;
},
scrollToBottom() {
if (this.$_scrollingToBottom) return;
this.$_scrollingToBottom = true;
const el = this.$el;
this.$nextTick(() => {
el.scrollTop = el.scrollHeight + 5e3;
const cb = () => {
el.scrollTop = el.scrollHeight + 5e3;
requestAnimationFrame(() => {
el.scrollTop = el.scrollHeight + 5e3;
if (this.$_undefinedSizes === 0) this.$_scrollingToBottom = false;
else requestAnimationFrame(cb);
});
};
requestAnimationFrame(cb);
});
}
}
};
function render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_RecycleScroller = resolveComponent("RecycleScroller");
return openBlock(), createBlock(_component_RecycleScroller, mergeProps({
ref: "scroller",
items: $options.itemsWithSize,
"min-item-size": $props.minItemSize,
direction: _ctx.direction,
"key-field": "id",
"list-tag": _ctx.listTag,
"item-tag": _ctx.itemTag
}, _ctx.$attrs, {
onResize: $options.onScrollerResize,
onVisible: $options.onScrollerVisible
}), {
default: withCtx(({ item: itemWithSize, index, active }) => [renderSlot(_ctx.$slots, "default", normalizeProps(guardReactiveProps({
item: itemWithSize.item,
index,
active,
itemWithSize
})))]),
before: withCtx(() => [renderSlot(_ctx.$slots, "before")]),
after: withCtx(() => [renderSlot(_ctx.$slots, "after")]),
empty: withCtx(() => [renderSlot(_ctx.$slots, "empty")]),
_: 3
}, 16, [
"items",
"min-item-size",
"direction",
"list-tag",
"item-tag",
"onResize",
"onVisible"
]);
}
script$1.render = render;
script$1.__file = "src/components/DynamicScroller.vue";
var script = {
name: "DynamicScrollerItem",
inject: [
"vscrollData",
"vscrollParent",
"vscrollResizeObserver"
],
props: {
item: { required: true },
watchData: {
type: Boolean,
default: false
},
active: {
type: Boolean,
required: true
},
index: {
type: Number,
default: void 0
},
sizeDependencies: {
type: [Array, Object],
default: null
},
emitResize: {
type: Boolean,
default: false
},
tag: {
type: String,
default: "div"
}
},
emits: ["resize"],
computed: {
id() {
if (this.vscrollData.simpleArray) return this.index;
if (this.vscrollData.keyField in this.item) return this.item[this.vscrollData.keyField];
throw new Error(`keyField '${this.vscrollData.keyField}' not found in your item. You should set a valid keyField prop on your Scroller`);
},
size() {
return this.vscrollData.sizes[this.id] || 0;
},
finalActive() {
return this.active && this.vscrollData.active;
}
},
watch: {
watchData: "updateWatchData",
id(value, oldValue) {
this.$el.$_vs_id = this.id;
if (!this.size) this.onDataUpdate();
if (this.$_sizeObserved) {
const oldSize = this.vscrollData.sizes[oldValue];
const size = this.vscrollData.sizes[value];
if (oldSize != null && oldSize !== size) this.applySize(oldSize);
}
},
finalActive(value) {
if (!this.size) {
if (value) {
if (!this.vscrollParent.$_undefinedMap[this.id]) {
this.vscrollParent.$_undefinedSizes++;
this.vscrollParent.$_undefinedMap[this.id] = true;
}
} else if (this.vscrollParent.$_undefinedMap[this.id]) {
this.vscrollParent.$_undefinedSizes--;
this.vscrollParent.$_undefinedMap[this.id] = false;
}
}
if (this.vscrollResizeObserver) if (value) this.observeSize();
else this.unobserveSize();
else if (value && this.$_pendingVScrollUpdate === this.id) this.updateSize();
}
},
created() {
if (this.$isServer) return;
this.$_forceNextVScrollUpdate = null;
this.updateWatchData();
if (!this.vscrollResizeObserver) {
for (const k in this.sizeDependencies) this.$watch(() => this.sizeDependencies[k], this.onDataUpdate);
this.vscrollParent.$_events.on("vscroll:update", this.onVscrollUpdate);
}
},
mounted() {
if (this.finalActive) {
this.updateSize();
this.observeSize();
}
},
beforeUnmount() {
this.vscrollParent.$_events.off("vscroll:update", this.onVscrollUpdate);
this.unobserveSize();
},
methods: {
updateSize() {
if (this.finalActive) {
if (this.$_pendingSizeUpdate !== this.id) {
this.$_pendingSizeUpdate = this.id;
this.$_forceNextVScrollUpdate = null;
this.$_pendingVScrollUpdate = null;
this.computeSize(this.id);
}
} else this.$_forceNextVScrollUpdate = this.id;
},
updateWatchData() {
if (this.watchData && !this.vscrollResizeObserver) this.$_watchData = this.$watch("item", () => {
this.onDataUpdate();
}, { deep: true });
else if (this.$_watchData) {
this.$_watchData();
this.$_watchData = null;
}
},
onVscrollUpdate({ force }) {
if (!this.finalActive && force) this.$_pendingVScrollUpdate = this.id;
if (this.$_forceNextVScrollUpdate === this.id || force || !this.size) this.updateSize();
},
onDataUpdate() {
this.updateSize();
},
computeSize(id) {
this.$nextTick(() => {
if (this.id === id) {
const width = this.$el.offsetWidth;
const height = this.$el.offsetHeight;
this.applyWidthHeight(width, height);
}
this.$_pendingSizeUpdate = null;
});
},
applyWidthHeight(width, height) {
const size = ~~(this.vscrollParent.direction === "vertical" ? height : width);
if (size && this.size !== size) this.applySize(size);
},
applySize(size) {
if (this.vscrollParent.$_undefinedMap[this.id]) {
this.vscrollParent.$_undefinedSizes--;
this.vscrollParent.$_undefinedMap[this.id] = void 0;
}
this.vscrollData.sizes[this.id] = size;
if (this.emitResize) this.$emit("resize", this.id);
},
observeSize() {
if (!this.vscrollResizeObserver) return;
if (this.$_sizeObserved) return;
this.vscrollResizeObserver.observe(this.$el);
this.$el.$_vs_id = this.id;
this.$el.$_vs_onResize = this.onResize;
this.$_sizeObserved = true;
},
unobserveSize() {
if (!this.vscrollResizeObserver) return;
if (!this.$_sizeObserved) return;
this.vscrollResizeObserver.unobserve(this.$el);
this.$el.$_vs_onResize = void 0;
this.$_sizeObserved = false;
},
onResize(id, width, height) {
if (this.id === id) this.applyWidthHeight(width, height);
}
},
render() {
return h(this.tag, this.$slots.default());
}
};
script.__file = "src/components/DynamicScrollerItem.vue";
function getParentNodes() {
const activeNode = useNDVStore().activeNode;
const { workflowObject, getNodeByName } = useWorkflowsStore();
if (!activeNode || !workflowObject) return [];
return workflowObject.getParentNodesByDepth(activeNode?.name).filter(({ name }, i, nodes) => {
return name !== activeNode.name && nodes.findIndex((node) => node.name === name) === i;
}).map((n) => getNodeByName(n.name)).filter((n) => n !== null);
}
function getSchemas() {
const parentNodes = getParentNodes();
const parentNodesNames = parentNodes.map((node) => node?.name);
const { getSchemaForExecutionData, getInputDataWithPinned } = useDataSchema();
const parentNodesSchemas = parentNodes.map((node) => {
const inputData = getInputDataWithPinned(node);
return {
nodeName: node?.name || "",
schema: getSchemaForExecutionData(executionDataToJson(inputData), false)
};
}).filter((node) => node.schema?.value.length > 0);
return {
parentNodesNames,
inputSchema: parentNodesSchemas.shift(),
parentNodesSchemas
};
}
var estimateNumberOfTokens = (item, averageTokenLength) => {
if (typeof item === "object") return Math.ceil(JSON.stringify(item).length / averageTokenLength);
return 0;
};
var calculateRemainingTokens = (error) => {
const tokens = error.message.match(/\d+/g);
if (!tokens || tokens.length < 2) throw error;
const maxTokens = parseInt(tokens[0], 10);
return parseInt(tokens[1], 10) - maxTokens;
};
var trimParentNodesSchema = (payload, remainingTokensToReduce, averageTokenLength) => {
let parentNodesTokenCount = estimateNumberOfTokens(payload.context.schema, averageTokenLength);
if (remainingTokensToReduce > parentNodesTokenCount) {
remainingTokensToReduce -= parentNodesTokenCount;
payload.context.schema = [];
}
if (payload.context.schema.length) {
const nodes = [...payload.context.schema];
for (let nodeIndex = 0; nodeIndex < nodes.length; nodeIndex++) {
if (payload.question.includes(nodes[nodeIndex].nodeName)) continue;
const nodeTokens = estimateNumberOfTokens(nodes[nodeIndex], averageTokenLength);
remainingTokensToReduce -= nodeTokens;
parentNodesTokenCount -= nodeTokens;
payload.context.schema.splice(nodeIndex, 1);
if (remainingTokensToReduce <= 0) break;
}
}
return [remainingTokensToReduce, parentNodesTokenCount];
};
var trimInputSchemaProperties = (payload, remainingTokensToReduce, averageTokenLength, parentNodesTokenCount) => {
if (remainingTokensToReduce <= 0) return remainingTokensToReduce;
if (Array.isArray(payload.context.inputSchema.schema.value)) {
const props$1 = [...payload.context.inputSchema.schema.value];
for (let index = 0; index < props$1.length; index++) {
const key = props$1[index].key;
if (key && payload.question.includes(key)) continue;
const propTokens = estimateNumberOfTokens(props$1[index], averageTokenLength);
remainingTokensToReduce -= propTokens;
payload.context.inputSchema.schema.value.splice(index, 1);
if (remainingTokensToReduce <= 0) break;
}
}
if (remainingTokensToReduce > 0) {
payload.context.schema = [];
remainingTokensToReduce -= parentNodesTokenCount;
}
return remainingTokensToReduce;
};
function reducePayloadSizeOrThrow(payload, error, averageTokenLength = 4) {
let remainingTokensToReduce = calculateRemainingTokens(error);
const [remaining, parentNodesTokenCount] = trimParentNodesSchema(payload, remainingTokensToReduce, averageTokenLength);
remainingTokensToReduce = remaining;
remainingTokensToReduce = trimInputSchemaProperties(payload, remainingTokensToReduce, averageTokenLength, parentNodesTokenCount);
if (remainingTokensToReduce > 0) throw error;
}
async function generateCodeForAiTransform(prompt$1, path, retries = 1) {
const schemas = getSchemas();
const payload = {
question: prompt$1,
context: {
schema: schemas.parentNodesSchemas,
inputSchema: schemas.inputSchema,
ndvPushRef: useNDVStore().pushRef,
pushRef: useRootStore().pushRef
},
forNode: "transform"
};
let value;
if (useSettingsStore().isAskAiEnabled) {
const { restApiContext } = useRootStore();
let code = "";
while (retries > 0) try {
const { code: generatedCode } = await generateCodeForPrompt(restApiContext, payload);
code = generatedCode;
break;
} catch (e) {
if (typeof e.message === "string" && e.message.includes("maximum context length")) {
reducePayloadSizeOrThrow(payload, e);
continue;
}
retries--;
if (!retries) throw e;
}
value = code;
} else throw new ApplicationError("AI code generation is not enabled");
if (value === void 0) return;
return {
name: path,
value: await gu(String(value), {
parser: "babel",
plugins: [Ln, estree_exports]
})
};
}
function splitText(textarea, textareaRowsData) {
if (textareaRowsData) return textareaRowsData;
const rows = [];
const linesToRowsMap = [];
const style$1 = window.getComputedStyle(textarea);
const padding = parseFloat(style$1.paddingLeft) + parseFloat(style$1.paddingRight);
const border = parseFloat(style$1.borderLeftWidth) + parseFloat(style$1.borderRightWidth);
const textareaWidth = textarea.clientWidth - padding - border;
const context = createTextContext(style$1);
const lines = textarea.value.split("\n");
lines.forEach((_) => {
linesToRowsMap.push([]);
});
lines.forEach((line, index) => {
if (line === "") {
rows.push(line);
linesToRowsMap[index].push(rows.length - 1);
return;
}
let currentLine = "";
line.split(/(\s+)/).forEach((word) => {
const testLine = currentLine + word;
if (context.measureText(testLine).width <= textareaWidth) currentLine = testLine;
else {
rows.push(currentLine.trimEnd());
linesToRowsMap[index].push(rows.length - 1);
currentLine = word;
}
});
if (currentLine) {
rows.push(currentLine.trimEnd());
linesToRowsMap[index].push(rows.length - 1);
}
});
return {
rows,
linesToRowsMap
};
}
function createTextContext(style$1) {
const context = document.createElement("canvas").getContext("2d");
context.font = `${style$1.fontWeight} ${style$1.fontSize} ${style$1.fontFamily}`;
return context;
}
var getRowIndex = (textareaY, lineHeight) => {
const rowHeight = parseInt(lineHeight, 10);
const snapPosition = textareaY - rowHeight / 2 - 1;
return Math.floor(snapPosition / rowHeight);
};
var getColumnIndex = (rowText, textareaX, font) => {
const span = document.createElement("span");
span.style.font = font;
span.style.visibility = "hidden";
span.style.position = "absolute";
span.style.whiteSpace = "pre";
document.body.appendChild(span);
let left = 0;
let right = rowText.length;
let col = 0;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
span.textContent = rowText.substring(0, mid);
if (span.getBoundingClientRect().width <= textareaX) {
col = mid;
left = mid + 1;
} else right = mid - 1;
}
document.body.removeChild(span);
return rowText.length === col ? col : col - 1;
};
function getUpdatedTextareaValue(event, textareaRowsData, value) {
const textarea = event.target;
const rect = textarea.getBoundingClientRect();
const textareaX = event.clientX - rect.left;
const textareaY = event.clientY - rect.top;
const { lineHeight, font } = window.getComputedStyle(textarea);
const rowIndex = getRowIndex(textareaY, lineHeight);
const rowsData = splitText(textarea, textareaRowsData);
let newText = value;
if (rowsData.rows[rowIndex] === void 0) newText = `${textarea.value} ${value}`;
const { rows, linesToRowsMap } = rowsData;
const rowText = rows[rowIndex];
if (rowText === "") rows[rowIndex] = value;
else {
const col = getColumnIndex(rowText, textareaX, font);
rows[rowIndex] = [
rows[rowIndex].slice(0, col).trim(),
value,
rows[rowIndex].slice(col).trim()
].join(" ").trim();
}
newText = linesToRowsMap.map((lineMap) => {
return lineMap.map((index) => rows[index]).join(" ");
}).join("\n");
return newText;
}
function getTextareaCursorPosition(textarea, textareaRowsData, clientX, clientY) {
const rect = textarea.getBoundingClientRect();
const textareaX = clientX - rect.left;
const textareaY = clientY - rect.top;
const { lineHeight, font } = window.getComputedStyle(textarea);
const rowIndex = getRowIndex(textareaY, lineHeight);
const { rows } = splitText(textarea, textareaRowsData);
if (rowIndex < 0 || rowIndex >= rows.length) return textarea.value.length;
const rowText = rows[rowIndex];
const col = getColumnIndex(rowText, textareaX, font);
return rows.slice(0, rowIndex).reduce((acc, curr) => acc + curr.length + 1, 0) + col;
}
var NODE_TEST_STEP_POPUP_COUNT_KEY = "N8N_NODE_TEST_STEP_POPUP_COUNT";
var MAX_POPUP_COUNT = 10;
var POPUP_UPDATE_DELAY = 3e3;
var NodeExecuteButton_default = /* @__PURE__ */ defineComponent({
inheritAttrs: false,
__name: "NodeExecuteButton",
props: {
nodeName: {},
telemetrySource: {},
disabled: {
type: Boolean,
default: false
},
label: {},
type: {},
size: {},
icon: {},
square: {
type: Boolean,
default: false
},
transparent: {
type: Boolean,
default: false
},
hideIcon: { type: Boolean },
hideLabel: { type: Boolean },
tooltip: {},
tooltipPlacement: {},
showLoadingSpinner: {
type: Boolean,
default: true
},
executionMode: { default: "inclusive" }
},
emits: [
"stopExecution",
"execute",
"valueChanged"
],
setup(__props, { emit: __emit }) {
const props$1 = __props;
const emit = __emit;
const slots = useSlots();
const lastPopupCountUpdate = ref(0);
const codeGenerationInProgress = ref(false);
const { runWorkflow, stopCurrentExecution } = useRunWorkflow({ router: useRouter() });
const workflowsStore = useWorkflowsStore();
const workflowState = injectWorkflowState();
const externalHooks = useExternalHooks();
const toast = useToast();
const ndvStore = useNDVStore();
const nodeTypesStore = useNodeTypesStore();
const i18n = useI18n();
const message = useMessage();
const telemetry = useTelemetry();
const uiStore = useUIStore();
const node = computed(() => workflowsStore.getNodeByName(props$1.nodeName));
const pinnedData = usePinnedData(node);
const nodeType = computed(() => {
return node.value ? nodeTypesStore.getNodeType(node.value.type, node.value.typeVersion) : null;
});
const isNodeRunning = computed(() => {
if (!workflowsStore.isWorkflowRunning || codeGenerationInProgress.value) return false;
const triggeredNode = workflowsStore.executedNode;
return workflowState.executingNode.isNodeExecuting(node.value?.name ?? "") || triggeredNode === node.value?.name;
});
const isTriggerNode = computed(() => {
return node.value ? nodeTypesStore.isTriggerNode(node.value.type) : false;
});
const isManualTriggerNode = computed(() => nodeType.value ? nodeType.value.name === MANUAL_TRIGGER_NODE_TYPE : false);
const isChatNode = computed(() => nodeType.value ? nodeType.value.name === CHAT_TRIGGER_NODE_TYPE : false);
const isChatChild = computed(() => workflowsStore.checkIfNodeHasChatParent(props$1.nodeName));
const isFormTriggerNode = computed(() => nodeType.value ? nodeType.value.name === FORM_TRIGGER_NODE_TYPE : false);
const isPollingTypeNode = computed(() => !!nodeType.value?.polling);
const isScheduleTrigger = computed(() => !!nodeType.value?.group.includes("schedule"));
const isWebhookNode = computed(() => nodeType.value ? nodeType.value.name === WEBHOOK_NODE_TYPE : false);
const isListeningForEvents = computed(() => {
const waitingOnWebhook = workflowsStore.executionWaitingForWebhook;
const executedNode = workflowsStore.executedNode;
return !!node.value && !node.value.disabled && isTriggerNode.value && waitingOnWebhook && (!executedNode || executedNode === props$1.nodeName);
});
const isListeningForWorkflowEvents = computed(() => {
return isNodeRunning.value && isTriggerNode.value && !isScheduleTrigger.value && !isManualTriggerNode.value;
});
const hasIssues = computed(() => Boolean(node.value?.issues && (node.value.issues.parameters || node.value.issues.credentials)));
const disabledHint = computed(() => {
if (isListeningForEvents.value) return "";
if (codeGenerationInProgress.value) return i18n.baseText("ndv.execute.generatingCode");
if (node?.value?.disabled) return i18n.baseText("ndv.execute.nodeIsDisabled");
if (isTriggerNode.value && hasIssues.value) {
const activeNode = ndvStore.activeNode;
if (activeNode && activeNode.name !== props$1.nodeName) return i18n.baseText("ndv.execute.fixPrevious");
return i18n.baseText("ndv.execute.requiredFieldsMissing");
}
if (workflowsStore.isWorkflowRunning && !isNodeRunning.value) return i18n.baseText("ndv.execute.workflowAlreadyRunning");
return "";
});
const tooltipText = computed(() => {
if (shouldGenerateCode.value) return i18n.baseText("ndv.execute.generateCodeAndTestNode.description");
if (disabledHint.value) return disabledHint.value;
if (props$1.tooltip && !isLoading.value && testStepButtonPopupCount() < MAX_POPUP_COUNT) return props$1.tooltip;
return "";
});
const buttonLabel = computed(() => {
if (props$1.hideLabel) return "";
if (isListeningForEvents.value || isListeningForWorkflowEvents.value) return i18n.baseText("ndv.execute.stopListening");
if (props$1.label) return props$1.label;
if (isChatNode.value) return i18n.baseText("ndv.execute.testChat");
if (isWebhookNode.value) return i18n.baseText("ndv.execute.listenForTestEvent");
if (isFormTriggerNode.value) return i18n.baseText("ndv.execute.testStep");
if (isPollingTypeNode.value || nodeType.value?.mockManualExecution) return i18n.baseText("ndv.execute.fetchEvent");
return i18n.baseText("ndv.execute.testNode");
});
const isLoading = computed(() => codeGenerationInProgress.value || isNodeRunning.value && !isListeningForEvents.value && !isListeningForWorkflowEvents.value);
const buttonIcon = computed(() => {
if (props$1.icon) return props$1.icon;
if (shouldGenerateCode.value) return "terminal";
if (!isListeningForEvents.value && !props$1.hideIcon) return "flask-conical";
});
const shouldGenerateCode = computed(() => {
if (node.value?.type !== "n8n-nodes-base.aiTransform") return false;
if (!node.value?.parameters?.instructions) return false;
if (!node.value?.parameters?.jsCode) return true;
if (node.value?.parameters["codeGeneratedForPrompt"] && (node.value?.parameters?.instructions).trim() !== (node.value?.parameters?.["codeGeneratedForPrompt"]).trim()) return true;
return false;
});
async function stopWaitingForWebhook() {
try {
await workflowsStore.removeTestWebhook(workflowsStore.workflowId);
} catch (error) {
toast.showError(error, "Error stopping webhook");
}
}
function testStepButtonPopupCount() {
return Number(localStorage.getItem(NODE_TEST_STEP_POPUP_COUNT_KEY));
}
function onMouseOver() {
const count = testStepButtonPopupCount();
if (count < MAX_POPUP_COUNT && !disabledHint.value && tooltipText.value) {
const now = Date.now();
if (!lastPopupCountUpdate.value || now - lastPopupCountUpdate.value >= POPUP_UPDATE_DELAY) {
localStorage.setItem(NODE_TEST_STEP_POPUP_COUNT_KEY, `${count + 1}`);
lastPopupCountUpdate.value = now;
}
}
}
async function onClick() {
if (shouldGenerateCode.value) {
codeGenerationInProgress.value = true;
try {
toast.showMessage({
title: i18n.baseText("ndv.execute.generateCode.title"),
message: i18n.baseText("ndv.execute.generateCode.message", { interpolate: { nodeName: node.value?.name } }),
type: "success"
});
const prompt$1 = node.value?.parameters?.instructions;
const updateInformation = await generateCodeForAiTransform(prompt$1, `parameters.${AI_TRANSFORM_JS_CODE}`, 5);
if (!updateInformation) return;
emit("valueChanged", updateInformation);
emit("valueChanged", {
name: `parameters.${AI_TRANSFORM_CODE_GENERATED_FOR_PROMPT}`,
value: prompt$1
});
useTelemetry().trackAiTransform("generationFinished", {
prompt: prompt$1,
code: updateInformation.value
});
} catch (error) {
useTelemetry().trackAiTransform("generationFinished", {
prompt,
code: "",
hasError: true
});
toast.showMessage({
type: "error",
title: i18n.baseText("codeNodeEditor.askAi.generationFailed"),
message: error.message
});
}
codeGenerationInProgress.value = false;
}
if (isChatNode.value || isChatChild.value && ndvStore.isInputPanelEmpty) {
ndvStore.unsetActiveNodeName();
workflowsStore.chatPartialExecutionDestinationNode = props$1.nodeName;
nodeViewEventBus.emit("openChat");
} else if (isListeningForEvents.value) await stopWaitingForWebhook();
else if (isListeningForWorkflowEvents.value) {
await stopCurrentExecution();
emit("stopExecution");
} else {
let shouldUnpinAndExecute = false;
if (pinnedData.hasData.value) {
shouldUnpinAndExecute = await message.confirm(i18n.baseText("ndv.pinData.unpinAndExecute.description"), i18n.baseText("ndv.pinData.unpinAndExecute.title"), {
confirmButtonText: i18n.baseText("ndv.pinData.unpinAndExecute.confirm"),
cancelButtonText: i18n.baseText("ndv.pinData.unpinAndExecute.cancel")
}) === MODAL_CONFIRM;
if (shouldUnpinAndExecute && node.value) pinnedData.unsetData("unpin-and-execute-modal");
}
if (!pinnedData.hasData.value || shouldUnpinAndExecute) if (node.value && needsAgentInput(node.value)) uiStore.openModalWithData({
name: FROM_AI_PARAMETERS_MODAL_KEY,
data: { nodeName: props$1.nodeName }
});
else {
const telemetryPayload = {
node_type: nodeType.value ? nodeType.value.name : null,
workflow_id: workflowsStore.workflowId,
source: props$1.telemetrySource,
push_ref: ndvStore.pushRef
};
telemetry.track("User clicked execute node button", telemetryPayload);
await externalHooks.run("nodeExecuteButton.onClick", telemetryPayload);
await runWorkflow({
destinationNode: {
nodeName: props$1.nodeName,
mode: props$1.executionMode
},
source: "RunData.ExecuteNodeButton"
});
emit("execute");
}
}
}
return (_ctx, _cache) => {
return openBlock(), createBlock(unref(N8nTooltip_default), {
placement: _ctx.tooltipPlacement ?? "right",
disabled: !tooltipText.value && !slots.persistentTooltipContent,
visible: slots.persistentTooltipContent ? true : void 0
}, {
content: withCtx(() => [renderSlot(_ctx.$slots, "persistentTooltipContent", {}, () => [createTextVNode(toDisplayString(tooltipText.value), 1)])]),
default: withCtx(() => [createVNode(unref(N8nButton_default), mergeProps(_ctx.$attrs, {
loading: isLoading.value && _ctx.showLoadingSpinner,
disabled: _ctx.disabled || !!disabledHint.value || isLoading.value && !_ctx.showLoadingSpinner,
label: buttonLabel.value,
type: _ctx.type,
size: _ctx.size,
icon: buttonIcon.value,
square: _ctx.square,
"transparent-background": _ctx.transparent,
title: !isTriggerNode.value && !tooltipText.value ? unref(i18n).baseText("ndv.execute.testNode.description") : "",
onMouseover: onMouseOver,
onClick
}), null, 16, [
"loading",
"disabled",
"label",
"type",
"size",
"icon",
"square",
"transparent-background",
"title"
])]),
_: 3
}, 8, [
"placement",
"disabled",
"visible"
]);
};
}
});
var import_pick = /* @__PURE__ */ __toESM(require_pick());
var VirtualSchema_default = /* @__PURE__ */ __plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
__name: "VirtualSchema",
props: {
nodes: { default: () => [] },
node: { default: null },
data: { default: () => [] },
mappingEnabled: {
type: Boolean,
default: false
},
paneType: {},
connectionType: { default: NodeConnectionTypes.Main },
search: { default: "" },
compact: {
type: Boolean,
default: false
},
outputIndex: { default: void 0 },
previewExecution: { default: void 0 },
truncateLimit: { default: 600 }
},
emits: ["clear:search", "execute"],
setup(__props, { emit: __emit }) {
const props$1 = __props;
const telemetry = useTelemetry();
const telemetryContext = useTelemetryContext();
const i18n = useI18n();
const ndvStore = useNDVStore();
const nodeTypesStore = useNodeTypesStore();
const workflowsStore = useWorkflowsStore();
const schemaPreviewStore = useSchemaPreviewStore();
const environmentsStore = environments_store_default();
const settingsStore = useSettingsStore();
const { getSchemaForExecutionData, getSchemaForJsonSchema, getSchema, filterSchema } = useDataSchema();
const { closedNodes, flattenSchema, flattenMultipleSchemas, toggleNode } = useFlattenSchema();
const { getNodeInputData, getLastRunIndexWithData, hasNodeExecuted } = useNodeHelpers();
const emit = __emit;
const scroller = ref();
const closedNodesBeforeSearch = ref(/* @__PURE__ */ new Set());
const canDraggableDrop = computed(() => ndvStore.canDraggableDrop);
const draggableStickyPosition = computed(() => ndvStore.draggableStickyPos);
const toggleNodeExclusiveAndScrollTop = (id) => {
if (closedNodes.value.has(id)) closedNodes.value = new Set(items.value.map((item) => item.id));
toggleNode(id);
scroller.value?.scrollToItem(0);
};
const getNodeSchema = async (fullNode, connectedNode) => {
const pinData = workflowsStore.pinDataByNodeName(connectedNode.name);
const isNodeExecuted = (pinData ? pinData.length > 0 : false) || hasNodeExecuted(connectedNode.name);
const previewExecutionData = !isNodeExecuted && props$1.previewExecution ? props$1.previewExecution.data : void 0;
const connectedOutputIndexes = connectedNode.indicies.length > 0 ? connectedNode.indicies : [0];
const connectedOutputsWithData = connectedOutputIndexes.map((outputIndex) => ({
outputIndex,
runIndex: getLastRunIndexWithData(fullNode.name, outputIndex, props$1.connectionType, previewExecutionData)
})).filter(({ runIndex }) => runIndex !== -1);
let filteredOutputsWithData = connectedOutputsWithData;
if (props$1.outputIndex !== void 0 && connectedOutputIndexes.length > 1 && connectedOutputIndexes.includes(props$1.outputIndex)) filteredOutputsWithData = connectedOutputsWithData.filter(({ outputIndex }) => outputIndex === props$1.outputIndex);
const nodeData = filteredOutputsWithData.map(({ outputIndex, runIndex }) => {
return getNodeInputData(fullNode, runIndex, outputIndex, props$1.paneType, props$1.connectionType, previewExecutionData);
}).flat();
const hasBinary = nodeData.some((data$1) => !isEmpty(data$1.binary));
const data = pinData ?? executionDataToJson(nodeData);
const isDataEmpty = data.length === 0 || data.length === 1 && Object.keys(data[0]).length === 0;
let schema = getSchemaForExecutionData(data);
const lastSuccessfulPreview = !isDataEmpty && Boolean(previewExecutionData);
let preview = lastSuccessfulPreview;
if (isDataEmpty && !preview) {
const previewSchema = await getSchemaPreview(fullNode);
if (previewSchema.ok) {
schema = getSchemaForJsonSchema(previewSchema.result);
preview = true;
}
}
return {
schema,
connectedOutputIndexes,
itemsCount: !isDataEmpty ? data.length : 0,
runIndex: connectedOutputsWithData[0]?.runIndex ?? 0,
preview,
lastSuccessfulPreview,
hasBinary,
isNodeExecuted,
isDataEmpty
};
};
const isVariablesEnabled = computed(() => settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.Variables]);
const contextSchema = computed(() => {
const $vars = environmentsStore.variablesAsObject;
return filterSchema(getSchema({
$now: DateTime.now().toISO(),
$today: DateTime.now().set({
hour: 0,
minute: 0,
second: 0,
millisecond: 0
}).toISO(),
$vars,
$execution: {
id: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
mode: "test",
resumeUrl: i18n.baseText("dataMapping.schemaView.execution.resumeUrl")
},
$workflow: (0, import_pick.default)(workflowsStore.workflow, [
"id",
"name",
"active"
])
}), props$1.search);
});
const contextItems = computed(() => {
const header = {
id: "variables",
type: "header",
title: i18n.baseText("dataMapping.schemaView.variablesContextTitle"),
collapsable: true,
itemCount: null
};
if (closedNodes.value.has(header.id)) return [header];
const schema = contextSchema.value;
if (!schema) return [];
const fields = flattenSchema({
schema,
depth: 1,
isDataEmpty: false,
truncateLimit: props$1.truncateLimit
}).flatMap((renderItem) => {
if (renderItem.type === "item" && renderItem.depth === 1 && renderItem.title === "$vars") {
const isVarsOpen = !closedNodes.value.has(renderItem.id);
if (!isVariablesEnabled.value) {
renderItem.collapsable = false;
renderItem.locked = true;
renderItem.lockedTooltip = i18n.baseText("dataMapping.schemaView.variablesUpgrade");
return renderItem;
}
if (isVarsOpen && environmentsStore.scopedVariables.length === 0) return [renderItem, {
type: "notice",
id: "notice-variablesEmpty",
level: renderItem.level ?? 0,
message: i18n.baseText("dataMapping.schemaView.variablesEmpty")
}];
}
return renderItem;
});
return [header].concat(fields);
});
const nodeSchema = computedAsync(async () => {
const search = props$1.search;
if (props$1.data.length === 0) {
const previewSchema = await getSchemaPreview(props$1.node);
if (previewSchema.ok) return filterSchema(getSchemaForJsonSchema(previewSchema.result), search);
}
return filterSchema(getSchemaForExecutionData(props$1.data), search);
}, null);
async function getSchemaPreview(node) {
if (!node) return createResultError(/* @__PURE__ */ new Error());
if (nodeTypesStore.getNodeType(node.type, node.typeVersion)?.group.includes("trigger")) return createResultError(/* @__PURE__ */ new Error("Trigger nodes do not have schema previews"));
const { type, typeVersion, parameters: { resource, operation } } = node;
return await schemaPreviewStore.getSchemaPreview({
nodeType: type,
version: typeVersion,
resource,
operation
});
}
const nodesSchemas = computedAsync(async () => {
const result = [];
const search = props$1.search;
for (const node of props$1.nodes) {
const fullNode = workflowsStore.getNodeByName(node.name);
if (!fullNode) continue;
const nodeType = nodeTypesStore.getNodeType(fullNode.type, fullNode.typeVersion);
if (!nodeType) continue;
const { schema, connectedOutputIndexes, itemsCount, preview, hasBinary, isNodeExecuted, isDataEmpty, runIndex, lastSuccessfulPreview } = await getNodeSchema(fullNode, node);
const filteredSchema = filterSchema(schema, search);
if (!filteredSchema) continue;
result.push({
node: fullNode,
connectedOutputIndexes,
depth: node.depth,
itemsCount,
nodeType,
schema: filteredSchema,
preview,
hasBinary,
isNodeExecuted,
isDataEmpty,
runIndex,
lastSuccessfulPreview
});
}
return result;
}, []);
const nodeAdditionalInfo = (node) => {
const returnData = [];
if (node.disabled) returnData.push(i18n.baseText("node.disabled"));
const connections = ndvStore.ndvNodeInputNumber[node.name];
if (connections) if (connections.length === 1) returnData.push(`Input ${connections}`);
else returnData.push(`Inputs ${connections.join(", ")}`);
return returnData.length ? `(${returnData.join(" | ")})` : "";
};
const flattenedNodes = computed(() => flattenMultipleSchemas(nodesSchemas.value, nodeAdditionalInfo, props$1.truncateLimit));
const flattenNodeSchema = computed(() => nodeSchema.value ? flattenSchema({
schema: nodeSchema.value,
depth: 0,
level: -1,
isDataEmpty: props$1.data.length === 0,
truncateLimit: props$1.truncateLimit
}) : []);
const isDebugging = computed(() => !props$1.nodes.length);
const items = computed(() => {
if (isDebugging.value || props$1.paneType === "output") return flattenNodeSchema.value;
return flattenedNodes.value.concat(contextItems.value);
});
const noSearchResults = computed(() => {
return Boolean(props$1.search.trim()) && !items.value.length;
});
watch(() => Boolean(props$1.search), (hasSearch) => {
if (hasSearch) {
closedNodesBeforeSearch.value = new Set(closedNodes.value);
closedNodes.value.clear();
} else if (closedNodes.value.size === 0) closedNodes.value = closedNodesBeforeSearch.value;
});
const unwatchItems = watch(items, (newItems) => {
if (newItems.length < 2) return;
closedNodes.value = new Set(newItems.filter((item) => item.type === "header").slice(1).map((item) => item.id));
unwatchItems();
});
const onDragStart = (el, data) => {
ndvStore.draggableStartDragging({
type: "mapping",
data: data ?? "",
dimensions: el?.getBoundingClientRect() ?? null
});
ndvStore.resetMappingTelemetry();
};
const onDragEnd = (el) => {
ndvStore.draggableStopDragging();
setTimeout(() => {
const mappingTelemetry = ndvStore.mappingTelemetry;
const parentNode = nodesSchemas.value.find(({ node }) => node.name === el.dataset.nodeName);
const isPreview = parentNode?.preview ?? false;
const hasCredential = !isEmpty(parentNode?.node.credentials);
const runIndex = Number(el.dataset.runIndex);
const telemetryPayload = {
src_node_type: el.dataset.nodeType,
src_field_name: el.dataset.name ?? "",
src_nodes_back: el.dataset.depth,
src_run_index: runIndex,
src_runs_total: runIndex,
src_field_nest_level: el.dataset.level ?? 0,
src_view: isPreview ? "schema_preview" : "schema",
src_has_credential: hasCredential,
src_element: el,
success: false,
view_shown: telemetryContext.view_shown,
...mappingTelemetry
};
useExternalHooks().run("runDataJson.onDragEnd", telemetryPayload);
telemetry.track("User dragged data for mapping", telemetryPayload);
}, 250);
};
return (_ctx, _cache) => {
const _directive_n8n_html = resolveDirective("n8n-html");
return openBlock(), createElementBlock("div", { class: normalizeClass([
"run-data-schema",
"full-height",
{
compact: props$1.compact,
"no-search-results": noSearchResults.value
}
]) }, [noSearchResults.value ? (openBlock(), createBlock(NDVEmptyState_default, {
key: 0,
title: unref(i18n).baseText("ndv.search.noNodeMatch.title")
}, {
default: withCtx(() => [createVNode(unref(I18nT), {
keypath: "ndv.search.noMatchSchema.description",
tag: "span",
scope: "global"
}, {
link: withCtx(() => [createBaseVNode("a", {
href: "#",
onClick: _cache[0] || (_cache[0] = withModifiers(($event) => emit("clear:search"), ["prevent"]))
}, toDisplayString(unref(i18n).baseText("ndv.search.noMatchSchema.description.link")), 1)]),
_: 1
})]),
_: 1
}, 8, ["title"])) : createCommentVNode("", true), items.value.length ? (openBlock(), createBlock(Draggable_default, {
key: 1,
class: "full-height",
type: "mapping",
"target-data-key": "mappable",
disabled: !_ctx.mappingEnabled,
"can-drop": canDraggableDrop.value,
"sticky-position": draggableStickyPosition.value,
onDragstart: onDragStart,
onDragend: onDragEnd
}, {
preview: withCtx(({ canDrop, el }) => [el ? (openBlock(), createBlock(MappingPill_default, {
key: 0,
html: el.outerHTML,
"can-drop": canDrop
}, null, 8, ["html", "can-drop"])) : createCommentVNode("", true)]),
default: withCtx(() => [createVNode(unref(script$1), {
ref_key: "scroller",
ref: scroller,
items: items.value,
"min-item-size": 38,
class: "full-height scroller"
}, {
default: withCtx(({ item, index, active }) => [createVNode(unref(script), {
item,
active,
"size-dependencies": [item],
"data-index": index
}, {
default: withCtx(() => [item.type === "header" ? (openBlock(), createBlock(VirtualSchemaHeader_default, mergeProps({ key: 0 }, item, {
collapsed: unref(closedNodes).has(item.id),
"onClick:toggle": ($event) => unref(toggleNode)(item.id),
onClick: ($event) => toggleNodeExclusiveAndScrollTop(item.id),
onExecute: _cache[1] || (_cache[1] = (nodeName) => emit("execute", nodeName))
}), null, 16, [
"collapsed",
"onClick:toggle",
"onClick"
])) : item.type === "item" ? (openBlock(), createBlock(VirtualSchemaItem_default, mergeProps({ key: 1 }, item, {
search: _ctx.search,
draggable: _ctx.mappingEnabled,
collapsed: unref(closedNodes).has(item.id),
highlight: unref(ndvStore).highlightDraggables,
onClick: ($event) => unref(toggleNode)(item.id)
}), null, 16, [
"search",
"draggable",
"collapsed",
"highlight",
"onClick"
])) : item.type === "icon" ? (openBlock(), createBlock(unref(N8nTooltip_default), {
key: 2,
content: item.tooltip,
placement: "top"
}, {
default: withCtx(() => [createVNode(unref(N8nIcon_default), {
size: "small",
icon: item.icon,
class: "icon"
}, null, 8, ["icon"])]),
_: 2
}, 1032, ["content"])) : item.type === "notice" ? withDirectives((openBlock(), createElementBlock("div", {
key: 3,
class: "notice",
style: normalizeStyle({ "--schema-level": item.level })
}, null, 4)), [[_directive_n8n_html, item.message]]) : item.type === "empty" ? (openBlock(), createElementBlock("div", {
key: 4,
class: "empty-schema",
style: normalizeStyle({ "--schema-level": item.level })
}, [createVNode(unref(N8nText_default), {
tag: "div",
size: "small"
}, {
default: withCtx(() => [item.key === "executeSchema" ? (openBlock(), createBlock(unref(I18nT), {
key: 0,
tag: "span",
keypath: "dataMapping.schemaView.executeSchema",
scope: "global"
}, {
link: withCtx(() => [unref(ndvStore).activeNodeName ? (openBlock(), createBlock(NodeExecuteButton_default, {
key: 0,
"node-name": unref(ndvStore).activeNodeName,
label: unref(i18n).baseText("ndv.input.noOutputData.executePrevious"),
"telemetry-source": "inputs",
size: "small",
type: "secondary",
"hide-icon": "",
"execution-mode": "exclusive"
}, null, 8, ["node-name", "label"])) : createCommentVNode("", true)]),
_: 1
})) : (openBlock(), createBlock(unref(I18nT), {
key: 1,
tag: "span",
keypath: `dataMapping.schemaView.${item.key}`,
scope: "global"
}, null, 8, ["keypath"]))]),
_: 2
}, 1024)], 4)) : createCommentVNode("", true)]),
_: 2
}, 1032, [
"item",
"active",
"size-dependencies",
"data-index"
])]),
_: 1
}, 8, ["items"])]),
_: 1
}, 8, [
"disabled",
"can-drop",
"sticky-position"
])) : createCommentVNode("", true)], 2);
};
}
}), [["__scopeId", "data-v-5923489c"]]);
export { getTextareaCursorPosition as a, script$1 as c, getParentNodes as i, NodeExecuteButton_default as n, getUpdatedTextareaValue as o, generateCodeForAiTransform as r, script as s, VirtualSchema_default as t };