Publish catalog updates on save

This commit is contained in:
kenny lopez
2026-07-23 13:33:53 -07:00
parent 4873d35f9b
commit 0520fe0e86
5 changed files with 56 additions and 64 deletions
@@ -98,8 +98,8 @@ type AgentDefinitionDialogProps = {
input: CreatePersonaInput | UpdatePersonaInput,
options: AgentDefinitionSubmitOptions,
) => Promise<unknown>;
/** Offers publishing alongside Save after a shared agent has been edited. */
showPublishUpdatesOption?: boolean;
/** Publishes saved changes when the edited agent is shared in the catalog. */
publishCatalogUpdatesOnSave?: boolean;
/** Rendered below the form fields in create mode only ("Where to run"). */
createRunSection?: React.ReactNode;
/** Extra create-mode submit gate (e.g. incomplete provider config). */
@@ -127,7 +127,7 @@ export function AgentDefinitionDialog({
runtimesLoading = false,
onOpenChange,
onSubmit,
showPublishUpdatesOption = false,
publishCatalogUpdatesOnSave = false,
createRunSection,
createSubmitBlocked = false,
}: AgentDefinitionDialogProps) {
@@ -166,8 +166,6 @@ export function AgentDefinitionDialog({
const [isAvatarUploadPending, setIsAvatarUploadPending] =
React.useState(false);
const [hasUserChanges, setHasUserChanges] = React.useState(false);
const [publishUpdatesChecked, setPublishUpdatesChecked] =
React.useState(false);
const {
globalConfig,
inheritedDefaults: {
@@ -224,7 +222,6 @@ export function AgentDefinitionDialog({
setShowAdvancedFields(false);
setIsAvatarUploadPending(false);
setHasUserChanges(false);
setPublishUpdatesChecked(false);
isRuntimeAutoSeededRef.current = false;
hasSeededForOpenRef.current = false;
}, [initialValues, open]);
@@ -271,7 +268,6 @@ export function AgentDefinitionDialog({
setShowAdvancedFields(false);
setIsAvatarUploadPending(false);
setHasUserChanges(false);
setPublishUpdatesChecked(false);
// isRuntimeAutoSeededRef and hasSeededForOpenRef are NOT reset here — the
// [initialValues, open] effect resets both when the dialog re-opens.
}
@@ -329,8 +325,7 @@ export function AgentDefinitionDialog({
...baseInput,
},
{
publishCatalogUpdates:
showPublishUpdatesOption && hasUserChanges && publishUpdatesChecked,
publishCatalogUpdates: publishCatalogUpdatesOnSave && hasUserChanges,
},
);
return;
@@ -736,9 +731,9 @@ export function AgentDefinitionDialog({
isAvatarUploadPending={isAvatarUploadPending}
isPending={isPending}
onCancel={() => handleOpenChange(false)}
onPublishUpdatesCheckedChange={setPublishUpdatesChecked}
publishUpdatesChecked={publishUpdatesChecked}
showPublishUpdates={showPublishUpdatesOption && hasUserChanges}
publishesCatalogUpdates={
publishCatalogUpdatesOnSave && hasUserChanges
}
submitBlockReason={displayName.trim() ? submitBlockReason : null}
submitLabel={submitLabel}
/>
@@ -1,14 +1,11 @@
import { Button } from "@/shared/ui/button";
import { Checkbox } from "@/shared/ui/checkbox";
type AgentDefinitionDialogFooterProps = {
canSubmit: boolean;
isAvatarUploadPending: boolean;
isPending: boolean;
onCancel: () => void;
onPublishUpdatesCheckedChange: (checked: boolean) => void;
publishUpdatesChecked: boolean;
showPublishUpdates: boolean;
publishesCatalogUpdates: boolean;
submitBlockReason: string | null;
submitLabel: string;
};
@@ -18,9 +15,7 @@ export function AgentDefinitionDialogFooter({
isAvatarUploadPending,
isPending,
onCancel,
onPublishUpdatesCheckedChange,
publishUpdatesChecked,
showPublishUpdates,
publishesCatalogUpdates,
submitBlockReason,
submitLabel,
}: AgentDefinitionDialogFooterProps) {
@@ -35,22 +30,14 @@ export function AgentDefinitionDialogFooter({
{submitBlockReason}
</p>
) : null}
{showPublishUpdates ? (
<label
className="flex cursor-pointer items-center gap-2 text-sm font-medium text-foreground"
htmlFor="persona-dialog-publish-updates"
{publishesCatalogUpdates ? (
<p
className="max-w-sm text-xs text-muted-foreground"
data-testid="persona-dialog-catalog-publish-notice"
>
<Checkbox
checked={publishUpdatesChecked}
data-testid="persona-dialog-publish-updates"
disabled={isPending || isAvatarUploadPending}
id="persona-dialog-publish-updates"
onCheckedChange={(checked) =>
onPublishUpdatesCheckedChange(checked === true)
}
/>
<span>Publish updates</span>
</label>
This agent is in the community catalog. Your changes will be
published when you save.
</p>
) : null}
</div>
@@ -73,7 +60,9 @@ export function AgentDefinitionDialogFooter({
? "Saving..."
: isAvatarUploadPending
? "Uploading..."
: submitLabel}
: publishesCatalogUpdates
? "Save and publish"
: submitLabel}
</Button>
</div>
</div>
@@ -69,7 +69,7 @@ type AgentDialogDefinitionEditProps = {
input: CreatePersonaInput | UpdatePersonaInput,
options: AgentDefinitionSubmitOptions,
) => Promise<unknown>;
showPublishUpdatesOption?: boolean;
publishCatalogUpdatesOnSave?: boolean;
};
type AgentDialogProps =
@@ -332,7 +332,7 @@ export function AgentsView() {
)
}
open={personas.personaDialogState !== null}
showPublishUpdatesOption={
publishCatalogUpdatesOnSave={
"id" in personas.personaDialogState.initialValues &&
personas.sharedCatalogPersonaIdSet.has(
personas.personaDialogState.initialValues.id,
+35 -27
View File
@@ -1399,29 +1399,48 @@ This deliberately long fenced-code example must not establish the minimum width
await page.getByLabel("Open actions for Catalog Analyst").click();
await page.getByRole("menuitem", { name: "Edit" }).click();
const editDialog = page.getByTestId("persona-dialog");
const publishUpdatesCheckbox = editDialog.getByTestId(
"persona-dialog-publish-updates",
const catalogPublishNotice = editDialog.getByTestId(
"persona-dialog-catalog-publish-notice",
);
await expect(publishUpdatesCheckbox).toHaveCount(0);
await expect(catalogPublishNotice).toHaveCount(0);
await expect(
editDialog.getByRole("button", { name: "Save and publish" }),
).toHaveCount(0);
await expect(
editDialog.getByRole("button", { name: "Save changes" }),
).toBeVisible();
await editDialog
.getByLabel("Agent instructions")
.fill("Review the latest catalog changes.");
await expect(publishUpdatesCheckbox).toBeVisible();
await expect(publishUpdatesCheckbox).toHaveAttribute(
"data-state",
"unchecked",
await expect(catalogPublishNotice).toHaveText(
"This agent is in the community catalog. Your changes will be published when you save.",
);
const [cancelButtonBox, publishUpdatesCheckboxBox] = await Promise.all([
editDialog.getByRole("button", { name: "Cancel" }).boundingBox(),
publishUpdatesCheckbox.boundingBox(),
]);
expect(
(publishUpdatesCheckboxBox?.x ?? 0) +
(publishUpdatesCheckboxBox?.width ?? 0),
).toBeLessThan(cancelButtonBox?.x ?? 0);
await editDialog.getByRole("button", { name: "Save changes" }).click();
await expect(
editDialog.getByRole("button", { name: "Save changes" }),
).toHaveCount(0);
await editDialog.getByRole("button", { name: "Save and publish" }).click();
await expect(editDialog).toHaveCount(0);
await page.getByLabel("Open actions for Catalog Analyst").click();
await page.getByRole("menuitem", { name: "Share" }).click();
await expect(catalogAccess).toHaveText("Agent only");
await expect(publishCatalogUpdatesButton).toHaveCount(0);
await page
.getByTestId("persona-share-dialog")
.getByRole("button", { name: "Close" })
.click();
await page.evaluate((id) => {
const storageKey = "buzz-persona-catalog-published-versions-v1";
const publishedVersions = JSON.parse(
localStorage.getItem(storageKey) ?? "{}",
) as Record<string, string>;
publishedVersions[id] = "stale";
localStorage.setItem(storageKey, JSON.stringify(publishedVersions));
}, personaId);
await gotoApp(page);
await page.getByTestId("open-agents-view").click();
await page.getByLabel("Open actions for Catalog Analyst").click();
await page.getByRole("menuitem", { name: "Share" }).click();
await expect(catalogAccess).toHaveText("Agent only");
@@ -1442,17 +1461,6 @@ This deliberately long fenced-code example must not establish the minimum width
.getByRole("button", { name: "Close" })
.click();
await page.getByLabel("Open actions for Catalog Analyst").click();
await page.getByRole("menuitem", { name: "Edit" }).click();
await editDialog
.getByLabel("Agent instructions")
.fill("Review and publish the latest catalog changes.");
await expect(publishUpdatesCheckbox).toBeVisible();
await publishUpdatesCheckbox.click();
await expect(publishUpdatesCheckbox).toHaveAttribute("data-state", "checked");
await editDialog.getByRole("button", { name: "Save changes" }).click();
await expect(editDialog).toHaveCount(0);
await page.getByLabel("Open actions for Catalog Analyst").click();
await page.getByRole("menuitem", { name: "Share" }).click();
await expect(catalogAccess).toHaveText("Agent only");