mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
feat(hub,web): support scheduling messages for future delivery (#590)
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import type React from 'react'
|
||||
import type { AppendMessage, AttachmentAdapter, ThreadMessageLike } from '@assistant-ui/react'
|
||||
import { useExternalMessageConverter, useExternalStoreRuntime } from '@assistant-ui/react'
|
||||
import type { PendingSchedule } from '@/components/AssistantChat/ScheduleTimePicker'
|
||||
import { resolvePendingSchedule } from '@/components/AssistantChat/ScheduleTimePicker'
|
||||
import { safeStringify } from '@hapi/protocol'
|
||||
import { renderEventLabel } from '@/chat/presentation'
|
||||
import type { ChatBlock, CliOutputBlock, CodexReview, UsageData } from '@/chat/types'
|
||||
@@ -298,10 +301,11 @@ export function useHappyRuntime(props: {
|
||||
blocks: readonly VisibleChatBlock[]
|
||||
isSending: boolean
|
||||
isRunning?: boolean
|
||||
onSendMessage: (text: string, attachments?: AttachmentMetadata[]) => void
|
||||
onSendMessage: (text: string, attachments?: AttachmentMetadata[], scheduledAt?: number | null) => void
|
||||
onAbort: () => Promise<void>
|
||||
attachmentAdapter?: AttachmentAdapter
|
||||
allowSendWhenInactive?: boolean
|
||||
pendingScheduleRef?: React.RefObject<PendingSchedule | null>
|
||||
}) {
|
||||
const isRunning = props.isRunning ?? props.session.thinking
|
||||
|
||||
@@ -316,8 +320,13 @@ export function useHappyRuntime(props: {
|
||||
const onNew = useCallback(async (message: AppendMessage) => {
|
||||
const { text, attachments } = extractMessageContent(message)
|
||||
if (!text && attachments.length === 0) return
|
||||
props.onSendMessage(text, attachments.length > 0 ? attachments : undefined)
|
||||
}, [props.onSendMessage])
|
||||
// Resolve pendingSchedule at send time (Date.now()) so preset-type schedules
|
||||
// ("5 minutes from now") are relative to the actual send action, not the
|
||||
// moment the user clicked the preset button.
|
||||
const sendNow = Date.now()
|
||||
const scheduledAt = resolvePendingSchedule(props.pendingScheduleRef?.current ?? null, sendNow)
|
||||
props.onSendMessage(text, attachments.length > 0 ? attachments : undefined, scheduledAt)
|
||||
}, [props.onSendMessage, props.pendingScheduleRef])
|
||||
|
||||
const onCancel = useCallback(async () => {
|
||||
await props.onAbort()
|
||||
@@ -344,5 +353,9 @@ export function useHappyRuntime(props: {
|
||||
props.attachmentAdapter
|
||||
])
|
||||
|
||||
// Note: pendingScheduleRef is intentionally not in the deps above.
|
||||
// The ref is read at send time inside onNew (not at render time), so changes
|
||||
// to pendingSchedule do not need to invalidate the adapter or re-run onNew.
|
||||
|
||||
return useExternalStoreRuntime(adapter)
|
||||
}
|
||||
|
||||
@@ -319,6 +319,14 @@ export default {
|
||||
'composer.send': 'Send',
|
||||
'composer.stop': 'Stop',
|
||||
'composer.voice': 'Voice assistant',
|
||||
'composer.scheduleSend': 'Schedule send',
|
||||
'composer.scheduleRelativeTab': 'Relative',
|
||||
'composer.scheduleSpecificTab': 'Specific',
|
||||
'composer.scheduleSpecificHint': 'Max 7 days. Requires the hub running; the CLI catches up the next time it connects after that time.',
|
||||
'composer.scheduleErrorPast': 'Scheduled time must be in the future.',
|
||||
'composer.scheduleErrorTooFar': 'Maximum schedule time is 7 days.',
|
||||
'queuedMessages.scheduledFor': 'Scheduled for {time}',
|
||||
'queuedMessages.editAlreadyInvoked': "Message already sent — it can't be edited",
|
||||
'composer.codexSlashUnsupported.title': 'Codex command unavailable',
|
||||
'composer.codexSlashUnsupported.body': 'HAPI remote mode does not yet run built-in Codex slash commands like {command}. Use natural language instead, or run it in the local Codex TUI.',
|
||||
|
||||
|
||||
@@ -321,6 +321,14 @@ export default {
|
||||
'composer.send': '发送',
|
||||
'composer.stop': '停止',
|
||||
'composer.voice': '语音助手',
|
||||
'composer.scheduleSend': '定时发送',
|
||||
'composer.scheduleRelativeTab': '相对时间',
|
||||
'composer.scheduleSpecificTab': '指定时间',
|
||||
'composer.scheduleSpecificHint': '最多 7 天。需 Hub 运行;CLI 下次连接时会接收消息。',
|
||||
'composer.scheduleErrorPast': '发送时间必须在未来。',
|
||||
'composer.scheduleErrorTooFar': '最多只能定时 7 天。',
|
||||
'queuedMessages.scheduledFor': '定时发送: {time}',
|
||||
'queuedMessages.editAlreadyInvoked': '消息已发送,无法编辑',
|
||||
'composer.codexSlashUnsupported.title': '无法执行 Codex 命令',
|
||||
'composer.codexSlashUnsupported.body': 'HAPI 远程模式暂不支持 {command} 这类 Codex 内建 slash command,请改用自然语言,或在本地 Codex TUI 中执行。',
|
||||
|
||||
|
||||
Reference in New Issue
Block a user