Add 4th tab in voice panel with quantitive and qualitive metrics

This commit is contained in:
dimitrievgs 2026-05-11 00:43:57 +03:00
parent a103966178
commit 143160ee26
2 changed files with 67 additions and 3 deletions

31
main.ts
View File

@ -26,10 +26,12 @@ interface LLMAgentSettings {
voiceCommandModel: string;
logsFolder: string;
voiceInterval: number;
metricsInterval: number;
voiceDisplayLimit: number; // 3000
voiceContextLimit: number; // N2 символов для LLM
promptRegular: string;
promptCommands: string;
promptMetrics: string;
markerStart: string; // "старт"
markerStop: string; // "стоп"
}
@ -48,10 +50,12 @@ const DEFAULT_SETTINGS: LLMAgentSettings = {
voiceCommandModel: '',
logsFolder: 'audio-logs',
voiceInterval: 1,
metricsInterval: 15,
voiceDisplayLimit: 30000,
voiceContextLimit: 30000,
promptRegular: '',
promptCommands: '',
promptMetrics: '',
markerStart: 'старт',
markerStop: 'стоп'
};
@ -76,6 +80,7 @@ export default class LLMAgentPlugin extends Plugin {
settingsToSync.systemPrompt = await engine.processRawSettingValue(this.settings.systemPrompt);
settingsToSync.promptRegular = await engine.processRawSettingValue(this.settings.promptRegular);
settingsToSync.promptCommands = await engine.processRawSettingValue(this.settings.promptCommands);
settingsToSync.promptMetrics = await engine.processRawSettingValue(this.settings.promptMetrics);
const adapter = this.app.vault.adapter;
if (adapter instanceof FileSystemAdapter) {
@ -163,8 +168,12 @@ export default class LLMAgentPlugin extends Plugin {
this.addSettingTab(new SampleSettingTab(this.app, this));
await this.fetchAvailableModels();
// Вызываем один раз при запуске
await this.syncSettings();
// Ждем полной инициализации хранилища перед вызовом зависимых функций
// await this.syncSettings();
this.app.workspace.onLayoutReady(async () => {
await this.syncSettings();
});
this.registerEvent(
this.app.vault.on('modify', async (file) => {
@ -348,6 +357,19 @@ class SampleSettingTab extends PluginSettingTab {
})
);
new Setting(containerEl)
.setName('Интервал аналитики (мин)')
.setDesc('Как часто Python и LLM генерируют графики и качественный анализ (Таб 4).')
.addText((text) =>
text
.setValue(String(this.plugin.settings.metricsInterval))
.onChange(async (value) => {
this.plugin.settings.metricsInterval = Number(value) || 15;
await this.plugin.saveSettings();
await this.plugin.syncSettings();
})
);
new Setting(containerEl)
.setName('Объем контекста (символы)')
.setDesc('Сколько последних символов транскрибации отправлять в модель.')
@ -401,6 +423,11 @@ class SampleSettingTab extends PluginSettingTab {
'promptCommands'
);
this.addPromptAutocomplete(
new Setting(containerEl).setName('Промпт качественной аналитики'),
'promptMetrics'
);
containerEl.createEl('h2', { text: 'Управление ссылками и файлами' });
new Setting(containerEl)

View File

@ -2,7 +2,7 @@ import LLMAgentPlugin from 'main';
import { MarkdownRenderer, setIcon, Notice } from 'obsidian';
export class VoicePanel {
private activeTab: 'res1' | 'res2' | 'trans' = 'res1';
private activeTab: 'res1' | 'res2' | 'trans' | 'metrics' = 'res1';
constructor(
private container: HTMLElement,
@ -25,6 +25,7 @@ export class VoicePanel {
<button class="v-tab active" data-tab="res1">Response 1</button>
<button class="v-tab" data-tab="res2">Response 2</button>
<button class="v-tab" data-tab="trans">Transcription</button>
<button class="v-tab" data-tab="metrics">Metrics</button>
</div>
</div>
<div id="v-body" class="voice-body selectable"></div>
@ -84,6 +85,42 @@ export class VoicePanel {
return;
}
if (this.activeTab === 'metrics') {
if (!data.metrics) return;
// 1. Строгая проверка через JSON.stringify, чтобы отсечь лишние апдейты
// и не перестраивать DOM (что и вызывает дергание)
const newDataString = JSON.stringify(data.metrics);
if ((body as any).lastMetricsData === newDataString) return;
(body as any).lastMetricsData = newDataString;
// 2. Запоминаем позицию скролла перед обновлением
const scrollTop = body.scrollTop;
body.empty();
// Рендер графиков
if (data.metrics.graphs && data.metrics.graphs.length > 0) {
const graphsContainer = document.createElement('div');
graphsContainer.style.display = "flex";
graphsContainer.style.flexDirection = "column";
graphsContainer.innerHTML = data.metrics.graphs.join('');
body.appendChild(graphsContainer);
}
// Рендер текста
if (data.metrics.text) {
const textContainer = document.createElement('div');
textContainer.addClass('markdown-rendered');
await MarkdownRenderer.renderMarkdown(data.metrics.text, textContainer, '', this.plugin);
body.appendChild(textContainer);
}
// 3. Возвращаем скролл на место сразу после рендера в следующем микротаске
setTimeout(() => { body.scrollTop = scrollTop; }, 0);
return;
}
// Получаем записи (они уже приходят отсортированные: новые в начале)
const entries: { id: string; content: string }[] = this.activeTab === 'res1' ? data.response1 : data.response2;