Markdown Edit/Preview tabs + read-only .md viewer for skills & profiles

Adds MarkdownEditor (react-markdown + remark-gfm, no raw HTML — authored/retrieved
content is data, not markup) with Edit | Preview tabs, wired into the AGENTS.md and
SKILL.md editors, the agent persona, and the review artifact.

Adds a read-only "View" on every skill and agent-profile card — including builtins,
which previously had no way to be inspected at all — rendering the full SKILL.md /
AGENTS.md (frontmatter + body + actions/golden tests). Collapses a same-version
builtin that an org has forked so its own copy shadows it, keeping the version
picker unambiguous and the item clearly editable/versionable.

Also lands the agent-face wiring on the seat configurator (a live xl preview with a
state cycler) and the review inbox header.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-15 15:26:14 +03:30
parent d50cd2790e
commit 4758e4b5de
8 changed files with 1841 additions and 20 deletions
+35 -3
View File
@@ -2,6 +2,8 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
import { KeyRound, Plug, Plus, Bot, Sparkles, Trash2, Wand2 } from 'lucide-react'
import { toast } from 'sonner'
import { AppShell } from '@/components/AppShell'
import { AgentFace, type FaceState } from '@/components/AgentFace'
import { MarkdownEditor } from '@/components/MarkdownEditor'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
@@ -101,6 +103,7 @@ export function SeatsPage() {
const [mcp, setMcp] = useState({ name: '', endpoint: '', headerName: 'Authorization', headerValue: '' })
const [newSeat, setNewSeat] = useState('')
const [selectedSeat, setSelectedSeat] = useState<string | null>(null)
const [facePreview, setFacePreview] = useState<FaceState>('idle')
const [profiles, setProfiles] = useState<AgentProfileLite[]>([])
const [agent, setAgent] = useState({
name: '',
@@ -459,6 +462,36 @@ export function SeatsPage() {
</CardHeader>
{selected && (
<CardContent className="flex flex-col gap-4">
<div className="flex items-center gap-4 rounded-lg border bg-muted/30 p-4">
<AgentFace
size="xl"
name={agent.name}
monogram={agent.monogram || agent.name}
state={facePreview}
/>
<div className="flex min-w-0 flex-1 flex-col gap-2">
<div>
<p className="text-sm font-medium leading-tight">{agent.name || 'Unnamed agent'}</p>
<p className="text-xs text-muted-foreground">Live face preview each run state</p>
</div>
<div className="flex flex-wrap gap-1.5">
{(['idle', 'thinking', 'working', 'review', 'done', 'failed'] as FaceState[]).map((s) => (
<button
key={s}
type="button"
onClick={() => setFacePreview(s)}
className={cn(
'rounded-md border px-2 py-1 text-xs',
facePreview === s ? 'bg-foreground text-background' : 'text-muted-foreground',
)}
>
{s}
</button>
))}
</div>
</div>
</div>
{profiles.length > 0 && (
<Field label="Start from a profile (AGENTS.md)">
<Select value="" onValueChange={applyProfile}>
@@ -552,12 +585,11 @@ export function SeatsPage() {
<div className="flex flex-col gap-2">
<Label>Operating guide (persona)</Label>
<textarea
<MarkdownEditor
value={agent.persona}
onChange={(e) => setAgent({ ...agent, persona: e.target.value })}
onChange={(persona) => setAgent({ ...agent, persona })}
rows={4}
placeholder="The agent's persona / operating guide — set by a profile, editable here. Injected into the run."
className="w-full resize-y rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-xs outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50"
/>
</div>