Skip to content

Subzilla Mac Desktop Application #1

@onyxdevs

Description

@onyxdevs

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 IStripOptions from @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

  1. Dock Integration:
    • Progress badge during batch processing
    • Bounce on completion
    • Recent files in context menu
  2. Touch Bar Support:
    • Play/pause processing
    • Quick strip presets
    • Progress indicator
  3. System Integration:
    • Services menu: "Convert with Subzilla"
    • Quick Actions in Finder
    • Notification Center integration
    • System sounds for completion
  4. File System:
    • Preserve macOS metadata
    • Handle aliases and symlinks
    • iCloud Drive support
    • Respect file locks

Success Criteria

The application meets requirements when:

  1. ✅ All CLI features accessible through GUI
  2. ✅ Processes 100 files in under 10 seconds
  3. ✅ Native macOS appearance and behavior
  4. ✅ Zero configuration for basic use
  5. ✅ Preferences properly persist
  6. ✅ Drag-drop works flawlessly
  7. ✅ Auto-update functioning
  8. ✅ Signed and notarized
  9. ✅ Memory usage under 150MB
  10. ✅ 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
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions