Skip to main content
Version: v1 (Current)

App Manifest

The app-manifest.json file is the central configuration for your GxP plugin. It defines settings, translatable strings, assets, and runtime state that the platform injects into your plugin.

File Location

The manifest file should be in your project root:

my-plugin/
├── app-manifest.json # <-- Here
├── src/
└── ...

Basic Structure

{
"settings": {},
"strings": {},
"assets": {},
"triggerState": {},
"dependencies": [],
"permissions": []
}

Configuration Sections

Settings (pluginVars)

Define configurable settings that administrators can customize per deployment:

{
"settings": {
"primary_color": "#FFD600",
"background_color": "#ffffff",
"company_name": "Acme Corp",
"max_items": 10,
"enable_animations": true
}
}

Access in your component:

const store = useGxpStore();

// Get a setting with fallback
const color = store.getSetting('primary_color', '#000000');

// Check if setting exists
if (store.pluginVars.enable_animations) {
// ...
}

Use in templates with the gxp-settings modifier:

<span gxp-string="company_name" gxp-settings>Default Company</span>

Strings (stringsList)

Define translatable text content:

{
"strings": {
"default": {
"welcome_title": "Welcome to the Event",
"welcome_subtitle": "Please check in below",
"button_checkin": "Check In",
"button_cancel": "Cancel",
"error_not_found": "Registration not found"
}
}
}

Use in templates with the gxp-string directive:

<h1 gxp-string="welcome_title">Default Welcome</h1>
<button gxp-string="button_checkin">Check In</button>

Access programmatically:

const store = useGxpStore();
const title = store.getString('welcome_title', 'Default Title');
Hot Reload

Changes to strings in app-manifest.json are hot-reloaded during development. No page refresh needed!

Assets (assetList)

Define asset URLs (images, documents, etc.):

{
"assets": {
"hero_image": "/dev-assets/images/hero.jpg",
"logo": "/dev-assets/images/logo.png",
"background": "/dev-assets/images/bg-pattern.svg",
"welcome_video": "/dev-assets/videos/intro.mp4"
}
}

Use in templates with the gxp-src directive:

<img gxp-src="hero_image" src="/dev-assets/placeholder.jpg" alt="Hero" />
<img gxp-src="logo" src="/dev-assets/placeholder.jpg" alt="Logo" />

Access programmatically:

const store = useGxpStore();
const heroUrl = store.getAsset('hero_image', '/fallback.jpg');

Trigger State (triggerState)

Define dynamic runtime state that can change during plugin execution:

{
"triggerState": {
"is_active": true,
"current_step": 1,
"checked_in_count": 0,
"last_scan_result": null
}
}

Use in templates with the gxp-state modifier:

<span gxp-string="current_step" gxp-state>1</span>
<img gxp-src="dynamic_badge" gxp-state src="/placeholder.jpg" />

Update programmatically:

const store = useGxpStore();

// Update state
store.updateState('current_step', 2);
store.updateState('checked_in_count', store.triggerState.checked_in_count + 1);

// Read state
const step = store.getState('current_step', 1);

Dependencies

Dependencies define external API services your plugin can interact with. Each dependency maps API operations to endpoints that can be called via gxpStore.callApi().

Dependency Structure

{
"dependencies": [
{
"identifier": "access_points",
"model": "AccessPoint",
"permissionKey": "access_point",
"permissions": ["view_access_points", "manage_access_points"],
"operations": {
"access-points.index": "get:/v1/projects/{teamSlug}/{projectSlug}/access-points",
"access-points.show": "get:/v1/projects/{teamSlug}/{projectSlug}/access-points/{access_point}",
"access-points.store": "post:/v1/projects/{teamSlug}/{projectSlug}/access-points",
"access-points.update": "put:/v1/projects/{teamSlug}/{projectSlug}/access-points/{access_point}",
"access-points.destroy": "delete:/v1/projects/{teamSlug}/{projectSlug}/access-points/{access_point}"
},
"events": {
"AccessPointUpdated": "AccessPointUpdated",
"AccessPointDeleted": "AccessPointDeleted"
}
}
]
}

Dependency Fields

FieldTypeDescription
identifierstringUnique identifier for this dependency (used in callApi)
modelstringThe model/resource name from the API
permissionKeystringThe permission key used for access control
permissionsarrayList of permissions required to use this dependency
operationsobjectMap of operationId to method:path (e.g., "get:/v1/...")
eventsobjectMap of socket event names this dependency can emit/receive

Using the Add Dependency Wizard

The easiest way to add dependencies is using the CLI wizard:

gxdev add-dependency

This interactive wizard will:

  1. Load the OpenAPI specification from the API
  2. Display available API tags/models
  3. Let you select which endpoints to include
  4. Let you select which socket events to include
  5. Generate the complete dependency configuration
  6. Add it to your app-manifest.json

Calling Dependency APIs

Once defined, call any operation using gxpStore.callApi():

import { useGxpStore } from '@gx-runtime/stores/gxpPortalConfigStore';

const store = useGxpStore();

// List all access points
const accessPoints = await store.callApi('access-points.index', 'access_points');

// Get a specific access point
const accessPoint = await store.callApi('access-points.show', 'access_points', {
access_point: 123
});

// Create a new access point
const newAccessPoint = await store.callApi('access-points.store', 'access_points', {
name: 'Main Entrance',
location: 'Building A'
});

// Update an access point
await store.callApi('access-points.update', 'access_points', {
access_point: 123,
name: 'Updated Name'
});

// Delete an access point
await store.callApi('access-points.destroy', 'access_points', {
access_point: 123
});

The callApi method signature:

store.callApi(operationId, identifier, additionalData = {})
  • operationId: The operation key from operations (e.g., 'access-points.index')
  • identifier: The dependency identifier (e.g., 'access_points')
  • additionalData: Object containing path parameters and/or request body data

Returns response.data from the API response.

Permissions

Define permissions required by the plugin:

{
"permissions": [
"camera",
"bluetooth",
"notifications"
]
}

Check permissions in code:

const store = useGxpStore();

if (store.hasPermission('camera')) {
// Enable camera features
}

Complete Example

{
"settings": {
"primary_color": "#FFD600",
"secondary_color": "#1976D2",
"company_name": "TechConf 2024",
"check_in_timeout": 30,
"enable_badge_printing": true
},
"strings": {
"default": {
"welcome_title": "Welcome to TechConf 2024",
"welcome_subtitle": "Scan your QR code to check in",
"btn_manual_entry": "Enter Code Manually",
"btn_help": "Need Help?",
"success_message": "You're all set!",
"error_invalid_code": "Invalid code. Please try again.",
"error_already_checked_in": "You've already checked in."
}
},
"assets": {
"logo": "/dev-assets/images/techconf-logo.png",
"hero_background": "/dev-assets/images/hero-bg.jpg",
"success_animation": "/dev-assets/animations/success.json"
},
"triggerState": {
"is_scanning": false,
"current_attendee": null,
"badge_printing": false
},
"dependencies": [],
"permissions": ["camera"]
}

Directive Reference

DirectiveModifierSourceExample
gxp-string(none)stringsList<h1 gxp-string="title">Default</h1>
gxp-stringgxp-settingspluginVars<span gxp-string="company" gxp-settings>Acme</span>
gxp-stringgxp-assetsassetList<span gxp-string="logo_url" gxp-assets>/logo.png</span>
gxp-stringgxp-statetriggerState<span gxp-string="count" gxp-state>0</span>
gxp-src(none)assetList<img gxp-src="hero" src="/placeholder.jpg" />
gxp-srcgxp-statetriggerState<img gxp-src="badge" gxp-state src="/placeholder.jpg" />

Best Practices

  1. Use descriptive keys - welcome_title is better than title1
  2. Provide defaults - Always include fallback text in your templates
  3. Group related strings - Keep related strings together for easier management
  4. Use dev-assets for development - Put placeholder images in dev-assets/images/
  5. Keep settings minimal - Only expose settings that need admin configuration