Skip to content

Quick Look plugin for web-based previews πŸ§‘β€πŸ’» πŸ‘ πŸ“„

License

Notifications You must be signed in to change notification settings

jtbandes/quicklookjs

Repository files navigation

quicklookjs

npm version

quicklookjs demo

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>
  // We're using an async function so we can more easily interact with Promises returned by quicklookjs.
  // This isn't required; you can always use .then() instead of await.
  async function main() {
    // Step 1: get the file that we're supposed to show a preview for.
    // `file` is a File object, the same as if the user had dragged & dropped the file into your page.
    const { file, path } = await quicklook.getPreviewedFile();

    // Step 2: ...do anything you'd like in order to create a preview of the file!
    // File inherits from Blob (https://developer.mozilla.org/en-US/docs/Web/API/Blob), so you can
    // use Blob APIs like `text()` or `arrayBuffer()` to read the file.

    // Step 3: tell quicklookjs that we're ready to display the preview.
    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.

  1. Install this package.

    npm install --save-dev quicklookjs
  2. 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.

  3. 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).

  4. 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.

  5. 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.