Polish web chat presentation
This commit is contained in:
@@ -685,6 +685,18 @@ export const IconChecklistOutline14 = ({ size = 14, className }: IconProps) => (
|
||||
</svg>
|
||||
)
|
||||
|
||||
/** Text document glyph for context-disclosure rows. */
|
||||
export const IconTextOutline14 = ({ size = 14, className }: IconProps) => (
|
||||
<svg width={size} height={size} className={className} viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M4.083 1.167h4.55c.31 0 .606.123.825.342l2.7 2.7c.219.219.342.516.342.825v6.05c0 .966-.784 1.75-1.75 1.75H4.083a1.75 1.75 0 0 1-1.75-1.75V2.917c0-.967.784-1.75 1.75-1.75Zm4.084 1.166H4.083a.583.583 0 0 0-.583.584v8.166c0 .323.261.584.583.584h6.667a.583.583 0 0 0 .583-.584V5.25H9.917a1.75 1.75 0 0 1-1.75-1.75V2.333Zm1.166.825 1.175 1.175h-.591a.583.583 0 0 1-.584-.583v-.592ZM5.25 7h4.667v1.167H5.25V7Zm0 2.333h4.667V10.5H5.25V9.333Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
/** ic_ds_List_Pen_outline_16 */
|
||||
export const IconListPenOutline16 = ({ size = 16, className }: IconProps) => (
|
||||
<svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
@@ -24,6 +24,7 @@ export { TerminalBlock, DEFAULT_TERMINAL_MAX_LINES } from './TerminalBlock.tsx'
|
||||
export type { TerminalBlockProps } from './TerminalBlock.tsx'
|
||||
export { CodeBlock } from './markdown/CodeBlock.tsx'
|
||||
export { JsonBlock } from './markdown/JsonBlock.tsx'
|
||||
export type { JsonBlockProps } from './markdown/JsonBlock.tsx'
|
||||
export { MarkdownText } from './markdown/MarkdownText.tsx'
|
||||
export { MessageText } from './markdown/MessageText.tsx'
|
||||
export { extractMarkdownPlainText } from './markdown/plain-text.ts'
|
||||
|
||||
@@ -3,22 +3,54 @@
|
||||
}
|
||||
|
||||
.toggle {
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 24px;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
color: var(--dsw-alias-label-secondary);
|
||||
padding: 2px 6px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.toggle:hover {
|
||||
background: var(--dsw-alias-interactive-bg-hover);
|
||||
.leading {
|
||||
position: relative;
|
||||
flex: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 6px;
|
||||
color: var(--dsw-alias-label-tertiary);
|
||||
}
|
||||
|
||||
.iconIdle {
|
||||
display: inline-flex;
|
||||
opacity: 1;
|
||||
transition: opacity 100ms ease;
|
||||
}
|
||||
|
||||
.chevronHover {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
margin: auto;
|
||||
opacity: 0;
|
||||
transition: opacity 100ms ease;
|
||||
}
|
||||
|
||||
.toggle:hover .iconIdle {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.toggle:hover .chevronHover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.body {
|
||||
margin: 4px 0 0;
|
||||
margin: 4px 0 0 22px;
|
||||
padding: 8px;
|
||||
max-height: 200px;
|
||||
overflow: auto;
|
||||
|
||||
@@ -1,15 +1,28 @@
|
||||
// JsonBlock: collapsible JSON block (conversation side; independent from the RPC panel's PayloadJson to avoid cross-panel coupling).
|
||||
// JsonBlock: accessible JSON disclosure row (conversation side; independent
|
||||
// from the RPC panel's PayloadJson to avoid cross-panel coupling).
|
||||
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useMemo, useState, type ReactNode } from 'react'
|
||||
import { IconChevronDownOutline14, IconChevronRightOutline14 } from '../icons/index.tsx'
|
||||
import css from './JsonBlock.module.css'
|
||||
|
||||
const MAX_CHARS = 20_000
|
||||
|
||||
export function JsonBlock({ label, payload, defaultOpen = false }: {
|
||||
/** Props for the compact JSON disclosure used in conversation content. */
|
||||
export interface JsonBlockProps {
|
||||
label: string
|
||||
payload: unknown
|
||||
defaultOpen?: boolean
|
||||
}) {
|
||||
/** Semantic glyph shown while collapsed; hover previews the disclosure chevron. */
|
||||
collapsedIcon?: ReactNode
|
||||
}
|
||||
|
||||
/** Render a bounded, pretty-printed JSON disclosure. */
|
||||
export function JsonBlock({
|
||||
label,
|
||||
payload,
|
||||
defaultOpen = false,
|
||||
collapsedIcon,
|
||||
}: JsonBlockProps) {
|
||||
const [open, setOpen] = useState(defaultOpen)
|
||||
const body = useMemo(() => {
|
||||
if (!open) return ''
|
||||
@@ -23,10 +36,26 @@ export function JsonBlock({ label, payload, defaultOpen = false }: {
|
||||
}
|
||||
return s.length > MAX_CHARS ? `${s.slice(0, MAX_CHARS)}\n… 已截断,共 ${s.length} 字符` : s
|
||||
}, [open, payload])
|
||||
const leading = open
|
||||
? <IconChevronDownOutline14 />
|
||||
: collapsedIcon === undefined
|
||||
? <IconChevronRightOutline14 />
|
||||
: (
|
||||
<>
|
||||
<span className={css.iconIdle}>{collapsedIcon}</span>
|
||||
<IconChevronRightOutline14 className={css.chevronHover} />
|
||||
</>
|
||||
)
|
||||
return (
|
||||
<div className={css.root}>
|
||||
<button type="button" className={css.toggle} onClick={() => { setOpen(v => !v) }}>
|
||||
{open ? '▾' : '▸'} {label}
|
||||
<button
|
||||
type="button"
|
||||
className={css.toggle}
|
||||
aria-expanded={open}
|
||||
onClick={() => { setOpen(v => !v) }}
|
||||
>
|
||||
<span className={css.leading} aria-hidden="true">{leading}</span>
|
||||
<span>{label}</span>
|
||||
</button>
|
||||
{open && <pre className={css.body}>{body}</pre>}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user