Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
493fb1ec16 | ||
|
|
b167a69976 | ||
|
|
79b46bf210 | ||
|
|
98a26f9ae4 | ||
|
|
dbe3c5cfb9 | ||
|
|
25d7f7b2a4 | ||
|
|
b76e99ac0f | ||
|
|
67c3677621 | ||
|
|
bab6316e76 | ||
|
|
48730bca05 | ||
|
|
698b570ee6 | ||
|
|
a3f11dff18 | ||
|
|
5e359c2fb8 | ||
|
|
2147eae687 | ||
|
|
286295128d | ||
|
|
8501495c17 | ||
|
|
888c9166fc | ||
|
|
e5f6edf405 | ||
|
|
e8a52f717d | ||
|
|
d45750428c | ||
|
|
cf55ca9e28 | ||
|
|
3e5239f7d3 | ||
|
|
7669985f8e | ||
|
|
5047c9b6e7 | ||
|
|
bd2082e8c5 | ||
|
|
8f188acd3c | ||
|
|
0303ef2fd9 | ||
|
|
cb21a970b6 | ||
|
|
203daeb4a2 | ||
|
|
60dde11311 | ||
|
|
8b930fc57c | ||
|
|
f810ed0c14 | ||
|
|
53222fd83c | ||
|
|
a1f2497381 | ||
|
|
c27172c32e | ||
|
|
6e9b65d8b1 | ||
|
|
6d301f9961 | ||
|
|
659fea7afc | ||
|
|
e0303058a3 |
@@ -5,3 +5,7 @@ end_of_line = lf
|
|||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
max_line_length = 100
|
max_line_length = 100
|
||||||
|
|
||||||
|
[{Dockerfile}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ runs:
|
|||||||
-
|
-
|
||||||
name: Run `npm ci` with retries
|
name: Run `npm ci` with retries
|
||||||
shell: bash
|
shell: bash
|
||||||
run: npm run install-deps -- --ci --root-directory "${{ inputs.working-directory }}"
|
run: npm run install-deps -- --ci
|
||||||
|
working-directory: ${{ inputs.working-directory }}
|
||||||
|
|||||||
2
.github/actions/setup-node/action.yml
vendored
@@ -3,6 +3,6 @@ runs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Setup node
|
name: Setup node
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16.x
|
node-version: 16.x
|
||||||
|
|||||||
36
.github/workflows/checks.build.yaml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: build-checks
|
name: checks.build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
-
|
-
|
||||||
name: Setup node
|
name: Setup node
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
-
|
-
|
||||||
name: Setup node
|
name: Setup node
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
@@ -68,3 +68,33 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: Verify bundled desktop build artifacts
|
name: Verify bundled desktop build artifacts
|
||||||
run: npm run check:verify-build-artifacts -- --electron-bundled
|
run: npm run check:verify-build-artifacts -- --electron-bundled
|
||||||
|
|
||||||
|
build-docker:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ macos, ubuntu ] # Windows runners do not support Linux containers
|
||||||
|
fail-fast: false # Allows to see results from other combinations
|
||||||
|
runs-on: ${{ matrix.os }}-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
-
|
||||||
|
name: Install Docker on macOS
|
||||||
|
if: matrix.os == 'macos' # macOS runner is missing Docker
|
||||||
|
run: |-
|
||||||
|
# Install Docker
|
||||||
|
brew install docker
|
||||||
|
# Docker on macOS misses daemon due to licensing, so install colima as runtime
|
||||||
|
brew install colima
|
||||||
|
# Start the daemon
|
||||||
|
colima start
|
||||||
|
-
|
||||||
|
name: Build Docker image
|
||||||
|
run: docker build -t undergroundwires/privacy.sexy:latest .
|
||||||
|
-
|
||||||
|
name: Run Docker image on port 8080
|
||||||
|
run: docker run -d -p 8080:80 --rm --name privacy.sexy undergroundwires/privacy.sexy:latest
|
||||||
|
-
|
||||||
|
name: Check server is up and returns HTTP 200
|
||||||
|
run: node ./scripts/verify-web-server-status.js --url http://localhost:8080
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
-
|
-
|
||||||
name: Setup node
|
name: Setup node
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
|
|||||||
2
.github/workflows/checks.external-urls.yaml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
-
|
-
|
||||||
name: Setup node
|
name: Setup node
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
|
|||||||
2
.github/workflows/checks.quality.yaml
vendored
@@ -18,7 +18,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
-
|
-
|
||||||
name: Setup node
|
name: Setup node
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
|
|||||||
4
.github/workflows/checks.scripts.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
-
|
-
|
||||||
name: Setup node
|
name: Setup node
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
@@ -42,7 +42,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
-
|
-
|
||||||
name: Setup node
|
name: Setup node
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: security-checks
|
name: checks.security.dependencies
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -13,7 +13,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
-
|
-
|
||||||
name: Setup node
|
name: Setup node
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
42
.github/workflows/checks.security.sast.yaml
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
name: checks.security.sast
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 0' # at 00:00 on every Sunday
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: [
|
||||||
|
javascript # analyzes code written in JavaScript, TypeScript and both.
|
||||||
|
]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
-
|
||||||
|
name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
queries: +security-and-quality
|
||||||
|
-
|
||||||
|
name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
-
|
||||||
|
name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
||||||
|
with:
|
||||||
|
category: "/language:${{ matrix.language }}"
|
||||||
2
.github/workflows/release.desktop.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}-latest
|
runs-on: ${{ matrix.os }}-latest
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: master # otherwise it defaults to the version tag missing bump commit
|
ref: master # otherwise it defaults to the version tag missing bump commit
|
||||||
fetch-depth: 0 # fetch all history
|
fetch-depth: 0 # fetch all history
|
||||||
|
|||||||
8
.github/workflows/release.site.yaml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: "Infrastructure: Checkout"
|
name: "Infrastructure: Checkout"
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
path: aws
|
path: aws
|
||||||
repository: undergroundwires/aws-static-site-with-cd
|
repository: undergroundwires/aws-static-site-with-cd
|
||||||
@@ -75,7 +75,7 @@ jobs:
|
|||||||
working-directory: aws
|
working-directory: aws
|
||||||
-
|
-
|
||||||
name: "App: Checkout"
|
name: "App: Checkout"
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
path: app
|
path: app
|
||||||
ref: master # otherwise we don't get version bump commit
|
ref: master # otherwise we don't get version bump commit
|
||||||
@@ -84,7 +84,7 @@ jobs:
|
|||||||
uses: ./app/.github/actions/setup-node
|
uses: ./app/.github/actions/setup-node
|
||||||
-
|
-
|
||||||
name: "App: Install dependencies"
|
name: "App: Install dependencies"
|
||||||
uses: ./.github/actions/npm-install-dependencies
|
uses: ./app/.github/actions/npm-install-dependencies
|
||||||
with:
|
with:
|
||||||
working-directory: app
|
working-directory: app
|
||||||
-
|
-
|
||||||
@@ -102,7 +102,7 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: "App: Deploy to S3"
|
name: "App: Deploy to S3"
|
||||||
shell: bash
|
shell: bash
|
||||||
run: >-
|
run: |-
|
||||||
declare web_output_dir
|
declare web_output_dir
|
||||||
if ! web_output_dir=$(cd app && node scripts/print-dist-dir.js --web); then
|
if ! web_output_dir=$(cd app && node scripts/print-dist-dir.js --web); then
|
||||||
echo 'Error: Could not determine distribution directory.'
|
echo 'Error: Could not determine distribution directory.'
|
||||||
|
|||||||
2
.github/workflows/tests.e2e.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
-
|
-
|
||||||
name: Setup node
|
name: Setup node
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
|
|||||||
2
.github/workflows/tests.integration.yaml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
-
|
-
|
||||||
name: Setup node
|
name: Setup node
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
|
|||||||
2
.github/workflows/tests.unit.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
-
|
-
|
||||||
name: Set-up node
|
name: Set-up node
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
|
|||||||
56
CHANGELOG.md
@@ -1,5 +1,61 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.12.5 (2023-10-13)
|
||||||
|
|
||||||
|
* Fix Docker build and improve checks #220 | [7669985](https://github.com/undergroundwires/privacy.sexy/commit/7669985f8e1446e726a95626ecf35b3ce6b60a16)
|
||||||
|
* Add SAST security checks with SECURITY.md #178 | [3e5239f](https://github.com/undergroundwires/privacy.sexy/commit/3e5239f7d35e57749c01adf3dbbcd365aebb39c8)
|
||||||
|
* Add Scoop download instructions #174 | [cf55ca9](https://github.com/undergroundwires/privacy.sexy/commit/cf55ca9e28b064fa7a516077a9da23e3a8e3f534)
|
||||||
|
* win: fix and improve temp dir cleanup #176, #89 | [d457504](https://github.com/undergroundwires/privacy.sexy/commit/d45750428cca010daf2721b33a8ae3a01b28813b)
|
||||||
|
* win, linux: improve VSCode setting robustness #196 | [e8a52f7](https://github.com/undergroundwires/privacy.sexy/commit/e8a52f717dc799b34ceeb1c27c2b8219391dff6a)
|
||||||
|
* linux: fix obsolete Firefox DPI script #239 | [e5f6edf](https://github.com/undergroundwires/privacy.sexy/commit/e5f6edf405bcec7c29ea4d7932d1910620fa15f8)
|
||||||
|
* win: add removal of Edge assocations #64 | [888c916](https://github.com/undergroundwires/privacy.sexy/commit/888c9166fc66a2094137fa8be739cc21bafef5f6)
|
||||||
|
* win: improve Edge & OneDrive shortcut removal #73 | [8501495](https://github.com/undergroundwires/privacy.sexy/commit/8501495c170af61913288a63dbd369db5bbc5003)
|
||||||
|
* win: relocate and document SecHealthUI #190 | [2862951](https://github.com/undergroundwires/privacy.sexy/commit/286295128d0179358e0c6b7b6415d752175a1aed)
|
||||||
|
* Add developer toolkit UI component | [2147eae](https://github.com/undergroundwires/privacy.sexy/commit/2147eae687b82d05bc43bb4605d9068f148bb92a)
|
||||||
|
* win: fix and improve network data usage reset #265 | [5e359c2](https://github.com/undergroundwires/privacy.sexy/commit/5e359c2fb82a08e6acf7159b70ca86a8234b359b)
|
||||||
|
* win: improve app reversion and docs #260 | [a3f11df](https://github.com/undergroundwires/privacy.sexy/commit/a3f11dff187c821a00910c20dac05e285cda9073)
|
||||||
|
* Fix working directory in CI/CD web release | [698b570](https://github.com/undergroundwires/privacy.sexy/commit/698b570ee6e300d6703015464f4345b5e706f1cb)
|
||||||
|
* Implement new UI component for icons #230 | [48730bc](https://github.com/undergroundwires/privacy.sexy/commit/48730bca0506120bca4bf3a23545d59f2b1a9009)
|
||||||
|
* win: fix and improve AppCompat disabling #255 | [bab6316](https://github.com/undergroundwires/privacy.sexy/commit/bab6316e7625230cf4a4cf67c3aca417347db75c)
|
||||||
|
* win, linux, mac: fix typos and improve naming | [67c3677](https://github.com/undergroundwires/privacy.sexy/commit/67c3677621b201525a813e8a26f07d607176e89b)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.12.4...0.12.5)
|
||||||
|
|
||||||
|
## 0.12.4 (2023-09-25)
|
||||||
|
|
||||||
|
* win: fix Windows spotlight revert, docs, recommend | [659fea7](https://github.com/undergroundwires/privacy.sexy/commit/659fea7afcabcd0ea273cfdcc8c4bae190c126f3)
|
||||||
|
* win: fix Edge telemetry disabling for v116+ #242 | [6d301f9](https://github.com/undergroundwires/privacy.sexy/commit/6d301f99616ed49975876803d0098eafe4d3cb2e)
|
||||||
|
* win: fix, improve disabling automatic updates #252 | [6e9b65d](https://github.com/undergroundwires/privacy.sexy/commit/6e9b65d8b1b481c1471dde90876c37838b4ac4e5)
|
||||||
|
* win: refactor `update.mode` key for VSCode #215 | [c27172c](https://github.com/undergroundwires/privacy.sexy/commit/c27172c32e7c316b7cb0f44cab611eed89ca034e)
|
||||||
|
* Fix wrong action path in website CI deployment | [a1f2497](https://github.com/undergroundwires/privacy.sexy/commit/a1f24973813ccbdd7e1f06c64e1912a991a6bb64)
|
||||||
|
* Fix compiler bug with nested optional arguments | [53222fd](https://github.com/undergroundwires/privacy.sexy/commit/53222fd83c2846089746a217482195806f960d18)
|
||||||
|
* Fix no spacing after lists in documentation text | [f810ed0](https://github.com/undergroundwires/privacy.sexy/commit/f810ed0c147c2a46cae3b70b635ed81128646fff)
|
||||||
|
* Rewrite tooltip UI for efficiency and Vue 3.0 #230 | [8b930fc](https://github.com/undergroundwires/privacy.sexy/commit/8b930fc57c8ee6691ed6165bcb27d97e64a1a0c0)
|
||||||
|
* win: fix uninstallation of newer Edge #236 | [60dde11](https://github.com/undergroundwires/privacy.sexy/commit/60dde11311a2409537f5965f370b0daaaec53339)
|
||||||
|
* win: fix delivery optimization side-effects #173 | [203daeb](https://github.com/undergroundwires/privacy.sexy/commit/203daeb4a2fca0a0295cbc2a736394f9f87725e6)
|
||||||
|
* win: fix Defender scan artifacts removal #246 | [cb21a97](https://github.com/undergroundwires/privacy.sexy/commit/cb21a970b6b867e1476a5eb8a72b9a7fdd53a744)
|
||||||
|
* Fix outdated and broken links in README #161 | [0303ef2](https://github.com/undergroundwires/privacy.sexy/commit/0303ef2fd98b36306523e2a0c5f5ae812a4c6c99)
|
||||||
|
* Fix loss of tree node state when switching views | [8f188ac](https://github.com/undergroundwires/privacy.sexy/commit/8f188acd3c2d93e40c89569c74bc5cff992f0052)
|
||||||
|
* Fix slow appearance of nodes on tree view | [bd2082e](https://github.com/undergroundwires/privacy.sexy/commit/bd2082e8c574db065bb4462f30ea3ace2cb028cb)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.12.3...0.12.4)
|
||||||
|
|
||||||
|
## 0.12.3 (2023-09-09)
|
||||||
|
|
||||||
|
* linux: use user.js over prefs.js for Firefox #232 | [dae6d11](https://github.com/undergroundwires/privacy.sexy/commit/dae6d114daab6857d773071211eb57619b136281)
|
||||||
|
* win: fix typo in Defender retention script #213 | [35be05d](https://github.com/undergroundwires/privacy.sexy/commit/35be05df2094ea8bba4ee4725e6fa4956a79493d)
|
||||||
|
* Improve desktop runtime execution tests | [ad0576a](https://github.com/undergroundwires/privacy.sexy/commit/ad0576a752f8fd6ea2f917a59173fe61f9951246)
|
||||||
|
* Fix Windows artifact naming in desktop packaging | [f4d86fc](https://github.com/undergroundwires/privacy.sexy/commit/f4d86fccfd0e73e94c8c6e400a33514900bc5abe)
|
||||||
|
* Refactor and improve external URL checks | [19e42c9](https://github.com/undergroundwires/privacy.sexy/commit/19e42c9c52a18c813ded4265e687e01032cdd4c8)
|
||||||
|
* Fix memory leaks via auto-unsubscribing and DI | [eb096d0](https://github.com/undergroundwires/privacy.sexy/commit/eb096d07e276e1b4c8040220c47f186d02841e14)
|
||||||
|
* Refactor build configs and improve CI/CD checks | [0a2a1a0](https://github.com/undergroundwires/privacy.sexy/commit/0a2a1a026b0efb29624be82b06536c518c1ea439)
|
||||||
|
* Introduce retry mechanism for npm install in CI/CD | [4beb1bb](https://github.com/undergroundwires/privacy.sexy/commit/4beb1bb5748a60886210187ca3cdc7f4b41067c0)
|
||||||
|
* win: fix disable recent apps revert #211, #248 | [4ce327e](https://github.com/undergroundwires/privacy.sexy/commit/4ce327eb6af542ed2916d649553e5e1ba5833882)
|
||||||
|
* Change license to AGPLv3 | [821cc62](https://github.com/undergroundwires/privacy.sexy/commit/821cc62c4c8347cb76d041f82f574754e4d948c5)
|
||||||
|
* Introduce new TreeView UI component | [65f121c](https://github.com/undergroundwires/privacy.sexy/commit/65f121c451af87315e1c91df4198562e0445b2c2)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.12.2...0.12.3)
|
||||||
|
|
||||||
## 0.12.2 (2023-08-25)
|
## 0.12.2 (2023-08-25)
|
||||||
|
|
||||||
* Add automated checks for desktop app runtime #233 | [04b3133](https://github.com/undergroundwires/privacy.sexy/commit/04b3133500485d0d278a81a177a1677134131405)
|
* Add automated checks for desktop app runtime #233 | [04b3133](https://github.com/undergroundwires/privacy.sexy/commit/04b3133500485d0d278a81a177a1677134131405)
|
||||||
|
|||||||
15
Dockerfile
@@ -1,13 +1,16 @@
|
|||||||
# Build
|
# Build
|
||||||
FROM node:lts-alpine as build-stage
|
FROM node:lts-alpine AS build-stage
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package*.json ./
|
|
||||||
RUN npm install
|
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN npm run build
|
RUN npm run install-deps
|
||||||
|
RUN npm run build \
|
||||||
|
&& npm run check:verify-build-artifacts -- --web
|
||||||
|
RUN mkdir /dist \
|
||||||
|
&& dist_directory=$(node 'scripts/print-dist-dir.js' --web) \
|
||||||
|
&& cp -a "${dist_directory}/." '/dist'
|
||||||
|
|
||||||
# Production stage
|
# Production stage
|
||||||
FROM nginx:stable-alpine as production-stage
|
FROM nginx:stable-alpine AS production-stage
|
||||||
COPY --from=build-stage /app/dist /usr/share/nginx/html
|
COPY --from=build-stage /dist /usr/share/nginx/html
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
48
README.md
@@ -16,14 +16,6 @@
|
|||||||
src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat"
|
src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
<!-- Code quality -->
|
|
||||||
<br />
|
|
||||||
<a href="https://lgtm.com/projects/g/undergroundwires/privacy.sexy/context:javascript" target="_blank" rel="noopener noreferrer">
|
|
||||||
<img
|
|
||||||
alt="Language grade: JavaScript/TypeScript"
|
|
||||||
src="https://img.shields.io/lgtm/grade/javascript/g/undergroundwires/privacy.sexy.svg?logo=lgtm&logoWidth=18"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
<a href="https://codeclimate.com/github/undergroundwires/privacy.sexy/maintainability" target="_blank" rel="noopener noreferrer">
|
<a href="https://codeclimate.com/github/undergroundwires/privacy.sexy/maintainability" target="_blank" rel="noopener noreferrer">
|
||||||
<img
|
<img
|
||||||
alt="Maintainability"
|
alt="Maintainability"
|
||||||
@@ -50,6 +42,20 @@
|
|||||||
src="https://github.com/undergroundwires/privacy.sexy/workflows/e2e-tests/badge.svg"
|
src="https://github.com/undergroundwires/privacy.sexy/workflows/e2e-tests/badge.svg"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
|
<!-- Security checks -->
|
||||||
|
<br />
|
||||||
|
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.security.sast.yaml" target="_blank" rel="noopener noreferrer">
|
||||||
|
<img
|
||||||
|
alt="Status of dependency security checks"
|
||||||
|
src="https://github.com/undergroundwires/privacy.sexy/workflows/checks.security.sast/badge.svg"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.security.dependencies.yaml" target="_blank" rel="noopener noreferrer">
|
||||||
|
<img
|
||||||
|
alt="Status of Static Analysis Security Testing (SAST)"
|
||||||
|
src="https://github.com/undergroundwires/privacy.sexy/workflows/checks.security.dependencies/badge.svg"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
<!-- Checks -->
|
<!-- Checks -->
|
||||||
<br />
|
<br />
|
||||||
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.quality.yaml" target="_blank" rel="noopener noreferrer">
|
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.quality.yaml" target="_blank" rel="noopener noreferrer">
|
||||||
@@ -58,16 +64,10 @@
|
|||||||
src="https://github.com/undergroundwires/privacy.sexy/workflows/quality-checks/badge.svg"
|
src="https://github.com/undergroundwires/privacy.sexy/workflows/quality-checks/badge.svg"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.security.yaml" target="_blank" rel="noopener noreferrer">
|
|
||||||
<img
|
|
||||||
alt="Security checks status"
|
|
||||||
src="https://github.com/undergroundwires/privacy.sexy/workflows/security-checks/badge.svg"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.build.yaml" target="_blank" rel="noopener noreferrer">
|
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.build.yaml" target="_blank" rel="noopener noreferrer">
|
||||||
<img
|
<img
|
||||||
alt="Build checks status"
|
alt="Status of build checks"
|
||||||
src="https://github.com/undergroundwires/privacy.sexy/workflows/build-checks/badge.svg"
|
src="https://github.com/undergroundwires/privacy.sexy/workflows/checks.build/badge.svg"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.desktop-runtime-errors.yaml" target="_blank" rel="noopener noreferrer">
|
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.desktop-runtime-errors.yaml" target="_blank" rel="noopener noreferrer">
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
## Get started
|
## Get started
|
||||||
|
|
||||||
- 🌍️ **Online**: [https://privacy.sexy](https://privacy.sexy).
|
- 🌍️ **Online**: [https://privacy.sexy](https://privacy.sexy).
|
||||||
- 🖥️ **Offline**: Check [releases page](https://github.com/undergroundwires/privacy.sexy/releases), or download directly for: [Windows](https://github.com/undergroundwires/privacy.sexy/releases/download/0.11.2/privacy.sexy-Setup-0.11.2.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.11.2/privacy.sexy-0.11.2.dmg), [Linux](https://github.com/undergroundwires/pr.vacy.sexy/releases/download/0.11.2/privacy.sexy-0.11.2.AppImage).
|
- 🖥️ **Offline**: Download directly for: [Windows](https://github.com/undergroundwires/privacy.sexy/releases/download/0.12.5/privacy.sexy-Setup-0.12.5.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.12.5/privacy.sexy-0.12.5.dmg), [Linux](https://github.com/undergroundwires/privacy.sexy/releases/download/0.12.5/privacy.sexy-0.12.5.AppImage). For more options, see [here](#additional-install-options).
|
||||||
|
|
||||||
Online version does not require to run any software on your computer. Offline version has more functions such as running the scripts directly.
|
Online version does not require to run any software on your computer. Offline version has more functions such as running the scripts directly.
|
||||||
|
|
||||||
@@ -150,6 +150,16 @@ Online version does not require to run any software on your computer. Offline ve
|
|||||||
|
|
||||||
**Contribute 👷**. Contributions of any type are welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) as the starting point. It includes useful information like [how to add new scripts](./CONTRIBUTING.md#extend-scripts).
|
**Contribute 👷**. Contributions of any type are welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) as the starting point. It includes useful information like [how to add new scripts](./CONTRIBUTING.md#extend-scripts).
|
||||||
|
|
||||||
|
## Additional Install Options
|
||||||
|
|
||||||
|
- Check the [releases page](https://github.com/undergroundwires/privacy.sexy/releases) for all available versions.
|
||||||
|
- Using [Scoop](https://scoop.sh/#/apps?q=privacy.sexy&s=2&d=1&o=true) package manager on Windows:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
scoop bucket add extras
|
||||||
|
scoop install privacy.sexy
|
||||||
|
```
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
Refer to [development.md](./docs/development.md) for Docker usage and reading more about setting up your development environment.
|
Refer to [development.md](./docs/development.md) for Docker usage and reading more about setting up your development environment.
|
||||||
@@ -157,3 +167,7 @@ Refer to [development.md](./docs/development.md) for Docker usage and reading mo
|
|||||||
Check [architecture.md](./docs/architecture.md) for an overview of design and how different parts and layers work together. You can refer to [application.md](./docs/application.md) for a closer look at application layer codebase and [presentation.md](./docs/presentation.md) for code related to GUI layer. [collection-files.md](./docs/collection-files.md) explains the YAML files that are the core of the application and [templating.md](./docs/templating.md) documents how to use templating language in those files. In [ci-cd.md](./docs/ci-cd.md), you can read more about the pipelines that automates maintenance tasks and ensures you get what see.
|
Check [architecture.md](./docs/architecture.md) for an overview of design and how different parts and layers work together. You can refer to [application.md](./docs/application.md) for a closer look at application layer codebase and [presentation.md](./docs/presentation.md) for code related to GUI layer. [collection-files.md](./docs/collection-files.md) explains the YAML files that are the core of the application and [templating.md](./docs/templating.md) documents how to use templating language in those files. In [ci-cd.md](./docs/ci-cd.md), you can read more about the pipelines that automates maintenance tasks and ensures you get what see.
|
||||||
|
|
||||||
[docs/](./docs/) folder includes all other documentation.
|
[docs/](./docs/) folder includes all other documentation.
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
Security is a top priority at privacy.sexy. An extensive commitment to security verification ensures this priority. For any security concerns or vulnerabilities, please consult the [Security Policy](./SECURITY.md).
|
||||||
|
|||||||
31
SECURITY.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
privacy.sexy takes security seriously. Commitment is made to address all security issues with urgency. Responsible reporting of any discovered vulnerabilities in the project is highly encouraged.
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
Efforts to responsibly disclose findings are greatly appreciated. To report a security vulnerability, follow these steps:
|
||||||
|
|
||||||
|
- For general vulnerabilities, [open an issue](https://github.com/undergroundwires/privacy.sexy/issues/new/choose) using the bug report template.
|
||||||
|
- For sensitive matters, [contact the developer directly](https://undergroundwires.dev).
|
||||||
|
|
||||||
|
## Security Report Handling
|
||||||
|
|
||||||
|
Upon receipt of a security report, the following actions will be taken:
|
||||||
|
|
||||||
|
- The report will be confirmed, identifying the affected components.
|
||||||
|
- The impact and severity of the issue will be assessed.
|
||||||
|
- Work on a fix and plan a release to address the vulnerability will be initiated.
|
||||||
|
- The reporter will be kept updated about the progress.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Regular and extensive testing is conducted to ensure robust security in the project. Information about testing practices can be found in the [Testing Documentation](./docs/tests.md).
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For additional assistance or any unanswered questions, [submit a GitHub issue](https://github.com/undergroundwires/privacy.sexy/issues/new/choose). Security concerns are a priority, and necessary support to address them is assured.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Active contribution to the safety and security of privacy.sexy is thanked. This collaborative effort keeps the project resilient and trustworthy for all.
|
||||||
@@ -6,7 +6,10 @@ const CYPRESS_BASE_DIR = 'tests/e2e/';
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
fixturesFolder: `${CYPRESS_BASE_DIR}/fixtures`,
|
fixturesFolder: `${CYPRESS_BASE_DIR}/fixtures`,
|
||||||
screenshotsFolder: `${CYPRESS_BASE_DIR}/screenshots`,
|
screenshotsFolder: `${CYPRESS_BASE_DIR}/screenshots`,
|
||||||
|
|
||||||
|
video: true,
|
||||||
videosFolder: `${CYPRESS_BASE_DIR}/videos`,
|
videosFolder: `${CYPRESS_BASE_DIR}/videos`,
|
||||||
|
|
||||||
e2e: {
|
e2e: {
|
||||||
baseUrl: `http://localhost:${ViteConfig.server.port}/`,
|
baseUrl: `http://localhost:${ViteConfig.server.port}/`,
|
||||||
specPattern: `${CYPRESS_BASE_DIR}/**/*.cy.{js,jsx,ts,tsx}`, // Default: cypress/e2e/**/*.cy.{js,jsx,ts,tsx}
|
specPattern: `${CYPRESS_BASE_DIR}/**/*.cy.{js,jsx,ts,tsx}`, // Default: cypress/e2e/**/*.cy.{js,jsx,ts,tsx}
|
||||||
|
|||||||
@@ -174,3 +174,19 @@
|
|||||||
- `endCode:` *`string`* (**required**)
|
- `endCode:` *`string`* (**required**)
|
||||||
- Code that'll be inserted at the end of user created script.
|
- Code that'll be inserted at the end of user created script.
|
||||||
- Global variables such as `$homepage`, `$version`, `$date` can be used using [parameter substitution](./templating.md#parameter-substitution) code syntax such as `Welcome to {{ $homepage }}!`
|
- Global variables such as `$homepage`, `$version`, `$date` can be used using [parameter substitution](./templating.md#parameter-substitution) code syntax such as `Welcome to {{ $homepage }}!`
|
||||||
|
|
||||||
|
## Naming guidelines
|
||||||
|
|
||||||
|
- Prioritize consistency throughout all names.
|
||||||
|
- Use an instruction format like "do this, do that" for clear, direct guidance. This approach reduces potential confusion and offers easy-to-follow steps. It provides specific, unambiguous instructions.
|
||||||
|
- Ensure brand names adhere to their official casing.
|
||||||
|
- Choose clear and uncomplicated language.
|
||||||
|
- Favor the terms:
|
||||||
|
- "Disable" over "Turn off"
|
||||||
|
- "Configure" over "Set up"
|
||||||
|
- "Clear" over "Erase" or "Clean"
|
||||||
|
- "Minimize" over "Limit" or "Reduce" (when it enhances clarity)
|
||||||
|
- "Remove" over "Uninstall"
|
||||||
|
- Structure your phrases for clarity.
|
||||||
|
- For instance, "Disable XX telemetry" or "Clear XX data" are preferred over "Clear data from XX", "Disable telemetry in XX", or "Clear data of XX".
|
||||||
|
- Use sentence case rather than Title Case.
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ See [ci-cd.md](./ci-cd.md) for more information.
|
|||||||
|
|
||||||
1. Build: `docker build -t undergroundwires/privacy.sexy:latest .`
|
1. Build: `docker build -t undergroundwires/privacy.sexy:latest .`
|
||||||
2. Run: `docker run -it -p 8080:80 --rm --name privacy.sexy undergroundwires/privacy.sexy:latest`
|
2. Run: `docker run -it -p 8080:80 --rm --name privacy.sexy undergroundwires/privacy.sexy:latest`
|
||||||
|
3. Application should be available at [`http://localhost:8080`](http://localhost:8080)
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
@@ -81,11 +82,12 @@ See [ci-cd.md](./ci-cd.md) for more information.
|
|||||||
|
|
||||||
#### Automation scripts
|
#### Automation scripts
|
||||||
|
|
||||||
- [**`node scripts/print-dist-dir.js [-- <options>]`**](../scripts/print-dist-dir.js):
|
- [**`node scripts/print-dist-dir.js [<options>]`**](../scripts/print-dist-dir.js):
|
||||||
- Determines the absolute path of a distribution directory based on CLI arguments and outputs its absolute path.
|
- Determines the absolute path of a distribution directory based on CLI arguments and outputs its absolute path.
|
||||||
- Primarily used by automation scripts.
|
|
||||||
- [**`npm run check:verify-build-artifacts [-- <options>]`**](../scripts/verify-build-artifacts.js):
|
- [**`npm run check:verify-build-artifacts [-- <options>]`**](../scripts/verify-build-artifacts.js):
|
||||||
- Verifies the existence and content of build artifacts. Useful for ensuring that the build process is generating the expected output.
|
- Verifies the existence and content of build artifacts. Useful for ensuring that the build process is generating the expected output.
|
||||||
|
- [**`node scripts/verify-web-server-status.js --url [URL]`**](../scripts/verify-web-server-status.js):
|
||||||
|
- Checks if a specified server is up with retries and returns an HTTP 200 status code.
|
||||||
|
|
||||||
## Recommended extensions
|
## Recommended extensions
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ The presentation layer uses an event-driven architecture for bidirectional react
|
|||||||
- [**`fonts/`**](./../src/presentation/assets/fonts/): Contains fonts.
|
- [**`fonts/`**](./../src/presentation/assets/fonts/): Contains fonts.
|
||||||
- [**`styles/`**](./../src/presentation/assets/styles/): Contains shared styles.
|
- [**`styles/`**](./../src/presentation/assets/styles/): Contains shared styles.
|
||||||
- [**`components/`**](./../src/presentation/assets/styles/components): Contains styles coupled to Vue components.
|
- [**`components/`**](./../src/presentation/assets/styles/components): Contains styles coupled to Vue components.
|
||||||
- [**`vendors-extensions/`**](./../src/presentation/assets/styles/third-party-extensions): Contains styles for third-party components.
|
|
||||||
- [**`main.scss`**](./../src/presentation/assets/styles/main.scss): Main Sass file, imported by other components as single entrypoint.
|
- [**`main.scss`**](./../src/presentation/assets/styles/main.scss): Main Sass file, imported by other components as single entrypoint.
|
||||||
- [**`main.ts`**](./../src/presentation/main.ts): Starts Vue app.
|
- [**`main.ts`**](./../src/presentation/main.ts): Starts Vue app.
|
||||||
- [**`electron/`**](./../src/presentation/electron/): Contains Electron code.
|
- [**`electron/`**](./../src/presentation/electron/): Contains Electron code.
|
||||||
|
|||||||
@@ -56,6 +56,11 @@ These checks validate various qualities like runtime execution, building process
|
|||||||
- Use [various tools](./../package.json) and [scripts](./../scripts).
|
- Use [various tools](./../package.json) and [scripts](./../scripts).
|
||||||
- Are automatically executed as [GitHub workflows](./../.github/workflows).
|
- Are automatically executed as [GitHub workflows](./../.github/workflows).
|
||||||
|
|
||||||
|
### Security checks
|
||||||
|
|
||||||
|
- [`checks.security.sast`](./../.github/workflows/checks.security.sast.yaml): Utilizes CodeQL to conduct Static Analysis Security Testing (SAST) to ensure the secure integrity of the codebase.
|
||||||
|
- [`checks.security.dependencies`](./../.github/workflows/checks.security.dependencies.yaml): Performs audits on third-party dependencies to identify and mitigate potential vulnerabilities, safeguarding the project from exploitable weaknesses.
|
||||||
|
|
||||||
## Tests structure
|
## Tests structure
|
||||||
|
|
||||||
- [`package.json`](./../package.json): Defines test commands and includes tools used in tests.
|
- [`package.json`](./../package.json): Defines test commands and includes tools used in tests.
|
||||||
|
|||||||
9527
package-lock.json
generated
75
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "privacy.sexy",
|
"name": "privacy.sexy",
|
||||||
"version": "0.12.2",
|
"version": "0.12.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"slogan": "Now you have the choice",
|
"slogan": "Now you have the choice",
|
||||||
"description": "Enforce privacy & security best-practices on Windows, macOS and Linux, because privacy is sexy 🍑🍆",
|
"description": "Enforce privacy & security best-practices on Windows, macOS and Linux, because privacy is sexy 🍑🍆",
|
||||||
@@ -34,25 +34,21 @@
|
|||||||
"postuninstall": "electron-builder install-app-deps"
|
"postuninstall": "electron-builder install-app-deps"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
"@floating-ui/vue": "^1.0.2",
|
||||||
"@fortawesome/free-brands-svg-icons": "^6.4.0",
|
|
||||||
"@fortawesome/free-regular-svg-icons": "^6.4.0",
|
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
|
||||||
"@fortawesome/vue-fontawesome": "^2.0.9",
|
|
||||||
"@juggle/resize-observer": "^3.4.0",
|
"@juggle/resize-observer": "^3.4.0",
|
||||||
"ace-builds": "^1.23.4",
|
"ace-builds": "^1.30.0",
|
||||||
"cross-fetch": "^4.0.0",
|
"cross-fetch": "^4.0.0",
|
||||||
|
"electron-log": "^4.4.8",
|
||||||
"electron-progressbar": "^2.1.0",
|
"electron-progressbar": "^2.1.0",
|
||||||
|
"electron-updater": "^6.1.4",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"markdown-it": "^13.0.1",
|
"markdown-it": "^13.0.2",
|
||||||
"npm": "^9.8.1",
|
|
||||||
"v-tooltip": "2.1.3",
|
|
||||||
"vue": "^2.7.14"
|
"vue": "^2.7.14"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@modyfi/vite-plugin-yaml": "^1.0.4",
|
"@modyfi/vite-plugin-yaml": "^1.0.4",
|
||||||
"@rushstack/eslint-patch": "^1.3.2",
|
"@rushstack/eslint-patch": "^1.5.1",
|
||||||
"@types/ace": "^0.0.48",
|
"@types/ace": "^0.0.49",
|
||||||
"@types/file-saver": "^2.0.5",
|
"@types/file-saver": "^2.0.5",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||||
"@typescript-eslint/parser": "^5.62.0",
|
"@typescript-eslint/parser": "^5.62.0",
|
||||||
@@ -61,47 +57,42 @@
|
|||||||
"@vue/eslint-config-airbnb-with-typescript": "^7.0.0",
|
"@vue/eslint-config-airbnb-with-typescript": "^7.0.0",
|
||||||
"@vue/eslint-config-typescript": "^11.0.3",
|
"@vue/eslint-config-typescript": "^11.0.3",
|
||||||
"@vue/test-utils": "^1.3.6",
|
"@vue/test-utils": "^1.3.6",
|
||||||
"autoprefixer": "^10.4.15",
|
"autoprefixer": "^10.4.16",
|
||||||
"cypress": "^12.17.2",
|
"cypress": "^13.3.1",
|
||||||
"electron": "^25.3.2",
|
"electron": "^27.0.0",
|
||||||
"electron-builder": "^24.6.3",
|
"electron-builder": "^24.6.4",
|
||||||
"electron-devtools-installer": "^3.2.0",
|
"electron-devtools-installer": "^3.2.0",
|
||||||
"electron-icon-builder": "^2.0.1",
|
"electron-icon-builder": "^2.0.1",
|
||||||
"electron-log": "^4.4.8",
|
|
||||||
"electron-updater": "^6.1.4",
|
|
||||||
"electron-vite": "^1.0.27",
|
"electron-vite": "^1.0.27",
|
||||||
"eslint": "^8.46.0",
|
"eslint": "^8.51.0",
|
||||||
"eslint-plugin-cypress": "^2.14.0",
|
"eslint-plugin-cypress": "^2.15.1",
|
||||||
"eslint-plugin-vue": "^9.6.0",
|
"eslint-plugin-vue": "^9.17.0",
|
||||||
"eslint-plugin-vuejs-accessibility": "^1.2.0",
|
"eslint-plugin-vuejs-accessibility": "^2.2.0",
|
||||||
"icon-gen": "^3.0.1",
|
"icon-gen": "^4.0.0",
|
||||||
"jsdom": "^22.1.0",
|
"jsdom": "^22.1.0",
|
||||||
"markdownlint-cli": "^0.35.0",
|
"markdownlint-cli": "^0.37.0",
|
||||||
"postcss": "^8.4.28",
|
"postcss": "^8.4.31",
|
||||||
"remark-cli": "^11.0.0",
|
"remark-cli": "^12.0.0",
|
||||||
"remark-lint-no-dead-urls": "^1.1.0",
|
"remark-lint-no-dead-urls": "^1.1.0",
|
||||||
"remark-preset-lint-consistent": "^5.1.2",
|
"remark-preset-lint-consistent": "^5.1.2",
|
||||||
"remark-validate-links": "^12.1.1",
|
"remark-validate-links": "^13.0.0",
|
||||||
"sass": "^1.64.1",
|
"sass": "^1.69.3",
|
||||||
"start-server-and-test": "^2.0.0",
|
"start-server-and-test": "^2.0.1",
|
||||||
"svgexport": "^0.4.2",
|
"svgexport": "^0.4.2",
|
||||||
"terser": "^5.19.2",
|
"terser": "^5.21.0",
|
||||||
"tslib": "~2.4.0",
|
"tslib": "^2.6.2",
|
||||||
"typescript": "~4.6.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^4.4.9",
|
"vite": "^4.4.11",
|
||||||
"vitest": "^0.34.2",
|
"vitest": "^0.34.6",
|
||||||
"vue-tsc": "^1.8.8",
|
"vue-tsc": "^1.8.19",
|
||||||
"yaml-lint": "^1.7.0"
|
"yaml-lint": "^1.7.0"
|
||||||
},
|
},
|
||||||
"//devDependencies": {
|
"//devDependencies": {
|
||||||
"terser": "Used by @vitejs/plugin-legacy for minification",
|
"terser": "Used by @vitejs/plugin-legacy for minification",
|
||||||
"typescript": [
|
"@rushstack/eslint-patch": "Needed by @vue/eslint-config-typescript",
|
||||||
"Cannot upgrade to 5.X.X due to unmaintained @vue/cli-plugin-typescript, https://github.com/vuejs/vue-cli/issues/7401",
|
"@vue/eslint-config-typescript": "Cannot upgrade to 12.X.X due to @vue/eslint-config-airbnb-with-typescript, https://github.com/vuejs/eslint-config-airbnb/issues/58",
|
||||||
"Cannot upgrade to > 4.6.X otherwise unit tests do not work, https://github.com/evanw/node-source-map-support/issues/252"
|
"@typescript-eslint/eslint-plugin": "Cannot upgrade to 6.X.X due to @vue/eslint-config-airbnb-with-typescript, https://github.com/vuejs/eslint-config-airbnb/issues/58",
|
||||||
],
|
"@typescript-eslint/parser": "Cannot upgrade to 6.X.X due to @vue/eslint-config-airbnb-with-typescript, https://github.com/vuejs/eslint-config-airbnb/issues/58"
|
||||||
"tslib": "Cannot upgrade to > 2.4.X otherwise unit tests do not work, https://github.com/evanw/node-source-map-support/issues/252",
|
|
||||||
"@typescript-eslint/eslint-plugin": "Cannot upgrade to 6.X.X due to @vue/eslint-config-typescript, https://github.com/vuejs/eslint-config-typescript/pull/60",
|
|
||||||
"@typescript-eslint/parser": "Cannot upgrade to 6.X.X due to @vue/eslint-config-typescript, https://github.com/vuejs/eslint-config-typescript/pull/60"
|
|
||||||
},
|
},
|
||||||
"homepage": "https://privacy.sexy",
|
"homepage": "https://privacy.sexy",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
62
scripts/verify-web-server-status.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* This script checks if a server, provided as a CLI argument, is up
|
||||||
|
* and returns an HTTP 200 status code.
|
||||||
|
* It is designed to provide easy verification of server availability
|
||||||
|
* and will retry a specified number of times.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* node ./scripts/verify-web-server-status.js --url [URL]
|
||||||
|
*
|
||||||
|
* Options:
|
||||||
|
* --url URL of the server to check
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { get } from 'http';
|
||||||
|
|
||||||
|
const MAX_RETRIES = 30;
|
||||||
|
const RETRY_DELAY_IN_SECONDS = 3;
|
||||||
|
const URL_PARAMETER_NAME = '--url';
|
||||||
|
|
||||||
|
function checkServer(currentRetryCount = 1) {
|
||||||
|
const serverUrl = getServerUrl();
|
||||||
|
console.log(`Requesting ${serverUrl}...`);
|
||||||
|
get(serverUrl, (res) => {
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
console.log('🎊 Success: The server is up and returned HTTP 200.');
|
||||||
|
process.exit(0);
|
||||||
|
} else {
|
||||||
|
console.log(`Server returned HTTP status code ${res.statusCode}.`);
|
||||||
|
retry(currentRetryCount);
|
||||||
|
}
|
||||||
|
}).on('error', (err) => {
|
||||||
|
console.error('Error making the request:', err);
|
||||||
|
retry(currentRetryCount);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function retry(currentRetryCount) {
|
||||||
|
console.log(`Attempt ${currentRetryCount}/${MAX_RETRIES}:`);
|
||||||
|
console.log(`Retrying in ${RETRY_DELAY_IN_SECONDS} seconds.`);
|
||||||
|
|
||||||
|
const remainingTime = (MAX_RETRIES - currentRetryCount) * RETRY_DELAY_IN_SECONDS;
|
||||||
|
console.log(`Time remaining before timeout: ${remainingTime}s`);
|
||||||
|
|
||||||
|
if (currentRetryCount < MAX_RETRIES) {
|
||||||
|
setTimeout(() => checkServer(currentRetryCount + 1), RETRY_DELAY_IN_SECONDS * 1000);
|
||||||
|
} else {
|
||||||
|
console.log('Failure: The server at did not return HTTP 200 within the allocated time. Exiting.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getServerUrl() {
|
||||||
|
const urlIndex = process.argv.indexOf(URL_PARAMETER_NAME);
|
||||||
|
if (urlIndex === -1 || urlIndex === process.argv.length - 1) {
|
||||||
|
console.error(`Parameter "${URL_PARAMETER_NAME}" is not provided.`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
return process.argv[urlIndex + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
checkServer();
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { CompiledCode } from '../CompiledCode';
|
||||||
|
|
||||||
|
export interface CodeSegmentMerger {
|
||||||
|
mergeCodeParts(codeSegments: readonly CompiledCode[]): CompiledCode;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { CompiledCode } from '../CompiledCode';
|
||||||
|
import { CodeSegmentMerger } from './CodeSegmentMerger';
|
||||||
|
|
||||||
|
export class NewlineCodeSegmentMerger implements CodeSegmentMerger {
|
||||||
|
public mergeCodeParts(codeSegments: readonly CompiledCode[]): CompiledCode {
|
||||||
|
if (!codeSegments?.length) {
|
||||||
|
throw new Error('missing segments');
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
code: joinCodeParts(codeSegments.map((f) => f.code)),
|
||||||
|
revertCode: joinCodeParts(codeSegments.map((f) => f.revertCode)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function joinCodeParts(codeSegments: readonly string[]): string {
|
||||||
|
return codeSegments
|
||||||
|
.filter((segment) => segment?.length > 0)
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface ICompiledCode {
|
export interface CompiledCode {
|
||||||
readonly code: string;
|
readonly code: string;
|
||||||
readonly revertCode?: string;
|
readonly revertCode?: string;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { ISharedFunctionCollection } from '@/application/Parser/Script/Compiler/Function/ISharedFunctionCollection';
|
||||||
|
import { FunctionCall } from '../FunctionCall';
|
||||||
|
import type { SingleCallCompiler } from './SingleCall/SingleCallCompiler';
|
||||||
|
|
||||||
|
export interface FunctionCallCompilationContext {
|
||||||
|
readonly allFunctions: ISharedFunctionCollection;
|
||||||
|
readonly rootCallSequence: readonly FunctionCall[];
|
||||||
|
readonly singleCallCompiler: SingleCallCompiler;
|
||||||
|
}
|
||||||
@@ -1,149 +1,10 @@
|
|||||||
import { IReadOnlyFunctionCallArgumentCollection } from '@/application/Parser/Script/Compiler/Function/Call/Argument/IFunctionCallArgumentCollection';
|
import { ISharedFunctionCollection } from '@/application/Parser/Script/Compiler/Function/ISharedFunctionCollection';
|
||||||
import { IFunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/IFunctionCall';
|
|
||||||
import { FunctionCallArgument } from '@/application/Parser/Script/Compiler/Function/Call/Argument/FunctionCallArgument';
|
|
||||||
import { ISharedFunctionCollection } from '../../ISharedFunctionCollection';
|
|
||||||
import { IExpressionsCompiler } from '../../../Expressions/IExpressionsCompiler';
|
|
||||||
import { ExpressionsCompiler } from '../../../Expressions/ExpressionsCompiler';
|
|
||||||
import { ISharedFunction, IFunctionCode } from '../../ISharedFunction';
|
|
||||||
import { FunctionCall } from '../FunctionCall';
|
import { FunctionCall } from '../FunctionCall';
|
||||||
import { FunctionCallArgumentCollection } from '../Argument/FunctionCallArgumentCollection';
|
import { CompiledCode } from './CompiledCode';
|
||||||
import { IFunctionCallCompiler } from './IFunctionCallCompiler';
|
|
||||||
import { ICompiledCode } from './ICompiledCode';
|
|
||||||
|
|
||||||
export class FunctionCallCompiler implements IFunctionCallCompiler {
|
export interface FunctionCallCompiler {
|
||||||
public static readonly instance: IFunctionCallCompiler = new FunctionCallCompiler();
|
compileFunctionCalls(
|
||||||
|
calls: readonly FunctionCall[],
|
||||||
protected constructor(
|
|
||||||
private readonly expressionsCompiler: IExpressionsCompiler = new ExpressionsCompiler(),
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public compileCall(
|
|
||||||
calls: IFunctionCall[],
|
|
||||||
functions: ISharedFunctionCollection,
|
functions: ISharedFunctionCollection,
|
||||||
): ICompiledCode {
|
): CompiledCode;
|
||||||
if (!functions) { throw new Error('missing functions'); }
|
|
||||||
if (!calls) { throw new Error('missing calls'); }
|
|
||||||
if (calls.some((f) => !f)) { throw new Error('missing function call'); }
|
|
||||||
const context: ICompilationContext = {
|
|
||||||
allFunctions: functions,
|
|
||||||
callSequence: calls,
|
|
||||||
expressionsCompiler: this.expressionsCompiler,
|
|
||||||
};
|
|
||||||
const code = compileCallSequence(context);
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ICompilationContext {
|
|
||||||
allFunctions: ISharedFunctionCollection;
|
|
||||||
callSequence: readonly IFunctionCall[];
|
|
||||||
expressionsCompiler: IExpressionsCompiler;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ICompiledFunctionCall {
|
|
||||||
readonly code: string;
|
|
||||||
readonly revertCode: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function compileCallSequence(context: ICompilationContext): ICompiledFunctionCall {
|
|
||||||
const compiledFunctions = context.callSequence
|
|
||||||
.flatMap((call) => compileSingleCall(call, context));
|
|
||||||
return {
|
|
||||||
code: merge(compiledFunctions.map((f) => f.code)),
|
|
||||||
revertCode: merge(compiledFunctions.map((f) => f.revertCode)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function compileSingleCall(
|
|
||||||
call: IFunctionCall,
|
|
||||||
context: ICompilationContext,
|
|
||||||
): ICompiledFunctionCall[] {
|
|
||||||
const func = context.allFunctions.getFunctionByName(call.functionName);
|
|
||||||
ensureThatCallArgumentsExistInParameterDefinition(func, call.args);
|
|
||||||
if (func.body.code) { // Function with inline code
|
|
||||||
const compiledCode = compileCode(func.body.code, call.args, context.expressionsCompiler);
|
|
||||||
return [compiledCode];
|
|
||||||
}
|
|
||||||
// Function with inner calls
|
|
||||||
return func.body.calls
|
|
||||||
.map((innerCall) => {
|
|
||||||
const compiledArgs = compileArgs(innerCall.args, call.args, context.expressionsCompiler);
|
|
||||||
const compiledCall = new FunctionCall(innerCall.functionName, compiledArgs);
|
|
||||||
return compileSingleCall(compiledCall, context);
|
|
||||||
})
|
|
||||||
.flat();
|
|
||||||
}
|
|
||||||
|
|
||||||
function compileCode(
|
|
||||||
code: IFunctionCode,
|
|
||||||
args: IReadOnlyFunctionCallArgumentCollection,
|
|
||||||
compiler: IExpressionsCompiler,
|
|
||||||
): ICompiledFunctionCall {
|
|
||||||
return {
|
|
||||||
code: compiler.compileExpressions(code.execute, args),
|
|
||||||
revertCode: compiler.compileExpressions(code.revert, args),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function compileArgs(
|
|
||||||
argsToCompile: IReadOnlyFunctionCallArgumentCollection,
|
|
||||||
args: IReadOnlyFunctionCallArgumentCollection,
|
|
||||||
compiler: IExpressionsCompiler,
|
|
||||||
): IReadOnlyFunctionCallArgumentCollection {
|
|
||||||
return argsToCompile
|
|
||||||
.getAllParameterNames()
|
|
||||||
.map((parameterName) => {
|
|
||||||
const { argumentValue } = argsToCompile.getArgument(parameterName);
|
|
||||||
const compiledValue = compiler.compileExpressions(argumentValue, args);
|
|
||||||
return new FunctionCallArgument(parameterName, compiledValue);
|
|
||||||
})
|
|
||||||
.reduce((compiledArgs, arg) => {
|
|
||||||
compiledArgs.addArgument(arg);
|
|
||||||
return compiledArgs;
|
|
||||||
}, new FunctionCallArgumentCollection());
|
|
||||||
}
|
|
||||||
|
|
||||||
function merge(codeParts: readonly string[]): string {
|
|
||||||
return codeParts
|
|
||||||
.filter((part) => part?.length > 0)
|
|
||||||
.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
function ensureThatCallArgumentsExistInParameterDefinition(
|
|
||||||
func: ISharedFunction,
|
|
||||||
args: IReadOnlyFunctionCallArgumentCollection,
|
|
||||||
): void {
|
|
||||||
const callArgumentNames = args.getAllParameterNames();
|
|
||||||
const functionParameterNames = func.parameters.all.map((param) => param.name) || [];
|
|
||||||
const unexpectedParameters = findUnexpectedParameters(callArgumentNames, functionParameterNames);
|
|
||||||
throwIfNotEmpty(func.name, unexpectedParameters, functionParameterNames);
|
|
||||||
}
|
|
||||||
|
|
||||||
function findUnexpectedParameters(
|
|
||||||
callArgumentNames: string[],
|
|
||||||
functionParameterNames: string[],
|
|
||||||
): string[] {
|
|
||||||
if (!callArgumentNames.length && !functionParameterNames.length) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return callArgumentNames
|
|
||||||
.filter((callParam) => !functionParameterNames.includes(callParam));
|
|
||||||
}
|
|
||||||
|
|
||||||
function throwIfNotEmpty(
|
|
||||||
functionName: string,
|
|
||||||
unexpectedParameters: string[],
|
|
||||||
expectedParameters: string[],
|
|
||||||
) {
|
|
||||||
if (!unexpectedParameters.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new Error(
|
|
||||||
// eslint-disable-next-line prefer-template
|
|
||||||
`Function "${functionName}" has unexpected parameter(s) provided: `
|
|
||||||
+ `"${unexpectedParameters.join('", "')}"`
|
|
||||||
+ '. Expected parameter(s): '
|
|
||||||
+ (expectedParameters.length ? `"${expectedParameters.join('", "')}"` : 'none'),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
|
||||||
|
import { ISharedFunctionCollection } from '../../ISharedFunctionCollection';
|
||||||
|
import { FunctionCallCompiler } from './FunctionCallCompiler';
|
||||||
|
import { CompiledCode } from './CompiledCode';
|
||||||
|
import { FunctionCallCompilationContext } from './FunctionCallCompilationContext';
|
||||||
|
import { SingleCallCompiler } from './SingleCall/SingleCallCompiler';
|
||||||
|
import { AdaptiveFunctionCallCompiler } from './SingleCall/AdaptiveFunctionCallCompiler';
|
||||||
|
import { CodeSegmentMerger } from './CodeSegmentJoin/CodeSegmentMerger';
|
||||||
|
import { NewlineCodeSegmentMerger } from './CodeSegmentJoin/NewlineCodeSegmentMerger';
|
||||||
|
|
||||||
|
export class FunctionCallSequenceCompiler implements FunctionCallCompiler {
|
||||||
|
public static readonly instance: FunctionCallCompiler = new FunctionCallSequenceCompiler();
|
||||||
|
|
||||||
|
/* The constructor is protected to enforce the singleton pattern. */
|
||||||
|
protected constructor(
|
||||||
|
private readonly singleCallCompiler: SingleCallCompiler = new AdaptiveFunctionCallCompiler(),
|
||||||
|
private readonly codeSegmentMerger: CodeSegmentMerger = new NewlineCodeSegmentMerger(),
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public compileFunctionCalls(
|
||||||
|
calls: readonly FunctionCall[],
|
||||||
|
functions: ISharedFunctionCollection,
|
||||||
|
): CompiledCode {
|
||||||
|
if (!functions) { throw new Error('missing functions'); }
|
||||||
|
if (!calls?.length) { throw new Error('missing calls'); }
|
||||||
|
if (calls.some((f) => !f)) { throw new Error('missing function call'); }
|
||||||
|
const context: FunctionCallCompilationContext = {
|
||||||
|
allFunctions: functions,
|
||||||
|
rootCallSequence: calls,
|
||||||
|
singleCallCompiler: this.singleCallCompiler,
|
||||||
|
};
|
||||||
|
const codeSegments = context.rootCallSequence
|
||||||
|
.flatMap((call) => this.singleCallCompiler.compileSingleCall(call, context));
|
||||||
|
return this.codeSegmentMerger.mergeCodeParts(codeSegments);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { ISharedFunctionCollection } from '@/application/Parser/Script/Compiler/Function/ISharedFunctionCollection';
|
|
||||||
import { IFunctionCall } from '../IFunctionCall';
|
|
||||||
import { ICompiledCode } from './ICompiledCode';
|
|
||||||
|
|
||||||
export interface IFunctionCallCompiler {
|
|
||||||
compileCall(
|
|
||||||
calls: IFunctionCall[],
|
|
||||||
functions: ISharedFunctionCollection): ICompiledCode;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import { FunctionCall } from '../../FunctionCall';
|
||||||
|
import { CompiledCode } from '../CompiledCode';
|
||||||
|
import { FunctionCallCompilationContext } from '../FunctionCallCompilationContext';
|
||||||
|
import { IReadOnlyFunctionCallArgumentCollection } from '../../Argument/IFunctionCallArgumentCollection';
|
||||||
|
import { ISharedFunction } from '../../../ISharedFunction';
|
||||||
|
import { SingleCallCompiler } from './SingleCallCompiler';
|
||||||
|
import { SingleCallCompilerStrategy } from './SingleCallCompilerStrategy';
|
||||||
|
import { InlineFunctionCallCompiler } from './Strategies/InlineFunctionCallCompiler';
|
||||||
|
import { NestedFunctionCallCompiler } from './Strategies/NestedFunctionCallCompiler';
|
||||||
|
|
||||||
|
export class AdaptiveFunctionCallCompiler implements SingleCallCompiler {
|
||||||
|
public constructor(
|
||||||
|
private readonly strategies: SingleCallCompilerStrategy[] = [
|
||||||
|
new InlineFunctionCallCompiler(),
|
||||||
|
new NestedFunctionCallCompiler(),
|
||||||
|
],
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public compileSingleCall(
|
||||||
|
call: FunctionCall,
|
||||||
|
context: FunctionCallCompilationContext,
|
||||||
|
): CompiledCode[] {
|
||||||
|
const func = context.allFunctions.getFunctionByName(call.functionName);
|
||||||
|
ensureThatCallArgumentsExistInParameterDefinition(func, call.args);
|
||||||
|
const strategy = this.findStrategy(func);
|
||||||
|
return strategy.compileFunction(func, call, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private findStrategy(func: ISharedFunction): SingleCallCompilerStrategy {
|
||||||
|
const strategies = this.strategies.filter((strategy) => strategy.canCompile(func));
|
||||||
|
if (strategies.length > 1) {
|
||||||
|
throw new Error('Multiple strategies found to compile the function call.');
|
||||||
|
}
|
||||||
|
if (strategies.length === 0) {
|
||||||
|
throw new Error('No strategies found to compile the function call.');
|
||||||
|
}
|
||||||
|
return strategies[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureThatCallArgumentsExistInParameterDefinition(
|
||||||
|
func: ISharedFunction,
|
||||||
|
callArguments: IReadOnlyFunctionCallArgumentCollection,
|
||||||
|
): void {
|
||||||
|
const callArgumentNames = callArguments.getAllParameterNames();
|
||||||
|
const functionParameterNames = func.parameters.all.map((param) => param.name) || [];
|
||||||
|
const unexpectedParameters = findUnexpectedParameters(callArgumentNames, functionParameterNames);
|
||||||
|
throwIfUnexpectedParametersExist(func.name, unexpectedParameters, functionParameterNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
function findUnexpectedParameters(
|
||||||
|
callArgumentNames: string[],
|
||||||
|
functionParameterNames: string[],
|
||||||
|
): string[] {
|
||||||
|
if (!callArgumentNames.length && !functionParameterNames.length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return callArgumentNames
|
||||||
|
.filter((callParam) => !functionParameterNames.includes(callParam));
|
||||||
|
}
|
||||||
|
|
||||||
|
function throwIfUnexpectedParametersExist(
|
||||||
|
functionName: string,
|
||||||
|
unexpectedParameters: string[],
|
||||||
|
expectedParameters: string[],
|
||||||
|
) {
|
||||||
|
if (!unexpectedParameters.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
// eslint-disable-next-line prefer-template
|
||||||
|
`Function "${functionName}" has unexpected parameter(s) provided: `
|
||||||
|
+ `"${unexpectedParameters.join('", "')}"`
|
||||||
|
+ '. Expected parameter(s): '
|
||||||
|
+ (expectedParameters.length ? `"${expectedParameters.join('", "')}"` : 'none'),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { FunctionCall } from '../../FunctionCall';
|
||||||
|
import { CompiledCode } from '../CompiledCode';
|
||||||
|
import { FunctionCallCompilationContext } from '../FunctionCallCompilationContext';
|
||||||
|
|
||||||
|
export interface SingleCallCompiler {
|
||||||
|
compileSingleCall(
|
||||||
|
call: FunctionCall,
|
||||||
|
context: FunctionCallCompilationContext,
|
||||||
|
): CompiledCode[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { ISharedFunction } from '@/application/Parser/Script/Compiler/Function/ISharedFunction';
|
||||||
|
import { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
|
||||||
|
import { CompiledCode } from '../CompiledCode';
|
||||||
|
import { FunctionCallCompilationContext } from '../FunctionCallCompilationContext';
|
||||||
|
|
||||||
|
export interface SingleCallCompilerStrategy {
|
||||||
|
canCompile(func: ISharedFunction): boolean;
|
||||||
|
compileFunction(
|
||||||
|
calledFunction: ISharedFunction,
|
||||||
|
callToFunction: FunctionCall,
|
||||||
|
context: FunctionCallCompilationContext,
|
||||||
|
): CompiledCode[],
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
|
||||||
|
import { FunctionCallCompilationContext } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/FunctionCallCompilationContext';
|
||||||
|
|
||||||
|
export interface ArgumentCompiler {
|
||||||
|
createCompiledNestedCall(
|
||||||
|
nestedFunctionCall: FunctionCall,
|
||||||
|
parentFunctionCall: FunctionCall,
|
||||||
|
context: FunctionCallCompilationContext,
|
||||||
|
): FunctionCall;
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import { IReadOnlyFunctionCallArgumentCollection } from '@/application/Parser/Script/Compiler/Function/Call/Argument/IFunctionCallArgumentCollection';
|
||||||
|
import { FunctionCallArgument } from '@/application/Parser/Script/Compiler/Function/Call/Argument/FunctionCallArgument';
|
||||||
|
import { FunctionCallArgumentCollection } from '@/application/Parser/Script/Compiler/Function/Call/Argument/FunctionCallArgumentCollection';
|
||||||
|
import { ExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/ExpressionsCompiler';
|
||||||
|
import { IExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/IExpressionsCompiler';
|
||||||
|
import { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
|
||||||
|
import { FunctionCallCompilationContext } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/FunctionCallCompilationContext';
|
||||||
|
import { ParsedFunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/ParsedFunctionCall';
|
||||||
|
import { ArgumentCompiler } from './ArgumentCompiler';
|
||||||
|
|
||||||
|
export class NestedFunctionArgumentCompiler implements ArgumentCompiler {
|
||||||
|
constructor(
|
||||||
|
private readonly expressionsCompiler: IExpressionsCompiler = new ExpressionsCompiler(),
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public createCompiledNestedCall(
|
||||||
|
nestedFunction: FunctionCall,
|
||||||
|
parentFunction: FunctionCall,
|
||||||
|
context: FunctionCallCompilationContext,
|
||||||
|
): FunctionCall {
|
||||||
|
const compiledArgs = compileNestedFunctionArguments(
|
||||||
|
nestedFunction,
|
||||||
|
parentFunction.args,
|
||||||
|
context,
|
||||||
|
this.expressionsCompiler,
|
||||||
|
);
|
||||||
|
const compiledCall = new ParsedFunctionCall(nestedFunction.functionName, compiledArgs);
|
||||||
|
return compiledCall;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function compileNestedFunctionArguments(
|
||||||
|
nestedFunction: FunctionCall,
|
||||||
|
parentFunctionArgs: IReadOnlyFunctionCallArgumentCollection,
|
||||||
|
context: FunctionCallCompilationContext,
|
||||||
|
expressionsCompiler: IExpressionsCompiler,
|
||||||
|
): IReadOnlyFunctionCallArgumentCollection {
|
||||||
|
const requiredParameterNames = context
|
||||||
|
.allFunctions
|
||||||
|
.getRequiredParameterNames(nestedFunction.functionName);
|
||||||
|
const compiledArguments = nestedFunction.args
|
||||||
|
.getAllParameterNames()
|
||||||
|
// Compile each argument value
|
||||||
|
.map((paramName) => ({
|
||||||
|
parameterName: paramName,
|
||||||
|
compiledArgumentValue: compileArgument(
|
||||||
|
paramName,
|
||||||
|
nestedFunction,
|
||||||
|
parentFunctionArgs,
|
||||||
|
expressionsCompiler,
|
||||||
|
),
|
||||||
|
}))
|
||||||
|
// Filter out arguments with absent values
|
||||||
|
.filter(({
|
||||||
|
parameterName,
|
||||||
|
compiledArgumentValue,
|
||||||
|
}) => isValidNonAbsentArgumentValue(
|
||||||
|
parameterName,
|
||||||
|
compiledArgumentValue,
|
||||||
|
requiredParameterNames,
|
||||||
|
))
|
||||||
|
/*
|
||||||
|
Create argument object with non-absent values.
|
||||||
|
This is done after eliminating absent values because otherwise creating argument object
|
||||||
|
with absent values throws error.
|
||||||
|
*/
|
||||||
|
.map(({
|
||||||
|
parameterName,
|
||||||
|
compiledArgumentValue,
|
||||||
|
}) => new FunctionCallArgument(parameterName, compiledArgumentValue));
|
||||||
|
return buildArgumentCollectionFromArguments(compiledArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidNonAbsentArgumentValue(
|
||||||
|
parameterName: string,
|
||||||
|
argumentValue: string | undefined,
|
||||||
|
requiredParameterNames: string[],
|
||||||
|
): boolean {
|
||||||
|
if (argumentValue) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!requiredParameterNames.includes(parameterName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw new Error(`Compilation resulted in empty value for required parameter: "${parameterName}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function compileArgument(
|
||||||
|
parameterName: string,
|
||||||
|
nestedFunction: FunctionCall,
|
||||||
|
parentFunctionArgs: IReadOnlyFunctionCallArgumentCollection,
|
||||||
|
expressionsCompiler: IExpressionsCompiler,
|
||||||
|
): string {
|
||||||
|
try {
|
||||||
|
const { argumentValue: codeInArgument } = nestedFunction.args.getArgument(parameterName);
|
||||||
|
return expressionsCompiler.compileExpressions(codeInArgument, parentFunctionArgs);
|
||||||
|
} catch (err) {
|
||||||
|
throw new AggregateError([err], `Error when compiling argument for "${parameterName}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildArgumentCollectionFromArguments(
|
||||||
|
args: FunctionCallArgument[],
|
||||||
|
): FunctionCallArgumentCollection {
|
||||||
|
return args.reduce((compiledArgs, arg) => {
|
||||||
|
compiledArgs.addArgument(arg);
|
||||||
|
return compiledArgs;
|
||||||
|
}, new FunctionCallArgumentCollection());
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { ExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/ExpressionsCompiler';
|
||||||
|
import { IExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/IExpressionsCompiler';
|
||||||
|
import { ISharedFunction } from '@/application/Parser/Script/Compiler/Function/ISharedFunction';
|
||||||
|
import { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
|
||||||
|
import { CompiledCode } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/CompiledCode';
|
||||||
|
import { SingleCallCompilerStrategy } from '../SingleCallCompilerStrategy';
|
||||||
|
|
||||||
|
export class InlineFunctionCallCompiler implements SingleCallCompilerStrategy {
|
||||||
|
public constructor(
|
||||||
|
private readonly expressionsCompiler: IExpressionsCompiler = new ExpressionsCompiler(),
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public canCompile(func: ISharedFunction): boolean {
|
||||||
|
return func.body.code !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
public compileFunction(
|
||||||
|
calledFunction: ISharedFunction,
|
||||||
|
callToFunction: FunctionCall,
|
||||||
|
): CompiledCode[] {
|
||||||
|
const { code } = calledFunction.body;
|
||||||
|
const { args } = callToFunction;
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
code: this.expressionsCompiler.compileExpressions(code.execute, args),
|
||||||
|
revertCode: this.expressionsCompiler.compileExpressions(code.revert, args),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { ISharedFunction } from '@/application/Parser/Script/Compiler/Function/ISharedFunction';
|
||||||
|
import { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
|
||||||
|
import { FunctionCallCompilationContext } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/FunctionCallCompilationContext';
|
||||||
|
import { CompiledCode } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/CompiledCode';
|
||||||
|
import { SingleCallCompilerStrategy } from '../SingleCallCompilerStrategy';
|
||||||
|
import { ArgumentCompiler } from './Argument/ArgumentCompiler';
|
||||||
|
import { NestedFunctionArgumentCompiler } from './Argument/NestedFunctionArgumentCompiler';
|
||||||
|
|
||||||
|
export class NestedFunctionCallCompiler implements SingleCallCompilerStrategy {
|
||||||
|
public constructor(
|
||||||
|
private readonly argumentCompiler: ArgumentCompiler = new NestedFunctionArgumentCompiler(),
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public canCompile(func: ISharedFunction): boolean {
|
||||||
|
return func.body.calls !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
public compileFunction(
|
||||||
|
calledFunction: ISharedFunction,
|
||||||
|
callToFunction: FunctionCall,
|
||||||
|
context: FunctionCallCompilationContext,
|
||||||
|
): CompiledCode[] {
|
||||||
|
const nestedCalls = calledFunction.body.calls;
|
||||||
|
return nestedCalls.map((nestedCall) => {
|
||||||
|
try {
|
||||||
|
const compiledParentCall = this.argumentCompiler
|
||||||
|
.createCompiledNestedCall(nestedCall, callToFunction, context);
|
||||||
|
const compiledNestedCall = context.singleCallCompiler
|
||||||
|
.compileSingleCall(compiledParentCall, context);
|
||||||
|
return compiledNestedCall;
|
||||||
|
} catch (err) {
|
||||||
|
throw new AggregateError([err], `Error with call to "${nestedCall.functionName}" function from "${callToFunction.functionName}" function`);
|
||||||
|
}
|
||||||
|
}).flat();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,6 @@
|
|||||||
import { IReadOnlyFunctionCallArgumentCollection } from './Argument/IFunctionCallArgumentCollection';
|
import { IReadOnlyFunctionCallArgumentCollection } from './Argument/IFunctionCallArgumentCollection';
|
||||||
import { IFunctionCall } from './IFunctionCall';
|
|
||||||
|
|
||||||
export class FunctionCall implements IFunctionCall {
|
export interface FunctionCall {
|
||||||
constructor(
|
readonly functionName: string;
|
||||||
public readonly functionName: string,
|
readonly args: IReadOnlyFunctionCallArgumentCollection;
|
||||||
public readonly args: IReadOnlyFunctionCallArgumentCollection,
|
|
||||||
) {
|
|
||||||
if (!functionName) {
|
|
||||||
throw new Error('missing function name in function call');
|
|
||||||
}
|
|
||||||
if (!args) {
|
|
||||||
throw new Error('missing args');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import type { FunctionCallData, FunctionCallsData, FunctionCallParametersData } from '@/application/collections/';
|
import type { FunctionCallData, FunctionCallsData, FunctionCallParametersData } from '@/application/collections/';
|
||||||
import { IFunctionCall } from './IFunctionCall';
|
import { FunctionCall } from './FunctionCall';
|
||||||
import { FunctionCallArgumentCollection } from './Argument/FunctionCallArgumentCollection';
|
import { FunctionCallArgumentCollection } from './Argument/FunctionCallArgumentCollection';
|
||||||
import { FunctionCallArgument } from './Argument/FunctionCallArgument';
|
import { FunctionCallArgument } from './Argument/FunctionCallArgument';
|
||||||
import { FunctionCall } from './FunctionCall';
|
import { ParsedFunctionCall } from './ParsedFunctionCall';
|
||||||
|
|
||||||
export function parseFunctionCalls(calls: FunctionCallsData): IFunctionCall[] {
|
export function parseFunctionCalls(calls: FunctionCallsData): FunctionCall[] {
|
||||||
if (calls === undefined) {
|
if (calls === undefined) {
|
||||||
throw new Error('missing call data');
|
throw new Error('missing call data');
|
||||||
}
|
}
|
||||||
@@ -22,12 +22,12 @@ function getCallSequence(calls: FunctionCallsData): FunctionCallData[] {
|
|||||||
return [calls as FunctionCallData];
|
return [calls as FunctionCallData];
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseFunctionCall(call: FunctionCallData): IFunctionCall {
|
function parseFunctionCall(call: FunctionCallData): FunctionCall {
|
||||||
if (!call) {
|
if (!call) {
|
||||||
throw new Error('missing call data');
|
throw new Error('missing call data');
|
||||||
}
|
}
|
||||||
const callArgs = parseArgs(call.parameters);
|
const callArgs = parseArgs(call.parameters);
|
||||||
return new FunctionCall(call.function, callArgs);
|
return new ParsedFunctionCall(call.function, callArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseArgs(
|
function parseArgs(
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
import { IReadOnlyFunctionCallArgumentCollection } from './Argument/IFunctionCallArgumentCollection';
|
|
||||||
|
|
||||||
export interface IFunctionCall {
|
|
||||||
readonly functionName: string;
|
|
||||||
readonly args: IReadOnlyFunctionCallArgumentCollection;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { IReadOnlyFunctionCallArgumentCollection } from './Argument/IFunctionCallArgumentCollection';
|
||||||
|
import { FunctionCall } from './FunctionCall';
|
||||||
|
|
||||||
|
export class ParsedFunctionCall implements FunctionCall {
|
||||||
|
constructor(
|
||||||
|
public readonly functionName: string,
|
||||||
|
public readonly args: IReadOnlyFunctionCallArgumentCollection,
|
||||||
|
) {
|
||||||
|
if (!functionName) {
|
||||||
|
throw new Error('missing function name in function call');
|
||||||
|
}
|
||||||
|
if (!args) {
|
||||||
|
throw new Error('missing args');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { IReadOnlyFunctionParameterCollection } from './Parameter/IFunctionParameterCollection';
|
import { IReadOnlyFunctionParameterCollection } from './Parameter/IFunctionParameterCollection';
|
||||||
import { IFunctionCall } from './Call/IFunctionCall';
|
import { FunctionCall } from './Call/FunctionCall';
|
||||||
|
|
||||||
export interface ISharedFunction {
|
export interface ISharedFunction {
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
@@ -9,8 +9,8 @@ export interface ISharedFunction {
|
|||||||
|
|
||||||
export interface ISharedFunctionBody {
|
export interface ISharedFunctionBody {
|
||||||
readonly type: FunctionBodyType;
|
readonly type: FunctionBodyType;
|
||||||
readonly code: IFunctionCode;
|
readonly code: IFunctionCode | undefined;
|
||||||
readonly calls: readonly IFunctionCall[];
|
readonly calls: readonly FunctionCall[] | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum FunctionBodyType {
|
export enum FunctionBodyType {
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ import { ISharedFunction } from './ISharedFunction';
|
|||||||
|
|
||||||
export interface ISharedFunctionCollection {
|
export interface ISharedFunctionCollection {
|
||||||
getFunctionByName(name: string): ISharedFunction;
|
getFunctionByName(name: string): ISharedFunction;
|
||||||
|
getRequiredParameterNames(functionName: string): string[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { IFunctionCall } from './Call/IFunctionCall';
|
import { FunctionCall } from './Call/FunctionCall';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FunctionBodyType, IFunctionCode, ISharedFunction, ISharedFunctionBody,
|
FunctionBodyType, IFunctionCode, ISharedFunction, ISharedFunctionBody,
|
||||||
@@ -8,7 +8,7 @@ import { IReadOnlyFunctionParameterCollection } from './Parameter/IFunctionParam
|
|||||||
export function createCallerFunction(
|
export function createCallerFunction(
|
||||||
name: string,
|
name: string,
|
||||||
parameters: IReadOnlyFunctionParameterCollection,
|
parameters: IReadOnlyFunctionParameterCollection,
|
||||||
callSequence: readonly IFunctionCall[],
|
callSequence: readonly FunctionCall[],
|
||||||
): ISharedFunction {
|
): ISharedFunction {
|
||||||
if (!callSequence || !callSequence.length) {
|
if (!callSequence || !callSequence.length) {
|
||||||
throw new Error(`missing call sequence in function "${name}"`);
|
throw new Error(`missing call sequence in function "${name}"`);
|
||||||
@@ -38,7 +38,7 @@ class SharedFunction implements ISharedFunction {
|
|||||||
constructor(
|
constructor(
|
||||||
public readonly name: string,
|
public readonly name: string,
|
||||||
public readonly parameters: IReadOnlyFunctionParameterCollection,
|
public readonly parameters: IReadOnlyFunctionParameterCollection,
|
||||||
content: IFunctionCode | readonly IFunctionCall[],
|
content: IFunctionCode | readonly FunctionCall[],
|
||||||
bodyType: FunctionBodyType,
|
bodyType: FunctionBodyType,
|
||||||
) {
|
) {
|
||||||
if (!name) { throw new Error('missing function name'); }
|
if (!name) { throw new Error('missing function name'); }
|
||||||
@@ -46,7 +46,7 @@ class SharedFunction implements ISharedFunction {
|
|||||||
this.body = {
|
this.body = {
|
||||||
type: bodyType,
|
type: bodyType,
|
||||||
code: bodyType === FunctionBodyType.Code ? content as IFunctionCode : undefined,
|
code: bodyType === FunctionBodyType.Code ? content as IFunctionCode : undefined,
|
||||||
calls: bodyType === FunctionBodyType.Calls ? content as readonly IFunctionCall[] : undefined,
|
calls: bodyType === FunctionBodyType.Calls ? content as readonly FunctionCall[] : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,15 @@ export class SharedFunctionCollection implements ISharedFunctionCollection {
|
|||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getRequiredParameterNames(functionName: string): string[] {
|
||||||
|
return this
|
||||||
|
.getFunctionByName(functionName)
|
||||||
|
.parameters
|
||||||
|
.all
|
||||||
|
.filter((parameter) => !parameter.isOptional)
|
||||||
|
.map((parameter) => parameter.name);
|
||||||
|
}
|
||||||
|
|
||||||
private has(functionName: string) {
|
private has(functionName: string) {
|
||||||
return this.functionsByName.has(functionName);
|
return this.functionsByName.has(functionName);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import { NoEmptyLines } from '@/application/Parser/Script/Validation/Rules/NoEmp
|
|||||||
import { ICodeValidator } from '@/application/Parser/Script/Validation/ICodeValidator';
|
import { ICodeValidator } from '@/application/Parser/Script/Validation/ICodeValidator';
|
||||||
import { IScriptCompiler } from './IScriptCompiler';
|
import { IScriptCompiler } from './IScriptCompiler';
|
||||||
import { ISharedFunctionCollection } from './Function/ISharedFunctionCollection';
|
import { ISharedFunctionCollection } from './Function/ISharedFunctionCollection';
|
||||||
import { IFunctionCallCompiler } from './Function/Call/Compiler/IFunctionCallCompiler';
|
import { FunctionCallSequenceCompiler } from './Function/Call/Compiler/FunctionCallSequenceCompiler';
|
||||||
import { FunctionCallCompiler } from './Function/Call/Compiler/FunctionCallCompiler';
|
import { FunctionCallCompiler } from './Function/Call/Compiler/FunctionCallCompiler';
|
||||||
import { ISharedFunctionsParser } from './Function/ISharedFunctionsParser';
|
import { ISharedFunctionsParser } from './Function/ISharedFunctionsParser';
|
||||||
import { SharedFunctionsParser } from './Function/SharedFunctionsParser';
|
import { SharedFunctionsParser } from './Function/SharedFunctionsParser';
|
||||||
import { parseFunctionCalls } from './Function/Call/FunctionCallParser';
|
import { parseFunctionCalls } from './Function/Call/FunctionCallParser';
|
||||||
import { ICompiledCode } from './Function/Call/Compiler/ICompiledCode';
|
import { CompiledCode } from './Function/Call/Compiler/CompiledCode';
|
||||||
|
|
||||||
export class ScriptCompiler implements IScriptCompiler {
|
export class ScriptCompiler implements IScriptCompiler {
|
||||||
private readonly functions: ISharedFunctionCollection;
|
private readonly functions: ISharedFunctionCollection;
|
||||||
@@ -21,7 +21,7 @@ export class ScriptCompiler implements IScriptCompiler {
|
|||||||
functions: readonly FunctionData[] | undefined,
|
functions: readonly FunctionData[] | undefined,
|
||||||
syntax: ILanguageSyntax,
|
syntax: ILanguageSyntax,
|
||||||
sharedFunctionsParser: ISharedFunctionsParser = SharedFunctionsParser.instance,
|
sharedFunctionsParser: ISharedFunctionsParser = SharedFunctionsParser.instance,
|
||||||
private readonly callCompiler: IFunctionCallCompiler = FunctionCallCompiler.instance,
|
private readonly callCompiler: FunctionCallCompiler = FunctionCallSequenceCompiler.instance,
|
||||||
private readonly codeValidator: ICodeValidator = CodeValidator.instance,
|
private readonly codeValidator: ICodeValidator = CodeValidator.instance,
|
||||||
) {
|
) {
|
||||||
if (!syntax) { throw new Error('missing syntax'); }
|
if (!syntax) { throw new Error('missing syntax'); }
|
||||||
@@ -40,7 +40,7 @@ export class ScriptCompiler implements IScriptCompiler {
|
|||||||
if (!script) { throw new Error('missing script'); }
|
if (!script) { throw new Error('missing script'); }
|
||||||
try {
|
try {
|
||||||
const calls = parseFunctionCalls(script.call);
|
const calls = parseFunctionCalls(script.call);
|
||||||
const compiledCode = this.callCompiler.compileCall(calls, this.functions);
|
const compiledCode = this.callCompiler.compileFunctionCalls(calls, this.functions);
|
||||||
validateCompiledCode(compiledCode, this.codeValidator);
|
validateCompiledCode(compiledCode, this.codeValidator);
|
||||||
return new ScriptCode(
|
return new ScriptCode(
|
||||||
compiledCode.code,
|
compiledCode.code,
|
||||||
@@ -52,7 +52,7 @@ export class ScriptCompiler implements IScriptCompiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateCompiledCode(compiledCode: ICompiledCode, validator: ICodeValidator): void {
|
function validateCompiledCode(compiledCode: CompiledCode, validator: ICodeValidator): void {
|
||||||
[compiledCode.code, compiledCode.revertCode].forEach(
|
[compiledCode.code, compiledCode.revertCode].forEach(
|
||||||
(code) => validator.throwIfInvalid(code, [new NoEmptyLines()]),
|
(code) => validator.throwIfInvalid(code, [new NoEmptyLines()]),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Structure documented in "docs/collection-files.md"
|
# Structure is documented in "docs/collection-files.md"
|
||||||
os: linux
|
os: linux
|
||||||
scripting:
|
scripting:
|
||||||
language: shellscript
|
language: shellscript
|
||||||
@@ -135,7 +135,7 @@ actions:
|
|||||||
|
|
||||||
It uses `~/.ash_history` as the history file [3].
|
It uses `~/.ash_history` as the history file [3].
|
||||||
|
|
||||||
[1]: https://web.archive.org/web/20221030142637/https://en.wikipedia.org/wiki/Almquist_shell#Embededed_Linux "Almquist shell - Wikipedia | wikipedia.org"
|
[1]: https://web.archive.org/web/20221030142637/https://en.wikipedia.org/wiki/Almquist_shell#Embedded_Linux "Almquist shell - Wikipedia | wikipedia.org"
|
||||||
[2]: https://web.archive.org/web/20221029135416/https://android.googlesource.com/platform/system/core/+/master/shell_and_utilities/README.md "Android's shell and utilities | android.googlesource.com"
|
[2]: https://web.archive.org/web/20221029135416/https://android.googlesource.com/platform/system/core/+/master/shell_and_utilities/README.md "Android's shell and utilities | android.googlesource.com"
|
||||||
[3]: https://web.archive.org/web/20221029135513/https://github.com/brgl/busybox/blob/abbf17abccbf832365d9acf1c280369ba7d5f8b2/shell/ash.c#L13626 "busybox/ash.c source code | github.com/brgl/busybox"
|
[3]: https://web.archive.org/web/20221029135513/https://github.com/brgl/busybox/blob/abbf17abccbf832365d9acf1c280369ba7d5f8b2/shell/ash.c#L13626 "busybox/ash.c source code | github.com/brgl/busybox"
|
||||||
call:
|
call:
|
||||||
@@ -223,8 +223,8 @@ actions:
|
|||||||
1. Temporary Windows files. Wine saves temporary Windows files at `<wine folder>/drive_c/windows/temp/` [1] [2].
|
1. Temporary Windows files. Wine saves temporary Windows files at `<wine folder>/drive_c/windows/temp/` [1] [2].
|
||||||
2. Temporary Wine application cache [3] that is not connected to inner Windows files.
|
2. Temporary Wine application cache [3] that is not connected to inner Windows files.
|
||||||
|
|
||||||
[1]: https://web.archive.org/web/20180328090608/http://www.zdnet.com/article/keeping-temp-folders-clean/ "Keeping temp folders clean | ZDNet | dnet.com"
|
[1]: https://web.archive.org/web/20180328090608/http://www.zdnet.com/article/keeping-temp-folders-clean/ "Keeping temp folders clean | ZDNet | zdnet.com"
|
||||||
[2]: https://web.archive.org/web/20221029135944/https://ubuntuforums.org/showthread.php?t=1006132 "Why does Wine have its own temp folders? | UbuntuForums | ubuntuforums.org"
|
[2]: https://web.archive.org/web/20221029135944/https://ubuntuforums.org/showthread.php?t=1006132 "Why does Wine have its own temp folders? | UbuntuForums | ubuntuforums.org"
|
||||||
[3]: https://web.archive.org/web/20221029135955/https://wiki.debian.org/Wine#Mono_and_Gecko "Wine - Debian Wiki | wiki.debian.org"
|
[3]: https://web.archive.org/web/20221029135955/https://wiki.debian.org/Wine#Mono_and_Gecko "Wine - Debian Wiki | wiki.debian.org"
|
||||||
code: |-
|
code: |-
|
||||||
# Temporary Windows files for global prefix
|
# Temporary Windows files for global prefix
|
||||||
@@ -232,7 +232,7 @@ actions:
|
|||||||
# Wine cache:
|
# Wine cache:
|
||||||
rm -rfv ~/.cache/wine/
|
rm -rfv ~/.cache/wine/
|
||||||
-
|
-
|
||||||
name: Clear Winetricks downloads cache
|
name: Clear Winetricks cache
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
Winetricks is a helper script to download and install various redistributable runtime libraries
|
Winetricks is a helper script to download and install various redistributable runtime libraries
|
||||||
@@ -254,7 +254,7 @@ actions:
|
|||||||
docs: |-
|
docs: |-
|
||||||
LibreOffice is a free and open-source office productivity software suite, a project of The Document Foundation (TDF) [1].
|
LibreOffice is a free and open-source office productivity software suite, a project of The Document Foundation (TDF) [1].
|
||||||
|
|
||||||
`registrymodifications.xcu` is an XML file that contains the user-specified settings [2].
|
`registrymodifications.xcu` is an XML file that contains the user-specified settings [2].
|
||||||
It is found inside the user settings directory (`~/.config/libreoffice/4/user`) [2].
|
It is found inside the user settings directory (`~/.config/libreoffice/4/user`) [2].
|
||||||
It includes thumbnails generated [3], and a recent document list [4].
|
It includes thumbnails generated [3], and a recent document list [4].
|
||||||
|
|
||||||
@@ -263,7 +263,7 @@ actions:
|
|||||||
[1]: https://web.archive.org/web/20221029140306/https://en.wikipedia.org/wiki/LibreOffice "LibreOffice | Wikipedia | en.wikipedia.org"
|
[1]: https://web.archive.org/web/20221029140306/https://en.wikipedia.org/wiki/LibreOffice "LibreOffice | Wikipedia | en.wikipedia.org"
|
||||||
[2]: https://web.archive.org/web/20221029140313/https://wiki.documentfoundation.org/images/b/b0/LibreOffice_config_extension_writing.pdf "Config specification | LibreOffice documentation | wiki.documentfoundation.org"
|
[2]: https://web.archive.org/web/20221029140313/https://wiki.documentfoundation.org/images/b/b0/LibreOffice_config_extension_writing.pdf "Config specification | LibreOffice documentation | wiki.documentfoundation.org"
|
||||||
[3]: https://web.archive.org/web/20221029140438/https://askubuntu.com/questions/996397/where-are-libre-office-thumbnails-stored/996528 "Where are Libre Office thumbnails stored? | Ask Ubuntu Forums | askubuntu.com"
|
[3]: https://web.archive.org/web/20221029140438/https://askubuntu.com/questions/996397/where-are-libre-office-thumbnails-stored/996528 "Where are Libre Office thumbnails stored? | Ask Ubuntu Forums | askubuntu.com"
|
||||||
[4]: https://web.archive.org/web/20221029140501/https://forum.openoffice.org/en/forum/viewtopic.php?f=6&t=102020 "Restoring Recent documents list < Apache OpenOffice Community Forum | forum.openoffice.org"
|
[4]: https://web.archive.org/web/20221029140501/https://forum.openoffice.org/en/forum/viewtopic.php?f=6&t=102020 "Restoring Recent documents list | Apache OpenOffice Community Forum | forum.openoffice.org"
|
||||||
code: |-
|
code: |-
|
||||||
# Global installation
|
# Global installation
|
||||||
rm -f ~/.config/libreoffice/4/user/registrymodifications.xcu
|
rm -f ~/.config/libreoffice/4/user/registrymodifications.xcu
|
||||||
@@ -315,7 +315,7 @@ actions:
|
|||||||
parameters:
|
parameters:
|
||||||
file: cookies.sqlite-shm
|
file: cookies.sqlite-shm
|
||||||
-
|
-
|
||||||
name: Clear Thunderbird session restore (open windows and tabs)
|
name: Clear Thunderbird session restoration data (open windows and tabs)
|
||||||
docs: |-
|
docs: |-
|
||||||
The default window layout is saved in the session file (`session.json`) [1].
|
The default window layout is saved in the session file (`session.json`) [1].
|
||||||
It includes data on what tabs are open [1].
|
It includes data on what tabs are open [1].
|
||||||
@@ -326,7 +326,7 @@ actions:
|
|||||||
parameters:
|
parameters:
|
||||||
file: session.json
|
file: session.json
|
||||||
-
|
-
|
||||||
name: Clear Thunderbird accounts
|
name: Clear Thunderbird passwords
|
||||||
docs: |-
|
docs: |-
|
||||||
`logins.json` stores encrypted passwords [1].
|
`logins.json` stores encrypted passwords [1].
|
||||||
It replaces `signons.sqlite`, which had replaced `signons.txt` [1].
|
It replaces `signons.sqlite`, which had replaced `signons.txt` [1].
|
||||||
@@ -397,7 +397,7 @@ actions:
|
|||||||
-
|
-
|
||||||
function: DeleteFileFromThunderbirdProfiles
|
function: DeleteFileFromThunderbirdProfiles
|
||||||
parameters:
|
parameters:
|
||||||
file: abook.mab # Thunderbird < v78
|
file: abook.mab # < Thunderbird v78
|
||||||
-
|
-
|
||||||
name: Clear Thunderbird collected address book
|
name: Clear Thunderbird collected address book
|
||||||
docs: |-
|
docs: |-
|
||||||
@@ -419,9 +419,9 @@ actions:
|
|||||||
-
|
-
|
||||||
function: DeleteFileFromThunderbirdProfiles
|
function: DeleteFileFromThunderbirdProfiles
|
||||||
parameters:
|
parameters:
|
||||||
file: history.mab # Thunderbird < v78
|
file: history.mab # < Thunderbird v78
|
||||||
-
|
-
|
||||||
name: Clear Thunderbird's history of clicked links
|
name: Clear clicked links history in Thunderbird
|
||||||
docs: |-
|
docs: |-
|
||||||
Thunderbird saves annotations, bookmarks, favorite icons, input history, keywords, and browsing history
|
Thunderbird saves annotations, bookmarks, favorite icons, input history, keywords, and browsing history
|
||||||
(a list of pages visited) [1].
|
(a list of pages visited) [1].
|
||||||
@@ -444,11 +444,11 @@ actions:
|
|||||||
parameters:
|
parameters:
|
||||||
file: places.sqlite.wal
|
file: places.sqlite.wal
|
||||||
-
|
-
|
||||||
category: Clear development tools data
|
category: Clear data from development tools
|
||||||
docs: |-
|
docs: |-
|
||||||
This category includes tools that are typically used by developers, also known as "developer tools".
|
This category includes tools that are typically used by developers, also known as "developer tools".
|
||||||
These tools allow a developer to create, test, and debug software.
|
These tools allow a developer to create, test, and debug software.
|
||||||
Their data may leak data about the developer, his/her usage patterns, the environment used for development or the developed project.
|
Their data may leak data about the developer, their usage patterns, the environment used for development or the developed project.
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Clear Python history
|
name: Clear Python history
|
||||||
@@ -479,7 +479,7 @@ actions:
|
|||||||
[1]: https://web.archive.org/web/20221029142001/https://en.wikipedia.org/wiki/Visual_Studio_Code "Visual Studio Code | Wikipedia | en.wikipedia.org"
|
[1]: https://web.archive.org/web/20221029142001/https://en.wikipedia.org/wiki/Visual_Studio_Code "Visual Studio Code | Wikipedia | en.wikipedia.org"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Clear Visual Studio Code Crash Reports
|
name: Clear Visual Studio Code crash reports
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
Visual Studio Code stores crash reports that later on are uploaded to Microsoft servers by default [1].
|
Visual Studio Code stores crash reports that later on are uploaded to Microsoft servers by default [1].
|
||||||
@@ -559,7 +559,7 @@ actions:
|
|||||||
[1]: https://web.archive.org/web/20221029142932/https://learn.microsoft.com/en-us/cli/azure/ "Azure Command-Line Interface (CLI) - Overview | Microsoft Learn | learn.microsoft.com"
|
[1]: https://web.archive.org/web/20221029142932/https://learn.microsoft.com/en-us/cli/azure/ "Azure Command-Line Interface (CLI) - Overview | Microsoft Learn | learn.microsoft.com"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Clear Azure CLI telemetry
|
name: Clear Azure CLI telemetry data
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
The Azure CLI stores telemetry in the `telemetry` directory and `telemetry.txt`, `logs/telemetry.txt` files [1].
|
The Azure CLI stores telemetry in the `telemetry` directory and `telemetry.txt`, `logs/telemetry.txt` files [1].
|
||||||
@@ -591,7 +591,7 @@ actions:
|
|||||||
command: az
|
command: az
|
||||||
code: az cache purge
|
code: az cache purge
|
||||||
-
|
-
|
||||||
name: Clear Azure login data (logs out of the current session)
|
name: Clear Azure login data (this will log you out of the current session)
|
||||||
recommend: strict
|
recommend: strict
|
||||||
docs: |-
|
docs: |-
|
||||||
This script cleans the login data in three steps:
|
This script cleans the login data in three steps:
|
||||||
@@ -652,7 +652,7 @@ actions:
|
|||||||
rm -rfv /.cache/epiphany/*
|
rm -rfv /.cache/epiphany/*
|
||||||
# Flatpak installation
|
# Flatpak installation
|
||||||
rm -rfv ~/.var/app/org.gnome.Epiphany/cache/*
|
rm -rfv ~/.var/app/org.gnome.Epiphany/cache/*
|
||||||
# Snap insallation
|
# Snap installation
|
||||||
rm -rfv ~/~/snap/epiphany/common/.cache/*
|
rm -rfv ~/~/snap/epiphany/common/.cache/*
|
||||||
-
|
-
|
||||||
name: Clear GNOME Web browsing history
|
name: Clear GNOME Web browsing history
|
||||||
@@ -733,7 +733,7 @@ actions:
|
|||||||
# Snap installation
|
# Snap installation
|
||||||
rm -rfv ~/snap/firefox/common/.cache/*
|
rm -rfv ~/snap/firefox/common/.cache/*
|
||||||
-
|
-
|
||||||
name: Clear Firefox Crash Reports
|
name: Clear Firefox crash reports
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
Firefox stores crash reports in `~/.mozilla/firefox/Crash Reports/` to submit them later [1].
|
Firefox stores crash reports in `~/.mozilla/firefox/Crash Reports/` to submit them later [1].
|
||||||
@@ -769,7 +769,7 @@ actions:
|
|||||||
parameters:
|
parameters:
|
||||||
path: cookies.sqlite
|
path: cookies.sqlite
|
||||||
-
|
-
|
||||||
name: Clear Firefox URL history (downloads, bookmarks, website visits, annotations, icons, inputs, keywords)
|
name: Clear Firefox browsing history (URLs, downloads, bookmarks, visits, etc.)
|
||||||
docs: |-
|
docs: |-
|
||||||
The file "places.sqlite" stores the annotations, bookmarks, favorite icons, input history, keywords, and browsing history (a record of visited pages) [1].
|
The file "places.sqlite" stores the annotations, bookmarks, favorite icons, input history, keywords, and browsing history (a record of visited pages) [1].
|
||||||
The tables include [1]:
|
The tables include [1]:
|
||||||
@@ -777,7 +777,7 @@ actions:
|
|||||||
- `moz_annos`: Annotations
|
- `moz_annos`: Annotations
|
||||||
- `moz_bookmarks`: Bookmarks
|
- `moz_bookmarks`: Bookmarks
|
||||||
- `moz_bookmarks_roots`: Bookmark roots i.e. places, menu, toolbar, tags, unfiled
|
- `moz_bookmarks_roots`: Bookmark roots i.e. places, menu, toolbar, tags, unfiled
|
||||||
- `moz_favicons`: Favourite icons - including URL of icon
|
- `moz_favicons`: Favorite icons - including URL of icon
|
||||||
- `moz_historyvisits`: A history of the number of times a site has been visited
|
- `moz_historyvisits`: A history of the number of times a site has been visited
|
||||||
- `moz_inputhistory`: A history of URLs typed by the user
|
- `moz_inputhistory`: A history of URLs typed by the user
|
||||||
- `moz_items_annos`: Item annotations
|
- `moz_items_annos`: Item annotations
|
||||||
@@ -860,7 +860,7 @@ actions:
|
|||||||
parameters:
|
parameters:
|
||||||
path: formhistory.sqlite
|
path: formhistory.sqlite
|
||||||
-
|
-
|
||||||
name: Clear Firefox multi-account containers data
|
name: Clear Firefox "Multi-Account Containers" data
|
||||||
docs: |-
|
docs: |-
|
||||||
The `containers.json` file stores the details of containers used by the [Container Tabs](https://web.archive.org/web/20221029214648/https://support.mozilla.org/en-US/kb/containers) feature [1].
|
The `containers.json` file stores the details of containers used by the [Container Tabs](https://web.archive.org/web/20221029214648/https://support.mozilla.org/en-US/kb/containers) feature [1].
|
||||||
|
|
||||||
@@ -882,7 +882,7 @@ actions:
|
|||||||
parameters:
|
parameters:
|
||||||
path: sessionstore.jsonlz4
|
path: sessionstore.jsonlz4
|
||||||
-
|
-
|
||||||
category: Clear system and/or kernel data
|
category: Clear system and kernel usage data
|
||||||
docs: |-
|
docs: |-
|
||||||
Your system (operating system along with other software on it) and kernel store
|
Your system (operating system along with other software on it) and kernel store
|
||||||
data that may reveal your behavior and can be considered sensitive. These scripts clean
|
data that may reveal your behavior and can be considered sensitive. These scripts clean
|
||||||
@@ -916,7 +916,7 @@ actions:
|
|||||||
sudo rm -rfv /var/crash/*
|
sudo rm -rfv /var/crash/*
|
||||||
sudo rm -rfv /var/lib/systemd/coredump/
|
sudo rm -rfv /var/lib/systemd/coredump/
|
||||||
-
|
-
|
||||||
name: Clear system (journald) logs
|
name: Clear system logs (`journald`)
|
||||||
docs: |-
|
docs: |-
|
||||||
journald is the part of systemd that captures, queries, and removes logs when needed [1].
|
journald is the part of systemd that captures, queries, and removes logs when needed [1].
|
||||||
It allows removing logs by setting `--vacuum-time` [2].
|
It allows removing logs by setting `--vacuum-time` [2].
|
||||||
@@ -974,7 +974,7 @@ actions:
|
|||||||
and acts like a database of all the recently used files [3].
|
and acts like a database of all the recently used files [3].
|
||||||
|
|
||||||
GTK is used by many GUI software. GNOME uses the GTK+ library [2], while KDE uses the QT library [4].
|
GTK is used by many GUI software. GNOME uses the GTK+ library [2], while KDE uses the QT library [4].
|
||||||
`recently-used.xbel` is also used by other third-party GTK 3 based applications such as Firefox,
|
`recently-used.xbel` is used by other third-party GTK 3 based applications such as Firefox,
|
||||||
Visual Studio Code (and all other Electron applications [5]), Thunderbird…
|
Visual Studio Code (and all other Electron applications [5]), Thunderbird…
|
||||||
See also: ["List of GTK applications | Wikipedia"](https://web.archive.org/web/20221029221112/https://en.wikipedia.org/wiki/List_of_GTK_applications).
|
See also: ["List of GTK applications | Wikipedia"](https://web.archive.org/web/20221029221112/https://en.wikipedia.org/wiki/List_of_GTK_applications).
|
||||||
|
|
||||||
@@ -995,7 +995,7 @@ actions:
|
|||||||
name: Clear KDE-tracked recently used items list
|
name: Clear KDE-tracked recently used items list
|
||||||
recommend: strict
|
recommend: strict
|
||||||
docs: |-
|
docs: |-
|
||||||
`.desktop` files are used by KDE to store recent documents and are exposed as an API [1].
|
`.desktop` files are used by KDE to store recent documents and are exposed through an API [1].
|
||||||
|
|
||||||
They are not only specific to the desktop environment, but also used by applications.
|
They are not only specific to the desktop environment, but also used by applications.
|
||||||
Likewise, they are used by the KDE implementation of QT components, such as [QFileDialog](https://web.archive.org/web/20221029221146/https://doc.qt.io/qt-5/qfiledialog.html)
|
Likewise, they are used by the KDE implementation of QT components, such as [QFileDialog](https://web.archive.org/web/20221029221146/https://doc.qt.io/qt-5/qfiledialog.html)
|
||||||
@@ -1013,10 +1013,10 @@ actions:
|
|||||||
# From Flatpak packages
|
# From Flatpak packages
|
||||||
rm -rfv ~/.var/app/*/data/*.desktop
|
rm -rfv ~/.var/app/*/data/*.desktop
|
||||||
-
|
-
|
||||||
name: Clear trash
|
name: Empty trash
|
||||||
docs: |-
|
docs: |-
|
||||||
The trash location is standardized by ["The FreeDesktop.org Trash specification"](https://web.archive.org/web/20221029151648/https://specifications.freedesktop.org/trash-spec/trashspec-latest.html) [1].
|
The trash location is standardized by ["The FreeDesktop.org Trash specification"](https://web.archive.org/web/20221029151648/https://specifications.freedesktop.org/trash-spec/trashspec-latest.html) [1].
|
||||||
It uses the `$XDG_DATA_HOME/Trash` directory, [1] which defaults to `$HOME/.local/share/Trash` [2].
|
It uses the `$XDG_DATA_HOME/Trash` directory [1], which defaults to `$HOME/.local/share/Trash` [2].
|
||||||
|
|
||||||
Flatpak applications may use the same trash using special APIs [3]. However, Snap applications do not have an API to
|
Flatpak applications may use the same trash using special APIs [3]. However, Snap applications do not have an API to
|
||||||
use the global trash [4].
|
use the global trash [4].
|
||||||
@@ -1104,7 +1104,7 @@ actions:
|
|||||||
Snap is a software packaging and deployment system developed by Canonical [1].
|
Snap is a software packaging and deployment system developed by Canonical [1].
|
||||||
The packages are called snaps, and the tool for using them is called snapd [1].
|
The packages are called snaps, and the tool for using them is called snapd [1].
|
||||||
Snap is available on many distros and is supported out-of-the-box by distros
|
Snap is available on many distros and is supported out-of-the-box by distros
|
||||||
such as Ubuntu, Kubuntu, Xubuntu, Manjoro, Zorin OS, KDE Neon and Solus among
|
such as Ubuntu, Kubuntu, Xubuntu, Manjaro, Zorin OS, KDE Neon and Solus among
|
||||||
others [1].
|
others [1].
|
||||||
|
|
||||||
[1]: https://web.archive.org/web/20221029152606/https://en.wikipedia.org/wiki/Snap_%28software%29 "Snap | Wikipedia | en.wikipedia.org"
|
[1]: https://web.archive.org/web/20221029152606/https://en.wikipedia.org/wiki/Snap_%28software%29 "Snap | Wikipedia | en.wikipedia.org"
|
||||||
@@ -1154,7 +1154,7 @@ actions:
|
|||||||
[1]: https://web.archive.org/web/20221029153354/https://en.wikipedia.org/wiki/Flatpak "Flatpak - Wikipedia | en.wikipedia.org"
|
[1]: https://web.archive.org/web/20221029153354/https://en.wikipedia.org/wiki/Flatpak "Flatpak - Wikipedia | en.wikipedia.org"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Uninstall orphaned Flatpak runtimes
|
name: Remove orphaned Flatpak runtimes
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
This script removes runtimes and extensions that are not used by installed
|
This script removes runtimes and extensions that are not used by installed
|
||||||
@@ -1199,8 +1199,8 @@ actions:
|
|||||||
-
|
-
|
||||||
category: Clear APT data
|
category: Clear APT data
|
||||||
docs: |-
|
docs: |-
|
||||||
Advanced Package Tool (or APT) is a free software user interface that works with
|
Advanced Package Tool (APT) is a free software user interface that works with
|
||||||
core libraries to handle the installation and removal of software on Debiana, and
|
core libraries to handle the installation and removal of software on Debian, and
|
||||||
Debian-based Linux distributions [1].
|
Debian-based Linux distributions [1].
|
||||||
|
|
||||||
See also: ["APT | Debian Wiki"](https://web.archive.org/web/20221029221422/https://wiki.debian.org/Apt).
|
See also: ["APT | Debian Wiki"](https://web.archive.org/web/20221029221422/https://wiki.debian.org/Apt).
|
||||||
@@ -1222,7 +1222,7 @@ actions:
|
|||||||
command: apt-get
|
command: apt-get
|
||||||
code: sudo apt-get autoclean
|
code: sudo apt-get autoclean
|
||||||
-
|
-
|
||||||
name: Clear APT package list
|
name: Clear APT package file lists
|
||||||
docs: |-
|
docs: |-
|
||||||
This script removes package file lists. APT stores a copy of those files (in `/var/lib/apt/lists/`)
|
This script removes package file lists. APT stores a copy of those files (in `/var/lib/apt/lists/`)
|
||||||
and searches are done within those local files to avoid going through the network for every search [1].
|
and searches are done within those local files to avoid going through the network for every search [1].
|
||||||
@@ -1250,7 +1250,7 @@ actions:
|
|||||||
command: apt-get
|
command: apt-get
|
||||||
code: sudo apt-get -y autoremove --purge
|
code: sudo apt-get -y autoremove --purge
|
||||||
-
|
-
|
||||||
name: Clear APT package (.deb file) cache
|
name: Clear cache for APT packages
|
||||||
docs: |-
|
docs: |-
|
||||||
This script runs the `clean` command. It clears out the local repository of retrieved package files [1],
|
This script runs the `clean` command. It clears out the local repository of retrieved package files [1],
|
||||||
meaning that it deletes downloaded packages (`.deb`) already installed (and no longer needed) [2].
|
meaning that it deletes downloaded packages (`.deb`) already installed (and no longer needed) [2].
|
||||||
@@ -1266,7 +1266,7 @@ actions:
|
|||||||
command: apt-get
|
command: apt-get
|
||||||
code: sudo apt-get clean
|
code: sudo apt-get clean
|
||||||
-
|
-
|
||||||
category: Clear all cache
|
category: Clear shared caches
|
||||||
docs: |-
|
docs: |-
|
||||||
The scripts in this category bulk delete all cache entries for the given scope.
|
The scripts in this category bulk delete all cache entries for the given scope.
|
||||||
These scripts are designed to affect more than a single application and do a wide range of cleaning.
|
These scripts are designed to affect more than a single application and do a wide range of cleaning.
|
||||||
@@ -1303,13 +1303,13 @@ actions:
|
|||||||
such as [2]:
|
such as [2]:
|
||||||
- Locally-generated fonts (`/var/cache/fonts`)
|
- Locally-generated fonts (`/var/cache/fonts`)
|
||||||
- Locally-formatted manual pages (`/var/cache/man`)
|
- Locally-formatted manual pages (`/var/cache/man`)
|
||||||
- WWW proxy or cache data (`/var/cache/www`)
|
- WWW proxy or cache data (`/var/cache/www`)
|
||||||
|
|
||||||
[1]: https://web.archive.org/web/20130511220135/http://www.lindevdoc.org/wiki//var/cache "/var/cache - Linux Developer's Documentation"
|
[1]: https://web.archive.org/web/20130511220135/http://www.lindevdoc.org/wiki//var/cache "/var/cache - Linux Developer's Documentation"
|
||||||
[2]: https://web.archive.org/web/20221029154623/https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch05s05.html "5.5. /var/cache : Application cache data | refspecs.linuxfoundation.org"
|
[2]: https://web.archive.org/web/20221029154623/https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch05s05.html "5.5. /var/cache : Application cache data | refspecs.linuxfoundation.org"
|
||||||
code: rm -rf /var/cache/*
|
code: rm -rf /var/cache/*
|
||||||
-
|
-
|
||||||
name: Clear cache from Flatpak applications
|
name: Clear Flatpak application cache
|
||||||
docs: |-
|
docs: |-
|
||||||
Non-essential user-specific data is stored in `~/.var/app/<app-id>/cache` by Flatpak applications [1].
|
Non-essential user-specific data is stored in `~/.var/app/<app-id>/cache` by Flatpak applications [1].
|
||||||
This directory points to `XDG_CACHE_HOME` [1] from the XDG base directory specification [2].
|
This directory points to `XDG_CACHE_HOME` [1] from the XDG base directory specification [2].
|
||||||
@@ -1318,7 +1318,7 @@ actions:
|
|||||||
[2]: https://web.archive.org/web/20221029151712/https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html "XDG Base Directory Specification | specifications.freedesktop.org"
|
[2]: https://web.archive.org/web/20221029151712/https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html "XDG Base Directory Specification | specifications.freedesktop.org"
|
||||||
code: rm -rfv ~/.var/app/*/cache/*
|
code: rm -rfv ~/.var/app/*/cache/*
|
||||||
-
|
-
|
||||||
name: Clear cache from Snap applications
|
name: Clear Snap application cache
|
||||||
docs: |-
|
docs: |-
|
||||||
Application caches are isolated per application and stored in the `~/snaps/APP-NAME/VERSION/.cache`
|
Application caches are isolated per application and stored in the `~/snaps/APP-NAME/VERSION/.cache`
|
||||||
folders for Snap applications [1].
|
folders for Snap applications [1].
|
||||||
@@ -1408,7 +1408,7 @@ actions:
|
|||||||
[4]: https://web.archive.org/web/20221030133248/https://www.debian.org/legal/privacy "Debian -- Privacy Policy | www.debian.org"
|
[4]: https://web.archive.org/web/20221030133248/https://www.debian.org/legal/privacy "Debian -- Privacy Policy | www.debian.org"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
category: Disable sending package statistics (popcon)
|
category: Disable sending of package statistics (popcon)
|
||||||
docs: |-
|
docs: |-
|
||||||
Popularity Contest (or popcon) collects statistical data to determine which packages are the most popular [1].
|
Popularity Contest (or popcon) collects statistical data to determine which packages are the most popular [1].
|
||||||
It sends a list of packages installed and the access and change times of relevant files to the server via email
|
It sends a list of packages installed and the access and change times of relevant files to the server via email
|
||||||
@@ -1433,11 +1433,11 @@ actions:
|
|||||||
[4]: https://web.archive.org/web/20221029160841/https://popcon.debian.org/FAQ "FAQ | popcon | popcon.debian.org"
|
[4]: https://web.archive.org/web/20221029160841/https://popcon.debian.org/FAQ "FAQ | popcon | popcon.debian.org"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Opt-out of the popularity contest
|
name: Disable participation in Popularity Contest
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
`popularity-contest` checks `/etc/popularity-contest.conf` for the value of `PARTICIPATE` to send data [1].
|
`popularity-contest` checks `/etc/popularity-contest.conf` for the value of `PARTICIPATE` to send data [1].
|
||||||
Configuring this configuration allows you to opt-out of the submission of data [1].
|
Configuring this configuration allows you to opt out of the submission of data [1].
|
||||||
|
|
||||||
[1]: https://web.archive.org/web/20221029161047/https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=172824 "#172824 - popularity-contest: dpkg-reconfigure popularity-contest does not include PARTICIPATE=yes - Debian Bug report logs | bugs.debian.org"
|
[1]: https://web.archive.org/web/20221029161047/https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=172824 "#172824 - popularity-contest: dpkg-reconfigure popularity-contest does not include PARTICIPATE=yes - Debian Bug report logs | bugs.debian.org"
|
||||||
code: |-
|
code: |-
|
||||||
@@ -1455,7 +1455,7 @@ actions:
|
|||||||
echo "Skipping because configuration file ($config_file) is not found. Is popcon installed?"
|
echo "Skipping because configuration file ($config_file) is not found. Is popcon installed?"
|
||||||
fi
|
fi
|
||||||
-
|
-
|
||||||
name: Uninstall Popularity Contest (popcon)
|
name: Remove Popularity Contest (`popcon`) package
|
||||||
recommend: strict
|
recommend: strict
|
||||||
docs: |-
|
docs: |-
|
||||||
This script deletes the `popularity-contest` package.
|
This script deletes the `popularity-contest` package.
|
||||||
@@ -1501,7 +1501,7 @@ actions:
|
|||||||
[2]: https://web.archive.org/web/20221029161559/https://manpages.ubuntu.com/manpages/trusty/man1/reportbug.1.html "Ubuntu Manpage: reportbug - reports a bug to a debbugs server | manpages.ubuntu.com"
|
[2]: https://web.archive.org/web/20221029161559/https://manpages.ubuntu.com/manpages/trusty/man1/reportbug.1.html "Ubuntu Manpage: reportbug - reports a bug to a debbugs server | manpages.ubuntu.com"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Uninstall `reportbug` package
|
name: Remove `reportbug` package
|
||||||
recommend: strict
|
recommend: strict
|
||||||
docs: |-
|
docs: |-
|
||||||
This script uninstalls `reportbug` package, which includes the tool itself.
|
This script uninstalls `reportbug` package, which includes the tool itself.
|
||||||
@@ -1513,7 +1513,7 @@ actions:
|
|||||||
parameters:
|
parameters:
|
||||||
packageName: reportbug
|
packageName: reportbug
|
||||||
-
|
-
|
||||||
name: Uninstall Python modules for reportbug
|
name: Remove Python modules for `reportbug`
|
||||||
recommend: strict
|
recommend: strict
|
||||||
docs: |-
|
docs: |-
|
||||||
This script uninstalls `python3-reportbug`.
|
This script uninstalls `python3-reportbug`.
|
||||||
@@ -1526,7 +1526,7 @@ actions:
|
|||||||
parameters:
|
parameters:
|
||||||
packageName: python3-reportbug
|
packageName: python3-reportbug
|
||||||
-
|
-
|
||||||
name: Uninstall UI for reportbug (`reportbug-gtk` package)
|
name: Remove UI for reportbug (`reportbug-gtk` package)
|
||||||
recommend: strict
|
recommend: strict
|
||||||
docs: |-
|
docs: |-
|
||||||
This script uninstalls `reportbug-gtk`. It consists of a desktop file and an icon, and it has dependencies to enable the
|
This script uninstalls `reportbug-gtk`. It consists of a desktop file and an icon, and it has dependencies to enable the
|
||||||
@@ -1559,7 +1559,7 @@ actions:
|
|||||||
[2]: https://web.archive.org/web/20221029161821/https://pkgstats.archlinux.de/privacy-policy "Privacy policy | pkgstats | pkgstats.archlinux.de"
|
[2]: https://web.archive.org/web/20221029161821/https://pkgstats.archlinux.de/privacy-policy "Privacy policy | pkgstats | pkgstats.archlinux.de"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Uninstall `pkgstats` package
|
name: Remove `pkgstats` package
|
||||||
recommend: strict
|
recommend: strict
|
||||||
docs: |-
|
docs: |-
|
||||||
`pkgstats` package submits a list of installed packages to the Arch Linux project [1].
|
`pkgstats` package submits a list of installed packages to the Arch Linux project [1].
|
||||||
@@ -1573,7 +1573,7 @@ actions:
|
|||||||
parameters:
|
parameters:
|
||||||
packageName: pkgstats
|
packageName: pkgstats
|
||||||
-
|
-
|
||||||
name: Disable weekly pkgstats submission
|
name: Disable weekly `pkgstats` submission
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
`pkgstats` is set up to automatically run every week using systemd/timers [1]. Once disabled,
|
`pkgstats` is set up to automatically run every week using systemd/timers [1]. Once disabled,
|
||||||
@@ -1609,7 +1609,7 @@ actions:
|
|||||||
parameters:
|
parameters:
|
||||||
packageName: zorin-os-census
|
packageName: zorin-os-census
|
||||||
-
|
-
|
||||||
name: Remove the Zorin OS census unique ID
|
name: Remove Zorin OS census unique ID
|
||||||
docs: |-
|
docs: |-
|
||||||
Census submits a unique ID associated with the installation when pings are sent [1].
|
Census submits a unique ID associated with the installation when pings are sent [1].
|
||||||
Removing the identifier reduces the risk of your computer being identified.
|
Removing the identifier reduces the risk of your computer being identified.
|
||||||
@@ -1634,7 +1634,7 @@ actions:
|
|||||||
[2]: https://web.archive.org/web/20221029160241/https://en.wikipedia.org/wiki/List_of_Linux_distributions#Ubuntu-based "List of Linux distributions - Wikipedia | en.wikipedia.org"
|
[2]: https://web.archive.org/web/20221029160241/https://en.wikipedia.org/wiki/List_of_Linux_distributions#Ubuntu-based "List of Linux distributions - Wikipedia | en.wikipedia.org"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Disable online search results (collects queries)
|
name: Disable online search result collection (collects queries)
|
||||||
recommend: strict
|
recommend: strict
|
||||||
docs: |-
|
docs: |-
|
||||||
Online search is introduced in Ubuntu Lens in Ubuntu 12.10 [1]. It has been known as "Shopping Lens",
|
Online search is introduced in Ubuntu Lens in Ubuntu 12.10 [1]. It has been known as "Shopping Lens",
|
||||||
@@ -1652,7 +1652,7 @@ actions:
|
|||||||
code: gsettings set com.canonical.Unity.Lenses remote-content-search none
|
code: gsettings set com.canonical.Unity.Lenses remote-content-search none
|
||||||
revertCode: gsettings set com.canonical.Unity.Lenses remote-content-search all
|
revertCode: gsettings set com.canonical.Unity.Lenses remote-content-search all
|
||||||
-
|
-
|
||||||
category: Disable Ubuntu reporting metrics
|
category: Disable metrics reporting in Ubuntu
|
||||||
docs: |-
|
docs: |-
|
||||||
`ubuntu-report` reports hardware and other collected metrics like installer or upgrade information [1].
|
`ubuntu-report` reports hardware and other collected metrics like installer or upgrade information [1].
|
||||||
|
|
||||||
@@ -1682,7 +1682,7 @@ actions:
|
|||||||
[2]: https://web.archive.org/web/20221029162538/https://github.com/ubuntu/ubuntu-report/blob/8e6030ff9bbeacacf41a9b58ea638a5c9a6f864d/README.md "More diagnostics data from desktop | lists.ubuntu.com"
|
[2]: https://web.archive.org/web/20221029162538/https://github.com/ubuntu/ubuntu-report/blob/8e6030ff9bbeacacf41a9b58ea638a5c9a6f864d/README.md "More diagnostics data from desktop | lists.ubuntu.com"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Opt-out of Ubuntu reporting metrics
|
name: Disable participation in metrics reporting in Ubuntu
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
This script uses the `ubuntu-report` CLI to opt you out of data submission [1].
|
This script uses the `ubuntu-report` CLI to opt you out of data submission [1].
|
||||||
@@ -1708,7 +1708,7 @@ actions:
|
|||||||
>&2 echo 'Failed to opt in.'
|
>&2 echo 'Failed to opt in.'
|
||||||
fi
|
fi
|
||||||
-
|
-
|
||||||
name: Uninstall Ubuntu Report tool (`ubuntu-report`)
|
name: Remove Ubuntu Report tool (`ubuntu-report`)
|
||||||
recommend: strict
|
recommend: strict
|
||||||
docs: |-
|
docs: |-
|
||||||
`ubuntu-report` is installed as an apt package and can be uninstalled in this way [1].
|
`ubuntu-report` is installed as an apt package and can be uninstalled in this way [1].
|
||||||
@@ -1762,7 +1762,7 @@ actions:
|
|||||||
[4]: https://web.archive.org/web/20221029150025/https://support.starlabs.systems/kb/guides/disable-program-problem-reports "Disable program problem reports - Star Labs | support.starlabs.systems"
|
[4]: https://web.archive.org/web/20221029150025/https://support.starlabs.systems/kb/guides/disable-program-problem-reports "Disable program problem reports - Star Labs | support.starlabs.systems"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Uninstall `apport` package
|
name: Remove `apport` package
|
||||||
recommend: strict
|
recommend: strict
|
||||||
docs: |-
|
docs: |-
|
||||||
The `apport` package is responsible for automatically generating crash reports for debugging [1].
|
The `apport` package is responsible for automatically generating crash reports for debugging [1].
|
||||||
@@ -1804,7 +1804,7 @@ actions:
|
|||||||
parameters:
|
parameters:
|
||||||
serviceName: apport
|
serviceName: apport
|
||||||
-
|
-
|
||||||
name: Opt-out of Apport error messaging system
|
name: Disable participation in Apport error messaging system
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
Opting out of Apport prevents it from creating crash files after a crash [1].
|
Opting out of Apport prevents it from creating crash files after a crash [1].
|
||||||
@@ -1853,7 +1853,7 @@ actions:
|
|||||||
[3]: https://web.archive.org/web/20221029162829/https://help.ubuntu.com/community/ReportingBugs#A4._Collect_information_about_the_bug "ReportingBugs - Community Help Wiki | help.ubuntu.com"
|
[3]: https://web.archive.org/web/20221029162829/https://help.ubuntu.com/community/ReportingBugs#A4._Collect_information_about_the_bug "ReportingBugs - Community Help Wiki | help.ubuntu.com"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Uninstall `whoopsie` package
|
name: Remove `whoopsie` package
|
||||||
docs: |-
|
docs: |-
|
||||||
This script uninstalls the `whoopsie` package.
|
This script uninstalls the `whoopsie` package.
|
||||||
This package is used for error tracker submission in Ubuntu [1].
|
This package is used for error tracker submission in Ubuntu [1].
|
||||||
@@ -1879,7 +1879,7 @@ actions:
|
|||||||
parameters:
|
parameters:
|
||||||
serviceName: whoopsie
|
serviceName: whoopsie
|
||||||
-
|
-
|
||||||
name: Opt-out of reporting crashes
|
name: Disable crash report submissions
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
Once opted-out Whoopsie disables the related service [1].
|
Once opted-out Whoopsie disables the related service [1].
|
||||||
@@ -1897,7 +1897,7 @@ actions:
|
|||||||
sudo sed -i 's/report_crashes=false/report_crashes=true/' /etc/default/whoopsie
|
sudo sed -i 's/report_crashes=false/report_crashes=true/' /etc/default/whoopsie
|
||||||
fi
|
fi
|
||||||
-
|
-
|
||||||
category: Disable Zeitgeist (activity logging framework)
|
category: Disable Zeitgeist activity logging
|
||||||
docs: |-
|
docs: |-
|
||||||
Zeitgeist logs files opened, websites visited, conversations, and emails and provides
|
Zeitgeist logs files opened, websites visited, conversations, and emails and provides
|
||||||
this information over an API to applications [1].
|
this information over an API to applications [1].
|
||||||
@@ -1931,7 +1931,7 @@ actions:
|
|||||||
[7]: https://web.archive.org/web/20221029164405/https://launchpad.net/synapse-project "Synapse in Launchpad | launchpad.net"
|
[7]: https://web.archive.org/web/20221029164405/https://launchpad.net/synapse-project "Synapse in Launchpad | launchpad.net"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Stop the Zeitgeist deamon
|
name: Kill Zeitgeist daemon process
|
||||||
docs: |-
|
docs: |-
|
||||||
`zeitgeist-daemon` is a daemon providing an activity log [1]. Activity logs consist of a central
|
`zeitgeist-daemon` is a daemon providing an activity log [1]. Activity logs consist of a central
|
||||||
database for events such as file usage, browser history, and calendar entries [1].
|
database for events such as file usage, browser history, and calendar entries [1].
|
||||||
@@ -1957,7 +1957,7 @@ actions:
|
|||||||
# > used, zeitgeist-daemon will refuse to start if another running instance is found.
|
# > used, zeitgeist-daemon will refuse to start if another running instance is found.
|
||||||
revertCode: zeitgeist-daemon --replace
|
revertCode: zeitgeist-daemon --replace
|
||||||
-
|
-
|
||||||
name: Prevent Zeitgeist from running on startup
|
name: Remove Zeitgeist startup entry
|
||||||
docs: |-
|
docs: |-
|
||||||
This script removes the Autostart entry that is used by the Zeitgeist package to start itself [1].
|
This script removes the Autostart entry that is used by the Zeitgeist package to start itself [1].
|
||||||
[The XDG Autostart specification](https://web.archive.org/web/20221029223114/https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html)
|
[The XDG Autostart specification](https://web.archive.org/web/20221029223114/https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html)
|
||||||
@@ -2001,7 +2001,7 @@ actions:
|
|||||||
>&2 echo "Failed to restore access, file does not exist at $file."
|
>&2 echo "Failed to restore access, file does not exist at $file."
|
||||||
fi
|
fi
|
||||||
-
|
-
|
||||||
name: Uninstall the Zeitgeist package (can break integrated software)
|
name: Remove Zeitgeist package (can break integrated software)
|
||||||
docs: |-
|
docs: |-
|
||||||
The main functionality for Zeitgeist is provided by the `zeitgeist` package in Fedora [1] and Arch Linux [2],
|
The main functionality for Zeitgeist is provided by the `zeitgeist` package in Fedora [1] and Arch Linux [2],
|
||||||
and the `zeitgeist-core` package in Debian [3].
|
and the `zeitgeist-core` package in Debian [3].
|
||||||
@@ -2026,7 +2026,7 @@ actions:
|
|||||||
parameters:
|
parameters:
|
||||||
packageName: zeitgeist-core
|
packageName: zeitgeist-core
|
||||||
# Other related packages include (apt list *zeitgeist*):
|
# Other related packages include (apt list *zeitgeist*):
|
||||||
# zeitgeist-core, zeitgeist-datahub, python3-zeitgeist among many others.
|
# zeitgeist-core, zeitgeist-datahub, python3-zeitgeist among many others.
|
||||||
# Packages that use it include e.g. activity-log-manager, activity-log-manager-control-center..
|
# Packages that use it include e.g. activity-log-manager, activity-log-manager-control-center..
|
||||||
-
|
-
|
||||||
function: UninstallPacmanPackage
|
function: UninstallPacmanPackage
|
||||||
@@ -2073,11 +2073,11 @@ actions:
|
|||||||
echo "Skipping, connectivity checks are already disabled through $file."
|
echo "Skipping, connectivity checks are already disabled through $file."
|
||||||
else
|
else
|
||||||
echo -n "$content" | sudo tee "$file" > /dev/null
|
echo -n "$content" | sudo tee "$file" > /dev/null
|
||||||
echo 'Successfully disabled connectivity checks-'
|
echo 'Successfully disabled connectivity checks.'
|
||||||
fi
|
fi
|
||||||
if command -v 'nmcli' &> /dev/null; then
|
if command -v 'nmcli' &> /dev/null; then
|
||||||
sudo nmcli general reload
|
sudo nmcli general reload
|
||||||
echo 'Successfuly reloaded configuration.'
|
echo 'Successfully reloaded configuration.'
|
||||||
else
|
else
|
||||||
echo 'It will take effect after reboot.'
|
echo 'It will take effect after reboot.'
|
||||||
fi
|
fi
|
||||||
@@ -2086,11 +2086,11 @@ actions:
|
|||||||
echo 'Skipping, connectivity checks are not disabled.'
|
echo 'Skipping, connectivity checks are not disabled.'
|
||||||
else
|
else
|
||||||
sudo rm -fv "$file"
|
sudo rm -fv "$file"
|
||||||
echo 'Successfuly restored connectivity checks.'
|
echo 'Successfully restored connectivity checks.'
|
||||||
fi
|
fi
|
||||||
if command -v 'nmcli' &> /dev/null; then
|
if command -v 'nmcli' &> /dev/null; then
|
||||||
sudo nmcli general reload
|
sudo nmcli general reload
|
||||||
echo 'Successfuly reloaded configuration.'
|
echo 'Successfully reloaded configuration.'
|
||||||
else
|
else
|
||||||
echo 'It will take effect after reboot.'
|
echo 'It will take effect after reboot.'
|
||||||
fi
|
fi
|
||||||
@@ -2197,7 +2197,7 @@ actions:
|
|||||||
jsonValue: >-
|
jsonValue: >-
|
||||||
false
|
false
|
||||||
-
|
-
|
||||||
name: Do not run Microsoft online experiments on Visual Studio Code
|
name: Disable online experiments by Microsoft in Visual Studio Code
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
VS Code employs experiments to test new features or gradually roll them out [1].
|
VS Code employs experiments to test new features or gradually roll them out [1].
|
||||||
@@ -2217,7 +2217,7 @@ actions:
|
|||||||
jsonValue: >-
|
jsonValue: >-
|
||||||
false
|
false
|
||||||
-
|
-
|
||||||
name: Choose manual Visual Studio Code updates over automatic updates
|
name: Disable Visual Studio Code automatic updates in favor of manual updates
|
||||||
docs: |-
|
docs: |-
|
||||||
By default, VS Code is configured to automatically update when new versions are released [1].
|
By default, VS Code is configured to automatically update when new versions are released [1].
|
||||||
Automatic updates reduce your control over privacy by sending data to Microsoft servers.
|
Automatic updates reduce your control over privacy by sending data to Microsoft servers.
|
||||||
@@ -2245,7 +2245,7 @@ actions:
|
|||||||
jsonValue: >-
|
jsonValue: >-
|
||||||
"none"
|
"none"
|
||||||
-
|
-
|
||||||
name: Prevent fetching Visual Studio Code release notes from Microsoft servers
|
name: Disable fetching release notes from Microsoft servers after an update
|
||||||
docs: |-
|
docs: |-
|
||||||
This script prevents Visual Studio Code from displaying release notes after an update [1].
|
This script prevents Visual Studio Code from displaying release notes after an update [1].
|
||||||
|
|
||||||
@@ -2262,7 +2262,7 @@ actions:
|
|||||||
jsonValue: >-
|
jsonValue: >-
|
||||||
false
|
false
|
||||||
-
|
-
|
||||||
category: Configure auto-updates from Visual Studio Code extensions
|
category: Configure auto-update settings for Visual Studio Code extensions
|
||||||
docs: |-
|
docs: |-
|
||||||
These scripts control the automatic update behavior of extensions.
|
These scripts control the automatic update behavior of extensions.
|
||||||
Updates are fetched from Microsoft servers [1] [2].
|
Updates are fetched from Microsoft servers [1] [2].
|
||||||
@@ -2273,7 +2273,7 @@ actions:
|
|||||||
[2]: https://web.archive.org/web/20221029171719/https://github.com/microsoft/vscode-docs/blob/9a900b380e11530376104ffc83a004b82553728e/docs/editor/extension-marketplace.md#extension-auto-update "vscode-docs/extension-marketplace.md at 9a900b380e11530376104ffc83a004b82553728e · microsoft/vscode-docs · GitHub | github.com"
|
[2]: https://web.archive.org/web/20221029171719/https://github.com/microsoft/vscode-docs/blob/9a900b380e11530376104ffc83a004b82553728e/docs/editor/extension-marketplace.md#extension-auto-update "vscode-docs/extension-marketplace.md at 9a900b380e11530376104ffc83a004b82553728e · microsoft/vscode-docs · GitHub | github.com"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Prevent auto-updates of Visual Studio Code extensions
|
name: Disable automatic Visual Studio Code extension updates
|
||||||
docs: |-
|
docs: |-
|
||||||
By default, Visual Studio Code automatically downloads and installs updates for all
|
By default, Visual Studio Code automatically downloads and installs updates for all
|
||||||
extensions [1]. These updates are fetched from a Microsoft online service [1].
|
extensions [1]. These updates are fetched from a Microsoft online service [1].
|
||||||
@@ -2290,7 +2290,7 @@ actions:
|
|||||||
jsonValue: >-
|
jsonValue: >-
|
||||||
false
|
false
|
||||||
-
|
-
|
||||||
name: Prevent automatically checking Visual Studio Code extension updates from Microsoft servers
|
name: Disable Visual Studio Code automatic extension update checks
|
||||||
docs: |-
|
docs: |-
|
||||||
By default, Visual Studio Code automatically checks extensions for updates [1].
|
By default, Visual Studio Code automatically checks extensions for updates [1].
|
||||||
If an update for an extension is available, it is marked as outdated in the Extensions view [1].
|
If an update for an extension is available, it is marked as outdated in the Extensions view [1].
|
||||||
@@ -2308,7 +2308,7 @@ actions:
|
|||||||
jsonValue: >-
|
jsonValue: >-
|
||||||
false
|
false
|
||||||
-
|
-
|
||||||
name: Disable auto-fetching Microsoft recommendations in Visual Studio Code
|
name: Disable automatic fetching of Microsoft recommendations in Visual Studio Code
|
||||||
recommend: strict
|
recommend: strict
|
||||||
docs: |-
|
docs: |-
|
||||||
Visual Studio Code, by default, fetches recommendations from online Microsoft servers [1].
|
Visual Studio Code, by default, fetches recommendations from online Microsoft servers [1].
|
||||||
@@ -2328,7 +2328,7 @@ actions:
|
|||||||
jsonValue: >-
|
jsonValue: >-
|
||||||
true
|
true
|
||||||
-
|
-
|
||||||
name: Disable automatic fetching remote repository in Visual Studio Code
|
name: Disable automatic fetching of remote repositories in Visual Studio Code
|
||||||
docs: |-
|
docs: |-
|
||||||
Visual Studio Code (VS Code) periodically fetches changes from remote repositories [1].
|
Visual Studio Code (VS Code) periodically fetches changes from remote repositories [1].
|
||||||
This feature lets VS Code display how many changes your local repository is ahead or behind the remote [1].
|
This feature lets VS Code display how many changes your local repository is ahead or behind the remote [1].
|
||||||
@@ -2353,7 +2353,7 @@ actions:
|
|||||||
jsonValue: >-
|
jsonValue: >-
|
||||||
false
|
false
|
||||||
-
|
-
|
||||||
name: Prevent fetching package information from NPM and Bower in Visual Studio Code
|
name: Disable fetching package information from NPM and Bower in Visual Studio Code
|
||||||
docs: |-
|
docs: |-
|
||||||
Visual Studio Code fetches data from NPM and Bower to provide autocompletion and hover information
|
Visual Studio Code fetches data from NPM and Bower to provide autocompletion and hover information
|
||||||
for npm dependencies [1]. By default, this feature is enabled [2] and fetches data from
|
for npm dependencies [1]. By default, this feature is enabled [2] and fetches data from
|
||||||
@@ -2445,7 +2445,7 @@ actions:
|
|||||||
jsonValue: >-
|
jsonValue: >-
|
||||||
false
|
false
|
||||||
-
|
-
|
||||||
category: Disable Visual Studio Code Settings Sync data sharing
|
category: Disable data sharing for Visual Studio Code "Settings Sync"
|
||||||
docs: |-
|
docs: |-
|
||||||
**Settings Sync** is a Visual Studio Code feature that synchronizes settings, keybindings,
|
**Settings Sync** is a Visual Studio Code feature that synchronizes settings, keybindings,
|
||||||
and installed extensions across different machines [1]. This data is stored in the Microsoft
|
and installed extensions across different machines [1]. This data is stored in the Microsoft
|
||||||
@@ -2454,7 +2454,7 @@ actions:
|
|||||||
[1]: https://web.archive.org/web/20221029172856/https://github.com/microsoft/vscode-docs/blob/9a900b380e11530376104ffc83a004b82553728e/docs/editor/settings-sync.md "vscode-docs/settings-sync.md at main · microsoft/vscode-docs · GitHub | github.com"
|
[1]: https://web.archive.org/web/20221029172856/https://github.com/microsoft/vscode-docs/blob/9a900b380e11530376104ffc83a004b82553728e/docs/editor/settings-sync.md "vscode-docs/settings-sync.md at main · microsoft/vscode-docs · GitHub | github.com"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Disable synchronizing Visaul Studio Code keybindings
|
name: Disable synchronization of Visual Studio Code keybindings
|
||||||
docs: |-
|
docs: |-
|
||||||
By default [1], Keyboard Shortcuts are synchronized across platforms [2].
|
By default [1], Keyboard Shortcuts are synchronized across platforms [2].
|
||||||
You can disable this by setting `settingsSync.keybindingsPerPlatform` to `false` [1] [2].
|
You can disable this by setting `settingsSync.keybindingsPerPlatform` to `false` [1] [2].
|
||||||
@@ -2468,7 +2468,7 @@ actions:
|
|||||||
jsonValue: >-
|
jsonValue: >-
|
||||||
false
|
false
|
||||||
-
|
-
|
||||||
name: Disable synchronizing Visual Studio Code extension
|
name: Disable synchronization of Visual Studio Code extensions
|
||||||
docs: |-
|
docs: |-
|
||||||
Visual Studio Code synchronizes all built-in and installed extensions, along with their
|
Visual Studio Code synchronizes all built-in and installed extensions, along with their
|
||||||
global enablement state, by default [1] [2].
|
global enablement state, by default [1] [2].
|
||||||
@@ -2487,8 +2487,8 @@ actions:
|
|||||||
jsonValue: >-
|
jsonValue: >-
|
||||||
["*"]
|
["*"]
|
||||||
-
|
-
|
||||||
name: Disable synchronizing Visual Studio Code settings
|
name: Disable synchronization of Visual Studio Code settings
|
||||||
docs: |-
|
docs: |-
|
||||||
By default, Visual Studio Code synchronizes all settings, except for machine settings
|
By default, Visual Studio Code synchronizes all settings, except for machine settings
|
||||||
(those with machine or machine-overridable scopes), as these are specific to a given machine [1] [2].
|
(those with machine or machine-overridable scopes), as these are specific to a given machine [1] [2].
|
||||||
|
|
||||||
@@ -2573,22 +2573,131 @@ actions:
|
|||||||
See also: [What is browser fingerprinting? | AmIUnique.org](https://web.archive.org/web/20221029223510/https://www.amiunique.org/faq)
|
See also: [What is browser fingerprinting? | AmIUnique.org](https://web.archive.org/web/20221029223510/https://www.amiunique.org/faq)
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Enable Firefox First party isolation
|
category: Enable Firefox state partitioning (Total Cookie Protection)
|
||||||
recommend: strict
|
|
||||||
docs: |-
|
docs: |-
|
||||||
First-party isolation (also known as "double keying") can prevent third parties from tracking
|
Web browsers, including Firefox, save various data types such as cookies, cache, and site-specific details.
|
||||||
users across multiple sites [1].
|
While this data helps in providing a faster and personalized browsing experience, it can be exploited by websites to track
|
||||||
|
your activities across the internet, potentially compromising your privacy.
|
||||||
|
|
||||||
This script configures `privacy.firstparty.isolate` to be enabled, preventing third parties
|
State partitioning, also known as "Total Cookie Protection" [1], is a feature designed to enhance user privacy in Firefox.
|
||||||
from tracking users across websites, also known as supercookies [2].
|
It works by allocating different, isolated storage spaces for every website you visit [2]. This means that each website has its own
|
||||||
|
"compartment" where it saves its data, separate from other sites [2]. This structure limits websites' capabilities to track users
|
||||||
|
across various domains.
|
||||||
|
|
||||||
[1]: https://web.archive.org/web/20221025162743/https://wiki.archlinux.org/title/Firefox/Privacy#First_party_isolation "Firefox/Privacy - ArchWiki | wiki.archlinux.org"
|
The underlying technology for state partitioning in Firefox is termed "double-keying" [1]. In this method, when a website intends
|
||||||
[2]: https://web.archive.org/web/20221025200527/https://bugzilla.mozilla.org/show_bug.cgi?id=1397624#c0
|
to store data, Firefox attaches an extra identifier tied to the site's origin, ensuring unique data storage for each site [1]. For
|
||||||
call:
|
example, if two different sites incorporate content from the same third-party source, each of these sites will have its own unique
|
||||||
function: AddFirefoxPrefs
|
version of the third-party's data (like cookies) due to state partitioning [1]. This impedes the third-party's tracking ability
|
||||||
parameters:
|
between the sites.
|
||||||
prefName: privacy.firstparty.isolate
|
|
||||||
jsonValue: 'true'
|
This protection isn't just against known trackers [1]. Firefox applies state partitioning to all third-party content on a site,
|
||||||
|
ensuring a comprehensive privacy coverage, beyond just identifiable tracking sources [1] [3].
|
||||||
|
|
||||||
|
A notable misuse by some trackers is the creation of "supercookies" [4]. Contrary to standard cookies, which users can delete easily,
|
||||||
|
supercookies are harder to eliminate and block, posing a considerable privacy challenge. Through state partitioning, Firefox renders
|
||||||
|
supercookies ineffective for tracking users across sites [4]. As a part of this feature, Firefox not only ensures site-specific data
|
||||||
|
but also partitions multiple caches, such as HTTP cache, image cache, and favicon cache [4]. This partitioning prevents any potential
|
||||||
|
cache exploitation for tracking purposes [4].
|
||||||
|
|
||||||
|
In summary, enabling state partitioning in Firefox is a powerful privacy tool, helping to defend users from potential online tracking
|
||||||
|
and offering a more private browsing experience.
|
||||||
|
|
||||||
|
[1]: https://web.archive.org/web/20230918171957/https://hacks.mozilla.org/2021/02/introducing-state-partitioning/ "Introducing State Partitioning - Mozilla Hacks - the Web developer blog"
|
||||||
|
[2]: https://web.archive.org/web/20230918172155/https://developer.mozilla.org/en-US/docs/Web/Privacy/State_Partitioning "State Partitioning - Privacy on the web | MDN"
|
||||||
|
[3]: https://web.archive.org/web/20230918172352/https://blog.mozilla.org/security/2021/02/23/total-cookie-protection/ "Firefox 86 Introduces Total Cookie Protection - Mozilla Security Blog"
|
||||||
|
[4]: https://web.archive.org/web/20230918172503/https://blog.mozilla.org/security/2021/01/26/supercookie-protections/ "Firefox 85 Cracks Down on Supercookies - Mozilla Security Blog"
|
||||||
|
children:
|
||||||
|
-
|
||||||
|
name: Enable dynamic First-Party Isolation (dFPI)
|
||||||
|
recommend: standard
|
||||||
|
docs: |-
|
||||||
|
Dynamic First-Party Isolation, also known as dFPI, is an advanced privacy feature in Firefox. This feature commonly
|
||||||
|
referred to as:
|
||||||
|
|
||||||
|
- Total Cookie Protection [1],
|
||||||
|
- dFPI (dynamic First-Party Isolation) [2],
|
||||||
|
- Dynamic storage partitioning [3].
|
||||||
|
|
||||||
|
Essentially, dFPI is an enhanced version of a previous privacy tool known as First-Party Isolation (FPI) [4].
|
||||||
|
|
||||||
|
The primary purpose of dFPI is to improve user privacy online. It accomplishes this by preventing third-party websites from
|
||||||
|
accessing or tracking a user's data across different websites [1] [3].
|
||||||
|
|
||||||
|
By default, this feature is activated for all Firefox desktop users [5].
|
||||||
|
|
||||||
|
Within Firefox's settings, there's an option called `network.cookie.cookieBehavior` which governs how dFPI operates.
|
||||||
|
This setting has three potential values [3]:
|
||||||
|
|
||||||
|
- `5`: The browser will block known trackers and partition storage for third-party content.
|
||||||
|
- `4`: Only known trackers will be blocked without any partitioning of third-party storage.
|
||||||
|
- `0`: All trackers and third-party content are allowed.
|
||||||
|
|
||||||
|
This script sets the value to `5`, ensuring the highest level of privacy by blocking trackers and partitioning third-party
|
||||||
|
storage. This aligns with recommended privacy practices because even if you choose the `4` value, the older First-Party
|
||||||
|
Isolation (FPI) will still be active [6].
|
||||||
|
|
||||||
|
[1]: https://web.archive.org/web/20231003094145/https://support.mozilla.org/en-US/kb/total-cookie-protection-and-website-breakage-faq "Total Cookie Protection and website breakage FAQ | Firefox Help"
|
||||||
|
[2]: https://web.archive.org/web/20231003094154/https://bugzilla.mozilla.org/show_bug.cgi?id=1746646 "1746646 - (tcp-mochitests) [meta] Make mochitests work with TCP enabled (cookieBehavior = 5) | bugzilla.mozilla.org"
|
||||||
|
[3]: https://web.archive.org/web/20230918172155/https://developer.mozilla.org/en-US/docs/Web/Privacy/State_Partitioning#disable_dynamic_state_partitioning "State Partitioning - Privacy on the web | MDN"
|
||||||
|
[4]: https://web.archive.org/web/20231003094207/https://bugzilla.mozilla.org/show_bug.cgi?id=1649876#c5 "1649876 - Migrate FPI users to dFPI | bugzilla.mozilla.org"
|
||||||
|
[5]: https://blog.mozilla.org/en/products/firefox/firefox-rolls-out-total-cookie-protection-by-default-to-all-users-worldwide/ "Firefox Rolls Out Total Cookie Protection By Default"
|
||||||
|
[6]: https://web.archive.org/web/20231003094350/https://bugzilla.mozilla.org/show_bug.cgi?id=1631676#c25 "1631676 - Disable dfpi when privacy.firstparty.isolate=true | bugzilla.mozilla.org"
|
||||||
|
call:
|
||||||
|
function: AddFirefoxPrefs
|
||||||
|
parameters:
|
||||||
|
prefName: network.cookie.cookieBehavior
|
||||||
|
jsonValue: '5'
|
||||||
|
-
|
||||||
|
name: Enable Firefox network partitioning
|
||||||
|
recommend: standard
|
||||||
|
docs: |-
|
||||||
|
Network partitioning is a method used by Firefox to enhance user privacy [1]. When enabled, each website you visit has its own
|
||||||
|
isolated storage location, preventing it from accessing data from another website [1]. This limits the ability of websites to track
|
||||||
|
users across multiple sites [1].
|
||||||
|
|
||||||
|
Network Partitioning, formerly referred to as *cache partitioning* [2], is a subset of state partitioning [1]. While state partitioning
|
||||||
|
deals with data like cookies, network partitioning deals with networking-related components, such as caches and connection pools [1].
|
||||||
|
It ensures that these components are isolated to each website, further enhancing user privacy [1].
|
||||||
|
|
||||||
|
Firefox has enabled network partitioning by default since version 85 [1]. Once enabled, network partitioning becomes permanent,
|
||||||
|
meaning websites cannot bypass or weaken its restrictions [1].
|
||||||
|
|
||||||
|
Network partitioning can be controlled with the `privacy.partition.network_state` preference [1].
|
||||||
|
|
||||||
|
[1]: https://web.archive.org/web/20230918172155/https://developer.mozilla.org/en-US/docs/Web/Privacy/State_Partitioning "State Partitioning - Privacy on the web | MDN"
|
||||||
|
[2]: https://web.archive.org/web/20231003094417/https://bugzilla.mozilla.org/show_bug.cgi?id=1687569 "1687569 - cache partitioning causes web page saving extensions to waste more time and data | bugzilla.mozilla.org"
|
||||||
|
call:
|
||||||
|
function: AddFirefoxPrefs
|
||||||
|
parameters:
|
||||||
|
prefName: privacy.partition.network_state
|
||||||
|
jsonValue: 'true'
|
||||||
|
-
|
||||||
|
name: Disable outdated Firefox First-Party Isolation (FPI)
|
||||||
|
recommend: strict
|
||||||
|
docs: |-
|
||||||
|
First-party isolation (FPI) helps in preventing third parties from tracking users across multiple websites [1] [2]. This is sometimes
|
||||||
|
referred to as "double keying" (double-keying) [1] or supercookies [1] [2].
|
||||||
|
|
||||||
|
This script disables FPI in favor of a newer technology called dynamic First-Party Isolation (dFPI) for the following reasons:
|
||||||
|
|
||||||
|
1. FPI and dFPI conflict with each other, and they cannot function simultaneously [3] [4]. Additionally, Mozilla doesn't plan to make
|
||||||
|
them compatible [5].
|
||||||
|
2. FPI has been marked as deprecated and is expected to be phased out [3], with dFPI set to become the standard in the future [6].
|
||||||
|
3. dFPI, when combined with network partitioning, offers broader and more effective privacy coverage than FPI, being essentially a
|
||||||
|
superset of FPI [3] [7].
|
||||||
|
|
||||||
|
[1]: https://web.archive.org/web/20221025162743/https://wiki.archlinux.org/title/Firefox/Privacy#First_party_isolation "Firefox/Privacy - ArchWiki | wiki.archlinux.org"
|
||||||
|
[2]: https://web.archive.org/web/20221025200527/https://bugzilla.mozilla.org/show_bug.cgi?id=1397624#c0 "1397624 - Provide an option for first-party isolation in Private Browsing Mode | bugzilla.mozilla.org"
|
||||||
|
[3]: https://web.archive.org/web/20231003094207/https://bugzilla.mozilla.org/show_bug.cgi?id=1649876#c0 "1649876 - Migrate FPI users to dFPI | bugzilla.mozilla.org"
|
||||||
|
[4]: https://web.archive.org/web/20231003094350/https://bugzilla.mozilla.org/show_bug.cgi?id=1631676#c25 "1631676 - Disable dfpi when privacy.firstparty.isolate=true | bugzilla.mozilla.org"
|
||||||
|
[5]: https://web.archive.org/web/20231003094207/https://bugzilla.mozilla.org/show_bug.cgi?id=1649876#c3 "1649876 - Migrate FPI users to dFPI | bugzilla.mozilla.org"
|
||||||
|
[6]: https://web.archive.org/web/20231003094207/https://bugzilla.mozilla.org/show_bug.cgi?id=1649876#c5 "1649876 - Migrate FPI users to dFPI | bugzilla.mozilla.org"
|
||||||
|
[7]: https://web.archive.org/web/20231003094358/https://bugzilla.mozilla.org/show_bug.cgi?id=1637344#c2 "1637344 - Add message to show dFPI is incompatible with FPI | bugzilla.mozilla.org"
|
||||||
|
call:
|
||||||
|
function: AddFirefoxPrefs
|
||||||
|
parameters:
|
||||||
|
prefName: privacy.firstparty.isolate
|
||||||
|
jsonValue: 'false'
|
||||||
-
|
-
|
||||||
name: Enable Firefox tracking protection
|
name: Enable Firefox tracking protection
|
||||||
recommend: standard
|
recommend: standard
|
||||||
@@ -2601,7 +2710,7 @@ actions:
|
|||||||
This script enables the `privacy.resistFingerprinting` preference,
|
This script enables the `privacy.resistFingerprinting` preference,
|
||||||
activating anti-fingerprinting [1][2].
|
activating anti-fingerprinting [1][2].
|
||||||
|
|
||||||
Beyond privacy advantages, enabling tracking protection may reduce load time by 44% [3].
|
Beyond privacy advantages; enabling tracking protection may reduce load time by 44% [3].
|
||||||
|
|
||||||
Note: If you are already using an ad blocker with the correct lists, tracking protection might
|
Note: If you are already using an ad blocker with the correct lists, tracking protection might
|
||||||
be redundant.
|
be redundant.
|
||||||
@@ -2618,9 +2727,9 @@ actions:
|
|||||||
name: Enable Firefox anti-fingerprinting (may break some websites)
|
name: Enable Firefox anti-fingerprinting (may break some websites)
|
||||||
recommend: strict
|
recommend: strict
|
||||||
docs: |-
|
docs: |-
|
||||||
Fingerprinting Protection is a heavily developing experimental feature in Firefox [1].
|
Fingerprinting Protection is a heavily developed experimental feature in Firefox [1].
|
||||||
As part of a project to integrate features from the Tor Browser, Mozilla has launched an
|
As part of a project to integrate features from the Tor Browser, Mozilla launched an
|
||||||
anti-fingerprinting project in Firefox [2].
|
anti-fingerprinting project in Firefox [2].
|
||||||
|
|
||||||
This script enables the `privacy.resistFingerprinting` preference, activating
|
This script enables the `privacy.resistFingerprinting` preference, activating
|
||||||
anti-fingerprinting [1][2].
|
anti-fingerprinting [1][2].
|
||||||
@@ -2647,7 +2756,7 @@ actions:
|
|||||||
WebRTC (Web Real-Time Communication) is a free and open-source project offering real-time
|
WebRTC (Web Real-Time Communication) is a free and open-source project offering real-time
|
||||||
communication (RTC) via application programming interfaces (APIs) to web browsers and mobile
|
communication (RTC) via application programming interfaces (APIs) to web browsers and mobile
|
||||||
applications [2]. It enables audio and video communication to work inside web pages by facilitating
|
applications [2]. It enables audio and video communication to work inside web pages by facilitating
|
||||||
direct peer-to-peer communication, thereby eliminating the need for plugin installations or native
|
direct peer-to-peer communication, and thereby eliminating the need for plugin installations or native
|
||||||
app downloads [3]. Supported by Apple, Google, Microsoft, Mozilla, and Opera, WebRTC's
|
app downloads [3]. Supported by Apple, Google, Microsoft, Mozilla, and Opera, WebRTC's
|
||||||
specifications have been published by the World Wide Web Consortium (W3C) and the Internet
|
specifications have been published by the World Wide Web Consortium (W3C) and the Internet
|
||||||
Engineering Task Force (IETF) [2].
|
Engineering Task Force (IETF) [2].
|
||||||
@@ -2667,7 +2776,7 @@ actions:
|
|||||||
**interaction data** and **technical data** [1].
|
**interaction data** and **technical data** [1].
|
||||||
|
|
||||||
**Interaction data** includes metrics such as the number of open tabs and windows, the number of
|
**Interaction data** includes metrics such as the number of open tabs and windows, the number of
|
||||||
webpages visited, the number and type of installed Firefox Add-ons, session length, and
|
webpages visited, the number and type of installed Firefox Add-ons, session length, and
|
||||||
interactions with features offered by Mozilla or third parties. These features may include
|
interactions with features offered by Mozilla or third parties. These features may include
|
||||||
Firefox search features and search partner referrals [1].
|
Firefox search features and search partner referrals [1].
|
||||||
|
|
||||||
@@ -2680,14 +2789,14 @@ actions:
|
|||||||
[1]: https://web.archive.org/web/20220901045358/https://support.mozilla.org/en-US/kb/telemetry-clientid "Telemetry collection and deletion | Firefox Help | support.mozilla.org"
|
[1]: https://web.archive.org/web/20220901045358/https://support.mozilla.org/en-US/kb/telemetry-clientid "Telemetry collection and deletion | Firefox Help | support.mozilla.org"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Disable Firefox technical and interaction data collection
|
name: Disable collection of technical and interaction data in Firefox
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
This script alters the `datareporting.healthreport.uploadEnabled` preference, effectively
|
This script alters the `datareporting.healthreport.uploadEnabled` preference, effectively
|
||||||
disabling it. This preference regulates whether Firefox sends telemetry data [1].
|
disabling it. This preference regulates whether Firefox sends telemetry data [1].
|
||||||
|
|
||||||
The script halts all data transmitted from Firefox to Mozilla via Telemetry [2], preventing
|
The script halts all data transmitted from Firefox to Mozilla via Telemetry [2], preventing
|
||||||
Firefox from sending technical and interaction data to Mozilla [3].
|
Firefox from sending technical and interaction data to Mozilla [3].
|
||||||
|
|
||||||
For reasons of military security, it's recommended by the U.S. government (NIST 800-53) that
|
For reasons of military security, it's recommended by the U.S. government (NIST 800-53) that
|
||||||
this data collection is disabled [4].
|
this data collection is disabled [4].
|
||||||
@@ -2702,7 +2811,7 @@ actions:
|
|||||||
prefName: datareporting.healthreport.uploadEnabled
|
prefName: datareporting.healthreport.uploadEnabled
|
||||||
jsonValue: 'false'
|
jsonValue: 'false'
|
||||||
-
|
-
|
||||||
name: Disable verbose Firefox telemetry collection
|
name: Disable detailed telemetry collection in Firefox
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
This script disables the `toolkit.telemetry.enabled` preference, which controls whether the
|
This script disables the `toolkit.telemetry.enabled` preference, which controls whether the
|
||||||
@@ -2730,7 +2839,7 @@ actions:
|
|||||||
prefName: toolkit.telemetry.enabled
|
prefName: toolkit.telemetry.enabled
|
||||||
jsonValue: 'false'
|
jsonValue: 'false'
|
||||||
-
|
-
|
||||||
name: Disable Firefox telemetry archive
|
name: Disable archiving of Firefox telemetry
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
This script configures the `toolkit.telemetry.archive.enabled` preference to be disabled, thus
|
This script configures the `toolkit.telemetry.archive.enabled` preference to be disabled, thus
|
||||||
@@ -2784,12 +2893,12 @@ actions:
|
|||||||
docs: |-
|
docs: |-
|
||||||
This script sets the `toolkit.telemetry.log.level` preference to the least verbosity to minimize logs.
|
This script sets the `toolkit.telemetry.log.level` preference to the least verbosity to minimize logs.
|
||||||
This preference adjusts the telemetry logging verbosity as per `Log.jsm` [1].
|
This preference adjusts the telemetry logging verbosity as per `Log.jsm` [1].
|
||||||
By default, logging is restricted to the console service [1]
|
By default, logging is restricted to the console service [1].
|
||||||
It displays telemetry information in the browser console [2] and can be used to monitor telemetry activity
|
It displays telemetry information in the browser console [2] and can be used to monitor telemetry activity
|
||||||
in the JavaScript console [3]. This process is also referred to as telemetry tracing [4].
|
in the JavaScript console [3]. This process is also referred to as telemetry tracing [4].
|
||||||
|
|
||||||
The output can be accessed via the DevTools console [4], navigable through the menu by selecting
|
The output can be accessed via the DevTools console [4], navigable through the menu by selecting
|
||||||
"Tools" => "Developer Tools" => "Error Console" (CTRL+SHIFT+J).
|
"Tools" => "Developer Tools" => "Error Console" (CTRL+SHIFT+J).
|
||||||
|
|
||||||
The available verbosity levels, in descending order, are `Trace`, `Debug`, `Config`, `Info`, `Warn` (default),
|
The available verbosity levels, in descending order, are `Trace`, `Debug`, `Config`, `Info`, `Warn` (default),
|
||||||
`Error`, and `Fatal` [1].
|
`Error`, and `Fatal` [1].
|
||||||
@@ -2804,10 +2913,10 @@ actions:
|
|||||||
prefName: toolkit.telemetry.log.level
|
prefName: toolkit.telemetry.log.level
|
||||||
jsonValue: 'Fatal'
|
jsonValue: 'Fatal'
|
||||||
-
|
-
|
||||||
name: Disable dumping Firefox Telemetry log messages to stdout
|
name: Disable Firefox telemetry log output
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
This script deactivates `toolkit.telemetry.log.dump` as much as possible.
|
This script adjusts the `toolkit.telemetry.log.dump` setting to its most restricted value.
|
||||||
This preference controls whether to dump telemetry log messages to `stdout` [1].
|
This preference controls whether to dump telemetry log messages to `stdout` [1].
|
||||||
|
|
||||||
[1]: https://web.archive.org/web/20221015102124/https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/internals/preferences.html "Preferences and Defines — Firefox Source Docs documentation | firefox-source-docs.mozilla.org"
|
[1]: https://web.archive.org/web/20221015102124/https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/internals/preferences.html "Preferences and Defines — Firefox Source Docs documentation | firefox-source-docs.mozilla.org"
|
||||||
@@ -2840,7 +2949,7 @@ actions:
|
|||||||
[1]: https://web.archive.org/web/20221025163526/https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/concepts/pings.html "Telemetry pings — Firefox Source Docs documentation | firefox-source-docs.mozilla.org"
|
[1]: https://web.archive.org/web/20221025163526/https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/concepts/pings.html "Telemetry pings — Firefox Source Docs documentation | firefox-source-docs.mozilla.org"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Disable pinging to Firefox telemetry server
|
name: Disable pings to Firefox telemetry server
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
This script sets `toolkit.telemetry.server` to be empty.
|
This script sets `toolkit.telemetry.server` to be empty.
|
||||||
@@ -2965,7 +3074,7 @@ actions:
|
|||||||
name: Disable Firefox Pioneer study monitoring
|
name: Disable Firefox Pioneer study monitoring
|
||||||
recommend: standard
|
recommend: standard
|
||||||
docs: |-
|
docs: |-
|
||||||
This script configures `toolkit.telemetry.pioneer-new-studies-available` to be disabled to opt-out from
|
This script configures `toolkit.telemetry.pioneer-new-studies-available` to be disabled to opt out.
|
||||||
Firefox Pioneer program.
|
Firefox Pioneer program.
|
||||||
|
|
||||||
This setting disables availability check for Firefox Pioneer studies [1].
|
This setting disables availability check for Firefox Pioneer studies [1].
|
||||||
@@ -3020,7 +3129,7 @@ actions:
|
|||||||
prefName: network.captive-portal-service.enabled
|
prefName: network.captive-portal-service.enabled
|
||||||
jsonValue: 'false'
|
jsonValue: 'false'
|
||||||
-
|
-
|
||||||
category: Disable Firefox Phishing Protection (decreases your security)
|
category: Disable Firefox Phishing Protection (Safe Browsing) (decreases security)
|
||||||
docs: |-
|
docs: |-
|
||||||
Firefox's Safe Browsing feature, now referred to as Phishing Protection, is still internally
|
Firefox's Safe Browsing feature, now referred to as Phishing Protection, is still internally
|
||||||
recognized as "Safe Browsing" [1].
|
recognized as "Safe Browsing" [1].
|
||||||
@@ -3044,12 +3153,22 @@ actions:
|
|||||||
[3]: https://web.archive.org/web/20221025192516/https://www.usnews.com/opinion/articles/2016-06-22/google-is-the-worlds-biggest-censor-and-its-power-must-be-regulated "Google Is the World's Biggest Censor and Its Power Must Be Regulated | usnews.com"
|
[3]: https://web.archive.org/web/20221025192516/https://www.usnews.com/opinion/articles/2016-06-22/google-is-the-worlds-biggest-censor-and-its-power-must-be-regulated "Google Is the World's Biggest Censor and Its Power Must Be Regulated | usnews.com"
|
||||||
children:
|
children:
|
||||||
-
|
-
|
||||||
name: Disable Firefox plugin stability blocking
|
name: Disable blocking of unstable plugins in Firefox
|
||||||
docs: |-
|
docs: |-
|
||||||
This script sets `browser.safebrowsing.blockedURIs.enabled` to 'false', thereby disabling plugin
|
This script sets `browser.safebrowsing.blockedURIs.enabled` to 'false', thereby disabling plugin
|
||||||
stability blocking [1].
|
stability blocking [1].
|
||||||
|
|
||||||
|
Disabling this feature might have mixed implications:
|
||||||
|
|
||||||
|
- Disabling the blocking of unstable plugins can potentially expose the user to vulnerabilities associated with these plugins.
|
||||||
|
These plugins may include malware, which would compromise user privacy and data.
|
||||||
|
- It removes the need for data communication via Mozilla servers `addons.mozilla.org` for list retrieval [2]. This feature requires
|
||||||
|
frequent requests to download large amount of data.
|
||||||
|
|
||||||
|
If this blocking is removed, the user should be knowledgeable about the potential risks and will take precautions.
|
||||||
|
|
||||||
[1]: https://web.archive.org/web/20221025192643/https://wiki.mozilla.org/Security/Safe_Browsing#Prefs "Security/Safe Browsing - MozillaWiki | wiki.mozilla.org"
|
[1]: https://web.archive.org/web/20221025192643/https://wiki.mozilla.org/Security/Safe_Browsing#Prefs "Security/Safe Browsing - MozillaWiki | wiki.mozilla.org"
|
||||||
|
[2]: https://web.archive.org/web/20230811024650/https://blog.mozilla.org/addons/2020/08/24/introducing-a-scalable-add-ons-blocklist/ "Introducing a scalable add-ons blocklist | Mozilla Add-ons Community Blog"
|
||||||
call:
|
call:
|
||||||
function: AddFirefoxPrefs
|
function: AddFirefoxPrefs
|
||||||
parameters:
|
parameters:
|
||||||
@@ -3183,7 +3302,7 @@ functions:
|
|||||||
parameters:
|
parameters:
|
||||||
code: |-
|
code: |-
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import os, json
|
import os, json, sys
|
||||||
property_name = '{{ $setting }}'
|
property_name = '{{ $setting }}'
|
||||||
target = json.loads('{{ $jsonValue }}')
|
target = json.loads('{{ $jsonValue }}')
|
||||||
home_dir = f'/home/{os.getenv("SUDO_USER", os.getenv("USER"))}'
|
home_dir = f'/home/{os.getenv("SUDO_USER", os.getenv("USER"))}'
|
||||||
@@ -3200,7 +3319,15 @@ functions:
|
|||||||
continue
|
continue
|
||||||
print(f'Reading file at "{settings_file}".')
|
print(f'Reading file at "{settings_file}".')
|
||||||
file_content = file.read_text()
|
file_content = file.read_text()
|
||||||
json_object = json.loads(file_content)
|
if not file_content.strip():
|
||||||
|
print('Settings file is empty. Treating it as default empty JSON object.')
|
||||||
|
file_content = '{}'
|
||||||
|
json_object = None
|
||||||
|
try:
|
||||||
|
json_object = json.loads(file_content)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
print(f'Error, invalid JSON format in the settings file: "{settings_file}".', file=sys.stderr)
|
||||||
|
continue
|
||||||
if property_name not in json_object:
|
if property_name not in json_object:
|
||||||
print(f'Settings "{property_name}" is not configured.')
|
print(f'Settings "{property_name}" is not configured.')
|
||||||
else:
|
else:
|
||||||
@@ -3215,12 +3342,12 @@ functions:
|
|||||||
print(f'Successfully configured "{property_name}" to {json.dumps(target)}.')
|
print(f'Successfully configured "{property_name}" to {json.dumps(target)}.')
|
||||||
revertCode: |-
|
revertCode: |-
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import os, json
|
import os, json, sys
|
||||||
property_name = '{{ $setting }}'
|
property_name = '{{ $setting }}'
|
||||||
target = json.loads('{{ $jsonValue }}')
|
target = json.loads('{{ $jsonValue }}')
|
||||||
home_dir = f'/home/{os.getenv("SUDO_USER", os.getenv("USER"))}'
|
home_dir = f'/home/{os.getenv("SUDO_USER", os.getenv("USER"))}'
|
||||||
settings_files = [
|
settings_files = [
|
||||||
# Global installation (also snap that installs with "--classic" flag)
|
# Global installation (also Snap that installs with "--classic" flag)
|
||||||
f'{home_dir}/.config/Code/User/settings.json',
|
f'{home_dir}/.config/Code/User/settings.json',
|
||||||
# Flatpak installation
|
# Flatpak installation
|
||||||
f'{home_dir}/.var/app/com.visualstudio.code/config/Code/User/settings.json'
|
f'{home_dir}/.var/app/com.visualstudio.code/config/Code/User/settings.json'
|
||||||
@@ -3232,7 +3359,14 @@ functions:
|
|||||||
continue
|
continue
|
||||||
print(f'Reading file at "{settings_file}".')
|
print(f'Reading file at "{settings_file}".')
|
||||||
file_content = file.read_text()
|
file_content = file.read_text()
|
||||||
json_object = json.loads(file_content)
|
if not file_content.strip():
|
||||||
|
print(f'Skipping, no need to revert because settings file is empty: "{settings_file}".')
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
json_object = json.loads(file_content)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
print(f'Error, invalid JSON format in the settings file: "{settings_file}".', file=sys.stderr)
|
||||||
|
continue
|
||||||
if property_name not in json_object:
|
if property_name not in json_object:
|
||||||
print(f'Skipping, "{property_name}" is not configured.')
|
print(f'Skipping, "{property_name}" is not configured.')
|
||||||
continue
|
continue
|
||||||
@@ -3327,11 +3461,11 @@ functions:
|
|||||||
if ! sudo systemctl stop "$service"; then
|
if ! sudo systemctl stop "$service"; then
|
||||||
>&2 echo "Could not stop $service."
|
>&2 echo "Could not stop $service."
|
||||||
else
|
else
|
||||||
echo 'Successfuly stopped'
|
echo 'Successfully stopped'
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
if sudo systemctl disable "$service"; then
|
if sudo systemctl disable "$service"; then
|
||||||
echo "Successfuly disabled $service."
|
echo "Successfully disabled $service."
|
||||||
else
|
else
|
||||||
>&2 echo "Failed to disable $service."
|
>&2 echo "Failed to disable $service."
|
||||||
fi
|
fi
|
||||||
@@ -3348,7 +3482,7 @@ functions:
|
|||||||
echo "Skipping, $service is already enabled."
|
echo "Skipping, $service is already enabled."
|
||||||
else
|
else
|
||||||
if sudo systemctl enable "$service"; then
|
if sudo systemctl enable "$service"; then
|
||||||
echo "Successfuly enabled $service, it may require reboot to start."
|
echo "Successfully enabled $service, it may require reboot to start."
|
||||||
else
|
else
|
||||||
>&2 echo "Failed to enable $service."
|
>&2 echo "Failed to enable $service."
|
||||||
fi
|
fi
|
||||||
@@ -3675,7 +3809,7 @@ functions:
|
|||||||
echo "Succesfully restored."
|
echo "Succesfully restored."
|
||||||
else
|
else
|
||||||
>&2 echo "Failed to restore, backup file could not be found at $backup_file."
|
>&2 echo "Failed to restore, backup file could not be found at $backup_file."
|
||||||
>&2 echo "Was it change initially applied by privacy.sexy?"
|
>&2 echo "Was the change initially applied by privacy.sexy?"
|
||||||
fi
|
fi
|
||||||
-
|
-
|
||||||
name: ReplaceFileContent
|
name: ReplaceFileContent
|
||||||
@@ -3702,5 +3836,5 @@ functions:
|
|||||||
else
|
else
|
||||||
sudo rm -fv "$file"
|
sudo rm -fv "$file"
|
||||||
sudo mv "$file.backup" "$file"
|
sudo mv "$file.backup" "$file"
|
||||||
echo "Successfuly restored from backup: $file"
|
echo "Successfully restored from backup: $file"
|
||||||
fi
|
fi
|
||||||
|
|||||||
1
src/presentation/assets/icons/battery-full.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M464 160c8.8 0 16 7.2 16 16V336c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V176c0-8.8 7.2-16 16-16H464zM80 96C35.8 96 0 131.8 0 176V336c0 44.2 35.8 80 80 80H464c44.2 0 80-35.8 80-80V320c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32V176c0-44.2-35.8-80-80-80H80zm368 96H96V320H448V192z"/></svg>
|
||||||
|
After Width: | Height: | Size: 392 B |
1
src/presentation/assets/icons/battery-half.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M464 160c8.8 0 16 7.2 16 16V336c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V176c0-8.8 7.2-16 16-16H464zM80 96C35.8 96 0 131.8 0 176V336c0 44.2 35.8 80 80 80H464c44.2 0 80-35.8 80-80V320c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32V176c0-44.2-35.8-80-80-80H80zm208 96H96V320H288V192z"/></svg>
|
||||||
|
After Width: | Height: | Size: 392 B |
1
src/presentation/assets/icons/circle-info.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
|
||||||
|
After Width: | Height: | Size: 363 B |
1
src/presentation/assets/icons/copy.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M208 0H332.1c12.7 0 24.9 5.1 33.9 14.1l67.9 67.9c9 9 14.1 21.2 14.1 33.9V336c0 26.5-21.5 48-48 48H208c-26.5 0-48-21.5-48-48V48c0-26.5 21.5-48 48-48zM48 128h80v64H64V448H256V416h64v48c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V176c0-26.5 21.5-48 48-48z"/></svg>
|
||||||
|
After Width: | Height: | Size: 365 B |
1
src/presentation/assets/icons/desktop.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M64 0C28.7 0 0 28.7 0 64V352c0 35.3 28.7 64 64 64H240l-10.7 32H160c-17.7 0-32 14.3-32 32s14.3 32 32 32H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H346.7L336 416H512c35.3 0 64-28.7 64-64V64c0-35.3-28.7-64-64-64H64zM512 64V288H64V64H512z"/></svg>
|
||||||
|
After Width: | Height: | Size: 342 B |
1
src/presentation/assets/icons/face-smile.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M464 256A208 208 0 1 0 48 256a208 208 0 1 0 416 0zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zm177.6 62.1C192.8 334.5 218.8 352 256 352s63.2-17.5 78.4-33.9c9-9.7 24.2-10.4 33.9-1.4s10.4 24.2 1.4 33.9c-22 23.8-60 49.4-113.6 49.4s-91.7-25.5-113.6-49.4c-9-9.7-8.4-24.9 1.4-33.9s24.9-8.4 33.9 1.4zM144.4 208a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zm192-32a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
|
||||||
|
After Width: | Height: | Size: 495 B |
1
src/presentation/assets/icons/file-arrow-down.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M64 0C28.7 0 0 28.7 0 64V448c0 35.3 28.7 64 64 64H320c35.3 0 64-28.7 64-64V160H256c-17.7 0-32-14.3-32-32V0H64zM256 0V128H384L256 0zM216 232V334.1l31-31c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-72 72c-9.4 9.4-24.6 9.4-33.9 0l-72-72c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l31 31V232c0-13.3 10.7-24 24-24s24 10.7 24 24z"/></svg>
|
||||||
|
After Width: | Height: | Size: 428 B |
1
src/presentation/assets/icons/floppy-disk.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V173.3c0-17-6.7-33.3-18.7-45.3L352 50.7C340 38.7 323.7 32 306.7 32H64zm0 96c0-17.7 14.3-32 32-32H288c17.7 0 32 14.3 32 32v64c0 17.7-14.3 32-32 32H96c-17.7 0-32-14.3-32-32V128zM224 288a64 64 0 1 1 0 128 64 64 0 1 1 0-128z"/></svg>
|
||||||
|
After Width: | Height: | Size: 407 B |
1
src/presentation/assets/icons/folder-open.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M384 480h48c11.4 0 21.9-6 27.6-15.9l112-192c5.8-9.9 5.8-22.1 .1-32.1S555.5 224 544 224H144c-11.4 0-21.9 6-27.6 15.9L48 357.1V96c0-8.8 7.2-16 16-16H181.5c4.2 0 8.3 1.7 11.3 4.7l26.5 26.5c21 21 49.5 32.8 79.2 32.8H416c8.8 0 16 7.2 16 16v32h48V160c0-35.3-28.7-64-64-64H298.5c-17 0-33.3-6.7-45.3-18.7L226.7 50.7c-12-12-28.3-18.7-45.3-18.7H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H87.7 384z"/></svg>
|
||||||
|
After Width: | Height: | Size: 503 B |
1
src/presentation/assets/icons/folder.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M0 96C0 60.7 28.7 32 64 32H196.1c19.1 0 37.4 7.6 50.9 21.1L289.9 96H448c35.3 0 64 28.7 64 64V416c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V96zM64 80c-8.8 0-16 7.2-16 16V416c0 8.8 7.2 16 16 16H448c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16H286.6c-10.6 0-20.8-4.2-28.3-11.7L213.1 87c-4.5-4.5-10.6-7-17-7H64z"/></svg>
|
||||||
|
After Width: | Height: | Size: 419 B |
1
src/presentation/assets/icons/github.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
1
src/presentation/assets/icons/globe.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M352 256c0 22.2-1.2 43.6-3.3 64H163.3c-2.2-20.4-3.3-41.8-3.3-64s1.2-43.6 3.3-64H348.7c2.2 20.4 3.3 41.8 3.3 64zm28.8-64H503.9c5.3 20.5 8.1 41.9 8.1 64s-2.8 43.5-8.1 64H380.8c2.1-20.6 3.2-42 3.2-64s-1.1-43.4-3.2-64zm112.6-32H376.7c-10-63.9-29.8-117.4-55.3-151.6c78.3 20.7 142 77.5 171.9 151.6zm-149.1 0H167.7c6.1-36.4 15.5-68.6 27-94.7c10.5-23.6 22.2-40.7 33.5-51.5C239.4 3.2 248.7 0 256 0s16.6 3.2 27.8 13.8c11.3 10.8 23 27.9 33.5 51.5c11.6 26 20.9 58.2 27 94.7zm-209 0H18.6C48.6 85.9 112.2 29.1 190.6 8.4C165.1 42.6 145.3 96.1 135.3 160zM8.1 192H131.2c-2.1 20.6-3.2 42-3.2 64s1.1 43.4 3.2 64H8.1C2.8 299.5 0 278.1 0 256s2.8-43.5 8.1-64zM194.7 446.6c-11.6-26-20.9-58.2-27-94.6H344.3c-6.1 36.4-15.5 68.6-27 94.6c-10.5 23.6-22.2 40.7-33.5 51.5C272.6 508.8 263.3 512 256 512s-16.6-3.2-27.8-13.8c-11.3-10.8-23-27.9-33.5-51.5zM135.3 352c10 63.9 29.8 117.4 55.3 151.6C112.2 482.9 48.6 426.1 18.6 352H135.3zm358.1 0c-30 74.1-93.6 130.9-171.9 151.6c25.5-34.2 45.2-87.7 55.3-151.6H493.4z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
1
src/presentation/assets/icons/left-right.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M504.3 273.6c4.9-4.5 7.7-10.9 7.7-17.6s-2.8-13-7.7-17.6l-112-104c-7-6.5-17.2-8.2-25.9-4.4s-14.4 12.5-14.4 22l0 56-192 0 0-56c0-9.5-5.7-18.2-14.4-22s-18.9-2.1-25.9 4.4l-112 104C2.8 243 0 249.3 0 256s2.8 13 7.7 17.6l112 104c7 6.5 17.2 8.2 25.9 4.4s14.4-12.5 14.4-22l0-56 192 0 0 56c0 9.5 5.7 18.2 14.4 22s18.9 2.1 25.9-4.4l112-104z"/></svg>
|
||||||
|
After Width: | Height: | Size: 440 B |
1
src/presentation/assets/icons/magnifying-glass.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/></svg>
|
||||||
|
After Width: | Height: | Size: 343 B |
1
src/presentation/assets/icons/play.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg>
|
||||||
|
After Width: | Height: | Size: 257 B |
1
src/presentation/assets/icons/tag.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M0 80V229.5c0 17 6.7 33.3 18.7 45.3l176 176c25 25 65.5 25 90.5 0L418.7 317.3c25-25 25-65.5 0-90.5l-176-176c-12-12-28.3-18.7-45.3-18.7H48C21.5 32 0 53.5 0 80zm112 32a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
|
||||||
|
After Width: | Height: | Size: 310 B |
1
src/presentation/assets/icons/user-secret.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M224 16c-6.7 0-10.8-2.8-15.5-6.1C201.9 5.4 194 0 176 0c-30.5 0-52 43.7-66 89.4C62.7 98.1 32 112.2 32 128c0 14.3 25 27.1 64.6 35.9c-.4 4-.6 8-.6 12.1c0 17 3.3 33.2 9.3 48H45.4C38 224 32 230 32 237.4c0 1.7 .3 3.4 1 5l38.8 96.9C28.2 371.8 0 423.8 0 482.3C0 498.7 13.3 512 29.7 512H418.3c16.4 0 29.7-13.3 29.7-29.7c0-58.5-28.2-110.4-71.7-143L415 242.4c.6-1.6 1-3.3 1-5c0-7.4-6-13.4-13.4-13.4H342.7c6-14.8 9.3-31 9.3-48c0-4.1-.2-8.1-.6-12.1C391 155.1 416 142.3 416 128c0-15.8-30.7-29.9-78-38.6C324 43.7 302.5 0 272 0c-18 0-25.9 5.4-32.5 9.9c-4.8 3.3-8.8 6.1-15.5 6.1zm56 208H267.6c-16.5 0-31.1-10.6-36.3-26.2c-2.3-7-12.2-7-14.5 0c-5.2 15.6-19.9 26.2-36.3 26.2H168c-22.1 0-40-17.9-40-40V169.6c28.2 4.1 61 6.4 96 6.4s67.8-2.3 96-6.4V184c0 22.1-17.9 40-40 40zm-88 96l16 32L176 480 128 288l64 32zm128-32L272 480 240 352l16-32 64-32z"/></svg>
|
||||||
|
After Width: | Height: | Size: 934 B |
1
src/presentation/assets/icons/xmark.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></svg>
|
||||||
|
After Width: | Height: | Size: 390 B |
@@ -7,5 +7,3 @@
|
|||||||
@forward "./mixins";
|
@forward "./mixins";
|
||||||
|
|
||||||
@forward "./components/card";
|
@forward "./components/card";
|
||||||
|
|
||||||
@forward "./third-party-extensions/tooltip.scss";
|
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
// Based on https://github.com/Akryum/v-tooltip/blob/83615e394c96ca491a4df04b892ae87e833beb97/demo-src/src/App.vue#L179-L303
|
|
||||||
@use "@/presentation/assets/styles/colors" as *;
|
|
||||||
|
|
||||||
.tooltip {
|
|
||||||
display: block !important;
|
|
||||||
z-index: 10000;
|
|
||||||
.tooltip-inner {
|
|
||||||
background: $color-primary-darkest;
|
|
||||||
color: $color-on-primary;
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 5px 10px 4px;
|
|
||||||
}
|
|
||||||
.tooltip-arrow {
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
border-style: solid;
|
|
||||||
position: absolute;
|
|
||||||
margin: 5px;
|
|
||||||
border-color: $color-primary-darkest;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
&[x-placement^="top"] {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
.tooltip-arrow {
|
|
||||||
border-width: 5px 5px 0 5px;
|
|
||||||
border-left-color: transparent !important;
|
|
||||||
border-right-color: transparent !important;
|
|
||||||
border-bottom-color: transparent !important;
|
|
||||||
bottom: -5px;
|
|
||||||
left: calc(50% - 5px);
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&[aria-hidden='true'] {
|
|
||||||
visibility: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity .15s, visibility .15s;
|
|
||||||
}
|
|
||||||
&[aria-hidden='false'] {
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity .15s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
import { IconBootstrapper } from './Modules/IconBootstrapper';
|
|
||||||
import { VueConstructor, IVueBootstrapper } from './IVueBootstrapper';
|
import { VueConstructor, IVueBootstrapper } from './IVueBootstrapper';
|
||||||
import { VueBootstrapper } from './Modules/VueBootstrapper';
|
import { VueBootstrapper } from './Modules/VueBootstrapper';
|
||||||
import { TooltipBootstrapper } from './Modules/TooltipBootstrapper';
|
|
||||||
import { RuntimeSanityValidator } from './Modules/RuntimeSanityValidator';
|
import { RuntimeSanityValidator } from './Modules/RuntimeSanityValidator';
|
||||||
import { AppInitializationLogger } from './Modules/AppInitializationLogger';
|
import { AppInitializationLogger } from './Modules/AppInitializationLogger';
|
||||||
|
|
||||||
@@ -15,9 +13,7 @@ export class ApplicationBootstrapper implements IVueBootstrapper {
|
|||||||
|
|
||||||
private static getAllBootstrappers(): IVueBootstrapper[] {
|
private static getAllBootstrappers(): IVueBootstrapper[] {
|
||||||
return [
|
return [
|
||||||
new IconBootstrapper(),
|
|
||||||
new VueBootstrapper(),
|
new VueBootstrapper(),
|
||||||
new TooltipBootstrapper(),
|
|
||||||
new RuntimeSanityValidator(),
|
new RuntimeSanityValidator(),
|
||||||
new AppInitializationLogger(),
|
new AppInitializationLogger(),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
import { library } from '@fortawesome/fontawesome-svg-core';
|
|
||||||
import { faGithub } from '@fortawesome/free-brands-svg-icons';
|
|
||||||
/** BRAND ICONS (PREFIX: fab) */
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
|
||||||
/** REGULAR ICONS (PREFIX: far) */
|
|
||||||
import { faFolderOpen, faFolder, faSmile } from '@fortawesome/free-regular-svg-icons';
|
|
||||||
/** SOLID ICONS (PREFIX: fas (default)) */
|
|
||||||
import {
|
|
||||||
faTimes, faFileDownload, faCopy, faSearch, faInfoCircle, faUserSecret, faDesktop, faTag, faGlobe,
|
|
||||||
faSave, faBatteryFull, faBatteryHalf, faPlay, faArrowsAltH,
|
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import { IVueBootstrapper, VueConstructor } from '../IVueBootstrapper';
|
|
||||||
|
|
||||||
export class IconBootstrapper implements IVueBootstrapper {
|
|
||||||
public bootstrap(vue: VueConstructor): void {
|
|
||||||
library.add(
|
|
||||||
faGithub,
|
|
||||||
faUserSecret,
|
|
||||||
faSmile,
|
|
||||||
faDesktop,
|
|
||||||
faGlobe,
|
|
||||||
faTag,
|
|
||||||
faFolderOpen,
|
|
||||||
faFolder,
|
|
||||||
faTimes,
|
|
||||||
faFileDownload,
|
|
||||||
faSave,
|
|
||||||
faCopy,
|
|
||||||
faPlay,
|
|
||||||
faSearch,
|
|
||||||
faBatteryFull,
|
|
||||||
faBatteryHalf,
|
|
||||||
faInfoCircle,
|
|
||||||
faArrowsAltH,
|
|
||||||
);
|
|
||||||
vue.component('font-awesome-icon', FontAwesomeIcon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import VTooltip from 'v-tooltip';
|
|
||||||
import { VueConstructor, IVueBootstrapper } from '../IVueBootstrapper';
|
|
||||||
|
|
||||||
export class TooltipBootstrapper implements IVueBootstrapper {
|
|
||||||
public bootstrap(vue: VueConstructor): void {
|
|
||||||
vue.use(VTooltip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,11 +7,12 @@
|
|||||||
<TheCodeButtons class="app__row app__code-buttons" />
|
<TheCodeButtons class="app__row app__code-buttons" />
|
||||||
<TheFooter />
|
<TheFooter />
|
||||||
</div>
|
</div>
|
||||||
|
<OptionalDevToolkit />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineAsyncComponent, defineComponent } from 'vue';
|
||||||
import TheHeader from '@/presentation/components/TheHeader.vue';
|
import TheHeader from '@/presentation/components/TheHeader.vue';
|
||||||
import TheFooter from '@/presentation/components/TheFooter/TheFooter.vue';
|
import TheFooter from '@/presentation/components/TheFooter/TheFooter.vue';
|
||||||
import TheCodeButtons from '@/presentation/components/Code/CodeButtons/TheCodeButtons.vue';
|
import TheCodeButtons from '@/presentation/components/Code/CodeButtons/TheCodeButtons.vue';
|
||||||
@@ -22,6 +23,10 @@ import { provideDependencies } from '../bootstrapping/DependencyProvider';
|
|||||||
|
|
||||||
const singletonAppContext = await buildContext();
|
const singletonAppContext = await buildContext();
|
||||||
|
|
||||||
|
const OptionalDevToolkit = process.env.NODE_ENV !== 'production'
|
||||||
|
? defineAsyncComponent(() => import('@/presentation/components/DevToolkit/DevToolkit.vue'))
|
||||||
|
: null;
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
TheHeader,
|
TheHeader,
|
||||||
@@ -29,6 +34,7 @@ export default defineComponent({
|
|||||||
TheScriptArea,
|
TheScriptArea,
|
||||||
TheSearchBar,
|
TheSearchBar,
|
||||||
TheFooter,
|
TheFooter,
|
||||||
|
OptionalDevToolkit,
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
provideDependencies(singletonAppContext); // In Vue 3.0 we can move it to main.ts
|
provideDependencies(singletonAppContext); // In Vue 3.0 we can move it to main.ts
|
||||||
@@ -59,5 +65,4 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -4,30 +4,30 @@
|
|||||||
type="button"
|
type="button"
|
||||||
@click="onClicked"
|
@click="onClicked"
|
||||||
>
|
>
|
||||||
<font-awesome-icon
|
<AppIcon
|
||||||
class="button__icon"
|
class="button__icon"
|
||||||
:icon="[iconPrefix, iconName]"
|
:icon="iconName"
|
||||||
size="2x"
|
|
||||||
/>
|
/>
|
||||||
<div class="button__text">{{text}}</div>
|
<div class="button__text">{{text}}</div>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent, PropType } from 'vue';
|
||||||
|
import { IconName } from '@/presentation/components/Shared/Icon/IconName';
|
||||||
|
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
AppIcon,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
text: {
|
text: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
iconPrefix: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
iconName: {
|
iconName: {
|
||||||
type: String,
|
type: String as PropType<IconName>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -64,6 +64,10 @@ export default defineComponent({
|
|||||||
box-shadow: 0 3px 9px $color-primary-darkest;
|
box-shadow: 0 3px 9px $color-primary-darkest;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
@include clickable;
|
@include clickable;
|
||||||
|
|
||||||
width: 10%;
|
width: 10%;
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
<span class="dollar">$</span>
|
<span class="dollar">$</span>
|
||||||
<code><slot /></code>
|
<code><slot /></code>
|
||||||
<TooltipWrapper>
|
<TooltipWrapper>
|
||||||
<font-awesome-icon
|
<AppIcon
|
||||||
class="copy-button"
|
class="copy-button"
|
||||||
:icon="['fas', 'copy']"
|
icon="copy"
|
||||||
@click="copyCode"
|
@click="copyCode"
|
||||||
/>
|
/>
|
||||||
<template v-slot:tooltip>
|
<template v-slot:tooltip>
|
||||||
@@ -19,10 +19,12 @@
|
|||||||
import { defineComponent, useSlots } from 'vue';
|
import { defineComponent, useSlots } from 'vue';
|
||||||
import { Clipboard } from '@/infrastructure/Clipboard';
|
import { Clipboard } from '@/infrastructure/Clipboard';
|
||||||
import TooltipWrapper from '@/presentation/components/Shared/TooltipWrapper.vue';
|
import TooltipWrapper from '@/presentation/components/Shared/TooltipWrapper.vue';
|
||||||
|
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
TooltipWrapper,
|
TooltipWrapper,
|
||||||
|
AppIcon,
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const slots = useSlots();
|
const slots = useSlots();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<p>
|
<p>
|
||||||
<strong>2. The hard (manual) alternative</strong>. This requires you to do additional manual
|
<strong>2. The hard (manual) alternative</strong>. This requires you to do additional manual
|
||||||
steps. If you are unsure how to follow the instructions, hover on information
|
steps. If you are unsure how to follow the instructions, hover on information
|
||||||
(<font-awesome-icon :icon="['fas', 'info-circle']" />)
|
(<AppIcon icon="circle-info" />)
|
||||||
icons near the steps, or follow the easy alternative described above.
|
icons near the steps, or follow the easy alternative described above.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@@ -27,9 +27,9 @@
|
|||||||
<div class="step__action">
|
<div class="step__action">
|
||||||
<span>{{ step.action.instruction }}</span>
|
<span>{{ step.action.instruction }}</span>
|
||||||
<TooltipWrapper v-if="step.action.details">
|
<TooltipWrapper v-if="step.action.details">
|
||||||
<font-awesome-icon
|
<AppIcon
|
||||||
class="explanation"
|
class="explanation"
|
||||||
:icon="['fas', 'info-circle']"
|
icon="circle-info"
|
||||||
/>
|
/>
|
||||||
<template v-slot:tooltip>
|
<template v-slot:tooltip>
|
||||||
<div v-html="step.action.details" />
|
<div v-html="step.action.details" />
|
||||||
@@ -39,9 +39,9 @@
|
|||||||
<div v-if="step.code" class="step__code">
|
<div v-if="step.code" class="step__code">
|
||||||
<CodeInstruction>{{ step.code.instruction }}</CodeInstruction>
|
<CodeInstruction>{{ step.code.instruction }}</CodeInstruction>
|
||||||
<TooltipWrapper v-if="step.code.details">
|
<TooltipWrapper v-if="step.code.details">
|
||||||
<font-awesome-icon
|
<AppIcon
|
||||||
class="explanation"
|
class="explanation"
|
||||||
:icon="['fas', 'info-circle']"
|
icon="circle-info"
|
||||||
/>
|
/>
|
||||||
<template v-slot:tooltip>
|
<template v-slot:tooltip>
|
||||||
<div v-html="step.code.details" />
|
<div v-html="step.code.details" />
|
||||||
@@ -62,6 +62,7 @@ import {
|
|||||||
import { InjectionKeys } from '@/presentation/injectionSymbols';
|
import { InjectionKeys } from '@/presentation/injectionSymbols';
|
||||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||||
import TooltipWrapper from '@/presentation/components/Shared/TooltipWrapper.vue';
|
import TooltipWrapper from '@/presentation/components/Shared/TooltipWrapper.vue';
|
||||||
|
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
|
||||||
import CodeInstruction from './CodeInstruction.vue';
|
import CodeInstruction from './CodeInstruction.vue';
|
||||||
import { IInstructionListData } from './InstructionListData';
|
import { IInstructionListData } from './InstructionListData';
|
||||||
|
|
||||||
@@ -69,6 +70,7 @@ export default defineComponent({
|
|||||||
components: {
|
components: {
|
||||||
CodeInstruction,
|
CodeInstruction,
|
||||||
TooltipWrapper,
|
TooltipWrapper,
|
||||||
|
AppIcon,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -4,19 +4,16 @@
|
|||||||
v-if="canRun"
|
v-if="canRun"
|
||||||
text="Run"
|
text="Run"
|
||||||
v-on:click="executeCode"
|
v-on:click="executeCode"
|
||||||
icon-prefix="fas"
|
|
||||||
icon-name="play"
|
icon-name="play"
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
:text="isDesktopVersion ? 'Save' : 'Download'"
|
:text="isDesktopVersion ? 'Save' : 'Download'"
|
||||||
v-on:click="saveCode"
|
v-on:click="saveCode"
|
||||||
icon-prefix="fas"
|
:icon-name="isDesktopVersion ? 'floppy-disk' : 'file-arrow-down'"
|
||||||
:icon-name="isDesktopVersion ? 'save' : 'file-download'"
|
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
text="Copy"
|
text="Copy"
|
||||||
v-on:click="copyCode"
|
v-on:click="copyCode"
|
||||||
icon-prefix="fas"
|
|
||||||
icon-name="copy"
|
icon-name="copy"
|
||||||
/>
|
/>
|
||||||
<ModalDialog v-if="instructions" v-model="areInstructionsVisible">
|
<ModalDialog v-if="instructions" v-model="areInstructionsVisible">
|
||||||
|
|||||||
71
src/presentation/components/DevToolkit/DevToolkit.vue
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<template>
|
||||||
|
<div class="dev-toolkit">
|
||||||
|
<div class="title">
|
||||||
|
Tools
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<button
|
||||||
|
v-for="action in devActions"
|
||||||
|
@click="action.handler"
|
||||||
|
:key="action.name"
|
||||||
|
type="button">
|
||||||
|
{{ action.name }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { dumpNames } from './DumpNames';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
setup() {
|
||||||
|
const devActions: readonly DevAction[] = [
|
||||||
|
{
|
||||||
|
name: 'Log script/category names',
|
||||||
|
handler: async () => {
|
||||||
|
const names = await dumpNames();
|
||||||
|
console.log(names);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return {
|
||||||
|
devActions,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
interface DevAction {
|
||||||
|
readonly name: string;
|
||||||
|
readonly handler: () => void | Promise<void>;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
|
|
||||||
|
.dev-toolkit {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: rgba($color-on-surface, 0.5);
|
||||||
|
color: $color-on-primary;
|
||||||
|
padding: 10px;
|
||||||
|
z-index: 10000;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background-color: $color-primary;
|
||||||
|
color: $color-on-primary;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
35
src/presentation/components/DevToolkit/DumpNames.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { IApplication } from '@/domain/IApplication';
|
||||||
|
import { ApplicationFactory } from '@/application/ApplicationFactory';
|
||||||
|
|
||||||
|
export async function dumpNames(): Promise<string> {
|
||||||
|
const application = await ApplicationFactory.Current.getApp();
|
||||||
|
const names = collectNames(application);
|
||||||
|
const output = names.join('\n');
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectNames(application: IApplication): string[] {
|
||||||
|
const { collections } = application;
|
||||||
|
|
||||||
|
const allNames = [
|
||||||
|
...collections.flatMap((collection) => collection.getAllCategories().map((c) => c.name)),
|
||||||
|
...collections.flatMap((collection) => collection.getAllScripts().map((c) => c.name)),
|
||||||
|
];
|
||||||
|
|
||||||
|
const uniqueNames = [...new Set(allNames)];
|
||||||
|
|
||||||
|
return shuffle(uniqueNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shuffle an array of strings, returning a new array with elements in random order.
|
||||||
|
Uses the Fisher-Yates (or Durstenfeld) algorithm.
|
||||||
|
*/
|
||||||
|
function shuffle(array: readonly string[]): string[] {
|
||||||
|
const shuffledArray = [...array];
|
||||||
|
for (let i = array.length - 1; i > 0; i--) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1));
|
||||||
|
[shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]];
|
||||||
|
}
|
||||||
|
return shuffledArray;
|
||||||
|
}
|
||||||
@@ -4,9 +4,9 @@
|
|||||||
:style="{ cursor: cursorCssValue }"
|
:style="{ cursor: cursorCssValue }"
|
||||||
@mousedown="startResize">
|
@mousedown="startResize">
|
||||||
<div class="line" />
|
<div class="line" />
|
||||||
<font-awesome-icon
|
<AppIcon
|
||||||
class="icon"
|
class="icon"
|
||||||
:icon="['fas', 'arrows-alt-h']"
|
icon="left-right"
|
||||||
/>
|
/>
|
||||||
<div class="line" />
|
<div class="line" />
|
||||||
</div>
|
</div>
|
||||||
@@ -14,8 +14,12 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, onUnmounted } from 'vue';
|
import { defineComponent, onUnmounted } from 'vue';
|
||||||
|
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
AppIcon,
|
||||||
|
},
|
||||||
emits: {
|
emits: {
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
resized: (displacementX: number) => true,
|
resized: (displacementX: number) => true,
|
||||||
|
|||||||
@@ -17,18 +17,18 @@
|
|||||||
</span>
|
</span>
|
||||||
<span v-else>Oh no 😢</span>
|
<span v-else>Oh no 😢</span>
|
||||||
<!-- Expand icon -->
|
<!-- Expand icon -->
|
||||||
<font-awesome-icon
|
<AppIcon
|
||||||
class="card__inner__expand-icon"
|
class="card__inner__expand-icon"
|
||||||
:icon="['far', isExpanded ? 'folder-open' : 'folder']"
|
:icon="isExpanded ? 'folder-open' : 'folder'"
|
||||||
/>
|
/>
|
||||||
<!-- Indeterminate and full states -->
|
<!-- Indeterminate and full states -->
|
||||||
<div class="card__inner__state-icons">
|
<div class="card__inner__state-icons">
|
||||||
<font-awesome-icon
|
<AppIcon
|
||||||
:icon="['fa', 'battery-half']"
|
icon="battery-half"
|
||||||
v-if="isAnyChildSelected && !areAllChildrenSelected"
|
v-if="isAnyChildSelected && !areAllChildrenSelected"
|
||||||
/>
|
/>
|
||||||
<font-awesome-icon
|
<AppIcon
|
||||||
:icon="['fa', 'battery-full']"
|
icon="battery-full"
|
||||||
v-if="areAllChildrenSelected"
|
v-if="areAllChildrenSelected"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -38,8 +38,8 @@
|
|||||||
<ScriptsTree :categoryId="categoryId" />
|
<ScriptsTree :categoryId="categoryId" />
|
||||||
</div>
|
</div>
|
||||||
<div class="card__expander__close-button">
|
<div class="card__expander__close-button">
|
||||||
<font-awesome-icon
|
<AppIcon
|
||||||
:icon="['fas', 'times']"
|
icon="xmark"
|
||||||
v-on:click="collapse()"
|
v-on:click="collapse()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -52,6 +52,7 @@ import {
|
|||||||
defineComponent, ref, watch, computed,
|
defineComponent, ref, watch, computed,
|
||||||
inject,
|
inject,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
|
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
|
||||||
import { InjectionKeys } from '@/presentation/injectionSymbols';
|
import { InjectionKeys } from '@/presentation/injectionSymbols';
|
||||||
import ScriptsTree from '@/presentation/components/Scripts/View/Tree/ScriptsTree.vue';
|
import ScriptsTree from '@/presentation/components/Scripts/View/Tree/ScriptsTree.vue';
|
||||||
import { sleep } from '@/infrastructure/Threading/AsyncSleep';
|
import { sleep } from '@/infrastructure/Threading/AsyncSleep';
|
||||||
@@ -59,6 +60,7 @@ import { sleep } from '@/infrastructure/Threading/AsyncSleep';
|
|||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
ScriptsTree,
|
ScriptsTree,
|
||||||
|
AppIcon,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
categoryId: {
|
categoryId: {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
class="search__query__close-button"
|
class="search__query__close-button"
|
||||||
v-on:click="clearSearchQuery()"
|
v-on:click="clearSearchQuery()"
|
||||||
>
|
>
|
||||||
<font-awesome-icon :icon="['fas', 'times']" />
|
<AppIcon icon="xmark" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!searchHasMatches" class="search-no-matches">
|
<div v-if="!searchHasMatches" class="search-no-matches">
|
||||||
@@ -41,6 +41,7 @@ import {
|
|||||||
defineComponent, PropType, ref, computed,
|
defineComponent, PropType, ref, computed,
|
||||||
inject,
|
inject,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
|
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
|
||||||
import { InjectionKeys } from '@/presentation/injectionSymbols';
|
import { InjectionKeys } from '@/presentation/injectionSymbols';
|
||||||
import ScriptsTree from '@/presentation/components/Scripts/View/Tree/ScriptsTree.vue';
|
import ScriptsTree from '@/presentation/components/Scripts/View/Tree/ScriptsTree.vue';
|
||||||
import CardList from '@/presentation/components/Scripts/View/Cards/CardList.vue';
|
import CardList from '@/presentation/components/Scripts/View/Cards/CardList.vue';
|
||||||
@@ -52,6 +53,7 @@ export default defineComponent({
|
|||||||
components: {
|
components: {
|
||||||
ScriptsTree,
|
ScriptsTree,
|
||||||
CardList,
|
CardList,
|
||||||
|
AppIcon,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
currentView: {
|
currentView: {
|
||||||
|
|||||||
@@ -100,20 +100,27 @@ $text-size: 0.75em; // Lower looks bad on Firefox
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
@mixin set-paragraph-vertical-gap($paragraph-vertical-gap) {
|
||||||
Different browsers have different <p>, we should even this out.
|
p {
|
||||||
See CSS 2.1 specification https://www.w3.org/TR/CSS21/sample.html.
|
/*
|
||||||
*/
|
Remove default browser margin on paragraphs to ensure:
|
||||||
p {
|
1. A markdown text represented as a list (e.g. <ul>, <ol>) has same vertical spacing as a standalone paragraph (</p>).
|
||||||
|
2. The first paragraph in a sequence (first `<p>` usage) does not introduce top spacing.
|
||||||
|
3. Uniformity, so margin can be set consistently across browsers.
|
||||||
|
*/
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
Remove surrounding padding so a markdown text that is a list (e.g. <ul>)
|
Introduce spacing between successive elements and paragraphs.
|
||||||
has same outer padding as a paragraph (</p>).
|
E.g., spacing between two paragraphs (`p`), paragraphs after lists (<ul>, <ol>)...
|
||||||
*/
|
*/
|
||||||
margin: 0;
|
* {
|
||||||
+ p {
|
+ p {
|
||||||
margin-top: 1em;
|
margin-top: $paragraph-vertical-gap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@include set-paragraph-vertical-gap($text-size);
|
||||||
ul {
|
ul {
|
||||||
// CSS default is 40px, if the text is a bulletpoint, it leads to unexpected padding.
|
// CSS default is 40px, if the text is a bulletpoint, it leads to unexpected padding.
|
||||||
padding-inline-start: 1em;
|
padding-inline-start: 1em;
|
||||||
|
|||||||
@@ -6,14 +6,18 @@
|
|||||||
v-on:click.stop
|
v-on:click.stop
|
||||||
v-on:click="toggle()"
|
v-on:click="toggle()"
|
||||||
>
|
>
|
||||||
<font-awesome-icon :icon="['fas', 'info-circle']" />
|
<AppIcon icon="circle-info" />
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref } from 'vue';
|
import { defineComponent, ref } from 'vue';
|
||||||
|
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
AppIcon,
|
||||||
|
},
|
||||||
emits: [
|
emits: [
|
||||||
'show',
|
'show',
|
||||||
'hide',
|
'hide',
|
||||||
@@ -52,5 +56,4 @@ export default defineComponent({
|
|||||||
color: $color-primary-light;
|
color: $color-primary-light;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { NodeStateChangedEvent } from '../Node/State/StateAccess';
|
import { TreeNodeStateDescriptor } from '../Node/State/StateDescriptor';
|
||||||
import { ReadOnlyTreeNode } from '../Node/TreeNode';
|
import { ReadOnlyTreeNode } from '../Node/TreeNode';
|
||||||
|
|
||||||
export interface TreeNodeStateChangedEmittedEvent {
|
export interface TreeNodeStateChangedEmittedEvent {
|
||||||
readonly change: NodeStateChangedEvent;
|
|
||||||
readonly node: ReadOnlyTreeNode;
|
readonly node: ReadOnlyTreeNode;
|
||||||
|
readonly oldState?: TreeNodeStateDescriptor;
|
||||||
|
readonly newState: TreeNodeStateDescriptor;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ import {
|
|||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { TreeRoot } from '../TreeRoot/TreeRoot';
|
import { TreeRoot } from '../TreeRoot/TreeRoot';
|
||||||
import { useCurrentTreeNodes } from '../UseCurrentTreeNodes';
|
import { useCurrentTreeNodes } from '../UseCurrentTreeNodes';
|
||||||
import { NodeRenderingStrategy } from '../Rendering/NodeRenderingStrategy';
|
import { NodeRenderingStrategy } from '../Rendering/Scheduling/NodeRenderingStrategy';
|
||||||
import { useNodeState } from './UseNodeState';
|
import { useNodeState } from './UseNodeState';
|
||||||
import { TreeNode } from './TreeNode';
|
import { TreeNode } from './TreeNode';
|
||||||
import LeafTreeNode from './LeafTreeNode.vue';
|
import LeafTreeNode from './LeafTreeNode.vue';
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface DelayScheduler {
|
||||||
|
scheduleNext(callback: () => void, delayInMs: number): void;
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { ReadOnlyTreeNode } from '../../Node/TreeNode';
|
||||||
|
import { RenderQueueOrderer } from './RenderQueueOrderer';
|
||||||
|
|
||||||
|
export class CollapsedParentOrderer implements RenderQueueOrderer {
|
||||||
|
public orderNodes(nodes: Iterable<ReadOnlyTreeNode>): ReadOnlyTreeNode[] {
|
||||||
|
return orderNodes(nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function orderNodes(nodes: Iterable<ReadOnlyTreeNode>): ReadOnlyTreeNode[] {
|
||||||
|
return [...nodes]
|
||||||
|
.map((node, index) => ({ node, index }))
|
||||||
|
.sort((a, b) => {
|
||||||
|
const [
|
||||||
|
isANodeOfCollapsedParent,
|
||||||
|
isBNodeOfCollapsedParent,
|
||||||
|
] = [isParentCollapsed(a.node), isParentCollapsed(b.node)];
|
||||||
|
if (isANodeOfCollapsedParent !== isBNodeOfCollapsedParent) {
|
||||||
|
return (isANodeOfCollapsedParent ? 1 : 0) - (isBNodeOfCollapsedParent ? 1 : 0);
|
||||||
|
}
|
||||||
|
return a.index - b.index;
|
||||||
|
})
|
||||||
|
.map(({ node }) => node);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isParentCollapsed(node: ReadOnlyTreeNode): boolean {
|
||||||
|
const parentNode = node.hierarchy.parent;
|
||||||
|
if (parentNode) {
|
||||||
|
if (!parentNode.state.current.isExpanded) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return isParentCollapsed(parentNode);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { ReadOnlyTreeNode } from '../../Node/TreeNode';
|
||||||
|
|
||||||
|
export interface RenderQueueOrderer {
|
||||||
|
orderNodes(nodes: Iterable<ReadOnlyTreeNode>): ReadOnlyTreeNode[];
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { TreeNode } from '../Node/TreeNode';
|
import { TreeNode } from '../../Node/TreeNode';
|
||||||
|
|
||||||
export interface NodeRenderingStrategy {
|
export interface NodeRenderingStrategy {
|
||||||
shouldRender(node: TreeNode): boolean;
|
shouldRender(node: TreeNode): boolean;
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { DelayScheduler } from '../DelayScheduler';
|
||||||
|
|
||||||
|
export class TimeoutDelayScheduler implements DelayScheduler {
|
||||||
|
private timeoutId: ReturnType<typeof setTimeout> | undefined = undefined;
|
||||||
|
|
||||||
|
constructor(private readonly timer: TimeFunctions = {
|
||||||
|
clearTimeout: globalThis.clearTimeout.bind(globalThis),
|
||||||
|
setTimeout: globalThis.setTimeout.bind(globalThis),
|
||||||
|
}) { }
|
||||||
|
|
||||||
|
public scheduleNext(callback: () => void, delayInMs: number): void {
|
||||||
|
this.clear();
|
||||||
|
this.timeoutId = this.timer.setTimeout(callback, delayInMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private clear(): void {
|
||||||
|
if (this.timeoutId === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.timer.clearTimeout(this.timeoutId);
|
||||||
|
this.timeoutId = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TimeFunctions {
|
||||||
|
clearTimeout(id: ReturnType<typeof setTimeout>): void;
|
||||||
|
setTimeout(callback: () => void, delayInMs: number): ReturnType<typeof setTimeout>;
|
||||||
|
}
|
||||||
@@ -1,44 +1,43 @@
|
|||||||
import {
|
import {
|
||||||
WatchSource, computed, shallowRef, triggerRef, watch,
|
WatchSource, shallowRef, triggerRef, watch,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { ReadOnlyTreeNode } from '../Node/TreeNode';
|
import { ReadOnlyTreeNode } from '../Node/TreeNode';
|
||||||
import { useNodeStateChangeAggregator } from '../UseNodeStateChangeAggregator';
|
import { useNodeStateChangeAggregator } from '../UseNodeStateChangeAggregator';
|
||||||
import { TreeRoot } from '../TreeRoot/TreeRoot';
|
import { TreeRoot } from '../TreeRoot/TreeRoot';
|
||||||
import { useCurrentTreeNodes } from '../UseCurrentTreeNodes';
|
import { useCurrentTreeNodes } from '../UseCurrentTreeNodes';
|
||||||
import { NodeRenderingStrategy } from './NodeRenderingStrategy';
|
import { NodeRenderingStrategy } from './Scheduling/NodeRenderingStrategy';
|
||||||
|
import { DelayScheduler } from './DelayScheduler';
|
||||||
|
import { TimeoutDelayScheduler } from './Scheduling/TimeoutDelayScheduler';
|
||||||
|
import { RenderQueueOrderer } from './Ordering/RenderQueueOrderer';
|
||||||
|
import { CollapsedParentOrderer } from './Ordering/CollapsedParentOrderer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders tree nodes gradually to prevent UI freeze when loading large amounts of nodes.
|
* Renders tree nodes gradually to prevent UI freeze when loading large amounts of nodes.
|
||||||
*/
|
*/
|
||||||
export function useGradualNodeRendering(
|
export function useGradualNodeRendering(
|
||||||
treeWatcher: WatchSource<TreeRoot>,
|
treeWatcher: WatchSource<TreeRoot>,
|
||||||
|
useChangeAggregator = useNodeStateChangeAggregator,
|
||||||
|
useTreeNodes = useCurrentTreeNodes,
|
||||||
|
scheduler: DelayScheduler = new TimeoutDelayScheduler(),
|
||||||
|
initialBatchSize = 30,
|
||||||
|
subsequentBatchSize = 5,
|
||||||
|
orderer: RenderQueueOrderer = new CollapsedParentOrderer(),
|
||||||
): NodeRenderingStrategy {
|
): NodeRenderingStrategy {
|
||||||
const nodesToRender = new Set<ReadOnlyTreeNode>();
|
const nodesToRender = new Set<ReadOnlyTreeNode>();
|
||||||
const nodesBeingRendered = shallowRef(new Set<ReadOnlyTreeNode>());
|
const nodesBeingRendered = shallowRef(new Set<ReadOnlyTreeNode>());
|
||||||
let isFirstRender = true;
|
|
||||||
let isRenderingInProgress = false;
|
let isRenderingInProgress = false;
|
||||||
const renderingDelayInMs = 50;
|
const renderingDelayInMs = 50;
|
||||||
const initialBatchSize = 30;
|
|
||||||
const subsequentBatchSize = 5;
|
|
||||||
|
|
||||||
const { onNodeStateChange } = useNodeStateChangeAggregator(treeWatcher);
|
const { onNodeStateChange } = useChangeAggregator(treeWatcher);
|
||||||
const { nodes } = useCurrentTreeNodes(treeWatcher);
|
const { nodes } = useTreeNodes(treeWatcher);
|
||||||
|
|
||||||
const orderedNodes = computed<readonly ReadOnlyTreeNode[]>(() => nodes.value.flattenedNodes);
|
function updateNodeRenderQueue(node: ReadOnlyTreeNode, isVisible: boolean) {
|
||||||
|
if (isVisible
|
||||||
watch(() => orderedNodes.value, (newNodes) => {
|
|
||||||
newNodes.forEach((node) => updateNodeRenderQueue(node));
|
|
||||||
}, { immediate: true });
|
|
||||||
|
|
||||||
function updateNodeRenderQueue(node: ReadOnlyTreeNode) {
|
|
||||||
if (node.state.current.isVisible
|
|
||||||
&& !nodesToRender.has(node)
|
&& !nodesToRender.has(node)
|
||||||
&& !nodesBeingRendered.value.has(node)) {
|
&& !nodesBeingRendered.value.has(node)) {
|
||||||
nodesToRender.add(node);
|
nodesToRender.add(node);
|
||||||
if (!isRenderingInProgress) {
|
beginRendering();
|
||||||
scheduleRendering();
|
} else if (!isVisible) {
|
||||||
}
|
|
||||||
} else if (!node.state.current.isVisible) {
|
|
||||||
if (nodesToRender.has(node)) {
|
if (nodesToRender.has(node)) {
|
||||||
nodesToRender.delete(node);
|
nodesToRender.delete(node);
|
||||||
}
|
}
|
||||||
@@ -49,47 +48,57 @@ export function useGradualNodeRendering(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onNodeStateChange((node, change) => {
|
watch(() => nodes.value, (newNodes) => {
|
||||||
if (change.newState.isVisible === change.oldState.isVisible) {
|
nodesToRender.clear();
|
||||||
|
nodesBeingRendered.value.clear();
|
||||||
|
if (!newNodes || newNodes.flattenedNodes.length === 0) {
|
||||||
|
triggerRef(nodesBeingRendered);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updateNodeRenderQueue(node);
|
newNodes
|
||||||
|
.flattenedNodes
|
||||||
|
.filter((node) => node.state.current.isVisible)
|
||||||
|
.forEach((node) => nodesToRender.add(node));
|
||||||
|
beginRendering();
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
|
onNodeStateChange((change) => {
|
||||||
|
if (change.newState.isVisible === change.oldState?.isVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateNodeRenderQueue(change.node, change.newState.isVisible);
|
||||||
});
|
});
|
||||||
|
|
||||||
scheduleRendering();
|
function beginRendering() {
|
||||||
|
if (isRenderingInProgress) {
|
||||||
function scheduleRendering() {
|
return;
|
||||||
if (isFirstRender) {
|
|
||||||
renderNodeBatch();
|
|
||||||
isFirstRender = false;
|
|
||||||
} else {
|
|
||||||
const delayScheduler = new DelayScheduler(renderingDelayInMs);
|
|
||||||
delayScheduler.schedule(renderNodeBatch);
|
|
||||||
}
|
}
|
||||||
|
renderNextBatch(initialBatchSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderNodeBatch() {
|
function renderNextBatch(batchSize: number) {
|
||||||
if (nodesToRender.size === 0) {
|
if (nodesToRender.size === 0) {
|
||||||
isRenderingInProgress = false;
|
isRenderingInProgress = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isRenderingInProgress = true;
|
isRenderingInProgress = true;
|
||||||
const batchSize = isFirstRender ? initialBatchSize : subsequentBatchSize;
|
const orderedNodes = orderer.orderNodes(nodesToRender);
|
||||||
const sortedNodes = Array.from(nodesToRender).sort(
|
const currentBatch = orderedNodes.slice(0, batchSize);
|
||||||
(a, b) => orderedNodes.value.indexOf(a) - orderedNodes.value.indexOf(b),
|
if (currentBatch.length === 0) {
|
||||||
);
|
return;
|
||||||
const currentBatch = sortedNodes.slice(0, batchSize);
|
}
|
||||||
currentBatch.forEach((node) => {
|
currentBatch.forEach((node) => {
|
||||||
nodesToRender.delete(node);
|
nodesToRender.delete(node);
|
||||||
nodesBeingRendered.value.add(node);
|
nodesBeingRendered.value.add(node);
|
||||||
});
|
});
|
||||||
triggerRef(nodesBeingRendered);
|
triggerRef(nodesBeingRendered);
|
||||||
if (nodesToRender.size > 0) {
|
scheduler.scheduleNext(
|
||||||
scheduleRendering();
|
() => renderNextBatch(subsequentBatchSize),
|
||||||
}
|
renderingDelayInMs,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldNodeBeRendered(node: ReadOnlyTreeNode) {
|
function shouldNodeBeRendered(node: ReadOnlyTreeNode): boolean {
|
||||||
return nodesBeingRendered.value.has(node);
|
return nodesBeingRendered.value.has(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,21 +106,3 @@ export function useGradualNodeRendering(
|
|||||||
shouldRender: shouldNodeBeRendered,
|
shouldRender: shouldNodeBeRendered,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class DelayScheduler {
|
|
||||||
private timeoutId: ReturnType<typeof setTimeout> = null;
|
|
||||||
|
|
||||||
constructor(private delay: number) {}
|
|
||||||
|
|
||||||
schedule(callback: () => void) {
|
|
||||||
this.clear();
|
|
||||||
this.timeoutId = setTimeout(callback, this.delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
if (this.timeoutId) {
|
|
||||||
clearTimeout(this.timeoutId);
|
|
||||||
this.timeoutId = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
} from 'vue';
|
} from 'vue';
|
||||||
import HierarchicalTreeNode from '../Node/HierarchicalTreeNode.vue';
|
import HierarchicalTreeNode from '../Node/HierarchicalTreeNode.vue';
|
||||||
import { useCurrentTreeNodes } from '../UseCurrentTreeNodes';
|
import { useCurrentTreeNodes } from '../UseCurrentTreeNodes';
|
||||||
import { NodeRenderingStrategy } from '../Rendering/NodeRenderingStrategy';
|
import { NodeRenderingStrategy } from '../Rendering/Scheduling/NodeRenderingStrategy';
|
||||||
import { TreeRoot } from './TreeRoot';
|
import { TreeRoot } from './TreeRoot';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
|||||||
@@ -68,8 +68,13 @@ export default defineComponent({
|
|||||||
const nodeRenderingScheduler = useGradualNodeRendering(() => tree);
|
const nodeRenderingScheduler = useGradualNodeRendering(() => tree);
|
||||||
|
|
||||||
const { onNodeStateChange } = useNodeStateChangeAggregator(() => tree);
|
const { onNodeStateChange } = useNodeStateChangeAggregator(() => tree);
|
||||||
onNodeStateChange((node, change) => {
|
|
||||||
emit('nodeStateChanged', { node, change });
|
onNodeStateChange((change) => {
|
||||||
|
emit('nodeStateChanged', {
|
||||||
|
node: change.node,
|
||||||
|
newState: change.newState,
|
||||||
|
oldState: change.oldState,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@@ -6,14 +6,15 @@ import { TreeNodeCheckState } from './Node/State/CheckState';
|
|||||||
|
|
||||||
export function useAutoUpdateChildrenCheckState(
|
export function useAutoUpdateChildrenCheckState(
|
||||||
treeWatcher: WatchSource<TreeRoot>,
|
treeWatcher: WatchSource<TreeRoot>,
|
||||||
|
useChangeAggregator = useNodeStateChangeAggregator,
|
||||||
) {
|
) {
|
||||||
const { onNodeStateChange } = useNodeStateChangeAggregator(treeWatcher);
|
const { onNodeStateChange } = useChangeAggregator(treeWatcher);
|
||||||
|
|
||||||
onNodeStateChange((node, change) => {
|
onNodeStateChange((change) => {
|
||||||
if (change.newState.checkState === change.oldState.checkState) {
|
if (change.newState.checkState === change.oldState?.checkState) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updateChildrenCheckedState(node.hierarchy, change.newState.checkState);
|
updateChildrenCheckedState(change.node.hierarchy, change.newState.checkState);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,14 +7,15 @@ import { ReadOnlyTreeNode } from './Node/TreeNode';
|
|||||||
|
|
||||||
export function useAutoUpdateParentCheckState(
|
export function useAutoUpdateParentCheckState(
|
||||||
treeWatcher: WatchSource<TreeRoot>,
|
treeWatcher: WatchSource<TreeRoot>,
|
||||||
|
useChangeAggregator = useNodeStateChangeAggregator,
|
||||||
) {
|
) {
|
||||||
const { onNodeStateChange } = useNodeStateChangeAggregator(treeWatcher);
|
const { onNodeStateChange } = useChangeAggregator(treeWatcher);
|
||||||
|
|
||||||
onNodeStateChange((node, change) => {
|
onNodeStateChange((change) => {
|
||||||
if (change.newState.checkState === change.oldState.checkState) {
|
if (change.newState.checkState === change.oldState?.checkState) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updateNodeParentCheckedState(node.hierarchy);
|
updateNodeParentCheckedState(change.node.hierarchy);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||