Skip to content

Commit e67b7dd

Browse files
shmuel hizmiclaude
andcommitted
Refactor wmux codebase to follow coding rules
Apply coding-rules.md across all wmux and wmux-client files: - Remove all structural "what" comments (section separators, code descriptions) - Extract long functions into small, single-purpose named functions - Replace mutations (splice, Set.delete, Map.delete) with immutable patterns - Flatten nested if statements with guard clauses and extracted helpers - Deduplicate repeated class strings into constants (CommandPalette) - Extract sub-components (CategoriesGroup, ProcessesGroup, FilesGroup, ActionsGroup) - Extract custom hook (useKeyboardShortcuts) from WmuxApp - Consolidate scattered let variables into state object (process.ts) - Create shared types file to eliminate duplicated interfaces - Rename abbreviated variables (mod, cat, def, idOrPath) to descriptive names Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2a51cf8 commit e67b7dd

12 files changed

Lines changed: 677 additions & 552 deletions

File tree

packages/wmux-client/src/components/CommandPalette.tsx

Lines changed: 188 additions & 110 deletions
Large diffs are not rendered by default.

packages/wmux-client/src/components/Sidebar.tsx

Lines changed: 25 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,7 @@ import { ChevronDown, ChevronRight } from "lucide-react";
33
import { STATUS_COLORS } from "../styles/theme";
44
import { resolveIcon } from "../utils/icons";
55
import { FileTree } from "./FileViewer";
6-
7-
// ── Types ──
8-
9-
interface FileEntry {
10-
readonly path: string;
11-
readonly name: string;
12-
readonly isDir: boolean;
13-
readonly depth: number;
14-
readonly isExpanded: boolean;
15-
}
16-
17-
interface TabInfo {
18-
readonly id: string;
19-
readonly name: string;
20-
readonly description?: string;
21-
readonly icon?: string;
22-
readonly status: string;
23-
}
24-
25-
interface CategoryInfo {
26-
readonly name: string;
27-
readonly color: string;
28-
readonly icon?: string;
29-
readonly type: string;
30-
readonly tabs: readonly TabInfo[];
31-
readonly fileEntries?: readonly FileEntry[];
32-
readonly openFiles?: readonly { readonly path: string; readonly name: string }[];
33-
}
6+
import type { CategoryInfo, TabInfo } from "../types";
347

358
interface SidebarProps {
369
readonly categories: ReadonlyArray<CategoryInfo>;
@@ -44,7 +17,14 @@ interface SidebarProps {
4417
readonly onOpenFile: (path: string) => void;
4518
}
4619

47-
// ── Category header ──
20+
function categoryTabCount(category: CategoryInfo): number {
21+
return category.type === "files" ? (category.openFiles?.length ?? 0) : category.tabs.length;
22+
}
23+
24+
function selectAndExpand(isCollapsed: boolean, onSelect: () => void, onToggle: () => void): void {
25+
onSelect();
26+
if (isCollapsed) onToggle();
27+
}
4828

4929
function CategoryHeader({
5030
category,
@@ -59,13 +39,9 @@ function CategoryHeader({
5939
readonly onSelect: () => void;
6040
readonly onToggle: () => void;
6141
}): ReactElement {
62-
const CatIcon = resolveIcon(category.icon);
63-
const isFiles = category.type === "files";
64-
const count = isFiles ? (category.openFiles?.length ?? 0) : category.tabs.length;
65-
6642
return (
6743
<div
68-
onClick={() => { onSelect(); if (isCollapsed) onToggle(); }}
44+
onClick={() => selectAndExpand(isCollapsed, onSelect, onToggle)}
6945
className={`flex items-center gap-1.5 px-2 py-1.5 text-xs cursor-pointer transition-colors group relative ${
7046
isActive
7147
? "text-foreground/90"
@@ -89,14 +65,12 @@ function CategoryHeader({
8965
</span>
9066

9167
<span className="text-[10px] text-muted-foreground/30 tabular-nums pr-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
92-
{count}
68+
{categoryTabCount(category)}
9369
</span>
9470
</div>
9571
);
9672
}
9773

98-
// ── Tab item ──
99-
10074
function TabItem({
10175
tab,
10276
isActive,
@@ -141,7 +115,10 @@ function TabItem({
141115
);
142116
}
143117

144-
// ── Category section ──
118+
function selectCategoryAndAct(isActive: boolean, onSelectCategory: () => void, action: () => void): void {
119+
if (!isActive) onSelectCategory();
120+
action();
121+
}
145122

146123
function CategorySection({
147124
category,
@@ -188,8 +165,8 @@ function CategorySection({
188165
<div className="max-h-[50vh] overflow-y-auto pb-1">
189166
<FileTree
190167
entries={category.fileEntries}
191-
onToggleDir={(path) => { if (!isActive) onSelectCategory(); onToggleDir(path); }}
192-
onOpenFile={(path) => { if (!isActive) onSelectCategory(); onOpenFile(path); }}
168+
onToggleDir={(path) => selectCategoryAndAct(isActive, onSelectCategory, () => onToggleDir(path))}
169+
onOpenFile={(path) => selectCategoryAndAct(isActive, onSelectCategory, () => onOpenFile(path))}
193170
/>
194171
</div>
195172
)}
@@ -201,7 +178,7 @@ function CategorySection({
201178
key={tab.id}
202179
tab={tab}
203180
isActive={tab.id === activeTabId && isActive}
204-
onSelect={() => { if (!isActive) onSelectCategory(); onSelectTab(tab.id); }}
181+
onSelect={() => selectCategoryAndAct(isActive, onSelectCategory, () => onSelectTab(tab.id))}
205182
/>
206183
))}
207184
</div>
@@ -212,8 +189,6 @@ function CategorySection({
212189
);
213190
}
214191

215-
// ── Sidebar footer ──
216-
217192
function SidebarFooter(): ReactElement {
218193
const shortcuts = [
219194
{ key: "⌘K", label: "Command palette" },
@@ -235,8 +210,6 @@ function SidebarFooter(): ReactElement {
235210
);
236211
}
237212

238-
// ── Sidebar ──
239-
240213
export function Sidebar({
241214
categories,
242215
activeCategory,
@@ -251,15 +224,15 @@ export function Sidebar({
251224
return (
252225
<div className="w-[240px] min-w-[240px] bg-background border-r border-border/30 flex flex-col select-none h-full">
253226
<div className="flex-1 overflow-y-auto scrollbar-hide">
254-
{categories.map((cat) => (
227+
{categories.map((category) => (
255228
<CategorySection
256-
key={cat.name}
257-
category={cat}
258-
isActive={cat.name === activeCategory}
259-
isCollapsed={collapsedCategories.has(cat.name)}
229+
key={category.name}
230+
category={category}
231+
isActive={category.name === activeCategory}
232+
isCollapsed={collapsedCategories.has(category.name)}
260233
activeTabId={activeTabId}
261-
onSelectCategory={() => onSelectCategory(cat.name)}
262-
onToggleCollapse={() => onToggleCollapse(cat.name)}
234+
onSelectCategory={() => onSelectCategory(category.name)}
235+
onToggleCollapse={() => onToggleCollapse(category.name)}
263236
onSelectTab={onSelectTab}
264237
onToggleDir={onToggleDir}
265238
onOpenFile={onOpenFile}

packages/wmux-client/src/components/TabBar.tsx

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import { DragDropProvider } from "@dnd-kit/react";
33
import { useSortable } from "@dnd-kit/react/sortable";
44
import { X, Play, RotateCw, Square } from "lucide-react";
55

6-
// ── Types ──
7-
86
export interface Tab {
97
readonly id: string;
108
readonly title: string;
@@ -26,8 +24,6 @@ interface TabBarProps {
2624
} | null | undefined;
2725
}
2826

29-
// ── Sortable tab ──
30-
3127
function SortableTab({
3228
tab,
3329
index,
@@ -66,22 +62,27 @@ function SortableTab({
6662
);
6763
}
6864

69-
// ── Process actions ──
65+
const ACTION_BUTTON_CLASS = "flex items-center justify-center w-5 h-5 rounded border-none p-0 bg-transparent text-muted-foreground/50 hover:text-foreground disabled:opacity-20 disabled:cursor-default cursor-pointer transition-colors";
7066

7167
function ProcessActions({ status, onStart, onStop, onRestart }: NonNullable<TabBarProps["processActions"]>): ReactElement {
72-
const running = status === "running";
73-
const btn = "flex items-center justify-center w-5 h-5 rounded border-none p-0 bg-transparent text-muted-foreground/50 hover:text-foreground disabled:opacity-20 disabled:cursor-default cursor-pointer transition-colors";
68+
const isRunning = status === "running";
7469

7570
return (
7671
<div className="flex items-center gap-px px-1.5 border-l border-border/20 h-full ml-auto shrink-0">
77-
<button disabled={running} onClick={onStart} title="Start" className={btn}><Play size={10} /></button>
78-
<button disabled={!running} onClick={onRestart} title="Restart" className={btn}><RotateCw size={9} /></button>
79-
<button disabled={!running} onClick={onStop} title="Stop" className={btn}><Square size={9} /></button>
72+
<button disabled={isRunning} onClick={onStart} title="Start" className={ACTION_BUTTON_CLASS}><Play size={10} /></button>
73+
<button disabled={!isRunning} onClick={onRestart} title="Restart" className={ACTION_BUTTON_CLASS}><RotateCw size={9} /></button>
74+
<button disabled={!isRunning} onClick={onStop} title="Stop" className={ACTION_BUTTON_CLASS}><Square size={9} /></button>
8075
</div>
8176
);
8277
}
8378

84-
// ── Tab bar ──
79+
function reorderIds(ids: readonly string[], sourceId: string, targetId: string): string[] {
80+
const sourceIndex = ids.indexOf(sourceId);
81+
const targetIndex = ids.indexOf(targetId);
82+
if (sourceIndex === -1 || targetIndex === -1) return [...ids];
83+
const withoutSource = [...ids.slice(0, sourceIndex), ...ids.slice(sourceIndex + 1)];
84+
return [...withoutSource.slice(0, targetIndex), sourceId, ...withoutSource.slice(targetIndex)];
85+
}
8586

8687
export function TabBar({ tabs, activeId, categoryColor, onSelect, onClose, onReorder, processActions }: TabBarProps): ReactElement {
8788
return (
@@ -92,17 +93,10 @@ export function TabBar({ tabs, activeId, categoryColor, onSelect, onClose, onReo
9293
<DragDropProvider
9394
onDragEnd={(event) => {
9495
if (!onReorder || !event.operation.source || !event.operation.target) return;
95-
const oldId = String(event.operation.source.id);
96-
const newId = String(event.operation.target.id);
97-
if (oldId === newId) return;
98-
const ids = tabs.map((t) => t.id);
99-
const oldIdx = ids.indexOf(oldId);
100-
const newIdx = ids.indexOf(newId);
101-
if (oldIdx === -1 || newIdx === -1) return;
102-
const next = [...ids];
103-
next.splice(oldIdx, 1);
104-
next.splice(newIdx, 0, oldId);
105-
onReorder(next);
96+
const sourceId = String(event.operation.source.id);
97+
const targetId = String(event.operation.target.id);
98+
if (sourceId === targetId) return;
99+
onReorder(reorderIds(tabs.map((t) => t.id), sourceId, targetId));
106100
}}
107101
>
108102
{tabs.map((tab, i) => (

0 commit comments

Comments
 (0)