zhou-yusen_Moving-Note/settings.ts
Yusen 959bc999bd Fix remaining review issues
- Bump minAppVersion to 1.2.0 (setDisabled requires it)
- Add eslint-disable for required Node.js module imports
- Use instanceof TFile instead of type casting
- Wrap async callbacks to avoid void return warnings
2026-06-13 19:50:01 +08:00

279 lines
12 KiB
TypeScript

import { App, PluginSettingTab, Setting, Notice } from "obsidian";
import type MovingNotePlugin from "./main";
import { parseRepoUrl } from "./types";
import { validateToken } from "./github-auth";
import { SYNC_ICON } from "./sync-icon";
import { t, setLocaleOverride } from "./locale";
/**
* GitHub Fine-grained Token 创建链接
* 预填了 repo 权限,用户只需点确认再复制 Token
*/
const CREATE_TOKEN_URL =
"https://github.com/settings/tokens/new" +
"?scopes=repo" +
"&description=Moving+Note+Obsidian+Plugin";
export class MovingNoteSettingTab extends PluginSettingTab {
plugin: MovingNotePlugin;
constructor(app: App, plugin: MovingNotePlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const { containerEl } = this;
containerEl.empty();
// ─── Language selector ────────────────────────────
new Setting(containerEl)
.setName(t("settings.language"))
.setDesc(t("settings.language_desc"))
.addDropdown((dropdown) => {
dropdown.addOption("auto", "Auto");
dropdown.addOption("en", "English");
dropdown.addOption("zh", "中文");
dropdown.setValue(this.plugin.settings.language || "auto");
dropdown.onChange(async (value) => {
this.plugin.settings.language = value as "auto" | "en" | "zh";
await this.plugin.saveSettings();
setLocaleOverride(this.plugin.settings.language);
this.display();
});
});
// ─── Sync ────────────────────────────────────────
new Setting(containerEl).setName(t("settings.heading.sync")).setHeading();
new Setting(containerEl)
.setName(t("settings.syncDocs"))
.setDesc(t("settings.syncDocs_desc"))
.addButton((btn) => {
btn.buttonEl.addClass("moving-note-sync-btn");
btn.buttonEl.innerHTML = "";
const img = btn.buttonEl.createEl("img");
img.src = SYNC_ICON;
img.addEventListener("click", () => {
img.addClass("syncing");
void (async () => {
const result = await this.plugin.syncNow();
new Notice(result.message);
img.removeClass("syncing");
})();
});
});
// Status hint
if (this.plugin.settings.lastSyncSha) {
const statusP = containerEl.createEl("p");
statusP.textContent = t("settings.lastSync", {
sha: this.plugin.settings.lastSyncSha.substring(0, 8),
});
statusP.addClass("moving-note-status");
}
// ─── Sync Settings ───────────────────────────────
new Setting(containerEl).setName(t("settings.heading.syncSettings")).setHeading();
new Setting(containerEl)
.setName(t("settings.autoSyncInterval"))
.setDesc(t("settings.autoSyncInterval_desc"))
.addText((text) => {
text.inputEl.type = "number";
text.setPlaceholder("0")
.setValue(
String(this.plugin.settings.autoSyncInterval)
)
.onChange(async (value) => {
this.plugin.settings.autoSyncInterval = Math.max(
0,
Number(value) || 0
);
await this.plugin.saveSettings();
});
});
new Setting(containerEl)
.setName(t("settings.syncOnStartup"))
.setDesc(t("settings.syncOnStartup_desc"))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.syncOnStartup)
.onChange(async (value) => {
this.plugin.settings.syncOnStartup = value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName(t("settings.commitMsg"))
.setDesc(t("settings.commitMsg_desc"))
.addText((text) =>
text
.setPlaceholder(t("settings.commitMsg_placeholder"))
.setValue(this.plugin.settings.commitMessage)
.onChange(async (value) => {
this.plugin.settings.commitMessage =
value || "vault backup: {{date}}";
await this.plugin.saveSettings();
})
);
// ─── Remote Repository ───────────────────────────
new Setting(containerEl).setName(t("settings.heading.remote")).setHeading();
new Setting(containerEl)
.setName(t("settings.repoUrl"))
.setDesc(t("settings.repoUrl_desc"))
.addText((text) => {
text.setPlaceholder(t("settings.repoUrl_placeholder"))
.setValue(this.plugin.settings.repoUrl)
.onChange(async (value) => {
this.plugin.settings.repoUrl = value.trim();
await this.plugin.saveSettings();
this.display();
});
text.inputEl.addClass("moving-note-full-width");
});
// Display parsed result
const parsed = parseRepoUrl(this.plugin.settings.repoUrl);
if (parsed) {
const desc = containerEl.createEl("p");
desc.textContent = t("settings.parsed", {
owner: parsed.owner,
repo: parsed.repo,
});
desc.addClass("moving-note-parsed");
} else if (this.plugin.settings.repoUrl) {
const desc = containerEl.createEl("p");
desc.textContent = t("settings.parseError");
desc.addClass("moving-note-parse-error");
}
new Setting(containerEl)
.setName(t("settings.branch"))
.setDesc(t("settings.branch_desc"))
.addText((text) =>
text
.setPlaceholder("main")
.setValue(this.plugin.settings.branch)
.onChange(async (value) => {
this.plugin.settings.branch =
value.trim() || "main";
await this.plugin.saveSettings();
})
);
// ─── GitHub Login ────────────────────────────────
new Setting(containerEl).setName(t("settings.heading.github")).setHeading();
if (this.plugin.settings.githubToken) {
// Logged in
new Setting(containerEl)
.setName(t("settings.loggedIn", {
username: this.plugin.settings.githubUsername || t("settings.loggedIn_default"),
}))
.setDesc(t("settings.loggedIn_desc"))
.addButton((btn) =>
btn.setButtonText(t("settings.logout")).onClick(async () => {
this.plugin.settings.githubToken = "";
this.plugin.settings.githubUsername = "";
await this.plugin.saveSettings();
this.display();
})
);
} else {
// Not logged in — guide to create Token
new Setting(containerEl)
.setName(t("settings.step1"))
.setDesc(t("settings.step1_desc"))
.addButton((btn) => {
btn.setButtonText(t("settings.step1_btn"))
.setCta()
.onClick(() => {
window.open(CREATE_TOKEN_URL, "_blank");
});
});
// Token input
new Setting(containerEl)
.setName(t("settings.step2"))
.setDesc(t("settings.step2_desc"))
.addText((text) => {
text.inputEl.type = "password";
text.inputEl.addClass("moving-note-full-width");
text.setPlaceholder(t("settings.token_placeholder"))
.setValue(this.plugin.settings.githubToken)
.onChange(async (value) => {
this.plugin.settings.githubToken = value.trim();
});
})
.addButton((btn) =>
btn.setButtonText(t("settings.verify")).onClick(async () => {
if (!this.plugin.settings.githubToken) {
new Notice(t("settings.pasteFirst"));
return;
}
btn.setButtonText(t("settings.verifying"));
btn.setDisabled(true);
const result = await validateToken(
this.plugin.settings.githubToken
);
if (result.valid) {
this.plugin.settings.githubUsername =
result.username || "";
await this.plugin.saveSettings();
new Notice(
t("settings.loginSuccess", { username: result.username })
);
this.display();
} else {
new Notice(t("settings.loginFailed"));
btn.setButtonText(t("settings.verify"));
btn.setDisabled(false);
}
})
);
}
// Hints
if (!this.plugin.settings.repoUrl) {
const hintP = containerEl.createEl("p");
hintP.textContent = t("settings.hint.noRepo");
hintP.addClass("moving-note-hint");
} else if (!this.plugin.settings.githubToken) {
const hintP = containerEl.createEl("p");
hintP.textContent = t("settings.hint.noToken");
hintP.addClass("moving-note-hint");
}
// ─── Support Author ──────────────────────────────
new Setting(containerEl).setName(t("settings.heading.support")).setHeading();
new Setting(containerEl)
.setName(t("settings.donate"))
.setDesc(t("settings.donate_desc"))
.addButton((bt) => {
const link = bt.buttonEl.parentElement?.createEl("a", {
href: "https://ko-fi.com/gregweaver",
attr: {
target: "_blank",
},
});
if (link) {
link.createEl("img", {
attr: {
height: "36",
style: "border:0px;height:36px;",
src: "https://cdn.ko-fi.com/cdn/kofi3.png?v=3",
border: "0",
alt: "Buy Me a Coffee at ko-fi.com",
},
});
bt.buttonEl.remove();
}
});
}
}