Compare commits

...

55 Commits

Author SHA1 Message Date
Alberto Iannaccone
2be1fac585 ignore workspace root check in changelog workflow (#960) 2022-04-20 15:23:52 +02:00
Alberto Iannaccone
b35340caa9 2.0.0-rc6 (#955) 2022-04-20 11:53:06 +02:00
Alberto Iannaccone
e6b3e2ec23 fix update version script (#958) 2022-04-19 16:04:08 +02:00
Mark Sujew
c07232698c Allow to close files in certain folders (#946)
* Allow to close files in certain folders

* Only direct children are sketch files
2022-04-19 12:00:15 +02:00
github-actions[bot]
58e992af13 Updated translation files (#959)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-04-19 11:59:58 +02:00
Alberto Iannaccone
a44b84ffd0 set the current language on the localization provider (#957) 2022-04-15 15:54:37 +02:00
Alberto Iannaccone
a3640cf812 use electron reload command when changing language (#953) 2022-04-14 09:38:23 +02:00
github-actions[bot]
03a75273e3 Updated translation files (#950)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-04-12 11:36:08 +02:00
Alberto Iannaccone
6176e50acf Enable language packs (#941)
* install language packs

* register localization contribution to backend module

* copy i18n folder to build

* fix chinese language iid
2022-04-08 14:59:11 +02:00
Alberto Iannaccone
46a3466bc5 improve check of read-only files (#918) 2022-04-07 16:45:09 +02:00
Alberto Iannaccone
aba9db6a6b Correctly print backslash-escaped characters (#943) 2022-04-06 18:05:32 +02:00
per1234
e5b34624ac Disable automatic application start after install via Windows Installer (#942)
Arduino IDE is packaged for Windows in multiple formats:

- ZIP
- NSIS
- Windows Installer (AKA "MSI")

The interactive installer of the NSIS package makes it the best option for installation by users.

The other use case for the installers is deployment by a system administrator. The Windows Installer package was added
to offer an additional installer option for this specific use case.

In this use case, a "silent install" will often be required. Previously, the Windows Installer package was configured to
start the Arduino IDE after completing the installation. This behavior is likely to be problematic for the very use case
the Windows Installer package was intended for. That configuration was not intentional, but rather a result of using
whatever setting electron-builder happened to provide as a default.

The behavior of the Windows Installer package is hereby changed to not run after installation. This also aligns it with
the behavior of the NSIS package's silent installation (running the installer with the `/S` flag).

The behavior of the NSIS installer is unchanged:

- When in interactive mode: user chooses whether to start Arduino IDE
- When in silent mode: Arduino IDE does not start after installation
2022-04-06 10:56:33 +02:00
Mark Sujew
c430cf0d88 Disable widget dragging/splitting (#940) 2022-04-05 12:21:49 +02:00
github-actions[bot]
1969e292f0 Updated translation files (#768)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-04-04 16:58:04 +02:00
Akos Kitta
0db119d7ba #919, #881: Fixed 3rd party URLs-related issues (#920)
* Fixed empty string to URLs conversion

Closes #919.

Signed-off-by: Akos Kitta <kittaakos@gmail.com>

* #881: Fixed height of the 3rd part URLs `textarea`

Closes #881.

Signed-off-by: Akos Kitta <kittaakos@gmail.com>
2022-04-04 16:52:55 +02:00
Francesco Spissu
c9b498fb08 add notes for Windows contributor in BUILDING.md (#926)
* add notes for Windows contributor in BUILDING.md

* rephrase notes for Windows contributor in BUILDING.md

* Update notes for Windows contributor in BUILDING.md

Co-authored-by: per1234 <accounts@perglass.com>

* move Notes for Windows contributors in Build from source section

Co-authored-by: per1234 <accounts@perglass.com>
2022-03-29 17:53:16 +02:00
Akos Kitta
78004fa4ca Minified browser code in the packaged final app. (#931)
- Also switched to minified `monaco` code,
- Removed dead code from the packaged.

Signed-off-by: Akos Kitta <kittaakos@gmail.com>
2022-03-29 17:45:54 +02:00
Mark Sujew
4de7737d14 Automatically remove editors for deleted files (#894) 2022-03-21 10:44:51 +01:00
per1234
f36df02f5d Switch to form-based GitHub issue templates
This project provides the contributors with templates for the fundamental categories of issues:

- bug report
- feature request

This is helpful to the maintainers and developers because it establishes a standardized framework for the issues and
encourages the contributors to provide the essential information.

GitHub's original issue template system is very crude, simply pre-populating the issue description field with the text
from the template file.

https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#creating-issue-templates

The contributor may be confused by being presented with a mass of Markdown and placeholder
content where they expected a field to write their issue. They also may find it inconvenient to manuever around the
framework content and replace the placeholder content.

A far better system is now available with GitHub's recently introduced form-based issue templates:

https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#creating-issue-forms

The user is now presented with a web form. These may include multi-line input fields that have the same formatting and
attachment capabilities as the standard GitHub Issue composer, but also additional elements such as menus and checkboxes.

The use of this form-based system should provide a much better experience for the contributors and also result in higher
quality issues.
2022-03-17 03:20:12 -07:00
per1234
753872ea2a Add links for other communication channels to the GitHub issue template chooser
The automatically created issue template chooser provides a menu of links to the available issue report templates as
well as the security policy at the start of the issue creation process.

It is also possible to add additional arbitrary items to the chooser, through GitHub's "Contact Links" feature. These
are defined in a configuration file:

https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser

These links offer the possibility to redirect support requests and other sub-optimal usages of the issues to the
appropriate location. This benefits the user by providing them with a fast and frictionless path to what they need, and
benefits the maintainers by preventing inappropriate issues.
2022-03-17 03:18:41 -07:00
Mark Sujew
ca1c24050d Fix Find Next command 2022-03-15 14:48:32 +01:00
Alberto Iannaccone
61c2b1a007 Install oktokit in changelog workflow (#901)
* install oktokit in changelog workflow

* fix how the old changelog is cut off
2022-03-14 12:05:53 +01:00
Alberto Iannaccone
8cac0872a4 Version 2.0.0-rc5 (#897) 2022-03-10 12:02:13 +01:00
Alberto Iannaccone
70f1c5f8ec Add privacy policy menu item (#883) 2022-03-09 11:46:22 +01:00
ulemons
b416e5f9e8 handling pagination in getting the sketches (#875)
Co-authored-by: Umberto Sgueglia <umberto.sgueglia@external.fcagroup.com>
2022-03-08 17:11:18 +01:00
per1234
bfe6835cab Remove irrelevant statement from EULA
When using the interactive installer, the user is presented with a dialog requested they agree to this.

The previous statement about initiation of a download constituting agreement is relevant in the context of the text's source on the arduino.cc downloads page, but not at all in the context of the installer dialog.
2022-03-08 07:54:12 -08:00
Alberto Iannaccone
9e89964df2 remove dev tools menu item (#882) 2022-03-08 07:38:00 +00:00
Alberto Iannaccone
04c3d0c1d3 Fix sketch name duplicates (#887) 2022-03-07 16:34:16 +00:00
per1234
c9996df11c Add Linux AppImage to nightly build download links
Linux x86-64 builds of the Arduino IDE are now available in AppImage format in additional to the ZIP format.

Since only the AppImage format IDE supports auto-updates (the IDE installed from the ZIP will notify of available updates, but can't auto-update), this will be the preferred format and so good beta testing coverage of it is especially important.
2022-03-07 03:30:31 -08:00
per1234
49971ada07 Remove irrelevant trigger from "Compose full changelog" workflow
The "Compose full changelog" GitHub Actions workflow generates a changelog file from the release notes and uploads this
to Arduino's server for display to the user by the IDE updater.

Previously, this workflow could be triggered by either of two events:

- Release creation
- Release edit

To reduce the possibility of endless recursion, GitHub Actions ignores events which are triggered using the
auto-generated `GITHUB_TOKEN` access token. All release creations are done automatically by the "Arduino IDE" GitHub
Actions workflow, which uses this token.

For this reason, the release creation trigger will never be used. Since the behavior of the event being ignored by
GitHub Actions under these conditions is not at all obvious, having the workflow configured for such an irrelevant
trigger can cause confusion.

The workflow will be triggered by the manual edit which is done on every release to format the raw release notes
auto-generated from the commit history. So the fact that the release creation trigger doesn't work is not a problem.
2022-03-04 00:41:24 -08:00
Mark Sujew
e6b9d4e2aa Override the RELOAD_REQUESTED_SIGNAL correctly (#880) 2022-03-03 14:37:37 +00:00
Francesco Stasi
93a374d0c6 add PR template file (#838)
* add PR template file

* Update .github/PULL_REQUEST_TEMPLATE.md

Co-authored-by: per1234 <accounts@perglass.com>

* Update .github/PULL_REQUEST_TEMPLATE.md

Co-authored-by: per1234 <accounts@perglass.com>

Co-authored-by: per1234 <accounts@perglass.com>
2022-03-03 13:54:05 +00:00
Alberto Iannaccone
0fc7c78e11 Install Node.js 14.x on compose-full-changelog workflow (#878)
* Install Node.js 14.x on compose-full-changelog workflow

* change date formate in changelog file name

* improve node js installation in workflow
2022-03-03 08:38:48 +00:00
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
125 changed files with 8133 additions and 3228 deletions

72
.github/ISSUE_TEMPLATE/bug-report.yml vendored Normal file
View File

@@ -0,0 +1,72 @@
name: Bug report
description: Report a problem with the code or documentation in this repository.
labels:
- "type: imperfection"
body:
- type: textarea
id: description
attributes:
label: Describe the problem
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: To reproduce
description: Provide the specific set of steps we can follow to reproduce the problem.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
description: What would you expect to happen after following those instructions?
validations:
required: true
- type: input
id: project-version
attributes:
label: Arduino IDE version
description: |
Which version of the Arduino IDE are you using?
See **Help > About Arduino IDE** in the Arduino IDE menus (**Arduino IDE > About Arduino IDE** on macOS).
This should be the latest [nightly build](https://github.com/arduino/arduino-ide#nightly-builds).
validations:
required: true
- type: dropdown
id: os
attributes:
label: Operating system
description: Which operating system are you using on your computer?
options:
- Windows
- Linux
- macOS
validations:
required: true
- type: input
id: os-version
attributes:
label: Operating system version
description: Which version of the operating system are you using on your computer?
validations:
required: true
- type: textarea
id: additional
attributes:
label: Additional context
description: Add any additional information here.
validations:
required: false
- type: checkboxes
id: checklist
attributes:
label: Issue checklist
description: Please double-check that you have done each of the following things before submitting the issue.
options:
- label: I searched for previous reports in [the issue tracker](https://github.com/arduino/arduino-ide/issues?q=)
required: true
- label: I verified the problem still occurs when using the latest [nightly build](https://github.com/arduino/arduino-ide#nightly-builds)
required: true
- label: My report contains all necessary details
required: true

View File

@@ -1,32 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'type: imperfection'
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Windows]
- Version: [e.g. 2.0.0]
**Additional context**
Add any other context about the problem here.

13
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
# Source:
# https://github.com/arduino/tooling-project-assets/blob/main/issue-templates/template-choosers/general/config.yml
blank_issues_enabled: false
contact_links:
- name: Learn about using this project
url: https://github.com/arduino/arduino-ide#readme
about: Detailed usage documentation is available here.
- name: Support request
url: https://forum.arduino.cc/
about: We can help you out on the Arduino Forum!
- name: Discuss development work on the project
url: https://groups.google.com/a/arduino.cc/g/developers
about: Arduino Developers Mailing List

View File

@@ -0,0 +1,67 @@
name: Feature request
description: Suggest an enhancement to this project.
labels:
- "type: enhancement"
body:
- type: textarea
id: description
attributes:
label: Describe the request
validations:
required: true
- type: textarea
id: current
attributes:
label: Describe the current behavior
description: |
What is the current behavior of the Arduino IDE in relation to your request?
How can we reproduce that behavior?
validations:
required: true
- type: input
id: project-version
attributes:
label: Arduino IDE version
description: |
Which version of the Arduino IDE are you using?
See **Help > About Arduino IDE** in the Arduino IDE menus (**Arduino IDE > About Arduino IDE** on macOS).
This should be the latest [nightly build](https://github.com/arduino/arduino-ide#nightly-builds).
validations:
required: true
- type: dropdown
id: os
attributes:
label: Operating system
description: Which operating system are you using on your computer?
options:
- Windows
- Linux
- macOS
validations:
required: true
- type: input
id: os-version
attributes:
label: Operating system version
description: Which version of the operating system are you using on your computer?
validations:
required: true
- type: textarea
id: additional
attributes:
label: Additional context
description: Add any additional information here.
validations:
required: false
- type: checkboxes
id: checklist
attributes:
label: Issue checklist
description: Please double-check that you have done each of the following things before submitting the issue.
options:
- label: I searched for previous requests in [the issue tracker](https://github.com/arduino/arduino-ide/issues?q=)
required: true
- label: I verified the feature was still missing when using the latest [nightly build](https://github.com/arduino/arduino-ide#nightly-builds)
required: true
- label: My request contains all necessary details
required: true

View File

@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: 'type: enhancement'
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

15
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,15 @@
### Motivation
<!-- Why this pull request? -->
### Change description
<!-- What does your code do? -->
### Other information
<!-- Any additional information that could help the review process -->
### Reviewer checklist
* [ ] PR addresses a single concern.
* [ ] The PR has no duplicates (please search among the [Pull Requests](https://github.com/arduino/arduino-ide/pulls) before creating one)
* [ ] PR title and description are properly filled.
* [ ] Docs have been added / updated (for bug fixes / features)

View File

@@ -23,7 +23,7 @@ jobs:
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,6 +50,7 @@ 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') }}
@@ -96,9 +97,13 @@ jobs:
matrix:
artifact:
- path: '*Linux_64bit.zip'
name: Linux_X86-64
name: Linux_X86-64_zip
- path: '*Linux_64bit.AppImage'
name: Linux_X86-64_app_image
- path: '*macOS_64bit.dmg'
name: macOS
name: macOS_dmg
- path: '*macOS_64bit.zip'
name: macOS_zip
- path: '*Windows_64bit.exe'
name: Windows_X86-64_interactive_installer
- path: '*Windows_64bit.msi'

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

@@ -2,10 +2,13 @@ name: Compose full changelog
on:
release:
types: [created, edited]
types:
- edited
env:
CHANGELOG_ARTIFACTS: changelog
# See: https://github.com/actions/setup-node/#readme
NODE_VERSION: 14.x
jobs:
create-changelog:
@@ -15,6 +18,12 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
registry-url: 'https://registry.npmjs.org'
- name: Get Tag
id: tag_name
run: |
@@ -23,10 +32,11 @@ jobs:
- name: Create full changelog
id: full-changelog
run: |
yarn add @octokit/rest --ignore-workspace-root-check
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"
CHANGELOG_FILE_NAME="${{ steps.tag_name.outputs.TAG_NAME }}-$(date +%s).md"
# Create manifest file pointing to latest changelog file name
echo "$CHANGELOG_FILE_NAME" >> "${{ github.workspace }}/${{ env.CHANGELOG_ARTIFACTS }}/latest.txt"

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

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

@@ -42,6 +42,7 @@ The _frontend_ is running as an Electron renderer process and can invoke service
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.
> **Note**: Node.js 14 must be used instead of the version 12 recommended at the link above.
Once you have all the tools installed, you can build the editor following these steps
@@ -57,9 +58,7 @@ Once you have all the tools installed, you can build the editor following these
3. Rebuild the electron dependencies
```sh
cd electron-app
yarn theia rebuild:electron
cd ..
yarn rebuild:electron
```
4. Start the application
@@ -67,6 +66,13 @@ Once you have all the tools installed, you can build the editor following these
yarn start
```
### Notes for Windows contributors
Windows requires the Microsoft Visual C++ (MSVC) compiler toolset to be installed on your development machine.
In case it's not already present, it can be downloaded from the "**Tools for Visual Studio 20XX**" section of the Visual Studio [downloads page](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022) via the "**Build Tools for Visual Studio 20XX**" (e.g., "**Build Tools for Visual Studio 2022**") download link.
Select "**Desktop development with C++**" from the "**Workloads**" tab during the installation procedure.
### CI
This project is built on [GitHub Actions](https://github.com/arduino/arduino-ide/actions).
@@ -83,6 +89,7 @@ This project is built on [GitHub Actions](https://github.com/arduino/arduino-ide
git push origin 1.2.3
```
## Notes for macOS contributors
Beginning in macOS 10.14.5, the software [must be notarized to run](https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution). The signing and notarization processes for the Arduino IDE are managed by our Continuous Integration (CI) workflows, implemented with GitHub Actions. On every push and pull request, the Arduino IDE is built and saved to a workflow artifact. These artifacts can be used by contributors and beta testers who don't want to set up a build system locally.
For security reasons, signing and notarization are disabled for workflow runs for pull requests from forks of this repository. This means that macOS will block you from running those artifacts.

View File

@@ -23,13 +23,14 @@ should be considered unstable:
| Platform | 32 bit | 64 bit |
| --------- | ------------------------ | ------------------------------------------------------------------------------------------------------ |
| Linux | | [Nightly Linux 64 bit] |
| Linux | | [Nightly Linux AppImage 64 bit]<br />[Nightly Linux ZIP file 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 linux appimage 64 bit]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Linux_64bit.AppImage
[nightly linux zip file 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

View File

@@ -1,15 +1,16 @@
{
"name": "arduino-ide-extension",
"version": "2.0.0-rc3",
"version": "2.0.0-rc6",
"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",
"prepare": "yarn download-cli && yarn download-fwuploader && yarn download-ls && yarn copy-serial-plotter && yarn copy-i18n && 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",
"copy-i18n": "npx ncp ../i18n ./build/i18n",
"download-ls": "node ./scripts/download-ls.js",
"download-examples": "node ./scripts/download-examples.js",
"generate-protocol": "node ./scripts/generate-protocol.js",
@@ -21,22 +22,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",
@@ -64,6 +66,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",
@@ -81,6 +84,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",
@@ -153,7 +157,7 @@
],
"arduino": {
"cli": {
"version": "0.20.2"
"version": "0.21.0"
},
"fwuploader": {
"version": "2.0.0"

View File

@@ -1,36 +1,42 @@
// @ts-check
(async () => {
const { Octokit } = require('@octokit/rest');
const fs = require("fs");
const path = require("path");
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 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) => {
let fullChangelog = releases.reduce((acc, item, index) => {
// Process each line separately
const body = item.body.split('\n').map(processLine).join('\n')
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}\n\n---\n\n`;
return (
acc +
`## ${item.name}\n\n${body}${
index !== releases.length - 1 ? '\n\n---\n\n' : '\n'
}`
);
}, '');
const args = process.argv.slice(2)
const args = process.argv.slice(2);
if (args.length == 0) {
console.error("Missing argument to destination file")
process.exit(1)
console.error('Missing argument to destination file');
process.exit(1);
}
const changelogFile = path.resolve(args[0]);
@@ -38,19 +44,18 @@
changelogFile,
fullChangelog,
{
flag: "w+",
flag: 'w+',
},
err => {
(err) => {
if (err) {
console.error(err);
process.exit(1);
}
console.log("Changelog written to", changelogFile);
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.
@@ -61,7 +66,8 @@ const processLine = (line) => {
// * [#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;
let r =
/(\(|\[)#\d+(\)|\])(\(|\[)https:\/\/github\.com\/arduino\/arduino-ide\/(pull|issues)\/(\d+)\/?(\)|\])/gm;
if (r.test(line)) {
return line;
}
@@ -70,9 +76,12 @@ const processLine = (line) => {
// * #123
// If it does it's changed to:
// * [#123](https://github.com/arduino/arduino-ide/pull/123)
r = /#(\d+)/gm;
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)`)
return line.replace(
r,
`[#$1](https://github.com/arduino/arduino-ide/pull/$1)`
);
}
// Check if a link with one of the following format exists:
@@ -85,7 +94,8 @@ const processLine = (line) => {
// * [#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;
r =
/(https:\/\/github\.com\/arduino\/arduino-ide\/(pull|issues)\/(\d+)\/?)/gm;
if (r.test(line)) {
return line.replace(r, `[#$3]($1)`);
}
@@ -95,11 +105,12 @@ const processLine = (line) => {
// * 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;
r =
/(https:\/\/github\.com\/arduino\/arduino-ide\/compare\/([^\/]*))\/?\s?/gm;
if (r.test(line)) {
return line.replace(r, '[`$2`]($1)');;
return line.replace(r, '[`$2`]($1)');
}
// If nothing matches just return the line as is
return line;
}
};

View File

@@ -1,6 +1,6 @@
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,
SketchesService,
@@ -20,6 +20,7 @@ import {
FrontendApplication,
FrontendApplicationContribution,
LocalStorageService,
SaveableWidget,
StatusBar,
StatusBarAlignment,
} from '@theia/core/lib/browser';
@@ -38,13 +39,14 @@ import {
import { MessageService } from '@theia/core/lib/common/message-service';
import URI from '@theia/core/lib/common/uri';
import {
EditorCommands,
EditorMainMenu,
EditorManager,
EditorOpenerOptions,
} from '@theia/editor/lib/browser';
import { ProblemContribution } from '@theia/markers/lib/browser/problem/problem-contribution';
import { MonacoMenus } from '@theia/monaco/lib/browser/monaco-menu';
import { FileNavigatorContribution } from '@theia/navigator/lib/browser/navigator-contribution';
import { FileNavigatorCommands, FileNavigatorContribution } from '@theia/navigator/lib/browser/navigator-contribution';
import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
import { OutputContribution } from '@theia/output/lib/browser/output-contribution';
import { ScmContribution } from '@theia/scm/lib/browser/scm-contribution';
@@ -68,8 +70,12 @@ 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';
import { FileSystemFrontendContribution } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution';
const INIT_LIBS_AND_PACKAGES = 'initializedLibsAndPackages';
export const SKIP_IDE_VERSION = 'skipIDEVersion';
@injectable()
export class ArduinoFrontendContribution
@@ -78,8 +84,7 @@ export class ArduinoFrontendContribution
TabBarToolbarContribution,
CommandContribution,
MenuContribution,
ColorContribution
{
ColorContribution {
@inject(ILogger)
protected logger: ILogger;
@@ -157,6 +162,15 @@ export class ArduinoFrontendContribution
@inject(LocalStorageService)
protected readonly localStorageService: LocalStorageService;
@inject(FileSystemFrontendContribution)
protected readonly fileSystemFrontendContribution: FileSystemFrontendContribution;
@inject(IDEUpdater)
protected readonly updater: IDEUpdater;
@inject(IDEUpdaterDialog)
protected readonly updaterDialog: IDEUpdaterDialog;
protected invalidConfigPopup:
| Promise<void | 'No' | 'Yes' | undefined>
| undefined;
@@ -251,7 +265,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,
@@ -266,6 +280,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;
@@ -276,11 +315,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(() => {
@@ -288,17 +341,19 @@ 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');
this.fileSystemFrontendContribution.onDidChangeEditorFile(e => {
if (e.type === FileChangeType.DELETED) {
const editorWidget = e.editor;
if (SaveableWidget.is(editorWidget)) {
editorWidget.closeWithoutSaving();
} else {
editorWidget.close();
}
}
});
app.shell.leftPanelHandler.removeBottomMenu('settings-menu');
}
onStop(): void {
@@ -431,6 +486,18 @@ export class ArduinoFrontendContribution
}
},
});
for (const command of [
EditorCommands.SPLIT_EDITOR_DOWN,
EditorCommands.SPLIT_EDITOR_LEFT,
EditorCommands.SPLIT_EDITOR_RIGHT,
EditorCommands.SPLIT_EDITOR_UP,
EditorCommands.SPLIT_EDITOR_VERTICAL,
EditorCommands.SPLIT_EDITOR_HORIZONTAL,
FileNavigatorCommands.REVEAL_IN_NAVIGATOR
]) {
registry.unregisterCommand(command);
}
}
registerMenus(registry: MenuModelRegistry) {

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

@@ -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

@@ -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';
@@ -39,10 +39,6 @@ export class AddZipLibrary extends SketchContribution {
...ArduinoMenus.SKETCH__UTILS_GROUP,
'0_include',
];
// TODO: do we need it? calling `registerSubmenu` multiple times is noop, so it does not hurt.
registry.registerSubmenu(includeLibMenuPath, 'Include Library', {
order: '1',
});
registry.registerMenuAction([...includeLibMenuPath, '1_install'], {
commandId: AddZipLibrary.Commands.ADD_ZIP_LIBRARY.id,
label: nls.localize('arduino/library/addZip', 'Add .ZIP Library...'),

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,

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

@@ -43,10 +43,10 @@ export class EditContributions extends Contribution {
execute: () => this.run('actions.find'),
});
registry.registerCommand(EditContributions.Commands.FIND_NEXT, {
execute: () => this.run('actions.findWithSelection'),
execute: () => this.run('editor.action.nextMatchFindAction'),
});
registry.registerCommand(EditContributions.Commands.FIND_PREVIOUS, {
execute: () => this.run('editor.action.nextMatchFindAction'),
execute: () => this.run('editor.action.previousMatchFindAction'),
});
registry.registerCommand(EditContributions.Commands.USE_FOR_FIND, {
execute: () => this.run('editor.action.previousSelectionMatchFindAction'),

View File

@@ -13,6 +13,8 @@ import {
KeybindingRegistry,
} from './contribution';
import { nls } from '@theia/core/lib/common';
import { IDEUpdaterCommands } from '../ide-updater/ide-updater-commands';
import { ElectronCommands } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
@injectable()
export class Help extends Contribution {
@@ -83,9 +85,17 @@ export class Help extends Contribution {
Help.Commands.VISIT_ARDUINO,
createOpenHandler('https://www.arduino.cc/')
);
registry.registerCommand(
Help.Commands.PRIVACY_POLICY,
createOpenHandler('https://www.arduino.cc/en/privacy-policy')
);
}
registerMenus(registry: MenuModelRegistry): void {
registry.unregisterMenuAction({
commandId: ElectronCommands.TOGGLE_DEVELOPER_TOOLS.id,
});
registry.registerMenuAction(ArduinoMenus.HELP__MAIN_GROUP, {
commandId: Help.Commands.GETTING_STARTED.id,
order: '0',
@@ -115,6 +125,14 @@ export class Help extends Contribution {
commandId: Help.Commands.VISIT_ARDUINO.id,
order: '6',
});
registry.registerMenuAction(ArduinoMenus.HELP__FIND_GROUP, {
commandId: Help.Commands.PRIVACY_POLICY.id,
order: '7',
});
registry.registerMenuAction(ArduinoMenus.HELP__FIND_GROUP, {
commandId: IDEUpdaterCommands.CHECK_FOR_UPDATES.id,
order: '8',
});
}
registerKeybindings(registry: KeybindingRegistry): void {
@@ -162,5 +180,10 @@ export namespace Help {
label: nls.localize('arduino/help/visit', 'Visit Arduino.cc'),
category: 'Arduino',
};
export const PRIVACY_POLICY: Command = {
id: 'arduino-privacy-policy',
label: nls.localize('arduino/help/privacyPolicy', 'Privacy Policy'),
category: 'Arduino',
};
}
}

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 {

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

@@ -100,14 +100,29 @@ export class CreateApi {
return result;
}
async sketches(): Promise<Create.Sketch[]> {
async sketches(limit = 50): Promise<Create.Sketch[]> {
const url = new URL(`${this.domain()}/sketches`);
url.searchParams.set('user_id', 'me');
url.searchParams.set('limit', limit.toString());
const headers = await this.headers();
const result = await this.run<{ sketches: Create.Sketch[] }>(url, {
method: 'GET',
headers,
});
const result: { sketches: Create.Sketch[] } = { sketches: [] };
let partialSketches: Create.Sketch[] = [];
let currentOffset = 0;
do {
url.searchParams.set('offset', currentOffset.toString());
partialSketches = (
await this.run<{ sketches: Create.Sketch[] }>(url, {
method: 'GET',
headers,
})
).sketches;
if (partialSketches.length != 0) {
result.sketches = result.sketches.concat(partialSketches);
}
currentOffset = currentOffset + limit;
} while (partialSketches.length != 0);
result.sketches.forEach((sketch) => this.sketchCache.addSketch(sketch));
return result.sketches;
}

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

@@ -9,6 +9,7 @@ import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { FileDialogService } from '@theia/filesystem/lib/browser/file-dialog/file-dialog-service';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import {
AdditionalUrls,
CompilerWarningLiterals,
Network,
ProxySettings,
@@ -35,21 +36,32 @@ export class SettingsComponent extends React.Component<
if (
this.state &&
prevState &&
JSON.stringify(this.state) !== JSON.stringify(prevState)
JSON.stringify(SettingsComponent.State.toSettings(this.state)) !==
JSON.stringify(SettingsComponent.State.toSettings(prevState))
) {
this.props.settingsService.update(this.state, true);
this.props.settingsService.update(
SettingsComponent.State.toSettings(this.state),
true
);
}
}
componentDidMount(): void {
this.props.settingsService
.settings()
.then((settings) => this.setState(settings));
this.toDispose.push(
.then((settings) =>
this.setState(SettingsComponent.State.fromSettings(settings))
);
this.toDispose.pushAll([
this.props.settingsService.onDidChange((settings) =>
this.setState(settings)
)
);
this.setState((prevState) => ({
...SettingsComponent.State.merge(prevState, settings),
}))
),
this.props.settingsService.onDidReset((settings) =>
this.setState(SettingsComponent.State.fromSettings(settings))
),
]);
}
componentWillUnmount(): void {
@@ -260,18 +272,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"
@@ -302,8 +302,8 @@ export class SettingsComponent extends React.Component<
<input
className="theia-input stretch with-margin"
type="text"
value={this.state.additionalUrls.join(',')}
onChange={this.additionalUrlsDidChange}
value={this.state.rawAdditionalUrlsValue}
onChange={this.rawAdditionalUrlsValueDidChange}
/>
<i
className="fa fa-window-restore theia-button shrink"
@@ -444,7 +444,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 +456,7 @@ export class SettingsComponent extends React.Component<
protected numbersOnlyKeyDown = (
event: React.KeyboardEvent<HTMLInputElement>
) => {
): void => {
if (this.isControlKey(event)) {
return;
}
@@ -466,7 +468,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,42 +485,44 @@ export class SettingsComponent extends React.Component<
}
};
protected editAdditionalUrlDidClick = async () => {
protected editAdditionalUrlDidClick = async (): Promise<void> => {
const additionalUrls = await new AdditionalUrlsDialog(
this.state.additionalUrls,
AdditionalUrls.parse(this.state.rawAdditionalUrlsValue, ','),
this.props.windowService
).open();
if (additionalUrls) {
this.setState({ additionalUrls });
this.setState({
rawAdditionalUrlsValue: AdditionalUrls.stringify(additionalUrls),
});
}
};
protected editorFontSizeDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
const { value } = event.target;
if (value) {
this.setState({ editorFontSize: parseInt(value, 10) });
}
};
protected additionalUrlsDidChange = (
protected rawAdditionalUrlsValueDidChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
): void => {
this.setState({
additionalUrls: event.target.value.split(',').map((url) => url.trim()),
rawAdditionalUrlsValue: event.target.value,
});
};
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 +536,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 +568,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 +580,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 +597,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 +628,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 +638,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 +648,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 +658,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 +666,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 +678,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 +688,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;
@@ -709,5 +713,48 @@ export namespace SettingsComponent {
readonly windowService: WindowService;
readonly localizationProvider: AsyncLocalizationProvider;
}
export type State = Settings & { languages: string[] };
export type State = Settings & {
rawAdditionalUrlsValue: string;
};
export namespace State {
export function fromSettings(settings: Settings): State {
return {
...settings,
rawAdditionalUrlsValue: AdditionalUrls.stringify(
settings.additionalUrls
),
};
}
export function toSettings(state: State): Settings {
const parsedAdditionalUrls = AdditionalUrls.parse(
state.rawAdditionalUrlsValue,
','
);
return {
...state,
additionalUrls: AdditionalUrls.sameAs(
state.additionalUrls,
parsedAdditionalUrls
)
? state.additionalUrls
: parsedAdditionalUrls,
};
}
export function merge(prevState: State, settings: Settings): State {
const prevAdditionalUrls = AdditionalUrls.parse(
prevState.rawAdditionalUrlsValue,
','
);
return {
...settings,
rawAdditionalUrlsValue: prevState.rawAdditionalUrlsValue,
additionalUrls: AdditionalUrls.sameAs(
prevAdditionalUrls,
settings.additionalUrls
)
? prevAdditionalUrls
: settings.additionalUrls,
};
}
}
}

View File

@@ -11,6 +11,7 @@ import { FileDialogService } from '@theia/filesystem/lib/browser/file-dialog/fil
import { nls } from '@theia/core/lib/common';
import { SettingsComponent } from './settings-component';
import { AsyncLocalizationProvider } from '@theia/core/lib/common/i18n/localization';
import { AdditionalUrls } from '../../../common/protocol';
@injectable()
export class SettingsWidget extends ReactWidget {
@@ -96,7 +97,7 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
this.update();
}
protected onUpdateRequest(msg: Message) {
protected onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg);
this.widget.update();
}
@@ -105,7 +106,7 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
super.onActivateRequest(msg);
// calling settingsService.reset() in order to reload the settings from the preferenceService
// and update the UI including changes triggerd from the command palette
// and update the UI including changes triggered from the command palette
this.settingsService.reset();
this.widget.activate();
@@ -168,10 +169,7 @@ export class AdditionalUrlsDialog extends AbstractDialog<string[]> {
}
get value(): string[] {
return this.textArea.value
.split('\n')
.map((url) => url.trim())
.filter((url) => !!url);
return AdditionalUrls.parse(this.textArea.value, 'newline');
}
protected onAfterAttach(message: Message): void {

View File

@@ -1,43 +1,42 @@
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';
import { MaybePromise } from '@theia/core/lib/common/types';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { PreferenceService, PreferenceScope } from '@theia/core/lib/browser';
import { Index } from '../../../common/types';
import {
AdditionalUrls,
CompilerWarnings,
ConfigService,
FileSystemExt,
Network,
} from '../../../common/protocol';
import { nls } from '@theia/core/lib/common';
import { CommandService, nls } from '@theia/core/lib/common';
import { AsyncLocalizationProvider } from '@theia/core/lib/common/i18n/localization';
import { ElectronCommands } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
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 {
export interface Settings {
editorFontSize: number; // `editor.fontSize`
themeId: string; // `workbench.colorTheme`
autoSave: 'on' | 'off'; // `editor.autoSave`
@@ -48,7 +47,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`
@@ -56,7 +54,7 @@ export interface Settings extends Index {
sketchbookShowAllFiles: boolean; // `arduino.sketchbook.showAllFiles`
sketchbookPath: string; // CLI
additionalUrls: string[]; // CLI
additionalUrls: AdditionalUrls; // CLI
network: Network; // CLI
}
export namespace Settings {
@@ -85,15 +83,19 @@ export class SettingsService {
@inject(AsyncLocalizationProvider)
protected readonly localizationProvider: AsyncLocalizationProvider;
@inject(CommandService)
protected commandService: CommandService;
protected readonly onDidChangeEmitter = new Emitter<Readonly<Settings>>();
readonly onDidChange = this.onDidChangeEmitter.event;
protected readonly onDidResetEmitter = new Emitter<Readonly<Settings>>();
readonly onDidReset = this.onDidResetEmitter.event;
protected ready = new Deferred<void>();
protected _settings: Settings;
@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 +112,6 @@ export class SettingsService {
quickSuggestions,
autoScaleInterface,
interfaceScale,
// checkForUpdates,
verboseOnCompile,
compilerWarnings,
verboseOnUpload,
@@ -135,7 +136,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 +154,6 @@ export class SettingsService {
quickSuggestions,
autoScaleInterface,
interfaceScale,
// checkForUpdates,
verboseOnCompile,
compilerWarnings,
verboseOnUpload,
@@ -174,7 +173,10 @@ export class SettingsService {
async update(settings: Settings, fireDidChange = false): Promise<void> {
await this.ready.promise;
for (const key of Object.keys(settings)) {
this._settings[key] = settings[key];
if (key in this._settings) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this._settings as any)[key] = (settings as any)[key];
}
}
if (fireDidChange) {
this.onDidChangeEmitter.fire(this._settings);
@@ -183,7 +185,8 @@ export class SettingsService {
async reset(): Promise<void> {
const settings = await this.loadSettings();
return this.update(settings, true);
await this.update(settings, false);
this.onDidResetEmitter.fire(this._settings);
}
async validate(
@@ -224,6 +227,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 +242,6 @@ export class SettingsService {
quickSuggestions,
autoScaleInterface,
interfaceScale,
// checkForUpdates,
verboseOnCompile,
compilerWarnings,
verboseOnUpload,
@@ -253,71 +260,33 @@ 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);
window.location.reload();
// Only reload if the language differs from the current locale. `nls.locale === undefined` signals english as well
if (
currentLanguage !== (await this.localizationProvider.getCurrentLanguage())
) {
await this.localizationProvider.setCurrentLanguage(currentLanguage);
if (currentLanguage === 'en') {
window.localStorage.removeItem(nls.localeId);
} else {
window.localStorage.setItem(nls.localeId, currentLanguage);
}
this.commandService.executeCommand(ElectronCommands.RELOAD.id);
}
return true;

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

@@ -115,8 +115,10 @@ const _Row = ({
return (
(data.lines[index].lineLen && (
<div style={style}>
{timestamp}
{data.lines[index].message}
<pre>
{timestamp}
{data.lines[index].message}
</pre>
</div>
)) ||
null

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';

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

@@ -57,3 +57,7 @@
display: flex;
justify-content: center;
}
.p-Widget.dialogOverlay .dialogBlock .dialogContent.additional-urls-dialog {
display: block;
}

View File

@@ -9,6 +9,7 @@ import {
} from '@theia/core/lib/browser/connection-status-service';
import {
ApplicationShell as TheiaApplicationShell,
DockPanel,
Panel,
Widget,
} from '@theia/core/lib/browser';
@@ -16,6 +17,7 @@ import { Sketch } from '../../../common/protocol';
import { SaveAsSketch } from '../../contributions/save-as-sketch';
import { SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';
import { nls } from '@theia/core/lib/common';
import URI from '@theia/core/lib/common/uri';
@injectable()
export class ApplicationShell extends TheiaApplicationShell {
@@ -40,6 +42,9 @@ export class ApplicationShell extends TheiaApplicationShell {
// Make the editor un-closeable asynchronously.
this.sketchesServiceClient.currentSketch().then((sketch) => {
if (sketch) {
if (!this.isSketchFile(widget.editor.uri, sketch.uri)) {
return;
}
if (Sketch.isInSketch(widget.editor.uri, sketch)) {
widget.title.closable = false;
}
@@ -48,6 +53,14 @@ export class ApplicationShell extends TheiaApplicationShell {
}
}
private isSketchFile(uri: URI, sketchUriString: string): boolean {
const sketchUri = new URI(sketchUriString);
if (uri.parent.isEqual(sketchUri)) {
return true;
}
return false;
}
async addWidget(
widget: Widget,
options: Readonly<TheiaApplicationShell.WidgetOptions> = {}
@@ -74,6 +87,11 @@ export class ApplicationShell extends TheiaApplicationShell {
return super.addWidget(widget, { ...options, ref });
}
handleEvent(): boolean {
// NOOP, dragging has been disabled
return false
}
// Avoid hiding top panel as we use it for arduino toolbar
protected createTopPanel(): Panel {
const topPanel = super.createTopPanel();
@@ -101,3 +119,16 @@ export class ApplicationShell extends TheiaApplicationShell {
);
}
}
const originalHandleEvent = DockPanel.prototype.handleEvent;
DockPanel.prototype.handleEvent = function (event) {
switch (event.type) {
case 'p-dragenter':
case 'p-dragleave':
case 'p-dragover':
case 'p-drop':
return;
}
originalHandleEvent(event);
};

View File

@@ -11,7 +11,15 @@ export class CommonFrontendContribution extends TheiaCommonFrontendContribution
registerCommands(commandRegistry: CommandRegistry): void {
super.registerCommands(commandRegistry);
for (const command of [CommonCommands.CONFIGURE_DISPLAY_LANGUAGE]) {
for (const command of [
CommonCommands.CONFIGURE_DISPLAY_LANGUAGE,
CommonCommands.CLOSE_TAB,
CommonCommands.CLOSE_SAVED_TABS,
CommonCommands.CLOSE_OTHER_TABS,
CommonCommands.CLOSE_ALL_TABS,
CommonCommands.COLLAPSE_PANEL,
CommonCommands.TOGGLE_MAXIMIZED,
]) {
commandRegistry.unregisterCommand(command);
}
}
@@ -32,10 +40,6 @@ export class CommonFrontendContribution extends TheiaCommonFrontendContribution
CommonCommands.SELECT_ICON_THEME,
CommonCommands.SELECT_COLOR_THEME,
CommonCommands.ABOUT_COMMAND,
CommonCommands.CLOSE_TAB,
CommonCommands.CLOSE_OTHER_TABS,
CommonCommands.CLOSE_ALL_TABS,
CommonCommands.COLLAPSE_PANEL,
CommonCommands.SAVE_WITHOUT_FORMATTING, // Patched for https://github.com/eclipse-theia/theia/pull/8877
]) {
registry.unregisterMenuAction(command);

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

@@ -0,0 +1,2 @@
export const REMOTE_SKETCHBOOK_FOLDER = 'RemoteSketchbook';
export const ARDUINO_CLOUD_FOLDER = 'ArduinoCloud';

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

@@ -116,7 +116,7 @@ export interface Config {
readonly sketchDirUri: string;
readonly dataDirUri: string;
readonly downloadsDirUri: string;
readonly additionalUrls: string[];
readonly additionalUrls: AdditionalUrls;
readonly network: Network;
readonly daemon: Daemon;
}
@@ -141,3 +141,32 @@ export namespace Config {
);
}
}
export type AdditionalUrls = string[];
export namespace AdditionalUrls {
export function parse(value: string, delimiter: ',' | 'newline'): string[] {
return value
.trim()
.split(delimiter === ',' ? delimiter : /\r?\n/)
.map((url) => url.trim())
.filter((url) => !!url);
}
export function stringify(additionalUrls: AdditionalUrls): string {
return additionalUrls.join(',');
}
export function sameAs(left: AdditionalUrls, right: AdditionalUrls): boolean {
if (left.length !== right.length) {
return false;
}
const localeCompare = (left: string, right: string) =>
left.localeCompare(right);
const normalize = (url: string) => url.toLowerCase();
const normalizedLeft = left.map(normalize).sort(localeCompare);
const normalizedRight = right.map(normalize).sort(localeCompare);
for (let i = 0; i < normalizedLeft.length; i++) {
if (normalizedLeft[i] !== normalizedRight[i]) {
return false;
}
}
return true;
}
}

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

@@ -11,12 +11,13 @@ import { FrontendApplicationContribution } from '@theia/core/lib/browser/fronten
import { Sketch, SketchesService } from '../../common/protocol';
import { ConfigService } from './config-service';
import { SketchContainer } from './sketches-service';
import {
ARDUINO_CLOUD_FOLDER,
REMOTE_SKETCHBOOK_FOLDER,
} from '../../browser/utils/constants';
const READ_ONLY_FILES = [
'thingProperties.h',
'thingsProperties.h',
'sketch.json',
];
const READ_ONLY_FILES = ['sketch.json'];
const READ_ONLY_FILES_REMOTE = ['thingProperties.h', 'thingsProperties.h'];
@injectable()
export class SketchesServiceClientImpl
@@ -178,7 +179,17 @@ export class SketchesServiceClientImpl
if (toCheck.scheme === 'user-storage') {
return false;
}
if (READ_ONLY_FILES.includes(toCheck?.path?.base)) {
const isCloudSketch = toCheck
.toString()
.includes(`${REMOTE_SKETCHBOOK_FOLDER}/${ARDUINO_CLOUD_FOLDER}`);
const filesToCheck = [
...READ_ONLY_FILES,
...(isCloudSketch ? READ_ONLY_FILES_REMOTE : []),
];
if (filesToCheck.includes(toCheck?.path?.base)) {
return true;
}
const readOnly = !this.workspaceService

View File

@@ -1,7 +1,3 @@
export type RecursiveRequired<T> = {
[P in keyof T]-?: RecursiveRequired<T[P]>;
};
export interface Index {
[key: string]: any;
}

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,129 @@
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;
const currentVersionHeader = `\n\n---\n\n## ${this.updater.currentVersion}\n\n`;
// We only want to see the release notes of newer versions
const currentVersionIndex = changelog?.indexOf(currentVersionHeader);
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,45 @@
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';
import { RELOAD_REQUESTED_SIGNAL, Restart } from '@theia/core/lib/electron-common/messaging/electron-messages';
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 +49,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';
}
@@ -45,11 +158,21 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
app.on('second-instance', this.onSecondInstance.bind(this));
app.on('window-all-closed', this.onWindowAllClosed.bind(this));
ipcMain.on('restart', ({ sender }) => {
ipcMain.on(RELOAD_REQUESTED_SIGNAL, event => this.handleReload(event));
ipcMain.on(Restart, ({ sender }) => {
this.restart(sender.id);
});
}
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 +271,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 +343,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

@@ -12,7 +12,6 @@ import { Event, Emitter } from '@theia/core/lib/common/event';
import { environment } from '@theia/application-package/lib/environment';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
import { LocalizationProvider } from '@theia/core/lib/node/i18n/localization-provider';
import { ArduinoDaemon, NotificationServiceServer } from '../common/protocol';
import { DaemonLog } from './daemon-log';
import { CLI_CONFIG } from './cli-config';
@@ -32,9 +31,6 @@ export class ArduinoDaemonImpl
@inject(NotificationServiceServer)
protected readonly notificationService: NotificationServiceServer;
@inject(LocalizationProvider)
protected readonly localizationProvider: LocalizationProvider;
protected readonly toDispose = new DisposableCollection();
protected readonly onDaemonStartedEmitter = new Emitter<void>();
protected readonly onDaemonStoppedEmitter = new Emitter<void>();

View File

@@ -92,6 +92,8 @@ import { ArduinoFirmwareUploaderImpl } from './arduino-firmware-uploader-impl';
import { PlotterBackendContribution } from './plotter/plotter-backend-contribution';
import WebSocketServiceImpl from './web-socket/web-socket-service-impl';
import { WebSocketService } from './web-socket/web-socket-service';
import { ArduinoLocalizationContribution } from './arduino-localization-contribution';
import { LocalizationContribution } from '@theia/core/lib/node/i18n/localization-contribution';
export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(BackendApplication).toSelf().inSingletonScope();
@@ -340,4 +342,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(PlotterBackendContribution).toSelf().inSingletonScope();
bind(BackendApplicationContribution).toService(PlotterBackendContribution);
bind(ArduinoLocalizationContribution).toSelf().inSingletonScope();
bind(LocalizationContribution).toService(ArduinoLocalizationContribution);
});

View File

@@ -0,0 +1,152 @@
import {
LocalizationContribution,
LocalizationRegistry,
} from '@theia/core/lib/node/i18n/localization-contribution';
import { injectable } from 'inversify';
@injectable()
export class ArduinoLocalizationContribution
implements LocalizationContribution
{
async registerLocalizations(registry: LocalizationRegistry): Promise<void> {
registry.registerLocalizationFromRequire(
'af',
require('../../build/i18n/af.json')
);
registry.registerLocalizationFromRequire(
'en',
require('../../build/i18n/en.json')
);
registry.registerLocalizationFromRequire(
'fr',
require('../../build/i18n/fr.json')
);
registry.registerLocalizationFromRequire(
'ko',
require('../../build/i18n/ko.json')
);
registry.registerLocalizationFromRequire(
'pt',
require('../../build/i18n/pt.json')
);
registry.registerLocalizationFromRequire(
'uk_UA',
require('../../build/i18n/uk_UA.json')
);
registry.registerLocalizationFromRequire(
'ar',
require('../../build/i18n/ar.json')
);
registry.registerLocalizationFromRequire(
'es',
require('../../build/i18n/es.json')
);
registry.registerLocalizationFromRequire(
'he',
require('../../build/i18n/he.json')
);
registry.registerLocalizationFromRequire(
'my_MM',
require('../../build/i18n/my_MM.json')
);
registry.registerLocalizationFromRequire(
'ro',
require('../../build/i18n/ro.json')
);
registry.registerLocalizationFromRequire(
'zh-cn',
require('../../build/i18n/zh.json')
);
registry.registerLocalizationFromRequire(
'bg',
require('../../build/i18n/bg.json')
);
registry.registerLocalizationFromRequire(
'eu',
require('../../build/i18n/eu.json')
);
registry.registerLocalizationFromRequire(
'hu',
require('../../build/i18n/hu.json')
);
registry.registerLocalizationFromRequire(
'ne',
require('../../build/i18n/ne.json')
);
registry.registerLocalizationFromRequire(
'ru',
require('../../build/i18n/ru.json')
);
registry.registerLocalizationFromRequire(
'zh_TW',
require('../../build/i18n/zh_TW.json')
);
registry.registerLocalizationFromRequire(
'de',
require('../../build/i18n/de.json')
);
registry.registerLocalizationFromRequire(
'fa',
require('../../build/i18n/fa.json')
);
registry.registerLocalizationFromRequire(
'it',
require('../../build/i18n/it.json')
);
registry.registerLocalizationFromRequire(
'nl',
require('../../build/i18n/nl.json')
);
registry.registerLocalizationFromRequire(
'sv_SE',
require('../../build/i18n/sv_SE.json')
);
registry.registerLocalizationFromRequire(
'el',
require('../../build/i18n/el.json')
);
registry.registerLocalizationFromRequire(
'fil',
require('../../build/i18n/fil.json')
);
registry.registerLocalizationFromRequire(
'ja',
require('../../build/i18n/ja.json')
);
registry.registerLocalizationFromRequire(
'pl',
require('../../build/i18n/pl.json')
);
registry.registerLocalizationFromRequire(
'tr',
require('../../build/i18n/tr.json')
);
}
}

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

@@ -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

@@ -30,10 +30,11 @@ const WIN32_DRIVE_REGEXP = /^[a-zA-Z]:\\/;
const prefix = '.arduinoIDE-unsaved';
@injectable()
export class SketchesServiceImpl
extends CoreClientAware
implements SketchesService
{
export class SketchesServiceImpl extends CoreClientAware
implements SketchesService {
private sketchSuffixIndex = 1;
private lastSketchBaseName: string;
@inject(ConfigService)
protected readonly configService: ConfigService;
@@ -303,22 +304,31 @@ export class SketchesServiceImpl
monthNames[today.getMonth()]
}${today.getDate()}`;
const config = await this.configService.getConfiguration();
const user = FileUri.fsPath(config.sketchDirUri);
const sketchbookPath = FileUri.fsPath(config.sketchDirUri);
let sketchName: string | undefined;
for (let i = 97; i < 97 + 26; i++) {
const sketchNameCandidate = `${sketchBaseName}${String.fromCharCode(i)}`;
// Note: we check the future destination folder (`directories.user`) for name collision and not the temp folder!
if (await promisify(fs.exists)(path.join(user, sketchNameCandidate))) {
continue;
}
sketchName = sketchNameCandidate;
break;
// If it's another day, reset the count of sketches created today
if (this.lastSketchBaseName !== sketchBaseName) this.sketchSuffixIndex = 1;
let nameFound = false;
while (!nameFound) {
const sketchNameCandidate = `${sketchBaseName}${sketchIndexToLetters(
this.sketchSuffixIndex++
)}`;
// Note: we check the future destination folder (`directories.user`) for name collision and not the temp folder!
const sketchExists = await promisify(fs.exists)(
path.join(sketchbookPath, sketchNameCandidate)
);
if (!sketchExists) {
nameFound = true;
sketchName = sketchNameCandidate;
}
}
if (!sketchName) {
throw new Error('Cannot create a unique sketch name');
}
this.lastSketchBaseName = sketchBaseName;
const sketchDir = path.join(parentPath, sketchName);
const sketchFile = path.join(sketchDir, `${sketchName}.ino`);
@@ -507,3 +517,23 @@ interface SketchContainerWithDetails extends SketchContainer {
readonly children: SketchContainerWithDetails[];
readonly sketches: SketchWithDetails[];
}
/*
* When a new sketch is created, add a suffix to distinguish it
* from other new sketches I created today.
* If 'sketch_jul8a' is already used, go with 'sketch_jul8b'.
* If 'sketch_jul8b' already used, go with 'sketch_jul8c'.
* When it reacheas 'sketch_jul8z', go with 'sketch_jul8aa',
* and so on.
*/
function sketchIndexToLetters(num: number): string {
let out = '';
let pow;
do {
pow = Math.floor(num / 26);
const mod = num % 26;
out = (mod ? String.fromCharCode(96 + mod) : (--pow, 'z')) + out;
num = pow;
} while (pow > 0);
return out;
}

View File

@@ -0,0 +1,35 @@
import { expect } from 'chai';
import { AdditionalUrls } from '../../common/protocol';
describe('config-service', () => {
describe('additionalUrls', () => {
it('should consider additional URLs same as if they differ in case', () => {
expect(AdditionalUrls.sameAs(['aaaa'], ['AAAA'])).to.be.true;
});
it('should consider additional URLs same as if they have a different order', () => {
expect(AdditionalUrls.sameAs(['bbbb', 'aaaa'], ['aaaa', 'bbbb'])).to.be
.true;
});
it('should parse an empty string as an empty array', () => {
expect(AdditionalUrls.parse('', ',')).to.be.empty;
});
it('should parse a blank string as an empty array', () => {
expect(AdditionalUrls.parse(' ', ',')).to.be.empty;
});
it('should parse urls with commas', () => {
expect(AdditionalUrls.parse(' ,a , b , c, ', ',')).to.be.deep.equal([
'a',
'b',
'c',
]);
});
it("should parse urls with both '\\n' and '\\r\\n' line endings", () => {
expect(
AdditionalUrls.parse(
'a ' + '\r\n' + ' b ' + '\n' + ' c ' + '\r\n' + ' ' + '\n' + '',
'newline'
)
).to.be.deep.equal(['a', 'b', 'c']);
});
});
});

View File

@@ -1,29 +1,29 @@
{
"private": true,
"name": "browser-app",
"version": "2.0.0-rc3",
"version": "2.0.0-rc6",
"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-rc3"
"@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-rc6"
},
"devDependencies": {
"@theia/cli": "1.19.0"
"@theia/cli": "1.22.1"
},
"scripts": {
"prepare": "theia build --mode development",

View File

@@ -2,19 +2,18 @@
Building the Pro IDE on Linux `armv7l` (aka `armhf`) and `aarch64` (aka `arm64`):
1. Install Node.js 12.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):

View File

@@ -18,9 +18,9 @@ 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 \

View File

@@ -1,31 +1,32 @@
{
"private": true,
"name": "electron-app",
"version": "2.0.0-rc3",
"version": "2.0.0-rc6",
"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-rc3"
"@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-rc6"
},
"devDependencies": {
"@theia/cli": "1.19.0"
"@theia/cli": "1.22.1",
"electron": "^15.3.5"
},
"scripts": {
"prepare": "theia build --mode development",

View File

@@ -1,5 +1,3 @@
Terms of Service
By downloading the software from this page, you agree to the specified terms.
The Arduino software is provided to you "as is" and we make no express or implied warranties whatsoever with respect to its functionality, operability, or use, including, without limitation, any implied warranties of merchantability, fitness for a particular purpose, or infringement. We expressly disclaim any liability whatsoever for any direct, indirect, consequential, incidental or special damages, including, without limitation, lost revenues, lost profits, losses resulting from business interruption or loss of data, regardless of the form of action or legal theory under which the liability may be asserted, even if advised of the possibility or likelihood of such damages.

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,30 +3,32 @@
"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"
},
"scripts": {
"build": "yarn download:plugins && theia build --mode development && yarn patch",
"build:publish": "yarn download:plugins && theia build --mode production && yarn patch",
"build": "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 +53,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 +76,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,20 +96,19 @@
"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"
},
"msi": {
"runAfterFinish": false
},
"nsis": {
"oneClick": false,
"installerHeaderIcon": "resources/icon.ico",
@@ -139,9 +148,15 @@
"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"
"cortex-debug": "https://open-vsx.org/api/marus25/cortex-debug/0.3.10/file/marus25.cortex-debug-0.3.10.vsix",
"vscode-language-pack-nl": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-nl/1.48.3/file/MS-CEINTL.vscode-language-pack-nl-1.48.3.vsix",
"vscode-language-pack-fr": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-fr/1.53.2/file/MS-CEINTL.vscode-language-pack-fr-1.53.2.vsix",
"vscode-language-pack-zh-hans": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-zh-hans/1.53.2/file/MS-CEINTL.vscode-language-pack-zh-hans-1.53.2.vsix",
"vscode-language-pack-de": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-de/1.53.2/file/MS-CEINTL.vscode-language-pack-de-1.53.2.vsix",
"vscode-language-pack-ja":"https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-ja/1.53.2/file/MS-CEINTL.vscode-language-pack-ja-1.53.2.vsix",
"vscode-language-pack-tr": "https://open-vsx.org/api/MS-CEINTL/vscode-language-pack-tr/1.53.2/file/MS-CEINTL.vscode-language-pack-tr-1.53.2.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,502 @@
//@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',
'i18n'
]) {
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`,
`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);
}
if (expectedVersion) {
if (!versions.has(expectedVersion)) {
echo(
`Mismatching version configuration. Expected version was: '${expectedVersion}' actual was: '${Array.from(versions)[0]
}'.`
);
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`);
//------------------------------------------------------------------------------+
// 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"

350
i18n/af.json Normal file
View File

@@ -0,0 +1,350 @@
{
"arduino": {
"common": {
"offlineIndicator": "Dit lyk of jy van af lyn is. Sonder 'n internetverbinding kan die Arduino CLI moontlik nie die nodige hulpbronne aflaai nie en kan dit wanfunksionering veroorsaak. Koppel asseblief aan die internet en herbegin die toepassing.",
"noBoardSelected": "Geen bord geselekteer",
"selectedOn": "aan {0}",
"notConnected": "[nie gekoppel]",
"serialMonitor": "Seriaal Monitor",
"oldFormat": "The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?",
"later": "Later",
"selectBoard": "Kies Bord",
"unknown": "Onbekend",
"processing": "Verwerking",
"saveChangesToSketch": "Wil jy veranderinge aan hierdie skets stoor voordat jy toemaak?",
"loseChanges": "As jy nie stoor nie, sal jou veranderinge verlore gaan."
},
"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": "Skets",
"tools": "Gereedskap"
},
"debug": {
"optimizeForDebugging": "Geoptimaliseerd vir ontfouting",
"debugWithMessage": "Ontfouting {0}",
"noPlatformInstalledFor": "Platform is nie geïnstalleer vir ' {0} '",
"debuggingNotSupported": "Debugging is not supported by '{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.",
"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.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.",
"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": "Die OAuth2 domein.",
"auth.audience": "Die OAuth2 gehoor.",
"auth.registerUri": "Die URI wat gebruik word vir nuwe registrasies.",
"network": "Netwerk",
"sketchbook.location": "Sketsboek ligging",
"browse": "Snuffel",
"files.inside.sketches": "Toon lêers binne in Sketse aan",
"editorFontSize": "Editor lettertipe grootte",
"interfaceScale": "Koppelvlak skaal",
"showVerbose": "Wys ontfoutings uitset tydens",
"compilerWarnings": "Compiler warnings",
"automatic": "Automaties",
"compile": "saamstel",
"upload": "oplaai",
"verifyAfterUpload": "Verifieer kode na oplaai ",
"editorQuickSuggestions": "Editor Quick Suggestions",
"additionalManagerURLs": "Additional Boards Manager URLs",
"noProxy": "No proxy",
"manualProxy": "Manual proxy configuration",
"newSketchbookLocation": "Select new sketchbook location",
"choose": "Kies",
"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."
},
"cloud": {
"signIn": "TEKEN AAN",
"signOut": "Teken Uit",
"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": "Skakel:",
"embed": "Embed:",
"cloudSketchbook": "Wolk Skets",
"shareSketch": "Share Sketch",
"showHideRemoveSketchbook": "Show/Hide Remote Sketchbook",
"pullSketch": "Pull Sketch",
"openInCloudEditor": "Open in Cloud Editor",
"options": "Opsies...",
"share": "Deel...",
"remote": "Remote",
"emptySketchbook": "Jou Sketsboek is leeg",
"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",
"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": "Connected",
"offline": "Aflyn",
"profilePicture": "Profiel foto"
},
"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 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": "Programeerder",
"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": "Herselekteer 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": "Kies ander bord en poort...",
"boardListItem": "{0} by {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": "Board Info",
"board": "Board{0}",
"port": "Port{0}",
"getBoardInfo": "Get Board Info",
"inSketchbook": " (in Sketchbook)"
},
"boardsManager": "Boards Manager",
"about": {
"label": "About {0}",
"detail": "Version: {0}\nDate: {1}{2}\nCLI Version: {3}{4} [{5}]\n\n{6}"
},
"contributions": {
"addFile": "Add File",
"replaceTitle": "Vervang",
"fileAdded": "Een lêer by skets gevoeg"
},
"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": "Skets",
"moving": "Moving",
"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",
"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",
"privacyPolicy": "Privacy Policy"
},
"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} by {1}",
"noSupportedBoardConnected": "No supported board connected"
},
"firmware": {
"updater": "WiFi101 / WiFiNINA Firmware Updater",
"selectBoard": "Kies Bord",
"checkUpdates": "Check Updates",
"selectVersion": "Select firmware version",
"install": "Install",
"overwriteSketch": "Installation will overwrite the Sketch on the board.",
"installingFirmware": "Installing firmware.",
"successfullyInstalled": "Firmware successfully 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": "Tydstempel",
"noLineEndings": "No Line Ending",
"newLine": "Nuwe lyn",
"carriageReturn": "Wagterugkeer",
"newLineCarriageReturn": "Beide 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": "Konneksie het gevaal. Seriaal poort is besig: {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": "Weergawe {0}",
"moreInfo": "Meer inligting",
"install": "INSTALLEER",
"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": "Enige ongestoorde veranderinge sal nie gestoor word nie."
},
"compile": {
"error": "Compilation error: {0}"
},
"upload": {
"error": "{0} error: {1}"
},
"burnBootloader": {
"error": "Error while burning the bootloader: {0}"
}
},
"theia": {
"core": {
"couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.",
"offline": "Aflyn",
"daemonOffline": "CLI Daemon Offline",
"cannotConnectBackend": "Cannot connect to the backend.",
"cannotConnectDaemon": "Cannot connect to the CLI daemon."
},
"debug": {
"start": "Begin...",
"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": "Expand",
"collapse": "Collapse"
},
"workspace": {
"fileNewName": "Naam vir nuwe lêer",
"invalidFilename": "Ongeldige lêernaam",
"invalidExtension": ".{0} is not a valid extension",
"newFileName": "Nuwe naam vir lêer",
"deleteCurrentSketch": "Wil jy die huidige skets uitvee?",
"sketchDirectoryError": "There was an error creating the sketch directory. See the log for more details. The application will probably not work as expected."
}
},
"cloud": {
"GoToCloud": "GAAN NA WOLK"
}
}

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": "ادوات"
@@ -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": "افحص التحديثات عند التشغيل",
"editorQuickSuggestions": "Editor Quick Suggestions",
"additionalManagerURLs": "تدبير عناوين اللوحات الإضافية",
"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": "المجلد المسمى \"{0}\" موجود مسبقا. لا يمكن فتح الشيفرة البرمجية",
"saveFolderAs": "Save sketch folder as...",
"sketchbook": "Sketchbook",
@@ -223,7 +238,8 @@
"reference": "Reference",
"findInReference": "Find in Reference",
"faq": "Frequently Asked Questions",
"visit": "Visit Arduino.cc"
"visit": "Visit Arduino.cc",
"privacyPolicy": "Privacy Policy"
},
"certificate": {
"uploadRootCertificates": "Upload SSL Root Certificates",
@@ -243,14 +259,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": "تنصيب",
"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 +285,7 @@
"carriageReturn": "اعادة الحمل",
"newLineCarriageReturn": "كلاهما 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

@@ -14,6 +14,22 @@
"saveChangesToSketch": "Искате ли да запазите промените в тази скица, преди да затворите?",
"loseChanges": "Ако не запазите, промените ви ще бъдат загубени."
},
"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": "Скица",
"tools": "Инструменти"
@@ -31,7 +47,7 @@
"upload.verbose": "True за подробен изход за качване. False по подразбиране.",
"window.autoScale": "True , ако потребителският интерфейс автоматично се мащабира с размера на шрифта.",
"window.zoomLevel": "Регулирайте нивото на увеличение на прозореца. Оригиналният размер е 0 и всяко увеличение над (напр. 1) или под (напр. -1) представлява увеличение с 20% по-голямо или по-малко. Можете също да въведете десетични знаци, за да регулирате нивото на увеличение по-финно.",
"ide.autoUpdate": "True, за да активирате автоматичните проверки за актуализация. IDE ще проверява за актуализации автоматично и периодично.",
"ide.updateChannel": "Release channel to get updated from. 'stable' is the stable release, 'nightly' is the latest development build.",
"board.certificates": "Списък със сертификати, които могат да бъдат качени на платката",
"sketchbook.showAllFiles": "True , за да се покажат всички файлове вътре в скицата. По подразбиране е false.",
"cloud.enabled": "True , ако функциите за синхронизиране на скицата са активирани. По подразбиране е true.",
@@ -55,7 +71,6 @@
"compile": "компилиране",
"upload": "качване",
"verifyAfterUpload": "Потвърдете кода след качване",
"checkForUpdates": "Проверете за актуализации при стартиране",
"editorQuickSuggestions": "Бързи предложения на редактора",
"additionalManagerURLs": "Допълнителни URL адреси на мениджър на платки",
"noProxy": "Без прокси",
@@ -108,7 +123,7 @@
"installNow": "Ядрото „{0} {1}“ трябва да бъде инсталирано за текущо избраната платка „{2}“. Искате ли да го инсталирате сега?",
"configDialogTitle": "Изберете друга платка и порт",
"configDialog1": "Изберете както платка, така и порт, ако искате да качите скица.",
"configDialog2": "Ако изберете само платка, ще можете само да компилирате, но не и да качвате скицата си.",
"configDialog2": "If you only select a Board you will be able to compile, but not to upload your sketch.",
"pleasePickBoard": "Моля, изберете платка, свързана към порта, който сте избрали.",
"showAllAvailablePorts": "Показва всички налични портове, когато е активиран",
"programmer": "Програматор",
@@ -173,7 +188,7 @@
"showFolder": "Показване на папка за скици",
"sketch": "Скица",
"moving": "Преместване",
"movingMsg": "Файлът „{0}“ трябва да бъде в папка на скица, наречена „{1}.\nСъздай тази папка, премести файла и продължи?",
"movingMsg": "The file \"{0}\" needs to be inside a sketch folder named \"{1}\".\nCreate this folder, move the file, and continue?",
"cantOpen": "Папка с име „{0}“ вече съществува. Не може да се отвори скица.",
"saveFolderAs": "Запазете папката със скица като...",
"sketchbook": "Скицник",
@@ -223,7 +238,8 @@
"reference": "Референция",
"findInReference": "Намерете в Референцията",
"faq": "Често задавани въпроси",
"visit": "Посетете Arduino.cc"
"visit": "Посетете Arduino.cc",
"privacyPolicy": "Privacy Policy"
},
"certificate": {
"uploadRootCertificates": "Качете SSL коренни сертификати",
@@ -243,14 +259,14 @@
"noSupportedBoardConnected": "Няма свързана поддържана платка"
},
"firmware": {
"updater": "WiFi101 / WiFiNINA Обновяване на фърмуера",
"updater": "WiFi101 / WiFiNINA Firmware Updater",
"selectBoard": "Изберете платка",
"checkUpdates": "Проверете актуализации",
"selectVersion": "Изберете версия на фърмуера",
"install": "Инсталирай",
"overwriteSketch": "Инсталацията ще презапише скицата на платката.",
"installingFirmware": "Инсталиране на фърмуер.",
"successfullyInstalled": "Фърмуерът е инсталиран успешно.",
"successfullyInstalled": "Firmware successfully installed.",
"failedInstall": "Неуспешно инсталиране. Моля, опитайте отново."
},
"dialog": {
@@ -269,7 +285,7 @@
"carriageReturn": "Carriage Return",
"newLineCarriageReturn": "Както NL, така и CR",
"notConnected": "Няма връзка. Изберете платка и порт за автоматично свързване.",
"message": "Съобщение ({0} + Enter, за да изпратите съобщение до „{1}“ на „{2}",
"message": "Message ({0} + Enter to send message to '{1}' on '{2}')",
"connectionBusy": "Свързването е неуспешно. Серийният порт е зает: {0}",
"disconnected": "Прекъснато е връзката на {0} с {1}.",
"unexpectedError": "Неочаквана грешка. Повторно свързване на {0} на порт {1}.",

350
i18n/cs.json Normal file
View File

@@ -0,0 +1,350 @@
{
"arduino": {
"common": {
"offlineIndicator": "Nejspíše nejste online. Bez Internetového připojení nebude Arduino CLI schopno stáhnout potřebné zdroje a toto může způsobit chybu. Prosím připojte se k Internetu a restartujte aplikaci.",
"noBoardSelected": "Nebyla zvolena deska",
"selectedOn": "zapnuto{0}",
"notConnected": "[nepřipojen]",
"serialMonitor": "Seriový monitor",
"oldFormat": "{0}používá stále starý formát `.pde`. Chcete ho převést na soubor s příponou `.ino`?",
"later": "Později",
"selectBoard": "Zvolte desku",
"unknown": "Neznámý",
"processing": "Zpracovávám",
"saveChangesToSketch": "Chcete uložit změny před ukončením programu?",
"loseChanges": "Pokud projekt neuložíte, změny budou ztraceny. "
},
"ide-updater": {
"errorCheckingForUpdates": "Nastala chyba při kontrole aktualizací Arduino IDE{0}",
"notNowButton": "Nyní ne",
"versionDownloaded": "Arduino IDE {0}bylo staženo.",
"closeToInstallNotice": "Vypněte program a nainstalujte update na Váš stroj. ",
"closeAndInstallButton": "Zavřít a nainstalovat",
"downloadingNotice": "Stahuji poslední verzi Arduino IDE.",
"updateAvailable": "Je dostupná aktualizace",
"newVersionAvailable": "Je k dispozici nová verze Arduino IDE ({0}).",
"skipVersionButton": "přeskočit verzi",
"downloadButton": "Stáhnout",
"goToDownloadPage": "Je dostupná aktualizace pro Arduino IDE, ale nepodařilo se jej stáhnout a nainstalovat automaticky. Prosím navštivte stránku pro stažení a stáhněte prosím jeho poslední verzi ručně. ",
"goToDownloadButton": "přejit do stažených",
"ideUpdaterDialog": "Softwarová aktualizace",
"noUpdatesAvailable": "Nejsou k dispozici žádné aktualizace pro Arduino ID"
},
"menu": {
"sketch": "Sketch",
"tools": "Nástroje"
},
"debug": {
"optimizeForDebugging": "optimalizovat pro Debugging",
"debugWithMessage": "Debug - {0}",
"noPlatformInstalledFor": "Platforma není nainstalována pro '{0}'",
"debuggingNotSupported": "Debugging není podporován s '{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.",
"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.updateChannel": "Release channel to get updated from. 'stable' is the stable release, 'nightly' is the latest development build.",
"board.certificates": "Seznam certifikátů, které mohou být nahrány na desky",
"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": "Endpoint použitý pro stahování a odesílání sketchí z backendu. Ve výchozím stavu je toto směrováno na Arduino Cloud API.",
"auth.clientID": "OAuth2 klient ID",
"auth.domain": "Doména OAuth2 ",
"auth.audience": "OAuth2 audience",
"auth.registerUri": "URL použita při registraci nového uživatele. ",
"network": "Síť",
"sketchbook.location": "Umístění sketche",
"browse": "Procházet",
"files.inside.sketches": "Zobrazit soubory uvnitř sketche",
"editorFontSize": "Editor velikosti fontu",
"interfaceScale": "Měřítko prostředí",
"showVerbose": "Zobrazit podrobný výstup během",
"compilerWarnings": "Varování kompileru",
"automatic": "Automaticky",
"compile": "kompilovat",
"upload": "nahrát",
"verifyAfterUpload": "Kontrolovat kód po nahrání",
"editorQuickSuggestions": "Rychlá nápověda v editoru",
"additionalManagerURLs": "Další URL pro manager desek",
"noProxy": "Bez proxy",
"manualProxy": "Ruční nastavení proxy",
"newSketchbookLocation": "Zvolit nové umístění projektů",
"choose": "Zvolit",
"enterAdditionalURLs": "Vložte další URL, jednu pro každý řádek",
"unofficialBoardSupport": "Zde klikněte pro seznam adres neoficiálně podporovaných desek",
"invalid.sketchbook.location": "Neplatné umístění projektů:{0}",
"invalid.editorFontSize": "Neplatná velikost editoru písem: Toto číslo musí být kladné.",
"invalid.theme": "Neplatné téma."
},
"cloud": {
"signIn": "Přihlásit se",
"signOut": "Odhlásit se",
"chooseSketchVisibility": "Zvolte viditelnost sketche:",
"privateVisibility": "Soukromá. Pouze vy můžete vidět tuto sketch.",
"publicVisibility": "Veřejná. Kdokoli s odkazem může vidět tuto sketch.",
"link": "Adresa: ",
"embed": "Vložit: ",
"cloudSketchbook": "Cloudové umístění projektů",
"shareSketch": "Sdílet sketch",
"showHideRemoveSketchbook": "Zobrazit/skrýt vzdálenou sketch",
"pullSketch": "Stáhnout sketch",
"openInCloudEditor": "Otevřít v cloudovém editoru",
"options": "Možnosti...",
"share": "Sdílet...",
"remote": "Vzdálený",
"emptySketchbook": "Umístění projektů je prázdné",
"visitArduinoCloud": "Navštivte Arduino Cloud pro vytvoření cloudové sketche",
"signInToCloud": "Přihlásit se do Arduino Cloud",
"syncEditSketches": "Synchronizovat a editovat sketche v Arduino Cloud",
"learnMore": "Zjistit více",
"continue": "Pokračovat",
"pushSketch": "Odeslat sketch",
"pushSketchMsg": "Toto je veřejná sketch. Před odesláním se prosím ujistěte že jsou smazané citlivé informace ze souboru arduino_secrets.h . Tuto sketch můžete změnit na soukromou v panelu pro sdílení. ",
"pull": "Stáhnout",
"pullSketchMsg": "Stažením této sketche z cloudu přepíšete její lokální verzi. Chcete opravdu pokračovat?",
"donePulling": "Stahování dokončeno {0}.",
"notYetPulled": "Nemohu odeslat do cloudu jelikož z něj ještě nebylo staženo.",
"push": "Odeslat",
"pullFirst": "Nejprve musíte stáhnout z cloudu, aby bylo možno do něj odeslat.",
"donePushing": "Odesílání dokončeno {0}.",
"connected": "Připojen",
"offline": "Nepřipojen",
"profilePicture": "Profilový obrázek"
},
"board": {
"installManually": "Instalovat ručně",
"installNow": "\"{0}{1}\" jádro musí být instalováno pro aktuálně zvolenou \"{2}\" desku. Chcete ho nyní nainstalovat?",
"configDialogTitle": "Zvolte desku a port",
"configDialog1": "Pokud chcete nahrát sketch, musíte zvolit jak desku tak i port.",
"configDialog2": "Pokud zvolíte jen desku, budete schopni kompilovat sketch, ale nebudete ji moci nahrát do desky.",
"pleasePickBoard": "Vyberte prosím desku která je připojená k zvolenému portu. ",
"showAllAvailablePorts": "Zobrazit všechny dostupné porty (pokud je zaškrtnuto)",
"programmer": "Programátor",
"succesfullyInstalledPlatform": "Platforma {0}:{1} byla úspěšně nainstalována.",
"succesfullyUninstalledPlatform": "Platforma {0}:{1} byla úspěšně odinstalována.",
"couldNotFindPreviouslySelected": "Dříve zvolená deska '{0}' v instalované platformě '{1}' nebyla nalezena. Zvolte prosím manuálně desku kterou chcete použít. Chcete tuto desku zvolit nyní? ",
"reselectLater": "Zvolit později",
"noneSelected": "Nebyla zvolena deska.",
"noPortsSelected": "Nebyl zvolen port pro desku '{0}'.",
"noFQBN": "FQBN není dostupné pro zvolenou desku \"{0}\". Máte nainstalované příslušné jádro? ",
"openBoardsConfig": "Zvolte jinou desku a port...",
"boardListItem": "{0}v{1}",
"selectBoardForInfo": "Prosím zvolte desku pro získání informací o desce.",
"platformMissing": "Platforma pro zvolenou desku '{0}' není nainstalována.",
"selectPortForInfo": "Prosím zvolte port pro získání informací o desce.",
"boardInfo": "Info o desce",
"board": "Deska {0}",
"port": "Port {0}",
"getBoardInfo": "Získat info o desce.",
"inSketchbook": "(v projektech)"
},
"boardsManager": "Manažér desek",
"about": {
"label": "O {0}",
"detail": "Verze: {0}\nDatum: {1}[2]\nVerze CLI: {3}{4} [{5}]\n\n{6}"
},
"contributions": {
"addFile": "Přidat soubor",
"replaceTitle": "Vyměnit",
"fileAdded": "Soubor byl přidán do sketche."
},
"replaceMsg": "Vyměnit existující verzi {0}?",
"library": {
"addZip": "Přidat .ZIP knihovnu...",
"zipLibrary": "Knihovna",
"overwriteExistingLibrary": "Chcete přepsat existující knihovnu? ",
"successfullyInstalledZipLibrary": "Knihovna byla úspěšně nainstalována z archívu {0}",
"namedLibraryAlreadyExists": "Knihovna s názvem {0}již existuje. Chcete jí přepsat? ",
"libraryAlreadyExists": "Knihovna již existuje. Chcete jí přepsat?",
"include": "Zahrnout knihovnu",
"manageLibraries": "Spravovat knihovny...",
"arduinoLibraries": "Arduino knihovny",
"contributedLibraries": "Přispěné knihovny",
"title": "Manažér knihoven",
"needsOneDependency": "Knihovna <b>{0}:{1}</b>vyžaduje další závislost která není nainstalovaná:",
"needsMultipleDependencies": "Knihovna <b>{0}:{1}</b>vyžaduje další jinou závislost která není nainstalovaná:",
"installOneMissingDependency": "Chcete nainstalovat chybějící závislost? ",
"installMissingDependencies": "Chcete nainstalovat všechny chybějící závislosti? ",
"dependenciesForLibrary": "Závislosti pro knihovny {0}:{1}",
"installAll": "Instalovat vše",
"installOnly": "Instalovat pouze {0}",
"installedSuccessfully": "Knihovna {0}:{1}byla úspěšně nainstalována",
"uninstalledSuccessfully": "Knihovna {0}:{1}byla úspěšně odinstalována"
},
"selectZip": "Zvolte ZIP soubor s knihovnou kterou chcete přidat",
"sketch": {
"archiveSketch": "Archivovat sketch",
"saveSketchAs": "Uložit složku sketche jako...",
"createdArchive": "Vytvořen archív '{0}'.",
"new": "Nový",
"openRecent": "Otevřít nedávné",
"showFolder": "Zobrazit složku sketche",
"sketch": "Sketch",
"moving": "Přesouvám",
"movingMsg": "Soubor \"{0}\" musí být uvnitř složky která je shodná názvem \"{1}\".\nVytvořit složku, přesunout tam- soubor a pokračovat? ",
"cantOpen": "Složka s názvem \"{0}\" již existuje, proto nemohu otevřít sketch. ",
"saveFolderAs": "Uložit složku sketche jako...",
"sketchbook": "Projekty",
"upload": "Nahrát",
"uploadUsingProgrammer": "Nahrát pomocí programátoru",
"userFieldsNotFoundError": "Nemohu nalézt uživatelská pole pro připojenou desku",
"doneUploading": "Nahrávání dokončeno. ",
"configureAndUpload": "Nastavit a nahrát",
"verifyOrCompile": "Ověřit/Kompilovat",
"exportBinary": "Exportovat kompilované soubory",
"verify": "Ověřit",
"doneCompiling": "Ověřování dokončeno.",
"couldNotConnectToSerial": "Nebylo možné se opět připojit k seriovému portu. {0}",
"openSketchInNewWindow": "Otevřít sketch v novém okně",
"openFolder": "Otevřít složku",
"titleLocalSketchbook": "Složka lokálních projektů",
"titleSketchbook": "Projekty",
"close": "Opravdu chcete zavřít tuto sketch? "
},
"bootloader": {
"burnBootloader": "Nahrát zavaděč",
"doneBurningBootloader": "Nahrání zavaděče bylo dokončeno. "
},
"editor": {
"copyForForum": "Kopírovat pro forum (Markdown)",
"commentUncomment": "Komentovat/Odkomentovat",
"increaseIndent": "Zvětšit odrážku",
"decreaseIndent": "Zmenšit odrážku",
"increaseFontSize": "Zvýšit velikost písma",
"decreaseFontSize": "Zmenšit velikost písma",
"autoFormat": "Automatické formátování"
},
"examples": {
"menu": "Příklady",
"couldNotInitializeExamples": "Nebylo možné inicializovat vestavěné příklady.",
"builtInExamples": "Vestavěné příklady.",
"customLibrary": "Příklady pro vlastní knihovny",
"for": "Příklady pro {0}",
"forAny": "Příklady pro všechny desky"
},
"help": {
"search": "Hledat na Arduino.cc",
"keyword": "Napište klíčové slovo",
"gettingStarted": "Začínáme",
"environment": "Prostředí",
"troubleshooting": "Řešení problémů",
"reference": "Reference",
"findInReference": "Hledat v referencích",
"faq": "Často kladené otázky",
"visit": "Navštívit Arduino.cc",
"privacyPolicy": "Zásady ochrany osobních údajů"
},
"certificate": {
"uploadRootCertificates": "Nahrát certifikát SSL",
"openContext": "Otevřít kontext",
"remove": "Odstranit",
"upload": "Nahrát",
"addURL": "Přidat URL pro získání SSL certifikátu",
"enterURL": "Vložit URL",
"selectCertificateToUpload": "1. Zvolit certifikát pro nahrání",
"addNew": "Přidat nový",
"selectDestinationBoardToUpload": "2. Zvolit cílovou desku a nahrát certifikát",
"uploadingCertificates": "Nahrávám certifikát.",
"certificatesUploaded": "Certifikáty nahrány. ",
"uploadFailed": "Nahrání se nezdařilo. Prosím zkuste to později. ",
"selectBoard": "Zvolte desku...",
"boardAtPort": "{0}v{1}",
"noSupportedBoardConnected": "Není připojena žádná podporovaná deska"
},
"firmware": {
"updater": "WiFi101 / WiFiNINA Firmware Updater",
"selectBoard": "Zvolit desku",
"checkUpdates": "Zkontrolovat aktualizace",
"selectVersion": "Zvolit verzi firmwaru",
"install": "Instalovat",
"overwriteSketch": "Instalace přepíše sketch na desce.",
"installingFirmware": "Instaluji firmware.",
"successfullyInstalled": "Firmware byl úspěšně nainstalován. ",
"failedInstall": "Instalace se nezdařila. Prosím zkuste to později. "
},
"dialog": {
"dontAskAgain": "Znovu se neptat"
},
"userFields": {
"cancel": "Zrušit",
"upload": "Nahrát"
},
"serial": {
"toggleTimestamp": "Přepnout časovou značku",
"autoscroll": "Autoscroll",
"timestamp": "Časová značka",
"noLineEndings": "Bez konce řádku",
"newLine": "Nový řádek (NL)",
"carriageReturn": "Enter (CR)",
"newLineCarriageReturn": "Oba NL & CR",
"notConnected": "Nepřipojen. Zvolte desku a port pro automatické připojení.",
"message": "Zpráva ({0}+ Enter pro odeslání zprávy do '{1}' na '{2}')",
"connectionBusy": "Připojení se nezdařilo, sériový port je zaneprázdněn: {0}",
"disconnected": "Odpojen {0}od {1}.",
"unexpectedError": "Nastala neočekávaná chyba. Opět připojuji {0} na portu {1}.",
"failedReconnect": "Nepodařilo se připojit {0}k sériovému portu po 10 pokusech po sobě. Sériový port {1} je zaneprázdněn. ",
"reconnect": "Opětovné připojení {0} k {1}za [2] vteřin..."
},
"component": {
"uninstall": "Odinstalovat",
"uninstallMsg": "Chcete odinstalovat {0}?",
"by": "od",
"version": "Verze {0}",
"moreInfo": "Více informací",
"install": "Instalovat",
"filterSearch": "Filtrovat vyhledávání... "
},
"electron": {
"couldNotSave": "Nebylo možné uložit sketch. Zkopírujte prosím neuloženou práci do Vašeho oblíbeného textového editoru a restartujte IDE. ",
"unsavedChanges": "Jakékoli změny nebudou uloženy. "
},
"compile": {
"error": "Chyba kompilace: {0}"
},
"upload": {
"error": "{0}chyba: {1}"
},
"burnBootloader": {
"error": "Chyba během nahrávání zavaděče: {0}"
}
},
"theia": {
"core": {
"couldNotSave": "Nebylo možné uložit sketch. Zkopírujte prosím neuloženou práci do Vašeho oblíbeného textového editoru a restartujte IDE. ",
"offline": "Nepřipojen",
"daemonOffline": "CLI Daemon nepřipojen",
"cannotConnectBackend": "Nebylo možné se připojit k backendu. ",
"cannotConnectDaemon": "Nebylo možné se připojit k CLI Daemon."
},
"debug": {
"start": "Start...",
"typeNotSupported": "Typ \"{0}\" debugovacího spojení není podporován, ",
"startError": "Vyskytla se chyba při spouštění debugovacího spojení, zkontrolujte prosím log pro více informací. "
},
"editor": {
"unsavedTitle": "Neuloženo {0}"
},
"messages": {
"expand": "Rozšířit",
"collapse": "Složit"
},
"workspace": {
"fileNewName": "Název pro nový soubor",
"invalidFilename": "Neplatný název souboru.",
"invalidExtension": ".{0}je neplatná přípona",
"newFileName": "Nový název souboru",
"deleteCurrentSketch": "Opravdu chcete vymazat aktuální sketch? ",
"sketchDirectoryError": "Vyskytla se chyba při vytváření složky pro sketch. Více informací naleznete v logu. Aplikace nebude pravděpodobně pracovat správně. "
}
},
"cloud": {
"GoToCloud": "Přejdi do Cloudu"
}
}

View File

@@ -3,7 +3,7 @@
"common": {
"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}",
"selectedOn": "an {0}",
"notConnected": "[keine Verbindung]",
"serialMonitor": "Serieller Monitor",
"oldFormat": "Der Sketch '{0}' verwendet noch das alte '.pde' Format. Möchtest du auf die neuere '.ino' Endung wechseln?",
@@ -12,7 +12,23 @@
"unknown": "unbekannt",
"processing": "Verarbeiten",
"saveChangesToSketch": "Änderungen zu diesem Sketch vor dem Schließen sichern?",
"loseChanges": "Wenn du nicht sicherst, werden deine Änderungen gelöscht."
"loseChanges": "Wenn du nicht speicherst, werden deine Änderungen gelöscht."
},
"ide-updater": {
"errorCheckingForUpdates": "Fehler bei der Suche nach IDE Updates{0}",
"notNowButton": "Später",
"versionDownloaded": "Arduino IDE {0} wurde heruntergeladen",
"closeToInstallNotice": "Schließe die Software und installiere das Update auf deinem Computer",
"closeAndInstallButton": "Schließen und Installieren",
"downloadingNotice": "Die neueste Version der Arduino IDE wird heruntergeladen",
"updateAvailable": "Update Verfügbar",
"newVersionAvailable": "Eine neue Version der Arduino IDE ({0}) ist zum Download verfügbar",
"skipVersionButton": "Version Überspringen",
"downloadButton": "Download",
"goToDownloadPage": "Eine Update für die Arduino IDE ist verfügbar, konnte aber nicht automatisch heruntergeladen und installiert werden. Bitte gehen sie zur Download-Seite und laden sie dort die neueste Version herunter",
"goToDownloadButton": "Zum Download Wechseln",
"ideUpdaterDialog": "Software Update",
"noUpdatesAvailable": "Es gibt keine neuen Updates für die Arduino IDE"
},
"menu": {
"sketch": "Sketch",
@@ -31,7 +47,7 @@
"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.",
"ide.updateChannel": "Von welchem Kannal die Updates kommen sollen. 'stabil' ist die stabile Version, 'nächtlich' ist die neueste Entwicklerversion",
"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.",
@@ -55,7 +71,6 @@
"compile": "Kompilieren",
"upload": "Hochladen",
"verifyAfterUpload": "Code nach Hochladen überprüfen ",
"checkForUpdates": "Beim Start nach Updates suchen",
"editorQuickSuggestions": "Schnelle Editor Vorschläge",
"additionalManagerURLs": "Zusätzliche Boardverwalter-URLs",
"noProxy": "Kein Proxy",
@@ -85,7 +100,7 @@
"share": "Teilen....",
"remote": "Remote",
"emptySketchbook": "Dein Sketchbook ist leer",
"visitArduinoCloud": "Besuche Arduino Cloud um Cloud Sketche zu erstellen.",
"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",
@@ -173,7 +188,7 @@
"showFolder": "Zeige Sketch Ordner",
"sketch": "Sketch",
"moving": "Übertragen...",
"movingMsg": "Die Datei \"{0}\" muss sich in einem Sketch Ordner \"{1}\" befinden.\nDiesen Ordner erstellen, Datei verschieben und fortfahren?",
"movingMsg": "Die Datei \"{0}\" muss sich in einen Sketch Ordner \"{1}\" befinden.\nOrdner erstellen, Datei verschieben und fortfahren?",
"cantOpen": "Ein Ordner mit dem Namen \"{0}\" ist bereits vorhanden. Der Sketch kann nicht geöffnet werden.",
"saveFolderAs": "Sketch Ordner speichern als...",
"sketchbook": "Sketchbook",
@@ -223,7 +238,8 @@
"reference": "Referenz",
"findInReference": "Referenzsuche",
"faq": "Häufig gestellte Fragen",
"visit": "Besuche Arduino.cc"
"visit": "Besuche Arduino.cc",
"privacyPolicy": "Datenschutzbestimmungen"
},
"certificate": {
"uploadRootCertificates": "SSL Root Zertifikat hochladen",
@@ -243,7 +259,7 @@
"noSupportedBoardConnected": "Kein unterstütztes Board angeschlossen"
},
"firmware": {
"updater": "WiFi101 / WiFiNINA Firmware Aktualisierer",
"updater": "WiFi101 / WiFiNINA Firmware Aktualisierer",
"selectBoard": "Board auswählen",
"checkUpdates": "Nach Updates Suchen",
"selectVersion": "Firmware Version wählen",
@@ -269,7 +285,7 @@
"carriageReturn": "Zeilenumbruch",
"newLineCarriageReturn": "Sowohl NL als auch CR",
"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",
"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.",

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