Industrial Extension Development Tips
This page covers compatibility issues and best practices when developing extensions for industrial robots.
Environment Limitations
alert and confirm Functions Are Not Available
In the industrial robot extension environment, native alert() , confirm() , and prompt() functions cannot be used.
Problem Description
The WebView environment on teach pendants disables these blocking dialog functions. Calling them directly will result in:
alert()- No response or silent failureconfirm()- Unable to obtain user confirmationprompt()- Unable to obtain user input
Recommended Solution
We strongly recommend using UI frameworks (such as Element UI, Ant Design, etc.) or the runtime API directly to implement notifications and dialogs, instead of using the browser's native alert / confirm / prompt functions.
The runtime API provides complete notification and dialog functionality:
Using Runtime API (Recommended):
The way you use runtime APIs depends on your development approach:
Traditional Approach (directly including script tags in HTML): Use the global object
gbtExtensionDownload the runtime library to your local project using these methods:
bash# Method 1: Install via npm and copy pnpm add @agilebot/extension-runtime cp ./node_modules/@agilebot/extension-runtime/dist/browser.js ./static/extension-runtime.js # Method 2: Download directly from unpkg to your project curl -o ./static/extension-runtime.js https://unpkg.com/@agilebot/extension-runtime/dist/browser.js # Or use wget wget -O ./static/extension-runtime.js https://unpkg.com/@agilebot/extension-runtime/dist/browser.jsModule-based Approach (using build tools and modular development): Use
importsyntaxtsimport {rtmNotification,rtmMessageBox} from '@agilebot/extension-runtime'; // Show notificationsrtmNotification.success('Operation successful!');rtmNotification.error('Operation failed');rtmNotification.info('Information message'); // Show confirmation dialogrtmMessageBox.confirm('Are you sure you want to delete?') .then(() =>console.log('User confirmed')) .catch(() =>console.log('User cancelled')); // Get user inputrtmMessageBox.prompt('Please enter your name:') .then((result) =>console.log('User input:',result)) .catch(() =>console.log('User cancelled'));Modern build tools (like Vite, Webpack) will automatically bundle dependencies into the final files, no additional handling needed.
For more details on runtime APIs, please refer to Web Extension Runtime Library.
Alternative Solution: Compatibility Wrapper for Native alert/confirm
Use Only When Necessary
Only use the following compatibility wrapper when your project already extensively uses native alert() / confirm() functions and cannot be refactored.
For new projects or projects that can be refactored, please use the recommended runtime API or UI frameworks directly.
If you must maintain compatibility with native alert() / confirm() , you can wrap them with a compatibility layer:
Traditional Approach Compatibility Example:
// Check if running in extension environment
const isExtension = gbtExtension.isInExtension();
// Compatibility wrapper for alert
const alertSuccess = !isExtension
? alert
: gbtExtension.rtmNotification.success;
const alertError = !isExtension
? alert
: gbtExtension.rtmNotification.error;
const alertInfo = !isExtension
? alert
: gbtExtension.rtmNotification.info;
// Compatibility wrapper for confirm (async style)
const myConfirm = function(message) {
if (!isExtension) {
return Promise.resolve(confirm(message));
}
return new Promise((resolve) => {
gbtExtension.rtmMessageBox.confirm(message)
.then(() => resolve(true))
.catch(() => resolve(false));
});
};
// Usage example
alertSuccess('Operation successful!');
async function deleteItem() {
const confirmed = await myConfirm('Are you sure you want to delete?');
if (confirmed) {
console.log('User confirmed deletion');
}
}Module-based Approach Compatibility Example:
import { isInExtension, rtmNotification, rtmMessageBox } from '@agilebot/extension-runtime';
const isExtension = isInExtension();
// Compatibility wrapper for alert
const alertSuccess = !isExtension
? alert
: rtmNotification.success;
const alertError = !isExtension
? alert
: rtmNotification.error;
const alertInfo = !isExtension
? alert
: rtmNotification.info;
// Compatibility wrapper for confirm (async style)
const myConfirm = function(message: string) {
if (!isExtension) {
return Promise.resolve(confirm(message));
}
return new Promise((resolve) => {
rtmMessageBox.confirm(message)
.then(() => resolve(true))
.catch(() => resolve(false));
});
};Important Notes
When using the compatibility wrapper:
- The
confirmreplacement is asynchronous (returns a Promise), you must useawaitor.then()to handle it - Native
confirm()is synchronous, but the wrapper is async, which may require code structure adjustments alertis replaced with notifications, which have a different visual appearance than native dialogs- It's recommended to encapsulate these functions uniformly in your project entry file to avoid code duplication
Code Transpilation Configuration
Why Transpilation Is Needed
Extension frontend pages run in different device WebView environments:
Industrial Robots: Since teach pendants run Android systems (like Android 13) whose WebView may not support ES6+ features (like arrow functions, async/await, optional chaining, etc.), transpilation configuration is required to transpile modern JavaScript code to ES5-compatible code.
Collaborative Robots: Collaborative robots run newer versions of Android systems, and their WebView has good support for modern JavaScript features, so transpilation is usually not needed.
Tip
If you are developing extensions for industrial robots, you need to configure transpilation according to this document. If you are developing for collaborative robots, you most likely don't need transpilation configuration and can use modern JavaScript syntax directly.
Scenario 1: Using Babel Transpilation (Traditional Approach)
If you use the traditional approach, i.e., not using modern build tools but writing native JavaScript directly, you can use Babel to transpile code.
Configuration Example
Here's a complete package.json configuration example:
{
"name": "my-extension",
"private": true,
"scripts": {
"build": "babel ./static/script.js --out-file=./static/script-compiled.js"
},
"devDependencies": {
"@babel/cli": "^7.27.0",
"@babel/core": "^7.26.10",
"@babel/preset-env": "^7.26.9"
},
"babel": {
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"targets": {
"android": "13",
"chrome": "80"
}
}
]
]
}
}Key Configuration Details
Dependencies:
@babel/cli: Babel command-line tool@babel/core: Babel core library@babel/preset-env: Smart preset that automatically transpiles based on target environment
Transpilation Configuration:
json"babel": { "presets": [ [ "@babel/preset-env", { "modules": false, "targets": { "android": "13", "chrome": "80" } } ] ] }Build Script:
bashpnpm buildThis will transpile
./static/script.jsto./static/script-compiled.js
Usage Example
Suppose you have the following modern JavaScript code:
// Using arrow functions and async/await
const fetchData = async () => {
const response = await fetch('/api/data');
const data = await response.json();
return data?.results ?? [];
};
// Using optional chaining and nullish coalescing
const getUserName = (user) => {
return user?.profile?.name ?? 'Anonymous';
};After running pnpm build , Babel will transpile it to ES5-compatible code:
// Transpiled code (example)
var fetchData = function fetchData() {
return regeneratorRuntime.async(function fetchData$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return regeneratorRuntime.awrap(fetch('/api/data'));
case 2:
response = _context.sent;
// ...
}
}
});
};Then reference the transpiled file in HTML:
<script src="./static/script-compiled.js"></script>Scenario 2: Using Vite Legacy Plugin (Module-based Approach)
If you use modern build tools like Vite or Webpack, you can use the corresponding Legacy plugin to handle transpilation.
Vite Configuration Example
Install Plugin:
bashpnpm add -D @vitejs/plugin-legacyConfigure
vite.config.ts:tsimport {fileURLToPath,URL} from 'node:url' import {defineConfig} from 'vite' importvuefrom '@vitejs/plugin-vue' importlegacyfrom '@vitejs/plugin-legacy' export defaultdefineConfig({base: './',plugins: [vue(),legacy({targets: ['android >= 13', 'chrome >= 80'],modernPolyfills: true, }), ],resolve: {alias: { '@':fileURLToPath(newURL('./src', import.meta.url)) }, }, })Build Project:
bashpnpm build
What the Legacy Plugin Does
@vitejs/plugin-legacy automatically handles the following:
Generates two versions of code:
- Modern version (ES modules, for new browsers)
- Legacy version (ES5, for old browsers)
Injects polyfills (like Promise, Array.from, etc.)
Automatically loads the appropriate version based on browser capabilities
After building, Vite generates HTML like this:
<!-- Modern browser loading -->
<script type="module" src="./assets/index-abc123.js"></script>
<!-- Old browser loading -->
<script nomodule src="./assets/legacy-polyfills-def456.js"></script>
<script nomodule src="./assets/index-legacy-ghi789.js"></script>Other Build Tools
Webpack Configuration Example
If using Webpack, you can configure via babel-loader :
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', {
targets: {
android: '13',
chrome: '80'
}
}]
]
}
}
}
]
}
};Verifying Transpilation Results
After building, it's recommended to test on an teach pendant:
- Install the extension on the target device
- Open the extension page
- Verify that all features work correctly