✨ feat(vscode): 定义卡片底部带上那块问过 AI 的问答,点问题展开回答
This commit is contained in:
1 parent
fdf7a35b18
commit
b99659ea68
10 files changed
+336
-46
No files matched your search
@@ -82,8 +82,11 @@ function Thumbs({ urls, pending, onOpen, onRemove }: { urls: string[], pending?:
|
||||
)
|
||||
}
|
||||
|
||||
/** 看大图:盖住整个预览,点哪儿或按 Esc 关上;打开时焦点在「关闭」上,关上后回到点开它的地方。 */
|
||||
function ImagePreview({ url, onClose }: { url: string, onClose: () => void }): ReactElement {
|
||||
/**
|
||||
* 看大图:盖住整个预览,点哪儿或按 Esc 关上;打开时焦点在「关闭」上,关上后回到点开它的地方。
|
||||
* 平时 portal 到 body;`inPlace` 时就画在原处(定义卡片浮窗里:指针在大图上也算在浮窗里,浮窗不收)。
|
||||
*/
|
||||
function ImagePreview({ url, inPlace = false, onClose }: { url: string, inPlace?: boolean, onClose: () => void }): ReactElement {
|
||||
const close = useRef<HTMLButtonElement>(null)
|
||||
useEffect(() => {
|
||||
const opener = document.activeElement
|
||||
@@ -101,15 +104,15 @@ function ImagePreview({ url, onClose }: { url: string, onClose: () => void }): R
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
return createPortal(
|
||||
const lightbox = (
|
||||
<div className="cr-qa-lightbox" role="dialog" aria-modal="true" aria-label="图片大图" onClick={onClose} onKeyDown={onKeyDown}>
|
||||
<img src={url} alt="" />
|
||||
<button ref={close} type="button" className="cr-qa-lightbox-close" data-tooltip="关闭(Esc)" aria-label="关闭大图">
|
||||
<X size={16} strokeWidth={1.8} aria-hidden />
|
||||
</button>
|
||||
</div>,
|
||||
document.body,
|
||||
</div>
|
||||
)
|
||||
return inPlace ? lightbox : createPortal(lightbox, document.body)
|
||||
}
|
||||
|
||||
/** 复制按钮在每种复制结果下的图标和名字。 */
|
||||
@@ -130,6 +133,11 @@ function CopyAnswerButton({ answer }: { answer: string }): ReactElement {
|
||||
)
|
||||
}
|
||||
|
||||
/** 说过的一次展开后的整段回答:按 Markdown 排,在历史里是灰字。 */
|
||||
function HistoryAnswer({ answer }: { answer: string }): ReactElement {
|
||||
return <MarkdownPreview html={renderMarkdown(answer, 'answer')} className="cr-qa-answer" />
|
||||
}
|
||||
|
||||
/**
|
||||
* 说过的一次,灰字:说的话、时间、复制回答、删除(问答叫「已理解,删除」),带的图片,下面回答的前两行;点问题或预览展开整段回答(照样灰字,按 Markdown 排)。
|
||||
* 重写记录没写进文件时,最下面写着原因
|
||||
@@ -158,7 +166,7 @@ function HistoryItem({ entry, wording, now, onDelete, onOpenImage }: { entry: Qa
|
||||
</div>
|
||||
<Thumbs urls={entry.images ?? []} onOpen={onOpenImage} />
|
||||
{expanded
|
||||
? <MarkdownPreview html={renderMarkdown(entry.answer, 'answer')} className="cr-qa-answer" />
|
||||
? <HistoryAnswer answer={entry.answer} />
|
||||
: (
|
||||
<button type="button" className="cr-qa-preview" onClick={toggle}>
|
||||
{answerPreview(entry.answer)}
|
||||
@@ -174,6 +182,27 @@ function HistoryItem({ entry, wording, now, onDelete, onOpenImage }: { entry: Qa
|
||||
)
|
||||
}
|
||||
|
||||
/** 只读列表里问过的一次,灰字:只有问题一行,点了在下面展开带的图片和整段回答(同 [HistoryItem] 展开后的),再点收起。 */
|
||||
function AskedItem({ entry, onOpenImage }: { entry: QaEntryView, onOpenImage: (url: string) => void }): ReactElement {
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
return (
|
||||
<div className="cr-qa-entry" data-history="">
|
||||
<div className="cr-qa-head">
|
||||
<Sparkles size={12} strokeWidth={1.8} className="cr-qa-icon" aria-hidden />
|
||||
<button type="button" className="cr-qa-question" aria-expanded={expanded} data-tooltip={expanded ? '收起回答' : '展开回答'} onClick={() => setExpanded(value => !value)}>
|
||||
{entry.question}
|
||||
</button>
|
||||
</div>
|
||||
{expanded && (
|
||||
<>
|
||||
<Thumbs urls={entry.images ?? []} onOpen={onOpenImage} />
|
||||
<HistoryAnswer answer={entry.answer} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** 当前这一轮说的一次,照常颜色:说的话(答完了右边能复制回答),回答逐字长出来(按 Markdown 排),排着队、在做的事、出错(重写注释没写进文件也算)、停止写在下面 */
|
||||
function CurrentItem({ ask, wording, onOpenImage }: { ask: Ask, wording: ThreadWording, onOpenImage: (url: string) => void }): ReactElement {
|
||||
const running = ask.status === 'running'
|
||||
@@ -425,3 +454,24 @@ export function AskThread({ history, asks, open, profiles, hint = '这块代码'
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 只读的问答列表(定义卡片底部那一节):一行「问过 AI(N)」,下面按 `entries` 的先后只列问题,点一条展开 / 收起它的回答;不能删、不能接着问。
|
||||
* 看大图画在列表自己里面(见 [ImagePreview] 的 `inPlace`),没问过时什么都不画。
|
||||
*/
|
||||
export function AskedList({ entries }: { entries: QaEntryView[] }): ReactElement | null {
|
||||
/** 正在看大图的那张 */
|
||||
const [preview, setPreview] = useState<string>()
|
||||
if (entries.length === 0)
|
||||
return null
|
||||
return (
|
||||
<section className="cr-qa" aria-label="问过 AI">
|
||||
<div className="cr-qa-title">
|
||||
<Sparkles size={12} strokeWidth={1.8} className="cr-qa-icon" aria-hidden />
|
||||
{`问过 AI(${entries.length})`}
|
||||
</div>
|
||||
{entries.map(entry => <AskedItem key={entry.id} entry={entry} onOpenImage={setPreview} />)}
|
||||
{preview !== undefined && <ImagePreview url={preview} inPlace onClose={() => setPreview(undefined)} />}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user