Update dependencies and replace deprecated API calls
Replace activeDocument/activeWindow globals with Obsidian API equivalents (createFragment, createDiv, createSpan, window), update officeparser to use async API, switch diff2html import path and disable highlight option, prefix unused parameters with underscore, and update dependencies including @anthropic-ai/sdk, @google/genai, officeparser, and various dev dependencies
2026-05-17 11:42:09 +00:00
|
|
|
import { Diff2HtmlUI, type Diff2HtmlUIConfig } from "diff2html/lib/ui/js/diff2html-ui-base";
|
2025-11-27 12:39:08 +00:00
|
|
|
import { Event } from "Enums/Event";
|
2025-12-06 16:38:01 +00:00
|
|
|
import { ItemView, WorkspaceLeaf, Platform, type ViewStateResult } from "obsidian";
|
2025-11-27 12:39:08 +00:00
|
|
|
import { Resolve } from "Services/DependencyService";
|
|
|
|
|
import type { EventService } from "Services/EventService";
|
2025-12-06 16:38:01 +00:00
|
|
|
import type { DiffService } from "Services/DiffService";
|
2025-11-27 12:39:08 +00:00
|
|
|
import { Services } from "Services/Services";
|
2026-07-05 18:59:07 +00:00
|
|
|
import { VIEW_TYPE_MAIN, type MainView } from "./MainView";
|
2025-12-13 14:45:56 +00:00
|
|
|
import { tick } from "svelte";
|
2025-11-24 21:29:54 +00:00
|
|
|
|
|
|
|
|
export const VIEW_TYPE_DIFF = 'vaultkeeper-ai-diff-view';
|
|
|
|
|
|
|
|
|
|
interface DiffViewState {
|
|
|
|
|
diffString: string;
|
|
|
|
|
config: Diff2HtmlUIConfig;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class DiffView extends ItemView {
|
2025-11-27 12:39:08 +00:00
|
|
|
|
|
|
|
|
private readonly eventService: EventService;
|
2025-12-06 16:38:01 +00:00
|
|
|
private readonly diffService: DiffService;
|
2025-11-27 12:39:08 +00:00
|
|
|
|
2025-11-24 21:29:54 +00:00
|
|
|
private diffString: string = "";
|
|
|
|
|
private config: Diff2HtmlUIConfig = {};
|
|
|
|
|
|
|
|
|
|
private diffContainer: HTMLElement | null = null;
|
2025-12-06 16:38:01 +00:00
|
|
|
private mobileButtonsContainer: HTMLElement | null = null;
|
2025-11-24 21:29:54 +00:00
|
|
|
|
|
|
|
|
constructor(leaf: WorkspaceLeaf) {
|
|
|
|
|
super(leaf);
|
2025-11-27 12:39:08 +00:00
|
|
|
|
|
|
|
|
this.eventService = Resolve<EventService>(Services.EventService);
|
2025-12-06 16:38:01 +00:00
|
|
|
this.diffService = Resolve<DiffService>(Services.DiffService);
|
2025-11-27 12:39:08 +00:00
|
|
|
|
|
|
|
|
this.registerEvent(this.eventService.on(Event.DiffClosed, () => {
|
|
|
|
|
this.leaf.detach();
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override onClose(): Promise<void> {
|
|
|
|
|
// trigger DiffClosed event in case the user closed the tab
|
|
|
|
|
this.eventService.trigger(Event.DiffClosed);
|
|
|
|
|
return Promise.resolve();
|
2025-11-24 21:29:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getViewType(): string {
|
|
|
|
|
return VIEW_TYPE_DIFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getDisplayText(): string {
|
|
|
|
|
return "Vaultkeeper AI diff";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async setState(state: DiffViewState, result: ViewStateResult): Promise<void> {
|
|
|
|
|
this.diffString = state.diffString;
|
|
|
|
|
this.config = state.config;
|
|
|
|
|
|
|
|
|
|
this.renderDiff();
|
|
|
|
|
|
|
|
|
|
return super.setState(state, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getState(): Record<string, unknown> {
|
|
|
|
|
return {
|
|
|
|
|
diffString: this.diffString,
|
|
|
|
|
config: this.config
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private renderDiff() {
|
2025-11-27 12:39:08 +00:00
|
|
|
const container = this.resetContainer();
|
|
|
|
|
|
2025-12-06 16:38:01 +00:00
|
|
|
if (Platform.isMobile) {
|
|
|
|
|
container.addClass('diff-view-mobile');
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-27 12:39:08 +00:00
|
|
|
this.diffContainer = container.createDiv({ cls: 'd2h-wrapper' });
|
|
|
|
|
const diff2htmlUi = new Diff2HtmlUI(this.diffContainer, this.diffString, this.config);
|
|
|
|
|
|
|
|
|
|
diff2htmlUi.draw();
|
2025-12-05 18:39:01 +00:00
|
|
|
|
2025-12-06 16:38:01 +00:00
|
|
|
if (Platform.isMobile) {
|
|
|
|
|
this.mobileButtonsContainer = this.createMobileButtons();
|
|
|
|
|
}
|
|
|
|
|
|
Update dependencies and replace deprecated API calls
Replace activeDocument/activeWindow globals with Obsidian API equivalents (createFragment, createDiv, createSpan, window), update officeparser to use async API, switch diff2html import path and disable highlight option, prefix unused parameters with underscore, and update dependencies including @anthropic-ai/sdk, @google/genai, officeparser, and various dev dependencies
2026-05-17 11:42:09 +00:00
|
|
|
window.requestAnimationFrame(() => {
|
2025-12-05 18:39:01 +00:00
|
|
|
const firstChange = this.diffContainer?.querySelector('.d2h-change, .d2h-ins, .d2h-del');
|
|
|
|
|
firstChange?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
|
|
|
});
|
2025-11-27 12:39:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private resetContainer(): HTMLElement {
|
2025-11-24 21:29:54 +00:00
|
|
|
const container = this.contentEl;
|
|
|
|
|
container.empty();
|
|
|
|
|
|
|
|
|
|
if (this.diffContainer) {
|
|
|
|
|
this.diffContainer.remove();
|
2025-12-06 16:38:01 +00:00
|
|
|
this.diffContainer = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.mobileButtonsContainer) {
|
|
|
|
|
this.mobileButtonsContainer.remove();
|
|
|
|
|
this.mobileButtonsContainer = null;
|
2025-11-24 21:29:54 +00:00
|
|
|
}
|
2025-12-06 16:38:01 +00:00
|
|
|
|
2025-11-27 12:39:08 +00:00
|
|
|
return container;
|
2025-11-24 21:29:54 +00:00
|
|
|
}
|
2025-11-27 12:39:08 +00:00
|
|
|
|
2025-12-06 16:38:01 +00:00
|
|
|
private createMobileButtons(): HTMLElement {
|
|
|
|
|
const container = this.contentEl.createDiv({ cls: 'diff-mobile-controls' });
|
|
|
|
|
|
|
|
|
|
const acceptButton = container.createEl('button', {
|
|
|
|
|
cls: 'diff-mobile-button diff-mobile-accept',
|
|
|
|
|
text: 'Accept'
|
|
|
|
|
});
|
|
|
|
|
acceptButton.setAttribute('aria-label', 'Accept changes');
|
|
|
|
|
|
2026-07-05 18:59:07 +00:00
|
|
|
const suggestButton = container.createEl('button', {
|
|
|
|
|
cls: 'diff-mobile-button diff-mobile-suggest',
|
|
|
|
|
text: 'Suggest'
|
|
|
|
|
});
|
|
|
|
|
suggestButton.setAttribute('aria-label', 'Make a suggestion');
|
|
|
|
|
|
2025-12-06 16:38:01 +00:00
|
|
|
const rejectButton = container.createEl('button', {
|
|
|
|
|
cls: 'diff-mobile-button diff-mobile-reject',
|
|
|
|
|
text: 'Reject'
|
|
|
|
|
});
|
|
|
|
|
rejectButton.setAttribute('aria-label', 'Reject changes');
|
|
|
|
|
|
|
|
|
|
this.registerDomEvent(acceptButton, 'click', async () => {
|
|
|
|
|
this.diffService.onAccept();
|
|
|
|
|
await this.refocusMainView();
|
|
|
|
|
});
|
|
|
|
|
|
2026-07-05 18:59:07 +00:00
|
|
|
this.registerDomEvent(suggestButton, 'click', async () => {
|
|
|
|
|
await this.refocusMainView(true);
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-06 16:38:01 +00:00
|
|
|
this.registerDomEvent(rejectButton, 'click', async () => {
|
|
|
|
|
this.diffService.onReject();
|
|
|
|
|
await this.refocusMainView();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return container;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-05 18:59:07 +00:00
|
|
|
private async refocusMainView(focusChatInput: boolean = false): Promise<void> {
|
2025-12-13 14:45:56 +00:00
|
|
|
await tick().then(async () => {
|
|
|
|
|
const { workspace } = this.app;
|
|
|
|
|
const leaves = workspace.getLeavesOfType(VIEW_TYPE_MAIN);
|
|
|
|
|
|
|
|
|
|
if (leaves.length > 0) {
|
|
|
|
|
await workspace.revealLeaf(leaves[0]);
|
|
|
|
|
workspace.setActiveLeaf(leaves[0], { focus: true });
|
2026-07-05 18:59:07 +00:00
|
|
|
|
|
|
|
|
if (focusChatInput) {
|
|
|
|
|
(leaves[0].view as MainView).input?.focusInput(true);
|
|
|
|
|
}
|
2025-12-13 14:45:56 +00:00
|
|
|
}
|
|
|
|
|
});
|
2025-12-06 16:38:01 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-24 21:29:54 +00:00
|
|
|
}
|