Function bodies 8 total
AppDelegate class · swift · L745-L749 (5 LOC)MDViewer/App/AppDelegate.swift
class AppDelegate: NSObject, NSApplicationDelegate {
func application(_ application: NSApplication, open urls: [URL]) {
for url in urls { WindowCoordinator.shared.open(url: url) }
}
}WindowCoordinator class · swift · L5-L74 (70 LOC)MDViewer/App/WindowCoordinator.swift
final class WindowCoordinator {
static let shared = WindowCoordinator()
private init() {}
private var openHandler: ((WindowID) -> Void)?
private var pendingURLs: [URL] = []
// Weak references to all live AppState instances
private struct WeakRef { weak var appState: AppState? }
private var registeredStates: [WeakRef] = []
/// Each WindowView registers its AppState so we can reuse empty windows.
func registerAppState(_ appState: AppState) {
registeredStates.removeAll { $0.appState == nil }
registeredStates.append(WeakRef(appState: appState))
}
/// Called by the first window on appear. Drains any URLs queued before a window existed.
func register(handler: @escaping (WindowID) -> Void) {
openHandler = handler
let pending = pendingURLs
pendingURLs = []
for url in pending { open(url: url) }
}
func openEmpty() {
openHandler?(.empty())
}
func open(url: URL) {
rCoordinator class · swift · L230-L309 (80 LOC)MDViewer/Rendering/EditorView.swift
class Coordinator: NSObject, NSTextViewDelegate {
var parent: EditorView
weak var textView: NSTextView?
var highlighter: MarkdownHighlighter?
var currentFontFamily: String = "system"
var lastScrollToken: UUID? = nil
var lastEditorStyle: EditorStyle? = nil
init(_ parent: EditorView) { self.parent = parent }
func textDidChange(_ notification: Notification) {
guard let tv = notification.object as? NSTextView else { return }
// Don't propagate partial IME composition — wait for confirmed text
guard !tv.hasMarkedText() else { return }
parent.text = tv.string
}
func textViewDidChangeSelection(_ notification: Notification) {
guard let tv = notification.object as? NSTextView else { return }
// Skip cursor-sync while IME is composing
guard !tv.hasMarkedText() else { return }
let cursor = tv.selectedRange().locLineNumberRulerView class · swift · L4-L99 (96 LOC)MDViewer/Rendering/LineNumberRulerView.swift
final class LineNumberRulerView: NSRulerView {
private weak var textView: NSTextView?
private let numberFont = NSFont.monospacedSystemFont(ofSize: 10.5, weight: .regular)
private let rightPad: CGFloat = 8
init(textView: NSTextView, scrollView: NSScrollView) {
self.textView = textView
super.init(scrollView: scrollView, orientation: .verticalRuler)
clientView = textView
ruleThickness = 42
let nc = NotificationCenter.default
nc.addObserver(self, selector: #selector(refresh),
name: NSText.didChangeNotification, object: textView)
nc.addObserver(self, selector: #selector(refresh),
name: NSView.boundsDidChangeNotification,
object: scrollView.contentView)
}
required init(coder: NSCoder) { fatalError() }
@objc private func refresh() { needsDisplay = true }
// MARK: - Drawing
override func drawHashMarksAndLabels(in rect: NSRectMarkdownHighlighter class · swift · L4-L215 (212 LOC)MDViewer/Rendering/MarkdownHighlighter.swift
final class MarkdownHighlighter: NSObject, NSTextStorageDelegate {
var baseFont: NSFont = .systemFont(ofSize: 14) // updated by EditorView on zoom
var paragraphStyle: NSParagraphStyle = .default // updated by EditorView on zoom
weak var textView: NSTextView? // set by EditorView; used for IME check
var editorStyle: EditorStyle = StylesFile.systemDefaults.styles[0].editorStyle
private var isWorking = false
/// Bold version of baseFont, preserving its cascade list (e.g. PingFang SC).
private func makeBoldFont(size: CGFloat? = nil) -> NSFont {
let sz = size ?? baseFont.pointSize
let descriptor = baseFont.fontDescriptor.withSymbolicTraits(.bold)
return NSFont(descriptor: descriptor, size: sz) ?? NSFont.boldSystemFont(ofSize: sz)
}
// MARK: - Delegate
func textStorage(
_ textStorage: NSTextStorage,
didProcessEditing editedMask: NSTextStorageEditActions,
range: NCoordinator class · swift · L80-L185 (106 LOC)MDViewer/Rendering/MarkdownWebView.swift
class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
weak var webView: WKWebView?
var pendingContent: String = ""
var pendingFileURL: URL? = nil
var pendingCSS: String = ""
var lastContent: String = ""
var lastFileURL: URL? = nil
var lastCSS: String = ""
var isLoaded: Bool = false
var onPreviewClick: ((Int, CGFloat, CGFloat) -> Void)?
// Debounce timer for editor-mode renders (file-open bypasses it)
private var renderTimer: Timer?
deinit { renderTimer?.invalidate() }
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
isLoaded = true
let css = pendingCSS
let escaped = css
.replacingOccurrences(of: "\\", with: "\\\\")
.replacingOccurrences(of: "`", with: "\\`")
.replacingOccurrences(of: "$", with: "\\$")
webView.evaluateJavaScript("applyCuStyleManager class · swift · L5-L82 (78 LOC)MDViewer/Styles/StyleManager.swift
final class StyleManager: ObservableObject {
static let shared = StyleManager()
@Published private(set) var stylesFile: StylesFile
@Published private(set) var activeStyle: MarkdownStyle
private let fileWatcher = FileWatcher()
private var isRestoring = false
private var appearanceObserver: NSKeyValueObservation?
private init() {
let file = StylesFile.load()
stylesFile = file
activeStyle = file.resolvedEffectiveStyle()
}
func setup() {
// Ensure config file exists on disk
if !FileManager.default.fileExists(atPath: StylesFile.configURL.path) {
stylesFile.save()
}
// Watch for changes
fileWatcher.watch(url: StylesFile.configURL) { [weak self] in
self?.reload()
}
// Follow system dark/light mode transitions
appearanceObserver = NSApp.observe(\.effectiveAppearance) { [weak self] _, _ in
DispatchQueue.main.async {
gRepobility · severity-and-effort ranking · https://repobility.com
FileWatcher class · swift · L3-L57 (55 LOC)MDViewer/Utilities/FileWatcher.swift
final class FileWatcher {
private var source: DispatchSourceFileSystemObject?
private var watchedURL: URL?
private var onChange: (() -> Void)?
func watch(url: URL, onChange: @escaping () -> Void) {
self.watchedURL = url
self.onChange = onChange
startWatching(url: url)
}
func stop() {
source?.cancel()
source = nil
}
private func startWatching(url: URL) {
source?.cancel()
source = nil
let fd = open(url.path, O_EVTONLY)
guard fd != -1 else { return }
let src = DispatchSource.makeFileSystemObjectSource(
fileDescriptor: fd,
eventMask: [.write, .rename, .delete, .attrib],
queue: .main
)
src.setEventHandler { [weak self] in
guard let self else { return }
let flags = src.data
self.onChange?()
// Atomic-save editors replace the file via rename/delete;
// re-watch the p