Skip to content

Commit

Permalink
add support for custom application icons (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
charlespascoe authored Jun 16, 2022
1 parent a77c309 commit 518229f
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 8 deletions.
6 changes: 6 additions & 0 deletions client/src/main/active.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default class Active {
private suggestion: string = "";

app: string = "";
icon?: string;
customCommands: any[] = [];
customHints: any[] = [];
customWords: any[] = [];
Expand Down Expand Up @@ -384,14 +385,18 @@ export default class Active {
? this.languageSwitcherLanguage
: filenameToLanguage(filename);

const icon = plugin?.icon;

const send =
force ||
app != this.app ||
icon != this.icon ||
filename != this.filename ||
language != this.language ||
sourceAvailable != this.sourceAvailable;

this.app = app;
this.icon = icon;
this.filename = filename;
this.language = language;
this.sourceAvailable = sourceAvailable;
Expand All @@ -401,6 +406,7 @@ export default class Active {
this.bridge.setState(
{
app: this.app,
icon: this.icon,
dictateMode: this.dictateMode,
filename: this.filename,
firstPartyPluginAvailable: this.firstPartyPluginAvailable(),
Expand Down
3 changes: 2 additions & 1 deletion client/src/main/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ export default class App {
mainWindow,
miniModeWindow,
pluginManager,
stream
stream,
log
);

await custom.start();
Expand Down
14 changes: 11 additions & 3 deletions client/src/main/ipc/plugin-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type Plugin = {
lastActive: number;
websocket: WebSocket;
match?: string;
icon?: string;
};

export default class PluginManager {
Expand All @@ -35,10 +36,16 @@ export default class PluginManager {
this.plugins = result;
}

private updatePlugin(websocket: WebSocket, id: string, app: string, match?: string) {
private updatePlugin(websocket: WebSocket, id: string, app: string, match?: string, icon?: string) {
const plugin = this.fromId(id);
if (plugin) {
plugin.websocket = websocket;

// only update the icon if it has a value. an empty string clears the
// custom icon
if (icon != undefined) {
plugin.icon = icon;
}
} else {
if (app == "intellij") {
app = "jetbrains";
Expand All @@ -49,6 +56,7 @@ export default class PluginManager {
app,
websocket,
match,
icon,
lastActive: Date.now(),
lastHeartbeat: Date.now(),
});
Expand Down Expand Up @@ -148,8 +156,8 @@ export default class PluginManager {
});
}

updateActive(websocket: WebSocket, id: string, app: string, match?: string) {
this.updatePlugin(websocket, id, app, match);
updateActive(websocket: WebSocket, id: string, app: string, match?: string, icon?: string) {
this.updatePlugin(websocket, id, app, match, icon);
this.fromId(id)!.lastActive = Date.now();
}

Expand Down
21 changes: 19 additions & 2 deletions client/src/main/ipc/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import PluginManager from "./plugin-manager";
import RendererBridge from "../bridge";
import Stream from "../stream/stream";
import { core } from "../../gen/core";
import Log from "../log";

const maximumIconLength = 20000;

export default class IPCServer {
private server: WebSocket.Server;
Expand All @@ -18,7 +21,8 @@ export default class IPCServer {
private mainWindow: MainWindow,
private miniModeWindow: MiniModeWindow,
private pluginManager: PluginManager,
private stream: Stream
private stream: Stream,
private log: Log
) {
this.server = new WebSocket.Server({ host: "localhost", port: 17373 });
this.server.on("connection", (websocket) => {
Expand All @@ -27,11 +31,24 @@ export default class IPCServer {

// protocol messages from plugins
if (request.message == "active") {
let icon = request.data.icon;

const iconValid = (
icon == undefined ||
(typeof icon == 'string' && icon.startsWith('data:') && icon.length <= maximumIconLength)
);

if (!iconValid) {
this.log.logVerbose('Plugin provided an app icon that does not adhere to requirements');
icon = undefined;
}

this.pluginManager.updateActive(
websocket,
request.data.id,
request.data.app,
request.data.match
request.data.match,
icon,
);
} else if (request.message == "callback") {
this.pluginManager.resolve(request.data.callback, request.data.data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,16 @@ const apps: { [key: string]: { name: string; icon: string } } = {
const ActiveAppIndicatorComponent: React.FC<{
app: string;
pluginInstalled: boolean;
}> = ({ app, pluginInstalled }) => {
customIcon?: string;
}> = ({ app, pluginInstalled, customIcon }) => {
let name = app;
if (Object.keys(apps).includes(app)) {
name = apps[app].name;
} else if (!pluginInstalled) {
name = "Other";
}

const icon = Object.keys(apps).includes(app) ? apps[app].icon : windowIcon;
const icon = customIcon || (Object.keys(apps).includes(app) ? apps[app].icon : windowIcon);
return (
<div className="block text-xs drop-shadow-sm px-1.5 py-0.5 ">
<img
Expand All @@ -69,4 +70,5 @@ const ActiveAppIndicatorComponent: React.FC<{
export const ActiveAppIndicator = connect((state: any) => ({
app: state.app,
pluginInstalled: state.pluginInstalled,
customIcon: state.icon,
}))(ActiveAppIndicatorComponent);
1 change: 1 addition & 0 deletions client/src/renderer/state/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const initialState = {
executeSilenceThreshold: 1,
filename: "",
highlighted: [],
icon: "",
language: core.Language.LANGUAGE_DEFAULT,
languageSwitcherLanguage: core.Language.LANGUAGE_NONE,
latency: 1,
Expand Down
15 changes: 15 additions & 0 deletions web/src/components/docs/protocol/getting-started.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,21 @@ export const Connecting: React.FC = () => (
name. For instance, a plugin for Atom would supply a <code>match</code> of <code>atom</code>
, so when a process containing <code>atom</code> is in the foreground.
</li>
<li>
<code>icon</code>: The application icon to show in the main Serenade window when the
application is active. Must be encoded as a{" "}
<a
href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs"
className="text-purple-500 hover:text-purple-600 transition-colors cursor-pointer"
target="_blank"
aria-label="Data URLs - HTTP | MDN"
title="Data URLs - HTTP | MDN"
>data URL</a>
{" "}string and cannot be more than 20,000 characters long; ideally use a small (less than
48x48 pixels) version of the application icon. This field is only needed in the first{" "}
<code>active</code> message or whenever the icon changes (e.g. to show custom status
icons), and may be left out entirely if an icon isn't requred.
</li>
</ul>
<p>
Here's a snippet that opens a WebSocket connection and sends the <code>active</code> message.
Expand Down
15 changes: 15 additions & 0 deletions web/src/components/docs/protocol/messages-reference.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,21 @@ export const Content = () => (
a <code>match</code> of <code>atom</code>, so it will match any process with{" "}
<code>atom</code> in its name.
</li>
<li>
<code>icon</code> The application icon to show in the main Serenade window when the
application is active. Must be encoded as a{" "}
<a
href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs"
className="text-purple-500 hover:text-purple-600 transition-colors cursor-pointer"
target="_blank"
aria-label="Data URLs - HTTP | MDN"
title="Data URLs - HTTP | MDN"
>data URL</a>
{" "}string and cannot be more than 20,000 characters long; ideally use a small (less than
48x48 pixels) version of the application icon. This field is only needed in the first
<code>active</code> message or whenever the icon changes (e.g. to show custom status
icons), and may be left out entirely if an icon isn't requred.
</li>
</ul>
<Subsubheading title="heartbeat" />
<p>Sent by a plugin to keep the connection alive with the Serenade app.</p>
Expand Down

0 comments on commit 518229f

Please sign in to comment.