-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
Subzilla Mac Desktop Application - Implementation Specification
Project Overview
We will implement a minimalist Mac desktop application for Subzilla using Electron within our existing monorepo structure. The application mirrors ImageOptim's simplicity: users drag-and-drop subtitle files, see processing status, and get converted UTF-8 files. The app leverages our existing @subzilla/core and @subzilla/types packages directly.
Monorepo Integration
Package Structure
subzilla/
├── packages/
│ ├── cli/ # Existing CLI package
│ ├── core/ # Existing core processing logic
│ ├── types/ # Existing TypeScript definitions
│ └── mac/ # NEW: Mac desktop application
│ ├── src/
│ │ ├── main/ # Main process
│ │ │ ├── index.ts # Electron main entry
│ │ │ ├── ipc.ts # IPC handlers
│ │ │ ├── menu.ts # Native menu bar
│ │ │ ├── preferences.ts # Preferences management
│ │ │ └── updater.ts # Auto-update logic
│ │ ├── renderer/ # Renderer process
│ │ │ ├── index.html # Main window HTML
│ │ │ ├── preferences.html # Preferences window
│ │ │ ├── styles/
│ │ │ │ ├── main.css # Main window styles
│ │ │ │ └── preferences.css # Preferences styles
│ │ │ ├── js/
│ │ │ │ ├── app.js # Main window logic
│ │ │ │ └── preferences.js # Preferences logic
│ │ │ └── components/ # UI components
│ │ └── preload/ # Preload scripts
│ │ └── index.ts # Context bridge
│ ├── assets/ # Icons, images
│ │ ├── icon.icns # App icon
│ │ ├── dmg-background.png
│ │ └── icons/ # Status icons
│ ├── build/ # Build resources
│ │ └── entitlements.mac.plist
│ ├── package.json
│ ├── tsconfig.json
│ ├── electron-builder.yml
│ └── README.md
├── package.json # Workspace root
└── tsconfig.json # TypeScript project references
Package Configuration
packages/mac/package.json
{
"name": "@subzilla/mac",
"version": "1.0.0",
"description": "Mac desktop application for Subzilla subtitle converter",
"main": "dist/main/index.js",
"scripts": {
"build": "tsc && electron-builder",
"dev": "electron .",
"start": "electron dist/main/index.js",
"dist": "electron-builder",
"type-check": "tsc --noEmit",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"format": "prettier --write \"src/**/*.ts\"",
"format:check": "prettier --check \"src/**/*.ts\""
},
"keywords": [
"subtitle",
"converter",
"mac",
"desktop",
"electron"
],
"author": {
"name": "Obada Qawwas",
"email": "info@onyxdev.net",
"url": "https://onyxdev.net"
},
"license": "ISC",
"dependencies": {
"@subzilla/core": "workspace:*",
"@subzilla/types": "workspace:*",
"electron-updater": "^6.1.0"
},
"devDependencies": {
"@types/node": "^24.3.0",
"electron": "^31.0.0",
"electron-builder": "^24.0.0",
"typescript": "^5.9.2"
}
}
User Interface Specification
Main Window Design
State 1: Empty (Initial)
Window: 500x400px (resizable, min: 400x300px)
┌─────────────────────────────────────────┐
│ Subzilla − ○ × │
├─────────────────────────────────────────┤
│ │
│ 🦎 │
│ │
│ Drop subtitle files here │
│ or click to browse │
│ │
│ Supports: .srt .sub .ass .ssa .txt │
│ │
└─────────────────────────────────────────┘
State 2: Processing
┌─────────────────────────────────────────┐
│ Subzilla − ○ × │
├─────────────────────────────────────────┤
│ File Status Original Result │
│ ────────────────────────────────────────────│
│ movie.srt ✅ Win-1256 UTF-8 │
│ episode01.srt ⟳ Detecting... │
│ episode02.srt ⏸ — — │
│ broken.srt ⚠️ Unknown UTF-8* │
│ │
│ ▓▓▓▓▓▓░░░░ 2 of 4 files │
└─────────────────────────────────────────┘
State 3: Completed
┌─────────────────────────────────────────┐
│ Subzilla − ○ × │
├─────────────────────────────────────────┤
│ File Status Original Result │
│ ────────────────────────────────────────────│
│ movie.srt ✅ Win-1256 UTF-8 │
│ episode01.srt ✅ Win-1256 UTF-8 │
│ episode02.srt ✅ ASCII UTF-8 │
│ broken.srt ❌ Error — │
│ │
│ ✓ 3 converted, 1 failed [Clear List]│
└─────────────────────────────────────────┘
Preferences Window
The preferences window uses our existing IConfig interface structure with GUI controls:
General Tab
- Processing behavior (replace/suffix)
- Backup settings matching
IConfig.output - Application settings (sounds, notifications)
Formatting Tab
- All
IStripOptionsfrom@subzilla/types - Quick presets for common combinations
- Visual toggles for each strip option
Output Tab
- File format settings from
IConfig.output - Encoding options (UTF-8 with BOM option)
- Line endings matching our types
Processing Tab
- Parallel processing from
IConfig.batch - Error handling options
- Directory filters
Advanced Tab
- Logging configuration
- Cache management
- Reset functionality
Core Integration
Direct Package Usage
// src/main/processors/subtitle-processor.ts
import { SubtitleProcessor, BatchProcessor, ConfigManager } from '@subzilla/core';
import {
IConfig,
IConvertOptions,
IBatchOptions,
IStripOptions
} from '@subzilla/types';
export class MacSubtitleProcessor {
private processor: SubtitleProcessor;
private batchProcessor: BatchProcessor;
private config: IConfig;
// ...
}IPC Communication
// src/main/ipc.ts
import { ipcMain } from 'electron';
import { MacSubtitleProcessor } from './processors/subtitle-processor';
export function setupIPC(): void {
const processor = new MacSubtitleProcessor();
// ...
}Configuration Mapping
import { IConfig } from '@subzilla/types';
import Store from 'electron-store';
export class ConfigMapper {
private store: Store;
constructor() {
this.store = new Store({
name: 'preferences',
defaults: this.getDefaultConfig()
});
}
// ...
}Menu Bar Implementation
import { Menu, MenuItemConstructorOptions, app } from 'electron';
export function createMenu(): Menu {
const template: MenuItemConstructorOptions[] = [
{
label: 'Subzilla',
submenu: [
{ label: 'About Subzilla', role: 'about' },
{ type: 'separator' },
{ label: 'Preferences...', accelerator: 'Cmd+,', click: openPreferences },
{ type: 'separator' },
{ label: 'Hide Subzilla', role: 'hide' },
{ label: 'Hide Others', role: 'hideOthers' },
{ label: 'Show All', role: 'unhide' },
{ type: 'separator' },
{ label: 'Quit Subzilla', role: 'quit' }
]
},
{
label: 'File',
submenu: [
{ label: 'Open Files...', accelerator: 'Cmd+O', click: openFiles },
{ label: 'Open Recent', submenu: getRecentFiles() },
{ type: 'separator' },
{ label: 'Clear List', accelerator: 'Cmd+Delete', click: clearList },
{ type: 'separator' },
{ label: 'Close Window', role: 'close' }
]
},
// Additional menus...
];
return Menu.buildFromTemplate(template);
}Drag & Drop Implementation
// src/renderer/js/drag-drop.js
class DragDropHandler {
constructor() {
this.dropZone = document.getElementById('drop-zone');
this.fileList = document.getElementById('file-list');
this.setupEventListeners();
}
setupEventListeners() {
// ...
}
isValidSubtitle(filePath) {
// ...
}
}Auto-Update System
import { autoUpdater } from 'electron-updater';
import { dialog, BrowserWindow } from 'electron';
export class AutoUpdater {
constructor(private mainWindow: BrowserWindow) {
this.setupUpdater();
}
// ...
}Platform Integration
macOS-Specific Features
- Dock Integration:
- Progress badge during batch processing
- Bounce on completion
- Recent files in context menu
- Touch Bar Support:
- Play/pause processing
- Quick strip presets
- Progress indicator
- System Integration:
- Services menu: "Convert with Subzilla"
- Quick Actions in Finder
- Notification Center integration
- System sounds for completion
- File System:
- Preserve macOS metadata
- Handle aliases and symlinks
- iCloud Drive support
- Respect file locks
Success Criteria
The application meets requirements when:
- ✅ All CLI features accessible through GUI
- ✅ Processes 100 files in under 10 seconds
- ✅ Native macOS appearance and behavior
- ✅ Zero configuration for basic use
- ✅ Preferences properly persist
- ✅ Drag-drop works flawlessly
- ✅ Auto-update functioning
- ✅ Signed and notarized
- ✅ Memory usage under 150MB
- ✅ Electron framework invisible to user
Key Design Principles
- Simplicity First: Every feature must have zero-configuration default
- Progressive Disclosure: Hide complexity until needed
- Native Feel: Despite Electron, must feel like native Mac app
- Direct Integration: Use @subzilla/core directly, no subprocess
- Consistent Architecture: Follow monorepo patterns and conventions
- Performance: Respect system resources, use workers for heavy processing
- Reliability: Never lose user data, always create backups
Metadata
Metadata
Assignees
Labels
No labels