Skip to content

Commit c5621d0

Browse files
authored
Merge pull request #461 from iceljc/main
refine collapse styling
2 parents fcab638 + 6ec1dcf commit c5621d0

6 files changed

Lines changed: 136 additions & 22 deletions

File tree

src/lib/styles/pages/_chat.scss

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2996,17 +2996,36 @@
29962996
flex-wrap: wrap;
29972997
gap: 6px;
29982998
margin-top: 4px;
2999+
min-width: 0;
29993000
}
30003001

30013002

30023003
.mss-state-value {
30033004
flex: 1 1 calc(50% - 6px);
3004-
min-width: calc(50% - 6px);
3005+
min-width: 0;
3006+
max-width: 100%;
30053007
font-size: 14px;
30063008
padding: 4px 8px;
30073009
border-radius: 6px;
30083010
background-color: color-mix(in srgb, white 3%, transparent);
30093011
border: 1px solid color-mix(in srgb, white 5%, transparent);
3012+
cursor: pointer;
3013+
}
3014+
3015+
3016+
.mss-value {
3017+
overflow-wrap: anywhere;
3018+
word-break: break-word;
3019+
white-space: pre-wrap;
3020+
}
3021+
3022+
3023+
.mss-state-value-collapsed .mss-value {
3024+
white-space: nowrap;
3025+
overflow: hidden;
3026+
text-overflow: ellipsis;
3027+
word-break: normal;
3028+
overflow-wrap: normal;
30103029
}
30113030

30123031

src/lib/styles/pages/_conversation.scss

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -266,27 +266,52 @@
266266
* ========================================================================
267267
* Replaces the legacy `.text-collapse` rule from
268268
* src/lib/scss/custom/pages/_conversation.scss. The dialog text is
269-
* collapsed to the first 5 lines by default; clicking the More button
269+
* collapsed to the first 10 lines by default; clicking the More button
270270
* removes this class so the content can flow to its natural height.
271271
*
272272
* Two clamping mechanisms are layered here for reliability:
273-
* 1. `-webkit-line-clamp: 5` with `display: -webkit-box` gives the
273+
* 1. `-webkit-line-clamp: 10` with `display: -webkit-box` gives the
274274
* nicer "line truncation with ellipsis" effect on simple text.
275275
* 2. `max-height: 10lh` is the hard fallback. The inner `<Markdown>`
276276
* wraps its rendered HTML in a `.markdown-container` with
277277
* `overflow-x: auto`, which establishes its own block formatting
278278
* context — that BFC defeats line-clamp on the parent because text
279279
* flow is sealed inside the child. The max-height cap clips
280280
* reliably regardless of inner BFCs and is what actually keeps long
281-
* code blocks / tables collapsed.
281+
* code blocks / tables collapsed. A `::after` pseudo-element below
282+
* paints a '...' in the bottom-right corner so the truncation is
283+
* still signaled visually when line-clamp's native ellipsis is
284+
* suppressed by the inner BFC.
282285
*/
283286
.text-collapse {
287+
position: relative;
284288
overflow: hidden;
285289
max-height: 10lh;
286290
display: -webkit-box;
287291
-webkit-box-orient: vertical;
288-
-webkit-line-clamp: 5;
289-
line-clamp: 5;
292+
-webkit-line-clamp: 10;
293+
line-clamp: 10;
294+
}
295+
296+
/* Ellipsis fallback for collapsed content whose inner BFC defeats
297+
* -webkit-line-clamp (notably the Markdown container's overflow-x: auto
298+
* around code blocks / tables). The native line-clamp ellipsis already
299+
* handles plain text; this pseudo-element guarantees a '...' indicator
300+
* is rendered even when line-clamp is sealed inside the child BFC.
301+
*
302+
* The gradient fades from transparent to the conversation page
303+
* background (white) so the '...' sits cleanly over the last visible
304+
* line without obscuring readable text. */
305+
.text-collapse::after {
306+
content: '...';
307+
position: absolute;
308+
right: 0;
309+
bottom: 0;
310+
padding: 0 0.25rem 0 1.5rem;
311+
background: linear-gradient(to right, rgba(255, 255, 255, 0), rgb(255 255 255) 50%);
312+
color: inherit;
313+
font-weight: inherit;
314+
pointer-events: none;
290315
}
291316

292317

src/routes/VerticalLayout/Sidebar.svelte

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@
292292
{:else if item.subMenu}
293293
<li>
294294
<!-- svelte-ignore a11y_invalid_attribute -->
295-
<a href="javascript:void(0);" class="has-arrow clickable">
295+
<a href="javascript:void(0);" class="has-arrow cursor-pointer">
296296
<i class={item.icon}></i>
297297
<span>{$_(item.label)}</span>
298298
</a>
@@ -301,14 +301,14 @@
301301
{#if subMenu.isChildItem}
302302
<li>
303303
<!-- svelte-ignore a11y_invalid_attribute -->
304-
<a href="javascript:void(0);" class="has-arrow clickable">
304+
<a href="javascript:void(0);" class="has-arrow cursor-pointer">
305305
<span>{$_(subMenu.label)}</span>
306306
</a>
307307
<ul class="sub-menu mm-collapse">
308308
{#each subMenu.childItems as childItem}
309309
<li>
310310
<!-- svelte-ignore a11y_invalid_attribute -->
311-
<a href="javascript:void(0);" class="clickable" id={getCleanUrl(childItem.link)} onclick={() => goToPage(childItem.link)}>
311+
<a href="javascript:void(0);" class="cursor-pointer" id={getCleanUrl(childItem.link)} onclick={() => goToPage(childItem.link)}>
312312
{$_(childItem.label)}
313313
</a>
314314
</li>
@@ -318,7 +318,7 @@
318318
{:else}
319319
<li>
320320
<!-- svelte-ignore a11y_invalid_attribute -->
321-
<a href="javascript:void(0);" class="clickable" id={getCleanUrl(subMenu.link)} onclick={() => goToPage(subMenu.link)}>
321+
<a href="javascript:void(0);" class="cursor-pointer" id={getCleanUrl(subMenu.link)} onclick={() => goToPage(subMenu.link)}>
322322
{$_(subMenu.label)}
323323
</a>
324324
</li>
@@ -329,7 +329,7 @@
329329
{:else}
330330
<li>
331331
<!-- svelte-ignore a11y_invalid_attribute -->
332-
<a href="javascript:void(0);" class="clickable" id={getCleanUrl(item.link)} onclick={() => goToPage(item.link)}>
332+
<a href="javascript:void(0);" class="cursor-pointer" id={getCleanUrl(item.link)} onclick={() => goToPage(item.link)}>
333333
<i class={item.icon}></i>
334334
<span>{$_(item.label)}</span>
335335
</a>

src/routes/chat/[agentId]/[conversationId]/instant-log/message-state-log-element.svelte

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@
44
55
const defaultValue = '-';
66
7+
let collapsed = $state(true);
8+
9+
/** @param {KeyboardEvent} e @param {() => void} fn */
10+
function handleKey(e, fn) {
11+
if (e.key === 'Enter' || e.key === ' ') {
12+
e.preventDefault();
13+
fn();
14+
}
15+
}
16+
717
/** @param {any} value */
818
function buildDataValue(value) {
919
return value || defaultValue;
@@ -46,21 +56,37 @@
4656
</div>
4757
</div>
4858
<div class="mss-content">
49-
<div class="mss-state-value">
59+
<div
60+
class="mss-state-value"
61+
class:mss-state-value-collapsed={collapsed}
62+
role="button"
63+
tabindex="0"
64+
title={collapsed ? 'Click to expand' : 'Click to collapse'}
65+
onclick={() => (collapsed = !collapsed)}
66+
onkeydown={(e) => handleKey(e, () => (collapsed = !collapsed))}
67+
>
5068
<div class="mss-value">
5169
{beforeDataValue}
5270
</div>
53-
{#if !!beforeActiveRoundText}
71+
{#if !!beforeActiveRoundText && !collapsed}
5472
<div class="mss-active-rounds">
5573
{`${beforeActiveRoundText}`}
5674
</div>
5775
{/if}
5876
</div>
59-
<div class="mss-state-value mss-state-value-warn">
77+
<div
78+
class="mss-state-value mss-state-value-warn"
79+
class:mss-state-value-collapsed={collapsed}
80+
role="button"
81+
tabindex="0"
82+
title={collapsed ? 'Click to expand' : 'Click to collapse'}
83+
onclick={() => (collapsed = !collapsed)}
84+
onkeydown={(e) => handleKey(e, () => (collapsed = !collapsed))}
85+
>
6086
<div class="mss-value">
6187
{afterDataValue}
6288
</div>
63-
{#if !!afterActiveRoundText}
89+
{#if !!afterActiveRoundText && !collapsed}
6490
<div class="mss-active-rounds">
6591
{`${afterActiveRoundText}`}
6692
</div>

src/routes/chat/[agentId]/[conversationId]/persist-log/content-log-element.svelte

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@
88
let { data } = $props();
99
1010
let is_collapsed = $state(true);
11+
let contentEl = $state();
12+
let isOverflowing = $state(false);
13+
14+
const COLLAPSE_LINE_THRESHOLD = 10;
1115
const unknownAgent = "Uknown";
1216
const collapsedSources = [
17+
ContentLogSource.UserInput,
1318
ContentLogSource.Prompt,
1419
ContentLogSource.AgentResponse,
1520
ContentLogSource.FunctionCall,
@@ -48,12 +53,26 @@
4853
e.preventDefault();
4954
is_collapsed = !is_collapsed;
5055
}
56+
57+
$effect(() => {
58+
void data?.content;
59+
if (!contentEl || !collapsedSources.includes(data.source)) {
60+
isOverflowing = false;
61+
return;
62+
}
63+
requestAnimationFrame(() => {
64+
if (!contentEl) return;
65+
const cs = getComputedStyle(contentEl);
66+
let lineHeight = parseFloat(cs.lineHeight);
67+
if (!Number.isFinite(lineHeight) || lineHeight <= 0) {
68+
lineHeight = parseFloat(cs.fontSize) * 1.5 || 21;
69+
}
70+
isOverflowing = contentEl.scrollHeight > lineHeight * COLLAPSE_LINE_THRESHOLD + 1;
71+
});
72+
});
5173
</script>
5274
53-
<!--
54-
NOTE: id={`content-log-${data.message_id}`} is queried by chat-box.svelte's
55-
autoScrollToTargetLog(); keep this id attribute as-is.
56-
-->
75+
5776
<div class="cle-element" id={`content-log-${data.message_id}`}>
5877
<div class="cle-meta">
5978
<div>
@@ -77,11 +96,14 @@
7796
</div>
7897
</div>
7998
<div class={`cle-content ${logDisplayStyle}`}>
80-
<div class:cle-collapse={collapsedSources.includes(data.source) && !!is_collapsed}>
99+
<div
100+
bind:this={contentEl}
101+
class:cle-collapse={collapsedSources.includes(data.source) && isOverflowing && !!is_collapsed}
102+
>
81103
<Markdown containerClasses={logTextStyle} text={data?.content} rawText={rawTextSources.includes(data.source)} />
82104
</div>
83105
84-
{#if collapsedSources.includes(data.source)}
106+
{#if collapsedSources.includes(data.source) && isOverflowing}
85107
<button class="cle-toggle-btn" onclick={(e) => toggleText(e)}>
86108
{`${is_collapsed ? 'More +' : 'Less -'}`}
87109
</button>

src/routes/page/conversation/[conversationId]/conv-dialog-element.svelte

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,41 @@
33
import RcJsInterpreter from '../../../chat/[agentId]/[conversationId]/rich-content/rc-js-interpreter.svelte';
44
import { RichType } from '$lib/helpers/enums';
55
6+
const COLLAPSE_LINE_THRESHOLD = 10;
7+
68
/** @type {{ dialog: import('$conversationTypes').ChatResponseModel }} */
79
let { dialog } = $props();
810
911
let is_collapsed = $state(true);
12+
let contentEl = $state();
13+
let isOverflowing = $state(false);
1014
1115
/** @param {any} e */
1216
function toggleText(e) {
1317
e.preventDefault();
1418
is_collapsed = !is_collapsed;
1519
}
20+
21+
$effect(() => {
22+
void dialog?.rich_content?.message?.text;
23+
void dialog?.text;
24+
if (!contentEl) return;
25+
requestAnimationFrame(() => {
26+
if (!contentEl) return;
27+
const cs = getComputedStyle(contentEl);
28+
let lineHeight = parseFloat(cs.lineHeight);
29+
if (!Number.isFinite(lineHeight) || lineHeight <= 0) {
30+
lineHeight = parseFloat(cs.fontSize) * 1.5 || 21;
31+
}
32+
isOverflowing = contentEl.scrollHeight > lineHeight * COLLAPSE_LINE_THRESHOLD + 1;
33+
});
34+
});
1635
</script>
1736
1837
<div
38+
bind:this={contentEl}
1939
class="text-sm font-medium leading-relaxed"
20-
class:text-collapse={is_collapsed}
40+
class:text-collapse={is_collapsed && isOverflowing}
2141
>
2242
{#if dialog?.rich_content?.message?.rich_type === RichType.ProgramCode
2343
&& dialog?.rich_content?.message?.language === 'javascript'}
@@ -31,6 +51,7 @@
3151
{/if}
3252
</div>
3353
54+
{#if isOverflowing}
3455
<button
3556
type="button"
3657
class="mt-1 inline-flex items-center gap-1 text-xs font-medium text-primary transition-colors hover:text-primary-hover hover:underline cursor-pointer"
@@ -39,4 +60,5 @@
3960
<i class="mdi {is_collapsed ? 'mdi-chevron-down' : 'mdi-chevron-up'} text-sm leading-none"></i>
4061
<span>{is_collapsed ? 'More' : 'Less'}</span>
4162
</button>
63+
{/if}
4264

0 commit comments

Comments
 (0)