n8n-editor-ui
Version:
Workflow Editor UI for n8n
587 lines (581 loc) • 24.8 kB
JavaScript
;
(function () {
System.register(["./chunk-legacy-Re9CbdfL.js", "./vue.runtime.esm-bundler-legacy--Sn1h8On.js", "./_MapCache-legacy-DlYNX_X1.js", "./truncate-legacy-BGje52Or.js", "./icon-legacy-CqVXaCIS.js", "./useTelemetry-legacy-B3pzzkAE.js", "./constants-legacy-TKxWCqvM.js", "./useExternalHooks-legacy-8pg6K3ln.js", "./useStyles-legacy-E8NPEyeD.js"], function (_export, _context) {
"use strict";
var __toESM, computed, toDisplayString, createElementBlock, createCommentVNode, unref, ref, createVNode, defineComponent, createBlock, toValue, isVNode, onMounted, Fragment, renderSlot, normalizeStyle, withCtx, resolveDynamicComponent, openBlock, withModifiers, isString, Transition, render, vShow, init_shared_esm_bundler, normalizeClass, createBaseVNode, withDirectives, useI18n, definePropType, isElement, useTimeoutFn, buildProps, isClient, useGlobalComponentSettings, debugWarn, require_lib, useEventListener, CloseComponents, iconPropType, _export_sfc, withInstallFunction, EVENT_CODE, ElIcon, TypeComponentsMap, useUIStore, useWorkflowsStore, useTelemetry, ALLOWED_HTML_TAGS, VIEWS, ALLOWED_HTML_ATTRIBUTES, useExternalHooks, useStyles, notificationTypes, notificationProps, notificationEmits, _hoisted_1, _hoisted_2, _hoisted_3, _hoisted_4, NotificationConstructor, notifications, GAP_SIZE, seed, notify, ElNotification, import_lib, sanitizeIfString, capitalizeFirstLetter, getBannerRowHeight, scrollbarWidth, stickyNotificationQueue;
function close(id, position, userOnClose) {
const orientedNotifications = notifications[position];
const idx = orientedNotifications.findIndex(({
vm: vm2
}) => {
var _a;
return ((_a = vm2.component) == null ? void 0 : _a.props.id) === id;
});
if (idx === -1) return;
const {
vm
} = orientedNotifications[idx];
if (!vm) return;
userOnClose?.(vm);
const removedHeight = vm.el.offsetHeight;
const verticalPos = position.split("-")[0];
orientedNotifications.splice(idx, 1);
const len = orientedNotifications.length;
if (len < 1) return;
for (let i = idx; i < len; i++) {
const {
el,
component
} = orientedNotifications[i].vm;
const pos = Number.parseInt(el.style[verticalPos], 10) - removedHeight - GAP_SIZE;
component.props.offset = pos;
}
}
function closeAll() {
for (const orientedNotifications of Object.values(notifications)) orientedNotifications.forEach(({
vm
}) => {
vm.component.exposed.visible.value = false;
});
}
function sanitizeHtml(dirtyHtml) {
return (0, import_lib.default)(dirtyHtml, {
onTagAttr: (tag, name, value) => {
if (tag === "img" && name === "src") {
const isStaticImageFile = value.split("#")[0].match(/\.(jpeg|jpg|gif|png|webp)$/) !== null && value.startsWith("/static/");
if (!value.startsWith("https://") && !isStaticImageFile) return "";
}
if (ALLOWED_HTML_ATTRIBUTES.includes(name) || name.startsWith("data-")) {
if (name === "href" && !value.match(/^https?:\/\//gm) && !value.startsWith("/")) return "";
return `${name}="${(0, import_lib.escapeAttrValue)(value)}"`;
}
},
onTag: tag => {
if (!ALLOWED_HTML_TAGS.includes(tag)) return "";
}
});
}
/**
* Checks if the input is a string and sanitizes it by removing or escaping harmful characters,
* returning the original input if it's not a string.
*/
function isOutsideSelected(el) {
const selection = document.getSelection();
if (!selection?.anchorNode || !selection.focusNode || !el) return false;
return !el.contains(selection.anchorNode) && !el.contains(selection.focusNode) && (selection.anchorNode !== selection.focusNode || selection.anchorOffset !== selection.focusOffset);
}
function getScrollbarWidth() {
if (scrollbarWidth !== void 0) return scrollbarWidth;
const outer = document.createElement("div");
const inner = document.createElement("div");
outer.style.visibility = "hidden";
outer.style.overflow = "scroll";
document.body.appendChild(outer);
outer.appendChild(inner);
scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
outer.parentElement?.removeChild(outer);
return scrollbarWidth;
}
function isEventTargetContainedBy(eventTarget, maybeContainer) {
return !!(eventTarget instanceof Node && toValue(maybeContainer)?.contains(eventTarget));
}
//#endregion
//#region src/app/composables/useToast.ts
function useToast() {
const telemetry = useTelemetry();
const workflowsStore = useWorkflowsStore();
const uiStore = useUIStore();
const externalHooks = useExternalHooks();
const i18n = useI18n();
const {
APP_Z_INDEXES
} = useStyles();
function showMessage(messageData, track = true) {
const messageDefaults = {
dangerouslyUseHTMLString: true,
position: "bottom-right",
zIndex: APP_Z_INDEXES.TOASTS,
appendTo: "#app-grid",
customClass: "content-toast"
};
const {
message,
title
} = messageData;
const params = {
...messageDefaults,
...messageData
};
if (typeof message === "string") params.message = sanitizeHtml(message);
if (typeof title === "string") params.title = sanitizeHtml(title);
const notification = ElNotification(params);
if (params.duration === 0) stickyNotificationQueue.push(notification);
if (params.type === "error" && track) {
let messageForTelemetry;
if (typeof params.message === "string") messageForTelemetry = params.message;else if (params.message && typeof params.message === "object" && "props" in params.message && params.message.props) {
const props = params.message.props;
const hasErrorMessage = typeof props === "object" && props !== null && "errorMessage" in props;
const hasMessage = typeof props === "object" && props !== null && "message" in props;
if (hasErrorMessage) messageForTelemetry = String(props.errorMessage);else if (hasMessage) messageForTelemetry = String(props.message);else messageForTelemetry = "Unknown error";
} else messageForTelemetry = "Unknown error";
telemetry.track("Instance FE emitted error", {
error_title: params.title,
error_message: messageForTelemetry,
caused_by_credential: causedByCredential(messageForTelemetry),
workflow_id: workflowsStore.workflowId
});
}
return notification;
}
function showToast(config) {
let notification;
if (config.closeOnClick) {
const cb = config.onClick;
config.onClick = () => {
if (notification) notification.close();
if (cb) cb();
};
}
notification = showMessage({
title: config.title,
message: config.message,
onClick: config.onClick,
onClose: config.onClose,
duration: config.duration,
customClass: config.customClass,
type: config.type
});
return notification;
}
function collapsableDetails({
description,
node
}) {
if (!description) return "";
const errorDescription = description.length > 500 ? `${description.slice(0, 500)}...` : description;
return `
<br>
<br>
<details>
<summary
style="color: #ff6d5a; font-weight: bold; cursor: pointer;"
>
${i18n.baseText("showMessage.showDetails")}
</summary>
<p>${node.name}: ${errorDescription}</p>
</details>
`;
}
function showError(e, title, message) {
const error = e;
showMessage({
title,
message: `
${message ? `${message}<br/>` : ""}
<i>${error.message}</i>
${collapsableDetails(error)}`,
type: "error",
duration: 0
}, false);
externalHooks.run("showMessage.showError", {
title,
message,
errorMessage: error.message
});
telemetry.track("Instance FE emitted error", {
error_title: title,
error_description: message,
error_message: error.message,
caused_by_credential: causedByCredential(error.message),
workflow_id: workflowsStore.workflowId
});
}
function causedByCredential(message) {
if (!message || typeof message !== "string") return false;
return message.includes("Credentials for") && message.includes("are not set");
}
function clearAllStickyNotifications() {
stickyNotificationQueue.forEach(notification => {
if (notification) notification.close();
});
stickyNotificationQueue.length = 0;
}
function showNotificationForViews(views) {
const notifications$1 = [];
views.forEach(view => {
notifications$1.push(...(uiStore.pendingNotificationsForViews[view] ?? []));
});
if (notifications$1.length) {
notifications$1.forEach(async notification => {
setTimeout(() => {
showMessage(notification);
}, 5);
});
uiStore.setNotificationsForView(VIEWS.WORKFLOW, []);
}
}
return {
showMessage,
showToast,
showError,
clearAllStickyNotifications,
showNotificationForViews
};
}
//#endregion
_export({
a: isEventTargetContainedBy,
i: getScrollbarWidth,
o: isOutsideSelected,
s: sanitizeHtml,
t: useToast
});
return {
setters: [function (_chunkLegacy001Js) {
__toESM = _chunkLegacy001Js.o;
}, function (_vueRuntimeEsmBundlerLegacy003Js) {
computed = _vueRuntimeEsmBundlerLegacy003Js.C;
toDisplayString = _vueRuntimeEsmBundlerLegacy003Js.Cn;
createElementBlock = _vueRuntimeEsmBundlerLegacy003Js.D;
createCommentVNode = _vueRuntimeEsmBundlerLegacy003Js.E;
unref = _vueRuntimeEsmBundlerLegacy003Js.Gt;
ref = _vueRuntimeEsmBundlerLegacy003Js.It;
createVNode = _vueRuntimeEsmBundlerLegacy003Js.M;
defineComponent = _vueRuntimeEsmBundlerLegacy003Js.P;
createBlock = _vueRuntimeEsmBundlerLegacy003Js.T;
toValue = _vueRuntimeEsmBundlerLegacy003Js.Ut;
isVNode = _vueRuntimeEsmBundlerLegacy003Js.V;
onMounted = _vueRuntimeEsmBundlerLegacy003Js.Z;
Fragment = _vueRuntimeEsmBundlerLegacy003Js._;
renderSlot = _vueRuntimeEsmBundlerLegacy003Js.at;
normalizeStyle = _vueRuntimeEsmBundlerLegacy003Js.bn;
withCtx = _vueRuntimeEsmBundlerLegacy003Js.bt;
resolveDynamicComponent = _vueRuntimeEsmBundlerLegacy003Js.ct;
openBlock = _vueRuntimeEsmBundlerLegacy003Js.et;
withModifiers = _vueRuntimeEsmBundlerLegacy003Js.h;
isString = _vueRuntimeEsmBundlerLegacy003Js.mn;
Transition = _vueRuntimeEsmBundlerLegacy003Js.n;
render = _vueRuntimeEsmBundlerLegacy003Js.o;
vShow = _vueRuntimeEsmBundlerLegacy003Js.p;
init_shared_esm_bundler = _vueRuntimeEsmBundlerLegacy003Js.tn;
normalizeClass = _vueRuntimeEsmBundlerLegacy003Js.vn;
createBaseVNode = _vueRuntimeEsmBundlerLegacy003Js.w;
withDirectives = _vueRuntimeEsmBundlerLegacy003Js.xt;
}, function (_MapCacheLegacy005Js) {
useI18n = _MapCacheLegacy005Js.gt;
}, function (_truncateLegacy00hJs) {
definePropType = _truncateLegacy00hJs.F;
isElement = _truncateLegacy00hJs.J;
useTimeoutFn = _truncateLegacy00hJs.Lt;
buildProps = _truncateLegacy00hJs.P;
isClient = _truncateLegacy00hJs.Pt;
useGlobalComponentSettings = _truncateLegacy00hJs.S;
debugWarn = _truncateLegacy00hJs.V;
require_lib = _truncateLegacy00hJs.b;
useEventListener = _truncateLegacy00hJs.kt;
}, function (_iconLegacy00nJs) {
CloseComponents = _iconLegacy00nJs.c;
iconPropType = _iconLegacy00nJs.f;
_export_sfc = _iconLegacy00nJs.n;
withInstallFunction = _iconLegacy00nJs.o;
EVENT_CODE = _iconLegacy00nJs.r;
ElIcon = _iconLegacy00nJs.t;
TypeComponentsMap = _iconLegacy00nJs.u;
}, function (_useTelemetryLegacy00HJs) {
useUIStore = _useTelemetryLegacy00HJs.Fi;
useWorkflowsStore = _useTelemetryLegacy00HJs.o;
useTelemetry = _useTelemetryLegacy00HJs.t;
}, function (_constantsLegacy00TJs) {
ALLOWED_HTML_TAGS = _constantsLegacy00TJs.A;
VIEWS = _constantsLegacy00TJs.Io;
ALLOWED_HTML_ATTRIBUTES = _constantsLegacy00TJs.k;
}, function (_useExternalHooksLegacy013Js) {
useExternalHooks = _useExternalHooksLegacy013Js.t;
}, function (_useStylesLegacy015Js) {
useStyles = _useStylesLegacy015Js.t;
}],
execute: function () {
//#region ../../../node_modules/.pnpm/element-plus@2.4.3_patch_hash=3bc4ea0a42ad52c6bbc3d06c12c2963d55b57d6b5b8d436e46e7fd8ff_87815230ec1d292541fa9e3e306612e7/node_modules/element-plus/es/components/notification/src/notification.mjs
notificationTypes = ["success", "info", "warning", "error"];
notificationProps = buildProps({
customClass: {
type: String,
default: ""
},
dangerouslyUseHTMLString: {
type: Boolean,
default: false
},
duration: {
type: Number,
default: 4500
},
icon: {
type: iconPropType
},
id: {
type: String,
default: ""
},
message: {
type: definePropType([String, Object]),
default: ""
},
offset: {
type: Number,
default: 0
},
onClick: {
type: definePropType(Function),
default: () => void 0
},
onClose: {
type: definePropType(Function),
required: true
},
position: {
type: String,
values: ["top-right", "top-left", "bottom-right", "bottom-left"],
default: "top-right"
},
showClose: {
type: Boolean,
default: true
},
title: {
type: String,
default: ""
},
type: {
type: String,
values: [...notificationTypes, ""],
default: ""
},
zIndex: Number
});
notificationEmits = {
destroy: () => true
}; //#endregion
//#region ../../../node_modules/.pnpm/element-plus@2.4.3_patch_hash=3bc4ea0a42ad52c6bbc3d06c12c2963d55b57d6b5b8d436e46e7fd8ff_87815230ec1d292541fa9e3e306612e7/node_modules/element-plus/es/components/notification/src/notification2.mjs
_hoisted_1 = ["id"];
_hoisted_2 = ["textContent"];
_hoisted_3 = {
key: 0
};
_hoisted_4 = ["innerHTML"];
NotificationConstructor = /* @__PURE__ */_export_sfc(/* @__PURE__ */defineComponent({
...defineComponent({
name: "ElNotification"
}),
props: notificationProps,
emits: notificationEmits,
setup(__props, {
expose
}) {
const props = __props;
const {
ns,
zIndex
} = useGlobalComponentSettings("notification");
const {
nextZIndex,
currentZIndex
} = zIndex;
const {
Close
} = CloseComponents;
const visible = ref(false);
let timer = void 0;
const typeClass = computed(() => {
const type = props.type;
return type && TypeComponentsMap[props.type] ? ns.m(type) : "";
});
const iconComponent = computed(() => {
if (!props.type) return props.icon;
return TypeComponentsMap[props.type] || props.icon;
});
const horizontalClass = computed(() => props.position.endsWith("right") ? "right" : "left");
const verticalProperty = computed(() => props.position.startsWith("top") ? "top" : "bottom");
const positionStyle = computed(() => {
var _a;
return {
[verticalProperty.value]: `${props.offset}px`,
zIndex: (_a = props.zIndex) != null ? _a : currentZIndex.value
};
});
function startTimer() {
if (props.duration > 0) ({
stop: timer
} = useTimeoutFn(() => {
if (visible.value) close$1();
}, props.duration));
}
function clearTimer() {
timer?.();
}
function close$1() {
visible.value = false;
}
function onKeydown({
code
}) {
if (code === EVENT_CODE.delete || code === EVENT_CODE.backspace) clearTimer();else if (code === EVENT_CODE.esc) {
if (visible.value) close$1();
} else startTimer();
}
onMounted(() => {
startTimer();
nextZIndex();
visible.value = true;
});
useEventListener(document, "keydown", onKeydown);
expose({
visible,
close: close$1
});
return (_ctx, _cache) => {
return openBlock(), createBlock(Transition, {
name: unref(ns).b("fade"),
onBeforeLeave: _ctx.onClose,
onAfterLeave: _cache[1] || (_cache[1] = $event => _ctx.$emit("destroy")),
persisted: ""
}, {
default: withCtx(() => [withDirectives(createBaseVNode("div", {
id: _ctx.id,
class: normalizeClass([unref(ns).b(), _ctx.customClass, unref(horizontalClass)]),
style: normalizeStyle(unref(positionStyle)),
role: "alert",
onMouseenter: clearTimer,
onMouseleave: startTimer,
onClick: _cache[0] || (_cache[0] = (...args) => _ctx.onClick && _ctx.onClick(...args))
}, [unref(iconComponent) ? (openBlock(), createBlock(unref(ElIcon), {
key: 0,
class: normalizeClass([unref(ns).e("icon"), unref(typeClass)])
}, {
default: withCtx(() => [(openBlock(), createBlock(resolveDynamicComponent(unref(iconComponent))))]),
_: 1
}, 8, ["class"])) : createCommentVNode("v-if", true), createBaseVNode("div", {
class: normalizeClass(unref(ns).e("group"))
}, [createBaseVNode("h2", {
class: normalizeClass(unref(ns).e("title")),
textContent: toDisplayString(_ctx.title)
}, null, 10, _hoisted_2), withDirectives(createBaseVNode("div", {
class: normalizeClass(unref(ns).e("content")),
style: normalizeStyle(!!_ctx.title ? void 0 : {
margin: 0
})
}, [renderSlot(_ctx.$slots, "default", {}, () => [!_ctx.dangerouslyUseHTMLString ? (openBlock(), createElementBlock("p", _hoisted_3, toDisplayString(_ctx.message), 1)) : (openBlock(), createElementBlock(Fragment, {
key: 1
}, [createCommentVNode(" Caution here, message could've been compromised, never use user's input as message "), createBaseVNode("p", {
innerHTML: _ctx.message
}, null, 8, _hoisted_4)], 2112))])], 6), [[vShow, _ctx.message]]), _ctx.showClose ? (openBlock(), createBlock(unref(ElIcon), {
key: 0,
class: normalizeClass(unref(ns).e("closeBtn")),
onClick: withModifiers(close$1, ["stop"])
}, {
default: withCtx(() => [createVNode(unref(Close))]),
_: 1
}, 8, ["class", "onClick"])) : createCommentVNode("v-if", true)], 2)], 46, _hoisted_1), [[vShow, visible.value]])]),
_: 3
}, 8, ["name", "onBeforeLeave"]);
};
}
}), [["__file", "/home/runner/work/element-plus/element-plus/packages/components/notification/src/notification.vue"]]); //#endregion
//#region ../../../node_modules/.pnpm/element-plus@2.4.3_patch_hash=3bc4ea0a42ad52c6bbc3d06c12c2963d55b57d6b5b8d436e46e7fd8ff_87815230ec1d292541fa9e3e306612e7/node_modules/element-plus/es/components/notification/src/notify.mjs
init_shared_esm_bundler();
notifications = {
"top-left": [],
"top-right": [],
"bottom-left": [],
"bottom-right": []
};
GAP_SIZE = 16;
seed = 1;
notify = function (options = {}, context = null) {
if (!isClient) return {
close: () => void 0
};
if (typeof options === "string" || isVNode(options)) options = {
message: options
};
const position = options.position || "top-right";
let verticalOffset = options.offset || 0;
notifications[position].forEach(({
vm: vm2
}) => {
var _a;
verticalOffset += (((_a = vm2.el) == null ? void 0 : _a.offsetHeight) || 0) + GAP_SIZE;
});
verticalOffset += GAP_SIZE;
const id = `notification_${seed++}`;
const userOnClose = options.onClose;
const props = {
...options,
offset: verticalOffset,
id,
onClose: () => {
close(id, position, userOnClose);
}
};
let appendTo = document.body;
if (isElement(options.appendTo)) appendTo = options.appendTo;else if (isString(options.appendTo)) appendTo = document.querySelector(options.appendTo);
if (!isElement(appendTo)) {
debugWarn("ElNotification", "the appendTo option is not an HTMLElement. Falling back to document.body.");
appendTo = document.body;
}
const container = document.createElement("div");
const vm = createVNode(NotificationConstructor, props, isVNode(props.message) ? {
default: () => props.message
} : null);
vm.appContext = context != null ? context : notify._context;
vm.props.onDestroy = () => {
render(null, container);
};
render(vm, container);
notifications[position].push({
vm
});
appendTo.appendChild(container.firstElementChild);
return {
close: () => {
vm.component.exposed.visible.value = false;
}
};
};
notificationTypes.forEach(type => {
notify[type] = (options = {}) => {
if (typeof options === "string" || isVNode(options)) options = {
message: options
};
return notify({
...options,
type
});
};
});
notify.closeAll = closeAll;
notify._context = null;
//#endregion
//#region ../../../node_modules/.pnpm/element-plus@2.4.3_patch_hash=3bc4ea0a42ad52c6bbc3d06c12c2963d55b57d6b5b8d436e46e7fd8ff_87815230ec1d292541fa9e3e306612e7/node_modules/element-plus/es/components/notification/index.mjs
ElNotification = withInstallFunction(notify, "$notify"); //#endregion
//#region src/app/utils/htmlUtils.ts
import_lib = /* @__PURE__ */__toESM(require_lib());
_export("c", sanitizeIfString = message => {
if (typeof message === "string") return sanitizeHtml(message);
return message;
});
_export("n", capitalizeFirstLetter = text => {
return text.charAt(0).toUpperCase() + text.slice(1);
});
_export("r", getBannerRowHeight = async () => {
return await new Promise(resolve => {
setTimeout(() => {
resolve(document.getElementById("banners")?.clientHeight ?? 0);
}, 0);
});
});
stickyNotificationQueue = [];
}
};
});
})();