feat(frontend): refine app shell and assistant editors
This commit is contained in:
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user