Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c8e447cc57 | ||
![]() |
2e7f2d94bd | ||
![]() |
9b49712669 | ||
![]() |
0ab28266df | ||
![]() |
b09ae48536 | ||
![]() |
2aad0e3b16 | ||
![]() |
58aac236bf | ||
![]() |
ec24b6813d | ||
![]() |
d398ed1345 | ||
![]() |
fb10de1446 | ||
![]() |
24dc0bbc88 | ||
![]() |
fa9777e529 | ||
![]() |
77213507fb | ||
![]() |
bfec85c352 | ||
![]() |
f3d3d40c75 | ||
![]() |
5bf38d804e | ||
![]() |
9dec9c5a18 | ||
![]() |
43b5d4e22f | ||
![]() |
fe19e0ef26 | ||
![]() |
c0af297f48 | ||
![]() |
c97e34aa04 | ||
![]() |
01ee045beb | ||
![]() |
cf6f83c8a2 | ||
![]() |
4deaf4fb76 |
6
.github/workflows/build.yml
vendored
@@ -29,7 +29,7 @@ on:
|
||||
|
||||
env:
|
||||
# See vars.GO_VERSION field of https://github.com/arduino/arduino-cli/blob/master/DistTasks.yml
|
||||
GO_VERSION: "1.17"
|
||||
GO_VERSION: "1.19"
|
||||
JOB_TRANSFER_ARTIFACT: build-artifacts
|
||||
CHANGELOG_ARTIFACTS: changelog
|
||||
|
||||
@@ -69,7 +69,7 @@ jobs:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
@@ -235,7 +235,7 @@ jobs:
|
||||
echo "TAG_NAME=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Publish Release [GitHub]
|
||||
uses: svenstaro/upload-release-action@2.4.1
|
||||
uses: svenstaro/upload-release-action@2.5.0
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
release_name: ${{ steps.tag_name.outputs.TAG_NAME }}
|
||||
|
4
.github/workflows/check-i18n-task.yml
vendored
@@ -2,7 +2,7 @@ name: Check Internationalization
|
||||
|
||||
env:
|
||||
# See vars.GO_VERSION field of https://github.com/arduino/arduino-cli/blob/master/DistTasks.yml
|
||||
GO_VERSION: "1.17"
|
||||
GO_VERSION: "1.19"
|
||||
|
||||
# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows
|
||||
on:
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
|
4
.github/workflows/i18n-nightly-push.yml
vendored
@@ -2,7 +2,7 @@ name: i18n-nightly-push
|
||||
|
||||
env:
|
||||
# See vars.GO_VERSION field of https://github.com/arduino/arduino-cli/blob/master/DistTasks.yml
|
||||
GO_VERSION: "1.17"
|
||||
GO_VERSION: "1.19"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
|
4
.github/workflows/i18n-weekly-pull.yml
vendored
@@ -2,7 +2,7 @@ name: i18n-weekly-pull
|
||||
|
||||
env:
|
||||
# See vars.GO_VERSION field of https://github.com/arduino/arduino-cli/blob/master/DistTasks.yml
|
||||
GO_VERSION: "1.17"
|
||||
GO_VERSION: "1.19"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
|
4
.github/workflows/themes-weekly-pull.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
|
||||
env:
|
||||
# See vars.GO_VERSION field of https://github.com/arduino/arduino-cli/blob/master/DistTasks.yml
|
||||
GO_VERSION: "1.17"
|
||||
GO_VERSION: "1.19"
|
||||
NODE_VERSION: 16.x
|
||||
|
||||
jobs:
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "arduino-ide-extension",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.5",
|
||||
"description": "An extension for Theia building the Arduino IDE",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
@@ -53,12 +53,10 @@
|
||||
"@types/js-yaml": "^3.12.2",
|
||||
"@types/keytar": "^4.4.0",
|
||||
"@types/lodash.debounce": "^4.0.6",
|
||||
"@types/ncp": "^2.0.4",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"@types/p-queue": "^2.3.1",
|
||||
"@types/ps-tree": "^1.1.0",
|
||||
"@types/react-tabs": "^2.3.2",
|
||||
"@types/react-virtualized": "^9.21.21",
|
||||
"@types/temp": "^0.8.34",
|
||||
"@types/which": "^1.3.1",
|
||||
"@vscode/debugprotocol": "^1.51.0",
|
||||
@@ -67,6 +65,7 @@
|
||||
"auth0-js": "^9.14.0",
|
||||
"btoa": "^1.2.1",
|
||||
"classnames": "^2.3.1",
|
||||
"cpy": "^8.1.2",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"dateformat": "^3.0.3",
|
||||
"deepmerge": "2.0.1",
|
||||
@@ -77,13 +76,14 @@
|
||||
"glob": "^7.1.6",
|
||||
"google-protobuf": "^3.20.1",
|
||||
"hash.js": "^1.1.7",
|
||||
"is-online": "^9.0.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
"jsonc-parser": "^2.2.0",
|
||||
"just-diff": "^5.1.1",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"keytar": "7.2.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"minimatch": "^3.1.2",
|
||||
"ncp": "^2.0.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"open": "^8.0.6",
|
||||
"p-debounce": "^2.1.0",
|
||||
@@ -95,7 +95,6 @@
|
||||
"react-perfect-scrollbar": "^1.5.8",
|
||||
"react-select": "^5.6.0",
|
||||
"react-tabs": "^3.1.2",
|
||||
"react-virtualized": "^9.22.3",
|
||||
"react-window": "^1.8.6",
|
||||
"semver": "^7.3.2",
|
||||
"string-natural-compare": "^2.0.3",
|
||||
@@ -121,6 +120,7 @@
|
||||
"mocha": "^7.0.0",
|
||||
"mockdate": "^3.0.5",
|
||||
"moment": "^2.24.0",
|
||||
"ncp": "^2.0.0",
|
||||
"protoc": "^1.0.4",
|
||||
"shelljs": "^0.8.3",
|
||||
"uuid": "^3.2.1",
|
||||
@@ -163,7 +163,11 @@
|
||||
],
|
||||
"arduino": {
|
||||
"cli": {
|
||||
"version": "0.29.0"
|
||||
"version": {
|
||||
"owner": "arduino",
|
||||
"repo": "arduino-cli",
|
||||
"commitish": "71a8576"
|
||||
}
|
||||
},
|
||||
"fwuploader": {
|
||||
"version": "2.2.2"
|
||||
|
@@ -10,10 +10,7 @@ import {
|
||||
MenuContribution,
|
||||
MenuModelRegistry,
|
||||
} from '@theia/core';
|
||||
import {
|
||||
FrontendApplication,
|
||||
FrontendApplicationContribution,
|
||||
} from '@theia/core/lib/browser';
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser';
|
||||
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
|
||||
import { ColorRegistry } from '@theia/core/lib/browser/color-registry';
|
||||
import { CommonMenus } from '@theia/core/lib/browser/common-frontend-contribution';
|
||||
@@ -77,7 +74,7 @@ export class ArduinoFrontendContribution
|
||||
}
|
||||
}
|
||||
|
||||
onStart(app: FrontendApplication): void {
|
||||
onStart(): void {
|
||||
this.electronWindowPreferences.onPreferenceChanged((event) => {
|
||||
if (event.newValue !== event.oldValue) {
|
||||
switch (event.preferenceName) {
|
||||
@@ -98,8 +95,6 @@ export class ArduinoFrontendContribution
|
||||
webContents.setZoomLevel(zoomLevel);
|
||||
})
|
||||
);
|
||||
// Removes the _Settings_ (cog) icon from the left sidebar
|
||||
app.shell.leftPanelHandler.removeBottomMenu('settings-menu');
|
||||
}
|
||||
|
||||
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
|
@@ -79,7 +79,10 @@ import { ProblemManager as TheiaProblemManager } from '@theia/markers/lib/browse
|
||||
import { ProblemManager } from './theia/markers/problem-manager';
|
||||
import { BoardsAutoInstaller } from './boards/boards-auto-installer';
|
||||
import { ShellLayoutRestorer } from './theia/core/shell-layout-restorer';
|
||||
import { ListItemRenderer } from './widgets/component-list/list-item-renderer';
|
||||
import {
|
||||
ArduinoComponentContextMenuRenderer,
|
||||
ListItemRenderer,
|
||||
} from './widgets/component-list/list-item-renderer';
|
||||
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
|
||||
|
||||
import {
|
||||
@@ -90,6 +93,8 @@ import { EditorCommandContribution as TheiaEditorCommandContribution } from '@th
|
||||
import {
|
||||
FrontendConnectionStatusService,
|
||||
ApplicationConnectionStatusContribution,
|
||||
DaemonPort,
|
||||
IsOnline,
|
||||
} from './theia/core/connection-status-service';
|
||||
import {
|
||||
FrontendConnectionStatusService as TheiaFrontendConnectionStatusService,
|
||||
@@ -347,6 +352,11 @@ import { ConfigServiceClient } from './config/config-service-client';
|
||||
import { ValidateSketch } from './contributions/validate-sketch';
|
||||
import { RenameCloudSketch } from './contributions/rename-cloud-sketch';
|
||||
import { CreateFeatures } from './create/create-features';
|
||||
import { Account } from './contributions/account';
|
||||
import { SidebarBottomMenuWidget } from './theia/core/sidebar-bottom-menu-widget';
|
||||
import { SidebarBottomMenuWidget as TheiaSidebarBottomMenuWidget } from '@theia/core/lib/browser/shell/sidebar-bottom-menu-widget';
|
||||
import { CreateCloudCopy } from './contributions/create-cloud-copy';
|
||||
import { NativeImageCache } from './native-image-cache';
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
// Commands and toolbar items
|
||||
@@ -734,6 +744,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
Contribution.configure(bind, NewCloudSketch);
|
||||
Contribution.configure(bind, ValidateSketch);
|
||||
Contribution.configure(bind, RenameCloudSketch);
|
||||
Contribution.configure(bind, Account);
|
||||
Contribution.configure(bind, CloudSketchbookContribution);
|
||||
Contribution.configure(bind, CreateCloudCopy);
|
||||
|
||||
bindContributionProvider(bind, StartupTaskProvider);
|
||||
bind(StartupTaskProvider).toService(BoardsServiceProvider); // to reuse the boards config in another window
|
||||
@@ -912,8 +925,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(CreateFsProvider).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(CreateFsProvider);
|
||||
bind(FileServiceContribution).toService(CreateFsProvider);
|
||||
bind(CloudSketchbookContribution).toSelf().inSingletonScope();
|
||||
bind(CommandContribution).toService(CloudSketchbookContribution);
|
||||
bind(LocalCacheFsProvider).toSelf().inSingletonScope();
|
||||
bind(FileServiceContribution).toService(LocalCacheFsProvider);
|
||||
bind(CloudSketchbookCompositeWidget).toSelf();
|
||||
@@ -1014,4 +1025,17 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
},
|
||||
}))
|
||||
.inSingletonScope();
|
||||
|
||||
bind(SidebarBottomMenuWidget).toSelf();
|
||||
rebind(TheiaSidebarBottomMenuWidget).toService(SidebarBottomMenuWidget);
|
||||
|
||||
bind(ArduinoComponentContextMenuRenderer).toSelf().inSingletonScope();
|
||||
|
||||
bind(DaemonPort).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(DaemonPort);
|
||||
bind(IsOnline).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(IsOnline);
|
||||
// manages native images for the electron menu icons
|
||||
bind(NativeImageCache).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(NativeImageCache);
|
||||
});
|
||||
|
@@ -83,9 +83,13 @@ export class AuthenticationClientService
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(CloudUserCommands.LOGIN, {
|
||||
execute: () => this.service.login(),
|
||||
isEnabled: () => !this._session,
|
||||
isVisible: () => !this._session,
|
||||
});
|
||||
registry.registerCommand(CloudUserCommands.LOGOUT, {
|
||||
execute: () => this.service.logout(),
|
||||
isEnabled: () => !!this._session,
|
||||
isVisible: () => !!this._session,
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,8 @@
|
||||
import { Command } from '@theia/core/lib/common/command';
|
||||
|
||||
export const LEARN_MORE_URL =
|
||||
'https://docs.arduino.cc/software/ide-v2/tutorials/ide-v2-cloud-sketch-sync';
|
||||
|
||||
export namespace CloudUserCommands {
|
||||
export const LOGIN = Command.toLocalizedCommand(
|
||||
{
|
||||
@@ -16,9 +19,4 @@ export namespace CloudUserCommands {
|
||||
},
|
||||
'arduino/cloud/signOut'
|
||||
);
|
||||
|
||||
export const OPEN_PROFILE_CONTEXT_MENU: Command = {
|
||||
id: 'arduino-cloud-sketchbook--open-profile-menu',
|
||||
label: 'Contextual menu',
|
||||
};
|
||||
}
|
||||
|
@@ -174,7 +174,7 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution {
|
||||
// CLI returns the packages already sorted with the deprecated ones at the end of the list
|
||||
// in order to ensure the new ones are preferred
|
||||
const candidates = packagesForBoard.filter(
|
||||
({ installable, installedVersion }) => installable && !installedVersion
|
||||
({ installedVersion }) => !installedVersion
|
||||
);
|
||||
|
||||
return candidates[0];
|
||||
|
@@ -158,15 +158,9 @@ export class BoardsServiceProvider
|
||||
this.lastAvailablePortsOnUpload = undefined;
|
||||
}
|
||||
|
||||
private portToAutoSelectCanBeDerived(): boolean {
|
||||
return Boolean(
|
||||
this.lastBoardsConfigOnUpload && this.lastAvailablePortsOnUpload
|
||||
);
|
||||
}
|
||||
|
||||
attemptPostUploadAutoSelect(): void {
|
||||
setTimeout(() => {
|
||||
if (this.portToAutoSelectCanBeDerived()) {
|
||||
if (this.lastBoardsConfigOnUpload && this.lastAvailablePortsOnUpload) {
|
||||
this.attemptAutoSelect({
|
||||
ports: this._availablePorts,
|
||||
boards: this._availableBoards,
|
||||
@@ -185,12 +179,12 @@ export class BoardsServiceProvider
|
||||
private deriveBoardConfigToAutoSelect(
|
||||
newState: AttachedBoardsChangeEvent['newState']
|
||||
): void {
|
||||
if (!this.portToAutoSelectCanBeDerived()) {
|
||||
if (!this.lastBoardsConfigOnUpload || !this.lastAvailablePortsOnUpload) {
|
||||
this.boardConfigToAutoSelect = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
const oldPorts = this.lastAvailablePortsOnUpload!;
|
||||
const oldPorts = this.lastAvailablePortsOnUpload;
|
||||
const { ports: newPorts, boards: newBoards } = newState;
|
||||
|
||||
const appearedPorts =
|
||||
@@ -205,20 +199,39 @@ export class BoardsServiceProvider
|
||||
Port.sameAs(board.port, port)
|
||||
);
|
||||
|
||||
const lastBoardsConfigOnUpload = this.lastBoardsConfigOnUpload!;
|
||||
const lastBoardsConfigOnUpload = this.lastBoardsConfigOnUpload;
|
||||
|
||||
if (
|
||||
boardOnAppearedPort &&
|
||||
lastBoardsConfigOnUpload.selectedBoard &&
|
||||
Board.sameAs(
|
||||
if (boardOnAppearedPort && lastBoardsConfigOnUpload.selectedBoard) {
|
||||
const boardIsSameHardware = Board.hardwareIdEquals(
|
||||
boardOnAppearedPort,
|
||||
lastBoardsConfigOnUpload.selectedBoard
|
||||
)
|
||||
) {
|
||||
);
|
||||
|
||||
const boardIsSameFqbn = Board.sameAs(
|
||||
boardOnAppearedPort,
|
||||
lastBoardsConfigOnUpload.selectedBoard
|
||||
);
|
||||
|
||||
if (!boardIsSameHardware && !boardIsSameFqbn) continue;
|
||||
|
||||
let boardToAutoSelect = boardOnAppearedPort;
|
||||
if (boardIsSameHardware && !boardIsSameFqbn) {
|
||||
const { name, fqbn } = lastBoardsConfigOnUpload.selectedBoard;
|
||||
|
||||
boardToAutoSelect = {
|
||||
...boardToAutoSelect,
|
||||
name:
|
||||
boardToAutoSelect.name === Unknown || !boardToAutoSelect.name
|
||||
? name
|
||||
: boardToAutoSelect.name,
|
||||
fqbn: boardToAutoSelect.fqbn || fqbn,
|
||||
};
|
||||
}
|
||||
|
||||
this.clearBoardDiscoverySnapshot();
|
||||
|
||||
this.boardConfigToAutoSelect = {
|
||||
selectedBoard: boardOnAppearedPort,
|
||||
selectedBoard: boardToAutoSelect,
|
||||
selectedPort: port,
|
||||
};
|
||||
return;
|
||||
@@ -326,7 +339,9 @@ export class BoardsServiceProvider
|
||||
// it is just a FQBN, so we need to find the `selected` board among the `AvailableBoards`
|
||||
const selectedAvailableBoard = AvailableBoard.is(selectedBoard)
|
||||
? selectedBoard
|
||||
: this._availableBoards.find((availableBoard) =>
|
||||
: this._availableBoards.find(
|
||||
(availableBoard) =>
|
||||
Board.hardwareIdEquals(availableBoard, selectedBoard) ||
|
||||
Board.sameAs(availableBoard, selectedBoard)
|
||||
);
|
||||
if (
|
||||
@@ -353,9 +368,28 @@ export class BoardsServiceProvider
|
||||
|
||||
protected tryReconnect(): boolean {
|
||||
if (this.latestValidBoardsConfig && !this.canUploadTo(this.boardsConfig)) {
|
||||
// ** Reconnect to a board unplugged from, and plugged back into the same port
|
||||
for (const board of this.availableBoards.filter(
|
||||
({ state }) => state !== AvailableBoard.State.incomplete
|
||||
)) {
|
||||
if (
|
||||
Board.hardwareIdEquals(
|
||||
this.latestValidBoardsConfig.selectedBoard,
|
||||
board
|
||||
)
|
||||
) {
|
||||
const { name, fqbn } = this.latestValidBoardsConfig.selectedBoard;
|
||||
this.boardsConfig = {
|
||||
selectedBoard: {
|
||||
name: board.name === Unknown || !board.name ? name : board.name,
|
||||
fqbn: board.fqbn || fqbn,
|
||||
port: board.port,
|
||||
},
|
||||
selectedPort: board.port,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
this.latestValidBoardsConfig.selectedBoard.fqbn === board.fqbn &&
|
||||
this.latestValidBoardsConfig.selectedBoard.name === board.name &&
|
||||
@@ -365,12 +399,15 @@ export class BoardsServiceProvider
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// **
|
||||
|
||||
// ** Reconnect to a board whose port changed due to an upload
|
||||
if (!this.boardConfigToAutoSelect) return false;
|
||||
|
||||
this.boardsConfig = this.boardConfigToAutoSelect;
|
||||
this.boardConfigToAutoSelect = undefined;
|
||||
return true;
|
||||
// **
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
155
arduino-ide-extension/src/browser/contributions/account.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||
import { SidebarMenu } from '@theia/core/lib/browser/shell/sidebar-menu-widget';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { MenuPath } from '@theia/core/lib/common/menu';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { CloudUserCommands, LEARN_MORE_URL } from '../auth/cloud-user-commands';
|
||||
import { CreateFeatures } from '../create/create-features';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { ApplicationConnectionStatusContribution } from '../theia/core/connection-status-service';
|
||||
import {
|
||||
Command,
|
||||
CommandRegistry,
|
||||
Contribution,
|
||||
MenuModelRegistry,
|
||||
} from './contribution';
|
||||
|
||||
export const accountMenu: SidebarMenu = {
|
||||
id: 'arduino-accounts-menu',
|
||||
iconClass: 'codicon codicon-account',
|
||||
title: nls.localize('arduino/account/menuTitle', 'Arduino Cloud'),
|
||||
menuPath: ArduinoMenus.ARDUINO_ACCOUNT__CONTEXT,
|
||||
order: 0,
|
||||
};
|
||||
|
||||
@injectable()
|
||||
export class Account extends Contribution {
|
||||
@inject(WindowService)
|
||||
private readonly windowService: WindowService;
|
||||
@inject(CreateFeatures)
|
||||
private readonly createFeatures: CreateFeatures;
|
||||
@inject(ApplicationConnectionStatusContribution)
|
||||
private readonly connectionStatus: ApplicationConnectionStatusContribution;
|
||||
|
||||
private readonly toDispose = new DisposableCollection();
|
||||
private app: FrontendApplication;
|
||||
|
||||
override onStart(app: FrontendApplication): void {
|
||||
this.app = app;
|
||||
this.updateSidebarCommand();
|
||||
this.toDispose.push(
|
||||
this.createFeatures.onDidChangeEnabled((enabled) =>
|
||||
this.updateSidebarCommand(enabled)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
const openExternal = (url: string) =>
|
||||
this.windowService.openNewWindow(url, { external: true });
|
||||
const loggedIn = () => Boolean(this.createFeatures.session);
|
||||
const loggedInWithInternetConnection = () =>
|
||||
loggedIn() && this.connectionStatus.offlineStatus !== 'internet';
|
||||
registry.registerCommand(Account.Commands.LEARN_MORE, {
|
||||
execute: () => openExternal(LEARN_MORE_URL),
|
||||
isEnabled: () => !loggedIn(),
|
||||
isVisible: () => !loggedIn(),
|
||||
});
|
||||
registry.registerCommand(Account.Commands.GO_TO_PROFILE, {
|
||||
execute: () => openExternal('https://id.arduino.cc/'),
|
||||
isEnabled: () => loggedInWithInternetConnection(),
|
||||
isVisible: () => loggedIn(),
|
||||
});
|
||||
registry.registerCommand(Account.Commands.GO_TO_CLOUD_EDITOR, {
|
||||
execute: () => openExternal('https://create.arduino.cc/editor'),
|
||||
isEnabled: () => loggedInWithInternetConnection(),
|
||||
isVisible: () => loggedIn(),
|
||||
});
|
||||
registry.registerCommand(Account.Commands.GO_TO_IOT_CLOUD, {
|
||||
execute: () => openExternal('https://create.arduino.cc/iot/'),
|
||||
isEnabled: () => loggedInWithInternetConnection(),
|
||||
isVisible: () => loggedIn(),
|
||||
});
|
||||
}
|
||||
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
const register = (
|
||||
menuPath: MenuPath,
|
||||
...commands: (Command | [command: Command, menuLabel: string])[]
|
||||
) =>
|
||||
commands.forEach((command, index) => {
|
||||
const commandId = Array.isArray(command) ? command[0].id : command.id;
|
||||
const label = Array.isArray(command) ? command[1] : command.label;
|
||||
registry.registerMenuAction(menuPath, {
|
||||
label,
|
||||
commandId,
|
||||
order: String(index),
|
||||
});
|
||||
});
|
||||
|
||||
register(ArduinoMenus.ARDUINO_ACCOUNT__CONTEXT__SIGN_IN_GROUP, [
|
||||
CloudUserCommands.LOGIN,
|
||||
nls.localize('arduino/cloud/signInToCloud', 'Sign in to Arduino Cloud'),
|
||||
]);
|
||||
register(ArduinoMenus.ARDUINO_ACCOUNT__CONTEXT__LEARN_MORE_GROUP, [
|
||||
Account.Commands.LEARN_MORE,
|
||||
nls.localize('arduino/cloud/learnMore', 'Learn more'),
|
||||
]);
|
||||
register(
|
||||
ArduinoMenus.ARDUINO_ACCOUNT__CONTEXT__GO_TO_GROUP,
|
||||
[
|
||||
Account.Commands.GO_TO_PROFILE,
|
||||
nls.localize('arduino/account/goToProfile', 'Go to Profile'),
|
||||
],
|
||||
[
|
||||
Account.Commands.GO_TO_CLOUD_EDITOR,
|
||||
nls.localize('arduino/account/goToCloudEditor', 'Go to Cloud Editor'),
|
||||
],
|
||||
[
|
||||
Account.Commands.GO_TO_IOT_CLOUD,
|
||||
nls.localize('arduino/account/goToIoTCloud', 'Go to IoT Cloud'),
|
||||
]
|
||||
);
|
||||
register(
|
||||
ArduinoMenus.ARDUINO_ACCOUNT__CONTEXT__SIGN_OUT_GROUP,
|
||||
CloudUserCommands.LOGOUT
|
||||
);
|
||||
}
|
||||
|
||||
private updateSidebarCommand(
|
||||
visible: boolean = this.preferences['arduino.cloud.enabled']
|
||||
): void {
|
||||
if (!this.app) {
|
||||
return;
|
||||
}
|
||||
const handler = this.app.shell.leftPanelHandler;
|
||||
if (visible) {
|
||||
handler.addBottomMenu(accountMenu);
|
||||
} else {
|
||||
handler.removeBottomMenu(accountMenu.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Account {
|
||||
export namespace Commands {
|
||||
export const GO_TO_PROFILE: Command = {
|
||||
id: 'arduino-go-to-profile',
|
||||
};
|
||||
export const GO_TO_CLOUD_EDITOR: Command = {
|
||||
id: 'arduino-go-to-cloud-editor',
|
||||
};
|
||||
export const GO_TO_IOT_CLOUD: Command = {
|
||||
id: 'arduino-go-to-iot-cloud',
|
||||
};
|
||||
export const LEARN_MORE: Command = {
|
||||
id: 'arduino-learn-more',
|
||||
};
|
||||
}
|
||||
}
|
@@ -93,7 +93,7 @@ export abstract class CloudSketchContribution extends SketchContribution {
|
||||
);
|
||||
}
|
||||
try {
|
||||
await treeModel.sketchbookTree().pull({ node });
|
||||
await treeModel.sketchbookTree().pull({ node }, true);
|
||||
return node;
|
||||
} catch (err) {
|
||||
if (isNotFound(err)) {
|
||||
|
@@ -14,7 +14,6 @@ import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { open, OpenerService } from '@theia/core/lib/browser/opener-service';
|
||||
|
||||
import {
|
||||
MenuModelRegistry,
|
||||
MenuContribution,
|
||||
@@ -58,7 +57,7 @@ import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
|
||||
import { ExecuteWithProgress } from '../../common/protocol/progressible';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import { BoardsDataStore } from '../boards/boards-data-store';
|
||||
import { NotificationManager } from '../theia/messages/notifications-manager';
|
||||
import { NotificationManager } from '@theia/messages/lib/browser/notifications-manager';
|
||||
import { MessageType } from '@theia/core/lib/common/message-service-protocol';
|
||||
import { WorkspaceService } from '../theia/workspace/workspace-service';
|
||||
import { MainMenuManager } from '../../common/main-menu-manager';
|
||||
@@ -295,7 +294,7 @@ export abstract class CoreServiceContribution extends SketchContribution {
|
||||
}
|
||||
|
||||
private notificationId(message: string, ...actions: string[]): string {
|
||||
return this.notificationManager.getMessageId({
|
||||
return this.notificationManager['getMessageId']({
|
||||
text: message,
|
||||
actions,
|
||||
type: MessageType.Error,
|
||||
|
@@ -0,0 +1,118 @@
|
||||
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||
import { ApplicationShell } from '@theia/core/lib/browser/shell';
|
||||
import type { Command, CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import { Progress } from '@theia/core/lib/common/message-service-protocol';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { Create } from '../create/typings';
|
||||
import { ApplicationConnectionStatusContribution } from '../theia/core/connection-status-service';
|
||||
import { CloudSketchbookTree } from '../widgets/cloud-sketchbook/cloud-sketchbook-tree';
|
||||
import { SketchbookTree } from '../widgets/sketchbook/sketchbook-tree';
|
||||
import { SketchbookTreeModel } from '../widgets/sketchbook/sketchbook-tree-model';
|
||||
import { CloudSketchContribution, pushingSketch } from './cloud-contribution';
|
||||
import {
|
||||
CreateNewCloudSketchCallback,
|
||||
NewCloudSketch,
|
||||
NewCloudSketchParams,
|
||||
} from './new-cloud-sketch';
|
||||
import { saveOntoCopiedSketch } from './save-as-sketch';
|
||||
|
||||
interface CreateCloudCopyParams {
|
||||
readonly model: SketchbookTreeModel;
|
||||
readonly node: SketchbookTree.SketchDirNode;
|
||||
}
|
||||
function isCreateCloudCopyParams(arg: unknown): arg is CreateCloudCopyParams {
|
||||
return (
|
||||
typeof arg === 'object' &&
|
||||
(<CreateCloudCopyParams>arg).model !== undefined &&
|
||||
(<CreateCloudCopyParams>arg).model instanceof SketchbookTreeModel &&
|
||||
(<CreateCloudCopyParams>arg).node !== undefined &&
|
||||
SketchbookTree.SketchDirNode.is((<CreateCloudCopyParams>arg).node)
|
||||
);
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class CreateCloudCopy extends CloudSketchContribution {
|
||||
@inject(ApplicationConnectionStatusContribution)
|
||||
private readonly connectionStatus: ApplicationConnectionStatusContribution;
|
||||
|
||||
private shell: ApplicationShell;
|
||||
|
||||
override onStart(app: FrontendApplication): void {
|
||||
this.shell = app.shell;
|
||||
}
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(CreateCloudCopy.Commands.CREATE_CLOUD_COPY, {
|
||||
execute: (args: CreateCloudCopyParams) => this.createCloudCopy(args),
|
||||
isEnabled: (args: unknown) =>
|
||||
Boolean(this.createFeatures.session) && isCreateCloudCopyParams(args),
|
||||
isVisible: (args: unknown) =>
|
||||
Boolean(this.createFeatures.enabled) &&
|
||||
Boolean(this.createFeatures.session) &&
|
||||
this.connectionStatus.offlineStatus !== 'internet' &&
|
||||
isCreateCloudCopyParams(args),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* - creates new cloud sketch with the name of the params sketch,
|
||||
* - pulls the cloud sketch,
|
||||
* - copies files from params sketch to pulled cloud sketch in the cache folder,
|
||||
* - pushes the cloud sketch, and
|
||||
* - opens in new window.
|
||||
*/
|
||||
private async createCloudCopy(params: CreateCloudCopyParams): Promise<void> {
|
||||
const sketch = await this.sketchesService.loadSketch(
|
||||
params.node.fileStat.resource.toString()
|
||||
);
|
||||
const callback: CreateNewCloudSketchCallback = async (
|
||||
newSketch: Create.Sketch,
|
||||
newNode: CloudSketchbookTree.CloudSketchDirNode,
|
||||
progress: Progress
|
||||
) => {
|
||||
const treeModel = await this.treeModel();
|
||||
if (!treeModel) {
|
||||
throw new Error('Could not retrieve the cloud sketchbook tree model.');
|
||||
}
|
||||
|
||||
progress.report({
|
||||
message: nls.localize(
|
||||
'arduino/createCloudCopy/copyingSketchFilesMessage',
|
||||
'Copying local sketch files...'
|
||||
),
|
||||
});
|
||||
const localCacheFolderUri = newNode.uri.toString();
|
||||
await this.sketchesService.copy(sketch, {
|
||||
destinationUri: localCacheFolderUri,
|
||||
onlySketchFiles: true,
|
||||
});
|
||||
await saveOntoCopiedSketch(
|
||||
sketch,
|
||||
localCacheFolderUri,
|
||||
this.shell,
|
||||
this.editorManager
|
||||
);
|
||||
|
||||
progress.report({ message: pushingSketch(newSketch.name) });
|
||||
await treeModel.sketchbookTree().push(newNode, true, true);
|
||||
};
|
||||
return this.commandService.executeCommand(
|
||||
NewCloudSketch.Commands.NEW_CLOUD_SKETCH.id,
|
||||
<NewCloudSketchParams>{
|
||||
initialValue: params.node.fileStat.name,
|
||||
callback,
|
||||
skipShowErrorMessageOnOpen: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace CreateCloudCopy {
|
||||
export namespace Commands {
|
||||
export const CREATE_CLOUD_COPY: Command = {
|
||||
id: 'arduino-create-cloud-copy',
|
||||
iconClass: 'fa fa-arduino-cloud-upload',
|
||||
};
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
import * as PQueue from 'p-queue';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { CommandHandler } from '@theia/core/lib/common/command';
|
||||
import { CommandHandler, CommandService } from '@theia/core/lib/common/command';
|
||||
import {
|
||||
MenuPath,
|
||||
CompositeMenuNode,
|
||||
@@ -11,7 +11,11 @@ import {
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import { OpenSketch } from './open-sketch';
|
||||
import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus';
|
||||
import {
|
||||
ArduinoMenus,
|
||||
examplesLabel,
|
||||
PlaceholderMenuNode,
|
||||
} from '../menu/arduino-menus';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import { ExamplesService } from '../../common/protocol/examples-service';
|
||||
import {
|
||||
@@ -25,11 +29,73 @@ import {
|
||||
SketchRef,
|
||||
SketchContainer,
|
||||
SketchesError,
|
||||
Sketch,
|
||||
CoreService,
|
||||
SketchesService,
|
||||
Sketch,
|
||||
} from '../../common/protocol';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { unregisterSubmenu } from '../menu/arduino-menus';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { ApplicationError } from '@theia/core/lib/common/application-error';
|
||||
|
||||
/**
|
||||
* Creates a cloned copy of the example sketch and opens it in a new window.
|
||||
*/
|
||||
export async function openClonedExample(
|
||||
uri: string,
|
||||
services: {
|
||||
sketchesService: SketchesService;
|
||||
commandService: CommandService;
|
||||
},
|
||||
onError: {
|
||||
onDidFailClone?: (
|
||||
err: ApplicationError<
|
||||
number,
|
||||
{
|
||||
uri: string;
|
||||
}
|
||||
>,
|
||||
uri: string
|
||||
) => MaybePromise<unknown>;
|
||||
onDidFailOpen?: (
|
||||
err: ApplicationError<
|
||||
number,
|
||||
{
|
||||
uri: string;
|
||||
}
|
||||
>,
|
||||
sketch: Sketch
|
||||
) => MaybePromise<unknown>;
|
||||
} = {}
|
||||
): Promise<void> {
|
||||
const { sketchesService, commandService } = services;
|
||||
const { onDidFailClone, onDidFailOpen } = onError;
|
||||
try {
|
||||
const sketch = await sketchesService.cloneExample(uri);
|
||||
try {
|
||||
await commandService.executeCommand(
|
||||
OpenSketch.Commands.OPEN_SKETCH.id,
|
||||
sketch
|
||||
);
|
||||
} catch (openError) {
|
||||
if (SketchesError.NotFound.is(openError)) {
|
||||
if (onDidFailOpen) {
|
||||
await onDidFailOpen(openError, sketch);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw openError;
|
||||
}
|
||||
} catch (cloneError) {
|
||||
if (SketchesError.NotFound.is(cloneError)) {
|
||||
if (onDidFailClone) {
|
||||
await onDidFailClone(cloneError, uri);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw cloneError;
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export abstract class Examples extends SketchContribution {
|
||||
@@ -94,7 +160,7 @@ export abstract class Examples extends SketchContribution {
|
||||
// TODO: unregister submenu? https://github.com/eclipse-theia/theia/issues/7300
|
||||
registry.registerSubmenu(
|
||||
ArduinoMenus.FILE__EXAMPLES_SUBMENU,
|
||||
nls.localize('arduino/examples/menu', 'Examples'),
|
||||
examplesLabel,
|
||||
{
|
||||
order: '4',
|
||||
}
|
||||
@@ -174,47 +240,33 @@ export abstract class Examples extends SketchContribution {
|
||||
}
|
||||
|
||||
protected createHandler(uri: string): CommandHandler {
|
||||
const forceUpdate = () =>
|
||||
this.update({
|
||||
board: this.boardsServiceClient.boardsConfig.selectedBoard,
|
||||
forceRefresh: true,
|
||||
});
|
||||
return {
|
||||
execute: async () => {
|
||||
const sketch = await this.clone(uri);
|
||||
if (sketch) {
|
||||
try {
|
||||
return this.commandService.executeCommand(
|
||||
OpenSketch.Commands.OPEN_SKETCH.id,
|
||||
sketch
|
||||
);
|
||||
} catch (err) {
|
||||
if (SketchesError.NotFound.is(err)) {
|
||||
await openClonedExample(
|
||||
uri,
|
||||
{
|
||||
sketchesService: this.sketchesService,
|
||||
commandService: this.commandRegistry,
|
||||
},
|
||||
{
|
||||
onDidFailClone: () => {
|
||||
// Do not toast the error message. It's handled by the `Open Sketch` command.
|
||||
this.update({
|
||||
board: this.boardsServiceClient.boardsConfig.selectedBoard,
|
||||
forceRefresh: true,
|
||||
});
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
forceUpdate();
|
||||
},
|
||||
onDidFailOpen: (err) => {
|
||||
this.messageService.error(err.message);
|
||||
forceUpdate();
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private async clone(uri: string): Promise<Sketch | undefined> {
|
||||
try {
|
||||
const sketch = await this.sketchesService.cloneExample(uri);
|
||||
return sketch;
|
||||
} catch (err) {
|
||||
if (SketchesError.NotFound.is(err)) {
|
||||
this.messageService.error(err.message);
|
||||
this.update({
|
||||
board: this.boardsServiceClient.boardsConfig.selectedBoard,
|
||||
forceRefresh: true,
|
||||
});
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
|
@@ -6,7 +6,7 @@ import { Progress } from '@theia/core/lib/common/message-service-protocol';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { CreateUri } from '../create/create-uri';
|
||||
import { isConflict } from '../create/typings';
|
||||
import { Create, isConflict } from '../create/typings';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import {
|
||||
TaskFactoryImpl,
|
||||
@@ -15,13 +15,36 @@ import {
|
||||
import { CloudSketchbookTree } from '../widgets/cloud-sketchbook/cloud-sketchbook-tree';
|
||||
import { CloudSketchbookTreeModel } from '../widgets/cloud-sketchbook/cloud-sketchbook-tree-model';
|
||||
import { SketchbookCommands } from '../widgets/sketchbook/sketchbook-commands';
|
||||
import { Command, CommandRegistry, Sketch } from './contribution';
|
||||
import {
|
||||
CloudSketchContribution,
|
||||
pullingSketch,
|
||||
sketchAlreadyExists,
|
||||
synchronizingSketchbook,
|
||||
} from './cloud-contribution';
|
||||
import { Command, CommandRegistry, Sketch } from './contribution';
|
||||
|
||||
export interface CreateNewCloudSketchCallback {
|
||||
(
|
||||
newSketch: Create.Sketch,
|
||||
newNode: CloudSketchbookTree.CloudSketchDirNode,
|
||||
progress: Progress
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
export interface NewCloudSketchParams {
|
||||
/**
|
||||
* Value to populate the dialog `<input>` when it opens.
|
||||
*/
|
||||
readonly initialValue?: string | undefined;
|
||||
/**
|
||||
* Additional callback to call when the new cloud sketch has been created.
|
||||
*/
|
||||
readonly callback?: CreateNewCloudSketchCallback;
|
||||
/**
|
||||
* If `true`, the validation error message will not be visible in the input dialog, but the `OK` button will be disabled. Defaults to `true`.
|
||||
*/
|
||||
readonly skipShowErrorMessageOnOpen?: boolean;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class NewCloudSketch extends CloudSketchContribution {
|
||||
@@ -43,7 +66,12 @@ export class NewCloudSketch extends CloudSketchContribution {
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(NewCloudSketch.Commands.NEW_CLOUD_SKETCH, {
|
||||
execute: () => this.createNewSketch(true),
|
||||
execute: (params: NewCloudSketchParams) =>
|
||||
this.createNewSketch(
|
||||
params?.skipShowErrorMessageOnOpen === false ? false : true,
|
||||
params?.initialValue,
|
||||
params?.callback
|
||||
),
|
||||
isEnabled: () => Boolean(this.createFeatures.session),
|
||||
isVisible: () => this.createFeatures.enabled,
|
||||
});
|
||||
@@ -66,7 +94,8 @@ export class NewCloudSketch extends CloudSketchContribution {
|
||||
|
||||
private async createNewSketch(
|
||||
skipShowErrorMessageOnOpen: boolean,
|
||||
initialValue?: string | undefined
|
||||
initialValue?: string | undefined,
|
||||
callback?: CreateNewCloudSketchCallback
|
||||
): Promise<void> {
|
||||
const treeModel = await this.treeModel();
|
||||
if (treeModel) {
|
||||
@@ -75,7 +104,8 @@ export class NewCloudSketch extends CloudSketchContribution {
|
||||
rootNode,
|
||||
treeModel,
|
||||
skipShowErrorMessageOnOpen,
|
||||
initialValue
|
||||
initialValue,
|
||||
callback
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -84,13 +114,14 @@ export class NewCloudSketch extends CloudSketchContribution {
|
||||
rootNode: CompositeTreeNode,
|
||||
treeModel: CloudSketchbookTreeModel,
|
||||
skipShowErrorMessageOnOpen: boolean,
|
||||
initialValue?: string | undefined
|
||||
initialValue?: string | undefined,
|
||||
callback?: CreateNewCloudSketchCallback
|
||||
): Promise<void> {
|
||||
const existingNames = rootNode.children
|
||||
.filter(CloudSketchbookTree.CloudSketchDirNode.is)
|
||||
.map(({ fileStat }) => fileStat.name);
|
||||
const taskFactory = new TaskFactoryImpl((value) =>
|
||||
this.createNewSketchWithProgress(treeModel, value)
|
||||
this.createNewSketchWithProgress(treeModel, value, callback)
|
||||
);
|
||||
try {
|
||||
const dialog = new WorkspaceInputDialogWithProgress(
|
||||
@@ -118,7 +149,11 @@ export class NewCloudSketch extends CloudSketchContribution {
|
||||
} catch (err) {
|
||||
if (isConflict(err)) {
|
||||
await treeModel.refresh();
|
||||
return this.createNewSketch(false, taskFactory.value ?? initialValue);
|
||||
return this.createNewSketch(
|
||||
false,
|
||||
taskFactory.value ?? initialValue,
|
||||
callback
|
||||
);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
@@ -126,7 +161,8 @@ export class NewCloudSketch extends CloudSketchContribution {
|
||||
|
||||
private createNewSketchWithProgress(
|
||||
treeModel: CloudSketchbookTreeModel,
|
||||
value: string
|
||||
value: string,
|
||||
callback?: CreateNewCloudSketchCallback
|
||||
): (
|
||||
progress: Progress
|
||||
) => Promise<CloudSketchbookTree.CloudSketchDirNode | undefined> {
|
||||
@@ -143,6 +179,9 @@ export class NewCloudSketch extends CloudSketchContribution {
|
||||
await treeModel.refresh();
|
||||
progress.report({ message: pullingSketch(sketch.name) });
|
||||
const node = await this.pull(sketch);
|
||||
if (callback && node) {
|
||||
await callback(sketch, node, progress);
|
||||
}
|
||||
return node;
|
||||
};
|
||||
}
|
||||
@@ -152,7 +191,7 @@ export class NewCloudSketch extends CloudSketchContribution {
|
||||
): Promise<void> {
|
||||
return this.commandService.executeCommand(
|
||||
SketchbookCommands.OPEN_NEW_WINDOW.id,
|
||||
{ node }
|
||||
{ node, treeWidgetId: 'cloud-sketchbook-composite-widget' }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,55 +1,65 @@
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { WorkspaceServer } from '@theia/workspace/lib/common/workspace-protocol';
|
||||
import { NativeImage } from '@theia/core/electron-shared/electron';
|
||||
import { ThemeService } from '@theia/core/lib/browser/theming';
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import {
|
||||
SketchContribution,
|
||||
CommandRegistry,
|
||||
MenuModelRegistry,
|
||||
Sketch,
|
||||
} from './contribution';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { MainMenuManager } from '../../common/main-menu-manager';
|
||||
import { OpenSketch } from './open-sketch';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { MenuAction } from '@theia/core/lib/common/menu';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { SketchesError } from '../../common/protocol';
|
||||
import { ConfigServiceClient } from '../config/config-service-client';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import {
|
||||
isThemeNativeImage,
|
||||
NativeImageCache,
|
||||
ThemeNativeImage,
|
||||
} from '../native-image-cache';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { CloudSketchContribution } from './cloud-contribution';
|
||||
import { CommandRegistry, MenuModelRegistry, Sketch } from './contribution';
|
||||
import { OpenSketch } from './open-sketch';
|
||||
|
||||
@injectable()
|
||||
export class OpenRecentSketch extends SketchContribution {
|
||||
export class OpenRecentSketch extends CloudSketchContribution {
|
||||
@inject(CommandRegistry)
|
||||
protected readonly commandRegistry: CommandRegistry;
|
||||
|
||||
private readonly commandRegistry: CommandRegistry;
|
||||
@inject(MenuModelRegistry)
|
||||
protected readonly menuRegistry: MenuModelRegistry;
|
||||
|
||||
@inject(MainMenuManager)
|
||||
protected readonly mainMenuManager: MainMenuManager;
|
||||
|
||||
@inject(WorkspaceServer)
|
||||
protected readonly workspaceServer: WorkspaceServer;
|
||||
|
||||
private readonly menuRegistry: MenuModelRegistry;
|
||||
@inject(NotificationCenter)
|
||||
protected readonly notificationCenter: NotificationCenter;
|
||||
private readonly notificationCenter: NotificationCenter;
|
||||
@inject(NativeImageCache)
|
||||
private readonly imageCache: NativeImageCache;
|
||||
@inject(ConfigServiceClient)
|
||||
private readonly configServiceClient: ConfigServiceClient;
|
||||
@inject(ThemeService)
|
||||
private readonly themeService: ThemeService;
|
||||
|
||||
protected toDispose = new DisposableCollection();
|
||||
private readonly toDisposeBeforeRegister = new DisposableCollection();
|
||||
private readonly toDispose = new DisposableCollection(
|
||||
this.toDisposeBeforeRegister
|
||||
);
|
||||
private cloudImage: NativeImage | ThemeNativeImage;
|
||||
|
||||
override onStart(): void {
|
||||
this.toDispose.pushAll([
|
||||
this.notificationCenter.onRecentSketchesDidChange(({ sketches }) =>
|
||||
this.refreshMenu(sketches)
|
||||
);
|
||||
),
|
||||
this.themeService.onDidColorThemeChange(() => this.update()),
|
||||
]);
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
override async onReady(): Promise<void> {
|
||||
this.update();
|
||||
}
|
||||
|
||||
private update(forceUpdate?: boolean): void {
|
||||
this.sketchesService
|
||||
.recentlyOpenedSketches(forceUpdate)
|
||||
.then((sketches) => this.refreshMenu(sketches));
|
||||
this.imageCache.getImage('cloud').then((image) => {
|
||||
this.cloudImage = image;
|
||||
this.update();
|
||||
});
|
||||
}
|
||||
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
@@ -60,14 +70,20 @@ export class OpenRecentSketch extends SketchContribution {
|
||||
);
|
||||
}
|
||||
|
||||
private refreshMenu(sketches: Sketch[]): void {
|
||||
this.register(sketches);
|
||||
this.mainMenuManager.update();
|
||||
private update(forceUpdate?: boolean): void {
|
||||
this.sketchesService
|
||||
.recentlyOpenedSketches(forceUpdate)
|
||||
.then((sketches) => this.refreshMenu(sketches));
|
||||
}
|
||||
|
||||
protected register(sketches: Sketch[]): void {
|
||||
private refreshMenu(sketches: Sketch[]): void {
|
||||
this.register(sketches);
|
||||
this.menuManager.update();
|
||||
}
|
||||
|
||||
private register(sketches: Sketch[]): void {
|
||||
const order = 0;
|
||||
this.toDispose.dispose();
|
||||
this.toDisposeBeforeRegister.dispose();
|
||||
for (const sketch of sketches) {
|
||||
const { uri } = sketch;
|
||||
const command = { id: `arduino-open-recent--${uri}` };
|
||||
@@ -88,15 +104,16 @@ export class OpenRecentSketch extends SketchContribution {
|
||||
},
|
||||
};
|
||||
this.commandRegistry.registerCommand(command, handler);
|
||||
this.menuRegistry.registerMenuAction(
|
||||
ArduinoMenus.FILE__OPEN_RECENT_SUBMENU,
|
||||
{
|
||||
const menuAction = this.assignImage(sketch, {
|
||||
commandId: command.id,
|
||||
label: sketch.name,
|
||||
order: String(order),
|
||||
}
|
||||
});
|
||||
this.menuRegistry.registerMenuAction(
|
||||
ArduinoMenus.FILE__OPEN_RECENT_SUBMENU,
|
||||
menuAction
|
||||
);
|
||||
this.toDispose.pushAll([
|
||||
this.toDisposeBeforeRegister.pushAll([
|
||||
new DisposableCollection(
|
||||
Disposable.create(() =>
|
||||
this.commandRegistry.unregisterCommand(command)
|
||||
@@ -108,4 +125,25 @@ export class OpenRecentSketch extends SketchContribution {
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private assignImage(sketch: Sketch, menuAction: MenuAction): MenuAction {
|
||||
const image = this.nativeImageForTheme();
|
||||
if (image) {
|
||||
const dataDirUri = this.configServiceClient.tryGetDataDirUri();
|
||||
const isCloud = this.createFeatures.isCloud(sketch, dataDirUri);
|
||||
if (isCloud) {
|
||||
Object.assign(menuAction, { nativeImage: image });
|
||||
}
|
||||
}
|
||||
return menuAction;
|
||||
}
|
||||
|
||||
private nativeImageForTheme(): NativeImage | undefined {
|
||||
const image = this.cloudImage;
|
||||
if (isThemeNativeImage(image)) {
|
||||
const themeType = this.themeService.getCurrentTheme().type;
|
||||
return themeType === 'light' ? image.light : image.dark;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
@@ -123,7 +123,7 @@ export class RenameCloudSketch extends CloudSketchContribution {
|
||||
const toPosixPath = params.cloudUri.parent.resolve(value).path.toString();
|
||||
// push
|
||||
progress.report({ message: pushingSketch(params.sketch.name) });
|
||||
await treeModel.sketchbookTree().push(node);
|
||||
await treeModel.sketchbookTree().push(node, true);
|
||||
|
||||
// rename
|
||||
progress.report({
|
||||
|
@@ -6,6 +6,7 @@ import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shel
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { WorkspaceInput } from '@theia/workspace/lib/browser/workspace-service';
|
||||
import { StartupTask } from '../../electron-common/startup-task';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
@@ -28,7 +29,7 @@ import {
|
||||
@injectable()
|
||||
export class SaveAsSketch extends CloudSketchContribution {
|
||||
@inject(ApplicationShell)
|
||||
private readonly applicationShell: ApplicationShell;
|
||||
private readonly shell: ApplicationShell;
|
||||
@inject(WindowService)
|
||||
private readonly windowService: WindowService;
|
||||
|
||||
@@ -80,14 +81,17 @@ export class SaveAsSketch extends CloudSketchContribution {
|
||||
return false;
|
||||
}
|
||||
|
||||
const newWorkspaceUri = await this.sketchesService.copy(sketch, {
|
||||
const copiedSketch = await this.sketchesService.copy(sketch, {
|
||||
destinationUri,
|
||||
});
|
||||
if (!newWorkspaceUri) {
|
||||
return false;
|
||||
}
|
||||
const newWorkspaceUri = copiedSketch.uri;
|
||||
|
||||
await this.saveOntoCopiedSketch(sketch, newWorkspaceUri);
|
||||
await saveOntoCopiedSketch(
|
||||
sketch,
|
||||
newWorkspaceUri,
|
||||
this.shell,
|
||||
this.editorManager
|
||||
);
|
||||
if (markAsRecentlyOpened) {
|
||||
this.sketchesService.markAsRecentlyOpened(newWorkspaceUri);
|
||||
}
|
||||
@@ -238,53 +242,6 @@ ${dialogContent.question}`.trim();
|
||||
}
|
||||
return sketchFolderDestinationUri;
|
||||
}
|
||||
|
||||
private async saveOntoCopiedSketch(
|
||||
sketch: Sketch,
|
||||
newSketchFolderUri: string
|
||||
): Promise<void> {
|
||||
const widgets = this.applicationShell.widgets;
|
||||
const snapshots = new Map<string, Saveable.Snapshot>();
|
||||
for (const widget of widgets) {
|
||||
const saveable = Saveable.getDirty(widget);
|
||||
const uri = NavigatableWidget.getUri(widget);
|
||||
if (!uri) {
|
||||
continue;
|
||||
}
|
||||
const uriString = uri.toString();
|
||||
let relativePath: string;
|
||||
if (
|
||||
uriString.includes(sketch.uri) &&
|
||||
saveable &&
|
||||
saveable.createSnapshot
|
||||
) {
|
||||
// The main file will change its name during the copy process
|
||||
// We need to store the new name in the map
|
||||
if (sketch.mainFileUri === uriString) {
|
||||
const lastPart = new URI(newSketchFolderUri).path.base + uri.path.ext;
|
||||
relativePath = '/' + lastPart;
|
||||
} else {
|
||||
relativePath = uri.toString().substring(sketch.uri.length);
|
||||
}
|
||||
snapshots.set(relativePath, saveable.createSnapshot());
|
||||
}
|
||||
}
|
||||
await Promise.all(
|
||||
Array.from(snapshots.entries()).map(async ([path, snapshot]) => {
|
||||
const widgetUri = new URI(newSketchFolderUri + path);
|
||||
try {
|
||||
const widget = await this.editorManager.getOrCreateByUri(widgetUri);
|
||||
const saveable = Saveable.get(widget);
|
||||
if (saveable && saveable.applySnapshot) {
|
||||
saveable.applySnapshot(snapshot);
|
||||
await saveable.save();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface InvalidSketchFolderDialogContent {
|
||||
@@ -317,3 +274,48 @@ export namespace SaveAsSketch {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveOntoCopiedSketch(
|
||||
sketch: Sketch,
|
||||
newSketchFolderUri: string,
|
||||
shell: ApplicationShell,
|
||||
editorManager: EditorManager
|
||||
): Promise<void> {
|
||||
const widgets = shell.widgets;
|
||||
const snapshots = new Map<string, Saveable.Snapshot>();
|
||||
for (const widget of widgets) {
|
||||
const saveable = Saveable.getDirty(widget);
|
||||
const uri = NavigatableWidget.getUri(widget);
|
||||
if (!uri) {
|
||||
continue;
|
||||
}
|
||||
const uriString = uri.toString();
|
||||
let relativePath: string;
|
||||
if (uriString.includes(sketch.uri) && saveable && saveable.createSnapshot) {
|
||||
// The main file will change its name during the copy process
|
||||
// We need to store the new name in the map
|
||||
if (sketch.mainFileUri === uriString) {
|
||||
const lastPart = new URI(newSketchFolderUri).path.base + uri.path.ext;
|
||||
relativePath = '/' + lastPart;
|
||||
} else {
|
||||
relativePath = uri.toString().substring(sketch.uri.length);
|
||||
}
|
||||
snapshots.set(relativePath, saveable.createSnapshot());
|
||||
}
|
||||
}
|
||||
await Promise.all(
|
||||
Array.from(snapshots.entries()).map(async ([path, snapshot]) => {
|
||||
const widgetUri = new URI(newSketchFolderUri + path);
|
||||
try {
|
||||
const widget = await editorManager.getOrCreateByUri(widgetUri);
|
||||
const saveable = Saveable.get(widget);
|
||||
if (saveable && saveable.applySnapshot) {
|
||||
saveable.applySnapshot(snapshot);
|
||||
await saveable.save();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { fetch } from 'cross-fetch';
|
||||
import { SketchesService } from '../../common/protocol';
|
||||
import { uint8ArrayToString } from '../../common/utils';
|
||||
import { ArduinoPreferences } from '../arduino-preferences';
|
||||
import { AuthenticationClientService } from '../auth/authentication-client-service';
|
||||
import { SketchCache } from '../widgets/cloud-sketchbook/cloud-sketch-cache';
|
||||
@@ -9,59 +10,16 @@ import * as createPaths from './create-paths';
|
||||
import { posix } from './create-paths';
|
||||
import { Create, CreateError } from './typings';
|
||||
|
||||
export interface ResponseResultProvider {
|
||||
interface ResponseResultProvider {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(response: Response): Promise<any>;
|
||||
}
|
||||
export namespace ResponseResultProvider {
|
||||
namespace ResponseResultProvider {
|
||||
export const NOOP: ResponseResultProvider = async () => undefined;
|
||||
export const TEXT: ResponseResultProvider = (response) => response.text();
|
||||
export const JSON: ResponseResultProvider = (response) => response.json();
|
||||
}
|
||||
|
||||
// TODO: check if this is still needed: https://github.com/electron/electron/issues/18733
|
||||
// The original issue was reported for Electron 5.x and 6.x. Theia uses 15.x
|
||||
export function Utf8ArrayToStr(array: Uint8Array): string {
|
||||
let out, i, c;
|
||||
let char2, char3;
|
||||
|
||||
out = '';
|
||||
const len = array.length;
|
||||
i = 0;
|
||||
while (i < len) {
|
||||
c = array[i++];
|
||||
switch (c >> 4) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
// 0xxxxxxx
|
||||
out += String.fromCharCode(c);
|
||||
break;
|
||||
case 12:
|
||||
case 13:
|
||||
// 110x xxxx 10xx xxxx
|
||||
char2 = array[i++];
|
||||
out += String.fromCharCode(((c & 0x1f) << 6) | (char2 & 0x3f));
|
||||
break;
|
||||
case 14:
|
||||
// 1110 xxxx 10xx xxxx 10xx xxxx
|
||||
char2 = array[i++];
|
||||
char3 = array[i++];
|
||||
out += String.fromCharCode(
|
||||
((c & 0x0f) << 12) | ((char2 & 0x3f) << 6) | ((char3 & 0x3f) << 0)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
type ResourceType = 'f' | 'd';
|
||||
|
||||
@injectable()
|
||||
@@ -330,10 +288,9 @@ export class CreateApi {
|
||||
if (sketch) {
|
||||
const url = new URL(`${this.domain()}/sketches/${sketch.id}`);
|
||||
const headers = await this.headers();
|
||||
|
||||
// parse the secret file
|
||||
const secrets = (
|
||||
typeof content === 'string' ? content : Utf8ArrayToStr(content)
|
||||
typeof content === 'string' ? content : uint8ArrayToString(content)
|
||||
)
|
||||
.split(/\r?\n/)
|
||||
.reduce((prev, curr) => {
|
||||
@@ -397,7 +354,7 @@ export class CreateApi {
|
||||
const headers = await this.headers();
|
||||
|
||||
let data: string =
|
||||
typeof content === 'string' ? content : Utf8ArrayToStr(content);
|
||||
typeof content === 'string' ? content : uint8ArrayToString(content);
|
||||
data = await this.toggleSecretsInclude(posixPath, data, 'remove');
|
||||
|
||||
const payload = { data: btoa(data) };
|
||||
|
@@ -8,6 +8,9 @@ import { AuthenticationSession } from '../../node/auth/types';
|
||||
import { ArduinoPreferences } from '../arduino-preferences';
|
||||
import { AuthenticationClientService } from '../auth/authentication-client-service';
|
||||
import { LocalCacheFsProvider } from '../local-cache/local-cache-fs-provider';
|
||||
import { CreateUri } from './create-uri';
|
||||
|
||||
export type CloudSketchState = 'push' | 'pull';
|
||||
|
||||
@injectable()
|
||||
export class CreateFeatures implements FrontendApplicationContribution {
|
||||
@@ -18,13 +21,22 @@ export class CreateFeatures implements FrontendApplicationContribution {
|
||||
@inject(LocalCacheFsProvider)
|
||||
private readonly localCacheFsProvider: LocalCacheFsProvider;
|
||||
|
||||
/**
|
||||
* The keys are the Create URI of the sketches.
|
||||
*/
|
||||
private readonly _cloudSketchStates = new Map<string, CloudSketchState>();
|
||||
private readonly onDidChangeSessionEmitter = new Emitter<
|
||||
AuthenticationSession | undefined
|
||||
>();
|
||||
private readonly onDidChangeEnabledEmitter = new Emitter<boolean>();
|
||||
private readonly onDidChangeCloudSketchStateEmitter = new Emitter<{
|
||||
uri: URI;
|
||||
state: CloudSketchState | undefined;
|
||||
}>();
|
||||
private readonly toDispose = new DisposableCollection(
|
||||
this.onDidChangeSessionEmitter,
|
||||
this.onDidChangeEnabledEmitter
|
||||
this.onDidChangeEnabledEmitter,
|
||||
this.onDidChangeCloudSketchStateEmitter
|
||||
);
|
||||
private _enabled: boolean;
|
||||
private _session: AuthenticationSession | undefined;
|
||||
@@ -64,14 +76,46 @@ export class CreateFeatures implements FrontendApplicationContribution {
|
||||
return this.onDidChangeEnabledEmitter.event;
|
||||
}
|
||||
|
||||
get enabled(): boolean {
|
||||
return this._enabled;
|
||||
get onDidChangeCloudSketchState(): Event<{
|
||||
uri: URI;
|
||||
state: CloudSketchState | undefined;
|
||||
}> {
|
||||
return this.onDidChangeCloudSketchStateEmitter.event;
|
||||
}
|
||||
|
||||
get session(): AuthenticationSession | undefined {
|
||||
return this._session;
|
||||
}
|
||||
|
||||
get enabled(): boolean {
|
||||
return this._enabled;
|
||||
}
|
||||
|
||||
cloudSketchState(uri: URI): CloudSketchState | undefined {
|
||||
return this._cloudSketchStates.get(uri.toString());
|
||||
}
|
||||
|
||||
setCloudSketchState(uri: URI, state: CloudSketchState | undefined): void {
|
||||
if (uri.scheme !== CreateUri.scheme) {
|
||||
throw new Error(
|
||||
`Expected a URI with '${uri.scheme}' scheme. Got: ${uri.toString()}`
|
||||
);
|
||||
}
|
||||
const key = uri.toString();
|
||||
if (!state) {
|
||||
if (!this._cloudSketchStates.delete(key)) {
|
||||
console.warn(
|
||||
`Could not reset the cloud sketch state of ${key}. No state existed for the the cloud sketch.`
|
||||
);
|
||||
} else {
|
||||
this.onDidChangeCloudSketchStateEmitter.fire({ uri, state: undefined });
|
||||
}
|
||||
} else {
|
||||
this._cloudSketchStates.set(key, state);
|
||||
this.onDidChangeCloudSketchStateEmitter.fire({ uri, state });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `true` if the sketch is under `directories.data/RemoteSketchbook`. Otherwise, `false`.
|
||||
* Returns with `undefined` if `dataDirUri` is `undefined`.
|
||||
@@ -83,7 +127,10 @@ export class CreateFeatures implements FrontendApplicationContribution {
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
return dataDirUri.isEqualOrParent(new URI(sketch.uri));
|
||||
return dataDirUri
|
||||
.resolve('RemoteSketchbook')
|
||||
.resolve('ArduinoCloud')
|
||||
.isEqualOrParent(new URI(sketch.uri));
|
||||
}
|
||||
|
||||
cloudUri(sketch: Sketch): URI | undefined {
|
||||
|
@@ -29,6 +29,7 @@ import { CreateUri } from './create-uri';
|
||||
import { SketchesService } from '../../common/protocol';
|
||||
import { ArduinoPreferences } from '../arduino-preferences';
|
||||
import { Create } from './typings';
|
||||
import { stringToUint8Array } from '../../common/utils';
|
||||
|
||||
@injectable()
|
||||
export class CreateFsProvider
|
||||
@@ -154,7 +155,7 @@ export class CreateFsProvider
|
||||
|
||||
async readFile(uri: URI): Promise<Uint8Array> {
|
||||
const content = await this.getCreateApi.readFile(uri.path.toString());
|
||||
return new TextEncoder().encode(content);
|
||||
return stringToUint8Array(content);
|
||||
}
|
||||
|
||||
async writeFile(
|
||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.355 3.85509L2.85504 14.3551C2.76026 14.448 2.63281 14.5001 2.50006 14.5001C2.36731 14.5001 2.23986 14.448 2.14508 14.3551C2.0514 14.2607 1.99882 14.1331 1.99882 14.0001C1.99882 13.8671 2.0514 13.7395 2.14508 13.6451L3.82508 11.9651C3.24351 11.8742 2.70645 11.5991 2.29291 11.1802C1.87936 10.7613 1.61116 10.2208 1.52775 9.63811C1.44434 9.05543 1.55012 8.46136 1.82955 7.94328C2.10897 7.4252 2.54728 7.01047 3.08 6.76009C3.20492 6.18251 3.47405 5.64596 3.86232 5.20047C4.25058 4.75498 4.74532 4.41505 5.30042 4.21239C5.85552 4.00972 6.45289 3.9509 7.03686 4.04143C7.62082 4.13196 8.17236 4.36887 8.64004 4.73009C9.01346 4.56809 9.41786 4.48995 9.82475 4.50117C10.2316 4.51239 10.6311 4.6127 10.995 4.79503L12.645 3.14509C12.7392 3.05094 12.8669 2.99805 13 2.99805C13.1332 2.99805 13.2609 3.05094 13.355 3.14509C13.4492 3.23924 13.5021 3.36694 13.5021 3.50009C13.5021 3.63324 13.4492 3.76094 13.355 3.85509V3.85509Z" fill="#7F8C8D"/>
|
||||
<path d="M14.5 9.25047C14.4987 9.97942 14.2086 10.6782 13.6931 11.1936C13.1777 11.709 12.479 11.9992 11.75 12.0005H6.70996L12.355 6.35547C12.38 6.43042 12.4 6.50547 12.4201 6.58044C13.0153 6.72902 13.5436 7.07272 13.9206 7.55669C14.2976 8.04066 14.5016 8.63699 14.5 9.25047V9.25047Z" fill="#7F8C8D"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.5 9.24997C14.4987 9.97893 14.2086 10.6777 13.6932 11.1931C13.1777 11.7086 12.479 11.9987 11.75 12H4.25003C3.62476 11.9998 3.01822 11.7866 2.53034 11.3955C2.04247 11.0045 1.70238 10.4589 1.56612 9.84864C1.42986 9.2384 1.50556 8.59995 1.78074 8.0385C2.05593 7.47705 2.51418 7.0261 3.07998 6.75997C3.2049 6.18239 3.47404 5.64584 3.8623 5.20035C4.25056 4.75486 4.74531 4.41494 5.3004 4.21227C5.8555 4.0096 6.45288 3.95078 7.03684 4.04131C7.62081 4.13184 8.17234 4.36875 8.64003 4.72997C8.99025 4.57772 9.36814 4.49942 9.75003 4.49997C10.3635 4.49838 10.9598 4.70238 11.4438 5.07939C11.9278 5.45641 12.2715 5.9847 12.4201 6.57993C13.0153 6.7285 13.5436 7.07221 13.9206 7.55618C14.2976 8.04015 14.5016 8.63649 14.5 9.24997Z" fill="#7F8C8D"/>
|
||||
</svg>
|
After Width: | Height: | Size: 852 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.42 6.58044C12.4 6.50549 12.38 6.43042 12.355 6.35547L11.525 7.18555C11.5575 7.27223 11.6136 7.34811 11.6869 7.40464C11.7603 7.46117 11.8479 7.4961 11.94 7.50549C12.3852 7.55476 12.7947 7.77259 13.0843 8.11428C13.374 8.45597 13.5218 8.89557 13.4975 9.34284C13.4732 9.7901 13.2785 10.2111 12.9536 10.5194C12.6286 10.8276 12.1979 10.9998 11.75 11.0005H7.70996L6.70996 12.0005H11.75C12.421 12.0001 13.0688 11.7545 13.5714 11.3099C14.074 10.8653 14.3969 10.2524 14.4792 9.58644C14.5615 8.92048 14.3977 8.24739 14.0184 7.69379C13.6392 7.14019 13.0708 6.74425 12.42 6.58044V6.58044Z" fill="#7F8C8D"/>
|
||||
<path d="M13.355 3.14532C13.2606 3.05161 13.133 2.99902 13 2.99902C12.867 2.99902 12.7394 3.05161 12.6451 3.14532L10.995 4.79524C10.6311 4.61291 10.2316 4.5126 9.82472 4.50139C9.41783 4.49017 9.01343 4.56832 8.64002 4.73032C8.17233 4.3691 7.6208 4.13219 7.03684 4.04166C6.45287 3.95114 5.85549 4.00995 5.3004 4.21262C4.7453 4.41529 4.25056 4.75521 3.86229 5.2007C3.47403 5.64619 3.2049 6.18274 3.07997 6.76033C2.54726 7.01071 2.10896 7.42543 1.82954 7.9435C1.55013 8.46157 1.44434 9.05564 1.52775 9.63832C1.61115 10.221 1.87935 10.7615 2.29288 11.1804C2.70641 11.5993 3.24346 11.8744 3.82502 11.9653L2.14502 13.6453C2.05133 13.7397 1.99876 13.8673 1.99876 14.0003C1.99876 14.1333 2.05133 14.2609 2.14502 14.3553C2.23979 14.4482 2.36725 14.5003 2.5 14.5003C2.63275 14.5003 2.7602 14.4482 2.85498 14.3553L13.355 3.85528C13.4487 3.7609 13.5012 3.6333 13.5013 3.50031C13.5013 3.36732 13.4487 3.23972 13.355 3.14532V3.14532ZM4.79006 11.0003H4.25002C3.8356 11.0005 3.43458 10.8535 3.11841 10.5856C2.80224 10.3177 2.59145 9.94623 2.52362 9.5374C2.45578 9.12857 2.53529 8.70893 2.74799 8.35326C2.96069 7.99758 3.29275 7.72898 3.68502 7.59529C3.77434 7.56478 3.85319 7.50962 3.91248 7.43617C3.97176 7.36272 4.00904 7.274 4.02002 7.18025C4.09848 6.57783 4.39334 6.0245 4.84963 5.62341C5.30592 5.22233 5.89251 5.00087 6.50002 5.00032C7.1425 4.99652 7.76054 5.24628 8.21999 5.69539C8.30086 5.77275 8.40511 5.8211 8.5164 5.83285C8.6277 5.8446 8.73974 5.8191 8.83499 5.76033C9.10926 5.58886 9.42655 5.4987 9.75002 5.50032C9.9105 5.50127 10.0702 5.5231 10.225 5.56526L4.79006 11.0003Z" fill="#7F8C8D"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.75 12H4.25C3.62484 11.9999 3.01838 11.7868 2.53053 11.3959C2.04269 11.0049 1.70257 10.4595 1.56622 9.84934C1.42987 9.23923 1.50542 8.60087 1.78043 8.03945C2.05543 7.47802 2.51348 7.02702 3.0791 6.76076C3.24864 5.97929 3.68041 5.27932 4.3027 4.77712C4.92499 4.27492 5.70035 4.00071 6.5 4.00002C7.27505 3.99715 8.02853 4.25513 8.63916 4.73244C9.00591 4.57154 9.40329 4.49243 9.8037 4.5006C10.2041 4.50877 10.5979 4.60403 10.9578 4.77976C11.3177 4.9555 11.635 5.20748 11.8876 5.51822C12.1403 5.82895 12.3223 6.19097 12.4209 6.57912C13.0715 6.74324 13.6398 7.13939 14.0188 7.69309C14.3979 8.24679 14.5616 8.91989 14.4792 9.58582C14.3967 10.2518 14.0739 10.8646 13.5713 11.3092C13.0687 11.7538 12.421 11.9995 11.75 12ZM6.5 5.00002C5.89213 5.00017 5.30514 5.22179 4.84885 5.62344C4.39257 6.02508 4.09826 6.57921 4.021 7.18215C4.0093 7.27546 3.97153 7.36357 3.91202 7.43638C3.85252 7.50918 3.77369 7.56374 3.68458 7.59377C3.29236 7.72769 2.9604 7.99647 2.7478 8.35224C2.5352 8.70801 2.45576 9.12768 2.52363 9.53654C2.5915 9.9454 2.80227 10.3169 3.11841 10.5849C3.43455 10.8529 3.83555 11 4.25 11H11.75C12.198 10.9996 12.6289 10.8275 12.9539 10.5191C13.279 10.2108 13.4735 9.7896 13.4975 9.34221C13.5215 8.89481 13.3732 8.45522 13.083 8.11384C12.7929 7.77246 12.3829 7.55524 11.9375 7.50686C11.8238 7.4948 11.7176 7.44411 11.6368 7.36325C11.5559 7.28238 11.5052 7.17624 11.4932 7.06252C11.4474 6.63255 11.2439 6.2348 10.9219 5.94619C10.6 5.65758 10.1824 5.49861 9.75 5.50002C9.42739 5.49791 9.11079 5.58731 8.83692 5.75783C8.74185 5.81746 8.62955 5.84352 8.51794 5.83184C8.40633 5.82015 8.30185 5.77141 8.22119 5.69338C7.76046 5.24569 7.14241 4.99672 6.5 5.00002V5.00002Z" fill="#7F8C8D"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
@@ -12,7 +12,10 @@ import {
|
||||
LibrarySearch,
|
||||
LibraryService,
|
||||
} from '../../common/protocol/library-service';
|
||||
import { ListWidget } from '../widgets/component-list/list-widget';
|
||||
import {
|
||||
ListWidget,
|
||||
UserAbortError,
|
||||
} 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';
|
||||
@@ -141,6 +144,8 @@ export class LibraryListWidget extends ListWidget<
|
||||
// All
|
||||
installDependencies = true;
|
||||
}
|
||||
} else {
|
||||
throw new UserAbortError();
|
||||
}
|
||||
} else {
|
||||
// The lib does not have any dependencies.
|
||||
@@ -235,6 +240,21 @@ class MessageBoxDialog extends AbstractDialog<MessageBoxDialog.Result> {
|
||||
this.response = 0;
|
||||
super.handleEnter(event);
|
||||
}
|
||||
|
||||
protected override onAfterAttach(message: Message): void {
|
||||
super.onAfterAttach(message);
|
||||
let buttonToFocus: HTMLButtonElement | undefined = undefined;
|
||||
for (const child of Array.from(this.controlPanel.children)) {
|
||||
if (child instanceof HTMLButtonElement) {
|
||||
if (child.classList.contains('main')) {
|
||||
buttonToFocus = child;
|
||||
break;
|
||||
}
|
||||
buttonToFocus = child;
|
||||
}
|
||||
}
|
||||
buttonToFocus?.focus();
|
||||
}
|
||||
}
|
||||
export namespace MessageBoxDialog {
|
||||
export interface Options extends DialogProps {
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import { CommonMenus } from '@theia/core/lib/browser/common-frontend-contribution';
|
||||
import {
|
||||
MAIN_MENU_BAR,
|
||||
@@ -7,6 +6,8 @@ import {
|
||||
MenuPath,
|
||||
SubMenuOptions,
|
||||
} from '@theia/core/lib/common/menu';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
|
||||
export namespace ArduinoMenus {
|
||||
// Main menu
|
||||
@@ -154,6 +155,36 @@ export namespace ArduinoMenus {
|
||||
'2_resources',
|
||||
];
|
||||
|
||||
// -- Account
|
||||
export const ARDUINO_ACCOUNT__CONTEXT = ['arduino-account--context'];
|
||||
export const ARDUINO_ACCOUNT__CONTEXT__SIGN_IN_GROUP = [
|
||||
...ARDUINO_ACCOUNT__CONTEXT,
|
||||
'0_sign_in',
|
||||
];
|
||||
export const ARDUINO_ACCOUNT__CONTEXT__LEARN_MORE_GROUP = [
|
||||
...ARDUINO_ACCOUNT__CONTEXT,
|
||||
'1_learn_more',
|
||||
];
|
||||
export const ARDUINO_ACCOUNT__CONTEXT__GO_TO_GROUP = [
|
||||
...ARDUINO_ACCOUNT__CONTEXT,
|
||||
'2_go_to',
|
||||
];
|
||||
export const ARDUINO_ACCOUNT__CONTEXT__SIGN_OUT_GROUP = [
|
||||
...ARDUINO_ACCOUNT__CONTEXT,
|
||||
'3_sign_out',
|
||||
];
|
||||
|
||||
// Context menu from the library and boards manager widget
|
||||
export const ARDUINO_COMPONENT__CONTEXT = ['arduino-component--context'];
|
||||
export const ARDUINO_COMPONENT__CONTEXT__INFO_GROUP = [
|
||||
...ARDUINO_COMPONENT__CONTEXT,
|
||||
'0_info',
|
||||
];
|
||||
export const ARDUINO_COMPONENT__CONTEXT__ACTION_GROUP = [
|
||||
...ARDUINO_COMPONENT__CONTEXT,
|
||||
'1_action',
|
||||
];
|
||||
|
||||
// -- ROOT SSL CERTIFICATES
|
||||
export const ROOT_CERTIFICATES__CONTEXT = [
|
||||
'arduino-root-certificates--context',
|
||||
@@ -211,3 +242,5 @@ export class PlaceholderMenuNode implements MenuNode {
|
||||
return [...this.menuPath, 'placeholder'].join('-');
|
||||
}
|
||||
}
|
||||
|
||||
export const examplesLabel = nls.localize('arduino/examples/menu', 'Examples');
|
||||
|
116
arduino-ide-extension/src/browser/native-image-cache.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import {
|
||||
NativeImage,
|
||||
nativeImage,
|
||||
Size,
|
||||
} from '@theia/core/electron-shared/electron';
|
||||
import { Endpoint } from '@theia/core/lib/browser/endpoint';
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import fetch from 'cross-fetch';
|
||||
|
||||
const nativeImageIdentifierLiterals = ['cloud'] as const;
|
||||
export type NativeImageIdentifier =
|
||||
typeof nativeImageIdentifierLiterals[number];
|
||||
export const nativeImages: Record<
|
||||
NativeImageIdentifier,
|
||||
string | { light: string; dark: string }
|
||||
> = {
|
||||
cloud: { light: 'cloud-light.png', dark: 'cloud-dark.png' },
|
||||
};
|
||||
|
||||
export interface ThemeNativeImage {
|
||||
readonly light: NativeImage;
|
||||
readonly dark: NativeImage;
|
||||
}
|
||||
|
||||
export function isThemeNativeImage(arg: unknown): arg is ThemeNativeImage {
|
||||
return (
|
||||
typeof arg === 'object' &&
|
||||
(<ThemeNativeImage>arg).light !== undefined &&
|
||||
(<ThemeNativeImage>arg).dark !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
type Image = NativeImage | ThemeNativeImage;
|
||||
|
||||
@injectable()
|
||||
export class NativeImageCache implements FrontendApplicationContribution {
|
||||
private readonly cache = new Map<NativeImageIdentifier, Image>();
|
||||
private readonly loading = new Map<NativeImageIdentifier, Promise<Image>>();
|
||||
|
||||
onStart(): void {
|
||||
Object.keys(nativeImages).forEach((identifier: NativeImageIdentifier) =>
|
||||
this.getImage(identifier)
|
||||
);
|
||||
}
|
||||
|
||||
tryGetImage(identifier: NativeImageIdentifier): Image | undefined {
|
||||
return this.cache.get(identifier);
|
||||
}
|
||||
|
||||
async getImage(identifier: NativeImageIdentifier): Promise<Image> {
|
||||
const image = this.cache.get(identifier);
|
||||
if (image) {
|
||||
return image;
|
||||
}
|
||||
let loading = this.loading.get(identifier);
|
||||
if (!loading) {
|
||||
const deferred = new Deferred<Image>();
|
||||
loading = deferred.promise;
|
||||
this.loading.set(identifier, loading);
|
||||
this.fetchImage(identifier).then(
|
||||
(image) => {
|
||||
if (!this.cache.has(identifier)) {
|
||||
this.cache.set(identifier, image);
|
||||
}
|
||||
this.loading.delete(identifier);
|
||||
deferred.resolve(image);
|
||||
},
|
||||
(err) => {
|
||||
this.loading.delete(identifier);
|
||||
deferred.reject(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
return loading;
|
||||
}
|
||||
|
||||
private async fetchImage(identifier: NativeImageIdentifier): Promise<Image> {
|
||||
const value = nativeImages[identifier];
|
||||
if (typeof value === 'string') {
|
||||
return this.fetchIconData(value);
|
||||
}
|
||||
const [light, dark] = await Promise.all([
|
||||
this.fetchIconData(value.light),
|
||||
this.fetchIconData(value.dark),
|
||||
]);
|
||||
return { light, dark };
|
||||
}
|
||||
|
||||
private async fetchIconData(filename: string): Promise<NativeImage> {
|
||||
const path = `nativeImage/${filename}`;
|
||||
const endpoint = new Endpoint({ path }).getRestUrl().toString();
|
||||
const response = await fetch(endpoint);
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
const view = new Uint8Array(arrayBuffer);
|
||||
const buffer = Buffer.alloc(arrayBuffer.byteLength);
|
||||
buffer.forEach((_, index) => (buffer[index] = view[index]));
|
||||
const image = nativeImage.createFromBuffer(buffer);
|
||||
return this.maybeResize(image);
|
||||
}
|
||||
|
||||
private maybeResize(image: NativeImage): NativeImage {
|
||||
const currentSize = image.getSize();
|
||||
if (sizeEquals(currentSize, preferredSize)) {
|
||||
return image;
|
||||
}
|
||||
return image.resize(preferredSize);
|
||||
}
|
||||
}
|
||||
|
||||
const pixel = 16;
|
||||
const preferredSize: Size = { height: pixel, width: pixel };
|
||||
function sizeEquals(left: Size, right: Size): boolean {
|
||||
return left.height === right.height && left.width === right.width;
|
||||
}
|
@@ -117,6 +117,11 @@ export class MonitorWidget extends ReactWidget {
|
||||
(this.focusNode || this.node).focus();
|
||||
}
|
||||
|
||||
protected override onAfterShow(msg: Message): void {
|
||||
super.onAfterShow(msg);
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onFocusResolved = (element: HTMLElement | undefined) => {
|
||||
if (this.closing || !this.isAttached) {
|
||||
return;
|
||||
|
@@ -17,7 +17,7 @@ export class SerialMonitorOutput extends React.Component<
|
||||
* Do not touch it. It is used to be able to "follow" the serial monitor log.
|
||||
*/
|
||||
protected toDisposeBeforeUnmount = new DisposableCollection();
|
||||
private listRef: React.RefObject<any>;
|
||||
private listRef: React.RefObject<List>;
|
||||
|
||||
constructor(props: Readonly<SerialMonitorOutput.Props>) {
|
||||
super(props);
|
||||
@@ -34,12 +34,10 @@ export class SerialMonitorOutput extends React.Component<
|
||||
<List
|
||||
className="serial-monitor-messages"
|
||||
height={this.props.height}
|
||||
itemData={
|
||||
{
|
||||
itemData={{
|
||||
lines: this.state.lines,
|
||||
timestamp: this.state.timestamp,
|
||||
} as any
|
||||
}
|
||||
}}
|
||||
itemCount={this.state.lines.length}
|
||||
itemSize={18}
|
||||
width={'100%'}
|
||||
@@ -65,11 +63,13 @@ export class SerialMonitorOutput extends React.Component<
|
||||
this.state.charCount
|
||||
);
|
||||
const [lines, charCount] = truncateLines(newLines, totalCharCount);
|
||||
this.setState({
|
||||
this.setState(
|
||||
{
|
||||
lines,
|
||||
charCount,
|
||||
});
|
||||
this.scrollToBottom();
|
||||
},
|
||||
() => this.scrollToBottom()
|
||||
);
|
||||
}),
|
||||
this.props.clearConsoleEvent(() =>
|
||||
this.setState({ lines: [], charCount: 0 })
|
||||
@@ -91,11 +91,11 @@ export class SerialMonitorOutput extends React.Component<
|
||||
this.toDisposeBeforeUnmount.dispose();
|
||||
}
|
||||
|
||||
scrollToBottom = ((): void => {
|
||||
private readonly scrollToBottom = () => {
|
||||
if (this.listRef.current && this.props.monitorModel.autoscroll) {
|
||||
this.listRef.current.scrollToItem(this.state.lines.length, 'end');
|
||||
}
|
||||
}).bind(this);
|
||||
};
|
||||
}
|
||||
|
||||
const _Row = ({
|
||||
|
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1em" height="1em" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16"><g fill="#626262"><path d="M16 7.992C16 3.58 12.416 0 8 0S0 3.58 0 7.992c0 2.43 1.104 4.62 2.832 6.09c.016.016.032.016.032.032c.144.112.288.224.448.336c.08.048.144.111.224.175A7.98 7.98 0 0 0 8.016 16a7.98 7.98 0 0 0 4.48-1.375c.08-.048.144-.111.224-.16c.144-.111.304-.223.448-.335c.016-.016.032-.016.032-.032c1.696-1.487 2.8-3.676 2.8-6.106zm-8 7.001c-1.504 0-2.88-.48-4.016-1.279c.016-.128.048-.255.08-.383a4.17 4.17 0 0 1 .416-.991c.176-.304.384-.576.64-.816c.24-.24.528-.463.816-.639c.304-.176.624-.304.976-.4A4.15 4.15 0 0 1 8 10.342a4.185 4.185 0 0 1 2.928 1.166c.368.368.656.8.864 1.295c.112.288.192.592.24.911A7.03 7.03 0 0 1 8 14.993zm-2.448-7.4a2.49 2.49 0 0 1-.208-1.024c0-.351.064-.703.208-1.023c.144-.32.336-.607.576-.847c.24-.24.528-.431.848-.575c.32-.144.672-.208 1.024-.208c.368 0 .704.064 1.024.208c.32.144.608.336.848.575c.24.24.432.528.576.847c.144.32.208.672.208 1.023c0 .368-.064.704-.208 1.023a2.84 2.84 0 0 1-.576.848a2.84 2.84 0 0 1-.848.575a2.715 2.715 0 0 1-2.064 0a2.84 2.84 0 0 1-.848-.575a2.526 2.526 0 0 1-.56-.848zm7.424 5.306c0-.032-.016-.048-.016-.08a5.22 5.22 0 0 0-.688-1.406a4.883 4.883 0 0 0-1.088-1.135a5.207 5.207 0 0 0-1.04-.608a2.82 2.82 0 0 0 .464-.383a4.2 4.2 0 0 0 .624-.784a3.624 3.624 0 0 0 .528-1.934a3.71 3.71 0 0 0-.288-1.47a3.799 3.799 0 0 0-.816-1.199a3.845 3.845 0 0 0-1.2-.8a3.72 3.72 0 0 0-1.472-.287a3.72 3.72 0 0 0-1.472.288a3.631 3.631 0 0 0-1.2.815a3.84 3.84 0 0 0-.8 1.199a3.71 3.71 0 0 0-.288 1.47c0 .352.048.688.144 1.007c.096.336.224.64.4.927c.16.288.384.544.624.784c.144.144.304.271.48.383a5.12 5.12 0 0 0-1.04.624c-.416.32-.784.703-1.088 1.119a4.999 4.999 0 0 0-.688 1.406c-.016.032-.016.064-.016.08C1.776 11.636.992 9.91.992 7.992C.992 4.14 4.144.991 8 .991s7.008 3.149 7.008 7.001a6.96 6.96 0 0 1-2.032 4.907z"/></g></svg>
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -165,7 +165,7 @@ div#select-board-dialog .selectBoardContainer .list .item.selected i {
|
||||
border: 1px solid var(--theia-arduino-toolbar-dropdown-border);
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
height: 28px;
|
||||
height: var(--arduino-button-height);
|
||||
margin: 0 4px;
|
||||
overflow: hidden;
|
||||
padding: 0 10px;
|
||||
|
@@ -15,10 +15,10 @@
|
||||
|
||||
.p-TabBar-tabIcon.cloud-sketchbook-tree-icon {
|
||||
background-color: var(--theia-foreground);
|
||||
-webkit-mask: url(./cloud-sketchbook-tree-icon.svg);
|
||||
-webkit-mask: url(../icons/arduino-cloud.svg);
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
width: var(--theia-icon-size);
|
||||
width: 19px !important;
|
||||
height: var(--theia-icon-size);
|
||||
-webkit-mask-size: 100%;
|
||||
}
|
||||
@@ -26,7 +26,7 @@
|
||||
.p-mod-current
|
||||
.cloud-sketchbook-tree-icon {
|
||||
background-color: var(--theia-foreground);
|
||||
-webkit-mask: url(./cloud-sketchbook-tree-icon-filled.svg);
|
||||
-webkit-mask: url(../icons/arduino-cloud-filled.svg);
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
-webkit-mask-size: 100%;
|
||||
@@ -99,28 +99,9 @@
|
||||
color: var(--theia-textLink-foreground);
|
||||
}
|
||||
|
||||
.pull-sketch-icon {
|
||||
background-color: var(--theia-foreground);
|
||||
-webkit-mask: url(./pull-sketch-icon.svg);
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
width: var(--theia-icon-size);
|
||||
height: var(--theia-icon-size);
|
||||
}
|
||||
|
||||
.push-sketch-icon {
|
||||
background-color: var(--theia-foreground);
|
||||
-webkit-mask: url(./push-sketch-icon.svg);
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
width: var(--theia-icon-size);
|
||||
height: var(--theia-icon-size);
|
||||
}
|
||||
|
||||
.account-icon {
|
||||
background: url("./account-icon.svg") center center no-repeat;
|
||||
width: var(--theia-icon-size);
|
||||
height: var(--theia-icon-size);
|
||||
width: var(--theia-private-sidebar-icon-size);
|
||||
height: var(--theia-private-sidebar-icon-size);
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -199,3 +180,12 @@
|
||||
.arduino-share-sketch-dialog .sketch-link-embed textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.actions.item.flex-line .fa,
|
||||
.theia-file-icons-js.file-icon .fa {
|
||||
font-size: var(--theia-icon-size);
|
||||
}
|
||||
|
||||
.theia-file-icons-js.file-icon.not-in-sync-offline .fa {
|
||||
color: var(--theia-activityBar-inactiveForeground);
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@
|
||||
|
||||
min-width: 424px;
|
||||
max-height: 560px;
|
||||
padding: 0 28px;
|
||||
padding: 0 var(--arduino-button-height);
|
||||
}
|
||||
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogTitle {
|
||||
@@ -35,7 +35,7 @@
|
||||
}
|
||||
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogContent > input {
|
||||
margin-bottom: 28px;
|
||||
margin-bottom: var(--arduino-button-height);
|
||||
}
|
||||
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogContent > div {
|
||||
@@ -43,7 +43,7 @@
|
||||
}
|
||||
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogContent .dialogSection {
|
||||
margin-top: 28px;
|
||||
margin-top: var(--arduino-button-height);
|
||||
}
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogContent .dialogSection:first-child {
|
||||
margin-top: 0;
|
||||
|
@@ -1,14 +1,19 @@
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
src: url('fonts/OpenSans-Regular-webfont.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans Bold';
|
||||
src: url('fonts/OpenSans-Bold-webfont.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
src:
|
||||
url('fonts/FontAwesome.ttf?2jhpmq') format('truetype'),
|
||||
url('fonts/FontAwesome.woff?2jhpmq') format('woff'),
|
||||
url('fonts/FontAwesome.svg?2jhpmq#FontAwesome') format('svg');
|
||||
url('fonts/FontAwesome.ttf?h959em') format('truetype'),
|
||||
url('fonts/FontAwesome.woff?h959em') format('woff'),
|
||||
url('fonts/FontAwesome.svg?h959em#FontAwesome') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
@@ -674,3 +679,21 @@
|
||||
.fa-microchip:before {
|
||||
content: "\f2db";
|
||||
}
|
||||
.fa-arduino-cloud-download:before {
|
||||
content: "\e910";
|
||||
}
|
||||
.fa-arduino-cloud-upload:before {
|
||||
content: "\e914";
|
||||
}
|
||||
.fa-arduino-cloud:before {
|
||||
content: "\e915";
|
||||
}
|
||||
.fa-arduino-cloud-filled:before {
|
||||
content: "\e912";
|
||||
}
|
||||
.fa-arduino-cloud-offline:before {
|
||||
content: "\e913";
|
||||
}
|
||||
.fa-arduino-cloud-filled-offline:before {
|
||||
content: "\e911";
|
||||
}
|
||||
|
@@ -23,6 +23,12 @@
|
||||
<glyph unicode="" glyph-name="arduino-monitor" horiz-adv-x="1536" d="M651.891 59.977c-92.835 0-179.095 28.493-250.5 77.197l-129.659-129.658c-22.494-22.496-58.964-22.496-81.458 0s-22.494 58.963 0 81.459l124.954 124.954c-67.75 78.157-108.777 180.090-108.777 291.489 0 245.759 199.68 445.439 445.44 445.439s445.44-199.679 445.44-445.439c0-245.761-199.68-445.441-445.44-445.441zM651.891 797.257c-161.28 0-291.84-130.559-291.84-291.839s130.56-291.841 291.84-291.841c160.512 0 291.84 130.561 291.84 291.841 0 160.511-130.56 291.839-291.84 291.839zM1149.562 472.766c0-35.423 28.717-64.138 64.141-64.138s64.134 28.716 64.134 64.138c0 35.423-28.71 64.139-64.134 64.139s-64.141-28.716-64.141-64.139zM64.064 408.62c-35.382 0-64.064 28.682-64.064 64.063s28.682 64.064 64.064 64.064c35.381 0 64.064-28.682 64.064-64.064s-28.683-64.063-64.064-64.063zM1458.707 408.628c-35.418 0-64.134 28.716-64.134 64.138s28.717 64.139 64.134 64.139c35.424 0 64.141-28.716 64.141-64.139s-28.717-64.138-64.141-64.138zM652.659 424.010c-44.961 0-81.408 36.447-81.408 81.407s36.447 81.408 81.408 81.408c44.96 0 81.408-36.447 81.408-81.408s-36.448-81.407-81.408-81.407z" />
|
||||
<glyph unicode="" glyph-name="arduino-sketch-tabs-menu" d="M511.998 347.425c50.495 0 91.432 40.936 91.432 91.432s-40.936 91.432-91.432 91.432c-50.495 0-91.432-40.936-91.432-91.432s40.936-91.432 91.432-91.432zM923.433 347.425c50.494 0 91.432 40.936 91.432 91.432s-40.937 91.432-91.432 91.432c-50.494 0-91.432-40.936-91.432-91.432s40.937-91.432 91.432-91.432zM100.565 347.425c50.495 0 91.432 40.936 91.432 91.432s-40.936 91.432-91.432 91.432c-50.495 0-91.432-40.936-91.432-91.432s40.936-91.432 91.432-91.432z" />
|
||||
<glyph unicode="" glyph-name="arduino-plotter" horiz-adv-x="862" d="M323.368-19.351c-20.263 0-39 11.42-48.21 29.788l-146.789 293.581h-74.474c-29.789 0-53.895 24.107-53.895 53.895s24.105 53.895 53.895 53.895h107.789c20.421 0 39.053-11.528 48.21-29.788l96.527-193.056 180.263 720.949c5.842 23.579 26.737 40.263 51 40.842 23.947 1.579 45.893-15.158 52.894-38.421l150.162-500.526h67.681c29.788 0 53.895-24.107 53.895-53.895s-24.107-53.895-53.895-53.895h-107.789c-23.789 0-44.787 15.629-51.631 38.422l-105.316 351.104-168.052-672.053c-5.474-21.897-23.948-38.055-46.368-40.529-2-0.21-3.947-0.313-5.895-0.313h-0.001z" />
|
||||
<glyph unicode="" glyph-name="arduino-cloud-download" d="M684.256 156.891l-146.286-146.286c-6.932-6.802-16.255-10.606-25.964-10.606s-19.032 3.803-25.964 10.606l-146.286 146.286c-3.41 3.41-6.115 7.458-7.96 11.913s-2.796 9.23-2.796 14.052c-0.001 9.738 3.868 19.079 10.754 25.965s16.226 10.756 25.964 10.756c4.822 0 9.597-0.949 14.052-2.795s8.504-4.549 11.914-7.959l83.749-84.107v423.856c0 9.699 3.853 19.002 10.712 25.86s16.16 10.712 25.86 10.712c9.699 0 19.001-3.853 25.86-10.712s10.712-16.16 10.712-25.86v-423.856l83.749 84.107c6.886 6.886 16.227 10.756 25.966 10.756s19.079-3.869 25.966-10.756c6.886-6.886 10.755-16.227 10.755-25.966s-3.869-19.079-10.755-25.966zM786.286 292.572h-128c-9.699 0-19.001 3.852-25.86 10.711s-10.712 16.161-10.712 25.86c0 9.699 3.853 19.001 10.712 25.86s16.16 10.712 25.86 10.712h128c32.768 0.031 64.285 12.618 88.057 35.172 23.779 22.554 38.005 53.361 39.76 86.085s-9.092 64.877-30.318 89.846c-21.219 24.97-51.207 40.858-83.785 44.396-8.316 0.882-16.084 4.59-21.994 10.505-5.917 5.914-9.626 13.678-10.503 21.996-3.35 31.449-18.235 60.542-41.784 81.652-23.551 21.11-54.092 32.737-85.719 32.634-23.597 0.154-46.754-6.384-66.785-18.857-6.953-4.363-15.168-6.269-23.332-5.414s-15.805 4.42-21.704 10.128c-33.699 32.745-78.905 50.956-125.893 50.714-44.461-0.011-87.395-16.221-120.77-45.598s-54.9-69.908-60.551-114.009c-0.856-6.825-3.618-13.27-7.971-18.595s-10.119-9.315-16.636-11.512c-28.688-9.795-52.969-29.455-68.519-55.477s-21.361-56.718-16.396-86.623c4.964-29.905 20.381-57.078 43.504-76.68s52.454-30.362 82.768-30.363h128c9.699 0 19.002-3.853 25.86-10.712s10.711-16.16 10.711-25.86c0-9.699-3.853-19.002-10.711-25.86s-16.161-10.711-25.86-10.711h-128c-45.726 0.010-90.084 15.596-125.767 44.191s-60.559 68.491-70.532 113.116c-9.973 44.625-4.447 91.317 15.667 132.381s53.618 74.052 94.989 93.527c12.401 57.159 43.982 108.357 89.498 145.089s102.228 56.789 160.717 56.839c56.689 0.21 111.801-18.659 156.464-53.571 26.825 11.769 55.891 17.556 85.178 16.958s58.092-7.565 84.415-20.419c26.323-12.854 49.532-31.284 68.007-54.012 18.483-22.728 31.795-49.208 39.007-77.598 47.587-12.004 89.154-40.98 116.875-81.479 27.728-40.499 39.702-89.732 33.675-138.44-6.034-48.708-29.645-93.536-66.406-126.054s-84.136-50.488-133.215-50.527z" />
|
||||
<glyph unicode="" glyph-name="arduino-cloud-filled-offline" d="M854.72 704.131l-671.997-672.001c-6.066-5.946-14.223-9.28-22.719-9.28s-16.653 3.334-22.719 9.28c-5.996 6.042-9.361 14.208-9.361 22.72s3.365 16.678 9.361 22.72l107.52 107.52c-37.22 5.818-71.592 23.424-98.059 50.234s-43.632 61.402-48.97 98.694c-5.338 37.292 1.432 75.312 19.315 108.469s45.935 59.7 80.029 75.724c7.995 36.965 25.219 71.304 50.068 99.816s56.512 50.267 92.038 63.237c35.526 12.971 73.758 16.735 111.132 10.941s72.672-20.956 102.604-44.074c23.899 10.368 49.78 15.369 75.821 14.651 26.038-0.718 51.606-7.138 74.896-18.807l105.6 105.596c6.029 6.026 14.202 9.411 22.72 9.411 8.525 0 16.698-3.385 22.72-9.411 6.029-6.026 9.414-14.198 9.414-22.72s-3.386-16.694-9.414-22.72v0zM928 358.827c-0.083-46.653-18.65-91.375-51.642-124.36-32.986-32.986-77.702-51.558-124.358-51.642h-322.563l361.283 361.282c1.6-4.797 2.88-9.6 4.166-14.398 38.093-9.509 71.904-31.506 96.032-62.48s37.184-69.139 37.082-108.402v0z" />
|
||||
<glyph unicode="" glyph-name="arduino-cloud-filled" d="M928 358.859c-0.083-46.653-18.65-91.375-51.635-124.36-32.992-32.992-77.709-51.558-124.365-51.642h-479.998c-40.017 0.013-78.836 13.658-110.060 38.688-31.224 25.024-52.989 59.942-61.71 98.999-8.721 39.055-3.876 79.916 13.736 115.849s46.94 64.794 83.151 81.826c7.995 36.965 25.22 71.304 50.068 99.816s56.513 50.266 92.038 63.237c35.526 12.971 73.759 16.735 111.132 10.941s72.672-20.956 102.604-44.074c22.414 9.744 46.599 14.755 71.040 14.72 39.262 0.102 77.425-12.954 108.401-37.083s52.973-57.94 62.483-96.035c38.093-9.508 71.904-31.506 96.032-62.48s37.184-69.14 37.082-108.403z" />
|
||||
<glyph unicode="" glyph-name="arduino-cloud-offline" d="M794.88 529.709c-1.28 4.797-2.56 9.601-4.16 14.398l-53.12-53.125c2.080-5.548 5.67-10.404 10.362-14.022 4.698-3.618 10.304-5.853 16.198-6.454 28.493-3.153 54.701-17.094 73.235-38.963 18.541-21.868 28-50.003 26.445-78.628s-14.016-55.569-34.81-75.3c-20.8-19.725-48.365-30.746-77.030-30.79h-258.563l-64-64h322.563c42.944 0.026 84.403 15.744 116.57 44.198s52.832 67.68 58.099 110.301c5.267 42.621-5.216 85.699-29.491 121.13-24.269 35.43-60.646 60.771-102.298 71.254v0zM854.72 749.557c-6.042 5.997-14.208 9.363-22.72 9.363s-16.678-3.366-22.714-9.363l-105.606-105.595c-23.29 11.669-48.858 18.089-74.898 18.806s-51.923-4.284-75.821-14.652c-29.932 23.118-65.23 38.28-102.604 44.074s-75.606 2.029-111.132-10.941c-35.526-12.971-67.19-34.726-92.039-63.237s-42.073-62.851-50.068-99.816c-34.093-16.024-62.145-42.566-80.028-75.723s-24.653-71.177-19.315-108.468c5.338-37.292 22.502-71.884 48.968-98.693s60.837-44.416 98.057-50.234l-107.52-107.52c-5.996-6.042-9.361-14.208-9.361-22.72s3.364-16.678 9.361-22.72c6.065-5.946 14.223-9.28 22.719-9.28s16.653 3.334 22.719 9.28l672.001 672.001c5.997 6.040 9.357 14.207 9.363 22.718 0 8.511-3.366 16.678-9.363 22.719v0zM306.564 246.838h-34.563c-26.523-0.013-52.188 9.395-72.423 26.541s-33.725 40.92-38.067 67.085c-4.342 26.165 0.747 53.022 14.36 75.785s34.865 39.954 59.97 48.51c5.716 1.953 10.763 5.483 14.557 10.184s6.18 10.379 6.883 16.379c5.021 38.555 23.892 73.968 53.095 99.638s66.744 39.843 105.625 39.878c41.119 0.243 80.673-15.741 110.078-44.484 5.176-4.951 11.848-8.045 18.97-8.797s14.294 0.88 20.39 4.641c17.553 10.974 37.86 16.744 58.562 16.641 10.271-0.061 20.492-1.458 30.399-4.156l-347.836-347.843z" />
|
||||
<glyph unicode="" glyph-name="arduino-cloud-upload" d="M684.258 412.892c-6.932-6.799-16.255-10.607-25.964-10.607s-19.032 3.809-25.964 10.607l-83.751 84.118v-423.867c0-9.699-3.853-19.003-10.711-25.856-6.859-6.861-16.161-10.715-25.86-10.715s-19.001 3.855-25.86 10.715c-6.859 6.853-10.712 16.157-10.712 25.856v423.867l-83.749-84.118c-6.886-6.886-16.227-10.756-25.966-10.756s-19.079 3.869-25.966 10.756c-6.886 6.886-10.755 16.227-10.755 25.966s3.869 19.079 10.755 25.966l146.286 146.286c6.903 6.854 16.236 10.701 25.964 10.701s19.062-3.847 25.964-10.701l146.286-146.286c6.853-6.904 10.7-16.237 10.701-25.965s-3.845-19.062-10.698-25.966zM786.286 256.001h-128c-9.699 0-19.001 3.852-25.86 10.711s-10.712 16.161-10.712 25.86c0 9.699 3.853 19.001 10.712 25.86s16.16 10.712 25.86 10.712h128c32.768 0.031 64.285 12.618 88.057 35.172 23.779 22.554 38.005 53.361 39.76 86.085s-9.092 64.877-30.318 89.846c-21.219 24.97-51.207 40.858-83.785 44.396-8.316 0.882-16.084 4.59-21.994 10.505-5.917 5.914-9.626 13.678-10.503 21.996-3.35 31.449-18.235 60.542-41.784 81.652-23.551 21.11-54.092 32.737-85.719 32.634-23.597 0.154-46.754-6.384-66.785-18.857-6.954-4.362-15.168-6.268-23.331-5.413s-15.805 4.419-21.705 10.127c-33.699 32.745-78.905 50.956-125.893 50.714-44.461-0.011-87.395-16.221-120.77-45.598s-54.9-69.908-60.551-114.009c-0.856-6.825-3.618-13.27-7.971-18.595s-10.119-9.315-16.636-11.512c-28.688-9.795-52.969-29.455-68.519-55.477s-21.361-56.718-16.396-86.623c4.964-29.905 20.381-57.078 43.504-76.68s52.454-30.362 82.768-30.363h128c9.699 0 19.002-3.853 25.86-10.712s10.711-16.16 10.711-25.86c0-9.699-3.853-19.002-10.711-25.86s-16.161-10.711-25.86-10.711h-128c-45.726 0.010-90.084 15.596-125.767 44.191s-60.559 68.491-70.532 113.116c-9.973 44.625-4.447 91.317 15.667 132.381s53.618 74.052 94.989 93.527c12.401 57.159 43.982 108.357 89.498 145.089s102.228 56.789 160.717 56.839c56.689 0.21 111.801-18.659 156.464-53.571 26.825 11.769 55.891 17.556 85.178 16.958s58.092-7.565 84.415-20.419c26.323-12.854 49.532-31.284 68.007-54.012 18.483-22.728 31.795-49.208 39.007-77.598 47.587-12.004 89.154-40.98 116.875-81.479 27.728-40.499 39.702-89.732 33.675-138.44-6.034-48.708-29.645-93.536-66.406-126.054s-84.136-50.488-133.215-50.527z" />
|
||||
<glyph unicode="" glyph-name="arduino-cloud" d="M752 182.857h-480c-40.010 0.006-78.824 13.645-110.046 38.662-31.222 25.024-52.989 59.93-61.716 98.98-8.726 39.047-3.891 79.902 13.709 115.833s46.915 64.796 83.115 81.836c10.851 50.014 38.484 94.812 78.31 126.953s89.45 49.69 140.627 49.734c49.603 0.184 97.826-16.327 136.906-46.875 23.472 10.298 48.904 15.361 74.531 14.838s50.829-6.62 73.862-17.866c23.034-11.247 43.341-27.374 59.507-47.261 16.173-19.887 27.821-43.056 34.131-67.898 41.638-10.504 78.010-35.857 102.266-71.294 24.262-35.437 34.739-78.515 29.466-121.135-5.28-42.623-25.939-81.842-58.106-110.296s-73.619-44.179-116.563-44.211zM416 630.856c-38.904-0.010-76.471-14.193-105.674-39.899s-48.038-61.169-52.982-99.757c-0.749-5.972-3.166-11.611-6.975-16.271s-8.853-8.151-14.556-10.073c-25.102-8.571-46.348-25.773-59.954-48.542s-18.691-49.628-14.347-75.795c4.344-26.167 17.833-49.943 38.066-67.095s45.897-26.566 72.422-26.566h480c28.672 0.026 56.25 11.040 77.050 30.778 20.806 19.731 33.254 46.688 34.79 75.321s-7.955 56.767-26.528 78.616c-18.566 21.848-44.806 35.75-73.312 38.847-7.277 0.772-14.074 4.016-19.245 9.191-5.178 5.176-8.422 11.969-9.19 19.247-2.931 27.518-15.955 52.974-36.563 71.445-20.602 18.471-47.328 28.645-75.002 28.555-20.647 0.135-40.909-5.587-58.437-16.5-6.084-3.816-13.272-5.484-20.415-4.737s-13.83 3.868-18.992 8.861c-29.487 28.652-69.042 44.586-110.156 44.375v0z" />
|
||||
<glyph unicode="" glyph-name="music" horiz-adv-x="878" d="M877.714 822.857v-640c0-80.571-120.571-109.714-182.857-109.714s-182.857 29.143-182.857 109.714 120.571 109.714 182.857 109.714c37.714 0 75.429-6.857 109.714-22.286v306.857l-438.857-135.429v-405.143c0-80.571-120.571-109.714-182.857-109.714s-182.857 29.143-182.857 109.714 120.571 109.714 182.857 109.714c37.714 0 75.429-6.857 109.714-22.286v552.571c0 24 16 45.143 38.857 52.571l475.429 146.286c5.143 1.714 10.286 2.286 16 2.286 30.286 0 54.857-24.571 54.857-54.857z" />
|
||||
<glyph unicode="" glyph-name="search" horiz-adv-x="951" d="M658.286 475.428c0 141.143-114.857 256-256 256s-256-114.857-256-256 114.857-256 256-256 256 114.857 256 256zM950.857 0c0-40-33.143-73.143-73.143-73.143-19.429 0-38.286 8-51.429 21.714l-196 195.429c-66.857-46.286-146.857-70.857-228-70.857-222.286 0-402.286 180-402.286 402.286s180 402.286 402.286 402.286 402.286-180 402.286-402.286c0-81.143-24.571-161.143-70.857-228l196-196c13.143-13.143 21.143-32 21.143-51.429z" />
|
||||
<glyph unicode="" glyph-name="envelope-o" d="M950.857 91.428v438.857c-12-13.714-25.143-26.286-39.429-37.714-81.714-62.857-164-126.857-243.429-193.143-42.857-36-96-80-155.429-80h-1.143c-59.429 0-112.571 44-155.429 80-79.429 66.286-161.714 130.286-243.429 193.143-14.286 11.429-27.429 24-39.429 37.714v-438.857c0-9.714 8.571-18.286 18.286-18.286h841.143c9.714 0 18.286 8.571 18.286 18.286zM950.857 692c0 14.286 3.429 39.429-18.286 39.429h-841.143c-9.714 0-18.286-8.571-18.286-18.286 0-65.143 32.571-121.714 84-162.286 76.571-60 153.143-120.571 229.143-181.143 30.286-24.571 85.143-77.143 125.143-77.143h1.143c40 0 94.857 52.571 125.143 77.143 76 60.571 152.571 121.143 229.143 181.143 37.143 29.143 84 92.571 84 141.143zM1024 713.143v-621.714c0-50.286-41.143-91.429-91.429-91.429h-841.143c-50.286 0-91.429 41.143-91.429 91.429v621.714c0 50.286 41.143 91.429 91.429 91.429h841.143c50.286 0 91.429-41.143 91.429-91.429z" />
|
||||
|
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 164 KiB |
@@ -15,7 +15,7 @@
|
||||
}
|
||||
|
||||
.ide-updater-dialog--logo-container {
|
||||
margin-right: 28px;
|
||||
margin-right: var(--arduino-button-height);
|
||||
}
|
||||
|
||||
.ide-updater-dialog--logo {
|
||||
@@ -76,7 +76,7 @@
|
||||
.ide-updater-dialog .buttons-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 28px;
|
||||
margin-top: var(--arduino-button-height);
|
||||
}
|
||||
|
||||
.ide-updater-dialog .buttons-container a.theia-button {
|
||||
|
@@ -20,6 +20,10 @@
|
||||
@import './progress-bar.css';
|
||||
@import './settings-step-input.css';
|
||||
|
||||
:root {
|
||||
--arduino-button-height: 28px;
|
||||
}
|
||||
|
||||
/* Revive of the `--theia-icon-loading`. The variable has been removed from Theia while IDE2 still uses is. */
|
||||
/* The SVG icons are still part of Theia (1.31.1) */
|
||||
/* https://github.com/arduino/arduino-ide/pull/1662#issuecomment-1324997134 */
|
||||
@@ -64,9 +68,9 @@ body.theia-dark {
|
||||
|
||||
/* Makes the sidepanel a bit wider when opening the widget */
|
||||
.p-DockPanel-widget {
|
||||
min-width: 200px;
|
||||
min-width: 220px;
|
||||
min-height: 20px;
|
||||
height: 200px;
|
||||
height: 220px;
|
||||
}
|
||||
|
||||
/* Overrule the default Theia CSS button styles. */
|
||||
@@ -74,7 +78,7 @@ button.theia-button,
|
||||
.theia-button {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
font-family: 'Open Sans',sans-serif;
|
||||
font-family: 'Open Sans Bold',sans-serif;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
@@ -95,7 +99,7 @@ button.theia-button,
|
||||
}
|
||||
|
||||
button.theia-button {
|
||||
height: 28px;
|
||||
height: var(--arduino-button-height);
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
@@ -154,10 +158,6 @@ button.theia-button.message-box-dialog-button {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* High Contrast Theme rules */
|
||||
/* TODO: Remove it when the Theia version is upgraded to 1.27.0 and use Theia APIs to implement it*/
|
||||
.hc-black.hc-theia.theia-hc button.theia-button:hover,
|
||||
|
@@ -44,102 +44,152 @@
|
||||
height: 100%; /* This has top be 100% down to the `scrollContainer`. */
|
||||
}
|
||||
|
||||
.filterable-list-container .items-container > div > div:nth-child(odd) {
|
||||
background-color: var(--theia-sideBar-background);
|
||||
filter: contrast(105%);
|
||||
}
|
||||
|
||||
.filterable-list-container .items-container > div > div:nth-child(even) {
|
||||
background-color: var(--theia-sideBar-background);
|
||||
filter: contrast(95%);
|
||||
}
|
||||
|
||||
.filterable-list-container .items-container > div > div:hover {
|
||||
background-color: var(--theia-sideBar-background);
|
||||
filter: contrast(90%);
|
||||
}
|
||||
|
||||
.component-list-item {
|
||||
padding: 10px 10px 10px 15px;
|
||||
font-size: var(--theia-ui-font-size1);
|
||||
}
|
||||
|
||||
.component-list-item:hover {
|
||||
cursor: pointer;
|
||||
padding: 20px 15px 25px;
|
||||
}
|
||||
|
||||
.component-list-item .header {
|
||||
padding-bottom: 2px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: var(--theia-statusBar-height);
|
||||
}
|
||||
|
||||
.component-list-item .header .version-info {
|
||||
.component-list-item .header > div {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.component-list-item .header > div .p-TabBar-toolbar {
|
||||
align-self: start;
|
||||
padding: unset;
|
||||
margin-right: unset;
|
||||
}
|
||||
|
||||
.component-list-item:hover .header > div .p-TabBar-toolbar > div {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.component-list-item .header > div .p-TabBar-toolbar > div {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.component-list-item .header .title {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
white-space: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.component-list-item .header .title .name {
|
||||
font-family: 'Open Sans Bold';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.component-list-item .header .version {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.component-list-item .header .name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.component-list-item .header .author {
|
||||
font-weight: bold;
|
||||
color: var(--theia-panelTitle-inactiveForeground);
|
||||
}
|
||||
|
||||
.component-list-item:hover .header .author {
|
||||
color: var(--theia-foreground);
|
||||
}
|
||||
|
||||
.component-list-item .header .version {
|
||||
color: var(--theia-panelTitle-inactiveForeground);
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.component-list-item .footer .theia-button.install {
|
||||
height: auto; /* resets the default Theia button height in the filterable list widget */
|
||||
}
|
||||
|
||||
.component-list-item .header .installed:before {
|
||||
margin-left: 4px;
|
||||
.component-list-item .header .installed-version:before {
|
||||
min-width: 79px;
|
||||
display: inline-block;
|
||||
justify-self: end;
|
||||
background-color: var(--theia-button-background);
|
||||
text-align: center;
|
||||
background-color: var(--theia-arduino-toolbar-dropdown-option-backgroundHover);
|
||||
padding: 2px 4px 2px 4px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
max-height: calc(1em + 4px);
|
||||
color: var(--theia-button-foreground);
|
||||
content: attr(install);
|
||||
}
|
||||
|
||||
.component-list-item .header .installed:hover:before {
|
||||
background-color: var(--theia-button-foreground);
|
||||
color: var(--theia-button-background);
|
||||
content: attr(uninstall);
|
||||
content: attr(version);
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.component-list-item[min-width~="170px"] .footer {
|
||||
padding: 5px 5px 0px 0px;
|
||||
min-height: 35px;
|
||||
.component-list-item .header .installed-version:hover:before {
|
||||
content: attr(remove);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.component-list-item .content {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
flex-direction: column;
|
||||
padding-top: 4px;
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.component-list-item .content > p {
|
||||
margin-block-start: unset;
|
||||
margin-block-end: unset;
|
||||
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3;
|
||||
white-space: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.component-list-item .content > .info {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.component-list-item .footer {
|
||||
flex-direction: column-reverse;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.component-list-item .footer > * {
|
||||
display: inline-block;
|
||||
margin: 5px 0px 0px 10px;
|
||||
}
|
||||
|
||||
.filterable-list-container .separator {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.filterable-list-container .separator :last-child,
|
||||
.filterable-list-container .separator :first-child {
|
||||
min-height: 8px;
|
||||
max-height: 8px;
|
||||
min-width: 8px;
|
||||
max-width: 8px;
|
||||
}
|
||||
|
||||
div.filterable-list-container > div > div > div > div:nth-child(1) > div.separator :first-child,
|
||||
div.filterable-list-container > div > div > div > div:nth-child(1) > div.separator :last-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.filterable-list-container .separator .line {
|
||||
max-height: 1px;
|
||||
height: 1px;
|
||||
background-color: var(--theia-activityBar-inactiveForeground);
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.component-list-item:hover .footer > label {
|
||||
display: inline-block;
|
||||
align-self: center;
|
||||
margin: 5px 0px 0px 10px;
|
||||
}
|
||||
|
||||
.component-list-item .info a {
|
||||
@@ -151,13 +201,33 @@
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.component-list-item .theia-button.secondary.no-border {
|
||||
border: 2px solid var(--theia-button-foreground)
|
||||
}
|
||||
|
||||
.component-list-item .theia-button.secondary.no-border:hover {
|
||||
border: 2px solid var(--theia-secondaryButton-foreground)
|
||||
}
|
||||
|
||||
.component-list-item .theia-button {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.component-list-item .theia-select {
|
||||
height: var(--arduino-button-height);
|
||||
min-height: var(--arduino-button-height);
|
||||
width: 65px;
|
||||
min-width: 65px;
|
||||
}
|
||||
|
||||
/* High Contrast Theme rules */
|
||||
/* TODO: Remove it when the Theia version is upgraded to 1.27.0 and use Theia APIs to implement it*/
|
||||
.hc-black.hc-theia.theia-hc .component-list-item .header .installed:hover:before {
|
||||
.hc-black.hc-theia.theia-hc .component-list-item .header .installed-version:hover:before {
|
||||
background-color: transparent;
|
||||
outline: 1px dashed var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
.hc-black.hc-theia.theia-hc .component-list-item .header .installed:before {
|
||||
.hc-black.hc-theia.theia-hc .component-list-item .header .installed-version:before {
|
||||
color: var(--theia-button-background);
|
||||
border: 1px solid var(--theia-button-border);
|
||||
}
|
||||
|
@@ -28,8 +28,8 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
height: var(--arduino-button-height);
|
||||
width: var(--arduino-button-height);
|
||||
}
|
||||
|
||||
.p-TabBar-toolbar .item.arduino-tool-item .arduino-upload-sketch--toolbar,
|
||||
@@ -66,8 +66,8 @@
|
||||
}
|
||||
|
||||
.arduino-tool-icon {
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
height: var(--arduino-button-height);
|
||||
width: var(--arduino-button-height);
|
||||
}
|
||||
|
||||
.arduino-verify-sketch--toolbar-icon {
|
||||
@@ -100,19 +100,6 @@
|
||||
background-color: var(--theia-titleBar-activeBackground);
|
||||
}
|
||||
|
||||
#arduino-toolbar-panel {
|
||||
background: var(--theia-titleBar-activeBackground);
|
||||
color: var(--theia-titleBar-activeForeground);
|
||||
display: flex;
|
||||
min-height: var(--theia-private-menubar-height);
|
||||
border-bottom: 1px solid var(--theia-titleBar-border);
|
||||
}
|
||||
#arduino-toolbar-panel:window-inactive,
|
||||
#arduino-toolbar-panel:-moz-window-inactive {
|
||||
background: var(--theia-titleBar-inactiveBackground);
|
||||
color: var(--theia-titleBar-inactiveForeground);
|
||||
}
|
||||
|
||||
#arduino-toolbar-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
@@ -127,6 +114,10 @@
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.p-TabBar-toolbar .item > div {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
:root {
|
||||
--theia-private-menubar-height: 40px; /* set the topbar height */
|
||||
}
|
||||
|
@@ -1,4 +0,0 @@
|
||||
<svg width="8" height="7" viewBox="0 0 8 7" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.5 6.18999C2.415 6.18999 2.33 6.16999 2.25 6.12499C1.17 5.49999 0.5 4.33999 0.5 3.09499C0.5 1.84999 1.17 0.689992 2.25 0.0649925C2.49 -0.0700075 2.795 0.00999246 2.935 0.249992C3.07 0.489992 2.99 0.794992 2.75 0.934992C1.98 1.37499 1.5 2.20499 1.5 3.09499C1.5 3.98499 1.98 4.81499 2.75 5.25499C2.99 5.39499 3.07 5.69999 2.935 5.93999C2.84 6.09999 2.675 6.18999 2.5 6.18999Z" fill="#008184"/>
|
||||
<path d="M5.49993 6.18999C5.32493 6.18999 5.15993 6.09999 5.06493 5.93999C4.92493 5.69999 5.00993 5.39499 5.24993 5.25499C6.01993 4.81499 6.49993 3.98499 6.49993 3.09499C6.49993 2.20499 6.01993 1.37499 5.24993 0.934992C5.00993 0.794992 4.92993 0.489992 5.06493 0.249992C5.20493 0.00999246 5.50993 -0.0700075 5.74993 0.0649925C6.82993 0.689992 7.49993 1.84999 7.49993 3.09499C7.49993 4.33999 6.82993 5.49999 5.74993 6.12499C5.66993 6.16999 5.58493 6.18999 5.49993 6.18999Z" fill="#008184"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 992 B |
@@ -3,13 +3,6 @@
|
||||
mask: url('./sketchbook.svg');
|
||||
}
|
||||
|
||||
.sketch-folder-icon {
|
||||
background: url('./sketch-folder-icon.svg') center center no-repeat;
|
||||
background-position-x: 1px;
|
||||
width: var(--theia-icon-size);
|
||||
height: var(--theia-icon-size);
|
||||
}
|
||||
|
||||
.p-TabBar-tabIcon.sketchbook-tree-icon {
|
||||
background-color: var(--theia-foreground);
|
||||
-webkit-mask: url(./sketchbook-tree-icon.svg);
|
||||
|
@@ -7,8 +7,6 @@ import {
|
||||
SHELL_TABBAR_CONTEXT_MENU,
|
||||
TabBar,
|
||||
Widget,
|
||||
Layout,
|
||||
SplitPanel,
|
||||
} from '@theia/core/lib/browser';
|
||||
import {
|
||||
ConnectionStatus,
|
||||
@@ -19,11 +17,6 @@ import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { ToolbarAwareTabBar } from './tab-bars';
|
||||
|
||||
interface WidgetOptions
|
||||
extends Omit<TheiaApplicationShell.WidgetOptions, 'area'> {
|
||||
area?: TheiaApplicationShell.Area | 'toolbar';
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class ApplicationShell extends TheiaApplicationShell {
|
||||
@inject(MessageService)
|
||||
@@ -31,11 +24,10 @@ export class ApplicationShell extends TheiaApplicationShell {
|
||||
|
||||
@inject(ConnectionStatusService)
|
||||
private readonly connectionStatusService: ConnectionStatusService;
|
||||
private toolbarPanel: Panel;
|
||||
|
||||
override async addWidget(
|
||||
widget: Widget,
|
||||
options: Readonly<WidgetOptions> = {}
|
||||
options: Readonly<TheiaApplicationShell.WidgetOptions> = {}
|
||||
): Promise<void> {
|
||||
// By default, Theia open a widget **next** to the currently active in the target area.
|
||||
// Instead of this logic, we want to open the new widget after the last of the target area.
|
||||
@@ -45,12 +37,8 @@ export class ApplicationShell extends TheiaApplicationShell {
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (options.area === 'toolbar') {
|
||||
this.toolbarPanel.addWidget(widget);
|
||||
return;
|
||||
}
|
||||
const area = options.area || 'main';
|
||||
let ref: Widget | undefined = options.ref;
|
||||
const area: TheiaApplicationShell.Area = options.area || 'main';
|
||||
if (!ref && (area === 'main' || area === 'bottom')) {
|
||||
const tabBar = this.getTabBarFor(area);
|
||||
if (tabBar) {
|
||||
@@ -60,10 +48,7 @@ export class ApplicationShell extends TheiaApplicationShell {
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.addWidget(widget, {
|
||||
...(<TheiaApplicationShell.WidgetOptions>options),
|
||||
ref,
|
||||
});
|
||||
return super.addWidget(widget, { ...options, ref });
|
||||
}
|
||||
|
||||
override handleEvent(): boolean {
|
||||
@@ -71,46 +56,6 @@ export class ApplicationShell extends TheiaApplicationShell {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override initializeShell(): void {
|
||||
this.toolbarPanel = this.createToolbarPanel();
|
||||
super.initializeShell();
|
||||
}
|
||||
|
||||
private createToolbarPanel(): Panel {
|
||||
const toolbarPanel = new Panel();
|
||||
toolbarPanel.id = 'arduino-toolbar-panel';
|
||||
toolbarPanel.show();
|
||||
return toolbarPanel;
|
||||
}
|
||||
|
||||
protected override createLayout(): Layout {
|
||||
const bottomSplitLayout = this.createSplitLayout(
|
||||
[this.mainPanel, this.bottomPanel],
|
||||
[1, 0],
|
||||
{ orientation: 'vertical', spacing: 0 }
|
||||
);
|
||||
const panelForBottomArea = new SplitPanel({ layout: bottomSplitLayout });
|
||||
panelForBottomArea.id = 'theia-bottom-split-panel';
|
||||
|
||||
const leftRightSplitLayout = this.createSplitLayout(
|
||||
[
|
||||
this.leftPanelHandler.container,
|
||||
panelForBottomArea,
|
||||
this.rightPanelHandler.container,
|
||||
],
|
||||
[0, 1, 0],
|
||||
{ orientation: 'horizontal', spacing: 0 }
|
||||
);
|
||||
const panelForSideAreas = new SplitPanel({ layout: leftRightSplitLayout });
|
||||
panelForSideAreas.id = 'theia-left-right-split-panel';
|
||||
|
||||
return this.createBoxLayout(
|
||||
[this.topPanel, this.toolbarPanel, panelForSideAreas, this.statusBar],
|
||||
[0, 0, 1, 0],
|
||||
{ direction: 'top-to-bottom', spacing: 0 }
|
||||
);
|
||||
}
|
||||
|
||||
// Avoid hiding top panel as we use it for arduino toolbar
|
||||
protected override createTopPanel(): Panel {
|
||||
const topPanel = super.createTopPanel();
|
||||
|
@@ -1,106 +1,324 @@
|
||||
import {
|
||||
ApplicationConnectionStatusContribution as TheiaApplicationConnectionStatusContribution,
|
||||
ConnectionStatus,
|
||||
FrontendConnectionStatusService as TheiaFrontendConnectionStatusService,
|
||||
} from '@theia/core/lib/browser/connection-status-service';
|
||||
import type { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||
import { StatusBarAlignment } from '@theia/core/lib/browser/status-bar/status-bar';
|
||||
import { Disposable } from '@theia/core/lib/common/disposable';
|
||||
import { Emitter, Event } from '@theia/core/lib/common/event';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { MessageType } from '@theia/core/lib/common/message-service-protocol';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { Disposable } from '@theia/core/lib/common/disposable';
|
||||
import { StatusBarAlignment } from '@theia/core/lib/browser/status-bar/status-bar';
|
||||
import {
|
||||
FrontendConnectionStatusService as TheiaFrontendConnectionStatusService,
|
||||
ApplicationConnectionStatusContribution as TheiaApplicationConnectionStatusContribution,
|
||||
ConnectionStatus,
|
||||
} from '@theia/core/lib/browser/connection-status-service';
|
||||
import { NotificationManager } from '@theia/messages/lib/browser/notifications-manager';
|
||||
import { ArduinoDaemon } from '../../../common/protocol';
|
||||
import { assertUnreachable } from '../../../common/utils';
|
||||
import { CreateFeatures } from '../../create/create-features';
|
||||
import { NotificationCenter } from '../../notification-center';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import debounce = require('lodash.debounce');
|
||||
import isOnline = require('is-online');
|
||||
|
||||
@injectable()
|
||||
export class IsOnline implements FrontendApplicationContribution {
|
||||
private readonly onDidChangeOnlineEmitter = new Emitter<boolean>();
|
||||
private _online = false;
|
||||
private stopped = false;
|
||||
|
||||
onStart(): void {
|
||||
const checkOnline = async () => {
|
||||
if (!this.stopped) {
|
||||
try {
|
||||
const online = await isOnline();
|
||||
this.setOnline(online);
|
||||
} finally {
|
||||
window.setTimeout(() => checkOnline(), 6_000); // 6 seconds poll interval
|
||||
}
|
||||
}
|
||||
};
|
||||
checkOnline();
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.stopped = true;
|
||||
this.onDidChangeOnlineEmitter.dispose();
|
||||
}
|
||||
|
||||
get online(): boolean {
|
||||
return this._online;
|
||||
}
|
||||
|
||||
get onDidChangeOnline(): Event<boolean> {
|
||||
return this.onDidChangeOnlineEmitter.event;
|
||||
}
|
||||
|
||||
private setOnline(online: boolean) {
|
||||
const oldOnline = this._online;
|
||||
this._online = online;
|
||||
if (!this.stopped && this._online !== oldOnline) {
|
||||
this.onDidChangeOnlineEmitter.fire(this._online);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class DaemonPort implements FrontendApplicationContribution {
|
||||
@inject(ArduinoDaemon)
|
||||
private readonly daemon: ArduinoDaemon;
|
||||
@inject(NotificationCenter)
|
||||
private readonly notificationCenter: NotificationCenter;
|
||||
|
||||
private readonly onPortDidChangeEmitter = new Emitter<string | undefined>();
|
||||
private _port: string | undefined;
|
||||
|
||||
onStart(): void {
|
||||
this.daemon.tryGetPort().then(
|
||||
(port) => this.setPort(port),
|
||||
(reason) =>
|
||||
console.warn('Could not retrieve the CLI daemon port.', reason)
|
||||
);
|
||||
this.notificationCenter.onDaemonDidStart((port) => this.setPort(port));
|
||||
this.notificationCenter.onDaemonDidStop(() => this.setPort(undefined));
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.onPortDidChangeEmitter.dispose();
|
||||
}
|
||||
|
||||
get port(): string | undefined {
|
||||
return this._port;
|
||||
}
|
||||
|
||||
get onDidChangePort(): Event<string | undefined> {
|
||||
return this.onPortDidChangeEmitter.event;
|
||||
}
|
||||
|
||||
private setPort(port: string | undefined): void {
|
||||
const oldPort = this._port;
|
||||
this._port = port;
|
||||
if (this._port !== oldPort) {
|
||||
this.onPortDidChangeEmitter.fire(this._port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class FrontendConnectionStatusService extends TheiaFrontendConnectionStatusService {
|
||||
@inject(ArduinoDaemon)
|
||||
protected readonly daemon: ArduinoDaemon;
|
||||
|
||||
@inject(NotificationCenter)
|
||||
protected readonly notificationCenter: NotificationCenter;
|
||||
|
||||
protected connectedPort: string | undefined;
|
||||
@inject(DaemonPort)
|
||||
private readonly daemonPort: DaemonPort;
|
||||
@inject(IsOnline)
|
||||
private readonly isOnline: IsOnline;
|
||||
|
||||
@postConstruct()
|
||||
protected override async init(): Promise<void> {
|
||||
this.schedulePing();
|
||||
try {
|
||||
this.connectedPort = await this.daemon.tryGetPort();
|
||||
} catch {}
|
||||
this.notificationCenter.onDaemonDidStart(
|
||||
(port) => (this.connectedPort = port)
|
||||
);
|
||||
this.notificationCenter.onDaemonDidStop(
|
||||
() => (this.connectedPort = undefined)
|
||||
);
|
||||
const refresh = debounce(() => {
|
||||
this.updateStatus(!!this.connectedPort);
|
||||
this.updateStatus(Boolean(this.daemonPort.port) && this.isOnline.online);
|
||||
this.schedulePing();
|
||||
}, this.options.offlineTimeout - 10);
|
||||
this.wsConnectionProvider.onIncomingMessageActivity(() => refresh());
|
||||
}
|
||||
|
||||
protected override async performPingRequest(): Promise<void> {
|
||||
try {
|
||||
await this.pingService.ping();
|
||||
this.updateStatus(this.isOnline.online);
|
||||
} catch (e) {
|
||||
this.updateStatus(false);
|
||||
this.logger.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const connectionStatusStatusBar = 'connection-status';
|
||||
const theiaOffline = 'theia-mod-offline';
|
||||
|
||||
export type OfflineConnectionStatus =
|
||||
/**
|
||||
* There is no websocket connection between the frontend and the backend.
|
||||
*/
|
||||
| 'backend'
|
||||
/**
|
||||
* The CLI daemon port is not available. Could not establish the gRPC connection between the backend and the CLI.
|
||||
*/
|
||||
| 'daemon'
|
||||
/**
|
||||
* Cloud not connect to the Internet from the browser.
|
||||
*/
|
||||
| 'internet';
|
||||
|
||||
@injectable()
|
||||
export class ApplicationConnectionStatusContribution extends TheiaApplicationConnectionStatusContribution {
|
||||
@inject(ArduinoDaemon)
|
||||
protected readonly daemon: ArduinoDaemon;
|
||||
@inject(DaemonPort)
|
||||
private readonly daemonPort: DaemonPort;
|
||||
@inject(IsOnline)
|
||||
private readonly isOnline: IsOnline;
|
||||
@inject(MessageService)
|
||||
private readonly messageService: MessageService;
|
||||
@inject(NotificationManager)
|
||||
private readonly notificationManager: NotificationManager;
|
||||
@inject(CreateFeatures)
|
||||
private readonly createFeatures: CreateFeatures;
|
||||
|
||||
@inject(NotificationCenter)
|
||||
protected readonly notificationCenter: NotificationCenter;
|
||||
private readonly offlineStatusDidChangeEmitter = new Emitter<
|
||||
OfflineConnectionStatus | undefined
|
||||
>();
|
||||
private noInternetConnectionNotificationId: string | undefined;
|
||||
private _offlineStatus: OfflineConnectionStatus | undefined;
|
||||
|
||||
protected connectedPort: string | undefined;
|
||||
get offlineStatus(): OfflineConnectionStatus | undefined {
|
||||
return this._offlineStatus;
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
protected async init(): Promise<void> {
|
||||
try {
|
||||
this.connectedPort = await this.daemon.tryGetPort();
|
||||
} catch {}
|
||||
this.notificationCenter.onDaemonDidStart(
|
||||
(port) => (this.connectedPort = port)
|
||||
);
|
||||
this.notificationCenter.onDaemonDidStop(
|
||||
() => (this.connectedPort = undefined)
|
||||
);
|
||||
get onOfflineStatusDidChange(): Event<OfflineConnectionStatus | undefined> {
|
||||
return this.offlineStatusDidChangeEmitter.event;
|
||||
}
|
||||
|
||||
protected override onStateChange(state: ConnectionStatus): void {
|
||||
if (!this.connectedPort && state === ConnectionStatus.ONLINE) {
|
||||
if (
|
||||
(!Boolean(this.daemonPort.port) || !this.isOnline.online) &&
|
||||
state === ConnectionStatus.ONLINE
|
||||
) {
|
||||
return;
|
||||
}
|
||||
super.onStateChange(state);
|
||||
}
|
||||
|
||||
protected override handleOffline(): void {
|
||||
this.statusBar.setElement('connection-status', {
|
||||
const params = {
|
||||
port: this.daemonPort.port,
|
||||
online: this.isOnline.online,
|
||||
};
|
||||
this._offlineStatus = offlineConnectionStatusType(params);
|
||||
const { text, tooltip } = offlineMessage(params);
|
||||
this.statusBar.setElement(connectionStatusStatusBar, {
|
||||
alignment: StatusBarAlignment.LEFT,
|
||||
text: this.connectedPort
|
||||
? nls.localize('theia/core/offline', 'Offline')
|
||||
: '$(bolt) ' +
|
||||
nls.localize('theia/core/daemonOffline', 'CLI Daemon Offline'),
|
||||
tooltip: this.connectedPort
|
||||
? nls.localize(
|
||||
'theia/core/cannotConnectBackend',
|
||||
'Cannot connect to the backend.'
|
||||
)
|
||||
: nls.localize(
|
||||
'theia/core/cannotConnectDaemon',
|
||||
'Cannot connect to the CLI daemon.'
|
||||
),
|
||||
text,
|
||||
tooltip,
|
||||
priority: 5000,
|
||||
});
|
||||
this.toDisposeOnOnline.push(
|
||||
Disposable.create(() => this.statusBar.removeElement('connection-status'))
|
||||
);
|
||||
document.body.classList.add('theia-mod-offline');
|
||||
this.toDisposeOnOnline.push(
|
||||
document.body.classList.add(theiaOffline);
|
||||
this.toDisposeOnOnline.pushAll([
|
||||
Disposable.create(() =>
|
||||
document.body.classList.remove('theia-mod-offline')
|
||||
)
|
||||
this.statusBar.removeElement(connectionStatusStatusBar)
|
||||
),
|
||||
Disposable.create(() => document.body.classList.remove(theiaOffline)),
|
||||
Disposable.create(() => {
|
||||
this._offlineStatus = undefined;
|
||||
this.fireStatusDidChange();
|
||||
}),
|
||||
]);
|
||||
if (!this.isOnline.online) {
|
||||
const text = nls.localize(
|
||||
'arduino/connectionStatus/connectionLost',
|
||||
"Connection lost. Cloud sketch actions and updates won't be available."
|
||||
);
|
||||
this.noInternetConnectionNotificationId = this.notificationManager[
|
||||
'getMessageId'
|
||||
]({ text, type: MessageType.Warning });
|
||||
if (this.createFeatures.enabled) {
|
||||
this.messageService.warn(text);
|
||||
}
|
||||
this.toDisposeOnOnline.push(
|
||||
Disposable.create(() => this.clearNoInternetConnectionNotification())
|
||||
);
|
||||
}
|
||||
this.fireStatusDidChange();
|
||||
}
|
||||
|
||||
private clearNoInternetConnectionNotification(): void {
|
||||
if (this.noInternetConnectionNotificationId) {
|
||||
this.notificationManager.clear(this.noInternetConnectionNotificationId);
|
||||
this.noInternetConnectionNotificationId = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private fireStatusDidChange(): void {
|
||||
if (this.createFeatures.enabled) {
|
||||
return this.offlineStatusDidChangeEmitter.fire(this._offlineStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface OfflineMessageParams {
|
||||
readonly port: string | undefined;
|
||||
readonly online: boolean;
|
||||
}
|
||||
interface OfflineMessage {
|
||||
readonly text: string;
|
||||
readonly tooltip: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-API) exported for testing
|
||||
*
|
||||
* The precedence of the offline states are the following:
|
||||
* - No connection to the Theia backend,
|
||||
* - CLI daemon is offline, and
|
||||
* - There is no Internet connection.
|
||||
*/
|
||||
export function offlineMessage(params: OfflineMessageParams): OfflineMessage {
|
||||
const statusType = offlineConnectionStatusType(params);
|
||||
const text = getOfflineText(statusType);
|
||||
const tooltip = getOfflineTooltip(statusType);
|
||||
return { text, tooltip };
|
||||
}
|
||||
|
||||
function offlineConnectionStatusType(
|
||||
params: OfflineMessageParams
|
||||
): OfflineConnectionStatus {
|
||||
const { port, online } = params;
|
||||
if (port && online) {
|
||||
return 'backend';
|
||||
}
|
||||
if (!port) {
|
||||
return 'daemon';
|
||||
}
|
||||
return 'internet';
|
||||
}
|
||||
|
||||
export const backendOfflineText = nls.localize('theia/core/offline', 'Offline');
|
||||
export const daemonOfflineText = nls.localize(
|
||||
'theia/core/daemonOffline',
|
||||
'CLI Daemon Offline'
|
||||
);
|
||||
export const offlineText = nls.localize('theia/core/offlineText', 'Offline');
|
||||
export const backendOfflineTooltip = nls.localize(
|
||||
'theia/core/cannotConnectBackend',
|
||||
'Cannot connect to the backend.'
|
||||
);
|
||||
export const daemonOfflineTooltip = nls.localize(
|
||||
'theia/core/cannotConnectDaemon',
|
||||
'Cannot connect to the CLI daemon.'
|
||||
);
|
||||
export const offlineTooltip = offlineText;
|
||||
|
||||
function getOfflineText(statusType: OfflineConnectionStatus): string {
|
||||
switch (statusType) {
|
||||
case 'backend':
|
||||
return backendOfflineText;
|
||||
case 'daemon':
|
||||
return '$(bolt) ' + daemonOfflineText;
|
||||
case 'internet':
|
||||
return '$(alert) ' + offlineText;
|
||||
default:
|
||||
assertUnreachable(statusType);
|
||||
}
|
||||
}
|
||||
|
||||
function getOfflineTooltip(statusType: OfflineConnectionStatus): string {
|
||||
switch (statusType) {
|
||||
case 'backend':
|
||||
return backendOfflineTooltip;
|
||||
case 'daemon':
|
||||
return daemonOfflineTooltip;
|
||||
case 'internet':
|
||||
return offlineTooltip;
|
||||
default:
|
||||
assertUnreachable(statusType);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,91 @@
|
||||
import { SidebarBottomMenuWidget as TheiaSidebarBottomMenuWidget } from '@theia/core/lib/browser/shell/sidebar-bottom-menu-widget';
|
||||
import type { SidebarMenu } from '@theia/core/lib/browser/shell/sidebar-menu-widget';
|
||||
import type { MenuPath } from '@theia/core/lib/common/menu';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { accountMenu } from '../../contributions/account';
|
||||
import { CreateFeatures } from '../../create/create-features';
|
||||
import { ApplicationConnectionStatusContribution } from './connection-status-service';
|
||||
|
||||
@injectable()
|
||||
export class SidebarBottomMenuWidget extends TheiaSidebarBottomMenuWidget {
|
||||
@inject(CreateFeatures)
|
||||
private readonly createFeatures: CreateFeatures;
|
||||
@inject(ApplicationConnectionStatusContribution)
|
||||
private readonly connectionStatue: ApplicationConnectionStatusContribution;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.toDispose.pushAll([
|
||||
this.createFeatures.onDidChangeSession(() => this.update()),
|
||||
this.connectionStatue.onOfflineStatusDidChange(() => this.update()),
|
||||
]);
|
||||
}
|
||||
|
||||
protected override onClick(
|
||||
e: React.MouseEvent<HTMLElement, MouseEvent>,
|
||||
menuPath: MenuPath
|
||||
): void {
|
||||
const button = e.currentTarget.getBoundingClientRect();
|
||||
const options = {
|
||||
menuPath,
|
||||
includeAnchorArg: false,
|
||||
anchor: {
|
||||
x: button.left + button.width,
|
||||
// Bogus y coordinate?
|
||||
// https://github.com/eclipse-theia/theia/discussions/12170
|
||||
y: button.top,
|
||||
},
|
||||
showDisabled: true,
|
||||
};
|
||||
this.contextMenuRenderer.render(options);
|
||||
}
|
||||
|
||||
protected override render(): React.ReactNode {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{this.menus.map((menu) => this.renderMenu(menu))}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
private renderMenu(menu: SidebarMenu): React.ReactNode {
|
||||
// Removes the _Settings_ (cog) icon from the left sidebar
|
||||
if (menu.id === 'settings-menu') {
|
||||
return undefined;
|
||||
}
|
||||
const arduinoAccount = menu.id === accountMenu.id;
|
||||
const picture =
|
||||
arduinoAccount &&
|
||||
this.connectionStatue.offlineStatus !== 'internet' &&
|
||||
this.createFeatures.session?.account.picture;
|
||||
const className = typeof picture === 'string' ? undefined : menu.iconClass;
|
||||
return (
|
||||
<i
|
||||
key={menu.id}
|
||||
className={className}
|
||||
title={menu.title}
|
||||
onClick={(e) => this.onClick(e, menu.menuPath)}
|
||||
onMouseDown={this.onMouseDown}
|
||||
onMouseOut={this.onMouseOut}
|
||||
>
|
||||
{picture && (
|
||||
<div className="account-icon">
|
||||
<img
|
||||
src={picture}
|
||||
alt={nls.localize(
|
||||
'arduino/cloud/profilePicture',
|
||||
'Profile picture'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</i>
|
||||
);
|
||||
}
|
||||
}
|
@@ -23,6 +23,11 @@ export class TabBarRenderer extends TheiaTabBarRenderer {
|
||||
}
|
||||
|
||||
export class ToolbarAwareTabBar extends TheiaToolbarAwareTabBar {
|
||||
protected override async updateBreadcrumbs(): Promise<void> {
|
||||
// NOOP
|
||||
// IDE2 does not use breadcrumbs.
|
||||
}
|
||||
|
||||
private readonly doUpdateToolbar = debounce(() => super.updateToolbar(), 500);
|
||||
protected override updateToolbar(): void {
|
||||
// Unlike Theia, IDE2 debounces the toolbar updates with 500ms
|
||||
|
@@ -1,18 +1,28 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||
import { NavigatableWidget } from '@theia/core/lib/browser/navigatable-types';
|
||||
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
||||
import { Widget } from '@theia/core/lib/browser/widgets/widget';
|
||||
import { WindowTitleUpdater as TheiaWindowTitleUpdater } from '@theia/core/lib/browser/window/window-title-updater';
|
||||
import { ApplicationServer } from '@theia/core/lib/common/application-protocol';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
|
||||
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
||||
import { ConfigServiceClient } from '../../config/config-service-client';
|
||||
import { CreateFeatures } from '../../create/create-features';
|
||||
import {
|
||||
CurrentSketch,
|
||||
SketchesServiceClientImpl,
|
||||
} from '../../sketches-service-client-impl';
|
||||
|
||||
@injectable()
|
||||
export class WindowTitleUpdater extends TheiaWindowTitleUpdater {
|
||||
@@ -22,12 +32,22 @@ export class WindowTitleUpdater extends TheiaWindowTitleUpdater {
|
||||
private readonly applicationShell: ApplicationShell;
|
||||
@inject(WorkspaceService)
|
||||
private readonly workspaceService: WorkspaceService;
|
||||
|
||||
private _previousRepresentedFilename: string | undefined;
|
||||
@inject(SketchesServiceClientImpl)
|
||||
private readonly sketchesServiceClient: SketchesServiceClientImpl;
|
||||
@inject(ConfigServiceClient)
|
||||
private readonly configServiceClient: ConfigServiceClient;
|
||||
@inject(CreateFeatures)
|
||||
private readonly createFeatures: CreateFeatures;
|
||||
@inject(EditorManager)
|
||||
private readonly editorManager: EditorManager;
|
||||
|
||||
private readonly applicationName =
|
||||
FrontendApplicationConfigProvider.get().applicationName;
|
||||
private readonly toDispose = new DisposableCollection();
|
||||
|
||||
private previousRepresentedFilename: string | undefined;
|
||||
private applicationVersion: string | undefined;
|
||||
private hasCloudPrefix: boolean | undefined;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
@@ -43,6 +63,22 @@ export class WindowTitleUpdater extends TheiaWindowTitleUpdater {
|
||||
);
|
||||
}
|
||||
|
||||
override onStart(app: FrontendApplication): void {
|
||||
super.onStart(app);
|
||||
this.toDispose.pushAll([
|
||||
this.sketchesServiceClient.onCurrentSketchDidChange(() =>
|
||||
this.maybeSetCloudPrefix()
|
||||
),
|
||||
this.configServiceClient.onDidChangeDataDirUri(() =>
|
||||
this.maybeSetCloudPrefix()
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
protected override handleWidgetChange(widget?: Widget | undefined): void {
|
||||
if (isOSX) {
|
||||
this.maybeUpdateRepresentedFilename(widget);
|
||||
@@ -54,7 +90,7 @@ export class WindowTitleUpdater extends TheiaWindowTitleUpdater {
|
||||
|
||||
protected override updateTitleWidget(widget?: Widget | undefined): void {
|
||||
let activeEditorShort = '';
|
||||
const rootName = this.workspaceService.workspace?.name ?? '';
|
||||
let rootName = this.workspaceService.workspace?.name ?? '';
|
||||
let appName = `${this.applicationName}${
|
||||
this.applicationVersion ? ` ${this.applicationVersion}` : ''
|
||||
}`;
|
||||
@@ -69,6 +105,12 @@ export class WindowTitleUpdater extends TheiaWindowTitleUpdater {
|
||||
activeEditorShort = ` - ${base} `;
|
||||
}
|
||||
}
|
||||
if (this.hasCloudPrefix) {
|
||||
rootName = `[${nls.localize(
|
||||
'arduino/title/cloud',
|
||||
'Cloud'
|
||||
)}] ${rootName}`;
|
||||
}
|
||||
this.windowTitleService.update({ rootName, appName, activeEditorShort });
|
||||
}
|
||||
|
||||
@@ -77,10 +119,32 @@ export class WindowTitleUpdater extends TheiaWindowTitleUpdater {
|
||||
const { uri } = widget.editor;
|
||||
const filename = uri.path.toString();
|
||||
// Do not necessarily require the current window if not needed. It's a synchronous, blocking call.
|
||||
if (this._previousRepresentedFilename !== filename) {
|
||||
if (this.previousRepresentedFilename !== filename) {
|
||||
const currentWindow = remote.getCurrentWindow();
|
||||
currentWindow.setRepresentedFilename(uri.path.toString());
|
||||
this._previousRepresentedFilename = filename;
|
||||
this.previousRepresentedFilename = filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private maybeSetCloudPrefix(): void {
|
||||
if (typeof this.hasCloudPrefix === 'boolean') {
|
||||
return;
|
||||
}
|
||||
const sketch = this.sketchesServiceClient.tryGetCurrentSketch();
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return;
|
||||
}
|
||||
const dataDirUri = this.configServiceClient.tryGetDataDirUri();
|
||||
if (!dataDirUri) {
|
||||
return;
|
||||
}
|
||||
this.hasCloudPrefix = this.createFeatures.isCloud(sketch, dataDirUri);
|
||||
if (typeof this.hasCloudPrefix === 'boolean') {
|
||||
const editor =
|
||||
this.editorManager.activeEditor ?? this.editorManager.currentEditor;
|
||||
if (editor) {
|
||||
this.updateTitleWidget(editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { CancellationToken } from '@theia/core/lib/common/cancellation';
|
||||
import type {
|
||||
Message,
|
||||
ProgressMessage,
|
||||
ProgressUpdate,
|
||||
} from '@theia/core/lib/common/message-service-protocol';
|
||||
@@ -46,11 +45,4 @@ export class NotificationManager extends TheiaNotificationManager {
|
||||
}
|
||||
return Math.min((update.work.done / update.work.total) * 100, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* For `public` visibility.
|
||||
*/
|
||||
override getMessageId(message: Message): string {
|
||||
return super.getMessageId(message);
|
||||
}
|
||||
}
|
||||
|
@@ -19,8 +19,7 @@ export class ArduinoToolbarContainer extends Widget {
|
||||
this.toolbars = toolbars;
|
||||
}
|
||||
|
||||
override onAfterAttach(msg: Message): void {
|
||||
super.onAfterAttach(msg);
|
||||
override onAfterAttach(msg: Message) {
|
||||
for (const toolbar of this.toolbars) {
|
||||
Widget.attach(toolbar, this.node);
|
||||
}
|
||||
@@ -57,11 +56,9 @@ export class ArduinoToolbarContribution
|
||||
);
|
||||
}
|
||||
|
||||
onStart(app: FrontendApplication): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const options = <any>{
|
||||
area: 'toolbar',
|
||||
};
|
||||
app.shell.addWidget(this.arduinoToolbarContainer, options);
|
||||
onStart(app: FrontendApplication) {
|
||||
app.shell.addWidget(this.arduinoToolbarContainer, {
|
||||
area: 'top',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,88 @@
|
||||
import { TreeNode } from '@theia/core/lib/browser/tree';
|
||||
import { Command } from '@theia/core/lib/common/command';
|
||||
import { CloudSketchbookTreeModel } from './cloud-sketchbook-tree-model';
|
||||
|
||||
export namespace CloudSketchbookCommands {
|
||||
export interface Arg {
|
||||
model: CloudSketchbookTreeModel;
|
||||
node: TreeNode;
|
||||
event?: MouseEvent;
|
||||
}
|
||||
export namespace Arg {
|
||||
export function is(arg: unknown): arg is Arg {
|
||||
return (
|
||||
typeof arg === 'object' &&
|
||||
(<Arg>arg).model !== undefined &&
|
||||
(<Arg>arg).model instanceof CloudSketchbookTreeModel &&
|
||||
(<Arg>arg).node !== undefined &&
|
||||
TreeNode.is((<Arg>arg).node)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const TOGGLE_CLOUD_SKETCHBOOK = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'arduino-cloud-sketchbook--disable',
|
||||
label: 'Show/Hide Cloud Sketchbook',
|
||||
},
|
||||
'arduino/cloud/showHideSketchbook'
|
||||
);
|
||||
|
||||
export const PULL_SKETCH = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'arduino-cloud-sketchbook--pull-sketch',
|
||||
label: 'Pull Sketch',
|
||||
iconClass: 'fa fa-arduino-cloud-download',
|
||||
},
|
||||
'arduino/cloud/pullSketch'
|
||||
);
|
||||
|
||||
export const PUSH_SKETCH = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'arduino-cloud-sketchbook--push-sketch',
|
||||
label: 'Push Sketch',
|
||||
iconClass: 'fa fa-arduino-cloud-upload',
|
||||
},
|
||||
'arduino/cloud/pullSketch'
|
||||
);
|
||||
|
||||
export const PULL_SKETCH__TOOLBAR = {
|
||||
...PULL_SKETCH,
|
||||
id: `${PULL_SKETCH.id}-toolbar`,
|
||||
};
|
||||
|
||||
export const PUSH_SKETCH__TOOLBAR = {
|
||||
...PUSH_SKETCH,
|
||||
id: `${PUSH_SKETCH.id}-toolbar`,
|
||||
};
|
||||
|
||||
export const OPEN_IN_CLOUD_EDITOR = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'arduino-cloud-sketchbook--open-in-cloud-editor',
|
||||
label: 'Open in Cloud Editor',
|
||||
},
|
||||
'arduino/cloud/openInCloudEditor'
|
||||
);
|
||||
|
||||
export const OPEN_SKETCHBOOKSYNC_CONTEXT_MENU = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'arduino-sketchbook-sync--open-sketch-context-menu',
|
||||
label: 'Options...',
|
||||
iconClass: 'sketchbook-tree__opts',
|
||||
},
|
||||
'arduino/cloud/options'
|
||||
);
|
||||
|
||||
export const OPEN_SKETCH_SHARE_DIALOG = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'arduino-cloud-sketchbook--share-modal',
|
||||
label: 'Share...',
|
||||
},
|
||||
'arduino/cloud/share'
|
||||
);
|
||||
|
||||
export const OPEN_PROFILE_CONTEXT_MENU: Command = {
|
||||
id: 'arduino-cloud-sketchbook--open-profile-menu',
|
||||
label: 'Contextual menu',
|
||||
};
|
||||
}
|
@@ -5,7 +5,7 @@ import {
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { UserStatus } from './cloud-user-status';
|
||||
import { CloudStatus } from './cloud-status';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { CloudSketchbookTreeWidget } from './cloud-sketchbook-tree-widget';
|
||||
import { AuthenticationClientService } from '../../auth/authentication-client-service';
|
||||
@@ -13,6 +13,7 @@ import { CloudSketchbookTreeModel } from './cloud-sketchbook-tree-model';
|
||||
import { BaseSketchbookCompositeWidget } from '../sketchbook/sketchbook-composite-widget';
|
||||
import { CreateNew } from '../sketchbook/create-new';
|
||||
import { AuthenticationSession } from '../../../node/auth/types';
|
||||
import { ApplicationConnectionStatusContribution } from '../../theia/core/connection-status-service';
|
||||
|
||||
@injectable()
|
||||
export class CloudSketchbookCompositeWidget extends BaseSketchbookCompositeWidget<CloudSketchbookTreeWidget> {
|
||||
@@ -20,6 +21,9 @@ export class CloudSketchbookCompositeWidget extends BaseSketchbookCompositeWidge
|
||||
private readonly authenticationService: AuthenticationClientService;
|
||||
@inject(CloudSketchbookTreeWidget)
|
||||
private readonly cloudSketchbookTreeWidget: CloudSketchbookTreeWidget;
|
||||
@inject(ApplicationConnectionStatusContribution)
|
||||
private readonly connectionStatus: ApplicationConnectionStatusContribution;
|
||||
|
||||
private _session: AuthenticationSession | undefined;
|
||||
|
||||
constructor() {
|
||||
@@ -61,11 +65,12 @@ export class CloudSketchbookCompositeWidget extends BaseSketchbookCompositeWidge
|
||||
onClick={this.onDidClickCreateNew}
|
||||
/>
|
||||
)}
|
||||
<UserStatus
|
||||
<CloudStatus
|
||||
model={
|
||||
this.cloudSketchbookTreeWidget.model as CloudSketchbookTreeModel
|
||||
}
|
||||
authenticationService={this.authenticationService}
|
||||
connectionStatus={this.connectionStatus}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@@ -1,156 +1,94 @@
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { TreeNode } from '@theia/core/lib/browser/tree';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { Command, CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import {
|
||||
ContextMenuRenderer,
|
||||
RenderContextMenuOptions,
|
||||
} from '@theia/core/lib/browser';
|
||||
} from '@theia/core/lib/browser/context-menu-renderer';
|
||||
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||
import {
|
||||
PreferenceScope,
|
||||
PreferenceService,
|
||||
} from '@theia/core/lib/browser/preferences/preference-service';
|
||||
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { Emitter, Event } from '@theia/core/lib/common/event';
|
||||
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||
import { CloudSketchbookTree } from './cloud-sketchbook-tree';
|
||||
import { CloudSketchbookTreeModel } from './cloud-sketchbook-tree-model';
|
||||
import { CloudUserCommands } from '../../auth/cloud-user-commands';
|
||||
import { ShareSketchDialog } from '../../dialogs/cloud-share-sketch-dialog';
|
||||
import { CreateApi } from '../../create/create-api';
|
||||
import {
|
||||
PreferenceService,
|
||||
PreferenceScope,
|
||||
} from '@theia/core/lib/browser/preferences/preference-service';
|
||||
import { ArduinoMenus, PlaceholderMenuNode } from '../../menu/arduino-menus';
|
||||
import { SketchbookCommands } from '../sketchbook/sketchbook-commands';
|
||||
import {
|
||||
CurrentSketch,
|
||||
SketchesServiceClientImpl,
|
||||
} from '../../sketches-service-client-impl';
|
||||
import { Contribution } from '../../contributions/contribution';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { ArduinoPreferences } from '../../arduino-preferences';
|
||||
import { MainMenuManager } from '../../../common/main-menu-manager';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { ConfigServiceClient } from '../../config/config-service-client';
|
||||
import { CloudSketchContribution } from '../../contributions/cloud-contribution';
|
||||
import {
|
||||
Sketch,
|
||||
TabBarToolbarRegistry,
|
||||
} from '../../contributions/contribution';
|
||||
import { ShareSketchDialog } from '../../dialogs/cloud-share-sketch-dialog';
|
||||
import { ArduinoMenus, PlaceholderMenuNode } from '../../menu/arduino-menus';
|
||||
import { CurrentSketch } from '../../sketches-service-client-impl';
|
||||
import { ApplicationConnectionStatusContribution } from '../../theia/core/connection-status-service';
|
||||
import { SketchbookCommands } from '../sketchbook/sketchbook-commands';
|
||||
import { CloudSketchbookCommands } from './cloud-sketchbook-commands';
|
||||
import { CloudSketchbookTree } from './cloud-sketchbook-tree';
|
||||
import { CreateUri } from '../../create/create-uri';
|
||||
|
||||
export const SKETCHBOOKSYNC__CONTEXT = ['arduino-sketchbook-sync--context'];
|
||||
const SKETCHBOOKSYNC__CONTEXT = ['arduino-sketchbook-sync--context'];
|
||||
|
||||
// `Open Folder`, `Open in New Window`
|
||||
export const SKETCHBOOKSYNC__CONTEXT__MAIN_GROUP = [
|
||||
const SKETCHBOOKSYNC__CONTEXT__MAIN_GROUP = [
|
||||
...SKETCHBOOKSYNC__CONTEXT,
|
||||
'0_main',
|
||||
];
|
||||
|
||||
export const CLOUD_USER__CONTEXT = ['arduino-cloud-user--context'];
|
||||
export const CLOUD_USER__CONTEXT__USERNAME = [
|
||||
...CLOUD_USER__CONTEXT,
|
||||
'0_username',
|
||||
];
|
||||
export const CLOUD_USER__CONTEXT__MAIN_GROUP = [
|
||||
...CLOUD_USER__CONTEXT,
|
||||
'1_main',
|
||||
];
|
||||
|
||||
export namespace CloudSketchbookCommands {
|
||||
export interface Arg {
|
||||
model: CloudSketchbookTreeModel;
|
||||
node: TreeNode;
|
||||
event?: MouseEvent;
|
||||
}
|
||||
export namespace Arg {
|
||||
export function is(arg: Partial<Arg> | undefined): arg is Arg {
|
||||
return (
|
||||
!!arg && !!arg.node && arg.model instanceof CloudSketchbookTreeModel
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const TOGGLE_CLOUD_SKETCHBOOK = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'arduino-cloud-sketchbook--disable',
|
||||
label: 'Show/Hide Cloud Sketchbook',
|
||||
},
|
||||
'arduino/cloud/showHideSketchbook'
|
||||
);
|
||||
|
||||
export const PULL_SKETCH = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'arduino-cloud-sketchbook--pull-sketch',
|
||||
label: 'Pull Sketch',
|
||||
iconClass: 'pull-sketch-icon',
|
||||
},
|
||||
'arduino/cloud/pullSketch'
|
||||
);
|
||||
|
||||
export const PUSH_SKETCH = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'arduino-cloud-sketchbook--push-sketch',
|
||||
label: 'Push Sketch',
|
||||
iconClass: 'push-sketch-icon',
|
||||
},
|
||||
'arduino/cloud/pullSketch'
|
||||
);
|
||||
|
||||
export const OPEN_IN_CLOUD_EDITOR = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'arduino-cloud-sketchbook--open-in-cloud-editor',
|
||||
label: 'Open in Cloud Editor',
|
||||
},
|
||||
'arduino/cloud/openInCloudEditor'
|
||||
);
|
||||
|
||||
export const OPEN_SKETCHBOOKSYNC_CONTEXT_MENU = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'arduino-sketchbook-sync--open-sketch-context-menu',
|
||||
label: 'Options...',
|
||||
iconClass: 'sketchbook-tree__opts',
|
||||
},
|
||||
'arduino/cloud/options'
|
||||
);
|
||||
|
||||
export const OPEN_SKETCH_SHARE_DIALOG = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'arduino-cloud-sketchbook--share-modal',
|
||||
label: 'Share...',
|
||||
},
|
||||
'arduino/cloud/share'
|
||||
);
|
||||
|
||||
export const OPEN_PROFILE_CONTEXT_MENU: Command = {
|
||||
id: 'arduino-cloud-sketchbook--open-profile-menu',
|
||||
label: 'Contextual menu',
|
||||
};
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class CloudSketchbookContribution extends Contribution {
|
||||
@inject(FileService)
|
||||
protected readonly fileService: FileService;
|
||||
|
||||
export class CloudSketchbookContribution extends CloudSketchContribution {
|
||||
@inject(ContextMenuRenderer)
|
||||
protected readonly contextMenuRenderer: ContextMenuRenderer;
|
||||
|
||||
private readonly contextMenuRenderer: ContextMenuRenderer;
|
||||
@inject(MenuModelRegistry)
|
||||
protected readonly menuRegistry: MenuModelRegistry;
|
||||
|
||||
@inject(SketchesServiceClientImpl)
|
||||
protected readonly sketchServiceClient: SketchesServiceClientImpl;
|
||||
|
||||
private readonly menuRegistry: MenuModelRegistry;
|
||||
@inject(CommandRegistry)
|
||||
private readonly commandRegistry: CommandRegistry;
|
||||
@inject(WindowService)
|
||||
protected readonly windowService: WindowService;
|
||||
|
||||
@inject(CreateApi)
|
||||
protected readonly createApi: CreateApi;
|
||||
|
||||
private readonly windowService: WindowService;
|
||||
@inject(ArduinoPreferences)
|
||||
protected readonly arduinoPreferences: ArduinoPreferences;
|
||||
|
||||
private readonly arduinoPreferences: ArduinoPreferences;
|
||||
@inject(PreferenceService)
|
||||
protected readonly preferenceService: PreferenceService;
|
||||
private readonly preferenceService: PreferenceService;
|
||||
@inject(ConfigServiceClient)
|
||||
private readonly configServiceClient: ConfigServiceClient;
|
||||
@inject(ApplicationConnectionStatusContribution)
|
||||
private readonly connectionStatus: ApplicationConnectionStatusContribution;
|
||||
|
||||
@inject(MainMenuManager)
|
||||
protected readonly mainMenuManager: MainMenuManager;
|
||||
private readonly onDidChangeToolbarEmitter = new Emitter<void>();
|
||||
private readonly toDisposeBeforeNewContextMenu = new DisposableCollection();
|
||||
private readonly toDisposeOnStop = new DisposableCollection(
|
||||
this.onDidChangeToolbarEmitter,
|
||||
this.toDisposeBeforeNewContextMenu
|
||||
);
|
||||
private shell: ApplicationShell | undefined;
|
||||
|
||||
protected readonly toDisposeBeforeNewContextMenu = new DisposableCollection();
|
||||
override onStart(app: FrontendApplication): void {
|
||||
this.shell = app.shell;
|
||||
this.toDisposeOnStop.pushAll([
|
||||
this.connectionStatus.onOfflineStatusDidChange((offlineStatus) => {
|
||||
if (!offlineStatus || offlineStatus === 'internet') {
|
||||
this.fireToolbarChange();
|
||||
}
|
||||
}),
|
||||
this.createFeatures.onDidChangeSession(() => this.fireToolbarChange()),
|
||||
this.createFeatures.onDidChangeEnabled(() => this.fireToolbarChange()),
|
||||
this.createFeatures.onDidChangeCloudSketchState(() =>
|
||||
this.fireToolbarChange()
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.toDisposeOnStop.dispose();
|
||||
}
|
||||
|
||||
override registerMenus(menus: MenuModelRegistry): void {
|
||||
menus.registerMenuAction(ArduinoMenus.FILE__ADVANCED_SUBMENU, {
|
||||
@@ -160,6 +98,23 @@ export class CloudSketchbookContribution extends Contribution {
|
||||
});
|
||||
}
|
||||
|
||||
override registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
registry.registerItem({
|
||||
id: CloudSketchbookCommands.PULL_SKETCH__TOOLBAR.id,
|
||||
command: CloudSketchbookCommands.PULL_SKETCH__TOOLBAR.id,
|
||||
tooltip: CloudSketchbookCommands.PULL_SKETCH__TOOLBAR.label,
|
||||
priority: -2,
|
||||
onDidChange: this.onDidChangeToolbar,
|
||||
});
|
||||
registry.registerItem({
|
||||
id: CloudSketchbookCommands.PUSH_SKETCH__TOOLBAR.id,
|
||||
command: CloudSketchbookCommands.PUSH_SKETCH__TOOLBAR.id,
|
||||
tooltip: CloudSketchbookCommands.PUSH_SKETCH__TOOLBAR.label,
|
||||
priority: -1,
|
||||
onDidChange: this.onDidChangeToolbar,
|
||||
});
|
||||
}
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(CloudSketchbookCommands.TOGGLE_CLOUD_SKETCHBOOK, {
|
||||
execute: () => {
|
||||
@@ -169,32 +124,41 @@ export class CloudSketchbookContribution extends Contribution {
|
||||
PreferenceScope.User
|
||||
);
|
||||
},
|
||||
isEnabled: () => true,
|
||||
isVisible: () => true,
|
||||
});
|
||||
|
||||
registry.registerCommand(CloudSketchbookCommands.PULL_SKETCH, {
|
||||
execute: (arg) => arg.model.sketchbookTree().pull(arg),
|
||||
isEnabled: (arg) =>
|
||||
CloudSketchbookCommands.Arg.is(arg) &&
|
||||
CloudSketchbookTree.CloudSketchDirNode.is(arg.node),
|
||||
isVisible: (arg) =>
|
||||
CloudSketchbookCommands.Arg.is(arg) &&
|
||||
CloudSketchbookTree.CloudSketchDirNode.is(arg.node),
|
||||
isEnabled: (arg) => this.isCloudSketchDirNodeCommandArg(arg),
|
||||
isVisible: (arg) => this.isCloudSketchDirNodeCommandArg(arg),
|
||||
});
|
||||
|
||||
registry.registerCommand(CloudSketchbookCommands.PUSH_SKETCH, {
|
||||
execute: (arg) => arg.model.sketchbookTree().push(arg.node),
|
||||
isEnabled: (arg) =>
|
||||
CloudSketchbookCommands.Arg.is(arg) &&
|
||||
CloudSketchbookTree.CloudSketchDirNode.is(arg.node) &&
|
||||
this.isCloudSketchDirNodeCommandArg(arg) &&
|
||||
CloudSketchbookTree.CloudSketchTreeNode.isSynced(arg.node),
|
||||
isVisible: (arg) =>
|
||||
CloudSketchbookCommands.Arg.is(arg) &&
|
||||
CloudSketchbookTree.CloudSketchDirNode.is(arg.node) &&
|
||||
this.isCloudSketchDirNodeCommandArg(arg) &&
|
||||
CloudSketchbookTree.CloudSketchTreeNode.isSynced(arg.node),
|
||||
});
|
||||
|
||||
registry.registerCommand(CloudSketchbookCommands.PUSH_SKETCH__TOOLBAR, {
|
||||
execute: () =>
|
||||
this.executeDelegateWithCurrentSketch(
|
||||
CloudSketchbookCommands.PUSH_SKETCH.id
|
||||
),
|
||||
isEnabled: (arg) => this.isEnabledCloudSketchToolbar(arg),
|
||||
isVisible: (arg) => this.isVisibleCloudSketchToolbar(arg),
|
||||
});
|
||||
registry.registerCommand(CloudSketchbookCommands.PULL_SKETCH__TOOLBAR, {
|
||||
execute: () =>
|
||||
this.executeDelegateWithCurrentSketch(
|
||||
CloudSketchbookCommands.PULL_SKETCH.id
|
||||
),
|
||||
isEnabled: (arg) => this.isEnabledCloudSketchToolbar(arg),
|
||||
isVisible: (arg) => this.isVisibleCloudSketchToolbar(arg),
|
||||
});
|
||||
|
||||
registry.registerCommand(CloudSketchbookCommands.OPEN_IN_CLOUD_EDITOR, {
|
||||
execute: (arg) => {
|
||||
this.windowService.openNewWindow(
|
||||
@@ -202,12 +166,8 @@ export class CloudSketchbookContribution extends Contribution {
|
||||
{ external: true }
|
||||
);
|
||||
},
|
||||
isEnabled: (arg) =>
|
||||
CloudSketchbookCommands.Arg.is(arg) &&
|
||||
CloudSketchbookTree.CloudSketchDirNode.is(arg.node),
|
||||
isVisible: (arg) =>
|
||||
CloudSketchbookCommands.Arg.is(arg) &&
|
||||
CloudSketchbookTree.CloudSketchDirNode.is(arg.node),
|
||||
isEnabled: (arg) => this.isCloudSketchDirNodeCommandArg(arg),
|
||||
isVisible: (arg) => this.isCloudSketchDirNodeCommandArg(arg),
|
||||
});
|
||||
|
||||
registry.registerCommand(CloudSketchbookCommands.OPEN_SKETCH_SHARE_DIALOG, {
|
||||
@@ -218,12 +178,8 @@ export class CloudSketchbookContribution extends Contribution {
|
||||
createApi: this.createApi,
|
||||
}).open();
|
||||
},
|
||||
isEnabled: (arg) =>
|
||||
CloudSketchbookCommands.Arg.is(arg) &&
|
||||
CloudSketchbookTree.CloudSketchDirNode.is(arg.node),
|
||||
isVisible: (arg) =>
|
||||
CloudSketchbookCommands.Arg.is(arg) &&
|
||||
CloudSketchbookTree.CloudSketchDirNode.is(arg.node),
|
||||
isEnabled: (arg) => this.isCloudSketchDirNodeCommandArg(arg),
|
||||
isVisible: (arg) => this.isCloudSketchDirNodeCommandArg(arg),
|
||||
});
|
||||
|
||||
registry.registerCommand(
|
||||
@@ -327,53 +283,118 @@ export class CloudSketchbookContribution extends Contribution {
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
registry.registerCommand(CloudUserCommands.OPEN_PROFILE_CONTEXT_MENU, {
|
||||
execute: async (arg) => {
|
||||
this.toDisposeBeforeNewContextMenu.dispose();
|
||||
const container = arg.event.target;
|
||||
if (!container) {
|
||||
private get currentCloudSketch(): Sketch | undefined {
|
||||
const currentSketch = this.sketchServiceClient.tryGetCurrentSketch();
|
||||
// could not load sketch via CLI
|
||||
if (!CurrentSketch.isValid(currentSketch)) {
|
||||
return undefined;
|
||||
}
|
||||
// cannot determine if the sketch is in the cloud cache folder
|
||||
const dataDirUri = this.configServiceClient.tryGetDataDirUri();
|
||||
if (!dataDirUri) {
|
||||
return undefined;
|
||||
}
|
||||
// sketch is not in the cache folder
|
||||
if (!this.createFeatures.isCloud(currentSketch, dataDirUri)) {
|
||||
return undefined;
|
||||
}
|
||||
return currentSketch;
|
||||
}
|
||||
|
||||
private isVisibleCloudSketchToolbar(arg: unknown): boolean {
|
||||
// cloud preference is disabled
|
||||
if (!this.createFeatures.enabled) {
|
||||
return false;
|
||||
}
|
||||
if (!this.currentCloudSketch) {
|
||||
return false;
|
||||
}
|
||||
if (arg instanceof Widget) {
|
||||
return !!this.shell && this.shell.getWidgets('main').indexOf(arg) !== -1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private isEnabledCloudSketchToolbar(arg: unknown): boolean {
|
||||
if (!this.isVisibleCloudSketchToolbar(arg)) {
|
||||
return false;
|
||||
}
|
||||
// not logged in
|
||||
if (!this.createFeatures.session) {
|
||||
return false;
|
||||
}
|
||||
// no Internet connection
|
||||
if (this.connectionStatus.offlineStatus === 'internet') {
|
||||
return false;
|
||||
}
|
||||
// no pull/push context for the current cloud sketch
|
||||
const sketch = this.currentCloudSketch;
|
||||
if (sketch) {
|
||||
const cloudUri = this.createFeatures.cloudUri(sketch);
|
||||
if (cloudUri) {
|
||||
return !this.createFeatures.cloudSketchState(
|
||||
CreateUri.toUri(cloudUri.path.toString())
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private isCloudSketchDirNodeCommandArg(
|
||||
arg: unknown
|
||||
): arg is CloudSketchbookCommands.Arg & {
|
||||
node: CloudSketchbookTree.CloudSketchDirNode;
|
||||
} {
|
||||
return (
|
||||
CloudSketchbookCommands.Arg.is(arg) &&
|
||||
CloudSketchbookTree.CloudSketchDirNode.is(arg.node) &&
|
||||
!this.createFeatures.cloudSketchState(arg.node.remoteUri)
|
||||
);
|
||||
}
|
||||
|
||||
private async commandArgFromCurrentSketch(): Promise<
|
||||
CloudSketchbookCommands.Arg | undefined
|
||||
> {
|
||||
const sketch = this.currentCloudSketch;
|
||||
if (!sketch) {
|
||||
return undefined;
|
||||
}
|
||||
const model = await this.treeModel();
|
||||
if (!model) {
|
||||
return undefined;
|
||||
}
|
||||
const cloudUri = this.createFeatures.cloudUri(sketch);
|
||||
if (!cloudUri) {
|
||||
return undefined;
|
||||
}
|
||||
const posixPath = cloudUri.path.toString();
|
||||
const node = model.getNode(posixPath);
|
||||
if (CloudSketchbookTree.CloudSketchDirNode.is(node)) {
|
||||
return { model, node };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private async executeDelegateWithCurrentSketch(id: string): Promise<unknown> {
|
||||
const arg = await this.commandArgFromCurrentSketch();
|
||||
if (!arg) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.menuRegistry.registerMenuAction(CLOUD_USER__CONTEXT__MAIN_GROUP, {
|
||||
commandId: CloudUserCommands.LOGOUT.id,
|
||||
label: CloudUserCommands.LOGOUT.label,
|
||||
});
|
||||
this.toDisposeBeforeNewContextMenu.push(
|
||||
Disposable.create(() =>
|
||||
this.menuRegistry.unregisterMenuAction(CloudUserCommands.LOGOUT)
|
||||
)
|
||||
if (!this.commandRegistry.getActiveHandler(id, arg)) {
|
||||
throw new Error(
|
||||
`No active handler was available for the delegate command: ${id}. Cloud sketch tree node: ${arg.node.id}`
|
||||
);
|
||||
}
|
||||
return this.commandRegistry.executeCommand(id, arg);
|
||||
}
|
||||
|
||||
const placeholder = new PlaceholderMenuNode(
|
||||
CLOUD_USER__CONTEXT__USERNAME,
|
||||
arg.username
|
||||
);
|
||||
this.menuRegistry.registerMenuNode(
|
||||
CLOUD_USER__CONTEXT__USERNAME,
|
||||
placeholder
|
||||
);
|
||||
this.toDisposeBeforeNewContextMenu.push(
|
||||
Disposable.create(() =>
|
||||
this.menuRegistry.unregisterMenuNode(placeholder.id)
|
||||
)
|
||||
);
|
||||
private fireToolbarChange(): void {
|
||||
this.onDidChangeToolbarEmitter.fire();
|
||||
}
|
||||
|
||||
const options: RenderContextMenuOptions = {
|
||||
menuPath: CLOUD_USER__CONTEXT,
|
||||
anchor: {
|
||||
x: container.getBoundingClientRect().left,
|
||||
y:
|
||||
container.getBoundingClientRect().top -
|
||||
3.5 * container.offsetHeight,
|
||||
},
|
||||
args: [arg],
|
||||
};
|
||||
this.contextMenuRenderer.render(options);
|
||||
},
|
||||
});
|
||||
|
||||
this.registerMenus(this.menuRegistry);
|
||||
private get onDidChangeToolbar(): Event<void> {
|
||||
return this.onDidChangeToolbarEmitter.event;
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ import URI from '@theia/core/lib/common/uri';
|
||||
import { Create } from '../../create/typings';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { ApplicationConnectionStatusContribution } from '../../theia/core/connection-status-service';
|
||||
|
||||
function sketchBaseDir(sketch: Create.Sketch): FileStat {
|
||||
// extract the sketch path
|
||||
@@ -63,15 +64,22 @@ export class CloudSketchbookTreeModel extends SketchbookTreeModel {
|
||||
private readonly authenticationService: AuthenticationClientService;
|
||||
@inject(LocalCacheFsProvider)
|
||||
private readonly localCacheFsProvider: LocalCacheFsProvider;
|
||||
@inject(ApplicationConnectionStatusContribution)
|
||||
private readonly connectionStatus: ApplicationConnectionStatusContribution;
|
||||
|
||||
private _localCacheFsProviderReady: Deferred<void> | undefined;
|
||||
|
||||
@postConstruct()
|
||||
protected override init(): void {
|
||||
super.init();
|
||||
this.toDispose.push(
|
||||
this.authenticationService.onSessionDidChange(() => this.updateRoot())
|
||||
);
|
||||
this.toDispose.pushAll([
|
||||
this.authenticationService.onSessionDidChange(() => this.updateRoot()),
|
||||
this.connectionStatus.onOfflineStatusDidChange((offlineStatus) => {
|
||||
if (!offlineStatus) {
|
||||
this.updateRoot();
|
||||
}
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
override *getNodesByUri(uri: URI): IterableIterator<TreeNode> {
|
||||
|
@@ -5,16 +5,17 @@ import { CloudSketchbookTreeModel } from './cloud-sketchbook-tree-model';
|
||||
import { AuthenticationClientService } from '../../auth/authentication-client-service';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { CloudSketchbookTree } from './cloud-sketchbook-tree';
|
||||
import { CloudUserCommands } from '../../auth/cloud-user-commands';
|
||||
import {
|
||||
CloudUserCommands,
|
||||
LEARN_MORE_URL,
|
||||
} from '../../auth/cloud-user-commands';
|
||||
import { NodeProps } from '@theia/core/lib/browser/tree/tree-widget';
|
||||
import { TreeNode } from '@theia/core/lib/browser/tree';
|
||||
import { CompositeTreeNode } from '@theia/core/lib/browser';
|
||||
import { shell } from '@theia/core/electron-shared/@electron/remote';
|
||||
import { SketchbookTreeWidget } from '../sketchbook/sketchbook-tree-widget';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
const LEARN_MORE_URL =
|
||||
'https://docs.arduino.cc/software/ide-v2/tutorials/ide-v2-cloud-sketch-sync';
|
||||
import { ApplicationConnectionStatusContribution } from '../../theia/core/connection-status-service';
|
||||
|
||||
@injectable()
|
||||
export class CloudSketchbookTreeWidget extends SketchbookTreeWidget {
|
||||
@@ -27,6 +28,9 @@ export class CloudSketchbookTreeWidget extends SketchbookTreeWidget {
|
||||
@inject(CloudSketchbookTree)
|
||||
protected readonly cloudSketchbookTree: CloudSketchbookTree;
|
||||
|
||||
@inject(ApplicationConnectionStatusContribution)
|
||||
private readonly connectionStatus: ApplicationConnectionStatusContribution;
|
||||
|
||||
protected override renderTree(model: TreeModel): React.ReactNode {
|
||||
if (this.shouldShowWelcomeView()) return this.renderViewWelcome();
|
||||
if (this.shouldShowEmptyView()) return this.renderEmptyView();
|
||||
@@ -91,10 +95,33 @@ export class CloudSketchbookTreeWidget extends SketchbookTreeWidget {
|
||||
return classNames;
|
||||
}
|
||||
|
||||
protected override renderIcon(
|
||||
node: TreeNode,
|
||||
props: NodeProps
|
||||
): React.ReactNode {
|
||||
if (CloudSketchbookTree.CloudSketchDirNode.is(node)) {
|
||||
const synced = CloudSketchbookTree.CloudSketchTreeNode.isSynced(node);
|
||||
const offline = this.connectionStatus.offlineStatus === 'internet';
|
||||
const icon = `fa fa-arduino-cloud${synced ? '-filled' : ''}${
|
||||
offline ? '-offline' : ''
|
||||
}`;
|
||||
return (
|
||||
<div
|
||||
className={`theia-file-icons-js file-icon${
|
||||
!synced && offline ? ' not-in-sync-offline' : ''
|
||||
}`}
|
||||
>
|
||||
<div className={icon} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return super.renderIcon(node, props);
|
||||
}
|
||||
|
||||
protected override renderInlineCommands(node: any): React.ReactNode {
|
||||
if (CloudSketchbookTree.CloudSketchDirNode.is(node) && node.commands) {
|
||||
return Array.from(new Set(node.commands)).map((command) =>
|
||||
this.renderInlineCommand(command.id, node, {
|
||||
this.renderInlineCommand(command, node, {
|
||||
username: this.authenticationService.session?.account?.label,
|
||||
})
|
||||
);
|
||||
|
@@ -22,15 +22,22 @@ import {
|
||||
LocalCacheFsProvider,
|
||||
LocalCacheUri,
|
||||
} from '../../local-cache/local-cache-fs-provider';
|
||||
import { CloudSketchbookCommands } from './cloud-sketchbook-contributions';
|
||||
import { CloudSketchbookCommands } from './cloud-sketchbook-commands';
|
||||
import { DoNotAskAgainConfirmDialog } from '../../dialogs/do-not-ask-again-dialog';
|
||||
import { SketchbookTree } from '../sketchbook/sketchbook-tree';
|
||||
import { firstToUpperCase } from '../../../common/utils';
|
||||
import { assertUnreachable } from '../../../common/utils';
|
||||
import { FileStat } from '@theia/filesystem/lib/common/files';
|
||||
import { WorkspaceNode } from '@theia/navigator/lib/browser/navigator-tree';
|
||||
import { posix, splitSketchPath } from '../../create/create-paths';
|
||||
import { Create } from '../../create/typings';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { ApplicationConnectionStatusContribution } from '../../theia/core/connection-status-service';
|
||||
import { ExecuteWithProgress } from '../../../common/protocol/progressible';
|
||||
import {
|
||||
pullingSketch,
|
||||
pushingSketch,
|
||||
} from '../../contributions/cloud-contribution';
|
||||
import { CloudSketchState, CreateFeatures } from '../../create/create-features';
|
||||
|
||||
const MESSAGE_TIMEOUT = 5 * 1000;
|
||||
const deepmerge = require('deepmerge').default;
|
||||
@@ -54,6 +61,19 @@ export class CloudSketchbookTree extends SketchbookTree {
|
||||
@inject(CreateApi)
|
||||
private readonly createApi: CreateApi;
|
||||
|
||||
@inject(ApplicationConnectionStatusContribution)
|
||||
private readonly connectionStatus: ApplicationConnectionStatusContribution;
|
||||
|
||||
@inject(CreateFeatures)
|
||||
private readonly createFeatures: CreateFeatures;
|
||||
|
||||
protected override init(): void {
|
||||
this.toDispose.push(
|
||||
this.connectionStatus.onOfflineStatusDidChange(() => this.refresh())
|
||||
);
|
||||
super.init();
|
||||
}
|
||||
|
||||
async pushPublicWarn(
|
||||
node: CloudSketchbookTree.CloudSketchDirNode
|
||||
): Promise<boolean> {
|
||||
@@ -84,7 +104,7 @@ export class CloudSketchbookTree extends SketchbookTree {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
|
||||
async pull(arg: any): Promise<void> {
|
||||
async pull(arg: any, noProgress = false): Promise<void> {
|
||||
const {
|
||||
// model,
|
||||
node,
|
||||
@@ -118,7 +138,17 @@ export class CloudSketchbookTree extends SketchbookTree {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return this.runWithState(node, 'pulling', async (node) => {
|
||||
return this.runWithState(
|
||||
node,
|
||||
'pull',
|
||||
async (node) => {
|
||||
await this.pullNode(node);
|
||||
},
|
||||
noProgress
|
||||
);
|
||||
}
|
||||
|
||||
private async pullNode(node: CloudSketchbookTree.CloudSketchDirNode) {
|
||||
const commandsCopy = node.commands;
|
||||
node.commands = [];
|
||||
|
||||
@@ -133,17 +163,20 @@ export class CloudSketchbookTree extends SketchbookTree {
|
||||
this.messageService.info(
|
||||
nls.localize(
|
||||
'arduino/cloud/donePulling',
|
||||
'Done pulling ‘{0}’.',
|
||||
"Done pulling '{0}'.",
|
||||
node.fileStat.name
|
||||
),
|
||||
{
|
||||
timeout: MESSAGE_TIMEOUT,
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async push(node: CloudSketchbookTree.CloudSketchDirNode): Promise<void> {
|
||||
async push(
|
||||
node: CloudSketchbookTree.CloudSketchDirNode,
|
||||
noProgress = false,
|
||||
ignorePushWarnings = false
|
||||
): Promise<void> {
|
||||
if (!CloudSketchbookTree.CloudSketchTreeNode.isSynced(node)) {
|
||||
throw new Error(
|
||||
nls.localize(
|
||||
@@ -158,7 +191,8 @@ export class CloudSketchbookTree extends SketchbookTree {
|
||||
return;
|
||||
}
|
||||
|
||||
const warn = this.arduinoPreferences['arduino.cloud.push.warn'];
|
||||
const warn =
|
||||
!ignorePushWarnings && this.arduinoPreferences['arduino.cloud.push.warn'];
|
||||
|
||||
if (warn) {
|
||||
const ok = await new DoNotAskAgainConfirmDialog({
|
||||
@@ -178,7 +212,17 @@ export class CloudSketchbookTree extends SketchbookTree {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return this.runWithState(node, 'pushing', async (node) => {
|
||||
return this.runWithState(
|
||||
node,
|
||||
'push',
|
||||
async (node) => {
|
||||
await this.pushNode(node);
|
||||
},
|
||||
noProgress
|
||||
);
|
||||
}
|
||||
|
||||
private async pushNode(node: CloudSketchbookTree.CloudSketchDirNode) {
|
||||
if (!CloudSketchbookTree.CloudSketchTreeNode.isSynced(node)) {
|
||||
throw new Error(
|
||||
nls.localize(
|
||||
@@ -201,14 +245,13 @@ export class CloudSketchbookTree extends SketchbookTree {
|
||||
this.messageService.info(
|
||||
nls.localize(
|
||||
'arduino/cloud/donePushing',
|
||||
'Done pushing ‘{0}’.',
|
||||
"Done pushing '{0}'.",
|
||||
node.fileStat.name
|
||||
),
|
||||
{
|
||||
timeout: MESSAGE_TIMEOUT,
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private async recursiveURIs(uri: URI): Promise<URI[]> {
|
||||
@@ -310,31 +353,37 @@ export class CloudSketchbookTree extends SketchbookTree {
|
||||
|
||||
private async runWithState<T>(
|
||||
node: CloudSketchbookTree.CloudSketchDirNode & Partial<DecoratedTreeNode>,
|
||||
state: CloudSketchbookTree.CloudSketchDirNode.State,
|
||||
task: (node: CloudSketchbookTree.CloudSketchDirNode) => MaybePromise<T>
|
||||
state: CloudSketchState,
|
||||
task: (node: CloudSketchbookTree.CloudSketchDirNode) => MaybePromise<T>,
|
||||
noProgress = false
|
||||
): Promise<T> {
|
||||
const decoration: WidgetDecoration.TailDecoration = {
|
||||
data: `${firstToUpperCase(state)}...`,
|
||||
fontData: {
|
||||
color: 'var(--theia-list-highlightForeground)',
|
||||
},
|
||||
};
|
||||
this.createFeatures.setCloudSketchState(node.remoteUri, state);
|
||||
try {
|
||||
node.state = state;
|
||||
this.mergeDecoration(node, { tailDecorations: [decoration] });
|
||||
const result = await (noProgress
|
||||
? task(node)
|
||||
: ExecuteWithProgress.withProgress(
|
||||
this.taskMessage(state, node.uri.path.name),
|
||||
this.messageService,
|
||||
async (progress) => {
|
||||
progress.report({ work: { done: 0, total: NaN } });
|
||||
return task(node);
|
||||
}
|
||||
));
|
||||
await this.refresh(node);
|
||||
const result = await task(node);
|
||||
return result;
|
||||
} finally {
|
||||
delete node.state;
|
||||
// TODO: find a better way to attach and detach decorators. Do we need a proper `TreeDecorator` instead?
|
||||
const index = node.decorationData?.tailDecorations?.findIndex(
|
||||
(candidate) => JSON.stringify(decoration) === JSON.stringify(candidate)
|
||||
);
|
||||
if (typeof index === 'number' && index !== -1) {
|
||||
node.decorationData?.tailDecorations?.splice(index, 1);
|
||||
this.createFeatures.setCloudSketchState(node.remoteUri, undefined);
|
||||
}
|
||||
await this.refresh(node);
|
||||
}
|
||||
|
||||
private taskMessage(state: CloudSketchState, input: string): string {
|
||||
switch (state) {
|
||||
case 'pull':
|
||||
return pullingSketch(input);
|
||||
case 'push':
|
||||
return pushingSketch(input);
|
||||
default:
|
||||
assertUnreachable(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,7 +550,7 @@ export class CloudSketchbookTree extends SketchbookTree {
|
||||
};
|
||||
}
|
||||
|
||||
protected readonly notInSyncDecoration: WidgetDecoration.Data = {
|
||||
protected readonly notInSyncOfflineDecoration: WidgetDecoration.Data = {
|
||||
fontData: {
|
||||
color: 'var(--theia-activityBar-inactiveForeground)',
|
||||
},
|
||||
@@ -522,11 +571,15 @@ export class CloudSketchbookTree extends SketchbookTree {
|
||||
node.fileStat.resource.path.toString()
|
||||
);
|
||||
|
||||
const commands = [CloudSketchbookCommands.PULL_SKETCH];
|
||||
const commands: Command[] = [];
|
||||
if (this.connectionStatus.offlineStatus !== 'internet') {
|
||||
commands.push(CloudSketchbookCommands.PULL_SKETCH);
|
||||
}
|
||||
|
||||
if (
|
||||
CloudSketchbookTree.CloudSketchTreeNode.is(node) &&
|
||||
CloudSketchbookTree.CloudSketchTreeNode.isSynced(node)
|
||||
CloudSketchbookTree.CloudSketchTreeNode.isSynced(node) &&
|
||||
this.connectionStatus.offlineStatus !== 'internet'
|
||||
) {
|
||||
commands.push(CloudSketchbookCommands.PUSH_SKETCH);
|
||||
}
|
||||
@@ -557,14 +610,15 @@ export class CloudSketchbookTree extends SketchbookTree {
|
||||
}
|
||||
}
|
||||
|
||||
// add style decoration for not-in-sync files
|
||||
// add style decoration for not-in-sync files when offline
|
||||
if (
|
||||
CloudSketchbookTree.CloudSketchTreeNode.is(node) &&
|
||||
!CloudSketchbookTree.CloudSketchTreeNode.isSynced(node)
|
||||
!CloudSketchbookTree.CloudSketchTreeNode.isSynced(node) &&
|
||||
this.connectionStatus.offlineStatus === 'internet'
|
||||
) {
|
||||
this.mergeDecoration(node, this.notInSyncDecoration);
|
||||
this.mergeDecoration(node, this.notInSyncOfflineDecoration);
|
||||
} else {
|
||||
this.removeDecoration(node, this.notInSyncDecoration);
|
||||
this.removeDecoration(node, this.notInSyncOfflineDecoration);
|
||||
}
|
||||
|
||||
return node;
|
||||
@@ -644,7 +698,7 @@ export namespace CloudSketchbookTree {
|
||||
export interface CloudSketchDirNode
|
||||
extends Omit<SketchbookTree.SketchDirNode, 'fileStat'>,
|
||||
CloudSketchTreeNode {
|
||||
state?: CloudSketchDirNode.State;
|
||||
state?: CloudSketchState;
|
||||
isPublic?: boolean;
|
||||
sketchId?: string;
|
||||
commands?: Command[];
|
||||
@@ -653,7 +707,5 @@ export namespace CloudSketchbookTree {
|
||||
export function is(node: TreeNode | undefined): node is CloudSketchDirNode {
|
||||
return SketchbookTree.SketchDirNode.is(node);
|
||||
}
|
||||
|
||||
export type State = 'syncing' | 'pulling' | 'pushing';
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,101 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { CloudSketchbookTreeModel } from './cloud-sketchbook-tree-model';
|
||||
import { AuthenticationClientService } from '../../auth/authentication-client-service';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { ApplicationConnectionStatusContribution } from '../../theia/core/connection-status-service';
|
||||
|
||||
export class CloudStatus extends React.Component<
|
||||
CloudStatus.Props,
|
||||
CloudStatus.State
|
||||
> {
|
||||
protected readonly toDispose = new DisposableCollection();
|
||||
|
||||
constructor(props: CloudStatus.Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
status: this.status,
|
||||
refreshing: false,
|
||||
};
|
||||
}
|
||||
|
||||
override componentDidMount(): void {
|
||||
this.toDispose.push(
|
||||
this.props.connectionStatus.onOfflineStatusDidChange(() =>
|
||||
this.setState({ status: this.status })
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
override componentWillUnmount(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
override render(): React.ReactNode {
|
||||
if (!this.props.authenticationService.session) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="cloud-connection-status flex-line">
|
||||
<div className="status item flex-line">
|
||||
<div
|
||||
className={`${
|
||||
this.state.status === 'connected'
|
||||
? 'connected-status-icon'
|
||||
: 'offline-status-icon'
|
||||
}`}
|
||||
/>
|
||||
{this.state.status === 'connected'
|
||||
? nls.localize('arduino/cloud/connected', 'Connected')
|
||||
: nls.localize('arduino/cloud/offline', 'Offline')}
|
||||
</div>
|
||||
<div className="actions item flex-line">
|
||||
{this.props.connectionStatus.offlineStatus === 'internet' ? (
|
||||
<div
|
||||
className="fa fa-arduino-cloud-offline"
|
||||
title={nls.localize('arduino/cloud/offline', 'Offline')}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
title={nls.localize('arduino/cloud/sync', 'Sync')}
|
||||
className={`fa fa-reload ${
|
||||
(this.state.refreshing && 'rotating') || ''
|
||||
}`}
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={this.onDidClickRefresh}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private onDidClickRefresh = () => {
|
||||
this.setState({ refreshing: true });
|
||||
Promise.all([
|
||||
this.props.model.updateRoot(),
|
||||
new Promise((resolve) => setTimeout(() => resolve(true), 1000)),
|
||||
]).then(() => {
|
||||
this.props.model.sketchbookTree().refresh();
|
||||
this.setState({ refreshing: false });
|
||||
});
|
||||
};
|
||||
|
||||
private get status(): 'connected' | 'offline' {
|
||||
return this.props.connectionStatus.offlineStatus === 'internet'
|
||||
? 'offline'
|
||||
: 'connected';
|
||||
}
|
||||
}
|
||||
|
||||
export namespace CloudStatus {
|
||||
export interface Props {
|
||||
readonly model: CloudSketchbookTreeModel;
|
||||
readonly authenticationService: AuthenticationClientService;
|
||||
readonly connectionStatus: ApplicationConnectionStatusContribution;
|
||||
}
|
||||
export interface State {
|
||||
status: 'connected' | 'offline';
|
||||
refreshing?: boolean;
|
||||
}
|
||||
}
|
@@ -1,134 +0,0 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import { CloudSketchbookTreeModel } from './cloud-sketchbook-tree-model';
|
||||
import { AuthenticationClientService } from '../../auth/authentication-client-service';
|
||||
import { CloudUserCommands } from '../../auth/cloud-user-commands';
|
||||
import { AuthenticationSessionAccountInformation } from '../../../common/protocol/authentication-service';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
export class UserStatus extends React.Component<
|
||||
UserStatus.Props,
|
||||
UserStatus.State
|
||||
> {
|
||||
protected readonly toDispose = new DisposableCollection();
|
||||
|
||||
constructor(props: UserStatus.Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
status: this.status,
|
||||
accountInfo: props.authenticationService.session?.account,
|
||||
refreshing: false,
|
||||
};
|
||||
}
|
||||
|
||||
override componentDidMount(): void {
|
||||
const statusListener = () => this.setState({ status: this.status });
|
||||
window.addEventListener('online', statusListener);
|
||||
window.addEventListener('offline', statusListener);
|
||||
this.toDispose.pushAll([
|
||||
this.props.authenticationService.onSessionDidChange((session) =>
|
||||
this.setState({ accountInfo: session?.account })
|
||||
),
|
||||
Disposable.create(() =>
|
||||
window.removeEventListener('online', statusListener)
|
||||
),
|
||||
Disposable.create(() =>
|
||||
window.removeEventListener('offline', statusListener)
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
override componentWillUnmount(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
override render(): React.ReactNode {
|
||||
if (!this.props.authenticationService.session) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="cloud-connection-status flex-line">
|
||||
<div className="status item flex-line">
|
||||
<div
|
||||
className={`${
|
||||
this.state.status === 'connected'
|
||||
? 'connected-status-icon'
|
||||
: 'offline-status-icon'
|
||||
}`}
|
||||
/>
|
||||
{this.state.status === 'connected'
|
||||
? nls.localize('arduino/cloud/connected', 'Connected')
|
||||
: nls.localize('arduino/cloud/offline', 'Offline')}
|
||||
</div>
|
||||
<div className="actions item flex-line">
|
||||
<div
|
||||
title={nls.localize('arduino/cloud/sync', 'Sync')}
|
||||
className={`fa fa-reload ${
|
||||
(this.state.refreshing && 'rotating') || ''
|
||||
}`}
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={this.onDidClickRefresh}
|
||||
/>
|
||||
</div>
|
||||
<div className="account item flex-line">
|
||||
<div
|
||||
title={nls.localize('arduino/cloud/account', 'Account')}
|
||||
className="account-icon"
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.props.model.commandRegistry.executeCommand(
|
||||
CloudUserCommands.OPEN_PROFILE_CONTEXT_MENU.id,
|
||||
{
|
||||
event: event.nativeEvent,
|
||||
username: this.state.accountInfo?.label,
|
||||
}
|
||||
);
|
||||
}}
|
||||
>
|
||||
{this.state.accountInfo?.picture && (
|
||||
<img
|
||||
src={this.state.accountInfo?.picture}
|
||||
alt={nls.localize(
|
||||
'arduino/cloud/profilePicture',
|
||||
'Profile picture'
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private onDidClickRefresh = () => {
|
||||
this.setState({ refreshing: true });
|
||||
Promise.all([
|
||||
this.props.model.updateRoot(),
|
||||
new Promise((resolve) => setTimeout(() => resolve(true), 1000)),
|
||||
]).then(() => {
|
||||
this.props.model.sketchbookTree().refresh();
|
||||
this.setState({ refreshing: false });
|
||||
});
|
||||
};
|
||||
|
||||
private get status(): 'connected' | 'offline' {
|
||||
return window.navigator.onLine ? 'connected' : 'offline';
|
||||
}
|
||||
}
|
||||
|
||||
export namespace UserStatus {
|
||||
export interface Props {
|
||||
readonly model: CloudSketchbookTreeModel;
|
||||
readonly authenticationService: AuthenticationClientService;
|
||||
}
|
||||
export interface State {
|
||||
status: 'connected' | 'offline';
|
||||
accountInfo?: AuthenticationSessionAccountInformation;
|
||||
refreshing?: boolean;
|
||||
}
|
||||
}
|
@@ -1,60 +1,76 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import type { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||
import { Installable } from '../../../common/protocol/installable';
|
||||
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||
import { ListItemRenderer } from './list-item-renderer';
|
||||
import type { ListItemRenderer } from './list-item-renderer';
|
||||
import { UserAbortError } from './list-widget';
|
||||
|
||||
export class ComponentListItem<
|
||||
T extends ArduinoComponent
|
||||
> extends React.Component<ComponentListItem.Props<T>, ComponentListItem.State> {
|
||||
constructor(props: ComponentListItem.Props<T>) {
|
||||
super(props);
|
||||
if (props.item.installable) {
|
||||
const version = props.item.availableVersions.filter(
|
||||
(version) => version !== props.item.installedVersion
|
||||
)[0];
|
||||
this.state = {
|
||||
selectedVersion: version,
|
||||
};
|
||||
}
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
override render(): React.ReactNode {
|
||||
const { item, itemRenderer } = this.props;
|
||||
const selectedVersion =
|
||||
this.props.edited?.item.name === item.name
|
||||
? this.props.edited.selectedVersion
|
||||
: this.latestVersion;
|
||||
return (
|
||||
<>
|
||||
{itemRenderer.renderItem(
|
||||
Object.assign(this.state, { item }),
|
||||
this.install.bind(this),
|
||||
this.uninstall.bind(this),
|
||||
this.onVersionChange.bind(this)
|
||||
)}
|
||||
{itemRenderer.renderItem({
|
||||
item,
|
||||
selectedVersion,
|
||||
inProgress: this.state.inProgress,
|
||||
install: (item) => this.install(item),
|
||||
uninstall: (item) => this.uninstall(item),
|
||||
onVersionChange: (version) => this.onVersionChange(version),
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
private async install(item: T): Promise<void> {
|
||||
const toInstall = this.state.selectedVersion;
|
||||
const version = this.props.item.availableVersions.filter(
|
||||
(version) => version !== this.state.selectedVersion
|
||||
)[0];
|
||||
this.setState({
|
||||
selectedVersion: version,
|
||||
});
|
||||
try {
|
||||
await this.props.install(item, toInstall);
|
||||
} catch {
|
||||
this.setState({
|
||||
selectedVersion: toInstall,
|
||||
});
|
||||
}
|
||||
await this.withState('installing', () =>
|
||||
this.props.install(
|
||||
item,
|
||||
this.props.edited?.item.name === item.name
|
||||
? this.props.edited.selectedVersion
|
||||
: Installable.latest(this.props.item.availableVersions)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private async uninstall(item: T): Promise<void> {
|
||||
await this.props.uninstall(item);
|
||||
await this.withState('uninstalling', () => this.props.uninstall(item));
|
||||
}
|
||||
|
||||
private async withState(
|
||||
inProgress: 'installing' | 'uninstalling',
|
||||
task: () => Promise<unknown>
|
||||
): Promise<void> {
|
||||
this.setState({ inProgress });
|
||||
try {
|
||||
await task();
|
||||
} catch (err) {
|
||||
if (err instanceof UserAbortError) {
|
||||
// No state update when user cancels the task
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
} finally {
|
||||
this.setState({ inProgress: undefined });
|
||||
}
|
||||
}
|
||||
|
||||
private onVersionChange(version: Installable.Version): void {
|
||||
this.setState({ selectedVersion: version });
|
||||
this.props.onItemEdit(this.props.item, version);
|
||||
}
|
||||
|
||||
private get latestVersion(): Installable.Version | undefined {
|
||||
return Installable.latest(this.props.item.availableVersions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,10 +79,18 @@ export namespace ComponentListItem {
|
||||
readonly item: T;
|
||||
readonly install: (item: T, version?: Installable.Version) => Promise<void>;
|
||||
readonly uninstall: (item: T) => Promise<void>;
|
||||
readonly edited?: {
|
||||
item: T;
|
||||
selectedVersion: Installable.Version;
|
||||
};
|
||||
readonly onItemEdit: (
|
||||
item: T,
|
||||
selectedVersion: Installable.Version
|
||||
) => void;
|
||||
readonly itemRenderer: ListItemRenderer<T>;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
selectedVersion?: Installable.Version;
|
||||
inProgress?: 'installing' | 'uninstalling' | undefined;
|
||||
}
|
||||
}
|
||||
|
@@ -1,148 +1,32 @@
|
||||
import 'react-virtualized/styles.css';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer';
|
||||
import {
|
||||
CellMeasurer,
|
||||
CellMeasurerCache,
|
||||
} from 'react-virtualized/dist/commonjs/CellMeasurer';
|
||||
import type {
|
||||
ListRowProps,
|
||||
ListRowRenderer,
|
||||
} from 'react-virtualized/dist/commonjs/List';
|
||||
import List from 'react-virtualized/dist/commonjs/List';
|
||||
import { Virtuoso } from '@theia/core/shared/react-virtuoso';
|
||||
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||
import { Installable } from '../../../common/protocol/installable';
|
||||
import { ComponentListItem } from './component-list-item';
|
||||
import { ListItemRenderer } from './list-item-renderer';
|
||||
|
||||
function sameAs<T>(
|
||||
left: T[],
|
||||
right: T[],
|
||||
...compareProps: (keyof T)[]
|
||||
): boolean {
|
||||
if (left === right) {
|
||||
return true;
|
||||
}
|
||||
const leftLength = left.length;
|
||||
if (leftLength !== right.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < leftLength; i++) {
|
||||
for (const prop of compareProps) {
|
||||
const leftValue = left[i][prop];
|
||||
const rightValue = right[i][prop];
|
||||
if (leftValue !== rightValue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export class ComponentList<T extends ArduinoComponent> extends React.Component<
|
||||
ComponentList.Props<T>
|
||||
> {
|
||||
private readonly cache: CellMeasurerCache;
|
||||
private resizeAllFlag: boolean;
|
||||
private list: List | undefined;
|
||||
private mostRecentWidth: number | undefined;
|
||||
|
||||
constructor(props: ComponentList.Props<T>) {
|
||||
super(props);
|
||||
this.cache = new CellMeasurerCache({
|
||||
defaultHeight: 140,
|
||||
fixedWidth: true,
|
||||
});
|
||||
}
|
||||
|
||||
override render(): React.ReactNode {
|
||||
return (
|
||||
<AutoSizer>
|
||||
{({ width, height }) => {
|
||||
if (this.mostRecentWidth && this.mostRecentWidth !== width) {
|
||||
this.resizeAllFlag = true;
|
||||
setTimeout(() => this.clearAll(), 0);
|
||||
}
|
||||
this.mostRecentWidth = width;
|
||||
return (
|
||||
<List
|
||||
className={'items-container'}
|
||||
rowRenderer={this.createItem}
|
||||
height={height}
|
||||
width={width}
|
||||
rowCount={this.props.items.length}
|
||||
rowHeight={this.cache.rowHeight}
|
||||
deferredMeasurementCache={this.cache}
|
||||
ref={this.setListRef}
|
||||
estimatedRowSize={140}
|
||||
// If default value, then `react-virtualized` will optimize and list item will not receive a `:hover` event.
|
||||
// Hence, install and version `<select>` won't be visible even if the mouse cursor is over the `<div>`.
|
||||
// See https://github.com/bvaughn/react-virtualized/blob/005be24a608add0344284053dae7633be86053b2/source/Grid/Grid.js#L38-L42
|
||||
scrollingResetTimeInterval={0}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</AutoSizer>
|
||||
);
|
||||
}
|
||||
|
||||
override componentDidUpdate(prevProps: ComponentList.Props<T>): void {
|
||||
if (
|
||||
this.resizeAllFlag ||
|
||||
!sameAs(this.props.items, prevProps.items, 'name', 'installedVersion')
|
||||
) {
|
||||
this.clearAll(true);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly setListRef = (ref: List | null): void => {
|
||||
this.list = ref || undefined;
|
||||
};
|
||||
|
||||
private clearAll(scrollToTop = false): void {
|
||||
this.resizeAllFlag = false;
|
||||
this.cache.clearAll();
|
||||
if (this.list) {
|
||||
this.list.recomputeRowHeights();
|
||||
if (scrollToTop) {
|
||||
this.list.scrollToPosition(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly createItem: ListRowRenderer = ({
|
||||
index,
|
||||
parent,
|
||||
key,
|
||||
style,
|
||||
}: ListRowProps): React.ReactNode => {
|
||||
const item = this.props.items[index];
|
||||
return (
|
||||
<CellMeasurer
|
||||
cache={this.cache}
|
||||
columnIndex={0}
|
||||
key={key}
|
||||
rowIndex={index}
|
||||
parent={parent}
|
||||
>
|
||||
{({ registerChild }) => (
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
<div ref={registerChild} style={style}>
|
||||
<Virtuoso
|
||||
data={this.props.items}
|
||||
itemContent={(_: number, item: T) => (
|
||||
<ComponentListItem<T>
|
||||
key={this.props.itemLabel(item)}
|
||||
item={item}
|
||||
itemRenderer={this.props.itemRenderer}
|
||||
install={this.props.install}
|
||||
uninstall={this.props.uninstall}
|
||||
edited={this.props.edited}
|
||||
onItemEdit={this.props.onItemEdit}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</CellMeasurer>
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
export namespace ComponentList {
|
||||
export interface Props<T extends ArduinoComponent> {
|
||||
readonly items: T[];
|
||||
@@ -150,5 +34,13 @@ export namespace ComponentList {
|
||||
readonly itemRenderer: ListItemRenderer<T>;
|
||||
readonly install: (item: T, version?: Installable.Version) => Promise<void>;
|
||||
readonly uninstall: (item: T) => Promise<void>;
|
||||
readonly edited?: {
|
||||
item: T;
|
||||
selectedVersion: Installable.Version;
|
||||
};
|
||||
readonly onItemEdit: (
|
||||
item: T,
|
||||
selectedVersion: Installable.Version
|
||||
) => void;
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ import { ListItemRenderer } from './list-item-renderer';
|
||||
import { ResponseServiceClient } from '../../../common/protocol';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { FilterRenderer } from './filter-renderer';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
|
||||
export class FilterableListContainer<
|
||||
T extends ArduinoComponent,
|
||||
@@ -23,21 +24,30 @@ export class FilterableListContainer<
|
||||
FilterableListContainer.Props<T, S>,
|
||||
FilterableListContainer.State<T, S>
|
||||
> {
|
||||
private readonly toDispose: DisposableCollection;
|
||||
|
||||
constructor(props: Readonly<FilterableListContainer.Props<T, S>>) {
|
||||
super(props);
|
||||
this.state = {
|
||||
searchOptions: props.defaultSearchOptions,
|
||||
items: [],
|
||||
};
|
||||
this.toDispose = new DisposableCollection();
|
||||
}
|
||||
|
||||
override componentDidMount(): void {
|
||||
this.search = debounce(this.search, 500, { trailing: true });
|
||||
this.search(this.state.searchOptions);
|
||||
this.toDispose.pushAll([
|
||||
this.props.searchOptionsDidChange((newSearchOptions) => {
|
||||
const { searchOptions } = this.state;
|
||||
this.setSearchOptionsAndUpdate({ ...searchOptions, ...newSearchOptions });
|
||||
this.setSearchOptionsAndUpdate({
|
||||
...searchOptions,
|
||||
...newSearchOptions,
|
||||
});
|
||||
}),
|
||||
this.props.onDidShow(() => this.setState({ edited: undefined })),
|
||||
]);
|
||||
}
|
||||
|
||||
override componentDidUpdate(): void {
|
||||
@@ -46,6 +56,10 @@ export class FilterableListContainer<
|
||||
this.props.container.updateScrollBar();
|
||||
}
|
||||
|
||||
override componentWillUnmount(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
override render(): React.ReactNode {
|
||||
return (
|
||||
<div className={'filterable-list-container'}>
|
||||
@@ -90,11 +104,13 @@ export class FilterableListContainer<
|
||||
itemRenderer={itemRenderer}
|
||||
install={this.install.bind(this)}
|
||||
uninstall={this.uninstall.bind(this)}
|
||||
edited={this.state.edited}
|
||||
onItemEdit={this.onItemEdit.bind(this)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
protected handlePropChange = (prop: keyof S, value: S[keyof S]): void => {
|
||||
private handlePropChange = (prop: keyof S, value: S[keyof S]): void => {
|
||||
const searchOptions = {
|
||||
...this.state.searchOptions,
|
||||
[prop]: value,
|
||||
@@ -106,15 +122,14 @@ export class FilterableListContainer<
|
||||
this.setState({ searchOptions }, () => this.search(searchOptions));
|
||||
}
|
||||
|
||||
protected search(searchOptions: S): void {
|
||||
private search(searchOptions: S): void {
|
||||
const { searchable } = this.props;
|
||||
searchable.search(searchOptions).then((items) => this.setState({ items }));
|
||||
searchable
|
||||
.search(searchOptions)
|
||||
.then((items) => this.setState({ items, edited: undefined }));
|
||||
}
|
||||
|
||||
protected async install(
|
||||
item: T,
|
||||
version: Installable.Version
|
||||
): Promise<void> {
|
||||
private async install(item: T, version: Installable.Version): Promise<void> {
|
||||
const { install, searchable } = this.props;
|
||||
await ExecuteWithProgress.doWithProgress({
|
||||
...this.props,
|
||||
@@ -124,10 +139,10 @@ export class FilterableListContainer<
|
||||
run: ({ progressId }) => install({ item, progressId, version }),
|
||||
});
|
||||
const items = await searchable.search(this.state.searchOptions);
|
||||
this.setState({ items });
|
||||
this.setState({ items, edited: undefined });
|
||||
}
|
||||
|
||||
protected async uninstall(item: T): Promise<void> {
|
||||
private async uninstall(item: T): Promise<void> {
|
||||
const ok = await new ConfirmDialog({
|
||||
title: nls.localize('arduino/component/uninstall', 'Uninstall'),
|
||||
msg: nls.localize(
|
||||
@@ -152,7 +167,11 @@ export class FilterableListContainer<
|
||||
run: ({ progressId }) => uninstall({ item, progressId }),
|
||||
});
|
||||
const items = await searchable.search(this.state.searchOptions);
|
||||
this.setState({ items });
|
||||
this.setState({ items, edited: undefined });
|
||||
}
|
||||
|
||||
private onItemEdit(item: T, selectedVersion: Installable.Version): void {
|
||||
this.setState({ edited: { item, selectedVersion } });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,6 +190,7 @@ export namespace FilterableListContainer {
|
||||
readonly searchOptionsDidChange: Event<Partial<S> | undefined>;
|
||||
readonly messageService: MessageService;
|
||||
readonly responseService: ResponseServiceClient;
|
||||
readonly onDidShow: Event<void>;
|
||||
readonly install: ({
|
||||
item,
|
||||
progressId,
|
||||
@@ -193,5 +213,9 @@ export namespace FilterableListContainer {
|
||||
export interface State<T, S extends Searchable.Options> {
|
||||
searchOptions: S;
|
||||
items: T[];
|
||||
edited?: {
|
||||
item: T;
|
||||
selectedVersion: Installable.Version;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -1,137 +1,783 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { ApplicationError } from '@theia/core';
|
||||
import {
|
||||
Anchor,
|
||||
ContextMenuRenderer,
|
||||
} from '@theia/core/lib/browser/context-menu-renderer';
|
||||
import { TabBarToolbar } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
import { codicon } from '@theia/core/lib/browser/widgets/widget';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { Installable } from '../../../common/protocol/installable';
|
||||
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||
import { ComponentListItem } from './component-list-item';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import {
|
||||
CommandHandler,
|
||||
CommandRegistry,
|
||||
CommandService,
|
||||
} from '@theia/core/lib/common/command';
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import {
|
||||
MenuModelRegistry,
|
||||
MenuPath,
|
||||
SubMenuOptions,
|
||||
} from '@theia/core/lib/common/menu';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { Unknown } from '../../../common/nls';
|
||||
import {
|
||||
CoreService,
|
||||
ExamplesService,
|
||||
LibraryPackage,
|
||||
Sketch,
|
||||
SketchContainer,
|
||||
SketchesService,
|
||||
SketchRef,
|
||||
} from '../../../common/protocol';
|
||||
import type { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||
import { Installable } from '../../../common/protocol/installable';
|
||||
import { openClonedExample } from '../../contributions/examples';
|
||||
import {
|
||||
ArduinoMenus,
|
||||
examplesLabel,
|
||||
unregisterSubmenu,
|
||||
} from '../../menu/arduino-menus';
|
||||
|
||||
const moreInfoLabel = nls.localize('arduino/component/moreInfo', 'More info');
|
||||
const otherVersionsLabel = nls.localize(
|
||||
'arduino/component/otherVersions',
|
||||
'Other Versions'
|
||||
);
|
||||
const installLabel = nls.localize('arduino/component/install', 'Install');
|
||||
const installLatestLabel = nls.localize(
|
||||
'arduino/component/installLatest',
|
||||
'Install Latest'
|
||||
);
|
||||
function installVersionLabel(selectedVersion: string) {
|
||||
return nls.localize(
|
||||
'arduino/component/installVersion',
|
||||
'Install {0}',
|
||||
selectedVersion
|
||||
);
|
||||
}
|
||||
const updateLabel = nls.localize('arduino/component/update', 'Update');
|
||||
const removeLabel = nls.localize('arduino/component/remove', 'Remove');
|
||||
const byLabel = nls.localize('arduino/component/by', 'by');
|
||||
function nameAuthorLabel(name: string, author: string) {
|
||||
return nls.localize('arduino/component/title', '{0} by {1}', name, author);
|
||||
}
|
||||
function installedLabel(installedVersion: string) {
|
||||
return nls.localize(
|
||||
'arduino/component/installed',
|
||||
'{0} installed',
|
||||
installedVersion
|
||||
);
|
||||
}
|
||||
function clickToOpenInBrowserLabel(href: string): string | undefined {
|
||||
return nls.localize(
|
||||
'arduino/component/clickToOpen',
|
||||
'Click to open in browser: {0}',
|
||||
href
|
||||
);
|
||||
}
|
||||
|
||||
interface MenuTemplate {
|
||||
readonly menuLabel: string;
|
||||
}
|
||||
interface MenuActionTemplate extends MenuTemplate {
|
||||
readonly menuPath: MenuPath;
|
||||
readonly handler: CommandHandler;
|
||||
/**
|
||||
* If not defined the insertion oder will be the order string.
|
||||
*/
|
||||
readonly order?: string;
|
||||
}
|
||||
interface SubmenuTemplate extends MenuTemplate {
|
||||
readonly menuLabel: string;
|
||||
readonly submenuPath: MenuPath;
|
||||
readonly options?: SubMenuOptions;
|
||||
}
|
||||
function isMenuTemplate(arg: unknown): arg is MenuTemplate {
|
||||
return (
|
||||
typeof arg === 'object' &&
|
||||
(arg as MenuTemplate).menuLabel !== undefined &&
|
||||
typeof (arg as MenuTemplate).menuLabel === 'string'
|
||||
);
|
||||
}
|
||||
function isMenuActionTemplate(arg: MenuTemplate): arg is MenuActionTemplate {
|
||||
return (
|
||||
isMenuTemplate(arg) &&
|
||||
(arg as MenuActionTemplate).handler !== undefined &&
|
||||
typeof (arg as MenuActionTemplate).handler === 'object' &&
|
||||
(arg as MenuActionTemplate).menuPath !== undefined &&
|
||||
Array.isArray((arg as MenuActionTemplate).menuPath)
|
||||
);
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class ArduinoComponentContextMenuRenderer {
|
||||
@inject(CommandRegistry)
|
||||
private readonly commandRegistry: CommandRegistry;
|
||||
@inject(MenuModelRegistry)
|
||||
private readonly menuRegistry: MenuModelRegistry;
|
||||
@inject(ContextMenuRenderer)
|
||||
private readonly contextMenuRenderer: ContextMenuRenderer;
|
||||
|
||||
private readonly toDisposeBeforeRender = new DisposableCollection();
|
||||
private menuIndexCounter = 0;
|
||||
|
||||
async render(
|
||||
anchor: Anchor,
|
||||
...templates: (MenuActionTemplate | SubmenuTemplate)[]
|
||||
): Promise<void> {
|
||||
this.toDisposeBeforeRender.dispose();
|
||||
this.toDisposeBeforeRender.pushAll([
|
||||
Disposable.create(() => (this.menuIndexCounter = 0)),
|
||||
...templates.map((template) => this.registerMenu(template)),
|
||||
]);
|
||||
const options = {
|
||||
menuPath: ArduinoMenus.ARDUINO_COMPONENT__CONTEXT,
|
||||
anchor,
|
||||
showDisabled: true,
|
||||
};
|
||||
this.contextMenuRenderer.render(options);
|
||||
}
|
||||
|
||||
private registerMenu(
|
||||
template: MenuActionTemplate | SubmenuTemplate
|
||||
): Disposable {
|
||||
if (isMenuActionTemplate(template)) {
|
||||
const { menuLabel, menuPath, handler, order } = template;
|
||||
const id = this.generateCommandId(menuLabel, menuPath);
|
||||
const index = this.menuIndexCounter++;
|
||||
return new DisposableCollection(
|
||||
this.commandRegistry.registerCommand({ id }, handler),
|
||||
this.menuRegistry.registerMenuAction(menuPath, {
|
||||
commandId: id,
|
||||
label: menuLabel,
|
||||
order: typeof order === 'string' ? order : String(index).padStart(4),
|
||||
})
|
||||
);
|
||||
} else {
|
||||
const { menuLabel, submenuPath, options } = template;
|
||||
return new DisposableCollection(
|
||||
this.menuRegistry.registerSubmenu(submenuPath, menuLabel, options),
|
||||
Disposable.create(() =>
|
||||
unregisterSubmenu(submenuPath, this.menuRegistry)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private generateCommandId(menuLabel: string, menuPath: MenuPath): string {
|
||||
return `arduino--component-context-${menuPath.join('-')}-${menuLabel}`;
|
||||
}
|
||||
}
|
||||
|
||||
interface ListItemRendererParams<T extends ArduinoComponent> {
|
||||
readonly item: T;
|
||||
readonly selectedVersion: Installable.Version | undefined;
|
||||
readonly inProgress?: 'installing' | 'uninstalling' | undefined;
|
||||
readonly install: (item: T) => Promise<void>;
|
||||
readonly uninstall: (item: T) => Promise<void>;
|
||||
readonly onVersionChange: (version: Installable.Version) => void;
|
||||
}
|
||||
|
||||
interface ListItemRendererServices {
|
||||
readonly windowService: WindowService;
|
||||
readonly messagesService: MessageService;
|
||||
readonly commandService: CommandService;
|
||||
readonly coreService: CoreService;
|
||||
readonly examplesService: ExamplesService;
|
||||
readonly sketchesService: SketchesService;
|
||||
readonly contextMenuRenderer: ArduinoComponentContextMenuRenderer;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class ListItemRenderer<T extends ArduinoComponent> {
|
||||
@inject(WindowService)
|
||||
protected windowService: WindowService;
|
||||
private readonly windowService: WindowService;
|
||||
@inject(MessageService)
|
||||
private readonly messageService: MessageService;
|
||||
@inject(CommandService)
|
||||
private readonly commandService: CommandService;
|
||||
@inject(CoreService)
|
||||
private readonly coreService: CoreService;
|
||||
@inject(ExamplesService)
|
||||
private readonly examplesService: ExamplesService;
|
||||
@inject(SketchesService)
|
||||
private readonly sketchesService: SketchesService;
|
||||
@inject(ArduinoComponentContextMenuRenderer)
|
||||
private readonly contextMenuRenderer: ArduinoComponentContextMenuRenderer;
|
||||
|
||||
protected onMoreInfoClick = (
|
||||
private readonly onMoreInfo = (href: string | undefined): void => {
|
||||
if (href) {
|
||||
this.windowService.openNewWindow(href, { external: true });
|
||||
}
|
||||
};
|
||||
|
||||
renderItem(params: ListItemRendererParams<T>): React.ReactNode {
|
||||
const action = this.action(params);
|
||||
return (
|
||||
<>
|
||||
<Separator />
|
||||
<div className="component-list-item noselect">
|
||||
<Header
|
||||
params={params}
|
||||
action={action}
|
||||
services={this.services}
|
||||
onMoreInfo={this.onMoreInfo}
|
||||
/>
|
||||
<Content params={params} onMoreInfo={this.onMoreInfo} />
|
||||
<Footer params={params} action={action} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
private action(params: ListItemRendererParams<T>): Installable.Action {
|
||||
const {
|
||||
item: { installedVersion, availableVersions },
|
||||
selectedVersion,
|
||||
} = params;
|
||||
return Installable.action({
|
||||
installed: installedVersion,
|
||||
available: availableVersions,
|
||||
selected: selectedVersion,
|
||||
});
|
||||
}
|
||||
|
||||
private get services(): ListItemRendererServices {
|
||||
return {
|
||||
windowService: this.windowService,
|
||||
messagesService: this.messageService,
|
||||
commandService: this.commandService,
|
||||
coreService: this.coreService,
|
||||
sketchesService: this.sketchesService,
|
||||
examplesService: this.examplesService,
|
||||
contextMenuRenderer: this.contextMenuRenderer,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Separator extends React.Component {
|
||||
override render(): React.ReactNode {
|
||||
return (
|
||||
<div className="separator">
|
||||
<div />
|
||||
<div className="line" />
|
||||
<div />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Header<T extends ArduinoComponent> extends React.Component<
|
||||
Readonly<{
|
||||
params: ListItemRendererParams<T>;
|
||||
action: Installable.Action;
|
||||
services: ListItemRendererServices;
|
||||
onMoreInfo: (href: string | undefined) => void;
|
||||
}>
|
||||
> {
|
||||
override render(): React.ReactNode {
|
||||
return (
|
||||
<div className="header">
|
||||
<div>
|
||||
<Title {...this.props} />
|
||||
<Toolbar {...this.props} />
|
||||
</div>
|
||||
<InstalledVersion {...this.props} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Toolbar<T extends ArduinoComponent> extends React.Component<
|
||||
Readonly<{
|
||||
params: ListItemRendererParams<T>;
|
||||
action: Installable.Action;
|
||||
services: ListItemRendererServices;
|
||||
onMoreInfo: (href: string | undefined) => void;
|
||||
}>
|
||||
> {
|
||||
private readonly onClick = (event: React.MouseEvent): void => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
const anchor = this.toAnchor(event);
|
||||
this.showContextMenu(anchor);
|
||||
};
|
||||
|
||||
override render(): React.ReactNode {
|
||||
return (
|
||||
<div className={TabBarToolbar.Styles.TAB_BAR_TOOLBAR}>
|
||||
<div className={`${TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM} enabled`}>
|
||||
<div
|
||||
id="__more__"
|
||||
className={codicon('ellipsis', true)}
|
||||
title={nls.localizeByDefault('More Actions...')}
|
||||
onClick={this.onClick}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private toAnchor(event: React.MouseEvent): Anchor {
|
||||
const itemBox = event.currentTarget
|
||||
.closest('.' + TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM)
|
||||
?.getBoundingClientRect();
|
||||
return itemBox
|
||||
? {
|
||||
y: itemBox.bottom + itemBox.height / 2,
|
||||
x: itemBox.left,
|
||||
}
|
||||
: event.nativeEvent;
|
||||
}
|
||||
|
||||
private async showContextMenu(anchor: Anchor): Promise<void> {
|
||||
this.props.services.contextMenuRenderer.render(
|
||||
anchor,
|
||||
this.moreInfo,
|
||||
...(await this.examples),
|
||||
...this.otherVersions,
|
||||
...this.actions
|
||||
);
|
||||
}
|
||||
|
||||
private get moreInfo(): MenuActionTemplate {
|
||||
const {
|
||||
params: {
|
||||
item: { moreInfoLink },
|
||||
},
|
||||
} = this.props;
|
||||
return {
|
||||
menuLabel: moreInfoLabel,
|
||||
menuPath: ArduinoMenus.ARDUINO_COMPONENT__CONTEXT,
|
||||
handler: {
|
||||
execute: () => this.props.onMoreInfo(moreInfoLink),
|
||||
isEnabled: () => Boolean(moreInfoLink),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private get examples(): Promise<(MenuActionTemplate | SubmenuTemplate)[]> {
|
||||
const {
|
||||
params: {
|
||||
item,
|
||||
item: { installedVersion, name },
|
||||
},
|
||||
services: { examplesService },
|
||||
} = this.props;
|
||||
// TODO: `LibraryPackage.is` should not be here but it saves one extra `lib list`
|
||||
// gRPC equivalent call with the name of a platform which will result an empty array.
|
||||
if (!LibraryPackage.is(item) || !installedVersion) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
const submenuPath = [
|
||||
...ArduinoMenus.ARDUINO_COMPONENT__CONTEXT,
|
||||
'examples',
|
||||
];
|
||||
return examplesService.find({ libraryName: name }).then((containers) => [
|
||||
{
|
||||
submenuPath,
|
||||
menuLabel: examplesLabel,
|
||||
options: { order: String(0) },
|
||||
},
|
||||
...containers
|
||||
.map((container) => this.flattenContainers(container, submenuPath))
|
||||
.reduce((acc, curr) => acc.concat(curr), []),
|
||||
]);
|
||||
}
|
||||
|
||||
private flattenContainers(
|
||||
container: SketchContainer,
|
||||
menuPath: MenuPath,
|
||||
depth = 0
|
||||
): (MenuActionTemplate | SubmenuTemplate)[] {
|
||||
const templates: (MenuActionTemplate | SubmenuTemplate)[] = [];
|
||||
const { label } = container;
|
||||
if (depth > 0) {
|
||||
menuPath = [...menuPath, label];
|
||||
templates.push({
|
||||
submenuPath: menuPath,
|
||||
menuLabel: label,
|
||||
options: { order: label.toLocaleLowerCase() },
|
||||
});
|
||||
}
|
||||
return templates
|
||||
.concat(
|
||||
...container.sketches.map((sketch) =>
|
||||
this.sketchToMenuTemplate(sketch, menuPath)
|
||||
)
|
||||
)
|
||||
.concat(
|
||||
container.children
|
||||
.map((childContainer) =>
|
||||
this.flattenContainers(childContainer, menuPath, ++depth)
|
||||
)
|
||||
.reduce((acc, curr) => acc.concat(curr), [])
|
||||
);
|
||||
}
|
||||
|
||||
private sketchToMenuTemplate(
|
||||
sketch: SketchRef,
|
||||
menuPath: MenuPath
|
||||
): MenuActionTemplate {
|
||||
const { name, uri } = sketch;
|
||||
const { sketchesService, commandService } = this.props.services;
|
||||
return {
|
||||
menuLabel: name,
|
||||
menuPath,
|
||||
handler: {
|
||||
execute: () =>
|
||||
openClonedExample(
|
||||
uri,
|
||||
{ sketchesService, commandService },
|
||||
this.onExampleOpenError
|
||||
),
|
||||
},
|
||||
order: name.toLocaleLowerCase(),
|
||||
};
|
||||
}
|
||||
|
||||
private get onExampleOpenError(): {
|
||||
onDidFailClone: (
|
||||
err: ApplicationError<number, unknown>,
|
||||
uri: string
|
||||
) => unknown;
|
||||
onDidFailOpen: (
|
||||
err: ApplicationError<number, unknown>,
|
||||
sketch: Sketch
|
||||
) => unknown;
|
||||
} {
|
||||
const {
|
||||
services: { messagesService, coreService },
|
||||
} = this.props;
|
||||
const handle = async (err: ApplicationError<number, unknown>) => {
|
||||
messagesService.error(err.message);
|
||||
return coreService.refresh();
|
||||
};
|
||||
return {
|
||||
onDidFailClone: handle,
|
||||
onDidFailOpen: handle,
|
||||
};
|
||||
}
|
||||
|
||||
private get otherVersions(): (MenuActionTemplate | SubmenuTemplate)[] {
|
||||
const {
|
||||
params: {
|
||||
item: { availableVersions },
|
||||
selectedVersion,
|
||||
onVersionChange,
|
||||
},
|
||||
} = this.props;
|
||||
const submenuPath = [
|
||||
...ArduinoMenus.ARDUINO_COMPONENT__CONTEXT,
|
||||
'other-versions',
|
||||
];
|
||||
return [
|
||||
{
|
||||
submenuPath,
|
||||
menuLabel: otherVersionsLabel,
|
||||
options: { order: String(1) },
|
||||
},
|
||||
...availableVersions
|
||||
.filter((version) => version !== selectedVersion)
|
||||
.map((version) => ({
|
||||
menuPath: submenuPath,
|
||||
menuLabel: version,
|
||||
handler: {
|
||||
execute: () => onVersionChange(version),
|
||||
},
|
||||
})),
|
||||
];
|
||||
}
|
||||
|
||||
private get actions(): MenuActionTemplate[] {
|
||||
const {
|
||||
action,
|
||||
params: {
|
||||
item,
|
||||
item: { availableVersions, installedVersion },
|
||||
install,
|
||||
uninstall,
|
||||
selectedVersion,
|
||||
},
|
||||
} = this.props;
|
||||
const removeAction = {
|
||||
menuLabel: removeLabel,
|
||||
menuPath: ArduinoMenus.ARDUINO_COMPONENT__CONTEXT__ACTION_GROUP,
|
||||
handler: {
|
||||
execute: () => uninstall(item),
|
||||
},
|
||||
};
|
||||
const installAction = {
|
||||
menuLabel: installVersionLabel(
|
||||
selectedVersion ?? Installable.latest(availableVersions) ?? ''
|
||||
),
|
||||
menuPath: ArduinoMenus.ARDUINO_COMPONENT__CONTEXT__ACTION_GROUP,
|
||||
handler: {
|
||||
execute: () => install(item),
|
||||
},
|
||||
};
|
||||
const installLatestAction = {
|
||||
menuLabel: installLatestLabel,
|
||||
menuPath: ArduinoMenus.ARDUINO_COMPONENT__CONTEXT__ACTION_GROUP,
|
||||
handler: {
|
||||
execute: () => install(item),
|
||||
},
|
||||
};
|
||||
const updateAction = {
|
||||
menuLabel: updateLabel,
|
||||
menuPath: ArduinoMenus.ARDUINO_COMPONENT__CONTEXT__ACTION_GROUP,
|
||||
handler: {
|
||||
execute: () => install(item),
|
||||
},
|
||||
};
|
||||
switch (action) {
|
||||
case 'unknown':
|
||||
return [];
|
||||
case 'remove': {
|
||||
return [removeAction];
|
||||
}
|
||||
case 'update': {
|
||||
return [removeAction, updateAction];
|
||||
}
|
||||
case 'installLatest':
|
||||
return [
|
||||
...(Boolean(installedVersion) ? [removeAction] : []),
|
||||
installLatestAction,
|
||||
];
|
||||
case 'installSelected': {
|
||||
return [
|
||||
...(Boolean(installedVersion) ? [removeAction] : []),
|
||||
installAction,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Title<T extends ArduinoComponent> extends React.Component<
|
||||
Readonly<{
|
||||
params: ListItemRendererParams<T>;
|
||||
}>
|
||||
> {
|
||||
override render(): React.ReactNode {
|
||||
const { name, author } = this.props.params.item;
|
||||
const title =
|
||||
name && author ? nameAuthorLabel(name, author) : name ? name : Unknown;
|
||||
return (
|
||||
<div className="title" title={title}>
|
||||
{name && author ? (
|
||||
<>
|
||||
{<span className="name">{name}</span>}{' '}
|
||||
{<span className="author">{`${byLabel} ${author}`}</span>}
|
||||
</>
|
||||
) : name ? (
|
||||
<span className="name">{name}</span>
|
||||
) : (
|
||||
<span className="name">{Unknown}</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class InstalledVersion<T extends ArduinoComponent> extends React.Component<
|
||||
Readonly<{
|
||||
params: ListItemRendererParams<T>;
|
||||
}>
|
||||
> {
|
||||
private readonly onClick = (): void => {
|
||||
this.props.params.uninstall(this.props.params.item);
|
||||
};
|
||||
|
||||
override render(): React.ReactNode {
|
||||
const { installedVersion } = this.props.params.item;
|
||||
return (
|
||||
installedVersion && (
|
||||
<div className="version">
|
||||
<span
|
||||
className="installed-version"
|
||||
onClick={this.onClick}
|
||||
{...{
|
||||
version: installedLabel(installedVersion),
|
||||
remove: removeLabel,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Content<T extends ArduinoComponent> extends React.Component<
|
||||
Readonly<{
|
||||
params: ListItemRendererParams<T>;
|
||||
onMoreInfo: (href: string | undefined) => void;
|
||||
}>
|
||||
> {
|
||||
override render(): React.ReactNode {
|
||||
const {
|
||||
params: {
|
||||
item: { summary, description },
|
||||
},
|
||||
} = this.props;
|
||||
const content = [summary, description].filter(Boolean).join(' ');
|
||||
return (
|
||||
<div className="content" title={content}>
|
||||
<p>{content}</p>
|
||||
<MoreInfo {...this.props} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MoreInfo<T extends ArduinoComponent> extends React.Component<
|
||||
Readonly<{
|
||||
params: ListItemRendererParams<T>;
|
||||
onMoreInfo: (href: string | undefined) => void;
|
||||
}>
|
||||
> {
|
||||
private readonly onClick = (
|
||||
event: React.SyntheticEvent<HTMLAnchorElement, Event>
|
||||
): void => {
|
||||
const { target } = event.nativeEvent;
|
||||
if (target instanceof HTMLAnchorElement) {
|
||||
this.windowService.openNewWindow(target.href, { external: true });
|
||||
this.props.onMoreInfo(target.href);
|
||||
event.nativeEvent.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
renderItem(
|
||||
input: ComponentListItem.State & { item: T },
|
||||
install: (item: T) => Promise<void>,
|
||||
uninstall: (item: T) => Promise<void>,
|
||||
onVersionChange: (version: Installable.Version) => void
|
||||
): React.ReactNode {
|
||||
const { item } = input;
|
||||
let nameAndAuthor: JSX.Element;
|
||||
if (item.name && item.author) {
|
||||
const name = <span className="name">{item.name}</span>;
|
||||
const author = <span className="author">{item.author}</span>;
|
||||
nameAndAuthor = (
|
||||
<span>
|
||||
{name} {nls.localize('arduino/component/by', 'by')} {author}
|
||||
</span>
|
||||
override render(): React.ReactNode {
|
||||
const {
|
||||
params: {
|
||||
item: { moreInfoLink: href },
|
||||
},
|
||||
} = this.props;
|
||||
return (
|
||||
href && (
|
||||
<div className="info" title={clickToOpenInBrowserLabel(href)}>
|
||||
<a href={href} onClick={this.onClick}>
|
||||
{moreInfoLabel}
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
} else if (item.name) {
|
||||
nameAndAuthor = <span className="name">{item.name}</span>;
|
||||
} else if ((item as any).id) {
|
||||
nameAndAuthor = <span className="name">{(item as any).id}</span>;
|
||||
} else {
|
||||
nameAndAuthor = <span className="name">{Unknown}</span>;
|
||||
}
|
||||
const onClickUninstall = () => uninstall(item);
|
||||
const installedVersion = !!item.installedVersion && (
|
||||
<div className="version-info">
|
||||
<span className="version">
|
||||
{nls.localize(
|
||||
'arduino/component/version',
|
||||
'Version {0}',
|
||||
item.installedVersion
|
||||
)}
|
||||
</span>
|
||||
<span
|
||||
className="installed uppercase"
|
||||
onClick={onClickUninstall}
|
||||
{...{
|
||||
install: nls.localize('arduino/component/installed', 'Installed'),
|
||||
uninstall: nls.localize('arduino/component/uninstall', 'Uninstall'),
|
||||
}}
|
||||
/>
|
||||
}
|
||||
|
||||
class Footer<T extends ArduinoComponent> extends React.Component<
|
||||
Readonly<{
|
||||
params: ListItemRendererParams<T>;
|
||||
action: Installable.Action;
|
||||
}>
|
||||
> {
|
||||
override render(): React.ReactNode {
|
||||
return (
|
||||
<div className="footer">
|
||||
<SelectVersion {...this.props} />
|
||||
<Button {...this.props} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const summary = <div className="summary">{item.summary}</div>;
|
||||
const description = <div className="summary">{item.description}</div>;
|
||||
|
||||
const moreInfo = !!item.moreInfoLink && (
|
||||
<a href={item.moreInfoLink} onClick={this.onMoreInfoClick}>
|
||||
{nls.localize('arduino/component/moreInfo', 'More info')}
|
||||
</a>
|
||||
);
|
||||
const onClickInstall = () => install(item);
|
||||
const installButton = item.installable && (
|
||||
<button
|
||||
className="theia-button secondary install uppercase"
|
||||
onClick={onClickInstall}
|
||||
>
|
||||
{nls.localize('arduino/component/install', 'Install')}
|
||||
</button>
|
||||
);
|
||||
|
||||
const onSelectChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
class SelectVersion<T extends ArduinoComponent> extends React.Component<
|
||||
Readonly<{
|
||||
params: ListItemRendererParams<T>;
|
||||
action: Installable.Action;
|
||||
}>
|
||||
> {
|
||||
private readonly onChange = (
|
||||
event: React.ChangeEvent<HTMLSelectElement>
|
||||
): void => {
|
||||
const version = event.target.value;
|
||||
if (version) {
|
||||
onVersionChange(version);
|
||||
this.props.params.onVersionChange(version);
|
||||
}
|
||||
};
|
||||
|
||||
const versions = (() => {
|
||||
const { availableVersions } = item;
|
||||
if (availableVersions.length === 0) {
|
||||
return undefined;
|
||||
} else if (availableVersions.length === 1) {
|
||||
return <label>{availableVersions[0]}</label>;
|
||||
} else {
|
||||
override render(): React.ReactNode {
|
||||
const {
|
||||
selectedVersion,
|
||||
item: { availableVersions },
|
||||
} = this.props.params;
|
||||
switch (this.props.action) {
|
||||
case 'installLatest': // fall-through
|
||||
case 'installSelected': // fall-through
|
||||
case 'update': // fall-through
|
||||
case 'remove':
|
||||
return (
|
||||
<select
|
||||
className="theia-select"
|
||||
value={input.selectedVersion}
|
||||
onChange={onSelectChange}
|
||||
value={selectedVersion}
|
||||
onChange={this.onChange}
|
||||
>
|
||||
{item.availableVersions
|
||||
.filter((version) => version !== item.installedVersion) // Filter the version that is currently installed.
|
||||
.map((version) => (
|
||||
{availableVersions.map((version) => (
|
||||
<option value={version} key={version}>
|
||||
{version}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
case 'unknown':
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
class Button<T extends ArduinoComponent> extends React.Component<
|
||||
Readonly<{
|
||||
params: ListItemRendererParams<T>;
|
||||
action: Installable.Action;
|
||||
}>
|
||||
> {
|
||||
override render(): React.ReactNode {
|
||||
const {
|
||||
params: { item, install, uninstall, inProgress: state },
|
||||
} = this.props;
|
||||
const classNames = ['theia-button install uppercase'];
|
||||
let onClick;
|
||||
let label;
|
||||
switch (this.props.action) {
|
||||
case 'unknown':
|
||||
return undefined;
|
||||
case 'installLatest': {
|
||||
classNames.push('primary');
|
||||
label = installLabel;
|
||||
onClick = () => install(item);
|
||||
break;
|
||||
}
|
||||
case 'installSelected': {
|
||||
classNames.push('secondary');
|
||||
label = installLabel;
|
||||
onClick = () => install(item);
|
||||
break;
|
||||
}
|
||||
case 'update': {
|
||||
classNames.push('secondary');
|
||||
label = updateLabel;
|
||||
onClick = () => install(item);
|
||||
break;
|
||||
}
|
||||
case 'remove': {
|
||||
classNames.push('secondary', 'no-border');
|
||||
label = removeLabel;
|
||||
onClick = () => uninstall(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className="component-list-item noselect">
|
||||
<div className="header">
|
||||
{nameAndAuthor}
|
||||
{installedVersion}
|
||||
</div>
|
||||
<div className="content">
|
||||
{summary}
|
||||
{description}
|
||||
</div>
|
||||
<div className="info">{moreInfo}</div>
|
||||
<div className="footer">
|
||||
{versions}
|
||||
{installButton}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className={classNames.join(' ')}
|
||||
onClick={onClick}
|
||||
disabled={Boolean(state)}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -29,29 +29,27 @@ export abstract class ListWidget<
|
||||
> extends ReactWidget {
|
||||
@inject(MessageService)
|
||||
protected readonly messageService: MessageService;
|
||||
|
||||
@inject(CommandService)
|
||||
protected readonly commandService: CommandService;
|
||||
|
||||
@inject(ResponseServiceClient)
|
||||
protected readonly responseService: ResponseServiceClient;
|
||||
|
||||
@inject(NotificationCenter)
|
||||
protected readonly notificationCenter: NotificationCenter;
|
||||
@inject(CommandService)
|
||||
private readonly commandService: CommandService;
|
||||
@inject(ResponseServiceClient)
|
||||
private readonly responseService: ResponseServiceClient;
|
||||
|
||||
/**
|
||||
* Do not touch or use it. It is for setting the focus on the `input` after the widget activation.
|
||||
*/
|
||||
protected focusNode: HTMLElement | undefined;
|
||||
private focusNode: HTMLElement | undefined;
|
||||
private readonly didReceiveFirstFocus = new Deferred();
|
||||
protected readonly searchOptionsChangeEmitter = new Emitter<
|
||||
private readonly searchOptionsChangeEmitter = new Emitter<
|
||||
Partial<S> | undefined
|
||||
>();
|
||||
private readonly onDidShowEmitter = new Emitter<void>();
|
||||
/**
|
||||
* Instead of running an `update` from the `postConstruct` `init` method,
|
||||
* we use this variable to track first activate, then run.
|
||||
*/
|
||||
protected firstActivate = true;
|
||||
private firstUpdate = true;
|
||||
|
||||
constructor(protected options: ListWidget.Options<T, S>) {
|
||||
super();
|
||||
@@ -64,7 +62,10 @@ export abstract class ListWidget<
|
||||
this.addClass('arduino-list-widget');
|
||||
this.node.tabIndex = 0; // To be able to set the focus on the widget.
|
||||
this.scrollOptions = undefined;
|
||||
this.toDispose.push(this.searchOptionsChangeEmitter);
|
||||
this.toDispose.pushAll([
|
||||
this.searchOptionsChangeEmitter,
|
||||
this.onDidShowEmitter,
|
||||
]);
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
@@ -81,12 +82,14 @@ export abstract class ListWidget<
|
||||
protected override onAfterShow(message: Message): void {
|
||||
this.maybeUpdateOnFirstRender();
|
||||
super.onAfterShow(message);
|
||||
this.onDidShowEmitter.fire();
|
||||
}
|
||||
|
||||
private maybeUpdateOnFirstRender() {
|
||||
if (this.firstActivate) {
|
||||
this.firstActivate = false;
|
||||
if (this.firstUpdate) {
|
||||
this.firstUpdate = false;
|
||||
this.update();
|
||||
this.didReceiveFirstFocus.promise.then(() => this.focusNode?.focus());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +109,9 @@ export abstract class ListWidget<
|
||||
this.updateScrollBar();
|
||||
}
|
||||
|
||||
protected onFocusResolved = (element: HTMLElement | undefined): void => {
|
||||
private readonly onFocusResolved = (
|
||||
element: HTMLElement | undefined
|
||||
): void => {
|
||||
this.focusNode = element;
|
||||
this.didReceiveFirstFocus.resolve();
|
||||
};
|
||||
@@ -133,7 +138,7 @@ export abstract class ListWidget<
|
||||
return this.options.installable.uninstall({ item, progressId });
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
override render(): React.ReactNode {
|
||||
return (
|
||||
<FilterableListContainer<T, S>
|
||||
defaultSearchOptions={this.options.defaultSearchOptions}
|
||||
@@ -149,6 +154,7 @@ export abstract class ListWidget<
|
||||
messageService={this.messageService}
|
||||
commandService={this.commandService}
|
||||
responseService={this.responseService}
|
||||
onDidShow={this.onDidShowEmitter.event}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -186,3 +192,10 @@ export namespace ListWidget {
|
||||
readonly defaultSearchOptions: S;
|
||||
}
|
||||
}
|
||||
|
||||
export class UserAbortError extends Error {
|
||||
constructor(message = 'User abort') {
|
||||
super(message);
|
||||
Object.setPrototypeOf(this, UserAbortError.prototype);
|
||||
}
|
||||
}
|
||||
|
@@ -27,17 +27,14 @@ export namespace SketchbookCommands {
|
||||
|
||||
export const OPEN_SKETCHBOOK_CONTEXT_MENU: Command = {
|
||||
id: 'arduino-sketchbook--open-sketch-context-menu',
|
||||
label: 'Contextual menu',
|
||||
iconClass: 'sketchbook-tree__opts',
|
||||
};
|
||||
|
||||
export const SKETCHBOOK_HIDE_FILES: Command = {
|
||||
id: 'arduino-sketchbook--hide-files',
|
||||
label: 'Contextual menu',
|
||||
};
|
||||
|
||||
export const SKETCHBOOK_SHOW_FILES: Command = {
|
||||
id: 'arduino-sketchbook--show-files',
|
||||
label: 'Contextual menu',
|
||||
};
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ import {
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { TreeNode } from '@theia/core/lib/browser/tree/tree';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import { Command, CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import {
|
||||
NodeProps,
|
||||
TreeProps,
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
SketchesServiceClientImpl,
|
||||
} from '../../sketches-service-client-impl';
|
||||
import { SelectableTreeNode } from '@theia/core/lib/browser/tree/tree-selection';
|
||||
import { Sketch } from '../../contributions/contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
const customTreeProps: TreeProps = {
|
||||
@@ -91,8 +90,8 @@ export class SketchbookTreeWidget extends FileTreeWidget {
|
||||
node: TreeNode,
|
||||
props: NodeProps
|
||||
): React.ReactNode {
|
||||
if (SketchbookTree.SketchDirNode.is(node) || Sketch.isSketchFile(node.id)) {
|
||||
return <div className="sketch-folder-icon file-icon"></div>;
|
||||
if (SketchbookTree.SketchDirNode.is(node)) {
|
||||
return undefined;
|
||||
}
|
||||
const icon = this.toNodeIcon(node);
|
||||
if (icon) {
|
||||
@@ -116,7 +115,6 @@ export class SketchbookTreeWidget extends FileTreeWidget {
|
||||
protected hoveredNodeId: string | undefined;
|
||||
protected setHoverNodeId(id: string | undefined): void {
|
||||
this.hoveredNodeId = id;
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected override createNodeAttributes(
|
||||
@@ -134,26 +132,34 @@ export class SketchbookTreeWidget extends FileTreeWidget {
|
||||
protected renderInlineCommands(node: TreeNode): React.ReactNode {
|
||||
if (SketchbookTree.SketchDirNode.is(node) && node.commands) {
|
||||
return Array.from(new Set(node.commands)).map((command) =>
|
||||
this.renderInlineCommand(command.id, node)
|
||||
this.renderInlineCommand(command, node)
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected renderInlineCommand(
|
||||
commandId: string,
|
||||
command: Command | string | [command: string, label: string],
|
||||
node: SketchbookTree.SketchDirNode,
|
||||
options?: any
|
||||
): React.ReactNode {
|
||||
const command = this.commandRegistry.getCommand(commandId);
|
||||
const icon = command?.iconClass;
|
||||
const commandId = Command.is(command)
|
||||
? command.id
|
||||
: Array.isArray(command)
|
||||
? command[0]
|
||||
: command;
|
||||
const resolvedCommand = this.commandRegistry.getCommand(commandId);
|
||||
const icon = resolvedCommand?.iconClass;
|
||||
const args = { model: this.model, node: node, ...options };
|
||||
if (
|
||||
command &&
|
||||
resolvedCommand &&
|
||||
icon &&
|
||||
this.commandRegistry.isEnabled(commandId, args) &&
|
||||
this.commandRegistry.isVisible(commandId, args)
|
||||
) {
|
||||
const label = Array.isArray(command)
|
||||
? command[1]
|
||||
: resolvedCommand.label ?? resolvedCommand.id;
|
||||
const className = [
|
||||
TREE_NODE_SEGMENT_CLASS,
|
||||
TREE_NODE_TAIL_CLASS,
|
||||
@@ -165,7 +171,7 @@ export class SketchbookTreeWidget extends FileTreeWidget {
|
||||
<div
|
||||
key={`${commandId}--${node.id}`}
|
||||
className={className}
|
||||
title={command?.label || command.id}
|
||||
title={label}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
@@ -9,6 +9,7 @@ import {
|
||||
WorkspaceRootNode,
|
||||
} from '@theia/navigator/lib/browser/navigator-tree';
|
||||
import { ArduinoPreferences } from '../../arduino-preferences';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
|
||||
@injectable()
|
||||
export class SketchbookTree extends FileNavigatorTree {
|
||||
@@ -18,7 +19,9 @@ export class SketchbookTree extends FileNavigatorTree {
|
||||
@inject(ArduinoPreferences)
|
||||
protected readonly arduinoPreferences: ArduinoPreferences;
|
||||
|
||||
override async resolveChildren(parent: CompositeTreeNode): Promise<TreeNode[]> {
|
||||
override async resolveChildren(
|
||||
parent: CompositeTreeNode
|
||||
): Promise<TreeNode[]> {
|
||||
const showAllFiles =
|
||||
this.arduinoPreferences['arduino.sketchbook.showAllFiles'];
|
||||
|
||||
@@ -71,7 +74,13 @@ export class SketchbookTree extends FileNavigatorTree {
|
||||
protected async augmentSketchNode(node: DirNode): Promise<void> {
|
||||
Object.assign(node, {
|
||||
type: 'sketch',
|
||||
commands: [SketchbookCommands.OPEN_SKETCHBOOK_CONTEXT_MENU],
|
||||
commands: [
|
||||
[
|
||||
'arduino-create-cloud-copy',
|
||||
nls.localize('arduino/createCloudCopy', 'Push Sketch to Cloud'),
|
||||
],
|
||||
SketchbookCommands.OPEN_SKETCHBOOK_CONTEXT_MENU,
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -96,7 +105,10 @@ export class SketchbookTree extends FileNavigatorTree {
|
||||
export namespace SketchbookTree {
|
||||
export interface SketchDirNode extends DirNode {
|
||||
readonly type: 'sketch';
|
||||
readonly commands?: Command[];
|
||||
/**
|
||||
* Theia command, the command ID string, or a tuple of command ID and preferred UI label. If the array construct is used, the label is the 1<sup>st</sup> of the array.
|
||||
*/
|
||||
readonly commands?: (Command | string | [string, string])[];
|
||||
}
|
||||
export namespace SketchDirNode {
|
||||
export function is(
|
||||
|
@@ -106,7 +106,7 @@ export class SketchbookWidgetContribution
|
||||
this.revealSketchNode(treeWidgetId, nodeUri),
|
||||
});
|
||||
registry.registerCommand(SketchbookCommands.OPEN_NEW_WINDOW, {
|
||||
execute: (arg) => this.openNewWindow(arg.node),
|
||||
execute: (arg) => this.openNewWindow(arg.node, arg?.treeWidgetId),
|
||||
isEnabled: (arg) =>
|
||||
!!arg && 'node' in arg && SketchbookTree.SketchDirNode.is(arg.node),
|
||||
isVisible: (arg) =>
|
||||
@@ -209,14 +209,20 @@ export class SketchbookWidgetContribution
|
||||
});
|
||||
}
|
||||
|
||||
private openNewWindow(node: SketchbookTree.SketchDirNode): void {
|
||||
const widget = this.tryGetWidget();
|
||||
if (widget) {
|
||||
const treeWidgetId = widget.activeTreeWidgetId();
|
||||
private openNewWindow(
|
||||
node: SketchbookTree.SketchDirNode,
|
||||
treeWidgetId?: string
|
||||
): void {
|
||||
if (!treeWidgetId) {
|
||||
const widget = this.tryGetWidget();
|
||||
if (!widget) {
|
||||
console.warn(`Could not retrieve active sketchbook tree ID.`);
|
||||
return;
|
||||
}
|
||||
treeWidgetId = widget.activeTreeWidgetId();
|
||||
}
|
||||
const widget = this.tryGetWidget();
|
||||
if (widget) {
|
||||
const nodeUri = node.uri.toString();
|
||||
const options: WorkspaceInput = {};
|
||||
Object.assign(options, {
|
||||
|
@@ -1,34 +1,35 @@
|
||||
import { Installable } from './installable';
|
||||
import type { Installable } from './installable';
|
||||
|
||||
export interface ArduinoComponent {
|
||||
readonly name: string;
|
||||
readonly deprecated?: boolean;
|
||||
readonly author: string;
|
||||
readonly summary: string;
|
||||
readonly description: string;
|
||||
readonly moreInfoLink?: string;
|
||||
readonly availableVersions: Installable.Version[];
|
||||
readonly installable: boolean;
|
||||
readonly installedVersion?: Installable.Version;
|
||||
/**
|
||||
* This is the `Type` in IDE (1.x) UI.
|
||||
*/
|
||||
readonly types: string[];
|
||||
readonly deprecated?: boolean;
|
||||
readonly moreInfoLink?: string;
|
||||
}
|
||||
export namespace ArduinoComponent {
|
||||
export function is(arg: any): arg is ArduinoComponent {
|
||||
export function is(arg: unknown): arg is ArduinoComponent {
|
||||
return (
|
||||
!!arg &&
|
||||
'name' in arg &&
|
||||
typeof arg['name'] === 'string' &&
|
||||
'author' in arg &&
|
||||
typeof arg['author'] === 'string' &&
|
||||
'summary' in arg &&
|
||||
typeof arg['summary'] === 'string' &&
|
||||
'description' in arg &&
|
||||
typeof arg['description'] === 'string' &&
|
||||
'installable' in arg &&
|
||||
typeof arg['installable'] === 'boolean'
|
||||
typeof arg === 'object' &&
|
||||
(<ArduinoComponent>arg).name !== undefined &&
|
||||
typeof (<ArduinoComponent>arg).name === 'string' &&
|
||||
(<ArduinoComponent>arg).author !== undefined &&
|
||||
typeof (<ArduinoComponent>arg).author === 'string' &&
|
||||
(<ArduinoComponent>arg).summary !== undefined &&
|
||||
typeof (<ArduinoComponent>arg).summary === 'string' &&
|
||||
(<ArduinoComponent>arg).description !== undefined &&
|
||||
typeof (<ArduinoComponent>arg).description === 'string' &&
|
||||
(<ArduinoComponent>arg).availableVersions !== undefined &&
|
||||
Array.isArray((<ArduinoComponent>arg).availableVersions) &&
|
||||
(<ArduinoComponent>arg).types !== undefined &&
|
||||
Array.isArray((<ArduinoComponent>arg).types)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -245,6 +245,7 @@ export interface Port {
|
||||
readonly protocol: string;
|
||||
readonly protocolLabel: string;
|
||||
readonly properties?: Record<string, string>;
|
||||
readonly hardwareId?: string;
|
||||
}
|
||||
export namespace Port {
|
||||
export type Properties = Record<string, string>;
|
||||
@@ -553,6 +554,19 @@ export namespace Board {
|
||||
return left.name === right.name && left.fqbn === right.fqbn;
|
||||
}
|
||||
|
||||
export function hardwareIdEquals(left: Board, right: Board): boolean {
|
||||
if (left.port && right.port) {
|
||||
const { hardwareId: leftHardwareId } = left.port;
|
||||
const { hardwareId: rightHardwareId } = right.port;
|
||||
|
||||
if (leftHardwareId && rightHardwareId) {
|
||||
return leftHardwareId === rightHardwareId;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function sameAs(left: Board, right: string | Board): boolean {
|
||||
// How to associate a selected board with one of the available cores: https://typefox.slack.com/archives/CJJHJCJSJ/p1571142327059200
|
||||
// 1. How to use the FQBN if any and infer the package ID from it: https://typefox.slack.com/archives/CJJHJCJSJ/p1571147549069100
|
||||
|
@@ -9,4 +9,8 @@ export interface ExamplesService {
|
||||
current: SketchContainer[];
|
||||
any: SketchContainer[];
|
||||
}>;
|
||||
/**
|
||||
* Finds example sketch containers for the installed library.
|
||||
*/
|
||||
find(options: { libraryName: string }): Promise<SketchContainer[]>;
|
||||
}
|
||||
|
@@ -51,6 +51,46 @@ export namespace Installable {
|
||||
};
|
||||
}
|
||||
|
||||
export const ActionLiterals = [
|
||||
'installLatest',
|
||||
'installSelected',
|
||||
'update',
|
||||
'remove',
|
||||
'unknown',
|
||||
] as const;
|
||||
export type Action = typeof ActionLiterals[number];
|
||||
|
||||
export function action(params: {
|
||||
installed?: Version | undefined;
|
||||
available: Version[];
|
||||
selected?: Version;
|
||||
}): Action {
|
||||
const { installed, available } = params;
|
||||
const latest = Installable.latest(available);
|
||||
if (!latest || (installed && !available.includes(installed))) {
|
||||
return 'unknown';
|
||||
}
|
||||
const selected = params.selected ?? latest;
|
||||
if (installed === selected) {
|
||||
return 'remove';
|
||||
}
|
||||
if (installed) {
|
||||
return selected === latest && installed !== latest
|
||||
? 'update'
|
||||
: 'installSelected';
|
||||
} else {
|
||||
return selected === latest ? 'installLatest' : 'installSelected';
|
||||
}
|
||||
}
|
||||
|
||||
export function latest(versions: Version[]): Version | undefined {
|
||||
if (!versions.length) {
|
||||
return undefined;
|
||||
}
|
||||
const ordered = versions.slice().sort(Installable.Version.COMPARATOR);
|
||||
return ordered[ordered.length - 1];
|
||||
}
|
||||
|
||||
export const Installed = <T extends ArduinoComponent>({
|
||||
installedVersion,
|
||||
}: T): boolean => {
|
||||
|
@@ -198,6 +198,10 @@ export namespace LibraryService {
|
||||
export namespace List {
|
||||
export interface Options {
|
||||
readonly fqbn?: string | undefined;
|
||||
/**
|
||||
* The name of the library to filter to.
|
||||
*/
|
||||
readonly libraryName?: string | undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -241,11 +245,15 @@ export interface LibraryPackage extends ArduinoComponent {
|
||||
readonly category: string;
|
||||
}
|
||||
export namespace LibraryPackage {
|
||||
export function is(arg: any): arg is LibraryPackage {
|
||||
export function is(arg: unknown): arg is LibraryPackage {
|
||||
return (
|
||||
ArduinoComponent.is(arg) &&
|
||||
'includes' in arg &&
|
||||
Array.isArray(arg['includes'])
|
||||
(<LibraryPackage>arg).includes !== undefined &&
|
||||
Array.isArray((<LibraryPackage>arg).includes) &&
|
||||
(<LibraryPackage>arg).exampleUris !== undefined &&
|
||||
Array.isArray((<LibraryPackage>arg).exampleUris) &&
|
||||
(<LibraryPackage>arg).location !== undefined &&
|
||||
typeof (<LibraryPackage>arg).location === 'number'
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -74,12 +74,15 @@ export interface SketchesService {
|
||||
isTemp(sketch: SketchRef): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* If `isTemp` is `true` for the `sketch`, you can call this method to move the sketch from the temp
|
||||
* location to `directories.user`. Resolves with the URI of the sketch after the move. Rejects, when the sketch
|
||||
* was not in the temp folder. This method always overrides. It's the callers responsibility to ask the user whether
|
||||
* the files at the destination can be overwritten or not.
|
||||
* Recursively copies the sketch folder content including all files into the destination folder.
|
||||
* Resolves with the new URI of the sketch after the move. This method always overrides. It's the callers responsibility to ask the user whether
|
||||
* the files at the destination can be overwritten or not. This method copies all filesystem files, if you want to copy only sketch files,
|
||||
* but exclude, for example, language server log file, set the `onlySketchFiles` property to `true`. `onlySketchFiles` is `false` by default.
|
||||
*/
|
||||
copy(sketch: Sketch, options: { destinationUri: string }): Promise<string>;
|
||||
copy(
|
||||
sketch: Sketch,
|
||||
options: { destinationUri: string; onlySketchFiles?: boolean }
|
||||
): Promise<Sketch>;
|
||||
|
||||
/**
|
||||
* Returns with the container sketch for the input `uri`. If the `uri` is not in a sketch folder, the promise resolves to `undefined`.
|
||||
@@ -157,8 +160,6 @@ export namespace Sketch {
|
||||
// (non-API) exported for the tests
|
||||
export const defaultSketchFolderName = 'sketch';
|
||||
// (non-API) exported for the tests
|
||||
export const defaultFallbackFirstChar = '0';
|
||||
// (non-API) exported for the tests
|
||||
export const defaultFallbackChar = '_';
|
||||
// (non-API) exported for the tests
|
||||
export function reservedFilename(name: string): string {
|
||||
@@ -176,11 +177,11 @@ export namespace Sketch {
|
||||
// (non-API) exported for the tests
|
||||
export const invalidSketchFolderNameMessage = nls.localize(
|
||||
'arduino/sketch/invalidSketchName',
|
||||
'The name must start with a letter or number, followed by letters, numbers, dashes, dots and underscores. Maximum length is 63 characters.'
|
||||
'The name must start with a letter, number, or underscore, followed by letters, numbers, dashes, dots and underscores. Maximum length is 63 characters.'
|
||||
);
|
||||
const invalidCloudSketchFolderNameMessage = nls.localize(
|
||||
'arduino/sketch/invalidCloudSketchName',
|
||||
'The name must start with a letter or number, followed by letters, numbers, dashes, dots and underscores. Maximum length is 36 characters.'
|
||||
'The name must start with a letter, number, or underscore, followed by letters, numbers, dashes, dots and underscores. Maximum length is 36 characters.'
|
||||
);
|
||||
/**
|
||||
* `undefined` if the candidate sketch folder name is valid. Otherwise, the validation error message.
|
||||
@@ -193,7 +194,7 @@ export namespace Sketch {
|
||||
if (validFilenameError) {
|
||||
return validFilenameError;
|
||||
}
|
||||
return /^[0-9a-zA-Z]{1}[0-9a-zA-Z_\.-]{0,62}$/.test(candidate)
|
||||
return /^[0-9a-zA-Z_]{1}[0-9a-zA-Z_\.-]{0,62}$/.test(candidate)
|
||||
? undefined
|
||||
: invalidSketchFolderNameMessage;
|
||||
}
|
||||
@@ -208,7 +209,7 @@ export namespace Sketch {
|
||||
if (validFilenameError) {
|
||||
return validFilenameError;
|
||||
}
|
||||
return /^[0-9a-zA-Z]{1}[0-9a-zA-Z_\.-]{0,35}$/.test(candidate)
|
||||
return /^[0-9a-zA-Z_]{1}[0-9a-zA-Z_\.-]{0,35}$/.test(candidate)
|
||||
? undefined
|
||||
: invalidCloudSketchFolderNameMessage;
|
||||
}
|
||||
@@ -252,10 +253,7 @@ export namespace Sketch {
|
||||
return defaultSketchFolderName;
|
||||
}
|
||||
const validName = candidate
|
||||
? candidate
|
||||
.replace(/^[^0-9a-zA-Z]{1}/g, defaultFallbackFirstChar)
|
||||
.replace(/[^0-9a-zA-Z_]/g, defaultFallbackChar)
|
||||
.slice(0, 63)
|
||||
? candidate.replace(/[^0-9a-zA-Z_]/g, defaultFallbackChar).slice(0, 63)
|
||||
: defaultSketchFolderName;
|
||||
if (appendTimestampSuffix) {
|
||||
return `${validName.slice(0, 63 - timestampSuffixLength)}${
|
||||
@@ -283,10 +281,7 @@ export namespace Sketch {
|
||||
return defaultSketchFolderName;
|
||||
}
|
||||
return candidate
|
||||
? candidate
|
||||
.replace(/^[^0-9a-zA-Z]{1}/g, defaultFallbackFirstChar)
|
||||
.replace(/[^0-9a-zA-Z_]/g, defaultFallbackChar)
|
||||
.slice(0, 36)
|
||||
? candidate.replace(/[^0-9a-zA-Z_]/g, defaultFallbackChar).slice(0, 36)
|
||||
: defaultSketchFolderName;
|
||||
}
|
||||
|
||||
|
@@ -20,3 +20,21 @@ export function startsWithUpperCase(what: string): boolean {
|
||||
export function isNullOrUndefined(what: unknown): what is undefined | null {
|
||||
return what === undefined || what === null;
|
||||
}
|
||||
|
||||
// Use it for and exhaustive `switch` statements
|
||||
// https://stackoverflow.com/a/39419171/5529090
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export function assertUnreachable(_: never): never {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
// Text encoder can crash in electron browser: https://github.com/arduino/arduino-ide/issues/634#issuecomment-1440039171
|
||||
export function uint8ArrayToString(uint8Array: Uint8Array): string {
|
||||
return uint8Array.reduce(
|
||||
(text, byte) => text + String.fromCharCode(byte),
|
||||
''
|
||||
);
|
||||
}
|
||||
export function stringToUint8Array(text: string): Uint8Array {
|
||||
return Uint8Array.from(text, (char) => char.charCodeAt(0));
|
||||
}
|
||||
|
@@ -1,10 +1,13 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { NativeImage } from '@theia/core/electron-shared/electron';
|
||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import {
|
||||
ActionMenuNode,
|
||||
CommandMenuNode,
|
||||
CompoundMenuNode,
|
||||
CompoundMenuNodeRole,
|
||||
MAIN_MENU_BAR,
|
||||
MenuNode,
|
||||
MenuPath,
|
||||
} from '@theia/core/lib/common/menu';
|
||||
@@ -38,9 +41,18 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
||||
});
|
||||
}
|
||||
|
||||
override createElectronMenuBar(): Electron.Menu | null {
|
||||
override createElectronMenuBar(): Electron.Menu {
|
||||
this._toggledCommands.clear(); // https://github.com/eclipse-theia/theia/issues/8977
|
||||
return super.createElectronMenuBar();
|
||||
const menuModel = this.menuProvider.getMenu(MAIN_MENU_BAR);
|
||||
const template = this.fillMenuTemplate([], menuModel, [], {
|
||||
rootMenuPath: MAIN_MENU_BAR,
|
||||
});
|
||||
if (isOSX) {
|
||||
template.unshift(this.createOSXMenu());
|
||||
}
|
||||
const menu = remote.Menu.buildFromTemplate(this.escapeAmpersand(template));
|
||||
this._menu = menu;
|
||||
return menu;
|
||||
}
|
||||
|
||||
override async setMenuBar(): Promise<void> {
|
||||
@@ -51,7 +63,13 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
||||
this.updateWhenReady = true;
|
||||
return;
|
||||
}
|
||||
return super.setMenuBar();
|
||||
await this.preferencesService.ready;
|
||||
const createdMenuBar = this.createElectronMenuBar();
|
||||
if (isOSX) {
|
||||
remote.Menu.setApplicationMenu(createdMenuBar);
|
||||
} else {
|
||||
remote.getCurrentWindow().setMenu(createdMenuBar);
|
||||
}
|
||||
}
|
||||
|
||||
override createElectronContextMenu(
|
||||
@@ -262,6 +280,12 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
||||
delete menuItem.click;
|
||||
}
|
||||
}
|
||||
|
||||
// Native image customization for IDE2
|
||||
if (isMenuNodeWithNativeImage(node)) {
|
||||
menuItem.icon = node.action.nativeImage;
|
||||
}
|
||||
|
||||
parentItems.push(menuItem);
|
||||
|
||||
if (this.commandRegistry.getToggledHandler(commandId, ...args)) {
|
||||
@@ -298,3 +322,23 @@ const AlwaysVisibleSubmenus: MenuPath[] = [
|
||||
ArduinoMenus.TOOLS__PORTS_SUBMENU, // #655
|
||||
ArduinoMenus.FILE__SKETCHBOOK_SUBMENU, // #569
|
||||
];
|
||||
|
||||
// Theia does not support icons for electron menu items.
|
||||
// This is a hack to show a cloud icon as a native image for the cloud sketches in `File` > `Open Recent` menu.
|
||||
type MenuNodeWithNativeImage = MenuNode & {
|
||||
action: ActionMenuNode & { nativeImage: NativeImage };
|
||||
};
|
||||
type ActionMenuNodeWithNativeImage = ActionMenuNode & {
|
||||
nativeImage: NativeImage;
|
||||
};
|
||||
function isMenuNodeWithNativeImage(
|
||||
node: MenuNode
|
||||
): node is MenuNodeWithNativeImage {
|
||||
if (node instanceof ActionMenuNode) {
|
||||
const action: unknown = node['action'];
|
||||
if ((<ActionMenuNodeWithNativeImage>action).nativeImage !== undefined) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@@ -1,38 +1,53 @@
|
||||
import {
|
||||
getCurrentWebContents,
|
||||
getCurrentWindow,
|
||||
} from '@theia/core/electron-shared/@electron/remote';
|
||||
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||
import { KeybindingRegistry } from '@theia/core/lib/browser/keybinding';
|
||||
import { PreferenceScope } from '@theia/core/lib/browser/preferences/preference-scope';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||
import { KeybindingRegistry } from '@theia/core/lib/browser/keybinding';
|
||||
import {
|
||||
ElectronCommands,
|
||||
ElectronMenuContribution as TheiaElectronMenuContribution,
|
||||
ElectronCommands,
|
||||
} from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
|
||||
import { ZoomLevel } from '@theia/core/lib/electron-browser/window/electron-window-preferences';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { MainMenuManager } from '../../../common/main-menu-manager';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||
import { ZoomLevel } from '@theia/core/lib/electron-browser/window/electron-window-preferences';
|
||||
import { PreferenceScope } from '@theia/core/lib/browser/preferences/preference-scope';
|
||||
import {
|
||||
getCurrentWindow,
|
||||
getCurrentWebContents,
|
||||
} from '@theia/core/electron-shared/@electron/remote';
|
||||
|
||||
@injectable()
|
||||
export class ElectronMenuContribution
|
||||
extends TheiaElectronMenuContribution
|
||||
implements MainMenuManager
|
||||
{
|
||||
private app: FrontendApplication;
|
||||
@inject(FrontendApplicationStateService)
|
||||
private readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
// private appReady = false;
|
||||
// private updateWhenReady = false;
|
||||
|
||||
override onStart(app: FrontendApplication): void {
|
||||
this.app = app;
|
||||
super.onStart(app);
|
||||
this.appStateService.reachedState('ready').then(() => {
|
||||
// this.appReady = true;
|
||||
// if (this.updateWhenReady) {
|
||||
// this.update();
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
protected override hideTopPanel(): void {
|
||||
// NOOP
|
||||
// We reuse the `div` for the Arduino toolbar.
|
||||
}
|
||||
|
||||
update(): void {
|
||||
// no menu updates before `onStart`
|
||||
if (!this.app) {
|
||||
return;
|
||||
}
|
||||
this.setMenu(this.app);
|
||||
// if (this.appReady) {
|
||||
(this as any).setMenu();
|
||||
// } else {
|
||||
// this.updateWhenReady = true;
|
||||
// }
|
||||
}
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
|
@@ -19,13 +19,8 @@ import {
|
||||
} from '@theia/core/lib/electron-main/electron-main-application';
|
||||
import { URI } from '@theia/core/shared/vscode-uri';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import {
|
||||
RequestTitleBarStyle,
|
||||
Restart,
|
||||
TitleBarStyleAtStartup,
|
||||
TitleBarStyleChanged,
|
||||
} from '@theia/core/lib/electron-common/messaging/electron-messages';
|
||||
import * as os from '@theia/core/lib/common/os';
|
||||
import { Restart } from '@theia/core/lib/electron-common/messaging/electron-messages';
|
||||
import { TheiaBrowserWindowOptions } from '@theia/core/lib/electron-main/theia-electron-window';
|
||||
import { IsTempSketch } from '../../node/is-temp-sketch';
|
||||
import {
|
||||
@@ -181,7 +176,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
|
||||
private attachFileAssociations(cwd: string): void {
|
||||
// OSX: register open-file event
|
||||
if (isOSX) {
|
||||
if (os.isOSX) {
|
||||
app.on('open-file', async (event, path) => {
|
||||
event.preventDefault();
|
||||
const resolvedPath = await this.resolvePath(path, cwd);
|
||||
@@ -335,19 +330,10 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
}
|
||||
|
||||
protected override getTitleBarStyle(
|
||||
config: FrontendApplicationConfig
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_config: FrontendApplicationConfig
|
||||
): 'native' | 'custom' {
|
||||
const storedFrame = this.electronStore.get('windowstate')?.frame;
|
||||
if (storedFrame !== undefined) {
|
||||
return !!storedFrame ? 'native' : 'custom';
|
||||
}
|
||||
if (config.preferences && config.preferences['window.titleBarStyle']) {
|
||||
const titleBarStyle = config.preferences['window.titleBarStyle'];
|
||||
if (titleBarStyle === 'native' || titleBarStyle === 'custom') {
|
||||
return titleBarStyle;
|
||||
}
|
||||
}
|
||||
return 'custom';
|
||||
return 'native';
|
||||
}
|
||||
|
||||
protected override hookApplicationEvents(): void {
|
||||
@@ -365,21 +351,6 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
this.delete(sketch);
|
||||
}
|
||||
});
|
||||
ipcMain.on(TitleBarStyleChanged, ({ sender }, titleBarStyle: string) => {
|
||||
this.useNativeWindowFrame = isOSX || titleBarStyle === 'native';
|
||||
const browserWindow = BrowserWindow.fromId(sender.id);
|
||||
if (browserWindow) {
|
||||
this.saveWindowState(browserWindow);
|
||||
} else {
|
||||
console.warn(`no BrowserWindow with id: ${sender.id}`);
|
||||
}
|
||||
});
|
||||
ipcMain.on(RequestTitleBarStyle, ({ sender }) => {
|
||||
sender.send(
|
||||
TitleBarStyleAtStartup,
|
||||
this.didUseNativeWindowFrameOnStart.get(sender.id) ? 'native' : 'custom'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
protected override async onSecondInstance(
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import { join } from 'path';
|
||||
import { promises as fs } from 'fs';
|
||||
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
||||
import { spawn, ChildProcess } from 'child_process';
|
||||
import { FileUri } from '@theia/core/lib/node/file-uri';
|
||||
@@ -16,7 +15,7 @@ import { BackendApplicationContribution } from '@theia/core/lib/node/backend-app
|
||||
import { ArduinoDaemon, NotificationServiceServer } from '../common/protocol';
|
||||
import { CLI_CONFIG } from './cli-config';
|
||||
import { getExecPath } from './exec-util';
|
||||
import { ErrnoException } from './utils/errors';
|
||||
import { SettingsReader } from './settings-reader';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoDaemonImpl
|
||||
@@ -32,6 +31,9 @@ export class ArduinoDaemonImpl
|
||||
@inject(NotificationServiceServer)
|
||||
private readonly notificationService: NotificationServiceServer;
|
||||
|
||||
@inject(SettingsReader)
|
||||
private readonly settingsReader: SettingsReader;
|
||||
|
||||
private readonly toDispose = new DisposableCollection();
|
||||
private readonly onDaemonStartedEmitter = new Emitter<string>();
|
||||
private readonly onDaemonStoppedEmitter = new Emitter<void>();
|
||||
@@ -134,8 +136,6 @@ export class ArduinoDaemonImpl
|
||||
const cliConfigPath = join(FileUri.fsPath(configDirUri), CLI_CONFIG);
|
||||
const args = [
|
||||
'daemon',
|
||||
'--format',
|
||||
'jsonmini',
|
||||
'--port',
|
||||
'0',
|
||||
'--config-file',
|
||||
@@ -149,34 +149,12 @@ export class ArduinoDaemonImpl
|
||||
}
|
||||
|
||||
private async debugDaemon(): Promise<boolean> {
|
||||
// Poor man's preferences on the backend. (https://github.com/arduino/arduino-ide/issues/1056#issuecomment-1153975064)
|
||||
const configDirUri = await this.envVariablesServer.getConfigDirUri();
|
||||
const configDirPath = FileUri.fsPath(configDirUri);
|
||||
try {
|
||||
const raw = await fs.readFile(join(configDirPath, 'settings.json'), {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
const json = this.tryParse(raw);
|
||||
if (json) {
|
||||
const value = json['arduino.cli.daemon.debug'];
|
||||
return typeof value === 'boolean' && !!value;
|
||||
const settings = await this.settingsReader.read();
|
||||
if (settings) {
|
||||
const value = settings['arduino.cli.daemon.debug'];
|
||||
return value === true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
if (ErrnoException.isENOENT(error)) {
|
||||
return false;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private tryParse(raw: string): any | undefined {
|
||||
try {
|
||||
return JSON.parse(raw);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
protected async spawnDaemonProcess(): Promise<{
|
||||
@@ -197,6 +175,13 @@ export class ArduinoDaemonImpl
|
||||
|
||||
daemon.stdout.on('data', (data) => {
|
||||
const message = data.toString();
|
||||
this.onData(message);
|
||||
if (!grpcServerIsReady) {
|
||||
const error = DaemonError.parse(message);
|
||||
if (error) {
|
||||
ready.reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
let port = '';
|
||||
let address = '';
|
||||
@@ -217,14 +202,6 @@ export class ArduinoDaemonImpl
|
||||
}
|
||||
});
|
||||
|
||||
this.onData(message);
|
||||
if (!grpcServerIsReady) {
|
||||
const error = DaemonError.parse(message);
|
||||
if (error) {
|
||||
ready.reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (port.length && address.length) {
|
||||
grpcServerIsReady = true;
|
||||
ready.resolve({ daemon, port });
|
||||
|
@@ -118,6 +118,8 @@ import {
|
||||
LocalDirectoryPluginDeployerResolverWithFallback,
|
||||
PluginDeployer_GH_12064,
|
||||
} from './theia/plugin-ext/plugin-deployer';
|
||||
import { SettingsReader } from './settings-reader';
|
||||
import { NativeImageDataProvider } from './native-image-data-provider';
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(BackendApplication).toSelf().inSingletonScope();
|
||||
@@ -403,6 +405,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
.toSelf()
|
||||
.inSingletonScope();
|
||||
rebind(PluginDeployer).to(PluginDeployer_GH_12064).inSingletonScope();
|
||||
|
||||
bind(SettingsReader).toSelf().inSingletonScope();
|
||||
// to serve native images for the electron menus
|
||||
bind(NativeImageDataProvider).toSelf().inSingletonScope();
|
||||
bind(BackendApplicationContribution).toService(NativeImageDataProvider);
|
||||
});
|
||||
|
||||
function bindChildLogger(bind: interfaces.Bind, name: string): void {
|
||||
|
@@ -323,14 +323,14 @@ export class BoardDiscovery
|
||||
}
|
||||
|
||||
private fromRpcPort(rpcPort: RpcPort): Port {
|
||||
const port = {
|
||||
return {
|
||||
address: rpcPort.getAddress(),
|
||||
addressLabel: rpcPort.getLabel(),
|
||||
protocol: rpcPort.getProtocol(),
|
||||
protocolLabel: rpcPort.getProtocolLabel(),
|
||||
properties: Port.Properties.create(rpcPort.getPropertiesMap().toObject()),
|
||||
hardwareId: rpcPort.getHardwareId(),
|
||||
};
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -388,66 +388,6 @@ export namespace ConfigValue {
|
||||
}
|
||||
}
|
||||
|
||||
export class BoardAttachRequest extends jspb.Message {
|
||||
|
||||
hasInstance(): boolean;
|
||||
clearInstance(): void;
|
||||
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
|
||||
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): BoardAttachRequest;
|
||||
|
||||
getBoardUri(): string;
|
||||
setBoardUri(value: string): BoardAttachRequest;
|
||||
|
||||
getSketchPath(): string;
|
||||
setSketchPath(value: string): BoardAttachRequest;
|
||||
|
||||
getSearchTimeout(): string;
|
||||
setSearchTimeout(value: string): BoardAttachRequest;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): BoardAttachRequest.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: BoardAttachRequest): BoardAttachRequest.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: BoardAttachRequest, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): BoardAttachRequest;
|
||||
static deserializeBinaryFromReader(message: BoardAttachRequest, reader: jspb.BinaryReader): BoardAttachRequest;
|
||||
}
|
||||
|
||||
export namespace BoardAttachRequest {
|
||||
export type AsObject = {
|
||||
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
|
||||
boardUri: string,
|
||||
sketchPath: string,
|
||||
searchTimeout: string,
|
||||
}
|
||||
}
|
||||
|
||||
export class BoardAttachResponse extends jspb.Message {
|
||||
|
||||
hasTaskProgress(): boolean;
|
||||
clearTaskProgress(): void;
|
||||
getTaskProgress(): cc_arduino_cli_commands_v1_common_pb.TaskProgress | undefined;
|
||||
setTaskProgress(value?: cc_arduino_cli_commands_v1_common_pb.TaskProgress): BoardAttachResponse;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): BoardAttachResponse.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: BoardAttachResponse): BoardAttachResponse.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: BoardAttachResponse, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): BoardAttachResponse;
|
||||
static deserializeBinaryFromReader(message: BoardAttachResponse, reader: jspb.BinaryReader): BoardAttachResponse;
|
||||
}
|
||||
|
||||
export namespace BoardAttachResponse {
|
||||
export type AsObject = {
|
||||
taskProgress?: cc_arduino_cli_commands_v1_common_pb.TaskProgress.AsObject,
|
||||
}
|
||||
}
|
||||
|
||||
export class BoardListRequest extends jspb.Message {
|
||||
|
||||
hasInstance(): boolean;
|
||||
@@ -458,6 +398,9 @@ export class BoardListRequest extends jspb.Message {
|
||||
getTimeout(): number;
|
||||
setTimeout(value: number): BoardListRequest;
|
||||
|
||||
getFqbn(): string;
|
||||
setFqbn(value: string): BoardListRequest;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): BoardListRequest.AsObject;
|
||||
@@ -473,6 +416,7 @@ export namespace BoardListRequest {
|
||||
export type AsObject = {
|
||||
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
|
||||
timeout: number,
|
||||
fqbn: string,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -19,8 +19,6 @@ var cc_arduino_cli_commands_v1_common_pb = require('../../../../../cc/arduino/cl
|
||||
goog.object.extend(proto, cc_arduino_cli_commands_v1_common_pb);
|
||||
var cc_arduino_cli_commands_v1_port_pb = require('../../../../../cc/arduino/cli/commands/v1/port_pb.js');
|
||||
goog.object.extend(proto, cc_arduino_cli_commands_v1_port_pb);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.BoardAttachRequest', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.BoardAttachResponse', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.BoardDetailsRequest', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.BoardDetailsResponse', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.BoardIdentificationProperties', null, global);
|
||||
@@ -251,48 +249,6 @@ if (goog.DEBUG && !COMPILED) {
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.ConfigValue.displayName = 'proto.cc.arduino.cli.commands.v1.ConfigValue';
|
||||
}
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
* server response, or constructed directly in Javascript. The array is used
|
||||
* in place and becomes part of the constructed object. It is not cloned.
|
||||
* If no data is provided, the constructed object will be empty, but still
|
||||
* valid.
|
||||
* @extends {jspb.Message}
|
||||
* @constructor
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.cc.arduino.cli.commands.v1.BoardAttachRequest, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
/**
|
||||
* @public
|
||||
* @override
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.displayName = 'proto.cc.arduino.cli.commands.v1.BoardAttachRequest';
|
||||
}
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
* server response, or constructed directly in Javascript. The array is used
|
||||
* in place and becomes part of the constructed object. It is not cloned.
|
||||
* If no data is provided, the constructed object will be empty, but still
|
||||
* valid.
|
||||
* @extends {jspb.Message}
|
||||
* @constructor
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachResponse = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.cc.arduino.cli.commands.v1.BoardAttachResponse, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
/**
|
||||
* @public
|
||||
* @override
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachResponse.displayName = 'proto.cc.arduino.cli.commands.v1.BoardAttachResponse';
|
||||
}
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
@@ -3132,398 +3088,6 @@ proto.cc.arduino.cli.commands.v1.ConfigValue.prototype.setSelected = function(va
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* Optional fields that are not set will be set to undefined.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
|
||||
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
|
||||
* JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.cc.arduino.cli.commands.v1.BoardAttachRequest.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
|
||||
* the JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.BoardAttachRequest} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
instance: (f = msg.getInstance()) && cc_arduino_cli_commands_v1_common_pb.Instance.toObject(includeInstance, f),
|
||||
boardUri: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
sketchPath: jspb.Message.getFieldWithDefault(msg, 3, ""),
|
||||
searchTimeout: jspb.Message.getFieldWithDefault(msg, 4, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.BoardAttachRequest}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.cc.arduino.cli.commands.v1.BoardAttachRequest;
|
||||
return proto.cc.arduino.cli.commands.v1.BoardAttachRequest.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.BoardAttachRequest} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.BoardAttachRequest}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = new cc_arduino_cli_commands_v1_common_pb.Instance;
|
||||
reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.Instance.deserializeBinaryFromReader);
|
||||
msg.setInstance(value);
|
||||
break;
|
||||
case 2:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setBoardUri(value);
|
||||
break;
|
||||
case 3:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setSketchPath(value);
|
||||
break;
|
||||
case 4:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setSearchTimeout(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.serializeBinaryToWriter(this, writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the given message to binary data (in protobuf wire
|
||||
* format), writing to the given BinaryWriter.
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.BoardAttachRequest} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.serializeBinaryToWriter = function(message, writer) {
|
||||
var f = undefined;
|
||||
f = message.getInstance();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
1,
|
||||
f,
|
||||
cc_arduino_cli_commands_v1_common_pb.Instance.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = message.getBoardUri();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
2,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getSketchPath();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
3,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getSearchTimeout();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
4,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional Instance instance = 1;
|
||||
* @return {?proto.cc.arduino.cli.commands.v1.Instance}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.prototype.getInstance = function() {
|
||||
return /** @type{?proto.cc.arduino.cli.commands.v1.Instance} */ (
|
||||
jspb.Message.getWrapperField(this, cc_arduino_cli_commands_v1_common_pb.Instance, 1));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {?proto.cc.arduino.cli.commands.v1.Instance|undefined} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.BoardAttachRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.prototype.setInstance = function(value) {
|
||||
return jspb.Message.setWrapperField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the message field making it undefined.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.BoardAttachRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.prototype.clearInstance = function() {
|
||||
return this.setInstance(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return {boolean}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.prototype.hasInstance = function() {
|
||||
return jspb.Message.getField(this, 1) != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string board_uri = 2;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.prototype.getBoardUri = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.BoardAttachRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.prototype.setBoardUri = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string sketch_path = 3;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.prototype.getSketchPath = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.BoardAttachRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.prototype.setSketchPath = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 3, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string search_timeout = 4;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.prototype.getSearchTimeout = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.BoardAttachRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachRequest.prototype.setSearchTimeout = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 4, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* Optional fields that are not set will be set to undefined.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
|
||||
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
|
||||
* JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachResponse.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.cc.arduino.cli.commands.v1.BoardAttachResponse.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
|
||||
* the JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.BoardAttachResponse} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachResponse.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
taskProgress: (f = msg.getTaskProgress()) && cc_arduino_cli_commands_v1_common_pb.TaskProgress.toObject(includeInstance, f)
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.BoardAttachResponse}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachResponse.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.cc.arduino.cli.commands.v1.BoardAttachResponse;
|
||||
return proto.cc.arduino.cli.commands.v1.BoardAttachResponse.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.BoardAttachResponse} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.BoardAttachResponse}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachResponse.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = new cc_arduino_cli_commands_v1_common_pb.TaskProgress;
|
||||
reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.TaskProgress.deserializeBinaryFromReader);
|
||||
msg.setTaskProgress(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachResponse.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachResponse.serializeBinaryToWriter(this, writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the given message to binary data (in protobuf wire
|
||||
* format), writing to the given BinaryWriter.
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.BoardAttachResponse} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachResponse.serializeBinaryToWriter = function(message, writer) {
|
||||
var f = undefined;
|
||||
f = message.getTaskProgress();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
1,
|
||||
f,
|
||||
cc_arduino_cli_commands_v1_common_pb.TaskProgress.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional TaskProgress task_progress = 1;
|
||||
* @return {?proto.cc.arduino.cli.commands.v1.TaskProgress}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachResponse.prototype.getTaskProgress = function() {
|
||||
return /** @type{?proto.cc.arduino.cli.commands.v1.TaskProgress} */ (
|
||||
jspb.Message.getWrapperField(this, cc_arduino_cli_commands_v1_common_pb.TaskProgress, 1));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {?proto.cc.arduino.cli.commands.v1.TaskProgress|undefined} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.BoardAttachResponse} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachResponse.prototype.setTaskProgress = function(value) {
|
||||
return jspb.Message.setWrapperField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the message field making it undefined.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.BoardAttachResponse} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachResponse.prototype.clearTaskProgress = function() {
|
||||
return this.setTaskProgress(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return {boolean}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardAttachResponse.prototype.hasTaskProgress = function() {
|
||||
return jspb.Message.getField(this, 1) != null;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto.
|
||||
@@ -3554,7 +3118,8 @@ proto.cc.arduino.cli.commands.v1.BoardListRequest.prototype.toObject = function(
|
||||
proto.cc.arduino.cli.commands.v1.BoardListRequest.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
instance: (f = msg.getInstance()) && cc_arduino_cli_commands_v1_common_pb.Instance.toObject(includeInstance, f),
|
||||
timeout: jspb.Message.getFieldWithDefault(msg, 2, 0)
|
||||
timeout: jspb.Message.getFieldWithDefault(msg, 2, 0),
|
||||
fqbn: jspb.Message.getFieldWithDefault(msg, 3, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -3600,6 +3165,10 @@ proto.cc.arduino.cli.commands.v1.BoardListRequest.deserializeBinaryFromReader =
|
||||
var value = /** @type {number} */ (reader.readInt64());
|
||||
msg.setTimeout(value);
|
||||
break;
|
||||
case 3:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setFqbn(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -3644,6 +3213,13 @@ proto.cc.arduino.cli.commands.v1.BoardListRequest.serializeBinaryToWriter = func
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getFqbn();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
3,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3702,6 +3278,24 @@ proto.cc.arduino.cli.commands.v1.BoardListRequest.prototype.setTimeout = functio
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string fqbn = 3;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardListRequest.prototype.getFqbn = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.BoardListRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.BoardListRequest.prototype.setFqbn = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 3, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* List of repeated fields within this message type.
|
||||
|
@@ -27,7 +27,6 @@ interface IArduinoCoreServiceService extends grpc.ServiceDefinition<grpc.Untyped
|
||||
loadSketch: IArduinoCoreServiceService_ILoadSketch;
|
||||
archiveSketch: IArduinoCoreServiceService_IArchiveSketch;
|
||||
boardDetails: IArduinoCoreServiceService_IBoardDetails;
|
||||
boardAttach: IArduinoCoreServiceService_IBoardAttach;
|
||||
boardList: IArduinoCoreServiceService_IBoardList;
|
||||
boardListAll: IArduinoCoreServiceService_IBoardListAll;
|
||||
boardSearch: IArduinoCoreServiceService_IBoardSearch;
|
||||
@@ -148,15 +147,6 @@ interface IArduinoCoreServiceService_IBoardDetails extends grpc.MethodDefinition
|
||||
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse>;
|
||||
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse>;
|
||||
}
|
||||
interface IArduinoCoreServiceService_IBoardAttach extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_board_pb.BoardAttachRequest, cc_arduino_cli_commands_v1_board_pb.BoardAttachResponse> {
|
||||
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/BoardAttach";
|
||||
requestStream: false;
|
||||
responseStream: true;
|
||||
requestSerialize: grpc.serialize<cc_arduino_cli_commands_v1_board_pb.BoardAttachRequest>;
|
||||
requestDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_board_pb.BoardAttachRequest>;
|
||||
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_board_pb.BoardAttachResponse>;
|
||||
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_board_pb.BoardAttachResponse>;
|
||||
}
|
||||
interface IArduinoCoreServiceService_IBoardList extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_board_pb.BoardListRequest, cc_arduino_cli_commands_v1_board_pb.BoardListResponse> {
|
||||
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/BoardList";
|
||||
requestStream: false;
|
||||
@@ -423,7 +413,6 @@ export interface IArduinoCoreServiceServer {
|
||||
loadSketch: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse>;
|
||||
archiveSketch: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchRequest, cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchResponse>;
|
||||
boardDetails: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest, cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse>;
|
||||
boardAttach: grpc.handleServerStreamingCall<cc_arduino_cli_commands_v1_board_pb.BoardAttachRequest, cc_arduino_cli_commands_v1_board_pb.BoardAttachResponse>;
|
||||
boardList: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_board_pb.BoardListRequest, cc_arduino_cli_commands_v1_board_pb.BoardListResponse>;
|
||||
boardListAll: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_board_pb.BoardListAllRequest, cc_arduino_cli_commands_v1_board_pb.BoardListAllResponse>;
|
||||
boardSearch: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_board_pb.BoardSearchRequest, cc_arduino_cli_commands_v1_board_pb.BoardSearchResponse>;
|
||||
@@ -482,8 +471,6 @@ export interface IArduinoCoreServiceClient {
|
||||
boardDetails(request: cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse) => void): grpc.ClientUnaryCall;
|
||||
boardDetails(request: cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse) => void): grpc.ClientUnaryCall;
|
||||
boardDetails(request: cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse) => void): grpc.ClientUnaryCall;
|
||||
boardAttach(request: cc_arduino_cli_commands_v1_board_pb.BoardAttachRequest, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_board_pb.BoardAttachResponse>;
|
||||
boardAttach(request: cc_arduino_cli_commands_v1_board_pb.BoardAttachRequest, metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_board_pb.BoardAttachResponse>;
|
||||
boardList(request: cc_arduino_cli_commands_v1_board_pb.BoardListRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardListResponse) => void): grpc.ClientUnaryCall;
|
||||
boardList(request: cc_arduino_cli_commands_v1_board_pb.BoardListRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardListResponse) => void): grpc.ClientUnaryCall;
|
||||
boardList(request: cc_arduino_cli_commands_v1_board_pb.BoardListRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardListResponse) => void): grpc.ClientUnaryCall;
|
||||
@@ -584,8 +571,6 @@ export class ArduinoCoreServiceClient extends grpc.Client implements IArduinoCor
|
||||
public boardDetails(request: cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse) => void): grpc.ClientUnaryCall;
|
||||
public boardDetails(request: cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse) => void): grpc.ClientUnaryCall;
|
||||
public boardDetails(request: cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse) => void): grpc.ClientUnaryCall;
|
||||
public boardAttach(request: cc_arduino_cli_commands_v1_board_pb.BoardAttachRequest, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_board_pb.BoardAttachResponse>;
|
||||
public boardAttach(request: cc_arduino_cli_commands_v1_board_pb.BoardAttachRequest, metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_board_pb.BoardAttachResponse>;
|
||||
public boardList(request: cc_arduino_cli_commands_v1_board_pb.BoardListRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardListResponse) => void): grpc.ClientUnaryCall;
|
||||
public boardList(request: cc_arduino_cli_commands_v1_board_pb.BoardListRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardListResponse) => void): grpc.ClientUnaryCall;
|
||||
public boardList(request: cc_arduino_cli_commands_v1_board_pb.BoardListRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardListResponse) => void): grpc.ClientUnaryCall;
|
||||
|
@@ -49,28 +49,6 @@ function deserialize_cc_arduino_cli_commands_v1_ArchiveSketchResponse(buffer_arg
|
||||
return cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchResponse.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_BoardAttachRequest(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_board_pb.BoardAttachRequest)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.BoardAttachRequest');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_cc_arduino_cli_commands_v1_BoardAttachRequest(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_board_pb.BoardAttachRequest.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_BoardAttachResponse(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_board_pb.BoardAttachResponse)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.BoardAttachResponse');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_cc_arduino_cli_commands_v1_BoardAttachResponse(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_board_pb.BoardAttachResponse.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_BoardDetailsRequest(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.BoardDetailsRequest');
|
||||
@@ -1012,19 +990,6 @@ boardDetails: {
|
||||
responseSerialize: serialize_cc_arduino_cli_commands_v1_BoardDetailsResponse,
|
||||
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_BoardDetailsResponse,
|
||||
},
|
||||
// Attach a board to a sketch. When the `fqbn` field of a request is not
|
||||
// provided, the FQBN of the attached board will be used.
|
||||
boardAttach: {
|
||||
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/BoardAttach',
|
||||
requestStream: false,
|
||||
responseStream: true,
|
||||
requestType: cc_arduino_cli_commands_v1_board_pb.BoardAttachRequest,
|
||||
responseType: cc_arduino_cli_commands_v1_board_pb.BoardAttachResponse,
|
||||
requestSerialize: serialize_cc_arduino_cli_commands_v1_BoardAttachRequest,
|
||||
requestDeserialize: deserialize_cc_arduino_cli_commands_v1_BoardAttachRequest,
|
||||
responseSerialize: serialize_cc_arduino_cli_commands_v1_BoardAttachResponse,
|
||||
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_BoardAttachResponse,
|
||||
},
|
||||
// List the boards currently connected to the computer.
|
||||
boardList: {
|
||||
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/BoardList',
|
||||
@@ -1368,5 +1333,3 @@ enumerateMonitorPortSettings: {
|
||||
},
|
||||
};
|
||||
|
||||
// BOOTSTRAP COMMANDS
|
||||
// -------------------
|
||||
|
@@ -172,6 +172,31 @@ export namespace InitResponse {
|
||||
|
||||
}
|
||||
|
||||
export class FailedInstanceInitError extends jspb.Message {
|
||||
getReason(): FailedInstanceInitReason;
|
||||
setReason(value: FailedInstanceInitReason): FailedInstanceInitError;
|
||||
|
||||
getMessage(): string;
|
||||
setMessage(value: string): FailedInstanceInitError;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): FailedInstanceInitError.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: FailedInstanceInitError): FailedInstanceInitError.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: FailedInstanceInitError, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): FailedInstanceInitError;
|
||||
static deserializeBinaryFromReader(message: FailedInstanceInitError, reader: jspb.BinaryReader): FailedInstanceInitError;
|
||||
}
|
||||
|
||||
export namespace FailedInstanceInitError {
|
||||
export type AsObject = {
|
||||
reason: FailedInstanceInitReason,
|
||||
message: string,
|
||||
}
|
||||
}
|
||||
|
||||
export class DestroyRequest extends jspb.Message {
|
||||
|
||||
hasInstance(): boolean;
|
||||
@@ -364,6 +389,9 @@ export class NewSketchRequest extends jspb.Message {
|
||||
getSketchDir(): string;
|
||||
setSketchDir(value: string): NewSketchRequest;
|
||||
|
||||
getOverwrite(): boolean;
|
||||
setOverwrite(value: boolean): NewSketchRequest;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): NewSketchRequest.AsObject;
|
||||
@@ -380,6 +408,7 @@ export namespace NewSketchRequest {
|
||||
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
|
||||
sketchName: string,
|
||||
sketchDir: string,
|
||||
overwrite: boolean,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,6 +514,9 @@ export class ArchiveSketchRequest extends jspb.Message {
|
||||
getIncludeBuildDir(): boolean;
|
||||
setIncludeBuildDir(value: boolean): ArchiveSketchRequest;
|
||||
|
||||
getOverwrite(): boolean;
|
||||
setOverwrite(value: boolean): ArchiveSketchRequest;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): ArchiveSketchRequest.AsObject;
|
||||
@@ -501,6 +533,7 @@ export namespace ArchiveSketchRequest {
|
||||
sketchPath: string,
|
||||
archivePath: string,
|
||||
includeBuildDir: boolean,
|
||||
overwrite: boolean,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,3 +553,10 @@ export namespace ArchiveSketchResponse {
|
||||
export type AsObject = {
|
||||
}
|
||||
}
|
||||
|
||||
export enum FailedInstanceInitReason {
|
||||
FAILED_INSTANCE_INIT_REASON_UNSPECIFIED = 0,
|
||||
FAILED_INSTANCE_INIT_REASON_INVALID_INDEX_URL = 1,
|
||||
FAILED_INSTANCE_INIT_REASON_INDEX_LOAD_ERROR = 2,
|
||||
FAILED_INSTANCE_INIT_REASON_TOOL_LOAD_ERROR = 3,
|
||||
}
|
||||
|
@@ -37,6 +37,8 @@ goog.exportSymbol('proto.cc.arduino.cli.commands.v1.CreateRequest', null, global
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.CreateResponse', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.DestroyRequest', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.DestroyResponse', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.FailedInstanceInitError', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.FailedInstanceInitReason', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.InitRequest', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.InitResponse', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.InitResponse.MessageCase', null, global);
|
||||
@@ -156,6 +158,27 @@ if (goog.DEBUG && !COMPILED) {
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.InitResponse.Progress.displayName = 'proto.cc.arduino.cli.commands.v1.InitResponse.Progress';
|
||||
}
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
* server response, or constructed directly in Javascript. The array is used
|
||||
* in place and becomes part of the constructed object. It is not cloned.
|
||||
* If no data is provided, the constructed object will be empty, but still
|
||||
* valid.
|
||||
* @extends {jspb.Message}
|
||||
* @constructor
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.FailedInstanceInitError = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.cc.arduino.cli.commands.v1.FailedInstanceInitError, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
/**
|
||||
* @public
|
||||
* @override
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.FailedInstanceInitError.displayName = 'proto.cc.arduino.cli.commands.v1.FailedInstanceInitError';
|
||||
}
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
@@ -1398,6 +1421,166 @@ proto.cc.arduino.cli.commands.v1.InitResponse.prototype.hasProfile = function()
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* Optional fields that are not set will be set to undefined.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
|
||||
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
|
||||
* JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.FailedInstanceInitError.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.cc.arduino.cli.commands.v1.FailedInstanceInitError.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
|
||||
* the JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.FailedInstanceInitError} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.FailedInstanceInitError.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
reason: jspb.Message.getFieldWithDefault(msg, 1, 0),
|
||||
message: jspb.Message.getFieldWithDefault(msg, 2, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.FailedInstanceInitError}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.FailedInstanceInitError.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.cc.arduino.cli.commands.v1.FailedInstanceInitError;
|
||||
return proto.cc.arduino.cli.commands.v1.FailedInstanceInitError.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.FailedInstanceInitError} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.FailedInstanceInitError}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.FailedInstanceInitError.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = /** @type {!proto.cc.arduino.cli.commands.v1.FailedInstanceInitReason} */ (reader.readEnum());
|
||||
msg.setReason(value);
|
||||
break;
|
||||
case 2:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setMessage(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.FailedInstanceInitError.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
proto.cc.arduino.cli.commands.v1.FailedInstanceInitError.serializeBinaryToWriter(this, writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the given message to binary data (in protobuf wire
|
||||
* format), writing to the given BinaryWriter.
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.FailedInstanceInitError} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.FailedInstanceInitError.serializeBinaryToWriter = function(message, writer) {
|
||||
var f = undefined;
|
||||
f = message.getReason();
|
||||
if (f !== 0.0) {
|
||||
writer.writeEnum(
|
||||
1,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getMessage();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
2,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional FailedInstanceInitReason reason = 1;
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.FailedInstanceInitReason}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.FailedInstanceInitError.prototype.getReason = function() {
|
||||
return /** @type {!proto.cc.arduino.cli.commands.v1.FailedInstanceInitReason} */ (jspb.Message.getFieldWithDefault(this, 1, 0));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.FailedInstanceInitReason} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.FailedInstanceInitError} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.FailedInstanceInitError.prototype.setReason = function(value) {
|
||||
return jspb.Message.setProto3EnumField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string message = 2;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.FailedInstanceInitError.prototype.getMessage = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.FailedInstanceInitError} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.FailedInstanceInitError.prototype.setMessage = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto.
|
||||
@@ -2546,7 +2729,8 @@ proto.cc.arduino.cli.commands.v1.NewSketchRequest.toObject = function(includeIns
|
||||
var f, obj = {
|
||||
instance: (f = msg.getInstance()) && cc_arduino_cli_commands_v1_common_pb.Instance.toObject(includeInstance, f),
|
||||
sketchName: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
sketchDir: jspb.Message.getFieldWithDefault(msg, 3, "")
|
||||
sketchDir: jspb.Message.getFieldWithDefault(msg, 3, ""),
|
||||
overwrite: jspb.Message.getBooleanFieldWithDefault(msg, 4, false)
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -2596,6 +2780,10 @@ proto.cc.arduino.cli.commands.v1.NewSketchRequest.deserializeBinaryFromReader =
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setSketchDir(value);
|
||||
break;
|
||||
case 4:
|
||||
var value = /** @type {boolean} */ (reader.readBool());
|
||||
msg.setOverwrite(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -2647,6 +2835,13 @@ proto.cc.arduino.cli.commands.v1.NewSketchRequest.serializeBinaryToWriter = func
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getOverwrite();
|
||||
if (f) {
|
||||
writer.writeBool(
|
||||
4,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2723,6 +2918,24 @@ proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.setSketchDir = funct
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional bool overwrite = 4;
|
||||
* @return {boolean}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.getOverwrite = function() {
|
||||
return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 4, false));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {boolean} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.NewSketchRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.setOverwrite = function(value) {
|
||||
return jspb.Message.setProto3BooleanField(this, 4, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3382,7 +3595,8 @@ proto.cc.arduino.cli.commands.v1.ArchiveSketchRequest.toObject = function(includ
|
||||
var f, obj = {
|
||||
sketchPath: jspb.Message.getFieldWithDefault(msg, 1, ""),
|
||||
archivePath: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
includeBuildDir: jspb.Message.getBooleanFieldWithDefault(msg, 3, false)
|
||||
includeBuildDir: jspb.Message.getBooleanFieldWithDefault(msg, 3, false),
|
||||
overwrite: jspb.Message.getBooleanFieldWithDefault(msg, 4, false)
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -3431,6 +3645,10 @@ proto.cc.arduino.cli.commands.v1.ArchiveSketchRequest.deserializeBinaryFromReade
|
||||
var value = /** @type {boolean} */ (reader.readBool());
|
||||
msg.setIncludeBuildDir(value);
|
||||
break;
|
||||
case 4:
|
||||
var value = /** @type {boolean} */ (reader.readBool());
|
||||
msg.setOverwrite(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -3481,6 +3699,13 @@ proto.cc.arduino.cli.commands.v1.ArchiveSketchRequest.serializeBinaryToWriter =
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getOverwrite();
|
||||
if (f) {
|
||||
writer.writeBool(
|
||||
4,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3538,6 +3763,24 @@ proto.cc.arduino.cli.commands.v1.ArchiveSketchRequest.prototype.setIncludeBuildD
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional bool overwrite = 4;
|
||||
* @return {boolean}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.ArchiveSketchRequest.prototype.getOverwrite = function() {
|
||||
return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 4, false));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {boolean} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.ArchiveSketchRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.ArchiveSketchRequest.prototype.setOverwrite = function(value) {
|
||||
return jspb.Message.setProto3BooleanField(this, 4, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3639,4 +3882,14 @@ proto.cc.arduino.cli.commands.v1.ArchiveSketchResponse.serializeBinaryToWriter =
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.FailedInstanceInitReason = {
|
||||
FAILED_INSTANCE_INIT_REASON_UNSPECIFIED: 0,
|
||||
FAILED_INSTANCE_INIT_REASON_INVALID_INDEX_URL: 1,
|
||||
FAILED_INSTANCE_INIT_REASON_INDEX_LOAD_ERROR: 2,
|
||||
FAILED_INSTANCE_INIT_REASON_TOOL_LOAD_ERROR: 3
|
||||
};
|
||||
|
||||
goog.object.extend(exports, proto.cc.arduino.cli.commands.v1);
|
||||
|
@@ -255,6 +255,12 @@ export class Platform extends jspb.Message {
|
||||
addType(value: string, index?: number): string;
|
||||
|
||||
|
||||
hasHelp(): boolean;
|
||||
clearHelp(): void;
|
||||
getHelp(): HelpResources | undefined;
|
||||
setHelp(value?: HelpResources): Platform;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): Platform.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: Platform): Platform.AsObject;
|
||||
@@ -278,6 +284,7 @@ export namespace Platform {
|
||||
manuallyInstalled: boolean,
|
||||
deprecated: boolean,
|
||||
typeList: Array<string>,
|
||||
help?: HelpResources.AsObject,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,3 +370,24 @@ export namespace Profile {
|
||||
fqbn: string,
|
||||
}
|
||||
}
|
||||
|
||||
export class HelpResources extends jspb.Message {
|
||||
getOnline(): string;
|
||||
setOnline(value: string): HelpResources;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): HelpResources.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: HelpResources): HelpResources.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: HelpResources, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): HelpResources;
|
||||
static deserializeBinaryFromReader(message: HelpResources, reader: jspb.BinaryReader): HelpResources;
|
||||
}
|
||||
|
||||
export namespace HelpResources {
|
||||
export type AsObject = {
|
||||
online: string,
|
||||
}
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ goog.exportSymbol('proto.cc.arduino.cli.commands.v1.DownloadProgress.MessageCase
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.DownloadProgressEnd', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.DownloadProgressStart', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.DownloadProgressUpdate', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.HelpResources', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.InstalledPlatformReference', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.Instance', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.Platform', null, global);
|
||||
@@ -258,6 +259,27 @@ if (goog.DEBUG && !COMPILED) {
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Profile.displayName = 'proto.cc.arduino.cli.commands.v1.Profile';
|
||||
}
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
* server response, or constructed directly in Javascript. The array is used
|
||||
* in place and becomes part of the constructed object. It is not cloned.
|
||||
* If no data is provided, the constructed object will be empty, but still
|
||||
* valid.
|
||||
* @extends {jspb.Message}
|
||||
* @constructor
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.HelpResources = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.cc.arduino.cli.commands.v1.HelpResources, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
/**
|
||||
* @public
|
||||
* @override
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.HelpResources.displayName = 'proto.cc.arduino.cli.commands.v1.HelpResources';
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1608,7 +1630,8 @@ proto.cc.arduino.cli.commands.v1.Platform.toObject = function(includeInstance, m
|
||||
proto.cc.arduino.cli.commands.v1.Board.toObject, includeInstance),
|
||||
manuallyInstalled: jspb.Message.getBooleanFieldWithDefault(msg, 9, false),
|
||||
deprecated: jspb.Message.getBooleanFieldWithDefault(msg, 10, false),
|
||||
typeList: (f = jspb.Message.getRepeatedField(msg, 11)) == null ? undefined : f
|
||||
typeList: (f = jspb.Message.getRepeatedField(msg, 11)) == null ? undefined : f,
|
||||
help: (f = msg.getHelp()) && proto.cc.arduino.cli.commands.v1.HelpResources.toObject(includeInstance, f)
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -1690,6 +1713,11 @@ proto.cc.arduino.cli.commands.v1.Platform.deserializeBinaryFromReader = function
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.addType(value);
|
||||
break;
|
||||
case 12:
|
||||
var value = new proto.cc.arduino.cli.commands.v1.HelpResources;
|
||||
reader.readMessage(value,proto.cc.arduino.cli.commands.v1.HelpResources.deserializeBinaryFromReader);
|
||||
msg.setHelp(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -1797,6 +1825,14 @@ proto.cc.arduino.cli.commands.v1.Platform.serializeBinaryToWriter = function(mes
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getHelp();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
12,
|
||||
f,
|
||||
proto.cc.arduino.cli.commands.v1.HelpResources.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2037,6 +2073,43 @@ proto.cc.arduino.cli.commands.v1.Platform.prototype.clearTypeList = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional HelpResources help = 12;
|
||||
* @return {?proto.cc.arduino.cli.commands.v1.HelpResources}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Platform.prototype.getHelp = function() {
|
||||
return /** @type{?proto.cc.arduino.cli.commands.v1.HelpResources} */ (
|
||||
jspb.Message.getWrapperField(this, proto.cc.arduino.cli.commands.v1.HelpResources, 12));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {?proto.cc.arduino.cli.commands.v1.HelpResources|undefined} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.Platform} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Platform.prototype.setHelp = function(value) {
|
||||
return jspb.Message.setWrapperField(this, 12, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the message field making it undefined.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.Platform} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Platform.prototype.clearHelp = function() {
|
||||
return this.setHelp(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return {boolean}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Platform.prototype.hasHelp = function() {
|
||||
return jspb.Message.getField(this, 12) != null;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2577,4 +2650,134 @@ proto.cc.arduino.cli.commands.v1.Profile.prototype.setFqbn = function(value) {
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* Optional fields that are not set will be set to undefined.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
|
||||
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
|
||||
* JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.HelpResources.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.cc.arduino.cli.commands.v1.HelpResources.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
|
||||
* the JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.HelpResources} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.HelpResources.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
online: jspb.Message.getFieldWithDefault(msg, 1, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.HelpResources}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.HelpResources.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.cc.arduino.cli.commands.v1.HelpResources;
|
||||
return proto.cc.arduino.cli.commands.v1.HelpResources.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.HelpResources} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.HelpResources}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.HelpResources.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setOnline(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.HelpResources.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
proto.cc.arduino.cli.commands.v1.HelpResources.serializeBinaryToWriter(this, writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the given message to binary data (in protobuf wire
|
||||
* format), writing to the given BinaryWriter.
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.HelpResources} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.HelpResources.serializeBinaryToWriter = function(message, writer) {
|
||||
var f = undefined;
|
||||
f = message.getOnline();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
1,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string online = 1;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.HelpResources.prototype.getOnline = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.HelpResources} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.HelpResources.prototype.setOnline = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
goog.object.extend(exports, proto.cc.arduino.cli.commands.v1);
|
||||
|
@@ -182,6 +182,11 @@ export class CompileResponse extends jspb.Message {
|
||||
getProgress(): cc_arduino_cli_commands_v1_common_pb.TaskProgress | undefined;
|
||||
setProgress(value?: cc_arduino_cli_commands_v1_common_pb.TaskProgress): CompileResponse;
|
||||
|
||||
clearBuildPropertiesList(): void;
|
||||
getBuildPropertiesList(): Array<string>;
|
||||
setBuildPropertiesList(value: Array<string>): CompileResponse;
|
||||
addBuildProperties(value: string, index?: number): string;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): CompileResponse.AsObject;
|
||||
@@ -203,6 +208,7 @@ export namespace CompileResponse {
|
||||
boardPlatform?: cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference.AsObject,
|
||||
buildPlatform?: cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference.AsObject,
|
||||
progress?: cc_arduino_cli_commands_v1_common_pb.TaskProgress.AsObject,
|
||||
buildPropertiesList: Array<string>,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1052,7 +1052,7 @@ proto.cc.arduino.cli.commands.v1.CompileRequest.prototype.setSkipLibrariesDiscov
|
||||
* @private {!Array<number>}
|
||||
* @const
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.CompileResponse.repeatedFields_ = [4,5];
|
||||
proto.cc.arduino.cli.commands.v1.CompileResponse.repeatedFields_ = [4,5,9];
|
||||
|
||||
|
||||
|
||||
@@ -1094,7 +1094,8 @@ proto.cc.arduino.cli.commands.v1.CompileResponse.toObject = function(includeInst
|
||||
proto.cc.arduino.cli.commands.v1.ExecutableSectionSize.toObject, includeInstance),
|
||||
boardPlatform: (f = msg.getBoardPlatform()) && cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference.toObject(includeInstance, f),
|
||||
buildPlatform: (f = msg.getBuildPlatform()) && cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference.toObject(includeInstance, f),
|
||||
progress: (f = msg.getProgress()) && cc_arduino_cli_commands_v1_common_pb.TaskProgress.toObject(includeInstance, f)
|
||||
progress: (f = msg.getProgress()) && cc_arduino_cli_commands_v1_common_pb.TaskProgress.toObject(includeInstance, f),
|
||||
buildPropertiesList: (f = jspb.Message.getRepeatedField(msg, 9)) == null ? undefined : f
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -1168,6 +1169,10 @@ proto.cc.arduino.cli.commands.v1.CompileResponse.deserializeBinaryFromReader = f
|
||||
reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.TaskProgress.deserializeBinaryFromReader);
|
||||
msg.setProgress(value);
|
||||
break;
|
||||
case 9:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.addBuildProperties(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -1258,6 +1263,13 @@ proto.cc.arduino.cli.commands.v1.CompileResponse.serializeBinaryToWriter = funct
|
||||
cc_arduino_cli_commands_v1_common_pb.TaskProgress.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = message.getBuildPropertiesList();
|
||||
if (f.length > 0) {
|
||||
writer.writeRepeatedString(
|
||||
9,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1550,6 +1562,43 @@ proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.hasProgress = functio
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* repeated string build_properties = 9;
|
||||
* @return {!Array<string>}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.getBuildPropertiesList = function() {
|
||||
return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 9));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {!Array<string>} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.CompileResponse} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.setBuildPropertiesList = function(value) {
|
||||
return jspb.Message.setField(this, 9, value || []);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @param {number=} opt_index
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.CompileResponse} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.addBuildProperties = function(value, opt_index) {
|
||||
return jspb.Message.addToRepeatedField(this, 9, value, opt_index);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the list making it empty but non-null.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.CompileResponse} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.clearBuildPropertiesList = function() {
|
||||
return this.setBuildPropertiesList([]);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -82,6 +82,23 @@ export namespace PlatformInstallResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export class PlatformLoadingError extends jspb.Message {
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): PlatformLoadingError.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: PlatformLoadingError): PlatformLoadingError.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: PlatformLoadingError, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): PlatformLoadingError;
|
||||
static deserializeBinaryFromReader(message: PlatformLoadingError, reader: jspb.BinaryReader): PlatformLoadingError;
|
||||
}
|
||||
|
||||
export namespace PlatformLoadingError {
|
||||
export type AsObject = {
|
||||
}
|
||||
}
|
||||
|
||||
export class PlatformDownloadRequest extends jspb.Message {
|
||||
|
||||
hasInstance(): boolean;
|
||||
|
@@ -24,6 +24,7 @@ goog.exportSymbol('proto.cc.arduino.cli.commands.v1.PlatformInstallRequest', nul
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.PlatformInstallResponse', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.PlatformListRequest', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.PlatformListResponse', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.PlatformLoadingError', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.PlatformSearchRequest', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.PlatformSearchResponse', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.PlatformUninstallRequest', null, global);
|
||||
@@ -72,6 +73,27 @@ if (goog.DEBUG && !COMPILED) {
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformInstallResponse.displayName = 'proto.cc.arduino.cli.commands.v1.PlatformInstallResponse';
|
||||
}
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
* server response, or constructed directly in Javascript. The array is used
|
||||
* in place and becomes part of the constructed object. It is not cloned.
|
||||
* If no data is provided, the constructed object will be empty, but still
|
||||
* valid.
|
||||
* @extends {jspb.Message}
|
||||
* @constructor
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformLoadingError = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.cc.arduino.cli.commands.v1.PlatformLoadingError, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
/**
|
||||
* @public
|
||||
* @override
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformLoadingError.displayName = 'proto.cc.arduino.cli.commands.v1.PlatformLoadingError';
|
||||
}
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
@@ -809,6 +831,107 @@ proto.cc.arduino.cli.commands.v1.PlatformInstallResponse.prototype.hasTaskProgre
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* Optional fields that are not set will be set to undefined.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
|
||||
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
|
||||
* JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformLoadingError.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.cc.arduino.cli.commands.v1.PlatformLoadingError.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
|
||||
* the JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.PlatformLoadingError} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformLoadingError.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.PlatformLoadingError}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformLoadingError.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.cc.arduino.cli.commands.v1.PlatformLoadingError;
|
||||
return proto.cc.arduino.cli.commands.v1.PlatformLoadingError.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.PlatformLoadingError} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.PlatformLoadingError}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformLoadingError.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformLoadingError.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
proto.cc.arduino.cli.commands.v1.PlatformLoadingError.serializeBinaryToWriter(this, writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the given message to binary data (in protobuf wire
|
||||
* format), writing to the given BinaryWriter.
|
||||
* @param {!proto.cc.arduino.cli.commands.v1.PlatformLoadingError} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.PlatformLoadingError.serializeBinaryToWriter = function(message, writer) {
|
||||
var f = undefined;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto.
|
||||
|
@@ -406,6 +406,9 @@ export class LibrarySearchRequest extends jspb.Message {
|
||||
getQuery(): string;
|
||||
setQuery(value: string): LibrarySearchRequest;
|
||||
|
||||
getOmitReleasesDetails(): boolean;
|
||||
setOmitReleasesDetails(value: boolean): LibrarySearchRequest;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): LibrarySearchRequest.AsObject;
|
||||
@@ -421,6 +424,7 @@ export namespace LibrarySearchRequest {
|
||||
export type AsObject = {
|
||||
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
|
||||
query: string,
|
||||
omitReleasesDetails: boolean,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,6 +469,11 @@ export class SearchedLibrary extends jspb.Message {
|
||||
getLatest(): LibraryRelease | undefined;
|
||||
setLatest(value?: LibraryRelease): SearchedLibrary;
|
||||
|
||||
clearAvailableVersionsList(): void;
|
||||
getAvailableVersionsList(): Array<string>;
|
||||
setAvailableVersionsList(value: Array<string>): SearchedLibrary;
|
||||
addAvailableVersions(value: string, index?: number): string;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): SearchedLibrary.AsObject;
|
||||
@@ -482,6 +491,7 @@ export namespace SearchedLibrary {
|
||||
|
||||
releasesMap: Array<[string, LibraryRelease.AsObject]>,
|
||||
latest?: LibraryRelease.AsObject,
|
||||
availableVersionsList: Array<string>,
|
||||
}
|
||||
}
|
||||
|
||||
|