mirror of
https://github.com/andy-stack/vaultkeeper-ai.git
synced 2026-07-22 16:30:27 +00:00
This change simplifies the patch mechanism by replacing the unified diff format with a straightforward find-and-replace approach. The PatchVaultFile function now accepts oldContent and newContent parameters instead of a diff patch string, making it more intuitive and reliable for AI agents to use. Key changes: - Update PatchVaultFile function signature and documentation - Replace diff.applyPatch() with String.replace() - Simplify error handling for content matching - Update all tests to reflect new API - Remove unused applyPatch method from DiffService - Fix diff container height calculation in CSS - Fix error notification logic in ChatService - Fix conversation deletion order in TopBar component
120 lines
No EOL
3.8 KiB
TypeScript
120 lines
No EOL
3.8 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 { Component } from 'obsidian';
|
|
import { AbortService } from './AbortService';
|
|
|
|
interface DiffResult {
|
|
accepted: boolean;
|
|
suggestion?: string;
|
|
}
|
|
|
|
export class DiffService extends Component {
|
|
|
|
private readonly plugin: VaultkeeperAIPlugin;
|
|
private readonly eventService: EventService;
|
|
private readonly abortService: AbortService;
|
|
|
|
private diffResolve?: (result: DiffResult) => void;
|
|
|
|
private ongoingDiff: boolean = false;
|
|
|
|
public constructor() {
|
|
super();
|
|
this.plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
|
|
this.eventService = Resolve<EventService>(Services.EventService);
|
|
this.abortService = Resolve<AbortService>(Services.AbortService);
|
|
|
|
this.registerEvent(this.eventService.on(Event.DiffClosed, () => {
|
|
this.cancelPendingDiff();
|
|
}));
|
|
}
|
|
|
|
public async requestDiff(oldFileName: string, newFileName: string, oldContent: string, newContent: string): Promise<DiffResult> {
|
|
const diffString = this.createDiffString(oldFileName, newFileName, oldContent, newContent);
|
|
|
|
const outputFormat: OutputFormatType = "line-by-line";
|
|
|
|
const config: Diff2HtmlUIConfig = {
|
|
drawFileList: false,
|
|
matching: "words",
|
|
outputFormat: outputFormat,
|
|
highlight: true,
|
|
fileListToggle: false,
|
|
fileContentToggle: false,
|
|
synchronisedScroll: true,
|
|
colorScheme: ColorSchemeType.AUTO
|
|
};
|
|
|
|
this.ongoingDiff = true;
|
|
|
|
const signal = this.abortService.signal();
|
|
return new Promise<DiffResult>((resolve, reject) => {
|
|
if (signal.aborted) {
|
|
this.finishDiff();
|
|
reject(this.abortService.reason());
|
|
return;
|
|
}
|
|
|
|
const abortHandler = () => {
|
|
this.finishDiff();
|
|
reject(this.abortService.reason());
|
|
};
|
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
|
|
this.diffResolve = (result: DiffResult) => {
|
|
signal.removeEventListener("abort", abortHandler);
|
|
resolve(result);
|
|
};
|
|
|
|
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 cancelPendingDiff() {
|
|
if (this.ongoingDiff) {
|
|
if (this.diffResolve) {
|
|
this.diffResolve({ accepted: false });
|
|
}
|
|
this.finishDiff();
|
|
}
|
|
}
|
|
|
|
private createDiffString(oldFileName: string, newFileName: string, oldContent: string, newContent: string): string {
|
|
return Diff.createTwoFilesPatch(oldFileName, newFileName, oldContent, newContent, undefined, undefined, { context: Infinity });
|
|
}
|
|
|
|
private finishDiff() {
|
|
this.ongoingDiff = false;
|
|
this.diffResolve = undefined;
|
|
this.eventService.trigger(Event.DiffClosed);
|
|
}
|
|
|
|
} |