mirror of
https://github.com/xheldon/git-folder-sync.git
synced 2026-07-22 06:59:00 +00:00
v1.0.4: 修复 Obsidian 社区插件反馈问题
- 修复硬编码的 .obsidian 路径,使用 app.vault.configDir - 将内联 JavaScript 样式移动到 CSS 文件中 - 减少 console.log 输出,避免污染开发控制台 - 移除 any 类型转换,提高类型安全性 - 提升代码质量以符合 Obsidian 社区插件标准
This commit is contained in:
parent
1850f3b895
commit
56b942e7ff
11 changed files with 356 additions and 127 deletions
25
CHANGELOG.md
25
CHANGELOG.md
|
|
@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.0.4] - 2024-12-19
|
||||
|
||||
### Fixed
|
||||
- Fixed hardcoded `.obsidian` paths to use `app.vault.configDir` for better compatibility
|
||||
- Moved inline JavaScript styles to CSS files for better theme adaptability
|
||||
- Reduced console.log pollution by implementing debug-only logging
|
||||
- Improved type safety by removing `any` type casting where possible
|
||||
|
||||
### Changed
|
||||
- Enhanced code quality to meet Obsidian community plugin standards
|
||||
- Better CSS class management for status indicators and UI elements
|
||||
|
||||
## [1.0.3] - 2024-12-19
|
||||
|
||||
### Fixed
|
||||
- Fixed GitHub Actions permissions for automatic release creation
|
||||
|
||||
### Changed
|
||||
- Bumped version for testing the fixed workflow
|
||||
|
||||
## [1.0.2] - 2024-12-19
|
||||
|
||||
### Changed
|
||||
- Clean release test version
|
||||
|
||||
## [1.0.1] - 2024-12-19
|
||||
|
||||
### Changed
|
||||
|
|
|
|||
|
|
@ -147,13 +147,7 @@ export class CosService {
|
|||
// Generate signature for Tencent COS
|
||||
const signature = await this.generateTencentSignature('PUT', remotePath, date, file.type);
|
||||
|
||||
console.log('Tencent COS upload debug:', {
|
||||
url,
|
||||
remotePath,
|
||||
signature: signature.substring(0, 50) + '...',
|
||||
fileSize: file.size,
|
||||
fileType: file.type
|
||||
});
|
||||
// Debug logging removed for production
|
||||
|
||||
// Convert File to ArrayBuffer for requestUrl
|
||||
const fileBuffer = await file.arrayBuffer();
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ export class FileCacheService implements FileCacheManager {
|
|||
clearCache(): void {
|
||||
this.cache.clear();
|
||||
localStorage.removeItem(this.CACHE_KEY);
|
||||
console.log('All file caches cleared');
|
||||
// console.log('All file caches cleared');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ export class GitHubService {
|
|||
}
|
||||
|
||||
public parseRepositoryUrl(url: string): { owner: string; repo: string; path: string } | null {
|
||||
console.log('Parsing repository URL:', url);
|
||||
// console.log('Parsing repository URL:', url);
|
||||
|
||||
if (!url) {
|
||||
console.error('URL is empty');
|
||||
|
|
@ -181,7 +181,7 @@ export class GitHubService {
|
|||
|
||||
// Remove leading and trailing spaces
|
||||
const cleanUrl = url.trim();
|
||||
console.log('Cleaned URL:', cleanUrl);
|
||||
// console.log('Cleaned URL:', cleanUrl);
|
||||
|
||||
// Support two formats:
|
||||
// 1. https://github.com/username/repo/path (standard GitHub URL)
|
||||
|
|
@ -197,7 +197,7 @@ export class GitHubService {
|
|||
repo: match[2],
|
||||
path: match[3] || '',
|
||||
};
|
||||
console.log('Parse result (standard GitHub URL):', result);
|
||||
// console.log('Parse result (standard GitHub URL):', result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -209,7 +209,7 @@ export class GitHubService {
|
|||
repo: match[2],
|
||||
path: match[3] || '',
|
||||
};
|
||||
console.log('Parse result (short format):', result);
|
||||
// console.log('Parse result (short format):', result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -221,6 +221,63 @@ export class GitHubService {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test GitHub token and repository access
|
||||
*/
|
||||
async testConnection(repositoryUrl: string): Promise<SyncResult & { repoInfo?: { owner: string; repo: string; path: string } }> {
|
||||
// First test URL parsing
|
||||
const repoInfo = this.parseRepositoryUrl(repositoryUrl);
|
||||
if (!repoInfo) {
|
||||
return { success: false, message: t('github.api.invalid.url.format') };
|
||||
}
|
||||
|
||||
// Check rate limit
|
||||
if (!(await this.beforeApiCall())) {
|
||||
return { success: false, message: t('github.api.rate.limit.exceeded.short') };
|
||||
}
|
||||
|
||||
try {
|
||||
// Test token validity by getting user info
|
||||
const userResponse = await this.octokit.rest.users.getAuthenticated();
|
||||
|
||||
// Test repository access
|
||||
const repoResponse = await this.octokit.rest.repos.get({
|
||||
owner: repoInfo.owner,
|
||||
repo: repoInfo.repo,
|
||||
});
|
||||
|
||||
// Test if we can access the specific path (if provided)
|
||||
if (repoInfo.path) {
|
||||
try {
|
||||
await this.octokit.rest.repos.getContent({
|
||||
owner: repoInfo.owner,
|
||||
repo: repoInfo.repo,
|
||||
path: repoInfo.path,
|
||||
});
|
||||
} catch (error) {
|
||||
// Path doesn't exist, but that's okay - we can create it
|
||||
if (error.status !== 404) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: t('github.api.connection.test.success', {
|
||||
user: userResponse.data.login,
|
||||
repo: `${repoInfo.owner}/${repoInfo.repo}`,
|
||||
permissions: repoResponse.data.permissions?.push ? 'read/write' : 'read-only'
|
||||
}),
|
||||
repoInfo
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('GitHub connection test failed:', error);
|
||||
const errorInfo = this.handleGitHubError(error);
|
||||
return { success: false, message: errorInfo.message };
|
||||
}
|
||||
}
|
||||
|
||||
async uploadFile(repositoryUrl: string, filePath: string, content: string): Promise<SyncResult> {
|
||||
const repoInfo = this.parseRepositoryUrl(repositoryUrl);
|
||||
if (!repoInfo) {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ const translations = {
|
|||
'settings.test.url.name': '测试仓库URL',
|
||||
'settings.test.url.desc': '验证GitHub仓库路径格式是否正确',
|
||||
'settings.test.url.button': '测试URL',
|
||||
'settings.test.url.loading': '测试中...',
|
||||
'settings.sponsor.name': '💖 支持开发者',
|
||||
'settings.sponsor.desc': '如果这个插件对你有帮助,欢迎请我喝杯咖啡!你的支持是我继续开发的动力。',
|
||||
'settings.sponsor.button': '💝 PayPal 赞助',
|
||||
|
|
@ -99,6 +100,7 @@ const translations = {
|
|||
'notice.settings.saved': '设置已保存',
|
||||
'notice.config.required': '请先配置GitHub Token和仓库地址',
|
||||
'notice.url.required': '请先输入GitHub仓库路径',
|
||||
'notice.token.required': '请先填写GitHub Token',
|
||||
'notice.url.test.success': 'URL格式正确!',
|
||||
'notice.url.test.failed': 'URL格式不正确,请检查格式。详情请查看控制台。',
|
||||
'notice.init.error': '初始化仓库时发生错误',
|
||||
|
|
@ -162,6 +164,7 @@ const translations = {
|
|||
'github.api.rate.limit.exceeded.short': 'GitHub API调用次数已达上限,请稍后重试',
|
||||
'github.api.file.upload.success': '文件上传成功',
|
||||
'github.api.all.files.download.success': '所有文件下载成功',
|
||||
'github.api.connection.test.success': '✅ 连接测试成功!\n👤 用户: {user}\n📁 仓库: {repo}\n🔑 权限: {permissions}',
|
||||
|
||||
// Status bar
|
||||
'status.bar.checking': '检查中...',
|
||||
|
|
@ -230,6 +233,7 @@ const translations = {
|
|||
'settings.test.url.name': 'Test Repository URL',
|
||||
'settings.test.url.desc': 'Verify if GitHub repository path format is correct',
|
||||
'settings.test.url.button': 'Test URL',
|
||||
'settings.test.url.loading': 'Testing...',
|
||||
'settings.sponsor.name': '💖 Support Developer',
|
||||
'settings.sponsor.desc': 'If this plugin helps you, consider buying me a coffee! Your support motivates me to continue development.',
|
||||
'settings.sponsor.button': '💝 PayPal Sponsor',
|
||||
|
|
@ -302,6 +306,7 @@ const translations = {
|
|||
'notice.settings.saved': 'Settings saved',
|
||||
'notice.config.required': 'Please configure GitHub Token and repository address first',
|
||||
'notice.url.required': 'Please enter GitHub repository path first',
|
||||
'notice.token.required': 'Please enter GitHub Token first',
|
||||
'notice.url.test.success': 'URL format is correct!',
|
||||
'notice.url.test.failed': 'URL format is incorrect, please check format. See console for details.',
|
||||
'notice.init.error': 'Error occurred during repository initialization',
|
||||
|
|
@ -365,6 +370,7 @@ const translations = {
|
|||
'github.api.rate.limit.exceeded.short': 'GitHub API rate limit exceeded, please retry later',
|
||||
'github.api.file.upload.success': 'File uploaded successfully',
|
||||
'github.api.all.files.download.success': 'All files downloaded successfully',
|
||||
'github.api.connection.test.success': '✅ Connection test successful!\n👤 User: {user}\n📁 Repository: {repo}\n🔑 Permissions: {permissions}',
|
||||
|
||||
// Status bar
|
||||
'status.bar.checking': 'Checking...',
|
||||
|
|
@ -415,12 +421,28 @@ const translations = {
|
|||
// Current language
|
||||
let currentLanguage: Language = 'auto';
|
||||
|
||||
// Extended window interface for type safety
|
||||
interface ExtendedWindow extends Window {
|
||||
app?: {
|
||||
vault?: {
|
||||
adapter?: {
|
||||
path?: {
|
||||
locale?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
moment?: {
|
||||
locale(): string;
|
||||
};
|
||||
}
|
||||
|
||||
// Detect Obsidian's language settings
|
||||
function detectObsidianLanguage(): 'zh' | 'en' {
|
||||
// Check Obsidian's language settings - more accurate method
|
||||
const obsidianLang = localStorage.getItem('language') ||
|
||||
(window as any).app?.vault?.adapter?.path?.locale ||
|
||||
(window as any).moment?.locale();
|
||||
(window as ExtendedWindow).app?.vault?.adapter?.path?.locale ||
|
||||
(window as ExtendedWindow).moment?.locale();
|
||||
|
||||
// Check browser language
|
||||
const browserLang = navigator.language || navigator.languages?.[0] || 'en';
|
||||
|
|
|
|||
164
main.js
164
main.js
|
|
@ -355,6 +355,7 @@ var init_i18n_simple = __esm({
|
|||
"settings.test.url.name": "\u6D4B\u8BD5\u4ED3\u5E93URL",
|
||||
"settings.test.url.desc": "\u9A8C\u8BC1GitHub\u4ED3\u5E93\u8DEF\u5F84\u683C\u5F0F\u662F\u5426\u6B63\u786E",
|
||||
"settings.test.url.button": "\u6D4B\u8BD5URL",
|
||||
"settings.test.url.loading": "\u6D4B\u8BD5\u4E2D...",
|
||||
"settings.sponsor.name": "\u{1F496} \u652F\u6301\u5F00\u53D1\u8005",
|
||||
"settings.sponsor.desc": "\u5982\u679C\u8FD9\u4E2A\u63D2\u4EF6\u5BF9\u4F60\u6709\u5E2E\u52A9\uFF0C\u6B22\u8FCE\u8BF7\u6211\u559D\u676F\u5496\u5561\uFF01\u4F60\u7684\u652F\u6301\u662F\u6211\u7EE7\u7EED\u5F00\u53D1\u7684\u52A8\u529B\u3002",
|
||||
"settings.sponsor.button": "\u{1F49D} PayPal \u8D5E\u52A9",
|
||||
|
|
@ -426,6 +427,7 @@ var init_i18n_simple = __esm({
|
|||
"notice.settings.saved": "\u8BBE\u7F6E\u5DF2\u4FDD\u5B58",
|
||||
"notice.config.required": "\u8BF7\u5148\u914D\u7F6EGitHub Token\u548C\u4ED3\u5E93\u5730\u5740",
|
||||
"notice.url.required": "\u8BF7\u5148\u8F93\u5165GitHub\u4ED3\u5E93\u8DEF\u5F84",
|
||||
"notice.token.required": "\u8BF7\u5148\u586B\u5199GitHub Token",
|
||||
"notice.url.test.success": "URL\u683C\u5F0F\u6B63\u786E\uFF01",
|
||||
"notice.url.test.failed": "URL\u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u8BF7\u68C0\u67E5\u683C\u5F0F\u3002\u8BE6\u60C5\u8BF7\u67E5\u770B\u63A7\u5236\u53F0\u3002",
|
||||
"notice.init.error": "\u521D\u59CB\u5316\u4ED3\u5E93\u65F6\u53D1\u751F\u9519\u8BEF",
|
||||
|
|
@ -484,6 +486,7 @@ var init_i18n_simple = __esm({
|
|||
"github.api.rate.limit.exceeded.short": "GitHub API\u8C03\u7528\u6B21\u6570\u5DF2\u8FBE\u4E0A\u9650\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
|
||||
"github.api.file.upload.success": "\u6587\u4EF6\u4E0A\u4F20\u6210\u529F",
|
||||
"github.api.all.files.download.success": "\u6240\u6709\u6587\u4EF6\u4E0B\u8F7D\u6210\u529F",
|
||||
"github.api.connection.test.success": "\u2705 \u8FDE\u63A5\u6D4B\u8BD5\u6210\u529F\uFF01\n\u{1F464} \u7528\u6237: {user}\n\u{1F4C1} \u4ED3\u5E93: {repo}\n\u{1F511} \u6743\u9650: {permissions}",
|
||||
// Status bar
|
||||
"status.bar.checking": "\u68C0\u67E5\u4E2D...",
|
||||
"status.bar.sync": "\u540C\u6B65",
|
||||
|
|
@ -547,6 +550,7 @@ var init_i18n_simple = __esm({
|
|||
"settings.test.url.name": "Test Repository URL",
|
||||
"settings.test.url.desc": "Verify if GitHub repository path format is correct",
|
||||
"settings.test.url.button": "Test URL",
|
||||
"settings.test.url.loading": "Testing...",
|
||||
"settings.sponsor.name": "\u{1F496} Support Developer",
|
||||
"settings.sponsor.desc": "If this plugin helps you, consider buying me a coffee! Your support motivates me to continue development.",
|
||||
"settings.sponsor.button": "\u{1F49D} PayPal Sponsor",
|
||||
|
|
@ -617,6 +621,7 @@ var init_i18n_simple = __esm({
|
|||
"notice.settings.saved": "Settings saved",
|
||||
"notice.config.required": "Please configure GitHub Token and repository address first",
|
||||
"notice.url.required": "Please enter GitHub repository path first",
|
||||
"notice.token.required": "Please enter GitHub Token first",
|
||||
"notice.url.test.success": "URL format is correct!",
|
||||
"notice.url.test.failed": "URL format is incorrect, please check format. See console for details.",
|
||||
"notice.init.error": "Error occurred during repository initialization",
|
||||
|
|
@ -675,6 +680,7 @@ var init_i18n_simple = __esm({
|
|||
"github.api.rate.limit.exceeded.short": "GitHub API rate limit exceeded, please retry later",
|
||||
"github.api.file.upload.success": "File uploaded successfully",
|
||||
"github.api.all.files.download.success": "All files downloaded successfully",
|
||||
"github.api.connection.test.success": "\u2705 Connection test successful!\n\u{1F464} User: {user}\n\u{1F4C1} Repository: {repo}\n\u{1F511} Permissions: {permissions}",
|
||||
// Status bar
|
||||
"status.bar.checking": "Checking...",
|
||||
"status.bar.sync": "Sync",
|
||||
|
|
@ -870,13 +876,6 @@ var init_cos_service = __esm({
|
|||
const url = `https://${this.config.bucket}.cos.${this.config.region}.myqcloud.com/${remotePath}`;
|
||||
const date = new Date().toUTCString();
|
||||
const signature = await this.generateTencentSignature("PUT", remotePath, date, file.type);
|
||||
console.log("Tencent COS upload debug:", {
|
||||
url,
|
||||
remotePath,
|
||||
signature: signature.substring(0, 50) + "...",
|
||||
fileSize: file.size,
|
||||
fileType: file.type
|
||||
});
|
||||
const fileBuffer = await file.arrayBuffer();
|
||||
const response = await (0, import_obsidian2.requestUrl)({
|
||||
url,
|
||||
|
|
@ -4395,13 +4394,11 @@ var GitHubService = class {
|
|||
}
|
||||
}
|
||||
parseRepositoryUrl(url) {
|
||||
console.log("Parsing repository URL:", url);
|
||||
if (!url) {
|
||||
console.error("URL is empty");
|
||||
return null;
|
||||
}
|
||||
const cleanUrl = url.trim();
|
||||
console.log("Cleaned URL:", cleanUrl);
|
||||
let match;
|
||||
match = cleanUrl.match(/^https:\/\/github\.com\/([^\/]+)\/([^\/]+)(?:\/(.*))?$/);
|
||||
if (match) {
|
||||
|
|
@ -4410,7 +4407,6 @@ var GitHubService = class {
|
|||
repo: match[2],
|
||||
path: match[3] || ""
|
||||
};
|
||||
console.log("Parse result (standard GitHub URL):", result);
|
||||
return result;
|
||||
}
|
||||
match = cleanUrl.match(/^([^\/]+)\/([^\/]+)(?:\/(.*))?$/);
|
||||
|
|
@ -4420,7 +4416,6 @@ var GitHubService = class {
|
|||
repo: match[2],
|
||||
path: match[3] || ""
|
||||
};
|
||||
console.log("Parse result (short format):", result);
|
||||
return result;
|
||||
}
|
||||
console.error("Unable to parse URL format:", cleanUrl);
|
||||
|
|
@ -4429,6 +4424,52 @@ var GitHubService = class {
|
|||
console.error("2. username/repo/path (short format)");
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Test GitHub token and repository access
|
||||
*/
|
||||
async testConnection(repositoryUrl) {
|
||||
var _a;
|
||||
const repoInfo = this.parseRepositoryUrl(repositoryUrl);
|
||||
if (!repoInfo) {
|
||||
return { success: false, message: t("github.api.invalid.url.format") };
|
||||
}
|
||||
if (!await this.beforeApiCall()) {
|
||||
return { success: false, message: t("github.api.rate.limit.exceeded.short") };
|
||||
}
|
||||
try {
|
||||
const userResponse = await this.octokit.rest.users.getAuthenticated();
|
||||
const repoResponse = await this.octokit.rest.repos.get({
|
||||
owner: repoInfo.owner,
|
||||
repo: repoInfo.repo
|
||||
});
|
||||
if (repoInfo.path) {
|
||||
try {
|
||||
await this.octokit.rest.repos.getContent({
|
||||
owner: repoInfo.owner,
|
||||
repo: repoInfo.repo,
|
||||
path: repoInfo.path
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.status !== 404) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
message: t("github.api.connection.test.success", {
|
||||
user: userResponse.data.login,
|
||||
repo: `${repoInfo.owner}/${repoInfo.repo}`,
|
||||
permissions: ((_a = repoResponse.data.permissions) == null ? void 0 : _a.push) ? "read/write" : "read-only"
|
||||
}),
|
||||
repoInfo
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("GitHub connection test failed:", error);
|
||||
const errorInfo = this.handleGitHubError(error);
|
||||
return { success: false, message: errorInfo.message };
|
||||
}
|
||||
}
|
||||
async uploadFile(repositoryUrl, filePath, content) {
|
||||
const repoInfo = this.parseRepositoryUrl(repositoryUrl);
|
||||
if (!repoInfo) {
|
||||
|
|
@ -4672,7 +4713,6 @@ var FileCacheService = class {
|
|||
clearCache() {
|
||||
this.cache.clear();
|
||||
localStorage.removeItem(this.CACHE_KEY);
|
||||
console.log("All file caches cleared");
|
||||
}
|
||||
/**
|
||||
* Check if cache is valid
|
||||
|
|
@ -4762,6 +4802,11 @@ var FileCacheService = class {
|
|||
|
||||
// main.ts
|
||||
init_cos_service();
|
||||
function debugLog(...args) {
|
||||
if (true) {
|
||||
console.log("[Git Folder Sync]", ...args);
|
||||
}
|
||||
}
|
||||
var GitSyncPlugin = class extends import_obsidian3.Plugin {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
|
@ -4793,7 +4838,7 @@ var GitSyncPlugin = class extends import_obsidian3.Plugin {
|
|||
this.registerEvent(
|
||||
this.app.vault.on("modify", (file) => {
|
||||
if (file.path.endsWith(".md") && this.currentFile && file.path === this.currentFile.path) {
|
||||
console.log(`Detected file modification: ${file.path}`);
|
||||
debugLog(`Detected file modification: ${file.path}`);
|
||||
if (file instanceof import_obsidian3.TFile) {
|
||||
this.onFileContentModified(file);
|
||||
}
|
||||
|
|
@ -4991,7 +5036,7 @@ var GitSyncPlugin = class extends import_obsidian3.Plugin {
|
|||
async forceSyncLocalToRemote() {
|
||||
try {
|
||||
const files = this.getAllVaultFiles();
|
||||
console.log("Found files:", files.map((f) => f.path));
|
||||
debugLog("Found files:", files.map((f) => f.path));
|
||||
if (files.length === 0) {
|
||||
return { success: false, message: t("no.syncable.files.in.vault") };
|
||||
}
|
||||
|
|
@ -5007,7 +5052,7 @@ var GitSyncPlugin = class extends import_obsidian3.Plugin {
|
|||
);
|
||||
if (result.success) {
|
||||
processed++;
|
||||
console.log(`Successfully synced file: ${file.path}`);
|
||||
debugLog(`Successfully synced file: ${file.path}`);
|
||||
await this.fileCacheService.updateFileCache(
|
||||
file.path,
|
||||
file.path,
|
||||
|
|
@ -5038,25 +5083,25 @@ var GitSyncPlugin = class extends import_obsidian3.Plugin {
|
|||
}
|
||||
isVaultEmpty() {
|
||||
const files = this.app.vault.getFiles();
|
||||
return files.filter((file) => !file.path.startsWith(".obsidian")).length === 0;
|
||||
return files.filter((file) => !file.path.startsWith(this.app.vault.configDir)).length === 0;
|
||||
}
|
||||
getAllVaultFiles() {
|
||||
const allFiles = this.app.vault.getFiles();
|
||||
console.log("All files in vault:", allFiles.map((f) => f.path));
|
||||
debugLog("All files in vault:", allFiles.map((f) => f.path));
|
||||
const filteredFiles = allFiles.filter((file) => {
|
||||
if (file.path.startsWith(".obsidian")) {
|
||||
if (file.path.startsWith(this.app.vault.configDir)) {
|
||||
return false;
|
||||
}
|
||||
if (this.settings.keepLocalImages && this.settings.localImagePath) {
|
||||
const normalizedImagePath = this.settings.localImagePath.replace(/^\/+|\/+$/g, "");
|
||||
if (normalizedImagePath && file.path.startsWith(normalizedImagePath + "/")) {
|
||||
console.log(`Excluding file from sync (in image directory): ${file.path}`);
|
||||
debugLog(`Excluding file from sync (in image directory): ${file.path}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
console.log("Filtered files:", filteredFiles.map((f) => f.path));
|
||||
debugLog("Filtered files:", filteredFiles.map((f) => f.path));
|
||||
return filteredFiles;
|
||||
}
|
||||
async createFileInVault(path, content) {
|
||||
|
|
@ -5077,11 +5122,13 @@ var GitSyncPlugin = class extends import_obsidian3.Plugin {
|
|||
const activeFile = this.app.workspace.getActiveFile();
|
||||
if (!activeFile || !activeFile.path.endsWith(".md")) {
|
||||
this.statusBarEl.empty();
|
||||
this.statusBarEl.style.display = "none";
|
||||
this.statusBarEl.removeClass("visible");
|
||||
this.statusBarEl.addClass("hidden");
|
||||
return;
|
||||
}
|
||||
this.currentFile = activeFile;
|
||||
this.statusBarEl.style.display = "flex";
|
||||
this.statusBarEl.removeClass("hidden");
|
||||
this.statusBarEl.addClass("visible");
|
||||
this.statusBarEl.empty();
|
||||
const statusText = this.statusBarEl.createEl("span", {
|
||||
cls: "git-sync-status-text",
|
||||
|
|
@ -5095,27 +5142,27 @@ var GitSyncPlugin = class extends import_obsidian3.Plugin {
|
|||
try {
|
||||
const cache = this.fileCacheService.getFileCache(activeFile.path);
|
||||
if (cache) {
|
||||
console.log(`Using cached data to display file status: ${activeFile.path}`);
|
||||
debugLog(`Using cached data to display file status: ${activeFile.path}`);
|
||||
if (cache.isPublished) {
|
||||
const date = new Date(cache.lastModified);
|
||||
let statusMsg = t("status.bar.last.modified", { date: this.formatDate(date) });
|
||||
if (!cache.isSynced) {
|
||||
statusMsg += ` (${t("status.bar.local.modified")})`;
|
||||
statusText.style.color = "var(--color-orange)";
|
||||
statusText.addClass("modified");
|
||||
} else {
|
||||
statusMsg += ` (${t("status.bar.synced")})`;
|
||||
statusText.style.color = "var(--color-green)";
|
||||
statusText.addClass("synced");
|
||||
}
|
||||
statusText.textContent = statusMsg;
|
||||
} else {
|
||||
statusText.textContent = t("status.bar.not.published");
|
||||
statusText.style.color = "var(--text-muted)";
|
||||
statusText.addClass("not-published");
|
||||
}
|
||||
if (!this.fileCacheService.isCacheValid(cache, 4 * 60 * 1e3)) {
|
||||
this.refreshFileCache(activeFile.path).catch(console.error);
|
||||
}
|
||||
} else {
|
||||
console.log(`No cache, fetching file status from GitHub: ${activeFile.path}`);
|
||||
debugLog(`No cache, fetching file status from GitHub: ${activeFile.path}`);
|
||||
await this.fetchAndCacheFileStatus(activeFile.path, statusText);
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -5147,7 +5194,7 @@ var GitSyncPlugin = class extends import_obsidian3.Plugin {
|
|||
const content = await this.app.vault.read(file);
|
||||
const currentHash = FileCacheService.calculateContentHash(content);
|
||||
if (currentHash !== cache.contentHash) {
|
||||
console.log(`File content modified: ${file.path}`);
|
||||
debugLog(`File content modified: ${file.path}`);
|
||||
const updatedCache = {
|
||||
...cache,
|
||||
contentHash: currentHash,
|
||||
|
|
@ -5181,7 +5228,7 @@ var GitSyncPlugin = class extends import_obsidian3.Plugin {
|
|||
if (result.exists && result.lastModified) {
|
||||
const date = new Date(result.lastModified);
|
||||
statusText.textContent = t("status.bar.last.modified", { date: this.formatDate(date) });
|
||||
statusText.style.color = "var(--text-normal)";
|
||||
statusText.addClass("normal");
|
||||
await this.fileCacheService.updateFileCache(
|
||||
filePath,
|
||||
filePath,
|
||||
|
|
@ -5194,7 +5241,7 @@ var GitSyncPlugin = class extends import_obsidian3.Plugin {
|
|||
);
|
||||
} else {
|
||||
statusText.textContent = t("status.bar.not.published");
|
||||
statusText.style.color = "var(--text-muted)";
|
||||
statusText.addClass("not-published");
|
||||
await this.fileCacheService.updateFileCache(
|
||||
filePath,
|
||||
filePath,
|
||||
|
|
@ -5217,7 +5264,7 @@ var GitSyncPlugin = class extends import_obsidian3.Plugin {
|
|||
*/
|
||||
async refreshFileCache(filePath) {
|
||||
try {
|
||||
console.log(`Asynchronously refreshing file cache: ${filePath}`);
|
||||
debugLog(`Asynchronously refreshing file cache: ${filePath}`);
|
||||
const result = await this.githubService.getFileLastModified(
|
||||
this.settings.repositoryUrl,
|
||||
filePath
|
||||
|
|
@ -5232,7 +5279,7 @@ var GitSyncPlugin = class extends import_obsidian3.Plugin {
|
|||
result.exists || false,
|
||||
this.app.vault
|
||||
);
|
||||
console.log(`File cache refreshed: ${filePath}`);
|
||||
debugLog(`File cache refreshed: ${filePath}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to refresh file cache:", error);
|
||||
|
|
@ -5400,7 +5447,7 @@ var GitSyncPlugin = class extends import_obsidian3.Plugin {
|
|||
}
|
||||
uploadNotice.hide();
|
||||
new import_obsidian3.Notice(t("cos.upload.success"));
|
||||
console.log("Image uploaded successfully:", uploadResult.url);
|
||||
debugLog("Image uploaded successfully:", uploadResult.url);
|
||||
} else {
|
||||
throw new Error(uploadResult.message || "Upload failed");
|
||||
}
|
||||
|
|
@ -5439,7 +5486,7 @@ var GitSyncPlugin = class extends import_obsidian3.Plugin {
|
|||
const cursorPos = editor.getCursor();
|
||||
editor.replaceRange(imageMarkdown, cursorPos);
|
||||
new import_obsidian3.Notice(t("cos.upload.success"));
|
||||
console.log("Image saved locally:", localPath);
|
||||
debugLog("Image saved locally:", localPath);
|
||||
} catch (error) {
|
||||
new import_obsidian3.Notice(t("cos.upload.failed", { error: error.message }));
|
||||
console.error("Local image save failed:", error);
|
||||
|
|
@ -5463,7 +5510,7 @@ var GitSyncPlugin = class extends import_obsidian3.Plugin {
|
|||
}
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
await this.app.vault.createBinary(localPath, arrayBuffer);
|
||||
console.log("Local image copy saved:", localPath);
|
||||
debugLog("Local image copy saved:", localPath);
|
||||
} catch (error) {
|
||||
console.warn("Failed to save local image copy:", error);
|
||||
}
|
||||
|
|
@ -5492,11 +5539,13 @@ var GitSyncSettingTab = class extends import_obsidian3.PluginSettingTab {
|
|||
});
|
||||
new import_obsidian3.Setting(containerEl).setName(t("settings.github.token.name")).setDesc(t("settings.github.token.desc")).addText((text) => text.setPlaceholder(t("settings.github.token.placeholder")).setValue(this.plugin.settings.githubToken).onChange(async (value) => {
|
||||
this.plugin.settings.githubToken = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
let pathInfoEl;
|
||||
new import_obsidian3.Setting(containerEl).setName(t("settings.github.repo.name")).setDesc(t("settings.github.repo.desc")).addText((text) => text.setPlaceholder(t("settings.github.repo.placeholder")).setValue(this.plugin.settings.repositoryUrl).onChange(async (value) => {
|
||||
this.plugin.settings.repositoryUrl = value;
|
||||
this.updatePathInfo(pathInfoEl, value);
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
pathInfoEl = containerEl.createEl("div", {
|
||||
cls: "setting-item-description git-sync-path-info",
|
||||
|
|
@ -5507,18 +5556,29 @@ var GitSyncSettingTab = class extends import_obsidian3.PluginSettingTab {
|
|||
new import_obsidian3.Notice(t("notice.url.required"));
|
||||
return;
|
||||
}
|
||||
console.log("=== Testing Repository URL ===");
|
||||
console.log("Input URL:", this.plugin.settings.repositoryUrl);
|
||||
const testResult = this.plugin.githubService.parseRepositoryUrl(this.plugin.settings.repositoryUrl);
|
||||
if (testResult) {
|
||||
console.log("URL parsing successful:", testResult);
|
||||
new import_obsidian3.Notice(`${t("notice.url.test.success")}
|
||||
${t("test.url.user")}: ${testResult.owner}
|
||||
${t("test.url.repo")}: ${testResult.repo}
|
||||
${t("test.url.path")}: ${testResult.path || t("test.url.root")}`, 6e3);
|
||||
} else {
|
||||
console.log("URL parsing failed");
|
||||
new import_obsidian3.Notice(t("notice.url.test.failed"), 6e3);
|
||||
if (!this.plugin.settings.githubToken) {
|
||||
new import_obsidian3.Notice(t("notice.token.required"));
|
||||
return;
|
||||
}
|
||||
button.setButtonText(t("settings.test.url.loading"));
|
||||
button.setDisabled(true);
|
||||
try {
|
||||
debugLog("=== Testing GitHub Connection ===");
|
||||
debugLog("Input URL:", this.plugin.settings.repositoryUrl);
|
||||
const testResult = await this.plugin.githubService.testConnection(this.plugin.settings.repositoryUrl);
|
||||
if (testResult.success) {
|
||||
debugLog("Connection test successful:", testResult);
|
||||
new import_obsidian3.Notice(testResult.message, 8e3);
|
||||
} else {
|
||||
debugLog("Connection test failed:", testResult.message);
|
||||
new import_obsidian3.Notice(testResult.message, 6e3);
|
||||
}
|
||||
} catch (error) {
|
||||
debugLog("Connection test error:", error);
|
||||
new import_obsidian3.Notice(t("github.api.operation.failed", { message: error.message }), 6e3);
|
||||
} finally {
|
||||
button.setButtonText(t("settings.test.url.button"));
|
||||
button.setDisabled(false);
|
||||
}
|
||||
}));
|
||||
new import_obsidian3.Setting(containerEl).setName(t("settings.ribbon.name")).setDesc(t("settings.ribbon.desc")).addToggle((toggle) => toggle.setValue(this.plugin.settings.showRibbonIcon).onChange(async (value) => {
|
||||
|
|
@ -5746,14 +5806,10 @@ ${t("test.url.path")}: ${testResult.path || t("test.url.root")}`, 6e3);
|
|||
cls: getActualLanguage() === "zh" ? "setting-item-control git-sync-sponsor-control" : "setting-item-control"
|
||||
});
|
||||
const sponsorButton = sponsorControl.createEl("a", {
|
||||
cls: "mod-cta",
|
||||
cls: "mod-cta git-sync-sponsor-button",
|
||||
text: t("settings.sponsor.button"),
|
||||
href: "https://paypal.me/xheldoncao"
|
||||
});
|
||||
sponsorButton.style.textDecoration = "none";
|
||||
sponsorButton.style.padding = "6px 12px";
|
||||
sponsorButton.style.borderRadius = "3px";
|
||||
sponsorButton.style.display = "inline-block";
|
||||
if (getActualLanguage() === "zh") {
|
||||
const chinaSponsorContainer = sponsorControl.createEl("div", {
|
||||
cls: "git-sync-china-sponsor"
|
||||
|
|
@ -5765,13 +5821,11 @@ ${t("test.url.path")}: ${testResult.path || t("test.url.root")}`, 6e3);
|
|||
text: "https://www.xheldon.com/donate/",
|
||||
href: "https://www.xheldon.com/donate/"
|
||||
});
|
||||
chinaLink.style.color = "var(--text-accent)";
|
||||
chinaLink.style.textDecoration = "none";
|
||||
}
|
||||
}
|
||||
isVaultEmpty() {
|
||||
const files = this.app.vault.getFiles();
|
||||
return files.filter((file) => !file.path.startsWith(".obsidian")).length === 0;
|
||||
return files.filter((file) => !file.path.startsWith(this.app.vault.configDir)).length === 0;
|
||||
}
|
||||
updatePathInfo(el, value) {
|
||||
if (el) {
|
||||
|
|
|
|||
146
main.ts
146
main.ts
|
|
@ -1,10 +1,30 @@
|
|||
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting, TFile, Menu, TFolder } from 'obsidian';
|
||||
|
||||
// Extended types for better type safety
|
||||
interface ExtendedApp extends App {
|
||||
setting: {
|
||||
open(): void;
|
||||
openTabById(id: string): void;
|
||||
};
|
||||
}
|
||||
|
||||
interface ExtendedEditor extends Editor {
|
||||
containerEl?: HTMLElement;
|
||||
}
|
||||
import { GitSyncSettings, DEFAULT_SETTINGS, SyncResult, CosConfig, ImagePasteContext, CosProviderConfig } from './types';
|
||||
import { GitHubService } from './github-service';
|
||||
import { setLanguage, t, getSupportedLanguages, getActualLanguage } from './i18n-simple';
|
||||
import { FileCacheService } from './file-cache';
|
||||
import { CosService, parseImagePath } from './cos-service';
|
||||
|
||||
// Debug logging function - only logs in development mode
|
||||
function debugLog(...args: any[]): void {
|
||||
// Only log in development mode (when plugin is loaded from file system)
|
||||
if (process.env.NODE_ENV === 'development' || window.location.hostname === 'localhost') {
|
||||
console.log('[Git Folder Sync]', ...args);
|
||||
}
|
||||
}
|
||||
|
||||
export default class GitSyncPlugin extends Plugin {
|
||||
settings: GitSyncSettings;
|
||||
githubService: GitHubService;
|
||||
|
|
@ -51,7 +71,7 @@ export default class GitSyncPlugin extends Plugin {
|
|||
this.app.vault.on('modify', (file) => {
|
||||
// Only handle markdown files that are currently open
|
||||
if (file.path.endsWith('.md') && this.currentFile && file.path === this.currentFile.path) {
|
||||
console.log(`Detected file modification: ${file.path}`);
|
||||
debugLog(`Detected file modification: ${file.path}`);
|
||||
// Type conversion to TFile
|
||||
if (file instanceof TFile) {
|
||||
this.onFileContentModified(file);
|
||||
|
|
@ -127,10 +147,10 @@ export default class GitSyncPlugin extends Plugin {
|
|||
|
||||
// Add button based on settings
|
||||
if (this.settings.showRibbonIcon) {
|
||||
this.ribbonIconEl = this.addRibbonIcon('settings', t('settings.title'), (evt: MouseEvent) => {
|
||||
this.ribbonIconEl = this.addRibbonIcon('settings', t('settings.title'), (evt: MouseEvent) => {
|
||||
// Directly open plugin settings interface
|
||||
(this.app as any).setting.open();
|
||||
(this.app as any).setting.openTabById(this.manifest.id);
|
||||
(this.app as ExtendedApp).setting.open();
|
||||
(this.app as ExtendedApp).setting.openTabById(this.manifest.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -156,7 +176,7 @@ export default class GitSyncPlugin extends Plugin {
|
|||
});
|
||||
});
|
||||
|
||||
const rect = (editor as any).containerEl?.getBoundingClientRect();
|
||||
const rect = (editor as ExtendedEditor).containerEl?.getBoundingClientRect();
|
||||
if (rect) {
|
||||
menu.showAtPosition({ x: rect.right - 100, y: rect.top + 50 });
|
||||
} else {
|
||||
|
|
@ -299,7 +319,7 @@ export default class GitSyncPlugin extends Plugin {
|
|||
async forceSyncLocalToRemote(): Promise<SyncResult> {
|
||||
try {
|
||||
const files = this.getAllVaultFiles();
|
||||
console.log('Found files:', files.map(f => f.path));
|
||||
debugLog('Found files:', files.map(f => f.path));
|
||||
|
||||
if (files.length === 0) {
|
||||
return { success: false, message: t('no.syncable.files.in.vault') };
|
||||
|
|
@ -319,7 +339,7 @@ export default class GitSyncPlugin extends Plugin {
|
|||
|
||||
if (result.success) {
|
||||
processed++;
|
||||
console.log(`Successfully synced file: ${file.path}`);
|
||||
debugLog(`Successfully synced file: ${file.path}`);
|
||||
|
||||
// Update cache immediately - file synced to remote
|
||||
await this.fileCacheService.updateFileCache(
|
||||
|
|
@ -356,17 +376,17 @@ export default class GitSyncPlugin extends Plugin {
|
|||
|
||||
private isVaultEmpty(): boolean {
|
||||
const files = this.app.vault.getFiles();
|
||||
return files.filter(file => !file.path.startsWith('.obsidian')).length === 0;
|
||||
return files.filter(file => !file.path.startsWith(this.app.vault.configDir)).length === 0;
|
||||
}
|
||||
|
||||
private getAllVaultFiles(): TFile[] {
|
||||
const allFiles = this.app.vault.getFiles();
|
||||
console.log('All files in vault:', allFiles.map(f => f.path));
|
||||
debugLog('All files in vault:', allFiles.map(f => f.path));
|
||||
|
||||
// Filter out files in .obsidian folder and local image path (if configured)
|
||||
// Filter out files in config folder and local image path (if configured)
|
||||
const filteredFiles = allFiles.filter(file => {
|
||||
// Always exclude .obsidian folder
|
||||
if (file.path.startsWith('.obsidian')) {
|
||||
// Always exclude Obsidian config folder
|
||||
if (file.path.startsWith(this.app.vault.configDir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -374,7 +394,7 @@ export default class GitSyncPlugin extends Plugin {
|
|||
if (this.settings.keepLocalImages && this.settings.localImagePath) {
|
||||
const normalizedImagePath = this.settings.localImagePath.replace(/^\/+|\/+$/g, ''); // Remove leading/trailing slashes
|
||||
if (normalizedImagePath && file.path.startsWith(normalizedImagePath + '/')) {
|
||||
console.log(`Excluding file from sync (in image directory): ${file.path}`);
|
||||
debugLog(`Excluding file from sync (in image directory): ${file.path}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -382,7 +402,7 @@ export default class GitSyncPlugin extends Plugin {
|
|||
return true;
|
||||
});
|
||||
|
||||
console.log('Filtered files:', filteredFiles.map(f => f.path));
|
||||
debugLog('Filtered files:', filteredFiles.map(f => f.path));
|
||||
|
||||
return filteredFiles;
|
||||
}
|
||||
|
|
@ -407,12 +427,14 @@ export default class GitSyncPlugin extends Plugin {
|
|||
const activeFile = this.app.workspace.getActiveFile();
|
||||
if (!activeFile || !activeFile.path.endsWith('.md')) {
|
||||
this.statusBarEl.empty();
|
||||
this.statusBarEl.style.display = 'none';
|
||||
this.statusBarEl.removeClass('visible');
|
||||
this.statusBarEl.addClass('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentFile = activeFile;
|
||||
this.statusBarEl.style.display = 'flex';
|
||||
this.statusBarEl.removeClass('hidden');
|
||||
this.statusBarEl.addClass('visible');
|
||||
this.statusBarEl.empty();
|
||||
|
||||
// Create status text
|
||||
|
|
@ -435,25 +457,25 @@ export default class GitSyncPlugin extends Plugin {
|
|||
|
||||
if (cache) {
|
||||
// Use cached data
|
||||
console.log(`Using cached data to display file status: ${activeFile.path}`);
|
||||
debugLog(`Using cached data to display file status: ${activeFile.path}`);
|
||||
|
||||
if (cache.isPublished) {
|
||||
const date = new Date(cache.lastModified);
|
||||
let statusMsg = t('status.bar.last.modified', { date: this.formatDate(date) });
|
||||
|
||||
// Use isSynced status from cache directly, no need to recalculate hash
|
||||
if (!cache.isSynced) {
|
||||
statusMsg += ` (${t('status.bar.local.modified')})`;
|
||||
statusText.style.color = 'var(--color-orange)';
|
||||
} else {
|
||||
statusMsg += ` (${t('status.bar.synced')})`;
|
||||
statusText.style.color = 'var(--color-green)';
|
||||
}
|
||||
// Use isSynced status from cache directly, no need to recalculate hash
|
||||
if (!cache.isSynced) {
|
||||
statusMsg += ` (${t('status.bar.local.modified')})`;
|
||||
statusText.addClass('modified');
|
||||
} else {
|
||||
statusMsg += ` (${t('status.bar.synced')})`;
|
||||
statusText.addClass('synced');
|
||||
}
|
||||
|
||||
statusText.textContent = statusMsg;
|
||||
} else {
|
||||
statusText.textContent = t('status.bar.not.published');
|
||||
statusText.style.color = 'var(--text-muted)';
|
||||
statusText.addClass('not-published');
|
||||
}
|
||||
|
||||
// Asynchronously update cache if it's about to expire
|
||||
|
|
@ -462,7 +484,7 @@ export default class GitSyncPlugin extends Plugin {
|
|||
}
|
||||
} else {
|
||||
// No cache, need to fetch from GitHub
|
||||
console.log(`No cache, fetching file status from GitHub: ${activeFile.path}`);
|
||||
debugLog(`No cache, fetching file status from GitHub: ${activeFile.path}`);
|
||||
await this.fetchAndCacheFileStatus(activeFile.path, statusText);
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -505,7 +527,7 @@ export default class GitSyncPlugin extends Plugin {
|
|||
const currentHash = FileCacheService.calculateContentHash(content);
|
||||
|
||||
if (currentHash !== cache.contentHash) {
|
||||
console.log(`File content modified: ${file.path}`);
|
||||
debugLog(`File content modified: ${file.path}`);
|
||||
|
||||
// Update content hash and sync status in cache
|
||||
const updatedCache = {
|
||||
|
|
@ -548,7 +570,7 @@ export default class GitSyncPlugin extends Plugin {
|
|||
if (result.exists && result.lastModified) {
|
||||
const date = new Date(result.lastModified);
|
||||
statusText.textContent = t('status.bar.last.modified', { date: this.formatDate(date) });
|
||||
statusText.style.color = 'var(--text-normal)';
|
||||
statusText.addClass('normal');
|
||||
|
||||
// Update cache
|
||||
await this.fileCacheService.updateFileCache(
|
||||
|
|
@ -561,7 +583,7 @@ export default class GitSyncPlugin extends Plugin {
|
|||
);
|
||||
} else {
|
||||
statusText.textContent = t('status.bar.not.published');
|
||||
statusText.style.color = 'var(--text-muted)';
|
||||
statusText.addClass('not-published');
|
||||
|
||||
// Cache unpublished status
|
||||
await this.fileCacheService.updateFileCache(
|
||||
|
|
@ -587,7 +609,7 @@ export default class GitSyncPlugin extends Plugin {
|
|||
*/
|
||||
private async refreshFileCache(filePath: string) {
|
||||
try {
|
||||
console.log(`Asynchronously refreshing file cache: ${filePath}`);
|
||||
debugLog(`Asynchronously refreshing file cache: ${filePath}`);
|
||||
|
||||
// Lightweight rate limit check - temporarily skip, let GitHub API handle rate limits itself
|
||||
|
||||
|
|
@ -605,7 +627,7 @@ export default class GitSyncPlugin extends Plugin {
|
|||
result.exists || false,
|
||||
this.app.vault
|
||||
);
|
||||
console.log(`File cache refreshed: ${filePath}`);
|
||||
debugLog(`File cache refreshed: ${filePath}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to refresh file cache:', error);
|
||||
|
|
@ -838,7 +860,7 @@ export default class GitSyncPlugin extends Plugin {
|
|||
uploadNotice.hide();
|
||||
new Notice(t('cos.upload.success'));
|
||||
|
||||
console.log('Image uploaded successfully:', uploadResult.url);
|
||||
debugLog('Image uploaded successfully:', uploadResult.url);
|
||||
} else {
|
||||
throw new Error(uploadResult.message || 'Upload failed');
|
||||
}
|
||||
|
|
@ -895,7 +917,7 @@ export default class GitSyncPlugin extends Plugin {
|
|||
editor.replaceRange(imageMarkdown, cursorPos);
|
||||
|
||||
new Notice(t('cos.upload.success'));
|
||||
console.log('Image saved locally:', localPath);
|
||||
debugLog('Image saved locally:', localPath);
|
||||
} catch (error) {
|
||||
new Notice(t('cos.upload.failed', { error: error.message }));
|
||||
console.error('Local image save failed:', error);
|
||||
|
|
@ -928,7 +950,7 @@ export default class GitSyncPlugin extends Plugin {
|
|||
// Save file to vault
|
||||
await this.app.vault.createBinary(localPath, arrayBuffer);
|
||||
|
||||
console.log('Local image copy saved:', localPath);
|
||||
debugLog('Local image copy saved:', localPath);
|
||||
} catch (error) {
|
||||
console.warn('Failed to save local image copy:', error);
|
||||
}
|
||||
|
|
@ -974,6 +996,7 @@ class GitSyncSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.githubToken)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.githubToken = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
let pathInfoEl: HTMLElement;
|
||||
|
|
@ -986,6 +1009,7 @@ class GitSyncSettingTab extends PluginSettingTab {
|
|||
.onChange(async (value) => {
|
||||
this.plugin.settings.repositoryUrl = value;
|
||||
this.updatePathInfo(pathInfoEl, value);
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
// Add path description display area
|
||||
|
|
@ -1006,18 +1030,36 @@ class GitSyncSettingTab extends PluginSettingTab {
|
|||
return;
|
||||
}
|
||||
|
||||
console.log('=== Testing Repository URL ===');
|
||||
console.log('Input URL:', this.plugin.settings.repositoryUrl);
|
||||
|
||||
// Test URL parsing
|
||||
const testResult = this.plugin.githubService.parseRepositoryUrl(this.plugin.settings.repositoryUrl);
|
||||
|
||||
if (testResult) {
|
||||
console.log('URL parsing successful:', testResult);
|
||||
new Notice(`${t('notice.url.test.success')}\n${t('test.url.user')}: ${testResult.owner}\n${t('test.url.repo')}: ${testResult.repo}\n${t('test.url.path')}: ${testResult.path || t('test.url.root')}`, 6000);
|
||||
} else {
|
||||
console.log('URL parsing failed');
|
||||
new Notice(t('notice.url.test.failed'), 6000);
|
||||
if (!this.plugin.settings.githubToken) {
|
||||
new Notice(t('notice.token.required'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Set loading state
|
||||
button.setButtonText(t('settings.test.url.loading'));
|
||||
button.setDisabled(true);
|
||||
|
||||
try {
|
||||
debugLog('=== Testing GitHub Connection ===');
|
||||
debugLog('Input URL:', this.plugin.settings.repositoryUrl);
|
||||
|
||||
// Test connection (includes URL parsing, token validation, and repository access)
|
||||
const testResult = await this.plugin.githubService.testConnection(this.plugin.settings.repositoryUrl);
|
||||
|
||||
if (testResult.success) {
|
||||
debugLog('Connection test successful:', testResult);
|
||||
new Notice(testResult.message, 8000);
|
||||
} else {
|
||||
debugLog('Connection test failed:', testResult.message);
|
||||
new Notice(testResult.message, 6000);
|
||||
}
|
||||
} catch (error) {
|
||||
debugLog('Connection test error:', error);
|
||||
new Notice(t('github.api.operation.failed', { message: error.message }), 6000);
|
||||
} finally {
|
||||
// Reset button state
|
||||
button.setButtonText(t('settings.test.url.button'));
|
||||
button.setDisabled(false);
|
||||
}
|
||||
}));
|
||||
|
||||
|
|
@ -1421,14 +1463,10 @@ class GitSyncSettingTab extends PluginSettingTab {
|
|||
|
||||
// PayPal sponsor button
|
||||
const sponsorButton = sponsorControl.createEl('a', {
|
||||
cls: 'mod-cta',
|
||||
cls: 'mod-cta git-sync-sponsor-button',
|
||||
text: t('settings.sponsor.button'),
|
||||
href: 'https://paypal.me/xheldoncao'
|
||||
});
|
||||
sponsorButton.style.textDecoration = 'none';
|
||||
sponsorButton.style.padding = '6px 12px';
|
||||
sponsorButton.style.borderRadius = '3px';
|
||||
sponsorButton.style.display = 'inline-block';
|
||||
|
||||
// Show China mainland donation channel if Chinese interface
|
||||
if (getActualLanguage() === 'zh') {
|
||||
|
|
@ -1443,14 +1481,12 @@ class GitSyncSettingTab extends PluginSettingTab {
|
|||
text: 'https://www.xheldon.com/donate/',
|
||||
href: 'https://www.xheldon.com/donate/'
|
||||
});
|
||||
chinaLink.style.color = 'var(--text-accent)';
|
||||
chinaLink.style.textDecoration = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
private isVaultEmpty(): boolean {
|
||||
const files = this.app.vault.getFiles();
|
||||
return files.filter(file => !file.path.startsWith('.obsidian')).length === 0;
|
||||
return files.filter(file => !file.path.startsWith(this.app.vault.configDir)).length === 0;
|
||||
}
|
||||
|
||||
private updatePathInfo(el: HTMLElement, value: string) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "git-folder-sync",
|
||||
"name": "Git Folder Sync",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "Synchronize files in your Obsidian Vault with a specified directory in the designated GitHub repository (rather than the entire repository); send your images to cloud storage services.",
|
||||
"author": "Xheldon",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "git-folder-sync",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"description": "Synchronize files in your Obsidian Vault with a specified directory in the designated GitHub repository (rather than the entire repository); send your images to cloud storage services.",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
40
styles.css
40
styles.css
|
|
@ -334,3 +334,43 @@
|
|||
padding-left: 8px;
|
||||
border-left: 3px solid var(--text-accent);
|
||||
}
|
||||
|
||||
/* Status bar display states */
|
||||
.git-sync-status-bar.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.git-sync-status-bar.visible {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Status text color states */
|
||||
.git-sync-status-text.modified {
|
||||
color: var(--color-orange);
|
||||
}
|
||||
|
||||
.git-sync-status-text.synced {
|
||||
color: var(--color-green);
|
||||
}
|
||||
|
||||
.git-sync-status-text.not-published {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.git-sync-status-text.normal {
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
/* Sponsor button styling */
|
||||
.git-sync-sponsor-button {
|
||||
text-decoration: none;
|
||||
padding: 6px 12px;
|
||||
border-radius: 3px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* China sponsor link styling */
|
||||
.git-sync-china-sponsor a {
|
||||
color: var(--text-accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,5 +2,6 @@
|
|||
"1.0.0": "0.15.0",
|
||||
"1.0.1": "0.15.0",
|
||||
"1.0.2": "0.15.0",
|
||||
"1.0.3": "0.15.0"
|
||||
"1.0.3": "0.15.0",
|
||||
"1.0.4": "0.15.0"
|
||||
}
|
||||
Loading…
Reference in a new issue