add history panel toggle button

This commit is contained in:
dimitrievgs 2025-09-23 23:18:59 +03:00
parent 11c44e38f2
commit 351605a052
3 changed files with 570 additions and 283 deletions

248
main.js

File diff suppressed because one or more lines are too long

View File

@ -2,120 +2,132 @@
/* CSS переменные, чтобы создать атмосферу Шира из "Властелина колец" */ /* CSS переменные, чтобы создать атмосферу Шира из "Властелина колец" */
:root { :root {
/* Цвета сообщений чата */ /* Цвета сообщений чата */
--llm-user-border-color: #79A07E; /* Ширский зеленый */ --llm-user-border-color: #79a07e; /* Ширский зеленый */
--llm-user-background-color: #E6F2E6; /* Очень светлый ширский зеленый */ --llm-user-background-color: #e6f2e6; /* Очень светлый ширский зеленый */
--llm-llm-border-color: #B2A27E; /* Нейтральный, землистый оттенок */ --llm-llm-border-color: #b2a27e; /* Нейтральный, землистый оттенок */
--llm-llm-background-color: #F8F3E9; /* Светлый кремовый */ --llm-llm-background-color: #f8f3e9; /* Светлый кремовый */
--llm-tool-border-color: #B08D57; /* Древесный коричневый */ --llm-tool-border-color: #b08d57; /* Древесный коричневый */
--llm-tool-background-color: #FDF4E3; /* Светлый песочный */ --llm-tool-background-color: #fdf4e3; /* Светлый песочный */
--llm-error-border-color: #C25B5B; /* Приглушенный красный */ --llm-error-border-color: #c25b5b; /* Приглушенный красный */
--llm-error-background-color: #F7E0E0; /* Светлый приглушенный красный */ --llm-error-background-color: #f7e0e0; /* Светлый приглушенный красный */
--llm-assistant-border-color: #9C9C8F; /* Серый камень */ --llm-assistant-border-color: #9c9c8f; /* Серый камень */
--llm-assistant-background-color: #ECECE4; /* Светло-серый, землистый */ --llm-assistant-background-color: #ecece4; /* Светло-серый, землистый */
/* Общие стили узлов графа */ /* Общие стили узлов графа */
--node-padding: 10px; --node-padding: 10px;
--node-border-radius: 5px; --node-border-radius: 5px;
--node-font-size: 14px; --node-font-size: 14px;
--node-font-weight: bold; --node-font-weight: bold;
--node-text-align: center; --node-text-align: center;
/* Переменные для текста */ /* Переменные для текста */
--font-text-size: 14px; --font-text-size: 14px;
--text-normal: #333; --text-normal: #333;
--text-muted: #666; --text-muted: #666;
--background-secondary-alt: #f0f0f0; --background-secondary-alt: #f0f0f0;
--font-monospace: 'Roboto Mono', monospace; --font-monospace: 'Roboto Mono', monospace;
--text-accent: #007bff; --text-accent: #007bff;
/* Цвета для кнопок и интерактивных элементов */ /* Цвета для кнопок и интерактивных элементов */
--button-primary-background: #5A8F6A; /* Насыщенный ширский зеленый для кнопок */ --button-primary-background: #5a8f6a; /* Насыщенный ширский зеленый для кнопок */
--button-primary-hover-background: #4A7A5C; /* Темнее для наведения */ --button-primary-hover-background: #4a7a5c; /* Темнее для наведения */
--button-primary-text-color: #FFFFFF; /* Белый текст */ --button-primary-text-color: #ffffff; /* Белый текст */
--button-secondary-background: #D9D1BF; /* Светлый землистый для второстепенных кнопок */ --button-secondary-background: #d9d1bf; /* Светлый землистый для второстепенных кнопок */
--button-secondary-hover-background: #C4BDA9; /* Темнее для наведения */ --button-secondary-hover-background: #c4bda9; /* Темнее для наведения */
--button-secondary-text-color: #333333; /* Темный текст для второстепенных */ --button-secondary-text-color: #333333; /* Темный текст для второстепенных */
--border-color-light: #D3CBBD; /* Светлая граница, землистая */ --border-color-light: #d3cbbd; /* Светлая граница, землистая */
--border-color-dark: #A99B87; /* Темная граница, землистая */ --border-color-dark: #a99b87; /* Темная граница, землистая */
--background-modifier-form-field: #F5EFE1; /* Светлый земляной фон для полей */ --background-modifier-form-field: #f5efe1; /* Светлый земляной фон для полей */
--background-modifier-form-field-highlighted: #E4DCCF; /* Немного темнее для выделенных полей */ --background-modifier-form-field-highlighted: #e4dccf; /* Немного темнее для выделенных полей */
--node-text-color: var(--text-normal); /* Используем общую переменную для текста узлов */ --node-text-color: var(
--text-normal
); /* Используем общую переменную для текста узлов */
} }
.llm-agent-chat-panel .message-type-user { .llm-agent-chat-panel .message-type-user {
border: 1px solid var(--llm-user-border-color); /* Цвет для user */ border: 1px solid var(--llm-user-border-color); /* Цвет для user */
background-color: var(--llm-user-background-color); /* Светло-голубой фон для user */ background-color: var(
--llm-user-background-color
); /* Светло-голубой фон для user */
} }
.llm-agent-chat-panel .message-type-llm { .llm-agent-chat-panel .message-type-llm {
border: 1px solid var(--llm-llm-border-color); /* Цвет для llm */ border: 1px solid var(--llm-llm-border-color); /* Цвет для llm */
background-color: var(--llm-llm-background-color); /* Светло-зеленый фон для llm */ background-color: var(
--llm-llm-background-color
); /* Светло-зеленый фон для llm */
} }
.llm-agent-chat-panel .message-type-tool { .llm-agent-chat-panel .message-type-tool {
border: 1px solid var(--llm-tool-border-color); /* Цвет для tool */ border: 1px solid var(--llm-tool-border-color); /* Цвет для tool */
background-color: var(--llm-tool-background-color); /* Светло-оранжевый фон для tool */ background-color: var(
--llm-tool-background-color
); /* Светло-оранжевый фон для tool */
} }
.llm-agent-chat-panel .message-type-error { .llm-agent-chat-panel .message-type-error {
border: 1px solid var(--llm-error-border-color); /* Красный цвет для error */ border: 1px solid var(--llm-error-border-color); /* Красный цвет для error */
background-color: var(--llm-error-background-color); /* Светло-красный фон для error */ background-color: var(
--llm-error-background-color
); /* Светло-красный фон для error */
} }
.llm-agent-chat-panel .message-type-assistant { .llm-agent-chat-panel .message-type-assistant {
border: 1px solid var(--llm-assistant-border-color); /* Цвет для assistant */ border: 1px solid var(--llm-assistant-border-color); /* Цвет для assistant */
background-color: var(--llm-assistant-background-color); /* Светло-серый фон для assistant */ background-color: var(
--llm-assistant-background-color
); /* Светло-серый фон для assistant */
} }
/* Общие стили для сообщений */ /* Общие стили для сообщений */
.llm-agent-chat-panel .message { .llm-agent-chat-panel .message {
margin-bottom: 10px; margin-bottom: 10px;
display: flex; /* Flex-контейнер для всего сообщения */ display: flex; /* Flex-контейнер для всего сообщения */
flex-direction: column; /* Элементы внутри сообщения будут выстраиваться вертикально */ flex-direction: column; /* Элементы внутри сообщения будут выстраиваться вертикально */
padding: 10px; /* Общие отступы для всего сообщения */ padding: 10px; /* Общие отступы для всего сообщения */
border-radius: 5px; border-radius: 5px;
max-width: 80%; /* Ограничение ширины сообщения */ max-width: 80%; /* Ограничение ширины сообщения */
word-break: break-word; /* Разбиение слов для длинных сообщений */ word-break: break-word; /* Разбиение слов для длинных сообщений */
overflow-wrap: break-word; /* Перенос слов */ overflow-wrap: break-word; /* Перенос слов */
} }
.llm-agent-chat-panel .message.user { .llm-agent-chat-panel .message.user {
margin-left: auto; /* Выравнивание по правому краю для пользователя */ margin-left: auto; /* Выравнивание по правому краю для пользователя */
} }
.llm-agent-chat-panel .message.assistant { .llm-agent-chat-panel .message.assistant {
margin-right: auto; /* Выравнивание по левому краю для ассистента, если потребуется */ margin-right: auto; /* Выравнивание по левому краю для ассистента, если потребуется */
} }
.chat-meta { .chat-meta {
margin-bottom: 5px; /* Отступ между мета-информацией и содержимым */ margin-bottom: 5px; /* Отступ между мета-информацией и содержимым */
} }
.chat-sender-info { .chat-sender-info {
display: flex; /* Делаем sender-info flex-контейнером */ display: flex; /* Делаем sender-info flex-контейнером */
align-items: center; /* Выравнивание иконки и текста по центру */ align-items: center; /* Выравнивание иконки и текста по центру */
gap: 8px; /* Расстояние между иконкой и текстом */ gap: 8px; /* Расстояние между иконкой и текстом */
} }
.chat-message-icon svg { .chat-message-icon svg {
width: 20px; /* Размер иконки */ width: 20px; /* Размер иконки */
height: 20px; height: 20px;
flex-shrink: 0; /* Предотвращает сжатие иконки */ flex-shrink: 0; /* Предотвращает сжатие иконки */
} }
.chat-role-label { .chat-role-label {
font-weight: bold; font-weight: bold;
font-size: 0.9em; font-size: 0.9em;
color: var(--text-muted); color: var(--text-muted);
} }
.chat-message-content { .chat-message-content {
font-size: var(--font-text-size); font-size: var(--font-text-size);
line-height: 1.5; line-height: 1.5;
color: var(--text-normal); color: var(--text-normal);
user-select: text; /* Разрешить выделение текста внутри содержимого */ user-select: text; /* Разрешить выделение текста внутри содержимого */
} }
/* Стили для узлов графа */ /* Стили для узлов графа */
@ -125,282 +137,304 @@
.react-flow__node-tool, .react-flow__node-tool,
.react-flow__node-error, .react-flow__node-error,
.react-flow__node-assistant { .react-flow__node-assistant {
padding: var(--node-padding); padding: var(--node-padding);
border-radius: var(--node-border-radius); border-radius: var(--node-border-radius);
text-align: var(--node-text-align); text-align: var(--node-text-align);
font-size: var(--node-font-size); font-size: var(--node-font-size);
font-weight: var(--node-font-weight); font-weight: var(--node-font-weight);
color: var(--node-text-color); color: var(--node-text-color);
} }
.react-flow__node-user { .react-flow__node-user {
border: 1px solid var(--llm-user-border-color); /* Цвет для user */ border: 1px solid var(--llm-user-border-color); /* Цвет для user */
background-color: var(--llm-user-background-color); /* Светло-голубой фон для user */ background-color: var(
--llm-user-background-color
); /* Светло-голубой фон для user */
} }
.react-flow__node-llm { .react-flow__node-llm {
border: 1px solid var(--llm-llm-border-color); /* Цвет для llm */ border: 1px solid var(--llm-llm-border-color); /* Цвет для llm */
background-color: var(--llm-llm-background-color); /* Светло-зеленый фон для llm */ background-color: var(
--llm-llm-background-color
); /* Светло-зеленый фон для llm */
} }
.react-flow__node-tool { .react-flow__node-tool {
border: 1px solid var(--llm-tool-border-color); /* Цвет для tool */ border: 1px solid var(--llm-tool-border-color); /* Цвет для tool */
background-color: var(--llm-tool-background-color); /* Светло-оранжевый фон для tool */ background-color: var(
--llm-tool-background-color
); /* Светло-оранжевый фон для tool */
} }
.react-flow__node-error { .react-flow__node-error {
border: 1px solid var(--llm-error-border-color); /* Красный цвет для error */ border: 1px solid var(--llm-error-border-color); /* Красный цвет для error */
background-color: var(--llm-error-background-color); /* Светло-красный фон для error */ background-color: var(
--llm-error-background-color
); /* Светло-красный фон для error */
} }
.react-flow__node-assistant { .react-flow__node-assistant {
border: 1px solid var(--llm-assistant-border-color); /* Нейтральный серый для ассистента */ border: 1px solid var(--llm-assistant-border-color); /* Нейтральный серый для ассистента */
background-color: var(--llm-assistant-background-color); /* Светло-серый фон */ background-color: var(--llm-assistant-background-color); /* Светло-серый фон */
} }
/* ----------------------------------------------- Chat Panel Styles -------------------------------------------------------- */ /* ----------------------------------------------- Chat Panel Styles -------------------------------------------------------- */
.llm-agent-chat-panel { .llm-agent-chat-panel {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
} }
.chat-history { .chat-history {
flex-grow: 1; flex-grow: 1;
overflow-y: auto; overflow-y: auto;
padding: 10px; padding: 10px;
-webkit-user-select: text; /* Разрешить выделение для WebKit-браузеров */ -webkit-user-select: text; /* Разрешить выделение для WebKit-браузеров */
-moz-user-select: text; /* Разрешить выделение для Firefox */ -moz-user-select: text; /* Разрешить выделение для Firefox */
user-select: text; /* Стандартное свойство для выделения текста */ user-select: text; /* Стандартное свойство для выделения текста */
scroll-behavior: smooth; scroll-behavior: smooth;
} }
/* Стили для inline-кода в сообщениях */ /* Стили для inline-кода в сообщениях */
.chat-message-content code { .chat-message-content code {
background-color: var(--background-secondary-alt); background-color: var(--background-secondary-alt);
border-radius: 4px; border-radius: 4px;
padding: 2px 4px; padding: 2px 4px;
font-family: var(--font-monospace); font-family: var(--font-monospace);
font-size: 0.9em; font-size: 0.9em;
color: var(--text-accent); color: var(--text-accent);
} }
/* Стиль для форм ввода чата */ /* Стиль для форм ввода чата */
.chat-input-form { .chat-input-form {
display: flex; display: flex;
padding: 10px; padding: 10px;
border-top: 1px solid #eee; border-top: 1px solid #eee;
gap: 10px; gap: 10px;
} }
.command-input { .command-input {
position: relative; position: relative;
flex-grow: 1; flex-grow: 1;
} }
#message-input { #message-input {
width: 100%; width: 100%;
padding: 8px; padding: 8px;
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 4px; border-radius: 4px;
box-sizing: border-box; box-sizing: border-box;
scroll-margin-bottom: 30px; /* Добавляет отступ в 20px снизу при прокрутке к этому элементу */ scroll-margin-bottom: 30px; /* Добавляет отступ в 20px снизу при прокрутке к этому элементу */
} }
.submit-button { .submit-button {
padding: 4px 8px; /* Восстанавливаем паддинг */ padding: 4px 8px; /* Восстанавливаем паддинг */
border: none; /* Убираем рамку */ border: none; /* Убираем рамку */
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
font-size: 11px; font-size: 11px;
transition: all 0.2s; transition: all 0.2s;
/* Явно переопределяем стили, которые могли быть затронуты */ /* Явно переопределяем стили, которые могли быть затронуты */
width: auto; /* Убедимся, что ширина не ограничена */ width: auto; /* Убедимся, что ширина не ограничена */
height: auto; /* Убедимся, что высота не ограничена */ height: auto; /* Убедимся, что высота не ограничена */
background-color: var(--button-primary-background) !important; /* Используем переменную для основного цвета кнопки */ background-color: var(
color: var(--button-primary-text-color) !important; /* Используем переменную для цвета текста кнопки */ --button-primary-background
opacity: 1; /* Если .clickable-icon устанавливает opacity */ ) !important; /* Используем переменную для основного цвета кнопки */
display: inline-flex; /* Или flex, если нужно выравнивание элементов внутри */ color: var(
align-items: center; /* Для вертикального выравнивания контента, если display: flex */ --button-primary-text-color
justify-content: center; /* Для горизонтального выравнивания контента, если display: flex */ ) !important; /* Используем переменную для цвета текста кнопки */
opacity: 1; /* Если .clickable-icon устанавливает opacity */
display: inline-flex; /* Или flex, если нужно выравнивание элементов внутри */
align-items: center; /* Для вертикального выравнивания контента, если display: flex */
justify-content: center; /* Для горизонтального выравнивания контента, если display: flex */
} }
#send-button:hover { #send-button:hover {
background-color: var(--button-primary-hover-background) !important; /* Переменная для наведения */ background-color: var(
--button-primary-hover-background
) !important; /* Переменная для наведения */
} }
/* Добавляем стили для нового интерфейса ввода */ /* Добавляем стили для нового интерфейса ввода */
.chat-input-container { .chat-input-container {
border-top: 1px solid var(--background-modifier-border); border-top: 1px solid var(--background-modifier-border);
padding: 8px 10px 4px 10px; padding: 8px 10px 4px 10px;
position: relative; position: relative;
} }
.scroll-container { .scroll-container {
overflow-y: auto; overflow-y: auto;
scrollbar-width: thin; scrollbar-width: thin;
max-height: 70vh; max-height: 70vh;
margin-bottom: 8px; margin-bottom: 8px;
} }
.message-input-editor { .message-input-editor {
outline: none; outline: none;
font-size: 14px; font-size: 14px;
line-height: 1.4; line-height: 1.4;
min-height: 20px; min-height: 20px;
padding: 8px 0; padding: 8px 0;
color: var(--text-normal); color: var(--text-normal);
position: relative; position: relative;
} }
.message-input-editor.is-empty::before { .message-input-editor.is-empty::before {
content: attr(data-placeholder); content: attr(data-placeholder);
color: var(--text-muted); color: var(--text-muted);
position: absolute; position: absolute;
pointer-events: none; pointer-events: none;
} }
.chat-controls { .chat-controls {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
font-size: 12px; font-size: 12px;
color: var(--text-muted); color: var(--text-muted);
} }
.control-buttons-left { .control-buttons-left {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
} }
.control-buttons-right { .control-buttons-right {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
} }
.model-select-container { .model-select-container {
position: relative; position: relative;
} }
.model-select-button { .model-select-button {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 4px; gap: 4px;
padding: 4px 8px; padding: 4px 8px;
border: none; border: none;
border-radius: 8px; border-radius: 8px;
background-color: var(--button-secondary-background); /* Используем переменную для второстепенных кнопок */ background-color: var(
color: var(--button-secondary-text-color); /* Используем переменную для текста второстепенных кнопок */ --button-secondary-background
cursor: pointer; ); /* Используем переменную для второстепенных кнопок */
font-size: 11px; color: var(
transition: all 0.2s; --button-secondary-text-color
); /* Используем переменную для текста второстепенных кнопок */
cursor: pointer;
font-size: 11px;
transition: all 0.2s;
} }
.model-select-button:hover { .model-select-button:hover {
background-color: var(--button-secondary-hover-background); /* Используем переменную для наведения второстепенных кнопок */ background-color: var(
filter: brightness(1.1); /* Можно оставить или скорректировать под тему */ --button-secondary-hover-background
); /* Используем переменную для наведения второстепенных кнопок */
filter: brightness(1.1); /* Можно оставить или скорректировать под тему */
} }
.model-name { .model-name {
max-width: 120px; max-width: 120px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.dropdown-icon { .dropdown-icon {
width: 12px; width: 12px;
height: 12px; height: 12px;
flex-shrink: 0; flex-shrink: 0;
} }
.model-dropdown { .model-dropdown {
position: absolute; position: absolute;
/* Убираем жесткое bottom: 100%; т.к. позиционирование будет задаваться в JS */ /* Убираем жесткое bottom: 100%; т.к. позиционирование будет задаваться в JS */
/* bottom: 100%; */ /* bottom: 100%; */
left: 0; left: 0;
/* right: 0; */ /* Это может привести к растягиванию, лучше оставить auto или задать ширину */ /* right: 0; */ /* Это может привести к растягиванию, лучше оставить auto или задать ширину */
width: max-content; /* Чтобы дропдаун не растягивался на всю ширину родителя */ width: max-content; /* Чтобы дропдаун не растягивался на всю ширину родителя */
min-width: 150px; /* Минимальная ширина */ min-width: 150px; /* Минимальная ширина */
background-color: var(--background-primary); /* Предполагается, что --background-primary есть и подходит */ background-color: var(
border: 1px solid var(--border-color-dark); /* Используем новую переменную для границы */ --background-primary
border-radius: 8px; ); /* Предполагается, что --background-primary есть и подходит */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); /* Можно скорректировать тень в соответствии с темой */ border: 1px solid var(--border-color-dark); /* Используем новую переменную для границы */
z-index: 1000; border-radius: 8px;
/* margin-bottom: 4px; */ /* Это будет задавать JS через top/bottom */ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); /* Можно скорректировать тень в соответствии с темой */
max-height: 200px; z-index: 1000;
overflow-y: auto; /* margin-bottom: 4px; */ /* Это будет задавать JS через top/bottom */
max-height: 200px;
overflow-y: auto;
} }
.model-option { .model-option {
padding: 8px 12px; padding: 8px 12px;
cursor: pointer; cursor: pointer;
font-size: 11px; font-size: 11px;
color: var(--text-normal); color: var(--text-normal);
transition: background-color 0.2s; transition: background-color 0.2s;
} }
.model-option:hover { .model-option:hover {
} }
.model-option:first-child { .model-option:first-child {
border-radius: 8px 8px 0 0; border-radius: 8px 8px 0 0;
} }
.model-option:last-child { .model-option:last-child {
border-radius: 0 0 8px 8px; border-radius: 0 0 8px 8px;
} }
.submit-button { .submit-button {
padding: 4px 8px; padding: 4px 8px;
border: none; border: none;
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
font-size: 11px; font-size: 11px;
transition: all 0.2s; transition: all 0.2s;
} }
.submit-button:hover:enabled { .submit-button:hover:enabled {
filter: brightness(1.25); filter: brightness(1.25);
} }
.submit-button:disabled { .submit-button:disabled {
opacity: 0.5; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
} }
.submit-text { .submit-text {
font-family: system-ui, -apple-system, sans-serif; font-family: system-ui, -apple-system, sans-serif;
} }
/* Обновляем стили для suggestions */ /* Обновляем стили для suggestions */
.suggestions { .suggestions {
position: absolute; position: absolute;
bottom: 100%; bottom: 100%;
left: 0; left: 0;
right: 0; right: 0;
background-color: var(--background-primary); /* Предполагается, что --background-primary есть и подходит */ background-color: var(
border: 1px solid var(--border-color-dark); /* Используем новую переменную для границы */ --background-primary
border-radius: 8px; ); /* Предполагается, что --background-primary есть и подходит */
max-height: 150px; border: 1px solid var(--border-color-dark); /* Используем новую переменную для границы */
overflow-y: auto; border-radius: 8px;
z-index: 100; max-height: 150px;
margin-bottom: 4px; overflow-y: auto;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); /* Можно скорректировать тень в соответствии с темой */ z-index: 100;
margin-bottom: 4px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); /* Можно скорректировать тень в соответствии с темой */
} }
.suggestion-item { .suggestion-item {
padding: 8px 12px; padding: 8px 12px;
cursor: pointer; cursor: pointer;
font-size: 12px; font-size: 12px;
color: var(--text-normal); color: var(--text-normal);
transition: background-color 0.2s; transition: background-color 0.2s;
} }
.suggestion-item:hover { .suggestion-item:hover {
@ -408,10 +442,67 @@
/* Удаляем старые стили для chat-input-form и message-input */ /* Удаляем старые стили для chat-input-form и message-input */
.chat-input-form { .chat-input-form {
display: none; display: none;
} }
#message-input { #message-input {
display: none; display: none;
} }
/* ----------------------------------------------- History Panel Styles --------------------------------------------------- */
.llm-agent-history-sidebar {
border-right: 1px solid var(--background-modifier-border);
overflow-y: auto;
background-color: var(--background-secondary);
transition: width 0.3s ease, min-width 0.3s ease, border-right 0.3s ease;
min-width: 0;
}
.llm-agent-history-sidebar.expanded {
width: 200px;
min-width: 200px;
}
.llm-agent-history-sidebar.collapsed {
width: 0;
min-width: 0;
border-right: none;
overflow: hidden;
}
/* ----------------------------------------------- Toggle Button Styles --------------------------------------------------- */
.history-toggle-button {
position: absolute;
top: 10px;
left: 10px;
z-index: 9999; /* Увеличиваем z-index, чтобы быть выше React Flow */
width: 32px;
height: 32px;
border: none;
border-radius: 6px;
background-color: var(--background-primary);
border: 1px solid var(--border-color-light);
cursor: pointer;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
transition: all 0.2s ease;
}
.history-toggle-button:hover {
background-color: var(--background-modifier-hover);
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);
transform: translateY(-1px);
}
/* ----------------------------------------------- Graph Container Styles -------------------------------------------------- */
.llm-agent-graph-container {
flex: 1;
position: relative;
background-color: var(--background-primary);
}

View File

@ -28,6 +28,8 @@ export class GraphView extends ItemView {
historyContainer: HTMLDivElement; historyContainer: HTMLDivElement;
graphContainer: HTMLDivElement; graphContainer: HTMLDivElement;
reactRoot: ReactDOM.Root | null; reactRoot: ReactDOM.Root | null;
toggleButton: HTMLButtonElement;
isHistoryCollapsed: boolean;
constructor(leaf: WorkspaceLeaf, plugin: LLMAgentPlugin) { constructor(leaf: WorkspaceLeaf, plugin: LLMAgentPlugin) {
super(leaf); super(leaf);
@ -38,6 +40,7 @@ export class GraphView extends ItemView {
this.selectedGraphId = null; this.selectedGraphId = null;
this.historyPanel = null; this.historyPanel = null;
this.reactRoot = null; // Инициализируем null this.reactRoot = null; // Инициализируем null
this.isHistoryCollapsed = true;
} }
/** /**
@ -80,13 +83,8 @@ export class GraphView extends ItemView {
// Левая панель для истории // Левая панель для истории
this.historyContainer = mainContainer.createDiv('llm-agent-history-sidebar') as HTMLDivElement; this.historyContainer = mainContainer.createDiv('llm-agent-history-sidebar') as HTMLDivElement;
this.historyContainer.style.cssText = ` // Убираем инлайн стили - теперь в CSS
width: 300px; this.historyContainer.classList.add(this.isHistoryCollapsed ? 'collapsed' : 'expanded');
min-width: 300px;
border-right: 1px solid var(--background-modifier-border); /* ИСПРАВЛЕНО: Использование переменной Obsidian */
overflow-y: auto;
background-color: var(--background-secondary); /* Убедимся, что фон установлен */
`;
// Правая панель для графа // Правая панель для графа
this.graphContainer = mainContainer.createDiv('llm-agent-graph-container') as HTMLDivElement; this.graphContainer = mainContainer.createDiv('llm-agent-graph-container') as HTMLDivElement;
@ -105,6 +103,9 @@ export class GraphView extends ItemView {
// Инициализируем React Flow // Инициализируем React Flow
this.initializeGraphPanel(); this.initializeGraphPanel();
// Создаем кнопку переключения
this.addToggleButton();
// Подписываемся на события // Подписываемся на события
this.plugin.eventBus.on('new-chat', this.handleNewChat.bind(this)); this.plugin.eventBus.on('new-chat', this.handleNewChat.bind(this));
this.plugin.eventBus.on('graph-selected', this.handleGraphSelected.bind(this)); this.plugin.eventBus.on('graph-selected', this.handleGraphSelected.bind(this));
@ -133,6 +134,25 @@ export class GraphView extends ItemView {
this.plugin.eventBus.off('graph-selected', this.handleGraphSelected); // Отписываемся и отсюда this.plugin.eventBus.off('graph-selected', this.handleGraphSelected); // Отписываемся и отсюда
} }
// ----------------------------------------------- History Panel Toggle --------------------------------------------------
/**
* Переключает состояние панели истории между свернутым и развернутым.
*/
toggleHistoryPanel(): void {
this.isHistoryCollapsed = !this.isHistoryCollapsed;
if (this.isHistoryCollapsed) {
this.historyContainer.classList.remove('expanded');
this.historyContainer.classList.add('collapsed');
this.toggleButton.textContent = '📋';
} else {
this.historyContainer.classList.remove('collapsed');
this.historyContainer.classList.add('expanded');
this.toggleButton.textContent = '❌';
}
}
// ----------------------------------------------- Graph Panel Integration -------------------------------------------------- // ----------------------------------------------- Graph Panel Integration --------------------------------------------------
/** /**
@ -160,6 +180,16 @@ export class GraphView extends ItemView {
} }
} }
addToggleButton(): void {
console.log('Creating toggle button...');
this.toggleButton = document.createElement('button');
this.toggleButton.className = 'history-toggle-button';
this.toggleButton.textContent = this.isHistoryCollapsed ? '📋' : '❌';
this.toggleButton.addEventListener('click', this.toggleHistoryPanel.bind(this));
this.graphContainer.appendChild(this.toggleButton);
console.log('Toggle button created and appended:', this.toggleButton);
}
/** /**
* Рендерит или обновляет GraphPanelComponent в DOM. * Рендерит или обновляет GraphPanelComponent в DOM.
*/ */
@ -268,8 +298,8 @@ export class GraphView extends ItemView {
/** /**
* Обработчик выбора графа из панели истории. * Обработчик выбора графа из панели истории.
* Устанавливает выбранный граф и загружает его данные. * Устанавливает выбранный граф и загружает его данные.
* @param {{ graphId: string; currentNode: string | null }} data - Объект, содержащий ID выбранного графа и текущий узел. * @param {{ graphId: string; currentNode: string | null }} data - Объект, содержащий ID выбранного графа и текущий узел.
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async handleGraphSelected(data: { graphId: string; currentNode: string | null }): Promise<void> { async handleGraphSelected(data: { graphId: string; currentNode: string | null }): Promise<void> {
this.selectedGraphId = data.graphId; this.selectedGraphId = data.graphId;