diff --git a/src/changelog.ts b/src/changelog.ts index ce1ddba..3c9b60e 100644 --- a/src/changelog.ts +++ b/src/changelog.ts @@ -20,6 +20,20 @@ export interface ChangelogRelease { } export const CHANGELOG: ChangelogRelease[] = [ + { + version: '1.3.0', + entries: [ + { text: 'The plugin now speaks multiple languages — English, Traditional Chinese, and Simplified Chinese. It follows your Obsidian display language automatically, or you can pick one in Settings.', notable: true }, + { text: 'Checking sync status is now much faster, especially in vaults with lots of files.', notable: true }, + { text: 'Fixed a bug where a linked (symlinked) folder could be pulled incorrectly instead of being treated as a link.', notable: true }, + { text: 'Added a setting to keep specific files or folders out of sync, in addition to what your repo\'s .gitignore already excludes.' }, + { text: 'Settings now show your connection status at a glance, so you can tell right away if something needs attention.' }, + { text: 'The conflict resolution window can now be resized to see more content at once.' }, + { text: 'Picking your sync folders is easier now, with a folder browser instead of typing paths by hand.' }, + { text: 'Connection errors now explain what went wrong in plain language instead of a raw technical error.' }, + { text: 'You\'ll now see a short "what\'s new" summary right after updating, so you don\'t miss new features.' }, + ], + }, { version: '1.2.1', entries: [ diff --git a/src/i18n/index.ts b/src/i18n/index.ts index 7f78619..50cba80 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -1,13 +1,26 @@ import en, { TranslationKey } from './locales/en'; import zhTw from './locales/zh-tw'; +import zhCn from './locales/zh-cn'; export type { TranslationKey }; const locales: Record>> = { en, 'zh-tw': zhTw, + 'zh-cn': zhCn, }; +/** User-facing language choices exposed in the settings UI. 'system' follows Obsidian's display language. */ +export type LanguageSetting = 'system' | 'en' | 'zh-tw' | 'zh-cn'; + +// Explicit language chosen in plugin settings, or 'system' (default) to follow +// Obsidian's display language. Set once at load via setLanguageOverride(). +let languageOverride: LanguageSetting = 'system'; + +export function setLanguageOverride(language: LanguageSetting): void { + languageOverride = language; +} + // Obsidian sets window.moment's locale to match the app's display language // before plugins load. Not typed in the `obsidian` package, so read it off // the global defensively. @@ -24,11 +37,12 @@ function detectMomentLocale(): string { // English when there's no matching translation. function resolveLocale(rawLocale: string): string { if (rawLocale in locales) return rawLocale; - if (rawLocale.startsWith('zh')) return 'zh-tw'; + if (rawLocale.startsWith('zh')) return rawLocale.includes('cn') ? 'zh-cn' : 'zh-tw'; return 'en'; } export function getActiveLocale(): string { + if (languageOverride !== 'system') return languageOverride; return resolveLocale(detectMomentLocale()); } diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index d2d35eb..c1839b0 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -6,6 +6,13 @@ const en = { 'settings.connectionStatus.disconnected': 'Not connected', 'settings.connectionStatus.withDetail': '{label} — {detail}', + 'settings.language.name': 'Language', + 'settings.language.desc': 'UI language for this plugin', + 'settings.language.option.system': 'System default', + 'settings.language.option.en': 'English', + 'settings.language.option.zhTw': '繁體中文 (Traditional Chinese)', + 'settings.language.option.zhCn': '简体中文 (Simplified Chinese)', + 'settings.gitService.name': 'Git service', 'settings.gitService.desc': 'Choose your Git hosting service', @@ -102,6 +109,9 @@ const en = { 'whatsNew.viewChangelog': 'View full changelog', 'whatsNew.gotIt': 'Got it', + 'settings.whatsNewBanner.title': "What's new in v{version}", + 'settings.whatsNewBanner.dismiss': 'Dismiss', + 'syncStatus.viewTitle': 'Sync status', 'syncStatus.emptyPrompt': 'Click "Refresh" to check sync status', 'syncStatus.progress.checkingWithCount': 'Checking files… {current}/{total} ({pct}%)', diff --git a/src/i18n/locales/zh-cn.ts b/src/i18n/locales/zh-cn.ts new file mode 100644 index 0000000..ac984d7 --- /dev/null +++ b/src/i18n/locales/zh-cn.ts @@ -0,0 +1,200 @@ +import type { TranslationKey } from './en'; + +// Only needs to cover the keys that have a translation; anything missing +// falls back to en.ts at lookup time. +const zhCn: Partial> = { + 'settings.connectionStatus.checking': '检查中…', + 'settings.connectionStatus.connected': '已连接', + 'settings.connectionStatus.disconnected': '未连接', + 'settings.connectionStatus.withDetail': '{label} — {detail}', + + 'settings.language.name': '语言', + 'settings.language.desc': '此插件的界面语言', + 'settings.language.option.system': '系统默认', + 'settings.language.option.en': 'English', + 'settings.language.option.zhTw': '繁體中文', + 'settings.language.option.zhCn': '简体中文', + + 'settings.gitService.name': 'Git 服务', + 'settings.gitService.desc': '选择您的 Git 托管服务', + + 'settings.branch.name': '分支', + 'settings.branch.desc': '要推送或拉取的分支', + 'settings.branch.placeholder': 'Main', + + 'settings.rootPath.name': '根路径', + 'settings.rootPath.desc': '选填:仓库中的子文件夹(例如「notes」)', + 'settings.rootPath.placeholder': '输入子文件夹路径', + + 'settings.vaultFolder.name': '库文件夹', + 'settings.vaultFolder.desc': '选填:仅同步此库文件夹内的文件(例如「sync」则只同步 sync 文件夹内的文件)', + 'settings.vaultFolder.placeholder': '留空以同步所有文件', + + 'settings.ignorePatterns.name': '忽略规则', + 'settings.ignorePatterns.desc': '选填:以 .gitignore 语法(每行一条)排除本机文件,会与远程仓库的 .gitignore 规则一并应用。', + + 'settings.symlinks.name': '符号链接', + 'settings.symlinks.desc.supported': '符号链接的同步方式:「real」会在桌面端重建链接,移动端则改为同步目标内容;「follow」始终同步目标内容;「skip」则忽略符号链接。', + 'settings.symlinks.desc.unsupported': '符号链接的同步方式:「follow」以普通文件同步目标内容,「skip」则忽略符号链接。真实符号链接仅支持 GitHub。', + 'settings.symlinks.option.real': '真实符号链接(推荐)', + 'settings.symlinks.option.follow': 'Follow(同步目标内容)', + 'settings.symlinks.option.skip': 'Skip(忽略)', + + 'settings.testConnection.name': '测试连接', + 'settings.testConnection.desc': '验证您的 {service} 设置', + 'settings.testConnection.button': '测试连接', + 'settings.testConnection.failed': '连接失败:{reason}', + 'settings.testConnection.failed.unreachable': '无法连接到仓库', + 'settings.testConnection.branchNotFound.badge': '找不到分支「{branch}」', + 'settings.testConnection.branchNotFound.notice': '已连接,但找不到分支「{branch}」。请检查分支设置,或确认仓库中存在此分支。', + 'settings.testConnection.success': '{service} 连接成功!', + + 'settings.token.placeholder': '输入您的令牌', + 'settings.token.show': '显示令牌', + 'settings.token.hide': '隐藏令牌', + + 'settings.gitlab.token.name': 'GitLab 个人访问令牌', + 'settings.gitlab.token.desc': '请在 GitLab 用户设置 > access tokens 创建具有「API」范围的令牌', + 'settings.gitlab.baseUrl.name': 'GitLab 基础 URL', + 'settings.gitlab.baseUrl.desc': '默认为 https://gitlab.com', + 'settings.gitlab.projectId.name': '项目 ID', + 'settings.gitlab.projectId.desc': '可在 GitLab 项目总览中找到', + 'settings.gitlab.projectId.placeholder': '输入数字项目 ID', + + 'settings.gitea.token.name': 'Gitea 个人访问令牌', + 'settings.gitea.token.desc': '请在用户设置 > applications > access tokens 创建令牌', + 'settings.gitea.baseUrl.name': 'Gitea 基础 URL', + 'settings.gitea.baseUrl.desc': '您的 Gitea 实例网址(例如 https://gitea.example.com)', + + 'settings.github.token.name': 'GitHub 个人访问令牌', + 'settings.github.token.desc': '请在 GitHub 设置 > developer settings > personal access tokens 创建具有「repo」范围的令牌', + + 'settings.repoOwner.name': '仓库所有者', + 'settings.repoOwner.desc.gitea': 'Gitea 用户名或组织名称', + 'settings.repoOwner.desc.github': 'GitHub 用户名或组织名称', + 'settings.repoOwner.placeholder': '用户名', + + 'settings.repoName.name': '仓库名称', + 'settings.repoName.desc.gitea': '仓库的名称', + 'settings.repoName.desc.github': 'GitHub 仓库的名称', + 'settings.repoName.placeholder': '我的笔记', + + 'main.ribbon.openSyncStatus': '打开同步状态', + 'main.ribbon.push': '推送', + 'main.ribbon.pushTo': '推送至 {service}', + 'main.command.openSyncStatus': '打开同步状态', + 'main.command.pushCurrentFile': '推送当前文件', + 'main.command.pullCurrentFile': '拉取当前文件', + 'main.command.pushAllFiles': '推送所有文件', + 'main.command.pullAllFiles': '拉取所有文件', + 'main.notice.noActiveNote': '没有可推送的当前笔记', + 'main.contextMenu.pushTo': '推送至 {service}', + 'main.contextMenu.pullFrom': '从 {service} 拉取', + 'main.notice.noFilesToRun': '设置的库文件夹中没有可{op}的文件', + 'main.confirm.pushAll': '要推送 {count} 个文件至 {service} 吗?', + 'main.confirm.pullAll': '要从 {service} 拉取 {count} 个文件吗?这将覆盖本机变更。', + 'main.progress.running': '{verb} 0/{total} 个文件...', + 'main.progress.step': '{verb} {current}/{total}:{fileName}', + 'main.notice.runFailed': '{verb}失败:{message}', + 'main.op.push': '推送', + 'main.op.pull': '拉取', + 'main.verb.pushing': '推送中', + 'main.verb.pulling': '拉取中', + 'main.verb.push': '推送', + 'main.verb.pull': '拉取', + + 'confirmModal.title': '确认', + 'confirmModal.confirm': '确认', + 'confirmModal.cancel': '取消', + + 'whatsNew.title': '新功能', + 'whatsNew.viewChangelog': '查看完整更新日志', + 'whatsNew.gotIt': '知道了', + + 'settings.whatsNewBanner.title': 'v{version} 更新重点', + 'settings.whatsNewBanner.dismiss': '关闭提示', + + 'syncStatus.viewTitle': '同步状态', + 'syncStatus.emptyPrompt': '点击「刷新」以检查同步状态', + 'syncStatus.progress.checkingWithCount': '检查文件中… {current}/{total}({pct}%)', + 'syncStatus.progress.checking': '检查文件中…', + 'syncStatus.lastSync': '上次同步:{time}', + 'syncStatus.tab.all': '全部', + 'syncStatus.tab.synced': '已同步', + 'syncStatus.tab.modified': '有变更', + 'syncStatus.tab.unsynced': '仅本机', + 'syncStatus.tab.remote-only': '远程', + 'syncStatus.noFilesForFilter': '没有{filter}的文件', + 'syncStatus.confirmDeleteLocal': '删除本机文件「{path}」?将依您库的「已删除文件」设置处理。', + 'syncStatus.notice.deleted': '已删除 {path}', + 'syncStatus.notice.deleteFailed': '删除失败:{message}', + 'syncStatus.notice.opFailed': '{verb}失败:{message}', + 'syncStatus.notice.alreadyRefreshing': '正在刷新中…', + 'syncStatus.notice.refreshed': '已检查 {local} 个本机文件与 {remote} 个远程文件', + 'syncStatus.notice.refreshFailed': '刷新失败:{message}', + 'syncStatus.notice.noPushableFiles.selected': '未选取任何可推送的文件。', + 'syncStatus.notice.noPushableFiles.found': '没有可推送的文件。', + 'syncStatus.notice.noPullableFiles.selected': '未选取任何可拉取的文件。', + 'syncStatus.notice.noPullableFiles.found': '没有可拉取的文件。', + 'syncStatus.confirm.pushSelected': '要推送 {count} 个文件至 {service} 吗?', + 'syncStatus.confirm.pullSelected': '要从 {service} 拉取 {count} 个文件吗?这将覆盖本机变更。', + 'syncStatus.notice.opCompleted': '{verb}完成,刷新中…', + 'syncStatus.notice.nothingToDelete': '没有可删除的项目', + 'syncStatus.notice.noFilesSelected': '尚未选取任何文件', + 'syncStatus.confirmDelete.localAndRemote': '要删除 {local} 个本机文件(依库的回收站设置处理)与 {remote} 个远程文件(无法恢复)吗?', + 'syncStatus.confirmDelete.localOnly': '要删除 {local} 个本机文件吗?将依您库的「已删除文件」设置处理。', + 'syncStatus.confirmDelete.remoteOnly': '要删除 {remote} 个远程文件吗?此操作无法恢复。', + 'syncStatus.notice.deleteResult.partial': '已删除 {succeeded}/{total} 个,{failed} 个失败。', + 'syncStatus.notice.deleteResult.partialWithMessage': '已删除 {succeeded}/{total} 个,{failed} 个失败:{message}', + 'syncStatus.notice.deleteResult.success': '已删除 {total} 个文件', + 'syncStatus.progress.deleting': '删除中 0/{total} 个文件…', + 'syncStatus.progress.pushing': '推送中 {current}/{total}:{name}', + 'syncStatus.progress.pulling': '拉取中 {current}/{total}:{name}', + 'syncStatus.progress.deletingLocal': '删除本机 {current}/{total}:{path}', + 'syncStatus.progress.deletingRemote': '删除远程 {current}/{total}:{path}', + + 'actionBar.select': '选取', + 'actionBar.refresh': ' 刷新', + 'actionBar.refreshAll': '刷新所有状态', + 'actionBar.pushCount': ' 推送({count})', + 'actionBar.pushFiles': '推送 {count} 个文件', + 'actionBar.pullCount': ' 拉取({count})', + 'actionBar.pullFiles': '拉取 {count} 个文件', + 'actionBar.deleteCount': ' 删除({count})', + 'actionBar.deleteFiles': '删除 {count} 个文件', + + 'syncStatus.status.checking': '检查中', + + 'fileListItem.action.push': ' 推送', + 'fileListItem.action.pull': ' 拉取', + 'fileListItem.action.remove': ' 移除', + 'fileListItem.action.diff': ' 差异', + 'fileListItem.action.hide': ' 隐藏', + 'fileListItem.tooltip.pushToRemote': '推送至远程', + 'fileListItem.tooltip.pullFromRemote': '从远程拉取', + 'fileListItem.tooltip.deleteLocalFile': '删除本机文件', + 'fileListItem.tooltip.toggleDiff': '切换差异视图', + 'fileListItem.diff.symlinkChanged': '符号链接目标已变更', + 'fileListItem.diff.loading': '加载差异中…', + 'fileListItem.diff.clickToLoad': '点击「差异」以加载…', + 'fileListItem.diff.binaryChanged': '二进制文件已变更', + + 'diffPanel.remote': '远程', + 'diffPanel.local': '本机', + + 'syncConflictModal.title': '{fileName} 发生冲突', + 'syncConflictModal.description': '远程文件内容有所不同。请查看差异并选择要保留的版本。', + 'syncConflictModal.tab.diff': '差异', + 'syncConflictModal.tab.local': '本机', + 'syncConflictModal.tab.remote': '远程', + 'syncConflictModal.localVersion': '本机版本', + 'syncConflictModal.remoteVersion': '远程版本', + 'syncConflictModal.differences': '差异', + 'syncConflictModal.keepLocal': '保留本机', + 'syncConflictModal.keepLocal.tooltip': '以本机内容覆盖远程', + 'syncConflictModal.keepRemote': '保留远程', + 'syncConflictModal.keepRemote.tooltip': '以远程内容覆盖本机', + 'syncConflictModal.cancel': '取消', +}; + +export default zhCn; diff --git a/src/i18n/locales/zh-tw.ts b/src/i18n/locales/zh-tw.ts index a536f94..c0968b2 100644 --- a/src/i18n/locales/zh-tw.ts +++ b/src/i18n/locales/zh-tw.ts @@ -8,6 +8,13 @@ const zhTw: Partial> = { 'settings.connectionStatus.disconnected': '未連線', 'settings.connectionStatus.withDetail': '{label} — {detail}', + 'settings.language.name': '語言', + 'settings.language.desc': '此外掛的介面語言', + 'settings.language.option.system': '系統預設', + 'settings.language.option.en': 'English', + 'settings.language.option.zhTw': '繁體中文', + 'settings.language.option.zhCn': '简体中文', + 'settings.gitService.name': 'Git 服務', 'settings.gitService.desc': '選擇您的 Git 代管服務', @@ -104,6 +111,9 @@ const zhTw: Partial> = { 'whatsNew.viewChangelog': '查看完整更新日誌', 'whatsNew.gotIt': '知道了', + 'settings.whatsNewBanner.title': 'v{version} 更新重點', + 'settings.whatsNewBanner.dismiss': '關閉提示', + 'syncStatus.viewTitle': '同步狀態', 'syncStatus.emptyPrompt': '點擊「重新整理」以檢查同步狀態', 'syncStatus.progress.checkingWithCount': '檢查檔案中… {current}/{total}({pct}%)', diff --git a/src/main.ts b/src/main.ts index 459571d..55d48f7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,7 +13,7 @@ import { ConfirmModal } from './ui/ConfirmModal'; import { WhatsNewModal } from './ui/WhatsNewModal'; import { CHANGELOG, getUnseenReleases } from './changelog'; import { compareVersions } from './utils/version'; -import { t } from './i18n'; +import { t, setLanguageOverride } from './i18n'; export type ConnectionStatusState = 'checking' | 'connected' | 'disconnected'; @@ -422,6 +422,7 @@ export default class GitLabFilesPush extends Plugin { async loadSettings() { this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData() as Partial); + setLanguageOverride(this.settings.language); } async saveSettings() { diff --git a/src/settings.ts b/src/settings.ts index fba6861..1d4370a 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -2,7 +2,8 @@ import {App, PluginSettingTab, Setting, Notice, TextComponent} from 'obsidian'; import GitLabFilesPush, { type ConnectionStatus } from "./main"; import {FolderSuggest} from "./ui/FolderSuggest"; import {RemoteFolderSuggest} from "./ui/RemoteFolderSuggest"; -import { t } from "./i18n"; +import { t, setLanguageOverride, type LanguageSetting } from "./i18n"; +import { CHANGELOG } from "./changelog"; // Minimal shape of Obsidian >= 1.13's SettingDefinitionItem. Declared locally so // the plugin still type-checks against older Obsidian typings (minAppVersion @@ -51,6 +52,10 @@ export interface GitLabFilesPushSettings { ignorePatterns: string; /** Plugin version last seen by this vault, used to show a "what's new" tip after an update. */ lastSeenVersion: string; + /** Version whose "what's new" banner in the settings tab has been dismissed, if any. */ + bannerDismissedVersion: string; + /** UI language. 'system' follows Obsidian's display language, falling back to English if unsupported. */ + language: LanguageSetting; } export function getServiceName(settings: GitLabFilesPushSettings): string { @@ -90,7 +95,9 @@ export const DEFAULT_SETTINGS: GitLabFilesPushSettings = { vaultFolder: '', symlinkHandling: 'real', ignorePatterns: '', - lastSeenVersion: '' + lastSeenVersion: '', + bannerDismissedVersion: '', + language: 'system' } const CONNECTION_TEST_DEBOUNCE_MS = 800; @@ -151,6 +158,41 @@ export class GitLabSyncSettingTab extends PluginSettingTab { // badge element is instead created once per renderSettings pass and // updated in place by setStatusBadge(), driven by the plugin's shared // connection status (see main.ts) so it stays in sync with the status bar. + // Persistent (until dismissed) banner surfacing the current version's notable + // highlights right at the top of the settings tab, so users who dismissed or + // never saw the WhatsNewModal (see main.ts) can still find them. Separate + // from `lastSeenVersion` — that gate controls the once-per-upgrade modal, + // this one just tracks whether the banner itself was dismissed. + private renderWhatsNewBanner(containerEl: HTMLElement): void { + const currentVersion = this.plugin.manifest.version; + if (this.plugin.settings.bannerDismissedVersion === currentVersion) return; + + const release = CHANGELOG.find(r => r.version === currentVersion); + const notableEntries = release?.entries.filter(entry => entry.notable) ?? []; + if (notableEntries.length === 0) return; + + const banner = containerEl.createDiv({ cls: 'gfs-whats-new-banner' }); + const textEl = banner.createDiv({ cls: 'gfs-whats-new-banner-text' }); + textEl.createEl('strong', { text: t('settings.whatsNewBanner.title', { version: currentVersion }) }); + const list = textEl.createEl('ul', { cls: 'gfs-whats-new-banner-list' }); + for (const entry of notableEntries) { + list.createEl('li', { text: entry.text }); + } + + const dismissBtn = banner.createEl('button', { + cls: 'gfs-whats-new-banner-dismiss', + text: '×', + attr: { 'aria-label': t('settings.whatsNewBanner.dismiss') } + }); + dismissBtn.addEventListener('click', () => { + void (async () => { + this.plugin.settings.bannerDismissedVersion = currentVersion; + await this.plugin.saveSettings(); + this.refresh(); + })(); + }); + } + private renderConnectionStatus(containerEl: HTMLElement): void { this.statusBadgeEl = containerEl.createDiv({ cls: 'gfs-connection-status' }); this.unsubscribeConnectionStatus?.(); @@ -188,8 +230,25 @@ export class GitLabSyncSettingTab extends PluginSettingTab { private renderSettings(containerEl: HTMLElement): void { containerEl.empty(); + this.renderWhatsNewBanner(containerEl); this.renderConnectionStatus(containerEl); + new Setting(containerEl) + .setName(t('settings.language.name')) + .setDesc(t('settings.language.desc')) + .addDropdown(dropdown => dropdown + .addOption('system', t('settings.language.option.system')) + .addOption('en', t('settings.language.option.en')) + .addOption('zh-tw', t('settings.language.option.zhTw')) + .addOption('zh-cn', t('settings.language.option.zhCn')) + .setValue(this.plugin.settings.language) + .onChange((value: string) => { + this.plugin.settings.language = value as LanguageSetting; + void this.plugin.saveSettings(); + setLanguageOverride(this.plugin.settings.language); + this.refresh(); + })); + new Setting(containerEl) .setName(t('settings.gitService.name')) .setDesc(t('settings.gitService.desc')) diff --git a/styles.css b/styles.css index 2580410..28599bf 100644 --- a/styles.css +++ b/styles.css @@ -762,3 +762,40 @@ .ssv-whats-new-notable { font-weight: 600; } + +/* ── Settings "what's new" banner ──────────────────────────────── */ +.gfs-whats-new-banner { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; + padding: 10px 12px; + margin-bottom: 12px; + border-radius: 8px; + background: var(--background-secondary); + border: 1px solid var(--background-modifier-border); +} + +.gfs-whats-new-banner-text { + flex: 1; +} + +.gfs-whats-new-banner-list { + margin: 4px 0 0; + padding-left: 20px; +} + +.gfs-whats-new-banner-dismiss { + background: none; + border: none; + box-shadow: none; + cursor: pointer; + color: var(--text-muted); + font-size: 1.1em; + line-height: 1; + padding: 2px 4px; +} + +.gfs-whats-new-banner-dismiss:hover { + color: var(--text-normal); +} diff --git a/tests/logic/sync-manager-mapping.test.ts b/tests/logic/sync-manager-mapping.test.ts index b8fe60b..2f5cc04 100644 --- a/tests/logic/sync-manager-mapping.test.ts +++ b/tests/logic/sync-manager-mapping.test.ts @@ -55,6 +55,8 @@ const mockSettings: GitLabFilesPushSettings = { symlinkHandling: 'real', ignorePatterns: '', lastSeenVersion: '', + bannerDismissedVersion: '', + language: 'system', }; describe('SyncManager Mapping', () => { diff --git a/tests/logic/sync-manager.test.ts b/tests/logic/sync-manager.test.ts index e68fcca..5c9ed26 100644 --- a/tests/logic/sync-manager.test.ts +++ b/tests/logic/sync-manager.test.ts @@ -61,7 +61,9 @@ const mockSettings: GitLabFilesPushSettings = { vaultFolder: '', symlinkHandling: 'real', ignorePatterns: '', - lastSeenVersion: '' + lastSeenVersion: '', + bannerDismissedVersion: '', + language: 'system' }; describe('SyncManager', () => { diff --git a/tests/ui/SettingsConnectionStatus.test.ts b/tests/ui/SettingsConnectionStatus.test.ts index dea319a..6da4ce4 100644 --- a/tests/ui/SettingsConnectionStatus.test.ts +++ b/tests/ui/SettingsConnectionStatus.test.ts @@ -21,6 +21,7 @@ function createPluginStub(testConnection: () => Promise): return { settings: { ...DEFAULT_SETTINGS }, + manifest: { version: '0.0.0-test' }, saveSettings: vi.fn().mockResolvedValue(undefined), initializeGitService: vi.fn(), gitService: { testConnection },