mirror of
https://github.com/xheldon/git-folder-sync.git
synced 2026-07-22 06:59:00 +00:00
[done]cos support
This commit is contained in:
parent
b3ad2efb2b
commit
6698cd79c1
3 changed files with 316 additions and 209 deletions
|
|
@ -50,6 +50,8 @@ const translations = {
|
|||
|
||||
// COS settings
|
||||
'settings.image.section.title': '图片处理',
|
||||
'settings.image.enable.name': '启用图片处理功能',
|
||||
'settings.image.enable.desc': '开启后,粘贴图片时将自动上传到云存储并替换为链接',
|
||||
'settings.cos.provider.section.title': '云服务商设置',
|
||||
'settings.image.upload.section.title': '图片上传设置',
|
||||
'settings.cos.provider.name': '云存储服务商',
|
||||
|
|
@ -191,8 +193,9 @@ const translations = {
|
|||
|
||||
// COS upload messages
|
||||
'cos.upload.success': '图片上传成功',
|
||||
'cos.upload.failed': '图片上传失败:{error}',
|
||||
'cos.upload.failed': '图片上传失败: {error}',
|
||||
'cos.upload.uploading': '正在上传图片...',
|
||||
'cos.upload.no.config': '图片处理功能已启用,但需要配置云存储或本地存储路径',
|
||||
'cos.test.success': 'COS配置测试成功',
|
||||
'cos.test.failed': 'COS配置测试失败:{error}',
|
||||
'cos.error.unsupported.provider': '不支持的云存储服务商:{provider}',
|
||||
|
|
@ -202,7 +205,7 @@ const translations = {
|
|||
'cos.error.aws.upload.failed': 'AWS S3上传失败({status}):{error}',
|
||||
'cos.error.cloudflare.upload.failed': 'Cloudflare R2上传失败({status}):{error}',
|
||||
'cos.error.config.invalid': 'COS配置不完整,请检查配置项',
|
||||
'cos.paste.placeholder': '',
|
||||
'cos.paste.placeholder': '正在上传 {filename}...',
|
||||
},
|
||||
en: {
|
||||
// Settings interface
|
||||
|
|
@ -250,6 +253,8 @@ const translations = {
|
|||
|
||||
// COS settings
|
||||
'settings.image.section.title': 'Image Processing',
|
||||
'settings.image.enable.name': 'Enable Image Processing',
|
||||
'settings.image.enable.desc': 'When enabled, images pasted will be automatically uploaded to cloud storage and replaced with links',
|
||||
'settings.cos.provider.section.title': 'Cloud Provider Settings',
|
||||
'settings.image.upload.section.title': 'Image Upload Settings',
|
||||
'settings.cos.provider.name': 'Cloud Storage Provider',
|
||||
|
|
@ -393,6 +398,7 @@ const translations = {
|
|||
'cos.upload.success': 'Image uploaded successfully',
|
||||
'cos.upload.failed': 'Image upload failed: {error}',
|
||||
'cos.upload.uploading': 'Uploading image...',
|
||||
'cos.upload.no.config': 'Image processing is enabled, but cloud storage or local storage path needs to be configured',
|
||||
'cos.test.success': 'COS configuration test successful',
|
||||
'cos.test.failed': 'COS configuration test failed: {error}',
|
||||
'cos.error.unsupported.provider': 'Unsupported cloud storage provider: {provider}',
|
||||
|
|
@ -402,7 +408,7 @@ const translations = {
|
|||
'cos.error.aws.upload.failed': 'AWS S3 upload failed ({status}): {error}',
|
||||
'cos.error.cloudflare.upload.failed': 'Cloudflare R2 upload failed ({status}): {error}',
|
||||
'cos.error.config.invalid': 'COS configuration is incomplete, please check settings',
|
||||
'cos.paste.placeholder': '',
|
||||
'cos.paste.placeholder': 'Uploading {filename}...',
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
507
main.ts
507
main.ts
|
|
@ -13,6 +13,7 @@ export default class GitSyncPlugin extends Plugin {
|
|||
statusBarEl: HTMLElement | null = null;
|
||||
currentFile: TFile | null = null;
|
||||
private fileModifyTimeout: NodeJS.Timeout | null = null;
|
||||
private isProcessingImagePaste: boolean = false;
|
||||
|
||||
async onload() {
|
||||
await this.loadSettings();
|
||||
|
|
@ -60,7 +61,11 @@ export default class GitSyncPlugin extends Plugin {
|
|||
);
|
||||
|
||||
// Listen for paste events to handle image uploads
|
||||
this.registerDomEvent(document, 'paste', this.handlePasteEvent.bind(this));
|
||||
// Use capture phase to intercept events before Obsidian's handlers
|
||||
this.registerDomEvent(document, 'paste', this.handlePasteEvent.bind(this), { capture: true });
|
||||
|
||||
// Also listen on the workspace container to catch events that might bypass document listener
|
||||
this.registerDomEvent(document.body, 'paste', this.handlePasteEvent.bind(this), { capture: true });
|
||||
|
||||
// Add sync button to note editing interface
|
||||
this.addCommand({
|
||||
|
|
@ -671,30 +676,58 @@ export default class GitSyncPlugin extends Plugin {
|
|||
return;
|
||||
}
|
||||
|
||||
// Check if COS is configured
|
||||
if (!this.isCosConfigured()) {
|
||||
// Check if there are any image files in clipboard
|
||||
const imageFiles: File[] = [];
|
||||
for (let i = 0; i < clipboardData.files.length; i++) {
|
||||
const file = clipboardData.files[i];
|
||||
if (file.type.startsWith('image/')) {
|
||||
imageFiles.push(file);
|
||||
}
|
||||
}
|
||||
|
||||
// If no image files, let Obsidian handle it normally
|
||||
if (imageFiles.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Process each file in clipboard
|
||||
for (let i = 0; i < clipboardData.files.length; i++) {
|
||||
const file = clipboardData.files[i];
|
||||
|
||||
// Only handle image files
|
||||
if (!file.type.startsWith('image/')) {
|
||||
continue;
|
||||
}
|
||||
// If image processing is disabled, let Obsidian handle it normally
|
||||
if (!this.settings.enableImageProcessing) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent default paste behavior for images
|
||||
event.preventDefault();
|
||||
// If image processing is enabled, we take control immediately
|
||||
// Prevent default paste behavior as early as possible
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
// Set flag to indicate image paste is being processed
|
||||
this.isProcessingImagePaste = true;
|
||||
|
||||
// Check if COS is configured for upload
|
||||
const cosConfigured = this.isCosConfigured();
|
||||
|
||||
// Process each image file
|
||||
for (const file of imageFiles) {
|
||||
try {
|
||||
await this.handleImageUpload(file, activeView.editor);
|
||||
if (cosConfigured) {
|
||||
// Upload to COS and insert CDN link
|
||||
await this.handleImageUpload(file, activeView.editor);
|
||||
} else if (this.settings.keepLocalImages && this.settings.localImagePath) {
|
||||
// Only save locally and insert local link
|
||||
await this.handleLocalImageOnly(file, activeView.editor);
|
||||
} else {
|
||||
// Show notice that configuration is needed
|
||||
new Notice(t('cos.upload.no.config'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error handling image upload:', error);
|
||||
console.error('Error handling image:', error);
|
||||
new Notice(t('cos.upload.failed', { error: error.message }));
|
||||
}
|
||||
}
|
||||
|
||||
// Reset flag after processing all images
|
||||
this.isProcessingImagePaste = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -820,6 +853,52 @@ export default class GitSyncPlugin extends Plugin {
|
|||
new Notice(t('cos.upload.failed', { error: error.message }));
|
||||
|
||||
console.error('Image upload failed:', error);
|
||||
throw error; // Re-throw to be handled by the caller
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle local image storage only (when COS is not configured)
|
||||
*/
|
||||
private async handleLocalImageOnly(file: File, editor: Editor): Promise<void> {
|
||||
// Generate unique filename
|
||||
const timestamp = Date.now();
|
||||
const extension = file.name.split('.').pop() || 'png';
|
||||
const fileName = `image-${timestamp}.${extension}`;
|
||||
|
||||
try {
|
||||
// Save image locally using the configured local path
|
||||
const localPath = parseImagePath(this.settings.localImagePath, {
|
||||
currentFilePath: this.currentFile!.path,
|
||||
fileName: fileName
|
||||
});
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
const dirPath = localPath.substring(0, localPath.lastIndexOf('/'));
|
||||
if (dirPath) {
|
||||
try {
|
||||
await this.app.vault.createFolder(dirPath);
|
||||
} catch (error) {
|
||||
// Folder might already exist, ignore error
|
||||
}
|
||||
}
|
||||
|
||||
// Convert File to ArrayBuffer
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
|
||||
// Save file to vault
|
||||
await this.app.vault.createBinary(localPath, arrayBuffer);
|
||||
|
||||
// Insert image link at cursor position
|
||||
const imageMarkdown = ``;
|
||||
const cursorPos = editor.getCursor();
|
||||
editor.replaceRange(imageMarkdown, cursorPos);
|
||||
|
||||
new Notice(t('cos.upload.success'));
|
||||
console.log('Image saved locally:', localPath);
|
||||
} catch (error) {
|
||||
new Notice(t('cos.upload.failed', { error: error.message }));
|
||||
console.error('Local image save failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -959,225 +1038,241 @@ class GitSyncSettingTab extends PluginSettingTab {
|
|||
cls: 'git-sync-section-title'
|
||||
});
|
||||
|
||||
// Cloud provider settings subsection
|
||||
containerEl.createEl('h3', {
|
||||
text: t('settings.cos.provider.section.title'),
|
||||
cls: 'git-sync-subsection-title'
|
||||
});
|
||||
|
||||
// Cloud storage provider
|
||||
// Enable image processing toggle
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.provider.name'))
|
||||
.setDesc(t('settings.cos.provider.desc'))
|
||||
.addDropdown(dropdown => {
|
||||
dropdown.addOption('aliyun', t('settings.cos.provider.aliyun'));
|
||||
dropdown.addOption('tencent', t('settings.cos.provider.tencent'));
|
||||
dropdown.addOption('aws', t('settings.cos.provider.aws'));
|
||||
dropdown.addOption('cloudflare', t('settings.cos.provider.cloudflare'));
|
||||
|
||||
dropdown.setValue(this.plugin.settings.cosProvider);
|
||||
dropdown.onChange(async (value) => {
|
||||
this.plugin.settings.cosProvider = value as 'aliyun' | 'tencent' | 'aws' | 'cloudflare';
|
||||
.setName(t('settings.image.enable.name'))
|
||||
.setDesc(t('settings.image.enable.desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableImageProcessing)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.enableImageProcessing = value;
|
||||
await this.plugin.saveSettings();
|
||||
// Re-render settings to show/hide region field and load saved config
|
||||
// Re-render to show/hide image processing settings
|
||||
this.display();
|
||||
});
|
||||
}));
|
||||
|
||||
// Only show image processing settings if enabled
|
||||
if (this.plugin.settings.enableImageProcessing) {
|
||||
// Cloud provider settings subsection
|
||||
containerEl.createEl('h3', {
|
||||
text: t('settings.cos.provider.section.title'),
|
||||
cls: 'git-sync-subsection-title'
|
||||
});
|
||||
|
||||
// Get current config for the selected provider
|
||||
const currentConfig = this.plugin.getCurrentCosConfig();
|
||||
|
||||
// Secret ID
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.secret.id.name') + ' *')
|
||||
.setDesc(t('settings.cos.secret.id.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder(t('settings.cos.secret.id.placeholder'))
|
||||
.setValue(currentConfig.secretId)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.updateCurrentCosConfig({ secretId: value });
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
// Secret Key
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.secret.key.name') + ' *')
|
||||
.setDesc(t('settings.cos.secret.key.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder(t('settings.cos.secret.key.placeholder'))
|
||||
.setValue(currentConfig.secretKey)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.updateCurrentCosConfig({ secretKey: value });
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
// Bucket
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.bucket.name') + ' *')
|
||||
.setDesc(t('settings.cos.bucket.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder(t('settings.cos.bucket.placeholder'))
|
||||
.setValue(currentConfig.bucket)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.updateCurrentCosConfig({ bucket: value });
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
// Region (required for all providers)
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.region.name') + ' *')
|
||||
.setDesc(t('settings.cos.region.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder(t('settings.cos.region.placeholder'))
|
||||
.setValue(currentConfig.region)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.updateCurrentCosConfig({ region: value });
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
// Endpoint (only for Cloudflare R2)
|
||||
if (this.plugin.settings.cosProvider === 'cloudflare') {
|
||||
// Cloud storage provider
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.endpoint.name') + ' *')
|
||||
.setDesc(t('settings.cos.endpoint.desc'))
|
||||
.setName(t('settings.cos.provider.name'))
|
||||
.setDesc(t('settings.cos.provider.desc'))
|
||||
.addDropdown(dropdown => {
|
||||
dropdown.addOption('aliyun', t('settings.cos.provider.aliyun'));
|
||||
dropdown.addOption('tencent', t('settings.cos.provider.tencent'));
|
||||
dropdown.addOption('aws', t('settings.cos.provider.aws'));
|
||||
dropdown.addOption('cloudflare', t('settings.cos.provider.cloudflare'));
|
||||
|
||||
dropdown.setValue(this.plugin.settings.cosProvider);
|
||||
dropdown.onChange(async (value) => {
|
||||
this.plugin.settings.cosProvider = value as 'aliyun' | 'tencent' | 'aws' | 'cloudflare';
|
||||
await this.plugin.saveSettings();
|
||||
// Re-render settings to show/hide region field and load saved config
|
||||
this.display();
|
||||
});
|
||||
});
|
||||
|
||||
// Get current config for the selected provider
|
||||
const currentConfig = this.plugin.getCurrentCosConfig();
|
||||
|
||||
// Secret ID
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.secret.id.name') + ' *')
|
||||
.setDesc(t('settings.cos.secret.id.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder(t('settings.cos.endpoint.placeholder'))
|
||||
.setValue(currentConfig.endpoint)
|
||||
.setPlaceholder(t('settings.cos.secret.id.placeholder'))
|
||||
.setValue(currentConfig.secretId)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.updateCurrentCosConfig({ endpoint: value });
|
||||
this.plugin.updateCurrentCosConfig({ secretId: value });
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
}
|
||||
|
||||
// CDN URL
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.cdn.name'))
|
||||
.setDesc(t('settings.cos.cdn.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder(t('settings.cos.cdn.placeholder'))
|
||||
.setValue(currentConfig.cdnUrl)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.updateCurrentCosConfig({ cdnUrl: value });
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
// Secret Key
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.secret.key.name') + ' *')
|
||||
.setDesc(t('settings.cos.secret.key.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder(t('settings.cos.secret.key.placeholder'))
|
||||
.setValue(currentConfig.secretKey)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.updateCurrentCosConfig({ secretKey: value });
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
// Clear current configuration button
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.clear.name'))
|
||||
.setDesc(t('settings.cos.clear.desc'))
|
||||
.addButton(button => button
|
||||
.setButtonText(t('settings.cos.clear.button'))
|
||||
.setWarning()
|
||||
.onClick(async () => {
|
||||
this.plugin.clearCurrentCosConfig();
|
||||
await this.plugin.saveSettings();
|
||||
new Notice(t('settings.cos.clear.success'));
|
||||
// Re-render to show cleared fields
|
||||
this.display();
|
||||
}));
|
||||
// Bucket
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.bucket.name') + ' *')
|
||||
.setDesc(t('settings.cos.bucket.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder(t('settings.cos.bucket.placeholder'))
|
||||
.setValue(currentConfig.bucket)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.updateCurrentCosConfig({ bucket: value });
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
// Test COS configuration
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.test.name'))
|
||||
.setDesc(t('settings.cos.test.desc'))
|
||||
.addButton(button => button
|
||||
.setButtonText(t('settings.cos.test.button'))
|
||||
.onClick(async () => {
|
||||
// Validate required fields based on provider
|
||||
const requiredFields = [
|
||||
currentConfig.secretId,
|
||||
currentConfig.secretKey,
|
||||
currentConfig.bucket,
|
||||
currentConfig.region // Region is required for all providers
|
||||
];
|
||||
// Region (required for all providers)
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.region.name') + ' *')
|
||||
.setDesc(t('settings.cos.region.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder(t('settings.cos.region.placeholder'))
|
||||
.setValue(currentConfig.region)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.updateCurrentCosConfig({ region: value });
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
// Add provider-specific required fields
|
||||
if (this.plugin.settings.cosProvider === 'cloudflare') {
|
||||
requiredFields.push(currentConfig.endpoint); // Endpoint only required for Cloudflare R2
|
||||
}
|
||||
// Endpoint (only for Cloudflare R2)
|
||||
if (this.plugin.settings.cosProvider === 'cloudflare') {
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.endpoint.name') + ' *')
|
||||
.setDesc(t('settings.cos.endpoint.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder(t('settings.cos.endpoint.placeholder'))
|
||||
.setValue(currentConfig.endpoint)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.updateCurrentCosConfig({ endpoint: value });
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
}
|
||||
|
||||
if (requiredFields.some(field => !field.trim())) {
|
||||
new Notice(t('cos.error.config.invalid'));
|
||||
return;
|
||||
}
|
||||
// CDN URL
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.cdn.name'))
|
||||
.setDesc(t('settings.cos.cdn.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder(t('settings.cos.cdn.placeholder'))
|
||||
.setValue(currentConfig.cdnUrl)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.updateCurrentCosConfig({ cdnUrl: value });
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
// Set loading state
|
||||
button.setButtonText(t('settings.cos.test.loading'));
|
||||
button.setDisabled(true);
|
||||
// Clear current configuration button
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.clear.name'))
|
||||
.setDesc(t('settings.cos.clear.desc'))
|
||||
.addButton(button => button
|
||||
.setButtonText(t('settings.cos.clear.button'))
|
||||
.setWarning()
|
||||
.onClick(async () => {
|
||||
this.plugin.clearCurrentCosConfig();
|
||||
await this.plugin.saveSettings();
|
||||
new Notice(t('settings.cos.clear.success'));
|
||||
// Re-render to show cleared fields
|
||||
this.display();
|
||||
}));
|
||||
|
||||
try {
|
||||
const cosConfig = {
|
||||
provider: this.plugin.settings.cosProvider,
|
||||
secretId: currentConfig.secretId,
|
||||
secretKey: currentConfig.secretKey,
|
||||
bucket: currentConfig.bucket,
|
||||
endpoint: currentConfig.endpoint,
|
||||
cdnUrl: currentConfig.cdnUrl,
|
||||
region: currentConfig.region
|
||||
};
|
||||
// Test COS configuration
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.test.name'))
|
||||
.setDesc(t('settings.cos.test.desc'))
|
||||
.addButton(button => button
|
||||
.setButtonText(t('settings.cos.test.button'))
|
||||
.onClick(async () => {
|
||||
// Validate required fields based on provider
|
||||
const requiredFields = [
|
||||
currentConfig.secretId,
|
||||
currentConfig.secretKey,
|
||||
currentConfig.bucket,
|
||||
currentConfig.region // Region is required for all providers
|
||||
];
|
||||
|
||||
const cosService = new (await import('./cos-service')).CosService(cosConfig);
|
||||
const result = await cosService.testConnection();
|
||||
|
||||
if (result.success) {
|
||||
new Notice(t('cos.test.success'));
|
||||
} else {
|
||||
new Notice(t('cos.test.failed', { error: result.message }));
|
||||
// Add provider-specific required fields
|
||||
if (this.plugin.settings.cosProvider === 'cloudflare') {
|
||||
requiredFields.push(currentConfig.endpoint); // Endpoint only required for Cloudflare R2
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('COS test error:', error);
|
||||
new Notice(t('cos.test.failed', { error: error.message }));
|
||||
} finally {
|
||||
button.setButtonText(t('settings.cos.test.button'));
|
||||
button.setDisabled(false);
|
||||
}
|
||||
}));
|
||||
|
||||
// Image upload settings subsection
|
||||
containerEl.createEl('h3', {
|
||||
text: t('settings.image.upload.section.title'),
|
||||
cls: 'git-sync-subsection-title'
|
||||
});
|
||||
if (requiredFields.some(field => !field.trim())) {
|
||||
new Notice(t('cos.error.config.invalid'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep images locally
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.keep.local.name'))
|
||||
.setDesc(t('settings.cos.keep.local.desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.keepLocalImages)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.keepLocalImages = value;
|
||||
await this.plugin.saveSettings();
|
||||
// Re-render to show/hide local path setting
|
||||
this.display();
|
||||
}));
|
||||
// Set loading state
|
||||
button.setButtonText(t('settings.cos.test.loading'));
|
||||
button.setDisabled(true);
|
||||
|
||||
// Local storage path (only when keepLocalImages is true)
|
||||
if (this.plugin.settings.keepLocalImages) {
|
||||
try {
|
||||
const cosConfig = {
|
||||
provider: this.plugin.settings.cosProvider,
|
||||
secretId: currentConfig.secretId,
|
||||
secretKey: currentConfig.secretKey,
|
||||
bucket: currentConfig.bucket,
|
||||
endpoint: currentConfig.endpoint,
|
||||
cdnUrl: currentConfig.cdnUrl,
|
||||
region: currentConfig.region
|
||||
};
|
||||
|
||||
const cosService = new (await import('./cos-service')).CosService(cosConfig);
|
||||
const result = await cosService.testConnection();
|
||||
|
||||
if (result.success) {
|
||||
new Notice(t('cos.test.success'));
|
||||
} else {
|
||||
new Notice(t('cos.test.failed', { error: result.message }));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('COS test error:', error);
|
||||
new Notice(t('cos.test.failed', { error: error.message }));
|
||||
} finally {
|
||||
button.setButtonText(t('settings.cos.test.button'));
|
||||
button.setDisabled(false);
|
||||
}
|
||||
}));
|
||||
|
||||
// Image upload settings subsection
|
||||
containerEl.createEl('h3', {
|
||||
text: t('settings.image.upload.section.title'),
|
||||
cls: 'git-sync-subsection-title'
|
||||
});
|
||||
|
||||
// Keep images locally
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.local.path.name'))
|
||||
.setDesc(t('settings.cos.local.path.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder(t('settings.cos.local.path.placeholder'))
|
||||
.setValue(this.plugin.settings.localImagePath)
|
||||
.setName(t('settings.cos.keep.local.name'))
|
||||
.setDesc(t('settings.cos.keep.local.desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.keepLocalImages)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.localImagePath = value;
|
||||
this.plugin.settings.keepLocalImages = value;
|
||||
await this.plugin.saveSettings();
|
||||
// Re-render to show/hide local path setting
|
||||
this.display();
|
||||
}));
|
||||
|
||||
// Local storage path (only when keepLocalImages is true)
|
||||
if (this.plugin.settings.keepLocalImages) {
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.local.path.name'))
|
||||
.setDesc(t('settings.cos.local.path.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder(t('settings.cos.local.path.placeholder'))
|
||||
.setValue(this.plugin.settings.localImagePath)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.localImagePath = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
}
|
||||
|
||||
// Upload path template
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.upload.path.name'))
|
||||
.setDesc(t('settings.cos.upload.path.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder(t('settings.cos.upload.path.placeholder'))
|
||||
.setValue(this.plugin.settings.imageUploadPath)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.imageUploadPath = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
}
|
||||
|
||||
// Upload path template
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.cos.upload.path.name'))
|
||||
.setDesc(t('settings.cos.upload.path.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder(t('settings.cos.upload.path.placeholder'))
|
||||
.setValue(this.plugin.settings.imageUploadPath)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.imageUploadPath = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
|
||||
|
||||
// Danger zone title
|
||||
|
|
|
|||
6
types.ts
6
types.ts
|
|
@ -14,6 +14,9 @@ export interface GitSyncSettings {
|
|||
showRibbonIcon: boolean; // Whether to show sidebar button
|
||||
language: 'zh' | 'en' | 'auto'; // Language setting, auto means follow system
|
||||
|
||||
// Image processing settings
|
||||
enableImageProcessing: boolean; // Whether to enable image processing and upload functionality
|
||||
|
||||
// COS (Cloud Object Storage) settings
|
||||
cosProvider: 'aliyun' | 'tencent' | 'aws' | 'cloudflare'; // Current selected provider
|
||||
cosConfigs: {
|
||||
|
|
@ -38,6 +41,9 @@ export const DEFAULT_SETTINGS: GitSyncSettings = {
|
|||
showRibbonIcon: true,
|
||||
language: 'auto', // Follow Obsidian language setting
|
||||
|
||||
// Image processing defaults
|
||||
enableImageProcessing: false,
|
||||
|
||||
// COS default settings
|
||||
cosProvider: 'aliyun',
|
||||
cosConfigs: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue