Fossil SCM

Initial work on the /chat option to zoom a single message into its own view, the idea being that it will render pikchrs larger. The initial bits are in place but i've yet to find the CSS combination which will get the message widget to stretch out in the zoom view.

stephan 2025-11-27 13:16 trunk
Commit ff297e1a35aaf3d38aed86c6f0f1cd8f5c5681f1db876f6b4c804f8ee659104f
+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,20 +462,24 @@
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;
476477
}
478
+body.chat #chat-zoom {
479
+ justify-content: space-between;
480
+}
477481
body.chat #chat-config #chat-config-options {
478482
/* /chat config options go here */
479483
flex: 1 1 auto;
480484
display: flex;
481485
flex-direction: column;
482486
--- src/style.chat.css
+++ src/style.chat.css
@@ -462,20 +462,24 @@
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 flex-direction: column;
482
--- src/style.chat.css
+++ src/style.chat.css
@@ -462,20 +462,24 @@
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-config #chat-config-options {
482 /* /chat config options go here */
483 flex: 1 1 auto;
484 display: flex;
485 flex-direction: column;
486

Keyboard Shortcuts

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