mirror of
https://github.com/logancyang/obsidian-copilot.git
synced 2026-07-22 07:50:24 +00:00
chore(eslint): enable no-unnecessary-type-assertion and no-misused-promises (#2441)
* chore(eslint): enable no-unnecessary-type-assertion and no-misused-promises
Auto-fixed all 40 no-unnecessary-type-assertion violations via `eslint --fix`.
For no-misused-promises, configured `checksVoidReturn` with two relaxations
(documented inline):
- `attributes: false` — `onClick={async () => ...}` is the standard React pattern
- `inheritedMethods: false` — Obsidian's Plugin.onload/onunload are async
Fixed the remaining 10 violations by hand:
- Wrapped async subscribers/listeners with `void` (projectManager, useChatFileDrop,
main.ts, webViewerServiceSelection)
- Widened ConfirmModal subclass callback types to `() => void | Promise<void>`
- Widened MobileCardDropdownAction.onClick to match parent prop types
- Made SettingsMainV2 handleReset sync (it never awaited anything)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(eslint): move no-misused-promises to TS-only config block
The @typescript-eslint plugin is registered only for .ts/.tsx files via
obsidianmd's recommendedTypeChecked. Enabling the rule in the
JS+TS-shared block caused CI to fail with "could not find plugin
@typescript-eslint" because the plugin isn't loaded for .js/.mjs files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3b8af4d341
commit
8499b85a1b
26 changed files with 80 additions and 68 deletions
|
|
@ -95,8 +95,6 @@ export default [
|
|||
|
||||
// --- Medium: promise / method ergonomics ---
|
||||
"@typescript-eslint/unbound-method": "off", // 68 violations
|
||||
"@typescript-eslint/no-misused-promises": "off", // 46 violations
|
||||
"@typescript-eslint/no-unnecessary-type-assertion": "off", // 40 violations
|
||||
"@typescript-eslint/no-floating-promises": "off", // 39 violations
|
||||
|
||||
// --- Quick wins: small enough to fix and enable in a single PR ---
|
||||
|
|
@ -172,6 +170,14 @@ export default [
|
|||
"@typescript-eslint/no-empty-function": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-unused-vars": ["error", { args: "none" }],
|
||||
// checksVoidReturn relaxed for:
|
||||
// - attributes: async event handlers in JSX (onClick={async () => ...}) are
|
||||
// the standard React pattern; React already handles them correctly.
|
||||
// - inheritedMethods: Obsidian's Plugin.onload/onunload are commonly async.
|
||||
"@typescript-eslint/no-misused-promises": [
|
||||
"error",
|
||||
{ checksVoidReturn: { attributes: false, inheritedMethods: false } },
|
||||
],
|
||||
// TypeScript handles undefined-identifier detection (and does so cross-realm
|
||||
// correctly); per typescript-eslint's own guidance, disable no-undef on TS.
|
||||
"no-undef": "off",
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ export class ChatOpenRouter extends ChatOpenAI {
|
|||
role: "tool",
|
||||
content: msg.content,
|
||||
tool_call_id: msg.tool_call_id,
|
||||
} as OpenRouterMessageParam;
|
||||
};
|
||||
}
|
||||
|
||||
if (msg.additional_kwargs?.function_call) {
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ export class GitHubCopilotChatModel extends ChatOpenAICompletions {
|
|||
|
||||
const provider = GitHubCopilotProvider.getInstance();
|
||||
// scorecard: streaming requires fetch — cannot use requestUrl
|
||||
const baseFetch = fetchImplementation ?? (configuration?.fetch as FetchImplementation) ?? fetch;
|
||||
const baseFetch = fetchImplementation ?? configuration?.fetch ?? fetch;
|
||||
const authedFetch = GitHubCopilotChatModel.buildAuthedFetch(provider, baseFetch);
|
||||
|
||||
super({
|
||||
|
|
@ -115,7 +115,7 @@ export class GitHubCopilotChatModel extends ChatOpenAICompletions {
|
|||
configuration: {
|
||||
...(configuration ?? {}),
|
||||
// Reason: OpenAI SDK appends "/chat/completions" to baseURL automatically
|
||||
baseURL: (configuration?.baseURL as string) ?? COPILOT_API_BASE,
|
||||
baseURL: configuration?.baseURL ?? COPILOT_API_BASE,
|
||||
fetch: authedFetch,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ export class GitHubCopilotResponsesModel extends ChatOpenAI {
|
|||
const { fetchImplementation, configuration, apiKey, ...rest } = fields;
|
||||
|
||||
const provider = GitHubCopilotProvider.getInstance();
|
||||
const baseFetch = fetchImplementation ?? (configuration?.fetch as FetchImplementation) ?? fetch;
|
||||
const baseFetch = fetchImplementation ?? configuration?.fetch ?? fetch;
|
||||
const authedFetch = buildGitHubCopilotAuthedFetch(provider, baseFetch);
|
||||
|
||||
super({
|
||||
|
|
@ -100,7 +100,7 @@ export class GitHubCopilotResponsesModel extends ChatOpenAI {
|
|||
streamUsage: false,
|
||||
configuration: {
|
||||
...(configuration ?? {}),
|
||||
baseURL: (configuration?.baseURL as string) ?? COPILOT_API_BASE,
|
||||
baseURL: configuration?.baseURL ?? COPILOT_API_BASE,
|
||||
fetch: authedFetch,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -57,11 +57,11 @@ export default class ProjectManager {
|
|||
this.loadTracker = ProjectLoadTracker.getInstance(this.app);
|
||||
|
||||
// Set up subscriptions
|
||||
subscribeToModelKeyChange(async () => {
|
||||
await this.getCurrentChainManager().createChainWithNewModel();
|
||||
subscribeToModelKeyChange(() => {
|
||||
void this.getCurrentChainManager().createChainWithNewModel();
|
||||
});
|
||||
|
||||
subscribeToChainTypeChange(async () => {
|
||||
subscribeToChainTypeChange(() => {
|
||||
// When switching from other modes to project mode, no need to update the chain.
|
||||
if (isProjectMode()) {
|
||||
return;
|
||||
|
|
@ -72,14 +72,14 @@ export default class ProjectManager {
|
|||
settings.indexVaultToVectorStore === VAULT_VECTOR_STORE_STRATEGY.ON_MODE_SWITCH &&
|
||||
(getChainType() === ChainType.VAULT_QA_CHAIN ||
|
||||
getChainType() === ChainType.COPILOT_PLUS_CHAIN);
|
||||
await this.getCurrentChainManager().createChainWithNewModel({
|
||||
void this.getCurrentChainManager().createChainWithNewModel({
|
||||
refreshIndex: shouldAutoIndex,
|
||||
});
|
||||
});
|
||||
|
||||
// Subscribe to Project changes
|
||||
subscribeToProjectChange(async (project) => {
|
||||
await this.switchProject(project);
|
||||
subscribeToProjectChange((project) => {
|
||||
void this.switchProject(project);
|
||||
});
|
||||
|
||||
// Subscribe to project cache changes to monitor project modifications
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ describe("processedPrompt()", () => {
|
|||
.mockResolvedValueOnce("here is the note content for note0")
|
||||
.mockResolvedValueOnce("note content for note1");
|
||||
|
||||
const { getFileName } = jest.requireMock("@/utils") as any;
|
||||
const { getFileName } = jest.requireMock("@/utils");
|
||||
getFileName.mockReturnValueOnce("Variable1 Note").mockReturnValueOnce("Variable2 Note");
|
||||
|
||||
const mockNote1 = mockTFile({ path: "path/to/note1.md", basename: "Variable1 Note" });
|
||||
|
|
@ -175,7 +175,7 @@ describe("processedPrompt()", () => {
|
|||
const selectedText = "";
|
||||
|
||||
(getFileContent as jest.Mock).mockResolvedValue("Content of the active note");
|
||||
const { getFileName } = jest.requireMock("@/utils") as any;
|
||||
const { getFileName } = jest.requireMock("@/utils");
|
||||
getFileName.mockReturnValue("Active Note");
|
||||
|
||||
const result = await processPrompt(doc.content, selectedText, mockVault, mockActiveNote);
|
||||
|
|
@ -238,7 +238,7 @@ describe("processedPrompt()", () => {
|
|||
(getNotesFromTags as jest.Mock).mockReturnValue([mockNoteForTag]);
|
||||
|
||||
// Mock getFileName to return the basename
|
||||
const { getFileName } = jest.requireMock("@/utils") as any;
|
||||
const { getFileName } = jest.requireMock("@/utils");
|
||||
getFileName.mockReturnValue("Tagged Note");
|
||||
|
||||
// Mock getFileContent to return content for the note
|
||||
|
|
@ -270,7 +270,7 @@ describe("processedPrompt()", () => {
|
|||
(getNotesFromTags as jest.Mock).mockReturnValue([mockNoteForTag1, mockNoteForTag2]);
|
||||
|
||||
// Mock getFileName to return the basename
|
||||
const { getFileName } = jest.requireMock("@/utils") as any;
|
||||
const { getFileName } = jest.requireMock("@/utils");
|
||||
getFileName.mockImplementation((file: TFile) => file.basename);
|
||||
|
||||
// Mock getFileContent to return content for each note
|
||||
|
|
@ -324,7 +324,7 @@ describe("processedPrompt()", () => {
|
|||
|
||||
(extractTemplateNoteFiles as jest.Mock).mockReturnValue([mockNoteFile]);
|
||||
|
||||
const { getFileName } = jest.requireMock("@/utils") as any;
|
||||
const { getFileName } = jest.requireMock("@/utils");
|
||||
getFileName.mockReturnValue("Test Note");
|
||||
|
||||
(getFileContent as jest.Mock).mockResolvedValue("Test note content");
|
||||
|
|
@ -362,7 +362,7 @@ describe("processedPrompt()", () => {
|
|||
// Only Note1 should be extracted since it's wrapped in {[[]]}
|
||||
(extractTemplateNoteFiles as jest.Mock).mockReturnValue([mockNote1]);
|
||||
|
||||
const { getFileName } = jest.requireMock("@/utils") as any;
|
||||
const { getFileName } = jest.requireMock("@/utils");
|
||||
getFileName.mockImplementation((file: TFile) => file.basename);
|
||||
|
||||
(getFileContent as jest.Mock).mockImplementation((file: TFile) => {
|
||||
|
|
@ -459,7 +459,7 @@ describe("processedPrompt()", () => {
|
|||
const selectedText = "";
|
||||
|
||||
// Mock getFileName and getFileContent
|
||||
const { getFileName } = jest.requireMock("@/utils") as any;
|
||||
const { getFileName } = jest.requireMock("@/utils");
|
||||
getFileName.mockReturnValue("Active Note");
|
||||
|
||||
(getFileContent as jest.Mock).mockResolvedValue("Content of the active note");
|
||||
|
|
@ -511,7 +511,7 @@ describe("processedPrompt()", () => {
|
|||
|
||||
// Mock getFileContent for the active note when processed via {}
|
||||
(getFileContent as jest.Mock).mockResolvedValue("Content of the active note");
|
||||
const { getFileName } = jest.requireMock("@/utils") as any;
|
||||
const { getFileName } = jest.requireMock("@/utils");
|
||||
getFileName.mockReturnValue("Active Note");
|
||||
|
||||
const result = await processPrompt(doc.content, selectedText, mockVault, mockActiveNote);
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ export function AtMentionTypeahead({
|
|||
|
||||
return (
|
||||
<TypeaheadMenuPopover
|
||||
options={searchResults as any[]}
|
||||
options={searchResults}
|
||||
selectedIndex={selectedIndex}
|
||||
onSelect={handleSelect}
|
||||
onHighlight={handleHighlight}
|
||||
|
|
|
|||
|
|
@ -63,9 +63,7 @@ jest.mock("obsidian", () => {
|
|||
};
|
||||
});
|
||||
|
||||
const { __renderMarkdownMock: renderMarkdownMock } = jest.requireMock("obsidian") as {
|
||||
__renderMarkdownMock: jest.Mock;
|
||||
};
|
||||
const { __renderMarkdownMock: renderMarkdownMock } = jest.requireMock("obsidian");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Verifies that the HTML string passed to MarkdownRenderer.renderMarkdown
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export function useAtMentionSearch(
|
|||
key: `note-${file.basename}-${index}`,
|
||||
title: file.basename,
|
||||
subtitle: file.path,
|
||||
category: "notes" as AtMentionCategory,
|
||||
category: "notes",
|
||||
data: file,
|
||||
content: undefined,
|
||||
icon: React.createElement(FileText, { className: "tw-size-4" }),
|
||||
|
|
@ -64,7 +64,7 @@ export function useAtMentionSearch(
|
|||
key: `tool-${tool}`,
|
||||
title: tool,
|
||||
subtitle: getToolDescription(tool),
|
||||
category: "tools" as AtMentionCategory,
|
||||
category: "tools",
|
||||
data: tool,
|
||||
content: getToolDescription(tool),
|
||||
icon: React.createElement(Wrench, { className: "tw-size-4" }),
|
||||
|
|
@ -79,7 +79,7 @@ export function useAtMentionSearch(
|
|||
key: `folder-${folder.path}`,
|
||||
title: folder.name,
|
||||
subtitle: folder.path,
|
||||
category: "folders" as AtMentionCategory,
|
||||
category: "folders",
|
||||
data: folder,
|
||||
content: undefined,
|
||||
icon: React.createElement(Folder, { className: "tw-size-4" }),
|
||||
|
|
@ -97,7 +97,7 @@ export function useAtMentionSearch(
|
|||
key: `webtab-${tab.url || tab.title || index}-${index}`,
|
||||
title: tab.title || "Untitled",
|
||||
subtitle: isLoaded ? tab.url : "Tab not loaded",
|
||||
category: "webTabs" as AtMentionCategory,
|
||||
category: "webTabs",
|
||||
data: tab,
|
||||
content: undefined,
|
||||
disabled: !isLoaded,
|
||||
|
|
@ -129,7 +129,7 @@ export function useAtMentionSearch(
|
|||
key: "active-web-tab",
|
||||
title: "Active Web Tab",
|
||||
subtitle: undefined,
|
||||
category: "activeWebTab" as AtMentionCategory,
|
||||
category: "activeWebTab",
|
||||
data: activeWebTab,
|
||||
content: undefined,
|
||||
icon: React.createElement(Globe, { className: "tw-size-4" }),
|
||||
|
|
@ -142,7 +142,7 @@ export function useAtMentionSearch(
|
|||
key: `active-note-${currentActiveFile.path}`,
|
||||
title: "Active Note",
|
||||
subtitle: undefined,
|
||||
category: "activeNote" as AtMentionCategory,
|
||||
category: "activeNote",
|
||||
data: currentActiveFile,
|
||||
content: undefined,
|
||||
icon: React.createElement(FileClock, { className: "tw-size-4" }),
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ export function AtMentionCommandPlugin({
|
|||
<>
|
||||
{state.isOpen && (
|
||||
<TypeaheadMenuPortal
|
||||
options={displayOptions as any[]}
|
||||
options={displayOptions}
|
||||
selectedIndex={state.selectedIndex}
|
||||
onSelect={handleSelect}
|
||||
onHighlight={handleHighlight}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { App } from "obsidian";
|
|||
import { ConfirmModal } from "./ConfirmModal";
|
||||
|
||||
export class RebuildIndexConfirmModal extends ConfirmModal {
|
||||
constructor(app: App, onConfirm: () => void) {
|
||||
constructor(app: App, onConfirm: () => void | Promise<void>) {
|
||||
super(
|
||||
app,
|
||||
onConfirm,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { ConfirmModal } from "@/components/modals/ConfirmModal";
|
|||
import { App } from "obsidian";
|
||||
|
||||
export class ResetSettingsConfirmModal extends ConfirmModal {
|
||||
constructor(app: App, onConfirm: () => void) {
|
||||
constructor(app: App, onConfirm: () => void | Promise<void>) {
|
||||
super(
|
||||
app,
|
||||
onConfirm,
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ function AddProjectModalContent({
|
|||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
await onSave(saveData as ProjectConfig);
|
||||
await onSave(saveData);
|
||||
} catch (e) {
|
||||
new Notice(err2String(e));
|
||||
setTouched((prev) => ({
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@ export class QuickAskOverlay {
|
|||
|
||||
const getVisibleRect = (pos: number | null): AnchorRect | null => {
|
||||
if (typeof pos !== "number") return null;
|
||||
const coords = this.options.view.coordsAtPos(pos) as AnchorRect | null;
|
||||
const coords = this.options.view.coordsAtPos(pos);
|
||||
if (!coords) return null;
|
||||
return this.isAnchorRectVisible(coords, visibleRect) ? coords : null;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { ChevronDown, ChevronRight, GripVertical, MoreVertical } from "lucide-re
|
|||
export interface MobileCardDropdownAction<T = any> {
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
onClick: (item: T) => void;
|
||||
onClick: (item: T) => void | Promise<void>;
|
||||
variant?: "default" | "destructive";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const mockElectron = {
|
|||
|
||||
jest.mock("electron", () => mockElectron);
|
||||
|
||||
window.TextEncoder = TextEncoder as any;
|
||||
window.TextEncoder = TextEncoder;
|
||||
window.TextDecoder = TextDecoder as any;
|
||||
|
||||
// Now we can import our modules
|
||||
|
|
|
|||
|
|
@ -241,16 +241,20 @@ export function useChatFileDrop(props: UseChatFileDropProps): UseChatFileDropRet
|
|||
}
|
||||
};
|
||||
|
||||
const handleDropEvent = (e: DragEvent) => {
|
||||
void handleDrop(e);
|
||||
};
|
||||
|
||||
// Attach event listeners
|
||||
container.addEventListener("dragover", handleDragOver);
|
||||
container.addEventListener("dragleave", handleDragLeave);
|
||||
container.addEventListener("drop", handleDrop);
|
||||
container.addEventListener("drop", handleDropEvent);
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
container.removeEventListener("dragover", handleDragOver);
|
||||
container.removeEventListener("dragleave", handleDragLeave);
|
||||
container.removeEventListener("drop", handleDrop);
|
||||
container.removeEventListener("drop", handleDropEvent);
|
||||
};
|
||||
}, [app, contextNotes, selectedImages, onAddImage, setContextNotes, containerRef]);
|
||||
|
||||
|
|
|
|||
16
src/main.ts
16
src/main.ts
|
|
@ -103,13 +103,15 @@ export default class CopilotPlugin extends Plugin {
|
|||
|
||||
async onload(): Promise<void> {
|
||||
await this.loadSettings();
|
||||
this.settingsUnsubscriber = subscribeToSettingsChange(async (prev, next) => {
|
||||
if (next.enableEncryption) {
|
||||
await this.saveData(await encryptAllKeys(next));
|
||||
} else {
|
||||
await this.saveData(next);
|
||||
}
|
||||
registerCommands(this, prev, next);
|
||||
this.settingsUnsubscriber = subscribeToSettingsChange((prev, next) => {
|
||||
void (async () => {
|
||||
if (next.enableEncryption) {
|
||||
await this.saveData(await encryptAllKeys(next));
|
||||
} else {
|
||||
await this.saveData(next);
|
||||
}
|
||||
registerCommands(this, prev, next);
|
||||
})();
|
||||
});
|
||||
this.addSettingTab(new CopilotSettingTab(this.app, this));
|
||||
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ describe("MiyoServiceDiscovery", () => {
|
|||
host: "127.0.0.1",
|
||||
port: 8742,
|
||||
pid: 999,
|
||||
} as MiyoServiceConfig),
|
||||
}),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ describe("SearchCore retrieve", () => {
|
|||
});
|
||||
|
||||
it("should pass queries and salient terms as recall queries", async () => {
|
||||
const searchCore = new SearchCore(mockApp as any);
|
||||
const searchCore = new SearchCore(mockApp);
|
||||
|
||||
const retrieveResult = await searchCore.retrieve("#ProjectAlpha/Phase1 update");
|
||||
|
||||
|
|
@ -128,7 +128,7 @@ describe("SearchCore retrieve", () => {
|
|||
});
|
||||
|
||||
it("should bypass ceilings when returnAll is enabled", async () => {
|
||||
const searchCore = new SearchCore(mockApp as any);
|
||||
const searchCore = new SearchCore(mockApp);
|
||||
|
||||
buildFromCandidatesMock.mockResolvedValueOnce(5);
|
||||
batchCachedReadGrepMock.mockResolvedValueOnce(["note1.md", "note2.md"]);
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ describe("TieredLexicalRetriever", () => {
|
|||
if (path === "note1.md" || path === "note2.md") {
|
||||
const file = new (TFile as any)(path);
|
||||
Object.setPrototypeOf(file, (TFile as any).prototype);
|
||||
(file as any).stat = { mtime: 1000, ctime: 1000 };
|
||||
file.stat = { mtime: 1000, ctime: 1000 };
|
||||
return file;
|
||||
}
|
||||
return null;
|
||||
|
|
@ -209,7 +209,7 @@ describe("TieredLexicalRetriever", () => {
|
|||
if (path === "test.md") {
|
||||
const file = new (TFile as any)(path);
|
||||
Object.setPrototypeOf(file, (TFile as any).prototype);
|
||||
(file as any).stat = { mtime: 1000, ctime: 1000 };
|
||||
file.stat = { mtime: 1000, ctime: 1000 };
|
||||
return file;
|
||||
}
|
||||
return null;
|
||||
|
|
@ -260,7 +260,7 @@ describe("TieredLexicalRetriever", () => {
|
|||
if (path === "large.md" || path === "other.md") {
|
||||
const file = new (TFile as any)(path);
|
||||
Object.setPrototypeOf(file, (TFile as any).prototype);
|
||||
(file as any).stat = { mtime: 1000, ctime: 1000 };
|
||||
file.stat = { mtime: 1000, ctime: 1000 };
|
||||
return file;
|
||||
}
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ function tryExtractPluginApi(entry: unknown): WebViewerPluginApi | null {
|
|||
typeof maybe.openUrl === "function" || typeof maybe.handleOpenUrl === "function";
|
||||
|
||||
if (hasOpenCapability) {
|
||||
return maybe as WebViewerPluginApi;
|
||||
return maybe;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,10 +142,12 @@ export class WebSelectionTracker {
|
|||
private scheduleNext(): void {
|
||||
if (!this.isRunning) return;
|
||||
|
||||
this.timeoutId = window.setTimeout(async () => {
|
||||
await this.checkSelection();
|
||||
// Schedule next only after current check completes
|
||||
this.scheduleNext();
|
||||
this.timeoutId = window.setTimeout(() => {
|
||||
void (async () => {
|
||||
await this.checkSelection();
|
||||
// Schedule next only after current check completes
|
||||
this.scheduleNext();
|
||||
})();
|
||||
}, this.intervalMs);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ describe("sanitizeSettings - legacy Miyo settings cleanup", () => {
|
|||
miyoRemoteVaultPath: "\\\\Mac\\Home\\Downloads\\graham-essays-main",
|
||||
};
|
||||
|
||||
const sanitized = sanitizeSettings(legacySettings as any);
|
||||
const sanitized = sanitizeSettings(legacySettings);
|
||||
|
||||
expect(sanitized.enableMiyo).toBe(true);
|
||||
expect(sanitized.miyoServerUrl).toBe("http://127.0.0.1:8742");
|
||||
|
|
@ -209,7 +209,7 @@ describe("sanitizeSettings - legacy Miyo settings cleanup", () => {
|
|||
miyoRemoteVaultPath: "\\\\Mac\\Home\\Downloads\\graham-essays-main",
|
||||
};
|
||||
|
||||
const sanitized = sanitizeSettings(legacySettings as any);
|
||||
const sanitized = sanitizeSettings(legacySettings);
|
||||
const sanitizedRecord = sanitized as unknown as Record<string, unknown>;
|
||||
|
||||
expect(sanitized.userId).toBeTruthy();
|
||||
|
|
|
|||
|
|
@ -86,8 +86,8 @@ const SettingsMainV2: React.FC<SettingsMainV2Props> = ({ plugin }) => {
|
|||
const [resetKey, setResetKey] = React.useState(0);
|
||||
const { latestVersion, hasUpdate } = useLatestVersion(plugin.manifest.version);
|
||||
|
||||
const handleReset = async () => {
|
||||
const modal = new ResetSettingsConfirmModal(app, async () => {
|
||||
const handleReset = () => {
|
||||
const modal = new ResetSettingsConfirmModal(app, () => {
|
||||
resetSettings();
|
||||
// Increment the key to force re-render of all components
|
||||
setResetKey((prev) => prev + 1);
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export const obsidianDailyNoteTool = createLangChainTool({
|
|||
func: async (args) => {
|
||||
const { command, vault } = args;
|
||||
|
||||
const params = buildCliParams(args as Record<string, unknown>);
|
||||
const params = buildCliParams(args);
|
||||
const result = await runObsidianCliCommand({ command, vault, params });
|
||||
|
||||
if (!result.ok) throwCliFailure(result);
|
||||
|
|
@ -107,7 +107,7 @@ export const obsidianPropertiesTool = createLangChainTool({
|
|||
throw new Error("name is required for property:read");
|
||||
}
|
||||
|
||||
const params = buildCliParams(args as Record<string, unknown>);
|
||||
const params = buildCliParams(args);
|
||||
const result = await runObsidianCliCommand({ command, vault, params });
|
||||
|
||||
if (!result.ok) throwCliFailure(result);
|
||||
|
|
@ -158,7 +158,7 @@ export const obsidianTasksTool = createLangChainTool({
|
|||
schema: tasksSchema,
|
||||
func: async (args) => {
|
||||
const { command, vault } = args;
|
||||
const params = buildCliParams(args as Record<string, unknown>);
|
||||
const params = buildCliParams(args);
|
||||
const result = await runObsidianCliCommand({ command, vault, params });
|
||||
|
||||
if (!result.ok) throwCliFailure(result);
|
||||
|
|
@ -215,7 +215,7 @@ export const obsidianLinksTool = createLangChainTool({
|
|||
schema: linksSchema,
|
||||
func: async (args) => {
|
||||
const { command, vault } = args;
|
||||
const params = buildCliParams(args as Record<string, unknown>);
|
||||
const params = buildCliParams(args);
|
||||
const result = await runObsidianCliCommand({ command, vault, params });
|
||||
|
||||
if (!result.ok) throwCliFailure(result);
|
||||
|
|
@ -262,7 +262,7 @@ export const obsidianTemplatesTool = createLangChainTool({
|
|||
throw new Error("name is required for template:read");
|
||||
}
|
||||
|
||||
const params = buildCliParams(args as Record<string, unknown>);
|
||||
const params = buildCliParams(args);
|
||||
const result = await runObsidianCliCommand({ command, vault, params });
|
||||
|
||||
if (!result.ok) throwCliFailure(result);
|
||||
|
|
@ -343,7 +343,7 @@ export const obsidianBasesTool = createLangChainTool({
|
|||
throw new Error(`file or path is required for ${command}`);
|
||||
}
|
||||
|
||||
const params = buildCliParams(args as Record<string, unknown>);
|
||||
const params = buildCliParams(args);
|
||||
const result = await runObsidianCliCommand({ command, vault, params });
|
||||
|
||||
if (!result.ok) throwCliFailure(result);
|
||||
|
|
|
|||
Loading…
Reference in a new issue