import { bx as addTokenUsageData, by as emptyTokenUsageData, bz as parseErrorMetadata, bA as isChatNode, bB as NodeConnectionTypes, bC as v4, bD as get, bE as AGENT_LANGCHAIN_NODE_TYPE, bF as MANUAL_CHAT_TRIGGER_NODE_TYPE, bG as CHAT_TRIGGER_NODE_TYPE, bH as isEmpty, a as useToast, r as ref, x as computed, c as useI18n$1, bI as usePinnedData, am as useMessage, an as MODAL_CONFIRM, a1 as useWorkflowsStore, at as useRootStore, bJ as useLogsStore, b as useRouter, bK as useNodeHelpers, bL as useRunWorkflow, V as VIEWS, bM as chatEventBus, a7 as watch, bN as provide, d as defineComponent, h as createElementBlock, g as openBlock, n as normalizeClass, i as createVNode, j as createBaseVNode, l as unref, p as N8nText, w as withCtx, X as renderSlot, k as createTextVNode, t as toDisplayString, _ as _export_sfc, a2 as useRoute, af as useSourceControlStore, bO as useCanvasOperations, ad as useNodeTypesStore, bP as START_NODE_TYPE, e as createBlock, f as createCommentVNode, a9 as Tooltip, bQ as formatTokenUsageCount, bR as getDefaultExportFromCjs, bS as requireUpperFirst, F as Fragment, aG as useTemplateRef, bT as useTimestamp, bU as toTime, bV as toDayMonth, B as withModifiers, A as renderList, ap as normalizeStyle, bW as NodeIcon, ab as I18nT, N as N8nIcon, aa as _sfc_main$k, q as N8nButton, Y as nextTick, bX as getScrollbarWidth, bY as useVirtualList, bZ as toRef, K as mergeProps, O as N8nRadioButtons, b_ as inject, b$ as isRef, c0 as toRefs, o as onMounted, c1 as normalizeProps, c2 as guardReactiveProps, c3 as resolveDynamicComponent, c4 as markdownLink, c5 as useFileDialog, c6 as onUnmounted, b2 as withDirectives, c7 as vModelText, b8 as withKeys, c8 as useAttrs, c9 as useClipboard, bq as createSlots, aY as useNDVStore, ca as PopOutWindowKey, cb as resolveDirective, cc as RunData, C as N8nLink, cd as waitingNodeTooltip, a_ as useLocalStorage, ce as LOG_DETAILS_PANEL_STATE, cf as KeyboardShortcutTooltip, cg as N8nResizeWrapper, ch as useStyles, aM as N8nActionDropdown, ci as Workflow, cj as useThrottleFn, ck as parse, cl as shallowRef, Q as useUIStore, cm as useCanvasStore, al as useTelemetry, ax as useDocumentTitle, cn as onScopeDispose, W as onBeforeUnmount, co as useProvideTooltipAppendTo, cp as LOGS_PANEL_STATE, cq as LOCAL_STORAGE_PANEL_HEIGHT, cr as LOCAL_STORAGE_OVERVIEW_PANEL_WIDTH, cs as LOCAL_STORAGE_PANEL_WIDTH, ct as useActiveElement, bm as useKeybindings, cu as ndvEventBus } from "./index-CeNA_ukL.js"; import { _ as __unplugin_components_1 } from "./AnimatedSpinner-Boagztc5.js"; import { _ as _sfc_main$j } from "./ConsumedTokensDetails.vue_vue_type_script_setup_true_lang-DfVZTSuh.js"; import { H as HighlightJS, V as VueMarkdown } from "./core-CZWe7osv.js"; import { c as canvasEventBus } from "./canvas-X-1icOh2.js"; const TOOL_EXECUTOR_NODE_NAME = "PartialExecutionToolExecutor"; function constructChatWebsocketUrl(url, executionId, sessionId2, isPublic) { const baseUrl = new URL(url).origin; const wsProtocol = baseUrl.startsWith("https") ? "wss" : "ws"; const wsUrl = baseUrl.replace(/^https?/, wsProtocol); return `${wsUrl}/chat?sessionId=${sessionId2}&executionId=${executionId}${isPublic ? "&isPublic=true" : ""}`; } function getConsumedTokens(task) { if (!task.data) { return emptyTokenUsageData; } const tokenUsage = Object.values(task.data).flat().flat().reduce((acc, curr) => { const tokenUsageData = curr?.json?.tokenUsage ?? curr?.json?.tokenUsageEstimate; if (!tokenUsageData) return acc; return addTokenUsageData(acc, { ...tokenUsageData, isEstimate: !!curr?.json.tokenUsageEstimate }); }, emptyTokenUsageData); return tokenUsage; } function createNode(node, context, runIndex, runData, children = []) { return { parent: context.parent, node, // The ID consists of workflow ID, node ID and run index (including ancestor's), which // makes it possible to identify the same log across different executions id: `${context.workflow.id}:${node.id}:${[...context.ancestorRunIndexes, runIndex].join(":")}`, runIndex, runData, children, consumedTokens: runData ? getConsumedTokens(runData) : emptyTokenUsageData, workflow: context.workflow, executionId: context.executionId, execution: context.data }; } function getChildNodes(treeNode, node, runIndex, context) { const subExecutionLocator = findSubExecutionLocator(treeNode); if (subExecutionLocator !== void 0) { const workflow = context.workflows[subExecutionLocator.workflowId]; const subWorkflowRunData = context.subWorkflowData[subExecutionLocator.executionId]; if (!workflow || !subWorkflowRunData) { return []; } return createLogTreeRec({ ...context, parent: treeNode, ancestorRunIndexes: [...context.ancestorRunIndexes, runIndex ?? 0], workflow, executionId: subExecutionLocator.executionId, data: subWorkflowRunData }); } const connectedSubNodes = context.workflow.getParentNodes(node.name, "ALL_NON_MAIN", 1); function isMatchedSource(source) { return (source?.previousNode === node.name || isPlaceholderLog(treeNode) && source?.previousNode === TOOL_EXECUTOR_NODE_NAME) && (runIndex === void 0 || source.previousNodeRun === runIndex); } return connectedSubNodes.flatMap( (subNodeName) => (context.data.resultData.runData[subNodeName] ?? []).flatMap((t, index) => { const isMatched = t.source.some((source) => source !== null) ? t.source.some(isMatchedSource) : runIndex === void 0 || index === runIndex; if (!isMatched) { return []; } const subNode = context.workflow.getNode(subNodeName); return subNode ? getTreeNodeData(subNode, t, index, { ...context, ancestorRunIndexes: [...context.ancestorRunIndexes, runIndex ?? 0], parent: treeNode }) : []; }) ); } function getTreeNodeData(node, runData, runIndex, context) { const treeNode = createNode(node, context, runIndex ?? 0, runData); const children = getChildNodes(treeNode, node, runIndex, context).sort(sortLogEntries); if ((runData === void 0 || node.disabled) && children.length === 0) { return []; } treeNode.children = children; return [treeNode]; } function getTotalConsumedTokens(...usage) { return usage.reduce(addTokenUsageData, emptyTokenUsageData); } function getSubtreeTotalConsumedTokens(treeNode, includeSubWorkflow) { const executionId = treeNode.executionId; function calculate(currentNode) { if (!includeSubWorkflow && currentNode.executionId !== executionId) { return emptyTokenUsageData; } return getTotalConsumedTokens( currentNode.consumedTokens, ...currentNode.children.map(calculate) ); } return calculate(treeNode); } function findLogEntryToAutoSelect(subTree) { const entryWithError = findLogEntryRec((e) => !!e.runData?.error, subTree); if (entryWithError) { return entryWithError; } const entryForAiAgent = findLogEntryRec( (entry) => entry.node.type === AGENT_LANGCHAIN_NODE_TYPE || entry.parent?.node.type === AGENT_LANGCHAIN_NODE_TYPE && isPlaceholderLog(entry.parent), subTree ); if (entryForAiAgent) { return entryForAiAgent; } return subTree[subTree.length - 1]; } function createLogTree(workflow, response, workflows = {}, subWorkflowData = {}) { return createLogTreeRec({ parent: void 0, ancestorRunIndexes: [], executionId: response.id, workflow, workflows, data: response.data ?? { resultData: { runData: {} } }, subWorkflowData }); } function createLogTreeRec(context) { const runData = context.data.resultData.runData; return Object.entries(runData).flatMap(([nodeName, taskData]) => { const node = context.workflow.getNode(nodeName); if (node === null) { return []; } const childNodes = context.workflow.getChildNodes(nodeName, "ALL_NON_MAIN"); if (childNodes.length === 0) { return taskData.map((task, runIndex) => ({ node, task, runIndex, nodeHasMultipleRuns: taskData.length > 1 })); } if (childNodes.some((child) => (runData[child] ?? []).length > 0)) { return []; } const firstChild = context.workflow.getNode(childNodes[0]); if (firstChild === null) { return []; } return [{ node: firstChild, nodeHasMultipleRuns: false }]; }).flatMap( ({ node, runIndex, task, nodeHasMultipleRuns }) => getTreeNodeData(node, task, nodeHasMultipleRuns ? runIndex : void 0, context) ).sort(sortLogEntries); } function findLogEntryRec(isMatched, entries) { for (const entry of entries) { if (isMatched(entry)) { return entry; } const child = findLogEntryRec(isMatched, entry.children); if (child) { return child; } } return void 0; } function findSelectedLogEntry(selection, entries, isExecuting) { switch (selection.type) { case "initial": return isExecuting ? void 0 : findLogEntryToAutoSelect(entries); case "none": return void 0; case "selected": { const found = findLogEntryRec((e) => e.id === selection.entry.id, entries); if (found === void 0 && !isExecuting) { for (let runIndex = selection.entry.runIndex - 1; runIndex >= 0; runIndex--) { const fallback = findLogEntryRec( (e) => e.workflow.id === selection.entry.workflow.id && e.node.id === selection.entry.node.id && e.runIndex === runIndex, entries ); if (fallback !== void 0) { return fallback; } } } return found; } } } function flattenLogEntries(entries, collapsedEntryIds, ret = []) { for (const entry of entries) { ret.push(entry); if (!collapsedEntryIds[entry.id]) { flattenLogEntries(entry.children, collapsedEntryIds, ret); } } return ret; } function getEntryAtRelativeIndex(entries, id, relativeIndex) { const offset = entries.findIndex((e) => e.id === id); return offset === -1 ? void 0 : entries[offset + relativeIndex]; } function sortLogEntries(a, b) { if (a.runData === void 0) { return a.children.length > 0 ? sortLogEntries(a.children[0], b) : 0; } if (b.runData === void 0) { return b.children.length > 0 ? sortLogEntries(a, b.children[0]) : 0; } if (a.runData.startTime === b.runData.startTime) { return a.runData.executionIndex - b.runData.executionIndex; } return a.runData.startTime - b.runData.startTime; } function mergeStartData(startData, response) { if (!response.data) { return response; } const nodeNames = [ ...new Set( Object.keys(startData).concat(Object.keys(response.data.resultData.runData)) ).values() ]; const runData = Object.fromEntries( nodeNames.map((nodeName) => { const tasks = response.data?.resultData.runData[nodeName] ?? []; const mergedTasks = tasks.concat( (startData[nodeName] ?? []).filter( (task) => ( // To remove duplicate runs, we check start time in addition to execution index // because nodes such as Wait and Form emits multiple websocket events with // different execution index for a single run tasks.every( (t) => t.startTime < task.startTime && t.executionIndex !== task.executionIndex ) ) ).map((task) => ({ ...task, executionTime: 0, executionStatus: "running" })) ); return [nodeName, mergedTasks]; }) ); return { ...response, data: { ...response.data, resultData: { ...response.data.resultData, runData } } }; } function hasSubExecution(entry) { return findSubExecutionLocator(entry) !== void 0; } function findSubExecutionLocator(entry) { const metadata = entry.runData?.metadata?.subExecution; if (metadata) { return { workflowId: metadata.workflowId, executionId: metadata.executionId }; } return parseErrorMetadata(entry.runData?.error)?.subExecution; } function getDepth(entry) { let depth = 0; let currentEntry = entry; while (currentEntry.parent !== void 0) { currentEntry = currentEntry.parent; depth++; } return depth; } function getInputKey(node) { if (node.type === MANUAL_CHAT_TRIGGER_NODE_TYPE && node.typeVersion < 1.1) { return "input"; } if (node.type === CHAT_TRIGGER_NODE_TYPE) { return "chatInput"; } return "chatInput"; } function extractChatInput(workflow, resultData) { const chatTrigger = workflow.nodes.find(isChatNode); if (chatTrigger === void 0) { return void 0; } const inputKey = getInputKey(chatTrigger); const runData = (resultData.runData[chatTrigger.name] ?? [])[0]; const message = runData?.data?.[NodeConnectionTypes.Main]?.[0]?.[0]?.json?.[inputKey]; if (runData === void 0 || typeof message !== "string") { return void 0; } return { text: message, sender: "user", id: v4() }; } function extractBotResponse(resultData, executionId, emptyText2) { const lastNodeExecuted = resultData.lastNodeExecuted; if (!lastNodeExecuted) return void 0; const nodeResponseDataArray = get(resultData.runData, lastNodeExecuted) ?? []; const nodeResponseData = nodeResponseDataArray[nodeResponseDataArray.length - 1]; let responseMessage; if (get(nodeResponseData, "error")) { responseMessage = "[ERROR: " + get(nodeResponseData, "error.message") + "]"; } else { const responseData = get(nodeResponseData, "data.main[0][0].json"); const text = extractResponseText(responseData) ?? emptyText2; if (!text) { return void 0; } responseMessage = text; } return { text: responseMessage, sender: "bot", id: executionId ?? v4() }; } function extractResponseText(responseData) { if (!responseData || isEmpty(responseData)) { return void 0; } const paths = ["output", "text", "response.text", "message"]; const matchedPath = paths.find((path) => get(responseData, path)); if (!matchedPath) return JSON.stringify(responseData, null, 2); const matchedOutput = get(responseData, matchedPath); if (typeof matchedOutput === "object") { return "```json\n" + JSON.stringify(matchedOutput, null, 2) + "\n```"; } return matchedOutput?.toString() ?? ""; } function restoreChatHistory(workflowExecutionData, emptyText2) { if (!workflowExecutionData?.data) { return []; } const userMessage = extractChatInput( workflowExecutionData.workflowData, workflowExecutionData.data.resultData ); const botMessage = extractBotResponse( workflowExecutionData.data.resultData, workflowExecutionData.id, emptyText2 ); return [...userMessage ? [userMessage] : [], ...botMessage ? [botMessage] : []]; } async function processFiles(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(new Error(`Error reading file: ${reader.error?.message ?? "Unknown error"}`)); reader.readAsDataURL(file); }); }); return await Promise.all(filePromises); } function isSubNodeLog(logEntry) { return logEntry.parent !== void 0 && logEntry.parent.executionId === logEntry.executionId; } function isPlaceholderLog(treeNode) { return treeNode.runData === void 0; } function useChatMessaging({ chatTrigger, messages: messages2, sessionId: sessionId2, executionResultData, onRunChatWorkflow, ws }) { const locale = useI18n$1(); const { showError } = useToast(); const previousMessageIndex = ref(0); const isLoading = ref(false); const setLoadingState = (loading) => { isLoading.value = loading; }; async function convertFileToBinaryData(file) { const reader = new FileReader(); return await new Promise((resolve, reject) => { reader.onload = () => { const binaryData = { data: reader.result.split("base64,")?.[1] ?? "", mimeType: file.type, fileName: file.name, fileSize: `${file.size} bytes`, fileExtension: file.name.split(".").pop() ?? "", fileType: file.type.split("/")[0] }; resolve(binaryData); }; reader.onerror = () => { reject(new Error("Failed to convert file to binary data")); }; reader.readAsDataURL(file); }); } 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(new Error("Chat Trigger Node could not be found!"), "Trigger Node not found"); return; } const inputKey = getInputKey(triggerNode); const inputPayload = { json: { sessionId: sessionId2.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; } const responseMode = triggerNode.parameters.options?.responseMode; if (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) { messages2.value.push(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) { const confirmResult = 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") } ); if (!(confirmResult === MODAL_CONFIRM)) return; pinnedChatData.unsetData("unpin-and-send-chat-message-modal"); } const newMessage = { text: message, sender: "user", sessionId: sessionId2.value, id: v4(), files }; messages2.value.push(newMessage); if (ws.value?.readyState === WebSocket.OPEN && !isLoading.value) { ws.value.send( JSON.stringify({ sessionId: sessionId2.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$1 = "Chat"; const ChatOptionsSymbol = "ChatOptions"; const ChatSymbol = "Chat"; function useChatState(isReadOnly) { const locale = useI18n$1(); const workflowsStore = useWorkflowsStore(); const rootStore = useRootStore(); const logsStore = useLogsStore(); const router = useRouter(); const nodeHelpers = useNodeHelpers(); const { runWorkflow } = useRunWorkflow({ router }); const ws = ref(null); const messages2 = ref([]); const currentSessionId = ref(v4().replace(/-/g, "")); 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, messages: messages2, sessionId: currentSessionId, executionResultData: computed(() => workflowsStore.getWorkflowExecution?.data?.resultData), onRunChatWorkflow, ws }); function createChatConfig(params) { const chatConfig2 = { messages: params.messages, sendMessage: params.sendMessage, initialMessages: ref([]), currentSessionId: params.currentSessionId, waitingForResponse: params.isLoading }; const chatOptions2 = { 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 }; return { chatConfig: chatConfig2, chatOptions: chatOptions2 }; } const { chatConfig, chatOptions } = createChatConfig({ messages: messages2, sendMessage, currentSessionId, isLoading, isDisabled: computed(() => isReadOnly), allowFileUploads, locale }); const restoredChatMessages = computed( () => restoreChatHistory( workflowsStore.workflowExecutionData, locale.baseText("chat.window.chat.response.empty") ) ); provide(ChatSymbol, 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 = workflowsStore.chatPartialExecutionDestinationNode; 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() }; messages2.value.push(newMessage); if (logsStore.isOpen) { chatEventBus.emit("focusInput"); } }; ws.value.onclose = () => { setLoadingState(false); ws.value = null; }; } await createExecutionPromise(); workflowsStore.appendChatMessage(payload.message); return response; } return; } function refreshSession() { workflowsStore.setWorkflowExecutionData(null); nodeHelpers.updateNodesExecutionIssues(); messages2.value = []; currentSessionId.value = v4().replace(/-/g, ""); 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"); } return { currentSessionId, messages: computed(() => isReadOnly ? restoredChatMessages.value : messages2.value), previousChatMessages, sendMessage, refreshSession, displayExecution }; } const _sfc_main$i = /* @__PURE__ */ defineComponent({ __name: "LogsPanelHeader", props: { title: {} }, emits: ["click"], setup(__props, { emit: __emit }) { const emit = __emit; return (_ctx, _cache) => { return openBlock(), createElementBlock("header", { class: normalizeClass(_ctx.$style.container), onClick: _cache[0] || (_cache[0] = ($event) => emit("click")) }, [ createVNode(unref(N8nText), { 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); }; } }); const container$8 = "_container_1uwbw_123"; const title$2 = "_title_1uwbw_144"; const actions$1 = "_actions_1uwbw_152"; const style0$b = { container: container$8, title: title$2, actions: actions$1 }; const cssModules$b = { "$style": style0$b }; const LogsPanelHeader = /* @__PURE__ */ _export_sfc(_sfc_main$i, [["__cssModules", cssModules$b]]); 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) => node.type === START_NODE_TYPE || nodeTypesStore.isTriggerNode(node.type)).every((node) => node.disabled) ); return computed( () => !isReadOnlyRoute.value && !isReadOnlyEnvironment.value && !isWorkflowRunning.value && !allTriggerNodesDisabled.value && !!workflowExecutionData.value ); } const _sfc_main$h = /* @__PURE__ */ defineComponent({ __name: "LogsViewConsumedTokenCountText", props: { consumedTokens: {} }, setup(__props) { const locale = useI18n$1(); return (_ctx, _cache) => { const _component_ConsumedTokensDetails = _sfc_main$j; return _ctx.consumedTokens !== void 0 ? (openBlock(), createBlock(unref(Tooltip), { key: 0, enterable: false }, { content: withCtx(() => [ createVNode(_component_ConsumedTokensDetails, { "consumed-tokens": _ctx.consumedTokens }, null, 8, ["consumed-tokens"]) ]), default: withCtx(() => [ createBaseVNode("span", null, toDisplayString(unref(locale).baseText("runData.aiContentBlock.tokens", { interpolate: { count: unref(formatTokenUsageCount)(_ctx.consumedTokens, "total") } })), 1) ]), _: 1 })) : createCommentVNode("", true); }; } }); var upperFirstExports = requireUpperFirst(); const upperFirst = /* @__PURE__ */ getDefaultExportFromCjs(upperFirstExports); const _hoisted_1$e = { key: 0 }; const _sfc_main$g = /* @__PURE__ */ defineComponent({ __name: "LogsViewNodeName", props: { name: {}, isError: { type: Boolean }, isDeleted: { type: Boolean } }, setup(__props) { return (_ctx, _cache) => { return openBlock(), createBlock(unref(N8nText), { tag: "div", bold: true, size: "small", class: normalizeClass(_ctx.$style.name), color: _ctx.isError ? "danger" : void 0 }, { default: withCtx(() => [ _ctx.isDeleted ? (openBlock(), createElementBlock("del", _hoisted_1$e, toDisplayString(_ctx.name), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [ createTextVNode(toDisplayString(_ctx.name), 1) ], 64)) ]), _: 1 }, 8, ["class", "color"]); }; } }); const name$1 = "_name_1t0q3_123"; const style0$a = { name: name$1 }; const cssModules$a = { "$style": style0$a }; const LogsViewNodeName = /* @__PURE__ */ _export_sfc(_sfc_main$g, [["__cssModules", cssModules$a]]); const _hoisted_1$d = ["aria-expanded", "aria-selected"]; const _sfc_main$f = /* @__PURE__ */ defineComponent({ __name: "LogsOverviewRow", props: { data: {}, isSelected: { type: Boolean }, isReadOnly: { type: Boolean }, shouldShowTokenCountColumn: { type: Boolean }, isCompact: { type: Boolean }, latestInfo: {}, expanded: { type: Boolean }, canOpenNdv: { type: Boolean } }, emits: ["toggleExpanded", "toggleSelected", "triggerPartialExecution", "openNdv"], setup(__props, { emit: __emit }) { const props = __props; const emit = __emit; const container2 = useTemplateRef("containerRef"); const locale = useI18n$1(); const now = useTimestamp({ interval: 1e3 }); const nodeTypeStore = useNodeTypesStore(); const type = computed(() => nodeTypeStore.getNodeType(props.data.node.type)); const isRunning = computed(() => props.data.runData?.executionStatus === "running"); const isWaiting = computed(() => props.data.runData?.executionStatus === "waiting"); const isSettled = computed(() => !isRunning.value && !isWaiting.value); const isError = computed(() => !!props.data.runData?.error); const statusTextKeyPath = computed( () => isSettled.value ? "logs.overview.body.summaryText.in" : "logs.overview.body.summaryText.for" ); const startedAtText = computed(() => { if (props.data.runData === void 0) { return "—"; } const time = new Date(props.data.runData.startTime); return locale.baseText("logs.overview.body.started", { interpolate: { time: `${toTime(time, true)}, ${toDayMonth(time)}` } }); }); const statusText = computed(() => upperFirst(props.data.runData?.executionStatus ?? "")); const timeText = computed( () => props.data.runData ? locale.displayTimer( isSettled.value ? props.data.runData.executionTime : Math.floor((now.value - props.data.runData.startTime) / 1e3) * 1e3, true ) : void 0 ); const subtreeConsumedTokens = computed( () => props.shouldShowTokenCountColumn ? getSubtreeTotalConsumedTokens(props.data, false) : void 0 ); const hasChildren = computed(() => props.data.children.length > 0 || hasSubExecution(props.data)); const indents = computed(() => { const ret = []; let data = props.data; while (data.parent !== void 0) { const siblings = data.parent?.children ?? []; const lastSibling = siblings[siblings.length - 1]; ret.unshift({ straight: lastSibling?.id !== data.id, curved: data === props.data }); data = data.parent; } return ret; }); watch( () => props.isSelected, (isSelected) => { void nextTick(() => { if (isSelected) { container2.value?.focus(); } }); }, { immediate: true } ); return (_ctx, _cache) => { const _component_NodeIcon = NodeIcon; const _component_AnimatedSpinner = __unplugin_components_1; return openBlock(), createElementBlock("div", { ref: "containerRef", role: "treeitem", tabindex: "-1", "aria-expanded": props.data.children.length > 0 && props.expanded, "aria-selected": props.isSelected, class: normalizeClass({ [_ctx.$style.container]: true, [_ctx.$style.compact]: props.isCompact, [_ctx.$style.error]: isError.value, [_ctx.$style.selected]: props.isSelected }), onClick: _cache[3] || (_cache[3] = withModifiers(($event) => emit("toggleSelected"), ["stop"])) }, [ (openBlock(true), createElementBlock(Fragment, null, renderList(indents.value, (indent2, level) => { return openBlock(), createElementBlock("div", { key: level, class: normalizeClass({ [_ctx.$style.indent]: true, [_ctx.$style.connectorCurved]: indent2.curved, [_ctx.$style.connectorStraight]: indent2.straight }) }, null, 2); }), 128)), createBaseVNode("div", { class: normalizeClass(_ctx.$style.background), style: normalizeStyle({ "--indent-depth": indents.value.length }) }, null, 6), createVNode(_component_NodeIcon, { "node-type": type.value, size: 16, class: normalizeClass(_ctx.$style.icon) }, null, 8, ["node-type", "class"]), createVNode(LogsViewNodeName, { class: normalizeClass(_ctx.$style.name), name: _ctx.latestInfo?.name ?? props.data.node.name, "is-error": isError.value, "is-deleted": _ctx.latestInfo?.deleted ?? false }, null, 8, ["class", "name", "is-error", "is-deleted"]), !_ctx.isCompact ? (openBlock(), createBlock(unref(N8nText), { key: 0, tag: "div", color: "text-light", size: "small", class: normalizeClass(_ctx.$style.timeTook) }, { default: withCtx(() => [ timeText.value !== void 0 ? (openBlock(), createBlock(unref(I18nT), { key: 0, keypath: statusTextKeyPath.value, scope: "global" }, { status: withCtx(() => [ createVNode(unref(N8nText), { color: isError.value ? "danger" : void 0, bold: isError.value, size: "small" }, { default: withCtx(() => [ isRunning.value ? (openBlock(), createBlock(_component_AnimatedSpinner, { key: 0, class: normalizeClass(_ctx.$style.statusTextIcon) }, null, 8, ["class"])) : isWaiting.value ? (openBlock(), createBlock(unref(N8nIcon), { key: 1, icon: "status-waiting", class: normalizeClass(_ctx.$style.statusTextIcon) }, null, 8, ["class"])) : isError.value ? (openBlock(), createBlock(unref(N8nIcon), { key: 2, icon: "triangle-alert", class: normalizeClass(_ctx.$style.statusTextIcon) }, null, 8, ["class"])) : createCommentVNode("", true), createTextVNode(" " + toDisplayString(statusText.value), 1) ]), _: 1 }, 8, ["color", "bold"]) ]), time: withCtx(() => [ createTextVNode(toDisplayString(timeText.value), 1) ]), _: 1 }, 8, ["keypath"])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [ createTextVNode("—") ], 64)) ]), _: 1 }, 8, ["class"])) : createCommentVNode("", true), !_ctx.isCompact ? (openBlock(), createBlock(unref(N8nText), { key: 1, tag: "div", color: "text-light", size: "small", class: normalizeClass(_ctx.$style.startedAt) }, { default: withCtx(() => [ createTextVNode(toDisplayString(startedAtText.value), 1) ]), _: 1 }, 8, ["class"])) : createCommentVNode("", true), !_ctx.isCompact && subtreeConsumedTokens.value !== void 0 ? (openBlock(), createBlock(unref(N8nText), { key: 2, tag: "div", color: "text-light", size: "small", class: normalizeClass(_ctx.$style.consumedTokens) }, { default: withCtx(() => [ subtreeConsumedTokens.value.totalTokens > 0 && (props.data.children.length === 0 || !props.expanded) ? (openBlock(), createBlock(_sfc_main$h, { key: 0, "consumed-tokens": subtreeConsumedTokens.value }, null, 8, ["consumed-tokens"])) : createCommentVNode("", true) ]), _: 1 }, 8, ["class"])) : createCommentVNode("", true), isError.value && _ctx.isCompact ? (openBlock(), createBlock(unref(N8nIcon), { key: 3, size: "medium", color: "danger", icon: "triangle-alert", class: normalizeClass(_ctx.$style.compactErrorIcon) }, null, 8, ["class"])) : createCommentVNode("", true), !_ctx.isCompact || !props.latestInfo?.deleted ? (openBlock(), createBlock(unref(_sfc_main$k), { key: 4, type: "secondary", size: "small", icon: "square-pen", "icon-size": "medium", style: normalizeStyle({ visibility: props.canOpenNdv ? "" : "hidden" }), disabled: props.latestInfo?.deleted, class: normalizeClass(_ctx.$style.openNdvButton), "aria-label": unref(locale).baseText("logs.overview.body.open"), onClick: _cache[0] || (_cache[0] = withModifiers(($event) => emit("openNdv"), ["stop"])) }, null, 8, ["style", "disabled", "class", "aria-label"])) : createCommentVNode("", true), !_ctx.isCompact || !props.isReadOnly && !props.latestInfo?.deleted && !props.latestInfo?.disabled ? (openBlock(), createBlock(unref(_sfc_main$k), { key: 5, type: "secondary", size: "small", icon: "play", "aria-label": unref(locale).baseText("logs.overview.body.run"), class: normalizeClass([_ctx.$style.partialExecutionButton, indents.value.length > 0 ? _ctx.$style.unavailable : ""]), disabled: props.latestInfo?.deleted || props.latestInfo?.disabled, onClick: _cache[1] || (_cache[1] = withModifiers(($event) => emit("triggerPartialExecution"), ["stop"])) }, null, 8, ["aria-label", "class", "disabled"])) : createCommentVNode("", true), _ctx.isCompact && !hasChildren.value ? (openBlock(), createElementBlock(Fragment, { key: 6 }, [ isRunning.value ? (openBlock(), createBlock(_component_AnimatedSpinner, { key: 0, class: normalizeClass(_ctx.$style.statusIcon) }, null, 8, ["class"])) : isWaiting.value ? (openBlock(), createBlock(unref(N8nIcon), { key: 1, icon: "status-waiting", class: normalizeClass(_ctx.$style.statusIcon) }, null, 8, ["class"])) : createCommentVNode("", true) ], 64)) : createCommentVNode("", true), !_ctx.isCompact || hasChildren.value ? (openBlock(), createBlock(unref(N8nButton), { key: 7, type: "secondary", size: "small", icon: props.expanded ? "chevron-down" : "chevron-up", "icon-size": "medium", square: true, style: normalizeStyle({ visibility: hasChildren.value ? "" : "hidden" }), class: normalizeClass(_ctx.$style.toggleButton), "aria-label": unref(locale).baseText("logs.overview.body.toggleRow"), onClick: _cache[2] || (_cache[2] = withModifiers(($event) => emit("toggleExpanded"), ["stop"])) }, null, 8, ["icon", "style", "class", "aria-label"])) : createCommentVNode("", true) ], 10, _hoisted_1$d); }; } }); const container$7 = "_container_6ygvb_123"; const background = "_background_6ygvb_140"; const selected = "_selected_6ygvb_149"; const error = "_error_6ygvb_155"; const indent = "_indent_6ygvb_159"; const connectorCurved = "_connectorCurved_6ygvb_168"; const connectorStraight = "_connectorStraight_6ygvb_178"; const icon$3 = "_icon_6ygvb_187"; const name = "_name_6ygvb_193"; const timeTook = "_timeTook_6ygvb_199"; const statusTextIcon = "_statusTextIcon_6ygvb_204"; const startedAt = "_startedAt_6ygvb_209"; const consumedTokens = "_consumedTokens_6ygvb_215"; const compactErrorIcon = "_compactErrorIcon_6ygvb_222"; const partialExecutionButton = "_partialExecutionButton_6ygvb_234"; const openNdvButton = "_openNdvButton_6ygvb_235"; const compact = "_compact_6ygvb_222"; const unavailable = "_unavailable_6ygvb_245"; const toggleButton = "_toggleButton_6ygvb_253"; const statusIcon = "_statusIcon_6ygvb_277"; const placeholder$1 = "_placeholder_6ygvb_285"; const style0$9 = { container: container$7, background, selected, error, indent, connectorCurved, connectorStraight, icon: icon$3, name, timeTook, statusTextIcon, startedAt, consumedTokens, compactErrorIcon, partialExecutionButton, openNdvButton, compact, unavailable, toggleButton, statusIcon, placeholder: placeholder$1 }; const cssModules$9 = { "$style": style0$9 }; const LogsOverviewRow = /* @__PURE__ */ _export_sfc(_sfc_main$f, [["__cssModules", cssModules$9]]); const _sfc_main$e = /* @__PURE__ */ defineComponent({ __name: "LogsViewExecutionSummary", props: { status: {}, consumedTokens: {}, startTime: {}, timeTook: {} }, setup(__props) { const locale = useI18n$1(); const now = useTimestamp({ interval: 1e3 }); const executionStatusText = computed( () => __props.status === "running" || __props.status === "waiting" ? locale.baseText("logs.overview.body.summaryText.for", { interpolate: { status: upperFirst(__props.status), time: locale.displayTimer(Math.floor((now.value - __props.startTime) / 1e3) * 1e3, true) } }) : __props.timeTook === void 0 ? upperFirst(__props.status) : locale.baseText("logs.overview.body.summaryText.in", { interpolate: { status: upperFirst(__props.status), time: locale.displayTimer(__props.timeTook, true) } }) ); return (_ctx, _cache) => { return openBlock(), createBlock(unref(N8nText), { 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(_sfc_main$h, { key: 0, "consumed-tokens": _ctx.consumedTokens }, null, 8, ["consumed-tokens"])) : createCommentVNode("", true) ]), _: 1 }, 8, ["class"]); }; } }); const container$6 = "_container_pt5hk_123"; const style0$8 = { container: container$6 }; const cssModules$8 = { "$style": style0$8 }; const LogsViewExecutionSummary = /* @__PURE__ */ _export_sfc(_sfc_main$e, [["__cssModules", cssModules$8]]); const _sfc_main$d = /* @__PURE__ */ defineComponent({ __name: "LogsOverviewPanel", props: { isOpen: { type: Boolean }, selected: {}, isReadOnly: { type: Boolean }, isCompact: { type: Boolean }, execution: {}, entries: {}, flatLogEntries: {}, latestNodeInfo: {} }, emits: ["clickHeader", "select", "clearExecutionData", "openNdv", "toggleExpanded"], setup(__props, { emit: __emit }) { const emit = __emit; const locale = useI18n$1(); const router = useRouter(); const runWorkflow = useRunWorkflow({ router }); const isClearExecutionButtonVisible = useClearExecutionButtonVisible(); const isEmpty2 = 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 consumedTokens2 = computed( () => getTotalConsumedTokens( ...__props.entries.map( (entry) => getSubtreeTotalConsumedTokens( entry, false // Exclude token usages from sub workflow which is loaded only after expanding the row ) ) ) ); const shouldShowTokenCountColumn = computed( () => consumedTokens2.value.totalTokens > 0 || __props.entries.some((entry) => getSubtreeTotalConsumedTokens(entry, true).totalTokens > 0) ); const isExpanded = computed( () => __props.flatLogEntries.reduce((acc, entry, index, arr) => { acc[entry.id] = arr[index + 1]?.parent?.id === entry.id; return acc; }, {}) ); const virtualList = useVirtualList( toRef(() => __props.flatLogEntries), { itemHeight: 32 } ); function handleSwitchView(value) { emit("select", value === "overview" ? void 0 : __props.flatLogEntries[0]); } async function handleTriggerPartialExecution(treeNode) { const latestName = __props.latestNodeInfo[treeNode.node.id]?.name ?? treeNode.node.name; if (latestName) { await runWorkflow.runWorkflow({ destinationNode: latestName }); } } watch( [() => __props.execution?.status === "running", () => __props.flatLogEntries.length], async ([isRunning, flatEntryCount], [wasRunning]) => { await nextTick(() => { if (__props.selected === void 0 && (isRunning || wasRunning)) { virtualList.scrollTo(flatEntryCount - 1); } }); }, { immediate: true } ); watch( () => __props.selected?.id, async (selectedId) => { await nextTick(() => { if (selectedId === void 0) { return; } const index = virtualList.list.value.some((e) => e.data.id === selectedId) ? -1 : __props.flatLogEntries.findIndex((e) => e.id === selectedId); if (index >= 0) { virtualList.scrollTo(index); } }); }, { immediate: true } ); return (_ctx, _cache) => { return openBlock(), createElementBlock("div", { class: normalizeClass([_ctx.$style.container, hasStaticScrollbar ? _ctx.$style.staticScrollBar : ""]), "data-test-id": "logs-overview" }, [ createVNode(LogsPanelHeader, { title: unref(locale).baseText("logs.overview.header.title"), "data-test-id": "logs-overview-header", onClick: _cache[1] || (_cache[1] = ($event) => emit("clickHeader")) }, { actions: withCtx(() => [ unref(isClearExecutionButtonVisible) ? (openBlock(), createBlock(unref(Tooltip), { key: 0, content: unref(locale).baseText("logs.overview.header.actions.clearExecution.tooltip") }, { default: withCtx(() => [ createVNode(unref(N8nButton), { 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"]), _ctx.isOpen ? (openBlock(), createElementBlock("div", { key: 0, class: normalizeClass([_ctx.$style.content, isEmpty2.value ? _ctx.$style.empty : ""]), "data-test-id": "logs-overview-body" }, [ isEmpty2.value || _ctx.execution === void 0 ? (openBlock(), createBlock(unref(N8nText), { 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, { "data-test-id": "logs-overview-status", class: normalizeClass(_ctx.$style.summary), status: _ctx.execution.status, "consumed-tokens": consumedTokens2.value, "start-time": +new Date(_ctx.execution.startedAt), "time-took": _ctx.execution.startedAt && _ctx.execution.stoppedAt ? +new Date(_ctx.execution.stoppedAt) - +new Date(_ctx.execution.startedAt) : void 0 }, null, 8, ["class", "status", "consumed-tokens", "start-time", "time-took"]), createBaseVNode("div", mergeProps({ class: _ctx.$style.tree }, unref(virtualList).containerProps), [ createBaseVNode("div", mergeProps(unref(virtualList).wrapperProps.value, { role: "tree" }), [ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(virtualList).list.value, ({ data, index }) => { return openBlock(), createBlock(LogsOverviewRow, { key: index, data, "is-read-only": _ctx.isReadOnly, "is-selected": data.id === _ctx.selected?.id, "is-compact": _ctx.isCompact, "should-show-token-count-column": shouldShowTokenCountColumn.value, "latest-info": _ctx.latestNodeInfo[data.node.id], expanded: isExpanded.value[data.id], "can-open-ndv": data.executionId === _ctx.execution?.id, onToggleExpanded: ($event) => emit("toggleExpanded", data), onOpenNdv: ($event) => emit("openNdv", data), onTriggerPartialExecution: ($event) => handleTriggerPartialExecution(data), onToggleSelected: ($event) => emit("select", _ctx.selected?.id === data.id ? void 0 : data) }, null, 8, ["data", "is-read-only", "is-selected", "is-compact", "should-show-token-count-column", "latest-info", "expanded", "can-open-ndv", "onToggleExpanded", "onOpenNdv", "onTriggerPartialExecution", "onToggleSelected"]); }), 128)) ], 16) ], 16), createVNode(unref(N8nRadioButtons), { 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); }; } }); const container$5 = "_container_pydb1_123"; const clearButton = "_clearButton_pydb1_133"; const content$1 = "_content_pydb1_139"; const empty = "_empty_pydb1_149"; const emptyText = "_emptyText_pydb1_154"; const summary = "_summary_pydb1_159"; const tree = "_tree_pydb1_163"; const staticScrollBar = "_staticScrollBar_pydb1_168"; const switchViewButtons = "_switchViewButtons_pydb1_193"; const style0$7 = { container: container$5, clearButton, content: content$1, empty, emptyText, summary, tree, staticScrollBar, switchViewButtons }; const cssModules$7 = { "$style": style0$7 }; const LogsOverviewPanel = /* @__PURE__ */ _export_sfc(_sfc_main$d, [["__cssModules", cssModules$7]]); function bash(hljs) { const regex = hljs.regex; const VAR = {}; const BRACED_VAR = { begin: /\$\{/, end: /\}/, contains: [ "self", { begin: /:-/, contains: [VAR] } // default values ] }; Object.assign(VAR, { className: "variable", variants: [ { begin: regex.concat( /\$[\w\d#@][\w\d_]*/, // negative look-ahead tries to avoid matching patterns that are not // Perl at all like $ident$, @ident@, etc. `(?![\\w\\d])(?![$])` ) }, BRACED_VAR ] }); const SUBST = { className: "subst", begin: /\$\(/, end: /\)/, contains: [hljs.BACKSLASH_ESCAPE] }; const COMMENT = hljs.inherit( hljs.COMMENT(), { match: [ /(^|\s)/, /#.*$/ ], scope: { 2: "comment" } } ); const HERE_DOC = { begin: /<<-?\s*(?=\w+)/, starts: { contains: [ hljs.END_SAME_AS_BEGIN({ begin: /(\w+)/, end: /(\w+)/, className: "string" }) ] } }; const QUOTE_STRING = { className: "string", begin: /"/, end: /"/, contains: [ hljs.BACKSLASH_ESCAPE, VAR, SUBST ] }; SUBST.contains.push(QUOTE_STRING); const ESCAPED_QUOTE = { match: /\\"/ }; const APOS_STRING = { className: "string", begin: /'/, end: /'/ }; const ESCAPED_APOS = { match: /\\'/ }; const ARITHMETIC = { begin: /\$?\(\(/, end: /\)\)/, contains: [ { begin: /\d+#[0-9a-f]+/, className: "number" }, hljs.NUMBER_MODE, VAR ] }; const SH_LIKE_SHELLS = [ "fish", "bash", "zsh", "sh", "csh", "ksh", "tcsh", "dash", "scsh" ]; const KNOWN_SHEBANG = hljs.SHEBANG({ binary: `(${SH_LIKE_SHELLS.join("|")})`, relevance: 10 }); const FUNCTION = { className: "function", begin: /\w[\w\d_]*\s*\(\s*\)\s*\{/, returnBegin: true, contains: [hljs.inherit(hljs.TITLE_MODE, { begin: /\w[\w\d_]*/ })], relevance: 0 }; const KEYWORDS2 = [ "if", "then", "else", "elif", "fi", "time", "for", "while", "until", "in", "do", "done", "case", "esac", "coproc", "function", "select" ]; const LITERALS2 = [ "true", "false" ]; const PATH_MODE = { match: /(\/[a-z._-]+)+/ }; const SHELL_BUILT_INS = [ "break", "cd", "continue", "eval", "exec", "exit", "export", "getopts", "hash", "pwd", "readonly", "return", "shift", "test", "times", "trap", "umask", "unset" ]; const BASH_BUILT_INS = [ "alias", "bind", "builtin", "caller", "command", "declare", "echo", "enable", "help", "let", "local", "logout", "mapfile", "printf", "read", "readarray", "source", "sudo", "type", "typeset", "ulimit", "unalias" ]; const ZSH_BUILT_INS = [ "autoload", "bg", "bindkey", "bye", "cap", "chdir", "clone", "comparguments", "compcall", "compctl", "compdescribe", "compfiles", "compgroups", "compquote", "comptags", "comptry", "compvalues", "dirs", "disable", "disown", "echotc", "echoti", "emulate", "fc", "fg", "float", "functions", "getcap", "getln", "history", "integer", "jobs", "kill", "limit", "log", "noglob", "popd", "print", "pushd", "pushln", "rehash", "sched", "setcap", "setopt", "stat", "suspend", "ttyctl", "unfunction", "unhash", "unlimit", "unsetopt", "vared", "wait", "whence", "where", "which", "zcompile", "zformat", "zftp", "zle", "zmodload", "zparseopts", "zprof", "zpty", "zregexparse", "zsocket", "zstyle", "ztcp" ]; const GNU_CORE_UTILS = [ "chcon", "chgrp", "chown", "chmod", "cp", "dd", "df", "dir", "dircolors", "ln", "ls", "mkdir", "mkfifo", "mknod", "mktemp", "mv", "realpath", "rm", "rmdir", "shred", "sync", "touch", "truncate", "vdir", "b2sum", "base32", "base64", "cat", "cksum", "comm", "csplit", "cut", "expand", "fmt", "fold", "head", "join", "md5sum", "nl", "numfmt", "od", "paste", "ptx", "pr", "sha1sum", "sha224sum", "sha256sum", "sha384sum", "sha512sum", "shuf", "sort", "split", "sum", "tac", "tail", "tr", "tsort", "unexpand", "uniq", "wc", "arch", "basename", "chroot", "date", "dirname", "du", "echo", "env", "expr", "factor", // "false", // keyword literal already "groups", "hostid", "id", "link", "logname", "nice", "nohup", "nproc", "pathchk", "pinky", "printenv", "printf", "pwd", "readlink", "runcon", "seq", "sleep", "stat", "stdbuf", "stty", "tee", "test", "timeout", // "true", // keyword literal already "tty", "uname", "unlink", "uptime", "users", "who", "whoami", "yes" ]; return { name: "Bash", aliases: [ "sh", "zsh" ], keywords: { $pattern: /\b[a-z][a-z0-9._-]+\b/, keyword: KEYWORDS2, literal: LITERALS2, built_in: [ ...SHELL_BUILT_INS, ...BASH_BUILT_INS, // Shell modifiers "set", "shopt", ...ZSH_BUILT_INS, ...GNU_CORE_UTILS ] }, contains: [ KNOWN_SHEBANG, // to catch known shells and boost relevancy hljs.SHEBANG(), // to catch unknown shells but still highlight the shebang FUNCTION, ARITHMETIC, COMMENT, HERE_DOC, PATH_MODE, QUOTE_STRING, ESCAPED_QUOTE, APOS_STRING, ESCAPED_APOS, VAR ] }; } const IDENT_RE$1 = "[A-Za-z$_][0-9A-Za-z$_]*"; const KEYWORDS$1 = [ "as", // for exports "in", "of", "if", "for", "while", "finally", "var", "new", "function", "do", "return", "void", "else", "break", "catch", "instanceof", "with", "throw", "case", "default", "try", "switch", "continue", "typeof", "delete", "let", "yield", "const", "class", // JS handles these with a special rule // "get", // "set", "debugger", "async", "await", "static", "import", "from", "export", "extends", // It's reached stage 3, which is "recommended for implementation": "using" ]; const LITERALS$1 = [ "true", "false", "null", "undefined", "NaN", "Infinity" ]; const TYPES$1 = [ // Fundamental objects "Object", "Function", "Boolean", "Symbol", // numbers and dates "Math", "Date", "Number", "BigInt", // text "String", "RegExp", // Indexed collections "Array", "Float32Array", "Float64Array", "Int8Array", "Uint8Array", "Uint8ClampedArray", "Int16Array", "Int32Array", "Uint16Array", "Uint32Array", "BigInt64Array", "BigUint64Array", // Keyed collections "Set", "Map", "WeakSet", "WeakMap", // Structured data "ArrayBuffer", "SharedArrayBuffer", "Atomics", "DataView", "JSON", // Control abstraction objects "Promise", "Generator", "GeneratorFunction", "AsyncFunction", // Reflection "Reflect", "Proxy", // Internationalization "Intl", // WebAssembly "WebAssembly" ]; const ERROR_TYPES$1 = [ "Error", "EvalError", "InternalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", "URIError" ]; const BUILT_IN_GLOBALS$1 = [ "setInterval", "setTimeout", "clearInterval", "clearTimeout", "require", "exports", "eval", "isFinite", "isNaN", "parseFloat", "parseInt", "decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent", "escape", "unescape" ]; const BUILT_IN_VARIABLES$1 = [ "arguments", "this", "super", "console", "window", "document", "localStorage", "sessionStorage", "module", "global" // Node.js ]; const BUILT_INS$1 = [].concat( BUILT_IN_GLOBALS$1, TYPES$1, ERROR_TYPES$1 ); function javascript$1(hljs) { const regex = hljs.regex; const hasClosingTag = (match, { after }) => { const tag = "", end: "" }; const XML_SELF_CLOSING = /<[A-Za-z0-9\\._:-]+\s*\/>/; const XML_TAG = { begin: /<[A-Za-z0-9\\._:-]+/, end: /\/[A-Za-z0-9\\._:-]+>|\/>/, /** * @param {RegExpMatchArray} match * @param {CallbackResponse} response */ isTrulyOpeningTag: (match, response) => { const afterMatchIndex = match[0].length + match.index; const nextChar = match.input[afterMatchIndex]; if ( // HTML should not include another raw `<` inside a tag // nested type? // `>`, etc. nextChar === "<" || // the , gives away that this is not HTML // `` nextChar === "," ) { response.ignoreMatch(); return; } if (nextChar === ">") { if (!hasClosingTag(match, { after: afterMatchIndex })) { response.ignoreMatch(); } } let m; const afterMatch = match.input.substring(afterMatchIndex); if (m = afterMatch.match(/^\s*=/)) { response.ignoreMatch(); return; } if (m = afterMatch.match(/^\s+extends\s+/)) { if (m.index === 0) { response.ignoreMatch(); return; } } } }; const KEYWORDS$1$1 = { $pattern: IDENT_RE$1, keyword: KEYWORDS$1, literal: LITERALS$1, built_in: BUILT_INS$1, "variable.language": BUILT_IN_VARIABLES$1 }; const decimalDigits = "[0-9](_?[0-9])*"; const frac = `\\.(${decimalDigits})`; const decimalInteger = `0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*`; const NUMBER = { className: "number", variants: [ // DecimalLiteral { begin: `(\\b(${decimalInteger})((${frac})|\\.)?|(${frac}))[eE][+-]?(${decimalDigits})\\b` }, { begin: `\\b(${decimalInteger})\\b((${frac})\\b|\\.)?|(${frac})\\b` }, // DecimalBigIntegerLiteral { begin: `\\b(0|[1-9](_?[0-9])*)n\\b` }, // NonDecimalIntegerLiteral { begin: "\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b" }, { begin: "\\b0[bB][0-1](_?[0-1])*n?\\b" }, { begin: "\\b0[oO][0-7](_?[0-7])*n?\\b" }, // LegacyOctalIntegerLiteral (does not include underscore separators) // https://tc39.es/ecma262/#sec-additional-syntax-numeric-literals { begin: "\\b0[0-7]+n?\\b" } ], relevance: 0 }; const SUBST = { className: "subst", begin: "\\$\\{", end: "\\}", keywords: KEYWORDS$1$1, contains: [] // defined later }; const HTML_TEMPLATE = { begin: ".?html`", end: "", starts: { end: "`", returnEnd: false, contains: [ hljs.BACKSLASH_ESCAPE, SUBST ], subLanguage: "xml" } }; const CSS_TEMPLATE = { begin: ".?css`", end: "", starts: { end: "`", returnEnd: false, contains: [ hljs.BACKSLASH_ESCAPE, SUBST ], subLanguage: "css" } }; const GRAPHQL_TEMPLATE = { begin: ".?gql`", end: "", starts: { end: "`", returnEnd: false, contains: [ hljs.BACKSLASH_ESCAPE, SUBST ], subLanguage: "graphql" } }; const TEMPLATE_STRING = { className: "string", begin: "`", end: "`", contains: [ hljs.BACKSLASH_ESCAPE, SUBST ] }; const JSDOC_COMMENT = hljs.COMMENT( /\/\*\*(?!\/)/, "\\*/", { relevance: 0, contains: [ { begin: "(?=@[A-Za-z]+)", relevance: 0, contains: [ { className: "doctag", begin: "@[A-Za-z]+" }, { className: "type", begin: "\\{", end: "\\}", excludeEnd: true, excludeBegin: true, relevance: 0 }, { className: "variable", begin: IDENT_RE$1$1 + "(?=\\s*(-)|$)", endsParent: true, relevance: 0 }, // eat spaces (not newlines) so we can find // types or variables { begin: /(?=[^\n])\s/, relevance: 0 } ] } ] } ); const COMMENT = { className: "comment", variants: [ JSDOC_COMMENT, hljs.C_BLOCK_COMMENT_MODE, hljs.C_LINE_COMMENT_MODE ] }; const SUBST_INTERNALS = [ hljs.APOS_STRING_MODE, hljs.QUOTE_STRING_MODE, HTML_TEMPLATE, CSS_TEMPLATE, GRAPHQL_TEMPLATE, TEMPLATE_STRING, // Skip numbers when they are part of a variable name { match: /\$\d+/ }, NUMBER // This is intentional: // See https://github.com/highlightjs/highlight.js/issues/3288 // hljs.REGEXP_MODE ]; SUBST.contains = SUBST_INTERNALS.concat({ // we need to pair up {} inside our subst to prevent // it from ending too early by matching another } begin: /\{/, end: /\}/, keywords: KEYWORDS$1$1, contains: [ "self" ].concat(SUBST_INTERNALS) }); const SUBST_AND_COMMENTS = [].concat(COMMENT, SUBST.contains); const PARAMS_CONTAINS = SUBST_AND_COMMENTS.concat([ // eat recursive parens in sub expressions { begin: /(\s*)\(/, end: /\)/, keywords: KEYWORDS$1$1, contains: ["self"].concat(SUBST_AND_COMMENTS) } ]); const PARAMS = { className: "params", // convert this to negative lookbehind in v12 begin: /(\s*)\(/, // to match the parms with end: /\)/, excludeBegin: true, excludeEnd: true, keywords: KEYWORDS$1$1, contains: PARAMS_CONTAINS }; const CLASS_OR_EXTENDS = { variants: [ // class Car extends vehicle { match: [ /class/, /\s+/, IDENT_RE$1$1, /\s+/, /extends/, /\s+/, regex.concat(IDENT_RE$1$1, "(", regex.concat(/\./, IDENT_RE$1$1), ")*") ], scope: { 1: "keyword", 3: "title.class", 5: "keyword", 7: "title.class.inherited" } }, // class Car { match: [ /class/, /\s+/, IDENT_RE$1$1 ], scope: { 1: "keyword", 3: "title.class" } } ] }; const CLASS_REFERENCE = { relevance: 0, match: regex.either( // Hard coded exceptions /\bJSON/, // Float32Array, OutT /\b[A-Z][a-z]+([A-Z][a-z]*|\d)*/, // CSSFactory, CSSFactoryT /\b[A-Z]{2,}([A-Z][a-z]+|\d)+([A-Z][a-z]*)*/, // FPs, FPsT /\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\d)*([A-Z][a-z]*)*/ // P // single letters are not highlighted // BLAH // this will be flagged as a UPPER_CASE_CONSTANT instead ), className: "title.class", keywords: { _: [ // se we still get relevance credit for JS library classes ...TYPES$1, ...ERROR_TYPES$1 ] } }; const USE_STRICT = { label: "use_strict", className: "meta", relevance: 10, begin: /^\s*['"]use (strict|asm)['"]/ }; const FUNCTION_DEFINITION = { variants: [ { match: [ /function/, /\s+/, IDENT_RE$1$1, /(?=\s*\()/ ] }, // anonymous function { match: [ /function/, /\s*(?=\()/ ] } ], className: { 1: "keyword", 3: "title.function" }, label: "func.def", contains: [PARAMS], illegal: /%/ }; const UPPER_CASE_CONSTANT = { relevance: 0, match: /\b[A-Z][A-Z_0-9]+\b/, className: "variable.constant" }; function noneOf(list) { return regex.concat("(?!", list.join("|"), ")"); } const FUNCTION_CALL = { match: regex.concat( /\b/, noneOf([ ...BUILT_IN_GLOBALS$1, "super", "import" ].map((x) => `${x}\\s*\\(`)), IDENT_RE$1$1, regex.lookahead(/\s*\(/) ), className: "title.function", relevance: 0 }; const PROPERTY_ACCESS = { begin: regex.concat(/\./, regex.lookahead( regex.concat(IDENT_RE$1$1, /(?![0-9A-Za-z$_(])/) )), end: IDENT_RE$1$1, excludeBegin: true, keywords: "prototype", className: "property", relevance: 0 }; const GETTER_OR_SETTER = { match: [ /get|set/, /\s+/, IDENT_RE$1$1, /(?=\()/ ], className: { 1: "keyword", 3: "title.function" }, contains: [ { // eat to avoid empty params begin: /\(\)/ }, PARAMS ] }; const FUNC_LEAD_IN_RE = "(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|" + hljs.UNDERSCORE_IDENT_RE + ")\\s*=>"; const FUNCTION_VARIABLE = { match: [ /const|var|let/, /\s+/, IDENT_RE$1$1, /\s*/, /=\s*/, /(async\s*)?/, // async is optional regex.lookahead(FUNC_LEAD_IN_RE) ], keywords: "async", className: { 1: "keyword", 3: "title.function" }, contains: [ PARAMS ] }; return { name: "JavaScript", aliases: ["js", "jsx", "mjs", "cjs"], keywords: KEYWORDS$1$1, // this will be extended by TypeScript exports: { PARAMS_CONTAINS, CLASS_REFERENCE }, illegal: /#(?![$_A-z])/, contains: [ hljs.SHEBANG({ label: "shebang", binary: "node", relevance: 5 }), USE_STRICT, hljs.APOS_STRING_MODE, hljs.QUOTE_STRING_MODE, HTML_TEMPLATE, CSS_TEMPLATE, GRAPHQL_TEMPLATE, TEMPLATE_STRING, COMMENT, // Skip numbers when they are part of a variable name { match: /\$\d+/ }, NUMBER, CLASS_REFERENCE, { scope: "attr", match: IDENT_RE$1$1 + regex.lookahead(":"), relevance: 0 }, FUNCTION_VARIABLE, { // "value" container begin: "(" + hljs.RE_STARTERS_RE + "|\\b(case|return|throw)\\b)\\s*", keywords: "return throw case", relevance: 0, contains: [ COMMENT, hljs.REGEXP_MODE, { className: "function", // we have to count the parens to make sure we actually have the // correct bounding ( ) before the =>. There could be any number of // sub-expressions inside also surrounded by parens. begin: FUNC_LEAD_IN_RE, returnBegin: true, end: "\\s*=>", contains: [ { className: "params", variants: [ { begin: hljs.UNDERSCORE_IDENT_RE, relevance: 0 }, { className: null, begin: /\(\s*\)/, skip: true }, { begin: /(\s*)\(/, end: /\)/, excludeBegin: true, excludeEnd: true, keywords: KEYWORDS$1$1, contains: PARAMS_CONTAINS } ] } ] }, { // could be a comma delimited list of params to a function call begin: /,/, relevance: 0 }, { match: /\s+/, relevance: 0 }, { // JSX variants: [ { begin: FRAGMENT.begin, end: FRAGMENT.end }, { match: XML_SELF_CLOSING }, { begin: XML_TAG.begin, // we carefully check the opening tag to see if it truly // is a tag and not a false positive "on:begin": XML_TAG.isTrulyOpeningTag, end: XML_TAG.end } ], subLanguage: "xml", contains: [ { begin: XML_TAG.begin, end: XML_TAG.end, skip: true, contains: ["self"] } ] } ] }, FUNCTION_DEFINITION, { // prevent this from getting swallowed up by function // since they appear "function like" beginKeywords: "while if switch catch for" }, { // we have to count the parens to make sure we actually have the correct // bounding ( ). There could be any number of sub-expressions inside // also surrounded by parens. begin: "\\b(?!function)" + hljs.UNDERSCORE_IDENT_RE + "\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", // end parens returnBegin: true, label: "func.def", contains: [ PARAMS, hljs.inherit(hljs.TITLE_MODE, { begin: IDENT_RE$1$1, className: "title.function" }) ] }, // catch ... so it won't trigger the property rule below { match: /\.\.\./, relevance: 0 }, PROPERTY_ACCESS, // hack: prevents detection of keywords in some circumstances // .keyword() // $keyword = x { match: "\\$" + IDENT_RE$1$1, relevance: 0 }, { match: [/\bconstructor(?=\s*\()/], className: { 1: "title.function" }, contains: [PARAMS] }, FUNCTION_CALL, UPPER_CASE_CONSTANT, CLASS_OR_EXTENDS, GETTER_OR_SETTER, { match: /\$[(.]/ // relevance booster for a pattern common to JS libs: `$(something)` and `$.something` } ] }; } function python(hljs) { const regex = hljs.regex; const IDENT_RE2 = new RegExp("[\\p{XID_Start}_]\\p{XID_Continue}*", "u"); const RESERVED_WORDS = [ "and", "as", "assert", "async", "await", "break", "case", "class", "continue", "def", "del", "elif", "else", "except", "finally", "for", "from", "global", "if", "import", "in", "is", "lambda", "match", "nonlocal|10", "not", "or", "pass", "raise", "return", "try", "while", "with", "yield" ]; const BUILT_INS2 = [ "__import__", "abs", "all", "any", "ascii", "bin", "bool", "breakpoint", "bytearray", "bytes", "callable", "chr", "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod", "enumerate", "eval", "exec", "filter", "float", "format", "frozenset", "getattr", "globals", "hasattr", "hash", "help", "hex", "id", "input", "int", "isinstance", "issubclass", "iter", "len", "list", "locals", "map", "max", "memoryview", "min", "next", "object", "oct", "open", "ord", "pow", "print", "property", "range", "repr", "reversed", "round", "set", "setattr", "slice", "sorted", "staticmethod", "str", "sum", "super", "tuple", "type", "vars", "zip" ]; const LITERALS2 = [ "__debug__", "Ellipsis", "False", "None", "NotImplemented", "True" ]; const TYPES2 = [ "Any", "Callable", "Coroutine", "Dict", "List", "Literal", "Generic", "Optional", "Sequence", "Set", "Tuple", "Type", "Union" ]; const KEYWORDS2 = { $pattern: /[A-Za-z]\w+|__\w+__/, keyword: RESERVED_WORDS, built_in: BUILT_INS2, literal: LITERALS2, type: TYPES2 }; const PROMPT = { className: "meta", begin: /^(>>>|\.\.\.) / }; const SUBST = { className: "subst", begin: /\{/, end: /\}/, keywords: KEYWORDS2, illegal: /#/ }; const LITERAL_BRACKET = { begin: /\{\{/, relevance: 0 }; const STRING = { className: "string", contains: [hljs.BACKSLASH_ESCAPE], variants: [ { begin: /([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/, end: /'''/, contains: [ hljs.BACKSLASH_ESCAPE, PROMPT ], relevance: 10 }, { begin: /([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/, end: /"""/, contains: [ hljs.BACKSLASH_ESCAPE, PROMPT ], relevance: 10 }, { begin: /([fF][rR]|[rR][fF]|[fF])'''/, end: /'''/, contains: [ hljs.BACKSLASH_ESCAPE, PROMPT, LITERAL_BRACKET, SUBST ] }, { begin: /([fF][rR]|[rR][fF]|[fF])"""/, end: /"""/, contains: [ hljs.BACKSLASH_ESCAPE, PROMPT, LITERAL_BRACKET, SUBST ] }, { begin: /([uU]|[rR])'/, end: /'/, relevance: 10 }, { begin: /([uU]|[rR])"/, end: /"/, relevance: 10 }, { begin: /([bB]|[bB][rR]|[rR][bB])'/, end: /'/ }, { begin: /([bB]|[bB][rR]|[rR][bB])"/, end: /"/ }, { begin: /([fF][rR]|[rR][fF]|[fF])'/, end: /'/, contains: [ hljs.BACKSLASH_ESCAPE, LITERAL_BRACKET, SUBST ] }, { begin: /([fF][rR]|[rR][fF]|[fF])"/, end: /"/, contains: [ hljs.BACKSLASH_ESCAPE, LITERAL_BRACKET, SUBST ] }, hljs.APOS_STRING_MODE, hljs.QUOTE_STRING_MODE ] }; const digitpart = "[0-9](_?[0-9])*"; const pointfloat = `(\\b(${digitpart}))?\\.(${digitpart})|\\b(${digitpart})\\.`; const lookahead = `\\b|${RESERVED_WORDS.join("|")}`; const NUMBER = { className: "number", relevance: 0, variants: [ // exponentfloat, pointfloat // https://docs.python.org/3.9/reference/lexical_analysis.html#floating-point-literals // optionally imaginary // https://docs.python.org/3.9/reference/lexical_analysis.html#imaginary-literals // Note: no leading \b because floats can start with a decimal point // and we don't want to mishandle e.g. `fn(.5)`, // no trailing \b for pointfloat because it can end with a decimal point // and we don't want to mishandle e.g. `0..hex()`; this should be safe // because both MUST contain a decimal point and so cannot be confused with // the interior part of an identifier { begin: `(\\b(${digitpart})|(${pointfloat}))[eE][+-]?(${digitpart})[jJ]?(?=${lookahead})` }, { begin: `(${pointfloat})[jJ]?` }, // decinteger, bininteger, octinteger, hexinteger // https://docs.python.org/3.9/reference/lexical_analysis.html#integer-literals // optionally "long" in Python 2 // https://docs.python.org/2.7/reference/lexical_analysis.html#integer-and-long-integer-literals // decinteger is optionally imaginary // https://docs.python.org/3.9/reference/lexical_analysis.html#imaginary-literals { begin: `\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${lookahead})` }, { begin: `\\b0[bB](_?[01])+[lL]?(?=${lookahead})` }, { begin: `\\b0[oO](_?[0-7])+[lL]?(?=${lookahead})` }, { begin: `\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${lookahead})` }, // imagnumber (digitpart-based) // https://docs.python.org/3.9/reference/lexical_analysis.html#imaginary-literals { begin: `\\b(${digitpart})[jJ](?=${lookahead})` } ] }; const COMMENT_TYPE = { className: "comment", begin: regex.lookahead(/# type:/), end: /$/, keywords: KEYWORDS2, contains: [ { // prevent keywords from coloring `type` begin: /# type:/ }, // comment within a datatype comment includes no keywords { begin: /#/, end: /\b\B/, endsWithParent: true } ] }; const PARAMS = { className: "params", variants: [ // Exclude params in functions without params { className: "", begin: /\(\s*\)/, skip: true }, { begin: /\(/, end: /\)/, excludeBegin: true, excludeEnd: true, keywords: KEYWORDS2, contains: [ "self", PROMPT, NUMBER, STRING, hljs.HASH_COMMENT_MODE ] } ] }; SUBST.contains = [ STRING, NUMBER, PROMPT ]; return { name: "Python", aliases: [ "py", "gyp", "ipython" ], unicodeRegex: true, keywords: KEYWORDS2, illegal: /(<\/|\?)|=>/, contains: [ PROMPT, NUMBER, { // very common convention scope: "variable.language", match: /\bself\b/ }, { // eat "if" prior to string so that it won't accidentally be // labeled as an f-string beginKeywords: "if", relevance: 0 }, { match: /\bor\b/, scope: "keyword" }, STRING, COMMENT_TYPE, hljs.HASH_COMMENT_MODE, { match: [ /\bdef/, /\s+/, IDENT_RE2 ], scope: { 1: "keyword", 3: "title.function" }, contains: [PARAMS] }, { variants: [ { match: [ /\bclass/, /\s+/, IDENT_RE2, /\s*/, /\(\s*/, IDENT_RE2, /\s*\)/ ] }, { match: [ /\bclass/, /\s+/, IDENT_RE2 ] } ], scope: { 1: "keyword", 3: "title.class", 6: "title.class.inherited" } }, { className: "meta", begin: /^[\t ]*@/, end: /(?=#)|$/, contains: [ NUMBER, PARAMS, STRING ] } ] }; } const IDENT_RE = "[A-Za-z$_][0-9A-Za-z$_]*"; const KEYWORDS = [ "as", // for exports "in", "of", "if", "for", "while", "finally", "var", "new", "function", "do", "return", "void", "else", "break", "catch", "instanceof", "with", "throw", "case", "default", "try", "switch", "continue", "typeof", "delete", "let", "yield", "const", "class", // JS handles these with a special rule // "get", // "set", "debugger", "async", "await", "static", "import", "from", "export", "extends", // It's reached stage 3, which is "recommended for implementation": "using" ]; const LITERALS = [ "true", "false", "null", "undefined", "NaN", "Infinity" ]; const TYPES = [ // Fundamental objects "Object", "Function", "Boolean", "Symbol", // numbers and dates "Math", "Date", "Number", "BigInt", // text "String", "RegExp", // Indexed collections "Array", "Float32Array", "Float64Array", "Int8Array", "Uint8Array", "Uint8ClampedArray", "Int16Array", "Int32Array", "Uint16Array", "Uint32Array", "BigInt64Array", "BigUint64Array", // Keyed collections "Set", "Map", "WeakSet", "WeakMap", // Structured data "ArrayBuffer", "SharedArrayBuffer", "Atomics", "DataView", "JSON", // Control abstraction objects "Promise", "Generator", "GeneratorFunction", "AsyncFunction", // Reflection "Reflect", "Proxy", // Internationalization "Intl", // WebAssembly "WebAssembly" ]; const ERROR_TYPES = [ "Error", "EvalError", "InternalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", "URIError" ]; const BUILT_IN_GLOBALS = [ "setInterval", "setTimeout", "clearInterval", "clearTimeout", "require", "exports", "eval", "isFinite", "isNaN", "parseFloat", "parseInt", "decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent", "escape", "unescape" ]; const BUILT_IN_VARIABLES = [ "arguments", "this", "super", "console", "window", "document", "localStorage", "sessionStorage", "module", "global" // Node.js ]; const BUILT_INS = [].concat( BUILT_IN_GLOBALS, TYPES, ERROR_TYPES ); function javascript(hljs) { const regex = hljs.regex; const hasClosingTag = (match, { after }) => { const tag = "", end: "" }; const XML_SELF_CLOSING = /<[A-Za-z0-9\\._:-]+\s*\/>/; const XML_TAG = { begin: /<[A-Za-z0-9\\._:-]+/, end: /\/[A-Za-z0-9\\._:-]+>|\/>/, /** * @param {RegExpMatchArray} match * @param {CallbackResponse} response */ isTrulyOpeningTag: (match, response) => { const afterMatchIndex = match[0].length + match.index; const nextChar = match.input[afterMatchIndex]; if ( // HTML should not include another raw `<` inside a tag // nested type? // `>`, etc. nextChar === "<" || // the , gives away that this is not HTML // `` nextChar === "," ) { response.ignoreMatch(); return; } if (nextChar === ">") { if (!hasClosingTag(match, { after: afterMatchIndex })) { response.ignoreMatch(); } } let m; const afterMatch = match.input.substring(afterMatchIndex); if (m = afterMatch.match(/^\s*=/)) { response.ignoreMatch(); return; } if (m = afterMatch.match(/^\s+extends\s+/)) { if (m.index === 0) { response.ignoreMatch(); return; } } } }; const KEYWORDS$12 = { $pattern: IDENT_RE, keyword: KEYWORDS, literal: LITERALS, built_in: BUILT_INS, "variable.language": BUILT_IN_VARIABLES }; const decimalDigits = "[0-9](_?[0-9])*"; const frac = `\\.(${decimalDigits})`; const decimalInteger = `0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*`; const NUMBER = { className: "number", variants: [ // DecimalLiteral { begin: `(\\b(${decimalInteger})((${frac})|\\.)?|(${frac}))[eE][+-]?(${decimalDigits})\\b` }, { begin: `\\b(${decimalInteger})\\b((${frac})\\b|\\.)?|(${frac})\\b` }, // DecimalBigIntegerLiteral { begin: `\\b(0|[1-9](_?[0-9])*)n\\b` }, // NonDecimalIntegerLiteral { begin: "\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b" }, { begin: "\\b0[bB][0-1](_?[0-1])*n?\\b" }, { begin: "\\b0[oO][0-7](_?[0-7])*n?\\b" }, // LegacyOctalIntegerLiteral (does not include underscore separators) // https://tc39.es/ecma262/#sec-additional-syntax-numeric-literals { begin: "\\b0[0-7]+n?\\b" } ], relevance: 0 }; const SUBST = { className: "subst", begin: "\\$\\{", end: "\\}", keywords: KEYWORDS$12, contains: [] // defined later }; const HTML_TEMPLATE = { begin: ".?html`", end: "", starts: { end: "`", returnEnd: false, contains: [ hljs.BACKSLASH_ESCAPE, SUBST ], subLanguage: "xml" } }; const CSS_TEMPLATE = { begin: ".?css`", end: "", starts: { end: "`", returnEnd: false, contains: [ hljs.BACKSLASH_ESCAPE, SUBST ], subLanguage: "css" } }; const GRAPHQL_TEMPLATE = { begin: ".?gql`", end: "", starts: { end: "`", returnEnd: false, contains: [ hljs.BACKSLASH_ESCAPE, SUBST ], subLanguage: "graphql" } }; const TEMPLATE_STRING = { className: "string", begin: "`", end: "`", contains: [ hljs.BACKSLASH_ESCAPE, SUBST ] }; const JSDOC_COMMENT = hljs.COMMENT( /\/\*\*(?!\/)/, "\\*/", { relevance: 0, contains: [ { begin: "(?=@[A-Za-z]+)", relevance: 0, contains: [ { className: "doctag", begin: "@[A-Za-z]+" }, { className: "type", begin: "\\{", end: "\\}", excludeEnd: true, excludeBegin: true, relevance: 0 }, { className: "variable", begin: IDENT_RE$12 + "(?=\\s*(-)|$)", endsParent: true, relevance: 0 }, // eat spaces (not newlines) so we can find // types or variables { begin: /(?=[^\n])\s/, relevance: 0 } ] } ] } ); const COMMENT = { className: "comment", variants: [ JSDOC_COMMENT, hljs.C_BLOCK_COMMENT_MODE, hljs.C_LINE_COMMENT_MODE ] }; const SUBST_INTERNALS = [ hljs.APOS_STRING_MODE, hljs.QUOTE_STRING_MODE, HTML_TEMPLATE, CSS_TEMPLATE, GRAPHQL_TEMPLATE, TEMPLATE_STRING, // Skip numbers when they are part of a variable name { match: /\$\d+/ }, NUMBER // This is intentional: // See https://github.com/highlightjs/highlight.js/issues/3288 // hljs.REGEXP_MODE ]; SUBST.contains = SUBST_INTERNALS.concat({ // we need to pair up {} inside our subst to prevent // it from ending too early by matching another } begin: /\{/, end: /\}/, keywords: KEYWORDS$12, contains: [ "self" ].concat(SUBST_INTERNALS) }); const SUBST_AND_COMMENTS = [].concat(COMMENT, SUBST.contains); const PARAMS_CONTAINS = SUBST_AND_COMMENTS.concat([ // eat recursive parens in sub expressions { begin: /(\s*)\(/, end: /\)/, keywords: KEYWORDS$12, contains: ["self"].concat(SUBST_AND_COMMENTS) } ]); const PARAMS = { className: "params", // convert this to negative lookbehind in v12 begin: /(\s*)\(/, // to match the parms with end: /\)/, excludeBegin: true, excludeEnd: true, keywords: KEYWORDS$12, contains: PARAMS_CONTAINS }; const CLASS_OR_EXTENDS = { variants: [ // class Car extends vehicle { match: [ /class/, /\s+/, IDENT_RE$12, /\s+/, /extends/, /\s+/, regex.concat(IDENT_RE$12, "(", regex.concat(/\./, IDENT_RE$12), ")*") ], scope: { 1: "keyword", 3: "title.class", 5: "keyword", 7: "title.class.inherited" } }, // class Car { match: [ /class/, /\s+/, IDENT_RE$12 ], scope: { 1: "keyword", 3: "title.class" } } ] }; const CLASS_REFERENCE = { relevance: 0, match: regex.either( // Hard coded exceptions /\bJSON/, // Float32Array, OutT /\b[A-Z][a-z]+([A-Z][a-z]*|\d)*/, // CSSFactory, CSSFactoryT /\b[A-Z]{2,}([A-Z][a-z]+|\d)+([A-Z][a-z]*)*/, // FPs, FPsT /\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\d)*([A-Z][a-z]*)*/ // P // single letters are not highlighted // BLAH // this will be flagged as a UPPER_CASE_CONSTANT instead ), className: "title.class", keywords: { _: [ // se we still get relevance credit for JS library classes ...TYPES, ...ERROR_TYPES ] } }; const USE_STRICT = { label: "use_strict", className: "meta", relevance: 10, begin: /^\s*['"]use (strict|asm)['"]/ }; const FUNCTION_DEFINITION = { variants: [ { match: [ /function/, /\s+/, IDENT_RE$12, /(?=\s*\()/ ] }, // anonymous function { match: [ /function/, /\s*(?=\()/ ] } ], className: { 1: "keyword", 3: "title.function" }, label: "func.def", contains: [PARAMS], illegal: /%/ }; const UPPER_CASE_CONSTANT = { relevance: 0, match: /\b[A-Z][A-Z_0-9]+\b/, className: "variable.constant" }; function noneOf(list) { return regex.concat("(?!", list.join("|"), ")"); } const FUNCTION_CALL = { match: regex.concat( /\b/, noneOf([ ...BUILT_IN_GLOBALS, "super", "import" ].map((x) => `${x}\\s*\\(`)), IDENT_RE$12, regex.lookahead(/\s*\(/) ), className: "title.function", relevance: 0 }; const PROPERTY_ACCESS = { begin: regex.concat(/\./, regex.lookahead( regex.concat(IDENT_RE$12, /(?![0-9A-Za-z$_(])/) )), end: IDENT_RE$12, excludeBegin: true, keywords: "prototype", className: "property", relevance: 0 }; const GETTER_OR_SETTER = { match: [ /get|set/, /\s+/, IDENT_RE$12, /(?=\()/ ], className: { 1: "keyword", 3: "title.function" }, contains: [ { // eat to avoid empty params begin: /\(\)/ }, PARAMS ] }; const FUNC_LEAD_IN_RE = "(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|" + hljs.UNDERSCORE_IDENT_RE + ")\\s*=>"; const FUNCTION_VARIABLE = { match: [ /const|var|let/, /\s+/, IDENT_RE$12, /\s*/, /=\s*/, /(async\s*)?/, // async is optional regex.lookahead(FUNC_LEAD_IN_RE) ], keywords: "async", className: { 1: "keyword", 3: "title.function" }, contains: [ PARAMS ] }; return { name: "JavaScript", aliases: ["js", "jsx", "mjs", "cjs"], keywords: KEYWORDS$12, // this will be extended by TypeScript exports: { PARAMS_CONTAINS, CLASS_REFERENCE }, illegal: /#(?![$_A-z])/, contains: [ hljs.SHEBANG({ label: "shebang", binary: "node", relevance: 5 }), USE_STRICT, hljs.APOS_STRING_MODE, hljs.QUOTE_STRING_MODE, HTML_TEMPLATE, CSS_TEMPLATE, GRAPHQL_TEMPLATE, TEMPLATE_STRING, COMMENT, // Skip numbers when they are part of a variable name { match: /\$\d+/ }, NUMBER, CLASS_REFERENCE, { scope: "attr", match: IDENT_RE$12 + regex.lookahead(":"), relevance: 0 }, FUNCTION_VARIABLE, { // "value" container begin: "(" + hljs.RE_STARTERS_RE + "|\\b(case|return|throw)\\b)\\s*", keywords: "return throw case", relevance: 0, contains: [ COMMENT, hljs.REGEXP_MODE, { className: "function", // we have to count the parens to make sure we actually have the // correct bounding ( ) before the =>. There could be any number of // sub-expressions inside also surrounded by parens. begin: FUNC_LEAD_IN_RE, returnBegin: true, end: "\\s*=>", contains: [ { className: "params", variants: [ { begin: hljs.UNDERSCORE_IDENT_RE, relevance: 0 }, { className: null, begin: /\(\s*\)/, skip: true }, { begin: /(\s*)\(/, end: /\)/, excludeBegin: true, excludeEnd: true, keywords: KEYWORDS$12, contains: PARAMS_CONTAINS } ] } ] }, { // could be a comma delimited list of params to a function call begin: /,/, relevance: 0 }, { match: /\s+/, relevance: 0 }, { // JSX variants: [ { begin: FRAGMENT.begin, end: FRAGMENT.end }, { match: XML_SELF_CLOSING }, { begin: XML_TAG.begin, // we carefully check the opening tag to see if it truly // is a tag and not a false positive "on:begin": XML_TAG.isTrulyOpeningTag, end: XML_TAG.end } ], subLanguage: "xml", contains: [ { begin: XML_TAG.begin, end: XML_TAG.end, skip: true, contains: ["self"] } ] } ] }, FUNCTION_DEFINITION, { // prevent this from getting swallowed up by function // since they appear "function like" beginKeywords: "while if switch catch for" }, { // we have to count the parens to make sure we actually have the correct // bounding ( ). There could be any number of sub-expressions inside // also surrounded by parens. begin: "\\b(?!function)" + hljs.UNDERSCORE_IDENT_RE + "\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", // end parens returnBegin: true, label: "func.def", contains: [ PARAMS, hljs.inherit(hljs.TITLE_MODE, { begin: IDENT_RE$12, className: "title.function" }) ] }, // catch ... so it won't trigger the property rule below { match: /\.\.\./, relevance: 0 }, PROPERTY_ACCESS, // hack: prevents detection of keywords in some circumstances // .keyword() // $keyword = x { match: "\\$" + IDENT_RE$12, relevance: 0 }, { match: [/\bconstructor(?=\s*\()/], className: { 1: "title.function" }, contains: [PARAMS] }, FUNCTION_CALL, UPPER_CASE_CONSTANT, CLASS_OR_EXTENDS, GETTER_OR_SETTER, { match: /\$[(.]/ // relevance booster for a pattern common to JS libs: `$(something)` and `$.something` } ] }; } function typescript(hljs) { const regex = hljs.regex; const tsLanguage = javascript(hljs); const IDENT_RE$12 = IDENT_RE; const TYPES2 = [ "any", "void", "number", "boolean", "string", "object", "never", "symbol", "bigint", "unknown" ]; const NAMESPACE = { begin: [ /namespace/, /\s+/, hljs.IDENT_RE ], beginScope: { 1: "keyword", 3: "title.class" } }; const INTERFACE = { beginKeywords: "interface", end: /\{/, excludeEnd: true, keywords: { keyword: "interface extends", built_in: TYPES2 }, contains: [tsLanguage.exports.CLASS_REFERENCE] }; const USE_STRICT = { className: "meta", relevance: 10, begin: /^\s*['"]use strict['"]/ }; const TS_SPECIFIC_KEYWORDS = [ "type", // "namespace", "interface", "public", "private", "protected", "implements", "declare", "abstract", "readonly", "enum", "override", "satisfies" ]; const KEYWORDS$12 = { $pattern: IDENT_RE, keyword: KEYWORDS.concat(TS_SPECIFIC_KEYWORDS), literal: LITERALS, built_in: BUILT_INS.concat(TYPES2), "variable.language": BUILT_IN_VARIABLES }; const DECORATOR = { className: "meta", begin: "@" + IDENT_RE$12 }; const swapMode = (mode, label, replacement) => { const indx = mode.contains.findIndex((m) => m.label === label); if (indx === -1) { throw new Error("can not find mode to replace"); } mode.contains.splice(indx, 1, replacement); }; Object.assign(tsLanguage.keywords, KEYWORDS$12); tsLanguage.exports.PARAMS_CONTAINS.push(DECORATOR); const ATTRIBUTE_HIGHLIGHT = tsLanguage.contains.find((c) => c.scope === "attr"); const OPTIONAL_KEY_OR_ARGUMENT = Object.assign( {}, ATTRIBUTE_HIGHLIGHT, { match: regex.concat(IDENT_RE$12, regex.lookahead(/\s*\?:/)) } ); tsLanguage.exports.PARAMS_CONTAINS.push([ tsLanguage.exports.CLASS_REFERENCE, // class reference for highlighting the params types ATTRIBUTE_HIGHLIGHT, // highlight the params key OPTIONAL_KEY_OR_ARGUMENT // Added for optional property assignment highlighting ]); tsLanguage.contains = tsLanguage.contains.concat([ DECORATOR, NAMESPACE, INTERFACE, OPTIONAL_KEY_OR_ARGUMENT // Added for optional property assignment highlighting ]); swapMode(tsLanguage, "shebang", hljs.SHEBANG()); swapMode(tsLanguage, "use_strict", USE_STRICT); const functionDeclaration = tsLanguage.contains.find((m) => m.label === "func.def"); functionDeclaration.relevance = 0; Object.assign(tsLanguage, { name: "TypeScript", aliases: [ "ts", "tsx", "mts", "cts" ] }); return tsLanguage; } function xml(hljs) { const regex = hljs.regex; const TAG_NAME_RE = regex.concat(/[\p{L}_]/u, regex.optional(/[\p{L}0-9_.-]*:/u), /[\p{L}0-9_.-]*/u); const XML_IDENT_RE = /[\p{L}0-9._:-]+/u; const XML_ENTITIES = { className: "symbol", begin: /&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/ }; const XML_META_KEYWORDS = { begin: /\s/, contains: [ { className: "keyword", begin: /#?[a-z_][a-z1-9_-]+/, illegal: /\n/ } ] }; const XML_META_PAR_KEYWORDS = hljs.inherit(XML_META_KEYWORDS, { begin: /\(/, end: /\)/ }); const APOS_META_STRING_MODE = hljs.inherit(hljs.APOS_STRING_MODE, { className: "string" }); const QUOTE_META_STRING_MODE = hljs.inherit(hljs.QUOTE_STRING_MODE, { className: "string" }); const TAG_INTERNALS = { endsWithParent: true, illegal: /`]+/ } ] } ] } ] }; return { name: "HTML, XML", aliases: [ "html", "xhtml", "rss", "atom", "xjb", "xsd", "xsl", "plist", "wsf", "svg" ], case_insensitive: true, unicodeRegex: true, contains: [ { className: "meta", begin: //, relevance: 10, contains: [ XML_META_KEYWORDS, QUOTE_META_STRING_MODE, APOS_META_STRING_MODE, XML_META_PAR_KEYWORDS, { begin: /\[/, end: /\]/, contains: [ { className: "meta", begin: //, contains: [ XML_META_KEYWORDS, XML_META_PAR_KEYWORDS, QUOTE_META_STRING_MODE, APOS_META_STRING_MODE ] } ] } ] }, hljs.COMMENT( //, { relevance: 10 } ), { begin: //, relevance: 10 }, XML_ENTITIES, // xml processing instructions { className: "meta", end: /\?>/, variants: [ { begin: /<\?xml/, relevance: 10, contains: [ QUOTE_META_STRING_MODE ] }, { begin: /<\?[a-z][a-z0-9]+/ } ] }, { className: "tag", /* The lookahead pattern (?=...) ensures that 'begin' only matches ')/, end: />/, keywords: { name: "style" }, contains: [TAG_INTERNALS], starts: { end: /<\/style>/, returnEnd: true, subLanguage: [ "css", "xml" ] } }, { className: "tag", // See the comment in the