Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Draft
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
10 changes: 7 additions & 3 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions codersdk/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -5174,6 +5174,7 @@ const (
ExperimentChatAdvisor Experiment = "chat-advisor" // Enables the advisor tool for root agent chats.
ExperimentChatVirtualDesktop Experiment = "chat-virtual-desktop" // Enables virtual desktop and computer use provider for agents.
ExperimentAgentLifecycleHooks Experiment = "agent-lifecycle-hooks" // Enables chat lifecycle hook webhooks for agent chats.
ExperimentChatVimNavigation Experiment = "chat-vim-navigation" // Enables the opt-in vim-style keyboard navigation setting for agent chats.
)

func (e Experiment) DisplayName() string {
Expand Down Expand Up @@ -5204,6 +5205,8 @@ func (e Experiment) DisplayName() string {
return "Chat Virtual Desktop"
case ExperimentAgentLifecycleHooks:
return "Agent Lifecycle Hooks"
case ExperimentChatVimNavigation:
return "Chat Vim Navigation"
default:
// Split on hyphen and convert to title case
// e.g. "mcp-server-http" -> "Mcp Server Http"
Expand All @@ -5228,6 +5231,7 @@ var ExperimentsKnown = Experiments{
ExperimentChatAdvisor,
ExperimentChatVirtualDesktop,
ExperimentAgentLifecycleHooks,
ExperimentChatVimNavigation,
}

// ExperimentsSafe should include all experiments that are safe for
Expand Down
6 changes: 3 additions & 3 deletions docs/reference/api/schemas.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions site/src/api/typesGenerated.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions site/src/pages/AgentsPage/AgentSettingsGeneralPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@ import {
updateUserChatDebugLogging,
userChatDebugLogging,
} from "#/api/queries/chats";
import { useDashboard } from "#/modules/dashboard/useDashboard";
import { AgentSettingsGeneralPageView } from "./AgentSettingsGeneralPageView";
import { VIM_NAVIGATION_EXPERIMENT } from "./hooks/useVimNavigation";

const AgentSettingsGeneralPage: FC = () => {
const queryClient = useQueryClient();
const { experiments } = useDashboard();
const showVimNavigationSettings = experiments.includes(
VIM_NAVIGATION_EXPERIMENT,
);
const userPromptQuery = useQuery(chatUserCustomPrompt());
const userDebugLoggingQuery = useQuery(userChatDebugLogging());
const saveUserPromptMutation = useMutation(
Expand All @@ -29,6 +35,7 @@ const AgentSettingsGeneralPage: FC = () => {
onSaveUserDebugLogging={saveUserDebugLoggingMutation.mutate}
isSavingUserDebugLogging={saveUserDebugLoggingMutation.isPending}
isSaveUserDebugLoggingError={saveUserDebugLoggingMutation.isError}
showVimNavigationSettings={showVimNavigationSettings}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite";
import { expect, fn, spyOn, userEvent, waitFor, within } from "storybook/test";
import { API } from "#/api/api";
import type { AgentChatSendShortcut } from "#/api/typesGenerated";
import { withVimNavigationPreference } from "#/testHelpers/vimNavigation";
import {
AgentSettingsGeneralPageView,
type AgentSettingsGeneralPageViewProps,
Expand Down Expand Up @@ -29,6 +30,7 @@ const baseArgs: AgentSettingsGeneralPageViewProps = {
onSaveUserDebugLogging: fn(),
isSavingUserDebugLogging: false,
isSaveUserDebugLoggingError: false,
showVimNavigationSettings: false,
};

const meta = {
Expand Down Expand Up @@ -151,6 +153,21 @@ export const TogglesSendShortcut: Story = {
},
};

export const VimNavigationCtrlModifier: Story = {
args: { showVimNavigationSettings: true },
beforeEach: withVimNavigationPreference("ctrl"),
};

export const VimNavigationAltModifier: Story = {
args: { showVimNavigationSettings: true },
beforeEach: withVimNavigationPreference("alt"),
};

export const VimNavigationMetaModifier: Story = {
args: { showVimNavigationSettings: true },
beforeEach: withVimNavigationPreference("meta"),
};

export const ShowsChatDebugLoggingToggle: Story = {
args: {
userDebugLoggingData: {
Expand Down
11 changes: 10 additions & 1 deletion site/src/pages/AgentsPage/AgentSettingsGeneralPageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { UseMutateFunction } from "react-query";
import type * as TypesGen from "#/api/typesGenerated";
import { ChatFullWidthSettings } from "./components/ChatFullWidthSettings";
import { ChatSendShortcutSettings } from "./components/ChatSendShortcutSettings";
import { ChatVimNavigationSettings } from "./components/ChatVimNavigationSettings";
import {
CodeDiffDisplaySettings,
ShellToolDisplaySettings,
Expand Down Expand Up @@ -31,6 +32,7 @@ export interface AgentSettingsGeneralPageViewProps {
>;
isSavingUserDebugLogging: boolean;
isSaveUserDebugLoggingError: boolean;
showVimNavigationSettings: boolean;
}

export const AgentSettingsGeneralPageView: FC<
Expand All @@ -44,6 +46,7 @@ export const AgentSettingsGeneralPageView: FC<
onSaveUserDebugLogging,
isSavingUserDebugLogging,
isSaveUserDebugLoggingError,
showVimNavigationSettings,
}) => {
return (
<div className="flex flex-col gap-8">
Expand All @@ -59,7 +62,13 @@ export const AgentSettingsGeneralPageView: FC<
isAnyPromptSaving={isSavingUserPrompt}
/>
<ChatFullWidthSettings />
<ChatSendShortcutSettings />
<div className="flex flex-col gap-4">
<h3 className="m-0 text-sm font-semibold text-content-primary">
Keyboard shortcuts
</h3>
<ChatSendShortcutSettings />
{showVimNavigationSettings && <ChatVimNavigationSettings />}
</div>
<ThinkingDisplaySettings />
<ShellToolDisplaySettings />
<CodeDiffDisplaySettings />
Expand Down
39 changes: 34 additions & 5 deletions site/src/pages/AgentsPage/AgentsPageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
cancelChatListRefetches,
cancelLoadedChatEntityRefetch,
chatEntityKey,
chat as chatQueryOptions,
infiniteChats,
invalidateChatCostTree,
invalidateChatDiffContents,
Expand Down Expand Up @@ -79,6 +80,10 @@ import { ResizableChatsSidebarFrame } from "./components/ChatsSidebar/ResizableC
import { useAgentsPageKeybindings } from "./hooks/useAgentsPageKeybindings";
import { useAgentsPWA } from "./hooks/useAgentsPWA";
import { useOrganizationChatModels } from "./hooks/useOrganizationChatModels";
import {
useVimNavigationActive,
useVimNavigationModifier,
} from "./hooks/useVimNavigation";
import { getAgentSidebarFilters } from "./utils/agentSidebarFilters";
import {
archiveChatAndDeleteWorkspace,
Expand Down Expand Up @@ -680,9 +685,38 @@ const AgentsPageLayout: FC = () => {
});
}, [queryClient]);

// State for the shared rename-chat dialog. Lifted here so both the
// sidebar menu and the chat top bar open the same dialog instance.
const [chatPendingRename, setChatPendingRename] =
useState<TypesGen.Chat | null>(null);

const vimNavigationEnabled = useVimNavigationActive();
const [vimModifier] = useVimNavigationModifier();
useAgentsPageKeybindings({
onNewAgent: handleNewAgent,
onToggleSearch: () => setIsSearchDialogOpen((open) => !open),
onRenameActiveChat: () => {
if (!agentId) {
return;
}
// The active chat may be a child embedded in its root's
// `children`, or absent from the paginated list entirely
// (filtered out or not yet loaded), in which case the
// per-chat query cache populated by the open chat page is
// used.
const activeChat =
chatList
.flatMap((chat) => [chat, ...(chat.children ?? [])])
.find((chat) => chat.id === agentId) ??
queryClient.getQueryData<TypesGen.Chat>(
chatQueryOptions(agentId).queryKey,
);
if (activeChat) {
setChatPendingRename(activeChat);
}
},
vimNavigationEnabled,
vimModifier,
});

// Fetch workspace name for the confirmation dialog. Only
Expand Down Expand Up @@ -715,11 +749,6 @@ const AgentsPageLayout: FC = () => {
]),
);

// State for the shared rename-chat dialog. Lifted here so both the
// sidebar menu and the chat top bar open the same dialog instance.
const [chatPendingRename, setChatPendingRename] =
useState<TypesGen.Chat | null>(null);

const outletContextValue: AgentsPageOutletContext = {
chatErrorReasons,
setChatErrorReason,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ export const ChatSendShortcutSettings: FC = () => {

return (
<div className="flex flex-col gap-2">
<h3 className="m-0 text-sm font-semibold text-content-primary">
Keyboard shortcuts
</h3>
<div className="flex items-center justify-between gap-4">
<p
id={descriptionId}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { afterEach, describe, expect, it, vi } from "vitest";
import { renderComponent } from "#/testHelpers/renderHelpers";
import { isMac } from "#/utils/platform";
import {
VIM_NAVIGATION_MODIFIER_STORAGE_KEY,
VIM_NAVIGATION_STORAGE_KEY,
} from "../hooks/useVimNavigation";
import { ChatVimNavigationSettings } from "./ChatVimNavigationSettings";

vi.mock("#/utils/platform", async (importOriginal) => ({
...(await importOriginal<typeof import("#/utils/platform")>()),
isMac: vi.fn(),
}));

const isMacMock = vi.mocked(isMac);

describe("ChatVimNavigationSettings", () => {
afterEach(() => {
localStorage.removeItem(VIM_NAVIGATION_STORAGE_KEY);
localStorage.removeItem(VIM_NAVIGATION_MODIFIER_STORAGE_KEY);
});

it("stores the toggle state", async () => {
isMacMock.mockReturnValue(false);
renderComponent(<ChatVimNavigationSettings />);

await userEvent.click(
screen.getByRole("switch", { name: "Vim-style chat navigation" }),
);

expect(localStorage.getItem(VIM_NAVIGATION_STORAGE_KEY)).toBe("true");
});

it("stores the selected modifier", async () => {
isMacMock.mockReturnValue(false);
renderComponent(<ChatVimNavigationSettings />);

await userEvent.click(
screen.getByRole("combobox", { name: "Vim navigation modifier" }),
);
await userEvent.click(await screen.findByRole("option", { name: "Alt" }));

expect(localStorage.getItem(VIM_NAVIGATION_MODIFIER_STORAGE_KEY)).toBe(
"alt",
);
});
});
Loading
Loading