Skip to content

Commit cd2797a

Browse files
committed
feat: add SidebarView resize API and auto-widen sidebar for AI tab
- Add SidebarView.resize(width) and SidebarView.getWidth() APIs for programmatic sidebar resizing with proper persistence and resync - Auto-widen sidebar to 370px on first AI tab activation when narrower than the preferred width, using a one-time view state flag - Switch to Files tab when Show in File Tree is triggered from another tab - Expose SidebarView on brackets.test for integration tests - Add integration tests for resize API, Show in File Tree tab switching, and sidebar width save/restore
1 parent 241e4dd commit cd2797a

4 files changed

Lines changed: 156 additions & 14 deletions

File tree

src/brackets.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ define(function (require, exports, module) {
294294
SearchResultsView: require("search/SearchResultsView"),
295295
ScrollTrackMarkers: require("search/ScrollTrackMarkers"),
296296
SidebarTabs: require("view/SidebarTabs"),
297+
SidebarView: require("project/SidebarView"),
297298
WorkingSetView: require("project/WorkingSetView"),
298299
doneLoading: false
299300
};

src/project/SidebarView.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,9 +326,45 @@ define(function (require, exports, module) {
326326
CommandManager.register(Strings.CMD_SHOW_SIDEBAR, Commands.SHOW_SIDEBAR, show);
327327
CommandManager.register(Strings.CMD_HIDE_SIDEBAR, Commands.HIDE_SIDEBAR, hide);
328328

329+
/**
330+
* Programmatically resize the sidebar to the given width. Persists
331+
* the new size so it is restored on reload, resyncs the drag handle,
332+
* and fires `panelResizeEnd`.
333+
*
334+
* @param {number} width Desired sidebar width in pixels
335+
*/
336+
function resize(width) {
337+
if (!$sidebar || !$sidebar.length) {
338+
return;
339+
}
340+
width = Math.round(width);
341+
$sidebar.width(width);
342+
$(".content").css("left", width);
343+
Resizer.resyncSizer($sidebar);
344+
var sidebarPrefs = PreferencesManager.getViewState("sidebar") || {};
345+
sidebarPrefs.size = width;
346+
PreferencesManager.setViewState("sidebar", sidebarPrefs);
347+
$sidebar.trigger("panelResizeEnd", [width]);
348+
}
349+
350+
/**
351+
* Get the current sidebar width in pixels. Returns the CSS width
352+
* even if the sidebar is hidden (so the value can be restored later).
353+
*
354+
* @return {number}
355+
*/
356+
function getWidth() {
357+
if (!$sidebar || !$sidebar.length) {
358+
return 0;
359+
}
360+
return $sidebar.width();
361+
}
362+
329363
// Define public API
330364
exports.toggle = toggle;
331365
exports.show = show;
332366
exports.hide = hide;
333367
exports.isVisible = isVisible;
368+
exports.resize = resize;
369+
exports.getWidth = getWidth;
334370
});

src/view/SidebarTabs.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@
3737
*/
3838
define(function (require, exports, module) {
3939

40-
const AppInit = require("utils/AppInit"),
41-
EventDispatcher = require("utils/EventDispatcher");
40+
const AppInit = require("utils/AppInit"),
41+
EventDispatcher = require("utils/EventDispatcher"),
42+
PreferencesManager = require("preferences/PreferencesManager");
4243

4344
// --- Constants -----------------------------------------------------------
4445

@@ -48,6 +49,16 @@ define(function (require, exports, module) {
4849
*/
4950
const SIDEBAR_TAB_FILES = "sidebar-tab-files";
5051

52+
/**
53+
* Preferred sidebar width (px) when a non-files tab (e.g. AI) is
54+
* first activated. Applied once if the current width is narrower.
55+
* @const {number}
56+
*/
57+
const AI_TAB_GOOD_WIDTH = 370;
58+
59+
/** Preference key used to track whether the initial width bump has been applied. */
60+
const PREF_AI_WIDTH_SET_INITIAL = "aiTabWidthSetInitial";
61+
5162
// --- Events --------------------------------------------------------------
5263

5364
/**
@@ -423,6 +434,17 @@ define(function (require, exports, module) {
423434

424435
_applyTabVisibility();
425436

437+
// One-time sidebar width bump when switching to a non-files tab
438+
if (id !== SIDEBAR_TAB_FILES && $sidebar && $sidebar.length) {
439+
if (!PreferencesManager.getViewState(PREF_AI_WIDTH_SET_INITIAL)) {
440+
const SidebarView = require("project/SidebarView");
441+
if (SidebarView.getWidth() < AI_TAB_GOOD_WIDTH) {
442+
SidebarView.resize(AI_TAB_GOOD_WIDTH);
443+
}
444+
PreferencesManager.setViewState(PREF_AI_WIDTH_SET_INITIAL, true);
445+
}
446+
}
447+
426448
if (previousTabId !== id) {
427449
exports.trigger(EVENT_TAB_CHANGED, id, previousTabId);
428450
}

test/spec/SidebarTabs-integ-test.js

Lines changed: 95 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,20 @@
1818
*
1919
*/
2020

21-
/*global describe, it, expect, beforeAll, afterAll, beforeEach, awaitsFor */
21+
/*global describe, it, expect, beforeAll, afterAll, beforeEach, awaitsFor, awaitsForDone, jsPromise */
2222

2323
define(function (require, exports, module) {
2424

2525
const SpecRunnerUtils = require("spec/SpecRunnerUtils");
2626

2727
let SidebarTabs,
28+
SidebarView,
29+
CommandManager,
30+
Commands,
2831
testWindow,
2932
brackets,
30-
_$;
33+
_$,
34+
originalSidebarWidth;
3135

3236
// Test tab constants
3337
const TEST_TAB_ID = "sidebar-tab-test";
@@ -39,23 +43,22 @@ define(function (require, exports, module) {
3943
testWindow = await SpecRunnerUtils.createTestWindowAndRun();
4044
brackets = testWindow.brackets;
4145
SidebarTabs = brackets.test.SidebarTabs;
46+
SidebarView = brackets.test.SidebarView;
47+
CommandManager = brackets.test.CommandManager;
48+
Commands = brackets.test.Commands;
4249
_$ = testWindow.$;
50+
originalSidebarWidth = SidebarView.getWidth();
4351
}, 30000);
4452

4553
afterAll(async function () {
46-
// Reset to files tab
54+
// Reset to files tab and restore original sidebar width
4755
SidebarTabs.setActiveTab(SidebarTabs.SIDEBAR_TAB_FILES);
48-
49-
// Remove any test tabs that may still exist
50-
const allTabs = SidebarTabs.getAllTabs();
51-
allTabs.forEach(function (tab) {
52-
if (tab.id !== SidebarTabs.SIDEBAR_TAB_FILES) {
53-
// Clear content first so removeTab succeeds
54-
// (skip tabs that have content - they belong to other extensions)
55-
}
56-
});
56+
SidebarView.resize(originalSidebarWidth);
5757

5858
SidebarTabs = null;
59+
SidebarView = null;
60+
CommandManager = null;
61+
Commands = null;
5962
testWindow = null;
6063
brackets = null;
6164
_$ = null;
@@ -454,5 +457,85 @@ define(function (require, exports, module) {
454457
expect($navTabBar.hasClass("has-tabs")).toBe(countAfterRemove >= 2);
455458
});
456459
});
460+
461+
describe("Show in File Tree command", function () {
462+
const testProjectPath = SpecRunnerUtils.getTestPath("/spec/DocumentCommandHandlers-test-files");
463+
464+
beforeAll(async function () {
465+
await SpecRunnerUtils.loadProjectInTestWindow(testProjectPath);
466+
}, 30000);
467+
468+
afterAll(async function () {
469+
// Close all files opened during these tests
470+
await awaitsForDone(
471+
CommandManager.execute(Commands.FILE_CLOSE_ALL, { _forceClose: true }),
472+
"close all files"
473+
);
474+
}, 30000);
475+
476+
it("should switch to files tab when executed from a non-files tab", async function () {
477+
// Open a file so handleShowInTree has an activeFile
478+
await awaitsForDone(
479+
SpecRunnerUtils.openProjectFiles(["test.js"]),
480+
"open test file"
481+
);
482+
483+
SidebarTabs.addTab(TEST_TAB_ID, "Test Tab", "fa-solid fa-flask");
484+
SidebarTabs.setActiveTab(TEST_TAB_ID);
485+
expect(SidebarTabs.getActiveTab()).toBe(TEST_TAB_ID);
486+
487+
await awaitsForDone(
488+
CommandManager.execute(Commands.NAVIGATE_SHOW_IN_FILE_TREE),
489+
"show in file tree"
490+
);
491+
492+
expect(SidebarTabs.getActiveTab()).toBe(SidebarTabs.SIDEBAR_TAB_FILES);
493+
});
494+
495+
it("should remain on files tab when already on files tab", async function () {
496+
await awaitsForDone(
497+
SpecRunnerUtils.openProjectFiles(["test.js"]),
498+
"open test file"
499+
);
500+
expect(SidebarTabs.getActiveTab()).toBe(SidebarTabs.SIDEBAR_TAB_FILES);
501+
502+
await awaitsForDone(
503+
CommandManager.execute(Commands.NAVIGATE_SHOW_IN_FILE_TREE),
504+
"show in file tree"
505+
);
506+
507+
expect(SidebarTabs.getActiveTab()).toBe(SidebarTabs.SIDEBAR_TAB_FILES);
508+
});
509+
});
510+
511+
describe("SidebarView resize", function () {
512+
let SidebarView, originalWidth;
513+
514+
beforeAll(function () {
515+
SidebarView = brackets.test.SidebarView;
516+
originalWidth = SidebarView.getWidth();
517+
});
518+
519+
afterAll(function () {
520+
SidebarView.resize(originalWidth);
521+
});
522+
523+
it("should change the sidebar width", function () {
524+
SidebarView.resize(400);
525+
expect(SidebarView.getWidth()).toBe(400);
526+
});
527+
528+
it("should update the .content left offset", function () {
529+
SidebarView.resize(350);
530+
expect(parseInt(_$(".content").css("left"), 10)).toBe(350);
531+
});
532+
533+
it("should persist the width in view state", function () {
534+
const PreferencesManager = testWindow.brackets.test.PreferencesManager;
535+
SidebarView.resize(380);
536+
const prefs = PreferencesManager.getViewState("sidebar");
537+
expect(prefs.size).toBe(380);
538+
});
539+
});
457540
});
458541
});

0 commit comments

Comments
 (0)