Limit commands to applicable contexts

This commit is contained in:
vorotamoroz
2026-07-24 16:13:49 +00:00
parent 6afeb0b409
commit 0e5475b7e3
27 changed files with 727 additions and 75 deletions
+15 -6
View File
@@ -2,13 +2,14 @@ import type { LiveSyncCore } from "@/main";
import { LOG_LEVEL_NOTICE } from "octagonal-wheels/common/logger";
import { fireAndForget } from "octagonal-wheels/promises";
import { AbstractModule } from "@/modules/AbstractModule";
import { $msg } from "@/common/translation";
// Separated Module for basic menu commands, which are not related to obsidian specific features. It is expected to be used in other platforms with minimal changes.
// However, it is odd that it has here at all; it really ought to be in each respective feature. It will likely be moved eventually. Until now, addCommand pointed to Obsidian's version.
export class ModuleBasicMenu extends AbstractModule {
_everyOnloadStart(): Promise<boolean> {
this.addCommand({
id: "livesync-replicate",
name: "Replicate now",
name: $msg("Sync now"),
callback: async () => {
await this.services.replication.replicate();
},
@@ -56,14 +57,18 @@ export class ModuleBasicMenu extends AbstractModule {
this.addCommand({
id: "livesync-scan-files",
name: "Scan storage and database again",
callback: async () => {
await this.services.vault.scanVault(true);
checkCallback: (checking) => {
if (!this.settings.useAdvancedMode) return false;
if (!checking) {
fireAndForget(() => this.services.vault.scanVault(true));
}
return true;
},
});
this.addCommand({
id: "livesync-runbatch",
name: "Run pended batch processes",
name: $msg("Apply pending changes now"),
callback: async () => {
await this.services.fileProcessing.commitPendingFileEvents();
},
@@ -73,8 +78,12 @@ export class ModuleBasicMenu extends AbstractModule {
this.addCommand({
id: "livesync-abortsync",
name: "Abort synchronization immediately",
callback: () => {
this.core.replicator.terminateSync();
checkCallback: (checking) => {
if (!this.settings.useAdvancedMode) return false;
if (!checking) {
this.core.replicator.terminateSync();
}
return true;
},
});
return Promise.resolve(true);
@@ -0,0 +1,139 @@
import { describe, expect, it, vi } from "vitest";
import type { Command } from "@/deps";
import { ModuleBasicMenu } from "./ModuleBasicMenu";
type RegisteredCommand = Command & {
checkCallback?: (checking: boolean) => boolean | void;
};
function createFixture() {
const commands: RegisteredCommand[] = [];
const settings = {
liveSync: false,
useAdvancedMode: false,
enableDebugTools: false,
};
const services = {
API: {
addLog: vi.fn(),
addCommand: vi.fn((command: RegisteredCommand) => {
commands.push(command);
return command;
}),
registerWindow: vi.fn(),
addRibbonIcon: vi.fn(),
registerProtocolHandler: vi.fn(),
},
replication: {
replicate: vi.fn(async () => undefined),
},
vault: {
getActiveFilePath: vi.fn((): string | null => "note.md"),
scanVault: vi.fn(async () => undefined),
},
control: {
applySettings: vi.fn(async () => undefined),
},
setting: {
saveSettingData: vi.fn(async () => undefined),
},
appLifecycle: {
isSuspended: vi.fn(() => false),
setSuspended: vi.fn(),
},
fileProcessing: {
commitPendingFileEvents: vi.fn(async () => true),
},
UI: {
promptCopyToClipboard: vi.fn(async (_title: string, _value: string) => true),
},
path: {
path2id: vi.fn(async () => "f:note"),
},
};
const core = {
settings,
_services: services,
services,
localDatabase: {
getDBEntry: vi.fn(async () => false),
localDatabase: {
get: vi.fn(async () => ({
_id: "f:note",
_rev: "2-current",
_conflicts: [],
path: "note.md",
ctime: 100,
mtime: 200,
size: 12,
type: "plain",
children: ["h:private-chunk-id"],
eden: {},
})),
},
getDBEntryMeta: vi.fn(async () => ({
_id: "f:note",
_rev: "2-current",
_conflicts: [],
path: "note.md",
ctime: 100,
mtime: 200,
size: 12,
type: "plain",
datatype: "plain",
data: "",
children: ["h:private-chunk-id"],
eden: {},
})),
allDocsRaw: vi.fn(async () => ({
rows: [{ id: "h:private-chunk-id", key: "h:private-chunk-id", value: { rev: "1-chunk" } }],
})),
},
storageAccess: {
isExistsIncludeHidden: vi.fn(async () => true),
statHidden: vi.fn(async () => ({ ctime: 100, mtime: 200, size: 12, type: "file" })),
},
replicator: {
terminateSync: vi.fn(),
},
};
const module = new ModuleBasicMenu(core as never);
return {
commands,
core,
module,
services,
settings,
getCommand(id: string) {
const command = commands.find((candidate) => candidate.id === id);
expect(command, `command ${id}`).toBeDefined();
return command!;
},
};
}
describe("ModuleBasicMenu command palette", () => {
it("uses clear user-facing names without changing the established command IDs", async () => {
const fixture = createFixture();
await fixture.module._everyOnloadStart();
expect(fixture.getCommand("livesync-replicate").name).toBe("Sync now");
expect(fixture.getCommand("livesync-runbatch").name).toBe("Apply pending changes now");
});
it("keeps maintenance commands out of the normal palette", async () => {
const fixture = createFixture();
await fixture.module._everyOnloadStart();
expect(fixture.getCommand("livesync-scan-files").checkCallback?.(true)).toBe(false);
expect(fixture.getCommand("livesync-abortsync").checkCallback?.(true)).toBe(false);
fixture.settings.useAdvancedMode = true;
expect(fixture.getCommand("livesync-scan-files").checkCallback?.(true)).toBe(true);
expect(fixture.getCommand("livesync-abortsync").checkCallback?.(true)).toBe(true);
});
});
@@ -12,7 +12,7 @@ import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts
import type { PageFunctions } from "./SettingPane.ts";
import { visibleOnly } from "./SettingPane.ts";
import { request } from "@/deps.ts";
import { SetupManager, UserMode } from "@/modules/features/SetupManager.ts";
import { SetupManager } from "@/modules/features/SetupManager.ts";
import { LiveSyncError } from "@vrtmrz/livesync-commonlib/compat/common/LSError";
import {
createCoreSettingsAfterFullReset,
@@ -40,8 +40,7 @@ export function paneSetup(
.addButton((text) => {
text.setButtonText($msg("Rerun Wizard")).onClick(async () => {
const setupManager = this.core.getModule(SetupManager);
await setupManager.onOnboard(UserMode.ExistingUser);
// await this.plugin.moduleSetupObsidian.onBoardingWizard(true);
await setupManager.startOnBoarding();
});
});