mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-07-05 12:25:22 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2090d42631 | |||
| c9ff34842f |
@@ -0,0 +1,360 @@
|
||||
# Architectural Decision Record: Setting Definition Repository
|
||||
|
||||
## Status
|
||||
|
||||
Proposed
|
||||
|
||||
## Release
|
||||
|
||||
Not scheduled. Intended as a design direction before refactoring the settings dialogue.
|
||||
|
||||
## Context
|
||||
|
||||
The current settings dialogue is implemented around `ObsidianLiveSyncSettingTab`.
|
||||
It is effective and feature rich, but several responsibilities are combined in the
|
||||
same layer:
|
||||
|
||||
- editing-state buffering and dirty-state tracking,
|
||||
- local-only dialogue state such as `configPassphrase`, `preset`, `syncMode`, and
|
||||
`deviceAndVaultName`,
|
||||
- persistence through `SettingService`,
|
||||
- Obsidian `Setting` component rendering,
|
||||
- pane layout and visibility rules,
|
||||
- validation and value coercion,
|
||||
- setup wizard transitions,
|
||||
- rebuild/restart side effects,
|
||||
- remote diagnostics and maintenance actions.
|
||||
|
||||
Some setting metadata already exists in `configurationNames` and related setting
|
||||
constants. This is a useful seed, but it is not a complete source of truth. The
|
||||
metadata does not currently describe storage domain, value kind, validation,
|
||||
capability requirements, migration behaviour, cross-setting dependencies, or
|
||||
whether a value should be rendered by a generic control or a custom pane.
|
||||
|
||||
The settings dialogue also contains a mix of simple controls and workflow panels.
|
||||
Examples:
|
||||
|
||||
- Simple controls: `showStatusOnEditor`, `syncOnSave`, `customChunkSize`,
|
||||
`readChunksOnline`, `useTimeouts`.
|
||||
- Derived or dialogue-only values: `syncMode`, `preset`, `configPassphrase`,
|
||||
`deviceAndVaultName`.
|
||||
- Workflow panels: remote configuration management, E2EE setup, setup wizard,
|
||||
local/remote rebuild, maintenance commands, Customisation Sync dialogue open.
|
||||
|
||||
This matters primarily for maintainability and platform independence. A setting
|
||||
should have one shared definition of its domain meaning regardless of whether it
|
||||
is displayed in Obsidian, surfaced in a CLI, used by a WebApp, or documented.
|
||||
Tests benefit from this separation, but testability is a consequence rather than
|
||||
the main design goal.
|
||||
|
||||
Real Obsidian E2E remains the right signal for Obsidian shell behaviour such as
|
||||
opening the settings tab, rendering real Obsidian components, and verifying
|
||||
user-visible workflows. Harness-based tests can then focus on deterministic
|
||||
setting semantics that no longer depend on mounting the whole Obsidian setting
|
||||
tab.
|
||||
|
||||
## Decision
|
||||
|
||||
Introduce a platform-neutral Setting Definition Repository as the source of
|
||||
truth for setting metadata and setting semantics.
|
||||
|
||||
The repository should live in the shared domain layer, not under the Obsidian
|
||||
dialogue implementation and not under a generic model bucket. In the current
|
||||
layout this means `src/lib/src/common/settings`.
|
||||
|
||||
The repository should not be an Obsidian UI abstraction. It should describe what
|
||||
a setting is and how it behaves. Obsidian, CLI, WebApp, tests, and documentation
|
||||
can then consume the same definitions through their own renderers or adapters.
|
||||
The Obsidian settings tab should use an Obsidian renderer that maps repository
|
||||
definitions to native Obsidian `Setting` components for simple controls, while
|
||||
workflow panes remain custom.
|
||||
|
||||
The existing Obsidian setting dialogue should be migrated incrementally. Complex
|
||||
workflow panes should remain custom-rendered at first. Simple controls should
|
||||
move to repository-driven rendering first.
|
||||
|
||||
## Proposed Model
|
||||
|
||||
Each setting definition should describe a single setting key or a dialogue-only
|
||||
virtual key.
|
||||
|
||||
```ts
|
||||
type SettingStorageDomain = "persisted" | "local" | "derived" | "ephemeral";
|
||||
|
||||
type SettingValueKind = "boolean" | "text" | "password" | "number" | "select" | "textarea" | "string-list" | "custom";
|
||||
|
||||
type SettingCapability = "database-user" | "server-admin" | "filesystem" | "obsidian-shell" | "obsidian-plugin-host";
|
||||
|
||||
type SettingDefinition<TSettings, TKey extends keyof TSettings | string> = {
|
||||
key: TKey;
|
||||
storage: SettingStorageDomain;
|
||||
kind: SettingValueKind;
|
||||
defaultValue?: unknown;
|
||||
labelKey: string;
|
||||
descriptionKey?: string;
|
||||
label: string;
|
||||
description?: string;
|
||||
category: string;
|
||||
pane?: string;
|
||||
section?: string;
|
||||
level?: "ADVANCED" | "POWER_USER" | "EDGE_CASE";
|
||||
status?: "BETA" | "ALPHA" | "EXPERIMENTAL";
|
||||
obsolete?: boolean;
|
||||
internal?: boolean;
|
||||
placeholder?: string;
|
||||
options?: Record<string, string>;
|
||||
secret?: boolean;
|
||||
requiredCapabilities?: SettingCapability[];
|
||||
visible?: (context: SettingEvaluationContext<TSettings>) => boolean;
|
||||
enabled?: (context: SettingEvaluationContext<TSettings>) => boolean;
|
||||
validate?: (value: unknown, context: SettingEvaluationContext<TSettings>) => SettingValidationResult;
|
||||
coerce?: (value: unknown, context: SettingEvaluationContext<TSettings>) => unknown;
|
||||
affects?: SettingEffect[];
|
||||
commit?: SettingCommitPolicy<TKey>;
|
||||
render?: "auto" | "custom";
|
||||
};
|
||||
```
|
||||
|
||||
`SettingEvaluationContext` should carry the current editing settings, persisted
|
||||
settings, platform capabilities, and remote capability information if known. It
|
||||
should not carry an Obsidian `App`.
|
||||
|
||||
`labelKey` and `descriptionKey` should be i18n keys. During migration, the key
|
||||
may be the literal English text already used by `configurationNames` and
|
||||
`SettingInformation`. This matches the current i18n behaviour where an unknown
|
||||
key resolves to the key itself, avoids adding translation resources up front, and
|
||||
lets us later replace literal keys with stable resource keys without changing
|
||||
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. 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
|
||||
|
||||
Settings should be classified by where they live:
|
||||
|
||||
- `persisted`: normal `ObsidianLiveSyncSettings` values saved through
|
||||
`SettingService`.
|
||||
- `local`: values stored outside the main settings document, for example local
|
||||
storage or device/vault identity.
|
||||
- `derived`: values computed from persisted settings, for example `syncMode`.
|
||||
- `ephemeral`: dialogue-only inputs such as `preset`.
|
||||
|
||||
This makes current special cases explicit:
|
||||
|
||||
- `configPassphrase` is local-only.
|
||||
- `deviceAndVaultName` is local/service managed.
|
||||
- `syncMode` is derived from `liveSync` and `periodicReplication`.
|
||||
- `preset` is ephemeral and expands to several persisted settings.
|
||||
|
||||
## Rendering Strategy
|
||||
|
||||
The repository should support generic rendering, but it should not force every
|
||||
pane to become schema-driven immediately.
|
||||
|
||||
Use three levels:
|
||||
|
||||
1. **Auto-rendered controls**
|
||||
Simple `boolean`, `number`, `text`, `password`, `select`, and `textarea`
|
||||
settings. These can replace many `new Setting(...).autoWire...` calls.
|
||||
|
||||
2. **Repository-defined groups with custom sections**
|
||||
A pane can declare layout, headings, and order through the repository but keep
|
||||
a custom renderer for the section body.
|
||||
|
||||
3. **Fully custom workflow panes**
|
||||
Remote configuration management, E2EE setup, setup wizard, maintenance, and
|
||||
rebuild flows should remain custom until their side effects are separately
|
||||
modelled.
|
||||
|
||||
The Obsidian setting dialogue becomes a renderer of repository definitions plus a
|
||||
host for custom workflow panes.
|
||||
|
||||
## Side Effects
|
||||
|
||||
Setting changes should distinguish value persistence from effects.
|
||||
|
||||
Examples of effects:
|
||||
|
||||
- `requires-local-rebuild`
|
||||
- `requires-remote-rebuild`
|
||||
- `requires-restart`
|
||||
- `requires-apply-settings`
|
||||
- `suspends-sync`
|
||||
- `updates-unresolved-error-ui`
|
||||
- `changes-active-remote`
|
||||
- `expands-preset`
|
||||
|
||||
The current `isNeedRebuildLocal()` and `isNeedRebuildRemote()` methods should
|
||||
eventually be replaced by repository metadata. This would make rebuild prompts
|
||||
testable without rendering the full settings tab.
|
||||
|
||||
## Capability Requirements
|
||||
|
||||
Some settings and actions require capabilities that not all users or platforms
|
||||
have.
|
||||
|
||||
Examples:
|
||||
|
||||
- CouchDB server diagnostics and automatic CouchDB repair require server-admin
|
||||
capability.
|
||||
- Normal CouchDB sync requires only database-user capability.
|
||||
- Hidden File Sync and Customisation Sync require filesystem capability.
|
||||
- Obsidian plug-in reload requires obsidian-plugin-host capability.
|
||||
- Opening settings panes and workspace views requires obsidian-shell capability.
|
||||
|
||||
Capability metadata should be used for:
|
||||
|
||||
- warning text in Obsidian settings,
|
||||
- disabling unsupported actions,
|
||||
- CLI/WebApp help output,
|
||||
- Harness tests for visibility and enabled-state rules.
|
||||
|
||||
The repository should not introduce a generic cross-platform `PluginManager`
|
||||
concept. Obsidian plug-in host behaviour should remain an Obsidian-specific
|
||||
adapter or custom workflow.
|
||||
|
||||
## Current Assumptions to Preserve
|
||||
|
||||
- Settings can be edited in a buffer before being saved.
|
||||
- Some values save immediately unless `holdValue` is set.
|
||||
- Some values require explicit Apply buttons.
|
||||
- Visibility and enabled-state often depend on other editing values.
|
||||
- Some settings are hidden in setup wizard mode.
|
||||
- Advanced, power-user, and edge-case levels remain supported.
|
||||
- The dialogue can be reloaded while preserving dirty local edits.
|
||||
- Existing `SettingService` remains responsible for encryption, persistence,
|
||||
migration, and applying settings.
|
||||
- Existing complex setup and remote configuration workflows remain custom.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
### Phase 1: Repository Skeleton
|
||||
|
||||
- Create a repository module in the shared setting domain
|
||||
(`src/lib/src/common/settings`), not under the Obsidian dialogue folder.
|
||||
- Move existing `configurationNames` metadata into repository definitions without
|
||||
changing runtime behaviour.
|
||||
- Add storage domain, kind, i18n keys, internal marker, pane, section, level, and
|
||||
secret metadata for a small subset of settings.
|
||||
- Keep `getConfig()`, `getConfName()`, and existing callers working through a
|
||||
compatibility facade.
|
||||
|
||||
### Phase 2: Evaluation API
|
||||
|
||||
- Add pure functions:
|
||||
- `getSettingDefinition(key)`
|
||||
- `listSettingDefinitions(filter)`
|
||||
- `evaluateSetting(definition, context)`
|
||||
- `validateSettingValue(key, value, context)`
|
||||
- `getSettingEffects(changedKeys, context)`
|
||||
- Add unit tests for derived values, visibility, enabled-state, validation, and
|
||||
rebuild/restart effects.
|
||||
|
||||
### Phase 3: Obsidian Renderer for Simple Controls
|
||||
|
||||
- Add a small renderer that maps repository definitions to Obsidian `Setting`
|
||||
controls.
|
||||
- Migrate one low-risk pane first, likely Appearance/Logging or Advanced memory
|
||||
cache settings.
|
||||
- Keep custom panes untouched.
|
||||
- Keep `LiveSyncSetting` as a compatibility wrapper during migration.
|
||||
|
||||
### Phase 4: Derived and Local Values
|
||||
|
||||
- Model `syncMode`, `preset`, `configPassphrase`, and `deviceAndVaultName`
|
||||
explicitly.
|
||||
- Replace ad hoc save paths in `ObsidianLiveSyncSettingTab` with storage-domain
|
||||
handlers.
|
||||
- Keep user-visible behaviour unchanged.
|
||||
|
||||
### Phase 5: Effects and Capability Warnings
|
||||
|
||||
- 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
|
||||
administrator-only actions.
|
||||
|
||||
### Phase 6: Documentation and Non-Obsidian Consumers
|
||||
|
||||
- Treat documentation as an authored source, not as an output that must be fully
|
||||
generated from code.
|
||||
- Optionally combine repository metadata with a documentation source such as YAML
|
||||
to generate or lint `docs/settings.md`.
|
||||
- Use the repository to verify that documented settings exist, that defaults and
|
||||
storage domains are consistent, and that internal settings are intentionally
|
||||
omitted or documented as internal.
|
||||
- Expose repository metadata to CLI/WebApp where useful.
|
||||
- Let Harness tests assert the same repository semantics used by Obsidian.
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
Use Harness or unit tests for:
|
||||
|
||||
- default value coverage,
|
||||
- type/kind consistency,
|
||||
- every persisted setting has a definition or is explicitly internal,
|
||||
- visibility and enabled-state predicates,
|
||||
- derived values such as `syncMode`,
|
||||
- preset expansion,
|
||||
- rebuild/restart effect calculation,
|
||||
- capability warnings.
|
||||
|
||||
Use real Obsidian E2E for:
|
||||
|
||||
- opening the actual setting tab,
|
||||
- rendering Obsidian `Setting` components,
|
||||
- setup wizard flow,
|
||||
- remote configuration workflow,
|
||||
- actual restart prompts,
|
||||
- workflows that depend on Obsidian settings shell behaviour.
|
||||
|
||||
## Consequences
|
||||
|
||||
Positive:
|
||||
|
||||
- Setting semantics are maintained in one platform-neutral place.
|
||||
- Setting semantics become testable without mounting Obsidian UI.
|
||||
- Documentation, CLI, WebApp, and Obsidian can share setting metadata where it is
|
||||
useful.
|
||||
- Capability-sensitive settings become explicit.
|
||||
- Future settings are less likely to be implemented in only one surface.
|
||||
- The Obsidian settings dialogue can be refactored incrementally.
|
||||
|
||||
Negative:
|
||||
|
||||
- There will be a temporary compatibility layer between old setting constants and
|
||||
the repository.
|
||||
- Some panes will remain custom, so the repository will not remove all UI code.
|
||||
- Definition metadata can become stale if not enforced by tests.
|
||||
- Over-generalising workflow panes would make the repository harder to maintain.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Do not replace `SettingService` persistence in the first phase.
|
||||
- Do not make Obsidian plug-in host operations cross-platform.
|
||||
- Do not convert all setting panes to schema-driven UI at once.
|
||||
- Do not require real Obsidian E2E for every setting definition.
|
||||
- Do not remove custom renderers for remote setup, E2EE setup, or maintenance
|
||||
workflows.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- What should the exact `CapabilityProvider` interface look like for static
|
||||
platform capabilities and runtime-probed remote capabilities? This should be
|
||||
decided while implementing the Obsidian renderer so the interface follows a
|
||||
real consumer instead of an abstract capability model.
|
||||
+1
-1
Submodule src/lib updated: 5c1033485e...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 {
|
||||
|
||||
Reference in New Issue
Block a user