Skip to content

Commit 0e0f7ec

Browse files
feat(settings): add shortcut search bar in Shortcuts section (#419)
Co-authored-by: Thomas Ricouard <ricouard77@gmail.com>
1 parent fb84f82 commit 0e0f7ec

9 files changed

Lines changed: 138 additions & 10 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "codex-monitor",
33
"private": true,
4-
"version": "0.7.51",
4+
"version": "0.7.52",
55
"type": "module",
66
"scripts": {
77
"sync:material-icons": "node scripts/sync-material-icons.mjs",

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "codex-monitor"
3-
version = "0.7.51"
3+
version = "0.7.52"
44
description = "A Tauri App"
55
authors = ["you"]
66
edition = "2021"

src-tauri/gen/apple/codex-monitor_iOS/Info.plist

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
<key>CFBundlePackageType</key>
1616
<string>APPL</string>
1717
<key>CFBundleShortVersionString</key>
18-
<string>0.7.51</string>
18+
<string>0.7.52</string>
1919
<key>CFBundleVersion</key>
20-
<string>0.7.51</string>
20+
<string>0.7.52</string>
2121
<key>LSRequiresIPhoneOS</key>
2222
<true/>
2323
<key>UILaunchStoryboardName</key>

src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://schema.tauri.app/config/2",
33
"productName": "Codex Monitor",
4-
"version": "0.7.51",
4+
"version": "0.7.52",
55
"identifier": "com.dimillian.codexmonitor",
66
"build": {
77
"beforeDevCommand": "npm run dev",

src/features/settings/components/SettingsView.test.tsx

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1820,4 +1820,82 @@ describe("SettingsView Shortcuts", () => {
18201820
expect(onClose).toHaveBeenCalledTimes(1);
18211821
});
18221822
});
1823+
1824+
it("filters shortcuts by search query", async () => {
1825+
render(
1826+
<SettingsView
1827+
workspaceGroups={[]}
1828+
groupedWorkspaces={[]}
1829+
ungroupedLabel="Ungrouped"
1830+
onClose={vi.fn()}
1831+
onMoveWorkspace={vi.fn()}
1832+
onDeleteWorkspace={vi.fn()}
1833+
onCreateWorkspaceGroup={vi.fn().mockResolvedValue(null)}
1834+
onRenameWorkspaceGroup={vi.fn().mockResolvedValue(null)}
1835+
onMoveWorkspaceGroup={vi.fn().mockResolvedValue(null)}
1836+
onDeleteWorkspaceGroup={vi.fn().mockResolvedValue(null)}
1837+
onAssignWorkspaceGroup={vi.fn().mockResolvedValue(null)}
1838+
reduceTransparency={false}
1839+
onToggleTransparency={vi.fn()}
1840+
appSettings={baseSettings}
1841+
openAppIconById={{}}
1842+
onUpdateAppSettings={vi.fn().mockResolvedValue(undefined)}
1843+
onRunDoctor={vi.fn().mockResolvedValue(createDoctorResult())}
1844+
onUpdateWorkspaceCodexBin={vi.fn().mockResolvedValue(undefined)}
1845+
onUpdateWorkspaceSettings={vi.fn().mockResolvedValue(undefined)}
1846+
scaleShortcutTitle="Scale shortcut"
1847+
scaleShortcutText="Use Command +/-"
1848+
onTestNotificationSound={vi.fn()}
1849+
onTestSystemNotification={vi.fn()}
1850+
dictationModelStatus={null}
1851+
onDownloadDictationModel={vi.fn()}
1852+
onCancelDictationDownload={vi.fn()}
1853+
onRemoveDictationModel={vi.fn()}
1854+
initialSection="shortcuts"
1855+
/>,
1856+
);
1857+
1858+
const searchInput = screen.getByLabelText("Search shortcuts");
1859+
expect(screen.getByText("Toggle terminal panel")).toBeTruthy();
1860+
expect(screen.getByText("Cycle model")).toBeTruthy();
1861+
1862+
await act(async () => {
1863+
fireEvent.change(searchInput, { target: { value: "navigation" } });
1864+
});
1865+
await waitFor(() => {
1866+
expect(screen.getByText("Next workspace")).toBeTruthy();
1867+
expect(screen.queryByText("Toggle terminal panel")).toBeNull();
1868+
});
1869+
1870+
await act(async () => {
1871+
fireEvent.change(searchInput, { target: { value: "sidebars" } });
1872+
});
1873+
await waitFor(() => {
1874+
expect(screen.getByText("Toggle projects sidebar")).toBeTruthy();
1875+
expect(screen.queryByText("Next workspace")).toBeNull();
1876+
});
1877+
1878+
await act(async () => {
1879+
fireEvent.change(searchInput, { target: { value: "new shortcut while focused" } });
1880+
});
1881+
await waitFor(() => {
1882+
expect(screen.getByText("Cycle model")).toBeTruthy();
1883+
expect(screen.queryByText("Toggle terminal panel")).toBeNull();
1884+
});
1885+
1886+
await act(async () => {
1887+
fireEvent.change(searchInput, { target: { value: "no-such-shortcut" } });
1888+
});
1889+
await waitFor(() => {
1890+
expect(screen.getByText('No shortcuts match "no-such-shortcut".')).toBeTruthy();
1891+
});
1892+
1893+
await act(async () => {
1894+
fireEvent.click(screen.getByRole("button", { name: "Clear" }));
1895+
});
1896+
await waitFor(() => {
1897+
expect(screen.getByText("Toggle terminal panel")).toBeTruthy();
1898+
expect(screen.queryByText('No shortcuts match "no-such-shortcut".')).toBeNull();
1899+
});
1900+
});
18231901
});

src/features/settings/components/sections/SettingsShortcutsSection.tsx

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { KeyboardEvent } from "react";
1+
import { useMemo, useState, type KeyboardEvent } from "react";
22
import { formatShortcut, getDefaultInterruptShortcut } from "@utils/shortcuts";
33
import { isMacPlatform } from "@utils/platformPaths";
44
import type {
@@ -73,6 +73,7 @@ export function SettingsShortcutsSection({
7373
onClearShortcut,
7474
}: SettingsShortcutsSectionProps) {
7575
const isMac = isMacPlatform();
76+
const [searchQuery, setSearchQuery] = useState("");
7677

7778
const groups: ShortcutGroup[] = [
7879
{
@@ -209,13 +210,53 @@ export function SettingsShortcutsSection({
209210
},
210211
];
211212

213+
const normalizedSearchQuery = searchQuery.trim().toLowerCase();
214+
const filteredGroups = useMemo(() => {
215+
if (!normalizedSearchQuery) {
216+
return groups;
217+
}
218+
return groups
219+
.map((group) => ({
220+
...group,
221+
items: group.items.filter((item) => {
222+
const searchValue = `${group.title} ${group.subtitle} ${item.label} ${item.help}`.toLowerCase();
223+
return searchValue.includes(normalizedSearchQuery);
224+
}),
225+
}))
226+
.filter((group) => group.items.length > 0);
227+
}, [groups, normalizedSearchQuery]);
228+
212229
return (
213230
<section className="settings-section">
214231
<div className="settings-section-title">Shortcuts</div>
215232
<div className="settings-section-subtitle">
216233
Customize keyboard shortcuts for file actions, composer, panels, and navigation.
217234
</div>
218-
{groups.map((group, index) => (
235+
<div className="settings-field settings-shortcuts-search">
236+
<label className="settings-field-label" htmlFor="settings-shortcuts-search">
237+
Search shortcuts
238+
</label>
239+
<div className="settings-field-row">
240+
<input
241+
id="settings-shortcuts-search"
242+
className="settings-input"
243+
placeholder="Search shortcuts"
244+
value={searchQuery}
245+
onChange={(event) => setSearchQuery(event.target.value)}
246+
/>
247+
{searchQuery && (
248+
<button
249+
type="button"
250+
className="ghost settings-button-compact"
251+
onClick={() => setSearchQuery("")}
252+
>
253+
Clear
254+
</button>
255+
)}
256+
</div>
257+
<div className="settings-help">Filter by section name, action, or default shortcut.</div>
258+
</div>
259+
{filteredGroups.map((group, index) => (
219260
<div key={group.title}>
220261
{index > 0 && <div className="settings-divider" />}
221262
<div className="settings-subsection-title">{group.title}</div>
@@ -231,6 +272,11 @@ export function SettingsShortcutsSection({
231272
))}
232273
</div>
233274
))}
275+
{filteredGroups.length === 0 && (
276+
<div className="settings-empty">
277+
No shortcuts match {normalizedSearchQuery ? `"${searchQuery.trim()}"` : "your search"}.
278+
</div>
279+
)}
234280
</section>
235281
);
236282
}

src/styles/settings.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@
177177
margin-right: 4px;
178178
}
179179

180+
.settings-shortcuts-search {
181+
margin-bottom: 20px;
182+
}
183+
180184
.settings-command-preview {
181185
margin: 0;
182186
padding: 10px 12px;

0 commit comments

Comments
 (0)