@processmaker/modeler 1.43.0 → 1.43.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,7 +23,7 @@
23
23
  @close="close"
24
24
  @save-state="pushToUndoStack"
25
25
  @clearSelection="clearSelection"
26
- :players="players"
26
+ :players="filteredPlayers"
27
27
  @action="handleToolbarAction"
28
28
  />
29
29
  <b-row class="modeler h-100">
@@ -56,6 +56,29 @@
56
56
  v-if="showWelcomeMessage"
57
57
  />
58
58
 
59
+ <CreateAssetsCard
60
+ ref="createAssetsCard"
61
+ v-if="isAiGenerated"
62
+ @onGenerateAssets="generateAssets()"
63
+ @closeCreateAssets="onCloseCreateAssets()"
64
+ />
65
+
66
+ <GeneratingAssetsCard
67
+ ref="generatingAssetsCard"
68
+ v-if="generatingAi"
69
+ />
70
+
71
+ <AssetsCreatedCard
72
+ ref="assetsCreatedCard"
73
+ v-if="assetsCreated"
74
+ @closeAssetsCreated="onCloseAssetsCreated()"
75
+ />
76
+
77
+ <CreateAssetsFailCard
78
+ ref="createAssetsFailCard"
79
+ v-if="false"
80
+ />
81
+
59
82
  <InspectorButton
60
83
  ref="inspector-button"
61
84
  v-show="showComponent && showInspectorButton"
@@ -143,6 +166,7 @@
143
166
  />
144
167
 
145
168
  <RailBottom
169
+ v-if="!generatingAi"
146
170
  :nodeTypes="nodeTypes"
147
171
  :paper-manager="paperManager"
148
172
  :graph="graph"
@@ -165,6 +189,11 @@
165
189
  @save-state="pushToUndoStack"
166
190
  :isMultiplayer="isMultiplayer"
167
191
  />
192
+ <RemoteCursor
193
+ v-for="player in filteredPlayers"
194
+ :key="player.id"
195
+ :data="player"
196
+ />
168
197
  </b-row>
169
198
  </span>
170
199
  </template>
@@ -172,12 +201,17 @@
172
201
  <script>
173
202
 
174
203
  import Vue from 'vue';
204
+
175
205
  import _ from 'lodash';
176
206
  import { dia } from 'jointjs';
177
207
  import boundaryEventConfig from '../nodes/boundaryEvent';
178
208
  import BpmnModdle from 'bpmn-moddle';
179
209
  import ExplorerRail from '../rails/explorer-rail/explorer';
180
210
  import WelcomeMessage from '../welcome/WelcomeMessage.vue';
211
+ import CreateAssetsCard from '../aiMessages/CreateAssetsCard.vue';
212
+ import GeneratingAssetsCard from '../aiMessages/GeneratingAssetsCard.vue';
213
+ import AssetsCreatedCard from '../aiMessages/AssetsCreatedCard.vue';
214
+ import CreateAssetsFailCard from '../aiMessages/CreateAssetsFailCard.vue';
181
215
  import { isJSON } from 'lodash-contrib';
182
216
  import pull from 'lodash/pull';
183
217
  import remove from 'lodash/remove';
@@ -251,6 +285,10 @@ export default {
251
285
  Selection,
252
286
  RailBottom,
253
287
  WelcomeMessage,
288
+ CreateAssetsCard,
289
+ GeneratingAssetsCard,
290
+ AssetsCreatedCard,
291
+ CreateAssetsFailCard,
254
292
  RemoteCursor,
255
293
  },
256
294
  props: {
@@ -348,6 +386,12 @@ export default {
348
386
  isResizingPreview: false,
349
387
  currentCursorPosition: 0,
350
388
  previewPanelWidth: 600,
389
+ isAiGenerated: window.ProcessMaker?.modeler?.isAiGenerated,
390
+ generatingAi: false,
391
+ assetsCreated: false,
392
+ // ^ TODO: To be changed depending on microservice response
393
+ currentNonce: null,
394
+ promptSessionId: '',
351
395
  flowTypes: [
352
396
  'processmaker-modeler-sequence-flow',
353
397
  'processmaker-modeler-message-flow',
@@ -401,6 +445,12 @@ export default {
401
445
  },
402
446
  },
403
447
  computed: {
448
+ filteredPlayers() {
449
+ const allPlayers = _.uniqBy(this.players, 'name');
450
+ return allPlayers.filter(player => {
451
+ return player.name.toLowerCase() !== window.ProcessMaker.user?.fullName.toLowerCase();
452
+ });
453
+ },
404
454
  showWelcomeMessage() {
405
455
  return !this.selectedNode && !this.nodes.length && !store.getters.isReadOnly && this.isLoaded;
406
456
  },
@@ -1613,6 +1663,7 @@ export default {
1613
1663
  },
1614
1664
  pointerMoveHandler(event) {
1615
1665
  const { clientX: x, clientY: y } = event;
1666
+ window.ProcessMaker.EventBus.$emit('multiplayer-updateMousePosition', { top: y, left: x });
1616
1667
  if (store.getters.isReadOnly) {
1617
1668
  if (this.canvasDragPosition && !this.clientLeftPaper) {
1618
1669
  this.paperManager.translate(
@@ -1662,8 +1713,23 @@ export default {
1662
1713
  enableMultiplayer(value) {
1663
1714
  store.commit('enableMultiplayer', value);
1664
1715
  },
1665
- addPlayer(player) {
1666
- this.players.push(player);
1716
+ emptyPlayers(){
1717
+ this.players = [];
1718
+ },
1719
+ addPlayer(data) {
1720
+ const player = this.players.find(player => player.id === data.id);
1721
+ if (!player) {
1722
+ this.players.push(data);
1723
+ }
1724
+ },
1725
+ /**
1726
+ * Update Client Cursor
1727
+ * @param {Object} data
1728
+ */
1729
+ updateClientCursor(data) {
1730
+ if (data) {
1731
+ this.players = this.players.map((item) => (item.id === data.id ? { ...item, ...data } : item));
1732
+ }
1667
1733
  },
1668
1734
  removePlayer(playerId) {
1669
1735
  const playerIndex = this.players.findIndex(player => player.id === playerId);
@@ -1678,6 +1744,119 @@ export default {
1678
1744
  updateLasso(){
1679
1745
  this.$refs.selector.updateSelectionBox();
1680
1746
  },
1747
+ getNonce() {
1748
+ const max = 999999999999999;
1749
+ const nonce = Math.floor(Math.random() * max);
1750
+ this.currentNonce = nonce;
1751
+ localStorage.currentNonce = this.currentNonce;
1752
+ },
1753
+ getPromptSessionForUser() {
1754
+ // Get sessions list
1755
+ let promptSessions = localStorage.getItem('promptSessions');
1756
+
1757
+ // If promptSessions does not exist, set it as an empty array
1758
+ promptSessions = promptSessions ? JSON.parse(promptSessions) : [];
1759
+ let item = promptSessions.find(item => item.userId === window.ProcessMaker?.modeler?.process?.user_id && item.server === window.location.host);
1760
+
1761
+ if (item) {
1762
+ return item.promptSessionId;
1763
+ }
1764
+
1765
+ return '';
1766
+ },
1767
+ setPromptSessions(promptSessionId) {
1768
+ let index = 'userId';
1769
+ let id = window.ProcessMaker?.modeler?.process?.user_id;
1770
+
1771
+ // Get sessions list
1772
+ let promptSessions = localStorage.getItem('promptSessions');
1773
+
1774
+ // If promptSessions does not exist, set it as an empty array
1775
+ promptSessions = promptSessions ? JSON.parse(promptSessions) : [];
1776
+
1777
+ let item = promptSessions.find(item => item[index] === id && item.server === window.location.host);
1778
+
1779
+ if (item) {
1780
+ item.promptSessionId = promptSessionId;
1781
+ } else {
1782
+ promptSessions.push({ [index]: id, server: window.location.host, promptSessionId });
1783
+ }
1784
+
1785
+ localStorage.setItem('promptSessions', JSON.stringify(promptSessions));
1786
+ },
1787
+ removePromptSessionForUser() {
1788
+ // Get sessions list
1789
+ let promptSessions = localStorage.getItem('promptSessions');
1790
+
1791
+ // If promptSessions does not exist, set it as an empty array
1792
+ promptSessions = promptSessions ? JSON.parse(promptSessions) : [];
1793
+
1794
+ let item = promptSessions.find(item => item.userId === window.ProcessMaker?.modeler?.process?.user_id && item.server === window.location.host);
1795
+
1796
+ if (item) {
1797
+ item.promptSessionId = '';
1798
+ }
1799
+
1800
+ localStorage.setItem('promptSessions', JSON.stringify(promptSessions));
1801
+ },
1802
+ fetchHistory() {
1803
+ let url = '/package-ai/getPromptSessionHistory';
1804
+
1805
+ let params = {
1806
+ server: window.location.host,
1807
+ processId: this.processId,
1808
+ };
1809
+
1810
+ if (this.promptSessionId && this.promptSessionId !== null && this.promptSessionId !== '') {
1811
+ params = {
1812
+ promptSessionId: this.promptSessionId,
1813
+ };
1814
+ }
1815
+
1816
+ window.ProcessMaker.apiClient.post(url, params)
1817
+ .then(response => {
1818
+ this.setPromptSessions((response.data.promptSessionId));
1819
+ this.promptSessionId = (response.data.promptSessionId);
1820
+ localStorage.promptSessionId = (response.data.promptSessionId);
1821
+ }).catch((error) => {
1822
+ const errorMsg = error.response?.data?.message || error.message;
1823
+
1824
+ if (error.response.status === 404) {
1825
+ this.removePromptSessionForUser();
1826
+ localStorage.promptSessionId = '';
1827
+ this.promptSessionId = '';
1828
+ } else {
1829
+ window.ProcessMaker.alert(errorMsg, 'danger');
1830
+ }
1831
+ });
1832
+ },
1833
+ generateAssets() {
1834
+ this.getNonce();
1835
+
1836
+ // TODO: Add endpoint to get the promprSessionId
1837
+
1838
+ const params = {
1839
+ promptSessionId: this.promptSessionId,
1840
+ nonce: this.currentNonce,
1841
+ processId: window.ProcessMaker?.modeler?.process?.id,
1842
+ };
1843
+
1844
+ const url = '/package-ai/generateProcessArtifacts';
1845
+
1846
+ window.ProcessMaker.apiClient.post(url, params)
1847
+ .then(() => {
1848
+ })
1849
+ .catch((error) => {
1850
+ const errorMsg = error.response?.data?.message || error.message;
1851
+ window.ProcessMaker.alert(errorMsg, 'danger');
1852
+ });
1853
+ },
1854
+ onCloseCreateAssets() {
1855
+ this.isAiGenerated = false;
1856
+ },
1857
+ onCloseAssetsCreated() {
1858
+ this.assetsCreated = false;
1859
+ },
1681
1860
  },
1682
1861
  created() {
1683
1862
  if (runningInCypressTest()) {
@@ -1866,6 +2045,14 @@ export default {
1866
2045
  this.redirect(redirectUrl);
1867
2046
  }
1868
2047
  });
2048
+
2049
+ // AI Setup
2050
+ this.currentNonce = localStorage.currentNonce;
2051
+ if (!localStorage.getItem('promptSessions') || localStorage.getItem('promptSessions') === 'null') {
2052
+ localStorage.setItem('promptSessions', JSON.stringify([]));
2053
+ }
2054
+ this.promptSessionId = this.getPromptSessionForUser();
2055
+ this.fetchHistory();
1869
2056
  },
1870
2057
  };
1871
2058
  </script>
@@ -1,33 +1,19 @@
1
1
  <template>
2
- <div class="remote-cursor" :style="{ left: left + 'px', top: top + 'px' }">
3
- <inline-svg :src="cursorIcon" :fill="cursorColor" />
4
-
2
+ <div class="remote-cursor" :style="{ left: data.cursor.left + 'px', top: data.cursor.top + 'px' }">
3
+ <svg width="23" height="19" viewBox="0 0 23 19" xmlns="http://www.w3.org/2000/svg">
4
+ <path d="M11.5 17.5L2 1L20.5 7L13 9L11.5 17.5Z" :stroke="stroke" :fill="data.color"/>
5
+ </svg>
5
6
  <div class="remote-username">
6
- {{ username }}
7
+ {{ data.name }}
7
8
  </div>
8
9
  </div>
9
10
  </template>
10
11
 
11
12
  <script>
12
- import InlineSvg from 'vue-inline-svg';
13
-
14
13
  export default {
15
- components: {
16
- InlineSvg,
17
- },
18
14
  props: {
19
- cursorColor: {
20
- type: String,
21
- default: '#000000',
22
- },
23
- username: {
24
- type: String,
25
- },
26
- top: {
27
- type: Number,
28
- },
29
- left: {
30
- type: Number,
15
+ data: {
16
+ type: Object,
31
17
  },
32
18
  },
33
19
  data() {
@@ -35,6 +21,12 @@ export default {
35
21
  cursorIcon: require('@/components/multiplayer/remoteCursor/cursor.svg'),
36
22
  };
37
23
  },
24
+ computed: {
25
+ stroke: () => {
26
+ return '#212529';
27
+ },
28
+ },
29
+
38
30
  };
39
31
  </script>
40
32
 
@@ -45,6 +37,7 @@ export default {
45
37
  display: flex;
46
38
  width: auto;
47
39
  height: 34px;
40
+ z-index: 2;
48
41
  }
49
42
 
50
43
  &-username {
@@ -84,12 +84,12 @@ export default {
84
84
  // Control coordinates
85
85
  const controlEl = entries[0].target.getBoundingClientRect();
86
86
  // Zoom coordinates
87
- const zoomEl = this.$refs.zoomBox.$el.getBoundingClientRect();
87
+ const zoomEl = this.$refs.zoomBox?.$el.getBoundingClientRect();
88
88
  // Undo/Redo coordinates
89
- const undoRedoEl = this.$refs.undoRedoBox.$el.getBoundingClientRect();
89
+ const undoRedoEl = this.$refs.undoRedoBox?.$el.getBoundingClientRect();
90
90
 
91
91
  // Checks overlapping
92
- if (this.overlap) {
92
+ if (this.overlap && undoRedoEl) {
93
93
  if (controlEl.width < this.widthOverlapControl) {
94
94
  // Get the computed styles of the ZoomControl
95
95
  const zoomStyles = window.getComputedStyle(this.$refs.zoomBox.$el);
@@ -107,7 +107,7 @@ export default {
107
107
  this.overlap = false;
108
108
  }
109
109
  }
110
- } else if (undoRedoEl.left < zoomEl.right) {
110
+ } else if (undoRedoEl?.left < zoomEl?.right) {
111
111
  this.overlap = true;
112
112
  this.widthOverlapControl = controlEl.width;
113
113
  this.leftOverlapUndoRedo = undoRedoEl.left;
@@ -7,6 +7,8 @@
7
7
  @openPanel="handleOpenPanel"
8
8
  />
9
9
 
10
+ <AiGenerateButton/>
11
+
10
12
  <ValidateButton @openIssue="handleOpenIssue" />
11
13
 
12
14
  <ValidatePanel
@@ -22,11 +24,13 @@
22
24
  import store from '@/store';
23
25
  import { ValidateButton, ValidateIssue, ValidatePanel } from '@/components/topRail/validateControl';
24
26
  import MultiplayerViewUsers from '@/components/topRail/multiplayerViewUsers/MultiplayerViewUsers';
27
+ import AiGenerateButton from '../aiMessages/AiGenerateButton.vue';
25
28
  export default {
26
29
  components: {
27
30
  ValidateButton,
28
31
  ValidateIssue,
29
32
  ValidatePanel,
33
+ AiGenerateButton,
30
34
  MultiplayerViewUsers,
31
35
  },
32
36
  props: {
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <b-avatar-group class="container" v-show="isMultiplayer">
3
- <template v-for="item in filteredPlayers" >
3
+ <template v-for="item in players" >
4
4
  <Avatar
5
5
  :badgeBackgroundColor="item.color"
6
6
  :imgSrc="item.avatar"
@@ -13,7 +13,6 @@
13
13
  </template>
14
14
 
15
15
  <script>
16
- import { uniqBy } from 'lodash';
17
16
  import Avatar from '@/components/topRail/multiplayerViewUsers/avatar/Avatar';
18
17
  import store from '@/store';
19
18
 
@@ -28,12 +27,6 @@ export default {
28
27
  },
29
28
  },
30
29
  computed: {
31
- filteredPlayers() {
32
- const allPlayers = uniqBy(this.players, 'name');
33
- return allPlayers.filter(player => {
34
- return player.name.toLowerCase() !== window.ProcessMaker.user?.fullName.toLowerCase();
35
- });
36
- },
37
30
  isMultiplayer: () => store.getters.isMultiplayer,
38
31
  },
39
32
  };
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <span
3
3
  class="b-avatar rounded-circle"
4
- :style="{'backgroundColor': generateColorHsl(userName, saturationRange, lightnessRange)}"
4
+ :style="{'backgroundColor': backgroundColor}"
5
5
  >
6
6
  <span v-if="imgSrc" class="b-avatar-img" :id="id">
7
7
  <img :src="imgSrc" :alt="userName">
@@ -46,16 +46,9 @@ export default {
46
46
  saturation: 50,
47
47
  lightness: 50,
48
48
  range: 10,
49
+ backgroundColor: '#104A75',
49
50
  };
50
51
  },
51
- computed: {
52
- saturationRange() {
53
- return this.getRange(this.saturation, this.range);
54
- },
55
- lightnessRange() {
56
- return this.getRange(this.lightness, this.range);
57
- },
58
- },
59
52
  methods: {
60
53
  /**
61
54
  * Get the initials from a given name.
@@ -69,73 +62,6 @@ export default {
69
62
  const lastNameIn = nameArray[nameArray.length - 1].charAt(0).toUpperCase();
70
63
  return `${firstNameIn}${lastNameIn}`;
71
64
  },
72
- /**
73
- * Calculates a hash value for a given string.
74
- *
75
- * @param {string} str - The input string for which the hash needs to be calculated.
76
- * @returns {number} The calculated hash value for the input string.
77
- */
78
- getHashOfString(str){
79
- let hash = 0;
80
- for (let i = 0; i < str.length; i++) {
81
- hash = str.charCodeAt(i) + ((hash << 5) - hash);
82
- }
83
- hash = Math.abs(hash);
84
- return hash;
85
- },
86
- /**
87
- * Calculates a range around a given value.
88
- *
89
- * @param {number} value - The central value.
90
- * @param {number} range - The range value.
91
- * @returns {number[]} An array containing the lower and upper bounds of the range.
92
- */
93
- getRange(value, range) {
94
- return [Math.max(0, value-range), Math.min(value + range, 100)];
95
- },
96
- /**
97
- * Get the hash number to within our range
98
- *
99
- * @param {Number} hash
100
- * @param {Number} min
101
- * @param {Number} max
102
- * @returns {Number}
103
- */
104
- normalizeHash(hash, min, max){
105
- return Math.floor((hash % (max - min)) + min);
106
- },
107
- /**
108
- *Generate Unique Color, create a string using our h,s,l values.
109
- * @param {String} name
110
- * @param {Array} saturationRange
111
- * @param {Array} lightnessRange
112
- * @returns {Number}
113
- */
114
- generateHSL(name, saturationRange, lightnessRange) {
115
- const hash = this.getHashOfString(name);
116
- const h = this.normalizeHash(hash, 0, 360);
117
- const s = this.normalizeHash(hash, saturationRange[0], saturationRange[1]);
118
- const l = this.normalizeHash(hash, lightnessRange[0], lightnessRange[1]);
119
- return [h, s, l];
120
- },
121
- /**
122
- * Convert HSL array to string
123
- * @param {Array} hsl
124
- * @returns {String}
125
- */
126
- HSLtoString(hsl) {
127
- return `hsl(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%)`;
128
- },
129
- /**
130
- * Generate a unique hsl value.
131
- * @param {String} name
132
- * @param {Array} saturationRange
133
- * @param {Array} lightnessRange
134
- * @returns {String}
135
- */
136
- generateColorHsl(id, saturationRange, lightnessRange) {
137
- return this.HSLtoString(this.generateHSL(id, saturationRange, lightnessRange));
138
- },
139
65
  },
140
66
  };
141
67
 
@@ -2,5 +2,4 @@
2
2
  text-align: center;
3
3
  line-height: 2.4rem;
4
4
  color: #ffffff;
5
- font-weight: bold;
6
5
  }
@@ -148,7 +148,7 @@ export default {
148
148
  },
149
149
  };
150
150
  </script>
151
- <style>
151
+ <style scoped>
152
152
  .message {
153
153
  color: #5f666d;
154
154
  font-style: italic;
@@ -187,11 +187,6 @@ export default {
187
187
  border-color: #0872C2;
188
188
  }
189
189
 
190
- .ai-icon {
191
- width: 22px;
192
- height: 22px;
193
- }
194
-
195
190
  .ai-icon {
196
191
  width: 22px;
197
192
  height: 22px;
@@ -6,7 +6,7 @@ import Room from './room';
6
6
  import store from '@/store';
7
7
  import { getBoundaryEventData } from '@/components/nodes/boundaryEvent/boundaryEventUtils';
8
8
  import { InspectorUtils } from './inspector.utils';
9
-
9
+ import ColorUtil from '../colorUtil';
10
10
  export default class Multiplayer {
11
11
  clientIO = null;
12
12
  yDoc = null;
@@ -16,7 +16,7 @@ export default class Multiplayer {
16
16
  room = null;
17
17
  inspector = null;
18
18
  deletedItem = null;
19
-
19
+ colorUtil = null;
20
20
  constructor(modeler) {
21
21
  // define document
22
22
  this.yDoc = new Y.Doc();
@@ -42,33 +42,28 @@ export default class Multiplayer {
42
42
  } else {
43
43
  this.clientIO.disconnect();
44
44
  }
45
+ this.colorUtil = new ColorUtil(50, 50, 10);
45
46
  }
47
+
46
48
  webSocketEvents() {
47
49
  this.clientIO.on('connect', () => {
48
50
  // Join the room
51
+ this.modeler.emptyPlayers();
49
52
  this.clientIO.emit('joinRoom', {
50
53
  roomName: this.room.getRoom(),
51
54
  clientName: window.ProcessMaker.user?.fullName,
52
55
  clientAvatar: window.ProcessMaker.user?.avatar,
56
+ clientColor: window.ProcessMaker.user?.color || this.colorUtil.randomColor(window.ProcessMaker.user?.fullName),
57
+ clientCursor: {
58
+ top: 300,
59
+ left: 300,
60
+ },
53
61
  });
54
62
  });
55
63
 
56
64
  this.clientIO.on('clientJoined', (payload) => {
57
65
  this.modeler.enableMultiplayer(payload.isMultiplayer);
58
-
59
- if (payload.isMultiplayer) {
60
- payload.clients.map(client => {
61
- const newPlayer = {
62
- id: client.id,
63
- name: client.name,
64
- color: client.color,
65
- avatar: client.avatar || null,
66
- top: 90,
67
- left: 80,
68
- };
69
- this.modeler.addPlayer(newPlayer);
70
- });
71
- }
66
+ this.addPlayers(payload);
72
67
  });
73
68
 
74
69
  this.clientIO.on('clientLeft', (payload) => {
@@ -85,6 +80,10 @@ export default class Multiplayer {
85
80
  this.syncLocalNodes(clientId);
86
81
  }
87
82
  });
83
+
84
+ this.clientIO.on('updateUserCursor', async(payload) => {
85
+ this.updateClientCursor(payload);
86
+ });
88
87
 
89
88
  // Listen for updates when a new element is added
90
89
  this.clientIO.on('createElement', async(payload) => {
@@ -198,6 +197,49 @@ export default class Multiplayer {
198
197
  this.updateFlows(data);
199
198
  }
200
199
  });
200
+ window.ProcessMaker.EventBus.$on('multiplayer-updateMousePosition', ( data ) => {
201
+ if (this.modeler.isMultiplayer) {
202
+ this.updateMousePosition(data);
203
+ }
204
+ });
205
+ }
206
+ /**
207
+ * Add a Player
208
+ * @param {Object} payload
209
+ */
210
+ addPlayers(payload) {
211
+ if (payload.isMultiplayer) {
212
+ payload.clients.map(client => {
213
+ const newPlayer = {
214
+ id: client.id,
215
+ name: client.name,
216
+ color: client.color,
217
+ avatar: client.avatar,
218
+ cursor: client.cursor,
219
+ };
220
+ this.modeler.addPlayer(newPlayer);
221
+ });
222
+ }
223
+ }
224
+ /**
225
+ * Updates the mouse position
226
+ * @param {Object} data
227
+ */
228
+ updateMousePosition(data) {
229
+ this.clientIO.emit('cursorTrackingUpdate', {
230
+ roomName: this.room.getRoom(),
231
+ clientId: this.clientIO.id,
232
+ clientCursor: data,
233
+ });
234
+ }
235
+ /**
236
+ * Update Client cursor handler
237
+ * @param {Object} payload
238
+ */
239
+ updateClientCursor(payload){
240
+ payload.clients.map(client => {
241
+ this.modeler.updateClientCursor(client);
242
+ });
201
243
  }
202
244
  /**
203
245
  * Sync the modeler nodes with the microservice