Imported from pokushalov/Vyora (
AGENTS.md). Install upstream withnpx skills add pokushalov/Vyora. Copyright stays with the author.
AGENTS.md
Project Overview
Vyora is a native macOS image and video viewer built entirely in a single Swift file (main.swift) using SwiftUI + AppKit. It compiles via swiftc without an Xcode project.
Architecture
Single-file design
Everything lives in main.swift (~1400 lines), organized with // MARK: sections:
- App entry -
ImageViewerApp: App(SwiftUIWindowscene — structurally single-window, a second viewer window cannot exist) - App delegate -
AppDelegatehandles file opens, dock icon, lifecycle - Model -
ViewerModel: ObservableObject(singleton via.shared) owns all state - Layout constants -
Layoutenum with reserved areas - Window sizing -
WindowSizerresizes window to fit image/video - Views -
ContentView,ImageCanvas,VideoCanvas,EmptyState,TopBar,BottomBar,InfoPanel,SettingsView - Helpers -
VisualEffectBackground,KeyHandlingView,ScrollWheelView,PlayerViewRepresentable
Key patterns
ViewerModelis a singleton (ViewerModel.shared) so both SwiftUI andAppDelegateaccess the same state- All file-open channels (
.onOpenURL,AppDelegate.application(_:open:), drag-and-drop) funnel throughenqueueIncomingURLs, which coalesces URLs arriving in the same runloop tick — dedupes double delivery and turns multi-select opens into a browsable set - Image loading is async via
DispatchQueue+NSCache(5 items) + neighbor prefetch usingImageIOfor immediate bitmap decode - Video uses
AVPlayerViewwrapped inNSViewRepresentable(not SwiftUI'sVideoPlayer, which crashes on some macOS versions) - Window resize is deferred via
DispatchQueue.main.asyncto avoid crashes during SwiftUI layout passes - Recent files and user-granted folder roots (
grantedFolders) use Security-Scoped Bookmarks for sandbox compatibility; granted roots are re-opened on every launch so anything underneath never re-prompts
Build system
No Xcode project. build.sh calls swiftc directly, assembles .app bundle, generates Info.plist, copies to /Applications, registers with Launch Services.
Coding Conventions
- All code in
main.swift- do not split into multiple files - Use
// MARK: -comments to organize sections - SwiftUI views are structs, state management via
@EnvironmentObjectpointing toViewerModel - Prefer
NSViewRepresentablewrappers over SwiftUI-native controls when stability is needed (e.g.,AVPlayerView,NSVisualEffectView) - Keyboard shortcuts use
KeyCatcherView(NSView subclass) withkeyCodeconstants, not SwiftUI.keyboardShortcut
Common Tasks
Adding a new supported format
Add the extension to ViewerModel.imageExts or ViewerModel.videoExts.
Adding a new keyboard shortcut
Add a case in the KeyHandlingView closure inside ContentView.body (search for switch event.keyCode).
Adding a bottom toolbar button
Add a ToolButton(systemName:) call inside BottomBar.body.
Adding an Info.plist key
Edit the heredoc in build.sh (search for cat > "$APP_DIR/Contents/Info.plist").
Rebuilding the icon
Delete Vyora.icns and AppStoreIcon.png, then run ./build.sh. The icon is drawn programmatically in make_icon.swift.
Testing
No unit tests. Manual testing:
./build.sh- should compile and install without errors- Open the app, drag-and-drop an image folder
- Navigate with arrows, test zoom (scroll wheel, double-click, +/- buttons)
- Open a video - verify playback and slideshow behavior
- Press
I- verify info panel with EXIF data - Press
⌘,- verify Settings window - Right-click → Copy Image, paste elsewhere
- Test "Open With → Vyora" from Finder