Podman Desktop is organized so that you can modularly add new functionality in the form of "extensions" as well as the corresponding extension-api. This allows you to communicate with Podman Desktop without having to know the internal-workings. You look for the API call and Podman Desktop will do the rest.
Most extensions are externally loaded, however, we also dog-food our own API by loading them as [internal extensions](https://github.com/containers/podman-desktop/tree/main/extensions) that use the same API. These internal maintained extensions can be used as an example and basis of how to build an externally-loaded extension.
## Overview of creating a new extension
We try to simplify extension creation as much as possible by utilizing `package.json` as well as keeping activations simplistic within the extension by only providing two entrypoints: `activate()` and `deactivate()` from within the extension.
All functionality with Podman Desktop is also communicated entirely through the `extension-api` which is loaded as `import * as extensionApi from '@podman-desktop/api';`. The API code is located [here](https://github.com/containers/podman-desktop/blob/main/packages/extension-api/src/extension-api.d.ts) while the website representation of the code is located [here](https://podman-desktop.io/api).
### Activating
When activating an extension, Podman Desktop will:
1. Search and load the JavaScript file specified in `main` entry of the `package.json` file in the extension directory (typically `extension.js`).
2. Run the exported `activate` function.
### Deactivating
When deactivating an extension, Podman Desktop will:
1. Run the (optional) exported `deactivate` function.
2. Dispose of any resources that have been added to `extensionContext.subscriptions`, see `deactivateExtension` in [extension-loader.ts](https://github.com/containers/podman-desktop/blob/main/packages/main/src/plugin/extension-loader.ts).
This is an example `extensions/foobar/src/extensions.ts` file with the basic `activate` and `deactivate` functionality, provided that you already have a `package.json` created as well:
Keep in mind that the above example is not a full representation of every functionality an extension can be used for. Examples such as creating a new provider, new commands, expanding the internal Podman Desktop functionality can also be implemented. See our [API documnentation](https://podman-desktop.io/api) for more information.
Below is documentation and/or "boiler-plate" code that can help expand your extension.
### Using `ProviderStatus`
Podman Desktop runs each provider via series of statuses from [extension-api](https://github.com/containers/podman-desktop/blob/main/packages/extension-api/src/extension-api.d.ts).
```ts
export type ProviderStatus =
| 'not-installed'
| 'installed'
| 'configured'
| 'ready'
| 'started'
| 'stopped'
| 'starting'
| 'stopping'
| 'error'
| 'unknown';
```
`ProviderStatus` supplies information to the main Provider page detailing whether or not that Provider is installed, ready, started, stopped, etc.
This can be updated throughout your extension by calling for example: `provider.updateStatus('installed')`. Podman Desktop will show the status on the main screen.
> **_NOTE:_** ProviderStatus is for information purposes only and can be used from within the extension to keep track if `activate()` and `deactivate()` are working correctly.
> **_NOTE:_** The `unknown` status is unique as it will not show in the extension section of Podman Desktop, it will also not be accessible via API calls. Unknown statuses typically happen when Podman Desktop is unable to load the extension.
`ProviderConnectionStatus` is the main "Lifecycle" of your extension. The status is updated automatically by Podman Desktop and reflected within the provider.
Upon a successful start up via the `activate` function within your extension, `ProviderConnectionStatus` will be reflected as 'started'.
`ProviderConnectionStatus` statuses are used in two areas, [extension-loader.ts](https://github.com/containers/podman-desktop/blob/main/packages/main/src/plugin/extension-loader.ts) and [tray-menu.ts](https://github.com/containers/podman-desktop/blob/main/packages/main/src/tray-menu.ts):
-`extension-loader.ts`: Attempts to load the extension and sets the status accordingly (either `started`, `stopped`, `starting` or `stopping`). If an unknown error has occurred, the status is set to `unknown`. `extension-loader.ts` also sends an API call to Podman Desktop to update the UI of the extension.
-`tray-menu.ts`: If `extensionApi.tray.registerMenuItem(item);` API call has been used, a tray menu of the extension will be created. When created, Podman Desktop will use the `ProviderConnectionStatus` to indicate the status within the tray menu.
### Adding commands
## Commands
Declare commands using `contributes` section of package.json file.
```json
"contributes": {
"commands": [
{
"command": "my.command",
"title": "This is my command",
"category": "Optional category to prefix title",
"enablement": "myProperty === myValue"
},
],
}
```
If optional `enablement` property evaluates to false, command palette will not display this command.
To register the callback of the command, use the following code:
```ts
import * as extensionApi from '@podman-desktop/api';
await extensionApi.window.showInformationMessage('Clicked on my command');
});
);
```
### Expanding the `extension-api` API
Sometimes you'll need to add new functionality to the API in order to make an internal change within Podman Desktop. An example would be a new UI/UX component that happens within the renderer, you'd need to expand the API in order to make that change to Podman Desktop's inner-workings.
Please note that an API contribution is **subject to approval** as we want to maintain sustainability / consistency in the API. A discussion within an issue would be beneficial before writing code.
In this example, we'll add a new function to simply display: "hello world" in the console.
1. Add the new function to `/packages/extension-api/src/extension-api.d.ts`, under a namespace. This will make it accessible within the API when it's being called within your extension:
2. The `packages/main/src/plugin/extension-loader.ts` acts as an extension loader that defines all the actions needed by the API. Modify it to add the main functionality of `hello()` under the `foobar` namespace const: <!-- markdownlint-disable-line MD029 -->
3. The above code won't work until we've created the class! So let's create a `packages/main/src/plugin/foobar-client.ts` file with the functionality: <!-- markdownlint-disable-line MD029 -->
4. An instance of this class needs to be created and passed to the constructor of the `ExtensionLoader`, in `packages/main/src/plugin/index.ts`: <!-- markdownlint-disable-line MD029 -->
For example if you contribute a property named `podman.binary.path` it will display `Path` in Podman Desktop UI setting, and if you change it to `podman.binary.pathToBinary` it becomes `Path To Binary` in the title.