Add Obsidian setting definition renderer

This commit is contained in:
vorotamoroz
2026-07-03 10:33:32 +00:00
parent c9ff34842f
commit 2090d42631
15 changed files with 243 additions and 142 deletions
@@ -110,6 +110,7 @@ type SettingDefinition<TSettings, TKey extends keyof TSettings | string> = {
validate?: (value: unknown, context: SettingEvaluationContext<TSettings>) => SettingValidationResult;
coerce?: (value: unknown, context: SettingEvaluationContext<TSettings>) => unknown;
affects?: SettingEffect[];
commit?: SettingCommitPolicy<TKey>;
render?: "auto" | "custom";
};
```
@@ -127,8 +128,15 @@ consumers. `label` and `description` remain compatibility aliases while existing
code still expects resolved strings.
`internal` should mark settings that are not currently editable from the UI.
Obsolete settings are internal by default. Persisted settings without UI metadata
are also internal until explicitly classified otherwise.
Obsolete settings are internal by default. Missing UI metadata alone should not
make a setting internal; `kind`, `render`, and explicit `internal` metadata
should decide whether the automatic renderer can safely handle it.
`commit` should describe when a value is persisted, not how a button is rendered.
Immediate settings can save on change. Explicit settings are held in the editing
buffer until an apply action commits the configured group. This keeps apply
buttons out of the repository while still making grouped save behaviour
testable.
## Storage Domains
@@ -273,6 +281,9 @@ adapter or custom workflow.
- Replace `isNeedRebuildLocal()` and `isNeedRebuildRemote()` with
repository-driven effect calculation.
- Model explicit commit groups for settings that must be applied together, for
example configuration encryption passphrase settings, setting sync file, and
database suffix changes.
- Add capability metadata for CouchDB diagnostics, repair, Hidden File Sync, and
Obsidian-only plug-in operations.
- Use this to improve warnings for database-scoped CouchDB users and
+1 -1
Submodule src/lib updated: 72a2f684d1...c7c5fe21be
@@ -8,7 +8,13 @@ import {
type ValueComponent,
} from "@/deps.ts";
import { unique } from "octagonal-wheels/collection";
import { LEVEL_ADVANCED, LEVEL_POWER_USER, statusDisplay, type ConfigurationItem } from "@lib/common/types.ts";
import {
LEVEL_ADVANCED,
LEVEL_POWER_USER,
statusDisplay,
type ConfigurationItem,
type SettingDefinition,
} from "@lib/common/types.ts";
import { type ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
import {
type AllSettingItemKey,
@@ -18,9 +24,21 @@ import {
type AllNumericItemKey,
type AllBooleanItemKey,
} from "./settingConstants.ts";
import { $msg } from "@lib/common/i18n.ts";
import { $msg, $t } from "@lib/common/i18n.ts";
import { wrapMemo, type AutoWireOption, type OnUpdateResult } from "./SettingPane.ts";
function configurationFromDefinition(definition: SettingDefinition<AllSettingItemKey>): ConfigurationItem {
return {
name: $t(definition.labelKey),
desc: definition.descriptionKey ? $t(definition.descriptionKey) : undefined,
placeHolder: definition.placeholder,
status: definition.status,
obsolete: definition.obsolete,
level: definition.level,
isHidden: definition.secret,
};
}
export class LiveSyncSetting extends Setting {
autoWiredComponent?: TextComponent | ToggleComponent | DropdownComponent | ButtonComponent | TextAreaComponent;
applyButtonComponent?: ButtonComponent;
@@ -56,7 +74,7 @@ export class LiveSyncSetting extends Setting {
return this;
}
autoWireSetting(key: AllSettingItemKey, opt?: AutoWireOption) {
const conf = getConfig(key);
const conf = opt?.settingDefinition ? configurationFromDefinition(opt.settingDefinition) : getConfig(key);
if (!conf) {
// throw new Error($msg("liveSyncSetting.errorNoSuchSettingItem", { key }));
return;
@@ -0,0 +1,76 @@
import {
getExplicitSettingCommitGroup,
getSettingDefinition,
type AllBooleanItemKey,
type AllNumericItemKey,
type AllSettingItemKey,
type AllStringItemKey,
} from "./settingConstants.ts";
import type { LiveSyncSetting } from "./LiveSyncSetting.ts";
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
import type { AutoWireOption } from "./SettingPane.ts";
export type ObsidianSettingRenderOption<TOption extends string = string> = AutoWireOption & {
options?: Record<TOption, string>;
clampMin?: number;
clampMax?: number;
acceptZero?: boolean;
renderInternal?: boolean;
};
export function addObsidianApplyButton(setting: LiveSyncSetting, group: string, text?: string): LiveSyncSetting {
const commitGroup = getExplicitSettingCommitGroup(group);
if (!commitGroup) {
throw new Error(`No explicit setting commit group found for '${group}'`);
}
return setting.addApplyButton(commitGroup.applyKeys, text);
}
export function renderObsidianApplyButton(containerEl: HTMLElement, group: string, text?: string): LiveSyncSetting {
return addObsidianApplyButton(new Setting(containerEl), group, text);
}
export function renderObsidianSetting<TOption extends string = string>(
containerEl: HTMLElement,
key: AllSettingItemKey,
opt: ObsidianSettingRenderOption<TOption> = {}
): LiveSyncSetting {
const definition = getSettingDefinition(key);
if (!definition) {
throw new Error(`No setting definition found for '${key}'`);
}
if (definition.internal && !opt.renderInternal) {
throw new Error(`Setting '${key}' is internal and cannot be rendered automatically`);
}
if (definition.render === "custom" || definition.kind === "custom") {
throw new Error(`Setting '${key}' requires a custom renderer`);
}
const isExplicitCommit = definition.commit?.mode === "explicit";
const holdValue = opt.holdValue ?? isExplicitCommit;
const setting = new Setting(containerEl);
const wireOption = {
...opt,
holdValue,
settingDefinition: definition,
};
if (opt.options) {
return setting.autoWireDropDown(key as AllStringItemKey, { ...wireOption, options: opt.options });
}
switch (definition.kind) {
case "boolean":
return setting.autoWireToggle(key as AllBooleanItemKey, wireOption);
case "number":
return setting.autoWireNumeric(key as AllNumericItemKey, wireOption);
case "password":
return setting.autoWireText(key as AllStringItemKey, { ...wireOption, isPassword: true });
case "textarea":
return setting.autoWireTextArea(key as AllStringItemKey, wireOption);
case "select":
throw new Error(`Setting '${key}' requires select options`);
case "text":
return setting.autoWireText(key as AllStringItemKey, wireOption);
}
}
@@ -1,41 +1,37 @@
import { ChunkAlgorithmNames } from "@lib/common/types.ts";
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
import { renderObsidianSetting } from "./ObsidianSettingRenderer.ts";
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
import type { PageFunctions } from "./SettingPane.ts";
export function paneAdvanced(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel }: PageFunctions): void {
void addPanel(paneEl, "Memory cache").then((paneEl) => {
new Setting(paneEl).autoWireNumeric("hashCacheMaxCount", { clampMin: 10 });
// new Setting(paneEl).autoWireNumeric("hashCacheMaxAmount", { clampMin: 1 });
renderObsidianSetting(paneEl, "hashCacheMaxCount", { clampMin: 10 });
// renderObsidianSetting(paneEl, "hashCacheMaxAmount", { clampMin: 1 });
});
void addPanel(paneEl, "Local Database Tweak").then((paneEl) => {
paneEl.addClass("wizardHidden");
const items = ChunkAlgorithmNames;
new Setting(paneEl).autoWireDropDown("chunkSplitterVersion", {
renderObsidianSetting(paneEl, "chunkSplitterVersion", {
options: items,
});
new Setting(paneEl).autoWireNumeric("customChunkSize", { clampMin: 0, acceptZero: true });
renderObsidianSetting(paneEl, "customChunkSize", { clampMin: 0, acceptZero: true });
});
void addPanel(paneEl, "Transfer Tweak").then((paneEl) => {
new Setting(paneEl)
.setClass("wizardHidden")
.autoWireToggle("readChunksOnline", { onUpdate: this.onlyOnCouchDB });
new Setting(paneEl)
.setClass("wizardHidden")
.autoWireToggle("useOnlyLocalChunk", { onUpdate: this.onlyOnCouchDB });
renderObsidianSetting(paneEl, "readChunksOnline", { onUpdate: this.onlyOnCouchDB }).setClass("wizardHidden");
renderObsidianSetting(paneEl, "useOnlyLocalChunk", { onUpdate: this.onlyOnCouchDB }).setClass("wizardHidden");
new Setting(paneEl).setClass("wizardHidden").autoWireNumeric("concurrencyOfReadChunksOnline", {
renderObsidianSetting(paneEl, "concurrencyOfReadChunksOnline", {
clampMin: 10,
onUpdate: this.onlyOnCouchDB,
});
}).setClass("wizardHidden");
new Setting(paneEl).setClass("wizardHidden").autoWireNumeric("minimumIntervalOfReadChunksOnline", {
renderObsidianSetting(paneEl, "minimumIntervalOfReadChunksOnline", {
clampMin: 10,
onUpdate: this.onlyOnCouchDB,
});
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("autoAcceptCompatibleTweak");
}).setClass("wizardHidden");
renderObsidianSetting(paneEl, "autoAcceptCompatibleTweak").setClass("wizardHidden");
// new Setting(paneEl)
// .setClass("wizardHidden")
// .autoWireToggle("sendChunksBulk", { onUpdate: onlyOnCouchDB })
@@ -1,6 +1,7 @@
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
import { EVENT_REQUEST_OPEN_PLUGIN_SYNC_DIALOG, eventHub } from "@/common/events.ts";
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
import { renderObsidianSetting } from "./ObsidianSettingRenderer.ts";
import type { PageFunctions } from "./SettingPane.ts";
import { enableOnly, visibleOnly } from "./SettingPane.ts";
export function paneCustomisationSync(
@@ -35,27 +36,27 @@ export function paneCustomisationSync(
visibleOnly(() => this.isConfiguredAs("usePluginSync", true))
);
new Setting(paneEl).autoWireText("deviceAndVaultName", {
renderObsidianSetting(paneEl, "deviceAndVaultName", {
placeHolder: "desktop",
onUpdate: enableOnlyOnPluginSyncIsNotEnabled,
});
new Setting(paneEl).autoWireToggle("usePluginSyncV2");
renderObsidianSetting(paneEl, "usePluginSyncV2");
new Setting(paneEl).autoWireToggle("usePluginSync", {
renderObsidianSetting(paneEl, "usePluginSync", {
onUpdate: enableOnly(() => !this.isConfiguredAs("deviceAndVaultName", "")),
});
new Setting(paneEl).autoWireToggle("autoSweepPlugins", {
renderObsidianSetting(paneEl, "autoSweepPlugins", {
onUpdate: visibleOnlyOnPluginSyncEnabled,
});
new Setting(paneEl).autoWireToggle("autoSweepPluginsPeriodic", {
renderObsidianSetting(paneEl, "autoSweepPluginsPeriodic", {
onUpdate: visibleOnly(
() => this.isConfiguredAs("usePluginSync", true) && this.isConfiguredAs("autoSweepPlugins", true)
),
});
new Setting(paneEl).autoWireToggle("notifyPluginOrSettingUpdated", {
renderObsidianSetting(paneEl, "notifyPluginOrSettingUpdated", {
onUpdate: visibleOnlyOnPluginSyncEnabled,
});
@@ -1,6 +1,7 @@
import { $msg, $t } from "@lib/common/i18n.ts";
import { SUPPORTED_I18N_LANGS, type I18N_LANGS } from "@lib/common/rosetta.ts";
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
import { renderObsidianSetting } from "./ObsidianSettingRenderer.ts";
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
import type { PageFunctions } from "./SettingPane.ts";
import { visibleOnly } from "./SettingPane.ts";
@@ -16,20 +17,20 @@ export function paneGeneral(
// ["", $msg("obsidianLiveSyncSettingTab.defaultLanguage")],
...SUPPORTED_I18N_LANGS.map((e) => [e, $t(`lang-${e}`)]),
]) as Record<I18N_LANGS, string>;
new Setting(paneEl).autoWireDropDown("displayLanguage", {
renderObsidianSetting(paneEl, "displayLanguage", {
options: languages,
});
this.addOnSaved("displayLanguage", () => this.display());
new Setting(paneEl).autoWireToggle("showStatusOnEditor");
renderObsidianSetting(paneEl, "showStatusOnEditor");
this.addOnSaved("showStatusOnEditor", () => {
eventHub.emitEvent(EVENT_ON_UNRESOLVED_ERROR);
});
new Setting(paneEl).autoWireToggle("showOnlyIconsOnEditor", {
renderObsidianSetting(paneEl, "showOnlyIconsOnEditor", {
onUpdate: visibleOnly(() => this.isConfiguredAs("showStatusOnEditor", true)),
});
new Setting(paneEl).autoWireToggle("showStatusOnStatusbar");
new Setting(paneEl).autoWireToggle("hideFileWarningNotice");
new Setting(paneEl).autoWireDropDown("networkWarningStyle", {
renderObsidianSetting(paneEl, "showStatusOnStatusbar");
renderObsidianSetting(paneEl, "hideFileWarningNotice");
renderObsidianSetting(paneEl, "networkWarningStyle", {
options: {
[NetworkWarningStyles.BANNER]: "Show full banner",
[NetworkWarningStyles.ICON]: "Show icon only",
@@ -43,9 +44,9 @@ export function paneGeneral(
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleLogging")).then((paneEl) => {
paneEl.addClass("wizardHidden");
new Setting(paneEl).autoWireToggle("lessInformationInLog");
renderObsidianSetting(paneEl, "lessInformationInLog");
new Setting(paneEl).autoWireToggle("showVerboseLog", {
renderObsidianSetting(paneEl, "showVerboseLog", {
onUpdate: visibleOnly(() => this.isConfiguredAs("lessInformationInLog", false)),
});
});
@@ -25,6 +25,7 @@ import { ICHeader, ICXHeader, PSCHeader } from "@/common/types.ts";
import { HiddenFileSync } from "@/features/HiddenFileSync/CmdHiddenFileSync.ts";
import { EVENT_REQUEST_SHOW_HISTORY } from "@/common/obsidianEvents.ts";
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
import { renderObsidianSetting } from "./ObsidianSettingRenderer.ts";
import type { PageFunctions } from "./SettingPane.ts";
import { isNotFoundError } from "@lib/common/utils.doc.ts";
export function paneHatch(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel }: PageFunctions): void {
@@ -87,14 +88,14 @@ export function paneHatch(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement,
eventHub.emitEvent(EVENT_REQUEST_CHECK_REMOTE_SIZE);
})
);
new Setting(paneEl).autoWireToggle("writeLogToTheFile");
renderObsidianSetting(paneEl, "writeLogToTheFile");
});
void addPanel(paneEl, "Scram Switches").then((paneEl) => {
new Setting(paneEl).autoWireToggle("suspendFileWatching");
renderObsidianSetting(paneEl, "suspendFileWatching");
this.addOnSaved("suspendFileWatching", () => this.services.appLifecycle.askRestart());
new Setting(paneEl).autoWireToggle("suspendParseReplicationResult");
renderObsidianSetting(paneEl, "suspendParseReplicationResult");
this.addOnSaved("suspendParseReplicationResult", () => this.services.appLifecycle.askRestart());
});
@@ -7,6 +7,7 @@ import {
} from "@lib/common/types.ts";
import { Logger } from "@lib/common/logger.ts";
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
import { addObsidianApplyButton, renderObsidianApplyButton, renderObsidianSetting } from "./ObsidianSettingRenderer.ts";
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
import type { PageFunctions } from "./SettingPane.ts";
import { visibleOnly } from "./SettingPane.ts";
@@ -16,17 +17,17 @@ import { migrateDatabases } from "./settingUtils.ts";
export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel }: PageFunctions): void {
void addPanel(paneEl, "Compatibility (Metadata)").then((paneEl) => {
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("deleteMetadataOfDeletedFiles");
renderObsidianSetting(paneEl, "deleteMetadataOfDeletedFiles").setClass("wizardHidden");
new Setting(paneEl).setClass("wizardHidden").autoWireNumeric("automaticallyDeleteMetadataOfDeletedFiles", {
renderObsidianSetting(paneEl, "automaticallyDeleteMetadataOfDeletedFiles", {
onUpdate: visibleOnly(() => this.isConfiguredAs("deleteMetadataOfDeletedFiles", true)),
});
}).setClass("wizardHidden");
});
void addPanel(paneEl, "Compatibility (Conflict Behaviour)").then((paneEl) => {
paneEl.addClass("wizardHidden");
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("disableMarkdownAutoMerge");
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("writeDocumentsIfConflicted");
renderObsidianSetting(paneEl, "disableMarkdownAutoMerge").setClass("wizardHidden");
renderObsidianSetting(paneEl, "writeDocumentsIfConflicted").setClass("wizardHidden");
});
void addPanel(paneEl, "Compatibility (Database structure)").then((paneEl) => {
@@ -113,18 +114,18 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
});
}
}
new Setting(paneEl).autoWireToggle("handleFilenameCaseSensitive", { holdValue: true }).setClass("wizardHidden");
renderObsidianSetting(paneEl, "handleFilenameCaseSensitive", { holdValue: true }).setClass("wizardHidden");
});
void addPanel(paneEl, "Compatibility (Internal API Usage)").then((paneEl) => {
new Setting(paneEl).autoWireToggle("watchInternalFileChanges", { invert: true });
renderObsidianSetting(paneEl, "watchInternalFileChanges", { invert: true });
});
void addPanel(paneEl, "Compatibility (Remote Database)").then((paneEl) => {
new Setting(paneEl).autoWireDropDown("E2EEAlgorithm", {
renderObsidianSetting(paneEl, "E2EEAlgorithm", {
options: E2EEAlgorithmNames,
});
});
new Setting(paneEl).autoWireToggle("useDynamicIterationCount", {
renderObsidianSetting(paneEl, "useDynamicIterationCount", {
holdValue: true,
onUpdate: visibleOnly(
() =>
@@ -134,16 +135,15 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
});
void addPanel(paneEl, "Edge case addressing (Database)").then((paneEl) => {
new Setting(paneEl)
.autoWireText("additionalSuffixOfDatabaseName", { holdValue: true })
.addApplyButton(["additionalSuffixOfDatabaseName"]);
renderObsidianSetting(paneEl, "additionalSuffixOfDatabaseName");
renderObsidianApplyButton(paneEl, "database-suffix");
this.addOnSaved("additionalSuffixOfDatabaseName", async (key) => {
Logger("Suffix has been changed. Reopening database...", LOG_LEVEL_NOTICE);
await this.services.databaseEvents.initialiseDatabase();
});
new Setting(paneEl).autoWireDropDown("hashAlg", {
renderObsidianSetting(paneEl, "hashAlg", {
options: {
"": "Old Algorithm",
xxhash32: "xxhash32 (Fast but less collision resistance)",
@@ -157,15 +157,15 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
});
});
void addPanel(paneEl, "Edge case addressing (Behaviour)").then((paneEl) => {
new Setting(paneEl).autoWireToggle("doNotSuspendOnFetching");
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("doNotDeleteFolder");
new Setting(paneEl).autoWireToggle("processSizeMismatchedFiles");
renderObsidianSetting(paneEl, "doNotSuspendOnFetching");
renderObsidianSetting(paneEl, "doNotDeleteFolder").setClass("wizardHidden");
renderObsidianSetting(paneEl, "processSizeMismatchedFiles");
});
void addPanel(paneEl, "Edge case addressing (Processing)").then((paneEl) => {
new Setting(paneEl).autoWireToggle("disableWorkerForGeneratingChunks");
renderObsidianSetting(paneEl, "disableWorkerForGeneratingChunks");
new Setting(paneEl).autoWireToggle("processSmallFilesInUIThread", {
renderObsidianSetting(paneEl, "processSmallFilesInUIThread", {
onUpdate: visibleOnly(() => this.isConfiguredAs("disableWorkerForGeneratingChunks", false)),
});
});
@@ -173,11 +173,11 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
// new Setting(paneEl).autoWireToggle("useRequestAPI");
// });
void addPanel(paneEl, "Compatibility (Trouble addressed)").then((paneEl) => {
new Setting(paneEl).autoWireToggle("disableCheckingConfigMismatch");
renderObsidianSetting(paneEl, "disableCheckingConfigMismatch");
});
void addPanel(paneEl, "Remediation").then((paneEl) => {
let dateEl: HTMLSpanElement;
new Setting(paneEl)
const remediationSetting = new Setting(paneEl)
.addText((text) => {
const updateDateText = () => {
if (this.editingSettings.maxMTimeForReflectEvents == 0) {
@@ -212,8 +212,8 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
updateDateText();
return text;
})
.setAuto("maxMTimeForReflectEvents")
.addApplyButton(["maxMTimeForReflectEvents"]);
.setAuto("maxMTimeForReflectEvents");
addObsidianApplyButton(remediationSetting, "remediation-reflect-events");
this.addOnSaved("maxMTimeForReflectEvents", async (key) => {
const buttons = ["Restart Now", "Later"] as const;
@@ -240,6 +240,6 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
// .setClass("wizardHidden");
// new Setting(paneEl).autoWireNumeric("maxAgeInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden");
new Setting(paneEl).autoWireToggle("enableCompression").setClass("wizardHidden");
renderObsidianSetting(paneEl, "enableCompression").setClass("wizardHidden");
});
}
@@ -1,5 +1,5 @@
import { type ConfigPassphraseStore } from "@lib/common/types.ts";
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
import { renderObsidianApplyButton, renderObsidianSetting } from "./ObsidianSettingRenderer.ts";
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
import type { PageFunctions } from "./SettingPane.ts";
@@ -21,14 +21,14 @@ export function panePowerUsers(
this.onlyOnCouchDB
).addClass("wizardHidden");
new Setting(paneEl)
.setClass("wizardHidden")
.autoWireNumeric("batch_size", { clampMin: 2, onUpdate: this.onlyOnCouchDB });
new Setting(paneEl).setClass("wizardHidden").autoWireNumeric("batches_limit", {
renderObsidianSetting(paneEl, "batch_size", { clampMin: 2, onUpdate: this.onlyOnCouchDB }).setClass(
"wizardHidden"
);
renderObsidianSetting(paneEl, "batches_limit", {
clampMin: 2,
onUpdate: this.onlyOnCouchDB,
});
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("useTimeouts", { onUpdate: this.onlyOnCouchDB });
}).setClass("wizardHidden");
renderObsidianSetting(paneEl, "useTimeouts", { onUpdate: this.onlyOnCouchDB }).setClass("wizardHidden");
});
void addPanel(paneEl, "Configuration Encryption").then((paneEl) => {
const passphrase_options: Record<ConfigPassphraseStore, string> = {
@@ -37,23 +37,18 @@ export function panePowerUsers(
ASK_AT_LAUNCH: "Ask an passphrase at every launch",
};
new Setting(paneEl)
.setName("Encrypting sensitive configuration items")
.autoWireDropDown("configPassphraseStore", {
options: passphrase_options,
holdValue: true,
})
.setClass("wizardHidden");
renderObsidianSetting(paneEl, "configPassphraseStore", {
options: passphrase_options,
}).setClass("wizardHidden");
new Setting(paneEl)
.autoWireText("configPassphrase", { isPassword: true, holdValue: true })
renderObsidianSetting(paneEl, "configPassphrase")
.setClass("wizardHidden")
.addOnUpdate(() => ({
disabled: !this.isConfiguredAs("configPassphraseStore", "LOCALSTORAGE"),
}));
new Setting(paneEl).addApplyButton(["configPassphrase", "configPassphraseStore"]).setClass("wizardHidden");
renderObsidianApplyButton(paneEl, "configuration-encryption").setClass("wizardHidden");
});
void addPanel(paneEl, "Developer").then((paneEl) => {
new Setting(paneEl).autoWireToggle("enableDebugTools").setClass("wizardHidden");
renderObsidianSetting(paneEl, "enableDebugTools").setClass("wizardHidden");
});
}
@@ -10,6 +10,7 @@ import {
import { Menu, type ButtonComponent } from "@/deps.ts";
import { $msg } from "@lib/common/i18n.ts";
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
import { renderObsidianSetting } from "./ObsidianSettingRenderer.ts";
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
import type { PageFunctions } from "./SettingPane.ts";
// import { visibleOnly } from "./SettingPane.ts";
@@ -674,7 +675,7 @@ export function paneRemoteConfig(
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleNotification"), () => {}).then((paneEl) => {
paneEl.addClass("wizardHidden");
new Setting(paneEl).autoWireNumeric("notifyThresholdOfRemoteStorageSize", {}).setClass("wizardHidden");
renderObsidianSetting(paneEl, "notifyThresholdOfRemoteStorageSize", {}).setClass("wizardHidden");
});
// new Setting(paneEl).setClass("wizardOnly").addButton((button) =>
@@ -4,6 +4,7 @@ import MultipleRegExpControl from "./MultipleRegExpControl.svelte";
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
import { mount } from "svelte";
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
import { renderObsidianSetting } from "./ObsidianSettingRenderer.ts";
import type { PageFunctions } from "./SettingPane.ts";
import { visibleOnly } from "./SettingPane.ts";
export function paneSelector(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel }: PageFunctions): void {
@@ -46,12 +47,12 @@ export function paneSelector(this: ObsidianLiveSyncSettingTab, paneEl: HTMLEleme
},
},
});
new Setting(paneEl).setClass("wizardHidden").autoWireNumeric("syncMaxSizeInMB", { clampMin: 0 });
renderObsidianSetting(paneEl, "syncMaxSizeInMB", { clampMin: 0 }).setClass("wizardHidden");
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("useIgnoreFiles");
new Setting(paneEl).setClass("wizardHidden").autoWireTextArea("ignoreFiles", {
renderObsidianSetting(paneEl, "useIgnoreFiles").setClass("wizardHidden");
renderObsidianSetting(paneEl, "ignoreFiles", {
onUpdate: visibleOnly(() => this.isConfiguredAs("useIgnoreFiles", true)),
});
}).setClass("wizardHidden");
});
void addPanel(paneEl, "Hidden Files", undefined, undefined, LEVEL_ADVANCED).then((paneEl) => {
const targetPatternSetting = new Setting(paneEl)
@@ -15,6 +15,7 @@ import { DEFAULT_SETTINGS } from "@lib/common/types.ts";
import { request } from "@/deps.ts";
import { SetupManager, UserMode } from "@/modules/features/SetupManager.ts";
import { LiveSyncError } from "@lib/common/LSError.ts";
import { renderObsidianSetting } from "./ObsidianSettingRenderer.ts";
export function paneSetup(
this: ObsidianLiveSyncSettingTab,
paneEl: HTMLElement,
@@ -107,10 +108,9 @@ export function paneSetup(
});
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleExtraFeatures")).then((paneEl) => {
new Setting(paneEl).autoWireToggle("useAdvancedMode");
new Setting(paneEl).autoWireToggle("usePowerUserMode");
new Setting(paneEl).autoWireToggle("useEdgeCaseMode");
renderObsidianSetting(paneEl, "useAdvancedMode");
renderObsidianSetting(paneEl, "usePowerUserMode");
renderObsidianSetting(paneEl, "useEdgeCaseMode");
this.addOnSaved("useAdvancedMode", () => this.display());
this.addOnSaved("usePowerUserMode", () => this.display());
@@ -2,6 +2,7 @@ import { type ObsidianLiveSyncSettings, LOG_LEVEL_NOTICE, REMOTE_COUCHDB, LEVEL_
import { Logger } from "@lib/common/logger.ts";
import { $msg } from "@lib/common/i18n.ts";
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
import { renderObsidianApplyButton, renderObsidianSetting } from "./ObsidianSettingRenderer.ts";
import { EVENT_REQUEST_COPY_SETUP_URI, eventHub } from "@/common/events.ts";
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
import type { PageFunctions } from "./SettingPane.ts";
@@ -31,18 +32,16 @@ export function paneSyncSettings(
DISABLE: $msg("obsidianLiveSyncSettingTab.optionDisableAllAutomatic"),
};
new Setting(paneEl)
.autoWireDropDown("preset", {
options: options,
holdValue: true,
})
.addButton((button) => {
button.setButtonText($msg("obsidianLiveSyncSettingTab.btnApply"));
button.onClick(async () => {
// await this.saveSettings(["preset"]);
await this.saveAllDirtySettings();
});
renderObsidianSetting(paneEl, "preset", {
options: options,
holdValue: true,
}).addButton((button) => {
button.setButtonText($msg("obsidianLiveSyncSettingTab.btnApply"));
button.onClick(async () => {
// await this.saveSettings(["preset"]);
await this.saveAllDirtySettings();
});
});
this.addOnSaved("preset", async (currentPreset) => {
if (currentPreset == "") {
@@ -136,7 +135,7 @@ export function paneSyncSettings(
const onlyOnNonLiveSync = visibleOnly(() => !this.isConfiguredAs("syncMode", "LIVESYNC"));
const onlyOnPeriodic = visibleOnly(() => this.isConfiguredAs("syncMode", "PERIODIC"));
const optionsSyncMode =
const optionsSyncMode: Record<string, string> =
this.editingSettings.remoteType == REMOTE_COUCHDB
? {
ONEVENTS: $msg("obsidianLiveSyncSettingTab.optionOnEvents"),
@@ -148,12 +147,9 @@ export function paneSyncSettings(
PERIODIC: $msg("obsidianLiveSyncSettingTab.optionPeriodicAndEvents"),
};
new Setting(paneEl)
.autoWireDropDown("syncMode", {
//@ts-ignore
options: optionsSyncMode,
})
.setClass("wizardHidden");
renderObsidianSetting(paneEl, "syncMode", {
options: optionsSyncMode,
}).setClass("wizardHidden");
this.addOnSaved("syncMode", async (value) => {
this.editingSettings.liveSync = false;
this.editingSettings.periodicReplication = false;
@@ -167,32 +163,28 @@ export function paneSyncSettings(
await this.services.control.applySettings();
});
new Setting(paneEl)
.autoWireNumeric("periodicReplicationInterval", {
clampMax: 5000,
onUpdate: onlyOnPeriodic,
})
.setClass("wizardHidden");
renderObsidianSetting(paneEl, "periodicReplicationInterval", {
clampMax: 5000,
onUpdate: onlyOnPeriodic,
}).setClass("wizardHidden");
new Setting(paneEl).autoWireNumeric("syncMinimumInterval", {
renderObsidianSetting(paneEl, "syncMinimumInterval", {
onUpdate: onlyOnNonLiveSync,
});
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("syncOnSave", { onUpdate: onlyOnNonLiveSync });
new Setting(paneEl)
.setClass("wizardHidden")
.autoWireToggle("syncOnEditorSave", { onUpdate: onlyOnNonLiveSync });
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("syncOnFileOpen", { onUpdate: onlyOnNonLiveSync });
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("syncOnStart", { onUpdate: onlyOnNonLiveSync });
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("syncAfterMerge", { onUpdate: onlyOnNonLiveSync });
renderObsidianSetting(paneEl, "syncOnSave", { onUpdate: onlyOnNonLiveSync }).setClass("wizardHidden");
renderObsidianSetting(paneEl, "syncOnEditorSave", { onUpdate: onlyOnNonLiveSync }).setClass("wizardHidden");
renderObsidianSetting(paneEl, "syncOnFileOpen", { onUpdate: onlyOnNonLiveSync }).setClass("wizardHidden");
renderObsidianSetting(paneEl, "syncOnStart", { onUpdate: onlyOnNonLiveSync }).setClass("wizardHidden");
renderObsidianSetting(paneEl, "syncAfterMerge", { onUpdate: onlyOnNonLiveSync }).setClass("wizardHidden");
// Desktop app only, and only for the sync modes that keep a background replication channel
// (LiveSync and Periodic). Ignored on mobile, where suspending preserves battery. The
// visibility predicate mirrors the runtime guard in ModuleObsidianEvents.
if (!this.services.API.isMobile()) {
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("keepReplicationActiveInBackground", {
renderObsidianSetting(paneEl, "keepReplicationActiveInBackground", {
onUpdate: visibleOnly(
() => this.isConfiguredAs("syncMode", "LIVESYNC") || this.isConfiguredAs("syncMode", "PERIODIC")
),
});
}).setClass("wizardHidden");
}
});
@@ -203,15 +195,15 @@ export function paneSyncSettings(
visibleOnly(() => !this.isConfiguredAs("syncMode", "LIVESYNC"))
).then((paneEl) => {
paneEl.addClass("wizardHidden");
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("batchSave");
new Setting(paneEl).setClass("wizardHidden").autoWireNumeric("batchSaveMinimumDelay", {
renderObsidianSetting(paneEl, "batchSave").setClass("wizardHidden");
renderObsidianSetting(paneEl, "batchSaveMinimumDelay", {
acceptZero: true,
onUpdate: visibleOnly(() => this.isConfiguredAs("batchSave", true)),
});
new Setting(paneEl).setClass("wizardHidden").autoWireNumeric("batchSaveMaximumDelay", {
}).setClass("wizardHidden");
renderObsidianSetting(paneEl, "batchSaveMaximumDelay", {
acceptZero: true,
onUpdate: visibleOnly(() => this.isConfiguredAs("batchSave", true)),
});
}).setClass("wizardHidden");
});
void addPanel(
@@ -222,9 +214,9 @@ export function paneSyncSettings(
LEVEL_ADVANCED
).then((paneEl) => {
paneEl.addClass("wizardHidden");
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("trashInsteadDelete");
renderObsidianSetting(paneEl, "trashInsteadDelete").setClass("wizardHidden");
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("doNotDeleteFolder");
renderObsidianSetting(paneEl, "doNotDeleteFolder").setClass("wizardHidden");
});
void addPanel(
paneEl,
@@ -235,11 +227,11 @@ export function paneSyncSettings(
).then((paneEl) => {
paneEl.addClass("wizardHidden");
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("resolveConflictsByNewerFile");
renderObsidianSetting(paneEl, "resolveConflictsByNewerFile").setClass("wizardHidden");
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("checkConflictOnlyOnOpen");
renderObsidianSetting(paneEl, "checkConflictOnlyOnOpen").setClass("wizardHidden");
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("showMergeDialogOnlyOnActive");
renderObsidianSetting(paneEl, "showMergeDialogOnlyOnActive").setClass("wizardHidden");
});
void addPanel(
@@ -250,11 +242,12 @@ export function paneSyncSettings(
LEVEL_ADVANCED
).then((paneEl) => {
paneEl.addClass("wizardHidden");
new Setting(paneEl).autoWireText("settingSyncFile", { holdValue: true }).addApplyButton(["settingSyncFile"]);
renderObsidianSetting(paneEl, "settingSyncFile");
renderObsidianApplyButton(paneEl, "setting-sync-file");
new Setting(paneEl).autoWireToggle("writeCredentialsForSettingSync");
renderObsidianSetting(paneEl, "writeCredentialsForSettingSync");
new Setting(paneEl).autoWireToggle("notifyAllSettingSyncFile");
renderObsidianSetting(paneEl, "notifyAllSettingSyncFile");
});
void addPanel(
@@ -313,14 +306,14 @@ export function paneSyncSettings(
});
}
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("suppressNotifyHiddenFilesChange", {});
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("syncInternalFilesBeforeReplication", {
renderObsidianSetting(paneEl, "suppressNotifyHiddenFilesChange", {}).setClass("wizardHidden");
renderObsidianSetting(paneEl, "syncInternalFilesBeforeReplication", {
onUpdate: visibleOnly(() => this.isConfiguredAs("watchInternalFileChanges", true)),
});
}).setClass("wizardHidden");
new Setting(paneEl).setClass("wizardHidden").autoWireNumeric("syncInternalFilesInterval", {
renderObsidianSetting(paneEl, "syncInternalFilesInterval", {
clampMin: 10,
acceptZero: true,
});
}).setClass("wizardHidden");
});
}
@@ -1,5 +1,11 @@
import { $msg } from "@lib/common/i18n";
import { LEVEL_ADVANCED, LEVEL_EDGE_CASE, LEVEL_POWER_USER, type ConfigLevel } from "@lib/common/types";
import {
LEVEL_ADVANCED,
LEVEL_EDGE_CASE,
LEVEL_POWER_USER,
type ConfigLevel,
type SettingDefinition,
} from "@lib/common/types";
import type { AllSettingItemKey, AllSettings } from "./settingConstants";
export const combineOnUpdate = (func1: OnUpdateFunc, func2: OnUpdateFunc): OnUpdateFunc => {
@@ -77,6 +83,7 @@ export type AutoWireOption = {
invert?: boolean;
onUpdate?: OnUpdateFunc;
obsolete?: boolean;
settingDefinition?: SettingDefinition<AllSettingItemKey>;
};
export function findAttrFromParent(el: HTMLElement, attr: string): string {