n8n-editor-ui
Version:
Workflow Editor UI for n8n
2,326 lines • 95.9 kB
JavaScript
import { o as __toESM } from "./chunk-6z4oVpB-.js";
import { C as computed, Cn as toDisplayString, D as createElementBlock, E as createCommentVNode, G as nextTick, Gt as unref, Ht as toRefs, I as guardReactiveProps, It as ref, J as onBeforeUnmount, M as createVNode, Nt as onScopeDispose, P as defineComponent, Q as onUnmounted, T as createBlock, Vt as toRef, W as mergeProps, Z as onMounted, _ as Fragment, _t as watch, at as renderSlot, bn as normalizeStyle, bt as withCtx, ct as resolveDynamicComponent, et as openBlock, f as vModelText, h as withModifiers, it as renderList, j as createTextVNode, jt as isRef, k as createSlots, m as withKeys, mt as useTemplateRef, nt as provide, st as resolveDirective, ut as useAttrs, vn as normalizeClass, w as createBaseVNode, xt as withDirectives, yn as normalizeProps, z as inject, zt as shallowRef } from "./vue.runtime.esm-bundler-tP5dCd7J.js";
import { A as useFileDialog, B as useTimestamp, M as useLocalStorage, _t as useI18n, b as useActiveElement, vt as I18nT } from "./_MapCache-ficiegRb.js";
import { C as N8nResizeWrapper_default, Sn as N8nButton_default, T as N8nRadioButtons_default, gt as require_markdown_it_link_attributes, ht as N8nTooltip_default, mt as N8nLink_default, ot as N8nActionDropdown_default, wn as N8nIcon_default, wt as useProvideTooltipAppendTo, xn as N8nText_default, xt as N8nIconButton_default } from "./src-DgvumQTi.js";
import { t as __plugin_vue_export_helper_default } from "./_plugin-vue_export-helper-BwBpWJRZ.js";
import { v as useRoute, y as useRouter } from "./truncate-B1HVeveJ.js";
import { C as getScrollbarWidth, Gn as useNodeHelpers, Kn as injectWorkflowState, M as useNDVStore, Mt as waitingNodeTooltip, N as useWorkflowsStore, Nt as useNodeTypesStore, O as useTelemetry, Yn as useDocumentTitle, Zn as useSourceControlStore, fn as require_upperFirst, fs as v4_default, gr as useCanvasStore, m as useMessage, oa as useUIStore, v as useToast, xt as isChatNode } from "./builder.store-sBTWwxRU.js";
import { Ro as VIEWS, rs as MODAL_CONFIRM, tc as PopOutWindowKey } from "./constants-BbpucWL4.js";
import { r as useRootStore } from "./_baseOrderBy-B_6CV8x6.js";
import { n as ndvEventBus } from "./assistant.store-bS9AHOoo.js";
import { t as useRunWorkflow } from "./useRunWorkflow-BQJbxiOd.js";
import { t as usePinnedData } from "./usePinnedData-CL2qWhwp.js";
import { a as useLogsStore, c as LOCAL_STORAGE_PANEL_WIDTH, d as chatEventBus, i as useExperimentalNdvStore, l as LOGS_PANEL_STATE, o as LOCAL_STORAGE_OVERVIEW_PANEL_WIDTH, r as canvasEventBus, s as LOCAL_STORAGE_PANEL_HEIGHT, t as useCanvasOperations, u as LOG_DETAILS_PANEL_STATE } from "./useCanvasOperations-CAHezq7d.js";
import { t as NodeIcon_default } from "./NodeIcon-BVhrZNYI.js";
import { t as KeyboardShortcutTooltip_default } from "./KeyboardShortcutTooltip-B4E72qs1.js";
import { t as useClipboard } from "./useClipboard-CyG-SBdG.js";
import { n as RunDataItemCount_default, t as RunData_default } from "./RunData-B4jIonVZ.js";
import { t as NDVEmptyState_default } from "./NDVEmptyState-CpUQ-969.js";
import { t as VueMarkdown_default } from "./VueMarkdown-D9lDzMKr.js";
import { t as useKeybindings } from "./useKeybindings-CKeFso4-.js";
import { t as convertFileToBinaryData } from "./fileUtils-D_SiS8no.js";
import { t as ChatFile_default } from "./ChatFile-Dtf_b24Y.js";
import { a as bash, i as javascript, n as typescript, r as python, t as xml } from "./xml-B6veOr9z.js";
import { _ as isPlaceholderLog, a as LogsViewConsumedTokenCountText_default, b as restoreChatHistory, f as getDepth, g as getTotalConsumedTokens, h as getSubtreeTotalConsumedTokens, i as LogsViewNodeName_default, l as findLogEntryRec, m as getInputKey, n as useLogsExecutionData, p as getEntryAtRelativeIndex, r as LogsOverviewRows_default, s as extractBotResponse, t as useLogsTreeExpand, u as findSelectedLogEntry, v as isSubNodeLog, y as processFiles } from "./useLogsTreeExpand-WR32ACA6.js";
import { t as core_default } from "./core-BbiIkN91.js";
function constructChatWebsocketUrl(url, executionId, sessionId$1, isPublic) {
const baseUrl = new URL(url).origin;
const wsProtocol = baseUrl.startsWith("https") ? "wss" : "ws";
return `${baseUrl.replace(/^https?/, wsProtocol)}/chat?sessionId=${sessionId$1}&executionId=${executionId}${isPublic ? "&isPublic=true" : ""}`;
}
function useChatMessaging({ chatTrigger, sessionId: sessionId$1, executionResultData, onRunChatWorkflow, ws, onNewMessage }) {
const locale = useI18n();
const { showError } = useToast();
const previousMessageIndex = ref(0);
const isLoading = ref(false);
const setLoadingState = (loading) => {
isLoading.value = loading;
};
async function getKeyedFiles(files) {
const binaryData = {};
await Promise.all(files.map(async (file, index) => {
const data = await convertFileToBinaryData(file);
const key = `data${index}`;
binaryData[key] = data;
}));
return binaryData;
}
function extractFileMeta(file) {
return {
fileName: file.name,
fileSize: `${file.size} bytes`,
fileExtension: file.name.split(".").pop() ?? "",
fileType: file.type.split("/")[0],
mimeType: file.type
};
}
async function startWorkflowWithMessage(message, files) {
const triggerNode = chatTrigger.value;
if (!triggerNode) {
showError(/* @__PURE__ */ new Error("Chat Trigger Node could not be found!"), "Trigger Node not found");
return;
}
const inputKey = getInputKey(triggerNode);
const inputPayload = { json: {
sessionId: sessionId$1.value,
action: "sendMessage",
[inputKey]: message
} };
if (files && files.length > 0) {
const filesMeta = files.map((file) => extractFileMeta(file));
const binaryData = await getKeyedFiles(files);
inputPayload.json.files = filesMeta;
inputPayload.binary = binaryData;
}
const nodeData = {
startTime: Date.now(),
executionTime: 0,
executionIndex: 0,
executionStatus: "success",
data: { main: [[inputPayload]] },
source: [null]
};
isLoading.value = true;
const response = await onRunChatWorkflow({
triggerNode: triggerNode.name,
nodeData,
source: "RunData.ManualChatMessage",
message
});
isLoading.value = false;
ws.value = null;
if (!response?.executionId) return;
if (triggerNode.parameters.options?.responseMode === "responseNodes") return;
const chatMessage = executionResultData.value ? extractBotResponse(executionResultData.value, response.executionId, locale.baseText("chat.window.chat.response.empty")) : void 0;
if (chatMessage !== void 0) onNewMessage(chatMessage);
}
async function sendMessage(message, files) {
previousMessageIndex.value = 0;
if (message.trim() === "" && (!files || files.length === 0)) {
showError(new Error(locale.baseText("chat.window.chat.provideMessage")), locale.baseText("chat.window.chat.emptyChatMessage"));
return;
}
const pinnedChatData = usePinnedData(chatTrigger.value);
if (pinnedChatData.hasData.value) {
if (!(await useMessage().confirm(locale.baseText("chat.window.chat.unpinAndExecute.description"), locale.baseText("chat.window.chat.unpinAndExecute.title"), {
confirmButtonText: locale.baseText("chat.window.chat.unpinAndExecute.confirm"),
cancelButtonText: locale.baseText("chat.window.chat.unpinAndExecute.cancel")
}) === "confirm")) return;
pinnedChatData.unsetData("unpin-and-send-chat-message-modal");
}
const newMessage = {
text: message,
sender: "user",
sessionId: sessionId$1.value,
id: v4_default(),
files
};
onNewMessage(newMessage);
if (ws.value?.readyState === WebSocket.OPEN && !isLoading.value) {
ws.value.send(JSON.stringify({
sessionId: sessionId$1.value,
action: "sendMessage",
chatInput: message,
files: await processFiles(files)
}));
isLoading.value = true;
} else await startWorkflowWithMessage(newMessage.text, files);
}
return {
previousMessageIndex,
isLoading: computed(() => isLoading.value),
setLoadingState,
sendMessage
};
}
const ChatSymbol = "Chat";
const ChatOptionsSymbol = "ChatOptions";
var ChatSymbol$1 = "Chat";
function useChatState(isReadOnly) {
const locale = useI18n();
const workflowsStore = useWorkflowsStore();
const workflowState = injectWorkflowState();
const rootStore = useRootStore();
const logsStore = useLogsStore();
const router = useRouter();
const nodeHelpers = useNodeHelpers();
const { runWorkflow } = useRunWorkflow({ router });
const ws = ref(null);
const messages$1 = computed(() => logsStore.chatSessionMessages);
const currentSessionId = computed(() => logsStore.chatSessionId);
const previousChatMessages = computed(() => workflowsStore.getPastChatMessages);
const chatTriggerNode = computed(() => workflowsStore.allNodes.find(isChatNode) ?? null);
const allowFileUploads = computed(() => (chatTriggerNode.value?.parameters?.options)?.allowFileUploads === true);
const allowedFilesMimeTypes = computed(() => (chatTriggerNode.value?.parameters?.options)?.allowedFilesMimeTypes?.toString() ?? "");
const respondNodesResponseMode = computed(() => (chatTriggerNode.value?.parameters?.options)?.responseMode === "responseNodes");
const { sendMessage, isLoading, setLoadingState } = useChatMessaging({
chatTrigger: chatTriggerNode,
sessionId: currentSessionId,
executionResultData: computed(() => workflowsStore.getWorkflowExecution?.data?.resultData),
onRunChatWorkflow,
onNewMessage: logsStore.addChatMessage,
ws
});
function createChatConfig(params) {
return {
chatConfig: {
messages: params.messages,
sendMessage: params.sendMessage,
initialMessages: ref([]),
currentSessionId: params.currentSessionId,
waitingForResponse: params.isLoading
},
chatOptions: {
i18n: { en: {
title: "",
footer: "",
subtitle: "",
inputPlaceholder: params.locale.baseText("chat.window.chat.placeholder"),
getStarted: "",
closeButtonTooltip: ""
} },
webhookUrl: "",
mode: "window",
showWindowCloseButton: true,
disabled: params.isDisabled,
allowFileUploads: params.allowFileUploads,
allowedFilesMimeTypes
}
};
}
const { chatConfig, chatOptions } = createChatConfig({
messages: messages$1,
sendMessage,
currentSessionId,
isLoading,
isDisabled: computed(() => isReadOnly),
allowFileUploads,
locale
});
const restoredChatMessages = computed(() => restoreChatHistory(workflowsStore.workflowExecutionData, locale.baseText("chat.window.chat.response.empty")));
provide(ChatSymbol$1, chatConfig);
provide(ChatOptionsSymbol, chatOptions);
async function createExecutionPromise() {
return await new Promise((resolve) => {
const resolveIfFinished = (isRunning) => {
if (!isRunning) {
unwatch();
resolve();
}
};
const unwatch = watch(() => workflowsStore.isWorkflowRunning, resolveIfFinished);
resolveIfFinished(workflowsStore.isWorkflowRunning);
});
}
async function onRunChatWorkflow(payload) {
const runWorkflowOptions = {
triggerNode: payload.triggerNode,
nodeData: payload.nodeData,
source: payload.source
};
if (workflowsStore.chatPartialExecutionDestinationNode) {
runWorkflowOptions.destinationNode = {
nodeName: workflowsStore.chatPartialExecutionDestinationNode,
mode: "inclusive"
};
workflowsStore.chatPartialExecutionDestinationNode = null;
}
const response = await runWorkflow(runWorkflowOptions);
if (response) {
if (respondNodesResponseMode.value) {
const wsUrl = constructChatWebsocketUrl(rootStore.urlBaseEditor, response.executionId, currentSessionId.value, false);
ws.value = new WebSocket(wsUrl);
ws.value.onmessage = (event) => {
if (event.data === "n8n|heartbeat") {
ws.value?.send("n8n|heartbeat-ack");
return;
}
if (event.data === "n8n|continue") {
setLoadingState(true);
return;
}
setLoadingState(false);
const newMessage = {
text: event.data,
sender: "bot",
sessionId: currentSessionId.value,
id: v4_default()
};
logsStore.addChatMessage(newMessage);
if (logsStore.isOpen) chatEventBus.emit("focusInput");
};
ws.value.onclose = () => {
setLoadingState(false);
ws.value = null;
};
}
await createExecutionPromise();
workflowsStore.appendChatMessage(payload.message);
return response;
}
}
function refreshSession() {
workflowState.setWorkflowExecutionData(null);
nodeHelpers.updateNodesExecutionIssues();
logsStore.resetChatSessionId();
logsStore.resetMessages();
if (logsStore.isOpen) chatEventBus.emit("focusInput");
}
function displayExecution(executionId) {
const route = router.resolve({
name: VIEWS.EXECUTION_PREVIEW,
params: {
name: workflowsStore.workflowId,
executionId
}
});
window.open(route.href, "_blank");
}
watch(() => workflowsStore.workflowId, (_newWorkflowId, prevWorkflowId) => {
if (!prevWorkflowId) return;
refreshSession();
});
return {
currentSessionId: computed(() => logsStore.chatSessionId),
messages: computed(() => isReadOnly ? restoredChatMessages.value : logsStore.chatSessionMessages),
previousChatMessages,
sendMessage,
refreshSession,
displayExecution
};
}
var LogsPanelHeader_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "LogsPanelHeader",
props: {
title: {},
isClickable: { type: Boolean }
},
emits: ["click"],
setup(__props, { emit: __emit }) {
const emit = __emit;
function handleClick() {
if (__props.isClickable) emit("click");
}
return (_ctx, _cache) => {
return openBlock(), createElementBlock("header", {
class: normalizeClass([_ctx.$style.container, { [_ctx.$style.clickable]: _ctx.isClickable }]),
onClick: handleClick
}, [createVNode(unref(N8nText_default), {
class: normalizeClass(_ctx.$style.title),
bold: true,
size: "small"
}, {
default: withCtx(() => [renderSlot(_ctx.$slots, "title", {}, () => [createTextVNode(toDisplayString(_ctx.title), 1)])]),
_: 3
}, 8, ["class"]), createBaseVNode("div", { class: normalizeClass(_ctx.$style.actions) }, [renderSlot(_ctx.$slots, "actions")], 2)], 2);
};
}
});
var LogsPanelHeader_vue_vue_type_style_index_0_lang_module_default = {
container: "_container_1y331_123",
clickable: "_clickable_1y331_135",
title: "_title_1y331_143",
actions: "_actions_1y331_151"
};
var LogsPanelHeader_default = /* @__PURE__ */ __plugin_vue_export_helper_default(LogsPanelHeader_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": LogsPanelHeader_vue_vue_type_style_index_0_lang_module_default }]]);
var import_upperFirst = /* @__PURE__ */ __toESM(require_upperFirst());
var LogsViewExecutionSummary_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "LogsViewExecutionSummary",
props: {
status: {},
consumedTokens: {},
startTime: {},
timeTook: {}
},
setup(__props) {
const locale = useI18n();
const now = useTimestamp({ interval: 1e3 });
const executionStatusText = computed(() => __props.status === "running" || __props.status === "waiting" ? locale.baseText("logs.overview.body.summaryText.for", { interpolate: {
status: (0, import_upperFirst.default)(__props.status),
time: locale.displayTimer(Math.floor((now.value - __props.startTime) / 1e3) * 1e3, true)
} }) : __props.timeTook === void 0 ? (0, import_upperFirst.default)(__props.status) : locale.baseText("logs.overview.body.summaryText.in", { interpolate: {
status: (0, import_upperFirst.default)(__props.status),
time: locale.displayTimer(__props.timeTook, true)
} }));
return (_ctx, _cache) => {
return openBlock(), createBlock(unref(N8nText_default), {
tag: "div",
color: "text-light",
size: "small",
class: normalizeClass(_ctx.$style.container)
}, {
default: withCtx(() => [createBaseVNode("span", null, toDisplayString(executionStatusText.value), 1), _ctx.consumedTokens.totalTokens > 0 ? (openBlock(), createBlock(LogsViewConsumedTokenCountText_default, {
key: 0,
"consumed-tokens": _ctx.consumedTokens
}, null, 8, ["consumed-tokens"])) : createCommentVNode("", true)]),
_: 1
}, 8, ["class"]);
};
}
});
var LogsViewExecutionSummary_vue_vue_type_style_index_0_lang_module_default = { container: "_container_1sayb_123" };
var LogsViewExecutionSummary_default = /* @__PURE__ */ __plugin_vue_export_helper_default(LogsViewExecutionSummary_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": LogsViewExecutionSummary_vue_vue_type_style_index_0_lang_module_default }]]);
function useClearExecutionButtonVisible() {
const route = useRoute();
const sourceControlStore = useSourceControlStore();
const workflowsStore = useWorkflowsStore();
const workflowExecutionData = computed(() => workflowsStore.workflowExecutionData);
const isWorkflowRunning = computed(() => workflowsStore.isWorkflowRunning);
const isReadOnlyRoute = computed(() => !!route?.meta?.readOnlyCanvas);
const { editableWorkflow } = useCanvasOperations();
const nodeTypesStore = useNodeTypesStore();
const isReadOnlyEnvironment = computed(() => sourceControlStore.preferences.branchReadOnly);
const allTriggerNodesDisabled = computed(() => editableWorkflow.value.nodes.filter((node) => nodeTypesStore.isTriggerNode(node.type)).every((node) => node.disabled));
return computed(() => !isReadOnlyRoute.value && !isReadOnlyEnvironment.value && !isWorkflowRunning.value && !allTriggerNodesDisabled.value && !!workflowExecutionData.value);
}
var LogsOverviewPanel_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "LogsOverviewPanel",
props: {
isOpen: { type: Boolean },
selected: {},
isReadOnly: { type: Boolean },
isCompact: { type: Boolean },
execution: {},
entries: {},
flatLogEntries: {},
latestNodeInfo: {},
isHeaderClickable: { type: Boolean }
},
emits: [
"clickHeader",
"select",
"clearExecutionData",
"openNdv",
"toggleExpanded"
],
setup(__props, { emit: __emit }) {
const emit = __emit;
const locale = useI18n();
const isClearExecutionButtonVisible = useClearExecutionButtonVisible();
const isEmpty = computed(() => __props.flatLogEntries.length === 0 || __props.execution === void 0);
const switchViewOptions = computed(() => [{
label: locale.baseText("logs.overview.header.switch.overview"),
value: "overview"
}, {
label: locale.baseText("logs.overview.header.switch.details"),
value: "details"
}]);
const hasStaticScrollbar = getScrollbarWidth() > 0;
const consumedTokens = computed(() => getTotalConsumedTokens(...__props.entries.map((entry) => getSubtreeTotalConsumedTokens(entry, false))));
const timeTook = computed(() => __props.execution?.startedAt && __props.execution.stoppedAt ? +new Date(__props.execution.stoppedAt) - +new Date(__props.execution.startedAt) : void 0);
const shouldShowTokenCountColumn = computed(() => consumedTokens.value.totalTokens > 0 || __props.entries.some((entry) => getSubtreeTotalConsumedTokens(entry, true).totalTokens > 0));
function handleSwitchView(value) {
emit("select", value === "overview" ? void 0 : __props.flatLogEntries[0]);
}
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
class: normalizeClass([_ctx.$style.container, hasStaticScrollbar ? _ctx.$style.staticScrollBar : ""]),
"data-test-id": "logs-overview"
}, [createVNode(LogsPanelHeader_default, {
title: unref(locale).baseText("logs.overview.header.title"),
"data-test-id": "logs-overview-header",
"is-clickable": _ctx.isHeaderClickable,
onClick: _cache[1] || (_cache[1] = ($event) => emit("clickHeader"))
}, {
actions: withCtx(() => [unref(isClearExecutionButtonVisible) ? (openBlock(), createBlock(unref(N8nTooltip_default), {
key: 0,
content: unref(locale).baseText("logs.overview.header.actions.clearExecution.tooltip")
}, {
default: withCtx(() => [createVNode(unref(N8nButton_default), {
size: "mini",
type: "secondary",
icon: "trash-2",
"icon-size": "medium",
"data-test-id": "clear-execution-data-button",
class: normalizeClass(_ctx.$style.clearButton),
onClick: _cache[0] || (_cache[0] = withModifiers(($event) => emit("clearExecutionData"), ["stop"]))
}, {
default: withCtx(() => [createTextVNode(toDisplayString(unref(locale).baseText("logs.overview.header.actions.clearExecution")), 1)]),
_: 1
}, 8, ["class"])]),
_: 1
}, 8, ["content"])) : createCommentVNode("", true), renderSlot(_ctx.$slots, "actions")]),
_: 3
}, 8, ["title", "is-clickable"]), _ctx.isOpen ? (openBlock(), createElementBlock("div", {
key: 0,
class: normalizeClass([_ctx.$style.content, isEmpty.value ? _ctx.$style.empty : ""]),
"data-test-id": "logs-overview-body"
}, [isEmpty.value || _ctx.execution === void 0 ? (openBlock(), createBlock(unref(N8nText_default), {
key: 0,
tag: "p",
size: "medium",
color: "text-base",
class: normalizeClass(_ctx.$style.emptyText),
"data-test-id": "logs-overview-empty"
}, {
default: withCtx(() => [createTextVNode(toDisplayString(unref(locale).baseText("logs.overview.body.empty.message")), 1)]),
_: 1
}, 8, ["class"])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
createVNode(LogsViewExecutionSummary_default, {
"data-test-id": "logs-overview-status",
class: normalizeClass(_ctx.$style.summary),
status: _ctx.execution.status,
"consumed-tokens": consumedTokens.value,
"start-time": +new Date(_ctx.execution.startedAt),
"time-took": timeTook.value
}, null, 8, [
"class",
"status",
"consumed-tokens",
"start-time",
"time-took"
]),
createVNode(LogsOverviewRows_default, {
"is-read-only": _ctx.isReadOnly,
selected: _ctx.selected,
"is-compact": _ctx.isCompact,
"should-show-token-count-column": shouldShowTokenCountColumn.value,
"latest-node-info": _ctx.latestNodeInfo,
"flat-log-entries": _ctx.flatLogEntries,
"can-open-ndv": true,
execution: _ctx.execution,
onToggleExpanded: _cache[2] || (_cache[2] = ($event) => emit("toggleExpanded", $event)),
onOpenNdv: _cache[3] || (_cache[3] = ($event) => emit("openNdv", $event)),
onSelect: _cache[4] || (_cache[4] = ($event) => emit("select", $event))
}, null, 8, [
"is-read-only",
"selected",
"is-compact",
"should-show-token-count-column",
"latest-node-info",
"flat-log-entries",
"execution"
]),
createVNode(unref(N8nRadioButtons_default), {
size: "small-medium",
class: normalizeClass(_ctx.$style.switchViewButtons),
"model-value": _ctx.selected ? "details" : "overview",
options: switchViewOptions.value,
"onUpdate:modelValue": handleSwitchView
}, null, 8, [
"class",
"model-value",
"options"
])
], 64))], 2)) : createCommentVNode("", true)], 2);
};
}
});
var LogsOverviewPanel_vue_vue_type_style_index_0_lang_module_default = {
container: "_container_svlfi_123",
clearButton: "_clearButton_svlfi_133",
content: "_content_svlfi_139",
empty: "_empty_svlfi_149",
emptyText: "_emptyText_svlfi_154",
summary: "_summary_svlfi_159",
switchViewButtons: "_switchViewButtons_svlfi_163"
};
var LogsOverviewPanel_default = /* @__PURE__ */ __plugin_vue_export_helper_default(LogsOverviewPanel_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": LogsOverviewPanel_vue_vue_type_style_index_0_lang_module_default }]]);
function useChat() {
return inject(ChatSymbol);
}
function useOptions() {
return { options: inject(ChatOptionsSymbol) };
}
function useI18n$1() {
const { options } = useOptions();
const language = options?.defaultLanguage ?? "en";
function t(key) {
const val = options?.i18n?.[language]?.[key];
if (isRef(val)) return val.value;
return val ?? key;
}
function te(key) {
return !!options?.i18n?.[language]?.[key];
}
return {
t,
te
};
}
var import_markdown_it_link_attributes = /* @__PURE__ */ __toESM(require_markdown_it_link_attributes());
var _hoisted_1$5 = {
key: 0,
class: "chat-message-actions"
};
var _hoisted_2$2 = {
key: 2,
class: "chat-message-files"
};
var Message_default = /* @__PURE__ */ defineComponent({
__name: "Message",
props: { message: {} },
setup(__props, { expose: __expose }) {
const props = __props;
core_default.registerLanguage("javascript", javascript);
core_default.registerLanguage("typescript", typescript);
core_default.registerLanguage("python", python);
core_default.registerLanguage("xml", xml);
core_default.registerLanguage("bash", bash);
const { message } = toRefs(props);
const { options } = useOptions();
const messageContainer = ref(null);
const fileSources = ref({});
const messageText = computed(() => {
return message.value.text || "<Empty response>";
});
const classes = computed(() => {
return {
"chat-message-from-user": message.value.sender === "user",
"chat-message-from-bot": message.value.sender === "bot",
"chat-message-transparent": message.value.transparent === true
};
});
const linksNewTabPlugin = (vueMarkdownItInstance) => {
vueMarkdownItInstance.use(import_markdown_it_link_attributes.default, { attrs: {
target: "_blank",
rel: "noopener"
} });
};
const scrollToView = () => {
if (messageContainer.value?.scrollIntoView) messageContainer.value.scrollIntoView({ block: "start" });
};
const markdownOptions = { highlight(str, lang) {
if (lang && core_default.getLanguage(lang)) try {
return core_default.highlight(str, { language: lang }).value;
} catch {}
return "";
} };
const messageComponents = { ...options?.messageComponents ?? {} };
__expose({ scrollToView });
const readFileAsDataURL = async (file) => await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file);
});
onMounted(async () => {
if (message.value.files) for (const file of message.value.files) try {
const dataURL = await readFileAsDataURL(file);
fileSources.value[file.name] = dataURL;
} catch (error) {
console.error("Error reading file:", error);
}
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
ref_key: "messageContainer",
ref: messageContainer,
class: normalizeClass(["chat-message", classes.value])
}, [!!_ctx.$slots.beforeMessage ? (openBlock(), createElementBlock("div", _hoisted_1$5, [renderSlot(_ctx.$slots, "beforeMessage", normalizeProps(guardReactiveProps({ message: unref(message) })))])) : createCommentVNode("", true), renderSlot(_ctx.$slots, "default", {}, () => [unref(message).type === "component" && messageComponents[unref(message).key] ? (openBlock(), createBlock(resolveDynamicComponent(messageComponents[unref(message).key]), normalizeProps(mergeProps({ key: 0 }, unref(message).arguments)), null, 16)) : (openBlock(), createBlock(unref(VueMarkdown_default), {
key: 1,
class: "chat-message-markdown",
source: messageText.value,
options: markdownOptions,
plugins: [linksNewTabPlugin]
}, null, 8, ["source", "plugins"])), (unref(message).files ?? []).length > 0 ? (openBlock(), createElementBlock("div", _hoisted_2$2, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(message).files ?? [], (file) => {
return openBlock(), createElementBlock("div", {
key: file.name,
class: "chat-message-file"
}, [createVNode(ChatFile_default, {
file,
"is-removable": false,
"is-previewable": true
}, null, 8, ["file"])]);
}), 128))])) : createCommentVNode("", true)])], 2);
};
}
});
var _hoisted_1$4 = {
viewBox: "0 0 24 24",
width: "1.2em",
height: "1.2em"
};
function render$1(_ctx, _cache) {
return openBlock(), createElementBlock("svg", _hoisted_1$4, _cache[0] || (_cache[0] = [createBaseVNode("path", {
fill: "currentColor",
d: "M16.5 6v11.5a4 4 0 0 1-4 4a4 4 0 0 1-4-4V5A2.5 2.5 0 0 1 11 2.5A2.5 2.5 0 0 1 13.5 5v10.5a1 1 0 0 1-1 1a1 1 0 0 1-1-1V6H10v9.5a2.5 2.5 0 0 0 2.5 2.5a2.5 2.5 0 0 0 2.5-2.5V5a4 4 0 0 0-4-4a4 4 0 0 0-4 4v12.5a5.5 5.5 0 0 0 5.5 5.5a5.5 5.5 0 0 0 5.5-5.5V6z"
}, null, -1)]));
}
var paperclip_default = {
name: "mdi-paperclip",
render: render$1
};
var _hoisted_1$3 = {
viewBox: "0 0 24 24",
width: "1.2em",
height: "1.2em"
};
function render(_ctx, _cache) {
return openBlock(), createElementBlock("svg", _hoisted_1$3, _cache[0] || (_cache[0] = [createBaseVNode("path", {
fill: "currentColor",
d: "m2 21l21-9L2 3v7l15 2l-15 2z"
}, null, -1)]));
}
var send_default = {
name: "mdi-send",
render
};
var _hoisted_1$2 = { class: "chat-inputs" };
var _hoisted_2$1 = {
key: 0,
class: "chat-input-left-panel"
};
var _hoisted_3$1 = ["disabled", "placeholder"];
var _hoisted_4 = { class: "chat-inputs-controls" };
var _hoisted_5 = ["disabled"];
var _hoisted_6 = ["disabled"];
var _hoisted_7 = {
key: 0,
class: "chat-files"
};
var Input_default = /* @__PURE__ */ __plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
__name: "Input",
props: { placeholder: { default: "inputPlaceholder" } },
emits: ["arrowKeyDown", "escapeKeyDown"],
setup(__props, { emit: __emit }) {
const props = __props;
const { t } = useI18n$1();
const emit = __emit;
const { options } = useOptions();
const chatStore = useChat();
const { waitingForResponse } = chatStore;
const files = ref(null);
const chatTextArea = ref(null);
const input = ref("");
const isSubmitting = ref(false);
const resizeObserver = ref(null);
const waitingForChatResponse = ref(false);
const isSubmitDisabled = computed(() => {
if (waitingForChatResponse.value) return false;
return input.value === "" || unref(waitingForResponse) || options.disabled?.value === true;
});
const isInputDisabled = computed(() => options.disabled?.value === true);
const isFileUploadDisabled = computed(() => isFileUploadAllowed.value && unref(waitingForResponse) && !options.disabled?.value);
const isFileUploadAllowed = computed(() => unref(options.allowFileUploads) === true);
const allowedFileTypes = computed(() => unref(options.allowedFilesMimeTypes));
const styleVars = computed(() => {
const controlsCount = isFileUploadAllowed.value ? 2 : 1;
return { "--controls-count": controlsCount };
});
const { open: openFileDialog, reset: resetFileDialog, onChange } = useFileDialog({
multiple: true,
reset: false
});
onChange((newFiles) => {
if (!newFiles) return;
const newFilesDT = new DataTransfer();
if (files.value) for (let i = 0; i < files.value.length; i++) newFilesDT.items.add(files.value[i]);
for (let i = 0; i < newFiles.length; i++) newFilesDT.items.add(newFiles[i]);
files.value = newFilesDT.files;
});
onMounted(() => {
chatEventBus.on("focusInput", focusChatInput);
chatEventBus.on("blurInput", blurChatInput);
chatEventBus.on("setInputValue", setInputValue);
if (chatTextArea.value) {
resizeObserver.value = new ResizeObserver((entries) => {
for (const entry of entries) if (entry.target === chatTextArea.value) adjustTextAreaHeight();
});
resizeObserver.value.observe(chatTextArea.value);
}
});
onUnmounted(() => {
chatEventBus.off("focusInput", focusChatInput);
chatEventBus.off("blurInput", blurChatInput);
chatEventBus.off("setInputValue", setInputValue);
if (resizeObserver.value) {
resizeObserver.value.disconnect();
resizeObserver.value = null;
}
});
function blurChatInput() {
if (chatTextArea.value) chatTextArea.value.blur();
}
function focusChatInput() {
if (chatTextArea.value) chatTextArea.value.focus();
}
function setInputValue(value) {
input.value = value;
focusChatInput();
}
function attachFiles() {
if (files.value) {
const filesToAttach = Array.from(files.value);
resetFileDialog();
files.value = null;
return filesToAttach;
}
return [];
}
function setupWebsocketConnection(executionId) {
if (options.webhookUrl && chatStore.currentSessionId.value) try {
const wsUrl = constructChatWebsocketUrl(options.webhookUrl, executionId, chatStore.currentSessionId.value, true);
chatStore.ws = new WebSocket(wsUrl);
chatStore.ws.onmessage = (e) => {
if (e.data === "n8n|heartbeat") {
chatStore.ws?.send("n8n|heartbeat-ack");
return;
}
if (e.data === "n8n|continue") {
waitingForChatResponse.value = false;
chatStore.waitingForResponse.value = true;
return;
}
const newMessage = {
id: v4_default(),
text: e.data,
sender: "bot"
};
chatStore.messages.value.push(newMessage);
waitingForChatResponse.value = true;
chatStore.waitingForResponse.value = false;
};
chatStore.ws.onclose = () => {
chatStore.ws = null;
waitingForChatResponse.value = false;
chatStore.waitingForResponse.value = false;
};
} catch (error) {
console.error("Error setting up websocket connection", error);
}
}
async function processFiles$1(data) {
if (!data || data.length === 0) return [];
const filePromises = data.map(async (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve({
name: file.name,
type: file.type,
data: reader.result
});
reader.onerror = () => reject(/* @__PURE__ */ new Error(`Error reading file: ${reader.error?.message ?? "Unknown error"}`));
reader.readAsDataURL(file);
});
});
return await Promise.all(filePromises);
}
async function respondToChatNode(ws, messageText) {
const sentMessage = {
id: v4_default(),
text: messageText,
sender: "user",
files: files.value ? attachFiles() : void 0
};
chatStore.messages.value.push(sentMessage);
ws.send(JSON.stringify({
sessionId: chatStore.currentSessionId.value,
action: "sendMessage",
chatInput: messageText,
files: await processFiles$1(sentMessage.files)
}));
chatStore.waitingForResponse.value = true;
waitingForChatResponse.value = false;
}
async function onSubmit(event) {
event.preventDefault();
if (isSubmitDisabled.value) return;
const messageText = input.value;
input.value = "";
isSubmitting.value = true;
if (chatStore.ws && waitingForChatResponse.value) {
await respondToChatNode(chatStore.ws, messageText);
return;
}
const response = await chatStore.sendMessage(messageText, attachFiles());
if (response?.executionId) setupWebsocketConnection(response.executionId);
isSubmitting.value = false;
}
async function onSubmitKeydown(event) {
if (event.shiftKey || event.isComposing) return;
await onSubmit(event);
adjustTextAreaHeight();
}
function onFileRemove(file) {
if (!files.value) return;
const dt = new DataTransfer();
for (let i = 0; i < files.value.length; i++) {
const currentFile = files.value[i];
if (file.name !== currentFile.name) dt.items.add(currentFile);
}
resetFileDialog();
files.value = dt.files;
}
function onKeyDown(event) {
if (event.key === "ArrowUp" || event.key === "ArrowDown") {
event.preventDefault();
emit("arrowKeyDown", {
key: event.key,
currentInputValue: input.value
});
} else if (event.key === "Escape") {
event.preventDefault();
emit("escapeKeyDown", { currentInputValue: input.value });
}
}
function onOpenFileDialog() {
if (isFileUploadDisabled.value) return;
openFileDialog({ accept: unref(allowedFileTypes) });
}
function adjustTextAreaHeight() {
const textarea = chatTextArea.value;
if (!textarea) return;
textarea.style.height = "var(--chat--textarea--height)";
const newHeight = Math.min(textarea.scrollHeight, 480);
textarea.style.height = `${newHeight}px`;
}
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
class: "chat-input",
style: normalizeStyle(styleVars.value),
onKeydown: withModifiers(onKeyDown, ["stop"])
}, [createBaseVNode("div", _hoisted_1$2, [
_ctx.$slots.leftPanel ? (openBlock(), createElementBlock("div", _hoisted_2$1, [renderSlot(_ctx.$slots, "leftPanel", {}, void 0, true)])) : createCommentVNode("", true),
withDirectives(createBaseVNode("textarea", {
ref_key: "chatTextArea",
ref: chatTextArea,
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => input.value = $event),
"data-test-id": "chat-input",
disabled: isInputDisabled.value,
placeholder: unref(t)(props.placeholder),
onKeydown: withKeys(onSubmitKeydown, ["enter"]),
onInput: adjustTextAreaHeight,
onMousedown: adjustTextAreaHeight,
onFocus: adjustTextAreaHeight
}, null, 40, _hoisted_3$1), [[vModelText, input.value]]),
createBaseVNode("div", _hoisted_4, [isFileUploadAllowed.value ? (openBlock(), createElementBlock("button", {
key: 0,
disabled: isFileUploadDisabled.value,
class: "chat-input-file-button",
"data-test-id": "chat-attach-file-button",
onClick: onOpenFileDialog
}, [createVNode(unref(paperclip_default), {
height: "24",
width: "24"
})], 8, _hoisted_5)) : createCommentVNode("", true), createBaseVNode("button", {
disabled: isSubmitDisabled.value,
class: "chat-input-send-button",
onClick: onSubmit
}, [createVNode(unref(send_default), {
height: "24",
width: "24"
})], 8, _hoisted_6)])
]), files.value?.length && (!isSubmitting.value || waitingForChatResponse.value) ? (openBlock(), createElementBlock("div", _hoisted_7, [(openBlock(true), createElementBlock(Fragment, null, renderList(files.value, (file) => {
return openBlock(), createBlock(ChatFile_default, {
key: file.name,
file,
"is-removable": true,
"is-previewable": true,
onRemove: onFileRemove
}, null, 8, ["file"]);
}), 128))])) : createCommentVNode("", true)], 36);
};
}
}), [["__scopeId", "data-v-71b23fec"]]);
var MessageTyping_default = /* @__PURE__ */ defineComponent({
__name: "MessageTyping",
props: { animation: { default: "bouncing" } },
setup(__props) {
const props = __props;
const message = {
id: "typing",
text: "",
sender: "bot"
};
const messageContainer = ref();
const classes = computed(() => {
return {
"chat-message-typing": true,
[`chat-message-typing-animation-${props.animation}`]: true
};
});
onMounted(() => {
messageContainer.value?.scrollToView();
});
return (_ctx, _cache) => {
return openBlock(), createBlock(unref(Message_default), {
ref_key: "messageContainer",
ref: messageContainer,
class: normalizeClass(classes.value),
message,
"data-test-id": "chat-message-typing"
}, {
default: withCtx(() => _cache[0] || (_cache[0] = [createBaseVNode("div", { class: "chat-message-typing-body" }, [
createBaseVNode("span", { class: "chat-message-typing-circle" }),
createBaseVNode("span", { class: "chat-message-typing-circle" }),
createBaseVNode("span", { class: "chat-message-typing-circle" })
], -1)])),
_: 1
}, 8, ["class"]);
};
}
});
var _hoisted_1$1 = {
key: 0,
class: "empty-container"
};
var _hoisted_2 = {
class: "empty",
"data-test-id": "chat-messages-empty"
};
var _hoisted_3 = {
key: 1,
class: "chat-messages-list"
};
var MessagesList_default = /* @__PURE__ */ defineComponent({
__name: "MessagesList",
props: {
messages: {},
emptyText: {}
},
setup(__props) {
const chatStore = useChat();
const messageComponents = ref([]);
const { initialMessages, waitingForResponse } = chatStore;
watch(() => messageComponents.value.length, () => {
const lastMessageComponent = messageComponents.value[messageComponents.value.length - 1];
if (lastMessageComponent) lastMessageComponent.scrollToView();
});
return (_ctx, _cache) => {
return _ctx.emptyText && unref(initialMessages).length === 0 && _ctx.messages.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_1$1, [createBaseVNode("div", _hoisted_2, [createVNode(unref(N8nIcon_default), {
icon: "message-circle",
size: "large",
class: "emptyIcon"
}), createVNode(unref(N8nText_default), {
tag: "p",
size: "medium",
color: "text-base"
}, {
default: withCtx(() => [createTextVNode(toDisplayString(_ctx.emptyText), 1)]),
_: 1
})])])) : (openBlock(), createElementBlock("div", _hoisted_3, [
(openBlock(true), createElementBlock(Fragment, null, renderList(unref(initialMessages), (initialMessage) => {
return openBlock(), createBlock(Message_default, {
key: initialMessage.id,
message: initialMessage
}, null, 8, ["message"]);
}), 128)),
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.messages, (message) => {
return openBlock(), createBlock(Message_default, {
key: message.id,
ref_for: true,
ref_key: "messageComponents",
ref: messageComponents,
message
}, {
beforeMessage: withCtx(({ message: message$1 }) => [renderSlot(_ctx.$slots, "beforeMessage", mergeProps({ ref_for: true }, { message: message$1 }))]),
_: 2
}, 1032, ["message"]);
}), 128)),
unref(waitingForResponse) ? (openBlock(), createBlock(MessageTyping_default, { key: 0 })) : createCommentVNode("", true)
]));
};
}
});
var MessageOptionTooltip_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "MessageOptionTooltip",
props: { placement: {
type: String,
default: "top"
} },
setup(__props) {
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.$style.container) }, [createVNode(unref(N8nTooltip_default), { placement: __props.placement }, {
content: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
default: withCtx(() => [createBaseVNode("span", { class: normalizeClass(_ctx.$style.icon) }, [createVNode(unref(N8nIcon_default), {
icon: "info",
size: "xsmall"
})], 2)]),
_: 3
}, 8, ["placement"])], 2);
};
}
});
var MessageOptionTooltip_vue_vue_type_style_index_0_lang_module_default = {
container: "_container_e8qal_123",
icon: "_icon_e8qal_129"
};
var MessageOptionTooltip_default = /* @__PURE__ */ __plugin_vue_export_helper_default(MessageOptionTooltip_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": MessageOptionTooltip_vue_vue_type_style_index_0_lang_module_default }]]);
var MessageOptionAction_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "MessageOptionAction",
props: {
label: {},
icon: {},
placement: {}
},
setup(__props) {
const attrs = useAttrs();
const onClick = () => {
attrs.onClick?.();
};
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.$style.container) }, [createVNode(unref(N8nTooltip_default), { placement: _ctx.placement }, {
content: withCtx(() => [createTextVNode(toDisplayString(_ctx.label), 1)]),
default: withCtx(() => [createVNode(unref(N8nIcon_default), {
class: normalizeClass(_ctx.$style.icon),
icon: _ctx.icon,
size: "xsmall",
onClick
}, null, 8, ["class", "icon"])]),
_: 1
}, 8, ["placement"])], 2);
};
}
});
var MessageOptionAction_vue_vue_type_style_index_0_lang_module_default = {
container: "_container_19q7h_123",
icon: "_icon_19q7h_129"
};
var MessageOptionAction_default = /* @__PURE__ */ __plugin_vue_export_helper_default(MessageOptionAction_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": MessageOptionAction_vue_vue_type_style_index_0_lang_module_default }]]);
var _hoisted_1 = ["onClick"];
var ChatMessagesPanel_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "ChatMessagesPanel",
props: {
pastChatMessages: {},
messages: {},
sessionId: {},
showCloseButton: { type: Boolean },
isOpen: {
type: Boolean,
default: true
},
isReadOnly: {
type: Boolean,
default: false
},
isHeaderClickable: { type: Boolean }
},
emits: [
"displayExecution",
"sendMessage",
"refreshSession",
"close",
"clickHeader"
],
setup(__props, { emit: __emit }) {
const props = __props;
const emit = __emit;
const clipboard = useClipboard();
const locale = useI18n();
const toast = useToast();
const previousMessageIndex = ref(-1);
const currentInputBuffer = ref("");
const sessionIdText = computed(() => locale.baseText("chat.window.session.id", { interpolate: { id: `${props.sessionId.slice(0, 5)}...` } }));
const inputPlaceholder = computed(() => {
if (props.messages.length > 0) return locale.baseText("chat.window.chat.placeholder");
return locale.baseText("chat.window.chat.placeholderPristine");
});
function isTextMessage(message) {
return message.type === "text" || !message.type;
}
function repostMessage(message) {
sendMessage(message.text);
}
function reuseMessage(message) {
chatEventBus.emit("setInputValue", message.text);
}
function sendMessage(message) {
previousMessageIndex.value = -1;
currentInputBuffer.value = "";
emit("sendMessage", message);
}
function onRefreshSession() {
emit("refreshSession");
}
function onArrowKeyDown({ currentInputValue, key }) {
const pastMessages = props.pastChatMessages;
if (pastMessages.length === 0) return;
if (currentInputValue.length === 0 && previousMessageIndex.value !== -1) {
previousMessageIndex.value = -1;
currentInputBuffer.value = "";
}
if (previousMessageIndex.value === -1 && currentInputValue.length > 0) currentInputBuffer.value = currentInputValue;
if (key === "ArrowUp") {
chatEventBus.emit("blurInput");
if (previousMessageIndex.value === -1) previousMessageIndex.value = pastMessages.length - 1;
else if (previousMessageIndex.value > 0) previousMessageIndex.value--;
const selectedMessage = pastMessages[previousMessageIndex.value];
chatEventBus.emit("setInputValue", selectedMessage);
chatEventBus.emit("focusInput");
} else if (key === "ArrowDown") {
if (previousMessageIndex.value === -1) return;
chatEventBus.emit("blurInput");
if (previousMessageIndex.value < pastMessages.length - 1) {
previousMessageIndex.value++;
const selectedMessage = pastMessages[previousMessageIndex.value];
chatEventBus.emit("setInputValue", selectedMessage);
} else {
previousMessageIndex.value = -1;
chatEventBus.emit("setInputValue", currentInputBuffer.value);
currentInputBuffer.value = "";
}
chatEventBus.emit("focusInput");
}
}
function onEscapeKey() {
if (previousMessageIndex.value === -1) return;
previousMessageIndex.value = -1;
chatEventBus.emit("setInputValue", currentInputBuffer.value);
currentInputBuffer.value = "";
}
async function copySessionId() {
await clipboard.copy(props.sessionId);
toast.showMessage({
title: locale.baseText("generic.copiedToClipboard"),
message: "",
type: "success"
});
}
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
class: normalizeClass([_ctx.$style.chat, "ignore-key-press-canvas"]),
"data-test-id": "workflow-lm-chat-dialog",
tabindex: "0"
}, [
createVNode(LogsPanelHeader_default, {
"data-test-id": "chat-header",
title: unref(locale).baseText("chat.window.title"),
"is-clickable": _ctx.isHeaderClickable,
onClick: _cache[0] || (_cache[0] = ($event) => emit("clickHeader"))
}, {
actions: withCtx(() => [unref(clipboard).isSupported && !_ctx.isReadOnly ? (openBlock(), createBlock(unref(N8nTooltip_default), { key: 0 }, {
content: withCtx(() => [
createTextVNode(toDisplayString(_ctx.sessionId) + " ", 1),
_cache[3] || (_cache[3] = createBaseVNode("br", null, null, -1)),
createTextVNode(" " + toDisplayString(unref(locale).baseText("chat.window.session.id.copy")), 1)
]),
default: withCtx(() => [createVNode(unref(N8nButton_default), {
"data-test-id": "chat-session-id",
type: "secondary",
size: "mini",
class: normalizeClass(_ctx.$style.newHeaderButton),
onClick: withModifiers(copySessionId, ["stop"])
}, {
default: withCtx(() => [createTextVNode(toDisplayString(sessionIdText.value), 1)]),
_: 1
}, 8, ["class"])]),
_: 1
})) : createCommentVNode("", true), _ctx.messages.length > 0 && !_ctx.isReadOnly ? (openBlock(), createBlock(unref(N8nTooltip_default), {
key: 1,
content: unref(locale).baseText("chat.window.session.resetSession")
}, {
default: withCtx(() => [createVNode(unref(N8nIconButton_default), {
class: normalizeClass(_ctx.$style.newHeaderButton),
"data-test-id": "refresh-session-button",
outline: "",
type: "secondary",
size: "small",
"icon-size": "medium",
icon: "undo-2",
title: unref(locale).baseText("chat.window.session.reset"),
onClick: withModifiers(onRefreshSession, ["stop"])
}, null, 8, ["class", "title"])]),
_: 1
}, 8, ["content"])) : createCommentVNode("", true)]),
_: 1
}, 8, ["title", "is-clickable"]),
_ctx.isOpen ? (openBlock(), createElementBlock("main", {
key: 0,
class: normalizeClass(_ctx.$style.chatBody),
"data-test-id": "canvas-chat-body"
}, [createVNode(MessagesList_default, {
messages: _ctx.messages,
class: normalizeClass(_ctx.$style.messages),
"empty-text": unref(locale).baseText("chat.window.chat.emptyChatMessage.v2")
}, {
beforeMessage: withCtx(({ message }) => [
!_ctx.isReadOnly && message.sender === "bot" && !message.id.includes("preload") ? (openBlock(), createBlock(MessageOptionTooltip_default, {
key: 0,
placement: "right",
"data-test-id": "execution-id-tooltip"
}, {
default: withCtx(() => [createTextVNode(toDisplayString(unref(locale).baseText("chat.window.chat.chatMessageOptions.executionId")) + ": ", 1), createBaseVNode("a", {
href: "#",
onClick: ($event) => emit("displayExecution", message.id)
}, toDisplayString(message.id), 9, _hoisted_1)]),
_: 2
}, 1024)) : createCommentVNode("", true),
!_ctx.isReadOnly && isTextMessage(message) && message.sender === "user" ? (openBlock(), createBlock(MessageOptionAction_default, {
key: 1,
"data-test-id": "repost-message-button",
icon: "redo-2",
label: unref(locale).baseText("chat.window.chat.chatMessageOptions.repostMessage"),
placement: "left",
onClickOnce: ($event) => repostMessage(message)
}, null, 8, ["label", "onClickOnce"])) : createCommentVNode("", true),
!_ctx.isReadOnly && isTextMessage(message) && message.sender === "user" ? (openBlock(), createBlock(MessageOptionAction_default, {
key: 2,
"data-test-id": "reuse-message-button",
icon: "files",
label: unref(locale).baseText("chat.window.chat.chatMessageOptions.reuseMessage"),
placement: "left",
onClick: ($event) => reuseMessage(message)
}, null, 8, ["label", "onClick"])) : createCommentVNode("", true)
]),
_: 1
}, 8, [
"messages",
"class",
"empty-text"
])], 2)) : createCommentVNode("", true),
_ctx.isOpen ? (openBlock(), createElementBlock("div", {
key: 1,
class: normalizeClass(_ctx.$style.messagesInput)
}, [createVNode(Input_default, {
"data-test-id": "lm-chat-inputs",
placeholder: inputPlaceholder.value,
onArrowKeyDown,
onEscapeKeyDown: onEscapeKey
}, createSlots({ _: 2 }, [_ctx.pastChatMessages.length > 0 ? {
name: "leftPanel",
fn: withCtx(() => [createBaseVNode("div", { class: normalizeClass(_ctx.$style.messagesHistory) }, [createVNode(unref(N8nButton_default), {
title: "Navigate to previous message",
icon: "chevron-up",
type: "tertiary",
text: "",
size: "mini",
disabled: previousMessageIndex.value === 0,
onClick: _cache[1] || (_cache[1] = ($event) => onArrowKeyDown({
currentInputValue: "",
key: "ArrowUp"
}))
}, null, 8, ["disabled"]), createVNode(unref(N8nButton_default), {
title: "Navigate to next message",
icon: "chevron-down",
type: "tertiary",
text: "",
size: "mini",
disabled: previousMessageIndex.value === -1,
onClick: _cache[2] || (_cache[2] = ($event) => onArrowKeyDown({
currentInputValue: "",
key: "ArrowDown"
}))
}, null, 8, ["disabled"])], 2)]),
key: "0"
} : void 0]), 1032, ["placeholder"])], 2)) : createCommentVNode("", true)
], 2);
};
}
});
var ChatMessagesPanel_vue_vue_type_style_index_0_lang_module_default = {
chat: "_chat_1lcb4_123",
chatHeader: "_chatHeader_1lcb4_151",
chatTitle: "_chatTitle_1lcb4_164",
session: "_session_1lcb4_168",
sessionId: "_sessionId_1lcb4_176",
copyable: "_copyable_1lcb4_182",
headerButton: "_headerButton_1lcb4_186",
newHeaderButton: "_newHeaderButton_1lcb4_191",
chatBody: "_chatBody_1lcb4_196",
messages: "_messages_1lcb4_205",
messagesInput: "_messagesInput_1lcb4_216",
messagesHistory: "_messagesHistory_1lcb4_250"
};
var ChatMessagesPanel_default = /* @__PURE__ */ __plugin_vue_export_helper_default(ChatMessagesPanel_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": ChatMessagesPanel_vue_vue_type_style_index_0_lang_module_default }]]);
var LogsViewRunData_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "LogsViewRunData",
props: {
title: {},
paneType: {},
logEntry: {},
collapsingTableColumnName: {},
searchShortcut: {}
},
emits: ["collapsingTableColumnChanged"],
setup(__props, { emit: __emit }) {
const emit = __emit;
const locale = useI18n();
const ndvStore = useNDVStore();
const popOutWindow = inject(PopOutWindowKey, ref());
const displayMode = ref(__props.paneType === "input" ? "schema" : "table");
const isMultipleInput = computed(() => __props.paneType === "input" && (__props.logEntry.runData?.source.length ?? 0) > 1);
const runDataProps = computed(() => {
if (isSubNodeLog(__props.logEntry) || __props.paneType === "output") return {
node: __props.logEntry.node,
runIndex: __props.logEntry.runIndex
};
const source = __props.logEntry.runData?.source[0];
const node = source && __props.logEntry.workflow.getNode(source.previousNode);
if (!source || !node) return;
return {
node: {
...node,
disabled: false
},
runIndex: source.previousNodeRun ?? 0,
overrideOutputs: [source.previousNodeOutput ?? 0]
};
});
const isExecuting = computed(() => __props.paneType === "output" && (__props.logEntry.runData?.executionStatus === "running" || __props.logEntry.runData?.executionStatus === "waiting"));
function handleClickOpenNdv() {
ndvStore.setActiveNodeName(__props.logEntry.node.name, "logs_view");
}
function handleChangeDisplayMode(value) {
displayMode.value = value;
}
return (_ctx, _cache) => {
const _directive_n8n_html = resolveDirective("n8n-html");
return runDataProps.value ? (openBlock(), createBlock(RunData_default, mergeProps({ key: 0 }, runDataProps.value, {
key: `run-data${unref(popOutWindow) ? "-pop-out" : ""}`,
class: _ctx.$style.component,
"workflow-object": _ctx.logEntry.workflow,
"workflow-execution": _ctx.logEntry.execution,
"no-data-in-branch-message": unref(locale).baseText("ndv.output.noOutputDataInBranch"),
"executing-message": unref(locale).baseText("ndv.output.executing"),
"pane-type": _ctx.paneType,
"disable-run-index-selection": true,
compact: true,
"show-actions-on-hover": true,
"disable-pin": true,
"disable-edit": true,
"disable-hover-highlight": true,
"disable-settings-hint": true,
"display-mode": displayMode.value,
"disable-ai-content": !unref(isSubNodeLog)(_ctx.logEntry),
"is-executing": isExecuting.value,
"table-header-bg-color": "light",
"collapsing-table-column-name": _ctx.collapsingTableColumnName,
"search-shortcut": _ctx.searchShortcut,
onDisplayModeChange: handleChangeDisplayMode,
onCollapsingTableColumnChanged: _cache[0] || (_cache[0] = ($event) => emit("collapsingTableColumnChanged", $event))
}), createSlots({
header: withCtx(() => [createVNode(unref(N8nText_default), {
class: normalizeClass(_ctx.$style.title),
bold: true,
color: "text-light",
size: "small"
}, {
default: withCtx(() => [createTextVNode(toDisplayString(_ctx.title), 1)]),
_: 1
}, 8, ["class"])]),
"header-end": withCtx((itemCountProps) => [createVNode(RunDataItemCount_default, mergeProps(itemCountProps, { search: displayMode.value === "schema" ? "" : itemCountProps.search }), null, 16, ["search"])]),
"no-output-data": withCtx(() => [createVNode(NDVEmptyState_default, { title: unref(locale).baseText("ndv.output.noOutputData.title") }, null, 8, ["title"])]),
"node-waiting": withCtx(() => [createVNode(NDVEmptyState_default, {
title: unref(locale).baseText("ndv.output.waitNodeWaiting.title"),
wide: ""
}, {
default: withCtx(() => [withDirectives(createBaseVNode("span", null, null, 512), [[_directive_n8n_html, unref(waitingNodeTooltip)(_ctx.logEntry.node, _ctx.logEntry.workflow)]])]),
_: 1
}, 8, ["title"])]),
_: 2
}, [isMultipleInput.value ? {
name: "content",
fn: withCtx(() => []),
key: "0"
} : void 0, isMultipleInput.value ? {
name: "callout-message",
fn: withCtx(() => [createVNode(unref(I18nT), {
keypath: "logs.details.body.multipleInputs",
scope: "global"
}, {
button: withCtx(() => [createVNode(unref(N8nLink_default), {
size: "small",
onClick: handleClickOpenNdv
}, {
default: withCtx(() => [createTextVNode(toDisplayString(unref(locale).baseText("logs.details.body.multipleInputs.openingTheNode")), 1)]),
_: 1
})]),
_: 1
})]),
key: "1"
} : void 0]), 1040, [
"class",
"workflow-object",
"workflow-execution",
"no-data-in-branch-message",
"executing-message",
"pane-type",
"display-mode",
"disable-ai-content",
"is-executing",
"collapsing-table-column-name",
"search-shortcut"
])) : createCommentVNode("", true);
};
}
});
var LogsViewRunData_vue_vue_type_style_index_0_lang_module_default = {
component: "_component_1mnpf_123",
title: "_title_1mnpf_127"
};
var LogsViewRunData_default = /* @__PURE__ */ __plugin_vue_export_helper_default(LogsViewRunData_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": LogsViewRunData_vue_vue_type_style_index_0_lang_module_default }]]);
function useResizablePanel(localStorageKey, { container: container$8, defaultSize, snap = true, minSize = 0, maxSize = (size) => size, position = "left", allowCollapse, allowFullSize }) {
const containerSize = ref(0);
const persistedSize = useLocalStorage(localStorageKey, -1, { writeDefaults: false });
const isResizing = ref(false);
const sizeOnResizeStart = ref();
const minSizeValue = computed(() => resolveSize(minSize, containerSize.value));
const maxSizeValue = computed(() => resolveSize(maxSize, containerSize.value));
const constrainedSize = computed(() => {
const sizeInPixels = persistedSize.value >= 0 && persistedSize.value <= 1 ? containerSize.value * persistedSize.value : -1;
if (isResizing.value && allowCollapse && sizeInPixels < 30) return 0;
if (isResizing.value && allowFullSize && sizeInPixels > containerSize.value - 30) return containerSize.value;
const defaultSizeValue = resolveSize(defaultSize, containerSize.value);
if (Number.isNaN(sizeInPixels) || !Number.isFinite(sizeInPixels) || sizeInPixels < 0) return defaultSizeValue;
return Math.max(minSizeValue.value, Math.min(snap && Math.abs(defaultSizeValue - sizeInPixels) < 30 ? defaultSizeValue : sizeInPixels, maxSizeValue.value));
});
function getSize(el) {
return position === "bottom" ? el.height : el.width;
}
function getOffsetSize(el) {
return position === "bottom" ? el.offsetHeight : el.offsetWidth;
}
function getValue(data) {
return position === "bottom" ? data.y : data.x;
}
function resolveSize(getter, containerSizeValue) {
return typeof getter === "number" ? getter : getter(containerSizeValue);
}
function onResize(data) {
const containerRect = unref(container$8)?.getBoundingClientRect();
const newSizeInPixels = Math.max(0, position === "bottom" ? (containerRect ? getSize(containerRect) : 0) - getValue(data) : getValue(data) - (containerRect ? getValue(containerRect) : 0));
isResizing.value = true;
persistedSize.value = newSizeInPixels / containerSize.value;
if (sizeOnResizeStart.value === void 0) sizeOnResizeStart.value = persistedSize.value;
}
function onResizeEnd() {
if (minSizeValue.value > 0 && constrainedSize.value <= 0 || maxSizeValue.value < containerSize.value && constrainedSize.value >= containerSize.value) persistedSize.value = sizeOnResizeStart.value;
sizeOnResizeStart.value = void 0;
isResizing.value = false;
}
watch(() => unref(container$8), (el, _, onCleanUp) => {
if (!el) return;
const observer = new ResizeObserver(() => {
containerSize.value = getOffsetSize(el);
});
observer.observe(el);
containerSize.value = getOffsetSize(el);
onCleanUp(() => observer.disconnect());
}, { immediate: true });
return {
isResizing: computed(() => isResizing.value),
isCollapsed: computed(() => isResizing.value && constrainedSize.value <= 0),
isFullSize: computed(() => isResizing.value && constrainedSize.value >= containerSize.value),
size: constrainedSize,
onResize,
onResizeEnd
};
}
var MIN_IO_PANEL_WIDTH = 200;
var LogDetailsPanel_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "LogDetailsPanel",
props: {
isOpen: { type: Boolean },
logEntry: {},
window: {},
latestInfo: {},
panels: {},
collapsingInputTableColumnName: {},
collapsingOutputTableColumnName: {},
isHeaderClickable: { type: Boolean }
},
emits: [
"clickHeader",
"toggleInputOpen",
"toggleOutputOpen",
"collapsingInputTableColumnChanged",
"collapsingOutputTableColumnChanged"
],
setup(__props, { emit: __emit }) {
const emit = __emit;
const locale = useI18n();
const nodeTypeStore = useNodeTypesStore();
const ndvStore = useNDVStore();
const experimentalNdvStore = useExperimentalNdvStore();
const type = computed(() => nodeTypeStore.getNodeType(__props.logEntry.node.type));
const consumedTokens = computed(() => getSubtreeTotalConsumedTokens(__props.logEntry, false));
const isTriggerNode = computed(() => type.value?.group.includes("trigger"));
const container$8 = useTemplateRef("container");
const resizer = useResizablePanel("N8N_LOGS_INPUT_PANEL_WIDTH", {
container: container$8,
defaultSize: (size) => size / 2,
minSize: MIN_IO_PANEL_WIDTH,
maxSize: (size) => size - MIN_IO_PANEL_WIDTH,
allowCollapse: true,
allowFullSize: true
});
const shouldResize = computed(() => __props.panels === LOG_DETAILS_PANEL_STATE.BOTH);
const searchShortcutPriorityPanel = computed(() => ndvStore.isNDVOpen || experimentalNdvStore.isMapperOpen ? void 0 : __props.panels === LOG_DETAILS_PANEL_STATE.INPUT ? "input" : "output");
function handleResizeEnd() {
if (resizer.isCollapsed.value) emit("toggleInputOpen", false);
if (resizer.isFullSize.value) emit("toggleOutputOpen", false);
resizer.onResizeEnd();
}
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
ref_key: "container",
ref: container$8,
class: normalizeClass(_ctx.$style.container),
"data-test-id": "log-details"
}, [createVNode(LogsPanelHeader_default, {
"data-test-id": "log-details-header",
class: normalizeClass(_ctx.$style.header),
"is-clickable": _ctx.isHeaderClickable,
onClick: _cache[2] || (_cache[2] = ($event) => emit("clickHeader"))
}, {
title: withCtx(() => [createBaseVNode("div", { class: normalizeClass(_ctx.$style.title) }, [
createVNode(NodeIcon_default, {
"node-type": type.value,
size: 16,
class: normalizeClass(_ctx.$style.icon)
}, null, 8, ["node-type", "class"]),
createVNode(LogsViewNodeName_default, {
name: _ctx.latestInfo?.name ?? _ctx.logEntry.node.name,
"is-deleted": _ctx.latestInfo?.deleted ?? false
}, null, 8, ["name", "is-deleted"]),
_ctx.isOpen && _ctx.logEntry.runData !== void 0 ? (openBlock(), createBlock(LogsViewExecutionSummary_default, {
key: 0,
class: normalizeClass(_ctx.$style.executionSummary),
status: _ctx.logEntry.runData.executionStatus ?? "unknown",
"consumed-tokens": consumedTokens.value,
"start-time": _ctx.logEntry.runData.startTime,
"time-took": _ctx.logEntry.runData.executionTime
}, null, 8, [
"class",
"status",
"consumed-tokens",
"start-time",
"time-took"
])) : createCommentVNode("", true)
], 2)]),
actions: withCtx(() => [_ctx.isOpen && !isTriggerNode.value && !unref(isPlaceholderLog)(_ctx.logEntry) ? (openBlock(), createElementBlock("div", {
key: 0,
class: normalizeClass(_ctx.$style.actions)
}, [createVNode(KeyboardShortcutTooltip_default, {
label: unref(locale).baseText("generic.shortcutHint"),
shortcut: { keys: ["i"] }
}, {
default: withCtx(() => [createVNode(unref(N8nButton_default), {
size: "mini",
type: "secondary",
class: normalizeClass(_ctx.panels === unref(LOG_DETAILS_PANEL_STATE).OUTPUT ? "" : _ctx.$style.pressed),
onClick: _cache[0] || (_cache[0] = withModifiers(($event) => emit("toggleInputOpen"), ["stop"]))
}, {
default: withCtx(() => [createTextVNode(toDisplayString(unref(locale).baseText("logs.details.header.actions.input")), 1)]),
_: 1
}, 8, ["class"])]),
_: 1
}, 8, ["label"]), createVNode(KeyboardShortcutTooltip_default, {
label: unref(locale).baseText("generic.shortcutHint"),
shortcut: { keys: ["o"] }
}, {
default: withCtx(() => [createVNode(unref(N8nButton_default), {
size: "mini",
type: "secondary",
class: normalizeClass(_ctx.panels === unref(LOG_DETAILS_PANEL_STATE).INPUT ? "" : _ctx.$style.pressed),
onClick: _cache[1] || (_cache[1] = withModifiers(($event) => emit("toggleOutputOpen"), ["stop"]))
}, {
default: withCtx(() => [createTextVNode(toDisplayString(unref(locale).baseText("logs.details.header.actions.output")), 1)]),
_: 1
}, 8, ["class"])]),
_: 1
}, 8, ["label"])], 2)) : createCommentVNode("", true), renderSlot(_ctx.$slots, "actions")]),
_: 3
}, 8, ["class", "is-clickable"]), _ctx.isOpen ? (openBlock(), createElementBlock("div", {
key: 0,
class: normalizeClass(_ctx.$style.content),
"data-test-id": "logs-details-body"
}, [unref(isPlaceholderLog)(_ctx.logEntry) ? (openBlock(), createElementBlock("div", {
key: 0,
class: normalizeClass(_ctx.$style.placeholder)
}, [createVNode(unref(N8nText_default), { color: "text-base" }, {
default: withCtx(() => [createTextVNode(toDisplayString(unref(locale).baseText("ndv.output.runNodeHint")), 1)]),
_: 1
})], 2)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [!isTriggerNode.value && _ctx.panels !== unref(LOG_DETAILS_PANEL_STATE).OUTPUT ? (openBlock(), createBlock(unref(N8nResizeWrapper_default), {
key: 0,
class: normalizeClass({
[_ctx.$style.inputResizer]: true,
[_ctx.$style.collapsed]: unref(resizer).isCollapsed.value,
[_ctx.$style.full]: unref(resizer).isFullSize.value
}),
width: unref(resizer).size.value,
style: normalizeStyle(shouldResize.value ? { width: `${unref(resizer).size.value ?? 0}px` } : void 0),
"supported-directions": ["right"],
"is-resizing-enabled": shouldResize.value,
window: _ctx.window,
onResize: unref(resizer).onResize,
onResizeend: handleResizeEnd
}, {
default: withCtx(() => [createVNode(LogsViewRunData_default, {
"data-test-id": "log-details-input",
"pane-type": "input",
title: unref(locale).baseText("logs.details.header.actions.input"),
"log-entry": _ctx.logEntry,
"collapsing-table-column-name": _ctx.collapsingInputTableColumnName,
"search-shortcut": searchShortcutPriorityPanel.value === "input" ? "ctrl+f" : void 0,
onCollapsingTableColumnChanged: _cache[3] || (_cache[3] = ($event) => emit("collapsingInputTableColumnChanged", $event))
}, null, 8, [
"title",
"log-entry",
"collapsing-table-column-name",
"search-shortcut"
])]),
_: 1
}, 8, [
"class",
"width",
"style",
"is-resizing-enabled",
"window",
"onResize"
])) : createCommentVNode("", true), isTriggerNode.value || _ctx.panels !== unref(LOG_DETAILS_PANEL_STATE).INPUT ? (openBlock(), createBlock(LogsViewRunData_default, {
key: 1,
"data-test-id": "log-details-output",
"pane-type": "output",
class: normalizeClass(_ctx.$style.outputPanel),
title: unref(locale).baseText("logs.details.header.actions.output"),
"log-entry": _ctx.logEntry,
"collapsing-table-column-name": _ctx.collapsingOutputTableColumnName,
"search-shortcut": searchShortcutPriorityPanel.value === "output" ? "ctrl+f" : void 0,
onCollapsingTableColumnChanged: _cache[4] || (_cache[4] = ($event) => emit("collapsingOutputTableColumnChanged", $event))
}, null, 8, [
"class",
"title",
"log-entry",
"collapsing-table-column-name",
"search-shortcut"
])) : createCommentVNode("", true)], 64))], 2)) : createCommentVNode("", true)], 2);
};
}
});
var LogDetailsPanel_vue_vue_type_style_index_0_lang_module_default = {
container: "_container_w6lwl_123",
header: "_header_w6lwl_132",
actions: "_actions_w6lwl_136",
pressed: "_pressed_w6lwl_142",
title: "_title_w6lwl_146",
icon: "_icon_w6lwl_152",
executionSummary: "_executionSummary_w6lwl_156",
content: "_content_w6lwl_160",
outputPanel: "_outputPanel_w6lwl_168",
inputResizer: "_inputResizer_w6lwl_173",
collapsed: "_collapsed_w6lwl_177",
full: "_full_w6lwl_177",
placeholder: "_placeholder_w6lwl_181"
};
var LogDetailsPanel_default = /* @__PURE__ */ __plugin_vue_export_helper_default(LogDetailsPanel_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": LogDetailsPanel_vue_vue_type_style_index_0_lang_module_default }]]);
var LogsPanelActions_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "LogsPanelActions",
props: {
isOpen: { type: Boolean },
isSyncSelectionEnabled: { type: Boolean },
showToggleButton: { type: Boolean },
showPopOutButton: { type: Boolean }
},
emits: [
"popOut",
"toggleOpen",
"toggleSyncSelection"
],
setup(__props, { emit: __emit }) {
const emit = __emit;
const locales = useI18n();
const popOutButtonText = computed(() => locales.baseText("runData.panel.actions.popOut"));
const toggleButtonText = computed(() => locales.baseText(__props.isOpen ? "runData.panel.actions.collapse" : "runData.panel.actions.open"));
const menuItems = computed(() => [{
id: "toggleSyncSelection",
label: locales.baseText("runData.panel.actions.sync"),
checked: __props.isSyncSelectionEnabled
}, ...__props.showPopOutButton ? [{
id: "popOut",
label: popOutButtonText.value
}] : []]);
function handleSelectMenuItem(selected) {
switch (selected) {
case "popOut":
emit(selected);
return;
case "toggleSyncSelection":
emit(selected);
return;
}
}
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.$style.container) }, [
!_ctx.isOpen && _ctx.showPopOutButton ? (openBlock(), createBlock(unref(N8nTooltip_default), {
key: 0,
content: popOutButtonText.value
}, {
default: withCtx(() => [createVNode(unref(N8nIconButton_default), {
icon: "pop-out",
type: "tertiary",
text: "",
size: "small",
"icon-size": "medium",
"aria-label": popOutButtonText.value,
onClick: _cache[0] || (_cache[0] = withModifiers(($event) => emit("popOut"), ["stop"]))
}, null, 8, ["aria-label"])]),
_: 1
}, 8, ["content"])) : createCommentVNode("", true),
_ctx.isOpen ? (openBlock(), createBlock(unref(N8nActionDropdown_default), {
key: 1,
"icon-size": "small",
"activator-icon": "ellipsis",
"activator-size": "small",
items: menuItems.value,
teleported: false,
onSelect: handleSelectMenuItem,
onClick: _cache[1] || (_cache[1] = withModifiers(() => {}, ["stop"]))
}, null, 8, ["items"])) : createCommentVNode("", true),
_ctx.showToggleButton ? (openBlock(), createBlock(KeyboardShortcutTooltip_default, {
key: `tooltip-${_ctx.isOpen}`,
label: unref(locales).baseText("generic.shortcutHint"),
shortcut: { keys: ["l"] }
}, {
default: withCtx(() => [createVNode(unref(N8nIconButton_default), {
type: "tertiary",
text: "",
size: "small",
"icon-size": "medium",
icon: _ctx.isOpen ? "chevron-down" : "chevron-up",
"aria-label": toggleButtonText.value,
onClick: _cache[2] || (_cache[2] = withModifiers(($event) => emit("toggleOpen"), ["stop"]))
}, null, 8, ["icon", "aria-label"])]),
_: 1
}, 8, ["label"])) : createCommentVNode("", true)
], 2);
};
}
});
var LogsPanelActions_vue_vue_type_style_index_0_lang_module_default = { container: "_container_yw9ej_123" };
var LogsPanelActions_default = /* @__PURE__ */ __plugin_vue_export_helper_default(LogsPanelActions_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": LogsPanelActions_vue_vue_type_style_index_0_lang_module_default }]]);
function useLogsSelection(execution, tree, flatLogEntries, toggleExpand) {
const telemetry = useTelemetry();
const manualLogEntrySelection = shallowRef({ type: "initial" });
const nodeIdToSelect = shallowRef();
const isExecutionStopped = computed(() => execution.value?.stoppedAt !== void 0);
const selected = computed(() => findSelectedLogEntry(manualLogEntrySelection.value, tree.value, !isExecutionStopped.value));
const logsStore = useLogsStore();
const uiStore = useUIStore();
const canvasStore = useCanvasStore();
const workflowsStore = useWorkflowsStore();
function syncSelectionToCanvasIfEnabled(value) {
if (!logsStore.isLogSelectionSyncedWithCanvas) return;
canvasEventBus.emit("nodes:select", {
ids: [value.node.id],
panIntoView: true
});
}
function select(value) {
manualLogEntrySelection.value = value === void 0 ? { type: "none" } : {
type: "selected",
entry: value
};
if (value) {
syncSelectionToCanvasIfEnabled(value);
telemetry.track("User selected node in log view", {
node_type: value.node.type,
node_id: value.node.id,
execution_id: execution.value?.id,
workflow_id: execution.value?.workflowData.id,
subworkflow_depth: getDepth(value)
});
}
}
function selectPrev() {
const entries = flatLogEntries.value;
if (entries.length === 0) return;
const prevEntry = selected.value ? getEntryAtRelativeIndex(entries, selected.value.id, -1) ?? entries[0] : entries[entries.length - 1];
manualLogEntrySelection.value = {
type: "selected",
entry: prevEntry
};
syncSelectionToCanvasIfEnabled(prevEntry);
}
function selectNext() {
const entries = flatLogEntries.value;
if (entries.length === 0) return;
const nextEntry = selected.value ? getEntryAtRelativeIndex(entries, selected.value.id, 1) ?? entries[entries.length - 1] : entries[0];
manualLogEntrySelection.value = {
type: "selected",
entry: nextEntry
};
syncSelectionToCanvasIfEnabled(nextEntry);
}
watch(selected, (sel) => {
if (sel) logsStore.setSubNodeSelected(isSubNodeLog(sel));
}, { immediate: true });
watch([() => uiStore.lastSelectedNode, () => logsStore.isLogSelectionSyncedWithCanvas], ([selectedOnCanvas, shouldSync]) => {
const selectedNodeId = selectedOnCanvas ? workflowsStore.nodesByName[selectedOnCanvas]?.id : void 0;
nodeIdToSelect.value = shouldSync && !canvasStore.hasRangeSelection && selected.value?.node.id !== selectedNodeId ? selectedNodeId : void 0;
}, { immediate: true });
watch([tree, nodeIdToSelect], ([latestTree, id]) => {
if (id === void 0) return;
const entry = findLogEntryRec((e) => e.node.id === id, latestTree);
if (!entry) return;
nodeIdToSelect.value = void 0;
manualLogEntrySelection.value = {
type: "selected",
entry
};
let parent = entry.parent;
while (parent !== void 0) {
toggleExpand(parent, true);
parent = parent.parent;
}
}, { immediate: true });
return {
selected,
select,
selectPrev,
selectNext
};
}
function isStyle(node) {
return node instanceof HTMLStyleElement || node instanceof HTMLLinkElement && node.rel === "stylesheet";
}
function syncStyleMutations(destination, mutations) {
const currentStyles = destination.document.head.querySelectorAll("style, link[rel=\"stylesheet\"]");
for (const mutation of mutations) {
for (const node of mutation.addedNodes) if (isStyle(node)) destination.document.head.appendChild(node.cloneNode(true));
for (const node of mutation.removedNodes) if (isStyle(node)) {
for (const found of currentStyles) if (found.isEqualNode(node)) found.remove();
}
}
}
function copyFavicon(source, target) {
const iconUrl = source.document.querySelector("link[rel=icon]")?.getAttribute("href");
if (iconUrl) {
const link = target.document.createElement("link");
link.setAttribute("rel", "icon");
link.setAttribute("href", iconUrl);
target.document.head.appendChild(link);
}
}
function usePopOutWindow({ title: title$3, container: container$8, content: content$2, initialHeight, initialWidth, shouldPopOut, onRequestClose }) {
const popOutWindow = ref();
const isUnmounting = ref(false);
const canPopOut = computed(() => window.parent === window);
const isPoppedOut = computed(() => !!popOutWindow.value);
const tooltipContainer = computed(() => isPoppedOut.value ? content$2.value ?? void 0 : void 0);
const observer = new MutationObserver((mutations) => {
if (popOutWindow.value) syncStyleMutations(popOutWindow.value, mutations);
});
const documentTitle = useDocumentTitle(popOutWindow);
observer.observe(document.head, {
childList: true,
subtree: true
});
provide(PopOutWindowKey, popOutWindow);
useProvideTooltipAppendTo(tooltipContainer);
async function showPopOut() {
if (!content$2.value) return;
if (!popOutWindow.value) {
const options = `popup=yes,width=${initialWidth},height=${initialHeight},left=100,top=100,toolbar=no,menubar=no,scrollbars=yes,resizable=yes`;
popOutWindow.value = window.open("", "_blank", options) ?? void 0;
}
if (!popOutWindow.value) return;
copyFavicon(window, popOutWindow.value);
for (const styleSheet of [...document.styleSheets]) try {
const cssRules = [...styleSheet.cssRules].map((rule) => rule.cssText).join("");
const style = document.createElement("style");
style.textContent = cssRules;
popOutWindow.value.document.head.appendChild(style);
} catch (e) {
const link = document.createElement("link");
link.rel = "stylesheet";
link.type = styleSheet.type;
link.media = styleSheet.media;
link.href = styleSheet.href;
popOutWindow.value.document.head.appendChild(link);
}
popOutWindow.value.document.body.append(content$2.value);
popOutWindow.value.addEventListener("pagehide", () => !isUnmounting.value && onRequestClose());
}
function hidePopOut() {
popOutWindow.value?.close();
popOutWindow.value = void 0;
if (content$2.value) container$8.value?.appendChild(content$2.value);
}
watch(shouldPopOut, (value) => value ? requestAnimationFrame(showPopOut) : hidePopOut(), { immediate: true });
watch([title$3, popOutWindow], ([newTitle, win]) => {
if (win) documentTitle.set(newTitle);
}, { immediate: true });
onScopeDispose(() => {
observer.disconnect();
});
onBeforeUnmount(() => {
isUnmounting.value = true;
if (popOutWindow.value) {
popOutWindow.value.close();
onRequestClose();
}
});
return {
canPopOut,
isPoppedOut,
popOutWindow
};
}
var INITIAL_POPUP_HEIGHT = 400;
var COLLAPSED_PANEL_HEIGHT = 32;
function useLogsPanelLayout(workflowName, popOutContainer, popOutContent$1, container$8, logsContainer$1) {
const logsStore = useLogsStore();
const telemetry = useTelemetry();
const resizer = useResizablePanel(LOCAL_STORAGE_PANEL_HEIGHT, {
container: document.body,
position: "bottom",
snap: false,
defaultSize: (size) => size * .3,
minSize: 160,
maxSize: (size) => size * .75,
allowCollapse: true
});
const chatPanelResizer = useResizablePanel(LOCAL_STORAGE_PANEL_WIDTH, {
container: container$8,
defaultSize: (size) => Math.min(800, size * .3),
minSize: 240,
maxSize: (size) => size * .8
});
const overviewPanelResizer = useResizablePanel(LOCAL_STORAGE_OVERVIEW_PANEL_WIDTH, {
container: logsContainer$1,
defaultSize: (size) => Math.min(240, size * .2),
minSize: 80,
maxSize: 500,
allowFullSize: true
});
const isOpen = computed(() => logsStore.isOpen ? !resizer.isCollapsed.value : resizer.isResizing.value && resizer.size.value > 0);
const isCollapsingDetailsPanel = computed(() => overviewPanelResizer.isFullSize.value);
const popOutWindowTitle = computed(() => `Logs - ${workflowName.value}`);
const shouldPopOut = computed(() => logsStore.state === LOGS_PANEL_STATE.FLOATING);
const { canPopOut, isPoppedOut, popOutWindow } = usePopOutWindow({
title: popOutWindowTitle,
initialHeight: INITIAL_POPUP_HEIGHT,
initialWidth: window.document.body.offsetWidth * .8,
container: popOutContainer,
content: popOutContent$1,
shouldPopOut,
onRequestClose: () => {
if (!isOpen.value) return;
telemetry.track("User toggled log view", { new_state: "attached" });
logsStore.setPreferPoppedOut(false);
}
});
function handleToggleOpen(open) {
const wasOpen = logsStore.isOpen;
if (open === wasOpen) return;
logsStore.toggleOpen(open);
telemetry.track("User toggled log view", { new_state: wasOpen ? "collapsed" : "attached" });
}
function handlePopOut() {
telemetry.track("User toggled log view", { new_state: "floating" });
logsStore.toggleOpen(true);
logsStore.setPreferPoppedOut(true);
}
function handleResizeEnd() {
if (!logsStore.isOpen && !resizer.isCollapsed.value) handleToggleOpen(true);
if (resizer.isCollapsed.value) handleToggleOpen(false);
resizer.onResizeEnd();
}
watch([
() => logsStore.state,
resizer.size,
isPoppedOut
], ([state, height]) => {
const updatedHeight = state === LOGS_PANEL_STATE.FLOATING ? 0 : state === LOGS_PANEL_STATE.ATTACHED ? height : COLLAPSED_PANEL_HEIGHT;
if (state === LOGS_PANEL_STATE.FLOATING) popOutWindow?.value?.document.documentElement.style.setProperty("--logs-panel--height", "100vh");
else document.documentElement.style.setProperty("--logs-panel--height", `${updatedHeight}px`);
logsStore.setHeight(updatedHeight);
}, { immediate: true });
onBeforeUnmount(() => logsStore.setHeight(0));
return {
height: resizer.size,
chatPanelWidth: chatPanelResizer.size,
overviewPanelWidth: overviewPanelResizer.size,
canPopOut,
isOpen,
isCollapsingDetailsPanel,
isPoppedOut,
isOverviewPanelFullWidth: overviewPanelResizer.isFullSize,
popOutWindow,
onToggleOpen: handleToggleOpen,
onPopOut: handlePopOut,
onResize: resizer.onResize,
onResizeEnd: handleResizeEnd,
onChatPanelResize: chatPanelResizer.onResize,
onChatPanelResizeEnd: chatPanelResizer.onResizeEnd,
onOverviewPanelResize: overviewPanelResizer.onResize,
onOverviewPanelResizeEnd: overviewPanelResizer.onResizeEnd
};
}
var LogsViewKeyboardEventListener_default = /* @__PURE__ */ defineComponent({
__name: "LogsViewKeyboardEventListener",
props: {
keyMap: {},
container: {}
},
setup(__props) {
const popOutWindow = inject(PopOutWindowKey, ref());
const activeElement = useActiveElement({ window: popOutWindow?.value });
const isBlurred = computed(() => {
if (popOutWindow?.value) return popOutWindow.value.document.activeElement === null;
return !activeElement.value || !__props.container || !__props.container.contains(activeElement.value) && __props.container !== activeElement.value;
});
useKeybindings(toRef(() => __props.keyMap), { disabled: isBlurred });
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div");
};
}
});
var LogsPanel_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "LogsPanel",
props: { isReadOnly: {
type: Boolean,
default: false
} },
setup(__props) {
const props = __props;
const container$8 = useTemplateRef("container");
const logsContainer$1 = useTemplateRef("logsContainer");
const popOutContainer = useTemplateRef("popOutContainer");
const popOutContent$1 = useTemplateRef("popOutContent");
const logsStore = useLogsStore();
const ndvStore = useNDVStore();
const workflowsStore = useWorkflowsStore();
const { height, chatPanelWidth, overviewPanelWidth, canPopOut, isOpen, isPoppedOut, isCollapsingDetailsPanel, isOverviewPanelFullWidth, popOutWindow, onResize, onResizeEnd, onToggleOpen, onPopOut, onChatPanelResize, onChatPanelResizeEnd, onOverviewPanelResize, onOverviewPanelResizeEnd } = useLogsPanelLayout(computed(() => workflowsStore.workflow.name), popOutContainer, popOutContent$1, container$8, logsContainer$1);
const { currentSessionId, messages: messages$1, previousChatMessages, sendMessage, refreshSession, displayExecution } = useChatState(props.isReadOnly);
const { entries, execution, hasChat, latestNodeNameById, resetExecutionData, loadSubExecution } = useLogsExecutionData({ isEnabled: isOpen });
const { flatLogEntries, toggleExpanded } = useLogsTreeExpand(entries, loadSubExecution);
const { selected, select, selectNext, selectPrev } = useLogsSelection(execution, entries, flatLogEntries, toggleExpanded);
const inputTableColumnCollapsing = ref();
const outputTableColumnCollapsing = ref();
const isLogDetailsOpen = computed(() => isOpen.value && selected.value !== void 0);
const isLogDetailsVisuallyOpen = computed(() => isLogDetailsOpen.value && !isCollapsingDetailsPanel.value);
const logsPanelActionsProps = computed(() => ({
isOpen: isOpen.value,
isSyncSelectionEnabled: logsStore.isLogSelectionSyncedWithCanvas,
showToggleButton: !isPoppedOut.value,
showPopOutButton: canPopOut.value && !isPoppedOut.value,
onPopOut,
onToggleOpen,
onToggleSyncSelection: logsStore.toggleLogSelectionSync
}));
const inputCollapsingColumnName = computed(() => inputTableColumnCollapsing.value?.nodeName === selected.value?.node.name ? inputTableColumnCollapsing.value?.columnName ?? null : null);
const outputCollapsingColumnName = computed(() => outputTableColumnCollapsing.value?.nodeName === selected.value?.node.name ? outputTableColumnCollapsing.value?.columnName ?? null : null);
const keyMap = computed(() => ({
j: selectNext,
k: selectPrev,
Escape: () => select(void 0),
ArrowDown: selectNext,
ArrowUp: selectPrev,
Space: () => selected.value && toggleExpanded(selected.value),
Enter: () => selected.value && handleOpenNdv(selected.value),
...isPoppedOut.value ? {
i: () => logsStore.toggleInputOpen(),
o: () => logsStore.toggleOutputOpen()
} : {}
}));
function handleResizeOverviewPanelEnd() {
if (isOverviewPanelFullWidth.value) select(void 0);
onOverviewPanelResizeEnd();
}
function handleOpenNdv(treeNode) {
ndvStore.setActiveNodeName(treeNode.node.name, "logs_view");
nextTick(() => {
const source = treeNode.runData?.source[0];
const inputBranch = source?.previousNodeOutput ?? 0;
ndvEventBus.emit("updateInputNodeName", source?.previousNode);
ndvEventBus.emit("setInputBranchIndex", inputBranch);
ndvStore.setOutputRunIndex(treeNode.runIndex);
});
}
function handleChangeInputTableColumnCollapsing(columnName) {
inputTableColumnCollapsing.value = columnName && selected.value ? {
nodeName: selected.value.node.name,
columnName
} : void 0;
}
function handleChangeOutputTableColumnCollapsing(columnName) {
outputTableColumnCollapsing.value = columnName && selected.value ? {
nodeName: selected.value.node.name,
columnName
} : void 0;
}
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
ref_key: "popOutContainer",
ref: popOutContainer,
"data-test-id": "logs-panel"
}, [(openBlock(), createBlock(LogsViewKeyboardEventListener_default, {
key: String(!!unref(popOutWindow)),
"key-map": keyMap.value,
container: unref(container$8)
}, null, 8, ["key-map", "container"])), createBaseVNode("div", {
ref_key: "popOutContent",
ref: popOutContent$1,
class: normalizeClass([_ctx.$style.popOutContent, unref(isPoppedOut) ? _ctx.$style.poppedOut : ""])
}, [createVNode(unref(N8nResizeWrapper_default), {
height: unref(isPoppedOut) ? void 0 : unref(height),
"supported-directions": ["top"],
"is-resizing-enabled": !unref(isPoppedOut),
class: normalizeClass(_ctx.$style.resizeWrapper),
style: normalizeStyle({ height: unref(isOpen) && !unref(isPoppedOut) ? `${unref(height)}px` : "auto" }),
onResize: unref(onResize),
onResizeend: unref(onResizeEnd)
}, {
default: withCtx(() => [createBaseVNode("div", {
ref_key: "container",
ref: container$8,
class: normalizeClass(_ctx.$style.container),
tabindex: "-1"
}, [unref(hasChat) && (!props.isReadOnly || unref(messages$1).length > 0) ? (openBlock(), createBlock(unref(N8nResizeWrapper_default), {
key: 0,
"supported-directions": ["right"],
"is-resizing-enabled": unref(isOpen),
width: unref(chatPanelWidth),
style: normalizeStyle({ width: `${unref(chatPanelWidth)}px` }),
class: normalizeClass(_ctx.$style.chat),
window: unref(popOutWindow),
onResize: unref(onChatPanelResize),
onResizeend: unref(onChatPanelResizeEnd)
}, {
default: withCtx(() => [(openBlock(), createBlock(ChatMessagesPanel_default, {
key: `canvas-chat-${unref(currentSessionId)}${unref(isPoppedOut) ? "-pop-out" : ""}`,
"data-test-id": "canvas-chat",
"is-open": unref(isOpen),
"is-read-only": _ctx.isReadOnly,
messages: unref(messages$1),
"session-id": unref(currentSessionId),
"past-chat-messages": unref(previousChatMessages),
"show-close-button": false,
"is-new-logs-enabled": true,
"is-header-clickable": !unref(isPoppedOut),
onClose: unref(onToggleOpen),
onRefreshSession: unref(refreshSession),
onDisplayExecution: unref(displayExecution),
onSendMessage: unref(sendMessage),
onClickHeader: unref(onToggleOpen)
}, null, 8, [
"is-open",
"is-read-only",
"messages",
"session-id",
"past-chat-messages",
"is-header-clickable",
"onClose",
"onRefreshSession",
"onDisplayExecution",
"onSendMessage",
"onClickHeader"
]))]),
_: 1
}, 8, [
"is-resizing-enabled",
"width",
"style",
"class",
"window",
"onResize",
"onResizeend"
])) : createCommentVNode("", true), createBaseVNode("div", {
ref_key: "logsContainer",
ref: logsContainer$1,
class: normalizeClass(_ctx.$style.logsContainer)
}, [createVNode(unref(N8nResizeWrapper_default), {
class: normalizeClass(_ctx.$style.overviewResizer),
width: unref(overviewPanelWidth),
style: normalizeStyle({ width: isLogDetailsVisuallyOpen.value ? `${unref(overviewPanelWidth)}px` : "" }),
"supported-directions": ["right"],
"is-resizing-enabled": isLogDetailsOpen.value,
window: unref(popOutWindow),
onResize: unref(onOverviewPanelResize),
onResizeend: handleResizeOverviewPanelEnd
}, {
default: withCtx(() => [createVNode(LogsOverviewPanel_default, {
class: normalizeClass(_ctx.$style.logsOverview),
"is-open": unref(isOpen),
"is-read-only": _ctx.isReadOnly,
"is-compact": isLogDetailsVisuallyOpen.value,
selected: unref(selected),
execution: unref(execution),
entries: unref(entries),
"latest-node-info": unref(latestNodeNameById),
"flat-log-entries": unref(flatLogEntries),
"is-header-clickable": !unref(isPoppedOut),
onClickHeader: unref(onToggleOpen),
onSelect: unref(select),
onClearExecutionData: unref(resetExecutionData),
onToggleExpanded: unref(toggleExpanded),
onOpenNdv: handleOpenNdv
}, {
actions: withCtx(() => [!isLogDetailsVisuallyOpen.value ? (openBlock(), createBlock(LogsPanelActions_default, normalizeProps(mergeProps({ key: 0 }, logsPanelActionsProps.value)), null, 16)) : createCommentVNode("", true)]),
_: 1
}, 8, [
"class",
"is-open",
"is-read-only",
"is-compact",
"selected",
"execution",
"entries",
"latest-node-info",
"flat-log-entries",
"is-header-clickable",
"onClickHeader",
"onSelect",
"onClearExecutionData",
"onToggleExpanded"
])]),
_: 1
}, 8, [
"class",
"width",
"style",
"is-resizing-enabled",
"window",
"onResize"
]), isLogDetailsVisuallyOpen.value && unref(selected) ? (openBlock(), createBlock(LogDetailsPanel_default, {
key: 0,
class: normalizeClass(_ctx.$style.logDetails),
"is-open": unref(isOpen),
"log-entry": unref(selected),
window: unref(popOutWindow),
"latest-info": unref(latestNodeNameById)[unref(selected).node.id],
panels: unref(logsStore).detailsState,
"collapsing-input-table-column-name": inputCollapsingColumnName.value,
"collapsing-output-table-column-name": outputCollapsingColumnName.value,
"is-header-clickable": !unref(isPoppedOut),
onClickHeader: unref(onToggleOpen),
onToggleInputOpen: unref(logsStore).toggleInputOpen,
onToggleOutputOpen: unref(logsStore).toggleOutputOpen,
onCollapsingInputTableColumnChanged: handleChangeInputTableColumnCollapsing,
onCollapsingOutputTableColumnChanged: handleChangeOutputTableColumnCollapsing
}, {
actions: withCtx(() => [isLogDetailsVisuallyOpen.value ? (openBlock(), createBlock(LogsPanelActions_default, normalizeProps(mergeProps({ key: 0 }, logsPanelActionsProps.value)), null, 16)) : createCommentVNode("", true)]),
_: 1
}, 8, [
"class",
"is-open",
"log-entry",
"window",
"latest-info",
"panels",
"collapsing-input-table-column-name",
"collapsing-output-table-column-name",
"is-header-clickable",
"onClickHeader",
"onToggleInputOpen",
"onToggleOutputOpen"
])) : createCommentVNode("", true)], 2)], 2)]),
_: 1
}, 8, [
"height",
"is-resizing-enabled",
"class",
"style",
"onResize",
"onResizeend"
])], 2)], 512);
};
}
});
var LogsPanel_vue_vue_type_style_index_0_lang_module_default = {
popOutContent: "_popOutContent_17exv_123",
resizeWrapper: "_resizeWrapper_17exv_129",
poppedOut: "_poppedOut_17exv_136",
container: "_container_17exv_140",
chat: "_chat_17exv_149",
logsContainer: "_logsContainer_17exv_153",
overviewResizer: "_overviewResizer_17exv_163",
logsOverview: "_logsOverview_17exv_171",
logsDetails: "_logsDetails_17exv_175"
};
var LogsPanel_default = /* @__PURE__ */ __plugin_vue_export_helper_default(LogsPanel_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": LogsPanel_vue_vue_type_style_index_0_lang_module_default }]]);
export { LogsPanel_default as t };