# EditorViewModel in Palmier Pro: The Central Hub for Managing Editor State

> Discover how Palmier Pro's EditorViewModel synchronizes UI and project data, serving as the single source of truth for editor state and timeline information.

- Repository: [Palmier/palmier-pro](https://github.com/palmier-io/palmier-pro)
- Tags: internals
- Published: 2026-06-23

---

**The `EditorViewModel` is the central, observable data hub that keeps the entire editor UI in sync with the underlying project model, acting as the single source of truth for both persisted timeline data and transient interface state.**

The `EditorViewModel` class in the Palmier Pro video editor serves as the backbone of the application's state management layer. Built with SwiftUI's `@Observable` macro and running on the `@MainActor`, this view model ensures instantaneous UI updates while coordinating between the timeline, media library, and generation services. Understanding how `EditorViewModel` manages editor state is essential for developers extending Palmier Pro's functionality or integrating with its project model.

## Core Responsibilities of EditorViewModel

### Persisted Project Data

At the heart of [`EditorViewModel.swift`](https://github.com/palmier-io/palmier-pro/blob/main/EditorViewModel.swift) (lines 27-32), the view model stores the three critical components of a `VideoProject`: the timeline, media manifest, and generation log. These properties ensure that every edit, media reference, and AI generation history persists across app sessions:

```swift
var timeline = Timeline()
var mediaManifest = MediaManifest()
var generationLog = GenerationLog()

```

### Panel Focus and Layout State

The view model tracks interface layout through `focusedPanel`, `maximizedPanel`, and `layoutPreset` (lines 35-48, 99-107). These values synchronize with `UserDefaults` to restore the editor's exact configuration between launches, maintaining user preferences for panel visibility and workspace arrangement.

### Transient UI State

Between lines 55-96, `EditorViewModel` maintains ephemeral editing state including `currentFrame`, `selectedClipIds`, `zoomScale`, `toolMode`, and `previewTabs`. Because the class is marked `@Observable`, mutations to these properties automatically trigger SwiftUI view updates without manual publisher management.

### Media Library Cache

Lines 108-131 implement an in-memory asset management system comprising `mediaAssets`, `mediaVisualCache`, and a `searchIndex`. The `mediaResolver` property maintains a connection to the current project URL, enabling efficient thumbnail generation and media validation without blocking the main thread.

### Service Coordination

The view model owns high-level service dependencies including `generationService` and `agentService` (lines 32-38), exposing AI-driven capabilities to SwiftUI views through the `agentPanelVisible` toggle. This centralized ownership prevents service lifecycle issues and ensures consistent state across generation workflows.

### Playback Bridge

Acting as intermediary between the UI and `VideoEngine`, the view model implements playback controls in lines 27-35 and 58-70. Methods like `togglePlayback()`, `play()`, `pause()`, and `seekToFrame(_:)` update both the underlying engine and observable UI state (`isPlaying`, `playheadState`), keeping visual playheads synchronized with audio output.

### Undo Support and Drag Handling

To support complex editing operations, lines 94-100 store pre-operation snapshots in `dragBefore` and `preDragTimeline`. The `notifyTimelineChangedDebounced` method (lines 161-169) prevents excessive rebuilds during rapid drag interactions while maintaining undo history integrity.

### Project Telemetry

The `telemetrySnapshot()` and `updateTelemetryContext()` methods (lines 89-112) aggregate statistics about tracks, clips, and generation logs, feeding anonymized usage data to the telemetry pipeline for performance optimization.

## Working with EditorViewModel: Code Examples

The following examples demonstrate practical interactions with `EditorViewModel` from SwiftUI views.

### Toggling Playback

This button implementation reads the `isPlaying` property (line 59) and calls `togglePlayback()` (lines 29-35):

```swift
Button(viewModel.isPlaying ? "Pause" : "Play") {
    viewModel.togglePlayback()
}

```

### Adding Clips to the Timeline

The `placeClip` method (lines 30-77) creates `Clip` instances, handles audio linking, and returns generated identifiers:

```swift
let newClipIds = viewModel.placeClip(
    asset: selectedMediaAsset,
    trackIndex: 0,
    startFrame: viewModel.currentFrame,
    durationFrames: 120
)

```

### Managing Multi-Selection

Selection state drives both the timeline visualization and inspector panels through the observable set:

```swift
viewModel.selectedClipIds = Set([clipA.id, clipB.id])

```

### Navigating Preview Tabs

Tab switching updates both current state and navigation history (lines 92-95):

```swift
viewModel.activePreviewTabId = PreviewTab.media.id
viewModel.previewTabHistory.append(viewModel.activePreviewTabId)

```

### Triggering AI Edits

Extension methods in `EditorViewModel+AIEdit.swift` coordinate with the generation service:

```swift
await viewModel.performAIEdit(on: clip.id, prompt: "Make it brighter")

```

## Modular Architecture via Extensions

Rather than monolithic implementation, Palmier Pro distributes editor functionality across focused extension files. This architecture keeps the core [`EditorViewModel.swift`](https://github.com/palmier-io/palmier-pro/blob/main/EditorViewModel.swift) manageable while providing namespaced APIs for specific features:

- **`EditorViewModel+Tracks.swift`** – Track-level mutations including add, delete, and reorder operations
- **`EditorViewModel+TimelineRange.swift`** – Range selection, trimming, and ripple-mode calculations
- **`EditorViewModel+AIEdit.swift`** – AI-driven editing workflows
- **`EditorViewModel+Ripple.swift`** – Ripple editing for batch timeline adjustments
- **`EditorViewModel+Linking.swift`** – Video/audio clip linking management
- **`EditorViewModel+Clipboard.swift`** – Cut, copy, and paste operations
- **`EditorViewModel+GeneratedClips.swift`** – Handling of AI-generated clip replacements
- **`EditorViewModel+PreviewTabs.swift`** – Tab navigation and history management
- **`EditorViewModel+MediaLibrary.swift`** – Media search index construction and caching
- **`EditorViewModel+Cost.swift`** – AI generation cost analysis utilities
- **`EditorViewModel+SaveAsMedia.swift`** – Export functionality for individual clips

## Summary

- **`EditorViewModel`** acts as the single source of truth for both persisted `VideoProject` data and transient UI state in Palmier Pro.
- The `@Observable` macro and `@MainActor` conformance ensure thread-safe, automatic SwiftUI updates.
- Core project data (timeline, media manifest, generation log) lives in the main file at lines 27-32.
- Playback control bridges the gap between UI gestures and the low-level `VideoEngine`.
- Extension files in `Sources/PalmierPro/Editor/ViewModel/` provide modular, feature-specific APIs while maintaining a thin core view model.
- Built-in telemetry, undo support, and debounced change notifications support professional editing workflows.

## Frequently Asked Questions

### How does EditorViewModel synchronize with SwiftUI views?

The class utilizes the `@Observable` macro introduced in Swift 5.9, which automatically tracks property access within views and triggers updates when values change. Combined with `@MainActor` isolation, this ensures all UI updates occur on the main thread without manual dispatch queue management.

### Where is the playback state actually managed?

While `EditorViewModel` exposes `isPlaying` and `playheadState` properties, it delegates actual media decoding and audio output to the `VideoEngine` instance. Methods like `togglePlayback()` and `seekToFrame(_:)` in lines 27-35 and 58-70 forward commands to the engine while updating observable UI state to reflect playback progress.

### Can I extend EditorViewModel for custom editor features?

Yes. The codebase encourages extension-based additions. Create a new file following the `EditorViewModel+Feature.swift` naming convention in `Sources/PalmierPro/Editor/ViewModel/`. Extensions can access all public and internal properties of the view model, allowing you to add methods that manipulate the `timeline`, `selectedClipIds`, or coordinate with `generationService`.

### How does EditorViewModel handle project persistence?

The view model stores references to `Timeline`, `MediaManifest`, and `GenerationLog` objects which conform to `Codable`. When the user saves a project, these structures serialize to the project bundle. Panel layouts and preferences synchronize automatically with `UserDefaults` via the `focusedPanel` and `layoutPreset` properties.