diff --git a/CHANGELOG.md b/CHANGELOG.md index b71f69d7..61ee08dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/app/lib/builtin/default_layout/src/extension.dart b/app/lib/builtin/default_layout/src/extension.dart index 86737398..912f50f5 100644 --- a/app/lib/builtin/default_layout/src/extension.dart +++ b/app/lib/builtin/default_layout/src/extension.dart @@ -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(_kSidebarCollapsed); + if (sidebarCollapsed != null) { + ctx.arrangement.setCollapsed(Slots.sidebar, sidebarCollapsed); + } + final contextCollapsed = s.get(_kContextCollapsed); + if (contextCollapsed != null) { + ctx.arrangement.setCollapsed(Slots.contextPanel, contextCollapsed); + } + final sidebarSize = s.get(_kSidebarSize); + if (sidebarSize != null) ctx.arrangement.setSize(Slots.sidebar, sidebarSize); + final contextSize = s.get(_kContextSize); + if (contextSize != null) ctx.arrangement.setSize(Slots.contextPanel, contextSize); + final editorRatio = s.get(_kEditorRatio); + if (editorRatio != null) ctx.arrangement.setEditorRatio(editorRatio); + final activeLeft = s.get(_kActiveLeft); + if (activeLeft != null) ctx.panels.activateTab(Slots.sidebar, activeLeft); + final activeRight = s.get(_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 _reset(List args) async { final preset = _preset; final ctx = _ctx;