Compare commits

..

11 Commits

Author SHA1 Message Date
undergroundwires
f9a54c7e68 Fix Colima builds failing 2024-05-20 17:32:21 +02:00
undergroundwires
292362135d Centralize and optimize ResizeObserver usage
This commit addresses failures in end-to-end tests that occurred due to
`ResizeObserver` loop limit exceptions.

These errors were triggered by Vue dependency upgrades in the commit
aae5434451.
The errors had the following message:
> `ResizeObserver loop completed with undelivered notifications`

This error happens when there are too many observations and the observer
is not able to deliver all observations within a single animation frame.
See: WICG/resize-observer#38

his commit resolves the issue by controlling how many observations are
delivered per animation frame and limiting it to only one.

It improves performance by reducing layout trashing, improving frame
rates, and managing resources more effectively.

Changes:

- Introduce an animation frame control to manage observations more
  efficiently.
- Centralized `ResizeObserver` management within the `UseResizeObserver`
  hook to improve consistency and reuse across the application.
2024-05-20 10:36:49 +02:00
undergroundwires
aae5434451 Bump Vue to latest and fix universal selector CSS
This commit updates the Vue package from v3.4.21 to v3.4.27.

This version change addressed styling issues introduced by changes in
CSS universal selector handling in Vue 3.4.22.
The change that has caused this:
- vuejs/core#10551
- vuejs/core#10548
- vuejs/core@54a6afa75a

This commit fixes two main issues that this has led to:

1. Universal CSS selector causing 'Revert' buttons to stretch and
   truncate incorrectly.
   This is fixed by modifying selectors to apply styles more
   specifically, maintaining correct display of toggle buttons.
2. Universal `*` selector that's used to understand parent HTML
   structure causing information tooltip icons to be misaligned.
   This is fixed by replacing `*` with a new `InfoTooltipWrapper`
   component, which manages layout concerns more explicitly and
   maintainably.
2024-05-19 15:02:38 +02:00
undergroundwires
2390530d92 ci/cd: fix quality checks not running on all OSes
Previously, quality checks were mistakenly configured to run only on
Ubuntu.

This commit modifies the CI/CD workflow to use the matrix strategy,
allowing the quality checks to be executed on macOS, Ubuntu and Windows.

Additionally, this update resolves the `MD034/no-bare-urls Bare URL
used` linting error that surfaced when testing on Windows.
2024-05-18 13:14:05 +02:00
undergroundwires
9ab3ff75b0 Migrate to GitHub issue forms
This commit transitions from HTML-based issue templates to GitHub issue
forms, enhancing user experience by preventing accidental submissions
with comment-like metadata. This change makes submitting issues more
intuitive and reduces the chances of user errors (such as #355).

Key change include:

- Use a friendlier tone in the templates.
- Detail examples and descriptions to guide users more effectively.
- Rename templates for improved clarity and easy navigation.
- Add "a note from the maintainer".
- Include a direct link for donations to support the project.
2024-05-17 17:52:14 +02:00
undergroundwires
d25c4e8c81 Add support for macOS universal binary #348, #362
This commit introduces a universal binary format in the distributed MDG
files for macOS, improving support for both Apple Silicon (ARM) and
Intel (x64) architectures.

It uses `electron-builder` to package both architectures into a single
executable, ensuring the application can natively on any macOS hardware
without depending on the GitHub runners' architecture. It fixes the
issue related to prior releases that supported only the architecture of
the build environment itself, which is subject to change.

Changes:

- Update DMG distribution to include both ARM64 and x64 architectures.
- Enhance system requirements documentation to reflect support for both
  architectures.
- Modify CI/CD workflows to check desktop runtime errors for both ARM64
  and x64 versions on macOS.

Resolves:

- Issue #348: Initial request for Apple Silicon support.
- Issue #362: Correction of distribution limited to ARM64 in release
  0.13.3.

`electron-builder` support:
- electron-userland/electron-builder#5475
- electron-userland/electron-builder#5689
- electron-userland/electron-builder#5426
2024-05-16 10:53:55 +02:00
undergroundwires
4a7efa27c8 Fix e2e test failing on Windows
The recent addition of revert logic in first visible card on Windows
(Privacy Cleanup) in cec0b4b, introduced an issue where end-to-end (e2e)
started failing due to the handling of hidden elements.

This commit improves the test to correctly handle the hidden card
scenario, explicitly filtering visible elements to ensure that only
visible elements are handled.
2024-05-15 09:03:18 +02:00
undergroundwires
cec0b4b4f6 win: standardize registry edit + delete on revert
This commit standardizes the management of registry keys and their
corresponding revert on delete action across all scripts using
`SetRegistryValue` function.

It improves script reliability, addresses previous errors, and corrects
the revert actions to match the default OS state when not explicitly set
by the OS.

Key changes:

- Use SetRegistryValue for uniformity.
- Remove error messages for non-existent registry keys, recognizing them
  as expected states rather than errors.
- Add missing revert actions to scripts where they were absent.
- Correct the revert logic in existing scripts to match the default OS
  configurations, particularly when the OS does not set a default value.
- Update documentation about default OS state for the related scripts.

This change improves maintainability by centralizing and standardizing
registry interactions, reducing the risk of errors and inconsistencies
in script behaviors.
2024-05-14 12:41:20 +02:00
undergroundwires
a1922c50c1 ci/cd: fix recent Docker build failures on macOS
The GitHub workflow for testing Docker builds on macOS was consistently
failing. This commit downgrades the macOS version used for Docker tests
to `macos-13`, which is the latest Intel-based macOS runner, instead of
the ARM-based `macos-14` which `macos-latest` points to.

This change is necessary because the hypervisor framework required for
Docker is not supported on the ARM-based macOS runners provided by
GitHub. This issue was causing failures when attempting to run Colima
with QEMU using `-accel hvf`, which is unsupported on these runners.
Switching to an Intel-based runner resolves this issue.

Related issues:
- actions/runner-images#9460
- actions/runner-images#9741
- abiosoft/colima#1023
2024-05-13 11:09:58 +02:00
undergroundwires
870120bc13 Add specific empty function name compiler error
This commit adds checks to rjeect functions with empty or whitespace
names. The compiler throws a specific errror when it encounters a
function data object lacking a proper name.

This provides early detection and clear feedback on invalid function
definitions, helping in faster debugging and ensuring script integrity
in the compilation process.

The enhancement aims to provide early detection and clear feedback
on invalid function definitions, aiding in faster debugging and
ensuring script integrity in the compilation process.

when it encounters a function data object lacking a proper name.
It covers scenarios where the function name might be an empty string,
undefined, or solely consist of whitespace.
2024-05-12 12:32:42 +02:00
undergroundwires-bot
f38cf73485 ⬆️ bump everywhere to 0.13.3 2024-05-11 09:58:21 +00:00
42 changed files with 4211 additions and 1616 deletions

View File

@@ -1,57 +0,0 @@
---
name: Bug report (script bug or unexpected script behavior)
about: Create a bug report for generated scripts to help privacy.sexy improve
labels: bug
title: '[BUG]: '
---
<!--
Thank you for reporting an issue with generated script(s).
Please fill in as much of the template below as you're able.
As a small open source project with small community, it can sometimes take a long time for issues to be addressed so please be patient.
-->
### Description
<!--
A clear and concise description of what the bug is.
-->
### OS
<!--
Which OS are you using? What version of OS you were using?
On Windows: Open "Start button" > "Settings" > "System" > "About".
On macOS: Open "Apple menu (top left corner)" > "About This Mac".
On Linux: Open terminal > type: lsb_release -a > copy paste the result.
-->
### Reproduction steps
<!--
How can the bug be recreated?
It's the most important information in the bug report. Bugs that cannot be reproduced cannot be fixed and verified.
E.g.
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
-->
### Scripts
<!--
If applicable, please attach the generated privacy.sexy file instead of copy pasting which becomes too long.
-->
### Screenshots
<!--
If applicable, add screenshots to help explain your problem.
-->
### Additional information
<!--
If applicable, add any other context about the problem here.
-->

View File

@@ -0,0 +1,114 @@
name: "Bug Report: Script Issues"
description: 🐛 Report issues with generated scripts to enhance privacy.sexy
labels: [ 'bug' ]
title: '[Bug]: '
body:
-
type: markdown
attributes:
value: |-
Thank you for contributing to privacy.sexy and guiding our direction! 🌟
Please complete as much of the form below as possible.
Your feedback is valuable, even if you can't provide all details.
-
type: textarea
attributes:
label: Description
description: A clear and concise description of what the bug is.
placeholder: >-
For example: "After running the cleanup script, music playback stopped functioning."
validations:
required: true
-
type: textarea
attributes:
label: How can the bug be recreated?
description: |-
This is the most important information in the bug report.
Bugs that cannot be reproduced cannot be fixed or verified.
placeholder: |-
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
validations:
required: true
-
type: textarea
attributes:
label: Operating system
description: |-
Please specify your operating system and its version.
- On Windows: Open "Start button" > "Settings" > "System" > "About".
- On macOS: Open "Apple menu (top left corner)" > "About This Mac".
- On Linux: Open terminal > type: lsb_release -a > copy paste the result.
placeholder: >-
For example: "Windows 11 Pro 22H3"
validations:
required: false
-
type: textarea
attributes:
label: Script file
description: |-
If applicable, share the generated privacy.sexy file.
GitHub may restrict script file attachments.
Upload your script file to a service like [GitHub Gist](https://gist.github.com/) and share the link here.
If you used the desktop version to run the script, it is already stored on your system.
See the [documentation to locate it](https://github.com/undergroundwires/privacy.sexy/blob/master/docs/desktop/desktop-vs-web-features.md#secure-script-executionstorage).
> **💡 Tip:** You can attach script files by dragging them into this area.
placeholder: |-
Attach the script, or post GitHub Gist link.
For example: https://gist.github.com/privacysexy-forks/6d85ad8ca27acc8c6a5417d4af28c9b6.
validations:
required: false
-
type: textarea
attributes:
label: Screenshots
description: |-
If applicable, add screenshots to help explain your problem.
> **💡 Tip:** You can attach screenshots by clicking this area to highlight it and then pasting them or dragging files in.
placeholder: Attach screenshots here or link to image hosting.
validations:
required: false
-
type: textarea
attributes:
label: Additional information
description: |-
If applicable, add any other context about the problem here.
Helpful information includes:
- Application logs (desktop version only), see: [how to find application logs](https://github.com/undergroundwires/privacy.sexy/blob/master/docs/desktop/desktop-vs-web-features.md#logging).
- Terminal output
- Proposed solutions
- Other related context such as related issues, software behavior, etc.
> **💡 Tip:** You can attach log files by dragging them into this area.
placeholder: >-
For example: "Here are the logs I get from the privacy.sexy 0.13.2 desktop application: ..."
validations:
required: false
-
type: markdown
attributes:
value: |-
---
**✉️ A friendly note from the maintainer:**
> [!NOTE]
> We are a small open-source project with a small community.
> It can sometimes take a long time for issues to be addressed, so please be patient.
> Consider [donating](https://undergroundwires.dev/donate) to keep privacy.sexy alive and improve support ❤️.
> But your issue will eventually get attention regardless.
> <p align="right">@undergroundwires</p>
---

View File

@@ -0,0 +1,104 @@
name: "Bug Report: General"
description: 🐛 Report general issues to enhance privacy.sexy
labels: [ 'bug' ]
title: '[Bug]: '
body:
-
type: markdown
attributes:
value: |-
Thank you for contributing to privacy.sexy and guiding our direction! 🌟
Please complete as much of the form below as possible.
Your feedback is valuable, even if you can't provide all details.
-
type: textarea
attributes:
label: Description
description: Provide a clear and concise description of the issue.
placeholder: >-
For example: "I cannot select any scripts."
validations:
required: true
-
type: textarea
attributes:
label: Reproduction steps
description: |-
This is the most important information in the bug report.
Bugs that cannot be reproduced cannot be fixed or verified.
placeholder: |-
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
validations:
required: true
-
type: textarea
attributes:
label: Expected behavior
description: Describe what you expected to happen when the error occurred.
placeholder: >-
For example: "I expected the settings menu to open smoothly without crashing.".
validations:
required: true
-
type: textarea
attributes:
label: Screenshots
description: |-
If applicable, add screenshots to help explain your problem.
> **💡 Tip:** You can attach screenshots by clicking this area to highlight it and then pasting them or dragging files in.
placeholder: >-
Attach screenshots here or link to image hosting.
validations:
required: false
-
type: textarea
attributes:
label: privacy.sexy environment details
description: |-
If applicable, mention how you were using privacy.sexy when the bug occurred:
- Web (on which operating system and browser?)
- Or desktop (Windows, macOS, or Linux?)
placeholder: >-
For example: "The web version on Edge browser on Windows 11 23H2."
validations:
required: false
-
type: textarea
attributes:
label: Additional context
description: |-
If applicable, add any other context about the problem here.
Helpful information includes:
- Application logs (desktop version only), see: [how to find application logs](https://github.com/undergroundwires/privacy.sexy/blob/master/docs/desktop/desktop-vs-web-features.md#logging).
- Terminal output
- Proposed solutions
- Other related context such as related issues, software behavior, etc.
> **💡 Tip:** You can attach log files by dragging them into this area.
placeholder: >-
For example: "Here are the logs I get from the privacy.sexy 0.13.2 desktop application: ..."
validations:
required: false
-
type: markdown
attributes:
value: |-
---
**✉️ A friendly note from the maintainer:**
> [!NOTE]
> We are a small open-source project with a small community.
> It can sometimes take a long time for issues to be addressed, so please be patient.
> Consider [donating](https://undergroundwires.dev/donate) to keep privacy.sexy alive and improve support ❤️.
> But your issue will eventually get attention regardless.
> <p align="right">@undergroundwires</p>
---

View File

@@ -1,55 +0,0 @@
---
name: Bug report (unrelated to generated scripts)
about: Create a bug report that's not related to generated scripts to help privacy.sexy improve
labels: bug
title: '[BUG]: '
---
<!--
Thank you for reporting an issue.
Please fill in as much of the template below as you're able.
As a small open source project with small community, it can sometimes take a long time for issues to be addressed so please be patient.
-->
### Description
<!--
A clear and concise description of what the bug is.
-->
### Reproduction steps
<!--
It's the most important information in the bug report. Bugs that cannot be reproduced cannot be fixed and verified.
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
-->
### Expected behavior
<!--
A clear and concise description of what you expected to happen.
-->
### Screenshots
<!--
If applicable, add screenshots to help explain your problem.
-->
### Distribution
<!--
If applicable, mention how you were using privacy.sexy when the bug was encountered:
- Web (on Desktop or mobile?)
- Or desktop (Windows, macOS or Linux?)
-->
### Additional context
<!--
If applicable, add any other context about the problem here.
-->

View File

@@ -1,36 +0,0 @@
---
name: Feature request
about: Suggest an idea for privacy.sexy
labels: enhancement
---
<!--
Thank you for suggesting an idea to improve privacy better 🤗.
Please fill in as much of the template below as you're able.
-->
### Problem description
<!--
What are we trying to solve?
Please add a clear and concise description of the problem you are seeking to solve with this feature request.
E.g. I'm always frustrated when [...]
-->
### Proposed solution
<!--
Describe the solution you'd like in a clear and concise manner.
-->
### Alternatives considered
<!--
A clear and concise description of any alternative solutions or features you've considered.
-->
### Additional information
<!--
If applicable, add any other context or screenshots about the feature request here.
-->

View File

@@ -0,0 +1,73 @@
name: "Suggestion: Feature"
description: 💡 Suggest new ideas to enhance privacy.sexy
labels: [ 'enhancement' ]
title: '[Feature]: '
body:
-
type: markdown
attributes:
value: |-
Thank you for contributing to privacy.sexy and guiding our direction! 🌟
Please complete as much of the form below as possible.
Your feedback is valuable, even if you can't provide all details.
-
type: textarea
attributes:
label: Problem statement
description: |-
What are we trying to solve?
Please add a clear and concise description of the problem you are seeking to solve with this feature request.
placeholder: >-
For example: "Every time I use the app, I struggle with..."
validations:
required: true
-
type: textarea
attributes:
label: Proposed solution
description: |-
Describe the solution you'd like in a clear and concise manner.
placeholder: >-
For example: "It would be great if the app could..."
validations:
required: true
-
type: textarea
attributes:
label: Alternatives considered
description: |-
Have you considered any alternative solutions or features?
Different perspectives can inspire new ideas.
placeholder: >-
For example: "We could also solve it by...".
validations:
required: false
-
type: textarea
attributes:
label: Additional information
description: |-
If applicable, add any other context or screenshots about the feature request here.
> **💡 Tip:** You can attach files or screenshots by dragging them into this area.
placeholder: >-
For example: "Challenges can be ..., but I'm unsure about ..., here is some documentation about it: ..."
validations:
required: false
-
type: markdown
attributes:
value: |-
---
**✉️ A friendly note from the maintainer:**
> [!NOTE]
> We are a small open-source project with a small community.
> It can sometimes take a long time for issues to be addressed, so please be patient.
> Consider [donating](https://undergroundwires.dev/donate) to keep privacy.sexy alive and improve support ❤️.
> But your issue will eventually get attention regardless.
> <p align="right">@undergroundwires</p>
---

View File

@@ -1,60 +0,0 @@
---
name: New script suggestion
about: Suggest a new script for privacy.sexy
labels: enhancement
---
<!--
Thank you for contributing to privacy.sexy! 🌟
For guidance, see our script guidelines: https://github.com/undergroundwires/privacy.sexy/blob/master/docs/script-guidelines.md.
Consider submitting a PR for faster implementation: https://github.com/undergroundwires/privacy.sexy/blob/master/CONTRIBUTING.md#extend-scripts.
-->
### Operating system
<!--
Specify the OS: Windows, macOS, or Linux.
-->
### Name
<!--
Suggest a name for the script.
Naming conventions: https://github.com/undergroundwires/privacy.sexy/blob/master/docs/script-guidelines.md#name.
-->
### Code
<!--
Provide or explain the code to execute when the script runs.
Code guidelines: https://github.com/undergroundwires/privacy.sexy/blob/master/docs/script-guidelines.md#code.
-->
### Revert code
<!--
Include code to revert changes to the default state.
Leave blank for non-reversible scripts.
-->
### Category
<!--
Suggest a category for the script.
If unsure, leave blank for maintainers to decide.
-->
### Recommendation level
<!--
Suggest a recommendation level: STANDARD (non-breaking), STRICT (limits functionality), or NONE (for advanced users).
If unsure, leave blank for maintainers to decide.
-->
### Documentation/References
<!--
Provide any relevant documentation or references.
Prefer high-quality sources such as vendor documentation.
Documentation guidelines: https://github.com/undergroundwires/privacy.sexy/blob/master/docs/script-guidelines.md#documentation.
-->

View File

@@ -0,0 +1,133 @@
name: "Suggestion: New Script"
description: 💡 Suggest new scripts to enhance privacy.sexy
labels: [ 'enhancement' ]
title: '[New script]: '
body:
-
type: markdown
attributes:
value: |-
Thank you for contributing to privacy.sexy and guiding our direction! 🌟
Please complete as much of the form below as possible.
Your feedback is valuable, even if you can't provide all details.
For guidance, see our [script guidelines](https://github.com/undergroundwires/privacy.sexy/blob/master/docs/script-guidelines.md).
Consider submitting a PR to get your script added more quickly: (see [CONTRIBUTING.md](https://github.com/undergroundwires/privacy.sexy/blob/master/CONTRIBUTING.md#extend-scripts))
-
type: dropdown
attributes:
label: Operating system
description: Which operating system will the new script configure?
options:
- macOS
- Windows
- Linux
- All of them
validations:
required: false
-
type: textarea
attributes:
label: Name of the script
description: |-
Suggest a name for the script that clearly describes its function.
See [script naming conventions](https://github.com/undergroundwires/privacy.sexy/blob/master/docs/script-guidelines.md#name) for best practices.
placeholder: E.g, "Disable error data submission"
validations:
required: true
-
type: textarea
attributes:
label: Documentation/References
description: |-
Provide any relevant documentation or references.
Prefer high-quality sources such as vendor documentation.
See [documentation guidelines](https://github.com/undergroundwires/privacy.sexy/blob/master/docs/script-guidelines.md#documentation) for best practices.
placeholder: >-
For example: "This script will disable the error data submission, see https://microsoft.com/...".
validations:
required: true
-
type: textarea
attributes:
label: Code
description: |-
If possible, provide or explain the code that the script should execute.
See [script code guidelines](https://github.com/undergroundwires/privacy.sexy/blob/master/docs/script-guidelines.md#code).
placeholder: |-
For example: "Set registry key like this `reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection" /v "AllowTelemetry" /t "REG_DWORD" /d "1"`".
validations:
required: false
-
type: textarea
attributes:
label: Revert code
description: |-
If applicable, provide revert code to restore the changes made by the script.
The revert code restores changes to their default state before script execution.
Leave blank for non-reversible scripts.
placeholder: |-
For example: "Revert to operating system default like this `reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection" /v "AllowTelemetry" /t "REG_DWORD" /d "0"`".
validations:
required: false
-
type: textarea
attributes:
label: Suggested category
description: |-
Suggest a category for the script.
If unsure, leave blank for maintainers to decide.
placeholder: >-
For example: "Privacy Cleanup > Clear system logs"
-
type: dropdown
attributes:
label: Recommendation level
description: |-
Suggest a recommendation level for the script:
- **Standard**: Recommended for most users without side-effects.
- **Strict**: Provides improved privacy at the cost of some functionality.
- **None**: For advanced users or specific needs.
If unsure, leave blank for maintainers to decide.
options:
- Standard
- Strict
- None (do not recommend)
validations:
required: false
-
type: textarea
attributes:
label: Additional information
description: |-
If applicable, add any other context or screenshots about the script request here.
> **💡 Tip:** You can attach additional documents or screenshots by dragging them into this area or pasting directly.
placeholder: >-
For example: "Challenges can be ..., but I am unsure about ..."
validations:
required: false
-
type: markdown
attributes:
value: |-
---
**✉️ A friendly note from the maintainer:**
> [!NOTE]
> We are a small open-source project with a small community.
> It can sometimes take a long time for issues to be addressed, so please be patient.
> Consider [donating](https://undergroundwires.dev/donate) to keep privacy.sexy alive and improve support ❤️.
> But your issue will eventually get attention regardless.
> <p align="right">@undergroundwires</p>
---

View File

@@ -1 +1,7 @@
blank_issues_enabled: true
# This file must be named `config.yml`. GitHub does not recognize the file if it is named `config.yaml`.
blank_issues_enabled: true
contact_links:
- name: Donate
url: https://undergroundwires.dev/donate/
about: ❤️ Donate to support the free software you love to keep it alive.
# A separate link for reporting vulnerabilities is not included here because GitHub generates it automatically.

View File

@@ -72,20 +72,35 @@ jobs:
build-docker:
strategy:
matrix:
os: [ macos, ubuntu ] # Windows runners do not support Linux containers
os:
- macos-13 # Downgraded due to lack of nested virtualization support in ARM-based runners (See: actions/runner-images#9460, actions/runner-images#9741, abiosoft/colima#1023)
- ubuntu-latest
# - windows-latest # Windows runners do not support Linux containers
fail-fast: false # Allows to see results from other combinations
runs-on: ${{ matrix.os }}-latest
runs-on: ${{ matrix.os }}
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Install Docker on macOS
if: matrix.os == 'macos' # macOS runner is missing Docker
if: contains(matrix.os, 'macos') # macOS runner is missing Docker
run: |-
# Verify Intel-based macOS
arch=$(uname -m)
case "$arch" in
i386|x86_64)
echo "Supported architecture: $arch"
;;
*)
>&2 echo 'The macOS is not running on a supported Intel architecture. Virtualization is not supported.'
exit 1
;;
esac
# Install Docker
brew install docker
# Docker on macOS misses daemon due to licensing, so install colima as runtime
# Docker on macOS does not include the Docker daemon due to licensing issues.
# Install Colima to use as the Docker runtime.
brew install colima
# Start the daemon
colima start

View File

@@ -9,9 +9,13 @@ jobs:
run-check:
strategy:
matrix:
os: [ macos, ubuntu, windows ]
os:
- macos-latest # Latest Apple silicon (ARM64)
- macos-12 # Latest Intel-based (x86-64)
- ubuntu-latest
- windows-latest
fail-fast: false # Allows to see results from other combinations
runs-on: ${{ matrix.os }}-latest
runs-on: ${{ matrix.os }}
steps:
-
name: Checkout
@@ -24,7 +28,7 @@ jobs:
uses: ./.github/actions/npm-install-dependencies
-
name: Configure Ubuntu
if: matrix.os == 'ubuntu'
if: contains(matrix.os, 'ubuntu') # macOS runner is missing Docker
shell: bash
run: |-
sudo apt update

View File

@@ -4,7 +4,7 @@ on: [ push, pull_request ]
jobs:
lint:
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}-latest
strategy:
matrix:
lint-command:

View File

@@ -1,5 +1,32 @@
# Changelog
## 0.13.3 (2024-05-11)
* win: organize and document network disablement | [2eed6f4](https://github.com/undergroundwires/privacy.sexy/commit/2eed6f4afb6cf85fdc1d6acb808f82405a35cafd)
* win: improve disabling SMBv1 protocol | [f584fab](https://github.com/undergroundwires/privacy.sexy/commit/f584fabb50c7de70ba43751d721af94d8fa2fa8a)
* win: improve disabling insecure renegotiations | [f261ab4](https://github.com/undergroundwires/privacy.sexy/commit/f261ab4cd9a53e31325e5c6da9129542971fe84b)
* win: doc, improve, encourage cipher disabling | [8b224ee](https://github.com/undergroundwires/privacy.sexy/commit/8b224eefe71be6a556a1085d8fe20dbd4b889430)
* ci/cd: add check for TODO comments | [4e21f05](https://github.com/undergroundwires/privacy.sexy/commit/4e21f05031d6cc90cda684bd598bec4735f8103b)
* win: improve 'Snipping Tool' removal #343 | [e18907c](https://github.com/undergroundwires/privacy.sexy/commit/e18907ca91e483255b44d14d7d923d7eef92afbd)
* ci/cd: lint Python scripts using `pylint` | [23bac0f](https://github.com/undergroundwires/privacy.sexy/commit/23bac0fc76ad697abb34f3fb327df5cdeb40286a)
* win: improve disabling insecure hashes #131 | [d19dde6](https://github.com/undergroundwires/privacy.sexy/commit/d19dde603ddac47022ee2e0ea865d53857560c26)
* Add system requirements documentation #134 | [0fc2ffc](https://github.com/undergroundwires/privacy.sexy/commit/0fc2ffc1ea36a9248c6a92da85a29f7b04b33796)
* win, linux, mac: fix various typos #349 | [694bf1a](https://github.com/undergroundwires/privacy.sexy/commit/694bf1a74d935531d7cd46891823af1fa58c3c8c)
* Fix script cancellation with new dialog on Linux | [8c17396](https://github.com/undergroundwires/privacy.sexy/commit/8c173962857a39dc0c9e5886cb2af4937e6618e7)
* win: improve disabling protocols | [4ef16ce](https://github.com/undergroundwires/privacy.sexy/commit/4ef16cea56789120cd041412d86b5577cccf0725)
* win: fix Copilot by excluding `r.bing.com` #329 | [66a5688](https://github.com/undergroundwires/privacy.sexy/commit/66a56888a4b3ead1a6bfef0feffa0218535701fe)
* Fix blank window on load on desktop version #348 | [813d820](https://github.com/undergroundwires/privacy.sexy/commit/813d820b85e1b623c50f8e0325ad372bf2f344f9)
* Improve desktop icon quality and generation | [ab25e0a](https://github.com/undergroundwires/privacy.sexy/commit/ab25e0a066be14ea979dafd0f80e1091bd5d33f8)
* win: improve enabling secure connections #175 | [c75df1c](https://github.com/undergroundwires/privacy.sexy/commit/c75df1c8c1151b64cbf014383dea0b748a8c78b3)
* Fix VSCode script issues with added CI/CD tests | [1d7cafc](https://github.com/undergroundwires/privacy.sexy/commit/1d7cafc831dcc339a10646794410dad7096bfe60)
* Fix win execution with whitespace in username #351 | [a334320](https://github.com/undergroundwires/privacy.sexy/commit/a3343205b1196d5a81fd3cee2ae661ce871a7bef)
* Fix misaligned tooltip positions in modal dialogs | [dd71536](https://github.com/undergroundwires/privacy.sexy/commit/dd71536316ec819caeb418b8635d544ac80e58ad)
* Fix Chromium scrollbar-induced layout shifts | [bc4879c](https://github.com/undergroundwires/privacy.sexy/commit/bc4879cfe97becac3c54f6b40780a89464d3b772)
* ci/cd: remove `check-latest` from `setup-node` | [52a4730](https://github.com/undergroundwires/privacy.sexy/commit/52a4730073b8ebfb2ce9d530b44e4a179f5849fe)
* win: categorize and rename network security #131 | [9fd193e](https://github.com/undergroundwires/privacy.sexy/commit/9fd193e676f1f0646898f5130fbfaaf25050b2e3)
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.13.2...0.13.3)
## 0.13.2 (2024-04-15)
* Update documentation for `logo-update.js` script | [4a9b430](https://github.com/undergroundwires/privacy.sexy/commit/4a9b430702bc6082426b50ecc3a06362b5720796)

View File

@@ -122,7 +122,7 @@
## Get started
- 🌍️ **Online**: [https://privacy.sexy](https://privacy.sexy).
- 🖥️ **Offline**: Download directly for: [Windows](https://github.com/undergroundwires/privacy.sexy/releases/download/0.13.2/privacy.sexy-Setup-0.13.2.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.13.2/privacy.sexy-0.13.2.dmg), [Linux](https://github.com/undergroundwires/privacy.sexy/releases/download/0.13.2/privacy.sexy-0.13.2.AppImage). For more options, see [here](#additional-install-options).
- 🖥️ **Offline**: Download directly for: [Windows](https://github.com/undergroundwires/privacy.sexy/releases/download/0.13.3/privacy.sexy-Setup-0.13.3.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.13.3/privacy.sexy-0.13.3.dmg), [Linux](https://github.com/undergroundwires/privacy.sexy/releases/download/0.13.3/privacy.sexy-0.13.3.AppImage). For more options, see [here](#additional-install-options).
See also:

View File

@@ -8,7 +8,7 @@ systems or configurations that haven't undergone official testing.
- **Version:** Windows 10 and later.
- **Processor:** Intel Pentium 4 or later.
- **Architecture:** 64-bit (x64), ARM.
- **Architecture:** 64-bit (x86-64), ARM (ARM64).
> **⚠️ Compatibility Note:**
> ARM version is only compatible with Windows 11 and later.
@@ -17,24 +17,20 @@ systems or configurations that haven't undergone official testing.
## macOS
- **Version:** macOS Catalina (10.15) and later.
- **Architecture:** Intel-based (64-bit), Apple Silicon (ARM).
> **⚠️ Compatibility Note:**
> Apple Silicon version runs non-natively, leading to slower performance due to emulation [2].
- **Architecture:** Intel-based (x86-64), Apple silicon (ARM64).
## Linux
- **Version:** Ubuntu 18.04 and later, Fedora 32 and later, and Debian 10 and later.
- **Processor:** Intel Pentium 4 or later.
- **Architecture:** 64-bit (x64).
- **Architecture:** 64-bit (x86-64).
## References
System requirements reflect Electron's platform capabilities [3] and Chromium's recommended configurations [4].
System requirements reflect Electron's platform capabilities [2] and Chromium's recommended configurations [3].
For details on the build process, see [electron-builder configuration file](./../../electron-builder.cjs).
[1]: https://web.archive.org/web/20240428082726/https://learn.microsoft.com/en-us/windows/arm/add-arm-support#emulation-on-arm-based-devices-for-x86-or-x64-windows-apps "Add support Arm devices to your Windows app | Microsoft Learn | learn.microsoft.com"
[2]: https://archive.today/2024.04.28-082901/https://developer.apple.com/documentation/apple-silicon/building-a-universal-macos-binary%23overview "Building a universal macOS binary | Apple Developer Documentation | developer.apple.com"
[3]: https://archive.ph/2024.04.28-082958/https://github.com/electron/electron/blob/main/README.md#platform-support "Platform Support | electron/README.md at main · electron/electron · GitHub | github.com"
[4]: https://web.archive.org/web/20240428082945/https://support.google.com/chrome/a/answer/7100626?hl=en "Chrome browser system requirements - Chrome Enterprise and Education Help | support.google.com"
[2]: https://archive.ph/2024.04.28-082958/https://github.com/electron/electron/blob/main/README.md#platform-support "Platform Support | electron/README.md at main · electron/electron · GitHub | github.com"
[3]: https://web.archive.org/web/20240428082945/https://support.google.com/chrome/a/answer/7100626?hl=en "Chrome browser system requirements - Chrome Enterprise and Education Help | support.google.com"

View File

@@ -43,7 +43,10 @@ module.exports = {
// macOS
mac: {
target: 'dmg',
target: {
target: 'dmg',
arch: 'universal',
},
},
dmg: {
artifactName: '${name}-${version}.${ext}',

263
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "privacy.sexy",
"version": "0.13.2",
"version": "0.13.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "privacy.sexy",
"version": "0.13.2",
"version": "0.13.3",
"hasInstallScript": true,
"dependencies": {
"@floating-ui/vue": "^1.0.6",
@@ -17,7 +17,7 @@
"electron-updater": "^6.1.9",
"file-saver": "^2.0.5",
"markdown-it": "^14.1.0",
"vue": "^3.4.21"
"vue": "^3.4.27"
},
"devDependencies": {
"@modyfi/vite-plugin-yaml": "^1.1.0",
@@ -522,9 +522,9 @@
}
},
"node_modules/@babel/parser": {
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz",
"integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==",
"version": "7.24.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz",
"integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==",
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -3947,49 +3947,49 @@
}
},
"node_modules/@vue/compiler-core": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.21.tgz",
"integrity": "sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.27.tgz",
"integrity": "sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==",
"dependencies": {
"@babel/parser": "^7.23.9",
"@vue/shared": "3.4.21",
"@babel/parser": "^7.24.4",
"@vue/shared": "3.4.27",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.0.2"
"source-map-js": "^1.2.0"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.21.tgz",
"integrity": "sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz",
"integrity": "sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==",
"dependencies": {
"@vue/compiler-core": "3.4.21",
"@vue/shared": "3.4.21"
"@vue/compiler-core": "3.4.27",
"@vue/shared": "3.4.27"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.21.tgz",
"integrity": "sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz",
"integrity": "sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==",
"dependencies": {
"@babel/parser": "^7.23.9",
"@vue/compiler-core": "3.4.21",
"@vue/compiler-dom": "3.4.21",
"@vue/compiler-ssr": "3.4.21",
"@vue/shared": "3.4.21",
"@babel/parser": "^7.24.4",
"@vue/compiler-core": "3.4.27",
"@vue/compiler-dom": "3.4.27",
"@vue/compiler-ssr": "3.4.27",
"@vue/shared": "3.4.27",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.7",
"postcss": "^8.4.35",
"source-map-js": "^1.0.2"
"magic-string": "^0.30.10",
"postcss": "^8.4.38",
"source-map-js": "^1.2.0"
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.21.tgz",
"integrity": "sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz",
"integrity": "sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==",
"dependencies": {
"@vue/compiler-dom": "3.4.21",
"@vue/shared": "3.4.21"
"@vue/compiler-dom": "3.4.27",
"@vue/shared": "3.4.27"
}
},
"node_modules/@vue/eslint-config-airbnb": {
@@ -4099,48 +4099,48 @@
}
},
"node_modules/@vue/reactivity": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.21.tgz",
"integrity": "sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.27.tgz",
"integrity": "sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==",
"dependencies": {
"@vue/shared": "3.4.21"
"@vue/shared": "3.4.27"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.21.tgz",
"integrity": "sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.27.tgz",
"integrity": "sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==",
"dependencies": {
"@vue/reactivity": "3.4.21",
"@vue/shared": "3.4.21"
"@vue/reactivity": "3.4.27",
"@vue/shared": "3.4.27"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.21.tgz",
"integrity": "sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.27.tgz",
"integrity": "sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==",
"dependencies": {
"@vue/runtime-core": "3.4.21",
"@vue/shared": "3.4.21",
"@vue/runtime-core": "3.4.27",
"@vue/shared": "3.4.27",
"csstype": "^3.1.3"
}
},
"node_modules/@vue/server-renderer": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.21.tgz",
"integrity": "sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.27.tgz",
"integrity": "sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==",
"dependencies": {
"@vue/compiler-ssr": "3.4.21",
"@vue/shared": "3.4.21"
"@vue/compiler-ssr": "3.4.27",
"@vue/shared": "3.4.27"
},
"peerDependencies": {
"vue": "3.4.21"
"vue": "3.4.27"
}
},
"node_modules/@vue/shared": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz",
"integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g=="
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
"integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA=="
},
"node_modules/@vue/test-utils": {
"version": "2.4.5",
@@ -10777,14 +10777,11 @@
}
},
"node_modules/magic-string": {
"version": "0.30.8",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz",
"integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==",
"version": "0.30.10",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
"integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
},
"engines": {
"node": ">=12"
}
},
"node_modules/map-age-cleaner": {
@@ -16982,15 +16979,15 @@
}
},
"node_modules/vue": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz",
"integrity": "sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.27.tgz",
"integrity": "sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==",
"dependencies": {
"@vue/compiler-dom": "3.4.21",
"@vue/compiler-sfc": "3.4.21",
"@vue/runtime-dom": "3.4.21",
"@vue/server-renderer": "3.4.21",
"@vue/shared": "3.4.21"
"@vue/compiler-dom": "3.4.27",
"@vue/compiler-sfc": "3.4.27",
"@vue/runtime-dom": "3.4.27",
"@vue/server-renderer": "3.4.27",
"@vue/shared": "3.4.27"
},
"peerDependencies": {
"typescript": "*"
@@ -17918,9 +17915,9 @@
}
},
"@babel/parser": {
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz",
"integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg=="
"version": "7.24.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz",
"integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg=="
},
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
"version": "7.23.3",
@@ -20277,49 +20274,49 @@
}
},
"@vue/compiler-core": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.21.tgz",
"integrity": "sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.27.tgz",
"integrity": "sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==",
"requires": {
"@babel/parser": "^7.23.9",
"@vue/shared": "3.4.21",
"@babel/parser": "^7.24.4",
"@vue/shared": "3.4.27",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.0.2"
"source-map-js": "^1.2.0"
}
},
"@vue/compiler-dom": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.21.tgz",
"integrity": "sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz",
"integrity": "sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==",
"requires": {
"@vue/compiler-core": "3.4.21",
"@vue/shared": "3.4.21"
"@vue/compiler-core": "3.4.27",
"@vue/shared": "3.4.27"
}
},
"@vue/compiler-sfc": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.21.tgz",
"integrity": "sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz",
"integrity": "sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==",
"requires": {
"@babel/parser": "^7.23.9",
"@vue/compiler-core": "3.4.21",
"@vue/compiler-dom": "3.4.21",
"@vue/compiler-ssr": "3.4.21",
"@vue/shared": "3.4.21",
"@babel/parser": "^7.24.4",
"@vue/compiler-core": "3.4.27",
"@vue/compiler-dom": "3.4.27",
"@vue/compiler-ssr": "3.4.27",
"@vue/shared": "3.4.27",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.7",
"postcss": "^8.4.35",
"source-map-js": "^1.0.2"
"magic-string": "^0.30.10",
"postcss": "^8.4.38",
"source-map-js": "^1.2.0"
}
},
"@vue/compiler-ssr": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.21.tgz",
"integrity": "sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz",
"integrity": "sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==",
"requires": {
"@vue/compiler-dom": "3.4.21",
"@vue/shared": "3.4.21"
"@vue/compiler-dom": "3.4.27",
"@vue/shared": "3.4.27"
}
},
"@vue/eslint-config-airbnb": {
@@ -20395,45 +20392,45 @@
}
},
"@vue/reactivity": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.21.tgz",
"integrity": "sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.27.tgz",
"integrity": "sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==",
"requires": {
"@vue/shared": "3.4.21"
"@vue/shared": "3.4.27"
}
},
"@vue/runtime-core": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.21.tgz",
"integrity": "sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.27.tgz",
"integrity": "sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==",
"requires": {
"@vue/reactivity": "3.4.21",
"@vue/shared": "3.4.21"
"@vue/reactivity": "3.4.27",
"@vue/shared": "3.4.27"
}
},
"@vue/runtime-dom": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.21.tgz",
"integrity": "sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.27.tgz",
"integrity": "sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==",
"requires": {
"@vue/runtime-core": "3.4.21",
"@vue/shared": "3.4.21",
"@vue/runtime-core": "3.4.27",
"@vue/shared": "3.4.27",
"csstype": "^3.1.3"
}
},
"@vue/server-renderer": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.21.tgz",
"integrity": "sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.27.tgz",
"integrity": "sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==",
"requires": {
"@vue/compiler-ssr": "3.4.21",
"@vue/shared": "3.4.21"
"@vue/compiler-ssr": "3.4.27",
"@vue/shared": "3.4.27"
}
},
"@vue/shared": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz",
"integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g=="
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
"integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA=="
},
"@vue/test-utils": {
"version": "2.4.5",
@@ -25486,9 +25483,9 @@
}
},
"magic-string": {
"version": "0.30.8",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz",
"integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==",
"version": "0.30.10",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
"integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
"requires": {
"@jridgewell/sourcemap-codec": "^1.4.15"
}
@@ -29953,15 +29950,15 @@
}
},
"vue": {
"version": "3.4.21",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz",
"integrity": "sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.27.tgz",
"integrity": "sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==",
"requires": {
"@vue/compiler-dom": "3.4.21",
"@vue/compiler-sfc": "3.4.21",
"@vue/runtime-dom": "3.4.21",
"@vue/server-renderer": "3.4.21",
"@vue/shared": "3.4.21"
"@vue/compiler-dom": "3.4.27",
"@vue/compiler-sfc": "3.4.27",
"@vue/runtime-dom": "3.4.27",
"@vue/server-renderer": "3.4.27",
"@vue/shared": "3.4.27"
}
},
"vue-component-type-helpers": {

View File

@@ -1,6 +1,6 @@
{
"name": "privacy.sexy",
"version": "0.13.2",
"version": "0.13.3",
"private": true,
"slogan": "Privacy is sexy",
"description": "Enforce privacy & security best-practices on Windows, macOS and Linux, because privacy is sexy.",
@@ -42,7 +42,7 @@
"electron-updater": "^6.1.9",
"file-saver": "^2.0.5",
"markdown-it": "^14.1.0",
"vue": "^3.4.21"
"vue": "^3.4.27"
},
"devDependencies": {
"@modyfi/vite-plugin-yaml": "^1.1.0",

View File

@@ -15,18 +15,26 @@ const DefaultOptions: ThrottleOptions = {
timer: PlatformTimer,
};
export function throttle(
export interface ThrottleFunction {
(
callback: CallbackType,
waitInMs: number,
options?: Partial<ThrottleOptions>,
): CallbackType;
}
export const throttle: ThrottleFunction = (
callback: CallbackType,
waitInMs: number,
options: Partial<ThrottleOptions> = DefaultOptions,
): CallbackType {
): CallbackType => {
const defaultedOptions: ThrottleOptions = {
...DefaultOptions,
...options,
};
const throttler = new Throttler(waitInMs, callback, defaultedOptions);
return (...args: unknown[]) => throttler.invoke(...args);
}
};
class Throttler {
private lastExecutionTime: number | null = null;

View File

@@ -98,6 +98,7 @@ function hasCall(data: FunctionData): data is CallFunctionData {
}
function ensureValidFunctions(functions: readonly FunctionData[]) {
ensureNoUnnamedFunctions(functions);
ensureNoDuplicatesInFunctionNames(functions);
ensureEitherCallOrCodeIsDefined(functions);
ensureNoDuplicateCode(functions);
@@ -108,6 +109,16 @@ function printList(list: readonly string[]): string {
return `"${list.join('","')}"`;
}
function ensureNoUnnamedFunctions(functions: readonly FunctionData[]) {
const functionsWithoutNames = functions.filter(
(func) => !func.name || func.name.trim().length === 0,
);
if (functionsWithoutNames.length) {
const invalidFunctions = functionsWithoutNames.map((f) => JSON.stringify(f));
throw new Error(`Some function(s) have no names:\n${invalidFunctions.join('\n')}`);
}
}
function ensureEitherCallOrCodeIsDefined(holders: readonly FunctionData[]) {
// Ensure functions do not define both call and code
const withBothCallAndCode = holders.filter((holder) => hasCode(holder) && hasCall(holder));

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,7 @@
<template>
<span class="info-container">
<span
class="info-container"
>
<TooltipWrapper>
<AppIcon icon="circle-info" />
<template #tooltip>
@@ -19,27 +21,17 @@ export default defineComponent({
TooltipWrapper,
AppIcon,
},
props: {
hasLeftMargin: {
type: Boolean,
default: false,
},
},
});
</script>
<style scoped lang="scss">
@use "@/presentation/assets/styles/main" as *;
@mixin apply-style-when-placed-after-non-text {
* + & {
@content;
}
}
<style lang="scss">
.info-container {
vertical-align: text-top;
* + & { // If it's followed by any other element
vertical-align: middle;
@include set-property-ch-value-with-fallback(
$property: margin-left,
$value-in-ch: 0.5,
)
}
}
</style>

View File

@@ -0,0 +1,36 @@
<template>
<span class="info-tooltip-wrapper">
<span>
<slot />
</span>
<span>
<InfoTooltipInline>
<slot name="info" />
</InfoTooltipInline>
</span>
</span>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import InfoTooltipInline from './InfoTooltipInline.vue';
export default defineComponent({
components: {
InfoTooltipInline,
},
});
</script>
<style lang="scss">
@use "@/presentation/assets/styles/main" as *;
.info-tooltip-wrapper {
display: flex;
align-items: center;
@include set-property-ch-value-with-fallback(
$property: gap,
$value-in-ch: 0.5,
)
}
</style>

View File

@@ -17,7 +17,7 @@
<p>
This requires you to do additional manual
steps. If you are unsure how to follow the instructions, tap or hover on information
<InfoTooltip>Engage with icons like this for extra wisdom!</InfoTooltip>
<InfoTooltipInline>Engage with icons like this for extra wisdom!</InfoTooltipInline>
icons near the steps, or follow the easy alternative described above.
</p>
<p>
@@ -32,12 +32,12 @@ import { defineComponent, computed } from 'vue';
import { injectKey } from '@/presentation/injectionSymbols';
import { OperatingSystem } from '@/domain/OperatingSystem';
import { getOperatingSystemDisplayName } from '@/presentation/components/Shared/OperatingSystemNames';
import InfoTooltip from './InfoTooltip.vue';
import InfoTooltipInline from './Help/InfoTooltipInline.vue';
import PlatformInstructionSteps from './Steps/PlatformInstructionSteps.vue';
export default defineComponent({
components: {
InfoTooltip,
InfoTooltipInline,
PlatformInstructionSteps,
},
props: {

View File

@@ -2,7 +2,7 @@
<InstructionSteps>
<InstructionStep>
Download the file.
<InfoTooltip>
<InfoTooltipInline>
<p>
You should have already been prompted to save the script file.
</p>
@@ -10,11 +10,11 @@
If this was not the case or you did not save the script when prompted,
please try to download your script file again.
</p>
</InfoTooltip>
</InfoTooltipInline>
</InstructionStep>
<InstructionStep>
Open terminal.
<InfoTooltip>
<InfoTooltipInline>
<p>
Opening terminal changes based on the distro you run.
</p>
@@ -39,30 +39,32 @@
</li>
</ul>
</p>
</InfoTooltip>
</InfoTooltipInline>
</InstructionStep>
<InstructionStep>
<p>
Navigate to the folder where you downloaded the file e.g.:
</p>
<p>
<CopyableCommand>cd ~/Downloads</CopyableCommand>
<InfoTooltip>
<p>
Press on <code>enter/return</code> key after running the command.
</p>
<p>
If the file is not downloaded on Downloads folder,
change <code>Downloads</code> to path where the file is downloaded.
</p>
<p>
This command means:
<ul>
<li><code>cd</code> will change the current folder.</li>
<li><code>~</code> is the user home directory.</li>
</ul>
</p>
</InfoTooltip>
<InfoTooltipWrapper>
<CopyableCommand>cd ~/Downloads</CopyableCommand>
<template #info>
<p>
Press on <code>enter/return</code> key after running the command.
</p>
<p>
If the file is not downloaded on Downloads folder,
change <code>Downloads</code> to path where the file is downloaded.
</p>
<p>
This command means:
<ul>
<li><code>cd</code> will change the current folder.</li>
<li><code>~</code> is the user home directory.</li>
</ul>
</p>
</template>
</InfoTooltipWrapper>
</p>
</InstructionStep>
<InstructionStep>
@@ -70,26 +72,28 @@
Give the file execute permissions:
</p>
<p>
<CopyableCommand>chmod +x {{ filename }}</CopyableCommand>
<InfoTooltip>
<p>
Press on <code>enter/return</code> key after running the command.
</p>
<p>
It will make the file executable.
</p>
<p>
If you use desktop environment you can alternatively (instead of running the command):
<ol>
<li>Locate the file using your file manager.</li>
<li>Right click on the file, select "Properties".</li>
<li>Go to "Permissions" and check "Allow executing file as program".</li>
</ol>
</p>
<p>
These GUI steps and name of options may change depending on your file manager.'
</p>
</InfoTooltip>
<InfoTooltipWrapper>
<CopyableCommand>chmod +x {{ filename }}</CopyableCommand>
<template #info>
<p>
Press on <code>enter/return</code> key after running the command.
</p>
<p>
It will make the file executable.
</p>
<p>
If you use desktop environment you can alternatively (instead of running the command):
<ol>
<li>Locate the file using your file manager.</li>
<li>Right click on the file, select "Properties".</li>
<li>Go to "Permissions" and check "Allow executing file as program".</li>
</ol>
</p>
<p>
These GUI steps and name of options may change depending on your file manager.'
</p>
</template>
</InfoTooltipWrapper>
</p>
</InstructionStep>
<InstructionStep>
@@ -97,21 +101,24 @@
Execute the file:
</p>
<p>
<CopyableCommand>./{{ filename }}</CopyableCommand>
<InfoTooltip>
<p>
If you have desktop environment, instead of running this command you can alternatively:
</p>
<ol>
<li>Locate the file using your file manager.</li>
<li>Right click on the file, select "Run as program".</li>
</ol>
</InfoTooltip>
<InfoTooltipWrapper>
<CopyableCommand>./{{ filename }}</CopyableCommand>
<template #info>
<p>
If you have desktop environment, instead of running this command
you can alternatively:
</p>
<ol>
<li>Locate the file using your file manager.</li>
<li>Right click on the file, select "Run as program".</li>
</ol>
</template>
</InfoTooltipWrapper>
</p>
</InstructionStep>
<InstructionStep>
If asked, enter your administrator password.
<InfoTooltip>
<InfoTooltipInline>
<p>
As you type, your password will be hidden but the keys are still
registered, so keep typing.
@@ -122,7 +129,7 @@
<p>
Administrator privileges are required to configure OS.
</p>
</InfoTooltip>
</InfoTooltipInline>
</InstructionStep>
</InstructionSteps>
</template>
@@ -131,13 +138,15 @@
import { defineComponent } from 'vue';
import InstructionSteps from '../InstructionSteps.vue';
import InstructionStep from '../InstructionStep.vue';
import InfoTooltip from '../../InfoTooltip.vue';
import InfoTooltipInline from '../../Help/InfoTooltipInline.vue';
import InfoTooltipWrapper from '../../Help/InfoTooltipWrapper.vue';
import CopyableCommand from '../CopyableCommand.vue';
export default defineComponent({
components: {
CopyableCommand,
InfoTooltip,
InfoTooltipInline,
InfoTooltipWrapper,
InstructionSteps,
InstructionStep,
},

View File

@@ -2,7 +2,7 @@
<InstructionSteps>
<InstructionStep>
Download the file.
<InfoTooltip>
<InfoTooltipInline>
<p>
You should have already been prompted to save the script file.
</p>
@@ -10,38 +10,38 @@
If this was not the case or you did not save the script when prompted,
please try to download your script file again.
</p>
</InfoTooltip>
</InfoTooltipInline>
</InstructionStep>
<InstructionStep>
Open terminal.
<InfoTooltip>
<InfoTooltipInline>
Type Terminal into Spotlight or open it from the Applications -> Utilities folder.
</InfoTooltip>
</InfoTooltipInline>
</InstructionStep>
<InstructionStep>
<p>
Navigate to the folder where you downloaded the file e.g.:
</p>
<p>
<CopyableCommand>
cd ~/Downloads
</CopyableCommand>
<InfoTooltip>
<p>
Press on <code>enter/return</code> key after running the command.
</p>
<p>
If the file is not downloaded on Downloads folder,
change <code>Downloads</code> to path where the file is downloaded.
</p>
<p>
This command means:
<ul>
<li><code>cd</code> will change the current folder.</li>
<li><code>~</code> is the user home directory.</li>
</ul>
</p>
</InfoTooltip>
<InfoTooltipWrapper>
<CopyableCommand>cd ~/Downloads</CopyableCommand>
<template #info>
<p>
Press on <code>enter/return</code> key after running the command.
</p>
<p>
If the file is not downloaded on Downloads folder,
change <code>Downloads</code> to path where the file is downloaded.
</p>
<p>
This command means:
<ul>
<li><code>cd</code> will change the current folder.</li>
<li><code>~</code> is the user home directory.</li>
</ul>
</p>
</template>
</InfoTooltipWrapper>
</p>
</InstructionStep>
<InstructionStep>
@@ -49,15 +49,17 @@
Give the file execute permissions:
</p>
<p>
<CopyableCommand>chmod +x {{ filename }}</CopyableCommand>
<InfoTooltip>
<p>
Press on <code>enter/return</code> key after running the command.
</p>
<p>
It will make the file executable.
</p>
</InfoTooltip>
<InfoTooltipWrapper>
<CopyableCommand>chmod +x {{ filename }}</CopyableCommand>
<template #info>
<p>
Press on <code>enter/return</code> key after running the command.
</p>
<p>
It will make the file executable.
</p>
</template>
</InfoTooltipWrapper>
</p>
</InstructionStep>
<InstructionStep>
@@ -65,15 +67,17 @@
Execute the file:
</p>
<p>
<CopyableCommand>./{{ filename }}</CopyableCommand>
<InfoTooltip>
Alternatively you can locate the file in <strong>Finder</strong> and double click on it.
</InfoTooltip>
<InfoTooltipWrapper>
<CopyableCommand>./{{ filename }}</CopyableCommand>
<template #info>
Alternatively you can locate the file in <strong>Finder</strong> and double click on it.
</template>
</InfoTooltipWrapper>
</p>
</InstructionStep>
<InstructionStep>
If asked, enter your administrator password.
<InfoTooltip>
<InfoTooltipInline>
<p>
As you type, your password will be hidden but the keys are
still registered, so keep typing.
@@ -84,7 +88,7 @@
<p>
Administrator privileges are required to configure OS.
</p>
</InfoTooltip>
</InfoTooltipInline>
</InstructionStep>
</InstructionSteps>
</template>
@@ -93,13 +97,15 @@
import { defineComponent } from 'vue';
import InstructionSteps from '../InstructionSteps.vue';
import InstructionStep from '../InstructionStep.vue';
import InfoTooltip from '../../InfoTooltip.vue';
import InfoTooltipInline from '../../Help/InfoTooltipInline.vue';
import InfoTooltipWrapper from '../../Help/InfoTooltipWrapper.vue';
import CopyableCommand from '../CopyableCommand.vue';
export default defineComponent({
components: {
CopyableCommand,
InfoTooltip,
InfoTooltipInline,
InfoTooltipWrapper,
InstructionSteps,
InstructionStep,
},

View File

@@ -2,13 +2,13 @@
<InstructionSteps>
<InstructionStep>
Download the file.
<InfoTooltip>
<InfoTooltipInline>
<p>If a save prompt doesn't appear, try downloading the script again.</p>
</InfoTooltip>
</InfoTooltipInline>
</InstructionStep>
<InstructionStep>
If warned by your browser, keep the file.
<InfoTooltip>
<InfoTooltipInline>
<!--
Tests (15/01/2023):
- Edge (Defender activated): "filename isn't commonly downloaded..."
@@ -33,11 +33,11 @@
For <strong>Firefox</strong> and <strong>Chrome</strong>, typically no additional
action is needed.
</p>
</InfoTooltip>
</InfoTooltipInline>
</InstructionStep>
<InstructionStep>
If your antivirus (e.g., Defender) alerts you, address the warning.
<InfoTooltip>
<InfoTooltipInline>
<!--
Tests (15/01/2023):
- Edge (Defender activated): "Couldn't download - Virus detected"
@@ -75,7 +75,7 @@
<li>and keep real-time protection enabled whenever possible.</li>
</ul>
</blockquote>
</InfoTooltip>
</InfoTooltipInline>
</InstructionStep>
<InstructionStep>
<!--
@@ -85,7 +85,7 @@
- Firefox: "filename is executable file. Executable files may contain..?" OK/Cancel
-->
Open the downloaded file.
<InfoTooltip>
<InfoTooltipInline>
<p>
Confirm any browser prompts to open the file.
</p>
@@ -100,11 +100,11 @@
<strong>Edge</strong> and <strong>Chrome</strong> users usually will not
encounter additional prompts.
</p>
</InfoTooltip>
</InfoTooltipInline>
</InstructionStep>
<InstructionStep>
If prompted, confirm SmartScreen warnings.
<InfoTooltip>
<InfoTooltipInline>
<p>
Windows SmartScreen might display a cautionary message.
</p>
@@ -118,11 +118,11 @@
<li>Select <strong>Run anyway</strong>.</li>
</ol>
</p>
</InfoTooltip>
</InfoTooltipInline>
</InstructionStep>
<InstructionStep>
If administrative permissions are requested, grant them.
<InfoTooltip>
<InfoTooltipInline>
<p>
The script may request administrative rights to apply changes.
</p>
@@ -132,7 +132,7 @@
<p>
Click <strong>Yes</strong> to authorize and run the script.
</p>
</InfoTooltip>
</InfoTooltipInline>
</InstructionStep>
</InstructionSteps>
</template>
@@ -141,11 +141,11 @@
import { defineComponent } from 'vue';
import InstructionSteps from '../InstructionSteps.vue';
import InstructionStep from '../InstructionStep.vue';
import InfoTooltip from '../../InfoTooltip.vue';
import InfoTooltipInline from '../../Help/InfoTooltipInline.vue';
export default defineComponent({
components: {
InfoTooltip,
InfoTooltipInline,
InstructionSteps,
InstructionStep,
},

View File

@@ -1,8 +1,9 @@
import {
computed, readonly, ref, watch,
computed, readonly, ref, shallowRef, watch,
} from 'vue';
import { throttle } from '@/application/Common/Timing/Throttle';
import { useAutoUnsubscribedEventListener } from '../Shared/Hooks/UseAutoUnsubscribedEventListener';
import { useResizeObserver } from '../Shared/Hooks/Resize/UseResizeObserver';
const RESIZE_EVENT_THROTTLE_MS = 200;
@@ -29,11 +30,17 @@ function getScrollbarGutterWidth(): number {
function useBodyWidth() {
const width = ref(document.body.offsetWidth);
const observer = new ResizeObserver((entries) => throttle(() => {
for (const entry of entries) {
width.value = entry.borderBoxSize[0].inlineSize;
}
}, RESIZE_EVENT_THROTTLE_MS));
observer.observe(document.body, { box: 'border-box' });
useResizeObserver(
{
observedElementRef: shallowRef(document.body),
throttleInMs: RESIZE_EVENT_THROTTLE_MS,
observeCallback: (entries) => {
for (const entry of entries) {
width.value = entry.borderBoxSize[0].inlineSize;
}
},
observeOptions: { box: 'border-box' },
},
);
return readonly(width);
}

View File

@@ -68,9 +68,7 @@ export default defineComponent({
flex-direction: column;
flex: 1; // Expands the container to fill available horizontal space, enabling alignment of child items.
max-width: 100%; // Prevents horizontal expansion of inner content (e.g., when a code block is shown)
*:not(:first-child) {
margin-left: $spacing-absolute-small;
}
.header {
display: flex;
flex-direction: row;
@@ -80,6 +78,7 @@ export default defineComponent({
}
.docs {
background: $color-primary-darkest;
margin-left: $spacing-absolute-small;
margin-top: $spacing-relative-x-small;
color: $color-on-primary;
text-transform: none;

View File

@@ -0,0 +1,41 @@
import { onBeforeUnmount } from 'vue';
export function useAnimationFrameLimiter(
cancelAnimationFrame: CancelAnimationFrameFunction = window.cancelAnimationFrame,
requestAnimationFrame: RequestAnimationFrameFunction = window.requestAnimationFrame,
onTeardown: RegisterTeardownCallbackFunction = onBeforeUnmount,
): AnimationFrameLimiter {
let requestId: AnimationFrameId | null = null;
const cancelNextFrame = () => {
if (requestId === null) {
return;
}
cancelAnimationFrame(requestId);
};
const resetNextFrame = (callback: AnimationFrameRequestCallback) => {
cancelNextFrame();
requestId = requestAnimationFrame(callback);
};
onTeardown(() => {
cancelNextFrame();
});
return {
cancelNextFrame,
resetNextFrame,
};
}
export type CancelAnimationFrameFunction = typeof window.cancelAnimationFrame;
export type RequestAnimationFrameFunction = (callback: AnimationFrameRequestCallback) => number;
export type RegisterTeardownCallbackFunction = (callback: () => void) => void;
export type AnimationFrameId = ReturnType<typeof requestAnimationFrame>;
export type AnimationFrameRequestCallback = () => void;
export interface AnimationFrameLimiter {
cancelNextFrame(): void;
resetNextFrame(callback: AnimationFrameRequestCallback): void;
}

View File

@@ -0,0 +1,67 @@
import {
onBeforeMount, onBeforeUnmount,
watch, type Ref,
} from 'vue';
import { throttle, type ThrottleFunction } from '@/application/Common/Timing/Throttle';
import { useResizeObserverPolyfill } from './UseResizeObserverPolyfill';
import { useAnimationFrameLimiter } from './UseAnimationFrameLimiter';
export function useResizeObserver(
config: ResizeObserverConfig,
usePolyfill = useResizeObserverPolyfill,
useFrameLimiter = useAnimationFrameLimiter,
throttler: ThrottleFunction = throttle,
onSetup: LifecycleHookRegistration = onBeforeMount,
onTeardown: LifecycleHookRegistration = onBeforeUnmount,
) {
const { resetNextFrame, cancelNextFrame } = useFrameLimiter();
// This prevents the 'ResizeObserver loop completed with undelivered notifications' error when
// the browser can't process all observations within one animation frame.
// Reference: https://github.com/WICG/resize-observer/issues/38
const { resizeObserverReady } = usePolyfill();
// This ensures compatibility with ancient browsers. All modern browsers support ResizeObserver.
// Compatibility info: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#browser_compatibility
const throttledCallback = throttler(config.observeCallback, config.throttleInMs);
// Throttling enhances performance during rapid changes such as window resizing.
let observer: ResizeObserver | null;
const disposeObserver = () => {
cancelNextFrame();
observer?.disconnect();
observer = null;
};
onSetup(() => {
watch(() => config.observedElementRef.value, (element) => {
if (!element) {
disposeObserver();
return;
}
resizeObserverReady.then((createObserver) => {
disposeObserver();
observer = createObserver((...args) => {
resetNextFrame(() => throttledCallback(...args));
});
observer.observe(element, config?.observeOptions);
});
}, { immediate: true });
});
onTeardown(() => {
disposeObserver();
});
}
export interface ResizeObserverConfig {
readonly observedElementRef: ObservedElementReference;
readonly throttleInMs: number;
readonly observeCallback: ResizeObserverCallback;
readonly observeOptions?: ResizeObserverOptions;
}
export type ObservedElementReference = Readonly<Ref<HTMLElement | undefined>>;
export type LifecycleHookRegistration = (callback: () => void) => void;

View File

@@ -16,11 +16,17 @@ async function polyfillResizeObserver(): Promise<typeof ResizeObserver> {
return polyfillLoader.getValue();
}
interface ResizeObserverCreator {
(
...args: ConstructorParameters<typeof ResizeObserver>
): ResizeObserver;
}
export function useResizeObserverPolyfill() {
const resizeObserverReady = new Promise<void>((resolve) => {
const resizeObserverReady = new Promise<ResizeObserverCreator>((resolve) => {
onMounted(async () => {
await polyfillResizeObserver();
resolve();
resolve((args) => new ResizeObserver(args));
});
});
return { resizeObserverReady };

View File

@@ -6,10 +6,9 @@
<script lang="ts">
import {
defineComponent, shallowRef, onMounted, onBeforeUnmount, watch,
defineComponent, shallowRef, onMounted, watch,
} from 'vue';
import { useResizeObserverPolyfill } from '@/presentation/components/Shared/Hooks/UseResizeObserverPolyfill';
import { throttle } from '@/application/Common/Timing/Throttle';
import { useResizeObserver } from './Hooks/Resize/UseResizeObserver';
export default defineComponent({
emits: {
@@ -20,31 +19,21 @@ export default defineComponent({
/* eslint-enable @typescript-eslint/no-unused-vars */
},
setup(_, { emit }) {
const { resizeObserverReady } = useResizeObserverPolyfill();
const containerElement = shallowRef<HTMLElement>();
let width = 0;
let height = 0;
let observer: ResizeObserver | undefined;
onMounted(() => {
watch(() => containerElement.value, async (element) => {
if (!element) {
disposeObserver();
return;
}
resizeObserverReady.then(() => {
disposeObserver();
observer = new ResizeObserver(throttle(updateSize, 200));
observer.observe(element);
});
updateSize(); // Do not throttle, immediately inform new width
}, { immediate: true });
useResizeObserver({
observedElementRef: containerElement,
observeCallback: updateSize,
throttleInMs: 200,
});
onBeforeUnmount(() => {
disposeObserver();
onMounted(() => {
watch(() => containerElement.value, async () => {
updateSize();
}, { immediate: true });
});
function updateSize() {
@@ -81,11 +70,6 @@ export default defineComponent({
return { isChanged: true };
}
function disposeObserver() {
observer?.disconnect();
observer = undefined;
}
return {
containerElement,
};

View File

@@ -34,7 +34,7 @@ import {
useFloating, arrow, shift, flip, type Placement, offset, type Side, type Coords, autoUpdate,
} from '@floating-ui/vue';
import { defineComponent, shallowRef, computed } from 'vue';
import { useResizeObserverPolyfill } from '@/presentation/components/Shared/Hooks/UseResizeObserverPolyfill';
import { useResizeObserverPolyfill } from '@/presentation/components/Shared/Hooks/Resize/UseResizeObserverPolyfill';
import { throttle } from '@/application/Common/Timing/Throttle';
import { type TargetEventListener } from '@/presentation/components/Shared/Hooks/UseAutoUnsubscribedEventListener';
import { injectKey } from '@/presentation/injectionSymbols';

View File

@@ -105,5 +105,5 @@ console.log(`Status code: ${status.code}`);
- This is useful for websites that do not respond to HEAD requests, such as those behind certain CDN or web application firewalls.
- Provide patterns as regular expressions (`RegExp`), allowing them to match any part of a URL.
- Examples:
- To match any URL starting with "https://example.com/api": `/^https:\/\/example\.com\/api/`
- To match any domain ending with "cloudflare.com": `/^https:\/\/.*\.cloudflare\.com\//`
- To match any URL starting with `https://example.com/api`: `/^https:\/\/example\.com\/api/`
- To match any domain ending with `cloudflare.com`: `/^https:\/\/.*\.cloudflare\.com\//`

View File

@@ -10,6 +10,7 @@ describe('revert toggle', () => {
cardIndex: 1, // first is often cleanup that may lack revert button
});
cy.get('.toggle-switch')
.filter(':visible') // Avoid side-effects from hidden cards
.first()
.as('toggleSwitch');
});

View File

@@ -1,6 +1,6 @@
import { describe, it, expect } from 'vitest';
import { TimerStub } from '@tests/unit/shared/Stubs/TimerStub';
import { throttle, type ThrottleOptions } from '@/application/Common/Timing/Throttle';
import { throttle, type ThrottleFunction, type ThrottleOptions } from '@/application/Common/Timing/Throttle';
import type { Timer } from '@/application/Common/Timing/Timer';
import { formatAssertionMessage } from '@tests/shared/FormatAssertionMessage';
@@ -275,7 +275,7 @@ describe('throttle', () => {
});
});
type CallbackType = Parameters<typeof throttle>[0];
type CallbackType = Parameters<ThrottleFunction>[0];
class TestContext {
private options: Partial<ThrottleOptions> | undefined = {
@@ -315,7 +315,7 @@ class TestContext {
return this;
}
public throttle(): ReturnType<typeof throttle> {
public throttle(): ReturnType<ThrottleFunction> {
return throttle(
this.callback,
this.waitInMs,

View File

@@ -26,6 +26,27 @@ describe('SharedFunctionsParser', () => {
});
describe('parseFunctions', () => {
describe('validates functions', () => {
it('throws when functions have no names', () => {
// arrange
const invalidFunctions = [
createFunctionDataWithCode()
.withCode('test function 1')
.withName(' '), // Whitespace,
createFunctionDataWithCode()
.withCode('test function 2')
.withName(undefined as unknown as string), // Undefined
createFunctionDataWithCode()
.withCode('test function 3')
.withName(''), // Empty
];
const expectedError = `Some function(s) have no names:\n${invalidFunctions.map((f) => JSON.stringify(f)).join('\n')}`;
// act
const act = () => new ParseFunctionsCallerWithDefaults()
.withFunctions(invalidFunctions)
.parseFunctions();
// assert
expect(act).to.throw(expectedError);
});
it('throws when functions have same names', () => {
// arrange
const name = 'same-func-name';

View File

@@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest';
import { ref, type Ref } from 'vue';
import { useDragHandler, type DragDomModifier } from '@/presentation/components/Scripts/Slider/UseDragHandler';
import { ThrottleStub } from '@tests/unit/shared/Stubs/ThrottleStub';
import { throttle } from '@/application/Common/Timing/Throttle';
import { type ThrottleFunction } from '@/application/Common/Timing/Throttle';
import type { ConstructorArguments } from '@/TypeHelpers';
describe('useDragHandler', () => {
@@ -235,7 +235,7 @@ describe('useDragHandler', () => {
function initializeDragHandlerWithMocks(mocks?: {
readonly dragDomModifier?: DragDomModifier;
readonly draggableElementRef?: Ref<HTMLElement>;
readonly throttler?: typeof throttle,
readonly throttler?: ThrottleFunction,
}) {
return useDragHandler(
mocks?.draggableElementRef ?? ref(document.createElement('div')),

View File

@@ -0,0 +1,133 @@
import { describe, it, expect } from 'vitest';
import {
useAnimationFrameLimiter, type AnimationFrameId, type AnimationFrameRequestCallback,
type CancelAnimationFrameFunction, type RegisterTeardownCallbackFunction,
type RequestAnimationFrameFunction,
} from '@/presentation/components/Shared/Hooks/Resize/UseAnimationFrameLimiter';
import { expectExists } from '@tests/shared/Assertions/ExpectExists';
describe('useAnimationFrameLimiter', () => {
describe('resetNextFrame', () => {
it('schedules the callback in the next animation frame', () => {
// arrange
const expectedCallback = () => {};
let scheduledCallback: AnimationFrameRequestCallback | undefined;
const requestAnimationFrame: RequestAnimationFrameFunction = (callback) => {
scheduledCallback = callback;
return 0;
};
const context = new TestContext()
.withRequestAnimationFrameFunction(requestAnimationFrame);
// act
const { resetNextFrame } = context.useAnimationFrameLimiter();
resetNextFrame(expectedCallback);
// assert
expect(scheduledCallback).to.equal(expectedCallback);
});
it('cancels the existing animation frame before scheduling a new one', () => {
// arrange
const expectedCancelledAnimationFrameId: AnimationFrameId = 5;
let actualCancelledAnimationFrameId: AnimationFrameId | undefined;
const requestAnimationFrame: RequestAnimationFrameFunction = () => {
return expectedCancelledAnimationFrameId;
};
const cancelAnimationFrame: CancelAnimationFrameFunction = (animationFrameId) => {
actualCancelledAnimationFrameId = animationFrameId;
};
const context = new TestContext()
.withRequestAnimationFrameFunction(requestAnimationFrame)
.withCancelAnimationFrame(cancelAnimationFrame);
// act
const { resetNextFrame } = context.useAnimationFrameLimiter();
resetNextFrame(() => {}); // Nothing to cancel in first call
resetNextFrame(() => {});
// assert
expect(actualCancelledAnimationFrameId).to.equal(expectedCancelledAnimationFrameId);
});
});
describe('cancelNextFrame', () => {
it('cancels the scheduled animation frame if one exists', () => {
// arrange
const expectedCancelledAnimationFrameId: AnimationFrameId = 5;
let actualCancelledAnimationFrameId: AnimationFrameId | undefined;
const requestAnimationFrame: RequestAnimationFrameFunction = () => {
return expectedCancelledAnimationFrameId;
};
const cancelAnimationFrame: CancelAnimationFrameFunction = (animationFrameId) => {
actualCancelledAnimationFrameId = animationFrameId;
};
const context = new TestContext()
.withRequestAnimationFrameFunction(requestAnimationFrame)
.withCancelAnimationFrame(cancelAnimationFrame);
// act
const { resetNextFrame, cancelNextFrame } = context.useAnimationFrameLimiter();
resetNextFrame(() => {}); // Schedule the initial one
cancelNextFrame();
// assert
expect(actualCancelledAnimationFrameId).to.equal(expectedCancelledAnimationFrameId);
});
});
it('automatically cancels the animation frame on cleanup', () => {
// arrange
let actualCancelledAnimationFrameId: AnimationFrameId | undefined;
let actualCleanupCallback: (() => void) | undefined;
const onTeardownCallback: RegisterTeardownCallbackFunction = (cleanupCallback) => {
actualCleanupCallback = cleanupCallback;
};
const expectedCancelledAnimationFrameId: AnimationFrameId = 5;
const requestAnimationFrame: RequestAnimationFrameFunction = () => {
return expectedCancelledAnimationFrameId;
};
const cancelAnimationFrame: CancelAnimationFrameFunction = (animationFrameId) => {
actualCancelledAnimationFrameId = animationFrameId;
};
const testContext = new TestContext()
.withOnTeardownCallback(onTeardownCallback)
.withRequestAnimationFrameFunction(requestAnimationFrame)
.withCancelAnimationFrame(cancelAnimationFrame);
// act
const { resetNextFrame } = testContext.useAnimationFrameLimiter();
resetNextFrame(() => {}); // Schedule the initial one
// assert
expectExists(actualCleanupCallback);
actualCleanupCallback();
expect(actualCancelledAnimationFrameId).to.equal(expectedCancelledAnimationFrameId);
});
});
class TestContext {
private cancelAnimationFrame: CancelAnimationFrameFunction = () => {};
private requestAnimationFrameFunction: RequestAnimationFrameFunction = () => Math.random();
private onTeardownCallback: RegisterTeardownCallbackFunction = () => {};
public withRequestAnimationFrameFunction(
requestAnimationFrameFunction: RequestAnimationFrameFunction,
): this {
this.requestAnimationFrameFunction = requestAnimationFrameFunction;
return this;
}
public withCancelAnimationFrame(
cancelAnimationFrame: CancelAnimationFrameFunction,
): this {
this.cancelAnimationFrame = cancelAnimationFrame;
return this;
}
public withOnTeardownCallback(
registerCleanupCallback: RegisterTeardownCallbackFunction,
): this {
this.onTeardownCallback = registerCleanupCallback;
return this;
}
public useAnimationFrameLimiter(): ReturnType<typeof useAnimationFrameLimiter> {
return useAnimationFrameLimiter(
this.cancelAnimationFrame,
this.requestAnimationFrameFunction,
this.onTeardownCallback,
);
}
}

View File

@@ -0,0 +1,117 @@
import { shallowRef } from 'vue';
import { useResizeObserver, type LifecycleHookRegistration, type ObservedElementReference } from '@/presentation/components/Shared/Hooks/Resize/UseResizeObserver';
import { flushPromiseResolutionQueue } from '@tests/unit/shared/PromiseInspection';
import type { AnimationFrameLimiter } from '@/presentation/components/Shared/Hooks/Resize/UseAnimationFrameLimiter';
import { ThrottleStub } from '@tests/unit/shared/Stubs/ThrottleStub';
import { expectExists } from '@tests/shared/Assertions/ExpectExists';
describe('UseResizeObserver', () => {
it('registers observer once mounted', async () => {
// arrange
let registeredElement: Element | null = null;
const expectedElement = document.createElement('div');
const resizeObserverStub = createResizeObserverStub();
resizeObserverStub.observe = (element) => {
registeredElement = element;
};
// act
new TestContext()
.withObservedElementRef(shallowRef(expectedElement))
.withResizeObserver(resizeObserverStub)
.useResizeObserver();
await flushPromiseResolutionQueue();
// assert
expect(registeredElement).to.equal(expectedElement);
});
it('disposes observer once unmounted', async () => {
// arrange
let isObserverDisconnected = false;
const resizeObserverStub = createResizeObserverStub();
resizeObserverStub.disconnect = () => {
isObserverDisconnected = true;
};
let teardownCallback: (() => void) | undefined;
// act
new TestContext()
.withResizeObserver(resizeObserverStub)
.withOnTeardown((callback) => {
teardownCallback = callback;
})
.useResizeObserver();
await flushPromiseResolutionQueue();
expectExists(teardownCallback);
teardownCallback();
// assert
expect(isObserverDisconnected).to.equal(true);
});
});
function createResizeObserverStub(): ResizeObserver {
return {
disconnect: () => {},
observe: () => {},
unobserve: () => {},
};
}
function createFrameLimiterStub(): AnimationFrameLimiter {
return {
cancelNextFrame: () => {},
resetNextFrame: (callback) => { callback(); },
};
}
class TestContext {
private resizeObserver: ResizeObserver = createResizeObserverStub();
private observedElementRef: ObservedElementReference = shallowRef(document.createElement('div'));
private onSetup: LifecycleHookRegistration = (callback) => { callback(); };
private onTeardown: LifecycleHookRegistration = () => { };
public withResizeObserver(resizeObserver: ResizeObserver): this {
this.resizeObserver = resizeObserver;
return this;
}
public withObservedElementRef(observedElementRef: ObservedElementReference): this {
this.observedElementRef = observedElementRef;
return this;
}
public withOnSetup(onSetup: LifecycleHookRegistration): this {
this.onSetup = onSetup;
return this;
}
public withOnTeardown(onTeardown: LifecycleHookRegistration): this {
this.onTeardown = onTeardown;
return this;
}
public useResizeObserver() {
return useResizeObserver(
...this.buildParameters(),
);
}
private buildParameters(): Parameters<typeof useResizeObserver> {
return [
{
observedElementRef: this.observedElementRef,
throttleInMs: 50,
observeCallback: () => {},
},
() => ({
resizeObserverReady: Promise.resolve(() => this.resizeObserver),
}),
() => createFrameLimiterStub(),
new ThrottleStub()
.withImmediateExecution(true)
.func,
this.onSetup,
this.onTeardown,
];
}
}

View File

@@ -1,13 +1,13 @@
import type { CallbackType, throttle } from '@/application/Common/Timing/Throttle';
import type { CallbackType, ThrottleFunction } from '@/application/Common/Timing/Throttle';
export class ThrottleStub {
public readonly throttleInitializationCallArgs: Array<Parameters<typeof throttle>> = [];
public readonly throttleInitializationCallArgs: Array<Parameters<ThrottleFunction>> = [];
public readonly throttledFunctionCallArgs = new Array<readonly unknown[]>();
private executeImmediately: boolean = false;
public func = (callback: CallbackType, waitInMs: number): ReturnType<typeof throttle> => {
public func = (callback: CallbackType, waitInMs: number): ReturnType<ThrottleFunction> => {
this.throttleInitializationCallArgs.push([callback, waitInMs]);
return (...args: readonly unknown[]) => {
this.throttledFunctionCallArgs.push([...args]);