mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-18 22:54:41 +00:00
feat(app): show which messages are queued (#15587)
This commit is contained in:
@@ -46,12 +46,18 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: var(--surface-weak);
|
background: var(--surface-weak);
|
||||||
border: 1px solid var(--border-weak-base);
|
border: 1px solid var(--border-weak-base);
|
||||||
transition: border-color 0.15s ease;
|
transition:
|
||||||
|
border-color 0.15s ease,
|
||||||
|
opacity 0.3s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: var(--border-strong-base);
|
border-color: var(--border-strong-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&[data-queued] {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
&[data-type="image"] {
|
&[data-type="image"] {
|
||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
@@ -101,6 +107,11 @@
|
|||||||
border: 1px solid var(--border-weak-base);
|
border: 1px solid var(--border-weak-base);
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
|
||||||
|
&[data-queued] {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
[data-highlight="file"] {
|
[data-highlight="file"] {
|
||||||
color: var(--syntax-property);
|
color: var(--syntax-property);
|
||||||
@@ -113,6 +124,14 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-slot="user-message-queued-indicator"] {
|
||||||
|
margin-top: 6px;
|
||||||
|
margin-right: 2px;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
color: var(--text-weak);
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
[data-slot="user-message-copy-wrapper"] {
|
[data-slot="user-message-copy-wrapper"] {
|
||||||
min-height: 24px;
|
min-height: 24px;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
@@ -149,6 +168,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-slot="user-message-meta-tail"] {
|
[data-slot="user-message-meta-tail"] {
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ export interface MessageProps {
|
|||||||
parts: PartType[]
|
parts: PartType[]
|
||||||
showAssistantCopyPartID?: string | null
|
showAssistantCopyPartID?: string | null
|
||||||
interrupted?: boolean
|
interrupted?: boolean
|
||||||
|
queued?: boolean
|
||||||
showReasoningSummaries?: boolean
|
showReasoningSummaries?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,6 +501,7 @@ export function Message(props: MessageProps) {
|
|||||||
message={userMessage() as UserMessage}
|
message={userMessage() as UserMessage}
|
||||||
parts={props.parts}
|
parts={props.parts}
|
||||||
interrupted={props.interrupted}
|
interrupted={props.interrupted}
|
||||||
|
queued={props.queued}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Match>
|
</Match>
|
||||||
@@ -679,7 +681,12 @@ function ContextToolGroup(props: { parts: ToolPart[]; busy?: boolean }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UserMessageDisplay(props: { message: UserMessage; parts: PartType[]; interrupted?: boolean }) {
|
export function UserMessageDisplay(props: {
|
||||||
|
message: UserMessage
|
||||||
|
parts: PartType[]
|
||||||
|
interrupted?: boolean
|
||||||
|
queued?: boolean
|
||||||
|
}) {
|
||||||
const data = useData()
|
const data = useData()
|
||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
@@ -759,6 +766,7 @@ export function UserMessageDisplay(props: { message: UserMessage; parts: PartTyp
|
|||||||
<div
|
<div
|
||||||
data-slot="user-message-attachment"
|
data-slot="user-message-attachment"
|
||||||
data-type={file.mime.startsWith("image/") ? "image" : "file"}
|
data-type={file.mime.startsWith("image/") ? "image" : "file"}
|
||||||
|
data-queued={props.queued ? "" : undefined}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (file.mime.startsWith("image/") && file.url) {
|
if (file.mime.startsWith("image/") && file.url) {
|
||||||
openImagePreview(file.url, file.filename)
|
openImagePreview(file.url, file.filename)
|
||||||
@@ -787,9 +795,14 @@ export function UserMessageDisplay(props: { message: UserMessage; parts: PartTyp
|
|||||||
<Show when={text()}>
|
<Show when={text()}>
|
||||||
<>
|
<>
|
||||||
<div data-slot="user-message-body">
|
<div data-slot="user-message-body">
|
||||||
<div data-slot="user-message-text">
|
<div data-slot="user-message-text" data-queued={props.queued ? "" : undefined}>
|
||||||
<HighlightedText text={text()} references={inlineFiles()} agents={agents()} />
|
<HighlightedText text={text()} references={inlineFiles()} agents={agents()} />
|
||||||
</div>
|
</div>
|
||||||
|
<Show when={props.queued}>
|
||||||
|
<div data-slot="user-message-queued-indicator">
|
||||||
|
<TextShimmer text={i18n.t("ui.message.queued")} />
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
<div data-slot="user-message-copy-wrapper" data-interrupted={props.interrupted ? "" : undefined}>
|
<div data-slot="user-message-copy-wrapper" data-interrupted={props.interrupted ? "" : undefined}>
|
||||||
<Show when={metaHead() || metaTail()}>
|
<Show when={metaHead() || metaTail()}>
|
||||||
|
|||||||
@@ -192,11 +192,31 @@ export function SessionTurn(
|
|||||||
(item): item is AssistantMessage => item.role === "assistant" && typeof item.time.completed !== "number",
|
(item): item is AssistantMessage => item.role === "assistant" && typeof item.time.completed !== "number",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const pendingUser = createMemo(() => {
|
||||||
|
const item = pending()
|
||||||
|
if (!item?.parentID) return
|
||||||
|
const messages = allMessages() ?? emptyMessages
|
||||||
|
const result = Binary.search(messages, item.parentID, (m) => m.id)
|
||||||
|
const msg = result.found ? messages[result.index] : messages.find((m) => m.id === item.parentID)
|
||||||
|
if (!msg || msg.role !== "user") return
|
||||||
|
return msg
|
||||||
|
})
|
||||||
|
|
||||||
const active = createMemo(() => {
|
const active = createMemo(() => {
|
||||||
const msg = message()
|
const msg = message()
|
||||||
|
const parent = pendingUser()
|
||||||
|
if (!msg || !parent) return false
|
||||||
|
return parent.id === msg.id
|
||||||
|
})
|
||||||
|
|
||||||
|
const queued = createMemo(() => {
|
||||||
|
const id = message()?.id
|
||||||
|
if (!id) return false
|
||||||
|
if (!pendingUser()) return false
|
||||||
const item = pending()
|
const item = pending()
|
||||||
if (!msg || !item) return false
|
if (!item) return false
|
||||||
return item.parentID === msg.id
|
return id > item.id
|
||||||
})
|
})
|
||||||
|
|
||||||
const parts = createMemo(() => {
|
const parts = createMemo(() => {
|
||||||
@@ -334,6 +354,7 @@ export function SessionTurn(
|
|||||||
)
|
)
|
||||||
const showThinking = createMemo(() => {
|
const showThinking = createMemo(() => {
|
||||||
if (!working() || !!error()) return false
|
if (!working() || !!error()) return false
|
||||||
|
if (queued()) return false
|
||||||
if (status().type === "retry") return false
|
if (status().type === "retry") return false
|
||||||
if (showReasoningSummaries()) return assistantVisible() === 0
|
if (showReasoningSummaries()) return assistantVisible() === 0
|
||||||
if (assistantTailVisible() === "text") return false
|
if (assistantTailVisible() === "text") return false
|
||||||
@@ -364,7 +385,7 @@ export function SessionTurn(
|
|||||||
class={props.classes?.container}
|
class={props.classes?.container}
|
||||||
>
|
>
|
||||||
<div data-slot="session-turn-message-content" aria-live="off">
|
<div data-slot="session-turn-message-content" aria-live="off">
|
||||||
<Message message={msg()} parts={parts()} interrupted={interrupted()} />
|
<Message message={msg()} parts={parts()} interrupted={interrupted()} queued={queued()} />
|
||||||
</div>
|
</div>
|
||||||
<Show when={compaction()}>
|
<Show when={compaction()}>
|
||||||
{(part) => (
|
{(part) => (
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ export const dict = {
|
|||||||
"ui.message.copyResponse": "نسخ الرد",
|
"ui.message.copyResponse": "نسخ الرد",
|
||||||
"ui.message.copied": "تم النسخ!",
|
"ui.message.copied": "تم النسخ!",
|
||||||
"ui.message.interrupted": "تمت المقاطعة",
|
"ui.message.interrupted": "تمت المقاطعة",
|
||||||
|
"ui.message.queued": "في الانتظار",
|
||||||
"ui.message.attachment.alt": "مرفق",
|
"ui.message.attachment.alt": "مرفق",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "محذوف",
|
"ui.patch.action.deleted": "محذوف",
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ export const dict = {
|
|||||||
"ui.message.copyResponse": "Copiar resposta",
|
"ui.message.copyResponse": "Copiar resposta",
|
||||||
"ui.message.copied": "Copiado!",
|
"ui.message.copied": "Copiado!",
|
||||||
"ui.message.interrupted": "Interrompido",
|
"ui.message.interrupted": "Interrompido",
|
||||||
|
"ui.message.queued": "Na fila",
|
||||||
"ui.message.attachment.alt": "anexo",
|
"ui.message.attachment.alt": "anexo",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "Excluído",
|
"ui.patch.action.deleted": "Excluído",
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ export const dict = {
|
|||||||
"ui.message.copyResponse": "Kopiraj odgovor",
|
"ui.message.copyResponse": "Kopiraj odgovor",
|
||||||
"ui.message.copied": "Kopirano!",
|
"ui.message.copied": "Kopirano!",
|
||||||
"ui.message.interrupted": "Prekinuto",
|
"ui.message.interrupted": "Prekinuto",
|
||||||
|
"ui.message.queued": "U redu",
|
||||||
"ui.message.attachment.alt": "prilog",
|
"ui.message.attachment.alt": "prilog",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "Obrisano",
|
"ui.patch.action.deleted": "Obrisano",
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ export const dict = {
|
|||||||
"ui.message.copyResponse": "Kopier svar",
|
"ui.message.copyResponse": "Kopier svar",
|
||||||
"ui.message.copied": "Kopieret!",
|
"ui.message.copied": "Kopieret!",
|
||||||
"ui.message.interrupted": "Afbrudt",
|
"ui.message.interrupted": "Afbrudt",
|
||||||
|
"ui.message.queued": "I kø",
|
||||||
"ui.message.attachment.alt": "vedhæftning",
|
"ui.message.attachment.alt": "vedhæftning",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "Slettet",
|
"ui.patch.action.deleted": "Slettet",
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ export const dict = {
|
|||||||
"ui.message.copyResponse": "Antwort kopieren",
|
"ui.message.copyResponse": "Antwort kopieren",
|
||||||
"ui.message.copied": "Kopiert!",
|
"ui.message.copied": "Kopiert!",
|
||||||
"ui.message.interrupted": "Unterbrochen",
|
"ui.message.interrupted": "Unterbrochen",
|
||||||
|
"ui.message.queued": "In Warteschlange",
|
||||||
"ui.message.attachment.alt": "Anhang",
|
"ui.message.attachment.alt": "Anhang",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "Gelöscht",
|
"ui.patch.action.deleted": "Gelöscht",
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ export const dict: Record<string, string> = {
|
|||||||
"ui.message.copyResponse": "Copy response",
|
"ui.message.copyResponse": "Copy response",
|
||||||
"ui.message.copied": "Copied",
|
"ui.message.copied": "Copied",
|
||||||
"ui.message.interrupted": "Interrupted",
|
"ui.message.interrupted": "Interrupted",
|
||||||
|
"ui.message.queued": "Queued",
|
||||||
"ui.message.attachment.alt": "attachment",
|
"ui.message.attachment.alt": "attachment",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "Deleted",
|
"ui.patch.action.deleted": "Deleted",
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ export const dict = {
|
|||||||
"ui.message.copyResponse": "Copiar respuesta",
|
"ui.message.copyResponse": "Copiar respuesta",
|
||||||
"ui.message.copied": "¡Copiado!",
|
"ui.message.copied": "¡Copiado!",
|
||||||
"ui.message.interrupted": "Interrumpido",
|
"ui.message.interrupted": "Interrumpido",
|
||||||
|
"ui.message.queued": "En cola",
|
||||||
"ui.message.attachment.alt": "adjunto",
|
"ui.message.attachment.alt": "adjunto",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "Eliminado",
|
"ui.patch.action.deleted": "Eliminado",
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ export const dict = {
|
|||||||
"ui.message.copyResponse": "Copier la réponse",
|
"ui.message.copyResponse": "Copier la réponse",
|
||||||
"ui.message.copied": "Copié !",
|
"ui.message.copied": "Copié !",
|
||||||
"ui.message.interrupted": "Interrompu",
|
"ui.message.interrupted": "Interrompu",
|
||||||
|
"ui.message.queued": "En file",
|
||||||
"ui.message.attachment.alt": "pièce jointe",
|
"ui.message.attachment.alt": "pièce jointe",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "Supprimé",
|
"ui.patch.action.deleted": "Supprimé",
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ export const dict = {
|
|||||||
"ui.message.copyResponse": "応答をコピー",
|
"ui.message.copyResponse": "応答をコピー",
|
||||||
"ui.message.copied": "コピーしました!",
|
"ui.message.copied": "コピーしました!",
|
||||||
"ui.message.interrupted": "中断",
|
"ui.message.interrupted": "中断",
|
||||||
|
"ui.message.queued": "待機中",
|
||||||
"ui.message.attachment.alt": "添付ファイル",
|
"ui.message.attachment.alt": "添付ファイル",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "削除済み",
|
"ui.patch.action.deleted": "削除済み",
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ export const dict = {
|
|||||||
"ui.message.copyResponse": "응답 복사",
|
"ui.message.copyResponse": "응답 복사",
|
||||||
"ui.message.copied": "복사됨!",
|
"ui.message.copied": "복사됨!",
|
||||||
"ui.message.interrupted": "중단됨",
|
"ui.message.interrupted": "중단됨",
|
||||||
|
"ui.message.queued": "대기 중",
|
||||||
"ui.message.attachment.alt": "첨부 파일",
|
"ui.message.attachment.alt": "첨부 파일",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "삭제됨",
|
"ui.patch.action.deleted": "삭제됨",
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ export const dict: Record<Keys, string> = {
|
|||||||
"ui.message.copyResponse": "Kopier svar",
|
"ui.message.copyResponse": "Kopier svar",
|
||||||
"ui.message.copied": "Kopiert!",
|
"ui.message.copied": "Kopiert!",
|
||||||
"ui.message.interrupted": "Avbrutt",
|
"ui.message.interrupted": "Avbrutt",
|
||||||
|
"ui.message.queued": "I kø",
|
||||||
"ui.message.attachment.alt": "vedlegg",
|
"ui.message.attachment.alt": "vedlegg",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "Slettet",
|
"ui.patch.action.deleted": "Slettet",
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ export const dict = {
|
|||||||
"ui.message.copyResponse": "Kopiuj odpowiedź",
|
"ui.message.copyResponse": "Kopiuj odpowiedź",
|
||||||
"ui.message.copied": "Skopiowano!",
|
"ui.message.copied": "Skopiowano!",
|
||||||
"ui.message.interrupted": "Przerwano",
|
"ui.message.interrupted": "Przerwano",
|
||||||
|
"ui.message.queued": "W kolejce",
|
||||||
"ui.message.attachment.alt": "załącznik",
|
"ui.message.attachment.alt": "załącznik",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "Usunięto",
|
"ui.patch.action.deleted": "Usunięto",
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ export const dict = {
|
|||||||
"ui.message.copyResponse": "Копировать ответ",
|
"ui.message.copyResponse": "Копировать ответ",
|
||||||
"ui.message.copied": "Скопировано!",
|
"ui.message.copied": "Скопировано!",
|
||||||
"ui.message.interrupted": "Прервано",
|
"ui.message.interrupted": "Прервано",
|
||||||
|
"ui.message.queued": "В очереди",
|
||||||
"ui.message.attachment.alt": "вложение",
|
"ui.message.attachment.alt": "вложение",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "Удалено",
|
"ui.patch.action.deleted": "Удалено",
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ export const dict = {
|
|||||||
"ui.message.copyResponse": "คัดลอกคำตอบ",
|
"ui.message.copyResponse": "คัดลอกคำตอบ",
|
||||||
"ui.message.copied": "คัดลอกแล้ว!",
|
"ui.message.copied": "คัดลอกแล้ว!",
|
||||||
"ui.message.interrupted": "ถูกขัดจังหวะ",
|
"ui.message.interrupted": "ถูกขัดจังหวะ",
|
||||||
|
"ui.message.queued": "อยู่ในคิว",
|
||||||
"ui.message.attachment.alt": "ไฟล์แนบ",
|
"ui.message.attachment.alt": "ไฟล์แนบ",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "ลบ",
|
"ui.patch.action.deleted": "ลบ",
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ export const dict = {
|
|||||||
"ui.message.copyResponse": "Yanıtı kopyala",
|
"ui.message.copyResponse": "Yanıtı kopyala",
|
||||||
"ui.message.copied": "Kopyalandı",
|
"ui.message.copied": "Kopyalandı",
|
||||||
"ui.message.interrupted": "Kesildi",
|
"ui.message.interrupted": "Kesildi",
|
||||||
|
"ui.message.queued": "Sırada",
|
||||||
"ui.message.attachment.alt": "ek",
|
"ui.message.attachment.alt": "ek",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "Silindi",
|
"ui.patch.action.deleted": "Silindi",
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ export const dict = {
|
|||||||
"ui.message.copyResponse": "复制回复",
|
"ui.message.copyResponse": "复制回复",
|
||||||
"ui.message.copied": "已复制!",
|
"ui.message.copied": "已复制!",
|
||||||
"ui.message.interrupted": "已中断",
|
"ui.message.interrupted": "已中断",
|
||||||
|
"ui.message.queued": "排队中",
|
||||||
"ui.message.attachment.alt": "附件",
|
"ui.message.attachment.alt": "附件",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "已删除",
|
"ui.patch.action.deleted": "已删除",
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ export const dict = {
|
|||||||
"ui.message.copyResponse": "複製回覆",
|
"ui.message.copyResponse": "複製回覆",
|
||||||
"ui.message.copied": "已複製!",
|
"ui.message.copied": "已複製!",
|
||||||
"ui.message.interrupted": "已中斷",
|
"ui.message.interrupted": "已中斷",
|
||||||
|
"ui.message.queued": "排隊中",
|
||||||
"ui.message.attachment.alt": "附件",
|
"ui.message.attachment.alt": "附件",
|
||||||
|
|
||||||
"ui.patch.action.deleted": "已刪除",
|
"ui.patch.action.deleted": "已刪除",
|
||||||
|
|||||||
Reference in New Issue
Block a user