Frontend
Note
The first section covers the experimental v0.2 frontend plugins, while the second half covers the legacy v0.1 frontend plugins.
Frontend plugins are standalone NPM packages that add new widgets and functionality to the tangram web interface. This system is designed for modularity, allowing you to build and share custom UI components.
1. Project Structure¶
A frontend plugin is a standard TypeScript/Vue project that produces a library build.
my-tangram-frontend-plugin/
├── package.json
├── vite.config.ts
└── src/
├── MyWidget.vue
└── index.ts
2. Plugin Entry Point (index.ts)¶
The main file specified in your package.json must export an install function. This function is the plugin's entry point and receives the TangramApi object, which provides methods for interacting with the core application.
import type { TangramApi } from "@open-aviation/tangram/types";
import MyWidget from "./MyWidget.vue";
export function install(api: TangramApi) {
// use the API to register a new widget component.
// the first argument is a unique ID for your widget.
// the second is the Vue component itself.
api.registerWidget("my-widget", MyWidget);
}
The TangramApi provides two main functions:
registerWidget(id: string, component: Component): Makes your component available to the core UI.getVueApp(): App: Provides access to the core Vue application instance for advanced use cases.
3. vite configuration¶
To simplify the build process, tangram provides a shared Vite plugin. This handles the complex configuration needed to build your plugin as a library and generate a plugin.json manifest file.
import { defineConfig } from "vite";
import { tangramPlugin } from "@open-aviation/tangram/vite-plugin";
export default defineConfig({
plugins: [tangramPlugin()],
});
This standardized build produces a dist-frontend directory containing your compiled JavaScript and the manifest file. tangram uses this manifest to discover and load your plugin.
4. Building and using your plugin¶
First, build your frontend assets. If you are in the monorepo, pnpm build will handle this.
Next, ensure the generated dist-frontend directory is included in your Python package's wheel. This is typically done in pyproject.toml.
[tool.hatch.build.targets.wheel.force-include]
"dist-frontend" = "my_plugin/dist-frontend"
Finally, install your Python package and enable it in your tangram.toml:
[core]
plugins = ["my_tangram_plugin"]
When tangram serve runs, it will:
- Serve the static assets from your plugin's
dist-frontenddirectory. - Include your plugin in the
/manifest.jsonendpoint. - The core web app will fetch the manifest and dynamically load and install your plugin.
sequenceDiagram
participant P as Plugin Module
participant B as Browser
participant S as Tangram Server
B->>S: GET /manifest.json
S-->>B: Respond with {"plugins": ["my_plugin"]}
B->>S: GET /plugins/my_plugin/plugin.json
S-->>B: Respond with {"main": "index.js"}
B->>S: GET /plugins/my_plugin/index.js
S-->>B: Serve plugin's JS entry point
Note over B, P: Browser executes plugin code
P->>B: install(tangramApi)
Note over B: Plugin registers its widgets
Danger
The following guide is the legacy v0.1 plugins system and should NOT be used. See the above.
Frontend plugins are Vue.js components that can be dynamically loaded into the tangram web application. The common use case is to get data from the backend (REST API or Websocket) and display it in a custom way, such as on a map or in a table.
Vue components¶
The core Vue components are currently located in the src/components/ directory: plugins can be added to this directory (default fallback location) or in a custom directory, then the full path will be specified as an environment variable.
The usual way to import a component in a vue file is to import it like this:
import MyPlugin from "./components/MyPlugin.vue";
then to include a node in the template:
<MyPlugin />
A plugin component will be imported a bit differently, as it is usually not located in the src/components/ directory. Instead, it will be imported dynamically based on the environment variable TANGRAM_WEB_MYPLUGIN_PLUGIN. The environment variable shall be defined in the .env file, and it should point to the full path of the plugin component file. Then the component should be declared in the vite.config.js file, which is responsible for loading the Vue components dynamically:
plugins: [
// ..., other settings
dynamicComponentsPlugin({
envPath: "../.env",
fallbackDir: "./src/plugins/",
availablePlugins: [
"airportSearch",
"systemInfo",
"sensorsInfo",
"cityPair",
// list all your plugins here
"myPlugin", // This is your custom plugin
],
}),
];
Then you will be able to include the node in the template part of another component like this:
<template>
<plugin-myplugin />
</template>
Tip
There is one use case where it is convenient to have a plugin in the default src/components/ directory.
In some cases, you would like to have several possible implementations for a functionality. This can be done in several Vue files, and you can switch the full path to the file in the .env file. If the environment variable is not defined, the plugin will be loaded from the default src/components/ directory.
TANGRAM_WEB_MYPLUGIN_PLUGIN |
Resulting component path |
|---|---|
| undefined | src/plugins/MyPlugin.vue |
/path/to/plugins/MyPlugin1.vue |
/path/to/plugins/MyPlugin1.vue |
/path/to/plugins/MyPlugin2.vue |
/path/to/plugins/MyPlugin2.vue |
Example usage:
Technical details¶
Registration process¶
When Tangram initializes, the plugin system:
- reads the list of available plugins
- checks for environment variable overrides
- imports components from the specified paths or fallbacks
- registers the components with the Vue application
Hot reloading¶
The plugin system monitors any change to the environment configuration and:
- Invalidates cached module definitions
- Reloads the plugin components
- Triggers a page refresh to show the updated components
Implementation Details¶
The dynamic component system is implemented as a Vite plugin (vite-plugin-dynamic-components.js) that:
- Creates a virtual module (
virtual:plugin-components) at build time - Dynamically generates import statements based on environment configuration
- Exports a
registerComponentsfunction that Vue uses during initialization - Watches for changes to the environment variables
Key functions in the implementation:
createEnvVarsMap(): Collects environment variables with theTANGRAM_WEB_prefixgenerateComponentName(): Converts plugin names to kebab-case component namesload(): Generates the dynamic import and registration codeconfigureServer(): Sets up watchers for hot reloading
Troubleshooting¶
If the component does not load or behaves unexpectedly, consider the following:
- Verify the environment variable path is correct and absolute
- Check console logs for fallback path messages
- Check the web console in process-compose for any errors during component loading
- Ensure the component is correctly registered in
vite.config.js
If you see style conflicts or unexpected behavior:
- Use scoped styles to prevent CSS leaks
- Be aware of global styles that might affect your component
If the hot reloading does not work as expected:
- Verify the env file is being watched correctly
- Check for errors in the console during reload