enh: <think> tag support

This commit is contained in:
Timothy Jaeryang Baek
2025-01-22 00:13:24 -08:00
parent 8d3c73aed5
commit c9dc7299c5
7 changed files with 116 additions and 54 deletions

View File

@@ -127,6 +127,50 @@ select {
-webkit-appearance: none;
}
@keyframes shimmer {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
.shimmer {
background: linear-gradient(90deg, #9a9b9e 25%, #2a2929 50%, #9a9b9e 75%);
background-size: 200% 100%;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: shimmer 4s linear infinite;
color: #818286; /* Fallback color */
}
:global(.dark) .shimmer {
background: linear-gradient(90deg, #818286 25%, #eae5e5 50%, #818286 75%);
background-size: 200% 100%;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: shimmer 4s linear infinite;
color: #a1a3a7; /* Darker fallback color for dark mode */
}
@keyframes smoothFadeIn {
0% {
opacity: 0;
transform: translateY(-10px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.status-description {
animation: smoothFadeIn 0.2s forwards;
}
.katex-mathml {
display: none;
}

View File

@@ -195,7 +195,11 @@
</ul>
{/if}
{:else if token.type === 'details'}
<Collapsible title={token.summary} className="w-fit space-y-1">
<Collapsible
title={token.summary}
isLoading={token?.isLoading ?? false}
className="w-fit space-y-1"
>
<div class=" mb-1.5" slot="content">
<svelte:self id={`${id}-${tokenIdx}-d`} tokens={marked.lexer(token.text)} />
</div>

View File

@@ -1,3 +1,3 @@
<div class=" self-center font-semibold mb-0.5 line-clamp-1 flex gap-1 items-center">
<div class=" self-center font-semibold line-clamp-1 flex gap-1 items-center">
<slot />
</div>

View File

@@ -1234,48 +1234,4 @@
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
@keyframes shimmer {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
.shimmer {
background: linear-gradient(90deg, #9a9b9e 25%, #2a2929 50%, #9a9b9e 75%);
background-size: 200% 100%;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: shimmer 4s linear infinite;
color: #818286; /* Fallback color */
}
:global(.dark) .shimmer {
background: linear-gradient(90deg, #818286 25%, #eae5e5 50%, #818286 75%);
background-size: 200% 100%;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: shimmer 4s linear infinite;
color: #a1a3a7; /* Darker fallback color for dark mode */
}
@keyframes smoothFadeIn {
0% {
opacity: 0;
transform: translateY(-10px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.status-description {
animation: smoothFadeIn 0.2s forwards;
}
</style>

View File

@@ -9,12 +9,14 @@
import ChevronUp from '../icons/ChevronUp.svelte';
import ChevronDown from '../icons/ChevronDown.svelte';
import Spinner from './Spinner.svelte';
export let open = false;
export let className = '';
export let buttonClassName =
'w-fit text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition';
export let title = null;
export let isLoading = false;
export let grow = false;
@@ -34,12 +36,23 @@
}
}}
>
<div class=" w-full font-medium flex items-center justify-between gap-2">
<div
class=" w-full font-medium flex items-center justify-between gap-2 {isLoading === true
? 'shimmer'
: ''}
"
>
{#if isLoading}
<div>
<Spinner className="size-4" />
</div>
{/if}
<div class="">
{title}
</div>
<div>
<div class="flex self-center translate-y-[1px]">
{#if open}
<ChevronUp strokeWidth="3.5" className="size-3.5" />
{:else}

View File

@@ -18,18 +18,26 @@ function findMatchingClosingTag(src, openTag, closeTag) {
function detailsTokenizer(src) {
const detailsRegex = /^<details>\n/;
const summaryRegex = /^<summary>(.*?)<\/summary>\n/;
const loadingRegex = /<loading\s*\/>/; // Detect <loading/>
if (detailsRegex.test(src)) {
const endIndex = findMatchingClosingTag(src, '<details>', '</details>');
if (endIndex === -1) return;
const fullMatch = src.slice(0, endIndex);
let content = fullMatch.slice(10, -10).trim(); // Remove <details> and </details>
let summary = '';
let isLoading = false;
const summaryMatch = summaryRegex.exec(content);
if (summaryMatch) {
summary = summaryMatch[1].trim();
// Detect and remove <loading/>
if (loadingRegex.test(summary)) {
isLoading = true;
summary = summary.replace(loadingRegex, '').trim(); // Remove <loading/> from summary
}
content = content.slice(summaryMatch[0].length).trim();
}
@@ -37,7 +45,8 @@ function detailsTokenizer(src) {
type: 'details',
raw: fullMatch,
summary: summary,
text: content
text: content,
isLoading: isLoading // Include loading property to indicate if <loading/> was present
};
}
}