2224 lines
84 KiB
JavaScript
Executable File
2224 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-CeNA_ukL.js";
|
|
import { _ as __unplugin_components_0, W as WorkflowActivator } from "./WorkflowActivator-BxX80zGr.js";
|
|
import { u as useBeforeUnload } from "./useBeforeUnload-DGets1eb.js";
|
|
import { _ as _sfc_main$f } from "./PushConnectionTracker.vue_vue_type_script_setup_true_lang-CX6d3I0M.js";
|
|
import { T as Tag } from "./Tag-ZyDcgFEj.js";
|
|
import { u as usePushConnection } from "./usePushConnection-Brn1MCOr.js";
|
|
import "./ProjectBreadcrumb-CcYMzyYS.js";
|
|
import "./useWorkflowActivate-CuNKq-WZ.js";
|
|
import "./global-link-actions-C5l8bN84.js";
|
|
import "./readyToRunWorkflows.store-DGBtTmGX.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 WORKFLOW_NAME_MAX_WIDTH_SMALL_SCREENS = 150;
|
|
const WORKFLOW_NAME_MAX_WIDTH_WIDE_SCREENS = 200;
|
|
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 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": ["XS", "SM"].includes(bp) ? WORKFLOW_NAME_MAX_WIDTH_SMALL_SCREENS : WORKFLOW_NAME_MAX_WIDTH_WIDE_SCREENS,
|
|
"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-3cc77b55"]]);
|
|
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
|
|
};
|