mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-09-29 22:58:32 +00:00
Compare commits
68 Commits
2.0.0-beta
...
2.0.0-rc2
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0b6fc0b973 | ||
![]() |
c91fe2d775 | ||
![]() |
bbded57ae4 | ||
![]() |
a8ae0bb4e0 | ||
![]() |
49d12d99ff | ||
![]() |
767b09d2f1 | ||
![]() |
88397931c5 | ||
![]() |
5ddab1ded7 | ||
![]() |
f0d9894a16 | ||
![]() |
59e4c57ecd | ||
![]() |
dd76f9180c | ||
![]() |
6e34a27b7e | ||
![]() |
a090dfe99c | ||
![]() |
74bfdc4c56 | ||
![]() |
20f7712129 | ||
![]() |
9863dc2f90 | ||
![]() |
13734a642c | ||
![]() |
7ac7ae9063 | ||
![]() |
437caeb348 | ||
![]() |
3b04d8df26 | ||
![]() |
99d65531c4 | ||
![]() |
4f4ccb8c66 | ||
![]() |
7bc83eba1d | ||
![]() |
72750f0be3 | ||
![]() |
8cbf7f419c | ||
![]() |
ea2aeec69b | ||
![]() |
b83702fde3 | ||
![]() |
5be3e9de2d | ||
![]() |
e8bc7d7179 | ||
![]() |
acbb164c3c | ||
![]() |
99099b06aa | ||
![]() |
5c958bc6c7 | ||
![]() |
11b75bd610 | ||
![]() |
61262c23ac | ||
![]() |
7503739a9f | ||
![]() |
060ab5bccb | ||
![]() |
1c42b8cefc | ||
![]() |
825f0b0f2a | ||
![]() |
846c22cb03 | ||
![]() |
fc0f67493b | ||
![]() |
54a67fc67c | ||
![]() |
7f8b227c39 | ||
![]() |
ba177be41d | ||
![]() |
0eb2d25570 | ||
![]() |
e9db1c0482 | ||
![]() |
79b075c961 | ||
![]() |
a46f36acd1 | ||
![]() |
bfb90a8b4f | ||
![]() |
658c19f55b | ||
![]() |
3f8a07654d | ||
![]() |
a8ec7c2640 | ||
![]() |
a7a1f95ced | ||
![]() |
835e9913ae | ||
![]() |
d3d6ba8176 | ||
![]() |
0f82e91380 | ||
![]() |
7d5381bbde | ||
![]() |
302fb7b6af | ||
![]() |
6233e1fa98 | ||
![]() |
2cb9889fe4 | ||
![]() |
bed6e0b741 | ||
![]() |
302f0109dd | ||
![]() |
735d3733e2 | ||
![]() |
4b36852f57 | ||
![]() |
b84b6c921d | ||
![]() |
289f07f187 | ||
![]() |
b9c777a5c3 | ||
![]() |
92af4bef26 | ||
![]() |
167f059163 |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -2,7 +2,7 @@
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: 'type: bug'
|
||||
labels: 'type: imperfection'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
24
.github/label-configuration-files/labels.yml
vendored
Normal file
24
.github/label-configuration-files/labels.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Used by the "Sync Labels" workflow
|
||||
# See: https://github.com/Financial-Times/github-label-sync#label-config-file
|
||||
|
||||
- name: "topic: accessibility"
|
||||
color: "00ffff"
|
||||
description: Enabling the use of the software by everyone
|
||||
- name: "topic: CLI"
|
||||
color: "00ffff"
|
||||
description: Related to Arduino CLI
|
||||
- name: "topic: debugger"
|
||||
color: "00ffff"
|
||||
description: Related to the integrated debugger
|
||||
- name: "topic: language server"
|
||||
color: "00ffff"
|
||||
description: Related to the Arduino Language Server
|
||||
- name: "topic: serial monitor"
|
||||
color: "00ffff"
|
||||
description: Related to the Serial Monitor
|
||||
- name: "topic: theia"
|
||||
color: "00ffff"
|
||||
description: Related to the Theia IDE framework
|
||||
- name: "topic: theme"
|
||||
color: "00ffff"
|
||||
description: Related to GUI theming
|
131
.github/tools/fetch_athena_stats.py
vendored
Normal file
131
.github/tools/fetch_athena_stats.py
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
import boto3
|
||||
import semver
|
||||
import os
|
||||
import logging
|
||||
import uuid
|
||||
import time
|
||||
|
||||
|
||||
# logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
|
||||
log = logging.getLogger()
|
||||
logging.getLogger("boto3").setLevel(logging.CRITICAL)
|
||||
logging.getLogger("botocore").setLevel(logging.CRITICAL)
|
||||
logging.getLogger("urllib3").setLevel(logging.CRITICAL)
|
||||
|
||||
|
||||
def execute(client, statement, dest_s3_output_location):
|
||||
log.info("execute query: {} dumping in {}".format(statement, dest_s3_output_location))
|
||||
result = client.start_query_execution(
|
||||
QueryString=statement,
|
||||
ClientRequestToken=str(uuid.uuid4()),
|
||||
ResultConfiguration={
|
||||
"OutputLocation": dest_s3_output_location,
|
||||
},
|
||||
)
|
||||
execution_id = result["QueryExecutionId"]
|
||||
log.info("wait for query {} completion".format(execution_id))
|
||||
wait_for_query_execution_completion(client, execution_id)
|
||||
log.info("operation successful")
|
||||
return execution_id
|
||||
|
||||
|
||||
def wait_for_query_execution_completion(client, query_execution_id):
|
||||
query_ended = False
|
||||
while not query_ended:
|
||||
query_execution = client.get_query_execution(QueryExecutionId=query_execution_id)
|
||||
state = query_execution["QueryExecution"]["Status"]["State"]
|
||||
if state == "SUCCEEDED":
|
||||
query_ended = True
|
||||
elif state in ["FAILED", "CANCELLED"]:
|
||||
raise BaseException(
|
||||
"query failed or canceled: {}".format(query_execution["QueryExecution"]["Status"]["StateChangeReason"])
|
||||
)
|
||||
else:
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def valid(key):
|
||||
split = key.split("_")
|
||||
if len(split) < 1:
|
||||
return False
|
||||
try:
|
||||
semver.parse(split[0])
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_results(client, execution_id):
|
||||
results_paginator = client.get_paginator("get_query_results")
|
||||
results_iter = results_paginator.paginate(QueryExecutionId=execution_id, PaginationConfig={"PageSize": 1000})
|
||||
res = {}
|
||||
for results_page in results_iter:
|
||||
for row in results_page["ResultSet"]["Rows"][1:]:
|
||||
# Loop through the JSON objects
|
||||
key = row["Data"][0]["VarCharValue"]
|
||||
if valid(key):
|
||||
res[key] = row["Data"][1]["VarCharValue"]
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def convert_data(data):
|
||||
result = []
|
||||
for key, value in data.items():
|
||||
# 0.18.0_macOS_64bit.tar.gz
|
||||
split_key = key.split("_")
|
||||
if len(split_key) != 3:
|
||||
continue
|
||||
(version, os_version, arch) = split_key
|
||||
arch_split = arch.split(".")
|
||||
if len(arch_split) < 1:
|
||||
continue
|
||||
arch = arch_split[0]
|
||||
if len(arch) > 10:
|
||||
# This can't be an architecture really.
|
||||
# It's an ugly solution but works for now so deal with it.
|
||||
continue
|
||||
repo = os.environ["GITHUB_REPOSITORY"].split("/")[1]
|
||||
result.append(
|
||||
{
|
||||
"type": "gauge",
|
||||
"name": "arduino.downloads.total",
|
||||
"value": value,
|
||||
"host": os.environ["GITHUB_REPOSITORY"],
|
||||
"tags": [
|
||||
f"version:{version}",
|
||||
f"os:{os_version}",
|
||||
f"arch:{arch}",
|
||||
"cdn:downloads.arduino.cc",
|
||||
f"project:{repo}",
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
DEST_S3_OUTPUT = os.environ["AWS_ATHENA_OUTPUT_LOCATION"]
|
||||
AWS_ATHENA_SOURCE_TABLE = os.environ["AWS_ATHENA_SOURCE_TABLE"]
|
||||
|
||||
session = boto3.session.Session(region_name="us-east-1")
|
||||
athena_client = session.client("athena")
|
||||
|
||||
# Load all partitions before querying downloads
|
||||
execute(athena_client, f"MSCK REPAIR TABLE {AWS_ATHENA_SOURCE_TABLE};", DEST_S3_OUTPUT)
|
||||
|
||||
query = f"""SELECT replace(json_extract_scalar(url_decode(url_decode(querystring)),
|
||||
'$.data.url'), 'https://downloads.arduino.cc/arduino-ide/arduino-ide_', '')
|
||||
AS flavor, count(json_extract(url_decode(url_decode(querystring)),'$')) AS gauge
|
||||
FROM {AWS_ATHENA_SOURCE_TABLE}
|
||||
WHERE json_extract_scalar(url_decode(url_decode(querystring)),'$.data.url')
|
||||
LIKE 'https://downloads.arduino.cc/arduino-ide/arduino-ide_%'
|
||||
AND json_extract_scalar(url_decode(url_decode(querystring)),'$.data.url')
|
||||
NOT LIKE '%latest%' -- exclude latest redirect
|
||||
group by 1 ;"""
|
||||
exec_id = execute(athena_client, query, DEST_S3_OUTPUT)
|
||||
results = get_results(athena_client, exec_id)
|
||||
result_json = convert_data(results)
|
||||
|
||||
print(f"::set-output name=result::{result_json}")
|
57
.github/workflows/arduino-stats.yaml
vendored
Normal file
57
.github/workflows/arduino-stats.yaml
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
name: arduino-stats
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# run every day at 07:00 AM, 03:00 PM and 11:00 PM
|
||||
- cron: "0 7,15,23 * * *"
|
||||
workflow_dispatch:
|
||||
repository_dispatch:
|
||||
|
||||
jobs:
|
||||
push-stats:
|
||||
# This workflow is only of value to the arduino/arduino-ide repository and
|
||||
# would always fail in forks
|
||||
if: github.repository == 'arduino/arduino-ide'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Fetch downloads count form Arduino CDN using AWS Athena
|
||||
id: fetch
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.STATS_AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.STATS_AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_ATHENA_SOURCE_TABLE: ${{ secrets.STATS_AWS_ATHENA_SOURCE_TABLE }}
|
||||
AWS_ATHENA_OUTPUT_LOCATION: ${{ secrets.STATS_AWS_ATHENA_OUTPUT_LOCATION }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
run: |
|
||||
pip install boto3 semver
|
||||
python .github/tools/fetch_athena_stats.py
|
||||
|
||||
- name: Send metrics
|
||||
uses: masci/datadog@v1
|
||||
with:
|
||||
api-key: ${{ secrets.DD_API_KEY }}
|
||||
# Metrics input expects YAML but JSON will work just right.
|
||||
metrics: ${{steps.fetch.outputs.result}}
|
||||
|
||||
- name: Report failure
|
||||
if: failure()
|
||||
uses: masci/datadog@v1
|
||||
with:
|
||||
api-key: ${{ secrets.DD_API_KEY }}
|
||||
events: |
|
||||
- title: "Arduino IDE stats failing"
|
||||
text: "Stats collection failed"
|
||||
alert_type: "error"
|
||||
host: ${{ github.repository }}
|
||||
tags:
|
||||
- "project:arduino-ide"
|
||||
- "cdn:downloads.arduino.cc"
|
||||
- "workflow:${{ github.workflow }}"
|
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@@ -70,6 +70,7 @@ jobs:
|
||||
|
||||
elif [ "${{ runner.OS }}" = "Windows" ]; then
|
||||
export CSC_LINK="${{ runner.temp }}/signing_certificate.pfx"
|
||||
npm config set msvs_version 2017 --global
|
||||
echo "${{ secrets.WINDOWS_SIGNING_CERTIFICATE_PFX }}" | base64 --decode > "$CSC_LINK"
|
||||
|
||||
export CSC_KEY_PASSWORD="${{ secrets.WINDOWS_SIGNING_CERTIFICATE_PASSWORD }}"
|
||||
|
45
.github/workflows/check-certificates.yml
vendored
45
.github/workflows/check-certificates.yml
vendored
@@ -1,35 +1,41 @@
|
||||
name: Check for issues with signing certificates
|
||||
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/check-certificates.md
|
||||
name: Check Certificates
|
||||
|
||||
# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/check-certificates.ya?ml'
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/check-certificates.ya?ml'
|
||||
schedule:
|
||||
# run every 10 hours
|
||||
- cron: "0 */10 * * *"
|
||||
# workflow_dispatch event allows the workflow to be triggered manually.
|
||||
# This could be used to run an immediate check after updating certificate secrets.
|
||||
# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#workflow_dispatch
|
||||
# Run every 10 hours.
|
||||
- cron: '0 */10 * * *'
|
||||
workflow_dispatch:
|
||||
repository_dispatch:
|
||||
|
||||
env:
|
||||
# Begin notifications when there are less than this many days remaining before expiration
|
||||
# Begin notifications when there are less than this many days remaining before expiration.
|
||||
EXPIRATION_WARNING_PERIOD: 30
|
||||
|
||||
jobs:
|
||||
check-certificates:
|
||||
name: ${{ matrix.certificate.identifier }}
|
||||
# Only run when the workflow will have access to the certificate secrets.
|
||||
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
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
matrix:
|
||||
certificate:
|
||||
- identifier: macOS signing certificate # Text used to identify the certificate in notifications
|
||||
certificate-secret: APPLE_SIGNING_CERTIFICATE_P12 # The name of the secret that contains the certificate
|
||||
password-secret: KEYCHAIN_PASSWORD # The name of the secret that contains the certificate password
|
||||
# Additional certificate definitions can be added to this list.
|
||||
- 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.
|
||||
- identifier: Windows signing certificate
|
||||
certificate-secret: WINDOWS_SIGNING_CERTIFICATE_PFX
|
||||
password-secret: WINDOWS_SIGNING_CERTIFICATE_PASSWORD
|
||||
@@ -37,7 +43,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set certificate path environment variable
|
||||
run: |
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable
|
||||
# See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable
|
||||
echo "CERTIFICATE_PATH=${{ runner.temp }}/certificate.p12" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Decode certificate
|
||||
@@ -59,18 +65,17 @@ jobs:
|
||||
exit 1
|
||||
)
|
||||
|
||||
# See: https://github.com/rtCamp/action-slack-notify
|
||||
- name: Slack notification of certificate verification failure
|
||||
if: failure()
|
||||
uses: rtCamp/action-slack-notify@v2.1.0
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.TEAM_TOOLING_CHANNEL_SLACK_WEBHOOK }}
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||
SLACK_MESSAGE: |
|
||||
:warning::warning::warning::warning:
|
||||
WARNING: ${{ github.repository }} ${{ matrix.certificate.identifier }} verification failed!!!
|
||||
:warning::warning::warning::warning:
|
||||
SLACK_COLOR: danger
|
||||
MSG_MINIMAL: true
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
|
||||
- name: Get days remaining before certificate expiration date
|
||||
env:
|
||||
@@ -99,7 +104,7 @@ jobs:
|
||||
|
||||
DAYS_BEFORE_EXPIRATION="$((($(date --utc --date="$EXPIRATION_DATE" +%s) - $(date --utc +%s)) / 60 / 60 / 24))"
|
||||
|
||||
# Display the expiration information in the log
|
||||
# Display the expiration information in the log.
|
||||
echo "Certificate expiration date: $EXPIRATION_DATE"
|
||||
echo "Days remaining before expiration: $DAYS_BEFORE_EXPIRATION"
|
||||
|
||||
@@ -114,14 +119,14 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Slack notification of pending certificate expiration
|
||||
# Don't send spurious expiration notification if verification fails
|
||||
# Don't send spurious expiration notification if verification fails.
|
||||
if: failure() && steps.check-expiration.outcome == 'failure'
|
||||
uses: rtCamp/action-slack-notify@v2.1.0
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.TEAM_TOOLING_CHANNEL_SLACK_WEBHOOK }}
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||
SLACK_MESSAGE: |
|
||||
:warning::warning::warning::warning:
|
||||
WARNING: ${{ github.repository }} ${{ matrix.certificate.identifier }} will expire in ${{ steps.get-days-before-expiration.outputs.days }} days!!!
|
||||
:warning::warning::warning::warning:
|
||||
SLACK_COLOR: danger
|
||||
MSG_MINIMAL: true
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
|
38
.github/workflows/check-i18n-task.yml
vendored
Normal file
38
.github/workflows/check-i18n-task.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: Check Internationalization
|
||||
|
||||
# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/check-i18n-task.ya?ml'
|
||||
- '**/package.json'
|
||||
- '**.ts'
|
||||
- 'i18n/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/check-i18n-task.ya?ml'
|
||||
- '**/package.json'
|
||||
- '**.ts'
|
||||
- 'i18n/**'
|
||||
workflow_dispatch:
|
||||
repository_dispatch:
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js 12.x
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12.14.1'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Check for errors
|
||||
run: yarn i18n:check
|
96
.github/workflows/github-stats.yaml
vendored
Normal file
96
.github/workflows/github-stats.yaml
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
name: github-stats
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# run every 30 minutes
|
||||
- cron: "*/30 * * * *"
|
||||
workflow_dispatch:
|
||||
repository_dispatch:
|
||||
|
||||
jobs:
|
||||
push-stats:
|
||||
# This workflow is only of value to the arduino/arduino-ide repository and
|
||||
# would always fail in forks
|
||||
if: github.repository == 'arduino/arduino-ide'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Fetch downloads count
|
||||
id: fetch
|
||||
uses: actions/github-script@v4
|
||||
with:
|
||||
github-token: ${{github.token}}
|
||||
script: |
|
||||
let metrics = []
|
||||
|
||||
// Get a list of releases
|
||||
const opts = github.repos.listReleases.endpoint.merge({
|
||||
...context.repo
|
||||
})
|
||||
const releases = await github.paginate(opts)
|
||||
|
||||
// Get download stats for every release
|
||||
for (const rel of releases) {
|
||||
// Names for assets are like `arduino-ide_2.0.0-beta.12_Linux_64bit.zip`,
|
||||
// we'll use this later to split the asset file name more easily
|
||||
const baseName = `arduino-ide_${rel.name}_`
|
||||
|
||||
// Get a list of assets for this release
|
||||
const opts = github.repos.listReleaseAssets.endpoint.merge({
|
||||
...context.repo,
|
||||
release_id: rel.id
|
||||
})
|
||||
const assets = await github.paginate(opts)
|
||||
|
||||
for (const asset of assets) {
|
||||
// Ignore files that are not arduino-ide packages
|
||||
if (!asset.name.startsWith(baseName)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Strip the base and remove file extension to get `Linux_32bit`
|
||||
systemArch = asset.name.replace(baseName, "").split(".")[0].split("_")
|
||||
|
||||
// Add a metric object to the list of gathered metrics
|
||||
metrics.push({
|
||||
"type": "gauge",
|
||||
"name": "arduino.downloads.total",
|
||||
"value": asset.download_count,
|
||||
"host": "${{ github.repository }}",
|
||||
"tags": [
|
||||
`version:${rel.name}`,
|
||||
`os:${systemArch[0]}`,
|
||||
`arch:${systemArch[1]}`,
|
||||
"cdn:github.com",
|
||||
"project:arduino-ide"
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// The action will put whatever we return from this function in
|
||||
// `outputs.result`, JSON encoded. So we just return the array
|
||||
// of objects and GitHub will do the rest.
|
||||
return metrics
|
||||
|
||||
- name: Send metrics
|
||||
uses: masci/datadog@v1
|
||||
with:
|
||||
api-key: ${{ secrets.DD_API_KEY }}
|
||||
# Metrics input expects YAML but JSON will work just right.
|
||||
metrics: ${{steps.fetch.outputs.result}}
|
||||
|
||||
- name: Report failure
|
||||
if: failure()
|
||||
uses: masci/datadog@v1
|
||||
with:
|
||||
api-key: ${{ secrets.DD_API_KEY }}
|
||||
events: |
|
||||
- title: "Arduino IDE stats failing"
|
||||
text: "Stats collection failed"
|
||||
alert_type: "error"
|
||||
host: ${{ github.repository }}
|
||||
tags:
|
||||
- "project:arduino-ide"
|
||||
- "cdn:github.com"
|
||||
- "workflow:${{ github.workflow }}"
|
30
.github/workflows/i18n-nightly-push.yml
vendored
Normal file
30
.github/workflows/i18n-nightly-push.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: i18n-nightly-push
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# run every day at 1AM
|
||||
- cron: '0 1 * * *'
|
||||
|
||||
jobs:
|
||||
push-to-transifex:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js 12.x
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12.14.1'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Run i18n:push script
|
||||
run: yarn run i18n:push
|
||||
env:
|
||||
TRANSIFEX_ORGANIZATION: ${{ secrets.TRANSIFEX_ORGANIZATION }}
|
||||
TRANSIFEX_PROJECT: ${{ secrets.TRANSIFEX_PROJECT }}
|
||||
TRANSIFEX_RESOURCE: ${{ secrets.TRANSIFEX_RESOURCE }}
|
||||
TRANSIFEX_API_KEY: ${{ secrets.TRANSIFEX_API_KEY }}
|
38
.github/workflows/i18n-weekly-pull.yml
vendored
Normal file
38
.github/workflows/i18n-weekly-pull.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: i18n-weekly-pull
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# run every monday at 2AM
|
||||
- cron: '0 2 * * 1'
|
||||
|
||||
jobs:
|
||||
pull-from-transifex:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js 12.x
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12.14.1'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Run i18n:pull script
|
||||
run: yarn run i18n:pull
|
||||
env:
|
||||
TRANSIFEX_ORGANIZATION: ${{ secrets.TRANSIFEX_ORGANIZATION }}
|
||||
TRANSIFEX_PROJECT: ${{ secrets.TRANSIFEX_PROJECT }}
|
||||
TRANSIFEX_RESOURCE: ${{ secrets.TRANSIFEX_RESOURCE }}
|
||||
TRANSIFEX_API_KEY: ${{ secrets.TRANSIFEX_API_KEY }}
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v3
|
||||
with:
|
||||
commit-message: Updated translation files
|
||||
title: Update translation files
|
||||
branch: i18n/translations-update
|
||||
author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
139
.github/workflows/sync-labels.yml
vendored
Normal file
139
.github/workflows/sync-labels.yml
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/sync-labels.md
|
||||
name: Sync Labels
|
||||
|
||||
# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- ".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"
|
||||
schedule:
|
||||
# Run daily at 8 AM UTC to sync with changes to shared label configurations.
|
||||
- cron: "0 8 * * *"
|
||||
workflow_dispatch:
|
||||
repository_dispatch:
|
||||
|
||||
env:
|
||||
CONFIGURATIONS_FOLDER: .github/label-configuration-files
|
||||
CONFIGURATIONS_ARTIFACT: label-configuration-files
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Download JSON schema for labels configuration file
|
||||
id: download-schema
|
||||
uses: carlosperate/download-file-action@v1.0.3
|
||||
with:
|
||||
file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/arduino-tooling-gh-label-configuration-schema.json
|
||||
location: ${{ runner.temp }}/label-configuration-schema
|
||||
|
||||
- name: Install JSON schema validator
|
||||
run: |
|
||||
sudo npm install \
|
||||
--global \
|
||||
ajv-cli \
|
||||
ajv-formats
|
||||
|
||||
- name: Validate local labels configuration
|
||||
run: |
|
||||
# See: https://github.com/ajv-validator/ajv-cli#readme
|
||||
ajv validate \
|
||||
--all-errors \
|
||||
-c ajv-formats \
|
||||
-s "${{ steps.download-schema.outputs.file-path }}" \
|
||||
-d "${{ env.CONFIGURATIONS_FOLDER }}/*.{yml,yaml}"
|
||||
|
||||
download:
|
||||
needs: check
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
filename:
|
||||
# Filenames of the shared configurations to apply to the repository in addition to the local configuration.
|
||||
# https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/sync-labels
|
||||
- universal.yml
|
||||
- tooling.yml
|
||||
|
||||
steps:
|
||||
- name: Download
|
||||
uses: carlosperate/download-file-action@v1.0.3
|
||||
with:
|
||||
file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }}
|
||||
|
||||
- name: Pass configuration files to next job via workflow artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: |
|
||||
*.yaml
|
||||
*.yml
|
||||
if-no-files-found: error
|
||||
name: ${{ env.CONFIGURATIONS_ARTIFACT }}
|
||||
|
||||
sync:
|
||||
needs: download
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
# See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable
|
||||
echo "MERGED_CONFIGURATION_PATH=${{ runner.temp }}/labels.yml" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Determine whether to dry run
|
||||
id: dry-run
|
||||
if: >
|
||||
github.event_name == 'pull_request' ||
|
||||
(
|
||||
(
|
||||
github.event_name == 'push' ||
|
||||
github.event_name == 'workflow_dispatch'
|
||||
) &&
|
||||
github.ref != format('refs/heads/{0}', github.event.repository.default_branch)
|
||||
)
|
||||
run: |
|
||||
# Use of this flag in the github-label-sync command will cause it to only check the validity of the
|
||||
# configuration.
|
||||
echo "::set-output name=flag::--dry-run"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Download configuration files artifact
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: ${{ env.CONFIGURATIONS_ARTIFACT }}
|
||||
path: ${{ env.CONFIGURATIONS_FOLDER }}
|
||||
|
||||
- name: Remove unneeded artifact
|
||||
uses: geekyeggo/delete-artifact@v1
|
||||
with:
|
||||
name: ${{ env.CONFIGURATIONS_ARTIFACT }}
|
||||
|
||||
- name: Merge label configuration files
|
||||
run: |
|
||||
# Merge all configuration files
|
||||
shopt -s extglob
|
||||
cat "${{ env.CONFIGURATIONS_FOLDER }}"/*.@(yml|yaml) > "${{ env.MERGED_CONFIGURATION_PATH }}"
|
||||
|
||||
- name: Install github-label-sync
|
||||
run: sudo npm install --global github-label-sync
|
||||
|
||||
- name: Sync labels
|
||||
env:
|
||||
GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
# See: https://github.com/Financial-Times/github-label-sync
|
||||
github-label-sync \
|
||||
--labels "${{ env.MERGED_CONFIGURATION_PATH }}" \
|
||||
${{ steps.dry-run.outputs.flag }} \
|
||||
${{ github.repository }}
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,7 +7,8 @@ build/
|
||||
Examples/
|
||||
!electron/build/
|
||||
src-gen/
|
||||
*webpack.config.js
|
||||
!webpack.config.js
|
||||
gen-webpack.config.js
|
||||
.DS_Store
|
||||
# switching from `electron` to `browser` in dev mode.
|
||||
.browser_modules
|
||||
|
12
.vscode/launch.json
vendored
12
.vscode/launch.json
vendored
@@ -8,10 +8,6 @@
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd",
|
||||
"env": {
|
||||
"NODE_ENV": "development",
|
||||
"NODE_PRESERVE_SYMLINKS": "1"
|
||||
}
|
||||
},
|
||||
"cwd": "${workspaceFolder}/electron-app",
|
||||
"protocol": "inspector",
|
||||
@@ -23,7 +19,8 @@
|
||||
"--app-project-path=${workspaceRoot}/electron-app",
|
||||
"--remote-debugging-port=9222",
|
||||
"--no-app-auto-install",
|
||||
"--plugins=local-dir:../plugins"
|
||||
"--plugins=local-dir:../plugins",
|
||||
"--hosted-plugin-inspect=9339"
|
||||
],
|
||||
"env": {
|
||||
"NODE_ENV": "development"
|
||||
@@ -33,7 +30,8 @@
|
||||
"${workspaceRoot}/electron-app/src-gen/backend/*.js",
|
||||
"${workspaceRoot}/electron-app/src-gen/frontend/*.js",
|
||||
"${workspaceRoot}/electron-app/lib/**/*.js",
|
||||
"${workspaceRoot}/arduino-ide-extension/lib/**/*.js"
|
||||
"${workspaceRoot}/arduino-ide-extension/lib/**/*.js",
|
||||
"${workspaceRoot}/node_modules/@theia/**/*.js"
|
||||
],
|
||||
"smartStep": true,
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
@@ -79,6 +77,8 @@
|
||||
"args": [
|
||||
"--require",
|
||||
"reflect-metadata/Reflect",
|
||||
"--require",
|
||||
"ignore-styles",
|
||||
"--no-timeouts",
|
||||
"--colors",
|
||||
"**/${fileBasenameNoExtension}.js"
|
||||
|
@@ -14,7 +14,7 @@ The _Electron main_ process is responsible for:
|
||||
- managing the application lifecycle via listeners, and
|
||||
- creating and managing the web pages for the app.
|
||||
|
||||
In Electron, the process that runs the main entry JavaScript file is called the main process. The _Electron main_ process can display a GUI by creating web pages. An Electron app always has exactly on main process.
|
||||
In Electron, the process that runs the main entry JavaScript file is called the main process. The _Electron main_ process can display a GUI by creating web pages. An Electron app always has exactly one main process.
|
||||
|
||||
By default, whenever the _Electron main_ process creates a web page, it will instantiate a new `BrowserWindow` instance. Since Electron uses Chromium for displaying web pages, Chromium's multi-process architecture is also used. Each web page in Electron runs in its own process, which is called the renderer process. Each `BrowserWindow` instance runs the web page in its own renderer process. When a `BrowserWindow` instance is destroyed, the corresponding renderer process is also terminated. The main process manages all web pages and their corresponding renderer processes. Each renderer process is isolated and only cares about the web page running in it.<sup>[[1]]</sup>
|
||||
|
||||
|
@@ -30,17 +30,20 @@ The Core Service is responsible for building your sketches and uploading them to
|
||||
- compiling a sketch for a selected board type
|
||||
- uploading a sketch to a connected board
|
||||
|
||||
#### Monitor Service
|
||||
#### Serial Service
|
||||
|
||||
The Monitor Service allows getting information back from sketches running on your Arduino boards.
|
||||
The Serial Service allows getting information back from sketches running on your Arduino boards.
|
||||
|
||||
- [src/common/protocol/monitor-service.ts](./src/common/protocol/monitor-service.ts) implements the common classes and interfaces
|
||||
- [src/node/monitor-service-impl.ts](./src/node/monitor-service-impl.ts) implements the service backend:
|
||||
- [src/common/protocol/serial-service.ts](./src/common/protocol/serial-service.ts) implements the common classes and interfaces
|
||||
- [src/node/serial/serial-service-impl.ts](./src/node/serial/serial-service-impl.ts) implements the service backend:
|
||||
- connecting to / disconnecting from a board
|
||||
- receiving and sending data
|
||||
- [src/browser/monitor/monitor-widget.tsx](./src/browser/monitor/monitor-widget.tsx) implements the serial monitor front-end:
|
||||
- [src/browser/serial/serial-connection-manager.ts](./src/browser/serial/serial-connection-manager.ts) handles the serial connection in the frontend
|
||||
- [src/browser/serial/monitor/monitor-widget.tsx](./src/browser/serial/monitor/monitor-widget.tsx) implements the serial monitor front-end:
|
||||
- viewing the output from a connected board
|
||||
- entering data to send to the board
|
||||
- [src/browser/serial/plotter/plotter-frontend-contribution.ts](./src/browser/serial/plotter/plotter-frontend-contribution.ts) implements the serial plotter front-end:
|
||||
- opening a new window running the [Serial Plotter Web App](https://github.com/arduino/arduino-serial-plotter-webapp)
|
||||
|
||||
#### Config Service
|
||||
|
||||
@@ -58,3 +61,13 @@ The Config Service knows about your system, like for example the default sketch
|
||||
#### 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`
|
||||
|
||||
### 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
|
||||
|
1
arduino-ide-extension/arduino-icons.json
Normal file
1
arduino-ide-extension/arduino-icons.json
Normal file
File diff suppressed because one or more lines are too long
@@ -1,13 +1,14 @@
|
||||
{
|
||||
"name": "arduino-ide-extension",
|
||||
"version": "2.0.0-beta.10",
|
||||
"version": "2.0.0-rc2",
|
||||
"description": "An extension for Theia building the Arduino IDE",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"prepare": "yarn download-cli && yarn download-fwuploader && yarn download-ls && yarn clean && yarn download-examples && yarn build",
|
||||
"prepare": "yarn download-cli && yarn download-fwuploader && yarn download-ls && yarn copy-serial-plotter && yarn clean && yarn download-examples && yarn build && yarn test",
|
||||
"clean": "rimraf lib",
|
||||
"download-cli": "node ./scripts/download-cli.js",
|
||||
"download-fwuploader": "node ./scripts/download-fwuploader.js",
|
||||
"copy-serial-plotter": "npx ncp ../node_modules/arduino-serial-plotter-webapp ./build/arduino-serial-plotter-webapp",
|
||||
"download-ls": "node ./scripts/download-ls.js",
|
||||
"download-examples": "node ./scripts/download-examples.js",
|
||||
"generate-protocol": "node ./scripts/generate-protocol.js",
|
||||
@@ -18,22 +19,24 @@
|
||||
"test:watch": "mocha --watch --watch-files lib \"./lib/test/**/*.test.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.1.1",
|
||||
"@theia/application-package": "next",
|
||||
"@theia/core": "next",
|
||||
"@theia/editor": "next",
|
||||
"@theia/filesystem": "next",
|
||||
"@theia/git": "next",
|
||||
"@theia/keymaps": "next",
|
||||
"@theia/markers": "next",
|
||||
"@theia/monaco": "next",
|
||||
"@theia/navigator": "next",
|
||||
"@theia/outline-view": "next",
|
||||
"@theia/preferences": "next",
|
||||
"@theia/output": "next",
|
||||
"@theia/search-in-workspace": "next",
|
||||
"@theia/terminal": "next",
|
||||
"@theia/workspace": "next",
|
||||
"@grpc/grpc-js": "^1.3.7",
|
||||
"@theia/application-package": "1.19.0",
|
||||
"@theia/core": "1.19.0",
|
||||
"@theia/editor": "1.19.0",
|
||||
"@theia/editor-preview": "1.19.0",
|
||||
"@theia/filesystem": "1.19.0",
|
||||
"@theia/git": "1.19.0",
|
||||
"@theia/keymaps": "1.19.0",
|
||||
"@theia/markers": "1.19.0",
|
||||
"@theia/monaco": "1.19.0",
|
||||
"@theia/navigator": "1.19.0",
|
||||
"@theia/outline-view": "1.19.0",
|
||||
"@theia/output": "1.19.0",
|
||||
"@theia/preferences": "1.19.0",
|
||||
"@theia/search-in-workspace": "1.19.0",
|
||||
"@theia/terminal": "1.19.0",
|
||||
"@theia/workspace": "1.19.0",
|
||||
"@tippyjs/react": "^4.2.5",
|
||||
"@types/atob": "^2.1.2",
|
||||
"@types/auth0-js": "^9.14.0",
|
||||
"@types/btoa": "^1.2.3",
|
||||
@@ -49,10 +52,10 @@
|
||||
"@types/ps-tree": "^1.1.0",
|
||||
"@types/react-select": "^3.0.0",
|
||||
"@types/react-tabs": "^2.3.2",
|
||||
"@types/sinon": "^7.5.2",
|
||||
"@types/temp": "^0.8.34",
|
||||
"@types/which": "^1.3.1",
|
||||
"ajv": "^6.5.3",
|
||||
"arduino-serial-plotter-webapp": "0.0.15",
|
||||
"async-mutex": "^0.3.0",
|
||||
"atob": "^2.1.2",
|
||||
"auth0-js": "^9.14.0",
|
||||
@@ -63,6 +66,7 @@
|
||||
"fuzzy": "^0.1.3",
|
||||
"glob": "^7.1.6",
|
||||
"google-protobuf": "^3.11.4",
|
||||
"grpc": "^1.24.11",
|
||||
"hash.js": "^1.1.7",
|
||||
"is-valid-path": "^0.1.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
@@ -74,20 +78,26 @@
|
||||
"open": "^8.0.6",
|
||||
"p-queue": "^5.0.0",
|
||||
"ps-tree": "^1.2.0",
|
||||
"query-string": "^7.0.1",
|
||||
"react-disable": "^0.1.0",
|
||||
"react-select": "^3.0.4",
|
||||
"react-tabs": "^3.1.2",
|
||||
"react-window": "^1.8.6",
|
||||
"semver": "^7.3.2",
|
||||
"string-natural-compare": "^2.0.3",
|
||||
"temp": "^0.9.1",
|
||||
"tree-kill": "^1.2.1",
|
||||
"upath": "^1.1.2",
|
||||
"url": "^0.11.0",
|
||||
"which": "^1.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.2.7",
|
||||
"@types/chai-string": "^1.4.2",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/react-window": "^1.8.5",
|
||||
"@types/sinon": "^10.0.6",
|
||||
"@types/sinon-chai": "^3.2.6",
|
||||
"chai": "^4.2.0",
|
||||
"chai-string": "^1.5.0",
|
||||
"decompress": "^4.2.0",
|
||||
@@ -96,10 +106,13 @@
|
||||
"download": "^7.1.0",
|
||||
"grpc_tools_node_protoc_ts": "^4.1.0",
|
||||
"mocha": "^7.0.0",
|
||||
"mockdate": "^3.0.5",
|
||||
"moment": "^2.24.0",
|
||||
"protoc": "^1.0.4",
|
||||
"shelljs": "^0.8.3",
|
||||
"sinon": "^9.0.1",
|
||||
"sinon": "^12.0.1",
|
||||
"sinon-chai": "^3.7.0",
|
||||
"typemoq": "^2.1.0",
|
||||
"uuid": "^3.2.1",
|
||||
"yargs": "^11.1.0"
|
||||
},
|
||||
@@ -108,7 +121,8 @@
|
||||
},
|
||||
"mocha": {
|
||||
"require": [
|
||||
"reflect-metadata/Reflect"
|
||||
"reflect-metadata/Reflect",
|
||||
"ignore-styles"
|
||||
],
|
||||
"reporter": "spec",
|
||||
"colors": true,
|
||||
@@ -137,10 +151,10 @@
|
||||
],
|
||||
"arduino": {
|
||||
"cli": {
|
||||
"version": "0.18.3"
|
||||
"version": "0.20.2"
|
||||
},
|
||||
"fwuploader": {
|
||||
"version": "1.0.2"
|
||||
"version": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,69 +4,78 @@
|
||||
// - https://downloads.arduino.cc/arduino-language-server/clangd/clangd_${VERSION}_${SUFFIX}
|
||||
|
||||
(() => {
|
||||
const DEFAULT_ALS_VERSION = '0.5.0';
|
||||
const DEFAULT_CLANGD_VERSION = 'snapshot_20210124';
|
||||
|
||||
const DEFAULT_ALS_VERSION = 'nightly';
|
||||
const DEFAULT_CLANGD_VERSION = 'snapshot_20210124';
|
||||
const path = require('path');
|
||||
const shell = require('shelljs');
|
||||
const downloader = require('./downloader');
|
||||
|
||||
const path = require('path');
|
||||
const shell = require('shelljs');
|
||||
const downloader = require('./downloader');
|
||||
const yargs = require('yargs')
|
||||
.option('ls-version', {
|
||||
alias: 'lv',
|
||||
default: DEFAULT_ALS_VERSION,
|
||||
describe: `The version of the 'arduino-language-server' to download. Defaults to ${DEFAULT_ALS_VERSION}.`,
|
||||
})
|
||||
.option('clangd-version', {
|
||||
alias: 'cv',
|
||||
default: DEFAULT_CLANGD_VERSION,
|
||||
choices: ['snapshot_20210124'],
|
||||
describe: `The version of 'clangd' to download. Defaults to ${DEFAULT_CLANGD_VERSION}.`,
|
||||
})
|
||||
.option('force-download', {
|
||||
alias: 'fd',
|
||||
default: false,
|
||||
describe: `If set, this script force downloads the 'arduino-language-server' even if it already exists on the file system.`,
|
||||
})
|
||||
.version(false)
|
||||
.parse();
|
||||
|
||||
const yargs = require('yargs')
|
||||
.option('ls-version', {
|
||||
alias: 'lv',
|
||||
default: DEFAULT_ALS_VERSION,
|
||||
choices: ['nightly'],
|
||||
describe: `The version of the 'arduino-language-server' to download. Defaults to ${DEFAULT_ALS_VERSION}.`
|
||||
})
|
||||
.option('clangd-version', {
|
||||
alias: 'cv',
|
||||
default: DEFAULT_CLANGD_VERSION,
|
||||
choices: ['snapshot_20210124'],
|
||||
describe: `The version of 'clangd' to download. Defaults to ${DEFAULT_CLANGD_VERSION}.`
|
||||
})
|
||||
.option('force-download', {
|
||||
alias: 'fd',
|
||||
default: false,
|
||||
describe: `If set, this script force downloads the 'arduino-language-server' even if it already exists on the file system.`
|
||||
})
|
||||
.version(false).parse();
|
||||
const alsVersion = yargs['ls-version'];
|
||||
const clangdVersion = yargs['clangd-version'];
|
||||
const force = yargs['force-download'];
|
||||
const { platform, arch } = process;
|
||||
|
||||
const alsVersion = yargs['ls-version'];
|
||||
const clangdVersion = yargs['clangd-version']
|
||||
const force = yargs['force-download'];
|
||||
const { platform, arch } = process;
|
||||
const build = path.join(__dirname, '..', 'build');
|
||||
const lsExecutablePath = path.join(
|
||||
build,
|
||||
`arduino-language-server${platform === 'win32' ? '.exe' : ''}`
|
||||
);
|
||||
|
||||
const build = path.join(__dirname, '..', 'build');
|
||||
const lsExecutablePath = path.join(build, `arduino-language-server${platform === 'win32' ? '.exe' : ''}`);
|
||||
let clangdExecutablePath, lsSuffix, clangdPrefix;
|
||||
switch (platform) {
|
||||
case 'darwin':
|
||||
clangdExecutablePath = path.join(build, 'bin', 'clangd');
|
||||
lsSuffix = 'macOS_64bit.tar.gz';
|
||||
clangdPrefix = 'mac';
|
||||
break;
|
||||
case 'linux':
|
||||
clangdExecutablePath = path.join(build, 'bin', 'clangd');
|
||||
lsSuffix = 'Linux_64bit.tar.gz';
|
||||
clangdPrefix = 'linux';
|
||||
break;
|
||||
case 'win32':
|
||||
clangdExecutablePath = path.join(build, 'bin', 'clangd.exe');
|
||||
lsSuffix = 'Windows_64bit.zip';
|
||||
clangdPrefix = 'windows';
|
||||
break;
|
||||
}
|
||||
if (!lsSuffix) {
|
||||
shell.echo(
|
||||
`The arduino-language-server is not available for ${platform} ${arch}.`
|
||||
);
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
let clangdExecutablePath, lsSuffix, clangdPrefix;
|
||||
switch (platform) {
|
||||
case 'darwin':
|
||||
clangdExecutablePath = path.join(build, 'bin', 'clangd')
|
||||
lsSuffix = 'macOS_amd64.zip';
|
||||
clangdPrefix = 'mac';
|
||||
break;
|
||||
case 'linux':
|
||||
clangdExecutablePath = path.join(build, 'bin', 'clangd')
|
||||
lsSuffix = 'Linux_amd64.zip';
|
||||
clangdPrefix = 'linux'
|
||||
break;
|
||||
case 'win32':
|
||||
clangdExecutablePath = path.join(build, 'bin', 'clangd.exe')
|
||||
lsSuffix = 'Windows_amd64.zip';
|
||||
clangdPrefix = 'windows';
|
||||
break;
|
||||
}
|
||||
if (!lsSuffix) {
|
||||
shell.echo(`The arduino-language-server is not available for ${platform} ${arch}.`);
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
const alsUrl = `https://downloads.arduino.cc/arduino-language-server/${alsVersion === 'nightly' ? 'nightly/arduino-language-server' : 'arduino-language-server_' + alsVersion}_${lsSuffix}`;
|
||||
downloader.downloadUnzipAll(alsUrl, build, lsExecutablePath, force);
|
||||
|
||||
const clangdUrl = `https://downloads.arduino.cc/arduino-language-server/clangd/clangd-${clangdPrefix}-${clangdVersion}.zip`;
|
||||
downloader.downloadUnzipAll(clangdUrl, build, clangdExecutablePath, force, { strip: 1 }); // `strip`: the new clangd (12.x) is zipped into a folder, so we have to strip the outmost folder.
|
||||
const alsUrl = `https://downloads.arduino.cc/arduino-language-server/${
|
||||
alsVersion === 'nightly'
|
||||
? 'nightly/arduino-language-server'
|
||||
: 'arduino-language-server_' + alsVersion
|
||||
}_${lsSuffix}`;
|
||||
downloader.downloadUnzipAll(alsUrl, build, lsExecutablePath, force);
|
||||
|
||||
const clangdUrl = `https://downloads.arduino.cc/arduino-language-server/clangd/clangd-${clangdPrefix}-${clangdVersion}.zip`;
|
||||
downloader.downloadUnzipAll(clangdUrl, build, clangdExecutablePath, force, {
|
||||
strip: 1,
|
||||
}); // `strip`: the new clangd (12.x) is zipped into a folder, so we have to strip the outmost folder.
|
||||
})();
|
||||
|
@@ -1,20 +1,30 @@
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import * as React from 'react';
|
||||
import { remote } from 'electron';
|
||||
import {
|
||||
BoardsService,
|
||||
Port,
|
||||
SketchesService,
|
||||
ExecutableService,
|
||||
Sketch,
|
||||
LibraryService,
|
||||
} from '../common/protocol';
|
||||
import { Mutex } from 'async-mutex';
|
||||
import {
|
||||
MAIN_MENU_BAR,
|
||||
MenuContribution,
|
||||
MenuModelRegistry,
|
||||
SelectionService,
|
||||
ILogger,
|
||||
DisposableCollection,
|
||||
} from '@theia/core';
|
||||
import {
|
||||
ContextMenuRenderer,
|
||||
FrontendApplication,
|
||||
FrontendApplicationContribution,
|
||||
OpenerService,
|
||||
LocalStorageService,
|
||||
StatusBar,
|
||||
StatusBarAlignment,
|
||||
} from '@theia/core/lib/browser';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
|
||||
import { ColorRegistry } from '@theia/core/lib/browser/color-registry';
|
||||
import { CommonMenus } from '@theia/core/lib/browser/common-frontend-contribution';
|
||||
@@ -33,7 +43,6 @@ import {
|
||||
EditorManager,
|
||||
EditorOpenerOptions,
|
||||
} from '@theia/editor/lib/browser';
|
||||
import { FileDialogService } from '@theia/filesystem/lib/browser/file-dialog';
|
||||
import { ProblemContribution } from '@theia/markers/lib/browser/problem/problem-contribution';
|
||||
import { MonacoMenus } from '@theia/monaco/lib/browser/monaco-menu';
|
||||
import { FileNavigatorContribution } from '@theia/navigator/lib/browser/navigator-contribution';
|
||||
@@ -42,43 +51,27 @@ import { OutputContribution } from '@theia/output/lib/browser/output-contributio
|
||||
import { ScmContribution } from '@theia/scm/lib/browser/scm-contribution';
|
||||
import { SearchInWorkspaceFrontendContribution } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution';
|
||||
import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import * as React from 'react';
|
||||
import { remote } from 'electron';
|
||||
import { MainMenuManager } from '../common/main-menu-manager';
|
||||
import {
|
||||
BoardsService,
|
||||
CoreService,
|
||||
Port,
|
||||
SketchesService,
|
||||
ExecutableService,
|
||||
Sketch,
|
||||
} from '../common/protocol';
|
||||
import { ArduinoDaemon } from '../common/protocol/arduino-daemon';
|
||||
import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { FileChangeType } from '@theia/filesystem/lib/browser';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import { ConfigService } from '../common/protocol/config-service';
|
||||
import { FileSystemExt } from '../common/protocol/filesystem-ext';
|
||||
import { ArduinoCommands } from './arduino-commands';
|
||||
import { BoardsConfig } from './boards/boards-config';
|
||||
import { BoardsConfigDialog } from './boards/boards-config-dialog';
|
||||
import { BoardsDataStore } from './boards/boards-data-store';
|
||||
import { BoardsServiceProvider } from './boards/boards-service-provider';
|
||||
import { BoardsToolBarItem } from './boards/boards-toolbar-item';
|
||||
import { EditorMode } from './editor-mode';
|
||||
import { ArduinoMenus } from './menu/arduino-menus';
|
||||
import { MonitorConnection } from './monitor/monitor-connection';
|
||||
import { MonitorViewContribution } from './monitor/monitor-view-contribution';
|
||||
import { WorkspaceService } from './theia/workspace/workspace-service';
|
||||
import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution';
|
||||
import { ArduinoToolbar } from './toolbar/arduino-toolbar';
|
||||
import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { ResponseService } from '../common/protocol/response-service';
|
||||
import { ArduinoPreferences } from './arduino-preferences';
|
||||
import { SketchesServiceClientImpl } from '../common/protocol/sketches-service-client-impl';
|
||||
import { SaveAsSketch } from './contributions/save-as-sketch';
|
||||
import { FileChangeType } from '@theia/filesystem/lib/browser';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import { SketchbookWidgetContribution } from './widgets/sketchbook/sketchbook-widget-contribution';
|
||||
|
||||
const INIT_LIBS_AND_PACKAGES = 'initializedLibsAndPackages';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoFrontendContribution
|
||||
implements
|
||||
@@ -97,24 +90,15 @@ export class ArduinoFrontendContribution
|
||||
@inject(BoardsService)
|
||||
protected readonly boardsService: BoardsService;
|
||||
|
||||
@inject(CoreService)
|
||||
protected readonly coreService: CoreService;
|
||||
@inject(LibraryService)
|
||||
protected readonly libraryService: LibraryService;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceClientImpl: BoardsServiceProvider;
|
||||
|
||||
@inject(SelectionService)
|
||||
protected readonly selectionService: SelectionService;
|
||||
|
||||
@inject(EditorManager)
|
||||
protected readonly editorManager: EditorManager;
|
||||
|
||||
@inject(ContextMenuRenderer)
|
||||
protected readonly contextMenuRenderer: ContextMenuRenderer;
|
||||
|
||||
@inject(FileDialogService)
|
||||
protected readonly fileDialogService: FileDialogService;
|
||||
|
||||
@inject(FileService)
|
||||
protected readonly fileService: FileService;
|
||||
|
||||
@@ -124,21 +108,12 @@ export class ArduinoFrontendContribution
|
||||
@inject(BoardsConfigDialog)
|
||||
protected readonly boardsConfigDialog: BoardsConfigDialog;
|
||||
|
||||
@inject(MenuModelRegistry)
|
||||
protected readonly menuRegistry: MenuModelRegistry;
|
||||
|
||||
@inject(CommandRegistry)
|
||||
protected readonly commandRegistry: CommandRegistry;
|
||||
|
||||
@inject(StatusBar)
|
||||
protected readonly statusBar: StatusBar;
|
||||
|
||||
@inject(WorkspaceService)
|
||||
protected readonly workspaceService: WorkspaceService;
|
||||
|
||||
@inject(MonitorConnection)
|
||||
protected readonly monitorConnection: MonitorConnection;
|
||||
|
||||
@inject(FileNavigatorContribution)
|
||||
protected readonly fileNavigatorContributions: FileNavigatorContribution;
|
||||
|
||||
@@ -163,42 +138,26 @@ export class ArduinoFrontendContribution
|
||||
@inject(EditorMode)
|
||||
protected readonly editorMode: EditorMode;
|
||||
|
||||
@inject(ArduinoDaemon)
|
||||
protected readonly daemon: ArduinoDaemon;
|
||||
|
||||
@inject(OpenerService)
|
||||
protected readonly openerService: OpenerService;
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
|
||||
@inject(BoardsDataStore)
|
||||
protected readonly boardsDataStore: BoardsDataStore;
|
||||
|
||||
@inject(MainMenuManager)
|
||||
protected readonly mainMenuManager: MainMenuManager;
|
||||
|
||||
@inject(FileSystemExt)
|
||||
protected readonly fileSystemExt: FileSystemExt;
|
||||
|
||||
@inject(HostedPluginSupport)
|
||||
protected hostedPluginSupport: HostedPluginSupport;
|
||||
|
||||
@inject(ExecutableService)
|
||||
protected executableService: ExecutableService;
|
||||
|
||||
@inject(ResponseService)
|
||||
protected readonly responseService: ResponseService;
|
||||
|
||||
@inject(ArduinoPreferences)
|
||||
protected readonly arduinoPreferences: ArduinoPreferences;
|
||||
|
||||
@inject(SketchesServiceClientImpl)
|
||||
protected readonly sketchServiceClient: SketchesServiceClientImpl;
|
||||
|
||||
@inject(FrontendApplicationStateService)
|
||||
protected readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
@inject(LocalStorageService)
|
||||
protected readonly localStorageService: LocalStorageService;
|
||||
|
||||
protected invalidConfigPopup:
|
||||
| Promise<void | 'No' | 'Yes' | undefined>
|
||||
| undefined;
|
||||
@@ -206,10 +165,34 @@ export class ArduinoFrontendContribution
|
||||
|
||||
@postConstruct()
|
||||
protected async init(): Promise<void> {
|
||||
const isFirstStartup = !(await this.localStorageService.getData(
|
||||
INIT_LIBS_AND_PACKAGES
|
||||
));
|
||||
if (isFirstStartup) {
|
||||
await this.localStorageService.setData(INIT_LIBS_AND_PACKAGES, true);
|
||||
const avrPackage = await this.boardsService.getBoardPackage({
|
||||
id: 'arduino:avr',
|
||||
});
|
||||
const builtInLibrary = (
|
||||
await this.libraryService.search({
|
||||
query: 'Arduino_BuiltIn',
|
||||
})
|
||||
)[0];
|
||||
|
||||
!!avrPackage && (await this.boardsService.install({ item: avrPackage }));
|
||||
!!builtInLibrary &&
|
||||
(await this.libraryService.install({
|
||||
item: builtInLibrary,
|
||||
installDependencies: true,
|
||||
}));
|
||||
}
|
||||
if (!window.navigator.onLine) {
|
||||
// tslint:disable-next-line:max-line-length
|
||||
this.messageService.warn(
|
||||
'You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.'
|
||||
nls.localize(
|
||||
'arduino/common/offlineIndicator',
|
||||
'You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.'
|
||||
)
|
||||
);
|
||||
}
|
||||
const updateStatusBar = ({
|
||||
@@ -220,15 +203,22 @@ export class ArduinoFrontendContribution
|
||||
alignment: StatusBarAlignment.RIGHT,
|
||||
text: selectedBoard
|
||||
? `$(microchip) ${selectedBoard.name}`
|
||||
: '$(close) no board selected',
|
||||
: `$(close) ${nls.localize(
|
||||
'arduino/common/noBoardSelected',
|
||||
'No board selected'
|
||||
)}`,
|
||||
className: 'arduino-selected-board',
|
||||
});
|
||||
if (selectedBoard) {
|
||||
this.statusBar.setElement('arduino-selected-port', {
|
||||
alignment: StatusBarAlignment.RIGHT,
|
||||
text: selectedPort
|
||||
? `on ${Port.toString(selectedPort)}`
|
||||
: '[not connected]',
|
||||
? nls.localize(
|
||||
'arduino/common/selectedOn',
|
||||
'on {0}',
|
||||
Port.toString(selectedPort)
|
||||
)
|
||||
: nls.localize('arduino/common/notConnected', '[not connected]'),
|
||||
className: 'arduino-selected-port',
|
||||
});
|
||||
}
|
||||
@@ -309,7 +299,7 @@ export class ArduinoFrontendContribution
|
||||
webContents.setZoomLevel(event.newValue || 0);
|
||||
}
|
||||
});
|
||||
app.shell.leftPanelHandler.removeMenu('settings-menu');
|
||||
app.shell.leftPanelHandler.removeBottomMenu('settings-menu');
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
@@ -365,15 +355,14 @@ export class ArduinoFrontendContribution
|
||||
);
|
||||
}
|
||||
}
|
||||
const { clangdUri, cliUri, lsUri } = await this.executableService.list();
|
||||
const [clangdPath, cliPath, lsPath, cliConfigPath] = await Promise.all([
|
||||
const { clangdUri, lsUri } = await this.executableService.list();
|
||||
const [clangdPath, lsPath] = await Promise.all([
|
||||
this.fileService.fsPath(new URI(clangdUri)),
|
||||
this.fileService.fsPath(new URI(cliUri)),
|
||||
this.fileService.fsPath(new URI(lsUri)),
|
||||
this.fileService.fsPath(
|
||||
new URI(await this.configService.getCliConfigFileUri())
|
||||
),
|
||||
]);
|
||||
|
||||
const config = await this.configService.getConfiguration();
|
||||
|
||||
this.languageServerFqbn = await Promise.race([
|
||||
new Promise<undefined>((_, reject) =>
|
||||
setTimeout(
|
||||
@@ -385,10 +374,10 @@ export class ArduinoFrontendContribution
|
||||
'arduino.languageserver.start',
|
||||
{
|
||||
lsPath,
|
||||
cliPath,
|
||||
cliDaemonAddr: `localhost:${config.daemon.port}`, // TODO: verify if this port is coming from the BE
|
||||
clangdPath,
|
||||
log: currentSketchPath ? currentSketchPath : log,
|
||||
cliConfigPath,
|
||||
cliDaemonInstance: '1',
|
||||
board: {
|
||||
fqbn,
|
||||
name: name ? `"${name}"` : undefined,
|
||||
@@ -421,7 +410,7 @@ export class ArduinoFrontendContribution
|
||||
registry.registerItem({
|
||||
id: 'toggle-serial-monitor',
|
||||
command: MonitorViewContribution.TOGGLE_SERIAL_MONITOR_TOOLBAR,
|
||||
tooltip: 'Serial Monitor',
|
||||
tooltip: nls.localize('arduino/common/serialMonitor', 'Serial Monitor'),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -456,12 +445,21 @@ export class ArduinoFrontendContribution
|
||||
registry.getMenu(MAIN_MENU_BAR).removeNode(menuId(TerminalMenus.TERMINAL));
|
||||
registry.getMenu(MAIN_MENU_BAR).removeNode(menuId(CommonMenus.VIEW));
|
||||
|
||||
registry.registerSubmenu(ArduinoMenus.SKETCH, 'Sketch');
|
||||
registry.registerSubmenu(ArduinoMenus.TOOLS, 'Tools');
|
||||
registry.registerSubmenu(
|
||||
ArduinoMenus.SKETCH,
|
||||
nls.localize('arduino/menu/sketch', 'Sketch')
|
||||
);
|
||||
registry.registerSubmenu(
|
||||
ArduinoMenus.TOOLS,
|
||||
nls.localize('arduino/menu/tools', 'Tools')
|
||||
);
|
||||
registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, {
|
||||
commandId: ArduinoCommands.TOGGLE_COMPILE_FOR_DEBUG.id,
|
||||
label: 'Optimize for Debugging',
|
||||
order: '4',
|
||||
label: nls.localize(
|
||||
'arduino/debug/optimizeForDebugging',
|
||||
'Optimize for Debugging'
|
||||
),
|
||||
order: '5',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -472,13 +470,17 @@ export class ArduinoFrontendContribution
|
||||
for (const uri of [mainFileUri, ...rootFolderFileUris]) {
|
||||
await this.ensureOpened(uri);
|
||||
}
|
||||
await this.ensureOpened(mainFileUri, true);
|
||||
if (mainFileUri.endsWith('.pde')) {
|
||||
const message = `The '${sketch.name}' still uses the old \`.pde\` format. Do you want to switch to the new \`.ino\` extension?`;
|
||||
const message = nls.localize(
|
||||
'arduino/common/oldFormat',
|
||||
"The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?",
|
||||
sketch.name
|
||||
);
|
||||
const yes = nls.localize('vscode/extensionsUtils/yes', 'Yes');
|
||||
this.messageService
|
||||
.info(message, 'Later', 'Yes')
|
||||
.info(message, nls.localize('arduino/common/later', 'Later'), yes)
|
||||
.then(async (answer) => {
|
||||
if (answer === 'Yes') {
|
||||
if (answer === yes) {
|
||||
this.commandRegistry.executeCommand(
|
||||
SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
|
||||
{
|
||||
|
@@ -42,8 +42,8 @@ import { FileNavigatorContribution as TheiaFileNavigatorContribution } from '@th
|
||||
import { KeymapsFrontendContribution } from './theia/keymaps/keymaps-frontend-contribution';
|
||||
import { KeymapsFrontendContribution as TheiaKeymapsFrontendContribution } from '@theia/keymaps/lib/browser/keymaps-frontend-contribution';
|
||||
import { ArduinoToolbarContribution } from './toolbar/arduino-toolbar-contribution';
|
||||
import { EditorContribution as TheiaEditorContribution } from '@theia/editor/lib/browser/editor-contribution';
|
||||
import { EditorContribution } from './theia/editor/editor-contribution';
|
||||
import { EditorPreviewContribution as TheiaEditorPreviewContribution } from '@theia/editor-preview/lib/browser/editor-preview-contribution';
|
||||
import { EditorPreviewContribution } from './theia/editor/editor-contribution';
|
||||
import { MonacoStatusBarContribution as TheiaMonacoStatusBarContribution } from '@theia/monaco/lib/browser/monaco-status-bar-contribution';
|
||||
import { MonacoStatusBarContribution } from './theia/monaco/monaco-status-bar-contribution';
|
||||
import {
|
||||
@@ -69,20 +69,20 @@ import { ScmContribution } from './theia/scm/scm-contribution';
|
||||
import { SearchInWorkspaceFrontendContribution as TheiaSearchInWorkspaceFrontendContribution } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution';
|
||||
import { SearchInWorkspaceFrontendContribution } from './theia/search-in-workspace/search-in-workspace-frontend-contribution';
|
||||
import { LibraryListWidgetFrontendContribution } from './library/library-widget-frontend-contribution';
|
||||
import { MonitorServiceClientImpl } from './monitor/monitor-service-client-impl';
|
||||
import { SerialServiceClientImpl } from './serial/serial-service-client-impl';
|
||||
import {
|
||||
MonitorServicePath,
|
||||
MonitorService,
|
||||
MonitorServiceClient,
|
||||
} from '../common/protocol/monitor-service';
|
||||
SerialServicePath,
|
||||
SerialService,
|
||||
SerialServiceClient,
|
||||
} from '../common/protocol/serial-service';
|
||||
import {
|
||||
ConfigService,
|
||||
ConfigServicePath,
|
||||
} from '../common/protocol/config-service';
|
||||
import { MonitorWidget } from './monitor/monitor-widget';
|
||||
import { MonitorViewContribution } from './monitor/monitor-view-contribution';
|
||||
import { MonitorConnection } from './monitor/monitor-connection';
|
||||
import { MonitorModel } from './monitor/monitor-model';
|
||||
import { MonitorWidget } from './serial/monitor/monitor-widget';
|
||||
import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution';
|
||||
import { SerialConnectionManager } from './serial/serial-connection-manager';
|
||||
import { SerialModel } from './serial/serial-model';
|
||||
import { TabBarDecoratorService as TheiaTabBarDecoratorService } from '@theia/core/lib/browser/shell/tab-bar-decorator';
|
||||
import { TabBarDecoratorService } from './theia/core/tab-bar-decorator';
|
||||
import { ProblemManager as TheiaProblemManager } from '@theia/markers/lib/browser';
|
||||
@@ -154,7 +154,7 @@ import {
|
||||
} from '../common/protocol/examples-service';
|
||||
import { BuiltInExamples, LibraryExamples } from './contributions/examples';
|
||||
import { IncludeLibrary } from './contributions/include-library';
|
||||
import { OutputChannelManager as TheiaOutputChannelManager } from '@theia/output/lib/common/output-channel';
|
||||
import { OutputChannelManager as TheiaOutputChannelManager } from '@theia/output/lib/browser/output-channel';
|
||||
import { OutputChannelManager } from './theia/output/output-channel';
|
||||
import {
|
||||
OutputChannelRegistryMainImpl as TheiaOutputChannelRegistryMainImpl,
|
||||
@@ -165,8 +165,9 @@ import { MonacoTextModelService as TheiaMonacoTextModelService } from '@theia/mo
|
||||
import { MonacoTextModelService } from './theia/monaco/monaco-text-model-service';
|
||||
import { ResponseServiceImpl } from './response-service-impl';
|
||||
import {
|
||||
ResponseServicePath,
|
||||
ResponseService,
|
||||
ResponseServiceArduino,
|
||||
ResponseServicePath,
|
||||
} from '../common/protocol/response-service';
|
||||
import { NotificationCenter } from './notification-center';
|
||||
import {
|
||||
@@ -189,12 +190,12 @@ import { BoardSelection } from './contributions/board-selection';
|
||||
import { OpenRecentSketch } from './contributions/open-recent-sketch';
|
||||
import { Help } from './contributions/help';
|
||||
import { bindArduinoPreferences } from './arduino-preferences';
|
||||
import { SettingsService } from './dialogs/settings/settings';
|
||||
import {
|
||||
SettingsService,
|
||||
SettingsDialog,
|
||||
SettingsWidget,
|
||||
SettingsDialogProps,
|
||||
} from './settings';
|
||||
} from './dialogs/settings/settings-dialog';
|
||||
import { AddFile } from './contributions/add-file';
|
||||
import { ArchiveSketch } from './contributions/archive-sketch';
|
||||
import { OutputToolbarContribution as TheiaOutputToolbarContribution } from '@theia/output/lib/browser/output-toolbar-contribution';
|
||||
@@ -206,12 +207,12 @@ import { DebugConfigurationManager } from './theia/debug/debug-configuration-man
|
||||
import { DebugConfigurationManager as TheiaDebugConfigurationManager } from '@theia/debug/lib/browser/debug-configuration-manager';
|
||||
import { SearchInWorkspaceWidget as TheiaSearchInWorkspaceWidget } from '@theia/search-in-workspace/lib/browser/search-in-workspace-widget';
|
||||
import { SearchInWorkspaceWidget } from './theia/search-in-workspace/search-in-workspace-widget';
|
||||
import { SearchInWorkspaceFactory as TheiaSearchInWorkspaceFactory } from '@theia/search-in-workspace/lib/browser/search-in-workspace-factory';
|
||||
import { SearchInWorkspaceFactory } from './theia/search-in-workspace/search-in-workspace-factory';
|
||||
import { SearchInWorkspaceResultTreeWidget as TheiaSearchInWorkspaceResultTreeWidget } from '@theia/search-in-workspace/lib/browser/search-in-workspace-result-tree-widget';
|
||||
import { SearchInWorkspaceResultTreeWidget } from './theia/search-in-workspace/search-in-workspace-result-tree-widget';
|
||||
import { MonacoEditorProvider } from './theia/monaco/monaco-editor-provider';
|
||||
import { MonacoEditorProvider as TheiaMonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
|
||||
import { DebugEditorModel } from './theia/debug/debug-editor-model';
|
||||
import { DebugEditorModelFactory } from '@theia/debug/lib/browser/editor/debug-editor-model';
|
||||
import { StorageWrapper } from './storage-wrapper';
|
||||
import { NotificationManager } from './theia/messages/notifications-manager';
|
||||
import { NotificationManager as TheiaNotificationManager } from '@theia/messages/lib/browser/notifications-manager';
|
||||
@@ -223,7 +224,7 @@ import { CloudSketchbookWidget } from './widgets/cloud-sketchbook/cloud-sketchbo
|
||||
import { CloudSketchbookTreeWidget } from './widgets/cloud-sketchbook/cloud-sketchbook-tree-widget';
|
||||
import { createCloudSketchbookTreeWidget } from './widgets/cloud-sketchbook/cloud-sketchbook-tree-container';
|
||||
import { CreateApi } from './create/create-api';
|
||||
import { ShareSketchDialog } from './dialogs.ts/cloud-share-sketch-dialog';
|
||||
import { ShareSketchDialog } from './dialogs/cloud-share-sketch-dialog';
|
||||
import { AuthenticationClientService } from './auth/authentication-client-service';
|
||||
import {
|
||||
AuthenticationService,
|
||||
@@ -237,6 +238,30 @@ import { SketchbookWidget } from './widgets/sketchbook/sketchbook-widget';
|
||||
import { SketchbookTreeWidget } from './widgets/sketchbook/sketchbook-tree-widget';
|
||||
import { createSketchbookTreeWidget } from './widgets/sketchbook/sketchbook-tree-container';
|
||||
import { SketchCache } from './widgets/cloud-sketchbook/cloud-sketch-cache';
|
||||
import { UploadFirmware } from './contributions/upload-firmware';
|
||||
import {
|
||||
UploadFirmwareDialog,
|
||||
UploadFirmwareDialogProps,
|
||||
UploadFirmwareDialogWidget,
|
||||
} from './dialogs/firmware-uploader/firmware-uploader-dialog';
|
||||
|
||||
import { UploadCertificate } from './contributions/upload-certificate';
|
||||
import {
|
||||
ArduinoFirmwareUploader,
|
||||
ArduinoFirmwareUploaderPath,
|
||||
} from '../common/protocol/arduino-firmware-uploader';
|
||||
import {
|
||||
UploadCertificateDialog,
|
||||
UploadCertificateDialogProps,
|
||||
UploadCertificateDialogWidget,
|
||||
} from './dialogs/certificate-uploader/certificate-uploader-dialog';
|
||||
import { PlotterFrontendContribution } from './serial/plotter/plotter-frontend-contribution';
|
||||
import {
|
||||
UserFieldsDialog,
|
||||
UserFieldsDialogProps,
|
||||
UserFieldsDialogWidget,
|
||||
} from './dialogs/user-fields/user-fields-dialog';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
const ElementQueries = require('css-element-queries/src/ElementQueries');
|
||||
|
||||
@@ -355,7 +380,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(BoardsConfigDialogWidget).toSelf().inSingletonScope();
|
||||
bind(BoardsConfigDialog).toSelf().inSingletonScope();
|
||||
bind(BoardsConfigDialogProps).toConstantValue({
|
||||
title: 'Select Board',
|
||||
title: nls.localize('arduino/common/selectBoard', 'Select Board'),
|
||||
});
|
||||
|
||||
// Core service
|
||||
@@ -369,8 +394,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
.inSingletonScope();
|
||||
|
||||
// Serial monitor
|
||||
bind(MonitorModel).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(MonitorModel);
|
||||
bind(SerialModel).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(SerialModel);
|
||||
bind(MonitorWidget).toSelf();
|
||||
bindViewContribution(bind, MonitorViewContribution);
|
||||
bind(TabBarToolbarContribution).toService(MonitorViewContribution);
|
||||
@@ -378,28 +403,19 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
id: MonitorWidget.ID,
|
||||
createWidget: () => context.container.get(MonitorWidget),
|
||||
}));
|
||||
// Frontend binding for the serial monitor service
|
||||
bind(MonitorService)
|
||||
// Frontend binding for the serial service
|
||||
bind(SerialService)
|
||||
.toDynamicValue((context) => {
|
||||
const connection = context.container.get(WebSocketConnectionProvider);
|
||||
const client = context.container.get(MonitorServiceClientImpl);
|
||||
return connection.createProxy(MonitorServicePath, client);
|
||||
})
|
||||
.inSingletonScope();
|
||||
bind(MonitorConnection).toSelf().inSingletonScope();
|
||||
// Serial monitor service client to receive and delegate notifications from the backend.
|
||||
bind(MonitorServiceClientImpl).toSelf().inSingletonScope();
|
||||
bind(MonitorServiceClient)
|
||||
.toDynamicValue((context) => {
|
||||
const client = context.container.get(MonitorServiceClientImpl);
|
||||
WebSocketConnectionProvider.createProxy(
|
||||
context.container,
|
||||
MonitorServicePath,
|
||||
client
|
||||
);
|
||||
return client;
|
||||
const client =
|
||||
context.container.get<SerialServiceClient>(SerialServiceClient);
|
||||
return connection.createProxy(SerialServicePath, client);
|
||||
})
|
||||
.inSingletonScope();
|
||||
bind(SerialConnectionManager).toSelf().inSingletonScope();
|
||||
|
||||
// Serial service client to receive and delegate notifications from the backend.
|
||||
bind(SerialServiceClient).to(SerialServiceClientImpl).inSingletonScope();
|
||||
|
||||
bind(WorkspaceService).toSelf().inSingletonScope();
|
||||
rebind(TheiaWorkspaceService).toService(WorkspaceService);
|
||||
@@ -423,7 +439,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
rebind(TheiaKeymapsFrontendContribution)
|
||||
.to(KeymapsFrontendContribution)
|
||||
.inSingletonScope();
|
||||
rebind(TheiaEditorContribution).to(EditorContribution).inSingletonScope();
|
||||
rebind(TheiaEditorPreviewContribution)
|
||||
.to(EditorPreviewContribution)
|
||||
.inSingletonScope();
|
||||
rebind(TheiaMonacoStatusBarContribution)
|
||||
.to(MonacoStatusBarContribution)
|
||||
.inSingletonScope();
|
||||
@@ -476,6 +494,12 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
|
||||
bind(SearchInWorkspaceWidget).toSelf();
|
||||
rebind(TheiaSearchInWorkspaceWidget).toService(SearchInWorkspaceWidget);
|
||||
|
||||
// replace search icon
|
||||
rebind(TheiaSearchInWorkspaceFactory)
|
||||
.to(SearchInWorkspaceFactory)
|
||||
.inSingletonScope();
|
||||
|
||||
rebind(TheiaSearchInWorkspaceResultTreeWidget).toDynamicValue(
|
||||
({ container }) => {
|
||||
const childContainer = createTreeContainer(container);
|
||||
@@ -522,6 +546,15 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
)
|
||||
.inSingletonScope();
|
||||
|
||||
bind(ArduinoFirmwareUploader)
|
||||
.toDynamicValue((context) =>
|
||||
WebSocketConnectionProvider.createProxy(
|
||||
context.container,
|
||||
ArduinoFirmwareUploaderPath
|
||||
)
|
||||
)
|
||||
.inSingletonScope();
|
||||
|
||||
// File-system extension
|
||||
bind(FileSystemExt)
|
||||
.toDynamicValue((context) =>
|
||||
@@ -571,12 +604,15 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
Contribution.configure(bind, About);
|
||||
Contribution.configure(bind, Debug);
|
||||
Contribution.configure(bind, Sketchbook);
|
||||
Contribution.configure(bind, UploadFirmware);
|
||||
Contribution.configure(bind, UploadCertificate);
|
||||
Contribution.configure(bind, BoardSelection);
|
||||
Contribution.configure(bind, OpenRecentSketch);
|
||||
Contribution.configure(bind, Help);
|
||||
Contribution.configure(bind, AddFile);
|
||||
Contribution.configure(bind, ArchiveSketch);
|
||||
Contribution.configure(bind, AddZipLibrary);
|
||||
Contribution.configure(bind, PlotterFrontendContribution);
|
||||
|
||||
bind(ResponseServiceImpl)
|
||||
.toSelf()
|
||||
@@ -589,7 +625,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
);
|
||||
return responseService;
|
||||
});
|
||||
|
||||
bind(ResponseService).toService(ResponseServiceImpl);
|
||||
bind(ResponseServiceArduino).toService(ResponseServiceImpl);
|
||||
|
||||
bind(NotificationCenter).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(NotificationCenter);
|
||||
@@ -639,16 +677,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(DebugConfigurationManager).toSelf().inSingletonScope();
|
||||
rebind(TheiaDebugConfigurationManager).toService(DebugConfigurationManager);
|
||||
|
||||
// Patch for the debug hover: https://github.com/eclipse-theia/theia/pull/9256/
|
||||
rebind(DebugEditorModelFactory)
|
||||
.toDynamicValue(
|
||||
({ container }) =>
|
||||
<DebugEditorModelFactory>(
|
||||
((editor) => DebugEditorModel.createModel(container, editor))
|
||||
)
|
||||
)
|
||||
.inSingletonScope();
|
||||
|
||||
// Preferences
|
||||
bindArduinoPreferences(bind);
|
||||
|
||||
@@ -658,7 +686,10 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(SettingsWidget).toSelf().inSingletonScope();
|
||||
bind(SettingsDialog).toSelf().inSingletonScope();
|
||||
bind(SettingsDialogProps).toConstantValue({
|
||||
title: 'Preferences',
|
||||
title: nls.localize(
|
||||
'vscode/preferences.contribution/preferences',
|
||||
'Preferences'
|
||||
),
|
||||
});
|
||||
|
||||
bind(StorageWrapper).toSelf().inSingletonScope();
|
||||
@@ -713,4 +744,21 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
id: 'cloud-sketchbook-composite-widget',
|
||||
createWidget: () => ctx.container.get(CloudSketchbookCompositeWidget),
|
||||
}));
|
||||
|
||||
bind(UploadFirmwareDialogWidget).toSelf().inSingletonScope();
|
||||
bind(UploadFirmwareDialog).toSelf().inSingletonScope();
|
||||
bind(UploadFirmwareDialogProps).toConstantValue({
|
||||
title: 'UploadFirmware',
|
||||
});
|
||||
bind(UploadCertificateDialogWidget).toSelf().inSingletonScope();
|
||||
bind(UploadCertificateDialog).toSelf().inSingletonScope();
|
||||
bind(UploadCertificateDialogProps).toConstantValue({
|
||||
title: 'UploadCertificate',
|
||||
});
|
||||
|
||||
bind(UserFieldsDialogWidget).toSelf().inSingletonScope();
|
||||
bind(UserFieldsDialog).toSelf().inSingletonScope();
|
||||
bind(UserFieldsDialogProps).toConstantValue({
|
||||
title: 'UserFields',
|
||||
});
|
||||
});
|
||||
|
@@ -6,6 +6,7 @@ import {
|
||||
PreferenceContribution,
|
||||
PreferenceSchema,
|
||||
} from '@theia/core/lib/browser/preferences';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { CompilerWarningLiterals, CompilerWarnings } from '../common/protocol';
|
||||
|
||||
export const ArduinoConfigSchema: PreferenceSchema = {
|
||||
@@ -13,24 +14,34 @@ export const ArduinoConfigSchema: PreferenceSchema = {
|
||||
properties: {
|
||||
'arduino.language.log': {
|
||||
type: 'boolean',
|
||||
description:
|
||||
"True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.",
|
||||
description: nls.localize(
|
||||
'arduino/preferences/language.log',
|
||||
"True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default."
|
||||
),
|
||||
default: false,
|
||||
},
|
||||
'arduino.compile.verbose': {
|
||||
type: 'boolean',
|
||||
description: 'True for verbose compile output. False by default',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/compile.verbose',
|
||||
'True for verbose compile output. False by default'
|
||||
),
|
||||
default: false,
|
||||
},
|
||||
'arduino.compile.warnings': {
|
||||
enum: [...CompilerWarningLiterals],
|
||||
description:
|
||||
"Tells gcc which warning level to use. It's 'None' by default",
|
||||
description: nls.localize(
|
||||
'arduino/preferences/compile.warnings',
|
||||
"Tells gcc which warning level to use. It's 'None' by default"
|
||||
),
|
||||
default: 'None',
|
||||
},
|
||||
'arduino.upload.verbose': {
|
||||
type: 'boolean',
|
||||
description: 'True for verbose upload output. False by default.',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/upload.verbose',
|
||||
'True for verbose upload output. False by default.'
|
||||
),
|
||||
default: false,
|
||||
},
|
||||
'arduino.upload.verify': {
|
||||
@@ -39,76 +50,114 @@ export const ArduinoConfigSchema: PreferenceSchema = {
|
||||
},
|
||||
'arduino.window.autoScale': {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'True if the user interface automatically scales with the font size.',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/window.autoScale',
|
||||
'True if the user interface automatically scales with the font size.'
|
||||
),
|
||||
default: true,
|
||||
},
|
||||
'arduino.window.zoomLevel': {
|
||||
type: 'number',
|
||||
description:
|
||||
'Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/window.zoomLevel',
|
||||
'Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.'
|
||||
),
|
||||
default: 0,
|
||||
},
|
||||
'arduino.ide.autoUpdate': {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'True to enable automatic update checks. The IDE will check for updates automatically and periodically.',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/ide.autoUpdate',
|
||||
'True to enable automatic update checks. The IDE will check for updates automatically and periodically.'
|
||||
),
|
||||
default: true,
|
||||
},
|
||||
'arduino.board.certificates': {
|
||||
type: 'string',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/board.certificates',
|
||||
'List of certificates that can be uploaded to boards'
|
||||
),
|
||||
default: '',
|
||||
},
|
||||
'arduino.sketchbook.showAllFiles': {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'True to show all sketch files inside the sketch. It is false by default.',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/sketchbook.showAllFiles',
|
||||
'True to show all sketch files inside the sketch. It is false by default.'
|
||||
),
|
||||
default: false,
|
||||
},
|
||||
'arduino.cloud.enabled': {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'True if the sketch sync functions are enabled. Defaults to true.',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/cloud.enabled',
|
||||
'True if the sketch sync functions are enabled. Defaults to true.'
|
||||
),
|
||||
default: true,
|
||||
},
|
||||
'arduino.cloud.pull.warn': {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'True if users should be warned before pulling a cloud sketch. Defaults to true.',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/cloud.pull.warn',
|
||||
'True if users should be warned before pulling a cloud sketch. Defaults to true.'
|
||||
),
|
||||
default: true,
|
||||
},
|
||||
'arduino.cloud.push.warn': {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'True if users should be warned before pushing a cloud sketch. Defaults to true.',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/cloud.push.warn',
|
||||
'True if users should be warned before pushing a cloud sketch. Defaults to true.'
|
||||
),
|
||||
default: true,
|
||||
},
|
||||
'arduino.cloud.pushpublic.warn': {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'True if users should be warned before pushing a public sketch to the cloud. Defaults to true.',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/cloud.pushpublic.warn',
|
||||
'True if users should be warned before pushing a public sketch to the cloud. Defaults to true.'
|
||||
),
|
||||
default: true,
|
||||
},
|
||||
'arduino.cloud.sketchSyncEnpoint': {
|
||||
type: 'string',
|
||||
description:
|
||||
'The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/cloud.sketchSyncEnpoint',
|
||||
'The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.'
|
||||
),
|
||||
default: 'https://api2.arduino.cc/create',
|
||||
},
|
||||
'arduino.auth.clientID': {
|
||||
type: 'string',
|
||||
description: 'The OAuth2 client ID.',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/auth.clientID',
|
||||
'The OAuth2 client ID.'
|
||||
),
|
||||
default: 'C34Ya6ex77jTNxyKWj01lCe1vAHIaPIo',
|
||||
},
|
||||
'arduino.auth.domain': {
|
||||
type: 'string',
|
||||
description: 'The OAuth2 domain.',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/auth.domain',
|
||||
'The OAuth2 domain.'
|
||||
),
|
||||
default: 'login.arduino.cc',
|
||||
},
|
||||
'arduino.auth.audience': {
|
||||
type: 'string',
|
||||
description: 'The 0Auth2 audience.',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/auth.audience',
|
||||
'The OAuth2 audience.'
|
||||
),
|
||||
default: 'https://api.arduino.cc',
|
||||
},
|
||||
'arduino.auth.registerUri': {
|
||||
type: 'string',
|
||||
description: 'The URI used to register a new user.',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/auth.registerUri',
|
||||
'The URI used to register a new user.'
|
||||
),
|
||||
default: 'https://auth.arduino.cc/login#/register',
|
||||
},
|
||||
},
|
||||
@@ -123,6 +172,7 @@ export interface ArduinoConfiguration {
|
||||
'arduino.window.autoScale': boolean;
|
||||
'arduino.window.zoomLevel': number;
|
||||
'arduino.ide.autoUpdate': boolean;
|
||||
'arduino.board.certificates': string;
|
||||
'arduino.sketchbook.showAllFiles': boolean;
|
||||
'arduino.cloud.enabled': boolean;
|
||||
'arduino.cloud.pull.warn': boolean;
|
||||
|
@@ -1,15 +1,21 @@
|
||||
import { Command } from '@theia/core/lib/common/command';
|
||||
|
||||
export namespace CloudUserCommands {
|
||||
export const LOGIN: Command = {
|
||||
id: 'arduino-cloud--login',
|
||||
label: 'Sign in',
|
||||
};
|
||||
export const LOGIN = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'arduino-cloud--login',
|
||||
label: 'Sign in',
|
||||
},
|
||||
'arduino/cloud/signIn'
|
||||
);
|
||||
|
||||
export const LOGOUT: Command = {
|
||||
id: 'arduino-cloud--logout',
|
||||
label: 'Sign Out',
|
||||
};
|
||||
export const LOGOUT = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'arduino-cloud--logout',
|
||||
label: 'Sign Out',
|
||||
},
|
||||
'arduino/cloud/signOut'
|
||||
);
|
||||
|
||||
export const OPEN_PROFILE_CONTEXT_MENU: Command = {
|
||||
id: 'arduino-cloud-sketchbook--open-profile-menu',
|
||||
|
@@ -7,10 +7,10 @@ import {
|
||||
Board,
|
||||
} from '../../common/protocol/boards-service';
|
||||
import { BoardsServiceProvider } from './boards-service-provider';
|
||||
import { BoardsListWidgetFrontendContribution } from './boards-widget-frontend-contribution';
|
||||
import { BoardsConfig } from './boards-config';
|
||||
import { Installable } from '../../common/protocol';
|
||||
import { ResponseServiceImpl } from '../response-service-impl';
|
||||
import { Installable, ResponseServiceArduino } from '../../common/protocol';
|
||||
import { BoardsListWidgetFrontendContribution } from './boards-widget-frontend-contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
/**
|
||||
* Listens on `BoardsConfig.Config` changes, if a board is selected which does not
|
||||
@@ -27,8 +27,8 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution {
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceClient: BoardsServiceProvider;
|
||||
|
||||
@inject(ResponseServiceImpl)
|
||||
protected readonly responseService: ResponseServiceImpl;
|
||||
@inject(ResponseServiceArduino)
|
||||
protected readonly responseService: ResponseServiceArduino;
|
||||
|
||||
@inject(BoardsListWidgetFrontendContribution)
|
||||
protected readonly boardsManagerFrontendContribution: BoardsListWidgetFrontendContribution;
|
||||
@@ -44,9 +44,10 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution {
|
||||
}
|
||||
|
||||
protected ensureCoreExists(config: BoardsConfig.Config): void {
|
||||
const { selectedBoard } = config;
|
||||
const { selectedBoard, selectedPort } = config;
|
||||
if (
|
||||
selectedBoard &&
|
||||
selectedPort &&
|
||||
!this.notifications.find((board) => Board.sameAs(board, selectedBoard))
|
||||
) {
|
||||
this.notifications.push(selectedBoard);
|
||||
@@ -81,12 +82,23 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution {
|
||||
const version = candidate.availableVersions[0]
|
||||
? `[v ${candidate.availableVersions[0]}]`
|
||||
: '';
|
||||
const yes = nls.localize('vscode/extensionsUtils/yes', 'Yes');
|
||||
const manualInstall = nls.localize(
|
||||
'arduino/board/installManually',
|
||||
'Install Manually'
|
||||
);
|
||||
// tslint:disable-next-line:max-line-length
|
||||
this.messageService
|
||||
.info(
|
||||
`The \`"${candidate.name} ${version}"\` core has to be installed for the currently selected \`"${selectedBoard.name}"\` board. Do you want to install it now?`,
|
||||
'Install Manually',
|
||||
'Yes'
|
||||
nls.localize(
|
||||
'arduino/board/installNow',
|
||||
'The "{0} {1}" core has to be installed for the currently selected "{2}" board. Do you want to install it now?',
|
||||
candidate.name,
|
||||
version,
|
||||
selectedBoard.name
|
||||
),
|
||||
manualInstall,
|
||||
yes
|
||||
)
|
||||
.then(async (answer) => {
|
||||
const index = this.notifications.findIndex((board) =>
|
||||
@@ -95,7 +107,7 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution {
|
||||
if (index !== -1) {
|
||||
this.notifications.splice(index, 1);
|
||||
}
|
||||
if (answer === 'Yes') {
|
||||
if (answer === yes) {
|
||||
await Installable.installWithProgress({
|
||||
installable: this.boardsService,
|
||||
item: candidate,
|
||||
@@ -105,7 +117,7 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution {
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (answer) {
|
||||
if (answer === manualInstall) {
|
||||
this.boardsManagerFrontendContribution
|
||||
.openView({ reveal: true })
|
||||
.then((widget) =>
|
||||
|
@@ -1,15 +1,12 @@
|
||||
import { injectable, inject, postConstruct } from 'inversify';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import {
|
||||
AbstractDialog,
|
||||
DialogProps,
|
||||
Widget,
|
||||
DialogError,
|
||||
} from '@theia/core/lib/browser';
|
||||
import { DialogProps, Widget, DialogError } from '@theia/core/lib/browser';
|
||||
import { AbstractDialog } from '../theia/dialogs/dialogs';
|
||||
import { BoardsConfig } from './boards-config';
|
||||
import { BoardsService } from '../../common/protocol/boards-service';
|
||||
import { BoardsServiceProvider } from './boards-service-provider';
|
||||
import { BoardsConfigDialogWidget } from './boards-config-dialog-widget';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class BoardsConfigDialogProps extends DialogProps {}
|
||||
@@ -36,8 +33,10 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
|
||||
this.contentNode.classList.add('select-board-dialog');
|
||||
this.contentNode.appendChild(this.createDescription());
|
||||
|
||||
this.appendCloseButton('CANCEL');
|
||||
this.appendAcceptButton('OK');
|
||||
this.appendCloseButton(
|
||||
nls.localize('vscode/issueMainService/cancel', 'Cancel')
|
||||
);
|
||||
this.appendAcceptButton(nls.localize('vscode/issueMainService/ok', 'OK'));
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
@@ -67,7 +66,10 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
|
||||
head.classList.add('head');
|
||||
|
||||
const title = document.createElement('div');
|
||||
title.textContent = 'Select Other Board & Port';
|
||||
title.textContent = nls.localize(
|
||||
'arduino/board/configDialogTitle',
|
||||
'Select Other Board & Port'
|
||||
);
|
||||
title.classList.add('title');
|
||||
head.appendChild(title);
|
||||
|
||||
@@ -76,8 +78,14 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
|
||||
head.appendChild(text);
|
||||
|
||||
for (const paragraph of [
|
||||
'Select both a Board and a Port if you want to upload a sketch.',
|
||||
'If you only select a Board you will be able just to compile, but not to upload your sketch.',
|
||||
nls.localize(
|
||||
'arduino/board/configDialog1',
|
||||
'Select both a Board and a Port if you want to upload a sketch.'
|
||||
),
|
||||
nls.localize(
|
||||
'arduino/board/configDialog2',
|
||||
'If you only select a Board you will be able just to compile, but not to upload your sketch.'
|
||||
),
|
||||
]) {
|
||||
const p = document.createElement('div');
|
||||
p.textContent = paragraph;
|
||||
@@ -121,7 +129,10 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
|
||||
protected isValid(value: BoardsConfig.Config): DialogError {
|
||||
if (!value.selectedBoard) {
|
||||
if (value.selectedPort) {
|
||||
return 'Please pick a board connected to the port you have selected.';
|
||||
return nls.localize(
|
||||
'arduino/board/pleasePickBoard',
|
||||
'Please pick a board connected to the port you have selected.'
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@@ -10,7 +10,12 @@ import {
|
||||
BoardWithPackage,
|
||||
} from '../../common/protocol/boards-service';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { BoardsServiceProvider } from './boards-service-provider';
|
||||
import {
|
||||
AvailableBoard,
|
||||
BoardsServiceProvider,
|
||||
} from './boards-service-provider';
|
||||
import { naturalCompare } from '../../common/utils';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
export namespace BoardsConfig {
|
||||
export interface Config {
|
||||
@@ -183,11 +188,50 @@ export class BoardsConfig extends React.Component<
|
||||
.filter(notEmpty);
|
||||
}
|
||||
|
||||
protected get availableBoards(): AvailableBoard[] {
|
||||
return this.props.boardsServiceProvider.availableBoards;
|
||||
}
|
||||
|
||||
protected queryPorts = async (
|
||||
availablePorts: MaybePromise<Port[]> = this.availablePorts
|
||||
) => {
|
||||
const ports = await availablePorts;
|
||||
return { knownPorts: ports.sort(Port.compare) };
|
||||
// Available ports must be sorted in this order:
|
||||
// 1. Serial with recognized boards
|
||||
// 2. Serial with guessed boards
|
||||
// 3. Serial with incomplete boards
|
||||
// 4. Network with recognized boards
|
||||
// 5. Other protocols with recognized boards
|
||||
const ports = (await availablePorts).sort((left: Port, right: Port) => {
|
||||
if (left.protocol === 'serial' && right.protocol !== 'serial') {
|
||||
return -1;
|
||||
} else if (left.protocol !== 'serial' && right.protocol === 'serial') {
|
||||
return 1;
|
||||
} else if (left.protocol === 'network' && right.protocol !== 'network') {
|
||||
return -1;
|
||||
} else if (left.protocol !== 'network' && right.protocol === 'network') {
|
||||
return 1;
|
||||
} else if (left.protocol === right.protocol) {
|
||||
// We show ports, including those that have guessed
|
||||
// or unrecognized boards, so we must sort those too.
|
||||
const leftBoard = this.availableBoards.find((board) =>
|
||||
Port.sameAs(board.port, left)
|
||||
);
|
||||
const rightBoard = this.availableBoards.find((board) =>
|
||||
Port.sameAs(board.port, right)
|
||||
);
|
||||
if (leftBoard && !rightBoard) {
|
||||
return -1;
|
||||
} else if (!leftBoard && rightBoard) {
|
||||
return 1;
|
||||
} else if (leftBoard?.state! < rightBoard?.state!) {
|
||||
return -1;
|
||||
} else if (leftBoard?.state! > rightBoard?.state!) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return naturalCompare(left.address, right.address);
|
||||
});
|
||||
return { knownPorts: ports };
|
||||
};
|
||||
|
||||
protected toggleFilterPorts = () => {
|
||||
@@ -265,7 +309,7 @@ export class BoardsConfig extends React.Component<
|
||||
<div className="boards list">
|
||||
{Array.from(distinctBoards.values()).map((board) => (
|
||||
<Item<BoardWithPackage>
|
||||
key={`${board.name}-${board.packageName}`}
|
||||
key={toKey(board)}
|
||||
item={board}
|
||||
label={board.name}
|
||||
details={board.details}
|
||||
@@ -280,8 +324,24 @@ export class BoardsConfig extends React.Component<
|
||||
}
|
||||
|
||||
protected renderPorts(): React.ReactNode {
|
||||
const filter = this.state.showAllPorts ? () => true : Port.isBoardPort;
|
||||
const ports = this.state.knownPorts.filter(filter);
|
||||
let ports = [] as Port[];
|
||||
if (this.state.showAllPorts) {
|
||||
ports = this.state.knownPorts;
|
||||
} else {
|
||||
ports = this.state.knownPorts.filter((port) => {
|
||||
if (port.protocol === 'serial') {
|
||||
return true;
|
||||
}
|
||||
// All other ports with different protocol are
|
||||
// only shown if there is a recognized board
|
||||
// connected
|
||||
for (const board of this.availableBoards) {
|
||||
if (board.port?.address === port.address) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return !ports.length ? (
|
||||
<div className="loading noselect">No ports discovered</div>
|
||||
) : (
|
||||
@@ -302,7 +362,12 @@ export class BoardsConfig extends React.Component<
|
||||
protected renderPortsFooter(): React.ReactNode {
|
||||
return (
|
||||
<div className="noselect">
|
||||
<label title="Shows all available ports when enabled">
|
||||
<label
|
||||
title={nls.localize(
|
||||
'arduino/board/showAllAvailablePorts',
|
||||
'Shows all available ports when enabled'
|
||||
)}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
defaultChecked={this.state.showAllPorts}
|
||||
|
@@ -12,6 +12,7 @@ import { FrontendApplicationContribution } from '@theia/core/lib/browser';
|
||||
import { BoardsDataStore } from './boards-data-store';
|
||||
import { MainMenuManager } from '../../common/main-menu-manager';
|
||||
import { ArduinoMenus, unregisterSubmenu } from '../menu/arduino-menus';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class BoardsDataMenuUpdater implements FrontendApplicationContribution {
|
||||
@@ -115,9 +116,13 @@ export class BoardsDataMenuUpdater implements FrontendApplicationContribution {
|
||||
...ArduinoMenus.TOOLS__BOARD_SETTINGS_GROUP,
|
||||
'z02_programmers',
|
||||
];
|
||||
const programmerNls = nls.localize(
|
||||
'arduino/board/programmer',
|
||||
'Programmer'
|
||||
);
|
||||
const label = selectedProgrammer
|
||||
? `Programmer: "${selectedProgrammer.name}"`
|
||||
: 'Programmer';
|
||||
? `${programmerNls}: "${selectedProgrammer.name}"`
|
||||
: programmerNls;
|
||||
this.menuRegistry.registerSubmenu(programmersMenuPath, label);
|
||||
this.toDisposeOnBoardChange.push(
|
||||
Disposable.create(() =>
|
||||
|
@@ -5,11 +5,12 @@ import {
|
||||
} from '../../common/protocol/boards-service';
|
||||
import { ListWidget } from '../widgets/component-list/list-widget';
|
||||
import { ListItemRenderer } from '../widgets/component-list/list-item-renderer';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class BoardsListWidget extends ListWidget<BoardsPackage> {
|
||||
static WIDGET_ID = 'boards-list-widget';
|
||||
static WIDGET_LABEL = 'Boards Manager';
|
||||
static WIDGET_LABEL = nls.localize('arduino/boardsManager', 'Boards Manager');
|
||||
|
||||
constructor(
|
||||
@inject(BoardsService) protected service: BoardsService,
|
||||
@@ -19,7 +20,7 @@ export class BoardsListWidget extends ListWidget<BoardsPackage> {
|
||||
super({
|
||||
id: BoardsListWidget.WIDGET_ID,
|
||||
label: BoardsListWidget.WIDGET_LABEL,
|
||||
iconClass: 'fa fa-microchip',
|
||||
iconClass: 'fa fa-arduino-boards',
|
||||
searchable: service,
|
||||
installable: service,
|
||||
itemLabel: (item: BoardsPackage) => item.name,
|
||||
@@ -52,7 +53,12 @@ export class BoardsListWidget extends ListWidget<BoardsPackage> {
|
||||
}): Promise<void> {
|
||||
await super.install({ item, progressId, version });
|
||||
this.messageService.info(
|
||||
`Successfully installed platform ${item.name}:${version}`,
|
||||
nls.localize(
|
||||
'arduino/board/succesfullyInstalledPlatform',
|
||||
'Successfully installed platform {0}:{1}',
|
||||
item.name,
|
||||
version
|
||||
),
|
||||
{ timeout: 3000 }
|
||||
);
|
||||
}
|
||||
@@ -66,7 +72,12 @@ export class BoardsListWidget extends ListWidget<BoardsPackage> {
|
||||
}): Promise<void> {
|
||||
await super.uninstall({ item, progressId });
|
||||
this.messageService.info(
|
||||
`Successfully uninstalled platform ${item.name}:${item.installedVersion}`,
|
||||
nls.localize(
|
||||
'arduino/board/succesfullyUninstalledPlatform',
|
||||
'Successfully uninstalled platform {0}:{1}',
|
||||
item.name,
|
||||
item.installedVersion!
|
||||
),
|
||||
{ timeout: 3000 }
|
||||
);
|
||||
}
|
||||
|
@@ -12,12 +12,14 @@ import {
|
||||
BoardsPackage,
|
||||
AttachedBoardsChangeEvent,
|
||||
BoardWithPackage,
|
||||
BoardUserField,
|
||||
} from '../../common/protocol';
|
||||
import { BoardsConfig } from './boards-config';
|
||||
import { naturalCompare } from '../../common/utils';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { ArduinoCommands } from '../arduino-commands';
|
||||
import { StorageWrapper } from '../storage-wrapper';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
@@ -41,6 +43,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
protected readonly onAvailableBoardsChangedEmitter = new Emitter<
|
||||
AvailableBoard[]
|
||||
>();
|
||||
protected readonly onAvailablePortsChangedEmitter = new Emitter<Port[]>();
|
||||
|
||||
/**
|
||||
* Used for the auto-reconnecting. Sometimes, the attached board gets disconnected after uploading something to it.
|
||||
@@ -63,11 +66,12 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
* This even also fires, when the boards package was not available for the currently selected board,
|
||||
* and the user installs the board package. Note: installing a board package will set the `fqbn` of the
|
||||
* currently selected board.\
|
||||
* This even also emitted when the board package for the currently selected board was uninstalled.
|
||||
* This event is also emitted when the board package for the currently selected board was uninstalled.
|
||||
*/
|
||||
readonly onBoardsConfigChanged = this.onBoardsConfigChangedEmitter.event;
|
||||
readonly onAvailableBoardsChanged =
|
||||
this.onAvailableBoardsChangedEmitter.event;
|
||||
readonly onAvailablePortsChanged = this.onAvailablePortsChangedEmitter.event;
|
||||
|
||||
onStart(): void {
|
||||
this.notificationCenter.onAttachedBoardsChanged(
|
||||
@@ -87,6 +91,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
]).then(([attachedBoards, availablePorts]) => {
|
||||
this._attachedBoards = attachedBoards;
|
||||
this._availablePorts = availablePorts;
|
||||
this.onAvailablePortsChangedEmitter.fire(this._availablePorts);
|
||||
this.reconcileAvailableBoards().then(() => this.tryReconnect());
|
||||
});
|
||||
}
|
||||
@@ -101,6 +106,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
}
|
||||
this._attachedBoards = event.newState.boards;
|
||||
this._availablePorts = event.newState.ports;
|
||||
this.onAvailablePortsChangedEmitter.fire(this._availablePorts);
|
||||
this.reconcileAvailableBoards().then(() => this.tryReconnect());
|
||||
}
|
||||
|
||||
@@ -134,14 +140,20 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
selectedBoard.packageId === event.item.id &&
|
||||
!installedBoard
|
||||
) {
|
||||
const yes = nls.localize('vscode/extensionsUtils/yes', 'Yes');
|
||||
this.messageService
|
||||
.warn(
|
||||
`Could not find previously selected board '${selectedBoard.name}' in installed platform '${event.item.name}'. Please manually reselect the board you want to use. Do you want to reselect it now?`,
|
||||
'Reselect later',
|
||||
'Yes'
|
||||
nls.localize(
|
||||
'arduino/board/couldNotFindPreviouslySelected',
|
||||
"Could not find previously selected board '{0}' in installed platform '{1}'. Please manually reselect the board you want to use. Do you want to reselect it now?",
|
||||
selectedBoard.name,
|
||||
event.item.name
|
||||
),
|
||||
nls.localize('arduino/board/reselectLater', 'Reselect later'),
|
||||
yes
|
||||
)
|
||||
.then(async (answer) => {
|
||||
if (answer === 'Yes') {
|
||||
if (answer === yes) {
|
||||
this.commandService.executeCommand(
|
||||
ArduinoCommands.OPEN_BOARDS_DIALOG.id,
|
||||
selectedBoard.name
|
||||
@@ -264,6 +276,18 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
return boards;
|
||||
}
|
||||
|
||||
async selectedBoardUserFields(): Promise<BoardUserField[]> {
|
||||
if (!this._boardsConfig.selectedBoard || !this._boardsConfig.selectedPort) {
|
||||
return [];
|
||||
}
|
||||
const fqbn = this._boardsConfig.selectedBoard.fqbn;
|
||||
if (!fqbn) {
|
||||
return [];
|
||||
}
|
||||
const protocol = this._boardsConfig.selectedPort.protocol;
|
||||
return await this.boardsService.getBoardUserFields({ fqbn, protocol });
|
||||
}
|
||||
|
||||
/**
|
||||
* `true` if the `config.selectedBoard` is defined; hence can compile against the board. Otherwise, `false`.
|
||||
*/
|
||||
@@ -277,9 +301,12 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
|
||||
if (!config.selectedBoard) {
|
||||
if (!options.silent) {
|
||||
this.messageService.warn('No boards selected.', {
|
||||
timeout: 3000,
|
||||
});
|
||||
this.messageService.warn(
|
||||
nls.localize('arduino/board/noneSelected', 'No boards selected.'),
|
||||
{
|
||||
timeout: 3000,
|
||||
}
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -301,9 +328,16 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
const { name } = config.selectedBoard;
|
||||
if (!config.selectedPort) {
|
||||
if (!options.silent) {
|
||||
this.messageService.warn(`No ports selected for board: '${name}'.`, {
|
||||
timeout: 3000,
|
||||
});
|
||||
this.messageService.warn(
|
||||
nls.localize(
|
||||
'arduino/board/noPortsSelected',
|
||||
"No ports selected for board: '{0}'.",
|
||||
name
|
||||
),
|
||||
{
|
||||
timeout: 3000,
|
||||
}
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -311,7 +345,11 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
if (!config.selectedBoard.fqbn) {
|
||||
if (!options.silent) {
|
||||
this.messageService.warn(
|
||||
`The FQBN is not available for the selected board ${name}. Do you have the corresponding core installed?`,
|
||||
nls.localize(
|
||||
'arduino/board/noFQBN',
|
||||
'The FQBN is not available for the selected board "{0}". Do you have the corresponding core installed?',
|
||||
name
|
||||
),
|
||||
{ timeout: 3000 }
|
||||
);
|
||||
}
|
||||
@@ -363,7 +401,6 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
}
|
||||
|
||||
protected async reconcileAvailableBoards(): Promise<void> {
|
||||
const attachedBoards = this._attachedBoards;
|
||||
const availablePorts = this._availablePorts;
|
||||
// Unset the port on the user's config, if it is not available anymore.
|
||||
if (
|
||||
@@ -381,51 +418,73 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
const boardsConfig = this.boardsConfig;
|
||||
const currentAvailableBoards = this._availableBoards;
|
||||
const availableBoards: AvailableBoard[] = [];
|
||||
const availableBoardPorts = availablePorts.filter(Port.isBoardPort);
|
||||
const attachedSerialBoards = attachedBoards.filter(({ port }) => !!port);
|
||||
const attachedBoards = this._attachedBoards.filter(({ port }) => !!port);
|
||||
const availableBoardPorts = availablePorts.filter((port) => {
|
||||
if (port.protocol === 'serial') {
|
||||
// We always show all serial ports, even if there
|
||||
// is no recognized board connected to it
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const boardPort of availableBoardPorts) {
|
||||
let state = AvailableBoard.State.incomplete; // Initial pessimism.
|
||||
let board = attachedSerialBoards.find(({ port }) =>
|
||||
Port.sameAs(boardPort, port)
|
||||
);
|
||||
if (board) {
|
||||
state = AvailableBoard.State.recognized;
|
||||
} else {
|
||||
// If the selected board is not recognized because it is a 3rd party board: https://github.com/arduino/arduino-cli/issues/623
|
||||
// We still want to show it without the red X in the boards toolbar: https://github.com/arduino/arduino-pro-ide/issues/198#issuecomment-599355836
|
||||
const lastSelectedBoard = await this.getLastSelectedBoardOnPort(
|
||||
boardPort
|
||||
);
|
||||
if (lastSelectedBoard) {
|
||||
board = {
|
||||
...lastSelectedBoard,
|
||||
port: boardPort,
|
||||
};
|
||||
state = AvailableBoard.State.guessed;
|
||||
// All other ports with different protocol are
|
||||
// only shown if there is a recognized board
|
||||
// connected
|
||||
for (const board of attachedBoards) {
|
||||
if (board.port?.address === port.address) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!board) {
|
||||
availableBoards.push({
|
||||
name: 'Unknown',
|
||||
port: boardPort,
|
||||
state,
|
||||
});
|
||||
} else {
|
||||
const selected = BoardsConfig.Config.sameAs(boardsConfig, board);
|
||||
availableBoards.push({
|
||||
return false;
|
||||
});
|
||||
|
||||
for (const boardPort of availableBoardPorts) {
|
||||
const board = attachedBoards.find(({ port }) =>
|
||||
Port.sameAs(boardPort, port)
|
||||
);
|
||||
const lastSelectedBoard = await this.getLastSelectedBoardOnPort(
|
||||
boardPort
|
||||
);
|
||||
|
||||
let availableBoard = {} as AvailableBoard;
|
||||
if (board) {
|
||||
availableBoard = {
|
||||
...board,
|
||||
state,
|
||||
selected,
|
||||
state: AvailableBoard.State.recognized,
|
||||
selected: BoardsConfig.Config.sameAs(boardsConfig, board),
|
||||
port: boardPort,
|
||||
});
|
||||
};
|
||||
} else if (lastSelectedBoard) {
|
||||
// If the selected board is not recognized because it is a 3rd party board: https://github.com/arduino/arduino-cli/issues/623
|
||||
// We still want to show it without the red X in the boards toolbar: https://github.com/arduino/arduino-pro-ide/issues/198#issuecomment-599355836
|
||||
availableBoard = {
|
||||
...lastSelectedBoard,
|
||||
state: AvailableBoard.State.guessed,
|
||||
selected: BoardsConfig.Config.sameAs(boardsConfig, lastSelectedBoard),
|
||||
port: boardPort,
|
||||
};
|
||||
} else {
|
||||
availableBoard = {
|
||||
name: nls.localize('arduino/common/unknown', 'Unknown'),
|
||||
port: boardPort,
|
||||
state: AvailableBoard.State.incomplete,
|
||||
};
|
||||
}
|
||||
availableBoards.push(availableBoard);
|
||||
}
|
||||
|
||||
if (
|
||||
boardsConfig.selectedBoard &&
|
||||
!availableBoards.some(({ selected }) => selected)
|
||||
) {
|
||||
// If the selected board has the same port of an unknown board
|
||||
// that is already in availableBoards we might get a duplicate port.
|
||||
// So we remove the one already in the array and add the selected one.
|
||||
const found = availableBoards.findIndex(
|
||||
(board) => board.port?.address === boardsConfig.selectedPort?.address
|
||||
);
|
||||
if (found >= 0) {
|
||||
availableBoards.splice(found, 1);
|
||||
}
|
||||
availableBoards.push({
|
||||
...boardsConfig.selectedBoard,
|
||||
port: boardsConfig.selectedPort,
|
||||
@@ -434,28 +493,24 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
});
|
||||
}
|
||||
|
||||
const sortedAvailableBoards = availableBoards.sort(AvailableBoard.compare);
|
||||
let hasChanged =
|
||||
sortedAvailableBoards.length !== currentAvailableBoards.length;
|
||||
for (let i = 0; !hasChanged && i < sortedAvailableBoards.length; i++) {
|
||||
availableBoards.sort(AvailableBoard.compare);
|
||||
|
||||
let hasChanged = availableBoards.length !== currentAvailableBoards.length;
|
||||
for (let i = 0; !hasChanged && i < availableBoards.length; i++) {
|
||||
const [left, right] = [availableBoards[i], currentAvailableBoards[i]];
|
||||
hasChanged =
|
||||
AvailableBoard.compare(
|
||||
sortedAvailableBoards[i],
|
||||
currentAvailableBoards[i]
|
||||
) !== 0;
|
||||
!!AvailableBoard.compare(left, right) ||
|
||||
left.selected !== right.selected;
|
||||
}
|
||||
if (hasChanged) {
|
||||
this._availableBoards = sortedAvailableBoards;
|
||||
this._availableBoards = availableBoards;
|
||||
this.onAvailableBoardsChangedEmitter.fire(this._availableBoards);
|
||||
}
|
||||
}
|
||||
|
||||
protected async getLastSelectedBoardOnPort(
|
||||
port: Port | string | undefined
|
||||
port: Port
|
||||
): Promise<Board | undefined> {
|
||||
if (!port) {
|
||||
return undefined;
|
||||
}
|
||||
const key = this.getLastSelectedBoardOnPortKey(port);
|
||||
return this.getData<Board>(key);
|
||||
}
|
||||
@@ -564,35 +619,39 @@ export namespace AvailableBoard {
|
||||
return !!board.port;
|
||||
}
|
||||
|
||||
// Available boards must be sorted in this order:
|
||||
// 1. Serial with recognized boards
|
||||
// 2. Serial with guessed boards
|
||||
// 3. Serial with incomplete boards
|
||||
// 4. Network with recognized boards
|
||||
// 5. Other protocols with recognized boards
|
||||
export const compare = (left: AvailableBoard, right: AvailableBoard) => {
|
||||
if (left.selected && !right.selected) {
|
||||
if (left.port?.protocol === 'serial' && right.port?.protocol !== 'serial') {
|
||||
return -1;
|
||||
}
|
||||
if (right.selected && !left.selected) {
|
||||
} else if (
|
||||
left.port?.protocol !== 'serial' &&
|
||||
right.port?.protocol === 'serial'
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
let result = naturalCompare(left.name, right.name);
|
||||
if (result !== 0) {
|
||||
return result;
|
||||
}
|
||||
if (left.fqbn && right.fqbn) {
|
||||
result = naturalCompare(left.fqbn, right.fqbn);
|
||||
if (result !== 0) {
|
||||
return result;
|
||||
} else if (
|
||||
left.port?.protocol === 'network' &&
|
||||
right.port?.protocol !== 'network'
|
||||
) {
|
||||
return -1;
|
||||
} else if (
|
||||
left.port?.protocol !== 'network' &&
|
||||
right.port?.protocol === 'network'
|
||||
) {
|
||||
return 1;
|
||||
} else if (left.port?.protocol === right.port?.protocol) {
|
||||
// We show all ports, including those that have guessed
|
||||
// or unrecognized boards, so we must sort those too.
|
||||
if (left.state < right.state) {
|
||||
return -1;
|
||||
} else if (left.state > right.state) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (left.port && right.port) {
|
||||
result = Port.compare(left.port, right.port);
|
||||
if (result !== 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (!!left.selected && !right.selected) {
|
||||
return -1;
|
||||
}
|
||||
if (!!right.selected && !left.selected) {
|
||||
return 1;
|
||||
}
|
||||
return left.state - right.state;
|
||||
return naturalCompare(left.port?.address!, right.port?.address!);
|
||||
};
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ import {
|
||||
BoardsServiceProvider,
|
||||
AvailableBoard,
|
||||
} from './boards-service-provider';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
export interface BoardsDropDownListCoords {
|
||||
readonly top: number;
|
||||
@@ -49,6 +50,10 @@ export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
|
||||
if (coords === 'hidden') {
|
||||
return '';
|
||||
}
|
||||
const footerLabel = nls.localize(
|
||||
'arduino/board/openBoardsConfig',
|
||||
'Select other board and port…'
|
||||
);
|
||||
return (
|
||||
<div
|
||||
className="arduino-boards-dropdown-list"
|
||||
@@ -57,17 +62,25 @@ export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
|
||||
...coords,
|
||||
}}
|
||||
>
|
||||
{this.renderItem({
|
||||
label: 'Select Other Board & Port',
|
||||
onClick: () => this.props.openBoardsConfig(),
|
||||
})}
|
||||
{items
|
||||
.map(({ name, port, selected, onClick }) => ({
|
||||
label: `${name} at ${Port.toString(port)}`,
|
||||
label: nls.localize(
|
||||
'arduino/board/boardListItem',
|
||||
'{0} at {1}',
|
||||
name,
|
||||
Port.toString(port)
|
||||
),
|
||||
selected,
|
||||
onClick,
|
||||
}))
|
||||
.map(this.renderItem)}
|
||||
<div
|
||||
key={footerLabel}
|
||||
className="arduino-boards-dropdown-item arduino-board-dropdown-footer"
|
||||
onClick={() => this.props.openBoardsConfig()}
|
||||
>
|
||||
<div>{footerLabel}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -152,7 +165,10 @@ export class BoardsToolBarItem extends React.Component<
|
||||
const { coords, availableBoards } = this.state;
|
||||
const boardsConfig = this.props.boardsServiceClient.boardsConfig;
|
||||
const title = BoardsConfig.Config.toString(boardsConfig, {
|
||||
default: 'no board selected',
|
||||
default: nls.localize(
|
||||
'arduino/common/noBoardSelected',
|
||||
'No board selected'
|
||||
),
|
||||
});
|
||||
const decorator = (() => {
|
||||
const selectedBoard = availableBoards.find(({ selected }) => selected);
|
||||
|
@@ -12,6 +12,7 @@ import {
|
||||
} from './contribution';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { ConfigService } from '../../common/protocol';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class About extends Contribution {
|
||||
@@ -30,7 +31,11 @@ export class About extends Contribution {
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.HELP__ABOUT_GROUP, {
|
||||
commandId: About.Commands.ABOUT_APP.id,
|
||||
label: `About ${this.applicationName}`,
|
||||
label: nls.localize(
|
||||
'arduino/about/label',
|
||||
'About {0}',
|
||||
this.applicationName
|
||||
),
|
||||
order: '0',
|
||||
});
|
||||
}
|
||||
@@ -42,16 +47,24 @@ export class About extends Contribution {
|
||||
status: cliStatus,
|
||||
} = await this.configService.getVersion();
|
||||
const buildDate = this.buildDate;
|
||||
const detail = (showAll: boolean) => `Version: ${remote.app.getVersion()}
|
||||
Date: ${buildDate ? buildDate : 'dev build'}${
|
||||
buildDate && showAll ? ` (${this.ago(buildDate)})` : ''
|
||||
}
|
||||
CLI Version: ${version}${cliStatus ? ` ${cliStatus}` : ''} [${commit}]
|
||||
|
||||
${showAll ? `Copyright © ${new Date().getFullYear()} Arduino SA` : ''}
|
||||
`;
|
||||
const ok = 'OK';
|
||||
const copy = 'Copy';
|
||||
const detail = (showAll: boolean) =>
|
||||
nls.localize(
|
||||
'arduino/about/detail',
|
||||
'Version: {0}\nDate: {1}{2}\nCLI Version: {3}{4} [{5}]\n\n{6}',
|
||||
remote.app.getVersion(),
|
||||
buildDate ? buildDate : nls.localize('', 'dev build'),
|
||||
buildDate && showAll ? ` (${this.ago(buildDate)})` : '',
|
||||
version,
|
||||
cliStatus ? ` ${cliStatus}` : '',
|
||||
commit,
|
||||
nls.localize(
|
||||
'arduino/about/copyright',
|
||||
'Copyright © {0} Arduino SA',
|
||||
new Date().getFullYear().toString()
|
||||
)
|
||||
);
|
||||
const ok = nls.localize('vscode/issueMainService/ok', 'OK');
|
||||
const copy = nls.localize('vscode/textInputActions/copy', 'Copy');
|
||||
const buttons = !isWindows && !isOSX ? [copy, ok] : [ok, copy];
|
||||
const { response } = await remote.dialog.showMessageBox(
|
||||
remote.getCurrentWindow(),
|
||||
@@ -85,26 +98,86 @@ ${showAll ? `Copyright © ${new Date().getFullYear()} Arduino SA` : ''}
|
||||
const other = moment(isoTime);
|
||||
let result = now.diff(other, 'minute');
|
||||
if (result < 60) {
|
||||
return result === 1 ? `${result} minute ago` : `${result} minute ago`;
|
||||
return result === 1
|
||||
? nls.localize(
|
||||
'vscode/date/date.fromNow.minutes.singular.ago',
|
||||
'{0} minute ago',
|
||||
result.toString()
|
||||
)
|
||||
: nls.localize(
|
||||
'vscode/date/date.fromNow.minutes.plural.ago',
|
||||
'{0} minutes ago',
|
||||
result.toString()
|
||||
);
|
||||
}
|
||||
result = now.diff(other, 'hour');
|
||||
if (result < 25) {
|
||||
return result === 1 ? `${result} hour ago` : `${result} hours ago`;
|
||||
return result === 1
|
||||
? nls.localize(
|
||||
'vscode/date/date.fromNow.hours.singular.ago',
|
||||
'{0} hour ago',
|
||||
result.toString()
|
||||
)
|
||||
: nls.localize(
|
||||
'vscode/date/date.fromNow.hours.plural.ago',
|
||||
'{0} hours ago',
|
||||
result.toString()
|
||||
);
|
||||
}
|
||||
result = now.diff(other, 'day');
|
||||
if (result < 8) {
|
||||
return result === 1 ? `${result} day ago` : `${result} days ago`;
|
||||
return result === 1
|
||||
? nls.localize(
|
||||
'vscode/date/date.fromNow.days.singular.ago',
|
||||
'{0} day ago',
|
||||
result.toString()
|
||||
)
|
||||
: nls.localize(
|
||||
'vscode/date/date.fromNow.days.plural.ago',
|
||||
'{0} days ago',
|
||||
result.toString()
|
||||
);
|
||||
}
|
||||
result = now.diff(other, 'week');
|
||||
if (result < 5) {
|
||||
return result === 1 ? `${result} week ago` : `${result} weeks ago`;
|
||||
return result === 1
|
||||
? nls.localize(
|
||||
'vscode/date/date.fromNow.weeks.singular.ago',
|
||||
'{0} week ago',
|
||||
result.toString()
|
||||
)
|
||||
: nls.localize(
|
||||
'vscode/date/date.fromNow.weeks.plural.ago',
|
||||
'{0} weeks ago',
|
||||
result.toString()
|
||||
);
|
||||
}
|
||||
result = now.diff(other, 'month');
|
||||
if (result < 13) {
|
||||
return result === 1 ? `${result} month ago` : `${result} months ago`;
|
||||
return result === 1
|
||||
? nls.localize(
|
||||
'vscode/date/date.fromNow.months.singular.ago',
|
||||
'{0} month ago',
|
||||
result.toString()
|
||||
)
|
||||
: nls.localize(
|
||||
'vscode/date/date.fromNow.months.plural.ago',
|
||||
'{0} months ago',
|
||||
result.toString()
|
||||
);
|
||||
}
|
||||
result = now.diff(other, 'year');
|
||||
return result === 1 ? `${result} year ago` : `${result} years ago`;
|
||||
return result === 1
|
||||
? nls.localize(
|
||||
'vscode/date/date.fromNow.years.singular.ago',
|
||||
'{0} year ago',
|
||||
result.toString()
|
||||
)
|
||||
: nls.localize(
|
||||
'vscode/date/date.fromNow.years.plural.ago',
|
||||
'{0} years ago',
|
||||
result.toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,7 @@ import {
|
||||
URI,
|
||||
} from './contribution';
|
||||
import { FileDialogService } from '@theia/filesystem/lib/browser';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class AddFile extends SketchContribution {
|
||||
@@ -24,7 +25,7 @@ export class AddFile extends SketchContribution {
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.SKETCH__UTILS_GROUP, {
|
||||
commandId: AddFile.Commands.ADD_FILE.id,
|
||||
label: 'Add File...',
|
||||
label: nls.localize('arduino/contributions/addFile', 'Add File') + '...',
|
||||
order: '2',
|
||||
});
|
||||
}
|
||||
@@ -35,7 +36,7 @@ export class AddFile extends SketchContribution {
|
||||
return;
|
||||
}
|
||||
const toAddUri = await this.fileDialogService.showOpenDialog({
|
||||
title: 'Add File',
|
||||
title: nls.localize('arduino/contributions/addFile', 'Add File'),
|
||||
canSelectFiles: true,
|
||||
canSelectFolders: false,
|
||||
canSelectMany: false,
|
||||
@@ -50,9 +51,16 @@ export class AddFile extends SketchContribution {
|
||||
if (exists) {
|
||||
const { response } = await remote.dialog.showMessageBox({
|
||||
type: 'question',
|
||||
title: 'Replace',
|
||||
buttons: ['Cancel', 'OK'],
|
||||
message: `Replace the existing version of ${filename}?`,
|
||||
title: nls.localize('arduino/contributions/replaceTitle', 'Replace'),
|
||||
buttons: [
|
||||
nls.localize('vscode/issueMainService/cancel', 'Cancel'),
|
||||
nls.localize('vscode/issueMainService/ok', 'OK'),
|
||||
],
|
||||
message: nls.localize(
|
||||
'arduino/replaceMsg',
|
||||
'Replace the existing version of {0}?',
|
||||
filename
|
||||
),
|
||||
});
|
||||
if (response === 0) {
|
||||
// Cancel
|
||||
@@ -60,9 +68,15 @@ export class AddFile extends SketchContribution {
|
||||
}
|
||||
}
|
||||
await this.fileService.copy(toAddUri, targetUri, { overwrite: true });
|
||||
this.messageService.info('One file added to the sketch.', {
|
||||
timeout: 2000,
|
||||
});
|
||||
this.messageService.info(
|
||||
nls.localize(
|
||||
'arduino/contributions/fileAdded',
|
||||
'One file added to the sketch.'
|
||||
),
|
||||
{
|
||||
timeout: 2000,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -4,22 +4,26 @@ import URI from '@theia/core/lib/common/uri';
|
||||
import { ConfirmDialog } from '@theia/core/lib/browser/dialogs';
|
||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { ResponseServiceImpl } from '../response-service-impl';
|
||||
import { Installable, LibraryService } from '../../common/protocol';
|
||||
import {
|
||||
Installable,
|
||||
LibraryService,
|
||||
ResponseServiceArduino,
|
||||
} from '../../common/protocol';
|
||||
import {
|
||||
SketchContribution,
|
||||
Command,
|
||||
CommandRegistry,
|
||||
MenuModelRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class AddZipLibrary extends SketchContribution {
|
||||
@inject(EnvVariablesServer)
|
||||
protected readonly envVariableServer: EnvVariablesServer;
|
||||
|
||||
@inject(ResponseServiceImpl)
|
||||
protected readonly responseService: ResponseServiceImpl;
|
||||
@inject(ResponseServiceArduino)
|
||||
protected readonly responseService: ResponseServiceArduino;
|
||||
|
||||
@inject(LibraryService)
|
||||
protected readonly libraryService: LibraryService;
|
||||
@@ -41,7 +45,7 @@ export class AddZipLibrary extends SketchContribution {
|
||||
});
|
||||
registry.registerMenuAction([...includeLibMenuPath, '1_install'], {
|
||||
commandId: AddZipLibrary.Commands.ADD_ZIP_LIBRARY.id,
|
||||
label: 'Add .ZIP Library...',
|
||||
label: nls.localize('arduino/library/addZip', 'Add .ZIP Library...'),
|
||||
order: '1',
|
||||
});
|
||||
}
|
||||
@@ -50,12 +54,15 @@ export class AddZipLibrary extends SketchContribution {
|
||||
const homeUri = await this.envVariableServer.getHomeDirUri();
|
||||
const defaultPath = await this.fileService.fsPath(new URI(homeUri));
|
||||
const { canceled, filePaths } = await remote.dialog.showOpenDialog({
|
||||
title: "Select a zip file containing the library you'd like to add",
|
||||
title: nls.localize(
|
||||
'arduino/selectZip',
|
||||
"Select a zip file containing the library you'd like to add"
|
||||
),
|
||||
defaultPath,
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{
|
||||
name: 'Library',
|
||||
name: nls.localize('arduino/library/zipLibrary', 'Library'),
|
||||
extensions: ['zip'],
|
||||
},
|
||||
],
|
||||
@@ -68,9 +75,12 @@ export class AddZipLibrary extends SketchContribution {
|
||||
if (error instanceof AlreadyInstalledError) {
|
||||
const result = await new ConfirmDialog({
|
||||
msg: error.message,
|
||||
title: 'Do you want to overwrite the existing library?',
|
||||
ok: 'Yes',
|
||||
cancel: 'No',
|
||||
title: nls.localize(
|
||||
'arduino/library/overwriteExistingLibrary',
|
||||
'Do you want to overwrite the existing library?'
|
||||
),
|
||||
ok: nls.localize('vscode/extensionsUtils/yes', 'Yes'),
|
||||
cancel: nls.localize('vscode/extensionsUtils/no', 'No'),
|
||||
}).open();
|
||||
if (result) {
|
||||
await this.doInstall(zipUri, true);
|
||||
@@ -84,14 +94,18 @@ export class AddZipLibrary extends SketchContribution {
|
||||
try {
|
||||
await Installable.doWithProgress({
|
||||
messageService: this.messageService,
|
||||
progressText: `Processing ${new URI(zipUri).path.base}`,
|
||||
progressText:
|
||||
nls.localize('arduino/common/processing', 'Processing') +
|
||||
` ${new URI(zipUri).path.base}`,
|
||||
responseService: this.responseService,
|
||||
run: () => this.libraryService.installZip({ zipUri, overwrite }),
|
||||
});
|
||||
this.messageService.info(
|
||||
`Successfully installed library from ${
|
||||
nls.localize(
|
||||
'arduino/library/successfullyInstalledZipLibrary',
|
||||
'Successfully installed library from {0} archive',
|
||||
new URI(zipUri).path.base
|
||||
} archive`,
|
||||
),
|
||||
{ timeout: 3000 }
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -101,12 +115,19 @@ export class AddZipLibrary extends SketchContribution {
|
||||
const name = match[1].trim();
|
||||
if (name) {
|
||||
throw new AlreadyInstalledError(
|
||||
`A library folder named ${name} already exists. Do you want to overwrite it?`,
|
||||
nls.localize(
|
||||
'arduino/library/namedLibraryAlreadyExists',
|
||||
'A library folder named {0} already exists. Do you want to overwrite it?',
|
||||
name
|
||||
),
|
||||
name
|
||||
);
|
||||
} else {
|
||||
throw new AlreadyInstalledError(
|
||||
'A library already exists. Do you want to overwrite it?'
|
||||
nls.localize(
|
||||
'arduino/library/libraryAlreadyExists',
|
||||
'A library already exists. Do you want to overwrite it?'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ import {
|
||||
CommandRegistry,
|
||||
MenuModelRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class ArchiveSketch extends SketchContribution {
|
||||
@@ -21,7 +22,7 @@ export class ArchiveSketch extends SketchContribution {
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, {
|
||||
commandId: ArchiveSketch.Commands.ARCHIVE_SKETCH.id,
|
||||
label: 'Archive Sketch',
|
||||
label: nls.localize('arduino/sketch/archiveSketch', 'Archive Sketch'),
|
||||
order: '1',
|
||||
});
|
||||
}
|
||||
@@ -42,7 +43,10 @@ export class ArchiveSketch extends SketchContribution {
|
||||
new URI(config.sketchDirUri).resolve(archiveBasename)
|
||||
);
|
||||
const { filePath, canceled } = await remote.dialog.showSaveDialog({
|
||||
title: 'Save sketch folder as...',
|
||||
title: nls.localize(
|
||||
'arduino/sketch/saveSketchAs',
|
||||
'Save sketch folder as...'
|
||||
),
|
||||
defaultPath,
|
||||
});
|
||||
if (!filePath || canceled) {
|
||||
@@ -53,9 +57,16 @@ export class ArchiveSketch extends SketchContribution {
|
||||
return;
|
||||
}
|
||||
await this.sketchService.archive(sketch, destinationUri.toString());
|
||||
this.messageService.info(`Created archive '${archiveBasename}'.`, {
|
||||
timeout: 2000,
|
||||
});
|
||||
this.messageService.info(
|
||||
nls.localize(
|
||||
'arduino/sketch/createdArchive',
|
||||
"Created archive '{0}'.",
|
||||
archiveBasename
|
||||
),
|
||||
{
|
||||
timeout: 2000,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -23,6 +23,7 @@ import {
|
||||
Port,
|
||||
} from '../../common/protocol';
|
||||
import { SketchContribution, Command, CommandRegistry } from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class BoardSelection extends SketchContribution {
|
||||
@@ -53,19 +54,29 @@ export class BoardSelection extends SketchContribution {
|
||||
this.boardsServiceProvider.boardsConfig;
|
||||
if (!selectedBoard) {
|
||||
this.messageService.info(
|
||||
'Please select a board to obtain board info.'
|
||||
nls.localize(
|
||||
'arduino/board/selectBoardForInfo',
|
||||
'Please select a board to obtain board info.'
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!selectedBoard.fqbn) {
|
||||
this.messageService.info(
|
||||
`The platform for the selected '${selectedBoard.name}' board is not installed.`
|
||||
nls.localize(
|
||||
'arduino/board/platformMissing',
|
||||
"The platform for the selected '{0}' board is not installed.",
|
||||
selectedBoard.name
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!selectedPort) {
|
||||
this.messageService.info(
|
||||
'Please select a port to obtain board info.'
|
||||
nls.localize(
|
||||
'arduino/board/selectPortForInfo',
|
||||
'Please select a port to obtain board info.'
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -78,11 +89,11 @@ export class BoardSelection extends SketchContribution {
|
||||
VID: ${VID}
|
||||
PID: ${PID}`;
|
||||
await remote.dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
message: 'Board Info',
|
||||
title: 'Board Info',
|
||||
message: nls.localize('arduino/board/boardInfo', 'Board Info'),
|
||||
title: nls.localize('arduino/board/boardInfo', 'Board Info'),
|
||||
type: 'info',
|
||||
detail,
|
||||
buttons: ['OK'],
|
||||
buttons: [nls.localize('vscode/issueMainService/ok', 'OK')],
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -99,6 +110,9 @@ PID: ${PID}`;
|
||||
this.boardsServiceProvider.onAvailableBoardsChanged(
|
||||
this.updateMenus.bind(this)
|
||||
);
|
||||
this.boardsServiceProvider.onAvailablePortsChanged(
|
||||
this.updateMenus.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
protected async updateMenus(): Promise<void> {
|
||||
@@ -127,7 +141,11 @@ PID: ${PID}`;
|
||||
// The board specific items, and the rest, have order with `z`. We needed something between `0` and `z` with natural-order.
|
||||
this.menuModelRegistry.registerSubmenu(
|
||||
boardsSubmenuPath,
|
||||
`Board${!!boardsSubmenuLabel ? `: "${boardsSubmenuLabel}"` : ''}`,
|
||||
nls.localize(
|
||||
'arduino/board/board',
|
||||
'Board{0}',
|
||||
!!boardsSubmenuLabel ? `: "${boardsSubmenuLabel}"` : ''
|
||||
),
|
||||
{ order: '100' }
|
||||
);
|
||||
this.toDisposeBeforeMenuRebuild.push(
|
||||
@@ -144,7 +162,11 @@ PID: ${PID}`;
|
||||
const portsSubmenuLabel = config.selectedPort?.address;
|
||||
this.menuModelRegistry.registerSubmenu(
|
||||
portsSubmenuPath,
|
||||
`Port${!!portsSubmenuLabel ? `: "${portsSubmenuLabel}"` : ''}`,
|
||||
nls.localize(
|
||||
'arduino/board/port',
|
||||
'Port{0}',
|
||||
portsSubmenuLabel ? `: "${portsSubmenuLabel}"` : ''
|
||||
),
|
||||
{ order: '101' }
|
||||
);
|
||||
this.toDisposeBeforeMenuRebuild.push(
|
||||
@@ -155,7 +177,7 @@ PID: ${PID}`;
|
||||
|
||||
const getBoardInfo = {
|
||||
commandId: BoardSelection.Commands.GET_BOARD_INFO.id,
|
||||
label: 'Get Board Info',
|
||||
label: nls.localize('arduino/board/getBoardInfo', 'Get Board Info'),
|
||||
order: '103',
|
||||
};
|
||||
this.menuModelRegistry.registerMenuAction(
|
||||
@@ -173,17 +195,26 @@ PID: ${PID}`;
|
||||
|
||||
this.menuModelRegistry.registerMenuAction(boardsManagerGroup, {
|
||||
commandId: `${BoardsListWidget.WIDGET_ID}:toggle`,
|
||||
label: 'Boards Manager...',
|
||||
label: `${BoardsListWidget.WIDGET_LABEL}...`,
|
||||
});
|
||||
|
||||
// Installed boards
|
||||
for (const board of installedBoards) {
|
||||
const { packageId, packageName, fqbn, name } = board;
|
||||
const { packageId, packageName, fqbn, name, manuallyInstalled } = board;
|
||||
|
||||
const packageLabel =
|
||||
packageName +
|
||||
`${
|
||||
manuallyInstalled
|
||||
? nls.localize('arduino/board/inSketchbook', ' (in Sketchbook)')
|
||||
: ''
|
||||
}`;
|
||||
// Platform submenu
|
||||
const platformMenuPath = [...boardsPackagesGroup, packageId];
|
||||
// Note: Registering the same submenu twice is a noop. No need to group the boards per platform.
|
||||
this.menuModelRegistry.registerSubmenu(platformMenuPath, packageName);
|
||||
this.menuModelRegistry.registerSubmenu(platformMenuPath, packageLabel, {
|
||||
order: packageName.toLowerCase(),
|
||||
});
|
||||
|
||||
const id = `arduino-select-board--${fqbn}`;
|
||||
const command = { id };
|
||||
@@ -219,19 +250,25 @@ PID: ${PID}`;
|
||||
}
|
||||
|
||||
// Installed ports
|
||||
const registerPorts = (ports: AvailablePorts) => {
|
||||
const registerPorts = (
|
||||
protocol: string,
|
||||
protocolOrder: number,
|
||||
ports: AvailablePorts
|
||||
) => {
|
||||
const addresses = Object.keys(ports);
|
||||
if (!addresses.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Register placeholder for protocol
|
||||
const [port] = ports[addresses[0]];
|
||||
const protocol = port.protocol;
|
||||
const menuPath = [...portsSubmenuPath, protocol];
|
||||
const menuPath = [
|
||||
...portsSubmenuPath,
|
||||
`${protocolOrder.toString()}_${protocol}`,
|
||||
];
|
||||
const placeholder = new PlaceholderMenuNode(
|
||||
menuPath,
|
||||
`${firstToUpperCase(port.protocol)} ports`
|
||||
`${firstToUpperCase(protocol)} ports`,
|
||||
{ order: protocolOrder.toString() }
|
||||
);
|
||||
this.menuModelRegistry.registerMenuNode(menuPath, placeholder);
|
||||
this.toDisposeBeforeMenuRebuild.push(
|
||||
@@ -240,63 +277,76 @@ PID: ${PID}`;
|
||||
)
|
||||
);
|
||||
|
||||
for (const address of addresses) {
|
||||
if (!!ports[address]) {
|
||||
const [port, boards] = ports[address];
|
||||
if (!boards.length) {
|
||||
boards.push({
|
||||
name: '',
|
||||
});
|
||||
}
|
||||
for (const { name, fqbn } of boards) {
|
||||
const id = `arduino-select-port--${address}${
|
||||
fqbn ? `--${fqbn}` : ''
|
||||
}`;
|
||||
const command = { id };
|
||||
const handler = {
|
||||
execute: () => {
|
||||
if (
|
||||
!Port.equals(
|
||||
port,
|
||||
this.boardsServiceProvider.boardsConfig.selectedPort
|
||||
)
|
||||
) {
|
||||
this.boardsServiceProvider.boardsConfig = {
|
||||
selectedBoard:
|
||||
this.boardsServiceProvider.boardsConfig.selectedBoard,
|
||||
selectedPort: port,
|
||||
};
|
||||
}
|
||||
},
|
||||
isToggled: () =>
|
||||
Port.equals(
|
||||
port,
|
||||
this.boardsServiceProvider.boardsConfig.selectedPort
|
||||
),
|
||||
};
|
||||
const label = `${address}${name ? ` (${name})` : ''}`;
|
||||
const menuAction = {
|
||||
commandId: id,
|
||||
label,
|
||||
order: `1${label}`, // `1` comes after the placeholder which has order `0`
|
||||
};
|
||||
this.commandRegistry.registerCommand(command, handler);
|
||||
this.toDisposeBeforeMenuRebuild.push(
|
||||
Disposable.create(() =>
|
||||
this.commandRegistry.unregisterCommand(command)
|
||||
)
|
||||
);
|
||||
this.menuModelRegistry.registerMenuAction(menuPath, menuAction);
|
||||
}
|
||||
// First we show addresses with recognized boards connected,
|
||||
// then all the rest.
|
||||
const sortedAddresses = Object.keys(ports);
|
||||
sortedAddresses.sort((left: string, right: string): number => {
|
||||
const [, leftBoards] = ports[left];
|
||||
const [, rightBoards] = ports[right];
|
||||
return rightBoards.length - leftBoards.length;
|
||||
});
|
||||
|
||||
for (let i = 0; i < sortedAddresses.length; i++) {
|
||||
const address = sortedAddresses[i];
|
||||
const [port, boards] = ports[address];
|
||||
let label = `${address}`;
|
||||
if (boards.length) {
|
||||
const boardsList = boards.map((board) => board.name).join(', ');
|
||||
label = `${label} (${boardsList})`;
|
||||
}
|
||||
const id = `arduino-select-port--${address}`;
|
||||
const command = { id };
|
||||
const handler = {
|
||||
execute: () => {
|
||||
if (
|
||||
!Port.equals(
|
||||
port,
|
||||
this.boardsServiceProvider.boardsConfig.selectedPort
|
||||
)
|
||||
) {
|
||||
this.boardsServiceProvider.boardsConfig = {
|
||||
selectedBoard:
|
||||
this.boardsServiceProvider.boardsConfig.selectedBoard,
|
||||
selectedPort: port,
|
||||
};
|
||||
}
|
||||
},
|
||||
isToggled: () =>
|
||||
Port.equals(
|
||||
port,
|
||||
this.boardsServiceProvider.boardsConfig.selectedPort
|
||||
),
|
||||
};
|
||||
const menuAction = {
|
||||
commandId: id,
|
||||
label,
|
||||
order: `${protocolOrder + i + 1}`,
|
||||
};
|
||||
this.commandRegistry.registerCommand(command, handler);
|
||||
this.toDisposeBeforeMenuRebuild.push(
|
||||
Disposable.create(() =>
|
||||
this.commandRegistry.unregisterCommand(command)
|
||||
)
|
||||
);
|
||||
this.menuModelRegistry.registerMenuAction(menuPath, menuAction);
|
||||
}
|
||||
};
|
||||
|
||||
const { serial, network, unknown } =
|
||||
AvailablePorts.groupByProtocol(availablePorts);
|
||||
registerPorts(serial);
|
||||
registerPorts(network);
|
||||
registerPorts(unknown);
|
||||
const grouped = AvailablePorts.byProtocol(availablePorts);
|
||||
let protocolOrder = 100;
|
||||
// We first show serial and network ports, then all the rest
|
||||
['serial', 'network'].forEach((protocol) => {
|
||||
const ports = grouped.get(protocol);
|
||||
if (ports) {
|
||||
registerPorts(protocol, protocolOrder, ports);
|
||||
grouped.delete(protocol);
|
||||
protocolOrder = protocolOrder + 100;
|
||||
}
|
||||
});
|
||||
grouped.forEach((ports, protocol) => {
|
||||
registerPorts(protocol, protocolOrder, ports);
|
||||
protocolOrder = protocolOrder + 100;
|
||||
});
|
||||
|
||||
this.mainMenuManager.update();
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { OutputChannelManager } from '@theia/output/lib/common/output-channel';
|
||||
import { OutputChannelManager } from '@theia/output/lib/browser/output-channel';
|
||||
import { CoreService } from '../../common/protocol';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { BoardsDataStore } from '../boards/boards-data-store';
|
||||
import { MonitorConnection } from '../monitor/monitor-connection';
|
||||
import { SerialConnectionManager } from '../serial/serial-connection-manager';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import {
|
||||
SketchContribution,
|
||||
@@ -11,14 +11,15 @@ import {
|
||||
CommandRegistry,
|
||||
MenuModelRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class BurnBootloader extends SketchContribution {
|
||||
@inject(CoreService)
|
||||
protected readonly coreService: CoreService;
|
||||
|
||||
@inject(MonitorConnection)
|
||||
protected readonly monitorConnection: MonitorConnection;
|
||||
@inject(SerialConnectionManager)
|
||||
protected readonly serialConnection: SerialConnectionManager;
|
||||
|
||||
@inject(BoardsDataStore)
|
||||
protected readonly boardsDataStore: BoardsDataStore;
|
||||
@@ -38,19 +39,18 @@ export class BurnBootloader extends SketchContribution {
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.TOOLS__BOARD_SETTINGS_GROUP, {
|
||||
commandId: BurnBootloader.Commands.BURN_BOOTLOADER.id,
|
||||
label: 'Burn Bootloader',
|
||||
label: nls.localize(
|
||||
'arduino/bootloader/burnBootloader',
|
||||
'Burn Bootloader'
|
||||
),
|
||||
order: 'z99',
|
||||
});
|
||||
}
|
||||
|
||||
async burnBootloader(): Promise<void> {
|
||||
const monitorConfig = this.monitorConnection.monitorConfig;
|
||||
if (monitorConfig) {
|
||||
await this.monitorConnection.disconnect();
|
||||
}
|
||||
try {
|
||||
const { boardsConfig } = this.boardsServiceClientImpl;
|
||||
const port = boardsConfig.selectedPort?.address;
|
||||
const port = boardsConfig.selectedPort;
|
||||
const [fqbn, { selectedProgrammer: programmer }, verify, verbose] =
|
||||
await Promise.all([
|
||||
this.boardsDataStore.appendConfigToFqbn(
|
||||
@@ -68,15 +68,25 @@ export class BurnBootloader extends SketchContribution {
|
||||
verify,
|
||||
verbose,
|
||||
});
|
||||
this.messageService.info('Done burning bootloader.', {
|
||||
timeout: 3000,
|
||||
});
|
||||
this.messageService.info(
|
||||
nls.localize(
|
||||
'arduino/bootloader/doneBurningBootloader',
|
||||
'Done burning bootloader.'
|
||||
),
|
||||
{
|
||||
timeout: 3000,
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
this.messageService.error(e.toString());
|
||||
} finally {
|
||||
if (monitorConfig) {
|
||||
await this.monitorConnection.connect(monitorConfig);
|
||||
let errorMessage = "";
|
||||
if (typeof e === "string") {
|
||||
errorMessage = e;
|
||||
} else {
|
||||
errorMessage = e.toString();
|
||||
}
|
||||
this.messageService.error(errorMessage);
|
||||
} finally {
|
||||
await this.serialConnection.reconnectAfterUpload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ import {
|
||||
KeybindingRegistry,
|
||||
URI,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
/**
|
||||
* Closes the `current` closeable editor, or any closeable current widget from the main area, or the current sketch window.
|
||||
@@ -64,10 +65,25 @@ export class Close extends SketchContribution {
|
||||
if (isTemp && (await this.wasTouched(uri))) {
|
||||
const { response } = await remote.dialog.showMessageBox({
|
||||
type: 'question',
|
||||
buttons: ["Don't Save", 'Cancel', 'Save'],
|
||||
message:
|
||||
'Do you want to save changes to this sketch before closing?',
|
||||
detail: "If you don't save, your changes will be lost.",
|
||||
buttons: [
|
||||
nls.localize(
|
||||
'vscode/abstractTaskService/saveBeforeRun.dontSave',
|
||||
"Don't Save"
|
||||
),
|
||||
nls.localize('vscode/issueMainService/cancel', 'Cancel'),
|
||||
nls.localize(
|
||||
'vscode/abstractTaskService/saveBeforeRun.save',
|
||||
'Save'
|
||||
),
|
||||
],
|
||||
message: nls.localize(
|
||||
'arduino/common/saveChangesToSketch',
|
||||
'Do you want to save changes to this sketch before closing?'
|
||||
),
|
||||
detail: nls.localize(
|
||||
'arduino/common/loseChanges',
|
||||
"If you don't save, your changes will be lost."
|
||||
),
|
||||
});
|
||||
if (response === 1) {
|
||||
// Cancel
|
||||
@@ -93,7 +109,7 @@ export class Close extends SketchContribution {
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
|
||||
commandId: Close.Commands.CLOSE.id,
|
||||
label: 'Close',
|
||||
label: nls.localize('vscode/editor.contribution/close', 'Close'),
|
||||
order: '5',
|
||||
});
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
||||
import { open, OpenerService } from '@theia/core/lib/browser/opener-service';
|
||||
import { OutputChannelManager } from '@theia/output/lib/common/output-channel';
|
||||
import { OutputChannelManager } from '@theia/output/lib/browser/output-channel';
|
||||
import {
|
||||
MenuModelRegistry,
|
||||
MenuContribution,
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
CommandService,
|
||||
} from '@theia/core/lib/common/command';
|
||||
import { EditorMode } from '../editor-mode';
|
||||
import { SettingsService } from '../settings';
|
||||
import { SettingsService } from '../dialogs/settings/settings';
|
||||
import { SketchesServiceClientImpl } from '../../common/protocol/sketches-service-client-impl';
|
||||
import {
|
||||
SketchesService,
|
||||
|
@@ -12,6 +12,7 @@ import {
|
||||
SketchContribution,
|
||||
TabBarToolbarRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class Debug extends SketchContribution {
|
||||
@@ -33,7 +34,10 @@ export class Debug extends SketchContribution {
|
||||
/**
|
||||
* If `undefined`, debugging is enabled. Otherwise, the reason why it's disabled.
|
||||
*/
|
||||
protected _disabledMessages?: string = 'No board selected'; // Initial pessimism.
|
||||
protected _disabledMessages?: string = nls.localize(
|
||||
'arduino/common/noBoardSelected',
|
||||
'No board selected'
|
||||
); // Initial pessimism.
|
||||
protected disabledMessageDidChangeEmitter = new Emitter<string | undefined>();
|
||||
protected onDisabledMessageDidChange =
|
||||
this.disabledMessageDidChangeEmitter.event;
|
||||
@@ -51,8 +55,12 @@ export class Debug extends SketchContribution {
|
||||
command: Debug.Commands.START_DEBUGGING.id,
|
||||
tooltip: `${
|
||||
this.disabledMessage
|
||||
? `Debug - ${this.disabledMessage}`
|
||||
: 'Start Debugging'
|
||||
? nls.localize(
|
||||
'arduino/debug/debugWithMessage',
|
||||
'Debug - {0}',
|
||||
this.disabledMessage
|
||||
)
|
||||
: Debug.Commands.START_DEBUGGING.label
|
||||
}`,
|
||||
priority: 3,
|
||||
onDidChange: this.onDisabledMessageDidChange as Event<void>,
|
||||
@@ -63,8 +71,12 @@ export class Debug extends SketchContribution {
|
||||
() =>
|
||||
(this.debugToolbarItem.tooltip = `${
|
||||
this.disabledMessage
|
||||
? `Debug - ${this.disabledMessage}`
|
||||
: 'Start Debugging'
|
||||
? nls.localize(
|
||||
'arduino/debug/debugWithMessage',
|
||||
'Debug - {0}',
|
||||
this.disabledMessage
|
||||
)
|
||||
: Debug.Commands.START_DEBUGGING.label
|
||||
}`)
|
||||
);
|
||||
const refreshState = async (
|
||||
@@ -72,22 +84,37 @@ export class Debug extends SketchContribution {
|
||||
.selectedBoard
|
||||
) => {
|
||||
if (!board) {
|
||||
this.disabledMessage = 'No board selected';
|
||||
this.disabledMessage = nls.localize(
|
||||
'arduino/common/noBoardSelected',
|
||||
'No board selected'
|
||||
);
|
||||
return;
|
||||
}
|
||||
const fqbn = board.fqbn;
|
||||
if (!fqbn) {
|
||||
this.disabledMessage = `Platform is not installed for '${board.name}'`;
|
||||
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 = `Platform is not installed for '${board.name}'`;
|
||||
this.disabledMessage = nls.localize(
|
||||
'arduino/debug/noPlatformInstalledFor',
|
||||
"Platform is not installed for '{0}'",
|
||||
board.name
|
||||
);
|
||||
return;
|
||||
}
|
||||
const { debuggingSupported } = details;
|
||||
if (!debuggingSupported) {
|
||||
this.disabledMessage = `Debugging is not supported by '${board.name}'`;
|
||||
this.disabledMessage = nls.localize(
|
||||
'arduino/debug/debuggingNotSupported',
|
||||
"Debugging is not supported by '{0}'",
|
||||
board.name
|
||||
);
|
||||
} else {
|
||||
this.disabledMessage = undefined;
|
||||
}
|
||||
@@ -155,10 +182,13 @@ export class Debug extends SketchContribution {
|
||||
|
||||
export namespace Debug {
|
||||
export namespace Commands {
|
||||
export const START_DEBUGGING: Command = {
|
||||
id: 'arduino-start-debug',
|
||||
label: 'Start Debugging',
|
||||
category: 'Arduino',
|
||||
};
|
||||
export const START_DEBUGGING = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'arduino-start-debug',
|
||||
label: 'Start Debugging',
|
||||
category: 'Arduino',
|
||||
},
|
||||
'vscode/debug.contribution/startDebuggingHelp'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import {
|
||||
CommandRegistry,
|
||||
} from './contribution';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
// TODO: [macOS]: to remove `Start Dictation...` and `Emoji & Symbol` see this thread: https://github.com/electron/electron/issues/8283#issuecomment-269522072
|
||||
// Depends on https://github.com/eclipse-theia/theia/pull/7964
|
||||
@@ -79,16 +80,6 @@ export class EditContributions extends Contribution {
|
||||
{ execute: () => this.run('editor.action.formatDocument') }
|
||||
);
|
||||
registry.registerCommand(EditContributions.Commands.COPY_FOR_FORUM, {
|
||||
execute: async () => {
|
||||
const value = await this.currentValue();
|
||||
if (value !== undefined) {
|
||||
this.clipboardService.writeText(`[code]
|
||||
${value}
|
||||
[/code]`);
|
||||
}
|
||||
},
|
||||
});
|
||||
registry.registerCommand(EditContributions.Commands.COPY_FOR_GITHUB, {
|
||||
execute: async () => {
|
||||
const value = await this.currentValue();
|
||||
if (value !== undefined) {
|
||||
@@ -111,80 +102,99 @@ ${value}
|
||||
});
|
||||
registry.registerMenuAction(ArduinoMenus.EDIT__TEXT_CONTROL_GROUP, {
|
||||
commandId: EditContributions.Commands.COPY_FOR_FORUM.id,
|
||||
label: 'Copy for Forum',
|
||||
label: nls.localize(
|
||||
'arduino/editor/copyForForum',
|
||||
'Copy for Forum (Markdown)'
|
||||
),
|
||||
order: '2',
|
||||
});
|
||||
registry.registerMenuAction(ArduinoMenus.EDIT__TEXT_CONTROL_GROUP, {
|
||||
commandId: EditContributions.Commands.COPY_FOR_GITHUB.id,
|
||||
label: 'Copy for GitHub',
|
||||
commandId: CommonCommands.PASTE.id,
|
||||
order: '3',
|
||||
});
|
||||
registry.registerMenuAction(ArduinoMenus.EDIT__TEXT_CONTROL_GROUP, {
|
||||
commandId: CommonCommands.PASTE.id,
|
||||
commandId: CommonCommands.SELECT_ALL.id,
|
||||
order: '4',
|
||||
});
|
||||
registry.registerMenuAction(ArduinoMenus.EDIT__TEXT_CONTROL_GROUP, {
|
||||
commandId: CommonCommands.SELECT_ALL.id,
|
||||
order: '5',
|
||||
});
|
||||
registry.registerMenuAction(ArduinoMenus.EDIT__TEXT_CONTROL_GROUP, {
|
||||
commandId: EditContributions.Commands.GO_TO_LINE.id,
|
||||
label: 'Go to Line...',
|
||||
order: '6',
|
||||
label: nls.localize(
|
||||
'vscode/standaloneStrings/gotoLineActionLabel',
|
||||
'Go to Line...'
|
||||
),
|
||||
order: '5',
|
||||
});
|
||||
|
||||
registry.registerMenuAction(ArduinoMenus.EDIT__CODE_CONTROL_GROUP, {
|
||||
commandId: EditContributions.Commands.TOGGLE_COMMENT.id,
|
||||
label: 'Comment/Uncomment',
|
||||
label: nls.localize(
|
||||
'arduino/editor/commentUncomment',
|
||||
'Comment/Uncomment'
|
||||
),
|
||||
order: '0',
|
||||
});
|
||||
registry.registerMenuAction(ArduinoMenus.EDIT__CODE_CONTROL_GROUP, {
|
||||
commandId: EditContributions.Commands.INDENT_LINES.id,
|
||||
label: 'Increase Indent',
|
||||
label: nls.localize('arduino/editor/increaseIndent', 'Increase Indent'),
|
||||
order: '1',
|
||||
});
|
||||
registry.registerMenuAction(ArduinoMenus.EDIT__CODE_CONTROL_GROUP, {
|
||||
commandId: EditContributions.Commands.OUTDENT_LINES.id,
|
||||
label: 'Decrease Indent',
|
||||
label: nls.localize('arduino/editor/decreaseIndent', 'Decrease Indent'),
|
||||
order: '2',
|
||||
});
|
||||
|
||||
registry.registerMenuAction(ArduinoMenus.EDIT__FONT_CONTROL_GROUP, {
|
||||
commandId: EditContributions.Commands.INCREASE_FONT_SIZE.id,
|
||||
label: 'Increase Font Size',
|
||||
label: nls.localize(
|
||||
'arduino/editor/increaseFontSize',
|
||||
'Increase Font Size'
|
||||
),
|
||||
order: '0',
|
||||
});
|
||||
registry.registerMenuAction(ArduinoMenus.EDIT__FONT_CONTROL_GROUP, {
|
||||
commandId: EditContributions.Commands.DECREASE_FONT_SIZE.id,
|
||||
label: 'Decrease Font Size',
|
||||
label: nls.localize(
|
||||
'arduino/editor/decreaseFontSize',
|
||||
'Decrease Font Size'
|
||||
),
|
||||
order: '1',
|
||||
});
|
||||
|
||||
registry.registerMenuAction(ArduinoMenus.EDIT__FIND_GROUP, {
|
||||
commandId: EditContributions.Commands.FIND.id,
|
||||
label: 'Find',
|
||||
label: nls.localize('vscode/findController/startFindAction', 'Find'),
|
||||
order: '0',
|
||||
});
|
||||
registry.registerMenuAction(ArduinoMenus.EDIT__FIND_GROUP, {
|
||||
commandId: EditContributions.Commands.FIND_NEXT.id,
|
||||
label: 'Find Next',
|
||||
label: nls.localize(
|
||||
'vscode/findController/findNextMatchAction',
|
||||
'Find Next'
|
||||
),
|
||||
order: '1',
|
||||
});
|
||||
registry.registerMenuAction(ArduinoMenus.EDIT__FIND_GROUP, {
|
||||
commandId: EditContributions.Commands.FIND_PREVIOUS.id,
|
||||
label: 'Find Previous',
|
||||
label: nls.localize(
|
||||
'vscode/findController/findPreviousMatchAction',
|
||||
'Find Previous'
|
||||
),
|
||||
order: '2',
|
||||
});
|
||||
registry.registerMenuAction(ArduinoMenus.EDIT__FIND_GROUP, {
|
||||
commandId: EditContributions.Commands.USE_FOR_FIND.id,
|
||||
label: 'Use Selection for Find', // XXX: The Java IDE uses `Use Selection For Find`.
|
||||
label: nls.localize(
|
||||
'vscode/findController/startFindWithSelectionAction',
|
||||
'Use Selection for Find'
|
||||
), // XXX: The Java IDE uses `Use Selection For Find`.
|
||||
order: '3',
|
||||
});
|
||||
|
||||
// `Tools`
|
||||
registry.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, {
|
||||
commandId: EditContributions.Commands.AUTO_FORMAT.id,
|
||||
label: 'Auto Format', // XXX: The Java IDE uses `Use Selection For Find`.
|
||||
label: nls.localize('arduino/editor/autoFormat', 'Auto Format'), // XXX: The Java IDE uses `Use Selection For Find`.
|
||||
order: '0',
|
||||
});
|
||||
}
|
||||
@@ -195,11 +205,6 @@ ${value}
|
||||
keybinding: 'CtrlCmd+Shift+C',
|
||||
when: 'editorFocus',
|
||||
});
|
||||
registry.registerKeybinding({
|
||||
command: EditContributions.Commands.COPY_FOR_GITHUB.id,
|
||||
keybinding: 'CtrlCmd+Alt+C',
|
||||
when: 'editorFocus',
|
||||
});
|
||||
registry.registerKeybinding({
|
||||
command: EditContributions.Commands.GO_TO_LINE.id,
|
||||
keybinding: 'CtrlCmd+L',
|
||||
@@ -280,9 +285,6 @@ export namespace EditContributions {
|
||||
export const COPY_FOR_FORUM: Command = {
|
||||
id: 'arduino-copy-for-forum',
|
||||
};
|
||||
export const COPY_FOR_GITHUB: Command = {
|
||||
id: 'arduino-copy-for-github',
|
||||
};
|
||||
export const GO_TO_LINE: Command = {
|
||||
id: 'arduino-go-to-line',
|
||||
};
|
||||
|
@@ -22,6 +22,7 @@ import {
|
||||
} from './contribution';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { Board, Sketch, SketchContainer } from '../../common/protocol';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export abstract class Examples extends SketchContribution {
|
||||
@@ -69,9 +70,13 @@ export abstract class Examples extends SketchContribution {
|
||||
}
|
||||
// Registering the same submenu multiple times has no side-effect.
|
||||
// TODO: unregister submenu? https://github.com/eclipse-theia/theia/issues/7300
|
||||
registry.registerSubmenu(ArduinoMenus.FILE__EXAMPLES_SUBMENU, 'Examples', {
|
||||
order: '4',
|
||||
});
|
||||
registry.registerSubmenu(
|
||||
ArduinoMenus.FILE__EXAMPLES_SUBMENU,
|
||||
nls.localize('arduino/examples/menu', 'Examples'),
|
||||
{
|
||||
order: '4',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
registerRecursively(
|
||||
@@ -166,11 +171,19 @@ export class BuiltInExamples extends Examples {
|
||||
sketchContainers = await this.examplesService.builtIns();
|
||||
} catch (e) {
|
||||
console.error('Could not initialize built-in examples.', e);
|
||||
this.messageService.error('Could not initialize built-in examples.');
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'arduino/examples/couldNotInitializeExamples',
|
||||
'Could not initialize built-in examples.'
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.toDispose.dispose();
|
||||
for (const container of ['Built-in examples', ...sketchContainers]) {
|
||||
for (const container of [
|
||||
nls.localize('arduino/examples/builtInExamples', 'Built-in examples'),
|
||||
...sketchContainers,
|
||||
]) {
|
||||
this.registerRecursively(
|
||||
container,
|
||||
ArduinoMenus.EXAMPLES__BUILT_IN_GROUP,
|
||||
@@ -211,13 +224,22 @@ export class LibraryExamples extends Examples {
|
||||
fqbn,
|
||||
});
|
||||
if (user.length) {
|
||||
(user as any).unshift('Examples from Custom Libraries');
|
||||
(user as any).unshift(
|
||||
nls.localize(
|
||||
'arduino/examples/customLibrary',
|
||||
'Examples from Custom Libraries'
|
||||
)
|
||||
);
|
||||
}
|
||||
if (name && fqbn && current.length) {
|
||||
(current as any).unshift(`Examples for ${name}`);
|
||||
(current as any).unshift(
|
||||
nls.localize('arduino/examples/for', 'Examples for {0}', name)
|
||||
);
|
||||
}
|
||||
if (any.length) {
|
||||
(any as any).unshift('Examples for any board');
|
||||
(any as any).unshift(
|
||||
nls.localize('arduino/examples/forAny', 'Examples for any board')
|
||||
);
|
||||
}
|
||||
for (const container of user) {
|
||||
this.registerRecursively(
|
||||
|
@@ -3,8 +3,8 @@ import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
||||
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { CommandHandler } from '@theia/core/lib/common/command';
|
||||
import { QuickInputService } from '@theia/core/lib/browser/quick-open/quick-input-service';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { QuickInputService } from '@theia/core/lib/browser/quick-input/quick-input-service';
|
||||
import {
|
||||
Contribution,
|
||||
Command,
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
CommandRegistry,
|
||||
KeybindingRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class Help extends Contribution {
|
||||
@@ -60,9 +61,9 @@ export class Help extends Contribution {
|
||||
}
|
||||
}
|
||||
if (!searchFor) {
|
||||
searchFor = await this.quickInputService.open({
|
||||
prompt: 'Search on Arduino.cc',
|
||||
placeHolder: 'Type a keyword',
|
||||
searchFor = await this.quickInputService.input({
|
||||
prompt: nls.localize('arduino/help/search', 'Search on Arduino.cc'),
|
||||
placeHolder: nls.localize('arduino/help/keyword', 'Type a keyword'),
|
||||
});
|
||||
}
|
||||
if (searchFor) {
|
||||
@@ -128,37 +129,37 @@ export namespace Help {
|
||||
export namespace Commands {
|
||||
export const GETTING_STARTED: Command = {
|
||||
id: 'arduino-getting-started',
|
||||
label: 'Getting Started',
|
||||
label: nls.localize('arduino/help/gettingStarted', 'Getting Started'),
|
||||
category: 'Arduino',
|
||||
};
|
||||
export const ENVIRONMENT: Command = {
|
||||
id: 'arduino-environment',
|
||||
label: 'Environment',
|
||||
label: nls.localize('arduino/help/environment', 'Environment'),
|
||||
category: 'Arduino',
|
||||
};
|
||||
export const TROUBLESHOOTING: Command = {
|
||||
id: 'arduino-troubleshooting',
|
||||
label: 'Troubleshooting',
|
||||
label: nls.localize('arduino/help/troubleshooting', 'Troubleshooting'),
|
||||
category: 'Arduino',
|
||||
};
|
||||
export const REFERENCE: Command = {
|
||||
id: 'arduino-reference',
|
||||
label: 'Reference',
|
||||
label: nls.localize('arduino/help/reference', 'Reference'),
|
||||
category: 'Arduino',
|
||||
};
|
||||
export const FIND_IN_REFERENCE: Command = {
|
||||
id: 'arduino-find-in-reference',
|
||||
label: 'Find in Reference',
|
||||
label: nls.localize('arduino/help/findInReference', 'Find in Reference'),
|
||||
category: 'Arduino',
|
||||
};
|
||||
export const FAQ: Command = {
|
||||
id: 'arduino-faq',
|
||||
label: 'Frequently Asked Questions',
|
||||
label: nls.localize('arduino/help/faq', 'Frequently Asked Questions'),
|
||||
category: 'Arduino',
|
||||
};
|
||||
export const VISIT_ARDUINO: Command = {
|
||||
id: 'arduino-visit-arduino',
|
||||
label: 'Visit Arduino.cc',
|
||||
label: nls.localize('arduino/help/visit', 'Visit Arduino.cc'),
|
||||
category: 'Arduino',
|
||||
};
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ import { LibraryListWidget } from '../library/library-list-widget';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import { SketchContribution, Command, CommandRegistry } from './contribution';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class IncludeLibrary extends SketchContribution {
|
||||
@@ -59,13 +60,20 @@ export class IncludeLibrary extends SketchContribution {
|
||||
...ArduinoMenus.SKETCH__UTILS_GROUP,
|
||||
'0_include',
|
||||
];
|
||||
registry.registerSubmenu(includeLibMenuPath, 'Include Library', {
|
||||
order: '1',
|
||||
});
|
||||
registry.registerSubmenu(
|
||||
includeLibMenuPath,
|
||||
nls.localize('arduino/library/include', 'Include Library'),
|
||||
{
|
||||
order: '1',
|
||||
}
|
||||
);
|
||||
// `Manage Libraries...` group.
|
||||
registry.registerMenuAction([...includeLibMenuPath, '0_manage'], {
|
||||
commandId: `${LibraryListWidget.WIDGET_ID}:toggle`,
|
||||
label: 'Manage Libraries...',
|
||||
label: nls.localize(
|
||||
'arduino/library/manageLibraries',
|
||||
'Manage Libraries...'
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -101,10 +109,17 @@ export class IncludeLibrary extends SketchContribution {
|
||||
const userMenuPath = [...includeLibMenuPath, '3_contributed'];
|
||||
const { user, rest } = LibraryPackage.groupByLocation(libraries);
|
||||
if (rest.length) {
|
||||
(rest as any).unshift('Arduino libraries');
|
||||
(rest as any).unshift(
|
||||
nls.localize('arduino/library/arduinoLibraries', 'Arduino libraries')
|
||||
);
|
||||
}
|
||||
if (user.length) {
|
||||
(user as any).unshift('Contributed libraries');
|
||||
(user as any).unshift(
|
||||
nls.localize(
|
||||
'arduino/library/contributedLibraries',
|
||||
'Contributed libraries'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
for (const library of user) {
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { injectable } from 'inversify';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
|
||||
@@ -27,7 +28,7 @@ export class NewSketch extends SketchContribution {
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
|
||||
commandId: NewSketch.Commands.NEW_SKETCH.id,
|
||||
label: 'New',
|
||||
label: nls.localize('arduino/sketch/new', 'New'),
|
||||
order: '0',
|
||||
});
|
||||
}
|
||||
@@ -43,7 +44,7 @@ export class NewSketch extends SketchContribution {
|
||||
registry.registerItem({
|
||||
id: NewSketch.Commands.NEW_SKETCH__TOOLBAR.id,
|
||||
command: NewSketch.Commands.NEW_SKETCH__TOOLBAR.id,
|
||||
tooltip: 'New',
|
||||
tooltip: nls.localize('arduino/sketch/new', 'New'),
|
||||
priority: 3,
|
||||
});
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { MainMenuManager } from '../../common/main-menu-manager';
|
||||
import { OpenSketch } from './open-sketch';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class OpenRecentSketch extends SketchContribution {
|
||||
@@ -48,7 +49,7 @@ export class OpenRecentSketch extends SketchContribution {
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerSubmenu(
|
||||
ArduinoMenus.FILE__OPEN_RECENT_SUBMENU,
|
||||
'Open Recent',
|
||||
nls.localize('arduino/sketch/openRecent', 'Open Recent'),
|
||||
{ order: '2' }
|
||||
);
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ import {
|
||||
MenuModelRegistry,
|
||||
KeybindingRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class OpenSketchExternal extends SketchContribution {
|
||||
@@ -21,7 +22,7 @@ export class OpenSketchExternal extends SketchContribution {
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.SKETCH__UTILS_GROUP, {
|
||||
commandId: OpenSketchExternal.Commands.OPEN_EXTERNAL.id,
|
||||
label: 'Show Sketch Folder',
|
||||
label: nls.localize('arduino/sketch/showFolder', 'Show Sketch Folder'),
|
||||
order: '0',
|
||||
});
|
||||
}
|
||||
@@ -36,7 +37,7 @@ export class OpenSketchExternal extends SketchContribution {
|
||||
protected async openExternal(): Promise<void> {
|
||||
const uri = await this.sketchServiceClient.currentSketchFile();
|
||||
if (uri) {
|
||||
const exists = this.fileService.exists(new URI(uri));
|
||||
const exists = await this.fileService.exists(new URI(uri));
|
||||
if (exists) {
|
||||
const fsPath = await this.fileService.fsPath(new URI(uri));
|
||||
if (fsPath) {
|
||||
|
@@ -22,6 +22,7 @@ import { ExamplesService } from '../../common/protocol/examples-service';
|
||||
import { BuiltInExamples } from './examples';
|
||||
import { Sketchbook } from './sketchbook';
|
||||
import { SketchContainer } from '../../common/protocol';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class OpenSketch extends SketchContribution {
|
||||
@@ -70,7 +71,10 @@ export class OpenSketch extends SketchContribution {
|
||||
ArduinoMenus.OPEN_SKETCH__CONTEXT__OPEN_GROUP,
|
||||
{
|
||||
commandId: OpenSketch.Commands.OPEN_SKETCH.id,
|
||||
label: 'Open...',
|
||||
label: nls.localize(
|
||||
'vscode/workspaceActions/openFileFolder',
|
||||
'Open...'
|
||||
),
|
||||
}
|
||||
);
|
||||
this.toDispose.push(
|
||||
@@ -115,7 +119,7 @@ export class OpenSketch extends SketchContribution {
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
|
||||
commandId: OpenSketch.Commands.OPEN_SKETCH.id,
|
||||
label: 'Open...',
|
||||
label: nls.localize('vscode/workspaceActions/openFileFolder', 'Open...'),
|
||||
order: '1',
|
||||
});
|
||||
}
|
||||
@@ -131,7 +135,7 @@ export class OpenSketch extends SketchContribution {
|
||||
registry.registerItem({
|
||||
id: OpenSketch.Commands.OPEN_SKETCH__TOOLBAR.id,
|
||||
command: OpenSketch.Commands.OPEN_SKETCH__TOOLBAR.id,
|
||||
tooltip: 'Open',
|
||||
tooltip: nls.localize('vscode/dialogMainService/open', 'Open'),
|
||||
priority: 4,
|
||||
});
|
||||
}
|
||||
@@ -155,7 +159,7 @@ export class OpenSketch extends SketchContribution {
|
||||
properties: ['createDirectory', 'openFile'],
|
||||
filters: [
|
||||
{
|
||||
name: 'Sketch',
|
||||
name: nls.localize('arduino/sketch/sketch', 'Sketch'),
|
||||
extensions: ['ino', 'pde'],
|
||||
},
|
||||
],
|
||||
@@ -178,10 +182,18 @@ export class OpenSketch extends SketchContribution {
|
||||
const name = new URI(sketchFileUri).path.name;
|
||||
const nameWithExt = this.labelProvider.getName(new URI(sketchFileUri));
|
||||
const { response } = await remote.dialog.showMessageBox({
|
||||
title: 'Moving',
|
||||
title: nls.localize('arduino/sketch/moving', 'Moving'),
|
||||
type: 'question',
|
||||
buttons: ['Cancel', 'OK'],
|
||||
message: `The file "${nameWithExt}" needs to be inside a sketch folder named as "${name}".\nCreate this folder, move the file, and continue?`,
|
||||
buttons: [
|
||||
nls.localize('vscode/issueMainService/cancel', 'Cancel'),
|
||||
nls.localize('vscode/issueMainService/ok', 'OK'),
|
||||
],
|
||||
message: nls.localize(
|
||||
'arduino/sketch/movingMsg',
|
||||
'The file "{0}" needs to be inside a sketch folder named as "{1}".\nCreate this folder, move the file, and continue?',
|
||||
nameWithExt,
|
||||
name
|
||||
),
|
||||
});
|
||||
if (response === 1) {
|
||||
// OK
|
||||
@@ -190,8 +202,12 @@ export class OpenSketch extends SketchContribution {
|
||||
if (exists) {
|
||||
await remote.dialog.showMessageBox({
|
||||
type: 'error',
|
||||
title: 'Error',
|
||||
message: `A folder named "${name}" already exists. Can't open sketch.`,
|
||||
title: nls.localize('vscode/dialog/dialogErrorMessage', 'Error'),
|
||||
message: nls.localize(
|
||||
'arduino/sketch/cantOpen',
|
||||
'A folder named "{0}" already exists. Can\'t open sketch.',
|
||||
name
|
||||
),
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ import {
|
||||
CommandRegistry,
|
||||
} from './contribution';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class QuitApp extends Contribution {
|
||||
@@ -25,7 +26,7 @@ export class QuitApp extends Contribution {
|
||||
if (!isOSX) {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__QUIT_GROUP, {
|
||||
commandId: QuitApp.Commands.QUIT_APP.id,
|
||||
label: 'Quit',
|
||||
label: nls.localize('vscode/bulkEditService/quit', 'Quit'),
|
||||
order: '0',
|
||||
});
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ import {
|
||||
MenuModelRegistry,
|
||||
KeybindingRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class SaveAsSketch extends SketchContribution {
|
||||
@@ -22,7 +23,7 @@ export class SaveAsSketch extends SketchContribution {
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
|
||||
commandId: SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
|
||||
label: 'Save As...',
|
||||
label: nls.localize('vscode/fileCommands/saveAs', 'Save As...'),
|
||||
order: '7',
|
||||
});
|
||||
}
|
||||
@@ -73,7 +74,10 @@ export class SaveAsSketch extends SketchContribution {
|
||||
: sketchDirUri.resolve(sketch.name);
|
||||
const defaultPath = await this.fileService.fsPath(defaultUri);
|
||||
const { filePath, canceled } = await remote.dialog.showSaveDialog({
|
||||
title: 'Save sketch folder as...',
|
||||
title: nls.localize(
|
||||
'arduino/sketch/saveFolderAs',
|
||||
'Save sketch folder as...'
|
||||
),
|
||||
defaultPath,
|
||||
});
|
||||
if (!filePath || canceled) {
|
||||
|
@@ -2,6 +2,7 @@ import { injectable } from 'inversify';
|
||||
import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
|
||||
import { SaveAsSketch } from './save-as-sketch';
|
||||
import {
|
||||
SketchContribution,
|
||||
Command,
|
||||
@@ -10,6 +11,7 @@ import {
|
||||
KeybindingRegistry,
|
||||
TabBarToolbarRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class SaveSketch extends SketchContribution {
|
||||
@@ -28,7 +30,7 @@ export class SaveSketch extends SketchContribution {
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
|
||||
commandId: SaveSketch.Commands.SAVE_SKETCH.id,
|
||||
label: 'Save',
|
||||
label: nls.localize('vscode/fileCommands/save', 'Save'),
|
||||
order: '6',
|
||||
});
|
||||
}
|
||||
@@ -44,12 +46,28 @@ export class SaveSketch extends SketchContribution {
|
||||
registry.registerItem({
|
||||
id: SaveSketch.Commands.SAVE_SKETCH__TOOLBAR.id,
|
||||
command: SaveSketch.Commands.SAVE_SKETCH__TOOLBAR.id,
|
||||
tooltip: 'Save',
|
||||
tooltip: nls.localize('vscode/fileCommands/save', 'Save'),
|
||||
priority: 5,
|
||||
});
|
||||
}
|
||||
|
||||
async saveSketch(): Promise<void> {
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
if (!sketch) {
|
||||
return;
|
||||
}
|
||||
const isTemp = await this.sketchService.isTemp(sketch);
|
||||
if (isTemp) {
|
||||
return this.commandService.executeCommand(
|
||||
SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
|
||||
{
|
||||
execOnlyIfTemp: false,
|
||||
openAfterMove: true,
|
||||
wipeOriginal: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return this.commandService.executeCommand(CommonCommands.SAVE_ALL.id);
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,9 @@ import {
|
||||
KeybindingRegistry,
|
||||
} from './contribution';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { Settings as Preferences, SettingsDialog } from '../settings';
|
||||
import { Settings as Preferences } from '../dialogs/settings/settings';
|
||||
import { SettingsDialog } from '../dialogs/settings/settings-dialog';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class Settings extends SketchContribution {
|
||||
@@ -40,7 +42,11 @@ export class Settings extends SketchContribution {
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__PREFERENCES_GROUP, {
|
||||
commandId: Settings.Commands.OPEN.id,
|
||||
label: 'Preferences...',
|
||||
label:
|
||||
nls.localize(
|
||||
'vscode/preferences.contribution/preferences',
|
||||
'Preferences'
|
||||
) + '...',
|
||||
order: '0',
|
||||
});
|
||||
registry.registerSubmenu(ArduinoMenus.FILE__ADVANCED_SUBMENU, 'Advanced');
|
||||
@@ -58,7 +64,11 @@ export namespace Settings {
|
||||
export namespace Commands {
|
||||
export const OPEN: Command = {
|
||||
id: 'arduino-settings-open',
|
||||
label: 'Open Preferences...',
|
||||
label:
|
||||
nls.localize(
|
||||
'vscode/preferences.contribution/openSettings2',
|
||||
'Open Preferences'
|
||||
) + '...',
|
||||
category: 'Arduino',
|
||||
};
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus';
|
||||
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { SketchesServiceClientImpl } from '../../common/protocol/sketches-service-client-impl';
|
||||
import { LocalCacheFsProvider } from '../local-cache/local-cache-fs-provider';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class SketchControl extends SketchContribution {
|
||||
@@ -87,13 +88,13 @@ export class SketchControl extends SketchContribution {
|
||||
currentSketch &&
|
||||
parentsketch &&
|
||||
parentsketch.uri === currentSketch.uri &&
|
||||
(await this.allowRename(parentsketch.uri))
|
||||
this.allowRename(parentsketch.uri)
|
||||
) {
|
||||
this.menuRegistry.registerMenuAction(
|
||||
ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP,
|
||||
{
|
||||
commandId: WorkspaceCommands.FILE_RENAME.id,
|
||||
label: 'Rename',
|
||||
label: nls.localize('vscode/fileActions/rename', 'Rename'),
|
||||
order: '1',
|
||||
}
|
||||
);
|
||||
@@ -107,7 +108,7 @@ export class SketchControl extends SketchContribution {
|
||||
} else {
|
||||
const renamePlaceholder = new PlaceholderMenuNode(
|
||||
ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP,
|
||||
'Rename'
|
||||
nls.localize('vscode/fileActions/rename', 'Rename')
|
||||
);
|
||||
this.menuRegistry.registerMenuNode(
|
||||
ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP,
|
||||
@@ -124,13 +125,13 @@ export class SketchControl extends SketchContribution {
|
||||
currentSketch &&
|
||||
parentsketch &&
|
||||
parentsketch.uri === currentSketch.uri &&
|
||||
(await this.allowDelete(parentsketch.uri))
|
||||
this.allowDelete(parentsketch.uri)
|
||||
) {
|
||||
this.menuRegistry.registerMenuAction(
|
||||
ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP,
|
||||
{
|
||||
commandId: WorkspaceCommands.FILE_DELETE.id, // TODO: customize delete. Wipe sketch if deleting main file. Close window.
|
||||
label: 'Delete',
|
||||
label: nls.localize('vscode/fileActions/delete', 'Delete'),
|
||||
order: '2',
|
||||
}
|
||||
);
|
||||
@@ -144,7 +145,7 @@ export class SketchControl extends SketchContribution {
|
||||
} else {
|
||||
const deletePlaceholder = new PlaceholderMenuNode(
|
||||
ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP,
|
||||
'Delete'
|
||||
nls.localize('vscode/fileActions/delete', 'Delete')
|
||||
);
|
||||
this.menuRegistry.registerMenuNode(
|
||||
ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP,
|
||||
@@ -204,7 +205,7 @@ export class SketchControl extends SketchContribution {
|
||||
ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP,
|
||||
{
|
||||
commandId: WorkspaceCommands.NEW_FILE.id,
|
||||
label: 'New Tab',
|
||||
label: nls.localize('vscode/menubar/mNewTab', 'New Tab'),
|
||||
order: '0',
|
||||
}
|
||||
);
|
||||
@@ -213,7 +214,7 @@ export class SketchControl extends SketchContribution {
|
||||
ArduinoMenus.SKETCH_CONTROL__CONTEXT__NAVIGATION_GROUP,
|
||||
{
|
||||
commandId: CommonCommands.PREVIOUS_TAB.id,
|
||||
label: 'Previous Tab',
|
||||
label: nls.localize('vscode/menubar/mShowPreviousTab', 'Previous Tab'),
|
||||
order: '0',
|
||||
}
|
||||
);
|
||||
@@ -221,7 +222,7 @@ export class SketchControl extends SketchContribution {
|
||||
ArduinoMenus.SKETCH_CONTROL__CONTEXT__NAVIGATION_GROUP,
|
||||
{
|
||||
commandId: CommonCommands.NEXT_TAB.id,
|
||||
label: 'Next Tab',
|
||||
label: nls.localize('vscode/menubar/mShowNextTab', 'Next Tab'),
|
||||
order: '0',
|
||||
}
|
||||
);
|
||||
@@ -249,20 +250,24 @@ export class SketchControl extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
protected async isCloudSketch(uri: string) {
|
||||
const cloudCacheLocation = this.localCacheFsProvider.from(new URI(uri));
|
||||
protected isCloudSketch(uri: string): boolean {
|
||||
try {
|
||||
const cloudCacheLocation = this.localCacheFsProvider.from(new URI(uri));
|
||||
|
||||
if (cloudCacheLocation) {
|
||||
return true;
|
||||
if (cloudCacheLocation) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected async allowRename(uri: string) {
|
||||
protected allowRename(uri: string): boolean {
|
||||
return !this.isCloudSketch(uri);
|
||||
}
|
||||
|
||||
protected async allowDelete(uri: string) {
|
||||
protected allowDelete(uri: string): boolean {
|
||||
return !this.isCloudSketch(uri);
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ import { NotificationCenter } from '../notification-center';
|
||||
import { Examples } from './examples';
|
||||
import { SketchContainer } from '../../common/protocol';
|
||||
import { OpenSketch } from './open-sketch';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class Sketchbook extends Examples {
|
||||
@@ -38,7 +39,7 @@ export class Sketchbook extends Examples {
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerSubmenu(
|
||||
ArduinoMenus.FILE__SKETCHBOOK_SUBMENU,
|
||||
'Sketchbook',
|
||||
nls.localize('arduino/sketch/sketchbook', 'Sketchbook'),
|
||||
{ order: '3' }
|
||||
);
|
||||
}
|
||||
|
@@ -0,0 +1,140 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import {
|
||||
Command,
|
||||
MenuModelRegistry,
|
||||
CommandRegistry,
|
||||
Contribution,
|
||||
} from './contribution';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { UploadCertificateDialog } from '../dialogs/certificate-uploader/certificate-uploader-dialog';
|
||||
import { ContextMenuRenderer } from '@theia/core/lib/browser/context-menu-renderer';
|
||||
import {
|
||||
PreferenceScope,
|
||||
PreferenceService,
|
||||
} from '@theia/core/lib/browser/preferences/preference-service';
|
||||
import { ArduinoPreferences } from '../arduino-preferences';
|
||||
import {
|
||||
arduinoCert,
|
||||
certificateList,
|
||||
} from '../dialogs/certificate-uploader/utils';
|
||||
import { ArduinoFirmwareUploader } from '../../common/protocol/arduino-firmware-uploader';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class UploadCertificate extends Contribution {
|
||||
@inject(UploadCertificateDialog)
|
||||
protected readonly dialog: UploadCertificateDialog;
|
||||
|
||||
@inject(ContextMenuRenderer)
|
||||
protected readonly contextMenuRenderer: ContextMenuRenderer;
|
||||
|
||||
@inject(PreferenceService)
|
||||
protected readonly preferenceService: PreferenceService;
|
||||
|
||||
@inject(ArduinoPreferences)
|
||||
protected readonly arduinoPreferences: ArduinoPreferences;
|
||||
|
||||
@inject(ArduinoFirmwareUploader)
|
||||
protected readonly arduinoFirmwareUploader: ArduinoFirmwareUploader;
|
||||
|
||||
protected dialogOpened = false;
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(UploadCertificate.Commands.OPEN, {
|
||||
execute: async () => {
|
||||
try {
|
||||
this.dialogOpened = true;
|
||||
await this.dialog.open();
|
||||
} finally {
|
||||
this.dialogOpened = false;
|
||||
}
|
||||
},
|
||||
isEnabled: () => !this.dialogOpened,
|
||||
});
|
||||
|
||||
registry.registerCommand(UploadCertificate.Commands.REMOVE_CERT, {
|
||||
execute: async (certToRemove) => {
|
||||
const certs = this.arduinoPreferences.get('arduino.board.certificates');
|
||||
|
||||
this.preferenceService.set(
|
||||
'arduino.board.certificates',
|
||||
certificateList(certs)
|
||||
.filter((c) => c !== certToRemove)
|
||||
.join(','),
|
||||
PreferenceScope.User
|
||||
);
|
||||
},
|
||||
isEnabled: (certToRemove) => certToRemove !== arduinoCert,
|
||||
});
|
||||
|
||||
registry.registerCommand(UploadCertificate.Commands.UPLOAD_CERT, {
|
||||
execute: async ({ fqbn, address, urls }) => {
|
||||
return this.arduinoFirmwareUploader.uploadCertificates(
|
||||
`-b ${fqbn} -a ${address} ${urls
|
||||
.map((url: string) => `-u ${url}`)
|
||||
.join(' ')}`
|
||||
);
|
||||
},
|
||||
isEnabled: () => true,
|
||||
});
|
||||
|
||||
registry.registerCommand(UploadCertificate.Commands.OPEN_CERT_CONTEXT, {
|
||||
execute: async (args: any) => {
|
||||
this.contextMenuRenderer.render({
|
||||
menuPath: ArduinoMenus.ROOT_CERTIFICATES__CONTEXT,
|
||||
anchor: {
|
||||
x: args.x,
|
||||
y: args.y,
|
||||
},
|
||||
args: [args.cert],
|
||||
});
|
||||
},
|
||||
isEnabled: () => true,
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.TOOLS__FIRMWARE_UPLOADER_GROUP, {
|
||||
commandId: UploadCertificate.Commands.OPEN.id,
|
||||
label: UploadCertificate.Commands.OPEN.label,
|
||||
order: '1',
|
||||
});
|
||||
|
||||
registry.registerMenuAction(ArduinoMenus.ROOT_CERTIFICATES__CONTEXT, {
|
||||
commandId: UploadCertificate.Commands.REMOVE_CERT.id,
|
||||
label: UploadCertificate.Commands.REMOVE_CERT.label,
|
||||
order: '1',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export namespace UploadCertificate {
|
||||
export namespace Commands {
|
||||
export const OPEN: Command = {
|
||||
id: 'arduino-upload-certificate-open',
|
||||
label: nls.localize(
|
||||
'arduino/certificate/uploadRootCertificates',
|
||||
'Upload SSL Root Certificates'
|
||||
),
|
||||
category: 'Arduino',
|
||||
};
|
||||
|
||||
export const OPEN_CERT_CONTEXT: Command = {
|
||||
id: 'arduino-certificate-open-context',
|
||||
label: nls.localize('arduino/certificate/openContext', 'Open context'),
|
||||
category: 'Arduino',
|
||||
};
|
||||
|
||||
export const REMOVE_CERT: Command = {
|
||||
id: 'arduino-certificate-remove',
|
||||
label: nls.localize('arduino/certificate/remove', 'Remove'),
|
||||
category: 'Arduino',
|
||||
};
|
||||
|
||||
export const UPLOAD_CERT: Command = {
|
||||
id: 'arduino-certificate-upload',
|
||||
label: nls.localize('arduino/certificate/upload', 'Upload'),
|
||||
category: 'Arduino',
|
||||
};
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import {
|
||||
Command,
|
||||
MenuModelRegistry,
|
||||
CommandRegistry,
|
||||
Contribution,
|
||||
} from './contribution';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { UploadFirmwareDialog } from '../dialogs/firmware-uploader/firmware-uploader-dialog';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class UploadFirmware extends Contribution {
|
||||
@inject(UploadFirmwareDialog)
|
||||
protected readonly dialog: UploadFirmwareDialog;
|
||||
|
||||
protected dialogOpened = false;
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(UploadFirmware.Commands.OPEN, {
|
||||
execute: async () => {
|
||||
try {
|
||||
this.dialogOpened = true;
|
||||
await this.dialog.open();
|
||||
} finally {
|
||||
this.dialogOpened = false;
|
||||
}
|
||||
},
|
||||
isEnabled: () => !this.dialogOpened,
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.TOOLS__FIRMWARE_UPLOADER_GROUP, {
|
||||
commandId: UploadFirmware.Commands.OPEN.id,
|
||||
label: UploadFirmware.Commands.OPEN.label,
|
||||
order: '0',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export namespace UploadFirmware {
|
||||
export namespace Commands {
|
||||
export const OPEN: Command = {
|
||||
id: 'arduino-upload-firmware-open',
|
||||
label: nls.localize(
|
||||
'arduino/firmware/updater',
|
||||
'WiFi101 / WiFiNINA Firmware Updater'
|
||||
),
|
||||
category: 'Arduino',
|
||||
};
|
||||
}
|
||||
}
|
@@ -1,10 +1,10 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { CoreService } from '../../common/protocol';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { BoardUserField, CoreService } from '../../common/protocol';
|
||||
import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus';
|
||||
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
|
||||
import { BoardsDataStore } from '../boards/boards-data-store';
|
||||
import { MonitorConnection } from '../monitor/monitor-connection';
|
||||
import { SerialConnectionManager } from '../serial/serial-connection-manager';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import {
|
||||
SketchContribution,
|
||||
@@ -14,14 +14,19 @@ import {
|
||||
KeybindingRegistry,
|
||||
TabBarToolbarRegistry,
|
||||
} from './contribution';
|
||||
import { UserFieldsDialog } from '../dialogs/user-fields/user-fields-dialog';
|
||||
import { DisposableCollection, nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class UploadSketch extends SketchContribution {
|
||||
@inject(CoreService)
|
||||
protected readonly coreService: CoreService;
|
||||
|
||||
@inject(MonitorConnection)
|
||||
protected readonly monitorConnection: MonitorConnection;
|
||||
@inject(SerialConnectionManager)
|
||||
protected readonly serialConnection: SerialConnectionManager;
|
||||
|
||||
@inject(MenuModelRegistry)
|
||||
protected readonly menuRegistry: MenuModelRegistry;
|
||||
|
||||
@inject(BoardsDataStore)
|
||||
protected readonly boardsDataStore: BoardsDataStore;
|
||||
@@ -29,16 +34,89 @@ export class UploadSketch extends SketchContribution {
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceClientImpl: BoardsServiceProvider;
|
||||
|
||||
@inject(UserFieldsDialog)
|
||||
protected readonly userFieldsDialog: UserFieldsDialog;
|
||||
|
||||
protected cachedUserFields: Map<string, BoardUserField[]> = new Map();
|
||||
|
||||
protected readonly onDidChangeEmitter = new Emitter<Readonly<void>>();
|
||||
readonly onDidChange = this.onDidChangeEmitter.event;
|
||||
|
||||
protected uploadInProgress = false;
|
||||
protected boardRequiresUserFields = false;
|
||||
|
||||
protected readonly menuActionsDisposables = new DisposableCollection();
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.boardsServiceClientImpl.onBoardsConfigChanged(async () => {
|
||||
const userFields =
|
||||
await this.boardsServiceClientImpl.selectedBoardUserFields();
|
||||
this.boardRequiresUserFields = userFields.length > 0;
|
||||
this.registerMenus(this.menuRegistry);
|
||||
});
|
||||
}
|
||||
|
||||
private selectedFqbnAddress(): string {
|
||||
const { boardsConfig } = this.boardsServiceClientImpl;
|
||||
const fqbn = boardsConfig.selectedBoard?.fqbn;
|
||||
if (!fqbn) {
|
||||
return '';
|
||||
}
|
||||
const address =
|
||||
boardsConfig.selectedBoard?.port?.address ||
|
||||
boardsConfig.selectedPort?.address;
|
||||
if (!address) {
|
||||
return '';
|
||||
}
|
||||
return fqbn + '|' + address;
|
||||
}
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(UploadSketch.Commands.UPLOAD_SKETCH, {
|
||||
execute: () => this.uploadSketch(),
|
||||
execute: async () => {
|
||||
const key = this.selectedFqbnAddress();
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
if (this.boardRequiresUserFields && !this.cachedUserFields.has(key)) {
|
||||
// Deep clone the array of board fields to avoid editing the cached ones
|
||||
this.userFieldsDialog.value = (
|
||||
await this.boardsServiceClientImpl.selectedBoardUserFields()
|
||||
).map((f) => ({ ...f }));
|
||||
const result = await this.userFieldsDialog.open();
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
this.cachedUserFields.set(key, result);
|
||||
}
|
||||
this.uploadSketch();
|
||||
},
|
||||
isEnabled: () => !this.uploadInProgress,
|
||||
});
|
||||
registry.registerCommand(UploadSketch.Commands.UPLOAD_WITH_CONFIGURATION, {
|
||||
execute: async () => {
|
||||
const key = this.selectedFqbnAddress();
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cached = this.cachedUserFields.get(key);
|
||||
// Deep clone the array of board fields to avoid editing the cached ones
|
||||
this.userFieldsDialog.value = (
|
||||
cached ??
|
||||
(await this.boardsServiceClientImpl.selectedBoardUserFields())
|
||||
).map((f) => ({ ...f }));
|
||||
|
||||
const result = await this.userFieldsDialog.open();
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
this.cachedUserFields.set(key, result);
|
||||
this.uploadSketch();
|
||||
},
|
||||
isEnabled: () => !this.uploadInProgress && this.boardRequiresUserFields,
|
||||
});
|
||||
registry.registerCommand(
|
||||
UploadSketch.Commands.UPLOAD_SKETCH_USING_PROGRAMMER,
|
||||
{
|
||||
@@ -57,16 +135,46 @@ export class UploadSketch extends SketchContribution {
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, {
|
||||
commandId: UploadSketch.Commands.UPLOAD_SKETCH.id,
|
||||
label: 'Upload',
|
||||
order: '1',
|
||||
});
|
||||
registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, {
|
||||
commandId: UploadSketch.Commands.UPLOAD_SKETCH_USING_PROGRAMMER.id,
|
||||
label: 'Upload Using Programmer',
|
||||
order: '2',
|
||||
});
|
||||
this.menuActionsDisposables.dispose();
|
||||
|
||||
this.menuActionsDisposables.push(
|
||||
registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, {
|
||||
commandId: UploadSketch.Commands.UPLOAD_SKETCH.id,
|
||||
label: nls.localize('arduino/sketch/upload', 'Upload'),
|
||||
order: '1',
|
||||
})
|
||||
);
|
||||
if (this.boardRequiresUserFields) {
|
||||
this.menuActionsDisposables.push(
|
||||
registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, {
|
||||
commandId: UploadSketch.Commands.UPLOAD_WITH_CONFIGURATION.id,
|
||||
label: UploadSketch.Commands.UPLOAD_WITH_CONFIGURATION.label,
|
||||
order: '2',
|
||||
})
|
||||
);
|
||||
} else {
|
||||
this.menuActionsDisposables.push(
|
||||
registry.registerMenuNode(
|
||||
ArduinoMenus.SKETCH__MAIN_GROUP,
|
||||
new PlaceholderMenuNode(
|
||||
ArduinoMenus.SKETCH__MAIN_GROUP,
|
||||
// commandId: UploadSketch.Commands.UPLOAD_WITH_CONFIGURATION.id,
|
||||
UploadSketch.Commands.UPLOAD_WITH_CONFIGURATION.label!,
|
||||
{ order: '2' }
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
this.menuActionsDisposables.push(
|
||||
registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, {
|
||||
commandId: UploadSketch.Commands.UPLOAD_SKETCH_USING_PROGRAMMER.id,
|
||||
label: nls.localize(
|
||||
'arduino/sketch/uploadUsingProgrammer',
|
||||
'Upload Using Programmer'
|
||||
),
|
||||
order: '3',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
registerKeybindings(registry: KeybindingRegistry): void {
|
||||
@@ -84,7 +192,7 @@ export class UploadSketch extends SketchContribution {
|
||||
registry.registerItem({
|
||||
id: UploadSketch.Commands.UPLOAD_SKETCH_TOOLBAR.id,
|
||||
command: UploadSketch.Commands.UPLOAD_SKETCH_TOOLBAR.id,
|
||||
tooltip: 'Upload',
|
||||
tooltip: nls.localize('arduino/sketch/upload', 'Upload'),
|
||||
priority: 1,
|
||||
onDidChange: this.onDidChange,
|
||||
});
|
||||
@@ -104,15 +212,7 @@ export class UploadSketch extends SketchContribution {
|
||||
if (!sketch) {
|
||||
return;
|
||||
}
|
||||
let shouldAutoConnect = false;
|
||||
const monitorConfig = this.monitorConnection.monitorConfig;
|
||||
if (monitorConfig) {
|
||||
await this.monitorConnection.disconnect();
|
||||
if (this.monitorConnection.autoConnect) {
|
||||
shouldAutoConnect = true;
|
||||
}
|
||||
this.monitorConnection.autoConnect = false;
|
||||
}
|
||||
|
||||
try {
|
||||
const { boardsConfig } = this.boardsServiceClientImpl;
|
||||
const [fqbn, { selectedProgrammer }, verify, verbose, sourceOverride] =
|
||||
@@ -130,7 +230,18 @@ export class UploadSketch extends SketchContribution {
|
||||
const sketchUri = sketch.uri;
|
||||
const optimizeForDebug = this.editorMode.compileForDebug;
|
||||
const { selectedPort } = boardsConfig;
|
||||
const port = selectedPort?.address;
|
||||
const port = selectedPort;
|
||||
const userFields =
|
||||
this.cachedUserFields.get(this.selectedFqbnAddress()) ?? [];
|
||||
if (userFields.length === 0 && this.boardRequiresUserFields) {
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'arduino/sketch/userFieldsNotFoundError',
|
||||
"Can't find user fields for connected board"
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usingProgrammer) {
|
||||
const programmer = selectedProgrammer;
|
||||
@@ -143,6 +254,7 @@ export class UploadSketch extends SketchContribution {
|
||||
verbose,
|
||||
verify,
|
||||
sourceOverride,
|
||||
userFields,
|
||||
};
|
||||
} else {
|
||||
options = {
|
||||
@@ -153,6 +265,7 @@ export class UploadSketch extends SketchContribution {
|
||||
verbose,
|
||||
verify,
|
||||
sourceOverride,
|
||||
userFields,
|
||||
};
|
||||
}
|
||||
this.outputChannelManager.getChannel('Arduino').clear();
|
||||
@@ -161,32 +274,23 @@ export class UploadSketch extends SketchContribution {
|
||||
} else {
|
||||
await this.coreService.upload(options);
|
||||
}
|
||||
this.messageService.info('Done uploading.', { timeout: 3000 });
|
||||
this.messageService.info(
|
||||
nls.localize('arduino/sketch/doneUploading', 'Done uploading.'),
|
||||
{ timeout: 3000 }
|
||||
);
|
||||
} catch (e) {
|
||||
this.messageService.error(e.toString());
|
||||
let errorMessage = '';
|
||||
if (typeof e === 'string') {
|
||||
errorMessage = e;
|
||||
} else {
|
||||
errorMessage = e.toString();
|
||||
}
|
||||
this.messageService.error(errorMessage);
|
||||
} finally {
|
||||
this.uploadInProgress = false;
|
||||
this.onDidChangeEmitter.fire();
|
||||
|
||||
if (monitorConfig) {
|
||||
const { board, port } = monitorConfig;
|
||||
try {
|
||||
await this.boardsServiceClientImpl.waitUntilAvailable(
|
||||
Object.assign(board, { port }),
|
||||
10_000
|
||||
);
|
||||
if (shouldAutoConnect) {
|
||||
// Enabling auto-connect will trigger a connect.
|
||||
this.monitorConnection.autoConnect = true;
|
||||
} else {
|
||||
await this.monitorConnection.connect(monitorConfig);
|
||||
}
|
||||
} catch (waitError) {
|
||||
this.messageService.error(
|
||||
`Could not reconnect to serial monitor. ${waitError.toString()}`
|
||||
);
|
||||
}
|
||||
}
|
||||
setTimeout(() => this.serialConnection.reconnectAfterUpload(), 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,6 +300,14 @@ export namespace UploadSketch {
|
||||
export const UPLOAD_SKETCH: Command = {
|
||||
id: 'arduino-upload-sketch',
|
||||
};
|
||||
export const UPLOAD_WITH_CONFIGURATION: Command = {
|
||||
id: 'arduino-upload-with-configuration-sketch',
|
||||
label: nls.localize(
|
||||
'arduino/sketch/configureAndUpload',
|
||||
'Configure And Upload'
|
||||
),
|
||||
category: 'Arduino',
|
||||
};
|
||||
export const UPLOAD_SKETCH_USING_PROGRAMMER: Command = {
|
||||
id: 'arduino-upload-sketch-using-programmer',
|
||||
};
|
||||
|
@@ -13,6 +13,7 @@ import {
|
||||
KeybindingRegistry,
|
||||
TabBarToolbarRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class VerifySketch extends SketchContribution {
|
||||
@@ -52,13 +53,16 @@ export class VerifySketch extends SketchContribution {
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, {
|
||||
commandId: VerifySketch.Commands.VERIFY_SKETCH.id,
|
||||
label: 'Verify/Compile',
|
||||
label: nls.localize('arduino/sketch/verifyOrCompile', 'Verify/Compile'),
|
||||
order: '0',
|
||||
});
|
||||
registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, {
|
||||
commandId: VerifySketch.Commands.EXPORT_BINARIES.id,
|
||||
label: 'Export compiled Binary',
|
||||
order: '3',
|
||||
label: nls.localize(
|
||||
'arduino/sketch/exportBinary',
|
||||
'Export Compiled Binary'
|
||||
),
|
||||
order: '4',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -77,7 +81,7 @@ export class VerifySketch extends SketchContribution {
|
||||
registry.registerItem({
|
||||
id: VerifySketch.Commands.VERIFY_SKETCH_TOOLBAR.id,
|
||||
command: VerifySketch.Commands.VERIFY_SKETCH_TOOLBAR.id,
|
||||
tooltip: 'Verify',
|
||||
tooltip: nls.localize('arduino/sketch/verify', 'Verify'),
|
||||
priority: 0,
|
||||
onDidChange: this.onDidChange,
|
||||
});
|
||||
@@ -118,9 +122,18 @@ export class VerifySketch extends SketchContribution {
|
||||
sourceOverride,
|
||||
compilerWarnings,
|
||||
});
|
||||
this.messageService.info('Done compiling.', { timeout: 3000 });
|
||||
this.messageService.info(
|
||||
nls.localize('arduino/sketch/doneCompiling', 'Done compiling.'),
|
||||
{ timeout: 3000 }
|
||||
);
|
||||
} catch (e) {
|
||||
this.messageService.error(e.toString());
|
||||
let errorMessage = "";
|
||||
if (typeof e === "string") {
|
||||
errorMessage = e;
|
||||
} else {
|
||||
errorMessage = e.toString();
|
||||
}
|
||||
this.messageService.error(errorMessage);
|
||||
} finally {
|
||||
this.verifyInProgress = false;
|
||||
this.onDidChangeEmitter.fire();
|
||||
|
@@ -97,6 +97,7 @@
|
||||
"editorWhitespace.foreground": "#bfbfbf",
|
||||
"editor.lineHighlightBackground": "#434f5410",
|
||||
"editor.selectionBackground": "#ffcb00",
|
||||
"editorWidget.background": "#F7F9F9",
|
||||
"focusBorder": "#7fcbcd99",
|
||||
"menubar.selectionBackground": "#ffffff",
|
||||
"menubar.selectionForeground": "#212121",
|
||||
|
@@ -0,0 +1,46 @@
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import * as React from 'react';
|
||||
|
||||
export const CertificateAddComponent = ({
|
||||
addCertificate,
|
||||
}: {
|
||||
addCertificate: (cert: string) => void;
|
||||
}): React.ReactElement => {
|
||||
const [value, setValue] = React.useState('');
|
||||
|
||||
const handleChange = React.useCallback((event) => {
|
||||
setValue(event.target.value);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<form
|
||||
className="certificate-add"
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
addCertificate(value);
|
||||
setValue('');
|
||||
}}
|
||||
>
|
||||
<label>
|
||||
<div>
|
||||
{nls.localize(
|
||||
'arduino/certificate/addURL',
|
||||
'Add URL to fetch SSL certificate'
|
||||
)}
|
||||
</div>
|
||||
<input
|
||||
className="theia-input"
|
||||
placeholder={nls.localize(
|
||||
'arduino/certificate/enterURL',
|
||||
'Enter URL'
|
||||
)}
|
||||
type="text"
|
||||
name="add"
|
||||
onChange={handleChange}
|
||||
value={value}
|
||||
/>
|
||||
</label>
|
||||
</form>
|
||||
);
|
||||
};
|
@@ -0,0 +1,51 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export const CertificateListComponent = ({
|
||||
certificates,
|
||||
selectedCerts,
|
||||
setSelectedCerts,
|
||||
openContextMenu,
|
||||
}: {
|
||||
certificates: string[];
|
||||
selectedCerts: string[];
|
||||
setSelectedCerts: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
openContextMenu: (x: number, y: number, cert: string) => void;
|
||||
}): React.ReactElement => {
|
||||
const handleOnChange = (event: any) => {
|
||||
const target = event.target;
|
||||
|
||||
const newSelectedCerts = selectedCerts.filter(
|
||||
(cert) => cert !== target.name
|
||||
);
|
||||
|
||||
if (target.checked) {
|
||||
newSelectedCerts.push(target.name);
|
||||
}
|
||||
|
||||
setSelectedCerts(newSelectedCerts);
|
||||
};
|
||||
|
||||
const handleContextMenu = (event: React.MouseEvent, cert: string) => {
|
||||
openContextMenu(event.clientX, event.clientY, cert);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="certificate-list">
|
||||
{certificates.map((certificate, i) => (
|
||||
<label
|
||||
key={i}
|
||||
className="certificate-row"
|
||||
onContextMenu={(e) => handleContextMenu(e, certificate)}
|
||||
>
|
||||
<span className="fl1">{certificate}</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
name={certificate}
|
||||
checked={selectedCerts.includes(certificate)}
|
||||
onChange={handleOnChange}
|
||||
/>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
@@ -0,0 +1,178 @@
|
||||
import * as React from 'react';
|
||||
import Tippy from '@tippyjs/react';
|
||||
import { AvailableBoard } from '../../boards/boards-service-provider';
|
||||
import { CertificateListComponent } from './certificate-list';
|
||||
import { SelectBoardComponent } from './select-board-components';
|
||||
import { CertificateAddComponent } from './certificate-add-new';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
export const CertificateUploaderComponent = ({
|
||||
availableBoards,
|
||||
certificates,
|
||||
addCertificate,
|
||||
updatableFqbns,
|
||||
uploadCertificates,
|
||||
openContextMenu,
|
||||
}: {
|
||||
availableBoards: AvailableBoard[];
|
||||
certificates: string[];
|
||||
addCertificate: (cert: string) => void;
|
||||
updatableFqbns: string[];
|
||||
uploadCertificates: (
|
||||
fqbn: string,
|
||||
address: string,
|
||||
urls: string[]
|
||||
) => Promise<any>;
|
||||
openContextMenu: (x: number, y: number, cert: string) => void;
|
||||
}): React.ReactElement => {
|
||||
const [installFeedback, setInstallFeedback] = React.useState<
|
||||
'ok' | 'fail' | 'installing' | null
|
||||
>(null);
|
||||
|
||||
const [showAdd, setShowAdd] = React.useState(false);
|
||||
|
||||
const [selectedCerts, setSelectedCerts] = React.useState<string[]>([]);
|
||||
|
||||
const [selectedBoard, setSelectedBoard] =
|
||||
React.useState<AvailableBoard | null>(null);
|
||||
|
||||
const installCertificates = async () => {
|
||||
if (!selectedBoard || !selectedBoard.fqbn || !selectedBoard.port) {
|
||||
return;
|
||||
}
|
||||
|
||||
setInstallFeedback('installing');
|
||||
|
||||
try {
|
||||
await uploadCertificates(
|
||||
selectedBoard.fqbn,
|
||||
selectedBoard.port.address,
|
||||
selectedCerts
|
||||
);
|
||||
setInstallFeedback('ok');
|
||||
} catch {
|
||||
setInstallFeedback('fail');
|
||||
}
|
||||
};
|
||||
|
||||
const onBoardSelect = React.useCallback(
|
||||
(board: AvailableBoard) => {
|
||||
const newFqbn = (board && board.fqbn) || null;
|
||||
const prevFqbn = (selectedBoard && selectedBoard.fqbn) || null;
|
||||
|
||||
if (newFqbn !== prevFqbn) {
|
||||
setInstallFeedback(null);
|
||||
setSelectedBoard(board);
|
||||
}
|
||||
},
|
||||
[selectedBoard]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="dialogSection">
|
||||
<div className="dialogRow">
|
||||
<strong className="fl1">
|
||||
{nls.localize(
|
||||
'arduino/certificate/selectCertificateToUpload',
|
||||
'1. Select certificate to upload'
|
||||
)}
|
||||
</strong>
|
||||
<Tippy
|
||||
content={
|
||||
<CertificateAddComponent
|
||||
addCertificate={(cert) => {
|
||||
addCertificate(cert);
|
||||
setShowAdd(false);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
placement="bottom-end"
|
||||
onClickOutside={() => setShowAdd(false)}
|
||||
visible={showAdd}
|
||||
interactive={true}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="theia-button primary add-cert-btn"
|
||||
onClick={() => {
|
||||
showAdd ? setShowAdd(false) : setShowAdd(true);
|
||||
}}
|
||||
>
|
||||
{nls.localize('arduino/certificate/addNew', 'Add New')}{' '}
|
||||
<span className="fa fa-caret-down caret"></span>
|
||||
</button>
|
||||
</Tippy>
|
||||
</div>
|
||||
<div className="dialogRow">
|
||||
<CertificateListComponent
|
||||
certificates={certificates}
|
||||
selectedCerts={selectedCerts}
|
||||
setSelectedCerts={setSelectedCerts}
|
||||
openContextMenu={openContextMenu}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dialogSection">
|
||||
<div className="dialogRow">
|
||||
<strong>
|
||||
{nls.localize(
|
||||
'arduino/certificate/selectDestinationBoardToUpload',
|
||||
'2. Select destination board and upload certificate'
|
||||
)}
|
||||
</strong>
|
||||
</div>
|
||||
<div className="dialogRow">
|
||||
<div className="fl1">
|
||||
<SelectBoardComponent
|
||||
availableBoards={availableBoards}
|
||||
updatableFqbns={updatableFqbns}
|
||||
onBoardSelect={onBoardSelect}
|
||||
selectedBoard={selectedBoard}
|
||||
busy={installFeedback === 'installing'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dialogRow">
|
||||
<div className="upload-status">
|
||||
{installFeedback === 'installing' && (
|
||||
<div className="success">
|
||||
<div className="spinner" />
|
||||
{nls.localize(
|
||||
'arduino/certificate/uploadingCertificates',
|
||||
'Uploading certificates.'
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{installFeedback === 'ok' && (
|
||||
<div className="success">
|
||||
<i className="fa fa-info status-icon" />
|
||||
{nls.localize(
|
||||
'arduino/certificate/certificatesUploaded',
|
||||
'Certificates uploaded.'
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{installFeedback === 'fail' && (
|
||||
<div className="warn">
|
||||
<i className="fa fa-exclamation status-icon" />
|
||||
{nls.localize(
|
||||
'arduino/certificate/uploadFailed',
|
||||
'Upload failed. Please try again.'
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="theia-button primary install-cert-btn"
|
||||
onClick={installCertificates}
|
||||
disabled={selectedCerts.length === 0 || !selectedBoard}
|
||||
>
|
||||
{nls.localize('arduino/certificate/upload', 'Upload')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
@@ -0,0 +1,197 @@
|
||||
import * as React from 'react';
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import { DialogProps } from '@theia/core/lib/browser/dialogs';
|
||||
import { AbstractDialog } from '../../theia/dialogs/dialogs';
|
||||
import { Widget } from '@phosphor/widgets';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
||||
import {
|
||||
AvailableBoard,
|
||||
BoardsServiceProvider,
|
||||
} from '../../boards/boards-service-provider';
|
||||
import { CertificateUploaderComponent } from './certificate-uploader-component';
|
||||
import { ArduinoPreferences } from '../../arduino-preferences';
|
||||
import {
|
||||
PreferenceScope,
|
||||
PreferenceService,
|
||||
} from '@theia/core/lib/browser/preferences/preference-service';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import { certificateList, sanifyCertString } from './utils';
|
||||
import { ArduinoFirmwareUploader } from '../../../common/protocol/arduino-firmware-uploader';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class UploadCertificateDialogWidget extends ReactWidget {
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceClient: BoardsServiceProvider;
|
||||
|
||||
@inject(ArduinoPreferences)
|
||||
protected readonly arduinoPreferences: ArduinoPreferences;
|
||||
|
||||
@inject(PreferenceService)
|
||||
protected readonly preferenceService: PreferenceService;
|
||||
|
||||
@inject(CommandRegistry)
|
||||
protected readonly commandRegistry: CommandRegistry;
|
||||
|
||||
@inject(ArduinoFirmwareUploader)
|
||||
protected readonly arduinoFirmwareUploader: ArduinoFirmwareUploader;
|
||||
|
||||
protected certificates: string[] = [];
|
||||
protected updatableFqbns: string[] = [];
|
||||
protected availableBoards: AvailableBoard[] = [];
|
||||
|
||||
public busyCallback = (busy: boolean) => {
|
||||
return;
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.arduinoPreferences.ready.then(() => {
|
||||
this.certificates = certificateList(
|
||||
this.arduinoPreferences.get('arduino.board.certificates')
|
||||
);
|
||||
});
|
||||
this.arduinoPreferences.onPreferenceChanged((event) => {
|
||||
if (
|
||||
event.preferenceName === 'arduino.board.certificates' &&
|
||||
event.newValue !== event.oldValue
|
||||
) {
|
||||
this.certificates = certificateList(event.newValue);
|
||||
this.update();
|
||||
}
|
||||
});
|
||||
|
||||
this.arduinoFirmwareUploader.updatableBoards().then((fqbns) => {
|
||||
this.updatableFqbns = fqbns;
|
||||
this.update();
|
||||
});
|
||||
|
||||
this.boardsServiceClient.onAvailableBoardsChanged((availableBoards) => {
|
||||
this.availableBoards = availableBoards;
|
||||
this.update();
|
||||
});
|
||||
}
|
||||
|
||||
private addCertificate(certificate: string) {
|
||||
const certString = sanifyCertString(certificate);
|
||||
|
||||
if (certString.length > 0) {
|
||||
this.certificates.push(sanifyCertString(certificate));
|
||||
}
|
||||
|
||||
this.preferenceService.set(
|
||||
'arduino.board.certificates',
|
||||
this.certificates.join(','),
|
||||
PreferenceScope.User
|
||||
);
|
||||
}
|
||||
|
||||
protected openContextMenu(x: number, y: number, cert: string): void {
|
||||
this.commandRegistry.executeCommand(
|
||||
'arduino-certificate-open-context',
|
||||
Object.assign({}, { x, y, cert })
|
||||
);
|
||||
}
|
||||
|
||||
protected uploadCertificates(
|
||||
fqbn: string,
|
||||
address: string,
|
||||
urls: string[]
|
||||
): Promise<any> {
|
||||
this.busyCallback(true);
|
||||
return this.commandRegistry
|
||||
.executeCommand('arduino-certificate-upload', {
|
||||
fqbn,
|
||||
address,
|
||||
urls,
|
||||
})
|
||||
.finally(() => this.busyCallback(false));
|
||||
}
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
return (
|
||||
<CertificateUploaderComponent
|
||||
availableBoards={this.availableBoards}
|
||||
certificates={this.certificates}
|
||||
updatableFqbns={this.updatableFqbns}
|
||||
addCertificate={this.addCertificate.bind(this)}
|
||||
uploadCertificates={this.uploadCertificates.bind(this)}
|
||||
openContextMenu={this.openContextMenu.bind(this)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class UploadCertificateDialogProps extends DialogProps {}
|
||||
|
||||
@injectable()
|
||||
export class UploadCertificateDialog extends AbstractDialog<void> {
|
||||
@inject(UploadCertificateDialogWidget)
|
||||
protected readonly widget: UploadCertificateDialogWidget;
|
||||
|
||||
private busy = false;
|
||||
|
||||
constructor(
|
||||
@inject(UploadCertificateDialogProps)
|
||||
protected readonly props: UploadCertificateDialogProps
|
||||
) {
|
||||
super({
|
||||
title: nls.localize(
|
||||
'arduino/certificate/uploadRootCertificates',
|
||||
'Upload SSL Root Certificates'
|
||||
),
|
||||
});
|
||||
this.contentNode.classList.add('certificate-uploader-dialog');
|
||||
this.acceptButton = undefined;
|
||||
}
|
||||
|
||||
get value(): void {
|
||||
return;
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
}
|
||||
Widget.attach(this.widget, this.contentNode);
|
||||
this.widget.busyCallback = this.busyCallback.bind(this);
|
||||
super.onAfterAttach(msg);
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message): void {
|
||||
super.onUpdateRequest(msg);
|
||||
this.widget.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
this.widget.activate();
|
||||
}
|
||||
|
||||
protected handleEnter(event: KeyboardEvent): boolean | void {
|
||||
return false;
|
||||
}
|
||||
|
||||
close(): void {
|
||||
if (this.busy) {
|
||||
return;
|
||||
}
|
||||
super.close();
|
||||
}
|
||||
|
||||
busyCallback(busy: boolean): void {
|
||||
this.busy = busy;
|
||||
if (busy) {
|
||||
this.closeCrossNode.classList.add('disabled');
|
||||
} else {
|
||||
this.closeCrossNode.classList.remove('disabled');
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,108 @@
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import * as React from 'react';
|
||||
import { AvailableBoard } from '../../boards/boards-service-provider';
|
||||
import { ArduinoSelect } from '../../widgets/arduino-select';
|
||||
|
||||
type BoardOption = { value: string; label: string };
|
||||
|
||||
export const SelectBoardComponent = ({
|
||||
availableBoards,
|
||||
updatableFqbns,
|
||||
onBoardSelect,
|
||||
selectedBoard,
|
||||
busy,
|
||||
}: {
|
||||
availableBoards: AvailableBoard[];
|
||||
updatableFqbns: string[];
|
||||
onBoardSelect: (board: AvailableBoard | null) => void;
|
||||
selectedBoard: AvailableBoard | null;
|
||||
busy: boolean;
|
||||
}): React.ReactElement => {
|
||||
const [selectOptions, setSelectOptions] = React.useState<BoardOption[]>([]);
|
||||
|
||||
const [selectBoardPlaceholder, setSelectBoardPlaceholder] =
|
||||
React.useState('');
|
||||
|
||||
const selectOption = React.useCallback(
|
||||
(boardOpt: BoardOption) => {
|
||||
onBoardSelect(
|
||||
(boardOpt &&
|
||||
availableBoards.find((board) => board.fqbn === boardOpt.value)) ||
|
||||
null
|
||||
);
|
||||
},
|
||||
[availableBoards, onBoardSelect]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
// if there is activity going on, skip updating the boards (avoid flickering)
|
||||
if (busy) {
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholderTxt = nls.localize(
|
||||
'arduino/certificate/selectBoard',
|
||||
'Select a board...'
|
||||
);
|
||||
let selBoard = -1;
|
||||
const updatableBoards = availableBoards.filter(
|
||||
(board) => board.port && board.fqbn && updatableFqbns.includes(board.fqbn)
|
||||
);
|
||||
const boardsList: BoardOption[] = updatableBoards.map((board, i) => {
|
||||
if (board.selected) {
|
||||
selBoard = i;
|
||||
}
|
||||
return {
|
||||
label: nls.localize(
|
||||
'arduino/certificate/boardAtPort',
|
||||
'{0} at {1}',
|
||||
board.name,
|
||||
board.port?.address ?? ''
|
||||
),
|
||||
value: board.fqbn || '',
|
||||
};
|
||||
});
|
||||
|
||||
if (boardsList.length === 0) {
|
||||
placeholderTxt = nls.localize(
|
||||
'arduino/certificate/noSupportedBoardConnected',
|
||||
'No supported board connected'
|
||||
);
|
||||
}
|
||||
|
||||
setSelectBoardPlaceholder(placeholderTxt);
|
||||
setSelectOptions(boardsList);
|
||||
|
||||
if (selectedBoard) {
|
||||
selBoard = boardsList
|
||||
.map((boardOpt) => boardOpt.value)
|
||||
.indexOf(selectedBoard.fqbn || '');
|
||||
}
|
||||
|
||||
selectOption(boardsList[selBoard] || null);
|
||||
}, [busy, availableBoards, selectOption, updatableFqbns, selectedBoard]);
|
||||
|
||||
return (
|
||||
<ArduinoSelect
|
||||
id="board-select"
|
||||
menuPosition="fixed"
|
||||
isDisabled={selectOptions.length === 0 || busy}
|
||||
placeholder={selectBoardPlaceholder}
|
||||
options={selectOptions}
|
||||
value={
|
||||
(selectedBoard && {
|
||||
value: selectedBoard.fqbn,
|
||||
label: nls.localize(
|
||||
'arduino/certificate/boardAtPort',
|
||||
'{0} at {1}',
|
||||
selectedBoard.name,
|
||||
selectedBoard.port?.address ?? ''
|
||||
),
|
||||
}) ||
|
||||
null
|
||||
}
|
||||
tabSelectsValue={false}
|
||||
onChange={selectOption}
|
||||
/>
|
||||
);
|
||||
};
|
@@ -0,0 +1,38 @@
|
||||
export const arduinoCert = 'arduino.cc:443';
|
||||
|
||||
export function sanifyCertString(cert: string): string {
|
||||
const regex = /^(?:.*:\/\/)*(\S+\.+[^:]*):*(\d*)*$/gm;
|
||||
|
||||
const m = regex.exec(cert);
|
||||
|
||||
if (!m) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const domain = m[1] || '';
|
||||
const port = m[2] || '443';
|
||||
|
||||
if (domain.length === 0 || port.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return `${domain}:${port}`;
|
||||
}
|
||||
|
||||
export function certificateList(certificates: string): string[] {
|
||||
let certs = certificates
|
||||
.split(',')
|
||||
.map((cert) => sanifyCertString(cert.trim()))
|
||||
.filter((cert) => {
|
||||
// remove empty certificates
|
||||
if (!cert || cert.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// add arduino certificate at the top of the list
|
||||
certs = certs.filter((cert) => cert !== arduinoCert);
|
||||
certs.unshift(arduinoCert);
|
||||
return certs;
|
||||
}
|
@@ -3,12 +3,10 @@ import { inject, injectable } from 'inversify';
|
||||
import { Widget } from '@phosphor/widgets';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import { clipboard } from 'electron';
|
||||
import {
|
||||
AbstractDialog,
|
||||
ReactWidget,
|
||||
DialogProps,
|
||||
} from '@theia/core/lib/browser';
|
||||
import { ReactWidget, DialogProps } from '@theia/core/lib/browser';
|
||||
import { AbstractDialog } from '../theia/dialogs/dialogs';
|
||||
import { CreateApi } from '../create/create-api';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
const RadioButton = (props: {
|
||||
id: string;
|
||||
@@ -42,10 +40,6 @@ export const ShareSketchComponent = ({
|
||||
createApi: CreateApi;
|
||||
domain?: string;
|
||||
}): React.ReactElement => {
|
||||
// const [publicVisibility, setPublicVisibility] = React.useState<boolean>(
|
||||
// treeNode.isPublic
|
||||
// );
|
||||
|
||||
const [loading, setloading] = React.useState<boolean>(false);
|
||||
|
||||
const radioChangeHandler = async (event: React.BaseSyntheticEvent) => {
|
||||
@@ -66,12 +60,20 @@ export const ShareSketchComponent = ({
|
||||
|
||||
return (
|
||||
<div id="widget-container arduino-sharesketch-dialog">
|
||||
<p>Choose visibility of your Sketch:</p>
|
||||
<p>
|
||||
{nls.localize(
|
||||
'arduino/cloud/chooseSketchVisibility',
|
||||
'Choose visibility of your Sketch:'
|
||||
)}
|
||||
</p>
|
||||
<RadioButton
|
||||
changed={radioChangeHandler}
|
||||
id="1"
|
||||
isSelected={treeNode.isPublic === false}
|
||||
label="Private. Only you can view the Sketch."
|
||||
label={nls.localize(
|
||||
'arduino/cloud/privateVisibility',
|
||||
'Private. Only you can view the Sketch.'
|
||||
)}
|
||||
value="private"
|
||||
isDisabled={loading}
|
||||
/>
|
||||
@@ -79,14 +81,17 @@ export const ShareSketchComponent = ({
|
||||
changed={radioChangeHandler}
|
||||
id="2"
|
||||
isSelected={treeNode.isPublic === true}
|
||||
label="Public. Anyone with the link can view the Sketch."
|
||||
label={nls.localize(
|
||||
'arduino/cloud/publicVisibility',
|
||||
'Public. Anyone with the link can view the Sketch.'
|
||||
)}
|
||||
value="public"
|
||||
isDisabled={loading}
|
||||
/>
|
||||
|
||||
{treeNode.isPublic && (
|
||||
<div>
|
||||
<p>Link:</p>
|
||||
<p>{nls.localize('arduino/cloud/link', 'Link:')}</p>
|
||||
<div className="sketch-link">
|
||||
<input
|
||||
type="text"
|
||||
@@ -99,10 +104,10 @@ export const ShareSketchComponent = ({
|
||||
value="copy"
|
||||
className="theia-button secondary"
|
||||
>
|
||||
Copy
|
||||
{nls.localize('vscode/textInputActions/copy', 'Copy')}
|
||||
</button>
|
||||
</div>
|
||||
<p>Embed:</p>
|
||||
<p>{nls.localize('arduino/cloud/embed', 'Embed:')}</p>
|
||||
<div className="sketch-link-embed">
|
||||
<textarea
|
||||
readOnly
|
@@ -6,9 +6,10 @@ import {
|
||||
ConfirmDialogProps,
|
||||
DialogError,
|
||||
} from '@theia/core/lib/browser/dialogs';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class DoNotAskAgainConfirmDialogProps extends ConfirmDialogProps {
|
||||
export class DoNotAskAgainDialogProps extends ConfirmDialogProps {
|
||||
readonly onAccept: () => Promise<void>;
|
||||
}
|
||||
|
||||
@@ -17,8 +18,8 @@ export class DoNotAskAgainConfirmDialog extends ConfirmDialog {
|
||||
protected readonly doNotAskAgainCheckbox: HTMLInputElement;
|
||||
|
||||
constructor(
|
||||
@inject(DoNotAskAgainConfirmDialogProps)
|
||||
protected readonly props: DoNotAskAgainConfirmDialogProps
|
||||
@inject(DoNotAskAgainDialogProps)
|
||||
protected readonly props: DoNotAskAgainDialogProps
|
||||
) {
|
||||
super(props);
|
||||
this.controlPanel.removeChild(this.errorMessageNode);
|
||||
@@ -31,7 +32,10 @@ export class DoNotAskAgainConfirmDialog extends ConfirmDialog {
|
||||
const doNotAskAgainLabel = document.createElement('label');
|
||||
doNotAskAgainLabel.classList.add('flex-line');
|
||||
doNotAskAgainNode.appendChild(doNotAskAgainLabel);
|
||||
doNotAskAgainLabel.textContent = "Don't ask again";
|
||||
doNotAskAgainLabel.textContent = nls.localize(
|
||||
'arduino/dialog/dontAskAgain',
|
||||
"Don't ask again"
|
||||
);
|
||||
this.doNotAskAgainCheckbox = document.createElement('input');
|
||||
this.doNotAskAgainCheckbox.setAttribute('align-self', 'center');
|
||||
doNotAskAgainLabel.appendChild(this.doNotAskAgainCheckbox);
|
@@ -0,0 +1,222 @@
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import * as React from 'react';
|
||||
import {
|
||||
ArduinoFirmwareUploader,
|
||||
FirmwareInfo,
|
||||
} from '../../../common/protocol/arduino-firmware-uploader';
|
||||
import { AvailableBoard } from '../../boards/boards-service-provider';
|
||||
import { ArduinoSelect } from '../../widgets/arduino-select';
|
||||
import { SelectBoardComponent } from '../certificate-uploader/select-board-components';
|
||||
|
||||
type FirmwareOption = { value: string; label: string };
|
||||
|
||||
export const FirmwareUploaderComponent = ({
|
||||
availableBoards,
|
||||
firmwareUploader,
|
||||
updatableFqbns,
|
||||
flashFirmware,
|
||||
isOpen,
|
||||
}: {
|
||||
availableBoards: AvailableBoard[];
|
||||
firmwareUploader: ArduinoFirmwareUploader;
|
||||
updatableFqbns: string[];
|
||||
flashFirmware: (firmware: FirmwareInfo, port: string) => Promise<any>;
|
||||
isOpen: any;
|
||||
}): React.ReactElement => {
|
||||
// boolean states for buttons
|
||||
const [firmwaresFetching, setFirmwaresFetching] = React.useState(false);
|
||||
|
||||
const [installFeedback, setInstallFeedback] = React.useState<
|
||||
'ok' | 'fail' | 'installing' | null
|
||||
>(null);
|
||||
|
||||
const [selectedBoard, setSelectedBoard] =
|
||||
React.useState<AvailableBoard | null>(null);
|
||||
|
||||
const [availableFirmwares, setAvailableFirmwares] = React.useState<
|
||||
FirmwareInfo[]
|
||||
>([]);
|
||||
React.useEffect(() => {
|
||||
setAvailableFirmwares([]);
|
||||
}, [isOpen]);
|
||||
const [selectedFirmware, setSelectedFirmware] =
|
||||
React.useState<FirmwareOption | null>(null);
|
||||
|
||||
const [firmwareOptions, setFirmwareOptions] = React.useState<
|
||||
FirmwareOption[]
|
||||
>([]);
|
||||
|
||||
const fetchFirmwares = React.useCallback(async () => {
|
||||
setInstallFeedback(null);
|
||||
setFirmwaresFetching(true);
|
||||
if (!selectedBoard) {
|
||||
return;
|
||||
}
|
||||
|
||||
// fetch the firmwares for the selected board
|
||||
const firmwaresForFqbn = await firmwareUploader.availableFirmwares(
|
||||
selectedBoard.fqbn || ''
|
||||
);
|
||||
setAvailableFirmwares(firmwaresForFqbn);
|
||||
|
||||
const firmwaresOpts = firmwaresForFqbn.map((f) => ({
|
||||
label: f.firmware_version,
|
||||
value: f.firmware_version,
|
||||
}));
|
||||
|
||||
setFirmwareOptions(firmwaresOpts);
|
||||
|
||||
if (firmwaresForFqbn.length > 0) setSelectedFirmware(firmwaresOpts[0]);
|
||||
setFirmwaresFetching(false);
|
||||
}, [firmwareUploader, selectedBoard]);
|
||||
|
||||
const installFirmware = React.useCallback(async () => {
|
||||
setInstallFeedback('installing');
|
||||
|
||||
const firmwareToFlash = availableFirmwares.find(
|
||||
(firmware) => firmware.firmware_version === selectedFirmware?.value
|
||||
);
|
||||
|
||||
try {
|
||||
const installStatus =
|
||||
!!firmwareToFlash &&
|
||||
!!selectedBoard?.port &&
|
||||
(await flashFirmware(firmwareToFlash, selectedBoard?.port.address));
|
||||
|
||||
setInstallFeedback((installStatus && 'ok') || 'fail');
|
||||
} catch {
|
||||
setInstallFeedback('fail');
|
||||
}
|
||||
}, [firmwareUploader, selectedBoard, selectedFirmware, availableFirmwares]);
|
||||
|
||||
const onBoardSelect = React.useCallback(
|
||||
(board: AvailableBoard) => {
|
||||
const newFqbn = (board && board.fqbn) || null;
|
||||
const prevFqbn = (selectedBoard && selectedBoard.fqbn) || null;
|
||||
|
||||
if (newFqbn !== prevFqbn) {
|
||||
setInstallFeedback(null);
|
||||
setAvailableFirmwares([]);
|
||||
setSelectedBoard(board);
|
||||
}
|
||||
},
|
||||
[selectedBoard]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="dialogSection">
|
||||
<div className="dialogRow">
|
||||
<label htmlFor="board-select">
|
||||
{nls.localize('arduino/firmware/selectBoard', 'Select Board')}
|
||||
</label>
|
||||
</div>
|
||||
<div className="dialogRow">
|
||||
<div className="fl1">
|
||||
<SelectBoardComponent
|
||||
availableBoards={availableBoards}
|
||||
updatableFqbns={updatableFqbns}
|
||||
onBoardSelect={onBoardSelect}
|
||||
selectedBoard={selectedBoard}
|
||||
busy={installFeedback === 'installing'}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="theia-button secondary"
|
||||
disabled={
|
||||
selectedBoard === null ||
|
||||
firmwaresFetching ||
|
||||
installFeedback === 'installing'
|
||||
}
|
||||
onClick={fetchFirmwares}
|
||||
>
|
||||
{nls.localize('arduino/firmware/checkUpdates', 'Check Updates')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{availableFirmwares.length > 0 && (
|
||||
<>
|
||||
<div className="dialogSection">
|
||||
<div className="dialogRow">
|
||||
<label htmlFor="firmware-select" className="fl1">
|
||||
{nls.localize(
|
||||
'arduino/firmware/selectVersion',
|
||||
'Select firmware version'
|
||||
)}
|
||||
</label>
|
||||
<ArduinoSelect
|
||||
id="firmware-select"
|
||||
menuPosition="fixed"
|
||||
isDisabled={
|
||||
!selectedBoard ||
|
||||
firmwaresFetching ||
|
||||
installFeedback === 'installing'
|
||||
}
|
||||
options={firmwareOptions}
|
||||
value={selectedFirmware}
|
||||
tabSelectsValue={false}
|
||||
onChange={(value) => {
|
||||
if (value) {
|
||||
setInstallFeedback(null);
|
||||
setSelectedFirmware(value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="theia-button primary"
|
||||
disabled={
|
||||
selectedFirmware === null ||
|
||||
firmwaresFetching ||
|
||||
installFeedback === 'installing'
|
||||
}
|
||||
onClick={installFirmware}
|
||||
>
|
||||
{nls.localize('arduino/firmware/install', 'Install')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dialogSection">
|
||||
{installFeedback === null && (
|
||||
<div className="dialogRow warn">
|
||||
<i className="fa fa-exclamation status-icon" />
|
||||
{nls.localize(
|
||||
'arduino/firmware/overwriteSketch',
|
||||
'Installation will overwrite the Sketch on the board.'
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{installFeedback === 'installing' && (
|
||||
<div className="dialogRow success">
|
||||
<div className="spinner" />
|
||||
{nls.localize(
|
||||
'arduino/firmware/installingFirmware',
|
||||
'Installing firmware.'
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{installFeedback === 'ok' && (
|
||||
<div className="dialogRow success">
|
||||
<i className="fa fa-info status-icon" />
|
||||
{nls.localize(
|
||||
'arduino/firmware/successfullyInstalled',
|
||||
'Firmware succesfully installed.'
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{installFeedback === 'fail' && (
|
||||
<div className="dialogRow warn">
|
||||
<i className="fa fa-exclamation status-icon" />
|
||||
{nls.localize(
|
||||
'arduino/firmware/failedInstall',
|
||||
'Installation failed. Please try again.'
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@@ -0,0 +1,142 @@
|
||||
import * as React from 'react';
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import { DialogProps } from '@theia/core/lib/browser/dialogs';
|
||||
import { AbstractDialog } from '../../theia/dialogs/dialogs';
|
||||
import { Widget } from '@phosphor/widgets';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
||||
import {
|
||||
AvailableBoard,
|
||||
BoardsServiceProvider,
|
||||
} from '../../boards/boards-service-provider';
|
||||
import {
|
||||
ArduinoFirmwareUploader,
|
||||
FirmwareInfo,
|
||||
} from '../../../common/protocol/arduino-firmware-uploader';
|
||||
import { FirmwareUploaderComponent } from './firmware-uploader-component';
|
||||
import { UploadFirmware } from '../../contributions/upload-firmware';
|
||||
|
||||
@injectable()
|
||||
export class UploadFirmwareDialogWidget extends ReactWidget {
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceClient: BoardsServiceProvider;
|
||||
|
||||
@inject(ArduinoFirmwareUploader)
|
||||
protected readonly arduinoFirmwareUploader: ArduinoFirmwareUploader;
|
||||
|
||||
protected updatableFqbns: string[] = [];
|
||||
protected availableBoards: AvailableBoard[] = [];
|
||||
protected isOpen = new Object();
|
||||
|
||||
public busyCallback = (busy: boolean) => {
|
||||
return;
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.arduinoFirmwareUploader.updatableBoards().then((fqbns) => {
|
||||
this.updatableFqbns = fqbns;
|
||||
this.update();
|
||||
});
|
||||
|
||||
this.boardsServiceClient.onAvailableBoardsChanged((availableBoards) => {
|
||||
this.availableBoards = availableBoards;
|
||||
this.update();
|
||||
});
|
||||
}
|
||||
|
||||
protected flashFirmware(firmware: FirmwareInfo, port: string): Promise<any> {
|
||||
this.busyCallback(true);
|
||||
return this.arduinoFirmwareUploader
|
||||
.flash(firmware, port)
|
||||
.finally(() => this.busyCallback(false));
|
||||
}
|
||||
|
||||
onCloseRequest(msg: Message): void {
|
||||
super.onCloseRequest(msg);
|
||||
this.isOpen = new Object();
|
||||
}
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
return (
|
||||
<form>
|
||||
<FirmwareUploaderComponent
|
||||
availableBoards={this.availableBoards}
|
||||
firmwareUploader={this.arduinoFirmwareUploader}
|
||||
flashFirmware={this.flashFirmware.bind(this)}
|
||||
updatableFqbns={this.updatableFqbns}
|
||||
isOpen={this.isOpen}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class UploadFirmwareDialogProps extends DialogProps {}
|
||||
|
||||
@injectable()
|
||||
export class UploadFirmwareDialog extends AbstractDialog<void> {
|
||||
@inject(UploadFirmwareDialogWidget)
|
||||
protected readonly widget: UploadFirmwareDialogWidget;
|
||||
|
||||
private busy = false;
|
||||
|
||||
constructor(
|
||||
@inject(UploadFirmwareDialogProps)
|
||||
protected readonly props: UploadFirmwareDialogProps
|
||||
) {
|
||||
super({ title: UploadFirmware.Commands.OPEN.label || '' });
|
||||
this.contentNode.classList.add('firmware-uploader-dialog');
|
||||
this.acceptButton = undefined;
|
||||
}
|
||||
|
||||
get value(): void {
|
||||
return;
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
}
|
||||
Widget.attach(this.widget, this.contentNode);
|
||||
this.widget.busyCallback = this.busyCallback.bind(this);
|
||||
super.onAfterAttach(msg);
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message): void {
|
||||
super.onUpdateRequest(msg);
|
||||
this.widget.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
this.widget.activate();
|
||||
}
|
||||
|
||||
protected handleEnter(event: KeyboardEvent): boolean | void {
|
||||
return false;
|
||||
}
|
||||
|
||||
close(): void {
|
||||
if (this.busy) {
|
||||
return;
|
||||
}
|
||||
this.widget.close();
|
||||
super.close();
|
||||
}
|
||||
|
||||
busyCallback(busy: boolean): void {
|
||||
this.busy = busy;
|
||||
if (busy) {
|
||||
this.closeCrossNode.classList.add('disabled');
|
||||
} else {
|
||||
this.closeCrossNode.classList.remove('disabled');
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,308 +1,22 @@
|
||||
import * as React from 'react';
|
||||
import { injectable, inject, postConstruct } from 'inversify';
|
||||
import { Widget } from '@phosphor/widgets';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
|
||||
import 'react-tabs/style/react-tabs.css';
|
||||
import { Disable } from 'react-disable';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { deepClone } from '@theia/core/lib/common/objects';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { ThemeService } from '@theia/core/lib/browser/theming';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { FileDialogService } from '@theia/filesystem/lib/browser/file-dialog/file-dialog-service';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import {
|
||||
AbstractDialog,
|
||||
DialogProps,
|
||||
PreferenceService,
|
||||
PreferenceScope,
|
||||
DialogError,
|
||||
ReactWidget,
|
||||
} from '@theia/core/lib/browser';
|
||||
import { Index } from '../common/types';
|
||||
import {
|
||||
CompilerWarnings,
|
||||
CompilerWarningLiterals,
|
||||
ConfigService,
|
||||
FileSystemExt,
|
||||
Network,
|
||||
ProxySettings,
|
||||
} from '../common/protocol';
|
||||
|
||||
export interface Settings extends Index {
|
||||
editorFontSize: number; // `editor.fontSize`
|
||||
themeId: string; // `workbench.colorTheme`
|
||||
autoSave: 'on' | 'off'; // `editor.autoSave`
|
||||
quickSuggestions: Record<'other' | 'comments' | 'strings', boolean>; // `editor.quickSuggestions`
|
||||
|
||||
autoScaleInterface: boolean; // `arduino.window.autoScale`
|
||||
interfaceScale: number; // `arduino.window.zoomLevel` https://github.com/eclipse-theia/theia/issues/8751
|
||||
checkForUpdates?: boolean; // `arduino.ide.autoUpdate`
|
||||
verboseOnCompile: boolean; // `arduino.compile.verbose`
|
||||
compilerWarnings: CompilerWarnings; // `arduino.compile.warnings`
|
||||
verboseOnUpload: boolean; // `arduino.upload.verbose`
|
||||
verifyAfterUpload: boolean; // `arduino.upload.verify`
|
||||
enableLsLogs: boolean; // `arduino.language.log`
|
||||
sketchbookShowAllFiles: boolean; // `arduino.sketchbook.showAllFiles`
|
||||
|
||||
sketchbookPath: string; // CLI
|
||||
additionalUrls: string[]; // CLI
|
||||
network: Network; // CLI
|
||||
}
|
||||
export namespace Settings {
|
||||
export function belongsToCli<K extends keyof Settings>(key: K): boolean {
|
||||
return key === 'sketchbookPath' || key === 'additionalUrls';
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class SettingsService {
|
||||
@inject(FileService)
|
||||
protected readonly fileService: FileService;
|
||||
|
||||
@inject(FileSystemExt)
|
||||
protected readonly fileSystemExt: FileSystemExt;
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
|
||||
@inject(PreferenceService)
|
||||
protected readonly preferenceService: PreferenceService;
|
||||
|
||||
@inject(FrontendApplicationStateService)
|
||||
protected readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
protected readonly onDidChangeEmitter = new Emitter<Readonly<Settings>>();
|
||||
readonly onDidChange = this.onDidChangeEmitter.event;
|
||||
|
||||
protected ready = new Deferred<void>();
|
||||
protected _settings: Settings;
|
||||
|
||||
@postConstruct()
|
||||
protected async init(): Promise<void> {
|
||||
await this.appStateService.reachedState('ready'); // Hack for https://github.com/eclipse-theia/theia/issues/8993
|
||||
const settings = await this.loadSettings();
|
||||
this._settings = deepClone(settings);
|
||||
this.ready.resolve();
|
||||
}
|
||||
|
||||
protected async loadSettings(): Promise<Settings> {
|
||||
await this.preferenceService.ready;
|
||||
const [
|
||||
editorFontSize,
|
||||
themeId,
|
||||
autoSave,
|
||||
quickSuggestions,
|
||||
autoScaleInterface,
|
||||
interfaceScale,
|
||||
// checkForUpdates,
|
||||
verboseOnCompile,
|
||||
compilerWarnings,
|
||||
verboseOnUpload,
|
||||
verifyAfterUpload,
|
||||
enableLsLogs,
|
||||
sketchbookShowAllFiles,
|
||||
cliConfig,
|
||||
] = await Promise.all([
|
||||
this.preferenceService.get<number>('editor.fontSize', 12),
|
||||
this.preferenceService.get<string>(
|
||||
'workbench.colorTheme',
|
||||
'arduino-theme'
|
||||
),
|
||||
this.preferenceService.get<'on' | 'off'>('editor.autoSave', 'on'),
|
||||
this.preferenceService.get<Record<string, unknown>>(
|
||||
'editor.quickSuggestion',
|
||||
{
|
||||
other: false,
|
||||
comments: false,
|
||||
strings: false,
|
||||
}
|
||||
),
|
||||
this.preferenceService.get<boolean>('arduino.window.autoScale', true),
|
||||
this.preferenceService.get<number>('arduino.window.zoomLevel', 0),
|
||||
// this.preferenceService.get<string>('arduino.ide.autoUpdate', true),
|
||||
this.preferenceService.get<boolean>('arduino.compile.verbose', true),
|
||||
this.preferenceService.get<any>('arduino.compile.warnings', 'None'),
|
||||
this.preferenceService.get<boolean>('arduino.upload.verbose', true),
|
||||
this.preferenceService.get<boolean>('arduino.upload.verify', true),
|
||||
this.preferenceService.get<boolean>('arduino.language.log', true),
|
||||
this.preferenceService.get<boolean>(
|
||||
'arduino.sketchbook.showAllFiles',
|
||||
false
|
||||
),
|
||||
this.configService.getConfiguration(),
|
||||
]);
|
||||
const { additionalUrls, sketchDirUri, network } = cliConfig;
|
||||
const sketchbookPath = await this.fileService.fsPath(new URI(sketchDirUri));
|
||||
return {
|
||||
editorFontSize,
|
||||
themeId,
|
||||
autoSave,
|
||||
quickSuggestions,
|
||||
autoScaleInterface,
|
||||
interfaceScale,
|
||||
// checkForUpdates,
|
||||
verboseOnCompile,
|
||||
compilerWarnings,
|
||||
verboseOnUpload,
|
||||
verifyAfterUpload,
|
||||
enableLsLogs,
|
||||
sketchbookShowAllFiles,
|
||||
additionalUrls,
|
||||
sketchbookPath,
|
||||
network,
|
||||
};
|
||||
}
|
||||
|
||||
async settings(): Promise<Settings> {
|
||||
await this.ready.promise;
|
||||
return this._settings;
|
||||
}
|
||||
|
||||
async update(settings: Settings, fireDidChange = false): Promise<void> {
|
||||
await this.ready.promise;
|
||||
for (const key of Object.keys(settings)) {
|
||||
this._settings[key] = settings[key];
|
||||
}
|
||||
if (fireDidChange) {
|
||||
this.onDidChangeEmitter.fire(this._settings);
|
||||
}
|
||||
}
|
||||
|
||||
async reset(): Promise<void> {
|
||||
const settings = await this.loadSettings();
|
||||
return this.update(settings, true);
|
||||
}
|
||||
|
||||
async validate(
|
||||
settings: MaybePromise<Settings> = this.settings()
|
||||
): Promise<string | true> {
|
||||
try {
|
||||
const { sketchbookPath, editorFontSize, themeId } = await settings;
|
||||
const sketchbookDir = await this.fileSystemExt.getUri(sketchbookPath);
|
||||
if (!(await this.fileService.exists(new URI(sketchbookDir)))) {
|
||||
return `Invalid sketchbook location: ${sketchbookPath}`;
|
||||
}
|
||||
if (editorFontSize <= 0) {
|
||||
return 'Invalid editor font size. It must be a positive integer.';
|
||||
}
|
||||
if (
|
||||
!ThemeService.get()
|
||||
.getThemes()
|
||||
.find(({ id }) => id === themeId)
|
||||
) {
|
||||
return 'Invalid theme.';
|
||||
}
|
||||
return true;
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
return err.message;
|
||||
}
|
||||
return String(err);
|
||||
}
|
||||
}
|
||||
|
||||
async save(): Promise<string | true> {
|
||||
await this.ready.promise;
|
||||
const {
|
||||
editorFontSize,
|
||||
themeId,
|
||||
autoSave,
|
||||
quickSuggestions,
|
||||
autoScaleInterface,
|
||||
interfaceScale,
|
||||
// checkForUpdates,
|
||||
verboseOnCompile,
|
||||
compilerWarnings,
|
||||
verboseOnUpload,
|
||||
verifyAfterUpload,
|
||||
enableLsLogs,
|
||||
sketchbookPath,
|
||||
additionalUrls,
|
||||
network,
|
||||
sketchbookShowAllFiles,
|
||||
} = this._settings;
|
||||
const [config, sketchDirUri] = await Promise.all([
|
||||
this.configService.getConfiguration(),
|
||||
this.fileSystemExt.getUri(sketchbookPath),
|
||||
]);
|
||||
(config as any).additionalUrls = additionalUrls;
|
||||
(config as any).sketchDirUri = sketchDirUri;
|
||||
(config as any).network = network;
|
||||
|
||||
await Promise.all([
|
||||
this.preferenceService.set(
|
||||
'editor.fontSize',
|
||||
editorFontSize,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
'workbench.colorTheme',
|
||||
themeId,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
'editor.autoSave',
|
||||
autoSave,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
'editor.quickSuggestions',
|
||||
quickSuggestions,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
'arduino.window.autoScale',
|
||||
autoScaleInterface,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
'arduino.window.zoomLevel',
|
||||
interfaceScale,
|
||||
PreferenceScope.User
|
||||
),
|
||||
// this.preferenceService.set('arduino.ide.autoUpdate', checkForUpdates, PreferenceScope.User),
|
||||
this.preferenceService.set(
|
||||
'arduino.compile.verbose',
|
||||
verboseOnCompile,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
'arduino.compile.warnings',
|
||||
compilerWarnings,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
'arduino.upload.verbose',
|
||||
verboseOnUpload,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
'arduino.upload.verify',
|
||||
verifyAfterUpload,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
'arduino.language.log',
|
||||
enableLsLogs,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
'arduino.sketchbook.showAllFiles',
|
||||
sketchbookShowAllFiles,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.configService.setConfiguration(config),
|
||||
]);
|
||||
this.onDidChangeEmitter.fire(this._settings);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} from '../../../common/protocol';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { Settings, SettingsService } from './settings';
|
||||
import { AdditionalUrlsDialog } from './settings-dialog';
|
||||
import { AsyncLocalizationProvider } from '@theia/core/lib/common/i18n/localization';
|
||||
|
||||
export class SettingsComponent extends React.Component<
|
||||
SettingsComponent.Props,
|
||||
@@ -349,8 +63,8 @@ export class SettingsComponent extends React.Component<
|
||||
return (
|
||||
<Tabs>
|
||||
<TabList>
|
||||
<Tab>Settings</Tab>
|
||||
<Tab>Network</Tab>
|
||||
<Tab>{nls.localize('vscode/settingsTree/settings', 'Settings')}</Tab>
|
||||
<Tab>{nls.localize('arduino/preferences/network', 'Network')}</Tab>
|
||||
</TabList>
|
||||
<TabPanel>{this.renderSettings()}</TabPanel>
|
||||
<TabPanel>{this.renderNetwork()}</TabPanel>
|
||||
@@ -361,7 +75,10 @@ export class SettingsComponent extends React.Component<
|
||||
protected renderSettings(): React.ReactNode {
|
||||
return (
|
||||
<div className="content noselect">
|
||||
Sketchbook location:
|
||||
{nls.localize(
|
||||
'arduino/preferences/sketchbook.location',
|
||||
'Sketchbook location'
|
||||
) + ':'}
|
||||
<div className="flex-line">
|
||||
<input
|
||||
className="theia-input stretch"
|
||||
@@ -373,7 +90,7 @@ export class SettingsComponent extends React.Component<
|
||||
className="theia-button shrink"
|
||||
onClick={this.browseSketchbookDidClick}
|
||||
>
|
||||
Browse
|
||||
{nls.localize('arduino/preferences/browse', 'Browse')}
|
||||
</button>
|
||||
</div>
|
||||
<label className="flex-line">
|
||||
@@ -382,15 +99,49 @@ export class SettingsComponent extends React.Component<
|
||||
checked={this.state.sketchbookShowAllFiles === true}
|
||||
onChange={this.sketchbookShowAllFilesDidChange}
|
||||
/>
|
||||
Show files inside Sketches
|
||||
{nls.localize(
|
||||
'arduino/preferences/files.inside.sketches',
|
||||
'Show files inside Sketches'
|
||||
)}
|
||||
</label>
|
||||
<div className="flex-line">
|
||||
<div className="column">
|
||||
<div className="flex-line">Editor font size:</div>
|
||||
<div className="flex-line">Interface scale:</div>
|
||||
<div className="flex-line">Theme:</div>
|
||||
<div className="flex-line">Show verbose output during:</div>
|
||||
<div className="flex-line">Compiler warnings:</div>
|
||||
<div className="flex-line">
|
||||
{nls.localize(
|
||||
'arduino/preferences/editorFontSize',
|
||||
'Editor font size'
|
||||
) + ':'}
|
||||
</div>
|
||||
<div className="flex-line">
|
||||
{nls.localize(
|
||||
'arduino/preferences/interfaceScale',
|
||||
'Interface scale'
|
||||
) + ':'}
|
||||
</div>
|
||||
<div className="flex-line">
|
||||
{nls.localize(
|
||||
'vscode/themes.contribution/selectTheme.label',
|
||||
'Theme'
|
||||
) + ':'}
|
||||
</div>
|
||||
<div className="flex-line">
|
||||
{nls.localize(
|
||||
'vscode/editorStatus/status.editor.mode',
|
||||
'Language'
|
||||
) + ':'}
|
||||
</div>
|
||||
<div className="flex-line">
|
||||
{nls.localize(
|
||||
'arduino/preferences/showVerbose',
|
||||
'Show verbose output during'
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-line">
|
||||
{nls.localize(
|
||||
'arduino/preferences/compilerWarnings',
|
||||
'Compiler warnings'
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="column">
|
||||
<div className="flex-line">
|
||||
@@ -411,7 +162,7 @@ export class SettingsComponent extends React.Component<
|
||||
checked={this.state.autoScaleInterface}
|
||||
onChange={this.autoScaleInterfaceDidChange}
|
||||
/>
|
||||
Automatic
|
||||
{nls.localize('arduino/preferences/automatic', 'Automatic')}
|
||||
</label>
|
||||
<input
|
||||
className="theia-input small with-margin"
|
||||
@@ -431,7 +182,7 @@ export class SettingsComponent extends React.Component<
|
||||
ThemeService.get()
|
||||
.getThemes()
|
||||
.find(({ id }) => id === this.state.themeId)?.label ||
|
||||
'Unknown'
|
||||
nls.localize('arduino/common/unknown', 'Unknown')
|
||||
}
|
||||
onChange={this.themeDidChange}
|
||||
>
|
||||
@@ -444,6 +195,27 @@ export class SettingsComponent extends React.Component<
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex-line">
|
||||
<select
|
||||
className="theia-select"
|
||||
value={this.state.currentLanguage}
|
||||
onChange={this.languageDidChange}
|
||||
>
|
||||
{this.state.languages.map((label) => (
|
||||
<option key={label} value={label}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<span style={{ marginLeft: '5px' }}>
|
||||
(
|
||||
{nls.localize(
|
||||
'vscode/extensionsActions/reloadRequired',
|
||||
'Reload required'
|
||||
)}
|
||||
)
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex-line">
|
||||
<label className="flex-line">
|
||||
<input
|
||||
@@ -451,7 +223,7 @@ export class SettingsComponent extends React.Component<
|
||||
checked={this.state.verboseOnCompile}
|
||||
onChange={this.verboseOnCompileDidChange}
|
||||
/>
|
||||
compile
|
||||
{nls.localize('arduino/preferences/compile', 'compile')}
|
||||
</label>
|
||||
<label className="flex-line">
|
||||
<input
|
||||
@@ -459,7 +231,7 @@ export class SettingsComponent extends React.Component<
|
||||
checked={this.state.verboseOnUpload}
|
||||
onChange={this.verboseOnUploadDidChange}
|
||||
/>
|
||||
upload
|
||||
{nls.localize('arduino/preferences/upload', 'upload')}
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex-line">
|
||||
@@ -483,7 +255,10 @@ export class SettingsComponent extends React.Component<
|
||||
checked={this.state.verifyAfterUpload}
|
||||
onChange={this.verifyAfterUploadDidChange}
|
||||
/>
|
||||
Verify code after upload
|
||||
{nls.localize(
|
||||
'arduino/preferences/verifyAfterUpload',
|
||||
'Verify code after upload'
|
||||
)}
|
||||
</label>
|
||||
<label className="flex-line">
|
||||
<input
|
||||
@@ -492,7 +267,10 @@ export class SettingsComponent extends React.Component<
|
||||
onChange={this.checkForUpdatesDidChange}
|
||||
disabled={true}
|
||||
/>
|
||||
Check for updates on startup
|
||||
{nls.localize(
|
||||
'arduino/preferences/checkForUpdates',
|
||||
'Check for updates on startup'
|
||||
)}
|
||||
</label>
|
||||
<label className="flex-line">
|
||||
<input
|
||||
@@ -500,7 +278,10 @@ export class SettingsComponent extends React.Component<
|
||||
checked={this.state.autoSave === 'on'}
|
||||
onChange={this.autoSaveDidChange}
|
||||
/>
|
||||
Auto save
|
||||
{nls.localize(
|
||||
'vscode/fileActions.contribution/miAutoSave',
|
||||
'Auto save'
|
||||
)}
|
||||
</label>
|
||||
<label className="flex-line">
|
||||
<input
|
||||
@@ -508,18 +289,16 @@ export class SettingsComponent extends React.Component<
|
||||
checked={this.state.quickSuggestions.other === true}
|
||||
onChange={this.quickSuggestionsOtherDidChange}
|
||||
/>
|
||||
Editor Quick Suggestions
|
||||
</label>
|
||||
<label className="flex-line">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={this.state.enableLsLogs}
|
||||
onChange={this.enableLsLogsDidChange}
|
||||
/>
|
||||
Enable language server logging
|
||||
{nls.localize(
|
||||
'arduino/preferences/editorQuickSuggestions',
|
||||
'Editor Quick Suggestions'
|
||||
)}
|
||||
</label>
|
||||
<div className="flex-line">
|
||||
Additional boards manager URLs:
|
||||
{nls.localize(
|
||||
'arduino/preferences/additionalManagerURLs',
|
||||
'Additional boards manager URLs'
|
||||
) + ':'}
|
||||
<input
|
||||
className="theia-input stretch with-margin"
|
||||
type="text"
|
||||
@@ -545,7 +324,7 @@ export class SettingsComponent extends React.Component<
|
||||
checked={this.state.network === 'none'}
|
||||
onChange={this.noProxyDidChange}
|
||||
/>
|
||||
No proxy
|
||||
{nls.localize('arduino/preferences/noProxy', 'No proxy')}
|
||||
</label>
|
||||
<label className="flex-line">
|
||||
<input
|
||||
@@ -553,7 +332,10 @@ export class SettingsComponent extends React.Component<
|
||||
checked={this.state.network !== 'none'}
|
||||
onChange={this.manualProxyDidChange}
|
||||
/>
|
||||
Manual proxy configuration
|
||||
{nls.localize(
|
||||
'arduino/preferences/manualProxy',
|
||||
'Manual proxy configuration'
|
||||
)}
|
||||
</label>
|
||||
</form>
|
||||
{this.renderProxySettings()}
|
||||
@@ -686,8 +468,11 @@ export class SettingsComponent extends React.Component<
|
||||
|
||||
protected browseSketchbookDidClick = async () => {
|
||||
const uri = await this.props.fileDialogService.showOpenDialog({
|
||||
title: 'Select new sketchbook location',
|
||||
openLabel: 'Choose',
|
||||
title: nls.localize(
|
||||
'arduino/preferences/newSketchbookLocation',
|
||||
'Select new sketchbook location'
|
||||
),
|
||||
openLabel: nls.localize('arduino/preferences/choose', 'Choose'),
|
||||
canSelectFiles: false,
|
||||
canSelectMany: false,
|
||||
canSelectFolders: true,
|
||||
@@ -731,12 +516,6 @@ export class SettingsComponent extends React.Component<
|
||||
this.setState({ autoScaleInterface: event.target.checked });
|
||||
};
|
||||
|
||||
protected enableLsLogsDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
this.setState({ enableLsLogs: event.target.checked });
|
||||
};
|
||||
|
||||
protected interfaceScaleDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
@@ -799,6 +578,13 @@ export class SettingsComponent extends React.Component<
|
||||
}
|
||||
};
|
||||
|
||||
protected languageDidChange = (
|
||||
event: React.ChangeEvent<HTMLSelectElement>
|
||||
) => {
|
||||
const selectedLanguage = event.target.value;
|
||||
this.setState({ currentLanguage: selectedLanguage });
|
||||
};
|
||||
|
||||
protected compilerWarningsDidChange = (
|
||||
event: React.ChangeEvent<HTMLSelectElement>
|
||||
) => {
|
||||
@@ -921,167 +707,7 @@ export namespace SettingsComponent {
|
||||
readonly fileService: FileService;
|
||||
readonly fileDialogService: FileDialogService;
|
||||
readonly windowService: WindowService;
|
||||
readonly localizationProvider: AsyncLocalizationProvider;
|
||||
}
|
||||
export type State = Settings;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class SettingsWidget extends ReactWidget {
|
||||
@inject(SettingsService)
|
||||
protected readonly settingsService: SettingsService;
|
||||
|
||||
@inject(FileService)
|
||||
protected readonly fileService: FileService;
|
||||
|
||||
@inject(FileDialogService)
|
||||
protected readonly fileDialogService: FileDialogService;
|
||||
|
||||
@inject(WindowService)
|
||||
protected readonly windowService: WindowService;
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
return (
|
||||
<SettingsComponent
|
||||
settingsService={this.settingsService}
|
||||
fileService={this.fileService}
|
||||
fileDialogService={this.fileDialogService}
|
||||
windowService={this.windowService}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class SettingsDialogProps extends DialogProps {}
|
||||
|
||||
@injectable()
|
||||
export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
|
||||
@inject(SettingsService)
|
||||
protected readonly settingsService: SettingsService;
|
||||
|
||||
@inject(SettingsWidget)
|
||||
protected readonly widget: SettingsWidget;
|
||||
|
||||
constructor(
|
||||
@inject(SettingsDialogProps)
|
||||
protected readonly props: SettingsDialogProps
|
||||
) {
|
||||
super(props);
|
||||
this.contentNode.classList.add('arduino-settings-dialog');
|
||||
this.appendCloseButton('CANCEL');
|
||||
this.appendAcceptButton('OK');
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.toDispose.push(
|
||||
this.settingsService.onDidChange(this.validate.bind(this))
|
||||
);
|
||||
}
|
||||
|
||||
protected async isValid(settings: Promise<Settings>): Promise<DialogError> {
|
||||
const result = await this.settingsService.validate(settings);
|
||||
if (typeof result === 'string') {
|
||||
return result;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
get value(): Promise<Settings> {
|
||||
return this.settingsService.settings();
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
}
|
||||
Widget.attach(this.widget, this.contentNode);
|
||||
this.toDisposeOnDetach.push(
|
||||
this.settingsService.onDidChange(() => this.update())
|
||||
);
|
||||
super.onAfterAttach(msg);
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message) {
|
||||
super.onUpdateRequest(msg);
|
||||
this.widget.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
|
||||
// calling settingsService.reset() in order to reload the settings from the preferenceService
|
||||
// and update the UI including changes triggerd from the command palette
|
||||
this.settingsService.reset();
|
||||
|
||||
this.widget.activate();
|
||||
}
|
||||
}
|
||||
|
||||
export class AdditionalUrlsDialog extends AbstractDialog<string[]> {
|
||||
protected readonly textArea: HTMLTextAreaElement;
|
||||
|
||||
constructor(urls: string[], windowService: WindowService) {
|
||||
super({ title: 'Additional Boards Manager URLs' });
|
||||
|
||||
this.contentNode.classList.add('additional-urls-dialog');
|
||||
|
||||
const description = document.createElement('div');
|
||||
description.textContent = 'Enter additional URLs, one for each row';
|
||||
description.style.marginBottom = '5px';
|
||||
this.contentNode.appendChild(description);
|
||||
|
||||
this.textArea = document.createElement('textarea');
|
||||
this.textArea.className = 'theia-input';
|
||||
this.textArea.setAttribute('style', 'flex: 0;');
|
||||
this.textArea.value = urls
|
||||
.filter((url) => url.trim())
|
||||
.filter((url) => !!url)
|
||||
.join('\n');
|
||||
this.textArea.wrap = 'soft';
|
||||
this.textArea.cols = 90;
|
||||
this.textArea.rows = 5;
|
||||
this.contentNode.appendChild(this.textArea);
|
||||
|
||||
const anchor = document.createElement('div');
|
||||
anchor.classList.add('link');
|
||||
anchor.textContent = 'Click for a list of unofficial board support URLs';
|
||||
anchor.style.marginTop = '5px';
|
||||
anchor.style.cursor = 'pointer';
|
||||
this.addEventListener(anchor, 'click', () =>
|
||||
windowService.openNewWindow(
|
||||
'https://github.com/arduino/Arduino/wiki/Unofficial-list-of-3rd-party-boards-support-urls',
|
||||
{ external: true }
|
||||
)
|
||||
);
|
||||
this.contentNode.appendChild(anchor);
|
||||
|
||||
this.appendAcceptButton('OK');
|
||||
this.appendCloseButton('Cancel');
|
||||
}
|
||||
|
||||
get value(): string[] {
|
||||
return this.textArea.value
|
||||
.split('\n')
|
||||
.map((url) => url.trim())
|
||||
.filter((url) => !!url);
|
||||
}
|
||||
|
||||
protected onAfterAttach(message: Message): void {
|
||||
super.onAfterAttach(message);
|
||||
this.addUpdateListener(this.textArea, 'input');
|
||||
}
|
||||
|
||||
protected onActivateRequest(message: Message): void {
|
||||
super.onActivateRequest(message);
|
||||
this.textArea.focus();
|
||||
}
|
||||
|
||||
protected handleEnter(event: KeyboardEvent): boolean | void {
|
||||
if (event.target instanceof HTMLInputElement) {
|
||||
return super.handleEnter(event);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
export type State = Settings & { languages: string[] };
|
||||
}
|
@@ -0,0 +1,193 @@
|
||||
import * as React from 'react';
|
||||
import { injectable, inject, postConstruct } from 'inversify';
|
||||
import { Widget } from '@phosphor/widgets';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import { DialogError, ReactWidget } from '@theia/core/lib/browser';
|
||||
import { AbstractDialog, DialogProps } from '@theia/core/lib/browser';
|
||||
import { Settings, SettingsService } from './settings';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { FileDialogService } from '@theia/filesystem/lib/browser/file-dialog/file-dialog-service';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { SettingsComponent } from './settings-component';
|
||||
import { AsyncLocalizationProvider } from '@theia/core/lib/common/i18n/localization';
|
||||
|
||||
@injectable()
|
||||
export class SettingsWidget extends ReactWidget {
|
||||
@inject(SettingsService)
|
||||
protected readonly settingsService: SettingsService;
|
||||
|
||||
@inject(FileService)
|
||||
protected readonly fileService: FileService;
|
||||
|
||||
@inject(FileDialogService)
|
||||
protected readonly fileDialogService: FileDialogService;
|
||||
|
||||
@inject(WindowService)
|
||||
protected readonly windowService: WindowService;
|
||||
|
||||
@inject(AsyncLocalizationProvider)
|
||||
protected readonly localizationProvider: AsyncLocalizationProvider;
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
return (
|
||||
<SettingsComponent
|
||||
settingsService={this.settingsService}
|
||||
fileService={this.fileService}
|
||||
fileDialogService={this.fileDialogService}
|
||||
windowService={this.windowService}
|
||||
localizationProvider={this.localizationProvider}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class SettingsDialogProps extends DialogProps {}
|
||||
|
||||
@injectable()
|
||||
export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
|
||||
@inject(SettingsService)
|
||||
protected readonly settingsService: SettingsService;
|
||||
|
||||
@inject(SettingsWidget)
|
||||
protected readonly widget: SettingsWidget;
|
||||
|
||||
constructor(
|
||||
@inject(SettingsDialogProps)
|
||||
protected readonly props: SettingsDialogProps
|
||||
) {
|
||||
super(props);
|
||||
this.contentNode.classList.add('arduino-settings-dialog');
|
||||
this.appendCloseButton(
|
||||
nls.localize('vscode/issueMainService/cancel', 'Cancel')
|
||||
);
|
||||
this.appendAcceptButton(nls.localize('vscode/issueMainService/ok', 'OK'));
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.toDispose.push(
|
||||
this.settingsService.onDidChange(this.validate.bind(this))
|
||||
);
|
||||
}
|
||||
|
||||
protected async isValid(settings: Promise<Settings>): Promise<DialogError> {
|
||||
const result = await this.settingsService.validate(settings);
|
||||
if (typeof result === 'string') {
|
||||
return result;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
get value(): Promise<Settings> {
|
||||
return this.settingsService.settings();
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
}
|
||||
Widget.attach(this.widget, this.contentNode);
|
||||
this.toDisposeOnDetach.push(
|
||||
this.settingsService.onDidChange(() => this.update())
|
||||
);
|
||||
super.onAfterAttach(msg);
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message) {
|
||||
super.onUpdateRequest(msg);
|
||||
this.widget.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
|
||||
// calling settingsService.reset() in order to reload the settings from the preferenceService
|
||||
// and update the UI including changes triggerd from the command palette
|
||||
this.settingsService.reset();
|
||||
|
||||
this.widget.activate();
|
||||
}
|
||||
}
|
||||
|
||||
export class AdditionalUrlsDialog extends AbstractDialog<string[]> {
|
||||
protected readonly textArea: HTMLTextAreaElement;
|
||||
|
||||
constructor(urls: string[], windowService: WindowService) {
|
||||
super({
|
||||
title: nls.localize(
|
||||
'arduino/preferences/additionalManagerURLs',
|
||||
'Additional Boards Manager URLs'
|
||||
),
|
||||
});
|
||||
|
||||
this.contentNode.classList.add('additional-urls-dialog');
|
||||
|
||||
const description = document.createElement('div');
|
||||
description.textContent = nls.localize(
|
||||
'arduino/preferences/enterAdditionalURLs',
|
||||
'Enter additional URLs, one for each row'
|
||||
);
|
||||
description.style.marginBottom = '5px';
|
||||
this.contentNode.appendChild(description);
|
||||
|
||||
this.textArea = document.createElement('textarea');
|
||||
this.textArea.className = 'theia-input';
|
||||
this.textArea.setAttribute('style', 'flex: 0;');
|
||||
this.textArea.value = urls
|
||||
.filter((url) => url.trim())
|
||||
.filter((url) => !!url)
|
||||
.join('\n');
|
||||
this.textArea.wrap = 'soft';
|
||||
this.textArea.cols = 90;
|
||||
this.textArea.rows = 5;
|
||||
this.contentNode.appendChild(this.textArea);
|
||||
|
||||
const anchor = document.createElement('div');
|
||||
anchor.classList.add('link');
|
||||
anchor.textContent = nls.localize(
|
||||
'arduino/preferences/unofficialBoardSupport',
|
||||
'Click for a list of unofficial board support URLs'
|
||||
);
|
||||
anchor.style.marginTop = '5px';
|
||||
anchor.style.cursor = 'pointer';
|
||||
this.addEventListener(anchor, 'click', () =>
|
||||
windowService.openNewWindow(
|
||||
'https://github.com/arduino/Arduino/wiki/Unofficial-list-of-3rd-party-boards-support-urls',
|
||||
{ external: true }
|
||||
)
|
||||
);
|
||||
this.contentNode.appendChild(anchor);
|
||||
|
||||
this.appendAcceptButton(nls.localize('vscode/issueMainService/ok', 'OK'));
|
||||
this.appendCloseButton(
|
||||
nls.localize('vscode/issueMainService/cancel', 'Cancel')
|
||||
);
|
||||
}
|
||||
|
||||
get value(): string[] {
|
||||
return this.textArea.value
|
||||
.split('\n')
|
||||
.map((url) => url.trim())
|
||||
.filter((url) => !!url);
|
||||
}
|
||||
|
||||
protected onAfterAttach(message: Message): void {
|
||||
super.onAfterAttach(message);
|
||||
this.addUpdateListener(this.textArea, 'input');
|
||||
}
|
||||
|
||||
protected onActivateRequest(message: Message): void {
|
||||
super.onActivateRequest(message);
|
||||
this.textArea.focus();
|
||||
}
|
||||
|
||||
protected handleEnter(event: KeyboardEvent): boolean | void {
|
||||
if (event.target instanceof HTMLInputElement) {
|
||||
return super.handleEnter(event);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
325
arduino-ide-extension/src/browser/dialogs/settings/settings.tsx
Normal file
325
arduino-ide-extension/src/browser/dialogs/settings/settings.tsx
Normal file
@@ -0,0 +1,325 @@
|
||||
import { injectable, inject, postConstruct } from 'inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { deepClone } from '@theia/core/lib/common/objects';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { ThemeService } from '@theia/core/lib/browser/theming';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import { PreferenceService, PreferenceScope } from '@theia/core/lib/browser';
|
||||
import { Index } from '../../../common/types';
|
||||
import {
|
||||
CompilerWarnings,
|
||||
ConfigService,
|
||||
FileSystemExt,
|
||||
Network,
|
||||
} from '../../../common/protocol';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { AsyncLocalizationProvider } from '@theia/core/lib/common/i18n/localization';
|
||||
|
||||
const EDITOR_SETTING = 'editor';
|
||||
const FONT_SIZE_SETTING = `${EDITOR_SETTING}.fontSize`;
|
||||
const AUTO_SAVE_SETTING = `${EDITOR_SETTING}.autoSave`;
|
||||
const QUICK_SUGGESTIONS_SETTING = `${EDITOR_SETTING}.quickSuggestions`;
|
||||
const ARDUINO_SETTING = 'arduino';
|
||||
const WINDOW_SETTING = `${ARDUINO_SETTING}.window`;
|
||||
// const IDE_SETTING = `${ARDUINO_SETTING}.ide`;
|
||||
const COMPILE_SETTING = `${ARDUINO_SETTING}.compile`;
|
||||
const UPLOAD_SETTING = `${ARDUINO_SETTING}.upload`;
|
||||
const SKETCHBOOK_SETTING = `${ARDUINO_SETTING}.sketchbook`;
|
||||
const AUTO_SCALE_SETTING = `${WINDOW_SETTING}.autoScale`;
|
||||
const ZOOM_LEVEL_SETTING = `${WINDOW_SETTING}.zoomLevel`;
|
||||
// const AUTO_UPDATE_SETTING = `${IDE_SETTING}.autoUpdate`;
|
||||
const COMPILE_VERBOSE_SETTING = `${COMPILE_SETTING}.verbose`;
|
||||
const COMPILE_WARNINGS_SETTING = `${COMPILE_SETTING}.warnings`;
|
||||
const UPLOAD_VERBOSE_SETTING = `${UPLOAD_SETTING}.verbose`;
|
||||
const UPLOAD_VERIFY_SETTING = `${UPLOAD_SETTING}.verify`;
|
||||
const SHOW_ALL_FILES_SETTING = `${SKETCHBOOK_SETTING}.showAllFiles`;
|
||||
|
||||
export interface Settings extends Index {
|
||||
editorFontSize: number; // `editor.fontSize`
|
||||
themeId: string; // `workbench.colorTheme`
|
||||
autoSave: 'on' | 'off'; // `editor.autoSave`
|
||||
quickSuggestions: Record<'other' | 'comments' | 'strings', boolean>; // `editor.quickSuggestions`
|
||||
|
||||
languages: string[]; // `languages from the plugins`
|
||||
currentLanguage: string;
|
||||
|
||||
autoScaleInterface: boolean; // `arduino.window.autoScale`
|
||||
interfaceScale: number; // `arduino.window.zoomLevel` https://github.com/eclipse-theia/theia/issues/8751
|
||||
checkForUpdates?: boolean; // `arduino.ide.autoUpdate`
|
||||
verboseOnCompile: boolean; // `arduino.compile.verbose`
|
||||
compilerWarnings: CompilerWarnings; // `arduino.compile.warnings`
|
||||
verboseOnUpload: boolean; // `arduino.upload.verbose`
|
||||
verifyAfterUpload: boolean; // `arduino.upload.verify`
|
||||
sketchbookShowAllFiles: boolean; // `arduino.sketchbook.showAllFiles`
|
||||
|
||||
sketchbookPath: string; // CLI
|
||||
additionalUrls: string[]; // CLI
|
||||
network: Network; // CLI
|
||||
}
|
||||
export namespace Settings {
|
||||
export function belongsToCli<K extends keyof Settings>(key: K): boolean {
|
||||
return key === 'sketchbookPath' || key === 'additionalUrls';
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class SettingsService {
|
||||
@inject(FileService)
|
||||
protected readonly fileService: FileService;
|
||||
|
||||
@inject(FileSystemExt)
|
||||
protected readonly fileSystemExt: FileSystemExt;
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
|
||||
@inject(PreferenceService)
|
||||
protected readonly preferenceService: PreferenceService;
|
||||
|
||||
@inject(FrontendApplicationStateService)
|
||||
protected readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
@inject(AsyncLocalizationProvider)
|
||||
protected readonly localizationProvider: AsyncLocalizationProvider;
|
||||
|
||||
protected readonly onDidChangeEmitter = new Emitter<Readonly<Settings>>();
|
||||
readonly onDidChange = this.onDidChangeEmitter.event;
|
||||
|
||||
protected ready = new Deferred<void>();
|
||||
protected _settings: Settings;
|
||||
|
||||
@postConstruct()
|
||||
protected async init(): Promise<void> {
|
||||
await this.appStateService.reachedState('ready'); // Hack for https://github.com/eclipse-theia/theia/issues/8993
|
||||
const settings = await this.loadSettings();
|
||||
this._settings = deepClone(settings);
|
||||
this.ready.resolve();
|
||||
}
|
||||
|
||||
protected async loadSettings(): Promise<Settings> {
|
||||
await this.preferenceService.ready;
|
||||
const [
|
||||
languages,
|
||||
currentLanguage,
|
||||
editorFontSize,
|
||||
themeId,
|
||||
autoSave,
|
||||
quickSuggestions,
|
||||
autoScaleInterface,
|
||||
interfaceScale,
|
||||
// checkForUpdates,
|
||||
verboseOnCompile,
|
||||
compilerWarnings,
|
||||
verboseOnUpload,
|
||||
verifyAfterUpload,
|
||||
sketchbookShowAllFiles,
|
||||
cliConfig,
|
||||
] = await Promise.all([
|
||||
['en', ...(await this.localizationProvider.getAvailableLanguages())],
|
||||
this.localizationProvider.getCurrentLanguage(),
|
||||
this.preferenceService.get<number>(FONT_SIZE_SETTING, 12),
|
||||
this.preferenceService.get<string>(
|
||||
'workbench.colorTheme',
|
||||
'arduino-theme'
|
||||
),
|
||||
this.preferenceService.get<'on' | 'off'>(AUTO_SAVE_SETTING, 'on'),
|
||||
this.preferenceService.get<
|
||||
Record<'other' | 'comments' | 'strings', boolean>
|
||||
>(QUICK_SUGGESTIONS_SETTING, {
|
||||
other: false,
|
||||
comments: false,
|
||||
strings: false,
|
||||
}),
|
||||
this.preferenceService.get<boolean>(AUTO_SCALE_SETTING, true),
|
||||
this.preferenceService.get<number>(ZOOM_LEVEL_SETTING, 0),
|
||||
// this.preferenceService.get<string>(AUTO_UPDATE_SETTING, true),
|
||||
this.preferenceService.get<boolean>(COMPILE_VERBOSE_SETTING, true),
|
||||
this.preferenceService.get<any>(COMPILE_WARNINGS_SETTING, 'None'),
|
||||
this.preferenceService.get<boolean>(UPLOAD_VERBOSE_SETTING, true),
|
||||
this.preferenceService.get<boolean>(UPLOAD_VERIFY_SETTING, true),
|
||||
this.preferenceService.get<boolean>(SHOW_ALL_FILES_SETTING, false),
|
||||
this.configService.getConfiguration(),
|
||||
]);
|
||||
const { additionalUrls, sketchDirUri, network } = cliConfig;
|
||||
const sketchbookPath = await this.fileService.fsPath(new URI(sketchDirUri));
|
||||
return {
|
||||
editorFontSize,
|
||||
themeId,
|
||||
languages,
|
||||
currentLanguage,
|
||||
autoSave,
|
||||
quickSuggestions,
|
||||
autoScaleInterface,
|
||||
interfaceScale,
|
||||
// checkForUpdates,
|
||||
verboseOnCompile,
|
||||
compilerWarnings,
|
||||
verboseOnUpload,
|
||||
verifyAfterUpload,
|
||||
sketchbookShowAllFiles,
|
||||
additionalUrls,
|
||||
sketchbookPath,
|
||||
network,
|
||||
};
|
||||
}
|
||||
|
||||
async settings(): Promise<Settings> {
|
||||
await this.ready.promise;
|
||||
return this._settings;
|
||||
}
|
||||
|
||||
async update(settings: Settings, fireDidChange = false): Promise<void> {
|
||||
await this.ready.promise;
|
||||
for (const key of Object.keys(settings)) {
|
||||
this._settings[key] = settings[key];
|
||||
}
|
||||
if (fireDidChange) {
|
||||
this.onDidChangeEmitter.fire(this._settings);
|
||||
}
|
||||
}
|
||||
|
||||
async reset(): Promise<void> {
|
||||
const settings = await this.loadSettings();
|
||||
return this.update(settings, true);
|
||||
}
|
||||
|
||||
async validate(
|
||||
settings: MaybePromise<Settings> = this.settings()
|
||||
): Promise<string | true> {
|
||||
try {
|
||||
const { sketchbookPath, editorFontSize, themeId } = await settings;
|
||||
const sketchbookDir = await this.fileSystemExt.getUri(sketchbookPath);
|
||||
if (!(await this.fileService.exists(new URI(sketchbookDir)))) {
|
||||
return nls.localize(
|
||||
'arduino/preferences/invalid.sketchbook.location',
|
||||
'Invalid sketchbook location: {0}',
|
||||
sketchbookPath
|
||||
);
|
||||
}
|
||||
if (editorFontSize <= 0) {
|
||||
return nls.localize(
|
||||
'arduino/preferences/invalid.editorFontSize',
|
||||
'Invalid editor font size. It must be a positive integer.'
|
||||
);
|
||||
}
|
||||
if (
|
||||
!ThemeService.get()
|
||||
.getThemes()
|
||||
.find(({ id }) => id === themeId)
|
||||
) {
|
||||
return nls.localize(
|
||||
'arduino/preferences/invalid.theme',
|
||||
'Invalid theme.'
|
||||
);
|
||||
}
|
||||
return true;
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
return err.message;
|
||||
}
|
||||
return String(err);
|
||||
}
|
||||
}
|
||||
|
||||
async save(): Promise<string | true> {
|
||||
await this.ready.promise;
|
||||
const {
|
||||
currentLanguage,
|
||||
editorFontSize,
|
||||
themeId,
|
||||
autoSave,
|
||||
quickSuggestions,
|
||||
autoScaleInterface,
|
||||
interfaceScale,
|
||||
// checkForUpdates,
|
||||
verboseOnCompile,
|
||||
compilerWarnings,
|
||||
verboseOnUpload,
|
||||
verifyAfterUpload,
|
||||
sketchbookPath,
|
||||
additionalUrls,
|
||||
network,
|
||||
sketchbookShowAllFiles,
|
||||
} = this._settings;
|
||||
const [config, sketchDirUri] = await Promise.all([
|
||||
this.configService.getConfiguration(),
|
||||
this.fileSystemExt.getUri(sketchbookPath),
|
||||
]);
|
||||
(config as any).additionalUrls = additionalUrls;
|
||||
(config as any).sketchDirUri = sketchDirUri;
|
||||
(config as any).network = network;
|
||||
(config as any).locale = currentLanguage;
|
||||
|
||||
await Promise.all([
|
||||
this.preferenceService.set(
|
||||
'editor.fontSize',
|
||||
editorFontSize,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
'workbench.colorTheme',
|
||||
themeId,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
'editor.autoSave',
|
||||
autoSave,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
'editor.quickSuggestions',
|
||||
quickSuggestions,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
AUTO_SCALE_SETTING,
|
||||
autoScaleInterface,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
ZOOM_LEVEL_SETTING,
|
||||
interfaceScale,
|
||||
PreferenceScope.User
|
||||
),
|
||||
// this.preferenceService.set(AUTO_UPDATE_SETTING, checkForUpdates, PreferenceScope.User),
|
||||
this.preferenceService.set(
|
||||
COMPILE_VERBOSE_SETTING,
|
||||
verboseOnCompile,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
COMPILE_WARNINGS_SETTING,
|
||||
compilerWarnings,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
UPLOAD_VERBOSE_SETTING,
|
||||
verboseOnUpload,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
UPLOAD_VERIFY_SETTING,
|
||||
verifyAfterUpload,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
SHOW_ALL_FILES_SETTING,
|
||||
sketchbookShowAllFiles,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.configService.setConfiguration(config),
|
||||
]);
|
||||
this.onDidChangeEmitter.fire(this._settings);
|
||||
|
||||
// after saving all the settings, if we need to change the language we need to perform a reload
|
||||
if (currentLanguage !== nls.locale) {
|
||||
window.localStorage.setItem(nls.localeId, currentLanguage);
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -0,0 +1,98 @@
|
||||
import * as React from 'react';
|
||||
import { BoardUserField } from '../../../common/protocol';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
export const UserFieldsComponent = ({
|
||||
initialBoardUserFields,
|
||||
updateUserFields,
|
||||
cancel,
|
||||
accept,
|
||||
}: {
|
||||
initialBoardUserFields: BoardUserField[];
|
||||
updateUserFields: (userFields: BoardUserField[]) => void;
|
||||
cancel: () => void;
|
||||
accept: () => Promise<void>;
|
||||
}): React.ReactElement => {
|
||||
const [boardUserFields, setBoardUserFields] = React.useState<
|
||||
BoardUserField[]
|
||||
>(initialBoardUserFields);
|
||||
|
||||
const [uploadButtonDisabled, setUploadButtonDisabled] =
|
||||
React.useState<boolean>(true);
|
||||
|
||||
React.useEffect(() => {
|
||||
setBoardUserFields(initialBoardUserFields);
|
||||
}, [initialBoardUserFields]);
|
||||
|
||||
const updateUserField =
|
||||
(index: number) => (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newBoardUserFields = [...boardUserFields];
|
||||
newBoardUserFields[index].value = e.target.value;
|
||||
setBoardUserFields(newBoardUserFields);
|
||||
};
|
||||
|
||||
const allFieldsHaveValues = (userFields: BoardUserField[]): boolean => {
|
||||
return (
|
||||
userFields &&
|
||||
userFields.length > 0 &&
|
||||
userFields
|
||||
.map<boolean>((field: BoardUserField): boolean => {
|
||||
return field.value.length > 0;
|
||||
})
|
||||
.reduce((previous: boolean, current: boolean): boolean => {
|
||||
return previous && current;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
updateUserFields(boardUserFields);
|
||||
setUploadButtonDisabled(!allFieldsHaveValues(boardUserFields));
|
||||
}, [boardUserFields]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="user-fields-container">
|
||||
<div className="user-fields-list">
|
||||
{boardUserFields.map((field, index) => {
|
||||
return (
|
||||
<div className="dialogSection" key={index}>
|
||||
<div className="dialogRow">
|
||||
<label className="field-label">{field.label}</label>
|
||||
</div>
|
||||
<div className="dialogRow">
|
||||
<input
|
||||
type={field.secret ? 'password' : 'text'}
|
||||
value={field.value}
|
||||
className="theia-input"
|
||||
placeholder={'Enter ' + field.label}
|
||||
onChange={updateUserField(index)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="dialogSection">
|
||||
<div className="dialogRow button-container">
|
||||
<button
|
||||
type="button"
|
||||
className="theia-button secondary install-cert-btn"
|
||||
onClick={cancel}
|
||||
>
|
||||
{nls.localize('arduino/userFields/cancel', 'Cancel')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="theia-button primary install-cert-btn"
|
||||
disabled={uploadButtonDisabled}
|
||||
onClick={accept}
|
||||
>
|
||||
{nls.localize('arduino/userFields/upload', 'Upload')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@@ -0,0 +1,121 @@
|
||||
import * as React from 'react';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import {
|
||||
AbstractDialog,
|
||||
DialogProps,
|
||||
ReactWidget,
|
||||
} from '@theia/core/lib/browser';
|
||||
import { Widget } from '@phosphor/widgets';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import { UploadSketch } from '../../contributions/upload-sketch';
|
||||
import { UserFieldsComponent } from './user-fields-component';
|
||||
import { BoardUserField } from '../../../common/protocol';
|
||||
|
||||
@injectable()
|
||||
export class UserFieldsDialogWidget extends ReactWidget {
|
||||
protected _currentUserFields: BoardUserField[] = [];
|
||||
|
||||
constructor(private cancel: () => void, private accept: () => Promise<void>) {
|
||||
super();
|
||||
}
|
||||
|
||||
set currentUserFields(userFields: BoardUserField[]) {
|
||||
this.setUserFields(userFields);
|
||||
}
|
||||
|
||||
get currentUserFields(): BoardUserField[] {
|
||||
return this._currentUserFields;
|
||||
}
|
||||
|
||||
resetUserFieldsValue(): void {
|
||||
this._currentUserFields = this._currentUserFields.map((field) => {
|
||||
field.value = '';
|
||||
return field;
|
||||
});
|
||||
}
|
||||
|
||||
protected setUserFields(userFields: BoardUserField[]): void {
|
||||
this._currentUserFields = userFields;
|
||||
}
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
return (
|
||||
<form>
|
||||
<UserFieldsComponent
|
||||
initialBoardUserFields={this._currentUserFields}
|
||||
updateUserFields={this.setUserFields.bind(this)}
|
||||
cancel={this.cancel}
|
||||
accept={this.accept}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class UserFieldsDialogProps extends DialogProps {}
|
||||
|
||||
@injectable()
|
||||
export class UserFieldsDialog extends AbstractDialog<BoardUserField[]> {
|
||||
protected readonly widget: UserFieldsDialogWidget;
|
||||
|
||||
constructor(
|
||||
@inject(UserFieldsDialogProps)
|
||||
protected readonly props: UserFieldsDialogProps
|
||||
) {
|
||||
super({
|
||||
title: UploadSketch.Commands.UPLOAD_WITH_CONFIGURATION.label || '',
|
||||
});
|
||||
this.titleNode.classList.add('user-fields-dialog-title');
|
||||
this.contentNode.classList.add('user-fields-dialog-content');
|
||||
this.acceptButton = undefined;
|
||||
this.widget = new UserFieldsDialogWidget(
|
||||
this.close.bind(this),
|
||||
this.accept.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
set value(userFields: BoardUserField[]) {
|
||||
this.widget.currentUserFields = userFields;
|
||||
}
|
||||
|
||||
get value(): BoardUserField[] {
|
||||
return this.widget.currentUserFields;
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
}
|
||||
Widget.attach(this.widget, this.contentNode);
|
||||
super.onAfterAttach(msg);
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message): void {
|
||||
super.onUpdateRequest(msg);
|
||||
this.widget.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
this.widget.activate();
|
||||
}
|
||||
|
||||
protected async accept(): Promise<void> {
|
||||
// If the user presses enter and at least
|
||||
// a field is empty don't accept the input
|
||||
for (const field of this.value) {
|
||||
if (field.value.length === 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return super.accept();
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this.widget.resetUserFieldsValue();
|
||||
this.widget.close();
|
||||
super.close();
|
||||
}
|
||||
}
|
@@ -1,4 +1,7 @@
|
||||
<!--Copyright (c) Microsoft Corporation. All rights reserved.-->
|
||||
<!--Copyright (C) 2019 TypeFox and others.-->
|
||||
<!--Licensed under the MIT License. See License.txt in the project root for license information.-->
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="#F6F6F6" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0)"><path d="M15.1673 18.0687V23.0247C15.1673 23.5637 15.2723 24.5 14.7315 24.5H12.8328V23.3327H14V19.6122L13.7988 19.4022C13.0604 20.0803 12.1008 20.4669 11.0986 20.49C10.0964 20.5132 9.11994 20.1714 8.351 19.5282C7 18.1737 7.13826 16.3327 8.60475 14H4.66726V15.1672H3.50001V13.2685C3.50001 12.7277 4.43626 12.8327 4.97526 12.8327H9.76326L15.1673 18.0687ZM11.6673 5.83275H10.5V4.66725H12.775C13.3123 4.66725 14 4.9245 14 5.4635V9.366L14.8593 10.3862C14.927 9.83979 15.1906 9.33644 15.6013 8.96958C16.0119 8.60271 16.5416 8.39723 17.0923 8.39125C17.2298 8.37945 17.3684 8.39492 17.5 8.43675V5.83275H18.6673V8.88825C18.703 8.99154 18.7618 9.08536 18.8391 9.16265C18.9164 9.23995 19.0102 9.29871 19.1135 9.3345H22.1673V10.5H19.5615C19.593 10.5 19.6105 10.675 19.6105 10.85C19.6058 11.4034 19.4011 11.9365 19.0341 12.3508C18.6671 12.7651 18.1626 13.0326 17.6138 13.104L18.634 14H22.5383C23.0773 14 23.3345 14.6807 23.3345 15.225V17.5H22.1673V16.3327H19.2273L11.6673 8.98275V5.83275ZM14 0C11.2311 0 8.52431 0.821086 6.22202 2.35943C3.91973 3.89776 2.12532 6.08426 1.06569 8.64243C0.00606593 11.2006 -0.271181 14.0155 0.269012 16.7313C0.809205 19.447 2.14258 21.9416 4.10051 23.8995C6.05845 25.8574 8.55301 27.1908 11.2687 27.731C13.9845 28.2712 16.7994 27.9939 19.3576 26.9343C21.9157 25.8747 24.1022 24.0803 25.6406 21.778C27.1789 19.4757 28 16.7689 28 14C28 10.287 26.525 6.72601 23.8995 4.1005C21.274 1.475 17.713 0 14 0V0ZM25.6673 14C25.6692 16.6908 24.7364 19.2988 23.0283 21.378L6.622 4.97175C8.33036 3.57269 10.4009 2.68755 12.5927 2.41935C14.7845 2.15115 17.0074 2.51091 19.0027 3.45676C20.998 4.40262 22.6836 5.89567 23.8635 7.76217C25.0433 9.62867 25.6689 11.7919 25.6673 14ZM2.33276 14C2.33066 11.3091 3.26351 8.70111 4.97176 6.622L21.378 23.03C19.6693 24.4284 17.5987 25.313 15.407 25.5807C13.2153 25.8485 10.9926 25.4884 8.99754 24.5425C7.00244 23.5965 5.31693 22.1036 4.13708 20.2373C2.95722 18.3709 2.33152 16.208 2.33276 14Z" fill="white"/></g><defs><clipPath id="clip0"><rect width="28" height="28" fill="#F6F6F6"/></clipPath></defs></svg>
|
||||
<svg width="23" height="22" viewBox="0 0 23 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.6259 14.2462C13.4329 14.2464 13.2452 14.1827 13.0922 14.0652C12.9391 13.9476 12.8292 13.7827 12.7796 13.5962C12.73 13.4097 12.7434 13.212 12.8178 13.034C12.8922 12.8559 13.0234 12.7074 13.1909 12.6116L19.7355 8.87405L9.25 2.88232V9.25C9.25 9.48206 9.15781 9.70462 8.99372 9.86872C8.82962 10.0328 8.60706 10.125 8.375 10.125C8.14294 10.125 7.92038 10.0328 7.75628 9.86872C7.59219 9.70462 7.5 9.48206 7.5 9.25V1.375C7.49991 1.22171 7.54012 1.07108 7.61658 0.938223C7.69304 0.805362 7.80308 0.694931 7.93567 0.617993C8.06825 0.541055 8.21873 0.500314 8.37202 0.499851C8.52531 0.499389 8.67603 0.539221 8.80908 0.615357L21.9341 8.11438C22.068 8.19089 22.1793 8.30145 22.2568 8.43486C22.3342 8.56826 22.375 8.71977 22.375 8.87402C22.375 9.02827 22.3342 9.17978 22.2568 9.31319C22.1793 9.4466 22.068 9.55716 21.9341 9.63367L14.0591 14.1309C13.9273 14.2066 13.7779 14.2464 13.6259 14.2462Z" fill="#BDC7C7"/>
|
||||
<path d="M11 17.125C11.2321 17.125 11.4546 17.0328 11.6187 16.8687C11.7828 16.7046 11.875 16.4821 11.875 16.25C11.875 16.0179 11.7828 15.7954 11.6187 15.6313C11.4546 15.4672 11.2321 15.375 11 15.375H10.0724C10.0297 15.0014 9.95355 14.6325 9.84495 14.2725L10.7462 13.3712C10.8278 13.2897 10.8925 13.1928 10.9367 13.0862C10.9808 12.9796 11.0035 12.8654 11.0035 12.75C11.0035 12.6346 10.9808 12.5204 10.9367 12.4138C10.8925 12.3072 10.8278 12.2104 10.7462 12.1288C10.6646 12.0472 10.5678 11.9825 10.4612 11.9383C10.3546 11.8942 10.2404 11.8714 10.125 11.8714C10.0096 11.8714 9.89537 11.8942 9.78878 11.9383C9.68219 11.9825 9.58533 12.0472 9.50375 12.1288L9.04 12.5925C8.72733 12.1175 8.30488 11.7248 7.80837 11.4477C7.31186 11.1705 6.75589 11.0169 6.1875 11C5.6191 11.0169 5.06314 11.1705 4.56663 11.4477C4.07011 11.7249 3.64766 12.1176 3.335 12.5926L2.87125 12.1288C2.70649 11.964 2.48303 11.8715 2.25002 11.8715C2.01701 11.8715 1.79355 11.964 1.62878 12.1288C1.46401 12.2935 1.37145 12.517 1.37144 12.75C1.37144 12.983 1.46399 13.2065 1.62875 13.3712L2.53 14.2725C2.4214 14.6325 2.34526 15.0014 2.3025 15.375H1.375C1.14294 15.375 0.920376 15.4672 0.756282 15.6313C0.592187 15.7954 0.5 16.0179 0.5 16.25C0.5 16.4821 0.592187 16.7046 0.756282 16.8687C0.920376 17.0328 1.14294 17.125 1.375 17.125H2.30255C2.34532 17.4986 2.42145 17.8675 2.53005 18.2275L2.5038 18.2538L1.6288 19.1288C1.46485 19.2939 1.37285 19.5172 1.37285 19.75C1.37285 19.9827 1.46485 20.206 1.6288 20.3712C1.79466 20.5338 2.01771 20.625 2.25002 20.625C2.48233 20.625 2.70537 20.5338 2.87123 20.3712L3.33498 19.9074C3.64765 20.3824 4.0701 20.7751 4.56661 21.0523C5.06313 21.3295 5.6191 21.4831 6.1875 21.5C6.7559 21.4831 7.31186 21.3295 7.80837 21.0523C8.30489 20.7751 8.72734 20.3824 9.04 19.9074L9.50375 20.3712C9.66961 20.5339 9.89265 20.625 10.125 20.625C10.3573 20.625 10.5803 20.5339 10.7462 20.3712C10.9101 20.206 11.0021 19.9827 11.0021 19.75C11.0021 19.5172 10.9101 19.2939 10.7462 19.1288L9.87118 18.2538L9.84493 18.2275C9.95353 17.8675 10.0297 17.4986 10.0724 17.125L11 17.125ZM6.1875 12.75C6.98367 12.75 7.68375 13.4588 8.06875 14.5H4.30625C4.69125 13.4588 5.39133 12.75 6.1875 12.75ZM6.1875 19.75C4.9975 19.75 4 18.1487 4 16.25H8.375C8.375 18.1487 7.3775 19.75 6.1875 19.75Z" fill="#BDC7C7"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 3.4 KiB |
@@ -1,7 +1,8 @@
|
||||
import { injectable, postConstruct, inject } from 'inversify';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import { addEventListener } from '@theia/core/lib/browser/widgets/widget';
|
||||
import { AbstractDialog, DialogProps } from '@theia/core/lib/browser/dialogs';
|
||||
import { DialogProps } from '@theia/core/lib/browser/dialogs';
|
||||
import { AbstractDialog } from '../theia/dialogs/dialogs';
|
||||
import {
|
||||
LibraryPackage,
|
||||
LibraryService,
|
||||
@@ -9,11 +10,15 @@ import {
|
||||
import { ListWidget } from '../widgets/component-list/list-widget';
|
||||
import { Installable } from '../../common/protocol';
|
||||
import { ListItemRenderer } from '../widgets/component-list/list-item-renderer';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class LibraryListWidget extends ListWidget<LibraryPackage> {
|
||||
static WIDGET_ID = 'library-list-widget';
|
||||
static WIDGET_LABEL = 'Library Manager';
|
||||
static WIDGET_LABEL = nls.localize(
|
||||
'arduino/library/title',
|
||||
'Library Manager'
|
||||
);
|
||||
|
||||
constructor(
|
||||
@inject(LibraryService) protected service: LibraryService,
|
||||
@@ -23,7 +28,7 @@ export class LibraryListWidget extends ListWidget<LibraryPackage> {
|
||||
super({
|
||||
id: LibraryListWidget.WIDGET_ID,
|
||||
label: LibraryListWidget.WIDGET_LABEL,
|
||||
iconClass: 'library-tab-icon',
|
||||
iconClass: 'fa fa-arduino-library',
|
||||
searchable: service,
|
||||
installable: service,
|
||||
itemLabel: (item: LibraryPackage) => item.name,
|
||||
@@ -60,11 +65,20 @@ export class LibraryListWidget extends ListWidget<LibraryPackage> {
|
||||
let installDependencies: boolean | undefined = undefined;
|
||||
if (dependencies.length) {
|
||||
const message = document.createElement('div');
|
||||
message.innerHTML = `The library <b>${item.name}:${version}</b> needs ${
|
||||
message.innerHTML =
|
||||
dependencies.length === 1
|
||||
? 'another dependency'
|
||||
: 'some other dependencies'
|
||||
} currently not installed:`;
|
||||
? nls.localize(
|
||||
'arduino/library/needsOneDependency',
|
||||
'The library <b>{0}:{1}</b> needs another dependency currently not installed:',
|
||||
item.name,
|
||||
version
|
||||
)
|
||||
: nls.localize(
|
||||
'arduino/library/needsMultipleDependencies',
|
||||
'The library <b>{0}:{1}</b> needs some other dependencies currently not installed:',
|
||||
item.name,
|
||||
version
|
||||
);
|
||||
const listContainer = document.createElement('div');
|
||||
listContainer.style.maxHeight = '300px';
|
||||
listContainer.style.overflowY = 'auto';
|
||||
@@ -79,16 +93,34 @@ export class LibraryListWidget extends ListWidget<LibraryPackage> {
|
||||
listContainer.appendChild(list);
|
||||
message.appendChild(listContainer);
|
||||
const question = document.createElement('div');
|
||||
question.textContent = `Would you like to install ${
|
||||
question.textContent =
|
||||
dependencies.length === 1
|
||||
? 'the missing dependency'
|
||||
: 'all the missing dependencies'
|
||||
}?`;
|
||||
? nls.localize(
|
||||
'arduino/library/installOneMissingDependency',
|
||||
'Would you like to install the missing dependency?'
|
||||
)
|
||||
: nls.localize(
|
||||
'arduino/library/installMissingDependencies',
|
||||
'Would you like to install all the missing dependencies?'
|
||||
);
|
||||
message.appendChild(question);
|
||||
const result = await new MessageBoxDialog({
|
||||
title: `Dependencies for library ${item.name}:${version}`,
|
||||
title: nls.localize(
|
||||
'arduino/library/dependenciesForLibrary',
|
||||
'Dependencies for library {0}:{1}',
|
||||
item.name,
|
||||
version
|
||||
),
|
||||
message,
|
||||
buttons: ['Install all', `Install ${item.name} only`, 'Cancel'],
|
||||
buttons: [
|
||||
nls.localize('arduino/library/installAll', 'Install all'),
|
||||
nls.localize(
|
||||
'arduino/library/installOnly',
|
||||
'Install {0} only',
|
||||
item.name
|
||||
),
|
||||
nls.localize('vscode/issueMainService/cancel', 'Cancel'),
|
||||
],
|
||||
maxWidth: 740, // Aligned with `settings-dialog.css`.
|
||||
}).open();
|
||||
|
||||
@@ -115,7 +147,12 @@ export class LibraryListWidget extends ListWidget<LibraryPackage> {
|
||||
installDependencies,
|
||||
});
|
||||
this.messageService.info(
|
||||
`Successfully installed library ${item.name}:${version}`,
|
||||
nls.localize(
|
||||
'arduino/library/installedSuccessfully',
|
||||
'Successfully installed library {0}:{1}',
|
||||
item.name,
|
||||
version
|
||||
),
|
||||
{ timeout: 3000 }
|
||||
);
|
||||
}
|
||||
@@ -130,7 +167,12 @@ export class LibraryListWidget extends ListWidget<LibraryPackage> {
|
||||
}): Promise<void> {
|
||||
await super.uninstall({ item, progressId });
|
||||
this.messageService.info(
|
||||
`Successfully uninstalled library ${item.name}:${item.installedVersion}`,
|
||||
nls.localize(
|
||||
'arduino/library/uninstalledSuccessfully',
|
||||
'Successfully uninstalled library {0}:{1}',
|
||||
item.name,
|
||||
item.installedVersion!
|
||||
),
|
||||
{ timeout: 3000 }
|
||||
);
|
||||
}
|
||||
@@ -142,7 +184,9 @@ class MessageBoxDialog extends AbstractDialog<MessageBoxDialog.Result> {
|
||||
constructor(protected readonly options: MessageBoxDialog.Options) {
|
||||
super(options);
|
||||
this.contentNode.appendChild(this.createMessageNode(this.options.message));
|
||||
(options.buttons || ['OK']).forEach((text, index) => {
|
||||
(
|
||||
options.buttons || [nls.localize('vscode/issueMainService/ok', 'OK')]
|
||||
).forEach((text, index) => {
|
||||
const button = this.createButton(text);
|
||||
button.classList.add(index === 0 ? 'main' : 'secondary');
|
||||
this.controlPanel.appendChild(button);
|
||||
|
@@ -4,6 +4,7 @@ import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-con
|
||||
import { MenuModelRegistry } from '@theia/core';
|
||||
import { LibraryListWidget } from './library-list-widget';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class LibraryListWidgetFrontendContribution
|
||||
@@ -31,7 +32,10 @@ export class LibraryListWidgetFrontendContribution
|
||||
if (this.toggleCommand) {
|
||||
menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, {
|
||||
commandId: this.toggleCommand.id,
|
||||
label: 'Manage Libraries...',
|
||||
label: nls.localize(
|
||||
'arduino/library/manageLibraries',
|
||||
'Manage Libraries...'
|
||||
),
|
||||
order: '3',
|
||||
});
|
||||
}
|
||||
|
@@ -86,8 +86,13 @@ export namespace ArduinoMenus {
|
||||
|
||||
// -- Tools
|
||||
export const TOOLS = [...MAIN_MENU_BAR, '4_tools'];
|
||||
// `Auto Format`, `Library Manager...`, `Boards Manager...`
|
||||
// `Auto Format`, `Archive Sketch`, `Manage Libraries...`, `Serial Monitor`, Serial Plotter
|
||||
export const TOOLS__MAIN_GROUP = [...TOOLS, '0_main'];
|
||||
// `WiFi101 / WiFiNINA Firmware Updater`
|
||||
export const TOOLS__FIRMWARE_UPLOADER_GROUP = [
|
||||
...TOOLS,
|
||||
'1_firmware_uploader',
|
||||
];
|
||||
// `Board`, `Port`, and `Get Board Info`.
|
||||
export const TOOLS__BOARD_SELECTION_GROUP = [...TOOLS, '2_board_selection'];
|
||||
// Core settings, such as `Processor` and `Programmers` for the board and `Burn Bootloader`
|
||||
@@ -143,6 +148,11 @@ export namespace ArduinoMenus {
|
||||
...SKETCH_CONTROL__CONTEXT,
|
||||
'2_resources',
|
||||
];
|
||||
|
||||
// -- ROOT SSL CERTIFICATES
|
||||
export const ROOT_CERTIFICATES__CONTEXT = [
|
||||
'arduino-root-certificates--context',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,354 +0,0 @@
|
||||
import { injectable, inject, postConstruct } from 'inversify';
|
||||
import { deepClone } from '@theia/core/lib/common/objects';
|
||||
import { Emitter, Event } from '@theia/core/lib/common/event';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import {
|
||||
MonitorService,
|
||||
MonitorConfig,
|
||||
MonitorError,
|
||||
Status,
|
||||
} from '../../common/protocol/monitor-service';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import {
|
||||
Port,
|
||||
Board,
|
||||
BoardsService,
|
||||
AttachedBoardsChangeEvent,
|
||||
} from '../../common/protocol/boards-service';
|
||||
import { MonitorServiceClientImpl } from './monitor-service-client-impl';
|
||||
import { BoardsConfig } from '../boards/boards-config';
|
||||
import { MonitorModel } from './monitor-model';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
|
||||
@injectable()
|
||||
export class MonitorConnection {
|
||||
@inject(MonitorModel)
|
||||
protected readonly monitorModel: MonitorModel;
|
||||
|
||||
@inject(MonitorService)
|
||||
protected readonly monitorService: MonitorService;
|
||||
|
||||
@inject(MonitorServiceClientImpl)
|
||||
protected readonly monitorServiceClient: MonitorServiceClientImpl;
|
||||
|
||||
@inject(BoardsService)
|
||||
protected readonly boardsService: BoardsService;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
|
||||
@inject(NotificationCenter)
|
||||
protected readonly notificationCenter: NotificationCenter;
|
||||
|
||||
@inject(MessageService)
|
||||
protected messageService: MessageService;
|
||||
|
||||
@inject(FrontendApplicationStateService)
|
||||
protected readonly applicationState: FrontendApplicationStateService;
|
||||
|
||||
protected state: MonitorConnection.State | undefined;
|
||||
/**
|
||||
* Note: The idea is to toggle this property from the UI (`Monitor` view)
|
||||
* and the boards config and the boards attachment/detachment logic can be at on place, here.
|
||||
*/
|
||||
protected _autoConnect = false;
|
||||
protected readonly onConnectionChangedEmitter = new Emitter<
|
||||
MonitorConnection.State | undefined
|
||||
>();
|
||||
/**
|
||||
* This emitter forwards all read events **iff** the connection is established.
|
||||
*/
|
||||
protected readonly onReadEmitter = new Emitter<{ message: string }>();
|
||||
|
||||
/**
|
||||
* Array for storing previous monitor errors received from the server, and based on the number of elements in this array,
|
||||
* we adjust the reconnection delay.
|
||||
* Super naive way: we wait `array.length * 1000` ms. Once we hit 10 errors, we do not try to reconnect and clean the array.
|
||||
*/
|
||||
protected monitorErrors: MonitorError[] = [];
|
||||
protected reconnectTimeout?: number;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.monitorServiceClient.onError(async (error) => {
|
||||
let shouldReconnect = false;
|
||||
if (this.state) {
|
||||
const { code, config } = error;
|
||||
const { board, port } = config;
|
||||
const options = { timeout: 3000 };
|
||||
switch (code) {
|
||||
case MonitorError.ErrorCodes.CLIENT_CANCEL: {
|
||||
console.debug(
|
||||
`Connection was canceled by client: ${MonitorConnection.State.toString(
|
||||
this.state
|
||||
)}.`
|
||||
);
|
||||
break;
|
||||
}
|
||||
case MonitorError.ErrorCodes.DEVICE_BUSY: {
|
||||
this.messageService.warn(
|
||||
`Connection failed. Serial port is busy: ${Port.toString(port)}.`,
|
||||
options
|
||||
);
|
||||
shouldReconnect = this.autoConnect;
|
||||
this.monitorErrors.push(error);
|
||||
break;
|
||||
}
|
||||
case MonitorError.ErrorCodes.DEVICE_NOT_CONFIGURED: {
|
||||
this.messageService.info(
|
||||
`Disconnected ${Board.toString(board, {
|
||||
useFqbn: false,
|
||||
})} from ${Port.toString(port)}.`,
|
||||
options
|
||||
);
|
||||
break;
|
||||
}
|
||||
case undefined: {
|
||||
this.messageService.error(
|
||||
`Unexpected error. Reconnecting ${Board.toString(
|
||||
board
|
||||
)} on port ${Port.toString(port)}.`,
|
||||
options
|
||||
);
|
||||
console.error(JSON.stringify(error));
|
||||
shouldReconnect = this.connected && this.autoConnect;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const oldState = this.state;
|
||||
this.state = undefined;
|
||||
this.onConnectionChangedEmitter.fire(this.state);
|
||||
if (shouldReconnect) {
|
||||
if (this.monitorErrors.length >= 10) {
|
||||
this.messageService.warn(
|
||||
`Failed to reconnect ${Board.toString(board, {
|
||||
useFqbn: false,
|
||||
})} to the the serial-monitor after 10 consecutive attempts. The ${Port.toString(
|
||||
port
|
||||
)} serial port is busy. after 10 consecutive attempts.`
|
||||
);
|
||||
this.monitorErrors.length = 0;
|
||||
} else {
|
||||
const attempts = this.monitorErrors.length || 1;
|
||||
if (this.reconnectTimeout !== undefined) {
|
||||
// Clear the previous timer.
|
||||
window.clearTimeout(this.reconnectTimeout);
|
||||
}
|
||||
const timeout = attempts * 1000;
|
||||
this.messageService.warn(
|
||||
`Reconnecting ${Board.toString(board, {
|
||||
useFqbn: false,
|
||||
})} to ${Port.toString(port)} in ${attempts} seconds...`,
|
||||
{ timeout }
|
||||
);
|
||||
this.reconnectTimeout = window.setTimeout(
|
||||
() => this.connect(oldState.config),
|
||||
timeout
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
this.boardsServiceProvider.onBoardsConfigChanged(
|
||||
this.handleBoardConfigChange.bind(this)
|
||||
);
|
||||
this.notificationCenter.onAttachedBoardsChanged((event) => {
|
||||
if (this.autoConnect && this.connected) {
|
||||
const { boardsConfig } = this.boardsServiceProvider;
|
||||
if (
|
||||
this.boardsServiceProvider.canUploadTo(boardsConfig, {
|
||||
silent: false,
|
||||
})
|
||||
) {
|
||||
const { attached } = AttachedBoardsChangeEvent.diff(event);
|
||||
if (
|
||||
attached.boards.some(
|
||||
(board) =>
|
||||
!!board.port && BoardsConfig.Config.sameAs(boardsConfig, board)
|
||||
)
|
||||
) {
|
||||
const { selectedBoard: board, selectedPort: port } = boardsConfig;
|
||||
const { baudRate } = this.monitorModel;
|
||||
this.disconnect().then(() =>
|
||||
this.connect({ board, port, baudRate })
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// Handles the `baudRate` changes by reconnecting if required.
|
||||
this.monitorModel.onChange(({ property }) => {
|
||||
if (property === 'baudRate' && this.autoConnect && this.connected) {
|
||||
const { boardsConfig } = this.boardsServiceProvider;
|
||||
this.handleBoardConfigChange(boardsConfig);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get connected(): boolean {
|
||||
return !!this.state;
|
||||
}
|
||||
|
||||
get monitorConfig(): MonitorConfig | undefined {
|
||||
return this.state ? this.state.config : undefined;
|
||||
}
|
||||
|
||||
get autoConnect(): boolean {
|
||||
return this._autoConnect;
|
||||
}
|
||||
|
||||
set autoConnect(value: boolean) {
|
||||
const oldValue = this._autoConnect;
|
||||
this._autoConnect = value;
|
||||
// When we enable the auto-connect, we have to connect
|
||||
if (!oldValue && value) {
|
||||
// We have to make sure the previous boards config has been restored.
|
||||
// Otherwise, we might start the auto-connection without configured boards.
|
||||
this.applicationState.reachedState('started_contributions').then(() => {
|
||||
const { boardsConfig } = this.boardsServiceProvider;
|
||||
this.handleBoardConfigChange(boardsConfig);
|
||||
});
|
||||
} else if (oldValue && !value) {
|
||||
if (this.reconnectTimeout !== undefined) {
|
||||
window.clearTimeout(this.reconnectTimeout);
|
||||
this.monitorErrors.length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async connect(config: MonitorConfig): Promise<Status> {
|
||||
if (this.connected) {
|
||||
const disconnectStatus = await this.disconnect();
|
||||
if (!Status.isOK(disconnectStatus)) {
|
||||
return disconnectStatus;
|
||||
}
|
||||
}
|
||||
console.info(
|
||||
`>>> Creating serial monitor connection for ${Board.toString(
|
||||
config.board
|
||||
)} on port ${Port.toString(config.port)}...`
|
||||
);
|
||||
const connectStatus = await this.monitorService.connect(config);
|
||||
if (Status.isOK(connectStatus)) {
|
||||
const requestMessage = () => {
|
||||
this.monitorService.request().then(({ message }) => {
|
||||
if (this.connected) {
|
||||
this.onReadEmitter.fire({ message });
|
||||
requestMessage();
|
||||
}
|
||||
});
|
||||
};
|
||||
requestMessage();
|
||||
this.state = { config };
|
||||
console.info(
|
||||
`<<< Serial monitor connection created for ${Board.toString(
|
||||
config.board,
|
||||
{ useFqbn: false }
|
||||
)} on port ${Port.toString(config.port)}.`
|
||||
);
|
||||
}
|
||||
this.onConnectionChangedEmitter.fire(this.state);
|
||||
return Status.isOK(connectStatus);
|
||||
}
|
||||
|
||||
async disconnect(): Promise<Status> {
|
||||
if (!this.connected) {
|
||||
return Status.OK;
|
||||
}
|
||||
const stateCopy = deepClone(this.state);
|
||||
if (!stateCopy) {
|
||||
return Status.OK;
|
||||
}
|
||||
console.log('>>> Disposing existing monitor connection...');
|
||||
const status = await this.monitorService.disconnect();
|
||||
if (Status.isOK(status)) {
|
||||
console.log(
|
||||
`<<< Disposed connection. Was: ${MonitorConnection.State.toString(
|
||||
stateCopy
|
||||
)}`
|
||||
);
|
||||
} else {
|
||||
console.warn(
|
||||
`<<< Could not dispose connection. Activate connection: ${MonitorConnection.State.toString(
|
||||
stateCopy
|
||||
)}`
|
||||
);
|
||||
}
|
||||
this.state = undefined;
|
||||
this.onConnectionChangedEmitter.fire(this.state);
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the data to the connected serial monitor.
|
||||
* The desired EOL is appended to `data`, you do not have to add it.
|
||||
* It is a NOOP if connected.
|
||||
*/
|
||||
async send(data: string): Promise<Status> {
|
||||
if (!this.connected) {
|
||||
return Status.NOT_CONNECTED;
|
||||
}
|
||||
return new Promise<Status>((resolve) => {
|
||||
this.monitorService
|
||||
.send(data + this.monitorModel.lineEnding)
|
||||
.then(() => resolve(Status.OK));
|
||||
});
|
||||
}
|
||||
|
||||
get onConnectionChanged(): Event<MonitorConnection.State | undefined> {
|
||||
return this.onConnectionChangedEmitter.event;
|
||||
}
|
||||
|
||||
get onRead(): Event<{ message: string }> {
|
||||
return this.onReadEmitter.event;
|
||||
}
|
||||
|
||||
protected async handleBoardConfigChange(
|
||||
boardsConfig: BoardsConfig.Config
|
||||
): Promise<void> {
|
||||
if (this.autoConnect) {
|
||||
if (
|
||||
this.boardsServiceProvider.canUploadTo(boardsConfig, {
|
||||
silent: false,
|
||||
})
|
||||
) {
|
||||
// Instead of calling `getAttachedBoards` and filtering for `AttachedSerialBoard` we have to check the available ports.
|
||||
// The connected board might be unknown. See: https://github.com/arduino/arduino-pro-ide/issues/127#issuecomment-563251881
|
||||
this.boardsService.getAvailablePorts().then((ports) => {
|
||||
if (
|
||||
ports.some((port) => Port.equals(port, boardsConfig.selectedPort))
|
||||
) {
|
||||
new Promise<void>((resolve) => {
|
||||
// First, disconnect if connected.
|
||||
if (this.connected) {
|
||||
this.disconnect().then(() => resolve());
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
}).then(() => {
|
||||
// Then (re-)connect.
|
||||
const { selectedBoard: board, selectedPort: port } = boardsConfig;
|
||||
const { baudRate } = this.monitorModel;
|
||||
this.connect({ board, port, baudRate });
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace MonitorConnection {
|
||||
export interface State {
|
||||
readonly config: MonitorConfig;
|
||||
}
|
||||
|
||||
export namespace State {
|
||||
export function toString(state: State): string {
|
||||
const { config } = state;
|
||||
const { board, port } = config;
|
||||
return `${Board.toString(board)} ${Port.toString(port)}`;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import {
|
||||
MonitorServiceClient,
|
||||
MonitorError,
|
||||
} from '../../common/protocol/monitor-service';
|
||||
|
||||
@injectable()
|
||||
export class MonitorServiceClientImpl implements MonitorServiceClient {
|
||||
protected readonly onErrorEmitter = new Emitter<MonitorError>();
|
||||
readonly onError = this.onErrorEmitter.event;
|
||||
|
||||
notifyError(error: MonitorError): void {
|
||||
this.onErrorEmitter.fire(error);
|
||||
}
|
||||
}
|
@@ -1,394 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import * as dateFormat from 'dateformat';
|
||||
import { postConstruct, injectable, inject } from 'inversify';
|
||||
import { OptionsType } from 'react-select/src/types';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import { Event, Emitter } from '@theia/core/lib/common/event';
|
||||
import { Key, KeyCode } from '@theia/core/lib/browser/keys';
|
||||
import {
|
||||
DisposableCollection,
|
||||
Disposable,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import {
|
||||
ReactWidget,
|
||||
Message,
|
||||
Widget,
|
||||
MessageLoop,
|
||||
} from '@theia/core/lib/browser/widgets';
|
||||
import { Board, Port } from '../../common/protocol/boards-service';
|
||||
import { MonitorConfig } from '../../common/protocol/monitor-service';
|
||||
import { ArduinoSelect } from '../widgets/arduino-select';
|
||||
import { MonitorModel } from './monitor-model';
|
||||
import { MonitorConnection } from './monitor-connection';
|
||||
import { MonitorServiceClientImpl } from './monitor-service-client-impl';
|
||||
|
||||
@injectable()
|
||||
export class MonitorWidget extends ReactWidget {
|
||||
static readonly ID = 'serial-monitor';
|
||||
|
||||
@inject(MonitorModel)
|
||||
protected readonly monitorModel: MonitorModel;
|
||||
|
||||
@inject(MonitorConnection)
|
||||
protected readonly monitorConnection: MonitorConnection;
|
||||
|
||||
@inject(MonitorServiceClientImpl)
|
||||
protected readonly monitorServiceClient: MonitorServiceClientImpl;
|
||||
|
||||
protected widgetHeight: number;
|
||||
|
||||
/**
|
||||
* Do not touch or use it. It is for setting the focus on the `input` after the widget activation.
|
||||
*/
|
||||
protected focusNode: HTMLElement | undefined;
|
||||
/**
|
||||
* Guard against re-rendering the view after the close was requested.
|
||||
* See: https://github.com/eclipse-theia/theia/issues/6704
|
||||
*/
|
||||
protected closing = false;
|
||||
protected readonly clearOutputEmitter = new Emitter<void>();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.id = MonitorWidget.ID;
|
||||
this.title.label = 'Serial Monitor';
|
||||
this.title.iconClass = 'monitor-tab-icon';
|
||||
this.title.closable = true;
|
||||
this.scrollOptions = undefined;
|
||||
this.toDispose.push(this.clearOutputEmitter);
|
||||
this.toDispose.push(
|
||||
Disposable.create(() => {
|
||||
this.monitorConnection.autoConnect = false;
|
||||
if (this.monitorConnection.connected) {
|
||||
this.monitorConnection.disconnect();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.update();
|
||||
this.toDispose.push(
|
||||
this.monitorConnection.onConnectionChanged(() => this.clearConsole())
|
||||
);
|
||||
}
|
||||
|
||||
clearConsole(): void {
|
||||
this.clearOutputEmitter.fire(undefined);
|
||||
this.update();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
super.onAfterAttach(msg);
|
||||
this.monitorConnection.autoConnect = true;
|
||||
}
|
||||
|
||||
onCloseRequest(msg: Message): void {
|
||||
this.closing = true;
|
||||
super.onCloseRequest(msg);
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message): void {
|
||||
// TODO: `this.isAttached`
|
||||
// See: https://github.com/eclipse-theia/theia/issues/6704#issuecomment-562574713
|
||||
if (!this.closing && this.isAttached) {
|
||||
super.onUpdateRequest(msg);
|
||||
}
|
||||
}
|
||||
|
||||
protected onResize(msg: Widget.ResizeMessage): void {
|
||||
super.onResize(msg);
|
||||
this.widgetHeight = msg.height;
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
(this.focusNode || this.node).focus();
|
||||
}
|
||||
|
||||
protected onFocusResolved = (element: HTMLElement | undefined) => {
|
||||
if (this.closing || !this.isAttached) {
|
||||
return;
|
||||
}
|
||||
this.focusNode = element;
|
||||
requestAnimationFrame(() =>
|
||||
MessageLoop.sendMessage(this, Widget.Msg.ActivateRequest)
|
||||
);
|
||||
};
|
||||
|
||||
protected get lineEndings(): OptionsType<SelectOption<MonitorModel.EOL>> {
|
||||
return [
|
||||
{
|
||||
label: 'No Line Ending',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
label: 'New Line',
|
||||
value: '\n',
|
||||
},
|
||||
{
|
||||
label: 'Carriage Return',
|
||||
value: '\r',
|
||||
},
|
||||
{
|
||||
label: 'Both NL & CR',
|
||||
value: '\r\n',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
protected get baudRates(): OptionsType<SelectOption<MonitorConfig.BaudRate>> {
|
||||
const baudRates: Array<MonitorConfig.BaudRate> = [
|
||||
300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200,
|
||||
];
|
||||
return baudRates.map((baudRate) => ({
|
||||
label: baudRate + ' baud',
|
||||
value: baudRate,
|
||||
}));
|
||||
}
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
const { baudRates, lineEndings } = this;
|
||||
const lineEnding =
|
||||
lineEndings.find((item) => item.value === this.monitorModel.lineEnding) ||
|
||||
lineEndings[1]; // Defaults to `\n`.
|
||||
const baudRate =
|
||||
baudRates.find((item) => item.value === this.monitorModel.baudRate) ||
|
||||
baudRates[4]; // Defaults to `9600`.
|
||||
return (
|
||||
<div className="serial-monitor">
|
||||
<div className="head">
|
||||
<div className="send">
|
||||
<SerialMonitorSendInput
|
||||
monitorConfig={this.monitorConnection.monitorConfig}
|
||||
resolveFocus={this.onFocusResolved}
|
||||
onSend={this.onSend}
|
||||
/>
|
||||
</div>
|
||||
<div className="config">
|
||||
<div className="select">
|
||||
<ArduinoSelect
|
||||
maxMenuHeight={this.widgetHeight - 40}
|
||||
options={lineEndings}
|
||||
defaultValue={lineEnding}
|
||||
onChange={this.onChangeLineEnding}
|
||||
/>
|
||||
</div>
|
||||
<div className="select">
|
||||
<ArduinoSelect
|
||||
className="select"
|
||||
maxMenuHeight={this.widgetHeight - 40}
|
||||
options={baudRates}
|
||||
defaultValue={baudRate}
|
||||
onChange={this.onChangeBaudRate}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="body">
|
||||
<SerialMonitorOutput
|
||||
monitorModel={this.monitorModel}
|
||||
monitorConnection={this.monitorConnection}
|
||||
clearConsoleEvent={this.clearOutputEmitter.event}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
protected readonly onSend = (value: string) => this.doSend(value);
|
||||
protected async doSend(value: string): Promise<void> {
|
||||
this.monitorConnection.send(value);
|
||||
}
|
||||
|
||||
protected readonly onChangeLineEnding = (
|
||||
option: SelectOption<MonitorModel.EOL>
|
||||
) => {
|
||||
this.monitorModel.lineEnding = option.value;
|
||||
};
|
||||
|
||||
protected readonly onChangeBaudRate = (
|
||||
option: SelectOption<MonitorConfig.BaudRate>
|
||||
) => {
|
||||
this.monitorModel.baudRate = option.value;
|
||||
};
|
||||
}
|
||||
|
||||
export namespace SerialMonitorSendInput {
|
||||
export interface Props {
|
||||
readonly monitorConfig?: MonitorConfig;
|
||||
readonly onSend: (text: string) => void;
|
||||
readonly resolveFocus: (element: HTMLElement | undefined) => void;
|
||||
}
|
||||
export interface State {
|
||||
text: string;
|
||||
}
|
||||
}
|
||||
|
||||
export class SerialMonitorSendInput extends React.Component<
|
||||
SerialMonitorSendInput.Props,
|
||||
SerialMonitorSendInput.State
|
||||
> {
|
||||
constructor(props: Readonly<SerialMonitorSendInput.Props>) {
|
||||
super(props);
|
||||
this.state = { text: '' };
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onSend = this.onSend.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return (
|
||||
<input
|
||||
ref={this.setRef}
|
||||
type="text"
|
||||
className={`theia-input ${this.props.monitorConfig ? '' : 'warning'}`}
|
||||
placeholder={this.placeholder}
|
||||
value={this.state.text}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
protected get placeholder(): string {
|
||||
const { monitorConfig } = this.props;
|
||||
if (!monitorConfig) {
|
||||
return 'Not connected. Select a board and a port to connect automatically.';
|
||||
}
|
||||
const { board, port } = monitorConfig;
|
||||
return `Message (${
|
||||
isOSX ? '⌘' : 'Ctrl'
|
||||
}+Enter to send message to '${Board.toString(board, {
|
||||
useFqbn: false,
|
||||
})}' on '${Port.toString(port)}')`;
|
||||
}
|
||||
|
||||
protected setRef = (element: HTMLElement | null) => {
|
||||
if (this.props.resolveFocus) {
|
||||
this.props.resolveFocus(element || undefined);
|
||||
}
|
||||
};
|
||||
|
||||
protected onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
this.setState({ text: event.target.value });
|
||||
}
|
||||
|
||||
protected onSend(): void {
|
||||
this.props.onSend(this.state.text);
|
||||
this.setState({ text: '' });
|
||||
}
|
||||
|
||||
protected onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||
const keyCode = KeyCode.createKeyCode(event.nativeEvent);
|
||||
if (keyCode) {
|
||||
const { key, meta, ctrl } = keyCode;
|
||||
if (key === Key.ENTER && ((isOSX && meta) || (!isOSX && ctrl))) {
|
||||
this.onSend();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace SerialMonitorOutput {
|
||||
export interface Props {
|
||||
readonly monitorModel: MonitorModel;
|
||||
readonly monitorConnection: MonitorConnection;
|
||||
readonly clearConsoleEvent: Event<void>;
|
||||
}
|
||||
export interface State {
|
||||
content: string;
|
||||
timestamp: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export class SerialMonitorOutput extends React.Component<
|
||||
SerialMonitorOutput.Props,
|
||||
SerialMonitorOutput.State
|
||||
> {
|
||||
/**
|
||||
* Do not touch it. It is used to be able to "follow" the serial monitor log.
|
||||
*/
|
||||
protected anchor: HTMLElement | null;
|
||||
protected toDisposeBeforeUnmount = new DisposableCollection();
|
||||
|
||||
constructor(props: Readonly<SerialMonitorOutput.Props>) {
|
||||
super(props);
|
||||
this.state = {
|
||||
content: '',
|
||||
timestamp: this.props.monitorModel.timestamp,
|
||||
};
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div style={{ whiteSpace: 'pre', fontFamily: 'monospace' }}>
|
||||
{this.state.content}
|
||||
</div>
|
||||
<div
|
||||
style={{ float: 'left', clear: 'both' }}
|
||||
ref={(element) => {
|
||||
this.anchor = element;
|
||||
}}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.scrollToBottom();
|
||||
this.toDisposeBeforeUnmount.pushAll([
|
||||
this.props.monitorConnection.onRead(({ message }) => {
|
||||
const rawLines = message.split('\n');
|
||||
const lines: string[] = [];
|
||||
const timestamp = () =>
|
||||
this.state.timestamp
|
||||
? `${dateFormat(new Date(), 'H:M:ss.l')} -> `
|
||||
: '';
|
||||
for (let i = 0; i < rawLines.length; i++) {
|
||||
if (i === 0 && this.state.content.length !== 0) {
|
||||
lines.push(rawLines[i]);
|
||||
} else {
|
||||
lines.push(timestamp() + rawLines[i]);
|
||||
}
|
||||
}
|
||||
const content = this.state.content + lines.join('\n');
|
||||
this.setState({ content });
|
||||
}),
|
||||
this.props.clearConsoleEvent(() => this.setState({ content: '' })),
|
||||
this.props.monitorModel.onChange(({ property }) => {
|
||||
if (property === 'timestamp') {
|
||||
const { timestamp } = this.props.monitorModel;
|
||||
this.setState({ timestamp });
|
||||
}
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
// TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout?
|
||||
this.toDisposeBeforeUnmount.dispose();
|
||||
}
|
||||
|
||||
protected scrollToBottom(): void {
|
||||
if (this.props.monitorModel.autoscroll && this.anchor) {
|
||||
this.anchor.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface SelectOption<T> {
|
||||
readonly label: string;
|
||||
readonly value: T;
|
||||
}
|
@@ -1,15 +1,15 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { OutputContribution } from '@theia/output/lib/browser/output-contribution';
|
||||
import { OutputChannelManager } from '@theia/output/lib/common/output-channel';
|
||||
import { OutputChannelManager } from '@theia/output/lib/browser/output-channel';
|
||||
import {
|
||||
ResponseService,
|
||||
OutputMessage,
|
||||
ProgressMessage,
|
||||
ResponseServiceArduino,
|
||||
} from '../common/protocol/response-service';
|
||||
|
||||
@injectable()
|
||||
export class ResponseServiceImpl implements ResponseService {
|
||||
export class ResponseServiceImpl implements ResponseServiceArduino {
|
||||
@inject(OutputContribution)
|
||||
protected outputContribution: OutputContribution;
|
||||
|
||||
@@ -17,8 +17,13 @@ export class ResponseServiceImpl implements ResponseService {
|
||||
protected outputChannelManager: OutputChannelManager;
|
||||
|
||||
protected readonly progressDidChangeEmitter = new Emitter<ProgressMessage>();
|
||||
|
||||
readonly onProgressDidChange = this.progressDidChangeEmitter.event;
|
||||
|
||||
clearArduinoChannel(): void {
|
||||
this.outputChannelManager.getChannel('Arduino').clear();
|
||||
}
|
||||
|
||||
appendToOutput(message: OutputMessage): void {
|
||||
const { chunk } = message;
|
||||
const channel = this.outputChannelManager.getChannel('Arduino');
|
||||
@@ -26,10 +31,6 @@ export class ResponseServiceImpl implements ResponseService {
|
||||
channel.append(chunk);
|
||||
}
|
||||
|
||||
clearArduinoChannel(): void {
|
||||
this.outputChannelManager.getChannel('Arduino').clear();
|
||||
}
|
||||
|
||||
reportProgress(progress: ProgressMessage): void {
|
||||
this.progressDidChangeEmitter.fire(progress);
|
||||
}
|
||||
|
@@ -0,0 +1,69 @@
|
||||
import { Line, SerialMonitorOutput } from './serial-monitor-send-output';
|
||||
|
||||
export function messagesToLines(
|
||||
messages: string[],
|
||||
prevLines: Line[] = [],
|
||||
charCount = 0,
|
||||
separator = '\n'
|
||||
): [Line[], number] {
|
||||
const linesToAdd: Line[] = prevLines.length
|
||||
? [prevLines[prevLines.length - 1]]
|
||||
: [{ message: '', lineLen: 0 }];
|
||||
if (!(Symbol.iterator in Object(messages))) return [prevLines, charCount];
|
||||
|
||||
for (const message of messages) {
|
||||
const messageLen = message.length;
|
||||
charCount += messageLen;
|
||||
const lastLine = linesToAdd[linesToAdd.length - 1];
|
||||
|
||||
// if the previous messages ends with "separator" add a new line
|
||||
if (lastLine.message.charAt(lastLine.message.length - 1) === separator) {
|
||||
linesToAdd.push({
|
||||
message,
|
||||
timestamp: new Date(),
|
||||
lineLen: messageLen,
|
||||
});
|
||||
} else {
|
||||
// concatenate to the last line
|
||||
linesToAdd[linesToAdd.length - 1].message += message;
|
||||
linesToAdd[linesToAdd.length - 1].lineLen += messageLen;
|
||||
if (!linesToAdd[linesToAdd.length - 1].timestamp) {
|
||||
linesToAdd[linesToAdd.length - 1].timestamp = new Date();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prevLines.splice(prevLines.length - 1, 1, ...linesToAdd);
|
||||
return [prevLines, charCount];
|
||||
}
|
||||
|
||||
export function truncateLines(
|
||||
lines: Line[],
|
||||
charCount: number,
|
||||
maxCharacters: number = SerialMonitorOutput.MAX_CHARACTERS
|
||||
): [Line[], number] {
|
||||
let charsToDelete = charCount - maxCharacters;
|
||||
let lineIndex = 0;
|
||||
while (charsToDelete > 0 || lineIndex > 0) {
|
||||
const firstLineLength = lines[lineIndex]?.lineLen;
|
||||
|
||||
if (charsToDelete >= firstLineLength) {
|
||||
// every time a full line to delete is found, move the index.
|
||||
lineIndex++;
|
||||
charsToDelete -= firstLineLength;
|
||||
charCount -= firstLineLength;
|
||||
continue;
|
||||
}
|
||||
|
||||
// delete all previous lines
|
||||
lines.splice(0, lineIndex);
|
||||
lineIndex = 0;
|
||||
|
||||
const newFirstLine = lines[0]?.message?.substring(charsToDelete);
|
||||
const deletedCharsCount = firstLineLength - newFirstLine.length;
|
||||
charCount -= deletedCharsCount;
|
||||
charsToDelete -= deletedCharsCount;
|
||||
lines[0].message = newFirstLine;
|
||||
}
|
||||
return [lines, charCount];
|
||||
}
|
@@ -1,31 +1,41 @@
|
||||
import * as React from 'react';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { AbstractViewContribution } from '@theia/core/lib/browser';
|
||||
import { AbstractViewContribution, codicon } from '@theia/core/lib/browser';
|
||||
import { MonitorWidget } from './monitor-widget';
|
||||
import { MenuModelRegistry, Command, CommandRegistry } from '@theia/core';
|
||||
import {
|
||||
TabBarToolbarContribution,
|
||||
TabBarToolbarRegistry,
|
||||
} from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
|
||||
import { MonitorModel } from './monitor-model';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { ArduinoToolbar } from '../../toolbar/arduino-toolbar';
|
||||
import { SerialModel } from '../serial-model';
|
||||
import { ArduinoMenus } from '../../menu/arduino-menus';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
export namespace SerialMonitor {
|
||||
export namespace Commands {
|
||||
export const AUTOSCROLL: Command = {
|
||||
id: 'serial-monitor-autoscroll',
|
||||
label: 'Autoscroll',
|
||||
};
|
||||
export const TIMESTAMP: Command = {
|
||||
id: 'serial-monitor-timestamp',
|
||||
label: 'Timestamp',
|
||||
};
|
||||
export const CLEAR_OUTPUT: Command = {
|
||||
id: 'serial-monitor-clear-output',
|
||||
label: 'Clear Output',
|
||||
iconClass: 'clear-all',
|
||||
};
|
||||
export const AUTOSCROLL = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'serial-monitor-autoscroll',
|
||||
label: 'Autoscroll',
|
||||
},
|
||||
'arduino/serial/autoscroll'
|
||||
);
|
||||
export const TIMESTAMP = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'serial-monitor-timestamp',
|
||||
label: 'Timestamp',
|
||||
},
|
||||
'arduino/serial/timestamp'
|
||||
);
|
||||
export const CLEAR_OUTPUT = Command.toLocalizedCommand(
|
||||
{
|
||||
id: 'serial-monitor-clear-output',
|
||||
label: 'Clear Output',
|
||||
iconClass: codicon('clear-all'),
|
||||
},
|
||||
'vscode/output.contribution/clearOutput.label'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,12 +48,12 @@ export class MonitorViewContribution
|
||||
static readonly TOGGLE_SERIAL_MONITOR_TOOLBAR =
|
||||
MonitorWidget.ID + ':toggle-toolbar';
|
||||
|
||||
@inject(MonitorModel) protected readonly model: MonitorModel;
|
||||
@inject(SerialModel) protected readonly model: SerialModel;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
widgetId: MonitorWidget.ID,
|
||||
widgetName: 'Serial Monitor',
|
||||
widgetName: MonitorWidget.LABEL,
|
||||
defaultWidgetOptions: {
|
||||
area: 'bottom',
|
||||
},
|
||||
@@ -56,7 +66,7 @@ export class MonitorViewContribution
|
||||
if (this.toggleCommand) {
|
||||
menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, {
|
||||
commandId: this.toggleCommand.id,
|
||||
label: 'Serial Monitor',
|
||||
label: MonitorWidget.LABEL,
|
||||
order: '5',
|
||||
});
|
||||
}
|
||||
@@ -78,7 +88,10 @@ export class MonitorViewContribution
|
||||
registry.registerItem({
|
||||
id: SerialMonitor.Commands.CLEAR_OUTPUT.id,
|
||||
command: SerialMonitor.Commands.CLEAR_OUTPUT.id,
|
||||
tooltip: 'Clear Output',
|
||||
tooltip: nls.localize(
|
||||
'vscode/output.contribution/clearOutput.label',
|
||||
'Clear Output'
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -120,7 +133,10 @@ export class MonitorViewContribution
|
||||
return (
|
||||
<React.Fragment key="autoscroll-toolbar-item">
|
||||
<div
|
||||
title="Toggle Autoscroll"
|
||||
title={nls.localize(
|
||||
'vscode/output.contribution/toggleAutoScroll',
|
||||
'Toggle Autoscroll'
|
||||
)}
|
||||
className={`item enabled fa fa-angle-double-down arduino-monitor ${
|
||||
this.model.autoscroll ? 'toggled' : ''
|
||||
}`}
|
||||
@@ -139,7 +155,10 @@ export class MonitorViewContribution
|
||||
return (
|
||||
<React.Fragment key="line-ending-toolbar-item">
|
||||
<div
|
||||
title="Toggle Timestamp"
|
||||
title={nls.localize(
|
||||
'arduino/serial/toggleTimestamp',
|
||||
'Toggle Timestamp'
|
||||
)}
|
||||
className={`item enabled fa fa-clock-o arduino-monitor ${
|
||||
this.model.timestamp ? 'toggled' : ''
|
||||
}`}
|
@@ -0,0 +1,225 @@
|
||||
import * as React from 'react';
|
||||
import { postConstruct, injectable, inject } from 'inversify';
|
||||
import { OptionsType } from 'react-select/src/types';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { Disposable } from '@theia/core/lib/common/disposable';
|
||||
import {
|
||||
ReactWidget,
|
||||
Message,
|
||||
Widget,
|
||||
MessageLoop,
|
||||
} from '@theia/core/lib/browser/widgets';
|
||||
import { SerialConfig } from '../../../common/protocol/serial-service';
|
||||
import { ArduinoSelect } from '../../widgets/arduino-select';
|
||||
import { SerialModel } from '../serial-model';
|
||||
import { SerialConnectionManager } from '../serial-connection-manager';
|
||||
import { SerialMonitorSendInput } from './serial-monitor-send-input';
|
||||
import { SerialMonitorOutput } from './serial-monitor-send-output';
|
||||
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class MonitorWidget extends ReactWidget {
|
||||
static readonly LABEL = nls.localize(
|
||||
'arduino/common/serialMonitor',
|
||||
'Serial Monitor'
|
||||
);
|
||||
static readonly ID = 'serial-monitor';
|
||||
|
||||
@inject(SerialModel)
|
||||
protected readonly serialModel: SerialModel;
|
||||
|
||||
@inject(SerialConnectionManager)
|
||||
protected readonly serialConnection: SerialConnectionManager;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
|
||||
protected widgetHeight: number;
|
||||
|
||||
/**
|
||||
* Do not touch or use it. It is for setting the focus on the `input` after the widget activation.
|
||||
*/
|
||||
protected focusNode: HTMLElement | undefined;
|
||||
/**
|
||||
* Guard against re-rendering the view after the close was requested.
|
||||
* See: https://github.com/eclipse-theia/theia/issues/6704
|
||||
*/
|
||||
protected closing = false;
|
||||
protected readonly clearOutputEmitter = new Emitter<void>();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.id = MonitorWidget.ID;
|
||||
this.title.label = MonitorWidget.LABEL;
|
||||
this.title.iconClass = 'monitor-tab-icon';
|
||||
this.title.closable = true;
|
||||
this.scrollOptions = undefined;
|
||||
this.toDispose.push(this.clearOutputEmitter);
|
||||
this.toDispose.push(
|
||||
Disposable.create(() => this.serialConnection.closeWStoBE())
|
||||
);
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.update();
|
||||
this.toDispose.push(
|
||||
this.serialConnection.onConnectionChanged(() => this.clearConsole())
|
||||
);
|
||||
this.toDispose.push(this.serialModel.onChange(() => this.update()));
|
||||
}
|
||||
|
||||
clearConsole(): void {
|
||||
this.clearOutputEmitter.fire(undefined);
|
||||
this.update();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
super.onAfterAttach(msg);
|
||||
this.serialConnection.openWSToBE();
|
||||
}
|
||||
|
||||
onCloseRequest(msg: Message): void {
|
||||
this.closing = true;
|
||||
super.onCloseRequest(msg);
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message): void {
|
||||
// TODO: `this.isAttached`
|
||||
// See: https://github.com/eclipse-theia/theia/issues/6704#issuecomment-562574713
|
||||
if (!this.closing && this.isAttached) {
|
||||
super.onUpdateRequest(msg);
|
||||
}
|
||||
}
|
||||
|
||||
protected onResize(msg: Widget.ResizeMessage): void {
|
||||
super.onResize(msg);
|
||||
this.widgetHeight = msg.height;
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
(this.focusNode || this.node).focus();
|
||||
}
|
||||
|
||||
protected onFocusResolved = (element: HTMLElement | undefined) => {
|
||||
if (this.closing || !this.isAttached) {
|
||||
return;
|
||||
}
|
||||
this.focusNode = element;
|
||||
requestAnimationFrame(() =>
|
||||
MessageLoop.sendMessage(this, Widget.Msg.ActivateRequest)
|
||||
);
|
||||
};
|
||||
|
||||
protected get lineEndings(): OptionsType<
|
||||
SerialMonitorOutput.SelectOption<SerialModel.EOL>
|
||||
> {
|
||||
return [
|
||||
{
|
||||
label: nls.localize('arduino/serial/noLineEndings', 'No Line Ending'),
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
label: nls.localize('arduino/serial/newLine', 'New Line'),
|
||||
value: '\n',
|
||||
},
|
||||
{
|
||||
label: nls.localize('arduino/serial/carriageReturn', 'Carriage Return'),
|
||||
value: '\r',
|
||||
},
|
||||
{
|
||||
label: nls.localize(
|
||||
'arduino/serial/newLineCarriageReturn',
|
||||
'Both NL & CR'
|
||||
),
|
||||
value: '\r\n',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
protected get baudRates(): OptionsType<
|
||||
SerialMonitorOutput.SelectOption<SerialConfig.BaudRate>
|
||||
> {
|
||||
const baudRates: Array<SerialConfig.BaudRate> = [
|
||||
300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200,
|
||||
];
|
||||
return baudRates.map((baudRate) => ({
|
||||
label: baudRate + ' baud',
|
||||
value: baudRate,
|
||||
}));
|
||||
}
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
const { baudRates, lineEndings } = this;
|
||||
const lineEnding =
|
||||
lineEndings.find((item) => item.value === this.serialModel.lineEnding) ||
|
||||
lineEndings[1]; // Defaults to `\n`.
|
||||
const baudRate =
|
||||
baudRates.find((item) => item.value === this.serialModel.baudRate) ||
|
||||
baudRates[4]; // Defaults to `9600`.
|
||||
return (
|
||||
<div className="serial-monitor">
|
||||
<div className="head">
|
||||
<div className="send">
|
||||
<SerialMonitorSendInput
|
||||
serialConnection={this.serialConnection}
|
||||
resolveFocus={this.onFocusResolved}
|
||||
onSend={this.onSend}
|
||||
/>
|
||||
</div>
|
||||
<div className="config">
|
||||
<div className="select">
|
||||
<ArduinoSelect
|
||||
maxMenuHeight={this.widgetHeight - 40}
|
||||
options={lineEndings}
|
||||
value={lineEnding}
|
||||
onChange={this.onChangeLineEnding}
|
||||
/>
|
||||
</div>
|
||||
<div className="select">
|
||||
<ArduinoSelect
|
||||
className="select"
|
||||
maxMenuHeight={this.widgetHeight - 40}
|
||||
options={baudRates}
|
||||
value={baudRate}
|
||||
onChange={this.onChangeBaudRate}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="body">
|
||||
<SerialMonitorOutput
|
||||
serialModel={this.serialModel}
|
||||
serialConnection={this.serialConnection}
|
||||
clearConsoleEvent={this.clearOutputEmitter.event}
|
||||
height={Math.floor(this.widgetHeight - 50)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
protected readonly onSend = (value: string) => this.doSend(value);
|
||||
protected async doSend(value: string): Promise<void> {
|
||||
this.serialConnection.send(value);
|
||||
}
|
||||
|
||||
protected readonly onChangeLineEnding = (
|
||||
option: SerialMonitorOutput.SelectOption<SerialModel.EOL>
|
||||
) => {
|
||||
this.serialModel.lineEnding = option.value;
|
||||
};
|
||||
|
||||
protected readonly onChangeBaudRate = (
|
||||
option: SerialMonitorOutput.SelectOption<SerialConfig.BaudRate>
|
||||
) => {
|
||||
this.serialModel.baudRate = option.value;
|
||||
};
|
||||
}
|
@@ -0,0 +1,118 @@
|
||||
import * as React from 'react';
|
||||
import { Key, KeyCode } from '@theia/core/lib/browser/keys';
|
||||
import { Board, Port } from '../../../common/protocol/boards-service';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import { DisposableCollection, nls } from '@theia/core/lib/common';
|
||||
import { SerialConnectionManager } from '../serial-connection-manager';
|
||||
import { SerialPlotter } from '../plotter/protocol';
|
||||
|
||||
export namespace SerialMonitorSendInput {
|
||||
export interface Props {
|
||||
readonly serialConnection: SerialConnectionManager;
|
||||
readonly onSend: (text: string) => void;
|
||||
readonly resolveFocus: (element: HTMLElement | undefined) => void;
|
||||
}
|
||||
export interface State {
|
||||
text: string;
|
||||
connected: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export class SerialMonitorSendInput extends React.Component<
|
||||
SerialMonitorSendInput.Props,
|
||||
SerialMonitorSendInput.State
|
||||
> {
|
||||
protected toDisposeBeforeUnmount = new DisposableCollection();
|
||||
|
||||
constructor(props: Readonly<SerialMonitorSendInput.Props>) {
|
||||
super(props);
|
||||
this.state = { text: '', connected: false };
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onSend = this.onSend.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.props.serialConnection.isBESerialConnected().then((connected) => {
|
||||
this.setState({ connected });
|
||||
});
|
||||
|
||||
this.toDisposeBeforeUnmount.pushAll([
|
||||
this.props.serialConnection.onRead(({ messages }) => {
|
||||
if (
|
||||
messages.command ===
|
||||
SerialPlotter.Protocol.Command.MIDDLEWARE_CONFIG_CHANGED &&
|
||||
'connected' in messages.data
|
||||
) {
|
||||
this.setState({ connected: messages.data.connected });
|
||||
}
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
// TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout?
|
||||
this.toDisposeBeforeUnmount.dispose();
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return (
|
||||
<input
|
||||
ref={this.setRef}
|
||||
type="text"
|
||||
className={`theia-input ${this.state.connected ? '' : 'warning'}`}
|
||||
placeholder={this.placeholder}
|
||||
value={this.state.text}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
protected get placeholder(): string {
|
||||
const serialConfig = this.props.serialConnection.getConfig();
|
||||
if (!this.state.connected || !serialConfig) {
|
||||
return nls.localize(
|
||||
'arduino/serial/notConnected',
|
||||
'Not connected. Select a board and a port to connect automatically.'
|
||||
);
|
||||
}
|
||||
const { board, port } = serialConfig;
|
||||
return nls.localize(
|
||||
'arduino/serial/message',
|
||||
"Message ({0} + Enter to send message to '{1}' on '{2}'",
|
||||
isOSX ? '⌘' : nls.localize('vscode/keybindingLabels/ctrlKey', 'Ctrl'),
|
||||
board
|
||||
? Board.toString(board, {
|
||||
useFqbn: false,
|
||||
})
|
||||
: 'unknown',
|
||||
port ? Port.toString(port) : 'unknown'
|
||||
);
|
||||
}
|
||||
|
||||
protected setRef = (element: HTMLElement | null) => {
|
||||
if (this.props.resolveFocus) {
|
||||
this.props.resolveFocus(element || undefined);
|
||||
}
|
||||
};
|
||||
|
||||
protected onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
this.setState({ text: event.target.value });
|
||||
}
|
||||
|
||||
protected onSend(): void {
|
||||
this.props.onSend(this.state.text);
|
||||
this.setState({ text: '' });
|
||||
}
|
||||
|
||||
protected onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||
const keyCode = KeyCode.createKeyCode(event.nativeEvent);
|
||||
if (keyCode) {
|
||||
const { key, meta, ctrl } = keyCode;
|
||||
if (key === Key.ENTER && ((isOSX && meta) || (!isOSX && ctrl))) {
|
||||
this.onSend();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,146 @@
|
||||
import * as React from 'react';
|
||||
import { Event } from '@theia/core/lib/common/event';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { areEqual, FixedSizeList as List } from 'react-window';
|
||||
import { SerialModel } from '../serial-model';
|
||||
import { SerialConnectionManager } from '../serial-connection-manager';
|
||||
import dateFormat = require('dateformat');
|
||||
import { messagesToLines, truncateLines } from './monitor-utils';
|
||||
|
||||
export type Line = { message: string; timestamp?: Date; lineLen: number };
|
||||
|
||||
export class SerialMonitorOutput extends React.Component<
|
||||
SerialMonitorOutput.Props,
|
||||
SerialMonitorOutput.State
|
||||
> {
|
||||
/**
|
||||
* Do not touch it. It is used to be able to "follow" the serial monitor log.
|
||||
*/
|
||||
protected toDisposeBeforeUnmount = new DisposableCollection();
|
||||
private listRef: React.RefObject<any>;
|
||||
|
||||
constructor(props: Readonly<SerialMonitorOutput.Props>) {
|
||||
super(props);
|
||||
this.listRef = React.createRef();
|
||||
this.state = {
|
||||
lines: [],
|
||||
timestamp: this.props.serialModel.timestamp,
|
||||
charCount: 0,
|
||||
};
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return (
|
||||
<List
|
||||
className="serial-monitor-messages"
|
||||
height={this.props.height}
|
||||
itemData={
|
||||
{
|
||||
lines: this.state.lines,
|
||||
timestamp: this.state.timestamp,
|
||||
} as any
|
||||
}
|
||||
itemCount={this.state.lines.length}
|
||||
itemSize={18}
|
||||
width={'100%'}
|
||||
ref={this.listRef}
|
||||
>
|
||||
{Row}
|
||||
</List>
|
||||
);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.scrollToBottom();
|
||||
this.toDisposeBeforeUnmount.pushAll([
|
||||
this.props.serialConnection.onRead(({ messages }) => {
|
||||
const [newLines, totalCharCount] = messagesToLines(
|
||||
messages,
|
||||
this.state.lines,
|
||||
this.state.charCount
|
||||
);
|
||||
const [lines, charCount] = truncateLines(newLines, totalCharCount);
|
||||
|
||||
this.setState({
|
||||
lines,
|
||||
charCount,
|
||||
});
|
||||
this.scrollToBottom();
|
||||
}),
|
||||
this.props.clearConsoleEvent(() =>
|
||||
this.setState({ lines: [], charCount: 0 })
|
||||
),
|
||||
this.props.serialModel.onChange(({ property }) => {
|
||||
if (property === 'timestamp') {
|
||||
const { timestamp } = this.props.serialModel;
|
||||
this.setState({ timestamp });
|
||||
}
|
||||
if (property === 'autoscroll') {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
// TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout?
|
||||
this.toDisposeBeforeUnmount.dispose();
|
||||
}
|
||||
|
||||
scrollToBottom = ((): void => {
|
||||
if (this.listRef.current && this.props.serialModel.autoscroll) {
|
||||
this.listRef.current.scrollToItem(this.state.lines.length, 'end');
|
||||
}
|
||||
}).bind(this);
|
||||
}
|
||||
|
||||
const _Row = ({
|
||||
index,
|
||||
style,
|
||||
data,
|
||||
}: {
|
||||
index: number;
|
||||
style: any;
|
||||
data: { lines: Line[]; timestamp: boolean };
|
||||
}) => {
|
||||
const timestamp =
|
||||
(data.timestamp &&
|
||||
`${dateFormat(data.lines[index].timestamp, 'H:M:ss.l')} -> `) ||
|
||||
'';
|
||||
return (
|
||||
(data.lines[index].lineLen && (
|
||||
<div style={style}>
|
||||
{timestamp}
|
||||
{data.lines[index].message}
|
||||
</div>
|
||||
)) ||
|
||||
null
|
||||
);
|
||||
};
|
||||
const Row = React.memo(_Row, areEqual);
|
||||
|
||||
export namespace SerialMonitorOutput {
|
||||
export interface Props {
|
||||
readonly serialModel: SerialModel;
|
||||
readonly serialConnection: SerialConnectionManager;
|
||||
readonly clearConsoleEvent: Event<void>;
|
||||
readonly height: number;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
lines: Line[];
|
||||
timestamp: boolean;
|
||||
charCount: number;
|
||||
}
|
||||
|
||||
export interface SelectOption<T> {
|
||||
readonly label: string;
|
||||
readonly value: T;
|
||||
}
|
||||
|
||||
export const MAX_CHARACTERS = 1_000_000;
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
import { ThemeService } from '@theia/core/lib/browser/theming';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import {
|
||||
Command,
|
||||
CommandRegistry,
|
||||
MaybePromise,
|
||||
MenuModelRegistry,
|
||||
} from '@theia/core';
|
||||
import { SerialModel } from '../serial-model';
|
||||
import { ArduinoMenus } from '../../menu/arduino-menus';
|
||||
import { Contribution } from '../../contributions/contribution';
|
||||
import { Endpoint, FrontendApplication } from '@theia/core/lib/browser';
|
||||
import { ipcRenderer } from '@theia/core/shared/electron';
|
||||
import { SerialConfig } from '../../../common/protocol';
|
||||
import { SerialConnectionManager } from '../serial-connection-manager';
|
||||
import { SerialPlotter } from './protocol';
|
||||
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
|
||||
const queryString = require('query-string');
|
||||
|
||||
export namespace SerialPlotterContribution {
|
||||
export namespace Commands {
|
||||
export const OPEN: Command = {
|
||||
id: 'serial-plotter-open',
|
||||
label: 'Serial Plotter',
|
||||
category: 'Arduino',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class PlotterFrontendContribution extends Contribution {
|
||||
protected window: Window | null;
|
||||
protected url: string;
|
||||
protected wsPort: number;
|
||||
|
||||
@inject(SerialModel)
|
||||
protected readonly model: SerialModel;
|
||||
|
||||
@inject(ThemeService)
|
||||
protected readonly themeService: ThemeService;
|
||||
|
||||
@inject(SerialConnectionManager)
|
||||
protected readonly serialConnection: SerialConnectionManager;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
|
||||
onStart(app: FrontendApplication): MaybePromise<void> {
|
||||
this.url = new Endpoint({ path: '/plotter' }).getRestUrl().toString();
|
||||
|
||||
ipcRenderer.on('CLOSE_CHILD_WINDOW', async () => {
|
||||
if (!!this.window) {
|
||||
this.window = null;
|
||||
}
|
||||
});
|
||||
return super.onStart(app);
|
||||
}
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(SerialPlotterContribution.Commands.OPEN, {
|
||||
execute: this.connect.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(menus: MenuModelRegistry): void {
|
||||
menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, {
|
||||
commandId: SerialPlotterContribution.Commands.OPEN.id,
|
||||
label: SerialPlotterContribution.Commands.OPEN.label,
|
||||
order: '7',
|
||||
});
|
||||
}
|
||||
|
||||
async connect(): Promise<void> {
|
||||
if (!!this.window) {
|
||||
this.window.focus();
|
||||
return;
|
||||
}
|
||||
const wsPort = this.serialConnection.getWsPort();
|
||||
if (wsPort) {
|
||||
this.open(wsPort);
|
||||
} else {
|
||||
this.messageService.error(`Couldn't open serial plotter`);
|
||||
}
|
||||
}
|
||||
|
||||
protected async open(wsPort: number): Promise<void> {
|
||||
const initConfig: Partial<SerialPlotter.Config> = {
|
||||
baudrates: SerialConfig.BaudRates.map((b) => b),
|
||||
currentBaudrate: this.model.baudRate,
|
||||
currentLineEnding: this.model.lineEnding,
|
||||
darkTheme: this.themeService.getCurrentTheme().type === 'dark',
|
||||
wsPort,
|
||||
interpolate: this.model.interpolate,
|
||||
connected: await this.serialConnection.isBESerialConnected(),
|
||||
serialPort: this.boardsServiceProvider.boardsConfig.selectedPort?.address,
|
||||
};
|
||||
const urlWithParams = queryString.stringifyUrl(
|
||||
{
|
||||
url: this.url,
|
||||
query: initConfig,
|
||||
},
|
||||
{ arrayFormat: 'comma' }
|
||||
);
|
||||
this.window = window.open(urlWithParams, 'serialPlotter');
|
||||
}
|
||||
}
|
26
arduino-ide-extension/src/browser/serial/plotter/protocol.ts
Normal file
26
arduino-ide-extension/src/browser/serial/plotter/protocol.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export namespace SerialPlotter {
|
||||
export type Config = {
|
||||
currentBaudrate: number;
|
||||
baudrates: number[];
|
||||
currentLineEnding: string;
|
||||
darkTheme: boolean;
|
||||
wsPort: number;
|
||||
interpolate: boolean;
|
||||
serialPort: string;
|
||||
connected: boolean;
|
||||
generate?: boolean;
|
||||
};
|
||||
export namespace Protocol {
|
||||
export enum Command {
|
||||
PLOTTER_SET_BAUDRATE = 'PLOTTER_SET_BAUDRATE',
|
||||
PLOTTER_SET_LINE_ENDING = 'PLOTTER_SET_LINE_ENDING',
|
||||
PLOTTER_SET_INTERPOLATE = 'PLOTTER_SET_INTERPOLATE',
|
||||
PLOTTER_SEND_MESSAGE = 'PLOTTER_SEND_MESSAGE',
|
||||
MIDDLEWARE_CONFIG_CHANGED = 'MIDDLEWARE_CONFIG_CHANGED',
|
||||
}
|
||||
export type Message = {
|
||||
command: SerialPlotter.Protocol.Command;
|
||||
data?: any;
|
||||
};
|
||||
}
|
||||
}
|
@@ -0,0 +1,361 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { Emitter, Event } from '@theia/core/lib/common/event';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import {
|
||||
SerialService,
|
||||
SerialConfig,
|
||||
SerialError,
|
||||
Status,
|
||||
SerialServiceClient,
|
||||
} from '../../common/protocol/serial-service';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import {
|
||||
Port,
|
||||
Board,
|
||||
BoardsService,
|
||||
} from '../../common/protocol/boards-service';
|
||||
import { BoardsConfig } from '../boards/boards-config';
|
||||
import { SerialModel } from './serial-model';
|
||||
import { ThemeService } from '@theia/core/lib/browser/theming';
|
||||
import { CoreService } from '../../common/protocol';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
|
||||
@injectable()
|
||||
export class SerialConnectionManager {
|
||||
protected config: Partial<SerialConfig> = {
|
||||
board: undefined,
|
||||
port: undefined,
|
||||
baudRate: undefined,
|
||||
};
|
||||
|
||||
protected readonly onConnectionChangedEmitter = new Emitter<boolean>();
|
||||
|
||||
/**
|
||||
* This emitter forwards all read events **if** the connection is established.
|
||||
*/
|
||||
protected readonly onReadEmitter = new Emitter<{ messages: string[] }>();
|
||||
|
||||
/**
|
||||
* Array for storing previous serial errors received from the server, and based on the number of elements in this array,
|
||||
* we adjust the reconnection delay.
|
||||
* Super naive way: we wait `array.length * 1000` ms. Once we hit 10 errors, we do not try to reconnect and clean the array.
|
||||
*/
|
||||
protected serialErrors: SerialError[] = [];
|
||||
protected reconnectTimeout?: number;
|
||||
|
||||
/**
|
||||
* When the websocket server is up on the backend, we save the port here, so that the client knows how to connect to it
|
||||
* */
|
||||
protected wsPort?: number;
|
||||
protected webSocket?: WebSocket;
|
||||
|
||||
constructor(
|
||||
@inject(SerialModel) protected readonly serialModel: SerialModel,
|
||||
@inject(SerialService) protected readonly serialService: SerialService,
|
||||
@inject(SerialServiceClient)
|
||||
protected readonly serialServiceClient: SerialServiceClient,
|
||||
@inject(BoardsService) protected readonly boardsService: BoardsService,
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceProvider: BoardsServiceProvider,
|
||||
@inject(MessageService) protected messageService: MessageService,
|
||||
@inject(ThemeService) protected readonly themeService: ThemeService,
|
||||
@inject(CoreService) protected readonly core: CoreService,
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceClientImpl: BoardsServiceProvider
|
||||
) {
|
||||
this.serialServiceClient.onWebSocketChanged(
|
||||
this.handleWebSocketChanged.bind(this)
|
||||
);
|
||||
this.serialServiceClient.onBaudRateChanged((baudRate) => {
|
||||
if (this.serialModel.baudRate !== baudRate) {
|
||||
this.serialModel.baudRate = baudRate;
|
||||
}
|
||||
});
|
||||
this.serialServiceClient.onLineEndingChanged((lineending) => {
|
||||
if (this.serialModel.lineEnding !== lineending) {
|
||||
this.serialModel.lineEnding = lineending;
|
||||
}
|
||||
});
|
||||
this.serialServiceClient.onInterpolateChanged((interpolate) => {
|
||||
if (this.serialModel.interpolate !== interpolate) {
|
||||
this.serialModel.interpolate = interpolate;
|
||||
}
|
||||
});
|
||||
|
||||
this.serialServiceClient.onError(this.handleError.bind(this));
|
||||
this.boardsServiceProvider.onBoardsConfigChanged(
|
||||
this.handleBoardConfigChange.bind(this)
|
||||
);
|
||||
|
||||
// Handles the `baudRate` changes by reconnecting if required.
|
||||
this.serialModel.onChange(async ({ property }) => {
|
||||
if (
|
||||
property === 'baudRate' &&
|
||||
(await this.serialService.isSerialPortOpen())
|
||||
) {
|
||||
const { boardsConfig } = this.boardsServiceProvider;
|
||||
this.handleBoardConfigChange(boardsConfig);
|
||||
}
|
||||
|
||||
// update the current values in the backend and propagate to websocket clients
|
||||
this.serialService.updateWsConfigParam({
|
||||
...(property === 'lineEnding' && {
|
||||
currentLineEnding: this.serialModel.lineEnding,
|
||||
}),
|
||||
...(property === 'interpolate' && {
|
||||
interpolate: this.serialModel.interpolate,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
this.themeService.onDidColorThemeChange((theme) => {
|
||||
this.serialService.updateWsConfigParam({
|
||||
darkTheme: theme.newTheme.type === 'dark',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updated the config in the BE passing only the properties that has changed.
|
||||
* BE will create a new connection if needed.
|
||||
*
|
||||
* @param newConfig the porperties of the config that has changed
|
||||
*/
|
||||
async setConfig(newConfig: Partial<SerialConfig>): Promise<void> {
|
||||
let configHasChanged = false;
|
||||
Object.keys(this.config).forEach((key: keyof SerialConfig) => {
|
||||
if (newConfig[key] !== this.config[key]) {
|
||||
configHasChanged = true;
|
||||
this.config = { ...this.config, [key]: newConfig[key] };
|
||||
}
|
||||
});
|
||||
|
||||
if (configHasChanged) {
|
||||
this.serialService.updateWsConfigParam({
|
||||
currentBaudrate: this.config.baudRate,
|
||||
serialPort: this.config.port?.address,
|
||||
});
|
||||
|
||||
if (isSerialConfig(this.config)) {
|
||||
this.serialService.setSerialConfig(this.config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getConfig(): Partial<SerialConfig> {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
getWsPort(): number | undefined {
|
||||
return this.wsPort;
|
||||
}
|
||||
|
||||
protected handleWebSocketChanged(wsPort: number): void {
|
||||
this.wsPort = wsPort;
|
||||
}
|
||||
|
||||
get serialConfig(): SerialConfig | undefined {
|
||||
return isSerialConfig(this.config)
|
||||
? (this.config as SerialConfig)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
async isBESerialConnected(): Promise<boolean> {
|
||||
return await this.serialService.isSerialPortOpen();
|
||||
}
|
||||
|
||||
openWSToBE(): void {
|
||||
if (!isSerialConfig(this.config)) {
|
||||
this.messageService.error(
|
||||
`Please select a board and a port to open the serial connection.`
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.webSocket && this.wsPort) {
|
||||
try {
|
||||
this.webSocket = new WebSocket(`ws://localhost:${this.wsPort}`);
|
||||
this.webSocket.onmessage = (res) => {
|
||||
const messages = JSON.parse(res.data);
|
||||
this.onReadEmitter.fire({ messages });
|
||||
};
|
||||
} catch {
|
||||
this.messageService.error(`Unable to connect to websocket`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closeWStoBE(): void {
|
||||
if (this.webSocket) {
|
||||
try {
|
||||
this.webSocket.close();
|
||||
this.webSocket = undefined;
|
||||
} catch {
|
||||
this.messageService.error(`Unable to close websocket`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles error on the SerialServiceClient and try to reconnect, eventually
|
||||
*/
|
||||
async handleError(error: SerialError): Promise<void> {
|
||||
if (!(await this.serialService.isSerialPortOpen())) return;
|
||||
const { code, config } = error;
|
||||
const { board, port } = config;
|
||||
const options = { timeout: 3000 };
|
||||
switch (code) {
|
||||
case SerialError.ErrorCodes.CLIENT_CANCEL: {
|
||||
console.debug(
|
||||
`Serial connection was canceled by client: ${Serial.Config.toString(
|
||||
this.config
|
||||
)}.`
|
||||
);
|
||||
break;
|
||||
}
|
||||
case SerialError.ErrorCodes.DEVICE_BUSY: {
|
||||
this.messageService.warn(
|
||||
nls.localize(
|
||||
'arduino/serial/connectionBusy',
|
||||
'Connection failed. Serial port is busy: {0}',
|
||||
Port.toString(port)
|
||||
),
|
||||
options
|
||||
);
|
||||
this.serialErrors.push(error);
|
||||
break;
|
||||
}
|
||||
case SerialError.ErrorCodes.DEVICE_NOT_CONFIGURED: {
|
||||
this.messageService.info(
|
||||
nls.localize(
|
||||
'arduino/serial/disconnected',
|
||||
'Disconnected {0} from {1}.',
|
||||
Board.toString(board, {
|
||||
useFqbn: false,
|
||||
}),
|
||||
Port.toString(port)
|
||||
),
|
||||
options
|
||||
);
|
||||
break;
|
||||
}
|
||||
case undefined: {
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'arduino/serial/unexpectedError',
|
||||
'Unexpected error. Reconnecting {0} on port {1}.',
|
||||
Board.toString(board),
|
||||
Port.toString(port)
|
||||
),
|
||||
options
|
||||
);
|
||||
console.error(JSON.stringify(error));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((await this.serialService.clientsAttached()) > 0) {
|
||||
if (this.serialErrors.length >= 10) {
|
||||
this.messageService.warn(
|
||||
nls.localize(
|
||||
'arduino/serial/failedReconnect',
|
||||
'Failed to reconnect {0} to serial port after 10 consecutive attempts. The {1} serial port is busy.',
|
||||
Board.toString(board, {
|
||||
useFqbn: false,
|
||||
}),
|
||||
Port.toString(port)
|
||||
)
|
||||
);
|
||||
this.serialErrors.length = 0;
|
||||
} else {
|
||||
const attempts = this.serialErrors.length || 1;
|
||||
if (this.reconnectTimeout !== undefined) {
|
||||
// Clear the previous timer.
|
||||
window.clearTimeout(this.reconnectTimeout);
|
||||
}
|
||||
const timeout = attempts * 1000;
|
||||
this.messageService.warn(
|
||||
nls.localize(
|
||||
'arduino/serial/reconnect',
|
||||
'Reconnecting {0} to {1} in {2} seconds...',
|
||||
Board.toString(board, {
|
||||
useFqbn: false,
|
||||
}),
|
||||
Port.toString(port),
|
||||
attempts.toString()
|
||||
)
|
||||
);
|
||||
this.reconnectTimeout = window.setTimeout(
|
||||
() => this.reconnectAfterUpload(),
|
||||
timeout
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async reconnectAfterUpload(): Promise<void> {
|
||||
try {
|
||||
if (isSerialConfig(this.config)) {
|
||||
await this.boardsServiceClientImpl.waitUntilAvailable(
|
||||
Object.assign(this.config.board, { port: this.config.port }),
|
||||
10_000
|
||||
);
|
||||
this.serialService.connectSerialIfRequired();
|
||||
}
|
||||
} catch (waitError) {
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'arduino/sketch/couldNotConnectToSerial',
|
||||
'Could not reconnect to serial port. {0}',
|
||||
waitError.toString()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the data to the connected serial port.
|
||||
* The desired EOL is appended to `data`, you do not have to add it.
|
||||
* It is a NOOP if connected.
|
||||
*/
|
||||
async send(data: string): Promise<Status> {
|
||||
if (!(await this.serialService.isSerialPortOpen())) {
|
||||
return Status.NOT_CONNECTED;
|
||||
}
|
||||
return new Promise<Status>((resolve) => {
|
||||
this.serialService
|
||||
.sendMessageToSerial(data + this.serialModel.lineEnding)
|
||||
.then(() => resolve(Status.OK));
|
||||
});
|
||||
}
|
||||
|
||||
get onConnectionChanged(): Event<boolean> {
|
||||
return this.onConnectionChangedEmitter.event;
|
||||
}
|
||||
|
||||
get onRead(): Event<{ messages: any }> {
|
||||
return this.onReadEmitter.event;
|
||||
}
|
||||
|
||||
protected async handleBoardConfigChange(
|
||||
boardsConfig: BoardsConfig.Config
|
||||
): Promise<void> {
|
||||
const { selectedBoard: board, selectedPort: port } = boardsConfig;
|
||||
const { baudRate } = this.serialModel;
|
||||
const newConfig: Partial<SerialConfig> = { board, port, baudRate };
|
||||
this.setConfig(newConfig);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Serial {
|
||||
export namespace Config {
|
||||
export function toString(config: Partial<SerialConfig>): string {
|
||||
if (!isSerialConfig(config)) return '';
|
||||
const { board, port } = config;
|
||||
return `${Board.toString(board)} ${Port.toString(port)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isSerialConfig(config: Partial<SerialConfig>): config is SerialConfig {
|
||||
return !!config.board && !!config.baudRate && !!config.port;
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { Emitter, Event } from '@theia/core/lib/common/event';
|
||||
import { MonitorConfig } from '../../common/protocol/monitor-service';
|
||||
import { SerialConfig } from '../../common/protocol';
|
||||
import {
|
||||
FrontendApplicationContribution,
|
||||
LocalStorageService,
|
||||
@@ -8,8 +8,8 @@ import {
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
|
||||
@injectable()
|
||||
export class MonitorModel implements FrontendApplicationContribution {
|
||||
protected static STORAGE_ID = 'arduino-monitor-model';
|
||||
export class SerialModel implements FrontendApplicationContribution {
|
||||
protected static STORAGE_ID = 'arduino-serial-model';
|
||||
|
||||
@inject(LocalStorageService)
|
||||
protected readonly localStorageService: LocalStorageService;
|
||||
@@ -18,26 +18,28 @@ export class MonitorModel implements FrontendApplicationContribution {
|
||||
protected readonly boardsServiceClient: BoardsServiceProvider;
|
||||
|
||||
protected readonly onChangeEmitter: Emitter<
|
||||
MonitorModel.State.Change<keyof MonitorModel.State>
|
||||
SerialModel.State.Change<keyof SerialModel.State>
|
||||
>;
|
||||
protected _autoscroll: boolean;
|
||||
protected _timestamp: boolean;
|
||||
protected _baudRate: MonitorConfig.BaudRate;
|
||||
protected _lineEnding: MonitorModel.EOL;
|
||||
protected _baudRate: SerialConfig.BaudRate;
|
||||
protected _lineEnding: SerialModel.EOL;
|
||||
protected _interpolate: boolean;
|
||||
|
||||
constructor() {
|
||||
this._autoscroll = true;
|
||||
this._timestamp = false;
|
||||
this._baudRate = MonitorConfig.BaudRate.DEFAULT;
|
||||
this._lineEnding = MonitorModel.EOL.DEFAULT;
|
||||
this._baudRate = SerialConfig.BaudRate.DEFAULT;
|
||||
this._lineEnding = SerialModel.EOL.DEFAULT;
|
||||
this._interpolate = false;
|
||||
this.onChangeEmitter = new Emitter<
|
||||
MonitorModel.State.Change<keyof MonitorModel.State>
|
||||
SerialModel.State.Change<keyof SerialModel.State>
|
||||
>();
|
||||
}
|
||||
|
||||
onStart(): void {
|
||||
this.localStorageService
|
||||
.getData<MonitorModel.State>(MonitorModel.STORAGE_ID)
|
||||
.getData<SerialModel.State>(SerialModel.STORAGE_ID)
|
||||
.then((state) => {
|
||||
if (state) {
|
||||
this.restoreState(state);
|
||||
@@ -45,7 +47,7 @@ export class MonitorModel implements FrontendApplicationContribution {
|
||||
});
|
||||
}
|
||||
|
||||
get onChange(): Event<MonitorModel.State.Change<keyof MonitorModel.State>> {
|
||||
get onChange(): Event<SerialModel.State.Change<keyof SerialModel.State>> {
|
||||
return this.onChangeEmitter.event;
|
||||
}
|
||||
|
||||
@@ -78,11 +80,11 @@ export class MonitorModel implements FrontendApplicationContribution {
|
||||
);
|
||||
}
|
||||
|
||||
get baudRate(): MonitorConfig.BaudRate {
|
||||
get baudRate(): SerialConfig.BaudRate {
|
||||
return this._baudRate;
|
||||
}
|
||||
|
||||
set baudRate(baudRate: MonitorConfig.BaudRate) {
|
||||
set baudRate(baudRate: SerialConfig.BaudRate) {
|
||||
this._baudRate = baudRate;
|
||||
this.storeState().then(() =>
|
||||
this.onChangeEmitter.fire({
|
||||
@@ -92,11 +94,11 @@ export class MonitorModel implements FrontendApplicationContribution {
|
||||
);
|
||||
}
|
||||
|
||||
get lineEnding(): MonitorModel.EOL {
|
||||
get lineEnding(): SerialModel.EOL {
|
||||
return this._lineEnding;
|
||||
}
|
||||
|
||||
set lineEnding(lineEnding: MonitorModel.EOL) {
|
||||
set lineEnding(lineEnding: SerialModel.EOL) {
|
||||
this._lineEnding = lineEnding;
|
||||
this.storeState().then(() =>
|
||||
this.onChangeEmitter.fire({
|
||||
@@ -106,29 +108,46 @@ export class MonitorModel implements FrontendApplicationContribution {
|
||||
);
|
||||
}
|
||||
|
||||
protected restoreState(state: MonitorModel.State): void {
|
||||
get interpolate(): boolean {
|
||||
return this._interpolate;
|
||||
}
|
||||
|
||||
set interpolate(i: boolean) {
|
||||
this._interpolate = i;
|
||||
this.storeState().then(() =>
|
||||
this.onChangeEmitter.fire({
|
||||
property: 'interpolate',
|
||||
value: this._interpolate,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
protected restoreState(state: SerialModel.State): void {
|
||||
this._autoscroll = state.autoscroll;
|
||||
this._timestamp = state.timestamp;
|
||||
this._baudRate = state.baudRate;
|
||||
this._lineEnding = state.lineEnding;
|
||||
this._interpolate = state.interpolate;
|
||||
}
|
||||
|
||||
protected async storeState(): Promise<void> {
|
||||
return this.localStorageService.setData(MonitorModel.STORAGE_ID, {
|
||||
return this.localStorageService.setData(SerialModel.STORAGE_ID, {
|
||||
autoscroll: this._autoscroll,
|
||||
timestamp: this._timestamp,
|
||||
baudRate: this._baudRate,
|
||||
lineEnding: this._lineEnding,
|
||||
interpolate: this._interpolate,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export namespace MonitorModel {
|
||||
export namespace SerialModel {
|
||||
export interface State {
|
||||
autoscroll: boolean;
|
||||
timestamp: boolean;
|
||||
baudRate: MonitorConfig.BaudRate;
|
||||
baudRate: SerialConfig.BaudRate;
|
||||
lineEnding: EOL;
|
||||
interpolate: boolean;
|
||||
}
|
||||
export namespace State {
|
||||
export interface Change<K extends keyof State> {
|
@@ -0,0 +1,48 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import {
|
||||
SerialServiceClient,
|
||||
SerialError,
|
||||
SerialConfig,
|
||||
} from '../../common/protocol/serial-service';
|
||||
import { SerialModel } from './serial-model';
|
||||
|
||||
@injectable()
|
||||
export class SerialServiceClientImpl implements SerialServiceClient {
|
||||
protected readonly onErrorEmitter = new Emitter<SerialError>();
|
||||
readonly onError = this.onErrorEmitter.event;
|
||||
|
||||
protected readonly onWebSocketChangedEmitter = new Emitter<number>();
|
||||
readonly onWebSocketChanged = this.onWebSocketChangedEmitter.event;
|
||||
|
||||
protected readonly onBaudRateChangedEmitter =
|
||||
new Emitter<SerialConfig.BaudRate>();
|
||||
readonly onBaudRateChanged = this.onBaudRateChangedEmitter.event;
|
||||
|
||||
protected readonly onLineEndingChangedEmitter =
|
||||
new Emitter<SerialModel.EOL>();
|
||||
readonly onLineEndingChanged = this.onLineEndingChangedEmitter.event;
|
||||
|
||||
protected readonly onInterpolateChangedEmitter = new Emitter<boolean>();
|
||||
readonly onInterpolateChanged = this.onInterpolateChangedEmitter.event;
|
||||
|
||||
notifyError(error: SerialError): void {
|
||||
this.onErrorEmitter.fire(error);
|
||||
}
|
||||
|
||||
notifyWebSocketChanged(message: number): void {
|
||||
this.onWebSocketChangedEmitter.fire(message);
|
||||
}
|
||||
|
||||
notifyBaudRateChanged(message: SerialConfig.BaudRate): void {
|
||||
this.onBaudRateChangedEmitter.fire(message);
|
||||
}
|
||||
|
||||
notifyLineEndingChanged(message: SerialModel.EOL): void {
|
||||
this.onLineEndingChangedEmitter.fire(message);
|
||||
}
|
||||
|
||||
notifyInterpolateChanged(message: boolean): void {
|
||||
this.onInterpolateChangedEmitter.fire(message);
|
||||
}
|
||||
}
|
@@ -1,224 +1,237 @@
|
||||
div#select-board-dialog {
|
||||
margin: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
div#select-board-dialog .selectBoardContainer .body {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.select-board-dialog .head {
|
||||
margin: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
div.dialogContent.select-board-dialog > div.head .title {
|
||||
font-weight: 400;
|
||||
letter-spacing: .02em;
|
||||
font-size: 1.2em;
|
||||
color: var(--theia-arduino-branding-primary);
|
||||
margin-bottom: 10px;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.02em;
|
||||
font-size: 1.2em;
|
||||
color: var(--theia-arduino-branding-primary);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
div#select-board-dialog .selectBoardContainer .body .list .item.selected {
|
||||
background: var(--theia-secondaryButton-hoverBackground);
|
||||
background: var(--theia-secondaryButton-hoverBackground);
|
||||
}
|
||||
|
||||
div#select-board-dialog .selectBoardContainer .body .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 .body .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-editor-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-editor-foreground);
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .search input:focus {
|
||||
box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .container {
|
||||
flex: 1;
|
||||
padding: 0px 10px 0px 0px;
|
||||
min-width: 240px;
|
||||
max-width: 240px;
|
||||
flex: 1;
|
||||
padding: 0px 10px 0px 0px;
|
||||
min-width: 240px;
|
||||
max-width: 240px;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .left.container .content {
|
||||
margin: 0 5px 0 0;
|
||||
margin: 0 5px 0 0;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .right.container .content {
|
||||
margin: 0 0 0 5px;
|
||||
margin: 0 0 0 5px;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .container .content .title {
|
||||
color: #7f8c8d;
|
||||
padding: 0px 0px 10px 0px;
|
||||
text-transform: uppercase;
|
||||
color: #7f8c8d;
|
||||
padding: 0px 0px 10px 0px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .container .content .footer {
|
||||
padding: 10px 5px 10px 0px;
|
||||
padding: 10px 5px 10px 0px;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .container .content .loading {
|
||||
font-size: var(--theia-ui-font-size1);
|
||||
color: var(--theia-arduino-branding-secondary);
|
||||
padding: 10px 5px 10px 10px;
|
||||
text-transform: uppercase;
|
||||
/* The max, min-height comes from `.body .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-arduino-branding-secondary);
|
||||
padding: 10px 5px 10px 10px;
|
||||
text-transform: uppercase;
|
||||
/* The max, min-height comes from `.body .list` 200px + 47px top padding - 2 * 10px top padding */
|
||||
max-height: 227px;
|
||||
min-height: 227px;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .list .item {
|
||||
padding: 10px 5px 10px 10px;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
padding: 10px 5px 10px 10px;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .list .item .selected-icon {
|
||||
margin-left: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .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 .body .list .item.missing {
|
||||
opacity: var(--theia-mod-disabled-opacity);
|
||||
opacity: var(--theia-mod-disabled-opacity);
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .list .item:hover {
|
||||
background: var(--theia-secondaryButton-hoverBackground);
|
||||
background: var(--theia-secondaryButton-hoverBackground);
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .list .label {
|
||||
max-width: 215px;
|
||||
width: 215px;
|
||||
white-space: pre;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 215px;
|
||||
width: 215px;
|
||||
white-space: pre;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .list {
|
||||
max-height: 200px;
|
||||
min-height: 200px;
|
||||
overflow-y: auto;
|
||||
max-height: 200px;
|
||||
min-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .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 .body .search {
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 5px;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.p-Widget.dialogOverlay .dialogContent.select-board-dialog {
|
||||
width: 500px;
|
||||
width: 500px;
|
||||
}
|
||||
.arduino-boards-toolbar-item-container {
|
||||
margin-left: 3px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.arduino-boards-toolbar-item-container .arduino-boards-toolbar-item .inner-container {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
width: 100%;
|
||||
.arduino-boards-toolbar-item-container
|
||||
.arduino-boards-toolbar-item
|
||||
.inner-container {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.arduino-boards-toolbar-item-container .arduino-boards-toolbar-item .inner-container .notAttached {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
color: red;
|
||||
margin: 0 5px;
|
||||
.arduino-boards-toolbar-item-container
|
||||
.arduino-boards-toolbar-item
|
||||
.inner-container
|
||||
.notAttached {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
color: red;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.arduino-boards-toolbar-item-container .arduino-boards-toolbar-item .inner-container .guessed {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
color: var(--theia-warningBackground);
|
||||
margin: 0 5px;
|
||||
.arduino-boards-toolbar-item-container
|
||||
.arduino-boards-toolbar-item
|
||||
.inner-container
|
||||
.guessed {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
color: var(--theia-warningBackground);
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.arduino-boards-toolbar-item-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 220px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
.arduino-boards-toolbar-item .label {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 5px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 5px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.arduino-boards-toolbar-item .caret {
|
||||
width: 10px;
|
||||
margin-right: 5px;
|
||||
width: 10px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.arduino-boards-toolbar-item {
|
||||
background: var(--theia-tab-unfocusedActiveBackground);
|
||||
color: var(--theia-foreground);
|
||||
height: 22px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
margin: 0px 3px 0px 3px;
|
||||
border: 1px solid var(--theia-dropdown-border);
|
||||
background: var(--theia-tab-unfocusedActiveBackground);
|
||||
color: var(--theia-foreground);
|
||||
height: 22px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
margin: 0px 3px 0px 3px;
|
||||
border: 1px solid var(--theia-dropdown-border);
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-list {
|
||||
border: 3px solid var(--theia-activityBar-background);
|
||||
margin: -1px;
|
||||
z-index: 1;
|
||||
border: 1px solid var(--theia-dropdown-border);
|
||||
border: 3px solid var(--theia-activityBar-background);
|
||||
margin: -1px;
|
||||
z-index: 1;
|
||||
border: 1px solid var(--theia-dropdown-border);
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item {
|
||||
font-size: var(--theia-ui-font-size1);
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
color: var(--theia-foreground);
|
||||
background: var(--theia-tab-unfocusedActiveBackground);
|
||||
border: 1px solid var(--theia-tab-unfocusedActiveBackground);
|
||||
font-size: var(--theia-ui-font-size1);
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
color: var(--theia-foreground);
|
||||
background: var(--theia-tab-unfocusedActiveBackground);
|
||||
border: 1px solid var(--theia-tab-unfocusedActiveBackground);
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item .fa-check {
|
||||
color: var(--theia-arduino-branding-primary);
|
||||
align-self: center;
|
||||
color: var(--theia-arduino-branding-primary);
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item.selected,
|
||||
.arduino-boards-dropdown-item:hover {
|
||||
border: 1px solid var(--theia-focusBorder);
|
||||
border: 1px solid var(--theia-focusBorder);
|
||||
}
|
||||
|
||||
.arduino-board-dropdown-footer {
|
||||
color: var(--theia-arduino-branding-primary);
|
||||
border-top: 1px solid var(--theia-dropdown-border);
|
||||
}
|
||||
|
@@ -0,0 +1,74 @@
|
||||
.certificate-uploader-dialog {
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.certificate-uploader-dialog .theia-select {
|
||||
border: none !important;
|
||||
}
|
||||
.certificate-uploader-dialog .arduino-select__control {
|
||||
height: 31px;
|
||||
background: var(--theia-menubar-selectionBackground) !important;
|
||||
}
|
||||
|
||||
.certificate-uploader-dialog .dialogRow > button{
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.certificate-uploader-dialog .certificate-list {
|
||||
border: 1px solid #BDC7C7;
|
||||
border-radius: 2px;;
|
||||
background: var(--theia-menubar-selectionBackground) !important;
|
||||
overflow: auto;
|
||||
height: 120px;
|
||||
flex: 1;
|
||||
}
|
||||
.certificate-uploader-dialog .certificate-list .certificate-row {
|
||||
display: flex;
|
||||
padding: 6px 10px 5px 10px
|
||||
}
|
||||
.certificate-uploader-dialog .certificate-list .certificate-row:hover {
|
||||
background-color: var(--theia-list-activeSelectionBackground);
|
||||
}
|
||||
|
||||
.certificate-uploader-dialog .upload-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
.certificate-uploader-dialog .success {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #1DA086;
|
||||
}
|
||||
|
||||
.certificate-uploader-dialog .warn {
|
||||
color: #C11F09;
|
||||
}
|
||||
|
||||
.certificate-uploader-dialog .status-icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.certificate-uploader-dialog .add-cert-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.certificate-uploader-dialog .add-cert-btn .caret {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.certificate-add {
|
||||
padding: 16px;
|
||||
background-color: var(--theia-list-hoverBackground);
|
||||
border-radius: 3px;
|
||||
border: 1px solid #BDC7C7;
|
||||
}
|
||||
|
||||
.certificate-add input {
|
||||
margin-top: 12px;
|
||||
padding: 0 12px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
.codicon-debug-alt:before {
|
||||
font-family: 'FontAwesome' !important;
|
||||
content: "\e905" !important
|
||||
}
|
65
arduino-ide-extension/src/browser/style/dialogs.css
Normal file
65
arduino-ide-extension/src/browser/style/dialogs.css
Normal file
@@ -0,0 +1,65 @@
|
||||
.p-Widget.dialogOverlay .dialogBlock {
|
||||
border-radius: 3px;
|
||||
padding: 0 28px;
|
||||
box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.25);
|
||||
min-height: 0px;
|
||||
}
|
||||
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogTitle {
|
||||
padding: 36px 0 28px;
|
||||
font-weight: 500;
|
||||
background-color: transparent;
|
||||
font-size: var(--theia-ui-font-size2);
|
||||
color: var(--theia-list-inactiveSelectionForeground);
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogControl {
|
||||
padding: 0 0 36px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogContent {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogContent > div {
|
||||
padding: 0 0 12px;
|
||||
}
|
||||
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogContent .dialogSection {
|
||||
margin-top: 28px;
|
||||
}
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogContent .dialogSection:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogContent .dialogSection .dialogRow {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.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 {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.fl1{
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.status-icon {
|
||||
margin-right: 6px;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.fa.disabled {
|
||||
opacity: .4;
|
||||
}
|
@@ -4,3 +4,7 @@
|
||||
background-size: 13px;
|
||||
background-image: var(--theia-icon-circle);
|
||||
}
|
||||
|
||||
.monaco-list-row.show-file-icons.focused {
|
||||
background-color: #d6ebff;
|
||||
}
|
||||
|
@@ -0,0 +1,32 @@
|
||||
.firmware-uploader-dialog {
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.firmware-uploader-dialog .theia-select {
|
||||
border: none !important;
|
||||
}
|
||||
.firmware-uploader-dialog .arduino-select__control {
|
||||
height: 31px;
|
||||
background: var(--theia-menubar-selectionBackground) !important;
|
||||
}
|
||||
|
||||
.firmware-uploader-dialog .dialogRow > button{
|
||||
width: 33%;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.firmware-uploader-dialog #firmware-select {
|
||||
flex: unset;
|
||||
}
|
||||
|
||||
.firmware-uploader-dialog .success {
|
||||
color: #1DA086;
|
||||
}
|
||||
|
||||
.firmware-uploader-dialog .warn {
|
||||
color: #C11F09;
|
||||
}
|
||||
|
||||
.firmware-uploader-dialog .status-icon {
|
||||
margin-right: 10px;
|
||||
}
|
711
arduino-ide-extension/src/browser/style/fonts.css
Normal file
711
arduino-ide-extension/src/browser/style/fonts.css
Normal file
@@ -0,0 +1,711 @@
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
src:
|
||||
url('fonts/FontAwesome.ttf?nuchcq') format('truetype'),
|
||||
url('fonts/FontAwesome.woff?nuchcq') format('woff'),
|
||||
url('fonts/FontAwesome.svg?nuchcq#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;
|
||||
|
||||
/* Better Font Rendering =========== */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.fa-arduino-debugger:before {
|
||||
content: "\e905";
|
||||
}
|
||||
|
||||
.fa-arduino-search:before {
|
||||
content: "\e901";
|
||||
}
|
||||
.fa-arduino-boards:before {
|
||||
content: "\e902";
|
||||
}
|
||||
.fa-arduino-library:before {
|
||||
content: "\e903";
|
||||
}
|
||||
.fa-arduino-folder:before {
|
||||
content: "\e904";
|
||||
}
|
||||
.fa-reload:before {
|
||||
content: "\e900";
|
||||
}
|
||||
.fa-asterisk:before {
|
||||
content: "\f069";
|
||||
}
|
||||
.fa-plus:before {
|
||||
content: "\f067";
|
||||
}
|
||||
.fa-question:before {
|
||||
content: "\f128";
|
||||
}
|
||||
.fa-minus:before {
|
||||
content: "\f068";
|
||||
}
|
||||
.fa-music:before {
|
||||
content: "\f001";
|
||||
}
|
||||
.fa-search:before {
|
||||
content: "\f002";
|
||||
}
|
||||
.fa-envelope-o:before {
|
||||
content: "\f003";
|
||||
}
|
||||
.fa-heart:before {
|
||||
content: "\f004";
|
||||
}
|
||||
.fa-star:before {
|
||||
content: "\f005";
|
||||
}
|
||||
.fa-star-o:before {
|
||||
content: "\f006";
|
||||
}
|
||||
.fa-user:before {
|
||||
content: "\f007";
|
||||
}
|
||||
.fa-film:before {
|
||||
content: "\f008";
|
||||
}
|
||||
.fa-th-large:before {
|
||||
content: "\f009";
|
||||
}
|
||||
.fa-th:before {
|
||||
content: "\f00a";
|
||||
}
|
||||
.fa-th-list:before {
|
||||
content: "\f00b";
|
||||
}
|
||||
.fa-check:before {
|
||||
content: "\f00c";
|
||||
}
|
||||
.fa-close:before {
|
||||
content: "\f00d";
|
||||
}
|
||||
.fa-remove:before {
|
||||
content: "\f00d";
|
||||
}
|
||||
.fa-times:before {
|
||||
content: "\f00d";
|
||||
}
|
||||
.fa-search-plus:before {
|
||||
content: "\f00e";
|
||||
}
|
||||
.fa-search-minus:before {
|
||||
content: "\f010";
|
||||
}
|
||||
.fa-power-off:before {
|
||||
content: "\f011";
|
||||
}
|
||||
.fa-signal:before {
|
||||
content: "\f012";
|
||||
}
|
||||
.fa-cog:before {
|
||||
content: "\f013";
|
||||
}
|
||||
.fa-gear:before {
|
||||
content: "\f013";
|
||||
}
|
||||
.fa-trash-o:before {
|
||||
content: "\f014";
|
||||
}
|
||||
.fa-home:before {
|
||||
content: "\f015";
|
||||
}
|
||||
.fa-file-o:before {
|
||||
content: "\f016";
|
||||
}
|
||||
.fa-clock-o:before {
|
||||
content: "\f017";
|
||||
}
|
||||
.fa-road:before {
|
||||
content: "\f018";
|
||||
}
|
||||
.fa-download:before {
|
||||
content: "\f019";
|
||||
}
|
||||
.fa-arrow-circle-o-down:before {
|
||||
content: "\f01a";
|
||||
}
|
||||
.fa-arrow-circle-o-up:before {
|
||||
content: "\f01b";
|
||||
}
|
||||
.fa-inbox:before {
|
||||
content: "\f01c";
|
||||
}
|
||||
.fa-play-circle-o:before {
|
||||
content: "\f01d";
|
||||
}
|
||||
.fa-repeat:before {
|
||||
content: "\f01e";
|
||||
}
|
||||
.fa-rotate-right:before {
|
||||
content: "\f01e";
|
||||
}
|
||||
.fa-refresh:before {
|
||||
content: "\f021";
|
||||
}
|
||||
.fa-list-alt:before {
|
||||
content: "\f022";
|
||||
}
|
||||
.fa-lock:before {
|
||||
content: "\f023";
|
||||
}
|
||||
.fa-flag:before {
|
||||
content: "\f024";
|
||||
}
|
||||
.fa-volume-off:before {
|
||||
content: "\f026";
|
||||
}
|
||||
.fa-volume-down:before {
|
||||
content: "\f027";
|
||||
}
|
||||
.fa-volume-up:before {
|
||||
content: "\f028";
|
||||
}
|
||||
.fa-qrcode:before {
|
||||
content: "\f029";
|
||||
}
|
||||
.fa-tag:before {
|
||||
content: "\f02b";
|
||||
}
|
||||
.fa-tags:before {
|
||||
content: "\f02c";
|
||||
}
|
||||
.fa-book:before {
|
||||
content: "\f02d";
|
||||
}
|
||||
.fa-bookmark:before {
|
||||
content: "\f02e";
|
||||
}
|
||||
.fa-print:before {
|
||||
content: "\f02f";
|
||||
}
|
||||
.fa-text-height:before {
|
||||
content: "\f034";
|
||||
}
|
||||
.fa-text-width:before {
|
||||
content: "\f035";
|
||||
}
|
||||
.fa-align-left:before {
|
||||
content: "\f036";
|
||||
}
|
||||
.fa-align-center:before {
|
||||
content: "\f037";
|
||||
}
|
||||
.fa-align-right:before {
|
||||
content: "\f038";
|
||||
}
|
||||
.fa-align-justify:before {
|
||||
content: "\f039";
|
||||
}
|
||||
.fa-list:before {
|
||||
content: "\f03a";
|
||||
}
|
||||
.fa-dedent:before {
|
||||
content: "\f03b";
|
||||
}
|
||||
.fa-outdent:before {
|
||||
content: "\f03b";
|
||||
}
|
||||
.fa-indent:before {
|
||||
content: "\f03c";
|
||||
}
|
||||
.fa-video-camera:before {
|
||||
content: "\f03d";
|
||||
}
|
||||
.fa-pencil:before {
|
||||
content: "\f040";
|
||||
}
|
||||
.fa-map-marker:before {
|
||||
content: "\f041";
|
||||
}
|
||||
.fa-adjust:before {
|
||||
content: "\f042";
|
||||
}
|
||||
.fa-tint:before {
|
||||
content: "\f043";
|
||||
}
|
||||
.fa-edit:before {
|
||||
content: "\f044";
|
||||
}
|
||||
.fa-pencil-square-o:before {
|
||||
content: "\f044";
|
||||
}
|
||||
.fa-share-square-o:before {
|
||||
content: "\f045";
|
||||
}
|
||||
.fa-check-square-o:before {
|
||||
content: "\f046";
|
||||
}
|
||||
.fa-arrows:before {
|
||||
content: "\f047";
|
||||
}
|
||||
.fa-step-backward:before {
|
||||
content: "\f048";
|
||||
}
|
||||
.fa-fast-backward:before {
|
||||
content: "\f049";
|
||||
}
|
||||
.fa-backward:before {
|
||||
content: "\f04a";
|
||||
}
|
||||
.fa-play:before {
|
||||
content: "\f04b";
|
||||
}
|
||||
.fa-pause:before {
|
||||
content: "\f04c";
|
||||
}
|
||||
.fa-stop:before {
|
||||
content: "\f04d";
|
||||
}
|
||||
.fa-forward:before {
|
||||
content: "\f04e";
|
||||
}
|
||||
.fa-fast-forward:before {
|
||||
content: "\f050";
|
||||
}
|
||||
.fa-step-forward:before {
|
||||
content: "\f051";
|
||||
}
|
||||
.fa-eject:before {
|
||||
content: "\f052";
|
||||
}
|
||||
.fa-chevron-left:before {
|
||||
content: "\f053";
|
||||
}
|
||||
.fa-chevron-right:before {
|
||||
content: "\f054";
|
||||
}
|
||||
.fa-plus-circle:before {
|
||||
content: "\f055";
|
||||
}
|
||||
.fa-minus-circle:before {
|
||||
content: "\f056";
|
||||
}
|
||||
.fa-times-circle:before {
|
||||
content: "\f057";
|
||||
}
|
||||
.fa-check-circle:before {
|
||||
content: "\f058";
|
||||
}
|
||||
.fa-question-circle:before {
|
||||
content: "\f059";
|
||||
}
|
||||
.fa-info-circle:before {
|
||||
content: "\f05a";
|
||||
}
|
||||
.fa-crosshairs:before {
|
||||
content: "\f05b";
|
||||
}
|
||||
.fa-times-circle-o:before {
|
||||
content: "\f05c";
|
||||
}
|
||||
.fa-check-circle-o:before {
|
||||
content: "\f05d";
|
||||
}
|
||||
.fa-ban:before {
|
||||
content: "\f05e";
|
||||
}
|
||||
.fa-arrow-left:before {
|
||||
content: "\f060";
|
||||
}
|
||||
.fa-arrow-right:before {
|
||||
content: "\f061";
|
||||
}
|
||||
.fa-arrow-up:before {
|
||||
content: "\f062";
|
||||
}
|
||||
.fa-arrow-down:before {
|
||||
content: "\f063";
|
||||
}
|
||||
.fa-mail-forward:before {
|
||||
content: "\f064";
|
||||
}
|
||||
.fa-share:before {
|
||||
content: "\f064";
|
||||
}
|
||||
.fa-expand:before {
|
||||
content: "\f065";
|
||||
}
|
||||
.fa-compress:before {
|
||||
content: "\f066";
|
||||
}
|
||||
.fa-exclamation-circle:before {
|
||||
content: "\f06a";
|
||||
}
|
||||
.fa-eye:before {
|
||||
content: "\f06e";
|
||||
}
|
||||
.fa-eye-slash:before {
|
||||
content: "\f070";
|
||||
}
|
||||
.fa-exclamation-triangle:before {
|
||||
content: "\f071";
|
||||
}
|
||||
.fa-warning:before {
|
||||
content: "\f071";
|
||||
}
|
||||
.fa-calendar:before {
|
||||
content: "\f073";
|
||||
}
|
||||
.fa-random:before {
|
||||
content: "\f074";
|
||||
}
|
||||
.fa-comment:before {
|
||||
content: "\f075";
|
||||
}
|
||||
.fa-chevron-up:before {
|
||||
content: "\f077";
|
||||
}
|
||||
.fa-chevron-down:before {
|
||||
content: "\f078";
|
||||
}
|
||||
.fa-retweet:before {
|
||||
content: "\f079";
|
||||
}
|
||||
.fa-folder:before {
|
||||
content: "\f07b";
|
||||
}
|
||||
.fa-folder-open:before {
|
||||
content: "\f07c";
|
||||
}
|
||||
.fa-arrows-v:before {
|
||||
content: "\f07d";
|
||||
}
|
||||
.fa-arrows-h:before {
|
||||
content: "\f07e";
|
||||
}
|
||||
.fa-bar-chart:before {
|
||||
content: "\f080";
|
||||
}
|
||||
.fa-bar-chart-o:before {
|
||||
content: "\f080";
|
||||
}
|
||||
.fa-cogs:before {
|
||||
content: "\f085";
|
||||
}
|
||||
.fa-gears:before {
|
||||
content: "\f085";
|
||||
}
|
||||
.fa-thumbs-o-up:before {
|
||||
content: "\f087";
|
||||
}
|
||||
.fa-thumbs-o-down:before {
|
||||
content: "\f088";
|
||||
}
|
||||
.fa-star-half:before {
|
||||
content: "\f089";
|
||||
}
|
||||
.fa-heart-o:before {
|
||||
content: "\f08a";
|
||||
}
|
||||
.fa-sign-out:before {
|
||||
content: "\f08b";
|
||||
}
|
||||
.fa-thumb-tack:before {
|
||||
content: "\f08d";
|
||||
}
|
||||
.fa-external-link:before {
|
||||
content: "\f08e";
|
||||
}
|
||||
.fa-sign-in:before {
|
||||
content: "\f090";
|
||||
}
|
||||
.fa-upload:before {
|
||||
content: "\f093";
|
||||
}
|
||||
.fa-square-o:before {
|
||||
content: "\f096";
|
||||
}
|
||||
.fa-bookmark-o:before {
|
||||
content: "\f097";
|
||||
}
|
||||
.fa-hdd-o:before {
|
||||
content: "\f0a0";
|
||||
}
|
||||
.fa-bell-o:before {
|
||||
content: "\f0a2";
|
||||
}
|
||||
.fa-certificate:before {
|
||||
content: "\f0a3";
|
||||
}
|
||||
.fa-arrow-circle-left:before {
|
||||
content: "\f0a8";
|
||||
}
|
||||
.fa-arrow-circle-right:before {
|
||||
content: "\f0a9";
|
||||
}
|
||||
.fa-arrow-circle-up:before {
|
||||
content: "\f0aa";
|
||||
}
|
||||
.fa-arrow-circle-down:before {
|
||||
content: "\f0ab";
|
||||
}
|
||||
.fa-globe:before {
|
||||
content: "\f0ac";
|
||||
}
|
||||
.fa-wrench:before {
|
||||
content: "\f0ad";
|
||||
}
|
||||
.fa-tasks:before {
|
||||
content: "\f0ae";
|
||||
}
|
||||
.fa-filter:before {
|
||||
content: "\f0b0";
|
||||
}
|
||||
.fa-briefcase:before {
|
||||
content: "\f0b1";
|
||||
}
|
||||
.fa-arrows-alt:before {
|
||||
content: "\f0b2";
|
||||
}
|
||||
.fa-cloud:before {
|
||||
content: "\f0c2";
|
||||
}
|
||||
.fa-copy:before {
|
||||
content: "\f0c5";
|
||||
}
|
||||
.fa-files-o:before {
|
||||
content: "\f0c5";
|
||||
}
|
||||
.fa-floppy-o:before {
|
||||
content: "\f0c7";
|
||||
}
|
||||
.fa-save:before {
|
||||
content: "\f0c7";
|
||||
}
|
||||
.fa-square:before {
|
||||
content: "\f0c8";
|
||||
}
|
||||
.fa-bars:before {
|
||||
content: "\f0c9";
|
||||
}
|
||||
.fa-navicon:before {
|
||||
content: "\f0c9";
|
||||
}
|
||||
.fa-reorder:before {
|
||||
content: "\f0c9";
|
||||
}
|
||||
.fa-list-ul:before {
|
||||
content: "\f0ca";
|
||||
}
|
||||
.fa-list-ol:before {
|
||||
content: "\f0cb";
|
||||
}
|
||||
.fa-table:before {
|
||||
content: "\f0ce";
|
||||
}
|
||||
.fa-caret-down:before {
|
||||
content: "\f0d7";
|
||||
}
|
||||
.fa-caret-up:before {
|
||||
content: "\f0d8";
|
||||
}
|
||||
.fa-caret-left:before {
|
||||
content: "\f0d9";
|
||||
}
|
||||
.fa-caret-right:before {
|
||||
content: "\f0da";
|
||||
}
|
||||
.fa-columns:before {
|
||||
content: "\f0db";
|
||||
}
|
||||
.fa-sort:before {
|
||||
content: "\f0dc";
|
||||
}
|
||||
.fa-unsorted:before {
|
||||
content: "\f0dc";
|
||||
}
|
||||
.fa-sort-desc:before {
|
||||
content: "\f0dd";
|
||||
}
|
||||
.fa-sort-down:before {
|
||||
content: "\f0dd";
|
||||
}
|
||||
.fa-sort-asc:before {
|
||||
content: "\f0de";
|
||||
}
|
||||
.fa-sort-up:before {
|
||||
content: "\f0de";
|
||||
}
|
||||
.fa-rotate-left:before {
|
||||
content: "\f0e2";
|
||||
}
|
||||
.fa-undo:before {
|
||||
content: "\f0e2";
|
||||
}
|
||||
.fa-file-text-o:before {
|
||||
content: "\f0f6";
|
||||
}
|
||||
.fa-plus-square:before {
|
||||
content: "\f0fe";
|
||||
}
|
||||
.fa-angle-double-left:before {
|
||||
content: "\f100";
|
||||
}
|
||||
.fa-angle-double-right:before {
|
||||
content: "\f101";
|
||||
}
|
||||
.fa-angle-double-up:before {
|
||||
content: "\f102";
|
||||
}
|
||||
.fa-angle-double-down:before {
|
||||
content: "\f103";
|
||||
}
|
||||
.fa-angle-left:before {
|
||||
content: "\f104";
|
||||
}
|
||||
.fa-angle-right:before {
|
||||
content: "\f105";
|
||||
}
|
||||
.fa-angle-up:before {
|
||||
content: "\f106";
|
||||
}
|
||||
.fa-angle-down:before {
|
||||
content: "\f107";
|
||||
}
|
||||
.fa-desktop:before {
|
||||
content: "\f108";
|
||||
}
|
||||
.fa-laptop:before {
|
||||
content: "\f109";
|
||||
}
|
||||
.fa-tablet:before {
|
||||
content: "\f10a";
|
||||
}
|
||||
.fa-mobile:before {
|
||||
content: "\f10b";
|
||||
}
|
||||
.fa-mobile-phone:before {
|
||||
content: "\f10b";
|
||||
}
|
||||
.fa-circle-o:before {
|
||||
content: "\f10c";
|
||||
}
|
||||
.fa-quote-left:before {
|
||||
content: "\f10d";
|
||||
}
|
||||
.fa-quote-right:before {
|
||||
content: "\f10e";
|
||||
}
|
||||
.fa-spinner:before {
|
||||
content: "\f110";
|
||||
}
|
||||
.fa-circle:before {
|
||||
content: "\f111";
|
||||
}
|
||||
.fa-mail-reply:before {
|
||||
content: "\f112";
|
||||
}
|
||||
.fa-reply:before {
|
||||
content: "\f112";
|
||||
}
|
||||
.fa-github-alt:before {
|
||||
content: "\f113";
|
||||
}
|
||||
.fa-folder-o:before {
|
||||
content: "\f114";
|
||||
}
|
||||
.fa-folder-open-o:before {
|
||||
content: "\f115";
|
||||
}
|
||||
.fa-gamepad:before {
|
||||
content: "\f11b";
|
||||
}
|
||||
.fa-keyboard-o:before {
|
||||
content: "\f11c";
|
||||
}
|
||||
.fa-flag-o:before {
|
||||
content: "\f11d";
|
||||
}
|
||||
.fa-flag-checkered:before {
|
||||
content: "\f11e";
|
||||
}
|
||||
.fa-terminal:before {
|
||||
content: "\f120";
|
||||
}
|
||||
.fa-code:before {
|
||||
content: "\f121";
|
||||
}
|
||||
.fa-mail-reply-all:before {
|
||||
content: "\f122";
|
||||
}
|
||||
.fa-reply-all:before {
|
||||
content: "\f122";
|
||||
}
|
||||
.fa-star-half-empty:before {
|
||||
content: "\f123";
|
||||
}
|
||||
.fa-star-half-full:before {
|
||||
content: "\f123";
|
||||
}
|
||||
.fa-star-half-o:before {
|
||||
content: "\f123";
|
||||
}
|
||||
.fa-crop:before {
|
||||
content: "\f125";
|
||||
}
|
||||
.fa-code-fork:before {
|
||||
content: "\f126";
|
||||
}
|
||||
.fa-chain-broken:before {
|
||||
content: "\f127";
|
||||
}
|
||||
.fa-unlink:before {
|
||||
content: "\f127";
|
||||
}
|
||||
.fa-info:before {
|
||||
content: "\f129";
|
||||
}
|
||||
.fa-exclamation:before {
|
||||
content: "\f12a";
|
||||
}
|
||||
.fa-rocket:before {
|
||||
content: "\f135";
|
||||
}
|
||||
.fa-maxcdn:before {
|
||||
content: "\f136";
|
||||
}
|
||||
.fa-chevron-circle-left:before {
|
||||
content: "\f137";
|
||||
}
|
||||
.fa-chevron-circle-right:before {
|
||||
content: "\f138";
|
||||
}
|
||||
.fa-chevron-circle-up:before {
|
||||
content: "\f139";
|
||||
}
|
||||
.fa-chevron-circle-down:before {
|
||||
content: "\f13a";
|
||||
}
|
||||
.fa-ellipsis-h:before {
|
||||
content: "\f141";
|
||||
}
|
||||
.fa-long-arrow-down:before {
|
||||
content: "\f175";
|
||||
}
|
||||
.fa-long-arrow-up:before {
|
||||
content: "\f176";
|
||||
}
|
||||
.fa-long-arrow-left:before {
|
||||
content: "\f177";
|
||||
}
|
||||
.fa-long-arrow-right:before {
|
||||
content: "\f178";
|
||||
}
|
||||
.fa-microchip:before {
|
||||
content: "\f2db";
|
||||
}
|
194
arduino-ide-extension/src/browser/style/fonts/FontAwesome.svg
Normal file
194
arduino-ide-extension/src/browser/style/fonts/FontAwesome.svg
Normal file
@@ -0,0 +1,194 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Generated by IcoMoon</metadata>
|
||||
<defs>
|
||||
<font id="FontAwesome" horiz-adv-x="1024">
|
||||
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
||||
<missing-glyph horiz-adv-x="1024" />
|
||||
<glyph unicode=" " horiz-adv-x="512" d="" />
|
||||
<glyph unicode="" glyph-name="reload" d="M512.083-9.079c118.817 0 232.769 47.2 316.781 131.213 84.019 84.019 131.219 197.969 131.219 316.785 0 8.487-3.373 16.627-9.376 22.628-5.997 6.001-14.138 9.373-22.624 9.373s-16.627-3.372-22.63-9.373c-5.997-6.001-9.37-14.141-9.37-22.627-0.019-87.81-30.131-172.959-85.318-241.26s-132.115-115.622-217.962-134.086c-85.848-18.458-175.428-6.931-253.811 32.646-78.383 39.584-140.833 104.832-176.941 184.87-36.108 80.045-43.693 170.045-21.49 255.001s72.852 159.737 143.505 211.878c70.653 52.141 157.042 78.492 244.768 74.662s171.487-37.612 237.33-95.712h-158.081c-8.487 0-16.626-3.372-22.627-9.373s-9.373-14.141-9.373-22.627c0-8.487 3.372-16.627 9.373-22.628s14.14-9.372 22.627-9.372h224.001c8.486 0 16.627 3.371 22.624 9.372 6.003 6.001 9.376 14.141 9.376 22.628v224c0 8.487-3.373 16.626-9.376 22.627-5.997 6.001-14.138 9.373-22.624 9.373s-16.627-3.371-22.63-9.373c-5.997-6.001-9.37-14.14-9.37-22.627v-136.96c-55.162 46.332-120.678 78.68-191.002 94.301s-143.375 14.052-212.963-4.571c-69.588-18.623-133.659-53.753-186.78-102.41s-93.725-109.405-118.369-177.096c-24.644-67.69-32.602-140.324-23.199-211.745 9.404-71.419 35.891-139.521 77.216-198.523 41.325-59.008 96.27-107.174 160.174-140.422s134.885-50.598 206.922-50.573v0z" />
|
||||
<glyph unicode="" glyph-name="arduino-search" d="M984.883 21.297l-272.696 272.696c63.208 77.401 94.257 176.128 86.728 275.773-7.533 99.645-53.065 192.585-127.19 259.604s-171.168 102.992-271.064 100.479c-99.897-2.512-195.009-43.317-265.67-113.978s-111.466-165.773-113.978-265.67c-2.512-99.898 33.46-196.942 100.479-271.062 67.019-74.124 159.959-119.661 259.604-127.19 99.643-7.533 198.375 23.516 275.771 86.724l272.696-272.696c3.624-3.651 7.929-6.549 12.68-8.526 4.746-1.977 9.835-2.996 14.982-2.996 5.142 0 10.231 1.020 14.982 2.996 4.746 1.977 9.056 4.875 12.675 8.526 3.651 3.624 6.549 7.934 8.526 12.68 1.981 4.746 2.996 9.839 2.996 14.982s-1.015 10.236-2.996 14.982c-1.977 4.746-4.875 9.056-8.526 12.675zM411.833 227.379c-61.639 0-121.894 18.276-173.145 52.522s-91.197 82.917-114.785 139.865c-23.588 56.948-29.76 119.61-17.735 180.065s41.707 115.986 85.293 159.571c43.585 43.585 99.117 73.268 159.571 85.293s123.12 5.854 180.063-17.735c56.948-23.588 105.623-63.533 139.865-114.784 34.246-51.251 52.527-111.506 52.527-173.145 0-82.655-32.835-161.926-91.283-220.374-58.448-58.444-137.715-91.278-220.371-91.278z" />
|
||||
<glyph unicode="" glyph-name="arduino-boards" horiz-adv-x="1178" d="M1107.2 931.657h-896c-11.881 0-23.277-4.72-31.678-13.122s-13.122-19.797-13.122-31.678v-268.8h-89.6c-11.881 0-23.277-4.72-31.678-13.122s-13.122-19.797-13.122-31.678v-268.8c0-11.884 4.72-23.276 13.122-31.677s19.797-13.123 31.678-13.123h89.6v-268.8c0-11.884 4.72-23.276 13.122-31.677s19.797-13.123 31.678-13.123h896c11.884 0 23.276 4.721 31.677 13.123s13.123 19.794 13.123 31.677v896c0 11.881-4.721 23.277-13.123 31.678s-19.794 13.122-31.677 13.122zM121.6 349.257v179.2h134.4v-179.2h-134.4zM1062.4 35.657h-89.6v44.8c0 11.884-4.721 23.276-13.123 31.677s-19.794 13.123-31.677 13.123c-11.884 0-23.276-4.721-31.677-13.123s-13.123-19.794-13.123-31.677v-44.8h-89.6v44.8c0 11.884-4.721 23.276-13.123 31.677s-19.794 13.123-31.677 13.123c-11.884 0-23.276-4.721-31.677-13.123s-13.123-19.794-13.123-31.677v-44.8h-89.6v44.8c0 11.884-4.721 23.276-13.123 31.677s-19.794 13.123-31.677 13.123c-11.884 0-23.276-4.721-31.677-13.123s-13.123-19.794-13.123-31.677v-44.8h-89.6v44.8c0 11.884-4.72 23.276-13.122 31.677s-19.797 13.123-31.678 13.123c-11.881 0-23.277-4.721-31.678-13.123s-13.122-19.794-13.122-31.677v-44.8h-89.6v224h44.8c11.881 0 23.277 4.721 31.678 13.123s13.122 19.794 13.122 31.677v268.8c0 11.881-4.72 23.277-13.122 31.678s-19.797 13.122-31.678 13.122h-44.8v224h89.6v-44.8c0-11.881 4.72-23.277 13.122-31.678s19.797-13.122 31.678-13.122c11.881 0 23.277 4.72 31.678 13.122s13.122 19.797 13.122 31.678v44.8h89.6v-44.8c0-11.881 4.721-23.277 13.123-31.678s19.794-13.122 31.677-13.122c11.884 0 23.276 4.72 31.677 13.122s13.123 19.797 13.123 31.678v44.8h89.6v-44.8c0-11.881 4.721-23.277 13.123-31.678s19.794-13.122 31.677-13.122c11.884 0 23.276 4.72 31.677 13.122s13.123 19.797 13.123 31.678v44.8h89.6v-44.8c0-11.881 4.721-23.277 13.123-31.678s19.794-13.122 31.677-13.122c11.884 0 23.276 4.72 31.677 13.122s13.123 19.797 13.123 31.678v44.8h89.6v-806.4zM928 618.057h-268.8c-11.884 0-23.276-4.72-31.677-13.122s-13.123-19.797-13.123-31.678v-268.8c0-11.884 4.721-23.276 13.123-31.677s19.794-13.123 31.677-13.123h268.8c11.884 0 23.276 4.721 31.677 13.123s13.123 19.794 13.123 31.677v268.8c0 11.881-4.721 23.277-13.123 31.678s-19.794 13.122-31.677 13.122zM883.2 349.257h-179.2v179.2h179.2v-179.2z" />
|
||||
<glyph unicode="" glyph-name="arduino-library" d="M1006.669 49.097l-164.271 625.707c-2.778 9.338-9.097 17.219-17.604 21.963-8.512 4.743-18.539 5.972-27.942 3.424l-135.518-33.973v220.64c0 9.901-3.934 19.397-10.935 26.399s-16.495 10.935-26.398 10.935h-149.333c-9.903 0-19.396-3.933-26.398-10.935s-10.935-16.497-10.935-26.399v-112h-149.333v74.667c0 9.901-3.933 19.397-10.935 26.399s-16.497 10.935-26.399 10.935h-186.667c-9.901 0-19.397-3.933-26.399-10.935s-10.935-16.497-10.935-26.399v-858.667c0-9.903 3.933-19.396 10.935-26.398s16.497-10.935 26.399-10.935h560c9.903 0 19.396 3.934 26.398 10.935s10.935 16.495 10.935 26.398v435.682l114.987-437.922c2.116-8.213 6.967-15.462 13.751-20.553 6.788-5.090 15.104-7.714 23.582-7.445 3.226-0.363 6.481-0.363 9.707 0l157.918 41.438c9.566 2.483 17.758 8.661 22.775 17.173 2.475 4.527 3.985 9.519 4.437 14.66 0.448 5.141-0.171 10.317-1.822 15.206zM213.333 28.19h-112v784h112v-784zM437.333 28.19h-149.333v672h149.333v-672zM586.667 28.19h-74.667v821.333h74.667v-821.333zM839.040 43.87l-145.225 553.28 85.867 22.773 145.225-554.4-85.867-21.653z" />
|
||||
<glyph unicode="" glyph-name="arduino-folder" horiz-adv-x="1252" d="M1173.333 737.523h-527.15l-184.175 184.675c-4.651 4.614-10.167 8.263-16.232 10.741s-12.559 3.733-19.11 3.695h-348.444c-13.202 0-25.863-5.244-35.198-14.58s-14.58-21.996-14.58-35.198v-895.999c0-13.204 5.244-25.862 14.58-35.197s21.997-14.581 35.198-14.581h1095.111c13.204 0 25.862 5.245 35.197 14.581s14.581 21.993 14.581 35.197v696.888c0 13.202-5.245 25.863-14.581 35.198s-21.993 14.58-35.197 14.58zM128 837.079h278.258l99.556-99.556h-377.813v99.556zM1123.556 40.635h-995.556v597.333h995.556v-597.333z" />
|
||||
<glyph unicode="" glyph-name="arduino-debugger" horiz-adv-x="1071" d="M634.224 287.761c-8.983-0.009-17.72 2.956-24.841 8.425-7.126 5.474-12.241 13.149-14.55 21.83s-1.685 17.883 1.778 26.168c3.463 8.29 9.57 15.202 17.366 19.661l304.621 173.966-488.052 278.888v-296.387c0-10.801-4.291-21.16-11.929-28.799s-17.997-11.929-28.799-11.929c-10.801 0-21.16 4.291-28.799 11.929s-11.929 17.997-11.929 28.799v366.545c-0.004 7.135 1.867 14.146 5.426 20.33s8.681 11.324 14.852 14.905c6.171 3.581 13.175 5.477 20.31 5.499s14.15-1.832 20.343-5.376l610.91-349.045c6.232-3.561 11.413-8.707 15.020-14.917 3.603-6.209 5.502-13.261 5.502-20.441s-1.899-14.232-5.502-20.441c-3.607-6.21-8.788-11.356-15.020-14.917l-366.545-209.326c-6.135-3.523-13.089-5.376-20.163-5.367zM512 153.766c10.803 0 21.16 4.291 28.798 11.93s11.93 17.994 11.93 28.798c0 10.803-4.291 21.16-11.93 28.798s-17.994 11.93-28.798 11.93h-43.176c-1.987 17.389-5.532 34.56-10.587 51.316l41.949 41.951c3.798 3.793 6.81 8.304 8.867 13.265 2.053 4.962 3.109 10.277 3.109 15.649s-1.057 10.687-3.109 15.649c-2.057 4.962-5.069 9.467-8.867 13.265s-8.304 6.81-13.265 8.867c-4.962 2.053-10.277 3.114-15.649 3.114s-10.688-1.061-15.65-3.114c-4.961-2.057-9.47-5.069-13.267-8.867l-21.585-21.583c-14.553 22.109-34.216 40.387-57.327 53.285-23.11 12.902-48.988 20.052-75.444 20.838-26.456-0.787-52.334-7.936-75.444-20.838s-42.774-31.181-57.327-53.29l-21.585 21.588c-7.669 7.671-18.070 11.976-28.915 11.976s-21.247-4.305-28.916-11.976c-7.669-7.666-11.978-18.069-11.978-28.914s4.308-21.248 11.977-28.914l41.949-41.951c-5.055-16.756-8.599-33.927-10.589-51.316h-43.171c-10.801 0-21.161-4.291-28.799-11.93s-11.929-17.994-11.929-28.798c0-10.803 4.291-21.16 11.929-28.798s17.997-11.93 28.799-11.93h43.173c1.991-17.389 5.534-34.56 10.589-51.316l-1.222-1.224-40.727-40.727c-7.631-7.685-11.913-18.078-11.913-28.914 0-10.831 4.282-21.225 11.913-28.914 7.72-7.568 18.102-11.813 28.915-11.813s21.194 4.245 28.915 11.813l21.585 21.588c14.553-22.109 34.216-40.387 57.327-53.29s48.989-20.052 75.445-20.838c26.456 0.787 52.334 7.936 75.444 20.838s42.774 31.181 57.327 53.29l21.585-21.588c7.72-7.573 18.102-11.813 28.916-11.813 10.813 0 21.192 4.24 28.914 11.813 7.629 7.689 11.911 18.083 11.911 28.914 0 10.836-4.282 21.229-11.911 28.914l-41.95 41.951c5.055 16.756 8.6 33.927 10.588 51.316h43.176zM288 357.402c37.058 0 69.644-32.991 87.564-81.455h-175.127c17.92 48.463 50.506 81.455 87.564 81.455zM288 31.584c-55.389 0-101.818 74.533-101.818 162.909h203.636c0-88.376-46.429-162.909-101.818-162.909z" />
|
||||
<glyph unicode="" glyph-name="music" horiz-adv-x="878" d="M877.714 822.857v-640c0-80.571-120.571-109.714-182.857-109.714s-182.857 29.143-182.857 109.714 120.571 109.714 182.857 109.714c37.714 0 75.429-6.857 109.714-22.286v306.857l-438.857-135.429v-405.143c0-80.571-120.571-109.714-182.857-109.714s-182.857 29.143-182.857 109.714 120.571 109.714 182.857 109.714c37.714 0 75.429-6.857 109.714-22.286v552.571c0 24 16 45.143 38.857 52.571l475.429 146.286c5.143 1.714 10.286 2.286 16 2.286 30.286 0 54.857-24.571 54.857-54.857z" />
|
||||
<glyph unicode="" glyph-name="search" horiz-adv-x="951" d="M658.286 475.428c0 141.143-114.857 256-256 256s-256-114.857-256-256 114.857-256 256-256 256 114.857 256 256zM950.857 0c0-40-33.143-73.143-73.143-73.143-19.429 0-38.286 8-51.429 21.714l-196 195.429c-66.857-46.286-146.857-70.857-228-70.857-222.286 0-402.286 180-402.286 402.286s180 402.286 402.286 402.286 402.286-180 402.286-402.286c0-81.143-24.571-161.143-70.857-228l196-196c13.143-13.143 21.143-32 21.143-51.429z" />
|
||||
<glyph unicode="" glyph-name="envelope-o" d="M950.857 91.428v438.857c-12-13.714-25.143-26.286-39.429-37.714-81.714-62.857-164-126.857-243.429-193.143-42.857-36-96-80-155.429-80h-1.143c-59.429 0-112.571 44-155.429 80-79.429 66.286-161.714 130.286-243.429 193.143-14.286 11.429-27.429 24-39.429 37.714v-438.857c0-9.714 8.571-18.286 18.286-18.286h841.143c9.714 0 18.286 8.571 18.286 18.286zM950.857 692c0 14.286 3.429 39.429-18.286 39.429h-841.143c-9.714 0-18.286-8.571-18.286-18.286 0-65.143 32.571-121.714 84-162.286 76.571-60 153.143-120.571 229.143-181.143 30.286-24.571 85.143-77.143 125.143-77.143h1.143c40 0 94.857 52.571 125.143 77.143 76 60.571 152.571 121.143 229.143 181.143 37.143 29.143 84 92.571 84 141.143zM1024 713.143v-621.714c0-50.286-41.143-91.429-91.429-91.429h-841.143c-50.286 0-91.429 41.143-91.429 91.429v621.714c0 50.286 41.143 91.429 91.429 91.429h841.143c50.286 0 91.429-41.143 91.429-91.429z" />
|
||||
<glyph unicode="" glyph-name="heart" d="M512 0c-9.143 0-18.286 3.429-25.143 10.286l-356.571 344c-4.571 4-130.286 118.857-130.286 256 0 167.429 102.286 267.429 273.143 267.429 100 0 193.714-78.857 238.857-123.429 45.143 44.571 138.857 123.429 238.857 123.429 170.857 0 273.143-100 273.143-267.429 0-137.143-125.714-252-130.857-257.143l-356-342.857c-6.857-6.857-16-10.286-25.143-10.286z" />
|
||||
<glyph unicode="" glyph-name="star" horiz-adv-x="951" d="M950.857 581.143c0-10.286-7.429-20-14.857-27.429l-207.429-202.286 49.143-285.714c0.571-4 0.571-7.429 0.571-11.429 0-14.857-6.857-28.571-23.429-28.571-8 0-16 2.857-22.857 6.857l-256.571 134.857-256.571-134.857c-7.429-4-14.857-6.857-22.857-6.857-16.571 0-24 13.714-24 28.571 0 4 0.571 7.429 1.143 11.429l49.143 285.714-208 202.286c-6.857 7.429-14.286 17.143-14.286 27.429 0 17.143 17.714 24 32 26.286l286.857 41.714 128.571 260c5.143 10.857 14.857 23.429 28 23.429s22.857-12.571 28-23.429l128.571-260 286.857-41.714c13.714-2.286 32-9.143 32-26.286z" />
|
||||
<glyph unicode="" glyph-name="star-o" horiz-adv-x="951" d="M649.714 377.143l174.857 169.714-241.143 35.429-108 218.286-108-218.286-241.143-35.429 174.857-169.714-41.714-240.571 216 113.714 215.429-113.714zM950.857 581.143c0-10.286-7.429-20-14.857-27.429l-207.429-202.286 49.143-285.714c0.571-4 0.571-7.429 0.571-11.429 0-15.429-6.857-28.571-23.429-28.571-8 0-16 2.857-22.857 6.857l-256.571 134.857-256.571-134.857c-7.429-4-14.857-6.857-22.857-6.857-16.571 0-24 13.714-24 28.571 0 4 0.571 7.429 1.143 11.429l49.143 285.714-208 202.286c-6.857 7.429-14.286 17.143-14.286 27.429 0 17.143 17.714 24 32 26.286l286.857 41.714 128.571 260c5.143 10.857 14.857 23.429 28 23.429s22.857-12.571 28-23.429l128.571-260 286.857-41.714c13.714-2.286 32-9.143 32-26.286z" />
|
||||
<glyph unicode="" glyph-name="user" horiz-adv-x="731" d="M731.429 151.428c0-83.429-54.857-151.429-121.714-151.429h-488c-66.857 0-121.714 68-121.714 151.429 0 150.286 37.143 324 186.857 324 46.286-45.143 109.143-73.143 178.857-73.143s132.571 28 178.857 73.143c149.714 0 186.857-173.714 186.857-324zM585.143 658.286c0-121.143-98.286-219.429-219.429-219.429s-219.429 98.286-219.429 219.429 98.286 219.429 219.429 219.429 219.429-98.286 219.429-219.429z" />
|
||||
<glyph unicode="" glyph-name="film" horiz-adv-x="1097" d="M219.429 36.571v73.143c0 20-16.571 36.571-36.571 36.571h-73.143c-20 0-36.571-16.571-36.571-36.571v-73.143c0-20 16.571-36.571 36.571-36.571h73.143c20 0 36.571 16.571 36.571 36.571zM219.429 256v73.143c0 20-16.571 36.571-36.571 36.571h-73.143c-20 0-36.571-16.571-36.571-36.571v-73.143c0-20 16.571-36.571 36.571-36.571h73.143c20 0 36.571 16.571 36.571 36.571zM219.429 475.428v73.143c0 20-16.571 36.571-36.571 36.571h-73.143c-20 0-36.571-16.571-36.571-36.571v-73.143c0-20 16.571-36.571 36.571-36.571h73.143c20 0 36.571 16.571 36.571 36.571zM804.571 36.571v292.571c0 20-16.571 36.571-36.571 36.571h-438.857c-20 0-36.571-16.571-36.571-36.571v-292.571c0-20 16.571-36.571 36.571-36.571h438.857c20 0 36.571 16.571 36.571 36.571zM219.429 694.857v73.143c0 20-16.571 36.571-36.571 36.571h-73.143c-20 0-36.571-16.571-36.571-36.571v-73.143c0-20 16.571-36.571 36.571-36.571h73.143c20 0 36.571 16.571 36.571 36.571zM1024 36.571v73.143c0 20-16.571 36.571-36.571 36.571h-73.143c-20 0-36.571-16.571-36.571-36.571v-73.143c0-20 16.571-36.571 36.571-36.571h73.143c20 0 36.571 16.571 36.571 36.571zM804.571 475.428v292.571c0 20-16.571 36.571-36.571 36.571h-438.857c-20 0-36.571-16.571-36.571-36.571v-292.571c0-20 16.571-36.571 36.571-36.571h438.857c20 0 36.571 16.571 36.571 36.571zM1024 256v73.143c0 20-16.571 36.571-36.571 36.571h-73.143c-20 0-36.571-16.571-36.571-36.571v-73.143c0-20 16.571-36.571 36.571-36.571h73.143c20 0 36.571 16.571 36.571 36.571zM1024 475.428v73.143c0 20-16.571 36.571-36.571 36.571h-73.143c-20 0-36.571-16.571-36.571-36.571v-73.143c0-20 16.571-36.571 36.571-36.571h73.143c20 0 36.571 16.571 36.571 36.571zM1024 694.857v73.143c0 20-16.571 36.571-36.571 36.571h-73.143c-20 0-36.571-16.571-36.571-36.571v-73.143c0-20 16.571-36.571 36.571-36.571h73.143c20 0 36.571 16.571 36.571 36.571zM1097.143 786.286v-768c0-50.286-41.143-91.429-91.429-91.429h-914.286c-50.286 0-91.429 41.143-91.429 91.429v768c0 50.286 41.143 91.429 91.429 91.429h914.286c50.286 0 91.429-41.143 91.429-91.429z" />
|
||||
<glyph unicode="" glyph-name="th-large" horiz-adv-x="951" d="M438.857 365.714v-219.429c0-40-33.143-73.143-73.143-73.143h-292.571c-40 0-73.143 33.143-73.143 73.143v219.429c0 40 33.143 73.143 73.143 73.143h292.571c40 0 73.143-33.143 73.143-73.143zM438.857 804.571v-219.429c0-40-33.143-73.143-73.143-73.143h-292.571c-40 0-73.143 33.143-73.143 73.143v219.429c0 40 33.143 73.143 73.143 73.143h292.571c40 0 73.143-33.143 73.143-73.143zM950.857 365.714v-219.429c0-40-33.143-73.143-73.143-73.143h-292.571c-40 0-73.143 33.143-73.143 73.143v219.429c0 40 33.143 73.143 73.143 73.143h292.571c40 0 73.143-33.143 73.143-73.143zM950.857 804.571v-219.429c0-40-33.143-73.143-73.143-73.143h-292.571c-40 0-73.143 33.143-73.143 73.143v219.429c0 40 33.143 73.143 73.143 73.143h292.571c40 0 73.143-33.143 73.143-73.143z" />
|
||||
<glyph unicode="" glyph-name="th" d="M292.571 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857z" />
|
||||
<glyph unicode="" glyph-name="th-list" d="M292.571 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-548.571c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h548.571c30.286 0 54.857-24.571 54.857-54.857zM292.571 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-548.571c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h548.571c30.286 0 54.857-24.571 54.857-54.857zM1024 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-548.571c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h548.571c30.286 0 54.857-24.571 54.857-54.857z" />
|
||||
<glyph unicode="" glyph-name="check" d="M954.857 627.428c0-14.286-5.714-28.571-16-38.857l-491.429-491.429c-10.286-10.286-24.571-16-38.857-16s-28.571 5.714-38.857 16l-284.571 284.571c-10.286 10.286-16 24.571-16 38.857s5.714 28.571 16 38.857l77.714 77.714c10.286 10.286 24.571 16 38.857 16s28.571-5.714 38.857-16l168-168.571 374.857 375.429c10.286 10.286 24.571 16 38.857 16s28.571-5.714 38.857-16l77.714-77.714c10.286-10.286 16-24.571 16-38.857z" />
|
||||
<glyph unicode="" glyph-name="close, remove, times" horiz-adv-x="805" d="M725.322 168.514c3.477-3.448 6.237-7.547 8.118-12.069 1.885-4.515 2.853-9.364 2.853-14.259s-0.967-9.744-2.853-14.266c-1.881-4.515-4.641-8.623-8.118-12.069-3.448-3.472-7.547-6.232-12.069-8.118-4.515-1.881-9.364-2.848-14.259-2.848s-9.744 0.967-14.266 2.848c-4.522 1.885-8.623 4.646-12.069 8.118l-270.371 270.375-270.372-270.375c-3.448-3.472-7.549-6.232-12.069-8.118-4.519-1.881-9.366-2.848-14.263-2.848s-9.744 0.967-14.263 2.848c-4.519 1.885-8.622 4.646-12.069 8.118-3.474 3.448-6.235 7.555-8.118 12.069-1.884 4.522-2.853 9.37-2.853 14.266s0.97 9.744 2.853 14.259c1.884 4.522 4.643 8.623 8.118 12.069l270.372 270.375-270.372 270.372c-3.456 3.456-6.201 7.565-8.072 12.082s-2.835 9.36-2.835 14.25c0 4.891 0.964 9.732 2.835 14.25s4.617 8.626 8.072 12.081c3.456 3.456 7.564 6.201 12.081 8.072s9.361 2.835 14.25 2.835c4.891 0 9.732-0.964 14.25-2.835s8.626-4.617 12.081-8.072l270.372-270.372 270.371 270.372c6.984 6.983 16.455 10.909 26.335 10.909 9.875 0 19.347-3.923 26.33-10.909s10.909-16.456 10.909-26.333c0-9.877-3.923-19.348-10.909-26.333l-270.371-270.372 270.371-270.375z" />
|
||||
<glyph unicode="" glyph-name="search-plus" horiz-adv-x="951" d="M585.143 493.714v-36.571c0-9.714-8.571-18.286-18.286-18.286h-128v-128c0-9.714-8.571-18.286-18.286-18.286h-36.571c-9.714 0-18.286 8.571-18.286 18.286v128h-128c-9.714 0-18.286 8.571-18.286 18.286v36.571c0 9.714 8.571 18.286 18.286 18.286h128v128c0 9.714 8.571 18.286 18.286 18.286h36.571c9.714 0 18.286-8.571 18.286-18.286v-128h128c9.714 0 18.286-8.571 18.286-18.286zM658.286 475.428c0 141.143-114.857 256-256 256s-256-114.857-256-256 114.857-256 256-256 256 114.857 256 256zM950.857 0c0-40.571-32.571-73.143-73.143-73.143-19.429 0-38.286 8-51.429 21.714l-196 195.429c-66.857-46.286-146.857-70.857-228-70.857-222.286 0-402.286 180-402.286 402.286s180 402.286 402.286 402.286 402.286-180 402.286-402.286c0-81.143-24.571-161.143-70.857-228l196-196c13.143-13.143 21.143-32 21.143-51.429z" />
|
||||
<glyph unicode="" glyph-name="search-minus" horiz-adv-x="951" d="M585.143 493.714v-36.571c0-9.714-8.571-18.286-18.286-18.286h-329.143c-9.714 0-18.286 8.571-18.286 18.286v36.571c0 9.714 8.571 18.286 18.286 18.286h329.143c9.714 0 18.286-8.571 18.286-18.286zM658.286 475.428c0 141.143-114.857 256-256 256s-256-114.857-256-256 114.857-256 256-256 256 114.857 256 256zM950.857 0c0-40.571-32.571-73.143-73.143-73.143-19.429 0-38.286 8-51.429 21.714l-196 195.429c-66.857-46.286-146.857-70.857-228-70.857-222.286 0-402.286 180-402.286 402.286s180 402.286 402.286 402.286 402.286-180 402.286-402.286c0-81.143-24.571-161.143-70.857-228l196-196c13.143-13.143 21.143-32 21.143-51.429z" />
|
||||
<glyph unicode="" glyph-name="power-off" horiz-adv-x="878" d="M877.714 438.857c0-241.714-197.143-438.857-438.857-438.857s-438.857 197.143-438.857 438.857c0 138.857 64 266.857 175.429 350.286 32.571 24.571 78.286 18.286 102.286-14.286 24.571-32 17.714-78.286-14.286-102.286-74.286-56-117.143-141.143-117.143-233.714 0-161.143 131.429-292.571 292.571-292.571s292.571 131.429 292.571 292.571c0 92.571-42.857 177.714-117.143 233.714-32 24-38.857 70.286-14.286 102.286 24 32.571 70.286 38.857 102.286 14.286 111.429-83.429 175.429-211.429 175.429-350.286zM512 877.714v-365.714c0-40-33.143-73.143-73.143-73.143s-73.143 33.143-73.143 73.143v365.714c0 40 33.143 73.143 73.143 73.143s73.143-33.143 73.143-73.143z" />
|
||||
<glyph unicode="" glyph-name="signal" d="M146.286 128v-109.714c0-10.286-8-18.286-18.286-18.286h-109.714c-10.286 0-18.286 8-18.286 18.286v109.714c0 10.286 8 18.286 18.286 18.286h109.714c10.286 0 18.286-8 18.286-18.286zM365.714 201.143v-182.857c0-10.286-8-18.286-18.286-18.286h-109.714c-10.286 0-18.286 8-18.286 18.286v182.857c0 10.286 8 18.286 18.286 18.286h109.714c10.286 0 18.286-8 18.286-18.286zM585.143 347.428v-329.143c0-10.286-8-18.286-18.286-18.286h-109.714c-10.286 0-18.286 8-18.286 18.286v329.143c0 10.286 8 18.286 18.286 18.286h109.714c10.286 0 18.286-8 18.286-18.286zM804.571 566.857v-548.571c0-10.286-8-18.286-18.286-18.286h-109.714c-10.286 0-18.286 8-18.286 18.286v548.571c0 10.286 8 18.286 18.286 18.286h109.714c10.286 0 18.286-8 18.286-18.286zM1024 859.428v-841.143c0-10.286-8-18.286-18.286-18.286h-109.714c-10.286 0-18.286 8-18.286 18.286v841.143c0 10.286 8 18.286 18.286 18.286h109.714c10.286 0 18.286-8 18.286-18.286z" />
|
||||
<glyph unicode="" glyph-name="cog, gear" horiz-adv-x="878" d="M585.143 438.857c0 80.571-65.714 146.286-146.286 146.286s-146.286-65.714-146.286-146.286 65.714-146.286 146.286-146.286 146.286 65.714 146.286 146.286zM877.714 501.143v-126.857c0-8.571-6.857-18.857-16-20.571l-105.714-16c-6.286-18.286-13.143-35.429-22.286-52 19.429-28 40-53.143 61.143-78.857 3.429-4 5.714-9.143 5.714-14.286s-1.714-9.143-5.143-13.143c-13.714-18.286-90.857-102.286-110.286-102.286-5.143 0-10.286 2.286-14.857 5.143l-78.857 61.714c-16.571-8.571-34.286-16-52-21.714-4-34.857-7.429-72-16.571-106.286-2.286-9.143-10.286-16-20.571-16h-126.857c-10.286 0-19.429 7.429-20.571 17.143l-16 105.143c-17.714 5.714-34.857 12.571-51.429 21.143l-80.571-61.143c-4-3.429-9.143-5.143-14.286-5.143s-10.286 2.286-14.286 6.286c-30.286 27.429-70.286 62.857-94.286 96-2.857 4-4 8.571-4 13.143 0 5.143 1.714 9.143 4.571 13.143 19.429 26.286 40.571 51.429 60 78.286-9.714 18.286-17.714 37.143-23.429 56.571l-104.571 15.429c-9.714 1.714-16.571 10.857-16.571 20.571v126.857c0 8.571 6.857 18.857 15.429 20.571l106.286 16c5.714 18.286 13.143 35.429 22.286 52.571-19.429 27.429-40 53.143-61.143 78.857-3.429 4-5.714 8.571-5.714 13.714s2.286 9.143 5.143 13.143c13.714 18.857 90.857 102.286 110.286 102.286 5.143 0 10.286-2.286 14.857-5.714l78.857-61.143c16.571 8.571 34.286 16 52 21.714 4 34.857 7.429 72 16.571 106.286 2.286 9.143 10.286 16 20.571 16h126.857c10.286 0 19.429-7.429 20.571-17.143l16-105.143c17.714-5.714 34.857-12.571 51.429-21.143l81.143 61.143c3.429 3.429 8.571 5.143 13.714 5.143s10.286-2.286 14.286-5.714c30.286-28 70.286-63.429 94.286-97.143 2.857-3.429 4-8 4-12.571 0-5.143-1.714-9.143-4.571-13.143-19.429-26.286-40.571-51.429-60-78.286 9.714-18.286 17.714-37.143 23.429-56l104.571-16c9.714-1.714 16.571-10.857 16.571-20.571z" />
|
||||
<glyph unicode="" glyph-name="trash-o" horiz-adv-x="805" d="M292.571 530.286v-329.143c0-10.286-8-18.286-18.286-18.286h-36.571c-10.286 0-18.286 8-18.286 18.286v329.143c0 10.286 8 18.286 18.286 18.286h36.571c10.286 0 18.286-8 18.286-18.286zM438.857 530.286v-329.143c0-10.286-8-18.286-18.286-18.286h-36.571c-10.286 0-18.286 8-18.286 18.286v329.143c0 10.286 8 18.286 18.286 18.286h36.571c10.286 0 18.286-8 18.286-18.286zM585.143 530.286v-329.143c0-10.286-8-18.286-18.286-18.286h-36.571c-10.286 0-18.286 8-18.286 18.286v329.143c0 10.286 8 18.286 18.286 18.286h36.571c10.286 0 18.286-8 18.286-18.286zM658.286 116.571v541.714h-512v-541.714c0-27.429 15.429-43.429 18.286-43.429h475.429c2.857 0 18.286 16 18.286 43.429zM274.286 731.428h256l-27.429 66.857c-1.714 2.286-6.857 5.714-9.714 6.286h-181.143c-3.429-0.571-8-4-9.714-6.286zM804.571 713.143v-36.571c0-10.286-8-18.286-18.286-18.286h-54.857v-541.714c0-62.857-41.143-116.571-91.429-116.571h-475.429c-50.286 0-91.429 51.429-91.429 114.286v544h-54.857c-10.286 0-18.286 8-18.286 18.286v36.571c0 10.286 8 18.286 18.286 18.286h176.571l40 95.429c11.429 28 45.714 50.857 76 50.857h182.857c30.286 0 64.571-22.857 76-50.857l40-95.429h176.571c10.286 0 18.286-8 18.286-18.286z" />
|
||||
<glyph unicode="" glyph-name="home" horiz-adv-x="951" d="M804.571 384v-274.286c0-20-16.571-36.571-36.571-36.571h-219.429v219.429h-146.286v-219.429h-219.429c-20 0-36.571 16.571-36.571 36.571v274.286c0 1.143 0.571 2.286 0.571 3.429l328.571 270.857 328.571-270.857c0.571-1.143 0.571-2.286 0.571-3.429zM932 423.428l-35.429-42.286c-2.857-3.429-7.429-5.714-12-6.286h-1.714c-4.571 0-8.571 1.143-12 4l-395.429 329.714-395.429-329.714c-4-2.857-8.571-4.571-13.714-4-4.571 0.571-9.143 2.857-12 6.286l-35.429 42.286c-6.286 7.429-5.143 19.429 2.286 25.714l410.857 342.286c24 20 62.857 20 86.857 0l139.429-116.571v111.429c0 10.286 8 18.286 18.286 18.286h109.714c10.286 0 18.286-8 18.286-18.286v-233.143l125.143-104c7.429-6.286 8.571-18.286 2.286-25.714z" />
|
||||
<glyph unicode="" glyph-name="file-o" horiz-adv-x="878" d="M838.857 733.714c21.143-21.143 38.857-63.429 38.857-93.714v-658.286c0-30.286-24.571-54.857-54.857-54.857h-768c-30.286 0-54.857 24.571-54.857 54.857v914.286c0 30.286 24.571 54.857 54.857 54.857h512c30.286 0 72.571-17.714 93.714-38.857zM585.143 873.143v-214.857h214.857c-3.429 9.714-8.571 19.429-12.571 23.429l-178.857 178.857c-4 4-13.714 9.143-23.429 12.571zM804.571 0v585.143h-237.714c-30.286 0-54.857 24.571-54.857 54.857v237.714h-438.857v-877.714h731.429z" />
|
||||
<glyph unicode="" glyph-name="clock-o" horiz-adv-x="878" d="M512 640v-256c0-10.286-8-18.286-18.286-18.286h-182.857c-10.286 0-18.286 8-18.286 18.286v36.571c0 10.286 8 18.286 18.286 18.286h128v201.143c0 10.286 8 18.286 18.286 18.286h36.571c10.286 0 18.286-8 18.286-18.286zM749.714 438.857c0 171.429-139.429 310.857-310.857 310.857s-310.857-139.429-310.857-310.857 139.429-310.857 310.857-310.857 310.857 139.429 310.857 310.857zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="download" horiz-adv-x="951" d="M731.429 182.857c0 20-16.571 36.571-36.571 36.571s-36.571-16.571-36.571-36.571 16.571-36.571 36.571-36.571 36.571 16.571 36.571 36.571zM877.714 182.857c0 20-16.571 36.571-36.571 36.571s-36.571-16.571-36.571-36.571 16.571-36.571 36.571-36.571 36.571 16.571 36.571 36.571zM950.857 310.857v-182.857c0-30.286-24.571-54.857-54.857-54.857h-841.143c-30.286 0-54.857 24.571-54.857 54.857v182.857c0 30.286 24.571 54.857 54.857 54.857h265.714l77.143-77.714c21.143-20.571 48.571-32 77.714-32s56.571 11.429 77.714 32l77.714 77.714h265.143c30.286 0 54.857-24.571 54.857-54.857zM765.143 636c5.714-13.714 2.857-29.714-8-40l-256-256c-6.857-7.429-16.571-10.857-25.714-10.857s-18.857 3.429-25.714 10.857l-256 256c-10.857 10.286-13.714 26.286-8 40 5.714 13.143 18.857 22.286 33.714 22.286h146.286v256c0 20 16.571 36.571 36.571 36.571h146.286c20 0 36.571-16.571 36.571-36.571v-256h146.286c14.857 0 28-9.143 33.714-22.286z" />
|
||||
<glyph unicode="" glyph-name="arrow-circle-o-down" horiz-adv-x="878" d="M640 420.571c0-5.143-2.286-9.714-5.714-13.714l-182.286-182.286c-4-3.429-8.571-5.143-13.143-5.143s-9.143 1.714-13.143 5.143l-182.857 182.857c-5.143 5.714-6.857 13.143-4 20s9.714 11.429 17.143 11.429h109.714v201.143c0 10.286 8 18.286 18.286 18.286h109.714c10.286 0 18.286-8 18.286-18.286v-201.143h109.714c10.286 0 18.286-8 18.286-18.286zM438.857 749.714c-171.429 0-310.857-139.429-310.857-310.857s139.429-310.857 310.857-310.857 310.857 139.429 310.857 310.857-139.429 310.857-310.857 310.857zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857v0c242.286 0 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="arrow-circle-o-up" horiz-adv-x="878" d="M638.857 450.286c-2.857-6.857-9.714-11.429-17.143-11.429h-109.714v-201.143c0-10.286-8-18.286-18.286-18.286h-109.714c-10.286 0-18.286 8-18.286 18.286v201.143h-109.714c-10.286 0-18.286 8-18.286 18.286 0 5.143 2.286 9.714 5.714 13.714l182.286 182.286c4 3.429 8.571 5.143 13.143 5.143s9.143-1.714 13.143-5.143l182.857-182.857c5.143-5.714 6.857-13.143 4-20zM438.857 749.714c-171.429 0-310.857-139.429-310.857-310.857s139.429-310.857 310.857-310.857 310.857 139.429 310.857 310.857-139.429 310.857-310.857 310.857zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857v0c242.286 0 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="inbox" horiz-adv-x="878" d="M584.571 402.286h180.571c-1.143 2.857-1.714 6.286-2.857 9.143l-121.143 283.429h-404.571l-121.143-283.429c-1.143-2.857-1.714-6.286-2.857-9.143h180.571l54.286-109.714h182.857zM877.714 385.143v-275.429c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v275.429c0 20.571 6.286 50.857 14.286 70.286l136 315.429c8 18.857 30.857 33.714 50.857 33.714h475.429c20 0 42.857-14.857 50.857-33.714l136-315.429c8-19.429 14.286-49.714 14.286-70.286z" />
|
||||
<glyph unicode="" glyph-name="play-circle-o" horiz-adv-x="878" d="M676.571 438.857c0-13.143-6.857-25.143-18.286-31.429l-310.857-182.857c-5.714-3.429-12-5.143-18.286-5.143s-12.571 1.714-18.286 4.571c-11.429 6.857-18.286 18.857-18.286 32v365.714c0 13.143 6.857 25.143 18.286 32 11.429 6.286 25.714 6.286 36.571-0.571l310.857-182.857c11.429-6.286 18.286-18.286 18.286-31.429zM749.714 438.857c0 171.429-139.429 310.857-310.857 310.857s-310.857-139.429-310.857-310.857 139.429-310.857 310.857-310.857 310.857 139.429 310.857 310.857zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="repeat, rotate-right" horiz-adv-x="878" d="M877.714 804.571v-256c0-20-16.571-36.571-36.571-36.571h-256c-14.857 0-28 9.143-33.714 22.857-5.714 13.143-2.857 29.143 8 39.429l78.857 78.857c-53.714 49.714-124.571 78.286-199.429 78.286-161.143 0-292.571-131.429-292.571-292.571s131.429-292.571 292.571-292.571c90.857 0 174.857 41.143 230.857 113.714 2.857 4 8 6.286 13.143 6.857 5.143 0 10.286-1.714 14.286-5.143l78.286-78.857c6.857-6.286 6.857-17.143 1.143-24.571-83.429-100.571-206.857-158.286-337.714-158.286-241.714 0-438.857 197.143-438.857 438.857s197.143 438.857 438.857 438.857c112.571 0 221.714-45.143 302.286-121.143l74.286 73.714c10.286 10.857 26.286 13.714 40 8 13.143-5.714 22.286-18.857 22.286-33.714z" />
|
||||
<glyph unicode="" glyph-name="refresh" horiz-adv-x="878" d="M863.429 347.428c0-1.143 0-2.857-0.571-4-48.571-202.286-215.429-343.429-426.286-343.429-111.429 0-219.429 44-300.571 121.143l-73.714-73.714c-6.857-6.857-16-10.857-25.714-10.857-20 0-36.571 16.571-36.571 36.571v256c0 20 16.571 36.571 36.571 36.571h256c20 0 36.571-16.571 36.571-36.571 0-9.714-4-18.857-10.857-25.714l-78.286-78.286c53.714-50.286 125.143-78.857 198.857-78.857 101.714 0 196 52.571 249.143 139.429 13.714 22.286 20.571 44 30.286 66.857 2.857 8 8.571 13.143 17.143 13.143h109.714c10.286 0 18.286-8.571 18.286-18.286zM877.714 804.571v-256c0-20-16.571-36.571-36.571-36.571h-256c-20 0-36.571 16.571-36.571 36.571 0 9.714 4 18.857 10.857 25.714l78.857 78.857c-54.286 50.286-125.714 78.286-199.429 78.286-101.714 0-196-52.571-249.143-139.429-13.714-22.286-20.571-44-30.286-66.857-2.857-8-8.571-13.143-17.143-13.143h-113.714c-10.286 0-18.286 8.571-18.286 18.286v4c49.143 202.857 217.714 343.429 428.571 343.429 112 0 221.143-44.571 302.286-121.143l74.286 73.714c6.857 6.857 16 10.857 25.714 10.857 20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="list-alt" d="M219.429 274.286v-36.571c0-9.714-8.571-18.286-18.286-18.286h-36.571c-9.714 0-18.286 8.571-18.286 18.286v36.571c0 9.714 8.571 18.286 18.286 18.286h36.571c9.714 0 18.286-8.571 18.286-18.286zM219.429 420.571v-36.571c0-9.714-8.571-18.286-18.286-18.286h-36.571c-9.714 0-18.286 8.571-18.286 18.286v36.571c0 9.714 8.571 18.286 18.286 18.286h36.571c9.714 0 18.286-8.571 18.286-18.286zM219.429 566.857v-36.571c0-9.714-8.571-18.286-18.286-18.286h-36.571c-9.714 0-18.286 8.571-18.286 18.286v36.571c0 9.714 8.571 18.286 18.286 18.286h36.571c9.714 0 18.286-8.571 18.286-18.286zM877.714 274.286v-36.571c0-9.714-8.571-18.286-18.286-18.286h-548.571c-9.714 0-18.286 8.571-18.286 18.286v36.571c0 9.714 8.571 18.286 18.286 18.286h548.571c9.714 0 18.286-8.571 18.286-18.286zM877.714 420.571v-36.571c0-9.714-8.571-18.286-18.286-18.286h-548.571c-9.714 0-18.286 8.571-18.286 18.286v36.571c0 9.714 8.571 18.286 18.286 18.286h548.571c9.714 0 18.286-8.571 18.286-18.286zM877.714 566.857v-36.571c0-9.714-8.571-18.286-18.286-18.286h-548.571c-9.714 0-18.286 8.571-18.286 18.286v36.571c0 9.714 8.571 18.286 18.286 18.286h548.571c9.714 0 18.286-8.571 18.286-18.286zM950.857 164.571v475.429c0 9.714-8.571 18.286-18.286 18.286h-841.143c-9.714 0-18.286-8.571-18.286-18.286v-475.429c0-9.714 8.571-18.286 18.286-18.286h841.143c9.714 0 18.286 8.571 18.286 18.286zM1024 786.286v-621.714c0-50.286-41.143-91.429-91.429-91.429h-841.143c-50.286 0-91.429 41.143-91.429 91.429v621.714c0 50.286 41.143 91.429 91.429 91.429h841.143c50.286 0 91.429-41.143 91.429-91.429z" />
|
||||
<glyph unicode="" glyph-name="lock" horiz-adv-x="658" d="M182.857 512h292.571v109.714c0 80.571-65.714 146.286-146.286 146.286s-146.286-65.714-146.286-146.286v-109.714zM658.286 457.143v-329.143c0-30.286-24.571-54.857-54.857-54.857h-548.571c-30.286 0-54.857 24.571-54.857 54.857v329.143c0 30.286 24.571 54.857 54.857 54.857h18.286v109.714c0 140.571 115.429 256 256 256s256-115.429 256-256v-109.714h18.286c30.286 0 54.857-24.571 54.857-54.857z" />
|
||||
<glyph unicode="" glyph-name="volume-off" horiz-adv-x="439" d="M438.857 749.714v-621.714c0-20-16.571-36.571-36.571-36.571-9.714 0-18.857 4-25.714 10.857l-190.286 190.286h-149.714c-20 0-36.571 16.571-36.571 36.571v219.429c0 20 16.571 36.571 36.571 36.571h149.714l190.286 190.286c6.857 6.857 16 10.857 25.714 10.857 20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="volume-down" horiz-adv-x="658" d="M438.857 749.714v-621.714c0-20-16.571-36.571-36.571-36.571-9.714 0-18.857 4-25.714 10.857l-190.286 190.286h-149.714c-20 0-36.571 16.571-36.571 36.571v219.429c0 20 16.571 36.571 36.571 36.571h149.714l190.286 190.286c6.857 6.857 16 10.857 25.714 10.857 20 0 36.571-16.571 36.571-36.571zM658.286 438.857c0-57.143-34.857-112.571-88.571-134.286-4.571-2.286-9.714-2.857-14.286-2.857-20 0-36.571 16-36.571 36.571 0 43.429 66.286 31.429 66.286 100.571s-66.286 57.143-66.286 100.571c0 20.571 16.571 36.571 36.571 36.571 4.571 0 9.714-0.571 14.286-2.857 53.714-21.143 88.571-77.143 88.571-134.286z" />
|
||||
<glyph unicode="" glyph-name="volume-up" horiz-adv-x="951" d="M438.857 749.714v-621.714c0-20-16.571-36.571-36.571-36.571-9.714 0-18.857 4-25.714 10.857l-190.286 190.286h-149.714c-20 0-36.571 16.571-36.571 36.571v219.429c0 20 16.571 36.571 36.571 36.571h149.714l190.286 190.286c6.857 6.857 16 10.857 25.714 10.857 20 0 36.571-16.571 36.571-36.571zM658.286 438.857c0-57.143-34.857-112.571-88.571-134.286-4.571-2.286-9.714-2.857-14.286-2.857-20 0-36.571 16-36.571 36.571 0 43.429 66.286 31.429 66.286 100.571s-66.286 57.143-66.286 100.571c0 20.571 16.571 36.571 36.571 36.571 4.571 0 9.714-0.571 14.286-2.857 53.714-21.143 88.571-77.143 88.571-134.286zM804.571 438.857c0-116-69.714-224-177.143-269.143-4.571-1.714-9.714-2.857-14.286-2.857-20.571 0-37.143 16.571-37.143 36.571 0 16 9.143 26.857 22.286 33.714 15.429 8 29.714 14.857 43.429 25.143 56.571 41.143 89.714 106.857 89.714 176.571s-33.143 135.429-89.714 176.571c-13.714 10.286-28 17.143-43.429 25.143-13.143 6.857-22.286 17.714-22.286 33.714 0 20 16.571 36.571 36.571 36.571 5.143 0 10.286-1.143 14.857-2.857 107.429-45.143 177.143-153.143 177.143-269.143zM950.857 438.857c0-175.429-104.571-334.286-265.714-403.429-4.571-1.714-9.714-2.857-14.857-2.857-20 0-36.571 16.571-36.571 36.571 0 16.571 8.571 25.714 22.286 33.714 8 4.571 17.143 7.429 25.714 12 16 8.571 32 18.286 46.857 29.143 93.714 69.143 149.143 178.286 149.143 294.857s-55.429 225.714-149.143 294.857c-14.857 10.857-30.857 20.571-46.857 29.143-8.571 4.571-17.714 7.429-25.714 12-13.714 8-22.286 17.143-22.286 33.714 0 20 16.571 36.571 36.571 36.571 5.143 0 10.286-1.143 14.857-2.857 161.143-69.143 265.714-228 265.714-403.429z" />
|
||||
<glyph unicode="" glyph-name="qrcode" horiz-adv-x="805" d="M219.429 292.571v-73.143h-73.143v73.143h73.143zM219.429 731.428v-73.143h-73.143v73.143h73.143zM658.286 731.428v-73.143h-73.143v73.143h73.143zM73.143 146.857h219.429v218.857h-219.429v-218.857zM73.143 585.143h219.429v219.429h-219.429v-219.429zM512 585.143h219.429v219.429h-219.429v-219.429zM365.714 438.857v-365.714h-365.714v365.714h365.714zM658.286 146.286v-73.143h-73.143v73.143h73.143zM804.571 146.286v-73.143h-73.143v73.143h73.143zM804.571 438.857v-219.429h-219.429v73.143h-73.143v-219.429h-73.143v365.714h219.429v-73.143h73.143v73.143h73.143zM365.714 877.714v-365.714h-365.714v365.714h365.714zM804.571 877.714v-365.714h-365.714v365.714h365.714z" />
|
||||
<glyph unicode="" glyph-name="tag" horiz-adv-x="866" d="M256 694.857c0 40.571-32.571 73.143-73.143 73.143s-73.143-32.571-73.143-73.143 32.571-73.143 73.143-73.143 73.143 32.571 73.143 73.143zM865.714 365.714c0-19.429-8-38.286-21.143-51.429l-280.571-281.143c-13.714-13.143-32.571-21.143-52-21.143s-38.286 8-51.429 21.143l-408.571 409.143c-29.143 28.571-52 84-52 124.571v237.714c0 40 33.143 73.143 73.143 73.143h237.714c40.571 0 96-22.857 125.143-52l408.571-408c13.143-13.714 21.143-32.571 21.143-52z" />
|
||||
<glyph unicode="" glyph-name="tags" horiz-adv-x="1085" d="M256 694.857c0 40.571-32.571 73.143-73.143 73.143s-73.143-32.571-73.143-73.143 32.571-73.143 73.143-73.143 73.143 32.571 73.143 73.143zM865.714 365.714c0-19.429-8-38.286-21.143-51.429l-280.571-281.143c-13.714-13.143-32.571-21.143-52-21.143s-38.286 8-51.429 21.143l-408.571 409.143c-29.143 28.571-52 84-52 124.571v237.714c0 40 33.143 73.143 73.143 73.143h237.714c40.571 0 96-22.857 125.143-52l408.571-408c13.143-13.714 21.143-32.571 21.143-52zM1085.143 365.714c0-19.429-8-38.286-21.143-51.429l-280.571-281.143c-13.714-13.143-32.571-21.143-52-21.143-29.714 0-44.571 13.714-64 33.714l268.571 268.571c13.143 13.143 21.143 32 21.143 51.429s-8 38.286-21.143 52l-408.571 408c-29.143 29.143-84.571 52-125.143 52h128c40.571 0 96-22.857 125.143-52l408.571-408c13.143-13.714 21.143-32.571 21.143-52z" />
|
||||
<glyph unicode="" glyph-name="book" horiz-adv-x="953" d="M936.571 677.714c14.286-20.571 18.286-47.429 10.286-73.714l-157.143-517.714c-14.286-48.571-64.571-86.286-113.714-86.286h-527.429c-58.286 0-120.571 46.286-141.714 105.714-9.143 25.714-9.143 50.857-1.143 72.571 1.143 11.429 3.429 22.857 4 36.571 0.571 9.143-4.571 16.571-3.429 23.429 2.286 13.714 14.286 23.429 23.429 38.857 17.143 28.571 36.571 74.857 42.857 104.571 2.857 10.857-2.857 23.429 0 33.143 2.857 10.857 13.714 18.857 19.429 29.143 15.429 26.286 35.429 77.143 38.286 104 1.143 12-4.571 25.143-1.143 34.286 4 13.143 16.571 18.857 25.143 30.286 13.714 18.857 36.571 73.143 40 103.429 1.143 9.714-4.571 19.429-2.857 29.714 2.286 10.857 16 22.286 25.143 35.429 24 35.429 28.571 113.714 101.143 93.143l-0.571-1.714c9.714 2.286 19.429 5.143 29.143 5.143h434.857c26.857 0 50.857-12 65.143-32 14.857-20.571 18.286-47.429 10.286-74.286l-156.571-517.714c-26.857-88-41.714-107.429-114.286-107.429h-496.571c-7.429 0-16.571-1.714-21.714-8.571-4.571-6.857-5.143-12-0.571-24.571 11.429-33.143 50.857-40 82.286-40h527.429c21.143 0 45.714 12 52 32.571l171.429 564c3.429 10.857 3.429 22.286 2.857 32.571 13.143-5.143 25.143-13.143 33.714-24.571zM328.571 676.571c-3.429-10.286 2.286-18.286 12.571-18.286h347.429c9.714 0 20.571 8 24 18.286l12 36.571c3.429 10.286-2.286 18.286-12.571 18.286h-347.429c-9.714 0-20.571-8-24-18.286zM281.143 530.286c-3.429-10.286 2.286-18.286 12.571-18.286h347.429c9.714 0 20.571 8 24 18.286l12 36.571c3.429 10.286-2.286 18.286-12.571 18.286h-347.429c-9.714 0-20.571-8-24-18.286z" />
|
||||
<glyph unicode="" glyph-name="print" horiz-adv-x="951" d="M219.429 73.143h512v146.286h-512v-146.286zM219.429 438.857h512v219.429h-91.429c-30.286 0-54.857 24.571-54.857 54.857v91.429h-365.714v-365.714zM877.714 402.286c0 20-16.571 36.571-36.571 36.571s-36.571-16.571-36.571-36.571 16.571-36.571 36.571-36.571 36.571 16.571 36.571 36.571zM950.857 402.286v-237.714c0-9.714-8.571-18.286-18.286-18.286h-128v-91.429c0-30.286-24.571-54.857-54.857-54.857h-548.571c-30.286 0-54.857 24.571-54.857 54.857v91.429h-128c-9.714 0-18.286 8.571-18.286 18.286v237.714c0 60 49.714 109.714 109.714 109.714h36.571v310.857c0 30.286 24.571 54.857 54.857 54.857h384c30.286 0 72-17.143 93.714-38.857l86.857-86.857c21.714-21.714 38.857-63.429 38.857-93.714v-146.286h36.571c60 0 109.714-49.714 109.714-109.714z" />
|
||||
<glyph unicode="" glyph-name="text-height" horiz-adv-x="1030" d="M996.571 146.286c25.143 0 33.143-16 17.714-36l-72-92.571c-15.429-20-40.571-20-56 0l-72 92.571c-15.429 20-7.429 36 17.714 36h45.714v585.143h-45.714c-25.143 0-33.143 16-17.714 36l72 92.571c15.429 20 40.571 20 56 0l72-92.571c15.429-20 7.429-36-17.714-36h-45.714v-585.143h45.714zM46.286 877.143l30.857-15.429c4-1.714 108.571-2.857 120.571-2.857 50.286 0 100.571 2.286 150.857 2.286 41.143 0 81.714-0.571 122.857-0.571h167.429c22.857 0 36-5.143 51.429 16.571l24 0.571c5.143 0 10.857-0.571 16-0.571 1.143-64 1.143-128 1.143-192 0-20 0.571-42.286-2.857-62.286-12.571-4.571-25.714-8.571-38.857-10.286-13.143 22.857-22.286 48-30.857 73.143-4 11.429-17.714 88.571-18.857 89.714-12 14.857-25.143 12-42.857 12-52 0-106.286 2.286-157.714-4-2.857-25.143-5.143-52-4.571-77.714 0.571-160.571 2.286-321.143 2.286-481.714 0-44-6.857-90.286 5.714-132.571 43.429-22.286 94.857-25.714 139.429-45.714 1.143-9.143 2.857-18.857 2.857-28.571 0-5.143-0.571-10.857-1.714-16.571l-19.429-0.571c-81.143-2.286-161.143 10.286-242.857 10.286-57.714 0-115.429-10.286-173.143-10.286-0.571 9.714-1.714 20-1.714 29.714v5.143c21.714 34.857 100 35.429 136 56.571 12.571 28 10.857 182.857 10.857 218.857 0 115.429-3.429 230.857-3.429 346.286v66.857c0 10.286 2.286 51.429-4.571 59.429-8 8.571-82.857 6.857-92.571 6.857-21.143 0-82.286-9.714-98.857-21.714-27.429-18.857-27.429-133.143-61.714-135.429-10.286 6.286-24.571 15.429-32 25.143v218.857z" />
|
||||
<glyph unicode="" glyph-name="text-width" horiz-adv-x="878" d="M46.286 877.143l30.857-15.429c4-1.714 108.571-2.857 120.571-2.857 50.286 0 100.571 2.286 150.857 2.286 151.429 0 304.571 3.429 456-1.714 12.571-0.571 24.571 7.429 32 17.714l24 0.571c5.143 0 10.857-0.571 16-0.571 1.143-64 1.143-128 1.143-192 0-20.571 0.571-42.286-2.857-62.286-12.571-4.571-25.714-8.571-38.857-10.286-13.143 22.857-22.286 48-30.857 73.143-4 11.429-18.286 88.571-18.857 89.714-4 5.143-9.143 8.571-15.429 10.857-4.571 1.714-32 1.143-37.714 1.143-70.286 0-151.429 4-220.571-4-2.857-25.143-5.143-52-4.571-77.714l0.571-86.857v29.714c0.571-93.143 1.714-185.714 1.714-278.286 0-44-6.857-90.286 5.714-132.571 43.429-22.286 94.857-25.714 139.429-45.714 1.143-9.143 2.857-18.857 2.857-28.571 0-5.143-0.571-10.857-1.714-16.571l-19.429-0.571c-81.143-2.286-161.143 10.286-242.857 10.286-57.714 0-115.429-10.286-173.143-10.286-0.571 9.714-1.714 20-1.714 29.714v5.143c21.714 34.857 100 35.429 136 56.571 14.286 32 10.286 302.286 10.286 352.571 0 8-2.857 16.571-2.857 25.143 0 23.429 4 157.714-4.571 167.429-8 8.571-82.857 6.857-92.571 6.857-24 0-158.286-12.571-172-21.714-26.857-17.714-27.429-132.571-61.714-135.429-10.286 6.286-24.571 15.429-32 25.143v218.857zM748.571 144.571c20 0 96-68 111.429-80 8.571-6.857 14.857-16.571 14.857-28s-6.286-21.143-14.857-28c-15.429-12-91.429-80-111.429-80-26.286 0-17.143 61.143-17.143 71.429h-585.143c0-10.286 9.143-71.429-17.143-71.429-20 0-96 68-111.429 80-8.571 6.857-14.857 16.571-14.857 28s6.286 21.143 14.857 28c15.429 12 91.429 80 111.429 80 26.286 0 17.143-61.143 17.143-71.429h585.143c0 10.286-9.143 71.429 17.143 71.429z" />
|
||||
<glyph unicode="" glyph-name="align-left" d="M1024 182.857v-73.143c0-20-16.571-36.571-36.571-36.571h-950.857c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h950.857c20 0 36.571-16.571 36.571-36.571zM804.571 402.286v-73.143c0-20-16.571-36.571-36.571-36.571h-731.429c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h731.429c20 0 36.571-16.571 36.571-36.571zM950.857 621.714v-73.143c0-20-16.571-36.571-36.571-36.571h-877.714c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h877.714c20 0 36.571-16.571 36.571-36.571zM731.429 841.143v-73.143c0-20-16.571-36.571-36.571-36.571h-658.286c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h658.286c20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="align-center" d="M1024 182.857v-73.143c0-20-16.571-36.571-36.571-36.571h-950.857c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h950.857c20 0 36.571-16.571 36.571-36.571zM804.571 402.286v-73.143c0-20-16.571-36.571-36.571-36.571h-512c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h512c20 0 36.571-16.571 36.571-36.571zM950.857 621.714v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM731.429 841.143v-73.143c0-20-16.571-36.571-36.571-36.571h-365.714c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h365.714c20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="align-right" d="M1024 182.857v-73.143c0-20-16.571-36.571-36.571-36.571h-950.857c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h950.857c20 0 36.571-16.571 36.571-36.571zM1024 402.286v-73.143c0-20-16.571-36.571-36.571-36.571h-731.429c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h731.429c20 0 36.571-16.571 36.571-36.571zM1024 621.714v-73.143c0-20-16.571-36.571-36.571-36.571h-877.714c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h877.714c20 0 36.571-16.571 36.571-36.571zM1024 841.143v-73.143c0-20-16.571-36.571-36.571-36.571h-658.286c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h658.286c20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="align-justify" d="M1024 182.857v-73.143c0-20-16.571-36.571-36.571-36.571h-950.857c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h950.857c20 0 36.571-16.571 36.571-36.571zM1024 402.286v-73.143c0-20-16.571-36.571-36.571-36.571h-950.857c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h950.857c20 0 36.571-16.571 36.571-36.571zM1024 621.714v-73.143c0-20-16.571-36.571-36.571-36.571h-950.857c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h950.857c20 0 36.571-16.571 36.571-36.571zM1024 841.143v-73.143c0-20-16.571-36.571-36.571-36.571h-950.857c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h950.857c20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="list" d="M146.286 201.143v-109.714c0-9.714-8.571-18.286-18.286-18.286h-109.714c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h109.714c9.714 0 18.286-8.571 18.286-18.286zM146.286 420.571v-109.714c0-9.714-8.571-18.286-18.286-18.286h-109.714c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h109.714c9.714 0 18.286-8.571 18.286-18.286zM146.286 640v-109.714c0-9.714-8.571-18.286-18.286-18.286h-109.714c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h109.714c9.714 0 18.286-8.571 18.286-18.286zM1024 201.143v-109.714c0-9.714-8.571-18.286-18.286-18.286h-768c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h768c9.714 0 18.286-8.571 18.286-18.286zM146.286 859.428v-109.714c0-9.714-8.571-18.286-18.286-18.286h-109.714c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h109.714c9.714 0 18.286-8.571 18.286-18.286zM1024 420.571v-109.714c0-9.714-8.571-18.286-18.286-18.286h-768c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h768c9.714 0 18.286-8.571 18.286-18.286zM1024 640v-109.714c0-9.714-8.571-18.286-18.286-18.286h-768c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h768c9.714 0 18.286-8.571 18.286-18.286zM1024 859.428v-109.714c0-9.714-8.571-18.286-18.286-18.286h-768c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h768c9.714 0 18.286-8.571 18.286-18.286z" />
|
||||
<glyph unicode="" glyph-name="dedent, outdent" d="M219.429 640v-329.143c0-9.714-8.571-18.286-18.286-18.286-4.571 0-9.714 1.714-13.143 5.143l-164.571 164.571c-3.429 3.429-5.143 8.571-5.143 13.143s1.714 9.714 5.143 13.143l164.571 164.571c3.429 3.429 8.571 5.143 13.143 5.143 9.714 0 18.286-8.571 18.286-18.286zM1024 201.143v-109.714c0-9.714-8.571-18.286-18.286-18.286h-987.429c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h987.429c9.714 0 18.286-8.571 18.286-18.286zM1024 420.571v-109.714c0-9.714-8.571-18.286-18.286-18.286h-621.714c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h621.714c9.714 0 18.286-8.571 18.286-18.286zM1024 640v-109.714c0-9.714-8.571-18.286-18.286-18.286h-621.714c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h621.714c9.714 0 18.286-8.571 18.286-18.286zM1024 859.428v-109.714c0-9.714-8.571-18.286-18.286-18.286h-987.429c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h987.429c9.714 0 18.286-8.571 18.286-18.286z" />
|
||||
<glyph unicode="" glyph-name="indent" d="M201.143 475.428c0-4.571-1.714-9.714-5.143-13.143l-164.571-164.571c-3.429-3.429-8.571-5.143-13.143-5.143-9.714 0-18.286 8.571-18.286 18.286v329.143c0 9.714 8.571 18.286 18.286 18.286 4.571 0 9.714-1.714 13.143-5.143l164.571-164.571c3.429-3.429 5.143-8.571 5.143-13.143zM1024 201.143v-109.714c0-9.714-8.571-18.286-18.286-18.286h-987.429c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h987.429c9.714 0 18.286-8.571 18.286-18.286zM1024 420.571v-109.714c0-9.714-8.571-18.286-18.286-18.286h-621.714c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h621.714c9.714 0 18.286-8.571 18.286-18.286zM1024 640v-109.714c0-9.714-8.571-18.286-18.286-18.286h-621.714c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h621.714c9.714 0 18.286-8.571 18.286-18.286zM1024 859.428v-109.714c0-9.714-8.571-18.286-18.286-18.286h-987.429c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h987.429c9.714 0 18.286-8.571 18.286-18.286z" />
|
||||
<glyph unicode="" glyph-name="pencil" horiz-adv-x="866" d="M207.429 73.143l52 52-134.286 134.286-52-52v-61.143h73.143v-73.143h61.143zM506.286 603.428c0 7.429-5.143 12.571-12.571 12.571-3.429 0-6.857-1.143-9.714-4l-309.714-309.714c-2.857-2.857-4-6.286-4-9.714 0-7.429 5.143-12.571 12.571-12.571 3.429 0 6.857 1.143 9.714 4l309.714 309.714c2.857 2.857 4 6.286 4 9.714zM475.429 713.143l237.714-237.714-475.429-475.429h-237.714v237.714zM865.714 658.286c0-19.429-8-38.286-21.143-51.429l-94.857-94.857-237.714 237.714 94.857 94.286c13.143 13.714 32 21.714 51.429 21.714s38.286-8 52-21.714l134.286-133.714c13.143-13.714 21.143-32.571 21.143-52z" />
|
||||
<glyph unicode="" glyph-name="adjust" horiz-adv-x="878" d="M438.857 128v621.714c-171.429 0-310.857-139.429-310.857-310.857s139.429-310.857 310.857-310.857zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="edit, pencil-square-o" horiz-adv-x="1025" d="M507.429 274.286l66.286 66.286-86.857 86.857-66.286-66.286v-32h54.857v-54.857h32zM758.857 685.714c-5.143 5.143-13.714 4.571-18.857-0.571l-200-200c-5.143-5.143-5.714-13.714-0.571-18.857s13.714-4.571 18.857 0.571l200 200c5.143 5.143 5.714 13.714 0.571 18.857zM804.571 346.286v-108.571c0-90.857-73.714-164.571-164.571-164.571h-475.429c-90.857 0-164.571 73.714-164.571 164.571v475.429c0 90.857 73.714 164.571 164.571 164.571h475.429c22.857 0 45.714-4.571 66.857-14.286 5.143-2.286 9.143-7.429 10.286-13.143 1.143-6.286-0.571-12-5.143-16.571l-28-28c-5.143-5.143-12-6.857-18.286-4.571-8.571 2.286-17.143 3.429-25.714 3.429h-475.429c-50.286 0-91.429-41.143-91.429-91.429v-475.429c0-50.286 41.143-91.429 91.429-91.429h475.429c50.286 0 91.429 41.143 91.429 91.429v72c0 4.571 1.714 9.143 5.143 12.571l36.571 36.571c5.714 5.714 13.143 6.857 20 4s11.429-9.143 11.429-16.571zM749.714 768l164.571-164.571-384-384h-164.571v164.571zM1003.429 692.571l-52.571-52.571-164.571 164.571 52.571 52.571c21.143 21.143 56.571 21.143 77.714 0l86.857-86.857c21.143-21.143 21.143-56.571 0-77.714z" />
|
||||
<glyph unicode="" glyph-name="share-square-o" horiz-adv-x="954" d="M804.571 385.714v-148c0-90.857-73.714-164.571-164.571-164.571h-475.429c-90.857 0-164.571 73.714-164.571 164.571v475.429c0 90.857 73.714 164.571 164.571 164.571h145.714c9.714 0 18.286-8 18.286-18.286 0-9.143-6.286-16.571-14.857-18.286-28.571-9.714-54.286-21.143-76-34.286-2.857-1.143-5.714-2.286-9.143-2.286h-64c-50.286 0-91.429-41.143-91.429-91.429v-475.429c0-50.286 41.143-91.429 91.429-91.429h475.429c50.286 0 91.429 41.143 91.429 91.429v122.286c0 6.857 4 13.143 10.286 16.571 11.429 5.143 21.714 12.571 30.857 21.143 5.143 5.143 13.143 7.429 20 4.571s12-9.143 12-16.571zM940 669.143l-219.429-219.429c-6.857-7.429-16-10.857-25.714-10.857-4.571 0-9.714 1.143-14.286 2.857-13.143 5.714-22.286 18.857-22.286 33.714v109.714h-91.429c-125.714 0-205.714-24-250.286-74.857-46.286-53.143-60-138.857-42.286-270.286 1.143-8-4-16-11.429-19.429-2.286-0.571-4.571-1.143-6.857-1.143-5.714 0-11.429 2.857-14.857 7.429-4 5.714-94.857 134.286-94.857 248.571 0 153.143 48 329.143 420.571 329.143h91.429v109.714c0 14.857 9.143 28 22.286 33.714 4.571 1.714 9.714 2.857 14.286 2.857 9.714 0 18.857-4 25.714-10.857l219.429-219.429c14.286-14.286 14.286-37.143 0-51.429z" />
|
||||
<glyph unicode="" glyph-name="check-square-o" horiz-adv-x="955" d="M804.571 419.428v-181.714c0-90.857-73.714-164.571-164.571-164.571h-475.429c-90.857 0-164.571 73.714-164.571 164.571v475.429c0 90.857 73.714 164.571 164.571 164.571h475.429c22.857 0 45.714-4.571 66.857-14.286 5.143-2.286 9.143-7.429 10.286-13.143 1.143-6.286-0.571-12-5.143-16.571l-28-28c-3.429-3.429-8.571-5.714-13.143-5.714-1.714 0-3.429 0.571-5.143 1.143-8.571 2.286-17.143 3.429-25.714 3.429h-475.429c-50.286 0-91.429-41.143-91.429-91.429v-475.429c0-50.286 41.143-91.429 91.429-91.429h475.429c50.286 0 91.429 41.143 91.429 91.429v145.143c0 4.571 1.714 9.143 5.143 12.571l36.571 36.571c4 4 8.571 5.714 13.143 5.714 2.286 0 4.571-0.571 6.857-1.714 6.857-2.857 11.429-9.143 11.429-16.571zM936.571 698.857l-465.143-465.143c-18.286-18.286-46.857-18.286-65.143 0l-245.714 245.714c-18.286 18.286-18.286 46.857 0 65.143l62.857 62.857c18.286 18.286 46.857 18.286 65.143 0l150.286-150.286 369.714 369.714c18.286 18.286 46.857 18.286 65.143 0l62.857-62.857c18.286-18.286 18.286-46.857 0-65.143z" />
|
||||
<glyph unicode="" glyph-name="arrows" d="M1024 438.857c0-9.714-4-18.857-10.857-25.714l-146.286-146.286c-6.857-6.857-16-10.857-25.714-10.857-20 0-36.571 16.571-36.571 36.571v73.143h-219.429v-219.429h73.143c20 0 36.571-16.571 36.571-36.571 0-9.714-4-18.857-10.857-25.714l-146.286-146.286c-6.857-6.857-16-10.857-25.714-10.857s-18.857 4-25.714 10.857l-146.286 146.286c-6.857 6.857-10.857 16-10.857 25.714 0 20 16.571 36.571 36.571 36.571h73.143v219.429h-219.429v-73.143c0-20-16.571-36.571-36.571-36.571-9.714 0-18.857 4-25.714 10.857l-146.286 146.286c-6.857 6.857-10.857 16-10.857 25.714s4 18.857 10.857 25.714l146.286 146.286c6.857 6.857 16 10.857 25.714 10.857 20 0 36.571-16.571 36.571-36.571v-73.143h219.429v219.429h-73.143c-20 0-36.571 16.571-36.571 36.571 0 9.714 4 18.857 10.857 25.714l146.286 146.286c6.857 6.857 16 10.857 25.714 10.857s18.857-4 25.714-10.857l146.286-146.286c6.857-6.857 10.857-16 10.857-25.714 0-20-16.571-36.571-36.571-36.571h-73.143v-219.429h219.429v73.143c0 20 16.571 36.571 36.571 36.571 9.714 0 18.857-4 25.714-10.857l146.286-146.286c6.857-6.857 10.857-16 10.857-25.714z" />
|
||||
<glyph unicode="" glyph-name="step-backward" horiz-adv-x="585" d="M559.429 870.286c14.286 14.286 25.714 9.143 25.714-10.857v-841.143c0-20-11.429-25.143-25.714-10.857l-405.714 405.714c-3.429 3.429-5.714 6.857-7.429 10.857v-387.429c0-20-16.571-36.571-36.571-36.571h-73.143c-20 0-36.571 16.571-36.571 36.571v804.571c0 20 16.571 36.571 36.571 36.571h73.143c20 0 36.571-16.571 36.571-36.571v-387.429c1.714 4 4 7.429 7.429 10.857z" />
|
||||
<glyph unicode="" glyph-name="fast-backward" d="M998.286 870.286c14.286 14.286 25.714 9.143 25.714-10.857v-841.143c0-20-11.429-25.143-25.714-10.857l-405.714 405.714c-3.429 3.429-5.714 6.857-7.429 10.857v-405.714c0-20-11.429-25.143-25.714-10.857l-405.714 405.714c-3.429 3.429-5.714 6.857-7.429 10.857v-387.429c0-20-16.571-36.571-36.571-36.571h-73.143c-20 0-36.571 16.571-36.571 36.571v804.571c0 20 16.571 36.571 36.571 36.571h73.143c20 0 36.571-16.571 36.571-36.571v-387.429c1.714 4 4 7.429 7.429 10.857l405.714 405.714c14.286 14.286 25.714 9.143 25.714-10.857v-405.714c1.714 4 4 7.429 7.429 10.857z" />
|
||||
<glyph unicode="" glyph-name="backward" horiz-adv-x="1017" d="M925.143 870.286c14.286 14.286 25.714 9.143 25.714-10.857v-841.143c0-20-11.429-25.143-25.714-10.857l-405.714 405.714c-3.429 3.429-5.714 6.857-7.429 10.857v-405.714c0-20-11.429-25.143-25.714-10.857l-405.714 405.714c-14.286 14.286-14.286 37.143 0 51.429l405.714 405.714c14.286 14.286 25.714 9.143 25.714-10.857v-405.714c1.714 4 4 7.429 7.429 10.857z" />
|
||||
<glyph unicode="" glyph-name="play" horiz-adv-x="809" d="M790.857 421.143l-758.857-421.714c-17.714-9.714-32-1.143-32 18.857v841.143c0 20 14.286 28.571 32 18.857l758.857-421.714c17.714-9.714 17.714-25.714 0-35.429z" />
|
||||
<glyph unicode="" glyph-name="pause" horiz-adv-x="878" d="M877.714 841.143v-804.571c0-20-16.571-36.571-36.571-36.571h-292.571c-20 0-36.571 16.571-36.571 36.571v804.571c0 20 16.571 36.571 36.571 36.571h292.571c20 0 36.571-16.571 36.571-36.571zM365.714 841.143v-804.571c0-20-16.571-36.571-36.571-36.571h-292.571c-20 0-36.571 16.571-36.571 36.571v804.571c0 20 16.571 36.571 36.571 36.571h292.571c20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="stop" horiz-adv-x="878" d="M877.714 841.143v-804.571c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v804.571c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="forward" horiz-adv-x="885" d="M25.714 7.428c-14.286-14.286-25.714-9.143-25.714 10.857v841.143c0 20 11.429 25.143 25.714 10.857l405.714-405.714c3.429-3.429 5.714-6.857 7.429-10.857v405.714c0 20 11.429 25.143 25.714 10.857l405.714-405.714c14.286-14.286 14.286-37.143 0-51.429l-405.714-405.714c-14.286-14.286-25.714-9.143-25.714 10.857v405.714c-1.714-4-4-7.429-7.429-10.857z" />
|
||||
<glyph unicode="" glyph-name="fast-forward" d="M25.714 7.428c-14.286-14.286-25.714-9.143-25.714 10.857v841.143c0 20 11.429 25.143 25.714 10.857l405.714-405.714c3.429-3.429 5.714-6.857 7.429-10.857v405.714c0 20 11.429 25.143 25.714 10.857l405.714-405.714c3.429-3.429 5.714-6.857 7.429-10.857v387.429c0 20 16.571 36.571 36.571 36.571h73.143c20 0 36.571-16.571 36.571-36.571v-804.571c0-20-16.571-36.571-36.571-36.571h-73.143c-20 0-36.571 16.571-36.571 36.571v387.429c-1.714-4-4-7.429-7.429-10.857l-405.714-405.714c-14.286-14.286-25.714-9.143-25.714 10.857v405.714c-1.714-4-4-7.429-7.429-10.857z" />
|
||||
<glyph unicode="" glyph-name="step-forward" horiz-adv-x="585" d="M25.714 7.428c-14.286-14.286-25.714-9.143-25.714 10.857v841.143c0 20 11.429 25.143 25.714 10.857l405.714-405.714c3.429-3.429 5.714-6.857 7.429-10.857v387.429c0 20 16.571 36.571 36.571 36.571h73.143c20 0 36.571-16.571 36.571-36.571v-804.571c0-20-16.571-36.571-36.571-36.571h-73.143c-20 0-36.571 16.571-36.571 36.571v387.429c-1.714-4-4-7.429-7.429-10.857z" />
|
||||
<glyph unicode="" glyph-name="eject" horiz-adv-x="879" d="M8 391.428l405.714 405.714c14.286 14.286 37.143 14.286 51.429 0l405.714-405.714c14.286-14.286 9.143-25.714-10.857-25.714h-841.143c-20 0-25.143 11.429-10.857 25.714zM841.714 73.143h-804.571c-20 0-36.571 16.571-36.571 36.571v146.286c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571v-146.286c0-20-16.571-36.571-36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="chevron-left" horiz-adv-x="768" d="M669.143 778.857l-303.429-303.429 303.429-303.429c14.286-14.286 14.286-37.143 0-51.429l-94.857-94.857c-14.286-14.286-37.143-14.286-51.429 0l-424 424c-14.286 14.286-14.286 37.143 0 51.429l424 424c14.286 14.286 37.143 14.286 51.429 0l94.857-94.857c14.286-14.286 14.286-37.143 0-51.429z" />
|
||||
<glyph unicode="" glyph-name="chevron-right" horiz-adv-x="695" d="M632.571 449.714l-424-424c-14.286-14.286-37.143-14.286-51.429 0l-94.857 94.857c-14.286 14.286-14.286 37.143 0 51.429l303.429 303.429-303.429 303.429c-14.286 14.286-14.286 37.143 0 51.429l94.857 94.857c14.286 14.286 37.143 14.286 51.429 0l424-424c14.286-14.286 14.286-37.143 0-51.429z" />
|
||||
<glyph unicode="" glyph-name="plus-circle" horiz-adv-x="878" d="M694.857 402.286v73.143c0 20-16.571 36.571-36.571 36.571h-146.286v146.286c0 20-16.571 36.571-36.571 36.571h-73.143c-20 0-36.571-16.571-36.571-36.571v-146.286h-146.286c-20 0-36.571-16.571-36.571-36.571v-73.143c0-20 16.571-36.571 36.571-36.571h146.286v-146.286c0-20 16.571-36.571 36.571-36.571h73.143c20 0 36.571 16.571 36.571 36.571v146.286h146.286c20 0 36.571 16.571 36.571 36.571zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="minus-circle" horiz-adv-x="878" d="M694.857 402.286v73.143c0 20-16.571 36.571-36.571 36.571h-438.857c-20 0-36.571-16.571-36.571-36.571v-73.143c0-20 16.571-36.571 36.571-36.571h438.857c20 0 36.571 16.571 36.571 36.571zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="times-circle" horiz-adv-x="878" d="M656.571 309.714c0 9.714-4 18.857-10.857 25.714l-103.429 103.429 103.429 103.429c6.857 6.857 10.857 16 10.857 25.714s-4 19.429-10.857 26.286l-51.429 51.429c-6.857 6.857-16.571 10.857-26.286 10.857s-18.857-4-25.714-10.857l-103.429-103.429-103.429 103.429c-6.857 6.857-16 10.857-25.714 10.857s-19.429-4-26.286-10.857l-51.429-51.429c-6.857-6.857-10.857-16.571-10.857-26.286s4-18.857 10.857-25.714l103.429-103.429-103.429-103.429c-6.857-6.857-10.857-16-10.857-25.714s4-19.429 10.857-26.286l51.429-51.429c6.857-6.857 16.571-10.857 26.286-10.857s18.857 4 25.714 10.857l103.429 103.429 103.429-103.429c6.857-6.857 16-10.857 25.714-10.857s19.429 4 26.286 10.857l51.429 51.429c6.857 6.857 10.857 16.571 10.857 26.286zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="check-circle" horiz-adv-x="878" d="M733.714 531.428c0 9.714-3.429 19.429-10.286 26.286l-52 51.429c-6.857 6.857-16 10.857-25.714 10.857s-18.857-4-25.714-10.857l-233.143-232.571-129.143 129.143c-6.857 6.857-16 10.857-25.714 10.857s-18.857-4-25.714-10.857l-52-51.429c-6.857-6.857-10.286-16.571-10.286-26.286s3.429-18.857 10.286-25.714l206.857-206.857c6.857-6.857 16.571-10.857 25.714-10.857 9.714 0 19.429 4 26.286 10.857l310.286 310.286c6.857 6.857 10.286 16 10.286 25.714zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="question-circle" horiz-adv-x="878" d="M512 164.571v109.714c0 10.286-8 18.286-18.286 18.286h-109.714c-10.286 0-18.286-8-18.286-18.286v-109.714c0-10.286 8-18.286 18.286-18.286h109.714c10.286 0 18.286 8 18.286 18.286zM658.286 548.571c0 104.571-109.714 182.857-208 182.857-93.143 0-162.857-40-212-121.714-5.143-8-2.857-18.286 4.571-24l75.429-57.143c2.857-2.286 6.857-3.429 10.857-3.429 5.143 0 10.857 2.286 14.286 6.857 26.857 34.286 38.286 44.571 49.143 52.571 9.714 6.857 28.571 13.714 49.143 13.714 36.571 0 70.286-23.429 70.286-48.571 0-29.714-15.429-44.571-50.286-60.571-40.571-18.286-96-65.714-96-121.143v-20.571c0-10.286 8-18.286 18.286-18.286h109.714c10.286 0 18.286 8 18.286 18.286v0c0 13.143 16.571 41.143 43.429 56.571 43.429 24.571 102.857 57.714 102.857 144.571zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="info-circle" horiz-adv-x="878" d="M585.143 164.571v91.429c0 10.286-8 18.286-18.286 18.286h-54.857v292.571c0 10.286-8 18.286-18.286 18.286h-182.857c-10.286 0-18.286-8-18.286-18.286v-91.429c0-10.286 8-18.286 18.286-18.286h54.857v-182.857h-54.857c-10.286 0-18.286-8-18.286-18.286v-91.429c0-10.286 8-18.286 18.286-18.286h256c10.286 0 18.286 8 18.286 18.286zM512 676.571v91.429c0 10.286-8 18.286-18.286 18.286h-109.714c-10.286 0-18.286-8-18.286-18.286v-91.429c0-10.286 8-18.286 18.286-18.286h109.714c10.286 0 18.286 8 18.286 18.286zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="crosshairs" horiz-adv-x="878" d="M684 365.714h-62.286c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h62.286c-24.571 82.286-89.714 147.429-172 172v-62.286c0-20-16.571-36.571-36.571-36.571h-73.143c-20 0-36.571 16.571-36.571 36.571v62.286c-82.286-24.571-147.429-89.714-172-172h62.286c20 0 36.571-16.571 36.571-36.571v-73.143c0-20-16.571-36.571-36.571-36.571h-62.286c24.571-82.286 89.714-147.429 172-172v62.286c0 20 16.571 36.571 36.571 36.571h73.143c20 0 36.571-16.571 36.571-36.571v-62.286c82.286 24.571 147.429 89.714 172 172zM877.714 475.428v-73.143c0-20-16.571-36.571-36.571-36.571h-81.714c-28-122.857-124.571-219.429-247.429-247.429v-81.714c0-20-16.571-36.571-36.571-36.571h-73.143c-20 0-36.571 16.571-36.571 36.571v81.714c-122.857 28-219.429 124.571-247.429 247.429h-81.714c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h81.714c28 122.857 124.571 219.429 247.429 247.429v81.714c0 20 16.571 36.571 36.571 36.571h73.143c20 0 36.571-16.571 36.571-36.571v-81.714c122.857-28 219.429-124.571 247.429-247.429h81.714c20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="times-circle-o" horiz-adv-x="878" d="M626.857 334.286l-83.429-83.429c-7.429-7.429-18.857-7.429-26.286 0l-78.286 78.286-78.286-78.286c-7.429-7.429-18.857-7.429-26.286 0l-83.429 83.429c-7.429 7.429-7.429 18.857 0 26.286l78.286 78.286-78.286 78.286c-7.429 7.429-7.429 18.857 0 26.286l83.429 83.429c7.429 7.429 18.857 7.429 26.286 0l78.286-78.286 78.286 78.286c7.429 7.429 18.857 7.429 26.286 0l83.429-83.429c7.429-7.429 7.429-18.857 0-26.286l-78.286-78.286 78.286-78.286c7.429-7.429 7.429-18.857 0-26.286zM749.714 438.857c0 171.429-139.429 310.857-310.857 310.857s-310.857-139.429-310.857-310.857 139.429-310.857 310.857-310.857 310.857 139.429 310.857 310.857zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="check-circle-o" horiz-adv-x="878" d="M669.143 486.286l-241.143-241.143c-14.286-14.286-37.143-14.286-51.429 0l-168 168c-14.286 14.286-14.286 37.143 0 51.429l58.286 58.286c14.286 14.286 37.143 14.286 51.429 0l84-84 157.143 157.143c14.286 14.286 37.143 14.286 51.429 0l58.286-58.286c14.286-14.286 14.286-37.143 0-51.429zM749.714 438.857c0 171.429-139.429 310.857-310.857 310.857s-310.857-139.429-310.857-310.857 139.429-310.857 310.857-310.857 310.857 139.429 310.857 310.857zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="ban" horiz-adv-x="878" d="M749.714 440.571c0 62.286-18.286 120-49.714 168.571l-430.857-430.286c49.143-32 107.429-50.857 169.714-50.857 171.429 0 310.857 140 310.857 312.571zM178.857 269.714l431.429 430.857c-49.143 33.143-108 52-171.429 52-171.429 0-310.857-140-310.857-312 0-63.429 18.857-121.714 50.857-170.857zM877.714 440.571c0-243.429-196.571-440.571-438.857-440.571s-438.857 197.143-438.857 440.571c0 242.857 196.571 440 438.857 440s438.857-197.143 438.857-440z" />
|
||||
<glyph unicode="" glyph-name="arrow-left" horiz-adv-x="914" d="M877.714 438.857v-73.143c0-38.857-25.714-73.143-66.857-73.143h-402.286l167.429-168c13.714-13.143 21.714-32 21.714-51.429s-8-38.286-21.714-51.429l-42.857-43.429c-13.143-13.143-32-21.143-51.429-21.143s-38.286 8-52 21.143l-372 372.571c-13.143 13.143-21.143 32-21.143 51.429s8 38.286 21.143 52l372 371.429c13.714 13.714 32.571 21.714 52 21.714s37.714-8 51.429-21.714l42.857-42.286c13.714-13.714 21.714-32.571 21.714-52s-8-38.286-21.714-52l-167.429-167.429h402.286c41.143 0 66.857-34.286 66.857-73.143z" />
|
||||
<glyph unicode="" glyph-name="arrow-right" horiz-adv-x="841" d="M841.143 402.286c0-19.429-7.429-38.286-21.143-52l-372-372c-13.714-13.143-32.571-21.143-52-21.143s-37.714 8-51.429 21.143l-42.857 42.857c-13.714 13.714-21.714 32.571-21.714 52s8 38.286 21.714 52l167.429 167.429h-402.286c-41.143 0-66.857 34.286-66.857 73.143v73.143c0 38.857 25.714 73.143 66.857 73.143h402.286l-167.429 168c-13.714 13.143-21.714 32-21.714 51.429s8 38.286 21.714 51.429l42.857 42.857c13.714 13.714 32 21.714 51.429 21.714s38.286-8 52-21.714l372-372c13.714-13.143 21.143-32 21.143-51.429z" />
|
||||
<glyph unicode="" glyph-name="arrow-up" horiz-adv-x="951" d="M920.571 396c0-19.429-8-37.714-21.143-51.429l-42.857-42.857c-13.714-13.714-32.571-21.714-52-21.714s-38.286 8-51.429 21.714l-168 167.429v-402.286c0-41.143-34.286-66.857-73.143-66.857h-73.143c-38.857 0-73.143 25.714-73.143 66.857v402.286l-168-167.429c-13.143-13.714-32-21.714-51.429-21.714s-38.286 8-51.429 21.714l-42.857 42.857c-13.714 13.714-21.714 32-21.714 51.429s8 38.286 21.714 52l372 372c13.143 13.714 32 21.143 51.429 21.143s38.286-7.429 52-21.143l372-372c13.143-13.714 21.143-32.571 21.143-52z" />
|
||||
<glyph unicode="" glyph-name="arrow-down" horiz-adv-x="951" d="M920.571 475.428c0-19.429-8-38.286-21.143-51.429l-372-372.571c-13.714-13.143-32.571-21.143-52-21.143s-38.286 8-51.429 21.143l-372 372.571c-13.714 13.143-21.714 32-21.714 51.429s8 38.286 21.714 52l42.286 42.857c13.714 13.143 32.571 21.143 52 21.143s38.286-8 51.429-21.143l168-168v402.286c0 40 33.143 73.143 73.143 73.143h73.143c40 0 73.143-33.143 73.143-73.143v-402.286l168 168c13.143 13.143 32 21.143 51.429 21.143s38.286-8 52-21.143l42.857-42.857c13.143-13.714 21.143-32.571 21.143-52z" />
|
||||
<glyph unicode="" glyph-name="mail-forward, share" d="M1024 585.143c0-9.714-4-18.857-10.857-25.714l-292.571-292.571c-6.857-6.857-16-10.857-25.714-10.857-20 0-36.571 16.571-36.571 36.571v146.286h-128c-246.286 0-408-47.429-408-320 0-23.429 1.143-46.857 2.857-70.286 0.571-9.143 2.857-19.429 2.857-28.571 0-10.857-6.857-20-18.286-20-8 0-12 4-16 9.714-8.571 12-14.857 30.286-21.143 43.429-32.571 73.143-72.571 177.714-72.571 257.714 0 64 6.286 129.714 30.286 190.286 79.429 197.143 312.571 230.286 500 230.286h128v146.286c0 20 16.571 36.571 36.571 36.571 9.714 0 18.857-4 25.714-10.857l292.571-292.571c6.857-6.857 10.857-16 10.857-25.714z" />
|
||||
<glyph unicode="" glyph-name="expand" horiz-adv-x="878" d="M431.429 347.428c0-4.571-2.286-9.714-5.714-13.143l-189.714-189.714 82.286-82.286c6.857-6.857 10.857-16 10.857-25.714 0-20-16.571-36.571-36.571-36.571h-256c-20 0-36.571 16.571-36.571 36.571v256c0 20 16.571 36.571 36.571 36.571 9.714 0 18.857-4 25.714-10.857l82.286-82.286 189.714 189.714c3.429 3.429 8.571 5.714 13.143 5.714s9.714-2.286 13.143-5.714l65.143-65.143c3.429-3.429 5.714-8.571 5.714-13.143zM877.714 841.143v-256c0-20-16.571-36.571-36.571-36.571-9.714 0-18.857 4-25.714 10.857l-82.286 82.286-189.714-189.714c-3.429-3.429-8.571-5.714-13.143-5.714s-9.714 2.286-13.143 5.714l-65.143 65.143c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l189.714 189.714-82.286 82.286c-6.857 6.857-10.857 16-10.857 25.714 0 20 16.571 36.571 36.571 36.571h256c20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="compress" horiz-adv-x="878" d="M438.857 402.286v-256c0-20-16.571-36.571-36.571-36.571-9.714 0-18.857 4-25.714 10.857l-82.286 82.286-189.714-189.714c-3.429-3.429-8.571-5.714-13.143-5.714s-9.714 2.286-13.143 5.714l-65.143 65.143c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l189.714 189.714-82.286 82.286c-6.857 6.857-10.857 16-10.857 25.714 0 20 16.571 36.571 36.571 36.571h256c20 0 36.571-16.571 36.571-36.571zM870.286 786.286c0-4.571-2.286-9.714-5.714-13.143l-189.714-189.714 82.286-82.286c6.857-6.857 10.857-16 10.857-25.714 0-20-16.571-36.571-36.571-36.571h-256c-20 0-36.571 16.571-36.571 36.571v256c0 20 16.571 36.571 36.571 36.571 9.714 0 18.857-4 25.714-10.857l82.286-82.286 189.714 189.714c3.429 3.429 8.571 5.714 13.143 5.714s9.714-2.286 13.143-5.714l65.143-65.143c3.429-3.429 5.714-8.571 5.714-13.143z" />
|
||||
<glyph unicode="" glyph-name="plus" horiz-adv-x="805" d="M804.571 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-237.714v-237.714c0-30.286-24.571-54.857-54.857-54.857h-109.714c-30.286 0-54.857 24.571-54.857 54.857v237.714h-237.714c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h237.714v237.714c0 30.286 24.571 54.857 54.857 54.857h109.714c30.286 0 54.857-24.571 54.857-54.857v-237.714h237.714c30.286 0 54.857-24.571 54.857-54.857z" />
|
||||
<glyph unicode="" glyph-name="minus" horiz-adv-x="805" d="M804.571 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-694.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h694.857c30.286 0 54.857-24.571 54.857-54.857z" />
|
||||
<glyph unicode="" glyph-name="asterisk" horiz-adv-x="951" d="M846.857 350.857c34.857-20 46.857-65.143 26.857-100l-36.571-62.857c-20-34.857-65.143-46.857-100-26.857l-152 87.429v-175.429c0-40-33.143-73.143-73.143-73.143h-73.143c-40 0-73.143 33.143-73.143 73.143v175.429l-152-87.429c-34.857-20-80-8-100 26.857l-36.571 62.857c-20 34.857-8 80 26.857 100l152 88-152 88c-34.857 20-46.857 65.143-26.857 100l36.571 62.857c20 34.857 65.143 46.857 100 26.857l152-87.429v175.429c0 40 33.143 73.143 73.143 73.143h73.143c40 0 73.143-33.143 73.143-73.143v-175.429l152 87.429c34.857 20 80 8 100-26.857l36.571-62.857c20-34.857 8-80-26.857-100l-152-88z" />
|
||||
<glyph unicode="" glyph-name="exclamation-circle" horiz-adv-x="878" d="M438.857 877.714c242.286 0 438.857-196.571 438.857-438.857s-196.571-438.857-438.857-438.857-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857zM512 165.143v108.571c0 10.286-8 18.857-17.714 18.857h-109.714c-10.286 0-18.857-8.571-18.857-18.857v-108.571c0-10.286 8.571-18.857 18.857-18.857h109.714c9.714 0 17.714 8.571 17.714 18.857zM510.857 361.714l10.286 354.857c0 4-1.714 8-5.714 10.286-3.429 2.857-8.571 4.571-13.714 4.571h-125.714c-5.143 0-10.286-1.714-13.714-4.571-4-2.286-5.714-6.286-5.714-10.286l9.714-354.857c0-8 8.571-14.286 19.429-14.286h105.714c10.286 0 18.857 6.286 19.429 14.286z" />
|
||||
<glyph unicode="" glyph-name="eye" d="M950.857 402.286c-54.286 84-128.571 156-217.714 201.714 22.857-38.857 34.857-83.429 34.857-128.571 0-141.143-114.857-256-256-256s-256 114.857-256 256c0 45.143 12 89.714 34.857 128.571-89.143-45.714-163.429-117.714-217.714-201.714 97.714-150.857 255.429-256 438.857-256s341.143 105.143 438.857 256zM539.429 621.714c0 14.857-12.571 27.429-27.429 27.429-95.429 0-173.714-78.286-173.714-173.714 0-14.857 12.571-27.429 27.429-27.429s27.429 12.571 27.429 27.429c0 65.143 53.714 118.857 118.857 118.857 14.857 0 27.429 12.571 27.429 27.429zM1024 402.286c0-14.286-4.571-27.429-11.429-39.429-105.143-173.143-297.714-289.714-500.571-289.714s-395.429 117.143-500.571 289.714c-6.857 12-11.429 25.143-11.429 39.429s4.571 27.429 11.429 39.429c105.143 172.571 297.714 289.714 500.571 289.714s395.429-117.143 500.571-289.714c6.857-12 11.429-25.143 11.429-39.429z" />
|
||||
<glyph unicode="" glyph-name="eye-slash" d="M317.143 188l44.571 80.571c-66.286 48-105.714 125.143-105.714 206.857 0 45.143 12 89.714 34.857 128.571-89.143-45.714-163.429-117.714-217.714-201.714 59.429-92 143.429-169.143 244-214.286zM539.429 621.714c0 14.857-12.571 27.429-27.429 27.429-95.429 0-173.714-78.286-173.714-173.714 0-14.857 12.571-27.429 27.429-27.429s27.429 12.571 27.429 27.429c0 65.714 53.714 118.857 118.857 118.857 14.857 0 27.429 12.571 27.429 27.429zM746.857 730.857c0-1.143 0-4-0.571-5.143-120.571-215.429-240-432-360.571-647.429l-28-50.857c-3.429-5.714-9.714-9.143-16-9.143-10.286 0-64.571 33.143-76.571 40-5.714 3.429-9.143 9.143-9.143 16 0 9.143 19.429 40 25.143 49.714-110.857 50.286-204 136-269.714 238.857-7.429 11.429-11.429 25.143-11.429 39.429 0 13.714 4 28 11.429 39.429 113.143 173.714 289.714 289.714 500.571 289.714 34.286 0 69.143-3.429 102.857-9.714l30.857 55.429c3.429 5.714 9.143 9.143 16 9.143 10.286 0 64-33.143 76-40 5.714-3.429 9.143-9.143 9.143-15.429zM768 475.428c0-106.286-65.714-201.143-164.571-238.857l160 286.857c2.857-16 4.571-32 4.571-48zM1024 402.286c0-14.857-4-26.857-11.429-39.429-17.714-29.143-40-57.143-62.286-82.857-112-128.571-266.286-206.857-438.286-206.857l42.286 75.429c166.286 14.286 307.429 115.429 396.571 253.714-42.286 65.714-96.571 123.429-161.143 168l36 64c70.857-47.429 142.286-118.857 186.857-192.571 7.429-12.571 11.429-24.571 11.429-39.429z" />
|
||||
<glyph unicode="" glyph-name="exclamation-triangle, warning" d="M585.143 165.143v108.571c0 10.286-8 18.857-18.286 18.857h-109.714c-10.286 0-18.286-8.571-18.286-18.857v-108.571c0-10.286 8-18.857 18.286-18.857h109.714c10.286 0 18.286 8.571 18.286 18.857zM584 378.857l10.286 262.286c0 3.429-1.714 8-5.714 10.857-3.429 2.857-8.571 6.286-13.714 6.286h-125.714c-5.143 0-10.286-3.429-13.714-6.286-4-2.857-5.714-8.571-5.714-12l9.714-261.143c0-7.429 8.571-13.143 19.429-13.143h105.714c10.286 0 18.857 5.714 19.429 13.143zM576 912.571l438.857-804.571c12.571-22.286 12-49.714-1.143-72s-37.143-36-62.857-36h-877.714c-25.714 0-49.714 13.714-62.857 36s-13.714 49.714-1.143 72l438.857 804.571c12.571 23.429 37.143 38.286 64 38.286s51.429-14.857 64-38.286z" />
|
||||
<glyph unicode="" glyph-name="calendar" horiz-adv-x="951" d="M73.143 0h164.571v164.571h-164.571v-164.571zM274.286 0h182.857v164.571h-182.857v-164.571zM73.143 201.143h164.571v182.857h-164.571v-182.857zM274.286 201.143h182.857v182.857h-182.857v-182.857zM73.143 420.571h164.571v164.571h-164.571v-164.571zM493.714 0h182.857v164.571h-182.857v-164.571zM274.286 420.571h182.857v164.571h-182.857v-164.571zM713.143 0h164.571v164.571h-164.571v-164.571zM493.714 201.143h182.857v182.857h-182.857v-182.857zM292.571 694.857v164.571c0 9.714-8.571 18.286-18.286 18.286h-36.571c-9.714 0-18.286-8.571-18.286-18.286v-164.571c0-9.714 8.571-18.286 18.286-18.286h36.571c9.714 0 18.286 8.571 18.286 18.286zM713.143 201.143h164.571v182.857h-164.571v-182.857zM493.714 420.571h182.857v164.571h-182.857v-164.571zM713.143 420.571h164.571v164.571h-164.571v-164.571zM731.429 694.857v164.571c0 9.714-8.571 18.286-18.286 18.286h-36.571c-9.714 0-18.286-8.571-18.286-18.286v-164.571c0-9.714 8.571-18.286 18.286-18.286h36.571c9.714 0 18.286 8.571 18.286 18.286zM950.857 731.428v-731.429c0-40-33.143-73.143-73.143-73.143h-804.571c-40 0-73.143 33.143-73.143 73.143v731.429c0 40 33.143 73.143 73.143 73.143h73.143v54.857c0 50.286 41.143 91.429 91.429 91.429h36.571c50.286 0 91.429-41.143 91.429-91.429v-54.857h219.429v54.857c0 50.286 41.143 91.429 91.429 91.429h36.571c50.286 0 91.429-41.143 91.429-91.429v-54.857h73.143c40 0 73.143-33.143 73.143-73.143z" />
|
||||
<glyph unicode="" glyph-name="random" d="M380.571 676c-32-49.143-55.429-102.286-78.286-156-33.143 69.143-69.714 138.286-156 138.286h-128c-10.286 0-18.286 8-18.286 18.286v109.714c0 10.286 8 18.286 18.286 18.286h128c101.714 0 176.571-47.429 234.286-128.571zM1024 219.428c0-4.571-1.714-9.714-5.143-13.143l-182.857-182.857c-3.429-3.429-8.571-5.143-13.143-5.143-9.714 0-18.286 8.571-18.286 18.286v109.714c-169.714 0-274.286-20-380 128.571 31.429 49.143 54.857 102.286 77.714 156 33.143-69.143 69.714-138.286 156-138.286h146.286v109.714c0 10.286 8 18.286 18.286 18.286 5.143 0 9.714-2.286 13.714-5.714l182.286-182.286c3.429-3.429 5.143-8.571 5.143-13.143zM1024 731.428c0-4.571-1.714-9.714-5.143-13.143l-182.857-182.857c-3.429-3.429-8.571-5.143-13.143-5.143-9.714 0-18.286 8-18.286 18.286v109.714h-146.286c-76 0-112-52-144-113.714-16.571-32-30.857-65.143-44.571-97.714-63.429-147.429-137.714-300.571-323.429-300.571h-128c-10.286 0-18.286 8-18.286 18.286v109.714c0 10.286 8 18.286 18.286 18.286h128c76 0 112 52 144 113.714 16.571 32 30.857 65.143 44.571 97.714 63.429 147.429 137.714 300.571 323.429 300.571h146.286v109.714c0 10.286 8 18.286 18.286 18.286 5.143 0 9.714-2.286 13.714-5.714l182.286-182.286c3.429-3.429 5.143-8.571 5.143-13.143z" />
|
||||
<glyph unicode="" glyph-name="comment" d="M1024 438.857c0-202.286-229.143-365.714-512-365.714-28 0-56 1.714-82.857 4.571-74.857-66.286-164-113.143-262.857-138.286-20.571-5.714-42.857-9.714-65.143-12.571-12.571-1.143-24.571 8-27.429 21.714v0.571c-2.857 14.286 6.857 22.857 15.429 33.143 36 40.571 77.143 74.857 104 170.286-117.714 66.857-193.143 170.286-193.143 286.286 0 201.714 229.143 365.714 512 365.714s512-163.429 512-365.714z" />
|
||||
<glyph unicode="" glyph-name="chevron-up" d="M961.714 190.286l-94.857-94.286c-14.286-14.286-37.143-14.286-51.429 0l-303.429 303.429-303.429-303.429c-14.286-14.286-37.143-14.286-51.429 0l-94.857 94.286c-14.286 14.286-14.286 37.714 0 52l424 423.429c14.286 14.286 37.143 14.286 51.429 0l424-423.429c14.286-14.286 14.286-37.714 0-52z" />
|
||||
<glyph unicode="" glyph-name="chevron-down" d="M961.714 489.143l-424-423.429c-14.286-14.286-37.143-14.286-51.429 0l-424 423.429c-14.286 14.286-14.286 37.714 0 52l94.857 94.286c14.286 14.286 37.143 14.286 51.429 0l303.429-303.429 303.429 303.429c14.286 14.286 37.143 14.286 51.429 0l94.857-94.286c14.286-14.286 14.286-37.714 0-52z" />
|
||||
<glyph unicode="" glyph-name="retweet" horiz-adv-x="1097" d="M731.429 91.428c0-9.714-8.571-18.286-18.286-18.286h-548.571c-21.143 0-18.286 22.286-18.286 36.571v329.143h-109.714c-20 0-36.571 16.571-36.571 36.571 0 8.571 2.857 17.143 8.571 23.429l182.857 219.429c6.857 8 17.143 12.571 28 12.571s21.143-4.571 28-12.571l182.857-219.429c5.714-6.286 8.571-14.857 8.571-23.429 0-20-16.571-36.571-36.571-36.571h-109.714v-219.429h329.143c5.143 0 10.857-2.286 14.286-6.286l91.429-109.714c2.286-3.429 4-8 4-12zM1097.143 329.143c0-8.571-2.857-17.143-8.571-23.429l-182.857-219.429c-6.857-8-17.143-13.143-28-13.143s-21.143 5.143-28 13.143l-182.857 219.429c-5.714 6.286-8.571 14.857-8.571 23.429 0 20 16.571 36.571 36.571 36.571h109.714v219.429h-329.143c-5.143 0-10.857 2.286-14.286 6.857l-91.429 109.714c-2.286 2.857-4 7.429-4 11.429 0 9.714 8.571 18.286 18.286 18.286h548.571c21.143 0 18.286-22.286 18.286-36.571v-329.143h109.714c20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="folder" horiz-adv-x="951" d="M950.857 603.428v-402.286c0-70.286-57.714-128-128-128h-694.857c-70.286 0-128 57.714-128 128v548.571c0 70.286 57.714 128 128 128h182.857c70.286 0 128-57.714 128-128v-18.286h384c70.286 0 128-57.714 128-128z" />
|
||||
<glyph unicode="" glyph-name="folder-open" horiz-adv-x="1074" d="M1073.714 406.857c0-13.714-8.571-27.429-17.714-37.714l-192-226.286c-33.143-38.857-100.571-69.714-150.857-69.714h-621.714c-20.571 0-49.714 6.286-49.714 32 0 13.714 8.571 27.429 17.714 37.714l192 226.286c33.143 38.857 100.571 69.714 150.857 69.714h621.714c20.571 0 49.714-6.286 49.714-32zM877.714 603.428v-91.429h-475.429c-71.429 0-160-40.571-206.286-95.429l-195.429-229.714c0 4.571-0.571 9.714-0.571 14.286v548.571c0 70.286 57.714 128 128 128h182.857c70.286 0 128-57.714 128-128v-18.286h310.857c70.286 0 128-57.714 128-128z" />
|
||||
<glyph unicode="" glyph-name="arrows-v" horiz-adv-x="439" d="M402.286 768c0-20-16.571-36.571-36.571-36.571h-73.143v-585.143h73.143c20 0 36.571-16.571 36.571-36.571 0-9.714-4-18.857-10.857-25.714l-146.286-146.286c-6.857-6.857-16-10.857-25.714-10.857s-18.857 4-25.714 10.857l-146.286 146.286c-6.857 6.857-10.857 16-10.857 25.714 0 20 16.571 36.571 36.571 36.571h73.143v585.143h-73.143c-20 0-36.571 16.571-36.571 36.571 0 9.714 4 18.857 10.857 25.714l146.286 146.286c6.857 6.857 16 10.857 25.714 10.857s18.857-4 25.714-10.857l146.286-146.286c6.857-6.857 10.857-16 10.857-25.714z" />
|
||||
<glyph unicode="" glyph-name="arrows-h" d="M1024 438.857c0-9.714-4-18.857-10.857-25.714l-146.286-146.286c-6.857-6.857-16-10.857-25.714-10.857-20 0-36.571 16.571-36.571 36.571v73.143h-585.143v-73.143c0-20-16.571-36.571-36.571-36.571-9.714 0-18.857 4-25.714 10.857l-146.286 146.286c-6.857 6.857-10.857 16-10.857 25.714s4 18.857 10.857 25.714l146.286 146.286c6.857 6.857 16 10.857 25.714 10.857 20 0 36.571-16.571 36.571-36.571v-73.143h585.143v73.143c0 20 16.571 36.571 36.571 36.571 9.714 0 18.857-4 25.714-10.857l146.286-146.286c6.857-6.857 10.857-16 10.857-25.714z" />
|
||||
<glyph unicode="" glyph-name="cogs, gears" horiz-adv-x="1097" d="M512 438.857c0 80.571-65.714 146.286-146.286 146.286s-146.286-65.714-146.286-146.286 65.714-146.286 146.286-146.286 146.286 65.714 146.286 146.286zM950.857 146.286c0 40-33.143 73.143-73.143 73.143s-73.143-33.143-73.143-73.143c0-40.571 33.143-73.143 73.143-73.143 40.571 0 73.143 33.143 73.143 73.143zM950.857 731.428c0 40-33.143 73.143-73.143 73.143s-73.143-33.143-73.143-73.143c0-40.571 33.143-73.143 73.143-73.143 40.571 0 73.143 33.143 73.143 73.143zM731.429 490.857v-105.714c0-7.429-5.714-16-13.143-17.143l-88.571-13.714c-4.571-14.857-10.857-29.143-18.286-43.429 16-22.857 33.143-44 51.429-65.714 2.286-3.429 4-6.857 4-11.429 0-4-1.143-8-4-10.857-11.429-15.429-75.429-85.143-92-85.143-4.571 0-8.571 1.714-12 4l-65.714 51.429c-14.286-7.429-28.571-13.143-44-17.714-2.857-29.143-5.714-60.571-13.143-88.571-2.286-8-9.143-13.714-17.143-13.714h-106.286c-8 0-16 6.286-17.143 14.286l-13.143 87.429c-14.857 4.571-29.143 10.857-42.857 17.714l-67.429-50.857c-2.857-2.857-7.429-4-11.429-4-4.571 0-8.571 1.714-12 4.571-14.857 13.714-82.286 74.857-82.286 91.429 0 4 1.714 7.429 4 10.857 16.571 21.714 33.714 42.857 50.286 65.143-8 15.429-14.857 30.857-20 46.857l-86.857 13.714c-8 1.143-13.714 8.571-13.714 16.571v105.714c0 7.429 5.714 16 13.143 17.143l88.571 13.714c4.571 14.857 10.857 29.143 18.286 43.429-16 22.857-33.143 44-51.429 65.714-2.286 3.429-4 7.429-4 11.429s1.143 8 4 11.429c11.429 15.429 75.429 84.571 92 84.571 4.571 0 8.571-1.714 12-4l65.714-51.429c14.286 7.429 28.571 13.143 44 18.286 2.857 28.571 5.714 60 13.143 88 2.286 8 9.143 13.714 17.143 13.714h106.286c8 0 16-6.286 17.143-14.286l13.143-87.429c14.857-4.571 29.143-10.857 42.857-17.714l67.429 50.857c3.429 2.857 7.429 4 11.429 4 4.571 0 8.571-1.714 12-4.571 14.857-13.714 82.286-75.429 82.286-91.429 0-4-1.714-7.429-4-10.857-16.571-22.286-33.714-42.857-49.714-65.143 7.429-15.429 14.286-30.857 19.429-46.857l86.857-13.143c8-1.714 13.714-9.143 13.714-17.143zM1097.143 186.286v-80c0-8.571-73.714-16.571-85.143-17.714-4.571-10.857-10.286-20.571-17.143-29.714 5.143-11.429 29.143-68.571 29.143-78.857 0-1.714-0.571-2.857-2.286-4-6.857-4-68-40.571-70.857-40.571-7.429 0-50.286 57.143-56 65.714-5.714-0.571-11.429-1.143-17.143-1.143s-11.429 0.571-17.143 1.143c-5.714-8.571-48.571-65.714-56-65.714-2.857 0-64 36.571-70.857 40.571-1.714 1.143-2.286 2.857-2.286 4 0 9.714 24 67.429 29.143 78.857-6.857 9.143-12.571 18.857-17.143 29.714-11.429 1.143-85.143 9.143-85.143 17.714v80c0 8.571 73.714 16.571 85.143 17.714 4.571 10.286 10.286 20.571 17.143 29.714-5.143 11.429-29.143 69.143-29.143 78.857 0 1.143 0.571 2.857 2.286 4 6.857 3.429 68 40 70.857 40 7.429 0 50.286-56.571 56-65.143 5.714 0.571 11.429 1.143 17.143 1.143s11.429-0.571 17.143-1.143c16 22.286 33.143 44.571 52.571 64l3.429 1.143c2.857 0 64-36 70.857-40 1.714-1.143 2.286-2.857 2.286-4 0-10.286-24-67.429-29.143-78.857 6.857-9.143 12.571-19.429 17.143-29.714 11.429-1.143 85.143-9.143 85.143-17.714zM1097.143 771.428v-80c0-8.571-73.714-16.571-85.143-17.714-4.571-10.857-10.286-20.571-17.143-29.714 5.143-11.429 29.143-68.571 29.143-78.857 0-1.714-0.571-2.857-2.286-4-6.857-4-68-40.571-70.857-40.571-7.429 0-50.286 57.143-56 65.714-5.714-0.571-11.429-1.143-17.143-1.143s-11.429 0.571-17.143 1.143c-5.714-8.571-48.571-65.714-56-65.714-2.857 0-64 36.571-70.857 40.571-1.714 1.143-2.286 2.857-2.286 4 0 9.714 24 67.429 29.143 78.857-6.857 9.143-12.571 18.857-17.143 29.714-11.429 1.143-85.143 9.143-85.143 17.714v80c0 8.571 73.714 16.571 85.143 17.714 4.571 10.286 10.286 20.571 17.143 29.714-5.143 11.429-29.143 69.143-29.143 78.857 0 1.143 0.571 2.857 2.286 4 6.857 3.429 68 40 70.857 40 7.429 0 50.286-56.571 56-65.143 5.714 0.571 11.429 1.143 17.143 1.143s11.429-0.571 17.143-1.143c16 22.286 33.143 44.571 52.571 64l3.429 1.143c2.857 0 64-36 70.857-40 1.714-1.143 2.286-2.857 2.286-4 0-10.286-24-67.429-29.143-78.857 6.857-9.143 12.571-19.429 17.143-29.714 11.429-1.143 85.143-9.143 85.143-17.714z" />
|
||||
<glyph unicode="" glyph-name="star-half" horiz-adv-x="475" d="M475.429 932.571v-765.143l-256.571-134.857c-7.429-4-14.857-6.857-22.857-6.857-16.571 0-24 13.714-24 28.571 0 4 0.571 7.429 1.143 11.429l49.143 285.714-208 202.286c-6.857 7.429-14.286 17.143-14.286 27.429 0 17.143 17.714 24 32 26.286l286.857 41.714 128.571 260c5.143 10.857 14.857 23.429 28 23.429v0z" />
|
||||
<glyph unicode="" glyph-name="heart-o" d="M950.857 610.286c0 160.571-108.571 194.286-200 194.286-85.143 0-181.143-92-210.857-127.429-13.714-16.571-42.286-16.571-56 0-29.714 35.429-125.714 127.429-210.857 127.429-91.429 0-200-33.714-200-194.286 0-104.571 105.714-201.714 106.857-202.857l332-320 331.429 319.429c1.714 1.714 107.429 98.857 107.429 203.429zM1024 610.286c0-137.143-125.714-252-130.857-257.143l-356-342.857c-6.857-6.857-16-10.286-25.143-10.286s-18.286 3.429-25.143 10.286l-356.571 344c-4.571 4-130.286 118.857-130.286 256 0 167.429 102.286 267.429 273.143 267.429 100 0 193.714-78.857 238.857-123.429 45.143 44.571 138.857 123.429 238.857 123.429 170.857 0 273.143-100 273.143-267.429z" />
|
||||
<glyph unicode="" glyph-name="sign-out" horiz-adv-x="896" d="M365.714 128c0-16 7.429-54.857-18.286-54.857h-182.857c-90.857 0-164.571 73.714-164.571 164.571v402.286c0 90.857 73.714 164.571 164.571 164.571h182.857c9.714 0 18.286-8.571 18.286-18.286 0-16 7.429-54.857-18.286-54.857h-182.857c-50.286 0-91.429-41.143-91.429-91.429v-402.286c0-50.286 41.143-91.429 91.429-91.429h164.571c14.286 0 36.571 2.857 36.571-18.286zM896 438.857c0-9.714-4-18.857-10.857-25.714l-310.857-310.857c-6.857-6.857-16-10.857-25.714-10.857-20 0-36.571 16.571-36.571 36.571v164.571h-256c-20 0-36.571 16.571-36.571 36.571v219.429c0 20 16.571 36.571 36.571 36.571h256v164.571c0 20 16.571 36.571 36.571 36.571 9.714 0 18.857-4 25.714-10.857l310.857-310.857c6.857-6.857 10.857-16 10.857-25.714z" />
|
||||
<glyph unicode="" glyph-name="thumb-tack" horiz-adv-x="658" d="M274.286 457.143v256c0 10.286-8 18.286-18.286 18.286s-18.286-8-18.286-18.286v-256c0-10.286 8-18.286 18.286-18.286s18.286 8 18.286 18.286zM658.286 256c0-20-16.571-36.571-36.571-36.571h-245.143l-29.143-276c-1.143-9.143-8.571-16.571-17.714-16.571h-0.571c-9.143 0-16.571 6.286-18.286 15.429l-43.429 277.143h-230.857c-20 0-36.571 16.571-36.571 36.571 0 93.714 70.857 182.857 146.286 182.857v292.571c-40 0-73.143 33.143-73.143 73.143s33.143 73.143 73.143 73.143h365.714c40 0 73.143-33.143 73.143-73.143s-33.143-73.143-73.143-73.143v-292.571c75.429 0 146.286-89.143 146.286-182.857z" />
|
||||
<glyph unicode="" glyph-name="external-link" d="M804.571 420.571v-182.857c0-90.857-73.714-164.571-164.571-164.571h-475.429c-90.857 0-164.571 73.714-164.571 164.571v475.429c0 90.857 73.714 164.571 164.571 164.571h402.286c10.286 0 18.286-8 18.286-18.286v-36.571c0-10.286-8-18.286-18.286-18.286h-402.286c-50.286 0-91.429-41.143-91.429-91.429v-475.429c0-50.286 41.143-91.429 91.429-91.429h475.429c50.286 0 91.429 41.143 91.429 91.429v182.857c0 10.286 8 18.286 18.286 18.286h36.571c10.286 0 18.286-8 18.286-18.286zM1024 914.286v-292.571c0-20-16.571-36.571-36.571-36.571-9.714 0-18.857 4-25.714 10.857l-100.571 100.571-372.571-372.571c-3.429-3.429-8.571-5.714-13.143-5.714s-9.714 2.286-13.143 5.714l-65.143 65.143c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l372.571 372.571-100.571 100.571c-6.857 6.857-10.857 16-10.857 25.714 0 20 16.571 36.571 36.571 36.571h292.571c20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="sign-in" horiz-adv-x="878" d="M676.571 438.857c0-9.714-4-18.857-10.857-25.714l-310.857-310.857c-6.857-6.857-16-10.857-25.714-10.857-20 0-36.571 16.571-36.571 36.571v164.571h-256c-20 0-36.571 16.571-36.571 36.571v219.429c0 20 16.571 36.571 36.571 36.571h256v164.571c0 20 16.571 36.571 36.571 36.571 9.714 0 18.857-4 25.714-10.857l310.857-310.857c6.857-6.857 10.857-16 10.857-25.714zM877.714 640v-402.286c0-90.857-73.714-164.571-164.571-164.571h-182.857c-9.714 0-18.286 8.571-18.286 18.286 0 16-7.429 54.857 18.286 54.857h182.857c50.286 0 91.429 41.143 91.429 91.429v402.286c0 50.286-41.143 91.429-91.429 91.429h-164.571c-14.286 0-36.571-2.857-36.571 18.286 0 16-7.429 54.857 18.286 54.857h182.857c90.857 0 164.571-73.714 164.571-164.571z" />
|
||||
<glyph unicode="" glyph-name="upload" horiz-adv-x="951" d="M731.429 109.714c0 20-16.571 36.571-36.571 36.571s-36.571-16.571-36.571-36.571 16.571-36.571 36.571-36.571 36.571 16.571 36.571 36.571zM877.714 109.714c0 20-16.571 36.571-36.571 36.571s-36.571-16.571-36.571-36.571 16.571-36.571 36.571-36.571 36.571 16.571 36.571 36.571zM950.857 237.714v-182.857c0-30.286-24.571-54.857-54.857-54.857h-841.143c-30.286 0-54.857 24.571-54.857 54.857v182.857c0 30.286 24.571 54.857 54.857 54.857h244c15.429-42.286 56-73.143 103.429-73.143h146.286c47.429 0 88 30.857 103.429 73.143h244c30.286 0 54.857-24.571 54.857-54.857zM765.143 608c-5.714-13.714-18.857-22.857-33.714-22.857h-146.286v-256c0-20-16.571-36.571-36.571-36.571h-146.286c-20 0-36.571 16.571-36.571 36.571v256h-146.286c-14.857 0-28 9.143-33.714 22.857-5.714 13.143-2.857 29.143 8 39.429l256 256c6.857 7.429 16.571 10.857 25.714 10.857s18.857-3.429 25.714-10.857l256-256c10.857-10.286 13.714-26.286 8-39.429z" />
|
||||
<glyph unicode="" glyph-name="square-o" horiz-adv-x="805" d="M640 804.571h-475.429c-50.286 0-91.429-41.143-91.429-91.429v-475.429c0-50.286 41.143-91.429 91.429-91.429h475.429c50.286 0 91.429 41.143 91.429 91.429v475.429c0 50.286-41.143 91.429-91.429 91.429zM804.571 713.143v-475.429c0-90.857-73.714-164.571-164.571-164.571h-475.429c-90.857 0-164.571 73.714-164.571 164.571v475.429c0 90.857 73.714 164.571 164.571 164.571h475.429c90.857 0 164.571-73.714 164.571-164.571z" />
|
||||
<glyph unicode="" glyph-name="bookmark-o" horiz-adv-x="731" d="M658.286 804.571h-585.143v-709.714l292.571 280.571 50.857-48.571 241.714-232v709.714zM665.143 877.714c8.571 0 17.143-1.714 25.143-5.143 25.143-9.714 41.143-33.143 41.143-58.857v-736.571c0-25.714-16-49.143-41.143-58.857-8-3.429-16.571-4.571-25.143-4.571-17.714 0-34.286 6.286-47.429 18.286l-252 242.286-252-242.286c-13.143-12-29.714-18.857-47.429-18.857-8.571 0-17.143 1.714-25.143 5.143-25.143 9.714-41.143 33.143-41.143 58.857v736.571c0 25.714 16 49.143 41.143 58.857 8 3.429 16.571 5.143 25.143 5.143h598.857z" />
|
||||
<glyph unicode="" glyph-name="hdd-o" horiz-adv-x="878" d="M594.286 256c0-25.143-20.571-45.714-45.714-45.714s-45.714 20.571-45.714 45.714 20.571 45.714 45.714 45.714 45.714-20.571 45.714-45.714zM740.571 256c0-25.143-20.571-45.714-45.714-45.714s-45.714 20.571-45.714 45.714 20.571 45.714 45.714 45.714 45.714-20.571 45.714-45.714zM804.571 164.571v182.857c0 9.714-8.571 18.286-18.286 18.286h-694.857c-9.714 0-18.286-8.571-18.286-18.286v-182.857c0-9.714 8.571-18.286 18.286-18.286h694.857c9.714 0 18.286 8.571 18.286 18.286zM101.714 438.857h674.286l-89.714 275.429c-2.857 9.714-13.714 17.143-24 17.143h-446.857c-10.286 0-21.143-7.429-24-17.143zM877.714 347.428v-182.857c0-50.286-41.143-91.429-91.429-91.429h-694.857c-50.286 0-91.429 41.143-91.429 91.429v182.857c0 15.429 4.571 28.571 9.143 42.857l112.571 346.286c13.143 40 51.429 68 93.714 68h446.857c42.286 0 80.571-28 93.714-68l112.571-346.286c4.571-14.286 9.143-27.429 9.143-42.857z" />
|
||||
<glyph unicode="" glyph-name="bell-o" d="M521.143-18.286c0 5.143-4 9.143-9.143 9.143-45.143 0-82.286 37.143-82.286 82.286 0 5.143-4 9.143-9.143 9.143s-9.143-4-9.143-9.143c0-55.429 45.143-100.571 100.571-100.571 5.143 0 9.143 4 9.143 9.143zM140.571 146.286h742.857c-102.286 115.429-152 272-152 475.429 0 73.714-69.714 182.857-219.429 182.857s-219.429-109.143-219.429-182.857c0-203.429-49.714-360-152-475.429zM987.429 146.286c0-40-33.143-73.143-73.143-73.143h-256c0-80.571-65.714-146.286-146.286-146.286s-146.286 65.714-146.286 146.286h-256c-40 0-73.143 33.143-73.143 73.143 84.571 71.429 182.857 199.429 182.857 475.429 0 109.714 90.857 229.714 242.286 252-2.857 6.857-4.571 14.286-4.571 22.286 0 30.286 24.571 54.857 54.857 54.857s54.857-24.571 54.857-54.857c0-8-1.714-15.429-4.571-22.286 151.429-22.286 242.286-142.286 242.286-252 0-276 98.286-404 182.857-475.429z" />
|
||||
<glyph unicode="" glyph-name="certificate" horiz-adv-x="878" d="M786.286 438.857l78.857-77.143c10.857-10.286 14.857-25.714 11.429-40-4-14.286-15.429-25.714-29.714-29.143l-107.429-27.429 30.286-106.286c4-14.286 0-29.714-10.857-40-10.286-10.857-25.714-14.857-40-10.857l-106.286 30.286-27.429-107.429c-3.429-14.286-14.857-25.714-29.143-29.714-3.429-0.571-7.429-1.143-10.857-1.143-10.857 0-21.714 4.571-29.143 12.571l-77.143 78.857-77.143-78.857c-10.286-10.857-25.714-14.857-40-11.429-14.857 4-25.714 15.429-29.143 29.714l-27.429 107.429-106.286-30.286c-14.286-4-29.714 0-40 10.857-10.857 10.286-14.857 25.714-10.857 40l30.286 106.286-107.429 27.429c-14.286 3.429-25.714 14.857-29.714 29.143-3.429 14.286 0.571 29.714 11.429 40l78.857 77.143-78.857 77.143c-10.857 10.286-14.857 25.714-11.429 40 4 14.286 15.429 25.714 29.714 29.143l107.429 27.429-30.286 106.286c-4 14.286 0 29.714 10.857 40 10.286 10.857 25.714 14.857 40 10.857l106.286-30.286 27.429 107.429c3.429 14.286 14.857 25.714 29.143 29.143 14.286 4 29.714 0 40-10.857l77.143-79.429 77.143 79.429c10.286 10.857 25.143 14.857 40 10.857 14.286-3.429 25.714-14.857 29.143-29.143l27.429-107.429 106.286 30.286c14.286 4 29.714 0 40-10.857 10.857-10.286 14.857-25.714 10.857-40l-30.286-106.286 107.429-27.429c14.286-3.429 25.714-14.857 29.714-29.143 3.429-14.286-0.571-29.714-11.429-40z" />
|
||||
<glyph unicode="" glyph-name="arrow-circle-left" horiz-adv-x="878" d="M731.429 402.286v73.143c0 20-16.571 36.571-36.571 36.571h-286.857l108 108c6.857 6.857 10.857 16 10.857 25.714s-4 18.857-10.857 25.714l-52 52c-6.857 6.857-16 10.286-25.714 10.286s-18.857-3.429-25.714-10.286l-258.857-258.857c-6.857-6.857-10.286-16-10.286-25.714s3.429-18.857 10.286-25.714l258.857-258.857c6.857-6.857 16-10.286 25.714-10.286s18.857 3.429 25.714 10.286l52 52c6.857 6.857 10.286 16 10.286 25.714s-3.429 18.857-10.286 25.714l-108 108h286.857c20 0 36.571 16.571 36.571 36.571zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="arrow-circle-right" horiz-adv-x="878" d="M734.286 438.857c0 9.714-3.429 18.857-10.286 25.714l-258.857 258.857c-6.857 6.857-16 10.286-25.714 10.286s-18.857-3.429-25.714-10.286l-52-52c-6.857-6.857-10.286-16-10.286-25.714s3.429-18.857 10.286-25.714l108-108h-286.857c-20 0-36.571-16.571-36.571-36.571v-73.143c0-20 16.571-36.571 36.571-36.571h286.857l-108-108c-6.857-6.857-10.857-16-10.857-25.714s4-18.857 10.857-25.714l52-52c6.857-6.857 16-10.286 25.714-10.286s18.857 3.429 25.714 10.286l258.857 258.857c6.857 6.857 10.286 16 10.286 25.714zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="arrow-circle-up" horiz-adv-x="878" d="M733.714 439.428c0 9.714-3.429 18.857-10.286 25.714l-258.857 258.857c-6.857 6.857-16 10.286-25.714 10.286s-18.857-3.429-25.714-10.286l-258.857-258.857c-6.857-6.857-10.286-16-10.286-25.714s3.429-18.857 10.286-25.714l52-52c6.857-6.857 16-10.286 25.714-10.286s18.857 3.429 25.714 10.286l108 108v-286.857c0-20 16.571-36.571 36.571-36.571h73.143c20 0 36.571 16.571 36.571 36.571v286.857l108-108c6.857-6.857 16-10.857 25.714-10.857s18.857 4 25.714 10.857l52 52c6.857 6.857 10.286 16 10.286 25.714zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="arrow-circle-down" horiz-adv-x="878" d="M733.714 438.286c0 9.714-3.429 18.857-10.286 25.714l-52 52c-6.857 6.857-16 10.286-25.714 10.286s-18.857-3.429-25.714-10.286l-108-108v286.857c0 20-16.571 36.571-36.571 36.571h-73.143c-20 0-36.571-16.571-36.571-36.571v-286.857l-108 108c-6.857 6.857-16 10.857-25.714 10.857s-18.857-4-25.714-10.857l-52-52c-6.857-6.857-10.286-16-10.286-25.714s3.429-18.857 10.286-25.714l258.857-258.857c6.857-6.857 16-10.286 25.714-10.286s18.857 3.429 25.714 10.286l258.857 258.857c6.857 6.857 10.286 16 10.286 25.714zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="wrench" horiz-adv-x="962" d="M219.429 109.714c0 20-16.571 36.571-36.571 36.571s-36.571-16.571-36.571-36.571 16.571-36.571 36.571-36.571 36.571 16.571 36.571 36.571zM587.429 349.714l-389.714-389.714c-13.143-13.143-32-21.143-51.429-21.143s-38.286 8-52 21.143l-60.571 61.714c-13.714 13.143-21.714 32-21.714 51.429s8 38.286 21.714 52l389.143 389.143c29.714-74.857 89.714-134.857 164.571-164.571zM949.714 598.286c0-18.857-6.857-42.286-13.143-60.571-36-101.714-133.714-172-241.714-172-141.143 0-256 114.857-256 256s114.857 256 256 256c41.714 0 96-12.571 130.857-36 5.714-4 9.143-9.143 9.143-16 0-6.286-4-12.571-9.143-16l-167.429-96.571v-128l110.286-61.143c18.857 10.857 151.429 94.286 162.857 94.286s18.286-8.571 18.286-20z" />
|
||||
<glyph unicode="" glyph-name="tasks" d="M585.143 146.286h365.714v73.143h-365.714v-73.143zM365.714 438.857h585.143v73.143h-585.143v-73.143zM731.429 731.428h219.429v73.143h-219.429v-73.143zM1024 256v-146.286c0-20-16.571-36.571-36.571-36.571h-950.857c-20 0-36.571 16.571-36.571 36.571v146.286c0 20 16.571 36.571 36.571 36.571h950.857c20 0 36.571-16.571 36.571-36.571zM1024 548.571v-146.286c0-20-16.571-36.571-36.571-36.571h-950.857c-20 0-36.571 16.571-36.571 36.571v146.286c0 20 16.571 36.571 36.571 36.571h950.857c20 0 36.571-16.571 36.571-36.571zM1024 841.143v-146.286c0-20-16.571-36.571-36.571-36.571h-950.857c-20 0-36.571 16.571-36.571 36.571v146.286c0 20 16.571 36.571 36.571 36.571h950.857c20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="filter" horiz-adv-x="805" d="M801.714 782.286c5.714-13.714 2.857-29.714-8-40l-281.714-281.714v-424c0-14.857-9.143-28-22.286-33.714-4.571-1.714-9.714-2.857-14.286-2.857-9.714 0-18.857 3.429-25.714 10.857l-146.286 146.286c-6.857 6.857-10.857 16-10.857 25.714v277.714l-281.714 281.714c-10.857 10.286-13.714 26.286-8 40 5.714 13.143 18.857 22.286 33.714 22.286h731.429c14.857 0 28-9.143 33.714-22.286z" />
|
||||
<glyph unicode="" glyph-name="briefcase" d="M365.714 804.571h292.571v73.143h-292.571v-73.143zM1024 438.857v-274.286c0-50.286-41.143-91.429-91.429-91.429h-841.143c-50.286 0-91.429 41.143-91.429 91.429v274.286h384v-91.429c0-20 16.571-36.571 36.571-36.571h182.857c20 0 36.571 16.571 36.571 36.571v91.429h384zM585.143 438.857v-73.143h-146.286v73.143h146.286zM1024 713.143v-219.429h-1024v219.429c0 50.286 41.143 91.429 91.429 91.429h201.143v91.429c0 30.286 24.571 54.857 54.857 54.857h329.143c30.286 0 54.857-24.571 54.857-54.857v-91.429h201.143c50.286 0 91.429-41.143 91.429-91.429z" />
|
||||
<glyph unicode="" glyph-name="arrows-alt" horiz-adv-x="878" d="M733.143 641.714l-202.857-202.857 202.857-202.857 82.286 82.286c10.286 10.857 26.286 13.714 40 8 13.143-5.714 22.286-18.857 22.286-33.714v-256c0-20-16.571-36.571-36.571-36.571h-256c-14.857 0-28 9.143-33.714 22.857-5.714 13.143-2.857 29.143 8 39.429l82.286 82.286-202.857 202.857-202.857-202.857 82.286-82.286c10.857-10.286 13.714-26.286 8-39.429-5.714-13.714-18.857-22.857-33.714-22.857h-256c-20 0-36.571 16.571-36.571 36.571v256c0 14.857 9.143 28 22.857 33.714 13.143 5.714 29.143 2.857 39.429-8l82.286-82.286 202.857 202.857-202.857 202.857-82.286-82.286c-6.857-6.857-16-10.857-25.714-10.857-4.571 0-9.714 1.143-13.714 2.857-13.714 5.714-22.857 18.857-22.857 33.714v256c0 20 16.571 36.571 36.571 36.571h256c14.857 0 28-9.143 33.714-22.857 5.714-13.143 2.857-29.143-8-39.429l-82.286-82.286 202.857-202.857 202.857 202.857-82.286 82.286c-10.857 10.286-13.714 26.286-8 39.429 5.714 13.714 18.857 22.857 33.714 22.857h256c20 0 36.571-16.571 36.571-36.571v-256c0-14.857-9.143-28-22.286-33.714-4.571-1.714-9.714-2.857-14.286-2.857-9.714 0-18.857 4-25.714 10.857z" />
|
||||
<glyph unicode="" glyph-name="cloud" horiz-adv-x="1097" d="M1097.143 292.571c0-121.143-98.286-219.429-219.429-219.429h-621.714c-141.143 0-256 114.857-256 256 0 102.286 60.571 190.857 147.429 231.429-0.571 8-1.143 16.571-1.143 24.571 0 161.714 130.857 292.571 292.571 292.571 122.286 0 226.857-74.857 270.857-181.714 25.143 22.286 58.286 35.429 94.857 35.429 80.571 0 146.286-65.714 146.286-146.286 0-29.143-8.571-56-23.429-78.857 97.143-22.857 169.714-109.714 169.714-213.714z" />
|
||||
<glyph unicode="" glyph-name="copy, files-o" d="M969.143 731.428c30.286 0 54.857-24.571 54.857-54.857v-694.857c0-30.286-24.571-54.857-54.857-54.857h-548.571c-30.286 0-54.857 24.571-54.857 54.857v164.571h-310.857c-30.286 0-54.857 24.571-54.857 54.857v384c0 30.286 17.714 72.571 38.857 93.714l233.143 233.143c21.143 21.143 63.429 38.857 93.714 38.857h237.714c30.286 0 54.857-24.571 54.857-54.857v-187.429c22.286 13.143 50.857 22.857 73.143 22.857h237.714zM658.286 609.714l-170.857-170.857h170.857v170.857zM292.571 829.143l-170.857-170.857h170.857v170.857zM404.571 459.428l180.571 180.571v237.714h-219.429v-237.714c0-30.286-24.571-54.857-54.857-54.857h-237.714v-365.714h292.571v146.286c0 30.286 17.714 72.571 38.857 93.714zM950.857 0v658.286h-219.429v-237.714c0-30.286-24.571-54.857-54.857-54.857h-237.714v-365.714h512z" />
|
||||
<glyph unicode="" glyph-name="floppy-o, save" horiz-adv-x="878" d="M219.429 73.143h438.857v219.429h-438.857v-219.429zM731.429 73.143h73.143v512c0 10.857-9.714 34.286-17.143 41.714l-160.571 160.571c-8 8-30.286 17.143-41.714 17.143v-237.714c0-30.286-24.571-54.857-54.857-54.857h-329.143c-30.286 0-54.857 24.571-54.857 54.857v237.714h-73.143v-731.429h73.143v237.714c0 30.286 24.571 54.857 54.857 54.857h475.429c30.286 0 54.857-24.571 54.857-54.857v-237.714zM512 603.428v182.857c0 9.714-8.571 18.286-18.286 18.286h-109.714c-9.714 0-18.286-8.571-18.286-18.286v-182.857c0-9.714 8.571-18.286 18.286-18.286h109.714c9.714 0 18.286 8.571 18.286 18.286zM877.714 585.143v-530.286c0-30.286-24.571-54.857-54.857-54.857h-768c-30.286 0-54.857 24.571-54.857 54.857v768c0 30.286 24.571 54.857 54.857 54.857h530.286c30.286 0 72-17.143 93.714-38.857l160-160c21.714-21.714 38.857-63.429 38.857-93.714z" />
|
||||
<glyph unicode="" glyph-name="square" horiz-adv-x="878" d="M877.714 713.143v-548.571c0-90.857-73.714-164.571-164.571-164.571h-548.571c-90.857 0-164.571 73.714-164.571 164.571v548.571c0 90.857 73.714 164.571 164.571 164.571h548.571c90.857 0 164.571-73.714 164.571-164.571z" />
|
||||
<glyph unicode="" glyph-name="bars, navicon, reorder" horiz-adv-x="878" d="M877.714 182.857v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM877.714 475.428v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM877.714 768v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="list-ul" d="M219.429 146.286c0-60.571-49.143-109.714-109.714-109.714s-109.714 49.143-109.714 109.714 49.143 109.714 109.714 109.714 109.714-49.143 109.714-109.714zM219.429 438.857c0-60.571-49.143-109.714-109.714-109.714s-109.714 49.143-109.714 109.714 49.143 109.714 109.714 109.714 109.714-49.143 109.714-109.714zM1024 201.143v-109.714c0-9.714-8.571-18.286-18.286-18.286h-694.857c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h694.857c9.714 0 18.286-8.571 18.286-18.286zM219.429 731.428c0-60.571-49.143-109.714-109.714-109.714s-109.714 49.143-109.714 109.714 49.143 109.714 109.714 109.714 109.714-49.143 109.714-109.714zM1024 493.714v-109.714c0-9.714-8.571-18.286-18.286-18.286h-694.857c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h694.857c9.714 0 18.286-8.571 18.286-18.286zM1024 786.286v-109.714c0-9.714-8.571-18.286-18.286-18.286h-694.857c-9.714 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8.571 18.286 18.286 18.286h694.857c9.714 0 18.286-8.571 18.286-18.286z" />
|
||||
<glyph unicode="" glyph-name="list-ol" horiz-adv-x="1033" d="M217.714 25.143c0-62.857-49.143-98.286-108.571-98.286-36 0-72.571 12-98.286 37.714l32.571 50.286c15.429-14.286 38.857-25.714 60.571-25.714 20 0 41.143 9.714 41.143 32.571 0 32-36.571 33.714-60 32l-14.857 32c20.571 26.286 39.429 55.429 64 77.714v0.571c-18.286 0-37.143-1.143-55.429-1.143v-30.286h-60.571v86.857h190.286v-50.286l-54.286-65.714c38.286-9.143 63.429-38.857 63.429-78.286zM218.857 383.428v-90.857h-206.857c-1.714 10.286-3.429 20.571-3.429 30.857 0 105.714 129.143 121.714 129.143 169.714 0 19.429-12 29.714-30.857 29.714-20 0-36.571-17.143-46.286-33.143l-48.571 33.714c18.857 39.429 57.714 61.714 101.143 61.714 53.143 0 98.857-31.429 98.857-88 0-84.571-124-103.429-125.714-148h72.571v34.286h60zM1024 201.143v-109.714c0-9.714-8.571-18.286-18.286-18.286h-694.857c-10.286 0-18.286 8.571-18.286 18.286v109.714c0 10.286 8 18.286 18.286 18.286h694.857c9.714 0 18.286-8 18.286-18.286zM219.429 714.857v-56.571h-191.429v56.571h61.143c0 46.286 0.571 92.571 0.571 138.857v6.857h-1.143c-6.286-12.571-17.714-21.143-28.571-30.857l-40.571 43.429 77.714 72.571h60.571v-230.857h61.714zM1024 493.714v-109.714c0-9.714-8.571-18.286-18.286-18.286h-694.857c-10.286 0-18.286 8.571-18.286 18.286v109.714c0 10.286 8 18.286 18.286 18.286h694.857c9.714 0 18.286-8 18.286-18.286zM1024 786.286v-109.714c0-9.714-8.571-18.286-18.286-18.286h-694.857c-10.286 0-18.286 8.571-18.286 18.286v109.714c0 9.714 8 18.286 18.286 18.286h694.857c9.714 0 18.286-8.571 18.286-18.286z" />
|
||||
<glyph unicode="" glyph-name="table" horiz-adv-x="951" d="M292.571 164.571v109.714c0 10.286-8 18.286-18.286 18.286h-182.857c-10.286 0-18.286-8-18.286-18.286v-109.714c0-10.286 8-18.286 18.286-18.286h182.857c10.286 0 18.286 8 18.286 18.286zM292.571 384v109.714c0 10.286-8 18.286-18.286 18.286h-182.857c-10.286 0-18.286-8-18.286-18.286v-109.714c0-10.286 8-18.286 18.286-18.286h182.857c10.286 0 18.286 8 18.286 18.286zM585.143 164.571v109.714c0 10.286-8 18.286-18.286 18.286h-182.857c-10.286 0-18.286-8-18.286-18.286v-109.714c0-10.286 8-18.286 18.286-18.286h182.857c10.286 0 18.286 8 18.286 18.286zM292.571 603.428v109.714c0 10.286-8 18.286-18.286 18.286h-182.857c-10.286 0-18.286-8-18.286-18.286v-109.714c0-10.286 8-18.286 18.286-18.286h182.857c10.286 0 18.286 8 18.286 18.286zM585.143 384v109.714c0 10.286-8 18.286-18.286 18.286h-182.857c-10.286 0-18.286-8-18.286-18.286v-109.714c0-10.286 8-18.286 18.286-18.286h182.857c10.286 0 18.286 8 18.286 18.286zM877.714 164.571v109.714c0 10.286-8 18.286-18.286 18.286h-182.857c-10.286 0-18.286-8-18.286-18.286v-109.714c0-10.286 8-18.286 18.286-18.286h182.857c10.286 0 18.286 8 18.286 18.286zM585.143 603.428v109.714c0 10.286-8 18.286-18.286 18.286h-182.857c-10.286 0-18.286-8-18.286-18.286v-109.714c0-10.286 8-18.286 18.286-18.286h182.857c10.286 0 18.286 8 18.286 18.286zM877.714 384v109.714c0 10.286-8 18.286-18.286 18.286h-182.857c-10.286 0-18.286-8-18.286-18.286v-109.714c0-10.286 8-18.286 18.286-18.286h182.857c10.286 0 18.286 8 18.286 18.286zM877.714 603.428v109.714c0 10.286-8 18.286-18.286 18.286h-182.857c-10.286 0-18.286-8-18.286-18.286v-109.714c0-10.286 8-18.286 18.286-18.286h182.857c10.286 0 18.286 8 18.286 18.286zM950.857 786.286v-621.714c0-50.286-41.143-91.429-91.429-91.429h-768c-50.286 0-91.429 41.143-91.429 91.429v621.714c0 50.286 41.143 91.429 91.429 91.429h768c50.286 0 91.429-41.143 91.429-91.429z" />
|
||||
<glyph unicode="" glyph-name="caret-down" horiz-adv-x="585" d="M585.143 548.571c0-9.714-4-18.857-10.857-25.714l-256-256c-6.857-6.857-16-10.857-25.714-10.857s-18.857 4-25.714 10.857l-256 256c-6.857 6.857-10.857 16-10.857 25.714 0 20 16.571 36.571 36.571 36.571h512c20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="caret-up" horiz-adv-x="585" d="M585.143 256c0-20-16.571-36.571-36.571-36.571h-512c-20 0-36.571 16.571-36.571 36.571 0 9.714 4 18.857 10.857 25.714l256 256c6.857 6.857 16 10.857 25.714 10.857s18.857-4 25.714-10.857l256-256c6.857-6.857 10.857-16 10.857-25.714z" />
|
||||
<glyph unicode="" glyph-name="caret-left" horiz-adv-x="402" d="M365.714 694.857v-512c0-20-16.571-36.571-36.571-36.571-9.714 0-18.857 4-25.714 10.857l-256 256c-6.857 6.857-10.857 16-10.857 25.714s4 18.857 10.857 25.714l256 256c6.857 6.857 16 10.857 25.714 10.857 20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="caret-right" horiz-adv-x="329" d="M329.143 438.857c0-9.714-4-18.857-10.857-25.714l-256-256c-6.857-6.857-16-10.857-25.714-10.857-20 0-36.571 16.571-36.571 36.571v512c0 20 16.571 36.571 36.571 36.571 9.714 0 18.857-4 25.714-10.857l256-256c6.857-6.857 10.857-16 10.857-25.714z" />
|
||||
<glyph unicode="" glyph-name="columns" horiz-adv-x="951" d="M91.429 73.143h347.429v658.286h-365.714v-640c0-9.714 8.571-18.286 18.286-18.286zM877.714 91.428v640h-365.714v-658.286h347.429c9.714 0 18.286 8.571 18.286 18.286zM950.857 786.286v-694.857c0-50.286-41.143-91.429-91.429-91.429h-768c-50.286 0-91.429 41.143-91.429 91.429v694.857c0 50.286 41.143 91.429 91.429 91.429h768c50.286 0 91.429-41.143 91.429-91.429z" />
|
||||
<glyph unicode="" glyph-name="sort, unsorted" horiz-adv-x="585" d="M585.143 329.143c0-9.714-4-18.857-10.857-25.714l-256-256c-6.857-6.857-16-10.857-25.714-10.857s-18.857 4-25.714 10.857l-256 256c-6.857 6.857-10.857 16-10.857 25.714 0 20 16.571 36.571 36.571 36.571h512c20 0 36.571-16.571 36.571-36.571zM585.143 548.571c0-20-16.571-36.571-36.571-36.571h-512c-20 0-36.571 16.571-36.571 36.571 0 9.714 4 18.857 10.857 25.714l256 256c6.857 6.857 16 10.857 25.714 10.857s18.857-4 25.714-10.857l256-256c6.857-6.857 10.857-16 10.857-25.714z" />
|
||||
<glyph unicode="" glyph-name="sort-desc, sort-down" horiz-adv-x="585" d="M585.143 329.143c0-9.714-4-18.857-10.857-25.714l-256-256c-6.857-6.857-16-10.857-25.714-10.857s-18.857 4-25.714 10.857l-256 256c-6.857 6.857-10.857 16-10.857 25.714 0 20 16.571 36.571 36.571 36.571h512c20 0 36.571-16.571 36.571-36.571z" />
|
||||
<glyph unicode="" glyph-name="sort-asc, sort-up" horiz-adv-x="585" d="M585.143 548.571c0-20-16.571-36.571-36.571-36.571h-512c-20 0-36.571 16.571-36.571 36.571 0 9.714 4 18.857 10.857 25.714l256 256c6.857 6.857 16 10.857 25.714 10.857s18.857-4 25.714-10.857l256-256c6.857-6.857 10.857-16 10.857-25.714z" />
|
||||
<glyph unicode="" glyph-name="rotate-left, undo" horiz-adv-x="878" d="M877.714 438.857c0-241.714-197.143-438.857-438.857-438.857-130.857 0-254.286 57.714-337.714 158.286-5.714 7.429-5.143 18.286 1.143 24.571l78.286 78.857c4 3.429 9.143 5.143 14.286 5.143 5.143-0.571 10.286-2.857 13.143-6.857 56-72.571 140-113.714 230.857-113.714 161.143 0 292.571 131.429 292.571 292.571s-131.429 292.571-292.571 292.571c-74.857 0-145.714-28.571-198.857-78.286l78.286-78.857c10.857-10.286 13.714-26.286 8-39.429-5.714-13.714-18.857-22.857-33.714-22.857h-256c-20 0-36.571 16.571-36.571 36.571v256c0 14.857 9.143 28 22.857 33.714 13.143 5.714 29.143 2.857 39.429-8l74.286-73.714c80.571 76 189.714 121.143 302.286 121.143 241.714 0 438.857-197.143 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="file-text-o" horiz-adv-x="878" d="M838.857 733.714c21.143-21.143 38.857-63.429 38.857-93.714v-658.286c0-30.286-24.571-54.857-54.857-54.857h-768c-30.286 0-54.857 24.571-54.857 54.857v914.286c0 30.286 24.571 54.857 54.857 54.857h512c30.286 0 72.571-17.714 93.714-38.857zM585.143 873.143v-214.857h214.857c-3.429 9.714-8.571 19.429-12.571 23.429l-178.857 178.857c-4 4-13.714 9.143-23.429 12.571zM804.571 0v585.143h-237.714c-30.286 0-54.857 24.571-54.857 54.857v237.714h-438.857v-877.714h731.429zM219.429 493.714c0 10.286 8 18.286 18.286 18.286h402.286c10.286 0 18.286-8 18.286-18.286v-36.571c0-10.286-8-18.286-18.286-18.286h-402.286c-10.286 0-18.286 8-18.286 18.286v36.571zM640 365.714c10.286 0 18.286-8 18.286-18.286v-36.571c0-10.286-8-18.286-18.286-18.286h-402.286c-10.286 0-18.286 8-18.286 18.286v36.571c0 10.286 8 18.286 18.286 18.286h402.286zM640 219.428c10.286 0 18.286-8 18.286-18.286v-36.571c0-10.286-8-18.286-18.286-18.286h-402.286c-10.286 0-18.286 8-18.286 18.286v36.571c0 10.286 8 18.286 18.286 18.286h402.286z" />
|
||||
<glyph unicode="" glyph-name="plus-square" horiz-adv-x="878" d="M731.429 402.286v73.143c0 20-16.571 36.571-36.571 36.571h-182.857v182.857c0 20-16.571 36.571-36.571 36.571h-73.143c-20 0-36.571-16.571-36.571-36.571v-182.857h-182.857c-20 0-36.571-16.571-36.571-36.571v-73.143c0-20 16.571-36.571 36.571-36.571h182.857v-182.857c0-20 16.571-36.571 36.571-36.571h73.143c20 0 36.571 16.571 36.571 36.571v182.857h182.857c20 0 36.571 16.571 36.571 36.571zM877.714 713.143v-548.571c0-90.857-73.714-164.571-164.571-164.571h-548.571c-90.857 0-164.571 73.714-164.571 164.571v548.571c0 90.857 73.714 164.571 164.571 164.571h548.571c90.857 0 164.571-73.714 164.571-164.571z" />
|
||||
<glyph unicode="" glyph-name="angle-double-left" horiz-adv-x="603" d="M358.286 164.571c0-4.571-2.286-9.714-5.714-13.143l-28.571-28.571c-3.429-3.429-8.571-5.714-13.143-5.714s-9.714 2.286-13.143 5.714l-266.286 266.286c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l266.286 266.286c3.429 3.429 8.571 5.714 13.143 5.714s9.714-2.286 13.143-5.714l28.571-28.571c3.429-3.429 5.714-8.571 5.714-13.143s-2.286-9.714-5.714-13.143l-224.571-224.571 224.571-224.571c3.429-3.429 5.714-8.571 5.714-13.143zM577.714 164.571c0-4.571-2.286-9.714-5.714-13.143l-28.571-28.571c-3.429-3.429-8.571-5.714-13.143-5.714s-9.714 2.286-13.143 5.714l-266.286 266.286c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l266.286 266.286c3.429 3.429 8.571 5.714 13.143 5.714s9.714-2.286 13.143-5.714l28.571-28.571c3.429-3.429 5.714-8.571 5.714-13.143s-2.286-9.714-5.714-13.143l-224.571-224.571 224.571-224.571c3.429-3.429 5.714-8.571 5.714-13.143z" />
|
||||
<glyph unicode="" glyph-name="angle-double-right" horiz-adv-x="567" d="M340 402.286c0-4.571-2.286-9.714-5.714-13.143l-266.286-266.286c-3.429-3.429-8.571-5.714-13.143-5.714s-9.714 2.286-13.143 5.714l-28.571 28.571c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l224.571 224.571-224.571 224.571c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l28.571 28.571c3.429 3.429 8.571 5.714 13.143 5.714s9.714-2.286 13.143-5.714l266.286-266.286c3.429-3.429 5.714-8.571 5.714-13.143zM559.429 402.286c0-4.571-2.286-9.714-5.714-13.143l-266.286-266.286c-3.429-3.429-8.571-5.714-13.143-5.714s-9.714 2.286-13.143 5.714l-28.571 28.571c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l224.571 224.571-224.571 224.571c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l28.571 28.571c3.429 3.429 8.571 5.714 13.143 5.714s9.714-2.286 13.143-5.714l266.286-266.286c3.429-3.429 5.714-8.571 5.714-13.143z" />
|
||||
<glyph unicode="" glyph-name="angle-double-up" horiz-adv-x="658" d="M614.286 201.143c0-4.571-2.286-9.714-5.714-13.143l-28.571-28.571c-3.429-3.429-8-5.714-13.143-5.714-4.571 0-9.714 2.286-13.143 5.714l-224.571 224.571-224.571-224.571c-3.429-3.429-8.571-5.714-13.143-5.714s-9.714 2.286-13.143 5.714l-28.571 28.571c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l266.286 266.286c3.429 3.429 8.571 5.714 13.143 5.714s9.714-2.286 13.143-5.714l266.286-266.286c3.429-3.429 5.714-8.571 5.714-13.143zM614.286 420.571c0-4.571-2.286-9.714-5.714-13.143l-28.571-28.571c-3.429-3.429-8-5.714-13.143-5.714-4.571 0-9.714 2.286-13.143 5.714l-224.571 224.571-224.571-224.571c-3.429-3.429-8.571-5.714-13.143-5.714s-9.714 2.286-13.143 5.714l-28.571 28.571c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l266.286 266.286c3.429 3.429 8.571 5.714 13.143 5.714s9.714-2.286 13.143-5.714l266.286-266.286c3.429-3.429 5.714-8.571 5.714-13.143z" />
|
||||
<glyph unicode="" glyph-name="angle-double-down" horiz-adv-x="658" d="M614.286 457.143c0-4.571-2.286-9.714-5.714-13.143l-266.286-266.286c-3.429-3.429-8.571-5.714-13.143-5.714s-9.714 2.286-13.143 5.714l-266.286 266.286c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l28.571 28.571c3.429 3.429 8 5.714 13.143 5.714 4.571 0 9.714-2.286 13.143-5.714l224.571-224.571 224.571 224.571c3.429 3.429 8.571 5.714 13.143 5.714s9.714-2.286 13.143-5.714l28.571-28.571c3.429-3.429 5.714-8.571 5.714-13.143zM614.286 676.571c0-4.571-2.286-9.714-5.714-13.143l-266.286-266.286c-3.429-3.429-8.571-5.714-13.143-5.714s-9.714 2.286-13.143 5.714l-266.286 266.286c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l28.571 28.571c3.429 3.429 8 5.714 13.143 5.714 4.571 0 9.714-2.286 13.143-5.714l224.571-224.571 224.571 224.571c3.429 3.429 8.571 5.714 13.143 5.714s9.714-2.286 13.143-5.714l28.571-28.571c3.429-3.429 5.714-8.571 5.714-13.143z" />
|
||||
<glyph unicode="" glyph-name="angle-left" horiz-adv-x="384" d="M358.286 640c0-4.571-2.286-9.714-5.714-13.143l-224.571-224.571 224.571-224.571c3.429-3.429 5.714-8.571 5.714-13.143s-2.286-9.714-5.714-13.143l-28.571-28.571c-3.429-3.429-8.571-5.714-13.143-5.714s-9.714 2.286-13.143 5.714l-266.286 266.286c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l266.286 266.286c3.429 3.429 8.571 5.714 13.143 5.714s9.714-2.286 13.143-5.714l28.571-28.571c3.429-3.429 5.714-8 5.714-13.143z" />
|
||||
<glyph unicode="" glyph-name="angle-right" horiz-adv-x="347" d="M340 402.286c0-4.571-2.286-9.714-5.714-13.143l-266.286-266.286c-3.429-3.429-8.571-5.714-13.143-5.714s-9.714 2.286-13.143 5.714l-28.571 28.571c-3.429 3.429-5.714 8-5.714 13.143 0 4.571 2.286 9.714 5.714 13.143l224.571 224.571-224.571 224.571c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l28.571 28.571c3.429 3.429 8.571 5.714 13.143 5.714s9.714-2.286 13.143-5.714l266.286-266.286c3.429-3.429 5.714-8.571 5.714-13.143z" />
|
||||
<glyph unicode="" glyph-name="angle-up" horiz-adv-x="658" d="M614.286 274.286c0-4.571-2.286-9.714-5.714-13.143l-28.571-28.571c-3.429-3.429-8-5.714-13.143-5.714-4.571 0-9.714 2.286-13.143 5.714l-224.571 224.571-224.571-224.571c-3.429-3.429-8.571-5.714-13.143-5.714s-9.714 2.286-13.143 5.714l-28.571 28.571c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l266.286 266.286c3.429 3.429 8.571 5.714 13.143 5.714s9.714-2.286 13.143-5.714l266.286-266.286c3.429-3.429 5.714-8.571 5.714-13.143z" />
|
||||
<glyph unicode="" glyph-name="angle-down" horiz-adv-x="658" d="M614.286 530.286c0-4.571-2.286-9.714-5.714-13.143l-266.286-266.286c-3.429-3.429-8.571-5.714-13.143-5.714s-9.714 2.286-13.143 5.714l-266.286 266.286c-3.429 3.429-5.714 8.571-5.714 13.143s2.286 9.714 5.714 13.143l28.571 28.571c3.429 3.429 8 5.714 13.143 5.714 4.571 0 9.714-2.286 13.143-5.714l224.571-224.571 224.571 224.571c3.429 3.429 8.571 5.714 13.143 5.714s9.714-2.286 13.143-5.714l28.571-28.571c3.429-3.429 5.714-8.571 5.714-13.143z" />
|
||||
<glyph unicode="" glyph-name="circle-o" horiz-adv-x="878" d="M438.857 749.714c-171.429 0-310.857-139.429-310.857-310.857s139.429-310.857 310.857-310.857 310.857 139.429 310.857 310.857-139.429 310.857-310.857 310.857zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857v0c242.286 0 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="spinner" d="M300.571 154.286c0-40-32.571-73.143-73.143-73.143-40 0-73.143 33.143-73.143 73.143 0 40.571 33.143 73.143 73.143 73.143 40.571 0 73.143-32.571 73.143-73.143zM585.143 36.571c0-40.571-32.571-73.143-73.143-73.143s-73.143 32.571-73.143 73.143 32.571 73.143 73.143 73.143 73.143-32.571 73.143-73.143zM182.857 438.857c0-40.571-32.571-73.143-73.143-73.143s-73.143 32.571-73.143 73.143 32.571 73.143 73.143 73.143 73.143-32.571 73.143-73.143zM869.714 154.286c0-40-33.143-73.143-73.143-73.143-40.571 0-73.143 33.143-73.143 73.143 0 40.571 32.571 73.143 73.143 73.143 40 0 73.143-32.571 73.143-73.143zM318.857 723.428c0-50.286-41.143-91.429-91.429-91.429s-91.429 41.143-91.429 91.429 41.143 91.429 91.429 91.429 91.429-41.143 91.429-91.429zM987.429 438.857c0-40.571-32.571-73.143-73.143-73.143s-73.143 32.571-73.143 73.143 32.571 73.143 73.143 73.143 73.143-32.571 73.143-73.143zM621.714 841.143c0-60.571-49.143-109.714-109.714-109.714s-109.714 49.143-109.714 109.714 49.143 109.714 109.714 109.714 109.714-49.143 109.714-109.714zM924.571 723.428c0-70.857-57.714-128-128-128-70.857 0-128 57.143-128 128 0 70.286 57.143 128 128 128 70.286 0 128-57.714 128-128z" />
|
||||
<glyph unicode="" glyph-name="circle" horiz-adv-x="878" d="M877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="mail-reply, reply" d="M1024 310.857c0-80-40-184.571-72.571-257.714-6.286-13.143-12.571-31.429-21.143-43.429-4-5.714-8-9.714-16-9.714-11.429 0-18.286 9.143-18.286 20 0 9.143 2.286 19.429 2.857 28.571 1.714 23.429 2.857 46.857 2.857 70.286 0 272.571-161.714 320-408 320h-128v-146.286c0-20-16.571-36.571-36.571-36.571-9.714 0-18.857 4-25.714 10.857l-292.571 292.571c-6.857 6.857-10.857 16-10.857 25.714s4 18.857 10.857 25.714l292.571 292.571c6.857 6.857 16 10.857 25.714 10.857 20 0 36.571-16.571 36.571-36.571v-146.286h128c187.429 0 420.571-33.143 500-230.286 24-60.571 30.286-126.286 30.286-190.286z" />
|
||||
<glyph unicode="" glyph-name="folder-o" horiz-adv-x="951" d="M877.714 201.143v402.286c0 30.286-24.571 54.857-54.857 54.857h-402.286c-30.286 0-54.857 24.571-54.857 54.857v36.571c0 30.286-24.571 54.857-54.857 54.857h-182.857c-30.286 0-54.857-24.571-54.857-54.857v-548.571c0-30.286 24.571-54.857 54.857-54.857h694.857c30.286 0 54.857 24.571 54.857 54.857zM950.857 603.428v-402.286c0-70.286-57.714-128-128-128h-694.857c-70.286 0-128 57.714-128 128v548.571c0 70.286 57.714 128 128 128h182.857c70.286 0 128-57.714 128-128v-18.286h384c70.286 0 128-57.714 128-128z" />
|
||||
<glyph unicode="" glyph-name="folder-open-o" horiz-adv-x="1091" d="M1017.714 418.857c0 16-17.714 20-30.286 20h-621.714c-30.286 0-70.286-18.857-89.714-42.286l-168-207.429c-5.143-6.857-10.286-14.286-10.286-22.857 0-16 17.714-20 30.286-20h621.714c30.286 0 70.286 18.857 89.714 42.857l168 207.429c5.143 6.286 10.286 13.714 10.286 22.286zM365.714 512h438.857v91.429c0 30.286-24.571 54.857-54.857 54.857h-329.143c-30.286 0-54.857 24.571-54.857 54.857v36.571c0 30.286-24.571 54.857-54.857 54.857h-182.857c-30.286 0-54.857-24.571-54.857-54.857v-487.429l146.286 180c33.143 40.571 94.286 69.714 146.286 69.714zM1090.857 418.857c0-25.143-10.857-49.143-26.286-68.571l-168.571-207.429c-32.571-40-94.857-69.714-146.286-69.714h-621.714c-70.286 0-128 57.714-128 128v548.571c0 70.286 57.714 128 128 128h182.857c70.286 0 128-57.714 128-128v-18.286h310.857c70.286 0 128-57.714 128-128v-91.429h109.714c38.857 0 77.714-17.714 94.857-54.286 5.714-12 8.571-25.143 8.571-38.857z" />
|
||||
<glyph unicode="" glyph-name="keyboard-o" horiz-adv-x="1097" d="M219.429 283.428v-54.857c0-5.143-4-9.143-9.143-9.143h-54.857c-5.143 0-9.143 4-9.143 9.143v54.857c0 5.143 4 9.143 9.143 9.143h54.857c5.143 0 9.143-4 9.143-9.143zM292.571 429.714v-54.857c0-5.143-4-9.143-9.143-9.143h-128c-5.143 0-9.143 4-9.143 9.143v54.857c0 5.143 4 9.143 9.143 9.143h128c5.143 0 9.143-4 9.143-9.143zM219.429 576v-54.857c0-5.143-4-9.143-9.143-9.143h-54.857c-5.143 0-9.143 4-9.143 9.143v54.857c0 5.143 4 9.143 9.143 9.143h54.857c5.143 0 9.143-4 9.143-9.143zM804.571 283.428v-54.857c0-5.143-4-9.143-9.143-9.143h-493.714c-5.143 0-9.143 4-9.143 9.143v54.857c0 5.143 4 9.143 9.143 9.143h493.714c5.143 0 9.143-4 9.143-9.143zM438.857 429.714v-54.857c0-5.143-4-9.143-9.143-9.143h-54.857c-5.143 0-9.143 4-9.143 9.143v54.857c0 5.143 4 9.143 9.143 9.143h54.857c5.143 0 9.143-4 9.143-9.143zM365.714 576v-54.857c0-5.143-4-9.143-9.143-9.143h-54.857c-5.143 0-9.143 4-9.143 9.143v54.857c0 5.143 4 9.143 9.143 9.143h54.857c5.143 0 9.143-4 9.143-9.143zM585.143 429.714v-54.857c0-5.143-4-9.143-9.143-9.143h-54.857c-5.143 0-9.143 4-9.143 9.143v54.857c0 5.143 4 9.143 9.143 9.143h54.857c5.143 0 9.143-4 9.143-9.143zM512 576v-54.857c0-5.143-4-9.143-9.143-9.143h-54.857c-5.143 0-9.143 4-9.143 9.143v54.857c0 5.143 4 9.143 9.143 9.143h54.857c5.143 0 9.143-4 9.143-9.143zM731.429 429.714v-54.857c0-5.143-4-9.143-9.143-9.143h-54.857c-5.143 0-9.143 4-9.143 9.143v54.857c0 5.143 4 9.143 9.143 9.143h54.857c5.143 0 9.143-4 9.143-9.143zM950.857 283.428v-54.857c0-5.143-4-9.143-9.143-9.143h-54.857c-5.143 0-9.143 4-9.143 9.143v54.857c0 5.143 4 9.143 9.143 9.143h54.857c5.143 0 9.143-4 9.143-9.143zM658.286 576v-54.857c0-5.143-4-9.143-9.143-9.143h-54.857c-5.143 0-9.143 4-9.143 9.143v54.857c0 5.143 4 9.143 9.143 9.143h54.857c5.143 0 9.143-4 9.143-9.143zM804.571 576v-54.857c0-5.143-4-9.143-9.143-9.143h-54.857c-5.143 0-9.143 4-9.143 9.143v54.857c0 5.143 4 9.143 9.143 9.143h54.857c5.143 0 9.143-4 9.143-9.143zM950.857 576v-201.143c0-5.143-4-9.143-9.143-9.143h-128c-5.143 0-9.143 4-9.143 9.143v54.857c0 5.143 4 9.143 9.143 9.143h64v137.143c0 5.143 4 9.143 9.143 9.143h54.857c5.143 0 9.143-4 9.143-9.143zM1024 146.286v512h-950.857v-512h950.857zM1097.143 658.286v-512c0-40.571-32.571-73.143-73.143-73.143h-950.857c-40.571 0-73.143 32.571-73.143 73.143v512c0 40.571 32.571 73.143 73.143 73.143h950.857c40.571 0 73.143-32.571 73.143-73.143z" />
|
||||
<glyph unicode="" glyph-name="terminal" horiz-adv-x="957" d="M334.286 389.143l-266.286-266.286c-7.429-7.429-18.857-7.429-26.286 0l-28.571 28.571c-7.429 7.429-7.429 18.857 0 26.286l224.571 224.571-224.571 224.571c-7.429 7.429-7.429 18.857 0 26.286l28.571 28.571c7.429 7.429 18.857 7.429 26.286 0l266.286-266.286c7.429-7.429 7.429-18.857 0-26.286zM950.857 128v-36.571c0-10.286-8-18.286-18.286-18.286h-548.571c-10.286 0-18.286 8-18.286 18.286v36.571c0 10.286 8 18.286 18.286 18.286h548.571c10.286 0 18.286-8 18.286-18.286z" />
|
||||
<glyph unicode="" glyph-name="code" horiz-adv-x="1097" d="M352.571 151.428l-28.571-28.571c-7.429-7.429-18.857-7.429-26.286 0l-266.286 266.286c-7.429 7.429-7.429 18.857 0 26.286l266.286 266.286c7.429 7.429 18.857 7.429 26.286 0l28.571-28.571c7.429-7.429 7.429-18.857 0-26.286l-224.571-224.571 224.571-224.571c7.429-7.429 7.429-18.857 0-26.286zM690.286 761.143l-213.143-737.714c-2.857-9.714-13.143-15.429-22.286-12.571l-35.429 9.714c-9.714 2.857-15.429 13.143-12.571 22.857l213.143 737.714c2.857 9.714 13.143 15.429 22.286 12.571l35.429-9.714c9.714-2.857 15.429-13.143 12.571-22.857zM1065.714 389.143l-266.286-266.286c-7.429-7.429-18.857-7.429-26.286 0l-28.571 28.571c-7.429 7.429-7.429 18.857 0 26.286l224.571 224.571-224.571 224.571c-7.429 7.429-7.429 18.857 0 26.286l28.571 28.571c7.429 7.429 18.857 7.429 26.286 0l266.286-266.286c7.429-7.429 7.429-18.857 0-26.286z" />
|
||||
<glyph unicode="" glyph-name="mail-reply-all, reply-all" horiz-adv-x="1021" d="M365.714 332.571v-40c0-14.857-9.143-28-22.286-33.714-4.571-1.714-9.714-2.857-14.286-2.857-9.714 0-18.857 3.429-25.714 10.857l-292.571 292.571c-14.286 14.286-14.286 37.143 0 51.429l292.571 292.571c10.286 10.857 26.286 13.714 40 8 13.143-5.714 22.286-18.857 22.286-33.714v-39.429l-226.857-227.429c-14.286-14.286-14.286-37.143 0-51.429zM1024 310.857c0-118.857-89.714-293.714-93.714-301.143-2.857-6.286-9.143-9.714-16-9.714-1.714 0-3.429 0-5.143 0.571-8.571 2.857-13.714 10.857-13.143 19.429 16.571 156-2.857 258.857-60.571 322.857-48.571 54.286-127.429 83.429-250.286 93.143v-143.429c0-14.857-9.143-28-22.286-33.714-4.571-1.714-9.714-2.857-14.286-2.857-9.714 0-18.857 3.429-25.714 10.857l-292.571 292.571c-14.286 14.286-14.286 37.143 0 51.429l292.571 292.571c10.286 10.857 26.286 13.714 40 8 13.143-5.714 22.286-18.857 22.286-33.714v-149.714c157.714-10.857 270.286-52.571 342.286-126.286 86.286-88.571 96.571-208.571 96.571-290.857z" />
|
||||
<glyph unicode="" glyph-name="star-half-empty, star-half-full, star-half-o" horiz-adv-x="951" d="M677.714 404l146.857 142.857-241.143 35.429-17.143 34.286-90.857 184v-550.286l33.714-17.714 181.714-96-34.286 202.857-6.857 37.714zM936 553.714l-207.429-202.286 49.143-285.714c4-25.143-5.143-40-22.857-40-6.286 0-14.286 2.286-22.857 6.857l-256.571 134.857-256.571-134.857c-8.571-4.571-16.571-6.857-22.857-6.857-17.714 0-26.857 14.857-22.857 40l49.143 285.714-208 202.286c-24.571 24.571-16.571 48.571 17.714 53.714l286.857 41.714 128.571 260c7.429 15.429 17.714 23.429 28 23.429v0c10.286 0 20-8 28-23.429l128.571-260 286.857-41.714c34.286-5.143 42.286-29.143 17.143-53.714z" />
|
||||
<glyph unicode="" glyph-name="crop" horiz-adv-x="953" d="M318.286 219.428h340v340zM292.571 245.143l340 340h-340v-340zM950.857 201.143v-109.714c0-10.286-8-18.286-18.286-18.286h-128v-128c0-10.286-8-18.286-18.286-18.286h-109.714c-10.286 0-18.286 8-18.286 18.286v128h-493.714c-10.286 0-18.286 8-18.286 18.286v493.714h-128c-10.286 0-18.286 8-18.286 18.286v109.714c0 10.286 8 18.286 18.286 18.286h128v128c0 10.286 8 18.286 18.286 18.286h109.714c10.286 0 18.286-8 18.286-18.286v-128h486.286l140.571 141.143c7.429 6.857 18.857 6.857 26.286 0 6.857-7.429 6.857-18.857 0-26.286l-141.143-140.571v-486.286h128c10.286 0 18.286-8 18.286-18.286z" />
|
||||
<glyph unicode="" glyph-name="code-fork" horiz-adv-x="585" d="M164.571 109.714c0 30.286-24.571 54.857-54.857 54.857s-54.857-24.571-54.857-54.857 24.571-54.857 54.857-54.857 54.857 24.571 54.857 54.857zM164.571 768c0 30.286-24.571 54.857-54.857 54.857s-54.857-24.571-54.857-54.857 24.571-54.857 54.857-54.857 54.857 24.571 54.857 54.857zM530.286 694.857c0 30.286-24.571 54.857-54.857 54.857s-54.857-24.571-54.857-54.857 24.571-54.857 54.857-54.857 54.857 24.571 54.857 54.857zM585.143 694.857c0-40.571-22.286-76-54.857-94.857-1.714-206.286-148-252-245.143-282.857-90.857-28.571-120.571-42.286-120.571-97.714v-14.857c32.571-18.857 54.857-54.286 54.857-94.857 0-60.571-49.143-109.714-109.714-109.714s-109.714 49.143-109.714 109.714c0 40.571 22.286 76 54.857 94.857v468.571c-32.571 18.857-54.857 54.286-54.857 94.857 0 60.571 49.143 109.714 109.714 109.714s109.714-49.143 109.714-109.714c0-40.571-22.286-76-54.857-94.857v-284c29.143 14.286 60 24 88 32.571 106.286 33.714 166.857 58.857 168 178.286-32.571 18.857-54.857 54.286-54.857 94.857 0 60.571 49.143 109.714 109.714 109.714s109.714-49.143 109.714-109.714z" />
|
||||
<glyph unicode="" glyph-name="chain-broken, unlink" horiz-adv-x="951" d="M250.857 224.571l-146.286-146.286c-4-3.429-8.571-5.143-13.143-5.143s-9.143 1.714-13.143 5.143c-6.857 7.429-6.857 18.857 0 26.286l146.286 146.286c7.429 6.857 18.857 6.857 26.286 0 6.857-7.429 6.857-18.857 0-26.286zM347.429 201.143v-182.857c0-10.286-8-18.286-18.286-18.286s-18.286 8-18.286 18.286v182.857c0 10.286 8 18.286 18.286 18.286s18.286-8 18.286-18.286zM219.429 329.143c0-10.286-8-18.286-18.286-18.286h-182.857c-10.286 0-18.286 8-18.286 18.286s8 18.286 18.286 18.286h182.857c10.286 0 18.286-8 18.286-18.286zM941.714 256c0-44-17.143-85.143-48.571-116l-84-83.429c-30.857-30.857-72-47.429-116-47.429s-85.714 17.143-116.571 48.571l-190.857 191.429c-9.714 9.714-17.143 20.571-24 32l136.571 10.286 156-156.571c20.571-20.571 57.143-21.143 77.714-0.571l84 83.429c10.286 10.286 16 24 16 38.286 0 14.857-5.714 28.571-16 38.857l-156.571 157.143 10.286 136.571c11.429-6.857 22.286-14.286 32-24l192-192c30.857-31.429 48-72.571 48-116.571zM589.143 669.714l-136.571-10.286-156 156.571c-10.286 10.286-24 16-38.857 16s-28.571-5.714-38.857-15.429l-84-83.429c-10.286-10.286-16-24-16-38.286 0-14.857 5.714-28.571 16-38.857l156.571-156.571-10.286-137.143c-11.429 6.857-22.286 14.286-32 24l-192 192c-30.857 31.429-48 72.571-48 116.571s17.143 85.143 48.571 116l84 83.429c30.857 30.857 72 47.429 116 47.429s85.714-17.143 116.571-48.571l190.857-191.429c9.714-9.714 17.143-20.571 24-32zM950.857 621.714c0-10.286-8-18.286-18.286-18.286h-182.857c-10.286 0-18.286 8-18.286 18.286s8 18.286 18.286 18.286h182.857c10.286 0 18.286-8 18.286-18.286zM640 932.571v-182.857c0-10.286-8-18.286-18.286-18.286s-18.286 8-18.286 18.286v182.857c0 10.286 8 18.286 18.286 18.286s18.286-8 18.286-18.286zM872.571 846.286l-146.286-146.286c-4-3.429-8.571-5.143-13.143-5.143s-9.143 1.714-13.143 5.143c-6.857 7.429-6.857 18.857 0 26.286l146.286 146.286c7.429 6.857 18.857 6.857 26.286 0 6.857-7.429 6.857-18.857 0-26.286z" />
|
||||
<glyph unicode="" glyph-name="question" horiz-adv-x="635" d="M402.286 233.143v-137.143c0-12.571-10.286-22.857-22.857-22.857h-137.143c-12.571 0-22.857 10.286-22.857 22.857v137.143c0 12.571 10.286 22.857 22.857 22.857h137.143c12.571 0 22.857-10.286 22.857-22.857zM582.857 576c0-108.571-73.714-150.286-128-180.571-33.714-19.429-54.857-58.857-54.857-75.429v0c0-12.571-9.714-27.429-22.857-27.429h-137.143c-12.571 0-20.571 19.429-20.571 32v25.714c0 69.143 68.571 128.571 118.857 151.429 44 20 62.286 38.857 62.286 75.429 0 32-41.714 60.571-88 60.571-25.714 0-49.143-8-61.714-16.571-13.714-9.714-27.429-23.429-61.143-65.714-4.571-5.714-11.429-9.143-17.714-9.143-5.143 0-9.714 1.714-14.286 4.571l-93.714 71.429c-9.714 7.429-12 20-5.714 30.286 61.714 102.286 148.571 152 265.143 152 122.286 0 259.429-97.714 259.429-228.571z" />
|
||||
<glyph unicode="" glyph-name="info" d="M511.999-9.143c-88.606 0-175.222 26.276-248.895 75.5-73.673 49.229-131.095 119.199-165.002 201.057-33.908 81.864-42.78 171.94-25.494 258.844s59.954 166.729 122.608 229.383c62.654 62.654 142.48 105.322 229.383 122.608s176.98 8.414 258.844-25.494c81.859-33.908 151.828-91.329 201.057-165.002 49.224-73.673 75.5-160.29 75.5-248.895 0-118.815-47.201-232.765-131.215-316.785-84.019-84.014-197.97-131.215-316.785-131.215zM511.999 822.857c-75.948 0-150.19-22.521-213.339-64.716s-112.367-102.167-141.431-172.334c-29.064-70.167-36.668-147.375-21.852-221.866 14.817-74.486 51.389-142.909 105.093-196.613s122.126-90.276 196.615-105.093c74.488-14.817 151.699-7.214 221.863 21.852 70.17 29.066 130.14 78.285 172.334 141.43 42.194 63.15 64.717 137.39 64.717 213.34 0 101.843-40.458 199.515-112.471 271.529s-169.687 112.471-271.529 112.471zM495.999 598.857c26.511 0 48 21.49 48 48s-21.489 48-48 48c-26.51 0-48-21.49-48-48s21.49-48 48-48zM671.999 246.857c0-8.489-3.369-16.625-9.375-22.625-6.001-6.006-14.136-9.375-22.625-9.375h-256c-8.487 0-16.626 3.369-22.627 9.375-6.001 6.001-9.373 14.136-9.373 22.625s3.372 16.625 9.373 22.625c6.001 6.006 14.14 9.375 22.627 9.375h96v192h-64c-8.487 0-16.626 3.372-22.627 9.373s-9.373 14.14-9.373 22.627c0 8.487 3.372 16.626 9.373 22.627s14.14 9.373 22.627 9.373h96c8.489 0 16.625-3.372 22.625-9.373 6.006-6.001 9.375-14.14 9.375-22.627v-224h96c8.489 0 16.625-3.369 22.625-9.375 6.006-6.001 9.375-14.136 9.375-22.625z" />
|
||||
<glyph unicode="" glyph-name="exclamation" d="M917.119 6.517h-812.798c-5.469 0.020-10.84 1.444-15.602 4.137-4.762 2.688-8.755 6.554-11.598 11.223-2.809 4.864-4.287 10.383-4.287 16s1.479 11.136 4.287 16l406.4 799.999c2.685 5.242 6.765 9.64 11.79 12.712s10.8 4.697 16.688 4.697c5.893 0 11.668-1.625 16.691-4.697 5.028-3.072 9.103-7.471 11.791-12.712l406.4-799.999c2.806-4.864 4.285-10.383 4.285-16s-1.48-11.136-4.285-16c-3.057-5.059-7.46-9.175-12.713-11.884-5.253-2.714-11.151-3.917-17.050-3.476zM156.481 70.517h708.481l-354.243 697.279-354.238-697.279zM510.709 134.517c26.511 0 48 21.489 48 48s-21.489 48-48 48c-26.51 0-48-21.489-48-48s21.49-48 48-48zM510.709 294.517c-8.487 0-16.626 3.369-22.627 9.375-6.001 6.001-9.373 14.136-9.373 22.625v224c0 8.487 3.372 16.626 9.373 22.627s14.14 9.373 22.627 9.373c8.489 0 16.625-3.372 22.625-9.373 6.006-6.001 9.375-14.14 9.375-22.627v-224c0-8.489-3.369-16.625-9.375-22.625-6.001-6.006-14.136-9.375-22.625-9.375z" />
|
||||
<glyph unicode="" glyph-name="rocket" horiz-adv-x="967" d="M822.857 694.857c0 30.286-24.571 54.857-54.857 54.857s-54.857-24.571-54.857-54.857 24.571-54.857 54.857-54.857 54.857 24.571 54.857 54.857zM950.857 859.428c0-189.714-52.571-316-188-452-33.143-32.571-70.857-66.286-111.429-100.571l-11.429-216.571c-0.571-5.714-4-11.429-9.143-14.857l-219.429-128c-2.857-1.714-5.714-2.286-9.143-2.286-4.571 0-9.143 1.714-13.143 5.143l-36.571 36.571c-4.571 5.143-6.286 12-4.571 18.286l48.571 157.714-160.571 160.571-157.714-48.571c-1.714-0.571-3.429-0.571-5.143-0.571-4.571 0-9.714 1.714-13.143 5.143l-36.571 36.571c-5.714 6.286-6.857 15.429-2.857 22.286l128 219.429c3.429 5.143 9.143 8.571 14.857 9.143l216.571 11.429c34.286 40.571 68 78.286 100.571 111.429 142.857 142.286 252 188 450.857 188 10.286 0 19.429-8 19.429-18.286z" />
|
||||
<glyph unicode="" glyph-name="maxcdn" horiz-adv-x="1013" d="M997.143 509.143l-93.714-436h-190.857l101.714 475.429c4.571 20 1.714 38.286-8.571 50.286-9.714 12-26.857 18.857-47.429 18.857h-96.571l-116.571-544.571h-190.857l116.571 544.571h-163.429l-116.571-544.571h-190.857l116.571 544.571-87.429 186.857h729.143c77.143 0 147.429-32 192.571-88 45.714-56 62.286-132 46.286-207.429z" />
|
||||
<glyph unicode="" glyph-name="chevron-circle-left" horiz-adv-x="878" d="M519.429 153.714l58.286 58.286c14.286 14.286 14.286 37.143 0 51.429l-175.429 175.429 175.429 175.429c14.286 14.286 14.286 37.143 0 51.429l-58.286 58.286c-14.286 14.286-37.143 14.286-51.429 0l-259.429-259.429c-14.286-14.286-14.286-37.143 0-51.429l259.429-259.429c14.286-14.286 37.143-14.286 51.429 0zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="chevron-circle-right" horiz-adv-x="878" d="M409.714 153.714l259.429 259.429c14.286 14.286 14.286 37.143 0 51.429l-259.429 259.429c-14.286 14.286-37.143 14.286-51.429 0l-58.286-58.286c-14.286-14.286-14.286-37.143 0-51.429l175.429-175.429-175.429-175.429c-14.286-14.286-14.286-37.143 0-51.429l58.286-58.286c14.286-14.286 37.143-14.286 51.429 0zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="chevron-circle-up" horiz-adv-x="878" d="M665.714 300l58.286 58.286c14.286 14.286 14.286 37.143 0 51.429l-259.429 259.429c-14.286 14.286-37.143 14.286-51.429 0l-259.429-259.429c-14.286-14.286-14.286-37.143 0-51.429l58.286-58.286c14.286-14.286 37.143-14.286 51.429 0l175.429 175.429 175.429-175.429c14.286-14.286 37.143-14.286 51.429 0zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="chevron-circle-down" horiz-adv-x="878" d="M464.571 208.571l259.429 259.429c14.286 14.286 14.286 37.143 0 51.429l-58.286 58.286c-14.286 14.286-37.143 14.286-51.429 0l-175.429-175.429-175.429 175.429c-14.286 14.286-37.143 14.286-51.429 0l-58.286-58.286c-14.286-14.286-14.286-37.143 0-51.429l259.429-259.429c14.286-14.286 37.143-14.286 51.429 0zM877.714 438.857c0-242.286-196.571-438.857-438.857-438.857s-438.857 196.571-438.857 438.857 196.571 438.857 438.857 438.857 438.857-196.571 438.857-438.857z" />
|
||||
<glyph unicode="" glyph-name="ellipsis-h" horiz-adv-x="805" d="M219.429 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-109.714c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h109.714c30.286 0 54.857-24.571 54.857-54.857zM512 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-109.714c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h109.714c30.286 0 54.857-24.571 54.857-54.857zM804.571 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-109.714c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h109.714c30.286 0 54.857-24.571 54.857-54.857z" />
|
||||
<glyph unicode="" glyph-name="long-arrow-down" horiz-adv-x="439" d="M437.143 208.571c2.857-6.857 1.714-14.286-2.857-20l-200-219.429c-3.429-3.429-8-5.714-13.143-5.714v0c-5.143 0-10.286 2.286-13.714 5.714l-202.857 219.429c-4.571 5.714-5.714 13.143-2.857 20 2.857 6.286 9.143 10.857 16.571 10.857h128v713.143c0 10.286 8 18.286 18.286 18.286h109.714c10.286 0 18.286-8 18.286-18.286v-713.143h128c7.429 0 13.714-4 16.571-10.857z" />
|
||||
<glyph unicode="" glyph-name="long-arrow-up" horiz-adv-x="439" d="M437.143 669.143c-2.857-6.286-9.143-10.857-16.571-10.857h-128v-713.143c0-10.286-8-18.286-18.286-18.286h-109.714c-10.286 0-18.286 8-18.286 18.286v713.143h-128c-7.429 0-13.714 4-16.571 10.857s-1.714 14.286 2.857 20l200 219.429c3.429 3.429 8 5.714 13.143 5.714v0c5.143 0 10.286-2.286 13.714-5.714l202.857-219.429c4.571-5.714 5.714-13.143 2.857-20z" />
|
||||
<glyph unicode="" glyph-name="long-arrow-left" horiz-adv-x="1061" d="M1024 493.714v-109.714c0-10.286-8-18.286-18.286-18.286h-713.143v-128c0-7.429-4-13.714-10.857-16.571s-14.286-1.714-20 2.857l-219.429 200c-3.429 3.429-5.714 8-5.714 13.143v0c0 5.143 2.286 10.286 5.714 13.714l219.429 202.286c5.714 5.143 13.143 6.286 20 3.429 6.286-2.857 10.857-9.143 10.857-16.571v-128h713.143c10.286 0 18.286-8 18.286-18.286z" />
|
||||
<glyph unicode="" glyph-name="long-arrow-right" horiz-adv-x="987" d="M987.429 440.571c0-5.143-2.286-10.286-5.714-13.714l-219.429-202.286c-5.714-5.143-13.143-6.286-20-3.429-6.286 2.857-10.857 9.143-10.857 16.571v128h-713.143c-10.286 0-18.286 8-18.286 18.286v109.714c0 10.286 8 18.286 18.286 18.286h713.143v128c0 7.429 4 13.714 10.857 16.571s14.286 1.714 20-2.857l219.429-200c3.429-3.429 5.714-8 5.714-13.143v0z" />
|
||||
<glyph unicode="" glyph-name="microchip" horiz-adv-x="878" d="M109.714 219.428v-73.143h-64c-5.143 0-9.143 4-9.143 9.143v9.143h-27.429c-5.143 0-9.143 4-9.143 9.143v18.286c0 5.143 4 9.143 9.143 9.143h27.429v9.143c0 5.143 4 9.143 9.143 9.143h64zM109.714 365.714v-73.143h-64c-5.143 0-9.143 4-9.143 9.143v9.143h-27.429c-5.143 0-9.143 4-9.143 9.143v18.286c0 5.143 4 9.143 9.143 9.143h27.429v9.143c0 5.143 4 9.143 9.143 9.143h64zM109.714 512v-73.143h-64c-5.143 0-9.143 4-9.143 9.143v9.143h-27.429c-5.143 0-9.143 4-9.143 9.143v18.286c0 5.143 4 9.143 9.143 9.143h27.429v9.143c0 5.143 4 9.143 9.143 9.143h64zM109.714 658.286v-73.143h-64c-5.143 0-9.143 4-9.143 9.143v9.143h-27.429c-5.143 0-9.143 4-9.143 9.143v18.286c0 5.143 4 9.143 9.143 9.143h27.429v9.143c0 5.143 4 9.143 9.143 9.143h64zM109.714 804.571v-73.143h-64c-5.143 0-9.143 4-9.143 9.143v9.143h-27.429c-5.143 0-9.143 4-9.143 9.143v18.286c0 5.143 4 9.143 9.143 9.143h27.429v9.143c0 5.143 4 9.143 9.143 9.143h64zM731.429 896v-841.143c0-30.286-24.571-54.857-54.857-54.857h-475.429c-30.286 0-54.857 24.571-54.857 54.857v841.143c0 30.286 24.571 54.857 54.857 54.857h475.429c30.286 0 54.857-24.571 54.857-54.857zM877.714 192v-18.286c0-5.143-4-9.143-9.143-9.143h-27.429v-9.143c0-5.143-4-9.143-9.143-9.143h-64v73.143h64c5.143 0 9.143-4 9.143-9.143v-9.143h27.429c5.143 0 9.143-4 9.143-9.143zM877.714 338.286v-18.286c0-5.143-4-9.143-9.143-9.143h-27.429v-9.143c0-5.143-4-9.143-9.143-9.143h-64v73.143h64c5.143 0 9.143-4 9.143-9.143v-9.143h27.429c5.143 0 9.143-4 9.143-9.143zM877.714 484.571v-18.286c0-5.143-4-9.143-9.143-9.143h-27.429v-9.143c0-5.143-4-9.143-9.143-9.143h-64v73.143h64c5.143 0 9.143-4 9.143-9.143v-9.143h27.429c5.143 0 9.143-4 9.143-9.143zM877.714 630.857v-18.286c0-5.143-4-9.143-9.143-9.143h-27.429v-9.143c0-5.143-4-9.143-9.143-9.143h-64v73.143h64c5.143 0 9.143-4 9.143-9.143v-9.143h27.429c5.143 0 9.143-4 9.143-9.143zM877.714 777.143v-18.286c0-5.143-4-9.143-9.143-9.143h-27.429v-9.143c0-5.143-4-9.143-9.143-9.143h-64v73.143h64c5.143 0 9.143-4 9.143-9.143v-9.143h27.429c5.143 0 9.143-4 9.143-9.143z" />
|
||||
</font></defs></svg>
|
After Width: | Height: | Size: 148 KiB |
BIN
arduino-ide-extension/src/browser/style/fonts/FontAwesome.ttf
Normal file
BIN
arduino-ide-extension/src/browser/style/fonts/FontAwesome.ttf
Normal file
Binary file not shown.
BIN
arduino-ide-extension/src/browser/style/fonts/FontAwesome.woff
Normal file
BIN
arduino-ide-extension/src/browser/style/fonts/FontAwesome.woff
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user