mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-09-26 13:18:32 +00:00
Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
06ad7d143c | ||
![]() |
b78c4621fc |
124
.eslintrc.js
124
.eslintrc.js
@@ -1,66 +1,66 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser', // Specifies the ESLint parser
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
|
||||
sourceType: 'module', // Allows for the use of imports
|
||||
ecmaFeatures: {
|
||||
jsx: true, // Allows for the parsing of JSX
|
||||
parser: '@typescript-eslint/parser', // Specifies the ESLint parser
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
|
||||
sourceType: 'module', // Allows for the use of imports
|
||||
ecmaFeatures: {
|
||||
jsx: true, // Allows for the parsing of JSX
|
||||
},
|
||||
},
|
||||
},
|
||||
ignorePatterns: [
|
||||
'node_modules/*',
|
||||
'**/node_modules/*',
|
||||
'.github/*',
|
||||
'.browser_modules/*',
|
||||
'docs/*',
|
||||
'scripts/*',
|
||||
'electron-app/lib/*',
|
||||
'electron-app/src-gen/*',
|
||||
'electron-app/gen-webpack*.js',
|
||||
'!electron-app/webpack.config.js',
|
||||
'electron-app/plugins/*',
|
||||
'arduino-ide-extension/src/node/cli-protocol',
|
||||
'**/lib/*',
|
||||
],
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect', // Tells eslint-plugin-react to automatically detect the version of React to use
|
||||
},
|
||||
},
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin
|
||||
'plugin:react/recommended', // Uses the recommended rules from @eslint-plugin-react
|
||||
'plugin:react-hooks/recommended', // Uses recommended rules from react hooks
|
||||
'plugin:prettier/recommended',
|
||||
'prettier', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
|
||||
],
|
||||
plugins: ['prettier', 'unused-imports'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-expressions': 'off',
|
||||
'@typescript-eslint/no-namespace': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'warn',
|
||||
'@typescript-eslint/no-empty-interface': 'warn',
|
||||
'no-unused-vars': 'off',
|
||||
'unused-imports/no-unused-imports': 'error',
|
||||
'unused-imports/no-unused-vars': [
|
||||
'warn',
|
||||
{
|
||||
vars: 'all',
|
||||
varsIgnorePattern: '^_',
|
||||
args: 'after-used',
|
||||
argsIgnorePattern: '^_',
|
||||
},
|
||||
ignorePatterns: [
|
||||
'node_modules/*',
|
||||
'**/node_modules/*',
|
||||
'.node_modules/*',
|
||||
'.github/*',
|
||||
'.browser_modules/*',
|
||||
'docs/*',
|
||||
'scripts/*',
|
||||
'electron-app/lib/*',
|
||||
'electron-app/src-gen/*',
|
||||
'electron-app/gen-webpack*.js',
|
||||
'!electron-app/webpack.config.js',
|
||||
'plugins/*',
|
||||
'arduino-ide-extension/src/node/cli-protocol',
|
||||
],
|
||||
'react/display-name': 'warn',
|
||||
eqeqeq: ['error', 'smart'],
|
||||
'guard-for-in': 'off',
|
||||
'id-blacklist': 'off',
|
||||
'id-match': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-unused-expressions': 'off',
|
||||
'no-var': 'error',
|
||||
radix: 'error',
|
||||
'prettier/prettier': 'warn',
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect', // Tells eslint-plugin-react to automatically detect the version of React to use
|
||||
},
|
||||
},
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin
|
||||
'plugin:react/recommended', // Uses the recommended rules from @eslint-plugin-react
|
||||
'plugin:react-hooks/recommended', // Uses recommended rules from react hooks
|
||||
'plugin:prettier/recommended',
|
||||
'prettier', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
|
||||
],
|
||||
plugins: ['prettier', 'unused-imports'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-expressions': 'off',
|
||||
'@typescript-eslint/no-namespace': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'warn',
|
||||
'@typescript-eslint/no-empty-interface': 'warn',
|
||||
'no-unused-vars': 'off',
|
||||
'unused-imports/no-unused-imports': 'error',
|
||||
'unused-imports/no-unused-vars': [
|
||||
'warn',
|
||||
{
|
||||
vars: 'all',
|
||||
varsIgnorePattern: '^_',
|
||||
args: 'after-used',
|
||||
argsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
'react/display-name': 'warn',
|
||||
eqeqeq: ['error', 'smart'],
|
||||
'guard-for-in': 'off',
|
||||
'id-blacklist': 'off',
|
||||
'id-match': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-unused-expressions': 'off',
|
||||
'no-var': 'error',
|
||||
radix: 'error',
|
||||
'prettier/prettier': 'warn',
|
||||
},
|
||||
};
|
||||
|
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: Bug report
|
||||
description: Report a problem with the code or documentation in this repository.
|
||||
labels:
|
||||
- 'type: imperfection'
|
||||
- "type: imperfection"
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
|
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: Feature request
|
||||
description: Suggest an enhancement to this project.
|
||||
labels:
|
||||
- 'type: enhancement'
|
||||
- "type: enhancement"
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
|
11
.github/PULL_REQUEST_TEMPLATE.md
vendored
11
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,18 +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)
|
||||
* [ ] 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)
|
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -12,4 +12,4 @@ updates:
|
||||
schedule:
|
||||
interval: daily
|
||||
labels:
|
||||
- 'topic: infrastructure'
|
||||
- "topic: infrastructure"
|
||||
|
32
.github/label-configuration-files/labels.yml
vendored
32
.github/label-configuration-files/labels.yml
vendored
@@ -1,27 +1,27 @@
|
||||
# Used by the "Sync Labels" workflow
|
||||
# See: https://github.com/Financial-Times/github-label-sync#label-config-file
|
||||
|
||||
- name: 'topic: accessibility'
|
||||
color: '00ffff'
|
||||
- name: "topic: accessibility"
|
||||
color: "00ffff"
|
||||
description: Enabling the use of the software by everyone
|
||||
- name: 'topic: CLI'
|
||||
color: '00ffff'
|
||||
- name: "topic: CLI"
|
||||
color: "00ffff"
|
||||
description: Related to Arduino CLI
|
||||
- name: 'topic: cloud'
|
||||
color: '00ffff'
|
||||
- name: "topic: cloud"
|
||||
color: "00ffff"
|
||||
description: Related to Arduino Cloud and cloud sketches
|
||||
- name: 'topic: debugger'
|
||||
color: '00ffff'
|
||||
- name: "topic: debugger"
|
||||
color: "00ffff"
|
||||
description: Related to the integrated debugger
|
||||
- name: 'topic: language server'
|
||||
color: '00ffff'
|
||||
- name: "topic: language server"
|
||||
color: "00ffff"
|
||||
description: Related to the Arduino Language Server
|
||||
- name: 'topic: serial monitor'
|
||||
color: '00ffff'
|
||||
- name: "topic: serial monitor"
|
||||
color: "00ffff"
|
||||
description: Related to the Serial Monitor
|
||||
- name: 'topic: theia'
|
||||
color: '00ffff'
|
||||
- name: "topic: theia"
|
||||
color: "00ffff"
|
||||
description: Related to the Theia IDE framework
|
||||
- name: 'topic: theme'
|
||||
color: '00ffff'
|
||||
- name: "topic: theme"
|
||||
color: "00ffff"
|
||||
description: Related to GUI theming
|
||||
|
112
.github/workflows/assets/linux.Dockerfile
vendored
112
.github/workflows/assets/linux.Dockerfile
vendored
@@ -1,112 +0,0 @@
|
||||
# The Arduino IDE Linux build workflow job runs in this container.
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM ubuntu:18.04
|
||||
|
||||
# See: https://unofficial-builds.nodejs.org/download/release/
|
||||
ARG node_version="18.17.1"
|
||||
|
||||
RUN \
|
||||
apt-get \
|
||||
--yes \
|
||||
update
|
||||
|
||||
# This is required to get add-apt-repository
|
||||
RUN \
|
||||
apt-get \
|
||||
--yes \
|
||||
install \
|
||||
"software-properties-common=0.96.24.32.22"
|
||||
|
||||
# Install Git
|
||||
# The PPA is required to get a modern version of Git. The version in the Ubuntu 18.04 package repository is 2.17.1,
|
||||
# while action/checkout@v3 requires 2.18 or higher.
|
||||
RUN \
|
||||
add-apt-repository \
|
||||
--yes \
|
||||
"ppa:git-core/ppa" && \
|
||||
apt-get \
|
||||
--yes \
|
||||
update && \
|
||||
\
|
||||
apt-get \
|
||||
--yes \
|
||||
install \
|
||||
"git" && \
|
||||
\
|
||||
apt-get \
|
||||
--yes \
|
||||
purge \
|
||||
"software-properties-common"
|
||||
|
||||
# The repository path must be added to safe.directory, otherwise any Git operations on it would fail with a
|
||||
# "dubious ownership" error. actions/checkout configures this, but it is not applied to containers.
|
||||
RUN \
|
||||
git config \
|
||||
--add \
|
||||
--global \
|
||||
"safe.directory" "/__w/arduino-ide/arduino-ide"
|
||||
ENV \
|
||||
GIT_CONFIG_GLOBAL="/root/.gitconfig"
|
||||
|
||||
# Install Python
|
||||
# The Python installed by actions/setup-python has dependency on a higher version of glibc than available in the
|
||||
# ubuntu:18.04 container.
|
||||
RUN \
|
||||
apt-get \
|
||||
--yes \
|
||||
install \
|
||||
"python3.8-minimal=3.8.0-3ubuntu1~18.04.2" && \
|
||||
\
|
||||
ln \
|
||||
--symbolic \
|
||||
--force \
|
||||
"$(which python3.8)" \
|
||||
"/usr/bin/python3"
|
||||
|
||||
# Install Theia's package dependencies
|
||||
# These are pre-installed in the GitHub Actions hosted runner machines.
|
||||
RUN \
|
||||
apt-get \
|
||||
--yes \
|
||||
install \
|
||||
"libsecret-1-dev=0.18.6-1" \
|
||||
"libx11-dev=2:1.6.4-3ubuntu0.4" \
|
||||
"libxkbfile-dev=1:1.0.9-2"
|
||||
|
||||
# Install Node.js
|
||||
# It is necessary to use the "unofficial" linux-x64-glibc-217 build because the official Node.js 18.x is dynamically
|
||||
# linked against glibc 2.28, while Ubuntu 18.04 has glibc 2.27.
|
||||
ARG node_installation_path="/tmp/node-installation"
|
||||
ARG artifact_name="node-v${node_version}-linux-x64-glibc-217"
|
||||
RUN \
|
||||
mkdir "$node_installation_path" && \
|
||||
cd "$node_installation_path" && \
|
||||
\
|
||||
apt-get \
|
||||
--yes \
|
||||
install \
|
||||
"wget=1.19.4-1ubuntu2.2" && \
|
||||
\
|
||||
archive_name="${artifact_name}.tar.xz" && \
|
||||
wget \
|
||||
"https://unofficial-builds.nodejs.org/download/release/v${node_version}/${archive_name}" && \
|
||||
\
|
||||
apt-get \
|
||||
--yes \
|
||||
purge \
|
||||
"wget" && \
|
||||
\
|
||||
tar \
|
||||
--file="$archive_name" \
|
||||
--extract && \
|
||||
rm "$archive_name"
|
||||
ENV PATH="${PATH}:${node_installation_path}/${artifact_name}/bin"
|
||||
|
||||
# Install Yarn
|
||||
# Yarn is pre-installed in the GitHub Actions hosted runner machines.
|
||||
RUN \
|
||||
npm \
|
||||
install \
|
||||
--global \
|
||||
"yarn@1.22.19"
|
480
.github/workflows/build.yml
vendored
480
.github/workflows/build.yml
vendored
@@ -12,17 +12,11 @@ on:
|
||||
- '.vscode/**'
|
||||
- 'docs/**'
|
||||
- 'scripts/**'
|
||||
- '!scripts/merge-channel-files.js'
|
||||
- 'static/**'
|
||||
- '*.md'
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+.[0-9]+*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
paid-runners:
|
||||
description: Include builds on non-free runners
|
||||
type: boolean
|
||||
default: false
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '.github/**'
|
||||
@@ -30,103 +24,16 @@ on:
|
||||
- '.vscode/**'
|
||||
- 'docs/**'
|
||||
- 'scripts/**'
|
||||
- '!scripts/merge-channel-files.js'
|
||||
- 'static/**'
|
||||
- '*.md'
|
||||
schedule:
|
||||
- cron: '0 3 * * *' # run every day at 3AM (https://docs.github.com/en/actions/reference/events-that-trigger-workflows#scheduled-events-schedule)
|
||||
workflow_run:
|
||||
workflows:
|
||||
- Push Container Images
|
||||
branches:
|
||||
- main
|
||||
types:
|
||||
- completed
|
||||
|
||||
env:
|
||||
# See vars.GO_VERSION field of https://github.com/arduino/arduino-cli/blob/master/DistTasks.yml
|
||||
GO_VERSION: '1.21'
|
||||
# See: https://github.com/actions/setup-node/#readme
|
||||
NODE_VERSION: '18.17'
|
||||
GO_VERSION: "1.19"
|
||||
JOB_TRANSFER_ARTIFACT: build-artifacts
|
||||
CHANGELOG_ARTIFACTS: changelog
|
||||
STAGED_CHANNEL_FILES_ARTIFACT: staged-channel-files
|
||||
BASE_BUILD_DATA: |
|
||||
- config:
|
||||
# Human identifier for the job.
|
||||
name: Windows
|
||||
runs-on: [self-hosted, windows-sign-pc]
|
||||
# The value is a string representing a JSON document.
|
||||
# Setting this to null causes the job to run directly in the runner machine instead of in a container.
|
||||
container: |
|
||||
null
|
||||
# Name of the secret that contains the certificate.
|
||||
certificate-secret: INSTALLER_CERT_WINDOWS_CER
|
||||
# Name of the secret that contains the certificate password.
|
||||
certificate-password-secret: INSTALLER_CERT_WINDOWS_PASSWORD
|
||||
# File extension for the certificate.
|
||||
certificate-extension: pfx
|
||||
# Container for windows cert signing
|
||||
certificate-container: INSTALLER_CERT_WINDOWS_CONTAINER
|
||||
# Quoting on the value is required here to allow the same comparison expression syntax to be used for this
|
||||
# and the companion needs.select-targets.outputs.merge-channel-files property (output values always have string
|
||||
# type).
|
||||
mergeable-channel-file: 'false'
|
||||
# as this runs on a self hosted runner, we need to avoid building with the default working directory path,
|
||||
# otherwise paths in the build job will be too long for `light.exe`
|
||||
# we use the below as a Symbolic link (just changing the wd will break the checkout action)
|
||||
# this is a work around (see: https://github.com/actions/checkout/issues/197).
|
||||
working-directory: 'C:\a'
|
||||
artifacts:
|
||||
- path: '*Windows_64bit.exe'
|
||||
name: Windows_X86-64_interactive_installer
|
||||
- path: '*Windows_64bit.msi'
|
||||
name: Windows_X86-64_MSI
|
||||
- path: '*Windows_64bit.zip'
|
||||
name: Windows_X86-64_zip
|
||||
- config:
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container: |
|
||||
{
|
||||
\"image\": \"ghcr.io/arduino/arduino-ide/linux:main\"
|
||||
}
|
||||
mergeable-channel-file: 'false'
|
||||
artifacts:
|
||||
- path: '*Linux_64bit.zip'
|
||||
name: Linux_X86-64_zip
|
||||
- path: '*Linux_64bit.AppImage'
|
||||
name: Linux_X86-64_app_image
|
||||
- config:
|
||||
name: macOS x86
|
||||
runs-on: macos-13
|
||||
container: |
|
||||
null
|
||||
# APPLE_SIGNING_CERTIFICATE_P12 secret was produced by following the procedure from:
|
||||
# https://www.kencochrane.com/2020/08/01/build-and-sign-golang-binaries-for-macos-with-github-actions/#exporting-the-developer-certificate
|
||||
certificate-secret: APPLE_SIGNING_CERTIFICATE_P12
|
||||
certificate-password-secret: KEYCHAIN_PASSWORD
|
||||
certificate-extension: p12
|
||||
mergeable-channel-file: 'true'
|
||||
artifacts:
|
||||
- path: '*macOS_64bit.dmg'
|
||||
name: macOS_X86-64_dmg
|
||||
- path: '*macOS_64bit.zip'
|
||||
name: macOS_X86-64_zip
|
||||
- config:
|
||||
name: macOS ARM
|
||||
runs-on: macos-latest
|
||||
container: |
|
||||
null
|
||||
certificate-secret: APPLE_SIGNING_CERTIFICATE_P12
|
||||
certificate-password-secret: KEYCHAIN_PASSWORD
|
||||
certificate-extension: p12
|
||||
mergeable-channel-file: 'true'
|
||||
artifacts:
|
||||
- path: '*macOS_arm64.dmg'
|
||||
name: macOS_arm64_dmg
|
||||
- path: '*macOS_arm64.zip'
|
||||
name: macOS_arm64_zip
|
||||
|
||||
jobs:
|
||||
run-determination:
|
||||
@@ -153,210 +60,56 @@ jobs:
|
||||
|
||||
echo "result=$RESULT" >> $GITHUB_OUTPUT
|
||||
|
||||
build-type-determination:
|
||||
build:
|
||||
name: build (${{ matrix.config.os }})
|
||||
needs: run-determination
|
||||
if: needs.run-determination.outputs.result == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
is-release: ${{ steps.determination.outputs.is-release }}
|
||||
is-nightly: ${{ steps.determination.outputs.is-nightly }}
|
||||
channel-name: ${{ steps.determination.outputs.channel-name }}
|
||||
publish-to-s3: ${{ steps.determination.outputs.publish-to-s3 }}
|
||||
permissions: {}
|
||||
steps:
|
||||
- name: Determine the type of build
|
||||
id: determination
|
||||
run: |
|
||||
if [[
|
||||
"${{ startsWith(github.ref, 'refs/tags/') }}" == "true"
|
||||
]]; then
|
||||
is_release="true"
|
||||
is_nightly="false"
|
||||
channel_name="stable"
|
||||
elif [[
|
||||
"${{ github.event_name }}" == "schedule" ||
|
||||
(
|
||||
"${{ github.event_name }}" == "workflow_dispatch" &&
|
||||
"${{ github.ref }}" == "refs/heads/main"
|
||||
)
|
||||
]]; then
|
||||
is_release="false"
|
||||
is_nightly="true"
|
||||
channel_name="nightly"
|
||||
else
|
||||
is_release="false"
|
||||
is_nightly="false"
|
||||
channel_name="nightly"
|
||||
fi
|
||||
|
||||
echo "is-release=$is_release" >> $GITHUB_OUTPUT
|
||||
echo "is-nightly=$is_nightly" >> $GITHUB_OUTPUT
|
||||
echo "channel-name=$channel_name" >> $GITHUB_OUTPUT
|
||||
# Only attempt upload to Amazon S3 if the credentials are available.
|
||||
echo "publish-to-s3=${{ secrets.AWS_SECRET_ACCESS_KEY != '' }}" >> $GITHUB_OUTPUT
|
||||
|
||||
select-targets:
|
||||
needs: build-type-determination
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
artifact-matrix: ${{ steps.assemble.outputs.artifact-matrix }}
|
||||
build-matrix: ${{ steps.assemble.outputs.build-matrix }}
|
||||
merge-channel-files: ${{ steps.assemble.outputs.merge-channel-files }}
|
||||
permissions: {}
|
||||
steps:
|
||||
- name: Assemble target data
|
||||
id: assemble
|
||||
run: |
|
||||
# Only run the builds that incur runner charges on release or select manually triggered runs.
|
||||
if [[
|
||||
"${{ needs.build-type-determination.outputs.is-release }}" == "true" ||
|
||||
"${{ github.event.inputs.paid-runners }}" == "true"
|
||||
]]; then
|
||||
build_matrix="$(
|
||||
(
|
||||
echo "${{ env.BASE_BUILD_DATA }}";
|
||||
echo "${{ env.PAID_RUNNER_BUILD_DATA }}"
|
||||
) | \
|
||||
yq \
|
||||
--output-format json \
|
||||
'[.[].config]'
|
||||
)"
|
||||
|
||||
artifact_matrix="$(
|
||||
(
|
||||
echo "${{ env.BASE_BUILD_DATA }}";
|
||||
echo "${{ env.PAID_RUNNER_BUILD_DATA }}"
|
||||
) | \
|
||||
yq \
|
||||
--output-format json \
|
||||
'[.[].artifacts.[]]'
|
||||
)"
|
||||
|
||||
# The build matrix produces two macOS jobs (x86 and ARM) so the "channel update info files"
|
||||
# generated by each must be merged.
|
||||
merge_channel_files="true"
|
||||
|
||||
else
|
||||
build_matrix="$(
|
||||
echo "${{ env.BASE_BUILD_DATA }}" | \
|
||||
yq \
|
||||
--output-format json \
|
||||
'[.[].config]'
|
||||
)"
|
||||
|
||||
artifact_matrix="$(
|
||||
echo "${{ env.BASE_BUILD_DATA }}" | \
|
||||
yq \
|
||||
--output-format json \
|
||||
'[.[].artifacts.[]]'
|
||||
)"
|
||||
|
||||
merge_channel_files="false"
|
||||
fi
|
||||
|
||||
# Set workflow step outputs.
|
||||
# See: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings
|
||||
delimiter="$RANDOM"
|
||||
echo "build-matrix<<$delimiter" >> $GITHUB_OUTPUT
|
||||
echo "$build_matrix" >> $GITHUB_OUTPUT
|
||||
echo "$delimiter" >> $GITHUB_OUTPUT
|
||||
|
||||
delimiter="$RANDOM"
|
||||
echo "artifact-matrix<<$delimiter" >> $GITHUB_OUTPUT
|
||||
echo "$artifact_matrix" >> $GITHUB_OUTPUT
|
||||
echo "$delimiter" >> $GITHUB_OUTPUT
|
||||
|
||||
echo "merge-channel-files=$merge_channel_files" >> $GITHUB_OUTPUT
|
||||
|
||||
build:
|
||||
name: build (${{ matrix.config.name }})
|
||||
needs:
|
||||
- build-type-determination
|
||||
- select-targets
|
||||
env:
|
||||
# https://github.blog/changelog/2024-03-07-github-actions-all-actions-will-run-on-node20-instead-of-node16-by-default/
|
||||
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true
|
||||
# Location of artifacts generated by build.
|
||||
BUILD_ARTIFACTS_PATH: electron-app/dist/build-artifacts
|
||||
# to skip passing signing credentials to electron-builder
|
||||
IS_WINDOWS_CONFIG: ${{ matrix.config.name == 'Windows' }}
|
||||
INSTALLER_CERT_WINDOWS_CER: "/tmp/cert.cer"
|
||||
# We are hardcoding the path for signtool because is not present on the windows PATH env var by default.
|
||||
# Keep in mind that this path could change when upgrading to a new runner version
|
||||
SIGNTOOL_PATH: "C:/Program Files (x86)/Windows Kits/10/bin/10.0.19041.0/x86/signtool.exe"
|
||||
WIN_CERT_PASSWORD: ${{ secrets.INSTALLER_CERT_WINDOWS_PASSWORD }}
|
||||
WIN_CERT_CONTAINER_NAME: ${{ secrets.INSTALLER_CERT_WINDOWS_CONTAINER }}
|
||||
strategy:
|
||||
matrix:
|
||||
config: ${{ fromJson(needs.select-targets.outputs.build-matrix) }}
|
||||
runs-on: ${{ matrix.config.runs-on }}
|
||||
container: ${{ fromJSON(matrix.config.container) }}
|
||||
defaults:
|
||||
run:
|
||||
# Avoid problems caused by different default shell for container jobs (sh) vs non-container jobs (bash).
|
||||
shell: bash
|
||||
|
||||
config:
|
||||
- os: windows-2019
|
||||
certificate-secret: WINDOWS_SIGNING_CERTIFICATE_PFX # Name of the secret that contains the certificate.
|
||||
certificate-password-secret: WINDOWS_SIGNING_CERTIFICATE_PASSWORD # Name of the secret that contains the certificate password.
|
||||
certificate-extension: pfx # File extension for the certificate.
|
||||
- os: ubuntu-20.04
|
||||
- os: macos-latest
|
||||
# APPLE_SIGNING_CERTIFICATE_P12 secret was produced by following the procedure from:
|
||||
# https://www.kencochrane.com/2020/08/01/build-and-sign-golang-binaries-for-macos-with-github-actions/#exporting-the-developer-certificate
|
||||
certificate-secret: APPLE_SIGNING_CERTIFICATE_P12
|
||||
certificate-password-secret: KEYCHAIN_PASSWORD
|
||||
certificate-extension: p12
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
timeout-minutes: 90
|
||||
|
||||
steps:
|
||||
- name: Symlink custom working directory
|
||||
shell: cmd
|
||||
if: runner.os == 'Windows' && matrix.config.working-directory
|
||||
run: |
|
||||
if not exist "${{ matrix.config.working-directory }}" mklink /d "${{ matrix.config.working-directory }}" "C:\actions-runner\_work\arduino-ide\arduino-ide"
|
||||
|
||||
- name: Checkout
|
||||
if: fromJSON(matrix.config.container) == null
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Checkout
|
||||
# actions/checkout@v4 has dependency on a higher version of glibc than available in the Linux container.
|
||||
if: fromJSON(matrix.config.container) != null
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js
|
||||
if: fromJSON(matrix.config.container) == null && runner.os != 'Windows'
|
||||
uses: actions/setup-node@v4
|
||||
- name: Install Node.js 16.14
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
node-version: '16.14'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Python 3.x
|
||||
if: fromJSON(matrix.config.container) == null && runner.os != 'Windows'
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11.x'
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install Go
|
||||
if: fromJSON(matrix.config.container) == null && runner.os != 'Windows'
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Install Go
|
||||
# actions/setup-go@v5 has dependency on a higher version of glibc than available in the Linux container.
|
||||
if: fromJSON(matrix.config.container) != null && runner.os != 'Windows'
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Install Taskfile
|
||||
if: fromJSON(matrix.config.container) == null && runner.os != 'Windows'
|
||||
uses: arduino/setup-task@v2
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: 3.x
|
||||
|
||||
- name: Install Taskfile
|
||||
# actions/setup-task@v2 has dependency on a higher version of glibc than available in the Linux container.
|
||||
if: fromJSON(matrix.config.container) != null && runner.os != 'Windows'
|
||||
uses: arduino/setup-task@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: 3.x
|
||||
|
||||
- name: Package
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
AC_USERNAME: ${{ secrets.AC_USERNAME }}
|
||||
@@ -364,18 +117,12 @@ jobs:
|
||||
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: ${{ needs.build-type-determination.outputs.is-nightly }}
|
||||
IS_RELEASE: ${{ needs.build-type-determination.outputs.is-release }}
|
||||
IS_NIGHTLY: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') }}
|
||||
IS_RELEASE: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
CAN_SIGN: ${{ secrets[matrix.config.certificate-secret] != '' }}
|
||||
# The CREATE_* environment vars are only used to run tests. These secrets are optional. Dependent tests will
|
||||
# be skipped if not available.
|
||||
CREATE_USERNAME: ${{ secrets.CREATE_USERNAME }}
|
||||
CREATE_PASSWORD: ${{ secrets.CREATE_PASSWORD }}
|
||||
CREATE_CLIENT_SECRET: ${{ secrets.CREATE_CLIENT_SECRET }}
|
||||
working-directory: ${{ runner.os == 'Windows' && matrix.config.working-directory || './' }}
|
||||
run: |
|
||||
# See: https://www.electron.build/code-signing
|
||||
if [ $CAN_SIGN = false ] || [ $IS_WINDOWS_CONFIG = true ]; then
|
||||
if [ $CAN_SIGN = false ]; then
|
||||
echo "Skipping the app signing: certificate not provided."
|
||||
else
|
||||
export CSC_LINK="${{ runner.temp }}/signing_certificate.${{ matrix.config.certificate-extension }}"
|
||||
@@ -384,6 +131,10 @@ jobs:
|
||||
export CSC_FOR_PULL_REQUEST=true
|
||||
fi
|
||||
|
||||
if [ "${{ runner.OS }}" = "Windows" ]; then
|
||||
npm config set msvs_version 2017 --global
|
||||
fi
|
||||
|
||||
npx node-gyp install
|
||||
yarn install --immutable
|
||||
|
||||
@@ -395,122 +146,36 @@ jobs:
|
||||
yarn --cwd electron-app rebuild
|
||||
yarn --cwd electron-app build
|
||||
yarn --cwd electron-app package
|
||||
|
||||
# Both macOS jobs generate a "channel update info file" with same path and name. The second job to complete would
|
||||
# overwrite the file generated by the first in the workflow artifact.
|
||||
- name: Stage channel file for merge
|
||||
if: >
|
||||
needs.select-targets.outputs.merge-channel-files == 'true' &&
|
||||
matrix.config.mergeable-channel-file == 'true'
|
||||
working-directory: ${{ runner.os == 'Windows' && matrix.config.working-directory || './' }}
|
||||
run: |
|
||||
staged_channel_files_path="${{ runner.temp }}/staged-channel-files"
|
||||
mkdir "$staged_channel_files_path"
|
||||
mv \
|
||||
"${{ env.BUILD_ARTIFACTS_PATH }}/${{ needs.build-type-determination.outputs.channel-name }}-mac.yml" \
|
||||
"${staged_channel_files_path}/${{ needs.build-type-determination.outputs.channel-name }}-mac-${{ runner.arch }}.yml"
|
||||
|
||||
# Set workflow environment variable for use in other steps.
|
||||
# See: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable
|
||||
echo "STAGED_CHANNEL_FILES_PATH=$staged_channel_files_path" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Upload staged-for-merge channel file artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
if: >
|
||||
needs.select-targets.outputs.merge-channel-files == 'true' &&
|
||||
matrix.config.mergeable-channel-file == 'true'
|
||||
with:
|
||||
if-no-files-found: error
|
||||
name: ${{ env.STAGED_CHANNEL_FILES_ARTIFACT }}
|
||||
path: ${{ runner.os == 'Windows' && matrix.config.working-directory && format('{0}/{1}', matrix.config.working-directory, env.STAGED_CHANNEL_FILES_PATH) || env.STAGED_CHANNEL_FILES_PATH }}
|
||||
|
||||
|
||||
- name: Upload [GitHub Actions]
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
|
||||
path: ${{ runner.os == 'Windows' && matrix.config.working-directory && format('{0}/{1}', matrix.config.working-directory, env.BUILD_ARTIFACTS_PATH) || env.BUILD_ARTIFACTS_PATH }}
|
||||
|
||||
- name: Manual Clean up for self-hosted runners
|
||||
if: runner.os == 'Windows' && matrix.config.working-directory
|
||||
shell: cmd
|
||||
run: |
|
||||
rmdir /s /q "${{ matrix.config.working-directory }}\${{ env.BUILD_ARTIFACTS_PATH }}"
|
||||
|
||||
merge-channel-files:
|
||||
needs:
|
||||
- build-type-determination
|
||||
- select-targets
|
||||
- build
|
||||
if: needs.select-targets.outputs.merge-channel-files == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
permissions: {}
|
||||
steps:
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
# See: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable
|
||||
echo "CHANNEL_FILES_PATH=${{ runner.temp }}/channel-files" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download staged-for-merge channel files artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ env.STAGED_CHANNEL_FILES_ARTIFACT }}
|
||||
path: ${{ env.CHANNEL_FILES_PATH }}
|
||||
|
||||
- name: Remove no longer needed artifact
|
||||
uses: geekyeggo/delete-artifact@v2
|
||||
with:
|
||||
name: ${{ env.STAGED_CHANNEL_FILES_ARTIFACT }}
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v2
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: 3.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Merge "channel update info files"
|
||||
run: |
|
||||
node \
|
||||
./scripts/merge-channel-files.js \
|
||||
--channel "${{ needs.build-type-determination.outputs.channel-name }}" \
|
||||
--input "${{ env.CHANNEL_FILES_PATH }}"
|
||||
|
||||
- name: Upload merged channel files to job transfer artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
if-no-files-found: error
|
||||
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
|
||||
path: ${{ env.CHANNEL_FILES_PATH }}
|
||||
path: electron-app/dist/build-artifacts
|
||||
|
||||
artifacts:
|
||||
name: ${{ matrix.artifact.name }} artifact
|
||||
needs:
|
||||
- select-targets
|
||||
- build
|
||||
needs: build
|
||||
if: always() && needs.build.result != 'skipped'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
artifact: ${{ fromJson(needs.select-targets.outputs.artifact-matrix) }}
|
||||
artifact:
|
||||
- path: '*Linux_64bit.zip'
|
||||
name: Linux_X86-64_zip
|
||||
- path: '*Linux_64bit.AppImage'
|
||||
name: Linux_X86-64_app_image
|
||||
- path: '*macOS_64bit.dmg'
|
||||
name: macOS_dmg
|
||||
- path: '*macOS_64bit.zip'
|
||||
name: macOS_zip
|
||||
- path: '*Windows_64bit.exe'
|
||||
name: Windows_X86-64_interactive_installer
|
||||
- path: '*Windows_64bit.msi'
|
||||
name: Windows_X86-64_MSI
|
||||
- path: '*Windows_64bit.zip'
|
||||
name: Windows_X86-64_zip
|
||||
|
||||
steps:
|
||||
- name: Download job transfer artifact
|
||||
@@ -526,22 +191,20 @@ jobs:
|
||||
path: ${{ env.JOB_TRANSFER_ARTIFACT }}/${{ matrix.artifact.path }}
|
||||
|
||||
changelog:
|
||||
needs:
|
||||
- build-type-determination
|
||||
- build
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
BODY: ${{ steps.changelog.outputs.BODY }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # To fetch all history for all branches and tags.
|
||||
|
||||
- name: Generate Changelog
|
||||
id: changelog
|
||||
env:
|
||||
IS_RELEASE: ${{ needs.build-type-determination.outputs.is-release }}
|
||||
IS_RELEASE: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
run: |
|
||||
export LATEST_TAG=$(git describe --abbrev=0)
|
||||
export GIT_LOG=$(git log --pretty=" - %s [%h]" $LATEST_TAG..HEAD | sed 's/ *$//g')
|
||||
@@ -567,27 +230,15 @@ jobs:
|
||||
echo "$BODY" > CHANGELOG.txt
|
||||
|
||||
- name: Upload Changelog [GitHub Actions]
|
||||
if: needs.build-type-determination.outputs.is-nightly == 'true'
|
||||
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main')
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
|
||||
path: CHANGELOG.txt
|
||||
|
||||
publish:
|
||||
needs:
|
||||
- build-type-determination
|
||||
- merge-channel-files
|
||||
- changelog
|
||||
if: >
|
||||
always() &&
|
||||
needs.build-type-determination.result == 'success' &&
|
||||
(
|
||||
needs.merge-channel-files.result == 'skipped' ||
|
||||
needs.merge-channel-files.result == 'success'
|
||||
) &&
|
||||
needs.changelog.result == 'success' &&
|
||||
needs.build-type-determination.outputs.publish-to-s3 == 'true' &&
|
||||
needs.build-type-determination.outputs.is-nightly == 'true'
|
||||
needs: changelog
|
||||
if: github.repository == 'arduino/arduino-ide' && (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main'))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download [GitHub Actions]
|
||||
@@ -607,19 +258,8 @@ jobs:
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
|
||||
release:
|
||||
needs:
|
||||
- build-type-determination
|
||||
- merge-channel-files
|
||||
- changelog
|
||||
if: >
|
||||
always() &&
|
||||
needs.build-type-determination.result == 'success' &&
|
||||
(
|
||||
needs.merge-channel-files.result == 'skipped' ||
|
||||
needs.merge-channel-files.result == 'success'
|
||||
) &&
|
||||
needs.changelog.result == 'success' &&
|
||||
needs.build-type-determination.outputs.is-release == 'true'
|
||||
needs: changelog
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download [GitHub Actions]
|
||||
@@ -643,8 +283,17 @@ jobs:
|
||||
file_glob: true
|
||||
body: ${{ needs.changelog.outputs.BODY }}
|
||||
|
||||
# Temporary measure to prevent release update offers before the manually produced builds are uploaded.
|
||||
# The step must be removed once fully automated builds are regained.
|
||||
- name: Remove "channel update info files" related to manual builds
|
||||
run: |
|
||||
# See: https://github.com/arduino/arduino-ide/issues/2018
|
||||
rm "${{ env.JOB_TRANSFER_ARTIFACT }}/stable-linux.yml"
|
||||
# See: https://github.com/arduino/arduino-ide/issues/408
|
||||
rm "${{ env.JOB_TRANSFER_ARTIFACT }}/stable-mac.yml"
|
||||
|
||||
- name: Publish Release [S3]
|
||||
if: needs.build-type-determination.outputs.publish-to-s3 == 'true'
|
||||
if: github.repository == 'arduino/arduino-ide'
|
||||
uses: docker://plugins/s3
|
||||
env:
|
||||
PLUGIN_SOURCE: '${{ env.JOB_TRANSFER_ARTIFACT }}/*'
|
||||
@@ -658,7 +307,6 @@ jobs:
|
||||
# This job must run after all jobs that use the transfer artifact.
|
||||
needs:
|
||||
- build
|
||||
- merge-channel-files
|
||||
- publish
|
||||
- release
|
||||
- artifacts
|
||||
|
65
.github/workflows/check-certificates.yml
vendored
65
.github/workflows/check-certificates.yml
vendored
@@ -74,11 +74,9 @@ jobs:
|
||||
- identifier: macOS signing certificate # Text used to identify certificate in notifications.
|
||||
certificate-secret: APPLE_SIGNING_CERTIFICATE_P12 # Name of the secret that contains the certificate.
|
||||
password-secret: KEYCHAIN_PASSWORD # Name of the secret that contains the certificate password.
|
||||
type: pkcs12
|
||||
- identifier: Windows signing certificate
|
||||
certificate-secret: INSTALLER_CERT_WINDOWS_CER
|
||||
# The password for the Windows certificate is not needed, because its not a container, but a single certificate.
|
||||
type: x509
|
||||
certificate-secret: WINDOWS_SIGNING_CERTIFICATE_PFX
|
||||
password-secret: WINDOWS_SIGNING_CERTIFICATE_PASSWORD
|
||||
|
||||
steps:
|
||||
- name: Set certificate path environment variable
|
||||
@@ -97,7 +95,7 @@ jobs:
|
||||
CERTIFICATE_PASSWORD: ${{ secrets[matrix.certificate.password-secret] }}
|
||||
run: |
|
||||
(
|
||||
openssl ${{ matrix.certificate.type }} \
|
||||
openssl pkcs12 \
|
||||
-in "${{ env.CERTIFICATE_PATH }}" \
|
||||
-legacy \
|
||||
-noout \
|
||||
@@ -124,43 +122,26 @@ jobs:
|
||||
CERTIFICATE_PASSWORD: ${{ secrets[matrix.certificate.password-secret] }}
|
||||
id: get-days-before-expiration
|
||||
run: |
|
||||
if [[ ${{ matrix.certificate.type }} == "pkcs12" ]]; then
|
||||
EXPIRATION_DATE="$(
|
||||
(
|
||||
openssl pkcs12 \
|
||||
-in "${{ env.CERTIFICATE_PATH }}" \
|
||||
-clcerts \
|
||||
-legacy \
|
||||
-nodes \
|
||||
-passin env:CERTIFICATE_PASSWORD
|
||||
) | (
|
||||
openssl x509 \
|
||||
-noout \
|
||||
-enddate
|
||||
) | (
|
||||
grep \
|
||||
--max-count=1 \
|
||||
--only-matching \
|
||||
--perl-regexp \
|
||||
'notAfter=(\K.*)'
|
||||
)
|
||||
)"
|
||||
elif [[ ${{ matrix.certificate.type }} == "x509" ]]; then
|
||||
EXPIRATION_DATE="$(
|
||||
(
|
||||
openssl x509 \
|
||||
-in ${{ env.CERTIFICATE_PATH }} \
|
||||
-noout \
|
||||
-enddate
|
||||
) | (
|
||||
grep \
|
||||
--max-count=1 \
|
||||
--only-matching \
|
||||
--perl-regexp \
|
||||
'notAfter=(\K.*)'
|
||||
)
|
||||
)"
|
||||
fi
|
||||
EXPIRATION_DATE="$(
|
||||
(
|
||||
openssl pkcs12 \
|
||||
-in "${{ env.CERTIFICATE_PATH }}" \
|
||||
-clcerts \
|
||||
-legacy \
|
||||
-nodes \
|
||||
-passin env:CERTIFICATE_PASSWORD
|
||||
) | (
|
||||
openssl x509 \
|
||||
-noout \
|
||||
-enddate
|
||||
) | (
|
||||
grep \
|
||||
--max-count=1 \
|
||||
--only-matching \
|
||||
--perl-regexp \
|
||||
'notAfter=(\K.*)'
|
||||
)
|
||||
)"
|
||||
|
||||
DAYS_BEFORE_EXPIRATION="$((($(date --utc --date="$EXPIRATION_DATE" +%s) - $(date --utc +%s)) / 60 / 60 / 24))"
|
||||
|
||||
|
58
.github/workflows/check-containers.yml
vendored
58
.github/workflows/check-containers.yml
vendored
@@ -1,58 +0,0 @@
|
||||
name: Check Containers
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/check-containers.ya?ml"
|
||||
- "**.Dockerfile"
|
||||
- "**/Dockerfile"
|
||||
push:
|
||||
paths:
|
||||
- ".github/workflows/check-containers.ya?ml"
|
||||
- "**.Dockerfile"
|
||||
- "**/Dockerfile"
|
||||
repository_dispatch:
|
||||
schedule:
|
||||
# Run periodically to catch breakage caused by external changes.
|
||||
- cron: "0 7 * * MON"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
run:
|
||||
name: Run (${{ matrix.image.path }})
|
||||
runs-on: ubuntu-latest
|
||||
permissions: {}
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
|
||||
env:
|
||||
IMAGE_NAME: name/app:latest
|
||||
REGISTRY: localhost:5000
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image:
|
||||
- path: .github/workflows/assets/linux.Dockerfile
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build and push to local registry
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ${{ matrix.image.path }}
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
- name: Run container
|
||||
run: |
|
||||
docker \
|
||||
run \
|
||||
--rm \
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
14
.github/workflows/check-i18n-task.yml
vendored
14
.github/workflows/check-i18n-task.yml
vendored
@@ -2,7 +2,7 @@ name: Check Internationalization
|
||||
|
||||
env:
|
||||
# See vars.GO_VERSION field of https://github.com/arduino/arduino-cli/blob/master/DistTasks.yml
|
||||
GO_VERSION: '1.21'
|
||||
GO_VERSION: "1.19"
|
||||
|
||||
# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows
|
||||
on:
|
||||
@@ -56,22 +56,22 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js 18.17
|
||||
uses: actions/setup-node@v4
|
||||
- name: Install Node.js 16.14
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.17'
|
||||
node-version: '16.14'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Install Taskfile
|
||||
uses: arduino/setup-task@v2
|
||||
uses: arduino/setup-task@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: 3.x
|
||||
|
8
.github/workflows/compose-full-changelog.yml
vendored
8
.github/workflows/compose-full-changelog.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
env:
|
||||
CHANGELOG_ARTIFACTS: changelog
|
||||
# See: https://github.com/actions/setup-node/#readme
|
||||
NODE_VERSION: '18.17'
|
||||
NODE_VERSION: 16.x
|
||||
|
||||
jobs:
|
||||
create-changelog:
|
||||
@@ -16,10 +16,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
- name: Create full changelog
|
||||
id: full-changelog
|
||||
run: |
|
||||
yarn add @octokit/rest@19.0.13 --ignore-workspace-root-check
|
||||
yarn add @octokit/rest --ignore-workspace-root-check
|
||||
mkdir "${{ github.workspace }}/${{ env.CHANGELOG_ARTIFACTS }}"
|
||||
|
||||
# Get the changelog file name to build
|
||||
|
14
.github/workflows/i18n-nightly-push.yml
vendored
14
.github/workflows/i18n-nightly-push.yml
vendored
@@ -2,7 +2,7 @@ name: i18n-nightly-push
|
||||
|
||||
env:
|
||||
# See vars.GO_VERSION field of https://github.com/arduino/arduino-cli/blob/master/DistTasks.yml
|
||||
GO_VERSION: '1.21'
|
||||
GO_VERSION: "1.19"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
@@ -14,22 +14,22 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js 18.17
|
||||
uses: actions/setup-node@v4
|
||||
- name: Install Node.js 16.14
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.17'
|
||||
node-version: '16.14'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v2
|
||||
uses: arduino/setup-task@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: 3.x
|
||||
|
14
.github/workflows/i18n-weekly-pull.yml
vendored
14
.github/workflows/i18n-weekly-pull.yml
vendored
@@ -2,7 +2,7 @@ name: i18n-weekly-pull
|
||||
|
||||
env:
|
||||
# See vars.GO_VERSION field of https://github.com/arduino/arduino-cli/blob/master/DistTasks.yml
|
||||
GO_VERSION: '1.21'
|
||||
GO_VERSION: "1.19"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
@@ -14,22 +14,22 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js 18.17
|
||||
uses: actions/setup-node@v4
|
||||
- name: Install Node.js 16.14
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.17'
|
||||
node-version: '16.14'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v2
|
||||
uses: arduino/setup-task@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: 3.x
|
||||
|
70
.github/workflows/push-container-images.yml
vendored
70
.github/workflows/push-container-images.yml
vendored
@@ -1,70 +0,0 @@
|
||||
name: Push Container Images
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/push-container-images.ya?ml"
|
||||
push:
|
||||
paths:
|
||||
- ".github/workflows/push-container-images.ya?ml"
|
||||
- "**.Dockerfile"
|
||||
- "**/Dockerfile"
|
||||
repository_dispatch:
|
||||
schedule:
|
||||
# Run periodically to catch breakage caused by external changes.
|
||||
- cron: "0 8 * * MON"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
push:
|
||||
name: Push (${{ matrix.image.name }})
|
||||
# Only run the job when GITHUB_TOKEN has the privileges required for Container registry login.
|
||||
if: >
|
||||
(
|
||||
github.event_name != 'pull_request' &&
|
||||
github.repository == 'arduino/arduino-ide'
|
||||
) ||
|
||||
(
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.head.repo.full_name == 'arduino/arduino-ide'
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image:
|
||||
- path: .github/workflows/assets/linux.Dockerfile
|
||||
name: ${{ github.repository }}/linux
|
||||
registry: ghcr.io
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
registry: ${{ matrix.image.registry }}
|
||||
username: ${{ github.repository_owner }}
|
||||
|
||||
- name: Extract metadata for image
|
||||
id: metadata
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ matrix.image.registry }}/${{ matrix.image.name }}
|
||||
|
||||
- name: Build and push image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ${{ matrix.image.path }}
|
||||
labels: ${{ steps.metadata.outputs.labels }}
|
||||
# Workflow is triggered on relevant events for the sake of a "dry run" validation but image is only pushed to
|
||||
# registry on commit to the main branch.
|
||||
push: ${{ github.ref == 'refs/heads/main' }}
|
||||
tags: ${{ steps.metadata.outputs.tags }}
|
14
.github/workflows/sync-labels.yml
vendored
14
.github/workflows/sync-labels.yml
vendored
@@ -5,15 +5,15 @@ name: Sync Labels
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/sync-labels.ya?ml'
|
||||
- '.github/label-configuration-files/*.ya?ml'
|
||||
- ".github/workflows/sync-labels.ya?ml"
|
||||
- ".github/label-configuration-files/*.ya?ml"
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/sync-labels.ya?ml'
|
||||
- '.github/label-configuration-files/*.ya?ml'
|
||||
- ".github/workflows/sync-labels.ya?ml"
|
||||
- ".github/label-configuration-files/*.ya?ml"
|
||||
schedule:
|
||||
# Run daily at 8 AM UTC to sync with changes to shared label configurations.
|
||||
- cron: '0 8 * * *'
|
||||
- cron: "0 8 * * *"
|
||||
workflow_dispatch:
|
||||
repository_dispatch:
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download JSON schema for labels configuration file
|
||||
id: download-schema
|
||||
@@ -106,7 +106,7 @@ jobs:
|
||||
echo "flag=--dry-run" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download configuration files artifact
|
||||
uses: actions/download-artifact@v3
|
||||
|
12
.github/workflows/themes-weekly-pull.yml
vendored
12
.github/workflows/themes-weekly-pull.yml
vendored
@@ -8,30 +8,30 @@ on:
|
||||
|
||||
env:
|
||||
# See vars.GO_VERSION field of https://github.com/arduino/arduino-cli/blob/master/DistTasks.yml
|
||||
GO_VERSION: '1.21'
|
||||
NODE_VERSION: '18.17'
|
||||
GO_VERSION: "1.19"
|
||||
NODE_VERSION: 16.x
|
||||
|
||||
jobs:
|
||||
pull-from-jsonbin:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v2
|
||||
uses: arduino/setup-task@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: 3.x
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,7 +1,7 @@
|
||||
node_modules/
|
||||
lib/
|
||||
downloads/
|
||||
arduino-ide-extension/src/node/resources
|
||||
resources/
|
||||
arduino-ide-extension/Examples/
|
||||
src-gen/
|
||||
gen-webpack.config.js
|
||||
|
@@ -1,11 +0,0 @@
|
||||
lib
|
||||
dist
|
||||
plugins
|
||||
src-gen
|
||||
i18n
|
||||
gen-webpack*
|
||||
.browser_modules
|
||||
arduino-ide-extension/src/node/resources
|
||||
cli-protocol
|
||||
*color-theme.json
|
||||
arduino-icons.json
|
7
.prettierrc
Normal file
7
.prettierrc
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"printWidth": 80,
|
||||
"endOfLine": "auto"
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"printWidth": 80,
|
||||
"endOfLine": "auto",
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.json",
|
||||
"options": {
|
||||
"tabWidth": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": "*.css",
|
||||
"options": {
|
||||
"tabWidth": 4,
|
||||
"singleQuote": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": "*.html",
|
||||
"options": {
|
||||
"tabWidth": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
15
.vscode/launch.json
vendored
15
.vscode/launch.json
vendored
@@ -7,7 +7,7 @@
|
||||
"name": "App",
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd",
|
||||
},
|
||||
"cwd": "${workspaceFolder}/electron-app",
|
||||
"args": [
|
||||
@@ -19,7 +19,7 @@
|
||||
"--no-app-auto-install",
|
||||
"--plugins=local-dir:./plugins",
|
||||
"--hosted-plugin-inspect=9339",
|
||||
"--no-ping-timeout"
|
||||
"--no-ping-timeout",
|
||||
],
|
||||
"env": {
|
||||
"NODE_ENV": "development"
|
||||
@@ -42,7 +42,7 @@
|
||||
"name": "App [Dev]",
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd",
|
||||
},
|
||||
"cwd": "${workspaceFolder}/electron-app",
|
||||
"args": [
|
||||
@@ -56,7 +56,7 @@
|
||||
"--hosted-plugin-inspect=9339",
|
||||
"--content-trace",
|
||||
"--open-devtools",
|
||||
"--no-ping-timeout"
|
||||
"--no-ping-timeout",
|
||||
],
|
||||
"env": {
|
||||
"NODE_ENV": "development"
|
||||
@@ -115,12 +115,15 @@
|
||||
"request": "attach",
|
||||
"name": "Attach by Process ID",
|
||||
"processId": "${command:PickProcess}"
|
||||
}
|
||||
},
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Launch Electron Backend & Frontend",
|
||||
"configurations": ["App", "Attach to Electron Frontend"]
|
||||
"configurations": [
|
||||
"App",
|
||||
"Attach to Electron Frontend"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -7,6 +7,6 @@
|
||||
},
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
}
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
}
|
||||
|
10
.vscode/tasks.json
vendored
10
.vscode/tasks.json
vendored
@@ -4,11 +4,8 @@
|
||||
{
|
||||
"label": "Rebuild App",
|
||||
"type": "shell",
|
||||
"command": "yarn rebuild",
|
||||
"command": "yarn rebuild:browser && yarn rebuild:electron",
|
||||
"group": "build",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/electron-app"
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new",
|
||||
@@ -40,7 +37,10 @@
|
||||
{
|
||||
"label": "Watch All",
|
||||
"type": "shell",
|
||||
"dependsOn": ["Watch Extension", "Watch App"]
|
||||
"dependsOn": [
|
||||
"Watch Extension",
|
||||
"Watch App"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
[](https://github.com/arduino/arduino-ide/actions?query=workflow%3A%22Arduino+IDE%22)
|
||||
|
||||
This repository contains the source code of the Arduino IDE 2.x. If you're looking for the old IDE, go to the [repository of the 1.x version](https://github.com/arduino/Arduino).
|
||||
This repository contains the source code of the Arduino IDE 2.x. If you're looking for the old IDE, go to the repository of the 1.x version at https://github.com/arduino/Arduino.
|
||||
|
||||
The Arduino IDE 2.x is a major rewrite, sharing no code with the IDE 1.x. It is based on the [Theia IDE](https://theia-ide.org/) framework and built with [Electron](https://www.electronjs.org/). The backend operations such as compilation and uploading are offloaded to an [arduino-cli](https://github.com/arduino/arduino-cli) instance running in daemon mode. This new IDE was developed with the goal of preserving the same interface and user experience of the previous major version in order to provide a frictionless upgrade.
|
||||
|
||||
|
@@ -55,14 +55,12 @@ The Config Service knows about your system, like for example the default sketch
|
||||
- checking whether a file is in a data or sketch directory
|
||||
|
||||
### `"arduino"` configuration in the `package.json`:
|
||||
|
||||
- `"cli"`:
|
||||
- `"version"` type `string` | `{ owner: string, repo: string, commitish?: string }`: if the type is a `string` and is a valid semver, it will get the corresponding [released](https://github.com/arduino/arduino-cli/releases) CLI. If the type is `string` and is a [date in `YYYYMMDD`](https://arduino.github.io/arduino-cli/latest/installation/#nightly-builds) format, it will get a nightly CLI. If the type is an object, a CLI, build from the sources in the `owner/repo` will be used. If `commitish` is not defined, the HEAD of the default branch will be used. In any other cases an error is thrown.
|
||||
- `"cli"`:
|
||||
- `"version"` type `string` | `{ owner: string, repo: string, commitish?: string }`: if the type is a `string` and is a valid semver, it will get the corresponding [released](https://github.com/arduino/arduino-cli/releases) CLI. If the type is `string` and is a [date in `YYYYMMDD`](https://arduino.github.io/arduino-cli/latest/installation/#nightly-builds) format, it will get a nightly CLI. If the type is an object, a CLI, build from the sources in the `owner/repo` will be used. If `commitish` is not defined, the HEAD of the default branch will be used. In any other cases an error is thrown.
|
||||
|
||||
#### Rebuild gRPC protocol interfaces
|
||||
|
||||
- Some CLI updates can bring changes to the gRPC interfaces, as the API might change. gRPC interfaces can be updated running the command
|
||||
`yarn --cwd arduino-ide-extension generate-protocol`
|
||||
- Some CLI updates can bring changes to the gRPC interfaces, as the API might change. gRPC interfaces can be updated running the command
|
||||
`yarn --cwd arduino-ide-extension generate-protocol`
|
||||
|
||||
### Update **clangd** and **ClangFormat**
|
||||
|
||||
@@ -74,13 +72,11 @@ The [**clangd** C++ language server](https://clangd.llvm.org/) and the [**ClangF
|
||||
1. Submit a pull request in [the `arduino/tooling-project-assets` repository](https://github.com/arduino/tooling-project-assets) to update the version in the `vars.DEFAULT_CLANG_FORMAT_VERSION` field of [`Taskfile.yml`](https://github.com/arduino/tooling-project-assets/blob/main/Taskfile.yml).
|
||||
|
||||
### Customize Icons
|
||||
|
||||
ArduinoIde uses a customized version of FontAwesome.
|
||||
In order to update/replace icons follow the following steps:
|
||||
|
||||
- import the file `arduino-icons.json` in [Icomoon](https://icomoon.io/app/#/projects)
|
||||
- load it
|
||||
- edit the icons as needed
|
||||
- !! download the **new** `arduino-icons.json` file and put it in this repo
|
||||
- Click on "Generate Font" in Icomoon, then download
|
||||
- place the updated fonts in the `src/style/fonts` directory
|
||||
- import the file `arduino-icons.json` in [Icomoon](https://icomoon.io/app/#/projects)
|
||||
- load it
|
||||
- edit the icons as needed
|
||||
- !! download the **new** `arduino-icons.json` file and put it in this repo
|
||||
- Click on "Generate Font" in Icomoon, then download
|
||||
- place the updated fonts in the `src/style/fonts` directory
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "arduino-ide-extension",
|
||||
"version": "2.3.3",
|
||||
"version": "2.2.0",
|
||||
"description": "An extension for Theia building the Arduino IDE",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
@@ -24,29 +24,29 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.8.14",
|
||||
"@theia/application-package": "1.41.0",
|
||||
"@theia/core": "1.41.0",
|
||||
"@theia/debug": "1.41.0",
|
||||
"@theia/editor": "1.41.0",
|
||||
"@theia/electron": "1.41.0",
|
||||
"@theia/filesystem": "1.41.0",
|
||||
"@theia/keymaps": "1.41.0",
|
||||
"@theia/markers": "1.41.0",
|
||||
"@theia/messages": "1.41.0",
|
||||
"@theia/monaco": "1.41.0",
|
||||
"@theia/application-package": "1.39.0",
|
||||
"@theia/core": "1.39.0",
|
||||
"@theia/debug": "1.39.0",
|
||||
"@theia/editor": "1.39.0",
|
||||
"@theia/electron": "1.39.0",
|
||||
"@theia/filesystem": "1.39.0",
|
||||
"@theia/keymaps": "1.39.0",
|
||||
"@theia/markers": "1.39.0",
|
||||
"@theia/messages": "1.39.0",
|
||||
"@theia/monaco": "1.39.0",
|
||||
"@theia/monaco-editor-core": "1.72.3",
|
||||
"@theia/navigator": "1.41.0",
|
||||
"@theia/outline-view": "1.41.0",
|
||||
"@theia/output": "1.41.0",
|
||||
"@theia/plugin-ext": "1.41.0",
|
||||
"@theia/preferences": "1.41.0",
|
||||
"@theia/scm": "1.41.0",
|
||||
"@theia/search-in-workspace": "1.41.0",
|
||||
"@theia/terminal": "1.41.0",
|
||||
"@theia/typehierarchy": "1.41.0",
|
||||
"@theia/workspace": "1.41.0",
|
||||
"@theia/navigator": "1.39.0",
|
||||
"@theia/outline-view": "1.39.0",
|
||||
"@theia/output": "1.39.0",
|
||||
"@theia/plugin-ext": "1.39.0",
|
||||
"@theia/preferences": "1.39.0",
|
||||
"@theia/scm": "1.39.0",
|
||||
"@theia/search-in-workspace": "1.39.0",
|
||||
"@theia/terminal": "1.39.0",
|
||||
"@theia/typehierarchy": "1.39.0",
|
||||
"@theia/workspace": "1.39.0",
|
||||
"@tippyjs/react": "^4.2.5",
|
||||
"@types/auth0-js": "^9.21.3",
|
||||
"@types/auth0-js": "^9.14.0",
|
||||
"@types/btoa": "^1.2.3",
|
||||
"@types/dateformat": "^3.0.1",
|
||||
"@types/google-protobuf": "^3.7.2",
|
||||
@@ -60,19 +60,18 @@
|
||||
"@types/temp": "^0.8.34",
|
||||
"arduino-serial-plotter-webapp": "0.2.0",
|
||||
"async-mutex": "^0.3.0",
|
||||
"auth0-js": "^9.23.2",
|
||||
"auth0-js": "^9.14.0",
|
||||
"btoa": "^1.2.1",
|
||||
"classnames": "^2.3.1",
|
||||
"cpy": "^10.0.0",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"dateformat": "^3.0.3",
|
||||
"deepmerge": "^4.2.2",
|
||||
"drivelist": "^9.2.4",
|
||||
"electron-updater": "^4.6.5",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-json-stable-stringify": "^2.1.0",
|
||||
"fast-safe-stringify": "^2.1.1",
|
||||
"filename-reserved-regex": "^2.0.0",
|
||||
"fqbn": "^1.0.5",
|
||||
"glob": "^7.1.6",
|
||||
"google-protobuf": "^3.20.1",
|
||||
"hash.js": "^1.1.7",
|
||||
@@ -110,7 +109,7 @@
|
||||
"devDependencies": {
|
||||
"@octokit/rest": "^18.12.0",
|
||||
"@types/chai": "^4.2.7",
|
||||
"@types/mocha": "^10.0.0",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/react-window": "^1.8.5",
|
||||
"@xhmikosr/downloader": "^13.0.1",
|
||||
"chai": "^4.2.0",
|
||||
@@ -119,16 +118,19 @@
|
||||
"decompress-tarbz2": "^4.1.1",
|
||||
"decompress-targz": "^4.1.1",
|
||||
"decompress-unzip": "^4.0.1",
|
||||
"grpc_tools_node_protoc_ts": "^5.3.3",
|
||||
"mocha": "^10.2.0",
|
||||
"grpc_tools_node_protoc_ts": "^4.1.0",
|
||||
"mocha": "^7.0.0",
|
||||
"mockdate": "^3.0.5",
|
||||
"moment": "^2.24.0",
|
||||
"ncp": "^2.0.0",
|
||||
"rimraf": "^2.6.1"
|
||||
"rimraf": "^2.6.1",
|
||||
"shelljs": "^0.8.3",
|
||||
"uuid": "^3.2.1",
|
||||
"yargs": "^11.1.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"grpc-tools": "^1.12.4",
|
||||
"@pingghost/protoc": "^1.0.2"
|
||||
"grpc-tools": "^1.9.0",
|
||||
"protoc": "^1.0.4"
|
||||
},
|
||||
"mocha": {
|
||||
"require": [
|
||||
@@ -170,17 +172,13 @@
|
||||
],
|
||||
"arduino": {
|
||||
"arduino-cli": {
|
||||
"version": "1.0.4"
|
||||
"version": "0.34.0"
|
||||
},
|
||||
"arduino-fwuploader": {
|
||||
"version": "2.4.1"
|
||||
"version": "2.4.0"
|
||||
},
|
||||
"arduino-language-server": {
|
||||
"version": {
|
||||
"owner": "arduino",
|
||||
"repo": "arduino-language-server",
|
||||
"commitish": "05ec308"
|
||||
}
|
||||
"version": "0.7.4"
|
||||
},
|
||||
"clangd": {
|
||||
"version": "14.0.0"
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
(async () => {
|
||||
const path = require('path');
|
||||
const shell = require('shelljs');
|
||||
const semver = require('semver');
|
||||
const moment = require('moment');
|
||||
const downloader = require('./downloader');
|
||||
@@ -28,8 +29,8 @@
|
||||
})();
|
||||
|
||||
if (!version) {
|
||||
console.log(`Could not retrieve CLI version info from the 'package.json'.`);
|
||||
process.exit(1);
|
||||
shell.echo(`Could not retrieve CLI version info from the 'package.json'.`);
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
const { platform, arch } = process;
|
||||
@@ -70,24 +71,24 @@
|
||||
}
|
||||
})();
|
||||
if (!suffix) {
|
||||
console.log(`The CLI is not available for ${platform} ${arch}.`);
|
||||
process.exit(1);
|
||||
shell.echo(`The CLI is not available for ${platform} ${arch}.`);
|
||||
shell.exit(1);
|
||||
}
|
||||
if (semver.valid(version)) {
|
||||
const url = `https://downloads.arduino.cc/arduino-cli/arduino-cli_${version}_${suffix}`;
|
||||
console.log(
|
||||
shell.echo(
|
||||
`📦 Identified released version of the CLI. Downloading version ${version} from '${url}'`
|
||||
);
|
||||
await downloader.downloadUnzipFile(url, destinationPath, 'arduino-cli');
|
||||
} else if (moment(version, 'YYYYMMDD', true).isValid()) {
|
||||
const url = `https://downloads.arduino.cc/arduino-cli/nightly/arduino-cli_nightly-${version}_${suffix}`;
|
||||
console.log(
|
||||
shell.echo(
|
||||
`🌙 Identified nightly version of the CLI. Downloading version ${version} from '${url}'`
|
||||
);
|
||||
await downloader.downloadUnzipFile(url, destinationPath, 'arduino-cli');
|
||||
} else {
|
||||
console.log(`🔥 Could not interpret 'version': ${version}`);
|
||||
process.exit(1);
|
||||
shell.echo(`🔥 Could not interpret 'version': ${version}`);
|
||||
shell.exit(1);
|
||||
}
|
||||
} else {
|
||||
taskBuildFromGit(version, destinationPath, 'CLI');
|
||||
|
@@ -1,18 +1,14 @@
|
||||
// @ts-check
|
||||
|
||||
// The version to use.
|
||||
const version = '1.10.1';
|
||||
const version = '1.10.0';
|
||||
|
||||
(async () => {
|
||||
const os = require('node:os');
|
||||
const {
|
||||
existsSync,
|
||||
promises: fs,
|
||||
mkdirSync,
|
||||
readdirSync,
|
||||
cpSync,
|
||||
} = require('node:fs');
|
||||
const { existsSync, promises: fs } = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const shell = require('shelljs');
|
||||
const { v4 } = require('uuid');
|
||||
const { exec } = require('./utils');
|
||||
|
||||
const destination = path.join(
|
||||
@@ -24,38 +20,31 @@ const version = '1.10.1';
|
||||
'Examples'
|
||||
);
|
||||
if (existsSync(destination)) {
|
||||
console.log(
|
||||
shell.echo(
|
||||
`Skipping Git checkout of the examples because the repository already exists: ${destination}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const repository = await fs.mkdtemp(
|
||||
path.join(os.tmpdir(), 'arduino-examples-')
|
||||
);
|
||||
const repository = path.join(os.tmpdir(), `${v4()}-arduino-examples`);
|
||||
if (shell.mkdir('-p', repository).code !== 0) {
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
exec(
|
||||
'git',
|
||||
['clone', 'https://github.com/arduino/arduino-examples.git', repository],
|
||||
{ logStdout: true }
|
||||
shell
|
||||
);
|
||||
|
||||
exec(
|
||||
'git',
|
||||
['-C', repository, 'checkout', `tags/${version}`, '-b', version],
|
||||
{ logStdout: true }
|
||||
shell
|
||||
);
|
||||
|
||||
mkdirSync(destination, { recursive: true });
|
||||
const examplesPath = path.join(repository, 'examples');
|
||||
const exampleResources = readdirSync(examplesPath);
|
||||
for (const exampleResource of exampleResources) {
|
||||
cpSync(
|
||||
path.join(examplesPath, exampleResource),
|
||||
path.join(destination, exampleResource),
|
||||
{ recursive: true }
|
||||
);
|
||||
}
|
||||
shell.mkdir('-p', destination);
|
||||
shell.cp('-fR', path.join(repository, 'examples', '*'), destination);
|
||||
|
||||
const isSketch = async (pathLike) => {
|
||||
try {
|
||||
@@ -115,5 +104,5 @@ const version = '1.10.1';
|
||||
JSON.stringify(examples, null, 2),
|
||||
{ encoding: 'utf8' }
|
||||
);
|
||||
console.log(`Generated output to ${path.join(destination, 'examples.json')}`);
|
||||
shell.echo(`Generated output to ${path.join(destination, 'examples.json')}`);
|
||||
})();
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
(async () => {
|
||||
const path = require('node:path');
|
||||
const shell = require('shelljs');
|
||||
const semver = require('semver');
|
||||
const downloader = require('./downloader');
|
||||
const { taskBuildFromGit } = require('./utils');
|
||||
@@ -27,10 +28,10 @@
|
||||
})();
|
||||
|
||||
if (!version) {
|
||||
console.log(
|
||||
shell.echo(
|
||||
`Could not retrieve Firmware Uploader version info from the 'package.json'.`
|
||||
);
|
||||
process.exit(1);
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
const { platform, arch } = process;
|
||||
@@ -50,14 +51,7 @@
|
||||
const suffix = (() => {
|
||||
switch (platform) {
|
||||
case 'darwin':
|
||||
switch (arch) {
|
||||
case 'arm64':
|
||||
return 'macOS_ARM64.tar.gz';
|
||||
case 'x64':
|
||||
return 'macOS_64bit.tar.gz';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
return 'macOS_64bit.tar.gz';
|
||||
case 'win32':
|
||||
return 'Windows_64bit.zip';
|
||||
case 'linux': {
|
||||
@@ -77,14 +71,14 @@
|
||||
}
|
||||
})();
|
||||
if (!suffix) {
|
||||
console.log(
|
||||
shell.echo(
|
||||
`The Firmware Uploader is not available for ${platform} ${arch}.`
|
||||
);
|
||||
process.exit(1);
|
||||
shell.exit(1);
|
||||
}
|
||||
if (semver.valid(version)) {
|
||||
const url = `https://downloads.arduino.cc/arduino-fwuploader/arduino-fwuploader_${version}_${suffix}`;
|
||||
console.log(
|
||||
shell.echo(
|
||||
`📦 Identified released version of the Firmware Uploader. Downloading version ${version} from '${url}'`
|
||||
);
|
||||
await downloader.downloadUnzipFile(
|
||||
@@ -93,8 +87,8 @@
|
||||
'arduino-fwuploader'
|
||||
);
|
||||
} else {
|
||||
console.log(`🔥 Could not interpret 'version': ${version}`);
|
||||
process.exit(1);
|
||||
shell.echo(`🔥 Could not interpret 'version': ${version}`);
|
||||
shell.exit(1);
|
||||
}
|
||||
} else {
|
||||
taskBuildFromGit(version, destinationPath, 'Firmware Uploader');
|
||||
|
@@ -5,6 +5,7 @@
|
||||
|
||||
(() => {
|
||||
const path = require('path');
|
||||
const shell = require('shelljs');
|
||||
const downloader = require('./downloader');
|
||||
const { goBuildFromGit } = require('./utils');
|
||||
|
||||
@@ -24,20 +25,20 @@
|
||||
})();
|
||||
|
||||
if (!DEFAULT_LS_VERSION) {
|
||||
console.log(
|
||||
shell.echo(
|
||||
`Could not retrieve Arduino Language Server version info from the 'package.json'.`
|
||||
);
|
||||
process.exit(1);
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
if (!DEFAULT_CLANGD_VERSION) {
|
||||
console.log(
|
||||
shell.echo(
|
||||
`Could not retrieve clangd version info from the 'package.json'.`
|
||||
);
|
||||
process.exit(1);
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
const yargs = require('@theia/core/shared/yargs')
|
||||
const yargs = require('yargs')
|
||||
.option('ls-version', {
|
||||
alias: 'lv',
|
||||
default: DEFAULT_LS_VERSION,
|
||||
@@ -113,10 +114,10 @@
|
||||
throw new Error(`Unsupported platform/arch: ${platformArch}.`);
|
||||
}
|
||||
if (!lsSuffix || !clangdSuffix) {
|
||||
console.log(
|
||||
shell.echo(
|
||||
`The arduino-language-server is not available for ${platform} ${arch}.`
|
||||
);
|
||||
process.exit(1);
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
if (typeof lsVersion === 'string') {
|
||||
|
@@ -1,19 +1,20 @@
|
||||
// @ts-check
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const shell = require('shelljs');
|
||||
const decompress = require('decompress');
|
||||
const unzip = require('decompress-unzip');
|
||||
const untargz = require('decompress-targz');
|
||||
const untarbz2 = require('decompress-tarbz2');
|
||||
|
||||
process.on('unhandledRejection', (reason) => {
|
||||
console.log(String(reason));
|
||||
process.exit(1);
|
||||
process.on('unhandledRejection', (reason, _) => {
|
||||
shell.echo(String(reason));
|
||||
shell.exit(1);
|
||||
throw reason;
|
||||
});
|
||||
process.on('uncaughtException', (error) => {
|
||||
console.log(String(error));
|
||||
process.exit(1);
|
||||
shell.echo(String(error));
|
||||
shell.exit(1);
|
||||
throw error;
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -29,42 +30,55 @@ exports.downloadUnzipFile = async (
|
||||
force = false
|
||||
) => {
|
||||
if (fs.existsSync(targetFile) && !force) {
|
||||
console.log(`Skipping download because file already exists: ${targetFile}`);
|
||||
shell.echo(`Skipping download because file already exists: ${targetFile}`);
|
||||
return;
|
||||
}
|
||||
fs.mkdirSync(path.dirname(targetFile), { recursive: true });
|
||||
if (!fs.existsSync(path.dirname(targetFile))) {
|
||||
if (shell.mkdir('-p', path.dirname(targetFile)).code !== 0) {
|
||||
shell.echo('Could not create new directory.');
|
||||
shell.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const downloads = path.join(__dirname, '..', 'downloads');
|
||||
fs.rmSync(targetFile, { recursive: true, force: true });
|
||||
fs.rmSync(downloads, { recursive: true, force: true });
|
||||
if (shell.rm('-rf', targetFile, downloads).code !== 0) {
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
console.log(`>>> Downloading from '${url}'...`);
|
||||
shell.echo(`>>> Downloading from '${url}'...`);
|
||||
const { default: download } = await import('@xhmikosr/downloader');
|
||||
const data = await download(url);
|
||||
console.log(`<<< Download succeeded.`);
|
||||
shell.echo(`<<< Download succeeded.`);
|
||||
|
||||
console.log('>>> Decompressing...');
|
||||
shell.echo('>>> Decompressing...');
|
||||
const files = await decompress(data, downloads, {
|
||||
plugins: [unzip(), untargz(), untarbz2()],
|
||||
});
|
||||
if (files.length === 0) {
|
||||
console.log('Error ocurred while decompressing the archive.');
|
||||
process.exit(1);
|
||||
shell.echo('Error ocurred while decompressing the archive.');
|
||||
shell.exit(1);
|
||||
}
|
||||
const fileIndex = files.findIndex((f) => f.path.startsWith(filePrefix));
|
||||
if (fileIndex === -1) {
|
||||
console.log(
|
||||
shell.echo(
|
||||
`The downloaded artifact does not contain any file with prefix ${filePrefix}.`
|
||||
);
|
||||
process.exit(1);
|
||||
shell.exit(1);
|
||||
}
|
||||
console.log('<<< Decompressing succeeded.');
|
||||
shell.echo('<<< Decompressing succeeded.');
|
||||
|
||||
fs.renameSync(path.join(downloads, files[fileIndex].path), targetFile);
|
||||
if (!fs.existsSync(targetFile)) {
|
||||
console.log(`Could not find file: ${targetFile}`);
|
||||
process.exit(1);
|
||||
if (
|
||||
shell.mv('-f', path.join(downloads, files[fileIndex].path), targetFile)
|
||||
.code !== 0
|
||||
) {
|
||||
shell.echo(`Could not move file to target path: ${targetFile}`);
|
||||
shell.exit(1);
|
||||
}
|
||||
console.log(`Done: ${targetFile}`);
|
||||
if (!fs.existsSync(targetFile)) {
|
||||
shell.echo(`Could not find file: ${targetFile}`);
|
||||
shell.exit(1);
|
||||
}
|
||||
shell.echo(`Done: ${targetFile}`);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -72,7 +86,7 @@ exports.downloadUnzipFile = async (
|
||||
* @param targetDir {string} Directory into which to decompress the archive
|
||||
* @param targetFile {string} Path to the main file expected after decompressing
|
||||
* @param force {boolean} Whether to download even if the target file exists
|
||||
* @param decompressOptions {import('decompress').DecompressOptions|undefined} [decompressOptions]
|
||||
* @param decompressOptions {import('decompress').DecompressOptions}
|
||||
*/
|
||||
exports.downloadUnzipAll = async (
|
||||
url,
|
||||
@@ -82,16 +96,22 @@ exports.downloadUnzipAll = async (
|
||||
decompressOptions = undefined
|
||||
) => {
|
||||
if (fs.existsSync(targetFile) && !force) {
|
||||
console.log(`Skipping download because file already exists: ${targetFile}`);
|
||||
shell.echo(`Skipping download because file already exists: ${targetFile}`);
|
||||
return;
|
||||
}
|
||||
fs.mkdirSync(targetDir, { recursive: true });
|
||||
if (!fs.existsSync(targetDir)) {
|
||||
if (shell.mkdir('-p', targetDir).code !== 0) {
|
||||
shell.echo('Could not create new directory.');
|
||||
shell.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`>>> Downloading from '${url}'...`);
|
||||
shell.echo(`>>> Downloading from '${url}'...`);
|
||||
const { default: download } = await import('@xhmikosr/downloader');
|
||||
const data = await download(url);
|
||||
console.log(`<<< Download succeeded.`);
|
||||
shell.echo(`<<< Download succeeded.`);
|
||||
|
||||
console.log('>>> Decompressing...');
|
||||
shell.echo('>>> Decompressing...');
|
||||
let options = {
|
||||
plugins: [unzip(), untargz(), untarbz2()],
|
||||
};
|
||||
@@ -100,27 +120,14 @@ exports.downloadUnzipAll = async (
|
||||
}
|
||||
const files = await decompress(data, targetDir, options);
|
||||
if (files.length === 0) {
|
||||
console.log('Error ocurred while decompressing the archive.');
|
||||
process.exit(1);
|
||||
shell.echo('Error ocurred while decompressing the archive.');
|
||||
shell.exit(1);
|
||||
}
|
||||
console.log('<<< Decompressing succeeded.');
|
||||
shell.echo('<<< Decompressing succeeded.');
|
||||
|
||||
if (!fs.existsSync(targetFile)) {
|
||||
console.log(`Could not find file: ${targetFile}`);
|
||||
process.exit(1);
|
||||
shell.echo(`Could not find file: ${targetFile}`);
|
||||
shell.exit(1);
|
||||
}
|
||||
console.log(`Done: ${targetFile}`);
|
||||
shell.echo(`Done: ${targetFile}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @returns {Promise<import('node:buffer').Buffer>}
|
||||
*/
|
||||
async function download(url) {
|
||||
const { default: download } = await import('@xhmikosr/downloader');
|
||||
/** @type {import('node:buffer').Buffer} */
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const data = await download(url);
|
||||
return data;
|
||||
}
|
||||
|
@@ -3,20 +3,22 @@
|
||||
(async () => {
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const { mkdirSync, promises: fs, rmSync } = require('node:fs');
|
||||
const { exec } = require('./utils');
|
||||
const glob = require('glob');
|
||||
const { SemVer, gte, valid: validSemVer } = require('semver');
|
||||
// Use a node-protoc fork until apple arm32 is supported
|
||||
// https://github.com/YePpHa/node-protoc/pull/10
|
||||
const protoc = path.dirname(require('@pingghost/protoc/protoc'));
|
||||
const repository = await fs.mkdtemp(path.join(os.tmpdir(), 'arduino-cli-'));
|
||||
const { v4 } = require('uuid');
|
||||
const shell = require('shelljs');
|
||||
const protoc = path.dirname(require('protoc/protoc'));
|
||||
|
||||
const repository = path.join(os.tmpdir(), `${v4()}-arduino-cli`);
|
||||
if (shell.mkdir('-p', repository).code !== 0) {
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
const { owner, repo, commitish } = (() => {
|
||||
const pkg = require(path.join(__dirname, '..', 'package.json'));
|
||||
if (!pkg) {
|
||||
console.log(`Could not parse the 'package.json'.`);
|
||||
process.exit(1);
|
||||
shell.echo(`Could not parse the 'package.json'.`);
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
const defaultVersion = {
|
||||
@@ -46,21 +48,21 @@
|
||||
// We assume an object with `owner`, `repo`, commitish?` properties.
|
||||
const { owner, repo, commitish } = version;
|
||||
if (!owner) {
|
||||
console.log(`Could not retrieve 'owner' from ${JSON.stringify(version)}`);
|
||||
process.exit(1);
|
||||
shell.echo(`Could not retrieve 'owner' from ${JSON.stringify(version)}`);
|
||||
shell.exit(1);
|
||||
}
|
||||
if (!repo) {
|
||||
console.log(`Could not retrieve 'repo' from ${JSON.stringify(version)}`);
|
||||
process.exit(1);
|
||||
shell.echo(`Could not retrieve 'repo' from ${JSON.stringify(version)}`);
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
return { owner, repo, commitish };
|
||||
})();
|
||||
|
||||
const url = `https://github.com/${owner}/${repo}.git`;
|
||||
console.log(`>>> Cloning repository from '${url}'...`);
|
||||
exec('git', ['clone', url, repository], { logStdout: true });
|
||||
console.log(`<<< Repository cloned.`);
|
||||
shell.echo(`>>> Cloning repository from '${url}'...`);
|
||||
exec('git', ['clone', url, repository], shell);
|
||||
shell.echo(`<<< Repository cloned.`);
|
||||
|
||||
const { platform } = process;
|
||||
const resourcesFolder = path.join(
|
||||
@@ -74,12 +76,10 @@
|
||||
resourcesFolder,
|
||||
`arduino-cli${platform === 'win32' ? '.exe' : ''}`
|
||||
);
|
||||
const versionJson = exec(cli, ['version', '--format', 'json'], {
|
||||
logStdout: true,
|
||||
}).trim();
|
||||
const versionJson = exec(cli, ['version', '--format', 'json'], shell).trim();
|
||||
if (!versionJson) {
|
||||
console.log(`Could not retrieve the CLI version from ${cli}.`);
|
||||
process.exit(1);
|
||||
shell.echo(`Could not retrieve the CLI version from ${cli}.`);
|
||||
shell.exit(1);
|
||||
}
|
||||
// As of today (28.01.2021), the `VersionString` can be one of the followings:
|
||||
// - `nightly-YYYYMMDD` stands for the nightly build, we use the , the `commitish` from the `package.json` to check out the code.
|
||||
@@ -96,61 +96,52 @@
|
||||
}
|
||||
*/
|
||||
const versionObject = JSON.parse(versionJson);
|
||||
let version = versionObject.VersionString;
|
||||
if (validSemVer(version)) {
|
||||
// https://github.com/arduino/arduino-cli/pull/2374
|
||||
if (gte(new SemVer(version, { loose: true }), new SemVer('0.35.0-rc.1'))) {
|
||||
version = `v${version}`;
|
||||
}
|
||||
console.log(`>>> Checking out tagged version: '${version}'...`);
|
||||
exec('git', ['-C', repository, 'fetch', '--all', '--tags'], {
|
||||
logStdout: true,
|
||||
});
|
||||
const version = versionObject.VersionString;
|
||||
if (
|
||||
version &&
|
||||
!version.startsWith('nightly-') &&
|
||||
version !== '0.0.0-git' &&
|
||||
version !== 'git-snapshot'
|
||||
) {
|
||||
shell.echo(`>>> Checking out tagged version: '${version}'...`);
|
||||
exec('git', ['-C', repository, 'fetch', '--all', '--tags'], shell);
|
||||
exec(
|
||||
'git',
|
||||
['-C', repository, 'checkout', `tags/${version}`, '-b', version],
|
||||
{ logStdout: true }
|
||||
shell
|
||||
);
|
||||
console.log(`<<< Checked out tagged version: '${version}'.`);
|
||||
shell.echo(`<<< Checked out tagged version: '${version}'.`);
|
||||
} else if (commitish) {
|
||||
console.log(
|
||||
shell.echo(
|
||||
`>>> Checking out commitish from 'package.json': '${commitish}'...`
|
||||
);
|
||||
exec('git', ['-C', repository, 'checkout', commitish], { logStdout: true });
|
||||
console.log(
|
||||
exec('git', ['-C', repository, 'checkout', commitish], shell);
|
||||
shell.echo(
|
||||
`<<< Checked out commitish from 'package.json': '${commitish}'.`
|
||||
);
|
||||
} else if (versionObject.Commit) {
|
||||
console.log(
|
||||
shell.echo(
|
||||
`>>> Checking out commitish from the CLI: '${versionObject.Commit}'...`
|
||||
);
|
||||
exec('git', ['-C', repository, 'checkout', versionObject.Commit], {
|
||||
logStdout: true,
|
||||
});
|
||||
console.log(
|
||||
exec('git', ['-C', repository, 'checkout', versionObject.Commit], shell);
|
||||
shell.echo(
|
||||
`<<< Checked out commitish from the CLI: '${versionObject.Commit}'.`
|
||||
);
|
||||
} else {
|
||||
console.log(`WARN: no 'git checkout'. Generating from the HEAD revision.`);
|
||||
shell.echo(`WARN: no 'git checkout'. Generating from the HEAD revision.`);
|
||||
}
|
||||
|
||||
console.log('>>> Generating TS/JS API from:');
|
||||
exec('git', ['-C', repository, 'rev-parse', '--abbrev-ref', 'HEAD'], {
|
||||
logStdout: true,
|
||||
});
|
||||
shell.echo('>>> Generating TS/JS API from:');
|
||||
exec('git', ['-C', repository, 'rev-parse', '--abbrev-ref', 'HEAD'], shell);
|
||||
|
||||
const rpc = path.join(repository, 'rpc');
|
||||
const out = path.join(__dirname, '..', 'src', 'node', 'cli-protocol');
|
||||
// Must wipe the gen output folder. Otherwise, dangling service implementation remain in IDE2 code,
|
||||
// although it has been removed from the proto file.
|
||||
// For example, https://github.com/arduino/arduino-cli/commit/50a8bf5c3e61d5b661ccfcd6a055e82eeb510859.
|
||||
rmSync(out, { recursive: true, maxRetries: 5, force: true });
|
||||
mkdirSync(out, { recursive: true });
|
||||
shell.mkdir('-p', out);
|
||||
|
||||
const protos = await new Promise((resolve) =>
|
||||
glob('**/*.proto', { cwd: rpc }, (error, matches) => {
|
||||
if (error) {
|
||||
console.log(error.stack ?? error.message);
|
||||
shell.echo(error.stack ?? error.message);
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
@@ -158,11 +149,12 @@
|
||||
})
|
||||
);
|
||||
if (!protos || protos.length === 0) {
|
||||
console.log(`Could not find any .proto files under ${rpc}.`);
|
||||
process.exit(1);
|
||||
shell.echo(`Could not find any .proto files under ${rpc}.`);
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
// Generate JS code from the `.proto` files.
|
||||
|
||||
exec(
|
||||
'grpc_tools_node_protoc',
|
||||
[
|
||||
@@ -172,7 +164,7 @@
|
||||
rpc,
|
||||
...protos,
|
||||
],
|
||||
{ logStdout: true }
|
||||
shell
|
||||
);
|
||||
|
||||
// Generate the `.d.ts` files for JS.
|
||||
@@ -191,8 +183,8 @@
|
||||
rpc,
|
||||
...protos,
|
||||
],
|
||||
{ logStdout: true }
|
||||
shell
|
||||
);
|
||||
|
||||
console.log('<<< Generation was successful.');
|
||||
shell.echo('<<< Generation was successful.');
|
||||
})();
|
||||
|
@@ -3,21 +3,24 @@
|
||||
const exec = (
|
||||
/** @type {string} */ command,
|
||||
/** @type {readonly string[]} */ args,
|
||||
/** @type {Partial<import('node:child_process').ExecFileSyncOptionsWithStringEncoding> & { logStdout?: boolean }|undefined} */ options = undefined
|
||||
/** @type {import('shelljs')|undefined}*/ shell = undefined,
|
||||
/** @type {import('node:child_process').ExecFileSyncOptionsWithStringEncoding|undefined} */ options = undefined
|
||||
) => {
|
||||
try {
|
||||
const stdout = require('node:child_process').execFileSync(command, args, {
|
||||
encoding: 'utf8',
|
||||
...(options ?? {}),
|
||||
});
|
||||
if (options?.logStdout) {
|
||||
console.log(stdout.trim());
|
||||
const stdout = require('node:child_process').execFileSync(
|
||||
command,
|
||||
args,
|
||||
options ? options : { encoding: 'utf8' }
|
||||
);
|
||||
if (shell) {
|
||||
shell.echo(stdout.trim());
|
||||
}
|
||||
return stdout;
|
||||
} catch (err) {
|
||||
console.log(
|
||||
`Failed to execute ${command} with args: ${JSON.stringify(args)}`
|
||||
);
|
||||
if (shell) {
|
||||
shell.echo(err instanceof Error ? err.message : String(err));
|
||||
shell.exit(1);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
@@ -56,31 +59,32 @@ function buildFromGit(command, version, destinationPath, taskName) {
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const temp = require('temp');
|
||||
const shell = require('shelljs');
|
||||
|
||||
// We assume an object with `owner`, `repo`, commitish?` properties.
|
||||
if (typeof version !== 'object') {
|
||||
console.log(
|
||||
shell.echo(
|
||||
`Expected a \`{ owner, repo, commitish }\` object. Got <${version}> instead.`
|
||||
);
|
||||
}
|
||||
const { owner, repo, commitish } = version;
|
||||
if (!owner) {
|
||||
console.log(`Could not retrieve 'owner' from ${JSON.stringify(version)}`);
|
||||
process.exit(1);
|
||||
shell.echo(`Could not retrieve 'owner' from ${JSON.stringify(version)}`);
|
||||
shell.exit(1);
|
||||
}
|
||||
if (!repo) {
|
||||
console.log(`Could not retrieve 'repo' from ${JSON.stringify(version)}`);
|
||||
process.exit(1);
|
||||
shell.echo(`Could not retrieve 'repo' from ${JSON.stringify(version)}`);
|
||||
shell.exit(1);
|
||||
}
|
||||
const url = `https://github.com/${owner}/${repo}.git`;
|
||||
console.log(
|
||||
shell.echo(
|
||||
`Building ${taskName} from ${url}. Commitish: ${
|
||||
commitish ? commitish : 'HEAD'
|
||||
}`
|
||||
);
|
||||
|
||||
if (fs.existsSync(destinationPath)) {
|
||||
console.log(
|
||||
shell.echo(
|
||||
`Skipping the ${taskName} build because it already exists: ${destinationPath}`
|
||||
);
|
||||
return;
|
||||
@@ -93,51 +97,48 @@ function buildFromGit(command, version, destinationPath, taskName) {
|
||||
'node',
|
||||
'resources'
|
||||
);
|
||||
fs.mkdirSync(resourcesFolder, { recursive: true });
|
||||
|
||||
const tempRepoPath = temp.mkdirSync();
|
||||
console.log(`>>> Cloning ${taskName} source to ${tempRepoPath}...`);
|
||||
exec('git', ['clone', url, tempRepoPath], { logStdout: true });
|
||||
console.log(`<<< Cloned ${taskName} repo.`);
|
||||
|
||||
if (commitish) {
|
||||
console.log(`>>> Checking out ${commitish}...`);
|
||||
exec('git', ['-C', tempRepoPath, 'checkout', commitish], {
|
||||
logStdout: true,
|
||||
});
|
||||
console.log(`<<< Checked out ${commitish}.`);
|
||||
if (shell.mkdir('-p', resourcesFolder).code !== 0) {
|
||||
shell.echo('Could not create resources folder.');
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
exec('git', ['-C', tempRepoPath, 'rev-parse', '--short', 'HEAD'], {
|
||||
logStdout: true,
|
||||
});
|
||||
const tempRepoPath = temp.mkdirSync();
|
||||
shell.echo(`>>> Cloning ${taskName} source to ${tempRepoPath}...`);
|
||||
exec('git', ['clone', url, tempRepoPath], shell);
|
||||
shell.echo(`<<< Cloned ${taskName} repo.`);
|
||||
|
||||
console.log(`>>> Building the ${taskName}...`);
|
||||
exec(command, ['build'], {
|
||||
cwd: tempRepoPath,
|
||||
encoding: 'utf8',
|
||||
logStdout: true,
|
||||
});
|
||||
console.log(`<<< Done ${taskName} build.`);
|
||||
if (commitish) {
|
||||
shell.echo(`>>> Checking out ${commitish}...`);
|
||||
exec('git', ['-C', tempRepoPath, 'checkout', commitish], shell);
|
||||
shell.echo(`<<< Checked out ${commitish}.`);
|
||||
}
|
||||
|
||||
exec('git', ['-C', tempRepoPath, 'rev-parse', '--short', 'HEAD'], shell);
|
||||
|
||||
shell.echo(`>>> Building the ${taskName}...`);
|
||||
exec(command, ['build'], shell, { cwd: tempRepoPath, encoding: 'utf8' });
|
||||
shell.echo(`<<< Done ${taskName} build.`);
|
||||
|
||||
const binName = path.basename(destinationPath);
|
||||
if (!fs.existsSync(path.join(tempRepoPath, binName))) {
|
||||
console.log(
|
||||
shell.echo(
|
||||
`Could not find the ${taskName} at ${path.join(tempRepoPath, binName)}.`
|
||||
);
|
||||
process.exit(1);
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
const binPath = path.join(tempRepoPath, binName);
|
||||
console.log(
|
||||
shell.echo(
|
||||
`>>> Copying ${taskName} from ${binPath} to ${destinationPath}...`
|
||||
);
|
||||
fs.copyFileSync(binPath, destinationPath);
|
||||
console.log(`<<< Copied the ${taskName}.`);
|
||||
|
||||
console.log(`<<< Verifying ${taskName}...`);
|
||||
if (!fs.existsSync(destinationPath)) {
|
||||
process.exit(1);
|
||||
if (shell.cp(binPath, destinationPath).code !== 0) {
|
||||
shell.exit(1);
|
||||
}
|
||||
console.log(`>>> Verified ${taskName}.`);
|
||||
shell.echo(`<<< Copied the ${taskName}.`);
|
||||
|
||||
shell.echo(`<<< Verifying ${taskName}...`);
|
||||
if (!fs.existsSync(destinationPath)) {
|
||||
shell.exit(1);
|
||||
}
|
||||
shell.echo(`>>> Verified ${taskName}.`);
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import '../../src/browser/style/index.css';
|
||||
import { Container, ContainerModule } from '@theia/core/shared/inversify';
|
||||
import { ContainerModule } from '@theia/core/shared/inversify';
|
||||
import { WidgetFactory } from '@theia/core/lib/browser/widget-manager';
|
||||
import { CommandContribution } from '@theia/core/lib/common/command';
|
||||
import { bindViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
|
||||
@@ -180,7 +180,7 @@ import { TabBarRenderer } from './theia/core/tab-bars';
|
||||
import { EditorCommandContribution } from './theia/editor/editor-command';
|
||||
import { NavigatorTabBarDecorator as TheiaNavigatorTabBarDecorator } from '@theia/navigator/lib/browser/navigator-tab-bar-decorator';
|
||||
import { NavigatorTabBarDecorator } from './theia/navigator/navigator-tab-bar-decorator';
|
||||
import { Debug, DebugDisabledStatusMessageSource } from './contributions/debug';
|
||||
import { Debug } from './contributions/debug';
|
||||
import { Sketchbook } from './contributions/sketchbook';
|
||||
import { DebugFrontendApplicationContribution } from './theia/debug/debug-frontend-application-contribution';
|
||||
import { DebugFrontendApplicationContribution as TheiaDebugFrontendApplicationContribution } from '@theia/debug/lib/browser/debug-frontend-application-contribution';
|
||||
@@ -271,8 +271,8 @@ import { MonitorModel } from './monitor-model';
|
||||
import { MonitorManagerProxyClientImpl } from './monitor-manager-proxy-client-impl';
|
||||
import { EditorManager as TheiaEditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { EditorManager } from './theia/editor/editor-manager';
|
||||
import { HostedPluginEvents } from './hosted/hosted-plugin-events';
|
||||
import { HostedPluginSupportImpl } from './theia/plugin-ext/hosted-plugin';
|
||||
import { HostedPluginEvents } from './hosted-plugin-events';
|
||||
import { HostedPluginSupport } from './theia/plugin-ext/hosted-plugin';
|
||||
import { HostedPluginSupport as TheiaHostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||
import { Formatter, FormatterPath } from '../common/protocol/formatter';
|
||||
import { Format } from './contributions/format';
|
||||
@@ -358,20 +358,6 @@ import { MonacoEditorMenuContribution as TheiaMonacoEditorMenuContribution } fro
|
||||
import { UpdateArduinoState } from './contributions/update-arduino-state';
|
||||
import { TerminalFrontendContribution } from './theia/terminal/terminal-frontend-contribution';
|
||||
import { TerminalFrontendContribution as TheiaTerminalFrontendContribution } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
|
||||
import { SelectionService } from '@theia/core/lib/common/selection-service';
|
||||
import { CommandService } from '@theia/core/lib/common/command';
|
||||
import { CorePreferences } from '@theia/core/lib/browser/core-preferences';
|
||||
import { AutoSelectProgrammer } from './contributions/auto-select-programmer';
|
||||
import { HostedPluginSupport } from './hosted/hosted-plugin-support';
|
||||
import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
|
||||
import { DebugSessionManager } from './theia/debug/debug-session-manager';
|
||||
import { DebugWidget as TheiaDebugWidget } from '@theia/debug/lib/browser/view/debug-widget';
|
||||
import { DebugWidget } from './theia/debug/debug-widget';
|
||||
import { DebugViewModel } from '@theia/debug/lib/browser/view/debug-view-model';
|
||||
import { DebugSessionWidget } from '@theia/debug/lib/browser/view/debug-session-widget';
|
||||
import { DebugConfigurationWidget } from './theia/debug/debug-configuration-widget';
|
||||
import { DebugConfigurationWidget as TheiaDebugConfigurationWidget } from '@theia/debug/lib/browser/view/debug-configuration-widget';
|
||||
import { DebugToolBar } from '@theia/debug/lib/browser/view/debug-toolbar-widget';
|
||||
|
||||
// Hack to fix copy/cut/paste issue after electron version update in Theia.
|
||||
// https://github.com/eclipse-theia/theia/issues/12487
|
||||
@@ -465,9 +451,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
// To be able to track, and update the menu based on the core settings (aka. board details) of the currently selected board.
|
||||
bind(BoardsDataStore).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(BoardsDataStore);
|
||||
bind(CommandContribution).toService(BoardsDataStore);
|
||||
bind(StartupTaskProvider).toService(BoardsDataStore); // to inherit the boards config options, programmer, etc in a new window
|
||||
|
||||
// Logger for the Arduino daemon
|
||||
bind(ILogger)
|
||||
.toDynamicValue((ctx) => {
|
||||
@@ -767,13 +750,10 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
Contribution.configure(bind, CreateCloudCopy);
|
||||
Contribution.configure(bind, UpdateArduinoState);
|
||||
Contribution.configure(bind, BoardsDataMenuUpdater);
|
||||
Contribution.configure(bind, AutoSelectProgrammer);
|
||||
|
||||
bindContributionProvider(bind, StartupTaskProvider);
|
||||
bind(StartupTaskProvider).toService(BoardsServiceProvider); // to reuse the boards config in another window
|
||||
|
||||
bind(DebugDisabledStatusMessageSource).toService(Debug);
|
||||
|
||||
// Disabled the quick-pick customization from Theia when multiple formatters are available.
|
||||
// Use the default VS Code behavior, and pick the first one. In the IDE2, clang-format has `exclusive` selectors.
|
||||
bind(MonacoFormattingConflictsContribution).toSelf().inSingletonScope();
|
||||
@@ -816,19 +796,10 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
);
|
||||
const iconThemeService =
|
||||
context.container.get<IconThemeService>(IconThemeService);
|
||||
const selectionService =
|
||||
context.container.get<SelectionService>(SelectionService);
|
||||
const commandService =
|
||||
context.container.get<CommandService>(CommandService);
|
||||
const corePreferences =
|
||||
context.container.get<CorePreferences>(CorePreferences);
|
||||
return new TabBarRenderer(
|
||||
contextMenuRenderer,
|
||||
decoratorService,
|
||||
iconThemeService,
|
||||
selectionService,
|
||||
commandService,
|
||||
corePreferences
|
||||
iconThemeService
|
||||
);
|
||||
});
|
||||
|
||||
@@ -871,28 +842,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
// To be able to use a `launch.json` from outside of the workspace.
|
||||
bind(DebugConfigurationManager).toSelf().inSingletonScope();
|
||||
rebind(TheiaDebugConfigurationManager).toService(DebugConfigurationManager);
|
||||
// To update the currently selected debug config <select> option when starting a debug session.
|
||||
bind(DebugSessionManager).toSelf().inSingletonScope();
|
||||
rebind(TheiaDebugSessionManager).toService(DebugSessionManager);
|
||||
// Customized debug widget with its customized config <select> to update it programmatically.
|
||||
bind(WidgetFactory)
|
||||
.toDynamicValue(({ container }) => ({
|
||||
id: TheiaDebugWidget.ID,
|
||||
createWidget: () => {
|
||||
const child = new Container({ defaultScope: 'Singleton' });
|
||||
child.parent = container;
|
||||
child.bind(DebugViewModel).toSelf();
|
||||
child.bind(DebugToolBar).toSelf();
|
||||
child.bind(DebugSessionWidget).toSelf();
|
||||
child.bind(DebugConfigurationWidget).toSelf(); // with the patched select
|
||||
child // use the customized one in the Theia DI
|
||||
.bind(TheiaDebugConfigurationWidget)
|
||||
.toService(DebugConfigurationWidget);
|
||||
child.bind(DebugWidget).toSelf();
|
||||
return child.get(DebugWidget);
|
||||
},
|
||||
}))
|
||||
.inSingletonScope();
|
||||
|
||||
// To avoid duplicate tabs use deepEqual instead of string equal: https://github.com/eclipse-theia/theia/issues/11309
|
||||
bind(WidgetManager).toSelf().inSingletonScope();
|
||||
@@ -1021,9 +970,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
})
|
||||
.inSingletonScope();
|
||||
|
||||
bind(HostedPluginSupportImpl).toSelf().inSingletonScope();
|
||||
bind(HostedPluginSupport).toService(HostedPluginSupportImpl);
|
||||
rebind(TheiaHostedPluginSupport).toService(HostedPluginSupportImpl);
|
||||
bind(HostedPluginSupport).toSelf().inSingletonScope();
|
||||
rebind(TheiaHostedPluginSupport).toService(HostedPluginSupport);
|
||||
bind(HostedPluginEvents).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(HostedPluginEvents);
|
||||
|
||||
|
@@ -54,17 +54,11 @@ export function isMonitorWidgetDockPanel(
|
||||
return arg === 'bottom' || arg === 'right';
|
||||
}
|
||||
|
||||
export const defaultAsyncWorkers = 0 as const;
|
||||
export const minAsyncWorkers = defaultAsyncWorkers;
|
||||
export const maxAsyncWorkers = 8 as const;
|
||||
|
||||
type StrictPreferenceSchemaProperties<T extends object> = {
|
||||
[p in keyof T]: PreferenceSchemaProperty;
|
||||
};
|
||||
type ArduinoPreferenceSchemaProperties =
|
||||
StrictPreferenceSchemaProperties<ArduinoConfiguration> & {
|
||||
'arduino.window.zoomLevel': PreferenceSchemaProperty;
|
||||
};
|
||||
StrictPreferenceSchemaProperties<ArduinoConfiguration> & { 'arduino.window.zoomLevel': PreferenceSchemaProperty };
|
||||
|
||||
const properties: ArduinoPreferenceSchemaProperties = {
|
||||
'arduino.language.log': {
|
||||
@@ -83,16 +77,6 @@ const properties: ArduinoPreferenceSchemaProperties = {
|
||||
),
|
||||
default: false,
|
||||
},
|
||||
'arduino.language.asyncWorkers': {
|
||||
type: 'number',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/language.asyncWorkers',
|
||||
'Number of async workers used by the Arduino Language Server (clangd). Background index also uses this many workers. The minimum value is 0, and the maximum is 8. When it is 0, the language server uses all available cores. The default value is 0.'
|
||||
),
|
||||
minimum: minAsyncWorkers,
|
||||
maximum: maxAsyncWorkers,
|
||||
default: defaultAsyncWorkers,
|
||||
},
|
||||
'arduino.compile.verbose': {
|
||||
type: 'boolean',
|
||||
description: nls.localize(
|
||||
@@ -228,14 +212,6 @@ const properties: ArduinoPreferenceSchemaProperties = {
|
||||
),
|
||||
default: 'https://api2.arduino.cc/create',
|
||||
},
|
||||
'arduino.cloud.sharedSpaceID': {
|
||||
type: 'string',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/cloud.sharedSpaceId',
|
||||
'The ID of the Arduino Cloud shared space to load the sketchbook from. If empty, your private space is selected.'
|
||||
),
|
||||
default: '',
|
||||
},
|
||||
'arduino.auth.clientID': {
|
||||
type: 'string',
|
||||
description: nls.localize(
|
||||
@@ -320,7 +296,6 @@ export const ArduinoConfigSchema: PreferenceSchema = {
|
||||
export interface ArduinoConfiguration {
|
||||
'arduino.language.log': boolean;
|
||||
'arduino.language.realTimeDiagnostics': boolean;
|
||||
'arduino.language.asyncWorkers': number;
|
||||
'arduino.compile.verbose': boolean;
|
||||
'arduino.compile.experimental': boolean;
|
||||
'arduino.compile.revealRange': ErrorRevealStrategy;
|
||||
@@ -337,7 +312,6 @@ export interface ArduinoConfiguration {
|
||||
'arduino.cloud.push.warn': boolean;
|
||||
'arduino.cloud.pushpublic.warn': boolean;
|
||||
'arduino.cloud.sketchSyncEndpoint': string;
|
||||
'arduino.cloud.sharedSpaceID': string;
|
||||
'arduino.auth.clientID': string;
|
||||
'arduino.auth.domain': string;
|
||||
'arduino.auth.audience': string;
|
||||
|
@@ -48,17 +48,16 @@ namespace BoardsConfigComponent {
|
||||
}
|
||||
}
|
||||
|
||||
class Item<T> extends React.Component<{
|
||||
export abstract class Item<T> extends React.Component<{
|
||||
item: T;
|
||||
label: string;
|
||||
selected: boolean;
|
||||
onClick: (item: T) => void;
|
||||
missing?: boolean;
|
||||
details?: string;
|
||||
title?: string | ((item: T) => string);
|
||||
}> {
|
||||
override render(): React.ReactNode {
|
||||
const { selected, label, missing, details, item } = this.props;
|
||||
const { selected, label, missing, details } = this.props;
|
||||
const classNames = ['item'];
|
||||
if (selected) {
|
||||
classNames.push('selected');
|
||||
@@ -66,15 +65,11 @@ class Item<T> extends React.Component<{
|
||||
if (missing === true) {
|
||||
classNames.push('missing');
|
||||
}
|
||||
let title = this.props.title ?? `${label}${!details ? '' : details}`;
|
||||
if (typeof title === 'function') {
|
||||
title = title(item);
|
||||
}
|
||||
return (
|
||||
<div
|
||||
onClick={this.onClick}
|
||||
className={classNames.join(' ')}
|
||||
title={title}
|
||||
title={`${label}${!details ? '' : details}`}
|
||||
>
|
||||
<div className="label">{label}</div>
|
||||
{!details ? '' : <div className="details">{details}</div>}
|
||||
@@ -239,20 +234,9 @@ export class BoardsConfigComponent extends React.Component<
|
||||
distinctBoards.set(key, board);
|
||||
}
|
||||
}
|
||||
const title = (board: Board.Detailed): string => {
|
||||
const { details, manuallyInstalled } = board;
|
||||
let label = board.name;
|
||||
if (details) {
|
||||
label += details;
|
||||
}
|
||||
if (manuallyInstalled) {
|
||||
label += nls.localize('arduino/board/inSketchbook', ' (in Sketchbook)');
|
||||
}
|
||||
return label;
|
||||
};
|
||||
|
||||
const boardsList = Array.from(distinctBoards.values()).map((board) => (
|
||||
<Item<Board.Detailed>
|
||||
<Item<BoardWithPackage>
|
||||
key={toKey(board)}
|
||||
item={board}
|
||||
label={board.name}
|
||||
@@ -260,7 +244,6 @@ export class BoardsConfigComponent extends React.Component<
|
||||
selected={board.selected}
|
||||
onClick={this.selectBoard}
|
||||
missing={board.missing}
|
||||
title={title}
|
||||
/>
|
||||
));
|
||||
|
||||
|
@@ -1,51 +1,21 @@
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import { StorageService } from '@theia/core/lib/browser/storage-service';
|
||||
import type {
|
||||
Command,
|
||||
CommandContribution,
|
||||
CommandRegistry,
|
||||
} from '@theia/core/lib/common/command';
|
||||
import { LocalStorageService } from '@theia/core/lib/browser/storage-service';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { Emitter, Event } from '@theia/core/lib/common/event';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { deepClone, deepFreeze } from '@theia/core/lib/common/objects';
|
||||
import type { Mutable } from '@theia/core/lib/common/types';
|
||||
import { deepClone } from '@theia/core/lib/common/objects';
|
||||
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
||||
import { FQBN } from 'fqbn';
|
||||
import {
|
||||
BoardDetails,
|
||||
BoardsService,
|
||||
ConfigOption,
|
||||
ConfigValue,
|
||||
Programmer,
|
||||
isBoardIdentifierChangeEvent,
|
||||
isProgrammer,
|
||||
sanitizeFqbn,
|
||||
} from '../../common/protocol';
|
||||
import { notEmpty } from '../../common/utils';
|
||||
import type {
|
||||
StartupTask,
|
||||
StartupTaskProvider,
|
||||
} from '../../electron-common/startup-task';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { BoardsServiceProvider } from './boards-service-provider';
|
||||
|
||||
export interface SelectConfigOptionParams {
|
||||
readonly fqbn: string;
|
||||
readonly optionsToUpdate: readonly Readonly<{
|
||||
option: string;
|
||||
selectedValue: string;
|
||||
}>[];
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class BoardsDataStore
|
||||
implements
|
||||
FrontendApplicationContribution,
|
||||
StartupTaskProvider,
|
||||
CommandContribution
|
||||
{
|
||||
export class BoardsDataStore implements FrontendApplicationContribution {
|
||||
@inject(ILogger)
|
||||
@named('store')
|
||||
private readonly logger: ILogger;
|
||||
@@ -53,140 +23,46 @@ export class BoardsDataStore
|
||||
private readonly boardsService: BoardsService;
|
||||
@inject(NotificationCenter)
|
||||
private readonly notificationCenter: NotificationCenter;
|
||||
// When `@theia/workspace` is part of the application, the workspace-scoped storage service is the default implementation, and the `StorageService` symbol must be used for the injection.
|
||||
// https://github.com/eclipse-theia/theia/blob/ba3722b04ff91eb6a4af6a571c9e263c77cdd8b5/packages/workspace/src/browser/workspace-frontend-module.ts#L97-L98
|
||||
// In other words, store the data (such as the board configs) per sketch, not per IDE2 installation. https://github.com/arduino/arduino-ide/issues/2240
|
||||
@inject(StorageService)
|
||||
private readonly storageService: StorageService;
|
||||
@inject(BoardsServiceProvider)
|
||||
private readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
@inject(FrontendApplicationStateService)
|
||||
private readonly appStateService: FrontendApplicationStateService;
|
||||
@inject(LocalStorageService)
|
||||
private readonly storageService: LocalStorageService;
|
||||
|
||||
private readonly onDidChangeEmitter =
|
||||
new Emitter<BoardsDataStoreChangeEvent>();
|
||||
private readonly toDispose = new DisposableCollection(
|
||||
this.onDidChangeEmitter
|
||||
);
|
||||
private _selectedBoardData: BoardsDataStoreChange | undefined;
|
||||
private readonly onChangedEmitter = new Emitter<string[]>();
|
||||
private readonly toDispose = new DisposableCollection(this.onChangedEmitter);
|
||||
|
||||
onStart(): void {
|
||||
this.toDispose.pushAll([
|
||||
this.boardsServiceProvider.onBoardsConfigDidChange((event) => {
|
||||
if (isBoardIdentifierChangeEvent(event)) {
|
||||
this.updateSelectedBoardData(
|
||||
event.selectedBoard?.fqbn,
|
||||
// If the change event comes from toolbar and the FQBN contains custom board options, change the currently selected options
|
||||
// https://github.com/arduino/arduino-ide/issues/1588
|
||||
event.reason === 'toolbar'
|
||||
);
|
||||
}
|
||||
}),
|
||||
this.toDispose.push(
|
||||
this.notificationCenter.onPlatformDidInstall(async ({ item }) => {
|
||||
const boardsWithFqbn = item.boards
|
||||
const dataDidChangePerFqbn: string[] = [];
|
||||
for (const fqbn of item.boards
|
||||
.map(({ fqbn }) => fqbn)
|
||||
.filter(notEmpty);
|
||||
const changes: BoardsDataStoreChange[] = [];
|
||||
for (const fqbn of boardsWithFqbn) {
|
||||
.filter(notEmpty)
|
||||
.filter((fqbn) => !!fqbn)) {
|
||||
const key = this.getStorageKey(fqbn);
|
||||
const storedData =
|
||||
await this.storageService.getData<BoardsDataStore.Data>(key);
|
||||
if (!storedData) {
|
||||
// if no previously value is available for the board, do not update the cache
|
||||
continue;
|
||||
}
|
||||
const details = await this.loadBoardDetails(fqbn);
|
||||
if (details) {
|
||||
const data = createDataStoreEntry(details);
|
||||
await this.storageService.setData(key, data);
|
||||
changes.push({ fqbn, data });
|
||||
let data = await this.storageService.getData<ConfigOption[]>(key);
|
||||
if (!data || !data.length) {
|
||||
const details = await this.getBoardDetailsSafe(fqbn);
|
||||
if (details) {
|
||||
data = details.configOptions;
|
||||
if (data.length) {
|
||||
await this.storageService.setData(key, data);
|
||||
dataDidChangePerFqbn.push(fqbn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changes.length) {
|
||||
this.fireChanged(...changes);
|
||||
if (dataDidChangePerFqbn.length) {
|
||||
this.fireChanged(...dataDidChangePerFqbn);
|
||||
}
|
||||
}),
|
||||
this.onDidChange((event) => {
|
||||
const selectedFqbn =
|
||||
this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn;
|
||||
if (event.changes.find((change) => change.fqbn === selectedFqbn)) {
|
||||
this.updateSelectedBoardData(selectedFqbn);
|
||||
}
|
||||
}),
|
||||
]);
|
||||
|
||||
Promise.all([
|
||||
this.boardsServiceProvider.ready,
|
||||
this.appStateService.reachedState('ready'),
|
||||
]).then(() =>
|
||||
this.updateSelectedBoardData(
|
||||
this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn
|
||||
)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private async getSelectedBoardData(
|
||||
fqbn: string | undefined
|
||||
): Promise<BoardsDataStoreChange | undefined> {
|
||||
if (!fqbn) {
|
||||
return undefined;
|
||||
} else {
|
||||
const data = await this.getData(sanitizeFqbn(fqbn));
|
||||
if (data === BoardsDataStore.Data.EMPTY) {
|
||||
return undefined;
|
||||
}
|
||||
return { fqbn, data };
|
||||
}
|
||||
}
|
||||
|
||||
private async updateSelectedBoardData(
|
||||
fqbn: string | undefined,
|
||||
updateConfigOptions = false
|
||||
): Promise<void> {
|
||||
this._selectedBoardData = await this.getSelectedBoardData(fqbn);
|
||||
if (fqbn && updateConfigOptions) {
|
||||
const { options } = new FQBN(fqbn);
|
||||
if (options) {
|
||||
const optionsToUpdate = Object.entries(options).map(([key, value]) => ({
|
||||
option: key,
|
||||
selectedValue: value,
|
||||
}));
|
||||
const params = { fqbn, optionsToUpdate };
|
||||
await this.selectConfigOption(params);
|
||||
this._selectedBoardData = await this.getSelectedBoardData(fqbn); // reload the updated data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(USE_INHERITED_DATA, {
|
||||
execute: async (arg: unknown) => {
|
||||
if (isBoardsDataStoreChange(arg)) {
|
||||
await this.setData(arg);
|
||||
this.fireChanged(arg);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
tasks(): StartupTask[] {
|
||||
if (!this._selectedBoardData) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
{
|
||||
command: USE_INHERITED_DATA.id,
|
||||
args: [this._selectedBoardData],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
get onDidChange(): Event<BoardsDataStoreChangeEvent> {
|
||||
return this.onDidChangeEmitter.event;
|
||||
get onChanged(): Event<string[]> {
|
||||
return this.onChangedEmitter.event;
|
||||
}
|
||||
|
||||
async appendConfigToFqbn(
|
||||
@@ -196,7 +72,7 @@ export class BoardsDataStore
|
||||
return undefined;
|
||||
}
|
||||
const { configOptions } = await this.getData(fqbn);
|
||||
return new FQBN(fqbn).withConfigOptions(...configOptions).toString();
|
||||
return ConfigOption.decorate(fqbn, configOptions);
|
||||
}
|
||||
|
||||
async getData(fqbn: string | undefined): Promise<BoardsDataStore.Data> {
|
||||
@@ -205,19 +81,22 @@ export class BoardsDataStore
|
||||
}
|
||||
|
||||
const key = this.getStorageKey(fqbn);
|
||||
const storedData = await this.storageService.getData<
|
||||
let data = await this.storageService.getData<
|
||||
BoardsDataStore.Data | undefined
|
||||
>(key, undefined);
|
||||
if (BoardsDataStore.Data.is(storedData)) {
|
||||
return storedData;
|
||||
if (BoardsDataStore.Data.is(data)) {
|
||||
return data;
|
||||
}
|
||||
|
||||
const boardDetails = await this.loadBoardDetails(fqbn);
|
||||
const boardDetails = await this.getBoardDetailsSafe(fqbn);
|
||||
if (!boardDetails) {
|
||||
return BoardsDataStore.Data.EMPTY;
|
||||
}
|
||||
|
||||
const data = createDataStoreEntry(boardDetails);
|
||||
data = {
|
||||
configOptions: boardDetails.configOptions,
|
||||
programmers: boardDetails.programmers,
|
||||
};
|
||||
await this.storageService.setData(key, data);
|
||||
return data;
|
||||
}
|
||||
@@ -229,68 +108,59 @@ export class BoardsDataStore
|
||||
fqbn: string;
|
||||
selectedProgrammer: Programmer;
|
||||
}): Promise<boolean> {
|
||||
const sanitizedFQBN = sanitizeFqbn(fqbn);
|
||||
const storedData = deepClone(await this.getData(sanitizedFQBN));
|
||||
const { programmers } = storedData;
|
||||
const data = deepClone(await this.getData(fqbn));
|
||||
const { programmers } = data;
|
||||
if (!programmers.find((p) => Programmer.equals(selectedProgrammer, p))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const change: BoardsDataStoreChange = {
|
||||
fqbn: sanitizedFQBN,
|
||||
data: { ...storedData, selectedProgrammer },
|
||||
};
|
||||
await this.setData(change);
|
||||
this.fireChanged(change);
|
||||
await this.setData({
|
||||
fqbn,
|
||||
data: { ...data, selectedProgrammer },
|
||||
});
|
||||
this.fireChanged(fqbn);
|
||||
return true;
|
||||
}
|
||||
|
||||
async selectConfigOption(params: SelectConfigOptionParams): Promise<boolean> {
|
||||
const { fqbn, optionsToUpdate } = params;
|
||||
if (!optionsToUpdate.length) {
|
||||
async selectConfigOption({
|
||||
fqbn,
|
||||
option,
|
||||
selectedValue,
|
||||
}: {
|
||||
fqbn: string;
|
||||
option: string;
|
||||
selectedValue: string;
|
||||
}): Promise<boolean> {
|
||||
const data = deepClone(await this.getData(fqbn));
|
||||
const { configOptions } = data;
|
||||
const configOption = configOptions.find((c) => c.option === option);
|
||||
if (!configOption) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const sanitizedFQBN = sanitizeFqbn(fqbn);
|
||||
const mutableData = deepClone(await this.getData(sanitizedFQBN));
|
||||
let didChange = false;
|
||||
|
||||
for (const { option, selectedValue } of optionsToUpdate) {
|
||||
const { configOptions } = mutableData;
|
||||
const configOption = configOptions.find((c) => c.option === option);
|
||||
if (configOption) {
|
||||
const configOptionValueIndex = configOption.values.findIndex(
|
||||
(configOptionValue) => configOptionValue.value === selectedValue
|
||||
);
|
||||
if (configOptionValueIndex >= 0) {
|
||||
// unselect all
|
||||
configOption.values
|
||||
.map((value) => value as Mutable<ConfigValue>)
|
||||
.forEach((value) => (value.selected = false));
|
||||
const mutableConfigValue: Mutable<ConfigValue> =
|
||||
configOption.values[configOptionValueIndex];
|
||||
// make the new value `selected`
|
||||
mutableConfigValue.selected = true;
|
||||
didChange = true;
|
||||
}
|
||||
let updated = false;
|
||||
for (const value of configOption.values) {
|
||||
if (value.value === selectedValue) {
|
||||
(value as any).selected = true;
|
||||
updated = true;
|
||||
} else {
|
||||
(value as any).selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!didChange) {
|
||||
if (!updated) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const change: BoardsDataStoreChange = {
|
||||
fqbn: sanitizedFQBN,
|
||||
data: mutableData,
|
||||
};
|
||||
await this.setData(change);
|
||||
this.fireChanged(change);
|
||||
await this.setData({ fqbn, data });
|
||||
this.fireChanged(fqbn);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected async setData(change: BoardsDataStoreChange): Promise<void> {
|
||||
const { fqbn, data } = change;
|
||||
protected async setData({
|
||||
fqbn,
|
||||
data,
|
||||
}: {
|
||||
fqbn: string;
|
||||
data: BoardsDataStore.Data;
|
||||
}): Promise<void> {
|
||||
const key = this.getStorageKey(fqbn);
|
||||
return this.storageService.setData(key, data);
|
||||
}
|
||||
@@ -299,9 +169,11 @@ export class BoardsDataStore
|
||||
return `.arduinoIDE-configOptions-${fqbn}`;
|
||||
}
|
||||
|
||||
async loadBoardDetails(fqbn: string): Promise<BoardDetails | undefined> {
|
||||
protected async getBoardDetailsSafe(
|
||||
fqbn: string
|
||||
): Promise<BoardDetails | undefined> {
|
||||
try {
|
||||
const details = await this.boardsService.getBoardDetails({ fqbn });
|
||||
const details = this.boardsService.getBoardDetails({ fqbn });
|
||||
return details;
|
||||
} catch (err) {
|
||||
if (
|
||||
@@ -322,8 +194,8 @@ export class BoardsDataStore
|
||||
}
|
||||
}
|
||||
|
||||
protected fireChanged(...changes: BoardsDataStoreChange[]): void {
|
||||
this.onDidChangeEmitter.fire({ changes });
|
||||
protected fireChanged(...fqbn: string[]): void {
|
||||
this.onChangedEmitter.fire(fqbn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,86 +204,20 @@ export namespace BoardsDataStore {
|
||||
readonly configOptions: ConfigOption[];
|
||||
readonly programmers: Programmer[];
|
||||
readonly selectedProgrammer?: Programmer;
|
||||
readonly defaultProgrammerId?: string;
|
||||
}
|
||||
export namespace Data {
|
||||
export const EMPTY: Data = deepFreeze({
|
||||
export const EMPTY: Data = {
|
||||
configOptions: [],
|
||||
programmers: [],
|
||||
});
|
||||
|
||||
export function is(arg: unknown): arg is Data {
|
||||
};
|
||||
export function is(arg: any): arg is Data {
|
||||
return (
|
||||
typeof arg === 'object' &&
|
||||
arg !== null &&
|
||||
Array.isArray((<Data>arg).configOptions) &&
|
||||
Array.isArray((<Data>arg).programmers) &&
|
||||
((<Data>arg).selectedProgrammer === undefined ||
|
||||
isProgrammer((<Data>arg).selectedProgrammer)) &&
|
||||
((<Data>arg).defaultProgrammerId === undefined ||
|
||||
typeof (<Data>arg).defaultProgrammerId === 'string')
|
||||
!!arg &&
|
||||
'configOptions' in arg &&
|
||||
Array.isArray(arg['configOptions']) &&
|
||||
'programmers' in arg &&
|
||||
Array.isArray(arg['programmers'])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isEmptyData(data: BoardsDataStore.Data): boolean {
|
||||
return (
|
||||
Boolean(!data.configOptions.length) &&
|
||||
Boolean(!data.programmers.length) &&
|
||||
Boolean(!data.selectedProgrammer) &&
|
||||
Boolean(!data.defaultProgrammerId)
|
||||
);
|
||||
}
|
||||
|
||||
export function findDefaultProgrammer(
|
||||
programmers: readonly Programmer[],
|
||||
defaultProgrammerId: string | undefined | BoardsDataStore.Data
|
||||
): Programmer | undefined {
|
||||
if (!defaultProgrammerId) {
|
||||
return undefined;
|
||||
}
|
||||
const id =
|
||||
typeof defaultProgrammerId === 'string'
|
||||
? defaultProgrammerId
|
||||
: defaultProgrammerId.defaultProgrammerId;
|
||||
return programmers.find((p) => p.id === id);
|
||||
}
|
||||
function createDataStoreEntry(details: BoardDetails): BoardsDataStore.Data {
|
||||
const configOptions = details.configOptions.slice();
|
||||
const programmers = details.programmers.slice();
|
||||
const { defaultProgrammerId } = details;
|
||||
const selectedProgrammer = findDefaultProgrammer(
|
||||
programmers,
|
||||
defaultProgrammerId
|
||||
);
|
||||
const data = {
|
||||
configOptions,
|
||||
programmers,
|
||||
...(selectedProgrammer ? { selectedProgrammer } : {}),
|
||||
...(defaultProgrammerId ? { defaultProgrammerId } : {}),
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
export interface BoardsDataStoreChange {
|
||||
readonly fqbn: string;
|
||||
readonly data: BoardsDataStore.Data;
|
||||
}
|
||||
|
||||
function isBoardsDataStoreChange(arg: unknown): arg is BoardsDataStoreChange {
|
||||
return (
|
||||
typeof arg === 'object' &&
|
||||
arg !== null &&
|
||||
typeof (<BoardsDataStoreChange>arg).fqbn === 'string' &&
|
||||
BoardsDataStore.Data.is((<BoardsDataStoreChange>arg).data)
|
||||
);
|
||||
}
|
||||
|
||||
export interface BoardsDataStoreChangeEvent {
|
||||
readonly changes: readonly BoardsDataStoreChange[];
|
||||
}
|
||||
|
||||
const USE_INHERITED_DATA: Command = {
|
||||
id: 'arduino-use-inherited-boards-data',
|
||||
};
|
||||
|
@@ -12,7 +12,6 @@ import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { deepClone } from '@theia/core/lib/common/objects';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import type { Mutable } from '@theia/core/lib/common/types';
|
||||
import { inject, injectable, optional } from '@theia/core/shared/inversify';
|
||||
@@ -22,32 +21,31 @@ import {
|
||||
} from '@theia/output/lib/browser/output-channel';
|
||||
import {
|
||||
BoardIdentifier,
|
||||
BoardUserField,
|
||||
BoardWithPackage,
|
||||
boardIdentifierEquals,
|
||||
BoardsConfig,
|
||||
BoardsConfigChangeEvent,
|
||||
BoardsPackage,
|
||||
BoardsService,
|
||||
BoardUserField,
|
||||
BoardWithPackage,
|
||||
DetectedPorts,
|
||||
Port,
|
||||
PortIdentifier,
|
||||
boardIdentifierEquals,
|
||||
emptyBoardsConfig,
|
||||
isBoardIdentifier,
|
||||
isBoardIdentifierChangeEvent,
|
||||
isPortIdentifier,
|
||||
isPortIdentifierChangeEvent,
|
||||
Port,
|
||||
PortIdentifier,
|
||||
portIdentifierEquals,
|
||||
sanitizeFqbn,
|
||||
serializePlatformIdentifier,
|
||||
} from '../../common/protocol';
|
||||
import {
|
||||
BoardList,
|
||||
BoardListHistory,
|
||||
EditBoardsConfigActionParams,
|
||||
SelectBoardsConfigActionParams,
|
||||
createBoardList,
|
||||
EditBoardsConfigActionParams,
|
||||
isBoardListHistory,
|
||||
SelectBoardsConfigActionParams,
|
||||
} from '../../common/protocol/board-list';
|
||||
import type { Defined } from '../../common/types';
|
||||
import type {
|
||||
@@ -106,21 +104,6 @@ type BoardListHistoryUpdateResult =
|
||||
type BoardToSelect = BoardIdentifier | undefined | 'ignore-board';
|
||||
type PortToSelect = PortIdentifier | undefined | 'ignore-port';
|
||||
|
||||
function sanitizeBoardToSelectFQBN(board: BoardToSelect): BoardToSelect {
|
||||
if (isBoardIdentifier(board)) {
|
||||
return sanitizeBoardIdentifierFQBN(board);
|
||||
}
|
||||
return board;
|
||||
}
|
||||
function sanitizeBoardIdentifierFQBN(board: BoardIdentifier): BoardIdentifier {
|
||||
if (board.fqbn) {
|
||||
const copy: Mutable<BoardIdentifier> = deepClone(board);
|
||||
copy.fqbn = sanitizeFqbn(board.fqbn);
|
||||
return copy;
|
||||
}
|
||||
return board;
|
||||
}
|
||||
|
||||
interface UpdateBoardListHistoryParams {
|
||||
readonly portToSelect: PortToSelect;
|
||||
readonly boardToSelect: BoardToSelect;
|
||||
@@ -153,9 +136,6 @@ export interface BoardListUIActions {
|
||||
}
|
||||
export type BoardListUI = BoardList & BoardListUIActions;
|
||||
|
||||
export type BoardsConfigChangeEventUI = BoardsConfigChangeEvent &
|
||||
Readonly<{ reason?: UpdateBoardsConfigReason }>;
|
||||
|
||||
@injectable()
|
||||
export class BoardListDumper implements Disposable {
|
||||
@inject(OutputChannelManager)
|
||||
@@ -210,7 +190,7 @@ export class BoardsServiceProvider
|
||||
private _ready = new Deferred<void>();
|
||||
|
||||
private readonly boardsConfigDidChangeEmitter =
|
||||
new Emitter<BoardsConfigChangeEventUI>();
|
||||
new Emitter<BoardsConfigChangeEvent>();
|
||||
readonly onBoardsConfigDidChange = this.boardsConfigDidChangeEmitter.event;
|
||||
|
||||
private readonly boardListDidChangeEmitter = new Emitter<BoardListUI>();
|
||||
@@ -373,8 +353,7 @@ export class BoardsServiceProvider
|
||||
portToSelect !== 'ignore-port' &&
|
||||
!portIdentifierEquals(portToSelect, previousSelectedPort);
|
||||
const boardDidChangeEvent = boardDidChange
|
||||
? // The change event must always contain any custom board options. Hence the board to select is not sanitized.
|
||||
{ selectedBoard: boardToSelect, previousSelectedBoard }
|
||||
? { selectedBoard: boardToSelect, previousSelectedBoard }
|
||||
: undefined;
|
||||
const portDidChangeEvent = portDidChange
|
||||
? { selectedPort: portToSelect, previousSelectedPort }
|
||||
@@ -395,31 +374,16 @@ export class BoardsServiceProvider
|
||||
return false;
|
||||
}
|
||||
|
||||
// unlike for the board change event, every persistent state must not contain custom board config options in the FQBN
|
||||
const sanitizedBoardToSelect = sanitizeBoardToSelectFQBN(boardToSelect);
|
||||
|
||||
this.maybeUpdateBoardListHistory({
|
||||
portToSelect,
|
||||
boardToSelect: sanitizedBoardToSelect,
|
||||
});
|
||||
this.maybeUpdateBoardsData({
|
||||
boardToSelect: sanitizedBoardToSelect,
|
||||
reason,
|
||||
});
|
||||
this.maybeUpdateBoardListHistory({ portToSelect, boardToSelect });
|
||||
this.maybeUpdateBoardsData({ boardToSelect, reason });
|
||||
|
||||
if (isBoardIdentifierChangeEvent(event)) {
|
||||
this._boardsConfig.selectedBoard = event.selectedBoard
|
||||
? sanitizeBoardIdentifierFQBN(event.selectedBoard)
|
||||
: event.selectedBoard;
|
||||
this._boardsConfig.selectedBoard = event.selectedBoard;
|
||||
}
|
||||
if (isPortIdentifierChangeEvent(event)) {
|
||||
this._boardsConfig.selectedPort = event.selectedPort;
|
||||
}
|
||||
|
||||
if (reason) {
|
||||
event = Object.assign(event, { reason });
|
||||
}
|
||||
|
||||
this.boardsConfigDidChangeEmitter.fire(event);
|
||||
this.refreshBoardList();
|
||||
this.saveState();
|
||||
|
@@ -1,123 +0,0 @@
|
||||
import type { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
BoardDetails,
|
||||
Programmer,
|
||||
isBoardIdentifierChangeEvent,
|
||||
} from '../../common/protocol';
|
||||
import {
|
||||
BoardsDataStore,
|
||||
findDefaultProgrammer,
|
||||
isEmptyData,
|
||||
} from '../boards/boards-data-store';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import { Contribution } from './contribution';
|
||||
|
||||
/**
|
||||
* Before CLI 0.35.0-rc.3, there was no `programmer#default` property in the `board details` response.
|
||||
* This method does the programmer migration in the data store. If there is a programmer selected, it's a noop.
|
||||
* If no programmer is selected, it forcefully reloads the details from the CLI and updates it in the local storage.
|
||||
*/
|
||||
@injectable()
|
||||
export class AutoSelectProgrammer extends Contribution {
|
||||
@inject(BoardsServiceProvider)
|
||||
private readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
@inject(BoardsDataStore)
|
||||
private readonly boardsDataStore: BoardsDataStore;
|
||||
|
||||
override onStart(): void {
|
||||
this.boardsServiceProvider.onBoardsConfigDidChange((event) => {
|
||||
if (isBoardIdentifierChangeEvent(event)) {
|
||||
this.ensureProgrammerIsSelected();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
override onReady(): void {
|
||||
this.boardsServiceProvider.ready.then(() =>
|
||||
this.ensureProgrammerIsSelected()
|
||||
);
|
||||
}
|
||||
|
||||
private async ensureProgrammerIsSelected(): Promise<boolean> {
|
||||
return ensureProgrammerIsSelected({
|
||||
fqbn: this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn,
|
||||
getData: (fqbn) => this.boardsDataStore.getData(fqbn),
|
||||
loadBoardDetails: (fqbn) => this.boardsDataStore.loadBoardDetails(fqbn),
|
||||
selectProgrammer: (arg) => this.boardsDataStore.selectProgrammer(arg),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
interface EnsureProgrammerIsSelectedParams {
|
||||
fqbn: string | undefined;
|
||||
getData: (fqbn: string | undefined) => MaybePromise<BoardsDataStore.Data>;
|
||||
loadBoardDetails: (fqbn: string) => MaybePromise<BoardDetails | undefined>;
|
||||
selectProgrammer(options: {
|
||||
fqbn: string;
|
||||
selectedProgrammer: Programmer;
|
||||
}): MaybePromise<boolean>;
|
||||
}
|
||||
|
||||
export async function ensureProgrammerIsSelected(
|
||||
params: EnsureProgrammerIsSelectedParams
|
||||
): Promise<boolean> {
|
||||
const { fqbn, getData, loadBoardDetails, selectProgrammer } = params;
|
||||
if (!fqbn) {
|
||||
return false;
|
||||
}
|
||||
console.debug(`Ensuring a programmer is selected for ${fqbn}...`);
|
||||
const data = await getData(fqbn);
|
||||
if (isEmptyData(data)) {
|
||||
// For example, the platform is not installed.
|
||||
console.debug(`Skipping. No boards data is available for ${fqbn}.`);
|
||||
return false;
|
||||
}
|
||||
if (data.selectedProgrammer) {
|
||||
console.debug(
|
||||
`A programmer is already selected for ${fqbn}: '${data.selectedProgrammer.id}'.`
|
||||
);
|
||||
return true;
|
||||
}
|
||||
let programmer = findDefaultProgrammer(data.programmers, data);
|
||||
if (programmer) {
|
||||
// select the programmer if the default info is available
|
||||
const result = await selectProgrammer({
|
||||
fqbn,
|
||||
selectedProgrammer: programmer,
|
||||
});
|
||||
if (result) {
|
||||
console.debug(`Selected '${programmer.id}' programmer for ${fqbn}.`);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
console.debug(`Reloading board details for ${fqbn}...`);
|
||||
const reloadedData = await loadBoardDetails(fqbn);
|
||||
if (!reloadedData) {
|
||||
console.debug(`Skipping. No board details found for ${fqbn}.`);
|
||||
return false;
|
||||
}
|
||||
if (!reloadedData.programmers.length) {
|
||||
console.debug(`Skipping. ${fqbn} does not have programmers.`);
|
||||
return false;
|
||||
}
|
||||
programmer = findDefaultProgrammer(reloadedData.programmers, reloadedData);
|
||||
if (!programmer) {
|
||||
console.debug(
|
||||
`Skipping. Could not find a default programmer for ${fqbn}. Programmers were: `
|
||||
);
|
||||
return false;
|
||||
}
|
||||
const result = await selectProgrammer({
|
||||
fqbn,
|
||||
selectedProgrammer: programmer,
|
||||
});
|
||||
if (result) {
|
||||
console.debug(`Selected '${programmer.id}' programmer for ${fqbn}.`);
|
||||
} else {
|
||||
console.debug(
|
||||
`Could not select '${programmer.id}' programmer for ${fqbn}.`
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
@@ -35,7 +35,7 @@ export class BoardsDataMenuUpdater extends Contribution {
|
||||
private readonly toDisposeOnBoardChange = new DisposableCollection();
|
||||
|
||||
override onStart(): void {
|
||||
this.boardsDataStore.onDidChange(() =>
|
||||
this.boardsDataStore.onChanged(() =>
|
||||
this.updateMenuActions(
|
||||
this.boardsServiceProvider.boardsConfig.selectedBoard
|
||||
)
|
||||
@@ -87,7 +87,8 @@ export class BoardsDataMenuUpdater extends Contribution {
|
||||
execute: () =>
|
||||
this.boardsDataStore.selectConfigOption({
|
||||
fqbn,
|
||||
optionsToUpdate: [{ option, selectedValue: value.value }],
|
||||
option,
|
||||
selectedValue: value.value,
|
||||
}),
|
||||
isToggled: () => value.selected,
|
||||
};
|
||||
|
@@ -37,15 +37,11 @@ export class BurnBootloader extends CoreServiceContribution {
|
||||
'arduino/bootloader/burningBootloader',
|
||||
'Burning bootloader...'
|
||||
),
|
||||
task: (progressId, coreService, token) =>
|
||||
coreService.burnBootloader(
|
||||
{
|
||||
...options,
|
||||
progressId,
|
||||
},
|
||||
token
|
||||
),
|
||||
cancelable: true,
|
||||
task: (progressId, coreService) =>
|
||||
coreService.burnBootloader({
|
||||
...options,
|
||||
progressId,
|
||||
}),
|
||||
});
|
||||
this.messageService.info(
|
||||
nls.localize(
|
||||
|
@@ -1,89 +1,83 @@
|
||||
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
|
||||
import {
|
||||
FrontendApplication,
|
||||
FrontendApplicationContribution,
|
||||
} from '@theia/core/lib/browser/frontend-application';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import {
|
||||
KeybindingContribution,
|
||||
KeybindingRegistry,
|
||||
} from '@theia/core/lib/browser/keybinding';
|
||||
import { LabelProvider } from '@theia/core/lib/browser/label-provider';
|
||||
import { OpenerService, open } from '@theia/core/lib/browser/opener-service';
|
||||
import { Saveable } from '@theia/core/lib/browser/saveable';
|
||||
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
||||
import {
|
||||
TabBarToolbarContribution,
|
||||
TabBarToolbarRegistry,
|
||||
} from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
import { CancellationToken } from '@theia/core/lib/common/cancellation';
|
||||
import {
|
||||
Command,
|
||||
CommandContribution,
|
||||
CommandRegistry,
|
||||
CommandService,
|
||||
} from '@theia/core/lib/common/command';
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import {
|
||||
MenuContribution,
|
||||
MenuModelRegistry,
|
||||
} from '@theia/core/lib/common/menu';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { MessageType } from '@theia/core/lib/common/message-service-protocol';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { MaybePromise, isObject } from '@theia/core/lib/common/types';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
interfaces,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import { Saveable } from '@theia/core/lib/browser/saveable';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { NotificationManager } from '@theia/messages/lib/browser/notifications-manager';
|
||||
import { OutputChannelSeverity } from '@theia/output/lib/browser/output-channel';
|
||||
import { MainMenuManager } from '../../common/main-menu-manager';
|
||||
import { userAbort } from '../../common/nls';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { LabelProvider } from '@theia/core/lib/browser/label-provider';
|
||||
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { open, OpenerService } from '@theia/core/lib/browser/opener-service';
|
||||
import {
|
||||
CoreError,
|
||||
CoreService,
|
||||
FileSystemExt,
|
||||
ResponseServiceClient,
|
||||
Sketch,
|
||||
SketchesService,
|
||||
} from '../../common/protocol';
|
||||
MenuModelRegistry,
|
||||
MenuContribution,
|
||||
} from '@theia/core/lib/common/menu';
|
||||
import {
|
||||
ExecuteWithProgress,
|
||||
UserAbortApplicationError,
|
||||
} from '../../common/protocol/progressible';
|
||||
import { ArduinoPreferences } from '../arduino-preferences';
|
||||
import { BoardsDataStore } from '../boards/boards-data-store';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import { ConfigServiceClient } from '../config/config-service-client';
|
||||
import { DialogService } from '../dialog-service';
|
||||
KeybindingRegistry,
|
||||
KeybindingContribution,
|
||||
} from '@theia/core/lib/browser/keybinding';
|
||||
import {
|
||||
TabBarToolbarContribution,
|
||||
TabBarToolbarRegistry,
|
||||
} from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
import {
|
||||
FrontendApplicationContribution,
|
||||
FrontendApplication,
|
||||
} from '@theia/core/lib/browser/frontend-application';
|
||||
import {
|
||||
Command,
|
||||
CommandRegistry,
|
||||
CommandContribution,
|
||||
CommandService,
|
||||
} from '@theia/core/lib/common/command';
|
||||
import { SettingsService } from '../dialogs/settings/settings';
|
||||
import {
|
||||
CurrentSketch,
|
||||
SketchesServiceClientImpl,
|
||||
} from '../sketches-service-client-impl';
|
||||
import { ApplicationConnectionStatusContribution } from '../theia/core/connection-status-service';
|
||||
import {
|
||||
SketchesService,
|
||||
FileSystemExt,
|
||||
Sketch,
|
||||
CoreService,
|
||||
CoreError,
|
||||
ResponseServiceClient,
|
||||
} from '../../common/protocol';
|
||||
import { ArduinoPreferences } from '../arduino-preferences';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import { nls } from '@theia/core';
|
||||
import { OutputChannelManager } from '../theia/output/output-channel';
|
||||
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
|
||||
import { ExecuteWithProgress } from '../../common/protocol/progressible';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import { BoardsDataStore } from '../boards/boards-data-store';
|
||||
import { NotificationManager } from '@theia/messages/lib/browser/notifications-manager';
|
||||
import { MessageType } from '@theia/core/lib/common/message-service-protocol';
|
||||
import { WorkspaceService } from '../theia/workspace/workspace-service';
|
||||
import { MainMenuManager } from '../../common/main-menu-manager';
|
||||
import { ConfigServiceClient } from '../config/config-service-client';
|
||||
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
||||
import { DialogService } from '../dialog-service';
|
||||
import { ApplicationConnectionStatusContribution } from '../theia/core/connection-status-service';
|
||||
|
||||
export {
|
||||
Command,
|
||||
CommandRegistry,
|
||||
KeybindingRegistry,
|
||||
MenuModelRegistry,
|
||||
Sketch,
|
||||
KeybindingRegistry,
|
||||
TabBarToolbarRegistry,
|
||||
URI,
|
||||
Sketch,
|
||||
open,
|
||||
};
|
||||
|
||||
@@ -253,12 +247,6 @@ export abstract class CoreServiceContribution extends SketchContribution {
|
||||
}
|
||||
|
||||
protected handleError(error: unknown): void {
|
||||
if (isObject(error) && UserAbortApplicationError.is(error)) {
|
||||
this.outputChannelManager
|
||||
.getChannel('Arduino')
|
||||
.appendLine(userAbort, OutputChannelSeverity.Warning);
|
||||
return;
|
||||
}
|
||||
this.tryToastErrorMessage(error);
|
||||
}
|
||||
|
||||
@@ -305,13 +293,7 @@ export abstract class CoreServiceContribution extends SketchContribution {
|
||||
protected async doWithProgress<T>(options: {
|
||||
progressText: string;
|
||||
keepOutput?: boolean;
|
||||
task: (
|
||||
progressId: string,
|
||||
coreService: CoreService,
|
||||
cancellationToken?: CancellationToken
|
||||
) => Promise<T>;
|
||||
// false by default
|
||||
cancelable?: boolean;
|
||||
task: (progressId: string, coreService: CoreService) => Promise<T>;
|
||||
}): Promise<T> {
|
||||
const toDisposeOnComplete = new DisposableCollection(
|
||||
this.maybeActivateMonitorWidget()
|
||||
@@ -324,10 +306,8 @@ export abstract class CoreServiceContribution extends SketchContribution {
|
||||
messageService: this.messageService,
|
||||
responseService: this.responseService,
|
||||
progressText,
|
||||
run: ({ progressId, cancellationToken }) =>
|
||||
task(progressId, this.coreService, cancellationToken),
|
||||
run: ({ progressId }) => task(progressId, this.coreService),
|
||||
keepOutput,
|
||||
cancelable: options.cancelable,
|
||||
});
|
||||
toDisposeOnComplete.dispose();
|
||||
return result;
|
||||
|
@@ -1,173 +1,106 @@
|
||||
import { Emitter, Event } from '@theia/core/lib/common/event';
|
||||
import { MenuModelRegistry } from '@theia/core/lib/common/menu/menu-model-registry';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { noBoardSelected } from '../../common/nls';
|
||||
import { Event, Emitter } from '@theia/core/lib/common/event';
|
||||
import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import {
|
||||
BoardDetails,
|
||||
Board,
|
||||
BoardIdentifier,
|
||||
BoardsService,
|
||||
CheckDebugEnabledParams,
|
||||
ExecutableService,
|
||||
SketchRef,
|
||||
isBoardIdentifierChangeEvent,
|
||||
isCompileSummary,
|
||||
Sketch,
|
||||
} from '../../common/protocol';
|
||||
import { BoardsDataStore } from '../boards/boards-data-store';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import { HostedPluginSupport } from '../hosted/hosted-plugin-support';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { CurrentSketch } from '../sketches-service-client-impl';
|
||||
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
|
||||
import {
|
||||
URI,
|
||||
Command,
|
||||
CommandRegistry,
|
||||
SketchContribution,
|
||||
TabBarToolbarRegistry,
|
||||
URI,
|
||||
} from './contribution';
|
||||
import { MaybePromise, MenuModelRegistry, nls } from '@theia/core/lib/common';
|
||||
import { CurrentSketch } from '../sketches-service-client-impl';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
|
||||
const COMPILE_FOR_DEBUG_KEY = 'arduino-compile-for-debug';
|
||||
|
||||
interface StartDebugParams {
|
||||
/**
|
||||
* Absolute filesystem path to the Arduino CLI executable.
|
||||
*/
|
||||
readonly cliPath: string;
|
||||
/**
|
||||
* The the board to debug.
|
||||
*/
|
||||
readonly board: Readonly<{ fqbn: string; name?: string }>;
|
||||
/**
|
||||
* Absolute filesystem path of the sketch to debug.
|
||||
*/
|
||||
readonly sketchPath: string;
|
||||
/**
|
||||
* Location where the `launch.json` will be created on the fly before starting every debug session.
|
||||
* If not defined, it falls back to `sketchPath/.vscode/launch.json`.
|
||||
*/
|
||||
readonly launchConfigsDirPath?: string;
|
||||
/**
|
||||
* Absolute path to the `arduino-cli.yaml` file. If not specified, it falls back to `~/.arduinoIDE/arduino-cli.yaml`.
|
||||
*/
|
||||
readonly cliConfigPath?: string;
|
||||
/**
|
||||
* Programmer for the debugging.
|
||||
*/
|
||||
readonly programmer?: string;
|
||||
/**
|
||||
* Custom progress title to use when getting the debug information from the CLI.
|
||||
*/
|
||||
readonly title?: string;
|
||||
}
|
||||
type StartDebugResult = boolean;
|
||||
|
||||
export const DebugDisabledStatusMessageSource = Symbol(
|
||||
'DebugDisabledStatusMessageSource'
|
||||
);
|
||||
export interface DebugDisabledStatusMessageSource {
|
||||
/**
|
||||
* `undefined` if debugging is enabled (for the currently selected board + programmer + config options).
|
||||
* Otherwise, it's the human readable message why it's disabled.
|
||||
*/
|
||||
get message(): string | undefined;
|
||||
/**
|
||||
* Emits an event when {@link message} changes.
|
||||
*/
|
||||
get onDidChangeMessage(): Event<string | undefined>;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class Debug
|
||||
extends SketchContribution
|
||||
implements DebugDisabledStatusMessageSource
|
||||
{
|
||||
export class Debug extends SketchContribution {
|
||||
@inject(HostedPluginSupport)
|
||||
private readonly hostedPluginSupport: HostedPluginSupport;
|
||||
|
||||
@inject(NotificationCenter)
|
||||
private readonly notificationCenter: NotificationCenter;
|
||||
|
||||
@inject(ExecutableService)
|
||||
private readonly executableService: ExecutableService;
|
||||
|
||||
@inject(BoardsService)
|
||||
private readonly boardService: BoardsService;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
private readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
@inject(BoardsDataStore)
|
||||
private readonly boardsDataStore: BoardsDataStore;
|
||||
|
||||
/**
|
||||
* If `undefined`, debugging is enabled. Otherwise, the human-readable reason why it's disabled.
|
||||
* If `undefined`, debugging is enabled. Otherwise, the reason why it's disabled.
|
||||
*/
|
||||
private _message?: string = noBoardSelected; // Initial pessimism.
|
||||
private readonly didChangeMessageEmitter = new Emitter<string | undefined>();
|
||||
readonly onDidChangeMessage = this.didChangeMessageEmitter.event;
|
||||
private _disabledMessages?: string = nls.localize(
|
||||
'arduino/common/noBoardSelected',
|
||||
'No board selected'
|
||||
); // Initial pessimism.
|
||||
private disabledMessageDidChangeEmitter = new Emitter<string | undefined>();
|
||||
private onDisabledMessageDidChange =
|
||||
this.disabledMessageDidChangeEmitter.event;
|
||||
|
||||
get message(): string | undefined {
|
||||
return this._message;
|
||||
private get disabledMessage(): string | undefined {
|
||||
return this._disabledMessages;
|
||||
}
|
||||
private set message(message: string | undefined) {
|
||||
this._message = message;
|
||||
this.didChangeMessageEmitter.fire(this._message);
|
||||
private set disabledMessage(message: string | undefined) {
|
||||
this._disabledMessages = message;
|
||||
this.disabledMessageDidChangeEmitter.fire(this._disabledMessages);
|
||||
}
|
||||
|
||||
private readonly debugToolbarItem = {
|
||||
id: Debug.Commands.START_DEBUGGING.id,
|
||||
command: Debug.Commands.START_DEBUGGING.id,
|
||||
tooltip: `${
|
||||
this.message
|
||||
this.disabledMessage
|
||||
? nls.localize(
|
||||
'arduino/debug/debugWithMessage',
|
||||
'Debug - {0}',
|
||||
this.message
|
||||
this.disabledMessage
|
||||
)
|
||||
: Debug.Commands.START_DEBUGGING.label
|
||||
}`,
|
||||
priority: 3,
|
||||
onDidChange: this.onDidChangeMessage as Event<void>,
|
||||
onDidChange: this.onDisabledMessageDidChange as Event<void>,
|
||||
};
|
||||
|
||||
override onStart(): void {
|
||||
this.onDidChangeMessage(
|
||||
this.onDisabledMessageDidChange(
|
||||
() =>
|
||||
(this.debugToolbarItem.tooltip = `${
|
||||
this.message
|
||||
this.disabledMessage
|
||||
? nls.localize(
|
||||
'arduino/debug/debugWithMessage',
|
||||
'Debug - {0}',
|
||||
this.message
|
||||
this.disabledMessage
|
||||
)
|
||||
: Debug.Commands.START_DEBUGGING.label
|
||||
}`)
|
||||
);
|
||||
this.boardsServiceProvider.onBoardsConfigDidChange((event) => {
|
||||
if (isBoardIdentifierChangeEvent(event)) {
|
||||
this.updateMessage();
|
||||
}
|
||||
});
|
||||
this.notificationCenter.onPlatformDidInstall(() => this.updateMessage());
|
||||
this.notificationCenter.onPlatformDidUninstall(() => this.updateMessage());
|
||||
this.boardsDataStore.onDidChange((event) => {
|
||||
const selectedFqbn =
|
||||
this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn;
|
||||
if (event.changes.find((change) => change.fqbn === selectedFqbn)) {
|
||||
this.updateMessage();
|
||||
}
|
||||
});
|
||||
this.commandService.onDidExecuteCommand((event) => {
|
||||
const { commandId, args } = event;
|
||||
if (
|
||||
commandId === 'arduino.languageserver.notifyBuildDidComplete' &&
|
||||
isCompileSummary(args[0])
|
||||
) {
|
||||
this.updateMessage();
|
||||
this.refreshState(event.selectedBoard);
|
||||
}
|
||||
});
|
||||
this.notificationCenter.onPlatformDidInstall(() => this.refreshState());
|
||||
this.notificationCenter.onPlatformDidUninstall(() => this.refreshState());
|
||||
}
|
||||
|
||||
override onReady(): void {
|
||||
this.boardsServiceProvider.ready.then(() => this.updateMessage());
|
||||
override onReady(): MaybePromise<void> {
|
||||
this.refreshState();
|
||||
}
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
@@ -175,7 +108,7 @@ export class Debug
|
||||
execute: () => this.startDebug(),
|
||||
isVisible: (widget) =>
|
||||
ArduinoToolbar.is(widget) && widget.side === 'left',
|
||||
isEnabled: () => !this.message,
|
||||
isEnabled: () => !this.disabledMessage,
|
||||
});
|
||||
registry.registerCommand(Debug.Commands.TOGGLE_OPTIMIZE_FOR_DEBUG, {
|
||||
execute: () => this.toggleCompileForDebug(),
|
||||
@@ -198,56 +131,94 @@ export class Debug
|
||||
});
|
||||
}
|
||||
|
||||
private async updateMessage(): Promise<void> {
|
||||
try {
|
||||
await this.isDebugEnabled();
|
||||
this.message = undefined;
|
||||
} catch (err) {
|
||||
let message = String(err);
|
||||
if (err instanceof Error) {
|
||||
message = err.message;
|
||||
}
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
private async isDebugEnabled(
|
||||
board: BoardIdentifier | undefined = this.boardsServiceProvider.boardsConfig
|
||||
private async refreshState(
|
||||
board: Board | undefined = this.boardsServiceProvider.boardsConfig
|
||||
.selectedBoard
|
||||
): Promise<string> {
|
||||
const debugFqbn = await isDebugEnabled(
|
||||
board,
|
||||
(fqbn) => this.boardService.getBoardDetails({ fqbn }),
|
||||
(fqbn) => this.boardsDataStore.getData(fqbn),
|
||||
(fqbn) => this.boardsDataStore.appendConfigToFqbn(fqbn),
|
||||
(params) => this.boardService.checkDebugEnabled(params)
|
||||
);
|
||||
return debugFqbn;
|
||||
): Promise<void> {
|
||||
if (!board) {
|
||||
this.disabledMessage = nls.localize(
|
||||
'arduino/common/noBoardSelected',
|
||||
'No board selected'
|
||||
);
|
||||
return;
|
||||
}
|
||||
const fqbn = board.fqbn;
|
||||
if (!fqbn) {
|
||||
this.disabledMessage = nls.localize(
|
||||
'arduino/debug/noPlatformInstalledFor',
|
||||
"Platform is not installed for '{0}'",
|
||||
board.name
|
||||
);
|
||||
return;
|
||||
}
|
||||
const details = await this.boardService.getBoardDetails({ fqbn });
|
||||
if (!details) {
|
||||
this.disabledMessage = nls.localize(
|
||||
'arduino/debug/noPlatformInstalledFor',
|
||||
"Platform is not installed for '{0}'",
|
||||
board.name
|
||||
);
|
||||
return;
|
||||
}
|
||||
const { debuggingSupported } = details;
|
||||
if (!debuggingSupported) {
|
||||
this.disabledMessage = nls.localize(
|
||||
'arduino/debug/debuggingNotSupported',
|
||||
"Debugging is not supported by '{0}'",
|
||||
board.name
|
||||
);
|
||||
} else {
|
||||
this.disabledMessage = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async startDebug(
|
||||
board: BoardIdentifier | undefined = this.boardsServiceProvider.boardsConfig
|
||||
.selectedBoard,
|
||||
sketch:
|
||||
| CurrentSketch
|
||||
| undefined = this.sketchServiceClient.tryGetCurrentSketch()
|
||||
): Promise<StartDebugResult> {
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return false;
|
||||
.selectedBoard
|
||||
): Promise<void> {
|
||||
if (!board) {
|
||||
return;
|
||||
}
|
||||
const params = await this.createStartDebugParams(board);
|
||||
if (!params) {
|
||||
return false;
|
||||
const { name, fqbn } = board;
|
||||
if (!fqbn) {
|
||||
return;
|
||||
}
|
||||
await this.hostedPluginSupport.didStart;
|
||||
const [sketch, executables] = await Promise.all([
|
||||
this.sketchServiceClient.currentSketch(),
|
||||
this.executableService.list(),
|
||||
]);
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return;
|
||||
}
|
||||
const ideTempFolderUri = await this.sketchesService.getIdeTempFolderUri(
|
||||
sketch
|
||||
);
|
||||
const [cliPath, sketchPath, configPath] = await Promise.all([
|
||||
this.fileService.fsPath(new URI(executables.cliUri)),
|
||||
this.fileService.fsPath(new URI(sketch.uri)),
|
||||
this.fileService.fsPath(new URI(ideTempFolderUri)),
|
||||
]);
|
||||
const config = {
|
||||
cliPath,
|
||||
board: {
|
||||
fqbn,
|
||||
name,
|
||||
},
|
||||
sketchPath,
|
||||
configPath,
|
||||
};
|
||||
try {
|
||||
const result = await this.debug(params);
|
||||
return Boolean(result);
|
||||
await this.commandService.executeCommand('arduino.debug.start', config);
|
||||
} catch (err) {
|
||||
if (await this.isSketchNotVerifiedError(err, sketch)) {
|
||||
const yes = nls.localize('vscode/extensionsUtils/yes', 'Yes');
|
||||
const answer = await this.messageService.error(
|
||||
sketchIsNotCompiled(sketch.name),
|
||||
nls.localize(
|
||||
'arduino/debug/sketchIsNotCompiled',
|
||||
"Sketch '{0}' must be verified before starting a debug session. Please verify the sketch and start debugging again. Do you want to verify the sketch now?",
|
||||
sketch.name
|
||||
),
|
||||
yes
|
||||
);
|
||||
if (answer === yes) {
|
||||
@@ -259,16 +230,6 @@ export class Debug
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private async debug(
|
||||
params: StartDebugParams
|
||||
): Promise<StartDebugResult | undefined> {
|
||||
return this.commandService.executeCommand<StartDebugResult>(
|
||||
'arduino.debug.start',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
get compileForDebug(): boolean {
|
||||
@@ -276,7 +237,7 @@ export class Debug
|
||||
return value === 'true';
|
||||
}
|
||||
|
||||
private toggleCompileForDebug(): void {
|
||||
async toggleCompileForDebug(): Promise<void> {
|
||||
const oldState = this.compileForDebug;
|
||||
const newState = !oldState;
|
||||
window.localStorage.setItem(COMPILE_FOR_DEBUG_KEY, String(newState));
|
||||
@@ -285,7 +246,7 @@ export class Debug
|
||||
|
||||
private async isSketchNotVerifiedError(
|
||||
err: unknown,
|
||||
sketch: SketchRef
|
||||
sketch: Sketch
|
||||
): Promise<boolean> {
|
||||
if (err instanceof Error) {
|
||||
try {
|
||||
@@ -299,48 +260,6 @@ export class Debug
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private async createStartDebugParams(
|
||||
board: BoardIdentifier | undefined
|
||||
): Promise<StartDebugParams | undefined> {
|
||||
if (!board || !board.fqbn) {
|
||||
return undefined;
|
||||
}
|
||||
let debugFqbn: string | undefined = undefined;
|
||||
try {
|
||||
debugFqbn = await this.isDebugEnabled(board);
|
||||
} catch {}
|
||||
if (!debugFqbn) {
|
||||
return undefined;
|
||||
}
|
||||
const [sketch, executables, boardsData] = await Promise.all([
|
||||
this.sketchServiceClient.currentSketch(),
|
||||
this.executableService.list(),
|
||||
this.boardsDataStore.getData(board.fqbn),
|
||||
]);
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return undefined;
|
||||
}
|
||||
const ideTempFolderUri = await this.sketchesService.getIdeTempFolderUri(
|
||||
sketch
|
||||
);
|
||||
const [cliPath, sketchPath, launchConfigsDirPath] = await Promise.all([
|
||||
this.fileService.fsPath(new URI(executables.cliUri)),
|
||||
this.fileService.fsPath(new URI(sketch.uri)),
|
||||
this.fileService.fsPath(new URI(ideTempFolderUri)),
|
||||
]);
|
||||
return {
|
||||
board: { fqbn: debugFqbn, name: board.name },
|
||||
cliPath,
|
||||
sketchPath,
|
||||
launchConfigsDirPath,
|
||||
programmer: boardsData.selectedProgrammer?.id,
|
||||
title: nls.localize(
|
||||
'arduino/debug/getDebugInfo',
|
||||
'Getting debug info...'
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
export namespace Debug {
|
||||
export namespace Commands {
|
||||
@@ -365,78 +284,3 @@ export namespace Debug {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves with the FQBN to use for the `debug --info --programmer p --fqbn $FQBN` command. Otherwise, rejects.
|
||||
*
|
||||
* (non-API)
|
||||
*/
|
||||
export async function isDebugEnabled(
|
||||
board: BoardIdentifier | undefined,
|
||||
getDetails: (fqbn: string) => MaybePromise<BoardDetails | undefined>,
|
||||
getData: (fqbn: string) => MaybePromise<BoardsDataStore.Data>,
|
||||
appendConfigToFqbn: (fqbn: string) => MaybePromise<string | undefined>,
|
||||
checkDebugEnabled: (params: CheckDebugEnabledParams) => MaybePromise<string>
|
||||
): Promise<string> {
|
||||
if (!board) {
|
||||
throw new Error(noBoardSelected);
|
||||
}
|
||||
const { fqbn } = board;
|
||||
if (!fqbn) {
|
||||
throw new Error(noPlatformInstalledFor(board.name));
|
||||
}
|
||||
const [details, data, fqbnWithConfig] = await Promise.all([
|
||||
getDetails(fqbn),
|
||||
getData(fqbn),
|
||||
appendConfigToFqbn(fqbn),
|
||||
]);
|
||||
if (!details) {
|
||||
throw new Error(noPlatformInstalledFor(board.name));
|
||||
}
|
||||
if (!fqbnWithConfig) {
|
||||
throw new Error(
|
||||
`Failed to append boards config to the FQBN. Original FQBN was: ${fqbn}`
|
||||
);
|
||||
}
|
||||
const params = {
|
||||
fqbn: fqbnWithConfig,
|
||||
programmer: data.selectedProgrammer?.id,
|
||||
};
|
||||
try {
|
||||
const debugFqbn = await checkDebugEnabled(params);
|
||||
return debugFqbn;
|
||||
} catch (err) {
|
||||
throw new Error(debuggingNotSupported(board.name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-API)
|
||||
*/
|
||||
export function sketchIsNotCompiled(sketchName: string): string {
|
||||
return nls.localize(
|
||||
'arduino/debug/sketchIsNotCompiled',
|
||||
"Sketch '{0}' must be verified before starting a debug session. Please verify the sketch and start debugging again. Do you want to verify the sketch now?",
|
||||
sketchName
|
||||
);
|
||||
}
|
||||
/**
|
||||
* (non-API)
|
||||
*/
|
||||
export function noPlatformInstalledFor(boardName: string): string {
|
||||
return nls.localize(
|
||||
'arduino/debug/noPlatformInstalledFor',
|
||||
"Platform is not installed for '{0}'",
|
||||
boardName
|
||||
);
|
||||
}
|
||||
/**
|
||||
* (non-API)
|
||||
*/
|
||||
export function debuggingNotSupported(boardName: string): string {
|
||||
return nls.localize(
|
||||
'arduino/debug/debuggingNotSupported',
|
||||
"Debugging is not supported by '{0}'",
|
||||
boardName
|
||||
);
|
||||
}
|
||||
|
@@ -300,8 +300,8 @@ export class LibraryExamples extends Examples {
|
||||
this.notificationCenter.onLibraryDidUninstall(() => this.update());
|
||||
}
|
||||
|
||||
override onReady(): void {
|
||||
this.boardsServiceProvider.ready.then(() => this.update());
|
||||
override async onReady(): Promise<void> {
|
||||
this.update(); // no `await`
|
||||
}
|
||||
|
||||
protected override handleBoardChanged(board: Board | undefined): void {
|
||||
|
@@ -2,6 +2,7 @@ import PQueue from 'p-queue';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
||||
import { EditorManager } from '@theia/editor/lib/browser';
|
||||
import { MenuModelRegistry, MenuPath } from '@theia/core/lib/common/menu';
|
||||
import {
|
||||
Disposable,
|
||||
@@ -21,25 +22,28 @@ import { CurrentSketch } from '../sketches-service-client-impl';
|
||||
@injectable()
|
||||
export class IncludeLibrary extends SketchContribution {
|
||||
@inject(CommandRegistry)
|
||||
private readonly commandRegistry: CommandRegistry;
|
||||
protected readonly commandRegistry: CommandRegistry;
|
||||
|
||||
@inject(MenuModelRegistry)
|
||||
private readonly menuRegistry: MenuModelRegistry;
|
||||
protected readonly menuRegistry: MenuModelRegistry;
|
||||
|
||||
@inject(MainMenuManager)
|
||||
private readonly mainMenuManager: MainMenuManager;
|
||||
protected readonly mainMenuManager: MainMenuManager;
|
||||
|
||||
@inject(EditorManager)
|
||||
protected override readonly editorManager: EditorManager;
|
||||
|
||||
@inject(NotificationCenter)
|
||||
private readonly notificationCenter: NotificationCenter;
|
||||
protected readonly notificationCenter: NotificationCenter;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
private readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
protected readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
|
||||
@inject(LibraryService)
|
||||
private readonly libraryService: LibraryService;
|
||||
protected readonly libraryService: LibraryService;
|
||||
|
||||
private readonly queue = new PQueue({ autoStart: true, concurrency: 1 });
|
||||
private readonly toDispose = new DisposableCollection();
|
||||
protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 });
|
||||
protected readonly toDispose = new DisposableCollection();
|
||||
|
||||
override onStart(): void {
|
||||
this.boardsServiceProvider.onBoardsConfigDidChange(() =>
|
||||
@@ -52,8 +56,8 @@ export class IncludeLibrary extends SketchContribution {
|
||||
this.notificationCenter.onDidReinitialize(() => this.updateMenuActions());
|
||||
}
|
||||
|
||||
override onReady(): void {
|
||||
this.boardsServiceProvider.ready.then(() => this.updateMenuActions());
|
||||
override async onReady(): Promise<void> {
|
||||
this.updateMenuActions();
|
||||
}
|
||||
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
@@ -89,7 +93,7 @@ export class IncludeLibrary extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
private async updateMenuActions(): Promise<void> {
|
||||
protected async updateMenuActions(): Promise<void> {
|
||||
return this.queue.add(async () => {
|
||||
this.toDispose.dispose();
|
||||
this.mainMenuManager.update();
|
||||
@@ -135,7 +139,7 @@ export class IncludeLibrary extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
private registerLibrary(
|
||||
protected registerLibrary(
|
||||
libraryOrPlaceholder: LibraryPackage | string,
|
||||
menuPath: MenuPath
|
||||
): Disposable {
|
||||
@@ -168,7 +172,7 @@ export class IncludeLibrary extends SketchContribution {
|
||||
);
|
||||
}
|
||||
|
||||
private async includeLibrary(library: LibraryPackage): Promise<void> {
|
||||
protected async includeLibrary(library: LibraryPackage): Promise<void> {
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return;
|
||||
|
@@ -6,105 +6,40 @@ import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { Mutex } from 'async-mutex';
|
||||
import {
|
||||
ArduinoDaemon,
|
||||
assertSanitizedFqbn,
|
||||
BoardIdentifier,
|
||||
BoardsService,
|
||||
ExecutableService,
|
||||
isBoardIdentifierChangeEvent,
|
||||
sanitizeFqbn,
|
||||
} from '../../common/protocol';
|
||||
import {
|
||||
defaultAsyncWorkers,
|
||||
maxAsyncWorkers,
|
||||
minAsyncWorkers,
|
||||
} from '../arduino-preferences';
|
||||
import { BoardsDataStore } from '../boards/boards-data-store';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import { HostedPluginEvents } from '../hosted/hosted-plugin-events';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { CurrentSketch } from '../sketches-service-client-impl';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import { HostedPluginEvents } from '../hosted-plugin-events';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { SketchContribution, URI } from './contribution';
|
||||
|
||||
interface DaemonAddress {
|
||||
/**
|
||||
* The host where the Arduino CLI daemon is available.
|
||||
*/
|
||||
readonly hostname: string;
|
||||
/**
|
||||
* The port where the Arduino CLI daemon is listening.
|
||||
*/
|
||||
readonly port: number;
|
||||
/**
|
||||
* The [id](https://arduino.github.io/arduino-cli/latest/rpc/commands/#instance) of the initialized core Arduino client instance.
|
||||
*/
|
||||
readonly instance: number;
|
||||
}
|
||||
|
||||
interface StartLanguageServerParams {
|
||||
/**
|
||||
* Absolute filesystem path to the Arduino Language Server executable.
|
||||
*/
|
||||
readonly lsPath: string;
|
||||
/**
|
||||
* The hostname and the port for the gRPC channel connecting to the Arduino CLI daemon.
|
||||
* The `instance` number is for the initialized core Arduino client.
|
||||
*/
|
||||
readonly daemonAddress: DaemonAddress;
|
||||
/**
|
||||
* Absolute filesystem path to [`clangd`](https://clangd.llvm.org/).
|
||||
*/
|
||||
readonly clangdPath: string;
|
||||
/**
|
||||
* The board is relevant to start a specific "flavor" of the language.
|
||||
*/
|
||||
readonly board: { fqbn: string; name?: string };
|
||||
/**
|
||||
* `true` if the LS should generate the log files into the default location. The default location is the `cwd` of the process.
|
||||
* It's very often the same as the workspace root of the IDE, aka the sketch folder.
|
||||
* When it is a string, it is the absolute filesystem path to the folder to generate the log files.
|
||||
* If `string`, but the path is inaccessible, the log files will be generated into the default location.
|
||||
*/
|
||||
readonly log?: boolean | string;
|
||||
/**
|
||||
* Optional `env` for the language server process.
|
||||
*/
|
||||
readonly env?: NodeJS.ProcessEnv;
|
||||
/**
|
||||
* Additional flags for the Arduino Language server process.
|
||||
*/
|
||||
readonly flags?: readonly string[];
|
||||
/**
|
||||
* Set to `true`, to enable `Diagnostics`.
|
||||
*/
|
||||
readonly realTimeDiagnostics?: boolean;
|
||||
/**
|
||||
* If `true`, the logging is not forwarded to the _Output_ view via the language client.
|
||||
*/
|
||||
readonly silentOutput?: boolean;
|
||||
/**
|
||||
* Number of async workers used by `clangd`. Background index also uses this many workers. If `0`, `clangd` uses all available cores. It's `0` by default.
|
||||
*/
|
||||
readonly jobs?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* The FQBN the language server runs with or `undefined` if it could not start.
|
||||
*/
|
||||
type StartLanguageServerResult = string | undefined;
|
||||
import { BoardsDataStore } from '../boards/boards-data-store';
|
||||
|
||||
@injectable()
|
||||
export class InoLanguage extends SketchContribution {
|
||||
@inject(HostedPluginEvents)
|
||||
private readonly hostedPluginEvents: HostedPluginEvents;
|
||||
|
||||
@inject(ExecutableService)
|
||||
private readonly executableService: ExecutableService;
|
||||
|
||||
@inject(ArduinoDaemon)
|
||||
private readonly daemon: ArduinoDaemon;
|
||||
|
||||
@inject(BoardsService)
|
||||
private readonly boardsService: BoardsService;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
private readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
|
||||
@inject(NotificationCenter)
|
||||
private readonly notificationCenter: NotificationCenter;
|
||||
|
||||
@inject(BoardsDataStore)
|
||||
private readonly boardDataStore: BoardsDataStore;
|
||||
|
||||
@@ -145,7 +80,6 @@ export class InoLanguage extends SketchContribution {
|
||||
switch (preferenceName) {
|
||||
case 'arduino.language.log':
|
||||
case 'arduino.language.realTimeDiagnostics':
|
||||
case 'arduino.language.asyncWorkers':
|
||||
forceRestart();
|
||||
}
|
||||
}
|
||||
@@ -156,30 +90,30 @@ export class InoLanguage extends SketchContribution {
|
||||
this.notificationCenter.onPlatformDidInstall(() => forceRestart()),
|
||||
this.notificationCenter.onPlatformDidUninstall(() => forceRestart()),
|
||||
this.notificationCenter.onDidReinitialize(() => forceRestart()),
|
||||
this.boardDataStore.onDidChange((event) => {
|
||||
this.boardDataStore.onChanged((dataChangePerFqbn) => {
|
||||
if (this.languageServerFqbn) {
|
||||
const sanitizedFQBN = sanitizeFqbn(this.languageServerFqbn);
|
||||
// The incoming FQBNs might contain custom boards configs, sanitize them before the comparison.
|
||||
// https://github.com/arduino/arduino-ide/pull/2113#pullrequestreview-1499998328
|
||||
const matchingChange = event.changes.find(
|
||||
(change) => sanitizedFQBN === sanitizeFqbn(change.fqbn)
|
||||
const sanitizedFqbn = sanitizeFqbn(this.languageServerFqbn);
|
||||
if (!sanitizeFqbn) {
|
||||
throw new Error(
|
||||
`Failed to sanitize the FQBN of the running language server. FQBN with the board settings was: ${this.languageServerFqbn}`
|
||||
);
|
||||
}
|
||||
const matchingFqbn = dataChangePerFqbn.find(
|
||||
(fqbn) => sanitizedFqbn === fqbn
|
||||
);
|
||||
const { boardsConfig } = this.boardsServiceProvider;
|
||||
if (
|
||||
matchingChange &&
|
||||
boardsConfig.selectedBoard?.fqbn === matchingChange.fqbn
|
||||
matchingFqbn &&
|
||||
boardsConfig.selectedBoard?.fqbn === matchingFqbn
|
||||
) {
|
||||
start(boardsConfig.selectedBoard);
|
||||
}
|
||||
}
|
||||
}),
|
||||
]);
|
||||
Promise.all([
|
||||
this.boardsServiceProvider.ready,
|
||||
this.preferences.ready,
|
||||
]).then(() => {
|
||||
start(this.boardsServiceProvider.boardsConfig.selectedBoard);
|
||||
});
|
||||
this.boardsServiceProvider.ready.then(() =>
|
||||
start(this.boardsServiceProvider.boardsConfig.selectedBoard)
|
||||
);
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
@@ -192,7 +126,7 @@ export class InoLanguage extends SketchContribution {
|
||||
forceStart = false
|
||||
): Promise<void> {
|
||||
const port = await this.daemon.tryGetPort();
|
||||
if (typeof port !== 'number') {
|
||||
if (!port) {
|
||||
return;
|
||||
}
|
||||
const release = await this.languageServerStartMutex.acquire();
|
||||
@@ -224,6 +158,7 @@ export class InoLanguage extends SketchContribution {
|
||||
}
|
||||
return;
|
||||
}
|
||||
assertSanitizedFqbn(fqbn);
|
||||
const fqbnWithConfig = await this.boardDataStore.appendConfigToFqbn(fqbn);
|
||||
if (!fqbnWithConfig) {
|
||||
throw new Error(
|
||||
@@ -234,16 +169,11 @@ export class InoLanguage extends SketchContribution {
|
||||
// NOOP
|
||||
return;
|
||||
}
|
||||
this.logger.info(`Starting language server: ${fqbnWithConfig}`);
|
||||
const log = this.preferences.get('arduino.language.log');
|
||||
const realTimeDiagnostics = this.preferences.get(
|
||||
'arduino.language.realTimeDiagnostics'
|
||||
);
|
||||
const jobs = this.getAsyncWorkersPreferenceSafe();
|
||||
this.logger.info(
|
||||
`Starting language server: ${fqbnWithConfig}${
|
||||
jobs ? ` (async worker count: ${jobs})` : ''
|
||||
}`
|
||||
);
|
||||
let currentSketchPath: string | undefined = undefined;
|
||||
if (log) {
|
||||
const currentSketch = await this.sketchServiceClient.currentSketch();
|
||||
@@ -267,23 +197,22 @@ export class InoLanguage extends SketchContribution {
|
||||
);
|
||||
toDisposeOnRelease.push(Disposable.create(() => clearTimeout(timer)));
|
||||
}),
|
||||
this.start({
|
||||
lsPath,
|
||||
daemonAddress: {
|
||||
hostname: 'localhost',
|
||||
port,
|
||||
instance: 1, // TODO: get it from the backend
|
||||
},
|
||||
clangdPath,
|
||||
log: currentSketchPath ? currentSketchPath : log,
|
||||
board: {
|
||||
fqbn: fqbnWithConfig,
|
||||
name,
|
||||
},
|
||||
realTimeDiagnostics,
|
||||
silentOutput: true,
|
||||
jobs,
|
||||
}),
|
||||
this.commandService.executeCommand<string>(
|
||||
'arduino.languageserver.start',
|
||||
{
|
||||
lsPath,
|
||||
cliDaemonAddr: `localhost:${port}`,
|
||||
clangdPath,
|
||||
log: currentSketchPath ? currentSketchPath : log,
|
||||
cliDaemonInstance: '1',
|
||||
board: {
|
||||
fqbn: fqbnWithConfig,
|
||||
name: name ? `"${name}"` : undefined,
|
||||
},
|
||||
realTimeDiagnostics,
|
||||
silentOutput: true,
|
||||
}
|
||||
),
|
||||
]);
|
||||
} catch (e) {
|
||||
console.log(`Failed to start language server. Original FQBN: ${fqbn}`, e);
|
||||
@@ -293,28 +222,4 @@ export class InoLanguage extends SketchContribution {
|
||||
release();
|
||||
}
|
||||
}
|
||||
// The Theia preference UI validation is bogus.
|
||||
// To restrict the number of jobs to a valid value.
|
||||
private getAsyncWorkersPreferenceSafe(): number {
|
||||
const jobs = this.preferences.get(
|
||||
'arduino.language.asyncWorkers',
|
||||
defaultAsyncWorkers
|
||||
);
|
||||
if (jobs < minAsyncWorkers) {
|
||||
return minAsyncWorkers;
|
||||
}
|
||||
if (jobs > maxAsyncWorkers) {
|
||||
return maxAsyncWorkers;
|
||||
}
|
||||
return jobs;
|
||||
}
|
||||
|
||||
private async start(
|
||||
params: StartLanguageServerParams
|
||||
): Promise<StartLanguageServerResult | undefined> {
|
||||
return this.commandService.executeCommand<StartLanguageServerResult>(
|
||||
'arduino.languageserver.start',
|
||||
params
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -3,12 +3,10 @@ import { NavigatableWidget } from '@theia/core/lib/browser/navigatable';
|
||||
import { Saveable } from '@theia/core/lib/browser/saveable';
|
||||
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { ApplicationError } from '@theia/core/lib/common/application-error';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { WorkspaceInput } from '@theia/workspace/lib/browser/workspace-service';
|
||||
import { SketchesError } from '../../common/protocol';
|
||||
import { StartupTasks } from '../../electron-common/startup-task';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { CurrentSketch } from '../sketches-service-client-impl';
|
||||
@@ -37,29 +35,7 @@ export class SaveAsSketch extends CloudSketchContribution {
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(SaveAsSketch.Commands.SAVE_AS_SKETCH, {
|
||||
execute: async (args) => {
|
||||
try {
|
||||
return await this.saveAs(args);
|
||||
} catch (err) {
|
||||
let message = String(err);
|
||||
if (ApplicationError.is(err)) {
|
||||
if (SketchesError.SketchAlreadyContainsThisFile.is(err)) {
|
||||
message = nls.localize(
|
||||
'arduino/sketch/sketchAlreadyContainsThisFileMessage',
|
||||
'Failed to save sketch "{0}" as "{1}". {2}',
|
||||
err.data.sourceSketchName,
|
||||
err.data.targetSketchName,
|
||||
err.message
|
||||
);
|
||||
} else {
|
||||
message = err.message;
|
||||
}
|
||||
} else if (err instanceof Error) {
|
||||
message = err.message;
|
||||
}
|
||||
this.messageService.error(message);
|
||||
}
|
||||
},
|
||||
execute: (args) => this.saveAs(args),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -82,14 +58,13 @@ export class SaveAsSketch extends CloudSketchContribution {
|
||||
* Resolves `true` if the sketch was successfully saved as something.
|
||||
*/
|
||||
private async saveAs(
|
||||
params = SaveAsSketch.Options.DEFAULT
|
||||
): Promise<boolean> {
|
||||
const {
|
||||
{
|
||||
execOnlyIfTemp,
|
||||
openAfterMove,
|
||||
wipeOriginal,
|
||||
markAsRecentlyOpened,
|
||||
} = params;
|
||||
}: SaveAsSketch.Options = SaveAsSketch.Options.DEFAULT
|
||||
): Promise<boolean> {
|
||||
assertConnectedToBackend({
|
||||
connectionStatusService: this.connectionStatusService,
|
||||
messageService: this.messageService,
|
||||
|
@@ -19,18 +19,16 @@ export class SelectedBoard extends Contribution {
|
||||
private readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
|
||||
override onStart(): void {
|
||||
this.boardsServiceProvider.onBoardListDidChange((boardList) =>
|
||||
this.update(boardList)
|
||||
this.boardsServiceProvider.onBoardListDidChange(() =>
|
||||
this.update(this.boardsServiceProvider.boardList)
|
||||
);
|
||||
}
|
||||
|
||||
override onReady(): void {
|
||||
this.boardsServiceProvider.ready.then(() => this.update());
|
||||
this.update(this.boardsServiceProvider.boardList);
|
||||
}
|
||||
|
||||
private update(
|
||||
boardList: BoardList = this.boardsServiceProvider.boardList
|
||||
): void {
|
||||
private update(boardList: BoardList): void {
|
||||
const { selectedBoard, selectedPort } = boardList.boardsConfig;
|
||||
this.statusBar.setElement('arduino-selected-board', {
|
||||
alignment: StatusBarAlignment.RIGHT,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { HostedPluginSupport } from '../hosted/hosted-plugin-support';
|
||||
import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||
import type { ArduinoState } from 'vscode-arduino-api';
|
||||
import {
|
||||
BoardsService,
|
||||
@@ -21,10 +21,7 @@ import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import { CurrentSketch } from '../sketches-service-client-impl';
|
||||
import { SketchContribution } from './contribution';
|
||||
|
||||
/**
|
||||
* (non-API) exported for tests
|
||||
*/
|
||||
export interface UpdateStateParams<T extends ArduinoState = ArduinoState> {
|
||||
interface UpdateStateParams<T extends ArduinoState> {
|
||||
readonly key: keyof T;
|
||||
readonly value: T[keyof T];
|
||||
}
|
||||
@@ -68,13 +65,10 @@ export class UpdateArduinoState extends SketchContribution {
|
||||
this.updateCompileSummary(args[0]);
|
||||
}
|
||||
}),
|
||||
this.boardsDataStore.onDidChange((event) => {
|
||||
this.boardsDataStore.onChanged((fqbn) => {
|
||||
const selectedFqbn =
|
||||
this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn;
|
||||
if (
|
||||
selectedFqbn &&
|
||||
event.changes.find((change) => change.fqbn === selectedFqbn)
|
||||
) {
|
||||
if (selectedFqbn && fqbn.includes(selectedFqbn)) {
|
||||
this.updateBoardDetails(selectedFqbn);
|
||||
}
|
||||
}),
|
||||
@@ -82,9 +76,7 @@ export class UpdateArduinoState extends SketchContribution {
|
||||
}
|
||||
|
||||
override onReady(): void {
|
||||
this.boardsServiceProvider.ready.then(() => {
|
||||
this.updateBoardsConfig(this.boardsServiceProvider.boardsConfig);
|
||||
});
|
||||
this.updateBoardsConfig(this.boardsServiceProvider.boardsConfig); // TODO: verify!
|
||||
this.updateSketchPath(this.sketchServiceClient.tryGetCurrentSketch());
|
||||
this.updateUserDirPath(this.configService.tryGetSketchDirUri());
|
||||
this.updateDataDirPath(this.configService.tryGetDataDirUri());
|
||||
|
@@ -45,7 +45,10 @@ export namespace UploadFirmware {
|
||||
export namespace Commands {
|
||||
export const OPEN: Command = {
|
||||
id: 'arduino-upload-firmware-open',
|
||||
label: nls.localize('arduino/firmware/updater', 'Firmware Updater'),
|
||||
label: nls.localize(
|
||||
'arduino/firmware/updater',
|
||||
'Firmware Updater'
|
||||
),
|
||||
category: 'Arduino',
|
||||
};
|
||||
}
|
||||
|
@@ -1,8 +1,7 @@
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { FQBN } from 'fqbn';
|
||||
import { CoreService } from '../../common/protocol';
|
||||
import { CoreService, sanitizeFqbn } from '../../common/protocol';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { CurrentSketch } from '../sketches-service-client-impl';
|
||||
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
|
||||
@@ -127,7 +126,6 @@ export class UploadSketch extends CoreServiceContribution {
|
||||
usingProgrammer,
|
||||
verifyOptions
|
||||
);
|
||||
|
||||
if (!uploadOptions) {
|
||||
return;
|
||||
}
|
||||
@@ -138,37 +136,10 @@ export class UploadSketch extends CoreServiceContribution {
|
||||
|
||||
const uploadResponse = await this.doWithProgress({
|
||||
progressText: nls.localize('arduino/sketch/uploading', 'Uploading...'),
|
||||
task: async (progressId, coreService, token) => {
|
||||
try {
|
||||
return await coreService.upload(
|
||||
{ ...uploadOptions, progressId },
|
||||
token
|
||||
);
|
||||
} catch (err) {
|
||||
if (err.code === 4005) {
|
||||
const uploadWithProgrammerOptions = await this.uploadOptions(
|
||||
true,
|
||||
verifyOptions
|
||||
);
|
||||
if (uploadWithProgrammerOptions) {
|
||||
return coreService.upload(
|
||||
{ ...uploadWithProgrammerOptions, progressId },
|
||||
token
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
},
|
||||
task: (progressId, coreService) =>
|
||||
coreService.upload({ ...uploadOptions, progressId }),
|
||||
keepOutput: true,
|
||||
cancelable: true,
|
||||
});
|
||||
|
||||
if (!uploadResponse) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the port update is NOOP if nothing has changed
|
||||
this.boardsServiceProvider.updateConfig(uploadResponse.portAfterUpload);
|
||||
|
||||
@@ -201,11 +172,7 @@ export class UploadSketch extends CoreServiceContribution {
|
||||
const [fqbn, { selectedProgrammer: programmer }, verify, verbose] =
|
||||
await Promise.all([
|
||||
verifyOptions.fqbn, // already decorated FQBN
|
||||
this.boardsDataStore.getData(
|
||||
verifyOptions.fqbn
|
||||
? new FQBN(verifyOptions.fqbn).toString(true)
|
||||
: undefined
|
||||
),
|
||||
this.boardsDataStore.getData(sanitizeFqbn(verifyOptions.fqbn)),
|
||||
this.preferences.get('arduino.upload.verify'),
|
||||
this.preferences.get('arduino.upload.verbose'),
|
||||
]);
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { BoardUserField, CoreError } from '../../common/protocol';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import { UserFieldsDialog } from '../dialogs/user-fields/user-fields-dialog';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { Contribution, MenuModelRegistry } from './contribution';
|
||||
import { MenuModelRegistry, Contribution } from './contribution';
|
||||
import { UploadSketch } from './upload-sketch';
|
||||
|
||||
@injectable()
|
||||
@@ -21,11 +21,12 @@ export class UserFields extends Contribution {
|
||||
|
||||
protected override init(): void {
|
||||
super.init();
|
||||
this.boardsServiceProvider.onBoardsConfigDidChange(() => this.refresh());
|
||||
}
|
||||
|
||||
override onReady(): void {
|
||||
this.boardsServiceProvider.ready.then(() => this.refresh());
|
||||
this.boardsServiceProvider.onBoardsConfigDidChange(async () => {
|
||||
const userFields =
|
||||
await this.boardsServiceProvider.selectedBoardUserFields();
|
||||
this.boardRequiresUserFields = userFields.length > 0;
|
||||
this.menuManager.update();
|
||||
});
|
||||
}
|
||||
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
@@ -36,13 +37,6 @@ export class UserFields extends Contribution {
|
||||
});
|
||||
}
|
||||
|
||||
private async refresh(): Promise<void> {
|
||||
const userFields =
|
||||
await this.boardsServiceProvider.selectedBoardUserFields();
|
||||
this.boardRequiresUserFields = userFields.length > 0;
|
||||
this.menuManager.update();
|
||||
}
|
||||
|
||||
private selectedFqbnAddress(): string | undefined {
|
||||
const { boardsConfig } = this.boardsServiceProvider;
|
||||
const fqbn = boardsConfig.selectedBoard?.fqbn;
|
||||
|
@@ -1,18 +1,18 @@
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import type { CoreService } from '../../common/protocol';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { CurrentSketch } from '../sketches-service-client-impl';
|
||||
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
|
||||
import {
|
||||
CoreServiceContribution,
|
||||
Command,
|
||||
CommandRegistry,
|
||||
CoreServiceContribution,
|
||||
KeybindingRegistry,
|
||||
MenuModelRegistry,
|
||||
KeybindingRegistry,
|
||||
TabBarToolbarRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { CurrentSketch } from '../sketches-service-client-impl';
|
||||
import { CoreService } from '../../common/protocol';
|
||||
import { CoreErrorHandler } from './core-error-handler';
|
||||
|
||||
export interface VerifySketchParams {
|
||||
@@ -131,15 +131,11 @@ export class VerifySketch extends CoreServiceContribution {
|
||||
'arduino/sketch/compile',
|
||||
'Compiling sketch...'
|
||||
),
|
||||
task: (progressId, coreService, token) =>
|
||||
coreService.compile(
|
||||
{
|
||||
...options,
|
||||
progressId,
|
||||
},
|
||||
token
|
||||
),
|
||||
cancelable: true,
|
||||
task: (progressId, coreService) =>
|
||||
coreService.compile({
|
||||
...options,
|
||||
progressId,
|
||||
}),
|
||||
});
|
||||
this.messageService.info(
|
||||
nls.localize('arduino/sketch/doneCompiling', 'Done compiling.'),
|
||||
|
@@ -179,8 +179,7 @@ export class CreateApi {
|
||||
);
|
||||
})
|
||||
.catch((reason) => {
|
||||
if (reason?.status === 404)
|
||||
return [] as Create.Resource[]; // TODO: must not swallow 404
|
||||
if (reason?.status === 404) return [] as Create.Resource[];
|
||||
else throw reason;
|
||||
});
|
||||
}
|
||||
@@ -487,12 +486,18 @@ export class CreateApi {
|
||||
await this.run(url, init, ResponseResultProvider.NOOP);
|
||||
}
|
||||
|
||||
private fetchCounter = 0;
|
||||
private async run<T>(
|
||||
requestInfo: URL,
|
||||
init: RequestInit | undefined,
|
||||
resultProvider: ResponseResultProvider = ResponseResultProvider.JSON
|
||||
): Promise<T> {
|
||||
const fetchCount = `[${++this.fetchCounter}]`;
|
||||
const fetchStart = performance.now();
|
||||
const method = init?.method ? `${init.method}: ` : '';
|
||||
const url = requestInfo.toString();
|
||||
const response = await fetch(requestInfo.toString(), init);
|
||||
const fetchEnd = performance.now();
|
||||
if (!response.ok) {
|
||||
let details: string | undefined = undefined;
|
||||
try {
|
||||
@@ -503,25 +508,28 @@ export class CreateApi {
|
||||
const { statusText, status } = response;
|
||||
throw new CreateError(statusText, status, details);
|
||||
}
|
||||
const parseStart = performance.now();
|
||||
const result = await resultProvider(response);
|
||||
const parseEnd = performance.now();
|
||||
console.debug(
|
||||
`HTTP ${fetchCount} ${method}${url} [fetch: ${(
|
||||
fetchEnd - fetchStart
|
||||
).toFixed(2)} ms, parse: ${(parseEnd - parseStart).toFixed(
|
||||
2
|
||||
)} ms] body: ${
|
||||
typeof result === 'string' ? result : JSON.stringify(result)
|
||||
}`
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
private async headers(): Promise<Record<string, string>> {
|
||||
const token = await this.token();
|
||||
const headers: Record<string, string> = {
|
||||
return {
|
||||
'content-type': 'application/json',
|
||||
accept: 'application/json',
|
||||
authorization: `Bearer ${token}`,
|
||||
};
|
||||
|
||||
const sharedSpaceID =
|
||||
this.arduinoPreferences['arduino.cloud.sharedSpaceID'];
|
||||
if (sharedSpaceID) {
|
||||
headers['x-organization'] = sharedSpaceID;
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
private domain(apiVersion = 'v2'): string {
|
||||
|
@@ -82,13 +82,6 @@ export function isNotFound(err: unknown): err is NotFoundError {
|
||||
return isErrorWithStatusOf(err, 404);
|
||||
}
|
||||
|
||||
export type UnprocessableContentError = CreateError & { status: 422 };
|
||||
export function isUnprocessableContent(
|
||||
err: unknown
|
||||
): err is UnprocessableContentError {
|
||||
return isErrorWithStatusOf(err, 422);
|
||||
}
|
||||
|
||||
function isErrorWithStatusOf(
|
||||
err: unknown,
|
||||
status: number
|
||||
|
@@ -69,7 +69,6 @@ export const CertificateUploaderComponent = ({
|
||||
const onItemSelect = React.useCallback(
|
||||
(item: BoardOptionValue | null) => {
|
||||
if (!item) {
|
||||
setSelectedItem(null);
|
||||
return;
|
||||
}
|
||||
const board = item.board;
|
||||
|
@@ -1,9 +1,8 @@
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import React from '@theia/core/shared/react';
|
||||
import {
|
||||
boardListItemEquals,
|
||||
type BoardList,
|
||||
type BoardListItemWithBoard,
|
||||
import type {
|
||||
BoardList,
|
||||
BoardListItemWithBoard,
|
||||
} from '../../../common/protocol/board-list';
|
||||
import { ArduinoSelect } from '../../widgets/arduino-select';
|
||||
|
||||
@@ -76,9 +75,7 @@ export const SelectBoardComponent = ({
|
||||
setSelectOptions(boardOptions);
|
||||
|
||||
if (selectedItem) {
|
||||
selBoard = updatableBoards.findIndex((board) =>
|
||||
boardListItemEquals(board, selectedItem)
|
||||
);
|
||||
selBoard = updatableBoards.indexOf(selectedItem);
|
||||
}
|
||||
|
||||
selectOption(boardOptions[selBoard] || null);
|
||||
|
@@ -104,7 +104,6 @@ export const FirmwareUploaderComponent = ({
|
||||
const onItemSelect = React.useCallback(
|
||||
(item: BoardListItemWithBoard | null) => {
|
||||
if (!item) {
|
||||
setSelectedItem(null);
|
||||
return;
|
||||
}
|
||||
const board = item.board;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { DisposableCollection, Emitter, Event } from '@theia/core';
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { HostedPluginSupport } from './hosted-plugin-support';
|
||||
import { HostedPluginSupport } from './theia/plugin-ext/hosted-plugin';
|
||||
|
||||
/**
|
||||
* Frontend contribution to watch VS Code extension start/stop events from Theia.
|
@@ -1,14 +0,0 @@
|
||||
import type { Event } from '@theia/core/lib/common/event';
|
||||
|
||||
/*
|
||||
This implementation hides the default HostedPluginSupport implementation from Theia to be able to test it.
|
||||
Otherwise, the default implementation fails at require time due to the `import.meta` in the Theia plugin worker code.
|
||||
https://github.com/eclipse-theia/theia/blob/964f69ca3b3a5fb87ffa0177fb300b74ba0ca39f/packages/plugin-ext/src/hosted/browser/plugin-worker.ts#L30-L32
|
||||
*/
|
||||
|
||||
export const HostedPluginSupport = Symbol('HostedPluginSupport');
|
||||
export interface HostedPluginSupport {
|
||||
readonly didStart: Promise<void>;
|
||||
readonly onDidLoad: Event<void>;
|
||||
readonly onDidCloseConnection: Event<void>;
|
||||
}
|
@@ -12,13 +12,15 @@ import {
|
||||
LibrarySearch,
|
||||
LibraryService,
|
||||
} from '../../common/protocol/library-service';
|
||||
import { ListWidget } from '../widgets/component-list/list-widget';
|
||||
import {
|
||||
ListWidget,
|
||||
UserAbortError,
|
||||
} from '../widgets/component-list/list-widget';
|
||||
import { Installable } from '../../common/protocol';
|
||||
import { ListItemRenderer } from '../widgets/component-list/list-item-renderer';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { LibraryFilterRenderer } from '../widgets/component-list/filter-renderer';
|
||||
import { findChildTheiaButton, splitByBoldTag } from '../utils/dom';
|
||||
import { UserAbortError } from '../../common/protocol/progressible';
|
||||
|
||||
@injectable()
|
||||
export class LibraryListWidget extends ListWidget<
|
||||
|
@@ -46,7 +46,7 @@ export class NotificationCenter
|
||||
new Emitter<ProgressMessage>();
|
||||
private readonly indexUpdateDidFailEmitter =
|
||||
new Emitter<IndexUpdateDidFailParams>();
|
||||
private readonly daemonDidStartEmitter = new Emitter<number>();
|
||||
private readonly daemonDidStartEmitter = new Emitter<string>();
|
||||
private readonly daemonDidStopEmitter = new Emitter<void>();
|
||||
private readonly configDidChangeEmitter = new Emitter<ConfigState>();
|
||||
private readonly platformDidInstallEmitter = new Emitter<{
|
||||
@@ -136,7 +136,7 @@ export class NotificationCenter
|
||||
this.indexUpdateDidFailEmitter.fire(params);
|
||||
}
|
||||
|
||||
notifyDaemonDidStart(port: number): void {
|
||||
notifyDaemonDidStart(port: string): void {
|
||||
this.daemonDidStartEmitter.fire(port);
|
||||
}
|
||||
|
||||
|
@@ -67,7 +67,6 @@ export class SketchesServiceClientImpl
|
||||
);
|
||||
|
||||
private _currentSketch: CurrentSketch | undefined;
|
||||
private _currentIdeTempFolderUri: URI | undefined;
|
||||
private currentSketchLoaded = new Deferred<CurrentSketch>();
|
||||
|
||||
onStart(): void {
|
||||
@@ -75,10 +74,7 @@ export class SketchesServiceClientImpl
|
||||
this.watchSketchbookDir(sketchDirUri);
|
||||
const refreshCurrentSketch = async () => {
|
||||
const currentSketch = await this.loadCurrentSketch();
|
||||
const ideTempFolderUri = await this.getIdeTempFolderUriForSketch(
|
||||
currentSketch
|
||||
);
|
||||
this.useCurrentSketch(currentSketch, ideTempFolderUri);
|
||||
this.useCurrentSketch(currentSketch);
|
||||
};
|
||||
this.toDispose.push(
|
||||
this.configService.onDidChangeSketchDirUri((sketchDirUri) => {
|
||||
@@ -145,10 +141,7 @@ export class SketchesServiceClientImpl
|
||||
}
|
||||
|
||||
if (!Sketch.sameAs(this._currentSketch, reloadedSketch)) {
|
||||
const ideTempFolderUri = await this.getIdeTempFolderUriForSketch(
|
||||
reloadedSketch
|
||||
);
|
||||
this.useCurrentSketch(reloadedSketch, ideTempFolderUri, true);
|
||||
this.useCurrentSketch(reloadedSketch, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -186,23 +179,11 @@ export class SketchesServiceClientImpl
|
||||
]);
|
||||
}
|
||||
|
||||
private async getIdeTempFolderUriForSketch(
|
||||
sketch: CurrentSketch
|
||||
): Promise<URI | undefined> {
|
||||
if (CurrentSketch.isValid(sketch)) {
|
||||
const uri = await this.sketchesService.getIdeTempFolderUri(sketch);
|
||||
return new URI(uri);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private useCurrentSketch(
|
||||
currentSketch: CurrentSketch,
|
||||
ideTempFolderUri: URI | undefined,
|
||||
reassignPromise = false
|
||||
) {
|
||||
this._currentSketch = currentSketch;
|
||||
this._currentIdeTempFolderUri = ideTempFolderUri;
|
||||
if (reassignPromise) {
|
||||
this.currentSketchLoaded = new Deferred();
|
||||
}
|
||||
@@ -292,14 +273,6 @@ export class SketchesServiceClientImpl
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
this._currentIdeTempFolderUri &&
|
||||
this._currentIdeTempFolderUri.resolve('launch.json').toString() ===
|
||||
toCheck.toString()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isCloudSketch = toCheck
|
||||
.toString()
|
||||
.includes(`${REMOTE_SKETCHBOOK_FOLDER}/${ARDUINO_CLOUD_FOLDER}`);
|
||||
|
@@ -38,9 +38,7 @@
|
||||
.arduino-select__control.arduino-select__control--menu-is-open {
|
||||
border: 1px solid !important;
|
||||
border-color: var(--theia-focusBorder) !important;
|
||||
border-bottom-color: var(
|
||||
--theia-sideBar-background
|
||||
) !important; /* if the bottom border color has the same color as the background of the control, we make the border "invisible" */
|
||||
border-bottom-color: var(--theia-sideBar-background) !important; /* if the bottom border color has the same color as the background of the control, we make the border "invisible" */
|
||||
}
|
||||
|
||||
.arduino-select__value-container .arduino-select__single-value {
|
||||
|
@@ -1,324 +1,324 @@
|
||||
#select-board-dialog-container > .dialogBlock {
|
||||
width: 640px;
|
||||
height: 500px;
|
||||
width: 640px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
div#select-board-dialog {
|
||||
margin: 5px;
|
||||
height: 100%;
|
||||
margin: 5px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
div#select-board-dialog .selectBoardContainer {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
overflow: hidden;
|
||||
max-height: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
overflow: hidden;
|
||||
max-height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.select-board-dialog .head {
|
||||
margin: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.dialogContent.select-board-dialog {
|
||||
height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
div.dialogContent.select-board-dialog > div.head .title {
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.02em;
|
||||
font-size: 1.2em;
|
||||
color: var(--theia-editorWidget-foreground);
|
||||
margin-bottom: 10px;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.02em;
|
||||
font-size: 1.2em;
|
||||
color: var(--theia-editorWidget-foreground);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
|
||||
div#select-board-dialog .selectBoardContainer .list .item.selected {
|
||||
background: var(--theia-secondaryButton-hoverBackground);
|
||||
background: var(--theia-secondaryButton-hoverBackground);
|
||||
}
|
||||
|
||||
div#select-board-dialog .selectBoardContainer .list .item.selected i {
|
||||
color: var(--theia-arduino-branding-primary);
|
||||
color: var(--theia-arduino-branding-primary);
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .search,
|
||||
#select-board-dialog .selectBoardContainer .search input,
|
||||
#select-board-dialog .selectBoardContainer .list,
|
||||
#select-board-dialog .selectBoardContainer .list {
|
||||
background: var(--theia-editor-background);
|
||||
background: var(--theia-editor-background);
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .search input {
|
||||
border: none;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: 37px;
|
||||
padding: 10px 5px 10px 10px;
|
||||
margin: 0;
|
||||
vertical-align: top;
|
||||
display: flex;
|
||||
color: var(--theia-input-foreground);
|
||||
border: none;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: 37px;
|
||||
padding: 10px 5px 10px 10px;
|
||||
margin: 0;
|
||||
vertical-align: top;
|
||||
display: flex;
|
||||
color: var(--theia-input-foreground);
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .search input:focus {
|
||||
box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .container {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
max-height: 100%;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .container .content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .left.container .content {
|
||||
margin: 0 5px 0 0;
|
||||
margin: 0 5px 0 0;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .right.container .content {
|
||||
margin: 0 0 0 5px;
|
||||
margin: 0 0 0 5px;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .container .content .title {
|
||||
color: var(--theia-editorWidget-foreground);
|
||||
padding: 0px 0px 10px 0px;
|
||||
text-transform: uppercase;
|
||||
color: var(--theia-editorWidget-foreground);
|
||||
padding: 0px 0px 10px 0px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .container .content .footer {
|
||||
padding: 10px 5px 10px 0px;
|
||||
padding: 10px 5px 10px 0px;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .container .content .loading {
|
||||
font-size: var(--theia-ui-font-size1);
|
||||
color: var(--theia-editorWidget-foreground);
|
||||
padding: 10px 5px 10px 10px;
|
||||
text-transform: uppercase;
|
||||
/* The max, min-height comes from `.list` 200px + 47px top padding - 2 * 10px top padding */
|
||||
max-height: 227px;
|
||||
min-height: 227px;
|
||||
font-size: var(--theia-ui-font-size1);
|
||||
color: var(--theia-editorWidget-foreground);
|
||||
padding: 10px 5px 10px 10px;
|
||||
text-transform: uppercase;
|
||||
/* The max, min-height comes from `.list` 200px + 47px top padding - 2 * 10px top padding */
|
||||
max-height: 227px;
|
||||
min-height: 227px;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .list .item {
|
||||
padding: 10px 5px 10px 10px;
|
||||
display: flex;
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
flex: 1 0;
|
||||
padding: 10px 5px 10px 10px;
|
||||
display: flex;
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
flex: 1 0;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .list .item .selected-icon {
|
||||
margin-left: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .list .item .details {
|
||||
font-size: var(--theia-ui-font-size1);
|
||||
opacity: var(--theia-mod-disabled-opacity);
|
||||
width: 155px; /* used heuristics for the calculation */
|
||||
white-space: pre;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: var(--theia-ui-font-size1);
|
||||
opacity: var(--theia-mod-disabled-opacity);
|
||||
width: 155px; /* used heuristics for the calculation */
|
||||
white-space: pre;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .list .item.missing {
|
||||
opacity: var(--theia-mod-disabled-opacity);
|
||||
opacity: var(--theia-mod-disabled-opacity);
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .list .item:hover {
|
||||
background: var(--theia-secondaryButton-hoverBackground);
|
||||
background: var(--theia-secondaryButton-hoverBackground);
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .list .label {
|
||||
white-space: pre;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: pre;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .list {
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .ports.list {
|
||||
margin: 47px 0px 0px 0px; /* 47 is 37 as input height for the `Boards`, plus 10 margin bottom. */
|
||||
margin: 47px 0px 0px 0px; /* 47 is 37 as input height for the `Boards`, plus 10 margin bottom. */
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .search {
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 5px;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.arduino-boards-toolbar-item-container {
|
||||
align-items: center;
|
||||
background: var(--theia-arduino-toolbar-dropdown-background);
|
||||
border-radius: 1px;
|
||||
color: var(--theia-arduino-toolbar-dropdown-label);
|
||||
border: 1px solid var(--theia-arduino-toolbar-dropdown-border);
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
height: var(--arduino-button-height);
|
||||
margin: 0 4px;
|
||||
overflow: hidden;
|
||||
padding: 0 10px;
|
||||
width: 210px;
|
||||
align-items: center;
|
||||
background: var(--theia-arduino-toolbar-dropdown-background);
|
||||
border-radius: 1px;
|
||||
color: var(--theia-arduino-toolbar-dropdown-label);
|
||||
border: 1px solid var(--theia-arduino-toolbar-dropdown-border);
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
height: var(--arduino-button-height);
|
||||
margin: 0 4px;
|
||||
overflow: hidden;
|
||||
padding: 0 10px;
|
||||
width: 210px;
|
||||
}
|
||||
|
||||
.arduino-boards-toolbar-item--protocol,
|
||||
.arduino-boards-dropdown-item--protocol {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
font-size: 16px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.arduino-boards-toolbar-item--protocol,
|
||||
.arduino-boards-dropdown-item--protocol {
|
||||
color: var(--theia-arduino-toolbar-dropdown-label);
|
||||
color: var(--theia-arduino-toolbar-dropdown-label);
|
||||
}
|
||||
|
||||
.arduino-boards-toolbar-item-container .arduino-boards-toolbar-item {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.arduino-boards-toolbar-item--label {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.arduino-boards-toolbar-item--label-connected {
|
||||
font-family: "Open Sans Bold";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
font-family: 'Open Sans Bold';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.arduino-boards-toolbar-item-container .caret {
|
||||
width: 10px;
|
||||
margin-right: 5px;
|
||||
width: 10px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-list {
|
||||
margin: -1px;
|
||||
z-index: 1;
|
||||
border: 1px solid var(--theia-arduino-toolbar-dropdown-border);
|
||||
font-family: "Open Sans";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
margin: -1px;
|
||||
z-index: 1;
|
||||
border: 1px solid var(--theia-arduino-toolbar-dropdown-border);
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-list:focus {
|
||||
border: 1px solid var(--theia-arduino-toolbar-dropdown-borderActive);
|
||||
border: 1px solid var(--theia-arduino-toolbar-dropdown-borderActive);
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-list--items-container {
|
||||
overflow: auto;
|
||||
max-height: 404px;
|
||||
background: var(--theia-arduino-toolbar-dropdown-background);
|
||||
overflow: auto;
|
||||
max-height: 404px;
|
||||
background: var(--theia-arduino-toolbar-dropdown-background);
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-list--items-container::-webkit-scrollbar {
|
||||
background: var(--theia-arduino-toolbar-dropdown-background);
|
||||
background: var(--theia-arduino-toolbar-dropdown-background);
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item {
|
||||
background: var(--theia-arduino-toolbar-dropdown-background);
|
||||
color: var(--theia-arduino-toolbar-dropdown-label);
|
||||
cursor: default;
|
||||
display: flex;
|
||||
font-size: var(--theia-ui-font-size1);
|
||||
justify-content: space-between;
|
||||
padding: 10px;
|
||||
background: var(--theia-arduino-toolbar-dropdown-background);
|
||||
color: var(--theia-arduino-toolbar-dropdown-label);
|
||||
cursor: default;
|
||||
display: flex;
|
||||
font-size: var(--theia-ui-font-size1);
|
||||
justify-content: space-between;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item--board-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item--label {
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Redefine default codicon size https://github.com/microsoft/vscode/commit/38cd0a377b7abef34fb07fe770fc633e68819ba6 */
|
||||
.arduino-boards-dropdown-item .codicon[class*="codicon-"] {
|
||||
font-size: 14px;
|
||||
.arduino-boards-dropdown-item .codicon[class*='codicon-'] {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item .p-TabBar-toolbar {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
flex-direction: column;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item .p-TabBar-toolbar .item {
|
||||
margin: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item .p-TabBar-toolbar .item .action-label {
|
||||
padding: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item--board-label {
|
||||
font-size: 14px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item .arduino-boards-dropdown-item--protocol {
|
||||
margin-right: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item--port-label {
|
||||
font-size: 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item:hover {
|
||||
background: var(--theia-arduino-toolbar-dropdown-option-backgroundHover);
|
||||
background: var(--theia-arduino-toolbar-dropdown-option-backgroundHover);
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item--selected,
|
||||
.arduino-boards-dropdown-item--selected:hover {
|
||||
background: var(--theia-arduino-toolbar-dropdown-option-backgroundSelected);
|
||||
border: 1px solid
|
||||
var(--theia-arduino-toolbar-dropdown-option-backgroundSelected);
|
||||
background: var(--theia-arduino-toolbar-dropdown-option-backgroundSelected);
|
||||
border: 1px solid var(--theia-arduino-toolbar-dropdown-option-backgroundSelected);
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item--selected
|
||||
.arduino-boards-dropdown-item--port-label {
|
||||
color: var(--theia-arduino-toolbar-dropdown-label);
|
||||
.arduino-boards-dropdown-item--port-label {
|
||||
color: var(--theia-arduino-toolbar-dropdown-label);
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item--selected .fa {
|
||||
color: var(--theia-arduino-toolbar-dropdown-iconSelected);
|
||||
color: var(--theia-arduino-toolbar-dropdown-iconSelected);
|
||||
}
|
||||
|
||||
.arduino-board-dropdown-footer {
|
||||
color: var(--theia-secondaryButton-foreground);
|
||||
border-top: 1px solid var(--theia-dropdown-border);
|
||||
color: var(--theia-secondaryButton-foreground);
|
||||
border-top: 1px solid var(--theia-dropdown-border);
|
||||
}
|
||||
|
||||
@media only screen and (max-height: 400px) {
|
||||
div.dialogContent.select-board-dialog > div.head {
|
||||
display: none;
|
||||
}
|
||||
div.dialogContent.select-board-dialog > div.head {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .container .content .title {
|
||||
display: none;
|
||||
}
|
||||
#select-board-dialog .selectBoardContainer .container .content .title {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#select-board-dialog .no-result {
|
||||
text-transform: uppercase;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
padding: 10px 5px;
|
||||
overflow-wrap: break-word;
|
||||
text-transform: uppercase;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
padding: 10px 5px;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
@@ -12,4 +12,4 @@
|
||||
|
||||
.p-MenuBar-item.p-mod-active {
|
||||
color: var(--theia-menubar-selectionForeground);
|
||||
}
|
||||
}
|
@@ -1,75 +1,76 @@
|
||||
#certificate-uploader-dialog-container > .dialogBlock {
|
||||
width: 600px;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.certificate-uploader-dialog .theia-select {
|
||||
border: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
.certificate-uploader-dialog .arduino-select__control {
|
||||
height: 31px;
|
||||
background: var(--theia-dropdown-background) !important;
|
||||
height: 31px;
|
||||
background: var(--theia-dropdown-background) !important;
|
||||
}
|
||||
|
||||
.certificate-uploader-dialog .dialogRow > button {
|
||||
margin-right: 3px;
|
||||
.certificate-uploader-dialog .dialogRow > button{
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.certificate-uploader-dialog .certificate-list {
|
||||
border: 1px solid var(--theia-editorWidget-border);
|
||||
border-radius: 2px;
|
||||
color: var(--theia-editor-foreground);
|
||||
background-color: var(--theia-editor-background);
|
||||
overflow: auto;
|
||||
height: 120px;
|
||||
flex: 1;
|
||||
border: 1px solid var(--theia-editorWidget-border);
|
||||
border-radius: 2px;;
|
||||
color: var(--theia-editor-foreground);
|
||||
background-color: var(--theia-editor-background);
|
||||
overflow: auto;
|
||||
height: 120px;
|
||||
flex: 1;
|
||||
}
|
||||
.certificate-uploader-dialog .certificate-list .certificate-row {
|
||||
display: flex;
|
||||
padding: 6px 10px 5px 10px;
|
||||
display: flex;
|
||||
padding: 6px 10px 5px 10px
|
||||
}
|
||||
.certificate-uploader-dialog .certificate-list .certificate-row:hover {
|
||||
background-color: var(--theia-list-activeSelectionBackground);
|
||||
background-color: var(--theia-list-activeSelectionBackground);
|
||||
}
|
||||
|
||||
.certificate-uploader-dialog .upload-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
.certificate-uploader-dialog .success {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #1da086;
|
||||
.certificate-uploader-dialog .success {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #1DA086;
|
||||
}
|
||||
|
||||
.certificate-uploader-dialog .warn {
|
||||
color: #c11f09;
|
||||
.certificate-uploader-dialog .warn {
|
||||
color: #C11F09;
|
||||
}
|
||||
|
||||
.certificate-uploader-dialog .status-icon {
|
||||
margin-right: 10px;
|
||||
.certificate-uploader-dialog .status-icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.certificate-uploader-dialog .add-cert-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.certificate-uploader-dialog .add-cert-btn .caret {
|
||||
margin-left: 6px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.certificate-add {
|
||||
padding: 16px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid var(--theia-editorWidget-border);
|
||||
color: var(--theia-editorWidget-foreground);
|
||||
background-color: var(--theia-editorWidget-background);
|
||||
padding: 16px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid var(--theia-editorWidget-border);
|
||||
color: var(--theia-editorWidget-foreground);
|
||||
background-color: var(--theia-editorWidget-background);
|
||||
}
|
||||
|
||||
.certificate-add input {
|
||||
margin-top: 12px;
|
||||
padding: 0 12px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-top: 12px;
|
||||
padding: 0 12px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
}
|
||||
|
@@ -23,7 +23,8 @@
|
||||
-webkit-mask-size: 100%;
|
||||
}
|
||||
|
||||
.p-mod-current .cloud-sketchbook-tree-icon {
|
||||
.p-mod-current
|
||||
.cloud-sketchbook-tree-icon {
|
||||
background-color: var(--theia-foreground);
|
||||
-webkit-mask: url(../icons/arduino-cloud-filled.svg);
|
||||
-webkit-mask-position: center;
|
||||
@@ -32,49 +33,49 @@
|
||||
}
|
||||
|
||||
.sketchbook-trees-container
|
||||
.p-TabBar[data-orientation="horizontal"]
|
||||
> .p-TabBar-content {
|
||||
.p-TabBar[data-orientation="horizontal"]
|
||||
> .p-TabBar-content {
|
||||
justify-content: center;
|
||||
border-bottom: 1px solid var(--theia-tree-indentGuidesStroke);
|
||||
}
|
||||
|
||||
.sketchbook-trees-container
|
||||
.p-Widget.p-TabBar.p-DockPanel-tabBar
|
||||
> ul
|
||||
> li.p-TabBar-tab
|
||||
> div.p-TabBar-tabLabel {
|
||||
.p-Widget.p-TabBar.p-DockPanel-tabBar
|
||||
> ul
|
||||
> li.p-TabBar-tab
|
||||
> div.p-TabBar-tabLabel {
|
||||
display: none;
|
||||
width: 0px;
|
||||
max-width: 0px;
|
||||
}
|
||||
|
||||
.sketchbook-trees-container
|
||||
.p-Widget.p-TabBar.p-DockPanel-tabBar
|
||||
> ul
|
||||
> li.p-TabBar-tab
|
||||
> div.p-TabBar-tabCloseIcon {
|
||||
.p-Widget.p-TabBar.p-DockPanel-tabBar
|
||||
> ul
|
||||
> li.p-TabBar-tab
|
||||
> div.p-TabBar-tabCloseIcon {
|
||||
display: none;
|
||||
width: 0px;
|
||||
max-width: 0px;
|
||||
}
|
||||
|
||||
.sketchbook-trees-container
|
||||
.p-TabBar[data-orientation="horizontal"]
|
||||
.p-TabBar-tab {
|
||||
.p-TabBar[data-orientation="horizontal"]
|
||||
.p-TabBar-tab {
|
||||
min-width: 55px;
|
||||
}
|
||||
|
||||
.sketchbook-trees-container
|
||||
.p-Widget.p-TabBar.p-DockPanel-tabBar
|
||||
> ul
|
||||
> li.p-TabBar-tab {
|
||||
.p-Widget.p-TabBar.p-DockPanel-tabBar
|
||||
> ul
|
||||
> li.p-TabBar-tab {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.sketchbook-trees-container
|
||||
.p-Widget.p-TabBar.p-DockPanel-tabBar
|
||||
> ul
|
||||
> li.p-TabBar-tab.p-mod-current {
|
||||
.p-Widget.p-TabBar.p-DockPanel-tabBar
|
||||
> ul
|
||||
> li.p-TabBar-tab.p-mod-current {
|
||||
border-bottom: 2px solid var(--theia-activityBar-activeBorder);
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
.codicon-debug-alt:before {
|
||||
font-family: "FontAwesome" !important;
|
||||
content: "\e905" !important;
|
||||
}
|
||||
.codicon-debug-alt:before {
|
||||
font-family: 'FontAwesome' !important;
|
||||
content: "\e905" !important
|
||||
}
|
@@ -1,13 +1,36 @@
|
||||
/* Naive way of hiding the debug widget when the debug functionality is disabled https://github.com/arduino/arduino-ide/issues/14 */
|
||||
.theia-debug-container .debug-toolbar.hidden,
|
||||
.theia-debug-container .theia-session-container.hidden {
|
||||
visibility: hidden;
|
||||
/* TODO: remove after https://github.com/eclipse-theia/theia/pull/9256/ */
|
||||
|
||||
/* To fix colors in Theia. */
|
||||
.theia-debug-hover-title.number,
|
||||
.theia-debug-console-variable.number {
|
||||
color: var(--theia-variable-number-variable-color);
|
||||
}
|
||||
.theia-debug-hover-title.boolean,
|
||||
.theia-debug-console-variable.boolean {
|
||||
color: var(--theia-variable-boolean-variable-color);
|
||||
}
|
||||
.theia-debug-hover-title.string,
|
||||
.theia-debug-console-variable.string {
|
||||
color: var(--theia-variable-string-variable-color);
|
||||
}
|
||||
|
||||
.theia-debug-container .status-message {
|
||||
font-family: "Open Sans";
|
||||
font-style: normal;
|
||||
font-size: 12px;
|
||||
|
||||
padding: 10px;
|
||||
/* To unset the default debug hover dimension. */
|
||||
.theia-debug-hover {
|
||||
min-width: unset;
|
||||
min-height: unset;
|
||||
width: unset;
|
||||
height: unset;
|
||||
}
|
||||
|
||||
/* To adjust the left padding in the hover title. */
|
||||
.theia-debug-hover-title {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
/* Use the default Theia dimensions only iff the expression is complex (`!!expression.hasChildren~) */
|
||||
.theia-debug-hover.complex-value {
|
||||
min-width: 324px;
|
||||
min-height: 324px;
|
||||
width: 324px;
|
||||
height: 324px;
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@
|
||||
total = padding + margin = 96px
|
||||
*/
|
||||
max-width: calc(100% - 96px) !important;
|
||||
|
||||
|
||||
min-width: 424px;
|
||||
max-height: 560px;
|
||||
padding: 0 var(--arduino-button-height);
|
||||
@@ -56,23 +56,14 @@
|
||||
}
|
||||
|
||||
.p-Widget.dialogOverlay .dialogControl .spinner,
|
||||
.p-Widget.dialogOverlay
|
||||
.dialogBlock
|
||||
.dialogContent
|
||||
.dialogSection
|
||||
.dialogRow
|
||||
.spinner {
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogContent .dialogSection .dialogRow .spinner {
|
||||
background: var(--theia-icon-loading) center center no-repeat;
|
||||
animation: theia-spin 1.25s linear infinite;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.p-Widget.dialogOverlay
|
||||
.dialogBlock
|
||||
.dialogContent
|
||||
.dialogSection
|
||||
.dialogRow:first-child {
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogContent .dialogSection .dialogRow:first-child {
|
||||
margin-top: 0px;
|
||||
height: 32px;
|
||||
}
|
||||
@@ -87,7 +78,7 @@
|
||||
}
|
||||
|
||||
.fa.disabled {
|
||||
opacity: 0.4;
|
||||
opacity: .4;
|
||||
}
|
||||
|
||||
@media only screen and (max-height: 560px) {
|
||||
|
@@ -1,9 +1,7 @@
|
||||
/* Show the dirty indicator on unclosable widgets. On hover, it should still show the dot instead of the X. */
|
||||
/* https://github.com/arduino/arduino-pro-ide/issues/380 */
|
||||
.p-TabBar.theia-app-centers
|
||||
.p-TabBar-tab.p-mod-closable.a-mod-uncloseable.theia-mod-dirty
|
||||
> .p-TabBar-tabCloseIcon:before {
|
||||
content: "\ea71";
|
||||
.p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable.a-mod-uncloseable.theia-mod-dirty > .p-TabBar-tabCloseIcon:before {
|
||||
content: "\ea71";
|
||||
}
|
||||
|
||||
.monaco-list-row.show-file-icons.focused {
|
||||
|
@@ -1,31 +1,31 @@
|
||||
#firmware-uploader-dialog-container > .dialogBlock {
|
||||
width: 600px;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.firmware-uploader-dialog .theia-select {
|
||||
border: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
.firmware-uploader-dialog .arduino-select__control {
|
||||
height: 31px;
|
||||
background: var(--theia-input-background) !important;
|
||||
height: 31px;
|
||||
background: var(--theia-input-background) !important;
|
||||
}
|
||||
|
||||
.firmware-uploader-dialog .dialogRow > button {
|
||||
margin-right: 3px;
|
||||
.firmware-uploader-dialog .dialogRow > button{
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.firmware-uploader-dialog #firmware-select {
|
||||
flex: unset;
|
||||
flex: unset;
|
||||
}
|
||||
|
||||
.firmware-uploader-dialog .success {
|
||||
color: #1da086;
|
||||
.firmware-uploader-dialog .success {
|
||||
color: #1DA086;
|
||||
}
|
||||
|
||||
.firmware-uploader-dialog .warn {
|
||||
color: #c11f09;
|
||||
.firmware-uploader-dialog .warn {
|
||||
color: #C11F09;
|
||||
}
|
||||
|
||||
.firmware-uploader-dialog .status-icon {
|
||||
margin-right: 10px;
|
||||
.firmware-uploader-dialog .status-icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
@@ -1,698 +1,699 @@
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
src: url("fonts/OpenSans-Regular-webfont.woff") format("woff");
|
||||
font-family: 'Open Sans';
|
||||
src: url('fonts/OpenSans-Regular-webfont.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Open Sans Bold";
|
||||
src: url("fonts/OpenSans-Bold-webfont.woff") format("woff");
|
||||
font-family: 'Open Sans Bold';
|
||||
src: url('fonts/OpenSans-Bold-webfont.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "FontAwesome";
|
||||
src: url("fonts/FontAwesome.ttf?h959em") format("truetype"),
|
||||
url("fonts/FontAwesome.woff?h959em") format("woff"),
|
||||
url("fonts/FontAwesome.svg?h959em#FontAwesome") format("svg");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
font-family: 'FontAwesome';
|
||||
src:
|
||||
url('fonts/FontAwesome.ttf?h959em') format('truetype'),
|
||||
url('fonts/FontAwesome.woff?h959em') format('woff'),
|
||||
url('fonts/FontAwesome.svg?h959em#FontAwesome') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
.fa {
|
||||
/* use !important to prevent issues with browser extensions that change fonts */
|
||||
font-family: "FontAwesome" !important;
|
||||
speak: never;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
/* use !important to prevent issues with browser extensions that change fonts */
|
||||
font-family: 'FontAwesome' !important;
|
||||
speak: never;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
|
||||
/* Better Font Rendering =========== */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
/* Better Font Rendering =========== */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.fa-arduino-verify:before {
|
||||
content: "\e90b";
|
||||
content: "\e90b";
|
||||
}
|
||||
.fa-arduino-upload:before {
|
||||
content: "\e90c";
|
||||
content: "\e90c";
|
||||
}
|
||||
.fa-arduino-monitor:before {
|
||||
content: "\e90d";
|
||||
content: "\e90d";
|
||||
}
|
||||
.fa-arduino-sketch-tabs-menu:before {
|
||||
content: "\e90e";
|
||||
content: "\e90e";
|
||||
}
|
||||
.fa-arduino-plotter:before {
|
||||
content: "\e90f";
|
||||
content: "\e90f";
|
||||
}
|
||||
.fa-fa-check:before {
|
||||
content: "\e90a";
|
||||
content: "\e90a";
|
||||
}
|
||||
.fa-arduino-technology-3dimensionscube:before {
|
||||
content: "\e906";
|
||||
content: "\e906";
|
||||
}
|
||||
.fa-arduino-technology-usb:before {
|
||||
content: "\e907";
|
||||
content: "\e907";
|
||||
}
|
||||
.fa-arduino-technology-connection:before {
|
||||
content: "\e908";
|
||||
content: "\e908";
|
||||
}
|
||||
.fa-arduino-technology-bluetooth:before {
|
||||
content: "\e909";
|
||||
content: "\e909";
|
||||
}
|
||||
.fa-arduino-debugger:before {
|
||||
content: "\e905";
|
||||
content: "\e905";
|
||||
}
|
||||
.fa-arduino-search:before {
|
||||
content: "\e901";
|
||||
content: "\e901";
|
||||
}
|
||||
.fa-arduino-boards:before {
|
||||
content: "\e902";
|
||||
content: "\e902";
|
||||
}
|
||||
.fa-arduino-library:before {
|
||||
content: "\e903";
|
||||
content: "\e903";
|
||||
}
|
||||
.fa-arduino-folder:before {
|
||||
content: "\e904";
|
||||
content: "\e904";
|
||||
}
|
||||
.fa-reload:before {
|
||||
content: "\e900";
|
||||
content: "\e900";
|
||||
}
|
||||
.fa-asterisk:before {
|
||||
content: "\f069";
|
||||
content: "\f069";
|
||||
}
|
||||
.fa-plus:before {
|
||||
content: "\f067";
|
||||
content: "\f067";
|
||||
}
|
||||
.fa-question:before {
|
||||
content: "\f128";
|
||||
content: "\f128";
|
||||
}
|
||||
.fa-minus:before {
|
||||
content: "\f068";
|
||||
content: "\f068";
|
||||
}
|
||||
.fa-music:before {
|
||||
content: "\f001";
|
||||
content: "\f001";
|
||||
}
|
||||
.fa-search:before {
|
||||
content: "\f002";
|
||||
content: "\f002";
|
||||
}
|
||||
.fa-envelope-o:before {
|
||||
content: "\f003";
|
||||
content: "\f003";
|
||||
}
|
||||
.fa-heart:before {
|
||||
content: "\f004";
|
||||
content: "\f004";
|
||||
}
|
||||
.fa-star:before {
|
||||
content: "\f005";
|
||||
content: "\f005";
|
||||
}
|
||||
.fa-star-o:before {
|
||||
content: "\f006";
|
||||
content: "\f006";
|
||||
}
|
||||
.fa-user:before {
|
||||
content: "\f007";
|
||||
content: "\f007";
|
||||
}
|
||||
.fa-film:before {
|
||||
content: "\f008";
|
||||
content: "\f008";
|
||||
}
|
||||
.fa-th-large:before {
|
||||
content: "\f009";
|
||||
content: "\f009";
|
||||
}
|
||||
.fa-th:before {
|
||||
content: "\f00a";
|
||||
content: "\f00a";
|
||||
}
|
||||
.fa-th-list:before {
|
||||
content: "\f00b";
|
||||
content: "\f00b";
|
||||
}
|
||||
.fa-close:before {
|
||||
content: "\f00d";
|
||||
content: "\f00d";
|
||||
}
|
||||
.fa-remove:before {
|
||||
content: "\f00d";
|
||||
content: "\f00d";
|
||||
}
|
||||
.fa-times:before {
|
||||
content: "\f00d";
|
||||
content: "\f00d";
|
||||
}
|
||||
.fa-search-plus:before {
|
||||
content: "\f00e";
|
||||
content: "\f00e";
|
||||
}
|
||||
.fa-search-minus:before {
|
||||
content: "\f010";
|
||||
content: "\f010";
|
||||
}
|
||||
.fa-power-off:before {
|
||||
content: "\f011";
|
||||
content: "\f011";
|
||||
}
|
||||
.fa-signal:before {
|
||||
content: "\f012";
|
||||
content: "\f012";
|
||||
}
|
||||
.fa-cog:before {
|
||||
content: "\f013";
|
||||
content: "\f013";
|
||||
}
|
||||
.fa-gear:before {
|
||||
content: "\f013";
|
||||
content: "\f013";
|
||||
}
|
||||
.fa-trash-o:before {
|
||||
content: "\f014";
|
||||
content: "\f014";
|
||||
}
|
||||
.fa-home:before {
|
||||
content: "\f015";
|
||||
content: "\f015";
|
||||
}
|
||||
.fa-file-o:before {
|
||||
content: "\f016";
|
||||
content: "\f016";
|
||||
}
|
||||
.fa-clock-o:before {
|
||||
content: "\f017";
|
||||
content: "\f017";
|
||||
}
|
||||
.fa-download:before {
|
||||
content: "\f019";
|
||||
content: "\f019";
|
||||
}
|
||||
.fa-arrow-circle-o-down:before {
|
||||
content: "\f01a";
|
||||
content: "\f01a";
|
||||
}
|
||||
.fa-arrow-circle-o-up:before {
|
||||
content: "\f01b";
|
||||
content: "\f01b";
|
||||
}
|
||||
.fa-inbox:before {
|
||||
content: "\f01c";
|
||||
content: "\f01c";
|
||||
}
|
||||
.fa-play-circle-o:before {
|
||||
content: "\f01d";
|
||||
content: "\f01d";
|
||||
}
|
||||
.fa-repeat:before {
|
||||
content: "\f01e";
|
||||
content: "\f01e";
|
||||
}
|
||||
.fa-rotate-right:before {
|
||||
content: "\f01e";
|
||||
content: "\f01e";
|
||||
}
|
||||
.fa-refresh:before {
|
||||
content: "\f021";
|
||||
content: "\f021";
|
||||
}
|
||||
.fa-list-alt:before {
|
||||
content: "\f022";
|
||||
content: "\f022";
|
||||
}
|
||||
.fa-lock:before {
|
||||
content: "\f023";
|
||||
content: "\f023";
|
||||
}
|
||||
.fa-volume-off:before {
|
||||
content: "\f026";
|
||||
content: "\f026";
|
||||
}
|
||||
.fa-volume-down:before {
|
||||
content: "\f027";
|
||||
content: "\f027";
|
||||
}
|
||||
.fa-volume-up:before {
|
||||
content: "\f028";
|
||||
content: "\f028";
|
||||
}
|
||||
.fa-qrcode:before {
|
||||
content: "\f029";
|
||||
content: "\f029";
|
||||
}
|
||||
.fa-tag:before {
|
||||
content: "\f02b";
|
||||
content: "\f02b";
|
||||
}
|
||||
.fa-tags:before {
|
||||
content: "\f02c";
|
||||
content: "\f02c";
|
||||
}
|
||||
.fa-book:before {
|
||||
content: "\f02d";
|
||||
content: "\f02d";
|
||||
}
|
||||
.fa-print:before {
|
||||
content: "\f02f";
|
||||
content: "\f02f";
|
||||
}
|
||||
.fa-text-height:before {
|
||||
content: "\f034";
|
||||
content: "\f034";
|
||||
}
|
||||
.fa-text-width:before {
|
||||
content: "\f035";
|
||||
content: "\f035";
|
||||
}
|
||||
.fa-align-left:before {
|
||||
content: "\f036";
|
||||
content: "\f036";
|
||||
}
|
||||
.fa-align-center:before {
|
||||
content: "\f037";
|
||||
content: "\f037";
|
||||
}
|
||||
.fa-align-right:before {
|
||||
content: "\f038";
|
||||
content: "\f038";
|
||||
}
|
||||
.fa-align-justify:before {
|
||||
content: "\f039";
|
||||
content: "\f039";
|
||||
}
|
||||
.fa-list:before {
|
||||
content: "\f03a";
|
||||
content: "\f03a";
|
||||
}
|
||||
.fa-dedent:before {
|
||||
content: "\f03b";
|
||||
content: "\f03b";
|
||||
}
|
||||
.fa-outdent:before {
|
||||
content: "\f03b";
|
||||
content: "\f03b";
|
||||
}
|
||||
.fa-indent:before {
|
||||
content: "\f03c";
|
||||
content: "\f03c";
|
||||
}
|
||||
.fa-pencil:before {
|
||||
content: "\f040";
|
||||
content: "\f040";
|
||||
}
|
||||
.fa-adjust:before {
|
||||
content: "\f042";
|
||||
content: "\f042";
|
||||
}
|
||||
.fa-edit:before {
|
||||
content: "\f044";
|
||||
content: "\f044";
|
||||
}
|
||||
.fa-pencil-square-o:before {
|
||||
content: "\f044";
|
||||
content: "\f044";
|
||||
}
|
||||
.fa-share-square-o:before {
|
||||
content: "\f045";
|
||||
content: "\f045";
|
||||
}
|
||||
.fa-check-square-o:before {
|
||||
content: "\f046";
|
||||
content: "\f046";
|
||||
}
|
||||
.fa-arrows:before {
|
||||
content: "\f047";
|
||||
content: "\f047";
|
||||
}
|
||||
.fa-step-backward:before {
|
||||
content: "\f048";
|
||||
content: "\f048";
|
||||
}
|
||||
.fa-fast-backward:before {
|
||||
content: "\f049";
|
||||
content: "\f049";
|
||||
}
|
||||
.fa-backward:before {
|
||||
content: "\f04a";
|
||||
content: "\f04a";
|
||||
}
|
||||
.fa-play:before {
|
||||
content: "\f04b";
|
||||
content: "\f04b";
|
||||
}
|
||||
.fa-pause:before {
|
||||
content: "\f04c";
|
||||
content: "\f04c";
|
||||
}
|
||||
.fa-stop:before {
|
||||
content: "\f04d";
|
||||
content: "\f04d";
|
||||
}
|
||||
.fa-forward:before {
|
||||
content: "\f04e";
|
||||
content: "\f04e";
|
||||
}
|
||||
.fa-fast-forward:before {
|
||||
content: "\f050";
|
||||
content: "\f050";
|
||||
}
|
||||
.fa-step-forward:before {
|
||||
content: "\f051";
|
||||
content: "\f051";
|
||||
}
|
||||
.fa-eject:before {
|
||||
content: "\f052";
|
||||
content: "\f052";
|
||||
}
|
||||
.fa-chevron-left:before {
|
||||
content: "\f053";
|
||||
content: "\f053";
|
||||
}
|
||||
.fa-chevron-right:before {
|
||||
content: "\f054";
|
||||
content: "\f054";
|
||||
}
|
||||
.fa-plus-circle:before {
|
||||
content: "\f055";
|
||||
content: "\f055";
|
||||
}
|
||||
.fa-minus-circle:before {
|
||||
content: "\f056";
|
||||
content: "\f056";
|
||||
}
|
||||
.fa-times-circle:before {
|
||||
content: "\f057";
|
||||
content: "\f057";
|
||||
}
|
||||
.fa-check-circle:before {
|
||||
content: "\f058";
|
||||
content: "\f058";
|
||||
}
|
||||
.fa-question-circle:before {
|
||||
content: "\f059";
|
||||
content: "\f059";
|
||||
}
|
||||
.fa-info-circle:before {
|
||||
content: "\f05a";
|
||||
content: "\f05a";
|
||||
}
|
||||
.fa-crosshairs:before {
|
||||
content: "\f05b";
|
||||
content: "\f05b";
|
||||
}
|
||||
.fa-times-circle-o:before {
|
||||
content: "\f05c";
|
||||
content: "\f05c";
|
||||
}
|
||||
.fa-check-circle-o:before {
|
||||
content: "\f05d";
|
||||
content: "\f05d";
|
||||
}
|
||||
.fa-ban:before {
|
||||
content: "\f05e";
|
||||
content: "\f05e";
|
||||
}
|
||||
.fa-arrow-left:before {
|
||||
content: "\f060";
|
||||
content: "\f060";
|
||||
}
|
||||
.fa-arrow-right:before {
|
||||
content: "\f061";
|
||||
content: "\f061";
|
||||
}
|
||||
.fa-arrow-up:before {
|
||||
content: "\f062";
|
||||
content: "\f062";
|
||||
}
|
||||
.fa-arrow-down:before {
|
||||
content: "\f063";
|
||||
content: "\f063";
|
||||
}
|
||||
.fa-mail-forward:before {
|
||||
content: "\f064";
|
||||
content: "\f064";
|
||||
}
|
||||
.fa-share:before {
|
||||
content: "\f064";
|
||||
content: "\f064";
|
||||
}
|
||||
.fa-expand:before {
|
||||
content: "\f065";
|
||||
content: "\f065";
|
||||
}
|
||||
.fa-compress:before {
|
||||
content: "\f066";
|
||||
content: "\f066";
|
||||
}
|
||||
.fa-exclamation-circle:before {
|
||||
content: "\f06a";
|
||||
content: "\f06a";
|
||||
}
|
||||
.fa-eye:before {
|
||||
content: "\f06e";
|
||||
content: "\f06e";
|
||||
}
|
||||
.fa-eye-slash:before {
|
||||
content: "\f070";
|
||||
content: "\f070";
|
||||
}
|
||||
.fa-exclamation-triangle:before {
|
||||
content: "\f071";
|
||||
content: "\f071";
|
||||
}
|
||||
.fa-warning:before {
|
||||
content: "\f071";
|
||||
content: "\f071";
|
||||
}
|
||||
.fa-calendar:before {
|
||||
content: "\f073";
|
||||
content: "\f073";
|
||||
}
|
||||
.fa-random:before {
|
||||
content: "\f074";
|
||||
content: "\f074";
|
||||
}
|
||||
.fa-comment:before {
|
||||
content: "\f075";
|
||||
content: "\f075";
|
||||
}
|
||||
.fa-chevron-up:before {
|
||||
content: "\f077";
|
||||
content: "\f077";
|
||||
}
|
||||
.fa-chevron-down:before {
|
||||
content: "\f078";
|
||||
content: "\f078";
|
||||
}
|
||||
.fa-retweet:before {
|
||||
content: "\f079";
|
||||
content: "\f079";
|
||||
}
|
||||
.fa-folder:before {
|
||||
content: "\f07b";
|
||||
content: "\f07b";
|
||||
}
|
||||
.fa-folder-open:before {
|
||||
content: "\f07c";
|
||||
content: "\f07c";
|
||||
}
|
||||
.fa-arrows-v:before {
|
||||
content: "\f07d";
|
||||
content: "\f07d";
|
||||
}
|
||||
.fa-arrows-h:before {
|
||||
content: "\f07e";
|
||||
content: "\f07e";
|
||||
}
|
||||
.fa-cogs:before {
|
||||
content: "\f085";
|
||||
content: "\f085";
|
||||
}
|
||||
.fa-gears:before {
|
||||
content: "\f085";
|
||||
content: "\f085";
|
||||
}
|
||||
.fa-star-half:before {
|
||||
content: "\f089";
|
||||
content: "\f089";
|
||||
}
|
||||
.fa-heart-o:before {
|
||||
content: "\f08a";
|
||||
content: "\f08a";
|
||||
}
|
||||
.fa-sign-out:before {
|
||||
content: "\f08b";
|
||||
content: "\f08b";
|
||||
}
|
||||
.fa-thumb-tack:before {
|
||||
content: "\f08d";
|
||||
content: "\f08d";
|
||||
}
|
||||
.fa-external-link:before {
|
||||
content: "\f08e";
|
||||
content: "\f08e";
|
||||
}
|
||||
.fa-sign-in:before {
|
||||
content: "\f090";
|
||||
content: "\f090";
|
||||
}
|
||||
.fa-upload:before {
|
||||
content: "\f093";
|
||||
content: "\f093";
|
||||
}
|
||||
.fa-square-o:before {
|
||||
content: "\f096";
|
||||
content: "\f096";
|
||||
}
|
||||
.fa-bookmark-o:before {
|
||||
content: "\f097";
|
||||
content: "\f097";
|
||||
}
|
||||
.fa-hdd-o:before {
|
||||
content: "\f0a0";
|
||||
content: "\f0a0";
|
||||
}
|
||||
.fa-bell-o:before {
|
||||
content: "\f0a2";
|
||||
content: "\f0a2";
|
||||
}
|
||||
.fa-certificate:before {
|
||||
content: "\f0a3";
|
||||
content: "\f0a3";
|
||||
}
|
||||
.fa-arrow-circle-left:before {
|
||||
content: "\f0a8";
|
||||
content: "\f0a8";
|
||||
}
|
||||
.fa-arrow-circle-right:before {
|
||||
content: "\f0a9";
|
||||
content: "\f0a9";
|
||||
}
|
||||
.fa-arrow-circle-up:before {
|
||||
content: "\f0aa";
|
||||
content: "\f0aa";
|
||||
}
|
||||
.fa-arrow-circle-down:before {
|
||||
content: "\f0ab";
|
||||
content: "\f0ab";
|
||||
}
|
||||
.fa-wrench:before {
|
||||
content: "\f0ad";
|
||||
content: "\f0ad";
|
||||
}
|
||||
.fa-tasks:before {
|
||||
content: "\f0ae";
|
||||
content: "\f0ae";
|
||||
}
|
||||
.fa-filter:before {
|
||||
content: "\f0b0";
|
||||
content: "\f0b0";
|
||||
}
|
||||
.fa-briefcase:before {
|
||||
content: "\f0b1";
|
||||
content: "\f0b1";
|
||||
}
|
||||
.fa-arrows-alt:before {
|
||||
content: "\f0b2";
|
||||
content: "\f0b2";
|
||||
}
|
||||
.fa-cloud:before {
|
||||
content: "\f0c2";
|
||||
content: "\f0c2";
|
||||
}
|
||||
.fa-copy:before {
|
||||
content: "\f0c5";
|
||||
content: "\f0c5";
|
||||
}
|
||||
.fa-files-o:before {
|
||||
content: "\f0c5";
|
||||
content: "\f0c5";
|
||||
}
|
||||
.fa-floppy-o:before {
|
||||
content: "\f0c7";
|
||||
content: "\f0c7";
|
||||
}
|
||||
.fa-save:before {
|
||||
content: "\f0c7";
|
||||
content: "\f0c7";
|
||||
}
|
||||
.fa-square:before {
|
||||
content: "\f0c8";
|
||||
content: "\f0c8";
|
||||
}
|
||||
.fa-bars:before {
|
||||
content: "\f0c9";
|
||||
content: "\f0c9";
|
||||
}
|
||||
.fa-navicon:before {
|
||||
content: "\f0c9";
|
||||
content: "\f0c9";
|
||||
}
|
||||
.fa-reorder:before {
|
||||
content: "\f0c9";
|
||||
content: "\f0c9";
|
||||
}
|
||||
.fa-list-ul:before {
|
||||
content: "\f0ca";
|
||||
content: "\f0ca";
|
||||
}
|
||||
.fa-list-ol:before {
|
||||
content: "\f0cb";
|
||||
content: "\f0cb";
|
||||
}
|
||||
.fa-table:before {
|
||||
content: "\f0ce";
|
||||
content: "\f0ce";
|
||||
}
|
||||
.fa-caret-down:before {
|
||||
content: "\f0d7";
|
||||
content: "\f0d7";
|
||||
}
|
||||
.fa-caret-up:before {
|
||||
content: "\f0d8";
|
||||
content: "\f0d8";
|
||||
}
|
||||
.fa-caret-left:before {
|
||||
content: "\f0d9";
|
||||
content: "\f0d9";
|
||||
}
|
||||
.fa-caret-right:before {
|
||||
content: "\f0da";
|
||||
content: "\f0da";
|
||||
}
|
||||
.fa-columns:before {
|
||||
content: "\f0db";
|
||||
content: "\f0db";
|
||||
}
|
||||
.fa-sort:before {
|
||||
content: "\f0dc";
|
||||
content: "\f0dc";
|
||||
}
|
||||
.fa-unsorted:before {
|
||||
content: "\f0dc";
|
||||
content: "\f0dc";
|
||||
}
|
||||
.fa-sort-desc:before {
|
||||
content: "\f0dd";
|
||||
content: "\f0dd";
|
||||
}
|
||||
.fa-sort-down:before {
|
||||
content: "\f0dd";
|
||||
content: "\f0dd";
|
||||
}
|
||||
.fa-sort-asc:before {
|
||||
content: "\f0de";
|
||||
content: "\f0de";
|
||||
}
|
||||
.fa-sort-up:before {
|
||||
content: "\f0de";
|
||||
content: "\f0de";
|
||||
}
|
||||
.fa-rotate-left:before {
|
||||
content: "\f0e2";
|
||||
content: "\f0e2";
|
||||
}
|
||||
.fa-undo:before {
|
||||
content: "\f0e2";
|
||||
content: "\f0e2";
|
||||
}
|
||||
.fa-file-text-o:before {
|
||||
content: "\f0f6";
|
||||
content: "\f0f6";
|
||||
}
|
||||
.fa-plus-square:before {
|
||||
content: "\f0fe";
|
||||
content: "\f0fe";
|
||||
}
|
||||
.fa-angle-double-left:before {
|
||||
content: "\f100";
|
||||
content: "\f100";
|
||||
}
|
||||
.fa-angle-double-right:before {
|
||||
content: "\f101";
|
||||
content: "\f101";
|
||||
}
|
||||
.fa-angle-double-up:before {
|
||||
content: "\f102";
|
||||
content: "\f102";
|
||||
}
|
||||
.fa-angle-double-down:before {
|
||||
content: "\f103";
|
||||
content: "\f103";
|
||||
}
|
||||
.fa-angle-left:before {
|
||||
content: "\f104";
|
||||
content: "\f104";
|
||||
}
|
||||
.fa-angle-right:before {
|
||||
content: "\f105";
|
||||
content: "\f105";
|
||||
}
|
||||
.fa-angle-up:before {
|
||||
content: "\f106";
|
||||
content: "\f106";
|
||||
}
|
||||
.fa-angle-down:before {
|
||||
content: "\f107";
|
||||
content: "\f107";
|
||||
}
|
||||
.fa-circle-o:before {
|
||||
content: "\f10c";
|
||||
content: "\f10c";
|
||||
}
|
||||
.fa-spinner:before {
|
||||
content: "\f110";
|
||||
content: "\f110";
|
||||
}
|
||||
.fa-circle:before {
|
||||
content: "\f111";
|
||||
content: "\f111";
|
||||
}
|
||||
.fa-mail-reply:before {
|
||||
content: "\f112";
|
||||
content: "\f112";
|
||||
}
|
||||
.fa-reply:before {
|
||||
content: "\f112";
|
||||
content: "\f112";
|
||||
}
|
||||
.fa-folder-o:before {
|
||||
content: "\f114";
|
||||
content: "\f114";
|
||||
}
|
||||
.fa-folder-open-o:before {
|
||||
content: "\f115";
|
||||
content: "\f115";
|
||||
}
|
||||
.fa-keyboard-o:before {
|
||||
content: "\f11c";
|
||||
content: "\f11c";
|
||||
}
|
||||
.fa-terminal:before {
|
||||
content: "\f120";
|
||||
content: "\f120";
|
||||
}
|
||||
.fa-code:before {
|
||||
content: "\f121";
|
||||
content: "\f121";
|
||||
}
|
||||
.fa-mail-reply-all:before {
|
||||
content: "\f122";
|
||||
content: "\f122";
|
||||
}
|
||||
.fa-reply-all:before {
|
||||
content: "\f122";
|
||||
content: "\f122";
|
||||
}
|
||||
.fa-star-half-empty:before {
|
||||
content: "\f123";
|
||||
content: "\f123";
|
||||
}
|
||||
.fa-star-half-full:before {
|
||||
content: "\f123";
|
||||
content: "\f123";
|
||||
}
|
||||
.fa-star-half-o:before {
|
||||
content: "\f123";
|
||||
content: "\f123";
|
||||
}
|
||||
.fa-crop:before {
|
||||
content: "\f125";
|
||||
content: "\f125";
|
||||
}
|
||||
.fa-code-fork:before {
|
||||
content: "\f126";
|
||||
content: "\f126";
|
||||
}
|
||||
.fa-chain-broken:before {
|
||||
content: "\f127";
|
||||
content: "\f127";
|
||||
}
|
||||
.fa-unlink:before {
|
||||
content: "\f127";
|
||||
content: "\f127";
|
||||
}
|
||||
.fa-info:before {
|
||||
content: "\f129";
|
||||
content: "\f129";
|
||||
}
|
||||
.fa-exclamation:before {
|
||||
content: "\f12a";
|
||||
content: "\f12a";
|
||||
}
|
||||
.fa-rocket:before {
|
||||
content: "\f135";
|
||||
content: "\f135";
|
||||
}
|
||||
.fa-maxcdn:before {
|
||||
content: "\f136";
|
||||
content: "\f136";
|
||||
}
|
||||
.fa-chevron-circle-left:before {
|
||||
content: "\f137";
|
||||
content: "\f137";
|
||||
}
|
||||
.fa-chevron-circle-right:before {
|
||||
content: "\f138";
|
||||
content: "\f138";
|
||||
}
|
||||
.fa-chevron-circle-up:before {
|
||||
content: "\f139";
|
||||
content: "\f139";
|
||||
}
|
||||
.fa-chevron-circle-down:before {
|
||||
content: "\f13a";
|
||||
content: "\f13a";
|
||||
}
|
||||
.fa-ellipsis-h:before {
|
||||
content: "\f141";
|
||||
content: "\f141";
|
||||
}
|
||||
.fa-long-arrow-down:before {
|
||||
content: "\f175";
|
||||
content: "\f175";
|
||||
}
|
||||
.fa-long-arrow-up:before {
|
||||
content: "\f176";
|
||||
content: "\f176";
|
||||
}
|
||||
.fa-long-arrow-left:before {
|
||||
content: "\f177";
|
||||
content: "\f177";
|
||||
}
|
||||
.fa-long-arrow-right:before {
|
||||
content: "\f178";
|
||||
content: "\f178";
|
||||
}
|
||||
.fa-microchip:before {
|
||||
content: "\f2db";
|
||||
content: "\f2db";
|
||||
}
|
||||
.fa-arduino-cloud-download:before {
|
||||
content: "\e910";
|
||||
content: "\e910";
|
||||
}
|
||||
.fa-arduino-cloud-upload:before {
|
||||
content: "\e914";
|
||||
content: "\e914";
|
||||
}
|
||||
.fa-arduino-cloud:before {
|
||||
content: "\e915";
|
||||
content: "\e915";
|
||||
}
|
||||
.fa-arduino-cloud-filled:before {
|
||||
content: "\e912";
|
||||
content: "\e912";
|
||||
}
|
||||
.fa-arduino-cloud-offline:before {
|
||||
content: "\e913";
|
||||
content: "\e913";
|
||||
}
|
||||
.fa-arduino-cloud-filled-offline:before {
|
||||
content: "\e911";
|
||||
content: "\e911";
|
||||
}
|
||||
|
@@ -1,124 +1,124 @@
|
||||
#ide-updater-dialog-container > .dialogBlock {
|
||||
width: 546px;
|
||||
width: 546px;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .bold {
|
||||
font-weight: bold;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.ide-updater-dialog--pre-download {
|
||||
display: flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.ide-updater-dialog--downloading {
|
||||
flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.ide-updater-dialog--logo-container {
|
||||
margin-right: var(--arduino-button-height);
|
||||
margin-right: var(--arduino-button-height);
|
||||
}
|
||||
|
||||
.ide-updater-dialog--logo {
|
||||
background: url("./ide-logo.png") round;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
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 {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
margin-top: 0;
|
||||
min-width: 0;
|
||||
.ide-updater-dialog--content
|
||||
.ide-updater-dialog--new-version-text.dialogSection {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
margin-top: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .changelog {
|
||||
color: var(--theia-editor-foreground);
|
||||
background-color: var(--theia-editor-background);
|
||||
font-size: 12px;
|
||||
overflow: auto;
|
||||
padding: 0 12px;
|
||||
cursor: text;
|
||||
width: 100%;
|
||||
color: var(--theia-editor-foreground);
|
||||
background-color: var(--theia-editor-background);
|
||||
font-size: 12px;
|
||||
overflow: auto;
|
||||
padding: 0 12px;
|
||||
cursor: text;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .changelog .fallback {
|
||||
min-height: 180px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
min-height: 180px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .changelog .fallback .spinner {
|
||||
align-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.dialogContent.ide-updater-dialog
|
||||
.ide-updater-dialog--content
|
||||
.ide-updater-dialog--new-version-text
|
||||
.dialogRow.changelog-container {
|
||||
align-items: flex-start;
|
||||
border: 1px solid var(--theia-editorWidget-border);
|
||||
border-radius: 2px;
|
||||
overflow: auto;
|
||||
max-height: 180px;
|
||||
.ide-updater-dialog--content
|
||||
.ide-updater-dialog--new-version-text
|
||||
.dialogRow.changelog-container {
|
||||
align-items: flex-start;
|
||||
border: 1px solid var(--theia-editorWidget-border);
|
||||
border-radius: 2px;
|
||||
overflow: auto;
|
||||
max-height: 180px;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .changelog a {
|
||||
color: var(--theia-textLink-foreground);
|
||||
color: var(--theia-textLink-foreground);
|
||||
}
|
||||
|
||||
.ide-updater-dialog .changelog a:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .changelog code {
|
||||
background: var(--theia-textBlockQuote-background);
|
||||
border-radius: 2px;
|
||||
padding: 0 2px;
|
||||
background: var(--theia-textBlockQuote-background);
|
||||
border-radius: 2px;
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .changelog a code {
|
||||
color: var(--theia-textLink-foreground);
|
||||
color: var(--theia-textLink-foreground);
|
||||
}
|
||||
|
||||
.ide-updater-dialog .buttons-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: var(--arduino-button-height);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: var(--arduino-button-height);
|
||||
}
|
||||
|
||||
.ide-updater-dialog .buttons-container a.theia-button {
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
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);
|
||||
color: var(--theia-button-foreground);
|
||||
}
|
||||
|
||||
.ide-updater-dialog .buttons-container .push {
|
||||
margin-right: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.ide-updater-dialog--content {
|
||||
max-height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
max-height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#ide-updater-dialog-container .skip-version-button {
|
||||
margin-left: 79px;
|
||||
margin-right: auto;
|
||||
margin-left: 79px;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
/* https://github.com/arduino/arduino-ide/pull/2027#issuecomment-1533174638 */
|
||||
/* https://github.com/eclipse-theia/theia/commit/1b5ff9ee459df14cedc0e8266dd02dae93fcd1bf#diff-d8d45a890507a01141c010ad4a6891edf2ae727cfa6dfe604cebbd667812cccbR68 */
|
||||
/* Use normal whitespace handling for the changelog content in the update dialog. */
|
||||
.p-Widget.dialogOverlay .dialogContent.ide-updater-dialog {
|
||||
white-space: normal;
|
||||
white-space: normal;
|
||||
}
|
||||
|
@@ -1,177 +1,175 @@
|
||||
@import "./list-widget.css";
|
||||
@import "./boards-config-dialog.css";
|
||||
@import "./main.css";
|
||||
@import "./dialogs.css";
|
||||
@import "./monitor.css";
|
||||
@import "./arduino-select.css";
|
||||
@import "./status-bar.css";
|
||||
@import "./terminal.css";
|
||||
@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";
|
||||
@import "./sketchbook.css";
|
||||
@import "./cloud-sketchbook.css";
|
||||
@import "./fonts.css";
|
||||
@import "./custom-codicon.css";
|
||||
@import "./progress-bar.css";
|
||||
@import "./settings-step-input.css";
|
||||
@import './list-widget.css';
|
||||
@import './boards-config-dialog.css';
|
||||
@import './main.css';
|
||||
@import './dialogs.css';
|
||||
@import './monitor.css';
|
||||
@import './arduino-select.css';
|
||||
@import './status-bar.css';
|
||||
@import './terminal.css';
|
||||
@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';
|
||||
@import './sketchbook.css';
|
||||
@import './cloud-sketchbook.css';
|
||||
@import './fonts.css';
|
||||
@import './custom-codicon.css';
|
||||
@import './progress-bar.css';
|
||||
@import './settings-step-input.css';
|
||||
|
||||
:root {
|
||||
--arduino-button-height: 28px;
|
||||
--arduino-button-height: 28px;
|
||||
}
|
||||
|
||||
/* Revive of the `--theia-icon-loading`. The variable has been removed from Theia while IDE2 still uses is. */
|
||||
/* The SVG icons are still part of Theia (1.31.1) */
|
||||
/* https://github.com/arduino/arduino-ide/pull/1662#issuecomment-1324997134 */
|
||||
body {
|
||||
--theia-icon-loading: url(../icons/loading-light.svg);
|
||||
--theia-icon-loading-warning: url(../icons/loading-dark.svg);
|
||||
--theia-icon-loading: url(../icons/loading-light.svg);
|
||||
--theia-icon-loading-warning: url(../icons/loading-dark.svg);
|
||||
}
|
||||
body.theia-dark {
|
||||
--theia-icon-loading: url(../icons/loading-dark.svg);
|
||||
--theia-icon-loading-warning: url(../icons/loading-light.svg);
|
||||
--theia-icon-loading: url(../icons/loading-dark.svg);
|
||||
--theia-icon-loading-warning: url(../icons/loading-light.svg);
|
||||
}
|
||||
|
||||
.theia-input.warning:focus {
|
||||
outline-width: 1px;
|
||||
outline-style: solid;
|
||||
outline-offset: -1px;
|
||||
opacity: 1 !important;
|
||||
color: var(--theia-warningForeground);
|
||||
background-color: var(--theia-warningBackground);
|
||||
outline-width: 1px;
|
||||
outline-style: solid;
|
||||
outline-offset: -1px;
|
||||
opacity: 1 !important;
|
||||
color: var(--theia-warningForeground);
|
||||
background-color: var(--theia-warningBackground);
|
||||
}
|
||||
|
||||
.theia-input.warning {
|
||||
background-color: var(--theia-warningBackground);
|
||||
background-color: var(--theia-warningBackground);
|
||||
}
|
||||
|
||||
.theia-input.warning::placeholder {
|
||||
color: var(--theia-warningForeground);
|
||||
background-color: var(--theia-warningBackground);
|
||||
color: var(--theia-warningForeground);
|
||||
background-color: var(--theia-warningBackground);
|
||||
}
|
||||
|
||||
.theia-input.error:focus {
|
||||
outline-width: 1px;
|
||||
outline-style: solid;
|
||||
outline-offset: -1px;
|
||||
opacity: 1 !important;
|
||||
color: var(--theia-errorForeground);
|
||||
background-color: var(--theia-errorBackground);
|
||||
outline-width: 1px;
|
||||
outline-style: solid;
|
||||
outline-offset: -1px;
|
||||
opacity: 1 !important;
|
||||
color: var(--theia-errorForeground);
|
||||
background-color: var(--theia-errorBackground);
|
||||
}
|
||||
|
||||
.theia-input.error {
|
||||
background-color: var(--theia-errorBackground);
|
||||
background-color: var(--theia-errorBackground);
|
||||
}
|
||||
|
||||
.theia-input.error::placeholder {
|
||||
color: var(--theia-errorForeground);
|
||||
background-color: var(--theia-errorBackground);
|
||||
color: var(--theia-errorForeground);
|
||||
background-color: var(--theia-errorBackground);
|
||||
}
|
||||
|
||||
/* Makes the sidepanel a bit wider when opening the widget */
|
||||
.p-DockPanel-widget {
|
||||
min-width: 220px;
|
||||
min-height: 20px;
|
||||
height: 220px;
|
||||
min-width: 220px;
|
||||
min-height: 20px;
|
||||
height: 220px;
|
||||
}
|
||||
|
||||
/* Overrule the default Theia CSS button styles. */
|
||||
button.theia-button,
|
||||
.theia-button {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
font-family: "Open Sans Bold", sans-serif;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
letter-spacing: 0.01em;
|
||||
line-height: 24px;
|
||||
outline: none;
|
||||
padding: 0 16px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
border-width: 2px;
|
||||
border-radius: 32px;
|
||||
text-transform: uppercase;
|
||||
transition: none;
|
||||
box-shadow: none;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
font-family: 'Open Sans Bold',sans-serif;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
letter-spacing: .01em;
|
||||
line-height: 24px;
|
||||
outline: none;
|
||||
padding: 0 16px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
border-width: 2px;
|
||||
border-radius: 32px;
|
||||
text-transform: uppercase;
|
||||
transition: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
button.theia-button {
|
||||
height: var(--arduino-button-height);
|
||||
max-width: none;
|
||||
height: var(--arduino-button-height);
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.theia-button:active,
|
||||
.theia-button:focus {
|
||||
box-shadow: 0 0 0 2px var(--theia-focusBorder);
|
||||
box-shadow: 0 0 0 2px var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
button.theia-button.secondary {
|
||||
border: 2px solid var(--theia-secondaryButton-foreground);
|
||||
border: 2px solid var(--theia-secondaryButton-foreground);
|
||||
}
|
||||
|
||||
button.theia-button[disabled],
|
||||
.theia-button[disabled] {
|
||||
opacity: 0.5;
|
||||
color: var(--theia-button-foreground);
|
||||
background-color: var(--theia-button-background);
|
||||
button.theia-button[disabled], .theia-button[disabled] {
|
||||
opacity: 0.5;
|
||||
color: var(--theia-button-foreground);
|
||||
background-color: var(--theia-button-background);
|
||||
}
|
||||
|
||||
button.secondary[disabled],
|
||||
.theia-button.secondary[disabled] {
|
||||
color: var(--theia-secondaryButton-foreground);
|
||||
background-color: var(--theia-secondaryButton-background);
|
||||
button.secondary[disabled], .theia-button.secondary[disabled] {
|
||||
color: var(--theia-secondaryButton-foreground);
|
||||
background-color: var(--theia-secondaryButton-background);
|
||||
}
|
||||
|
||||
button.theia-button.message-box-dialog-button {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* To make the progress-bar slightly thicker, and use the color from the status bar */
|
||||
.theia-progress-bar-container {
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.theia-progress-bar {
|
||||
height: 4px;
|
||||
width: 3%;
|
||||
animation: progress-animation 1.3s 0s infinite
|
||||
cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
height: 4px;
|
||||
width: 3%;
|
||||
animation: progress-animation 1.3s 0s infinite
|
||||
cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
}
|
||||
|
||||
.theia-notification-item-progressbar {
|
||||
height: 4px;
|
||||
width: 66%;
|
||||
height: 4px;
|
||||
width: 66%;
|
||||
}
|
||||
|
||||
.flex-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.fa-reload {
|
||||
font-size: 14px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.debug-toolbar .debug-action > div {
|
||||
font-family: var(--theia-ui-font-family);
|
||||
font-size: var(--theia-ui-font-size0);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: center;
|
||||
justify-content: center;
|
||||
min-height: inherit;
|
||||
.debug-toolbar .debug-action>div {
|
||||
font-family: var(--theia-ui-font-family);
|
||||
font-size: var(--theia-ui-font-size0);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: center;
|
||||
justify-content: center;
|
||||
min-height: inherit;
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
.library-tab-icon {
|
||||
-webkit-mask: url("../icons/library-tab-icon.svg");
|
||||
mask: url("../icons/library-tab-icon.svg");
|
||||
-webkit-mask: url('../icons/library-tab-icon.svg');
|
||||
mask: url('../icons/library-tab-icon.svg');
|
||||
}
|
||||
|
||||
.arduino-list-widget {
|
||||
@@ -82,7 +82,7 @@
|
||||
}
|
||||
|
||||
.component-list-item .header .title .name {
|
||||
font-family: "Open Sans Bold";
|
||||
font-family: 'Open Sans Bold';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
@@ -112,9 +112,7 @@
|
||||
display: inline-block;
|
||||
justify-self: end;
|
||||
text-align: center;
|
||||
background-color: var(
|
||||
--theia-arduino-toolbar-dropdown-option-backgroundHover
|
||||
);
|
||||
background-color: var(--theia-arduino-toolbar-dropdown-option-backgroundHover);
|
||||
padding: 2px 4px 2px 4px;
|
||||
font-size: 12px;
|
||||
max-height: calc(1em + 4px);
|
||||
@@ -133,7 +131,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 4px;
|
||||
font-family: "Open Sans";
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
@@ -177,20 +175,8 @@
|
||||
max-width: 8px;
|
||||
}
|
||||
|
||||
div.filterable-list-container
|
||||
> div
|
||||
> div
|
||||
> div
|
||||
> div:nth-child(1)
|
||||
> div.separator
|
||||
:first-child,
|
||||
div.filterable-list-container
|
||||
> div
|
||||
> div
|
||||
> div
|
||||
> div:nth-child(1)
|
||||
> div.separator
|
||||
:last-child {
|
||||
div.filterable-list-container > div > div > div > div:nth-child(1) > div.separator :first-child,
|
||||
div.filterable-list-container > div > div > div > div:nth-child(1) > div.separator :last-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -216,11 +202,11 @@ div.filterable-list-container
|
||||
}
|
||||
|
||||
.component-list-item .theia-button.secondary.no-border {
|
||||
border: 2px solid var(--theia-button-foreground);
|
||||
border: 2px solid var(--theia-button-foreground)
|
||||
}
|
||||
|
||||
.component-list-item .theia-button.secondary.no-border:hover {
|
||||
border: 2px solid var(--theia-secondaryButton-foreground);
|
||||
border: 2px solid var(--theia-secondaryButton-foreground)
|
||||
}
|
||||
|
||||
.component-list-item .theia-button {
|
||||
|
@@ -1,12 +1,11 @@
|
||||
#theia-bottom-content-panel
|
||||
.p-TabBar[data-orientation="horizontal"].theia-app-bottom {
|
||||
#theia-bottom-content-panel .p-TabBar[data-orientation='horizontal'].theia-app-bottom {
|
||||
background: var(--theia-editorGroupHeader-tabsBackground);
|
||||
}
|
||||
|
||||
/* Avoid the Intellisense widget may be cover by the bottom panel partially.
|
||||
TODO: This issue may be resolved after monaco-editor upgrade */
|
||||
#theia-main-content-panel {
|
||||
z-index: auto;
|
||||
z-index: auto
|
||||
}
|
||||
|
||||
#theia-main-content-panel div[id^="code-editor-opener"] {
|
||||
@@ -24,6 +23,7 @@
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
|
||||
.p-TabBar-toolbar .item.arduino-tool-item > div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -42,15 +42,9 @@
|
||||
background: var(--theia-arduino-toolbar-button-hoverBackground);
|
||||
}
|
||||
|
||||
.p-TabBar-toolbar
|
||||
.item.arduino-tool-item.enabled:hover
|
||||
> div.toggle-serial-monitor,
|
||||
.p-TabBar-toolbar
|
||||
.item.arduino-tool-item.enabled:hover
|
||||
> div.toggle-serial-plotter {
|
||||
background-color: var(
|
||||
--theia-arduino-toolbar-button-secondary-hoverBackground
|
||||
);
|
||||
.p-TabBar-toolbar .item.arduino-tool-item.enabled:hover > div.toggle-serial-monitor,
|
||||
.p-TabBar-toolbar .item.arduino-tool-item.enabled:hover > div.toggle-serial-plotter {
|
||||
background-color: var(--theia-arduino-toolbar-button-secondary-hoverBackground);
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
@@ -61,14 +55,14 @@
|
||||
}
|
||||
|
||||
.item.arduino-tool-item.toggled {
|
||||
background-color: unset;
|
||||
opacity: 1;
|
||||
border: none;
|
||||
background-color: unset;
|
||||
opacity: 1;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.item.arduino-tool-item.toggled .arduino-verify-sketch--toolbar,
|
||||
.item.arduino-tool-item.toggled .arduino-upload-sketch--toolbar {
|
||||
background-color: var(--theia-arduino-toolbar-toggleBackground) !important;
|
||||
background-color: var(--theia-arduino-toolbar-toggleBackground) !important;
|
||||
}
|
||||
|
||||
.arduino-tool-icon {
|
||||
@@ -97,7 +91,7 @@
|
||||
}
|
||||
|
||||
.arduino-start-debug-icon {
|
||||
-webkit-mask: url("../icons/debug-dark.svg") 50% 60%;
|
||||
-webkit-mask: url('../icons/debug-dark.svg') 50% 60%;
|
||||
-webkit-mask-size: 70%;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
display: flex;
|
||||
@@ -181,12 +175,7 @@
|
||||
background-color: var(--theia-terminal-background);
|
||||
}
|
||||
|
||||
.theia-output
|
||||
.monaco-editor
|
||||
.lines-content.monaco-editor-background
|
||||
.view-lines
|
||||
.view-line
|
||||
.mtk1:not(.theia-output-error):not(.theia-output-warning) {
|
||||
.theia-output .monaco-editor .lines-content.monaco-editor-background .view-lines .view-line .mtk1:not(.theia-output-error):not(.theia-output-warning) {
|
||||
color: var(--theia-terminal-foreground);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
.monitor-tab-icon {
|
||||
-webkit-mask: url("../icons/monitor-tab-icon.svg");
|
||||
mask: url("../icons/monitor-tab-icon.svg");
|
||||
-webkit-mask: url('../icons/monitor-tab-icon.svg');
|
||||
mask: url('../icons/monitor-tab-icon.svg');
|
||||
}
|
||||
|
||||
.serial-monitor {
|
||||
@@ -10,8 +10,8 @@
|
||||
}
|
||||
|
||||
.serial-monitor-messages {
|
||||
white-space: "pre";
|
||||
font-family: monospace;
|
||||
white-space: 'pre';
|
||||
font-family: monospace
|
||||
}
|
||||
|
||||
.serial-monitor-messages pre {
|
||||
|
@@ -1,32 +1,32 @@
|
||||
.progress-bar {
|
||||
margin-top: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.progress-bar--outer {
|
||||
background: var(--theia-editorWidget-background);
|
||||
border-radius: 11px;
|
||||
height: 6px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: var(--theia-editorWidget-background);
|
||||
border-radius: 11px;
|
||||
height: 6px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-bar--inner {
|
||||
transition: width 1s;
|
||||
height: 100%;
|
||||
background: var(--theia-progressBar-background);
|
||||
border-radius: 11px;
|
||||
transition: width 1s;
|
||||
height: 100%;
|
||||
background: var(--theia-progressBar-background);
|
||||
border-radius: 11px;
|
||||
}
|
||||
|
||||
.progress-bar--percentage {
|
||||
align-items: flex-end;
|
||||
display: flex;
|
||||
height: 40px;
|
||||
justify-content: center;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
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;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
#arduino-settings-dialog-container > .dialogBlock {
|
||||
height: 531px;
|
||||
height: 531px;;
|
||||
max-width: 740px !important;
|
||||
width: calc(100% - 96px);
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
.settings-step-input-container {
|
||||
position: relative;
|
||||
position: relative
|
||||
}
|
||||
|
||||
.settings-step-input-element::-webkit-inner-spin-button,
|
||||
@@ -25,7 +25,7 @@
|
||||
right: 14px;
|
||||
}
|
||||
|
||||
.settings-step-input-container:hover > .settings-step-input-buttons-container {
|
||||
.settings-step-input-container:hover>.settings-step-input-buttons-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -47,4 +47,4 @@
|
||||
|
||||
.settings-step-input-button:hover {
|
||||
background: rgba(128, 128, 128, 0.8);
|
||||
}
|
||||
}
|
@@ -1,36 +1,37 @@
|
||||
.sketchbook-tab-icon {
|
||||
-webkit-mask: url("./sketchbook.svg");
|
||||
mask: url("./sketchbook.svg");
|
||||
-webkit-mask: url('./sketchbook.svg');
|
||||
mask: url('./sketchbook.svg');
|
||||
}
|
||||
|
||||
.p-TabBar-tabIcon.sketchbook-tree-icon {
|
||||
background-color: var(--theia-foreground);
|
||||
-webkit-mask: url(./sketchbook-tree-icon.svg);
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
width: 19px !important;
|
||||
height: var(--theia-icon-size);
|
||||
-webkit-mask-size: 100%;
|
||||
background-color: var(--theia-foreground);
|
||||
-webkit-mask: url(./sketchbook-tree-icon.svg);
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
width: 19px !important;
|
||||
height: var(--theia-icon-size);
|
||||
-webkit-mask-size: 100%;
|
||||
}
|
||||
|
||||
.p-mod-current .sketchbook-tree-icon {
|
||||
background-color: var(--theia-foreground);
|
||||
-webkit-mask: url(./sketchbook-tree-icon-filled.svg);
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
-webkit-mask-size: 100%;
|
||||
.p-mod-current
|
||||
.sketchbook-tree-icon {
|
||||
background-color: var(--theia-foreground);
|
||||
-webkit-mask: url(./sketchbook-tree-icon-filled.svg);
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
-webkit-mask-size: 100%;
|
||||
}
|
||||
|
||||
.sketchbook-trees-container {
|
||||
height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sketchbook-trees-container .create-new {
|
||||
min-height: 58px;
|
||||
height: 58px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 58px;
|
||||
height: 58px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
/*
|
||||
By default, theia-button has a left-margin. IDE2 does not need the left margin
|
||||
@@ -38,43 +39,44 @@ for the _New Remote? Sketch_. Otherwise, the button does not fit the default
|
||||
widget width.
|
||||
*/
|
||||
.sketchbook-trees-container .create-new .theia-button {
|
||||
margin-left: unset;
|
||||
margin-left: unset;
|
||||
}
|
||||
|
||||
.sketchbook-tree__opts {
|
||||
background-color: var(--theia-foreground);
|
||||
-webkit-mask: url(./sketchbook-opts-icon.svg);
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
width: var(--theia-icon-size);
|
||||
height: var(--theia-icon-size);
|
||||
background-color: var(--theia-foreground);
|
||||
-webkit-mask: url(./sketchbook-opts-icon.svg);
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
width: var(--theia-icon-size);
|
||||
height: var(--theia-icon-size);
|
||||
}
|
||||
|
||||
.active-sketch {
|
||||
font-weight: 500;
|
||||
background-color: var(--theia-list-activeSelectionBackground) !important;
|
||||
color: var(--theia-list-activeSelectionForeground) !important;
|
||||
font-weight: 500;
|
||||
background-color: var(--theia-list-activeSelectionBackground) !important;
|
||||
color: var(--theia-list-activeSelectionForeground) !important;
|
||||
|
||||
}
|
||||
|
||||
#arduino-sketchbook-tree-widget .theia-TreeNode {
|
||||
line-height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
#arduino-sketchbook-tree-widget .theia-TreeNodeSegmentGrow {
|
||||
flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.theia-TreeNode .sketchbook-commands-icons {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.theia-TreeNode:hover .sketchbook-commands-icons,
|
||||
.theia-TreeNode.theia-mod-selected .sketchbook-commands-icons {
|
||||
display: block;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.theia-Tree:focus .theia-TreeNode.theia-mod-selected,
|
||||
.theia-Tree .ReactVirtualized__List:focus .theia-TreeNode.theia-mod-selected {
|
||||
background: var(--theia-list-inactiveSelectionBackground);
|
||||
color: var(--theia-list-inactiveSelectionForeground) !important;
|
||||
background: var(--theia-list-inactiveSelectionBackground);
|
||||
color: var(--theia-list-inactiveSelectionForeground) !important;
|
||||
}
|
||||
|
@@ -1,32 +1,32 @@
|
||||
.user-fields-container {
|
||||
max-height: 332px;
|
||||
overflow: auto;
|
||||
padding: 2px;
|
||||
max-height: 332px;
|
||||
overflow: auto;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.user-fields-list {
|
||||
margin: 16px 0;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.user-fields-dialog-content {
|
||||
width: 408px;
|
||||
max-height: 491px;
|
||||
width: 408px;
|
||||
max-height: 491px;
|
||||
}
|
||||
|
||||
.user-fields-dialog-content .field-label {
|
||||
color: var(--theia-editorWidget-foreground);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 21px;
|
||||
letter-spacing: 0.01em;
|
||||
text-align: left;
|
||||
color: var(--theia-editorWidget-foreground);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 21px;
|
||||
letter-spacing: 0.01em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.user-fields-dialog-content .theia-input {
|
||||
flex-grow: 1;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.user-fields-dialog-content .button-container {
|
||||
justify-content: flex-end;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import {
|
||||
CommonCommands,
|
||||
CommonFrontendContribution as TheiaCommonFrontendContribution,
|
||||
} from '@theia/core/lib/browser/common-frontend-contribution';
|
||||
import type { OnWillStopAction } from '@theia/core/lib/browser/frontend-application';
|
||||
import type { KeybindingRegistry } from '@theia/core/lib/browser/keybinding';
|
||||
import type { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import type { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||
import {
|
||||
CommonFrontendContribution as TheiaCommonFrontendContribution,
|
||||
CommonCommands,
|
||||
} from '@theia/core/lib/browser/common-frontend-contribution';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import type { OnWillStopAction } from '@theia/core/lib/browser/frontend-application';
|
||||
import { KeybindingRegistry } from '@theia/core/lib/browser';
|
||||
import { isOSX } from '@theia/core';
|
||||
|
||||
@injectable()
|
||||
export class CommonFrontendContribution extends TheiaCommonFrontendContribution {
|
||||
@@ -25,7 +25,6 @@ export class CommonFrontendContribution extends TheiaCommonFrontendContribution
|
||||
CommonCommands.PIN_TAB,
|
||||
CommonCommands.UNPIN_TAB,
|
||||
CommonCommands.NEW_UNTITLED_FILE,
|
||||
CommonCommands.NEW_UNTITLED_TEXT_FILE,
|
||||
]) {
|
||||
commandRegistry.unregisterCommand(command);
|
||||
}
|
||||
@@ -49,7 +48,6 @@ export class CommonFrontendContribution extends TheiaCommonFrontendContribution
|
||||
CommonCommands.ABOUT_COMMAND,
|
||||
CommonCommands.SAVE_WITHOUT_FORMATTING, // Patched for https://github.com/eclipse-theia/theia/pull/8877,
|
||||
CommonCommands.NEW_UNTITLED_FILE,
|
||||
CommonCommands.NEW_UNTITLED_TEXT_FILE,
|
||||
]) {
|
||||
registry.unregisterMenuAction(command);
|
||||
}
|
||||
|
@@ -74,8 +74,8 @@ export class DaemonPort implements FrontendApplicationContribution {
|
||||
@inject(NotificationCenter)
|
||||
private readonly notificationCenter: NotificationCenter;
|
||||
|
||||
private readonly onPortDidChangeEmitter = new Emitter<number | undefined>();
|
||||
private _port: number | undefined;
|
||||
private readonly onPortDidChangeEmitter = new Emitter<string | undefined>();
|
||||
private _port: string | undefined;
|
||||
|
||||
onStart(): void {
|
||||
this.daemon.tryGetPort().then(
|
||||
@@ -91,15 +91,15 @@ export class DaemonPort implements FrontendApplicationContribution {
|
||||
this.onPortDidChangeEmitter.dispose();
|
||||
}
|
||||
|
||||
get port(): number | undefined {
|
||||
get port(): string | undefined {
|
||||
return this._port;
|
||||
}
|
||||
|
||||
get onDidChangePort(): Event<number | undefined> {
|
||||
get onDidChangePort(): Event<string | undefined> {
|
||||
return this.onPortDidChangeEmitter.event;
|
||||
}
|
||||
|
||||
private setPort(port: number | undefined): void {
|
||||
private setPort(port: string | undefined): void {
|
||||
const oldPort = this._port;
|
||||
this._port = port;
|
||||
if (this._port !== oldPort) {
|
||||
|
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Title, Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser';
|
||||
|
@@ -1,44 +1,44 @@
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import { Disposable } from '@theia/core/lib/common/disposable';
|
||||
import { Emitter, Event } from '@theia/core/lib/common/event';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { DebugConfigurationManager as TheiaDebugConfigurationManager } from '@theia/debug/lib/browser/debug-configuration-manager';
|
||||
import { DebugConfigurationModel as TheiaDebugConfigurationModel } from '@theia/debug/lib/browser/debug-configuration-model';
|
||||
import { DebugConfiguration } from '@theia/debug/lib/common/debug-common';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import {
|
||||
FileOperationError,
|
||||
FileOperationResult,
|
||||
} from '@theia/filesystem/lib/common/files';
|
||||
import debounce from 'p-debounce';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Event, Emitter } from '@theia/core/lib/common/event';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import { DebugConfiguration } from '@theia/debug/lib/common/debug-common';
|
||||
import { DebugConfigurationModel as TheiaDebugConfigurationModel } from '@theia/debug/lib/browser/debug-configuration-model';
|
||||
import { DebugConfigurationManager as TheiaDebugConfigurationManager } from '@theia/debug/lib/browser/debug-configuration-manager';
|
||||
import { SketchesService } from '../../../common/protocol';
|
||||
import {
|
||||
CurrentSketch,
|
||||
SketchesServiceClientImpl,
|
||||
} from '../../sketches-service-client-impl';
|
||||
import { maybeUpdateReadOnlyState } from '../monaco/monaco-editor-provider';
|
||||
import { DebugConfigurationModel } from './debug-configuration-model';
|
||||
import {
|
||||
FileOperationError,
|
||||
FileOperationResult,
|
||||
} from '@theia/filesystem/lib/common/files';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
|
||||
@injectable()
|
||||
export class DebugConfigurationManager extends TheiaDebugConfigurationManager {
|
||||
@inject(SketchesService)
|
||||
private readonly sketchesService: SketchesService;
|
||||
@inject(SketchesServiceClientImpl)
|
||||
private readonly sketchesServiceClient: SketchesServiceClientImpl;
|
||||
@inject(FrontendApplicationStateService)
|
||||
private readonly appStateService: FrontendApplicationStateService;
|
||||
@inject(FileService)
|
||||
private readonly fileService: FileService;
|
||||
protected readonly sketchesService: SketchesService;
|
||||
|
||||
private onTempContentDidChangeEmitter =
|
||||
@inject(SketchesServiceClientImpl)
|
||||
protected readonly sketchesServiceClient: SketchesServiceClientImpl;
|
||||
|
||||
@inject(FrontendApplicationStateService)
|
||||
protected readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
@inject(FileService)
|
||||
protected readonly fileService: FileService;
|
||||
|
||||
protected onTempContentDidChangeEmitter =
|
||||
new Emitter<TheiaDebugConfigurationModel.JsonContent>();
|
||||
get onTempContentDidChange(): Event<TheiaDebugConfigurationModel.JsonContent> {
|
||||
return this.onTempContentDidChangeEmitter.event;
|
||||
}
|
||||
|
||||
protected override async doInit(): Promise<void> {
|
||||
this.watchLaunchConfigEditor();
|
||||
this.appStateService.reachedState('ready').then(async () => {
|
||||
const tempContent = await this.getTempLaunchJsonContent();
|
||||
if (!tempContent) {
|
||||
@@ -75,19 +75,6 @@ export class DebugConfigurationManager extends TheiaDebugConfigurationManager {
|
||||
return super.doInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a listener on current sketch change, and maybe updates the readonly state of the editor showing the debug configuration. aka the `launch.json`.
|
||||
*/
|
||||
private watchLaunchConfigEditor(): Disposable {
|
||||
return this.sketchesServiceClient.onCurrentSketchDidChange(() => {
|
||||
for (const widget of this.editorManager.all) {
|
||||
maybeUpdateReadOnlyState(widget, (uri) =>
|
||||
this.sketchesServiceClient.isReadOnly(uri)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override updateModels = debounce(async () => {
|
||||
await this.appStateService.reachedState('ready');
|
||||
const roots = await this.workspaceService.roots;
|
||||
@@ -124,7 +111,7 @@ export class DebugConfigurationManager extends TheiaDebugConfigurationManager {
|
||||
this.updateCurrent();
|
||||
}, 500);
|
||||
|
||||
private async getTempLaunchJsonContent(): Promise<
|
||||
protected async getTempLaunchJsonContent(): Promise<
|
||||
(TheiaDebugConfigurationModel.JsonContent & { uri: URI }) | URI | undefined
|
||||
> {
|
||||
const sketch = await this.sketchesServiceClient.currentSketch();
|
||||
|
@@ -1,76 +0,0 @@
|
||||
import { SelectOption } from '@theia/core/lib/browser/widgets/select-component';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import React from '@theia/core/shared/react';
|
||||
import { DebugAction } from '@theia/debug/lib/browser/view/debug-action';
|
||||
import { DebugConfigurationSelect as TheiaDebugConfigurationSelect } from '@theia/debug/lib/browser/view/debug-configuration-select';
|
||||
import { DebugConfigurationWidget as TheiaDebugConfigurationWidget } from '@theia/debug/lib/browser/view/debug-configuration-widget';
|
||||
|
||||
/**
|
||||
* Patched to programmatically update the debug config <select> in the widget.
|
||||
*/
|
||||
@injectable()
|
||||
export class DebugConfigurationWidget extends TheiaDebugConfigurationWidget {
|
||||
override render(): React.ReactNode {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<DebugAction
|
||||
run={this.start}
|
||||
label={nls.localizeByDefault('Start Debugging')}
|
||||
iconClass="debug-start"
|
||||
ref={this.setStepRef}
|
||||
/>
|
||||
{/* The customized select component that will refresh when the config manager did change */}
|
||||
<DebugConfigurationSelect
|
||||
manager={this.manager}
|
||||
quickInputService={this.quickInputService}
|
||||
isMultiRoot={this.workspaceService.isMultiRootWorkspaceOpened}
|
||||
/>
|
||||
<DebugAction
|
||||
run={this.openConfiguration}
|
||||
label={nls.localizeByDefault('Open {0}', '"launch.json"')}
|
||||
iconClass="settings-gear"
|
||||
/>
|
||||
<DebugAction
|
||||
run={this.openConsole}
|
||||
label={nls.localizeByDefault('Debug Console')}
|
||||
iconClass="terminal"
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DebugConfigurationSelect extends TheiaDebugConfigurationSelect {
|
||||
private readonly toDisposeOnUnmount = new DisposableCollection();
|
||||
|
||||
override componentDidMount(): void {
|
||||
super.componentDidMount();
|
||||
this.toDisposeOnUnmount.push(
|
||||
this['manager'].onDidChange(() => this.refreshDebugConfigurations())
|
||||
);
|
||||
}
|
||||
|
||||
protected override renderOptions(): SelectOption[] {
|
||||
const options = super.renderOptions();
|
||||
const addConfiguration = options[options.length - 1];
|
||||
const separator = options[options.length - 2];
|
||||
// Remove "Add configuration..." and the preceding separator options.
|
||||
// They're expected to be the last two items.
|
||||
if (
|
||||
addConfiguration.value ===
|
||||
TheiaDebugConfigurationSelect.ADD_CONFIGURATION &&
|
||||
separator.separator
|
||||
) {
|
||||
options.splice(options.length - 2, 2);
|
||||
return options;
|
||||
}
|
||||
// Something is unexpected with the select options.
|
||||
return options;
|
||||
}
|
||||
|
||||
override componentWillUnmount(): void {
|
||||
this.toDisposeOnUnmount.dispose();
|
||||
}
|
||||
}
|
@@ -1,39 +0,0 @@
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { DebugSession } from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
|
||||
import { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
|
||||
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
|
||||
@injectable()
|
||||
export class DebugSessionManager extends TheiaDebugSessionManager {
|
||||
@inject(WorkspaceService)
|
||||
private readonly workspaceService: WorkspaceService;
|
||||
|
||||
protected override doStart(
|
||||
sessionId: string,
|
||||
options: DebugConfigurationSessionOptions
|
||||
): Promise<DebugSession> {
|
||||
this.syncCurrentOptions(options);
|
||||
return super.doStart(sessionId, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the debug config manager knows about the currently started options, and it's not the currently selected one, select it.
|
||||
*/
|
||||
private syncCurrentOptions(options: DebugConfigurationSessionOptions): void {
|
||||
const knownConfigOptions = this.debugConfigurationManager.find(
|
||||
options.configuration,
|
||||
options.workspaceFolderUri ??
|
||||
this.workspaceService
|
||||
.tryGetRoots()
|
||||
.map((stat) => stat.resource.toString())[0]
|
||||
);
|
||||
if (
|
||||
knownConfigOptions &&
|
||||
!deepEqual(knownConfigOptions, this.debugConfigurationManager.current)
|
||||
) {
|
||||
this.debugConfigurationManager.current = knownConfigOptions;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
import { codicon } from '@theia/core/lib/browser/widgets/widget';
|
||||
import { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { DebugWidget as TheiaDebugWidget } from '@theia/debug/lib/browser/view/debug-widget';
|
||||
import { DebugDisabledStatusMessageSource } from '../../contributions/debug';
|
||||
import {
|
||||
removeWidgetIfPresent,
|
||||
unshiftWidgetIfNotPresent,
|
||||
} from '../dialogs/widgets';
|
||||
|
||||
@injectable()
|
||||
export class DebugWidget extends TheiaDebugWidget {
|
||||
@inject(DebugDisabledStatusMessageSource)
|
||||
private readonly debugStatusMessageSource: DebugDisabledStatusMessageSource;
|
||||
|
||||
private readonly statusMessageWidget = new Widget();
|
||||
private readonly messageNode = document.createElement('div');
|
||||
|
||||
@postConstruct()
|
||||
protected override init(): void {
|
||||
super.init();
|
||||
this.messageNode.classList.add('status-message', 'noselect');
|
||||
this.statusMessageWidget.node.appendChild(this.messageNode);
|
||||
this.updateState();
|
||||
this.toDisposeOnDetach.pushAll([
|
||||
this.debugStatusMessageSource.onDidChangeMessage((message) =>
|
||||
this.updateState(message)
|
||||
),
|
||||
this.statusMessageWidget,
|
||||
]);
|
||||
}
|
||||
|
||||
private updateState(message = this.debugStatusMessageSource.message): void {
|
||||
requestAnimationFrame(() => {
|
||||
this.messageNode.textContent = message ?? '';
|
||||
const enabled = !message;
|
||||
updateVisibility(enabled, this.toolbar, this.sessionWidget);
|
||||
if (enabled) {
|
||||
removeWidgetIfPresent(this.layout, this.statusMessageWidget);
|
||||
} else {
|
||||
unshiftWidgetIfNotPresent(this.layout, this.statusMessageWidget);
|
||||
}
|
||||
this.title.iconClass = enabled ? codicon('debug-alt') : 'fa fa-ban'; // TODO: find a better icon?
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateVisibility(visible: boolean, ...widgets: Widget[]): void {
|
||||
widgets.forEach((widget) =>
|
||||
visible ? widget.removeClass('hidden') : widget.addClass('hidden')
|
||||
);
|
||||
}
|
@@ -1,51 +0,0 @@
|
||||
import {
|
||||
Layout,
|
||||
PanelLayout,
|
||||
Widget,
|
||||
} from '@theia/core/shared/@phosphor/widgets';
|
||||
|
||||
/**
|
||||
*
|
||||
* Removes the widget from the layout if the `layout` is a `PanelLayout` and the widget is present in the layout.
|
||||
* Otherwise, it's NOOP
|
||||
* @param layout the layout to remove the widget from. Must be a `PanelLayout`.
|
||||
* @param toRemove the widget to remove from the layout
|
||||
*/
|
||||
export function removeWidgetIfPresent(
|
||||
layout: Layout | null,
|
||||
toRemove: Widget
|
||||
): void {
|
||||
if (layout instanceof PanelLayout) {
|
||||
const index = layout.widgets.indexOf(toRemove);
|
||||
if (index < 0) {
|
||||
// Unlike the default `PanelLayout#removeWidget` behavior, (https://github.com/phosphorjs/phosphor/blob/9f5e11025b62d2c4a6fb59e2681ae1ed323dcde4/packages/widgets/src/panellayout.ts#L154-L156)
|
||||
// do not try to remove widget if it's not present (the index is negative).
|
||||
// Otherwise, required widgets could be removed based on the default ArrayExt behavior (https://github.com/phosphorjs/phosphor/blob/9f5e11025b62d2c4a6fb59e2681ae1ed323dcde4/packages/algorithm/src/array.ts#L1075-L1077)
|
||||
// See https://github.com/arduino/arduino-ide/issues/2354 for more details.
|
||||
return;
|
||||
}
|
||||
layout.removeWidget(toRemove);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Inserts the widget to the `0` index of the layout if the `layout` is a `PanelLayout` and the widget is not yet part of the layout.
|
||||
* Otherwise, it's NOOP
|
||||
* @param layout the layout to add the widget to. Must be a `PanelLayout`.
|
||||
* @param toAdd the widget to add to the layout
|
||||
*/
|
||||
export function unshiftWidgetIfNotPresent(
|
||||
layout: Layout | null,
|
||||
toAdd: Widget
|
||||
): void {
|
||||
if (layout instanceof PanelLayout) {
|
||||
const index = layout.widgets.indexOf(toAdd);
|
||||
if (index >= 0) {
|
||||
// Do not try to add the widget to the layout if it's already present.
|
||||
// This is the counterpart logic of the `removeWidgetIfPresent` function.
|
||||
return;
|
||||
}
|
||||
layout.insertWidget(0, toAdd);
|
||||
}
|
||||
}
|
@@ -1,20 +1,15 @@
|
||||
import { LOCKED_CLASS, lock } from '@theia/core/lib/browser/widgets/widget';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Title, Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
|
||||
import * as monaco from '@theia/monaco-editor-core';
|
||||
import type { ReferencesModel } from '@theia/monaco-editor-core/esm/vs/editor/contrib/gotoSymbol/browser/referencesModel';
|
||||
import {
|
||||
EditorServiceOverrides,
|
||||
MonacoEditor,
|
||||
} from '@theia/monaco/lib/browser/monaco-editor';
|
||||
import { EditorServiceOverrides, MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
||||
import { MonacoEditorProvider as TheiaMonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
|
||||
import { SketchesServiceClientImpl } from '../../sketches-service-client-impl';
|
||||
import * as monaco from '@theia/monaco-editor-core';
|
||||
import type { ReferencesModel } from '@theia/monaco-editor-core/esm/vs/editor/contrib/gotoSymbol/browser/referencesModel';
|
||||
|
||||
|
||||
type CancelablePromise = Promise<ReferencesModel> & {
|
||||
cancel: () => void;
|
||||
@@ -44,9 +39,7 @@ export class MonacoEditorProvider extends TheiaMonacoEditorProvider {
|
||||
|
||||
private installCustomReferencesController(editor: MonacoEditor): Disposable {
|
||||
const control = editor.getControl();
|
||||
const referencesController: any = control.getContribution(
|
||||
'editor.contrib.referencesController'
|
||||
);
|
||||
const referencesController: any = control.getContribution('editor.contrib.referencesController');
|
||||
const originalToggleWidget = referencesController.toggleWidget;
|
||||
const toDispose = new DisposableCollection();
|
||||
const toDisposeBeforeToggleWidget = new DisposableCollection();
|
||||
@@ -104,30 +97,3 @@ export class MonacoEditorProvider extends TheiaMonacoEditorProvider {
|
||||
editor.updateOptions({ readOnly });
|
||||
}
|
||||
}
|
||||
|
||||
// Theia cannot dynamically set an editor to writable once it was readonly.
|
||||
export function maybeUpdateReadOnlyState(
|
||||
widget: EditorWidget,
|
||||
isReadOnly: (uri: string | URI | monaco.Uri) => boolean
|
||||
): void {
|
||||
const editor = widget.editor;
|
||||
if (!(editor instanceof MonacoEditor)) {
|
||||
return;
|
||||
}
|
||||
const model = editor.document;
|
||||
const oldReadOnly = model.readOnly;
|
||||
const resource = model['resource'];
|
||||
const newReadOnly = Boolean(resource.isReadonly) || isReadOnly(resource.uri);
|
||||
if (oldReadOnly !== newReadOnly) {
|
||||
editor.getControl().updateOptions({ readOnly: newReadOnly });
|
||||
if (newReadOnly) {
|
||||
lock(widget.title);
|
||||
} else {
|
||||
unlock(widget.title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function unlock(title: Title<Widget>): void {
|
||||
title.className = title.className.replace(LOCKED_CLASS, '').trim();
|
||||
}
|
||||
|
@@ -19,9 +19,7 @@ export class MonacoTextModelService extends TheiaMonacoTextModelService {
|
||||
const factory = this.factories
|
||||
.getContributions()
|
||||
.find(({ scheme }) => resource.uri.scheme === scheme);
|
||||
const readOnly =
|
||||
Boolean(resource.isReadonly) ||
|
||||
this.sketchesServiceClient.isReadOnly(resource.uri);
|
||||
const readOnly = this.sketchesServiceClient.isReadOnly(resource.uri);
|
||||
return factory
|
||||
? factory.createModel(resource)
|
||||
: new MaybeReadonlyMonacoEditorModel(
|
||||
@@ -77,7 +75,7 @@ class MaybeReadonlyMonacoEditorModel extends SilentMonacoEditorModel {
|
||||
}
|
||||
this._dirty = dirty;
|
||||
if (dirty === false) {
|
||||
this['updateSavedVersionId']();
|
||||
(this as any).updateSavedVersionId();
|
||||
}
|
||||
this.onDirtyChangedEmitter.fire(undefined);
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ import {
|
||||
} from '@theia/monaco/lib/browser/monaco-theming-service';
|
||||
import { MonacoThemeRegistry as TheiaMonacoThemeRegistry } from '@theia/monaco/lib/browser/textmate/monaco-theme-registry';
|
||||
import type { ThemeMix } from '@theia/monaco/lib/browser/textmate/monaco-theme-types';
|
||||
import { HostedPluginSupport } from '../../hosted/hosted-plugin-support';
|
||||
import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||
import { ArduinoThemes, compatibleBuiltInTheme } from '../core/theming';
|
||||
import { WindowServiceExt } from '../core/window-service-ext';
|
||||
|
||||
|
@@ -14,8 +14,7 @@ export class FileNavigatorContribution extends TheiaFileNavigatorContribution {
|
||||
constructor(
|
||||
@inject(FileNavigatorPreferences)
|
||||
protected override readonly fileNavigatorPreferences: FileNavigatorPreferences,
|
||||
@inject(OpenerService)
|
||||
protected override readonly openerService: OpenerService,
|
||||
@inject(OpenerService) protected override readonly openerService: OpenerService,
|
||||
@inject(FileNavigatorFilter)
|
||||
protected override readonly fileNavigatorFilter: FileNavigatorFilter,
|
||||
@inject(WorkspaceService)
|
||||
|
@@ -8,9 +8,7 @@ import { OutputToolbarContribution as TheiaOutputToolbarContribution } from '@th
|
||||
|
||||
@injectable()
|
||||
export class OutputToolbarContribution extends TheiaOutputToolbarContribution {
|
||||
override async registerToolbarItems(
|
||||
registry: TabBarToolbarRegistry
|
||||
): Promise<void> {
|
||||
override async registerToolbarItems(registry: TabBarToolbarRegistry): Promise<void> {
|
||||
await super.registerToolbarItems(registry); // Why is it async?
|
||||
// It's a hack. Currently, it's not possible to unregister a toolbar contribution via API.
|
||||
(
|
||||
|
@@ -5,13 +5,9 @@ import {
|
||||
PluginContributions,
|
||||
HostedPluginSupport as TheiaHostedPluginSupport,
|
||||
} from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||
import { HostedPluginSupport } from '../../hosted/hosted-plugin-support';
|
||||
|
||||
@injectable()
|
||||
export class HostedPluginSupportImpl
|
||||
extends TheiaHostedPluginSupport
|
||||
implements HostedPluginSupport
|
||||
{
|
||||
export class HostedPluginSupport extends TheiaHostedPluginSupport {
|
||||
private readonly onDidLoadEmitter = new Emitter<void>();
|
||||
private readonly onDidCloseConnectionEmitter = new Emitter<void>();
|
||||
|
||||
|
@@ -8,10 +8,7 @@ export class ScmContribution extends TheiaScmContribution {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
protected override setStatusBarEntry(
|
||||
id: string,
|
||||
entry: StatusBarEntry
|
||||
): void {
|
||||
protected override setStatusBarEntry(id: string, entry: StatusBarEntry): void {
|
||||
// NOOP
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user