From 89e57198ad8dec6e642ca50c08642d02a93ffa97 Mon Sep 17 00:00:00 2001 From: dimitrievgs Date: Sat, 16 May 2026 17:54:24 +0300 Subject: [PATCH] Add scroll buttons to chat panel --- src/components/ChatPanel.ts | 35 +++++++++++++++++-- src/css/styles.css | 70 +++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 2 deletions(-) diff --git a/src/components/ChatPanel.ts b/src/components/ChatPanel.ts index 415566f..7cd4fa0 100644 --- a/src/components/ChatPanel.ts +++ b/src/components/ChatPanel.ts @@ -39,6 +39,8 @@ export class ChatPanel { customPromptIndicator: HTMLSpanElement | null = null; currentStreamingNodeId: string | null; stopButton: HTMLButtonElement; + scrollToBottomBtn: HTMLButtonElement; + scrollToTopBtn: HTMLButtonElement; maxTokensInput: HTMLInputElement; maxTokensResetBtn: HTMLButtonElement; @@ -446,6 +448,16 @@ export class ChatPanel { init(): void { this.container.innerHTML = `
+ +
+ + +
+
@@ -506,6 +518,9 @@ export class ChatPanel { this.customPromptIndicator = this.container.querySelector('#custom-prompt-indicator') as HTMLSpanElement; this.stopButton = this.container.querySelector('#stop-button') as HTMLButtonElement; + this.scrollToTopBtn = this.container.querySelector('#scroll-to-top-btn') as HTMLButtonElement; + this.scrollToBottomBtn = this.container.querySelector('#scroll-to-bottom-btn') as HTMLButtonElement; + this.attachedFilesContainer = this.container.querySelector('#attached-files-container') as HTMLDivElement; this.tempInput = this.container.querySelector('#temp-input') as HTMLInputElement; @@ -513,7 +528,7 @@ export class ChatPanel { this.maxTokensInput = this.container.querySelector('#max-tokens-input') as HTMLInputElement; this.maxTokensResetBtn = this.container.querySelector('#max-tokens-reset') as HTMLButtonElement; - const defaultTemp = (this.props.plugin.settings as any).temperatureChat ?? DEFAULT_TEMPERATURE; + const defaultTemp = (this.props.plugin.settings as any).temperatureChat ?? LLM_DEFAULTS.TEMPERATURE; this.tempInput.value = defaultTemp.toString(); // Устанавливаем дефолтное значение для токенов @@ -597,7 +612,7 @@ export class ChatPanel { }); this.tempResetBtn.addEventListener('click', () => { - const def = (this.props.plugin.settings as any).temperatureChat ?? DEFAULT_TEMPERATURE; + const def = (this.props.plugin.settings as any).temperatureChat ?? LLM_DEFAULTS.TEMPERATURE; this.tempInput.value = def.toString(); }); @@ -611,6 +626,22 @@ export class ChatPanel { } }); + // Логика прокрутки вверх и вниз при клике + this.scrollToTopBtn.addEventListener('click', () => { + const scrollContainer = this.container.closest('.view-content') as HTMLElement; + const historyContainer = this.container.querySelector('#chat-history') as HTMLElement; + + if (scrollContainer) scrollContainer.scrollTop = 0; + if (historyContainer) historyContainer.scrollTop = 0; + }); + this.scrollToBottomBtn.addEventListener('click', () => { + const scrollContainer = this.container.closest('.view-content') as HTMLElement; + const historyContainer = this.container.querySelector('#chat-history') as HTMLElement; + + if (scrollContainer) scrollContainer.scrollTop = scrollContainer.scrollHeight; + if (historyContainer) historyContainer.scrollTop = historyContainer.scrollHeight; + }); + // Обработчики Drag-n-Drop для внешних файлов this.setupDragAndDrop(); } diff --git a/src/css/styles.css b/src/css/styles.css index 8e4d5a4..e7c52e3 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -171,6 +171,7 @@ display: flex; flex-direction: column; height: 100%; + position: relative; } .chat-history { @@ -501,6 +502,75 @@ display: none; } +/* ----------------------------------------------- Кнопка прокрутки чат-панели до низа --------------------------------------------------- */ + +/* "Липкая" обертка, которая зависает в самом верху контейнера */ +.sticky-btn-wrapper { + position: sticky; + top: 14px; + z-index: 100; + display: flex; + flex-direction: column; /* Кнопки в колонку */ + align-items: flex-end; /* Прижать к правому краю */ + gap: 8px; /* Расстояние между кнопками */ + padding-right: 14px; + height: 0; + width: 100%; + pointer-events: none; + flex-shrink: 0; +} + +/* Добавить общий стиль для обеих кнопок и специфичный для новой */ +.scroll-to-bottom-btn, +.scroll-to-top-btn { + pointer-events: auto; + border: 2px solid var(--background-primary); + border-radius: 50%; + + /* Жестко задаем размеры (например, 40px) */ + width: 40px; + height: 40px; + min-width: 40px; + min-height: 40px; + + /* Запрещаем flexbox сжимать эти элементы */ + flex-shrink: 0; + + /* Сбрасываем стандартные паддинги Obsidian */ + padding: 0; + box-sizing: border-box; + + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + color: white; + background-color: var(--button-secondary-background); /* По умолчанию тусклее */ + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); + opacity: 0.8; +} + +.scroll-to-bottom-btn:hover, +.scroll-to-top-btn:hover { + opacity: 1; + transform: scale(1.1); + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4); + background-color: var(--button-primary-background) !important; +} + +.scroll-to-bottom-btn:active, +.scroll-to-top-btn:active { + transform: scale(0.95); +} + +/* Иконки кнопок */ +.scroll-to-bottom-btn svg, +.scroll-to-top-btn svg { + width: 18px; + height: 18px; + stroke-width: 3px; +} + /* ----------------------------------------------- Reference System Styles --------------------------------------------------- */ /* Reference Boxes */