New Obsidian Plugin: Image Gin Plugin
Bringing professional-grade content management to Obsidian with Obsidian plugins, one at a time. Introducing Image Gin for AI-based image generation with Recraft.ai
Image Gin Obsidian Plugin Initialization
Overview
Implemented the core settings infrastructure for the Image Gin Obsidian plugin, establishing a solid foundation for AI-powered image generation capabilities within Obsidian.
Why Care
Enables secure configuration of Recraft.ai API credentials
Provides a user-friendly interface for managing image generation settings
Sets the stage for future image generation features
Key Features
Settings Management
Added secure storage for Recraft API key and base URL
Implemented settings persistence using Obsidian's plugin API
Created a responsive settings UI with proper input validation
Added styles configuration with JSON editor for customizing image generation

Type Safety
Defined TypeScript interfaces for plugin settings
Added proper type checking for image dimensions and configurations
Implemented default settings with type safety
Developer Experience
Organized code with clear separation of concerns
Added comprehensive error handling
Set up proper module exports and imports
Technical Details
Settings Interface
TYPESCRIPTinterface ImageGinSettings { recraftApiKey: string; recraftBaseUrl: string; imagePromptKey: string; imageSizes: ImageSize[]; defaultBannerSize: ImageSize; defaultPortraitSize: ImageSize; retries: number; rateLimit: number; }Dependencies
Obsidian Plugin API
TypeScript for type safety
Next Steps
Implement image generation functionality
Add image preview capabilities
Create batch processing for multiple images
Add support for custom image styles and presets
Implement API integration with Recraft.ai using configured styles
Architecture
Data Flow
flowchart TD
A[User Action] --> B[Modal Opens]
B --> C[Extract URLs from Frontmatter]
C --> D[Fetch OpenGraph Data]
D --> E[Process and Sanitize Data]
E --> F[Update Frontmatter]
F --> G[Save Note] Core Components
Main Plugin (
main.ts)Modals
CurrentFileModal.ts- Main UI modal for image generation
Services
RecraftImageService.ts- API service handling image generation and file savingImageKitService.ts- API service handling uploading local image files to a remote image service.
Utilities
yamlFrontmatter.ts: Handles YAML frontmatter operations, avoids needing to use standardized YAML libraries which may not use Obsidian nuanced frontmatter standards.fileProcessor.ts: Manages file operationssettings.ts: Manages plugin configuration
Settings
settings.ts- Manages plugin configuration
Implementation Highlights
Image Gin Obsidian Plugin Development
Major Milestones Achieved
API Integration & Custom Styles
✅ Successfully integrated Recraft API with custom branded image styles
✅ Implemented proper
style_idparameter passing fromimageStylesJSONconfiguration✅ Fixed API request format to use
size: "2048x1024"instead of separate width/height parameters✅ Custom style ID (
73a249b2-879e-4240-9973-c6fb1715a882) now properly applied to all generated images
File System & Path Resolution
✅ Implemented absolute path support for image output folders
✅ Added intelligent path detection (absolute vs relative) in
RecraftImageService✅ Fixed symlink path duplication issues by using Node.js
fsoperations for absolute paths✅ Created user-configurable output folder setting in Obsidian settings UI
✅ Default path set to
assets/ImageGinwith support for custom absolute paths like/Users/mpstaton/code/lossless-monorepo/content/visuals/ContentDisplays
Modal UI & User Experience
✅ Simplified
CurrentFileModalimplementation with clean separation of concerns✅ Delegated all image generation logic to
RecraftImageService✅ Added proper error handling with user-friendly notifications
✅ Implemented progress indicators for image generation process
✅ Added frontmatter integration for
image_promptand generated image paths
Settings & Configuration
✅ Added comprehensive settings UI with API key, base URL, and model selection
✅ Implemented configurable image output folder with absolute path support
✅ Added style configuration display showing active custom/preset styles
✅ Created proper settings persistence and loading
Current Development Focus
ImageKit CDN Integration Completed (2025-07-21)
✅ ImageKitService.ts - Complete service for uploading images to ImageKit CDN
✅ ConvertLocalImagesForCurrentFile.ts - New modal for converting local images to CDN URLs
✅ Settings reorganization - Clear separation between Recraft and ImageKit settings
✅ Command registration - Added "Convert Local Images to Remote Images" command
✅ YAML frontmatter fixes - Resolved double-quoting issues in array handling
Size Toggle Investigation (Resolved)
✅ Fixed size selection toggles in modal UI
✅ Resolved
selectedSizesSet state tracking✅ All three sizes (Banner, Portrait, Square) now respect toggle state correctly
✅ Debug logging confirmed proper size filtering logic
Technical Architecture
Core Components
CurrentFileModal.ts- Main UI modal for image generation via Recraft APIConvertLocalImagesForCurrentFile.ts- Modal for converting local images to CDN URLsRecraftImageService.ts- API service handling image generation and file savingImageKitService.ts- CDN service for uploading images to ImageKitsettings.ts- Plugin settings management with organized UI sectionsyamlFrontmatter.ts- Utilities for frontmatter manipulation
Key Features
Dual Modal Workflow: Generate new images OR convert existing local images
Recraft Integration: Custom branded image styles via Recraft API
ImageKit CDN: Upload and host images on ImageKit with WebP conversion
Three image formats: Banner (2048x1024), Portrait (1024x1820), Square (1024x1024)
Flexible storage: Absolute and relative path support for image storage
Smart frontmatter: Integration for prompt and image path tracking
User experience: Progress indicators, error handling, and organized settings
Command palette: Two distinct commands for different workflows
Next Steps
✅ Phase 4 Complete: ImageKit integration and settings UI
🔄 Testing: End-to-end workflow testing (Generate → Upload → Convert)
📝 Documentation: Complete README with usage examples
🚀 Release: Prepare for Obsidian Community Plugin submission
🔧 Optimization: Performance improvements and error handling refinement
Code Samples
ImageKit Service Implementation
// ImageKitService.ts - Core upload functionality
export class ImageKitService {
async uploadFile(
fileBuffer: ArrayBuffer,
fileName: string,
folder?: string,
tags?: string[]
): Promise<ImageKitUploadResult> {
if (!this.settings.imageKit.enabled) {
throw new Error('ImageKit is not enabled in settings');
}
// Manual multipart form data construction for Obsidian compatibility
const boundary = '----formdata-obsidian-' + Math.random().toString(36);
const formData = this.buildMultipartFormData({
file: { buffer: fileBuffer, filename: finalFileName },
folder: uploadFolder,
tags: tags?.join(','),
transformation: this.settings.imageKit.convertToWebp ? 'webp' : undefined
}, boundary);
const response = await requestUrl({
url: this.settings.imageKit.uploadEndpoint,
method: 'POST',
headers: {
'Authorization': `Basic ${btoa(this.settings.imageKit.privateKey + ':')}`,
'Content-Type': `multipart/form-data; boundary=${boundary}`
},
body: formData.buffer
});
return response.json as ImageKitUploadResult;
}
} Convert Local Images Modal
// ConvertLocalImagesForCurrentFile.ts - Main conversion logic
export class ConvertLocalImagesForCurrentFile extends Modal {
private readonly IMAGE_PROPERTIES = [
'banner_image', 'portrait_image', 'square_image',
'og_image', 'featured_image', 'thumbnail'
];
private async handleConvert(): Promise<void> {
const imagekitService = new ImageKitService(this.plugin.settings);
const content = await this.app.vault.read(this.currentFile);
const frontmatter = extractFrontmatter(content) || {};
for (const propertyKey of this.selectedProperties) {
const property = this.imageProperties.find(p => p.key === propertyKey);
if (!property?.isLocalFile) continue;
try {
// Read local file and upload to ImageKit
const localPath = this.resolveLocalPath(property.value);
const fileBuffer = readFileSync(localPath);
const tags = imagekitService.extractTagsFromFrontmatter(frontmatter);
const uploadResult = await imagekitService.uploadFile(
fileBuffer.buffer,
this.generateFileName(property.key, localPath),
undefined, // Use default folder
tags
);
// Update frontmatter with ImageKit URL
frontmatter[property.key] = uploadResult.url;
} catch (error) {
console.error(`Error converting ${property.key}:`, error);
}
}
// Save updated frontmatter
const formattedFrontmatter = formatFrontmatter(frontmatter);
await updateFileFrontmatter(this.currentFile, formattedFrontmatter);
}
} Organized Settings UI
// settings.ts - Reorganized settings with clear sections
display(): void {
const { containerEl } = this;
containerEl.empty();
containerEl.createEl('h1', { text: 'Image Gin Settings' });
// === RECRAFT IMAGE GENERATION SETTINGS ===
containerEl.createEl('h2', { text: '🎨 Recraft Image Generation' });
// Recraft API settings
new Setting(containerEl)
.setName('Recraft API Key')
.setDesc('Your Recraft.ai API key for image generation')
.addText(text => text
.setValue(this.plugin.settings.recraftApiKey)
.onChange(async (value) => {
this.plugin.settings.recraftApiKey = value;
await this.plugin.saveSettings();
}));
// Image size presets and style configurations
this.renderImageSizeSettings(containerEl);
// === IMAGEKIT CDN SETTINGS ===
containerEl.createEl('h2', { text: '☁️ ImageKit CDN Upload & Hosting' });
// ImageKit settings
new Setting(containerEl)
.setName('Enable ImageKit CDN')
.setDesc('Upload generated images to ImageKit CDN for optimized delivery')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.imageKit.enabled)
.onChange(async (value) => {
this.plugin.settings.imageKit.enabled = value;
await this.plugin.saveSettings();
}));
} Usage Examples
Workflow 1: Generate New Images
Open a note with an
image_promptin frontmatterRun command: "Generate Images for Current File"
Select desired image sizes (Banner, Portrait, Square)
Images are generated via Recraft API and saved locally
Frontmatter is updated with image paths
Workflow 2: Convert Local Images to CDN
Open a note with local image paths in frontmatter
Run command: "Convert Local Images to Remote Images"
Select which image properties to convert
Images are uploaded to ImageKit CDN
Frontmatter is updated with ImageKit URLs
Optionally remove local files after successful upload
Configuration
Recraft Setup
Get API key from Recraft.ai
Configure in plugin settings under "🎨 Recraft Image Generation"
Set image output folder and size presets
Customize style configurations via JSON
ImageKit Setup
Get API keys from ImageKit.io
Configure in plugin settings under "☁️ ImageKit CDN Upload & Hosting"
Set upload folder structure and optimization preferences
Enable/disable local file cleanup after upload
Available Settings
interface ImageGinSettings {
recraftApiKey: string;
recraftBaseUrl: string;
imagePromptKey: string;
imageSizes: ImageSize[];
defaultBannerSize: string;
defaultPortraitSize: string;
retries: number;
rateLimit: number;
imageStylesJSON: string; // JSON string of style configurations
} Style Configuration Example
{
"creation_time": "2025-04-15T02:24:01.574783871Z",
"credits": 40,
"id": "<your_style_id>",
"is_private": true,
"style": "digital_illustration"
}