feat: complete mindspace conversation packages
This commit is contained in:
@@ -749,6 +749,20 @@ export async function uploadMindSpaceAsset(
|
||||
}
|
||||
}
|
||||
|
||||
export async function claimMindSpaceConversationUploads(
|
||||
sessionId: string,
|
||||
messageId: string,
|
||||
): Promise<{ claimedCount: number }> {
|
||||
const result = await apiFetch<{ data: { claimedCount: number } }>(
|
||||
`/mindspace/v1/conversation-packages/${encodeURIComponent(sessionId)}/claim-uploads`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ message_id: messageId }),
|
||||
},
|
||||
);
|
||||
return result.data;
|
||||
}
|
||||
|
||||
function uploadFileContent(
|
||||
url: string,
|
||||
file: File,
|
||||
|
||||
+180
-58
@@ -73,6 +73,7 @@ type PendingChatImage = {
|
||||
previewUrl: string;
|
||||
sizeBytes: number;
|
||||
uploadedUrl: string | null;
|
||||
sourceMessageId: string | null;
|
||||
uploadProgress: number | null;
|
||||
uploadStatus: 'queued' | 'uploading' | 'uploaded' | 'error';
|
||||
};
|
||||
@@ -195,8 +196,17 @@ export function ChatPanel({
|
||||
session: Session | null;
|
||||
capabilities?: CapabilityMap;
|
||||
grantedSkills?: string[];
|
||||
onSubmit: (text: string, imageUrls?: string[], previewImageUrls?: string[]) => void | Promise<void>;
|
||||
onUploadImage?: (file: File, onProgress?: (progress: number) => void) => Promise<string>;
|
||||
onSubmit: (
|
||||
text: string,
|
||||
imageUrls?: string[],
|
||||
previewImageUrls?: string[],
|
||||
options?: { messageId?: string },
|
||||
) => void | Promise<void>;
|
||||
onUploadImage?: (
|
||||
file: File,
|
||||
onProgress?: (progress: number) => void,
|
||||
options?: { messageId?: string },
|
||||
) => Promise<string>;
|
||||
onLoadOlderMessages?: () => void | Promise<void>;
|
||||
onStop: () => void | Promise<void>;
|
||||
onApproveTool: (allow: boolean) => void | Promise<void>;
|
||||
@@ -221,6 +231,8 @@ export function ChatPanel({
|
||||
const [packageLoading, setPackageLoading] = useState(false);
|
||||
const [conversationPackage, setConversationPackage] = useState<MindSpaceConversationPackage | null>(null);
|
||||
const [packageError, setPackageError] = useState<string | null>(null);
|
||||
const [packageFilter, setPackageFilter] = useState<MindSpaceConversationArtifact['kind'] | 'all'>('all');
|
||||
const [packageMessageFilter, setPackageMessageFilter] = useState<string | 'all'>('all');
|
||||
const [randomPrompt] = useState(
|
||||
() => CHAT_PLACEHOLDER_PROMPTS[Math.floor(Math.random() * CHAT_PLACEHOLDER_PROMPTS.length)],
|
||||
);
|
||||
@@ -291,6 +303,8 @@ export function ChatPanel({
|
||||
setPackageLoading(false);
|
||||
setConversationPackage(null);
|
||||
setPackageError(null);
|
||||
setPackageFilter('all');
|
||||
setPackageMessageFilter('all');
|
||||
}, [session?.id]);
|
||||
|
||||
const loadConversationPackage = useCallback(async () => {
|
||||
@@ -300,6 +314,12 @@ export function ChatPanel({
|
||||
try {
|
||||
const data = await getMindSpaceConversationPackage(session.id);
|
||||
setConversationPackage(data);
|
||||
setPackageFilter((current) =>
|
||||
current === 'all' || data.artifacts.some((artifact) => artifact.kind === current) ? current : 'all',
|
||||
);
|
||||
setPackageMessageFilter((current) =>
|
||||
current === 'all' || (data.messages ?? []).some((message) => message.messageId === current) ? current : 'all',
|
||||
);
|
||||
} catch (err) {
|
||||
setConversationPackage(null);
|
||||
if (err instanceof ApiError && err.status === 404) {
|
||||
@@ -437,6 +457,32 @@ export function ChatPanel({
|
||||
const voiceDisabled = busy || !!pendingTool || chatState === 'error' || offlineBlocked;
|
||||
const canSubmit = input.trim() || pendingImages.length > 0;
|
||||
const imageAttachmentDisabled = !onUploadImage || busy || uploadingImage || offlineBlocked || !!pendingTool;
|
||||
const packageArtifacts = conversationPackage?.artifacts ?? [];
|
||||
const packageFilterOptions = Array.from(
|
||||
packageArtifacts.reduce((counts, artifact) => {
|
||||
counts.set(artifact.kind, (counts.get(artifact.kind) ?? 0) + 1);
|
||||
return counts;
|
||||
}, new Map<MindSpaceConversationArtifact['kind'], number>()),
|
||||
).sort(([left], [right]) => packageArtifactLabel(left).localeCompare(packageArtifactLabel(right), 'zh-CN'));
|
||||
const packageMessageOptions = (conversationPackage?.messages ?? [])
|
||||
.filter((message) => message.artifactIds.length > 0)
|
||||
.map((message, index) => ({
|
||||
id: message.messageId,
|
||||
label: `消息 ${index + 1}`,
|
||||
count: message.artifactIds.length,
|
||||
artifactIds: new Set(message.artifactIds),
|
||||
}));
|
||||
const selectedMessageArtifacts =
|
||||
packageMessageFilter === 'all'
|
||||
? null
|
||||
: packageMessageOptions.find((message) => message.id === packageMessageFilter)?.artifactIds ?? null;
|
||||
const filteredPackageArtifacts =
|
||||
packageArtifacts.filter((artifact) => {
|
||||
const kindMatched = packageFilter === 'all' || artifact.kind === packageFilter;
|
||||
const messageMatched = !selectedMessageArtifacts || selectedMessageArtifacts.has(artifact.artifactId);
|
||||
return kindMatched && messageMatched;
|
||||
});
|
||||
const packageGroups = groupPackageArtifacts(filteredPackageArtifacts);
|
||||
|
||||
const revokePendingImage = (item: PendingChatImage) => {
|
||||
if (item.previewUrl.startsWith('blob:')) URL.revokeObjectURL(item.previewUrl);
|
||||
@@ -505,7 +551,12 @@ export function ChatPanel({
|
||||
setImageError('当前会话暂不支持图片发送');
|
||||
return;
|
||||
}
|
||||
const sentImages = [...pendingImages];
|
||||
const existingSourceMessageId = pendingImages.find((item) => item.sourceMessageId)?.sourceMessageId;
|
||||
const outgoingMessageId = existingSourceMessageId ?? crypto.randomUUID();
|
||||
const sentImages = pendingImages.map((item) => ({
|
||||
...item,
|
||||
sourceMessageId: item.sourceMessageId ?? outgoingMessageId,
|
||||
}));
|
||||
let uploadedImages = sentImages;
|
||||
|
||||
suppressVoiceUpdateRef.current = true;
|
||||
@@ -524,28 +575,39 @@ export function ChatPanel({
|
||||
setPendingImages((current) =>
|
||||
current.map((candidate) =>
|
||||
candidate.id === item.id
|
||||
? { ...candidate, uploadStatus: 'uploading', uploadProgress: 0 }
|
||||
? {
|
||||
...candidate,
|
||||
sourceMessageId: item.sourceMessageId,
|
||||
uploadStatus: 'uploading',
|
||||
uploadProgress: 0,
|
||||
}
|
||||
: candidate,
|
||||
),
|
||||
);
|
||||
const uploadedUrl = await onUploadImage!(item.file, (progress) => {
|
||||
setPendingImages((current) =>
|
||||
current.map((candidate) =>
|
||||
candidate.id === item.id
|
||||
? {
|
||||
...candidate,
|
||||
uploadStatus: 'uploading',
|
||||
uploadProgress: Math.round(progress * 100),
|
||||
}
|
||||
: candidate,
|
||||
),
|
||||
);
|
||||
});
|
||||
const uploadedUrl = await onUploadImage!(
|
||||
item.file,
|
||||
(progress) => {
|
||||
setPendingImages((current) =>
|
||||
current.map((candidate) =>
|
||||
candidate.id === item.id
|
||||
? {
|
||||
...candidate,
|
||||
sourceMessageId: item.sourceMessageId,
|
||||
uploadStatus: 'uploading',
|
||||
uploadProgress: Math.round(progress * 100),
|
||||
}
|
||||
: candidate,
|
||||
),
|
||||
);
|
||||
},
|
||||
{ messageId: item.sourceMessageId ?? outgoingMessageId },
|
||||
);
|
||||
setPendingImages((current) =>
|
||||
current.map((candidate) =>
|
||||
candidate.id === item.id
|
||||
? {
|
||||
...candidate,
|
||||
sourceMessageId: item.sourceMessageId,
|
||||
uploadedUrl,
|
||||
uploadStatus: 'uploaded',
|
||||
uploadProgress: 100,
|
||||
@@ -564,7 +626,7 @@ export function ChatPanel({
|
||||
}));
|
||||
const imagesToSend = uploadedUrls.filter(Boolean);
|
||||
const previewImagesToSend = imagesToSend;
|
||||
await onSubmit(trimmed, imagesToSend, previewImagesToSend);
|
||||
await onSubmit(trimmed, imagesToSend, previewImagesToSend, { messageId: outgoingMessageId });
|
||||
uploadedImages.forEach(revokePendingImage);
|
||||
setPendingImages([]);
|
||||
} catch (err) {
|
||||
@@ -607,6 +669,7 @@ export function ChatPanel({
|
||||
previewUrl: URL.createObjectURL(file),
|
||||
sizeBytes: file.size,
|
||||
uploadedUrl: null,
|
||||
sourceMessageId: null,
|
||||
uploadProgress: null,
|
||||
uploadStatus: 'queued' as const,
|
||||
}));
|
||||
@@ -639,6 +702,9 @@ export function ChatPanel({
|
||||
previewUrl: URL.createObjectURL(file),
|
||||
sizeBytes: file.size,
|
||||
uploadedUrl: null,
|
||||
sourceMessageId: null,
|
||||
uploadProgress: null,
|
||||
uploadStatus: 'queued' as const,
|
||||
}));
|
||||
setPendingImages((prev) => [...prev, ...placeholders]);
|
||||
};
|
||||
@@ -774,55 +840,111 @@ export function ChatPanel({
|
||||
</button>
|
||||
</div>
|
||||
<div className="conversation-package-meta">
|
||||
<span>{conversationPackage?.artifacts.length ?? 0} 个项目</span>
|
||||
<span>{packageArtifacts.length} 个项目</span>
|
||||
{conversationPackage?.uri && <code>{conversationPackage.uri}</code>}
|
||||
</div>
|
||||
{packageLoading && <div className="conversation-package-state">正在整理这个对话里的文件…</div>}
|
||||
{!packageLoading && packageError && (
|
||||
<div className="conversation-package-state conversation-package-state-muted">{packageError}</div>
|
||||
)}
|
||||
{!packageLoading && !packageError && conversationPackage && conversationPackage.artifacts.length === 0 && (
|
||||
<div className="conversation-package-state conversation-package-state-muted">
|
||||
这个对话暂时没有图片、文件或页面。
|
||||
<strong>对话包暂时打不开</strong>
|
||||
<span>{packageError}</span>
|
||||
<button type="button" className="ghost-btn" onClick={() => void loadConversationPackage()}>
|
||||
重试加载
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{!packageLoading && !packageError && conversationPackage && conversationPackage.artifacts.length > 0 && (
|
||||
<div className="conversation-package-groups">
|
||||
{groupPackageArtifacts(conversationPackage.artifacts).map(([label, artifacts]) => (
|
||||
<div className="conversation-package-group" key={label}>
|
||||
<h3>{label}</h3>
|
||||
<div className="conversation-package-items">
|
||||
{artifacts.map((artifact) => {
|
||||
const sizeLabel = formatPackageArtifactSize(artifact.sizeBytes);
|
||||
return (
|
||||
<a
|
||||
key={artifact.artifactId}
|
||||
className="conversation-package-item"
|
||||
href={artifact.canonicalUrl || undefined}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
aria-disabled={!artifact.canonicalUrl}
|
||||
onClick={(event) => {
|
||||
if (!artifact.canonicalUrl) event.preventDefault();
|
||||
}}
|
||||
>
|
||||
<span className="conversation-package-item-kind">{packageArtifactLabel(artifact.kind)}</span>
|
||||
<strong>{artifact.displayName || artifact.canonicalUrl || artifact.artifactId}</strong>
|
||||
<span>
|
||||
{[
|
||||
artifact.mimeType,
|
||||
sizeLabel,
|
||||
artifact.messageId ? `消息 ${artifact.messageId}` : null,
|
||||
].filter(Boolean).join(' · ') || '已记录到对话包'}
|
||||
</span>
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{!packageLoading && !packageError && conversationPackage && packageArtifacts.length === 0 && (
|
||||
<div className="conversation-package-state conversation-package-state-muted">
|
||||
<strong>这个对话还没有文件</strong>
|
||||
<span>发送图片、保存页面、生成公开页或下载文档后,这里会自动聚合成一个对话文件夹。</span>
|
||||
</div>
|
||||
)}
|
||||
{!packageLoading && !packageError && conversationPackage && packageArtifacts.length > 0 && (
|
||||
<>
|
||||
<div className="conversation-package-filters" aria-label="筛选对话包文件">
|
||||
<button
|
||||
type="button"
|
||||
className={packageFilter === 'all' ? 'is-active' : ''}
|
||||
onClick={() => setPackageFilter('all')}
|
||||
>
|
||||
全部 <span>{packageArtifacts.length}</span>
|
||||
</button>
|
||||
{packageFilterOptions.map(([kind, count]) => (
|
||||
<button
|
||||
type="button"
|
||||
key={kind}
|
||||
className={packageFilter === kind ? 'is-active' : ''}
|
||||
onClick={() => setPackageFilter(kind)}
|
||||
>
|
||||
{packageArtifactLabel(kind)} <span>{count}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{packageMessageOptions.length > 0 && (
|
||||
<div className="conversation-package-message-filters" aria-label="按消息筛选对话包文件">
|
||||
<span>按消息</span>
|
||||
<button
|
||||
type="button"
|
||||
className={packageMessageFilter === 'all' ? 'is-active' : ''}
|
||||
onClick={() => setPackageMessageFilter('all')}
|
||||
>
|
||||
全部消息
|
||||
</button>
|
||||
{packageMessageOptions.map((message) => (
|
||||
<button
|
||||
type="button"
|
||||
key={message.id}
|
||||
className={packageMessageFilter === message.id ? 'is-active' : ''}
|
||||
onClick={() => setPackageMessageFilter(message.id)}
|
||||
>
|
||||
{message.label} <span>{message.count}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{filteredPackageArtifacts.length === 0 ? (
|
||||
<div className="conversation-package-state conversation-package-state-muted">
|
||||
<strong>当前筛选没有项目</strong>
|
||||
<span>切换到“全部”或“全部消息”可以查看当前对话包里的其它产物。</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="conversation-package-groups">
|
||||
{packageGroups.map(([label, artifacts]) => (
|
||||
<div className="conversation-package-group" key={label}>
|
||||
<h3>{label}</h3>
|
||||
<div className="conversation-package-items">
|
||||
{artifacts.map((artifact) => {
|
||||
const sizeLabel = formatPackageArtifactSize(artifact.sizeBytes);
|
||||
return (
|
||||
<a
|
||||
key={artifact.artifactId}
|
||||
className="conversation-package-item"
|
||||
href={artifact.canonicalUrl || undefined}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
aria-disabled={!artifact.canonicalUrl}
|
||||
onClick={(event) => {
|
||||
if (!artifact.canonicalUrl) event.preventDefault();
|
||||
}}
|
||||
>
|
||||
<span className="conversation-package-item-kind">{packageArtifactLabel(artifact.kind)}</span>
|
||||
<strong>{artifact.displayName || artifact.canonicalUrl || artifact.artifactId}</strong>
|
||||
<span>
|
||||
{[
|
||||
artifact.mimeType,
|
||||
sizeLabel,
|
||||
].filter(Boolean).join(' · ') || '已记录到对话包'}
|
||||
</span>
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<div className="conversation-package-actions">
|
||||
<button type="button" className="ghost-btn" onClick={() => void loadConversationPackage()}>
|
||||
刷新
|
||||
|
||||
@@ -294,10 +294,12 @@ export function ChatView({
|
||||
session={session}
|
||||
capabilities={capabilities}
|
||||
grantedSkills={grantedSkills}
|
||||
onSubmit={(text, imageUrls, previewImageUrls) =>
|
||||
void submit(text, undefined, imageUrls, previewImageUrls)
|
||||
onSubmit={(text, imageUrls, previewImageUrls, options) =>
|
||||
void submit(text, { messageId: options?.messageId }, imageUrls, previewImageUrls)
|
||||
}
|
||||
onUploadImage={(file, onProgress, options) =>
|
||||
uploadChatImage(file, onProgress, { messageId: options?.messageId })
|
||||
}
|
||||
onUploadImage={uploadChatImage}
|
||||
onLoadOlderMessages={loadOlderMessages}
|
||||
onStop={stop}
|
||||
onApproveTool={approveTool}
|
||||
|
||||
@@ -138,12 +138,14 @@ export function SpaceChatPanel({
|
||||
session={session}
|
||||
capabilities={capabilities ?? undefined}
|
||||
grantedSkills={grantedSkills}
|
||||
onSubmit={(text, imageUrls, previewImageUrls) =>
|
||||
onSubmit={(text, imageUrls, previewImageUrls, options) =>
|
||||
chatBridge
|
||||
? void submit(text, context, imageUrls, previewImageUrls)
|
||||
: void submit(text, { mindspaceContext: context }, imageUrls, previewImageUrls)
|
||||
? void submit(text, { ...context, messageId: options?.messageId }, imageUrls, previewImageUrls)
|
||||
: void submit(text, { mindspaceContext: context, messageId: options?.messageId }, imageUrls, previewImageUrls)
|
||||
}
|
||||
onUploadImage={(file, onProgress, options) =>
|
||||
uploadChatImage(file, onProgress, { messageId: options?.messageId })
|
||||
}
|
||||
onUploadImage={uploadChatImage}
|
||||
onStop={stop}
|
||||
onApproveTool={approveTool}
|
||||
onPageSaved={onPageSaved}
|
||||
|
||||
@@ -130,6 +130,7 @@ export function usePageEditSubChat({
|
||||
const uploadChatImage = useCallback(async (
|
||||
file: File,
|
||||
onProgress?: (progress: number) => void,
|
||||
options: { messageId?: string | null } = {},
|
||||
): Promise<string> => {
|
||||
if (!file.type.startsWith('image/')) {
|
||||
throw new Error('只支持图片文件');
|
||||
@@ -138,6 +139,8 @@ export function usePageEditSubChat({
|
||||
const asset = await uploadMindSpaceAsset(categoryId, file, {
|
||||
maxImageBytes: CHAT_IMAGE_UPLOAD_MAX_INPUT_BYTES,
|
||||
onProgress,
|
||||
sessionId: sessionRef.current?.id ?? null,
|
||||
messageId: options.messageId ?? null,
|
||||
});
|
||||
return buildAbsoluteAssetImageUrl({
|
||||
id: asset.id,
|
||||
@@ -305,7 +308,7 @@ export function usePageEditSubChat({
|
||||
const submit = useCallback(
|
||||
async (
|
||||
text: string,
|
||||
context: MindSpaceChatContext,
|
||||
context: MindSpaceChatContext & { messageId?: string },
|
||||
imageUrls?: string[],
|
||||
previewImageUrls?: string[],
|
||||
) => {
|
||||
@@ -327,6 +330,7 @@ export function usePageEditSubChat({
|
||||
const userPrefix = buildUserAddressPrefix(user);
|
||||
const agentPrefix = `${userPrefix}${mindspacePrefix}`;
|
||||
const userMessage = buildUserMessage(trimmed, {
|
||||
id: context.messageId,
|
||||
agentText: `${agentPrefix}${trimmed}`,
|
||||
displayText: trimmed,
|
||||
imageUrls: normalizedImageUrls,
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
applyLocalLlmFallback,
|
||||
bootstrapProjectMemory,
|
||||
cancelRequest,
|
||||
claimMindSpaceConversationUploads,
|
||||
confirmTool,
|
||||
createAgentRun,
|
||||
deleteChatSession,
|
||||
@@ -572,6 +573,7 @@ export function useTKMindChat(
|
||||
const uploadChatImage = useCallback(async (
|
||||
file: File,
|
||||
onProgress?: (progress: number) => void,
|
||||
options: { messageId?: string | null } = {},
|
||||
): Promise<string> => {
|
||||
if (!file.type.startsWith('image/')) {
|
||||
throw new Error('只支持图片文件');
|
||||
@@ -581,6 +583,7 @@ export function useTKMindChat(
|
||||
maxImageBytes: CHAT_IMAGE_UPLOAD_MAX_INPUT_BYTES,
|
||||
onProgress,
|
||||
sessionId: sessionRef.current?.id ?? null,
|
||||
messageId: options.messageId ?? null,
|
||||
});
|
||||
return buildAbsoluteAssetImageUrl({
|
||||
id: asset.id,
|
||||
@@ -1122,7 +1125,7 @@ export function useTKMindChat(
|
||||
const submit = useCallback(
|
||||
async (
|
||||
text: string,
|
||||
options?: { mindspaceContext?: MindSpaceChatContext },
|
||||
options?: { mindspaceContext?: MindSpaceChatContext; messageId?: string },
|
||||
imageUrls?: string[],
|
||||
previewImageUrls?: string[],
|
||||
) => {
|
||||
@@ -1146,6 +1149,7 @@ export function useTKMindChat(
|
||||
const skillPrefix = buildAutoChatSkillPrefix(trimmed, grantedSkills ?? []);
|
||||
const agentPrefix = `${userPrefix}${mindspacePrefix}${skillPrefix}`;
|
||||
const userMessage = buildUserMessage(trimmed, {
|
||||
id: options?.messageId,
|
||||
agentText: `${agentPrefix}${trimmed}`,
|
||||
displayText: trimmed,
|
||||
imageUrls: normalizedImageUrls,
|
||||
@@ -1184,6 +1188,9 @@ export function useTKMindChat(
|
||||
if (!activeSessionId) {
|
||||
throw new Error('后台任务已提交,但未返回会话');
|
||||
}
|
||||
if (normalizedImageUrls.length > 0) {
|
||||
void claimMindSpaceConversationUploads(activeSessionId, userMessage.id).catch(() => {});
|
||||
}
|
||||
if (!session?.id || session.id !== activeSessionId) {
|
||||
const nextSession: Session = {
|
||||
id: activeSessionId,
|
||||
|
||||
@@ -2141,6 +2141,9 @@ body,
|
||||
}
|
||||
|
||||
.conversation-package-state {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
justify-items: center;
|
||||
padding: 28px 12px;
|
||||
border: 1px dashed rgba(158, 178, 202, 0.25);
|
||||
border-radius: 16px;
|
||||
@@ -2148,10 +2151,75 @@ body,
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.conversation-package-state strong {
|
||||
color: #edf6ff;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.conversation-package-state span {
|
||||
max-width: 460px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.conversation-package-state-muted {
|
||||
color: #95a9be;
|
||||
}
|
||||
|
||||
.conversation-package-filters {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
overflow-x: auto;
|
||||
padding: 2px 2px 4px;
|
||||
}
|
||||
|
||||
.conversation-package-message-filters {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
overflow-x: auto;
|
||||
margin-top: -8px;
|
||||
padding: 0 2px 4px;
|
||||
}
|
||||
|
||||
.conversation-package-message-filters > span:first-child {
|
||||
flex: 0 0 auto;
|
||||
color: #7f94ad;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.conversation-package-filters button,
|
||||
.conversation-package-message-filters button {
|
||||
border: 1px solid rgba(151, 176, 204, 0.18);
|
||||
border-radius: 999px;
|
||||
padding: 7px 10px;
|
||||
color: #afc1d5;
|
||||
background: rgba(255, 255, 255, 0.055);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.conversation-package-filters button:hover,
|
||||
.conversation-package-filters button.is-active,
|
||||
.conversation-package-message-filters button:hover,
|
||||
.conversation-package-message-filters button.is-active {
|
||||
border-color: rgba(255, 213, 157, 0.44);
|
||||
color: #ffe3bd;
|
||||
background: rgba(255, 189, 113, 0.14);
|
||||
}
|
||||
|
||||
.conversation-package-filters button span,
|
||||
.conversation-package-message-filters button span {
|
||||
margin-left: 4px;
|
||||
color: #7f94ad;
|
||||
}
|
||||
|
||||
.conversation-package-filters button.is-active span,
|
||||
.conversation-package-message-filters button.is-active span {
|
||||
color: #ffd59d;
|
||||
}
|
||||
|
||||
.conversation-package-groups {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -496,6 +496,20 @@ export type MindSpaceConversationPackage = {
|
||||
createdAt: number;
|
||||
updatedAt: number;
|
||||
}>;
|
||||
messages?: Array<{
|
||||
messageId: string;
|
||||
artifactIds: string[];
|
||||
kinds: MindSpaceConversationArtifactKind[];
|
||||
createdAt: number;
|
||||
updatedAt: number;
|
||||
}>;
|
||||
agentRuns?: Array<{
|
||||
agentRunId: string;
|
||||
artifactIds: string[];
|
||||
kinds: MindSpaceConversationArtifactKind[];
|
||||
createdAt: number;
|
||||
updatedAt: number;
|
||||
}>;
|
||||
};
|
||||
|
||||
export type MindSpacePageVersion = {
|
||||
|
||||
@@ -119,7 +119,13 @@ export function normalizeUserMessageForApi(message: Message): Message {
|
||||
|
||||
export function buildUserMessage(
|
||||
text: string,
|
||||
options?: { agentText?: string; displayText?: string; imageUrls?: string[]; previewImageUrls?: string[] },
|
||||
options?: {
|
||||
id?: string;
|
||||
agentText?: string;
|
||||
displayText?: string;
|
||||
imageUrls?: string[];
|
||||
previewImageUrls?: string[];
|
||||
},
|
||||
): Message {
|
||||
const displayText = stripImageUrlLines(options?.displayText ?? text);
|
||||
const imageUrls = (options?.imageUrls ?? []).filter((value) => typeof value === 'string' && value.trim());
|
||||
@@ -131,7 +137,7 @@ export function buildUserMessage(
|
||||
const agentText = [baseAgentText.trim(), imageText].filter(Boolean).join('\n\n');
|
||||
|
||||
return {
|
||||
id: crypto.randomUUID(),
|
||||
id: options?.id ?? crypto.randomUUID(),
|
||||
role: 'user',
|
||||
created: Math.floor(Date.now() / 1000),
|
||||
content: agentText ? [{ type: 'text', text: agentText }] : [],
|
||||
|
||||
Reference in New Issue
Block a user