Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions tibok/tibok/Resources/tibok-appstore.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>F2PFRMGC9V.com.kquinones.tibok</string>
</array>
<key>com.apple.security.temporary-exception.files.absolute-path.read-only</key>
<array>
<string>$(HOME)</string>
</array>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>
100 changes: 92 additions & 8 deletions tibok/tibok/tibokApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import WebKit

@main
struct tibokApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@StateObject private var appState = AppState()
@AppStorage("appearanceMode") private var appearanceMode: String = AppearanceMode.system.rawValue

Expand Down Expand Up @@ -38,12 +39,8 @@ struct tibokApp: App {
}

var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(appState)
.onOpenURL { url in
appState.loadDocument(from: url)
}
WindowGroup(id: "main") {
MainWindowContent(appState: appState, windowTitle: windowTitle)
.onAppear {
applyAppearance()
// Defer plugin initialization to next run loop iteration
Expand All @@ -63,9 +60,10 @@ struct tibokApp: App {
.onChange(of: appearanceMode) { _, _ in
applyAppearance()
}
.navigationTitle(windowTitle)
}
.handlesExternalEvents(matching: ["main"])
.commands {
// File menu commands
CommandGroup(replacing: .newItem) {
Button("New Document") {
appState.createNewDocument()
Expand Down Expand Up @@ -369,7 +367,18 @@ struct tibokApp: App {
.keyboardShortcut("l", modifiers: .command)
}

// Add Find menu
// Window menu - add main window item
CommandGroup(after: .windowList) {
Divider()

Button("Main Window") {
reopenMainWindow()
}
.keyboardShortcut("1", modifiers: [.command, .option])
}
}
.commands {
// Add Find menu (in separate .commands block to avoid 10-child limit)
CommandMenu("Find") {
Button("Find…") {
performFind(.showFindPanel)
Expand Down Expand Up @@ -796,3 +805,78 @@ struct HelpWebView: NSViewRepresentable {
}
}
}

// MARK: - Main Window Content

/// Wrapper view that captures the openWindow environment action for later use
struct MainWindowContent: View {
@ObservedObject var appState: AppState
let windowTitle: String
@Environment(\.openWindow) private var openWindow

var body: some View {
ContentView()
.environmentObject(appState)
.onOpenURL { url in
appState.loadDocument(from: url)
}
.navigationTitle(windowTitle)
.onAppear {
// Store the openWindow action for use outside SwiftUI
WindowAccessor.shared.openWindow = { id in
openWindow(id: id)
}
}
}
}

// MARK: - App Delegate

class AppDelegate: NSObject, NSApplicationDelegate {
/// Called when the user clicks the dock icon while the app is running
/// This handles the case where all windows are closed but the app is still active
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
if !flag {
// No visible windows - reopen the main window
reopenMainWindow()
}
return true
}
}

// MARK: - Window Management

/// Reopens the main application window
/// SwiftUI's WindowGroup automatically manages a single main window.
/// When all windows are closed, we can trigger a new window by using openWindow or
/// by activating the app which causes WindowGroup to create a new window instance.
func reopenMainWindow() {
// First, check if there's an existing window we can just bring forward
if let existingWindow = NSApp.windows.first(where: { window in
// Find the main content window (not settings, help, or panels)
window.isVisible && window.title.contains("tibok")
}) {
existingWindow.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
return
}

// If no main window exists, we need to trigger WindowGroup to create one
// Use the openWindow action stored by WindowAccessor
if let openWindow = WindowAccessor.shared.openWindow {
openWindow("main")
NSApp.activate(ignoringOtherApps: true)
return
}

// Fallback: Activate the app which often triggers window creation
NSApp.activate(ignoringOtherApps: true)
}

// MARK: - Window Accessor

/// Stores SwiftUI Environment actions for use outside of SwiftUI views
class WindowAccessor {
static let shared = WindowAccessor()
var openWindow: ((_ id: String) -> Void)?
}