UNPKG

bpmn-js-markdown-documentation-panel

Version:

A comprehensive documentation management plugin for Camunda Modeler with markdown support, element linking, and coverage tracking

2,033 lines 67.4 kB
import { marked } from "marked";

//#region ../../node_modules/.pnpm/bpmn-js@13.2.2/node_modules/bpmn-js/lib/util/ModelUtil.js
/**
* @typedef { import('../model/Types').Element } Element
* @typedef { import('../model/Types').ModdleElement } ModdleElement
*/
/**
* Is an element of the given BPMN type?
*
* @param  {Element|ModdleElement} element
* @param  {string} type
*
* @return {boolean}
*/
function is(element, type) {
	var bo = getBusinessObject(element);
	return bo && typeof bo.$instanceOf === "function" && bo.$instanceOf(type);
}
/**
* Return the business object for a given element.
*
* @param {Element|ModdleElement} element
*
* @return {ModdleElement}
*/
function getBusinessObject(element) {
	return element && element.businessObject || element;
}

//#endregion
//#region src/extension/managers/AutocompleteManager.ts
var AutocompleteManager = class {
	_callbacks;
	_selectedIndex = -1;
	constructor(options) {
		this._callbacks = options.callbacks;
	}
	setupAutocompleteEventListeners() {
		setTimeout(() => {
			const textarea = document.getElementById("doc-textarea");
			if (!textarea) return;
			textarea.addEventListener("input", () => {
				this.handleAutocomplete();
			});
			textarea.addEventListener("keydown", (event) => {
				this._handleAutocompleteKeydown(event);
			});
			document.addEventListener("click", (event) => {
				const dropdown = document.getElementById("autocomplete-dropdown");
				const textarea$1 = document.getElementById("doc-textarea");
				if (dropdown && !(event.target instanceof Node && dropdown.contains(event.target)) && event.target !== textarea$1) this.hideAutocomplete();
			});
		}, 100);
	}
	handleAutocomplete() {
		const textarea = document.getElementById("doc-textarea");
		if (!textarea) return;
		const cursorPos = textarea.selectionStart;
		const text = textarea.value;
		let hashPos = -1;
		for (let i = cursorPos - 1; i >= 0; i--) {
			if (text[i] === "#") {
				hashPos = i;
				break;
			}
			if (text[i] === " " || text[i] === "\n" || text[i] === "	") break;
		}
		if (hashPos >= 0) {
			if (hashPos > 0 && text[hashPos - 1] === "(") {
				let foundClosingBracket = false;
				for (let i = hashPos - 2; i >= 0; i--) {
					if (text[i] === "]") {
						foundClosingBracket = true;
						break;
					}
					if (text[i] === "\n" || text[i] === "\r") break;
				}
				if (foundClosingBracket) {
					const searchText = text.substring(hashPos + 1, cursorPos);
					if (!searchText.includes(" ") && !searchText.includes("\n")) {
						this._showAutocomplete(searchText, hashPos);
						return;
					}
				}
			}
		}
		this.hideAutocomplete();
	}
	hideAutocomplete() {
		const dropdown = document.getElementById("autocomplete-dropdown");
		if (!dropdown) return;
		dropdown.classList.remove("visible");
		this._selectedIndex = -1;
	}
	destroy() {
		this.hideAutocomplete();
	}
	_showAutocomplete(searchText, hashPos) {
		const dropdown = document.getElementById("autocomplete-dropdown");
		const autocompleteList = document.getElementById("autocomplete-list");
		const textarea = document.getElementById("doc-textarea");
		if (!dropdown || !autocompleteList || !textarea) return;
		const allElements = this._getAllElements();
		const filteredElements = allElements.filter((element) => element.id.toLowerCase().includes(searchText.toLowerCase()) || element.name.toLowerCase().includes(searchText.toLowerCase()));
		if (filteredElements.length === 0) {
			this.hideAutocomplete();
			return;
		}
		autocompleteList.innerHTML = "";
		filteredElements.slice(0, 10).forEach((element) => {
			const item = document.createElement("div");
			item.className = "autocomplete-item";
			item.innerHTML = `
        <div class="autocomplete-item-id">${element.id}</div>
        <div class="autocomplete-item-name">${element.name}</div>
        <div class="autocomplete-item-type">${element.type}</div>
      `;
			item.addEventListener("click", () => {
				this._selectAutocompleteItem(element.id, hashPos);
			});
			autocompleteList.appendChild(item);
		});
		this._positionAutocomplete(textarea, hashPos);
		dropdown.classList.add("visible");
		this._selectedIndex = 0;
		this._updateAutocompleteSelection(Array.from(autocompleteList.children));
	}
	_positionAutocomplete(textarea, hashPos) {
		const dropdown = document.getElementById("autocomplete-dropdown");
		if (!dropdown) return;
		const textareaRect = textarea.getBoundingClientRect();
		const style = window.getComputedStyle(textarea);
		const tempSpan = document.createElement("span");
		tempSpan.style.visibility = "hidden";
		tempSpan.style.position = "absolute";
		tempSpan.style.top = "-9999px";
		tempSpan.style.fontFamily = style.fontFamily;
		tempSpan.style.fontSize = style.fontSize;
		tempSpan.style.fontWeight = style.fontWeight;
		tempSpan.style.letterSpacing = style.letterSpacing;
		tempSpan.style.whiteSpace = "pre";
		const textUpToHash = textarea.value.substring(0, hashPos);
		const linesUpToHash = textUpToHash.split("\n");
		const currentLine = linesUpToHash[linesUpToHash.length - 1];
		tempSpan.textContent = currentLine;
		this._callbacks.getCanvasContainer().appendChild(tempSpan);
		this._callbacks.getCanvasContainer().removeChild(tempSpan);
		const left = textareaRect.left + 10;
		const top = textareaRect.top + 100;
		dropdown.style.setProperty("left", `${left}px`, "important");
		dropdown.style.setProperty("top", `${top}px`, "important");
		dropdown.style.setProperty("position", "fixed", "important");
		dropdown.style.setProperty("z-index", "10001", "important");
	}
	_getAllElements() {
		const elements = [];
		const seenIds = /* @__PURE__ */ new Set();
		const allElements = this._callbacks.getAllElements();
		allElements.forEach((element) => {
			if (element.businessObject?.id) {
				const bo = element.businessObject;
				const elementId = bo.id;
				if (seenIds.has(elementId)) return;
				seenIds.add(elementId);
				elements.push({
					id: elementId,
					name: bo.name || "Unnamed",
					type: this._callbacks.getElementTypeName(element)
				});
			}
		});
		return elements.sort((a, b) => a.id.localeCompare(b.id));
	}
	_handleAutocompleteKeydown(event) {
		const dropdown = document.getElementById("autocomplete-dropdown");
		if (!dropdown || !dropdown.classList.contains("visible")) return;
		const items = Array.from(dropdown.querySelectorAll(".autocomplete-item"));
		if (event.key === "ArrowDown") {
			event.preventDefault();
			this._selectedIndex = Math.min(this._selectedIndex + 1, items.length - 1);
			this._updateAutocompleteSelection(items);
		} else if (event.key === "ArrowUp") {
			event.preventDefault();
			this._selectedIndex = Math.max(this._selectedIndex - 1, 0);
			this._updateAutocompleteSelection(items);
		} else if (event.key === "Enter") {
			event.preventDefault();
			if (this._selectedIndex >= 0 && items[this._selectedIndex]) {
				const selectedId = items[this._selectedIndex].querySelector(".autocomplete-item-id")?.textContent;
				const textarea = document.getElementById("doc-textarea");
				if (!textarea) return;
				const cursorPos = textarea.selectionStart;
				const text = textarea.value;
				let hashPos = -1;
				for (let i = cursorPos - 1; i >= 0; i--) if (text[i] === "#") {
					hashPos = i;
					break;
				}
				if (hashPos >= 0 && selectedId) this._selectAutocompleteItem(selectedId, hashPos);
			}
		} else if (event.key === "Escape") {
			event.preventDefault();
			this.hideAutocomplete();
		}
	}
	_updateAutocompleteSelection(items) {
		const dropdown = document.getElementById("autocomplete-dropdown");
		const autocompleteList = document.getElementById("autocomplete-list");
		if (!dropdown || !autocompleteList) return;
		Array.from(items).forEach((item, index) => {
			if (index === this._selectedIndex) {
				item.classList.add("selected");
				const itemTop = item.offsetTop;
				const itemBottom = itemTop + item.offsetHeight;
				const dropdownTop = dropdown.scrollTop;
				const dropdownBottom = dropdownTop + dropdown.clientHeight;
				if (itemTop < dropdownTop) dropdown.scrollTop = itemTop;
				else if (itemBottom > dropdownBottom) dropdown.scrollTop = itemBottom - dropdown.clientHeight;
			} else item.classList.remove("selected");
		});
	}
	_selectAutocompleteItem(elementId, hashPos) {
		const textarea = document.getElementById("doc-textarea");
		if (!textarea) return;
		const text = textarea.value;
		const cursorPos = textarea.selectionStart;
		const beforeHash = text.substring(0, hashPos + 1);
		const afterCursor = text.substring(cursorPos);
		const newText = beforeHash + elementId + afterCursor;
		textarea.value = newText;
		const newCursorPos = hashPos + 1 + elementId.length;
		textarea.setSelectionRange(newCursorPos, newCursorPos);
		this.hideAutocomplete();
		this._callbacks.updatePreview();
		this._callbacks.saveDocumentationLive();
		textarea.focus();
	}
};

//#endregion
//#region src/extension/managers/ExportManager.ts
var ExportManager = class {
	_elementRegistry;
	_moddle;
	_canvas;
	constructor(elementRegistry, moddle, canvas) {
		this._elementRegistry = elementRegistry;
		this._moddle = moddle;
		this._canvas = canvas;
	}
	setupExportEventListeners() {
		setTimeout(() => {
			document.getElementById("export-btn")?.addEventListener("click", () => {
				this.handleExport();
			});
		}, 100);
	}
	handleExport() {
		this.exportDocumentation().catch((error) => {
			console.error("Export failed:", error);
			this._showNotification("Export failed", "error");
		});
	}
	/**
	* Export documentation in HTML format
	*/
	async exportDocumentation() {
		try {
			const processInfo = this._getProcessInfo();
			const elements = this._getAllElementsWithDocumentation();
			const documentedElements = elements.filter((el) => el.hasDocumentation);
			if (documentedElements.length === 0) {
				this._showNotification("No documented elements found to export", "warning");
				return;
			}
			const htmlContent = await this._generateHTMLExport(elements, processInfo);
			const processName = processInfo.name || processInfo.id || "Process";
			const filename = `${processName}_Documentation.html`;
			this._downloadFile(htmlContent, filename, "text/html");
			this._showNotification(`Documentation exported successfully (${documentedElements.length} elements)`, "success");
		} catch (error) {
			console.error("Export failed:", error);
			const errorMessage = error instanceof Error ? error.message : "Unknown error";
			this._showNotification(`Export failed: ${errorMessage}`, "error");
		}
	}
	/**
	* Get BPMN diagram as SVG
	*/
	async _getDiagramSVG() {
		try {
			const canvasContainer = this._canvas.getContainer();
			const svgElement = canvasContainer.querySelector("svg");
			if (svgElement) {
				const svgCopy = svgElement.cloneNode(true);
				const allElements = this._elementRegistry.getAll();
				let minX = Number.POSITIVE_INFINITY;
				let minY = Number.POSITIVE_INFINITY;
				let maxX = Number.NEGATIVE_INFINITY;
				let maxY = Number.NEGATIVE_INFINITY;
				allElements.forEach((element) => {
					if (element.x !== void 0 && element.y !== void 0 && element.width !== void 0 && element.height !== void 0) {
						minX = Math.min(minX, element.x);
						minY = Math.min(minY, element.y);
						maxX = Math.max(maxX, element.x + element.width);
						maxY = Math.max(maxY, element.y + element.height);
					}
				});
				const padding = 50;
				const diagramWidth = maxX - minX;
				const diagramHeight = maxY - minY;
				const viewBoxX = minX - padding;
				const viewBoxY = minY - padding;
				const viewBoxWidth = diagramWidth + padding * 2;
				const viewBoxHeight = diagramHeight + padding * 2;
				const viewBox = `${viewBoxX} ${viewBoxY} ${viewBoxWidth} ${viewBoxHeight}`;
				svgCopy.setAttribute("viewBox", viewBox);
				const aspectRatio = viewBoxWidth / viewBoxHeight;
				let svgWidth = 800;
				let svgHeight = 600;
				if (aspectRatio > svgWidth / svgHeight) svgHeight = svgWidth / aspectRatio;
				else svgWidth = svgHeight * aspectRatio;
				svgCopy.setAttribute("width", Math.round(svgWidth).toString());
				svgCopy.setAttribute("height", Math.round(svgHeight).toString());
				const background = document.createElementNS("http://www.w3.org/2000/svg", "rect");
				background.setAttribute("x", viewBoxX.toString());
				background.setAttribute("y", viewBoxY.toString());
				background.setAttribute("width", viewBoxWidth.toString());
				background.setAttribute("height", viewBoxHeight.toString());
				background.setAttribute("fill", "#ffffff");
				svgCopy.insertBefore(background, svgCopy.firstChild);
				return svgCopy.outerHTML;
			}
			return "";
		} catch (error) {
			console.error("Error getting diagram SVG:", error);
			return "";
		}
	}
	/**
	* Get all elements with their documentation status
	*/
	_getAllElementsWithDocumentation() {
		const elements = [];
		const seenIds = /* @__PURE__ */ new Set();
		const allElements = this._elementRegistry.getAll();
		allElements.forEach((element) => {
			if (element.businessObject?.id) {
				const bo = element.businessObject;
				const elementId = bo.id;
				if (seenIds.has(elementId)) return;
				seenIds.add(elementId);
				const documentation = this._getElementDocumentation(element);
				elements.push({
					id: elementId,
					name: bo.name || "Unnamed",
					type: this._getElementTypeName(element),
					hasDocumentation: !!documentation?.trim(),
					documentation: documentation || "",
					element
				});
			}
		});
		return elements.sort((a, b) => a.id.localeCompare(b.id));
	}
	/**
	* Get documentation for a specific element
	*/
	_getElementDocumentation(element) {
		if (!element || !element.businessObject) return "";
		const bo = element.businessObject;
		if (bo.documentation && bo.documentation.length > 0) return bo.documentation[0].text || "";
		return "";
	}
	/**
	* Get element type name for display
	*/
	_getElementTypeName(element) {
		if (!element || !element.businessObject) return "Unknown";
		const bo = element.businessObject;
		const type = bo.$type || "";
		if (type.includes(":")) {
			const typeName = type.split(":")[1];
			return typeName.replace(/([A-Z])/g, " $1").trim();
		}
		return type || "Unknown";
	}
	/**
	* Generate HTML export content
	*/
	async _generateHTMLExport(elements, processInfo) {
		const totalElements = elements.length;
		const documentedCount = elements.filter((el) => el.hasDocumentation).length;
		const undocumentedCount = totalElements - documentedCount;
		const coveragePercentage = totalElements > 0 ? Math.round(documentedCount / totalElements * 100) : 0;
		const processTitle = processInfo.name || processInfo.id || "BPMN Process";
		const processDocumentation = processInfo.element ? this._getElementDocumentation(processInfo.element) : "";
		const diagramSVG = await this._getDiagramSVG();
		const tocItems = elements.map((el) => `<li><a href="#element-${el.id}" class="toc-link">${el.name} (${el.id})</a></li>`).join("");
		const elementSections = elements.map((el) => {
			const markdownContent = el.documentation || "";
			const htmlContent = markdownContent ? marked(markdownContent) : "<p class='no-documentation'><em>No documentation available</em></p>";
			return `
        <div class="element-section" id="element-${el.id}">
          <div class="element-header">
            <div class="element-title-info">
              <h2 class="element-title">${el.name}</h2>
              <div class="element-meta">
                <span class="element-id">${el.id}</span>
              </div>
            </div>
          </div>
          <div class="element-content">
            ${htmlContent}
          </div>
        </div>
      `;
		}).join("");
		return `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>${this._escapeHtml(processTitle)} - Documentation</title>
  <style>
    ${this._generateStyles()}
  </style>
</head>
<body>
  <div class="container">
    <header class="header">
      <h1 class="main-title">${this._escapeHtml(processTitle)}</h1>
      ${processDocumentation ? `<p class="subtitle">${this._escapeHtml(processDocumentation)}</p>` : ""}
      <div class="export-info">
        <span class="export-date">Generated on ${(/* @__PURE__ */ new Date()).toLocaleDateString()}</span>
      </div>
    </header>
    
    <div class="stats-section">
      <div class="stats-grid">
        <div class="stat-card">
          <div class="stat-number">${totalElements}</div>
          <div class="stat-label">Total Elements</div>
        </div>
        <div class="stat-card documented">
          <div class="stat-number">${documentedCount}</div>
          <div class="stat-label">Documented</div>
        </div>
        <div class="stat-card undocumented">
          <div class="stat-number">${undocumentedCount}</div>
          <div class="stat-label">Undocumented</div>
        </div>
        <div class="stat-card coverage">
          <div class="stat-number">${coveragePercentage}%</div>
          <div class="stat-label">Coverage</div>
        </div>
      </div>
    </div>
    
    ${diagramSVG ? `
    <div class="diagram-section">
      <h2 class="section-title">Process Diagram</h2>
      <div class="diagram-container">
        ${diagramSVG}
      </div>
    </div>
    ` : ""}
    
    <div class="toc-section">
      <h2 class="section-title">Table of Contents</h2>
      <div class="toc-container">
        <ul class="toc-list">
          ${tocItems}
        </ul>
      </div>
    </div>
    
    <div class="documentation-section">
      <h2 class="section-title">Element Documentation</h2>
      ${elementSections}
    </div>
    
    <div class="back-to-top">
      <button onclick="window.scrollTo({top: 0, behavior: 'smooth'})" class="back-to-top-btn">
        ↑ Back to Top
      </button>
    </div>
  </div>
  
  <script>
    // Add smooth scrolling for table of contents links
    document.querySelectorAll('.toc-link').forEach(link => {
      link.addEventListener('click', function(e) {
        e.preventDefault();
        const target = document.querySelector(this.getAttribute('href'));
        if (target) {
          target.scrollIntoView({ behavior: 'smooth', block: 'start' });
        }
      });
    });
    
    // Show/hide back to top button
    window.addEventListener('scroll', function() {
      const backToTop = document.querySelector('.back-to-top');
      if (window.scrollY > 300) {
        backToTop.style.display = 'block';
      } else {
        backToTop.style.display = 'none';
      }
    });
  <\/script>
</body>
</html>`;
	}
	/**
	* Generate CSS styles for the HTML export
	*/
	_generateStyles() {
		return `
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
      line-height: 1.6;
      color: #262c33;
      background: #f8f9fa;
      min-height: 100vh;
    }
    
    .container {
      max-width: 1200px;
      margin: 20px auto;
      padding: 0;
      background: white;
      border: 1px solid #dae2ec;
      border-radius: 8px;
      overflow: hidden;
    }
    
    .header {
      background: white;
      color: #262c33;
      padding: 60px 40px;
      text-align: center;
      border-bottom: 1px solid #dae2ec;
    }
    

    
    .main-title {
      font-size: 2.5em;
      font-weight: 600;
      margin-bottom: 10px;
    }
    
    .subtitle {
      font-size: 1.1em;
      opacity: 0.9;
      margin-bottom: 20px;
    }
    
    .export-date {
      font-size: 0.9em;
      opacity: 0.8;
      background: #fafafa;
      padding: 4px 12px;
      border-radius: 4px;
      display: inline-block;
      border: 1px solid #dae2ec;
    }
    
    .stats-section {
      padding: 40px;
      background: #fafafa;
      border-bottom: 1px solid #dae2ec;
    }
    
    .stats-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      gap: 20px;
    }
    
    .stat-card {
      background: white;
      padding: 25px 20px;
      border-radius: 4px;
      text-align: center;
      border: 1px solid #dae2ec;
    }
    
    .stat-number {
      font-size: 2.2em;
      font-weight: 600;
      color: #262c33;
      display: block;
      margin-bottom: 5px;
    }
    
    .stat-label {
      color: #666;
      font-size: 0.9em;
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.5px;
    }
    
    .diagram-section {
      padding: 40px;
      background: white;
      border-bottom: 1px solid #dae2ec;
    }
    
    .section-title {
      font-size: 1.8em;
      color: #262c33;
      margin-bottom: 25px;
      display: inline-block;
    }
    
    .diagram-container {
      background: #fafafa;
      border-radius: 4px;
      padding: 20px;
      text-align: center;
      border: 1px solid #dae2ec;
    }
    
    .diagram-container svg {
      max-width: 100%;
      height: auto;
      border-radius: 4px;
    }
    
    .toc-section {
      padding: 40px;
      background: #fafafa;
      border-bottom: 1px solid #dae2ec;
    }
    
    .toc-container {
      background: white;
      border-radius: 4px;
      padding: 30px;
      border: 1px solid #dae2ec;
    }
    
    .toc-list {
      list-style: none;
      columns: 2;
      column-gap: 30px;
      column-fill: balance;
    }
    
    .toc-list li {
      break-inside: avoid;
      margin-bottom: 8px;
      position: relative;
    }
    
    .toc-link {
      color: #262c33;
      text-decoration: none;
      font-weight: 500;
      transition: color 0.3s ease;
    }
    
    .toc-link:hover {
      color: #666;
    }
    
    .documentation-section {
      padding: 40px;
      background: white;
    }
    
    .element-section {
      margin-bottom: 30px;
      background: white;
      border-radius: 4px;
      border: 1px solid #dae2ec;
      overflow: hidden;
    }
    
    .element-header {
      background: white;
      color: #262c33;
      padding: 20px 25px;
      display: flex;
      align-items: center;
      gap: 15px;
      border-bottom: 1px solid #dae2ec;
    }
    
    
    .element-title-info {
      flex: 1;
    }
    
    .element-title {
      font-size: 1.3em;
      font-weight: 600;
      margin-bottom: 8px;
    }
    
    .element-meta {
      display: flex;
      gap: 15px;
      align-items: center;
    }
    
    
    .element-id {
      font-size: 0.9em;
      opacity: 0.8;
      font-family: 'Monaco', 'Consolas', monospace;
    }
    
    .element-content {
      padding: 25px;
      background: white;
    }
    
    .element-content h1,
    .element-content h2,
    .element-content h3,
    .element-content h4,
    .element-content h5,
    .element-content h6 {
      color: #262c33;
      margin-top: 20px;
      margin-bottom: 12px;
      font-weight: 600;
    }
    
    .element-content p {
      margin-bottom: 15px;
      line-height: 1.7;
    }
    
    .element-content ul,
    .element-content ol {
      margin-bottom: 15px;
      padding-left: 25px;
    }
    
    .element-content li {
      margin-bottom: 8px;
      line-height: 1.6;
    }
    
    .element-content code {
      background: #fafafa;
      padding: 2px 6px;
      border-radius: 4px;
      font-family: 'Monaco', 'Consolas', monospace;
      font-size: 0.9em;
      color: #262c33;
    }
    
    .element-content pre {
      background: #fafafa;
      padding: 15px;
      border-radius: 4px;
      overflow-x: auto;
      margin-bottom: 15px;
      border-left: 3px solid #bfcbd9;
    }
    
    .element-content blockquote {
      border-left: 3px solid #bfcbd9;
      padding-left: 15px;
      margin: 15px 0;
      color: #666;
      font-style: italic;
      background: #fafafa;
      padding: 12px 15px;
      border-radius: 0 4px 4px 0;
    }
    
    .element-content table {
      width: 100%;
      border-collapse: collapse;
      margin-bottom: 15px;
      border: 1px solid #dae2ec;
      border-radius: 4px;
      overflow: hidden;
    }
    
    .element-content th,
    .element-content td {
      padding: 10px 12px;
      text-align: left;
      border-bottom: 1px solid #dae2ec;
    }
    
    .element-content th {
      background: #fafafa;
      color: #262c33;
      font-weight: 600;
    }
    
    .element-content tr:hover {
      background: #fafafa;
    }
    
    .element-content a {
      color: #262c33;
      text-decoration: none;
      font-weight: 500;
    }
    
    .element-content a:hover {
      color: #666;
      text-decoration: underline;
    }
    
    .no-documentation {
      color: #666;
      font-style: italic;
      text-align: center;
      padding: 15px;
      background: #fafafa;
      border-radius: 4px;
    }
    
    .back-to-top {
      position: fixed;
      bottom: 30px;
      right: 30px;
      display: none;
      z-index: 1000;
    }
    
    .back-to-top-btn {
      background: white;
      color: #262c33;
      border: 1px solid #dae2ec;
      padding: 10px 12px;
      border-radius: 4px;
      cursor: pointer;
      font-size: 1em;
      transition: background-color 0.3s ease;
    }
    
    .back-to-top-btn:hover {
      background: #fafafa;
    }
    
    @media (max-width: 768px) {
      .container {
        margin: 0;
        box-shadow: none;
      }
      
      .header {
        padding: 40px 20px;
      }
      
      .main-title {
        font-size: 2.2em;
      }
      
      .stats-section,
      .diagram-section,
      .toc-section,
      .documentation-section {
        padding: 20px;
      }
      
      .stats-grid {
        grid-template-columns: 1fr;
      }
      
      .toc-list {
        columns: 1;
      }
      
      .element-header {
        padding: 20px;
        flex-direction: column;
        text-align: center;
        gap: 10px;
      }
      
      .element-meta {
        justify-content: center;
      }
      
      .element-content {
        padding: 20px;
      }
    }
    
    @media print {
      body {
        background: white;
      }
      
      .container {
        box-shadow: none;
      }
      
      .back-to-top {
        display: none;
      }
      
      .element-section {
        page-break-inside: avoid;
        break-inside: avoid;
      }
    }
    `;
	}
	/**
	* Escape HTML special characters
	*/
	_escapeHtml(unsafe) {
		return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
	}
	/**
	* Get process information from BPMN diagram
	*/
	_getProcessInfo() {
		try {
			const rootElement = this._canvas.getRootElement();
			if (rootElement?.businessObject) {
				const bo = rootElement.businessObject;
				return {
					name: bo.name || null,
					id: bo.id || null,
					filename: "process.bpmn",
					element: rootElement
				};
			}
			return {
				name: null,
				id: null,
				filename: "process.bpmn",
				element: null
			};
		} catch (error) {
			console.error("Error getting process info:", error);
			return {
				name: null,
				id: null,
				filename: "process.bpmn",
				element: null
			};
		}
	}
	/**
	* Download file to user's system
	*/
	_downloadFile(content, filename, mimeType) {
		const blob = new Blob([content], { type: mimeType });
		const url = URL.createObjectURL(blob);
		const a = document.createElement("a");
		a.href = url;
		a.download = filename;
		document.body.appendChild(a);
		a.click();
		document.body.removeChild(a);
		URL.revokeObjectURL(url);
	}
	/**
	* Show notification to user
	*/
	_showNotification(message, type) {
		const notification = document.createElement("div");
		notification.className = `notification notification-${type}`;
		notification.textContent = message;
		Object.assign(notification.style, {
			position: "fixed",
			top: "20px",
			right: "20px",
			padding: "12px 16px",
			borderRadius: "4px",
			color: "white",
			fontSize: "14px",
			fontWeight: "500",
			zIndex: "10000",
			maxWidth: "300px",
			wordWrap: "break-word",
			backgroundColor: type === "success" ? "#28a745" : type === "warning" ? "#ffc107" : "#dc3545",
			boxShadow: "0 2px 8px rgba(0,0,0,0.2)"
		});
		this._canvas.getContainer().appendChild(notification);
		setTimeout(() => {
			if (notification.parentNode) notification.parentNode.removeChild(notification);
		}, 3e3);
	}
	destroy() {}
};

//#endregion
//#region src/extension/managers/OverviewManager.ts
var OverviewManager = class {
	_callbacks;
	_currentFilter = "all";
	_currentSearchTerm = "";
	constructor(options) {
		this._callbacks = options.callbacks;
	}
	setupOverviewEventListeners() {
		setTimeout(() => {
			document.getElementById("overview-search")?.addEventListener("input", (event) => {
				this.filterOverviewList(event.target.value);
			});
			document.getElementById("show-all")?.addEventListener("click", () => {
				this.setOverviewFilter("all");
			});
			document.getElementById("show-documented")?.addEventListener("click", () => {
				this.setOverviewFilter("documented");
			});
			document.getElementById("show-undocumented")?.addEventListener("click", () => {
				this.setOverviewFilter("undocumented");
			});
		}, 100);
	}
	refreshOverview() {
		this._updateFilterButtonStates();
		this._updateCoverageStats();
		this._updateOverviewList();
	}
	filterOverviewList(searchTerm) {
		this._currentSearchTerm = searchTerm;
		this._updateOverviewList();
	}
	setOverviewFilter(filter) {
		this._currentFilter = filter;
		const sidebar = this._callbacks.getSidebar();
		if (!sidebar) return;
		sidebar.querySelectorAll(".btn-small").forEach((btn) => {
			btn.classList.remove("active");
		});
		const activeButton = sidebar.querySelector(`#show-${filter}`);
		if (activeButton) activeButton.classList.add("active");
		this._updateOverviewList();
	}
	destroy() {}
	_updateFilterButtonStates() {
		const sidebar = this._callbacks.getSidebar();
		if (!sidebar) return;
		sidebar.querySelectorAll(".btn-small").forEach((btn) => {
			btn.classList.remove("active");
		});
		const activeButton = sidebar.querySelector(`#show-${this._currentFilter}`);
		if (activeButton) activeButton.classList.add("active");
	}
	_updateCoverageStats() {
		const sidebar = this._callbacks.getSidebar();
		if (!sidebar) return;
		const elements = this._getAllElementsWithDocumentation();
		const documentedCount = elements.filter((el) => el.hasDocumentation).length;
		const totalCount = elements.length;
		const percentage = totalCount > 0 ? Math.round(documentedCount / totalCount * 100) : 0;
		const documentedCountEl = sidebar.querySelector("#documented-count");
		if (documentedCountEl) documentedCountEl.textContent = documentedCount.toString();
		const totalCountEl = sidebar.querySelector("#total-count");
		if (totalCountEl) totalCountEl.textContent = totalCount.toString();
		const coveragePercentageEl = sidebar.querySelector("#coverage-percentage");
		if (coveragePercentageEl) coveragePercentageEl.textContent = `${percentage}%`;
		const progressBar = sidebar.querySelector("#coverage-progress");
		if (progressBar) progressBar.style.width = `${percentage}%`;
	}
	_getAllElementsWithDocumentation() {
		const elements = [];
		const seenIds = /* @__PURE__ */ new Set();
		const allElements = this._callbacks.getAllElements();
		allElements.forEach((element) => {
			if (element.businessObject?.id) {
				const bo = element.businessObject;
				const elementId = bo.id;
				if (seenIds.has(elementId)) return;
				seenIds.add(elementId);
				const documentation = this._callbacks.getElementDocumentation(element);
				elements.push({
					id: elementId,
					name: bo.name || "Unnamed",
					type: this._callbacks.getElementTypeName(element),
					hasDocumentation: !!documentation?.trim(),
					documentation: documentation || "",
					element
				});
			}
		});
		return elements.sort((a, b) => a.id.localeCompare(b.id));
	}
	_updateOverviewList() {
		const sidebar = this._callbacks.getSidebar();
		if (!sidebar) return;
		const overviewList = sidebar.querySelector("#overview-list");
		if (!overviewList) return;
		const elements = this._getAllElementsWithDocumentation();
		let filteredElements = elements;
		if (this._currentFilter === "documented") filteredElements = elements.filter((el) => el.hasDocumentation);
		else if (this._currentFilter === "undocumented") filteredElements = elements.filter((el) => !el.hasDocumentation);
		if (this._currentSearchTerm) {
			const searchTerm = this._currentSearchTerm.toLowerCase();
			filteredElements = filteredElements.filter((el) => el.id.toLowerCase().includes(searchTerm) || el.name.toLowerCase().includes(searchTerm) || el.documentation.toLowerCase().includes(searchTerm));
		}
		if (filteredElements.length === 0) {
			overviewList.innerHTML = "<div class=\"overview-loading\">No elements found</div>";
			return;
		}
		overviewList.innerHTML = filteredElements.map((element) => {
			const statusClass = element.hasDocumentation ? "documented" : "undocumented";
			const statusText = element.hasDocumentation ? "documented" : "undocumented";
			return `
        <div class="element-item ${statusClass}" data-element-id="${element.id}">
          <div class="element-header">
            <span class="element-id">${element.id}</span>
            <span class="element-status ${statusClass}">${statusText}</span>
          </div>
          <div class="element-info">
            <span>${element.name}</span>
            <span>•</span>
            <span>${element.type}</span>
          </div>
        </div>
      `;
		}).join("");
		overviewList.querySelectorAll(".element-item").forEach((card) => {
			card.addEventListener("click", () => {
				const elementId = card.getAttribute("data-element-id");
				if (elementId) {
					this._callbacks.selectElementById(elementId);
					this._callbacks.switchToElementTab();
				}
			});
		});
	}
};

//#endregion
//#region src/extension/managers/SidebarManager.ts
var SidebarManager = class {
	_canvas;
	_htmlGenerator;
	_onSidebarReady;
	_sidebar = null;
	_resizeObserver = null;
	_cleanupRaf = null;
	_isResizing = false;
	_resizeStartX = 0;
	_resizeStartWidth = 0;
	_isVerticalResizing = false;
	_resizeStartY = 0;
	_resizeStartHeight = 0;
	_customWidth = null;
	_wasVisible = false;
	constructor(options) {
		this._canvas = options.canvas;
		this._htmlGenerator = options.htmlGenerator;
		this._onSidebarReady = options.onSidebarReady;
	}
	initializeSidebar() {
		const existingSidebar = document.getElementById("documentation-sidebar");
		if (existingSidebar) existingSidebar.remove();
		const existingHandle = document.getElementById("horizontal-resize-handle");
		if (existingHandle) existingHandle.remove();
		const existingHelpPopover = document.getElementById("help-popover");
		if (existingHelpPopover) existingHelpPopover.remove();
		const sidebar = document.createElement("div");
		sidebar.id = "documentation-sidebar";
		sidebar.className = "documentation-sidebar";
		sidebar.style.display = "none";
		sidebar.innerHTML = this._htmlGenerator.generateSidebarHTML();
		const canvasContainer = this._getCanvasContainer();
		const currentPosition = window.getComputedStyle(canvasContainer).position;
		if (currentPosition === "static") canvasContainer.style.position = "relative";
		canvasContainer.appendChild(sidebar);
		this._sidebar = sidebar;
		const helpPopoverDiv = document.createElement("div");
		helpPopoverDiv.innerHTML = this._htmlGenerator.generateHelpPopoverHTML();
		const helpPopover = helpPopoverDiv.firstElementChild;
		canvasContainer.appendChild(helpPopover);
		const horizontalResizeHandle = document.createElement("div");
		horizontalResizeHandle.id = "horizontal-resize-handle";
		horizontalResizeHandle.className = "horizontal-resize-handle";
		canvasContainer.appendChild(horizontalResizeHandle);
		if (this._onSidebarReady) setTimeout(() => {
			this._onSidebarReady?.(sidebar);
		}, 10);
		setTimeout(() => {
			this.updateSidebarPosition();
			this.setupResizeObserver();
			this.setupResizeHandles();
		}, 100);
	}
	showSidebar() {
		if (this._sidebar && !this._sidebar.parentElement) {
			this.initializeSidebar();
			setTimeout(() => {
				this.showSidebar();
			}, 10);
			return;
		}
		this.updateSidebarPosition();
		if (this._sidebar) {
			this._sidebar.style.display = "flex";
			this._sidebar.classList.add("visible");
		}
		const horizontalHandle = document.getElementById("horizontal-resize-handle");
		if (horizontalHandle) horizontalHandle.style.display = "block";
		this._wasVisible = true;
	}
	hideSidebar() {
		if (this._sidebar) {
			this._wasVisible = this._sidebar.classList.contains("visible");
			this._sidebar.classList.remove("visible");
			this._sidebar.style.display = "none";
		}
		const horizontalHandle = document.getElementById("horizontal-resize-handle");
		if (horizontalHandle) horizontalHandle.style.display = "none";
	}
	updateSidebarPosition() {
		const canvasContainer = this._getCanvasContainer();
		const containerRect = canvasContainer.getBoundingClientRect();
		const propertiesPanel = document.querySelector(".bio-properties-panel-container") || document.querySelector(".djs-properties-panel") || document.querySelector("[data-tab=\"properties\"]") || document.querySelector(".properties-panel");
		if (propertiesPanel) {
			const panelRect = propertiesPanel.getBoundingClientRect();
			const topOffset = Math.max(panelRect.top - containerRect.top, 0);
			const bottomOffset = Math.max(containerRect.bottom - panelRect.bottom, 0);
			const availableHeight = containerRect.height - topOffset - bottomOffset;
			if (this._sidebar) {
				this._sidebar.style.top = `${topOffset}px`;
				this._sidebar.style.height = `${Math.max(availableHeight, 300)}px`;
				if (!this._isResizing) {
					const width = this._customWidth ? `${this._customWidth}px` : "350px";
					this._sidebar.style.setProperty("width", width, "important");
					const horizontalHandle = document.getElementById("horizontal-resize-handle");
					if (horizontalHandle) {
						const sidebarWidth = this._customWidth || 350;
						horizontalHandle.style.right = `${sidebarWidth}px`;
						horizontalHandle.style.top = `${topOffset}px`;
						horizontalHandle.style.height = `${Math.max(availableHeight, 900)}px`;
					}
				}
			}
		} else if (this._sidebar) {
			this._sidebar.style.right = "0px";
			this._sidebar.style.top = "0px";
			this._sidebar.style.height = "100%";
			if (!this._isResizing) {
				const width = this._customWidth ? `${this._customWidth}px` : "350px";
				this._sidebar.style.setProperty("width", width, "important");
				const horizontalHandle = document.getElementById("horizontal-resize-handle");
				if (horizontalHandle) {
					const sidebarWidth = this._customWidth || 350;
					horizontalHandle.style.right = `${sidebarWidth}px`;
					horizontalHandle.style.top = "0px";
					horizontalHandle.style.height = "100%";
				}
			}
		}
	}
	setupResizeObserver() {
		if (this._resizeObserver) this._resizeObserver.disconnect();
		const propertiesPanel = document.querySelector(".bio-properties-panel-container") || document.querySelector(".djs-properties-panel") || document.querySelector("[data-tab=\"properties\"]") || document.querySelector(".properties-panel");
		if (propertiesPanel && window.ResizeObserver) {
			this._resizeObserver = new ResizeObserver(() => {
				requestAnimationFrame(() => {
					this.updateSidebarPosition();
				});
			});
			this._resizeObserver.observe(propertiesPanel);
			const parentContainer = propertiesPanel.parentElement;
			if (parentContainer) this._resizeObserver.observe(parentContainer);
			const rightPanel = document.querySelector(".djs-properties-panel-parent") || document.querySelector(".properties-panel-parent") || propertiesPanel.closest(".panel");
			if (rightPanel && rightPanel !== propertiesPanel) this._resizeObserver.observe(rightPanel);
		}
		let rafId;
		const updatePosition = () => {
			if (this._sidebar?.classList.contains("visible") && this._sidebar.style.display !== "none" && !this._isResizing && !this._isVerticalResizing) this.updateSidebarPosition();
			rafId = requestAnimationFrame(updatePosition);
		};
		updatePosition();
		this._cleanupRaf = () => {
			if (rafId) cancelAnimationFrame(rafId);
		};
		window.addEventListener("resize", () => {
			this.updateSidebarPosition();
		});
	}
	setupResizeHandles() {
		this._setupHorizontalResize();
		this._setupVerticalResize();
	}
	getSidebar() {
		return this._sidebar;
	}
	isSidebarVisible() {
		return this._sidebar?.classList.contains("visible") ?? false;
	}
	destroy() {
		if (this._resizeObserver) this._resizeObserver.disconnect();
		if (this._cleanupRaf) this._cleanupRaf();
		if (this._sidebar) this._sidebar.remove();
		const horizontalHandle = document.getElementById("horizontal-resize-handle");
		if (horizontalHandle) horizontalHandle.remove();
		const helpPopover = document.getElementById("help-popover");
		if (helpPopover) helpPopover.remove();
	}
	_getCanvasContainer() {
		return this._canvas?.getContainer() ?? document.body;
	}
	_setupHorizontalResize() {
		const horizontalHandle = document.getElementById("horizontal-resize-handle");
		if (!horizontalHandle) return;
		let rafId = null;
		let pendingWidth = null;
		const handleMouseDown = (e) => {
			e.preventDefault();
			this._isResizing = true;
			this._resizeStartX = e.clientX;
			this._resizeStartWidth = this._sidebar?.offsetWidth || 350;
			document.body.style.cursor = "ew-resize";
			document.body.style.userSelect = "none";
			document.addEventListener("mousemove", handleMouseMove);
			document.addEventListener("mouseup", handleMouseUp);
		};
		const updateResize = () => {
			if (pendingWidth !== null && this._sidebar) {
				this._sidebar.style.setProperty("width", `${pendingWidth}px`, "important");
				const horizontalHandle$1 = document.getElementById("horizontal-resize-handle");
				if (horizontalHandle$1) horizontalHandle$1.style.right = `${pendingWidth}px`;
				pendingWidth = null;
			}
			rafId = null;
		};
		const handleMouseMove = (e) => {
			if (!this._isResizing || !this._sidebar) return;
			e.preventDefault();
			const deltaX = this._resizeStartX - e.clientX;
			const newWidth = this._resizeStartWidth + deltaX;
			const minWidth = 250;
			const maxWidth = window.innerWidth * .6;
			const constrainedWidth = Math.max(minWidth, Math.min(maxWidth, newWidth));
			this._customWidth = constrainedWidth;
			pendingWidth = constrainedWidth;
			if (rafId === null) rafId = requestAnimationFrame(updateResize);
		};
		const handleMouseUp = () => {
			this._isResizing = false;
			document.body.style.cursor = "";
			document.body.style.userSelect = "";
			if (rafId !== null) {
				cancelAnimationFrame(rafId);
				updateResize();
			}
			document.removeEventListener("mousemove", handleMouseMove);
			document.removeEventListener("mouseup", handleMouseUp);
		};
		horizontalHandle.addEventListener("mousedown", handleMouseDown);
	}
	_setupVerticalResize() {
		const verticalHandle = document.getElementById("resize-handle");
		if (!verticalHandle) return;
		let rafId = null;
		let pendingHeight = null;
		const handleMouseDown = (e) => {
			e.preventDefault();
			this._isVerticalResizing = true;
			this._resizeStartY = e.clientY;
			const previewElement = document.getElementById("doc-preview");
			this._resizeStartHeight = previewElement?.offsetHeight || 200;
			document.body.style.cursor = "ns-resize";
			document.body.style.userSelect = "none";
			document.addEventListener("mousemove", handleMouseMove);
			document.addEventListener("mouseup", handleMouseUp);
		};
		const updateResize = () => {
			if (pendingHeight !== null) {
				const previewElement = document.getElementById("doc-preview");
				if (previewElement) previewElement.style.height = `${pendingHeight}px`;
				pendingHeight = null;
			}
			rafId = null;
		};
		const handleMouseMove = (e) => {
			if (!this._isVerticalResizing) return;
			e.preventDefault();
			const deltaY = e.clientY - this._resizeStartY;
			const newHeight = this._resizeStartHeight + deltaY;
			const minHeight = 100;
			const maxHeight = window.innerHeight * .6;
			const constrainedHeight = Math.max(minHeight, Math.min(maxHeight, newHeight));
			pendingHeight = constrainedHeight;
			if (rafId === null) rafId = requestAnimationFrame(updateResize);
		};
		const handleMouseUp = () => {
			this._isVerticalResizing = false;
			document.body.style.cursor = "";
			document.body.style.userSelect = "";
			if (rafId !== null) {
				cancelAnimationFrame(rafId);
				updateResize();
			}
			document.removeEventListener("mousemove", handleMouseMove);
			document.removeEventListener("mouseup", handleMouseUp);
		};
		verticalHandle.addEventListener("mousedown", handleMouseDown);
	}
};

//#endregion
//#region src/extension/managers/TabManager.ts
var TabManager = class {
	_callbacks;
	constructor(options) {
		this._callbacks = options.callbacks;
	}
	setupTabEventListeners() {
		setTimeout(() => {
			document.getElementById("element-tab")?.addEventListener("click", () => {
				this.switchTab("element");
			});
			document.getElementById("overview-tab")?.addEventListener("click", () => {
				this.switchTab("overview");
				this._callbacks.onOverviewTabActivated();
			});
		}, 100);
	}
	switchTab(tabName) {
		if (!this._callbacks.isSidebarVisible()) return;
		const sidebar = this._callbacks.getSidebar();
		if (!sidebar) return;
		Array.from(sidebar.querySelectorAll(".tab-btn")).forEach((btn) => {
			const btnEl = btn;
			if (btnEl.dataset.tab === tabName) btnEl.classList.add("active");
			else btnEl.classList.remove("active");
		});
		Array.from(sidebar.querySelectorAll(".tab-panel")).forEach((panel) => {
			const panelEl = panel;
			if (panelEl.id === `${tabName}-panel`) panelEl.classList.add("active");
			else panelEl.classList.remove("active");
		});
		const elementMetadata = sidebar.querySelector("#element-metadata");
		if (elementMetadata) elementMetadata.style.display = "block";
		if (tabName === "element") this._callbacks.onElementTabActivated();
	}
	destroy() {}
};

//#endregion
//#region src/extension/managers/ViewManager.ts
var ViewManager = class {
	_currentView = "diagram";
	_viewCheckInterval = null;
	_callbacks;
	constructor(callbacks) {
		this._callbacks = callbacks;
	}
	getCurrentView() {
		return this._currentView;
	}
	setupViewDetection() {
		this._viewCheckInterval = setInterval(() => {
			const newView = this._detectCurrentView();
			if (newView !== this._currentView) {
				this._currentView = newView;
				this._callbacks.onViewChanged(newView);
				this.updateSidebarVisibility();
			}
		}, 500);
	}
	updateSidebarVisibility() {
		if (this._currentView === "xml") this._callbacks.hideSidebar();
		else if (this._currentView === "diagram") {
			const currentElement = this._callbacks.getCurrentElement();
			if (currentElement) {
				const documentation = this._callbacks.getElementDocumentation(currentElement);
				this._callbacks.showSidebar(documentation || "");
			}
		}
	}
	destroy() {
		if (this._viewCheckInterval) {
			clearInterval(this._viewCheckInterval);
			this._viewCheckInterval = null;
		}
	}
	_detectCurrentView() {
		const xmlEditor = document.querySelector(".cm-editor");
		const codeEditor = document.querySelector(".CodeMirror");
		if (xmlEditor && xmlEditor.offsetParent !== null || codeEditor && codeEditor.offsetParent !== null) return "xml";
		return "diagram";
	}
};

//#endregion
//#region src/extension/templates/HtmlTemplateGenerator.ts
var HtmlTemplateGenerator = class {
	_options;
	constructor(options) {
		this._options = options;
	}
	generateSidebarHTML() {
		const editorSection = this._generateEditorSection();
		return `
      <div class="documentation-header">
        <div class="header-content">
          <div class="title-row">
            <h3>Documentation${this._options.isModeler ? "" : " (Read-only)"}</h3>
            <button class="help-btn" id="help-btn">?</button>
          </div>
          <div class="element-metadata" id="element-metadata">
            <span class="element-name" id="element-name"></span>
          </div>
        </div>
        <button class="close-btn" id="close-sidebar">×</button>
      </div>
      <div class="tab-container">
        <div class="tab-buttons">
          <button class="tab-btn active" id="element-tab" data-tab="element">Element</button>
          <button class="tab-btn" id="overview-tab" data-tab="overview">Overview</button>
        </div>
        <button class="btn-export" id="export-btn">
          <span class="export-btn-icon">📤</span>
          <span>Export</span>
        </button>
      </div>
      <div class="tab-content">
        <div class="tab-panel active" id="element-panel">
          <div class="documentation-content">
            <div class="documentation-preview" id="doc-preview"></div>
            ${editorSection}
          </div>
        </div>
        <div class="tab-panel" id="overview-panel">
          <div class="overview-content">
            <div class="overview-header">
              <div class="coverage-summary">
                <div class="coverage-stats">
                  <span class="stat-item">
                    <strong id="documented-count">0</strong> documented
                  </span>
                  <span class="stat-item">
                    <strong id="total-count">0</strong> total elements
                  </span>
                  <span class="stat-item">
                    <strong id="coverage-percentage">0%</strong> coverage
                  </span>
                </div>
                <div class="coverage-bar">
                  <div class="coverage-progress" id="coverage-progress"></div>
                </div>
              </div>
              <div class="overview-search">
                <input type="text" id="overview-search" placeholder="Search documentation..." />
                <div class="search-actions">
                  <button class="btn-small" id="show-documented">Documented</button>
                  <button class="btn-small" id="show-undocumented">Undocumented</button>
                  <button class="btn-small active" id="show-all">All</button>
                </div>
              </div>
            </div>
            <div class="overview-list" id="overview-list">
              <div class="overview-loading">Loading elements...</div>
            </div>
          </div>
        </div>
      </div>
    `;
	}
	generateHelpPopoverHTML() {
		const helpContent = this._generateHelpContent();
		return `
      <div class="help-popover" id="help-popover">
        <div class="help-content">
          <h4>Documentation Panel Guide</h4>
          ${helpContent}
        </div>
      </div>
    `;
	}
	_generateHelpContent() {
		return this._options.isModeler ? `
      <div class="help-section">
        <strong>What is this panel?</strong>
        <p>This panel allows you to add and view documentation for BPMN elements in your diagram.</p>
      </div>
      <div class="help-section">
        <strong>How to use:</strong>
        <ul>
          <li><strong>Select an element</strong> - Click on any BPMN element (task, gateway, event, etc.) to view or edit its documentation</li>
          <li><strong>Edit documentation</strong> - Use the editor below to write documentation in Markdown format</li>
          <li><strong>Preview</strong> - The top section shows a live preview of your formatted documentation</li>
        </ul>
      </div>
      <div class="help-section">
        <strong>Markdown support:</strong>
        <p>Use standard Markdown syntax for formatting: **bold**, *italic*, lists, links, and more.</p>
      </div>
      <div class="help-section">
        <strong>Creating links:</strong>
        <p>Link to other BPMN elements using their ID:</p>
        <code>[Element Name](#elementId)</code>
        <p>Example: <code>[Check Inventory](#Task_CheckInventory)</code></p>
        <p>Link to external resources:</p>
        <code>[External Link](https://example.com)</code>
        <p><em>Tip: Element IDs can be found in the properties panel or by selecting the element. Type # inside () for autocomplete suggestions.</em></p>
      </div>
    ` : `
      <div class="help-section">
        <strong>What is this panel?</strong>
        <p>This panel displays documentation for BPMN elements in this diagram.</p>
      </div>
      <div class="help-section">
        <strong>How to use:</strong>
        <ul>
          <li><strong>Select an element</strong> - Click on any BPMN element (task, gateway, event, etc.) to view its documentation</li>
          <li><strong>Navigate</strong> - Click on element links to jump to related elements in the diagram</li>
          <li><strong>Overview</strong> - Use the Overview tab to see all documented elements</li>
        </ul>
      </div>
      <div class="help-section">
        <strong>Reading documentation:</strong>
        <p>Documentation is displayed in formatted view. Click on element links (shown in blue) to navigate to related elements in the diagram.</p>
      </div>
    `;
	}
	_generateEditorSection() {
		return this._options.isModeler ? `
      <div class="documentation-bottom">
        <div class="resize-handle" id="resize-handle"></div>
        <div class="documentation-editor">
          <div class="editor-container">
            <textarea id="doc-textarea" placeholder="Write documentation in Markdown..."></textarea>
            <div class="autocomplete-dropdown" id="autocomplete-dropdown">
              <div class="autocomplete-list" id="autocomplete-list"></div>
            </div>
          </div>
        </div>
      </div>
    ` : "";
	}
};

//#endregion
//#region src/extension/index.ts
var DocumentationExtension = class {
	_eventBus;
	_elementRegistry;
	_modeling;
	_moddle;
	_selection;
	_canvas;
	_currentElement;
	_isModeler;
	_currentView;
	_viewManager;
	_htmlGenerator;
	_sidebarManager;
	_tabManager;
	_overviewManager;
	_autocompleteManager;
	_exportManager;
	constructor(eventBus, elementRegistry, injector, moddle, selection, canvas) {
		this._modeling = injector.get("modeling", false);
		this._isModeler = !!this._modeling;
		this._eventBus = eventBus;
		this._elementRegistry = elementRegistry;
		this._moddle = moddle;
		this._selection = selection;
		this._canvas = canvas;
		this._currentElement = null;
		this._currentView = "diagram";
		this._htmlGenerator = new HtmlTemplateGenerator({ isModeler: this._isModeler });
		this._sidebarManager = new SidebarManager({
			canvas: this._canvas,
			htmlGenerator: this._htmlGenerator,
			onSidebarReady: () => this._onSidebarReady()
		});
		const tabCallbacks = {
			onOverviewTabActivated: () => this._overviewManager.refreshOverview(),
			onElementTabActivated: () => {},
			getSidebar: () => this._sidebarManager.getSidebar(),
			isSidebarVisible: () => this._sidebarManager.isSidebarVisible()
		};
		this._tabManager = new TabManager({ callbacks: tabCallbacks });
		const viewCallbacks = {
			onViewChanged: (newView) => {
				this._currentView = newView;
			},
			hideSidebar: () => this._sidebarManager.hideSidebar(),
			showSidebar: (documentation) => this._showSidebar(documentation),
			getElementDocumentation: (element) => this._getElementDocumentation(element),
			getCurrentElement: () => this._currentElement
		};
		this._viewManager = new ViewManager(viewCallbacks);
		const overviewCallbacks = {
			getAllElements: () => this._elementRegistry.getAll(),
			getElementDocumentation: (element) => this._getElementDocumentation(element),
			getElementTypeName: (element) => this._getElementTypeName(element),
			getSidebar: () => this._sidebarManager.getSidebar(),
			selectElementById: (elementId) => this._selectElementById(elementId),
			switchToElementTab: () => this._tabManager.switchTab("element")
		};
		this._overviewManager = new OverviewManager({ callbacks: overviewCallbacks });
		const autocompleteCallbacks = {
			getAllElements: () => this._elementRegistry.getAll(),
			getElementTypeName: (element) => this._getElementTypeName(element),
			getCanvasContainer: () => this._getCanvasContainer(),
			updatePreview: () => this._updatePreview(),
			saveDocumentationLive: () => this._saveDocumentationLive(),
			selectElementById: (elementId) => this._selectElementById(elementId),
			getCurrentElement: () => this._currentElement
		};
		this._autocompleteManager = new AutocompleteManager({ callbacks: autocompleteCallbacks });
		this._exportManager = new ExportManager(this._elementRegistry, this._moddle, this._canvas);
		this._sidebarManager.initializeSidebar();
		this._viewManager.setupViewDetection();
		this._exportManager.setupExportEventListeners();
		eventBus.on("element.click", (event) => {
			const { element } = event;
			this._handleElementClick(element);
		});
		eventBus.on("selection.changed", (event) => {
			const { newSelection } = event;
			if (newSelection && newSelection.length > 0) this._handleElementClick(newSelection[0]);
			else this._sidebarManager.hideSidebar();
		});
		eventBus.on("connection.click", (event) => {
			const { element } = event;
			this._handleElementClick(element);
		});
		eventBus.on("canvas.click", () => {
			setTimeout(() => {
				if (!this._currentElement) this._sidebarManager.hideSidebar();
			}, 10);
		});
		eventBus.on("import.done", () => {
			this._handleDiagramImport();
		});
		eventBus.on("import.parse.complete", () => {
			this._handleDiagramImport();
		});
		eventBus.on("diagram.destroy", () => {
			this._handleDiagramDestroy();
		});
		eventBus.on("diagram.clear", () => {
			this._handleDiagramClear();
		});
	}
	_onSidebarReady() {
		const helpBtn = document.getElementById("help-btn");
		if (helpBtn) {
			helpBtn.replaceWith(helpBtn.cloneNode(true));
			const newHelpBtn = document.getElementById("help-btn");
			newHelpBtn?.addEventListener("click", (event) => {
				event.preventDefault();
				event.stopPropagation();
				this._toggleHelpPopover();
			});
		}
		document.addEventListener("click", (event) => {
			const helpPopover = document.getElementById("help-popover");
			const helpBtn$1 = document.getElementById("help-btn");
			if (helpPopover?.classList.contains("visible") && !(event.target instanceof Node && helpPopover.contains(event.target)) && !(event.target instanceof Node && helpBtn$1 && helpBtn$1.contains(event.target))) this._hideHelpPopover();
		});
		if (this._isModeler) {
			const textarea = document.getElementById("doc-textarea");
			if (textarea) textarea.addEventListener("input", () => {
				this._updatePreview();
				this._saveDocumentationLive();
			});
			this._autocompleteManager.setupAutocompleteEventListeners();
		}
		setTimeout(() => {
			document.getElementById("close-sidebar")?.addEventListener("click", () => {
				this._currentElement = null;
				this._sidebarManager.hideSidebar();
			});
		}, 100);
		this._tabManager.setupTabEventListeners();
		this._overviewManager.setupOverviewEventListeners();
		this._setupScrollEventHandling();
	}
	_setupScrollEventHandling() {
		setTimeout(() => {
			const sidebar = document.getElementById("documentation-sidebar");
			if (sidebar) {
				sidebar.addEventListener("wheel", (event) => {
					if (event.target instanceof Element && sidebar.contains(event.target)) event.stopPropagation();
				}, { passive: false });
				sidebar.addEventListener("scroll", (event) => {
					if (event.target instanceof Element && sidebar.contains(event.target)) event.stopPropagation();
				}, { passive: true });
				const sidebarScrollableSelectors = [
					"#doc-preview",
					"#overview-list",
					"#doc-textarea",
					"#autocomplete-dropdown"
				];
				sidebarScrollableSelectors.forEach((selector) => {
					const element = sidebar.querySelector(selector);
					if (element) {
						element.addEventListener("wheel", (event) => {
							event.stopPropagation();
						}, { passive: false });
						element.addEventListener("scroll", (event) => {
							event.stopPropagation();
						}, { passive: true });
					}
				});
			}
			const helpPopover = document.getElementById("help-popover");
			if (helpPopover) {
				helpPopover.addEventListener("wheel", (event) => {
					event.stopPropagation();
				}, { passive: false });
				helpPopover.addEventListener("scroll", (event) => {
					event.stopPropagation();
				}, { passive: true });
			}
		}, 150);
	}
	_handleElementClick(element) {
		if (this._currentView === "xml") return;
		if (!element || !element.businessObject) {
			this._currentElement = null;
			this._sidebarManager.hideSidebar();
			return;
		}
		const documentation = this._getElementDocumentation(element);
		const hasCapability = this._hasDocumentationCapability(element);
		if (documentation || hasCapability) {
			this._currentElement = element;
			this._showSidebar(documentation || "");
		} else {
			this._currentElement = null;
			this._sidebarManager.hideSidebar();
		}
	}
	_getElementDocumentation(element) {
		const businessObject = element.businessObject;
		if (businessObject.documentation && businessObject.documentation.length > 0) return businessObject.documentation[0].text;
		return null;
	}
	_hasDocumentationCapability(element) {
		return element.businessObject;
	}
	_showSidebar(documentation) {
		if (this._isModeler) {
			const textarea = document.getElementById("doc-textarea");
			if (textarea) textarea.value = documentation || "";
		}
		this._updateElementMetadata();
		this._updatePreview();
		this._sidebarManager.showSidebar();
	}
	_hideSidebar() {
		this._sidebarManager.hideSidebar();
	}
	_toggleHelpPopover() {
		const helpPopover = document.getElementById("help-popover");
		console.log("Toggle help popover:", helpPopover?.classList.contains("visible"));
		if (helpPopover?.classList.contains("visible")) this._hideHelpPopover();
		else if (helpPopover) this._showHelpPopover();
	}
	_showHelpPopover() {
		const helpPopover = document.getElementById("help-popover");
		if (helpPopover) helpPopover.classList.add("visible");
	}
	_hideHelpPopover() {
		const helpPopover = document.getElementById("help-popover");
		if (helpPopover) helpPopover.classList.remove("visible");
	}
	_getCanvasContainer() {
		return this._canvas?.getContainer() ?? document.body;
	}
	async _updatePreview() {
		const preview = document.getElementById("doc-preview");
		if (!preview) return;
		let value = "";
		if (this._isModeler) {
			const textarea = document.getElementById("doc-textarea");
			if (!textarea) return;
			value = textarea.value;
		} else if (this._currentElement) value = this._getElementDocumentation(this._currentElement) || "";
		if (value?.trim()) {
			const rendered = await Promise.resolve(marked(value));
			preview.innerHTML = typeof rendered === "string" ? rendered : "";
			this._setupElementLinks(preview);
		} else preview.innerHTML = "<em>No documentation.</em>";
	}
	_setupElementLinks(container) {
		const links = container.querySelectorAll("a[href^=\"#\"]");
		links.forEach((link) => {
			link.addEventListener("click", (event) => {
				event.preventDefault();
				const elementId = link.getAttribute("href")?.substring(1);
				if (elementId) this._selectElementById(elementId);
			});
			link.addEventListener("mouseenter", () => {
				link.classList.add("hovered");
			});
			link.addEventListener("mouseleave", () => {
				link.classList.remove("hovered");
			});
		});
	}
	_selectElementById(elementId) {
		try {
			const element = this._elementRegistry.get(elementId);
			if (element) {
				this._selection.select(element);
				this._canvas.scrollToElement(element);
			} else {
				console.warn(`Element with ID "${elementId}" not found in the diagram`);
				this._showLinkNotification(`Element "${elementId}" not found`);
			}
		} catch (error) {
			console.error("Error selecting element:", error);
			this._showLinkNotification(`Error selecting element "${elementId}"`);
		}
	}
	_showLinkNotification(message) {
		const notification = document.createElement("div");
		notification.textContent = message;
		notification.style.cssText = `
      position: fixed;
      top: 20px;
      right: 20px;
      background: #f44336;
      color: white;
      padding: 8px 16px;
      border-radius: 4px;
      font-size: 12px;
      z-index: 10000;
      box-shadow: 0 2px 8px rgba(0,0,0,0.2);
    `;
		this._getCanvasContainer().appendChild(notification);
		setTimeout(() => {
			if (notification.parentNode) notification.parentNode.removeChild(notification);
		}, 3e3);
	}
	_saveDocumentationLive() {
		if (!this._currentElement || !this._modeling) return;
		const textarea = document.getElementById("doc-textarea");
		if (!textarea) return;
		const documentation = textarea.value;
		try {
			const documentationArray = [];
			if (documentation.trim()) {
				const docElement = this._moddle.create("bpmn:Documentation", { text: documentation });
				documentationArray.push(docElement);
			}
			this._modeling.updateProperties(this._currentElement, { documentation: documentationArray });
		} catch (error) {
			console.error("Error saving documentation:", error);
		}
	}
	_updateElementMetadata() {
		if (!this._currentElement) return;
		const businessObject = this._currentElement.businessObject;
		const elementId = businessObject.id || "Unknown ID";
		const elementNameElement = document.getElementById("element-name");
		if (elementNameElement) elementNameElement.textContent = elementId;
	}
	_getElementTypeName(element) {
		if (is(element, "bpmn:Task")) return "Task";
		if (is(element, "bpmn:UserTask")) return "User Task";
		if (is(element, "bpmn:ServiceTask")) return "Service Task";
		if (is(element, "bpmn:ScriptTask")) return "Script Task";
		if (is(element, "bpmn:CallActivity")) return "Call Activity";
		if (is(element, "bpmn:SubProcess")) return "Sub Process";
		if (is(element, "bpmn:StartEvent")) return "Start Event";
		if (is(element, "bpmn:EndEvent")) return "End Event";
		if (is(element, "bpmn:IntermediateThrowEvent")) return "Intermediate Event";
		if (is(element, "bpmn:IntermediateCatchEvent")) return "Intermediate Event";
		if (is(element, "bpmn:Gateway")) return "Gateway";
		if (is(element, "bpmn:ExclusiveGateway")) return "Exclusive Gateway";
		if (is(element, "bpmn:ParallelGateway")) return "Parallel Gateway";
		if (is(element, "bpmn:InclusiveGateway")) return "Inclusive Gateway";
		if (is(element, "bpmn:SequenceFlow")) return "Sequence Flow";
		if (is(element, "bpmn:MessageFlow")) return "Message Flow";
		if (is(element, "bpmn:DataObject")) return "Data Object";
		if (is(element, "bpmn:DataStore")) return "Data Store";
		if (is(element, "bpmn:Lane")) return "Lane";
		if (is(element, "bpmn:Participant")) return "Pool";
		if (is(element, "bpmn:Process")) return "Process";
		return "Element";
	}
	_handleDiagramImport() {
		this._currentElement = null;
		this._sidebarManager.hideSidebar();
		this._sidebarManager.initializeSidebar();
	}
	_handleDiagramDestroy() {
		this._cleanup();
	}
	_handleDiagramClear() {
		this._currentElement = null;
		this._sidebarManager.hideSidebar();
	}
	_cleanup() {
		this._currentElement = null;
		this._sidebarManager.hideSidebar();
	}
	destroy() {
		this._cleanup();
		this._sidebarManager.destroy();
		this._viewManager.destroy();
		this._autocompleteManager.destroy();
		this._exportManager.destroy();
	}
};
var extension_default = {
	__init__: ["documentationExtension"],
	documentationExtension: [
		"type",
		DocumentationExtension,
		"eventBus",
		"elementRegistry",
		"injector",
		"moddle",
		"selection",
		"canvas"
	]
};

//#endregion
export { extension_default as DocumentationExtension };
//# sourceMappingURL=bpmn-js-entry.js.map