Skip to content

Commit

Permalink
Support for macOS Monterey and bugfix.
Browse files Browse the repository at this point in the history
  • Loading branch information
sbarex committed Oct 30, 2021
1 parent cf14bda commit 2c61b34
Show file tree
Hide file tree
Showing 11 changed files with 376 additions and 173 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
# Changelog
=======

### 1.0.4 (29)
New features:
- On macOS 12 Monterey, the new lightweight data based preview will be used.
- Addeded more arguments to the CLI tool.
- CLI tool embed inline images handle also raw HTML fragments.
Bugfix:
- Handle markdown associated only to a dynamic UTI.
- Fixed issue with instantiated and never released `Markdown QL Extension Web Content` process for each file previewed.
- On macOS 12 Monterey now you can scroll the preview with the scrollbars.
- On macOS 12 Monterey the bug of trackpad scrolling in fullscreen preview has been fixed.

### 1.0.3 (28)
New features:
- Command line interface (CLI) tool.
Expand Down
3 changes: 3 additions & 0 deletions QLExtension/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>QLIsDataBasedPreview</key>
<true/>
<key>QLSupportedContentTypes</key>
<array>
<string>net.ia.markdown</string>
Expand All @@ -34,6 +36,7 @@
<string>org.textbundle.package</string>
<string>com.rstudio.rmarkdown</string>
<string>dyn.ah62d4rv4ge81e5pe</string>
<string>dyn.ah62d4rv4ge8043a</string>
</array>
<key>QLSupportsSearchableItems</key>
<false/>
Expand Down
132 changes: 48 additions & 84 deletions QLExtension/PreviewViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,14 @@ class PreviewViewController: NSViewController, QLPreviewingController {
}

override func viewDidDisappear() {
// This code will not be called on macOS 12 Monterey with QLIsDataBasedPreview set.

self.launcherService = nil
// Releases the script handler which retain a strong reference to self which prevents the WebKit process from being released.
self.webView.configuration.userContentController.removeScriptMessageHandler(forName: "imageExtensionHandler")
}

override func loadView() {
// This code will not be called on macOS 12 Monterey with QLIsDataBasedPreview set.

super.loadView()
// Do any additional setup after loading the view.

Expand Down Expand Up @@ -109,9 +111,6 @@ class PreviewViewController: NSViewController, QLPreviewingController {
configuration.preferences.javaScriptEnabled = settings.unsafeHTMLOption && settings.inlineImageExtension
configuration.allowsAirPlayForMediaPlayback = false

// Handler to replace raw <image> src with the embedded data.
configuration.userContentController.add(self, name: "imageExtensionHandler")

self.webView = MyWKWebView(frame: previewRect, configuration: configuration)
self.webView.autoresizingMask = [.height, .width]

Expand Down Expand Up @@ -167,6 +166,7 @@ class PreviewViewController: NSViewController, QLPreviewingController {
*/

func preparePreviewOfFile(at url: URL, completionHandler handler: @escaping (Error?) -> Void) {
// This code will not be called on macOS 12 Monterey with QLIsDataBasedPreview set.

// Add the supported content types to the QLSupportedContentTypes array in the Info.plist of the extension.

Expand All @@ -175,6 +175,44 @@ class PreviewViewController: NSViewController, QLPreviewingController {
// Call the completion handler so Quick Look knows that the preview is fully loaded.
// Quick Look will display a loading spinner while the completion handler is not called.

do {
self.handler = handler

let html = try renderMD(url: url)
/*
if #available(macOS 11, *) {
self.webView.mainFrame.loadHTMLString(html, baseURL: nil)
} else {
*/
self.webView.isHidden = true // hide the webview until complete rendering
self.webView.loadHTMLString(html, baseURL: url.deletingLastPathComponent())
/* } */
} catch {
handler(error)
}
}

@available(macOSApplicationExtension 12.0, *)
func providePreview(for request: QLFilePreviewRequest, completionHandler handler: @escaping (QLPreviewReply?, Error?) -> Void) {
// This code will be called on macOS 12 Monterey with QLIsDataBasedPreview set.

// print("providePreview for \(request.fileURL)")

do {
let html = try renderMD(url: request.fileURL)
let replay = QLPreviewReply(dataOfContentType: .html, contentSize: .zero) { _ in
return html.data(using: .utf8)!
}

// replay.title = request.fileURL.lastPathComponent
replay.stringEncoding = .utf8
handler(replay, nil)
} catch {
handler(nil, error)
}
}

func renderMD(url: URL) throws -> String {
os_log(
"Generating preview for file %{public}s",
log: self.log,
Expand All @@ -184,8 +222,6 @@ class PreviewViewController: NSViewController, QLPreviewingController {

let type = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") ?? "Light"

self.handler = handler

let settings = Settings.shared

let markdown_url: URL
Expand All @@ -199,27 +235,11 @@ class PreviewViewController: NSViewController, QLPreviewingController {
markdown_url = url
}

do {
let text = try settings.render(file: markdown_url, forAppearance: type == "Light" ? .light : .dark, baseDir: markdown_url.deletingLastPathComponent().path, log: self.log)
let text = try settings.render(file: markdown_url, forAppearance: type == "Light" ? .light : .dark, baseDir: markdown_url.deletingLastPathComponent().path, log: self.log)

let html = settings.getCompleteHTML(title: url.lastPathComponent, body: text, footer: "", basedir: url.deletingLastPathComponent())

let extrajs: String
if settings.unsafeHTMLOption && settings.inlineImageExtension {
extrajs = "<script type=\"text/javascript\">" + (settings.getBundleContents(forResource: "inlineimages", ofType: "js") ?? "") + "</script>\n";
} else {
extrajs = ""
}
let html = settings.getCompleteHTML(title: url.lastPathComponent, body: text, footer: extrajs)
/*
if #available(macOS 11, *) {
self.webView.mainFrame.loadHTMLString(html, baseURL: nil)
} else {
*/
self.webView.isHidden = true // hide the webview until complete rendering
self.webView.loadHTMLString(html, baseURL: url.deletingLastPathComponent())
/* } */
} catch {
handler(error)
}
return html
}
}

Expand All @@ -239,69 +259,13 @@ extension PreviewViewController: WebFrameLoadDelegate {
}
}

extension PreviewViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard message.name == "imageExtensionHandler", Settings.shared.unsafeHTMLOption && Settings.shared.inlineImageExtension else {
return
}
guard let dict = message.body as? [String : AnyObject], let src = dict["src"] as? String, let id = dict["id"] as? String else {
return
}

guard let data = get_base64_image(
src.cString(using: .utf8),
{ (path: UnsafePointer<Int8>?, context: UnsafeMutableRawPointer?) -> UnsafeMutablePointer<Int8>? in
let magic_file = Settings.shared.getResourceBundle().path(forResource: "magic", ofType: "mgc")?.cString(using: .utf8)

let r = magic_get_mime_by_file(path, magic_file)
return r
},
nil
) else {
return
}
defer {
data.deallocate()
}
let response: [String: String] = [
"src": src,
"id": id,
"data": String(cString: data)
]
let encoder = JSONEncoder()
guard let j = try? encoder.encode(response), let js = String(data: j, encoding: .utf8) else {
return
}

message.webView?.evaluateJavaScript("replaceImageSrc(\(js))") { (r, error) in
if let result = r as? Bool, !result {
os_log(
"Unable to replace <img> src %{public}s with the inline data.",
log: self.log,
type: .error,
src
)
}
if let error = error {
os_log(
"Unable to replace <img> src %{public}s with the inline data: %{public}s.",
log: self.log,
type: .error,
src, error.localizedDescription
)
}
}
}
}

extension PreviewViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
if let handler = self.handler {
// Show the Quick Look preview only after the complete rendering (preventing a flickering glitch).

handler(nil)
self.handler = nil
}
// Show the Quick Look preview only after the complete rendering (preventing a flickering glitch).
// Wait to show the webview to prevent a resize glitch.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.webView.isHidden = false
Expand Down
Loading

0 comments on commit 2c61b34

Please sign in to comment.