fix(web): address queue review feedback

This commit is contained in:
kingwl
2026-07-30 04:12:38 +08:00
parent ebbcfdd03b
commit 112b0fbad1
13 changed files with 154 additions and 53 deletions

View File

@@ -0,0 +1,13 @@
/** Queue contracts derived from the runtime session face and snapshot. */
import type {
ConversationSnapshot, SessionFace,
} from '@deepseek-ai/dsh-client-runtime/client'
/** One address accepted by the runtime session's queue mutation verb. */
export type QueueItemId = Parameters<SessionFace['updateQueue']>[0]
/** One mutation accepted by the runtime session's queue mutation verb. */
export type QueueAction = Parameters<SessionFace['updateQueue']>[1]
/** One row projected by the runtime session's authoritative queue snapshot. */
export type QueueRow = ConversationSnapshot['queue'][number]

View File

@@ -6,11 +6,11 @@
* (machine.ts) is package-private and never exported.
*/
import type { ClientContext, SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'
import type { InboxItemId } from '@deepseek-ai/dsh-client-connection/client'
import type {
ArbitrateKey, ArbitrateOutcome, CommandClaim, ConsumeTokenRequest, PickOutcome,
ReferenceInsert, SubmitOutcome, TokenSpan,
} from '@deepseek-ai/dsh-client-ui-slash/client'
import type { QueueRow } from '../contract/queue.ts'
/**
* The scoped-event application verbs: the hub's bail listeners call these,
@@ -101,11 +101,7 @@ export interface ComposerKeyboard {
}
/** One independently addressable row projected from the transient queue snapshot. */
export interface QueuedMessage {
readonly id: InboxItemId
readonly preview: string
readonly text: string | null
}
export type QueuedMessage = QueueRow
/** Guard union of the scoped consume-token event, checked by the machine. */
export type ConsumeTokenGuard = ConsumeTokenRequest['guard']

View File

@@ -7,15 +7,15 @@ import type { Context } from 'cordis'
import { useEffect, useState } from 'react'
import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
import type { SessionId } from '@deepseek-ai/dsh-client-runtime/client'
import type { InboxItemId, QueueAction } from '@deepseek-ai/dsh-client-connection/client'
import {
IconCheckOutline16, IconCloseOutline16, IconEditOutline16, IconTrashOutline16,
} from '@deepseek-ai/dsh-client-ui-primitives'
import type { QueueAction, QueueItemId } from '../contract/queue.ts'
import css from './QueueDock.module.css'
/** Queue operations injected by the session-scoped registration. */
export interface QueueDockInjected {
updateQueue: (itemId: InboxItemId, action: QueueAction) => Promise<void>
updateQueue: (itemId: QueueItemId, action: QueueAction) => Promise<void>
notify: (level: 'info' | 'error', text: string) => void
}
@@ -25,8 +25,8 @@ export type QueueDockProps = PropsRuntime<'conversation.input.dock'> & QueueDock
/** Queue strip: one preview line per queued message; renders null when the queue is empty. */
export function QueueDock({ useSession, updateQueue, notify }: QueueDockProps) {
const queue = useSession(s => s.queue)
const [editing, setEditing] = useState<{ id: InboxItemId; text: string } | null>(null)
const [busy, setBusy] = useState<InboxItemId | null>(null)
const [editing, setEditing] = useState<{ id: QueueItemId; text: string } | null>(null)
const [busy, setBusy] = useState<QueueItemId | null>(null)
useEffect(() => {
if (editing !== null && !queue.some(row => row.id === editing.id)) setEditing(null)
@@ -35,7 +35,7 @@ export function QueueDock({ useSession, updateQueue, notify }: QueueDockProps) {
if (queue.length === 0) return null
const applyAction = async (
itemId: InboxItemId,
itemId: QueueItemId,
action: QueueAction,
failure: string,
): Promise<boolean> => {

View File

@@ -13,7 +13,7 @@ import type { Context } from 'cordis'
// error, so scope resolution goes through the sessions service (scopeOf
// method) instead of the standalone helper.
import type { ISessions, SessionFace, SessionId } from '@deepseek-ai/dsh-client-runtime/client'
import type { InboxItemId, QueueAction } from '@deepseek-ai/dsh-client-connection/client'
import type { QueueAction, QueueItemId } from './contract/queue.ts'
import type { InputService } from './input/contract.ts'
/**
@@ -37,7 +37,7 @@ export interface IConversation {
* @param action - edit or remove operation.
* @returns completion; business failures reject.
*/
updateQueue(itemId: InboxItemId, action: QueueAction): Promise<void>
updateQueue(itemId: QueueItemId, action: QueueAction): Promise<void>
/**
* Cancel the scoped session's in-flight turn.
* @returns completion; failures reject as in send.
@@ -80,7 +80,7 @@ export class ConversationService extends Service implements IConversation {
}
/** Apply one operation to a pending queue occurrence. */
async updateQueue(itemId: InboxItemId, action: QueueAction): Promise<void> {
async updateQueue(itemId: QueueItemId, action: QueueAction): Promise<void> {
const session = this.scopedSession('updateQueue')
const result = await session.updateQueue(itemId, action)
if (!result.ok) {

View File

@@ -9,15 +9,15 @@ import { useSyncExternalStore } from 'react'
import type {
ConversationSnapshot, QueuedMessage, SessionId, SessionListState,
} from '@deepseek-ai/dsh-client-runtime/client'
import type { InboxItemId } from '@deepseek-ai/dsh-client-connection/client'
import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots'
import type { QueueItemId } from '../src/client/contract/queue.ts'
import type { InputState } from '../src/client/input/contract.ts'
import { QueueDock, queueDockEntry, type QueueDockInjected } from '../src/client/queue/QueueDock.tsx'
afterEach(cleanup)
const SID = 's1' as SessionId
const iid = (id: string): InboxItemId => id as InboxItemId
const iid = (id: string): QueueItemId => id as QueueItemId
function row(id: string, text: string | null, preview = text ?? '[image]'): QueuedMessage {
return { id: iid(id), preview, text }