проработай логику и механику интерфейса исправь логические ошибки  и напиши весь код целиком исправленый <!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8">
  <title>Визуальный Конструктор Скриптов на Python</title>
  <style>
    body {
      margin: 0;
      display: flex;
      flex-direction: column;
      height: 100vh;
      font-family: Arial, sans-serif;
    }
    #menu {
      background: #f1f1f1;
      padding: 10px;
      display: flex;
      border-bottom: 1px solid #ccc;
    }
    #menu button {
      margin-right: 10px;
      padding: 8px 15px;
      background-color: #337ab7;
      color: #fff;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      transition: background 0.3s;
    }
    #menu button:hover {
      background-color: #286090;
    }
    #content {
      display: flex;
      flex-grow: 1;
    }
    #sidebar {
      width: 200px;
      background: #f7f7f7;
      padding: 10px;
      overflow-y: auto;
      border-right: 1px solid #ccc;
      box-sizing: border-box;
    }
    #sidebar h3,
    #sidebar h4 {
      margin-top: 0;
    }
    #custom-elements {
      margin-top: 20px;
    }
    #properties {
      width: 250px;
      background: #f7f7f7;
      padding: 10px;
      overflow-y: auto;
      border-left: 1px solid #ccc;
      box-sizing: border-box;
    }
    #properties h3 {
      margin-top: 0;
    }
    #properties-content {
      margin-top: 10px;
    }
    .element-properties {
      margin-bottom: 20px;
      border-bottom: 1px solid #ccc;
      padding-bottom: 10px;
    }
    .element-properties h4 {
      margin: 0 0 10px 0;
    }
    #main-content {
      flex-grow: 1;
      display: flex;
    }
    #workspace-container {
      flex-grow: 1;
      display: flex;
      flex-direction: column;
    }
    #workspace {
      position: relative;
      background: #fff;
      overflow: auto;
      flex-grow: 1;
    }
    #selection-rectangle {
      position: absolute;
      border: 1px dashed #5bc0de;
      background: rgba(91, 192, 222, 0.1);
      display: none;
      pointer-events: none;
    }
    #console {
      background: #232323;
      color: #eaeaea;
      font-family: monospace;
      padding: 10px;
      overflow-y: auto;
      box-sizing: border-box;
      height: 150px;
    }
    #console h3 {
      margin-top: 0;
    }
    #console pre {
      white-space: pre-wrap;
    }
    .element {
      background: #d9edf7;
      margin: 5px 0;
      padding: 8px;
      border: 1px solid #bce8f1;
      border-radius: 4px;
      cursor: grab;
      user-select: none;
      transition: background 0.3s, transform 0.3s;
    }
    .element:hover {
      background: #c4e3f3;
      transform: scale(1.05);
    }
    .workspace-element {
      position: absolute;
      background: #fefefe;
      padding: 10px;
      border: 1px solid #ddd;
      border-radius: 4px;
      cursor: move;
      user-select: none;
      min-width: 150px;
      text-align: center;
      transition: box-shadow 0.2s, transform 0.3s;
      animation: fadeIn 0.5s;
    }
    .workspace-element:hover {
      box-shadow: 0 0 5px rgba(0,0,0,0.3);
      transform: scale(1.05);
    }
    .workspace-element.selected {
      border-color: #5cb85c;
      box-shadow: 0 0 10px rgba(92, 184, 92, 0.5);
    }
    @keyframes fadeIn {
      from { opacity: 0; transform: scale(0.8); }
      to { opacity: 1; transform: scale(1); }
    }
    .connector {
      width: 12px;
      height: 12px;
      background: #5bc0de;
      border: 1px solid #46b8da;
      border-radius: 50%;
      position: absolute;
      cursor: pointer;
      transition: background 0.2s;
    }
    .connector:hover {
      background: #31b0d5;
    }
    .connector.connecting {
      background: #d9534f;
    }
    .connector.active {
      background: #5cb85c;
    }
    .connector.input {
      left: -6px;
      top: 50%;
      transform: translateY(-50%);
    }
    .connector.output {
      right: -6px;
      top: 50%;
      transform: translateY(-50%);
    }
    .connector::after {
      content: attr(data-tooltip);
      position: absolute;
      left: 50%;
      bottom: 140%;
      transform: translateX(-50%);
      background: #333;
      color: #fff;
      padding: 2px 5px;
      border-radius: 3px;
      opacity: 0;
      white-space: nowrap;
      pointer-events: none;
      transition: opacity 0.2s;
      font-size: 12px;
      z-index: 10;
    }
    .connector:hover::after {
      opacity: 1;
    }
    #connections {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      pointer-events: none;
    }
    .line {
      stroke: #337ab7;
      stroke-width: 2;
      transition: stroke 0.2s;
    }
    .line:hover {
      stroke: #d9534f;
    }
    .line.active {
      stroke: #5cb85c;
    }
    #temp-line {
      stroke: #5bc0de;
      stroke-width: 2;
      stroke-dasharray: 5,5;
    }
    .marker {
      stroke: none;
      fill: #337ab7;
    }
    #properties-content form {
      display: flex;
      flex-direction: column;
    }
    #properties-content label {
      margin-bottom: 10px;
      display: flex;
      flex-direction: column;
    }
    #properties-content input[type="text"],
    #properties-content textarea,
    #properties-content select {
      width: 100%;
      padding: 5px;
      box-sizing: border-box;
    }
    #properties-content textarea {
      height: 60px;
    }
    #properties-content button {
      padding: 8px 15px;
      background-color: #5cb85c;
      color: #fff;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      margin-top: 10px;
      transition: background 0.3s;
    }
    #properties-content button:hover {
      background-color: #4cae4c;
    }
    #modal-overlay,
    #manage-elements-modal-overlay {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(0, 0, 0, 0.5);
      z-index: 100;
      display: none;
    }
    #modal,
    #manage-elements-modal {
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background: #fff;
      padding: 20px;
      border-radius: 4px;
      z-index: 101;
      width: 400px;
      max-width: 90%;
    }
    #modal h3,
    #manage-elements-modal h3 {
      margin-top: 0;
    }
    #modal form,
    #manage-elements-modal form {
      display: flex;
      flex-direction: column;
    }
    #modal label,
    #manage-elements-modal label {
      margin-bottom: 10px;
    }
    #modal input[type="text"],
    #modal textarea,
    #modal select,
    #manage-elements-modal input[type="text"],
    #manage-elements-modal textarea,
    #manage-elements-modal select {
      width: 100%;
      padding: 5px;
      box-sizing: border-box;
    }
    #modal textarea,
    #manage-elements-modal textarea {
      height: 80px;
    }
    #modal button,
    #manage-elements-modal button {
      padding: 8px 15px;
      background-color: #337ab7;
      color: #fff;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      margin-top: 10px;
      transition: background 0.3s;
    }
    #modal button:hover,
    #manage-elements-modal button:hover {
      background-color: #286090;
    }
    #close-modal,
    #close-manage-modal {
      background-color: #d9534f;
    }
    #close-modal:hover,
    #close-manage-modal:hover {
      background-color: #c9302c;
    }
    #custom-elements-list {
      list-style: none;
      padding: 0;
      margin: 0;
      max-height: 300px;
      overflow-y: auto;
    }
    #custom-elements-list li {
      padding: 10px;
      border-bottom: 1px solid #ccc;
      position: relative;
    }
    #custom-elements-list li:hover {
      background-color: #f1f1f1;
    }
    .edit-button,
    .delete-button {
      position: absolute;
      top: 10px;
      right: 70px;
      padding: 5px 10px;
      background-color: #5bc0de;
      color: #fff;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      margin-right: 5px;
    }
    .delete-button {
      right: 10px;
      background-color: #d9534f;
    }
    .edit-button:hover {
      background-color: #31b0d5;
    }
    .delete-button:hover {
      background-color: #c9302c;
    }
    #copy-code {
      padding: 8px 15px;
      background-color: #5bc0de;
      color: #fff;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      margin: 10px;
      transition: background 0.3s;
    }
    #copy-code:hover {
      background-color: #31b0d5;
    }
  </style>
</head>
<body>
  <div id="menu">
    <button id="save-project">Сохранить проект</button>
    <button id="load-project">Загрузить проект</button>
    <button id="create-element">Создать элемент</button>
    <button id="manage-elements">Управлять элементами</button>
    <button id="copy-code">Копировать код</button>
  </div>

  <div id="content">
    <div id="sidebar">
      <h3>Элементы</h3>
      <div class="element" data-type="variable" draggable="true">Переменная</div>
      <div class="element" data-type="condition" draggable="true">Условие</div>
      <div class="element" data-type="action" draggable="true">Действие</div>
      <div class="element" data-type="input" draggable="true">Логический Вход</div>
      <div class="element" data-type="output" draggable="true">Логический Выход</div>
      <div class="element" data-type="loop" draggable="true">Цикл</div>
      <div class="element" data-type="function" draggable="true">Функция</div>
      <div class="element" data-type="array" draggable="true">Массив</div>
      <div class="element" data-type="class" draggable="true">Класс</div>
      <div class="element" data-type="file" draggable="true">Файл</div>
      <div class="element" data-type="comment" draggable="true">Комментарий</div>
      <div class="element" data-type="exception" draggable="true">Исключение</div>
      <div id="custom-elements">
        <h4>Пользовательские элементы</h4>
      </div>
    </div>

    <div id="main-content">
      <div id="workspace-container">
        <div id="workspace">
          <svg id="connections"></svg>
          <div id="selection-rectangle"></div>
        </div>
        <div id="console">
          <h3>Результат Кода</h3>
          <pre id="console-output"></pre>
        </div>
      </div>
      <div id="properties">
        <h3>Свойства</h3>
        <div id="properties-content">
          <p>Выберите элемент, чтобы изменить его свойства.</p>
        </div>
      </div>
    </div>
  </div>

  <div id="modal-overlay">
    <div id="modal">
      <h3 id="modal-title">Создать новый элемент</h3>
      <form id="create-element-form">
        <label>
          Имя элемента:
          <input type="text" id="custom-element-name" required>
        </label>
        <label>
          Параметры элемента (в формате JSON):
          <textarea id="custom-element-params" placeholder='{"param1": "value1", "param2": "value2"}' required></textarea>
        </label>
        <label>
          Шаблон кода:
          <textarea id="custom-element-code-template" placeholder='код шаблона с параметрами типа {param1}, {param2}' required></textarea>
        </label>
        <label>
          Точки соединения:
          <select id="custom-element-connectors">
            <option value="input-output">Вход и Выход</option>
            <option value="input">Только Вход</option>
            <option value="output">Только Выход</option>
            <option value="none">Без точек</option>
          </select>
        </label>
        <button type="submit" id="create-element-button">Создать</button>
        <button type="button" id="close-modal">Отмена</button>
      </form>
    </div>
  </div>

  <div id="manage-elements-modal-overlay">
    <div id="manage-elements-modal">
      <h3>Управление элементами</h3>
      <ul id="custom-elements-list">
      </ul>
      <button type="button" id="close-manage-modal">Закрыть</button>
    </div>
  </div>

  <script>
    const menu = document.getElementById('menu');
    const saveProjectBtn = document.getElementById('save-project');
    const loadProjectBtn = document.getElementById('load-project');
    const createElementBtn = document.getElementById('create-element');
    const manageElementsBtn = document.getElementById('manage-elements');
    const copyCodeBtn = document.getElementById('copy-code');
    const sidebar = document.getElementById('sidebar');
    const workspace = document.getElementById('workspace');
    const consoleOutput = document.getElementById('console-output');
    const connectionsSvg = document.getElementById('connections');
    const propertiesPanel = document.getElementById('properties-content');
    const selectionRectangle = document.getElementById('selection-rectangle');
    const customElementsContainer = document.getElementById('custom-elements');
    const modalOverlay = document.getElementById('modal-overlay');
    const modal = document.getElementById('modal');
    const modalTitle = document.getElementById('modal-title');
    const createElementForm = document.getElementById('create-element-form');
    const createElementButton = document.getElementById('create-element-button');
    const closeModalBtn = document.getElementById('close-modal');
    const manageElementsModalOverlay = document.getElementById('manage-elements-modal-overlay');
    const manageElementsModal = document.getElementById('manage-elements-modal');
    const customElementsList = document.getElementById('custom-elements-list');
    const closeManageModalBtn = document.getElementById('close-manage-modal');

    let elements = [];
    let connections = [];
    let selectedElements = [];
    let customElements = [];
    let editingElementIndex = null;

    const tempLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
    tempLine.setAttribute('id', 'temp-line');
    connectionsSvg.appendChild(tempLine);

    const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
    const markerElem = document.createElementNS('http://www.w3.org/2000/svg', 'marker');
    markerElem.setAttribute('id', 'arrowhead');
    markerElem.setAttribute('markerWidth', '10');
    markerElem.setAttribute('markerHeight', '7');
    markerElem.setAttribute('refX', '10');
    markerElem.setAttribute('refY', '3.5');
    markerElem.setAttribute('orient', 'auto');

    const arrowPath = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
    arrowPath.setAttribute('points', '0 0, 10 3.5, 0 7');
    arrowPath.setAttribute('class', 'marker');

    markerElem.appendChild(arrowPath);
    defs.appendChild(markerElem);
    connectionsSvg.appendChild(defs);

    saveProjectBtn.addEventListener('click', saveProject);
    loadProjectBtn.addEventListener('click', loadProject);
    createElementBtn.addEventListener('click', openCreateElementModal);
    manageElementsBtn.addEventListener('click', openManageElementsModal);
    copyCodeBtn.addEventListener('click', copyCodeToClipboard);

    function saveProject() {
      const projectData = {
        elements: elements.map(el => ({
          id: el.id,
          type: el.type,
          x: el.x,
          y: el.y,
          properties: el.properties,
          custom: el.custom || false,
        })),
        connections,
        customElements,
      };
      localStorage.setItem('visualScriptProject', JSON.stringify(projectData));
      alert('Проект сохранен!');
    }

    function loadProject() {
      const projectData = JSON.parse(localStorage.getItem('visualScriptProject'));
      if (projectData) {
        clearWorkspace();
        customElements = projectData.customElements || [];
        saveCustomElementsToLocalStorage();
        customElementsContainer.innerHTML = '<h4>Пользовательские элементы</h4>';
        customElements.forEach(customEl => addCustomElementToSidebar(customEl));
        elements = projectData.elements.map(elData => {
          const elem = createWorkspaceElement(elData.type, elData.x, elData.y, elData.id, elData.custom);
          elem.properties = elData.properties;
          updateElementContent(elem.domElement, elem);
          return elem;
        });

        connections = projectData.connections;
        drawConnections();
        alert('Проект загружен!');
        generateCode();
      } else {
        alert('Нет сохраненного проекта.');
      }
    }

    function clearWorkspace() {
      elements.forEach(el => {
        el.domElement.remove();
      });
      elements = [];
      connections = [];
      drawConnections();
      generateCode();
    }

    function openCreateElementModal() {
      editingElementIndex = null;
      modalTitle.textContent = 'Создать новый элемент';
      createElementButton.textContent = 'Создать';
      modalOverlay.style.display = 'block';
      createElementForm.reset();
    }

    closeModalBtn.addEventListener('click', () => {
      modalOverlay.style.display = 'none';
      createElementForm.reset();
    });

    function saveCustomElementsToLocalStorage() {
      localStorage.setItem('customElements', JSON.stringify(customElements));
    }

    function loadCustomElementsFromLocalStorage() {
      const storedElements = JSON.parse(localStorage.getItem('customElements'));
      if (storedElements) {
        customElements = storedElements;
        customElementsContainer.innerHTML = '<h4>Пользовательские элементы</h4>';
        customElements.forEach(customEl => addCustomElementToSidebar(customEl));
      }
    }

    function addCustomElementToSidebar(customElement) {
      const customElementDiv = document.createElement('div');
      customElementDiv.classList.add('element');
      customElementDiv.textContent = customElement.name;
      customElementDiv.dataset.type = customElement.name;
      customElementDiv.dataset.custom = 'true';
      customElementDiv.draggable = true;
      customElementsContainer.appendChild(customElementDiv);

      customElementDiv.addEventListener('dragstart', (e) => {
        e.dataTransfer.setData('custom-element', JSON.stringify(customElement));
      });
    }

    sidebar.addEventListener('dragstart', (e) => {
      if (e.target.classList.contains('element')) {
        if (e.target.dataset.custom === 'true') {
          e.dataTransfer.setData('custom-element', JSON.stringify(customElements.find(el => el.name === e.target.dataset.type)));
        } else {
          e.dataTransfer.setData('text/plain', e.target.dataset.type);
        }
      }
    });

    workspace.addEventListener('dragover', (e) => {
      e.preventDefault();
    });

    workspace.addEventListener('drop', (e) => {
      e.preventDefault();
      const rect = workspace.getBoundingClientRect();
      const x = e.clientX - rect.left + workspace.scrollLeft;
      const y = e.clientY - rect.top + workspace.scrollTop;
      const customElementData = e.dataTransfer.getData('custom-element');
      if (customElementData) {
        const customElement = JSON.parse(customElementData);
        createWorkspaceElement(customElement.name, x, y, null, true);
      } else {
        const elementType = e.dataTransfer.getData('text/plain');
        createWorkspaceElement(elementType, x, y);
      }
    });

    function createWorkspaceElement(type, x, y, id = null, isCustom = false) {
      const elem = document.createElement('div');
      elem.classList.add('workspace-element');
      elem.dataset.type = type;
      elem.dataset.custom = isCustom;
      elem.style.left = x + 'px';
      elem.style.top = y + 'px';
      const content = document.createElement('div');
      content.textContent = type;
      elem.appendChild(content);
      const connectorsType = isCustom ? (customElements.find(el => el.name === type).connectors || 'input-output') : 'input-output';
      addConnectors(elem, connectorsType);
      const elementId = id || 'elem-' + Date.now() + '-' + Math.floor(Math.random() * 1000);
      elem.dataset.id = elementId;

      const elementData = {
        id: elementId,
        type: type,
        x: x,
        y: y,
        properties: {},
        domElement: elem,
        tempProperties: {},
        custom: isCustom,
      };

      if (isCustom) {
        const customElData = customElements.find(el => el.name === type);
        if (customElData) {
          elementData.properties = { ...customElData.params, codeTemplate: customElData.codeTemplate };
        }
      }

      elements.push(elementData);
      workspace.appendChild(elem);
      makeElementDraggable(elem);

      elem.addEventListener('click', (e) => {
        e.stopPropagation();
        if (!e.shiftKey && !e.ctrlKey) {
          clearSelection();
        }
        selectElement(elem);
      });

      const connectors = elem.querySelectorAll('.connector');
      connectors.forEach(connector => {
        connector.addEventListener('mousedown', (e) => {
          e.preventDefault();
          connectorMouseDown(e, connector);
        });
      });

      generateCode();
      return elementData;
    }

    function addConnectors(elem, connectorsType) {
      const existingConnectors = elem.querySelectorAll('.connector');
      existingConnectors.forEach(connector => connector.remove());

      if (connectorsType === 'input' || connectorsType === 'input-output') {
        const inputConnector = document.createElement('div');
        inputConnector.classList.add('connector', 'input');
        inputConnector.dataset.type = 'input';
        inputConnector.dataset.tooltip = 'Входная точка';
        elem.appendChild(inputConnector);

        inputConnector.addEventListener('mousedown', (e) => {
          e.preventDefault();
          connectorMouseDown(e, inputConnector);
        });
      }

      if (connectorsType === 'output' || connectorsType === 'input-output') {
        const outputConnector = document.createElement('div');
        outputConnector.classList.add('connector', 'output');
        outputConnector.dataset.type = 'output';
        outputConnector.dataset.tooltip = 'Выходная точка';
        elem.appendChild(outputConnector);

        outputConnector.addEventListener('mousedown', (e) => {
          e.preventDefault();
          connectorMouseDown(e, outputConnector);
        });
      }
    }

    function makeElementDraggable(elem) {
      let offsetX, offsetY;

      elem.addEventListener('mousedown', (e) => {
        if (e.target.classList.contains('connector')) {
          return;
        }
        if (!selectedElements.includes(elem)) {
          if (!e.shiftKey && !e.ctrlKey) {
            clearSelection();
          }
          selectElement(elem);
        }
        offsetX = e.clientX;
        offsetY = e.clientY;

        const initialPositions = selectedElements.map((el) => {
          return {
            el: el,
            x: parseInt(el.style.left),
            y: parseInt(el.style.top),
          };
        });

        function mouseMove(eMove) {
          const dx = eMove.clientX - offsetX;
          const dy = eMove.clientY - offsetY;

          initialPositions.forEach((pos) => {
            pos.el.style.left = pos.x + dx + 'px';
            pos.el.style.top = pos.y + dy + 'px';
            const elementData = elements.find((elData) => elData.id === pos.el.dataset.id);
            if (elementData) {
              elementData.x = parseInt(pos.el.style.left);
              elementData.y = parseInt(pos.el.style.top);
            }
          });

          drawConnections();
        }

        function mouseUp() {
          document.removeEventListener('mousemove', mouseMove);
          document.removeEventListener('mouseup', mouseUp);
          generateCode();
        }

        document.addEventListener('mousemove', mouseMove);
        document.addEventListener('mouseup', mouseUp);
      });
    }

    function selectElement(elem) {
      if (!selectedElements.includes(elem)) {
        selectedElements.push(elem);
        elem.classList.add('selected');
        showProperties();
      }
    }

    function clearSelection() {
      selectedElements.forEach((el) => el.classList.remove('selected'));
      selectedElements = [];
      showProperties();
    }

    let isSelecting = false;
    let selectionStart = { x: 0, y: 0 };
    let justSelected = false;

    workspace.addEventListener('mousedown', (e) => {
      if (e.target === workspace) {
        isSelecting = true;
        const rect = workspace.getBoundingClientRect();
        selectionStart.x = e.clientX - rect.left + workspace.scrollLeft;
        selectionStart.y = e.clientY - rect.top + workspace.scrollTop;

        selectionRectangle.style.display = 'block';
        selectionRectangle.style.left = selectionStart.x + 'px';
        selectionRectangle.style.top = selectionStart.y + 'px';
        selectionRectangle.style.width = '0px';
        selectionRectangle.style.height = '0px';
      }
    });

    workspace.addEventListener('mousemove', (e) => {
      if (isSelecting) {
        const rect = workspace.getBoundingClientRect();
        const x = e.clientX - rect.left + workspace.scrollLeft;
        const y = e.clientY - rect.top + workspace.scrollTop;
        const rectX = Math.min(x, selectionStart.x);
        const rectY = Math.min(y, selectionStart.y);
        const rectWidth = Math.abs(x - selectionStart.x);
        const rectHeight = Math.abs(y - selectionStart.y);

        selectionRectangle.style.left = rectX + 'px';
        selectionRectangle.style.top = rectY + 'px';
        selectionRectangle.style.width = rectWidth + 'px';
        selectionRectangle.style.height = rectHeight + 'px';

        clearSelection();

        elements.forEach((elData) => {
          const el = elData.domElement;
          const elRect = el.getBoundingClientRect();

          const workspaceRect = workspace.getBoundingClientRect();

          const elementRect = {
            left: elRect.left - workspaceRect.left + workspace.scrollLeft,
            top: elRect.top - workspaceRect.top + workspace.scrollTop,
            right: elRect.right - workspaceRect.left + workspace.scrollLeft,
            bottom: elRect.bottom - workspaceRect.top + workspace.scrollTop,
          };

          if (rectsIntersect({
            left: rectX,
            top: rectY,
            right: rectX + rectWidth,
            bottom: rectY + rectHeight,
          }, elementRect)) {
            selectElement(el);
          }
        });
      }
    });

    workspace.addEventListener('mouseup', (e) => {
      if (isSelecting) {
        isSelecting = false;
        selectionRectangle.style.display = 'none';
        justSelected = true;
        showProperties();
      }
    });

    workspace.addEventListener('click', (e) => {
      if (e.target === workspace) {
        if (!justSelected) {
          clearSelection();
        }
        justSelected = false;
      }
    });

    function rectsIntersect(rect1, rect2) {
      return !(
        rect2.left > rect1.right ||
        rect2.right < rect1.left ||
        rect2.top > rect1.bottom ||
        rect2.bottom < rect1.top
      );
    }

    function showProperties() {
      propertiesPanel.innerHTML = '';
      if (selectedElements.length === 0) {
        propertiesPanel.innerHTML = '<p>Выберите элемент, чтобы изменить его свойства.</p>';
        return;
      }

      selectedElements.forEach((el) => {
        const elementId = el.dataset.id;
        const elementData = elements.find((elData) => elData.id === elementId);

        if (elementData) {
          const container = document.createElement('div');
          container.classList.add('element-properties');

          const title = document.createElement('h4');
          title.textContent = `${capitalizeFirstLetter(elementData.type)} (${elementId})`;
          container.appendChild(title);

          const form = document.createElement('form');

          if (elementData.custom) {
            for (let paramName in elementData.properties) {
              if (paramName !== 'codeTemplate') {
                createInputField(form, paramName + ':', paramName, elementData.properties[paramName] || '');
              }
            }
          } else {
            switch (elementData.type) {
              case 'variable':
                createInputField(form, 'Имя переменной:', 'name', elementData.properties.name || 'var');
                createSelectField(form, 'Тип переменной:', 'type', elementData.properties.type || 'string', ['string', 'int', 'bool', 'real']);
                createInputField(form, 'Значение переменной:', 'value', elementData.properties.value || '');
                createCheckboxField(form, 'Объявить переменную заранее:', 'declareEarly', elementData.properties.declareEarly || false);
                if (elementData.properties.declareEarly) {
                  createInputField(form, 'Начальное значение:', 'initialValue', elementData.properties.initialValue || '');
                }
                break;
              case 'condition':
                createInputField(form, 'Условие:', 'condition', elementData.properties.condition || '');
                break;
              case 'action':
                createInputField(form, 'Действие:', 'action', elementData.properties.action || '');
                break;
              case 'input':
                createInputField(form, 'Имя входа:', 'name', elementData.properties.name || '');
                break;
              case 'output':
                createInputField(form, 'Имя выхода:', 'name', elementData.properties.name || '');
                break;
              case 'loop':
                createInputField(form, 'Количество итераций:', 'iterations', elementData.properties.iterations || '10');
                createInputField(form, 'Действие внутри цикла:', 'action', elementData.properties.action || '');
                break;
              case 'function':
                createInputField(form, 'Имя функции:', 'name', elementData.properties.name || 'my_function');
                createInputField(form, 'Параметры функции:', 'params', elementData.properties.params || '');
                createInputField(form, 'Тело функции:', 'body', elementData.properties.body || '');
                break;
              case 'array':
                createInputField(form, 'Имя массива:', 'name', elementData.properties.name || 'array');
                createInputField(form, 'Элементы:', 'elements', elementData.properties.elements || '1,2,3');
                break;
              case 'class':
                createInputField(form, 'Имя класса:', 'name', elementData.properties.name || 'MyClass');
                createInputField(form, 'Атрибуты:', 'attributes', elementData.properties.attributes || 'attr1,attr2');
                break;
              case 'file':
                createInputField(form, 'Имя файла:', 'name', elementData.properties.name || 'file.txt');
                createInputField(form, 'Режим:', 'mode', elementData.properties.mode || 'r');
                break;
              case 'comment':
                createInputField(form, 'Текст комментария:', 'text', elementData.properties.text || 'Something important');
                break;
              case 'exception':
                createInputField(form, 'Исключение:', 'exception', elementData.properties.exception || 'Exception');
                createInputField(form, 'Сообщение:', 'message', elementData.properties.message || 'An error occurred');
                break;
              default:
                break;
            }
          }

          const applyButton = document.createElement('button');
          applyButton.textContent = 'Применить';
          form.appendChild(applyButton);

          form.addEventListener('input', (e) => {
            const target = e.target;
            elementData.tempProperties[target.name] = target.type === 'checkbox' ? target.checked : target.value;
            if (target.name === 'declareEarly' && target.type === 'checkbox') {
              showProperties();
            }
          });

          form.addEventListener('submit', (e) => {
            e.preventDefault();
            if (Object.keys(elementData.tempProperties).length > 0) {
              Object.assign(elementData.properties, elementData.tempProperties);
              updateElementContent(el, elementData);
              generateCode();
              elementData.tempProperties = {};
            }
          });

          container.appendChild(form);
          propertiesPanel.appendChild(container);
        }
      });
    }

    function createInputField(form, labelText, propertyName, value) {
      const label = document.createElement('label');
      label.textContent = labelText;
      const input = document.createElement('input');
      input.type = 'text';
      input.name = propertyName;
      input.value = value;
      label.appendChild(input);
      form.appendChild(label);
    }

    function createSelectField(form, labelText, propertyName, selectedValue, options) {
      const label = document.createElement('label');
      label.textContent = labelText;
      const select = document.createElement('select');
      select.name = propertyName;
      options.forEach(optionValue => {
        const option = document.createElement('option');
        option.value = optionValue;
        option.textContent = optionValue;
        if (optionValue === selectedValue) {
          option.selected = true;
        }
        select.appendChild(option);
      });
      label.appendChild(select);
      form.appendChild(label);
    }

    function createCheckboxField(form, labelText, propertyName, isChecked) {
      const label = document.createElement('label');
      label.textContent = labelText;
      const input = document.createElement('input');
      input.type = 'checkbox';
      input.name = propertyName;
      input.checked = isChecked;
      label.appendChild(input);
      form.appendChild(label);
    }

    function updateElementContent(elem, elementData) {
      const content = elem.querySelector('div');
      if (elementData.custom) {
        content.textContent = elementData.type;
      } else {
        switch (elementData.type) {
          case 'variable':
            content.textContent = `${elementData.properties.name} = ${elementData.properties.value}`;
            break;
          case 'condition':
            content.textContent = `if ${elementData.properties.condition}:`;
            break;
          case 'action':
            content.textContent = `${elementData.properties.action}`;
            break;
          case 'input':
            content.textContent = `${elementData.properties.name} = input()`;
            break;
          case 'output':
            content.textContent = `print(${elementData.properties.name})`;
            break;
          case 'loop':
            content.textContent = `for i in range(${elementData.properties.iterations}):`;
            break;
          case 'function':
            content.textContent = `def ${elementData.properties.name}(${elementData.properties.params}): ...`;
            break;
          case 'array':
            content.textContent = `${elementData.properties.name} = [${elementData.properties.elements}]`;
            break;
          case 'class':
            content.textContent = `class ${elementData.properties.name}: ...`;
            break;
          case 'file':
            content.textContent = `open(${elementData.properties.name}, ${elementData.properties.mode})`;
            break;
          case 'comment':
            content.textContent = `# ${elementData.properties.text}`;
            break;
          case 'exception':
            content.textContent = `raise ${elementData.properties.exception}("${elementData.properties.message}")`;
            break;
          default:
            break;
        }
      }
    }

    let isConnecting = false;
    let startConnector = null;

    function connectorMouseDown(e, connector) {
      e.preventDefault();
      isConnecting = true;
      startConnector = connector;
      startConnector.classList.add('connecting');
      const startPos = getConnectorPosition(startConnector);
      tempLine.setAttribute('x1', startPos.x);
      tempLine.setAttribute('y1', startPos.y);
      tempLine.setAttribute('x2', startPos.x);
      tempLine.setAttribute('y2', startPos.y);
      tempLine.style.display = 'block';
      workspace.addEventListener('mousemove', connectorMouseMove);
      document.addEventListener('mouseup', connectorMouseUp);
    }

    function connectorMouseMove(e) {
      const rect = workspace.getBoundingClientRect();
      const x = e.clientX - rect.left + workspace.scrollLeft;
      const y = e.clientY - rect.top + workspace.scrollTop;
      tempLine.setAttribute('x2', x);
      tempLine.setAttribute('y2', y);
    }

    function connectorMouseUp(e) {
      workspace.removeEventListener('mousemove', connectorMouseMove);
      document.removeEventListener('mouseup', connectorMouseUp);

      tempLine.style.display = 'none';
      startConnector.classList.remove('connecting');

      if (e.target.classList.contains('connector') && e.target !== startConnector) {
        const fromConnector = startConnector;
        const toConnector = e.target;

        if (fromConnector.dataset.type !== toConnector.dataset.type) {
          const fromElementId = fromConnector.parentElement.dataset.id;
          const toElementId = toConnector.parentElement.dataset.id;

          let sourceId, targetId;
          let sourceConnectorType = fromConnector.dataset.type;
          let targetConnectorType = toConnector.dataset.type;

          if (fromConnector.dataset.type === 'output') {
            sourceId = fromElementId;
            targetId = toElementId;
          } else {
            sourceId = toElementId;
            targetId = fromElementId;
            sourceConnectorType = toConnector.dataset.type;
            targetConnectorType = fromConnector.dataset.type;
          }

          const existingConnection = connections.find(
            (conn) =>
              conn.from === sourceId &&
              conn.to === targetId &&
              conn.fromConnectorType === sourceConnectorType &&
              conn.toConnectorType === targetConnectorType
          );

          if (!existingConnection) {
            connections.push({
              from: sourceId,
              to: targetId,
              fromConnectorType: sourceConnectorType,
              toConnectorType: targetConnectorType,
            });

            drawConnections();
            generateCode();
          } else {
            alert('Соединение уже существует.');
          }
        } else {
          alert('Неверное соединение. Соединяйте выход с входом.');
        }
      }

      isConnecting = false;
      startConnector = null;
    }

    function drawConnections() {
      connectionsSvg.querySelectorAll('.line').forEach((line) => line.remove());

      connections.forEach((conn) => {
        const fromElem = document.querySelector(`.workspace-element[data-id="${conn.from}"]`);
        const toElem = document.querySelector(`.workspace-element[data-id="${conn.to}"]`);

        const fromConnector = fromElem.querySelector('.connector.output');
        const toConnector = toElem.querySelector('.connector.input');

        if (fromConnector && toConnector) {
          const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');

          const fromPos = getConnectorPosition(fromConnector);
          const toPos = getConnectorPosition(toConnector);

          line.setAttribute('x1', fromPos.x);
          line.setAttribute('y1', fromPos.y);
          line.setAttribute('x2', toPos.x);
          line.setAttribute('y2', toPos.y);
          line.classList.add('line');
          line.setAttribute('marker-end', 'url(#arrowhead)');

          line.addEventListener('click', (e) => {
            e.stopPropagation();
            if (confirm('Удалить соединение?')) {
              const index = connections.findIndex(
                (c) =>
                  c.from === conn.from &&
                  c.to === conn.to &&
                  c.fromConnectorType === conn.fromConnectorType &&
                  c.toConnectorType === conn.toConnectorType
              );
              if (index !== -1) {
                connections.splice(index, 1);
              }

              drawConnections();
              generateCode();
            }
          });

          connectionsSvg.appendChild(line);
        }
      });
    }

    function getConnectorPosition(connector) {
      const rect = connector.getBoundingClientRect();
      const parentRect = workspace.getBoundingClientRect();

      return {
        x: rect.left - parentRect.left + rect.width / 2 + workspace.scrollLeft,
        y: rect.top - parentRect.top + rect.height / 2 + workspace.scrollTop,
      };
    }

    function generateCode() {
      const variableDefinitions = [];
      const codeLines = [];
      const processedVariables = new Set();
      const processedElements = new Set();

      elements.forEach(element => {
        if (element.type === 'variable' && !processedVariables.has(element.id)) {
          processedVariables.add(element.id);
          if (element.properties.declareEarly && element.properties.name) {
            variableDefinitions.push(`${element.properties.name} = ${element.properties.initialValue}`);
          }
        }
      });

      getRoots().forEach(root => {
        generateElementCodePython(root, codeLines, 0, processedElements);
      });

      consoleOutput.textContent = [...variableDefinitions, ...codeLines].join('\n');
    }

    function getRoots() {
      return elements.filter(el => connections.every(conn => conn.to !== el.id));
    }

    function getChildren(elementId) {
      return connections
        .filter(conn => conn.from === elementId)
        .map(conn => elements.find(el => el.id === conn.to))
        .filter(el => el);
    }

    function generateElementCodePython(element, codeLines, indentLevel, processedElements) {
      if (processedElements.has(element.id)) return;
      processedElements.add(element.id);

      const indent = '  '.repeat(indentLevel);
      switch (element.type) {
        case 'variable':
          if (!element.properties.declareEarly) {
            codeLines.push(`${indent}${element.properties.name} = ${element.properties.value}`);
          }
          break;
        case 'condition':
          if (element.properties.condition) {
            codeLines.push(`${indent}if ${element.properties.condition}:`);
            const children = getChildren(element.id);
            if (children.length === 0) {
              codeLines.push(`${indent}  pass`);
            } else {
              children.forEach(child => generateElementCodePython(child, codeLines, indentLevel + 1, processedElements));
            }
          }
          break;
        case 'action':
          if (element.properties.action) {
            codeLines.push(`${indent}${element.properties.action}`);
          }
          break;
        case 'input':
          if (element.properties.name) {
            codeLines.push(`${indent}${element.properties.name} = input()`);
          }
          break;
        case 'output':
          if (element.properties.name) {
            codeLines.push(`${indent}print(${element.properties.name})`);
          }
          break;
        case 'loop':
          if (element.properties.iterations) {
            codeLines.push(`${indent}for i in range(${element.properties.iterations}):`);
            const loopChildren = getChildren(element.id);
            if (loopChildren.length === 0) {
              codeLines.push(`${indent}  pass`);
            } else {
              loopChildren.forEach(child => generateElementCodePython(child, codeLines, indentLevel + 1, processedElements));
            }
          }
          break;
        case 'function':
          if (element.properties.name) {
            const params = element.properties.params || '';
            const body = element.properties.body || 'pass';
            codeLines.push(`${indent}def ${element.properties.name}(${params}):`);
            codeLines.push(`${indent}  ${body}`);
          }
          break;
        case 'array':
          if (element.properties.name) {
            const elements = element.properties.elements || '';
            codeLines.push(`${indent}${element.properties.name} = [${elements}]`);
          }
          break;
        case 'class':
          if (element.properties.name) {
            const attributes = element.properties.attributes || '';
            codeLines.push(`${indent}class ${element.properties.name}:`);
            attributes.split(',').forEach(attr => {
              codeLines.push(`${indent}  def __init__(self, ${attr}):`);
              codeLines.push(`${indent}    self.${attr} = ${attr}`);
            });
          }
          break;
        case 'file':
          if (element.properties.name && element.properties.mode) {
            codeLines.push(`${indent}with open('${element.properties.name}', '${element.properties.mode}') as file:`);
            codeLines.push(`${indent}  pass  # работа с файлом`);
          }
          break;
        case 'comment':
          if (element.properties.text) {
            codeLines.push(`${indent}# ${element.properties.text}`);
          }
          break;
        case 'exception':
          if (element.properties.exception) {
            const message = element.properties.message || '';
            codeLines.push(`${indent}raise ${element.properties.exception}("${message}")`);
          }
          break;
        default:
          if (element.custom) {
            const customCode = generateCustomElementCodePython(element, indent);
            if (customCode) {
              codeLines.push(`${indent}${customCode}`);
            }
          }
          break;
      }
    }

    function generateCustomElementCodePython(element, indent) {
      const codeTemplate = element.properties.codeTemplate;
      if (codeTemplate) {
        let code = codeTemplate;
        for (const param in element.properties) {
          if (param !== 'codeTemplate') {
            const regex = new RegExp(`{${param}}`, 'g');
            code = code.replace(regex, element.properties[param]);
          }
        }
        return `${code}`;
      }
      return '';
    }

    function capitalizeFirstLetter(string) {
      return string.charAt(0).toUpperCase() + string.slice(1);
    }

    workspace.addEventListener('contextmenu', (e) => {
      e.preventDefault();
      let targetElem = null;
      if (e.target.classList.contains('workspace-element')) {
        targetElem = e.target;
      } else if (e.target.parentElement.classList.contains('workspace-element')) {
        targetElem = e.target.parentElement;
      }

      if (targetElem) {
        const elementId = targetElem.dataset.id;

        if (confirm('Удалить элемент и все связанные с ним соединения?')) {
          targetElem.style.transition = 'opacity 0.5s, transform 0.5s';
          targetElem.style.opacity = '0';
          targetElem.style.transform = 'scale(0.5)';

          setTimeout(() => {
            const elementIndex = elements.findIndex((el) => el.id === elementId);
            if (elementIndex !== -1) {
              elements.splice(elementIndex, 1);
            }

            connections = connections.filter(conn => conn.from !== elementId && conn.to !== elementId);
            targetElem.remove();

            if (selectedElements.includes(targetElem)) {
              selectedElements = selectedElements.filter((el) => el !== targetElem);
              showProperties();
            }

            drawConnections();
            generateCode();
          }, 500);
        }
      }
    });

    function openManageElementsModal() {
      manageElementsModalOverlay.style.display = 'block';
      renderCustomElementsList();
    }

    closeManageModalBtn.addEventListener('click', () => {
      manageElementsModalOverlay.style.display = 'none';
      customElementsList.innerHTML = '';
    });

    function renderCustomElementsList() {
      customElementsList.innerHTML = '';
      customElements.forEach((el, index) => {
        const li = document.createElement('li');
        li.textContent = el.name;
        const editBtn = document.createElement('button');
        editBtn.textContent = 'Редактировать';
        editBtn.classList.add('edit-button');
        editBtn.addEventListener('click', () => {
          openEditCustomElementModal(index);
        });

        const deleteBtn = document.createElement('button');
        deleteBtn.textContent = 'Удалить';
        deleteBtn.classList.add('delete-button');
        deleteBtn.addEventListener('click', () => {
          if (confirm(`Вы уверены, что хотите удалить элемент "${el.name}"?`)) {
            customElements.splice(index, 1);
            saveCustomElementsToLocalStorage();

            elements = elements.filter((elData) => {
              if (elData.custom && elData.type === el.name) {
                elData.domElement.remove();
                return false;
              }
              return true;
            });

            connections = connections.filter(conn => {
              const fromExists = elements.some(el => el.id === conn.from);
              const toExists = elements.some(el => el.id === conn.to);
              return fromExists && toExists;
            });

            customElementsContainer.innerHTML = '<h4>Пользовательские элементы</h4>';
            customElements.forEach(customEl => addCustomElementToSidebar(customEl));

            drawConnections();
            generateCode();
            renderCustomElementsList();
          }
        });

        li.appendChild(editBtn);
        li.appendChild(deleteBtn);
        customElementsList.appendChild(li);
      });
    }

    function openEditCustomElementModal(index) {
      const el = customElements[index];
      document.getElementById('custom-element-name').value = el.name;
      document.getElementById('custom-element-params').value = JSON.stringify(el.params, null, 2);
      document.getElementById('custom-element-code-template').value = el.codeTemplate;
      document.getElementById('custom-element-connectors').value = el.connectors;
      editingElementIndex = index;
      modalTitle.textContent = 'Редактировать элемент';
      createElementButton.textContent = 'Сохранить';
      createElementButton.dataset.editing = 'true';

      manageElementsModalOverlay.style.display = 'none';
      modalOverlay.style.display = 'block';
    }

    function handleCreateElementSubmit(e) {
      e.preventDefault();
      const name = document.getElementById('custom-element-name').value.trim();
      const paramsText = document.getElementById('custom-element-params').value.trim();
      const codeTemplate = document.getElementById('custom-element-code-template').value.trim();
      const connectors = document.getElementById('custom-element-connectors').value;

      if (!name || !paramsText || !codeTemplate) {
        alert('Пожалуйста, заполните все поля.');
        return;
      }

      let params;
      try {
        params = JSON.parse(paramsText);
      } catch (error) {
        alert('Ошибка в формате параметров. Используйте корректный JSON.');
        return;
      }

      if (editingElementIndex !== null) {
        if (customElements.some((el, idx) => el.name === name && idx !== editingElementIndex)) {
          alert('Элемент с таким именем уже существует.');
          return;
        }

        customElements[editingElementIndex] = {
          name,
          params,
          codeTemplate,
          connectors,
        };
        saveCustomElementsToLocalStorage();

        customElementsContainer.innerHTML = '<h4>Пользовательские элементы</h4>';
        customElements.forEach(customEl => addCustomElementToSidebar(customEl));

        elements.forEach((elData) => {
          if (elData.custom && elData.type === name) {
            elData.properties = { ...params, codeTemplate: elData.codeTemplate };
            addConnectors(elData.domElement, connectors);
            updateElementContent(elData.domElement, elData);
          }
        });

        drawConnections();
        generateCode();

        editingElementIndex = null;
        modalTitle.textContent = 'Создать новый элемент';
        createElementButton.textContent = 'Создать';
        delete createElementButton.dataset.editing;

        modalOverlay.style.display = 'none';
        createElementForm.reset();

        manageElementsModalOverlay.style.display = 'block';
        renderCustomElementsList();
      } else {
        if (customElements.some(el => el.name === name)) {
          alert('Элемент с таким именем уже существует.');
          return;
        }

        const customElement = {
          name,
          params,
          codeTemplate,
          connectors,
        };

        customElements.push(customElement);
        saveCustomElementsToLocalStorage();
        addCustomElementToSidebar(customElement);

        modalOverlay.style.display = 'none';
        createElementForm.reset();
      }
    }

    createElementForm.addEventListener('submit', handleCreateElementSubmit);

    loadCustomElementsFromLocalStorage();

    function copyCodeToClipboard() {
      const code = consoleOutput.textContent;
      if (code.trim() === '') {
        alert('Нет кода для копирования.');
        return;
      }
      navigator.clipboard.writeText(code).then(() => {
        alert('Код скопирован в буфер обмена!');
      }).catch(err => {
        alert('Ошибка при копировании: ' + err);
      });
    }
  </script>
</body>
</html>