feat: markdown rendering + font size slider
- Chat messages now render full markdown via Obsidian's MarkdownRenderer (headers, bold, italic, code blocks, lists, links) - Streaming shows plain text, swaps to rendered markdown on completion - User messages render instantly as markdown - New Chat font size slider in plugin settings (10-24px, default 14) - Live-applies via CSS custom property --hermes-chat-font-size - No restart needed
This commit is contained in:
parent
16fb2238dd
commit
695a8e0050
5 changed files with 45 additions and 14 deletions
16
main.js
16
main.js
File diff suppressed because one or more lines are too long
|
|
@ -1,4 +1,4 @@
|
||||||
import { ItemView, WorkspaceLeaf, MarkdownView } from "obsidian";
|
import { ItemView, WorkspaceLeaf, MarkdownView, MarkdownRenderer } from "obsidian";
|
||||||
import type { Message, ChatState, ChatCompletionResponse, SlashCommand } from "./types";
|
import type { Message, ChatState, ChatCompletionResponse, SlashCommand } from "./types";
|
||||||
import { ContextBridge } from "./context-bridge";
|
import { ContextBridge } from "./context-bridge";
|
||||||
|
|
||||||
|
|
@ -192,8 +192,13 @@ export class ChatView extends ItemView {
|
||||||
this.slashCommands = commands;
|
this.slashCommands = commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set chat font size via CSS custom property on the container. */
|
||||||
|
setChatFontSize(size: number): void {
|
||||||
|
this.containerEl.style.setProperty("--hermes-chat-font-size", `${size}px`);
|
||||||
|
}
|
||||||
|
|
||||||
/** Append a message bubble to the chat display and scroll to bottom. */
|
/** Append a message bubble to the chat display and scroll to bottom. */
|
||||||
appendMessage(role: "user" | "assistant" | "system", content: string): void {
|
async appendMessage(role: "user" | "assistant" | "system", content: string): Promise<void> {
|
||||||
if (!this.messagesContainer) return;
|
if (!this.messagesContainer) return;
|
||||||
|
|
||||||
const wrapper = this.messagesContainer.createDiv("chat-message");
|
const wrapper = this.messagesContainer.createDiv("chat-message");
|
||||||
|
|
@ -205,7 +210,7 @@ export class ChatView extends ItemView {
|
||||||
);
|
);
|
||||||
|
|
||||||
const contentEl = wrapper.createDiv("chat-message-content");
|
const contentEl = wrapper.createDiv("chat-message-content");
|
||||||
contentEl.setText(content);
|
await MarkdownRenderer.render(this.app, content, contentEl, "", this);
|
||||||
|
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
@ -399,11 +404,11 @@ export class ChatView extends ItemView {
|
||||||
const userInput = this.resolveSlashCommand(rawInput);
|
const userInput = this.resolveSlashCommand(rawInput);
|
||||||
|
|
||||||
// Display user message (show the resolved prompt, not the /trigger)
|
// Display user message (show the resolved prompt, not the /trigger)
|
||||||
this.appendMessage("user", userInput);
|
await this.appendMessage("user", userInput);
|
||||||
|
|
||||||
// Check API client availability
|
// Check API client availability
|
||||||
if (!this.apiClient) {
|
if (!this.apiClient) {
|
||||||
this.appendMessage(
|
await this.appendMessage(
|
||||||
"assistant",
|
"assistant",
|
||||||
"[API client not configured. Check plugin settings.]"
|
"[API client not configured. Check plugin settings.]"
|
||||||
);
|
);
|
||||||
|
|
@ -461,6 +466,12 @@ export class ChatView extends ItemView {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Replace streaming plain text with rendered markdown
|
||||||
|
if (contentDiv) {
|
||||||
|
contentDiv.empty();
|
||||||
|
await MarkdownRenderer.render(this.app, fullContent, contentDiv, "", this);
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ export default class HermesChatPlugin extends Plugin {
|
||||||
const view = new ChatView(leaf, client, this.contextBridge!);
|
const view = new ChatView(leaf, client, this.contextBridge!);
|
||||||
view.setContextActive(this.settings.enableContextInjection);
|
view.setContextActive(this.settings.enableContextInjection);
|
||||||
view.setSlashCommands(this.getAllCommands());
|
view.setSlashCommands(this.getAllCommands());
|
||||||
|
view.setChatFontSize(this.settings.chatFontSize);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -147,6 +148,7 @@ export default class HermesChatPlugin extends Plugin {
|
||||||
if (leaf.view instanceof ChatView) {
|
if (leaf.view instanceof ChatView) {
|
||||||
leaf.view.setContextActive(this.settings.enableContextInjection);
|
leaf.view.setContextActive(this.settings.enableContextInjection);
|
||||||
leaf.view.setSlashCommands(commands);
|
leaf.view.setSlashCommands(commands);
|
||||||
|
leaf.view.setChatFontSize(this.settings.chatFontSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ export interface HermesChatSettings {
|
||||||
apiKey: string;
|
apiKey: string;
|
||||||
showModelInfo: boolean;
|
showModelInfo: boolean;
|
||||||
enableContextInjection: boolean;
|
enableContextInjection: boolean;
|
||||||
|
chatFontSize: number;
|
||||||
customCommands: { trigger: string; description: string; prompt: string }[];
|
customCommands: { trigger: string; description: string; prompt: string }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,6 +34,7 @@ export const DEFAULT_SETTINGS: HermesChatSettings = {
|
||||||
apiKey: "0f224b188b52e8728d135ec339f8db8dfbf82d89a60021dcfe20d91c6174cf9b",
|
apiKey: "0f224b188b52e8728d135ec339f8db8dfbf82d89a60021dcfe20d91c6174cf9b",
|
||||||
showModelInfo: true,
|
showModelInfo: true,
|
||||||
enableContextInjection: true,
|
enableContextInjection: true,
|
||||||
|
chatFontSize: 14,
|
||||||
customCommands: [],
|
customCommands: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -101,6 +103,20 @@ export class HermesChatSettingTab extends PluginSettingTab {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName("Chat font size")
|
||||||
|
.setDesc("Font size for chat messages and input (10—24 px)")
|
||||||
|
.addSlider((slider) =>
|
||||||
|
slider
|
||||||
|
.setLimits(10, 24, 1)
|
||||||
|
.setValue(this.plugin.settings.chatFontSize)
|
||||||
|
.setDynamicTooltip()
|
||||||
|
.onChange(async (value) => {
|
||||||
|
this.plugin.settings.chatFontSize = value;
|
||||||
|
await this.plugin.saveData(this.plugin.settings);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// --- Slash commands ---
|
// --- Slash commands ---
|
||||||
containerEl.createEl("h3", { text: "Slash Commands" });
|
containerEl.createEl("h3", { text: "Slash Commands" });
|
||||||
containerEl.createEl("p", {
|
containerEl.createEl("p", {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
--hermes-chat-font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Message list --- */
|
/* --- Message list --- */
|
||||||
|
|
@ -45,6 +46,7 @@
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
color: var(--text-normal);
|
color: var(--text-normal);
|
||||||
|
font-size: var(--hermes-chat-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Loading indicator --- */
|
/* --- Loading indicator --- */
|
||||||
|
|
@ -74,7 +76,7 @@
|
||||||
max-height: 120px;
|
max-height: 120px;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-size: 0.9rem;
|
font-size: var(--hermes-chat-font-size);
|
||||||
outline: none;
|
outline: none;
|
||||||
transition: border-color 0.15s;
|
transition: border-color 0.15s;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue