feat(workspace): panel-preset switcher over Dockview - #120
Conversation
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
There was a problem hiding this comment.
Code Review
This pull request introduces workspace layout presets ('chat', 'chat-preview', and 'full') to the ComposerWorkspace component, allowing users to toggle between different panel configurations. It also updates the workspace styles to align with the CopilotKit token palette and adds comprehensive unit tests for the new presets. The feedback suggests wrapping localStorage operations in try-catch blocks to prevent runtime errors in restricted environments, refactoring the preset selection logic to use a safer switch statement, and cleaning up the now-obsolete extension-mode class and isExtension signal.
| const savedPreset = localStorage.getItem(WORKSPACE_PRESET_KEY); | ||
| if (savedPreset === 'chat' || savedPreset === 'chat-preview' || savedPreset === 'full') { | ||
| this.activePreset.set(savedPreset); | ||
| } |
There was a problem hiding this comment.
Accessing localStorage can throw a SecurityError or DOMException in environments where storage access is restricted (e.g., third-party iframe sandboxes or when cookies/local storage are disabled by the user). Wrapping the localStorage.getItem call in a try-catch block ensures the component initializes safely without crashing.
try {
const savedPreset = localStorage.getItem(WORKSPACE_PRESET_KEY);
if (savedPreset === 'chat' || savedPreset === 'chat-preview' || savedPreset === 'full') {
this.activePreset.set(savedPreset);
}
} catch (e) {
console.warn('Failed to read workspace preset from localStorage', e);
}| applyPreset(preset: WorkspacePreset): void { | ||
| if (!this.isDockviewInitialized()) return; | ||
| this.dockviewApi.clear(); | ||
| if (preset === 'chat') { | ||
| this.buildChatLayout(); | ||
| } else if (preset === 'chat-preview') { | ||
| this.buildChatPreviewLayout(); | ||
| } else { | ||
| this.buildFullLayout(); | ||
| } | ||
| this.activePreset.set(preset); | ||
| localStorage.setItem(WORKSPACE_PRESET_KEY, preset); | ||
| } |
There was a problem hiding this comment.
Using an explicit switch statement with a default fallback is safer and more maintainable than an if-else chain that defaults to the 'full' layout. If an invalid or unexpected preset value is passed at runtime, it should fall back to the default 'chat-preview' preset. Additionally, wrapping localStorage.setItem in a try-catch block prevents runtime crashes in environments where storage access is restricted.
applyPreset(preset: WorkspacePreset): void {
if (!this.isDockviewInitialized()) return;
this.dockviewApi.clear();
switch (preset) {
case 'chat':
this.buildChatLayout();
break;
case 'chat-preview':
this.buildChatPreviewLayout();
break;
case 'full':
this.buildFullLayout();
break;
default:
this.buildChatPreviewLayout();
preset = 'chat-preview';
break;
}
this.activePreset.set(preset);
try {
localStorage.setItem(WORKSPACE_PRESET_KEY, preset);
} catch (e) {
console.warn('Failed to save workspace preset to localStorage', e);
}
}| } | ||
|
|
||
| .workspace-toolbar { | ||
| flex: 0 0 auto; | ||
| display: flex; | ||
| align-items: center; | ||
| gap: var(--cpk-space-3); | ||
| padding: var(--cpk-space-2) var(--cpk-space-3); | ||
| } | ||
|
|
||
| &.extension-mode { | ||
| flex-direction: column; | ||
| .preset-switcher { | ||
| border: 1px solid var(--cpk-border); | ||
| border-radius: var(--cpk-radius-pill); | ||
| overflow: hidden; | ||
| background: var(--cpk-surface-elevated); | ||
| font-family: var(--cpk-font-body); | ||
|
|
||
| ::ng-deep .mat-button-toggle-label-content { | ||
| display: inline-flex; | ||
| align-items: center; | ||
| gap: var(--cpk-space-2); | ||
| line-height: 34px; | ||
| padding: 0 var(--cpk-space-3); | ||
| } | ||
|
|
||
| ::ng-deep .mat-button-toggle { | ||
| color: var(--cpk-text-secondary); | ||
| } | ||
|
|
||
| ::ng-deep .mat-button-toggle-checked { | ||
| background: var(--cpk-accent-bg); | ||
| color: var(--cpk-accent); | ||
| } | ||
|
|
||
| ::ng-deep mat-icon { | ||
| font-size: 18px; | ||
| width: 18px; | ||
| height: 18px; | ||
| } |
There was a problem hiding this comment.
0914553 to
b9585b2
Compare
- Three presets (Chat / Chat+Preview / Code & Engine) via a segmented toolbar; applyPreset() clears + rebuilds from per-preset builders - Default fresh layout is Chat+Preview; quick-arrange keeps Google's freeform localStorage restore; last preset remembered for the toggle - Retint Dockview --dv-* chrome onto --cpk-* tokens - Tests: preset panel sets + persistence; fix a leaked getItem spy
Replace the segmented preset switcher with one IDE-style toggle that cycles Chat -> Chat + Preview -> Code & Engine on each click, and relocate it from the cramped workspace toolbar into the app header, between New Session and the theme toggle. The button shows the active layout (icon + label) and its tooltip names the next one. Introduce a shared WorkspaceLayout service to bridge the header toggle and the router-outlet workspace: it owns the active-preset state, cycle logic, and localStorage persistence, while ComposerWorkspace registers a Dockview rebuild callback while mounted (and disposes it on destroy). The toggle only renders on the workspace route and is hidden on Gallery/Settings.
…ion, drop dead extension-mode
b9585b2 to
5016623
Compare
|
Addressed the Gemini review:
Rebased onto latest |
A segmented switcher fronting the Dockview workspace with named one-click layout presets, on top of the existing freeform Dockview.
What
applyPreset()clears and rebuilds Dockview per preset; freeform drag-and-save (composer_dockview_layout) is preserved untouched.composer_workspace_presetkey drives the toggle highlight.composer-workspace.{ts,ng.html,scss,spec.ts}.Why
Fast, named layout arrangements while keeping the freeform Dockview underneath.
Testing
Workspace unit spec passing;
ng buildclean.Notes
Styles reference the theme PR's
--cpk-*tokens and fall back to Material without it — functionally standalone.Contributed by CopilotKit.
Screenshots
The layout toggle lives in the header (single cycle button through Chat / Chat + Preview / Code & Engine):