Fossil SCM

Add a Zoom option to the message toolbar in /chat, the intent of which is to enable viewing of pikchrs which render illegibly small.

stephan 2025-11-27 13:53 trunk merge
Commit 72d93e8ccfa318171984de3f54cb5baeba2887727685451bec8107f9c7ba73c1
+5
--- src/chat.c
+++ src/chat.c
@@ -238,10 +238,15 @@
238238
@ </div>
239239
@ <div id='chat-messages-wrapper' class='chat-view'>
240240
/* New chat messages get inserted immediately after this element */
241241
@ <span id='message-inject-point'></span>
242242
@ </div>
243
+ @ <div id='chat-zoom' class='hidden chat-view'>
244
+ @ <div id='chat-zoom-content'></div>
245
+ @ <div class='button-bar'><button class='action-close'>Close Zoom</button></div>
246
+ @ </div>
247
+ @ <span id='chat-zoom-marker' class='hidden'><!-- placeholder marker for zoomed msg --></span>
243248
fossil_free(zProjectName);
244249
fossil_free(zInputPlaceholder0);
245250
builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch",
246251
"pikchr", "confirmer", "copybutton",
247252
NULL);
248253
--- src/chat.c
+++ src/chat.c
@@ -238,10 +238,15 @@
238 @ </div>
239 @ <div id='chat-messages-wrapper' class='chat-view'>
240 /* New chat messages get inserted immediately after this element */
241 @ <span id='message-inject-point'></span>
242 @ </div>
 
 
 
 
 
243 fossil_free(zProjectName);
244 fossil_free(zInputPlaceholder0);
245 builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch",
246 "pikchr", "confirmer", "copybutton",
247 NULL);
248
--- src/chat.c
+++ src/chat.c
@@ -238,10 +238,15 @@
238 @ </div>
239 @ <div id='chat-messages-wrapper' class='chat-view'>
240 /* New chat messages get inserted immediately after this element */
241 @ <span id='message-inject-point'></span>
242 @ </div>
243 @ <div id='chat-zoom' class='hidden chat-view'>
244 @ <div id='chat-zoom-content'></div>
245 @ <div class='button-bar'><button class='action-close'>Close Zoom</button></div>
246 @ </div>
247 @ <span id='chat-zoom-marker' class='hidden'><!-- placeholder marker for zoomed msg --></span>
248 fossil_free(zProjectName);
249 fossil_free(zInputPlaceholder0);
250 builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch",
251 "pikchr", "confirmer", "copybutton",
252 NULL);
253
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -142,10 +142,13 @@
142142
loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
143143
inputArea: E1("#chat-input-area"),
144144
inputLineWrapper: E1('#chat-input-line-wrapper'),
145145
fileSelectWrapper: E1('#chat-input-file-area'),
146146
viewMessages: E1('#chat-messages-wrapper'),
147
+ viewZoom: E1('#chat-zoom'),
148
+ zoomContent: E1('#chat-zoom-content'),
149
+ zoomMarker: E1('#chat-zoom-marker'),
147150
btnSubmit: E1('#chat-button-submit'),
148151
btnAttach: E1('#chat-button-attach'),
149152
inputX: E1('#chat-input-field-x'),
150153
input1: E1('#chat-input-field-single'),
151154
inputM: E1('#chat-input-field-multi'),
@@ -600,10 +603,36 @@
600603
if(this.e.currentView.$beforeShow) this.e.currentView.$beforeShow();
601604
D.removeClass(e,'hidden');
602605
this.animate(this.e.currentView, 'anim-fade-in-fast');
603606
return this.e.currentView;
604607
},
608
+
609
+ /**
610
+ Makes message element eMsg the content of this.e.viewZoom.
611
+ */
612
+ zoomMessage: function(eMsg){
613
+ const marker = this.e.zoomMarker;
614
+ if( !eMsg ){
615
+ if( this.e.zoomedMsg ){
616
+ marker.parentNode.insertBefore(this.e.zoomedMsg, marker);
617
+ delete this.e.zoomedMsg;
618
+ }
619
+ this.setCurrentView(this.e.viewMessages);
620
+ return;
621
+ }
622
+ console.log("zoom message",eMsg);
623
+ if( eMsg===this.e.zoomedMsg ){
624
+ return;
625
+ }
626
+ if( this.e.zoomedMsg ){
627
+ marker.parentNode.insertBefore(this.e.zoomedMsg, marker);
628
+ }
629
+ this.e.viewMessages.insertBefore(marker, eMsg);
630
+ this.e.zoomContent.appendChild(eMsg);
631
+ this.e.zoomedMsg = eMsg;
632
+ this.setCurrentView(this.e.viewZoom);
633
+ },
605634
/**
606635
Updates the "active user list" view if we are not currently
607636
batch-loading messages and if the active user list UI element
608637
is active.
609638
*/
@@ -1351,13 +1380,14 @@
13511380
// Date doesn't work, so dumb it down...
13521381
D.append(this.e, D.append(D.span(), eMsg.dataset.timestamp," zulu"));
13531382
}
13541383
const toolbar = D.addClass(D.div(), 'toolbar');
13551384
D.append(this.e, toolbar);
1385
+ const self = this;
1386
+
13561387
const btnDeleteLocal = D.button("Delete locally");
13571388
D.append(toolbar, btnDeleteLocal);
1358
- const self = this;
13591389
btnDeleteLocal.addEventListener('click', function(){
13601390
self.hide();
13611391
Chat.deleteMessageElem(eMsg)
13621392
});
13631393
if( eMsg.classList.contains('notification') ){
@@ -1444,10 +1474,15 @@
14441474
})
14451475
)
14461476
);
14471477
}/*jump-to button*/
14481478
}
1479
+ const btnZoom = D.button("Zoom");
1480
+ D.append(toolbar2, btnZoom);
1481
+ btnZoom.addEventListener('click', function(){
1482
+ Chat.zoomMessage(eMsg);
1483
+ });
14491484
const tab = eMsg.querySelector('.message-widget-tab');
14501485
D.append(tab, this.e);
14511486
D.removeClass(this.e, 'hidden');
14521487
Chat.animate(this.e, 'anim-fade-in-fast');
14531488
}/*refresh()*/,
@@ -2433,10 +2468,19 @@
24332468
ev.stopPropagation();
24342469
Chat.setCurrentView(Chat.e.viewMessages);
24352470
return false;
24362471
}, false);
24372472
})()/*search view setup*/;
2473
+
2474
+ (function(){/*Set up the zoom view */
2475
+ Chat.e.viewZoom.querySelector('button.action-close').addEventListener('click', function(ev){
2476
+ ev.preventDefault();
2477
+ ev.stopPropagation();
2478
+ Chat.zoomMessage(null);
2479
+ return false;
2480
+ }, false);
2481
+ })()/*zoom view setup*/;
24382482
24392483
/** Callback for poll() to inject new content into the page. jx ==
24402484
the response from /chat-poll. If atEnd is true, the message is
24412485
appended to the end of the chat list (for loading older
24422486
messages), else the beginning (the default). */
24432487
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -142,10 +142,13 @@
142 loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
143 inputArea: E1("#chat-input-area"),
144 inputLineWrapper: E1('#chat-input-line-wrapper'),
145 fileSelectWrapper: E1('#chat-input-file-area'),
146 viewMessages: E1('#chat-messages-wrapper'),
 
 
 
147 btnSubmit: E1('#chat-button-submit'),
148 btnAttach: E1('#chat-button-attach'),
149 inputX: E1('#chat-input-field-x'),
150 input1: E1('#chat-input-field-single'),
151 inputM: E1('#chat-input-field-multi'),
@@ -600,10 +603,36 @@
600 if(this.e.currentView.$beforeShow) this.e.currentView.$beforeShow();
601 D.removeClass(e,'hidden');
602 this.animate(this.e.currentView, 'anim-fade-in-fast');
603 return this.e.currentView;
604 },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
605 /**
606 Updates the "active user list" view if we are not currently
607 batch-loading messages and if the active user list UI element
608 is active.
609 */
@@ -1351,13 +1380,14 @@
1351 // Date doesn't work, so dumb it down...
1352 D.append(this.e, D.append(D.span(), eMsg.dataset.timestamp," zulu"));
1353 }
1354 const toolbar = D.addClass(D.div(), 'toolbar');
1355 D.append(this.e, toolbar);
 
 
1356 const btnDeleteLocal = D.button("Delete locally");
1357 D.append(toolbar, btnDeleteLocal);
1358 const self = this;
1359 btnDeleteLocal.addEventListener('click', function(){
1360 self.hide();
1361 Chat.deleteMessageElem(eMsg)
1362 });
1363 if( eMsg.classList.contains('notification') ){
@@ -1444,10 +1474,15 @@
1444 })
1445 )
1446 );
1447 }/*jump-to button*/
1448 }
 
 
 
 
 
1449 const tab = eMsg.querySelector('.message-widget-tab');
1450 D.append(tab, this.e);
1451 D.removeClass(this.e, 'hidden');
1452 Chat.animate(this.e, 'anim-fade-in-fast');
1453 }/*refresh()*/,
@@ -2433,10 +2468,19 @@
2433 ev.stopPropagation();
2434 Chat.setCurrentView(Chat.e.viewMessages);
2435 return false;
2436 }, false);
2437 })()/*search view setup*/;
 
 
 
 
 
 
 
 
 
2438
2439 /** Callback for poll() to inject new content into the page. jx ==
2440 the response from /chat-poll. If atEnd is true, the message is
2441 appended to the end of the chat list (for loading older
2442 messages), else the beginning (the default). */
2443
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -142,10 +142,13 @@
142 loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
143 inputArea: E1("#chat-input-area"),
144 inputLineWrapper: E1('#chat-input-line-wrapper'),
145 fileSelectWrapper: E1('#chat-input-file-area'),
146 viewMessages: E1('#chat-messages-wrapper'),
147 viewZoom: E1('#chat-zoom'),
148 zoomContent: E1('#chat-zoom-content'),
149 zoomMarker: E1('#chat-zoom-marker'),
150 btnSubmit: E1('#chat-button-submit'),
151 btnAttach: E1('#chat-button-attach'),
152 inputX: E1('#chat-input-field-x'),
153 input1: E1('#chat-input-field-single'),
154 inputM: E1('#chat-input-field-multi'),
@@ -600,10 +603,36 @@
603 if(this.e.currentView.$beforeShow) this.e.currentView.$beforeShow();
604 D.removeClass(e,'hidden');
605 this.animate(this.e.currentView, 'anim-fade-in-fast');
606 return this.e.currentView;
607 },
608
609 /**
610 Makes message element eMsg the content of this.e.viewZoom.
611 */
612 zoomMessage: function(eMsg){
613 const marker = this.e.zoomMarker;
614 if( !eMsg ){
615 if( this.e.zoomedMsg ){
616 marker.parentNode.insertBefore(this.e.zoomedMsg, marker);
617 delete this.e.zoomedMsg;
618 }
619 this.setCurrentView(this.e.viewMessages);
620 return;
621 }
622 console.log("zoom message",eMsg);
623 if( eMsg===this.e.zoomedMsg ){
624 return;
625 }
626 if( this.e.zoomedMsg ){
627 marker.parentNode.insertBefore(this.e.zoomedMsg, marker);
628 }
629 this.e.viewMessages.insertBefore(marker, eMsg);
630 this.e.zoomContent.appendChild(eMsg);
631 this.e.zoomedMsg = eMsg;
632 this.setCurrentView(this.e.viewZoom);
633 },
634 /**
635 Updates the "active user list" view if we are not currently
636 batch-loading messages and if the active user list UI element
637 is active.
638 */
@@ -1351,13 +1380,14 @@
1380 // Date doesn't work, so dumb it down...
1381 D.append(this.e, D.append(D.span(), eMsg.dataset.timestamp," zulu"));
1382 }
1383 const toolbar = D.addClass(D.div(), 'toolbar');
1384 D.append(this.e, toolbar);
1385 const self = this;
1386
1387 const btnDeleteLocal = D.button("Delete locally");
1388 D.append(toolbar, btnDeleteLocal);
 
1389 btnDeleteLocal.addEventListener('click', function(){
1390 self.hide();
1391 Chat.deleteMessageElem(eMsg)
1392 });
1393 if( eMsg.classList.contains('notification') ){
@@ -1444,10 +1474,15 @@
1474 })
1475 )
1476 );
1477 }/*jump-to button*/
1478 }
1479 const btnZoom = D.button("Zoom");
1480 D.append(toolbar2, btnZoom);
1481 btnZoom.addEventListener('click', function(){
1482 Chat.zoomMessage(eMsg);
1483 });
1484 const tab = eMsg.querySelector('.message-widget-tab');
1485 D.append(tab, this.e);
1486 D.removeClass(this.e, 'hidden');
1487 Chat.animate(this.e, 'anim-fade-in-fast');
1488 }/*refresh()*/,
@@ -2433,10 +2468,19 @@
2468 ev.stopPropagation();
2469 Chat.setCurrentView(Chat.e.viewMessages);
2470 return false;
2471 }, false);
2472 })()/*search view setup*/;
2473
2474 (function(){/*Set up the zoom view */
2475 Chat.e.viewZoom.querySelector('button.action-close').addEventListener('click', function(ev){
2476 ev.preventDefault();
2477 ev.stopPropagation();
2478 Chat.zoomMessage(null);
2479 return false;
2480 }, false);
2481 })()/*zoom view setup*/;
2482
2483 /** Callback for poll() to inject new content into the page. jx ==
2484 the response from /chat-poll. If atEnd is true, the message is
2485 appended to the end of the chat list (for loading older
2486 messages), else the beginning (the default). */
2487
--- src/style.chat.css
+++ src/style.chat.css
@@ -462,19 +462,33 @@
462462
Note that setting flex shrink to 0 breaks/disables scrolling!*/;
463463
margin-bottom: 0.2em;
464464
}
465465
body.chat #chat-config,
466466
body.chat #chat-search,
467
-body.chat #chat-preview {
467
+body.chat #chat-preview,
468
+body.chat #chat-zoom {
468469
/* /chat configuration widget */
469470
display: flex;
470471
flex-direction: column;
471472
overflow: auto;
472473
padding: 0;
473474
margin: 0;
474475
align-items: stretch;
475476
min-height: 6em;
477
+}
478
+body.chat #chat-zoom {
479
+ justify-content: space-between;
480
+}
481
+body.chat #chat-zoom-content {
482
+ display: flex;
483
+ overflow: auto;
484
+}
485
+body.chat #chat-zoom-content > .message-widget {
486
+ flex-grow: 1;
487
+}
488
+body.chat #chat-zoom-content > .message-widget .message-widget-content {
489
+ width: 100%;
476490
}
477491
body.chat #chat-config #chat-config-options {
478492
/* /chat config options go here */
479493
flex: 1 1 auto;
480494
display: flex;
481495
--- src/style.chat.css
+++ src/style.chat.css
@@ -462,19 +462,33 @@
462 Note that setting flex shrink to 0 breaks/disables scrolling!*/;
463 margin-bottom: 0.2em;
464 }
465 body.chat #chat-config,
466 body.chat #chat-search,
467 body.chat #chat-preview {
 
468 /* /chat configuration widget */
469 display: flex;
470 flex-direction: column;
471 overflow: auto;
472 padding: 0;
473 margin: 0;
474 align-items: stretch;
475 min-height: 6em;
 
 
 
 
 
 
 
 
 
 
 
 
 
476 }
477 body.chat #chat-config #chat-config-options {
478 /* /chat config options go here */
479 flex: 1 1 auto;
480 display: flex;
481
--- src/style.chat.css
+++ src/style.chat.css
@@ -462,19 +462,33 @@
462 Note that setting flex shrink to 0 breaks/disables scrolling!*/;
463 margin-bottom: 0.2em;
464 }
465 body.chat #chat-config,
466 body.chat #chat-search,
467 body.chat #chat-preview,
468 body.chat #chat-zoom {
469 /* /chat configuration widget */
470 display: flex;
471 flex-direction: column;
472 overflow: auto;
473 padding: 0;
474 margin: 0;
475 align-items: stretch;
476 min-height: 6em;
477 }
478 body.chat #chat-zoom {
479 justify-content: space-between;
480 }
481 body.chat #chat-zoom-content {
482 display: flex;
483 overflow: auto;
484 }
485 body.chat #chat-zoom-content > .message-widget {
486 flex-grow: 1;
487 }
488 body.chat #chat-zoom-content > .message-widget .message-widget-content {
489 width: 100%;
490 }
491 body.chat #chat-config #chat-config-options {
492 /* /chat config options go here */
493 flex: 1 1 auto;
494 display: flex;
495
--- www/changes.wiki
+++ www/changes.wiki
@@ -48,10 +48,12 @@
4848
"<tt>-u|--for-user</tt>" option.
4949
<li> The [/help/open|open command]'s new "<tt>--reopen REPOFILE</tt>" flag
5050
can be used to fix a checkout after moving its repository file.
5151
<li> Update internal Unicode character tables, used in regular expression
5252
handling, from version 13 to 17.
53
+ <li> Add a zoom-message option to [/help/www/chat|/chat] to better support
54
+ pikchr diagrams.
5355
</ol>
5456
5557
<h2 id='v2_27'>Changes for version 2.27 (2025-09-30)</h2><ol>
5658
<li> Close a potential Denial-of-Service attack against any public-facing Fossil
5759
server involving exponential behavior in Fossil's regexp implementation.
5860
--- www/changes.wiki
+++ www/changes.wiki
@@ -48,10 +48,12 @@
48 "<tt>-u|--for-user</tt>" option.
49 <li> The [/help/open|open command]'s new "<tt>--reopen REPOFILE</tt>" flag
50 can be used to fix a checkout after moving its repository file.
51 <li> Update internal Unicode character tables, used in regular expression
52 handling, from version 13 to 17.
 
 
53 </ol>
54
55 <h2 id='v2_27'>Changes for version 2.27 (2025-09-30)</h2><ol>
56 <li> Close a potential Denial-of-Service attack against any public-facing Fossil
57 server involving exponential behavior in Fossil's regexp implementation.
58
--- www/changes.wiki
+++ www/changes.wiki
@@ -48,10 +48,12 @@
48 "<tt>-u|--for-user</tt>" option.
49 <li> The [/help/open|open command]'s new "<tt>--reopen REPOFILE</tt>" flag
50 can be used to fix a checkout after moving its repository file.
51 <li> Update internal Unicode character tables, used in regular expression
52 handling, from version 13 to 17.
53 <li> Add a zoom-message option to [/help/www/chat|/chat] to better support
54 pikchr diagrams.
55 </ol>
56
57 <h2 id='v2_27'>Changes for version 2.27 (2025-09-30)</h2><ol>
58 <li> Close a potential Denial-of-Service attack against any public-facing Fossil
59 server involving exponential behavior in Fossil's regexp implementation.
60

Keyboard Shortcuts

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