Git Product home page Git Product logo

create-cerebro-plugin's Introduction

Create Cerebro Plugin

Fastest way to create Cerebro plugins.

Quick Overview

Before start make sure you have installed yarn package manager. Follow installation instructions if you don't.

yarn create cerebro-plugin my-plugin
cd ./my-plugin
yarn start

Plugins

A Cerebro plugin is just a javascript module. All you need is to write a function, that takes one object and call a function from arguments with your results.

You can create your plugin using create-cerebro-plugin so you can focus on code of your plugin, not on tools and configuration around it.

Prerequisites

Install and manage custom plugins

Sometimes you need to manually install a plugin (maybe you have published it to npm but you dind't added the keywords to the package.json so it is not available in Cerebro). If you want to test this plugin, you can install it manually:

  1. Open a terminal in the configuration directory of Cerebro

  2. Go to the plugins directory

    cd ./plugins
  3. Install the plugin

    npm install --save name-of-plugin
  4. Restart Cerebro

Plugin structure

This is a minimum source code of your plugin:

export const fn = (scope) => console.log(scope.term)

You can open the developer tools by pressing ctrl+shift+i(for the main window) and ctrl+shift+b(for the background). Developer mode should be enabled from the settings

This plugin will write to console all changes in your search field of Cerebro app. So, fn key is a heart of your plugin: this function receives scope object and you can send results back to Cerebro. Scope object is:

  • termString, entered by Cerebro user;
  • displayFunction(result: Object | Array<object>), display your result
  • updateFunction(id: String, result: Object), update your previously displayed result. This action updates only passed fields, so if you displayed result {id: 1, title: 'Result'} and call update(1, {subtitle: 'Subtitle'}), you will have merged result: {id: 1, title: 'Result', subtitle: 'Subtitle'};
  • hideFunction(id: String), hide result from results list by id. You can use it to remove temporar results, like "loading..." placeholder;
  • actions – object with main actions, provided for cerebro plugins:
    • openFunction(path: String), open external URL in browser or open local file;
    • revealFunction(path: String), reveal file in finder;
    • copyToClipboardFunction(text: String), copy text to clipboard;
    • replaceTermFunction(text: String), replace text in main Cerebro input;
    • hideWindowFunction(), hide main Cerebro window.
  • settings - Object, contains user provided values of all specified settings keys;

Let's show something in results list:

export const fn = (scope) => {
  scope.display({
    title: 'It works!',
    subtitle: `You entered ${scope.term}`
  })
}

scope.display accepts one result object or array of result objects. Result object is:

Basic fields

title

Type: String

Title of your result;

subtitle

Type: String

Subtitle of your result;

icon

Type: String

Icon, that is shown near your result. It can be absolute URL to external image, absolute path to local image or base64-encoded data-uri.

For local icons you can use path to some .app file, i.e. /Applications/Calculator.app will render default icon for Calculator application.

Advanced fields

id

Type: String Use this field when you need to update your result dynamically. Check id example

term

Type: String

Autocomplete for your result. So, user can update search term using tab button;

clipboard

Type: String

Text, that will be copied to clipboard using cmd+c, when your result is focused;

getPreview

Type: Function

Arguments: no

Function that returns preview for your result. Preview can be an html string or React component;

onSelect

Type: Function. Arguments: event: Event

Action, that should be executed when user selects your result. I.e, to open provided url in default browser:

onSelect: (event) => actions.open(`http://www.cerebroapp.com`),

If you don't want to close main window after your action, you should call event.preventDefault() in your action.

onKeyDown

Type: Function

Arguments: event: Event

Handle keyboard events when your result is focused, so you can do custom actions, i.e. reveal file in finder by cmd+r (or ctrl+r on windows and linux):

onKeyDown: (event) => {
  if ((event.metaKey || event.ctrlKey) && event.keyCode === 82) {
    actions.reveal(path);
    event.preventDefault();
  }
}

You can also prevent default action by event.preventDefault().

Advanced plugin fields

Along with fn, your module could have more keys:

keyword

Type: String

This field is used for autocomplete. You can prefix your plugin usage by this keyword. Checkout emoji example

name

Type: String

This field is also used for autocomplete and shown as title in results list. Checkout emoji example

initialize

Type: Function Arguments: no

Use this function, when you need to prepare some data for your plugin on start. If you need to do some long-running processes, check initializeAsync

Check initialize example

initializeAsync

Type: Function

Arguments: callback: Function(message: Object) – callback to send data back to main process.

Use this function when you need to execute some long-running initializer process. I.e. in contacts plugin we are fetching all contacts using osx-specific libraries using nodobjc module.

This function will be executed in another process and you can receive results using onMessage function.

Check initializeAsync and onMessage example

onMessage

Type: Function Arguments: message: Object – object that you sent from initializeAsync

Use this function to receive data back from your initializeAsync function.

Check initializeAsync and onMessage example

settings

Type: Object

This object is used to specify settings that a plugin user can change. Each setting should include a description and a type. Other keys include:

  • label - String, object key for the setting. also used to access it;
  • description - String, description of the setting;
  • type - String, used to decide element for rendering a setting:
    • string
    • number
    • bool
    • option
  • defaultValue - Any, default value for the setting;
  • options - Array, all possible options that can be selected by the user. applicable only for option;
  • multi - Bool, allows user to select more than one option for option settings. applicable only for option;
  • createable - Bool, allows user created options. applicable only for option;

Check settings example

Take a look at React Select for more details on how the option type works.

Available env variables

The following variables are available in the process.env object:

  • CEREBRO_VERSION – Version of Cerebro
  • CEREBRO_DATA_PATH – Path to Cerebro data directory

Styles for your plugin preview

Currently if you want to reuse main app styles, you can use CSS variables from main themes (light, dark)

It is better to reuse css variables so custom themes can affect not only main app styles, but your plugin too.

Example (reuse main border styles):

.item {
  border-bottom: var(--main-border);
}

Reusable components

Share

When your plugin is ready, you can share it with all Cerebro users so they can find and install it using plugins command in Cerebro.

All you need is to publish your module to npm. Just run from your plugin folder:

npm publish ./

If you have any problems check out publishing packages in npm documentation

Checklist

  1. Update your repository Readme.md, add screenshot or gif;
  2. Push your plugin to open github repository – this repository is used by cerebro, at least to show Readme.md of your plugin;
  3. Make sure that you have changed package.json metadata: module name, description, author and link to github repository;
  4. Add cerebro-plugin keyword to package.json keywords section. Otherwise your plugin won't be shown in Cerebro;

Examples

You always can check out source code of existing plugins, like:

Using id

export const fn = ({display}) => {
  display({
    id: 'my-id',
    title: 'Loading'
  })
  fetchResult().then((result) => {
    display({
      id: 'my-id',
      title: `Fetched result: ${result}`
    })
  });
}

Using icon

import icon from '[path-to-icon]/icon.png';

const plugin = ({display}) => {
  display({
    icon,
    title: 'Title',
    subtitle: 'Subtitle'
  });
}

export default {
  fn: plugin,
}

Using keyword and name

const plugin = (scope) => {
  const match = scope.term.match(/^emoj\s(.+)/);
  if (match) {
    searchEmojis(match[1]).then(results => {
      scope.display(results)
    })
  };
}

export default {
  name: 'Search emojis...',
  fn: plugin,
  keyword: 'emoj'
}

Using initialize

// Some data needed for your plugin
let data;

// Fetch some data only on app initialization
const initialize = () => {
  fetchYourData().then(result => {
    data = result
  });
}

const plugin = (scope) => {
  const results = search(data, scope.term);
  scope.display(results);
}

export default {
  initialize: initialize,
  fn: plugin
}

Using initializeAsync and onMessage

let data;

// Run some long-running initialization process in background
const initialize = (cb) => {
  fetchYourData().then(cb);
  // and re-fetch this information once in 1h
  setInterval(() => {
    initialize(cb);
  }, 60 * 3600);
}

const onMessage = (results) => {
  data = results;
}

const plugin = (scope) => {
  const results = search(data, scope.term);
  scope.display(results);
}

export default {
  initializeAsync: initialize,
  onMessage: onMessage,
  fn: plugin
}

Using cerebro-tools

import { memoize, search } from 'cerebro-tools';
import preprocessJson from './preprocessJson';

// Memoize your fetched data from external API
const fetchData = memoize(() => {
  return fetch('http://api/url')
    .then(response => response.json())
    .then(preprocessJson)
});

const plugin = ({term, display}) => {
  fetchData().then(data => {
    const results = search(data, term, (el) => el.name);
    display(term);
  })
}

export default {
  fn: plugin
};

using settings

const plugin = ({ display, settings }) => {
  const icon = require('[path-to-icon]/icon.png');

  display({
    icon: settings.icon ? icon : '',
    title: `${settings.username}, you have been around for ${settings.age}`,
    subtitle: `Favorite languages: ${settings.languages.join(',')}`,
  })
}

export default {
  fn: plugin,
  settings: {
    username: { type: 'string' },
    age: { type: 'number', defaultValue: 42 },
    icon: { type: 'bool' },
    languages: {
      type: 'option',
      description: 'Your favorite programming languages'
      options: [
        { label: 'JavaScript', value: 'js' },
        { label: 'Haskell', value: 'hsk' },
        { label: 'Rust', value: 'rs' }
      ],
      multi: true,
      createable: true,
    }
  }
}

create-cerebro-plugin's People

Contributors

brpaz avatar dubisdev avatar kelion avatar ogustavo-pereira avatar rzvnb avatar seldaek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

create-cerebro-plugin's Issues

Add an option to build script to skip minification

In some plugins minification breaks the code, in those plugins it makes sense to skip minification. Would be great to have an option to skip it in build script of cerebro-scripts, so user can update build script in package.json to cerebro-scripts build --skip-minification

it's 2020!!

can i creat plugin now?

using Windows 10 device

When creating a plugin, the init script fails

Console Error:

<dir>/cerebro/cerebro-plugin-convert/node_modules/cerebro-scripts/scripts/init.js:57
            throw err;
            ^

Error: ENOENT: no such file or directory, stat '<dir>/cerebro/cerebro-plugin-convert/npmignore'
    at Error (native)
error Command failed.
Exit code: 1
Command: ~/.config/yarn/global/node_modules/create-cerebro-plugin/index.js
Arguments: cerebro-plugin-convert
Directory: ~/cerebro
Output:

info Visit https://yarnpkg.com/en/docs/cli/create for documentation about this command.

It seems to fail because npmignore doesn't exist... except for that when I opened the directory in my text editor, there indeed was an npmignore file, which I had to manually convert to .npmignore

Test locally before publish

Hey,
When I started with my own plugin, using this here to init, how can I add it to my locally installed cerebro and try it out?
In the plugin documentation it is mentioned to public it on npm with the correct tags, so the cerebro plugin manager can find it. Is there a way to first test it locally before publish?
Thanks for help! :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.