diff --git a/app/hyperterm.js b/app/hyperterm.js
index 9bb92646..9335e804 100644
--- a/app/hyperterm.js
+++ b/app/hyperterm.js
@@ -1,10 +1,15 @@
-import Tabs from './tabs';
-import Term from './term';
+import Tabs_ from './tabs';
+import Term_ from './term';
import RPC from './rpc';
import Mousetrap from 'mousetrap';
import classes from 'classnames';
import shallowCompare from 'react-addons-shallow-compare';
import React, { Component } from 'react';
+import decorate from './plugins';
+
+// make subcomponents reload siwth plugin changes
+const Tabs = decorate(Tabs_);
+const Term = decorate(Term_);
export default class HyperTerm extends Component {
constructor (props) {
diff --git a/app/index.js b/app/index.js
index bee1cff7..5d0ab9e0 100644
--- a/app/index.js
+++ b/app/index.js
@@ -1,16 +1,14 @@
import { render } from 'react-dom';
-import HyperTerm from './hyperterm';
+import HyperTerm_ from './hyperterm';
import React from 'react';
import Config from './config';
-import Plugins from './plugins';
+import decorate from './plugins';
+
+// make the component reload with plugin changes
+const HyperTerm = decorate(HyperTerm_);
require('./css/hyperterm.css');
require('./css/tabs.css');
-const app =
-
-
-
-;
-
+const app = ;
render(app, document.getElementById('mount'));
diff --git a/app/package.json b/app/package.json
index c1d07b7f..14d41e81 100644
--- a/app/package.json
+++ b/app/package.json
@@ -11,6 +11,7 @@
"react": "15.1.0",
"react-addons-shallow-compare": "15.1.0",
"react-dom": "15.1.0",
+ "react-proxy": "1.1.8",
"semver-compare": "1.0.0"
},
"devDependencies": {
diff --git a/app/plugins.js b/app/plugins.js
index 91955736..253091a0 100644
--- a/app/plugins.js
+++ b/app/plugins.js
@@ -1,18 +1,88 @@
-import React from 'react';
+import { ipcRenderer, remote } from 'electron';
+import notify from './notify';
+import { createProxy } from 'react-proxy';
-export default class Plugins extends React.Component {
+// remote interface to `../plugins`
+let plugins = remote.require('./plugins');
- componentDidMount () {
-
- }
-
- render () {
- const child = React.Children.only(this.props.children);
- return React.cloneElement(child, this.props);
- }
-
- componentWillUnmount () {
+// `require`d modules
+let modules;
+// the fs locations where usr plugins are stored
+const { path, localPath } = plugins.getBasePaths();
+
+// where we store the decorated components
+let proxies = {};
+
+const clearCache = () => {
+ // clear require cache
+ for (const entry in window.require.cache) {
+ if (entry.indexOf(path) === 0 || entry.indexOf(localPath) === 0) {
+ // `require` is webpacks', `window.require`, electron's
+ delete window.require.cache[entry];
+ }
+ }
+};
+
+const loadModules = () => {
+ console.log('(re)loading renderer plugins');
+ const paths = plugins.getPaths();
+ modules = paths.plugins.concat(paths.localPlugins).map((path) => {
+ // window.require allows us to ensure this doens't get
+ // in the way of our build
+ try {
+ return window.require(path);
+ } catch (err) {
+ const name = remote.require('path').basename(path);
+ console.error(err.stack);
+ notify('Plugin load error', `"${name}" failed to load in the renderer process. Check Developer Tools for details.`);
+ }
+ });
+};
+
+const updateProxy = (name) => {
+ const [Component, proxy] = proxies[name];
+ let decorated = Component;
+ modules.forEach((mod) => {
+ const decorator = mod[`decorate${name}`];
+ if (decorator) {
+ console.log('decorating', name);
+ decorated = decorator(Component, __webpack_require__);
+ }
+ });
+ if (decorated !== Component) {
+ proxy.update(decorated);
+ }
+};
+
+const updateProxies = () => {
+ for (const name in proxies) {
+ updateProxy(name);
+ }
+};
+
+// load modules for initial decoration
+loadModules();
+
+// we want to refresh our modules cache every time
+// plugins reload.
+// the re-painting happens by the top-level `Config` component
+// that reacts to configuration changes and plugin changes
+ipcRenderer.on('plugins change', () => {
+ clearCache();
+ loadModules();
+ updateProxies();
+});
+
+export default function decorate (Component, props = null) {
+ const name = Component.name;
+
+ if (!proxies[name]) {
+ const proxy = createProxy(Component);
+ proxies[name] = [Component, proxy];
+ updateProxy(name);
}
+ const [, proxy] = proxies[name];
+ return proxy.get();
}