@eclipse-docks/core 0.7.126 → 0.7.130

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/dist/api/index.js +4 -4
  2. package/dist/api/services.d.ts +4 -0
  3. package/dist/api/services.d.ts.map +1 -1
  4. package/dist/{api-B3RXoez_.js → api-oPg3Bv1S.js} +422 -293
  5. package/dist/api-oPg3Bv1S.js.map +1 -0
  6. package/dist/components/attribution.d.ts +1 -0
  7. package/dist/components/attribution.d.ts.map +1 -1
  8. package/dist/components/command.d.ts +4 -5
  9. package/dist/components/command.d.ts.map +1 -1
  10. package/dist/components/extensions.d.ts +3 -0
  11. package/dist/components/extensions.d.ts.map +1 -1
  12. package/dist/components/filebrowser.d.ts.map +1 -1
  13. package/dist/components/language-selector.d.ts.map +1 -1
  14. package/dist/components/layout-switcher.d.ts +1 -1
  15. package/dist/components/layout-switcher.d.ts.map +1 -1
  16. package/dist/components/npm-packages.d.ts +1 -0
  17. package/dist/components/npm-packages.d.ts.map +1 -1
  18. package/dist/components/release-history.d.ts +9 -2
  19. package/dist/components/release-history.d.ts.map +1 -1
  20. package/dist/components/tasks.d.ts.map +1 -1
  21. package/dist/core/apploader.d.ts +2 -9
  22. package/dist/core/apploader.d.ts.map +1 -1
  23. package/dist/core/contribution-names.d.ts +0 -2
  24. package/dist/core/contribution-names.d.ts.map +1 -1
  25. package/dist/core/contribution-targets.d.ts +1 -0
  26. package/dist/core/contribution-targets.d.ts.map +1 -1
  27. package/dist/core/contributionregistry.d.ts +3 -0
  28. package/dist/core/contributionregistry.d.ts.map +1 -1
  29. package/dist/core/dialogservice.d.ts +3 -1
  30. package/dist/core/dialogservice.d.ts.map +1 -1
  31. package/dist/core/dropdown-item.d.ts +43 -0
  32. package/dist/core/dropdown-item.d.ts.map +1 -0
  33. package/dist/core/dropdown-menu-utils.d.ts +5 -0
  34. package/dist/core/dropdown-menu-utils.d.ts.map +1 -1
  35. package/dist/core/layout-panels.d.ts +25 -0
  36. package/dist/core/layout-panels.d.ts.map +1 -0
  37. package/dist/dialogs/navigable-info-dialog.d.ts.map +1 -1
  38. package/dist/dialogs/version-info-dialog.d.ts +18 -2
  39. package/dist/dialogs/version-info-dialog.d.ts.map +1 -1
  40. package/dist/externals/lit.js +1 -1
  41. package/dist/externals/webawesome.js +1 -1
  42. package/dist/index.js +4 -4
  43. package/dist/layouts/standard-layout.d.ts +9 -1
  44. package/dist/layouts/standard-layout.d.ts.map +1 -1
  45. package/dist/parts/contextmenu.d.ts +5 -2
  46. package/dist/parts/contextmenu.d.ts.map +1 -1
  47. package/dist/parts/index.d.ts.map +1 -1
  48. package/dist/parts/index.js +1 -1
  49. package/dist/parts/part.d.ts +4 -0
  50. package/dist/parts/part.d.ts.map +1 -1
  51. package/dist/parts/resizable-grid.d.ts +10 -3
  52. package/dist/parts/resizable-grid.d.ts.map +1 -1
  53. package/dist/parts/tabs.d.ts +6 -0
  54. package/dist/parts/tabs.d.ts.map +1 -1
  55. package/dist/{parts-CsoNFVTw.js → parts-CAt8aeU8.js} +1361 -975
  56. package/dist/parts-CAt8aeU8.js.map +1 -0
  57. package/dist/{webawesome-Y1_HnKa_.js → webawesome-BGXn04Mw.js} +18 -10
  58. package/dist/webawesome-BGXn04Mw.js.map +1 -0
  59. package/dist/{widget-DbLusgAH.js → widget-BqQtioK1.js} +11 -5
  60. package/dist/widget-BqQtioK1.js.map +1 -0
  61. package/dist/widgets/index.js +1 -1
  62. package/dist/{widgets-DnnSZhsi.js → widgets-C9V42Mqq.js} +2 -2
  63. package/dist/{widgets-DnnSZhsi.js.map → widgets-C9V42Mqq.js.map} +1 -1
  64. package/package.json +1 -1
  65. package/src/api/services.ts +20 -0
  66. package/src/commands/filebrowser-contributions.ts +0 -16
  67. package/src/components/attribution.ts +2 -0
  68. package/src/components/command.ts +45 -119
  69. package/src/components/extensions.ts +20 -3
  70. package/src/components/filebrowser.ts +15 -12
  71. package/src/components/language-selector.ts +0 -1
  72. package/src/components/layout-switcher.ts +39 -26
  73. package/src/components/npm-packages.ts +2 -0
  74. package/src/components/release-history.ts +29 -40
  75. package/src/components/tasks.ts +0 -1
  76. package/src/contributions/default-layout-contributions.ts +7 -43
  77. package/src/contributions/default-ui-contributions.ts +4 -4
  78. package/src/core/apploader.ts +7 -54
  79. package/src/core/contribution-names.ts +0 -3
  80. package/src/core/contribution-targets.ts +1 -0
  81. package/src/core/contributionregistry.ts +19 -3
  82. package/src/core/dialogservice.ts +76 -19
  83. package/src/core/dropdown-item.ts +187 -0
  84. package/src/core/dropdown-menu-utils.ts +59 -0
  85. package/src/core/layout-panels.ts +121 -0
  86. package/src/dialogs/navigable-info-dialog.ts +5 -2
  87. package/src/dialogs/version-info-dialog.ts +163 -21
  88. package/src/icons/icons.txt +4 -2
  89. package/src/icons/layout-standard-bottom-panel.svg +4 -4
  90. package/src/icons/layout-standard-full.svg +4 -4
  91. package/src/icons/layout-standard-left-aux.svg +8 -0
  92. package/src/icons/layout-standard-left-sidebar.svg +5 -0
  93. package/src/icons/layout-standard-right-aux.svg +8 -0
  94. package/src/icons/layout-standard-right-sidebar.svg +5 -0
  95. package/src/layouts/standard-layout.ts +81 -5
  96. package/src/parts/contextmenu.ts +39 -44
  97. package/src/parts/index.ts +4 -1
  98. package/src/parts/part.ts +26 -0
  99. package/src/parts/resizable-grid.ts +125 -57
  100. package/src/parts/tabs.ts +103 -0
  101. package/dist/api-B3RXoez_.js.map +0 -1
  102. package/dist/parts-CsoNFVTw.js.map +0 -1
  103. package/dist/webawesome-Y1_HnKa_.js.map +0 -1
  104. package/dist/widget-DbLusgAH.js.map +0 -1
  105. package/src/icons/layout-standard-bottom-sidebar.svg +0 -6
  106. package/src/icons/layout-standard.svg +0 -6
@@ -6,15 +6,11 @@ import { keyBindingManager } from '../core/keybindings'
6
6
  import {
7
7
  contributionRegistry,
8
8
  Contribution,
9
- CommandContribution,
10
- HTMLContribution,
11
9
  ContributionChangeEvent,
12
10
  TOPIC_CONTRIBUTEIONS_CHANGED,
13
- getContributionDisabled,
14
- getContributionVisible,
15
11
  } from '../core/contributionregistry'
16
12
  import { subscribe } from '../core/events'
17
- import { unsafeHTML } from 'lit/directives/unsafe-html.js'
13
+ import { renderDropdownItem, renderDropdownContribution, handleDropdownWaSelect } from '../core/dropdown-item'
18
14
 
19
15
  @customElement('docks-command')
20
16
  export class DocksCommand extends DocksWidget {
@@ -60,47 +56,32 @@ export class DocksCommand extends DocksWidget {
60
56
  @state()
61
57
  private dropdownContributions: Contribution[] = []
62
58
 
63
- private closeDropdown(dropdown: { open?: boolean } | null | undefined) {
64
- if (dropdown && dropdown.open !== undefined) {
65
- dropdown.open = false
66
- }
67
- }
68
-
69
- private closeParentDropdown() {
70
- this.closeDropdown(this.closest('wa-dropdown') as { open?: boolean } | null)
59
+ private handleSelect(event: CustomEvent) {
60
+ handleDropdownWaSelect(event);
71
61
  }
72
62
 
73
- private handleClick(event?: Event) {
74
- if (this.disabled) return
75
-
76
- if (event) {
77
- event.stopPropagation()
78
- }
79
-
80
- this.closeParentDropdown()
81
-
82
- if (this.action) {
83
- this.action(event)
84
- return
85
- }
86
-
87
- if (this.cmd) {
88
- void this.executeCommand(this.cmd, this.params);
89
- }
63
+ private getKeybinding(): string | null {
64
+ if (!this.cmd || this.action) return null
65
+ const keybindings = keyBindingManager.getKeyBindingsForCommand(this.cmd)
66
+ return keybindings.length > 0 ? keybindings[0] : null
90
67
  }
91
68
 
92
- private handleSelect(event: CustomEvent) {
93
- this.closeDropdown(event.target as { open?: boolean })
69
+ private hasLightDomContent(): boolean {
70
+ return Array.from(this.childNodes).some((node) => {
71
+ if (node.nodeType === Node.TEXT_NODE) {
72
+ return (node.textContent?.trim().length ?? 0) > 0
73
+ }
74
+ return node.nodeType === Node.ELEMENT_NODE
75
+ })
94
76
  }
95
77
 
96
- private isInDropdown(): boolean {
97
- return !!this.closest('wa-dropdown, wa-dropdown-menu')
78
+ private isIconOnlyDropdownTrigger(): boolean {
79
+ return !this.label && !this.hasLightDomContent()
98
80
  }
99
81
 
100
- private getKeybinding(): string | null {
101
- if (!this.cmd || this.action) return null
102
- const keybindings = keyBindingManager.getKeyBindingsForCommand(this.cmd)
103
- return keybindings.length > 0 ? keybindings[0] : null
82
+ private renderDropdownTriggerIcon(iconSlot: 'start' | undefined) {
83
+ if (!this.icon) return nothing
84
+ return icon(this.icon, { label: this.title, slot: iconSlot })
104
85
  }
105
86
 
106
87
  protected doBeforeUI() {
@@ -122,66 +103,29 @@ export class DocksCommand extends DocksWidget {
122
103
  }
123
104
  }
124
105
 
125
- private renderContribution(contribution: Contribution, slot?: string) {
126
- if ('command' in contribution) {
127
- const commandContribution = contribution as CommandContribution
128
- if (!getContributionVisible(commandContribution)) {
129
- return nothing
130
- }
131
- const disabled = getContributionDisabled(commandContribution)
132
- return html`
133
- <docks-command
134
- slot=${slot ?? nothing}
135
- cmd="${commandContribution.command}"
136
- icon="${commandContribution.icon || ''}"
137
- .params=${commandContribution.params || {}}
138
- ?disabled="${disabled}">
139
- ${commandContribution.label}
140
- </docks-command>
141
- `
106
+ private handleToolbarClick(event?: Event) {
107
+ if (this.disabled) return
108
+
109
+ if (event) {
110
+ event.stopPropagation()
142
111
  }
143
-
144
- if ('component' in contribution) {
145
- const htmlContribution = contribution as HTMLContribution
146
- const contents = htmlContribution.component
147
- if (contents instanceof Function) {
148
- return contents()
149
- }
150
- return unsafeHTML(contents)
112
+
113
+ if (this.action) {
114
+ this.action(event)
115
+ return
116
+ }
117
+
118
+ if (this.cmd) {
119
+ void this.executeCommand(this.cmd, this.params);
151
120
  }
152
-
153
- return nothing
154
121
  }
155
122
 
156
123
  render() {
157
124
  const keybinding = this.getKeybinding()
158
125
 
159
- // Nested dropdown menu item (e.g. context menu "Create new…").
160
- // docks-command renders the submenu; docks-contextmenu closes sibling
161
- // submenus across shadow boundaries when one opens.
162
- if (this.isInDropdown() && this.dropdown) {
163
- return html`
164
- <wa-dropdown-item ?disabled=${this.disabled}>
165
- ${icon(this.icon, { label: this.title, slot: 'icon' })}
166
- <slot></slot>
167
- ${this.dropdownContributions.map(c => this.renderContribution(c, 'submenu'))}
168
- </wa-dropdown-item>
169
- `
170
- }
171
-
172
- if (this.isInDropdown()) {
173
- return html`
174
- <wa-dropdown-item
175
- ?disabled=${this.disabled}
176
- @click=${(e: Event) => this.handleClick(e)}>
177
- ${icon(this.icon, { label: this.title, slot: 'icon' })}
178
- <slot></slot>
179
- ${keybinding ? html`<span class="keybinding">${keybinding}</span>` : ''}
180
- </wa-dropdown-item>
181
- `
182
- }
183
-
184
126
  if (this.dropdown) {
127
+ const iconOnlyTrigger = this.isIconOnlyDropdownTrigger()
128
+ const iconSlot = iconOnlyTrigger ? undefined : 'start'
185
129
  return html`
186
130
  <wa-dropdown
187
131
  placement=${this.placement}
@@ -194,8 +138,8 @@ export class DocksCommand extends DocksWidget {
194
138
  ?disabled=${this.disabled}
195
139
  ?with-caret=${this.withCaret}
196
140
  title=${keybinding ? `${this.title} (${keybinding})` : this.title}>
197
- ${icon(this.icon, { label: this.title, slot: 'start' })}
198
- <slot></slot>
141
+ ${this.renderDropdownTriggerIcon(iconSlot)}
142
+ ${iconOnlyTrigger ? nothing : html`<slot></slot>`}
199
143
  ${this.label ? this.title : nothing}
200
144
  </wa-button>
201
145
 
@@ -205,18 +149,17 @@ export class DocksCommand extends DocksWidget {
205
149
  </h6>
206
150
  ` : nothing}
207
151
 
208
- ${this.dropdownContributions.map(c => this.renderContribution(c))}
152
+ ${this.dropdownContributions.map(c => renderDropdownContribution(c))}
209
153
 
210
154
  ${this.cmd ? html`
211
155
  <wa-divider></wa-divider>
212
- <docks-command
213
- cmd="${this.cmd}"
214
- icon="${this.icon || ''}"
215
- .params=${this.params}
216
- ?disabled=${this.disabled}>
217
- <slot></slot>
218
- ${this.title}
219
- </docks-command>
156
+ ${renderDropdownItem({
157
+ cmd: this.cmd,
158
+ icon: this.icon,
159
+ label: this.title,
160
+ params: this.params,
161
+ disabled: this.disabled,
162
+ })}
220
163
  ` : nothing}
221
164
  </wa-dropdown>
222
165
  `
@@ -229,7 +172,7 @@ export class DocksCommand extends DocksWidget {
229
172
  size=${this.size}
230
173
  ?disabled=${this.disabled}
231
174
  title=${keybinding ? `${this.title} (${keybinding})` : this.title}
232
- @click=${(e: Event) => this.handleClick(e)}>
175
+ @click=${(e: Event) => this.handleToolbarClick(e)}>
233
176
  ${icon(this.icon, { label: this.title, slot: 'start' })}
234
177
  <slot></slot>
235
178
  </wa-button>
@@ -240,22 +183,6 @@ export class DocksCommand extends DocksWidget {
240
183
  :host {
241
184
  display: inline-block;
242
185
  }
243
-
244
- .keybinding {
245
- margin-left: auto;
246
- padding: 2px 6px;
247
- background: var(--wa-color-neutral-15);
248
- border: 1px solid var(--wa-color-neutral-25);
249
- border-radius: 3px;
250
- font-size: 10px;
251
- font-family: monospace;
252
- opacity: 0.7;
253
- }
254
-
255
- :host-context(.wa-light) .keybinding {
256
- background: var(--wa-color-neutral-85);
257
- border: 1px solid var(--wa-color-neutral-75);
258
- }
259
186
  `
260
187
  }
261
188
 
@@ -264,4 +191,3 @@ declare global {
264
191
  'docks-command': DocksCommand
265
192
  }
266
193
  }
267
-
@@ -225,7 +225,7 @@ export class DocksExtensions extends DocksPart {
225
225
  await extensionRegistry.loadExtensionFromUrl(this.registerExtensionData.url!, extension.id);
226
226
 
227
227
  toastInfo(`Extension ${extension.name} registered successfully`);
228
- this.showRegisterDialog = false;
228
+ this.closeRegisterDialog();
229
229
  this.registerExtensionData = {};
230
230
  this.requestUpdate();
231
231
  });
@@ -234,12 +234,29 @@ export class DocksExtensions extends DocksPart {
234
234
  }
235
235
  }
236
236
 
237
- private cancelRegisterExtension() {
237
+ private closeRegisterDialog() {
238
+ const dialog = this.renderRoot.querySelector('wa-dialog') as (HTMLElement & { open?: boolean }) | null;
239
+ if (dialog && dialog.open !== false) {
240
+ dialog.open = false;
241
+ return;
242
+ }
243
+ this.finishRegisterDialogClose();
244
+ }
245
+
246
+ private handleRegisterDialogAfterHide = () => {
247
+ this.finishRegisterDialogClose();
248
+ };
249
+
250
+ private finishRegisterDialogClose() {
238
251
  this.showRegisterDialog = false;
239
252
  this.registerExtensionData = {};
240
253
  this.requestUpdate();
241
254
  }
242
255
 
256
+ private cancelRegisterExtension() {
257
+ this.closeRegisterDialog();
258
+ }
259
+
243
260
  protected renderToolbar() {
244
261
  return html`
245
262
  <wa-input
@@ -272,7 +289,7 @@ export class DocksExtensions extends DocksPart {
272
289
  <wa-dialog
273
290
  label="Register Extension"
274
291
  open
275
- @wa-request-close=${() => this.cancelRegisterExtension()}
292
+ @wa-after-hide=${this.handleRegisterDialogAfterHide}
276
293
  style="--wa-dialog-width: 500px;">
277
294
  <div style="display: flex; flex-direction: column; gap: 1rem; padding: 1rem;">
278
295
  <wa-input
@@ -22,6 +22,7 @@ import { confirmDialog, infoDialog } from "../dialogs";
22
22
  import { editorRegistry } from "../core/editorregistry";
23
23
  import { TOPIC_CONTRIBUTEIONS_CHANGED, type ContributionChangeEvent } from '../core/contributionregistry';
24
24
  import { icon } from '../core/icon-utils';
25
+ import { renderDropdownItem, renderDropdownSubmenu } from '../core/dropdown-item';
25
26
  import { i18n } from '../core/i18n';
26
27
  import { createLogger } from '../core/logger';
27
28
 
@@ -95,7 +96,6 @@ export class DocksFileBrowser extends DocksPart {
95
96
  protected renderToolbar() {
96
97
  return html`
97
98
  <docks-command icon="folder-open" title="${t.CONNECT_WORKSPACE}" dropdown="filebrowser.connections"></docks-command>
98
- <docks-command cmd="refresh_resource" icon="repeat" title="${t.REFRESH_RESOURCE}"></docks-command>
99
99
  <docks-command icon="docks file-plus" title="${t.CREATE_NEW}" dropdown="filebrowser.create"></docks-command>
100
100
  `;
101
101
  }
@@ -106,23 +106,26 @@ export class DocksFileBrowser extends DocksPart {
106
106
  const editorOptions = file ? editorRegistry.getEditorOptionsForInput(file) : []
107
107
  const hasOpenWith = editorOptions.length > 0
108
108
  return html`
109
- <docks-command cmd="open_editor" icon="folder-open">${t.OPEN}</docks-command>
109
+ ${renderDropdownItem({ cmd: 'open_editor', icon: 'folder-open', label: t.OPEN })}
110
110
  ${hasOpenWith ? html`
111
111
  <wa-dropdown-item>
112
112
  ${icon('folder-open', { slot: 'icon' })}
113
113
  ${t.OPEN_WITH}
114
- ${editorOptions.map(opt => html`
115
- <docks-command
116
- slot="submenu"
117
- cmd="open_editor"
118
- icon="${opt.icon ?? 'file'}"
119
- .params=${{ path: file!.getWorkspacePath(), editorId: opt.editorId }}>
120
- ${opt.title}
121
- </docks-command>
122
- `)}
114
+ ${editorOptions.map(opt => renderDropdownItem({
115
+ cmd: 'open_editor',
116
+ icon: opt.icon ?? 'file',
117
+ label: opt.title,
118
+ params: { path: file!.getWorkspacePath(), editorId: opt.editorId },
119
+ slot: 'submenu',
120
+ }))}
123
121
  </wa-dropdown-item>
124
122
  ` : nothing}
125
- <docks-command icon="docks file-plus" dropdown="filebrowser.create">${t.CREATE_NEW}</docks-command>
123
+ ${renderDropdownSubmenu({
124
+ icon: 'docks file-plus',
125
+ label: t.CREATE_NEW,
126
+ contributionTarget: 'filebrowser.create',
127
+ })}
128
+ ${renderDropdownItem({ cmd: 'refresh_resource', icon: 'repeat', label: t.REFRESH_RESOURCE })}
126
129
  `;
127
130
  }
128
131
 
@@ -58,7 +58,6 @@ export const showLanguageSelectorDialog = async (): Promise<void> => {
58
58
  label="Select Language"
59
59
  open
60
60
  light-dismiss
61
- @wa-request-close=${handleClose}
62
61
  @wa-after-hide=${handleAfterHide}>
63
62
  <style>
64
63
  .language-list {
@@ -1,47 +1,60 @@
1
1
  import { html, css } from "lit";
2
2
  import { customElement, state } from "lit/decorators.js";
3
3
  import { DocksElement } from "../parts/element";
4
- import { appLoaderService } from "../core/apploader";
5
4
  import { icon } from '../core/icon-utils';
5
+ import {
6
+ DEFAULT_LAYOUT_PANEL_VISIBILITY,
7
+ findStandardLayout,
8
+ LAYOUT_PANEL_OPTIONS,
9
+ type LayoutPanelVisibility,
10
+ } from "../core/layout-panels";
6
11
 
7
12
  @customElement('docks-layout-switcher')
8
13
  export class DocksLayoutSwitcher extends DocksElement {
9
14
  @state()
10
- private currentLayoutId = 'standard';
15
+ private panels: LayoutPanelVisibility = { ...DEFAULT_LAYOUT_PANEL_VISIBILITY };
11
16
 
12
17
  protected doBeforeUI() {
13
- this.currentLayoutId = appLoaderService.getCurrentLayoutId();
14
- const updateLayout = () => {
15
- this.currentLayoutId = appLoaderService.getCurrentLayoutId();
18
+ const syncPanels = () => {
19
+ const layout = findStandardLayout();
20
+ this.panels = layout?.getPanelVisibility() ?? { ...DEFAULT_LAYOUT_PANEL_VISIBILITY };
16
21
  this.requestUpdate();
17
22
  };
18
- window.addEventListener('app-loaded', updateLayout);
19
- window.addEventListener('layout-changed', updateLayout);
23
+ syncPanels();
24
+ window.addEventListener('app-loaded', syncPanels);
25
+ window.addEventListener('layout-panels-changed', syncPanels);
20
26
  return () => {
21
- window.removeEventListener('app-loaded', updateLayout);
22
- window.removeEventListener('layout-changed', updateLayout);
27
+ window.removeEventListener('app-loaded', syncPanels);
28
+ window.removeEventListener('layout-panels-changed', syncPanels);
23
29
  };
24
30
  }
25
31
 
26
32
  private async handleSelect(e: CustomEvent) {
27
- const layoutId = e.detail?.item?.value;
28
- if (!layoutId || layoutId === this.currentLayoutId) return;
33
+ const layout = findStandardLayout();
34
+ if (!layout) {
35
+ return;
36
+ }
37
+
38
+ const item = e.detail?.item as (HTMLElement & { value?: string; checked?: boolean }) | undefined;
39
+ const key = item?.value as keyof LayoutPanelVisibility | undefined;
40
+ if (!item || !key) {
41
+ return;
42
+ }
29
43
  try {
30
- await appLoaderService.setPreferredLayoutId(layoutId);
44
+ await layout.setPanelVisibility({
45
+ [key]: item.checked ?? false,
46
+ });
47
+ this.panels = layout.getPanelVisibility();
31
48
  } catch (err) {
32
- console.error('Failed to switch layout:', err);
49
+ console.error('Failed to update layout panels:', err);
33
50
  }
34
51
  }
35
52
 
36
53
  protected render() {
37
- const layouts = appLoaderService.getRegisteredLayouts();
38
- if (layouts.length <= 1) {
54
+ if (!findStandardLayout()) {
39
55
  return html``;
40
56
  }
41
57
 
42
- const currentLayout = layouts.find((l) => l.id === this.currentLayoutId);
43
- const layoutName = currentLayout?.name ?? this.currentLayoutId;
44
-
45
58
  return html`
46
59
  <wa-dropdown
47
60
  placement="bottom-end"
@@ -53,17 +66,17 @@ export class DocksLayoutSwitcher extends DocksElement {
53
66
  appearance="plain"
54
67
  size="s"
55
68
  with-caret
56
- title="Switch layout (current: ${layoutName})">
57
- <wa-icon name="table-cells" label="Layout"></wa-icon>
69
+ title="Show or hide layout panels">
70
+ ${icon('docks layout-standard-full', { label: 'Layout panels' })}
58
71
  </wa-button>
59
- ${layouts.map(
60
- (layout) => html`
72
+ ${LAYOUT_PANEL_OPTIONS.map(
73
+ (panel) => html`
61
74
  <wa-dropdown-item
62
- value="${layout.id}"
75
+ value="${panel.key}"
63
76
  type="checkbox"
64
- ?checked=${layout.id === this.currentLayoutId}>
65
- ${icon(layout.icon, { label: layout.name, slot: 'icon' })}
66
- ${layout.name}
77
+ ?checked=${this.panels[panel.key]}>
78
+ ${icon(panel.icon, { label: panel.label, slot: 'icon' })}
79
+ ${panel.label}
67
80
  </wa-dropdown-item>
68
81
  `
69
82
  )}
@@ -5,6 +5,8 @@ import { appLoaderService } from "../core/apploader";
5
5
 
6
6
  @customElement("docks-npm-packages")
7
7
  export class DocksNpmPackages extends DocksPart {
8
+ protected scrollMode: "scroller" | "native" | "none" = "none";
9
+
8
10
  static styles = [
9
11
  css`
10
12
  .packages-panel {
@@ -40,6 +40,12 @@ function isNewerThan(releaseTag: string, currentVersion: string): boolean {
40
40
  return false;
41
41
  }
42
42
 
43
+ export type ReleaseHistoryNavState = {
44
+ hasMultiple: boolean;
45
+ atOldest: boolean;
46
+ atNewest: boolean;
47
+ };
48
+
43
49
  @customElement("docks-release-history")
44
50
  export class DocksReleaseHistory extends DocksPart {
45
51
  protected scrollMode: "scroller" | "native" | "none" = "none";
@@ -59,19 +65,12 @@ export class DocksReleaseHistory extends DocksPart {
59
65
  display: flex;
60
66
  flex-direction: column;
61
67
  gap: 0.5rem;
62
- height: 100%;
63
68
  padding: 0.75rem;
64
69
  box-sizing: border-box;
65
70
  }
66
71
 
67
- .release-panel wa-scroller {
68
- flex: 1;
69
- min-height: 0;
70
- }
71
-
72
- .release-nav {
73
- display: flex;
74
- gap: 0.5rem;
72
+ .release-body {
73
+ white-space: normal;
75
74
  }
76
75
  `,
77
76
  ];
@@ -88,20 +87,35 @@ export class DocksReleaseHistory extends DocksPart {
88
87
  semverEqual(release.tag_name, this.appVersion),
89
88
  );
90
89
  this.currentReleaseIndex = currentIndex >= 0 ? currentIndex : 0;
90
+ this.notifyChanged();
91
91
  }
92
92
 
93
- private showOlder() {
93
+ public getNavState(): ReleaseHistoryNavState {
94
+ return {
95
+ hasMultiple: this.releases.length > 1,
96
+ atOldest: this.currentReleaseIndex >= this.releases.length - 1,
97
+ atNewest: this.currentReleaseIndex <= 0,
98
+ };
99
+ }
100
+
101
+ public showOlder() {
94
102
  if (this.currentReleaseIndex < this.releases.length - 1) {
95
103
  this.currentReleaseIndex += 1;
104
+ this.notifyChanged();
96
105
  }
97
106
  }
98
107
 
99
- private showNewer() {
108
+ public showNewer() {
100
109
  if (this.currentReleaseIndex > 0) {
101
110
  this.currentReleaseIndex -= 1;
111
+ this.notifyChanged();
102
112
  }
103
113
  }
104
114
 
115
+ private notifyChanged() {
116
+ this.dispatchEvent(new CustomEvent("release-history-changed", { bubbles: true, composed: true }));
117
+ }
118
+
105
119
  protected renderContent() {
106
120
  const release = this.releases[this.currentReleaseIndex];
107
121
  if (!release) {
@@ -110,8 +124,6 @@ export class DocksReleaseHistory extends DocksPart {
110
124
 
111
125
  const isCurrent = semverEqual(release.tag_name, this.appVersion);
112
126
  const showUpdateHint = !isCurrent && isNewerThan(release.tag_name, this.appVersion);
113
- const atOldest = this.currentReleaseIndex === this.releases.length - 1;
114
- const atNewest = this.currentReleaseIndex === 0;
115
127
  const bodyHtml = release.body ? parseMarkdownHtml(release.body) : "";
116
128
 
117
129
  return html`
@@ -132,36 +144,13 @@ export class DocksReleaseHistory extends DocksPart {
132
144
  ? html`
133
145
  <wa-callout variant="warning">
134
146
  <wa-icon slot="icon" name="triangle-exclamation"></wa-icon>
135
- Update available — reload the page to update
147
+ A newer release is available than the version you are running.
148
+ Use <strong>Update Available</strong> in the toolbar when it appears.
136
149
  </wa-callout>
137
150
  `
138
151
  : ""}
139
- <wa-scroller orientation="vertical">
140
- ${bodyHtml
141
- ? html`<div style="white-space: normal;">${unsafeHTML(bodyHtml)}</div>`
142
- : ""}
143
- </wa-scroller>
144
- ${this.releases.length > 1
145
- ? html`
146
- <div class="release-nav">
147
- <wa-button-group>
148
- <wa-button
149
- variant="default"
150
- ?disabled=${atOldest}
151
- @click=${() => this.showOlder()}
152
- >
153
- ← Previous
154
- </wa-button>
155
- <wa-button
156
- variant="default"
157
- ?disabled=${atNewest}
158
- @click=${() => this.showNewer()}
159
- >
160
- Next →
161
- </wa-button>
162
- </wa-button-group>
163
- </div>
164
- `
152
+ ${bodyHtml
153
+ ? html`<div class="release-body">${unsafeHTML(bodyHtml)}</div>`
165
154
  : ""}
166
155
  </div>
167
156
  `;
@@ -76,7 +76,6 @@ function updateProgressDialog(forceOpen = false) {
76
76
  open
77
77
  light-dismiss
78
78
  style="--width: 600px;"
79
- @wa-request-close=${handleClose}
80
79
  @wa-after-hide=${handleAfterHide}
81
80
  >
82
81
  <style>
@@ -2,46 +2,10 @@ import "../layouts/standard-layout";
2
2
  import { contributionRegistry } from "../core/contributionregistry";
3
3
  import { SYSTEM_LAYOUTS } from "../core/contribution-targets";
4
4
 
5
- const standardLayouts: Array<{
6
- id: string;
7
- name: string;
8
- label: string;
9
- icon?: string;
10
- component: string | { tag: string; attributes?: Record<string, string> };
11
- }> = [
12
- {
13
- id: "standard",
14
- name: "Standard",
15
- label: "Standard",
16
- icon: "docks layout-standard",
17
- component: "docks-standard-layout",
18
- },
19
- {
20
- id: "standard-bottom-panel",
21
- name: "Standard (bottom panel)",
22
- label: "Standard (bottom panel)",
23
- icon: "docks layout-standard-bottom-panel",
24
- component: { tag: "docks-standard-layout", attributes: { "show-bottom-panel": "true" } },
25
- },
26
- {
27
- id: "standard-bottom-sidebar",
28
- name: "Standard (bottom sidebar)",
29
- label: "Standard (bottom sidebar)",
30
- icon: "docks layout-standard-bottom-sidebar",
31
- component: { tag: "docks-standard-layout", attributes: { "show-bottom-sidebar": "true" } },
32
- },
33
- {
34
- id: "standard-full",
35
- name: "Standard (panel + sidebar)",
36
- label: "Standard (panel + sidebar)",
37
- icon: "docks layout-standard-full",
38
- component: {
39
- tag: "docks-standard-layout",
40
- attributes: { "show-bottom-panel": "true", "show-bottom-sidebar": "true" },
41
- },
42
- },
43
- ];
44
-
45
- for (const layout of standardLayouts) {
46
- contributionRegistry.registerContribution(SYSTEM_LAYOUTS, layout);
47
- }
5
+ contributionRegistry.registerContribution(SYSTEM_LAYOUTS, {
6
+ id: "standard",
7
+ name: "Standard",
8
+ label: "Standard",
9
+ icon: "docks layout-standard-full",
10
+ component: "docks-standard-layout",
11
+ });