mirror of
https://github.com/coder/coder.git
synced 2026-10-16 14:48:56 +08:00
refactor(site/src/pages/AgentsPage/components): name the project actions gate and cover long names
This commit is contained in:
@@ -188,7 +188,8 @@ type AgentChatInputProps = {
|
||||
chatProjectId?: string;
|
||||
isChatProjectPending?: boolean;
|
||||
isWorkspaceLoading?: boolean;
|
||||
// Offers the chat project's action skills above the composer.
|
||||
// Opts in to project action skills above the composer, which also need
|
||||
// the chat-projects experiment and a composer the user can send from.
|
||||
showProjectActions?: boolean;
|
||||
// Queued user messages rendered above the textarea.
|
||||
queuedMessages?: readonly ChatQueuedMessage[];
|
||||
@@ -1318,6 +1319,13 @@ export const AgentChatInput: React.FC<AgentChatInputProps> = ({
|
||||
const draftOccupiesSlot =
|
||||
hasSendableContent || hasActiveUploads || speech.isRecording;
|
||||
const editingHoldsStop = isEditingHistoryMessage && !speech.isRecording;
|
||||
const canShowProjectActions =
|
||||
showProjectActions &&
|
||||
experiments.includes("chat-projects") &&
|
||||
!isReadOnly &&
|
||||
!isReadOnlyView &&
|
||||
hasModelOptions &&
|
||||
!aiGatewayDisabled;
|
||||
const showStopButton =
|
||||
!isReadOnlyView &&
|
||||
isStreaming &&
|
||||
@@ -1351,22 +1359,13 @@ export const AgentChatInput: React.FC<AgentChatInputProps> = ({
|
||||
isEditingHistoryMessage && "pt-1",
|
||||
)}
|
||||
>
|
||||
{showProjectActions &&
|
||||
experiments.includes("chat-projects") &&
|
||||
chatOrganizationId &&
|
||||
chatProjectId &&
|
||||
!isReadOnly &&
|
||||
!isReadOnlyView &&
|
||||
hasModelOptions &&
|
||||
!aiGatewayDisabled && (
|
||||
<ProjectSkillActions
|
||||
organizationId={chatOrganizationId}
|
||||
projectId={chatProjectId}
|
||||
onInvoke={(name) =>
|
||||
internalRef.current?.insertSkill("project", name)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{canShowProjectActions && chatOrganizationId && chatProjectId && (
|
||||
<ProjectSkillActions
|
||||
organizationId={chatOrganizationId}
|
||||
projectId={chatProjectId}
|
||||
onInvoke={(name) => internalRef.current?.insertSkill("project", name)}
|
||||
/>
|
||||
)}
|
||||
{queuedMessages.length > 0 && (
|
||||
<QueuedMessagesList
|
||||
messages={queuedMessages}
|
||||
|
||||
@@ -2088,6 +2088,10 @@ export const WithOrganizationAndProjectPickers: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
const mockProjectActionSkills: TypesGen.SkillMetadata[] = MockSkills.map(
|
||||
(skill) => ({ ...skill, action: true }),
|
||||
);
|
||||
|
||||
export const ProjectComposerWithActions: Story = {
|
||||
parameters: {
|
||||
experiments: ["chat-projects"],
|
||||
@@ -2095,7 +2099,7 @@ export const ProjectComposerWithActions: Story = {
|
||||
...defaultQueries,
|
||||
{
|
||||
key: chatProjectSkillsKey(MockChatProject.id),
|
||||
data: MockSkills.map((skill) => ({ ...skill, action: true })),
|
||||
data: mockProjectActionSkills,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -12,7 +12,10 @@ const mockActionSkills: SkillMetadata[] = [
|
||||
description: "Deploy the current branch to the staging cluster.",
|
||||
},
|
||||
{ name: "lint", description: "Run the linters and fix what they report." },
|
||||
{ name: "release", description: "Draft release notes for the next version." },
|
||||
{
|
||||
name: "release-notes-for-the-next-version-with-every-merged-change",
|
||||
description: "Draft release notes for the next version.",
|
||||
},
|
||||
{ name: "review", description: "Review changed files and suggest fixes." },
|
||||
{ name: "triage", description: "" },
|
||||
].map((skill) => ({
|
||||
|
||||
@@ -28,8 +28,10 @@ const mockSkills: SkillMetadata[] = [
|
||||
{ ...MockSkill, id: "skill-deploy", name: "deploy", action: true },
|
||||
];
|
||||
|
||||
const renderActions = (skills: SkillMetadata[]) => {
|
||||
vi.spyOn(API.experimental, "getChatProjectSkills").mockResolvedValue(skills);
|
||||
const renderActions = () => {
|
||||
vi.spyOn(API.experimental, "getChatProjectSkills").mockResolvedValue(
|
||||
mockSkills,
|
||||
);
|
||||
const onInvoke = vi.fn();
|
||||
render(
|
||||
<QueryClientProvider client={createTestQueryClient()}>
|
||||
@@ -51,7 +53,7 @@ describe("ProjectSkillActions", () => {
|
||||
});
|
||||
|
||||
it("offers the first three actions by name and the rest under More", async () => {
|
||||
const { user, onInvoke } = renderActions(mockSkills);
|
||||
const { user, onInvoke } = renderActions();
|
||||
|
||||
await user.click(await screen.findByRole("button", { name: "deploy" }));
|
||||
await user.click(screen.getByRole("button", { name: "lint" }));
|
||||
|
||||
@@ -88,7 +88,9 @@ export const ProjectSkillActions: React.FC<ProjectSkillActionsProps> = ({
|
||||
menuSelectionRef.current = skill.name;
|
||||
}}
|
||||
>
|
||||
<span className="text-content-primary">{skill.name}</span>
|
||||
<span className="max-w-full truncate text-content-primary">
|
||||
{skill.name}
|
||||
</span>
|
||||
{skill.description && (
|
||||
<span className="line-clamp-2 text-xs font-normal">
|
||||
{skill.description}
|
||||
|
||||
@@ -225,7 +225,9 @@ export const SkillEditorForm: React.FC<SkillEditorFormProps> = ({
|
||||
kind: "success",
|
||||
title: "Imported SKILL.md",
|
||||
detail: isCreate
|
||||
? "Updated name, description, and body fields."
|
||||
? showProjectSettings
|
||||
? "Updated name, description, body, and automatic use."
|
||||
: "Updated name, description, and body fields."
|
||||
: "Updated description and body fields. Kept the existing name.",
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user