quicklookjs
quicklookjs provides a macOS Quick Look Previewing extension that displays a web page. By packaging this preview extension with your native app, and replacing the web page with your own, you can implement a Quick Look preview experience using HTML/JavaScript (or a web framework of your choice).
📝 Create a preview page
The first step to creating a Quick Look preview is to write the preview page! You can see an example preview page, included with quicklookjs, at preview.html.
JavaScript in a quicklookjs preview page can use two functions on the global quicklook
object, getPreviewedFile
and finishedLoading
, to implement previews:
<script>
async function main() {
const { file, path } = await quicklook.getPreviewedFile();
await quicklook.finishedLoading();
}
main();
</script>
Other than these two functions, the preview implementation is totally up to you. Get creative! 🎨
📦 Add the preview extension to your app
To make your preview page available to macOS, it needs to be bundled inside the App Extension provided by quicklookjs.
-
Install this package.
npm install --save-dev quicklookjs
-
When packaging your app, copy the .appex
bundle into your application at .../Contents/PlugIns
:
mkdir -p MyApp.app/Contents/PlugIns
cp -R node_modules/quicklookjs/dist/PreviewExtension.appex MyApp.app/Contents/PlugIns/PreviewExtension.appex
If you're using electron-builder to package your app, you can do this by adding an extraFiles
entry to your builder configuration:
"extraFiles": [
{
"from": "node_modules/quicklookjs/dist/PreviewExtension.appex",
"to": "PlugIns/PreviewExtension.appex"
}
],
You might also want to perform the following steps as part of an afterPack
script.
-
Edit the extension's Info.plist to suit your app:
-
The extension's CFBundleIdentifier should be prefixed with the containing app's CFBundleIdentifier. For example, if the app bundle ID is com.example.MyApp
, the extension's bundle ID could be com.example.MyApp.PreviewExtension
.
-
Update the NSExtension.NSExtensionAttributes.QLSupportedContentTypes
with an array of the file content types you support. These can match the system-declared UTIs, or if your app declares custom types, these should match the UTTypeIdentifier
entries in your app's Info.plist. (See Declaring New Uniform Type Identifiers for more info about custom file types.)
-
Optionally customize the configuration dictionary under the QLJS
key (more about this below).
-
Copy your preview page into the extension bundle. By default the extension will load the page at PreviewExtension.appex/Contents/Resources/preview.html
which is pre-configured for you with an example page. The file name loaded by the extension is configurable.
-
Re-codesign the extension. This step is important because the modifications you made to the bundle render the existing signature invalid, and macOS requires Quick Look extensions to be signed/sandboxed. An entitlements plist file is included with the quicklookjs package to get you started.
codesign \
--sign - \
--force \
--entitlements node_modules/quicklookjs/dist/PreviewExtension.entitlements \
MyApp.app/Contents/PlugIns/PreviewExtension.appex
Now your app has Quick Look support! 🎉
🛠 Configuration
You can customize some of the preview extension's behaviors by editing the QLJS
dictionary in its Info.plist. The currently supported keys are:
-
loadingStrategy
(required): one of two pre-defined string values:
waitForSignal
: show the preview page only once it signals that it's ready via quicklook.finishedLoading()
. This is the preferred loading strategy.navigationComplete
: show the preview page as soon as it loads in the web view.
-
pagePath
(required): the path to the HTML page that will be displayed as the preview, relative to PreviewExtension.appex/Contents/Resources
. In the example Info.plist this is preview.html
.
-
preferredContentSize
(optional): a string specifying the size of the preview window. In the example Info.plist this is {500,300}
.
-
transparentBackground
(optional): a boolean specifying whether the web view should have a transparent background. This relies on undocumented APIs, so use it with caution.
🧠 Behind the scenes
Under the hood, quicklookjs loads your preview page in a WKWebView, and the quicklook
functions are provided in a user script.
In order to give your JavaScript code access to the previewed file as a File object, when you call getPreviewedFile()
, quicklookjs temporarily adds an <input type="file">
to the page, and sends a fake click event to the web view. This allows the native code to respond with a file URL, which the web view translates into a File object.