feat(frontend): refine app shell and assistant editors

This commit is contained in:
Xin Wang
2026-08-05 08:19:16 +08:00
parent 0ce5d9774e
commit b88e37789a
13 changed files with 623 additions and 453 deletions
@@ -5,6 +5,7 @@ import {
Braces,
Bot,
Brain,
Bug,
Database,
Loader2,
MessageSquareText,
@@ -33,6 +34,7 @@ import { SectionCard } from "@/components/editor/section-card";
import { VisionConfigSection } from "@/components/editor/vision-config-section";
import { TurnConfigEditor } from "@/components/turn-config-editor";
import { Button } from "@/components/ui/button";
import { TopbarPortal } from "@/components/layout/topbar-portal";
import { Input } from "@/components/ui/input";
import {
Select,
@@ -151,6 +153,7 @@ export function PromptEditor({
const selectedAnchorRef = useRef<PromptSectionId | null>(null);
const [activeSection, setActiveSection] =
useState<PromptSectionId>("conversation");
const [debugOpen, setDebugOpen] = useState(false);
useEffect(() => {
const container = scrollContainerRef.current;
@@ -263,43 +266,61 @@ export function PromptEditor({
}
return (
<div className="-mt-6 flex h-full flex-col gap-4 overflow-hidden bg-background">
<div className="flex shrink-0 items-center justify-between gap-6 border-b border-hairline pb-3 pt-1">
<div className="flex min-w-0 items-center gap-2">
<EditorBackButton onClick={onBack} />
<EditableTitle
value={form.name}
onChange={(value) => updateForm("name", value)}
/>
<AssistantIdentity assistantId={assistantId} />
</div>
<>
<TopbarPortal>
<div className="flex h-full min-w-0 flex-1 items-center justify-between gap-4 sm:-ml-2 lg:-ml-4">
<div className="flex min-w-0 items-center gap-2">
<EditorBackButton onClick={onBack} />
<EditableTitle
value={form.name}
onChange={(value) => updateForm("name", value)}
/>
<AssistantIdentity assistantId={assistantId} />
</div>
<div className="flex shrink-0 gap-2">
{saveError && (
<span className="self-center text-xs text-destructive">
{saveError}
</span>
)}
<Button
className="gap-2"
disabled={saving || !dirty || !form.name.trim()}
onClick={() => onSave()}
>
{saving ? (
<Loader2 size={16} className="animate-spin" />
) : (
<Save size={16} />
<div className="flex shrink-0 items-center gap-2">
{saveError && (
<span
role="alert"
title={saveError}
className="line-clamp-1 max-w-[min(32vw,420px)] text-right text-xs text-destructive"
>
{saveError}
</span>
)}
保存
</Button>
<Button
variant="outline"
className="gap-2 border-hairline-strong text-foreground hover:bg-surface-strong"
disabled={!assistantId}
onClick={() => setDebugOpen(true)}
>
<Bug size={16} />
调试
</Button>
<Button
className="gap-2"
disabled={saving || !dirty || !form.name.trim()}
onClick={() => onSave()}
>
{saving ? (
<Loader2 size={16} className="animate-spin" />
) : (
<Save size={16} />
)}
保存
</Button>
</div>
</div>
</div>
</TopbarPortal>
<div className="flex min-h-0 flex-1 gap-4">
<div className="flex min-h-0 min-w-0 flex-1 flex-col gap-3">
<div
data-app-content="full-bleed"
className="flex h-full min-h-0 overflow-hidden bg-background"
>
<div className="flex min-h-0 min-w-0 flex-1 flex-col">
<nav
aria-label="提示词配置分区"
className="shrink-0 overflow-x-auto overscroll-none"
className="shrink-0 overflow-x-auto overscroll-none bg-background px-4 pt-3 sm:px-6 sm:pt-4 lg:px-8"
>
<div className="grid min-w-[28rem] grid-cols-5 border-b border-hairline">
{promptSections.map((section) => {
@@ -327,7 +348,7 @@ export function PromptEditor({
<div
ref={scrollContainerRef}
className="scrollbar-subtle min-h-0 min-w-0 flex-1 space-y-3 overflow-y-auto overscroll-none bg-background pr-1"
className="scrollbar-subtle min-h-0 min-w-0 flex-1 space-y-3 overflow-y-auto overscroll-none bg-background px-4 pb-6 pt-3 sm:px-6 sm:pb-8 lg:px-8 lg:pb-10"
>
<section
ref={(element) => {
@@ -675,14 +696,18 @@ export function PromptEditor({
</div>
</div>
<DebugDrawer
assistantId={assistantId}
hasUnsavedChanges={dirty}
vision={form.visionEnabled}
dynamicVariablesEnabled
dynamicVariableDefinitions={dynamicVariableDefinitions}
/>
{debugOpen && (
<DebugDrawer
overlay
assistantId={assistantId}
onClose={() => setDebugOpen(false)}
hasUnsavedChanges={dirty}
vision={form.visionEnabled}
dynamicVariablesEnabled
dynamicVariableDefinitions={dynamicVariableDefinitions}
/>
)}
</div>
</div>
</>
);
}
@@ -10,6 +10,7 @@ import {
EditorBackButton,
} from "@/components/assistant-editor/editor-controls";
import { Button } from "@/components/ui/button";
import { TopbarPortal } from "@/components/layout/topbar-portal";
import { WorkflowEditor } from "@/components/workflow/WorkflowEditor";
import type { WorkflowSettings } from "@/components/workflow/types";
import {
@@ -92,55 +93,57 @@ export function WorkflowPage({
setDynamicVariablesOpen,
}: WorkflowPageProps) {
return (
<div className="-mt-6 flex h-full flex-col gap-4">
<div className="flex shrink-0 items-center justify-between gap-6 border-b border-hairline pb-3 pt-1">
<div className="flex min-w-0 items-center gap-2">
<EditorBackButton onClick={onBack} />
<EditableTitle value={workflowName} onChange={setWorkflowName} />
<AssistantIdentity assistantId={assistantId} />
</div>
<>
<TopbarPortal>
<div className="flex h-full min-w-0 flex-1 items-center justify-between gap-4 sm:-ml-2 lg:-ml-4">
<div className="flex min-w-0 items-center gap-2">
<EditorBackButton onClick={onBack} />
<EditableTitle value={workflowName} onChange={setWorkflowName} />
<AssistantIdentity assistantId={assistantId} />
</div>
<div className="flex shrink-0 gap-2">
{saveError && (
<span
role="alert"
title={saveError}
className="line-clamp-1 max-w-[min(42vw,560px)] self-center text-right text-sm leading-5 text-destructive"
>
{saveError}
</span>
)}
<Button
variant="outline"
className="gap-2 border-hairline-strong text-foreground hover:bg-surface-strong"
disabled={!assistantId}
onClick={() => {
setWorkflowSettingsOpen(false);
setDynamicVariablesOpen(false);
setWorkflowEditingNodeId(null);
setWorkflowEditingEdgeId(null);
setWorkflowDebugOpen(true);
}}
>
<Bug size={16} />
调试
</Button>
<Button
className="gap-2"
disabled={saving || !dirty || !workflowName.trim()}
onClick={onSave}
>
{saving ? (
<Loader2 size={16} className="animate-spin" />
) : (
<Save size={16} />
<div className="flex shrink-0 items-center gap-2">
{saveError && (
<span
role="alert"
title={saveError}
className="line-clamp-1 max-w-[min(32vw,420px)] text-right text-xs text-destructive"
>
{saveError}
</span>
)}
保存
</Button>
<Button
variant="outline"
className="gap-2 border-hairline-strong text-foreground hover:bg-surface-strong"
disabled={!assistantId}
onClick={() => {
setWorkflowSettingsOpen(false);
setDynamicVariablesOpen(false);
setWorkflowEditingNodeId(null);
setWorkflowEditingEdgeId(null);
setWorkflowDebugOpen(true);
}}
>
<Bug size={16} />
调试
</Button>
<Button
className="gap-2"
disabled={saving || !dirty || !workflowName.trim()}
onClick={onSave}
>
{saving ? (
<Loader2 size={16} className="animate-spin" />
) : (
<Save size={16} />
)}
保存
</Button>
</div>
</div>
</div>
</TopbarPortal>
<div className="min-h-0 flex-1">
<div data-app-content="full-bleed" className="h-full min-h-0">
<WorkflowEditor
value={workflowGraph}
onChange={setWorkflowGraph}
@@ -194,6 +197,6 @@ export function WorkflowPage({
}))}
/>
</div>
</div>
</>
);
}