Skip to content

Library Documentation

lakhoune edited this page Jun 11, 2023 · 13 revisions

The syncmeta application consists of several widgets. Each of them has a specific purpose. The widgets are listed below

Partial widgets

This is the main widget of the SyncMeta framework. It is used to create and edit models as well as metamodels.

Palette of elements that can be put on the canvas widget

Widget that gives awareness of activities of other users

Edit model attributes

Import/Export/Delete (meta-)models and guidance models. Download activity list as JSON

Import/Export/Delete viewpoint and views.

Export the design to JSON.

Export the design as ZIP (in the IMSLD format) or link the design to ILDE

One container to host them all. Contains the canvas widget, attribute widget, the palette widget, the activity widget and the debug widget.

Screenshot of the widget container:

Inter-Widget Communication(IWC)

For the local communication between the various widgets of the SyncMeta the new IWC library from the chair is used.

To initialize the IWC library, you have to call the static getInstance function of the IWCWrapper class. The getInstance function takes the widget name as a parameter. The widget name has to be the same as the name of the widget in the config file. The getInstance function returns an instance of the IWCWrapper class. Syncmeta widget names have to follow the naming convention: <name>-widget. So the widget name for the canvas widget is canvas-widget. This is necessary since the IWC library uses the widget name to determine the widget that should receive the message. The IWCWrapper class has the following functions:

  • sendLocalOTOperation - sends an OT operation to the other widgets
  • sendLocalNonOTOperation - sends a non-OT operation to the other widgets
  • registerOnDataReceivedCallback - registers a callback function that is called when a message is received. The callback function takes the operation as a parameter.
  • unregisterOnDataReceivedCallback - unregisters the callback function that is called when a message is received.

Creating a new widget

There are several steps to create a new widget:

  • Choose a name for your widget. The name has to be unique. The name is used to identify the widget in the config file

  • Add the widget name to the config file under the WIDGET.NAME property. For the examples we assume that you named it Example. The file should look like this:

          WIDGET: {
              NAME: {
                  MAIN: "Canvas",
                  PALETTE: "Palette",
                  ATTRIBUTE: "Property Browser",
                  ...,
                  EXAMPLE: "Example",
              },
          },
    
  • Create a new file in the /src/widgets/partials folder. You should name it <widget-name>.widget.ts, replace <widget-name> with the name you chose in the first step. This file will contain the code of your widget.

  • Define your widget. You can use the customElement decorator to register it in the registry. Use the getWidgetTagName to get the html tag name of the widget. Here is an example of the SAMPLE widget:

    @customElement(getWidgetTagName(CONFIG.WIDGET.NAME.SAMPLE))
    export class SampleWidget extends SyncMetaWidget(
        LitElement,
        getWidgetTagName(CONFIG.WIDGET.NAME.SAMPLE)
    ) {... } 
  • Now it is time to configure YJS. In order to use yjs, we have to pass four properties onto our widget. Those are:

    1. yjsHost is the host of the y-websocket server.
    2. yjsPort is the port of the y-websocket server.
    3. yjsProtocol is the protocol of the y-websocket server.
    4. yjsSpaceTitle is the title of the y-websocket space.

    Here is our SAMPLE widget with the yjs configuration:

    @customElement(getWidgetTagName(CONFIG.WIDGET.NAME.SAMPLE))
    export class SampleWidget extends SyncMetaWidget(
        LitElement,
        getWidgetTagName(CONFIG.WIDGET.NAME.SAMPLE)
    ) {
        @property({ type: String }) yjsHost = "localhost";
        @property({ type: Number }) yjsPort = 1234;
        @property({ type: String }) yjsProtocol = "ws";
        @property({ type: String }) yjsSpaceTitle = window.spaceTitle;
    
        firstUpdated() {
            super.firstUpdated();
            this.yjsInstance = getInstance({
                host: this.yjsHost,
                port: this.yjsPort,
                protocol: this.yjsProtocol,
                spaceTitle: this.yjsSpaceTitle,
                });
            const yDoc = await this.yjsInstance.connect()
        }
    } 

    For local development, you can let the defaults as they are. For production, you have to change them to match your y-websocket server. The firstUpdated function is called when the widget is first updated. This is the place where we initialize yjs. Note that getInstance function is not to be confused with the one from IWCW

  • Now we can register the widget for the IWCW library. We do this by calling the getInstance function of the IWCW library. Here is the example for our SAMPLE widget

    @customElement(getWidgetTagName(CONFIG.WIDGET.NAME.SAMPLE))
    export class SampleWidget extends SyncMetaWidget(
        LitElement,
        getWidgetTagName(CONFIG.WIDGET.NAME.SAMPLE)
    ) {
        @property({ type: String }) yjsHost = "localhost";
        @property({ type: Number }) yjsPort = 1234;
        @property({ type: String }) yjsProtocol = "ws";
        @property({ type: String }) yjsSpaceTitle = window.spaceTitle;
    
        iwcw: IWCW;
    
        firstUpdated() {
            super.firstUpdated();
            this.yjsInstance = getInstance({
                host: this.yjsHost,
                port: this.yjsPort,
                protocol: this.yjsProtocol,
                spaceTitle: this.yjsSpaceTitle,
            });
            const yDoc = await this.yjsInstance.connect()
            this.iwcw = IWCW.getInstance(CONFIG.WIDGET.NAME.SAMPLE);
        }
        render() {
            return html`<div>Sample Widget</div>`;
        }
    } 

Now we can use our widget in our application