@@ -255,6 +255,69 @@ <h3>Volume Controls</h3>
255255 roomZones : { } ,
256256 } ;
257257 let GMCPUpdateHandlers = {
258+ "Comm" : function ( ) {
259+
260+ var obj = GMCPStructs [ "Comm" ] ;
261+ if ( ! obj . Channel ) {
262+ return ;
263+ }
264+
265+ if ( ! GMCPWindows [ 'Comm' ] && GMCPWindows [ 'Comm' ] !== false ) {
266+
267+ GMCPWindows [ 'Comm' ] = new WinBox ( { title : "Communications" ,
268+ mount : document . getElementById ( "comm-output" ) ,
269+ background : "#1c6b60" , border : 1 ,
270+ x : "right" , y : 450 ,
271+ width :363 , height :20 + 290 ,
272+ header : 20 ,
273+ bottom : 60 , // Limit how far down it can go. This affects docking the minimized window.
274+ // end limit position range
275+ onclose : force => { GMCPWindows [ 'Comm' ] = false ; return false ; } ,
276+ } ) ;
277+
278+ GMCPWindows [ 'Comm' ] . Message = function ( channelName , fromName , fromSource , message ) {
279+
280+ // add new text output
281+ var tab = document . getElementById ( "comm-tab-" + channelName ) ;
282+ var panel = document . getElementById ( "comm-" + channelName ) ;
283+
284+ if ( tab . classList . contains ( 'active' ) ) {
285+ tab . dataset . unread = 0 ;
286+ tab . innerText = tab . dataset . label ;
287+ } else {
288+ tab . dataset . unread = parseInt ( tab . dataset . unread ) + 1 ;
289+ tab . innerText = tab . dataset . label + "(" + String ( tab . dataset . unread ) + ")" ;
290+ }
291+
292+ var p = document . createElement ( 'p' ) ;
293+ p . innerHTML = '<span class="text-name ' + fromSource + '">' +
294+ fromName +
295+ '</span>: ' +
296+ '<span class="text-body ' + fromSource + '">' +
297+ message +
298+ '</span>' ;
299+ panel . appendChild ( p ) ;
300+
301+ // clean up overflow
302+ panelContainer = GMCPWindows [ 'Comm' ] . window ;
303+ while ( panel . scrollHeight > panelContainer . clientHeight - 58 ) {
304+ if ( panel . childElementCount < 1 ) {
305+ break ;
306+ }
307+ panel . removeChild ( panel . firstElementChild ) ;
308+ }
309+
310+ } ;
311+
312+ }
313+
314+ if ( GMCPWindows [ 'Comm' ] === false ) {
315+ return ;
316+ }
317+
318+ GMCPWindows [ 'Comm' ] . Message ( obj . Channel . channel , obj . Channel . sender , obj . Channel . source , obj . Channel . text ) ;
319+
320+ } ,
258321 "Room" : function ( ) {
259322
260323 var obj = GMCPStructs [ "Room" ] ;
@@ -269,10 +332,12 @@ <h3>Volume Controls</h3>
269332 background : "#1c6b60" ,
270333 border : 1 ,
271334 x : "right" ,
272- y : 0 ,
273- width :300 ,
274- height :20 + 300 ,
335+ y : 66 ,
336+ width :363 ,
337+ height :20 + 363 ,
275338 header : 20 ,
339+ bottom : 60 , // Limit how far down it can go. This affects docking the minimized window.
340+ // end limit position range
276341 onclose : force => { GMCPWindows [ 'Map' ] = false ; return false ; } ,
277342 oncreate : ops => {
278343
@@ -391,17 +456,13 @@ <h3>Volume Controls</h3>
391456 mount : document . getElementById ( "vitals-bars" ) ,
392457 background : "#1c6b60" ,
393458 border : 1 ,
394- x : "right" ,
395- y : 320 ,
459+ x : window . innerWidth - 300 - 63 , // "right",
460+ y : 0 ,
396461 width :300 ,
397- height :20 + 120 ,
462+ height :20 + 40 ,
398463 header : 20 ,
464+ bottom : 60 , // Limit how far down it can go. This affects docking the minimized window.
399465 onclose : force => { GMCPWindows [ 'Char.Vitals' ] = false ; return false ; } ,
400- oncreate : opts => {
401- opts . width = document . getElementById ( 'vitals-bars' ) . style . width ;
402- opts . height = "48px" ;
403- }
404-
405466 } ) ;
406467 }
407468
@@ -983,86 +1044,181 @@ <h3>Volume Controls</h3>
9831044 /* add these styles once (e.g. in your global <style> block) */
9841045
9851046 # health-bar {
986- position : relative;
987- width : 100% ; /* or fixed px width */
988- height : 50% ;
989- background : linear-gradient (
990- to right,
991- # f44336 0% , /* red */
992- # ffeb3b 50% , /* yellow */
993- # 4caf50 100% /* green */
994- );
995- border-radius : 12px ;
996- box-shadow : inset 0 2px 4px rgba (0 , 0 , 0 , 0.6 );
997- overflow : hidden;
998- margin : 0 2px ;
1047+ position : relative;
1048+ width : 100% ; /* or fixed px width */
1049+ height : 50% ;
1050+ background : linear-gradient (
1051+ to right,
1052+ # f44336 0% , /* red */
1053+ # ffeb3b 50% , /* yellow */
1054+ # 4caf50 100% /* green */
1055+ );
1056+ border-radius : 12px ;
1057+ box-shadow : inset 0 2px 4px rgba (0 , 0 , 0 , 0.6 );
1058+ overflow : hidden;
1059+ margin : 0 2px ;
9991060 }
10001061
10011062 # health-bar .health-fill {
1002- height : 100% ;
1003- width : 0% ;
1004- background : # 333 ;
1005- float : right;
1006- transition : width 0.4s ease-out;
1063+ height : 100% ;
1064+ width : 0% ;
1065+ background : # 333 ;
1066+ float : right;
1067+ transition : width 0.4s ease-out;
10071068 }
10081069
10091070 # health-bar .health-text {
1010- position : absolute;
1011- top : 0 ; left : 0 ; right : 0 ; bottom : 0 ;
1012- display : flex;
1013- align-items : center;
1014- justify-content : center;
1015- font-family : monospace;
1016- font-size : 0.85em ;
1017- color : white;
1018- text-shadow : 0 1px 2px rgba (0 , 0 , 0 , 0.8 );
1019- pointer-events : none;
1071+ position : absolute;
1072+ top : 0 ; left : 0 ; right : 0 ; bottom : 0 ;
1073+ display : flex;
1074+ align-items : center;
1075+ justify-content : center;
1076+ font-family : monospace;
1077+ font-size : 0.85em ;
1078+ color : white;
1079+ text-shadow : 0 1px 2px rgba (0 , 0 , 0 , 0.8 );
1080+ pointer-events : none;
10201081 }
10211082
10221083
10231084 # mana-bar {
1024- position : relative;
1025- width : 100% ; /* or fixed px width */
1026- height : 50% ;
1027- background : linear-gradient (
1028- to right,
1029- # 1e108b 0% ,
1030- # 3a20fe 100%
1031- );
1032- border-radius : 12px ;
1033- box-shadow : inset 0 2px 4px rgba (0 , 0 , 0 , 0.6 );
1034- overflow : hidden;
1035- margin : 0 ;
1085+ position : relative;
1086+ width : 100% ; /* or fixed px width */
1087+ height : 50% ;
1088+ background : linear-gradient (
1089+ to right,
1090+ # 1e108b 0% ,
1091+ # 3a20fe 100%
1092+ );
1093+ border-radius : 12px ;
1094+ box-shadow : inset 0 2px 4px rgba (0 , 0 , 0 , 0.6 );
1095+ overflow : hidden;
1096+ margin : 0 ;
10361097 }
10371098
10381099 # mana-bar .mana-fill {
1039- height : 100% ;
1040- width : 0% ;
1041- background : # 333 ;
1042- float : right;
1043- transition : width 0.4s ease-out;
1100+ height : 100% ;
1101+ width : 0% ;
1102+ background : # 333 ;
1103+ float : right;
1104+ transition : width 0.4s ease-out;
10441105 }
10451106
10461107 # mana-bar .mana-text {
1047- position : absolute;
1048- top : 0 ; left : 0 ; right : 0 ; bottom : 0 ;
1049- display : flex;
1050- align-items : center;
1051- justify-content : center;
1052- font-family : monospace;
1053- font-size : 0.85em ;
1054- color : white;
1055- text-shadow : 0 1px 2px rgba (0 , 0 , 0 , 0.8 );
1056- pointer-events : none;
1108+ position : absolute;
1109+ top : 0 ; left : 0 ; right : 0 ; bottom : 0 ;
1110+ display : flex;
1111+ align-items : center;
1112+ justify-content : center;
1113+ font-family : monospace;
1114+ font-size : 0.85em ;
1115+ color : white;
1116+ text-shadow : 0 1px 2px rgba (0 , 0 , 0 , 0.8 );
1117+ pointer-events : none;
10571118 }
10581119 # vitals-bars {
10591120 height : 100% ;
10601121 }
1061- .wb-body { background : # 000 ; }
1122+ .wb-body { background : # 000 ; overflow : hidden; }
10621123 .wb-max { display : none; }
10631124 .wb-full { display : none; }
1125+
1126+ .tabs {
1127+ width : 100% ;
1128+ display : flex;
1129+ flex-direction : column;
1130+ height : 100% ; /* if you want full‐height panels */
1131+ }
1132+
1133+ .tab-buttons {
1134+ display : flex;
1135+ border-bottom : 1px solid # 0f3333 ;
1136+ }
1137+
1138+ .tab-button {
1139+ flex : 1 ;
1140+ padding : 0.75em ;
1141+ background : # 279888 ;
1142+ border : none;
1143+ cursor : pointer;
1144+ font : inherit;
1145+ font-size : small;
1146+ transition : background 0.2s ;
1147+ }
1148+
1149+ .tab-button : not (.active ): hover {
1150+ background : # 0f3333 ;
1151+ color : # dffbd1 ;
1152+ }
1153+
1154+ .tab-button .active {
1155+ background : # dffbd1 ;
1156+ border-bottom : 2px solid # 0f3333 ;
1157+ }
1158+
1159+ .tab-contents {
1160+ flex : 1 ;
1161+ padding : 0.25em ;
1162+ background : # 000 ;
1163+ }
1164+
1165+ .tab-content {
1166+ display : none;
1167+ height : 100% ;
1168+ }
1169+
1170+ .tab-content .active {
1171+ display : block;
1172+ }
1173+
1174+ .text-name .mob {
1175+ color : # 00ffff ;
1176+ }
1177+ .text-name .player {
1178+ color : # fce94f ;
1179+ }
1180+ .chat-window {
1181+ overflow : scroll;
1182+ background-color : # 000 ;
1183+ color : # fff ;
1184+ }
1185+ .chat-window .broadcast {
1186+ color : # d700d7 ;
1187+ }
1188+ .chat-window .whisper {
1189+ color : # 737670 ;
1190+ }
10641191 </ style >
1192+
1193+
1194+
1195+ < script >
1196+ // on DOM ready
1197+ document . addEventListener ( 'DOMContentLoaded' , ( ) => {
1198+ const buttons = document . querySelectorAll ( '.tab-button' ) ;
1199+ const panels = document . querySelectorAll ( '.tab-content' ) ;
1200+
1201+ buttons . forEach ( btn => {
1202+ btn . addEventListener ( 'click' , ( ) => {
1203+ const target = btn . dataset . tab ;
1204+
1205+ // deactivate all
1206+ buttons . forEach ( b => b . classList . remove ( 'active' ) ) ;
1207+ panels . forEach ( p => p . classList . remove ( 'active' ) ) ;
1208+
1209+ // activate the clicked one and its panel
1210+ btn . classList . add ( 'active' ) ;
1211+ btn . dataset . unread = 0 ;
1212+ btn . innerText = btn . dataset . label ;
1213+ document . getElementById ( target ) . classList . add ( 'active' ) ;
1214+ } ) ;
1215+ } ) ;
1216+ } ) ;
1217+ </ script >
1218+
10651219 < div style ="display:none; ">
1220+
1221+ <!-- VITALS window -->
10661222 < div id ="vitals-bars ">
10671223 < div id ="health-bar ">
10681224 < div class ="health-fill " style ="width: 100%; "> </ div >
@@ -1074,7 +1230,26 @@ <h3>Volume Controls</h3>
10741230 </ div >
10751231 </ div >
10761232
1233+ <!-- MAP window -->
10771234 < div id ="map-render " style ="width:100%; height:100%; "> </ div >
1235+
1236+ <!-- COMM window -->
1237+ < div id ="comm-output " class ="tabs ">
1238+ < div class ="tab-buttons ">
1239+ < button id ="comm-tab-say " class ="tab-button active " data-tab ="comm-say " data-label ="Say " data-unread ="0 "> Say</ button >
1240+ < button id ="comm-tab-whisper " class ="tab-button " data-tab ="comm-whisper " data-label ="Whisper " data-unread ="0 "> Whisper</ button >
1241+ < button id ="comm-tab-party " class ="tab-button " data-tab ="comm-party " data-label ="Party " data-unread ="0 "> Party</ button >
1242+ < button id ="comm-tab-broadcast " class ="tab-button " data-tab ="comm-broadcast " data-label ="Broadcasts " data-unread ="0 "> Broadcasts</ button >
1243+ </ div >
1244+ < div class ="tab-contents ">
1245+ < div id ="comm-say " class ="chat-window say tab-content active "> </ div >
1246+ < div id ="comm-whisper " class ="chat-window whisper tab-content "> </ div >
1247+ < div id ="comm-party " class ="chat-window party tab-content "> </ div >
1248+ < div id ="comm-broadcast " class ="chat-window broadcast tab-content "> </ div >
1249+ </ div >
1250+ </ div >
1251+
10781252 </ div >
1253+
10791254</ body >
10801255</ html >
0 commit comments