+
{assistantTypeOptions.map((option) => {
const selected = draftType === option.type;
@@ -1264,21 +1263,19 @@ export function AssistantPage(props: AssistantPageProps) {
-
- {createError && (
-
{createError}
- )}
-
+
+ {createError && (
+ {createError}
+ )}
+
-
- >
+
);
}
diff --git a/frontend/src/components/pages/ComponentsKnowledgePage.tsx b/frontend/src/components/pages/ComponentsKnowledgePage.tsx
index 07f17d8..efd0078 100644
--- a/frontend/src/components/pages/ComponentsKnowledgePage.tsx
+++ b/frontend/src/components/pages/ComponentsKnowledgePage.tsx
@@ -22,7 +22,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
-import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { DataList } from "@/components/ui/data-list";
import {
Dialog,
@@ -40,7 +39,17 @@ import {
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { ListToolbar } from "@/components/ui/list-toolbar";
-import { PageHeader } from "@/components/ui/page-header";
+import {
+ AssistantIdentity,
+ EditorBackButton,
+ EditableTitle,
+} from "@/components/assistant-editor/editor-controls";
+import { SectionCard } from "@/components/editor/section-card";
+import {
+ ListPageLayout,
+ ListPageSection,
+} from "@/components/layout/list-page-layout";
+import { TopbarPortal } from "@/components/layout/topbar-portal";
import { SearchInput } from "@/components/ui/search-input";
import {
Select,
@@ -196,23 +205,20 @@ function KnowledgeListView() {
}
return (
-
-
router.push("/components/knowledge/new")}
- >
-
- 添加知识库
-
- }
- />
-
-
+ router.push("/components/knowledge/new")}
+ >
+
+ 添加知识库
+
+ }
+ >
+
-
-
+
+
);
}
@@ -425,26 +431,21 @@ function KnowledgeCreateView() {
}
return (
-
-
-
-
添加知识库
-
- 填写名称、说明并选择 Embedding 模型。确认后将立即创建并进入编辑页。
-
-
-
+
router.push("/components/knowledge")}
>
返回列表
-
-
+ }
+ >
{loading ? (
@@ -452,57 +453,65 @@ function KnowledgeCreateView() {
) : (
<>
-
-
-
-
-
-
-
-
- Embedding 模型
-
-
- {models.length === 0 && (
-
- 请先在「模型资源」中添加并启用 Embedding 模型。
-
- )}
+
+
+
+
+
+
+ Embedding 配置
+
+
+
+
+
+
+
+ Embedding 模型
+
+
+ {models.length === 0 && (
+
+ 请先在「模型资源」中添加并启用 Embedding 模型。
+
+ )}
+
+
+
@@ -511,7 +520,6 @@ function KnowledgeCreateView() {
)}
>
)}
-
+
);
}
@@ -774,206 +781,219 @@ function KnowledgeEditView({ knowledgeBaseId }: { knowledgeBaseId: string }) {
}
return (
-
-
-
-
- void renameBase(value)} />
-
+ <>
+
+
+
+
router.push("/components/knowledge")}
+ />
+ void renameBase(value)}
+ placeholder="未命名知识库"
+ editLabel="知识库名称"
+ />
+
+
-
-
-
-
-
-
-
-
+ {error && (
+
+ {error}
+
+ )}
+
+
+
+
+
+
+
-
- 上传文件
-
- setTextOpen(true)}
- >
-
- 上传文本
-
-
-
- {
- void addFile(event.target.files?.[0]);
- event.target.value = "";
- }}
- />
+ fileInputRef.current?.click()}
+ >
+
+ 上传文件
+
+ setTextOpen(true)}
+ >
+
+ 上传文本
+
+
+
+
+
+
+
+
+
+
+
}
+ title="文档"
+ description="上传文件或文本,系统将自动切分并生成向量。"
+ >
+
+
+ setDocumentQuery(event.target.value)}
+ />
+
+
+ {filteredDocuments.length === 0 && (
+
+ 暂无文档,点击右上角「上传」开始入库。
+
+ )}
+ {filteredDocuments.map((document) => {
+ const canPreview = document.status === "ready";
+ return (
+
{
+ if (canPreview) void previewDocument(document);
+ }}
+ onKeyDown={(event) => {
+ if (
+ canPreview &&
+ (event.key === "Enter" || event.key === " ")
+ ) {
+ event.preventDefault();
+ void previewDocument(document);
+ }
+ }}
+ className={`rounded-xl border border-hairline p-3 transition-colors ${
+ canPreview
+ ? "cursor-pointer hover:bg-surface-strong/40"
+ : ""
+ }`}
+ >
+
+
+
+
+ {document.name}
+
+
+ {statusLabel(document.status)}
+
+
+
+ {document.sourceType === "file" ? "文件" : "文字"} ·{" "}
+ {formatBytes(document.sizeBytes)} ·{" "}
+ {document.chunkCount} 个分块
+
+
+
event.stopPropagation()}
+ onKeyDown={(event) => event.stopPropagation()}
+ >
+ {document.status === "failed" && (
+
+ )}
+ {canPreview && (
+
+ )}
+
+
+
+ {document.status === "processing" && (
+
+
+ 正在抽取、切分并生成向量…
+
+ )}
+ {document.errorMessage && (
+
+ {document.errorMessage}
+
+ )}
+
+ );
+ })}
+
+
+
- {error && (
-
- {error}
-
- )}
-
-
-
- 文档
-
-
-
-
- setDocumentQuery(event.target.value)}
- />
-
-
- {filteredDocuments.length === 0 && (
-
- 暂无文档,点击右上角「上传」开始入库。
-
- )}
- {filteredDocuments.map((document) => {
- const canPreview = document.status === "ready";
- return (
-
{
- if (canPreview) void previewDocument(document);
- }}
- onKeyDown={(event) => {
- if (
- canPreview &&
- (event.key === "Enter" || event.key === " ")
- ) {
- event.preventDefault();
- void previewDocument(document);
- }
- }}
- className={`rounded-xl border border-hairline p-3 transition-colors ${
- canPreview
- ? "cursor-pointer hover:bg-surface-strong/40"
- : ""
- }`}
- >
-
-
-
-
- {document.name}
-
-
- {statusLabel(document.status)}
-
-
-
- {document.sourceType === "file" ? "文件" : "文字"} ·{" "}
- {formatBytes(document.sizeBytes)} ·{" "}
- {document.chunkCount} 个分块
-
-
-
event.stopPropagation()}
- onKeyDown={(event) => event.stopPropagation()}
- >
- {document.status === "failed" && (
-
- )}
- {canPreview && (
-
- )}
-
-
-
- {document.status === "processing" && (
-
-
- 正在抽取、切分并生成向量…
-
- )}
- {document.errorMessage && (
-
- {document.errorMessage}
-
- )}
-
- );
- })}
-
-
-
+
{
+ void addFile(event.target.files?.[0]);
+ event.target.value = "";
+ }}
+ />
-
- );
-}
-
-function EditableTitle({
- value,
- onChange,
-}: {
- value: string;
- onChange: (value: string) => void;
-}) {
- const [editing, setEditing] = useState(false);
- const [draft, setDraft] = useState(value);
- const inputRef = useRef
(null);
-
- useEffect(() => {
- if (editing) {
- inputRef.current?.focus();
- inputRef.current?.select();
- }
- }, [editing]);
-
- function startEdit() {
- setDraft(value);
- setEditing(true);
- }
-
- function commit() {
- const next = draft.trim();
- if (next) onChange(next);
- setEditing(false);
- }
-
- if (editing) {
- return (
- setDraft(event.target.value)}
- onBlur={commit}
- onKeyDown={(event) => {
- if (event.key === "Enter") {
- event.preventDefault();
- commit();
- } else if (event.key === "Escape") {
- event.preventDefault();
- setEditing(false);
- }
- }}
- className="font-display display-sm w-[min(60vw,420px)] border-b border-primary bg-transparent text-ink outline-none"
- />
- );
- }
-
- return (
-
+ >
);
}
diff --git a/frontend/src/components/pages/ComponentsModelsPage.tsx b/frontend/src/components/pages/ComponentsModelsPage.tsx
index 4ee3218..6cd4054 100644
--- a/frontend/src/components/pages/ComponentsModelsPage.tsx
+++ b/frontend/src/components/pages/ComponentsModelsPage.tsx
@@ -33,7 +33,10 @@ import {
DialogTitle,
} from "@/components/ui/dialog";
import { DataList } from "@/components/ui/data-list";
-import { PageHeader } from "@/components/ui/page-header";
+import {
+ ListPageLayout,
+ ListPageSection,
+} from "@/components/layout/list-page-layout";
import { FilterPills } from "@/components/ui/filter-pills";
import { SearchInput } from "@/components/ui/search-input";
import { ListToolbar } from "@/components/ui/list-toolbar";
@@ -495,23 +498,18 @@ export function ComponentsModelsPage() {
}
return (
-
-
+
+
}
- />
-
-
+
+
-
+ >
);
}
diff --git a/frontend/src/components/pages/ComponentsToolsPage.tsx b/frontend/src/components/pages/ComponentsToolsPage.tsx
index e55706b..18fe86b 100644
--- a/frontend/src/components/pages/ComponentsToolsPage.tsx
+++ b/frontend/src/components/pages/ComponentsToolsPage.tsx
@@ -40,7 +40,10 @@ import {
import { Input } from "@/components/ui/input";
import { FilterPills } from "@/components/ui/filter-pills";
import { ListToolbar } from "@/components/ui/list-toolbar";
-import { PageHeader } from "@/components/ui/page-header";
+import {
+ ListPageLayout,
+ ListPageSection,
+} from "@/components/layout/list-page-layout";
import { SearchInput } from "@/components/ui/search-input";
import {
Select,
@@ -689,14 +692,14 @@ export function ComponentsToolsPage() {
];
return (
-
-
+
-
+ >
);
}
diff --git a/frontend/src/components/pages/HistoryPage.tsx b/frontend/src/components/pages/HistoryPage.tsx
index 3a45310..16cf7ae 100644
--- a/frontend/src/components/pages/HistoryPage.tsx
+++ b/frontend/src/components/pages/HistoryPage.tsx
@@ -31,7 +31,10 @@ import {
} from "@/components/ui/dialog";
import { FilterPills } from "@/components/ui/filter-pills";
import { ListToolbar } from "@/components/ui/list-toolbar";
-import { PageHeader } from "@/components/ui/page-header";
+import {
+ ListPageLayout,
+ ListPageSection,
+} from "@/components/layout/list-page-layout";
import { SearchInput } from "@/components/ui/search-input";
import {
conversationsApi,
@@ -364,13 +367,12 @@ export function HistoryPage() {
const pageEnd = pageStart + rows.length;
return (
-
-
+
-
-
+
+
-
+ >
);
}
diff --git a/frontend/src/components/ui/list-toolbar.tsx b/frontend/src/components/ui/list-toolbar.tsx
index 31f6b6d..e23292e 100644
--- a/frontend/src/components/ui/list-toolbar.tsx
+++ b/frontend/src/components/ui/list-toolbar.tsx
@@ -15,7 +15,7 @@ export function ListToolbar({ filters, search, className }: ListToolbarProps) {
return (