Skip to content

Commit 71925cd

Browse files
Trevor Hartclaude
authored andcommitted
feat: resizable sidebar and list panels with drag handles
Main uses fixed-width columns (200px / 280px / 1fr). This adds draggable resize handles between panels so users can adjust widths to their preference, with min/max constraints (sidebar 120–400px, list 180–600px). Improvements over main: - CSS-variable-driven 5-column grid replaces hardcoded 3-column - Drag lifecycle management cleans up listeners on view switch - CSS class toggling (.as-hidden) instead of inline style manipulation - 6px handle width for a comfortable grab target Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 44c605c commit 71925cd

3 files changed

Lines changed: 74 additions & 4 deletions

File tree

main.js

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

src/views/main-view.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,13 @@ export class AgentfilesView extends ItemView {
2525
private detailEl!: HTMLElement;
2626
private dashboardEl!: HTMLElement;
2727
private marketplaceEl!: HTMLElement;
28+
private resizeHandle1!: HTMLElement;
29+
private resizeHandle2!: HTMLElement;
2830

2931
private isDashboard = false;
3032
private isMarketplace = false;
3133
private updateRef: ReturnType<typeof this.store.on> | null = null;
34+
private dragCleanup: (() => void) | null = null;
3235

3336
constructor(
3437
leaf: WorkspaceLeaf,
@@ -60,7 +63,9 @@ export class AgentfilesView extends ItemView {
6063
container.addClass("as-container");
6164

6265
this.sidebarEl = container.createDiv("as-panel as-panel-sidebar");
66+
this.resizeHandle1 = this.createResizeHandle(container, this.sidebarEl, "--as-sidebar-width", 120, 400);
6367
this.listEl = container.createDiv("as-panel as-panel-list");
68+
this.resizeHandle2 = this.createResizeHandle(container, this.listEl, "--as-list-width", 180, 600);
6469
this.detailEl = container.createDiv("as-panel as-panel-detail");
6570
this.dashboardEl = container.createDiv("as-panel as-panel-dashboard as-hidden");
6671
this.marketplaceEl = container.createDiv("as-panel as-panel-marketplace as-hidden");
@@ -91,6 +96,8 @@ export class AgentfilesView extends ItemView {
9196
}
9297

9398
toggleDashboard(): void {
99+
this.dragCleanup?.();
100+
this.dragCleanup = null;
94101
this.isDashboard = !this.isDashboard;
95102
if (this.isMarketplace) {
96103
this.isMarketplace = false;
@@ -99,18 +106,24 @@ export class AgentfilesView extends ItemView {
99106
if (this.isDashboard) {
100107
this.listEl.addClass("as-hidden");
101108
this.detailEl.addClass("as-hidden");
109+
this.resizeHandle1.addClass("as-hidden");
110+
this.resizeHandle2.addClass("as-hidden");
102111
this.dashboardEl.removeClass("as-hidden");
103112
this.dashboardPanel.render();
104113
} else {
105114
this.listEl.removeClass("as-hidden");
106115
this.detailEl.removeClass("as-hidden");
116+
this.resizeHandle1.removeClass("as-hidden");
117+
this.resizeHandle2.removeClass("as-hidden");
107118
this.dashboardEl.addClass("as-hidden");
108119
}
109120
this.sidebarPanel.setDashboardActive(this.isDashboard);
110121
this.sidebarPanel.render();
111122
}
112123

113124
toggleMarketplace(): void {
125+
this.dragCleanup?.();
126+
this.dragCleanup = null;
114127
this.isMarketplace = !this.isMarketplace;
115128
if (this.isDashboard) {
116129
this.isDashboard = false;
@@ -119,11 +132,15 @@ export class AgentfilesView extends ItemView {
119132
if (this.isMarketplace) {
120133
this.listEl.addClass("as-hidden");
121134
this.detailEl.addClass("as-hidden");
135+
this.resizeHandle1.addClass("as-hidden");
136+
this.resizeHandle2.addClass("as-hidden");
122137
this.marketplaceEl.removeClass("as-hidden");
123138
this.marketplacePanel.render();
124139
} else {
125140
this.listEl.removeClass("as-hidden");
126141
this.detailEl.removeClass("as-hidden");
142+
this.resizeHandle1.removeClass("as-hidden");
143+
this.resizeHandle2.removeClass("as-hidden");
127144
this.marketplaceEl.addClass("as-hidden");
128145
}
129146
this.sidebarPanel.setMarketplaceActive(this.isMarketplace);
@@ -148,7 +165,44 @@ export class AgentfilesView extends ItemView {
148165
this.detailPanel.show(item);
149166
}
150167

168+
private createResizeHandle(
169+
container: HTMLElement,
170+
panel: HTMLElement,
171+
cssVar: string,
172+
min: number,
173+
max: number
174+
): HTMLElement {
175+
const handle = container.createDiv("as-resize-handle");
176+
let startX = 0;
177+
let startWidth = 0;
178+
179+
const onMouseMove = (e: MouseEvent) => {
180+
const newWidth = Math.min(max, Math.max(min, startWidth + (e.clientX - startX)));
181+
container.style.setProperty(cssVar, `${newWidth}px`);
182+
};
183+
184+
const onMouseUp = () => {
185+
handle.removeClass("is-dragging");
186+
document.removeEventListener("mousemove", onMouseMove);
187+
document.removeEventListener("mouseup", onMouseUp);
188+
this.dragCleanup = null;
189+
};
190+
191+
handle.addEventListener("mousedown", (e: MouseEvent) => {
192+
e.preventDefault();
193+
startX = e.clientX;
194+
startWidth = parseInt(container.style.getPropertyValue(cssVar)) || panel.offsetWidth;
195+
handle.addClass("is-dragging");
196+
document.addEventListener("mousemove", onMouseMove);
197+
document.addEventListener("mouseup", onMouseUp);
198+
this.dragCleanup = onMouseUp;
199+
});
200+
201+
return handle;
202+
}
203+
151204
onClose(): void {
205+
this.dragCleanup?.();
152206
if (this.updateRef) {
153207
this.store.offref(this.updateRef);
154208
}

styles.css

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
}
1313

1414
.as-container {
15+
--as-sidebar-width: 200px;
16+
--as-list-width: 280px;
1517
display: grid;
16-
grid-template-columns: 200px 280px 1fr;
18+
grid-template-columns: var(--as-sidebar-width) 6px var(--as-list-width) 6px 1fr;
1719
height: 100%;
1820
overflow: hidden;
1921
}
@@ -38,6 +40,20 @@
3840
overflow: hidden;
3941
}
4042

43+
/* Resize handles */
44+
.as-resize-handle {
45+
width: 6px;
46+
background: transparent;
47+
cursor: col-resize;
48+
transition: background 0.15s;
49+
z-index: 1;
50+
}
51+
52+
.as-resize-handle:hover,
53+
.as-resize-handle.is-dragging {
54+
background: var(--interactive-accent);
55+
}
56+
4157
/* Sidebar */
4258
.as-sidebar-section {
4359
margin-bottom: 8px;

0 commit comments

Comments
 (0)