mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-10-18 15:48:31 +00:00
279 lines
8.3 KiB
TypeScript
279 lines
8.3 KiB
TypeScript
import {
|
|
injectable,
|
|
postConstruct,
|
|
inject,
|
|
} from '@theia/core/shared/inversify';
|
|
import { Message } from '@theia/core/shared/@phosphor/messaging';
|
|
import { addEventListener } from '@theia/core/lib/browser/widgets/widget';
|
|
import { DialogProps } from '@theia/core/lib/browser/dialogs';
|
|
import { AbstractDialog } from '../theia/dialogs/dialogs';
|
|
import {
|
|
LibraryPackage,
|
|
LibrarySearch,
|
|
LibraryService,
|
|
} from '../../common/protocol/library-service';
|
|
import { ListWidget } from '../widgets/component-list/list-widget';
|
|
import { Installable } from '../../common/protocol';
|
|
import { ListItemRenderer } from '../widgets/component-list/list-item-renderer';
|
|
import { nls } from '@theia/core/lib/common';
|
|
import { LibraryFilterRenderer } from '../widgets/component-list/filter-renderer';
|
|
import { findChildTheiaButton, splitByBoldTag } from '../utils/dom';
|
|
import { UserAbortError } from '../../common/protocol/progressible';
|
|
|
|
@injectable()
|
|
export class LibraryListWidget extends ListWidget<
|
|
LibraryPackage,
|
|
LibrarySearch
|
|
> {
|
|
static WIDGET_ID = 'library-list-widget';
|
|
static WIDGET_LABEL = nls.localize(
|
|
'arduino/library/title',
|
|
'Library Manager'
|
|
);
|
|
|
|
constructor(
|
|
@inject(LibraryService) private service: LibraryService,
|
|
@inject(ListItemRenderer) itemRenderer: ListItemRenderer<LibraryPackage>,
|
|
@inject(LibraryFilterRenderer) filterRenderer: LibraryFilterRenderer
|
|
) {
|
|
super({
|
|
id: LibraryListWidget.WIDGET_ID,
|
|
label: LibraryListWidget.WIDGET_LABEL,
|
|
iconClass: 'fa fa-arduino-library',
|
|
searchable: service,
|
|
installable: service,
|
|
itemLabel: (item: LibraryPackage) => item.name,
|
|
itemRenderer,
|
|
filterRenderer,
|
|
defaultSearchOptions: { query: '', type: 'All', topic: 'All' },
|
|
});
|
|
}
|
|
|
|
@postConstruct()
|
|
protected override init(): void {
|
|
super.init();
|
|
this.toDispose.pushAll([
|
|
this.notificationCenter.onLibraryDidInstall(() =>
|
|
this.refresh(undefined)
|
|
),
|
|
this.notificationCenter.onLibraryDidUninstall(() =>
|
|
this.refresh(undefined)
|
|
),
|
|
]);
|
|
}
|
|
|
|
protected override async install({
|
|
item,
|
|
progressId,
|
|
version,
|
|
}: {
|
|
item: LibraryPackage;
|
|
progressId: string;
|
|
version: Installable.Version;
|
|
}): Promise<void> {
|
|
const dependencies = await this.service.listDependencies({
|
|
item,
|
|
version,
|
|
filterSelf: true,
|
|
});
|
|
let installDependencies: boolean | undefined = undefined;
|
|
if (dependencies.length) {
|
|
const message = document.createElement('div');
|
|
const textContent =
|
|
dependencies.length === 1
|
|
? nls.localize(
|
|
'arduino/library/needsOneDependency',
|
|
'The library <b>{0}:{1}</b> needs another dependency currently not installed:',
|
|
item.name,
|
|
version
|
|
)
|
|
: nls.localize(
|
|
'arduino/library/needsMultipleDependencies',
|
|
'The library <b>{0}:{1}</b> needs some other dependencies currently not installed:',
|
|
item.name,
|
|
version
|
|
);
|
|
const segments = splitByBoldTag(textContent);
|
|
if (!segments) {
|
|
message.textContent = textContent;
|
|
} else {
|
|
segments.map((segment) => {
|
|
const span = document.createElement('span');
|
|
if (typeof segment === 'string') {
|
|
span.textContent = segment;
|
|
} else {
|
|
const bold = document.createElement('b');
|
|
bold.textContent = segment.textContent;
|
|
span.appendChild(bold);
|
|
}
|
|
message.appendChild(span);
|
|
});
|
|
}
|
|
const listContainer = document.createElement('div');
|
|
listContainer.style.maxHeight = '300px';
|
|
listContainer.style.overflowY = 'auto';
|
|
const list = document.createElement('ul');
|
|
list.style.listStyleType = 'none';
|
|
for (const { name } of dependencies) {
|
|
const listItem = document.createElement('li');
|
|
listItem.textContent = ` - ${name}`;
|
|
listItem.style.fontWeight = 'bold';
|
|
list.appendChild(listItem);
|
|
}
|
|
listContainer.appendChild(list);
|
|
message.appendChild(listContainer);
|
|
const question = document.createElement('div');
|
|
question.textContent =
|
|
dependencies.length === 1
|
|
? nls.localize(
|
|
'arduino/library/installOneMissingDependency',
|
|
'Would you like to install the missing dependency?'
|
|
)
|
|
: nls.localize(
|
|
'arduino/library/installMissingDependencies',
|
|
'Would you like to install all the missing dependencies?'
|
|
);
|
|
message.appendChild(question);
|
|
const result = await new MessageBoxDialog({
|
|
title: nls.localize(
|
|
'arduino/library/installLibraryDependencies',
|
|
'Install library dependencies'
|
|
),
|
|
message,
|
|
buttons: [
|
|
nls.localize(
|
|
'arduino/library/installWithoutDependencies',
|
|
'Install without dependencies'
|
|
),
|
|
nls.localize('arduino/library/installAll', 'Install All'),
|
|
],
|
|
maxWidth: 740, // Aligned with `settings-dialog.css`.
|
|
}).open();
|
|
|
|
if (result) {
|
|
const { response } = result;
|
|
if (response === 0) {
|
|
// Current only
|
|
installDependencies = false;
|
|
} else if (response === 1) {
|
|
// All
|
|
installDependencies = true;
|
|
}
|
|
} else {
|
|
throw new UserAbortError();
|
|
}
|
|
} else {
|
|
// The lib does not have any dependencies.
|
|
installDependencies = false;
|
|
}
|
|
|
|
if (typeof installDependencies === 'boolean') {
|
|
await this.service.install({
|
|
item,
|
|
version,
|
|
progressId,
|
|
installDependencies,
|
|
});
|
|
this.messageService.info(
|
|
nls.localize(
|
|
'arduino/library/installedSuccessfully',
|
|
'Successfully installed library {0}:{1}',
|
|
item.name,
|
|
version
|
|
),
|
|
{ timeout: 3000 }
|
|
);
|
|
}
|
|
}
|
|
|
|
protected override async uninstall({
|
|
item,
|
|
progressId,
|
|
}: {
|
|
item: LibraryPackage;
|
|
progressId: string;
|
|
}): Promise<void> {
|
|
await super.uninstall({ item, progressId });
|
|
this.messageService.info(
|
|
nls.localize(
|
|
'arduino/library/uninstalledSuccessfully',
|
|
'Successfully uninstalled library {0}:{1}',
|
|
item.name,
|
|
item.installedVersion!
|
|
),
|
|
{ timeout: 3000 }
|
|
);
|
|
}
|
|
}
|
|
|
|
class MessageBoxDialog extends AbstractDialog<MessageBoxDialog.Result> {
|
|
protected response: number;
|
|
|
|
constructor(protected readonly options: MessageBoxDialog.Options) {
|
|
super(options);
|
|
this.contentNode.appendChild(this.createMessageNode(this.options.message));
|
|
(
|
|
options.buttons || [nls.localize('vscode/issueMainService/ok', 'OK')]
|
|
).forEach((text, index) => {
|
|
const button = this.createButton(text);
|
|
const isPrimaryButton =
|
|
index === (options.buttons ? options.buttons.length - 1 : 0);
|
|
button.title = text;
|
|
button.classList.add(
|
|
isPrimaryButton ? 'main' : 'secondary',
|
|
'message-box-dialog-button'
|
|
);
|
|
this.controlPanel.appendChild(button);
|
|
this.toDisposeOnDetach.push(
|
|
addEventListener(button, 'click', () => {
|
|
this.response = index;
|
|
this.accept();
|
|
})
|
|
);
|
|
});
|
|
}
|
|
|
|
protected override onCloseRequest(message: Message): void {
|
|
super.onCloseRequest(message);
|
|
this.accept();
|
|
}
|
|
|
|
get value(): MessageBoxDialog.Result {
|
|
return { response: this.response };
|
|
}
|
|
|
|
protected createMessageNode(message: string | HTMLElement): HTMLElement {
|
|
if (typeof message === 'string') {
|
|
const messageNode = document.createElement('div');
|
|
messageNode.textContent = message;
|
|
return messageNode;
|
|
}
|
|
return message;
|
|
}
|
|
|
|
protected override handleEnter(event: KeyboardEvent): boolean | void {
|
|
this.response = 0;
|
|
super.handleEnter(event);
|
|
}
|
|
|
|
protected override onAfterAttach(message: Message): void {
|
|
super.onAfterAttach(message);
|
|
findChildTheiaButton(this.controlPanel)?.focus();
|
|
}
|
|
}
|
|
export namespace MessageBoxDialog {
|
|
export interface Options extends DialogProps {
|
|
/**
|
|
* When empty, `['OK']` will be inferred.
|
|
*/
|
|
buttons?: string[];
|
|
message: string | HTMLElement;
|
|
}
|
|
export interface Result {
|
|
/**
|
|
* The index of `buttons` that was clicked.
|
|
*/
|
|
readonly response: number;
|
|
}
|
|
}
|