janusweb 1.7.2 → 1.7.3

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.
package/README.md CHANGED
@@ -77,7 +77,7 @@ Run a **local-first** viewer locally on your desktop, raspberry pi (ARM64) or se
77
77
 
78
78
  | executable | platforms |
79
79
  |-|-|
80
- | [janusxr.com](https://jbaicoianu.github.io/janusweb/janusxr.com) | <img src="https://i.imgur.com/v7cYVq1.png"/> |
80
+ | [janusxr.com](https://github.com/jbaicoianu/janusweb/releases/latest/download/janusxr.com) | <img src="https://i.imgur.com/v7cYVq1.png"/> |
81
81
 
82
82
  Run `janusxr.com` from anywhere, or put it into a folder with your 3D models or JanusXR rooms:
83
83
 
@@ -1,7 +1,6 @@
1
- <details name="toggle" open aria-live="polite" role="log">
2
- <summary>Text Chat <ui-notificationbutton name="unread" count="0"></ui-notificationbutton></summary>
3
- <janus-voip-client-callbutton></janus-voip-client-callbutton>
4
- <collection-indexed id="chatmessages" index="timestamp"></collection-indexed>
5
- <ui-list name="chatlist" collection="chatmessages" itemtemplate="janus.comms.chat_message" scrollable="1"></ui-list>
6
- <ui-input name="chatinput" placeholder="Press T for text chat"></ui-input>
7
- </details>
1
+ <collection-indexed id="chatmessages" index="timestamp"></collection-indexed>
2
+ <ui-list name="chatlist" class="comms-log" collection="chatmessages" itemtemplate="janus.comms.chat_message" scrollable="1" aria-live="polite" role="log"></ui-list>
3
+ <div class="comms-chat-input">
4
+ <ui-notificationbutton name="unread" count="0" title="Chat" label="&#128172;"></ui-notificationbutton>
5
+ <ui-input name="chatinput" placeholder="Press T to chat&#8230;"></ui-input>
6
+ </div>
@@ -1,45 +1,212 @@
1
1
  @import url('https://fonts.googleapis.com/css?family=Inconsolata|Montserrat');
2
2
 
3
+ /*
4
+ * Themeable slim comms bar. Consumers (e.g. a host app) restyle by setting
5
+ * these custom properties on janus-comms-panel rather than overriding rules.
6
+ */
3
7
  janus-comms-panel {
4
- display: flex;
5
- background: rgba(0,0,0,.5);
6
- flex-direction: column;
8
+ --comms-bar-bg: rgba(0,0,0,.5);
9
+ --comms-accent: #4cb96f; /* usernames, active states, scrollbar */
10
+ --comms-text: #eee;
11
+ --comms-muted: #aaa;
12
+ --comms-font: Inconsolata, monospace;
13
+ --comms-radius: .5em;
14
+ --comms-log-width: 22em;
15
+ --comms-log-max-height: 40vh;
16
+ --comms-show-presence: block; /* set to none to hide join/part/disconnect lines */
17
+ --comms-status-connected: #0a0;
18
+ --comms-status-connecting: #cc0;
19
+ --comms-status-disconnecting: #999;
20
+ --comms-status-disconnected: #b00;
21
+ --comms-status-private: #aaa;
22
+
23
+ position: relative;
24
+ display: inline-block;
7
25
  z-index: 100;
8
- padding-right: .5em;
9
- width: 25vw;
10
- min-width: 20em;
26
+ color: var(--comms-text);
27
+ font-family: var(--comms-font);
28
+ }
29
+
30
+ .janus-comms-bar {
31
+ display: flex;
32
+ flex-direction: row;
33
+ align-items: center;
34
+ gap: .25em;
35
+ padding: .2em .35em;
36
+ background: var(--comms-bar-bg);
37
+ border-radius: var(--comms-radius);
38
+ text-shadow: 0 0 3px #000, 0 0 1px #000;
11
39
  }
12
- janus-comms-panel details>summary {
40
+
41
+ /* Shared pill look for the interactive chips in the bar */
42
+ .janus-comms-bar>janus-comms-userlist>.comms-roster>summary,
43
+ .janus-comms-bar>janus-comms-invite {
13
44
  cursor: pointer;
14
45
  outline: 0;
46
+ border-radius: var(--comms-radius);
47
+ padding: .15em .4em;
48
+ white-space: nowrap;
49
+ transition: background 120ms linear;
15
50
  }
16
- janus-comms-chat ui-list[name="chatlist"] {
17
- height: 10em;
18
- overflow-y: scroll;
19
- font-family: Inconsolata, monospace;
20
- transform: rotate(0); /* Using an empty transform here forces the browser to composite this element, which fixes bugs which cause elements to disappear while scrolling */
21
- width: 100%;
51
+ .janus-comms-bar>janus-comms-userlist>.comms-roster>summary:hover,
52
+ .janus-comms-bar>janus-comms-invite:hover,
53
+ .janus-comms-bar>janus-comms-invite.state_active {
54
+ background: rgba(255,255,255,.15);
22
55
  }
23
- janus-comms-chat ui-list[name="chatlist"]>ui-item {
24
- padding: 0;
25
- text-shadow: 0 0 4px #000;
26
- border: 0;
56
+
57
+ /* ---- Connection status: a single dot in the bar ---- */
58
+ janus-comms-status {
59
+ display: flex;
60
+ align-items: center;
61
+ font-size: .8em;
62
+ }
63
+ janus-comms-status>span { /* the verbose "Connected as ..." label — hidden in bar mode */
64
+ display: none;
65
+ }
66
+ janus-comms-status .indicator {
67
+ display: inline-block;
68
+ width: .7em;
69
+ height: .7em;
70
+ border-radius: 50%;
71
+ box-shadow: 0 0 4px #000;
72
+ background: var(--comms-status-disconnected);
73
+ margin: 0 .25em;
74
+ }
75
+ janus-comms-status .indicator.connecting { background: var(--comms-status-connecting); }
76
+ janus-comms-status .indicator.connected { background: var(--comms-status-connected); }
77
+ janus-comms-status .indicator.disconnecting { background: var(--comms-status-disconnecting); }
78
+ janus-comms-status .indicator.private { background: var(--comms-status-private); }
79
+
80
+ /* ---- Roster: chip in the bar, roster list floats above ---- */
81
+ janus-comms-userlist.comms-roster {
82
+ position: relative;
83
+ display: block;
84
+ font-size: .85em;
85
+ z-index: 20; /* roster popover layers above the chat transcript (z 5) */
86
+ }
87
+ .comms-roster>summary {
88
+ list-style: none;
89
+ }
90
+ .comms-roster>summary::-webkit-details-marker { display: none; }
91
+ .comms-roster>summary ui-indicator {
92
+ font-weight: bold;
93
+ color: var(--comms-accent);
94
+ }
95
+ .comms-roster-popover {
96
+ position: absolute;
97
+ left: 0;
98
+ bottom: 100%;
99
+ margin-bottom: .4em;
100
+ min-width: 12em;
101
+ max-height: var(--comms-log-max-height);
102
+ overflow-y: auto;
103
+ padding: .4em .6em;
104
+ background: rgba(15,15,18,.97);
105
+ border: 1px solid rgba(255,255,255,.12);
106
+ border-radius: var(--comms-radius);
107
+ box-shadow: 0 4px 18px rgba(0,0,0,.55);
108
+ }
109
+ .comms-roster-popover p {
110
+ margin: 0;
111
+ font-size: .9em;
112
+ color: var(--comms-muted);
113
+ max-width: 16em;
114
+ }
115
+ ui-list[name="userlist_room"] {
116
+ display: grid;
117
+ grid-template-columns: repeat(auto-fill, minmax(9em, 1fr));
118
+ gap: .1em .6em;
119
+ font-size: .9em;
120
+ }
121
+
122
+ /* ---- Invite hub trigger ---- */
123
+ janus-comms-invite {
124
+ display: inline-block;
125
+ font-size: 1.05em;
126
+ line-height: 1;
127
+ user-select: none;
128
+ }
129
+
130
+ /* ---- Chat: input lives in the bar, transcript floats above and fades in ---- */
131
+ janus-comms-chat {
132
+ position: static; /* the transcript anchors to the panel so it left-aligns to the bar */
133
+ display: flex;
134
+ flex: 1 1 15em;
135
+ min-width: 11em;
136
+ }
137
+ .comms-chat-input {
138
+ display: flex;
139
+ align-items: center;
140
+ flex: 1;
141
+ gap: .2em;
142
+ }
143
+ janus-comms-chat ui-notificationbutton[name="unread"] {
144
+ cursor: pointer;
145
+ font-size: 1em;
146
+ line-height: 1;
27
147
  }
28
148
  janus-comms-chat ui-input {
29
- width: 100%;
149
+ flex: 1;
30
150
  display: flex;
31
151
  }
32
152
  janus-comms-chat ui-input>input {
33
153
  flex: 1 1;
34
- background: rgba(64,64,64,.6);
35
- color: white;
36
- padding: .25em;
154
+ background: rgba(0,0,0,.25);
155
+ color: var(--comms-text);
156
+ font-family: var(--comms-font);
157
+ padding: .25em .5em;
37
158
  border: 0;
38
- border-top: 1px solid #666;
159
+ border-radius: var(--comms-radius);
160
+ }
161
+ janus-comms-chat ui-input>input:focus {
162
+ background: rgba(0,0,0,.45);
163
+ outline: 1px solid var(--comms-accent);
39
164
  }
40
- janus-comms-chat ui-input>input:hover {
41
- background: rgba(64,64,64,.8);
165
+
166
+ /*
167
+ * The transcript floats above the bar, left-aligned to the panel (not the
168
+ * input box). It's opaque so nothing behind bleeds through, and sits at a low
169
+ * z within the panel so the roster/invite surfaces layer cleanly above it.
170
+ * Visibility is driven explicitly: pinned (click the badge), a transient flash
171
+ * on new/sent messages, or while the input is focused. No hover trigger — that
172
+ * fought the pin and made show/hide feel random.
173
+ */
174
+ janus-comms-chat .comms-log {
175
+ position: absolute;
176
+ left: 0;
177
+ bottom: 100%;
178
+ margin-bottom: .4em;
179
+ width: var(--comms-log-width);
180
+ max-width: 70vw;
181
+ max-height: var(--comms-log-max-height);
182
+ overflow-y: auto;
183
+ padding: .5em .7em;
184
+ background: rgba(15,15,18,.96);
185
+ border: 1px solid rgba(255,255,255,.12);
186
+ border-radius: var(--comms-radius);
187
+ box-shadow: 0 4px 18px rgba(0,0,0,.55);
188
+ font-family: var(--comms-font);
189
+ z-index: 5;
190
+ opacity: 0;
191
+ pointer-events: none;
192
+ /* Empty rotate() forces compositing so rows don't vanish while scrolling */
193
+ transform: translateY(.4em) rotate(0);
194
+ transition: opacity 160ms ease, transform 160ms ease;
195
+ }
196
+ janus-comms-panel.comms-log-pinned janus-comms-chat .comms-log,
197
+ janus-comms-panel.comms-log-flash janus-comms-chat .comms-log,
198
+ janus-comms-chat:focus-within .comms-log {
199
+ opacity: 1;
200
+ pointer-events: auto;
201
+ transform: none;
202
+ }
203
+ janus-comms-chat .comms-log>ui-item {
204
+ padding: 0;
205
+ text-shadow: 0 0 4px #000;
206
+ border: 0;
42
207
  }
208
+
209
+ /* ---- Message rows ---- */
43
210
  janus-comms-chat .message {
44
211
  display: block;
45
212
  text-indent: -1em;
@@ -48,7 +215,8 @@ janus-comms-chat .message {
48
215
  janus-comms-chat .join,
49
216
  janus-comms-chat .part,
50
217
  janus-comms-chat .disconnect {
51
- color: #999;
218
+ display: var(--comms-show-presence);
219
+ color: var(--comms-muted);
52
220
  font-weight: normal;
53
221
  font-style: italic;
54
222
  font-size: .7em;
@@ -57,80 +225,110 @@ janus-comms-chat .join .userid::before,
57
225
  janus-comms-chat .part .userid::before,
58
226
  janus-comms-chat .disconnect .userid::before {
59
227
  content: "-!- ";
60
- color: #999;
61
- }
62
- janus-comms-chat .join .userid {
63
- color: #9ff;
228
+ color: var(--comms-muted);
64
229
  }
230
+ janus-comms-chat .join .userid { color: #9ff; }
65
231
  janus-comms-chat .disconnect .userid,
66
- janus-comms-chat .part .userid {
67
- color: #099;
68
- }
232
+ janus-comms-chat .part .userid { color: #099; }
69
233
  janus-comms-chat .chat,
70
- janus-comms-chat .selfchat {
71
- color: #aaa;
72
- font-weight: normal;
234
+ janus-comms-chat .selfchat { color: var(--comms-text); font-weight: normal; }
235
+ janus-comms-chat .userid { color: var(--comms-accent); font-weight: 900; }
236
+ janus-comms-chat .selfchat .userid { color: #fff; font-weight: bold; }
237
+ janus-comms-chat .chat .userid::after,
238
+ janus-comms-chat .selfchat .userid::after { content: ':'; color: #666; }
239
+ janus-comms-chat .print .userid { display: none; }
240
+ janus-comms-chat .print { color: white; }
241
+
242
+ /* Log scrollbar */
243
+ janus-comms-chat .comms-log::-webkit-scrollbar,
244
+ .comms-roster-popover::-webkit-scrollbar { width: 6px; }
245
+ janus-comms-chat .comms-log::-webkit-scrollbar-track,
246
+ .comms-roster-popover::-webkit-scrollbar-track { background: rgba(255,255,255,.05); }
247
+ janus-comms-chat .comms-log::-webkit-scrollbar-thumb,
248
+ .comms-roster-popover::-webkit-scrollbar-thumb { background: var(--comms-accent); border-radius: 3px; }
249
+
250
+ /* Narrow screens: keep only the essentials */
251
+ @media (max-width: 48em) {
252
+ janus-comms-chat { flex-basis: 7em; }
253
+ janus-comms-chat .comms-log { width: 80vw; }
73
254
  }
74
- janus-comms-chat .userid {
75
- color: #4cb96f;
76
- font-weight: 900;
255
+
256
+ /*
257
+ * Popupbutton popups (the invite menu, the mic/device picker) are rendered at
258
+ * the document root, so they must float above the comms panel's stacking
259
+ * context (z 100) — otherwise they render *behind* the bar and its transcript.
260
+ */
261
+ ui-window.state_popup {
262
+ z-index: 10000;
77
263
  }
78
- janus-comms-chat .selfchat .userid {
264
+
265
+ /* ---- Invite popup menu (rendered at document root, outside the panel) ---- */
266
+ ui-window.janus-comms-invite-popup {
267
+ background: rgba(15,15,18,.98);
268
+ border: 1px solid rgba(255,255,255,.14);
269
+ border-radius: .5em;
270
+ box-shadow: 0 6px 24px rgba(0,0,0,.6);
79
271
  color: #fff;
80
- font-weight: bold;
81
272
  }
82
- /*
83
- janus-comms-chat .chat .userid::before,
84
- janus-comms-chat .selfchat .userid::before {
85
- content: '<';
86
- color: #666;
87
- }
88
- */
89
- janus-comms-chat .chat .userid::after,
90
- janus-comms-chat .selfchat .userid::after {
91
- content: ':';
92
- color: #666;
273
+ .janus-comms-invite-menu {
274
+ display: flex;
275
+ flex-direction: column;
276
+ gap: .1em;
277
+ min-width: 13em;
278
+ max-width: 90vw;
279
+ max-height: 80vh;
280
+ overflow-x: hidden;
281
+ overflow-y: auto;
282
+ padding: .3em;
283
+ }
284
+ /* Reset the site's uppercase/large/faded button look inside the menu */
285
+ .janus-comms-invite-popup ui-button {
286
+ display: block;
287
+ box-sizing: border-box;
288
+ width: 100%;
289
+ text-align: left;
290
+ text-transform: none;
291
+ font-size: .95em;
292
+ font-weight: normal;
293
+ line-height: 1.25;
294
+ white-space: nowrap;
295
+ color: #fff;
296
+ opacity: 1;
297
+ padding: .5em .6em;
298
+ border: 0;
299
+ border-radius: .4em;
300
+ box-shadow: none;
301
+ background: transparent;
302
+ cursor: pointer;
303
+ transition: background 120ms linear;
93
304
  }
94
- janus-comms-chat .print .userid {
95
- display: none;
305
+ .janus-comms-invite-popup ui-button:hover {
306
+ background: rgba(255,255,255,.12);
96
307
  }
97
- janus-comms-chat .print {
98
- color: white;
308
+ .janus-comms-invite-popup janus-voip-client-callbutton {
309
+ display: block;
99
310
  }
100
- janus-comms-status {
311
+ .janus-comms-invite-popup .janus-comms-schedule {
101
312
  display: flex;
102
- flex-direction: row;
313
+ flex-direction: column;
314
+ gap: .3em;
315
+ padding: .5em .6em .3em;
316
+ margin-top: .15em;
317
+ border-top: 1px solid rgba(255,255,255,.15);
103
318
  }
104
- janus-comms-status {
319
+ .janus-comms-invite-popup .janus-comms-schedule label {
105
320
  font-size: .8em;
321
+ color: #bbb;
106
322
  }
107
- janus-comms-status .indicator {
108
- border: 1px solid black;
109
- display: inline-block;
110
- width: 1em;
111
- height: 1em;
112
- border-radius: .5em;
113
- box-shadow: 0 0 5px black;
114
- background: #900;
115
- margin: .2em .4em .2em .2em;
116
- }
117
- janus-comms-status .indicator.connecting {
118
- background: #990;
119
- }
120
- janus-comms-status .indicator.connected {
121
- background: #090;
122
- }
123
- janus-comms-status .indicator.disconnecting {
124
- background: #999;
125
- }
126
- janus-comms-status .indicator.private {
127
- background: #aaa;
128
- }
129
- ui-list[name="userlist_room"] {
130
- display: grid;
131
- grid-template-columns: repeat(auto-fill, minmax(10em, 1fr));
132
- font-size: .75em;
323
+ .janus-comms-invite-popup .janus-comms-schedule input {
324
+ padding: .35em;
325
+ border-radius: .3em;
326
+ border: 1px solid #555;
327
+ background: #222;
328
+ color: #eee;
329
+ font-size: .9em;
133
330
  }
331
+
134
332
  janus-voip-picker>ul li {
135
333
  display: flex;
136
334
  xflex: 1;
@@ -1,5 +1,6 @@
1
- <janus-comms-status name="status"></janus-comms-status>
2
-
3
- <janus-comms-userlist labelfont="{labelfont}"></janus-comms-userlist>
4
- <!-- janus-voip-client></janus-voip-client -->
5
- <janus-comms-chat name="chat"></janus-comms-chat>
1
+ <div class="janus-comms-bar">
2
+ <janus-comms-status name="status"></janus-comms-status>
3
+ <janus-comms-userlist labelfont="{labelfont}"></janus-comms-userlist>
4
+ <janus-comms-invite></janus-comms-invite>
5
+ <janus-comms-chat name="chat"></janus-comms-chat>
6
+ </div>
@@ -126,13 +126,8 @@ elation.elements.define('janus-comms-userlist', class extends elation.elements.u
126
126
  this.userlist_room = this.userlist_room;
127
127
  this.userlist_online = this.userlist_online;
128
128
  this.userlist_friends = this.userlist_friends;
129
- if (room.private) {
130
- this.parentNode.elements.chat.open = false;
131
- this.elements.userlist_details.open = false;
132
- } else {
133
- this.parentNode.elements.chat.open = true;
134
- this.elements.userlist_details.open = true;
135
- }
129
+ // The roster is a collapsed popover chip in the bar now; leave it closed
130
+ // by default rather than forcing it (and the chat) open on room load.
136
131
  this.updateUsers();
137
132
  elation.events.add(this.room, 'join', ev => {
138
133
  setTimeout(() => {
@@ -204,7 +199,7 @@ elation.elements.define('janus-comms-chat', class extends elation.elements.base
204
199
  player.disable();
205
200
  this.updateUnread(0, true);
206
201
  }
207
- this.elements.chatinput.onblur = (ev) => this.elements.chatinput.placeholder = 'Press T for text chat';
202
+ this.elements.chatinput.onblur = (ev) => this.elements.chatinput.placeholder = 'Press T to chat';
208
203
 
209
204
  window.addEventListener('keydown', (ev) => {
210
205
  // FIXME - this should set up a bindable context in the engine's control system
@@ -218,12 +213,29 @@ elation.elements.define('janus-comms-chat', class extends elation.elements.base
218
213
  });
219
214
  elation.events.add(janus._target, 'clientprint', (ev) => this.handleClientPrint(ev.data));
220
215
  elation.events.add(this.janusweb, 'room_load_start', (ev) => { this.updateRoom(ev.element); });
221
- // FIXME - first element with the current design is a <detail> element, but this is fragile if that changes
222
- this.elements[0].addEventListener('toggle', (ev) => this.elements.chatinput.focus());
223
- this.updateRoom(room);
224
- if (window.innerWidth < 760) {
225
- this.elements.toggle.open = false;
216
+
217
+ // The transcript floats above the bar and fades in on activity. Clicking
218
+ // the unread badge pins it open so it can be read/scrolled at leisure.
219
+ if (this.elements.unread) {
220
+ this.elements.unread.addEventListener('click', (ev) => this.togglePin());
226
221
  }
222
+ this.updateRoom(room);
223
+ }
224
+ flashLog(duration = 6000) {
225
+ // Transient reveal on activity. Fades after `duration` unless the log is
226
+ // pinned or the input is focused (both handled purely in CSS).
227
+ let panel = this.closest('janus-comms-panel');
228
+ if (!panel) return;
229
+ panel.classList.add('comms-log-flash');
230
+ if (this.flashtimer) clearTimeout(this.flashtimer);
231
+ this.flashtimer = setTimeout(() => panel.classList.remove('comms-log-flash'), duration);
232
+ }
233
+ togglePin() {
234
+ // Explicit sticky show/hide — clicking the badge keeps the transcript up
235
+ // until clicked again, independent of the transient flash.
236
+ let panel = this.closest('janus-comms-panel');
237
+ if (!panel) return;
238
+ if (panel.classList.toggle('comms-log-pinned')) this.updateUnread(0, true);
227
239
  }
228
240
  scrollToBottom() {
229
241
  setTimeout(() => {
@@ -279,6 +291,7 @@ if (!this.elements.chatmessages) return;
279
291
 
280
292
  this.scrollToBottom();
281
293
  this.updateUnread(1);
294
+ this.flashLog();
282
295
  }
283
296
  sendMessage(msg) {
284
297
  if (msg && msg.length > 0) {
@@ -293,6 +306,7 @@ if (!this.elements.chatmessages) return;
293
306
  });
294
307
  this.elements.chatinput.value = '';
295
308
  this.scrollToBottom();
309
+ this.flashLog();
296
310
  }
297
311
  }
298
312
  handleClientPrint(msg) {
@@ -316,7 +330,9 @@ if (!this.elements.chatmessages) return;
316
330
  this.elements.unread.count = this.numunread;
317
331
  }
318
332
  updateRoom(newroom) {
319
- this.elements.toggle.open = !newroom.private;
333
+ // Retained for room_load_start wiring. The transcript now lives in a
334
+ // fade-in overlay rather than a <details> toggle, so there is no open
335
+ // state to sync here.
320
336
  }
321
337
  });
322
338
 
@@ -1,22 +1,15 @@
1
1
  {?room.private}
2
- <details name="userlist_details">
3
- <summary>No online users</summary>
4
- <p>This room is marked as private. You are not broadcasting your location while here - others can't see you, and you can't see them.</p>
2
+ <details name="userlist_details" class="comms-roster">
3
+ <summary title="Private room">&#128100; <ui-indicator name="roomusers" value="0"></ui-indicator></summary>
4
+ <div class="comms-roster-popover">
5
+ <p>This room is private. You're not broadcasting your location while here &mdash; others can't see you, and you can't see them.</p>
6
+ </div>
5
7
  </details>
6
8
  {:else}
7
- <details open name="userlist_details">
8
- <summary>Online Users (<ui-indicator name="roomusers" value="0"></ui-indicator>)</summary>
9
- <ui-list name="userlist_room" emptycontent="No other users in this room"></ui-list>
10
- <!-- ui-tabs showcounts="true">
11
- <ui-tab name="roomusers" label="Room">
12
- <ui-list name="userlist_room" emptycontent="No other users in this room"></ui-list>
13
- </ui-tab>
14
- <ui-tab name="globalusers" label="Online">
15
- <ui-list name="userlist_online" emptycontent="No users online"></ui-list>
16
- </ui-tab>
17
- <ui-tab name="friends" label="Friends">
18
- <ui-list name="userlist_friends" emptycontent="No friends online"></ui-list>
19
- </ui-tab>
20
- </ui-tabs -->
9
+ <details name="userlist_details" class="comms-roster">
10
+ <summary title="People in this room">&#128100; <ui-indicator name="roomusers" value="0"></ui-indicator></summary>
11
+ <div class="comms-roster-popover">
12
+ <ui-list name="userlist_room" emptycontent="No other users in this room"></ui-list>
13
+ </div>
21
14
  </details>
22
15
  {/room.private}