murashit_codex-panel/src/main.ts
2026-05-23 09:53:54 +09:00

121 lines
4.1 KiB
TypeScript

import { Plugin, type WorkspaceLeaf } from "obsidian";
import { VIEW_TYPE_CODEX_PANEL, VIEW_TYPE_CODEX_TURN_DIFF } from "./constants";
import { registerSelectionRewriteCommand } from "./features/selection-rewrite/command";
import { CodexChatView } from "./features/chat/view";
import { CodexChatTurnDiffView } from "./features/chat/chat-turn-diff-view";
import { DEFAULT_SETTINGS, getVaultPath, normalizeSettings, settingsMatchNormalizedData, type CodexPanelSettings } from "./settings/model";
import { CodexPanelSettingTab } from "./settings/tab";
import { persistedChatTurnDiffViewState, type ChatTurnDiffViewState } from "./features/chat/ui/turn-diff";
export default class CodexPanelPlugin extends Plugin {
settings: CodexPanelSettings = DEFAULT_SETTINGS;
vaultPath = "";
override async onload(): Promise<void> {
this.vaultPath = getVaultPath(this.app);
await this.loadSettings();
this.registerView(VIEW_TYPE_CODEX_PANEL, (leaf) => new CodexChatView(leaf, this));
this.registerView(VIEW_TYPE_CODEX_TURN_DIFF, (leaf) => new CodexChatTurnDiffView(leaf));
this.addRibbonIcon("bot-message-square", "Open panel", () => {
void this.activateView();
});
this.addCommand({
id: "open-panel",
name: "Open panel",
callback: () => void this.activateView(),
});
this.addCommand({
id: "open-new-panel",
name: "Open new panel",
callback: () => void this.activateNewView(),
});
this.addCommand({
id: "new-chat",
name: "New chat",
callback: async () => {
const view = await this.activateView();
await view.startNewThread();
},
});
registerSelectionRewriteCommand(this);
this.addSettingTab(new CodexPanelSettingTab(this.app, this));
}
async activateView(): Promise<CodexChatView> {
const leaf = await this.app.workspace.ensureSideLeaf(VIEW_TYPE_CODEX_PANEL, "right", {
active: true,
reveal: true,
});
return leaf.view as CodexChatView;
}
async activateNewView(): Promise<CodexChatView> {
const leaf = this.createRightSidebarTab();
if (!leaf) throw new Error("Could not create a right sidebar leaf.");
await leaf.setViewState({ type: VIEW_TYPE_CODEX_PANEL, active: true });
await this.app.workspace.revealLeaf(leaf);
return leaf.view as CodexChatView;
}
async openThreadInNewView(threadId: string): Promise<CodexChatView> {
const view = await this.activateNewView();
await view.openThread(threadId);
return view;
}
async openTurnDiff(state: ChatTurnDiffViewState): Promise<void> {
const existing = this.app.workspace.getLeavesOfType(VIEW_TYPE_CODEX_TURN_DIFF).at(0);
const leaf = existing ?? this.app.workspace.getLeaf("tab");
await leaf.setViewState({ type: VIEW_TYPE_CODEX_TURN_DIFF, active: true, state: { ...persistedChatTurnDiffViewState(state) } });
await leaf.loadIfDeferred();
if (leaf.view instanceof CodexChatTurnDiffView) {
leaf.view.setDiffPayload(state);
}
await this.app.workspace.revealLeaf(leaf);
}
refreshOpenViews(): void {
for (const leaf of this.app.workspace.getLeavesOfType(VIEW_TYPE_CODEX_PANEL)) {
if (leaf.view instanceof CodexChatView) {
leaf.view.refreshSettings();
}
}
}
refreshOpenThreadLists(): void {
for (const leaf of this.app.workspace.getLeavesOfType(VIEW_TYPE_CODEX_PANEL)) {
if (leaf.view instanceof CodexChatView) {
leaf.view.refreshThreadList();
}
}
}
async loadSettings(): Promise<void> {
const data: unknown = await this.loadData();
this.settings = normalizeSettings(data);
if (!settingsMatchNormalizedData(data, this.settings)) {
await this.saveSettings();
}
}
async saveSettings(): Promise<void> {
await this.saveData(this.settings);
}
private createRightSidebarTab(): WorkspaceLeaf | null {
const { workspace } = this.app;
const existing = workspace.getLeavesOfType(VIEW_TYPE_CODEX_PANEL).find((leaf) => leaf.getRoot() === workspace.rightSplit);
if (!existing) return workspace.getRightLeaf(false);
return workspace.createLeafInParent(existing.parent, Number.MAX_SAFE_INTEGER);
}
}