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.IconSizefor width and height dimensionsAppTheme.FontSizefor type scalingAppTheme.Textfor color values (primary, secondary, tertiary)AppTheme.Accentfor interactive elementsAppTheme.Spacingfor 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:
Sources/PalmierPro/UI/AppTheme.swift– Central design system defining colors, spacing, fonts, and shadows.Sources/PalmierPro/Toolbar/ToolbarView.swift– Primary toolbar layout demonstrating container composition.Sources/PalmierPro/Editor/ViewModel/EditorViewModel+Folders.swift– Example of state management patterns for folder-related UI actions.Sources/PalmierPro/Editor/ViewModel/EditorViewModel+AIEdit.swift– Reference for implementing panel visibility toggles and Boolean state flags.Sources/PalmierPro/MediaPanel/MediaTab/FolderTileView.swift– Complex SwiftUI component example following the design system conventions.Sources/PalmierPro/Preview/PreviewView.swift– Implementation ofNSViewRepresentablefor embedding AppKit views within SwiftUI.
Summary
- Create SwiftUI views (or
NSViewRepresentablefor AppKit needs) in the appropriate directory group. - Use only
AppThemeconstants 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.swiftor media panel hierarchies. - Apply standard modifiers including
.buttonStyle(.plain)andhoverHighlight()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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →