feat: improve app ui and project metadata

This commit is contained in:
Yaojia Wang
2026-06-28 07:31:20 +02:00
parent 3bd464d400
commit 90a66437d7
86 changed files with 4892 additions and 1108 deletions

View File

@@ -1,9 +1,15 @@
"use client";
import { useCallback, useMemo, useState } from "react";
import { ArrowLeft, Database, Sparkles } from "lucide-react";
import { useToast } from "@/components/Toast";
import { ConflictAdjudication } from "@/components/generation/ConflictAdjudication";
import { Button } from "@/components/ui/Button";
import { Badge } from "@/components/ui/Badge";
import { Field } from "@/components/ui/Field";
import { TextArea } from "@/components/ui/TextArea";
import { TextInput } from "@/components/ui/TextInput";
import type { ToolDescriptorView, ToolInputFieldView } from "@/lib/api/types";
import {
buildGenerateRequest,
@@ -115,25 +121,30 @@ export function GeneratorRunner({
return (
<section
className="flex flex-col gap-4 rounded border border-line bg-panel p-5"
className="flex flex-col gap-5 rounded border border-line bg-panel p-5 shadow-paper"
aria-label={tool.title}
>
<div className="flex items-start justify-between gap-4">
<div>
<h2 className="font-serif text-lg text-ink">{tool.title}</h2>
<div className="mb-2 flex flex-wrap items-center gap-2">
<h2 className="font-serif text-lg text-ink">{tool.title}</h2>
{tool.ingestable ? (
<Badge variant="info">
<Database className="h-3 w-3" aria-hidden="true" />
</Badge>
) : null}
</div>
<p className="mt-1 text-sm text-ink-soft">{tool.subtitle}</p>
</div>
<button
type="button"
onClick={onClose}
className="rounded border border-line px-3 py-1 text-sm text-ink-soft hover:text-ink"
>
<Button onClick={onClose} variant="secondary" size="sm">
<ArrowLeft className="h-4 w-4" aria-hidden="true" />
</button>
</Button>
</div>
<form
className="flex flex-col gap-3"
className="grid gap-3 rounded border border-line bg-bg/50 p-4"
onSubmit={(e) => {
e.preventDefault();
void onGenerate();
@@ -148,13 +159,10 @@ export function GeneratorRunner({
onChange={(v) => setField(field.name, v)}
/>
))}
<button
type="submit"
disabled={generating}
className="self-start rounded bg-cinnabar px-4 py-2 text-sm text-white disabled:opacity-50"
>
<Button type="submit" disabled={generating} variant="primary">
<Sparkles className="h-4 w-4" aria-hidden="true" />
{generating ? "生成中…" : "生成"}
</button>
</Button>
</form>
{gen.genStatus === "preview" && gen.preview ? (
@@ -181,18 +189,18 @@ export function GeneratorRunner({
</ul>
{canIngest && gen.ingestStatus !== "conflict" ? (
<button
type="button"
<Button
onClick={() => void runIngest(false)}
disabled={ingesting || (!singleObject && selected.size === 0)}
className="self-start rounded border border-cinnabar px-4 py-2 text-sm text-cinnabar disabled:opacity-50"
variant="outline"
>
<Database className="h-4 w-4" aria-hidden="true" />
{ingesting
? "入库中…"
: singleObject
? `入库为规则至 ${table}`
: `入库选中(${selected.size})至 ${table}`}
</button>
</Button>
) : null}
{gen.ingestStatus === "conflict" && gen.conflicts ? (
@@ -223,33 +231,24 @@ interface FormFieldProps {
// 声明式控件映射type → text / textarea / numberselect 暂同 text后端未给 options
function FormField({ field, value, onChange }: FormFieldProps) {
const labelText = field.required ? `${field.label} *` : field.label;
const common =
"mt-1 w-full rounded border border-line bg-bg px-3 py-2 text-sm text-ink";
return (
<label className="block text-sm text-ink-soft">
{labelText}
<Field label={field.label} help={field.help} required={field.required}>
{field.type === "textarea" ? (
<textarea
<TextArea
value={value}
onChange={(e) => onChange(e.target.value)}
rows={3}
className={`${common} resize-y`}
aria-label={field.label}
/>
) : (
<input
<TextInput
type={field.type === "number" ? "number" : "text"}
value={value}
onChange={(e) => onChange(e.target.value)}
className={common}
aria-label={field.label}
/>
)}
{field.help ? (
<span className="mt-1 block text-xs text-ink-soft/80">{field.help}</span>
) : null}
</label>
</Field>
);
}