Merge pull request #73054 from NousResearch/bb/codeblock-chromeless
Chromeless markdown code blocks
This commit is contained in:
commit
84858d76ba
|
|
@ -4,15 +4,16 @@ import { Codicon, type CodiconProps } from '@/components/ui/codicon'
|
|||
import { cn } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
* Rounded-card shell for fenced code (and any equivalent: diffs, raw payloads,
|
||||
* etc.) sized for the conversation column. Mirrors the expanded tool-row
|
||||
* pattern so code blocks read as the same family of artifact.
|
||||
* Rounded surface for fenced code (and any equivalent: diffs, raw payloads,
|
||||
* etc.) sized for the conversation column. Background only — no border, no
|
||||
* header, no language label — so a code block reads as a tinted slab of the
|
||||
* reply rather than an attached artifact.
|
||||
*/
|
||||
function CodeCard({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'min-w-0 max-w-full overflow-hidden rounded-[0.625rem] border border-border text-[length:var(--conversation-tool-font-size)] text-muted-foreground',
|
||||
'group/code relative min-w-0 max-w-full overflow-hidden rounded-[0.625rem] bg-(--ui-bg-editor) [--expandable-fade-from:var(--ui-bg-editor)] text-[length:var(--conversation-tool-font-size)] text-muted-foreground',
|
||||
className
|
||||
)}
|
||||
data-slot="code-card"
|
||||
|
|
@ -21,31 +22,6 @@ function CodeCard({ className, ...props }: React.ComponentProps<'div'>) {
|
|||
)
|
||||
}
|
||||
|
||||
function CodeCardHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
className={cn('flex items-center justify-between gap-2 border-b border-border px-2 py-1.5', className)}
|
||||
data-slot="code-card-header"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CodeCardTitle({ className, children, ...props }: React.ComponentProps<'span'>) {
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
'flex min-w-0 items-center gap-1.5 truncate text-[length:var(--conversation-tool-font-size)] font-medium leading-(--conversation-line-height) text-foreground/80',
|
||||
className
|
||||
)}
|
||||
data-slot="code-card-title"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
function CodeCardIcon({ className, ...props }: CodiconProps) {
|
||||
return (
|
||||
<Codicon
|
||||
|
|
@ -56,12 +32,6 @@ function CodeCardIcon({ className, ...props }: CodiconProps) {
|
|||
)
|
||||
}
|
||||
|
||||
function CodeCardSubtitle({ className, ...props }: React.ComponentProps<'span'>) {
|
||||
return (
|
||||
<span className={cn('font-normal text-muted-foreground', className)} data-slot="code-card-subtitle" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function CodeCardBody({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
|
|
@ -75,4 +45,4 @@ function CodeCardBody({ className, ...props }: React.ComponentProps<'div'>) {
|
|||
)
|
||||
}
|
||||
|
||||
export { CodeCard, CodeCardBody, CodeCardHeader, CodeCardIcon, CodeCardSubtitle, CodeCardTitle }
|
||||
export { CodeCard, CodeCardBody, CodeCardIcon }
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export function ExpandableBlock({ children, className }: ExpandableBlockProps) {
|
|||
// both sideways scrolling and text selection. Keep the fade
|
||||
// `pointer-events-none` and pin the only clickable target — a compact
|
||||
// toggle — to the right edge, clear of the draggable scrollbar track.
|
||||
<div className="pointer-events-none absolute inset-x-0 bottom-0 flex h-7 justify-end bg-linear-to-t from-(--ui-chat-surface-background) to-transparent">
|
||||
<div className="pointer-events-none absolute inset-x-0 bottom-0 flex h-7 justify-end bg-linear-to-t from-[var(--expandable-fade-from,var(--ui-chat-surface-background))] to-transparent">
|
||||
<button
|
||||
aria-expanded={expanded}
|
||||
aria-label={expanded ? 'Collapse' : 'Expand'}
|
||||
|
|
|
|||
|
|
@ -4,24 +4,18 @@ import type { SyntaxHighlighterProps } from '@assistant-ui/react-streamdown'
|
|||
import { type ComponentProps, type FC, lazy, Suspense, useMemo } from 'react'
|
||||
import type ShikiHighlighter from 'react-shiki'
|
||||
|
||||
import {
|
||||
CodeCard,
|
||||
CodeCardBody,
|
||||
CodeCardHeader,
|
||||
CodeCardIcon,
|
||||
CodeCardSubtitle,
|
||||
CodeCardTitle
|
||||
} from '@/components/chat/code-card'
|
||||
import { CodeCard, CodeCardBody } from '@/components/chat/code-card'
|
||||
import { ExpandableBlock } from '@/components/chat/expandable-block'
|
||||
import { CopyButton } from '@/components/ui/copy-button'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { codiconForLanguage, isLikelyProseCodeBlock, sanitizeLanguageTag } from '@/lib/markdown-code'
|
||||
import { isLikelyProseCodeBlock } from '@/lib/markdown-code'
|
||||
|
||||
/**
|
||||
* Streamdown's code adapter renders header + body as inline siblings, so we
|
||||
* own the wrapping `<CodeCard>` here and neutralize the upstream
|
||||
* `data-streamdown="code-block"` chrome from styles.css. Anything that wants
|
||||
* a card-shaped code surface should compose `CodeCard*` directly.
|
||||
* `data-streamdown="code-block"` chrome from styles.css. The card is
|
||||
* background-only — no header row, no language label — so a fence reads as a
|
||||
* tinted slab of the reply; copy is a hover-reveal control in the corner.
|
||||
*
|
||||
* `react-shiki` full bundle so all `bundledLanguages` work; theme switches
|
||||
* follow the document `color-scheme` via `defaultColor="light-dark()"`.
|
||||
|
|
@ -148,28 +142,19 @@ export const SyntaxHighlighter: FC<HermesSyntaxHighlighterProps> = ({
|
|||
return <div className="aui-prose-fence whitespace-pre-wrap wrap-anywhere text-foreground">{trimmed}</div>
|
||||
}
|
||||
|
||||
const cleanLanguage = sanitizeLanguageTag(language || '')
|
||||
const label = cleanLanguage && cleanLanguage !== 'unknown' ? cleanLanguage : ''
|
||||
const plain = defer || exceedsHighlightBudget(trimmed)
|
||||
|
||||
return (
|
||||
<CodeCard data-streaming={defer ? 'true' : undefined}>
|
||||
<CodeCardHeader>
|
||||
<CodeCardTitle>
|
||||
<CodeCardIcon name={codiconForLanguage(label)} />
|
||||
{t.assistant.tool.code}
|
||||
{label && <CodeCardSubtitle> · {label}</CodeCardSubtitle>}
|
||||
</CodeCardTitle>
|
||||
<CopyButton
|
||||
appearance="inline"
|
||||
className="-my-1 -mr-1 h-5 px-1 opacity-55 hover:opacity-100"
|
||||
iconClassName="size-2.5"
|
||||
label={t.assistant.tool.copyCode}
|
||||
showLabel={false}
|
||||
text={trimmed}
|
||||
/>
|
||||
</CodeCardHeader>
|
||||
<CodeCardBody>
|
||||
<CopyButton
|
||||
appearance="inline"
|
||||
className="absolute right-1.5 top-1.5 z-10 h-5 gap-0 rounded-md px-1 opacity-0 transition-opacity group-hover/code:opacity-100 focus-visible:opacity-100"
|
||||
iconClassName="size-2.5"
|
||||
label={t.assistant.tool.copyCode}
|
||||
showLabel={false}
|
||||
text={trimmed}
|
||||
/>
|
||||
<CodeCardBody className="[&_pre]:px-3 [&_pre]:py-2.5">
|
||||
<ExpandableBlock>
|
||||
<Pre className="aui-shiki m-0 overflow-hidden bg-transparent p-0">
|
||||
{plain ? (
|
||||
|
|
|
|||
|
|
@ -2326,7 +2326,6 @@ export const ar = defineLocale({
|
|||
continueLabel: 'متابعة'
|
||||
},
|
||||
tool: {
|
||||
code: 'الكود',
|
||||
copyCode: 'نسخ الكود',
|
||||
renderingImage: 'جار عرض الصورة...',
|
||||
copyOutput: 'نسخ الإخراج',
|
||||
|
|
|
|||
|
|
@ -2730,7 +2730,6 @@ export const en: Translations = {
|
|||
lateAnswerHint: 'This prompt is no longer waiting. Pick an option to draft it as a follow-up message.'
|
||||
},
|
||||
tool: {
|
||||
code: 'Code',
|
||||
copyCode: 'Copy code',
|
||||
renderingImage: 'Rendering image',
|
||||
copyOutput: 'Copy output',
|
||||
|
|
|
|||
|
|
@ -2574,7 +2574,6 @@ export const ja = defineLocale({
|
|||
lateAnswerHint: 'この質問はもう回答を待っていません。選択肢を選ぶとフォローアップメッセージとして下書きされます。'
|
||||
},
|
||||
tool: {
|
||||
code: 'コード',
|
||||
copyCode: 'コードをコピー',
|
||||
renderingImage: '画像をレンダリング中',
|
||||
copyOutput: '出力をコピー',
|
||||
|
|
|
|||
|
|
@ -2326,7 +2326,6 @@ export interface Translations {
|
|||
lateAnswerHint: string
|
||||
}
|
||||
tool: {
|
||||
code: string
|
||||
copyCode: string
|
||||
renderingImage: string
|
||||
copyOutput: string
|
||||
|
|
|
|||
|
|
@ -2495,7 +2495,6 @@ export const zhHant = defineLocale({
|
|||
lateAnswerHint: '此問題已不再等待回答。選擇一個選項會將其起草為後續訊息。'
|
||||
},
|
||||
tool: {
|
||||
code: '程式碼',
|
||||
copyCode: '複製程式碼',
|
||||
renderingImage: '正在渲染圖片',
|
||||
copyOutput: '複製輸出',
|
||||
|
|
|
|||
|
|
@ -2900,7 +2900,6 @@ export const zh: Translations = {
|
|||
lateAnswerHint: '此问题已不再等待回答。选择一个选项会将其起草为后续消息。'
|
||||
},
|
||||
tool: {
|
||||
code: '代码',
|
||||
copyCode: '复制代码',
|
||||
renderingImage: '正在渲染图片',
|
||||
copyOutput: '复制输出',
|
||||
|
|
|
|||
|
|
@ -1356,7 +1356,6 @@ text-* variant utilities. */ .btn-arc {
|
|||
margin-block: var(--paragraph-gap) 0;
|
||||
position: relative;
|
||||
transition:
|
||||
border-color 180ms ease-out,
|
||||
box-shadow 180ms ease-out,
|
||||
background-color 180ms ease-out;
|
||||
}
|
||||
|
|
@ -1365,7 +1364,6 @@ text-* variant utilities. */ .btn-arc {
|
|||
animation:
|
||||
code-card-stream-enter 180ms cubic-bezier(0.16, 1, 0.3, 1) both,
|
||||
code-card-stream-glow 1.8s ease-in-out 180ms infinite alternate;
|
||||
border-color: color-mix(in srgb, var(--dt-ring) 24%, var(--ui-stroke-tertiary));
|
||||
box-shadow:
|
||||
0 0 0 0.0625rem color-mix(in srgb, var(--dt-ring) 10%, transparent),
|
||||
0 0.625rem 1.75rem color-mix(in srgb, var(--dt-ring) 8%, transparent);
|
||||
|
|
@ -1678,14 +1676,12 @@ text-* variant utilities. */ .btn-arc {
|
|||
|
||||
@keyframes code-card-stream-glow {
|
||||
from {
|
||||
border-color: color-mix(in srgb, var(--dt-ring) 18%, var(--ui-stroke-tertiary));
|
||||
box-shadow:
|
||||
0 0 0 0.0625rem color-mix(in srgb, var(--dt-ring) 6%, transparent),
|
||||
0 0.5rem 1.5rem color-mix(in srgb, var(--dt-ring) 5%, transparent);
|
||||
}
|
||||
|
||||
to {
|
||||
border-color: color-mix(in srgb, var(--dt-ring) 32%, var(--ui-stroke-tertiary));
|
||||
box-shadow:
|
||||
0 0 0 0.0625rem color-mix(in srgb, var(--dt-ring) 12%, transparent),
|
||||
0 0.75rem 2rem color-mix(in srgb, var(--dt-ring) 10%, transparent);
|
||||
|
|
|
|||
Loading…
Reference in New Issue