feat(ai-writing): 课题研究笔记本内嵌打开 + 新建/所有空间入口(0616 #5)

- 左栏点笔记本/所有空间 → 右侧内嵌打开(不跳路由),写作布局保持挂载(display:none,不丢 setup 线程),顶部「返回写作」
- ResearchSidebar 加「新建空间」(弹窗 createSearchSpace) 与「所有空间」入口
- StudioNotebookDetailPage 加可选 notebookId prop;StudioNotebooksPage 加可选 onOpenNotebook(内嵌时卡片走回调而非 Link),列表内点空间内嵌打开详情
- 内嵌笔记本详情用 ChatRuntime 包裹(修复 useArtifacts must be used within an ArtifactsProvider)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
fengxiangdebaoziandClaude Opus 4.8 committed 2026-06-16 16:38:22 +08:00
1 parent f39cffcb9a
commit 0e4caa5a66
4 files changed
+243 -47

No files matched your search

@@ -209,8 +209,15 @@ function SourcesDialog({
}
// ── 主页面 ──────────────────────────────────────────────
export default function StudioNotebookDetailPage() {
const { notebookId } = useParams<{ notebookId: string }>();
export default function StudioNotebookDetailPage({
notebookId: notebookIdProp,
}: {
/** 内嵌使用(如课题研究右侧)时直接传入;缺省则从路由参数取。 */
notebookId?: number;
} = {}) {
const params = useParams<{ notebookId: string }>();
const notebookId =
notebookIdProp != null ? String(notebookIdProp) : params.notebookId;
const { notebooks, isLoading, token: studioToken } = useStudioNotebooks(); // token 用于跳转 callback
const { models } = useModels();
const queryClient = useQueryClient();
+33 -8
View File
@@ -39,17 +39,16 @@ function NotebookCard({
nb,
onRename,
onDelete,
onOpen,
}: {
nb: StudioNotebook;
onRename: (nb: StudioNotebook) => void;
onDelete: (nb: StudioNotebook) => void;
/** 传入则点击卡片回调(内嵌打开),不再路由跳转。 */
onOpen?: (id: number) => void;
}) {
return (
<div className="group relative flex flex-col rounded-[20px] bg-[#f2f2f2] transition-colors hover:bg-[#e8e8e8]" style={{ minHeight: 180 }}>
<Link
to={`/page/workspace/studio/notebooks/${nb.id}`}
className="flex flex-1 flex-col p-5"
>
const inner = (
<>
<div className="flex items-start justify-between">
<div className="flex size-11 items-center justify-center text-3xl leading-none select-none">
{nb.avatar_url ? (
@@ -67,7 +66,27 @@ function NotebookCard({
</span>
<span className="text-[13px] text-[#666]">{nb.source_count} 个来源</span>
</div>
</Link>
</>
);
return (
<div className="group relative flex flex-col rounded-[20px] bg-[#f2f2f2] transition-colors hover:bg-[#e8e8e8]" style={{ minHeight: 180 }}>
{onOpen ? (
<button
type="button"
onClick={() => onOpen(nb.id)}
className="flex flex-1 flex-col p-5 text-left"
>
{inner}
</button>
) : (
<Link
to={`/page/workspace/studio/notebooks/${nb.id}`}
className="flex flex-1 flex-col p-5"
>
{inner}
</Link>
)}
{/* three-dot menu — absolute so it doesn't shift layout */}
<div className="absolute right-4 top-4">
@@ -99,7 +118,12 @@ function NotebookCard({
);
}
export default function StudioNotebooksPage() {
export default function StudioNotebooksPage({
onOpenNotebook,
}: {
/** 内嵌使用(课题研究右侧)时传入:点击空间卡片回调,不跳路由。 */
onOpenNotebook?: (id: number) => void;
} = {}) {
const { notebooks, isLoading, isError } = useStudioNotebooks();
const queryClient = useQueryClient();
@@ -204,6 +228,7 @@ export default function StudioNotebooksPage() {
nb={nb}
onRename={(nb) => { setRenameTarget(nb); setRenameName(nb.name); setRenameError(""); }}
onDelete={(nb) => { setDeleteTarget(nb); setDeleteError(""); }}
onOpen={onOpenNotebook}
/>
))}
</div>