Fossil SCM

chat: added toggles for single/multi-line input (non-persistent) and monospace message font (persistent - affects message bodies and text input fields).

stephan 2020-12-25 17:52 trunk
Commit 9d24a2849018c58e735d689121263587f752535a9534eb6fbe12ab98d4ca0da1
3 files changed +4 -2 +75 -14 +14 -6
+4 -2
--- src/chat.c
+++ src/chat.c
@@ -102,12 +102,14 @@
102102
style_header("Chat");
103103
@ <div id='chat-input-area'>
104104
@ <form accept-encoding="utf-8" id="chat-form" autocomplete="off">
105105
@ <div id='chat-input-line'>
106106
@ <input type="text" name="msg" id="chat-input-single" \
107
- @ placeholder="Type message here.">
108
- @ <input type="submit" value="Send">
107
+ @ placeholder="Type message here." autocomplete="off">
108
+ @ <textarea rows="8" id="chat-input-multi" \
109
+ @ placeholder="Type message here" class="hidden"></textarea>
110
+ @ <input type="submit" value="Send" id="chat-message-submit">
109111
@ <span id="chat-settings-button" class="settings-icon"></span>
110112
@ </div>
111113
@ <div id='chat-input-file-area'>
112114
@ <div class='file-selection-wrapper'>
113115
@ <div class='help-buttonlet'>
114116
--- src/chat.c
+++ src/chat.c
@@ -102,12 +102,14 @@
102 style_header("Chat");
103 @ <div id='chat-input-area'>
104 @ <form accept-encoding="utf-8" id="chat-form" autocomplete="off">
105 @ <div id='chat-input-line'>
106 @ <input type="text" name="msg" id="chat-input-single" \
107 @ placeholder="Type message here.">
108 @ <input type="submit" value="Send">
 
 
109 @ <span id="chat-settings-button" class="settings-icon"></span>
110 @ </div>
111 @ <div id='chat-input-file-area'>
112 @ <div class='file-selection-wrapper'>
113 @ <div class='help-buttonlet'>
114
--- src/chat.c
+++ src/chat.c
@@ -102,12 +102,14 @@
102 style_header("Chat");
103 @ <div id='chat-input-area'>
104 @ <form accept-encoding="utf-8" id="chat-form" autocomplete="off">
105 @ <div id='chat-input-line'>
106 @ <input type="text" name="msg" id="chat-input-single" \
107 @ placeholder="Type message here." autocomplete="off">
108 @ <textarea rows="8" id="chat-input-multi" \
109 @ placeholder="Type message here" class="hidden"></textarea>
110 @ <input type="submit" value="Send" id="chat-message-submit">
111 @ <span id="chat-settings-button" class="settings-icon"></span>
112 @ </div>
113 @ <div id='chat-input-file-area'>
114 @ <div class='file-selection-wrapper'>
115 @ <div class='help-buttonlet'>
116
+75 -14
--- src/chat.js
+++ src/chat.js
@@ -16,11 +16,14 @@
1616
pageTitle: E1('head title'),
1717
loadToolbar: undefined /* the load-posts toolbar (dynamically created) */,
1818
inputWrapper: E1("#chat-input-area"),
1919
messagesWrapper: E1('#chat-messages-wrapper'),
2020
inputForm: E1('#chat-form'),
21
+ btnSubmit: E1('#chat-message-submit'),
2122
inputSingle: E1('#chat-input-single'),
23
+ inputMulti: E1('#chat-input-multi'),
24
+ inputCurrent: undefined/*one of inputSingle or inputMulti*/,
2225
inputFile: E1('#chat-input-file')
2326
},
2427
me: F.user.name,
2528
mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50,
2629
mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/,
@@ -38,21 +41,37 @@
3841
ajaxInflight: 0,
3942
/** Gets (no args) or sets (1 arg) the current input text field value,
4043
taking into account single- vs multi-line input. The getter returns
4144
a string and the setter returns this object. */
4245
inputValue: function(){
43
- const e = this.e.inputSingle;
46
+ const e = this.inputElement();
4447
if(arguments.length){
4548
e.value = arguments[0];
4649
return this;
47
- }else {
48
- return e.value;
4950
}
51
+ return e.value;
5052
},
5153
/** Asks the current user input field to take focus. Returns this. */
5254
inputFocus: function(){
53
- this.e.inputSingle.focus();
55
+ this.inputElement().focus();
56
+ return this;
57
+ },
58
+ /** Returns the current message input element. */
59
+ inputElement: function(){
60
+ return this.e.inputCurrent;
61
+ },
62
+ /** Toggles between single- and multi-line edit modes. Returns this. */
63
+ inputToggleSingleMulti: function(){
64
+ const old = this.e.inputCurrent;
65
+ if(this.e.inputCurrent === this.e.inputSingle){
66
+ this.e.inputCurrent = this.e.inputMulti;
67
+ }else{
68
+ this.e.inputCurrent = this.e.inputSingle;
69
+ }
70
+ D.addClass(old, 'hidden');
71
+ D.removeClass(this.e.inputCurrent, 'hidden');
72
+ this.e.inputCurrent.value = old.value;
5473
return this;
5574
},
5675
/** Enables (if yes is truthy) or disables all elements in
5776
* this.disableDuringAjax. */
5877
enableAjaxComponents: function(yes){
@@ -110,18 +129,23 @@
110129
settings:{
111130
get: (k,dflt)=>F.storage.get(k,dflt),
112131
getBool: (k,dflt)=>F.storage.getBool(k,dflt),
113132
set: (k,v)=>F.storage.set(k,v),
114133
defaults:{
115
- "images-inline": !!F.config.chat.imagesInline
134
+ "images-inline": !!F.config.chat.imagesInline,
135
+ "monospace-messages": false
116136
}
117137
}
118138
};
119139
Object.keys(cs.settings.defaults).forEach(function f(k){
120140
const v = cs.settings.get(k,f);
121141
if(f===v) cs.settings.set(k,cs.settings.defaults[k]);
122142
});
143
+ if(cs.settings.getBool('monospace-messages',false)){
144
+ document.body.classList.add('monospace-messages');
145
+ }
146
+ cs.e.inputCurrent = cs.e.inputSingle;
123147
cs.pageTitleOrig = cs.e.pageTitle.innerText;
124148
const qs = (e)=>document.querySelector(e);
125149
const argsToArray = function(args){
126150
return Array.prototype.slice.call(args,0);
127151
};
@@ -280,26 +304,49 @@
280304
(k)=>Chat.e.inputFile.addEventListener(k, dropEvents[k], true)
281305
);
282306
return bxs;
283307
})()/*drag/drop*/;
284308
285
- Chat.e.inputForm.addEventListener('submit',(e)=>{
286
- e.preventDefault();
287
- const fd = new FormData(Chat.e.inputForm);
288
- if(BlobXferState.blob/*replace file content with this*/){
289
- fd.set("file", BlobXferState.blob);
290
- }
291
- if( !!Chat.inputValue()
292
- || Chat.e.inputFile.value.length>0
293
- || BlobXferState.blob ){
309
+ Chat.submitMessage = function(){
310
+ const fd = new FormData(this.e.inputForm)
311
+ /* ^^^^ we don't really want/need the FORM element, but when
312
+ FormData() is default-constructed here then the server
313
+ segfaults, and i have no clue why! */;
314
+ const msg = this.inputValue();
315
+ if(msg) fd.set('msg',msg);
316
+ const file = BlobXferState.blob || this.e.inputFile.files[0];
317
+ if(file) fd.set("file", file);
318
+ if( msg || file ){
294319
fetch("chat-send",{
295320
method: 'POST',
296321
body: fd
297322
});
298323
}
299324
BlobXferState.clear();
300325
Chat.inputValue("").inputFocus();
326
+ };
327
+
328
+ Chat.e.inputSingle.addEventListener('keydown',function(ev){
329
+ if(13===ev.keyCode/*ENTER*/){
330
+ ev.preventDefault();
331
+ ev.stopPropagation();
332
+ Chat.submitMessage();
333
+ return false;
334
+ }
335
+ }, false);
336
+ Chat.e.inputMulti.addEventListener('keydown',function(ev){
337
+ if(ev.ctrlKey && 13 === ev.keyCode){
338
+ ev.preventDefault();
339
+ ev.stopPropagation();
340
+ Chat.submitMessage();
341
+ return false;
342
+ }
343
+ }, false);
344
+ Chat.e.btnSubmit.addEventListener('click',(e)=>{
345
+ e.preventDefault();
346
+ Chat.submitMessage();
347
+ return false;
301348
});
302349
303350
/* Returns a new TEXT node with the given text content. */
304351
/** Returns the local time string of Date object d, defaulting
305352
to the current time. */
@@ -391,10 +438,24 @@
391438
return rect.top + rect.height + 2;
392439
}
393440
});
394441
/* Settings menu entries... */
395442
const settingsOps = [{
443
+ label: "Multi-line input",
444
+ boolValue: ()=>Chat.inputElement()===Chat.e.inputMulti,
445
+ callback: function(){
446
+ Chat.inputToggleSingleMulti();
447
+ }
448
+ },{
449
+ label: "Monospace message font",
450
+ boolValue: ()=>document.body.classList.contains('monospace-messages'),
451
+ callback: function(){
452
+ document.body.classList.toggle('monospace-messages');
453
+ Chat.settings.set('monospace-messages',
454
+ document.body.classList.contains('monospace-messages'));
455
+ }
456
+ },{
396457
label: "Chat-only mode",
397458
boolValue: ()=>!!document.body.classList.contains('chat-only-mode'),
398459
callback: function f(){
399460
if(undefined === f.isHidden){
400461
f.isHidden = false;
401462
--- src/chat.js
+++ src/chat.js
@@ -16,11 +16,14 @@
16 pageTitle: E1('head title'),
17 loadToolbar: undefined /* the load-posts toolbar (dynamically created) */,
18 inputWrapper: E1("#chat-input-area"),
19 messagesWrapper: E1('#chat-messages-wrapper'),
20 inputForm: E1('#chat-form'),
 
21 inputSingle: E1('#chat-input-single'),
 
 
22 inputFile: E1('#chat-input-file')
23 },
24 me: F.user.name,
25 mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50,
26 mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/,
@@ -38,21 +41,37 @@
38 ajaxInflight: 0,
39 /** Gets (no args) or sets (1 arg) the current input text field value,
40 taking into account single- vs multi-line input. The getter returns
41 a string and the setter returns this object. */
42 inputValue: function(){
43 const e = this.e.inputSingle;
44 if(arguments.length){
45 e.value = arguments[0];
46 return this;
47 }else {
48 return e.value;
49 }
 
50 },
51 /** Asks the current user input field to take focus. Returns this. */
52 inputFocus: function(){
53 this.e.inputSingle.focus();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54 return this;
55 },
56 /** Enables (if yes is truthy) or disables all elements in
57 * this.disableDuringAjax. */
58 enableAjaxComponents: function(yes){
@@ -110,18 +129,23 @@
110 settings:{
111 get: (k,dflt)=>F.storage.get(k,dflt),
112 getBool: (k,dflt)=>F.storage.getBool(k,dflt),
113 set: (k,v)=>F.storage.set(k,v),
114 defaults:{
115 "images-inline": !!F.config.chat.imagesInline
 
116 }
117 }
118 };
119 Object.keys(cs.settings.defaults).forEach(function f(k){
120 const v = cs.settings.get(k,f);
121 if(f===v) cs.settings.set(k,cs.settings.defaults[k]);
122 });
 
 
 
 
123 cs.pageTitleOrig = cs.e.pageTitle.innerText;
124 const qs = (e)=>document.querySelector(e);
125 const argsToArray = function(args){
126 return Array.prototype.slice.call(args,0);
127 };
@@ -280,26 +304,49 @@
280 (k)=>Chat.e.inputFile.addEventListener(k, dropEvents[k], true)
281 );
282 return bxs;
283 })()/*drag/drop*/;
284
285 Chat.e.inputForm.addEventListener('submit',(e)=>{
286 e.preventDefault();
287 const fd = new FormData(Chat.e.inputForm);
288 if(BlobXferState.blob/*replace file content with this*/){
289 fd.set("file", BlobXferState.blob);
290 }
291 if( !!Chat.inputValue()
292 || Chat.e.inputFile.value.length>0
293 || BlobXferState.blob ){
 
294 fetch("chat-send",{
295 method: 'POST',
296 body: fd
297 });
298 }
299 BlobXferState.clear();
300 Chat.inputValue("").inputFocus();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301 });
302
303 /* Returns a new TEXT node with the given text content. */
304 /** Returns the local time string of Date object d, defaulting
305 to the current time. */
@@ -391,10 +438,24 @@
391 return rect.top + rect.height + 2;
392 }
393 });
394 /* Settings menu entries... */
395 const settingsOps = [{
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396 label: "Chat-only mode",
397 boolValue: ()=>!!document.body.classList.contains('chat-only-mode'),
398 callback: function f(){
399 if(undefined === f.isHidden){
400 f.isHidden = false;
401
--- src/chat.js
+++ src/chat.js
@@ -16,11 +16,14 @@
16 pageTitle: E1('head title'),
17 loadToolbar: undefined /* the load-posts toolbar (dynamically created) */,
18 inputWrapper: E1("#chat-input-area"),
19 messagesWrapper: E1('#chat-messages-wrapper'),
20 inputForm: E1('#chat-form'),
21 btnSubmit: E1('#chat-message-submit'),
22 inputSingle: E1('#chat-input-single'),
23 inputMulti: E1('#chat-input-multi'),
24 inputCurrent: undefined/*one of inputSingle or inputMulti*/,
25 inputFile: E1('#chat-input-file')
26 },
27 me: F.user.name,
28 mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50,
29 mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/,
@@ -38,21 +41,37 @@
41 ajaxInflight: 0,
42 /** Gets (no args) or sets (1 arg) the current input text field value,
43 taking into account single- vs multi-line input. The getter returns
44 a string and the setter returns this object. */
45 inputValue: function(){
46 const e = this.inputElement();
47 if(arguments.length){
48 e.value = arguments[0];
49 return this;
 
 
50 }
51 return e.value;
52 },
53 /** Asks the current user input field to take focus. Returns this. */
54 inputFocus: function(){
55 this.inputElement().focus();
56 return this;
57 },
58 /** Returns the current message input element. */
59 inputElement: function(){
60 return this.e.inputCurrent;
61 },
62 /** Toggles between single- and multi-line edit modes. Returns this. */
63 inputToggleSingleMulti: function(){
64 const old = this.e.inputCurrent;
65 if(this.e.inputCurrent === this.e.inputSingle){
66 this.e.inputCurrent = this.e.inputMulti;
67 }else{
68 this.e.inputCurrent = this.e.inputSingle;
69 }
70 D.addClass(old, 'hidden');
71 D.removeClass(this.e.inputCurrent, 'hidden');
72 this.e.inputCurrent.value = old.value;
73 return this;
74 },
75 /** Enables (if yes is truthy) or disables all elements in
76 * this.disableDuringAjax. */
77 enableAjaxComponents: function(yes){
@@ -110,18 +129,23 @@
129 settings:{
130 get: (k,dflt)=>F.storage.get(k,dflt),
131 getBool: (k,dflt)=>F.storage.getBool(k,dflt),
132 set: (k,v)=>F.storage.set(k,v),
133 defaults:{
134 "images-inline": !!F.config.chat.imagesInline,
135 "monospace-messages": false
136 }
137 }
138 };
139 Object.keys(cs.settings.defaults).forEach(function f(k){
140 const v = cs.settings.get(k,f);
141 if(f===v) cs.settings.set(k,cs.settings.defaults[k]);
142 });
143 if(cs.settings.getBool('monospace-messages',false)){
144 document.body.classList.add('monospace-messages');
145 }
146 cs.e.inputCurrent = cs.e.inputSingle;
147 cs.pageTitleOrig = cs.e.pageTitle.innerText;
148 const qs = (e)=>document.querySelector(e);
149 const argsToArray = function(args){
150 return Array.prototype.slice.call(args,0);
151 };
@@ -280,26 +304,49 @@
304 (k)=>Chat.e.inputFile.addEventListener(k, dropEvents[k], true)
305 );
306 return bxs;
307 })()/*drag/drop*/;
308
309 Chat.submitMessage = function(){
310 const fd = new FormData(this.e.inputForm)
311 /* ^^^^ we don't really want/need the FORM element, but when
312 FormData() is default-constructed here then the server
313 segfaults, and i have no clue why! */;
314 const msg = this.inputValue();
315 if(msg) fd.set('msg',msg);
316 const file = BlobXferState.blob || this.e.inputFile.files[0];
317 if(file) fd.set("file", file);
318 if( msg || file ){
319 fetch("chat-send",{
320 method: 'POST',
321 body: fd
322 });
323 }
324 BlobXferState.clear();
325 Chat.inputValue("").inputFocus();
326 };
327
328 Chat.e.inputSingle.addEventListener('keydown',function(ev){
329 if(13===ev.keyCode/*ENTER*/){
330 ev.preventDefault();
331 ev.stopPropagation();
332 Chat.submitMessage();
333 return false;
334 }
335 }, false);
336 Chat.e.inputMulti.addEventListener('keydown',function(ev){
337 if(ev.ctrlKey && 13 === ev.keyCode){
338 ev.preventDefault();
339 ev.stopPropagation();
340 Chat.submitMessage();
341 return false;
342 }
343 }, false);
344 Chat.e.btnSubmit.addEventListener('click',(e)=>{
345 e.preventDefault();
346 Chat.submitMessage();
347 return false;
348 });
349
350 /* Returns a new TEXT node with the given text content. */
351 /** Returns the local time string of Date object d, defaulting
352 to the current time. */
@@ -391,10 +438,24 @@
438 return rect.top + rect.height + 2;
439 }
440 });
441 /* Settings menu entries... */
442 const settingsOps = [{
443 label: "Multi-line input",
444 boolValue: ()=>Chat.inputElement()===Chat.e.inputMulti,
445 callback: function(){
446 Chat.inputToggleSingleMulti();
447 }
448 },{
449 label: "Monospace message font",
450 boolValue: ()=>document.body.classList.contains('monospace-messages'),
451 callback: function(){
452 document.body.classList.toggle('monospace-messages');
453 Chat.settings.set('monospace-messages',
454 document.body.classList.contains('monospace-messages'));
455 }
456 },{
457 label: "Chat-only mode",
458 boolValue: ()=>!!document.body.classList.contains('chat-only-mode'),
459 callback: function f(){
460 if(undefined === f.isHidden){
461 f.isHidden = false;
462
+14 -6
--- src/default.css
+++ src/default.css
@@ -1490,22 +1490,28 @@
14901490
body.chat .message-content {
14911491
display: inline-block;
14921492
border-radius: 0.25em;
14931493
border: 1px solid rgba(0,0,0,0.2);
14941494
box-shadow: 0.2em 0.2em 0.2em rgba(0, 0, 0, 0.29);
1495
- padding: 0.25em 1em;
1496
- margin-top: -0.75em;
1495
+ padding: 0.25em 0.5em;
1496
+ margin-top: -0.75em/*slide it up to the base of the LEGEND*/;
14971497
min-width: 9em /*avoid unsightly "underlap" with the user name label*/;
1498
+ white-space: pre-wrap/*needed for multi-line edits*/;
1499
+}
1500
+body.chat.monospace-messages .message-content,
1501
+body.chat.monospace-messages textarea,
1502
+body.chat.monospace-messages input[type=text]{
1503
+ font-family: monospace;
14981504
}
1505
+
14991506
/* User name for the post (a LEGEND element) */
15001507
body.chat .message-row .message-user {
15011508
border-radius: 0.25em 0.25em 0 0;
15021509
padding: 0 0.5em;
15031510
/*text-align: left; Firefox requires the 'align' attribute */
1504
- margin: 0 0.15em;
1511
+ margin: 0 0.25em 0.4em 0.15em;
15051512
padding: 0 0.5em 0em 0.5em;
1506
- margin-bottom: 0.4em;
15071513
cursor: pointer;
15081514
}
15091515
15101516
body.chat .fossil-tooltip.help-buttonlet-content {
15111517
font-size: 80%;
@@ -1631,10 +1637,11 @@
16311637
scrolling, but doing so causes #chat-messages-wrapper to scroll
16321638
behind it visibly, which is really ugly. Only current workaround is
16331639
to force an opaque background color on this element, but that's not
16341640
skin-friendly. */
16351641
position: sticky;
1642
+ position: -webkit-sticky /* supposedly some versions of Safari */;
16361643
top: 0;
16371644
padding: 0.5em 1em;
16381645
z-index: 100
16391646
/* see notes in #chat-messages-wrapper. The various popups require a
16401647
z-index higher than this one. */
@@ -1655,18 +1662,19 @@
16551662
}
16561663
body.chat #chat-input-line {
16571664
display: flex;
16581665
flex-direction: row;
16591666
margin-bottom: 0.25em;
1660
- align-items: center;
1667
+ align-items: flex-start;
16611668
}
16621669
body.chat #chat-input-line > input[type=submit] {
16631670
flex: 1 5 auto;
16641671
max-width: 6em;
16651672
margin: 0 1em;
16661673
}
1667
-body.chat #chat-input-line > input[type=text] {
1674
+body.chat #chat-input-line > input[type=text],
1675
+body.chat #chat-input-line > textarea {
16681676
flex: 5 1 auto;
16691677
}
16701678
body.chat #chat-input-file-area {
16711679
display: flex;
16721680
flex-direction: row;
16731681
--- src/default.css
+++ src/default.css
@@ -1490,22 +1490,28 @@
1490 body.chat .message-content {
1491 display: inline-block;
1492 border-radius: 0.25em;
1493 border: 1px solid rgba(0,0,0,0.2);
1494 box-shadow: 0.2em 0.2em 0.2em rgba(0, 0, 0, 0.29);
1495 padding: 0.25em 1em;
1496 margin-top: -0.75em;
1497 min-width: 9em /*avoid unsightly "underlap" with the user name label*/;
 
 
 
 
 
 
1498 }
 
1499 /* User name for the post (a LEGEND element) */
1500 body.chat .message-row .message-user {
1501 border-radius: 0.25em 0.25em 0 0;
1502 padding: 0 0.5em;
1503 /*text-align: left; Firefox requires the 'align' attribute */
1504 margin: 0 0.15em;
1505 padding: 0 0.5em 0em 0.5em;
1506 margin-bottom: 0.4em;
1507 cursor: pointer;
1508 }
1509
1510 body.chat .fossil-tooltip.help-buttonlet-content {
1511 font-size: 80%;
@@ -1631,10 +1637,11 @@
1631 scrolling, but doing so causes #chat-messages-wrapper to scroll
1632 behind it visibly, which is really ugly. Only current workaround is
1633 to force an opaque background color on this element, but that's not
1634 skin-friendly. */
1635 position: sticky;
 
1636 top: 0;
1637 padding: 0.5em 1em;
1638 z-index: 100
1639 /* see notes in #chat-messages-wrapper. The various popups require a
1640 z-index higher than this one. */
@@ -1655,18 +1662,19 @@
1655 }
1656 body.chat #chat-input-line {
1657 display: flex;
1658 flex-direction: row;
1659 margin-bottom: 0.25em;
1660 align-items: center;
1661 }
1662 body.chat #chat-input-line > input[type=submit] {
1663 flex: 1 5 auto;
1664 max-width: 6em;
1665 margin: 0 1em;
1666 }
1667 body.chat #chat-input-line > input[type=text] {
 
1668 flex: 5 1 auto;
1669 }
1670 body.chat #chat-input-file-area {
1671 display: flex;
1672 flex-direction: row;
1673
--- src/default.css
+++ src/default.css
@@ -1490,22 +1490,28 @@
1490 body.chat .message-content {
1491 display: inline-block;
1492 border-radius: 0.25em;
1493 border: 1px solid rgba(0,0,0,0.2);
1494 box-shadow: 0.2em 0.2em 0.2em rgba(0, 0, 0, 0.29);
1495 padding: 0.25em 0.5em;
1496 margin-top: -0.75em/*slide it up to the base of the LEGEND*/;
1497 min-width: 9em /*avoid unsightly "underlap" with the user name label*/;
1498 white-space: pre-wrap/*needed for multi-line edits*/;
1499 }
1500 body.chat.monospace-messages .message-content,
1501 body.chat.monospace-messages textarea,
1502 body.chat.monospace-messages input[type=text]{
1503 font-family: monospace;
1504 }
1505
1506 /* User name for the post (a LEGEND element) */
1507 body.chat .message-row .message-user {
1508 border-radius: 0.25em 0.25em 0 0;
1509 padding: 0 0.5em;
1510 /*text-align: left; Firefox requires the 'align' attribute */
1511 margin: 0 0.25em 0.4em 0.15em;
1512 padding: 0 0.5em 0em 0.5em;
 
1513 cursor: pointer;
1514 }
1515
1516 body.chat .fossil-tooltip.help-buttonlet-content {
1517 font-size: 80%;
@@ -1631,10 +1637,11 @@
1637 scrolling, but doing so causes #chat-messages-wrapper to scroll
1638 behind it visibly, which is really ugly. Only current workaround is
1639 to force an opaque background color on this element, but that's not
1640 skin-friendly. */
1641 position: sticky;
1642 position: -webkit-sticky /* supposedly some versions of Safari */;
1643 top: 0;
1644 padding: 0.5em 1em;
1645 z-index: 100
1646 /* see notes in #chat-messages-wrapper. The various popups require a
1647 z-index higher than this one. */
@@ -1655,18 +1662,19 @@
1662 }
1663 body.chat #chat-input-line {
1664 display: flex;
1665 flex-direction: row;
1666 margin-bottom: 0.25em;
1667 align-items: flex-start;
1668 }
1669 body.chat #chat-input-line > input[type=submit] {
1670 flex: 1 5 auto;
1671 max-width: 6em;
1672 margin: 0 1em;
1673 }
1674 body.chat #chat-input-line > input[type=text],
1675 body.chat #chat-input-line > textarea {
1676 flex: 5 1 auto;
1677 }
1678 body.chat #chat-input-file-area {
1679 display: flex;
1680 flex-direction: row;
1681

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button