persist layout state across sessions

Collapse state, panel sizes, active sidebar/context tabs, and
editor split ratio save to .clide/settings.yaml on every layout
change and restore on boot. Guards against missing projectDir
so tests and no-project scenarios degrade gracefully.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-22 23:02:48 +02:00
co-authored by Claude Opus 4.6
parent 43901bc387
commit 82c63da3ad
2 changed files with 57 additions and 0 deletions
+4
View File
@@ -45,6 +45,10 @@ heading, and (b) bumping `project.yaml` `version:` in the same commit.
`Ctrl+W` or `Escape` closes it. Prompt bar Y stays fixed
(D-049, T-035).
- Layout state persists across sessions — collapse state, sidebar
and context panel sizes, active sections, and editor split ratio
saved to `.clide/settings.yaml` (D-053, T-032).
### Changed
- Workspace renders Claude as the always-visible primary surface
@@ -113,8 +113,61 @@ class DefaultLayoutExtension extends ClideExtension {
_preset = classicPreset();
ctx.arrangement.registerSlotsInto(ctx.panels, _preset!);
ctx.arrangement.applyPreset(_preset!);
_restoreLayout(ctx);
ctx.arrangement.addListener(() => _persistLayout(ctx));
ctx.panels.addListener(() => _persistActiveTabs(ctx));
}
void _restoreLayout(ClideExtensionContext ctx) {
final s = ctx.settings;
final sidebarCollapsed = s.get<bool>(_kSidebarCollapsed);
if (sidebarCollapsed != null) {
ctx.arrangement.setCollapsed(Slots.sidebar, sidebarCollapsed);
}
final contextCollapsed = s.get<bool>(_kContextCollapsed);
if (contextCollapsed != null) {
ctx.arrangement.setCollapsed(Slots.contextPanel, contextCollapsed);
}
final sidebarSize = s.get<double>(_kSidebarSize);
if (sidebarSize != null) ctx.arrangement.setSize(Slots.sidebar, sidebarSize);
final contextSize = s.get<double>(_kContextSize);
if (contextSize != null) ctx.arrangement.setSize(Slots.contextPanel, contextSize);
final editorRatio = s.get<double>(_kEditorRatio);
if (editorRatio != null) ctx.arrangement.setEditorRatio(editorRatio);
final activeLeft = s.get<String>(_kActiveLeft);
if (activeLeft != null) ctx.panels.activateTab(Slots.sidebar, activeLeft);
final activeRight = s.get<String>(_kActiveRight);
if (activeRight != null) ctx.panels.activateTab(Slots.contextPanel, activeRight);
}
void _persistLayout(ClideExtensionContext ctx) {
if (ctx.settings.projectDir == null) return;
final s = ctx.settings;
final a = ctx.arrangement;
s.set(_kSidebarCollapsed, a.isCollapsed(Slots.sidebar));
s.set(_kContextCollapsed, a.isCollapsed(Slots.contextPanel));
s.set(_kSidebarSize, a.sizeOf(Slots.sidebar));
s.set(_kContextSize, a.sizeOf(Slots.contextPanel));
s.set(_kEditorRatio, a.editorRatio);
}
void _persistActiveTabs(ClideExtensionContext ctx) {
if (ctx.settings.projectDir == null) return;
final s = ctx.settings;
final left = ctx.panels.activeTabIn(Slots.sidebar);
if (left != null) s.set(_kActiveLeft, left);
final right = ctx.panels.activeTabIn(Slots.contextPanel);
if (right != null) s.set(_kActiveRight, right);
}
static const _kSidebarCollapsed = 'project.layout.sidebar.collapsed';
static const _kContextCollapsed = 'project.layout.context.collapsed';
static const _kSidebarSize = 'project.layout.sidebar.size';
static const _kContextSize = 'project.layout.context.size';
static const _kEditorRatio = 'project.layout.editor.ratio';
static const _kActiveLeft = 'project.layout.sidebar.activeTab';
static const _kActiveRight = 'project.layout.context.activeTab';
Future<IpcResponse> _reset(List<String> args) async {
final preset = _preset;
final ctx = _ctx;