authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-10-14 04:45:53-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-14 04:45:53-07:00
log7c43155883dcfe67340611ec68719aaeb3a5f8ff
tree98717287664f338ac68c0d2b28fbe4b366d95cd0
parent2cde3f770bbf7a6ed9d52a6b1f64a471c0303a5a
signature Signed by PGP key B5690EEEBB952194

fix(web): keybindings page says to connect an environment when none is connected (#19400)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

2 files changed, 35 insertions(+), 2 deletions(-)

apps/web/src/components/settings/KeybindingsSettings.environment.test.tsx+32-1
......@@ -7,6 +7,7 @@ import { reactHookHarness as hooks } from "../../test/reactHookHarness";
77const state = vi.hoisted(() => ({
88 canOperate: false,
99 canWriteSettings: true,
10 connected: true,
1011 openEditor: vi.fn(),
1112 upsert: vi.fn(),
1213 remove: vi.fn(),
......@@ -57,7 +58,9 @@ vi.mock("./SettingsScopeContext", () => ({
5758 availableEditors: [],
5859 },
5960 };
60 return { environment, connectedEnvironments: [environment] };
61 return state.connected
62 ? { environment, connectedEnvironments: [environment] }
63 : { environment: null, connectedEnvironments: [] };
6164 },
6265}));
6366
......@@ -104,6 +107,7 @@ describe("KeybindingsSettings editor permission", () => {
104107 hooks.reset();
105108 state.canOperate = false;
106109 state.canWriteSettings = true;
110 state.connected = true;
107111 state.openEditor.mockReset().mockResolvedValue({ _tag: "Success", value: undefined });
108112 state.upsert.mockReset().mockResolvedValue({ _tag: "Success", value: undefined });
109113 state.remove.mockReset().mockResolvedValue({ _tag: "Success", value: undefined });
......@@ -141,3 +145,30 @@ describe("KeybindingsSettings editor permission", () => {
141145 expect(openButton(renderPanel()).props.disabled).toBe(true);
142146 });
143147});
148
149function noticeText(panel: unknown) {
150 const notice = visitElements(panel, (element) => element.type === "p");
151 return notice?.props.children;
152}
153
154describe("KeybindingsSettings write notice", () => {
155 beforeEach(() => {
156 hooks.reset();
157 state.canWriteSettings = true;
158 state.connected = true;
159 });
160
161 // The hosted app has no environment until one is paired, so the editor is
162 // locked for a missing connection, not a missing permission.
163 it("asks for a connected environment when none is connected", () => {
164 state.connected = false;
165 expect(noticeText(renderPanel())).toBe("Connect an environment to change keybindings.");
166 });
167
168 it("names the permission when the connection cannot write settings", () => {
169 state.canWriteSettings = false;
170 expect(noticeText(renderPanel())).toBe(
171 "This connection can view keybindings but cannot change them.",
172 );
173 });
174});
apps/web/src/components/settings/KeybindingsSettings.tsx+3-1
......@@ -1502,7 +1502,9 @@ export function KeybindingsSettingsPanel() {
15021502
15031503 {!canWriteSettings ? (
15041504 <p className="text-xs text-muted-foreground">
1505 This connection can view keybindings but cannot change them.
1505 {connectedEnvironments.length === 0
1506 ? "Connect an environment to change keybindings."
1507 : "This connection can view keybindings but cannot change them."}
15061508 </p>
15071509 ) : null}
15081510 <div inert={!canWriteSettings} className="flex flex-col gap-8">