Compare commits
11 Commits
0.13.3
...
macosintel
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9a54c7e68 | ||
|
|
292362135d | ||
|
|
aae5434451 | ||
|
|
2390530d92 | ||
|
|
9ab3ff75b0 | ||
|
|
d25c4e8c81 | ||
|
|
4a7efa27c8 | ||
|
|
cec0b4b4f6 | ||
|
|
a1922c50c1 | ||
|
|
870120bc13 | ||
|
|
f38cf73485 |
57
.github/ISSUE_TEMPLATE/1-bug-report-scripts.md
vendored
57
.github/ISSUE_TEMPLATE/1-bug-report-scripts.md
vendored
@@ -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.
|
||||
-->
|
||||
114
.github/ISSUE_TEMPLATE/1-bug-report-scripts.yaml
vendored
Normal file
114
.github/ISSUE_TEMPLATE/1-bug-report-scripts.yaml
vendored
Normal 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>
|
||||
|
||||
---
|
||||
104
.github/ISSUE_TEMPLATE/2-bug-report-general.yaml
vendored
Normal file
104
.github/ISSUE_TEMPLATE/2-bug-report-general.yaml
vendored
Normal 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>
|
||||
|
||||
---
|
||||
55
.github/ISSUE_TEMPLATE/2-bug-report-generic.md
vendored
55
.github/ISSUE_TEMPLATE/2-bug-report-generic.md
vendored
@@ -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.
|
||||
-->
|
||||
36
.github/ISSUE_TEMPLATE/3-feature-request.md
vendored
36
.github/ISSUE_TEMPLATE/3-feature-request.md
vendored
@@ -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.
|
||||
-->
|
||||
73
.github/ISSUE_TEMPLATE/3-suggestion-feature.yaml
vendored
Normal file
73
.github/ISSUE_TEMPLATE/3-suggestion-feature.yaml
vendored
Normal 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>
|
||||
|
||||
---
|
||||
@@ -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.
|
||||
-->
|
||||
133
.github/ISSUE_TEMPLATE/4-suggestion-new-script.yaml
vendored
Normal file
133
.github/ISSUE_TEMPLATE/4-suggestion-new-script.yaml
vendored
Normal 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>
|
||||
|
||||
---
|
||||
6
.github/ISSUE_TEMPLATE/config.yml
vendored
6
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1 +1,7 @@
|
||||
# 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.
|
||||
|
||||
23
.github/workflows/checks.build.yaml
vendored
23
.github/workflows/checks.build.yaml
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
2
.github/workflows/checks.quality.yaml
vendored
2
.github/workflows/checks.quality.yaml
vendored
@@ -4,7 +4,7 @@ on: [ push, pull_request ]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.os }}-latest
|
||||
strategy:
|
||||
matrix:
|
||||
lint-command:
|
||||
|
||||
27
CHANGELOG.md
27
CHANGELOG.md
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
263
package-lock.json
generated
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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: {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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 };
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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\//`
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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')),
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
|
||||
Reference in New Issue
Block a user