change view of input+button
This commit is contained in:
parent
080865615c
commit
307279b849
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -20,3 +20,6 @@ data.json
|
||||||
|
|
||||||
# Exclude macOS Finder (System Explorer) View States
|
# Exclude macOS Finder (System Explorer) View States
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
styles.css
|
||||||
|
main.js
|
||||||
|
|
@ -9,23 +9,31 @@ export class ChatPanel {
|
||||||
container: HTMLElement;
|
container: HTMLElement;
|
||||||
props: {
|
props: {
|
||||||
chatHistory: ChatHistoryItem[];
|
chatHistory: ChatHistoryItem[];
|
||||||
onSendMessage: (message: string) => void;
|
onSendMessage: (message: string, selectedModel?: string) => void;
|
||||||
|
availableModels?: string[];
|
||||||
|
selectedModel?: string;
|
||||||
};
|
};
|
||||||
chatHistory: ChatHistoryItem[];
|
chatHistory: ChatHistoryItem[];
|
||||||
messageInput: HTMLInputElement;
|
messageInput: HTMLDivElement;
|
||||||
sendButton: HTMLButtonElement;
|
sendButton: HTMLButtonElement;
|
||||||
suggestionsContainer: HTMLDivElement;
|
suggestionsContainer: HTMLDivElement;
|
||||||
|
modelSelectButton: HTMLButtonElement;
|
||||||
|
modelDropdown: HTMLDivElement;
|
||||||
|
selectedModel: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
container: HTMLElement,
|
container: HTMLElement,
|
||||||
props: {
|
props: {
|
||||||
chatHistory: ChatHistoryItem[];
|
chatHistory: ChatHistoryItem[];
|
||||||
onSendMessage: (message: string) => void;
|
onSendMessage: (message: string, selectedModel?: string) => void;
|
||||||
|
availableModels?: string[];
|
||||||
|
selectedModel?: string;
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.props = props;
|
this.props = props;
|
||||||
this.chatHistory = props.chatHistory || [];
|
this.chatHistory = props.chatHistory || [];
|
||||||
|
this.selectedModel = props.selectedModel || 'gemini-2.5-flash';
|
||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
@ -34,33 +42,60 @@ export class ChatPanel {
|
||||||
this.container.innerHTML = `
|
this.container.innerHTML = `
|
||||||
<div class="llm-agent-chat-panel">
|
<div class="llm-agent-chat-panel">
|
||||||
<div class="chat-history" id="chat-history"></div>
|
<div class="chat-history" id="chat-history"></div>
|
||||||
<div class="chat-input-form">
|
<div class="chat-input-container">
|
||||||
<div class="command-input">
|
<div class="scroll-container">
|
||||||
<input type="text" id="message-input" placeholder="Введите сообщение или команду (/)..." />
|
<div
|
||||||
<div class="suggestions" id="suggestions" style="display: none;"></div>
|
contenteditable="true"
|
||||||
|
data-testid="editor-input-main"
|
||||||
|
class="message-input-editor"
|
||||||
|
tabindex="0"
|
||||||
|
data-placeholder="Введите сообщение или команду (/)"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" id="send-button">Отправить</button>
|
<div class="chat-controls">
|
||||||
|
<div class="control-buttons-left">
|
||||||
|
<div class="model-select-container">
|
||||||
|
<button class="model-select-button" id="model-select-button">
|
||||||
|
<span class="model-name" id="selected-model-name">${this.selectedModel}</span>
|
||||||
|
<svg class="dropdown-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div class="model-dropdown" id="model-dropdown" style="display: none;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="control-buttons-right">
|
||||||
|
<button class="submit-button" id="send-button" data-tooltip="Enter">
|
||||||
|
<span class="submit-text">⏎ Enter</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="suggestions" id="suggestions" style="display: none;"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
this.messageInput = this.container.querySelector('#message-input') as HTMLInputElement;
|
this.messageInput = this.container.querySelector('.message-input-editor') as HTMLDivElement;
|
||||||
this.sendButton = this.container.querySelector('#send-button') as HTMLButtonElement;
|
this.sendButton = this.container.querySelector('#send-button') as HTMLButtonElement;
|
||||||
this.suggestionsContainer = this.container.querySelector('#suggestions') as HTMLDivElement;
|
this.suggestionsContainer = this.container.querySelector('#suggestions') as HTMLDivElement;
|
||||||
|
this.modelSelectButton = this.container.querySelector('#model-select-button') as HTMLButtonElement;
|
||||||
|
this.modelDropdown = this.container.querySelector('#model-dropdown') as HTMLDivElement;
|
||||||
|
|
||||||
this.setupEventListeners();
|
this.setupEventListeners();
|
||||||
this.renderHistory();
|
this.renderHistory();
|
||||||
|
this.updateModelDropdown();
|
||||||
|
this.updatePlaceholder();
|
||||||
}
|
}
|
||||||
|
|
||||||
setupEventListeners(): void {
|
setupEventListeners(): void {
|
||||||
this.sendButton.addEventListener('click', () => this.handleSend());
|
this.sendButton.addEventListener('click', () => this.handleSend());
|
||||||
|
|
||||||
this.messageInput.addEventListener('keydown', (e) => {
|
this.messageInput.addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter' && !e.shiftKey) {
|
||||||
if (this.suggestionsContainer.style.display === 'block') {
|
if (this.suggestionsContainer.style.display === 'block') {
|
||||||
const firstSuggestion = this.suggestionsContainer.querySelector('.suggestion-item');
|
const firstSuggestion = this.suggestionsContainer.querySelector('.suggestion-item');
|
||||||
if (firstSuggestion) {
|
if (firstSuggestion) {
|
||||||
this.messageInput.value = firstSuggestion.textContent || '';
|
this.messageInput.textContent = firstSuggestion.textContent || '';
|
||||||
this.hideSuggestions();
|
this.hideSuggestions();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return;
|
return;
|
||||||
|
|
@ -71,12 +106,116 @@ export class ChatPanel {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.messageInput.addEventListener('input', (e) => {
|
this.messageInput.addEventListener('input', () => {
|
||||||
this.handleInput((e.target as HTMLInputElement).value);
|
this.handleInput();
|
||||||
|
this.updatePlaceholder();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.messageInput.addEventListener('focus', () => {
|
||||||
|
this.updatePlaceholder();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.messageInput.addEventListener('blur', () => {
|
||||||
|
this.updatePlaceholder();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Model selection
|
||||||
|
this.modelSelectButton.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
this.toggleModelDropdown();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close dropdown when clicking outside
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
if (!this.modelSelectButton.contains(e.target as Node) && !this.modelDropdown.contains(e.target as Node)) {
|
||||||
|
this.modelDropdown.style.display = 'none';
|
||||||
|
}
|
||||||
|
if (!this.suggestionsContainer.contains(e.target as Node)) {
|
||||||
|
this.hideSuggestions();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInput(value: string): void {
|
updatePlaceholder(): void {
|
||||||
|
const isEmpty = this.messageInput.textContent?.trim() === '';
|
||||||
|
const isFocused = document.activeElement === this.messageInput;
|
||||||
|
|
||||||
|
if (isEmpty && !isFocused) {
|
||||||
|
this.messageInput.classList.add('is-empty');
|
||||||
|
} else {
|
||||||
|
this.messageInput.classList.remove('is-empty');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleModelDropdown(): void {
|
||||||
|
const isVisible = this.modelDropdown.style.display === 'block';
|
||||||
|
if (isVisible) {
|
||||||
|
this.modelDropdown.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Показываем дропдаун, чтобы получить его размеры и положение
|
||||||
|
this.modelDropdown.style.display = 'block';
|
||||||
|
|
||||||
|
const modelSelectButtonRect = this.modelSelectButton.getBoundingClientRect();
|
||||||
|
const modelDropdownRect = this.modelDropdown.getBoundingClientRect();
|
||||||
|
const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
|
||||||
|
|
||||||
|
// ----------------------------------------------- Секция логики позиционирования дропдауна ----------------------------------------------------
|
||||||
|
// Определяем, достаточно ли места снизу
|
||||||
|
const spaceBelow = viewportHeight - modelSelectButtonRect.bottom;
|
||||||
|
// Определяем, достаточно ли места сверху
|
||||||
|
const spaceAbove = modelSelectButtonRect.top;
|
||||||
|
|
||||||
|
if (spaceBelow >= modelDropdownRect.height || spaceBelow >= spaceAbove) {
|
||||||
|
// Открываем вниз, если есть достаточно места или места снизу больше
|
||||||
|
this.modelDropdown.style.top = `${modelSelectButtonRect.height + 4}px`; // 4px отступ от кнопки
|
||||||
|
this.modelDropdown.style.bottom = 'auto';
|
||||||
|
} else {
|
||||||
|
// Открываем вверх
|
||||||
|
this.modelDropdown.style.bottom = `${modelSelectButtonRect.height + 4}px`; // 4px отступ от кнопки
|
||||||
|
this.modelDropdown.style.top = 'auto';
|
||||||
|
}
|
||||||
|
this.modelDropdown.style.left = '0';
|
||||||
|
this.modelDropdown.style.right = 'auto'; // Сброс right, чтобы left работал корректно
|
||||||
|
// ----------------------------------------------- End of Секция логики позиционирования дропдауна ---------------------------------------------
|
||||||
|
}
|
||||||
|
|
||||||
|
updateModelDropdown(): void {
|
||||||
|
if (!this.props.availableModels) return;
|
||||||
|
|
||||||
|
this.modelDropdown.innerHTML = this.props.availableModels
|
||||||
|
.map(
|
||||||
|
(model) => `
|
||||||
|
<div class="model-option" data-model="${model}">
|
||||||
|
${model}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.join('');
|
||||||
|
|
||||||
|
// Add event listeners to model options
|
||||||
|
this.modelDropdown.querySelectorAll('.model-option').forEach((option) => {
|
||||||
|
option.addEventListener('click', () => {
|
||||||
|
const model = (option as HTMLElement).dataset.model;
|
||||||
|
if (model) {
|
||||||
|
this.setSelectedModel(model);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedModel(model: string): void {
|
||||||
|
this.selectedModel = model;
|
||||||
|
const modelNameElement = this.container.querySelector('#selected-model-name');
|
||||||
|
if (modelNameElement) {
|
||||||
|
modelNameElement.textContent = model;
|
||||||
|
}
|
||||||
|
this.modelDropdown.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInput(): void {
|
||||||
|
const value = this.messageInput.textContent || '';
|
||||||
const commands = ['/help', '/imagine', '/analyze', '/subtitles_meet', '/subtitles_teams', '/summarize', '/chat'];
|
const commands = ['/help', '/imagine', '/analyze', '/subtitles_meet', '/subtitles_teams', '/summarize', '/chat'];
|
||||||
|
|
||||||
if (value.startsWith('/')) {
|
if (value.startsWith('/')) {
|
||||||
|
|
@ -100,7 +239,7 @@ export class ChatPanel {
|
||||||
// Добавляем обработчики кликов на предложения
|
// Добавляем обработчики кликов на предложения
|
||||||
this.suggestionsContainer.querySelectorAll('.suggestion-item').forEach((item) => {
|
this.suggestionsContainer.querySelectorAll('.suggestion-item').forEach((item) => {
|
||||||
item.addEventListener('click', () => {
|
item.addEventListener('click', () => {
|
||||||
this.messageInput.value = item.textContent || '';
|
this.messageInput.textContent = item.textContent || '';
|
||||||
this.hideSuggestions();
|
this.hideSuggestions();
|
||||||
this.messageInput.focus();
|
this.messageInput.focus();
|
||||||
});
|
});
|
||||||
|
|
@ -112,10 +251,11 @@ export class ChatPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSend(): void {
|
handleSend(): void {
|
||||||
const message = this.messageInput.value.trim();
|
const message = this.messageInput.textContent?.trim() || '';
|
||||||
if (message && this.props.onSendMessage) {
|
if (message && this.props.onSendMessage) {
|
||||||
this.props.onSendMessage(message);
|
this.props.onSendMessage(message, this.selectedModel);
|
||||||
this.messageInput.value = '';
|
this.messageInput.textContent = '';
|
||||||
|
this.updatePlaceholder();
|
||||||
this.hideSuggestions();
|
this.hideSuggestions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -181,6 +321,11 @@ export class ChatPanel {
|
||||||
this.renderHistory();
|
this.renderHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateAvailableModels(models: string[]): void {
|
||||||
|
this.props.availableModels = models;
|
||||||
|
this.updateModelDropdown();
|
||||||
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
this.container.innerHTML = '';
|
this.container.innerHTML = '';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -194,38 +194,210 @@
|
||||||
scroll-margin-bottom: 30px; /* Добавляет отступ в 20px снизу при прокрутке к этому элементу */
|
scroll-margin-bottom: 30px; /* Добавляет отступ в 20px снизу при прокрутке к этому элементу */
|
||||||
}
|
}
|
||||||
|
|
||||||
#send-button {
|
.submit-button {
|
||||||
padding: 8px 15px;
|
padding: 4px 8px; /* Восстанавливаем паддинг */
|
||||||
background-color: #007bff;
|
border: none; /* Убираем рамку */
|
||||||
color: white;
|
border-radius: 8px;
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
font-size: 11px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
/* Явно переопределяем стили, которые могли быть затронуты */
|
||||||
|
width: auto; /* Убедимся, что ширина не ограничена */
|
||||||
|
height: auto; /* Убедимся, что высота не ограничена */
|
||||||
|
background-color: #0078d4 !important; /* Или ваш основной цвет кнопки */
|
||||||
|
color: white !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: #0056b3;
|
background-color: #0056b3 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Добавляем стили для нового интерфейса ввода */
|
||||||
|
|
||||||
|
.chat-input-container {
|
||||||
|
border-top: 1px solid var(--background-modifier-border);
|
||||||
|
padding: 8px 10px 4px 10px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-container {
|
||||||
|
overflow-y: auto;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
max-height: 70vh;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-input-editor {
|
||||||
|
outline: none;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.4;
|
||||||
|
min-height: 20px;
|
||||||
|
padding: 8px 0;
|
||||||
|
color: var(--text-normal);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-input-editor.is-empty::before {
|
||||||
|
content: attr(data-placeholder);
|
||||||
|
color: var(--text-muted);
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-controls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-buttons-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-buttons-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-select-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-select-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: var(--background-modifier-form-field);
|
||||||
|
color: var(--text-muted);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 11px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-select-button:hover {
|
||||||
|
background-color: var(--background-modifier-form-field-highlighted);
|
||||||
|
filter: brightness(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-name {
|
||||||
|
max-width: 120px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-icon {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
/* Убираем жесткое bottom: 100%; т.к. позиционирование будет задаваться в JS */
|
||||||
|
/* bottom: 100%; */
|
||||||
|
left: 0;
|
||||||
|
/* right: 0; */ /* Это может привести к растягиванию, лучше оставить auto или задать ширину */
|
||||||
|
width: max-content; /* Чтобы дропдаун не растягивался на всю ширину родителя */
|
||||||
|
min-width: 150px; /* Минимальная ширина */
|
||||||
|
background-color: var(--background-primary);
|
||||||
|
border: 1px solid var(--background-modifier-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
z-index: 1000;
|
||||||
|
/* margin-bottom: 4px; */ /* Это будет задавать JS через top/bottom */
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-option {
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-normal);
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-option:hover {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-option:first-child {
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-option:last-child {
|
||||||
|
border-radius: 0 0 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-button {
|
||||||
|
padding: 4px 8px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 11px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-button:hover:enabled {
|
||||||
|
filter: brightness(1.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-button:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-text {
|
||||||
|
font-family: system-ui, -apple-system, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Обновляем стили для suggestions */
|
||||||
.suggestions {
|
.suggestions {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 100%; /* Помещаем над полем ввода */
|
bottom: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
background-color: white;
|
background-color: var(--background-primary);
|
||||||
border: 1px solid #ccc;
|
border: 1px solid var(--background-modifier-border);
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
max-height: 150px;
|
max-height: 150px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
z-index: 10;
|
z-index: 100;
|
||||||
margin-bottom: 5px; /* Отступ от поля ввода */
|
margin-bottom: 4px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.suggestion-item {
|
.suggestion-item {
|
||||||
padding: 8px 10px;
|
padding: 8px 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-normal);
|
||||||
|
transition: background-color 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.suggestion-item:hover {
|
.suggestion-item:hover {
|
||||||
background-color: #f0f0f0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Удаляем старые стили для chat-input-form и message-input */
|
||||||
|
.chat-input-form {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#message-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ export class ChatView extends ItemView {
|
||||||
currentNode: string | null;
|
currentNode: string | null;
|
||||||
chatContainer: HTMLDivElement;
|
chatContainer: HTMLDivElement;
|
||||||
chatPanel: ChatPanel | null;
|
chatPanel: ChatPanel | null;
|
||||||
|
availableModels: string[];
|
||||||
|
|
||||||
constructor(leaf: WorkspaceLeaf, plugin: LLMAgentPlugin) {
|
constructor(leaf: WorkspaceLeaf, plugin: LLMAgentPlugin) {
|
||||||
super(leaf);
|
super(leaf);
|
||||||
|
|
@ -26,6 +27,7 @@ export class ChatView extends ItemView {
|
||||||
this.selectedGraphId = null;
|
this.selectedGraphId = null;
|
||||||
this.currentNode = null;
|
this.currentNode = null;
|
||||||
this.chatPanel = null;
|
this.chatPanel = null;
|
||||||
|
this.availableModels = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
getViewType(): string {
|
getViewType(): string {
|
||||||
|
|
@ -48,10 +50,15 @@ export class ChatView extends ItemView {
|
||||||
this.chatContainer = container.createDiv() as HTMLDivElement;
|
this.chatContainer = container.createDiv() as HTMLDivElement;
|
||||||
this.chatContainer.addClass('llm-agent-chat-container');
|
this.chatContainer.addClass('llm-agent-chat-container');
|
||||||
|
|
||||||
|
// Загружаем доступные модели
|
||||||
|
await this.fetchAvailableModels();
|
||||||
|
|
||||||
// Инициализируем только чат панель
|
// Инициализируем только чат панель
|
||||||
this.chatPanel = new ChatPanel(this.chatContainer, {
|
this.chatPanel = new ChatPanel(this.chatContainer, {
|
||||||
chatHistory: this.chatHistory,
|
chatHistory: this.chatHistory,
|
||||||
onSendMessage: this.handleSendMessage.bind(this)
|
onSendMessage: this.handleSendMessage.bind(this),
|
||||||
|
availableModels: this.availableModels,
|
||||||
|
selectedModel: this.plugin.settings.defaultModel || 'gemini-2.5-flash'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Подписываемся на события
|
// Подписываемся на события
|
||||||
|
|
@ -113,9 +120,27 @@ export class ChatView extends ItemView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Загружает список доступных моделей с бэкенда.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async fetchAvailableModels(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${this.plugin.settings.apiBaseUrl}/models`);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Ошибка загрузки моделей: ${response.status}`);
|
||||||
|
}
|
||||||
|
this.availableModels = await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка загрузки доступных моделей:', error);
|
||||||
|
// Fallback к базовым моделям
|
||||||
|
this.availableModels = ['gemini-2.5-flash', 'gpt-4o-mini', 'claude-3-5-sonnet-20241022'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------- Event Handlers ---------------------------------------------------------------
|
// ----------------------------------------------- Event Handlers ---------------------------------------------------------------
|
||||||
|
|
||||||
async handleSendMessage(message: string): Promise<void> {
|
async handleSendMessage(message: string, selectedModel?: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${this.plugin.settings.apiBaseUrl}/chat`, {
|
const response = await fetch(`${this.plugin.settings.apiBaseUrl}/chat`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
@ -126,7 +151,8 @@ export class ChatView extends ItemView {
|
||||||
message: message,
|
message: message,
|
||||||
graph_id: this.selectedGraphId,
|
graph_id: this.selectedGraphId,
|
||||||
parent_node_id: this.currentNode,
|
parent_node_id: this.currentNode,
|
||||||
system_prompt: this.plugin.settings.systemPrompt
|
system_prompt: this.plugin.settings.systemPrompt,
|
||||||
|
model: selectedModel || 'gemini-2.5-flash'
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user