commentNode.ts 19.1 KB
Newer Older
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import * as modes from 'vs/editor/common/modes';
9
import { ActionsOrientation, ActionViewItem, ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
P
Peng Lyu 已提交
10
import { Action, IActionRunner, IAction } from 'vs/base/common/actions';
P
Peng Lyu 已提交
11
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
P
Peng Lyu 已提交
12
import { URI } from 'vs/base/common/uri';
13 14 15 16 17 18
import { ITextModel } from 'vs/editor/common/model';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
19 20
import { ICommentService } from 'vs/workbench/contrib/comments/browser/commentService';
import { SimpleCommentEditor } from 'vs/workbench/contrib/comments/browser/simpleCommentEditor';
21
import { Selection } from 'vs/editor/common/core/selection';
22
import { Emitter, Event } from 'vs/base/common/event';
23
import { INotificationService } from 'vs/platform/notification/common/notification';
P
Peng Lyu 已提交
24 25
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
26
import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdown';
P
Peng Lyu 已提交
27
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
28
import { ToggleReactionsAction, ReactionAction, ReactionActionViewItem } from './reactionsAction';
29 30
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ICommentThreadWidget } from 'vs/workbench/contrib/comments/common/commentThreadWidget';
31
import { MenuItemAction, SubmenuItemAction, IMenu } from 'vs/platform/actions/common/actions';
P
Peng Lyu 已提交
32
import { ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
P
Peng Lyu 已提交
33
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
P
Peng Lyu 已提交
34
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
35
import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commentFormActions';
36 37 38 39 40 41 42 43 44

export class CommentNode extends Disposable {
	private _domNode: HTMLElement;
	private _body: HTMLElement;
	private _md: HTMLElement;
	private _clearTimeout: any;

	private _editAction: Action;
	private _commentEditContainer: HTMLElement;
45
	private _commentDetailsContainer: HTMLElement;
P
Peng Lyu 已提交
46
	private _actionsToolbarContainer: HTMLElement;
47
	private _reactionsActionBar?: ActionBar;
P
Peng Lyu 已提交
48
	private _reactionActionsContainer?: HTMLElement;
49
	private _commentEditor: SimpleCommentEditor | null;
P
Peng Lyu 已提交
50
	private _commentEditorDisposables: IDisposable[] = [];
51
	private _commentEditorModel: ITextModel;
52
	private _isPendingLabel: HTMLElement;
53
	private _contextKeyService: IContextKeyService;
P
Peng Lyu 已提交
54
	private _commentContextValue: IContextKey<string>;
55

P
Peng Lyu 已提交
56
	protected actionRunner?: IActionRunner;
57
	protected toolbar: ToolBar | undefined;
58
	private _commentFormActions: CommentFormActions;
P
Peng Lyu 已提交
59

60 61
	private _onDidDelete = new Emitter<CommentNode>();

62 63 64 65
	public get domNode(): HTMLElement {
		return this._domNode;
	}

66 67
	public isEditing: boolean;

68
	constructor(
69
		private commentThread: modes.CommentThread,
70
		public comment: modes.Comment,
71
		private owner: string,
72
		private resource: URI,
73 74
		private parentEditor: ICodeEditor,
		private parentThread: ICommentThreadWidget,
75
		private markdownRenderer: MarkdownRenderer,
76 77 78 79 80
		@IThemeService private themeService: IThemeService,
		@IInstantiationService private instantiationService: IInstantiationService,
		@ICommentService private commentService: ICommentService,
		@IModelService private modelService: IModelService,
		@IModeService private modeService: IModeService,
P
Peng Lyu 已提交
81
		@IKeybindingService private keybindingService: IKeybindingService,
82
		@INotificationService private notificationService: INotificationService,
83 84
		@IContextMenuService private contextMenuService: IContextMenuService,
		@IContextKeyService contextKeyService: IContextKeyService
85 86 87 88
	) {
		super();

		this._domNode = dom.$('div.review-comment');
89
		this._contextKeyService = contextKeyService.createScoped(this._domNode);
P
Peng Lyu 已提交
90 91
		this._commentContextValue = this._contextKeyService.createKey('comment', comment.contextValue);

92 93
		this._domNode.tabIndex = 0;
		const avatar = dom.append(this._domNode, dom.$('div.avatar-container'));
94 95 96
		if (comment.userIconPath) {
			const img = <HTMLImageElement>dom.append(avatar, dom.$('img.avatar'));
			img.src = comment.userIconPath.toString();
97
			img.onerror = _ => img.remove();
98
		}
99
		this._commentDetailsContainer = dom.append(this._domNode, dom.$('.review-comment-contents'));
100

101
		this.createHeader(this._commentDetailsContainer);
102

103
		this._body = dom.append(this._commentDetailsContainer, dom.$('div.comment-body'));
104 105 106
		this._md = this.markdownRenderer.render(comment.body).element;
		this._body.appendChild(this._md);

P
Peng Lyu 已提交
107
		if (this.comment.commentReactions && this.comment.commentReactions.length && this.comment.commentReactions.filter(reaction => !!reaction.count).length) {
P
Peng Lyu 已提交
108
			this.createReactionsContainer(this._commentDetailsContainer);
P
Peng Lyu 已提交
109
		}
P
Peng Lyu 已提交
110

111 112 113 114 115
		this._domNode.setAttribute('aria-label', `${comment.userName}, ${comment.body.value}`);
		this._domNode.setAttribute('role', 'treeitem');
		this._clearTimeout = null;
	}

116 117 118 119
	public get onDidDelete(): Event<CommentNode> {
		return this._onDidDelete.event;
	}

120 121 122 123 124
	private createHeader(commentDetailsContainer: HTMLElement): void {
		const header = dom.append(commentDetailsContainer, dom.$('div.comment-title'));
		const author = dom.append(header, dom.$('strong.author'));
		author.innerText = this.comment.userName;

125 126
		this._isPendingLabel = dom.append(header, dom.$('span.isPending'));

P
Peng Lyu 已提交
127 128 129 130
		if (this.comment.label) {
			this._isPendingLabel.innerText = this.comment.label;
		} else {
			this._isPendingLabel.innerText = '';
R
rebornix 已提交
131 132
		}

P
Peng Lyu 已提交
133 134 135 136
		this._actionsToolbarContainer = dom.append(header, dom.$('.comment-actions.hidden'));
		this.createActionsToolbar();
	}

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
	private getToolbarActions(menu: IMenu): { primary: IAction[], secondary: IAction[] } {
		const contributedActions = menu.getActions({ shouldForwardArgs: true });
		const primary: IAction[] = [];
		const secondary: IAction[] = [];
		const result = { primary, secondary };
		fillInActions(contributedActions, result, false, g => /^inline/.test(g));
		return result;
	}

	private createToolbar() {
		this.toolbar = new ToolBar(this._actionsToolbarContainer, this.contextMenuService, {
			actionViewItemProvider: action => {
				if (action.id === ToggleReactionsAction.ID) {
					return new DropdownMenuActionViewItem(
						action,
						(<ToggleReactionsAction>action).menuActions,
						this.contextMenuService,
						action => {
							return this.actionViewItemProvider(action as Action);
						},
						this.actionRunner!,
						undefined,
						'toolbar-toggle-pickReactions',
						() => { return AnchorAlignment.RIGHT; }
					);
				}
				return this.actionViewItemProvider(action as Action);
			},
			orientation: ActionsOrientation.HORIZONTAL
		});

		this.toolbar.context = {
			thread: this.commentThread,
			commentUniqueId: this.comment.uniqueIdInThread,
			$mid: 9
		};

		this.registerActionBarListeners(this._actionsToolbarContainer);
		this._register(this.toolbar);
	}

P
Peng Lyu 已提交
178
	private createActionsToolbar() {
P
Peng Lyu 已提交
179
		const actions: IAction[] = [];
P
Peng Lyu 已提交
180

P
Peng Lyu 已提交
181 182 183
		let hasReactionHandler = this.commentService.hasReactionHandler(this.owner);

		if (hasReactionHandler) {
184
			let toggleReactionAction = this.createReactionPicker(this.comment.commentReactions || []);
P
Peng Lyu 已提交
185
			actions.push(toggleReactionAction);
186 187
		}

P
Peng Lyu 已提交
188
		let commentMenus = this.commentService.getCommentMenus(this.owner);
189
		const menu = commentMenus.getCommentTitleActions(this.comment, this._contextKeyService);
M
Matt Bierner 已提交
190 191
		this._register(menu);
		this._register(menu.onDidChange(e => {
192 193 194 195 196 197
			const { primary, secondary } = this.getToolbarActions(menu);
			if (!this.toolbar && (primary.length || secondary.length)) {
				this.createToolbar();
			}

			this.toolbar!.setActions(primary, secondary)();
198 199
		}));

200 201
		const { primary, secondary } = this.getToolbarActions(menu);
		actions.push(...primary);
P
Peng Lyu 已提交
202

203 204 205
		if (actions.length || secondary.length) {
			this.createToolbar();
			this.toolbar!.setActions(actions, secondary)();
206 207 208
		}
	}

209
	actionViewItemProvider(action: Action) {
P
Peng Lyu 已提交
210
		let options = {};
211
		if (action.id === ToggleReactionsAction.ID) {
P
Peng Lyu 已提交
212 213
			options = { label: false, icon: true };
		} else {
P
Peng Lyu 已提交
214
			options = { label: false, icon: true };
P
Peng Lyu 已提交
215
		}
P
Peng Lyu 已提交
216

P
Peng Lyu 已提交
217
		if (action.id === ReactionAction.ID) {
218
			let item = new ReactionActionViewItem(action);
P
Peng Lyu 已提交
219
			return item;
P
Peng Lyu 已提交
220
		} else if (action instanceof MenuItemAction) {
P
Peng Lyu 已提交
221
			let item = new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService);
P
Peng Lyu 已提交
222
			return item;
P
Peng Lyu 已提交
223
		} else {
224
			let item = new ActionViewItem({}, action, options);
P
Peng Lyu 已提交
225 226
			return item;
		}
P
Peng Lyu 已提交
227
	}
P
Peng Lyu 已提交
228

229
	private createReactionPicker(reactionGroup: modes.CommentReaction[]): ToggleReactionsAction {
230
		let toggleReactionActionViewItem: DropdownMenuActionViewItem;
P
Peng Lyu 已提交
231
		let toggleReactionAction = this._register(new ToggleReactionsAction(() => {
232 233
			if (toggleReactionActionViewItem) {
				toggleReactionActionViewItem.show();
P
Peng Lyu 已提交
234 235 236 237 238 239 240 241
			}
		}, nls.localize('commentToggleReaction', "Toggle Reaction")));

		let reactionMenuActions: Action[] = [];
		if (reactionGroup && reactionGroup.length) {
			reactionMenuActions = reactionGroup.map((reaction) => {
				return new Action(`reaction.command.${reaction.label}`, `${reaction.label}`, '', true, async () => {
					try {
242
						await this.commentService.toggleReaction(this.owner, this.resource, this.commentThread, this.comment, reaction);
P
Peng Lyu 已提交
243 244 245 246 247 248 249 250 251 252 253 254
					} catch (e) {
						const error = e.message
							? nls.localize('commentToggleReactionError', "Toggling the comment reaction failed: {0}.", e.message)
							: nls.localize('commentToggleReactionDefaultError', "Toggling the comment reaction failed");
						this.notificationService.error(error);
					}
				});
			});
		}

		toggleReactionAction.menuActions = reactionMenuActions;

255
		toggleReactionActionViewItem = new DropdownMenuActionViewItem(
P
Peng Lyu 已提交
256 257 258 259 260
			toggleReactionAction,
			(<ToggleReactionsAction>toggleReactionAction).menuActions,
			this.contextMenuService,
			action => {
				if (action.id === ToggleReactionsAction.ID) {
261
					return toggleReactionActionViewItem;
P
Peng Lyu 已提交
262
				}
263
				return this.actionViewItemProvider(action as Action);
P
Peng Lyu 已提交
264
			},
M
Matt Bierner 已提交
265
			this.actionRunner!,
P
Peng Lyu 已提交
266 267 268 269 270 271 272 273
			undefined,
			'toolbar-toggle-pickReactions',
			() => { return AnchorAlignment.RIGHT; }
		);

		return toggleReactionAction;
	}

P
Peng Lyu 已提交
274
	private createReactionsContainer(commentDetailsContainer: HTMLElement): void {
P
Peng Lyu 已提交
275 276
		this._reactionActionsContainer = dom.append(commentDetailsContainer, dom.$('div.comment-reactions'));
		this._reactionsActionBar = new ActionBar(this._reactionActionsContainer, {
277
			actionViewItemProvider: action => {
P
Peng Lyu 已提交
278
				if (action.id === ToggleReactionsAction.ID) {
279
					return new DropdownMenuActionViewItem(
P
Peng Lyu 已提交
280 281 282 283
						action,
						(<ToggleReactionsAction>action).menuActions,
						this.contextMenuService,
						action => {
284
							return this.actionViewItemProvider(action as Action);
P
Peng Lyu 已提交
285
						},
M
Matt Bierner 已提交
286
						this.actionRunner!,
P
Peng Lyu 已提交
287 288 289 290 291
						undefined,
						'toolbar-toggle-pickReactions',
						() => { return AnchorAlignment.RIGHT; }
					);
				}
292
				return this.actionViewItemProvider(action as Action);
P
Peng Lyu 已提交
293 294
			}
		});
M
Matt Bierner 已提交
295
		this._register(this._reactionsActionBar);
P
Peng Lyu 已提交
296

P
Peng Lyu 已提交
297 298 299
		let hasReactionHandler = this.commentService.hasReactionHandler(this.owner);
		this.comment.commentReactions!.filter(reaction => !!reaction.count).map(reaction => {
			let action = new ReactionAction(`reaction.${reaction.label}`, `${reaction.label}`, reaction.hasReacted && (reaction.canEdit || hasReactionHandler) ? 'active' : '', (reaction.canEdit || hasReactionHandler), async () => {
P
Peng Lyu 已提交
300
				try {
301
					await this.commentService.toggleReaction(this.owner, this.resource, this.commentThread, this.comment, reaction);
P
Peng Lyu 已提交
302 303 304 305 306 307 308 309 310 311 312 313 314 315
				} catch (e) {
					let error: string;

					if (reaction.hasReacted) {
						error = e.message
							? nls.localize('commentDeleteReactionError', "Deleting the comment reaction failed: {0}.", e.message)
							: nls.localize('commentDeleteReactionDefaultError', "Deleting the comment reaction failed");
					} else {
						error = e.message
							? nls.localize('commentAddReactionError', "Deleting the comment reaction failed: {0}.", e.message)
							: nls.localize('commentAddReactionDefaultError', "Deleting the comment reaction failed");
					}
					this.notificationService.error(error);
				}
P
Peng Lyu 已提交
316
			}, reaction.iconPath, reaction.count);
P
Peng Lyu 已提交
317

M
Matt Bierner 已提交
318 319 320
			if (this._reactionsActionBar) {
				this._reactionsActionBar.push(action, { label: true, icon: true });
			}
P
Peng Lyu 已提交
321
		});
P
Peng Lyu 已提交
322

P
Peng Lyu 已提交
323
		if (hasReactionHandler) {
324
			let toggleReactionAction = this.createReactionPicker(this.comment.commentReactions || []);
P
Peng Lyu 已提交
325
			this._reactionsActionBar.push(toggleReactionAction, { label: false, icon: true });
P
Peng Lyu 已提交
326
		}
P
Peng Lyu 已提交
327 328
	}

329 330
	private createCommentEditor(): void {
		const container = dom.append(this._commentEditContainer, dom.$('.edit-textarea'));
331
		this._commentEditor = this.instantiationService.createInstance(SimpleCommentEditor, container, SimpleCommentEditor.getEditorOptions(), this.parentEditor, this.parentThread);
P
Peng Lyu 已提交
332
		const resource = URI.parse(`comment:commentinput-${this.comment.uniqueIdInThread}-${Date.now()}.md`);
B
Benjamin Pasero 已提交
333
		this._commentEditorModel = this.modelService.createModel('', this.modeService.createByFilepathOrFirstLine(resource), resource, false);
334 335 336 337 338

		this._commentEditor.setModel(this._commentEditorModel);
		this._commentEditor.setValue(this.comment.body.value);
		this._commentEditor.layout({ width: container.clientWidth - 14, height: 90 });
		this._commentEditor.focus();
P
Peng Lyu 已提交
339

340 341 342 343
		const lastLine = this._commentEditorModel.getLineCount();
		const lastColumn = this._commentEditorModel.getLineContent(lastLine).length + 1;
		this._commentEditor.setSelection(new Selection(lastLine, lastColumn, lastLine, lastColumn));

344 345 346 347 348 349 350 351
		let commentThread = this.commentThread;
		commentThread.input = {
			uri: this._commentEditor.getModel()!.uri,
			value: this.comment.body.value
		};
		this.commentService.setActiveCommentThread(commentThread);

		this._commentEditorDisposables.push(this._commentEditor.onDidFocusEditorWidget(() => {
352
			commentThread.input = {
353
				uri: this._commentEditor!.getModel()!.uri,
354 355
				value: this.comment.body.value
			};
356
			this.commentService.setActiveCommentThread(commentThread);
357
		}));
358

359 360 361 362 363 364 365 366
		this._commentEditorDisposables.push(this._commentEditor.onDidChangeModelContent(e => {
			if (commentThread.input && this._commentEditor && this._commentEditor.getModel()!.uri === commentThread.input.uri) {
				let newVal = this._commentEditor.getValue();
				if (newVal !== commentThread.input.value) {
					let input = commentThread.input;
					input.value = newVal;
					commentThread.input = input;
					this.commentService.setActiveCommentThread(commentThread);
P
Peng Lyu 已提交
367
				}
368 369
			}
		}));
P
Peng Lyu 已提交
370

M
Matt Bierner 已提交
371 372
		this._register(this._commentEditor);
		this._register(this._commentEditorModel);
373 374 375
	}

	private removeCommentEditor() {
376
		this.isEditing = false;
P
Peng Lyu 已提交
377 378 379
		if (this._editAction) {
			this._editAction.enabled = true;
		}
380 381
		this._body.classList.remove('hidden');

P
Peng Lyu 已提交
382 383 384 385
		if (this._commentEditorModel) {
			this._commentEditorModel.dispose();
		}

P
Peng Lyu 已提交
386 387
		this._commentEditorDisposables.forEach(dispose => dispose.dispose());
		this._commentEditorDisposables = [];
388 389 390 391
		if (this._commentEditor) {
			this._commentEditor.dispose();
			this._commentEditor = null;
		}
392 393 394 395

		this._commentEditContainer.remove();
	}

P
Peng Lyu 已提交
396 397 398 399 400 401 402 403 404 405 406
	public switchToEditMode() {
		if (this.isEditing) {
			return;
		}

		this.isEditing = true;
		this._body.classList.add('hidden');
		this._commentEditContainer = dom.append(this._commentDetailsContainer, dom.$('.edit-container'));
		this.createCommentEditor();
		const formActions = dom.append(this._commentEditContainer, dom.$('.form-actions'));

407
		const menus = this.commentService.getCommentMenus(this.owner);
408
		const menu = menus.getCommentActions(this.comment, this._contextKeyService);
409

M
Matt Bierner 已提交
410 411
		this._register(menu);
		this._register(menu.onDidChange(() => {
412
			this._commentFormActions.setActions(menu);
413
		}));
P
Peng Lyu 已提交
414

415 416
		this._commentFormActions = new CommentFormActions(formActions, (action: IAction): void => {
			let text = this._commentEditor!.getValue();
P
Peng Lyu 已提交
417

418 419 420 421 422 423
			action.run({
				thread: this.commentThread,
				commentUniqueId: this.comment.uniqueIdInThread,
				text: text,
				$mid: 10
			});
424

425 426
			this.removeCommentEditor();
		}, this.themeService);
427

428
		this._commentFormActions.setActions(menu);
P
Peng Lyu 已提交
429 430
	}

431
	private registerActionBarListeners(actionsContainer: HTMLElement): void {
M
Matt Bierner 已提交
432
		this._register(dom.addDisposableListener(this._domNode, 'mouseenter', () => {
433 434 435
			actionsContainer.classList.remove('hidden');
		}));

M
Matt Bierner 已提交
436
		this._register(dom.addDisposableListener(this._domNode, 'focus', () => {
437 438 439
			actionsContainer.classList.remove('hidden');
		}));

M
Matt Bierner 已提交
440
		this._register(dom.addDisposableListener(this._domNode, 'mouseleave', () => {
441 442 443 444 445 446 447
			if (!this._domNode.contains(document.activeElement)) {
				actionsContainer.classList.add('hidden');
			}
		}));
	}

	update(newComment: modes.Comment) {
448

449 450 451 452 453 454
		if (newComment.body !== this.comment.body) {
			this._body.removeChild(this._md);
			this._md = this.markdownRenderer.render(newComment.body).element;
			this._body.appendChild(this._md);
		}

P
Peng Lyu 已提交
455 456 457 458 459 460 461 462
		if (newComment.mode !== undefined && newComment.mode !== this.comment.mode) {
			if (newComment.mode === modes.CommentMode.Editing) {
				this.switchToEditMode();
			} else {
				this.removeCommentEditor();
			}
		}

R
rebornix 已提交
463 464
		this.comment = newComment;

465 466
		if (newComment.label) {
			this._isPendingLabel.innerText = newComment.label;
467 468 469 470
		} else {
			this._isPendingLabel.innerText = '';
		}

471
		// update comment reactions
P
Peng Lyu 已提交
472 473
		if (this._reactionActionsContainer) {
			this._reactionActionsContainer.remove();
474 475 476 477 478 479
		}

		if (this._reactionsActionBar) {
			this._reactionsActionBar.clear();
		}

480
		if (this.comment.commentReactions && this.comment.commentReactions.some(reaction => !!reaction.count)) {
P
Peng Lyu 已提交
481
			this.createReactionsContainer(this._commentDetailsContainer);
482
		}
P
Peng Lyu 已提交
483 484 485 486 487 488

		if (this.comment.contextValue) {
			this._commentContextValue.set(this.comment.contextValue);
		} else {
			this._commentContextValue.reset();
		}
489 490 491 492 493 494 495 496 497 498 499
	}

	focus() {
		this.domNode.focus();
		if (!this._clearTimeout) {
			dom.addClass(this.domNode, 'focus');
			this._clearTimeout = setTimeout(() => {
				dom.removeClass(this.domNode, 'focus');
			}, 3000);
		}
	}
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
}

function fillInActions(groups: [string, Array<MenuItemAction | SubmenuItemAction>][], target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, useAlternativeActions: boolean, isPrimaryGroup: (group: string) => boolean = group => group === 'navigation'): void {
	for (let tuple of groups) {
		let [group, actions] = tuple;
		if (useAlternativeActions) {
			actions = actions.map(a => (a instanceof MenuItemAction) && !!a.alt ? a.alt : a);
		}

		if (isPrimaryGroup(group)) {
			const to = Array.isArray<IAction>(target) ? target : target.primary;

			to.unshift(...actions);
		} else {
			const to = Array.isArray<IAction>(target) ? target : target.secondary;

			if (to.length > 0) {
				to.push(new Separator());
			}

			to.push(...actions);
		}
	}
523
}