Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
582 changes: 291 additions & 291 deletions desktop/src/features/agents/ui/AgentDefinitionDialog.tsx

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions desktop/src/features/agents/ui/AgentDefinitionDialogShell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import type * as React from "react";

import { ChooserDialogContent } from "@/shared/ui/chooser-dialog-content";
import { Dialog } from "@/shared/ui/dialog";

type AgentDefinitionDialogShellProps = {
children: React.ReactNode;
description: string;
embedded: boolean;
footer: React.ReactNode;
onOpenChange: (open: boolean) => void;
open: boolean;
title: string;
};

export function AgentDefinitionDialogShell({
children,
description,
embedded,
footer,
onOpenChange,
open,
title,
}: AgentDefinitionDialogShellProps) {
if (embedded) {
return (
<div
className="relative flex min-h-0 min-w-0 flex-1 flex-col"
data-testid="persona-dialog"
>
<div className="min-h-0 min-w-0 flex-1 overflow-y-auto px-5 pb-20 pt-5">
{children}
</div>
<div className="pointer-events-none absolute inset-x-0 bottom-0 flex justify-end bg-linear-to-t from-background via-background/95 to-transparent px-4 pb-3 pt-10 [&_button]:pointer-events-auto">
{footer}
</div>
</div>
);
}

return (
<Dialog onOpenChange={onOpenChange} open={open}>
<ChooserDialogContent
className="max-w-3xl border-0"
contentClassName="pt-3"
data-testid="persona-dialog"
description={description}
footer={footer}
footerClassName="border-t-0 pt-0"
headerClassName="pb-2"
title={title}
>
{children}
</ChooserDialogContent>
</Dialog>
);
}
16 changes: 14 additions & 2 deletions desktop/src/features/agents/ui/AgentDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ import {

type AgentDialogCreateProps = {
mode: "definition";
embedded?: boolean;
submitLabel?: string;
initialValues?: CreatePersonaInput | null;
onDirtyChange?: (dirty: boolean) => void;
onOpenChange: (open: boolean) => void;
definitionError: Error | null;
isDefinitionPending: boolean;
Expand Down Expand Up @@ -120,12 +123,15 @@ export function AgentDialog(props: AgentDialogProps) {
}

function AgentCreateDialogRouter({
embedded,
initialValues: providedInitialValues,
onOpenChange,
definitionError,
isDefinitionPending,
runtimes,
runtimeCatalogStatus,
submitLabel,
onDirtyChange,
onSubmitDefinition,
}: AgentDialogCreateProps) {
const [runDraft, setRunDraft] = React.useState(emptyWhereToRunDraft);
Expand All @@ -145,14 +151,19 @@ function AgentCreateDialogRouter({
<WhereToRunSection
draft={runDraft}
isPending={isDefinitionPending}
onDraftChange={setRunDraft}
onDraftChange={(nextDraft) => {
setRunDraft(nextDraft);
onDirtyChange?.(true);
}}
/>
}
createSubmitBlocked={!canSubmitWhereToRun(runDraft)}
description={copy.description}
embedded={embedded}
error={definitionError}
initialValues={initialValues}
isPending={isDefinitionPending}
onDirtyChange={onDirtyChange}
onOpenChange={onOpenChange}
onSubmit={async (input) => {
const submitted = await onSubmitDefinition(
Expand All @@ -161,13 +172,14 @@ function AgentCreateDialogRouter({
resolveBackendIntent(runDraft),
);
if (submitted) {
onDirtyChange?.(false);
onOpenChange(false);
}
}}
open
runtimes={runtimes}
runtimeCatalogStatus={runtimeCatalogStatus}
submitLabel={copy.submitLabel}
submitLabel={submitLabel ?? copy.submitLabel}
title={copy.title}
/>
</AgentRunLocationProvider>
Expand Down
77 changes: 42 additions & 35 deletions desktop/src/features/agents/ui/AgentsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,10 @@ export function AgentsView() {
const fullAiDefaultsTriggerRef = React.useRef<HTMLButtonElement>(null);
const compactActionsTriggerRef = React.useRef<HTMLButtonElement>(null);
const [isAiDefaultsOpen, setIsAiDefaultsOpen] = React.useState(false);
// Exclusivity: create never sets `personaDialogState` (edit/dup/import do),
// so the create-mode and definition-edit AgentDialog mounts never coexist.
const [isCreateDialogOpen, setIsCreateDialogOpen] = React.useState(false);

function openUnifiedCreate() {
function openUnifiedCatalog() {
personas.prepareCreate();
setIsCreateDialogOpen(true);
personas.openCatalog();
}

function openAiDefaults(trigger: HTMLButtonElement | null) {
Expand Down Expand Up @@ -261,18 +258,14 @@ export function AgentsView() {
}
isPersonasLoading={personas.personasQuery.isLoading}
isPersonasPending={personas.isPending}
onCreatePersona={openUnifiedCreate}
onDiscoverPersonas={personas.openCatalog}
onOpenCatalog={openUnifiedCatalog}
onDuplicatePersona={personas.openDuplicate}
onEditPersona={personas.openEdit}
onSharePersona={personas.openShare}
onDeactivatePersona={(persona) => {
void personas.handleSetActive(persona, false, "library");
}}
onDeletePersona={personas.openDelete}
onImportSnapshotFile={(fileBytes, fileName) => {
void personas.handleImportSnapshotFile(fileBytes, fileName);
}}
/>

<TeamsSection
Expand Down Expand Up @@ -309,29 +302,6 @@ export function AgentsView() {
returnFocusRef={aiDefaultsTriggerRef}
/>

{isCreateDialogOpen ? (
<AgentDialog
definitionError={
personas.createPersonaMutation.error instanceof Error
? personas.createPersonaMutation.error
: null
}
isDefinitionPending={personas.isPending}
mode="definition"
onOpenChange={(open) => {
if (!open) setIsCreateDialogOpen(false);
}}
onSubmitDefinition={personas.handleSubmit}
runtimes={personas.acpRuntimesQuery.data ?? []}
runtimeCatalogStatus={
personas.acpRuntimesQuery.isLoading
? "loading"
: personas.acpRuntimesQuery.isError
? "error"
: "ready"
}
/>
) : null}
{agents.agentToAddToChannel ? (
<AddAgentToChannelDialog
agent={agents.agentToAddToChannel}
Expand Down Expand Up @@ -503,6 +473,32 @@ export function AgentsView() {
) : null}
{personas.isCatalogDialogOpen ? (
<PersonaCatalogDialog
createContent={({ onDirtyChange, onRequestClose }) => (
<AgentDialog
definitionError={
personas.createPersonaMutation.error instanceof Error
? personas.createPersonaMutation.error
: null
}
embedded
isDefinitionPending={personas.isPending}
mode="definition"
onDirtyChange={onDirtyChange}
onOpenChange={(open) => {
if (!open) onRequestClose();
}}
onSubmitDefinition={personas.handleSubmit}
runtimes={personas.acpRuntimesQuery.data ?? []}
runtimeCatalogStatus={
personas.acpRuntimesQuery.isLoading
? "loading"
: personas.acpRuntimesQuery.isError
? "error"
: "ready"
}
submitLabel="Add agent"
/>
)}
error={
personas.catalogQuery.error instanceof Error
? personas.catalogQuery.error
Expand All @@ -523,9 +519,20 @@ export function AgentsView() {
onClearFeedback={() => {
personas.clearFeedback("catalog");
}}
onImportFile={(fileBytes, fileName) => {
void personas.handleImportSnapshotFile(fileBytes, fileName);
}}
onOpenChange={personas.setIsCatalogDialogOpen}
onSelectPersona={(persona, active) => {
void personas.handleSetActive(persona, active, "catalog");
onSelectPersona={async (persona, active) => {
const addedPersona = await personas.handleSetActive(
persona,
active,
"catalog",
);
if (!active || !addedPersona) return;

personas.setIsCatalogDialogOpen(false);
openPersonaProfilePanel?.(addedPersona);
}}
open={personas.isCatalogDialogOpen}
personas={personas.catalogPersonas}
Expand Down
Loading
Loading