Create SHD desktop app repository

This commit is contained in:
bulava92
2026-10-28 15:26:43 +03:00
commit 4bc1a9a213
9 changed files with 1027 additions and 0 deletions
+226
View File
@@ -0,0 +1,226 @@
import Cocoa
import UserNotifications
@MainActor
final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate {
static weak var shared: AppDelegate?
private(set) var windowControllers: [MainWindowController] = []
override init() {
super.init()
AppDelegate.shared = self
}
func applicationDidFinishLaunching(_ notification: Notification) {
setupMainMenu()
setupNotifications()
let initialController = MainWindowController()
addWindowController(initialController)
initialController.showWindow(nil)
NSApp.activate(ignoringOtherApps: true)
}
func addWindowController(_ controller: MainWindowController) {
if !windowControllers.contains(where: { $0 === controller }) {
windowControllers.append(controller)
}
}
func removeWindowController(_ controller: MainWindowController) {
windowControllers.removeAll(where: { $0 === controller })
}
private func setupNotifications() {
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.alert, .badge, .sound]) { _, _ in }
}
nonisolated func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.banner, .badge, .sound])
}
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
if !flag {
if let first = windowControllers.first {
first.showWindow(nil)
} else {
let controller = MainWindowController()
addWindowController(controller)
controller.showWindow(nil)
}
}
return true
}
private func setupMainMenu() {
let mainMenu = NSMenu()
// App Menu
let appMenuItem = NSMenuItem()
let appMenu = NSMenu(title: "SHD")
appMenu.addItem(withTitle: "О программе SHD", action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)), keyEquivalent: "")
appMenu.addItem(NSMenuItem.separator())
appMenu.addItem(withTitle: "Скрыть SHD", action: #selector(NSApplication.hide(_:)), keyEquivalent: "h")
let hideOthersItem = appMenu.addItem(withTitle: "Скрыть остальные", action: #selector(NSApplication.hideOtherApplications(_:)), keyEquivalent: "h")
hideOthersItem.keyEquivalentModifierMask = [.command, .option]
appMenu.addItem(withTitle: "Показать все", action: #selector(NSApplication.unhideAllApplications(_:)), keyEquivalent: "")
appMenu.addItem(NSMenuItem.separator())
appMenu.addItem(withTitle: "Завершить SHD", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
appMenuItem.submenu = appMenu
mainMenu.addItem(appMenuItem)
// File Menu (New Tab, New Window, Print, Close)
let fileMenuItem = NSMenuItem()
let fileMenu = NSMenu(title: "Файл")
fileMenu.addItem(withTitle: "Новая вкладка", action: #selector(onNewTab), keyEquivalent: "t")
fileMenu.addItem(withTitle: "Новое окно", action: #selector(onNewWindow), keyEquivalent: "n")
fileMenu.addItem(NSMenuItem.separator())
fileMenu.addItem(withTitle: "Напечатать…", action: #selector(onPrint), keyEquivalent: "p")
fileMenu.addItem(NSMenuItem.separator())
fileMenu.addItem(withTitle: "Закрыть вкладку", action: #selector(onCloseTabOrWindow), keyEquivalent: "w")
fileMenuItem.submenu = fileMenu
mainMenu.addItem(fileMenuItem)
// Edit Menu
let editMenuItem = NSMenuItem()
let editMenu = NSMenu(title: "Правка")
editMenu.addItem(withTitle: "Отменить", action: Selector(("undo:")), keyEquivalent: "z")
let redoItem = editMenu.addItem(withTitle: "Повторить", action: Selector(("redo:")), keyEquivalent: "z")
redoItem.keyEquivalentModifierMask = [.command, .shift]
editMenu.addItem(NSMenuItem.separator())
editMenu.addItem(withTitle: "Вырезать", action: #selector(NSText.cut(_:)), keyEquivalent: "x")
editMenu.addItem(withTitle: "Скопировать", action: #selector(NSText.copy(_:)), keyEquivalent: "c")
editMenu.addItem(withTitle: "Вставить", action: #selector(NSText.paste(_:)), keyEquivalent: "v")
editMenu.addItem(withTitle: "Выбрать всё", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a")
editMenuItem.submenu = editMenu
mainMenu.addItem(editMenuItem)
// View Menu
let viewMenuItem = NSMenuItem()
let viewMenu = NSMenu(title: "Вид")
viewMenu.addItem(withTitle: "Обновить", action: #selector(onReload), keyEquivalent: "r")
let hardReload = viewMenu.addItem(withTitle: "Очистить кэш и обновить", action: #selector(onHardReload), keyEquivalent: "r")
hardReload.keyEquivalentModifierMask = [.command, .shift]
viewMenu.addItem(NSMenuItem.separator())
viewMenu.addItem(withTitle: "Увеличить", action: #selector(onZoomIn), keyEquivalent: "+")
viewMenu.addItem(withTitle: "Уменьшить", action: #selector(onZoomOut), keyEquivalent: "-")
viewMenu.addItem(withTitle: "Исходный масштаб", action: #selector(onResetZoom), keyEquivalent: "0")
viewMenu.addItem(NSMenuItem.separator())
viewMenu.addItem(withTitle: "Полноэкранный режим", action: #selector(NSWindow.toggleFullScreen(_:)), keyEquivalent: "f")
viewMenu.addItem(NSMenuItem.separator())
let inspectorItem = viewMenu.addItem(withTitle: "Инспектор веб-страницы", action: #selector(onShowInspector), keyEquivalent: "i")
inspectorItem.keyEquivalentModifierMask = [.command, .option]
let consoleItem = viewMenu.addItem(withTitle: "Консоль JavaScript", action: #selector(onShowConsole), keyEquivalent: "c")
consoleItem.keyEquivalentModifierMask = [.command, .option]
viewMenuItem.submenu = viewMenu
mainMenu.addItem(viewMenuItem)
// Account Menu
let accountMenuItem = NSMenuItem()
let accountMenu = NSMenu(title: "Учётная запись")
let browserLoginItem = accountMenu.addItem(
withTitle: "Войти по passkey в системном браузере…",
action: #selector(onBrowserPasskeyLogin),
keyEquivalent: "l"
)
browserLoginItem.keyEquivalentModifierMask = [.command, .shift]
browserLoginItem.target = self
accountMenuItem.submenu = accountMenu
mainMenu.addItem(accountMenuItem)
// Environment Menu
let envMenuItem = NSMenuItem()
let envMenu = NSMenu(title: "Контур")
let prodItem = envMenu.addItem(withTitle: "SHD Production (shd.xyz.su)", action: #selector(onSelectProd), keyEquivalent: "1")
prodItem.target = self
let devItem = envMenu.addItem(withTitle: "SHD Dev (dev-shd.xyz.su)", action: #selector(onSelectDev), keyEquivalent: "2")
devItem.target = self
envMenuItem.submenu = envMenu
mainMenu.addItem(envMenuItem)
// History Menu
let historyMenuItem = NSMenuItem()
let historyMenu = NSMenu(title: "История")
historyMenu.addItem(withTitle: "Назад", action: #selector(onGoBack), keyEquivalent: "[")
historyMenu.addItem(withTitle: "Вперед", action: #selector(onGoForward), keyEquivalent: "]")
historyMenuItem.submenu = historyMenu
mainMenu.addItem(historyMenuItem)
// Window Menu
let windowMenuItem = NSMenuItem()
let windowMenu = NSMenu(title: "Окно")
windowMenu.addItem(withTitle: "Убрать в Dock", action: #selector(NSWindow.performMiniaturize(_:)), keyEquivalent: "m")
windowMenu.addItem(withTitle: "Масштабировать", action: #selector(NSWindow.performZoom(_:)), keyEquivalent: "")
windowMenu.addItem(NSMenuItem.separator())
windowMenu.addItem(withTitle: "Объединить все окна", action: #selector(NSWindow.mergeAllWindows(_:)), keyEquivalent: "")
windowMenuItem.submenu = windowMenu
mainMenu.addItem(windowMenuItem)
NSApp.mainMenu = mainMenu
NSApp.windowsMenu = windowMenu
}
private var currentActiveController: MainWindowController? {
guard let keyWindow = NSApp.keyWindow else { return windowControllers.first }
return windowControllers.first(where: { $0.window === keyWindow }) ?? windowControllers.first
}
@objc private func onNewWindow() {
let controller = MainWindowController()
addWindowController(controller)
controller.showWindow(nil)
controller.window?.center()
}
@objc private func onNewTab() {
let controller = MainWindowController()
addWindowController(controller)
if let keyWindow = NSApp.keyWindow, let newWindow = controller.window {
keyWindow.addTabbedWindow(newWindow, ordered: .above)
newWindow.makeKeyAndOrderFront(nil)
} else {
controller.showWindow(nil)
}
}
func openTab(with request: URLRequest, relativeTo parentWindow: NSWindow?) {
let controller = MainWindowController(initialRequest: request)
addWindowController(controller)
if let hostWindow = parentWindow ?? NSApp.keyWindow, let newWindow = controller.window {
hostWindow.addTabbedWindow(newWindow, ordered: .above)
newWindow.makeKeyAndOrderFront(nil)
} else {
controller.showWindow(nil)
}
}
@objc private func onCloseTabOrWindow() {
if let keyWindow = NSApp.keyWindow {
keyWindow.performClose(nil)
}
}
@objc private func onPrint() { currentActiveController?.printCurrentPage() }
@objc private func onReload() { currentActiveController?.reload() }
@objc private func onHardReload() { currentActiveController?.reloadIgnoringCache() }
@objc private func onZoomIn() { currentActiveController?.zoomIn() }
@objc private func onZoomOut() { currentActiveController?.zoomOut() }
@objc private func onResetZoom() { currentActiveController?.resetZoom() }
@objc private func onGoBack() { currentActiveController?.goBack() }
@objc private func onGoForward() { currentActiveController?.goForward() }
@objc private func onSelectProd() { currentActiveController?.navigateToEnvironment(.production) }
@objc private func onSelectDev() { currentActiveController?.navigateToEnvironment(.dev) }
@objc private func onBrowserPasskeyLogin() { currentActiveController?.signInWithPasskeyInBrowser() }
@objc private func onShowInspector() { currentActiveController?.showInspector() }
@objc private func onShowConsole() { currentActiveController?.showConsole() }
}
MainActor.assumeIsolated {
let app = NSApplication.shared
let delegate = AppDelegate()
app.delegate = delegate
app.run()
}