How to Add New Frontend Components in Palmier Pro: A SwiftUI Guide

To add new frontend components in Palmier Pro, create a SwiftUI View that uses only AppTheme constants for styling, binds state to EditorViewModel, and inserts into the existing view hierarchy following the established design system patterns.

Palmier Pro’s frontend architecture combines SwiftUI with AppKit where low-level control is required. All visual styling lives in a central design system, ensuring consistency across the macOS application. When you add new frontend components, you must respect this structured approach to maintain the app’s unified visual language and interaction patterns.

Architecture Overview

Palmier Pro implements a layered UI architecture where SwiftUI handles the majority of interface elements, with AppKit (NSViewRepresentable) reserved for specific low-level graphics requirements. The AppTheme enum in Sources/PalmierPro/UI/AppTheme.swift serves as the single source of truth for all visual attributes—colors, spacing, font sizes, corner radii, and shadows. State management follows a unidirectional pattern where mutable data belongs exclusively in EditorViewModel, not in individual views.

Step-by-Step Guide to Adding Frontend Components

1. Create the SwiftUI View Component

Begin by creating a new Swift file in the appropriate directory. Define a struct conforming to the View protocol. For toolbar elements, place files in the Toolbar group; for media panel components, use MediaPanel.

import SwiftUI

struct BookmarksButton: View {
    @Environment(EditorViewModel.self) private var editor
    
    var body: some View {
        // Implementation details below
        EmptyView()
    }
}

If you require direct AppKit access—for example, when implementing complex rendering layers—create an NSViewRepresentable wrapper instead. Reference Sources/PalmierPro/Preview/PreviewView.swift for the correct implementation pattern.

2. Apply Design System Styling via AppTheme

Never hard-code visual values. Import AppTheme and reference its static constants for every attribute:

  • AppTheme.IconSize for width and height dimensions
  • AppTheme.FontSize for type scaling
  • AppTheme.Text for color values (primary, secondary, tertiary)
  • AppTheme.Accent for interactive elements
  • AppTheme.Spacing for layout margins

Apply the hoverHighlight() modifier to interactive elements, which provides consistent hover feedback across the application.

3. Connect State to EditorViewModel

Access shared state through the @Environment(EditorViewModel.self) property wrapper. This injects the central view model without manual initialization. For toggle actions or panel visibility, bind to Boolean properties on EditorViewModel:

Button(action: { editor.isBookmarksPanelVisible.toggle() }) {
    // Button content
}

If the component requires new state fields, add them to EditorViewModel following the pattern in Sources/PalmierPro/Editor/ViewModel/EditorViewModel+AIEdit.swift, which exposes panel visibility flags.

4. Integrate into the View Hierarchy

Insert your component into an existing container. For toolbar additions, modify Sources/PalmierPro/Toolbar/ToolbarView.swift and add your view to the main HStack:

HStack(spacing: AppTheme.Spacing.md) {
    ExistingButton()
    BookmarksButton()  // Your new component
}

For media panel components, integrate into Sources/PalmierPro/MediaPanel/MediaTab/FolderTileView.swift or create analogous container views.

5. Follow Interaction Patterns

Use .buttonStyle(.plain) for all buttons to ensure consistent styling with the rest of the interface. Add help text using the .help() modifier to support keyboard shortcut discovery. Maintain small, stateless view structs—complex logic belongs in EditorViewModel or dedicated extensions.

Complete Implementation Example

Below is a production-ready implementation of a toolbar button that toggles the Bookmarks panel, following Palmier Pro conventions:

import SwiftUI

/// A toolbar button that toggles the Bookmarks panel visibility.
struct BookmarksButton: View {
    @Environment(EditorViewModel.self) private var editor

    var body: some View {
        Button(action: { editor.isBookmarksPanelVisible.toggle() }) {
            Image(systemName: "bookmark")
                .font(.system(size: AppTheme.FontSize.md))
                .foregroundStyle(
                    editor.isBookmarksPanelVisible
                        ? AppTheme.Text.primaryColor
                        : AppTheme.Text.tertiaryColor
                )
                .frame(width: AppTheme.IconSize.sm, height: AppTheme.IconSize.sm)
                .hoverHighlight(isActive: editor.isBookmarksPanelVisible)
        }
        .buttonStyle(.plain)
        .help("Show Bookmarks (⌥B)")
    }
}

To wire this into the toolbar, edit Sources/PalmierPro/Toolbar/ToolbarView.swift and insert the component into the existing HStack (around line 37):

HStack(spacing: AppTheme.Spacing.md) {
    // Existing toolbar items...
    BookmarksButton()
}

Key Source Files and Patterns

Reference these implementation files when adding new frontend components:

Summary

  • Create SwiftUI views (or NSViewRepresentable for AppKit needs) in the appropriate directory group.
  • Use only AppTheme constants for visual styling—never hard-code colors, sizes, or spacing.
  • Bind state via @Environment(EditorViewModel.self) to access shared mutable data and actions.
  • Insert components into existing containers like ToolbarView.swift or media panel hierarchies.
  • Apply standard modifiers including .buttonStyle(.plain) and hoverHighlight() for consistent interaction feedback.

Frequently Asked Questions

Where should I place new component files in the Palmier Pro repository?

Place new SwiftUI components in Sources/PalmierPro/ under the appropriate functional group—Toolbar/ for toolbar elements, MediaPanel/ for media interface components, or UI/ for reusable generic elements. Follow the existing file naming convention: [ComponentName].swift for views and [ComponentName]+[Extension].swift for view modifiers or extensions.

Can I use AppKit views instead of SwiftUI for frontend components?

Yes, when you require low-level graphics performance or specific AppKit functionality, implement NSViewRepresentable to wrap AppKit views. Reference Sources/PalmierPro/Preview/PreviewView.swift for the correct protocol implementation pattern. However, default to SwiftUI for standard UI elements to maintain consistency with the rest of the application.

How do I handle user interactions in new components?

Route all user interactions through methods on EditorViewModel. Inject the view model using @Environment(EditorViewModel.self) rather than passing it through initializers. For button actions, call closures that invoke view model methods. For complex state changes, add properties to EditorViewModel following the pattern in EditorViewModel+Folders.swift.

What is the role of EditorViewModel in frontend components?

EditorViewModel serves as the single source of truth for mutable application state and business logic. Frontend components remain stateless and declarative, observing EditorViewModel properties for display state and calling its methods for actions. This architecture ensures predictable data flow and simplifies testing by decoupling UI from logic.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →