mirror of
https://github.com/andy-stack/vaultkeeper-ai.git
synced 2026-07-22 06:42:03 +00:00
Add markdown rendering support with KaTeX and highlight.js
- Updated package.json to include dependencies for rehype and remark plugins for markdown processing. - Added default highlight.js CSS for code highlighting. - Included KaTeX CSS for rendering mathematical expressions. - Created markdown.css for styling markdown content in the Obsidian plugin, incorporating styles for headings, paragraphs, lists, tables, and code blocks. - Customized highlight.js colors to match Obsidian theme.
This commit is contained in:
parent
0fb3e4632f
commit
3f37870aaa
28 changed files with 4628 additions and 201 deletions
|
|
@ -1,5 +1,4 @@
|
|||
import { AIProviderURL } from "Enums/ApiProvider";
|
||||
import { isValidJson } from "Helpers";
|
||||
import { request, type RequestUrlParam } from "obsidian";
|
||||
import { Resolve } from "Services/DependencyService";
|
||||
import { Services } from "Services/Services";
|
||||
|
|
@ -8,6 +7,7 @@ import type { GeminiActionDefinitions } from "Actioner/Gemini/GeminiActionDefini
|
|||
import { create_file } from "Actioner/Actions";
|
||||
import type { IAIClass } from "AIClasses/IAIClass";
|
||||
import type { IPrompt } from "AIClasses/IPrompt";
|
||||
import type { GenerateContentResponse, Part } from "@google/genai";
|
||||
|
||||
export class Gemini implements IAIClass {
|
||||
private readonly apiKey: string;
|
||||
|
|
@ -22,7 +22,7 @@ export class Gemini implements IAIClass {
|
|||
this.actionDefinitions = Resolve(Services.IActionDefinitions);
|
||||
}
|
||||
|
||||
public async apiRequest(userInput: string, actioner: IActioner): Promise<Part[] | null> { //AIResponse
|
||||
public async apiRequest(userInput: string, actioner: IActioner): Promise<Part[] | null> {
|
||||
let prompt: string = "The users prompt is: " + userInput;
|
||||
|
||||
let requestBody = JSON.stringify({
|
||||
|
|
@ -55,22 +55,14 @@ export class Gemini implements IAIClass {
|
|||
body: requestBody
|
||||
};
|
||||
|
||||
let response: GeminiApiResponse = JSON.parse(await request(reqParam));
|
||||
let response: GenerateContentResponse = JSON.parse(await request(reqParam));
|
||||
|
||||
console.log(response);
|
||||
|
||||
//TODO: tidy up this
|
||||
let ai_response: Part[] = response.candidates[0]?.content.parts ?? "{}";
|
||||
|
||||
// if (isValidJson(ai_response)) {
|
||||
// return JSON.parse(ai_response);
|
||||
// }
|
||||
let ai_response: Part[] = response.candidates?.first()?.content?.parts ?? [];
|
||||
|
||||
console.log(ai_response);
|
||||
|
||||
return ai_response;
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import type { Part } from "@google/genai";
|
||||
import type { IActioner } from "Actioner/IActioner";
|
||||
|
||||
export interface IAIClass {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ export class AIPrompt implements IPrompt {
|
|||
|
||||
public readonly instructionsArr: string[] = [
|
||||
"You are an AI assistant for the Obsidian note taking app.",
|
||||
//"In addition to answering questions, you can execute helpful functions which are defined below.",
|
||||
"The user has provided extra context to your responsibilities:",
|
||||
"You are a DND expert and can provide detailed information about DND rules, character creation, and gameplay mechanics. Please give concise responses."
|
||||
];
|
||||
|
|
@ -34,17 +33,14 @@ export class AIPrompt implements IPrompt {
|
|||
}
|
||||
|
||||
public readonly instructionsReminderArr: string[] = [
|
||||
"Ensure your response is valid JSON following the format defined above."
|
||||
""
|
||||
]
|
||||
public instructionsReminder(): string {
|
||||
return this.instructionsReminderArr.join("\n");
|
||||
}
|
||||
|
||||
public readonly responseFormatArr: string[] = [
|
||||
"All responses that are not function calls should be in JSON parsable format. The response should not be wrapped in ```json```. The following fields should be used:",
|
||||
"user_response - the response to be delivered to the user.",
|
||||
//"function_name - a string name for the desired function or null.",
|
||||
//"function_object - an object containing the function arguments or null."
|
||||
"",
|
||||
]
|
||||
public responseFormat(): string {
|
||||
return this.responseFormatArr.join("\n");
|
||||
|
|
|
|||
|
|
@ -1,14 +1,4 @@
|
|||
/**
|
||||
* Interfaces for requested custom API responses.
|
||||
*/
|
||||
|
||||
interface AIResponse {
|
||||
function_calls: FunctionCall[];
|
||||
|
||||
// function_name: string | null;
|
||||
// function_object: object | null;
|
||||
// user_response: string;
|
||||
}
|
||||
|
||||
interface CreateFileRequest {
|
||||
file_path: string;
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
/**
|
||||
* Interface for the top-level Gemini API response.
|
||||
*/
|
||||
interface GeminiApiResponse {
|
||||
candidates: Candidate[];
|
||||
usageMetadata: UsageMetadata;
|
||||
modelVersion: string;
|
||||
responseId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for a single candidate in the response.
|
||||
*/
|
||||
interface Candidate {
|
||||
content: Content;
|
||||
finishReason: string;
|
||||
index: number;
|
||||
citationMetadata: CitationMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for the content of a candidate.
|
||||
*/
|
||||
interface Content {
|
||||
parts: Part[];
|
||||
role: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for a single part of the content.
|
||||
* The `text` property contains a JSON string, which needs to be parsed.
|
||||
*/
|
||||
interface Part {
|
||||
text: string;
|
||||
functionCall: FunctionCall;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for a single function call.
|
||||
*/
|
||||
interface FunctionCall {
|
||||
name: string;
|
||||
args: object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for the metadata about citations.
|
||||
*/
|
||||
interface CitationMetadata {
|
||||
citationSources: CitationSource[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for a single citation source.
|
||||
*/
|
||||
interface CitationSource {
|
||||
startIndex: number;
|
||||
endIndex: number;
|
||||
uri: string;
|
||||
license: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for the usage metadata of the API call.
|
||||
*/
|
||||
interface UsageMetadata {
|
||||
promptTokenCount: number;
|
||||
candidatesTokenCount: number;
|
||||
totalTokenCount: number;
|
||||
promptTokensDetails: PromptTokensDetails[];
|
||||
thoughtsTokenCount: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for the details of prompt tokens.
|
||||
*/
|
||||
interface PromptTokensDetails {
|
||||
modality: string;
|
||||
tokenCount: number;
|
||||
}
|
||||
199
Components/ChatArea.svelte
Normal file
199
Components/ChatArea.svelte
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
<script lang="ts">
|
||||
import { Resolve } from "Services/DependencyService";
|
||||
import { MarkdownService } from "Services/MarkdownService";
|
||||
import { Services } from "Services/Services";
|
||||
|
||||
export let messages: Array<{id: string, content: string, isUser: boolean}> = [];
|
||||
|
||||
let chatContainer: HTMLDivElement;
|
||||
let markdownService: MarkdownService;
|
||||
|
||||
// Initialize service once
|
||||
try {
|
||||
markdownService = Resolve(Services.MarkdownService);
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize MarkdownService:', error);
|
||||
}
|
||||
|
||||
// Process each message content with markdown
|
||||
$: processedMessages = messages.map((message) => {
|
||||
if (message.isUser) {
|
||||
return {
|
||||
...message,
|
||||
htmlContent: `<p>${message.content}</p>`
|
||||
};
|
||||
} else {
|
||||
let htmlContent;
|
||||
try {
|
||||
htmlContent = markdownService?.formatToHTML(message.content) || `<p>${message.content}</p>`;
|
||||
} catch (err) {
|
||||
console.error('HTML processing failed:', err);
|
||||
htmlContent = `<p>${message.content}</p>`;
|
||||
}
|
||||
return {
|
||||
...message,
|
||||
htmlContent
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="chat-area" bind:this={chatContainer}>
|
||||
{#each processedMessages as message (message.id)}
|
||||
<div class="message-container" class:user={message.isUser} class:assistant={!message.isUser}>
|
||||
<div class="message-bubble" class:user={message.isUser} class:assistant={!message.isUser}>
|
||||
{#if message.isUser}
|
||||
<p>{message.content}</p>
|
||||
{:else}
|
||||
<div class="markdown-content">
|
||||
{@html message.htmlContent}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
{#if messages.length === 0}
|
||||
<div class="empty-state">
|
||||
<p>Start a conversation by typing a message below.</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Keep your existing styles -->
|
||||
<style>
|
||||
.chat-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: var(--size-4-3);
|
||||
gap: var(--p-spacing);
|
||||
}
|
||||
|
||||
.message-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.message-container.user {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.message-container.assistant {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.message-bubble {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.message-bubble.user {
|
||||
word-wrap: break-word;
|
||||
max-width: 70%;
|
||||
border: var(--border-width) solid var(--background-modifier-border);
|
||||
border-radius: var(--radius-m);
|
||||
padding: 0px var(--size-4-2);
|
||||
}
|
||||
|
||||
.message-bubble.assistant {
|
||||
word-wrap: break-word;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-style: italic;
|
||||
color: var(--text-muted);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.markdown-content {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Keep all your existing markdown styles */
|
||||
.markdown-content :global(h1),
|
||||
.markdown-content :global(h2),
|
||||
.markdown-content :global(h3),
|
||||
.markdown-content :global(h4),
|
||||
.markdown-content :global(h5),
|
||||
.markdown-content :global(h6) {
|
||||
margin: 1rem 0 0.5rem 0;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.markdown-content :global(h1) { font-size: 1.5em; }
|
||||
.markdown-content :global(h2) { font-size: 1.3em; }
|
||||
.markdown-content :global(h3) { font-size: 1.1em; }
|
||||
|
||||
.markdown-content :global(p) {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.markdown-content :global(pre) {
|
||||
background-color: var(--background-modifier-border);
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-s);
|
||||
overflow-x: auto;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.markdown-content :global(code) {
|
||||
background-color: var(--background-modifier-border);
|
||||
padding: 2px 4px;
|
||||
border-radius: var(--radius-s);
|
||||
font-family: var(--font-monospace, 'Courier New', monospace);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.markdown-content :global(pre code) {
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.markdown-content :global(blockquote) {
|
||||
border-left: 4px solid var(--background-modifier-border);
|
||||
margin: 1rem 0;
|
||||
padding-left: 1rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.markdown-content :global(ul),
|
||||
.markdown-content :global(ol) {
|
||||
padding-left: 1.5rem;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.markdown-content :global(li) {
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
.markdown-content :global(table) {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 1rem 0;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.markdown-content :global(th),
|
||||
.markdown-content :global(td) {
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
padding: 8px 12px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown-content :global(th) {
|
||||
background-color: var(--background-modifier-border);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.markdown-content :global(a) {
|
||||
color: var(--text-accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markdown-content :global(a:hover) {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
<script lang="ts">
|
||||
import type { Part } from "@google/genai";
|
||||
import type { IActioner } from "Actioner/IActioner";
|
||||
import type { IAIClass } from "AIClasses/IAIClass";
|
||||
import { Semaphore } from "Helpers";
|
||||
import { Resolve } from "Services/DependencyService";
|
||||
import { Services } from "Services/Services";
|
||||
import ChatArea from "./ChatArea.svelte";
|
||||
|
||||
let ai: IAIClass = Resolve(Services.IAIClass);
|
||||
let actioner: IActioner = Resolve(Services.IActioner);
|
||||
|
||||
let semaphore: Semaphore = new Semaphore(1, false);
|
||||
let textareaElement: HTMLTextAreaElement;
|
||||
|
||||
let userRequest = "";
|
||||
let isSubmitting = false;
|
||||
let messages: Array<{id: string, content: string, isUser: boolean}> = [];
|
||||
|
||||
async function handleSubmit() {
|
||||
if (userRequest.trim() === "" || isSubmitting) {
|
||||
return;
|
||||
}
|
||||
isSubmitting = true;
|
||||
|
||||
const requestToSend = userRequest;
|
||||
userRequest = "";
|
||||
textareaElement.value = "";
|
||||
autoResize();
|
||||
|
||||
// Add user message to chat
|
||||
const userMessageId = `user-${Date.now()}`;
|
||||
messages = [...messages, {
|
||||
id: userMessageId,
|
||||
content: requestToSend,
|
||||
isUser: true
|
||||
}];
|
||||
|
||||
if (!await semaphore.wait()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let response: Part[] | null = await ai.apiRequest(requestToSend, actioner);
|
||||
console.log(response);
|
||||
|
||||
// Add AI response to chat
|
||||
if (response && response.length > 0) {
|
||||
const aiMessageId = `ai-${Date.now()}`;
|
||||
|
||||
const responseText = response.map(part => part.text || '').join(' ');
|
||||
messages = [...messages, {
|
||||
id: aiMessageId,
|
||||
content: responseText,
|
||||
isUser: false
|
||||
}];
|
||||
}
|
||||
} finally {
|
||||
semaphore.release();
|
||||
isSubmitting = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeydown(e: KeyboardEvent) {
|
||||
if (e.key === 'Enter') {
|
||||
if (e.shiftKey) {
|
||||
return;
|
||||
} else {
|
||||
e.preventDefault();
|
||||
handleSubmit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function autoResize() {
|
||||
if (textareaElement) {
|
||||
textareaElement.style.height = 'auto';
|
||||
textareaElement.style.height = textareaElement.scrollHeight + 'px';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<main class="container">
|
||||
<div id="chat-container">
|
||||
<ChatArea bind:messages />
|
||||
</div>
|
||||
|
||||
<div id="input-container">
|
||||
<textarea
|
||||
id="input"
|
||||
bind:this={textareaElement}
|
||||
bind:value={userRequest}
|
||||
on:keydown={handleKeydown}
|
||||
on:input={autoResize}
|
||||
placeholder="Type a message..."
|
||||
disabled={isSubmitting}
|
||||
rows="1">
|
||||
</textarea>
|
||||
|
||||
<button id="submit" on:click={handleSubmit} disabled={isSubmitting || userRequest.trim() === ""}>
|
||||
{isSubmitting ? 'Sending...' : 'Submit'}
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr auto;
|
||||
grid-template-columns: 1fr;
|
||||
height: 100%;
|
||||
border-radius: var(--radius-m);
|
||||
color: var(--font-interface-theme);
|
||||
}
|
||||
|
||||
#chat-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
grid-row: 1;
|
||||
grid-column: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#input-container {
|
||||
grid-row: 2;
|
||||
grid-column: 1;
|
||||
display: grid;
|
||||
grid-template-rows: var(--size-4-3) 1fr var(--size-4-3);
|
||||
grid-template-columns: var(--size-4-3) 1fr var(--size-4-3) auto var(--size-4-3);
|
||||
border-radius: var(--modal-radius);
|
||||
background-color: var(--modal-background);
|
||||
}
|
||||
|
||||
#input {
|
||||
grid-row: 2;
|
||||
grid-column: 2;
|
||||
min-height: var(--input-height);
|
||||
max-height: var(--dialog-max-height);
|
||||
border-radius: var(--input-radius);
|
||||
font-weight: var(--input-font-weight);
|
||||
border-width: var(--input-border-width);
|
||||
resize: none;
|
||||
overflow-y: auto;
|
||||
color: var(--font-interface-theme);
|
||||
}
|
||||
|
||||
#submit {
|
||||
grid-row: 2;
|
||||
grid-column: 4;
|
||||
border-radius: var(--button-radius);
|
||||
align-self: end;
|
||||
background-color: var(--interactive-accent);
|
||||
transition-duration: 0.25s;
|
||||
}
|
||||
|
||||
#submit:hover {
|
||||
background-color: var(--interactive-accent-hover);
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { Resolve } from "Services/DependencyService";
|
||||
import { Services } from "Services/Services";
|
||||
import type { IActioner } from "Actioner/IActioner";
|
||||
import type { IAIClass } from "AIClasses/IAIClass";
|
||||
|
||||
interface Props {
|
||||
input: string;
|
||||
}
|
||||
|
||||
let {
|
||||
input
|
||||
}: Props = $props();
|
||||
|
||||
let output: string = $state("");
|
||||
|
||||
async function submit() {
|
||||
let aiClass: IAIClass = Resolve(Services.IAIClass)
|
||||
let actioner: IActioner = Resolve(Services.IActioner);
|
||||
|
||||
let aiResponse: Part[] | null = await aiClass.apiRequest(input, actioner);
|
||||
|
||||
if (aiResponse == null) {
|
||||
throw "Response was invalid JSON";
|
||||
}
|
||||
|
||||
for (let part of aiResponse) {
|
||||
if (part.functionCall) {
|
||||
let functionName: string = part.functionCall.name;
|
||||
let functionObject: object = part.functionCall.args;
|
||||
|
||||
await actioner[Symbol.for(functionName)](functionObject);
|
||||
};
|
||||
}
|
||||
|
||||
output = `Done: ${aiResponse}`
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<input
|
||||
type="string"
|
||||
bind:value={input}
|
||||
placeholder="Enter a prompt"
|
||||
aria-label="Enter a prompt"
|
||||
/>
|
||||
<button onclick={submit}>Submit</button>
|
||||
|
||||
<p>{output}</p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
display: flex;
|
||||
background-color: hotpink;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 0.4rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #0066ff;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.4rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #0055dd;
|
||||
}
|
||||
</style>
|
||||
46
Helpers.ts
46
Helpers.ts
|
|
@ -1,4 +1,4 @@
|
|||
import { TFile, TFolder, type Vault } from "obsidian";
|
||||
import { type Vault } from "obsidian";
|
||||
|
||||
export function isValidJson(str: string): boolean {
|
||||
try {
|
||||
|
|
@ -11,7 +11,7 @@ export function isValidJson(str: string): boolean {
|
|||
|
||||
export async function createDirectories(vault: Vault, filePath: string) {
|
||||
const dirPath: string = filePath.substring(0, filePath.lastIndexOf('/'));
|
||||
|
||||
|
||||
const dirs: string[] = dirPath.split('/');
|
||||
|
||||
let currentPath = "";
|
||||
|
|
@ -23,4 +23,46 @@ export async function createDirectories(vault: Vault, filePath: string) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Semaphore {
|
||||
private max: number;
|
||||
private count: number;
|
||||
private readonly waitAsync: boolean;
|
||||
private readonly queue: ((value: boolean) => void)[];
|
||||
|
||||
constructor(max: number, waitAsync: boolean) {
|
||||
this.max = max;
|
||||
this.count = max;
|
||||
this.waitAsync = waitAsync;
|
||||
this.queue = [];
|
||||
}
|
||||
|
||||
async wait(): Promise<boolean> {
|
||||
if (this.count > 0) {
|
||||
this.count--;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.waitAsync) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return new Promise<boolean>((resolve) => {
|
||||
this.queue.push(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
release(): void {
|
||||
if (this.queue.length > 0) {
|
||||
const resolve = this.queue.shift();
|
||||
if (resolve) {
|
||||
resolve(true);
|
||||
}
|
||||
} else {
|
||||
if (this.count < this.max) {
|
||||
this.count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
112
Services/MarkdownService.ts
Normal file
112
Services/MarkdownService.ts
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
import { unified, type Processor } from 'unified';
|
||||
import remarkParse from 'remark-parse';
|
||||
import remarkMath from 'remark-math';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkToc from 'remark-toc';
|
||||
import remarkEmoji from 'remark-emoji';
|
||||
import remarkRehype from 'remark-rehype';
|
||||
import rehypeKatex from 'rehype-katex';
|
||||
import rehypeHighlight from 'rehype-highlight';
|
||||
import rehypeStringify from 'rehype-stringify';
|
||||
|
||||
export class MarkdownService {
|
||||
private static processor: Processor<any, any, any, any, any> | null = null;
|
||||
|
||||
constructor() {
|
||||
if (!MarkdownService.processor) {
|
||||
MarkdownService.processor = this.createProcessor();
|
||||
}
|
||||
}
|
||||
|
||||
private createProcessor() {
|
||||
return unified()
|
||||
.use(remarkParse)
|
||||
.use(remarkMath)
|
||||
.use(remarkGfm)
|
||||
.use(remarkToc)
|
||||
.use(remarkEmoji)
|
||||
.use(remarkRehype, { allowDangerousHtml: false })
|
||||
.use(rehypeKatex)
|
||||
.use(rehypeHighlight)
|
||||
.use(rehypeStringify);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats LLM response text into HTML-friendly format
|
||||
* Handles output from Claude, ChatGPT, and Gemini
|
||||
* @param content - The raw LLM response text
|
||||
* @returns HTML string
|
||||
*/
|
||||
formatToHTML(content: string): string {
|
||||
try {
|
||||
const preprocessedContent = this.preprocessContent(content);
|
||||
const result = MarkdownService.processor!.processSync(preprocessedContent);
|
||||
return String(result);
|
||||
} catch (error) {
|
||||
console.error('Markdown processing failed:', error);
|
||||
return `<p>${this.escapeHtml(content)}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
private preprocessContent(content: string): string {
|
||||
return content
|
||||
// 1. LaTeX Math Notation (normalize different formats)
|
||||
.replace(/\\\[([\s\S]*?)\\\]/g, (_, equation) => `$$${equation}$$`) // block math
|
||||
.replace(/\\\(([\s\S]*?)\\\)/g, (_, equation) => `$${equation}$`) // inline math
|
||||
|
||||
// 2. Handle Code Block Language Tags (normalize variations)
|
||||
.replace(/```(\w+)\s*\n/g, '```$1\n')
|
||||
|
||||
// 3. Fix Common Escape Issues (AI models sometimes over-escape)
|
||||
.replace(/\\`/g, '`')
|
||||
.replace(/\\\*/g, '*')
|
||||
.replace(/\\_/g, '_')
|
||||
|
||||
// 4. Normalize Line Endings
|
||||
.replace(/\r\n/g, '\n')
|
||||
.replace(/\r/g, '\n')
|
||||
|
||||
// 5. Handle Triple Quotes (some models use these for code)
|
||||
.replace(/"""\s*(\w+)?\s*\n([\s\S]*?)\n"""/g, (match, lang, code) => {
|
||||
return lang ? `\`\`\`${lang}\n${code}\n\`\`\`` : `\`\`\`\n${code}\n\`\`\``;
|
||||
})
|
||||
|
||||
// 6. Clean up Multiple Consecutive Newlines
|
||||
.replace(/\n{4,}/g, '\n\n\n')
|
||||
|
||||
// 7. Handle Gemini's math-in-code-blocks tendency
|
||||
.replace(/```math\n([\s\S]*?)\n```/g, (_, equation) => {
|
||||
if (equation.includes('\\') || equation.includes('{') || equation.includes('^') || equation.includes('_')) {
|
||||
return `$$${equation}$$`;
|
||||
}
|
||||
return `\`\`\`\n${equation}\n\`\`\``;
|
||||
})
|
||||
|
||||
// 8. Fix Bold/Italic Formatting Issues
|
||||
.replace(/\*\*\s+([^*]+)\s+\*\*/g, '**$1**') // Remove extra spaces in bold
|
||||
.replace(/\*\s+([^*]+)\s+\*/g, '*$1*') // Remove extra spaces in italic
|
||||
|
||||
// 9. Fix Table Formatting Issues
|
||||
.replace(/^\|\s+/gm, '| ') // Clean up table cell spacing
|
||||
.replace(/\s+\|$/gm, ' |') // Clean up table cell spacing
|
||||
|
||||
// 10. Handle Alternative List Markers
|
||||
.replace(/^•\s/gm, '- ') // Convert bullet points to markdown
|
||||
.replace(/^→\s/gm, '- ') // Convert arrows to markdown
|
||||
|
||||
// 11. Fix Link Formatting Issues
|
||||
.replace(/\[\s+([^\]]+)\s+\]/g, '[$1]') // Remove spaces in link text
|
||||
|
||||
// 12. Handle Footnote Variations
|
||||
.replace(/\[\^(\w+)\]:/g, '[^$1]:') // Normalize footnote definitions
|
||||
|
||||
// 13. Clean up Heading Formatting
|
||||
.replace(/^#+\s+/gm, (match) => match.replace(/\s+/g, ' ')); // Single space after #
|
||||
}
|
||||
|
||||
private escapeHtml(text: string): string {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import type { IAIClass } from "AIClasses/IAIClass";
|
|||
import { GeminiActionDefinitions } from "Actioner/Gemini/GeminiActionDefinitions";
|
||||
import type { IActionDefinitions } from "Actioner/IActionDefinitions";
|
||||
import { Gemini } from "AIClasses/Gemini/Gemini";
|
||||
import { MarkdownService } from "./MarkdownService";
|
||||
|
||||
export function RegisterDependencies(plugin: DmsAssistantPlugin) {
|
||||
RegisterSingleton(Services.DmsAssistantPlugin, plugin);
|
||||
|
|
@ -18,8 +19,9 @@ export function RegisterDependencies(plugin: DmsAssistantPlugin) {
|
|||
RegisterSingleton(Services.ModalService, new ModalService())
|
||||
|
||||
RegisterSingleton<IPrompt>(Services.IPrompt, new AIPrompt());
|
||||
RegisterSingleton<IActioner>(Services.IActioner, new Actioner())
|
||||
RegisterSingleton<IActioner>(Services.IActioner, new Actioner());
|
||||
|
||||
RegisterTransient<MarkdownService>(Services.MarkdownService, () => new MarkdownService());
|
||||
RegisterAiProvider(plugin);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ export class Services {
|
|||
static DmsAssistantPlugin = Symbol("DmsAssistantPlugin");
|
||||
static OdbCache = Symbol("OdbCache");
|
||||
static ModalService = Symbol("ModalService");
|
||||
static MarkdownService = Symbol("MarkdownService");
|
||||
|
||||
// interfaces
|
||||
static IAIClass = Symbol("IAIClass");
|
||||
|
|
|
|||
9
Styles/highlight-default.min.css
vendored
Normal file
9
Styles/highlight-default.min.css
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/*!
|
||||
Theme: Default
|
||||
Description: Original highlight.js style
|
||||
Author: (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
|
||||
Maintainer: @highlightjs/core-team
|
||||
Website: https://highlightjs.org/
|
||||
License: see project LICENSE
|
||||
Touched: 2021
|
||||
*/pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#f3f3f3;color:#444}.hljs-comment{color:#697070}.hljs-punctuation,.hljs-tag{color:#444a}.hljs-tag .hljs-attr,.hljs-tag .hljs-name{color:#444}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag{font-weight:700}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#800}.hljs-section,.hljs-title{color:#800;font-weight:700}.hljs-link,.hljs-operator,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#ab5656}.hljs-literal{color:#695}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta .hljs-string{color:#38a}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
373
Styles/katex.min.css
vendored
Normal file
373
Styles/katex.min.css
vendored
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
/* KaTeX CSS with CDN font URLs */
|
||||
@font-face {
|
||||
font-family: 'KaTeX_AMS';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_AMS-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_AMS-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Caligraphic';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Caligraphic-Bold.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Caligraphic-Bold.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Caligraphic';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Caligraphic-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Caligraphic-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Fraktur';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Fraktur-Bold.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Fraktur-Bold.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Fraktur';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Fraktur-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Fraktur-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Main';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Main-Bold.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Main-Bold.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Main';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Main-Italic.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Main-Italic.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Main';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Main-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Main-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Math';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Math-Italic.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Math-Italic.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_SansSerif';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_SansSerif-Bold.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_SansSerif-Bold.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_SansSerif';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_SansSerif-Italic.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_SansSerif-Italic.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_SansSerif';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_SansSerif-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_SansSerif-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Script';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Script-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Script-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Size1';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Size1-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Size1-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Size2';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Size2-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Size2-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Size3';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Size3-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Size3-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Size4';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Size4-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Size4-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Typewriter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Typewriter-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Typewriter-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Rest of KaTeX CSS styles */
|
||||
.katex {
|
||||
font: normal 1.21em KaTeX_Main, Times, serif;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
text-indent: 0;
|
||||
text-rendering: auto;
|
||||
}
|
||||
|
||||
.katex * {
|
||||
border-color: currentColor;
|
||||
}
|
||||
|
||||
.katex .katex-version::after {
|
||||
content: "0.16.0";
|
||||
}
|
||||
|
||||
.katex .katex-mathml {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.katex .katex-html > .newline {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.katex .base {
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
width: min-content;
|
||||
}
|
||||
|
||||
.katex .strut {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.katex .textbf {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.katex .textit {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.katex .textrm {
|
||||
font-family: KaTeX_Main;
|
||||
}
|
||||
|
||||
.katex .textsf {
|
||||
font-family: KaTeX_SansSerif;
|
||||
}
|
||||
|
||||
.katex .texttt {
|
||||
font-family: KaTeX_Typewriter;
|
||||
}
|
||||
|
||||
.katex .mathnormal {
|
||||
font-family: KaTeX_Math;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.katex .mathit {
|
||||
font-family: KaTeX_Main;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.katex .mathrm {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.katex .mathbf {
|
||||
font-family: KaTeX_Main;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.katex .boldsymbol {
|
||||
font-family: KaTeX_Math;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.katex .amsrm {
|
||||
font-family: KaTeX_AMS;
|
||||
}
|
||||
|
||||
.katex .mathbb,
|
||||
.katex .textbb {
|
||||
font-family: KaTeX_AMS;
|
||||
}
|
||||
|
||||
.katex .mathcal {
|
||||
font-family: KaTeX_Caligraphic;
|
||||
}
|
||||
|
||||
.katex .mathfrak,
|
||||
.katex .textfrak {
|
||||
font-family: KaTeX_Fraktur;
|
||||
}
|
||||
|
||||
.katex .mathtt {
|
||||
font-family: KaTeX_Typewriter;
|
||||
}
|
||||
|
||||
.katex .mathscr,
|
||||
.katex .textscr {
|
||||
font-family: KaTeX_Script;
|
||||
}
|
||||
|
||||
.katex .mathsf,
|
||||
.katex .textsf {
|
||||
font-family: KaTeX_SansSerif;
|
||||
}
|
||||
|
||||
.katex .mathboldsf,
|
||||
.katex .textboldsf {
|
||||
font-family: KaTeX_SansSerif;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.katex .mathitsf,
|
||||
.katex .textitsf {
|
||||
font-family: KaTeX_SansSerif;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.katex .mainrm {
|
||||
font-family: KaTeX_Main;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.katex .vlist-t {
|
||||
display: inline-table;
|
||||
table-layout: fixed;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.katex .vlist-r {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.katex .vlist {
|
||||
display: table-cell;
|
||||
vertical-align: bottom;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.katex .vlist > span {
|
||||
display: block;
|
||||
height: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.katex .vlist > span > span {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.katex .vlist > span > .pstrut {
|
||||
overflow: hidden;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.katex .hlist {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.katex .hlist > span {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.katex .vcenter {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.katex .alignleft {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.katex .aligncenter {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.katex .alignright {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.katex .gothfrak,
|
||||
.katex .textgothfrak {
|
||||
font-family: KaTeX_Fraktur;
|
||||
}
|
||||
|
||||
.katex .cmr {
|
||||
font-family: KaTeX_Main;
|
||||
}
|
||||
|
||||
.katex .cmss {
|
||||
font-family: KaTeX_SansSerif;
|
||||
}
|
||||
|
||||
.katex .cmtt {
|
||||
font-family: KaTeX_Typewriter;
|
||||
}
|
||||
|
||||
.katex .katex-display {
|
||||
display: block;
|
||||
margin: 1em 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.katex .katex-display > .katex {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
text-align: initial;
|
||||
}
|
||||
|
||||
.katex .katex-display.leqno > .katex {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.katex .katex-display.fleqn > .katex {
|
||||
text-align: left;
|
||||
padding-left: 2em;
|
||||
}
|
||||
153
Styles/markdown.css
Normal file
153
Styles/markdown.css
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/* Import the downloaded CSS files */
|
||||
@import './katex.min.css';
|
||||
@import './highlight-default.min.css';
|
||||
|
||||
/* Additional markdown styling for Obsidian plugin */
|
||||
.markdown-processor-content {
|
||||
line-height: 1.6;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.markdown-processor-content h1,
|
||||
.markdown-processor-content h2,
|
||||
.markdown-processor-content h3,
|
||||
.markdown-processor-content h4,
|
||||
.markdown-processor-content h5,
|
||||
.markdown-processor-content h6 {
|
||||
margin: 1rem 0 0.5rem 0;
|
||||
color: var(--text-normal);
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
|
||||
.markdown-processor-content h1 { font-size: 1.5em; }
|
||||
.markdown-processor-content h2 { font-size: 1.3em; }
|
||||
.markdown-processor-content h3 { font-size: 1.1em; }
|
||||
|
||||
.markdown-processor-content p {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.markdown-processor-content pre {
|
||||
background-color: var(--background-primary-alt);
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-s);
|
||||
overflow-x: auto;
|
||||
margin: 0.5rem 0;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.markdown-processor-content code {
|
||||
background-color: var(--background-primary-alt);
|
||||
padding: 2px 4px;
|
||||
border-radius: var(--radius-s);
|
||||
font-family: var(--font-monospace);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.markdown-processor-content pre code {
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.markdown-processor-content blockquote {
|
||||
border-left: 4px solid var(--background-modifier-border);
|
||||
margin: 1rem 0;
|
||||
padding-left: 1rem;
|
||||
color: var(--text-muted);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.markdown-processor-content ul,
|
||||
.markdown-processor-content ol {
|
||||
padding-left: 1.5rem;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.markdown-processor-content li {
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
.markdown-processor-content table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 1rem 0;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.markdown-processor-content th,
|
||||
.markdown-processor-content td {
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
padding: 8px 12px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown-processor-content th {
|
||||
background-color: var(--background-primary-alt);
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
|
||||
.markdown-processor-content a {
|
||||
color: var(--text-accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markdown-processor-content a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Math content styling adjustments for Obsidian */
|
||||
.markdown-processor-content .katex {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.markdown-processor-content .katex-display {
|
||||
margin: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Task list styling */
|
||||
.markdown-processor-content input[type="checkbox"] {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
/* Footnotes styling */
|
||||
.markdown-processor-content .footnotes {
|
||||
margin-top: 2rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
/* Override highlight.js colors to use Obsidian theme colors */
|
||||
.markdown-processor-content .hljs {
|
||||
background: var(--background-primary-alt) !important;
|
||||
color: var(--text-normal) !important;
|
||||
}
|
||||
|
||||
.markdown-processor-content .hljs-comment,
|
||||
.markdown-processor-content .hljs-quote {
|
||||
color: var(--text-muted) !important;
|
||||
}
|
||||
|
||||
.markdown-processor-content .hljs-keyword,
|
||||
.markdown-processor-content .hljs-selector-tag,
|
||||
.markdown-processor-content .hljs-literal {
|
||||
color: var(--text-accent) !important;
|
||||
}
|
||||
|
||||
.markdown-processor-content .hljs-string,
|
||||
.markdown-processor-content .hljs-doctag {
|
||||
color: var(--color-green) !important;
|
||||
}
|
||||
|
||||
.markdown-processor-content .hljs-title,
|
||||
.markdown-processor-content .hljs-section,
|
||||
.markdown-processor-content .hljs-type {
|
||||
color: var(--color-blue) !important;
|
||||
}
|
||||
|
||||
.markdown-processor-content .hljs-number,
|
||||
.markdown-processor-content .hljs-variable,
|
||||
.markdown-processor-content .hljs-template-variable {
|
||||
color: var(--color-purple) !important;
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import { ItemView, WorkspaceLeaf } from 'obsidian';
|
||||
|
||||
import Input from '../Components/Input.svelte';
|
||||
import { mount, unmount } from 'svelte';
|
||||
import ChatWindow from 'Components/ChatWindow.svelte';
|
||||
|
||||
export const VIEW_TYPE_MAIN = 'main-view';
|
||||
|
||||
|
|
@ -10,7 +9,7 @@ export class MainView extends ItemView {
|
|||
super(leaf);
|
||||
}
|
||||
|
||||
input: ReturnType<typeof Input> | undefined;
|
||||
input: ReturnType<typeof ChatWindow> | undefined;
|
||||
|
||||
getViewType() {
|
||||
return VIEW_TYPE_MAIN;
|
||||
|
|
@ -24,11 +23,9 @@ export class MainView extends ItemView {
|
|||
const container = this.contentEl;
|
||||
container.empty();
|
||||
|
||||
this.input = mount(Input, {
|
||||
this.input = mount(ChatWindow, {
|
||||
target: container,
|
||||
props: {
|
||||
input: "",
|
||||
}
|
||||
props: {}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import esbuild from "esbuild";
|
||||
import process from "process";
|
||||
import builtins from "builtin-modules";
|
||||
import { copyFileSync, mkdirSync, existsSync, readdirSync, statSync } from "fs";
|
||||
import { join } from "path";
|
||||
|
||||
import esbuildSvelte from 'esbuild-svelte';
|
||||
import { sveltePreprocess } from 'svelte-preprocess';
|
||||
|
|
@ -14,6 +16,27 @@ if you want to view the source, please visit the github repository of this plugi
|
|||
|
||||
const prod = (process.argv[2] === "production");
|
||||
|
||||
// Function to copy directory recursively
|
||||
function copyDir(src, dest) {
|
||||
if (!existsSync(dest)) {
|
||||
mkdirSync(dest, { recursive: true });
|
||||
}
|
||||
|
||||
const files = readdirSync(src);
|
||||
|
||||
for (const file of files) {
|
||||
const srcPath = join(src, file);
|
||||
const destPath = join(dest, file);
|
||||
|
||||
if (statSync(srcPath).isDirectory()) {
|
||||
copyDir(srcPath, destPath);
|
||||
} else {
|
||||
copyFileSync(srcPath, destPath);
|
||||
console.log(`📁 Copied: ${srcPath} → ${destPath}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const context = await esbuild.context({
|
||||
plugins: [
|
||||
esbuildSvelte({
|
||||
|
|
@ -52,7 +75,24 @@ const context = await esbuild.context({
|
|||
|
||||
if (prod) {
|
||||
await context.rebuild();
|
||||
|
||||
// Copy styles after production build
|
||||
if (existsSync("Styles")) {
|
||||
copyDir("Styles", "styles");
|
||||
console.log("✅ Copied Styles directory to styles/");
|
||||
} else {
|
||||
console.log("⚠️ Styles directory not found, skipping copy");
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
} else {
|
||||
// Copy styles for development
|
||||
if (existsSync("Styles")) {
|
||||
copyDir("Styles", "styles");
|
||||
console.log("✅ Copied Styles directory to styles/");
|
||||
} else {
|
||||
console.log("⚠️ Styles directory not found, skipping copy");
|
||||
}
|
||||
|
||||
await context.watch();
|
||||
}
|
||||
}
|
||||
510
main.css
Normal file
510
main.css
Normal file
File diff suppressed because one or more lines are too long
2
main.ts
2
main.ts
|
|
@ -9,6 +9,8 @@ import { FileAction } from 'Enums/FileAction';
|
|||
import { Path } from 'Enums/Path';
|
||||
import { RegisterAiProvider, RegisterDependencies } from 'Services/ServiceRegistration';
|
||||
|
||||
import './styles/markdown.css';
|
||||
|
||||
interface DmsAssistantSettings {
|
||||
apiProvider: string;
|
||||
apiKey: string;
|
||||
|
|
|
|||
2469
package-lock.json
generated
2469
package-lock.json
generated
File diff suppressed because it is too large
Load diff
11
package.json
11
package.json
|
|
@ -30,6 +30,17 @@
|
|||
"dependencies": {
|
||||
"@google/genai": "^1.17.0",
|
||||
"express": "^5.1.0",
|
||||
"rehype-highlight": "^7.0.2",
|
||||
"rehype-katex": "^7.0.1",
|
||||
"rehype-stringify": "^10.0.1",
|
||||
"remark-emoji": "^5.0.2",
|
||||
"remark-footnotes": "^4.0.1",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"remark-math": "^6.0.0",
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-rehype": "^11.1.2",
|
||||
"remark-toc": "^9.0.0",
|
||||
"unified": "^11.0.5",
|
||||
"uuid": "^11.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
9
styles/highlight-default.min.css
vendored
Normal file
9
styles/highlight-default.min.css
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/*!
|
||||
Theme: Default
|
||||
Description: Original highlight.js style
|
||||
Author: (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
|
||||
Maintainer: @highlightjs/core-team
|
||||
Website: https://highlightjs.org/
|
||||
License: see project LICENSE
|
||||
Touched: 2021
|
||||
*/pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#f3f3f3;color:#444}.hljs-comment{color:#697070}.hljs-punctuation,.hljs-tag{color:#444a}.hljs-tag .hljs-attr,.hljs-tag .hljs-name{color:#444}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag{font-weight:700}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#800}.hljs-section,.hljs-title{color:#800;font-weight:700}.hljs-link,.hljs-operator,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#ab5656}.hljs-literal{color:#695}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta .hljs-string{color:#38a}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
373
styles/katex.min.css
vendored
Normal file
373
styles/katex.min.css
vendored
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
/* KaTeX CSS with CDN font URLs */
|
||||
@font-face {
|
||||
font-family: 'KaTeX_AMS';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_AMS-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_AMS-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Caligraphic';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Caligraphic-Bold.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Caligraphic-Bold.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Caligraphic';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Caligraphic-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Caligraphic-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Fraktur';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Fraktur-Bold.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Fraktur-Bold.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Fraktur';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Fraktur-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Fraktur-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Main';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Main-Bold.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Main-Bold.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Main';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Main-Italic.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Main-Italic.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Main';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Main-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Main-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Math';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Math-Italic.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Math-Italic.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_SansSerif';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_SansSerif-Bold.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_SansSerif-Bold.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_SansSerif';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_SansSerif-Italic.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_SansSerif-Italic.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_SansSerif';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_SansSerif-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_SansSerif-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Script';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Script-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Script-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Size1';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Size1-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Size1-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Size2';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Size2-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Size2-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Size3';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Size3-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Size3-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Size4';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Size4-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Size4-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'KaTeX_Typewriter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Typewriter-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/fonts/KaTeX_Typewriter-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Rest of KaTeX CSS styles */
|
||||
.katex {
|
||||
font: normal 1.21em KaTeX_Main, Times, serif;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
text-indent: 0;
|
||||
text-rendering: auto;
|
||||
}
|
||||
|
||||
.katex * {
|
||||
border-color: currentColor;
|
||||
}
|
||||
|
||||
.katex .katex-version::after {
|
||||
content: "0.16.0";
|
||||
}
|
||||
|
||||
.katex .katex-mathml {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.katex .katex-html > .newline {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.katex .base {
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
width: min-content;
|
||||
}
|
||||
|
||||
.katex .strut {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.katex .textbf {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.katex .textit {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.katex .textrm {
|
||||
font-family: KaTeX_Main;
|
||||
}
|
||||
|
||||
.katex .textsf {
|
||||
font-family: KaTeX_SansSerif;
|
||||
}
|
||||
|
||||
.katex .texttt {
|
||||
font-family: KaTeX_Typewriter;
|
||||
}
|
||||
|
||||
.katex .mathnormal {
|
||||
font-family: KaTeX_Math;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.katex .mathit {
|
||||
font-family: KaTeX_Main;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.katex .mathrm {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.katex .mathbf {
|
||||
font-family: KaTeX_Main;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.katex .boldsymbol {
|
||||
font-family: KaTeX_Math;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.katex .amsrm {
|
||||
font-family: KaTeX_AMS;
|
||||
}
|
||||
|
||||
.katex .mathbb,
|
||||
.katex .textbb {
|
||||
font-family: KaTeX_AMS;
|
||||
}
|
||||
|
||||
.katex .mathcal {
|
||||
font-family: KaTeX_Caligraphic;
|
||||
}
|
||||
|
||||
.katex .mathfrak,
|
||||
.katex .textfrak {
|
||||
font-family: KaTeX_Fraktur;
|
||||
}
|
||||
|
||||
.katex .mathtt {
|
||||
font-family: KaTeX_Typewriter;
|
||||
}
|
||||
|
||||
.katex .mathscr,
|
||||
.katex .textscr {
|
||||
font-family: KaTeX_Script;
|
||||
}
|
||||
|
||||
.katex .mathsf,
|
||||
.katex .textsf {
|
||||
font-family: KaTeX_SansSerif;
|
||||
}
|
||||
|
||||
.katex .mathboldsf,
|
||||
.katex .textboldsf {
|
||||
font-family: KaTeX_SansSerif;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.katex .mathitsf,
|
||||
.katex .textitsf {
|
||||
font-family: KaTeX_SansSerif;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.katex .mainrm {
|
||||
font-family: KaTeX_Main;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.katex .vlist-t {
|
||||
display: inline-table;
|
||||
table-layout: fixed;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.katex .vlist-r {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.katex .vlist {
|
||||
display: table-cell;
|
||||
vertical-align: bottom;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.katex .vlist > span {
|
||||
display: block;
|
||||
height: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.katex .vlist > span > span {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.katex .vlist > span > .pstrut {
|
||||
overflow: hidden;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.katex .hlist {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.katex .hlist > span {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.katex .vcenter {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.katex .alignleft {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.katex .aligncenter {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.katex .alignright {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.katex .gothfrak,
|
||||
.katex .textgothfrak {
|
||||
font-family: KaTeX_Fraktur;
|
||||
}
|
||||
|
||||
.katex .cmr {
|
||||
font-family: KaTeX_Main;
|
||||
}
|
||||
|
||||
.katex .cmss {
|
||||
font-family: KaTeX_SansSerif;
|
||||
}
|
||||
|
||||
.katex .cmtt {
|
||||
font-family: KaTeX_Typewriter;
|
||||
}
|
||||
|
||||
.katex .katex-display {
|
||||
display: block;
|
||||
margin: 1em 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.katex .katex-display > .katex {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
text-align: initial;
|
||||
}
|
||||
|
||||
.katex .katex-display.leqno > .katex {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.katex .katex-display.fleqn > .katex {
|
||||
text-align: left;
|
||||
padding-left: 2em;
|
||||
}
|
||||
153
styles/markdown.css
Normal file
153
styles/markdown.css
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/* Import the downloaded CSS files */
|
||||
@import './katex.min.css';
|
||||
@import './highlight-default.min.css';
|
||||
|
||||
/* Additional markdown styling for Obsidian plugin */
|
||||
.markdown-processor-content {
|
||||
line-height: 1.6;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.markdown-processor-content h1,
|
||||
.markdown-processor-content h2,
|
||||
.markdown-processor-content h3,
|
||||
.markdown-processor-content h4,
|
||||
.markdown-processor-content h5,
|
||||
.markdown-processor-content h6 {
|
||||
margin: 1rem 0 0.5rem 0;
|
||||
color: var(--text-normal);
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
|
||||
.markdown-processor-content h1 { font-size: 1.5em; }
|
||||
.markdown-processor-content h2 { font-size: 1.3em; }
|
||||
.markdown-processor-content h3 { font-size: 1.1em; }
|
||||
|
||||
.markdown-processor-content p {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.markdown-processor-content pre {
|
||||
background-color: var(--background-primary-alt);
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-s);
|
||||
overflow-x: auto;
|
||||
margin: 0.5rem 0;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.markdown-processor-content code {
|
||||
background-color: var(--background-primary-alt);
|
||||
padding: 2px 4px;
|
||||
border-radius: var(--radius-s);
|
||||
font-family: var(--font-monospace);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.markdown-processor-content pre code {
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.markdown-processor-content blockquote {
|
||||
border-left: 4px solid var(--background-modifier-border);
|
||||
margin: 1rem 0;
|
||||
padding-left: 1rem;
|
||||
color: var(--text-muted);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.markdown-processor-content ul,
|
||||
.markdown-processor-content ol {
|
||||
padding-left: 1.5rem;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.markdown-processor-content li {
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
.markdown-processor-content table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 1rem 0;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.markdown-processor-content th,
|
||||
.markdown-processor-content td {
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
padding: 8px 12px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown-processor-content th {
|
||||
background-color: var(--background-primary-alt);
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
|
||||
.markdown-processor-content a {
|
||||
color: var(--text-accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markdown-processor-content a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Math content styling adjustments for Obsidian */
|
||||
.markdown-processor-content .katex {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.markdown-processor-content .katex-display {
|
||||
margin: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Task list styling */
|
||||
.markdown-processor-content input[type="checkbox"] {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
/* Footnotes styling */
|
||||
.markdown-processor-content .footnotes {
|
||||
margin-top: 2rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
/* Override highlight.js colors to use Obsidian theme colors */
|
||||
.markdown-processor-content .hljs {
|
||||
background: var(--background-primary-alt) !important;
|
||||
color: var(--text-normal) !important;
|
||||
}
|
||||
|
||||
.markdown-processor-content .hljs-comment,
|
||||
.markdown-processor-content .hljs-quote {
|
||||
color: var(--text-muted) !important;
|
||||
}
|
||||
|
||||
.markdown-processor-content .hljs-keyword,
|
||||
.markdown-processor-content .hljs-selector-tag,
|
||||
.markdown-processor-content .hljs-literal {
|
||||
color: var(--text-accent) !important;
|
||||
}
|
||||
|
||||
.markdown-processor-content .hljs-string,
|
||||
.markdown-processor-content .hljs-doctag {
|
||||
color: var(--color-green) !important;
|
||||
}
|
||||
|
||||
.markdown-processor-content .hljs-title,
|
||||
.markdown-processor-content .hljs-section,
|
||||
.markdown-processor-content .hljs-type {
|
||||
color: var(--color-blue) !important;
|
||||
}
|
||||
|
||||
.markdown-processor-content .hljs-number,
|
||||
.markdown-processor-content .hljs-variable,
|
||||
.markdown-processor-content .hljs-template-variable {
|
||||
color: var(--color-purple) !important;
|
||||
}
|
||||
Loading…
Reference in a new issue