fix: font resize live-update + chat message persistence
- Font size slider now calls saveSettings() instead of saveData() so changes propagate to open views immediately - Chat messages persist across plugin reload via chatMessages array in plugin settings — ChatPersistence interface bridges ChatView to Plugin data layer - Messages auto-save on append and on close
This commit is contained in:
parent
695a8e0050
commit
a819e7dd2f
4 changed files with 61 additions and 11 deletions
12
main.js
12
main.js
File diff suppressed because one or more lines are too long
|
|
@ -16,11 +16,18 @@ export interface ApiClient {
|
||||||
): Promise<ChatCompletionResponse>;
|
): Promise<ChatCompletionResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Persistence interface — saves/loads chat messages via plugin data. */
|
||||||
|
export interface ChatPersistence {
|
||||||
|
loadMessages(): Message[];
|
||||||
|
saveMessages(messages: Message[]): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
export const VIEW_TYPE = "hermes-chat-view";
|
export const VIEW_TYPE = "hermes-chat-view";
|
||||||
|
|
||||||
export class ChatView extends ItemView {
|
export class ChatView extends ItemView {
|
||||||
private apiClient: ApiClient | null = null;
|
private apiClient: ApiClient | null = null;
|
||||||
private contextBridge: ContextBridge | null = null;
|
private contextBridge: ContextBridge | null = null;
|
||||||
|
private persistence: ChatPersistence | null = null;
|
||||||
private chatState: ChatState;
|
private chatState: ChatState;
|
||||||
private messagesContainer: HTMLElement | null = null;
|
private messagesContainer: HTMLElement | null = null;
|
||||||
private statusBar: HTMLElement | null = null;
|
private statusBar: HTMLElement | null = null;
|
||||||
|
|
@ -43,10 +50,11 @@ export class ChatView extends ItemView {
|
||||||
private suggestIndex: number = -1;
|
private suggestIndex: number = -1;
|
||||||
private filteredCommands: SlashCommand[] = [];
|
private filteredCommands: SlashCommand[] = [];
|
||||||
|
|
||||||
constructor(leaf: WorkspaceLeaf, apiClient?: ApiClient, contextBridge?: ContextBridge) {
|
constructor(leaf: WorkspaceLeaf, apiClient?: ApiClient, contextBridge?: ContextBridge, persistence?: ChatPersistence) {
|
||||||
super(leaf);
|
super(leaf);
|
||||||
this.apiClient = apiClient ?? null;
|
this.apiClient = apiClient ?? null;
|
||||||
this.contextBridge = contextBridge ?? null;
|
this.contextBridge = contextBridge ?? null;
|
||||||
|
this.persistence = persistence ?? null;
|
||||||
this.chatState = {
|
this.chatState = {
|
||||||
sessionId: null,
|
sessionId: null,
|
||||||
messages: [],
|
messages: [],
|
||||||
|
|
@ -167,10 +175,20 @@ export class ChatView extends ItemView {
|
||||||
// --- Status bar ---
|
// --- Status bar ---
|
||||||
this.statusBar = container.createDiv("chat-status-bar");
|
this.statusBar = container.createDiv("chat-status-bar");
|
||||||
this.updateStatus(this.chatState.model, null);
|
this.updateStatus(this.chatState.model, null);
|
||||||
|
|
||||||
|
// Restore saved messages from previous session
|
||||||
|
if (this.persistence) {
|
||||||
|
const saved = this.persistence.loadMessages();
|
||||||
|
if (saved.length > 0) {
|
||||||
|
for (const msg of saved) {
|
||||||
|
await this.appendMessage(msg.role, msg.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async onClose(): Promise<void> {
|
async onClose(): Promise<void> {
|
||||||
// No persistent cleanup needed
|
await this.persistence?.saveMessages(this.chatState.messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
@ -201,6 +219,17 @@ export class ChatView extends ItemView {
|
||||||
async appendMessage(role: "user" | "assistant" | "system", content: string): Promise<void> {
|
async appendMessage(role: "user" | "assistant" | "system", content: string): Promise<void> {
|
||||||
if (!this.messagesContainer) return;
|
if (!this.messagesContainer) return;
|
||||||
|
|
||||||
|
// Track in state for persistence
|
||||||
|
const msg: Message = {
|
||||||
|
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
||||||
|
role,
|
||||||
|
content,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
};
|
||||||
|
this.chatState.messages.push(msg);
|
||||||
|
// Fire-and-forget save — don't block rendering
|
||||||
|
this.persistence?.saveMessages(this.chatState.messages);
|
||||||
|
|
||||||
const wrapper = this.messagesContainer.createDiv("chat-message");
|
const wrapper = this.messagesContainer.createDiv("chat-message");
|
||||||
wrapper.addClass(`chat-message-${role}`);
|
wrapper.addClass(`chat-message-${role}`);
|
||||||
|
|
||||||
|
|
@ -472,6 +501,16 @@ export class ChatView extends ItemView {
|
||||||
await MarkdownRenderer.render(this.app, fullContent, contentDiv, "", this);
|
await MarkdownRenderer.render(this.app, fullContent, contentDiv, "", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track assistant response in state for persistence
|
||||||
|
const assistantMsg: Message = {
|
||||||
|
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
||||||
|
role: "assistant",
|
||||||
|
content: fullContent,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
};
|
||||||
|
this.chatState.messages.push(assistantMsg);
|
||||||
|
this.persistence?.saveMessages(this.chatState.messages);
|
||||||
|
|
||||||
if (response.sessionId) {
|
if (response.sessionId) {
|
||||||
this.chatState.sessionId = response.sessionId;
|
this.chatState.sessionId = response.sessionId;
|
||||||
this.updateStatus(response.model, response.sessionId);
|
this.updateStatus(response.model, response.sessionId);
|
||||||
|
|
|
||||||
11
src/main.ts
11
src/main.ts
|
|
@ -1,6 +1,6 @@
|
||||||
import { Plugin, WorkspaceLeaf } from "obsidian";
|
import { Plugin, WorkspaceLeaf } from "obsidian";
|
||||||
import { HermesChatSettingTab, HermesChatSettings, DEFAULT_SETTINGS, BUILTIN_COMMANDS } from "./settings";
|
import { HermesChatSettingTab, HermesChatSettings, DEFAULT_SETTINGS, BUILTIN_COMMANDS } from "./settings";
|
||||||
import { ChatView, VIEW_TYPE } from "./chat-view";
|
import { ChatView, VIEW_TYPE, ChatPersistence } from "./chat-view";
|
||||||
import { ApiClient } from "./api-client";
|
import { ApiClient } from "./api-client";
|
||||||
import { ContextBridge } from "./context-bridge";
|
import { ContextBridge } from "./context-bridge";
|
||||||
import type { GatewayConfig, SlashCommand } from "./types";
|
import type { GatewayConfig, SlashCommand } from "./types";
|
||||||
|
|
@ -19,7 +19,14 @@ export default class HermesChatPlugin extends Plugin {
|
||||||
VIEW_TYPE,
|
VIEW_TYPE,
|
||||||
(leaf: WorkspaceLeaf) => {
|
(leaf: WorkspaceLeaf) => {
|
||||||
const client = new ApiClient(this.getGatewayConfig());
|
const client = new ApiClient(this.getGatewayConfig());
|
||||||
const view = new ChatView(leaf, client, this.contextBridge!);
|
const persistence: ChatPersistence = {
|
||||||
|
loadMessages: () => this.settings.chatMessages,
|
||||||
|
saveMessages: async (messages) => {
|
||||||
|
this.settings.chatMessages = messages;
|
||||||
|
await this.saveData(this.settings);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const view = new ChatView(leaf, client, this.contextBridge!, persistence);
|
||||||
view.setContextActive(this.settings.enableContextInjection);
|
view.setContextActive(this.settings.enableContextInjection);
|
||||||
view.setSlashCommands(this.getAllCommands());
|
view.setSlashCommands(this.getAllCommands());
|
||||||
view.setChatFontSize(this.settings.chatFontSize);
|
view.setChatFontSize(this.settings.chatFontSize);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import { App, PluginSettingTab, Setting } from "obsidian";
|
import { App, PluginSettingTab, Setting } from "obsidian";
|
||||||
import type HermesChatPlugin from "./main";
|
import type HermesChatPlugin from "./main";
|
||||||
|
|
||||||
|
import type { Message } from "./types";
|
||||||
|
|
||||||
export interface HermesChatSettings {
|
export interface HermesChatSettings {
|
||||||
gatewayUrl: string;
|
gatewayUrl: string;
|
||||||
apiKey: string;
|
apiKey: string;
|
||||||
|
|
@ -8,6 +10,7 @@ export interface HermesChatSettings {
|
||||||
enableContextInjection: boolean;
|
enableContextInjection: boolean;
|
||||||
chatFontSize: number;
|
chatFontSize: number;
|
||||||
customCommands: { trigger: string; description: string; prompt: string }[];
|
customCommands: { trigger: string; description: string; prompt: string }[];
|
||||||
|
chatMessages: Message[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Built-in slash commands shipped with the plugin. */
|
/** Built-in slash commands shipped with the plugin. */
|
||||||
|
|
@ -36,6 +39,7 @@ export const DEFAULT_SETTINGS: HermesChatSettings = {
|
||||||
enableContextInjection: true,
|
enableContextInjection: true,
|
||||||
chatFontSize: 14,
|
chatFontSize: 14,
|
||||||
customCommands: [],
|
customCommands: [],
|
||||||
|
chatMessages: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export class HermesChatSettingTab extends PluginSettingTab {
|
export class HermesChatSettingTab extends PluginSettingTab {
|
||||||
|
|
@ -113,7 +117,7 @@ export class HermesChatSettingTab extends PluginSettingTab {
|
||||||
.setDynamicTooltip()
|
.setDynamicTooltip()
|
||||||
.onChange(async (value) => {
|
.onChange(async (value) => {
|
||||||
this.plugin.settings.chatFontSize = value;
|
this.plugin.settings.chatFontSize = value;
|
||||||
await this.plugin.saveData(this.plugin.settings);
|
await this.plugin.saveSettings();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue