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";...@@ -7,6 +7,7 @@ import { reactHookHarness as hooks } from "../../test/reactHookHarness";
7const state = vi.hoisted(() => ({7const state = vi.hoisted(() => ({
8 canOperate: false,8 canOperate: false,
9 canWriteSettings: true,9 canWriteSettings: true,
10 connected: true,
10 openEditor: vi.fn(),11 openEditor: vi.fn(),
11 upsert: vi.fn(),12 upsert: vi.fn(),
12 remove: vi.fn(),13 remove: vi.fn(),
...@@ -57,7 +58,9 @@ vi.mock("./SettingsScopeContext", () => ({...@@ -57,7 +58,9 @@ vi.mock("./SettingsScopeContext", () => ({
57 availableEditors: [],58 availableEditors: [],
58 },59 },
59 };60 };
60 return { environment, connectedEnvironments: [environment] };61 return state.connected
62 ? { environment, connectedEnvironments: [environment] }
63 : { environment: null, connectedEnvironments: [] };
61 },64 },
62}));65}));
6366
...@@ -104,6 +107,7 @@ describe("KeybindingsSettings editor permission", () => {...@@ -104,6 +107,7 @@ describe("KeybindingsSettings editor permission", () => {
104 hooks.reset();107 hooks.reset();
105 state.canOperate = false;108 state.canOperate = false;
106 state.canWriteSettings = true;109 state.canWriteSettings = true;
110 state.connected = true;
107 state.openEditor.mockReset().mockResolvedValue({ _tag: "Success", value: undefined });111 state.openEditor.mockReset().mockResolvedValue({ _tag: "Success", value: undefined });
108 state.upsert.mockReset().mockResolvedValue({ _tag: "Success", value: undefined });112 state.upsert.mockReset().mockResolvedValue({ _tag: "Success", value: undefined });
109 state.remove.mockReset().mockResolvedValue({ _tag: "Success", value: undefined });113 state.remove.mockReset().mockResolvedValue({ _tag: "Success", value: undefined });
...@@ -141,3 +145,30 @@ describe("KeybindingsSettings editor permission", () => {...@@ -141,3 +145,30 @@ describe("KeybindingsSettings editor permission", () => {
141 expect(openButton(renderPanel()).props.disabled).toBe(true);145 expect(openButton(renderPanel()).props.disabled).toBe(true);
142 });146 });
143});147});
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() {...@@ -1502,7 +1502,9 @@ export function KeybindingsSettingsPanel() {
15021502
1503 {!canWriteSettings ? (1503 {!canWriteSettings ? (
1504 <p className="text-xs text-muted-foreground">1504 <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."}
1506 </p>1508 </p>
1507 ) : null}1509 ) : null}
1508 <div inert={!canWriteSettings} className="flex flex-col gap-8">1510 <div inert={!canWriteSettings} className="flex flex-col gap-8">