Compare commits

..

24 Commits

Author SHA1 Message Date
undergroundwires
493fb1ec16 mac: add new scripts and category for services 2023-10-19 01:21:03 +02:00
undergroundwires
b167a69976 Fix YAML error for site release in CI/CD
Fix the syntax error in the GitHub action script that was caused by
improper multi-line YAML notation. This correction ensures the action
can successfully parse and execute.
2023-10-19 00:45:23 +02:00
undergroundwires
79b46bf210 Improve performance of rendering during search
Optimize the tree view rendering during searches by enhancing the render
queue ordering. This update changes the rendering order to prioritize
visible nodes, leading to faster appearance of these nodes during
searches. The ordering logic now ignores the depth in the hierarchy and
instead focused on the node order. The collapsed check for the node
itself is removed, ensuring that visible collapsed parents are first
while their invisible children are rendered later.
2023-10-18 16:44:49 +02:00
undergroundwires
98a26f9ae4 win: improve system app uninstall /w fallback #260
This commit improves soft deletion of system apps. Before if the package
was missing, it failed to recover or delete system apps. Now, it works
even though if `Get-AppxPackage` returns null (i.e. package is
non-existing), so it can be executed even after a hard delete. This
allows safely introducing hard-delete of system apps (as discussed in
 #260) with still keeping a robust soft-delete as complement.

Before, the script was dependent on `Get-AppxPackage.InstallLocation`,
however a system app can only be located in one of these folders:

- C:\Windows\SystemApps\{PackageFamilyName}
- C:\Windows\{ShortAppName}

To ensure resilience, this commit adjust the script to rename the files
within these directories if Get-AppxPackage fails, this provides a
fallback.
2023-10-17 13:56:32 +02:00
undergroundwires
dbe3c5cfb9 win: improve system app uninstall cleanup #73
- Add documentation about folders.
- Add more user-friendly logging.
- Continue uninstallation if single folder fails (remove throw).
- Continue uninstallation if renaming single file fails.
- Add handling of `Metadata` folder as suggested in #73.
2023-10-16 18:42:29 +02:00
undergroundwires
25d7f7b2a4 Bump dependencies to latest
This commit updates various dependencies to their latest versions.

Other changes include:

- Moved the following from `devDependencies` to `dependencies`:
  - `electron-log`
  - `electron-updater`
- Remove `npm` dependency.
- Code changes:
  - Add type casting in several places to align with the latest
    `typescript` version.
  - Adopt to new return type of `setTimeout`.
- Dependencies not upgraded due to
  `@vue/eslint-config-airbnb-with-typescript` not supporting
  `@eslint-typescript` V6 (see vuejs/eslint-config-airbnb#58):
  - `vue/eslint-config-typescript`
  - `@typescript-eslint/eslint-plugin`
  - `@typescript-eslint/parser`
- Enable video recording for cypress as it's disabled by default since
  13.X.X.
2023-10-16 02:06:19 +02:00
undergroundwires-bot
b76e99ac0f ⬆️ bump everywhere to 0.12.5 2023-10-14 14:17:25 +00:00
undergroundwires
67c3677621 win, linux, mac: fix typos and improve naming
- Use instruction format such as "do this, do that" to provide clear,
  direct instructions. This format minimize confusion and is easy to
  follow. They are specific and leave no room for interpretation,
  stating precisely what needs to be done without ambiguity.
- Fix typos and grammar issues.
- Improve consistency in script and category names.
- Revise sentences for more natural English language flow.
- Change brand name casing to match official branding.
- Change title case (all words start capitalized) to sentence case.
- Prioritize consistency over variations.
- Add minor documentation to explain scripts where the names are not
  clear.
- Add naming guidelines.
2023-10-13 20:14:33 +02:00
undergroundwires
bab6316e76 win: fix and improve AppCompat disabling #255
- Introduce a new parent category: 'Disable Application Compatibility
  framework" for better categorization.
- Move following existing scripts under the new category:
  - Disable Application Impact Telemetry (AIT)
  - Disable steps recorder
  - Disable Inventory Collector
  - Program Compatibility Assistant Service
- Add new scripts new scripts within the same category:
  - Disable Application Compatibility Engine
  - Disable "Program Compatibility Assistant (PCA)" feature
  - Disable "Program Compatibility Assistant Service" (`PcaSvc`)
- Add missing revert codes for:
  - 'Disable steps recorder'
- Fix revert codes for scripts:
  - 'Disable Inventory Collector'
  - 'Disable Application Impact Telemetry (AIT)' (as pointed in #255).
- Add extensive documentation for all related scripts.
- Rename scripts for clarity:
  - 'Disable Inventory Collector' > 'Disable "Inventory Collector"
    task'.
  - 'Program Compatibility Assistant Service' > 'Disable "Program
    Compatibility Assistant Service" (`PcaSvc`) service'.
  - 'Disable steps recorder' > 'Disable Steps Recorder (collects
    screenshots, mouse/keyboard input and UI data)'.
2023-10-12 14:49:35 +02:00
undergroundwires
48730bca05 Implement new UI component for icons #230
- Introduce `AppIcon.vue`, offering improved performance over the
  previous `fort-awesome` dependency. This implementation reduces bundle
  size by 67.31KB (tested for web using `npm run build -- --mode prod`).
- Migrate Font Awesome 5 icons to Font Awesome 6.

This commit facilitates migration to Vue 3.0 (#230) and ensures no Vue
component remains tightly bound to a specific Vue version, enhancing
code portability.

Font Awesome license is not included because Font Awesome revokes its
right:

> "Attribution is no longer required as of Font Awesome 3.0"
>
> Sources:
>
> - https://fontawesome.com/v4/license/ (archived: https://web.archive.org/web/20231003213441/https://fontawesome.com/v4/license/, https://archive.ph/Yy9j5)
> - https://github.com/FortAwesome/Font-Awesome/wiki (archived: https://web.archive.org/web/20231003214646/https://github.com/FortAwesome/Font-Awesome/wiki, https://archive.ph/C6sXv)

This commit removes following third-party production dependencies:

- `@fortawesome/vue-fontawesome`
- `@fortawesome/free-solid-svg-icons`
- `@fortawesome/free-regular-svg-icons`
- `@fortawesome/free-brands-svg-icons`
- `@fortawesome/fontawesome-svg-core`
2023-10-11 18:38:19 +02:00
undergroundwires
698b570ee6 Fix working directory in CI/CD web release
This commit fixes the CI/CD website release process which was failing
due to an incorrect working directory setting. The `working-directory`
is now correctly set within the action workflow, ensuring the `npm run
install-deps` command runs in project root directory where
`package.json` exists.
2023-10-10 15:37:59 +02:00
undergroundwires
a3f11dff18 win: improve app reversion and docs #260
This commit prepares for #260, aiming for a hard delete of system apps,
and necessitating a more reliable app reversion method.

- Improve documentation:
  - Add existence status for latest OS versions.
  - Add command for quick future testing.
  - Use archive links.
  - Document categories.
  - Add documentation to list of default apps to give context about why
    the package is here.
  - Fix wrong store URL for Cortana app.
  - Unify documentation of excluded apps.
- Fix categorization:
  - Categorize uninstallation of Windows store apps.
  - Remove "Zune" category (flatten children apps) to be able to align
    with latest branding.
  - Categorize uninstallation of Candy Crush apps.
  - Categorize uninstallation of OOBE apps.
- Rename:
  - "Uninstall Windows store apps" to "Uninstall Windows apps" as these
    apps are not necessarily store apps.
  - "Xbox Game Bar Plugin appcache" to "Xbox Game Bar Plugin".
  - "Groove Music" to "Windows Media Player".
  - "Movies and TV" to "Movies & TV".
  - "Your Phone" to "Phone Link".
  - "Cred Dialog Host" to "Credentials Dialog Host".
  - "Windows Voice Recorder" to "Windows Sound Recorder".
  - "Remote Desktop" to "Microsoft Remote Desktop"
  - "Microsoft To Do" to "Microsoft To Do: Lists, Tasks & Reminders".
  - "People Hub app (People Experience Host)" to "People Hub app".
  - "My Office" to "Microsoft 365 (Office)".
  - "iHeartRadio" to "iHeart: Radio, Music, Podcasts".
  - "Duolingo" to "Duolingo - Language Lessons".
  - "Photoshop Express" to "Adobe Photoshop Express".
  - "Spotify" to "Spotify - Music and Podcasts".
  - "Windows Alarms and Clock" to "Windows Clock".
  - "OOBE Network Captive Port" to "OOBE Network Captive Portal".
  - "Secure Assessment Browser app (breaks Microsoft Intune/Graph)" to
    "Take a Test app".
  - "Windows 10 Family Safety / Parental Controls" > "Microsoft Family
    Safety / Parental control".
  - "People / People Bar App on taskbar (People Experience Host)" > "My
    People"
  - "MSN News" > "Microsoft News"
  - "Minecraft for Windows 10" > "Minecraft for Windows"
  - "Snip & Sketch" > "Snipping Tool"
  - "Bio enrollment" > "Hello setup UI"
- Fix package names for:
  - `AdobeSystemIncorporated.AdobePhotoshop` >
    `AdobeSystemsIncorporated.AdobePhotoshopExpress`
2023-10-09 16:21:26 +02:00
undergroundwires
5e359c2fb8 win: fix and improve network data usage reset #265
Fix `Clear (Reset) Network Data Usage` trying to delete other files from
Windows system directory.

Changes:

- Precisely target the deletion of `C:\System32\sru\SRUDB.dat`.
- Improve documentation.
- Handle explicitly and better if `DPS` service is missing.
- Rename script from `Clear (Reset) Network Data Usage` to `Clear System
  Resource Usage Monitor (SRUM) data` for clearer representation.
- Migrate script from batchfile to PowerShell for better
  maintainability and readability.
- Add user-friendly output messages.
- Improve script logic to avoid unnecessary service start/stop when the
  file doesn't exist.
2023-10-08 15:55:06 +02:00
undergroundwires
2147eae687 Add developer toolkit UI component
The commit adds a new a UI component that's enabled in development mode.
This component, initially, provides a button that wen clicked, logs all
the script and category names to the console. It helps revising names
used throughout the application.

By having this component in a conditionally rendered component, it's
excluded from the production builds.
2023-10-07 15:14:53 +02:00
undergroundwires
286295128d win: relocate and document SecHealthUI #190
- Move removal of `SecHealthUI` app to "Privacy over security" category.
- Emphasize disruptive behavior in the script name.
- Add comprehensive documentation
2023-10-06 14:02:11 +02:00
undergroundwires
8501495c17 win: improve Edge & OneDrive shortcut removal #73
- Add script to remove Edge shortcuts upon uninstallation.
- Unify OneDrive shortcut removal logic with Edge's, introducing revert
  feature to the OneDrive removal script.
- Add more extensive documentation.
- Rename "Delete OneDrive shortcuts" to "Remove OneDrive shortcuts" to
  have consistent naming.
2023-10-05 11:50:21 +02:00
undergroundwires
888c9166fc win: add removal of Edge assocations #64
This commit introduces scripts for cleaning up file and URL associations
related to Microsoft Edge, enhancing the uninstallation process. The
changes adress the issues detailed in #64, improving system reliability,
integrity and security by preventing lingering associations.

Changes include:

- Introduce scripts to clear Edge browser file and URL associations.
- Provide extensive documentation for related scripts.
- Ensure thorough cleanup of URL, file, OpenWith menu, and toast
  associations.
- Recommend removing Microsoft Edge (Legacy) Dev Tools Client app on
  Strict to align with other Edge legacy removal recommendations.
2023-10-04 11:22:47 +02:00
undergroundwires
e5f6edf405 linux: fix obsolete Firefox DPI script #239
- Replace obsolete "Firefox First party isolation" with "Firefox state
  partitioning".
- Add comprehensive documentation for the new scripts.
- Introduce enabling dynamic First-Party Isolation (dFPI)
- Disable deprecated First-Party Isolation (FPI) to avoid conflicts with
  dFPI.
- Add script to enable Firefox network partitioning to cover
  functionality of older FPI script.
2023-10-03 12:36:06 +02:00
undergroundwires
e8a52f717d win, linux: improve VSCode setting robustness #196
This commit enhances the robustness of setting VSCode configurations,
ensuring consistent and reliable operation even in edge cases, such as
when the settings file is empty. This commit also uniforms behavior of
Linux and Windows modification of VSCode settings.

On Windows:

- Move parameters to on top of scripts to be able to easily test the
  scripts using PowerShell without compiling.
- Add a check to exit the script with an error message if the attempt to
  parse the JSON content fails.
- Omit the `OutString` cmdlet from the pipeline in the script for
  converting JSON file content to a PowerShell object. `Out-String` is
  unnecessary in this context because `Get-Content` already outputs the
  file content as a string array, which `ConvertFrom-Json` effectively.
  Additionally, using `Out-String` could potentially introduce issues by
  concatenating file content into a single string, causing
  `ConvertFrom-Json` to fail when processing pretty-printed JSON. By
  removing `Out-String`, the script is streamlined and potential errors
  are avoided.
- Add logic to handle empty settings file. Add an additional check for
  empty settings file, if the file is empty, the script writes a default
  empty JSON object (`{}`) to the file. The operation is logged to
  ensure transparency, notifying the user of the action taken. This
  change removes fails due to empty setting files.
- When reverting, do not fail if the setting file is missing because it
  means that default settings are already in-place.
- When reverting, show informative message if the key does not exist or
  does not have the value set by privacy.sexy and do not take any
  further action.
- If the desired value is already set, show a message for it and skip
  updating the setting file.

On Linux:

- Handles empty `settings.json` similarly to Windows.
- Add more user friendly error if JSON file cannot be parsed.
2023-10-02 14:33:55 +02:00
undergroundwires
d45750428c win: fix and improve temp dir cleanup #176, #89
This commit improve cleanup of temporary directories on Windows,
addressing issues #176 and #89.

Changes include:

- Fix side-effects caused by this script by clearing the contents of
  directories rather than deleting the directories themselves.
- Add the removal of Prefetch directory contents, which stores temporary
  files and can enhance privacy and free up disk space when cleared.
- Remove the command `del /f /q %localappdata%\Temp\*` due to its
  redundancy.
- Improve the granularity and documentation of cleanup scripts, and
  moving the `Clear temporary Windows files` category up in the hierarchy
  for better structure and clarity.

Co-authored-by: iam-py-test <84232764+iam-py-test@users.noreply.github.com>
2023-10-01 17:42:25 +02:00
undergroundwires
cf55ca9e28 Add Scoop download instructions #174
- Add "Further Installation Options" section.
- Move releases page reference to the new section to keep Get Started
  simple.

Co-authored-by: MrEddX <101912712+Zliced13@users.noreply.github.com>
2023-09-29 14:03:07 +02:00
undergroundwires
3e5239f7d3 Add SAST security checks with SECURITY.md #178
This commit incorporates Static Analysis Security Testing (SAST) using
CodeQL. This integration will enforce consistent security assessments
with every change and on a predetermined schedule.

This commit also involves a restructure of security checks. The existing
security-checks workflow is renamed to better reflect its functionality
related to dependency audits.

These changes will enhance the project's resilience against potential
vulnerabilities in both the codebase and third-party dependencies.

Changes include:

- Remove older LGTM badge that's replaced by SAST checks.
- Rename `checks.security.yaml` to `checks.security.dependencies.yaml`,
  reinforcing the focus on dependency audits.
- Update `README.md`, ensuring the clear representation of security
  check statuses, including new SAST integration.
- Add new `SECURITY.md`, establishing the protocol for reporting
  vulnerabilities and outlining the project's commitment to robust
  security testing.
- Enhance `docs/tests.md` with detailed information on the newly
  integrated security checks.
- Add reference to SECURITY.md in README.md.
2023-09-28 15:19:09 +02:00
undergroundwires
7669985f8e Fix Docker build and improve checks #220
This commit improves multiple aspects of Docker builds:

- Enable artifact output validation for Dockerfile.
- Correct the path references in Dockerfile for the distribution
  directory.
- Add Dockerfile specific indentation rules to `.editorconfig`.
- Use `npm run install-deps` for dependency installation, enhancing
  build reliability.
- Add automation script `verify-web-server-status.js` to verify running
  web server on given URL.
- Introduce automated build verification for Dockerfile:
  - On macOS, install Docker with colima as the container runtime
    because default agents do not include Docker and Docker runtime is
    not installed due to licensing issues (see actions/runner-images#17).
  - On Windows, there's no Linux container support (actions/runner#904,
    actions/runner-images#1143), so keep the checks for macOS and Ubuntu
    only.
2023-09-27 19:53:40 +02:00
undergroundwires-bot
5047c9b6e7 ⬆️ bump everywhere to 0.12.4 2023-09-26 11:45:04 +00:00
85 changed files with 9479 additions and 7775 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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 }}"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,45 @@
# 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) ## 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) * linux: use user.js over prefs.js for Firefox #232 | [dae6d11](https://github.com/undergroundwires/privacy.sexy/commit/dae6d114daab6857d773071211eb57619b136281)

View File

@@ -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;"]

View File

@@ -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.12.3/privacy.sexy-Setup-0.12.3.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.12.3/privacy.sexy-0.12.3.dmg), [Linux](https://github.com/undergroundwires/privacy.sexy/releases/download/0.12.3/privacy.sexy-0.12.3.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
View 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.

View File

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

View File

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

View File

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

View File

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

9400
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "privacy.sexy", "name": "privacy.sexy",
"version": "0.12.3", "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 🍑🍆",
@@ -35,24 +35,20 @@
}, },
"dependencies": { "dependencies": {
"@floating-ui/vue": "^1.0.2", "@floating-ui/vue": "^1.0.2",
"@fortawesome/fontawesome-svg-core": "^6.4.0",
"@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",
"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": {

View 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();

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -1,4 +1,3 @@
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 { RuntimeSanityValidator } from './Modules/RuntimeSanityValidator'; import { RuntimeSanityValidator } from './Modules/RuntimeSanityValidator';
@@ -14,7 +13,6 @@ export class ApplicationBootstrapper implements IVueBootstrapper {
private static getAllBootstrappers(): IVueBootstrapper[] { private static getAllBootstrappers(): IVueBootstrapper[] {
return [ return [
new IconBootstrapper(),
new VueBootstrapper(), new VueBootstrapper(),
new RuntimeSanityValidator(), new RuntimeSanityValidator(),
new AppInitializationLogger(), new AppInitializationLogger(),

View File

@@ -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);
}
}

View File

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

View File

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

View File

@@ -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();

View File

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

View File

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

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

View 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;
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,29 +0,0 @@
import { ReadOnlyTreeNode } from '../../Node/TreeNode';
import { RenderQueueOrderer } from './RenderQueueOrderer';
export class CollapseDepthOrderer implements RenderQueueOrderer {
public orderNodes(nodes: Iterable<ReadOnlyTreeNode>): ReadOnlyTreeNode[] {
return orderNodes(nodes);
}
}
function orderNodes(nodes: Iterable<ReadOnlyTreeNode>): ReadOnlyTreeNode[] {
return [...nodes]
.sort((a, b) => {
const [aCollapseStatus, bCollapseStatus] = [isNodeCollapsed(a), isNodeCollapsed(b)];
if (aCollapseStatus !== bCollapseStatus) {
return (aCollapseStatus ? 1 : 0) - (bCollapseStatus ? 1 : 0);
}
return a.hierarchy.depthInTree - b.hierarchy.depthInTree;
});
}
function isNodeCollapsed(node: ReadOnlyTreeNode): boolean {
if (!node.state.current.isExpanded) {
return true;
}
if (node.hierarchy.parent) {
return isNodeCollapsed(node.hierarchy.parent);
}
return false;
}

View File

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

View File

@@ -9,7 +9,7 @@ import { NodeRenderingStrategy } from './Scheduling/NodeRenderingStrategy';
import { DelayScheduler } from './DelayScheduler'; import { DelayScheduler } from './DelayScheduler';
import { TimeoutDelayScheduler } from './Scheduling/TimeoutDelayScheduler'; import { TimeoutDelayScheduler } from './Scheduling/TimeoutDelayScheduler';
import { RenderQueueOrderer } from './Ordering/RenderQueueOrderer'; import { RenderQueueOrderer } from './Ordering/RenderQueueOrderer';
import { CollapseDepthOrderer } from './Ordering/CollapseDepthOrderer'; 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.
@@ -21,7 +21,7 @@ export function useGradualNodeRendering(
scheduler: DelayScheduler = new TimeoutDelayScheduler(), scheduler: DelayScheduler = new TimeoutDelayScheduler(),
initialBatchSize = 30, initialBatchSize = 30,
subsequentBatchSize = 5, subsequentBatchSize = 5,
orderer: RenderQueueOrderer = new CollapseDepthOrderer(), 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>());

View File

@@ -0,0 +1,40 @@
<template>
<div v-html="svgContent" class="inline-icon" />
</template>
<script lang="ts">
import {
defineComponent,
PropType,
inject,
} from 'vue';
import { useSvgLoader } from './UseSvgLoader';
import { IconName } from './IconName';
export default defineComponent({
props: {
icon: {
type: String as PropType<IconName>,
required: true,
},
},
setup(props) {
const useSvgLoaderHook = inject('useSvgLoaderHook', useSvgLoader);
const { svgContent } = useSvgLoaderHook(() => props.icon);
return { svgContent };
},
});
</script>
<style lang="scss" scoped>
.inline-icon {
display: inline-block;
::v-deep svg { // using ::v-deep because when v-html is used the content doesn't go through Vue's template compiler.
display: inline-block;
height: 1em;
overflow: visible;
vertical-align: -0.125em;
}
}
</style>

View File

@@ -0,0 +1,22 @@
export const IconNames = [
'magnifying-glass',
'copy',
'circle-info',
'user-secret',
'tag',
'github',
'face-smile',
'globe',
'desktop',
'xmark',
'battery-half',
'battery-full',
'folder',
'folder-open',
'left-right',
'file-arrow-down',
'floppy-disk',
'play',
] as const;
export type IconName = typeof IconNames[number];

View File

@@ -0,0 +1,92 @@
import {
WatchSource, readonly, ref, watch,
} from 'vue';
import { AsyncLazy } from '@/infrastructure/Threading/AsyncLazy';
import { IconName } from './IconName';
export function useSvgLoader(
iconWatcher: WatchSource<IconName>,
loaders: FileLoaders = RawSvgLoaders,
) {
const svgContent = ref<string>('');
watch(iconWatcher, async (iconName) => {
svgContent.value = await lazyLoadSvg(iconName, loaders);
}, { immediate: true });
return {
svgContent: readonly(svgContent),
};
}
export function clearIconCache() {
LazyIconCache.clear();
}
export type FileLoaders = Record<string, () => Promise<string>>;
const LazyIconCache = new Map<IconName, AsyncLazy<string>>();
async function lazyLoadSvg(name: IconName, loaders: FileLoaders): Promise<string> {
let iconLoader = LazyIconCache.get(name);
if (!iconLoader) {
iconLoader = new AsyncLazy<string>(() => loadSvg(name, loaders));
LazyIconCache.set(name, iconLoader);
}
const icon = await iconLoader.getValue();
return icon;
}
async function loadSvg(name: IconName, loaders: FileLoaders): Promise<string> {
const iconPath = `/assets/icons/${name}.svg`;
const loader = loaders[iconPath];
if (!loader) {
throw new Error(`missing icon for "${name}" in "${iconPath}"`);
}
const svgContent = await loader();
const modifiedContent = modifySvg(svgContent);
return modifiedContent;
}
const RawSvgLoaders = import.meta.glob('@/presentation/assets/icons/**/*.svg', {
as: 'raw', // This will load the SVG file content as a string.
/*
Using `eager: true` to preload all icons.
Pros:
- Speed: Icons are instantly accessible post-initial load.
Cons:
- Increased initial load time due to preloading of all icons.
- Increased bundle size.
*/
eager: false,
});
function modifySvg(svgSource: string): string {
const parser = new DOMParser();
const doc = parser.parseFromString(svgSource, 'image/svg+xml');
let svgRoot = doc.documentElement;
svgRoot = removeSvgComments(svgRoot);
svgRoot = fillSvgCurrentColor(svgRoot);
return new XMLSerializer()
.serializeToString(svgRoot);
}
function removeSvgComments(svgRoot: HTMLElement): HTMLElement {
const comments = Array.from(svgRoot.childNodes).filter(
(node) => node.nodeType === Node.COMMENT_NODE,
);
for (const comment of comments) {
svgRoot.removeChild(comment);
}
Array.from(svgRoot.children).forEach((child) => {
removeSvgComments(child as HTMLElement);
});
return svgRoot;
}
function fillSvgCurrentColor(svgRoot: HTMLElement): HTMLElement {
svgRoot.querySelectorAll('path').forEach((el: Element) => {
el.setAttribute('fill', 'currentColor');
});
return svgRoot;
}

View File

@@ -10,9 +10,7 @@
class="dialog__close-button" class="dialog__close-button"
@click="hide" @click="hide"
> >
<font-awesome-icon <AppIcon icon="xmark" />
:icon="['fas', 'times']"
/>
</div> </div>
</div> </div>
</ModalContainer> </ModalContainer>
@@ -20,11 +18,13 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, computed } from 'vue'; import { defineComponent, computed } from 'vue';
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
import ModalContainer from './ModalContainer.vue'; import ModalContainer from './ModalContainer.vue';
export default defineComponent({ export default defineComponent({
components: { components: {
ModalContainer, ModalContainer,
AppIcon,
}, },
emits: { emits: {
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */

View File

@@ -6,7 +6,7 @@
'container-supported': hasCurrentOsDesktopVersion, 'container-supported': hasCurrentOsDesktopVersion,
}"> }">
<span class="description"> <span class="description">
<font-awesome-icon class="description__icon" :icon="['fas', 'desktop']" /> <AppIcon class="description__icon" icon="desktop" />
<span class="description__text">For desktop:</span> <span class="description__text">For desktop:</span>
</span> </span>
<span class="urls"> <span class="urls">
@@ -21,6 +21,7 @@
import { defineComponent, inject } from 'vue'; import { defineComponent, inject } from 'vue';
import { OperatingSystem } from '@/domain/OperatingSystem'; import { OperatingSystem } from '@/domain/OperatingSystem';
import { InjectionKeys } from '@/presentation/injectionSymbols'; import { InjectionKeys } from '@/presentation/injectionSymbols';
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
import DownloadUrlListItem from './DownloadUrlListItem.vue'; import DownloadUrlListItem from './DownloadUrlListItem.vue';
const supportedOperativeSystems: readonly OperatingSystem[] = [ const supportedOperativeSystems: readonly OperatingSystem[] = [
@@ -32,6 +33,7 @@ const supportedOperativeSystems: readonly OperatingSystem[] = [
export default defineComponent({ export default defineComponent({
components: { components: {
DownloadUrlListItem, DownloadUrlListItem,
AppIcon,
}, },
setup() { setup() {
const { os: currentOs } = inject(InjectionKeys.useRuntimeEnvironment); const { os: currentOs } = inject(InjectionKeys.useRuntimeEnvironment);

View File

@@ -3,7 +3,7 @@
<div class="footer"> <div class="footer">
<div class="footer__section"> <div class="footer__section">
<span v-if="isDesktop" class="footer__section__item"> <span v-if="isDesktop" class="footer__section__item">
<font-awesome-icon class="icon" :icon="['fas', 'globe']" /> <AppIcon class="icon" icon="globe" />
<span> <span>
Online version at <a :href="homepageUrl" target="_blank" rel="noopener noreferrer">{{ homepageUrl }}</a> Online version at <a :href="homepageUrl" target="_blank" rel="noopener noreferrer">{{ homepageUrl }}</a>
</span> </span>
@@ -15,24 +15,24 @@
<div class="footer__section"> <div class="footer__section">
<div class="footer__section__item"> <div class="footer__section__item">
<a :href="feedbackUrl" target="_blank" rel="noopener noreferrer"> <a :href="feedbackUrl" target="_blank" rel="noopener noreferrer">
<font-awesome-icon class="icon" :icon="['far', 'smile']" /> <AppIcon class="icon" icon="face-smile" />
<span>Feedback</span> <span>Feedback</span>
</a> </a>
</div> </div>
<div class="footer__section__item"> <div class="footer__section__item">
<a :href="repositoryUrl" target="_blank" rel="noopener noreferrer"> <a :href="repositoryUrl" target="_blank" rel="noopener noreferrer">
<font-awesome-icon class="icon" :icon="['fab', 'github']" /> <AppIcon class="icon" icon="github" />
<span>Source Code</span> <span>Source Code</span>
</a> </a>
</div> </div>
<div class="footer__section__item"> <div class="footer__section__item">
<a :href="releaseUrl" target="_blank" rel="noopener noreferrer"> <a :href="releaseUrl" target="_blank" rel="noopener noreferrer">
<font-awesome-icon class="icon" :icon="['fas', 'tag']" /> <AppIcon class="icon" icon="tag" />
<span>v{{ version }}</span> <span>v{{ version }}</span>
</a> </a>
</div> </div>
<div class="footer__section__item"> <div class="footer__section__item">
<font-awesome-icon class="icon" :icon="['fas', 'user-secret']" /> <AppIcon class="icon" icon="user-secret" />
<a @click="showPrivacyDialog()">Privacy</a> <a @click="showPrivacyDialog()">Privacy</a>
</div> </div>
</div> </div>
@@ -48,6 +48,7 @@ import {
defineComponent, ref, computed, inject, defineComponent, ref, computed, inject,
} from 'vue'; } from 'vue';
import ModalDialog from '@/presentation/components/Shared/Modal/ModalDialog.vue'; import ModalDialog from '@/presentation/components/Shared/Modal/ModalDialog.vue';
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
import { InjectionKeys } from '@/presentation/injectionSymbols'; import { InjectionKeys } from '@/presentation/injectionSymbols';
import DownloadUrlList from './DownloadUrlList.vue'; import DownloadUrlList from './DownloadUrlList.vue';
import PrivacyPolicy from './PrivacyPolicy.vue'; import PrivacyPolicy from './PrivacyPolicy.vue';
@@ -57,6 +58,7 @@ export default defineComponent({
ModalDialog, ModalDialog,
PrivacyPolicy, PrivacyPolicy,
DownloadUrlList, DownloadUrlList,
AppIcon,
}, },
setup() { setup() {
const { info } = inject(InjectionKeys.useApplication); const { info } = inject(InjectionKeys.useApplication);

View File

@@ -22,6 +22,7 @@ export default defineComponent({
}; };
}, },
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -7,7 +7,7 @@
v-model="searchQuery" v-model="searchQuery"
> >
<div class="icon-wrapper"> <div class="icon-wrapper">
<font-awesome-icon :icon="['fas', 'search']" /> <AppIcon icon="magnifying-glass" />
</div> </div>
</div> </div>
</template> </template>
@@ -19,11 +19,13 @@ import {
} from 'vue'; } from 'vue';
import { InjectionKeys } from '@/presentation/injectionSymbols'; import { InjectionKeys } from '@/presentation/injectionSymbols';
import { NonCollapsing } from '@/presentation/components/Scripts/View/Cards/NonCollapsingDirective'; import { NonCollapsing } from '@/presentation/components/Scripts/View/Cards/NonCollapsingDirective';
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
import { IReadOnlyUserFilter } from '@/application/Context/State/Filter/IUserFilter'; import { IReadOnlyUserFilter } from '@/application/Context/State/Filter/IUserFilter';
import { IFilterResult } from '@/application/Context/State/Filter/IFilterResult'; import { IFilterResult } from '@/application/Context/State/Filter/IFilterResult';
import { IEventSubscription } from '@/infrastructure/Events/IEventSource'; import { IEventSubscription } from '@/infrastructure/Events/IEventSource';
export default defineComponent({ export default defineComponent({
components: { AppIcon },
directives: { directives: {
NonCollapsing, NonCollapsing,
}, },

View File

@@ -0,0 +1,20 @@
import {
describe, it, expect,
} from 'vitest';
import { IconNames } from '@/presentation/components/Shared/Icon/IconName';
import { useSvgLoader } from '@/presentation/components/Shared/Icon/UseSvgLoader';
import { waitForValueChange } from '@tests/shared/WaitForValueChange';
describe('useSvgLoader', () => {
describe('can load all SVGs', () => {
for (const iconName of IconNames) {
it(iconName, async () => {
// act
const { svgContent } = useSvgLoader(() => iconName);
await waitForValueChange(svgContent);
// assert
expect(svgContent.value).toBeTruthy();
});
}
});
});

View File

@@ -0,0 +1,17 @@
import { WatchSource, watch } from 'vue';
export function waitForValueChange<T>(valueWatcher: WatchSource<T>, timeoutMs = 2000): Promise<T> {
return new Promise<T>((resolve, reject) => {
const unwatch = watch(valueWatcher, (newValue, oldValue) => {
if (newValue !== oldValue) {
unwatch();
resolve(newValue);
}
}, { immediate: false });
setTimeout(() => {
unwatch();
reject(new Error('Timeout waiting for value to change.'));
}, timeoutMs);
});
}

View File

@@ -59,7 +59,7 @@ describe('Script', () => {
describe('level', () => { describe('level', () => {
it('cannot construct with invalid wrong value', () => { it('cannot construct with invalid wrong value', () => {
// arrange // arrange
const invalidValue: RecommendationLevel = 55; const invalidValue: RecommendationLevel = 55 as never;
const expectedError = 'invalid level'; const expectedError = 'invalid level';
// act // act
const construct = () => new ScriptBuilder() const construct = () => new ScriptBuilder()

View File

@@ -22,7 +22,7 @@ describe('ScriptingDefinition', () => {
}); });
it('throws if unknown', () => { it('throws if unknown', () => {
// arrange // arrange
const unknownValue: ScriptingLanguage = 666; const unknownValue: ScriptingLanguage = 666 as never;
const errorMessage = `unsupported language: ${unknownValue}`; const errorMessage = `unsupported language: ${unknownValue}`;
// act // act
const act = () => new ScriptingDefinitionBuilder() const act = () => new ScriptingDefinitionBuilder()

View File

@@ -1,93 +0,0 @@
import { describe, it, expect } from 'vitest';
import { TreeNode } from '@/presentation/components/Scripts/View/Tree/TreeView/Node/TreeNode';
import { CollapseDepthOrderer } from '@/presentation/components/Scripts/View/Tree/TreeView/Rendering/Ordering/CollapseDepthOrderer';
import { HierarchyAccessStub } from '@tests/unit/shared/Stubs/HierarchyAccessStub';
import { TreeNodeStateAccessStub } from '@tests/unit/shared/Stubs/TreeNodeStateAccessStub';
import { TreeNodeStateDescriptorStub } from '@tests/unit/shared/Stubs/TreeNodeStateDescriptorStub';
import { TreeNodeStub } from '@tests/unit/shared/Stubs/TreeNodeStub';
describe('CollapseDepthOrderer', () => {
describe('orderNodes', () => {
it('should order by collapsed state and then by depth in', () => {
// arrange
const node1 = createNodeForOrder({
isExpanded: false,
depthInTree: 1,
});
const node2 = createNodeForOrder({
isExpanded: true,
depthInTree: 2,
});
const node3 = createNodeForOrder({
isExpanded: false,
depthInTree: 3,
});
const node4 = createNodeForOrder({
isExpanded: false,
depthInTree: 4,
});
const nodes = [node1, node2, node3, node4];
const expectedOrder = [node2, node1, node3, node4];
// act
const orderer = new CollapseDepthOrderer();
const orderedNodes = orderer.orderNodes(nodes);
// assert
expect(orderedNodes.map((node) => node.id)).to.deep
.equal(expectedOrder.map((node) => node.id));
});
it('should handle parent collapsed state', () => {
// arrange
const collapsedParent = createNodeForOrder({
isExpanded: false,
depthInTree: 0,
});
const childWithCollapsedParent = createNodeForOrder({
isExpanded: true,
depthInTree: 1,
parent: collapsedParent,
});
const deepExpandedNode = createNodeForOrder({
isExpanded: true,
depthInTree: 3,
});
const nodes = [childWithCollapsedParent, collapsedParent, deepExpandedNode];
const expectedOrder = [
deepExpandedNode, // comes first due to collapse parent of child
collapsedParent,
childWithCollapsedParent,
];
// act
const orderer = new CollapseDepthOrderer();
const orderedNodes = orderer.orderNodes(nodes);
// assert
expect(orderedNodes.map((node) => node.id)).to.deep
.equal(expectedOrder.map((node) => node.id));
});
});
});
function createNodeForOrder(options: {
readonly isExpanded: boolean;
readonly depthInTree: number;
readonly parent?: TreeNode;
}): TreeNode {
return new TreeNodeStub()
.withId([
`isExpanded: ${options.isExpanded}`,
`depthInTree: ${options.depthInTree}`,
...(options.parent ? [`parent: ${options.parent.id}`] : []),
].join(', '))
.withState(
new TreeNodeStateAccessStub()
.withCurrent(
new TreeNodeStateDescriptorStub()
.withVisibility(true)
.withExpansion(options.isExpanded),
),
)
.withHierarchy(
new HierarchyAccessStub()
.withDepthInTree(options.depthInTree)
.withParent(options.parent),
);
}

View File

@@ -0,0 +1,165 @@
import { describe, it, expect } from 'vitest';
import { TreeNode } from '@/presentation/components/Scripts/View/Tree/TreeView/Node/TreeNode';
import { CollapsedParentOrderer } from '@/presentation/components/Scripts/View/Tree/TreeView/Rendering/Ordering/CollapsedParentOrderer';
import { HierarchyAccessStub } from '@tests/unit/shared/Stubs/HierarchyAccessStub';
import { TreeNodeStateAccessStub } from '@tests/unit/shared/Stubs/TreeNodeStateAccessStub';
import { TreeNodeStateDescriptorStub } from '@tests/unit/shared/Stubs/TreeNodeStateDescriptorStub';
import { TreeNodeStub } from '@tests/unit/shared/Stubs/TreeNodeStub';
describe('CollapsedParentOrderer', () => {
describe('orderNodes', () => {
const scenarios: ReadonlyArray<{
readonly description: string;
readonly nodes: TreeNode[];
readonly expectedOrderedNodes: TreeNode[];
}> = [
{
description: 'handles empty nodes list',
nodes: [],
expectedOrderedNodes: [],
},
(() => {
const expectedNode = createNodeForOrder({
isExpanded: false,
});
return {
description: 'handles single node list',
nodes: [expectedNode],
expectedOrderedNodes: [expectedNode],
};
})(),
(() => {
const node1 = createNodeForOrder({
isExpanded: false,
});
const node2 = createNodeForOrder({
isExpanded: true,
});
const node3 = createNodeForOrder({
isExpanded: true,
});
const node4 = createNodeForOrder({
isExpanded: false,
});
return {
description: 'orders by index ignoring self collapsed state',
nodes: [node1, node2, node3, node4],
expectedOrderedNodes: [node1, node2, node3, node4],
};
})(),
(() => {
const node1 = createNodeForOrder({
isExpanded: false,
parent: createNodeForOrder({ isExpanded: true }),
});
const node2 = createNodeForOrder({
isExpanded: true,
parent: createNodeForOrder({ isExpanded: true }),
});
const node3 = createNodeForOrder({
isExpanded: true,
parent: createNodeForOrder({ isExpanded: true }),
});
const node4 = createNodeForOrder({
isExpanded: false,
parent: createNodeForOrder({ isExpanded: true }),
});
return {
description: 'orders by index if all parents are expanded',
nodes: [node1, node2, node3, node4],
expectedOrderedNodes: [node1, node2, node3, node4],
};
})(),
(() => {
const node1 = createNodeForOrder({
isExpanded: true,
parent: createNodeForOrder({ isExpanded: false }),
});
const node2 = createNodeForOrder({
isExpanded: true,
parent: createNodeForOrder({ isExpanded: true }),
});
const node3 = createNodeForOrder({
isExpanded: true,
});
const node4 = createNodeForOrder({
isExpanded: true,
parent: createNodeForOrder({ isExpanded: false }),
});
return {
description: 'order by parent collapsed state then by index',
nodes: [node1, node2, node3, node4],
expectedOrderedNodes: [node2, node3, node1, node4],
};
})(),
(() => {
const collapsedNode = createNodeForOrder({
isExpanded: false,
});
const collapsedNodeChild = createNodeForOrder({
isExpanded: true,
parent: collapsedNode,
});
const collapsedNodeNestedChild = createNodeForOrder({
isExpanded: true,
parent: collapsedNodeChild,
});
const expandedNode = createNodeForOrder({
isExpanded: true,
});
const expandedNodeChild = createNodeForOrder({
isExpanded: true,
parent: expandedNode,
});
const expandedNodeNestedChild = createNodeForOrder({
isExpanded: true,
parent: expandedNodeChild,
});
return {
description: 'should handle deep parent collapsed state',
nodes: [
collapsedNode, collapsedNodeChild, collapsedNodeNestedChild,
expandedNode, expandedNodeChild, expandedNodeNestedChild,
],
expectedOrderedNodes: [
collapsedNode, expandedNode, expandedNodeChild,
expandedNodeNestedChild, collapsedNodeChild, collapsedNodeNestedChild,
],
};
})(),
];
scenarios.forEach(({ description, nodes, expectedOrderedNodes }) => {
it(description, () => {
// act
const orderer = new CollapsedParentOrderer();
const orderedNodes = orderer.orderNodes(nodes);
// assert
expect(orderedNodes.map((node) => node.id)).to.deep
.equal(expectedOrderedNodes.map((node) => node.id));
});
});
});
});
function createNodeForOrder(options: {
readonly isExpanded: boolean;
readonly parent?: TreeNode;
}): TreeNode {
return new TreeNodeStub()
.withId([
`isExpanded: ${options.isExpanded}`,
...(options.parent ? [`parent: ${options.parent.id}`] : []),
].join(', '))
.withState(
new TreeNodeStateAccessStub()
.withCurrent(
new TreeNodeStateDescriptorStub()
.withVisibility(true)
.withExpansion(options.isExpanded),
),
)
.withHierarchy(
new HierarchyAccessStub()
.withParent(options.parent),
);
}

View File

@@ -1,6 +1,7 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { TimeFunctions, TimeoutDelayScheduler } from '@/presentation/components/Scripts/View/Tree/TreeView/Rendering/Scheduling/TimeoutDelayScheduler'; import { TimeFunctions, TimeoutDelayScheduler } from '@/presentation/components/Scripts/View/Tree/TreeView/Rendering/Scheduling/TimeoutDelayScheduler';
import { StubWithObservableMethodCalls } from '@tests/unit/shared/Stubs/StubWithObservableMethodCalls'; import { StubWithObservableMethodCalls } from '@tests/unit/shared/Stubs/StubWithObservableMethodCalls';
import { createMockTimeout } from '@tests/unit/shared/Stubs/TimeoutStub';
describe('TimeoutDelayScheduler', () => { describe('TimeoutDelayScheduler', () => {
describe('scheduleNext', () => { describe('scheduleNext', () => {
@@ -56,7 +57,8 @@ describe('TimeoutDelayScheduler', () => {
expect(setTimeoutCalls.length).toBe(2); expect(setTimeoutCalls.length).toBe(2);
const clearTimeoutCalls = timerStub.callHistory.filter((c) => c.methodName === 'clearTimeout'); const clearTimeoutCalls = timerStub.callHistory.filter((c) => c.methodName === 'clearTimeout');
expect(clearTimeoutCalls.length).toBe(1); expect(clearTimeoutCalls.length).toBe(1);
const [actualId] = clearTimeoutCalls[0].args; const [timeout] = clearTimeoutCalls[0].args;
const actualId = Number(timeout);
expect(actualId).toBe(idOfFirstSetTimeoutCall); expect(actualId).toBe(idOfFirstSetTimeoutCall);
}); });
}); });
@@ -78,6 +80,7 @@ class TimeFunctionsStub
methodName: 'setTimeout', methodName: 'setTimeout',
args: [callback, delayInMs], args: [callback, delayInMs],
}); });
return this.callHistory.filter((c) => c.methodName === 'setTimeout').length as unknown as ReturnType<typeof setTimeout>; const id = this.callHistory.filter((c) => c.methodName === 'setTimeout').length;
return createMockTimeout(id);
} }
} }

View File

@@ -0,0 +1,100 @@
import {
describe, it, expect,
} from 'vitest';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
import { IconName } from '@/presentation/components/Shared/Icon/IconName';
import { UseSvgLoaderStub } from '@tests/unit/shared/Stubs/UseSvgLoaderStub';
describe('AppIcon.vue', () => {
it('renders the correct SVG content based on the icon prop', async () => {
// arrange
const expectedIconName: IconName = 'magnifying-glass';
const expectedIconContent = '<svg id="expected-svg" />';
const svgLoaderStub = new UseSvgLoaderStub();
svgLoaderStub.withSvgIcon(expectedIconName, expectedIconContent);
// act
const wrapper = mountComponent({
iconPropValue: expectedIconName,
loader: svgLoaderStub,
});
await nextTick();
// assert
const actualSvg = extractAndNormalizeSvg(wrapper.html());
const expectedSvg = extractAndNormalizeSvg(expectedIconContent);
expect(actualSvg).to.equal(
expectedSvg,
`Expected:\n\n${expectedSvg}\n\nActual:\n\n${actualSvg}`,
);
});
it('updates the SVG content when the icon prop changes', async () => {
// arrange
const initialIconName: IconName = 'magnifying-glass';
const updatedIconName: IconName = 'copy';
const updatedIconContent = '<svg id="updated-svg" />';
const svgLoaderStub = new UseSvgLoaderStub();
svgLoaderStub.withSvgIcon(initialIconName, '<svg id="initial-svg" />');
svgLoaderStub.withSvgIcon(updatedIconName, updatedIconContent);
// act
const wrapper = mountComponent({
iconPropValue: initialIconName,
loader: svgLoaderStub,
});
await wrapper.setProps({ icon: updatedIconName });
await nextTick();
// assert
const actualSvg = extractAndNormalizeSvg(wrapper.html());
const expectedSvg = extractAndNormalizeSvg(updatedIconContent);
expect(actualSvg).to.equal(
expectedSvg,
`Expected:\n\n${expectedSvg}\n\nActual:\n\n${actualSvg}`,
);
});
});
function mountComponent(options: {
readonly iconPropValue: IconName,
readonly loader: UseSvgLoaderStub,
}) {
return shallowMount(AppIcon, {
propsData: {
icon: options.iconPropValue,
},
provide: {
useSvgLoaderHook: options.loader.get(),
},
});
}
function extractAndNormalizeSvg(svgString: string): string {
const svg = extractSvg(svgString);
return normalizeSvg(svg);
}
function extractSvg(svgString: string): string {
const svgMatches = svgString.match(/<svg[\s\S]*?(<\/svg>|\/>)/g);
if (!svgMatches || svgMatches.length === 0) {
throw new Error(`No SVG found in: ${svgString}`);
}
if (svgMatches.length > 1) {
throw new Error(`Multiple SVGs found in: ${svgString}`);
}
const svgContent = svgMatches[0];
return svgContent;
}
function normalizeSvg(svgString: string): string {
return svgString
.replace(/\n/g, '') // Remove newlines
.replace(/\s+/g, ' ') // Replace all whitespace sequences with a single space
.replace(/> </g, '><') // Remove spaces between tags
.replace(/ <\//g, '</') // Remove spaces before closing tags
.replace(/\s+\/>/g, '/>') // Remove spaces before self-closing tag end
.replace(/<(\w+)([^>]*)><\/\1>/g, '<$1$2/>') // Convert to self-closing SVG tags
.trim(); // Remove leading and trailing spaces
}

View File

@@ -0,0 +1,160 @@
import {
describe, it, expect, beforeEach,
} from 'vitest';
import { ref } from 'vue';
import { IconName } from '@/presentation/components/Shared/Icon/IconName';
import { FileLoaders, clearIconCache, useSvgLoader } from '@/presentation/components/Shared/Icon/UseSvgLoader';
import { waitForValueChange } from '@tests/shared/WaitForValueChange';
describe('useSvgLoader', () => {
beforeEach(() => {
clearIconCache();
});
describe('SVG loading', () => {
it('renders initial SVG content based on icon name', async () => {
// arrange
const expectedIconName: IconName = 'magnifying-glass';
const expectedIconContent = '<svg id="expected-content"/>';
const { loaders, addIcon } = useSvgMock();
addIcon(expectedIconName, expectedIconContent);
// act
const { svgContent } = useSvgLoader(() => expectedIconName, loaders);
await waitForValueChange(svgContent);
// assert
expect(svgContent.value).to.equal(expectedIconContent);
});
it('updates SVG content when icon name changes', async () => {
// arrange
const initialIconName: IconName = 'magnifying-glass';
const iconName = ref<IconName>(initialIconName);
const initialIconContent = '<svg id="initial"/>';
const updatedIconName: IconName = 'copy';
const updatedIconContent = '<svg id="updated"/>';
const { addIcon, loaders } = useSvgMock();
addIcon(initialIconName, initialIconContent);
addIcon(updatedIconName, updatedIconContent);
// act
const { svgContent } = useSvgLoader(() => iconName.value, loaders);
await waitForValueChange(svgContent);
iconName.value = updatedIconName;
await waitForValueChange(svgContent);
// assert
expect(svgContent.value).to.equal(updatedIconContent);
});
it('lazy loads SVG icons and does not preload', async () => {
// arrange
const expectedIconName: IconName = 'magnifying-glass';
const unexpectedIconName: IconName = 'copy';
const { addIcon, getSvgFetchCount, loaders } = useSvgMock();
addIcon(expectedIconName);
addIcon(unexpectedIconName);
// act
const { svgContent } = useSvgLoader(() => expectedIconName, loaders);
await waitForValueChange(svgContent);
// assert
expect(getSvgFetchCount(expectedIconName)).to.equal(1);
expect(getSvgFetchCount(unexpectedIconName)).to.equal(0);
});
it('avoids loading same SVG content multiple times for concurrent calls', async () => {
// arrange
const expectedIconName: IconName = 'magnifying-glass';
const { addIcon, getSvgFetchCount, loaders } = useSvgMock();
addIcon(expectedIconName);
// act
const { svgContent: svgContent1 } = useSvgLoader(() => expectedIconName, loaders);
const { svgContent: svgContent2 } = useSvgLoader(() => expectedIconName, loaders);
await Promise.all([
waitForValueChange(svgContent1),
waitForValueChange(svgContent2),
]);
// assert
expect(getSvgFetchCount(expectedIconName)).to.equal(1);
});
});
describe('SVG content manipulation', () => {
it('sets path fill color to currentColor', async () => {
// arrange
const expectedIconName: IconName = 'magnifying-glass';
const { addIcon, loaders } = useSvgMock();
addIcon(expectedIconName, '<svg id="svg-with-paths"><path /><path /></svg>');
// act
const { svgContent } = useSvgLoader(() => expectedIconName, loaders);
await waitForValueChange(svgContent);
// assert
const svgElement = new DOMParser().parseFromString(svgContent.value, 'image/svg+xml');
const pathElements = Array.from(svgElement.querySelectorAll('path'));
expect(pathElements).to.have.lengthOf(2, svgContent.value);
const fillAttributeValues = pathElements.map((el: Element) => el.getAttribute('fill'));
expect(fillAttributeValues).to.have.members(['currentColor', 'currentColor']);
});
it('removes comments from loaded SVG', async () => {
// arrange
const commentLine = '<!-- This is a comment -->';
const expectedIconName: IconName = 'magnifying-glass';
const { addIcon, loaders } = useSvgMock();
addIcon(expectedIconName, `<svg>${commentLine}<path></path></svg>`);
// act
const { svgContent } = useSvgLoader(() => expectedIconName, loaders);
await waitForValueChange(svgContent);
// assert
expect(svgContent.value).not.to.include(commentLine);
});
});
describe('icon cache management', () => {
it('reloads SVG content after clearing cache', async () => {
// arrange
const expectedIconName: IconName = 'magnifying-glass';
const { addIcon, getSvgFetchCount, loaders } = useSvgMock();
addIcon(expectedIconName);
// act
const { svgContent } = useSvgLoader(() => expectedIconName, loaders);
await waitForValueChange(svgContent);
expect(getSvgFetchCount(expectedIconName)).to.equal(1);
clearIconCache();
const { svgContent: newSvgContent } = useSvgLoader(() => expectedIconName, loaders);
await waitForValueChange(newSvgContent);
// assert
expect(getSvgFetchCount(expectedIconName)).to.equal(2);
});
});
});
function useSvgMock() {
const ICON_PATH_PREFIX = '/assets/icons/';
function getPath(iconName: IconName) {
return `${ICON_PATH_PREFIX}${iconName}.svg`;
}
const svgFetchCount = {} as Record<IconName, number>;
const loaders = {} as FileLoaders;
function addIcon(iconName: IconName, svgContent = '<svg id="stub" />') {
const path = getPath(iconName);
svgFetchCount[iconName] = 0;
loaders[path] = () => {
svgFetchCount[iconName] += 1;
return Promise.resolve(svgContent);
};
}
function getSvgFetchCount(iconName: IconName): number {
return svgFetchCount[iconName];
}
return {
loaders,
getSvgFetchCount,
getPath,
addIcon,
};
}

View File

@@ -3,6 +3,7 @@ import { throttle, ITimer, TimeoutType } from '@/presentation/components/Shared/
import { EventSource } from '@/infrastructure/Events/EventSource'; import { EventSource } from '@/infrastructure/Events/EventSource';
import { IEventSubscription } from '@/infrastructure/Events/IEventSource'; import { IEventSubscription } from '@/infrastructure/Events/IEventSource';
import { getAbsentObjectTestCases, itEachAbsentObjectValue } from '@tests/unit/shared/TestCases/AbsentTests'; import { getAbsentObjectTestCases, itEachAbsentObjectValue } from '@tests/unit/shared/TestCases/AbsentTests';
import { createMockTimeout } from '@tests/unit/shared/Stubs/TimeoutStub';
describe('throttle', () => { describe('throttle', () => {
describe('validates parameters', () => { describe('validates parameters', () => {
@@ -153,7 +154,7 @@ class TimerMock implements ITimer {
}); });
this.subscriptions.push(subscription); this.subscriptions.push(subscription);
const id = this.subscriptions.length - 1; const id = this.subscriptions.length - 1;
return TimerMock.mockTimeout(id); return createMockTimeout(id);
} }
public clearTimeout(timeoutId: TimeoutType): void { public clearTimeout(timeoutId: TimeoutType): void {
@@ -172,15 +173,4 @@ class TimerMock implements ITimer {
this.currentTime = ms; this.currentTime = ms;
this.timeChanged.notify(this.currentTime); this.timeChanged.notify(this.currentTime);
} }
private static mockTimeout(subscriptionId: number): TimeoutType {
const throwNodeSpecificCode = () => { throw new Error('node specific code'); };
return {
[Symbol.toPrimitive]: () => subscriptionId,
hasRef: throwNodeSpecificCode,
refresh: throwNodeSpecificCode,
ref: throwNodeSpecificCode,
unref: throwNodeSpecificCode,
};
}
} }

View File

@@ -4,12 +4,13 @@ export async function expectThrowsAsync(
method: () => Promise<unknown>, method: () => Promise<unknown>,
errorMessage: string, errorMessage: string,
) { ) {
let error: Error; let error: Error | undefined;
try { try {
await method(); await method();
} catch (err) { } catch (err) {
error = err; error = err;
} }
expect(error).toBeDefined();
expect(error).to.be.an(Error.name); expect(error).to.be.an(Error.name);
if (errorMessage) { if (errorMessage) {
expect(error.message).to.equal(errorMessage); expect(error.message).to.equal(errorMessage);

View File

@@ -0,0 +1,13 @@
export function createMockTimeout(timerId: number): ReturnType<typeof setTimeout> {
const throwErrorForNodeOperation = () => {
throw new Error('node specific operation was called');
};
return {
[Symbol.toPrimitive]: () => timerId,
[Symbol.dispose]: throwErrorForNodeOperation, // Cancels the timeout in node
hasRef: throwErrorForNodeOperation,
refresh: throwErrorForNodeOperation,
ref: throwErrorForNodeOperation,
unref: throwErrorForNodeOperation,
};
}

View File

@@ -0,0 +1,31 @@
import {
WatchSource, computed, ref, watch,
} from 'vue';
import { IconName } from '@/presentation/components/Shared/Icon/IconName';
import { useSvgLoader } from '@/presentation/components/Shared/Icon/UseSvgLoader';
export class UseSvgLoaderStub {
private readonly icons = new Map<IconName, string>();
public withSvgIcon(name: IconName, svgContent: string): this {
this.icons.set(name, svgContent);
return this;
}
public get(): typeof useSvgLoader {
return (iconWatcher: WatchSource<IconName>) => {
const iconName = ref<IconName | undefined>();
watch(iconWatcher, (newIconName) => {
iconName.value = newIconName;
}, { immediate: true });
return {
svgContent: computed<string>(() => {
if (!iconName.value) {
return '';
}
return this.icons.get(iconName.value) || '';
}),
};
};
}
}

18
tst.sh Executable file
View File

@@ -0,0 +1,18 @@
echo '--- Disable Location-Based Suggestions for Siri'
if $(csrutil status | grep 'enabled'); then
echo 'SIP must be disabled'
exit 1
fi
original_file='/System/Library/LaunchAgents/com.apple.parsecd.plist'
backup_file="/Users/tst/aq.disabled"
if [ -f "$original_file" ]; then
sudo launchctl unload -w "$original_file" 2> /dev/null
if sudo mv "$original_file" "$backup_file"; then
echo 'Disabled successfully'
else
>&2 echo 'Failed to disable'
fi
else
echo 'Already disabled'
fi