2229 lines
		
	
	
		
			84 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			2229 lines
		
	
	
		
			84 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
| import { d as defineComponent, r as ref, x as computed, e as createBlock, f as createCommentVNode, g as openBlock, l as unref, y as N8nPopoverReka, w as withCtx, j as createBaseVNode, n as normalizeClass, i as createVNode, h as createElementBlock, m as N8nHeading, k as createTextVNode, t as toDisplayString, z as N8nCallout, F as Fragment, A as renderList, B as withModifiers, N as N8nIcon, p as N8nText, C as N8nLink, D as useI18n, _ as _export_sfc, E as ElDropdown, G as N8nAvatar, H as ElDropdownMenu, I as ElDropdownItem, J as N8nUserInfo, K as mergeProps, L as MAIN_HEADER_TABS, O as N8nRadioButtons, P as useDebounce, Q as useUIStore, R as BREAKPOINT_SM, S as BREAKPOINT_XL, T as BREAKPOINT_LG, U as BREAKPOINT_MD, o as onMounted, W as onBeforeUnmount, X as renderSlot, Y as nextTick, Z as getBannerRowHeight, $ as defineStore, a0 as usePushConnectionStore, a1 as useWorkflowsStore, u as useUsersStore, a2 as useRoute, a3 as PLACEHOLDER_EMPTY_WORKFLOW_ID, a4 as STORES, a5 as TIME, a6 as useDocumentVisibility, a7 as watch, V as VIEWS, a8 as resolveComponent, a9 as Tooltip, aa as _sfc_main$d, c as useI18n$1, ab as I18nT, b as useRouter, ac as useEvaluationStore, ad as useNodeTypesStore, ae as useWorkflowSettingsCache, af as useSourceControlStore, ag as WORKFLOW_ACTIVE_MODAL_KEY, ah as ERROR_WORKFLOW_DOCS_URL, ai as EVALUATIONS_DOCS_URL, aj as TIME_SAVED_DOCS_URL, ak as WORKFLOW_SETTINGS_MODAL_KEY, al as useTelemetry, am as useMessage, an as MODAL_CONFIRM, ao as debounce, ap as normalizeStyle, aq as createEventBus, ar as useTagsStore, as as useCssModule, at as useRootStore, v as useSettingsStore, au as useProjectsStore, av as useFoldersStore, aw as useNpsSurveyStore, a as useToast, ax as useDocumentTitle, ay as useWorkflowSaving, az as useWorkflowHelpers, aA as usePageRedirectionHelper, aB as getResourcePermissions, aC as WORKFLOW_MENU_ACTIONS, aD as hasPermission, aE as EnterpriseEditionFeature, aF as ProjectTypes, aG as useTemplateRef, aH as InlineRename, aI as MAX_WORKFLOW_NAME_LENGTH, aJ as _sfc_main$e, aK as N8nBadge, q as N8nButton, aL as SaveButton, aM as N8nActionDropdown, aN as WORKFLOW_SHARE_MODAL_KEY, aO as nodeViewEventBus, aP as IMPORT_WORKFLOW_URL_MODAL_KEY, aQ as FileSaver_minExports, aR as DUPLICATE_MODAL_KEY, aS as ResourceType, aT as PROJECT_MOVE_RESOURCE_MODAL, aU as __vitePreload, aV as hyphenate, aW as h, aX as hasOwn, aY as useNDVStore, aZ as useExecutionsStore, a_ as useLocalStorage, a$ as LOCAL_STORAGE_HIDE_GITHUB_STAR_BUTTON, b0 as STICKY_NODE_TYPE, b1 as onBeforeMount, b2 as withDirectives, b3 as vShow, b4 as N8N_MAIN_GITHUB_REPO_URL } from "./index--OJ5nhDf.js";
 | |
| import { _ as __unplugin_components_0, W as WorkflowActivator } from "./WorkflowActivator-DZIgVvgL.js";
 | |
| import { u as useBeforeUnload } from "./useBeforeUnload-ZtUpNFCu.js";
 | |
| import { _ as _sfc_main$f } from "./PushConnectionTracker.vue_vue_type_script_setup_true_lang-zVio8c2n.js";
 | |
| import { T as Tag } from "./Tag-BQAJRKFE.js";
 | |
| import { u as usePushConnection } from "./usePushConnection-Db77V5LR.js";
 | |
| import "./ProjectBreadcrumb-BxMwihEP.js";
 | |
| import "./useWorkflowActivate-7Rw9KyzM.js";
 | |
| import "./global-link-actions--TiC75iP.js";
 | |
| import "./readyToRunWorkflows.store-Dhb8bhvk.js";
 | |
| const _hoisted_1$4 = ["data-action-id", "onClick"];
 | |
| const _hoisted_2$3 = { key: 0 };
 | |
| const _sfc_main$c = /* @__PURE__ */ defineComponent({
 | |
|   ...{ name: "N8nSuggestedActions" },
 | |
|   __name: "SuggestedActions",
 | |
|   props: {
 | |
|     title: {},
 | |
|     actions: {},
 | |
|     open: { type: Boolean },
 | |
|     ignoreAllLabel: { default: void 0 },
 | |
|     popoverAlignment: { default: void 0 },
 | |
|     notice: { default: void 0 }
 | |
|   },
 | |
|   emits: ["action-click", "ignore-click", "ignore-all", "update:open"],
 | |
|   setup(__props, { emit: __emit }) {
 | |
|     const props = __props;
 | |
|     const emit = __emit;
 | |
|     const { t } = useI18n();
 | |
|     const ignoringActions = ref(/* @__PURE__ */ new Set());
 | |
|     const completedCount = computed(() => props.actions.filter((action) => action.completed).length);
 | |
|     const handleActionClick = (action) => {
 | |
|       if (!action.completed) {
 | |
|         emit("action-click", action.id);
 | |
|       }
 | |
|     };
 | |
|     const handleIgnoreClick = (actionId) => {
 | |
|       ignoringActions.value.add(actionId);
 | |
|       setTimeout(() => {
 | |
|         emit("ignore-click", actionId);
 | |
|         ignoringActions.value.delete(actionId);
 | |
|       }, 500);
 | |
|     };
 | |
|     return (_ctx, _cache) => {
 | |
|       return completedCount.value !== _ctx.actions.length ? (openBlock(), createBlock(unref(N8nPopoverReka), {
 | |
|         key: 0,
 | |
|         open: _ctx.open,
 | |
|         width: "360px",
 | |
|         "max-height": "500px",
 | |
|         align: _ctx.popoverAlignment,
 | |
|         "onUpdate:open": _cache[2] || (_cache[2] = ($event) => _ctx.$emit("update:open", $event))
 | |
|       }, {
 | |
|         trigger: withCtx(() => [
 | |
|           createBaseVNode("div", {
 | |
|             class: normalizeClass([_ctx.$style.triggerContainer, _ctx.open ? _ctx.$style.activeTrigger : ""]),
 | |
|             "data-test-id": "suggested-action-count"
 | |
|           }, [
 | |
|             createVNode(unref(Tag), {
 | |
|               text: `${completedCount.value} / ${_ctx.actions.length}`
 | |
|             }, null, 8, ["text"])
 | |
|           ], 2)
 | |
|         ]),
 | |
|         content: withCtx(() => [
 | |
|           createBaseVNode("div", {
 | |
|             class: normalizeClass(_ctx.$style.popoverContent)
 | |
|           }, [
 | |
|             createVNode(unref(N8nHeading), { tag: "h4" }, {
 | |
|               default: withCtx(() => [
 | |
|                 createTextVNode(toDisplayString(_ctx.title), 1)
 | |
|               ]),
 | |
|               _: 1
 | |
|             }),
 | |
|             _ctx.notice ? (openBlock(), createBlock(unref(N8nCallout), {
 | |
|               key: 0,
 | |
|               theme: "warning"
 | |
|             }, {
 | |
|               default: withCtx(() => [
 | |
|                 createTextVNode(toDisplayString(_ctx.notice), 1)
 | |
|               ]),
 | |
|               _: 1
 | |
|             })) : createCommentVNode("", true),
 | |
|             (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.actions, (action) => {
 | |
|               return openBlock(), createElementBlock("div", {
 | |
|                 key: action.id,
 | |
|                 class: normalizeClass([
 | |
|                   {
 | |
|                     [_ctx.$style.actionItem]: true,
 | |
|                     [_ctx.$style.ignoring]: ignoringActions.value.has(action.id),
 | |
|                     [_ctx.$style.actionable]: !action.completed
 | |
|                   }
 | |
|                 ]),
 | |
|                 "data-test-id": "suggested-action-item",
 | |
|                 "data-action-id": action.id,
 | |
|                 onClick: withModifiers(() => handleActionClick(action), ["prevent", "stop"])
 | |
|               }, [
 | |
|                 createBaseVNode("div", {
 | |
|                   class: normalizeClass(_ctx.$style.checkboxContainer)
 | |
|                 }, [
 | |
|                   action.completed ? (openBlock(), createBlock(unref(N8nIcon), {
 | |
|                     key: 0,
 | |
|                     icon: "circle-check",
 | |
|                     color: "success"
 | |
|                   })) : (openBlock(), createBlock(unref(N8nIcon), {
 | |
|                     key: 1,
 | |
|                     icon: "circle",
 | |
|                     color: "foreground-dark"
 | |
|                   }))
 | |
|                 ], 2),
 | |
|                 createBaseVNode("div", {
 | |
|                   class: normalizeClass(_ctx.$style.actionItemBody)
 | |
|                 }, [
 | |
|                   createBaseVNode("div", {
 | |
|                     class: normalizeClass([action.completed ? "" : "mb-3xs", _ctx.$style.actionHeader])
 | |
|                   }, [
 | |
|                     createVNode(unref(N8nText), {
 | |
|                       size: "medium",
 | |
|                       bold: true
 | |
|                     }, {
 | |
|                       default: withCtx(() => [
 | |
|                         createTextVNode(toDisplayString(action.title), 1)
 | |
|                       ]),
 | |
|                       _: 2
 | |
|                     }, 1024)
 | |
|                   ], 2),
 | |
|                   !action.completed ? (openBlock(), createElementBlock("div", _hoisted_2$3, [
 | |
|                     createVNode(unref(N8nText), {
 | |
|                       size: "small",
 | |
|                       color: "text-base"
 | |
|                     }, {
 | |
|                       default: withCtx(() => [
 | |
|                         createTextVNode(toDisplayString(action.description) + " ", 1),
 | |
|                         action.moreInfoLink ? (openBlock(), createBlock(unref(N8nLink), {
 | |
|                           key: 0,
 | |
|                           to: action.moreInfoLink,
 | |
|                           size: "small",
 | |
|                           theme: "text",
 | |
|                           "new-window": "",
 | |
|                           underline: "",
 | |
|                           onClick: _cache[0] || (_cache[0] = withModifiers(() => {
 | |
|                           }, ["stop"]))
 | |
|                         }, {
 | |
|                           default: withCtx(() => [
 | |
|                             createTextVNode(toDisplayString(unref(t)("generic.moreInfo")), 1)
 | |
|                           ]),
 | |
|                           _: 2
 | |
|                         }, 1032, ["to"])) : createCommentVNode("", true)
 | |
|                       ]),
 | |
|                       _: 2
 | |
|                     }, 1024)
 | |
|                   ])) : createCommentVNode("", true)
 | |
|                 ], 2),
 | |
|                 createVNode(unref(N8nLink), {
 | |
|                   theme: "text",
 | |
|                   title: unref(t)("generic.ignore"),
 | |
|                   "data-test-id": "suggested-action-ignore",
 | |
|                   onClick: withModifiers(($event) => handleIgnoreClick(action.id), ["prevent", "stop"])
 | |
|                 }, {
 | |
|                   default: withCtx(() => [
 | |
|                     !action.completed ? (openBlock(), createBlock(unref(N8nIcon), {
 | |
|                       key: 0,
 | |
|                       icon: "x",
 | |
|                       size: "large"
 | |
|                     })) : createCommentVNode("", true)
 | |
|                   ]),
 | |
|                   _: 2
 | |
|                 }, 1032, ["title", "onClick"])
 | |
|               ], 10, _hoisted_1$4);
 | |
|             }), 128)),
 | |
|             createBaseVNode("div", {
 | |
|               class: normalizeClass(_ctx.$style.ignoreAllContainer)
 | |
|             }, [
 | |
|               createVNode(unref(N8nLink), {
 | |
|                 theme: "text",
 | |
|                 size: "small",
 | |
|                 underline: "",
 | |
|                 "data-test-id": "suggested-action-ignore-all",
 | |
|                 onClick: _cache[1] || (_cache[1] = withModifiers(($event) => emit("ignore-all"), ["prevent", "stop"]))
 | |
|               }, {
 | |
|                 default: withCtx(() => [
 | |
|                   createTextVNode(toDisplayString(_ctx.ignoreAllLabel ?? unref(t)("generic.ignoreAll")), 1)
 | |
|                 ]),
 | |
|                 _: 1
 | |
|               })
 | |
|             ], 2)
 | |
|           ], 2)
 | |
|         ]),
 | |
|         _: 1
 | |
|       }, 8, ["open", "align"])) : createCommentVNode("", true);
 | |
|     };
 | |
|   }
 | |
| });
 | |
| const triggerContainer = "_triggerContainer_1gki6_123";
 | |
| const activeTrigger = "_activeTrigger_1gki6_129";
 | |
| const popoverContent = "_popoverContent_1gki6_134";
 | |
| const actionItem = "_actionItem_1gki6_141";
 | |
| const ignoring = "_ignoring_1gki6_147";
 | |
| const actionable = "_actionable_1gki6_154";
 | |
| const actionHeader = "_actionHeader_1gki6_157";
 | |
| const actionItemBody = "_actionItemBody_1gki6_164";
 | |
| const checkboxContainer = "_checkboxContainer_1gki6_171";
 | |
| const ignoreAllContainer = "_ignoreAllContainer_1gki6_176";
 | |
| const style0$4 = {
 | |
|   triggerContainer,
 | |
|   activeTrigger,
 | |
|   popoverContent,
 | |
|   actionItem,
 | |
|   ignoring,
 | |
|   actionable,
 | |
|   actionHeader,
 | |
|   actionItemBody,
 | |
|   checkboxContainer,
 | |
|   ignoreAllContainer
 | |
| };
 | |
| const cssModules$5 = {
 | |
|   "$style": style0$4
 | |
| };
 | |
| const N8nSuggestedActions = /* @__PURE__ */ _export_sfc(_sfc_main$c, [["__cssModules", cssModules$5]]);
 | |
| const _hoisted_1$3 = {
 | |
|   class: "user-stack",
 | |
|   "data-test-id": "user-stack-container"
 | |
| };
 | |
| const _sfc_main$b = /* @__PURE__ */ defineComponent({
 | |
|   __name: "UserStack",
 | |
|   props: {
 | |
|     users: {},
 | |
|     currentUserEmail: { default: "" },
 | |
|     maxAvatars: { default: 2 },
 | |
|     dropdownTrigger: { default: "hover" }
 | |
|   },
 | |
|   setup(__props) {
 | |
|     const props = __props;
 | |
|     const nonEmptyGroups = computed(() => {
 | |
|       const users = {};
 | |
|       for (const groupName2 in props.users) {
 | |
|         if (props.users[groupName2].length > 0) {
 | |
|           users[groupName2] = props.users[groupName2];
 | |
|         }
 | |
|       }
 | |
|       return users;
 | |
|     });
 | |
|     const groupCount = computed(() => {
 | |
|       return Object.keys(nonEmptyGroups.value).length;
 | |
|     });
 | |
|     const flatUserList = computed(() => {
 | |
|       const users = [];
 | |
|       for (const groupName2 in props.users) {
 | |
|         users.push(...props.users[groupName2]);
 | |
|       }
 | |
|       return users;
 | |
|     });
 | |
|     const visibleAvatarCount = computed(() => {
 | |
|       return flatUserList.value.length >= props.maxAvatars ? props.maxAvatars : flatUserList.value.length;
 | |
|     });
 | |
|     const hiddenUsersCount = computed(() => {
 | |
|       return flatUserList.value.length - visibleAvatarCount.value;
 | |
|     });
 | |
|     const menuHeight = computed(() => {
 | |
|       return groupCount.value > 1 ? 220 : 190;
 | |
|     });
 | |
|     return (_ctx, _cache) => {
 | |
|       return openBlock(), createElementBlock("div", _hoisted_1$3, [
 | |
|         createVNode(unref(ElDropdown), {
 | |
|           trigger: _ctx.$props.dropdownTrigger,
 | |
|           "max-height": menuHeight.value,
 | |
|           "popper-class": "user-stack-popper"
 | |
|         }, {
 | |
|           dropdown: withCtx(() => [
 | |
|             createVNode(unref(ElDropdownMenu), {
 | |
|               class: "user-stack-list",
 | |
|               "data-test-id": "user-stack-list"
 | |
|             }, {
 | |
|               default: withCtx(() => [
 | |
|                 (openBlock(true), createElementBlock(Fragment, null, renderList(nonEmptyGroups.value, (groupUsers2, index) => {
 | |
|                   return openBlock(), createElementBlock("div", { key: index }, [
 | |
|                     createBaseVNode("div", {
 | |
|                       class: normalizeClass(_ctx.$style.groupContainer)
 | |
|                     }, [
 | |
|                       createVNode(unref(ElDropdownItem), null, {
 | |
|                         default: withCtx(() => [
 | |
|                           groupCount.value > 1 ? (openBlock(), createElementBlock("header", {
 | |
|                             key: 0,
 | |
|                             class: normalizeClass(_ctx.$style.groupName)
 | |
|                           }, toDisplayString(index), 3)) : createCommentVNode("", true)
 | |
|                         ]),
 | |
|                         _: 2
 | |
|                       }, 1024),
 | |
|                       createBaseVNode("div", {
 | |
|                         class: normalizeClass(_ctx.$style.groupUsers)
 | |
|                       }, [
 | |
|                         (openBlock(true), createElementBlock(Fragment, null, renderList(groupUsers2, (user) => {
 | |
|                           return openBlock(), createBlock(unref(ElDropdownItem), {
 | |
|                             key: user.id,
 | |
|                             "data-test-id": `user-stack-info-${user.id}`,
 | |
|                             class: normalizeClass(_ctx.$style.userInfoContainer)
 | |
|                           }, {
 | |
|                             default: withCtx(() => [
 | |
|                               createVNode(unref(N8nUserInfo), mergeProps({ ref_for: true }, user, {
 | |
|                                 "is-current-user": user.email === props.currentUserEmail
 | |
|                               }), null, 16, ["is-current-user"])
 | |
|                             ]),
 | |
|                             _: 2
 | |
|                           }, 1032, ["data-test-id", "class"]);
 | |
|                         }), 128))
 | |
|                       ], 2)
 | |
|                     ], 2)
 | |
|                   ]);
 | |
|                 }), 128))
 | |
|               ]),
 | |
|               _: 1
 | |
|             })
 | |
|           ]),
 | |
|           default: withCtx(() => [
 | |
|             createBaseVNode("div", {
 | |
|               class: normalizeClass(_ctx.$style.avatars),
 | |
|               "data-test-id": "user-stack-avatars"
 | |
|             }, [
 | |
|               (openBlock(true), createElementBlock(Fragment, null, renderList(flatUserList.value.slice(0, visibleAvatarCount.value), (user) => {
 | |
|                 return openBlock(), createBlock(unref(N8nAvatar), {
 | |
|                   key: user.id,
 | |
|                   "first-name": user.firstName,
 | |
|                   "last-name": user.lastName,
 | |
|                   class: normalizeClass(_ctx.$style.avatar),
 | |
|                   "data-test-id": `user-stack-avatar-${user.id}`,
 | |
|                   size: "small"
 | |
|                 }, null, 8, ["first-name", "last-name", "class", "data-test-id"]);
 | |
|               }), 128)),
 | |
|               hiddenUsersCount.value > 0 ? (openBlock(), createElementBlock("div", {
 | |
|                 key: 0,
 | |
|                 class: normalizeClass(_ctx.$style.hiddenBadge)
 | |
|               }, "+" + toDisplayString(hiddenUsersCount.value), 3)) : createCommentVNode("", true)
 | |
|             ], 2)
 | |
|           ]),
 | |
|           _: 1
 | |
|         }, 8, ["trigger", "max-height"])
 | |
|       ]);
 | |
|     };
 | |
|   }
 | |
| });
 | |
| const avatars = "_avatars_1u5zw_123";
 | |
| const avatar = "_avatar_1u5zw_123";
 | |
| const hiddenBadge = "_hiddenBadge_1u5zw_133";
 | |
| const groupContainer = "_groupContainer_1u5zw_148";
 | |
| const groupName = "_groupName_1u5zw_156";
 | |
| const groupUsers = "_groupUsers_1u5zw_164";
 | |
| const userInfoContainer = "_userInfoContainer_1u5zw_170";
 | |
| const style0$3 = {
 | |
|   avatars,
 | |
|   avatar,
 | |
|   hiddenBadge,
 | |
|   groupContainer,
 | |
|   groupName,
 | |
|   groupUsers,
 | |
|   userInfoContainer
 | |
| };
 | |
| const cssModules$4 = {
 | |
|   "$style": style0$3
 | |
| };
 | |
| const N8nUserStack = /* @__PURE__ */ _export_sfc(_sfc_main$b, [["__cssModules", cssModules$4]]);
 | |
| const _sfc_main$a = /* @__PURE__ */ defineComponent({
 | |
|   __name: "TabBar",
 | |
|   props: {
 | |
|     items: {},
 | |
|     modelValue: { default: MAIN_HEADER_TABS.WORKFLOW }
 | |
|   },
 | |
|   emits: ["update:modelValue"],
 | |
|   setup(__props, { emit: __emit }) {
 | |
|     const emit = __emit;
 | |
|     function onUpdateModelValue(tab, event) {
 | |
|       emit("update:modelValue", tab, event);
 | |
|     }
 | |
|     return (_ctx, _cache) => {
 | |
|       const _component_N8nRadioButtons = N8nRadioButtons;
 | |
|       return _ctx.items ? (openBlock(), createElementBlock("div", {
 | |
|         key: 0,
 | |
|         class: normalizeClass({
 | |
|           [_ctx.$style.container]: true,
 | |
|           ["tab-bar-container"]: true
 | |
|         })
 | |
|       }, [
 | |
|         createVNode(_component_N8nRadioButtons, {
 | |
|           "model-value": _ctx.modelValue,
 | |
|           options: _ctx.items,
 | |
|           "onUpdate:modelValue": onUpdateModelValue
 | |
|         }, null, 8, ["model-value", "options"])
 | |
|       ], 2)) : createCommentVNode("", true);
 | |
|     };
 | |
|   }
 | |
| });
 | |
| const container$3 = "_container_j6ct2_123";
 | |
| const style0$2 = {
 | |
|   container: container$3
 | |
| };
 | |
| const cssModules$3 = {
 | |
|   "$style": style0$2
 | |
| };
 | |
| const TabBar = /* @__PURE__ */ _export_sfc(_sfc_main$a, [["__cssModules", cssModules$3]]);
 | |
| const _sfc_main$9 = /* @__PURE__ */ defineComponent({
 | |
|   __name: "BreakpointsObserver",
 | |
|   props: {
 | |
|     valueXS: {},
 | |
|     valueXL: {},
 | |
|     valueLG: {},
 | |
|     valueMD: {},
 | |
|     valueSM: {},
 | |
|     valueDefault: {}
 | |
|   },
 | |
|   setup(__props) {
 | |
|     const props = __props;
 | |
|     const { callDebounced } = useDebounce();
 | |
|     const uiStore = useUIStore();
 | |
|     const width = ref(window.innerWidth);
 | |
|     const bp = computed(() => {
 | |
|       if (width.value < BREAKPOINT_SM) {
 | |
|         return "XS";
 | |
|       }
 | |
|       if (width.value >= BREAKPOINT_XL) {
 | |
|         return "XL";
 | |
|       }
 | |
|       if (width.value >= BREAKPOINT_LG) {
 | |
|         return "LG";
 | |
|       }
 | |
|       if (width.value >= BREAKPOINT_MD) {
 | |
|         return "MD";
 | |
|       }
 | |
|       return "SM";
 | |
|     });
 | |
|     const value = computed(() => {
 | |
|       if (props.valueXS && width.value < BREAKPOINT_SM) {
 | |
|         return props.valueXS;
 | |
|       }
 | |
|       if (props.valueXL && width.value >= BREAKPOINT_XL) {
 | |
|         return props.valueXL;
 | |
|       }
 | |
|       if (props.valueLG && width.value >= BREAKPOINT_LG) {
 | |
|         return props.valueLG;
 | |
|       }
 | |
|       if (props.valueMD && width.value >= BREAKPOINT_MD) {
 | |
|         return props.valueMD;
 | |
|       }
 | |
|       if (props.valueSM) {
 | |
|         return props.valueSM;
 | |
|       }
 | |
|       return props.valueDefault;
 | |
|     });
 | |
|     const onResize = () => {
 | |
|       void callDebounced(onResizeEnd, { debounceTime: 50 });
 | |
|     };
 | |
|     const onResizeEnd = async () => {
 | |
|       width.value = window.innerWidth;
 | |
|       await nextTick();
 | |
|       const bannerHeight = await getBannerRowHeight();
 | |
|       uiStore.updateBannersHeight(bannerHeight);
 | |
|     };
 | |
|     onMounted(() => {
 | |
|       window.addEventListener("resize", onResize);
 | |
|     });
 | |
|     onBeforeUnmount(() => {
 | |
|       window.removeEventListener("resize", onResize);
 | |
|     });
 | |
|     return (_ctx, _cache) => {
 | |
|       return openBlock(), createElementBlock("span", null, [
 | |
|         renderSlot(_ctx.$slots, "default", {
 | |
|           bp: bp.value,
 | |
|           value: value.value
 | |
|         })
 | |
|       ]);
 | |
|     };
 | |
|   }
 | |
| });
 | |
| const HEARTBEAT_INTERVAL = 5 * TIME.MINUTE;
 | |
| const useCollaborationStore = defineStore(STORES.COLLABORATION, () => {
 | |
|   const pushStore = usePushConnectionStore();
 | |
|   const workflowsStore = useWorkflowsStore();
 | |
|   const usersStore = useUsersStore();
 | |
|   const uiStore = useUIStore();
 | |
|   const route = useRoute();
 | |
|   const { addBeforeUnloadEventBindings, removeBeforeUnloadEventBindings, addBeforeUnloadHandler } = useBeforeUnload({ route });
 | |
|   const unloadTimeout = ref(null);
 | |
|   addBeforeUnloadHandler(() => {
 | |
|     notifyWorkflowClosed();
 | |
|     if (uiStore.stateIsDirty) {
 | |
|       unloadTimeout.value = setTimeout(() => notifyWorkflowOpened, 5 * TIME.SECOND);
 | |
|     }
 | |
|   });
 | |
|   const collaborators = ref([]);
 | |
|   const heartbeatTimer = ref(null);
 | |
|   const startHeartbeat = () => {
 | |
|     stopHeartbeat();
 | |
|     heartbeatTimer.value = window.setInterval(notifyWorkflowOpened, HEARTBEAT_INTERVAL);
 | |
|   };
 | |
|   const stopHeartbeat = () => {
 | |
|     if (heartbeatTimer.value !== null) {
 | |
|       clearInterval(heartbeatTimer.value);
 | |
|       heartbeatTimer.value = null;
 | |
|     }
 | |
|   };
 | |
|   const pushStoreEventListenerRemovalFn = ref(null);
 | |
|   function initialize() {
 | |
|     if (pushStoreEventListenerRemovalFn.value) {
 | |
|       return;
 | |
|     }
 | |
|     pushStoreEventListenerRemovalFn.value = pushStore.addEventListener((event) => {
 | |
|       if (event.type === "collaboratorsChanged" && event.data.workflowId === workflowsStore.workflowId) {
 | |
|         collaborators.value = event.data.collaborators;
 | |
|       }
 | |
|     });
 | |
|     addBeforeUnloadEventBindings();
 | |
|     notifyWorkflowOpened();
 | |
|     startHeartbeat();
 | |
|   }
 | |
|   function terminate() {
 | |
|     if (typeof pushStoreEventListenerRemovalFn.value === "function") {
 | |
|       pushStoreEventListenerRemovalFn.value();
 | |
|       pushStoreEventListenerRemovalFn.value = null;
 | |
|     }
 | |
|     notifyWorkflowClosed();
 | |
|     stopHeartbeat();
 | |
|     pushStore.clearQueue();
 | |
|     removeBeforeUnloadEventBindings();
 | |
|     if (unloadTimeout.value) {
 | |
|       clearTimeout(unloadTimeout.value);
 | |
|     }
 | |
|   }
 | |
|   function notifyWorkflowOpened() {
 | |
|     const { workflowId } = workflowsStore;
 | |
|     if (workflowId === PLACEHOLDER_EMPTY_WORKFLOW_ID) return;
 | |
|     pushStore.send({ type: "workflowOpened", workflowId });
 | |
|   }
 | |
|   function notifyWorkflowClosed() {
 | |
|     const { workflowId } = workflowsStore;
 | |
|     if (workflowId === PLACEHOLDER_EMPTY_WORKFLOW_ID) return;
 | |
|     pushStore.send({ type: "workflowClosed", workflowId });
 | |
|     collaborators.value = collaborators.value.filter(
 | |
|       ({ user }) => user.id !== usersStore.currentUserId
 | |
|     );
 | |
|   }
 | |
|   return {
 | |
|     collaborators,
 | |
|     initialize,
 | |
|     terminate,
 | |
|     startHeartbeat,
 | |
|     stopHeartbeat
 | |
|   };
 | |
| });
 | |
| const _sfc_main$8 = /* @__PURE__ */ defineComponent({
 | |
|   __name: "CollaborationPane",
 | |
|   setup(__props) {
 | |
|     const collaborationStore = useCollaborationStore();
 | |
|     const usersStore = useUsersStore();
 | |
|     const visibility = useDocumentVisibility();
 | |
|     watch(visibility, (visibilityState) => {
 | |
|       if (visibilityState === "hidden") {
 | |
|         collaborationStore.stopHeartbeat();
 | |
|       } else {
 | |
|         collaborationStore.startHeartbeat();
 | |
|       }
 | |
|     });
 | |
|     const showUserStack = computed(() => collaborationStore.collaborators.length > 1);
 | |
|     const collaboratorsSorted = computed(() => {
 | |
|       const users = collaborationStore.collaborators.map(({ user }) => user);
 | |
|       const index = users.findIndex((user) => user.id === usersStore.currentUser?.id);
 | |
|       if (index < 1) return { defaultGroup: users };
 | |
|       const [currentUser] = users.splice(index, 1);
 | |
|       return { defaultGroup: [currentUser, ...users] };
 | |
|     });
 | |
|     const currentUserEmail = computed(() => usersStore.currentUser?.email);
 | |
|     onMounted(() => {
 | |
|       collaborationStore.initialize();
 | |
|     });
 | |
|     onBeforeUnmount(() => {
 | |
|       collaborationStore.terminate();
 | |
|     });
 | |
|     return (_ctx, _cache) => {
 | |
|       const _component_n8n_user_stack = N8nUserStack;
 | |
|       return openBlock(), createElementBlock("div", {
 | |
|         class: normalizeClass(`collaboration-pane-container ${_ctx.$style.container}`),
 | |
|         "data-test-id": "collaboration-pane"
 | |
|       }, [
 | |
|         showUserStack.value ? (openBlock(), createBlock(_component_n8n_user_stack, {
 | |
|           key: 0,
 | |
|           users: collaboratorsSorted.value,
 | |
|           "current-user-email": currentUserEmail.value
 | |
|         }, null, 8, ["users", "current-user-email"])) : createCommentVNode("", true)
 | |
|       ], 2);
 | |
|     };
 | |
|   }
 | |
| });
 | |
| const container$2 = "_container_ame1i_123";
 | |
| const style0$1 = {
 | |
|   container: container$2
 | |
| };
 | |
| const cssModules$2 = {
 | |
|   "$style": style0$1
 | |
| };
 | |
| const CollaborationPane = /* @__PURE__ */ _export_sfc(_sfc_main$8, [["__cssModules", cssModules$2]]);
 | |
| const _hoisted_1$2 = { key: 0 };
 | |
| const _hoisted_2$2 = { key: 1 };
 | |
| const _sfc_main$7 = /* @__PURE__ */ defineComponent({
 | |
|   __name: "WorkflowHistoryButton",
 | |
|   props: {
 | |
|     workflowId: {},
 | |
|     isNewWorkflow: { type: Boolean },
 | |
|     isFeatureEnabled: { type: Boolean }
 | |
|   },
 | |
|   emits: ["upgrade"],
 | |
|   setup(__props, { emit: __emit }) {
 | |
|     const locale = useI18n$1();
 | |
|     const props = __props;
 | |
|     const emit = __emit;
 | |
|     const workflowHistoryRoute = computed(() => ({
 | |
|       name: VIEWS.WORKFLOW_HISTORY,
 | |
|       params: {
 | |
|         workflowId: props.workflowId
 | |
|       }
 | |
|     }));
 | |
|     return (_ctx, _cache) => {
 | |
|       const _component_N8nIconButton = _sfc_main$d;
 | |
|       const _component_RouterLink = resolveComponent("RouterLink");
 | |
|       const _component_N8nLink = N8nLink;
 | |
|       const _component_N8nTooltip = Tooltip;
 | |
|       return openBlock(), createBlock(_component_N8nTooltip, { placement: "bottom" }, {
 | |
|         content: withCtx(() => [
 | |
|           _ctx.isFeatureEnabled && _ctx.isNewWorkflow ? (openBlock(), createElementBlock("span", _hoisted_1$2, toDisplayString(unref(locale).baseText("workflowHistory.button.tooltip.empty")), 1)) : _ctx.isFeatureEnabled ? (openBlock(), createElementBlock("span", _hoisted_2$2, toDisplayString(unref(locale).baseText("workflowHistory.button.tooltip.enabled")), 1)) : (openBlock(), createBlock(unref(I18nT), {
 | |
|             key: 2,
 | |
|             keypath: "workflowHistory.button.tooltip.disabled",
 | |
|             scope: "global"
 | |
|           }, {
 | |
|             link: withCtx(() => [
 | |
|               createVNode(_component_N8nLink, {
 | |
|                 size: "small",
 | |
|                 onClick: _cache[0] || (_cache[0] = ($event) => emit("upgrade"))
 | |
|               }, {
 | |
|                 default: withCtx(() => [
 | |
|                   createTextVNode(toDisplayString(unref(locale).baseText("workflowHistory.button.tooltip.disabled.link")), 1)
 | |
|                 ]),
 | |
|                 _: 1
 | |
|               })
 | |
|             ]),
 | |
|             _: 1
 | |
|           }))
 | |
|         ]),
 | |
|         default: withCtx(() => [
 | |
|           createVNode(_component_RouterLink, { to: workflowHistoryRoute.value }, {
 | |
|             default: withCtx(() => [
 | |
|               createVNode(_component_N8nIconButton, {
 | |
|                 disabled: _ctx.isNewWorkflow || !_ctx.isFeatureEnabled,
 | |
|                 "data-test-id": "workflow-history-button",
 | |
|                 type: "highlight",
 | |
|                 icon: "history",
 | |
|                 size: "medium"
 | |
|               }, null, 8, ["disabled"])
 | |
|             ]),
 | |
|             _: 1
 | |
|           }, 8, ["to"])
 | |
|         ]),
 | |
|         _: 1
 | |
|       });
 | |
|     };
 | |
|   }
 | |
| });
 | |
| const _sfc_main$6 = /* @__PURE__ */ defineComponent({
 | |
|   __name: "WorkflowProductionChecklist",
 | |
|   props: {
 | |
|     workflow: {}
 | |
|   },
 | |
|   setup(__props) {
 | |
|     const props = __props;
 | |
|     const i18n = useI18n$1();
 | |
|     const router = useRouter();
 | |
|     const evaluationStore = useEvaluationStore();
 | |
|     const nodeTypesStore = useNodeTypesStore();
 | |
|     const workflowsCache = useWorkflowSettingsCache();
 | |
|     const uiStore = useUIStore();
 | |
|     const message = useMessage();
 | |
|     const telemetry = useTelemetry();
 | |
|     const sourceControlStore = useSourceControlStore();
 | |
|     const isPopoverOpen = ref(false);
 | |
|     const cachedSettings = ref(null);
 | |
|     const hasAINode = computed(() => {
 | |
|       const nodes = props.workflow.nodes;
 | |
|       return nodes.some((node) => {
 | |
|         const nodeType = nodeTypesStore.getNodeType(node.type, node.typeVersion);
 | |
|         return nodeType?.codex?.categories?.includes("AI");
 | |
|       });
 | |
|     });
 | |
|     const hasEvaluationSetOutputsNode = computed(() => {
 | |
|       return evaluationStore.evaluationSetOutputsNodeExist;
 | |
|     });
 | |
|     const hasErrorWorkflow = computed(() => {
 | |
|       return !!props.workflow.settings?.errorWorkflow;
 | |
|     });
 | |
|     const hasTimeSaved = computed(() => {
 | |
|       return props.workflow.settings?.timeSavedPerExecution !== void 0;
 | |
|     });
 | |
|     const isActivationModalOpen = computed(() => {
 | |
|       return uiStore.isModalActiveById[WORKFLOW_ACTIVE_MODAL_KEY];
 | |
|     });
 | |
|     const isProtectedEnvironment = computed(() => {
 | |
|       return sourceControlStore.preferences.branchReadOnly;
 | |
|     });
 | |
|     const availableActions = computed(() => {
 | |
|       if (!props.workflow.active || workflowsCache.isCacheLoading.value) {
 | |
|         return [];
 | |
|       }
 | |
|       const actions = [];
 | |
|       const suggestedActionSettings = cachedSettings.value?.suggestedActions ?? {};
 | |
|       if (!suggestedActionSettings.errorWorkflow?.ignored) {
 | |
|         actions.push({
 | |
|           id: "errorWorkflow",
 | |
|           title: i18n.baseText("workflowProductionChecklist.errorWorkflow.title"),
 | |
|           description: i18n.baseText("workflowProductionChecklist.errorWorkflow.description"),
 | |
|           moreInfoLink: ERROR_WORKFLOW_DOCS_URL,
 | |
|           completed: hasErrorWorkflow.value
 | |
|         });
 | |
|       }
 | |
|       if (hasAINode.value && evaluationStore.isEvaluationEnabled && !suggestedActionSettings.evaluations?.ignored) {
 | |
|         actions.push({
 | |
|           id: "evaluations",
 | |
|           title: i18n.baseText("workflowProductionChecklist.evaluations.title"),
 | |
|           description: i18n.baseText("workflowProductionChecklist.evaluations.description"),
 | |
|           moreInfoLink: EVALUATIONS_DOCS_URL,
 | |
|           completed: hasEvaluationSetOutputsNode.value
 | |
|         });
 | |
|       }
 | |
|       if (!suggestedActionSettings.timeSaved?.ignored) {
 | |
|         actions.push({
 | |
|           id: "timeSaved",
 | |
|           title: i18n.baseText("workflowProductionChecklist.timeSaved.title"),
 | |
|           description: i18n.baseText("workflowProductionChecklist.timeSaved.description"),
 | |
|           moreInfoLink: TIME_SAVED_DOCS_URL,
 | |
|           completed: hasTimeSaved.value
 | |
|         });
 | |
|       }
 | |
|       return actions;
 | |
|     });
 | |
|     async function loadWorkflowSettings() {
 | |
|       if (props.workflow.id) {
 | |
|         cachedSettings.value = await workflowsCache.getMergedWorkflowSettings(props.workflow.id);
 | |
|       }
 | |
|     }
 | |
|     async function handleActionClick(actionId) {
 | |
|       if (actionId === "evaluations") {
 | |
|         await router.push({
 | |
|           name: VIEWS.EVALUATION_EDIT,
 | |
|           params: { name: props.workflow.id }
 | |
|         });
 | |
|       } else if (actionId === "errorWorkflow" || actionId === "timeSaved") {
 | |
|         uiStore.openModal(WORKFLOW_SETTINGS_MODAL_KEY);
 | |
|       }
 | |
|       isPopoverOpen.value = false;
 | |
|     }
 | |
|     function isValidAction(action) {
 | |
|       return ["evaluations", "errorWorkflow", "timeSaved"].includes(action);
 | |
|     }
 | |
|     async function handleIgnoreClick(actionId) {
 | |
|       if (!isValidAction(actionId)) {
 | |
|         return;
 | |
|       }
 | |
|       await workflowsCache.ignoreSuggestedAction(props.workflow.id, actionId);
 | |
|       await loadWorkflowSettings();
 | |
|       telemetry.track("user clicked ignore suggested action", {
 | |
|         actionId
 | |
|       });
 | |
|     }
 | |
|     async function handleIgnoreAll() {
 | |
|       const ignoreAllConfirmed = await message.confirm(
 | |
|         i18n.baseText("workflowProductionChecklist.ignoreAllConfirmation.description"),
 | |
|         i18n.baseText("workflowProductionChecklist.ignoreAllConfirmation.title"),
 | |
|         {
 | |
|           confirmButtonText: i18n.baseText("workflowProductionChecklist.ignoreAllConfirmation.confirm")
 | |
|         }
 | |
|       );
 | |
|       if (ignoreAllConfirmed === MODAL_CONFIRM) {
 | |
|         await workflowsCache.ignoreAllSuggestedActionsForAllWorkflows(
 | |
|           availableActions.value.map((action) => action.id)
 | |
|         );
 | |
|         await loadWorkflowSettings();
 | |
|         telemetry.track("user clicked ignore suggested actions for all workflows");
 | |
|       }
 | |
|     }
 | |
|     function openSuggestedActions() {
 | |
|       isPopoverOpen.value = true;
 | |
|     }
 | |
|     function onPopoverOpened() {
 | |
|       telemetry.track("user opened suggested actions checklist");
 | |
|     }
 | |
|     function handlePopoverOpenChange(open) {
 | |
|       if (open) {
 | |
|         isPopoverOpen.value = true;
 | |
|         onPopoverOpened();
 | |
|       } else if (!isActivationModalOpen.value) {
 | |
|         isPopoverOpen.value = false;
 | |
|       }
 | |
|     }
 | |
|     watch(
 | |
|       () => props.workflow.active,
 | |
|       async (isActive, wasActive) => {
 | |
|         if (isActive && !wasActive) {
 | |
|           if (!cachedSettings.value?.firstActivatedAt) {
 | |
|             setTimeout(() => {
 | |
|               openSuggestedActions();
 | |
|             }, 0);
 | |
|           }
 | |
|           await workflowsCache.updateFirstActivatedAt(props.workflow.id);
 | |
|         }
 | |
|       }
 | |
|     );
 | |
|     onMounted(async () => {
 | |
|       await loadWorkflowSettings();
 | |
|     });
 | |
|     return (_ctx, _cache) => {
 | |
|       return availableActions.value.length > 0 ? (openBlock(), createBlock(unref(N8nSuggestedActions), {
 | |
|         key: 0,
 | |
|         open: isPopoverOpen.value,
 | |
|         title: unref(i18n).baseText("workflowProductionChecklist.title"),
 | |
|         actions: availableActions.value,
 | |
|         "ignore-all-label": unref(i18n).baseText("workflowProductionChecklist.turnOffWorkflowSuggestions"),
 | |
|         notice: isProtectedEnvironment.value ? unref(i18n).baseText("workflowProductionChecklist.readOnlyNotice") : "",
 | |
|         "popover-alignment": "end",
 | |
|         onActionClick: handleActionClick,
 | |
|         onIgnoreClick: handleIgnoreClick,
 | |
|         onIgnoreAll: handleIgnoreAll,
 | |
|         "onUpdate:open": handlePopoverOpenChange
 | |
|       }, null, 8, ["open", "title", "actions", "ignore-all-label", "notice"])) : createCommentVNode("", true);
 | |
|     };
 | |
|   }
 | |
| });
 | |
| const _sfc_main$5 = /* @__PURE__ */ defineComponent({
 | |
|   __name: "IntersectionObserver",
 | |
|   props: {
 | |
|     threshold: { default: 0 },
 | |
|     enabled: { type: Boolean, default: false },
 | |
|     eventBus: {}
 | |
|   },
 | |
|   emits: ["observed"],
 | |
|   setup(__props, { emit: __emit }) {
 | |
|     const props = __props;
 | |
|     const emit = __emit;
 | |
|     const observer = ref(null);
 | |
|     const root = ref(null);
 | |
|     onBeforeUnmount(() => {
 | |
|       if (props.enabled && observer.value) {
 | |
|         observer.value.disconnect();
 | |
|       }
 | |
|     });
 | |
|     onMounted(() => {
 | |
|       if (!props.enabled) {
 | |
|         return;
 | |
|       }
 | |
|       const options = {
 | |
|         root: root.value,
 | |
|         rootMargin: "0px",
 | |
|         threshold: props.threshold
 | |
|       };
 | |
|       const intersectionObserver = new IntersectionObserver((entries) => {
 | |
|         entries.forEach(({ target, isIntersecting }) => {
 | |
|           emit("observed", {
 | |
|             el: target,
 | |
|             isIntersecting
 | |
|           });
 | |
|         });
 | |
|       }, options);
 | |
|       observer.value = intersectionObserver;
 | |
|       props.eventBus.on("observe", (observed) => {
 | |
|         if (observed) {
 | |
|           intersectionObserver.observe(observed);
 | |
|         }
 | |
|       });
 | |
|       props.eventBus.on("unobserve", (observed) => {
 | |
|         intersectionObserver.unobserve(observed);
 | |
|       });
 | |
|     });
 | |
|     return (_ctx, _cache) => {
 | |
|       return openBlock(), createElementBlock("div", {
 | |
|         ref_key: "root",
 | |
|         ref: root
 | |
|       }, [
 | |
|         renderSlot(_ctx.$slots, "default")
 | |
|       ], 512);
 | |
|     };
 | |
|   }
 | |
| });
 | |
| const _sfc_main$4 = /* @__PURE__ */ defineComponent({
 | |
|   __name: "IntersectionObserved",
 | |
|   props: {
 | |
|     enabled: { type: Boolean, default: false },
 | |
|     eventBus: {}
 | |
|   },
 | |
|   setup(__props) {
 | |
|     const props = __props;
 | |
|     const observed = ref(null);
 | |
|     onMounted(async () => {
 | |
|       if (!props.enabled) {
 | |
|         return;
 | |
|       }
 | |
|       await nextTick();
 | |
|       props.eventBus.emit("observe", observed.value);
 | |
|     });
 | |
|     onBeforeUnmount(() => {
 | |
|       if (props.enabled) {
 | |
|         props.eventBus.emit("unobserve", observed.value);
 | |
|       }
 | |
|     });
 | |
|     return (_ctx, _cache) => {
 | |
|       return openBlock(), createElementBlock("span", {
 | |
|         ref_key: "observed",
 | |
|         ref: observed
 | |
|       }, [
 | |
|         renderSlot(_ctx.$slots, "default")
 | |
|       ], 512);
 | |
|     };
 | |
|   }
 | |
| });
 | |
| const _hoisted_1$1 = { class: "tags" };
 | |
| const _hoisted_2$1 = ["onClick"];
 | |
| const _sfc_main$3 = /* @__PURE__ */ defineComponent({
 | |
|   __name: "TagsContainer",
 | |
|   props: {
 | |
|     tagIds: {},
 | |
|     tagsById: {},
 | |
|     limit: { default: 20 },
 | |
|     clickable: { type: Boolean, default: false },
 | |
|     responsive: { type: Boolean, default: false },
 | |
|     hoverable: { type: Boolean, default: false }
 | |
|   },
 | |
|   emits: ["click"],
 | |
|   setup(__props, { emit: __emit }) {
 | |
|     const props = __props;
 | |
|     const emit = __emit;
 | |
|     const maxWidth = ref(320);
 | |
|     const intersectionEventBus = createEventBus();
 | |
|     const visibility = ref({});
 | |
|     const tagsContainer = ref();
 | |
|     const style = computed(() => ({
 | |
|       "max-width": `${maxWidth.value}px`
 | |
|     }));
 | |
|     const tags = computed(() => {
 | |
|       const allTags = props.tagIds.map((tagId) => props.tagsById[tagId]).filter(Boolean);
 | |
|       let toDisplay = props.limit ? allTags.slice(0, props.limit) : allTags;
 | |
|       toDisplay = toDisplay.map((tag) => ({
 | |
|         ...tag,
 | |
|         hidden: props.responsive && !visibility.value[tag.id]
 | |
|       }));
 | |
|       let visibleCount = toDisplay.length;
 | |
|       if (props.responsive) {
 | |
|         visibleCount = Object.values(visibility.value).reduce(
 | |
|           (accu, val) => val ? accu + 1 : accu,
 | |
|           0
 | |
|         );
 | |
|       }
 | |
|       if (visibleCount < allTags.length) {
 | |
|         const hidden = allTags.slice(visibleCount);
 | |
|         const hiddenTitle = hidden.reduce(
 | |
|           (accu, tag) => accu ? `${accu}, ${tag.name}` : tag.name,
 | |
|           ""
 | |
|         );
 | |
|         const countTag = {
 | |
|           id: "count",
 | |
|           name: `+${hidden.length}`,
 | |
|           title: hiddenTitle,
 | |
|           isCount: true
 | |
|         };
 | |
|         toDisplay.splice(visibleCount, 0, countTag);
 | |
|       }
 | |
|       return toDisplay;
 | |
|     });
 | |
|     const setMaxWidth = () => {
 | |
|       const container2 = tagsContainer.value?.$el;
 | |
|       const parent = container2?.parentNode;
 | |
|       if (parent) {
 | |
|         maxWidth.value = 0;
 | |
|         void nextTick(() => {
 | |
|           maxWidth.value = parent.clientWidth;
 | |
|         });
 | |
|       }
 | |
|     };
 | |
|     const debouncedSetMaxWidth = debounce(setMaxWidth, 100);
 | |
|     const onObserved = ({ el, isIntersecting }) => {
 | |
|       if (el.dataset.id) {
 | |
|         visibility.value = { ...visibility.value, [el.dataset.id]: isIntersecting };
 | |
|       }
 | |
|     };
 | |
|     const onClick = (e, tag) => {
 | |
|       if (props.clickable) {
 | |
|         e.stopPropagation();
 | |
|       }
 | |
|       if (!tag.hidden) {
 | |
|         emit("click", tag.id);
 | |
|       }
 | |
|     };
 | |
|     onMounted(() => {
 | |
|       setMaxWidth();
 | |
|       window.addEventListener("resize", debouncedSetMaxWidth);
 | |
|     });
 | |
|     onBeforeUnmount(() => {
 | |
|       window.removeEventListener("resize", debouncedSetMaxWidth);
 | |
|     });
 | |
|     return (_ctx, _cache) => {
 | |
|       const _component_el_tag = resolveComponent("el-tag");
 | |
|       return openBlock(), createBlock(_sfc_main$5, {
 | |
|         ref_key: "tagsContainer",
 | |
|         ref: tagsContainer,
 | |
|         threshold: 1,
 | |
|         class: "tags-container",
 | |
|         style: normalizeStyle(style.value),
 | |
|         enabled: _ctx.responsive,
 | |
|         "event-bus": unref(intersectionEventBus),
 | |
|         onObserved
 | |
|       }, {
 | |
|         default: withCtx(() => [
 | |
|           createBaseVNode("span", _hoisted_1$1, [
 | |
|             (openBlock(true), createElementBlock(Fragment, null, renderList(tags.value, (tag) => {
 | |
|               return openBlock(), createElementBlock("span", {
 | |
|                 key: tag.id,
 | |
|                 class: normalizeClass({ clickable: !tag.hidden }),
 | |
|                 onClick: (e) => onClick(e, tag)
 | |
|               }, [
 | |
|                 tag.isCount ? (openBlock(), createBlock(_component_el_tag, {
 | |
|                   key: 0,
 | |
|                   title: tag.title,
 | |
|                   type: "info",
 | |
|                   size: "small",
 | |
|                   class: "count-container",
 | |
|                   "disable-transitions": true
 | |
|                 }, {
 | |
|                   default: withCtx(() => [
 | |
|                     createTextVNode(toDisplayString(tag.name), 1)
 | |
|                   ]),
 | |
|                   _: 2
 | |
|                 }, 1032, ["title"])) : (openBlock(), createBlock(_sfc_main$4, {
 | |
|                   key: 1,
 | |
|                   class: normalizeClass({ hideTag: tag.hidden }),
 | |
|                   "data-id": tag.id,
 | |
|                   enabled: _ctx.responsive,
 | |
|                   "event-bus": unref(intersectionEventBus)
 | |
|                 }, {
 | |
|                   default: withCtx(() => [
 | |
|                     createVNode(_component_el_tag, {
 | |
|                       title: tag.name,
 | |
|                       type: "info",
 | |
|                       size: "mini",
 | |
|                       class: normalizeClass({ hoverable: _ctx.hoverable }),
 | |
|                       "disable-transitions": true
 | |
|                     }, {
 | |
|                       default: withCtx(() => [
 | |
|                         createTextVNode(toDisplayString(tag.name), 1)
 | |
|                       ]),
 | |
|                       _: 2
 | |
|                     }, 1032, ["title", "class"])
 | |
|                   ]),
 | |
|                   _: 2
 | |
|                 }, 1032, ["class", "data-id", "enabled", "event-bus"]))
 | |
|               ], 10, _hoisted_2$1);
 | |
|             }), 128))
 | |
|           ])
 | |
|         ]),
 | |
|         _: 1
 | |
|       }, 8, ["style", "enabled", "event-bus"]);
 | |
|     };
 | |
|   }
 | |
| });
 | |
| const TagsContainer = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-6bab2764"]]);
 | |
| const _sfc_main$2 = /* @__PURE__ */ defineComponent({
 | |
|   __name: "WorkflowTagsContainer",
 | |
|   props: {
 | |
|     tagIds: {},
 | |
|     limit: {},
 | |
|     clickable: { type: Boolean },
 | |
|     responsive: { type: Boolean },
 | |
|     hoverable: { type: Boolean }
 | |
|   },
 | |
|   emits: ["click"],
 | |
|   setup(__props, { emit: __emit }) {
 | |
|     const emit = __emit;
 | |
|     const annotationTagsStore = useTagsStore();
 | |
|     const tagsById = computed(() => annotationTagsStore.tagsById);
 | |
|     function onClick(tagId) {
 | |
|       emit("click", tagId);
 | |
|     }
 | |
|     return (_ctx, _cache) => {
 | |
|       return openBlock(), createBlock(TagsContainer, {
 | |
|         "tag-ids": _ctx.tagIds,
 | |
|         "tags-by-id": tagsById.value,
 | |
|         limit: _ctx.limit,
 | |
|         clickable: _ctx.clickable,
 | |
|         responsive: _ctx.responsive,
 | |
|         hoverable: _ctx.hoverable,
 | |
|         onClick
 | |
|       }, null, 8, ["tag-ids", "tags-by-id", "limit", "clickable", "responsive", "hoverable"]);
 | |
|     };
 | |
|   }
 | |
| });
 | |
| const INVALID_CHARS_REGEX = /[<>:"/\\|?*\u0000-\u001F\u007F-\u009F]/g;
 | |
| const ZERO_WIDTH_CHARS_REGEX = /[\u200B-\u200D\u2060\uFEFF]/g;
 | |
| const UNICODE_SPACES_REGEX = /[\u00A0\u2000-\u200A]/g;
 | |
| const LEADING_TRAILING_DOTS_SPACES_REGEX = /^[\s.]+|[\s.]+$/g;
 | |
| const WINDOWS_RESERVED_NAMES = /* @__PURE__ */ new Set([
 | |
|   "CON",
 | |
|   "PRN",
 | |
|   "AUX",
 | |
|   "NUL",
 | |
|   "COM1",
 | |
|   "COM2",
 | |
|   "COM3",
 | |
|   "COM4",
 | |
|   "COM5",
 | |
|   "COM6",
 | |
|   "COM7",
 | |
|   "COM8",
 | |
|   "COM9",
 | |
|   "LPT1",
 | |
|   "LPT2",
 | |
|   "LPT3",
 | |
|   "LPT4",
 | |
|   "LPT5",
 | |
|   "LPT6",
 | |
|   "LPT7",
 | |
|   "LPT8",
 | |
|   "LPT9"
 | |
| ]);
 | |
| const DEFAULT_FALLBACK_NAME = "untitled";
 | |
| const MAX_FILENAME_LENGTH = 200;
 | |
| const sanitizeFilename = (filename, maxLength = MAX_FILENAME_LENGTH) => {
 | |
|   if (!filename) {
 | |
|     return DEFAULT_FALLBACK_NAME;
 | |
|   }
 | |
|   let baseName = filename.trim().replace(INVALID_CHARS_REGEX, "_").replace(ZERO_WIDTH_CHARS_REGEX, "").replace(UNICODE_SPACES_REGEX, " ").replace(LEADING_TRAILING_DOTS_SPACES_REGEX, "");
 | |
|   if (!baseName) {
 | |
|     baseName = DEFAULT_FALLBACK_NAME;
 | |
|   }
 | |
|   if (WINDOWS_RESERVED_NAMES.has(baseName.toUpperCase())) {
 | |
|     baseName = `_${baseName}`;
 | |
|   }
 | |
|   if (baseName.length > maxLength) {
 | |
|     baseName = baseName.slice(0, maxLength);
 | |
|   }
 | |
|   return baseName;
 | |
| };
 | |
| const _hoisted_1 = {
 | |
|   class: "tags",
 | |
|   "data-test-id": "workflow-tags-container"
 | |
| };
 | |
| const _hoisted_2 = { key: 1 };
 | |
| const _hoisted_3 = { class: "archived" };
 | |
| const _sfc_main$1 = /* @__PURE__ */ defineComponent({
 | |
|   __name: "WorkflowDetails",
 | |
|   props: {
 | |
|     readOnly: { type: Boolean },
 | |
|     id: {},
 | |
|     tags: {},
 | |
|     name: {},
 | |
|     meta: {},
 | |
|     scopes: {},
 | |
|     active: { type: Boolean },
 | |
|     currentFolder: {},
 | |
|     isArchived: { type: Boolean }
 | |
|   },
 | |
|   setup(__props) {
 | |
|     const WORKFLOW_NAME_BP_TO_WIDTH = {
 | |
|       XS: 150,
 | |
|       SM: 200,
 | |
|       MD: 250,
 | |
|       LG: 500,
 | |
|       XL: 1e3
 | |
|     };
 | |
|     const props = __props;
 | |
|     const $style = useCssModule();
 | |
|     const rootStore = useRootStore();
 | |
|     const settingsStore = useSettingsStore();
 | |
|     const sourceControlStore = useSourceControlStore();
 | |
|     const tagsStore = useTagsStore();
 | |
|     const uiStore = useUIStore();
 | |
|     const usersStore = useUsersStore();
 | |
|     const workflowsStore = useWorkflowsStore();
 | |
|     const projectsStore = useProjectsStore();
 | |
|     const foldersStore = useFoldersStore();
 | |
|     const npsSurveyStore = useNpsSurveyStore();
 | |
|     const i18n = useI18n$1();
 | |
|     const router = useRouter();
 | |
|     const route = useRoute();
 | |
|     const locale = useI18n$1();
 | |
|     const telemetry = useTelemetry();
 | |
|     const message = useMessage();
 | |
|     const toast = useToast();
 | |
|     const documentTitle = useDocumentTitle();
 | |
|     const workflowSaving = useWorkflowSaving({ router });
 | |
|     const workflowHelpers = useWorkflowHelpers();
 | |
|     const pageRedirectionHelper = usePageRedirectionHelper();
 | |
|     const isTagsEditEnabled = ref(false);
 | |
|     const appliedTagIds = ref([]);
 | |
|     const tagsSaving = ref(false);
 | |
|     const importFileRef = ref();
 | |
|     const tagsEventBus = createEventBus();
 | |
|     const changeOwnerEventBus = createEventBus();
 | |
|     const hasChanged = (prev, curr) => {
 | |
|       if (prev.length !== curr.length) {
 | |
|         return true;
 | |
|       }
 | |
|       const set = new Set(prev);
 | |
|       return curr.reduce((acc, val) => acc || !set.has(val), false);
 | |
|     };
 | |
|     const isNewWorkflow = computed(() => {
 | |
|       return !props.id || props.id === PLACEHOLDER_EMPTY_WORKFLOW_ID || props.id === "new";
 | |
|     });
 | |
|     const isWorkflowSaving = computed(() => {
 | |
|       return uiStore.isActionActive.workflowSaving;
 | |
|     });
 | |
|     const onWorkflowPage = computed(() => {
 | |
|       return route.meta && (route.meta.nodeView || route.meta.keepWorkflowAlive === true);
 | |
|     });
 | |
|     const onExecutionsTab = computed(() => {
 | |
|       return [
 | |
|         VIEWS.EXECUTION_HOME.toString(),
 | |
|         VIEWS.WORKFLOW_EXECUTIONS.toString(),
 | |
|         VIEWS.EXECUTION_PREVIEW
 | |
|       ].includes(route.name || "");
 | |
|     });
 | |
|     const workflowPermissions = computed(() => getResourcePermissions(props.scopes).workflow);
 | |
|     const workflowMenuItems = computed(() => {
 | |
|       const actions = [
 | |
|         {
 | |
|           id: WORKFLOW_MENU_ACTIONS.DOWNLOAD,
 | |
|           label: locale.baseText("menuActions.download"),
 | |
|           disabled: !onWorkflowPage.value
 | |
|         }
 | |
|       ];
 | |
|       if (workflowPermissions.value.move && projectsStore.isTeamProjectFeatureEnabled) {
 | |
|         actions.push({
 | |
|           id: WORKFLOW_MENU_ACTIONS.CHANGE_OWNER,
 | |
|           label: locale.baseText("workflows.item.changeOwner"),
 | |
|           disabled: isNewWorkflow.value
 | |
|         });
 | |
|       }
 | |
|       if (!props.readOnly && !props.isArchived) {
 | |
|         actions.push({
 | |
|           id: WORKFLOW_MENU_ACTIONS.RENAME,
 | |
|           label: locale.baseText("generic.rename"),
 | |
|           disabled: !onWorkflowPage.value || workflowPermissions.value.update !== true
 | |
|         });
 | |
|       }
 | |
|       if (workflowPermissions.value.delete === true && !props.readOnly && !props.isArchived || isNewWorkflow.value) {
 | |
|         actions.unshift({
 | |
|           id: WORKFLOW_MENU_ACTIONS.DUPLICATE,
 | |
|           label: locale.baseText("menuActions.duplicate"),
 | |
|           disabled: !onWorkflowPage.value || !props.id
 | |
|         });
 | |
|         actions.push(
 | |
|           {
 | |
|             id: WORKFLOW_MENU_ACTIONS.IMPORT_FROM_URL,
 | |
|             label: locale.baseText("menuActions.importFromUrl"),
 | |
|             disabled: !onWorkflowPage.value || onExecutionsTab.value
 | |
|           },
 | |
|           {
 | |
|             id: WORKFLOW_MENU_ACTIONS.IMPORT_FROM_FILE,
 | |
|             label: locale.baseText("menuActions.importFromFile"),
 | |
|             disabled: !onWorkflowPage.value || onExecutionsTab.value
 | |
|           }
 | |
|         );
 | |
|       }
 | |
|       if (hasPermission(["rbac"], { rbac: { scope: "sourceControl:push" } })) {
 | |
|         actions.push({
 | |
|           id: WORKFLOW_MENU_ACTIONS.PUSH,
 | |
|           label: locale.baseText("menuActions.push"),
 | |
|           disabled: !sourceControlStore.isEnterpriseSourceControlEnabled || !onWorkflowPage.value || onExecutionsTab.value || sourceControlStore.preferences.branchReadOnly
 | |
|         });
 | |
|       }
 | |
|       actions.push({
 | |
|         id: WORKFLOW_MENU_ACTIONS.SETTINGS,
 | |
|         label: locale.baseText("generic.settings"),
 | |
|         disabled: !onWorkflowPage.value || isNewWorkflow.value
 | |
|       });
 | |
|       if (workflowPermissions.value.delete === true && !props.readOnly || isNewWorkflow.value) {
 | |
|         if (props.isArchived) {
 | |
|           actions.push({
 | |
|             id: WORKFLOW_MENU_ACTIONS.UNARCHIVE,
 | |
|             label: locale.baseText("menuActions.unarchive"),
 | |
|             disabled: !onWorkflowPage.value || isNewWorkflow.value
 | |
|           });
 | |
|           actions.push({
 | |
|             id: WORKFLOW_MENU_ACTIONS.DELETE,
 | |
|             label: locale.baseText("menuActions.delete"),
 | |
|             disabled: !onWorkflowPage.value || isNewWorkflow.value,
 | |
|             customClass: $style.deleteItem,
 | |
|             divided: true
 | |
|           });
 | |
|         } else {
 | |
|           actions.push({
 | |
|             id: WORKFLOW_MENU_ACTIONS.ARCHIVE,
 | |
|             label: locale.baseText("menuActions.archive"),
 | |
|             disabled: !onWorkflowPage.value || isNewWorkflow.value,
 | |
|             customClass: $style.deleteItem,
 | |
|             divided: true
 | |
|           });
 | |
|         }
 | |
|       }
 | |
|       return actions;
 | |
|     });
 | |
|     const isWorkflowHistoryFeatureEnabled = computed(() => {
 | |
|       return settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.WorkflowHistory];
 | |
|     });
 | |
|     const workflowTagIds = computed(() => {
 | |
|       return (props.tags ?? []).map((tag) => typeof tag === "string" ? tag : tag.id);
 | |
|     });
 | |
|     const currentProjectName = computed(() => {
 | |
|       if (projectsStore.currentProject?.type === ProjectTypes.Personal) {
 | |
|         return locale.baseText("projects.menu.personal");
 | |
|       }
 | |
|       return projectsStore.currentProject?.name;
 | |
|     });
 | |
|     const currentFolderForBreadcrumbs = computed(() => {
 | |
|       if (!isNewWorkflow.value && props.currentFolder) {
 | |
|         return props.currentFolder;
 | |
|       }
 | |
|       const folderId = route.query.parentFolderId;
 | |
|       if (folderId) {
 | |
|         return foldersStore.getCachedFolder(folderId);
 | |
|       }
 | |
|       return null;
 | |
|     });
 | |
|     watch(
 | |
|       () => props.id,
 | |
|       () => {
 | |
|         isTagsEditEnabled.value = false;
 | |
|         renameInput.value?.forceCancel();
 | |
|       }
 | |
|     );
 | |
|     function getWorkflowId() {
 | |
|       let id = void 0;
 | |
|       if (props.id !== PLACEHOLDER_EMPTY_WORKFLOW_ID) {
 | |
|         id = props.id;
 | |
|       } else if (route.params.name && route.params.name !== "new") {
 | |
|         id = route.params.name;
 | |
|       }
 | |
|       return id;
 | |
|     }
 | |
|     async function onSaveButtonClick() {
 | |
|       if (isWorkflowSaving.value) {
 | |
|         return;
 | |
|       }
 | |
|       const id = getWorkflowId();
 | |
|       const name = props.name;
 | |
|       const tags = props.tags;
 | |
|       const saved = await workflowSaving.saveCurrentWorkflow({
 | |
|         id,
 | |
|         name,
 | |
|         tags
 | |
|       });
 | |
|       if (saved) {
 | |
|         showCreateWorkflowSuccessToast(id);
 | |
|         await npsSurveyStore.fetchPromptsData();
 | |
|         if (route.name === VIEWS.EXECUTION_DEBUG) {
 | |
|           await router.replace({
 | |
|             name: VIEWS.WORKFLOW,
 | |
|             params: { name: props.id }
 | |
|           });
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     function onShareButtonClick() {
 | |
|       uiStore.openModalWithData({
 | |
|         name: WORKFLOW_SHARE_MODAL_KEY,
 | |
|         data: { id: props.id }
 | |
|       });
 | |
|       telemetry.track("User opened sharing modal", {
 | |
|         workflow_id: props.id,
 | |
|         user_id_sharer: usersStore.currentUser?.id,
 | |
|         sub_view: route.name === VIEWS.WORKFLOWS ? "Workflows listing" : "Workflow editor"
 | |
|       });
 | |
|     }
 | |
|     function onTagsEditEnable() {
 | |
|       appliedTagIds.value = props.tags ?? [];
 | |
|       isTagsEditEnabled.value = true;
 | |
|       setTimeout(() => {
 | |
|         renameInput.value?.forceCancel();
 | |
|         tagsEventBus.emit("focus");
 | |
|       }, 0);
 | |
|     }
 | |
|     async function onTagsBlur() {
 | |
|       const current = props.tags ?? [];
 | |
|       const tags = appliedTagIds.value;
 | |
|       if (!hasChanged(current, tags)) {
 | |
|         isTagsEditEnabled.value = false;
 | |
|         return;
 | |
|       }
 | |
|       if (tagsSaving.value) {
 | |
|         return;
 | |
|       }
 | |
|       tagsSaving.value = true;
 | |
|       const saved = await workflowSaving.saveCurrentWorkflow({ tags });
 | |
|       telemetry.track("User edited workflow tags", {
 | |
|         workflow_id: props.id,
 | |
|         new_tag_count: tags.length
 | |
|       });
 | |
|       tagsSaving.value = false;
 | |
|       if (saved) {
 | |
|         isTagsEditEnabled.value = false;
 | |
|       }
 | |
|     }
 | |
|     function onTagsEditEsc() {
 | |
|       isTagsEditEnabled.value = false;
 | |
|     }
 | |
|     const renameInput = useTemplateRef("renameInput");
 | |
|     function onNameToggle() {
 | |
|       if (renameInput.value?.forceFocus) {
 | |
|         renameInput.value.forceFocus();
 | |
|       }
 | |
|     }
 | |
|     async function onNameSubmit(name) {
 | |
|       const newName = name.trim();
 | |
|       if (!newName) {
 | |
|         toast.showMessage({
 | |
|           title: locale.baseText("renameAction.emptyName.title"),
 | |
|           message: locale.baseText("renameAction.emptyName.message"),
 | |
|           type: "error"
 | |
|         });
 | |
|         renameInput.value?.forceCancel();
 | |
|         return;
 | |
|       }
 | |
|       if (newName === props.name) {
 | |
|         renameInput.value?.forceCancel();
 | |
|         return;
 | |
|       }
 | |
|       uiStore.addActiveAction("workflowSaving");
 | |
|       const id = getWorkflowId();
 | |
|       const saved = await workflowSaving.saveCurrentWorkflow({ name });
 | |
|       if (saved) {
 | |
|         showCreateWorkflowSuccessToast(id);
 | |
|         workflowHelpers.setDocumentTitle(newName, "IDLE");
 | |
|       }
 | |
|       uiStore.removeActiveAction("workflowSaving");
 | |
|       renameInput.value?.forceCancel();
 | |
|     }
 | |
|     async function handleFileImport() {
 | |
|       const inputRef = importFileRef.value;
 | |
|       if (inputRef?.files && inputRef.files.length !== 0) {
 | |
|         const reader = new FileReader();
 | |
|         reader.onload = () => {
 | |
|           let workflowData;
 | |
|           try {
 | |
|             workflowData = JSON.parse(reader.result);
 | |
|           } catch (error) {
 | |
|             toast.showMessage({
 | |
|               title: locale.baseText("mainSidebar.showMessage.handleFileImport.title"),
 | |
|               message: locale.baseText("mainSidebar.showMessage.handleFileImport.message"),
 | |
|               type: "error"
 | |
|             });
 | |
|             return;
 | |
|           } finally {
 | |
|             reader.onload = null;
 | |
|             inputRef.value = "";
 | |
|           }
 | |
|           nodeViewEventBus.emit("importWorkflowData", { data: workflowData });
 | |
|         };
 | |
|         reader.readAsText(inputRef.files[0]);
 | |
|       }
 | |
|     }
 | |
|     async function onWorkflowMenuSelect(value) {
 | |
|       const action = value;
 | |
|       switch (action) {
 | |
|         case WORKFLOW_MENU_ACTIONS.DUPLICATE: {
 | |
|           uiStore.openModalWithData({
 | |
|             name: DUPLICATE_MODAL_KEY,
 | |
|             data: {
 | |
|               id: props.id,
 | |
|               name: props.name,
 | |
|               tags: props.tags,
 | |
|               parentFolderId: props.currentFolder?.id
 | |
|             }
 | |
|           });
 | |
|           break;
 | |
|         }
 | |
|         case WORKFLOW_MENU_ACTIONS.RENAME: {
 | |
|           onNameToggle();
 | |
|           break;
 | |
|         }
 | |
|         case WORKFLOW_MENU_ACTIONS.DOWNLOAD: {
 | |
|           const workflowData = await workflowHelpers.getWorkflowDataToSave();
 | |
|           const { tags, ...data } = workflowData;
 | |
|           const exportData = {
 | |
|             ...data,
 | |
|             meta: {
 | |
|               ...props.meta,
 | |
|               instanceId: rootStore.instanceId
 | |
|             },
 | |
|             tags: (tags ?? []).map((tagId) => {
 | |
|               const { usageCount, ...tag } = tagsStore.tagsById[tagId];
 | |
|               return tag;
 | |
|             })
 | |
|           };
 | |
|           const blob = new Blob([JSON.stringify(exportData, null, 2)], {
 | |
|             type: "application/json;charset=utf-8"
 | |
|           });
 | |
|           let name = props.name || "unsaved_workflow";
 | |
|           name = sanitizeFilename(name);
 | |
|           telemetry.track("User exported workflow", { workflow_id: workflowData.id });
 | |
|           FileSaver_minExports.saveAs(blob, name + ".json");
 | |
|           break;
 | |
|         }
 | |
|         case WORKFLOW_MENU_ACTIONS.IMPORT_FROM_URL: {
 | |
|           uiStore.openModal(IMPORT_WORKFLOW_URL_MODAL_KEY);
 | |
|           break;
 | |
|         }
 | |
|         case WORKFLOW_MENU_ACTIONS.IMPORT_FROM_FILE: {
 | |
|           importFileRef.value?.click();
 | |
|           break;
 | |
|         }
 | |
|         case WORKFLOW_MENU_ACTIONS.PUSH: {
 | |
|           try {
 | |
|             await onSaveButtonClick();
 | |
|             void router.push({
 | |
|               query: {
 | |
|                 ...route.query,
 | |
|                 sourceControl: "push"
 | |
|               }
 | |
|             });
 | |
|           } catch (error) {
 | |
|             switch (error.message) {
 | |
|               case "source_control_not_connected":
 | |
|                 toast.showError(
 | |
|                   { ...error, message: "" },
 | |
|                   locale.baseText("settings.sourceControl.error.not.connected.title"),
 | |
|                   locale.baseText("settings.sourceControl.error.not.connected.message")
 | |
|                 );
 | |
|                 break;
 | |
|               default:
 | |
|                 toast.showError(error, locale.baseText("error"));
 | |
|             }
 | |
|           }
 | |
|           break;
 | |
|         }
 | |
|         case WORKFLOW_MENU_ACTIONS.SETTINGS: {
 | |
|           uiStore.openModal(WORKFLOW_SETTINGS_MODAL_KEY);
 | |
|           break;
 | |
|         }
 | |
|         case WORKFLOW_MENU_ACTIONS.ARCHIVE: {
 | |
|           if (props.active) {
 | |
|             const archiveConfirmed = await message.confirm(
 | |
|               locale.baseText("mainSidebar.confirmMessage.workflowArchive.message", {
 | |
|                 interpolate: { workflowName: props.name }
 | |
|               }),
 | |
|               locale.baseText("mainSidebar.confirmMessage.workflowArchive.headline"),
 | |
|               {
 | |
|                 type: "warning",
 | |
|                 confirmButtonText: locale.baseText(
 | |
|                   "mainSidebar.confirmMessage.workflowArchive.confirmButtonText"
 | |
|                 ),
 | |
|                 cancelButtonText: locale.baseText(
 | |
|                   "mainSidebar.confirmMessage.workflowArchive.cancelButtonText"
 | |
|                 )
 | |
|               }
 | |
|             );
 | |
|             if (archiveConfirmed !== MODAL_CONFIRM) {
 | |
|               return;
 | |
|             }
 | |
|           }
 | |
|           try {
 | |
|             await workflowsStore.archiveWorkflow(props.id);
 | |
|           } catch (error) {
 | |
|             toast.showError(error, locale.baseText("generic.archiveWorkflowError"));
 | |
|             return;
 | |
|           }
 | |
|           uiStore.stateIsDirty = false;
 | |
|           toast.showMessage({
 | |
|             title: locale.baseText("mainSidebar.showMessage.handleArchive.title", {
 | |
|               interpolate: { workflowName: props.name }
 | |
|             }),
 | |
|             type: "success"
 | |
|           });
 | |
|           await router.push({ name: VIEWS.WORKFLOWS });
 | |
|           break;
 | |
|         }
 | |
|         case WORKFLOW_MENU_ACTIONS.UNARCHIVE: {
 | |
|           await workflowsStore.unarchiveWorkflow(props.id);
 | |
|           toast.showMessage({
 | |
|             title: locale.baseText("mainSidebar.showMessage.handleUnarchive.title", {
 | |
|               interpolate: { workflowName: props.name }
 | |
|             }),
 | |
|             type: "success"
 | |
|           });
 | |
|           break;
 | |
|         }
 | |
|         case WORKFLOW_MENU_ACTIONS.DELETE: {
 | |
|           const deleteConfirmed = await message.confirm(
 | |
|             locale.baseText("mainSidebar.confirmMessage.workflowDelete.message", {
 | |
|               interpolate: { workflowName: props.name }
 | |
|             }),
 | |
|             locale.baseText("mainSidebar.confirmMessage.workflowDelete.headline"),
 | |
|             {
 | |
|               type: "warning",
 | |
|               confirmButtonText: locale.baseText(
 | |
|                 "mainSidebar.confirmMessage.workflowDelete.confirmButtonText"
 | |
|               ),
 | |
|               cancelButtonText: locale.baseText(
 | |
|                 "mainSidebar.confirmMessage.workflowDelete.cancelButtonText"
 | |
|               )
 | |
|             }
 | |
|           );
 | |
|           if (deleteConfirmed !== MODAL_CONFIRM) {
 | |
|             return;
 | |
|           }
 | |
|           try {
 | |
|             await workflowsStore.deleteWorkflow(props.id);
 | |
|           } catch (error) {
 | |
|             toast.showError(error, locale.baseText("generic.deleteWorkflowError"));
 | |
|             return;
 | |
|           }
 | |
|           uiStore.stateIsDirty = false;
 | |
|           documentTitle.reset();
 | |
|           toast.showMessage({
 | |
|             title: locale.baseText("mainSidebar.showMessage.handleSelect1.title", {
 | |
|               interpolate: { workflowName: props.name }
 | |
|             }),
 | |
|             type: "success"
 | |
|           });
 | |
|           await router.push({ name: VIEWS.WORKFLOWS });
 | |
|           break;
 | |
|         }
 | |
|         case WORKFLOW_MENU_ACTIONS.CHANGE_OWNER: {
 | |
|           const workflowId = getWorkflowId();
 | |
|           if (!workflowId) {
 | |
|             return;
 | |
|           }
 | |
|           changeOwnerEventBus.once(
 | |
|             "resource-moved",
 | |
|             async () => await router.push({ name: VIEWS.WORKFLOWS })
 | |
|           );
 | |
|           uiStore.openModalWithData({
 | |
|             name: PROJECT_MOVE_RESOURCE_MODAL,
 | |
|             data: {
 | |
|               resource: workflowsStore.workflowsById[workflowId],
 | |
|               resourceType: ResourceType.Workflow,
 | |
|               resourceTypeLabel: locale.baseText("generic.workflow").toLowerCase(),
 | |
|               eventBus: changeOwnerEventBus
 | |
|             }
 | |
|           });
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     function goToUpgrade() {
 | |
|       void pageRedirectionHelper.goToUpgrade("workflow_sharing", "upgrade-workflow-sharing");
 | |
|     }
 | |
|     function goToWorkflowHistoryUpgrade() {
 | |
|       void pageRedirectionHelper.goToUpgrade("workflow-history", "upgrade-workflow-history");
 | |
|     }
 | |
|     function getPersonalProjectToastContent() {
 | |
|       const title = locale.baseText("workflows.create.personal.toast.title");
 | |
|       if (!props.currentFolder) {
 | |
|         return { title };
 | |
|       }
 | |
|       const toastMessage = locale.baseText("workflows.create.folder.toast.title", {
 | |
|         interpolate: {
 | |
|           projectName: "Personal",
 | |
|           folderName: props.currentFolder.name
 | |
|         }
 | |
|       });
 | |
|       return { title, toastMessage };
 | |
|     }
 | |
|     function getToastContent() {
 | |
|       const currentProject = projectsStore.currentProject;
 | |
|       const isPersonalProject = !projectsStore.currentProject || currentProject?.id === projectsStore.personalProject?.id;
 | |
|       const projectName = currentProjectName.value ?? "";
 | |
|       if (isPersonalProject) {
 | |
|         return getPersonalProjectToastContent();
 | |
|       }
 | |
|       const titleKey = props.currentFolder ? "workflows.create.folder.toast.title" : "workflows.create.project.toast.title";
 | |
|       const interpolateData = props.currentFolder ? { projectName, folderName: props.currentFolder.name ?? "" } : { projectName };
 | |
|       const title = locale.baseText(titleKey, { interpolate: interpolateData });
 | |
|       const toastMessage = locale.baseText("workflows.create.project.toast.text", {
 | |
|         interpolate: { projectName }
 | |
|       });
 | |
|       return { title, toastMessage };
 | |
|     }
 | |
|     function showCreateWorkflowSuccessToast(id) {
 | |
|       const shouldShowToast = !id || ["new", PLACEHOLDER_EMPTY_WORKFLOW_ID].includes(id);
 | |
|       if (!shouldShowToast) return;
 | |
|       const { title, toastMessage } = getToastContent();
 | |
|       toast.showMessage({
 | |
|         title,
 | |
|         message: toastMessage,
 | |
|         type: "success"
 | |
|       });
 | |
|     }
 | |
|     const onBreadcrumbsItemSelected = (item) => {
 | |
|       if (item.href) {
 | |
|         void router.push(item.href).catch((error) => {
 | |
|           toast.showError(error, i18n.baseText("folders.open.error.title"));
 | |
|         });
 | |
|       }
 | |
|     };
 | |
|     return (_ctx, _cache) => {
 | |
|       const _component_FolderBreadcrumbs = __unplugin_components_0;
 | |
|       const _component_N8nBadge = N8nBadge;
 | |
|       const _component_N8nButton = N8nButton;
 | |
|       const _component_N8nTooltip = Tooltip;
 | |
|       const _component_EnterpriseEdition = resolveComponent("EnterpriseEdition");
 | |
|       const _component_N8nActionDropdown = N8nActionDropdown;
 | |
|       return openBlock(), createElementBlock("div", {
 | |
|         class: normalizeClass(unref($style).container)
 | |
|       }, [
 | |
|         createVNode(_sfc_main$9, {
 | |
|           "value-x-s": 15,
 | |
|           "value-s-m": 25,
 | |
|           "value-m-d": 50,
 | |
|           class: "name-container",
 | |
|           "data-test-id": "canvas-breadcrumbs"
 | |
|         }, {
 | |
|           default: withCtx(({ bp }) => [
 | |
|             createVNode(_component_FolderBreadcrumbs, {
 | |
|               "current-folder": currentFolderForBreadcrumbs.value,
 | |
|               "current-folder-as-link": true,
 | |
|               onItemSelected: onBreadcrumbsItemSelected
 | |
|             }, {
 | |
|               append: withCtx(() => [
 | |
|                 unref(projectsStore).currentProject ?? unref(projectsStore).personalProject ? (openBlock(), createElementBlock("span", {
 | |
|                   key: 0,
 | |
|                   class: normalizeClass(unref($style)["path-separator"])
 | |
|                 }, "/", 2)) : createCommentVNode("", true),
 | |
|                 (openBlock(), createBlock(unref(InlineRename), {
 | |
|                   ref_key: "renameInput",
 | |
|                   ref: renameInput,
 | |
|                   key: _ctx.id,
 | |
|                   placeholder: "Workflow name",
 | |
|                   "data-test-id": "workflow-name-input",
 | |
|                   class: "name",
 | |
|                   "model-value": _ctx.name,
 | |
|                   "max-length": unref(MAX_WORKFLOW_NAME_LENGTH),
 | |
|                   "max-width": WORKFLOW_NAME_BP_TO_WIDTH[bp],
 | |
|                   "read-only": _ctx.readOnly || _ctx.isArchived || !isNewWorkflow.value && !workflowPermissions.value.update,
 | |
|                   disabled: _ctx.readOnly || _ctx.isArchived || !isNewWorkflow.value && !workflowPermissions.value.update,
 | |
|                   "onUpdate:modelValue": onNameSubmit
 | |
|                 }, null, 8, ["model-value", "max-length", "max-width", "read-only", "disabled"]))
 | |
|               ]),
 | |
|               _: 2
 | |
|             }, 1032, ["current-folder"])
 | |
|           ]),
 | |
|           _: 1
 | |
|         }),
 | |
|         createBaseVNode("span", _hoisted_1, [
 | |
|           unref(settingsStore).areTagsEnabled ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
 | |
|             isTagsEditEnabled.value && !(_ctx.readOnly || _ctx.isArchived) && (isNewWorkflow.value || workflowPermissions.value.update) ? (openBlock(), createBlock(_sfc_main$e, {
 | |
|               key: 0,
 | |
|               ref: "dropdown",
 | |
|               modelValue: appliedTagIds.value,
 | |
|               "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => appliedTagIds.value = $event),
 | |
|               "event-bus": unref(tagsEventBus),
 | |
|               placeholder: unref(i18n).baseText("workflowDetails.chooseOrCreateATag"),
 | |
|               class: "tags-edit",
 | |
|               "data-test-id": "workflow-tags-dropdown",
 | |
|               onBlur: onTagsBlur,
 | |
|               onEsc: onTagsEditEsc
 | |
|             }, null, 8, ["modelValue", "event-bus", "placeholder"])) : (_ctx.tags ?? []).length === 0 && !(_ctx.readOnly || _ctx.isArchived) && (isNewWorkflow.value || workflowPermissions.value.update) ? (openBlock(), createElementBlock("div", _hoisted_2, [
 | |
|               createBaseVNode("span", {
 | |
|                 class: "add-tag clickable",
 | |
|                 "data-test-id": "new-tag-link",
 | |
|                 onClick: onTagsEditEnable
 | |
|               }, " + " + toDisplayString(unref(i18n).baseText("workflowDetails.addTag")), 1)
 | |
|             ])) : (openBlock(), createBlock(_sfc_main$2, {
 | |
|               key: _ctx.id,
 | |
|               "tag-ids": workflowTagIds.value,
 | |
|               clickable: true,
 | |
|               responsive: true,
 | |
|               "data-test-id": "workflow-tags",
 | |
|               onClick: onTagsEditEnable
 | |
|             }, null, 8, ["tag-ids"]))
 | |
|           ], 64)) : createCommentVNode("", true),
 | |
|           createBaseVNode("span", _hoisted_3, [
 | |
|             _ctx.isArchived ? (openBlock(), createBlock(_component_N8nBadge, {
 | |
|               key: 0,
 | |
|               class: "ml-3xs",
 | |
|               theme: "tertiary",
 | |
|               bold: "",
 | |
|               "data-test-id": "workflow-archived-tag"
 | |
|             }, {
 | |
|               default: withCtx(() => [
 | |
|                 createTextVNode(toDisplayString(unref(locale).baseText("workflows.item.archived")), 1)
 | |
|               ]),
 | |
|               _: 1
 | |
|             })) : createCommentVNode("", true)
 | |
|           ])
 | |
|         ]),
 | |
|         createVNode(_sfc_main$f, { class: "actions" }, {
 | |
|           default: withCtx(() => [
 | |
|             !isNewWorkflow.value ? (openBlock(), createBlock(_sfc_main$6, {
 | |
|               key: 0,
 | |
|               workflow: unref(workflowsStore).workflow
 | |
|             }, null, 8, ["workflow"])) : createCommentVNode("", true),
 | |
|             createBaseVNode("span", {
 | |
|               class: normalizeClass(`activator ${unref($style).group}`)
 | |
|             }, [
 | |
|               createVNode(WorkflowActivator, {
 | |
|                 "is-archived": _ctx.isArchived,
 | |
|                 "workflow-active": _ctx.active,
 | |
|                 "workflow-id": _ctx.id,
 | |
|                 "workflow-permissions": workflowPermissions.value
 | |
|               }, null, 8, ["is-archived", "workflow-active", "workflow-id", "workflow-permissions"])
 | |
|             ], 2),
 | |
|             createVNode(_component_EnterpriseEdition, {
 | |
|               features: [unref(EnterpriseEditionFeature).Sharing]
 | |
|             }, {
 | |
|               fallback: withCtx(() => [
 | |
|                 createVNode(_component_N8nTooltip, null, {
 | |
|                   content: withCtx(() => [
 | |
|                     createVNode(unref(I18nT), {
 | |
|                       keypath: unref(uiStore).contextBasedTranslationKeys.workflows.sharing.unavailable.description.tooltip,
 | |
|                       tag: "span",
 | |
|                       scope: "global"
 | |
|                     }, {
 | |
|                       action: withCtx(() => [
 | |
|                         createBaseVNode("a", { onClick: goToUpgrade }, toDisplayString(unref(i18n).baseText(
 | |
|                           unref(uiStore).contextBasedTranslationKeys.workflows.sharing.unavailable.button
 | |
|                         )), 1)
 | |
|                       ]),
 | |
|                       _: 1
 | |
|                     }, 8, ["keypath"])
 | |
|                   ]),
 | |
|                   default: withCtx(() => [
 | |
|                     createVNode(_component_N8nButton, {
 | |
|                       type: "secondary",
 | |
|                       class: normalizeClass(["mr-2xs", unref($style).disabledShareButton])
 | |
|                     }, {
 | |
|                       default: withCtx(() => [
 | |
|                         createTextVNode(toDisplayString(unref(i18n).baseText("workflowDetails.share")), 1)
 | |
|                       ]),
 | |
|                       _: 1
 | |
|                     }, 8, ["class"])
 | |
|                   ]),
 | |
|                   _: 1
 | |
|                 })
 | |
|               ]),
 | |
|               default: withCtx(() => [
 | |
|                 createBaseVNode("div", {
 | |
|                   class: normalizeClass(unref($style).group)
 | |
|                 }, [
 | |
|                   !isNewWorkflow.value ? (openBlock(), createBlock(CollaborationPane, { key: 0 })) : createCommentVNode("", true),
 | |
|                   createVNode(_component_N8nButton, {
 | |
|                     type: "secondary",
 | |
|                     "data-test-id": "workflow-share-button",
 | |
|                     onClick: onShareButtonClick
 | |
|                   }, {
 | |
|                     default: withCtx(() => [
 | |
|                       createTextVNode(toDisplayString(unref(i18n).baseText("workflowDetails.share")), 1)
 | |
|                     ]),
 | |
|                     _: 1
 | |
|                   })
 | |
|                 ], 2)
 | |
|               ]),
 | |
|               _: 1
 | |
|             }, 8, ["features"]),
 | |
|             createBaseVNode("div", {
 | |
|               class: normalizeClass(unref($style).group)
 | |
|             }, [
 | |
|               createVNode(SaveButton, {
 | |
|                 type: "primary",
 | |
|                 saved: !unref(uiStore).stateIsDirty && !isNewWorkflow.value,
 | |
|                 disabled: isWorkflowSaving.value || _ctx.readOnly || _ctx.isArchived || !isNewWorkflow.value && !workflowPermissions.value.update,
 | |
|                 "is-saving": isWorkflowSaving.value,
 | |
|                 "with-shortcut": !_ctx.readOnly && !_ctx.isArchived && workflowPermissions.value.update,
 | |
|                 "shortcut-tooltip": unref(i18n).baseText("saveWorkflowButton.hint"),
 | |
|                 "data-test-id": "workflow-save-button",
 | |
|                 onClick: onSaveButtonClick
 | |
|               }, null, 8, ["saved", "disabled", "is-saving", "with-shortcut", "shortcut-tooltip"]),
 | |
|               createVNode(_sfc_main$7, {
 | |
|                 "workflow-id": props.id,
 | |
|                 "is-feature-enabled": isWorkflowHistoryFeatureEnabled.value,
 | |
|                 "is-new-workflow": isNewWorkflow.value,
 | |
|                 onUpgrade: goToWorkflowHistoryUpgrade
 | |
|               }, null, 8, ["workflow-id", "is-feature-enabled", "is-new-workflow"])
 | |
|             ], 2),
 | |
|             createBaseVNode("div", {
 | |
|               class: normalizeClass([unref($style).workflowMenuContainer, unref($style).group])
 | |
|             }, [
 | |
|               createBaseVNode("input", {
 | |
|                 ref_key: "importFileRef",
 | |
|                 ref: importFileRef,
 | |
|                 class: normalizeClass(unref($style).hiddenInput),
 | |
|                 type: "file",
 | |
|                 "data-test-id": "workflow-import-input",
 | |
|                 onChange: _cache[1] || (_cache[1] = ($event) => handleFileImport())
 | |
|               }, null, 34),
 | |
|               createVNode(_component_N8nActionDropdown, {
 | |
|                 items: workflowMenuItems.value,
 | |
|                 "data-test-id": "workflow-menu",
 | |
|                 onSelect: onWorkflowMenuSelect
 | |
|               }, null, 8, ["items"])
 | |
|             ], 2)
 | |
|           ]),
 | |
|           _: 1
 | |
|         })
 | |
|       ], 2);
 | |
|     };
 | |
|   }
 | |
| });
 | |
| const container$1 = "_container_14b3x_123";
 | |
| const group = "_group_14b3x_138";
 | |
| const hiddenInput = "_hiddenInput_14b3x_143";
 | |
| const deleteItem = "_deleteItem_14b3x_147";
 | |
| const disabledShareButton = "_disabledShareButton_14b3x_151";
 | |
| const closeNodeViewDiscovery = "_closeNodeViewDiscovery_14b3x_155";
 | |
| const style1 = {
 | |
|   container: container$1,
 | |
|   "path-separator": "_path-separator_14b3x_132",
 | |
|   group,
 | |
|   hiddenInput,
 | |
|   deleteItem,
 | |
|   disabledShareButton,
 | |
|   closeNodeViewDiscovery
 | |
| };
 | |
| const cssModules$1 = {
 | |
|   "$style": style1
 | |
| };
 | |
| const WorkflowDetails = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__cssModules", cssModules$1], ["__scopeId", "data-v-1f7160d5"]]);
 | |
| const GithubButton = defineComponent({
 | |
|   name: "github-button",
 | |
|   props: {
 | |
|     href: String,
 | |
|     ariaLabel: String,
 | |
|     title: String,
 | |
|     dataIcon: String,
 | |
|     dataColorScheme: String,
 | |
|     dataSize: String,
 | |
|     dataShowCount: String,
 | |
|     dataText: String
 | |
|   },
 | |
|   render: function() {
 | |
|     const props = { ref: "_" };
 | |
|     for (const key in this.$props) {
 | |
|       props[hyphenate(key)] = this.$props[key];
 | |
|     }
 | |
|     return h("span", [
 | |
|       hasOwn(this.$slots, "default") ? h("a", props, this.$slots.default()) : h("a", props)
 | |
|     ]);
 | |
|   },
 | |
|   mounted: function() {
 | |
|     this.paint();
 | |
|   },
 | |
|   beforeUpdate: function() {
 | |
|     this.reset();
 | |
|   },
 | |
|   updated: function() {
 | |
|     this.paint();
 | |
|   },
 | |
|   beforeUnmount: function() {
 | |
|     this.reset();
 | |
|   },
 | |
|   methods: {
 | |
|     paint: function() {
 | |
|       if (this.$el.lastChild !== this.$refs._) {
 | |
|         return;
 | |
|       }
 | |
|       const _ = this.$el.appendChild(document.createElement("span"));
 | |
|       const _this = this;
 | |
|       __vitePreload(() => import(
 | |
|         /* webpackMode: "eager" */
 | |
|         "./buttons.esm-BOkmSohe.js"
 | |
|       ), true ? [] : void 0).then(function(module) {
 | |
|         if (_this.$el.lastChild !== _) {
 | |
|           return;
 | |
|         }
 | |
|         module.render(_.appendChild(_this.$refs._), function(el) {
 | |
|           if (_this.$el.lastChild !== _) {
 | |
|             return;
 | |
|           }
 | |
|           _.parentNode.replaceChild(el, _);
 | |
|         });
 | |
|       });
 | |
|     },
 | |
|     reset: function() {
 | |
|       if (this.$refs._ == null) {
 | |
|         return;
 | |
|       }
 | |
|       this.$el.replaceChild(
 | |
|         /** @type {HTMLAnchorElement} */
 | |
|         this.$refs._,
 | |
|         this.$el.lastChild
 | |
|       );
 | |
|     }
 | |
|   }
 | |
| });
 | |
| const _sfc_main = /* @__PURE__ */ defineComponent({
 | |
|   __name: "MainHeader",
 | |
|   setup(__props) {
 | |
|     const router = useRouter();
 | |
|     const route = useRoute();
 | |
|     const locale = useI18n$1();
 | |
|     const pushConnection = usePushConnection({ router });
 | |
|     const ndvStore = useNDVStore();
 | |
|     const uiStore = useUIStore();
 | |
|     const sourceControlStore = useSourceControlStore();
 | |
|     const workflowsStore = useWorkflowsStore();
 | |
|     const executionsStore = useExecutionsStore();
 | |
|     const settingsStore = useSettingsStore();
 | |
|     const activeHeaderTab = ref(MAIN_HEADER_TABS.WORKFLOW);
 | |
|     const workflowToReturnTo = ref("");
 | |
|     const executionToReturnTo = ref("");
 | |
|     const dirtyState = ref(false);
 | |
|     const githubButtonHidden = useLocalStorage(LOCAL_STORAGE_HIDE_GITHUB_STAR_BUTTON, false);
 | |
|     const evaluationRoutes = [VIEWS.EVALUATION_EDIT, VIEWS.EVALUATION_RUNS_DETAIL];
 | |
|     const workflowRoutes = [VIEWS.WORKFLOW, VIEWS.NEW_WORKFLOW, VIEWS.EXECUTION_DEBUG];
 | |
|     const executionRoutes = [
 | |
|       VIEWS.EXECUTION_HOME,
 | |
|       VIEWS.WORKFLOW_EXECUTIONS,
 | |
|       VIEWS.EXECUTION_PREVIEW
 | |
|     ];
 | |
|     const tabBarItems = computed(() => {
 | |
|       return [
 | |
|         { value: MAIN_HEADER_TABS.WORKFLOW, label: locale.baseText("generic.editor") },
 | |
|         { value: MAIN_HEADER_TABS.EXECUTIONS, label: locale.baseText("generic.executions") },
 | |
|         { value: MAIN_HEADER_TABS.EVALUATION, label: locale.baseText("generic.tests") }
 | |
|       ];
 | |
|     });
 | |
|     const activeNode = computed(() => ndvStore.activeNode);
 | |
|     const hideMenuBar = computed(
 | |
|       () => Boolean(activeNode.value && activeNode.value.type !== STICKY_NODE_TYPE)
 | |
|     );
 | |
|     const workflow = computed(() => workflowsStore.workflow);
 | |
|     const workflowId = computed(
 | |
|       () => String(router.currentRoute.value.params.name || workflowsStore.workflowId)
 | |
|     );
 | |
|     const onWorkflowPage = computed(() => !!(route.meta.nodeView || route.meta.keepWorkflowAlive));
 | |
|     const readOnly = computed(() => sourceControlStore.preferences.branchReadOnly);
 | |
|     const isEnterprise = computed(
 | |
|       () => settingsStore.isQueueModeEnabled && settingsStore.isWorkerViewAvailable
 | |
|     );
 | |
|     const isTelemetryEnabled = computed(() => {
 | |
|       return settingsStore.isTelemetryEnabled;
 | |
|     });
 | |
|     const showGitHubButton = computed(
 | |
|       () => !isEnterprise.value && !settingsStore.settings.inE2ETests && !githubButtonHidden.value && isTelemetryEnabled.value
 | |
|     );
 | |
|     const parentFolderForBreadcrumbs = computed(() => {
 | |
|       if (!workflow.value.parentFolder) {
 | |
|         return void 0;
 | |
|       }
 | |
|       return {
 | |
|         id: workflow.value.parentFolder.id,
 | |
|         name: workflow.value.parentFolder.name,
 | |
|         parentFolder: workflow.value.parentFolder.parentFolderId ?? void 0
 | |
|       };
 | |
|     });
 | |
|     watch(route, (to, from) => {
 | |
|       syncTabsWithRoute(to, from);
 | |
|     });
 | |
|     onBeforeMount(() => {
 | |
|       pushConnection.initialize();
 | |
|     });
 | |
|     onBeforeUnmount(() => {
 | |
|       pushConnection.terminate();
 | |
|     });
 | |
|     onMounted(async () => {
 | |
|       dirtyState.value = uiStore.stateIsDirty;
 | |
|       syncTabsWithRoute(route);
 | |
|     });
 | |
|     function isViewRoute(name) {
 | |
|       return typeof name === "string" && [evaluationRoutes, workflowRoutes, executionRoutes].flat().includes(name);
 | |
|     }
 | |
|     function syncTabsWithRoute(to, from) {
 | |
|       const routeTabMapping = [
 | |
|         { routes: evaluationRoutes, tab: MAIN_HEADER_TABS.EVALUATION },
 | |
|         { routes: executionRoutes, tab: MAIN_HEADER_TABS.EXECUTIONS },
 | |
|         { routes: workflowRoutes, tab: MAIN_HEADER_TABS.WORKFLOW }
 | |
|       ];
 | |
|       if (to.name && isViewRoute(to.name)) {
 | |
|         const matchingTab = routeTabMapping.find(({ routes }) => routes.includes(to.name));
 | |
|         if (matchingTab) {
 | |
|           activeHeaderTab.value = matchingTab.tab;
 | |
|         }
 | |
|       }
 | |
|       if (to.params.name !== "new" && typeof to.params.name === "string") {
 | |
|         workflowToReturnTo.value = to.params.name;
 | |
|       }
 | |
|       if (from?.name === VIEWS.EXECUTION_PREVIEW && to.params.name === from.params.name && typeof from.params.executionId === "string") {
 | |
|         executionToReturnTo.value = from.params.executionId;
 | |
|       }
 | |
|     }
 | |
|     function onTabSelected(tab, event) {
 | |
|       const openInNewTab = event.ctrlKey || event.metaKey;
 | |
|       switch (tab) {
 | |
|         case MAIN_HEADER_TABS.WORKFLOW:
 | |
|           void navigateToWorkflowView(openInNewTab);
 | |
|           break;
 | |
|         case MAIN_HEADER_TABS.EXECUTIONS:
 | |
|           void navigateToExecutionsView(openInNewTab);
 | |
|           break;
 | |
|         case MAIN_HEADER_TABS.EVALUATION:
 | |
|           void navigateToEvaluationsView(openInNewTab);
 | |
|           break;
 | |
|       }
 | |
|     }
 | |
|     async function navigateToWorkflowView(openInNewTab) {
 | |
|       let routeToNavigateTo;
 | |
|       if (!["", "new", PLACEHOLDER_EMPTY_WORKFLOW_ID].includes(workflowToReturnTo.value)) {
 | |
|         routeToNavigateTo = {
 | |
|           name: VIEWS.WORKFLOW,
 | |
|           params: { name: workflowToReturnTo.value }
 | |
|         };
 | |
|       } else {
 | |
|         routeToNavigateTo = { name: VIEWS.NEW_WORKFLOW };
 | |
|       }
 | |
|       if (openInNewTab) {
 | |
|         const { href } = router.resolve(routeToNavigateTo);
 | |
|         window.open(href, "_blank");
 | |
|       } else if (route.name !== routeToNavigateTo.name) {
 | |
|         if (route.name === VIEWS.NEW_WORKFLOW) {
 | |
|           uiStore.stateIsDirty = dirtyState.value;
 | |
|         }
 | |
|         activeHeaderTab.value = MAIN_HEADER_TABS.WORKFLOW;
 | |
|         await router.push(routeToNavigateTo);
 | |
|       }
 | |
|     }
 | |
|     async function navigateToExecutionsView(openInNewTab) {
 | |
|       const routeWorkflowId = workflowId.value === PLACEHOLDER_EMPTY_WORKFLOW_ID ? "new" : workflowId.value;
 | |
|       const executionToReturnToValue = executionsStore.activeExecution?.id || executionToReturnTo.value;
 | |
|       const routeToNavigateTo = executionToReturnToValue ? {
 | |
|         name: VIEWS.EXECUTION_PREVIEW,
 | |
|         params: { name: routeWorkflowId, executionId: executionToReturnToValue }
 | |
|       } : {
 | |
|         name: VIEWS.EXECUTION_HOME,
 | |
|         params: { name: routeWorkflowId }
 | |
|       };
 | |
|       if (openInNewTab) {
 | |
|         const { href } = router.resolve(routeToNavigateTo);
 | |
|         window.open(href, "_blank");
 | |
|       } else if (route.name !== routeToNavigateTo.name) {
 | |
|         dirtyState.value = uiStore.stateIsDirty;
 | |
|         workflowToReturnTo.value = workflowId.value;
 | |
|         activeHeaderTab.value = MAIN_HEADER_TABS.EXECUTIONS;
 | |
|         await router.push(routeToNavigateTo);
 | |
|       }
 | |
|     }
 | |
|     async function navigateToEvaluationsView(openInNewTab) {
 | |
|       const routeWorkflowId = workflowId.value === PLACEHOLDER_EMPTY_WORKFLOW_ID ? "new" : workflowId.value;
 | |
|       const routeToNavigateTo = {
 | |
|         name: VIEWS.EVALUATION_EDIT,
 | |
|         params: { name: routeWorkflowId }
 | |
|       };
 | |
|       if (openInNewTab) {
 | |
|         const { href } = router.resolve(routeToNavigateTo);
 | |
|         window.open(href, "_blank");
 | |
|       } else if (route.name !== routeToNavigateTo.name) {
 | |
|         dirtyState.value = uiStore.stateIsDirty;
 | |
|         workflowToReturnTo.value = workflowId.value;
 | |
|         activeHeaderTab.value = MAIN_HEADER_TABS.EXECUTIONS;
 | |
|         await router.push(routeToNavigateTo);
 | |
|       }
 | |
|     }
 | |
|     function hideGithubButton() {
 | |
|       githubButtonHidden.value = true;
 | |
|     }
 | |
|     return (_ctx, _cache) => {
 | |
|       const _component_N8nIcon = N8nIcon;
 | |
|       return openBlock(), createElementBlock("div", {
 | |
|         class: normalizeClass(_ctx.$style.container)
 | |
|       }, [
 | |
|         createBaseVNode("div", {
 | |
|           class: normalizeClass({ [_ctx.$style["main-header"]]: true, [_ctx.$style.expanded]: !unref(uiStore).sidebarMenuCollapsed })
 | |
|         }, [
 | |
|           withDirectives(createBaseVNode("div", {
 | |
|             class: normalizeClass(_ctx.$style["top-menu"])
 | |
|           }, [
 | |
|             workflow.value?.name ? (openBlock(), createBlock(WorkflowDetails, {
 | |
|               key: 0,
 | |
|               id: workflow.value.id,
 | |
|               tags: workflow.value.tags,
 | |
|               name: workflow.value.name,
 | |
|               meta: workflow.value.meta,
 | |
|               scopes: workflow.value.scopes,
 | |
|               active: workflow.value.active,
 | |
|               "read-only": readOnly.value,
 | |
|               "current-folder": parentFolderForBreadcrumbs.value,
 | |
|               "is-archived": workflow.value.isArchived
 | |
|             }, null, 8, ["id", "tags", "name", "meta", "scopes", "active", "read-only", "current-folder", "is-archived"])) : createCommentVNode("", true),
 | |
|             showGitHubButton.value ? (openBlock(), createElementBlock("div", {
 | |
|               key: 1,
 | |
|               class: normalizeClass([_ctx.$style["github-button"], "hidden-sm-and-down"])
 | |
|             }, [
 | |
|               createBaseVNode("div", {
 | |
|                 class: normalizeClass(_ctx.$style["github-button-container"])
 | |
|               }, [
 | |
|                 createVNode(unref(GithubButton), {
 | |
|                   href: unref(N8N_MAIN_GITHUB_REPO_URL),
 | |
|                   "data-color-scheme": unref(uiStore).appliedTheme,
 | |
|                   "data-size": "large",
 | |
|                   "data-show-count": "true",
 | |
|                   "aria-label": unref(locale).baseText("editor.mainHeader.githubButton.label")
 | |
|                 }, {
 | |
|                   default: withCtx(() => [
 | |
|                     createTextVNode(toDisplayString(unref(locale).baseText("generic.star")), 1)
 | |
|                   ]),
 | |
|                   _: 1
 | |
|                 }, 8, ["href", "data-color-scheme", "aria-label"]),
 | |
|                 createVNode(_component_N8nIcon, {
 | |
|                   class: normalizeClass(_ctx.$style["close-github-button"]),
 | |
|                   icon: "circle-x",
 | |
|                   size: "medium",
 | |
|                   onClick: hideGithubButton
 | |
|                 }, null, 8, ["class"])
 | |
|               ], 2)
 | |
|             ], 2)) : createCommentVNode("", true)
 | |
|           ], 2), [
 | |
|             [vShow, !hideMenuBar.value]
 | |
|           ]),
 | |
|           onWorkflowPage.value ? (openBlock(), createBlock(TabBar, {
 | |
|             key: 0,
 | |
|             items: tabBarItems.value,
 | |
|             "model-value": activeHeaderTab.value,
 | |
|             "onUpdate:modelValue": onTabSelected
 | |
|           }, null, 8, ["items", "model-value"])) : createCommentVNode("", true)
 | |
|         ], 2)
 | |
|       ], 2);
 | |
|     };
 | |
|   }
 | |
| });
 | |
| const container = "_container_gg3to_123";
 | |
| const style0 = {
 | |
|   container,
 | |
|   "main-header": "_main-header_gg3to_130",
 | |
|   "top-menu": "_top-menu_gg3to_138",
 | |
|   "github-button": "_github-button_gg3to_149",
 | |
|   "close-github-button": "_close-github-button_gg3to_158",
 | |
|   "github-button-container": "_github-button-container_gg3to_173"
 | |
| };
 | |
| const cssModules = {
 | |
|   "$style": style0
 | |
| };
 | |
| const MainHeader = /* @__PURE__ */ _export_sfc(_sfc_main, [["__cssModules", cssModules]]);
 | |
| export {
 | |
|   MainHeader as default
 | |
| };
 |