Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/lib/styles/pages/_chat.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2996,17 +2996,36 @@
flex-wrap: wrap;
gap: 6px;
margin-top: 4px;
min-width: 0;
}


.mss-state-value {
flex: 1 1 calc(50% - 6px);
min-width: calc(50% - 6px);
min-width: 0;
max-width: 100%;
font-size: 14px;
padding: 4px 8px;
border-radius: 6px;
background-color: color-mix(in srgb, white 3%, transparent);
border: 1px solid color-mix(in srgb, white 5%, transparent);
cursor: pointer;
}


.mss-value {
overflow-wrap: anywhere;
word-break: break-word;
white-space: pre-wrap;
}


.mss-state-value-collapsed .mss-value {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
word-break: normal;
overflow-wrap: normal;
}


Expand Down
35 changes: 30 additions & 5 deletions src/lib/styles/pages/_conversation.scss
Original file line number Diff line number Diff line change
Expand Up @@ -266,27 +266,52 @@
* ========================================================================
* Replaces the legacy `.text-collapse` rule from
* src/lib/scss/custom/pages/_conversation.scss. The dialog text is
* collapsed to the first 5 lines by default; clicking the More button
* collapsed to the first 10 lines by default; clicking the More button
* removes this class so the content can flow to its natural height.
*
* Two clamping mechanisms are layered here for reliability:
* 1. `-webkit-line-clamp: 5` with `display: -webkit-box` gives the
* 1. `-webkit-line-clamp: 10` with `display: -webkit-box` gives the
* nicer "line truncation with ellipsis" effect on simple text.
* 2. `max-height: 10lh` is the hard fallback. The inner `<Markdown>`
* wraps its rendered HTML in a `.markdown-container` with
* `overflow-x: auto`, which establishes its own block formatting
* context — that BFC defeats line-clamp on the parent because text
* flow is sealed inside the child. The max-height cap clips
* reliably regardless of inner BFCs and is what actually keeps long
* code blocks / tables collapsed.
* code blocks / tables collapsed. A `::after` pseudo-element below
* paints a '...' in the bottom-right corner so the truncation is
* still signaled visually when line-clamp's native ellipsis is
* suppressed by the inner BFC.
*/
.text-collapse {
position: relative;
overflow: hidden;
max-height: 10lh;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 5;
line-clamp: 5;
-webkit-line-clamp: 10;
line-clamp: 10;
}

/* Ellipsis fallback for collapsed content whose inner BFC defeats
* -webkit-line-clamp (notably the Markdown container's overflow-x: auto
* around code blocks / tables). The native line-clamp ellipsis already
* handles plain text; this pseudo-element guarantees a '...' indicator
* is rendered even when line-clamp is sealed inside the child BFC.
*
* The gradient fades from transparent to the conversation page
* background (white) so the '...' sits cleanly over the last visible
* line without obscuring readable text. */
.text-collapse::after {
content: '...';
position: absolute;
right: 0;
bottom: 0;
padding: 0 0.25rem 0 1.5rem;
background: linear-gradient(to right, rgba(255, 255, 255, 0), rgb(255 255 255) 50%);
color: inherit;
font-weight: inherit;
pointer-events: none;
}


Expand Down
10 changes: 5 additions & 5 deletions src/routes/VerticalLayout/Sidebar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@
{:else if item.subMenu}
<li>
<!-- svelte-ignore a11y_invalid_attribute -->
<a href="javascript:void(0);" class="has-arrow clickable">
<a href="javascript:void(0);" class="has-arrow cursor-pointer">
<i class={item.icon}></i>
<span>{$_(item.label)}</span>
</a>
Expand All @@ -301,14 +301,14 @@
{#if subMenu.isChildItem}
<li>
<!-- svelte-ignore a11y_invalid_attribute -->
<a href="javascript:void(0);" class="has-arrow clickable">
<a href="javascript:void(0);" class="has-arrow cursor-pointer">
<span>{$_(subMenu.label)}</span>
</a>
<ul class="sub-menu mm-collapse">
{#each subMenu.childItems as childItem}
<li>
<!-- svelte-ignore a11y_invalid_attribute -->
<a href="javascript:void(0);" class="clickable" id={getCleanUrl(childItem.link)} onclick={() => goToPage(childItem.link)}>
<a href="javascript:void(0);" class="cursor-pointer" id={getCleanUrl(childItem.link)} onclick={() => goToPage(childItem.link)}>
{$_(childItem.label)}
</a>
</li>
Expand All @@ -318,7 +318,7 @@
{:else}
<li>
<!-- svelte-ignore a11y_invalid_attribute -->
<a href="javascript:void(0);" class="clickable" id={getCleanUrl(subMenu.link)} onclick={() => goToPage(subMenu.link)}>
<a href="javascript:void(0);" class="cursor-pointer" id={getCleanUrl(subMenu.link)} onclick={() => goToPage(subMenu.link)}>
{$_(subMenu.label)}
</a>
</li>
Expand All @@ -329,7 +329,7 @@
{:else}
<li>
<!-- svelte-ignore a11y_invalid_attribute -->
<a href="javascript:void(0);" class="clickable" id={getCleanUrl(item.link)} onclick={() => goToPage(item.link)}>
<a href="javascript:void(0);" class="cursor-pointer" id={getCleanUrl(item.link)} onclick={() => goToPage(item.link)}>
<i class={item.icon}></i>
<span>{$_(item.label)}</span>
</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@

const defaultValue = '-';

let collapsed = $state(true);

/** @param {KeyboardEvent} e @param {() => void} fn */
function handleKey(e, fn) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
fn();
}
}

/** @param {any} value */
function buildDataValue(value) {
return value || defaultValue;
Expand Down Expand Up @@ -46,21 +56,37 @@
</div>
</div>
<div class="mss-content">
<div class="mss-state-value">
<div
class="mss-state-value"
class:mss-state-value-collapsed={collapsed}
role="button"
tabindex="0"
title={collapsed ? 'Click to expand' : 'Click to collapse'}
onclick={() => (collapsed = !collapsed)}
onkeydown={(e) => handleKey(e, () => (collapsed = !collapsed))}
>
<div class="mss-value">
{beforeDataValue}
</div>
{#if !!beforeActiveRoundText}
{#if !!beforeActiveRoundText && !collapsed}
<div class="mss-active-rounds">
{`${beforeActiveRoundText}`}
</div>
{/if}
</div>
<div class="mss-state-value mss-state-value-warn">
<div
class="mss-state-value mss-state-value-warn"
class:mss-state-value-collapsed={collapsed}
role="button"
tabindex="0"
title={collapsed ? 'Click to expand' : 'Click to collapse'}
onclick={() => (collapsed = !collapsed)}
onkeydown={(e) => handleKey(e, () => (collapsed = !collapsed))}
>
<div class="mss-value">
{afterDataValue}
</div>
{#if !!afterActiveRoundText}
{#if !!afterActiveRoundText && !collapsed}
<div class="mss-active-rounds">
{`${afterActiveRoundText}`}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@
let { data } = $props();

let is_collapsed = $state(true);
let contentEl = $state();
let isOverflowing = $state(false);

const COLLAPSE_LINE_THRESHOLD = 10;
const unknownAgent = "Uknown";
const collapsedSources = [
ContentLogSource.UserInput,
ContentLogSource.Prompt,
ContentLogSource.AgentResponse,
ContentLogSource.FunctionCall,
Expand Down Expand Up @@ -48,12 +53,26 @@
e.preventDefault();
is_collapsed = !is_collapsed;
}

$effect(() => {
void data?.content;
if (!contentEl || !collapsedSources.includes(data.source)) {
isOverflowing = false;
return;
}
requestAnimationFrame(() => {
if (!contentEl) return;
const cs = getComputedStyle(contentEl);
let lineHeight = parseFloat(cs.lineHeight);
if (!Number.isFinite(lineHeight) || lineHeight <= 0) {
lineHeight = parseFloat(cs.fontSize) * 1.5 || 21;
}
isOverflowing = contentEl.scrollHeight > lineHeight * COLLAPSE_LINE_THRESHOLD + 1;
});
});
</script>

<!--
NOTE: id={`content-log-${data.message_id}`} is queried by chat-box.svelte's
autoScrollToTargetLog(); keep this id attribute as-is.
-->

<div class="cle-element" id={`content-log-${data.message_id}`}>
<div class="cle-meta">
<div>
Expand All @@ -77,11 +96,14 @@
</div>
</div>
<div class={`cle-content ${logDisplayStyle}`}>
<div class:cle-collapse={collapsedSources.includes(data.source) && !!is_collapsed}>
<div
bind:this={contentEl}
class:cle-collapse={collapsedSources.includes(data.source) && isOverflowing && !!is_collapsed}
>
<Markdown containerClasses={logTextStyle} text={data?.content} rawText={rawTextSources.includes(data.source)} />
</div>

{#if collapsedSources.includes(data.source)}
{#if collapsedSources.includes(data.source) && isOverflowing}
<button class="cle-toggle-btn" onclick={(e) => toggleText(e)}>
{`${is_collapsed ? 'More +' : 'Less -'}`}
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,41 @@
import RcJsInterpreter from '../../../chat/[agentId]/[conversationId]/rich-content/rc-js-interpreter.svelte';
import { RichType } from '$lib/helpers/enums';

const COLLAPSE_LINE_THRESHOLD = 10;

/** @type {{ dialog: import('$conversationTypes').ChatResponseModel }} */
let { dialog } = $props();

let is_collapsed = $state(true);
let contentEl = $state();
let isOverflowing = $state(false);

/** @param {any} e */
function toggleText(e) {
e.preventDefault();
is_collapsed = !is_collapsed;
}

$effect(() => {
void dialog?.rich_content?.message?.text;
void dialog?.text;
if (!contentEl) return;
requestAnimationFrame(() => {
if (!contentEl) return;
const cs = getComputedStyle(contentEl);
let lineHeight = parseFloat(cs.lineHeight);
if (!Number.isFinite(lineHeight) || lineHeight <= 0) {
lineHeight = parseFloat(cs.fontSize) * 1.5 || 21;
}
isOverflowing = contentEl.scrollHeight > lineHeight * COLLAPSE_LINE_THRESHOLD + 1;
});
});
</script>

<div
bind:this={contentEl}
class="text-sm font-medium leading-relaxed"
class:text-collapse={is_collapsed}
class:text-collapse={is_collapsed && isOverflowing}
>
{#if dialog?.rich_content?.message?.rich_type === RichType.ProgramCode
&& dialog?.rich_content?.message?.language === 'javascript'}
Expand All @@ -31,6 +51,7 @@
{/if}
</div>

{#if isOverflowing}
<button
type="button"
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"
Expand All @@ -39,4 +60,5 @@
<i class="mdi {is_collapsed ? 'mdi-chevron-down' : 'mdi-chevron-up'} text-sm leading-none"></i>
<span>{is_collapsed ? 'More' : 'Less'}</span>
</button>
{/if}

Loading