Best Practices for Developing with Palmier Pro: Swift 6.2 & AI-Native Video Editing
The essential rules for Palmier Pro development are: never hard-code UI constants—always use AppTheme.swift; prefer SwiftUI but implement parent drag-and-drop containers with AppKit to avoid shadowing bugs; extend AI capabilities via ToolExecutor extensions registered in ToolDefinitions.swift; and reuse thread-safe utilities from Utilities/ rather than writing ad-hoc solutions.
Palmier Pro is an AI-native macOS video editor built with Swift 6.2, SwiftUI, AppKit, and AVFoundation. The palmier-io/palmier-pro repository follows a strict design-system approach that separates UI from media processing and supports a pluggable agent architecture. Following these conventions keeps the codebase consistent, testable, and ready for future AI-driven enhancements.
Architectural Foundations for Palmier Pro Development
Centralize UI Constants in AppTheme
All visual constants—including colors, spacing, fonts, radii, and shadows—live in Sources/PalmierPro/UI/AppTheme.swift. Hard-coding numbers violates the design system and creates maintenance debt.
Always reference constants through AppTheme enums:
import SwiftUI
struct ExampleHeader: View {
var body: some View {
HStack {
Text("Welcome to Palmier Pro")
.font(.system(size: AppTheme.FontSize.title1,
weight: AppTheme.FontWeight.bold))
.foregroundColor(AppTheme.Text.primaryColor)
Spacer()
}
.padding(AppTheme.Spacing.lg)
.background(AppTheme.Background.surfaceColor)
.shadow(AppTheme.Shadow.md)
}
}
This approach ensures that padding(AppTheme.Spacing.md) and font(.system(size: AppTheme.FontSize.lg)) remain consistent across the macOS interface.
SwiftUI and AppKit Integration Patterns
While SwiftUI is preferred for most interfaces, drag-and-drop areas containing nested drop targets must use native AppKit. The "parent-onDrop shadows children" bug requires implementing leaf drop zones with .onDrop while parent containers use the AppKit NSView subclass MediaPanelDropArea found in Sources/PalmierPro/MediaPanel/MediaTab/MediaPanelDropArea.swift.
When you need a parent drop area, subclass NSView instead of using SwiftUI modifiers:
import AppKit
final class CustomDropArea: NSView {
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
registerForDraggedTypes([.fileURL])
}
required init?(coder: NSCoder) { fatalError() }
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
guard let url = sender.draggedFileURLs?.first else { return false }
// Forward to the SwiftUI side via NotificationCenter or Combine
NotificationCenter.default.post(name: .customDrop, object: url)
return true
}
}
Use this pattern when the area contains other drop targets, as nested SwiftUI .onDrop modifiers interfere with child interactions.
Timeline Architecture and State Management
Core timeline logic resides in Sources/PalmierPro/Timeline/, including TimelineView.swift, geometry calculations, ruler rendering, the SnapEngine, and ClipRenderer. Keep timeline state immutable where possible and delegate heavy computational work to SnapEngine and ClipRenderer rather than handling logic directly in views.
This separation allows the timeline to handle complex video editing operations while maintaining responsive UI updates through SwiftUI's state management.
Extending the AI Agent with ToolExecutor
The in-app AI agent is built from modular ToolExecutor extensions. To add new capabilities—such as audio-sync or caption generation—extend ToolExecutor in a separate file under Sources/PalmierPro/Agent/Tools/ (following the pattern of ToolExecutor+AudioSync.swift), then register the tool in Sources/PalmierPro/Agent/ToolDefinitions.swift.
Create the executor extension:
extension ToolExecutor {
func executeMyNewTool(_ request: MyToolRequest) async throws -> MyToolResponse {
Log.info("Running MyNewTool with payload: \(request.payload)")
let result = try await performNetworkCall(request)
return MyToolResponse(result: result)
}
}
Then register in ToolDefinitions.swift:
static let myNewTool = ToolDefinition(
name: "my_new_tool",
description: "Does something useful",
executor: { executor, request in
try await executor.executeMyNewTool(request as! MyToolRequest)
})
This modular architecture keeps agent capabilities organized and discoverable by the AI system.
Reuse Thread-Safe Utilities
Helpers such as DiskCache, ImageEncoder, and Log reside under Sources/PalmierPro/Utilities/. These utilities already respect the app's thread-safety and caching policies. Reuse them instead of writing ad-hoc code to ensure consistency across the codebase when handling media encoding, persistent caching, or structured logging.
Testing Requirements
The test suite lives under Tests/PalmierProTests/ and covers media import, timeline round-tripping, and AI-generated assets. Write unit tests for new modules and run swift test locally before committing changes to ensure regression coverage for the video editing pipeline.
Code Examples for Palmier Pro Development
Adding a New AI Tool Capability
When developing new AI features for Palmier Pro, following the ToolExecutor pattern ensures compatibility with the agent system:
import Foundation
struct MyToolRequest: Codable {
let payload: String
}
struct MyToolResponse: Codable {
let result: String
}
extension ToolExecutor {
func executeMyNewTool(_ request: MyToolRequest) async throws -> MyToolResponse {
// Reuse existing logging and error handling utilities
Log.info("Running MyNewTool with payload: \(request.payload)")
// Core logic – maybe call a local ML model or MCP endpoint
let result = try await performNetworkCall(request)
return MyToolResponse(result: result)
}
}
Remember to keep comments minimal and explain why something is done, not what, as specified in AGENTS.md.
Key Source Files and Organization
Understanding the repository structure is critical for effective Palmier Pro development:
Sources/PalmierPro/UI/AppTheme.swift— Centralized design-system constants (colors, spacing, fonts, shadows)Sources/PalmierPro/Timeline/— Timeline view, geometry, snap engine, and clip rendering viaTimelineView.swiftSources/PalmierPro/MediaPanel/— Media-panel UI, import handling, drag-and-drop, and thumbnail generationSources/PalmierPro/Agent/Tools/ToolExecutor+*.swift— Modular AI-agent tool implementations (audio sync, captions, etc.)Sources/PalmierPro/Utilities/— Shared helpers for caching (DiskCache), logging (Log), keychain access, and image encoding (ImageEncoder)Tests/PalmierProTests/— Unit and integration tests for media import, timeline round-trip, and AI featuresAGENTS.md— Coding-style guidelines and architectural notes
Follow the file-structure conventions: UI components go to UI/, media handling to MediaPanel/, timeline logic to Timeline/, and agent extensions to Agent/. Use descriptive enum cases and extensions (e.g., ClipType.themeColor) to maintain readability.
Summary
- Always pull UI constants from
AppThemeand never hard-code visual values; useAppTheme.Spacing.md,AppTheme.FontSize.lg, andAppTheme.Shadow.mdthroughout SwiftUI views - Prefer SwiftUI for interfaces, but use AppKit
NSViewimplementations (likeMediaPanelDropArea) for parent drag-and-drop containers to avoid the "parent-onDrop shadows children" bug - Keep timeline state immutable and delegate heavy work to
SnapEngineandClipRendererrather than processing directly inTimelineView.swift - Extend AI capabilities via
ToolExecutorextensions inSources/PalmierPro/Agent/Tools/and register them inToolDefinitions.swift - Reuse utilities from
Utilities/for caching, logging, and image encoding to maintain thread safety and caching policies - Write unit tests for every new component and run
swift testbefore merging to ensure media processing and timeline features remain stable
Frequently Asked Questions
How do I add a new color or spacing value to Palmier Pro?
Add new design constants to Sources/PalmierPro/UI/AppTheme.swift following the existing enum structure for colors, spacing, fonts, and shadows. Reference these constants throughout the UI using AppTheme.Spacing.md or AppTheme.Text.primaryColor rather than hard-coding values. This ensures consistency with the macOS design system and simplifies future theme updates across the video editor interface.
When should I use AppKit instead of SwiftUI in Palmier Pro?
Use AppKit's NSView for parent drag-and-drop containers that contain nested drop targets. SwiftUI's .onDrop modifier shadows child drop zones, causing interaction bugs where children cannot receive drop events. Implement leaf drop zones with SwiftUI .onDrop, but parent containers should use MediaPanelDropArea or similar NSView subclasses defined in Sources/PalmierPro/MediaPanel/MediaTab/MediaPanelDropArea.swift to maintain proper event propagation.
What is the correct way to implement a new AI agent tool?
Create a new file in Sources/PalmierPro/Agent/Tools/ extending ToolExecutor with your async execution method (following the ToolExecutor+AudioSync.swift pattern). Then register the tool in Sources/PalmierPro/Agent/ToolDefinitions.swift by adding a ToolDefinition entry that maps the tool name to your executor extension. This modular approach keeps agent capabilities organized and discoverable by the AI system while maintaining clean separation of concerns.
Where should I place utility functions in the Palmier Pro codebase?
Place reusable helpers in Sources/PalmierPro/Utilities/ alongside existing utilities like DiskCache, ImageEncoder, and Log. These utilities are engineered for thread safety and respect the app's caching policies. Avoid creating ad-hoc utility files in feature directories; instead, extend existing utilities or add new ones to the central Utilities/ folder to ensure consistent behavior across the media processing pipeline.
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 →