|
74 | 74 | let urlNotTrailing = $derived(page.url.pathname.replace(/\/$/, "")); |
75 | 75 | let downloadLink = $derived(urlNotTrailing + `/message/${message.id}/prompt`); |
76 | 76 |
|
| 77 | + // Zero-config reasoning autodetection: detect <think> blocks in content |
| 78 | + const THINK_BLOCK_REGEX = /(<think>[\s\S]*?(?:<\/think>|$))/g; |
| 79 | + let hasServerReasoning = $derived( |
| 80 | + reasoningUpdates && |
| 81 | + reasoningUpdates.length > 0 && |
| 82 | + !!message.reasoning && |
| 83 | + message.reasoning.trim().length > 0 |
| 84 | + ); |
| 85 | + let hasClientThink = $derived(!hasServerReasoning && /<think>/.test(message.content)); |
| 86 | +
|
77 | 87 | $effect(() => { |
78 | 88 | if (isCopied) { |
79 | 89 | setTimeout(() => { |
|
119 | 129 | </div> |
120 | 130 | {/if} |
121 | 131 |
|
122 | | - {#if reasoningUpdates && reasoningUpdates.length > 0 && message.reasoning && message.reasoning.trim().length > 0} |
| 132 | + {#if hasServerReasoning} |
123 | 133 | {@const summaries = reasoningUpdates |
124 | 134 | .filter((u) => u.subtype === MessageReasoningUpdateType.Status) |
125 | 135 | .map((u) => u.status)} |
|
136 | 146 | <IconLoading classNames="loading inline ml-2 first:ml-0" /> |
137 | 147 | {/if} |
138 | 148 |
|
139 | | - <div |
140 | | - class="prose max-w-none dark:prose-invert max-sm:prose-sm prose-headings:font-semibold prose-h1:text-lg prose-h2:text-base prose-h3:text-base prose-pre:bg-gray-800 dark:prose-pre:bg-gray-900" |
141 | | - > |
142 | | - <MarkdownRenderer content={message.content} /> |
143 | | - </div> |
| 149 | + {#if hasClientThink} |
| 150 | + {#each message.content.split(THINK_BLOCK_REGEX) as part, i} |
| 151 | + {#if part && part.startsWith("<think>")} |
| 152 | + {@const isClosed = part.endsWith("</think>")} |
| 153 | + {@const thinkContent = part.slice(7, isClosed ? -8 : undefined)} |
| 154 | + {@const summary = |
| 155 | + isClosed |
| 156 | + ? (thinkContent.trim().split(/\n+/)[0] || "Reasoning") |
| 157 | + : "Thinking..."} |
| 158 | + |
| 159 | + <OpenReasoningResults |
| 160 | + summary={summary} |
| 161 | + content={thinkContent} |
| 162 | + loading={isLast && loading && !isClosed} |
| 163 | + /> |
| 164 | + {:else if part && part.trim().length > 0} |
| 165 | + <div |
| 166 | + class="prose max-w-none dark:prose-invert max-sm:prose-sm prose-headings:font-semibold prose-h1:text-lg prose-h2:text-base prose-h3:text-base prose-pre:bg-gray-800 dark:prose-pre:bg-gray-900" |
| 167 | + > |
| 168 | + <MarkdownRenderer content={part} /> |
| 169 | + </div> |
| 170 | + {/if} |
| 171 | + {/each} |
| 172 | + {:else} |
| 173 | + <div |
| 174 | + class="prose max-w-none dark:prose-invert max-sm:prose-sm prose-headings:font-semibold prose-h1:text-lg prose-h2:text-base prose-h3:text-base prose-pre:bg-gray-800 dark:prose-pre:bg-gray-900" |
| 175 | + > |
| 176 | + <MarkdownRenderer content={message.content} /> |
| 177 | + </div> |
| 178 | + {/if} |
144 | 179 | </div> |
145 | 180 | </div> |
146 | 181 |
|
|
0 commit comments