Fossil SCM

Forcibly disable drop support in the new editor widget, as the browser will otherwise allow the user to drop images to it, which is confusing and does not work with our ability to upload images. Found a way to implement placeholder text in the contenteditable field.

stephan 2021-09-30 19:32 chat-input-rework
Commit 86d6be3fe212ad50b0d51124712ac8a614cb8b849e2b5f7fcf32ee375118981f
+3 -4
--- src/chat.c
+++ src/chat.c
@@ -142,12 +142,10 @@
142142
** send new chat message, delete older messages, or poll for changes.
143143
*/
144144
void chat_webpage(void){
145145
char *zAlert;
146146
char *zProjectName;
147
- const char * zInputPlaceholder2 = /* Placeholder for textarea input*/
148
- "Ctrl-Enter sends and Shift-Enter previews.";
149147
char * zInputPlaceholder0; /* Common text input placeholder value */
150148
login_check_credentials();
151149
if( !g.perm.Chat ){
152150
login_needed(g.anon.Chat);
153151
return;
@@ -160,12 +158,13 @@
160158
style_set_current_feature("chat");
161159
style_header("Chat");
162160
@ <div id='chat-input-area'>
163161
@ <div id='chat-input-line' class='single-line'>
164162
@ <div contenteditable id="chat-input-field" \
165
- @ placeholder="%h(zInputPlaceholder0) %h(zInputPlaceholder2)" \
166
- @ class></div>
163
+ @ data-placeholder0="%h(zInputPlaceholder0)" \
164
+ @ data-placeholder="%h(zInputPlaceholder0)" \
165
+ @ class=""></div>
167166
@ <div id='chat-edit-buttons'>
168167
@ <button id="chat-preview-button" \
169168
@ title="Preview message (Shift-Enter)">&#128065;</button>
170169
@ <button id="chat-settings-button" \
171170
@ title="Configure chat">&#9881;</button>
172171
--- src/chat.c
+++ src/chat.c
@@ -142,12 +142,10 @@
142 ** send new chat message, delete older messages, or poll for changes.
143 */
144 void chat_webpage(void){
145 char *zAlert;
146 char *zProjectName;
147 const char * zInputPlaceholder2 = /* Placeholder for textarea input*/
148 "Ctrl-Enter sends and Shift-Enter previews.";
149 char * zInputPlaceholder0; /* Common text input placeholder value */
150 login_check_credentials();
151 if( !g.perm.Chat ){
152 login_needed(g.anon.Chat);
153 return;
@@ -160,12 +158,13 @@
160 style_set_current_feature("chat");
161 style_header("Chat");
162 @ <div id='chat-input-area'>
163 @ <div id='chat-input-line' class='single-line'>
164 @ <div contenteditable id="chat-input-field" \
165 @ placeholder="%h(zInputPlaceholder0) %h(zInputPlaceholder2)" \
166 @ class></div>
 
167 @ <div id='chat-edit-buttons'>
168 @ <button id="chat-preview-button" \
169 @ title="Preview message (Shift-Enter)">&#128065;</button>
170 @ <button id="chat-settings-button" \
171 @ title="Configure chat">&#9881;</button>
172
--- src/chat.c
+++ src/chat.c
@@ -142,12 +142,10 @@
142 ** send new chat message, delete older messages, or poll for changes.
143 */
144 void chat_webpage(void){
145 char *zAlert;
146 char *zProjectName;
 
 
147 char * zInputPlaceholder0; /* Common text input placeholder value */
148 login_check_credentials();
149 if( !g.perm.Chat ){
150 login_needed(g.anon.Chat);
151 return;
@@ -160,12 +158,13 @@
158 style_set_current_feature("chat");
159 style_header("Chat");
160 @ <div id='chat-input-area'>
161 @ <div id='chat-input-line' class='single-line'>
162 @ <div contenteditable id="chat-input-field" \
163 @ data-placeholder0="%h(zInputPlaceholder0)" \
164 @ data-placeholder="%h(zInputPlaceholder0)" \
165 @ class=""></div>
166 @ <div id='chat-edit-buttons'>
167 @ <button id="chat-preview-button" \
168 @ title="Preview message (Shift-Enter)">&#128065;</button>
169 @ <button id="chat-settings-button" \
170 @ title="Configure chat">&#9881;</button>
171
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -1118,20 +1118,32 @@
11181118
},
11191119
dragenter: function(ev){
11201120
ev.preventDefault();
11211121
ev.dataTransfer.dropEffect = "copy";
11221122
D.addClass(dropHighlight, 'dragover');
1123
+ return false;
11231124
},
11241125
dragleave: function(ev){
11251126
D.removeClass(dropHighlight, 'dragover');
11261127
},
11271128
dragend: function(ev){
11281129
D.removeClass(dropHighlight, 'dragover');
11291130
}
1131
+ };
1132
+ const cancelEvent = function(ev){
1133
+ /* contenteditable tries to do its own thing with dropped data,
1134
+ which is not compatible with how we use it, so... */
1135
+ //if(ev.dataTransfer) ev.dataTransfer.dropEffect = 'none';
1136
+ ev.preventDefault();
1137
+ ev.stopPropagation();
1138
+ return false;
11301139
};
11311140
Object.keys(dropEvents).forEach(
1132
- (k)=>Chat.e.inputFile.addEventListener(k, dropEvents[k], true)
1141
+ (k)=>{
1142
+ Chat.e.inputFile.addEventListener(k, dropEvents[k], true);
1143
+ Chat.inputElement().addEventListener(k, cancelEvent, false);
1144
+ }
11331145
);
11341146
return bxs;
11351147
})()/*drag/drop*/;
11361148
11371149
const tzOffsetToString = function(off){
@@ -1203,23 +1215,20 @@
12031215
const inputWidgetKeydown = function f(ev){
12041216
if(!f.$toggleCtrl){
12051217
f.$toggleCtrl = function(currentMode){
12061218
currentMode = !currentMode;
12071219
Chat.settings.set('edit-ctrl-send', currentMode);
1208
- F.toast.message((currentMode ? "Ctrl-" : "")
1209
- +"Enter submits messages.");
12101220
};
12111221
f.$toggleCompact = function(currentMode){
12121222
currentMode = !currentMode;
12131223
Chat.settings.set('edit-compact-mode', currentMode);
12141224
};
12151225
}
1216
- //console.debug("Enter key event:", ev.keyCode, ev.ctrlKey, ev.shiftKey, ev);
12171226
if(13 !== ev.keyCode) return;
1227
+ const text = Chat.inputValue().trim();
12181228
const ctrlMode = Chat.settings.getBool('edit-ctrl-send', false);
12191229
//console.debug("Enter key event:", ctrlMode, ev.ctrlKey, ev.shiftKey, ev);
1220
- const text = Chat.inputValue().trim();
12211230
if(ev.shiftKey){
12221231
const compactMode = Chat.settings.getBool('edit-compact-mode', false);
12231232
ev.preventDefault();
12241233
ev.stopPropagation();
12251234
/* Shift-enter will run preview mode UNLESS preview mode is
@@ -1232,25 +1241,32 @@
12321241
}else{
12331242
Chat.e.btnPreview.click();
12341243
}
12351244
return false;
12361245
}
1237
- if(0 && (!ctrlMode && ev.ctrlKey && text)){
1238
- /* Ctrl-enter in Enter-sends mode SHOULD, with this logic add a
1239
- newline, but that is not happening, for reasons i don't
1240
- understand. Forcibly appending a newline do the input area
1241
- does not work, also for unknown reasons.
1242
- */
1243
- return;
1244
- }
12451246
if(ev.ctrlKey && !text){
12461247
/* Ctrl-enter on an empty field toggles Enter/Ctrl-enter mode */
12471248
ev.preventDefault();
12481249
ev.stopPropagation();
12491250
f.$toggleCtrl(ctrlMode);
12501251
return false;
1251
- }else if((!ctrlMode && !ev.ctrlKey) || (ev.ctrlKey && ctrlMode)){
1252
+ }
1253
+ if(!ctrlMode && ev.ctrlKey && text){
1254
+ //console.debug("!ctrlMode && ev.ctrlKey && text.");
1255
+ /* Ctrl-enter in Enter-sends mode SHOULD, with this logic add a
1256
+ newline, but that is not happening, for unknown reasons
1257
+ (possibly related to this element being a conteneditable DIV
1258
+ instead of a textarea). Forcibly appending a newline do the
1259
+ input area does not work, also for unknown reasons, and would
1260
+ only be suitable when we're at the end of the input.
1261
+
1262
+ Strangely, this approach DOES work for shift-enter, but we
1263
+ need shift-enter as a hotkey for preview mode.
1264
+ */
1265
+ return;
1266
+ }
1267
+ if((!ctrlMode && !ev.ctrlKey) || (ev.ctrlKey && ctrlMode)){
12521268
/* Ship it! */
12531269
ev.preventDefault();
12541270
ev.stopPropagation();
12551271
Chat.submitMessage();
12561272
return false;
@@ -1339,11 +1355,11 @@
13391355
"When the input field has focus, is empty, and preview "+
13401356
"mode is NOT active then Ctrl-Enter toggles this setting.",
13411357
boolValue: 'edit-ctrl-send'
13421358
},{
13431359
label: "Compact mode",
1344
- hint: "Toggle between a space-saving and more spacious writing area. "+
1360
+ hint: "Toggle between a space-saving or more spacious writing area. "+
13451361
"When the input field has focus, is empty, and preview mode "+
13461362
"is NOT active then Shift-Enter toggles this setting.",
13471363
boolValue: 'edit-compact-mode'
13481364
},{
13491365
label: "Left-align my posts",
@@ -1500,10 +1516,16 @@
15001516
});
15011517
Chat.settings.addListener('edit-compact-mode',function(s){
15021518
Chat.e.inputLine.classList[
15031519
s.value ? 'add' : 'remove'
15041520
]('compact');
1521
+ });
1522
+ Chat.settings.addListener('edit-ctrl-send',function(s){
1523
+ const label = (s.value ? "Ctrl-" : "")+"Enter submits messages.";
1524
+ const eInput = Chat.inputElement();
1525
+ eInput.dataset.placeholder = eInput.dataset.placeholder0 + " " +label;
1526
+ F.toast.message(label);
15051527
});
15061528
const valueKludges = {
15071529
/* Convert certain string-format values to other types... */
15081530
"false": false,
15091531
"true": true
15101532
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -1118,20 +1118,32 @@
1118 },
1119 dragenter: function(ev){
1120 ev.preventDefault();
1121 ev.dataTransfer.dropEffect = "copy";
1122 D.addClass(dropHighlight, 'dragover');
 
1123 },
1124 dragleave: function(ev){
1125 D.removeClass(dropHighlight, 'dragover');
1126 },
1127 dragend: function(ev){
1128 D.removeClass(dropHighlight, 'dragover');
1129 }
 
 
 
 
 
 
 
 
1130 };
1131 Object.keys(dropEvents).forEach(
1132 (k)=>Chat.e.inputFile.addEventListener(k, dropEvents[k], true)
 
 
 
1133 );
1134 return bxs;
1135 })()/*drag/drop*/;
1136
1137 const tzOffsetToString = function(off){
@@ -1203,23 +1215,20 @@
1203 const inputWidgetKeydown = function f(ev){
1204 if(!f.$toggleCtrl){
1205 f.$toggleCtrl = function(currentMode){
1206 currentMode = !currentMode;
1207 Chat.settings.set('edit-ctrl-send', currentMode);
1208 F.toast.message((currentMode ? "Ctrl-" : "")
1209 +"Enter submits messages.");
1210 };
1211 f.$toggleCompact = function(currentMode){
1212 currentMode = !currentMode;
1213 Chat.settings.set('edit-compact-mode', currentMode);
1214 };
1215 }
1216 //console.debug("Enter key event:", ev.keyCode, ev.ctrlKey, ev.shiftKey, ev);
1217 if(13 !== ev.keyCode) return;
 
1218 const ctrlMode = Chat.settings.getBool('edit-ctrl-send', false);
1219 //console.debug("Enter key event:", ctrlMode, ev.ctrlKey, ev.shiftKey, ev);
1220 const text = Chat.inputValue().trim();
1221 if(ev.shiftKey){
1222 const compactMode = Chat.settings.getBool('edit-compact-mode', false);
1223 ev.preventDefault();
1224 ev.stopPropagation();
1225 /* Shift-enter will run preview mode UNLESS preview mode is
@@ -1232,25 +1241,32 @@
1232 }else{
1233 Chat.e.btnPreview.click();
1234 }
1235 return false;
1236 }
1237 if(0 && (!ctrlMode && ev.ctrlKey && text)){
1238 /* Ctrl-enter in Enter-sends mode SHOULD, with this logic add a
1239 newline, but that is not happening, for reasons i don't
1240 understand. Forcibly appending a newline do the input area
1241 does not work, also for unknown reasons.
1242 */
1243 return;
1244 }
1245 if(ev.ctrlKey && !text){
1246 /* Ctrl-enter on an empty field toggles Enter/Ctrl-enter mode */
1247 ev.preventDefault();
1248 ev.stopPropagation();
1249 f.$toggleCtrl(ctrlMode);
1250 return false;
1251 }else if((!ctrlMode && !ev.ctrlKey) || (ev.ctrlKey && ctrlMode)){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1252 /* Ship it! */
1253 ev.preventDefault();
1254 ev.stopPropagation();
1255 Chat.submitMessage();
1256 return false;
@@ -1339,11 +1355,11 @@
1339 "When the input field has focus, is empty, and preview "+
1340 "mode is NOT active then Ctrl-Enter toggles this setting.",
1341 boolValue: 'edit-ctrl-send'
1342 },{
1343 label: "Compact mode",
1344 hint: "Toggle between a space-saving and more spacious writing area. "+
1345 "When the input field has focus, is empty, and preview mode "+
1346 "is NOT active then Shift-Enter toggles this setting.",
1347 boolValue: 'edit-compact-mode'
1348 },{
1349 label: "Left-align my posts",
@@ -1500,10 +1516,16 @@
1500 });
1501 Chat.settings.addListener('edit-compact-mode',function(s){
1502 Chat.e.inputLine.classList[
1503 s.value ? 'add' : 'remove'
1504 ]('compact');
 
 
 
 
 
 
1505 });
1506 const valueKludges = {
1507 /* Convert certain string-format values to other types... */
1508 "false": false,
1509 "true": true
1510
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -1118,20 +1118,32 @@
1118 },
1119 dragenter: function(ev){
1120 ev.preventDefault();
1121 ev.dataTransfer.dropEffect = "copy";
1122 D.addClass(dropHighlight, 'dragover');
1123 return false;
1124 },
1125 dragleave: function(ev){
1126 D.removeClass(dropHighlight, 'dragover');
1127 },
1128 dragend: function(ev){
1129 D.removeClass(dropHighlight, 'dragover');
1130 }
1131 };
1132 const cancelEvent = function(ev){
1133 /* contenteditable tries to do its own thing with dropped data,
1134 which is not compatible with how we use it, so... */
1135 //if(ev.dataTransfer) ev.dataTransfer.dropEffect = 'none';
1136 ev.preventDefault();
1137 ev.stopPropagation();
1138 return false;
1139 };
1140 Object.keys(dropEvents).forEach(
1141 (k)=>{
1142 Chat.e.inputFile.addEventListener(k, dropEvents[k], true);
1143 Chat.inputElement().addEventListener(k, cancelEvent, false);
1144 }
1145 );
1146 return bxs;
1147 })()/*drag/drop*/;
1148
1149 const tzOffsetToString = function(off){
@@ -1203,23 +1215,20 @@
1215 const inputWidgetKeydown = function f(ev){
1216 if(!f.$toggleCtrl){
1217 f.$toggleCtrl = function(currentMode){
1218 currentMode = !currentMode;
1219 Chat.settings.set('edit-ctrl-send', currentMode);
 
 
1220 };
1221 f.$toggleCompact = function(currentMode){
1222 currentMode = !currentMode;
1223 Chat.settings.set('edit-compact-mode', currentMode);
1224 };
1225 }
 
1226 if(13 !== ev.keyCode) return;
1227 const text = Chat.inputValue().trim();
1228 const ctrlMode = Chat.settings.getBool('edit-ctrl-send', false);
1229 //console.debug("Enter key event:", ctrlMode, ev.ctrlKey, ev.shiftKey, ev);
 
1230 if(ev.shiftKey){
1231 const compactMode = Chat.settings.getBool('edit-compact-mode', false);
1232 ev.preventDefault();
1233 ev.stopPropagation();
1234 /* Shift-enter will run preview mode UNLESS preview mode is
@@ -1232,25 +1241,32 @@
1241 }else{
1242 Chat.e.btnPreview.click();
1243 }
1244 return false;
1245 }
 
 
 
 
 
 
 
 
1246 if(ev.ctrlKey && !text){
1247 /* Ctrl-enter on an empty field toggles Enter/Ctrl-enter mode */
1248 ev.preventDefault();
1249 ev.stopPropagation();
1250 f.$toggleCtrl(ctrlMode);
1251 return false;
1252 }
1253 if(!ctrlMode && ev.ctrlKey && text){
1254 //console.debug("!ctrlMode && ev.ctrlKey && text.");
1255 /* Ctrl-enter in Enter-sends mode SHOULD, with this logic add a
1256 newline, but that is not happening, for unknown reasons
1257 (possibly related to this element being a conteneditable DIV
1258 instead of a textarea). Forcibly appending a newline do the
1259 input area does not work, also for unknown reasons, and would
1260 only be suitable when we're at the end of the input.
1261
1262 Strangely, this approach DOES work for shift-enter, but we
1263 need shift-enter as a hotkey for preview mode.
1264 */
1265 return;
1266 }
1267 if((!ctrlMode && !ev.ctrlKey) || (ev.ctrlKey && ctrlMode)){
1268 /* Ship it! */
1269 ev.preventDefault();
1270 ev.stopPropagation();
1271 Chat.submitMessage();
1272 return false;
@@ -1339,11 +1355,11 @@
1355 "When the input field has focus, is empty, and preview "+
1356 "mode is NOT active then Ctrl-Enter toggles this setting.",
1357 boolValue: 'edit-ctrl-send'
1358 },{
1359 label: "Compact mode",
1360 hint: "Toggle between a space-saving or more spacious writing area. "+
1361 "When the input field has focus, is empty, and preview mode "+
1362 "is NOT active then Shift-Enter toggles this setting.",
1363 boolValue: 'edit-compact-mode'
1364 },{
1365 label: "Left-align my posts",
@@ -1500,10 +1516,16 @@
1516 });
1517 Chat.settings.addListener('edit-compact-mode',function(s){
1518 Chat.e.inputLine.classList[
1519 s.value ? 'add' : 'remove'
1520 ]('compact');
1521 });
1522 Chat.settings.addListener('edit-ctrl-send',function(s){
1523 const label = (s.value ? "Ctrl-" : "")+"Enter submits messages.";
1524 const eInput = Chat.inputElement();
1525 eInput.dataset.placeholder = eInput.dataset.placeholder0 + " " +label;
1526 F.toast.message(label);
1527 });
1528 const valueKludges = {
1529 /* Convert certain string-format values to other types... */
1530 "false": false,
1531 "true": true
1532
--- src/style.chat.css
+++ src/style.chat.css
@@ -36,12 +36,12 @@
3636
min-width: 9em /*avoid unsightly "underlap" with the neighboring
3737
.message-widget-tab element*/;
3838
white-space: normal;
3939
}
4040
body.chat.monospace-messages .message-widget-content,
41
-body.chat.monospace-messages textarea,
42
-body.chat.monospace-messages input[type=text],
41
+/*body.chat.monospace-messages textarea,*/
42
+/*body.chat.monospace-messages input[type=text],*/
4343
body.chat.monospace-messages #chat-input-field{
4444
font-family: monospace;
4545
}
4646
body.chat .message-widget-content > * {
4747
margin: 0;
@@ -178,15 +178,21 @@
178178
widget from overlapping the page footer, whereas a margin of 0 is fine
179179
for FF/Chrome (and 2em is a *huge* waste of space for those). */
180180
margin-bottom: 0;
181181
}
182182
#chat-input-field {
183
+ display: inline-block/*supposed workaround for Chrome weirdness*/;
183184
padding: 0.2em;
184185
flex: 50 1 85%;
185186
background-color: rgba(156,156,156,0.3);
186187
overflow: auto;
187188
max-height: 8rem /*arbitrary!*/;
189
+ /*white-space: pre-wrap;*/
190
+}
191
+#chat-input-field:empty::before {
192
+ content: attr(data-placeholder);
193
+ opacity: 0.6;
188194
}
189195
#chat-input-field:not(:focus){
190196
border-width: 1px;
191197
border-style: solid;
192198
border-radius: 0.25em;
193199
--- src/style.chat.css
+++ src/style.chat.css
@@ -36,12 +36,12 @@
36 min-width: 9em /*avoid unsightly "underlap" with the neighboring
37 .message-widget-tab element*/;
38 white-space: normal;
39 }
40 body.chat.monospace-messages .message-widget-content,
41 body.chat.monospace-messages textarea,
42 body.chat.monospace-messages input[type=text],
43 body.chat.monospace-messages #chat-input-field{
44 font-family: monospace;
45 }
46 body.chat .message-widget-content > * {
47 margin: 0;
@@ -178,15 +178,21 @@
178 widget from overlapping the page footer, whereas a margin of 0 is fine
179 for FF/Chrome (and 2em is a *huge* waste of space for those). */
180 margin-bottom: 0;
181 }
182 #chat-input-field {
 
183 padding: 0.2em;
184 flex: 50 1 85%;
185 background-color: rgba(156,156,156,0.3);
186 overflow: auto;
187 max-height: 8rem /*arbitrary!*/;
 
 
 
 
 
188 }
189 #chat-input-field:not(:focus){
190 border-width: 1px;
191 border-style: solid;
192 border-radius: 0.25em;
193
--- src/style.chat.css
+++ src/style.chat.css
@@ -36,12 +36,12 @@
36 min-width: 9em /*avoid unsightly "underlap" with the neighboring
37 .message-widget-tab element*/;
38 white-space: normal;
39 }
40 body.chat.monospace-messages .message-widget-content,
41 /*body.chat.monospace-messages textarea,*/
42 /*body.chat.monospace-messages input[type=text],*/
43 body.chat.monospace-messages #chat-input-field{
44 font-family: monospace;
45 }
46 body.chat .message-widget-content > * {
47 margin: 0;
@@ -178,15 +178,21 @@
178 widget from overlapping the page footer, whereas a margin of 0 is fine
179 for FF/Chrome (and 2em is a *huge* waste of space for those). */
180 margin-bottom: 0;
181 }
182 #chat-input-field {
183 display: inline-block/*supposed workaround for Chrome weirdness*/;
184 padding: 0.2em;
185 flex: 50 1 85%;
186 background-color: rgba(156,156,156,0.3);
187 overflow: auto;
188 max-height: 8rem /*arbitrary!*/;
189 /*white-space: pre-wrap;*/
190 }
191 #chat-input-field:empty::before {
192 content: attr(data-placeholder);
193 opacity: 0.6;
194 }
195 #chat-input-field:not(:focus){
196 border-width: 1px;
197 border-style: solid;
198 border-radius: 0.25em;
199

Keyboard Shortcuts

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