feat(frontend): compact theme selector, adaptive prompt editor layout

- Theme dropdown: 亮色/暗色/随系统 as one row of three icons
- Prompt editor: sections centered (max-w-7xl) when debug drawer is
  closed, full-width aligned with tab edges when open
- Debug drawer opens by default
- Prompt textarea: 8 → 16 rows

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Xin Wang
2026-08-05 08:54:24 +08:00
co-authored by Claude
parent b88e37789a
commit bee1e97cdb
2 changed files with 382 additions and 367 deletions
@@ -43,11 +43,7 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import type {
DynamicVariableDefinition,
StartupConfig,
Tool,
} from "@/lib/api";
import type { DynamicVariableDefinition, StartupConfig, Tool } from "@/lib/api";
type ResourceOption = { value: string; label: string };
type PromptOpeningMode = StartupConfig["openingMode"];
@@ -153,7 +149,7 @@ export function PromptEditor({
const selectedAnchorRef = useRef<PromptSectionId | null>(null);
const [activeSection, setActiveSection] =
useState<PromptSectionId>("conversation");
const [debugOpen, setDebugOpen] = useState(false);
const [debugOpen, setDebugOpen] = useState(true);
useEffect(() => {
const container = scrollContainerRef.current;
@@ -245,7 +241,8 @@ export function PromptEditor({
updateForm("startup", {
...form.startup,
openingMode: nextMode,
openingMessage: nextMode === "confirmation"
openingMessage:
nextMode === "confirmation"
? {
title: openingMessage?.title ?? "重要提示",
message: openingMessage?.message ?? "请确认已阅读以上信息。",
@@ -322,6 +319,7 @@ export function PromptEditor({
aria-label="提示词配置分区"
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={debugOpen ? "w-full" : "mx-auto w-full max-w-7xl"}>
<div className="grid min-w-[28rem] grid-cols-5 border-b border-hairline">
{promptSections.map((section) => {
const active = activeSection === section.id;
@@ -344,11 +342,17 @@ export function PromptEditor({
);
})}
</div>
</div>
</nav>
<div
ref={scrollContainerRef}
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"
className="scrollbar-subtle min-h-0 min-w-0 flex-1 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"
>
<div
className={
debugOpen ? "space-y-3" : "mx-auto max-w-7xl space-y-3"
}
>
<section
ref={(element) => {
@@ -365,7 +369,7 @@ export function PromptEditor({
value={form.prompt}
onChange={(value) => updateForm("prompt", value)}
placeholder="请输入提示词,描述助手的角色、能力和回答要求"
rows={8}
rows={16}
/>
<DynamicVariableEditorHint
count={Object.keys(dynamicVariableDefinitions).length}
@@ -404,7 +408,10 @@ export function PromptEditor({
</SelectTrigger>
<SelectContent>
{OPENING_MODE_OPTIONS.map((option) => (
<SelectItem key={option.value} value={option.value}>
<SelectItem
key={option.value}
value={option.value}
>
{option.label}
</SelectItem>
))}
@@ -424,7 +431,9 @@ export function PromptEditor({
<Input
value={openingMessage.title}
onChange={(event) =>
updateOpeningMessage({ title: event.target.value })
updateOpeningMessage({
title: event.target.value,
})
}
placeholder="重要提示"
className="border-hairline-strong bg-background"
@@ -455,7 +464,8 @@ export function PromptEditor({
/>
</label>
<p className="text-xs leading-5 text-muted-foreground">
确认前关闭语音、文字和图片输入;确认后立即停止剩余开场白并进入 Agent。
确认前关闭语音、文字和图片输入;确认后立即停止剩余开场白并进入
Agent。
</p>
</div>
)}
@@ -478,7 +488,10 @@ export function PromptEditor({
</SelectTrigger>
<SelectContent>
{ENTRY_MODE_OPTIONS.map((option) => (
<SelectItem key={option.value} value={option.value}>
<SelectItem
key={option.value}
value={option.value}
>
{option.label}
</SelectItem>
))}
@@ -633,7 +646,8 @@ export function PromptEditor({
tools={tools.filter(
(tool) =>
tool.status === "active" &&
(form.runtimeMode === "pipeline" || tool.type !== "system"),
(form.runtimeMode === "pipeline" ||
tool.type !== "system"),
)}
selectedIds={form.toolIds}
onChange={(toolIds) => updateForm("toolIds", toolIds)}
@@ -695,6 +709,7 @@ export function PromptEditor({
</section>
</div>
</div>
</div>
{debugOpen && (
<DebugDrawer
@@ -1,7 +1,7 @@
"use client";
import { useEffect, useState } from "react";
import { Check, Laptop, Moon, Sun } from "lucide-react";
import { Laptop, Moon, Sun } from "lucide-react";
export type ThemeMode = "light" | "dark" | "system";
@@ -60,7 +60,7 @@ export function ThemeModeSelector() {
<div
role="radiogroup"
aria-label="界面主题"
className="space-y-1"
className="grid grid-cols-3 gap-1"
>
{themeOptions.map((option) => {
const Icon = option.icon;
@@ -72,17 +72,17 @@ export function ThemeModeSelector() {
type="button"
role="radio"
aria-checked={selected}
aria-label={option.label}
title={option.label}
onClick={() => selectMode(option.value)}
className={[
"flex h-10 w-full items-center gap-2.5 rounded-xl px-3 text-sm font-normal transition-colors",
"flex h-9 items-center justify-center rounded-xl transition-colors",
selected
? "bg-surface-strong text-foreground shadow-sm"
: "text-muted-foreground hover:bg-surface-strong/60 hover:text-foreground",
].join(" ")}
>
<Icon size={16} />
<span className="min-w-0 flex-1 text-left">{option.label}</span>
{selected && <Check size={15} className="shrink-0" />}
</button>
);
})}