mirror of
https://github.com/andy-stack/vaultkeeper-ai.git
synced 2026-07-22 06:42:03 +00:00
Implement conversation history modal with Svelte component, enhance conversation file system service for loading conversations, and update styles for modal presentation. Refactor file handling methods and remove unused modal service.
This commit is contained in:
parent
d0b4d5b852
commit
74eb20c2a1
15 changed files with 422 additions and 100 deletions
|
|
@ -185,7 +185,7 @@
|
|||
{/each}
|
||||
|
||||
{#if messages.length === 0}
|
||||
<div class="empty-state">
|
||||
<div class="conversation-empty-state">
|
||||
<div class="typing-in">{getGreetingByTime()}</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -241,9 +241,8 @@
|
|||
margin: var(--size-4-2);
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.conversation-empty-state {
|
||||
margin: auto;
|
||||
font-style: italic;
|
||||
color: var(--text-muted);
|
||||
pointer-events: none;
|
||||
|
|
@ -273,7 +272,7 @@
|
|||
.typing-in {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
animation: reveal-center 3s ease-in-out forwards;
|
||||
animation: reveal-center 1.5s ease-in-out forwards;
|
||||
max-width: 0;
|
||||
margin: 0 auto;
|
||||
padding: 5px;
|
||||
|
|
@ -285,21 +284,6 @@
|
|||
opacity: 0;
|
||||
filter: blur(1px);
|
||||
}
|
||||
50% {
|
||||
max-width: 50%;
|
||||
opacity: 0.9;
|
||||
filter: blur(0.5px)
|
||||
}
|
||||
70% {
|
||||
max-width: 60%;
|
||||
opacity: 0.95;
|
||||
filter: blur(0px)
|
||||
}
|
||||
90% {
|
||||
max-width: 80%;
|
||||
opacity: 0.95;
|
||||
filter: blur(0px)
|
||||
}
|
||||
100% {
|
||||
max-width: 100%;
|
||||
opacity: 1;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
import { setIcon, type WorkspaceLeaf } from 'obsidian';
|
||||
import { ConversationFileSystemService } from '../Services/ConversationFileSystemService';
|
||||
import { conversationStore } from '../Stores/conversationStore';
|
||||
import type { ConversationHistoryModal } from 'Modals/ConversationHistoryModal';
|
||||
|
||||
export let leaf: WorkspaceLeaf;
|
||||
|
||||
|
|
@ -22,7 +23,8 @@
|
|||
}
|
||||
|
||||
function openConversationHistory() {
|
||||
|
||||
const modal = Resolve<ConversationHistoryModal>(Services.ConversationHistoryModal);
|
||||
modal.open();
|
||||
}
|
||||
|
||||
function openSettings() {
|
||||
|
|
@ -121,16 +123,6 @@
|
|||
border-radius: var(--radius-m);
|
||||
}
|
||||
|
||||
.top-bar-button {
|
||||
margin: var(--size-4-2) 0px var(--size-4-2) 0px;
|
||||
padding: var(--size-4-1) var(--size-4-2) var(--size-4-1) var(--size-4-2);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.top-bar-button:hover {
|
||||
background-color: var(--color-base-35);
|
||||
}
|
||||
|
||||
.top-bar-divider {
|
||||
width: var(--divider-width);
|
||||
height: auto;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { dateToString } from "Helpers/Helpers";
|
||||
import type { ConversationContent } from "./ConversationContent";
|
||||
import { ConversationContent } from "./ConversationContent";
|
||||
|
||||
export class Conversation {
|
||||
|
||||
|
|
@ -8,6 +8,20 @@ export class Conversation {
|
|||
|
||||
contents: ConversationContent[] = [];
|
||||
|
||||
public static isConversationData(data: unknown): data is { title: string; created: string; contents: ConversationContent[] } {
|
||||
return (
|
||||
typeof data === 'object' &&
|
||||
data !== null &&
|
||||
'title' in data &&
|
||||
'created' in data &&
|
||||
'contents' in data &&
|
||||
typeof data.title === 'string' &&
|
||||
typeof data.created === 'string' &&
|
||||
Array.isArray(data.contents) &&
|
||||
data.contents.every(ConversationContent.isConversationContentData)
|
||||
);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.created = new Date();
|
||||
this.title = `${dateToString(this.created)}`;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,19 @@ export class ConversationContent {
|
|||
content: string
|
||||
timestamp: Date;
|
||||
|
||||
public static isConversationContentData(data: unknown): data is { role: string; content: string; timestamp: string } {
|
||||
return (
|
||||
typeof data === 'object' &&
|
||||
data !== null &&
|
||||
'role' in data &&
|
||||
'content' in data &&
|
||||
'timestamp' in data &&
|
||||
typeof data.role === 'string' &&
|
||||
typeof data.content === 'string' &&
|
||||
typeof data.timestamp === 'string'
|
||||
);
|
||||
}
|
||||
|
||||
constructor(role: string, content: string) {
|
||||
this.role = role;
|
||||
this.content = content;
|
||||
|
|
|
|||
|
|
@ -21,13 +21,21 @@ export function loadExternalCSS(href: string): Promise<void> {
|
|||
});
|
||||
}
|
||||
|
||||
export function dateToString(date: Date): string {
|
||||
return date.toLocaleString('sv-SE', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
}).replace(/[:\s]/g, '-');
|
||||
export function dateToString(date: Date, includeTime: boolean = true): string {
|
||||
if (includeTime) {
|
||||
return date.toLocaleString('sv-SE', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
}).replace(/[:\s]/g, '-');
|
||||
} else {
|
||||
return date.toLocaleDateString('sv-SE', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit'
|
||||
}).replace(/[:\s]/g, '-');
|
||||
}
|
||||
}
|
||||
74
Modals/ConversationHistoryModal.ts
Normal file
74
Modals/ConversationHistoryModal.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import { App, Modal } from 'obsidian';
|
||||
import ConversationHistoryModalSvelte from './ConversationHistoryModalSvelte.svelte';
|
||||
import type { Conversation } from 'Conversations/Conversation';
|
||||
import { mount, unmount } from 'svelte';
|
||||
import { Resolve } from 'Services/DependencyService';
|
||||
import { Services } from 'Services/Services';
|
||||
import type { ConversationFileSystemService } from 'Services/ConversationFileSystemService';
|
||||
import { dateToString } from 'Helpers/Helpers';
|
||||
|
||||
interface ListItem {
|
||||
id: string;
|
||||
date: string;
|
||||
title: string;
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
export class ConversationHistoryModal extends Modal {
|
||||
|
||||
private readonly conversationFileSystemService: ConversationFileSystemService = Resolve(Services.ConversationFileSystemService);
|
||||
|
||||
private component: Record<string, any> | null = null;
|
||||
private items: ListItem[];
|
||||
|
||||
constructor(app: App) {
|
||||
super(app);
|
||||
}
|
||||
|
||||
override async open() {
|
||||
const conversations: Conversation[] = await this.conversationFileSystemService.getAllConversations();
|
||||
|
||||
this.items = conversations.map((conversation, index) => ({
|
||||
id: index.toString(),
|
||||
date: dateToString(conversation.created, false),
|
||||
title: conversation.title,
|
||||
selected: false
|
||||
}));
|
||||
|
||||
super.open();
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const { contentEl, modalEl, containerEl } = this;
|
||||
|
||||
containerEl.addClass('conversation-history-modal');
|
||||
modalEl.addClass('conversation-history-modal');
|
||||
|
||||
this.component = mount(ConversationHistoryModalSvelte, {
|
||||
target: contentEl,
|
||||
props: {
|
||||
items: this.items,
|
||||
onClose: () => this.close(),
|
||||
onDelete: (itemIds: string[]) => this.handleDelete(itemIds)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleDelete(itemIds: string[]) {
|
||||
this.items = this.items.filter(item => !itemIds.includes(item.id));
|
||||
|
||||
if (this.component) {
|
||||
this.component.items = this.items;
|
||||
}
|
||||
}
|
||||
|
||||
onClose() {
|
||||
if (this.component) {
|
||||
unmount(this.component);
|
||||
this.component = null;
|
||||
}
|
||||
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
173
Modals/ConversationHistoryModalSvelte.svelte
Normal file
173
Modals/ConversationHistoryModalSvelte.svelte
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
<script lang="ts">
|
||||
import { setIcon } from "obsidian";
|
||||
import { fade } from "svelte/transition";
|
||||
|
||||
export let items: Array<{id: string, date: string, title: string, selected: boolean}>;
|
||||
export let onClose: () => void;
|
||||
export let onDelete: (itemIds: string[]) => void;
|
||||
|
||||
let deleteButton: HTMLButtonElement;
|
||||
let closeButton: HTMLButtonElement;
|
||||
|
||||
$: if (deleteButton) {
|
||||
setIcon(deleteButton, 'trash-2');
|
||||
}
|
||||
$: if (closeButton) {
|
||||
setIcon(closeButton, 'circle-x');
|
||||
}
|
||||
|
||||
let selectedItems = new Set<string>();
|
||||
|
||||
function toggleSelection(itemId: string) {
|
||||
if (selectedItems.has(itemId)) {
|
||||
selectedItems.delete(itemId);
|
||||
} else {
|
||||
selectedItems.add(itemId);
|
||||
}
|
||||
selectedItems = selectedItems;
|
||||
}
|
||||
|
||||
function handleDelete() {
|
||||
if (selectedItems.size === 0) {
|
||||
return;
|
||||
}
|
||||
onDelete(Array.from(selectedItems));
|
||||
selectedItems.clear();
|
||||
selectedItems = selectedItems;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="conversation-history-modal-container">
|
||||
<div class="conversation-history-modal-top-bar">
|
||||
<div class="conversation-history-modal-top-bar-content">
|
||||
<button
|
||||
bind:this={closeButton}
|
||||
id="close-button"
|
||||
class="top-bar-button clickable-icon"
|
||||
on:click={onClose}
|
||||
aria-label="Close Conversation History"
|
||||
></button>
|
||||
{#if selectedItems.size > 0}
|
||||
<button
|
||||
bind:this={deleteButton}
|
||||
in:fade={{ duration: 200 }} out:fade={{ duration: 200 }}
|
||||
id="delete-button"
|
||||
class="top-bar-button clickable-icon"
|
||||
on:click={handleDelete}
|
||||
aria-label="Delete Selected Conversations"
|
||||
></button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="conversation-history-modal-content">
|
||||
{#if items.length === 0}
|
||||
<p class="history-empty-state">
|
||||
Conversation history is empty.
|
||||
</p>
|
||||
{:else}
|
||||
{#each items as item (item.id)}
|
||||
<div class="history-list-modal-content">
|
||||
<span class="history-list-modal-date">{item.date}</span>
|
||||
<span class="history-list-modal-separator">|</span>
|
||||
<span class="history-list-modal-title">{item.title}</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="history-list-modal-checkbox"
|
||||
checked={selectedItems.has(item.id)}
|
||||
on:change={() => toggleSelection(item.id)}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.conversation-history-modal-container {
|
||||
display: grid;
|
||||
grid-template-rows: var(--size-4-3) auto var(--size-4-3) 1fr var(--size-4-3);
|
||||
grid-template-columns: var(--size-4-3) 1fr var(--size-4-3);
|
||||
}
|
||||
|
||||
.conversation-history-modal-top-bar {
|
||||
grid-row: 2;
|
||||
grid-column: 2;
|
||||
height: var(--size-4-16);
|
||||
display: grid;
|
||||
grid-template-rows: var(--size-4-3) 1fr var(--size-4-3);
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.conversation-history-modal-top-bar-content {
|
||||
grid-row: 2;
|
||||
grid-column: 1;
|
||||
display: grid;
|
||||
grid-template-rows: auto;
|
||||
grid-template-columns: var(--size-4-2) auto 1fr auto var(--size-4-2);
|
||||
background-color: var(--color-base-30);
|
||||
border-radius: var(--radius-m);
|
||||
}
|
||||
|
||||
.conversation-history-modal-content {
|
||||
grid-row: 4;
|
||||
grid-column: 2;
|
||||
min-height: 10vh;
|
||||
overflow: scroll;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.conversation-history-modal-content::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.history-empty-state {
|
||||
margin: 3vh 0px;
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.history-list-modal-content {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr;
|
||||
grid-template-columns: auto auto 1fr auto;
|
||||
}
|
||||
|
||||
.history-list-modal-date {
|
||||
grid-row: 1;
|
||||
grid-column: 1;
|
||||
margin: 0px var(--size-2-3) 0px var(--size-4-3)
|
||||
}
|
||||
|
||||
.history-list-modal-separator {
|
||||
grid-row: 1;
|
||||
grid-column: 2;
|
||||
color: var(--color-base-35);
|
||||
margin: 0px var(--size-4-3);
|
||||
}
|
||||
|
||||
.history-list-modal-title {
|
||||
grid-row: 1;
|
||||
grid-column: 3;
|
||||
display: inline-block;
|
||||
max-width: 80%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.history-list-modal-checkbox {
|
||||
grid-row: 1;
|
||||
grid-column: 4;
|
||||
margin: 0px var(--size-4-3) 0px var(--size-2-3)
|
||||
}
|
||||
|
||||
#delete-button {
|
||||
grid-row: 1;
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
#close-button {
|
||||
grid-row: 1;
|
||||
grid-column: 4;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export class Modals {
|
||||
static SimpleModal = Symbol("SimpleModal");
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
import { Modal, App } from "obsidian";
|
||||
|
||||
class SimpleModal extends Modal {
|
||||
constructor(app: App) {
|
||||
super(app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const { contentEl } = this;
|
||||
contentEl.setText('Woah!');
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,8 @@ import { Path } from "Enums/Path";
|
|||
import { Resolve } from "./DependencyService";
|
||||
import { FileSystemService } from "./FileSystemService";
|
||||
import { Services } from "./Services";
|
||||
import type { TFile } from "obsidian";
|
||||
import { Conversation } from "Conversations/Conversation";
|
||||
import { ConversationContent } from "Conversations/ConversationContent";
|
||||
|
||||
export class ConversationFileSystemService {
|
||||
|
||||
|
|
@ -14,10 +14,8 @@ export class ConversationFileSystemService {
|
|||
this.fileSystemService = Resolve<FileSystemService>(Services.FileSystemService);
|
||||
}
|
||||
|
||||
public generateConversationFilename(): string {
|
||||
const now = new Date();
|
||||
const timestamp = now.toISOString().replace(/:/g, '-').replace(/\..+/, '').replace('T', '-');
|
||||
return `${Path.Conversations}/conversation-${timestamp}.json`;
|
||||
public generateConversationPath(conversation: Conversation): string {
|
||||
return `${Path.Conversations}/${conversation.title}.json`;
|
||||
}
|
||||
|
||||
// public async loadConversation(filePath: string): Promise<{ messages:
|
||||
|
|
@ -27,7 +25,7 @@ export class ConversationFileSystemService {
|
|||
|
||||
public async saveConversation(conversation: Conversation): Promise<string> {
|
||||
if (!this.currentConversationPath) {
|
||||
this.currentConversationPath = this.generateConversationFilename();
|
||||
this.currentConversationPath = this.generateConversationPath(conversation);
|
||||
}
|
||||
|
||||
const conversationData = {
|
||||
|
|
@ -41,7 +39,6 @@ export class ConversationFileSystemService {
|
|||
};
|
||||
|
||||
await this.fileSystemService.writeObjectToFile(this.currentConversationPath, conversationData);
|
||||
console.log("Conversation saved to:", this.currentConversationPath);
|
||||
return this.currentConversationPath;
|
||||
}
|
||||
|
||||
|
|
@ -63,4 +60,26 @@ export class ConversationFileSystemService {
|
|||
return deleted;
|
||||
}
|
||||
|
||||
public async getAllConversations(): Promise<Conversation[]> {
|
||||
const files = await this.fileSystemService.listFilesInDirectory(Path.Conversations, false);
|
||||
const conversations: Conversation[] = [];
|
||||
|
||||
for (const file of files) {
|
||||
const data = await this.fileSystemService.readObjectFromFile(file.path);
|
||||
if (Conversation.isConversationData(data)) {
|
||||
const conversation: Conversation = new Conversation();
|
||||
conversation.title = data.title;
|
||||
conversation.created = new Date(data.created);
|
||||
conversation.contents = data.contents.map(content => {
|
||||
const conversationContent = new ConversationContent(content.role, content.content);
|
||||
conversationContent.timestamp = new Date(content.timestamp);
|
||||
return conversationContent;
|
||||
});
|
||||
conversations.push(conversation);
|
||||
}
|
||||
}
|
||||
|
||||
return conversations;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type AIAgentPlugin from "main";
|
||||
import { TAbstractFile, TFile, type Vault } from "obsidian";
|
||||
import { TAbstractFile, TFile, TFolder, type Vault } from "obsidian";
|
||||
import { Resolve } from "./DependencyService";
|
||||
import { Services } from "./Services";
|
||||
import { isValidJson } from "Helpers/Helpers";
|
||||
|
|
@ -12,32 +12,27 @@ export class FileSystemService {
|
|||
this.vault = Resolve<AIAgentPlugin>(Services.AIAgentPlugin).app.vault;
|
||||
}
|
||||
|
||||
public async readObjectFromFile(filePath: string): Promise<object | null> {
|
||||
public async readFile(filePath: string): Promise<string | null> {
|
||||
const file: TAbstractFile | null = this.vault.getAbstractFileByPath(filePath);
|
||||
if (file && file instanceof TFile) {
|
||||
const content = await this.vault.read(file);
|
||||
if (isValidJson(content) === true) {
|
||||
return JSON.parse(content);
|
||||
}
|
||||
return await this.vault.read(file);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async writeObjectToFile(filePath: string, data: object): Promise<boolean> {
|
||||
public async writeFile(filePath: string, content: string): Promise<boolean> {
|
||||
try {
|
||||
let file: TAbstractFile | null = this.vault.getAbstractFileByPath(filePath);
|
||||
|
||||
if (file && file instanceof TFile) {
|
||||
await this.vault.modify(file, JSON.stringify(data, null, 4));
|
||||
}
|
||||
else {
|
||||
if (file == null || !(file instanceof TFile)) {
|
||||
await this.createDirectories(this.vault, filePath);
|
||||
await this.vault.create(filePath, JSON.stringify(data, null, 4));
|
||||
await this.vault.create(filePath, content);
|
||||
return true;
|
||||
}
|
||||
|
||||
this.vault.modify(file as TFile, content);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error writing JSON file:", error);
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error writing file:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -58,6 +53,56 @@ export class FileSystemService {
|
|||
}
|
||||
}
|
||||
|
||||
public async listFilesInDirectory(dirPath: string, recursive: boolean = true): Promise<TFile[]> {
|
||||
const dir: TAbstractFile | null = this.vault.getAbstractFileByPath(dirPath);
|
||||
|
||||
if (dir == null || !(dir instanceof TFolder)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let files: TFile[] = [];
|
||||
for (let child of dir.children) {
|
||||
if (child instanceof TFile) {
|
||||
files.push(child);
|
||||
} else if (child instanceof TFolder && recursive) {
|
||||
const childFiles = await this.listFilesInDirectory(child.path, recursive);
|
||||
files = files.concat(childFiles);
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
public async readObjectFromFile(filePath: string): Promise<object | null> {
|
||||
const file: TAbstractFile | null = this.vault.getAbstractFileByPath(filePath);
|
||||
if (file && file instanceof TFile) {
|
||||
const content = await this.vault.read(file);
|
||||
if (isValidJson(content) === true) {
|
||||
return JSON.parse(content);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async writeObjectToFile(filePath: string, data: object): Promise<boolean> {
|
||||
try {
|
||||
let file: TAbstractFile | null = this.vault.getAbstractFileByPath(filePath);
|
||||
|
||||
if (file && file instanceof TFile) {
|
||||
await this.vault.modify(file, JSON.stringify(data, null, 4));
|
||||
}
|
||||
else {
|
||||
await this.createDirectories(this.vault, filePath);
|
||||
await this.vault.create(filePath, JSON.stringify(data, null, 4));
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error writing JSON file:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async createDirectories(vault: Vault, filePath: string) {
|
||||
const dirPath: string = filePath.substring(0, filePath.lastIndexOf('/'));
|
||||
|
||||
|
|
@ -73,5 +118,4 @@ export class FileSystemService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import { Modal } from "obsidian";
|
||||
import { Resolve } from "./DependencyService";
|
||||
|
||||
export class ModalService {
|
||||
|
||||
showModal(modal: symbol): void {
|
||||
let modalInstance: Modal = Resolve(modal);
|
||||
modalInstance.open();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ import { AIProvider } from "Enums/ApiProvider";
|
|||
import type AIAgentPlugin from "main";
|
||||
//import { OdbCache } from "ODB/Core/OdbCache";
|
||||
import { RegisterSingleton, RegisterTransient } from "./DependencyService";
|
||||
import { ModalService } from "./ModalService";
|
||||
import { Services } from "./Services";
|
||||
import { AIPrompt, type IPrompt } from "AIClasses/IPrompt";
|
||||
import { Actioner } from "Actioner/Actioner";
|
||||
|
|
@ -15,12 +14,13 @@ import { StreamingMarkdownService } from "./StreamingMarkdownService";
|
|||
import { MessageService } from "./MessageService";
|
||||
import { FileSystemService } from "./FileSystemService";
|
||||
import { ConversationFileSystemService } from "./ConversationFileSystemService";
|
||||
import { ConversationHistoryModal } from "Modals/ConversationHistoryModal";
|
||||
import type { App } from "obsidian";
|
||||
|
||||
export function RegisterDependencies(plugin: AIAgentPlugin) {
|
||||
RegisterSingleton(Services.MessageService, new MessageService());
|
||||
RegisterSingleton(Services.AIAgentPlugin, plugin);
|
||||
//RegisterSingleton(Services.OdbCache, new OdbCache());
|
||||
RegisterSingleton(Services.ModalService, new ModalService())
|
||||
RegisterSingleton(Services.FileSystemService, new FileSystemService());
|
||||
RegisterSingleton(Services.ConversationFileSystemService, new ConversationFileSystemService());
|
||||
|
||||
|
|
@ -29,6 +29,7 @@ export function RegisterDependencies(plugin: AIAgentPlugin) {
|
|||
|
||||
RegisterTransient<StreamingMarkdownService>(Services.StreamingMarkdownService, () => new StreamingMarkdownService());
|
||||
|
||||
RegisterModals(plugin.app);
|
||||
RegisterAiProvider(plugin);
|
||||
}
|
||||
|
||||
|
|
@ -37,4 +38,8 @@ export function RegisterAiProvider(plugin: AIAgentPlugin) {
|
|||
RegisterTransient<IActionDefinitions>(Services.IActionDefinitions, () => new GeminiActionDefinitions());
|
||||
RegisterSingleton<IAIClass>(Services.IAIClass, new Gemini(plugin.settings.apiKey));
|
||||
}
|
||||
}
|
||||
|
||||
function RegisterModals(app: App) {
|
||||
RegisterTransient<ConversationHistoryModal>(Services.ConversationHistoryModal, () => new ConversationHistoryModal(app));
|
||||
}
|
||||
|
|
@ -2,17 +2,18 @@ export class Services {
|
|||
static MessageService = Symbol("MessageService");
|
||||
static AIAgentPlugin = Symbol("AIAgentPlugin");
|
||||
static OdbCache = Symbol("OdbCache");
|
||||
static ModalService = Symbol("ModalService");
|
||||
static FileSystemService = Symbol("FileSystemService");
|
||||
static ConversationFileSystemService = Symbol("ConversationFileSystemService");
|
||||
static StreamingService = Symbol("StreamingService");
|
||||
static MarkdownService = Symbol("MarkdownService");
|
||||
static StreamingMarkdownService = Symbol("StreamingMarkdownService");
|
||||
|
||||
|
||||
// interfaces
|
||||
static IAIClass = Symbol("IAIClass");
|
||||
static IPrompt = Symbol("IPrompt");
|
||||
static IActioner = Symbol("IActioner");
|
||||
static IActionDefinitions = Symbol("IActionDefinitions");
|
||||
|
||||
// modals
|
||||
static ConversationHistoryModal = Symbol("ConversationHistoryModal");
|
||||
}
|
||||
26
styles.css
26
styles.css
|
|
@ -29,6 +29,32 @@
|
|||
padding-top: 0;
|
||||
}
|
||||
|
||||
/* ============================== */
|
||||
/* CSS Variables for Common Components */
|
||||
/* ============================== */
|
||||
|
||||
/* does this affect all modals? */
|
||||
.conversation-history-modal {
|
||||
padding: 0px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.conversation-history-modal .modal-header {
|
||||
display: none;
|
||||
}
|
||||
.conversation-history-modal .modal-close-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.top-bar-button {
|
||||
margin: var(--size-4-2) 0px var(--size-4-2) 0px;
|
||||
padding: var(--size-4-1) var(--size-4-2) var(--size-4-1) var(--size-4-2);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.top-bar-button:hover {
|
||||
background-color: var(--color-base-35);
|
||||
}
|
||||
|
||||
/* ============================== */
|
||||
/* CSS Variables for Theming */
|
||||
/* ============================== */
|
||||
|
|
|
|||
Loading…
Reference in a new issue