feat(mindspace): 公开页分享组件、workspace 路径与 Plaza 发布增强

- 新增 public share widget 与 workspace relative path 解析
- 增强 publications/chat-plaza 发布链路与缩略图 demo
- 补充 schema、cover 检查脚本与回归测试

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-05 23:14:41 +08:00
parent 69be1e5293
commit e2ad3bf62b
39 changed files with 2184 additions and 256 deletions
+24 -3
View File
@@ -1,10 +1,11 @@
import { useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { ApiError, quickPlazaFromChat } from '../api/client';
import { resolvePlazaPostUrl } from '../utils/publicUrl';
import { ChatLoadingSpinner } from './ChatLoadingSpinner';
type PlazaPublishPhase =
| { kind: 'confirm' }
| { kind: 'loading' }
| { kind: 'success'; plazaUrl: string }
| { kind: 'already'; plazaUrl: string; message: string }
@@ -28,11 +29,15 @@ export function ChatPlazaPublishModal({
messageId: string;
onClose: () => void;
}) {
const [phase, setPhase] = useState<PlazaPublishPhase>({ kind: 'loading' });
const [phase, setPhase] = useState<PlazaPublishPhase>({ kind: 'confirm' });
const closeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const onCloseRef = useRef(onClose);
onCloseRef.current = onClose;
const startPublish = useCallback(() => {
setPhase({ kind: 'loading' });
}, []);
useEffect(() => {
const prev = document.body.style.overflow;
document.body.style.overflow = 'hidden';
@@ -52,6 +57,7 @@ export function ChatPlazaPublishModal({
}, [onClose, phase.kind]);
useEffect(() => {
if (phase.kind !== 'loading') return;
const controller = new AbortController();
void quickPlazaFromChat({ sessionId, messageId }, controller.signal)
@@ -79,7 +85,7 @@ export function ChatPlazaPublishModal({
});
return () => controller.abort();
}, [sessionId, messageId]);
}, [phase.kind, sessionId, messageId]);
const canClose = phase.kind !== 'loading';
@@ -97,6 +103,21 @@ export function ChatPlazaPublishModal({
aria-busy={phase.kind === 'loading'}
onClick={(event) => event.stopPropagation()}
>
{phase.kind === 'confirm' && (
<div className="chat-plaza-publish-body">
<h3 id="chat-plaza-publish-title"> Plaza</h3>
<p>广</p>
<div className="chat-plaza-publish-actions">
<button type="button" className="chat-plaza-publish-close-btn" onClick={onClose}>
</button>
<button type="button" className="chat-plaza-publish-confirm-btn" onClick={startPublish}>
</button>
</div>
</div>
)}
{phase.kind === 'loading' && (
<div className="chat-plaza-publish-body">
<ChatLoadingSpinner className="chat-plaza-publish-spinner" />