mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-02-15 16:50:38 +00:00
Refined:
- Setting dialogue very slightly refined. - The hodgepodge inside the `Hatch` pane has been sorted into more explicit categorised panes. - Applying the settings will now be more informative. New features: - Word-segmented chunk building on users language. Fixed: - Sending chunks on `Send chunk in bulk` are now buffered to avoid the out-of-memory error. - `Send chunk in bulk` is back to default disabled. - Merging conflicts of JSON files are now works fine even if it contains `null`. Development: - Implemented the logic for automatically generating the stub of document for the setting dialogue.
This commit is contained in:
@@ -134,7 +134,13 @@ export function generatePatchObj(from: Record<string | number | symbol, any>, to
|
||||
//if type is not match, replace completely.
|
||||
ret[key] = { [MARK_SWAPPED]: value };
|
||||
} else {
|
||||
if (typeof (v) == "object" && typeof (value) == "object" && !Array.isArray(v) && !Array.isArray(value)) {
|
||||
if (v === null && value === null) {
|
||||
// NO OP.
|
||||
} else if (v === null && value !== null) {
|
||||
ret[key] = { [MARK_SWAPPED]: value };
|
||||
} else if (v !== null && value === null) {
|
||||
ret[key] = { [MARK_SWAPPED]: value };
|
||||
} else if (typeof (v) == "object" && typeof (value) == "object" && !Array.isArray(v) && !Array.isArray(value)) {
|
||||
const wk = generatePatchObj(v, value);
|
||||
if (Object.keys(wk).length > 0) ret[key] = wk;
|
||||
} else if (typeof (v) == "object" && typeof (value) == "object" && Array.isArray(v) && Array.isArray(value)) {
|
||||
@@ -169,6 +175,10 @@ export function applyPatch(from: Record<string | number | symbol, any>, patch: R
|
||||
delete ret[key];
|
||||
continue;
|
||||
}
|
||||
if (value === null) {
|
||||
ret[key] = null;
|
||||
continue;
|
||||
}
|
||||
if (typeof (value) == "object") {
|
||||
if (MARK_SWAPPED in value) {
|
||||
ret[key] = value[MARK_SWAPPED];
|
||||
@@ -251,6 +261,7 @@ export function mergeObject(
|
||||
|
||||
export function flattenObject(obj: Record<string | number | symbol, any>, path: string[] = []): [string, any][] {
|
||||
if (typeof (obj) != "object") return [[path.join("."), obj]];
|
||||
if (obj === null) return [[path.join("."), null]];
|
||||
if (Array.isArray(obj)) return [[path.join("."), JSON.stringify(obj)]];
|
||||
const e = Object.entries(obj);
|
||||
const ret = []
|
||||
@@ -489,6 +500,35 @@ export function useMemo<T>({ key, forceUpdate, validator }: MemoOption, updateFu
|
||||
return value;
|
||||
}
|
||||
|
||||
// const _static = new Map<string, any>();
|
||||
const _staticObj = new Map<string, {
|
||||
value: any
|
||||
}>();
|
||||
|
||||
export function useStatic<T>(key: string): { value: (T | undefined) };
|
||||
export function useStatic<T>(key: string, initial: T): { value: T };
|
||||
export function useStatic<T>(key: string, initial?: T) {
|
||||
// if (!_static.has(key) && initial) {
|
||||
// _static.set(key, initial);
|
||||
// }
|
||||
const obj = _staticObj.get(key);
|
||||
if (obj !== undefined) {
|
||||
return obj;
|
||||
} else {
|
||||
// let buf = initial;
|
||||
const obj = {
|
||||
_buf: initial,
|
||||
get value() {
|
||||
return this._buf as T;
|
||||
},
|
||||
set value(value: T) {
|
||||
this._buf = value
|
||||
}
|
||||
}
|
||||
_staticObj.set(key, obj);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
export function disposeMemo(key: string) {
|
||||
_cached.delete(key);
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ export class LogAddOn extends LiveSyncCommands {
|
||||
this.statusBarLabels = statusBarLabels;
|
||||
|
||||
const applyToDisplay = throttle((label: typeof statusBarLabels.value) => {
|
||||
const v = label;
|
||||
// const v = label;
|
||||
this.applyStatusBarText();
|
||||
|
||||
}, 20);
|
||||
@@ -161,10 +161,10 @@ export class LogAddOn extends LiveSyncCommands {
|
||||
|
||||
}
|
||||
onload(): void | Promise<void> {
|
||||
eventHub.on(EVENT_FILE_RENAMED, (evt: CustomEvent<{ oldPath: string, newPath: string }>) => {
|
||||
eventHub.onEvent(EVENT_FILE_RENAMED, (evt: CustomEvent<{ oldPath: string, newPath: string }>) => {
|
||||
this.setFileStatus();
|
||||
});
|
||||
eventHub.on(EVENT_LEAF_ACTIVE_CHANGED, () => this.onActiveLeafChange());
|
||||
eventHub.onEvent(EVENT_LEAF_ACTIVE_CHANGED, () => this.onActiveLeafChange());
|
||||
const w = document.querySelectorAll(`.livesync-status`);
|
||||
w.forEach(e => e.remove());
|
||||
|
||||
@@ -175,7 +175,7 @@ export class LogAddOn extends LiveSyncCommands {
|
||||
this.messageArea = this.statusDiv.createDiv({ cls: "livesync-status-messagearea" });
|
||||
this.logMessage = this.statusDiv.createDiv({ cls: "livesync-status-logmessage" });
|
||||
this.logHistory = this.statusDiv.createDiv({ cls: "livesync-status-loghistory" });
|
||||
eventHub.on(EVENT_LAYOUT_READY, () => this.adjustStatusDivPosition());
|
||||
eventHub.onEvent(EVENT_LAYOUT_READY, () => this.adjustStatusDivPosition());
|
||||
if (this.settings.showStatusOnStatusbar) {
|
||||
this.statusBar = this.plugin.addStatusBarItem();
|
||||
this.statusBar.addClass("syncstatusbar");
|
||||
|
||||
2
src/lib
2
src/lib
Submodule src/lib updated: 633af447d2...3108e3e3db
61
src/main.ts
61
src/main.ts
@@ -46,7 +46,7 @@ import { LogAddOn } from "./features/CmdStatusInsideEditor.ts";
|
||||
import { eventHub } from "./lib/src/hub/hub.ts";
|
||||
import { EVENT_FILE_RENAMED, EVENT_LAYOUT_READY, EVENT_LEAF_ACTIVE_CHANGED, EVENT_PLUGIN_LOADED, EVENT_PLUGIN_UNLOADED, EVENT_SETTING_SAVED } from "./common/events.ts";
|
||||
import { Semaphore } from "octagonal-wheels/concurrency/semaphore";
|
||||
|
||||
import { yieldMicrotask } from "octagonal-wheels/promises";
|
||||
|
||||
setNoticeClass(Notice);
|
||||
|
||||
@@ -354,10 +354,10 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
return new PouchDB(name, optionPass);
|
||||
}
|
||||
beforeOnUnload(db: LiveSyncLocalDB): void {
|
||||
this.kvDB.close();
|
||||
if (this.kvDB) this.kvDB.close();
|
||||
}
|
||||
onClose(db: LiveSyncLocalDB): void {
|
||||
this.kvDB.close();
|
||||
if (this.kvDB) this.kvDB.close();
|
||||
}
|
||||
getNewReplicator(settingOverride: Partial<ObsidianLiveSyncSettings> = {}): LiveSyncAbstractReplicator {
|
||||
const settings = { ...this.settings, ...settingOverride };
|
||||
@@ -367,8 +367,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
return new LiveSyncCouchDBReplicator(this);
|
||||
}
|
||||
async onInitializeDatabase(db: LiveSyncLocalDB): Promise<void> {
|
||||
await delay(10);
|
||||
this.kvDB = await OpenKeyValueDatabase(db.dbname + "-livesync-kv");
|
||||
// this.trench = new Trench(this.simpleStore);
|
||||
this.replicator = this.getNewReplicator();
|
||||
}
|
||||
async onResetDatabase(db: LiveSyncLocalDB): Promise<void> {
|
||||
@@ -376,8 +376,9 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
this.kvDB.del(kvDBKey);
|
||||
// localStorage.removeItem(lsKey);
|
||||
await this.kvDB.destroy();
|
||||
await yieldMicrotask();
|
||||
this.kvDB = await OpenKeyValueDatabase(db.dbname + "-livesync-kv");
|
||||
// this.trench = new Trench(this.simpleStore);
|
||||
await yieldMicrotask();
|
||||
this.replicator = this.getNewReplicator()
|
||||
}
|
||||
getReplicator() {
|
||||
@@ -710,7 +711,7 @@ ___However, to enable either of these changes, both remote and local databases n
|
||||
|
||||
}
|
||||
async onLayoutReady() {
|
||||
eventHub.emit(EVENT_LAYOUT_READY);
|
||||
eventHub.emitEvent(EVENT_LAYOUT_READY);
|
||||
this.registerFileWatchEvents();
|
||||
if (!this.localDatabase.isReady) {
|
||||
Logger(`Something went wrong! The local database is not ready`, LOG_LEVEL_NOTICE);
|
||||
@@ -1160,34 +1161,34 @@ Note: We can always able to read V1 format. It will be progressively converted.
|
||||
}
|
||||
|
||||
wireUpEvents() {
|
||||
eventHub.on(EVENT_SETTING_SAVED, (evt: CustomEvent<ObsidianLiveSyncSettings>) => {
|
||||
eventHub.onEvent(EVENT_SETTING_SAVED, (evt: CustomEvent<ObsidianLiveSyncSettings>) => {
|
||||
const settings = evt.detail;
|
||||
this.localDatabase.settings = settings;
|
||||
setLang(settings.displayLanguage);
|
||||
this.settingTab.requestReload();
|
||||
this.ignoreFiles = settings.ignoreFiles.split(",").map(e => e.trim());
|
||||
});
|
||||
eventHub.on(EVENT_SETTING_SAVED, (evt: CustomEvent<ObsidianLiveSyncSettings>) => {
|
||||
eventHub.onEvent(EVENT_SETTING_SAVED, (evt: CustomEvent<ObsidianLiveSyncSettings>) => {
|
||||
const settings = evt.detail;
|
||||
if (settings.settingSyncFile != "") {
|
||||
fireAndForget(() => this.saveSettingToMarkdown(settings.settingSyncFile));
|
||||
}
|
||||
})
|
||||
eventHub.on(EVENT_SETTING_SAVED, (evt: CustomEvent<ObsidianLiveSyncSettings>) => {
|
||||
eventHub.onEvent(EVENT_SETTING_SAVED, (evt: CustomEvent<ObsidianLiveSyncSettings>) => {
|
||||
fireAndForget(() => this.realizeSettingSyncMode());
|
||||
})
|
||||
}
|
||||
connectObsidianEvents() {
|
||||
// this.registerEvent(this.app.workspace.on("editor-change", ));
|
||||
this.registerEvent(this.app.vault.on("rename", (file, oldPath) => {
|
||||
eventHub.emit(EVENT_FILE_RENAMED, { newPath: file.path, old: oldPath });
|
||||
eventHub.emitEvent(EVENT_FILE_RENAMED, { newPath: file.path, old: oldPath });
|
||||
}));
|
||||
this.registerEvent(this.app.workspace.on("active-leaf-change", () => eventHub.emit(EVENT_LEAF_ACTIVE_CHANGED)));
|
||||
this.registerEvent(this.app.workspace.on("active-leaf-change", () => eventHub.emitEvent(EVENT_LEAF_ACTIVE_CHANGED)));
|
||||
}
|
||||
async onload() {
|
||||
this.wireUpEvents();
|
||||
this.connectObsidianEvents();
|
||||
eventHub.emit(EVENT_PLUGIN_LOADED, this);
|
||||
eventHub.emitEvent(EVENT_PLUGIN_LOADED, this);
|
||||
logStore.pipeTo(new QueueProcessor(logs => logs.forEach(e => this.addLog(e.message, e.level, e.key)), { suspended: false, batchSize: 20, concurrentLimit: 1, delay: 0 })).startPipeline();
|
||||
Logger("loading plugin");
|
||||
__onMissingTranslation(() => { });
|
||||
@@ -1214,6 +1215,36 @@ Note: We can always able to read V1 format. It will be progressively converted.
|
||||
}
|
||||
});
|
||||
})
|
||||
type STUB = {
|
||||
toc: Set<string>,
|
||||
stub: { [key: string]: { [key: string]: Map<string, Record<string, string>> } }
|
||||
};
|
||||
eventHub.onEvent("document-stub-created", async (e: CustomEvent<STUB>) => {
|
||||
const stub = e.detail.stub;
|
||||
const toc = e.detail.toc;
|
||||
|
||||
const stubDocX =
|
||||
Object.entries(stub).map(([key, value]) => {
|
||||
return [`## ${key}`, Object.entries(value).
|
||||
map(([key2, value2]) => {
|
||||
return [`### ${key2}`,
|
||||
([...(value2.entries())].map(([key3, value3]) => {
|
||||
// return `#### ${key3}` + "\n" + JSON.stringify(value3);
|
||||
const isObsolete = value3["is_obsolete"] ? " (obsolete)" : "";
|
||||
const desc = value3["desc"] ?? "";
|
||||
const key = value3["key"] ? "Setting key: " + value3["key"] + "\n" : "";
|
||||
return `#### ${key3}${isObsolete}\n${key}${desc}\n`
|
||||
}))].flat()
|
||||
}).flat()].flat()
|
||||
}).flat();
|
||||
const stubDocMD = `
|
||||
| Icon | Description |
|
||||
| :---: | ----------------------------------------------------------------- |
|
||||
` +
|
||||
[...toc.values()].map(e => `${e}`).join("\n") + "\n\n" +
|
||||
stubDocX.join("\n");
|
||||
await this.vaultAccess.adapterWrite(this.app.vault.configDir + "/ls-debug/stub-doc.md", stubDocMD);
|
||||
})
|
||||
}
|
||||
this.settingTab = new ObsidianLiveSyncSettingTab(this.app, this);
|
||||
this.addSettingTab(this.settingTab);
|
||||
@@ -1292,7 +1323,7 @@ Note: We can always able to read V1 format. It will be progressively converted.
|
||||
}
|
||||
|
||||
onunload() {
|
||||
eventHub.emit(EVENT_PLUGIN_UNLOADED);
|
||||
eventHub.emitEvent(EVENT_PLUGIN_UNLOADED);
|
||||
cancelAllPeriodicTask();
|
||||
cancelAllTasks();
|
||||
stopAllRunningProcessors();
|
||||
@@ -1305,7 +1336,7 @@ Note: We can always able to read V1 format. It will be progressively converted.
|
||||
}
|
||||
this.periodicSyncProcessor?.disable();
|
||||
if (this.localDatabase != null) {
|
||||
this.replicator.closeReplication();
|
||||
this.replicator?.closeReplication();
|
||||
this.localDatabase.close();
|
||||
}
|
||||
Logger($f`unloading plugin`);
|
||||
@@ -1496,7 +1527,7 @@ Note: We can always able to read V1 format. It will be progressively converted.
|
||||
|
||||
}
|
||||
await this.saveData(settings);
|
||||
eventHub.emit(EVENT_SETTING_SAVED, settings);
|
||||
eventHub.emitEvent(EVENT_SETTING_SAVED, settings);
|
||||
}
|
||||
|
||||
extractSettingFromWholeText(data: string): { preamble: string, body: string, postscript: string } {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
339
src/ui/components/LiveSyncSetting.ts
Normal file
339
src/ui/components/LiveSyncSetting.ts
Normal file
@@ -0,0 +1,339 @@
|
||||
import { Setting, TextComponent, type ToggleComponent, type DropdownComponent, ButtonComponent, type TextAreaComponent, type ValueComponent } from "obsidian";
|
||||
import { unique } from "octagonal-wheels/collection";
|
||||
import { LEVEL_ADVANCED, LEVEL_POWER_USER, statusDisplay, type ConfigurationItem } from "../../lib/src/common/types";
|
||||
import { type ObsidianLiveSyncSettingTab, type AutoWireOption, wrapMemo, type OnUpdateResult, createStub, findAttrFromParent } from "../ObsidianLiveSyncSettingTab";
|
||||
import { type AllSettingItemKey, getConfig, type AllSettings, type AllStringItemKey, type AllNumericItemKey, type AllBooleanItemKey } from "../settingConstants";
|
||||
|
||||
|
||||
export class LiveSyncSetting extends Setting {
|
||||
autoWiredComponent?: TextComponent | ToggleComponent | DropdownComponent | ButtonComponent | TextAreaComponent;
|
||||
applyButtonComponent?: ButtonComponent;
|
||||
selfKey?: AllSettingItemKey;
|
||||
watchDirtyKeys = [] as AllSettingItemKey[];
|
||||
holdValue: boolean = false;
|
||||
static env: ObsidianLiveSyncSettingTab;
|
||||
|
||||
descBuf: string | DocumentFragment = "";
|
||||
nameBuf: string | DocumentFragment = "";
|
||||
placeHolderBuf: string = "";
|
||||
hasPassword: boolean = false;
|
||||
|
||||
invalidateValue?: () => void;
|
||||
setValue?: (value: any) => void;
|
||||
constructor(containerEl: HTMLElement) {
|
||||
super(containerEl);
|
||||
LiveSyncSetting.env.settingComponents.push(this);
|
||||
}
|
||||
|
||||
_createDocStub(key: string, value: string | DocumentFragment) {
|
||||
DEV: {
|
||||
const paneName = findAttrFromParent(this.settingEl, "data-pane");
|
||||
const panelName = findAttrFromParent(this.settingEl, "data-panel");
|
||||
const itemName = typeof this.nameBuf == "string" ? this.nameBuf : this.nameBuf.textContent?.toString() ?? "";
|
||||
const strValue = typeof value == "string" ? value : value.textContent?.toString() ?? "";
|
||||
|
||||
createStub(itemName, key, strValue, panelName, paneName);
|
||||
}
|
||||
}
|
||||
|
||||
setDesc(desc: string | DocumentFragment): this {
|
||||
this.descBuf = desc;
|
||||
DEV: {
|
||||
this._createDocStub("desc", desc);
|
||||
}
|
||||
super.setDesc(desc);
|
||||
return this;
|
||||
}
|
||||
setName(name: string | DocumentFragment): this {
|
||||
this.nameBuf = name;
|
||||
DEV: {
|
||||
this._createDocStub("name", name);
|
||||
}
|
||||
super.setName(name);
|
||||
return this;
|
||||
}
|
||||
setAuto(key: AllSettingItemKey, opt?: AutoWireOption) {
|
||||
this.autoWireSetting(key, opt);
|
||||
return this;
|
||||
}
|
||||
autoWireSetting(key: AllSettingItemKey, opt?: AutoWireOption) {
|
||||
const conf = getConfig(key);
|
||||
if (!conf) {
|
||||
// throw new Error(`No such setting item :${key}`)
|
||||
return;
|
||||
}
|
||||
const name = `${conf.name}${statusDisplay(conf.status)}`;
|
||||
this.setName(name);
|
||||
if (conf.desc) {
|
||||
this.setDesc(conf.desc);
|
||||
}
|
||||
DEV: {
|
||||
this._createDocStub("key", key);
|
||||
if (conf.obsolete) this._createDocStub("is_obsolete", "true");
|
||||
if (conf.level) this._createDocStub("level", conf.level);
|
||||
}
|
||||
|
||||
this.holdValue = opt?.holdValue || this.holdValue;
|
||||
this.selfKey = key;
|
||||
if (conf.obsolete || opt?.obsolete) {
|
||||
this.settingEl.toggleClass("sls-setting-obsolete", true);
|
||||
}
|
||||
if (opt?.onUpdate) this.addOnUpdate(opt.onUpdate);
|
||||
const stat = this._getComputedStatus();
|
||||
if (stat.visibility === false) {
|
||||
this.settingEl.toggleClass("sls-setting-hidden", !stat.visibility);
|
||||
}
|
||||
return conf;
|
||||
}
|
||||
autoWireComponent(component: ValueComponent<any>, conf?: ConfigurationItem, opt?: AutoWireOption) {
|
||||
this.placeHolderBuf = conf?.placeHolder || opt?.placeHolder || "";
|
||||
if (conf?.level == LEVEL_ADVANCED) {
|
||||
this.settingEl.toggleClass("sls-setting-advanced", true);
|
||||
} else if (conf?.level == LEVEL_POWER_USER) {
|
||||
this.settingEl.toggleClass("sls-setting-poweruser", true);
|
||||
}
|
||||
if (this.placeHolderBuf && component instanceof TextComponent) {
|
||||
component.setPlaceholder(this.placeHolderBuf);
|
||||
}
|
||||
if (opt?.onUpdate) this.addOnUpdate(opt.onUpdate);
|
||||
}
|
||||
async commitValue<T extends AllSettingItemKey>(value: AllSettings[T]) {
|
||||
const key = this.selfKey as T;
|
||||
if (key !== undefined) {
|
||||
if (value != LiveSyncSetting.env.editingSettings[key]) {
|
||||
LiveSyncSetting.env.editingSettings[key] = value;
|
||||
if (!this.holdValue) {
|
||||
await LiveSyncSetting.env.saveSettings([key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
LiveSyncSetting.env.requestUpdate();
|
||||
}
|
||||
autoWireText(key: AllStringItemKey, opt?: AutoWireOption) {
|
||||
const conf = this.autoWireSetting(key, opt);
|
||||
this.addText(text => {
|
||||
this.autoWiredComponent = text;
|
||||
const setValue = wrapMemo((value: string) => text.setValue(value));
|
||||
this.invalidateValue = () => setValue(`${LiveSyncSetting.env.editingSettings[key]}`);
|
||||
this.invalidateValue();
|
||||
text.onChange(async (value) => {
|
||||
await this.commitValue(value);
|
||||
});
|
||||
if (opt?.isPassword) {
|
||||
text.inputEl.setAttribute("type", "password");
|
||||
this.hasPassword = true;
|
||||
}
|
||||
this.autoWireComponent(this.autoWiredComponent, conf, opt);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
autoWireTextArea(key: AllStringItemKey, opt?: AutoWireOption) {
|
||||
const conf = this.autoWireSetting(key, opt);
|
||||
this.addTextArea(text => {
|
||||
this.autoWiredComponent = text;
|
||||
const setValue = wrapMemo((value: string) => text.setValue(value));
|
||||
this.invalidateValue = () => setValue(`${LiveSyncSetting.env.editingSettings[key]}`);
|
||||
this.invalidateValue();
|
||||
text.onChange(async (value) => {
|
||||
await this.commitValue(value);
|
||||
});
|
||||
if (opt?.isPassword) {
|
||||
text.inputEl.setAttribute("type", "password");
|
||||
this.hasPassword = true;
|
||||
}
|
||||
this.autoWireComponent(this.autoWiredComponent, conf, opt);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
autoWireNumeric(key: AllNumericItemKey, opt: AutoWireOption & { clampMin?: number; clampMax?: number; acceptZero?: boolean; }) {
|
||||
const conf = this.autoWireSetting(key, opt);
|
||||
this.addText(text => {
|
||||
this.autoWiredComponent = text;
|
||||
if (opt.clampMin) {
|
||||
text.inputEl.setAttribute("min", `${opt.clampMin}`);
|
||||
}
|
||||
if (opt.clampMax) {
|
||||
text.inputEl.setAttribute("max", `${opt.clampMax}`);
|
||||
}
|
||||
let lastError = false;
|
||||
const setValue = wrapMemo((value: string) => text.setValue(value));
|
||||
this.invalidateValue = () => {
|
||||
if (!lastError) setValue(`${LiveSyncSetting.env.editingSettings[key]}`);
|
||||
};
|
||||
this.invalidateValue();
|
||||
text.onChange(async (TextValue) => {
|
||||
const parsedValue = Number(TextValue);
|
||||
const value = parsedValue;
|
||||
let hasError = false;
|
||||
if (isNaN(value)) hasError = true;
|
||||
if (opt.clampMax && opt.clampMax < value) hasError = true;
|
||||
if (opt.clampMin && opt.clampMin > value) {
|
||||
if (opt.acceptZero && value == 0) {
|
||||
// This is ok.
|
||||
} else {
|
||||
hasError = true;
|
||||
}
|
||||
}
|
||||
if (!hasError) {
|
||||
lastError = false;
|
||||
this.setTooltip(``);
|
||||
text.inputEl.toggleClass("sls-item-invalid-value", false);
|
||||
await this.commitValue(value);
|
||||
} else {
|
||||
this.setTooltip(`The value should ${opt.clampMin || "~"} < value < ${opt.clampMax || "~"}`);
|
||||
text.inputEl.toggleClass("sls-item-invalid-value", true);
|
||||
lastError = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
text.inputEl.setAttr("type", "number");
|
||||
this.autoWireComponent(this.autoWiredComponent, conf, opt);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
autoWireToggle(key: AllBooleanItemKey, opt?: AutoWireOption) {
|
||||
const conf = this.autoWireSetting(key, opt);
|
||||
this.addToggle(toggle => {
|
||||
this.autoWiredComponent = toggle;
|
||||
const setValue = wrapMemo((value: boolean) => toggle.setValue(opt?.invert ? !value : value));
|
||||
this.invalidateValue = () => setValue(LiveSyncSetting.env.editingSettings[key] ?? false);
|
||||
this.invalidateValue();
|
||||
|
||||
toggle.onChange(async (value) => {
|
||||
await this.commitValue(opt?.invert ? !value : value);
|
||||
});
|
||||
|
||||
this.autoWireComponent(this.autoWiredComponent, conf, opt);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
autoWireDropDown<T extends string>(key: AllStringItemKey, opt: AutoWireOption & { options: Record<T, string>; }) {
|
||||
const conf = this.autoWireSetting(key, opt);
|
||||
this.addDropdown(dropdown => {
|
||||
this.autoWiredComponent = dropdown;
|
||||
const setValue = wrapMemo((value: string) => {
|
||||
dropdown.setValue(value);
|
||||
});
|
||||
|
||||
dropdown
|
||||
.addOptions(opt.options);
|
||||
|
||||
this.invalidateValue = () => setValue(LiveSyncSetting.env.editingSettings[key] || "");
|
||||
this.invalidateValue();
|
||||
dropdown.onChange(async (value) => {
|
||||
await this.commitValue(value);
|
||||
});
|
||||
this.autoWireComponent(this.autoWiredComponent, conf, opt);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
addApplyButton(keys: AllSettingItemKey[], text?: string) {
|
||||
this.addButton((button) => {
|
||||
this.applyButtonComponent = button;
|
||||
this.watchDirtyKeys = unique([...keys, ...this.watchDirtyKeys]);
|
||||
button.setButtonText(text ?? "Apply");
|
||||
button.onClick(async () => {
|
||||
await LiveSyncSetting.env.saveSettings(keys);
|
||||
LiveSyncSetting.env.reloadAllSettings();
|
||||
});
|
||||
LiveSyncSetting.env.requestUpdate();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
addOnUpdate(func: () => OnUpdateResult) {
|
||||
this.updateHandlers.add(func);
|
||||
// this._applyOnUpdateHandlers();
|
||||
return this;
|
||||
}
|
||||
updateHandlers = new Set<() => OnUpdateResult>();
|
||||
|
||||
prevStatus: OnUpdateResult = {};
|
||||
|
||||
_getComputedStatus() {
|
||||
let newConf = {} as OnUpdateResult;
|
||||
for (const handler of this.updateHandlers) {
|
||||
newConf = {
|
||||
...newConf,
|
||||
...handler(),
|
||||
};
|
||||
}
|
||||
return newConf;
|
||||
}
|
||||
_applyOnUpdateHandlers() {
|
||||
if (this.updateHandlers.size > 0) {
|
||||
const newConf = this._getComputedStatus();
|
||||
const keys = Object.keys(newConf) as [keyof OnUpdateResult];
|
||||
for (const k of keys) {
|
||||
|
||||
if (k in this.prevStatus && this.prevStatus[k] == newConf[k]) {
|
||||
continue;
|
||||
}
|
||||
// const newValue = newConf[k];
|
||||
switch (k) {
|
||||
case "visibility":
|
||||
this.settingEl.toggleClass("sls-setting-hidden", !(newConf[k] || false));
|
||||
this.prevStatus[k] = newConf[k];
|
||||
break;
|
||||
case "classes":
|
||||
break;
|
||||
case "disabled":
|
||||
this.setDisabled((newConf[k] || false));
|
||||
this.settingEl.toggleClass("sls-setting-disabled", (newConf[k] || false));
|
||||
this.prevStatus[k] = newConf[k];
|
||||
break;
|
||||
case "isCta":
|
||||
{
|
||||
const component = this.autoWiredComponent;
|
||||
if (component instanceof ButtonComponent) {
|
||||
if (newConf[k]) {
|
||||
component.setCta();
|
||||
} else {
|
||||
component.removeCta();
|
||||
}
|
||||
}
|
||||
this.prevStatus[k] = newConf[k];
|
||||
}
|
||||
break;
|
||||
case "isWarning":
|
||||
{
|
||||
const component = this.autoWiredComponent;
|
||||
if (component instanceof ButtonComponent) {
|
||||
if (newConf[k]) {
|
||||
component.setWarning();
|
||||
} else {
|
||||
//TODO:IMPLEMENT
|
||||
// component.removeCta();
|
||||
}
|
||||
}
|
||||
this.prevStatus[k] = newConf[k];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_onUpdate() {
|
||||
if (this.applyButtonComponent) {
|
||||
const isDirty = LiveSyncSetting.env.isSomeDirty(this.watchDirtyKeys);
|
||||
this.applyButtonComponent.setDisabled(!isDirty);
|
||||
if (isDirty) {
|
||||
this.applyButtonComponent.setCta();
|
||||
} else {
|
||||
this.applyButtonComponent.removeCta();
|
||||
}
|
||||
}
|
||||
if (this.selfKey && !LiveSyncSetting.env.isDirty(this.selfKey) && this.invalidateValue) {
|
||||
this.invalidateValue();
|
||||
}
|
||||
if (this.holdValue && this.selfKey) {
|
||||
const isDirty = LiveSyncSetting.env.isDirty(this.selfKey);
|
||||
const alt = isDirty ? `Original: ${LiveSyncSetting.env.initialSettings![this.selfKey]}` : "";
|
||||
this.controlEl.toggleClass("sls-item-dirty", isDirty);
|
||||
if (!this.hasPassword) {
|
||||
this.nameEl.toggleClass("sls-item-dirty-help", isDirty);
|
||||
this.setTooltip(alt, { delay: 10, placement: "right" });
|
||||
}
|
||||
}
|
||||
this._applyOnUpdateHandlers();
|
||||
}
|
||||
}
|
||||
@@ -198,8 +198,9 @@ export const SettingInformation: Partial<Record<keyof AllSettings, Configuration
|
||||
"name": "Do not keep metadata of deleted files."
|
||||
},
|
||||
"useIndexedDBAdapter": {
|
||||
"name": "Use an old adapter for compatibility",
|
||||
"desc": "Before v0.17.16, we used an old adapter for the local database. Now the new adapter is preferred. However, it needs local database rebuilding. Please disable this toggle when you have enough time. If leave it enabled, also while fetching from the remote database, you will be asked to disable this."
|
||||
"name": "(Obsolete) Use an old adapter for compatibility",
|
||||
"desc": "Before v0.17.16, we used an old adapter for the local database. Now the new adapter is preferred. However, it needs local database rebuilding. Please disable this toggle when you have enough time. If leave it enabled, also while fetching from the remote database, you will be asked to disable this.",
|
||||
"obsolete": true
|
||||
},
|
||||
"watchInternalFileChanges": {
|
||||
"name": "Scan changes on customization sync",
|
||||
@@ -341,6 +342,18 @@ export const SettingInformation: Partial<Record<keyof AllSettings, Configuration
|
||||
name: "Maximum size of chunks to send in one request",
|
||||
desc: "MB"
|
||||
},
|
||||
"useAdvancedMode": {
|
||||
name: "Enable advanced features",
|
||||
// desc: "Enable advanced mode"
|
||||
},
|
||||
usePowerUserMode: {
|
||||
name: "Enable power user features",
|
||||
// desc: "Enable power user mode",
|
||||
// level: LEVEL_ADVANCED
|
||||
},
|
||||
useEdgeCaseMode: {
|
||||
name: "Enable edge case treatment features",
|
||||
},
|
||||
}
|
||||
function translateInfo(infoSrc: ConfigurationItem | undefined | false) {
|
||||
if (!infoSrc) return false;
|
||||
|
||||
Reference in New Issue
Block a user