From 2dd033a010f37f10ebdb1d02f2fa2f3eb871fc67 Mon Sep 17 00:00:00 2001 From: dimitrievgs Date: Thu, 30 Apr 2026 19:32:12 +0300 Subject: [PATCH] Add vertical resizer for voice panel --- src/css/styles.css | 42 +++++++++++++++------ src/views/GraphView.ts | 83 ++++++++++++++++++++++++++++++------------ 2 files changed, 89 insertions(+), 36 deletions(-) diff --git a/src/css/styles.css b/src/css/styles.css index d479525..2c2b641 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -757,11 +757,39 @@ border-top: 1px solid var(--background-modifier-border); } -.voice-panel-container { +.voice-panel-resizer { + height: 4px; + width: 100%; + cursor: ns-resize; + background-color: var(--divider-color); + flex-shrink: 0; + z-index: 10; +} + +.voice-panel-resizer:hover { + background-color: var(--interactive-accent); +} + +/* Убедимся, что нижняя панель не перекрывает контент */ +.voice-panel-fixed-bottom { display: flex; flex-direction: column; + min-height: 50px; /* Минимальная доступная высота */ +} + +.voice-panel-container { height: 100%; - padding: 5px; + display: flex; + flex-direction: column; +} + +.voice-body { + flex: 1; + overflow-y: auto; + background-color: var(--background-primary); + border: 1px inset var(--background-modifier-border); + margin: 4px; + padding: 8px; } .voice-header { @@ -791,16 +819,6 @@ border-bottom: 2px solid var(--text-accent); } -.voice-body { - flex: 1; - overflow-y: auto; - font-size: 12px; - font-family: var(--font-monospace); - padding: 10px; - background-color: var(--background-primary); - white-space: pre-wrap; /* Чтобы транскрибация не уходила в одну строку */ -} - /* Кнопки управления голосом */ .v-controls button { margin-right: 5px; diff --git a/src/views/GraphView.ts b/src/views/GraphView.ts index 82058c2..13e5e4a 100644 --- a/src/views/GraphView.ts +++ b/src/views/GraphView.ts @@ -91,56 +91,91 @@ export class GraphView extends ItemView { const container = this.containerEl.children[1] as HTMLElement; container.empty(); - // 1. Основной контейнер - теперь вертикальный стек const mainContainer = container.createDiv('llm-agent-graph-main'); mainContainer.style.cssText = ` display: flex; - flex-direction: column; /* Элементы кладутся друг под друга */ + flex-direction: column; height: 100%; width: 100%; overflow: hidden; `; - // 2. Верхняя часть (История + Граф) + // 1. Верхняя часть const upperContent = mainContainer.createDiv('upper-content-wrapper'); upperContent.style.cssText = ` display: flex; - flex-direction: row; /* Элементы бок о бок */ - flex: 1; /* Занимает всё свободное место (около 75-80%) */ + flex-direction: row; + flex: 1; width: 100%; - min-height: 0; /* Важно для корректного скролла внутри */ + min-height: 0; `; - // 3. Левая панель для истории (внутри верхней части) this.historyContainer = upperContent.createDiv('llm-agent-history-sidebar'); this.historyContainer.classList.add(this.isHistoryCollapsed ? 'collapsed' : 'expanded'); - // 4. Правая панель для графа (внутри верхней части) this.graphContainer = upperContent.createDiv('llm-agent-graph-container'); - this.graphContainer.style.cssText = ` - flex: 1; - position: relative; - background-color: var(--background-primary); - `; + this.graphContainer.style.cssText = `flex: 1; position: relative; background-color: var(--background-primary);`; - // 5. Контейнер для голосовой панели (строго ВНИЗУ) - const voiceContainer = mainContainer.createDiv('voice-panel-fixed-bottom'); - voiceContainer.style.cssText = ` - height: 200px; /* Фиксированная высота или % */ + // 2. РАЗДЕЛИТЕЛЬ (Resizer) + const resizer = mainContainer.createDiv('voice-panel-resizer'); + resizer.style.cssText = ` + height: 4px; width: 100%; - border-top: 1px solid var(--divider-color); - background-color: var(--background-secondary); - overflow: hidden; - flex-shrink: 0; /* Не дает панели сжиматься */ + cursor: ns-resize; + background-color: var(--divider-color); + flex-shrink: 0; + transition: background-color 0.2s; `; - // Инициализация компонентов + // 3. Нижняя панель + const voiceContainer = mainContainer.createDiv('voice-panel-fixed-bottom'); + let initialHeight = 150; // Высота по умолчанию + voiceContainer.style.cssText = ` + height: ${initialHeight}px; + width: 100%; + background-color: var(--background-secondary); + overflow: hidden; + flex-shrink: 0; + `; + + // ЛОГИКА ИЗМЕНЕНИЯ ВЫСОТЫ + let isResizing = false; + + resizer.addEventListener('mousedown', (e) => { + isResizing = true; + document.body.style.cursor = 'ns-resize'; + // Чтобы iframe/график не перехватывали события мыши + mainContainer.style.pointerEvents = 'none'; + }); + + document.addEventListener('mousemove', (e) => { + if (!isResizing) return; + + // Вычисляем расстояние от верха контейнера + const containerRect = mainContainer.getBoundingClientRect(); + const newHeight = containerRect.bottom - e.clientY; + + // Ограничения (мин 50px, макс 70% высоты) + if (newHeight > 50 && newHeight < containerRect.height * 0.7) { + voiceContainer.style.height = `${newHeight}px`; + } + }); + + document.addEventListener('mouseup', () => { + if (isResizing) { + isResizing = false; + document.body.style.cursor = 'default'; + mainContainer.style.pointerEvents = 'all'; + } + }); + + // Инициализация остальных компонентов this.historyPanel = new HistoryPanel( this.historyContainer, { graphs: this.graphsList, eventBus: this.plugin.eventBus }, this.plugin - ); - + ); + this.addToggleButton(); this.addGlobalActionButtons(); this.initializeGraphPanel();