/
raiden
/
obsidian-git-encrypt
Обзор
Документация
Войти
/
raiden
/
obsidian-git-encrypt
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/settings/settingsTab.ts
172 строки
6 KB
Robert Kuzhin
feat: add manual encrypted source control
09 авг 2026, 20:57
09 авг 2026, 20:57
3c8fa38
Код
Авторство
О чём код?
import { PluginSettingTab, Setting, type App, type Plugin } from "obsidian"; import type { SessionState } from "../session/index.js"; import { type EncryptedGitSettings, validateAbsolutePath, validateBranchSetting, validateRemoteSetting, } from "./settings.js"; export interface SettingsTabController { getSettings(): EncryptedGitSettings; getSessionState(): SessionState; saveSettings(settings: EncryptedGitSettings): Promise<void>; showTrackedFiles(): Promise<void>; lock(): Promise<void>; } export class EncryptedGitSettingsTab extends PluginSettingTab { public constructor( app: App, plugin: Plugin, private readonly controller: SettingsTabController, ) { super(app, plugin); } public override display(): void { this.containerEl.empty(); this.containerEl.createEl("h2", { text: "Encrypted Git" }); this.containerEl.createEl("p", { text: "Git stores only the encrypted mirror. The working vault remains plaintext on disk.", }); const settings = this.controller.getSettings(); const locked = !this.controller.getSessionState().hasKey; addValidatedTextSetting( this.containerEl, "Encrypted repository path", "Absolute sibling directory used as the only Git working tree.", settings.mirrorPath, locked, validateAbsolutePath, (value) => this.save({ ...this.controller.getSettings(), mirrorPath: value }), ); addValidatedTextSetting( this.containerEl, "Local state path", "Device-local encrypted state. Keep it outside the encrypted Git repository.", settings.statePath, locked, validateAbsolutePath, (value) => this.save({ ...this.controller.getSettings(), statePath: value }), ); addValidatedTextSetting( this.containerEl, "Remote URL", "Optional Git remote. Use an SSH agent or credential helper; embedded secrets are rejected.", settings.remoteUrl, locked, validateRemoteSetting, (value) => this.save({ ...this.controller.getSettings(), remoteUrl: value }), ); addValidatedTextSetting( this.containerEl, "Branch", "Remote branch used for encrypted synchronization.", settings.branch, locked, validateBranchSetting, (value) => this.save({ ...this.controller.getSettings(), branch: value }), ); new Setting(this.containerEl) .setName("Background fetch") .setDesc("Check for incoming commits without applying them or opening conflict dialogs.") .setDisabled(!locked) .addToggle((toggle) => toggle.setValue(settings.backgroundFetch).onChange((value) => { void this.save({ ...this.controller.getSettings(), backgroundFetch: value }); }), ); new Setting(this.containerEl) .setName("Background fetch interval") .setDesc("Minutes between read-only remote checks, from 1 to 1440.") .setDisabled(!locked || !settings.backgroundFetch) .addText((text) => { text.setValue(String(settings.backgroundFetchIntervalMinutes)); text.onChange((value) => { const parsed = Number(value); if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > 1_440) return; void this.save({ ...this.controller.getSettings(), backgroundFetchIntervalMinutes: parsed, }); }); }); new Setting(this.containerEl) .setName("Default commit message") .setDesc("Commit messages are Git metadata and are not encrypted.") .setDisabled(!locked) .addText((text) => { text.setValue(settings.commitMessage); text.onChange((value) => { if (value.length === 0 || value.length > 200 || /[\0\r\n]/u.test(value)) return; void this.save({ ...this.controller.getSettings(), commitMessage: value }); }); }); new Setting(this.containerEl) .setName("Tracked encrypted files") .setDesc("View vault paths currently present in the encrypted manifest.") .setDisabled(locked) .addButton((button) => button.setButtonText("View").onClick(() => { void this.controller.showTrackedFiles(); }), ); new Setting(this.containerEl) .setName("Lock current session") .setDesc("Flush pending changes before destroying the in-memory vault key.") .setDisabled(locked) .addButton((button) => button.setButtonText("Lock").onClick(() => { void this.controller.lock().then(() => this.display()); }), ); } private async save(settings: EncryptedGitSettings): Promise<void> { await this.controller.saveSettings(settings); } } function addValidatedTextSetting( container: HTMLElement, name: string, description: string, value: string, enabled: boolean, validate: (candidate: string) => string | undefined, save: (candidate: string) => Promise<void>, ): void { const setting = new Setting(container) .setName(name) .setDesc(description) .setDisabled(!enabled); setting.addText((text) => { let latestCandidate = value; const showError = (message: string | undefined): void => { setting.descEl.textContent = message ?? description; setting.settingEl.classList.toggle("encrypted-git-setting-invalid", message !== undefined); text.inputEl.setAttribute("aria-invalid", message === undefined ? "false" : "true"); }; text.setValue(value); text.onChange((candidate) => { latestCandidate = candidate; const error = validate(candidate); showError(error); if (error === undefined) { void save(candidate).catch(() => { if (latestCandidate === candidate) { showError("The configured paths must remain separate and safe."); } }); } }); }); }