Entrypoint Generator
Overview
Entrypoint generator is used for procedural generation of entrypoints.
Functionality
- Dynamically add, remove, update one or more entrypoints at any point in plugin lifetime
- Generated entrypoints can have accessories
- Generated entrypoints can have multiple actions, each of which can be a command or a view
For performance reasons, the entrypoints are generated once and stored for later use instead of being generated each time when main window is opened. This means that after initial generator run the only way to add, delete or remove generated entrypoint is based on some external event e.g. file change
Plugin Manifest
To use entrypoint generator, entrypoint with type "entrypoint-generator"
is required
Example
[[entrypoint]]
id = 'main'
name = 'Main'
path = 'src/main.tsx'
type = 'entrypoint-generator'
description = 'Description of a entrypoint generator'
Note on usage of async
JavaScript is a single-threaded language, at any time only one chunk of code is being executed.
This has implications on Gauntlet plugins.
Let's say in the same plugin you have 2 entrypoints: view and entrypoint generator, let's also say that your generator entrypoint needs to scan some directories. If you are using synchronous versions of functions to access file system, user will not be able to open any views because JS runtime is being busy with scanning directories. For this reason prefer using async versions of APIs where possible, or if your code is computationally intensive, offload the computation to a worker
Examples
Simple
- src/simple.tsx
- gauntlet.toml
import { GeneratorContext } from "@project-gauntlet/api/helpers";
import { ReactElement } from "react";
import { List } from "@project-gauntlet/api/components";
function ListView(): ReactElement {
return (
<List>
<List.Item id="example-item" title="Example Item"/>
</List>
)
}
export default function EntrypointGenerator({ add }: GeneratorContext): void {
add('generated', {
name: 'Generated Command',
actions: [
{
label: "Run the Gauntlet",
run: () => {
console.log('Running the Gauntlet...')
}
}
]
})
add('generated', {
name: 'Generated View',
actions: [
{
label: "Open generated view",
view: () => <ListView/>
}
]
})
}
Update based on file system events
This example shows how the name of the generated item can be dependent on the content of the file and will be updated when content of the file is updated
- src/fs-events.tsx
- gauntlet.toml
import { GeneratorContext } from "@project-gauntlet/api/helpers";
export default async function EntrypointGenerator({ add, remove }: GeneratorContext): Promise<void | (() => void)> {
const id = "generated";
const path = "/tmp/gauntlet-example";
await Deno.create(path)
add(id, generatedItem("default"))
const watcher = Deno.watchFs(path);
(async () => {
for await (const _event of watcher) {
try {
const value = await Deno.readTextFile(path);
add(id, generatedItem(value))
} catch (err) {
remove(id)
}
}
})();
return () => {
watcher.close()
}
}
function generatedItem(value: string) {
return {
name: `Generated Command - ${value}`,
actions: [
{
label: "Run the Gauntlet",
run: () => {
console.log('Running the Gauntlet...')
}
}
]
};
}
Accessories
- src/accessories.tsx
- gauntlet.toml
import { GeneratorContext } from "@project-gauntlet/api/helpers";
import { Icons } from "@project-gauntlet/api/components";
export default function EntrypointGenerator({ add }: GeneratorContext): void {
add('generated', {
name: 'Generated Command',
actions: [
{
label: "Run the Gauntlet",
run: () => {
console.log('Running the Gauntlet...')
}
}
],
accessories: [{ icon: Icons.Battery, text: "100 %" }]
})
}
Icons
- src/icons.tsx
- gauntlet.toml
import { GeneratorContext } from "@project-gauntlet/api/helpers";
export default async function EntrypointGenerator({ add }: GeneratorContext): Promise<void> {
const response = await fetch("https://img.icons8.com/?size=32&id=21276&format=png");
const arrayBuffer = await response.bytes();
add('generated', {
name: 'Generated Command',
actions: [
{
label: "Run the Gauntlet",
run: () => {
console.log('Running the Gauntlet...')
}
}
],
icon: arrayBuffer
});
}
Preferences
Try changing the preference values in Settings UI, the change will be automatically reflected in the entrypoint list
- src/preferences.tsx
- gauntlet.toml
import { GeneratorContext } from "@project-gauntlet/api/helpers";
type PluginGeneratorContext = GeneratorContext<{ testBool: boolean }, { testStr: string }>;
export default function EntrypointGenerator({ add, pluginPreferences, entrypointPreferences }: PluginGeneratorContext): void {
add('generated', {
name: 'Generated Command - ' + entrypointPreferences.testStr,
actions: [
{
label: "Run the Gauntlet",
run: () => {
console.log('Running the Gauntlet... ' + pluginPreferences.testBool)
}
}
],
});
}
[[preferences]]
id = 'testBool'
name = 'Test Boolean Preference'
type = 'bool'
default = true
description = ""
[[entrypoint]]
id = 'preferences'
name = 'Preferences'
path = 'src/preferences.tsx'
type = 'entrypoint-generator'
description = ''
[[entrypoint.preferences]]
id = 'testStr'
name = 'Test String Preference'
type = 'string'
default = 'test_value'
description = ""
Action shortcuts
It is possible to assign specific shortcut to Entrypoint Action that works when the Entrypoint is focused
See Plugin Manifest for more information on meaning of manifest property values
- src/action-shortcut.tsx
- gauntlet.toml
import { GeneratorContext } from "@project-gauntlet/api/helpers";
type PluginGeneratorContext = GeneratorContext<{ testBool: boolean }, { testStr: string }>;
export default function EntrypointGenerator({ add }: PluginGeneratorContext): void {
add('generated', {
name: 'Generated Command',
actions: [
{
label: "Primary Action", // Executed when Enter is pressed
run: () => {
console.log('Running the Gauntlet... - Primary Action')
}
},
{
label: "Secondary Action", // Executed when Shift+Enter is pressed
run: () => {
console.log('Running the Gauntlet... - Secondary Action')
}
},
{
ref: "anotherAction", // Executed when pressing shortcut specified in Plugin Manifest
label: "Another Action",
run: () => {
console.log('Running the Gauntlet... - Another Action')
}
}
],
});
}