collapsible standalone msgs
3clyp50 committed
Jan 27, 2026 at 14:39 UTC
700d0041cf817817d0c5b1b1aa8c83410113ab88
2 files changed
+93
webui/css/messages.css
+61
@@ -803,3 +803,64 @@
803
.dark-mode .message {
804
box-shadow: none;
805
}
806
+
807
+/* ===========================================
808
+ Collapsible Standalone Messages
809
+ Shows ~10 lines with fade-out, expand button reveals full content
810
+ =========================================== */
811
+
812
+.message.message-collapsible .message-body {
813
+ position: relative;
814
+ max-height: 15em !important;
815
+ overflow: hidden;
816
+ transition: max-height 0.3s ease-out;
817
+}
818
+
819
+.message.message-collapsible .message-body::after {
820
+ content: "";
821
+ position: absolute;
822
+ bottom: 0;
823
+ left: 0;
824
+ right: 0;
825
+ height: 3em;
826
+ background: linear-gradient(transparent, var(--color-chat-background));
827
+ pointer-events: none;
828
+ opacity: 1;
829
+ transition: opacity 0.3s ease-out;
830
+}
831
+
832
+/* Expanded state */
833
+.message.message-collapsible.expanded .message-body {
834
+ max-height: none !important;
835
+}
836
+
837
+.message.message-collapsible.expanded .message-body::after {
838
+ opacity: 0;
839
+}
840
+
841
+/* No fade when content fits */
842
+.message.message-collapsible:not(.has-overflow) .message-body::after {
843
+ opacity: 0;
844
+}
845
+
846
+/* Expand button - hidden by default, shown when overflow */
847
+.message.message-collapsible .expand-btn {
848
+ display: none;
849
+ background: transparent;
850
+ border: none;
851
+ color: var(--color-text-muted);
852
+ font-family: "Rubik", Arial, Helvetica, sans-serif;
853
+ font-size: var(--font-size-xs);
854
+ cursor: pointer;
855
+ padding: var(--spacing-xs) 0;
856
+ opacity: 0.7;
857
+ transition: opacity 0.15s ease;
858
+}
859
+
860
+.message.message-collapsible .expand-btn:hover {
861
+ opacity: 1;
862
+}
863
+
864
+.message.message-collapsible.has-overflow .expand-btn {
865
+ display: block;
866
+}
webui/js/messages.js
+32
@@ -668,6 +668,22 @@ function drawStandaloneMessage({
668
mainClass,
669
});
670
671
+ // Collapsible: show ~10 lines with fade, expand button reveals full content
672
+ messageDiv.classList.add("message-collapsible");
673
+
674
+ const expandBtn = ensureChild(messageDiv, ".expand-btn", "button", "expand-btn");
675
+ expandBtn.textContent = messageDiv.classList.contains("expanded") ? "Show less" : "Show more";
676
+ expandBtn.onclick = () => {
677
+ messageDiv.classList.toggle("expanded");
678
+ expandBtn.textContent = messageDiv.classList.contains("expanded") ? "Show less" : "Show more";
679
+ };
680
+
681
+ // Detect overflow after render - CSS handles visibility based on .has-overflow class
682
+ requestAnimationFrame(() => {
683
+ const body = messageDiv.querySelector(".message-body");
684
+ messageDiv.classList.toggle("has-overflow", body.scrollHeight > body.clientHeight);
685
+ });
686
+
687
// Render action buttons: get/create container, clear, append
688
const actionButtonsContainer = ensureChild(
689
messageDiv,
@@ -967,6 +983,22 @@ export function drawMessageResponse({
983
mainClass: "message-agent-response",
984
});
985
986
+ // Collapsible: show ~10 lines with fade, expand button reveals full content
987
+ messageDiv.classList.add("message-collapsible");
988
+
989
+ const expandBtn = ensureChild(messageDiv, ".expand-btn", "button", "expand-btn");
990
+ expandBtn.textContent = messageDiv.classList.contains("expanded") ? "Show less" : "Show more";
991
+ expandBtn.onclick = () => {
992
+ messageDiv.classList.toggle("expanded");
993
+ expandBtn.textContent = messageDiv.classList.contains("expanded") ? "Show less" : "Show more";
994
+ };
995
+
996
+ // Detect overflow after render - CSS handles visibility based on .has-overflow class
997
+ requestAnimationFrame(() => {
998
+ const body = messageDiv.querySelector(".message-body");
999
+ messageDiv.classList.toggle("has-overflow", body.scrollHeight > body.clientHeight);
1000
+ });
1001
+
1002
// Render action buttons: get/create container, clear, append
1003
const responseText = String(content ?? "");
1004
const responseActionButtons = responseText.trim()