Merge remote-tracking branch 'origin/master' into docs/post-v3-release-proofreading
# Conflicts: # docs/subsystems/feedback.i18n.yaml # docs/subsystems/feedback.zh.md
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// Shared IconActions chrome for user and assistant messages: copy
|
||||
// live, optional branch wiring, and an optional date-aware clock.
|
||||
|
||||
import { useCallback, useEffect, useId, useRef, useState } from 'react'
|
||||
import { useCallback, useEffect, useId, useRef, useState, type ReactNode } from 'react'
|
||||
import {
|
||||
IconBranchOutline16, IconCheckOutline16, IconCopyOutline16, Tooltip, writeClipboard,
|
||||
} from '@deepseek-ai/dsh-client-ui-primitives'
|
||||
@@ -29,6 +29,11 @@ export interface MessageIconActionsProps {
|
||||
branchUnavailable?: boolean | undefined
|
||||
/** Parent layout class composed onto the actions row. */
|
||||
className?: string | undefined
|
||||
/**
|
||||
* Slot-rendered actions owned by independent plugins, placed between the
|
||||
* built-in copy and branch controls.
|
||||
*/
|
||||
extraActions?: ReactNode
|
||||
/** The owning view's locale seat, passed down as a plain prop. */
|
||||
t: ChatViewSlotProps['t']
|
||||
}
|
||||
@@ -39,7 +44,8 @@ export interface MessageIconActionsProps {
|
||||
* @returns The actions row element.
|
||||
*/
|
||||
export function MessageIconActions({
|
||||
text, time, runMs, ttftMs, tokensPerSecond, clock, onBranch, branchUnavailable = false, className, t,
|
||||
text, time, runMs, ttftMs, tokensPerSecond, clock, onBranch, branchUnavailable = false, className,
|
||||
extraActions, t,
|
||||
}: MessageIconActionsProps) {
|
||||
const day = useCalendarDay()
|
||||
const reasonId = useId()
|
||||
@@ -109,6 +115,7 @@ export function MessageIconActions({
|
||||
{copied ? <IconCheckOutline16 /> : <IconCopyOutline16 />}
|
||||
</button>
|
||||
</Tooltip>
|
||||
{extraActions}
|
||||
{onBranch !== undefined && (
|
||||
<Tooltip label={branchUnavailable ? t('message.branchUnavailable') : t('message.branch')} side="bottom">
|
||||
{/* Native disabled buttons do not deliver the hover/focus events Tooltip needs. */}
|
||||
|
||||
@@ -5,11 +5,12 @@ import { MessageIconActions } from './MessageIconActions.tsx'
|
||||
import { assistantText } from './turn-assistant.ts'
|
||||
import css from './TurnTailNodeView.module.css'
|
||||
|
||||
type TurnTailNodeViewProps = ChatNodeViewProps<'turn-tail'> & PropsRenderSlots<'conversation.chat.turnTail'>
|
||||
type TurnTailNodeViewProps = ChatNodeViewProps<'turn-tail'>
|
||||
& PropsRenderSlots<'conversation.chat.turnTail' | 'conversation.chat.assistant-actions'>
|
||||
|
||||
/** Turn-local actions and feature tail over the Location index, independent of Assistant placement. */
|
||||
export const TurnTailNodeView = memo(function TurnTailNodeView({
|
||||
node, openFile, forkAt, renderSlotChain, t, useSession,
|
||||
node, openFile, forkAt, renderSlot, renderSlotChain, t, useSession,
|
||||
}: TurnTailNodeViewProps) {
|
||||
const data = node.data
|
||||
const hasLaterChatNode = useSession(snapshot =>
|
||||
@@ -25,6 +26,12 @@ export const TurnTailNodeView = memo(function TurnTailNodeView({
|
||||
const runMs = turn.start === undefined || turn.end === undefined
|
||||
? undefined
|
||||
: Math.max(0, turn.end.time - turn.start.time)
|
||||
// Interruption-frozen partials carry no messageId, so they address no
|
||||
// durable message and contribute no per-message actions.
|
||||
const messageId = closing.finalNode.messageId
|
||||
const assistantActions = messageId === undefined
|
||||
? null
|
||||
: renderSlot('conversation.chat.assistant-actions', { messageId })
|
||||
return (
|
||||
<div className={css.root} data-turn-tail={data.turn} data-time-hover-root>
|
||||
{tail}
|
||||
@@ -38,6 +45,7 @@ export const TurnTailNodeView = memo(function TurnTailNodeView({
|
||||
onBranch={() => { forkAt(closing.finalNode.seq) }}
|
||||
branchUnavailable={data.branchUnavailable || hasLaterChatNode}
|
||||
className={css.actions}
|
||||
extraActions={assistantActions}
|
||||
t={t}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -39,7 +39,10 @@ export function registerChatNodeRenderers(ctx: Context): void {
|
||||
name: 'conversation.chat.node',
|
||||
key: 'turn-tail',
|
||||
locale: NS,
|
||||
children: { 'conversation.chat.turnTail': { kind: 'chain', scope: 'session' } },
|
||||
children: {
|
||||
'conversation.chat.turnTail': { kind: 'chain', scope: 'session' },
|
||||
'conversation.chat.assistant-actions': { kind: 'list', scope: 'session' },
|
||||
},
|
||||
}, TurnTailNodeView))
|
||||
ctx.slots.inject('conversation.chat.node', () => ctx.slots.register(
|
||||
{ name: 'conversation.chat.node', key: 'unknown', locale: NS }, UnknownNodeView))
|
||||
|
||||
@@ -11,6 +11,7 @@ import type {
|
||||
TurnLocation, WorkspaceId,
|
||||
} from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { MarkdownFileMentions } from '@deepseek-ai/dsh-client-ui-primitives'
|
||||
import type { MessageId } from '@deepseek-ai/dsh-client-connection/client'
|
||||
import type {} from '@deepseek-ai/dsh-client-ui-layout/client'
|
||||
import type { ComposerBlock } from '../input/blocks.ts'
|
||||
import type {
|
||||
@@ -77,6 +78,18 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
||||
* only to return null; an all-declined chain renders nothing.
|
||||
*/
|
||||
'conversation.chat.turnTail': { kind: 'chain'; scope: 'session'; owner: TurnTailOwnerProps }
|
||||
/**
|
||||
* Action strip attached to one finalized assistant message, rendered
|
||||
* inside that message's IconActions row. The chat entry owns the render
|
||||
* site and passes the addressed message identity; contributors add
|
||||
* per-message actions without importing the conversation implementation.
|
||||
* Entries render by ascending `order`.
|
||||
*/
|
||||
'conversation.chat.assistant-actions': {
|
||||
kind: 'list'
|
||||
scope: 'session'
|
||||
owner: AssistantActionOwnerProps
|
||||
}
|
||||
/** Selected Tool call output inside the details panel. */
|
||||
'conversation.details.tool': { kind: 'single'; scope: 'session'; owner: DetailsToolOwnerProps }
|
||||
/**
|
||||
@@ -253,6 +266,16 @@ export interface TurnTailOwnerProps {
|
||||
openFile: (path: string) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Owner currency of the assistant-message action strip: the durable identity
|
||||
* of the one finalized message the contributed actions address. Only finalized
|
||||
* messages reach this slot, so the id is always present.
|
||||
*/
|
||||
export interface AssistantActionOwnerProps {
|
||||
/** Stable identity carried from the `assistant/message` event. */
|
||||
messageId: MessageId
|
||||
}
|
||||
|
||||
/** Hook constrained to business data published on the current Chat Node's Turn. */
|
||||
export type UseChatNodeTurnData = <Key extends Extract<keyof ConversationTurnDataMap, string>>(
|
||||
key: Key,
|
||||
|
||||
@@ -152,6 +152,7 @@ function finalNode(
|
||||
return {
|
||||
kind: 'assistant',
|
||||
seq: event.seq,
|
||||
messageId: event.data.message.id,
|
||||
time: event.time,
|
||||
turn: state.turn,
|
||||
step: state.step,
|
||||
|
||||
Reference in New Issue
Block a user