EditorViewModel in Palmier Pro: The Central Hub for Managing Editor State
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 (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:
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):
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:
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:
viewModel.selectedClipIds = Set([clipA.id, clipB.id])
Navigating Preview Tabs
Tab switching updates both current state and navigation history (lines 92-95):
viewModel.activePreviewTabId = PreviewTab.media.id
viewModel.previewTabHistory.append(viewModel.activePreviewTabId)
Triggering AI Edits
Extension methods in EditorViewModel+AIEdit.swift coordinate with the generation service:
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 manageable while providing namespaced APIs for specific features:
EditorViewModel+Tracks.swift– Track-level mutations including add, delete, and reorder operationsEditorViewModel+TimelineRange.swift– Range selection, trimming, and ripple-mode calculationsEditorViewModel+AIEdit.swift– AI-driven editing workflowsEditorViewModel+Ripple.swift– Ripple editing for batch timeline adjustmentsEditorViewModel+Linking.swift– Video/audio clip linking managementEditorViewModel+Clipboard.swift– Cut, copy, and paste operationsEditorViewModel+GeneratedClips.swift– Handling of AI-generated clip replacementsEditorViewModel+PreviewTabs.swift– Tab navigation and history managementEditorViewModel+MediaLibrary.swift– Media search index construction and cachingEditorViewModel+Cost.swift– AI generation cost analysis utilitiesEditorViewModel+SaveAsMedia.swift– Export functionality for individual clips
Summary
EditorViewModelacts as the single source of truth for both persistedVideoProjectdata and transient UI state in Palmier Pro.- The
@Observablemacro and@MainActorconformance 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →