Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin proposal #181

Closed
sindresorhus opened this issue May 16, 2017 · 0 comments
Closed

Plugin proposal #181

sindresorhus opened this issue May 16, 2017 · 0 comments

Comments

@sindresorhus
Copy link
Contributor

sindresorhus commented May 16, 2017

I'm looking for feedback about the proposed plugin API from Kap users and potential plugin creators. Any use-cases I'm missing? Anything that could be improved?


Intro

This proposal is focused on what I consider the main use-case for plugins, sharing. People want to save the recording, they want to upload it somewhere, they want to share it with other apps.

The scope is intentionally narrow so we can ship something soon. You may ask, why do we need to ship something soon? Can’t we just do everything at once? My goal is to ship something soon, so we can start gathering feedback on what people actually need. We won’t get real feedback on a non-existing feature. I think it’s better to iterate in public with users than ship something users might not even need sometime in the future.

Initial app view

The user no longer selects an export format upfront. We’ll remove that section. Format selection happens in the editor instead. Many user have complained about the previous behavior, including me, since if you picked the wrong format upfront, you could not change it. This will also enable users to get the recording in multiple formats. It also sets the stage for the plugin functionality.

Editor

The “Discard” button is removed as it’s no longer useful. People can just close the window with the red traffic light button.

The “Save” button will be removed. Instead we’ll add separate buttons for each format:

Loop [On|Off]                  Share: [GIF] [MP4] [WebM]

screen shot 2017-05-16 at 17 45 50

This is where plugins come in. We’ll make it possible for plugins to have an entry in a popup menu. For example, with a Dropbox plugin, the menu would look like:

[GIF] --> |Save             |
          |Upload to Dropbox|
          |Share on Giphy   |

The editor window is no longer dismissed after saving/sharing so the user is able to share to multiple places.

The share action is async and progress information should not be blocking the editor window. Meaning, the user can do other things while waiting.

Plugin management

Plugins will be managed in a new tab in Preferences. I’m thinking something similar to the Sketch plugin management view, but with ability to find and install plugins right in the GUI.

image

Like Hyper, plugins will just be npm packages that have the “kap-plugin” keyword in their package.json file and use the kap-prefix for the name. We can fetch all plugins from https://api.npms.io/v2/search?q="keywords:kap-plugin". API docs.

When a user installs a plugin in the plugins tab, Kap will in the background npm install the package to ~/Library/Application Support/Kap/Plugins. Uninstalling works the same. To show what plugins are already installed, Kap just reads the "dependencies" field in the package.json file in the plugins folder. Plugins are auto-updated in the background on Kap startup.

Plugins can come with default config that is editable. As an MVP we’ll do it simple and just open the JSON file in the users editor when they click the Settings button on a plugin. At a later point, I’d like to do what Atom does, and automagically create GUI for the JSON config JSON schema definitions provided by the plugin.

Later on we could also potentially show all plugins on the Kap website using the same npms.io API.

Creating a plugin

A plugin is just a Node.js module that exports some data. It runs in the main process of Electron.

By clearly defining the API we are able to have a stable interface even if our internals change, and it will require very little code by the plugin author (Neither of which would be true with Redux). This proposal is also very much forward thinking and will let us easily extend it in the future.

To illustrate how to create a plugin, let’s make one that uploads a GIF to giphy.com:

'use strict';

const giphy = service => {
    const title = 'Share to Giphy';
    const formats = ['gif'];

    const action = async meta => {
        const endpoint = 'https://upload.giphy.com/v1/gifs';
        const apiKey = service.config.get('apiKey');

        if (!apiKey) {
            service.notify('Missing API key');
            service.openConfigFile();
            return;
        }

        const {data: {id}} = await service.request(endpoint, {
            method: 'post',
            api_key: apiKey,
            file: meta.file,
        });

        service.copyToClipboard(`https://giphy.com/gifs/${id}`);
        service.notify('URL to the ${meta.format} has been copied to the clipboard');
    };

    const defaultConfig = {
        apiKey: ''
    };

    return {title, formats, action, defaultConfig};
};

exports.shareServices = [giphy];

See the Giphy API docs.

When we have decided on this proposal, I’ll make this as the first plugin as a proof of concept.

I’m using async/await here as it’s available in the latest Electron version.

I'm not considering versioning here. If we ever do a v2 of the share services we can simply require a version property. No need to complicate things up-front.

Here we export an array of share services. In the future we might define other kind of services. A plugin can export multiple services if needed.

A service is simply a function that receives a service instance with some utility methods and is expected to return an object with a title, action, and formats property and optionally a defaultConfig property. The title is what will be shown in the share menu in the Kap editor. The action is activated when you click the menu item. The formats is an array of supported formats by the plugin. The defaultConfig property should contain the default config of the plugin.

service.request() is just a wrapper for electron.net that lets us track progress and show it in the GUI.

service.config is a wrapper around electron-config. It enables the plugin to get and set config.

service.copyToClipboard() is simply a tiny wrapper around electron.clipboard.writeBookmark()

service.notify() is just a wrapper around electron.Notifications. Hopefully that PR will be done in time, otherwise we can just work around it by calling the HTML Notifications in the render process.

service.openConfigFile() does what you think. Just opens the plugin config file in the users editor, so they can edit it.

Note to self: In the future if we do the “JSON schema” thing, the user wouldn’t have to handle missing config, like done above with the API key, as they could just define it as required and we could handle the rest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants