Files
multica/packages/views/agents/components/tabs/instructions-tab.tsx
Naiyuan Qing d911cdf5ac refactor: extract all shared logic to packages — apps are now thin routing shells
- Add CoreProvider to @multica/core/platform — single component for API/stores/WS/QueryClient init
- Delete 13 platform files across web (6) and desktop (7), each app keeps only navigation.tsx
- Extract AppSidebar + DashboardLayout to @multica/views/layout
- Extract LoginPage to @multica/views/auth
- Extract AgentsPage (1,279 lines) to @multica/views/agents (11 files)
- Extract InboxPage (468 lines) to @multica/views/inbox (5 files)
- Extract SettingsPage + 6 tabs (1,277 lines) to @multica/views/settings (9 files)
- Fix AppLink to use forwardRef for Base UI render prop compatibility
- Fix Tailwind @source to scan .ts files (status config with bg-info/bg-warning)
- Suppress next-themes React 19 script tag warning
- Add WebProviders wrapper for Server→Client function passing
- Wire all desktop routes to shared views, remove PlaceholderPage
- Net: +106 / -4,094 lines

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 16:45:41 +08:00

72 lines
2.5 KiB
TypeScript

"use client";
import { useState, useEffect } from "react";
import { Loader2, Save } from "lucide-react";
import type { Agent } from "@multica/core/types";
import { Button } from "@multica/ui/components/ui/button";
export function InstructionsTab({
agent,
onSave,
}: {
agent: Agent;
onSave: (instructions: string) => Promise<void>;
}) {
const [value, setValue] = useState(agent.instructions ?? "");
const [saving, setSaving] = useState(false);
const isDirty = value !== (agent.instructions ?? "");
// Sync when switching between agents.
useEffect(() => {
setValue(agent.instructions ?? "");
}, [agent.id, agent.instructions]);
const handleSave = async () => {
setSaving(true);
try {
await onSave(value);
} catch {
// toast handled by parent
} finally {
setSaving(false);
}
};
return (
<div className="space-y-4">
<div>
<h3 className="text-sm font-semibold">Agent Instructions</h3>
<p className="text-xs text-muted-foreground mt-0.5">
Define this agent&apos;s identity and working style. These instructions are
injected into the agent&apos;s context for every task.
</p>
</div>
<textarea
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder={`Define this agent's role, expertise, and working style.\n\nExample:\nYou are a frontend engineer specializing in React and TypeScript.\n\n## Working Style\n- Write small, focused PRs — one commit per logical change\n- Prefer composition over inheritance\n- Always add unit tests for new components\n\n## Constraints\n- Do not modify shared/ types without explicit approval\n- Follow the existing component patterns in features/`}
className="w-full min-h-[300px] rounded-md border bg-transparent px-3 py-2 text-sm font-mono placeholder:text-muted-foreground/50 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring resize-y"
/>
<div className="flex items-center justify-between">
<span className="text-xs text-muted-foreground">
{value.length > 0 ? `${value.length} characters` : "No instructions set"}
</span>
<Button
size="xs"
onClick={handleSave}
disabled={!isDirty || saving}
>
{saving ? (
<Loader2 className="h-3 w-3 animate-spin" />
) : (
<Save className="h-3 w-3" />
)}
Save
</Button>
</div>
</div>
);
}