Revert "fix(web): keep a sent message's attachments out of the next message's limits"
This reverts commit e36c3f9a395e4409b2bb2d29e75d2a9ccee1280d.
This commit is contained in:
1 parent
6752c858a7
commit
4804ce4de4
18 files changed
+39
-151
No files matched your search
@@ -18,8 +18,8 @@
|
||||
en: 15cdd9d52dcc71b8
|
||||
zh: 53933fbbf5d39100
|
||||
/deepseek-ai-dsh-client-ui-attachment/use-this-package/draft-attachments:
|
||||
en: 2c4dc29fc7c54392
|
||||
zh: 0d27bff308577cef
|
||||
en: a77c8ec48e219aec
|
||||
zh: 756d4fe881403006
|
||||
/deepseek-ai-dsh-client-ui-attachment/use-this-package/message-images-and-the-lightbox:
|
||||
en: 8b3d7729152d8060
|
||||
zh: 815f932ccc9653f5
|
||||
|
||||
@@ -29,7 +29,7 @@ Mount this plugin alongside [`ui-conversation`](../ui-conversation/README.md) an
|
||||
|
||||
### Draft attachments
|
||||
|
||||
Images and generic files retain pick order in one non-wrapping horizontal rail. Every item is 64px high: an image is a 64px square thumbnail, while a generic file is a 240px-wide DeepSeek Web card with a 16px radius, blue gradient document glyph, filename, and uppercase extension plus byte size. Edge arrows page hidden overflow, the scrollbar stays hidden, and a newly added item is revealed at the rail's end. Uploading replaces a file glyph with a spinner and shows byte progress when the carrier reports it, with an indeterminate bar before the first report; failure shows retry, and removal controls appear on hover or keyboard focus while remaining visible on touch devices. Attachments of a message that is still being sent have no removal control. Clicking an image opens the original.
|
||||
Images and generic files retain pick order in one non-wrapping horizontal rail. Every item is 64px high: an image is a 64px square thumbnail, while a generic file is a 240px-wide DeepSeek Web card with a 16px radius, blue gradient document glyph, filename, and uppercase extension plus byte size. Edge arrows page hidden overflow, the scrollbar stays hidden, and a newly added item is revealed at the rail's end. Uploading replaces a file glyph with a spinner and shows byte progress when the carrier reports it, with an indeterminate bar before the first report; failure shows retry, and removal controls appear on hover or keyboard focus while remaining visible on touch devices. Clicking an image opens the original.
|
||||
|
||||
### Message images and the lightbox
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ kind: "package-reference"
|
||||
|
||||
### 草稿附件
|
||||
|
||||
图片与通用文件按选择顺序进入同一条不换行的横向附件栏。所有条目均为 64px 高:图片是 64px 方形缩略图,通用文件是 240px 宽的 DeepSeek Web 卡片,带 16px 圆角、蓝色渐变文档图标、文件名,以及大写扩展名与字节大小。溢出隐藏时由边缘箭头翻页,滚动条保持隐藏,新增条目会滚动到栏尾展示。文件上传时图标位置显示 spinner,传输层报告字节时显示进度,首次报告前使用不定态进度条;失败时显示重试。移除按钮在悬停或键盘聚焦时出现,在触摸设备上保持可见。仍在发送的消息的附件没有移除按钮。单击图片会打开原图。
|
||||
图片与通用文件按选择顺序进入同一条不换行的横向附件栏。所有条目均为 64px 高:图片是 64px 方形缩略图,通用文件是 240px 宽的 DeepSeek Web 卡片,带 16px 圆角、蓝色渐变文档图标、文件名,以及大写扩展名与字节大小。溢出隐藏时由边缘箭头翻页,滚动条保持隐藏,新增条目会滚动到栏尾展示。文件上传时图标位置显示 spinner,传输层报告字节时显示进度,首次报告前使用不定态进度条;失败时显示重试。移除按钮在悬停或键盘聚焦时出现,在触摸设备上保持可见。单击图片会打开原图。
|
||||
|
||||
### 消息图片与灯箱
|
||||
|
||||
|
||||
@@ -118,8 +118,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.removable:hover .name,
|
||||
.removable:focus-within .name {
|
||||
.card:hover .name,
|
||||
.card:focus-within .name {
|
||||
padding-right: 18px;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,7 @@ export function FileCard({
|
||||
state: FileCardState
|
||||
progress?: number
|
||||
labels: FileCardLabels
|
||||
/** Removes the file; a card without it offers no remove action. */
|
||||
onRemove?: (() => void) | undefined
|
||||
onRemove: () => void
|
||||
onRetry: () => void
|
||||
}) {
|
||||
const extension = fileExtension(name).toUpperCase().slice(0, 8)
|
||||
@@ -40,7 +39,7 @@ export function FileCard({
|
||||
const retryable = state === 'error'
|
||||
return (
|
||||
<div
|
||||
className={`${css.card}${retryable ? ` ${css.failed}` : ''}${onRemove === undefined ? '' : ` ${css.removable}`}`}
|
||||
className={`${css.card}${retryable ? ` ${css.failed}` : ''}`}
|
||||
title={name}
|
||||
>
|
||||
<span className={css.icon} aria-hidden>
|
||||
@@ -61,16 +60,14 @@ export function FileCard({
|
||||
<span className={css.meta}>{meta}</span>
|
||||
</span>
|
||||
)}
|
||||
{onRemove !== undefined && (
|
||||
<button
|
||||
type="button"
|
||||
className={retryable ? `${css.remove} ${css.removeFailed}` : css.remove}
|
||||
aria-label={labels.remove}
|
||||
onClick={onRemove}
|
||||
>
|
||||
<IconCloseFillRegular size={12} />
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className={retryable ? `${css.remove} ${css.removeFailed}` : css.remove}
|
||||
aria-label={labels.remove}
|
||||
onClick={onRemove}
|
||||
>
|
||||
<IconCloseFillRegular size={12} />
|
||||
</button>
|
||||
{state === 'uploading' && (
|
||||
<span className={css.progressTrack} aria-hidden>
|
||||
<span
|
||||
|
||||
@@ -19,7 +19,7 @@ interface ComposerRailItem extends AttachmentRailItem {
|
||||
|
||||
/** Draft image previews, pending-file cards, drop target, and original-image preview. */
|
||||
export function ComposerAttachments({
|
||||
attachments, canAcceptDrop, onAddFiles, onRemoveAttachment, sendingAttachmentIds, uploads, onRetryFile, dropLimits, t,
|
||||
attachments, canAcceptDrop, onAddFiles, onRemoveAttachment, uploads, onRetryFile, dropLimits, t,
|
||||
}: ComposerAttachmentsProps) {
|
||||
const [preview, setPreview] = useState<ComposerImageAttachment | null>(null)
|
||||
const [dragActive, setDragActive] = useState(false)
|
||||
@@ -53,7 +53,6 @@ export function ComposerAttachments({
|
||||
labels={attachmentRailLabels(t)}
|
||||
renderItem={(item) => {
|
||||
const attachment = item.attachment
|
||||
const removable = !sendingAttachmentIds.includes(attachment.id)
|
||||
if (attachment.kind === 'file') {
|
||||
const upload = uploads[attachment.id]
|
||||
return (
|
||||
@@ -67,7 +66,7 @@ export function ComposerAttachments({
|
||||
? { progress: upload.loaded / upload.total }
|
||||
: {}}
|
||||
labels={fileCardLabels(t, attachment.file.name)}
|
||||
onRemove={removable ? () => { onRemoveAttachment(attachment.id) } : undefined}
|
||||
onRemove={() => { onRemoveAttachment(attachment.id) }}
|
||||
onRetry={() => { onRetryFile(attachment.id) }}
|
||||
/>
|
||||
)
|
||||
@@ -82,16 +81,14 @@ export function ComposerAttachments({
|
||||
>
|
||||
<img src={attachment.previewUrl} alt={attachment.file.name || t('image.pending')} />
|
||||
</button>
|
||||
{removable && (
|
||||
<button
|
||||
type="button"
|
||||
className={css.remove}
|
||||
aria-label={t('image.remove', { name: attachment.file.name })}
|
||||
onClick={() => { onRemoveAttachment(attachment.id) }}
|
||||
>
|
||||
<IconCloseFillRegular size={12} />
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className={css.remove}
|
||||
aria-label={t('image.remove', { name: attachment.file.name })}
|
||||
onClick={() => { onRemoveAttachment(attachment.id) }}
|
||||
>
|
||||
<IconCloseFillRegular size={12} />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
|
||||
@@ -80,12 +80,11 @@ function props(overrides: Partial<ComposerAttachmentsOwnerProps> = {}): Composer
|
||||
canAcceptDrop: true,
|
||||
onAddFiles: () => {},
|
||||
onRemoveAttachment: () => {},
|
||||
sendingAttachmentIds: [],
|
||||
uploads: {},
|
||||
onRetryFile: () => {},
|
||||
t,
|
||||
...overrides,
|
||||
} as ComposerAttachmentsProps
|
||||
} as unknown as ComposerAttachmentsProps
|
||||
}
|
||||
|
||||
describe('ComposerAttachments', () => {
|
||||
@@ -203,26 +202,6 @@ describe('ComposerAttachments', () => {
|
||||
expect(view.queryByRole('dialog', { name: '原图预览' })).toBeNull()
|
||||
})
|
||||
|
||||
it('keeps the attachments of the message being sent without a remove action', () => {
|
||||
const onRemoveAttachment = vi.fn()
|
||||
const view = render(<ComposerAttachments {...props({
|
||||
attachments: [attachment('sent'), fileDraft('sent-file'), attachment('next')],
|
||||
uploads: {
|
||||
'sent-file': {
|
||||
status: 'ready', receiptId: 'receipt-sent-file' as never,
|
||||
file: { attachmentId: 'file-sent' as never, name: 'sent-file.pdf', bytes: 3 },
|
||||
},
|
||||
},
|
||||
sendingAttachmentIds: ['sent', 'sent-file'] as ComposerAttachment['id'][],
|
||||
onRemoveAttachment,
|
||||
})} />)
|
||||
expect(view.queryByRole('button', { name: '移除图片 sent.png' })).toBeNull()
|
||||
expect(view.queryByRole('button', { name: '移除文件 sent-file.pdf' })).toBeNull()
|
||||
expect(view.getByTitle('sent-file.pdf').textContent).toContain('PDF 3B')
|
||||
fireEvent.click(view.getByRole('button', { name: '移除图片 next.png' }))
|
||||
expect(onRemoveAttachment).toHaveBeenCalledWith('next')
|
||||
})
|
||||
|
||||
it('keeps images and files in pick order inside one attachment rail', () => {
|
||||
const view = render(<ComposerAttachments {...props({
|
||||
attachments: [attachment('first'), fileDraft('middle'), attachment('last')],
|
||||
|
||||
@@ -303,12 +303,6 @@ export interface InputState {
|
||||
* send waits, and the attachments of the message being sent stay attached.
|
||||
*/
|
||||
readonly phase: 'plain' | 'adjudicating' | 'claimed' | 'submitting' | 'sending'
|
||||
/**
|
||||
* Attachments of the message being sent, in order; empty unless `phase` is
|
||||
* `sending`. They stay in `attachmentIds` and cannot be removed until the
|
||||
* send settles; attachments added meanwhile belong to the next message.
|
||||
*/
|
||||
readonly sendingAttachmentIds: readonly DraftAttachmentId[]
|
||||
/** Present exactly while claimed/submitting (claim snapshot during flight; submit closure withheld). */
|
||||
readonly claim?: { readonly name: string; readonly token: string; readonly hint?: string; readonly attachments?: boolean }
|
||||
/** Reference occurrence view of the editor's chips, sorted by offset. */
|
||||
|
||||
@@ -70,8 +70,6 @@ export interface ComposerAttachmentsOwnerProps {
|
||||
onAddFiles: (files: readonly File[], directories?: ReadonlySet<File>) => void
|
||||
/** Remove one draft attachment through the Conversation service. */
|
||||
onRemoveAttachment: (id: DraftAttachmentId) => void
|
||||
/** Attachments of the message being sent, which the rail shows without a remove action. */
|
||||
sendingAttachmentIds: readonly DraftAttachmentId[]
|
||||
/** Current per-draft upload states for file-kind attachments. */
|
||||
uploads: DraftFileUploads
|
||||
/** Restart one failed file upload. */
|
||||
|
||||
@@ -116,8 +116,6 @@ function errorText(error: unknown): string {
|
||||
|
||||
const EMPTY_QUEUE: InboxState['next-turn'] = []
|
||||
|
||||
const EMPTY_ATTACHMENTS: readonly DraftAttachmentId[] = []
|
||||
|
||||
/** Unavailable catalogs contain no named text references. */
|
||||
const EMPTY_LEXICON: ReadonlyMap<'/' | '@', readonly string[]> = new Map()
|
||||
|
||||
@@ -848,7 +846,6 @@ export class SessionInputShell implements SessionInput {
|
||||
attachmentIds: this.attachmentIds,
|
||||
draftRev: this.rev,
|
||||
phase: core.phase,
|
||||
sendingAttachmentIds: this.core.sendingAttempt?.attachmentIds ?? EMPTY_ATTACHMENTS,
|
||||
...(core.claim !== undefined ? { claim: core.claim } : {}),
|
||||
occurrences: this.projection.occurrences,
|
||||
queue: this.deps.inbox?.getSnapshot()?.['next-turn'] ?? EMPTY_QUEUE,
|
||||
|
||||
@@ -28,7 +28,6 @@ import type {} from '@deepseek-ai/dsh-goal/client'
|
||||
// wire types: apiproxy's sessions contract declares it, and client-runtime's
|
||||
// api-remotes import already places it in every client program.
|
||||
import type { Translate } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import type { DraftAttachmentId } from '../contract/input.ts'
|
||||
import type { ComposerBarProps } from '../contract/slots.ts'
|
||||
import { DraftEditor } from '../input/editor/DraftEditor.tsx'
|
||||
import {
|
||||
@@ -44,8 +43,6 @@ import css from './InputBar.module.css'
|
||||
|
||||
export type InputBarProps = ComposerBarProps
|
||||
|
||||
const NO_ATTACHMENTS: readonly DraftAttachmentId[] = []
|
||||
|
||||
export const InputBar = memo(function InputBar({
|
||||
useSession, useInput, inputActions, keyboard, addFiles, removeAttachment, resolveDraftAttachments,
|
||||
retryFileUpload,
|
||||
@@ -81,7 +78,6 @@ export const InputBar = memo(function InputBar({
|
||||
() => input === undefined || resolveDraftAttachments === undefined ? [] : resolveDraftAttachments(input.attachmentIds),
|
||||
[resolveDraftAttachments, input?.attachmentIds],
|
||||
)
|
||||
const sendingAttachmentIds = input?.sendingAttachmentIds ?? NO_ATTACHMENTS
|
||||
const empty = draft.trim() === '' && attachments.length === 0
|
||||
const uploads = useFileUploads(snapshot => snapshot)
|
||||
// Send waits for every picked file: uploading and failed drafts both hold
|
||||
@@ -237,9 +233,8 @@ export const InputBar = memo(function InputBar({
|
||||
// refused as a whole batch, announced immediately, and never enters the
|
||||
// rail. Only the image subset is limit-checked: generic files carry no
|
||||
// client-side size or count limit and upload as soon as they are picked.
|
||||
// The images of a message being sent belong to that message, so the limits
|
||||
// count only the images of the next one. The host enforces the same image
|
||||
// limits at submit for callers that bypass this composer.
|
||||
// The host enforces the same image limits at submit for callers that bypass
|
||||
// this composer.
|
||||
const intakeFiles = useCallback((files: readonly File[], directories?: ReadonlySet<File>): void => {
|
||||
if (locked || commandBusy || (subagent !== null && !continuable) || addFiles === undefined || files.length === 0) return
|
||||
const rejected = ((): string | null => {
|
||||
@@ -251,8 +246,7 @@ export const InputBar = memo(function InputBar({
|
||||
if (imageLimits !== undefined) {
|
||||
const mediaTypes = imageLimits.mediaTypes as readonly string[]
|
||||
const images = files.filter(file => mediaTypes.includes(file.type))
|
||||
const imageAttachments = attachments.filter(attachment => attachment.kind === 'image'
|
||||
&& !sendingAttachmentIds.includes(attachment.id))
|
||||
const imageAttachments = attachments.filter(attachment => attachment.kind === 'image')
|
||||
if (imageAttachments.length + images.length > imageLimits.maxImagesPerMessage) {
|
||||
return t('image.tooMany', { count: imageLimits.maxImagesPerMessage })
|
||||
}
|
||||
@@ -268,7 +262,7 @@ export const InputBar = memo(function InputBar({
|
||||
return addFiles(files, directories)
|
||||
})()
|
||||
if (rejected !== null) showToast(rejected)
|
||||
}, [locked, commandBusy, subagent, continuable, addFiles, attachments, sendingAttachmentIds, imageLimits, showToast, t])
|
||||
}, [locked, commandBusy, subagent, continuable, addFiles, attachments, imageLimits, showToast, t])
|
||||
|
||||
const canAcceptDrop = (subagent === null || continuable) && !locked && !commandBusy && addFiles !== undefined
|
||||
|
||||
@@ -444,7 +438,6 @@ export const InputBar = memo(function InputBar({
|
||||
canAcceptDrop,
|
||||
onAddFiles: intakeFiles,
|
||||
onRemoveAttachment: (id) => { removeAttachment?.(id) },
|
||||
sendingAttachmentIds,
|
||||
uploads,
|
||||
onRetryFile: (id) => { retryFileUpload?.(id) },
|
||||
dropLimits: imageLimits === undefined ? undefined : {
|
||||
|
||||
@@ -619,16 +619,10 @@ describe('draft documents in the input shell', () => {
|
||||
expect(shell.addAttachments([nextAttachment])).toBe(true)
|
||||
expect(shell.removeAttachment(nextAttachment)).toBe(true)
|
||||
expect(shell.addAttachments([nextAttachment])).toBe(true)
|
||||
expect(shell.snapshot).toMatchObject({
|
||||
attachmentIds: [sentAttachment, nextAttachment], sendingAttachmentIds: [sentAttachment],
|
||||
})
|
||||
outcome.resolve({ kind: 'error', text: 'flight rejected' })
|
||||
await vi.waitFor(() => { expect(shell.notices.getSnapshot()?.text).toBe('flight rejected') })
|
||||
expect(shell.snapshot).toMatchObject({
|
||||
phase: 'plain',
|
||||
draft: attachmentOnly ? '' : 'outbound message',
|
||||
attachmentIds: [sentAttachment, nextAttachment],
|
||||
sendingAttachmentIds: [],
|
||||
phase: 'plain', draft: attachmentOnly ? '' : 'outbound message', attachmentIds: [sentAttachment, nextAttachment],
|
||||
})
|
||||
expect(shell.removeAttachment(sentAttachment)).toBe(true)
|
||||
expect(shell.requestDraftInitialization({ prompt: 'after settlement', clearPreviousDraft: true })).toBe('applied')
|
||||
@@ -709,7 +703,7 @@ describe('draft documents in the input shell', () => {
|
||||
expect(shell.addAttachments([nextAttachment])).toBe(true)
|
||||
outcome.resolve({ kind: 'success' })
|
||||
await vi.waitFor(() => { expect(shell.snapshot.phase).toBe('plain') })
|
||||
expect(shell.snapshot).toMatchObject({ draft, attachmentIds: [nextAttachment], sendingAttachmentIds: [] })
|
||||
expect(shell.snapshot).toMatchObject({ draft, attachmentIds: [nextAttachment] })
|
||||
expect(shell.caretSpan()).toEqual({ start: caret, end: caret })
|
||||
shell.editor.dispatchCommand(UNDO_COMMAND, undefined)
|
||||
shell.editor.update(() => {}, { discrete: true })
|
||||
|
||||
@@ -618,66 +618,6 @@ describe('image draft rail', () => {
|
||||
expect(within.view.queryByRole('alert')).toBeNull()
|
||||
})
|
||||
|
||||
it.each([
|
||||
['count', { maxImagesPerMessage: 1, maxMessageImageBytes: 4 * 1024 * 1024 }, 16, 16],
|
||||
['total size', { maxImagesPerMessage: 5, maxMessageImageBytes: 2 * 1024 * 1024 }, 1.5 * 1024 * 1024, 1024 * 1024],
|
||||
])('checks a new image against the next message\'s %s limit while a message is sent', async (_limit, caps, sentBytes, nextBytes) => {
|
||||
const limits = {
|
||||
maxImageBytes: 2 * 1024 * 1024,
|
||||
...caps,
|
||||
maxImagePixels: 40_000_000,
|
||||
maxImageDimension: 2000,
|
||||
mediaTypes: ['image/png'] as const,
|
||||
}
|
||||
const sent = {
|
||||
kind: 'image' as const, id: 'draft-1' as DraftAttachmentId,
|
||||
file: new File([new ArrayBuffer(sentBytes)], 'sent.png', { type: 'image/png' }), previewUrl: 'blob:draft-1',
|
||||
}
|
||||
const addFiles = vi.fn(() => null)
|
||||
const result = bench({ attachments: [sent], imageLimits: limits, addFiles })
|
||||
let settle!: (outcome: SubmitOutcome) => void
|
||||
result.sink.mockImplementationOnce(() => new Promise<SubmitOutcome>((resolve) => { settle = resolve }))
|
||||
fireEvent.keyDown(result.textarea, { key: 'Enter' })
|
||||
expect(attachmentOwner(result.slotCalls).sendingAttachmentIds).toEqual(['draft-1'])
|
||||
const next = new File([new ArrayBuffer(nextBytes)], 'next.png', { type: 'image/png' })
|
||||
act(() => { attachmentOwner(result.slotCalls).onAddFiles([next]) })
|
||||
expect(addFiles).toHaveBeenCalledWith([next], undefined)
|
||||
expect(result.view.queryByRole('alert')).toBeNull()
|
||||
await act(async () => { settle({ kind: 'success' }) })
|
||||
expect(attachmentOwner(result.slotCalls).sendingAttachmentIds).toEqual([])
|
||||
})
|
||||
|
||||
it('counts the images of a refused message against the next addition again', async () => {
|
||||
const sent = {
|
||||
kind: 'image' as const, id: 'draft-1' as DraftAttachmentId,
|
||||
file: new File([Uint8Array.of(1)], 'sent.png', { type: 'image/png' }), previewUrl: 'blob:draft-1',
|
||||
}
|
||||
const addFiles = vi.fn(() => null)
|
||||
const result = bench({
|
||||
attachments: [sent],
|
||||
addFiles,
|
||||
imageLimits: {
|
||||
maxImageBytes: 1024 * 1024,
|
||||
maxImagesPerMessage: 1,
|
||||
maxMessageImageBytes: 1024 * 1024,
|
||||
maxImagePixels: 40_000_000,
|
||||
maxImageDimension: 2000,
|
||||
mediaTypes: ['image/png'] as const,
|
||||
},
|
||||
})
|
||||
let refuse!: (outcome: SubmitOutcome) => void
|
||||
result.sink.mockImplementationOnce(() => new Promise<SubmitOutcome>((resolve) => { refuse = resolve }))
|
||||
fireEvent.keyDown(result.textarea, { key: 'Enter' })
|
||||
await act(async () => { refuse({ kind: 'error' }) })
|
||||
// The refused message's image stays in the draft, so the next message would carry it too.
|
||||
expect(attachmentOwner(result.slotCalls).sendingAttachmentIds).toEqual([])
|
||||
act(() => {
|
||||
attachmentOwner(result.slotCalls).onAddFiles([new File([Uint8Array.of(2)], 'next.png', { type: 'image/png' })])
|
||||
})
|
||||
expect(addFiles).not.toHaveBeenCalled()
|
||||
expect(result.view.getByRole('alert').textContent).toContain('一条消息最多添加 1 张图片')
|
||||
})
|
||||
|
||||
it('announces the format problem before any limit when the batch holds a non-image', () => {
|
||||
const addFiles = vi.fn(() => '仅支持 PNG、JPG、WebP、GIF 格式的图片')
|
||||
const result = bench({
|
||||
|
||||
@@ -94,9 +94,7 @@ function liveSession(initial: TestSnapshot) {
|
||||
}
|
||||
}
|
||||
|
||||
const INPUT_STATE: InputState = {
|
||||
draft: '', attachmentIds: [], draftRev: 0, phase: 'plain', sendingAttachmentIds: [], occurrences: [], queue: [],
|
||||
}
|
||||
const INPUT_STATE: InputState = { draft: '', attachmentIds: [], draftRev: 0, phase: 'plain', occurrences: [], queue: [] }
|
||||
|
||||
const t: QueueDockProps['t'] = makeTranslate(zh, commonZh)
|
||||
const usePanelInfo: GlobalStandardProps['usePanelInfo'] = selector => selector({ activePanelId: null })
|
||||
|
||||
@@ -213,7 +213,7 @@ function standaloneProps(
|
||||
): StandaloneBaseProps {
|
||||
const trajectory = historySnapshot(nodes)
|
||||
const input = createSnapshotStore<InputState>({
|
||||
draft: '', attachmentIds: [], draftRev: 0, phase: 'plain', sendingAttachmentIds: [], occurrences: [], queue: [],
|
||||
draft: '', attachmentIds: [], draftRev: 0, phase: 'plain', occurrences: [], queue: [],
|
||||
})
|
||||
const inputActions: InputActions = {
|
||||
captureInsertion: () => ({ start: 0, end: 0, draftRev: 0 }),
|
||||
@@ -342,7 +342,7 @@ function mount(fixture: Awaited<ReturnType<typeof bench>>) {
|
||||
createSnapshotStore<readonly ViewTab[]>(tabsOf(slots)),
|
||||
)
|
||||
const useInput = bindSnapshotSelector(createSnapshotStore<InputState>({
|
||||
draft: '', attachmentIds: [], draftRev: 0, phase: 'plain', sendingAttachmentIds: [], occurrences: [], queue: [],
|
||||
draft: '', attachmentIds: [], draftRev: 0, phase: 'plain', occurrences: [], queue: [],
|
||||
}))
|
||||
const inputActions: InputActions = {
|
||||
captureInsertion: () => ({ start: 0, end: 0, draftRev: 0 }),
|
||||
|
||||
@@ -104,7 +104,6 @@ const inputState: InputState = {
|
||||
attachmentIds: [],
|
||||
draftRev: 0,
|
||||
phase: 'plain',
|
||||
sendingAttachmentIds: [],
|
||||
occurrences: [],
|
||||
queue: [],
|
||||
}
|
||||
|
||||
@@ -104,7 +104,6 @@ const inputState: InputState = {
|
||||
attachmentIds: [],
|
||||
draftRev: 0,
|
||||
phase: 'plain',
|
||||
sendingAttachmentIds: [],
|
||||
occurrences: [],
|
||||
queue: [],
|
||||
}
|
||||
|
||||
@@ -443,6 +443,9 @@
|
||||
"packages/client/ui-attachment/tests/attachment-rail.client.spec.tsx": {
|
||||
"b42ab24a43e0f7542bf1deb8f7ac3a76698d29e098f8dc575af6c08774987c32": 1
|
||||
},
|
||||
"packages/client/ui-attachment/tests/composer-attachments.client.spec.tsx": {
|
||||
"ab22ebe23f59b5aa76d42502a0421f3953d1d170e8a4f900a0c442daf6f1397d": 1
|
||||
},
|
||||
"packages/client/ui-chat/src/client/conversation-nodes/turn-process.ts": {
|
||||
"a7d8cc8d42e37ad5cc4882241492ac572ed8163c7fb8663180e893c7178e906f": 1
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user