Make it. Make it now.
Learn about all the modules available in LaunchMenu:
export const info = {
name: "HelloWorld",
description: "A minimal example applet",
version: "0.0.0",
icon: <img width={30} src={Path.join(__dirname, "..", "images", "icon.png")} />,
};
export const settings = createSettings({
version: "0.0.0",
settings: () => createSettingsFolder({ ...info, children: {
username: createStringSetting({name: "Username", init: "Bob"}),
}}),
});
const items = [
createStandardMenuItem({
name: "Hello world",
onExecute: ({context}) =>
alert(`Hello ${context.settings.get(settings).username.get()}!`)
}),
];
export default declare({
info,
settings,
search: async (query, hook) => ({children: searchAction.get(items)}),
});
LaunchMenu consists of independent composable building blocks, allowing you to create any experience you desire. The interfaces are designed to be as flexible as possible without restricting utility.
Use common web technologies to create applets, including TypeScript and React, which increase the maintainability of your code. Given LaunchMenu is built with Electron your applets will be cross-platform and able to reach a larger audience.
LaunchMenu contains several systems to simplify common scenarios. Applets can make use of the settings system, menus and items, context menu actions, search handlers, themed components and more.
launchmenu-applet
to NPM in order for it to load into the applet ecosystem.const category = createStandardCategory({
name: "Hello"
});
const items = [
createStandardMenuItem({
name: "Hello world",
icon: <img width={30} src={Path.join(__dirname, "..", "images", "icon.png")} />,
content: <Box>Hello world!</Box>,
onExecute: () => alert(`Hello john!`),
}),
createFolderMenuItem({
name: "Some folder",
category,
children: [
createStandardMenuItem({
name: "Bye world",
description: "More items within this directory",
content: <Box>Bye world!</Box>,
onExecute: () => alert(`Bye bob`),
}),
],
}),
];
// Use a cache to keep the same items when typing
const resultCache = new SearchCache((name: string, image: string, species: string) =>
createStandardMenuItem({
name,
description: species,
content: <img width="100%" src={image} />,
onExecute: () => alert(`${name}!`),
})
);
export default declare({
info,
settings,
search: async (query, hook) => {
const rawData = await fetch(
`https://rickandmortyapi.com/api/character?name=${query.search}`
);
const data: {
results: {name: string; image: string; species: string}[];
} = rawData.ok ? await rawData.json() : {results: []};
const items = data.results.map(({name, image, species}) =>
resultCache.get(name, image, species)
);
return {children: searchAction.get(items)};
},
});
import { createContextAction, singlePromptExecuteHandler, promptSelectExecuteHandler,
Priority, createStandardMenuItem } from "@launchmenu/core";
import {Field} from "model-react";
export type ITaskPriority = "high" | "medium" | "low";
export const setTaskPriority = createContextAction({
name: "Set priority level",
contextItem: { priority: Priority.MEDIUM /* Not to be confused with ITaskPriority */ },
core: (fields: Field<ITaskPriority>[]) => {
const executeBinding = singlePromptExecuteHandler.createBinding({
fields,
valueRetriever: ({field}) =>
promptSelectExecuteHandler.createBinding({
field,
options: ["low", "medium", "high"],
createOptionView: level => createStandardMenuItem({name: level}),
}),
});
return {
// Return the bindings for executing the action in the menu
actionBindings: [executeBinding],
};
},
});
import {ITaskPriority, createStandardMenuItem} from "@launchmenu/core";
function createTaskMenuItem({name}: {name: string}) {
const level = new Field<ITaskPriority>("medium");
return createStandardMenuItem({
name,
description: hook => level.get(hook),
onExecute: () => alert(level.get()),
actionBindings: [setTaskPriority.createBinding(level)],
});
}
const items = [
createTaskMenuItem({name: "Meet Alice"}),
createTaskMenuItem({name: "Make pancakes"}),
createTaskMenuItem({name: "Free Hat"}),
];
export const settings = createSettings({
version: "0.0.0",
settings: () =>
createSettingsFolder({
...info,
children: {
username: createStringSetting({name: "Username", init: "Bob"}),
},
}),
});
const Content: FC<{text: string}> = ({text}) => {
const context = useIOContext();
const [hook] = useDataHook();
const name = context?.settings.get(settings).username.get(hook);
return (
<Box color="primary">
{text} {name}!
</Box>
);
};
const items = [
createStandardMenuItem({
name: "Hello world",
content: <Content text="Hello" />,
onExecute: ({context}) =>
alert(`Hello ${context.settings.get(settings).username.get()}!`),
}),
];
const createTimesItem = (name: string) => {
const times = new Field(1);
return createStandardMenuItem({
name: hook => `${name} x${times.get(hook)}`,
// Use a command to allow the user to revert the change
onExecute: () => new SetFieldCommand(times, times.get() + 1),
});
};
const items = [createTimesItem("Hello world"), createTimesItem("Bye world")];