# Diagram Server API Reference
The KLighD diagram server can be used to generate diagrams in VS Code or plain web sites using the KLighD CLI, and the communication API necessary is presented here.
Example implementations are the [Java Language Server](https://github.com/kieler/KLighD/tree/master/plugins/de.cau.cs.kieler.klighd.lsp) and a minimal [Python Language Server](https://github.com/kieler/plyghd-ls-demonstrator).

## Initialization
### `initialize` message
A typical language server initialization sequence looks like this, where KLighD initializes the socket connection (by default on port 5007) with the default [LSP initialize message](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize).
```json
{
  "jsonrpc": "2.0",
  "id": 0,
  "method": "initialize",
  "params": {
    "processId": null,
    "workspaceFolders": null,
    "rootUri": null,
    "clientInfo": { "name": "webview" },
    "capabilities": {},
    "initializationOptions": {
      "clientColorPreferences": ClientColorPreferences,
      "clientDiagramOptions": ClientDiagramOptions
    }
  }
}
```

TODO: during initialization, the server should be able to send if it can do automatic layout in the LSP `capabilities` message, with `false` being the default. We envision that the language server tells the client if it can do layout. If it cannot do layout, the client will activate layout. If it can, the layout will be done on the server.

`ClientColorPreferences` contains style preferences about the current color theme of the client.
These colors can be used during diagram synthesis to generate diagrams styled according to the client color theme.
The following is an example of the default dark color theme that the VS Code extension of KLighD will send during initialization.
The possible theme kinds are:
0 for light, 1 for dark, 2 for high contrast light, 3 for high contrast dark.
The `foreground`, `background`, and `highlight` fields are all optional.
```json
{
    "kind": 1,
    "foreground": "#D4D4D4",
    "background": "#1E1E1E",
    "highlight": "#0078D4"
}
```

`ClientDiagramOptions` is a collection of options stored by the client that the server should consider in its diagram generation:
```json
{
    "preference": {
        # collection of key-value pairs, representing the option's ID and its current value.
    },
    "render": {
        # collection of key-value pairs, representing the option's ID and its current value.
    },
    "synthesis": {
        # collection of key-value pairs, representing the option's ID and its current value. These are the only really important ones for the diagram synthesis.
    }
}
```

### `keith/preferences/setPreferences` message
After that and some default LSP messaging, such as the *initialized* message, the client sets some more preferences with the `keith/preferences/setPreferences` message:
```json
{
    "jsonrpc": "2.0",
    "method": "keith/preferences/setPreferences",
    "params": {
        "diagram.shouldSelectDiagram": false,
        "diagram.shouldSelectText": true,
        "diagram.incrementalDiagramGenerator": false
    }
}
```
The first two preferences are for interaction of the diagram with a text editor that may be open. They indicate, if a text selection should cause a diagram update with the same element selected (`shouldSelectDiagram`) or if a selection in the diagram should also select the corresponding region in the text (`shouldSelectText`).
The `incrementalDiagramGenerator` is a preference to toggle full (`false`) or incremental (`true`) diagram generation, i.E. generation of only segments of the diagram incrementally. See the diagramPiece messages.
TODO: it seems like the setPreferences message may be sent after the requestModel action in VS Code, check how this should be handled.


## Basic diagram generation loop
### `diagram/accept` message
All remaining diagram-related messages are sent via Sprotty's [action](https://github.com/eclipse-sprotty/sprotty/wiki/Architectural-Overview#actions) framework. Each action uses the new `diagram/accept` LSP message:
```json
{
  "jsonrpc": "2.0",
  "method": "diagram/accept",
  "params": {
    "clientId": "sprotty",
    "action": (Sprotty) Action
  }
}
```

### `requestModel` Action
Whenever the client requests a diagram, it sends Sprotty's `requestModel` action like this:
```json
{
  "kind": "requestModel",
  "options": {
    "needsClientLayout": true,
    "needsServerLayout": false,
    "sourceUri": "file:///path/to/your/file.kgt",
    "diagramType": "keith-diagram"
  },
  "requestId": "client_1"
}
```

The meaning of each parameter is as follows:
- `needsClientLayout`: If the (KLighD-) client performs the layout. See the capabilities message above.
- `needsServerLayout`: If the language server performs the layout. Only reasonable for Java or JavaScript language servers using ELK or elkjs. See the capabilities message above.
- `sourceUri`: The URI of the model for that the server should provide a diagram. The file extension may be used to only show available syntheses for the model type given by that extension. Here an example as a textual KGraph file (.kgt).
- `diagramType`: Sprotty-internal type. It is always `"keith-diagram"` for any KLighD diagram.
- `requestId`: An identifier to match future diagram requests and updates to, so the server can handle multiple diagrams at once.

### `setSyntheses` Action
After this request, the server needs to answer with the following three messages.
First, an action notifying the client on the available syntheses for the requested diagram:
```json
{
  "kind": "setSyntheses",
  "syntheses": [
    Synthesis
  ]
}
```
with each `Synthesis` being defined by an internal ID and a display name for the UI. An example for the synthesis for KGraphs in KLighD would result in this message:
```json
{
  "id": "de.cau.cs.kieler.graphs.klighd.syntheses.KGraphDiagramSynthesis",
  "displayName": "KGraphDiagramSynthesis"
}
```

The server can also decide on its own that a new diagram should be sent, starting this same sequence of actions.
A use case might be a model change, requiring a new diagram to be drawn for it.

### `updateOptions` Action
Second, an action notifying the client on the available options for the chosen synthesis:
```json
{
  "kind": "updateOptions",
  "valuedSynthesisOptions": [
    SynthesisOption
  ],
  "layoutOptions": [
    LayoutOption
  ],
  "actions": [
    (KLighD) Action
  ],
  "modelUri": "file:///path/to/your/file.kgt"
}
```
with `SynthesisOption` being defined like these examples:
```json
{
  "synthesisOption": {
    "id": "de.cau.cs.kieler.graphs.klighd.syntheses.KGraphDiagramSynthesis.CHECK-1675366116",
    "name": "Suppress Edge Adjustement",
    "type": 0,
    "sourceHash": 1197289400,
    "initialValue": true
  },
  "currentValue": true
}
```
```json
{
  "synthesisOption": {
    "id": "de.cau.cs.kieler.graphs.klighd.syntheses.KGraphDiagramSynthesis.CHOICE227839009",
    "name": "Default Values",
    "type": 1,
    "sourceHash": 1506798321,
    "values": ["As in Model", "On", "Off"],
    "initialValue": "As in Model"
  },
  "currentValue": "As in Model"
}
```
```json
{
  "synthesisOption": {
    "id": "de.cau.cs.kieler.sccharts.ui.synthesis.hooks.LabelShorteningHook.RANGE-230395005",
    "name": "Shortening Width",
    "type": 2,
    "range": { "first": 0, "second": 1000 },
    "stepSize": 10,
    "updateAction": "de.cau.cs.kieler.sccharts.ui.synthesis.hooks.LabelShorteningHook",
    "category": {
      "id": "de.cau.cs.kieler.sccharts.ui.synthesis.GeneralSynthesisOptions.CATEGORY-2025855158",
      "name": "Layout",
      "type": 5,
      "sourceHash": 1427301019,
      "initialValue": false
    },
    "sourceHash": 1170645708,
    "initialValue": 200
  },
  "currentValue": 200
}
```
```json
{
  "synthesisOption": {
    "id": "TEXT-1269441222",
    "name": "Shorten IDs by",
    "type": 3,
    "description": "Shorten the IDs of all artifacts by this prefix.",
    "sourceHash": 1441271795,
    "initialValue": ""
  },
  "currentValue": "de.cau.cs.kieler."
}

```
```json
TODO: SEPARATOR option example
```
```json
{
  "synthesisOption": {
    "id": "de.cau.cs.kieler.sccharts.ui.synthesis.GeneralSynthesisOptions.CATEGORY-2025855158",
    "name": "Layout",
    "type": 5,
    "sourceHash": 1427301019,
    "initialValue": false
  },
  "currentValue": false
}
```
- `"id"`: unique ID of the option
- `"name"`: Display name for the option
- `"type"`: option type with the mapping {0: check, 1: choice, 2: range, 3: text, 4: separator, 5: category}
- `"sourceHash"`: unique hash code of the option TODO: should not be needed anymore
- `"initialValue"`: the initial value of the option
- `"currentValue"`: The currently configured value of the option
- `"category"`: The full JSON definition of the category of the option (optional) TODO: I think only the ID is required here
- `"description"`: A descriptive text describing the option on hover (optional)
- `"range"`: the range of the option (only range options)
- `"stepSize"`: the allowed step size for ranges (only range options)
- `"values"`: the possible values of the option (only choice options)
- `""updateAction"`: ? TODO:

Two examples of `LayoutOption` may look like this:
```json
{
  "optionId": "org.eclipse.elk.layered.highDegreeNodes.treatment",
  "defaultValue": { "k": false, "v": "False" },
  "type": 1,
  "name": "High Degree Node Treatment",
  "description": "Makes room around high degree nodes to place leafs and trees.",
  "choices": ["false", "true"],
  "minValue": 0.0,
  "maxValue": 100.0,
  "availableValues": { "k": [true, false], "v": ["True", "False"] }
}
```
```json
{
  "optionId": "org.eclipse.elk.layered.crossingMinimization.strategy",
  "defaultValue": { "k": 0, "v": "Layer_sweep" },
  "type": 5,
  "name": "Crossing Minimization Strategy",
  "description": "Strategy for crossing minimization.",
  "choices": ["LAYER_SWEEP", "INTERACTIVE", "NONE"],
  "minValue": 0.0,
  "maxValue": 100.0,
  "availableValues": {
    "k": [1, 0],
    "v": ["Interactive", "Layer_sweep"]
  }
}
```

And an example of a KLighD `Action` looks like this:
```json
{
  "actionId": "de.cau.cs.kieler.spviz.osgiviz.viz.actions.UndoAction",
  "displayedName": "Undo",
  "tooltipText": "Undoes the last action performed on the view model."
}
```

### `requestBounds` Action
Third, the server can now send the entire KGraph to the client for (layout and) drawing.
In case the client should do the layout, a `requestBounds` action should be sent:
```json
{
  "kind": "requestBounds",
  "newRoot": {
    "properties": {
      "klighd.diagramBackground": { "red": 30, "green": 30, "blue": 30 }
    },
    "revision": 1,
    "type": "graph",
    "id": "file:///path/to/your/file.kgt",
    "children": [
        KNode (a single root node)
    ]
  }
}

```
The `klighd.diagramBackground` property optionally sets the canvas color, if the default white background is not suited for the diagram's color theme.
How this `KNode` should look like, see [KGraph Schema](https://github.com/kieler/klighd-vscode/blob/main/schema/klighd/SKGraphSchema.json)

### `setModel` action
If instead the server should perform the layout, a `setModel` action should be sent:
```json
{
  "clientId": "keith-diagram_sprotty",
  "action": {
    "kind": "setModel",
    "newRoot": {
      "properties": {},
      "revision": 1,
      "type": "graph",
      "id": "file:///path/to/your/file.kgt",
      "children": [
        KNode (a single root node)
      ]
    }
  }
}

```

### `updateModel` action
If a diagram for the same model has been sent previously already and the a new diagram is sent, an `updateModel` action should be sent instead.
This ensures that the client will try to animate the changes in the diagram.
```json
{
  "kind": "updateModel",
  "newRoot": {
    "properties": {},
    "revision": 2,
    "type": "graph",
    "id": "file:///path/to/your/file.kgt",
    "children": [
      KNode (a single root node)
    ]
  }
}

```

## Further diagram messages

### `checkImages` action
If the graph contains any KImages, the presence of cached images needs to be synchronized before the diagram can be sent. This leads to the following message sequence, staring with a `checkImages` action from the server:
```json
{
  "clientId": "keith-diagram_sprotty",
  "action": {
    "kind": "checkImages",
    "images": [
      {
        "bundleName": "de.cau.cs.kieler.klighd.ide",
        "imagePath": "icons/full/model/error_sign.png"
      }
    ]
  }
}
```
### `checkedImages` action
The client responds with a `checkedImages` response. The `k` and `v` values uniquely identify the image.
```json
{
  "clientId": "keith-diagram_sprotty",
  "action": {
    "kind": "checkedImages",
    "notCached": [
      {
        "k": "de.cau.cs.kieler.klighd.ide",
        "v": "icons/full/model/error_sign.png"
      }
    ],
    "responseId": ""
  }
}
```

### `storeImages` action
If the `checkedImages` action contains any non-cached images, the server sends base-64 versions of the images to the client in a `storeImages` action.
TODO: other image formats are possible as well with no bundlePath/imagePath but other refs.
```json
{
  "kind": "storeImages",
  "images": [
    {
      "k": {
        "k": "de.cau.cs.kieler.klighd.ide",
        "v": "icons/full/model/error_sign.png"
      },
      "v": "iVBORw0[.....full base-64 encoding of the image...]CYII\u003d"
    }
  ]
}
```

### `requestDiagramPiece` action
If the `incrementalDiagramGenerator` preference is turned on (this currently is a debug option and not fully supported yet), the diagram generation starts incrementally sending diagram pieces.
The first message in the diagram generation loop are the same as without the incremental strategy.
After the initial `setModel` or `updateModel` action, that only contains the root node, further messages are requested and sent.
The client may request new diagram pieces based on their ID with the `requestDiagramPiece` action:
```json
{
  "kind": "requestDiagramPiece",
  "requestId": "client_31",
  "modelElementId": "$root"
}
```

### `setDiagramPiece` action
This is then responded by the server with this message:
```json
{
  "kind": "setDiagramPiece",
  "diagramPiece": {
    KNode
  }
}
```
This message contains the requested element and its immediate children, but nothing further of the graph.
The client will integrate this into the current diagram.
After this, the client will request diagram pieces for all new graph IDs, expecting an answer, incrementally building up the diagram.

### `changeClientColorPreferences` action
If the client changes its color theme, this message is sent to the server.
The server is then expected to regenerate the current diagram with this new theme and send it back to the client.
```json
{
  "kind": "changeClientColorPreferences",
  "clientColorPreferences": {
    "kind": 1,
    "foreground": "#d4d4d4",
    "background": "#1e1e1e",
    "highlight": "#007fd4"
  }
}

```

TODO: `general/replaceContentInFile`
TODO: all the interactive messages/actions
TODO: all the structure-based editing messages (not yet on main)

## Interaction between diagram and text
### `diagram/openInTextEditor` message
After the client sent an `elementSelectedAction` (see below), the server might answer with a Sprotty `diagram/openInTextEditor` message.
This message traces the selected elements back to the ranges in the textual definition of the source that correspond to the selected element.
This message is only sent if the `diagram.shouldSelectText` preference is `true`.

```json
{
  "jsonrpc": "2.0",
  "method": "diagram/openInTextEditor",
  "params": {
    "location": {
      "uri": "file:///path/to/your.file",
      "range": {
        "start": { "line": 4, "character": 8 },
        "end": { "line": 4, "character": 13 }
      }
    },
    "forceOpen": false
  }
}
```
### `textDocument/documentHighlight`
The other way around, clicking on elements in the text on the client causes a `textDocument/documentHighligh` message, to which the server may respond to with a diagram selection of the corresponding diagram elements.
It should only be handled this way, if the `diagram.shouldSelectDiagram` preference is `true`.
```json
{
  "jsonrpc": "2.0",
  "id": 29,
  "method": "textDocument/documentHighlight",
  "params": {
    "textDocument": {
      "uri": "file:///path/to/your.file"
    },
    "position": { "line": 4, "character": 12 }
  }
}
```
This could lead to a sequence of responses by the server using default Sprotty selection actions like these to deselect everything and after that only select the clicked element, centering that clicked element as well:
```json
{
  "clientId": "keith-diagram_sprotty",
  "action": { "kind": "allSelected", "select": false }
}
```
```json
{
  "clientId": "keith-diagram_sprotty",
  "action": {
    "kind": "elementSelected",
    "selectedElementsIDs": ["$root$NMOTOR"],
    "deselectedElementsIDs": [],
    "preventOpenSelection": true
  }
}
```
```json
{
  "clientId": "keith-diagram_sprotty",
  "action": {
    "kind": "fit",
    "elementIds": ["$root$NMOTOR"],
    "maxZoom": 1.0,
    "animate": true
  }
}
```



## Interaction with the diagram
### Other Sprotty actions, such as `elementSelected` action
As an example, there are other actions defined and implemented by Sprotty, to that the server may react to. One of them is the `elementSelected` action, as shown here:
```json
{
  "kind": "elementSelected",
  "selectedElementsIDs": ["$root$NMOTOR"],
  "deselectedElementsIDs": ["$root$NMOTOR$NSetSpeeds$NSetSpeeds$NProcessInputs"]
}
```
For more information on such messages, see [Sprotty's action implementations](https://github.com/eclipse-sprotty/sprotty/blob/master/packages/sprotty-protocol/src/actions.ts).

### `performAction` action

The diagram's renderings may have actions associated with them that should be executed when clicking on them.
This message is sent by the client when the action is clicked on the client and should be executed by the server.
```json
{
  "kind": "performAction",
  "actionId": "de.cau.cs.kieler.sccharts.ui.synthesis.hooks.actions.MemorizingExpandCollapseAction",
  "kGraphElementId": "$root$NMOTOR$NSetSpeeds$NSetSpeeds$NProcessInputs",
  "kRenderingId": "$root$NMOTOR$NSetSpeeds$NSetSpeeds$NProcessInputs$$$R2$R0",
  "revision": 1
}
```
The IDs identify the action on the server (as sent in the rendering of the model), and the corresponding graph element and rendering.

## Interaction with the sidebar
- Whenever a *preference* is changed in the diagram sidebar, a `keith/preferences/setPreferences` message is sent, see above definition.

### `refreshLayout` action
This action can be issued by the sidebar and should lead to the server re-layout the current diagram and send it back to the client.
This message should therefore skip re-generating the model and only do the layout step after (see the architectural overview above.)
This message is only sensible to be handled if `needsServerLayout` is `true`.
```json
{
  "clientId": "keith-diagram_sprotty",
  "action": { "kind": "refreshLayout" }
}
```

### `refreshDiagram` action
This action should lead to the diagram to be fully re-generated.
```json
{
  "clientId": "keith-diagram_sprotty",
  "action": { "kind": "refreshDiagram" }
}
```

### `setSynthesis` action
The user can request a new diagram using a different synthesis via the sidebar.
This issues a `setSynthesis` action:
```json
{
  "clientId": "keith-diagram_sprotty",
  "action": {
    "kind": "setSynthesis",
    "id": "de.cau.cs.kieler.klighd.ide.syntheses.EObjectFallbackSynthesis"
  }
}
```
This should then trigger a new diagram generation loop using this synthesis.

### `keith/diagramOptions/setSynthesisOptions` method
To change an option, the client may send this message.
Most important, next to identifying the option, is the new `currentValue` of the option.
```json
{
  "jsonrpc": "2.0",
  "method": "keith/diagramOptions/setSynthesisOptions",
  "params": {
    "synthesisOptions": [
      SynthesisOption
    ],
    "uri": "file:///path/to/your.file"
  }
}
```

### `keith/diagramOptions/performAction` message
KLighD actions can also be performed by clicking on a button on the sidebar, if the `updateOptions` action contains such actions.
```json
{
  "jsonrpc": "2.0",
  "method": "keith/diagramOptions/performAction",
  "params": {
    "actionId": "de.cau.cs.kieler.spviz.osgiviz.viz.actions.UndoAction",
    "uri": "file:///path/to/your.file"
  }
}
```

### `keith/diagramOptions/setLayoutOptions` message
Layout options can also be changed from the sidebar, if the `updateOptions` action contains such layout options.

```json
{
  "jsonrpc": "2.0",
  "method": "keith/diagramOptions/setLayoutOptions",
  "params": {
    "layoutOptions": [
      {
        "optionId": "org.eclipse.elk.layered.highDegreeNodes.treatment",
        "value": true
      }
    ],
    "uri": "file:///path/to/your.file"
  }
}
```
