From 0971de4d5357ba77ae2588f6f64cb788be5770ed Mon Sep 17 00:00:00 2001 From: SofiaBili Date: Mon, 22 Sep 2025 13:35:33 +0200 Subject: [PATCH 1/3] Add version control api and message passing api for web extensibility --- .../extensibility-api/web/_index.md | 2 + .../message-passing-api.md | 319 ++++++++++++++++++ .../version-control-api.md | 88 +++++ 3 files changed, 409 insertions(+) create mode 100644 content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/message-passing-api.md create mode 100644 content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/version-control-api.md diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/_index.md index de90aa39057..e7584de8935 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/_index.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/_index.md @@ -45,3 +45,5 @@ Here is a list of how-tos for you to begin with: * [How to View User Preferences Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/preference-api/) * [How to Show a Modal Dialog Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/dialog-api/) * [How to Open Documents Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/editor-api/) +* [How to Exchange Information Between Active Views Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/message-passing-api/) +* [How to Show Version Control Information](/apidocs-mxsdk/apidocs/web-extensibility-api-11/version-control-api/) diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/message-passing-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/message-passing-api.md new file mode 100644 index 00000000000..264d9b4a2be --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/message-passing-api.md @@ -0,0 +1,319 @@ +--- +title: "Exchange Information Between Active Views Using Web API" +linktitle: "Communication between views" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-11/message-passing-api/ +--- + +## Introduction + +This how-to describes how to pass information between different active views (e.g., tabs, dialogs and panes) +of the same extension. + +## Prerequisites + +This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/getting-started/). Please complete that how-to before starting this one. You should also be familiar with creating menus as described in [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/menu-api/), and creating different kinds of views +such as [tabs](/apidocs-mxsdk/apidocs/web-extensibility-api-11/tab-api/) +and [panes](/apidocs-mxsdk/apidocs/web-extensibility-api-11/dockable-pane-api/). + +## Communication patterns + +To support passing information between different active contexts of an extension (its main entry point and active views), +we have introduced Message Passing API, which can be obtained in `studioPro.ui.messagePassing`, where `studioPro` is +the Studio Pro object obtained with `getStudioProApi` call. + +This API supports two communication patterns: request-reply and message broadcasting. + +### Request-reply + +In request-reply pattern, one endpoint sends a message to another endpoint and expects to receive a reply. +Message Passing API supports this pattern by allowing the sending side to send a message and specify a callback +that will be invoked once a reply has been received. + +To implement this behavior, insert this code in `main/index.ts` + +```typescript {hl_lines=["16-25"]} +import { IComponent, getStudioProApi } from "@mendix/extensions-api"; + +export const component: IComponent = { + async loaded(componentContext) { + const studioPro = getStudioProApi(componentContext); + let counter = 0; + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "message-passing.MainMenu", + caption: "Message passing", + subMenus: [ + { menuId: "message-passing.ShowTab", caption: "Show tab" }, + ], + }); + + await studioPro.ui.messagePassing.addMessageHandler<{type:string}>(async messageInfo => { + const messageData = messageInfo.message; + if (messageData.type === "incrementCounter") { + counter++; + await studioPro.ui.messagePassing.sendResponse(messageInfo.messageId, { + type: "counterValue", + counter + }); + } + }); + + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + async (args) => { + if (args.menuId === "message-passing.ShowTab") { + await studioPro.ui.tabs.open( + { + title: "MyExtension Tab" + }, + { + componentName: "extension/message-passing", + uiEntrypoint: "tab", + } + ); + } + } + ); + } +} +``` + +Insert the following code into `src/ui/index.tsx`: + +```typescript {hl_lines=["14-19"]} +import React, { StrictMode, useCallback, useState } from "react"; +import { createRoot } from "react-dom/client"; +import { ComponentContext, getStudioProApi, IComponent } from "@mendix/extensions-api"; + + +type MessagePassingAppProps = { + componentContext: ComponentContext +} + +function CounterState({ componentContext }: MessagePassingAppProps) { + const studioPro = getStudioProApi(componentContext); + const [counter, setCounter] = useState(null); + const incrementCounter = useCallback(async () => { + studioPro.ui.messagePassing.sendMessage( + { type: "incrementCounter"}, + async (response: {type: 'counterValue', counter: number}) => { + setCounter(response.counter); + } + ); + }, [componentContext]); + + return ( +
+ +

Counter value: {counter ?? "unknown"}

+
+ ); +} + +export const component: IComponent = { + async loaded(componentContext) { + createRoot(document.getElementById("root")!).render( + + + + ); + } +} +``` + +This extension will increase the value of `counter` in the main context every time the user presses a button in the +active tab. To implement this we send a message from the tab to the main context, expecting that the main context +will reply with the current value of `counter`. + +In the highlighted lines we illustrate the behavior of responding to a message. First, in the main context (the file `src/main/index.ts`) +we register a listener which will respond to every message sent from other contexts. Every message has an ID which +we can use if we want to respond to this message. We use this ID to identify the message we are responding to. + +When the main context sends a response, it will be picked up the `onResponse` callback registered in +the highlighted lines of the file (`src/ui/index.tsx`). Note that this callback will be invoked at most once, +as each message can have only one response. + +### Broadcast + +In the broadcast pattern, one context broadcasts messages to all other contexts that are listening to +a message. To implement this pattern, copy the following code to `src/main/index.ts`: + +```typescript +import { IComponent, getStudioProApi } from "@mendix/extensions-api"; + +export const component: IComponent = { + async loaded(componentContext) { + const studioPro = getStudioProApi(componentContext); + let counter = 0; + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "message-passing.MainMenu", + caption: "Message Passing", + subMenus: [ + { menuId: "message-passing.ShowTab", caption: "Show tab" }, + { menuId: "message-passing.ShowPane", caption: "Show pane" }, + ], + }); + + const paneHandle = await studioPro.ui.panes.register({ + title: 'Message Passing Pane', + initialPosition: 'right', + }, { + componentName: "extension/message-passing", + uiEntrypoint: "pane" + }) + + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + async (args) => { + if (args.menuId === "message-passing.ShowTab") { + await studioPro.ui.tabs.open( + { + title: "MyExtension Tab" + }, + { + componentName: "extension/message-passing", + uiEntrypoint: "tab", + } + ); + } else if (args.menuId === "message-passing.ShowPane") { + await studioPro.ui.panes.open(paneHandle); + } + } + ); + } +} +``` + +Then rename `src/ui/index.tsx` to `src/ui/tab.tsx` and paste the following code into it: + +```typescript {hl_lines=["13-15"]} +import React, { StrictMode, useCallback, useEffect, useState } from "react"; +import { createRoot } from "react-dom/client"; +import { ComponentContext, getStudioProApi, IComponent } from "@mendix/extensions-api"; + + +type MessagePassingAppProps = { + componentContext: ComponentContext +} + +function NameBroadcaster({ componentContext }: MessagePassingAppProps) { + const studioPro = getStudioProApi(componentContext); + const [name, setName] = useState(""); + useEffect(() => { + studioPro.ui.messagePassing.sendMessage({ type: "nameChanged", name }); + }, [name]); + + return ( +
+ Name: setName(e.target.value)} /> +
+ ); +} + +export const component: IComponent = { + async loaded(componentContext) { + createRoot(document.getElementById("root")!).render( + + + + ); + } +} +``` + +Then, create a file `src/ui/pane.tsx` and paste the following code into it: + +```typescript {hl_lines=["14-19"]} +import React, { StrictMode, useCallback, useEffect, useState } from "react"; +import { createRoot } from "react-dom/client"; +import { ComponentContext, getStudioProApi, IComponent } from "@mendix/extensions-api"; + + +type MessagePassingAppProps = { + componentContext: ComponentContext +} + +function Greeter({ componentContext }: MessagePassingAppProps) { + const studioPro = getStudioProApi(componentContext); + const [name, setName] = useState("unknown"); + useEffect(() => { + studioPro.ui.messagePassing.addMessageHandler<{ type: string; name: string }>(async messageInfo => { + const messageData = messageInfo.message; + if (messageData.type === "nameChanged") { + setName(messageData.name); + } + }); + }, [componentContext]); + + return ( +
+ Hello {name}! +
+ ); +} + +export const component: IComponent = { + async loaded(componentContext) { + createRoot(document.getElementById("root")!).render( + + + + ); + } +} +``` + +Add the new views to `build-extension.mjs` by replacing the value of `entryPoints` array by + +```javascript +const entryPoints = [ + { + in: 'src/main/index.ts', + out: 'main' + } +] + +entryPoints.push({ + in: 'src/ui/tab.tsx', + out: 'tab' +}) + +entryPoints.push({ + in: 'src/ui/pane.tsx', + out: 'pane' +}) +``` + +Lastly, replace the `manifest.json` contents by + +``` +{ + "mendixComponent": { + "entryPoints": { + "main": "main.js", + "ui": { + "tab": "tab.js", + "pane": "pane.js" + } + } + } +} +``` + +This code first ensures that both the tab and the pane are registered in `src/main/index.ts`. +Then, the code in the tab will send a message every time the value of `name` is updated. +The pane is listening to all the messages emitted from the tab and reacts by updating its +view any time the new message is received. + +## Conclusion + +You have mastered two patterns of communication between different active views in Studio Pro. + +## Extensibility Feedback + +If you would like to provide us with additional feedback, you can complete a small [survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback). + +Any feedback is appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/version-control-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/version-control-api.md new file mode 100644 index 00000000000..168ff90975b --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/version-control-api.md @@ -0,0 +1,88 @@ +--- +title: "Show Version Control Information Using Web API" +linktitle: "Show Version Control Information" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-11/version-control-api/ +--- + +## Introduction + +This how-to describes how to display version control information in Studio Pro. The extension adds a menu item that, when clicked, shows details about the current version control system, branch, and last commit. + +## Prerequisites + +This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/getting-started/). Complete that how-to before starting this one. You should also be familiar with creating menus as described in [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/menu-api/). + +## Showing Version Control Information + +The extension creates a menu item labeled **Current version control system**. When the menu is activated, it fetches version control details (system type, branch, last commit) and displays them in a message box. + +### Set Up the Extension Structure +In the example below, you create one menu item that will show version control details in a message box. + +It performs the following actions: +1. Creates a menu item labeled "Current version control system" +2. Listens for when the menu item is clicked +3. When clicked, retrieves the version control information which includes: + * The type of version control system (like Git) + * Current branch name + * Last commit details (SHA, author, message, and date) +4. Displays this information in a message box + +Replace your `src/main/index.ts` file with the following: + + +```typescript +import { IComponent, getStudioProApi } from "@mendix/extensions-api"; + +export const component: IComponent = { + async loaded(componentContext) { + const studioPro = getStudioProApi(componentContext); + const menuId = "version-control-menu"; + + await studioPro.ui.extensionsMenu.add({ + menuId, + caption: "Current version control system" + }); + + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + async (args) => { + if (args.menuId === menuId) { + const versionControlApi = studioPro.ui.versionControl; + const messageBoxApi = studioPro.ui.messageBoxes; + const versionControlSystemInfo = await versionControlApi.getVersionControlInfo(); + + if (versionControlSystemInfo == null) { + messageBoxApi.show("info", "This app is not version controlled"); + return; + } + + let message = `The system is ${versionControlSystemInfo.versionControlSystem}. Branch: ${versionControlSystemInfo.branch}.`; + + if (versionControlSystemInfo.lastCommit == null) { + message += "\n\nLast Commit: No commit information available."; + } else { + message += "\n\nLast Commit:\n"; + message += `SHA: ${versionControlSystemInfo.lastCommit.sha}\n`; + message += `Author: ${versionControlSystemInfo.lastCommit.author}\n`; + message += `Message: ${versionControlSystemInfo.lastCommit.message}\n`; + message += `Date: ${versionControlSystemInfo.lastCommit.date}`; + } + + messageBoxApi.show("info", message); + } + } + ); + } +} +``` + +## Conclusion + +You have seen how to add a menu in Studio Pro that shows version control details for your app, making it easy for users to see the branch and head commit information. + +## Extensibility Feedback + +If you would like to provide us with additional feedback, you can complete a small [survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback). + +Any feedback is appreciated. From 783a5389e8ca2a5f4adffbfeb3f408d90df7be94 Mon Sep 17 00:00:00 2001 From: quinntracy Date: Tue, 23 Sep 2025 14:12:01 +0200 Subject: [PATCH 2/3] Revise message passing API --- .../extensibility-api/web/_index.md | 26 +- .../message-passing-api.md | 338 +++++++++--------- 2 files changed, 177 insertions(+), 187 deletions(-) diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/_index.md index e7584de8935..fe6b31c59d8 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/_index.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/_index.md @@ -33,17 +33,17 @@ For detailed explanation on how to get started with extensions, check out [Get S ## How-tos -Here is a list of how-tos for you to begin with: - -* [How to Create a Dockable Pane Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/dockable-pane-api/) -* [How to Interact With Local App Files Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/local-app-files-api/) -* [How to Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/menu-api/) -* [How to Show a Message Box Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/messagebox-api/) -* [How to Access a Mendix Model Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/model-api/) -* [How to Open a Tab Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/tab-api/) -* [How to Show a Popup Notification Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/notification-api/) -* [How to View User Preferences Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/preference-api/) -* [How to Show a Modal Dialog Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/dialog-api/) -* [How to Open Documents Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/editor-api/) -* [How to Exchange Information Between Active Views Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/message-passing-api/) +Below is a list of how-tos for you to begin with: + +* [How to Create a Dockable Pane](/apidocs-mxsdk/apidocs/web-extensibility-api-11/dockable-pane-api/) +* [How to Interact With Local App Files](/apidocs-mxsdk/apidocs/web-extensibility-api-11/local-app-files-api/) +* [How to Create a Menu](/apidocs-mxsdk/apidocs/web-extensibility-api-11/menu-api/) +* [How to Show a Message Box](/apidocs-mxsdk/apidocs/web-extensibility-api-11/messagebox-api/) +* [How to Access a Mendix Model](/apidocs-mxsdk/apidocs/web-extensibility-api-11/model-api/) +* [How to Open a Tab](/apidocs-mxsdk/apidocs/web-extensibility-api-11/tab-api/) +* [How to Show a Popup Notification](/apidocs-mxsdk/apidocs/web-extensibility-api-11/notification-api/) +* [How to View User Preferences](/apidocs-mxsdk/apidocs/web-extensibility-api-11/preference-api/) +* [How to Show a Modal Dialog](/apidocs-mxsdk/apidocs/web-extensibility-api-11/dialog-api/) +* [How to Open Documents](/apidocs-mxsdk/apidocs/web-extensibility-api-11/editor-api/) +* [How to Exchange Information Between Active Views](/apidocs-mxsdk/apidocs/web-extensibility-api-11/message-passing-api/) * [How to Show Version Control Information](/apidocs-mxsdk/apidocs/web-extensibility-api-11/version-control-api/) diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/message-passing-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/message-passing-api.md index 264d9b4a2be..a6436f625b6 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/message-passing-api.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/message-passing-api.md @@ -1,35 +1,33 @@ --- title: "Exchange Information Between Active Views Using Web API" -linktitle: "Communication between views" +linktitle: "Communication Between Views" url: /apidocs-mxsdk/apidocs/web-extensibility-api-11/message-passing-api/ --- ## Introduction -This how-to describes how to pass information between different active views (e.g., tabs, dialogs and panes) -of the same extension. +This how-to describes how to pass information between different active views (such as tabs, dialogs, and panes) within the same extension. ## Prerequisites -This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/getting-started/). Please complete that how-to before starting this one. You should also be familiar with creating menus as described in [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/menu-api/), and creating different kinds of views -such as [tabs](/apidocs-mxsdk/apidocs/web-extensibility-api-11/tab-api/) -and [panes](/apidocs-mxsdk/apidocs/web-extensibility-api-11/dockable-pane-api/). +Before starting this how-to, make sure you have completed the following prerequisites: -## Communication patterns +* This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/getting-started/). Complete that how-to before starting this one. +* Make sure you are familiar with: + * Creating [menus](/apidocs-mxsdk/apidocs/web-extensibility-api-11/menu-api/) + * Creating different kinds of views, such as [tabs](/apidocs-mxsdk/apidocs/web-extensibility-api-11/tab-api/) and [panes](/apidocs-mxsdk/apidocs/web-extensibility-api-11/dockable-pane-api/) -To support passing information between different active contexts of an extension (its main entry point and active views), -we have introduced Message Passing API, which can be obtained in `studioPro.ui.messagePassing`, where `studioPro` is -the Studio Pro object obtained with `getStudioProApi` call. +## Communication Patterns + +Use the Message Passing API to pass information between different active contexts within an extension (such as its main entry point and active views). Access this API via `studioPro.ui.messagePassing`, where `studioPro` refers to the Studio Pro object returned by the `getStudioProApi` call. This API supports two communication patterns: request-reply and message broadcasting. -### Request-reply +### Request-Reply -In request-reply pattern, one endpoint sends a message to another endpoint and expects to receive a reply. -Message Passing API supports this pattern by allowing the sending side to send a message and specify a callback -that will be invoked once a reply has been received. +In the request-reply pattern, one endpoint sends a message to another endpoint and waits for a reply. The Message Passing API supports this pattern by allowing the sender to include a callback function, which is triggered when a reply is received. -To implement this behavior, insert this code in `main/index.ts` +To implement this behavior, insert the following code in `main/index.ts`: ```typescript {hl_lines=["16-25"]} import { IComponent, getStudioProApi } from "@mendix/extensions-api"; @@ -122,195 +120,187 @@ export const component: IComponent = { } ``` -This extension will increase the value of `counter` in the main context every time the user presses a button in the -active tab. To implement this we send a message from the tab to the main context, expecting that the main context -will reply with the current value of `counter`. +In this example, the extension increases the `counter` value in the main context every time the user clicks a button in the active tab. To achieve this, the tab sends a message to the main context, expecting a reply with the current `counter` value. -In the highlighted lines we illustrate the behavior of responding to a message. First, in the main context (the file `src/main/index.ts`) -we register a listener which will respond to every message sent from other contexts. Every message has an ID which -we can use if we want to respond to this message. We use this ID to identify the message we are responding to. +The highlighted lines demonstrate how to respond to a message. In the main context (`src/main/index.ts`), a listener is registered, which handles incoming messages from other contexts. Each message has an ID, which can be used to identify and respond to that specific message. -When the main context sends a response, it will be picked up the `onResponse` callback registered in -the highlighted lines of the file (`src/ui/index.tsx`). Note that this callback will be invoked at most once, -as each message can have only one response. +When the main context sends a response, it is received by the `onResponse` callback registered in +the highlighted lines of `src/ui/index.tsx`. Note that this callback will be invoked only once, +as each message can have a single response. ### Broadcast -In the broadcast pattern, one context broadcasts messages to all other contexts that are listening to -a message. To implement this pattern, copy the following code to `src/main/index.ts`: - -```typescript -import { IComponent, getStudioProApi } from "@mendix/extensions-api"; - -export const component: IComponent = { - async loaded(componentContext) { - const studioPro = getStudioProApi(componentContext); - let counter = 0; - // Add a menu item to the Extensions menu - await studioPro.ui.extensionsMenu.add({ - menuId: "message-passing.MainMenu", - caption: "Message Passing", - subMenus: [ - { menuId: "message-passing.ShowTab", caption: "Show tab" }, - { menuId: "message-passing.ShowPane", caption: "Show pane" }, - ], - }); - - const paneHandle = await studioPro.ui.panes.register({ - title: 'Message Passing Pane', - initialPosition: 'right', - }, { - componentName: "extension/message-passing", - uiEntrypoint: "pane" - }) - - // Open a tab when the menu item is clicked - studioPro.ui.extensionsMenu.addEventListener( - "menuItemActivated", - async (args) => { - if (args.menuId === "message-passing.ShowTab") { - await studioPro.ui.tabs.open( - { - title: "MyExtension Tab" - }, - { - componentName: "extension/message-passing", - uiEntrypoint: "tab", - } - ); - } else if (args.menuId === "message-passing.ShowPane") { - await studioPro.ui.panes.open(paneHandle); +In the broadcast pattern, one context sends a messages to all other contexts that are listening for it. To implement this pattern, do the following: + +1. Copy the following code into `src/main/index.ts`: + + ```typescript + import { IComponent, getStudioProApi } from "@mendix/extensions-api"; + + export const component: IComponent = { + async loaded(componentContext) { + const studioPro = getStudioProApi(componentContext); + let counter = 0; + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "message-passing.MainMenu", + caption: "Message Passing", + subMenus: [ + { menuId: "message-passing.ShowTab", caption: "Show tab" }, + { menuId: "message-passing.ShowPane", caption: "Show pane" }, + ], + }); + + const paneHandle = await studioPro.ui.panes.register({ + title: 'Message Passing Pane', + initialPosition: 'right', + }, { + componentName: "extension/message-passing", + uiEntrypoint: "pane" + }) + + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + async (args) => { + if (args.menuId === "message-passing.ShowTab") { + await studioPro.ui.tabs.open( + { + title: "MyExtension Tab" + }, + { + componentName: "extension/message-passing", + uiEntrypoint: "tab", + } + ); + } else if (args.menuId === "message-passing.ShowPane") { + await studioPro.ui.panes.open(paneHandle); + } } - } - ); + ); + } } -} -``` - -Then rename `src/ui/index.tsx` to `src/ui/tab.tsx` and paste the following code into it: - -```typescript {hl_lines=["13-15"]} -import React, { StrictMode, useCallback, useEffect, useState } from "react"; -import { createRoot } from "react-dom/client"; -import { ComponentContext, getStudioProApi, IComponent } from "@mendix/extensions-api"; + ``` +2. Rename `src/ui/index.tsx` to `src/ui/tab.tsx` and paste the following code into it: -type MessagePassingAppProps = { - componentContext: ComponentContext -} + ```typescript {hl_lines=["13-15"]} + import React, { StrictMode, useCallback, useEffect, useState } from "react"; + import { createRoot } from "react-dom/client"; + import { ComponentContext, getStudioProApi, IComponent } from "@mendix/extensions-api"; -function NameBroadcaster({ componentContext }: MessagePassingAppProps) { - const studioPro = getStudioProApi(componentContext); - const [name, setName] = useState(""); - useEffect(() => { - studioPro.ui.messagePassing.sendMessage({ type: "nameChanged", name }); - }, [name]); - return ( -
- Name: setName(e.target.value)} /> -
- ); -} + type MessagePassingAppProps = { + componentContext: ComponentContext + } -export const component: IComponent = { - async loaded(componentContext) { - createRoot(document.getElementById("root")!).render( - - - + function NameBroadcaster({ componentContext }: MessagePassingAppProps) { + const studioPro = getStudioProApi(componentContext); + const [name, setName] = useState(""); + useEffect(() => { + studioPro.ui.messagePassing.sendMessage({ type: "nameChanged", name }); + }, [name]); + + return ( +
+ Name: setName(e.target.value)} /> +
); } -} -``` -Then, create a file `src/ui/pane.tsx` and paste the following code into it: + export const component: IComponent = { + async loaded(componentContext) { + createRoot(document.getElementById("root")!).render( + + + + ); + } + } + ``` -```typescript {hl_lines=["14-19"]} -import React, { StrictMode, useCallback, useEffect, useState } from "react"; -import { createRoot } from "react-dom/client"; -import { ComponentContext, getStudioProApi, IComponent } from "@mendix/extensions-api"; +3. Create a new file `src/ui/pane.tsx` and paste the following code into it: + ```typescript {hl_lines=["14-19"]} + import React, { StrictMode, useCallback, useEffect, useState } from "react"; + import { createRoot } from "react-dom/client"; + import { ComponentContext, getStudioProApi, IComponent } from "@mendix/extensions-api"; -type MessagePassingAppProps = { - componentContext: ComponentContext -} -function Greeter({ componentContext }: MessagePassingAppProps) { - const studioPro = getStudioProApi(componentContext); - const [name, setName] = useState("unknown"); - useEffect(() => { - studioPro.ui.messagePassing.addMessageHandler<{ type: string; name: string }>(async messageInfo => { - const messageData = messageInfo.message; - if (messageData.type === "nameChanged") { - setName(messageData.name); - } - }); - }, [componentContext]); + type MessagePassingAppProps = { + componentContext: ComponentContext + } - return ( -
- Hello {name}! -
- ); -} + function Greeter({ componentContext }: MessagePassingAppProps) { + const studioPro = getStudioProApi(componentContext); + const [name, setName] = useState("unknown"); + useEffect(() => { + studioPro.ui.messagePassing.addMessageHandler<{ type: string; name: string }>(async messageInfo => { + const messageData = messageInfo.message; + if (messageData.type === "nameChanged") { + setName(messageData.name); + } + }); + }, [componentContext]); -export const component: IComponent = { - async loaded(componentContext) { - createRoot(document.getElementById("root")!).render( - - - + return ( +
+ Hello {name}! +
); } -} -``` -Add the new views to `build-extension.mjs` by replacing the value of `entryPoints` array by + export const component: IComponent = { + async loaded(componentContext) { + createRoot(document.getElementById("root")!).render( + + + + ); + } + } + ``` -```javascript -const entryPoints = [ - { - in: 'src/main/index.ts', - out: 'main' - } -] - -entryPoints.push({ - in: 'src/ui/tab.tsx', - out: 'tab' -}) - -entryPoints.push({ - in: 'src/ui/pane.tsx', - out: 'pane' -}) -``` +4. Update `build-extension.mjs` by replacing the `entryPoints` array with: -Lastly, replace the `manifest.json` contents by + ```javascript + const entryPoints = [ + { + in: 'src/main/index.ts', + out: 'main' + } + ] -``` -{ - "mendixComponent": { - "entryPoints": { - "main": "main.js", - "ui": { - "tab": "tab.js", - "pane": "pane.js" - } - } - } -} -``` + entryPoints.push({ + in: 'src/ui/tab.tsx', + out: 'tab' + }) + + entryPoints.push({ + in: 'src/ui/pane.tsx', + out: 'pane' + }) + ``` -This code first ensures that both the tab and the pane are registered in `src/main/index.ts`. -Then, the code in the tab will send a message every time the value of `name` is updated. -The pane is listening to all the messages emitted from the tab and reacts by updating its -view any time the new message is received. +5. Replace the contents of `manifest.json` with: -## Conclusion + ``` + { + "mendixComponent": { + "entryPoints": { + "main": "main.js", + "ui": { + "tab": "tab.js", + "pane": "pane.js" + } + } + } + } + ``` -You have mastered two patterns of communication between different active views in Studio Pro. +This setup ensures that both the tab and the pane are registered in `src/main/index.ts`. +The tab sends a message whenever the `name` value is updated. The pane listens for these messages emitted from the tab, and updates its +view accordingly each time a new message is received. ## Extensibility Feedback From 6829143c019e028ea2459a237281dde7c55ccbd3 Mon Sep 17 00:00:00 2001 From: quinntracy Date: Tue, 23 Sep 2025 15:02:17 +0200 Subject: [PATCH 3/3] Review VC API --- .../version-control-api.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/version-control-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/version-control-api.md index 168ff90975b..f92534c155b 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/version-control-api.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/version-control-api.md @@ -10,20 +10,25 @@ This how-to describes how to display version control information in Studio Pro. ## Prerequisites -This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/getting-started/). Complete that how-to before starting this one. You should also be familiar with creating menus as described in [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/menu-api/). +Before starting this how-to, make sure you have completed the following prerequisites: + +* This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/getting-started/). Complete that how-to before starting this one. +* Make sure you are familiar with creating menus, as described in [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/menu-api/). ## Showing Version Control Information -The extension creates a menu item labeled **Current version control system**. When the menu is activated, it fetches version control details (system type, branch, last commit) and displays them in a message box. +The extension creates a menu item named **Current version control system**. When the menu is activated, it fetches version control details (system type, branch, last commit) and displays them in a message box. ### Set Up the Extension Structure + In the example below, you create one menu item that will show version control details in a message box. It performs the following actions: -1. Creates a menu item labeled "Current version control system" + +1. Creates a menu item named **Current version control system** 2. Listens for when the menu item is clicked -3. When clicked, retrieves the version control information which includes: - * The type of version control system (like Git) +3. When clicked, it retrieves the version control information which includes: + * The type of version control system (for example, Git) * Current branch name * Last commit details (SHA, author, message, and date) 4. Displays this information in a message box @@ -77,10 +82,6 @@ export const component: IComponent = { } ``` -## Conclusion - -You have seen how to add a menu in Studio Pro that shows version control details for your app, making it easy for users to see the branch and head commit information. - ## Extensibility Feedback If you would like to provide us with additional feedback, you can complete a small [survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback).