Compare commits

...

51 Commits

Author SHA1 Message Date
Alberto Iannaccone
96b5edf427 fix IDE updater commands (#872)
* fix IDE updater commands

* reinitialise autoupdate when preferences change

* fix typo + add i18n strings
2022-03-01 16:34:43 +00:00
Alberto Iannaccone
a5a6a0b611 Go to download page when automatic update fails (#871)
* add preference to set a custom update url

* go to download page when update fails

* fix i18n check
2022-03-01 08:24:29 +00:00
Alberto Iannaccone
2a27a14a68 put Linux build files inside a folder before zipping (#870)
* add preference to set a custom update url

* put linux build inside a folder before zipping
2022-03-01 08:23:56 +00:00
Alberto Iannaccone
f2d492b5dc show represented file on MacOS (#868) 2022-03-01 08:17:05 +00:00
Alberto Iannaccone
5979e5aad2 add preference to set a custom update url (#865) 2022-02-28 14:04:54 +00:00
Alberto Iannaccone
baa9b5f7ab Automatically check for updates only once (#863)
* Automatically check for updates only once

* set windows version to 2019 on CI
2022-02-24 14:04:36 +00:00
Francesco Stasi
481497e384 Disable autodownload of updates on startup (#860) 2022-02-24 10:43:10 +00:00
Mark Sujew
0207778373 Enable opening the IDE from finder/explorer (#835)
* Enable opening the IDE from finder/explorer

* Make opening windows from args a bit more lenient
2022-02-23 16:39:27 +01:00
Francesco Stasi
d79f32efd7 bump vscode-arduino-tools (#859) 2022-02-23 16:07:40 +01:00
Francesco Stasi
3ab03dd62f Avoid duplicated yaml entries (#858) 2022-02-23 15:55:04 +01:00
Mark Sujew
bc3cb0c230 Save preferences in sequence (#856) 2022-02-23 11:08:19 +01:00
Alberto Iannaccone
473cb11053 Remove target section from electron-builder config (#853)
* remove target section from electron-builder config

* do not modify zip structure before moving to artifcats folder
2022-02-22 11:14:11 +00:00
Alberto Iannaccone
0a87fd00f3 IDE updater bugfixes (#846)
* IDE updater assorted bugfix

- add linux AppImage target
- fix hardcoded if condition that causes to always show the update dialog
- fix redundant test build version
- recalculate sha512 after notarization on macOS

* boost notarization speed

* recalculate artifacts hash
2022-02-21 21:40:46 +00:00
Alberto Iannaccone
9b1f15def8 upgrade IDE to rc4 (#841) 2022-02-17 10:39:39 +00:00
Alberto Iannaccone
77b430675d fix generation of updater channel files in CI (#840) 2022-02-17 09:29:56 +00:00
Alberto Iannaccone
f660058c75 Check for IDE update at startup (#797)
* Remove check for updates on startup setting

* Remove useless exported function

* Update template-package.json used to package IDE

* Add function to get channel file during packaging step

* Add updates check

* move ide updater on backend

* configure updater options

* add auto update preferences

* TMP check updates on start and download

* index on check-update-startup: fcb8f6e TMP check updates on start and download

* set version to skip on local storage

* add IDE setting to toggle update check on start-up

* comment out check for updates on startup and auto update settings

* Update Theia to 1.22.1

* updated CI

* download changelog and show it in IDE updater dialog

* remove useless file

* remove useless code

* add i18n to updater dialog

* fix i18n

* refactor UpdateInfo typing

* add macos zip to artifacts

* Simply use `--ignore-engines`

* Use correct --ignore-engines

* Fix semver#valid call

* Use C++17

* updated documentation

* add update channel preference

* update updater url

* updated documentation

* Fix the C++ version

* Build flag for cpp

* add disclaimer with correct node version

* Update `electron-builder`

* Fix `Electron.Menu` issue

* Skip electron rebuild

* Rebuild native dependencies beforehand

* Use resolutions section

* Update template-package.json as well

* move ide-updater to electron application

* refactor ide-updater service

* update yarn.lock

* update i18n

* Revert "Add gRPC user agent (#834)"

This reverts commit 5ab3a747a6.

* fix ide download url

* update latest file in CI

* fix i18n check

Co-authored-by: Silvano Cerza <silvanocerza@gmail.com>
Co-authored-by: Francesco Stasi <f.stasi@me.com>
Co-authored-by: Mark Sujew <msujew@yahoo.de>
2022-02-15 17:01:19 +00:00
Silvano Cerza
9ecff86bbe Fix version retrieval in node process (#837) 2022-02-15 16:52:13 +01:00
Silvano Cerza
5ab3a747a6 Add gRPC user agent (#834) 2022-02-14 12:39:48 +01:00
Silvano Cerza
877c1a1559 Fix board options not shown for manually installed platforms (#826) 2022-02-14 10:12:18 +01:00
Alberto Iannaccone
2f9bf86d75 update arduino-cli to 0.21.0 (#820) 2022-02-11 14:50:56 +00:00
Mark Sujew
112153fb96 Update Theia to 1.22.1 (#791) 2022-02-11 15:25:35 +01:00
Mark Sujew
69ac1f4779 Open all closed workspaces on startup (#780) 2022-02-11 10:57:44 +01:00
Ben
a20899ff43 When a new port is connected and checking to connect to it because previously connected board matches the name / fqbn, also check that the protocol matches. (#792) 2022-02-01 14:35:21 +01:00
Silvano Cerza
ef2be1c086 Small code fix 2022-01-31 17:29:56 +01:00
Silvano Cerza
af33dce0f6 Solve ports conflicts with same address and different protocol 2022-01-31 17:29:56 +01:00
Silvano Cerza
b3b22795f8 Fix compose-changelog.js overwriting itself when called with no arguments 2022-01-27 18:42:34 +01:00
Silvano Cerza
8a0454db51 Fix compose full changelog workflow 2022-01-27 18:10:30 +01:00
Silvano Cerza
f1a5d87ab2 Full changelog is now created from separate workflow 2022-01-27 16:56:03 +01:00
Silvano Cerza
cf0a2161af Add step to generate full changelog on release 2022-01-27 16:56:03 +01:00
Silvano Cerza
dcebd863cc Changelog file is now written to file 2022-01-27 16:56:03 +01:00
Silvano Cerza
e8477b14f3 Fix substitutions issues with compose-changelog script 2022-01-27 16:56:03 +01:00
Alberto Iannaccone
0230071b5f add script to compose full changelog 2022-01-27 16:56:03 +01:00
Alberto Iannaccone
1d88263c85 update ls to 0.6.0and clangd to 13.0.0 (#738) 2022-01-24 16:21:19 +00:00
Francesco Stasi
a71ac4c44d Update BUILDING.md 2022-01-21 10:47:12 +01:00
per1234
66fc27e58c Remove stray brace from compilation error output
An extra brace was inadvertently introduced into a template literal used to format output text in the event of an error
during compilation. This caused the text to end in a pointless `}`

For example:

```
Compilation error: exit status 1}
```

After this change, the output text is as expected:

```
Compilation error: exit status 1
```
2022-01-17 02:46:40 -08:00
per1234
bc365f4a8d Correct minor typos in UI text and documentation 2022-01-17 02:16:36 -08:00
per1234
a5891f9884 Update development docs for current repository
The original location of the project repository was `bcmi-labs/arduino-editor` and some of the internal development
documentation for the project contains references to the repository.

This documentation was not updated at the time the repository was moved to the current home in `arduino/arduino-ide`.
2022-01-17 02:16:08 -08:00
Francesco Stasi
fcdf16a937 Update BUILDING.md 2022-01-14 12:12:17 +01:00
github-actions[bot]
e0b6dbbf2a Updated translation files (#723)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-01-13 17:03:42 +01:00
Francesco Stasi
9529e78647 Improve build instructions (#706) 2022-01-13 17:02:45 +01:00
Francesco Stasi
51da3c0668 Version 2.0.0-rc3 2021-12-22 16:44:17 +01:00
Francesco Stasi
c00d3d33dd Merge remote-tracking branch 'origin/i18n/translations-update' 2021-12-22 16:43:22 +01:00
Francesco Stasi
cfa9b8aea6 bump serial plotter to 0.0.17 2021-12-22 11:32:44 +01:00
per1234
6106e9ff1a Use major version ref of carlosperate/download-file-action
The `carlosperate/download-file-action` action is used in the GitHub Actions workflows as a convenient way to download
external resources.

A major version ref has been added to that repository. It will always point to the latest release of the "1" major
version series. This means it is no longer necessary to do a full pin of the action version in use as before.

Use of the major version ref will cause the workflow to use a stable version of the action, while also benefiting from
ongoing development to the action up until such time as a new major release of an action is made. At that time we would
need to evaluate whether any changes to the workflow are required by the breaking change that triggered the major
release before manually updating the major ref (e.g., uses: `carlosperate/download-file-action@v2`). I think this
approach strikes the right balance between stability and maintainability for these workflows.
2021-12-21 01:19:29 -08:00
Francesco Stasi
b1d9f65a0d bump serial plotter version (#698) 2021-12-20 15:49:16 +01:00
Francesco Stasi
f4008100e1 Correctly transform uint8array to string (#696)
* correctly transform uint8array to string

* export function
2021-12-20 14:56:38 +01:00
Francesco Stasi
11a6959a24 serial monitor lines not to wrap (#697) 2021-12-20 14:56:26 +01:00
github-actions[bot]
3c6e11832b Updated translation files 2021-12-20 02:19:55 +00:00
Alberto Iannaccone
c064673ce1 Close serial port connection before flashing firmware (#688) 2021-12-15 09:31:12 +00:00
Silvano Cerza
cc5764e536 Update README.md
Co-authored-by: per1234 <accounts@perglass.com>
2021-12-14 17:47:31 +01:00
Silvano Cerza
9131f2d09e Update README.md with translations project link 2021-12-14 17:47:31 +01:00
120 changed files with 11107 additions and 3795 deletions

View File

@@ -15,15 +15,15 @@ on:
env:
JOB_TRANSFER_ARTIFACT: build-artifacts
CHANGELOG_ARTIFACTS: changelog
jobs:
build:
if: github.repository == 'arduino/arduino-ide'
strategy:
matrix:
config:
- os: windows-latest
- os: windows-2019
- os: ubuntu-18.04 # https://github.com/arduino/arduino-ide/issues/259
- os: macos-latest
runs-on: ${{ matrix.config.os }}
@@ -33,16 +33,16 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- name: Install Node.js 12.x
- name: Install Node.js 14.x
uses: actions/setup-node@v1
with:
node-version: '12.14.1'
node-version: '14.x'
registry-url: 'https://registry.npmjs.org'
- name: Install Python 2.7
- name: Install Python 3.x
uses: actions/setup-python@v2
with:
python-version: '2.7'
python-version: '3.x'
- name: Package
shell: bash
@@ -50,35 +50,36 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AC_USERNAME: ${{ secrets.AC_USERNAME }}
AC_PASSWORD: ${{ secrets.AC_PASSWORD }}
AC_TEAM_ID: ${{ secrets.AC_TEAM_ID }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
IS_NIGHTLY: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') }}
IS_RELEASE: ${{ startsWith(github.ref, 'refs/tags/') }}
IS_FORK: ${{ github.event.pull_request.head.repo.fork == true }}
run: |
# See: https://www.electron.build/code-signing
if [ $IS_FORK = true ]; then
echo "Skipping the app signing: building from a fork."
else
if [ "${{ runner.OS }}" = "macOS" ]; then
export CSC_LINK="${{ runner.temp }}/signing_certificate.p12"
# APPLE_SIGNING_CERTIFICATE_P12 secret was produced by following the procedure from:
# https://www.kencochrane.com/2020/08/01/build-and-sign-golang-binaries-for-macos-with-github-actions/#exporting-the-developer-certificate
echo "${{ secrets.APPLE_SIGNING_CERTIFICATE_P12 }}" | base64 --decode > "$CSC_LINK"
# See: https://www.electron.build/code-signing
if [ $IS_FORK = true ]; then
echo "Skipping the app signing: building from a fork."
else
if [ "${{ runner.OS }}" = "macOS" ]; then
export CSC_LINK="${{ runner.temp }}/signing_certificate.p12"
# APPLE_SIGNING_CERTIFICATE_P12 secret was produced by following the procedure from:
# https://www.kencochrane.com/2020/08/01/build-and-sign-golang-binaries-for-macos-with-github-actions/#exporting-the-developer-certificate
echo "${{ secrets.APPLE_SIGNING_CERTIFICATE_P12 }}" | base64 --decode > "$CSC_LINK"
export CSC_KEY_PASSWORD="${{ secrets.KEYCHAIN_PASSWORD }}"
export CSC_KEY_PASSWORD="${{ secrets.KEYCHAIN_PASSWORD }}"
elif [ "${{ runner.OS }}" = "Windows" ]; then
export CSC_LINK="${{ runner.temp }}/signing_certificate.pfx"
npm config set msvs_version 2017 --global
echo "${{ secrets.WINDOWS_SIGNING_CERTIFICATE_PFX }}" | base64 --decode > "$CSC_LINK"
elif [ "${{ runner.OS }}" = "Windows" ]; then
export CSC_LINK="${{ runner.temp }}/signing_certificate.pfx"
npm config set msvs_version 2017 --global
echo "${{ secrets.WINDOWS_SIGNING_CERTIFICATE_PFX }}" | base64 --decode > "$CSC_LINK"
export CSC_KEY_PASSWORD="${{ secrets.WINDOWS_SIGNING_CERTIFICATE_PASSWORD }}"
fi
export CSC_KEY_PASSWORD="${{ secrets.WINDOWS_SIGNING_CERTIFICATE_PASSWORD }}"
fi
fi
yarn --cwd ./electron/packager/
yarn --cwd ./electron/packager/ package
yarn --cwd ./electron/packager/
yarn --cwd ./electron/packager/ package
- name: Upload [GitHub Actions]
uses: actions/upload-artifact@v2
@@ -95,15 +96,19 @@ jobs:
strategy:
matrix:
artifact:
- path: "*Linux_64bit.zip"
name: Linux_X86-64
- path: "*macOS_64bit.dmg"
name: macOS
- path: "*Windows_64bit.exe"
- path: '*Linux_64bit.zip'
name: Linux_X86-64_zip
- path: '*Linux_64bit.AppImage'
name: Linux_X86-64_app_image
- path: '*macOS_64bit.dmg'
name: macOS_dmg
- path: '*macOS_64bit.zip'
name: macOS_zip
- path: '*Windows_64bit.exe'
name: Windows_X86-64_interactive_installer
- path: "*Windows_64bit.msi"
- path: '*Windows_64bit.msi'
name: Windows_X86-64_MSI
- path: "*Windows_64bit.zip"
- path: '*Windows_64bit.zip'
name: Windows_X86-64_zip
steps:
@@ -112,7 +117,7 @@ jobs:
with:
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
path: ${{ env.JOB_TRANSFER_ARTIFACT }}
- name: Upload tester build artifact
uses: actions/upload-artifact@v2
with:
@@ -135,24 +140,24 @@ jobs:
env:
IS_RELEASE: ${{ startsWith(github.ref, 'refs/tags/') }}
run: |
export LATEST_TAG=$(git describe --abbrev=0)
export GIT_LOG=$(git log --pretty=" - %s [%h]" $LATEST_TAG..HEAD | sed 's/ *$//g')
if [ "$IS_RELEASE" = true ]; then
export BODY=$(echo -e "$GIT_LOG")
else
export LATEST_TAG_WITH_LINK=$(echo "[$LATEST_TAG](https://github.com/arduino/arduino-ide/releases/tag/$LATEST_TAG)")
if [ -z "$GIT_LOG" ]; then
export BODY="There were no changes since version $LATEST_TAG_WITH_LINK."
else
export BODY=$(echo -e "Changes since version $LATEST_TAG_WITH_LINK:\n$GIT_LOG")
fi
export LATEST_TAG=$(git describe --abbrev=0)
export GIT_LOG=$(git log --pretty=" - %s [%h]" $LATEST_TAG..HEAD | sed 's/ *$//g')
if [ "$IS_RELEASE" = true ]; then
export BODY=$(echo -e "$GIT_LOG")
else
export LATEST_TAG_WITH_LINK=$(echo "[$LATEST_TAG](https://github.com/arduino/arduino-ide/releases/tag/$LATEST_TAG)")
if [ -z "$GIT_LOG" ]; then
export BODY="There were no changes since version $LATEST_TAG_WITH_LINK."
else
export BODY=$(echo -e "Changes since version $LATEST_TAG_WITH_LINK:\n$GIT_LOG")
fi
echo -e "$BODY"
OUTPUT_SAFE_BODY="${BODY//'%'/'%25'}"
OUTPUT_SAFE_BODY="${OUTPUT_SAFE_BODY//$'\n'/'%0A'}"
OUTPUT_SAFE_BODY="${OUTPUT_SAFE_BODY//$'\r'/'%0D'}"
echo "::set-output name=BODY::$OUTPUT_SAFE_BODY"
echo "$BODY" > CHANGELOG.txt
fi
echo -e "$BODY"
OUTPUT_SAFE_BODY="${BODY//'%'/'%25'}"
OUTPUT_SAFE_BODY="${OUTPUT_SAFE_BODY//$'\n'/'%0A'}"
OUTPUT_SAFE_BODY="${OUTPUT_SAFE_BODY//$'\r'/'%0D'}"
echo "::set-output name=BODY::$OUTPUT_SAFE_BODY"
echo "$BODY" > CHANGELOG.txt
- name: Upload Changelog [GitHub Actions]
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main')
@@ -175,9 +180,9 @@ jobs:
- name: Publish Nightly [S3]
uses: docker://plugins/s3
env:
PLUGIN_SOURCE: "${{ env.JOB_TRANSFER_ARTIFACT }}/*"
PLUGIN_STRIP_PREFIX: "${{ env.JOB_TRANSFER_ARTIFACT }}/"
PLUGIN_TARGET: "/arduino-ide/nightly"
PLUGIN_SOURCE: '${{ env.JOB_TRANSFER_ARTIFACT }}/*'
PLUGIN_STRIP_PREFIX: '${{ env.JOB_TRANSFER_ARTIFACT }}/'
PLUGIN_TARGET: '/arduino-ide/nightly'
PLUGIN_BUCKET: ${{ secrets.DOWNLOADS_BUCKET }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
@@ -211,9 +216,9 @@ jobs:
- name: Publish Release [S3]
uses: docker://plugins/s3
env:
PLUGIN_SOURCE: "${{ env.JOB_TRANSFER_ARTIFACT }}/*"
PLUGIN_STRIP_PREFIX: "${{ env.JOB_TRANSFER_ARTIFACT }}/"
PLUGIN_TARGET: "/arduino-ide"
PLUGIN_SOURCE: '${{ env.JOB_TRANSFER_ARTIFACT }}/*'
PLUGIN_STRIP_PREFIX: '${{ env.JOB_TRANSFER_ARTIFACT }}/'
PLUGIN_TARGET: '/arduino-ide'
PLUGIN_BUCKET: ${{ secrets.DOWNLOADS_BUCKET }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

View File

@@ -25,10 +25,10 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node.js 12.x
- name: Install Node.js 14.x
uses: actions/setup-node@v2
with:
node-version: '12.14.1'
node-version: '14.x'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies

View File

@@ -0,0 +1,45 @@
name: Compose full changelog
on:
release:
types: [created, edited]
env:
CHANGELOG_ARTIFACTS: changelog
jobs:
create-changelog:
if: github.repository == 'arduino/arduino-ide'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Get Tag
id: tag_name
run: |
echo ::set-output name=TAG_NAME::${GITHUB_REF#refs/tags/}
- name: Create full changelog
id: full-changelog
run: |
mkdir "${{ github.workspace }}/${{ env.CHANGELOG_ARTIFACTS }}"
# Get the changelog file name to build
CHANGELOG_FILE_NAME="${{ steps.tag_name.outputs.TAG_NAME }}-$(date --iso-8601=s).md"
# Create manifest file pointing to latest changelog file name
echo "$CHANGELOG_FILE_NAME" >> "${{ github.workspace }}/${{ env.CHANGELOG_ARTIFACTS }}/latest.txt"
# Compose changelog
yarn run compose-changelog "${{ github.workspace }}/${{ env.CHANGELOG_ARTIFACTS }}/$CHANGELOG_FILE_NAME"
- name: Publish Changelog [S3]
uses: docker://plugins/s3
env:
PLUGIN_SOURCE: '${{ env.CHANGELOG_ARTIFACTS }}/*'
PLUGIN_STRIP_PREFIX: '${{ env.CHANGELOG_ARTIFACTS }}/'
PLUGIN_TARGET: '/arduino-ide/changelog'
PLUGIN_BUCKET: ${{ secrets.DOWNLOADS_BUCKET }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

View File

@@ -12,10 +12,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- name: Install Node.js 12.x
- name: Install Node.js 14.x
uses: actions/setup-node@v2
with:
node-version: '12.14.1'
node-version: '14.x'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies

View File

@@ -12,10 +12,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- name: Install Node.js 12.x
- name: Install Node.js 14.x
uses: actions/setup-node@v2
with:
node-version: '12.14.1'
node-version: '14.x'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies

View File

@@ -31,7 +31,7 @@ jobs:
- name: Download JSON schema for labels configuration file
id: download-schema
uses: carlosperate/download-file-action@v1.0.3
uses: carlosperate/download-file-action@v1
with:
file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/arduino-tooling-gh-label-configuration-schema.json
location: ${{ runner.temp }}/label-configuration-schema
@@ -66,7 +66,7 @@ jobs:
steps:
- name: Download
uses: carlosperate/download-file-action@v1.0.3
uses: carlosperate/download-file-action@v1
with:
file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }}

16
.vscode/launch.json vendored
View File

@@ -37,6 +37,13 @@
"internalConsoleOptions": "openOnSessionStart",
"outputCapture": "std"
},
{
"type": "chrome",
"request": "attach",
"name": "Attach to Electron Frontend",
"port": 9222,
"webRoot": "${workspaceFolder}/electron-app"
},
{
"type": "node",
"request": "launch",
@@ -104,5 +111,14 @@
"program": "${workspaceRoot}/electron/packager/index.js",
"cwd": "${workspaceFolder}/electron/packager"
}
],
"compounds": [
{
"name": "Launch Electron Backend & Frontend",
"configurations": [
"App (Electron)",
"Attach to Electron Frontend"
]
}
]
}

View File

@@ -40,22 +40,31 @@ The _frontend_ is running as an Electron renderer process and can invoke service
## Build from source
If youre familiar with TypeScript, the [Theia IDE](https://theia-ide.org/), and if you want to contribute to the
project, you should be able to build the Arduino IDE locally. Please refer to the [Theia IDE prerequisites](https://github.com/theia-ide/theia/blob/master/doc/) documentation for the setup instructions.
project, you should be able to build the Arduino IDE locally.
Please refer to the [Theia IDE prerequisites](https://github.com/theia-ide/theia/blob/master/doc/) documentation for the setup instructions.
> **Note**: Node.js 14 must be used instead of the version 12 recommended at the link above.
### Build
```sh
yarn
```
Once you have all the tools installed, you can build the editor following these steps
### Rebuild the native dependencies
```sh
yarn rebuild:electron
```
1. Install the dependencies and build
```sh
yarn
```
### Start
```sh
yarn start
```
2. Rebuild the dependencies
```sh
yarn rebuild:browser
```
3. Rebuild the electron dependencies
```sh
yarn rebuild:electron
```
4. Start the application
```sh
yarn start
```
### CI
@@ -117,7 +126,7 @@ git add . \
git tag -a 0.2.0 -m "0.2.0" \
&& git push origin 0.2.0
```
- The release build starts automatically and uploads the artifacts with the changelog to the [release page](https://github.com/arduino/arduino-ide/releases).
- The release build starts automatically and uploads the artifacts with the changelog to the [release page](https://github.com/arduino/arduino-ide/releases).
- If you do not want to release the `EXE` and `MSI` installers, wipe them manually.
- If you do not like the generated changelog, modify it and update the GH release.

View File

@@ -15,29 +15,30 @@ The Arduino IDE 2.x is a major rewrite, sharing no code with the IDE 1.x. It is
## Download
You can download the latest version from the [software download page on the Arduino website](https://www.arduino.cc/en/software#experimental-software).
### Nightly builds
These builds are generated every day at 03:00 GMT from the `main` branch and
should be considered unstable:
Platform | 32 bit | 64 bit |
--------- | ------------------------ | ------------------------------------------------------------------------------------------------------ |
Linux | | [Nightly Linux 64 bit] |
Linux ARM | [🚧 Work in progress...] | [🚧 Work in progress...] |
Windows | | [Nightly Windows 64 bit installer]<br />[Nightly Windows 64 bit MSI]<br />[Nightly Windows 64 bit ZIP] |
macOS | | [Nightly macOS 64 bit] |
| Platform | 32 bit | 64 bit |
| --------- | ------------------------ | ------------------------------------------------------------------------------------------------------ |
| Linux | | [Nightly Linux 64 bit] |
| Linux ARM | [🚧 Work in progress...] | [🚧 Work in progress...] |
| Windows | | [Nightly Windows 64 bit installer]<br />[Nightly Windows 64 bit MSI]<br />[Nightly Windows 64 bit ZIP] |
| macOS | | [Nightly macOS 64 bit] |
[🚧 Work in progress...]: https://github.com/arduino/arduino-ide/issues/107
[Nightly Linux 64 bit]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Linux_64bit.zip
[Nightly Windows 64 bit installer]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Windows_64bit.exe
[Nightly Windows 64 bit MSI]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Windows_64bit.msi
[Nightly Windows 64 bit ZIP]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Windows_64bit.zip
[Nightly macOS 64 bit]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_macOS_64bit.dmg
[🚧 work in progress...]: https://github.com/arduino/arduino-ide/issues/107
[nightly linux 64 bit]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Linux_64bit.zip
[nightly windows 64 bit installer]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Windows_64bit.exe
[nightly windows 64 bit msi]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Windows_64bit.msi
[nightly windows 64 bit zip]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Windows_64bit.zip
[nightly macos 64 bit]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_macOS_64bit.dmg
> These links return an HTTP `302: Found` response, redirecting to latest
generated builds by replacing `latest` with the latest available build
date, using the format YYYYMMDD (i.e for 2019/Aug/06 `latest` is
replaced with `20190806`)
> generated builds by replacing `latest` with the latest available build
> date, using the format YYYYMMDD (i.e for 2019/Aug/06 `latest` is
> replaced with `20190806`)
## Support
@@ -47,8 +48,8 @@ If you need assistance, see the [Help Center](https://support.arduino.cc/hc/en-u
If you want to report an issue, you can submit it to the [issue tracker](https://github.com/arduino/arduino-ide/issues) of this repository. A few rules apply:
* Before posting, please check if the same problem has been already reported by someone else to avoid duplicates.
* Remember to include as much detail as you can about your hardware set-up, code and steps for reproducing the issue. Make sure you're using an original Arduino board.
- Before posting, please check if the same problem has been already reported by someone else to avoid duplicates.
- Remember to include as much detail as you can about your hardware set-up, code and steps for reproducing the issue. Make sure you're using an original Arduino board.
### Security
@@ -64,10 +65,13 @@ Contributions are very welcome! You can browse the list of open issues to see wh
This repository contains the main code, but two more repositories are included during the build process:
* [vscode-arduino-tools](https://github.com/arduino/vscode-arduino-tools): provides support for the language server and the debugger
* [arduino-language-server](https://github.com/arduino/arduino-language-server): provides the language server that parses Arduino code
- [vscode-arduino-tools](https://github.com/arduino/vscode-arduino-tools): provides support for the language server and the debugger
- [arduino-language-server](https://github.com/arduino/arduino-language-server): provides the language server that parses Arduino code
See the [BUILDING.md](BUILDING.md) for a technical overview of the application and instructions for building the code.
You can help with the translation of the Arduino IDE to your language here: [Arduino IDE on Transifex](https://www.transifex.com/arduino-1/ide2/dashboard/).
## Donations
This open source code was written by the Arduino team and is maintained on a daily basis with the help of the community. We invest a considerable amount of time in development, testing and optimization. Please consider [donating](https://www.arduino.cc/en/donate/) or [sponsoring](https://github.com/sponsors/arduino) to support our work, as well as [buying original Arduino boards](https://store.arduino.cc/) which is the best way to make sure our effort can continue in the long term.

View File

@@ -1,11 +1,12 @@
{
"name": "arduino-ide-extension",
"version": "2.0.0-rc2",
"version": "2.0.0-rc4",
"description": "An extension for Theia building the Arduino IDE",
"license": "AGPL-3.0-or-later",
"scripts": {
"prepare": "yarn download-cli && yarn download-fwuploader && yarn download-ls && yarn copy-serial-plotter && yarn clean && yarn download-examples && yarn build && yarn test",
"clean": "rimraf lib",
"compose-changelog": "node ./scripts/compose-changelog.js",
"download-cli": "node ./scripts/download-cli.js",
"download-fwuploader": "node ./scripts/download-fwuploader.js",
"copy-serial-plotter": "npx ncp ../node_modules/arduino-serial-plotter-webapp ./build/arduino-serial-plotter-webapp",
@@ -20,22 +21,23 @@
},
"dependencies": {
"@grpc/grpc-js": "^1.3.7",
"@theia/application-package": "1.19.0",
"@theia/core": "1.19.0",
"@theia/editor": "1.19.0",
"@theia/editor-preview": "1.19.0",
"@theia/filesystem": "1.19.0",
"@theia/git": "1.19.0",
"@theia/keymaps": "1.19.0",
"@theia/markers": "1.19.0",
"@theia/monaco": "1.19.0",
"@theia/navigator": "1.19.0",
"@theia/outline-view": "1.19.0",
"@theia/output": "1.19.0",
"@theia/preferences": "1.19.0",
"@theia/search-in-workspace": "1.19.0",
"@theia/terminal": "1.19.0",
"@theia/workspace": "1.19.0",
"@theia/application-package": "1.22.1",
"@theia/core": "1.22.1",
"@theia/editor": "1.22.1",
"@theia/editor-preview": "1.22.1",
"@theia/electron": "1.22.1",
"@theia/filesystem": "1.22.1",
"@theia/git": "1.22.1",
"@theia/keymaps": "1.22.1",
"@theia/markers": "1.22.1",
"@theia/monaco": "1.22.1",
"@theia/navigator": "1.22.1",
"@theia/outline-view": "1.22.1",
"@theia/output": "1.22.1",
"@theia/preferences": "1.22.1",
"@theia/search-in-workspace": "1.22.1",
"@theia/terminal": "1.22.1",
"@theia/workspace": "1.22.1",
"@tippyjs/react": "^4.2.5",
"@types/atob": "^2.1.2",
"@types/auth0-js": "^9.14.0",
@@ -55,7 +57,7 @@
"@types/temp": "^0.8.34",
"@types/which": "^1.3.1",
"ajv": "^6.5.3",
"arduino-serial-plotter-webapp": "0.0.15",
"arduino-serial-plotter-webapp": "0.0.17",
"async-mutex": "^0.3.0",
"atob": "^2.1.2",
"auth0-js": "^9.14.0",
@@ -63,6 +65,7 @@
"css-element-queries": "^1.2.0",
"dateformat": "^3.0.3",
"deepmerge": "2.0.1",
"electron-updater": "^4.6.5",
"fuzzy": "^0.1.3",
"glob": "^7.1.6",
"google-protobuf": "^3.11.4",
@@ -80,6 +83,7 @@
"ps-tree": "^1.2.0",
"query-string": "^7.0.1",
"react-disable": "^0.1.0",
"react-markdown": "^8.0.0",
"react-select": "^3.0.4",
"react-tabs": "^3.1.2",
"react-window": "^1.8.6",
@@ -92,6 +96,7 @@
"which": "^1.3.1"
},
"devDependencies": {
"@octokit/rest": "^18.12.0",
"@types/chai": "^4.2.7",
"@types/chai-string": "^1.4.2",
"@types/mocha": "^5.2.7",
@@ -151,10 +156,16 @@
],
"arduino": {
"cli": {
"version": "0.20.2"
"version": "0.21.0"
},
"fwuploader": {
"version": "2.0.0"
},
"clangd": {
"version": "13.0.0"
},
"languageServer": {
"version": "0.6.0"
}
}
}
}

View File

@@ -0,0 +1,116 @@
// @ts-check
(async () => {
const { Octokit } = require('@octokit/rest');
const fs = require('fs');
const path = require('path');
const octokit = new Octokit({
userAgent: 'Arduino IDE compose-changelog.js',
});
const response = await octokit.rest.repos
.listReleases({
owner: 'arduino',
repo: 'arduino-ide',
})
.catch((err) => {
console.error(err);
process.exit(1);
});
const releases = response.data;
let fullChangelog = releases.reduce((acc, item, index) => {
// Process each line separately
const body = item.body.split('\n').map(processLine).join('\n');
// item.name is the name of the release changelog
return (
acc +
`## ${item.name}\n\n${body}${
index !== releases.length - 1 ? '\n\n---\n\n' : '\n'
}`
);
}, '');
const args = process.argv.slice(2);
if (args.length == 0) {
console.error('Missing argument to destination file');
process.exit(1);
}
const changelogFile = path.resolve(args[0]);
await fs.writeFile(
changelogFile,
fullChangelog,
{
flag: 'w+',
},
(err) => {
if (err) {
console.error(err);
process.exit(1);
}
console.log('Changelog written to', changelogFile);
}
);
})();
// processLine applies different substitutions to line string.
// We're assuming that there are no more than one substitution
// per line to be applied.
const processLine = (line) => {
// Check if a link with one of the following format exists:
// * [#123](https://github.com/arduino/arduino-ide/pull/123)
// * [#123](https://github.com/arduino/arduino-ide/issues/123)
// * [#123](https://github.com/arduino/arduino-ide/pull/123/)
// * [#123](https://github.com/arduino/arduino-ide/issues/123/)
// If it does return the line as is.
let r =
/(\(|\[)#\d+(\)|\])(\(|\[)https:\/\/github\.com\/arduino\/arduino-ide\/(pull|issues)\/(\d+)\/?(\)|\])/gm;
if (r.test(line)) {
return line;
}
// Check if a issue or PR link with the following format exists:
// * #123
// If it does it's changed to:
// * [#123](https://github.com/arduino/arduino-ide/pull/123)
r = /(?<![\w\d\/_]{1})#((\d)+)(?![\w\d\/_]{1})/gm;
if (r.test(line)) {
return line.replace(
r,
`[#$1](https://github.com/arduino/arduino-ide/pull/$1)`
);
}
// Check if a link with one of the following format exists:
// * https://github.com/arduino/arduino-ide/pull/123
// * https://github.com/arduino/arduino-ide/issues/123
// * https://github.com/arduino/arduino-ide/pull/123/
// * https://github.com/arduino/arduino-ide/issues/123/
// If it does it's changed respectively to:
// * [#123](https://github.com/arduino/arduino-ide/pull/123)
// * [#123](https://github.com/arduino/arduino-ide/issues/123)
// * [#123](https://github.com/arduino/arduino-ide/pull/123/)
// * [#123](https://github.com/arduino/arduino-ide/issues/123/)
r =
/(https:\/\/github\.com\/arduino\/arduino-ide\/(pull|issues)\/(\d+)\/?)/gm;
if (r.test(line)) {
return line.replace(r, `[#$3]($1)`);
}
// Check if a link with the following format exists:
// * https://github.com/arduino/arduino-ide/compare/2.0.0-rc2...2.0.0-rc3
// * https://github.com/arduino/arduino-ide/compare/2.0.0-rc2...2.0.0-rc3/
// If it does it's changed to:
// * [`2.0.0-rc2...2.0.0-rc3`](https://github.com/arduino/arduino-ide/compare/2.0.0-rc2...2.0.0-rc3)
r =
/(https:\/\/github\.com\/arduino\/arduino-ide\/compare\/([^\/]*))\/?\s?/gm;
if (r.test(line)) {
return line.replace(r, '[`$2`]($1)');
}
// If nothing matches just return the line as is
return line;
};

View File

@@ -4,13 +4,38 @@
// - https://downloads.arduino.cc/arduino-language-server/clangd/clangd_${VERSION}_${SUFFIX}
(() => {
const DEFAULT_ALS_VERSION = '0.5.0';
const DEFAULT_CLANGD_VERSION = 'snapshot_20210124';
const path = require('path');
const shell = require('shelljs');
const downloader = require('./downloader');
const [DEFAULT_ALS_VERSION, DEFAULT_CLANGD_VERSION] = (() => {
const pkg = require(path.join(__dirname, '..', 'package.json'));
if (!pkg) return undefined;
const { arduino } = pkg;
if (!arduino) return undefined;
const { languageServer, clangd } = arduino;
if (!languageServer) return undefined;
if (!clangd) return undefined;
return [languageServer.version, clangd.version];
})();
if (!DEFAULT_ALS_VERSION) {
shell.echo(
`Could not retrieve Arduino Language Server version info from the 'package.json'.`
);
shell.exit(1);
}
if (!DEFAULT_CLANGD_VERSION) {
shell.echo(
`Could not retrieve clangd version info from the 'package.json'.`
);
shell.exit(1);
}
const yargs = require('yargs')
.option('ls-version', {
alias: 'lv',
@@ -20,7 +45,7 @@
.option('clangd-version', {
alias: 'cv',
default: DEFAULT_CLANGD_VERSION,
choices: ['snapshot_20210124'],
choices: [DEFAULT_CLANGD_VERSION, 'snapshot_20210124'],
describe: `The version of 'clangd' to download. Defaults to ${DEFAULT_CLANGD_VERSION}.`,
})
.option('force-download', {
@@ -35,32 +60,32 @@
const clangdVersion = yargs['clangd-version'];
const force = yargs['force-download'];
const { platform, arch } = process;
const platformArch = platform + '-' + arch;
const build = path.join(__dirname, '..', 'build');
const lsExecutablePath = path.join(
build,
`arduino-language-server${platform === 'win32' ? '.exe' : ''}`
);
let clangdExecutablePath, lsSuffix, clangdSuffix;
let clangdExecutablePath, lsSuffix, clangdPrefix;
switch (platform) {
case 'darwin':
clangdExecutablePath = path.join(build, 'bin', 'clangd');
switch (platformArch) {
case 'darwin-x64':
clangdExecutablePath = path.join(build, 'clangd');
lsSuffix = 'macOS_64bit.tar.gz';
clangdPrefix = 'mac';
clangdSuffix = 'macOS_64bit';
break;
case 'linux':
clangdExecutablePath = path.join(build, 'bin', 'clangd');
case 'linux-x64':
clangdExecutablePath = path.join(build, 'clangd');
lsSuffix = 'Linux_64bit.tar.gz';
clangdPrefix = 'linux';
clangdSuffix = 'Linux_64bit';
break;
case 'win32':
clangdExecutablePath = path.join(build, 'bin', 'clangd.exe');
case 'win32-x64':
clangdExecutablePath = path.join(build, 'clangd.exe');
lsSuffix = 'Windows_64bit.zip';
clangdPrefix = 'windows';
clangdSuffix = 'Windows_64bit';
break;
}
if (!lsSuffix) {
if (!lsSuffix || !clangdSuffix) {
shell.echo(
`The arduino-language-server is not available for ${platform} ${arch}.`
);
@@ -74,7 +99,7 @@
}_${lsSuffix}`;
downloader.downloadUnzipAll(alsUrl, build, lsExecutablePath, force);
const clangdUrl = `https://downloads.arduino.cc/arduino-language-server/clangd/clangd-${clangdPrefix}-${clangdVersion}.zip`;
const clangdUrl = `https://downloads.arduino.cc/tools/clangd_${clangdVersion}_${clangdSuffix}.tar.bz2`;
downloader.downloadUnzipAll(clangdUrl, build, clangdExecutablePath, force, {
strip: 1,
}); // `strip`: the new clangd (12.x) is zipped into a folder, so we have to strip the outmost folder.

View File

@@ -5,16 +5,17 @@ const download = require('download');
const decompress = require('decompress');
const unzip = require('decompress-unzip');
const untargz = require('decompress-targz');
const untarbz2 = require('decompress-tarbz2');
process.on('unhandledRejection', (reason, _) => {
shell.echo(String(reason));
shell.exit(1);
throw reason;
shell.echo(String(reason));
shell.exit(1);
throw reason;
});
process.on('uncaughtException', error => {
shell.echo(String(error));
shell.exit(1);
throw error;
process.on('uncaughtException', (error) => {
shell.echo(String(error));
shell.exit(1);
throw error;
});
/**
@@ -23,55 +24,62 @@ process.on('uncaughtException', error => {
* @param filePrefix {string} Prefix of the file name found in the archive
* @param force {boolean} Whether to download even if the target file exists. `false` by default.
*/
exports.downloadUnzipFile = async (url, targetFile, filePrefix, force = false) => {
if (fs.existsSync(targetFile) && !force) {
shell.echo(`Skipping download because file already exists: ${targetFile}`);
return;
}
if (!fs.existsSync(path.dirname(targetFile))) {
if (shell.mkdir('-p', path.dirname(targetFile)).code !== 0) {
shell.echo('Could not create new directory.');
shell.exit(1);
}
exports.downloadUnzipFile = async (
url,
targetFile,
filePrefix,
force = false
) => {
if (fs.existsSync(targetFile) && !force) {
shell.echo(`Skipping download because file already exists: ${targetFile}`);
return;
}
if (!fs.existsSync(path.dirname(targetFile))) {
if (shell.mkdir('-p', path.dirname(targetFile)).code !== 0) {
shell.echo('Could not create new directory.');
shell.exit(1);
}
}
const downloads = path.join(__dirname, '..', 'downloads');
if (shell.rm('-rf', targetFile, downloads).code !== 0) {
shell.exit(1);
}
const downloads = path.join(__dirname, '..', 'downloads');
if (shell.rm('-rf', targetFile, downloads).code !== 0) {
shell.exit(1);
}
shell.echo(`>>> Downloading from '${url}'...`);
const data = await download(url);
shell.echo(`<<< Download succeeded.`);
shell.echo(`>>> Downloading from '${url}'...`);
const data = await download(url);
shell.echo(`<<< Download succeeded.`);
shell.echo('>>> Decompressing...');
const files = await decompress(data, downloads, {
plugins: [
unzip(),
untargz()
]
});
if (files.length === 0) {
shell.echo('Error ocurred while decompressing the archive.');
shell.exit(1);
}
const fileIndex = files.findIndex(f => f.path.startsWith(filePrefix));
if (fileIndex === -1) {
shell.echo(`The downloaded artifact does not contain any file with prefix ${filePrefix}.`);
shell.exit(1);
}
shell.echo('<<< Decompressing succeeded.');
shell.echo('>>> Decompressing...');
const files = await decompress(data, downloads, {
plugins: [unzip(), untargz(), untarbz2()],
});
if (files.length === 0) {
shell.echo('Error ocurred while decompressing the archive.');
shell.exit(1);
}
const fileIndex = files.findIndex((f) => f.path.startsWith(filePrefix));
if (fileIndex === -1) {
shell.echo(
`The downloaded artifact does not contain any file with prefix ${filePrefix}.`
);
shell.exit(1);
}
shell.echo('<<< Decompressing succeeded.');
if (shell.mv('-f', path.join(downloads, files[fileIndex].path), targetFile).code !== 0) {
shell.echo(`Could not move file to target path: ${targetFile}`);
shell.exit(1);
}
if (!fs.existsSync(targetFile)) {
shell.echo(`Could not find file: ${targetFile}`);
shell.exit(1);
}
shell.echo(`Done: ${targetFile}`);
}
if (
shell.mv('-f', path.join(downloads, files[fileIndex].path), targetFile)
.code !== 0
) {
shell.echo(`Could not move file to target path: ${targetFile}`);
shell.exit(1);
}
if (!fs.existsSync(targetFile)) {
shell.echo(`Could not find file: ${targetFile}`);
shell.exit(1);
}
shell.echo(`Done: ${targetFile}`);
};
/**
* @param url {string} Download URL
@@ -79,42 +87,45 @@ exports.downloadUnzipFile = async (url, targetFile, filePrefix, force = false) =
* @param targetFile {string} Path to the main file expected after decompressing
* @param force {boolean} Whether to download even if the target file exists
*/
exports.downloadUnzipAll = async (url, targetDir, targetFile, force, decompressOptions = undefined) => {
if (fs.existsSync(targetFile) && !force) {
shell.echo(`Skipping download because file already exists: ${targetFile}`);
return;
}
if (!fs.existsSync(targetDir)) {
if (shell.mkdir('-p', targetDir).code !== 0) {
shell.echo('Could not create new directory.');
shell.exit(1);
}
exports.downloadUnzipAll = async (
url,
targetDir,
targetFile,
force,
decompressOptions = undefined
) => {
if (fs.existsSync(targetFile) && !force) {
shell.echo(`Skipping download because file already exists: ${targetFile}`);
return;
}
if (!fs.existsSync(targetDir)) {
if (shell.mkdir('-p', targetDir).code !== 0) {
shell.echo('Could not create new directory.');
shell.exit(1);
}
}
shell.echo(`>>> Downloading from '${url}'...`);
const data = await download(url);
shell.echo(`<<< Download succeeded.`);
shell.echo(`>>> Downloading from '${url}'...`);
const data = await download(url);
shell.echo(`<<< Download succeeded.`);
shell.echo('>>> Decompressing...');
let options = {
plugins: [
unzip(),
untargz()
]
};
if (decompressOptions) {
options = Object.assign(options, decompressOptions)
}
const files = await decompress(data, targetDir, options);
if (files.length === 0) {
shell.echo('Error ocurred while decompressing the archive.');
shell.exit(1);
}
shell.echo('<<< Decompressing succeeded.');
shell.echo('>>> Decompressing...');
let options = {
plugins: [unzip(), untargz(), untarbz2()],
};
if (decompressOptions) {
options = Object.assign(options, decompressOptions);
}
const files = await decompress(data, targetDir, options);
if (files.length === 0) {
shell.echo('Error ocurred while decompressing the archive.');
shell.exit(1);
}
shell.echo('<<< Decompressing succeeded.');
if (!fs.existsSync(targetFile)) {
shell.echo(`Could not find file: ${targetFile}`);
shell.exit(1);
}
shell.echo(`Done: ${targetFile}`);
}
if (!fs.existsSync(targetFile)) {
shell.echo(`Could not find file: ${targetFile}`);
shell.exit(1);
}
shell.echo(`Done: ${targetFile}`);
};

View File

@@ -1,9 +1,8 @@
import { inject, injectable, postConstruct } from 'inversify';
import * as React from 'react';
import { remote } from 'electron';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import {
BoardsService,
Port,
SketchesService,
ExecutableService,
Sketch,
@@ -69,8 +68,11 @@ import { ArduinoPreferences } from './arduino-preferences';
import { SketchesServiceClientImpl } from '../common/protocol/sketches-service-client-impl';
import { SaveAsSketch } from './contributions/save-as-sketch';
import { SketchbookWidgetContribution } from './widgets/sketchbook/sketchbook-widget-contribution';
import { IDEUpdaterDialog } from './dialogs/ide-updater/ide-updater-dialog';
import { IDEUpdater } from '../common/protocol/ide-updater';
const INIT_LIBS_AND_PACKAGES = 'initializedLibsAndPackages';
export const SKIP_IDE_VERSION = 'skipIDEVersion';
@injectable()
export class ArduinoFrontendContribution
@@ -79,8 +81,7 @@ export class ArduinoFrontendContribution
TabBarToolbarContribution,
CommandContribution,
MenuContribution,
ColorContribution
{
ColorContribution {
@inject(ILogger)
protected logger: ILogger;
@@ -158,6 +159,12 @@ export class ArduinoFrontendContribution
@inject(LocalStorageService)
protected readonly localStorageService: LocalStorageService;
@inject(IDEUpdater)
protected readonly updater: IDEUpdater;
@inject(IDEUpdaterDialog)
protected readonly updaterDialog: IDEUpdaterDialog;
protected invalidConfigPopup:
| Promise<void | 'No' | 'Yes' | undefined>
| undefined;
@@ -216,7 +223,7 @@ export class ArduinoFrontendContribution
? nls.localize(
'arduino/common/selectedOn',
'on {0}',
Port.toString(selectedPort)
selectedPort.address
)
: nls.localize('arduino/common/notConnected', '[not connected]'),
className: 'arduino-selected-port',
@@ -252,7 +259,7 @@ export class ArduinoFrontendContribution
});
}
onStart(app: FrontendApplication): void {
async onStart(app: FrontendApplication): Promise<void> {
// Initialize all `pro-mode` widgets. This is a NOOP if in normal mode.
for (const viewContribution of [
this.fileNavigatorContributions,
@@ -267,6 +274,31 @@ export class ArduinoFrontendContribution
viewContribution.initializeLayout(app);
}
}
this.updater
.init(
this.arduinoPreferences.get('arduino.ide.updateChannel'),
this.arduinoPreferences.get('arduino.ide.updateBaseUrl')
)
.then(() => this.updater.checkForUpdates(true))
.then(async (updateInfo) => {
if (!updateInfo) return;
const versionToSkip = await this.localStorageService.getData<string>(
SKIP_IDE_VERSION
);
if (versionToSkip === updateInfo.version) return;
this.updaterDialog.open(updateInfo);
})
.catch((e) => {
this.messageService.error(
nls.localize(
'arduino/ide-updater/errorCheckingForUpdates',
'Error while checking for Arduino IDE updates.\n{0}',
e.message
)
);
});
const start = async ({ selectedBoard }: BoardsConfig.Config) => {
if (selectedBoard) {
const { name, fqbn } = selectedBoard;
@@ -277,11 +309,25 @@ export class ArduinoFrontendContribution
};
this.boardsServiceClientImpl.onBoardsConfigChanged(start);
this.arduinoPreferences.onPreferenceChanged((event) => {
if (
event.preferenceName === 'arduino.language.log' &&
event.newValue !== event.oldValue
) {
start(this.boardsServiceClientImpl.boardsConfig);
if (event.newValue !== event.oldValue) {
switch (event.preferenceName) {
case 'arduino.language.log':
start(this.boardsServiceClientImpl.boardsConfig);
break;
case 'arduino.window.zoomLevel':
if (typeof event.newValue === 'number') {
const webContents = remote.getCurrentWebContents();
webContents.setZoomLevel(event.newValue || 0);
}
break;
case 'arduino.ide.updateChannel':
case 'arduino.ide.updateBaseUrl':
this.updater.init(
this.arduinoPreferences.get('arduino.ide.updateChannel'),
this.arduinoPreferences.get('arduino.ide.updateBaseUrl')
);
break;
}
}
});
this.arduinoPreferences.ready.then(() => {
@@ -289,16 +335,7 @@ export class ArduinoFrontendContribution
const zoomLevel = this.arduinoPreferences.get('arduino.window.zoomLevel');
webContents.setZoomLevel(zoomLevel);
});
this.arduinoPreferences.onPreferenceChanged((event) => {
if (
event.preferenceName === 'arduino.window.zoomLevel' &&
typeof event.newValue === 'number' &&
event.newValue !== event.oldValue
) {
const webContents = remote.getCurrentWebContents();
webContents.setZoomLevel(event.newValue || 0);
}
});
app.shell.leftPanelHandler.removeBottomMenu('settings-menu');
}

View File

@@ -262,6 +262,19 @@ import {
UserFieldsDialogWidget,
} from './dialogs/user-fields/user-fields-dialog';
import { nls } from '@theia/core/lib/common';
import { IDEUpdaterCommands } from './ide-updater/ide-updater-commands';
import {
IDEUpdater,
IDEUpdaterClient,
IDEUpdaterPath,
} from '../common/protocol/ide-updater';
import { IDEUpdaterClientImpl } from './ide-updater/ide-updater-client-impl';
import {
IDEUpdaterDialog,
IDEUpdaterDialogProps,
IDEUpdaterDialogWidget,
} from './dialogs/ide-updater/ide-updater-dialog';
import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider';
const ElementQueries = require('css-element-queries/src/ElementQueries');
@@ -407,8 +420,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(SerialService)
.toDynamicValue((context) => {
const connection = context.container.get(WebSocketConnectionProvider);
const client =
context.container.get<SerialServiceClient>(SerialServiceClient);
const client = context.container.get<SerialServiceClient>(
SerialServiceClient
);
return connection.createProxy(SerialServicePath, client);
})
.inSingletonScope();
@@ -472,12 +486,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
.inSingletonScope();
rebind(TheiaEditorWidgetFactory).to(EditorWidgetFactory).inSingletonScope();
rebind(TabBarToolbarFactory).toFactory(
({ container: parentContainer }) =>
() => {
const container = parentContainer.createChild();
container.bind(TabBarToolbar).toSelf().inSingletonScope();
return container.get(TabBarToolbar);
}
({ container: parentContainer }) => () => {
const container = parentContainer.createChild();
container.bind(TabBarToolbar).toSelf().inSingletonScope();
return container.get(TabBarToolbar);
}
);
bind(OutputWidget).toSelf().inSingletonScope();
rebind(TheiaOutputWidget).toService(OutputWidget);
@@ -642,13 +655,15 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
// Enable the dirty indicator on uncloseable widgets.
rebind(TabBarRendererFactory).toFactory((context) => () => {
const contextMenuRenderer =
context.container.get<ContextMenuRenderer>(ContextMenuRenderer);
const contextMenuRenderer = context.container.get<ContextMenuRenderer>(
ContextMenuRenderer
);
const decoratorService = context.container.get<TabBarDecoratorService>(
TabBarDecoratorService
);
const iconThemeService =
context.container.get<IconThemeService>(IconThemeService);
const iconThemeService = context.container.get<IconThemeService>(
IconThemeService
);
return new TabBarRenderer(
contextMenuRenderer,
decoratorService,
@@ -756,9 +771,32 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
title: 'UploadCertificate',
});
bind(IDEUpdaterDialogWidget).toSelf().inSingletonScope();
bind(IDEUpdaterDialog).toSelf().inSingletonScope();
bind(IDEUpdaterDialogProps).toConstantValue({
title: 'IDEUpdater',
});
bind(UserFieldsDialogWidget).toSelf().inSingletonScope();
bind(UserFieldsDialog).toSelf().inSingletonScope();
bind(UserFieldsDialogProps).toConstantValue({
title: 'UserFields',
});
bind(IDEUpdaterCommands).toSelf().inSingletonScope();
bind(CommandContribution).toService(IDEUpdaterCommands);
// Frontend binding for the IDE Updater service
bind(IDEUpdaterClientImpl).toSelf().inSingletonScope();
bind(IDEUpdaterClient).toService(IDEUpdaterClientImpl);
bind(IDEUpdater)
.toDynamicValue((context) => {
const client = context.container.get(IDEUpdaterClientImpl);
return ElectronIpcConnectionProvider.createProxy(
context.container,
IDEUpdaterPath,
client
);
})
.inSingletonScope();
});

View File

@@ -9,6 +9,11 @@ import {
import { nls } from '@theia/core/lib/common';
import { CompilerWarningLiterals, CompilerWarnings } from '../common/protocol';
export enum UpdateChannel {
Stable = 'stable',
Nightly = 'nightly',
}
export const ArduinoConfigSchema: PreferenceSchema = {
type: 'object',
properties: {
@@ -64,13 +69,22 @@ export const ArduinoConfigSchema: PreferenceSchema = {
),
default: 0,
},
'arduino.ide.autoUpdate': {
type: 'boolean',
'arduino.ide.updateChannel': {
type: 'string',
enum: Object.values(UpdateChannel) as UpdateChannel[],
default: UpdateChannel.Stable,
description: nls.localize(
'arduino/preferences/ide.autoUpdate',
'True to enable automatic update checks. The IDE will check for updates automatically and periodically.'
'arduino/preferences/ide.updateChannel',
"Release channel to get updated from. 'stable' is the stable release, 'nightly' is the latest development build."
),
},
'arduino.ide.updateBaseUrl': {
type: 'string',
default: 'https://downloads.arduino.cc/arduino-ide',
description: nls.localize(
'arduino/preferences/ide.updateBaseUrl',
`The base URL where to download updates from. Defaults to 'https://downloads.arduino.cc/arduino-ide'`
),
default: true,
},
'arduino.board.certificates': {
type: 'string',
@@ -171,7 +185,8 @@ export interface ArduinoConfiguration {
'arduino.upload.verify': boolean;
'arduino.window.autoScale': boolean;
'arduino.window.zoomLevel': number;
'arduino.ide.autoUpdate': boolean;
'arduino.ide.updateChannel': UpdateChannel;
'arduino.ide.updateBaseUrl': string;
'arduino.board.certificates': string;
'arduino.sketchbook.showAllFiles': boolean;
'arduino.cloud.enabled': boolean;
@@ -188,16 +203,10 @@ export interface ArduinoConfiguration {
export const ArduinoPreferences = Symbol('ArduinoPreferences');
export type ArduinoPreferences = PreferenceProxy<ArduinoConfiguration>;
export function createArduinoPreferences(
preferences: PreferenceService
): ArduinoPreferences {
return createPreferenceProxy(preferences, ArduinoConfigSchema);
}
export function bindArduinoPreferences(bind: interfaces.Bind): void {
bind(ArduinoPreferences).toDynamicValue((ctx) => {
const preferences = ctx.container.get<PreferenceService>(PreferenceService);
return createArduinoPreferences(preferences);
return createPreferenceProxy(preferences, ArduinoConfigSchema);
});
bind(PreferenceContribution).toConstantValue({
schema: ArduinoConfigSchema,

View File

@@ -1,5 +1,4 @@
import { toUnix } from 'upath';
import URI from '@theia/core/lib/common/uri';
import { URI } from '@theia/core/shared/vscode-uri';
import { isWindows } from '@theia/core/lib/common/os';
import { notEmpty } from '@theia/core/lib/common/objects';
import { MaybePromise } from '@theia/core/lib/common/types';
@@ -61,12 +60,8 @@ export class ArduinoWorkspaceRootResolver {
// - https://github.com/eclipse-theia/theia/blob/8196e9dcf9c8de8ea0910efeb5334a974f426966/packages/workspace/src/browser/workspace-service.ts#L423
protected hashToUri(hash: string | undefined): string | undefined {
if (hash && hash.length > 1 && hash.startsWith('#')) {
const path = hash.slice(1); // Trim the leading `#`.
return new URI(
toUnix(path.slice(isWindows && hash.startsWith('/') ? 1 : 0))
)
.withScheme('file')
.toString();
const path = decodeURI(hash.slice(1)).replace(/\\/g, '/'); // Trim the leading `#`, decode the URI and replace Windows separators
return URI.file(path.slice(isWindows && hash.startsWith('/') ? 1 : 0)).toString();
}
return undefined;
}

View File

@@ -84,7 +84,7 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
),
nls.localize(
'arduino/board/configDialog2',
'If you only select a Board you will be able just to compile, but not to upload your sketch.'
'If you only select a Board you will be able to compile, but not to upload your sketch.'
),
]) {
const p = document.createElement('div');

View File

@@ -167,7 +167,7 @@ export class BoardsConfig extends React.Component<
this.queryPorts(Promise.resolve(ports)).then(({ knownPorts }) => {
let { selectedPort } = this.state;
// If the currently selected port is not available anymore, unset the selected port.
if (removedPorts.some((port) => Port.equals(port, selectedPort))) {
if (removedPorts.some((port) => Port.sameAs(port, selectedPort))) {
selectedPort = undefined;
}
this.setState({ knownPorts, selectedPort }, () =>
@@ -213,11 +213,11 @@ export class BoardsConfig extends React.Component<
} else if (left.protocol === right.protocol) {
// We show ports, including those that have guessed
// or unrecognized boards, so we must sort those too.
const leftBoard = this.availableBoards.find((board) =>
Port.sameAs(board.port, left)
const leftBoard = this.availableBoards.find(
(board) => board.port === left
);
const rightBoard = this.availableBoards.find((board) =>
Port.sameAs(board.port, right)
const rightBoard = this.availableBoards.find(
(board) => board.port === right
);
if (leftBoard && !rightBoard) {
return -1;
@@ -348,10 +348,10 @@ export class BoardsConfig extends React.Component<
<div className="ports list">
{ports.map((port) => (
<Item<Port>
key={Port.toString(port)}
key={`${port.id}`}
item={port}
label={Port.toString(port)}
selected={Port.equals(this.state.selectedPort, port)}
selected={Port.sameAs(this.state.selectedPort, port)}
onClick={this.selectPort}
/>
))}
@@ -410,7 +410,7 @@ export namespace BoardsConfig {
return options.default;
}
const { name } = selectedBoard;
return `${name}${port ? ' at ' + Port.toString(port) : ''}`;
return `${name}${port ? ` at ${port.address}` : ''}`;
}
export function setConfig(

View File

@@ -1,7 +1,6 @@
import { injectable, inject, named } from 'inversify';
import { ILogger } from '@theia/core/lib/common/logger';
import { deepClone } from '@theia/core/lib/common/objects';
import { MaybePromise } from '@theia/core/lib/common/types';
import { Event, Emitter } from '@theia/core/lib/common/event';
import {
FrontendApplicationContribution,
@@ -11,7 +10,6 @@ import { notEmpty } from '../../common/utils';
import {
BoardsService,
ConfigOption,
Installable,
BoardDetails,
Programmer,
} from '../../common/protocol';
@@ -36,16 +34,12 @@ export class BoardsDataStore implements FrontendApplicationContribution {
onStart(): void {
this.notificationCenter.onPlatformInstalled(async ({ item }) => {
const { installedVersion: version } = item;
if (!version) {
return;
}
let shouldFireChanged = false;
for (const fqbn of item.boards
.map(({ fqbn }) => fqbn)
.filter(notEmpty)
.filter((fqbn) => !!fqbn)) {
const key = this.getStorageKey(fqbn, version);
const key = this.getStorageKey(fqbn);
let data = await this.storageService.getData<
ConfigOption[] | undefined
>(key);
@@ -72,33 +66,20 @@ export class BoardsDataStore implements FrontendApplicationContribution {
async appendConfigToFqbn(
fqbn: string | undefined,
boardsPackageVersion: MaybePromise<
Installable.Version | undefined
> = this.getBoardsPackageVersion(fqbn)
): Promise<string | undefined> {
if (!fqbn) {
return undefined;
}
const { configOptions } = await this.getData(fqbn, boardsPackageVersion);
const { configOptions } = await this.getData(fqbn);
return ConfigOption.decorate(fqbn, configOptions);
}
async getData(
fqbn: string | undefined,
boardsPackageVersion: MaybePromise<
Installable.Version | undefined
> = this.getBoardsPackageVersion(fqbn)
): Promise<BoardsDataStore.Data> {
async getData(fqbn: string | undefined): Promise<BoardsDataStore.Data> {
if (!fqbn) {
return BoardsDataStore.Data.EMPTY;
}
const version = await boardsPackageVersion;
if (!version) {
return BoardsDataStore.Data.EMPTY;
}
const key = this.getStorageKey(fqbn, version);
const key = this.getStorageKey(fqbn);
let data = await this.storageService.getData<
BoardsDataStore.Data | undefined
>(key, undefined);
@@ -124,25 +105,16 @@ export class BoardsDataStore implements FrontendApplicationContribution {
fqbn,
selectedProgrammer,
}: { fqbn: string; selectedProgrammer: Programmer },
boardsPackageVersion: MaybePromise<
Installable.Version | undefined
> = this.getBoardsPackageVersion(fqbn)
): Promise<boolean> {
const data = deepClone(await this.getData(fqbn, boardsPackageVersion));
const data = deepClone(await this.getData(fqbn));
const { programmers } = data;
if (!programmers.find((p) => Programmer.equals(selectedProgrammer, p))) {
return false;
}
const version = await boardsPackageVersion;
if (!version) {
return false;
}
await this.setData({
fqbn,
data: { ...data, selectedProgrammer },
version,
});
this.fireChanged();
return true;
@@ -153,12 +125,9 @@ export class BoardsDataStore implements FrontendApplicationContribution {
fqbn,
option,
selectedValue,
}: { fqbn: string; option: string; selectedValue: string },
boardsPackageVersion: MaybePromise<
Installable.Version | undefined
> = this.getBoardsPackageVersion(fqbn)
}: { fqbn: string; option: string; selectedValue: string }
): Promise<boolean> {
const data = deepClone(await this.getData(fqbn, boardsPackageVersion));
const data = deepClone(await this.getData(fqbn));
const { configOptions } = data;
const configOption = configOptions.find((c) => c.option === option);
if (!configOption) {
@@ -176,12 +145,7 @@ export class BoardsDataStore implements FrontendApplicationContribution {
if (!updated) {
return false;
}
const version = await boardsPackageVersion;
if (!version) {
return false;
}
await this.setData({ fqbn, data, version });
await this.setData({ fqbn, data });
this.fireChanged();
return true;
}
@@ -189,18 +153,16 @@ export class BoardsDataStore implements FrontendApplicationContribution {
protected async setData({
fqbn,
data,
version,
}: {
fqbn: string;
data: BoardsDataStore.Data;
version: Installable.Version;
}): Promise<void> {
const key = this.getStorageKey(fqbn, version);
const key = this.getStorageKey(fqbn);
return this.storageService.setData(key, data);
}
protected getStorageKey(fqbn: string, version: Installable.Version): string {
return `.arduinoIDE-configOptions-${version}-${fqbn}`;
protected getStorageKey(fqbn: string): string {
return `.arduinoIDE-configOptions-${fqbn}`;
}
protected async getBoardDetailsSafe(
@@ -231,21 +193,6 @@ export class BoardsDataStore implements FrontendApplicationContribution {
protected fireChanged(): void {
this.onChangedEmitter.fire();
}
protected async getBoardsPackageVersion(
fqbn: string | undefined
): Promise<Installable.Version | undefined> {
if (!fqbn) {
return undefined;
}
const boardsPackage = await this.boardsService.getContainerBoardPackage({
fqbn,
});
if (!boardsPackage) {
return undefined;
}
return boardsPackage.installedVersion;
}
}
export namespace BoardsDataStore {

View File

@@ -185,8 +185,8 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
const selectedAvailableBoard = AvailableBoard.is(selectedBoard)
? selectedBoard
: this._availableBoards.find((availableBoard) =>
Board.sameAs(availableBoard, selectedBoard)
);
Board.sameAs(availableBoard, selectedBoard)
);
if (
selectedAvailableBoard &&
selectedAvailableBoard.selected &&
@@ -230,7 +230,8 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
)) {
if (
this.latestValidBoardsConfig.selectedBoard.fqbn === board.fqbn &&
this.latestValidBoardsConfig.selectedBoard.name === board.name
this.latestValidBoardsConfig.selectedBoard.name === board.name &&
this.latestValidBoardsConfig.selectedPort.protocol === board.port?.protocol
) {
this.boardsConfig = {
...this.latestValidBoardsConfig,
@@ -244,7 +245,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
}
set boardsConfig(config: BoardsConfig.Config) {
this.doSetBoardsConfig(config);
this.setBoardsConfig(config);
this.saveState().finally(() =>
this.reconcileAvailableBoards().finally(() =>
this.onBoardsConfigChangedEmitter.fire(this._boardsConfig)
@@ -256,7 +257,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
return this._boardsConfig;
}
protected doSetBoardsConfig(config: BoardsConfig.Config): void {
protected setBoardsConfig(config: BoardsConfig.Config): void {
this.logger.info('Board config changed: ', JSON.stringify(config));
this._boardsConfig = config;
this.latestBoardsConfig = this._boardsConfig;
@@ -370,19 +371,19 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
const find = (needle: Board & { port: Port }, haystack: AvailableBoard[]) =>
haystack.find(
(board) =>
Board.equals(needle, board) && Port.equals(needle.port, board.port)
Board.equals(needle, board) && Port.sameAs(needle.port, board.port)
);
const timeoutTask =
!!timeout && timeout > 0
? new Promise<void>((_, reject) =>
setTimeout(
() => reject(new Error(`Timeout after ${timeout} ms.`)),
timeout
)
setTimeout(
() => reject(new Error(`Timeout after ${timeout} ms.`)),
timeout
)
)
: new Promise<void>(() => {
/* never */
});
/* never */
});
const waitUntilTask = new Promise<void>((resolve) => {
let candidate = find(what, this.availableBoards);
if (candidate) {
@@ -409,7 +410,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
Port.sameAs(port, this.boardsConfig.selectedPort)
)
) {
this.doSetBoardsConfig({
this.setBoardsConfig({
selectedBoard: this.boardsConfig.selectedBoard,
selectedPort: undefined,
});
@@ -533,9 +534,8 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
protected getLastSelectedBoardOnPortKey(port: Port | string): string {
// TODO: we lose the port's `protocol` info (`serial`, `network`, etc.) here if the `port` is a `string`.
return `last-selected-board-on-port:${
typeof port === 'string' ? port : Port.toString(port)
}`;
return `last-selected-board-on-port:${typeof port === 'string' ? port : port.address
}`;
}
protected async loadState(): Promise<void> {

View File

@@ -0,0 +1,28 @@
import * as React from 'react';
export type ProgressBarProps = {
percent?: number;
showPercentage?: boolean;
};
export default function ProgressBar({
percent = 0,
showPercentage = false,
}: ProgressBarProps): React.ReactElement {
const roundedPercent = Math.round(percent);
return (
<div className="progress-bar">
<div className="progress-bar--outer">
<div
className="progress-bar--inner"
style={{ width: `${roundedPercent}%` }}
/>
</div>
{showPercentage && (
<div className="progress-bar--percentage">
<div className="progress-bar--percentage-text">{roundedPercent}%</div>
</div>
)}
</div>
);
}

View File

@@ -1,6 +1,6 @@
import { inject, injectable } from 'inversify';
import * as moment from 'moment';
import { remote } from 'electron';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import { isOSX, isWindows } from '@theia/core/lib/common/os';
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';

View File

@@ -1,5 +1,5 @@
import { inject, injectable } from 'inversify';
import { remote } from 'electron';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import { ArduinoMenus } from '../menu/arduino-menus';
import {
SketchContribution,

View File

@@ -1,5 +1,5 @@
import { inject, injectable } from 'inversify';
import { remote } from 'electron';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import URI from '@theia/core/lib/common/uri';
import { ConfirmDialog } from '@theia/core/lib/browser/dialogs';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';

View File

@@ -1,5 +1,5 @@
import { injectable } from 'inversify';
import { remote } from 'electron';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import * as dateFormat from 'dateformat';
import URI from '@theia/core/lib/common/uri';
import { ArduinoMenus } from '../menu/arduino-menus';

View File

@@ -1,5 +1,5 @@
import { inject, injectable } from 'inversify';
import { remote } from 'electron';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
import {
DisposableCollection,
@@ -204,10 +204,9 @@ PID: ${PID}`;
const packageLabel =
packageName +
`${
manuallyInstalled
? nls.localize('arduino/board/inSketchbook', ' (in Sketchbook)')
: ''
`${manuallyInstalled
? nls.localize('arduino/board/inSketchbook', ' (in Sketchbook)')
: ''
}`;
// Platform submenu
const platformMenuPath = [...boardsPackagesGroup, packageId];
@@ -255,8 +254,8 @@ PID: ${PID}`;
protocolOrder: number,
ports: AvailablePorts
) => {
const addresses = Object.keys(ports);
if (!addresses.length) {
const portIDs = Object.keys(ports);
if (!portIDs.length) {
return;
}
@@ -279,27 +278,26 @@ PID: ${PID}`;
// First we show addresses with recognized boards connected,
// then all the rest.
const sortedAddresses = Object.keys(ports);
sortedAddresses.sort((left: string, right: string): number => {
const sortedIDs = Object.keys(ports).sort((left: string, right: string): number => {
const [, leftBoards] = ports[left];
const [, rightBoards] = ports[right];
return rightBoards.length - leftBoards.length;
});
for (let i = 0; i < sortedAddresses.length; i++) {
const address = sortedAddresses[i];
const [port, boards] = ports[address];
let label = `${address}`;
for (let i = 0; i < sortedIDs.length; i++) {
const portID = sortedIDs[i];
const [port, boards] = ports[portID];
let label = `${port.address}`;
if (boards.length) {
const boardsList = boards.map((board) => board.name).join(', ');
label = `${label} (${boardsList})`;
}
const id = `arduino-select-port--${address}`;
const id = `arduino-select-port--${portID}`;
const command = { id };
const handler = {
execute: () => {
if (
!Port.equals(
!Port.sameAs(
port,
this.boardsServiceProvider.boardsConfig.selectedPort
)
@@ -312,7 +310,7 @@ PID: ${PID}`;
}
},
isToggled: () =>
Port.equals(
Port.sameAs(
port,
this.boardsServiceProvider.boardsConfig.selectedPort
),

View File

@@ -1,6 +1,6 @@
import { inject, injectable } from 'inversify';
import { toArray } from '@phosphor/algorithm';
import { remote } from 'electron';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';

View File

@@ -13,6 +13,7 @@ import {
KeybindingRegistry,
} from './contribution';
import { nls } from '@theia/core/lib/common';
import { IDEUpdaterCommands } from '../ide-updater/ide-updater-commands';
@injectable()
export class Help extends Contribution {
@@ -115,6 +116,10 @@ export class Help extends Contribution {
commandId: Help.Commands.VISIT_ARDUINO.id,
order: '6',
});
registry.registerMenuAction(ArduinoMenus.HELP__FIND_GROUP, {
commandId: IDEUpdaterCommands.CHECK_FOR_UPDATES.id,
order: '7',
});
}
registerKeybindings(registry: KeybindingRegistry): void {

View File

@@ -1,5 +1,5 @@
import { injectable } from 'inversify';
import { remote } from 'electron';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import URI from '@theia/core/lib/common/uri';
import { ArduinoMenus } from '../menu/arduino-menus';
import {

View File

@@ -1,5 +1,5 @@
import { inject, injectable } from 'inversify';
import { remote } from 'electron';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import { MaybePromise } from '@theia/core/lib/common/types';
import { Widget, ContextMenuRenderer } from '@theia/core/lib/browser';
import {
@@ -190,7 +190,7 @@ export class OpenSketch extends SketchContribution {
],
message: nls.localize(
'arduino/sketch/movingMsg',
'The file "{0}" needs to be inside a sketch folder named as "{1}".\nCreate this folder, move the file, and continue?',
'The file "{0}" needs to be inside a sketch folder named "{1}".\nCreate this folder, move the file, and continue?',
nameWithExt,
name
),

View File

@@ -1,5 +1,5 @@
import { injectable } from 'inversify';
import { remote } from 'electron';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import { isOSX } from '@theia/core/lib/common/os';
import {
Contribution,

View File

@@ -1,5 +1,5 @@
import { injectable } from 'inversify';
import { remote } from 'electron';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import * as dateFormat from 'dateformat';
import { ArduinoMenus } from '../menu/arduino-menus';
import {

View File

@@ -45,7 +45,7 @@ export namespace UploadFirmware {
id: 'arduino-upload-firmware-open',
label: nls.localize(
'arduino/firmware/updater',
'WiFi101 / WiFiNINA Firmware Updater'
'WiFi101 / WiFiNINA Firmware Updater'
),
category: 'Arduino',
};

View File

@@ -15,6 +15,47 @@ export namespace ResponseResultProvider {
export const JSON: ResponseResultProvider = (response) => response.json();
}
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()
@@ -275,9 +316,7 @@ export class CreateApi {
// parse the secret file
const secrets = (
typeof content === 'string'
? content
: new TextDecoder().decode(content)
typeof content === 'string' ? content : Utf8ArrayToStr(content)
)
.split(/\r?\n/)
.reduce((prev, curr) => {
@@ -341,7 +380,7 @@ export class CreateApi {
const headers = await this.headers();
let data: string =
typeof content === 'string' ? content : new TextDecoder().decode(content);
typeof content === 'string' ? content : Utf8ArrayToStr(content);
data = await this.toggleSecretsInclude(posixPath, data, 'remove');
const payload = { data: btoa(data) };

View File

@@ -201,7 +201,7 @@ export const FirmwareUploaderComponent = ({
<i className="fa fa-info status-icon" />
{nls.localize(
'arduino/firmware/successfullyInstalled',
'Firmware succesfully installed.'
'Firmware successfully installed.'
)}
</div>
)}

View File

@@ -0,0 +1,210 @@
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { nls } from '@theia/core/lib/common';
import { shell } from 'electron';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import ReactMarkdown from 'react-markdown';
import { ProgressInfo, UpdateInfo } from '../../../common/protocol/ide-updater';
import ProgressBar from '../../components/ProgressBar';
export type IDEUpdaterComponentProps = {
updateInfo: UpdateInfo;
windowService: WindowService;
downloadFinished?: boolean;
downloadStarted?: boolean;
progress?: ProgressInfo;
error?: Error;
onDownload: () => void;
onClose: () => void;
onSkipVersion: () => void;
onCloseAndInstall: () => void;
};
export const IDEUpdaterComponent = ({
updateInfo: { version, releaseNotes },
downloadStarted = false,
downloadFinished = false,
windowService,
progress,
error,
onDownload,
onClose,
onSkipVersion,
onCloseAndInstall,
}: IDEUpdaterComponentProps): React.ReactElement => {
const changelogDivRef = React.useRef() as React.MutableRefObject<
HTMLDivElement
>;
React.useEffect(() => {
if (!!releaseNotes) {
let changelog: string;
if (typeof releaseNotes === 'string') changelog = releaseNotes;
else
changelog = releaseNotes.reduce((acc, item) => {
return item.note ? (acc += `${item.note}\n\n`) : acc;
}, '');
ReactDOM.render(
<ReactMarkdown
components={{
a: ({ href, children, ...props }) => (
<a onClick={() => href && shell.openExternal(href)} {...props}>
{children}
</a>
),
}}
>
{changelog}
</ReactMarkdown>,
changelogDivRef.current
);
}
}, [releaseNotes]);
const closeButton = (
<button onClick={onClose} type="button" className="theia-button secondary">
{nls.localize('arduino/ide-updater/notNowButton', 'Not now')}
</button>
);
const DownloadCompleted: () => React.ReactElement = () => (
<div className="ide-updater-dialog--downloaded">
<div>
{nls.localize(
'arduino/ide-updater/versionDownloaded',
'Arduino IDE {0} has been downloaded.',
version
)}
</div>
<div>
{nls.localize(
'arduino/ide-updater/closeToInstallNotice',
'Close the software and install the update on your machine.'
)}
</div>
<div className="buttons-container">
{closeButton}
<button
onClick={onCloseAndInstall}
type="button"
className="theia-button close-and-install"
>
{nls.localize(
'arduino/ide-updater/closeAndInstallButton',
'Close and Install'
)}
</button>
</div>
</div>
);
const DownloadStarted: () => React.ReactElement = () => (
<div className="ide-updater-dialog--downloading">
<div>
{nls.localize(
'arduino/ide-updater/downloadingNotice',
'Downloading the latest version of the Arduino IDE.'
)}
</div>
<ProgressBar percent={progress?.percent} showPercentage />
</div>
);
const PreDownload: () => React.ReactElement = () => (
<div className="ide-updater-dialog--pre-download">
<div className="ide-updater-dialog--logo-container">
<div className="ide-updater-dialog--logo"></div>
</div>
<div className="ide-updater-dialog--new-version-text dialogSection">
<div className="dialogRow">
<div className="bold">
{nls.localize(
'arduino/ide-updater/updateAvailable',
'Update Available'
)}
</div>
</div>
<div className="dialogRow">
{nls.localize(
'arduino/ide-updater/newVersionAvailable',
'A new version of Arduino IDE ({0}) is available for download.',
version
)}
</div>
{releaseNotes && (
<div className="dialogRow">
<div className="changelog-container" ref={changelogDivRef} />
</div>
)}
<div className="buttons-container">
<button
onClick={onSkipVersion}
type="button"
className="theia-button secondary skip-version"
>
{nls.localize(
'arduino/ide-updater/skipVersionButton',
'Skip Version'
)}
</button>
<div className="push"></div>
{closeButton}
<button
onClick={onDownload}
type="button"
className="theia-button primary"
>
{nls.localize('arduino/ide-updater/downloadButton', 'Download')}
</button>
</div>
</div>
</div>
);
const onGoToDownloadClick = (
event: React.SyntheticEvent<HTMLAnchorElement, Event>
) => {
const { target } = event.nativeEvent;
if (target instanceof HTMLAnchorElement) {
event.nativeEvent.preventDefault();
windowService.openNewWindow(target.href, { external: true });
onClose();
}
};
const GoToDownloadPage: () => React.ReactElement = () => (
<div className="ide-updater-dialog--go-to-download-page">
<div>
{nls.localize(
'arduino/ide-updater/goToDownloadPage',
"An update for the Arduino IDE is available, but we're not able to download and install it automatically. Please go to the download page and download the latest version from there."
)}
</div>
<div className="buttons-container">
{closeButton}
<a
className="theia-button primary"
href="https://www.arduino.cc/en/software#experimental-software"
onClick={onGoToDownloadClick}
>
{nls.localize(
'arduino/ide-updater/goToDownloadButton',
'Go To Download'
)}
</a>
</div>
</div>
);
return (
<div className="ide-updater-dialog--content">
{!!error ? (
<GoToDownloadPage />
) : downloadFinished ? (
<DownloadCompleted />
) : downloadStarted ? (
<DownloadStarted />
) : (
<PreDownload />
)}
</div>
);
};

View File

@@ -0,0 +1,173 @@
import * as React from 'react';
import { inject, injectable } from 'inversify';
import { DialogProps } from '@theia/core/lib/browser/dialogs';
import { AbstractDialog } from '../../theia/dialogs/dialogs';
import { Widget } from '@phosphor/widgets';
import { Message } from '@phosphor/messaging';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import { nls } from '@theia/core';
import { IDEUpdaterComponent } from './ide-updater-component';
import {
IDEUpdater,
IDEUpdaterClient,
ProgressInfo,
UpdateInfo,
} from '../../../common/protocol/ide-updater';
import { LocalStorageService } from '@theia/core/lib/browser';
import { SKIP_IDE_VERSION } from '../../arduino-frontend-contribution';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
@injectable()
export class IDEUpdaterDialogWidget extends ReactWidget {
protected isOpen = new Object();
updateInfo: UpdateInfo;
progressInfo: ProgressInfo | undefined;
error: Error | undefined;
downloadFinished: boolean;
downloadStarted: boolean;
onClose: () => void;
@inject(IDEUpdater)
protected readonly updater: IDEUpdater;
@inject(IDEUpdaterClient)
protected readonly updaterClient: IDEUpdaterClient;
@inject(LocalStorageService)
protected readonly localStorageService: LocalStorageService;
@inject(WindowService)
protected windowService: WindowService;
init(updateInfo: UpdateInfo, onClose: () => void): void {
this.updateInfo = updateInfo;
this.progressInfo = undefined;
this.error = undefined;
this.downloadStarted = false;
this.downloadFinished = false;
this.onClose = onClose;
this.updaterClient.onError((e) => {
this.error = e;
this.update();
});
this.updaterClient.onDownloadProgressChanged((e) => {
this.progressInfo = e;
this.update();
});
this.updaterClient.onDownloadFinished((e) => {
this.downloadFinished = true;
this.update();
});
}
async onSkipVersion(): Promise<void> {
this.localStorageService.setData<string>(
SKIP_IDE_VERSION,
this.updateInfo.version
);
this.close();
}
close(): void {
super.close();
this.onClose();
}
onDispose(): void {
if (this.downloadStarted && !this.downloadFinished)
this.updater.stopDownload();
}
async onDownload(): Promise<void> {
this.progressInfo = undefined;
this.downloadStarted = true;
this.error = undefined;
this.updater.downloadUpdate();
this.update();
}
onCloseAndInstall(): void {
this.updater.quitAndInstall();
}
protected render(): React.ReactNode {
return !!this.updateInfo ? (
<form>
<IDEUpdaterComponent
updateInfo={this.updateInfo}
windowService={this.windowService}
downloadStarted={this.downloadStarted}
downloadFinished={this.downloadFinished}
progress={this.progressInfo}
error={this.error}
onClose={this.close.bind(this)}
onSkipVersion={this.onSkipVersion.bind(this)}
onDownload={this.onDownload.bind(this)}
onCloseAndInstall={this.onCloseAndInstall.bind(this)}
/>
</form>
) : null;
}
}
@injectable()
export class IDEUpdaterDialogProps extends DialogProps {}
@injectable()
export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
@inject(IDEUpdaterDialogWidget)
protected readonly widget: IDEUpdaterDialogWidget;
constructor(
@inject(IDEUpdaterDialogProps)
protected readonly props: IDEUpdaterDialogProps
) {
super({
title: nls.localize(
'arduino/ide-updater/ideUpdaterDialog',
'Software Update'
),
});
this.contentNode.classList.add('ide-updater-dialog');
this.acceptButton = undefined;
}
get value(): UpdateInfo {
return this.widget.updateInfo;
}
protected onAfterAttach(msg: Message): void {
if (this.widget.isAttached) {
Widget.detach(this.widget);
}
Widget.attach(this.widget, this.contentNode);
super.onAfterAttach(msg);
this.update();
}
async open(
data: UpdateInfo | undefined = undefined
): Promise<UpdateInfo | undefined> {
if (data && data.version) {
this.widget.init(data, this.close.bind(this));
return super.open();
}
}
protected onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg);
this.widget.update();
}
protected onActivateRequest(msg: Message): void {
super.onActivateRequest(msg);
this.widget.activate();
}
close(): void {
this.widget.dispose();
super.close();
}
}

View File

@@ -260,18 +260,6 @@ export class SettingsComponent extends React.Component<
'Verify code after upload'
)}
</label>
<label className="flex-line">
<input
type="checkbox"
checked={this.state.checkForUpdates}
onChange={this.checkForUpdatesDidChange}
disabled={true}
/>
{nls.localize(
'arduino/preferences/checkForUpdates',
'Check for updates on startup'
)}
</label>
<label className="flex-line">
<input
type="checkbox"
@@ -444,7 +432,9 @@ export class SettingsComponent extends React.Component<
);
}
protected noopKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
protected noopKeyDown = (
event: React.KeyboardEvent<HTMLInputElement>
): void => {
if (this.isControlKey(event)) {
return;
}
@@ -454,7 +444,7 @@ export class SettingsComponent extends React.Component<
protected numbersOnlyKeyDown = (
event: React.KeyboardEvent<HTMLInputElement>
) => {
): void => {
if (this.isControlKey(event)) {
return;
}
@@ -466,7 +456,7 @@ export class SettingsComponent extends React.Component<
}
};
protected browseSketchbookDidClick = async () => {
protected browseSketchbookDidClick = async (): Promise<void> => {
const uri = await this.props.fileDialogService.showOpenDialog({
title: nls.localize(
'arduino/preferences/newSketchbookLocation',
@@ -483,7 +473,7 @@ export class SettingsComponent extends React.Component<
}
};
protected editAdditionalUrlDidClick = async () => {
protected editAdditionalUrlDidClick = async (): Promise<void> => {
const additionalUrls = await new AdditionalUrlsDialog(
this.state.additionalUrls,
this.props.windowService
@@ -495,7 +485,7 @@ export class SettingsComponent extends React.Component<
protected editorFontSizeDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
const { value } = event.target;
if (value) {
this.setState({ editorFontSize: parseInt(value, 10) });
@@ -504,7 +494,7 @@ export class SettingsComponent extends React.Component<
protected additionalUrlsDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
this.setState({
additionalUrls: event.target.value.split(',').map((url) => url.trim()),
});
@@ -512,13 +502,13 @@ export class SettingsComponent extends React.Component<
protected autoScaleInterfaceDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
this.setState({ autoScaleInterface: event.target.checked });
};
protected interfaceScaleDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
const { value } = event.target;
const percentage = parseInt(value, 10);
if (isNaN(percentage)) {
@@ -532,31 +522,25 @@ export class SettingsComponent extends React.Component<
protected verifyAfterUploadDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
this.setState({ verifyAfterUpload: event.target.checked });
};
protected checkForUpdatesDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
this.setState({ checkForUpdates: event.target.checked });
};
protected sketchbookShowAllFilesDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
this.setState({ sketchbookShowAllFiles: event.target.checked });
};
protected autoSaveDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
this.setState({ autoSave: event.target.checked ? 'on' : 'off' });
};
protected quickSuggestionsOtherDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
// need to persist react events through lifecycle https://reactjs.org/docs/events.html#event-pooling
const newVal = event.target.checked ? true : false;
@@ -570,7 +554,9 @@ export class SettingsComponent extends React.Component<
});
};
protected themeDidChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
protected themeDidChange = (
event: React.ChangeEvent<HTMLSelectElement>
): void => {
const { selectedIndex } = event.target.options;
const theme = ThemeService.get().getThemes()[selectedIndex];
if (theme) {
@@ -580,14 +566,14 @@ export class SettingsComponent extends React.Component<
protected languageDidChange = (
event: React.ChangeEvent<HTMLSelectElement>
) => {
): void => {
const selectedLanguage = event.target.value;
this.setState({ currentLanguage: selectedLanguage });
};
protected compilerWarningsDidChange = (
event: React.ChangeEvent<HTMLSelectElement>
) => {
): void => {
const { selectedIndex } = event.target.options;
const compilerWarnings = CompilerWarningLiterals[selectedIndex];
if (compilerWarnings) {
@@ -597,26 +583,28 @@ export class SettingsComponent extends React.Component<
protected verboseOnCompileDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
this.setState({ verboseOnCompile: event.target.checked });
};
protected verboseOnUploadDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
this.setState({ verboseOnUpload: event.target.checked });
};
protected sketchpathDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
const sketchbookPath = event.target.value;
if (sketchbookPath) {
this.setState({ sketchbookPath });
}
};
protected noProxyDidChange = (event: React.ChangeEvent<HTMLInputElement>) => {
protected noProxyDidChange = (
event: React.ChangeEvent<HTMLInputElement>
): void => {
if (event.target.checked) {
this.setState({ network: 'none' });
} else {
@@ -626,7 +614,7 @@ export class SettingsComponent extends React.Component<
protected manualProxyDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
if (event.target.checked) {
this.setState({ network: Network.Default() });
} else {
@@ -636,7 +624,7 @@ export class SettingsComponent extends React.Component<
protected httpProtocolDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
if (this.state.network !== 'none') {
const network = this.cloneProxySettings;
network.protocol = event.target.checked ? 'http' : 'socks';
@@ -646,7 +634,7 @@ export class SettingsComponent extends React.Component<
protected socksProtocolDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
if (this.state.network !== 'none') {
const network = this.cloneProxySettings;
network.protocol = event.target.checked ? 'socks' : 'http';
@@ -656,7 +644,7 @@ export class SettingsComponent extends React.Component<
protected hostnameDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
if (this.state.network !== 'none') {
const network = this.cloneProxySettings;
network.hostname = event.target.value;
@@ -664,7 +652,9 @@ export class SettingsComponent extends React.Component<
}
};
protected portDidChange = (event: React.ChangeEvent<HTMLInputElement>) => {
protected portDidChange = (
event: React.ChangeEvent<HTMLInputElement>
): void => {
if (this.state.network !== 'none') {
const network = this.cloneProxySettings;
network.port = event.target.value;
@@ -674,7 +664,7 @@ export class SettingsComponent extends React.Component<
protected usernameDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
if (this.state.network !== 'none') {
const network = this.cloneProxySettings;
network.username = event.target.value;
@@ -684,7 +674,7 @@ export class SettingsComponent extends React.Component<
protected passwordDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
if (this.state.network !== 'none') {
const network = this.cloneProxySettings;
network.password = event.target.value;

View File

@@ -1,7 +1,7 @@
import { injectable, inject, postConstruct } from 'inversify';
import URI from '@theia/core/lib/common/uri';
import { Emitter } from '@theia/core/lib/common/event';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { Deferred, timeout } from '@theia/core/lib/common/promise-util';
import { deepClone } from '@theia/core/lib/common/objects';
import { FileService } from '@theia/filesystem/lib/browser/file-service';
import { ThemeService } from '@theia/core/lib/browser/theming';
@@ -18,24 +18,22 @@ import {
import { nls } from '@theia/core/lib/common';
import { AsyncLocalizationProvider } from '@theia/core/lib/common/i18n/localization';
const EDITOR_SETTING = 'editor';
const FONT_SIZE_SETTING = `${EDITOR_SETTING}.fontSize`;
const AUTO_SAVE_SETTING = `${EDITOR_SETTING}.autoSave`;
const QUICK_SUGGESTIONS_SETTING = `${EDITOR_SETTING}.quickSuggestions`;
const ARDUINO_SETTING = 'arduino';
const WINDOW_SETTING = `${ARDUINO_SETTING}.window`;
// const IDE_SETTING = `${ARDUINO_SETTING}.ide`;
const COMPILE_SETTING = `${ARDUINO_SETTING}.compile`;
const UPLOAD_SETTING = `${ARDUINO_SETTING}.upload`;
const SKETCHBOOK_SETTING = `${ARDUINO_SETTING}.sketchbook`;
const AUTO_SCALE_SETTING = `${WINDOW_SETTING}.autoScale`;
const ZOOM_LEVEL_SETTING = `${WINDOW_SETTING}.zoomLevel`;
// const AUTO_UPDATE_SETTING = `${IDE_SETTING}.autoUpdate`;
const COMPILE_VERBOSE_SETTING = `${COMPILE_SETTING}.verbose`;
const COMPILE_WARNINGS_SETTING = `${COMPILE_SETTING}.warnings`;
const UPLOAD_VERBOSE_SETTING = `${UPLOAD_SETTING}.verbose`;
const UPLOAD_VERIFY_SETTING = `${UPLOAD_SETTING}.verify`;
const SHOW_ALL_FILES_SETTING = `${SKETCHBOOK_SETTING}.showAllFiles`;
export const EDITOR_SETTING = 'editor';
export const FONT_SIZE_SETTING = `${EDITOR_SETTING}.fontSize`;
export const AUTO_SAVE_SETTING = `${EDITOR_SETTING}.autoSave`;
export const QUICK_SUGGESTIONS_SETTING = `${EDITOR_SETTING}.quickSuggestions`;
export const ARDUINO_SETTING = 'arduino';
export const WINDOW_SETTING = `${ARDUINO_SETTING}.window`;
export const COMPILE_SETTING = `${ARDUINO_SETTING}.compile`;
export const UPLOAD_SETTING = `${ARDUINO_SETTING}.upload`;
export const SKETCHBOOK_SETTING = `${ARDUINO_SETTING}.sketchbook`;
export const AUTO_SCALE_SETTING = `${WINDOW_SETTING}.autoScale`;
export const ZOOM_LEVEL_SETTING = `${WINDOW_SETTING}.zoomLevel`;
export const COMPILE_VERBOSE_SETTING = `${COMPILE_SETTING}.verbose`;
export const COMPILE_WARNINGS_SETTING = `${COMPILE_SETTING}.warnings`;
export const UPLOAD_VERBOSE_SETTING = `${UPLOAD_SETTING}.verbose`;
export const UPLOAD_VERIFY_SETTING = `${UPLOAD_SETTING}.verify`;
export const SHOW_ALL_FILES_SETTING = `${SKETCHBOOK_SETTING}.showAllFiles`;
export interface Settings extends Index {
editorFontSize: number; // `editor.fontSize`
@@ -48,7 +46,6 @@ export interface Settings extends Index {
autoScaleInterface: boolean; // `arduino.window.autoScale`
interfaceScale: number; // `arduino.window.zoomLevel` https://github.com/eclipse-theia/theia/issues/8751
checkForUpdates?: boolean; // `arduino.ide.autoUpdate`
verboseOnCompile: boolean; // `arduino.compile.verbose`
compilerWarnings: CompilerWarnings; // `arduino.compile.warnings`
verboseOnUpload: boolean; // `arduino.upload.verbose`
@@ -93,7 +90,6 @@ export class SettingsService {
@postConstruct()
protected async init(): Promise<void> {
await this.appStateService.reachedState('ready'); // Hack for https://github.com/eclipse-theia/theia/issues/8993
const settings = await this.loadSettings();
this._settings = deepClone(settings);
this.ready.resolve();
@@ -110,7 +106,6 @@ export class SettingsService {
quickSuggestions,
autoScaleInterface,
interfaceScale,
// checkForUpdates,
verboseOnCompile,
compilerWarnings,
verboseOnUpload,
@@ -135,7 +130,6 @@ export class SettingsService {
}),
this.preferenceService.get<boolean>(AUTO_SCALE_SETTING, true),
this.preferenceService.get<number>(ZOOM_LEVEL_SETTING, 0),
// this.preferenceService.get<string>(AUTO_UPDATE_SETTING, true),
this.preferenceService.get<boolean>(COMPILE_VERBOSE_SETTING, true),
this.preferenceService.get<any>(COMPILE_WARNINGS_SETTING, 'None'),
this.preferenceService.get<boolean>(UPLOAD_VERBOSE_SETTING, true),
@@ -154,7 +148,6 @@ export class SettingsService {
quickSuggestions,
autoScaleInterface,
interfaceScale,
// checkForUpdates,
verboseOnCompile,
compilerWarnings,
verboseOnUpload,
@@ -224,6 +217,11 @@ export class SettingsService {
}
}
private async savePreference(name: string, value: unknown): Promise<void> {
await this.preferenceService.set(name, value, PreferenceScope.User);
await timeout(5);
}
async save(): Promise<string | true> {
await this.ready.promise;
const {
@@ -234,7 +232,6 @@ export class SettingsService {
quickSuggestions,
autoScaleInterface,
interfaceScale,
// checkForUpdates,
verboseOnCompile,
compilerWarnings,
verboseOnUpload,
@@ -253,70 +250,29 @@ export class SettingsService {
(config as any).network = network;
(config as any).locale = currentLanguage;
await Promise.all([
this.preferenceService.set(
'editor.fontSize',
editorFontSize,
PreferenceScope.User
),
this.preferenceService.set(
'workbench.colorTheme',
themeId,
PreferenceScope.User
),
this.preferenceService.set(
'editor.autoSave',
autoSave,
PreferenceScope.User
),
this.preferenceService.set(
'editor.quickSuggestions',
quickSuggestions,
PreferenceScope.User
),
this.preferenceService.set(
AUTO_SCALE_SETTING,
autoScaleInterface,
PreferenceScope.User
),
this.preferenceService.set(
ZOOM_LEVEL_SETTING,
interfaceScale,
PreferenceScope.User
),
// this.preferenceService.set(AUTO_UPDATE_SETTING, checkForUpdates, PreferenceScope.User),
this.preferenceService.set(
COMPILE_VERBOSE_SETTING,
verboseOnCompile,
PreferenceScope.User
),
this.preferenceService.set(
COMPILE_WARNINGS_SETTING,
compilerWarnings,
PreferenceScope.User
),
this.preferenceService.set(
UPLOAD_VERBOSE_SETTING,
verboseOnUpload,
PreferenceScope.User
),
this.preferenceService.set(
UPLOAD_VERIFY_SETTING,
verifyAfterUpload,
PreferenceScope.User
),
this.preferenceService.set(
SHOW_ALL_FILES_SETTING,
sketchbookShowAllFiles,
PreferenceScope.User
),
this.configService.setConfiguration(config),
]);
await this.savePreference('editor.fontSize', editorFontSize);
await this.savePreference('workbench.colorTheme', themeId);
await this.savePreference('editor.autoSave', autoSave);
await this.savePreference('editor.quickSuggestions', quickSuggestions);
await this.savePreference(AUTO_SCALE_SETTING, autoScaleInterface);
await this.savePreference(ZOOM_LEVEL_SETTING, interfaceScale);
await this.savePreference(ZOOM_LEVEL_SETTING, interfaceScale);
await this.savePreference(COMPILE_VERBOSE_SETTING, verboseOnCompile);
await this.savePreference(COMPILE_WARNINGS_SETTING, compilerWarnings);
await this.savePreference(UPLOAD_VERBOSE_SETTING, verboseOnUpload);
await this.savePreference(UPLOAD_VERIFY_SETTING, verifyAfterUpload);
await this.savePreference(SHOW_ALL_FILES_SETTING, sketchbookShowAllFiles);
await this.configService.setConfiguration(config);
this.onDidChangeEmitter.fire(this._settings);
// after saving all the settings, if we need to change the language we need to perform a reload
if (currentLanguage !== nls.locale) {
window.localStorage.setItem(nls.localeId, currentLanguage);
// Only reload if the language differs from the current locale. `nls.locale === undefined` signals english as well
if (currentLanguage !== nls.locale && !(currentLanguage === 'en' && nls.locale === undefined)) {
if (currentLanguage === 'en') {
window.localStorage.removeItem(nls.localeId);
} else {
window.localStorage.setItem(nls.localeId, currentLanguage);
}
window.location.reload();
}

View File

@@ -0,0 +1,40 @@
import { Emitter } from '@theia/core';
import { injectable } from '@theia/core/shared/inversify';
import { UpdateInfo, ProgressInfo } from 'electron-updater';
import { IDEUpdaterClient } from '../../common/protocol/ide-updater';
@injectable()
export class IDEUpdaterClientImpl implements IDEUpdaterClient {
protected readonly onErrorEmitter = new Emitter<Error>();
protected readonly onCheckingForUpdateEmitter = new Emitter<void>();
protected readonly onUpdateAvailableEmitter = new Emitter<UpdateInfo>();
protected readonly onUpdateNotAvailableEmitter = new Emitter<UpdateInfo>();
protected readonly onDownloadProgressEmitter = new Emitter<ProgressInfo>();
protected readonly onDownloadFinishedEmitter = new Emitter<UpdateInfo>();
readonly onError = this.onErrorEmitter.event;
readonly onCheckingForUpdate = this.onCheckingForUpdateEmitter.event;
readonly onUpdateAvailable = this.onUpdateAvailableEmitter.event;
readonly onUpdateNotAvailable = this.onUpdateNotAvailableEmitter.event;
readonly onDownloadProgressChanged = this.onDownloadProgressEmitter.event;
readonly onDownloadFinished = this.onDownloadFinishedEmitter.event;
notifyError(message: Error): void {
this.onErrorEmitter.fire(message);
}
notifyCheckingForUpdate(message: void): void {
this.onCheckingForUpdateEmitter.fire(message);
}
notifyUpdateAvailable(message: UpdateInfo): void {
this.onUpdateAvailableEmitter.fire(message);
}
notifyUpdateNotAvailable(message: UpdateInfo): void {
this.onUpdateNotAvailableEmitter.fire(message);
}
notifyDownloadProgressChanged(message: ProgressInfo): void {
this.onDownloadProgressEmitter.fire(message);
}
notifyDownloadFinished(message: UpdateInfo): void {
this.onDownloadFinishedEmitter.fire(message);
}
}

View File

@@ -0,0 +1,60 @@
import {
Command,
CommandContribution,
CommandRegistry,
MessageService,
nls,
} from '@theia/core';
import { injectable, inject } from 'inversify';
import { IDEUpdater, UpdateInfo } from '../../common/protocol/ide-updater';
import { IDEUpdaterDialog } from '../dialogs/ide-updater/ide-updater-dialog';
@injectable()
export class IDEUpdaterCommands implements CommandContribution {
constructor(
@inject(IDEUpdater)
private readonly updater: IDEUpdater,
@inject(MessageService)
protected readonly messageService: MessageService,
@inject(IDEUpdaterDialog)
protected readonly updaterDialog: IDEUpdaterDialog
) {}
registerCommands(registry: CommandRegistry): void {
registry.registerCommand(IDEUpdaterCommands.CHECK_FOR_UPDATES, {
execute: this.checkForUpdates.bind(this),
});
}
async checkForUpdates(initialCheck?: boolean): Promise<UpdateInfo | void> {
try {
const updateInfo = await this.updater.checkForUpdates(initialCheck);
if (!!updateInfo) {
this.updaterDialog.open(updateInfo);
} else {
this.messageService.info(
nls.localize(
'arduino/ide-updater/noUpdatesAvailable',
'There are no recent updates available for the Arduino IDE'
)
);
}
return updateInfo;
} catch (e) {
this.messageService.error(
nls.localize(
'arduino/ide-updater/errorCheckingForUpdates',
'Error while checking for Arduino IDE updates.\n{0}',
e.message
)
);
}
}
}
export namespace IDEUpdaterCommands {
export const CHECK_FOR_UPDATES: Command = {
id: 'arduino-ide-check-for-updates',
category: 'Arduino',
label: 'Check for Arduino IDE updates',
};
}

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import { Key, KeyCode } from '@theia/core/lib/browser/keys';
import { Board, Port } from '../../../common/protocol/boards-service';
import { Board } from '../../../common/protocol/boards-service';
import { isOSX } from '@theia/core/lib/common/os';
import { DisposableCollection, nls } from '@theia/core/lib/common';
import { SerialConnectionManager } from '../serial-connection-manager';
@@ -80,14 +80,14 @@ export class SerialMonitorSendInput extends React.Component<
const { board, port } = serialConfig;
return nls.localize(
'arduino/serial/message',
"Message ({0} + Enter to send message to '{1}' on '{2}'",
"Message ({0} + Enter to send message to '{1}' on '{2}')",
isOSX ? '⌘' : nls.localize('vscode/keybindingLabels/ctrlKey', 'Ctrl'),
board
? Board.toString(board, {
useFqbn: false,
})
: 'unknown',
port ? Port.toString(port) : 'unknown'
port ? port.address : 'unknown'
);
}

View File

@@ -43,6 +43,7 @@ export class SerialMonitorOutput extends React.Component<
itemCount={this.state.lines.length}
itemSize={18}
width={'100%'}
style={{ whiteSpace: 'nowrap' }}
ref={this.listRef}
>
{Row}

View File

@@ -10,7 +10,7 @@ import { SerialModel } from '../serial-model';
import { ArduinoMenus } from '../../menu/arduino-menus';
import { Contribution } from '../../contributions/contribution';
import { Endpoint, FrontendApplication } from '@theia/core/lib/browser';
import { ipcRenderer } from '@theia/core/shared/electron';
import { ipcRenderer } from '@theia/electron/shared/electron';
import { SerialConfig } from '../../../common/protocol';
import { SerialConnectionManager } from '../serial-connection-manager';
import { SerialPlotter } from './protocol';

View File

@@ -10,7 +10,6 @@ import {
} from '../../common/protocol/serial-service';
import { BoardsServiceProvider } from '../boards/boards-service-provider';
import {
Port,
Board,
BoardsService,
} from '../../common/protocol/boards-service';
@@ -217,7 +216,7 @@ export class SerialConnectionManager {
nls.localize(
'arduino/serial/connectionBusy',
'Connection failed. Serial port is busy: {0}',
Port.toString(port)
port.address
),
options
);
@@ -232,7 +231,7 @@ export class SerialConnectionManager {
Board.toString(board, {
useFqbn: false,
}),
Port.toString(port)
port.address
),
options
);
@@ -244,7 +243,7 @@ export class SerialConnectionManager {
'arduino/serial/unexpectedError',
'Unexpected error. Reconnecting {0} on port {1}.',
Board.toString(board),
Port.toString(port)
port.address
),
options
);
@@ -262,7 +261,7 @@ export class SerialConnectionManager {
Board.toString(board, {
useFqbn: false,
}),
Port.toString(port)
port.address
)
);
this.serialErrors.length = 0;
@@ -280,7 +279,7 @@ export class SerialConnectionManager {
Board.toString(board, {
useFqbn: false,
}),
Port.toString(port),
port.address,
attempts.toString()
)
);
@@ -351,7 +350,7 @@ export namespace Serial {
export function toString(config: Partial<SerialConfig>): string {
if (!isSerialConfig(config)) return '';
const { board, port } = config;
return `${Board.toString(board)} ${Port.toString(port)}`;
return `${Board.toString(board)} ${port.address}`;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,78 @@
.ide-updater-dialog {
width: 546px;
}
.ide-updater-dialog .bold {
font-weight: bold;
}
.ide-updater-dialog--pre-download {
display: flex;
}
.ide-updater-dialog--logo-container {
margin-right: 28px;
}
.ide-updater-dialog--logo {
background: url('./ide-logo.png') round;
width: 52px;
height: 52px;
}
.dialogContent.ide-updater-dialog
.ide-updater-dialog--content
.ide-updater-dialog--new-version-text.dialogSection {
margin-top: 0;
}
.ide-updater-dialog .changelog-container {
background: white;
border: 1px solid #dae3e3;
border-radius: 2px;
font-size: 12px;
height: 180px;
overflow: auto;
padding: 0 12px;
cursor: text;
}
.ide-updater-dialog .changelog-container a {
color: #018184;
}
.ide-updater-dialog .changelog-container a:hover {
text-decoration: underline;
cursor: pointer;
}
.ide-updater-dialog .changelog-container code {
background: #ecf1f1;
border-radius: 2px;
padding: 0 2px;
}
.ide-updater-dialog .changelog-container a code {
color: #018184;
}
.ide-updater-dialog .buttons-container {
display: flex;
justify-content: flex-end;
margin-top: 28px;
}
.ide-updater-dialog .buttons-container a.theia-button {
text-decoration: none;
display: flex;
align-items: center;
justify-content: center;
}
.ide-updater-dialog .buttons-container a.theia-button:hover {
color: var(--theia-button-foreground);
}
.ide-updater-dialog .buttons-container .push {
margin-right: auto;
}

View File

@@ -9,6 +9,7 @@
@import './editor.css';
@import './settings-dialog.css';
@import './firmware-uploader-dialog.css';
@import './ide-updater-dialog.css';
@import './certificate-uploader-dialog.css';
@import './user-fields-dialog.css';
@import './debug.css';
@@ -16,6 +17,7 @@
@import './cloud-sketchbook.css';
@import './fonts.css';
@import './custom-codicon.css';
@import './progress-bar.css';
.theia-input.warning:focus {
outline-width: 1px;

View File

@@ -0,0 +1,32 @@
.progress-bar {
margin-top: 20px;
}
.progress-bar--outer {
background: #e5e5e5;
border-radius: 11px;
height: 6px;
position: relative;
overflow: hidden;
}
.progress-bar--inner {
transition: width 1s;
height: 100%;
background: #008184;
border-radius: 11px;
}
.progress-bar--percentage {
align-items: flex-end;
display: flex;
height: 40px;
justify-content: center;
margin-top: 10px;
width: 100%;
}
.progress-bar--percentage-text {
font-size: 14px;
line-height: 24px;
}

View File

@@ -62,9 +62,15 @@ export class DebugSessionManager extends TheiaDebugSessionManager {
}
);
}
// TODO: remove as https://github.com/eclipse-theia/theia/issues/10164 is fixed
async terminateSessions(): Promise<void> {
await super.terminateSessions();
this.destroy(this.currentSession?.id);
async terminateSession(session?: DebugSession): Promise<void> {
if (!session) {
this.updateCurrentSession(this._currentSession);
session = this._currentSession;
}
// The cortex-debug extension does not respond to close requests
// So we simply terminate the debug session immediately
// Alternatively the `super.terminateSession` call will terminate it after 5 seconds without a response
await this.debug.terminateDebugSession(session!.id);
await super.terminateSession(session);
}
}

View File

@@ -1,5 +1,5 @@
import { inject, injectable } from 'inversify';
import { remote } from 'electron';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import URI from '@theia/core/lib/common/uri';
import { WorkspaceDeleteHandler as TheiaWorkspaceDeleteHandler } from '@theia/workspace/lib/browser/workspace-delete-handler';
import { SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';

View File

@@ -1,3 +1,4 @@
import * as remote from '@theia/core/electron-shared/@electron/remote';
import { injectable, inject } from 'inversify';
import URI from '@theia/core/lib/common/uri';
import { EditorWidget } from '@theia/editor/lib/browser';
@@ -18,6 +19,7 @@ import { ArduinoWorkspaceRootResolver } from '../../arduino-workspace-resolver';
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
import { BoardsConfig } from '../../boards/boards-config';
import { nls } from '@theia/core/lib/common';
import { URI as VSCodeUri } from '@theia/core/shared/vscode-uri';
@injectable()
export class WorkspaceService extends TheiaWorkspaceService {
@@ -67,7 +69,7 @@ export class WorkspaceService extends TheiaWorkspaceService {
this.workspaceUri = (async () => {
try {
const hash = window.location.hash;
const [recentWorkspaces, recentSketches] = await Promise.all([
const [recentWorkspacesPaths, recentSketches] = await Promise.all([
this.server.getRecentWorkspaces(),
this.sketchService
.getSketches({})
@@ -75,6 +77,10 @@ export class WorkspaceService extends TheiaWorkspaceService {
SketchContainer.toArray(container).map((s) => s.uri)
),
]);
// On Dindows, `getRecentWorkspaces` returns only file paths, not URIs as expected by the `isValid` method.
const recentWorkspaces = recentWorkspacesPaths.map((e) =>
VSCodeUri.file(e).toString()
);
const toOpen = await new ArduinoWorkspaceRootResolver({
isValid: this.isValid.bind(this),
}).resolve({ hash, recentWorkspaces, recentSketches });
@@ -124,6 +130,8 @@ export class WorkspaceService extends TheiaWorkspaceService {
}: FocusTracker.IChangedArgs<Widget>): void {
if (newValue instanceof EditorWidget) {
const { uri } = newValue.editor;
const currentWindow = remote.getCurrentWindow();
currentWindow.setRepresentedFilename(uri.path.toString());
if (Sketch.isSketchFile(uri.toString())) {
this.updateTitle();
} else {

View File

@@ -1,4 +1,4 @@
import { remote } from 'electron';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import { inject, injectable } from 'inversify';
import { CommandRegistry } from '@theia/core/lib/common/command';
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
@@ -80,7 +80,7 @@ export class SketchbookWidgetContribution
}
onStart(): void {
this.shell.currentChanged.connect(() =>
this.shell.onDidChangeCurrentWidget(() =>
this.onCurrentWidgetChangedHandler()
);

View File

@@ -7,13 +7,13 @@ export type AvailablePorts = Record<string, [Port, Array<Board>]>;
export namespace AvailablePorts {
export function byProtocol(availablePorts: AvailablePorts): Map<string, AvailablePorts> {
const grouped = new Map<string, AvailablePorts>();
for (const address of Object.keys(availablePorts)) {
const [port, boards] = availablePorts[address];
for (const portID of Object.keys(availablePorts)) {
const [port, boards] = availablePorts[portID];
let ports = grouped.get(port.protocol);
if (!ports) {
ports = {} as AvailablePorts;
}
ports[address] = [port, boards];
ports[portID] = [port, boards];
grouped.set(port.protocol, ports);
}
return grouped;
@@ -43,7 +43,7 @@ export namespace AttachedBoardsChangeEvent {
const visitedDetachedPorts: Port[] = [];
for (const board of attached.boards) {
const port = board.port
? ` on ${Port.toString(board.port, { useLabel: true })}`
? ` on ${Port.toString(board.port)}`
: '';
rows.push(` - Attached board: ${Board.toString(board)}${port}`);
if (board.port) {
@@ -52,7 +52,7 @@ export namespace AttachedBoardsChangeEvent {
}
for (const board of detached.boards) {
const port = board.port
? ` from ${Port.toString(board.port, { useLabel: true })}`
? ` from ${Port.toString(board.port)}`
: '';
rows.push(` - Detached board: ${Board.toString(board)}${port}`);
if (board.port) {
@@ -62,18 +62,14 @@ export namespace AttachedBoardsChangeEvent {
for (const port of attached.ports) {
if (!visitedAttachedPorts.find((p) => Port.sameAs(port, p))) {
rows.push(
` - New port is available on ${Port.toString(port, {
useLabel: true,
})}`
` - New port is available on ${Port.toString(port)}`
);
}
}
for (const port of detached.ports) {
if (!visitedDetachedPorts.find((p) => Port.sameAs(port, p))) {
rows.push(
` - Port is no longer available on ${Port.toString(port, {
useLabel: true,
})}`
` - Port is no longer available on ${Port.toString(port)}`
);
}
}
@@ -147,12 +143,14 @@ export interface BoardsService
}
export interface Port {
// id is the combination of address and protocol
// formatted like "<address>|<protocol>" used
// to univocally recognize a port
readonly id: string;
readonly address: string;
readonly addressLabel: string;
readonly protocol: string;
/**
* Optional label for the protocol. For example: `Serial Port (USB)`.
*/
readonly label?: string;
readonly protocolLabel: string;
}
export namespace Port {
export function is(arg: any): arg is Port {
@@ -165,14 +163,8 @@ export namespace Port {
);
}
export function toString(
port: Port,
options: { useLabel: boolean } = { useLabel: false }
): string {
if (options.useLabel && port.label) {
return `${port.address} ${port.label}`;
}
return port.address;
export function toString(port: Port): string {
return `${port.addressLabel} ${port.protocolLabel}`;
}
export function compare(left: Port, right: Port): number {
@@ -192,29 +184,12 @@ export namespace Port {
return naturalCompare(left.address!, right.address!);
}
export function equals(
export function sameAs(
left: Port | undefined,
right: Port | undefined
): boolean {
if (left && right) {
return (
left.address === right.address &&
left.protocol === right.protocol &&
(left.label || '') === (right.label || '')
);
}
return left === right;
}
export function sameAs(
left: Port | undefined,
right: Port | string | undefined
) {
if (left && right) {
if (typeof right === 'string') {
return left.address === right;
}
return left.address === right.address;
return left.address === right.address && left.protocol === right.protocol;
}
return false;
}

View File

@@ -0,0 +1,71 @@
import { JsonRpcServer } from '@theia/core/lib/common/messaging/proxy-factory';
import { Event } from '@theia/core/lib/common/event';
import { UpdateChannel } from '../../browser/arduino-preferences';
export interface ProgressInfo {
total: number;
delta: number;
transferred: number;
percent: number;
bytesPerSecond: number;
}
export interface ReleaseNoteInfo {
readonly version: string;
readonly note: string | null;
}
export interface BlockMapDataHolder {
size?: number;
blockMapSize?: number;
readonly sha512: string;
readonly isAdminRightsRequired?: boolean;
}
export interface UpdateFileInfo extends BlockMapDataHolder {
url: string;
}
export type UpdateInfo = {
readonly version: string;
readonly files: Array<UpdateFileInfo>;
releaseName?: string | null;
releaseNotes?: string | Array<ReleaseNoteInfo> | null;
releaseDate: string;
readonly stagingPercentage?: number;
};
export interface ProgressInfo {
total: number;
delta: number;
transferred: number;
percent: number;
bytesPerSecond: number;
}
export const IDEUpdaterPath = '/services/ide-updater';
export const IDEUpdater = Symbol('IDEUpdater');
export interface IDEUpdater extends JsonRpcServer<IDEUpdaterClient> {
init(channel: UpdateChannel, baseUrl: string): Promise<void>;
checkForUpdates(initialCheck?: boolean): Promise<UpdateInfo | void>;
downloadUpdate(): Promise<void>;
quitAndInstall(): void;
stopDownload(): void;
disconnectClient(client: IDEUpdaterClient): void;
}
export const IDEUpdaterClient = Symbol('IDEUpdaterClient');
export interface IDEUpdaterClient {
onError: Event<Error>;
onCheckingForUpdate: Event<void>;
onUpdateAvailable: Event<UpdateInfo>;
onUpdateNotAvailable: Event<UpdateInfo>;
onDownloadProgressChanged: Event<ProgressInfo>;
onDownloadFinished: Event<UpdateInfo>;
notifyError(message: Error): void;
notifyCheckingForUpdate(message: void): void;
notifyUpdateAvailable(message: UpdateInfo): void;
notifyUpdateNotAvailable(message: UpdateInfo): void;
notifyDownloadProgressChanged(message: ProgressInfo): void;
notifyDownloadFinished(message: UpdateInfo): void;
}

View File

@@ -1,5 +1,5 @@
import { inject, injectable, postConstruct } from 'inversify';
import { remote } from 'electron';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import {
ConnectionStatus,

View File

@@ -1,5 +1,5 @@
import { injectable } from 'inversify';
import { remote } from 'electron';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import { isOSX } from '@theia/core/lib/common/os';
import { Keybinding } from '@theia/core/lib/common/keybinding';
import {
@@ -15,7 +15,6 @@ import {
ArduinoMenus,
PlaceholderMenuNode,
} from '../../../browser/menu/arduino-menus';
import electron = require('@theia/core/shared/electron');
@injectable()
export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
@@ -35,9 +34,9 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
await this.preferencesService.ready;
const createdMenuBar = this.createElectronMenuBar();
if (isOSX) {
electron.remote.Menu.setApplicationMenu(createdMenuBar);
remote.Menu.setApplicationMenu(createdMenuBar);
} else {
electron.remote.getCurrentWindow().setMenu(createdMenuBar);
remote.getCurrentWindow().setMenu(createdMenuBar);
}
}
@@ -81,7 +80,7 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
protected createOSXMenu(): Electron.MenuItemConstructorOptions {
const { submenu } = super.createOSXMenu();
const label = 'Arduino IDE';
if (!!submenu && !(submenu instanceof remote.Menu)) {
if (!!submenu && Array.isArray(submenu)) {
const [, , /* about */ /* preferences */ ...rest] = submenu;
const about = this.fillMenuTemplate(
[],

View File

@@ -2,7 +2,10 @@ import { ContainerModule } from 'inversify';
import { JsonRpcConnectionHandler } from '@theia/core/lib/common/messaging/proxy-factory';
import { ElectronConnectionHandler } from '@theia/core/lib/electron-common/messaging/electron-connection-handler';
import { ElectronMainWindowService } from '@theia/core/lib/electron-common/electron-main-window-service';
import { ElectronMainApplication as TheiaElectronMainApplication } from '@theia/core/lib/electron-main/electron-main-application';
import {
ElectronMainApplication as TheiaElectronMainApplication,
ElectronMainApplicationContribution,
} from '@theia/core/lib/electron-main/electron-main-application';
import {
SplashService,
splashServicePath,
@@ -10,6 +13,12 @@ import {
import { SplashServiceImpl } from './splash/splash-service-impl';
import { ElectronMainApplication } from './theia/electron-main-application';
import { ElectronMainWindowServiceImpl } from './theia/electron-main-window-service';
import {
IDEUpdater,
IDEUpdaterClient,
IDEUpdaterPath,
} from '../common/protocol/ide-updater';
import { IDEUpdaterImpl } from './ide-updater/ide-updater-impl';
export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(ElectronMainApplication).toSelf().inSingletonScope();
@@ -28,4 +37,23 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
)
)
.inSingletonScope();
// IDE updater bindings
bind(IDEUpdaterImpl).toSelf().inSingletonScope();
bind(IDEUpdater).toService(IDEUpdaterImpl);
bind(ElectronMainApplicationContribution).toService(IDEUpdater);
bind(ElectronConnectionHandler)
.toDynamicValue(
(context) =>
new JsonRpcConnectionHandler<IDEUpdaterClient>(
IDEUpdaterPath,
(client) => {
const server = context.container.get<IDEUpdater>(IDEUpdater);
server.setClient(client);
client.onDidCloseConnection(() => server.disconnectClient(client));
return server;
}
)
)
.inSingletonScope();
});

View File

@@ -0,0 +1,131 @@
import { injectable } from '@theia/core/shared/inversify';
import { UpdateInfo, CancellationToken, autoUpdater } from 'electron-updater';
import fetch, { Response } from 'node-fetch';
import { UpdateChannel } from '../../browser/arduino-preferences';
import {
IDEUpdater,
IDEUpdaterClient,
} from '../../common/protocol/ide-updater';
const CHANGELOG_BASE_URL = 'https://downloads.arduino.cc/arduino-ide/changelog';
@injectable()
export class IDEUpdaterImpl implements IDEUpdater {
private isAlreadyChecked = false;
private updater = autoUpdater;
private cancellationToken?: CancellationToken;
protected theiaFEClient?: IDEUpdaterClient;
protected clients: Array<IDEUpdaterClient> = [];
constructor() {
this.updater.on('checking-for-update', (e) => {
this.clients.forEach((c) => c.notifyCheckingForUpdate(e));
});
this.updater.on('update-available', (e) => {
this.clients.forEach((c) => c.notifyUpdateAvailable(e));
});
this.updater.on('update-not-available', (e) => {
this.clients.forEach((c) => c.notifyUpdateNotAvailable(e));
});
this.updater.on('download-progress', (e) => {
this.clients.forEach((c) => c.notifyDownloadProgressChanged(e));
});
this.updater.on('update-downloaded', (e) => {
this.clients.forEach((c) => c.notifyDownloadFinished(e));
});
this.updater.on('error', (e) => {
this.clients.forEach((c) => c.notifyError(e));
});
}
async init(channel: UpdateChannel, baseUrl: string): Promise<void> {
this.updater.autoDownload = false;
this.updater.channel = channel;
this.updater.setFeedURL({
provider: 'generic',
url: `${baseUrl}/${channel === UpdateChannel.Nightly ? 'nightly' : ''}`,
channel,
});
}
setClient(client: IDEUpdaterClient | undefined): void {
if (client) this.clients.push(client);
}
async checkForUpdates(initialCheck?: boolean): Promise<UpdateInfo | void> {
if (initialCheck) {
if (this.isAlreadyChecked) return Promise.resolve();
this.isAlreadyChecked = true;
}
const {
updateInfo,
cancellationToken,
} = await this.updater.checkForUpdates();
this.cancellationToken = cancellationToken;
if (this.updater.currentVersion.compare(updateInfo.version) === -1) {
/*
'latest.txt' points to the latest changelog that has been generated by the CI,
so we need to make a first GET request to get the filename of the changelog
and a second GET to the actual changelog file
*/
try {
let response: Response | null = await fetch(
`${CHANGELOG_BASE_URL}/latest.txt`
);
const latestChangelogFileName = response.ok
? await response.text()
: null;
response = latestChangelogFileName
? await fetch(`${CHANGELOG_BASE_URL}/${latestChangelogFileName}`)
: null;
const changelog = response?.ok ? await response?.text() : null;
// We only want to see the release notes of newer versions
const currentVersionIndex = changelog?.indexOf(
`\r\n\r\n---\r\n\r\n## ${this.updater.currentVersion}\r\n\r\n`
);
const newChangelog =
currentVersionIndex && currentVersionIndex > 0
? changelog?.slice(0, currentVersionIndex)
: changelog;
updateInfo.releaseNotes = newChangelog;
} catch {
/*
if the request for the changelog fails, we'll just avoid to show it
to the user, but we will still show the update info
*/
}
return updateInfo;
}
}
async downloadUpdate(): Promise<void> {
try {
await this.updater.downloadUpdate(this.cancellationToken);
} catch (e) {
if (e.message === 'cancelled') return;
this.clients.forEach((c) => c.notifyError(e));
}
}
stopDownload(): void {
this.cancellationToken?.cancel();
}
quitAndInstall(): void {
this.updater.quitAndInstall();
}
disconnectClient(client: IDEUpdaterClient): void {
const index = this.clients.indexOf(client);
if (index !== -1) {
this.clients.splice(index, 1);
}
}
dispose(): void {
this.clients.forEach(this.disconnectClient.bind(this));
}
}

View File

@@ -1,29 +1,44 @@
import { inject, injectable } from 'inversify';
import {
app,
BrowserWindow,
BrowserWindowConstructorOptions,
screen,
} from 'electron';
import { app, BrowserWindow, BrowserWindowConstructorOptions, ipcMain, screen, Event as ElectronEvent } from '@theia/core/electron-shared/electron';
import { fork } from 'child_process';
import { AddressInfo } from 'net';
import { join } from 'path';
import { join, dirname } from 'path';
import * as fs from 'fs-extra';
import { initSplashScreen } from '../splash/splash-screen';
import { MaybePromise } from '@theia/core/lib/common/types';
import { ElectronSecurityToken } from '@theia/core/lib/electron-common/electron-token';
import { FrontendApplicationConfig } from '@theia/application-package/lib/application-props';
import {
ElectronMainApplication as TheiaElectronMainApplication,
ElectronMainExecutionParams,
TheiaBrowserWindowOptions,
} from '@theia/core/lib/electron-main/electron-main-application';
import { SplashServiceImpl } from '../splash/splash-service-impl';
import { ipcMain } from '@theia/core/shared/electron';
import { URI } from '@theia/core/shared/vscode-uri';
import * as electronRemoteMain from '@theia/core/electron-shared/@electron/remote/main';
import { Deferred } from '@theia/core/lib/common/promise-util';
import * as os from '@theia/core/lib/common/os';
app.commandLine.appendSwitch('disable-http-cache');
interface WorkspaceOptions {
file: string
x: number
y: number
width: number
height: number
isMaximized: boolean
isFullScreen: boolean
time: number
}
const WORKSPACES = 'workspaces';
@injectable()
export class ElectronMainApplication extends TheiaElectronMainApplication {
protected _windows: BrowserWindow[] = [];
protected startup = false;
protected openFilePromise = new Deferred();
@inject(SplashServiceImpl)
protected readonly splashService: SplashServiceImpl;
@@ -33,9 +48,106 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
// See: https://github.com/electron-userland/electron-builder/issues/2468
// Regression in Theia: https://github.com/eclipse-theia/theia/issues/8701
app.on('ready', () => app.setName(config.applicationName));
this.attachFileAssociations();
return super.start(config);
}
attachFileAssociations() {
// OSX: register open-file event
if (os.isOSX) {
app.on('open-file', async (event, uri) => {
event.preventDefault();
if (uri.endsWith('.ino') && await fs.pathExists(uri)) {
this.openFilePromise.reject();
await this.openSketch(dirname(uri));
}
});
setTimeout(() => this.openFilePromise.resolve(), 500);
} else {
this.openFilePromise.resolve();
}
}
protected async isValidSketchPath(uri: string): Promise<boolean | undefined> {
return typeof uri === 'string' && await fs.pathExists(uri);
}
protected async launch(params: ElectronMainExecutionParams): Promise<void> {
try {
// When running on MacOS, we either have to wait until
// 1. The `open-file` command has been received by the app, rejecting the promise
// 2. A short timeout resolves the promise automatically, falling back to the usual app launch
await this.openFilePromise.promise;
} catch {
// Application has received the `open-file` event and will skip the default application launch
return;
}
if (!os.isOSX && await this.launchFromArgs(params)) {
// Application has received a file in its arguments and will skip the default application launch
return;
}
this.startup = true;
const workspaces: WorkspaceOptions[] | undefined = this.electronStore.get(WORKSPACES);
let useDefault = true;
if (workspaces && workspaces.length > 0) {
for (const workspace of workspaces) {
if (await this.isValidSketchPath(workspace.file)) {
useDefault = false;
await this.openSketch(workspace);
}
}
}
this.startup = false;
if (useDefault) {
super.launch(params);
}
}
protected async launchFromArgs(params: ElectronMainExecutionParams): Promise<boolean> {
// Copy to prevent manipulation of original array
const argCopy = [...params.argv];
let uri: string | undefined;
for (const possibleUri of argCopy) {
if (possibleUri.endsWith('.ino') && await this.isValidSketchPath(possibleUri)) {
uri = possibleUri;
break;
}
}
if (uri) {
await this.openSketch(dirname(uri));
return true;
}
return false;
}
protected async openSketch(workspace: WorkspaceOptions | string): Promise<BrowserWindow> {
const options = await this.getLastWindowOptions();
let file: string;
if (typeof workspace === 'object') {
options.x = workspace.x;
options.y = workspace.y;
options.width = workspace.width;
options.height = workspace.height;
options.isMaximized = workspace.isMaximized;
options.isFullScreen = workspace.isFullScreen;
file = workspace.file;
} else {
file = workspace;
}
const [uri, electronWindow] = await Promise.all([this.createWindowUri(), this.createWindow(options)]);
electronWindow.loadURL(uri.withFragment(encodeURI(file)).toString(true));
return electronWindow;
}
protected avoidOverlap(options: TheiaBrowserWindowOptions): TheiaBrowserWindowOptions {
if (this.startup) {
return options;
}
return super.avoidOverlap(options);
}
protected getTitleBarStyle(): 'native' | 'custom' {
return 'native';
}
@@ -50,6 +162,14 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
});
}
protected async onSecondInstance(event: ElectronEvent, argv: string[], cwd: string): Promise<void> {
if (!os.isOSX && await this.launchFromArgs({ cwd, argv, secondInstance: true })) {
// Application has received a file in its arguments
return;
}
super.onSecondInstance(event, argv, cwd);
}
/**
* Use this rather than creating `BrowserWindow` instances from scratch, since some security parameters need to be set, this method will do it.
*
@@ -148,10 +268,12 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
}
}
});
this.attachClosedWorkspace(electronWindow);
this.attachReadyToShow(electronWindow);
this.attachSaveWindowState(electronWindow);
this.attachGlobalShortcuts(electronWindow);
this.restoreMaximizedState(electronWindow, options);
electronRemoteMain.enable(electronWindow.webContents);
return electronWindow;
}
@@ -218,6 +340,44 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
}
}
protected closedWorkspaces: WorkspaceOptions[] = [];
protected attachClosedWorkspace(window: BrowserWindow): void {
// Since the `before-quit` event is only fired when closing the *last* window
// We need to keep track of recently closed windows/workspaces manually
window.on('close', () => {
const url = window.webContents.getURL();
const workspace = URI.parse(url).fragment;
if (workspace) {
const workspaceUri = URI.file(workspace);
const bounds = window.getNormalBounds();
this.closedWorkspaces.push({
...bounds,
isMaximized: window.isMaximized(),
isFullScreen: window.isFullScreen(),
file: workspaceUri.fsPath,
time: Date.now()
})
}
});
}
protected onWillQuit(event: Electron.Event): void {
// Only add workspaces which were closed within the last second (1000 milliseconds)
const threshold = Date.now() - 1000;
const visited = new Set<string>();
const workspaces = this.closedWorkspaces.filter(e => {
if (e.time < threshold || visited.has(e.file)) {
return false;
}
visited.add(e.file);
return true;
}).sort((a, b) => a.file.localeCompare(b.file));
this.electronStore.set(WORKSPACES, workspaces);
super.onWillQuit(event);
}
get windows(): BrowserWindow[] {
return this._windows.slice();
}

View File

@@ -4,6 +4,7 @@ import {
} from '../common/protocol/arduino-firmware-uploader';
import { injectable, inject, named } from 'inversify';
import { ExecutableService } from '../common/protocol';
import { SerialService } from '../common/protocol/serial-service';
import { getExecPath, spawnCommand } from './exec-util';
import { ILogger } from '@theia/core/lib/common/logger';
@@ -18,6 +19,9 @@ export class ArduinoFirmwareUploaderImpl implements ArduinoFirmwareUploader {
@named('fwuploader')
protected readonly logger: ILogger;
@inject(SerialService)
protected readonly serialService: SerialService;
protected onError(error: any): void {
this.logger.error(error);
}
@@ -66,15 +70,26 @@ export class ArduinoFirmwareUploaderImpl implements ArduinoFirmwareUploader {
}
async flash(firmware: FirmwareInfo, port: string): Promise<string> {
return await this.runCommand([
'firmware',
'flash',
'--fqbn',
firmware.board_fqbn,
'--address',
port,
'--module',
`${firmware.module}@${firmware.firmware_version}`,
]);
let output;
try {
this.serialService.uploadInProgress = true;
await this.serialService.disconnect();
output = await this.runCommand([
'firmware',
'flash',
'--fqbn',
firmware.board_fqbn,
'--address',
port,
'--module',
`${firmware.module}@${firmware.firmware_version}`,
]);
} catch (e) {
throw e;
} finally {
this.serialService.uploadInProgress = false;
this.serialService.connectSerialIfRequired();
return output;
}
}
}

View File

@@ -203,7 +203,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
// #endregion Theia customizations
// Monitor client provider per connected frontend.
// Serial client provider per connected frontend.
bind(ConnectionContainerModule).toConstantValue(
ConnectionContainerModule.create(({ bind, bindBackendService }) => {
bind(MonitorClientProvider).toSelf().inSingletonScope();
@@ -260,17 +260,14 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
)
.inSingletonScope();
bind(ArduinoFirmwareUploaderImpl).toSelf().inSingletonScope();
bind(ArduinoFirmwareUploader).toService(ArduinoFirmwareUploaderImpl);
bind(BackendApplicationContribution).toService(ArduinoFirmwareUploaderImpl);
bind(ConnectionHandler)
.toDynamicValue(
(context) =>
new JsonRpcConnectionHandler(ArduinoFirmwareUploaderPath, () =>
context.container.get(ArduinoFirmwareUploader)
)
)
.inSingletonScope();
// Singleton per BE, each FE connection gets its proxy.
bind(ConnectionContainerModule).toConstantValue(
ConnectionContainerModule.create(({ bind, bindBackendService }) => {
bind(ArduinoFirmwareUploaderImpl).toSelf().inSingletonScope();
bind(ArduinoFirmwareUploader).toService(ArduinoFirmwareUploaderImpl);
bindBackendService(ArduinoFirmwareUploaderPath, ArduinoFirmwareUploader);
})
);
// Logger for the Arduino daemon
bind(ILogger)

View File

@@ -124,8 +124,22 @@ export class BoardDiscovery extends CoreClientAware {
const address = (detectedPort as any).getPort().getAddress();
const protocol = (detectedPort as any).getPort().getProtocol();
// Different discoveries can detect the same port with different
// protocols, so we consider the combination of address and protocol
// to be the id of a certain port to distinguish it from others.
// If we'd use only the address of a port to store it in a map
// we can have conflicts the same port is found with multiple
// protocols.
const portID = `${address}|${protocol}`;
const label = (detectedPort as any).getPort().getLabel();
const port = { address, protocol, label };
const protocolLabel = (detectedPort as any).getPort().getProtocolLabel();
const port = {
id: portID,
address,
addressLabel: label,
protocol,
protocolLabel,
};
const boards: Board[] = [];
for (const item of detectedPort.getMatchingBoardsList()) {
boards.push({
@@ -136,23 +150,21 @@ export class BoardDiscovery extends CoreClientAware {
}
if (eventType === 'add') {
if (newState[port.address]) {
const [, knownBoards] = newState[port.address];
if (newState[portID]) {
const [, knownBoards] = newState[portID];
console.warn(
`Port '${
port.address
}' was already available. Known boards before override: ${JSON.stringify(
`Port '${Port.toString(port)}' was already available. Known boards before override: ${JSON.stringify(
knownBoards
)}`
);
}
newState[port.address] = [port, boards];
newState[portID] = [port, boards];
} else if (eventType === 'remove') {
if (!newState[port.address]) {
console.warn(`Port '${port.address}' was not available. Skipping`);
if (!newState[portID]) {
console.warn(`Port '${Port.toString(port)}' was not available. Skipping`);
return;
}
delete newState[port.address];
delete newState[portID];
}
const oldAvailablePorts = this.getAvailablePorts(oldState);
@@ -179,8 +191,8 @@ export class BoardDiscovery extends CoreClientAware {
getAttachedBoards(state: AvailablePorts = this.state): Board[] {
const attachedBoards: Board[] = [];
for (const address of Object.keys(state)) {
const [, boards] = state[address];
for (const portID of Object.keys(state)) {
const [, boards] = state[portID];
attachedBoards.push(...boards);
}
return attachedBoards;
@@ -188,8 +200,8 @@ export class BoardDiscovery extends CoreClientAware {
getAvailablePorts(state: AvailablePorts = this.state): Port[] {
const availablePorts: Port[] = [];
for (const address of Object.keys(state)) {
const [port] = state[address];
for (const portID of Object.keys(state)) {
const [port] = state[portID];
availablePorts.push(port);
}
return availablePorts;

View File

@@ -12,6 +12,7 @@ import * as cc_arduino_cli_commands_v1_common_pb from "../../../../../cc/arduino
import * as cc_arduino_cli_commands_v1_board_pb from "../../../../../cc/arduino/cli/commands/v1/board_pb";
import * as cc_arduino_cli_commands_v1_compile_pb from "../../../../../cc/arduino/cli/commands/v1/compile_pb";
import * as cc_arduino_cli_commands_v1_core_pb from "../../../../../cc/arduino/cli/commands/v1/core_pb";
import * as cc_arduino_cli_commands_v1_monitor_pb from "../../../../../cc/arduino/cli/commands/v1/monitor_pb";
import * as cc_arduino_cli_commands_v1_upload_pb from "../../../../../cc/arduino/cli/commands/v1/upload_pb";
import * as cc_arduino_cli_commands_v1_lib_pb from "../../../../../cc/arduino/cli/commands/v1/lib_pb";
@@ -25,6 +26,7 @@ interface IArduinoCoreServiceService extends grpc.ServiceDefinition<grpc.Untyped
outdated: IArduinoCoreServiceService_IOutdated;
upgrade: IArduinoCoreServiceService_IUpgrade;
version: IArduinoCoreServiceService_IVersion;
newSketch: IArduinoCoreServiceService_INewSketch;
loadSketch: IArduinoCoreServiceService_ILoadSketch;
archiveSketch: IArduinoCoreServiceService_IArchiveSketch;
boardDetails: IArduinoCoreServiceService_IBoardDetails;
@@ -54,6 +56,8 @@ interface IArduinoCoreServiceService extends grpc.ServiceDefinition<grpc.Untyped
libraryResolveDependencies: IArduinoCoreServiceService_ILibraryResolveDependencies;
librarySearch: IArduinoCoreServiceService_ILibrarySearch;
libraryList: IArduinoCoreServiceService_ILibraryList;
monitor: IArduinoCoreServiceService_IMonitor;
enumerateMonitorPortSettings: IArduinoCoreServiceService_IEnumerateMonitorPortSettings;
}
interface IArduinoCoreServiceService_ICreate extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_commands_pb.CreateRequest, cc_arduino_cli_commands_v1_commands_pb.CreateResponse> {
@@ -137,6 +141,15 @@ interface IArduinoCoreServiceService_IVersion extends grpc.MethodDefinition<cc_a
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_commands_pb.VersionResponse>;
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_commands_pb.VersionResponse>;
}
interface IArduinoCoreServiceService_INewSketch extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse> {
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/NewSketch";
requestStream: false;
responseStream: false;
requestSerialize: grpc.serialize<cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest>;
requestDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest>;
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse>;
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse>;
}
interface IArduinoCoreServiceService_ILoadSketch extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse> {
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/LoadSketch";
requestStream: false;
@@ -398,6 +411,24 @@ interface IArduinoCoreServiceService_ILibraryList extends grpc.MethodDefinition<
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse>;
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse>;
}
interface IArduinoCoreServiceService_IMonitor extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest, cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse> {
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/Monitor";
requestStream: true;
responseStream: true;
requestSerialize: grpc.serialize<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest>;
requestDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest>;
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse>;
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse>;
}
interface IArduinoCoreServiceService_IEnumerateMonitorPortSettings extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest, cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse> {
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/EnumerateMonitorPortSettings";
requestStream: false;
responseStream: false;
requestSerialize: grpc.serialize<cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest>;
requestDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest>;
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse>;
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse>;
}
export const ArduinoCoreServiceService: IArduinoCoreServiceService;
@@ -411,6 +442,7 @@ export interface IArduinoCoreServiceServer {
outdated: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest, cc_arduino_cli_commands_v1_commands_pb.OutdatedResponse>;
upgrade: grpc.handleServerStreamingCall<cc_arduino_cli_commands_v1_commands_pb.UpgradeRequest, cc_arduino_cli_commands_v1_commands_pb.UpgradeResponse>;
version: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.VersionRequest, cc_arduino_cli_commands_v1_commands_pb.VersionResponse>;
newSketch: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse>;
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>;
@@ -440,6 +472,8 @@ export interface IArduinoCoreServiceServer {
libraryResolveDependencies: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_lib_pb.LibraryResolveDependenciesRequest, cc_arduino_cli_commands_v1_lib_pb.LibraryResolveDependenciesResponse>;
librarySearch: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_lib_pb.LibrarySearchRequest, cc_arduino_cli_commands_v1_lib_pb.LibrarySearchResponse>;
libraryList: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_lib_pb.LibraryListRequest, cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse>;
monitor: grpc.handleBidiStreamingCall<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest, cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse>;
enumerateMonitorPortSettings: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest, cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse>;
}
export interface IArduinoCoreServiceClient {
@@ -465,6 +499,9 @@ export interface IArduinoCoreServiceClient {
version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
newSketch(request: cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse) => void): grpc.ClientUnaryCall;
newSketch(request: cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse) => void): grpc.ClientUnaryCall;
newSketch(request: cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse) => void): grpc.ClientUnaryCall;
loadSketch(request: cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse) => void): grpc.ClientUnaryCall;
loadSketch(request: cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse) => void): grpc.ClientUnaryCall;
loadSketch(request: cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse) => void): grpc.ClientUnaryCall;
@@ -537,6 +574,12 @@ export interface IArduinoCoreServiceClient {
libraryList(request: cc_arduino_cli_commands_v1_lib_pb.LibraryListRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse) => void): grpc.ClientUnaryCall;
libraryList(request: cc_arduino_cli_commands_v1_lib_pb.LibraryListRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse) => void): grpc.ClientUnaryCall;
libraryList(request: cc_arduino_cli_commands_v1_lib_pb.LibraryListRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse) => void): grpc.ClientUnaryCall;
monitor(): grpc.ClientDuplexStream<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest, cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse>;
monitor(options: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest, cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse>;
monitor(metadata: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest, cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse>;
enumerateMonitorPortSettings(request: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse) => void): grpc.ClientUnaryCall;
enumerateMonitorPortSettings(request: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse) => void): grpc.ClientUnaryCall;
enumerateMonitorPortSettings(request: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse) => void): grpc.ClientUnaryCall;
}
export class ArduinoCoreServiceClient extends grpc.Client implements IArduinoCoreServiceClient {
@@ -563,6 +606,9 @@ export class ArduinoCoreServiceClient extends grpc.Client implements IArduinoCor
public version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
public version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
public version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
public newSketch(request: cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse) => void): grpc.ClientUnaryCall;
public newSketch(request: cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse) => void): grpc.ClientUnaryCall;
public newSketch(request: cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse) => void): grpc.ClientUnaryCall;
public loadSketch(request: cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse) => void): grpc.ClientUnaryCall;
public loadSketch(request: cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse) => void): grpc.ClientUnaryCall;
public loadSketch(request: cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse) => void): grpc.ClientUnaryCall;
@@ -634,4 +680,9 @@ export class ArduinoCoreServiceClient extends grpc.Client implements IArduinoCor
public libraryList(request: cc_arduino_cli_commands_v1_lib_pb.LibraryListRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse) => void): grpc.ClientUnaryCall;
public libraryList(request: cc_arduino_cli_commands_v1_lib_pb.LibraryListRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse) => void): grpc.ClientUnaryCall;
public libraryList(request: cc_arduino_cli_commands_v1_lib_pb.LibraryListRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse) => void): grpc.ClientUnaryCall;
public monitor(options?: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest, cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse>;
public monitor(metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest, cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse>;
public enumerateMonitorPortSettings(request: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse) => void): grpc.ClientUnaryCall;
public enumerateMonitorPortSettings(request: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse) => void): grpc.ClientUnaryCall;
public enumerateMonitorPortSettings(request: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse) => void): grpc.ClientUnaryCall;
}

View File

@@ -23,6 +23,7 @@ var cc_arduino_cli_commands_v1_common_pb = require('../../../../../cc/arduino/cl
var cc_arduino_cli_commands_v1_board_pb = require('../../../../../cc/arduino/cli/commands/v1/board_pb.js');
var cc_arduino_cli_commands_v1_compile_pb = require('../../../../../cc/arduino/cli/commands/v1/compile_pb.js');
var cc_arduino_cli_commands_v1_core_pb = require('../../../../../cc/arduino/cli/commands/v1/core_pb.js');
var cc_arduino_cli_commands_v1_monitor_pb = require('../../../../../cc/arduino/cli/commands/v1/monitor_pb.js');
var cc_arduino_cli_commands_v1_upload_pb = require('../../../../../cc/arduino/cli/commands/v1/upload_pb.js');
var cc_arduino_cli_commands_v1_lib_pb = require('../../../../../cc/arduino/cli/commands/v1/lib_pb.js');
@@ -268,6 +269,28 @@ function deserialize_cc_arduino_cli_commands_v1_DestroyResponse(buffer_arg) {
return cc_arduino_cli_commands_v1_commands_pb.DestroyResponse.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_commands_v1_EnumerateMonitorPortSettingsRequest(arg) {
if (!(arg instanceof cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest)) {
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.EnumerateMonitorPortSettingsRequest');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_cc_arduino_cli_commands_v1_EnumerateMonitorPortSettingsRequest(buffer_arg) {
return cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_commands_v1_EnumerateMonitorPortSettingsResponse(arg) {
if (!(arg instanceof cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse)) {
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.EnumerateMonitorPortSettingsResponse');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_cc_arduino_cli_commands_v1_EnumerateMonitorPortSettingsResponse(buffer_arg) {
return cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_commands_v1_GitLibraryInstallRequest(arg) {
if (!(arg instanceof cc_arduino_cli_commands_v1_lib_pb.GitLibraryInstallRequest)) {
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.GitLibraryInstallRequest');
@@ -510,6 +533,50 @@ function deserialize_cc_arduino_cli_commands_v1_LoadSketchResponse(buffer_arg) {
return cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_commands_v1_MonitorRequest(arg) {
if (!(arg instanceof cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest)) {
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.MonitorRequest');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_cc_arduino_cli_commands_v1_MonitorRequest(buffer_arg) {
return cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_commands_v1_MonitorResponse(arg) {
if (!(arg instanceof cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse)) {
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.MonitorResponse');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_cc_arduino_cli_commands_v1_MonitorResponse(buffer_arg) {
return cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_commands_v1_NewSketchRequest(arg) {
if (!(arg instanceof cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest)) {
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.NewSketchRequest');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_cc_arduino_cli_commands_v1_NewSketchRequest(buffer_arg) {
return cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_commands_v1_NewSketchResponse(arg) {
if (!(arg instanceof cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse)) {
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.NewSketchResponse');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_cc_arduino_cli_commands_v1_NewSketchResponse(buffer_arg) {
return cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_commands_v1_OutdatedRequest(arg) {
if (!(arg instanceof cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest)) {
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.OutdatedRequest');
@@ -974,6 +1041,18 @@ version: {
responseSerialize: serialize_cc_arduino_cli_commands_v1_VersionResponse,
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_VersionResponse,
},
// Create a new Sketch
newSketch: {
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/NewSketch',
requestStream: false,
responseStream: false,
requestType: cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest,
responseType: cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse,
requestSerialize: serialize_cc_arduino_cli_commands_v1_NewSketchRequest,
requestDeserialize: deserialize_cc_arduino_cli_commands_v1_NewSketchRequest,
responseSerialize: serialize_cc_arduino_cli_commands_v1_NewSketchResponse,
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_NewSketchResponse,
},
// Returns all files composing a Sketch
loadSketch: {
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/LoadSketch',
@@ -1331,6 +1410,30 @@ libraryList: {
responseSerialize: serialize_cc_arduino_cli_commands_v1_LibraryListResponse,
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_LibraryListResponse,
},
// Open a monitor connection to a board port
monitor: {
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/Monitor',
requestStream: true,
responseStream: true,
requestType: cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest,
responseType: cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse,
requestSerialize: serialize_cc_arduino_cli_commands_v1_MonitorRequest,
requestDeserialize: deserialize_cc_arduino_cli_commands_v1_MonitorRequest,
responseSerialize: serialize_cc_arduino_cli_commands_v1_MonitorResponse,
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_MonitorResponse,
},
// Returns the parameters that can be set in the MonitorRequest calls
enumerateMonitorPortSettings: {
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/EnumerateMonitorPortSettings',
requestStream: false,
responseStream: false,
requestType: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest,
responseType: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse,
requestSerialize: serialize_cc_arduino_cli_commands_v1_EnumerateMonitorPortSettingsRequest,
requestDeserialize: deserialize_cc_arduino_cli_commands_v1_EnumerateMonitorPortSettingsRequest,
responseSerialize: serialize_cc_arduino_cli_commands_v1_EnumerateMonitorPortSettingsResponse,
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_EnumerateMonitorPortSettingsResponse,
},
};
// BOOTSTRAP COMMANDS

View File

@@ -10,6 +10,7 @@ import * as cc_arduino_cli_commands_v1_common_pb from "../../../../../cc/arduino
import * as cc_arduino_cli_commands_v1_board_pb from "../../../../../cc/arduino/cli/commands/v1/board_pb";
import * as cc_arduino_cli_commands_v1_compile_pb from "../../../../../cc/arduino/cli/commands/v1/compile_pb";
import * as cc_arduino_cli_commands_v1_core_pb from "../../../../../cc/arduino/cli/commands/v1/core_pb";
import * as cc_arduino_cli_commands_v1_monitor_pb from "../../../../../cc/arduino/cli/commands/v1/monitor_pb";
import * as cc_arduino_cli_commands_v1_upload_pb from "../../../../../cc/arduino/cli/commands/v1/upload_pb";
import * as cc_arduino_cli_commands_v1_lib_pb from "../../../../../cc/arduino/cli/commands/v1/lib_pb";
@@ -489,6 +490,59 @@ export namespace VersionResponse {
}
}
export class NewSketchRequest 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): NewSketchRequest;
getSketchName(): string;
setSketchName(value: string): NewSketchRequest;
getSketchDir(): string;
setSketchDir(value: string): NewSketchRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): NewSketchRequest.AsObject;
static toObject(includeInstance: boolean, msg: NewSketchRequest): NewSketchRequest.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: NewSketchRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): NewSketchRequest;
static deserializeBinaryFromReader(message: NewSketchRequest, reader: jspb.BinaryReader): NewSketchRequest;
}
export namespace NewSketchRequest {
export type AsObject = {
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
sketchName: string,
sketchDir: string,
}
}
export class NewSketchResponse extends jspb.Message {
getMainFile(): string;
setMainFile(value: string): NewSketchResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): NewSketchResponse.AsObject;
static toObject(includeInstance: boolean, msg: NewSketchResponse): NewSketchResponse.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: NewSketchResponse, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): NewSketchResponse;
static deserializeBinaryFromReader(message: NewSketchResponse, reader: jspb.BinaryReader): NewSketchResponse;
}
export namespace NewSketchResponse {
export type AsObject = {
mainFile: string,
}
}
export class LoadSketchRequest extends jspb.Message {
hasInstance(): boolean;

View File

@@ -25,6 +25,8 @@ var cc_arduino_cli_commands_v1_compile_pb = require('../../../../../cc/arduino/c
goog.object.extend(proto, cc_arduino_cli_commands_v1_compile_pb);
var cc_arduino_cli_commands_v1_core_pb = require('../../../../../cc/arduino/cli/commands/v1/core_pb.js');
goog.object.extend(proto, cc_arduino_cli_commands_v1_core_pb);
var cc_arduino_cli_commands_v1_monitor_pb = require('../../../../../cc/arduino/cli/commands/v1/monitor_pb.js');
goog.object.extend(proto, cc_arduino_cli_commands_v1_monitor_pb);
var cc_arduino_cli_commands_v1_upload_pb = require('../../../../../cc/arduino/cli/commands/v1/upload_pb.js');
goog.object.extend(proto, cc_arduino_cli_commands_v1_upload_pb);
var cc_arduino_cli_commands_v1_lib_pb = require('../../../../../cc/arduino/cli/commands/v1/lib_pb.js');
@@ -41,6 +43,8 @@ goog.exportSymbol('proto.cc.arduino.cli.commands.v1.InitResponse.MessageCase', n
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.InitResponse.Progress', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.LoadSketchRequest', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.LoadSketchResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.NewSketchRequest', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.NewSketchResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.OutdatedRequest', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.OutdatedResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.UpdateCoreLibrariesIndexRequest', null, global);
@@ -452,6 +456,48 @@ if (goog.DEBUG && !COMPILED) {
*/
proto.cc.arduino.cli.commands.v1.VersionResponse.displayName = 'proto.cc.arduino.cli.commands.v1.VersionResponse';
}
/**
* 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.NewSketchRequest = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.cc.arduino.cli.commands.v1.NewSketchRequest, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.displayName = 'proto.cc.arduino.cli.commands.v1.NewSketchRequest';
}
/**
* 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.NewSketchResponse = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.cc.arduino.cli.commands.v1.NewSketchResponse, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.cc.arduino.cli.commands.v1.NewSketchResponse.displayName = 'proto.cc.arduino.cli.commands.v1.NewSketchResponse';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
@@ -3508,6 +3554,347 @@ proto.cc.arduino.cli.commands.v1.VersionResponse.prototype.setVersion = 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.NewSketchRequest.prototype.toObject = function(opt_includeInstance) {
return proto.cc.arduino.cli.commands.v1.NewSketchRequest.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.NewSketchRequest} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.toObject = function(includeInstance, msg) {
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, "")
};
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.NewSketchRequest}
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.cc.arduino.cli.commands.v1.NewSketchRequest;
return proto.cc.arduino.cli.commands.v1.NewSketchRequest.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.NewSketchRequest} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.cc.arduino.cli.commands.v1.NewSketchRequest}
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.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.setSketchName(value);
break;
case 3:
var value = /** @type {string} */ (reader.readString());
msg.setSketchDir(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.NewSketchRequest.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.cc.arduino.cli.commands.v1.NewSketchRequest.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.NewSketchRequest} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.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.getSketchName();
if (f.length > 0) {
writer.writeString(
2,
f
);
}
f = message.getSketchDir();
if (f.length > 0) {
writer.writeString(
3,
f
);
}
};
/**
* optional Instance instance = 1;
* @return {?proto.cc.arduino.cli.commands.v1.Instance}
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.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.NewSketchRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.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.NewSketchRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.clearInstance = function() {
return this.setInstance(undefined);
};
/**
* Returns whether this field is set.
* @return {boolean}
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.hasInstance = function() {
return jspb.Message.getField(this, 1) != null;
};
/**
* optional string sketch_name = 2;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.getSketchName = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.NewSketchRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.setSketchName = function(value) {
return jspb.Message.setProto3StringField(this, 2, value);
};
/**
* optional string sketch_dir = 3;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.getSketchDir = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.NewSketchRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.setSketchDir = function(value) {
return jspb.Message.setProto3StringField(this, 3, 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.NewSketchResponse.prototype.toObject = function(opt_includeInstance) {
return proto.cc.arduino.cli.commands.v1.NewSketchResponse.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.NewSketchResponse} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.commands.v1.NewSketchResponse.toObject = function(includeInstance, msg) {
var f, obj = {
mainFile: 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.NewSketchResponse}
*/
proto.cc.arduino.cli.commands.v1.NewSketchResponse.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.cc.arduino.cli.commands.v1.NewSketchResponse;
return proto.cc.arduino.cli.commands.v1.NewSketchResponse.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.NewSketchResponse} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.cc.arduino.cli.commands.v1.NewSketchResponse}
*/
proto.cc.arduino.cli.commands.v1.NewSketchResponse.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.setMainFile(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.NewSketchResponse.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.cc.arduino.cli.commands.v1.NewSketchResponse.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.NewSketchResponse} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.commands.v1.NewSketchResponse.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getMainFile();
if (f.length > 0) {
writer.writeString(
1,
f
);
}
};
/**
* optional string main_file = 1;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.NewSketchResponse.prototype.getMainFile = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.NewSketchResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.NewSketchResponse.prototype.setMainFile = function(value) {
return jspb.Message.setProto3StringField(this, 1, value);
};
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.

View File

@@ -74,6 +74,9 @@ export class TaskProgress extends jspb.Message {
getCompleted(): boolean;
setCompleted(value: boolean): TaskProgress;
getPercent(): number;
setPercent(value: number): TaskProgress;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): TaskProgress.AsObject;
@@ -90,6 +93,7 @@ export namespace TaskProgress {
name: string,
message: string,
completed: boolean,
percent: number,
}
}
@@ -181,6 +185,31 @@ export namespace Platform {
}
}
export class PlatformReference extends jspb.Message {
getId(): string;
setId(value: string): PlatformReference;
getVersion(): string;
setVersion(value: string): PlatformReference;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PlatformReference.AsObject;
static toObject(includeInstance: boolean, msg: PlatformReference): PlatformReference.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: PlatformReference, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): PlatformReference;
static deserializeBinaryFromReader(message: PlatformReference, reader: jspb.BinaryReader): PlatformReference;
}
export namespace PlatformReference {
export type AsObject = {
id: string,
version: string,
}
}
export class Board extends jspb.Message {
getName(): string;
setName(value: string): Board;

View File

@@ -19,6 +19,7 @@ goog.exportSymbol('proto.cc.arduino.cli.commands.v1.Board', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.DownloadProgress', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.Instance', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.Platform', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.PlatformReference', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.Programmer', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.TaskProgress', null, global);
/**
@@ -126,6 +127,27 @@ if (goog.DEBUG && !COMPILED) {
*/
proto.cc.arduino.cli.commands.v1.Platform.displayName = 'proto.cc.arduino.cli.commands.v1.Platform';
}
/**
* 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.PlatformReference = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.cc.arduino.cli.commands.v1.PlatformReference, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.cc.arduino.cli.commands.v1.PlatformReference.displayName = 'proto.cc.arduino.cli.commands.v1.PlatformReference';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
@@ -561,7 +583,8 @@ proto.cc.arduino.cli.commands.v1.TaskProgress.toObject = function(includeInstanc
var f, obj = {
name: jspb.Message.getFieldWithDefault(msg, 1, ""),
message: jspb.Message.getFieldWithDefault(msg, 2, ""),
completed: jspb.Message.getBooleanFieldWithDefault(msg, 3, false)
completed: jspb.Message.getBooleanFieldWithDefault(msg, 3, false),
percent: jspb.Message.getFloatingPointFieldWithDefault(msg, 4, 0.0)
};
if (includeInstance) {
@@ -610,6 +633,10 @@ proto.cc.arduino.cli.commands.v1.TaskProgress.deserializeBinaryFromReader = func
var value = /** @type {boolean} */ (reader.readBool());
msg.setCompleted(value);
break;
case 4:
var value = /** @type {number} */ (reader.readFloat());
msg.setPercent(value);
break;
default:
reader.skipField();
break;
@@ -660,6 +687,13 @@ proto.cc.arduino.cli.commands.v1.TaskProgress.serializeBinaryToWriter = function
f
);
}
f = message.getPercent();
if (f !== 0.0) {
writer.writeFloat(
4,
f
);
}
};
@@ -717,6 +751,24 @@ proto.cc.arduino.cli.commands.v1.TaskProgress.prototype.setCompleted = function(
};
/**
* optional float percent = 4;
* @return {number}
*/
proto.cc.arduino.cli.commands.v1.TaskProgress.prototype.getPercent = function() {
return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 4, 0.0));
};
/**
* @param {number} value
* @return {!proto.cc.arduino.cli.commands.v1.TaskProgress} returns this
*/
proto.cc.arduino.cli.commands.v1.TaskProgress.prototype.setPercent = function(value) {
return jspb.Message.setProto3FloatField(this, 4, value);
};
@@ -1340,6 +1392,166 @@ proto.cc.arduino.cli.commands.v1.Platform.prototype.setDeprecated = function(val
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.PlatformReference.prototype.toObject = function(opt_includeInstance) {
return proto.cc.arduino.cli.commands.v1.PlatformReference.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.PlatformReference} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.commands.v1.PlatformReference.toObject = function(includeInstance, msg) {
var f, obj = {
id: jspb.Message.getFieldWithDefault(msg, 1, ""),
version: 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.PlatformReference}
*/
proto.cc.arduino.cli.commands.v1.PlatformReference.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.cc.arduino.cli.commands.v1.PlatformReference;
return proto.cc.arduino.cli.commands.v1.PlatformReference.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.PlatformReference} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.cc.arduino.cli.commands.v1.PlatformReference}
*/
proto.cc.arduino.cli.commands.v1.PlatformReference.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.setId(value);
break;
case 2:
var value = /** @type {string} */ (reader.readString());
msg.setVersion(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.PlatformReference.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.cc.arduino.cli.commands.v1.PlatformReference.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.PlatformReference} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.commands.v1.PlatformReference.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getId();
if (f.length > 0) {
writer.writeString(
1,
f
);
}
f = message.getVersion();
if (f.length > 0) {
writer.writeString(
2,
f
);
}
};
/**
* optional string id = 1;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.PlatformReference.prototype.getId = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.PlatformReference} returns this
*/
proto.cc.arduino.cli.commands.v1.PlatformReference.prototype.setId = function(value) {
return jspb.Message.setProto3StringField(this, 1, value);
};
/**
* optional string version = 2;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.PlatformReference.prototype.getVersion = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.PlatformReference} returns this
*/
proto.cc.arduino.cli.commands.v1.PlatformReference.prototype.setVersion = function(value) {
return jspb.Message.setProto3StringField(this, 2, value);
};
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.

View File

@@ -149,6 +149,24 @@ export class CompileResponse extends jspb.Message {
addExecutableSectionsSize(value?: ExecutableSectionSize, index?: number): ExecutableSectionSize;
hasBoardPlatform(): boolean;
clearBoardPlatform(): void;
getBoardPlatform(): cc_arduino_cli_commands_v1_common_pb.PlatformReference | undefined;
setBoardPlatform(value?: cc_arduino_cli_commands_v1_common_pb.PlatformReference): CompileResponse;
hasBuildPlatform(): boolean;
clearBuildPlatform(): void;
getBuildPlatform(): cc_arduino_cli_commands_v1_common_pb.PlatformReference | undefined;
setBuildPlatform(value?: cc_arduino_cli_commands_v1_common_pb.PlatformReference): CompileResponse;
hasProgress(): boolean;
clearProgress(): void;
getProgress(): cc_arduino_cli_commands_v1_common_pb.TaskProgress | undefined;
setProgress(value?: cc_arduino_cli_commands_v1_common_pb.TaskProgress): CompileResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): CompileResponse.AsObject;
static toObject(includeInstance: boolean, msg: CompileResponse): CompileResponse.AsObject;
@@ -166,6 +184,9 @@ export namespace CompileResponse {
buildPath: string,
usedLibrariesList: Array<cc_arduino_cli_commands_v1_lib_pb.Library.AsObject>,
executableSectionsSizeList: Array<ExecutableSectionSize.AsObject>,
boardPlatform?: cc_arduino_cli_commands_v1_common_pb.PlatformReference.AsObject,
buildPlatform?: cc_arduino_cli_commands_v1_common_pb.PlatformReference.AsObject,
progress?: cc_arduino_cli_commands_v1_common_pb.TaskProgress.AsObject,
}
}

View File

@@ -971,7 +971,10 @@ proto.cc.arduino.cli.commands.v1.CompileResponse.toObject = function(includeInst
usedLibrariesList: jspb.Message.toObjectList(msg.getUsedLibrariesList(),
cc_arduino_cli_commands_v1_lib_pb.Library.toObject, includeInstance),
executableSectionsSizeList: jspb.Message.toObjectList(msg.getExecutableSectionsSizeList(),
proto.cc.arduino.cli.commands.v1.ExecutableSectionSize.toObject, includeInstance)
proto.cc.arduino.cli.commands.v1.ExecutableSectionSize.toObject, includeInstance),
boardPlatform: (f = msg.getBoardPlatform()) && cc_arduino_cli_commands_v1_common_pb.PlatformReference.toObject(includeInstance, f),
buildPlatform: (f = msg.getBuildPlatform()) && cc_arduino_cli_commands_v1_common_pb.PlatformReference.toObject(includeInstance, f),
progress: (f = msg.getProgress()) && cc_arduino_cli_commands_v1_common_pb.TaskProgress.toObject(includeInstance, f)
};
if (includeInstance) {
@@ -1030,6 +1033,21 @@ proto.cc.arduino.cli.commands.v1.CompileResponse.deserializeBinaryFromReader = f
reader.readMessage(value,proto.cc.arduino.cli.commands.v1.ExecutableSectionSize.deserializeBinaryFromReader);
msg.addExecutableSectionsSize(value);
break;
case 6:
var value = new cc_arduino_cli_commands_v1_common_pb.PlatformReference;
reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.PlatformReference.deserializeBinaryFromReader);
msg.setBoardPlatform(value);
break;
case 7:
var value = new cc_arduino_cli_commands_v1_common_pb.PlatformReference;
reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.PlatformReference.deserializeBinaryFromReader);
msg.setBuildPlatform(value);
break;
case 8:
var value = new cc_arduino_cli_commands_v1_common_pb.TaskProgress;
reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.TaskProgress.deserializeBinaryFromReader);
msg.setProgress(value);
break;
default:
reader.skipField();
break;
@@ -1096,6 +1114,30 @@ proto.cc.arduino.cli.commands.v1.CompileResponse.serializeBinaryToWriter = funct
proto.cc.arduino.cli.commands.v1.ExecutableSectionSize.serializeBinaryToWriter
);
}
f = message.getBoardPlatform();
if (f != null) {
writer.writeMessage(
6,
f,
cc_arduino_cli_commands_v1_common_pb.PlatformReference.serializeBinaryToWriter
);
}
f = message.getBuildPlatform();
if (f != null) {
writer.writeMessage(
7,
f,
cc_arduino_cli_commands_v1_common_pb.PlatformReference.serializeBinaryToWriter
);
}
f = message.getProgress();
if (f != null) {
writer.writeMessage(
8,
f,
cc_arduino_cli_commands_v1_common_pb.TaskProgress.serializeBinaryToWriter
);
}
};
@@ -1277,6 +1319,117 @@ proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.clearExecutableSectio
};
/**
* optional PlatformReference board_platform = 6;
* @return {?proto.cc.arduino.cli.commands.v1.PlatformReference}
*/
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.getBoardPlatform = function() {
return /** @type{?proto.cc.arduino.cli.commands.v1.PlatformReference} */ (
jspb.Message.getWrapperField(this, cc_arduino_cli_commands_v1_common_pb.PlatformReference, 6));
};
/**
* @param {?proto.cc.arduino.cli.commands.v1.PlatformReference|undefined} value
* @return {!proto.cc.arduino.cli.commands.v1.CompileResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.setBoardPlatform = function(value) {
return jspb.Message.setWrapperField(this, 6, value);
};
/**
* Clears the message field making it undefined.
* @return {!proto.cc.arduino.cli.commands.v1.CompileResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.clearBoardPlatform = function() {
return this.setBoardPlatform(undefined);
};
/**
* Returns whether this field is set.
* @return {boolean}
*/
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.hasBoardPlatform = function() {
return jspb.Message.getField(this, 6) != null;
};
/**
* optional PlatformReference build_platform = 7;
* @return {?proto.cc.arduino.cli.commands.v1.PlatformReference}
*/
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.getBuildPlatform = function() {
return /** @type{?proto.cc.arduino.cli.commands.v1.PlatformReference} */ (
jspb.Message.getWrapperField(this, cc_arduino_cli_commands_v1_common_pb.PlatformReference, 7));
};
/**
* @param {?proto.cc.arduino.cli.commands.v1.PlatformReference|undefined} value
* @return {!proto.cc.arduino.cli.commands.v1.CompileResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.setBuildPlatform = function(value) {
return jspb.Message.setWrapperField(this, 7, value);
};
/**
* Clears the message field making it undefined.
* @return {!proto.cc.arduino.cli.commands.v1.CompileResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.clearBuildPlatform = function() {
return this.setBuildPlatform(undefined);
};
/**
* Returns whether this field is set.
* @return {boolean}
*/
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.hasBuildPlatform = function() {
return jspb.Message.getField(this, 7) != null;
};
/**
* optional TaskProgress progress = 8;
* @return {?proto.cc.arduino.cli.commands.v1.TaskProgress}
*/
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.getProgress = function() {
return /** @type{?proto.cc.arduino.cli.commands.v1.TaskProgress} */ (
jspb.Message.getWrapperField(this, cc_arduino_cli_commands_v1_common_pb.TaskProgress, 8));
};
/**
* @param {?proto.cc.arduino.cli.commands.v1.TaskProgress|undefined} value
* @return {!proto.cc.arduino.cli.commands.v1.CompileResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.setProgress = function(value) {
return jspb.Message.setWrapperField(this, 8, value);
};
/**
* Clears the message field making it undefined.
* @return {!proto.cc.arduino.cli.commands.v1.CompileResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.clearProgress = function() {
return this.setProgress(undefined);
};
/**
* Returns whether this field is set.
* @return {boolean}
*/
proto.cc.arduino.cli.commands.v1.CompileResponse.prototype.hasProgress = function() {
return jspb.Message.getField(this, 8) != null;
};

View File

@@ -371,6 +371,9 @@ export class SupportedUserFieldsRequest extends jspb.Message {
getProtocol(): string;
setProtocol(value: string): SupportedUserFieldsRequest;
getAddress(): string;
setAddress(value: string): SupportedUserFieldsRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SupportedUserFieldsRequest.AsObject;
@@ -387,6 +390,7 @@ export namespace SupportedUserFieldsRequest {
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
fqbn: string,
protocol: string,
address: string,
}
}

View File

@@ -2718,7 +2718,8 @@ proto.cc.arduino.cli.commands.v1.SupportedUserFieldsRequest.toObject = function(
var f, obj = {
instance: (f = msg.getInstance()) && cc_arduino_cli_commands_v1_common_pb.Instance.toObject(includeInstance, f),
fqbn: jspb.Message.getFieldWithDefault(msg, 2, ""),
protocol: jspb.Message.getFieldWithDefault(msg, 3, "")
protocol: jspb.Message.getFieldWithDefault(msg, 3, ""),
address: jspb.Message.getFieldWithDefault(msg, 4, "")
};
if (includeInstance) {
@@ -2768,6 +2769,10 @@ proto.cc.arduino.cli.commands.v1.SupportedUserFieldsRequest.deserializeBinaryFro
var value = /** @type {string} */ (reader.readString());
msg.setProtocol(value);
break;
case 4:
var value = /** @type {string} */ (reader.readString());
msg.setAddress(value);
break;
default:
reader.skipField();
break;
@@ -2819,6 +2824,13 @@ proto.cc.arduino.cli.commands.v1.SupportedUserFieldsRequest.serializeBinaryToWri
f
);
}
f = message.getAddress();
if (f.length > 0) {
writer.writeString(
4,
f
);
}
};
@@ -2895,6 +2907,24 @@ proto.cc.arduino.cli.commands.v1.SupportedUserFieldsRequest.prototype.setProtoco
};
/**
* optional string address = 4;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.SupportedUserFieldsRequest.prototype.getAddress = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.SupportedUserFieldsRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.SupportedUserFieldsRequest.prototype.setAddress = function(value) {
return jspb.Message.setProto3StringField(this, 4, value);
};

View File

@@ -43,7 +43,10 @@ function deserialize_cc_arduino_cli_monitor_v1_StreamingOpenResponse(buffer_arg)
}
// MonitorService provides services for boards monitor
// MonitorService provides services for boards monitor.
// DEPRECATION WARNING: MonitorService is deprecated and will be removed in a
// future release. Use ArduinoCoreService.Monitor and
// ArduinoCoreService.EnumerateMonitorPortSettings instead.
var MonitorServiceService = exports['cc.arduino.cli.monitor.v1.MonitorService'] = {
// Open a bidirectional monitor stream. This can be used to implement
// something similar to the Arduino IDE's Serial Monitor.

View File

@@ -96,7 +96,7 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
e.details
);
this.responseService.appendToOutput({
chunk: `${errorMessage}}\n`,
chunk: `${errorMessage}\n`,
severity: 'error',
});
throw new Error(errorMessage);
@@ -159,8 +159,9 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
const p = new Port();
if (port) {
p.setAddress(port.address);
p.setLabel(port.label || '');
p.setLabel(port.addressLabel);
p.setProtocol(port.protocol);
p.setProtocolLabel(port.protocolLabel);
}
req.setPort(p);
if (programmer) {
@@ -229,8 +230,9 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
const p = new Port();
if (port) {
p.setAddress(port.address);
p.setLabel(port.label || '');
p.setLabel(port.addressLabel);
p.setProtocol(port.protocol);
p.setProtocolLabel(port.protocolLabel);
}
burnReq.setPort(p);
if (programmer) {

View File

@@ -17,7 +17,7 @@ export class ExecutableServiceImpl implements ExecutableService {
}> {
const [ls, clangd, cli, fwuploader] = await Promise.all([
getExecPath('arduino-language-server', this.onError.bind(this)),
getExecPath('clangd', this.onError.bind(this), undefined, true),
getExecPath('clangd', this.onError.bind(this), undefined),
getExecPath('arduino-cli', this.onError.bind(this)),
getExecPath('arduino-fwuploader', this.onError.bind(this)),
]);

View File

@@ -70,9 +70,11 @@ export abstract class GrpcClientProvider<C> {
protected abstract close(client: C): void;
protected get channelOptions(): Record<string, unknown> {
const pjson = require('../../package.json') || { version: '0.0.0' };
return {
'grpc.max_send_message_length': 512 * 1024 * 1024,
'grpc.max_receive_message_length': 512 * 1024 * 1024,
'grpc.primary_user_agent': `arduino-ide/${pjson.version}`,
};
}
}

View File

@@ -16,7 +16,7 @@ import {
MonitorConfig as GrpcMonitorConfig,
} from '../cli-protocol/cc/arduino/cli/monitor/v1/monitor_pb';
import { MonitorClientProvider } from './monitor-client-provider';
import { Board, Port } from '../../common/protocol/boards-service';
import { Board } from '../../common/protocol/boards-service';
import { WebSocketService } from '../web-socket/web-socket-service';
import { SerialPlotter } from '../../browser/serial/plotter/protocol';
import { Disposable } from '@theia/core/shared/vscode-languageserver-protocol';
@@ -88,7 +88,7 @@ export class SerialServiceImpl implements SerialService {
@inject(WebSocketService)
protected readonly webSocketService: WebSocketService
) {}
) { }
async isSerialPortOpen(): Promise<boolean> {
return !!this.serialConnection;
@@ -153,7 +153,7 @@ export class SerialServiceImpl implements SerialService {
this.logger.info(
`>>> Creating serial connection for ${Board.toString(
this.serialConfig.board
)} on port ${Port.toString(this.serialConfig.port)}...`
)} on port ${this.serialConfig.port.address}...`
);
if (this.serialConnection) {
@@ -225,7 +225,7 @@ export class SerialServiceImpl implements SerialService {
default:
break;
}
} catch (error) {}
} catch (error) { }
}
);
@@ -272,12 +272,12 @@ export class SerialServiceImpl implements SerialService {
serialConnection.duplex.write(req, () => {
const boardName = this.serialConfig?.board
? Board.toString(this.serialConfig.board, {
useFqbn: false,
})
useFqbn: false,
})
: 'unknown board';
const portName = this.serialConfig?.port
? Port.toString(this.serialConfig.port)
? this.serialConfig.port.address
: 'unknown port';
this.logger.info(
`<<< Serial connection created for ${boardName} on port ${portName}.`
@@ -330,7 +330,7 @@ export class SerialServiceImpl implements SerialService {
this.logger.info(
`<<< Disposed serial connection for ${Board.toString(config.board, {
useFqbn: false,
})} on port ${Port.toString(config.port)}.`
})} on port ${config.port.address}.`
);
duplex.cancel();

View File

@@ -4,11 +4,20 @@ import { Board, BoardsPackage, Port } from '../../../common/protocol';
export const aBoard: Board = {
fqbn: 'some:board:fqbn',
name: 'Some Arduino Board',
port: { address: '/lol/port1234', protocol: 'serial' },
port: {
id: '/lol/port1234|serial',
address: '/lol/port1234',
addressLabel: '/lol/port1234',
protocol: 'serial',
protocolLabel: 'Serial Port (USB)',
},
};
export const aPort: Port = {
id: aBoard.port!.id,
address: aBoard.port!.address,
addressLabel: aBoard.port!.addressLabel,
protocol: aBoard.port!.protocol,
protocolLabel: aBoard.port!.protocolLabel,
};
export const aBoardConfig: BoardsConfig.Config = {
selectedBoard: aBoard,
@@ -17,11 +26,20 @@ export const aBoardConfig: BoardsConfig.Config = {
export const anotherBoard: Board = {
fqbn: 'another:board:fqbn',
name: 'Another Arduino Board',
port: { address: '/kek/port5678', protocol: 'serial' },
port: {
id: '/kek/port5678|serial',
address: '/kek/port5678',
addressLabel: '/kek/port5678',
protocol: 'serial',
protocolLabel: 'Serial Port (USB)',
},
};
export const anotherPort: Port = {
id: anotherBoard.port!.id,
address: anotherBoard.port!.address,
addressLabel: anotherBoard.port!.addressLabel,
protocol: anotherBoard.port!.protocol,
protocolLabel: anotherBoard.port!.protocolLabel,
};
export const anotherBoardConfig: BoardsConfig.Config = {
selectedBoard: anotherBoard,

View File

@@ -22,7 +22,7 @@ describe('getExecPath', () => {
});
it('should resolve clangd', async () => {
const actual = await getExecPath('clangd', onError, '--version', true);
const actual = await getExecPath('clangd', onError, '--version');
const expected = os.platform() === 'win32' ? '\\clangd.exe' : '/clangd';
expect(actual).to.endsWith(expected);
});

View File

@@ -86,7 +86,7 @@ describe('SerialServiceImpl', () => {
context('when a disconnection is requested', () => {
const sandbox = createSandbox();
beforeEach(() => {});
beforeEach(() => { });
afterEach(function () {
sandbox.restore();
@@ -132,11 +132,11 @@ describe('SerialServiceImpl', () => {
return {
streamingOpen: () => {
return {
on: (str: string, cb: any) => {},
on: (str: string, cb: any) => { },
write: (chunk: any, cb: any) => {
cb();
},
cancel: () => {},
cancel: () => { },
};
},
} as MonitorServiceClient;
@@ -146,7 +146,7 @@ describe('SerialServiceImpl', () => {
await subject.setSerialConfig({
board: { name: 'test' },
port: { address: 'test', protocol: 'test' },
port: { id: 'test|test', address: 'test', addressLabel: 'test', protocol: 'test', protocolLabel: 'test' },
});
});

View File

@@ -1,29 +1,29 @@
{
"private": true,
"name": "browser-app",
"version": "2.0.0-rc2",
"version": "2.0.0-rc4",
"license": "AGPL-3.0-or-later",
"dependencies": {
"@theia/core": "1.19.0",
"@theia/debug": "1.19.0",
"@theia/editor": "1.19.0",
"@theia/editor-preview": "1.19.0",
"@theia/file-search": "1.19.0",
"@theia/filesystem": "1.19.0",
"@theia/keymaps": "1.19.0",
"@theia/messages": "1.19.0",
"@theia/monaco": "1.19.0",
"@theia/navigator": "1.19.0",
"@theia/plugin-ext": "1.19.0",
"@theia/plugin-ext-vscode": "1.19.0",
"@theia/preferences": "1.19.0",
"@theia/process": "1.19.0",
"@theia/terminal": "1.19.0",
"@theia/workspace": "1.19.0",
"arduino-ide-extension": "2.0.0-rc2"
"@theia/core": "1.22.1",
"@theia/debug": "1.22.1",
"@theia/editor": "1.22.1",
"@theia/editor-preview": "1.22.1",
"@theia/file-search": "1.22.1",
"@theia/filesystem": "1.22.1",
"@theia/keymaps": "1.22.1",
"@theia/messages": "1.22.1",
"@theia/monaco": "1.22.1",
"@theia/navigator": "1.22.1",
"@theia/plugin-ext": "1.22.1",
"@theia/plugin-ext-vscode": "1.22.1",
"@theia/preferences": "1.22.1",
"@theia/process": "1.22.1",
"@theia/terminal": "1.22.1",
"@theia/workspace": "1.22.1",
"arduino-ide-extension": "2.0.0-rc4"
},
"devDependencies": {
"@theia/cli": "1.19.0"
"@theia/cli": "1.22.1"
},
"scripts": {
"prepare": "theia build --mode development",
@@ -60,4 +60,4 @@
}
}
}
}
}

View File

@@ -6,7 +6,7 @@ Arduino IDE provides a Remote Sketchbook feature that can be used to upload sket
In order to use this feature, a user must be registered on [Arduino Cloud](https://store.arduino.cc/digital/create) and logged in.
This feature is completely optional and can be disabled in the IDE via _"File > Advanced > Hide Remote Sketchbook"_ menu item.
This feature is completely optional and can be disabled in the IDE via the _"File > Advanced > Hide Remote Sketchbook"_ menu item.
## Developer guide
A developer could use the content of this repo to create a customized version of this feature and implement a different remote storage as follows:

View File

@@ -2,19 +2,18 @@
Building the Pro IDE on Linux `armv7l` (aka `armhf`) and `aarch64` (aka `arm64`):
1. Install Node.js 10.x with [nvm](https://github.com/nvm-sh/nvm#install--update-script):
1. Install Node.js 14.x with [nvm](https://github.com/nvm-sh/nvm#install--update-script):
```
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
```
Restart your shell then:
```
nvm install 12.14.1
nvm use 12.14.1
nvm install 14
nvm use 14
```
Verify:
```
node -v
v12.14.1
```
2. Install [Yarn](https://classic.yarnpkg.com/en/docs/install/#debian-stable):
@@ -42,10 +41,10 @@ Building the Pro IDE on Linux `armv7l` (aka `armhf`) and `aarch64` (aka `arm64`)
- `libx11-dev`, and
- `libxkbfile-dev`
4. [Build it](https://github.com/bcmi-labs/arduino-editor#build-from-source) from the source:
4. [Build it](../../BUILDING.md#build-from-source) from the source:
```
git clone https://github.com/bcmi-labs/arduino-editor.git \
&& cd arduino-editor \
git clone https://github.com/arduino/arduino-ide.git \
&& cd arduino-ide \
&& yarn \
&& yarn rebuild:electron \
&& yarn --cwd ./electron-app start

View File

@@ -1,6 +1,6 @@
### Building and start the app from the sources on Ubuntu Linux
Tested and verified on Ubuntu 18.04.4. The source will be checked out to `~/dev/git/arduino-editor`.
Tested and verified on Ubuntu 18.04.4. The source will be checked out to `~/dev/git/arduino-ide`.
```
#!/bin/bash -i
@@ -18,16 +18,16 @@ sudo apt update \
build-essential \
&& wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash \
&& source ~/.bashrc \
&& nvm install 12.14.1 \
&& nvm use 12.14.1 \
&& nvm alias default 12.14.1 \
&& nvm install 14 \
&& nvm use 14 \
&& nvm alias default 14 \
&& curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - \
&& echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list \
&& sudo apt update && sudo apt install --no-install-recommends yarn \
&& mkdir -p ~/dev/git/ \
&& rm -rf ~/dev/git/arduino-editor \
&& git clone --depth 1 https://github.com/bcmi-labs/arduino-editor.git ~/dev/git/arduino-editor \
&& yarn --cwd ~/dev/git/arduino-editor \
&& yarn --cwd ~/dev/git/arduino-editor rebuild:electron \
&& yarn --cwd ~/dev/git/arduino-editor/electron-app start
&& rm -rf ~/dev/git/arduino-ide \
&& git clone --depth 1 https://github.com/arduino/arduino-ide.git ~/dev/git/arduino-ide \
&& yarn --cwd ~/dev/git/arduino-ide \
&& yarn --cwd ~/dev/git/arduino-ide rebuild:electron \
&& yarn --cwd ~/dev/git/arduino-ide/electron-app start
```

View File

@@ -1,31 +1,32 @@
{
"private": true,
"name": "electron-app",
"version": "2.0.0-rc2",
"version": "2.0.0-rc4",
"license": "AGPL-3.0-or-later",
"main": "src-gen/frontend/electron-main.js",
"dependencies": {
"@theia/core": "1.19.0",
"@theia/debug": "1.19.0",
"@theia/editor": "1.19.0",
"@theia/editor-preview": "1.19.0",
"@theia/electron": "1.19.0",
"@theia/file-search": "1.19.0",
"@theia/filesystem": "1.19.0",
"@theia/keymaps": "1.19.0",
"@theia/messages": "1.19.0",
"@theia/monaco": "1.19.0",
"@theia/navigator": "1.19.0",
"@theia/plugin-ext": "1.19.0",
"@theia/plugin-ext-vscode": "1.19.0",
"@theia/preferences": "1.19.0",
"@theia/process": "1.19.0",
"@theia/terminal": "1.19.0",
"@theia/workspace": "1.19.0",
"arduino-ide-extension": "2.0.0-rc2"
"@theia/core": "1.22.1",
"@theia/debug": "1.22.1",
"@theia/editor": "1.22.1",
"@theia/editor-preview": "1.22.1",
"@theia/electron": "1.22.1",
"@theia/file-search": "1.22.1",
"@theia/filesystem": "1.22.1",
"@theia/keymaps": "1.22.1",
"@theia/messages": "1.22.1",
"@theia/monaco": "1.22.1",
"@theia/navigator": "1.22.1",
"@theia/plugin-ext": "1.22.1",
"@theia/plugin-ext-vscode": "1.22.1",
"@theia/preferences": "1.22.1",
"@theia/process": "1.22.1",
"@theia/terminal": "1.22.1",
"@theia/workspace": "1.22.1",
"arduino-ide-extension": "2.0.0-rc4"
},
"devDependencies": {
"@theia/cli": "1.19.0"
"@theia/cli": "1.22.1",
"electron": "^15.3.5"
},
"scripts": {
"prepare": "theia build --mode development",
@@ -63,4 +64,4 @@
}
}
}
}
}

View File

@@ -2,27 +2,31 @@ const isCI = require('is-ci');
const { notarize } = require('electron-notarize');
exports.default = async function notarizing(context) {
if (!isCI) {
console.log('Skipping notarization: not on CI.');
return;
}
if (process.env.IS_FORK === 'true') {
console.log('Skipping the app notarization: building from a fork.');
return;
}
const { electronPlatformName, appOutDir } = context;
if (electronPlatformName !== 'darwin') {
return;
}
if (!isCI) {
console.log('Skipping notarization: not on CI.');
return;
}
if (process.env.IS_FORK === 'true') {
console.log('Skipping the app notarization: building from a fork.');
return;
}
const { electronPlatformName, appOutDir } = context;
if (electronPlatformName !== 'darwin') {
return;
}
const appName = context.packager.appInfo.productFilename;
const appBundleId = context.packager.config.appId;
console.log(`>>> Notarizing ${appBundleId} at ${appOutDir}/${appName}.app...`);
const appName = context.packager.appInfo.productFilename;
const appBundleId = context.packager.config.appId;
console.log(
`>>> Notarizing ${appBundleId} at ${appOutDir}/${appName}.app...`
);
return await notarize({
appBundleId,
appPath: `${appOutDir}/${appName}.app`,
appleId: process.env.AC_USERNAME,
appleIdPassword: process.env.AC_PASSWORD,
});
await notarize({
appBundleId,
appPath: `${appOutDir}/${appName}.app`,
appleId: process.env.AC_USERNAME,
appleIdPassword: process.env.AC_PASSWORD,
teamId: process.env.AC_TEAM_ID,
tool: 'notarytool',
});
};

View File

@@ -3,16 +3,18 @@
"author": "Arduino SA",
"resolutions": {
"**/fs-extra": "^4.0.3",
"electron-builder": "22.7.0"
"electron-builder": "22.10.5",
"find-git-exec": "0.0.4",
"dugite-extra": "0.1.15"
},
"dependencies": {
"node-log-rotate": "^0.1.5"
},
"devDependencies": {
"@theia/cli": "1.19.0",
"@theia/cli": "1.22.1",
"cross-env": "^7.0.2",
"electron-builder": "22.7.0",
"electron-notarize": "^0.3.0",
"electron-builder": "22.10.5",
"electron-notarize": "^1.1.1",
"is-ci": "^2.0.0",
"ncp": "^2.0.0",
"shelljs": "^0.8.3"
@@ -20,13 +22,14 @@
"scripts": {
"build": "yarn download:plugins && theia build --mode development && yarn patch",
"build:publish": "yarn download:plugins && theia build --mode production && yarn patch",
"rebuild": "yarn theia rebuild:electron",
"package": "cross-env DEBUG=* && electron-builder --publish=never",
"package:publish": "cross-env DEBUG=* && electron-builder --publish=always",
"download:plugins": "theia download:plugins",
"patch": "ncp ./patch/main.js ./src-gen/backend/main.js"
},
"engines": {
"node": ">=12.14.1 <13"
"node": ">=14.0.0 <15"
},
"repository": {
"type": "git",
@@ -51,9 +54,18 @@
"build": {
"productName": "Arduino IDE",
"asar": false,
"detectUpdateChannel": false,
"generateUpdatesFilesForAllChannels": true,
"npmRebuild": false,
"directories": {
"buildResources": "resources"
},
"fileAssociations": [
{
"ext": "ino",
"role": "Editor"
}
],
"files": [
"src-gen",
"lib",
@@ -65,8 +77,7 @@
"!node_modules/@theia/**/lib/*browser/*",
"node_modules/@theia/core/lib/browser/*",
"!node_modules/@typefox/monaco-editor-core/*",
"!node_modules/oniguruma/*",
"!node_modules/onigasm/*"
"!node_modules/electron/**"
],
"extraResources": [
{
@@ -86,16 +97,12 @@
"hardenedRuntime": true,
"gatekeeperAssess": false,
"entitlements": "resources/entitlements.mac.plist",
"entitlementsInherit": "resources/entitlements.mac.plist",
"target": [
"dmg"
]
"entitlementsInherit": "resources/entitlements.mac.plist"
},
"linux": {
"target": [
{
"target": "zip"
}
"zip",
"AppImage"
],
"category": "Development",
"icon": "resources/icons"
@@ -139,9 +146,9 @@
"theiaPluginsDir": "plugins",
"theiaPlugins": {
"vscode-builtin-cpp": "https://open-vsx.org/api/vscode/cpp/1.52.1/file/vscode.cpp-1.52.1.vsix",
"vscode-arduino-tools": "https://downloads.arduino.cc/vscode-arduino-tools/vscode-arduino-tools-0.0.2-beta.1.vsix",
"vscode-arduino-tools": "https://downloads.arduino.cc/vscode-arduino-tools/vscode-arduino-tools-0.0.2-beta.2.vsix",
"vscode-builtin-json": "https://open-vsx.org/api/vscode/json/1.46.1/file/vscode.json-1.46.1.vsix",
"vscode-builtin-json-language-features": "https://open-vsx.org/api/vscode/json-language-features/1.46.1/file/vscode.json-language-features-1.46.1.vsix",
"cortex-debug": "https://open-vsx.org/api/marus25/cortex-debug/0.3.10/file/marus25.cortex-debug-0.3.10.vsix"
}
}
}

View File

@@ -8,87 +8,106 @@ const dateFormat = require('dateformat');
const { isNightly, isRelease, git } = require('./utils');
function artifactName() {
const { platform, arch } = process;
const id = (() => {
if (isRelease) {
return getVersion();
} else if (isNightly) {
return `nightly-${timestamp()}`
} else {
return getVersion();
}
})();
const name = 'arduino-ide';
switch (platform) {
case 'win32': {
if (arch === 'x64') {
return `${name}_${id}_Windows_64bit.\$\{ext}`;
}
throw new Error(`Unsupported platform, arch: ${platform}, ${arch}`);
}
case 'darwin': {
return `${name}_${id}_macOS_64bit.\$\{ext}`;
}
case 'linux': {
switch (arch) {
case 'arm': {
return `${name}_${id}_Linux_ARMv7.\$\{ext}`;
}
case 'arm64': {
return `${name}_${id}_Linux_ARM64.\$\{ext}`;
}
case 'x64': {
return `${name}_${id}_Linux_64bit.\$\{ext}`;
}
default: {
throw new Error(`Unsupported platform, arch: ${platform}, ${arch}`);
}
}
}
default: throw new Error(`Unsupported platform, arch: ${platform}, ${arch}`);
const { platform, arch } = process;
const id = (() => {
if (isRelease) {
return getVersion();
} else if (isNightly) {
return `nightly-${timestamp()}`;
} else {
return getVersion();
}
})();
const name = 'arduino-ide';
switch (platform) {
case 'win32': {
if (arch === 'x64') {
return `${name}_${id}_Windows_64bit.\$\{ext}`;
}
throw new Error(`Unsupported platform, arch: ${platform}, ${arch}`);
}
case 'darwin': {
return `${name}_${id}_macOS_64bit.\$\{ext}`;
}
case 'linux': {
switch (arch) {
case 'arm': {
return `${name}_${id}_Linux_ARMv7.\$\{ext}`;
}
case 'arm64': {
return `${name}_${id}_Linux_ARM64.\$\{ext}`;
}
case 'x64': {
return `${name}_${id}_Linux_64bit.\$\{ext}`;
}
default: {
throw new Error(`Unsupported platform, arch: ${platform}, ${arch}`);
}
}
}
default:
throw new Error(`Unsupported platform, arch: ${platform}, ${arch}`);
}
}
function electronPlatform() {
switch (process.platform) {
case 'win32': {
return 'win';
}
case 'darwin': {
return 'mac';
}
case 'linux': {
return 'linux';
}
default: throw new Error(`Unsupported platform: ${process.platform}.`);
switch (process.platform) {
case 'win32': {
return 'win';
}
case 'darwin': {
return 'mac';
}
case 'linux': {
return 'linux';
}
default:
throw new Error(`Unsupported platform: ${process.platform}.`);
}
}
function getVersion() {
const repositoryRootPath = git('rev-parse --show-toplevel');
let version = JSON.parse(fs.readFileSync(path.join(repositoryRootPath, 'package.json'), { encoding: 'utf8' })).version;
const repositoryRootPath = git('rev-parse --show-toplevel');
let version = JSON.parse(
fs.readFileSync(path.join(repositoryRootPath, 'package.json'), {
encoding: 'utf8',
})
).version;
if (!semver.valid(version)) {
throw new Error(
`Could not read version from root package.json. Version was: '${version}'.`
);
}
if (!isRelease) {
if (isNightly) {
version = `${version}-nightly-${timestamp()}`;
} else {
version = `${version}-snapshot-${currentCommitish()}`;
}
if (!semver.valid(version)) {
throw new Error(`Could not read version from root package.json. Version was: '${version}'.`);
throw new Error(`Invalid patched version: '${version}'.`);
}
if (!isRelease) {
if (isNightly) {
version = `${version}-nightly.${timestamp()}`;
} else {
version = `${version}-snapshot.${currentCommitish()}`;
}
if (!semver.valid(version)) {
throw new Error(`Invalid patched version: '${version}'.`);
}
}
return version;
}
return version;
}
function getChannel() {
if (isRelease) {
return 'stable';
}
if (isNightly) {
return 'nightly';
}
return '';
}
function timestamp() {
return dateFormat(new Date(), 'yyyymmdd');
return dateFormat(new Date(), 'yyyymmdd');
}
function currentCommitish() {
return git('rev-parse --short HEAD');
return git('rev-parse --short HEAD');
}
// function currentBranch() {
@@ -96,28 +115,38 @@ function currentCommitish() {
// }
function generateTemplate(buildDate) {
// do `export PUBLISH=true yarn package` if you want to mimic CI build locally.
// const electronPublish = release || (isCI && currentBranch() === 'main') || process.env.PUBLISH === 'true';
const version = getVersion();
const productName = 'Arduino IDE';
const name = 'arduino-ide';
let customizations = {
name,
description: productName,
version,
build: {
productName,
appId: 'arduino.ProIDE',
[electronPlatform()]: {
artifactName: artifactName()
}
}
};
if (buildDate) {
customizations = merge(customizations, { theia: { frontend: { config: { buildDate } } } });
}
const template = require('../build/template-package.json');
return merge(template, customizations);
// do `export PUBLISH=true yarn package` if you want to mimic CI build locally.
// const electronPublish = release || (isCI && currentBranch() === 'main') || process.env.PUBLISH === 'true';
const version = getVersion();
const productName = 'Arduino IDE';
const name = 'arduino-ide';
const updateChannel = getChannel();
let customizations = {
name,
description: productName,
version,
theia: {
frontend: {
config: {
'arduino.ide.updateChannel': updateChannel,
},
},
},
build: {
productName,
appId: 'cc.arduino.IDE2',
[electronPlatform()]: {
artifactName: artifactName(),
},
},
};
if (buildDate) {
customizations = merge(customizations, {
theia: { frontend: { config: { buildDate } } },
});
}
const template = require('../build/template-package.json');
return merge(template, customizations);
}
module.exports = { generateTemplate };

View File

@@ -1,339 +1,504 @@
//@ts-check
(async () => {
const fs = require('fs');
const join = require('path').join;
const shell = require('shelljs');
const glob = require('glob');
const isCI = require('is-ci');
shell.env.THEIA_ELECTRON_SKIP_REPLACE_FFMPEG = '1'; // Do not run the ffmpeg validation for the packager.
shell.env.NODE_OPTIONS = '--max_old_space_size=4096'; // Increase heap size for the CI
shell.env.PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = 'true'; // Skip download and avoid `ERROR: Failed to download Chromium`.
const template = require('./config').generateTemplate(
new Date().toISOString()
);
const utils = require('./utils');
const merge = require('deepmerge');
const { isRelease, isElectronPublish, getChannelFile } = utils;
const { version } = template;
const { productName } = template.build;
const fs = require('fs');
const join = require('path').join;
const shell = require('shelljs');
const glob = require('glob');
const isCI = require('is-ci');
shell.env.THEIA_ELECTRON_SKIP_REPLACE_FFMPEG = '1'; // Do not run the ffmpeg validation for the packager.
shell.env.NODE_OPTIONS = '--max_old_space_size=4096'; // Increase heap size for the CI
shell.env.PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = 'true'; // Skip download and avoid `ERROR: Failed to download Chromium`.
const template = require('./config').generateTemplate(new Date().toISOString());
const utils = require('./utils');
const merge = require('deepmerge');
const { isRelease, isElectronPublish } = utils;
const { version } = template;
const { productName } = template.build;
echo(`📦 Building ${isRelease ? 'release ' : ''}version '${version}'...`);
echo(`📦 Building ${isRelease ? 'release ' : ''}version '${version}'...`);
const workingCopy = 'working-copy';
const workingCopy = 'working-copy';
/**
* Relative path from the `__dirname` to the root where the `arduino-ide-extension` and the `electron-app` folders are.
* This could come handy when moving the location of the `electron/packager`.
*/
const rootPath = join('..', '..');
/**
* Relative path from the `__dirname` to the root where the `arduino-ide-extension` and the `electron-app` folders are.
* This could come handy when moving the location of the `electron/packager`.
*/
const rootPath = join('..', '..');
// This is a HACK! We rename the root `node_modules` to something else. Otherwise, due to the hoisting,
// multiple Theia extensions will be picked up.
if (fs.existsSync(path(rootPath, 'node_modules'))) {
// We either do this or change the project structure.
echo(
"🔧 >>> [Hack] Renaming the root 'node_modules' folder to '.node_modules'..."
);
mv('-f', path(rootPath, 'node_modules'), path(rootPath, '.node_modules'));
echo(
"👌 <<< [Hack] Renamed the root 'node_modules' folder to '.node_modules'."
);
}
// This is a HACK! We rename the root `node_modules` to something else. Otherwise, due to the hoisting,
// multiple Theia extensions will be picked up.
if (fs.existsSync(path(rootPath, 'node_modules'))) {
// We either do this or change the project structure.
echo('🔧 >>> [Hack] Renaming the root \'node_modules\' folder to \'.node_modules\'...');
mv('-f', path(rootPath, 'node_modules'), path(rootPath, '.node_modules'));
echo('👌 <<< [Hack] Renamed the root \'node_modules\' folder to \'.node_modules\'.')
}
//---------------------------+
// Clean the previous state. |
//---------------------------+
// rm -rf ../working-copy
rm('-rf', path('..', workingCopy));
// Clean up the `./electron/build` folder.
const resourcesToKeep = [
'patch',
'resources',
'scripts',
'template-package.json',
];
fs.readdirSync(path('..', 'build'))
.filter((filename) => resourcesToKeep.indexOf(filename) === -1)
.forEach((filename) => rm('-rf', path('..', 'build', filename)));
//---------------------------+
// Clean the previous state. |
//---------------------------+
// rm -rf ../working-copy
rm('-rf', path('..', workingCopy));
// Clean up the `./electron/build` folder.
const resourcesToKeep = ['patch', 'resources', 'scripts', 'template-package.json'];
for (const filename of fs.readdirSync(path('..', 'build')).filter(filename => resourcesToKeep.indexOf(filename) === -1)) {
rm('-rf', path('..', 'build', filename));
}
const extensions = require('./extensions.json');
echo(
`Building the application with the following extensions:\n${extensions
.map((ext) => ` - ${ext}`)
.join(',\n')}`
);
const allDependencies = [...extensions, 'electron-app'];
const extensions = require('./extensions.json');
echo(`Building the application with the following extensions:\n${extensions.map(ext => ` - ${ext}`).join(',\n')}`);
const allDependencies = [
...extensions,
'electron-app'
]
//----------------------------------------------------------------------------------------------+
// Copy the following items into the `working-copy` folder. Make sure to reuse the `yarn.lock`. |
//----------------------------------------------------------------------------------------------+
mkdir('-p', path('..', workingCopy));
for (const name of [
...allDependencies,
'yarn.lock',
'package.json',
'lerna.json',
]) {
cp('-rf', path(rootPath, name), path('..', workingCopy));
}
//----------------------------------------------------------------------------------------------+
// Copy the following items into the `working-copy` folder. Make sure to reuse the `yarn.lock`. |
//----------------------------------------------------------------------------------------------+
mkdir('-p', path('..', workingCopy));
for (const name of [...allDependencies, 'yarn.lock', 'package.json', 'lerna.json']) {
cp('-rf', path(rootPath, name), path('..', workingCopy));
}
//----------------------------------------------+
// Sanity check: all versions must be the same. |
//----------------------------------------------+
verifyVersions(allDependencies);
//----------------------------------------------------------------------+
// Use the nightly patch version if not a release but requires publish. |
//----------------------------------------------------------------------+
if (!isRelease) {
for (const dependency of allDependencies) {
const pkg = require(`../working-copy/${dependency}/package.json`);
pkg.version = version;
for (const dependency in pkg.dependencies) {
if (allDependencies.indexOf(dependency) !== -1) {
pkg.dependencies[dependency] = version;
}
}
fs.writeFileSync(path('..', workingCopy, dependency, 'package.json'), JSON.stringify(pkg, null, 2));
//----------------------------------------------+
// Sanity check: all versions must be the same. |
//----------------------------------------------+
verifyVersions(allDependencies);
//----------------------------------------------------------------------+
// Use the nightly patch version if not a release but requires publish. |
//----------------------------------------------------------------------+
if (!isRelease) {
for (const dependency of allDependencies) {
const pkg = require(`../working-copy/${dependency}/package.json`);
pkg.version = version;
for (const dependency in pkg.dependencies) {
if (allDependencies.indexOf(dependency) !== -1) {
pkg.dependencies[dependency] = version;
}
}
fs.writeFileSync(
path('..', workingCopy, dependency, 'package.json'),
JSON.stringify(pkg, null, 2)
);
}
verifyVersions(allDependencies);
}
verifyVersions(allDependencies);
//-------------------------------------------------------------+
// Save some time: no need to build the `browser-app` example. |
//-------------------------------------------------------------+
//@ts-ignore
let pkg = require('../working-copy/package.json');
const workspaces = pkg.workspaces;
// We cannot remove the `electron-app`. Otherwise, there is not way to collect the unused dependencies.
const dependenciesToRemove = ['browser-app'];
for (const dependencyToRemove of dependenciesToRemove) {
const index = workspaces.indexOf(dependencyToRemove);
if (index !== -1) {
workspaces.splice(index, 1);
}
//-------------------------------------------------------------+
// Save some time: no need to build the `browser-app` example. |
//-------------------------------------------------------------+
//@ts-ignore
let pkg = require('../working-copy/package.json');
const workspaces = pkg.workspaces;
// We cannot remove the `electron-app`. Otherwise, there is not way to collect the unused dependencies.
const dependenciesToRemove = ['browser-app'];
for (const dependencyToRemove of dependenciesToRemove) {
const index = workspaces.indexOf(dependencyToRemove);
if (index !== -1) {
workspaces.splice(index, 1);
}
pkg.workspaces = workspaces;
fs.writeFileSync(path('..', workingCopy, 'package.json'), JSON.stringify(pkg, null, 2));
}
pkg.workspaces = workspaces;
fs.writeFileSync(
path('..', workingCopy, 'package.json'),
JSON.stringify(pkg, null, 2)
);
//-------------------------------------------------------------------------------------------------+
// Rebuild the extension with the copied `yarn.lock`. It is a must to use the same Theia versions. |
//-------------------------------------------------------------------------------------------------+
exec(`yarn --network-timeout 1000000 --cwd ${path('..', workingCopy)}`, `Building the ${productName} application`);
//-------------------------------------------------------------------------------------------------+
// Rebuild the extension with the copied `yarn.lock`. It is a must to use the same Theia versions. |
//-------------------------------------------------------------------------------------------------+
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', workingCopy)}`,
`Building the ${productName} application`
);
//-------------------------------------------------------------------------------------------------------------------------+
// Test the application. With this approach, we cannot publish test results to GH Actions but save 6-10 minutes per builds |
//-------------------------------------------------------------------------------------------------------------------------+
exec(`yarn --network-timeout 1000000 --cwd ${path('..', workingCopy)} test`, `Testing the ${productName} application`);
//-------------------------------------------------------------------------------------------------------------------------+
// Test the application. With this approach, we cannot publish test results to GH Actions but save 6-10 minutes per builds |
//-------------------------------------------------------------------------------------------------------------------------+
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', workingCopy)} test`,
`Testing the ${productName} application`
);
// Collect all unused dependencies by the backend. We have to remove them from the electron app.
// The `bundle.js` already contains everything we need for the frontend.
// We have to do it before changing the dependencies to `local-path`.
const unusedDependencies = await utils.collectUnusedDependencies('../working-copy/electron-app/');
// Collect all unused dependencies by the backend. We have to remove them from the electron app.
// The `bundle.js` already contains everything we need for the frontend.
// We have to do it before changing the dependencies to `local-path`.
const unusedDependencies = await utils.collectUnusedDependencies(
'../working-copy/electron-app/'
);
//-------------------------------------------------------------------------------------------------------------+
// Change the regular NPM dependencies to `local-paths`, so that we can build them without any NPM registries. |
//-------------------------------------------------------------------------------------------------------------+
for (const extension of extensions) {
if (extension !== 'arduino-ide-extension') { // Do not unlink self.
// @ts-ignore
pkg = require(`../working-copy/${extension}/package.json`);
// @ts-ignore
pkg.dependencies['arduino-ide-extension'] = 'file:../arduino-ide-extension';
fs.writeFileSync(path('..', workingCopy, extension, 'package.json'), JSON.stringify(pkg, null, 2));
}
//-------------------------------------------------------------------------------------------------------------+
// Change the regular NPM dependencies to `local-paths`, so that we can build them without any NPM registries. |
//-------------------------------------------------------------------------------------------------------------+
for (const extension of extensions) {
if (extension !== 'arduino-ide-extension') {
// Do not unlink self.
// @ts-ignore
pkg = require(`../working-copy/${extension}/package.json`);
// @ts-ignore
pkg.dependencies['arduino-ide-extension'] =
'file:../arduino-ide-extension';
fs.writeFileSync(
path('..', workingCopy, extension, 'package.json'),
JSON.stringify(pkg, null, 2)
);
}
}
//------------------------------------------------------------------------------------+
// Merge the `working-copy/package.json` with `electron/build/template-package.json`. |
//------------------------------------------------------------------------------------+
//------------------------------------------------------------------------------------+
// Merge the `working-copy/package.json` with `electron/build/template-package.json`. |
//------------------------------------------------------------------------------------+
// @ts-ignore
pkg = require('../working-copy/electron-app/package.json');
template.build.files = [
...template.build.files,
...unusedDependencies.map((name) => `!node_modules/${name}`),
];
const dependencies = {};
for (const extension of extensions) {
dependencies[extension] = `file:../working-copy/${extension}`;
}
// @ts-ignore
pkg.dependencies = { ...pkg.dependencies, ...dependencies };
pkg.devDependencies = { ...pkg.devDependencies, ...template.devDependencies };
// Deep-merging the Theia application configuration. We enable the electron window reload in dev mode but not for the final product. (arduino/arduino-pro-ide#187)
// @ts-ignore
const theia = merge(pkg.theia || {}, template.theia || {});
const content = {
...pkg,
...template,
theia,
// @ts-ignore
pkg = require('../working-copy/electron-app/package.json');
template.build.files = [...template.build.files, ...unusedDependencies.map(name => `!node_modules/${name}`)];
dependencies: pkg.dependencies,
devDependencies: pkg.devDependencies,
};
const overwriteMerge = (destinationArray, sourceArray, options) =>
sourceArray;
fs.writeFileSync(
path('..', 'build', 'package.json'),
JSON.stringify(
merge(content, template, { arrayMerge: overwriteMerge }),
null,
2
)
);
const dependencies = {};
for (const extension of extensions) {
dependencies[extension] = `file:../working-copy/${extension}`;
}
// @ts-ignore
pkg.dependencies = { ...pkg.dependencies, ...dependencies };
pkg.devDependencies = { ...pkg.devDependencies, ...template.devDependencies };
// Deep-merging the Theia application configuration. We enable the electron window reload in dev mode but not for the final product. (arduino/arduino-pro-ide#187)
// @ts-ignore
const theia = merge((pkg.theia || {}), (template.theia || {}));
const content = {
...pkg,
...template,
theia,
// @ts-ignore
dependencies: pkg.dependencies,
devDependencies: pkg.devDependencies
};
const overwriteMerge = (destinationArray, sourceArray, options) => sourceArray;
fs.writeFileSync(path('..', 'build', 'package.json'), JSON.stringify(merge(content, template, { arrayMerge: overwriteMerge }), null, 2));
echo(`📜 Effective 'package.json' for the ${productName} application is:
echo(`📜 Effective 'package.json' for the ${productName} application is:
-----------------------
${fs.readFileSync(path('..', 'build', 'package.json')).toString()}
-----------------------
`);
// Make sure the original `yarn.lock` file is used from the electron application.
if (fs.existsSync(path('..', 'build', 'yarn.lock'))) {
echo(`${path('..', 'build', 'yarn.lock')} must not exist.`);
// Make sure the original `yarn.lock` file is used from the electron application.
if (fs.existsSync(path('..', 'build', 'yarn.lock'))) {
echo(`${path('..', 'build', 'yarn.lock')} must not exist.`);
shell.exit(1);
}
cp('-rf', path(rootPath, 'yarn.lock'), path('..', 'build'));
if (!fs.existsSync(path('..', 'build', 'yarn.lock'))) {
echo(`${path('..', 'build', 'yarn.lock')} does not exist.`);
shell.exit(1);
}
//-------------------------------------------------------------------------------------------+
// Install all private and public dependencies for the electron application and build Theia. |
//-------------------------------------------------------------------------------------------+
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', 'build')}`,
'Installing dependencies'
);
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} build${isElectronPublish ? ':publish' : ''
}`,
`Building the ${productName} application`
);
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} rebuild`,
'Rebuild native dependencies'
);
//------------------------------------------------------------------------------+
// Create a throw away dotenv file which we use to feed the builder with input. |
//------------------------------------------------------------------------------+
const dotenv = 'electron-builder.env';
if (fs.existsSync(path('..', 'build', dotenv))) {
rm('-rf', path('..', 'build', dotenv));
}
// For the releases we use the desired tag as is defined by `$(Release.Tag)` from Azure.
// For the preview builds we use the version from the `electron/build/package.json` with the short commit hash.
fs.writeFileSync(path('..', 'build', dotenv), `ARDUINO_VERSION=${version}`);
//-----------------------------------+
// Package the electron application. |
//-----------------------------------+
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} package`,
`Packaging your ${productName} application`
);
//-----------------------------------------------------------------------------------------------------+
// Recalculate artifacts hash and copy to another folder (because they can change after signing them).
// Azure does not support wildcard for `PublishBuildArtifacts@1.pathToPublish` |
//-----------------------------------------------------------------------------------------------------+
if (isCI) {
try {
await recalculateArtifactsHash();
await copyFilesToBuildArtifacts();
} catch (e) {
echo(JSON.stringify(e));
shell.exit(1);
}
}
echo(`🎉 Success. Your application is at: ${path('..', 'build', 'dist')}`);
restore();
//--------+
// Utils. |
//--------+
function exec(command, toEcho) {
if (toEcho) {
echo(`⏱️ >>> ${toEcho}...`);
}
const { code, stderr, stdout } = shell.exec(command);
if (code !== 0) {
echo(`🔥 Error when executing ${command} => ${stderr}`);
shell.exit(1);
}
if (toEcho) {
echo(`👌 <<< ${toEcho}.`);
}
return stdout;
}
function cp(options, source, destination) {
shell.cp(options, source, destination);
assertNoError();
}
function rm(options, ...files) {
shell.rm(options, files);
assertNoError();
}
function mv(options, source, destination) {
shell.mv(options, source, destination);
assertNoError();
}
function mkdir(options, ...dir) {
shell.mkdir(options, dir);
assertNoError();
}
function echo(command) {
return shell.echo(command);
}
function assertNoError() {
const error = shell.error();
if (error) {
echo(error);
restore();
shell.exit(1);
}
}
function restore() {
if (fs.existsSync(path(rootPath, '.node_modules'))) {
echo(
"🔧 >>> [Restore] Renaming the root '.node_modules' folder to 'node_modules'..."
);
mv('-f', path(rootPath, '.node_modules'), path(rootPath, 'node_modules'));
echo(
"👌 >>> [Restore] Renamed the root '.node_modules' folder to 'node_modules'."
);
}
}
async function copyFilesToBuildArtifacts() {
echo(`🚢 Detected CI, moving build artifacts...`);
const { platform } = process;
const cwd = path('..', 'build', 'dist');
const targetFolder = path('..', 'build', 'dist', 'build-artifacts');
mkdir('-p', targetFolder);
const filesToCopy = [];
const channelFile = getChannelFile(platform);
// Channel file might be an empty string if we're not building a
// nightly or a full release. This can happen when building a package
// locally or a tester build when creating a new PR on GH.
if (!!channelFile && fs.existsSync(join(cwd, channelFile))) {
const channelFilePath = join(cwd, channelFile);
const newChannelFilePath = channelFilePath
?.replace('latest', 'stable')
?.replace('beta', 'nightly');
echo(`🔨 >>> Renaming ${channelFilePath} to ${newChannelFilePath}.`);
cp('-f', channelFilePath, newChannelFilePath);
filesToCopy.push(newChannelFilePath);
}
switch (platform) {
case 'linux': {
filesToCopy.push(
...glob
.sync('**/arduino-ide*.{zip,AppImage}', { cwd })
.map((p) => join(cwd, p))
);
break;
}
case 'win32': {
filesToCopy.push(
...glob
.sync('**/arduino-ide*.{exe,msi,zip}', { cwd })
.map((p) => join(cwd, p))
);
break;
}
case 'darwin': {
filesToCopy.push(
...glob
.sync('**/arduino-ide*.{dmg,zip}', { cwd })
.map((p) => join(cwd, p))
);
break;
}
default: {
echo(`Unsupported platform: ${platform}.`);
shell.exit(1);
}
}
cp('-rf', path(rootPath, 'yarn.lock'), path('..', 'build'));
if (!fs.existsSync(path('..', 'build', 'yarn.lock'))) {
echo(`${path('..', 'build', 'yarn.lock')} does not exist.`);
if (!filesToCopy.length) {
echo(`Could not collect any build artifacts from ${cwd}.`);
shell.exit(1);
}
for (const fileToCopy of filesToCopy) {
echo(`🚢 >>> Copying ${fileToCopy} to ${targetFolder}.`);
const isZip = await utils.isZip(fileToCopy);
if (isZip && platform === 'linux') {
await utils.adjustArchiveStructure(fileToCopy, targetFolder);
} else {
cp('-rf', fileToCopy, targetFolder);
}
echo(`👌 >>> Copied ${fileToCopy} to ${targetFolder}.`);
}
}
async function recalculateArtifactsHash() {
echo(`🚢 Detected CI, recalculating artifacts hash...`);
const { platform } = process;
const cwd = path('..', 'build', 'dist');
const channelFilePath = join(cwd, getChannelFile(platform));
const yaml = require('yaml');
try {
let fileContents = fs.readFileSync(channelFilePath, 'utf8');
const newChannelFile = yaml.parse(fileContents);
const { files, path } = newChannelFile;
const newSha512 = await hashFile(join(cwd, path));
newChannelFile.sha512 = newSha512;
if (!!files) {
const newFiles = [];
for (let file of files) {
const { url } = file;
const { size } = fs.statSync(join(cwd, url));
const newSha512 = await hashFile(join(cwd, url));
if (!newFiles.find((f) => f.sha512 === newSha512)) {
newFiles.push({ ...file, sha512: newSha512, size });
}
}
newChannelFile.files = newFiles;
}
const newChannelFileRaw = yaml.stringify(newChannelFile);
fs.writeFileSync(channelFilePath, newChannelFileRaw);
echo(`👌 >>> Channel file updated successfully. New channel file:`);
echo(newChannelFileRaw);
} catch (e) {
console.log(e);
}
}
async function hashFile(
file,
algorithm = 'sha512',
encoding = 'base64',
options
) {
const crypto = require('crypto');
return await new Promise((resolve, reject) => {
const hash = crypto.createHash(algorithm);
hash.on('error', reject).setEncoding(encoding);
fs.createReadStream(
file,
Object.assign({}, options, {
highWaterMark: 1024 * 1024,
/* better to use more memory but hash faster */
})
)
.on('error', reject)
.on('end', () => {
hash.end();
resolve(hash.read());
})
.pipe(hash, {
end: false,
});
});
}
/**
* Joins tha path from `__dirname`.
*/
function path(...paths) {
return join(__dirname, ...paths);
}
function verifyVersions(allDependencies, expectedVersion) {
const versions = new Set();
for (const dependency of allDependencies) {
versions.add(
require(`../working-copy/${dependency}/package.json`).version
);
}
if (versions.size !== 1) {
echo(
`Mismatching version configuration. All dependencies must have the same version. Versions were: ${JSON.stringify(
Array.from(versions),
null,
2
)}.`
);
shell.exit(1);
process.exit(1);
}
if (expectedVersion) {
if (!versions.has(expectedVersion)) {
echo(
`Mismatching version configuration. Expected version was: '${expectedVersion}' actual was: '${Array.from(versions)[0]
}'.`
);
shell.exit(1);
process.exit(1);
}
}
//-------------------------------------------------------------------------------------------+
// Install all private and public dependencies for the electron application and build Theia. |
//-------------------------------------------------------------------------------------------+
exec(`yarn --network-timeout 1000000 --cwd ${path('..', 'build')}`, 'Installing dependencies');
exec(`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} build${isElectronPublish ? ':publish' : ''}`, `Building the ${productName} application`);
//------------------------------------------------------------------------------+
// Create a throw away dotenv file which we use to feed the builder with input. |
//------------------------------------------------------------------------------+
const dotenv = 'electron-builder.env';
if (fs.existsSync(path('..', 'build', dotenv))) {
rm('-rf', path('..', 'build', dotenv));
}
// For the releases we use the desired tag as is defined by `$(Release.Tag)` from Azure.
// For the preview builds we use the version from the `electron/build/package.json` with the short commit hash.
fs.writeFileSync(path('..', 'build', dotenv), `ARDUINO_VERSION=${version}`);
//-----------------------------------+
// Package the electron application. |
//-----------------------------------+
exec(`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} package`, `Packaging your ${productName} application`);
//-----------------------------------------------------------------------------------------------------+
// Copy to another folder. Azure does not support wildcard for `PublishBuildArtifacts@1.pathToPublish` |
//-----------------------------------------------------------------------------------------------------+
if (isCI) {
try {
await copyFilesToBuildArtifacts();
} catch (e) {
echo(JSON.stringify(e));
shell.exit(1);
}
}
echo(`🎉 Success. Your application is at: ${path('..', 'build', 'dist')}`);
restore();
//--------+
// Utils. |
//--------+
function exec(command, toEcho) {
if (toEcho) {
echo(`⏱️ >>> ${toEcho}...`);
}
const { code, stderr, stdout } = shell.exec(command);
if (code !== 0) {
echo(`🔥 Error when executing ${command} => ${stderr}`);
shell.exit(1);
}
if (toEcho) {
echo(`👌 <<< ${toEcho}.`);
}
return stdout;
}
function cp(options, source, destination) {
shell.cp(options, source, destination);
assertNoError();
}
function rm(options, ...files) {
shell.rm(options, files);
assertNoError();
}
function mv(options, source, destination) {
shell.mv(options, source, destination);
assertNoError();
}
function mkdir(options, ...dir) {
shell.mkdir(options, dir);
assertNoError();
}
function echo(command) {
return shell.echo(command);
}
function assertNoError() {
const error = shell.error();
if (error) {
echo(error);
restore();
shell.exit(1);
}
}
function restore() {
if (fs.existsSync(path(rootPath, '.node_modules'))) {
echo('🔧 >>> [Restore] Renaming the root \'.node_modules\' folder to \'node_modules\'...');
mv('-f', path(rootPath, '.node_modules'), path(rootPath, 'node_modules'));
echo('👌 >>> [Restore] Renamed the root \'.node_modules\' folder to \'node_modules\'.');
}
}
async function copyFilesToBuildArtifacts() {
echo(`🚢 Detected CI, moving build artifacts...`);
const { platform } = process;
const cwd = path('..', 'build', 'dist');
const targetFolder = path('..', 'build', 'dist', 'build-artifacts');
mkdir('-p', targetFolder);
const filesToCopy = [];
switch (platform) {
case 'linux': {
filesToCopy.push(...glob.sync('**/arduino-ide*.{zip,AppImage}', { cwd }).map(p => join(cwd, p)));
break;
}
case 'win32': {
filesToCopy.push(...glob.sync('**/arduino-ide*.{exe,msi,zip}', { cwd }).map(p => join(cwd, p)));
break;
}
case 'darwin': {
filesToCopy.push(...glob.sync('**/arduino-ide*.{dmg,zip}', { cwd }).map(p => join(cwd, p)));
break;
}
default: {
echo(`Unsupported platform: ${platform}.`);
shell.exit(1);
}
}
if (!filesToCopy.length) {
echo(`Could not collect any build artifacts from ${cwd}.`);
shell.exit(1);
}
for (const fileToCopy of filesToCopy) {
echo(`🚢 >>> Copying ${fileToCopy} to ${targetFolder}.`);
const isZip = await utils.isZip(fileToCopy);
if (isZip) {
await utils.adjustArchiveStructure(fileToCopy, targetFolder);
} else {
cp('-rf', fileToCopy, targetFolder);
}
echo(`👌 >>> Copied ${fileToCopy} to ${targetFolder}.`);
}
}
/**
* Joins tha path from `__dirname`.
*/
function path(...paths) {
return join(__dirname, ...paths);
}
function verifyVersions(allDependencies, expectedVersion) {
const versions = new Set();
for (const dependency of allDependencies) {
versions.add(require(`../working-copy/${dependency}/package.json`).version);
}
if (versions.size !== 1) {
echo(`Mismatching version configuration. All dependencies must have the same version. Versions were: ${JSON.stringify(Array.from(versions), null, 2)}.`);
shell.exit(1);
process.exit(1);
}
if (expectedVersion) {
if (!versions.has(expectedVersion)) {
echo(`Mismatching version configuration. Expected version was: '${expectedVersion}' actual was: '${Array.from(versions)[0]}'.`);
shell.exit(1);
process.exit(1);
}
}
}
}
})();

View File

@@ -13,10 +13,11 @@
"author": "Arduino SA",
"license": "AGPL-3.0-or-later",
"dependencies": {
"7zip-min": "^1.1.1",
"@types/file-type": "^10.9.1",
"@types/temp": "^0.8.32",
"7zip-min": "^1.1.1",
"chai": "^4.2.0",
"crypto": "^1.0.1",
"dateformat": "^3.0.3",
"deepmerge": "2.01",
"depcheck": "^0.9.2",
@@ -25,13 +26,14 @@
"is-ci": "^2.0.0",
"mocha": "^7.1.1",
"semver": "^7.3.2",
"sinon": "^9.0.1",
"shelljs": "^0.8.3",
"sinon": "^9.0.1",
"temp": "^0.9.1",
"yaml": "^1.10.2",
"yargs": "^12.0.5"
},
"engines": {
"node": ">=12.14.1 <13"
"node": ">=14.0.0 <15"
},
"mocha": {
"reporter": "spec",
@@ -39,4 +41,4 @@
"watch-extensions": "js",
"timeout": 10000
}
}
}

View File

@@ -8,112 +8,112 @@ const sinon = require('sinon');
describe('utils', () => {
describe('adjustArchiveStructure', () => {
describe('adjustArchiveStructure', () => {
let consoleStub;
beforeEach(() => {
consoleStub = sinon.stub(console, 'log').value(() => { });
});
afterEach(() => {
consoleStub.reset();
track.cleanupSync();
});
it('should reject when not a zip file', async () => {
try {
const invalid = path.join(__dirname, 'resources', 'not-a-zip.dmg');
await testMe.adjustArchiveStructure(invalid, track.mkdirSync());
throw new Error('Expected a rejection');
} catch (e) {
expect(e).to.be.an.instanceOf(Error);
expect(e.message).to.be.equal('Expected a ZIP file.');
}
});
it('should reject when target directory does not exist', async () => {
try {
const zip = path.join(__dirname, 'resources', 'zip-with-base-folder.zip');
await testMe.adjustArchiveStructure(zip, path.join(__dirname, 'some', 'missing', 'path'));
throw new Error('Expected a rejection');
} catch (e) {
expect(e).to.be.an.instanceOf(Error);
expect(e.message.endsWith('does not exist.')).to.be.true;
}
});
it('should reject when target is a file', async () => {
try {
const zip = path.join(__dirname, 'resources', 'zip-with-base-folder.zip');
await testMe.adjustArchiveStructure(zip, path.join(__filename));
throw new Error('Expected a rejection');
} catch (e) {
expect(e).to.be.an.instanceOf(Error);
expect(e.message.endsWith('is not a directory.')).to.be.true;
}
});
it('should be a NOOP when the zip already has the desired base folder', async () => {
const zip = path.join(__dirname, 'resources', 'zip-with-base-folder.zip');
const actual = await testMe.adjustArchiveStructure(zip, track.mkdirSync());
expect(actual).to.be.equal(zip);
});
it('should handle whitespace in file path gracefully', async () => {
const zip = path.join(__dirname, 'resources', 'zip with whitespace.zip');
const out = track.mkdirSync();
const actual = await testMe.adjustArchiveStructure(zip, out, true);
expect(actual).to.be.equal(path.join(out, 'zip with whitespace.zip'));
console.log(actual);
expect(fs.existsSync(actual)).to.be.true;
const verifyOut = track.mkdirSync();
await unpack(actual, verifyOut);
const root = path.join(verifyOut, 'zip with whitespace');
expect(fs.existsSync(root)).to.be.true;
expect(fs.lstatSync(root).isDirectory()).to.be.true;
const subs = fs.readdirSync(root);
expect(subs).to.have.lengthOf(3);
expect(subs.sort()).to.be.deep.equal(['a.txt', 'b.txt', 'foo']);
});
it('should keep the symlinks after ZIP adjustments', async function () {
if (process.platform === 'win32') {
this.skip();
}
const zip = path.join(__dirname, 'resources', 'zip-with-symlink.zip');
const out = track.mkdirSync();
const actual = await testMe.adjustArchiveStructure(zip, out, true);
expect(actual).to.be.equal(path.join(out, 'zip-with-symlink.zip'));
console.log(actual);
expect(fs.existsSync(actual)).to.be.true;
const verifyOut = track.mkdirSync();
await unpack(actual, verifyOut);
expect(fs.lstatSync(path.join(verifyOut, 'zip-with-symlink', 'folder', 'symlinked-sub')).isSymbolicLink()).to.be.true;
});
it('should adjust the archive structure if base folder is not present', async () => {
const zip = path.join(__dirname, 'resources', 'zip-without-symlink.zip');
const out = track.mkdirSync();
const actual = await testMe.adjustArchiveStructure(zip, out, true);
expect(actual).to.be.equal(path.join(out, 'zip-without-symlink.zip'));
console.log(actual);
expect(fs.existsSync(actual)).to.be.true;
const verifyOut = track.mkdirSync();
await unpack(actual, verifyOut);
const root = path.join(verifyOut, 'zip-without-symlink');
expect(fs.existsSync(root)).to.be.true;
expect(fs.lstatSync(root).isDirectory()).to.be.true;
const subs = fs.readdirSync(root);
expect(subs).to.have.lengthOf(3);
expect(subs.sort()).to.be.deep.equal(['a.txt', 'b.txt', 'foo']);
});
let consoleStub;
beforeEach(() => {
consoleStub = sinon.stub(console, 'log').value(() => { });
});
});
afterEach(() => {
consoleStub.reset();
track.cleanupSync();
});
it('should reject when not a zip file', async () => {
try {
const invalid = path.join(__dirname, 'resources', 'not-a-zip.dmg');
await testMe.adjustArchiveStructure(invalid, track.mkdirSync());
throw new Error('Expected a rejection');
} catch (e) {
expect(e).to.be.an.instanceOf(Error);
expect(e.message).to.be.equal('Expected a ZIP file.');
}
});
it('should reject when target directory does not exist', async () => {
try {
const zip = path.join(__dirname, 'resources', 'zip-with-base-folder.zip');
await testMe.adjustArchiveStructure(zip, path.join(__dirname, 'some', 'missing', 'path'));
throw new Error('Expected a rejection');
} catch (e) {
expect(e).to.be.an.instanceOf(Error);
expect(e.message.endsWith('does not exist.')).to.be.true;
}
});
it('should reject when target is a file', async () => {
try {
const zip = path.join(__dirname, 'resources', 'zip-with-base-folder.zip');
await testMe.adjustArchiveStructure(zip, path.join(__filename));
throw new Error('Expected a rejection');
} catch (e) {
expect(e).to.be.an.instanceOf(Error);
expect(e.message.endsWith('is not a directory.')).to.be.true;
}
});
it('should be a NOOP when the zip already has the desired base folder', async () => {
const zip = path.join(__dirname, 'resources', 'zip-with-base-folder.zip');
const actual = await testMe.adjustArchiveStructure(zip, track.mkdirSync());
expect(actual).to.be.equal(zip);
});
it('should handle whitespace in file path gracefully', async () => {
const zip = path.join(__dirname, 'resources', 'zip with whitespace.zip');
const out = track.mkdirSync();
const actual = await testMe.adjustArchiveStructure(zip, out, true);
expect(actual).to.be.equal(path.join(out, 'zip with whitespace.zip'));
console.log(actual);
expect(fs.existsSync(actual)).to.be.true;
const verifyOut = track.mkdirSync();
await unpack(actual, verifyOut);
const root = path.join(verifyOut, 'zip with whitespace');
expect(fs.existsSync(root)).to.be.true;
expect(fs.lstatSync(root).isDirectory()).to.be.true;
const subs = fs.readdirSync(root);
expect(subs).to.have.lengthOf(3);
expect(subs.sort()).to.be.deep.equal(['a.txt', 'b.txt', 'foo']);
});
it('should keep the symlinks after ZIP adjustments', async function () {
if (process.platform === 'win32') {
this.skip();
}
const zip = path.join(__dirname, 'resources', 'zip-with-symlink.zip');
const out = track.mkdirSync();
const actual = await testMe.adjustArchiveStructure(zip, out, true);
expect(actual).to.be.equal(path.join(out, 'zip-with-symlink.zip'));
console.log(actual);
expect(fs.existsSync(actual)).to.be.true;
const verifyOut = track.mkdirSync();
await unpack(actual, verifyOut);
expect(fs.lstatSync(path.join(verifyOut, 'zip-with-symlink', 'folder', 'symlinked-sub')).isSymbolicLink()).to.be.true;
});
it('should adjust the archive structure if base folder is not present', async () => {
const zip = path.join(__dirname, 'resources', 'zip-without-symlink.zip');
const out = track.mkdirSync();
const actual = await testMe.adjustArchiveStructure(zip, out, true);
expect(actual).to.be.equal(path.join(out, 'zip-without-symlink.zip'));
console.log(actual);
expect(fs.existsSync(actual)).to.be.true;
const verifyOut = track.mkdirSync();
await unpack(actual, verifyOut);
const root = path.join(verifyOut, 'zip-without-symlink');
expect(fs.existsSync(root)).to.be.true;
expect(fs.lstatSync(root).isDirectory()).to.be.true;
const subs = fs.readdirSync(root);
expect(subs).to.have.lengthOf(3);
expect(subs.sort()).to.be.deep.equal(['a.txt', 'b.txt', 'foo']);
});
});
});

View File

@@ -12,35 +12,42 @@ const fromFile = require('file-type').fromFile;
* Resolves to an array of `npm` package names that are declared in the `package.json` but **not** used by the project.
*/
function collectUnusedDependencies(pathToProject = process.cwd()) {
const p = path.isAbsolute(pathToProject) ? pathToProject : path.resolve(process.cwd(), pathToProject);
console.log(`⏱️ >>> Collecting unused backend dependencies for ${p}...`);
return new Promise(resolve => {
depcheck(p, {
ignoreDirs: [
'frontend'
],
parsers: {
'*.js': depcheck.parser.es6,
'*.jsx': depcheck.parser.jsx
},
detectors: [
depcheck.detector.requireCallExpression,
depcheck.detector.importDeclaration
],
specials: [
depcheck.special.eslint,
depcheck.special.webpack
]
}, unused => {
const { dependencies } = unused
if (dependencies && dependencies.length > 0) {
console.log(`👌 <<< The following unused dependencies have been found: ${JSON.stringify(dependencies, null, 2)}`);
} else {
console.log('👌 <<< No unused dependencies have been found.');
}
resolve(dependencies);
});
})
const p = path.isAbsolute(pathToProject)
? pathToProject
: path.resolve(process.cwd(), pathToProject);
console.log(`⏱️ >>> Collecting unused backend dependencies for ${p}...`);
return new Promise((resolve) => {
depcheck(
p,
{
ignoreDirs: ['frontend'],
parsers: {
'*.js': depcheck.parser.es6,
'*.jsx': depcheck.parser.jsx,
},
detectors: [
depcheck.detector.requireCallExpression,
depcheck.detector.importDeclaration,
],
specials: [depcheck.special.eslint, depcheck.special.webpack],
},
(unused) => {
const { dependencies } = unused;
if (dependencies && dependencies.length > 0) {
console.log(
`👌 <<< The following unused dependencies have been found: ${JSON.stringify(
dependencies,
null,
2
)}`
);
} else {
console.log('👌 <<< No unused dependencies have been found.');
}
resolve(dependencies);
}
);
});
}
/**
@@ -50,101 +57,111 @@ function collectUnusedDependencies(pathToProject = process.cwd()) {
* If `pathToZip` is not a ZIP, rejects. `targetFolderName` is the destination folder not the new archive location.
*/
function adjustArchiveStructure(pathToZip, targetFolderName, noCleanup) {
return new Promise(async (resolve, reject) => {
if (!await isZip(pathToZip)) {
reject(new Error(`Expected a ZIP file.`));
return;
}
if (!fs.existsSync(targetFolderName)) {
reject(new Error(`${targetFolderName} does not exist.`));
return;
}
if (!fs.lstatSync(targetFolderName).isDirectory()) {
reject(new Error(`${targetFolderName} is not a directory.`));
return;
}
console.log(`⏱️ >>> Adjusting ZIP structure ${pathToZip}...`);
return new Promise(async (resolve, reject) => {
if (!(await isZip(pathToZip))) {
reject(new Error(`Expected a ZIP file.`));
return;
}
if (!fs.existsSync(targetFolderName)) {
reject(new Error(`${targetFolderName} does not exist.`));
return;
}
if (!fs.lstatSync(targetFolderName).isDirectory()) {
reject(new Error(`${targetFolderName} is not a directory.`));
return;
}
console.log(`⏱️ >>> Adjusting ZIP structure ${pathToZip}...`);
const root = basename(pathToZip);
const resources = await list(pathToZip);
const hasBaseFolder = resources.find(name => name === root);
if (hasBaseFolder) {
if (resources.filter(name => name.indexOf(path.sep) === -1).length > 1) {
console.warn(`${pathToZip} ZIP has the desired root folder ${root}, however the ZIP contains other entries too: ${JSON.stringify(resources)}`);
}
console.log(`👌 <<< The ZIP already has the desired ${root} folder.`);
resolve(pathToZip);
return;
}
const root = basename(pathToZip);
const resources = await list(pathToZip);
const hasBaseFolder = resources.find((name) => name === root);
if (hasBaseFolder) {
if (
resources.filter((name) => name.indexOf(path.sep) === -1).length > 1
) {
console.warn(
`${pathToZip} ZIP has the desired root folder ${root}, however the ZIP contains other entries too: ${JSON.stringify(
resources
)}`
);
}
console.log(`👌 <<< The ZIP already has the desired ${root} folder.`);
resolve(pathToZip);
return;
}
const track = temp.track();
try {
const unzipOut = path.join(track.mkdirSync(), root);
fs.mkdirSync(unzipOut);
await unpack(pathToZip, unzipOut);
const adjustedZip = path.join(targetFolderName, path.basename(pathToZip));
await pack(unzipOut, adjustedZip);
console.log(`👌 <<< Adjusted the ZIP structure. Moved the modified ${basename(pathToZip)} to the ${targetFolderName} folder.`);
resolve(adjustedZip);
} finally {
if (!noCleanup) {
track.cleanupSync();
}
}
});
const track = temp.track();
try {
const unzipOut = path.join(track.mkdirSync(), root);
fs.mkdirSync(unzipOut);
await unpack(pathToZip, unzipOut);
const adjustedZip = path.join(targetFolderName, path.basename(pathToZip));
await pack(unzipOut, adjustedZip);
console.log(
`👌 <<< Adjusted the ZIP structure. Moved the modified ${basename(
pathToZip
)} to the ${targetFolderName} folder.`
);
resolve(adjustedZip);
} finally {
if (!noCleanup) {
track.cleanupSync();
}
}
});
}
/**
* Returns the `basename` of `pathToFile` without the file extension.
*/
function basename(pathToFile) {
const name = path.basename(pathToFile);
const ext = path.extname(pathToFile);
return name.substr(0, name.length - ext.length);
const name = path.basename(pathToFile);
const ext = path.extname(pathToFile);
return name.substr(0, name.length - ext.length);
}
function unpack(what, where) {
return new Promise((resolve, reject) => {
zip.unpack(what, where, error => {
if (error) {
reject(error);
return;
}
resolve();
})
return new Promise((resolve, reject) => {
zip.unpack(what, where, (error) => {
if (error) {
reject(error);
return;
}
resolve();
});
});
}
function pack(what, where) {
return new Promise((resolve, reject) => {
zip.pack(what, where, error => {
if (error) {
reject(error);
return;
}
resolve();
})
return new Promise((resolve, reject) => {
zip.pack(what, where, (error) => {
if (error) {
reject(error);
return;
}
resolve();
});
});
}
function list(what) {
return new Promise((resolve, reject) => {
zip.list(what, (error, result) => {
if (error) {
reject(error);
return;
}
resolve(result.map(({ name }) => name));
})
return new Promise((resolve, reject) => {
zip.list(what, (error, result) => {
if (error) {
reject(error);
return;
}
resolve(result.map(({ name }) => name));
});
});
}
async function isZip(pathToFile) {
if (!fs.existsSync(pathToFile)) {
throw new Error(`${pathToFile} does not exist`);
}
const type = await fromFile(pathToFile);
return type && type.ext === 'zip';
if (!fs.existsSync(pathToFile)) {
throw new Error(`${pathToFile} does not exist`);
}
const type = await fromFile(pathToFile);
return type && type.ext === 'zip';
}
const isElectronPublish = false; // TODO: support auto-updates
@@ -152,20 +169,56 @@ const isNightly = process.env.IS_NIGHTLY === 'true';
const isRelease = process.env.IS_RELEASE === 'true';
function git(command) {
try {
const gitPath = shell.which('git');
const error = shell.error();
if (error) {
throw new Error(error);
}
const { stderr, stdout } = shell.exec(`"${gitPath}" ${command}`, { silent: true });
if (stderr) {
throw new Error(stderr.toString().trim());
}
return stdout.toString().trim();
} catch (e) {
throw e;
try {
const gitPath = shell.which('git');
const error = shell.error();
if (error) {
throw new Error(error);
}
const { stderr, stdout } = shell.exec(`"${gitPath}" ${command}`, {
silent: true,
});
if (stderr) {
throw new Error(stderr.toString().trim());
}
return stdout.toString().trim();
} catch (e) {
throw e;
}
}
module.exports = { collectUnusedDependencies, adjustArchiveStructure, isZip, unpack, isNightly, isRelease, isElectronPublish, git };
// getChannelFile returns the name of the channel file to be released
// together with the IDE file.
// The channel file depends on the platform and whether we're creating
// a nightly build or a full release.
// In all other cases, like when building a tester build for a PR,
// an empty string is returned since we don't need a channel file.
// The channel files are necessary for updates check with electron-updater
// to work correctly.
// For more information: https://www.electron.build/auto-update
function getChannelFile(platform) {
let currentChannel = 'beta';
if (isRelease) {
currentChannel = 'latest';
}
return (
currentChannel +
{
linux: '-linux.yml',
win32: '.yml',
darwin: '-mac.yml',
}[platform]
);
}
module.exports = {
collectUnusedDependencies,
adjustArchiveStructure,
isZip,
unpack,
isNightly,
isRelease,
isElectronPublish,
git,
getChannelFile,
};

View File

@@ -437,6 +437,11 @@ cross-spawn@^6.0.0:
shebang-command "^1.2.0"
which "^1.2.9"
crypto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037"
integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==
dateformat@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
@@ -1597,6 +1602,11 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
yaml@^1.10.2:
version "1.10.2"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
yargs-parser@13.1.2, yargs-parser@^13.1.2:
version "13.1.2"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"

334
i18n/bg.json Normal file
View File

@@ -0,0 +1,334 @@
{
"arduino": {
"common": {
"offlineIndicator": "Изглежда, че сте офлайн. Без интернет връзка, Arduino CLI може да не успее да изтегли необходимите ресурси и може да причини неизправност. Моля, свържете се с интернет и рестартирайте приложението.",
"noBoardSelected": "Не е избрана платка",
"selectedOn": "на {0}",
"notConnected": "[няма връзка]",
"serialMonitor": "Сериен Монитор",
"oldFormat": "„{0}“ все още използва стария формат .pde. Искате ли да преминете към новото разширение `.ino`?",
"later": "По-късно",
"selectBoard": "Изберете платка",
"unknown": "Неизвестно",
"processing": "Обработва се",
"saveChangesToSketch": "Искате ли да запазите промените в тази скица, преди да затворите?",
"loseChanges": "Ако не запазите, промените ви ще бъдат загубени."
},
"menu": {
"sketch": "Скица",
"tools": "Инструменти"
},
"debug": {
"optimizeForDebugging": "Оптимизиране за отстраняване на грешки",
"debugWithMessage": "Отстраняване на грешки - {0}",
"noPlatformInstalledFor": "Платформата не е инсталирана за „{0}“",
"debuggingNotSupported": "Отстраняването на грешки не се поддържа от „{0}“"
},
"preferences": {
"language.log": "True, ако езиковият сървър на Arduino трябва да генерира лог файлове в папката за скици. В противен случай false. По подразбиране е false.",
"compile.verbose": "True за подробен изход за компилиране. False по подразбиране",
"compile.warnings": "Указва на gcc кое ниво на предупреждение да използва. По подразбиране е „None“.",
"upload.verbose": "True за подробен изход за качване. False по подразбиране.",
"window.autoScale": "True , ако потребителският интерфейс автоматично се мащабира с размера на шрифта.",
"window.zoomLevel": "Регулирайте нивото на увеличение на прозореца. Оригиналният размер е 0 и всяко увеличение над (напр. 1) или под (напр. -1) представлява увеличение с 20% по-голямо или по-малко. Можете също да въведете десетични знаци, за да регулирате нивото на увеличение по-финно.",
"ide.autoUpdate": "True, за да активирате автоматичните проверки за актуализация. IDE ще проверява за актуализации автоматично и периодично.",
"board.certificates": "Списък със сертификати, които могат да бъдат качени на платката",
"sketchbook.showAllFiles": "True , за да се покажат всички файлове вътре в скицата. По подразбиране е false.",
"cloud.enabled": "True , ако функциите за синхронизиране на скицата са активирани. По подразбиране е true.",
"cloud.pull.warn": "True е, ако потребителите трябва да бъдат предупредени, преди да се изтегли скица от облака. По подразбиране е true.",
"cloud.push.warn": "True, ако потребителите трябва да бъдат предупредени, преди да бъде пусната скица в облака. По подразбиране е true.",
"cloud.pushpublic.warn": "True, ако потребителите трябва да бъдат предупредени, преди да бъде изпратена публична скица в облака. По подразбиране е true.",
"cloud.sketchSyncEnpoint": "Крайната точка, използвана за изпращане и изтегляне на скици от бекенда. По подразбиране той сочи към Arduino Cloud API.",
"auth.clientID": "OAuth2 клиентски идентификатор.",
"auth.domain": "OAuth2 домейнът.",
"auth.audience": "OAuth2 аудиторията.",
"auth.registerUri": "URI, използван за регистриране на нов потребител.",
"network": "Мрежа",
"sketchbook.location": "Местоположение на скицника",
"browse": "Прегледай",
"files.inside.sketches": "Показване на файлове в Sketches",
"editorFontSize": "Размер на шрифта на редактора",
"interfaceScale": "Мащаб на интерфейса",
"showVerbose": "Показване на подробен изход по време на",
"compilerWarnings": "Компилационни предупреждения",
"automatic": "Автоматично",
"compile": "компилиране",
"upload": "качване",
"verifyAfterUpload": "Потвърдете кода след качване",
"checkForUpdates": "Проверете за актуализации при стартиране",
"editorQuickSuggestions": "Бързи предложения на редактора",
"additionalManagerURLs": "Допълнителни URL адреси на мениджър на платки",
"noProxy": "Без прокси",
"manualProxy": "Ръчна конфигурация на прокси",
"newSketchbookLocation": "Изберете местоположение за новата скицниката",
"choose": "Изберете",
"enterAdditionalURLs": "Въведете допълнителни URL адреси, по един за всеки ред",
"unofficialBoardSupport": "Щракнете за списък с неофициално поддържаните URL адреси на платки",
"invalid.sketchbook.location": "Невалидно местоположение на скицника: {0}",
"invalid.editorFontSize": "Невалиден размер на шрифта на редактора. Трябва да е положително цяло число.",
"invalid.theme": "Невалидна тема."
},
"cloud": {
"signIn": "ВПИШИ СЕ",
"signOut": "Отписване",
"chooseSketchVisibility": "Изберете видимост на вашата скица:",
"privateVisibility": "Частен. Само вие можете да видите скицата.",
"publicVisibility": "Обществени. Всеки с връзката може да види скицата.",
"link": "Връзка:",
"embed": "Вграждане:",
"cloudSketchbook": "Облачен скицник",
"shareSketch": "Споделете скица",
"showHideRemoveSketchbook": "Показване/скриване на отдалечен скицник",
"pullSketch": "Издърпайте скица",
"openInCloudEditor": "Отворете в Cloud Editor",
"options": "Настроики...",
"share": "Сподели...",
"remote": "Отдалечен",
"emptySketchbook": "Вашият Скицник е празен",
"visitArduinoCloud": "Посетете Arduino Cloud, за да създадете облачни скици.",
"signInToCloud": "Влезте в Arduino Cloud",
"syncEditSketches": "Синхронизирайте и редактирайте на вашите Arduino облачни скици",
"learnMore": "Научете повече",
"continue": "Продължи",
"pushSketch": "Изпрати скица",
"pushSketchMsg": "Това е публична скица. Преди да я изпратите, уверете се, че всяка чувствителна информация е дефинирана във файловете arduino_secrets.h. Можете да направите скица частна от панела за споделяне.",
"pull": "Изтегли",
"pullSketchMsg": "Изтеглянето на тази скица от облака ще презапише нейната локална версия. Сигурен ли си, че искаш да продължиш?",
"donePulling": "Изтеглянето на „{0}“ приключи.",
"notYetPulled": "Не може да се изпрати към облака. Още не е изтеглен.",
"push": "Изпрати",
"pullFirst": "Първо трябва да дръпнете, за да можете да изпратите към облака.",
"donePushing": "Изпращането на „{0}“ приключи.",
"connected": "Свързано",
"offline": "Офлайн",
"profilePicture": "Снимка на профила"
},
"board": {
"installManually": "Инсталирай ръчно",
"installNow": "Ядрото „{0} {1}“ трябва да бъде инсталирано за текущо избраната платка „{2}“. Искате ли да го инсталирате сега?",
"configDialogTitle": "Изберете друга платка и порт",
"configDialog1": "Изберете както платка, така и порт, ако искате да качите скица.",
"configDialog2": "Ако изберете само платка, ще можете само да компилирате, но не и да качвате скицата си.",
"pleasePickBoard": "Моля, изберете платка, свързана към порта, който сте избрали.",
"showAllAvailablePorts": "Показва всички налични портове, когато е активиран",
"programmer": "Програматор",
"succesfullyInstalledPlatform": "Успешно инсталирана платформа {0}:{1}",
"succesfullyUninstalledPlatform": "Успешно деинсталирана платформа {0}:{1}",
"couldNotFindPreviouslySelected": "Не можа да се намери по-рано избрана платка „{0}“ в инсталираната платформа „{1}“. Моля, изберете отново ръчно платката, която искате да използвате. Искате ли да я изберете отново сега?",
"reselectLater": "Изберете отново по-късно",
"noneSelected": "Няма избрани платка.",
"noPortsSelected": "Няма избрани портове за платка: „{0}“.",
"noFQBN": "FQBN не е наличен за избраната платка „{0}“. Имате ли инсталирано съответното ядро?",
"openBoardsConfig": "Изберете друга платка и порт...",
"boardListItem": "{0} в {1}",
"selectBoardForInfo": "Моля, изберете платка, за да получите информация за нея.",
"platformMissing": "Платформата за избраната платка „{0}“ не е инсталирана.",
"selectPortForInfo": "Моля, изберете порт, за да получите информация за платката.",
"boardInfo": "Информация за платка",
"board": "Платка{0}",
"port": "Порт{0}",
"getBoardInfo": "Вземи информация за платката",
"inSketchbook": "(в Скицника)"
},
"boardsManager": "Мениджър на платки",
"about": {
"label": "Относно {0}",
"detail": "Версия: {0}\nДата: {1}{2}\nВерсия на CLI: {3}{4} [{5}]\n\n{6}"
},
"contributions": {
"addFile": "Добави файл",
"replaceTitle": "Замени",
"fileAdded": "Към скицата е добавен един файл."
},
"replaceMsg": "Да се замени ли съществуващата версия на {0}?",
"library": {
"addZip": "Добавяне на .ZIP библиотека...",
"zipLibrary": "Библиотека",
"overwriteExistingLibrary": "Искате ли да презапишете съществуващата библиотека?",
"successfullyInstalledZipLibrary": "Успешно инсталирана библиотека от {0} архив",
"namedLibraryAlreadyExists": "Библиотечна папка с име {0} вече съществува. Искате ли да я презапишете?",
"libraryAlreadyExists": "Библиотека вече съществува. Искате ли да я презапишете?",
"include": "Включи библиотека",
"manageLibraries": "Управление на библиотеки...",
"arduinoLibraries": "Arduino библиотеки",
"contributedLibraries": "Допринесени библиотеки",
"title": "Управител на библиотеки",
"needsOneDependency": "Библиотеката <b>{0}:{1}</b> се нуждае от друга зависимост, която в момента не е инсталирана:",
"needsMultipleDependencies": "Библиотеката <b>{0}:{1}</b> се нуждае от някои други зависимости, които в момента не са инсталирани:",
"installOneMissingDependency": "Искате ли да инсталирате липсващата зависимост?",
"installMissingDependencies": "Искате ли да инсталирате всички липсващи зависимости?",
"dependenciesForLibrary": "Зависимости за библиотека {0}:{1}",
"installAll": "Инсталирай всички",
"installOnly": "Инсталирайте само {0}",
"installedSuccessfully": "Успешно инсталирана библиотека {0}:{1}",
"uninstalledSuccessfully": "Успешно деинсталирана библиотека {0}:{1}"
},
"selectZip": "Изберете zip файл, съдържащ библиотеката, която искате да добавите",
"sketch": {
"archiveSketch": "Архивирай скица",
"saveSketchAs": "Запазете папката със скици като...",
"createdArchive": "Създаден е архив „{0}“.",
"new": "Нов",
"openRecent": "Отвори Скорошен",
"showFolder": "Показване на папка за скици",
"sketch": "Скица",
"moving": "Преместване",
"movingMsg": "Файлът „{0}“ трябва да бъде в папка на скица, наречена „{1}“.\nСъздай тази папка, премести файла и продължи?",
"cantOpen": "Папка с име „{0}“ вече съществува. Не може да се отвори скица.",
"saveFolderAs": "Запазете папката със скица като...",
"sketchbook": "Скицник",
"upload": "Качване",
"uploadUsingProgrammer": "Качване с помощта на програмист",
"userFieldsNotFoundError": "Не мога да намеря потребителски полета за свързаната платка",
"doneUploading": "Качването приключи.",
"configureAndUpload": "Конфигуриране и качване",
"verifyOrCompile": "Проверете/компилирайте",
"exportBinary": "Експортиране на компилиран двоичен файл",
"verify": "Потвърдете",
"doneCompiling": "Готово е компилирането.",
"couldNotConnectToSerial": "Не можа да се свърже отново със серийния порт. {0}",
"openSketchInNewWindow": "Отвори скицата в нов прозорец",
"openFolder": "Отвори папка",
"titleLocalSketchbook": "Локален скицник",
"titleSketchbook": "Скицник",
"close": "Наистина ли искате да затворите скицата?"
},
"bootloader": {
"burnBootloader": "Запишете буутлоудъра",
"doneBurningBootloader": "Записването на буутлоудъра приключи."
},
"editor": {
"copyForForum": "Копиране за форум (Markdown)",
"commentUncomment": "Коментиране/Премахване на коментар",
"increaseIndent": "Увеличете отстъпа",
"decreaseIndent": "Намаляване на отстъпа",
"increaseFontSize": "Увеличете размера на шрифта",
"decreaseFontSize": "Намаляване на размера на шрифта",
"autoFormat": "Автоматично форматиране"
},
"examples": {
"menu": "Примери",
"couldNotInitializeExamples": "Не можа да се инициализира вградените примери.",
"builtInExamples": "Вградени примери",
"customLibrary": "Примери от персонализирани библиотеки",
"for": "Примери за {0}",
"forAny": "Примери за всяка платка"
},
"help": {
"search": "Търсете в Arduino.cc",
"keyword": "Въведете ключова дума",
"gettingStarted": "Да започнем",
"environment": "Околна среда",
"troubleshooting": "Отстраняване на неизправности",
"reference": "Референция",
"findInReference": "Намерете в Референцията",
"faq": "Често задавани въпроси",
"visit": "Посетете Arduino.cc"
},
"certificate": {
"uploadRootCertificates": "Качете SSL коренни сертификати",
"openContext": "Отворен контекст",
"remove": "Премахване",
"upload": "Качване",
"addURL": "Добавете URL за извличане на SSL сертификат",
"enterURL": "Въведете URL",
"selectCertificateToUpload": "1. Изберете сертификат за качване",
"addNew": "Добави нов",
"selectDestinationBoardToUpload": "2. Изберете платка и качете сертификат",
"uploadingCertificates": "Качване на сертификати.",
"certificatesUploaded": "Качени сертификати.",
"uploadFailed": "Качването не бе успешно. Моля, опитайте отново.",
"selectBoard": "Изберете платка,,,",
"boardAtPort": "{0} в {1}",
"noSupportedBoardConnected": "Няма свързана поддържана платка"
},
"firmware": {
"updater": "WiFi101 / WiFiNINA Обновяване на фърмуера",
"selectBoard": "Изберете платка",
"checkUpdates": "Проверете актуализации",
"selectVersion": "Изберете версия на фърмуера",
"install": "Инсталирай",
"overwriteSketch": "Инсталацията ще презапише скицата на платката.",
"installingFirmware": "Инсталиране на фърмуер.",
"successfullyInstalled": "Фърмуерът е инсталиран успешно.",
"failedInstall": "Неуспешно инсталиране. Моля, опитайте отново."
},
"dialog": {
"dontAskAgain": "Не питай отново"
},
"userFields": {
"cancel": "Отмяна",
"upload": "Качване"
},
"serial": {
"toggleTimestamp": "Превключване на клеймото за време",
"autoscroll": "Автоматично превъртане",
"timestamp": "Печат за време",
"noLineEndings": "Без край на реда",
"newLine": "Нов ред",
"carriageReturn": "Carriage Return",
"newLineCarriageReturn": "Както NL, така и CR",
"notConnected": "Няма връзка. Изберете платка и порт за автоматично свързване.",
"message": "Съобщение ({0} + Enter, за да изпратите съобщение до „{1}“ на „{2}“",
"connectionBusy": "Свързването е неуспешно. Серийният порт е зает: {0}",
"disconnected": "Прекъснато е връзката на {0} с {1}.",
"unexpectedError": "Неочаквана грешка. Повторно свързване на {0} на порт {1}.",
"failedReconnect": "Неуспешно свързване на {0} към серийния порт след 10 последователни опита. Серийният порт {1} е зает.",
"reconnect": "Повторно свързване на {0} към {1} след {2} секунди..."
},
"component": {
"uninstall": "Деинсталиране",
"uninstallMsg": "Искате ли да деинсталирате {0}?",
"by": "от",
"version": "Версия {0}",
"moreInfo": "Повече информация",
"install": "ИНСТАЛИРАЙ",
"filterSearch": "Филтрирайте търсенето си..."
},
"electron": {
"couldNotSave": "Скицата не можа да бъде запазена. Моля, копирайте незапазената си работа в любимия си текстов редактор и рестартирайте IDE-то.",
"unsavedChanges": "Всички незапазени промени няма да бъдат запазени."
},
"compile": {
"error": "Грешка при компилация: {0}"
},
"upload": {
"error": "{0} грешка: {1}"
},
"burnBootloader": {
"error": "Грешка при записване на зареждащия инструмент: {0}"
}
},
"theia": {
"core": {
"couldNotSave": "Скицата не можа да бъде запазена. Моля, копирайте незапазената си работа в любимия си текстов редактор и рестартирайте IDE-то.",
"offline": "Офлайн",
"daemonOffline": "CLI Daemon офлайн",
"cannotConnectBackend": "Не може да се свърже с бекенда.",
"cannotConnectDaemon": "Не може да се свърже с демона на CLI."
},
"debug": {
"start": "Започнете...",
"typeNotSupported": "Типът на сесията за отстраняване на грешки „{0}“ не се поддържа.",
"startError": "Възникна грешка при стартиране на сесията за отстраняване на грешки, проверете регистрационните файлове за повече подробности."
},
"editor": {
"unsavedTitle": "Незапазено {0}"
},
"messages": {
"expand": "Разгънете",
"collapse": "Свиване"
},
"workspace": {
"fileNewName": "Име за нов файл",
"invalidFilename": "Невалидно име на файла.",
"invalidExtension": ".{0} не е валидно разширение",
"newFileName": "Ново име на файла",
"deleteCurrentSketch": "Искате ли да изтриете текущата скица?",
"sketchDirectoryError": "При създаването на директорията на скицата възникна грешка. Вижте дневника за повече подробности. Приложението вероятно няма да работи според очакванията."
}
},
"cloud": {
"GoToCloud": "КЪМ ОБЛАКА"
}
}

View File

@@ -1,334 +1,334 @@
{
"arduino": {
"common": {
"offlineIndicator": "You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.",
"noBoardSelected": "No board selected",
"selectedOn": "on {0}",
"notConnected": "[not connected]",
"offlineIndicator": "Anscheinend bist du offline. Ohne eine aktive Internetverbindung kann das Arduino CLI nicht die nötigen Ressourcen herunterladen, was zu Problemen führen kann. Bitte überprüfe deine Internetverbindung und starte das Programm neu. ",
"noBoardSelected": "Kein Board ausgewählt",
"selectedOn": "bei {0}",
"notConnected": "[keine Verbindung]",
"serialMonitor": "Serieller Monitor",
"oldFormat": "The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?",
"later": "Later",
"selectBoard": "Select Board",
"unknown": "Unknown",
"processing": "Processing",
"saveChangesToSketch": "Do you want to save changes to this sketch before closing?",
"loseChanges": "If you don't save, your changes will be lost."
"oldFormat": "Der Sketch '{0}' verwendet noch das alte '.pde' Format. Möchtest du auf die neuere '.ino' Endung wechseln?",
"later": "später",
"selectBoard": "Board wählen",
"unknown": "unbekannt",
"processing": "Verarbeiten",
"saveChangesToSketch": "Änderungen zu diesem Sketch vor dem Schließen sichern?",
"loseChanges": "Wenn du nicht sicherst, werden deine Änderungen gelöscht."
},
"menu": {
"sketch": "Sketch",
"tools": "Werkzeuge"
},
"debug": {
"optimizeForDebugging": "Optimize for Debugging",
"optimizeForDebugging": "Für Debugging optimieren",
"debugWithMessage": "Debug - {0}",
"noPlatformInstalledFor": "Platform is not installed for '{0}'",
"debuggingNotSupported": "Debugging is not supported by '{0}'"
"noPlatformInstalledFor": "Die Platform für '{0}' ist nicht instaliert.",
"debuggingNotSupported": "'{0}' unterstützt kein Debuggen"
},
"preferences": {
"language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.",
"compile.verbose": "True for verbose compile output. False by default",
"compile.warnings": "Tells gcc which warning level to use. It's 'None' by default",
"upload.verbose": "True for verbose upload output. False by default.",
"window.autoScale": "True if the user interface automatically scales with the font size.",
"window.zoomLevel": "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.",
"ide.autoUpdate": "True to enable automatic update checks. The IDE will check for updates automatically and periodically.",
"board.certificates": "List of certificates that can be uploaded to boards",
"sketchbook.showAllFiles": "True to show all sketch files inside the sketch. It is false by default.",
"cloud.enabled": "True if the sketch sync functions are enabled. Defaults to true.",
"cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.",
"cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.",
"cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.",
"cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.",
"auth.clientID": "The OAuth2 client ID.",
"auth.domain": "The OAuth2 domain.",
"auth.audience": "The OAuth2 audience.",
"auth.registerUri": "The URI used to register a new user.",
"language.log": "Wahr, wenn der Arduino Language Server Logfiles in den Sketch-Ordner schreiben soll. Sonst falsch. Standardeinstellung ist falsch.\n ",
"compile.verbose": "Wahr für ausführliche Compilerausgaben. Standardmäßig Falsch",
"compile.warnings": "Einstellung des Warnlevels für den GCC. Standardeinstellung ist 'None'.",
"upload.verbose": "Wahr für ausführliche Ausgaben während dem Hochladen. Standardmäßig Falsch.",
"window.autoScale": "Ist wahr, wenn die Benutzeroberfläche automatisch mit der Schriftgröße skaliert wird.",
"window.zoomLevel": "Stelle die Zoomstufe des Fensters ein. Der Standardwert ist 0, jede Vergrößerung (z.B.: 1) oder Verringerung (z.B.: -1) um eins steht für 20% Vergrößerung bzw. Verkleinerung des Fensters. Du kannst auch Kommazahlen eingeben, um die Zoomstufe feiner einzustellen.\n ",
"ide.autoUpdate": "Wahr, um automatisch auf Updates zu überprüfen. Die IDE wird automatisch regelmäßig auf Updates prüfen.",
"board.certificates": "Liste der Zertifikate, welche zu den Boards hochgeladen werden können.",
"sketchbook.showAllFiles": "Wahr, um alle Dateien im Sketch anzuzeigen. Standardmäßig Falsch.",
"cloud.enabled": "Wahr, wenn die Sketch-Syncfunctionen aktiv sind. Standardeinstellung ist wahr.",
"cloud.pull.warn": "Wahr, wenn Benutzer vor dem Herunterladen eines Sketches aus der Cloud gewarnt werden sollen. Standardmäßig Wahr.",
"cloud.push.warn": "Wahr, wenn Benutzer vor dem Hochladen eines Cloud-Sketches gewarnt werden sollen. Standardmäßig Wahr.",
"cloud.pushpublic.warn": "Wahr, wenn Benutzer vor dem Hochladen eines öffentlichen Sketches in die Cloud gewarnt werden sollen. Standardmäßig Wahr.",
"cloud.sketchSyncEnpoint": "Der Endpunkt, um Sketches zu/von einem Backend zu laden. Standardeinstellung ist die Arduino Cloud API.",
"auth.clientID": "Die OAuth2 client ID.",
"auth.domain": "Die OAuth2 Domain.",
"auth.audience": "Das The OAuth2 Audience.",
"auth.registerUri": "Das URI hat einen neuen Benutzer registriert.",
"network": "Netzwerk",
"sketchbook.location": "Sketchbook location",
"sketchbook.location": "Dateipfad des Sketchbooks",
"browse": "Durchsuchen",
"files.inside.sketches": "Show files inside Sketches",
"editorFontSize": "Editor font size",
"interfaceScale": "Interface scale",
"showVerbose": "Show verbose output during",
"compilerWarnings": "Compiler warnings",
"files.inside.sketches": "Dateien im Sketch zeigen",
"editorFontSize": "Editor Schriftgröße",
"interfaceScale": "Fenster Skalierung",
"showVerbose": "Debug Informationen während",
"compilerWarnings": "Warnungen des Übersetzers",
"automatic": "Automatisch",
"compile": "compile",
"upload": "upload",
"verifyAfterUpload": "Verify code after upload",
"compile": "Kompilieren",
"upload": "Hochladen",
"verifyAfterUpload": "Code nach Hochladen überprüfen ",
"checkForUpdates": "Beim Start nach Updates suchen",
"editorQuickSuggestions": "Editor Quick Suggestions",
"editorQuickSuggestions": "Schnelle Editor Vorschläge",
"additionalManagerURLs": "Zusätzliche Boardverwalter-URLs",
"noProxy": "No proxy",
"manualProxy": "Manual proxy configuration",
"newSketchbookLocation": "Select new sketchbook location",
"choose": "Choose",
"enterAdditionalURLs": "Enter additional URLs, one for each row",
"unofficialBoardSupport": "Click for a list of unofficial board support URLs",
"invalid.sketchbook.location": "Invalid sketchbook location: {0}",
"invalid.editorFontSize": "Invalid editor font size. It must be a positive integer.",
"invalid.theme": "Invalid theme."
"noProxy": "Kein Proxy",
"manualProxy": "Manuelle Proxy Einstellung",
"newSketchbookLocation": "Wähle einen neuen Ort für das Sketchbook ",
"choose": "Wähle",
"enterAdditionalURLs": "Füge zusätzliche URLs hinzu, jede Reihe einzeln",
"unofficialBoardSupport": "Klicke hier für eine Liste von inoffiziell unterstützten Boards",
"invalid.sketchbook.location": "Ungültiger Sketchbook Speicherort: {0}",
"invalid.editorFontSize": "Ungültige Editor Schriftgröße. Sie muss eine positive, ganze Zahl sein.",
"invalid.theme": "Ungültiges Erscheinungsbild"
},
"cloud": {
"signIn": "SIGN IN",
"signOut": "Sign Out",
"chooseSketchVisibility": "Choose visibility of your Sketch:",
"privateVisibility": "Private. Only you can view the Sketch.",
"publicVisibility": "Public. Anyone with the link can view the Sketch.",
"signIn": "Anmelden",
"signOut": "Abmelden",
"chooseSketchVisibility": "Wähle die Sichtbarkeit deines Sketches:",
"privateVisibility": "Private. Nur du siehst diesen Sketch.",
"publicVisibility": "Public. Jeder kann mit diesen Link den Sketch sehen.",
"link": "Link:",
"embed": "Embed:",
"embed": "Einbetten:",
"cloudSketchbook": "Cloud Sketchbook",
"shareSketch": "Share Sketch",
"showHideRemoveSketchbook": "Show/Hide Remote Sketchbook",
"shareSketch": "Sketch teilen",
"showHideRemoveSketchbook": "Zeige/Verstecke Remote Sketchbook",
"pullSketch": "Pull Sketch",
"openInCloudEditor": "Open in Cloud Editor",
"options": "Options...",
"share": "Share...",
"openInCloudEditor": "Im Cloud Editor öffnen",
"options": "Optionen....",
"share": "Teilen....",
"remote": "Remote",
"emptySketchbook": "Your Sketchbook is empty",
"visitArduinoCloud": "Visit Arduino Cloud to create Cloud Sketches.",
"signInToCloud": "Sign in to Arduino Cloud",
"syncEditSketches": "Sync and edit your Arduino Cloud Sketches",
"learnMore": "Learn more",
"continue": "Continue",
"emptySketchbook": "Dein Sketchbook ist leer",
"visitArduinoCloud": "Besuche Arduino Cloud um Cloud Sketche zu erstellen.",
"signInToCloud": "Anmelden zur Arduino Cloud",
"syncEditSketches": "Synchronisiere und editiere deine Arduino Cloud Sketches.",
"learnMore": "Mehr erfahren",
"continue": "Fortfahren",
"pushSketch": "Push Sketch",
"pushSketchMsg": "This is a Public Sketch. Before pushing, make sure any sensitive information is defined in arduino_secrets.h files. You can make a Sketch private from the Share panel.",
"pushSketchMsg": "Das ist ein öffentliches Sketch. Vor dem Pushen solltest du überprüfen, ob alle sensiblen Informationen in arduino_secrets.h definiert sind. Du kannst einen Sketch mit dem Teilen-Feld privat machen.",
"pull": "Pull",
"pullSketchMsg": "Pulling this Sketch from the Cloud will overwrite its local version. Are you sure you want to continue?",
"donePulling": "Done pulling {0}.",
"notYetPulled": "Cannot push to Cloud. It is not yet pulled.",
"pullSketchMsg": "Wenn du diesen Sketch aus der Cloud lädst, wird die lokale Version überschrieben. Möchtest du fortfahren?",
"donePulling": "Herunterladen von '{0}' erfolgreich.",
"notYetPulled": "Kann nicht in die Cloud geschoben werden. Es ist noch nicht heruntergeladen.",
"push": "Push",
"pullFirst": "You have to pull first to be able to push to the Cloud.",
"donePushing": "Done pushing {0}.",
"connected": "Connected",
"pullFirst": "Du musst zuerst herunterladen, damit du in die Cloud schieben kannst.",
"donePushing": "Hochladen von '{0}' erfolgreich.",
"connected": "Verbunden",
"offline": "Offline",
"profilePicture": "Profile picture"
"profilePicture": "Profilbild"
},
"board": {
"installManually": "Install Manually",
"installNow": "The \"{0} {1}\" core has to be installed for the currently selected \"{2}\" board. Do you want to install it now?",
"configDialogTitle": "Select Other Board & Port",
"configDialog1": "Select both a Board and a Port if you want to upload a sketch.",
"configDialog2": "If you only select a Board you will be able just to compile, but not to upload your sketch.",
"pleasePickBoard": "Please pick a board connected to the port you have selected.",
"showAllAvailablePorts": "Shows all available ports when enabled",
"installManually": "Manuell installieren",
"installNow": "Der \"{0} {1}\" Core muss für das ausgewählte \"{2}\" Board installiert werden. Jetzt installieren?",
"configDialogTitle": "Andere Bords und Ports wählen",
"configDialog1": "Wählen Sie ein Board und einen Port, wenn Sie den Sketch hochladen möchten.",
"configDialog2": "Wenn Sie nur ein Board auswählen, werden Sie den Sketch nur kompilieren können, jedoch nicht hochladen.",
"pleasePickBoard": "Bitte wählen Sie das Board, welches am ausgewählten Port angeschlossen ist.",
"showAllAvailablePorts": "Zeige alle verfügbaren Ports, wenn aktiviert.",
"programmer": "Programmer",
"succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}",
"succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}",
"couldNotFindPreviouslySelected": "Could not find previously selected board '{0}' in installed platform '{1}'. Please manually reselect the board you want to use. Do you want to reselect it now?",
"reselectLater": "Reselect later",
"noneSelected": "No boards selected.",
"noPortsSelected": "No ports selected for board: '{0}'.",
"noFQBN": "The FQBN is not available for the selected board \"{0}\". Do you have the corresponding core installed?",
"openBoardsConfig": "Select other board and port…",
"boardListItem": "{0} at {1}",
"selectBoardForInfo": "Please select a board to obtain board info.",
"platformMissing": "The platform for the selected '{0}' board is not installed.",
"selectPortForInfo": "Please select a port to obtain board info.",
"boardInfo": "Boardinformationen",
"succesfullyInstalledPlatform": "Plattform erfolgreich installiert {0}:{1}",
"succesfullyUninstalledPlatform": "Plattform erfolgreich deinstalliert {0}:{1}",
"couldNotFindPreviouslySelected": "Zuvor gewähltes Board '{0}' wurde nicht in der installierten Plaftform '{1}' gefunden. Bitte Board erneut auswählen. Jetzt auswählen?",
"reselectLater": "Später auswählen",
"noneSelected": "Kein Board ausgewählt.",
"noPortsSelected": "Kein Port für das Board : '{0}' ausgewählt.",
"noFQBN": "Der FQBN ist für das gewählte Board \"{0}\" nicht verfügbar. Wurde der zugehörige Core installiert?",
"openBoardsConfig": "Wähle einen anderes Board und einen anderen Port...",
"boardListItem": "{0} bei {1}",
"selectBoardForInfo": "Wähle ein Board für die Board-Informationen.",
"platformMissing": "Die Plattform für das gewählte '{0}' Board ist nicht installiert.",
"selectPortForInfo": "Wähle ein Port, um Informationen über das Board zu erhalten.",
"boardInfo": "Board-Informationen",
"board": "Board{0}",
"port": "Port{0}",
"getBoardInfo": "Get Board Info",
"inSketchbook": " (in Sketchbook)"
"getBoardInfo": "Board-Informationen abrufen",
"inSketchbook": "(im Sketchbook)"
},
"boardsManager": "Boardverwalter",
"boardsManager": "Board-Verwaltung",
"about": {
"label": "About {0}",
"detail": "Version: {0}\nDate: {1}{2}\nCLI Version: {3}{4} [{5}]\n\n{6}"
"label": "Über {0}",
"detail": "Version: {0}\nDatum: {1}{2}\nCLI Version: {3}{4} [{5}]\n\n{6}"
},
"contributions": {
"addFile": "Datei hinzufügen...",
"replaceTitle": "Replace",
"fileAdded": "One file added to the sketch."
"replaceTitle": "Ersetzen",
"fileAdded": "Eine Datei wurde zum Sketch hinzugefügt."
},
"replaceMsg": "Replace the existing version of {0}?",
"replaceMsg": "Existierende Version von {0} ersetzen?",
"library": {
"addZip": ".ZIP-Bibliothek hinzufügen...",
"zipLibrary": "Library",
"overwriteExistingLibrary": "Do you want to overwrite the existing library?",
"successfullyInstalledZipLibrary": "Successfully installed library from {0} archive",
"namedLibraryAlreadyExists": "A library folder named {0} already exists. Do you want to overwrite it?",
"libraryAlreadyExists": "A library already exists. Do you want to overwrite it?",
"include": "Include Library",
"manageLibraries": "Manage Libraries...",
"arduinoLibraries": "Arduino libraries",
"contributedLibraries": "Contributed libraries",
"title": "Library Manager",
"needsOneDependency": "The library <b>{0}:{1}</b> needs another dependency currently not installed:",
"needsMultipleDependencies": "The library <b>{0}:{1}</b> needs some other dependencies currently not installed:",
"installOneMissingDependency": "Would you like to install the missing dependency?",
"installMissingDependencies": "Would you like to install all the missing dependencies?",
"dependenciesForLibrary": "Dependencies for library {0}:{1}",
"installAll": "Install all",
"installOnly": "Install {0} only",
"installedSuccessfully": "Successfully installed library {0}:{1}",
"uninstalledSuccessfully": "Successfully uninstalled library {0}:{1}"
"zipLibrary": "Bibliothek",
"overwriteExistingLibrary": "Möchtest du die existierende Bibliothek überschreiben?",
"successfullyInstalledZipLibrary": "Bibliothek erfolgreich vom {0} Archiv installiert",
"namedLibraryAlreadyExists": "Der Bibliothek-Ordner '{0}' existiert bereits. Überschreiben?",
"libraryAlreadyExists": "Eine Bibliothek existiert bereits. Möchten sie diese überschreiben?",
"include": "Bibliothek einbinden",
"manageLibraries": "Bibliotheken verwalten...",
"arduinoLibraries": "Arduino Bibliotheken",
"contributedLibraries": "Bibliotheken, zu denen beigetragen wurde",
"title": "Bibliotheksverwalter",
"needsOneDependency": "Die Bibliothek <b>{0}:{1}</b> benötigt eine andere Abhängigkeit, welche derzeit nicht installiert ist:",
"needsMultipleDependencies": "Die Bibliothek <b>{0}:{1}</b> benötigt ein paar andere Abhängigkeiten, die derzeit nicht installiert sind:",
"installOneMissingDependency": "Möchten Sie die fehlende Ressource installieren?",
"installMissingDependencies": "Möchten Sie alle fehlenden Ressourcen installieren?",
"dependenciesForLibrary": "Abhängigkeiten für die Bibliothek {0}:{1}",
"installAll": "Alle installieren",
"installOnly": "Nur {0} installieren",
"installedSuccessfully": "Bibliothek {0}:{1} erfolgreich installiert",
"uninstalledSuccessfully": "Bibliothek {0}:{1} erfolgreich deinstalliert"
},
"selectZip": "Select a zip file containing the library you'd like to add",
"selectZip": "Wähle die ZIP-Datei, welche die hinzuzufügende Bibliothek enthält",
"sketch": {
"archiveSketch": "Sketch archivieren",
"saveSketchAs": "Save sketch folder as...",
"createdArchive": "Created archive '{0}'.",
"new": "New",
"openRecent": "Open Recent",
"showFolder": "Show Sketch Folder",
"saveSketchAs": "Sketch Ordner speichern als...",
"createdArchive": "Archiv '{0}' erstellt.",
"new": "Neu",
"openRecent": "Zuletzt geöffnet",
"showFolder": "Zeige Sketch Ordner",
"sketch": "Sketch",
"moving": "Moving",
"movingMsg": "The file \"{0}\" needs to be inside a sketch folder named as \"{1}\".\nCreate this folder, move the file, and continue?",
"moving": "Übertragen...",
"movingMsg": "Die Datei \"{0}\" muss sich in einem Sketch Ordner \"{1}\" befinden.\nDiesen Ordner erstellen, Datei verschieben und fortfahren?",
"cantOpen": "Ein Ordner mit dem Namen \"{0}\" ist bereits vorhanden. Der Sketch kann nicht geöffnet werden.",
"saveFolderAs": "Save sketch folder as...",
"saveFolderAs": "Sketch Ordner speichern als...",
"sketchbook": "Sketchbook",
"upload": "Hochladen",
"uploadUsingProgrammer": "Upload Using Programmer",
"userFieldsNotFoundError": "Can't find user fields for connected board",
"doneUploading": "Done uploading.",
"configureAndUpload": "Configure And Upload",
"verifyOrCompile": "Verify/Compile",
"exportBinary": "Export Compiled Binary",
"verify": "Verify",
"doneCompiling": "Done compiling.",
"couldNotConnectToSerial": "Could not reconnect to serial port. {0}",
"openSketchInNewWindow": "Open Sketch in New Window",
"openFolder": "Open Folder",
"titleLocalSketchbook": "Local Sketchbook",
"uploadUsingProgrammer": "Mit Programmer hochladen",
"userFieldsNotFoundError": "User Fields für das angeschlossene Board nicht gefunden",
"doneUploading": "Hochladen erfolgreich! ",
"configureAndUpload": "Konfigurieren und hochladen",
"verifyOrCompile": "Überprüfen/Kompilieren",
"exportBinary": "Kompilierte Binärdatei exportieren",
"verify": "Überprüfen",
"doneCompiling": "Kompilieren erfolgreich!",
"couldNotConnectToSerial": "Verbindung zum seriellen Port fehlgeschlagen. {0}",
"openSketchInNewWindow": "Sketch in neuen Fenster öffnen",
"openFolder": "Ordner öffnen",
"titleLocalSketchbook": "Lokales Sketchbook",
"titleSketchbook": "Sketchbook",
"close": "Are you sure you want to close the sketch?"
"close": "Sind Sie sicher, dass Sie diesen Sketch schließen möchten?"
},
"bootloader": {
"burnBootloader": "Bootloader brennen",
"doneBurningBootloader": "Done burning bootloader."
"doneBurningBootloader": "Bootloader erfolgreich gebrannt."
},
"editor": {
"copyForForum": "Für Forum kopieren (Markdown)",
"commentUncomment": "Kommentieren/Kommentar aufheben",
"increaseIndent": "Increase Indent",
"decreaseIndent": "Decrease Indent",
"increaseFontSize": "Increase Font Size",
"decreaseFontSize": "Schriftgröße verringern",
"autoFormat": "Automatische Formatierung"
"increaseIndent": "Einrückung erweitern",
"decreaseIndent": "Einrückung verringern ",
"increaseFontSize": "Schriftgröße vergrößern ",
"decreaseFontSize": "Schrift verkleinern",
"autoFormat": "automatisch Formatieren"
},
"examples": {
"menu": "Examples",
"couldNotInitializeExamples": "Could not initialize built-in examples.",
"menu": "Beispiele",
"couldNotInitializeExamples": "Mitgelieferte Beispiele konnten nicht initialisiert werden",
"builtInExamples": "Mitgelieferte Beispiele",
"customLibrary": "Examples from Custom Libraries",
"for": "Examples for {0}",
"forAny": "Examples for any board"
"customLibrary": "Beispiele aus eigenen Bibliotheken",
"for": "Beispiele für {0}",
"forAny": "Beispiele für jedes Board"
},
"help": {
"search": "Search on Arduino.cc",
"keyword": "Type a keyword",
"gettingStarted": "Getting Started",
"environment": "Environment",
"troubleshooting": "Troubleshooting",
"reference": "Reference",
"findInReference": "Find in Reference",
"faq": "Frequently Asked Questions",
"visit": "Visit Arduino.cc"
"search": "Suche auf Arduino.cc",
"keyword": "Schlagwort eingeben",
"gettingStarted": "Loslegen...",
"environment": "Umgebung",
"troubleshooting": "Fehlersuche",
"reference": "Referenz",
"findInReference": "Referenzsuche",
"faq": "Häufig gestellte Fragen",
"visit": "Besuche Arduino.cc"
},
"certificate": {
"uploadRootCertificates": "Upload SSL Root Certificates",
"openContext": "Open context",
"remove": "Remove",
"uploadRootCertificates": "SSL Root Zertifikat hochladen",
"openContext": "Kontext öffnen",
"remove": "entfernen",
"upload": "Hochladen",
"addURL": "Add URL to fetch SSL certificate",
"enterURL": "Enter URL",
"selectCertificateToUpload": "1. Select certificate to upload",
"addNew": "Add New",
"selectDestinationBoardToUpload": "2. Select destination board and upload certificate",
"uploadingCertificates": "Uploading certificates.",
"certificatesUploaded": "Certificates uploaded.",
"uploadFailed": "Upload failed. Please try again.",
"selectBoard": "Select a board...",
"boardAtPort": "{0} at {1}",
"noSupportedBoardConnected": "No supported board connected"
"addURL": "URL hinzufügen, um das SSL-Zertifikat abzurufen",
"enterURL": "URL eingeben",
"selectCertificateToUpload": "1. Zertifikat zum hochladen auswählen",
"addNew": "Neu hinzufügen",
"selectDestinationBoardToUpload": "2. Zielboard wählen und Zertifikat hochladen",
"uploadingCertificates": "Zertifikate hochladen...",
"certificatesUploaded": "Die Zertifikate wurden erfolgreich hochgeladen.",
"uploadFailed": "Hochladen fehlgeschlagen. Bitte versuchen Sie es erneut.",
"selectBoard": "Wähle ein Board...",
"boardAtPort": "{0} bei {1}",
"noSupportedBoardConnected": "Kein unterstütztes Board angeschlossen"
},
"firmware": {
"updater": "WiFi101 / WiFiNINA Firmware Updater",
"selectBoard": "Select Board",
"checkUpdates": "Check Updates",
"selectVersion": "Select firmware version",
"updater": "WiFi101 / WiFiNINA Firmware Aktualisierer",
"selectBoard": "Board auswählen",
"checkUpdates": "Nach Updates Suchen",
"selectVersion": "Firmware Version wählen",
"install": "Installieren",
"overwriteSketch": "Installation will overwrite the Sketch on the board.",
"installingFirmware": "Installing firmware.",
"successfullyInstalled": "Firmware succesfully installed.",
"failedInstall": "Installation failed. Please try again."
"overwriteSketch": "Die Installation wird den Sketch auf dem Board überschreiben.",
"installingFirmware": "Firmware wird installiert.",
"successfullyInstalled": "Firmware wurde erfolgreich installiert.",
"failedInstall": "Installation fehlgeschlagen. Bitte versuche es erneut "
},
"dialog": {
"dontAskAgain": "Don't ask again"
"dontAskAgain": "Nicht noch einmal fragen"
},
"userFields": {
"cancel": "Cancel",
"cancel": "abbrechen",
"upload": "Hochladen"
},
"serial": {
"toggleTimestamp": "Toggle Timestamp",
"autoscroll": "Autoscroll",
"timestamp": "Timestamp",
"toggleTimestamp": "Zeitstempel an/aus",
"autoscroll": "Automatisch scrollen",
"timestamp": "Zeitstempel",
"noLineEndings": "Kein Zeilenende",
"newLine": "Neue Zeile",
"carriageReturn": "Zeilenumbruch (CR)",
"carriageReturn": "Zeilenumbruch",
"newLineCarriageReturn": "Sowohl NL als auch CR",
"notConnected": "Not connected. Select a board and a port to connect automatically.",
"message": "Message ({0} + Enter to send message to '{1}' on '{2}'",
"connectionBusy": "Connection failed. Serial port is busy: {0}",
"disconnected": "Disconnected {0} from {1}.",
"unexpectedError": "Unexpected error. Reconnecting {0} on port {1}.",
"failedReconnect": "Failed to reconnect {0} to serial port after 10 consecutive attempts. The {1} serial port is busy.",
"reconnect": "Reconnecting {0} to {1} in {2} seconds..."
"notConnected": "Nicht verbunden. Wählen Sie ein Board und einen Port, um automatisch zu verbinden.",
"message": "Nachricht ({0} + Enter, um Nachricht an '{1}' auf '{2}' zu senden",
"connectionBusy": "Verbindung fehlgeschlagen. Serieller port ist belegt: {0}",
"disconnected": "{0} von {1} getrennt.",
"unexpectedError": "100%match\nUnerwarteter Fehler. {0} wird am Port {1} erneut verbunden.",
"failedReconnect": "Verbindung von {0} zum seriellen Port nach 10 Versuchen abgebrochen. Der {1} serielle Port ist belegt..",
"reconnect": "Verbinde {0} erneut zu {1} in {2} Sekunden..."
},
"component": {
"uninstall": "Uninstall",
"uninstallMsg": "Do you want to uninstall {0}?",
"by": "by",
"uninstall": "deinstalieren",
"uninstallMsg": "Möchten Sie {0} deinstallieren?",
"by": "von",
"version": "Version {0}",
"moreInfo": "More info",
"moreInfo": "Mehr Information",
"install": "Installieren",
"filterSearch": "Grenzen Sie Ihre Suche ein..."
"filterSearch": "Filtern Sie Ihre Suche..."
},
"electron": {
"couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.",
"unsavedChanges": "Any unsaved changes will not be saved."
"couldNotSave": "Der Sketch konnte nicht gesichert werden. Bitte kopiere deine ungesicherte Arbeit in deinen bevorzugten Texteditor und starte die IDE neu.",
"unsavedChanges": "Alle ungesicherten Änderung werden nicht gespeichert."
},
"compile": {
"error": "Compilation error: {0}"
"error": "Fehler beim kompilieren: {0}"
},
"upload": {
"error": "{0} error: {1}"
"error": "{0} Fehler: {1}"
},
"burnBootloader": {
"error": "Error while burning the bootloader: {0}"
"error": "Fehler beim Brennen des Bootloaders: {0}"
}
},
"theia": {
"core": {
"couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.",
"couldNotSave": "Der Sketch konnte nicht gesichert werden. Bitte kopiere deine ungesicherte Arbeit in deinen bevorzugten Texteditor und starte die IDE neu.",
"offline": "Offline",
"daemonOffline": "CLI Daemon Offline",
"cannotConnectBackend": "Cannot connect to the backend.",
"cannotConnectDaemon": "Cannot connect to the CLI daemon."
"cannotConnectBackend": "Keine Verbindung zum Backend.",
"cannotConnectDaemon": "Keine Verbindung zum CLI Daemon."
},
"debug": {
"start": "Start...",
"typeNotSupported": "The debug session type \"{0}\" is not supported.",
"startError": "There was an error starting the debug session, check the logs for more details."
"typeNotSupported": "Die Debug Session vom Typ \"{0}\" wird nicht unterstützt.",
"startError": "Es gab einen Fehler beim Start der Debug-Session. Überprüfe die Logs für mehr Informationen."
},
"editor": {
"unsavedTitle": "Unsaved {0}"
"unsavedTitle": "Nicht gespeichert {0}"
},
"messages": {
"expand": "Expand",
"collapse": "Collapse"
"expand": "Ausklappen",
"collapse": "Einklappen"
},
"workspace": {
"fileNewName": "Name for new file",
"invalidFilename": "Invalid filename.",
"fileNewName": "Name für die neue Datei",
"invalidFilename": "Ungültiger Dateiname. ",
"invalidExtension": "\".{0}\" ist keine gültige Dateierweiterung.",
"newFileName": "New name for file",
"deleteCurrentSketch": "Do you want to delete the current sketch?",
"sketchDirectoryError": "There was an error creating the sketch directory. See the log for more details. The application will probably not work as expected."
"newFileName": "Neuer Name für die Datei",
"deleteCurrentSketch": "Möchtest du den aktuellen Sketch löschen?",
"sketchDirectoryError": "Fehler beim Erstellen des Sketch-Ordners. Mehr Informationen siehe Log. Die Anwendung wird wahrscheinlich nicht wie erwartet funktionieren."
}
},
"cloud": {
"GoToCloud": "GO TO CLOUD"
"GoToCloud": "Zur Cloud gehen"
}
}

334
i18n/el.json Normal file
View File

@@ -0,0 +1,334 @@
{
"arduino": {
"common": {
"offlineIndicator": "Φαίνεται πως είστε εκτός σύνδεσης. Χωρίς σύνδεση στο Internet, το Arduino CLI ίσως να μη μπορεί να κάνει λήψη των απαιτούμενων πόρων και να υπάρξει δυσλειτουργία. Παρακαλώ συνδεθείτε στο Internet και επανεκκινήστε την εφαρμογή.",
"noBoardSelected": "Δεν έχει επιλεχθεί πλακέτα",
"selectedOn": "στο {0}",
"notConnected": "[μη συνδεμένο]",
"serialMonitor": "Παρακολούθηση Σειριακής",
"oldFormat": "Το '{0}' χρησιμοποιεί ακόμα το παλιό '.pde' στυλ. Θέλετε να αλλάξετε στην νέα κατάληξη '.ino';",
"later": "Αργότερα",
"selectBoard": "Επιλογή Πλακέτας",
"unknown": "Άγνωστο",
"processing": "Επεξεργασία",
"saveChangesToSketch": "Θέτε να αποθηκεύσετε τις αλλαγές σε αυτο το σχέδιο πριν κλείσετε; ",
"loseChanges": "Αν δεν αποθηκεύσετε, οι αλλαγές σας θα χαθούν."
},
"menu": {
"sketch": "Σχέδιο",
"tools": "Εργαλεία"
},
"debug": {
"optimizeForDebugging": "Βελτιστοποίηση για Αποσφαλμάτωση",
"debugWithMessage": "Αποσφαλμάτωση - {0}",
"noPlatformInstalledFor": "Δεν έχει εγκατασταθεί πλατφόρμα για '{0}'",
"debuggingNotSupported": "Δεν υποστιρίζεται αποσφαλμάτωση από '{0}'"
},
"preferences": {
"language.log": "Αληθές αν ο Arduino Language Server πρέπει να παράξει αρχεία κατάστασης στον φάκελο σχεδίου. Διαφορετικά, ψευδές. Είναι ψευδές απο προεπιλογή.",
"compile.verbose": "Αληθές για λεπτομερή έξοδο μεταγλώττισης. Ψευδές απο προεπιλογή.",
"compile.warnings": "Λέει στο gcc ποιο επίπεδο προειδοποίησης να χρησιμοποιήσει. Είναι 'None' απο προεπιλογή",
"upload.verbose": "Αληθές για λεπτομερή έξοδο ανεβάσματος. Ψευδές απο προεπιλογή.",
"window.autoScale": "Αληθές αν η διεπαφή χρήστη κλιμακλωνεται αυτόματα μαζί με το μέγεθος γραμματοσειράς.",
"window.zoomLevel": "Ρύθμιση του επιπέδου μεγέθυνσης του παραθύρου. Το αρχικό μέγεθος ειναι 0 και κάθε αύξηση (π.χ. 1) ή μείωση (π.χ. -1) αναπαριστά μεγέθυνση 20% μεγαλύτερη ή μικρότερη. Μπορούν να εισαχθούν και δεκαδικά για προσαρμογή της μεγέθυνσης με μεγαλύτερη λεπτομέρεια.",
"ide.autoUpdate": "Αληθές για ενεργοποίηση αυτομάτων ελέγχων ενημερώσεων. Το IDE θα ελέγχει για ενημερώσεις αυτόματα και τακτικά.",
"board.certificates": "Λίστα πιστοποιητικών που μπορούν να ανέβουν σε πλακέτες",
"sketchbook.showAllFiles": "Αληθές για εμφάνιση όλων των αρχείων σχεδίου μεσα στο σχέδιο. Είναι ψευδές απο προεπιλογή.",
"cloud.enabled": "Αληθές αν οι λειτουγίες συγχονισμού σχεδίου είναι ενεργοποιημένες. Προεπιλογή ως αληθές.",
"cloud.pull.warn": "Αληθές αν οι χρήστες πρέπει προειδοποιηθούν πριν τραβηχτεί ενα σχέδιο σύννεφου. Προεπιλογή ως αληθές.",
"cloud.push.warn": "Αληθές αν οι χρήστες πρέπει προειδοποιηθούν πριν σπρωχθεί ενα σχέδιο σύννεφου. Προεπιλογή ως αληθές. ",
"cloud.pushpublic.warn": "Αληθές αν οι χρήστες πρέπει προειδοποιηθούν πριν σπρωχθεί ενα δημόσιο σχέδιο σύννεφου. Προεπιλογή ως αληθές. ",
"cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.",
"auth.clientID": "The OAuth2 client ID.",
"auth.domain": "The OAuth2 domain.",
"auth.audience": "The OAuth2 audience.",
"auth.registerUri": "The URI used to register a new user.",
"network": "Δίκτυο",
"sketchbook.location": "Τοποθεσία σχεδίων",
"browse": "Περιήγηση",
"files.inside.sketches": "Εμφάνιση αρχείων μέσα σε Σχέδια",
"editorFontSize": "Μέγεθος γραμματοσειράς Συντάκτη",
"interfaceScale": "Κλίμακα διεπαφής",
"showVerbose": "Εμφάνιση λεπτομερούς εξόδου κατά τη διάρκεια",
"compilerWarnings": "Προειδοποιήσεις μεταγγλωτιστή",
"automatic": "Αυτόματο",
"compile": "μεταγλώττιση",
"upload": "ανέβασμα",
"verifyAfterUpload": "Επιβεβαίωση κώδικα μετά το ανέβασμα",
"checkForUpdates": "Έλεγχος ενημερώσεων κατά την εκκίνηση",
"editorQuickSuggestions": "Editor Quick Suggestions",
"additionalManagerURLs": "Πρόσθετοι Σύνδεσμοι Διαχειριστή Πλακετών",
"noProxy": "No proxy",
"manualProxy": "Manual proxy configuration",
"newSketchbookLocation": "Επιλογή νέας τοποθεσίας σχεδίων",
"choose": "Επιλογή",
"enterAdditionalURLs": "Τοποθετήστε πρόσθετους Συνδέσμους, ένα σε κάθε σειρά",
"unofficialBoardSupport": "Κλικ για λίστα Συνδέσμων ανεπίσημης υποστήριξης πλακετών",
"invalid.sketchbook.location": "Μη-έγκυρη τοποθεσία σχεδίων: {0}",
"invalid.editorFontSize": "Μη-έγκυρο μέγεθος γραμματοσειράς συντάκτη. Πρέπει να είναι θετικός ακέραιος.",
"invalid.theme": "Μη-έγκυρο θέμα."
},
"cloud": {
"signIn": "ΣΥΥΝΔΕΣΗ",
"signOut": "Αποσύνδεση",
"chooseSketchVisibility": "Επίλεξε την ορατότητα του Σχεδίου σου:",
"privateVisibility": "Ιδιωτικό. Μόνο εσύ μπορείς να δεις το Σχέδιο.",
"publicVisibility": "Δημόσιο. Οποιόσδηποτε με το σύνδεσμο μπορεί να δει το Σχέδιο.",
"link": "Σύνδεσμος:",
"embed": "Ενσωμάτωση:",
"cloudSketchbook": "Σχέδια Cloud",
"shareSketch": "Κοινοποίηση Σχεδίου",
"showHideRemoveSketchbook": "Εμφάνιση/Απόκρυψη Απομακρυνσμένων Σχεδίων",
"pullSketch": "Pull Sketch",
"openInCloudEditor": "Άνοιγμα σε Συντάκτη Cloud",
"options": "Επιλογές...",
"share": "Κοινοποίηση...",
"remote": "Απομακρυνσμένο",
"emptySketchbook": "Τα Σχέδια σου είναι άδεια.",
"visitArduinoCloud": "Επισκέψου το Arduino Cloud για δημιουργία Σχεδίων Cloud.",
"signInToCloud": "Σύνδεση στο Arduino Cloud",
"syncEditSketches": "Συγχρονισμός και τροποποίηση των Arduino Cloud Σχεδίων σου.",
"learnMore": "Μάθε περισσότερα",
"continue": "Συνέχεια",
"pushSketch": "Push Sketch",
"pushSketchMsg": "This is a Public Sketch. Before pushing, make sure any sensitive information is defined in arduino_secrets.h files. You can make a Sketch private from the Share panel.",
"pull": "Pull",
"pullSketchMsg": "Pulling this Sketch from the Cloud will overwrite its local version. Are you sure you want to continue?",
"donePulling": "Done pulling {0}.",
"notYetPulled": "Cannot push to Cloud. It is not yet pulled.",
"push": "Push",
"pullFirst": "You have to pull first to be able to push to the Cloud.",
"donePushing": "Done pushing {0}.",
"connected": "Συνδέθηκε",
"offline": "Εκτός Σύνδεσης",
"profilePicture": "Εικόνα προφίλ"
},
"board": {
"installManually": "Χειροκίνητη Εγκατάσταση",
"installNow": "Ο πυρήνας \"{0} {1}\" πρέπει να εγκατασταθεί για την επιλεγμένη πλακέτα {2}. Θέλεις να την εγκαταστήσεις τώρα;",
"configDialogTitle": "Επιλέξτε Άλλη Πλακέτα & Θύρα",
"configDialog1": "Επίλεξε και Πλακέτα και Θύρα αν θέλεις να ανεβάσεις ένα σχέδιο.",
"configDialog2": "Αν επιλέξεις μονο Πλακέτα θα μπορείς να κάνεις μόνο μεταγγλώτιση, αλλά οχι να ανεβάσεις το σχέδιο.",
"pleasePickBoard": "Πσρακαλώ επίλεξε μια πλακέτα που συνδέθηκε στην θύρα που έχεις επιλέξει.",
"showAllAvailablePorts": "Εμφανίζει όλες τις διαθέσιμες θύρες όταν είναι ενεργοποιημένο.",
"programmer": "Προγραμματιστής",
"succesfullyInstalledPlatform": "Επιτυχής εγκατάσταση πλατφόρμας {0}:{1}",
"succesfullyUninstalledPlatform": "Επιτυχής απεγκατάσταση πλατφόρμας {0}:{1}",
"couldNotFindPreviouslySelected": "Δεν έγινε εντοπισμός της προηγουμένως επιλεγμένης πλακέτας '{0}' στην εγκατεστημένη πλατφόρμα '{1}'. Παρακαλώ επίλεξε πάλι χειροκίνητα την πλακέτα που θέλεις να χρησιμοποιήσεις. Θέλεις να την επιλέξεις τώρα;",
"reselectLater": "Επιλογή αργότερα",
"noneSelected": "Δεν επιλέχθηκε πλακέτα",
"noPortsSelected": "Δεν επιλέχθηκε θύρα για την πλακέτα: '{0}'.",
"noFQBN": "The FQBN is not available for the selected board \"{0}\". Do you have the corresponding core installed?",
"openBoardsConfig": "Επιλογή διαφορετικής πλακέτας και θύρας...",
"boardListItem": "{0} στο {1}",
"selectBoardForInfo": "Παρακαλώ επίλεξε μια πλακέτα για εμφάνιση πληροφοριών πλακέτας.",
"platformMissing": "Η πλατφόρμα για την επιλεγμένη πλακέτα '{0}' δεν έχει εγκατασταθεί.",
"selectPortForInfo": "Παρακαλώ επίλεξε μια θύρα για εμφάνιση πληροφοριών πλακέτας.",
"boardInfo": "Πληροφορίες Πλακέτας",
"board": "Πλακέτα{0}",
"port": "Θύρα{0}",
"getBoardInfo": "Εμφάνιση Πληροφοριών Πλακέτας",
"inSketchbook": "(στα Σχέδια)"
},
"boardsManager": "Διαχειριστής Πλακετών",
"about": {
"label": "Σχετικά με {0}",
"detail": "Version: {0}\nDate: {1}{2}\nCLI Version: {3}{4} [{5}]\n\n{6}"
},
"contributions": {
"addFile": "Add File",
"replaceTitle": "Replace",
"fileAdded": "One file added to the sketch."
},
"replaceMsg": "Replace the existing version of {0}?",
"library": {
"addZip": "Add .ZIP Library...",
"zipLibrary": "Library",
"overwriteExistingLibrary": "Do you want to overwrite the existing library?",
"successfullyInstalledZipLibrary": "Successfully installed library from {0} archive",
"namedLibraryAlreadyExists": "A library folder named {0} already exists. Do you want to overwrite it?",
"libraryAlreadyExists": "A library already exists. Do you want to overwrite it?",
"include": "Include Library",
"manageLibraries": "Manage Libraries...",
"arduinoLibraries": "Arduino libraries",
"contributedLibraries": "Contributed libraries",
"title": "Library Manager",
"needsOneDependency": "The library <b>{0}:{1}</b> needs another dependency currently not installed:",
"needsMultipleDependencies": "The library <b>{0}:{1}</b> needs some other dependencies currently not installed:",
"installOneMissingDependency": "Would you like to install the missing dependency?",
"installMissingDependencies": "Would you like to install all the missing dependencies?",
"dependenciesForLibrary": "Dependencies for library {0}:{1}",
"installAll": "Install all",
"installOnly": "Install {0} only",
"installedSuccessfully": "Successfully installed library {0}:{1}",
"uninstalledSuccessfully": "Successfully uninstalled library {0}:{1}"
},
"selectZip": "Select a zip file containing the library you'd like to add",
"sketch": {
"archiveSketch": "Archive Sketch",
"saveSketchAs": "Save sketch folder as...",
"createdArchive": "Created archive '{0}'.",
"new": "New",
"openRecent": "Open Recent",
"showFolder": "Show Sketch Folder",
"sketch": "Sketch",
"moving": "Moving",
"movingMsg": "The file \"{0}\" needs to be inside a sketch folder named as \"{1}\".\nCreate this folder, move the file, and continue?",
"cantOpen": "A folder named \"{0}\" already exists. Can't open sketch.",
"saveFolderAs": "Save sketch folder as...",
"sketchbook": "Sketchbook",
"upload": "Upload",
"uploadUsingProgrammer": "Upload Using Programmer",
"userFieldsNotFoundError": "Can't find user fields for connected board",
"doneUploading": "Done uploading.",
"configureAndUpload": "Configure And Upload",
"verifyOrCompile": "Verify/Compile",
"exportBinary": "Export Compiled Binary",
"verify": "Verify",
"doneCompiling": "Done compiling.",
"couldNotConnectToSerial": "Could not reconnect to serial port. {0}",
"openSketchInNewWindow": "Open Sketch in New Window",
"openFolder": "Open Folder",
"titleLocalSketchbook": "Local Sketchbook",
"titleSketchbook": "Sketchbook",
"close": "Are you sure you want to close the sketch?"
},
"bootloader": {
"burnBootloader": "Burn Bootloader",
"doneBurningBootloader": "Done burning bootloader."
},
"editor": {
"copyForForum": "Copy for Forum (Markdown)",
"commentUncomment": "Comment/Uncomment",
"increaseIndent": "Increase Indent",
"decreaseIndent": "Decrease Indent",
"increaseFontSize": "Increase Font Size",
"decreaseFontSize": "Decrease Font Size",
"autoFormat": "Auto Format"
},
"examples": {
"menu": "Examples",
"couldNotInitializeExamples": "Could not initialize built-in examples.",
"builtInExamples": "Built-in examples",
"customLibrary": "Examples from Custom Libraries",
"for": "Examples for {0}",
"forAny": "Examples for any board"
},
"help": {
"search": "Search on Arduino.cc",
"keyword": "Type a keyword",
"gettingStarted": "Getting Started",
"environment": "Environment",
"troubleshooting": "Troubleshooting",
"reference": "Reference",
"findInReference": "Find in Reference",
"faq": "Frequently Asked Questions",
"visit": "Visit Arduino.cc"
},
"certificate": {
"uploadRootCertificates": "Upload SSL Root Certificates",
"openContext": "Open context",
"remove": "Remove",
"upload": "Upload",
"addURL": "Add URL to fetch SSL certificate",
"enterURL": "Enter URL",
"selectCertificateToUpload": "1. Select certificate to upload",
"addNew": "Add New",
"selectDestinationBoardToUpload": "2. Select destination board and upload certificate",
"uploadingCertificates": "Uploading certificates.",
"certificatesUploaded": "Certificates uploaded.",
"uploadFailed": "Upload failed. Please try again.",
"selectBoard": "Select a board...",
"boardAtPort": "{0} at {1}",
"noSupportedBoardConnected": "No supported board connected"
},
"firmware": {
"updater": "WiFi101 / WiFiNINA Firmware Updater",
"selectBoard": "Select Board",
"checkUpdates": "Check Updates",
"selectVersion": "Select firmware version",
"install": "Install",
"overwriteSketch": "Installation will overwrite the Sketch on the board.",
"installingFirmware": "Installing firmware.",
"successfullyInstalled": "Firmware succesfully installed.",
"failedInstall": "Installation failed. Please try again."
},
"dialog": {
"dontAskAgain": "Don't ask again"
},
"userFields": {
"cancel": "Cancel",
"upload": "Upload"
},
"serial": {
"toggleTimestamp": "Toggle Timestamp",
"autoscroll": "Autoscroll",
"timestamp": "Timestamp",
"noLineEndings": "No Line Ending",
"newLine": "New Line",
"carriageReturn": "Carriage Return",
"newLineCarriageReturn": "Both NL & CR",
"notConnected": "Not connected. Select a board and a port to connect automatically.",
"message": "Message ({0} + Enter to send message to '{1}' on '{2}'",
"connectionBusy": "Connection failed. Serial port is busy: {0}",
"disconnected": "Disconnected {0} from {1}.",
"unexpectedError": "Unexpected error. Reconnecting {0} on port {1}.",
"failedReconnect": "Failed to reconnect {0} to serial port after 10 consecutive attempts. The {1} serial port is busy.",
"reconnect": "Reconnecting {0} to {1} in {2} seconds..."
},
"component": {
"uninstall": "Uninstall",
"uninstallMsg": "Do you want to uninstall {0}?",
"by": "by",
"version": "Version {0}",
"moreInfo": "More info",
"install": "INSTALL",
"filterSearch": "Filter your search..."
},
"electron": {
"couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.",
"unsavedChanges": "Οι μη αποθηκευμένες αλλαγές θα χαθούν."
},
"compile": {
"error": "Compilation error: {0}"
},
"upload": {
"error": "{0} error: {1}"
},
"burnBootloader": {
"error": "Error while burning the bootloader: {0}"
}
},
"theia": {
"core": {
"couldNotSave": "Δεν έγινε αποθήκευση του προγράμματος. Παρακαλώ αντιγράψτε ό,τι δεν έχει αποθηκευθεί στον αγαπημένο σας επεξεργαστή κειμένου, και επανεκινήστε το Ολοκληρωμένο Περιβάλλον Ανάπτυξης IDE.",
"offline": "Εκτός Σύνδεσης",
"daemonOffline": "CLI Daemon Offline",
"cannotConnectBackend": "Cannot connect to the backend.",
"cannotConnectDaemon": "Cannot connect to the CLI daemon."
},
"debug": {
"start": "Έναρξη...",
"typeNotSupported": "The debug session type \"{0}\" is not supported.",
"startError": "There was an error starting the debug session, check the logs for more details."
},
"editor": {
"unsavedTitle": "Unsaved {0}"
},
"messages": {
"expand": "Επαναφορά",
"collapse": "Ελαχιστοποίηση"
},
"workspace": {
"fileNewName": "Name for new file",
"invalidFilename": "Invalid filename.",
"invalidExtension": ".{0} is not a valid extension",
"newFileName": "Νέο όνομα για το αρχείο",
"deleteCurrentSketch": "Do you want to delete the current sketch?",
"sketchDirectoryError": "Υπήρξε ένα λάθος κατά τη δημιουργία του φακέλου. Για περισσότερες λεπτομέρειες δείτε το αρχείο καταγραφής. Η εφαρμογή πιθανόν δε θα δουλέψει όπως αναμένεται."
}
},
"cloud": {
"GoToCloud": "GO TO CLOUD"
}
}

View File

@@ -14,6 +14,22 @@
"saveChangesToSketch": "Do you want to save changes to this sketch before closing?",
"loseChanges": "If you don't save, your changes will be lost."
},
"ide-updater": {
"errorCheckingForUpdates": "Error while checking for Arduino IDE updates.\n{0}",
"notNowButton": "Not now",
"versionDownloaded": "Arduino IDE {0} has been downloaded.",
"closeToInstallNotice": "Close the software and install the update on your machine.",
"closeAndInstallButton": "Close and Install",
"downloadingNotice": "Downloading the latest version of the Arduino IDE.",
"updateAvailable": "Update Available",
"newVersionAvailable": "A new version of Arduino IDE ({0}) is available for download.",
"skipVersionButton": "Skip Version",
"downloadButton": "Download",
"goToDownloadPage": "An update for the Arduino IDE is available, but we're not able to download and install it automatically. Please go to the download page and download the latest version from there.",
"goToDownloadButton": "Go To Download",
"ideUpdaterDialog": "Software Update",
"noUpdatesAvailable": "There are no recent updates available for the Arduino IDE"
},
"menu": {
"sketch": "Sketch",
"tools": "Tools"
@@ -31,7 +47,7 @@
"upload.verbose": "True for verbose upload output. False by default.",
"window.autoScale": "True if the user interface automatically scales with the font size.",
"window.zoomLevel": "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.",
"ide.autoUpdate": "True to enable automatic update checks. The IDE will check for updates automatically and periodically.",
"ide.updateChannel": "Release channel to get updated from. 'stable' is the stable release, 'nightly' is the latest development build.",
"board.certificates": "List of certificates that can be uploaded to boards",
"sketchbook.showAllFiles": "True to show all sketch files inside the sketch. It is false by default.",
"cloud.enabled": "True if the sketch sync functions are enabled. Defaults to true.",
@@ -55,7 +71,6 @@
"compile": "compile",
"upload": "upload",
"verifyAfterUpload": "Verify code after upload",
"checkForUpdates": "Check for updates on startup",
"editorQuickSuggestions": "Editor Quick Suggestions",
"additionalManagerURLs": "Additional Boards Manager URLs",
"noProxy": "No proxy",
@@ -108,7 +123,7 @@
"installNow": "The \"{0} {1}\" core has to be installed for the currently selected \"{2}\" board. Do you want to install it now?",
"configDialogTitle": "Select Other Board & Port",
"configDialog1": "Select both a Board and a Port if you want to upload a sketch.",
"configDialog2": "If you only select a Board you will be able just to compile, but not to upload your sketch.",
"configDialog2": "If you only select a Board you will be able to compile, but not to upload your sketch.",
"pleasePickBoard": "Please pick a board connected to the port you have selected.",
"showAllAvailablePorts": "Shows all available ports when enabled",
"programmer": "Programmer",
@@ -173,7 +188,7 @@
"showFolder": "Show Sketch Folder",
"sketch": "Sketch",
"moving": "Moving",
"movingMsg": "The file \"{0}\" needs to be inside a sketch folder named as \"{1}\".\nCreate this folder, move the file, and continue?",
"movingMsg": "The file \"{0}\" needs to be inside a sketch folder named \"{1}\".\nCreate this folder, move the file, and continue?",
"cantOpen": "A folder named \"{0}\" already exists. Can't open sketch.",
"saveFolderAs": "Save sketch folder as...",
"sketchbook": "Sketchbook",
@@ -243,14 +258,14 @@
"noSupportedBoardConnected": "No supported board connected"
},
"firmware": {
"updater": "WiFi101 / WiFiNINA Firmware Updater",
"updater": "WiFi101 / WiFiNINA Firmware Updater",
"selectBoard": "Select Board",
"checkUpdates": "Check Updates",
"selectVersion": "Select firmware version",
"install": "Install",
"overwriteSketch": "Installation will overwrite the Sketch on the board.",
"installingFirmware": "Installing firmware.",
"successfullyInstalled": "Firmware succesfully installed.",
"successfullyInstalled": "Firmware successfully installed.",
"failedInstall": "Installation failed. Please try again."
},
"dialog": {
@@ -269,7 +284,7 @@
"carriageReturn": "Carriage Return",
"newLineCarriageReturn": "Both NL & CR",
"notConnected": "Not connected. Select a board and a port to connect automatically.",
"message": "Message ({0} + Enter to send message to '{1}' on '{2}'",
"message": "Message ({0} + Enter to send message to '{1}' on '{2}')",
"connectionBusy": "Connection failed. Serial port is busy: {0}",
"disconnected": "Disconnected {0} from {1}.",
"unexpectedError": "Unexpected error. Reconnecting {0} on port {1}.",

View File

@@ -1,18 +1,18 @@
{
"arduino": {
"common": {
"offlineIndicator": "You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.",
"noBoardSelected": "No board selected",
"offlineIndicator": "Al parecer no estás en línea. Sin una conexión a internet, el CLI de Arduino no podrá descargar los recursos necesarios, lo cual puede ocasionar fallos. Por favor, conecte a internet y reinicie la aplicación.",
"noBoardSelected": "Ninguna placa seleccionada.",
"selectedOn": "on {0}",
"notConnected": "[not connected]",
"serialMonitor": "Monitor Serie",
"oldFormat": "The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?",
"oldFormat": "El '{0}' aún usa el antiguo formato `.pde` . ¿Deseas cambiar a la nueva extensión `.ino`?.",
"later": "Later",
"selectBoard": "Select Board",
"selectBoard": "Seleccionar Placa",
"unknown": "Unknown",
"processing": "Processing",
"saveChangesToSketch": "Do you want to save changes to this sketch before closing?",
"loseChanges": "If you don't save, your changes will be lost."
"processing": "Procesando",
"saveChangesToSketch": "¿Deseas guardar los cambios en este código antes de cerrar?",
"loseChanges": "Si no guardas, perderás los cambios."
},
"menu": {
"sketch": "Programa",
@@ -21,8 +21,8 @@
"debug": {
"optimizeForDebugging": "Optimize for Debugging",
"debugWithMessage": "Debug - {0}",
"noPlatformInstalledFor": "Platform is not installed for '{0}'",
"debuggingNotSupported": "Debugging is not supported by '{0}'"
"noPlatformInstalledFor": "La plataforma no está instalada para '{0}'",
"debuggingNotSupported": "La depuracion no esta soportada por '{0}'"
},
"preferences": {
"language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.",
@@ -34,7 +34,7 @@
"ide.autoUpdate": "True to enable automatic update checks. The IDE will check for updates automatically and periodically.",
"board.certificates": "List of certificates that can be uploaded to boards",
"sketchbook.showAllFiles": "True to show all sketch files inside the sketch. It is false by default.",
"cloud.enabled": "True if the sketch sync functions are enabled. Defaults to true.",
"cloud.enabled": "Verdadero si la sincronización de código está activada. Valor verdadero por defecto.",
"cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.",
"cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.",
"cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.",
@@ -52,11 +52,11 @@
"showVerbose": "Show verbose output during",
"compilerWarnings": "Compiler warnings",
"automatic": "Automático",
"compile": "compile",
"upload": "upload",
"verifyAfterUpload": "Verify code after upload",
"compile": "Compliar",
"upload": "Subir",
"verifyAfterUpload": "Verificar código después de subir",
"checkForUpdates": "Comprobar actualizaciones al iniciar",
"editorQuickSuggestions": "Editor Quick Suggestions",
"editorQuickSuggestions": "Sugerencias rápidas del editor",
"additionalManagerURLs": "Gestor de URLs Adicionales de Tarjetas",
"noProxy": "No proxy",
"manualProxy": "Manual proxy configuration",
@@ -65,24 +65,24 @@
"enterAdditionalURLs": "Enter additional URLs, one for each row",
"unofficialBoardSupport": "Click for a list of unofficial board support URLs",
"invalid.sketchbook.location": "Invalid sketchbook location: {0}",
"invalid.editorFontSize": "Invalid editor font size. It must be a positive integer.",
"invalid.theme": "Invalid theme."
"invalid.editorFontSize": "Tamaño de fuente invalido. Debe ser un entero positivo.",
"invalid.theme": "Tema no valido."
},
"cloud": {
"signIn": "SIGN IN",
"signOut": "Sign Out",
"chooseSketchVisibility": "Choose visibility of your Sketch:",
"privateVisibility": "Private. Only you can view the Sketch.",
"publicVisibility": "Public. Anyone with the link can view the Sketch.",
"link": "Link:",
"privateVisibility": "Privado: Solo tú puedes ver el código.",
"publicVisibility": "Público: Cualquiera con el enlace puede ver el código.",
"link": "Enlace:",
"embed": "Embed:",
"cloudSketchbook": "Cloud Sketchbook",
"shareSketch": "Share Sketch",
"shareSketch": "Compartir código",
"showHideRemoveSketchbook": "Show/Hide Remote Sketchbook",
"pullSketch": "Pull Sketch",
"openInCloudEditor": "Open in Cloud Editor",
"options": "Options...",
"share": "Share...",
"openInCloudEditor": "Abrir en editor web",
"options": "Opciones...",
"share": "Compartir...",
"remote": "Remote",
"emptySketchbook": "Your Sketchbook is empty",
"visitArduinoCloud": "Visit Arduino Cloud to create Cloud Sketches.",
@@ -99,19 +99,19 @@
"push": "Push",
"pullFirst": "You have to pull first to be able to push to the Cloud.",
"donePushing": "Done pushing {0}.",
"connected": "Connected",
"connected": "Conectado",
"offline": "Offline",
"profilePicture": "Profile picture"
"profilePicture": "Foto de perfil"
},
"board": {
"installManually": "Install Manually",
"installManually": "Instalar manualmente",
"installNow": "The \"{0} {1}\" core has to be installed for the currently selected \"{2}\" board. Do you want to install it now?",
"configDialogTitle": "Select Other Board & Port",
"configDialog1": "Select both a Board and a Port if you want to upload a sketch.",
"configDialog2": "If you only select a Board you will be able just to compile, but not to upload your sketch.",
"pleasePickBoard": "Please pick a board connected to the port you have selected.",
"showAllAvailablePorts": "Shows all available ports when enabled",
"programmer": "Programmer",
"programmer": "Programador",
"succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}",
"succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}",
"couldNotFindPreviouslySelected": "Could not find previously selected board '{0}' in installed platform '{1}'. Please manually reselect the board you want to use. Do you want to reselect it now?",
@@ -223,7 +223,7 @@
"reference": "Reference",
"findInReference": "Find in Reference",
"faq": "Frequently Asked Questions",
"visit": "Visit Arduino.cc"
"visit": "Visitar Arduino.cc"
},
"certificate": {
"uploadRootCertificates": "Upload SSL Root Certificates",

Some files were not shown because too many files have changed in this diff Show More