Adapt settings pages to Obsidian's declarative API

This commit is contained in:
vorotamoroz
2026-08-24 12:17:19 +00:00
parent d8f7762d01
commit 47759f6205
20 changed files with 1327 additions and 185 deletions
@@ -0,0 +1,191 @@
import { $msg } from "@/common/translation";
import {
LEVEL_ADVANCED,
LEVEL_EDGE_CASE,
LEVEL_POWER_USER,
type ConfigLevel,
} from "@vrtmrz/livesync-commonlib/compat/common/types";
import type { SettingDefinitionGroup } from "obsidian";
import { createAdvancedSettingSpecGroups, type AdvancedSettingSpecContext } from "./AdvancedSettingSpecs.ts";
import { toObsidianSettingDefinition, type PersistedSettingKey, type SettingSpec } from "./SettingSpec.ts";
import { getConfig } from "./settingConstants.ts";
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
import type { PageFunctions } from "./SettingPane.ts";
import { paneAdvanced } from "./PaneAdvanced.ts";
import { paneChangeLog } from "./PaneChangeLog.ts";
import { paneCustomisationSync } from "./PaneCustomisationSync.ts";
import { paneGeneral } from "./PaneGeneral.ts";
import { paneHatch } from "./PaneHatch.ts";
import { paneMaintenance } from "./PaneMaintenance.ts";
import { panePatches } from "./PanePatches.ts";
import { panePowerUsers } from "./PanePowerUsers.ts";
import { paneRemoteConfig } from "./PaneRemoteConfig.ts";
import { paneSelector } from "./PaneSelector.ts";
import { paneSetup } from "./PaneSetup.ts";
import { paneSyncSettings } from "./PaneSyncSettings.ts";
/** The existing pane renderer used by the imperative settings tab and custom pages. */
export type SettingsPaneRenderer = (
this: ObsidianLiveSyncSettingTab,
paneEl: HTMLElement,
functions: PageFunctions
) => void;
export type SettingsPageContent = "native" | "custom";
/** One established settings page, shared by the imperative and declarative renderers. */
export type SettingsPageEntry = {
id: string;
name: () => string;
icon: string;
order: number;
level?: ConfigLevel;
content: SettingsPageContent;
legacy: SettingsPaneRenderer;
};
/**
* Build the explicit page list in the same order as the existing settings tab.
*
* Names remain functions so a catalogue refresh observes the current language,
* while constructing the catalogue itself performs no rendering or persistence.
*/
export function createSettingsPageCatalogue(): SettingsPageEntry[] {
return [
{
id: "change-log",
name: () => $msg("obsidianLiveSyncSettingTab.panelChangeLog"),
icon: "💬",
order: 100,
level: undefined,
content: "custom",
legacy: paneChangeLog,
},
{
id: "setup",
name: () => $msg("obsidianLiveSyncSettingTab.panelSetup"),
icon: "🧙‍♂️",
order: 110,
level: undefined,
content: "custom",
legacy: paneSetup,
},
{
id: "general",
name: () => $msg("obsidianLiveSyncSettingTab.panelGeneralSettings"),
icon: "⚙️",
order: 20,
level: undefined,
content: "custom",
legacy: paneGeneral,
},
{
id: "remote-configuration",
name: () => $msg("obsidianLiveSyncSettingTab.panelRemoteConfiguration"),
icon: "🛰️",
order: 0,
level: undefined,
content: "custom",
legacy: paneRemoteConfig,
},
{
id: "synchronisation",
name: () => $msg("obsidianLiveSyncSettingTab.titleSyncSettings"),
icon: "🔄",
order: 30,
level: undefined,
content: "custom",
legacy: paneSyncSettings,
},
{
id: "selector",
name: () => "Selector",
icon: "🚦",
order: 33,
level: LEVEL_ADVANCED,
content: "custom",
legacy: paneSelector,
},
{
id: "customisation-sync",
name: () => "Customisation sync",
icon: "🔌",
order: 60,
level: LEVEL_ADVANCED,
content: "custom",
legacy: paneCustomisationSync,
},
{
id: "hatch",
name: () => "Hatch",
icon: "🧰",
order: 50,
level: undefined,
content: "custom",
legacy: paneHatch,
},
{
id: "advanced",
name: () => "Advanced",
icon: "🔧",
order: 46,
level: LEVEL_ADVANCED,
content: "native",
legacy: paneAdvanced,
},
{
id: "power-users",
name: () => "Power users",
icon: "💪",
order: 47,
level: LEVEL_POWER_USER,
content: "custom",
legacy: panePowerUsers,
},
{
id: "patches",
name: () => "Patches",
icon: "🩹",
order: 51,
level: LEVEL_EDGE_CASE,
content: "custom",
legacy: panePatches,
},
{
id: "maintenance",
name: () => "Maintenance",
icon: "🎛️",
order: 70,
level: undefined,
content: "custom",
legacy: paneMaintenance,
},
];
}
const numberRangeMessage = ({ min, max }: { min?: number; max?: number }): string =>
$msg("liveSyncSetting.valueShouldBeInRange", {
min: min === undefined ? "~" : `${min}`,
max: max === undefined ? "~" : `${max}`,
});
function toAdvancedSettingDefinition(spec: SettingSpec): ReturnType<typeof toObsidianSettingDefinition> {
const metadata = getConfig(spec.key);
if (!metadata) {
throw new Error(`Missing translated setting metadata for ${spec.key}`);
}
return toObsidianSettingDefinition(spec, metadata, {
valueShouldBeInRange: numberRangeMessage,
});
}
/** Convert the existing Advanced specifications to native Obsidian groups. */
export function createAdvancedSettingDefinitionGroups(
context: AdvancedSettingSpecContext
): SettingDefinitionGroup<PersistedSettingKey>[] {
return createAdvancedSettingSpecGroups(context).map((group) => ({
type: "group",
heading: group.heading,
items: group.items.map(toAdvancedSettingDefinition),
}));
}