mirror of
https://github.com/andy-stack/vaultkeeper-ai.git
synced 2026-07-22 06:42:03 +00:00
Implement a comprehensive diff viewing system that allows users to review and approve/reject file changes before they're applied. The system includes event-driven architecture for managing diff lifecycle and integrates diff2html for rich visual diffs. Key changes: - Add DiffService for managing diff approval workflow with accept/reject/suggest actions - Create EventService for type-safe event handling (DiffOpened/DiffClosed) - Add DiffView component with diff2html integration for visual diff rendering - Modify VaultService to propose changes and require confirmation before file operations - Update FileSystemService to support optional confirmation for write operations - Add Event enum for centralized event type definitions - Import custom styles and diff2html styles for proper diff rendering - Update ConversationContent validation to support optional toolId field - Remove FileManager from dependency injection (now accessed directly from app) - Update .gitignore to track styles.css instead of main.css
82 lines
No EOL
2.6 KiB
TypeScript
82 lines
No EOL
2.6 KiB
TypeScript
import * as Diff from 'diff';
|
|
import type VaultkeeperAIPlugin from 'main';
|
|
import { Resolve } from './DependencyService';
|
|
import { Services } from './Services';
|
|
import type { EventService } from './EventService';
|
|
import { Event } from 'Enums/Event';
|
|
import type { Diff2HtmlUIConfig } from 'diff2html/lib/ui/js/diff2html-ui';
|
|
import { ColorSchemeType, OutputFormatType } from 'diff2html/lib/types';
|
|
import { Platform } from 'obsidian';
|
|
|
|
interface DiffResult {
|
|
accepted: boolean;
|
|
suggestion?: string;
|
|
}
|
|
|
|
export class DiffService {
|
|
|
|
private readonly plugin: VaultkeeperAIPlugin;
|
|
private readonly eventService: EventService;
|
|
|
|
private diffResolve?: (result: DiffResult) => void;
|
|
|
|
public constructor() {
|
|
this.plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
|
|
this.eventService = Resolve<EventService>(Services.EventService);
|
|
}
|
|
|
|
public async requestDiff(oldFileName: string, newFileName: string, oldContent: string, newContent: string): Promise<DiffResult> {
|
|
const diffString = this.createDiffString(oldFileName, newFileName, oldContent, newContent);
|
|
|
|
const outputFormat: OutputFormatType = (Platform.isMobile || oldContent.trim() === "") ? "line-by-line" : "side-by-side";
|
|
|
|
const config: Diff2HtmlUIConfig = {
|
|
drawFileList: false,
|
|
matching: "words",
|
|
outputFormat: outputFormat,
|
|
highlight: true,
|
|
fileListToggle: false,
|
|
fileContentToggle: false,
|
|
synchronisedScroll: true,
|
|
colorScheme: ColorSchemeType.AUTO
|
|
};
|
|
|
|
return new Promise((resolve) => {
|
|
this.diffResolve = resolve;
|
|
|
|
void this.plugin.activateDiffView(diffString, config);
|
|
this.eventService.trigger(Event.DiffOpened);
|
|
});
|
|
}
|
|
|
|
public onAccept() {
|
|
if (this.diffResolve) {
|
|
this.diffResolve({ accepted: true });
|
|
}
|
|
this.finishDiff();
|
|
}
|
|
|
|
public onReject() {
|
|
if (this.diffResolve) {
|
|
this.diffResolve({ accepted: false });
|
|
}
|
|
this.finishDiff();
|
|
}
|
|
|
|
public onSuggest(suggestion: string) {
|
|
if (this.diffResolve) {
|
|
this.diffResolve({ accepted: false, suggestion: suggestion });
|
|
}
|
|
this.finishDiff();
|
|
}
|
|
|
|
private createDiffString(oldFileName: string, newFileName: string, oldContent: string, newContent: string): string {
|
|
return Diff.createTwoFilesPatch(oldFileName, newFileName, oldContent, newContent);
|
|
}
|
|
|
|
private finishDiff() {
|
|
this.diffResolve = undefined;
|
|
this.eventService.trigger(Event.DiffClosed);
|
|
}
|
|
|
|
} |