Compare commits

...

39 Commits

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

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

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

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

Other changes include:

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

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

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

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

This commit removes following third-party production dependencies:

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

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

Changes:

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

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

Changes include:

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

On Windows:

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

On Linux:

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

Changes include:

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

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

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

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

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

Changes include:

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

- Enable artifact output validation for Dockerfile.
- Correct the path references in Dockerfile for the distribution
  directory.
- Add Dockerfile specific indentation rules to `.editorconfig`.
- Use `npm run install-deps` for dependency installation, enhancing
  build reliability.
- Add automation script `verify-web-server-status.js` to verify running
  web server on given URL.
- Introduce automated build verification for Dockerfile:
  - On macOS, install Docker with colima as the container runtime
    because default agents do not include Docker and Docker runtime is
    not installed due to licensing issues (see actions/runner-images#17).
  - On Windows, there's no Linux container support (actions/runner#904,
    actions/runner-images#1143), so keep the checks for macOS and Ubuntu
    only.
2023-09-27 19:53:40 +02:00
undergroundwires-bot
5047c9b6e7 ⬆️ bump everywhere to 0.12.4 2023-09-26 11:45:04 +00:00
undergroundwires
bd2082e8c5 Fix slow appearance of nodes on tree view
The tree view rendering performance is optimized by improving the node
render queue ordering. The node rendering order is modified based on the
expansion state and the depth in the hierarchy, leading to faster
rendering of visible nodes. This optimization is applied when the tree
nodes are not expanded to improve the rendering speed.

This new ordering ensures that nodes are rendered more efficiently,
prioritizing nodes that are collapsed and are at a higher level in the
hierarchy.
2023-09-25 14:21:29 +02:00
undergroundwires
8f188acd3c Fix loss of tree node state when switching views
This commit fixes an issue where the check state of categories was lost
when toggling between card and tree views. This is solved by immediately
emitting node state changes for all nodes. This ensures consistent view
transitions without any loss of node state information.

Furthermore, this commit includes added unit tests for the modified code
sections.
2023-09-24 20:34:47 +02:00
undergroundwires
0303ef2fd9 Fix outdated and broken links in README #161
This commit fixes issues with download URLs of desktop application
artifacts on README.md

- Corrected typo in Linux AppImage link
- Updated older version links to the newest release

Co-authored-by: MrEddX <66828538+MrEddX@users.noreply.github.com>
2023-09-23 10:33:46 +02:00
undergroundwires
cb21a970b6 win: fix Defender scan artifacts removal #246
- Modify script to run as `TrustedInstaller`, resolving access right
  problems discussed in #246.
- Change script name for better alignment with its functionality.
- Improve script description for clarity and detailed documentation.
2023-09-22 14:11:52 +02:00
undergroundwires
203daeb4a2 win: fix delivery optimization side-effects #173
- Add non-intrusive way to disable delivery optimization. This new
  script do not introduce side-effects caused by disabling Delivery
  Optimization service.
- Recomend delivery optimization service (`DoSvc`) only on Strict
  mode, removing it from Standard recommendation.
- Categorize delivery optimization disabling under one category.
- Move disabling delivery optimization to "Disable OS collection" >
  "Disable Windows Update data collection".
- Add more documentation.
2023-09-21 11:40:15 +02:00
undergroundwires
60dde11311 win: fix uninstallation of newer Edge #236
- Fix script failing when multiple installations of Edge is found.
- Fix Edge not being able to be uninstalled due in newer Edge versions.
- Add documentation
- Add missing revert script
2023-09-20 07:48:50 +02:00
undergroundwires
8b930fc57c Rewrite tooltip UI for efficiency and Vue 3.0 #230
- Introduce a new UI component for tooltips.
- Fix tooltip arrow misalignment issues in code download/execution
  instructions dialogs.

Reasons for dropping `v-tooltip` dependency:

- Lack of support for Vue 3.0, which blocks migration to Vue 3.0 (see
  #230).
- Inability to render HTML content that's required for privacy.sexy.
- Inefficient, adding an extra 162.48 KB to the production bundle for
  web distribution (tested using `npm run build -- --mode production`).

Advantages of adopting `floating-ui` (Floating UI):

- Compatibility across multiple Vue versions including 2.0, 2.7, and 3.0.
- Reduced boilerplate resulting in cleaner, more maintainable code.
- Efficient position recalculations without reinventing the wheel.
2023-09-18 17:57:50 +02:00
undergroundwires
f810ed0c14 Fix no spacing after lists in documentation text
This commit adds missing vertical margin paragraphs that appear after
lists. It also changes vertical margin gap to match the font size along
with refactoring that makes paragraph gap modification easier to
understand.
2023-09-17 13:38:40 +02:00
undergroundwires
53222fd83c Fix compiler bug with nested optional arguments
This commit fixes compiler bug where it fails when optional values are
compiled into absent values in nested calls.

- Throw exception with more context for easier future debugging.
- Add better validation of argument values for nested calls.
- Refactor `FunctionCallCompiler` for better clarity and modularize it
  to make it more maintainable and testable.
- Refactor related interface to not have `I` prefix, and
  function/variable names for better clarity.

Context:

Discovered this issue while attempting to call
`RunInlineCodeAsTrustedInstaller` which in turn invokes `RunPowerShell`
for issue #246. This led to the realization that despite parameters
flagged as optional, the nested argument compilation didn't support
them.
2023-09-16 16:11:41 +02:00
undergroundwires
a1f2497381 Fix wrong action path in website CI deployment 2023-09-15 13:36:05 +02:00
Couleur
c27172c32e win: refactor update.mode key for VSCode #215
Removed unnecessary single quotes wrapping the value `manual` in yaml.
2023-09-14 12:47:33 +02:00
undergroundwires
6e9b65d8b1 win: fix, improve disabling automatic updates #252
- Add script to disable `WaaSMedicSvc` service (#252)
- Refine script granularity for more precise control.
- Introduce detailed documentation for the category and associated
  scripts.
- Fix `ScheduledInstallTime` being set to `3` which schedules updates to
  install at 3 AM.
- Fix `ScheduledInstallDay` is being set to `0` which schedules daily
  update installation.
- Fix `NoAutoUpdate` being set to `0` (enable) instead of `1` (disable).
- Add disabling of missing `wuauserv` service.
- Add parent category for disabling Windows update services for better
  organization.
2023-09-13 13:18:14 +02:00
billy
6d301f9961 win: fix Edge telemetry disabling for v116+ #242 2023-09-12 13:28:22 +02:00
undergroundwires
659fea7afc win: fix Windows spotlight revert, docs, recommend
- Move disabling Windows Spotlight from Standard to Strict
  recommendation due to unexpected behavior for some users (#65).
- Enhance documentation.
- Correct revert code to ensure return to the default OS state.
2023-09-11 14:08:33 +02:00
undergroundwires-bot
e0303058a3 ⬆️ bump everywhere to 0.12.3 2023-09-10 11:21:25 +00:00
174 changed files with 13779 additions and 8782 deletions

View File

@@ -5,3 +5,7 @@ end_of_line = lf
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
max_line_length = 100 max_line_length = 100
[{Dockerfile}]
indent_style = space
indent_size = 4

View File

@@ -8,4 +8,5 @@ runs:
- -
name: Run `npm ci` with retries name: Run `npm ci` with retries
shell: bash shell: bash
run: npm run install-deps -- --ci --root-directory "${{ inputs.working-directory }}" run: npm run install-deps -- --ci
working-directory: ${{ inputs.working-directory }}

View File

@@ -3,6 +3,6 @@ runs:
steps: steps:
- -
name: Setup node name: Setup node
uses: actions/setup-node@v2 uses: actions/setup-node@v3
with: with:
node-version: 16.x node-version: 16.x

View File

@@ -1,4 +1,4 @@
name: build-checks name: checks.build
on: on:
push: push:
@@ -21,7 +21,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- -
name: Setup node name: Setup node
uses: ./.github/actions/setup-node uses: ./.github/actions/setup-node
@@ -49,7 +49,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- -
name: Setup node name: Setup node
uses: ./.github/actions/setup-node uses: ./.github/actions/setup-node
@@ -68,3 +68,33 @@ jobs:
- -
name: Verify bundled desktop build artifacts name: Verify bundled desktop build artifacts
run: npm run check:verify-build-artifacts -- --electron-bundled run: npm run check:verify-build-artifacts -- --electron-bundled
build-docker:
strategy:
matrix:
os: [ macos, ubuntu ] # Windows runners do not support Linux containers
fail-fast: false # Allows to see results from other combinations
runs-on: ${{ matrix.os }}-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Install Docker on macOS
if: matrix.os == 'macos' # macOS runner is missing Docker
run: |-
# Install Docker
brew install docker
# Docker on macOS misses daemon due to licensing, so install colima as runtime
brew install colima
# Start the daemon
colima start
-
name: Build Docker image
run: docker build -t undergroundwires/privacy.sexy:latest .
-
name: Run Docker image on port 8080
run: docker run -d -p 8080:80 --rm --name privacy.sexy undergroundwires/privacy.sexy:latest
-
name: Check server is up and returns HTTP 200
run: node ./scripts/verify-web-server-status.js --url http://localhost:8080

View File

@@ -15,7 +15,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- -
name: Setup node name: Setup node
uses: ./.github/actions/setup-node uses: ./.github/actions/setup-node

View File

@@ -10,7 +10,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- -
name: Setup node name: Setup node
uses: ./.github/actions/setup-node uses: ./.github/actions/setup-node

View File

@@ -18,7 +18,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- -
name: Setup node name: Setup node
uses: ./.github/actions/setup-node uses: ./.github/actions/setup-node

View File

@@ -14,7 +14,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- -
name: Setup node name: Setup node
uses: ./.github/actions/setup-node uses: ./.github/actions/setup-node
@@ -42,7 +42,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- -
name: Setup node name: Setup node
uses: ./.github/actions/setup-node uses: ./.github/actions/setup-node

View File

@@ -1,4 +1,4 @@
name: security-checks name: checks.security.dependencies
on: on:
push: push:
@@ -13,7 +13,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- -
name: Setup node name: Setup node
uses: ./.github/actions/setup-node uses: ./.github/actions/setup-node

View File

@@ -0,0 +1,42 @@
name: checks.security.sast
on:
push:
pull_request:
schedule:
- cron: '0 0 * * 0' # at 00:00 on every Sunday
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [
javascript # analyzes code written in JavaScript, TypeScript and both.
]
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
queries: +security-and-quality
-
name: Autobuild
uses: github/codeql-action/autobuild@v2
-
name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{ matrix.language }}"

View File

@@ -14,7 +14,7 @@ jobs:
runs-on: ${{ matrix.os }}-latest runs-on: ${{ matrix.os }}-latest
steps: steps:
- -
uses: actions/checkout@v2 uses: actions/checkout@v4
with: with:
ref: master # otherwise it defaults to the version tag missing bump commit ref: master # otherwise it defaults to the version tag missing bump commit
fetch-depth: 0 # fetch all history fetch-depth: 0 # fetch all history

View File

@@ -10,7 +10,7 @@ jobs:
steps: steps:
- -
name: "Infrastructure: Checkout" name: "Infrastructure: Checkout"
uses: actions/checkout@v2 uses: actions/checkout@v4
with: with:
path: aws path: aws
repository: undergroundwires/aws-static-site-with-cd repository: undergroundwires/aws-static-site-with-cd
@@ -75,7 +75,7 @@ jobs:
working-directory: aws working-directory: aws
- -
name: "App: Checkout" name: "App: Checkout"
uses: actions/checkout@v2 uses: actions/checkout@v4
with: with:
path: app path: app
ref: master # otherwise we don't get version bump commit ref: master # otherwise we don't get version bump commit
@@ -84,7 +84,7 @@ jobs:
uses: ./app/.github/actions/setup-node uses: ./app/.github/actions/setup-node
- -
name: "App: Install dependencies" name: "App: Install dependencies"
uses: ./.github/actions/npm-install-dependencies uses: ./app/.github/actions/npm-install-dependencies
with: with:
working-directory: app working-directory: app
- -
@@ -102,7 +102,7 @@ jobs:
- -
name: "App: Deploy to S3" name: "App: Deploy to S3"
shell: bash shell: bash
run: >- run: |-
declare web_output_dir declare web_output_dir
if ! web_output_dir=$(cd app && node scripts/print-dist-dir.js --web); then if ! web_output_dir=$(cd app && node scripts/print-dist-dir.js --web); then
echo 'Error: Could not determine distribution directory.' echo 'Error: Could not determine distribution directory.'

View File

@@ -14,7 +14,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- -
name: Setup node name: Setup node
uses: ./.github/actions/setup-node uses: ./.github/actions/setup-node

View File

@@ -16,7 +16,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- -
name: Setup node name: Setup node
uses: ./.github/actions/setup-node uses: ./.github/actions/setup-node

View File

@@ -14,7 +14,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- -
name: Set-up node name: Set-up node
uses: ./.github/actions/setup-node uses: ./.github/actions/setup-node

View File

@@ -1,5 +1,61 @@
# Changelog # Changelog
## 0.12.5 (2023-10-13)
* Fix Docker build and improve checks #220 | [7669985](https://github.com/undergroundwires/privacy.sexy/commit/7669985f8e1446e726a95626ecf35b3ce6b60a16)
* Add SAST security checks with SECURITY.md #178 | [3e5239f](https://github.com/undergroundwires/privacy.sexy/commit/3e5239f7d35e57749c01adf3dbbcd365aebb39c8)
* Add Scoop download instructions #174 | [cf55ca9](https://github.com/undergroundwires/privacy.sexy/commit/cf55ca9e28b064fa7a516077a9da23e3a8e3f534)
* win: fix and improve temp dir cleanup #176, #89 | [d457504](https://github.com/undergroundwires/privacy.sexy/commit/d45750428cca010daf2721b33a8ae3a01b28813b)
* win, linux: improve VSCode setting robustness #196 | [e8a52f7](https://github.com/undergroundwires/privacy.sexy/commit/e8a52f717dc799b34ceeb1c27c2b8219391dff6a)
* linux: fix obsolete Firefox DPI script #239 | [e5f6edf](https://github.com/undergroundwires/privacy.sexy/commit/e5f6edf405bcec7c29ea4d7932d1910620fa15f8)
* win: add removal of Edge assocations #64 | [888c916](https://github.com/undergroundwires/privacy.sexy/commit/888c9166fc66a2094137fa8be739cc21bafef5f6)
* win: improve Edge & OneDrive shortcut removal #73 | [8501495](https://github.com/undergroundwires/privacy.sexy/commit/8501495c170af61913288a63dbd369db5bbc5003)
* win: relocate and document SecHealthUI #190 | [2862951](https://github.com/undergroundwires/privacy.sexy/commit/286295128d0179358e0c6b7b6415d752175a1aed)
* Add developer toolkit UI component | [2147eae](https://github.com/undergroundwires/privacy.sexy/commit/2147eae687b82d05bc43bb4605d9068f148bb92a)
* win: fix and improve network data usage reset #265 | [5e359c2](https://github.com/undergroundwires/privacy.sexy/commit/5e359c2fb82a08e6acf7159b70ca86a8234b359b)
* win: improve app reversion and docs #260 | [a3f11df](https://github.com/undergroundwires/privacy.sexy/commit/a3f11dff187c821a00910c20dac05e285cda9073)
* Fix working directory in CI/CD web release | [698b570](https://github.com/undergroundwires/privacy.sexy/commit/698b570ee6e300d6703015464f4345b5e706f1cb)
* Implement new UI component for icons #230 | [48730bc](https://github.com/undergroundwires/privacy.sexy/commit/48730bca0506120bca4bf3a23545d59f2b1a9009)
* win: fix and improve AppCompat disabling #255 | [bab6316](https://github.com/undergroundwires/privacy.sexy/commit/bab6316e7625230cf4a4cf67c3aca417347db75c)
* win, linux, mac: fix typos and improve naming | [67c3677](https://github.com/undergroundwires/privacy.sexy/commit/67c3677621b201525a813e8a26f07d607176e89b)
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.12.4...0.12.5)
## 0.12.4 (2023-09-25)
* win: fix Windows spotlight revert, docs, recommend | [659fea7](https://github.com/undergroundwires/privacy.sexy/commit/659fea7afcabcd0ea273cfdcc8c4bae190c126f3)
* win: fix Edge telemetry disabling for v116+ #242 | [6d301f9](https://github.com/undergroundwires/privacy.sexy/commit/6d301f99616ed49975876803d0098eafe4d3cb2e)
* win: fix, improve disabling automatic updates #252 | [6e9b65d](https://github.com/undergroundwires/privacy.sexy/commit/6e9b65d8b1b481c1471dde90876c37838b4ac4e5)
* win: refactor `update.mode` key for VSCode #215 | [c27172c](https://github.com/undergroundwires/privacy.sexy/commit/c27172c32e7c316b7cb0f44cab611eed89ca034e)
* Fix wrong action path in website CI deployment | [a1f2497](https://github.com/undergroundwires/privacy.sexy/commit/a1f24973813ccbdd7e1f06c64e1912a991a6bb64)
* Fix compiler bug with nested optional arguments | [53222fd](https://github.com/undergroundwires/privacy.sexy/commit/53222fd83c2846089746a217482195806f960d18)
* Fix no spacing after lists in documentation text | [f810ed0](https://github.com/undergroundwires/privacy.sexy/commit/f810ed0c147c2a46cae3b70b635ed81128646fff)
* Rewrite tooltip UI for efficiency and Vue 3.0 #230 | [8b930fc](https://github.com/undergroundwires/privacy.sexy/commit/8b930fc57c8ee6691ed6165bcb27d97e64a1a0c0)
* win: fix uninstallation of newer Edge #236 | [60dde11](https://github.com/undergroundwires/privacy.sexy/commit/60dde11311a2409537f5965f370b0daaaec53339)
* win: fix delivery optimization side-effects #173 | [203daeb](https://github.com/undergroundwires/privacy.sexy/commit/203daeb4a2fca0a0295cbc2a736394f9f87725e6)
* win: fix Defender scan artifacts removal #246 | [cb21a97](https://github.com/undergroundwires/privacy.sexy/commit/cb21a970b6b867e1476a5eb8a72b9a7fdd53a744)
* Fix outdated and broken links in README #161 | [0303ef2](https://github.com/undergroundwires/privacy.sexy/commit/0303ef2fd98b36306523e2a0c5f5ae812a4c6c99)
* Fix loss of tree node state when switching views | [8f188ac](https://github.com/undergroundwires/privacy.sexy/commit/8f188acd3c2d93e40c89569c74bc5cff992f0052)
* Fix slow appearance of nodes on tree view | [bd2082e](https://github.com/undergroundwires/privacy.sexy/commit/bd2082e8c574db065bb4462f30ea3ace2cb028cb)
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.12.3...0.12.4)
## 0.12.3 (2023-09-09)
* linux: use user.js over prefs.js for Firefox #232 | [dae6d11](https://github.com/undergroundwires/privacy.sexy/commit/dae6d114daab6857d773071211eb57619b136281)
* win: fix typo in Defender retention script #213 | [35be05d](https://github.com/undergroundwires/privacy.sexy/commit/35be05df2094ea8bba4ee4725e6fa4956a79493d)
* Improve desktop runtime execution tests | [ad0576a](https://github.com/undergroundwires/privacy.sexy/commit/ad0576a752f8fd6ea2f917a59173fe61f9951246)
* Fix Windows artifact naming in desktop packaging | [f4d86fc](https://github.com/undergroundwires/privacy.sexy/commit/f4d86fccfd0e73e94c8c6e400a33514900bc5abe)
* Refactor and improve external URL checks | [19e42c9](https://github.com/undergroundwires/privacy.sexy/commit/19e42c9c52a18c813ded4265e687e01032cdd4c8)
* Fix memory leaks via auto-unsubscribing and DI | [eb096d0](https://github.com/undergroundwires/privacy.sexy/commit/eb096d07e276e1b4c8040220c47f186d02841e14)
* Refactor build configs and improve CI/CD checks | [0a2a1a0](https://github.com/undergroundwires/privacy.sexy/commit/0a2a1a026b0efb29624be82b06536c518c1ea439)
* Introduce retry mechanism for npm install in CI/CD | [4beb1bb](https://github.com/undergroundwires/privacy.sexy/commit/4beb1bb5748a60886210187ca3cdc7f4b41067c0)
* win: fix disable recent apps revert #211, #248 | [4ce327e](https://github.com/undergroundwires/privacy.sexy/commit/4ce327eb6af542ed2916d649553e5e1ba5833882)
* Change license to AGPLv3 | [821cc62](https://github.com/undergroundwires/privacy.sexy/commit/821cc62c4c8347cb76d041f82f574754e4d948c5)
* Introduce new TreeView UI component | [65f121c](https://github.com/undergroundwires/privacy.sexy/commit/65f121c451af87315e1c91df4198562e0445b2c2)
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.12.2...0.12.3)
## 0.12.2 (2023-08-25) ## 0.12.2 (2023-08-25)
* Add automated checks for desktop app runtime #233 | [04b3133](https://github.com/undergroundwires/privacy.sexy/commit/04b3133500485d0d278a81a177a1677134131405) * Add automated checks for desktop app runtime #233 | [04b3133](https://github.com/undergroundwires/privacy.sexy/commit/04b3133500485d0d278a81a177a1677134131405)

View File

@@ -1,13 +1,16 @@
# Build # Build
FROM node:lts-alpine as build-stage FROM node:lts-alpine AS build-stage
WORKDIR /app WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . . COPY . .
RUN npm run build RUN npm run install-deps
RUN npm run build \
&& npm run check:verify-build-artifacts -- --web
RUN mkdir /dist \
&& dist_directory=$(node 'scripts/print-dist-dir.js' --web) \
&& cp -a "${dist_directory}/." '/dist'
# Production stage # Production stage
FROM nginx:stable-alpine as production-stage FROM nginx:stable-alpine AS production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html COPY --from=build-stage /dist /usr/share/nginx/html
EXPOSE 80 EXPOSE 80
CMD ["nginx", "-g", "daemon off;"] CMD ["nginx", "-g", "daemon off;"]

View File

@@ -16,14 +16,6 @@
src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat" src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat"
/> />
</a> </a>
<!-- Code quality -->
<br />
<a href="https://lgtm.com/projects/g/undergroundwires/privacy.sexy/context:javascript" target="_blank" rel="noopener noreferrer">
<img
alt="Language grade: JavaScript/TypeScript"
src="https://img.shields.io/lgtm/grade/javascript/g/undergroundwires/privacy.sexy.svg?logo=lgtm&logoWidth=18"
/>
</a>
<a href="https://codeclimate.com/github/undergroundwires/privacy.sexy/maintainability" target="_blank" rel="noopener noreferrer"> <a href="https://codeclimate.com/github/undergroundwires/privacy.sexy/maintainability" target="_blank" rel="noopener noreferrer">
<img <img
alt="Maintainability" alt="Maintainability"
@@ -50,6 +42,20 @@
src="https://github.com/undergroundwires/privacy.sexy/workflows/e2e-tests/badge.svg" src="https://github.com/undergroundwires/privacy.sexy/workflows/e2e-tests/badge.svg"
/> />
</a> </a>
<!-- Security checks -->
<br />
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.security.sast.yaml" target="_blank" rel="noopener noreferrer">
<img
alt="Status of dependency security checks"
src="https://github.com/undergroundwires/privacy.sexy/workflows/checks.security.sast/badge.svg"
/>
</a>
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.security.dependencies.yaml" target="_blank" rel="noopener noreferrer">
<img
alt="Status of Static Analysis Security Testing (SAST)"
src="https://github.com/undergroundwires/privacy.sexy/workflows/checks.security.dependencies/badge.svg"
/>
</a>
<!-- Checks --> <!-- Checks -->
<br /> <br />
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.quality.yaml" target="_blank" rel="noopener noreferrer"> <a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.quality.yaml" target="_blank" rel="noopener noreferrer">
@@ -58,16 +64,10 @@
src="https://github.com/undergroundwires/privacy.sexy/workflows/quality-checks/badge.svg" src="https://github.com/undergroundwires/privacy.sexy/workflows/quality-checks/badge.svg"
/> />
</a> </a>
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.security.yaml" target="_blank" rel="noopener noreferrer">
<img
alt="Security checks status"
src="https://github.com/undergroundwires/privacy.sexy/workflows/security-checks/badge.svg"
/>
</a>
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.build.yaml" target="_blank" rel="noopener noreferrer"> <a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.build.yaml" target="_blank" rel="noopener noreferrer">
<img <img
alt="Build checks status" alt="Status of build checks"
src="https://github.com/undergroundwires/privacy.sexy/workflows/build-checks/badge.svg" src="https://github.com/undergroundwires/privacy.sexy/workflows/checks.build/badge.svg"
/> />
</a> </a>
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.desktop-runtime-errors.yaml" target="_blank" rel="noopener noreferrer"> <a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.desktop-runtime-errors.yaml" target="_blank" rel="noopener noreferrer">
@@ -122,7 +122,7 @@
## Get started ## Get started
- 🌍️ **Online**: [https://privacy.sexy](https://privacy.sexy). - 🌍️ **Online**: [https://privacy.sexy](https://privacy.sexy).
- 🖥️ **Offline**: Check [releases page](https://github.com/undergroundwires/privacy.sexy/releases), or download directly for: [Windows](https://github.com/undergroundwires/privacy.sexy/releases/download/0.11.2/privacy.sexy-Setup-0.11.2.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.11.2/privacy.sexy-0.11.2.dmg), [Linux](https://github.com/undergroundwires/pr.vacy.sexy/releases/download/0.11.2/privacy.sexy-0.11.2.AppImage). - 🖥️ **Offline**: Download directly for: [Windows](https://github.com/undergroundwires/privacy.sexy/releases/download/0.12.5/privacy.sexy-Setup-0.12.5.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.12.5/privacy.sexy-0.12.5.dmg), [Linux](https://github.com/undergroundwires/privacy.sexy/releases/download/0.12.5/privacy.sexy-0.12.5.AppImage). For more options, see [here](#additional-install-options).
Online version does not require to run any software on your computer. Offline version has more functions such as running the scripts directly. Online version does not require to run any software on your computer. Offline version has more functions such as running the scripts directly.
@@ -150,6 +150,16 @@ Online version does not require to run any software on your computer. Offline ve
**Contribute 👷**. Contributions of any type are welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) as the starting point. It includes useful information like [how to add new scripts](./CONTRIBUTING.md#extend-scripts). **Contribute 👷**. Contributions of any type are welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) as the starting point. It includes useful information like [how to add new scripts](./CONTRIBUTING.md#extend-scripts).
## Additional Install Options
- Check the [releases page](https://github.com/undergroundwires/privacy.sexy/releases) for all available versions.
- Using [Scoop](https://scoop.sh/#/apps?q=privacy.sexy&s=2&d=1&o=true) package manager on Windows:
```powershell
scoop bucket add extras
scoop install privacy.sexy
```
## Development ## Development
Refer to [development.md](./docs/development.md) for Docker usage and reading more about setting up your development environment. Refer to [development.md](./docs/development.md) for Docker usage and reading more about setting up your development environment.
@@ -157,3 +167,7 @@ Refer to [development.md](./docs/development.md) for Docker usage and reading mo
Check [architecture.md](./docs/architecture.md) for an overview of design and how different parts and layers work together. You can refer to [application.md](./docs/application.md) for a closer look at application layer codebase and [presentation.md](./docs/presentation.md) for code related to GUI layer. [collection-files.md](./docs/collection-files.md) explains the YAML files that are the core of the application and [templating.md](./docs/templating.md) documents how to use templating language in those files. In [ci-cd.md](./docs/ci-cd.md), you can read more about the pipelines that automates maintenance tasks and ensures you get what see. Check [architecture.md](./docs/architecture.md) for an overview of design and how different parts and layers work together. You can refer to [application.md](./docs/application.md) for a closer look at application layer codebase and [presentation.md](./docs/presentation.md) for code related to GUI layer. [collection-files.md](./docs/collection-files.md) explains the YAML files that are the core of the application and [templating.md](./docs/templating.md) documents how to use templating language in those files. In [ci-cd.md](./docs/ci-cd.md), you can read more about the pipelines that automates maintenance tasks and ensures you get what see.
[docs/](./docs/) folder includes all other documentation. [docs/](./docs/) folder includes all other documentation.
## Security
Security is a top priority at privacy.sexy. An extensive commitment to security verification ensures this priority. For any security concerns or vulnerabilities, please consult the [Security Policy](./SECURITY.md).

31
SECURITY.md Normal file
View File

@@ -0,0 +1,31 @@
# Security Policy
privacy.sexy takes security seriously. Commitment is made to address all security issues with urgency. Responsible reporting of any discovered vulnerabilities in the project is highly encouraged.
## Reporting a Vulnerability
Efforts to responsibly disclose findings are greatly appreciated. To report a security vulnerability, follow these steps:
- For general vulnerabilities, [open an issue](https://github.com/undergroundwires/privacy.sexy/issues/new/choose) using the bug report template.
- For sensitive matters, [contact the developer directly](https://undergroundwires.dev).
## Security Report Handling
Upon receipt of a security report, the following actions will be taken:
- The report will be confirmed, identifying the affected components.
- The impact and severity of the issue will be assessed.
- Work on a fix and plan a release to address the vulnerability will be initiated.
- The reporter will be kept updated about the progress.
## Testing
Regular and extensive testing is conducted to ensure robust security in the project. Information about testing practices can be found in the [Testing Documentation](./docs/tests.md).
## Support
For additional assistance or any unanswered questions, [submit a GitHub issue](https://github.com/undergroundwires/privacy.sexy/issues/new/choose). Security concerns are a priority, and necessary support to address them is assured.
---
Active contribution to the safety and security of privacy.sexy is thanked. This collaborative effort keeps the project resilient and trustworthy for all.

View File

@@ -6,7 +6,10 @@ const CYPRESS_BASE_DIR = 'tests/e2e/';
export default defineConfig({ export default defineConfig({
fixturesFolder: `${CYPRESS_BASE_DIR}/fixtures`, fixturesFolder: `${CYPRESS_BASE_DIR}/fixtures`,
screenshotsFolder: `${CYPRESS_BASE_DIR}/screenshots`, screenshotsFolder: `${CYPRESS_BASE_DIR}/screenshots`,
video: true,
videosFolder: `${CYPRESS_BASE_DIR}/videos`, videosFolder: `${CYPRESS_BASE_DIR}/videos`,
e2e: { e2e: {
baseUrl: `http://localhost:${ViteConfig.server.port}/`, baseUrl: `http://localhost:${ViteConfig.server.port}/`,
specPattern: `${CYPRESS_BASE_DIR}/**/*.cy.{js,jsx,ts,tsx}`, // Default: cypress/e2e/**/*.cy.{js,jsx,ts,tsx} specPattern: `${CYPRESS_BASE_DIR}/**/*.cy.{js,jsx,ts,tsx}`, // Default: cypress/e2e/**/*.cy.{js,jsx,ts,tsx}

View File

@@ -174,3 +174,19 @@
- `endCode:` *`string`* (**required**) - `endCode:` *`string`* (**required**)
- Code that'll be inserted at the end of user created script. - Code that'll be inserted at the end of user created script.
- Global variables such as `$homepage`, `$version`, `$date` can be used using [parameter substitution](./templating.md#parameter-substitution) code syntax such as `Welcome to {{ $homepage }}!` - Global variables such as `$homepage`, `$version`, `$date` can be used using [parameter substitution](./templating.md#parameter-substitution) code syntax such as `Welcome to {{ $homepage }}!`
## Naming guidelines
- Prioritize consistency throughout all names.
- Use an instruction format like "do this, do that" for clear, direct guidance. This approach reduces potential confusion and offers easy-to-follow steps. It provides specific, unambiguous instructions.
- Ensure brand names adhere to their official casing.
- Choose clear and uncomplicated language.
- Favor the terms:
- "Disable" over "Turn off"
- "Configure" over "Set up"
- "Clear" over "Erase" or "Clean"
- "Minimize" over "Limit" or "Reduce" (when it enhances clarity)
- "Remove" over "Uninstall"
- Structure your phrases for clarity.
- For instance, "Disable XX telemetry" or "Clear XX data" are preferred over "Clear data from XX", "Disable telemetry in XX", or "Clear data of XX".
- Use sentence case rather than Title Case.

View File

@@ -60,6 +60,7 @@ See [ci-cd.md](./ci-cd.md) for more information.
1. Build: `docker build -t undergroundwires/privacy.sexy:latest .` 1. Build: `docker build -t undergroundwires/privacy.sexy:latest .`
2. Run: `docker run -it -p 8080:80 --rm --name privacy.sexy undergroundwires/privacy.sexy:latest` 2. Run: `docker run -it -p 8080:80 --rm --name privacy.sexy undergroundwires/privacy.sexy:latest`
3. Application should be available at [`http://localhost:8080`](http://localhost:8080)
### Building ### Building
@@ -81,11 +82,12 @@ See [ci-cd.md](./ci-cd.md) for more information.
#### Automation scripts #### Automation scripts
- [**`node scripts/print-dist-dir.js [-- <options>]`**](../scripts/print-dist-dir.js): - [**`node scripts/print-dist-dir.js [<options>]`**](../scripts/print-dist-dir.js):
- Determines the absolute path of a distribution directory based on CLI arguments and outputs its absolute path. - Determines the absolute path of a distribution directory based on CLI arguments and outputs its absolute path.
- Primarily used by automation scripts.
- [**`npm run check:verify-build-artifacts [-- <options>]`**](../scripts/verify-build-artifacts.js): - [**`npm run check:verify-build-artifacts [-- <options>]`**](../scripts/verify-build-artifacts.js):
- Verifies the existence and content of build artifacts. Useful for ensuring that the build process is generating the expected output. - Verifies the existence and content of build artifacts. Useful for ensuring that the build process is generating the expected output.
- [**`node scripts/verify-web-server-status.js --url [URL]`**](../scripts/verify-web-server-status.js):
- Checks if a specified server is up with retries and returns an HTTP 200 status code.
## Recommended extensions ## Recommended extensions

View File

@@ -20,7 +20,6 @@ The presentation layer uses an event-driven architecture for bidirectional react
- [**`fonts/`**](./../src/presentation/assets/fonts/): Contains fonts. - [**`fonts/`**](./../src/presentation/assets/fonts/): Contains fonts.
- [**`styles/`**](./../src/presentation/assets/styles/): Contains shared styles. - [**`styles/`**](./../src/presentation/assets/styles/): Contains shared styles.
- [**`components/`**](./../src/presentation/assets/styles/components): Contains styles coupled to Vue components. - [**`components/`**](./../src/presentation/assets/styles/components): Contains styles coupled to Vue components.
- [**`vendors-extensions/`**](./../src/presentation/assets/styles/third-party-extensions): Contains styles for third-party components.
- [**`main.scss`**](./../src/presentation/assets/styles/main.scss): Main Sass file, imported by other components as single entrypoint. - [**`main.scss`**](./../src/presentation/assets/styles/main.scss): Main Sass file, imported by other components as single entrypoint.
- [**`main.ts`**](./../src/presentation/main.ts): Starts Vue app. - [**`main.ts`**](./../src/presentation/main.ts): Starts Vue app.
- [**`electron/`**](./../src/presentation/electron/): Contains Electron code. - [**`electron/`**](./../src/presentation/electron/): Contains Electron code.

View File

@@ -56,6 +56,11 @@ These checks validate various qualities like runtime execution, building process
- Use [various tools](./../package.json) and [scripts](./../scripts). - Use [various tools](./../package.json) and [scripts](./../scripts).
- Are automatically executed as [GitHub workflows](./../.github/workflows). - Are automatically executed as [GitHub workflows](./../.github/workflows).
### Security checks
- [`checks.security.sast`](./../.github/workflows/checks.security.sast.yaml): Utilizes CodeQL to conduct Static Analysis Security Testing (SAST) to ensure the secure integrity of the codebase.
- [`checks.security.dependencies`](./../.github/workflows/checks.security.dependencies.yaml): Performs audits on third-party dependencies to identify and mitigate potential vulnerabilities, safeguarding the project from exploitable weaknesses.
## Tests structure ## Tests structure
- [`package.json`](./../package.json): Defines test commands and includes tools used in tests. - [`package.json`](./../package.json): Defines test commands and includes tools used in tests.

9527
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "privacy.sexy", "name": "privacy.sexy",
"version": "0.12.2", "version": "0.12.5",
"private": true, "private": true,
"slogan": "Now you have the choice", "slogan": "Now you have the choice",
"description": "Enforce privacy & security best-practices on Windows, macOS and Linux, because privacy is sexy 🍑🍆", "description": "Enforce privacy & security best-practices on Windows, macOS and Linux, because privacy is sexy 🍑🍆",
@@ -34,25 +34,21 @@
"postuninstall": "electron-builder install-app-deps" "postuninstall": "electron-builder install-app-deps"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.4.0", "@floating-ui/vue": "^1.0.2",
"@fortawesome/free-brands-svg-icons": "^6.4.0",
"@fortawesome/free-regular-svg-icons": "^6.4.0",
"@fortawesome/free-solid-svg-icons": "^6.4.0",
"@fortawesome/vue-fontawesome": "^2.0.9",
"@juggle/resize-observer": "^3.4.0", "@juggle/resize-observer": "^3.4.0",
"ace-builds": "^1.23.4", "ace-builds": "^1.30.0",
"cross-fetch": "^4.0.0", "cross-fetch": "^4.0.0",
"electron-log": "^4.4.8",
"electron-progressbar": "^2.1.0", "electron-progressbar": "^2.1.0",
"electron-updater": "^6.1.4",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"markdown-it": "^13.0.1", "markdown-it": "^13.0.2",
"npm": "^9.8.1",
"v-tooltip": "2.1.3",
"vue": "^2.7.14" "vue": "^2.7.14"
}, },
"devDependencies": { "devDependencies": {
"@modyfi/vite-plugin-yaml": "^1.0.4", "@modyfi/vite-plugin-yaml": "^1.0.4",
"@rushstack/eslint-patch": "^1.3.2", "@rushstack/eslint-patch": "^1.5.1",
"@types/ace": "^0.0.48", "@types/ace": "^0.0.49",
"@types/file-saver": "^2.0.5", "@types/file-saver": "^2.0.5",
"@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0", "@typescript-eslint/parser": "^5.62.0",
@@ -61,47 +57,42 @@
"@vue/eslint-config-airbnb-with-typescript": "^7.0.0", "@vue/eslint-config-airbnb-with-typescript": "^7.0.0",
"@vue/eslint-config-typescript": "^11.0.3", "@vue/eslint-config-typescript": "^11.0.3",
"@vue/test-utils": "^1.3.6", "@vue/test-utils": "^1.3.6",
"autoprefixer": "^10.4.15", "autoprefixer": "^10.4.16",
"cypress": "^12.17.2", "cypress": "^13.3.1",
"electron": "^25.3.2", "electron": "^27.0.0",
"electron-builder": "^24.6.3", "electron-builder": "^24.6.4",
"electron-devtools-installer": "^3.2.0", "electron-devtools-installer": "^3.2.0",
"electron-icon-builder": "^2.0.1", "electron-icon-builder": "^2.0.1",
"electron-log": "^4.4.8",
"electron-updater": "^6.1.4",
"electron-vite": "^1.0.27", "electron-vite": "^1.0.27",
"eslint": "^8.46.0", "eslint": "^8.51.0",
"eslint-plugin-cypress": "^2.14.0", "eslint-plugin-cypress": "^2.15.1",
"eslint-plugin-vue": "^9.6.0", "eslint-plugin-vue": "^9.17.0",
"eslint-plugin-vuejs-accessibility": "^1.2.0", "eslint-plugin-vuejs-accessibility": "^2.2.0",
"icon-gen": "^3.0.1", "icon-gen": "^4.0.0",
"jsdom": "^22.1.0", "jsdom": "^22.1.0",
"markdownlint-cli": "^0.35.0", "markdownlint-cli": "^0.37.0",
"postcss": "^8.4.28", "postcss": "^8.4.31",
"remark-cli": "^11.0.0", "remark-cli": "^12.0.0",
"remark-lint-no-dead-urls": "^1.1.0", "remark-lint-no-dead-urls": "^1.1.0",
"remark-preset-lint-consistent": "^5.1.2", "remark-preset-lint-consistent": "^5.1.2",
"remark-validate-links": "^12.1.1", "remark-validate-links": "^13.0.0",
"sass": "^1.64.1", "sass": "^1.69.3",
"start-server-and-test": "^2.0.0", "start-server-and-test": "^2.0.1",
"svgexport": "^0.4.2", "svgexport": "^0.4.2",
"terser": "^5.19.2", "terser": "^5.21.0",
"tslib": "~2.4.0", "tslib": "^2.6.2",
"typescript": "~4.6.2", "typescript": "^5.2.2",
"vite": "^4.4.9", "vite": "^4.4.11",
"vitest": "^0.34.2", "vitest": "^0.34.6",
"vue-tsc": "^1.8.8", "vue-tsc": "^1.8.19",
"yaml-lint": "^1.7.0" "yaml-lint": "^1.7.0"
}, },
"//devDependencies": { "//devDependencies": {
"terser": "Used by @vitejs/plugin-legacy for minification", "terser": "Used by @vitejs/plugin-legacy for minification",
"typescript": [ "@rushstack/eslint-patch": "Needed by @vue/eslint-config-typescript",
"Cannot upgrade to 5.X.X due to unmaintained @vue/cli-plugin-typescript, https://github.com/vuejs/vue-cli/issues/7401", "@vue/eslint-config-typescript": "Cannot upgrade to 12.X.X due to @vue/eslint-config-airbnb-with-typescript, https://github.com/vuejs/eslint-config-airbnb/issues/58",
"Cannot upgrade to > 4.6.X otherwise unit tests do not work, https://github.com/evanw/node-source-map-support/issues/252" "@typescript-eslint/eslint-plugin": "Cannot upgrade to 6.X.X due to @vue/eslint-config-airbnb-with-typescript, https://github.com/vuejs/eslint-config-airbnb/issues/58",
], "@typescript-eslint/parser": "Cannot upgrade to 6.X.X due to @vue/eslint-config-airbnb-with-typescript, https://github.com/vuejs/eslint-config-airbnb/issues/58"
"tslib": "Cannot upgrade to > 2.4.X otherwise unit tests do not work, https://github.com/evanw/node-source-map-support/issues/252",
"@typescript-eslint/eslint-plugin": "Cannot upgrade to 6.X.X due to @vue/eslint-config-typescript, https://github.com/vuejs/eslint-config-typescript/pull/60",
"@typescript-eslint/parser": "Cannot upgrade to 6.X.X due to @vue/eslint-config-typescript, https://github.com/vuejs/eslint-config-typescript/pull/60"
}, },
"homepage": "https://privacy.sexy", "homepage": "https://privacy.sexy",
"repository": { "repository": {

View File

@@ -0,0 +1,62 @@
/**
* Description:
* This script checks if a server, provided as a CLI argument, is up
* and returns an HTTP 200 status code.
* It is designed to provide easy verification of server availability
* and will retry a specified number of times.
*
* Usage:
* node ./scripts/verify-web-server-status.js --url [URL]
*
* Options:
* --url URL of the server to check
*/
import { get } from 'http';
const MAX_RETRIES = 30;
const RETRY_DELAY_IN_SECONDS = 3;
const URL_PARAMETER_NAME = '--url';
function checkServer(currentRetryCount = 1) {
const serverUrl = getServerUrl();
console.log(`Requesting ${serverUrl}...`);
get(serverUrl, (res) => {
if (res.statusCode === 200) {
console.log('🎊 Success: The server is up and returned HTTP 200.');
process.exit(0);
} else {
console.log(`Server returned HTTP status code ${res.statusCode}.`);
retry(currentRetryCount);
}
}).on('error', (err) => {
console.error('Error making the request:', err);
retry(currentRetryCount);
});
}
function retry(currentRetryCount) {
console.log(`Attempt ${currentRetryCount}/${MAX_RETRIES}:`);
console.log(`Retrying in ${RETRY_DELAY_IN_SECONDS} seconds.`);
const remainingTime = (MAX_RETRIES - currentRetryCount) * RETRY_DELAY_IN_SECONDS;
console.log(`Time remaining before timeout: ${remainingTime}s`);
if (currentRetryCount < MAX_RETRIES) {
setTimeout(() => checkServer(currentRetryCount + 1), RETRY_DELAY_IN_SECONDS * 1000);
} else {
console.log('Failure: The server at did not return HTTP 200 within the allocated time. Exiting.');
process.exit(1);
}
}
function getServerUrl() {
const urlIndex = process.argv.indexOf(URL_PARAMETER_NAME);
if (urlIndex === -1 || urlIndex === process.argv.length - 1) {
console.error(`Parameter "${URL_PARAMETER_NAME}" is not provided.`);
process.exit(1);
}
return process.argv[urlIndex + 1];
}
checkServer();

View File

@@ -0,0 +1,5 @@
import { CompiledCode } from '../CompiledCode';
export interface CodeSegmentMerger {
mergeCodeParts(codeSegments: readonly CompiledCode[]): CompiledCode;
}

View File

@@ -0,0 +1,20 @@
import { CompiledCode } from '../CompiledCode';
import { CodeSegmentMerger } from './CodeSegmentMerger';
export class NewlineCodeSegmentMerger implements CodeSegmentMerger {
public mergeCodeParts(codeSegments: readonly CompiledCode[]): CompiledCode {
if (!codeSegments?.length) {
throw new Error('missing segments');
}
return {
code: joinCodeParts(codeSegments.map((f) => f.code)),
revertCode: joinCodeParts(codeSegments.map((f) => f.revertCode)),
};
}
}
function joinCodeParts(codeSegments: readonly string[]): string {
return codeSegments
.filter((segment) => segment?.length > 0)
.join('\n');
}

View File

@@ -1,4 +1,4 @@
export interface ICompiledCode { export interface CompiledCode {
readonly code: string; readonly code: string;
readonly revertCode?: string; readonly revertCode?: string;
} }

View File

@@ -0,0 +1,9 @@
import { ISharedFunctionCollection } from '@/application/Parser/Script/Compiler/Function/ISharedFunctionCollection';
import { FunctionCall } from '../FunctionCall';
import type { SingleCallCompiler } from './SingleCall/SingleCallCompiler';
export interface FunctionCallCompilationContext {
readonly allFunctions: ISharedFunctionCollection;
readonly rootCallSequence: readonly FunctionCall[];
readonly singleCallCompiler: SingleCallCompiler;
}

View File

@@ -1,149 +1,10 @@
import { IReadOnlyFunctionCallArgumentCollection } from '@/application/Parser/Script/Compiler/Function/Call/Argument/IFunctionCallArgumentCollection'; import { ISharedFunctionCollection } from '@/application/Parser/Script/Compiler/Function/ISharedFunctionCollection';
import { IFunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/IFunctionCall';
import { FunctionCallArgument } from '@/application/Parser/Script/Compiler/Function/Call/Argument/FunctionCallArgument';
import { ISharedFunctionCollection } from '../../ISharedFunctionCollection';
import { IExpressionsCompiler } from '../../../Expressions/IExpressionsCompiler';
import { ExpressionsCompiler } from '../../../Expressions/ExpressionsCompiler';
import { ISharedFunction, IFunctionCode } from '../../ISharedFunction';
import { FunctionCall } from '../FunctionCall'; import { FunctionCall } from '../FunctionCall';
import { FunctionCallArgumentCollection } from '../Argument/FunctionCallArgumentCollection'; import { CompiledCode } from './CompiledCode';
import { IFunctionCallCompiler } from './IFunctionCallCompiler';
import { ICompiledCode } from './ICompiledCode';
export class FunctionCallCompiler implements IFunctionCallCompiler { export interface FunctionCallCompiler {
public static readonly instance: IFunctionCallCompiler = new FunctionCallCompiler(); compileFunctionCalls(
calls: readonly FunctionCall[],
protected constructor(
private readonly expressionsCompiler: IExpressionsCompiler = new ExpressionsCompiler(),
) {
}
public compileCall(
calls: IFunctionCall[],
functions: ISharedFunctionCollection, functions: ISharedFunctionCollection,
): ICompiledCode { ): CompiledCode;
if (!functions) { throw new Error('missing functions'); }
if (!calls) { throw new Error('missing calls'); }
if (calls.some((f) => !f)) { throw new Error('missing function call'); }
const context: ICompilationContext = {
allFunctions: functions,
callSequence: calls,
expressionsCompiler: this.expressionsCompiler,
};
const code = compileCallSequence(context);
return code;
}
}
interface ICompilationContext {
allFunctions: ISharedFunctionCollection;
callSequence: readonly IFunctionCall[];
expressionsCompiler: IExpressionsCompiler;
}
interface ICompiledFunctionCall {
readonly code: string;
readonly revertCode: string;
}
function compileCallSequence(context: ICompilationContext): ICompiledFunctionCall {
const compiledFunctions = context.callSequence
.flatMap((call) => compileSingleCall(call, context));
return {
code: merge(compiledFunctions.map((f) => f.code)),
revertCode: merge(compiledFunctions.map((f) => f.revertCode)),
};
}
function compileSingleCall(
call: IFunctionCall,
context: ICompilationContext,
): ICompiledFunctionCall[] {
const func = context.allFunctions.getFunctionByName(call.functionName);
ensureThatCallArgumentsExistInParameterDefinition(func, call.args);
if (func.body.code) { // Function with inline code
const compiledCode = compileCode(func.body.code, call.args, context.expressionsCompiler);
return [compiledCode];
}
// Function with inner calls
return func.body.calls
.map((innerCall) => {
const compiledArgs = compileArgs(innerCall.args, call.args, context.expressionsCompiler);
const compiledCall = new FunctionCall(innerCall.functionName, compiledArgs);
return compileSingleCall(compiledCall, context);
})
.flat();
}
function compileCode(
code: IFunctionCode,
args: IReadOnlyFunctionCallArgumentCollection,
compiler: IExpressionsCompiler,
): ICompiledFunctionCall {
return {
code: compiler.compileExpressions(code.execute, args),
revertCode: compiler.compileExpressions(code.revert, args),
};
}
function compileArgs(
argsToCompile: IReadOnlyFunctionCallArgumentCollection,
args: IReadOnlyFunctionCallArgumentCollection,
compiler: IExpressionsCompiler,
): IReadOnlyFunctionCallArgumentCollection {
return argsToCompile
.getAllParameterNames()
.map((parameterName) => {
const { argumentValue } = argsToCompile.getArgument(parameterName);
const compiledValue = compiler.compileExpressions(argumentValue, args);
return new FunctionCallArgument(parameterName, compiledValue);
})
.reduce((compiledArgs, arg) => {
compiledArgs.addArgument(arg);
return compiledArgs;
}, new FunctionCallArgumentCollection());
}
function merge(codeParts: readonly string[]): string {
return codeParts
.filter((part) => part?.length > 0)
.join('\n');
}
function ensureThatCallArgumentsExistInParameterDefinition(
func: ISharedFunction,
args: IReadOnlyFunctionCallArgumentCollection,
): void {
const callArgumentNames = args.getAllParameterNames();
const functionParameterNames = func.parameters.all.map((param) => param.name) || [];
const unexpectedParameters = findUnexpectedParameters(callArgumentNames, functionParameterNames);
throwIfNotEmpty(func.name, unexpectedParameters, functionParameterNames);
}
function findUnexpectedParameters(
callArgumentNames: string[],
functionParameterNames: string[],
): string[] {
if (!callArgumentNames.length && !functionParameterNames.length) {
return [];
}
return callArgumentNames
.filter((callParam) => !functionParameterNames.includes(callParam));
}
function throwIfNotEmpty(
functionName: string,
unexpectedParameters: string[],
expectedParameters: string[],
) {
if (!unexpectedParameters.length) {
return;
}
throw new Error(
// eslint-disable-next-line prefer-template
`Function "${functionName}" has unexpected parameter(s) provided: `
+ `"${unexpectedParameters.join('", "')}"`
+ '. Expected parameter(s): '
+ (expectedParameters.length ? `"${expectedParameters.join('", "')}"` : 'none'),
);
} }

View File

@@ -0,0 +1,36 @@
import { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
import { ISharedFunctionCollection } from '../../ISharedFunctionCollection';
import { FunctionCallCompiler } from './FunctionCallCompiler';
import { CompiledCode } from './CompiledCode';
import { FunctionCallCompilationContext } from './FunctionCallCompilationContext';
import { SingleCallCompiler } from './SingleCall/SingleCallCompiler';
import { AdaptiveFunctionCallCompiler } from './SingleCall/AdaptiveFunctionCallCompiler';
import { CodeSegmentMerger } from './CodeSegmentJoin/CodeSegmentMerger';
import { NewlineCodeSegmentMerger } from './CodeSegmentJoin/NewlineCodeSegmentMerger';
export class FunctionCallSequenceCompiler implements FunctionCallCompiler {
public static readonly instance: FunctionCallCompiler = new FunctionCallSequenceCompiler();
/* The constructor is protected to enforce the singleton pattern. */
protected constructor(
private readonly singleCallCompiler: SingleCallCompiler = new AdaptiveFunctionCallCompiler(),
private readonly codeSegmentMerger: CodeSegmentMerger = new NewlineCodeSegmentMerger(),
) { }
public compileFunctionCalls(
calls: readonly FunctionCall[],
functions: ISharedFunctionCollection,
): CompiledCode {
if (!functions) { throw new Error('missing functions'); }
if (!calls?.length) { throw new Error('missing calls'); }
if (calls.some((f) => !f)) { throw new Error('missing function call'); }
const context: FunctionCallCompilationContext = {
allFunctions: functions,
rootCallSequence: calls,
singleCallCompiler: this.singleCallCompiler,
};
const codeSegments = context.rootCallSequence
.flatMap((call) => this.singleCallCompiler.compileSingleCall(call, context));
return this.codeSegmentMerger.mergeCodeParts(codeSegments);
}
}

View File

@@ -1,9 +0,0 @@
import { ISharedFunctionCollection } from '@/application/Parser/Script/Compiler/Function/ISharedFunctionCollection';
import { IFunctionCall } from '../IFunctionCall';
import { ICompiledCode } from './ICompiledCode';
export interface IFunctionCallCompiler {
compileCall(
calls: IFunctionCall[],
functions: ISharedFunctionCollection): ICompiledCode;
}

View File

@@ -0,0 +1,78 @@
import { FunctionCall } from '../../FunctionCall';
import { CompiledCode } from '../CompiledCode';
import { FunctionCallCompilationContext } from '../FunctionCallCompilationContext';
import { IReadOnlyFunctionCallArgumentCollection } from '../../Argument/IFunctionCallArgumentCollection';
import { ISharedFunction } from '../../../ISharedFunction';
import { SingleCallCompiler } from './SingleCallCompiler';
import { SingleCallCompilerStrategy } from './SingleCallCompilerStrategy';
import { InlineFunctionCallCompiler } from './Strategies/InlineFunctionCallCompiler';
import { NestedFunctionCallCompiler } from './Strategies/NestedFunctionCallCompiler';
export class AdaptiveFunctionCallCompiler implements SingleCallCompiler {
public constructor(
private readonly strategies: SingleCallCompilerStrategy[] = [
new InlineFunctionCallCompiler(),
new NestedFunctionCallCompiler(),
],
) {
}
public compileSingleCall(
call: FunctionCall,
context: FunctionCallCompilationContext,
): CompiledCode[] {
const func = context.allFunctions.getFunctionByName(call.functionName);
ensureThatCallArgumentsExistInParameterDefinition(func, call.args);
const strategy = this.findStrategy(func);
return strategy.compileFunction(func, call, context);
}
private findStrategy(func: ISharedFunction): SingleCallCompilerStrategy {
const strategies = this.strategies.filter((strategy) => strategy.canCompile(func));
if (strategies.length > 1) {
throw new Error('Multiple strategies found to compile the function call.');
}
if (strategies.length === 0) {
throw new Error('No strategies found to compile the function call.');
}
return strategies[0];
}
}
function ensureThatCallArgumentsExistInParameterDefinition(
func: ISharedFunction,
callArguments: IReadOnlyFunctionCallArgumentCollection,
): void {
const callArgumentNames = callArguments.getAllParameterNames();
const functionParameterNames = func.parameters.all.map((param) => param.name) || [];
const unexpectedParameters = findUnexpectedParameters(callArgumentNames, functionParameterNames);
throwIfUnexpectedParametersExist(func.name, unexpectedParameters, functionParameterNames);
}
function findUnexpectedParameters(
callArgumentNames: string[],
functionParameterNames: string[],
): string[] {
if (!callArgumentNames.length && !functionParameterNames.length) {
return [];
}
return callArgumentNames
.filter((callParam) => !functionParameterNames.includes(callParam));
}
function throwIfUnexpectedParametersExist(
functionName: string,
unexpectedParameters: string[],
expectedParameters: string[],
) {
if (!unexpectedParameters.length) {
return;
}
throw new Error(
// eslint-disable-next-line prefer-template
`Function "${functionName}" has unexpected parameter(s) provided: `
+ `"${unexpectedParameters.join('", "')}"`
+ '. Expected parameter(s): '
+ (expectedParameters.length ? `"${expectedParameters.join('", "')}"` : 'none'),
);
}

View File

@@ -0,0 +1,10 @@
import { FunctionCall } from '../../FunctionCall';
import { CompiledCode } from '../CompiledCode';
import { FunctionCallCompilationContext } from '../FunctionCallCompilationContext';
export interface SingleCallCompiler {
compileSingleCall(
call: FunctionCall,
context: FunctionCallCompilationContext,
): CompiledCode[];
}

View File

@@ -0,0 +1,13 @@
import { ISharedFunction } from '@/application/Parser/Script/Compiler/Function/ISharedFunction';
import { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
import { CompiledCode } from '../CompiledCode';
import { FunctionCallCompilationContext } from '../FunctionCallCompilationContext';
export interface SingleCallCompilerStrategy {
canCompile(func: ISharedFunction): boolean;
compileFunction(
calledFunction: ISharedFunction,
callToFunction: FunctionCall,
context: FunctionCallCompilationContext,
): CompiledCode[],
}

View File

@@ -0,0 +1,10 @@
import { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
import { FunctionCallCompilationContext } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/FunctionCallCompilationContext';
export interface ArgumentCompiler {
createCompiledNestedCall(
nestedFunctionCall: FunctionCall,
parentFunctionCall: FunctionCall,
context: FunctionCallCompilationContext,
): FunctionCall;
}

View File

@@ -0,0 +1,109 @@
import { IReadOnlyFunctionCallArgumentCollection } from '@/application/Parser/Script/Compiler/Function/Call/Argument/IFunctionCallArgumentCollection';
import { FunctionCallArgument } from '@/application/Parser/Script/Compiler/Function/Call/Argument/FunctionCallArgument';
import { FunctionCallArgumentCollection } from '@/application/Parser/Script/Compiler/Function/Call/Argument/FunctionCallArgumentCollection';
import { ExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/ExpressionsCompiler';
import { IExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/IExpressionsCompiler';
import { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
import { FunctionCallCompilationContext } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/FunctionCallCompilationContext';
import { ParsedFunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/ParsedFunctionCall';
import { ArgumentCompiler } from './ArgumentCompiler';
export class NestedFunctionArgumentCompiler implements ArgumentCompiler {
constructor(
private readonly expressionsCompiler: IExpressionsCompiler = new ExpressionsCompiler(),
) { }
public createCompiledNestedCall(
nestedFunction: FunctionCall,
parentFunction: FunctionCall,
context: FunctionCallCompilationContext,
): FunctionCall {
const compiledArgs = compileNestedFunctionArguments(
nestedFunction,
parentFunction.args,
context,
this.expressionsCompiler,
);
const compiledCall = new ParsedFunctionCall(nestedFunction.functionName, compiledArgs);
return compiledCall;
}
}
function compileNestedFunctionArguments(
nestedFunction: FunctionCall,
parentFunctionArgs: IReadOnlyFunctionCallArgumentCollection,
context: FunctionCallCompilationContext,
expressionsCompiler: IExpressionsCompiler,
): IReadOnlyFunctionCallArgumentCollection {
const requiredParameterNames = context
.allFunctions
.getRequiredParameterNames(nestedFunction.functionName);
const compiledArguments = nestedFunction.args
.getAllParameterNames()
// Compile each argument value
.map((paramName) => ({
parameterName: paramName,
compiledArgumentValue: compileArgument(
paramName,
nestedFunction,
parentFunctionArgs,
expressionsCompiler,
),
}))
// Filter out arguments with absent values
.filter(({
parameterName,
compiledArgumentValue,
}) => isValidNonAbsentArgumentValue(
parameterName,
compiledArgumentValue,
requiredParameterNames,
))
/*
Create argument object with non-absent values.
This is done after eliminating absent values because otherwise creating argument object
with absent values throws error.
*/
.map(({
parameterName,
compiledArgumentValue,
}) => new FunctionCallArgument(parameterName, compiledArgumentValue));
return buildArgumentCollectionFromArguments(compiledArguments);
}
function isValidNonAbsentArgumentValue(
parameterName: string,
argumentValue: string | undefined,
requiredParameterNames: string[],
): boolean {
if (argumentValue) {
return true;
}
if (!requiredParameterNames.includes(parameterName)) {
return false;
}
throw new Error(`Compilation resulted in empty value for required parameter: "${parameterName}"`);
}
function compileArgument(
parameterName: string,
nestedFunction: FunctionCall,
parentFunctionArgs: IReadOnlyFunctionCallArgumentCollection,
expressionsCompiler: IExpressionsCompiler,
): string {
try {
const { argumentValue: codeInArgument } = nestedFunction.args.getArgument(parameterName);
return expressionsCompiler.compileExpressions(codeInArgument, parentFunctionArgs);
} catch (err) {
throw new AggregateError([err], `Error when compiling argument for "${parameterName}"`);
}
}
function buildArgumentCollectionFromArguments(
args: FunctionCallArgument[],
): FunctionCallArgumentCollection {
return args.reduce((compiledArgs, arg) => {
compiledArgs.addArgument(arg);
return compiledArgs;
}, new FunctionCallArgumentCollection());
}

View File

@@ -0,0 +1,31 @@
import { ExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/ExpressionsCompiler';
import { IExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/IExpressionsCompiler';
import { ISharedFunction } from '@/application/Parser/Script/Compiler/Function/ISharedFunction';
import { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
import { CompiledCode } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/CompiledCode';
import { SingleCallCompilerStrategy } from '../SingleCallCompilerStrategy';
export class InlineFunctionCallCompiler implements SingleCallCompilerStrategy {
public constructor(
private readonly expressionsCompiler: IExpressionsCompiler = new ExpressionsCompiler(),
) {
}
public canCompile(func: ISharedFunction): boolean {
return func.body.code !== undefined;
}
public compileFunction(
calledFunction: ISharedFunction,
callToFunction: FunctionCall,
): CompiledCode[] {
const { code } = calledFunction.body;
const { args } = callToFunction;
return [
{
code: this.expressionsCompiler.compileExpressions(code.execute, args),
revertCode: this.expressionsCompiler.compileExpressions(code.revert, args),
},
];
}
}

View File

@@ -0,0 +1,37 @@
import { ISharedFunction } from '@/application/Parser/Script/Compiler/Function/ISharedFunction';
import { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
import { FunctionCallCompilationContext } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/FunctionCallCompilationContext';
import { CompiledCode } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/CompiledCode';
import { SingleCallCompilerStrategy } from '../SingleCallCompilerStrategy';
import { ArgumentCompiler } from './Argument/ArgumentCompiler';
import { NestedFunctionArgumentCompiler } from './Argument/NestedFunctionArgumentCompiler';
export class NestedFunctionCallCompiler implements SingleCallCompilerStrategy {
public constructor(
private readonly argumentCompiler: ArgumentCompiler = new NestedFunctionArgumentCompiler(),
) {
}
public canCompile(func: ISharedFunction): boolean {
return func.body.calls !== undefined;
}
public compileFunction(
calledFunction: ISharedFunction,
callToFunction: FunctionCall,
context: FunctionCallCompilationContext,
): CompiledCode[] {
const nestedCalls = calledFunction.body.calls;
return nestedCalls.map((nestedCall) => {
try {
const compiledParentCall = this.argumentCompiler
.createCompiledNestedCall(nestedCall, callToFunction, context);
const compiledNestedCall = context.singleCallCompiler
.compileSingleCall(compiledParentCall, context);
return compiledNestedCall;
} catch (err) {
throw new AggregateError([err], `Error with call to "${nestedCall.functionName}" function from "${callToFunction.functionName}" function`);
}
}).flat();
}
}

View File

@@ -1,16 +1,6 @@
import { IReadOnlyFunctionCallArgumentCollection } from './Argument/IFunctionCallArgumentCollection'; import { IReadOnlyFunctionCallArgumentCollection } from './Argument/IFunctionCallArgumentCollection';
import { IFunctionCall } from './IFunctionCall';
export class FunctionCall implements IFunctionCall { export interface FunctionCall {
constructor( readonly functionName: string;
public readonly functionName: string, readonly args: IReadOnlyFunctionCallArgumentCollection;
public readonly args: IReadOnlyFunctionCallArgumentCollection,
) {
if (!functionName) {
throw new Error('missing function name in function call');
}
if (!args) {
throw new Error('missing args');
}
}
} }

View File

@@ -1,10 +1,10 @@
import type { FunctionCallData, FunctionCallsData, FunctionCallParametersData } from '@/application/collections/'; import type { FunctionCallData, FunctionCallsData, FunctionCallParametersData } from '@/application/collections/';
import { IFunctionCall } from './IFunctionCall'; import { FunctionCall } from './FunctionCall';
import { FunctionCallArgumentCollection } from './Argument/FunctionCallArgumentCollection'; import { FunctionCallArgumentCollection } from './Argument/FunctionCallArgumentCollection';
import { FunctionCallArgument } from './Argument/FunctionCallArgument'; import { FunctionCallArgument } from './Argument/FunctionCallArgument';
import { FunctionCall } from './FunctionCall'; import { ParsedFunctionCall } from './ParsedFunctionCall';
export function parseFunctionCalls(calls: FunctionCallsData): IFunctionCall[] { export function parseFunctionCalls(calls: FunctionCallsData): FunctionCall[] {
if (calls === undefined) { if (calls === undefined) {
throw new Error('missing call data'); throw new Error('missing call data');
} }
@@ -22,12 +22,12 @@ function getCallSequence(calls: FunctionCallsData): FunctionCallData[] {
return [calls as FunctionCallData]; return [calls as FunctionCallData];
} }
function parseFunctionCall(call: FunctionCallData): IFunctionCall { function parseFunctionCall(call: FunctionCallData): FunctionCall {
if (!call) { if (!call) {
throw new Error('missing call data'); throw new Error('missing call data');
} }
const callArgs = parseArgs(call.parameters); const callArgs = parseArgs(call.parameters);
return new FunctionCall(call.function, callArgs); return new ParsedFunctionCall(call.function, callArgs);
} }
function parseArgs( function parseArgs(

View File

@@ -1,6 +0,0 @@
import { IReadOnlyFunctionCallArgumentCollection } from './Argument/IFunctionCallArgumentCollection';
export interface IFunctionCall {
readonly functionName: string;
readonly args: IReadOnlyFunctionCallArgumentCollection;
}

View File

@@ -0,0 +1,16 @@
import { IReadOnlyFunctionCallArgumentCollection } from './Argument/IFunctionCallArgumentCollection';
import { FunctionCall } from './FunctionCall';
export class ParsedFunctionCall implements FunctionCall {
constructor(
public readonly functionName: string,
public readonly args: IReadOnlyFunctionCallArgumentCollection,
) {
if (!functionName) {
throw new Error('missing function name in function call');
}
if (!args) {
throw new Error('missing args');
}
}
}

View File

@@ -1,5 +1,5 @@
import { IReadOnlyFunctionParameterCollection } from './Parameter/IFunctionParameterCollection'; import { IReadOnlyFunctionParameterCollection } from './Parameter/IFunctionParameterCollection';
import { IFunctionCall } from './Call/IFunctionCall'; import { FunctionCall } from './Call/FunctionCall';
export interface ISharedFunction { export interface ISharedFunction {
readonly name: string; readonly name: string;
@@ -9,8 +9,8 @@ export interface ISharedFunction {
export interface ISharedFunctionBody { export interface ISharedFunctionBody {
readonly type: FunctionBodyType; readonly type: FunctionBodyType;
readonly code: IFunctionCode; readonly code: IFunctionCode | undefined;
readonly calls: readonly IFunctionCall[]; readonly calls: readonly FunctionCall[] | undefined;
} }
export enum FunctionBodyType { export enum FunctionBodyType {

View File

@@ -2,4 +2,5 @@ import { ISharedFunction } from './ISharedFunction';
export interface ISharedFunctionCollection { export interface ISharedFunctionCollection {
getFunctionByName(name: string): ISharedFunction; getFunctionByName(name: string): ISharedFunction;
getRequiredParameterNames(functionName: string): string[];
} }

View File

@@ -1,4 +1,4 @@
import { IFunctionCall } from './Call/IFunctionCall'; import { FunctionCall } from './Call/FunctionCall';
import { import {
FunctionBodyType, IFunctionCode, ISharedFunction, ISharedFunctionBody, FunctionBodyType, IFunctionCode, ISharedFunction, ISharedFunctionBody,
@@ -8,7 +8,7 @@ import { IReadOnlyFunctionParameterCollection } from './Parameter/IFunctionParam
export function createCallerFunction( export function createCallerFunction(
name: string, name: string,
parameters: IReadOnlyFunctionParameterCollection, parameters: IReadOnlyFunctionParameterCollection,
callSequence: readonly IFunctionCall[], callSequence: readonly FunctionCall[],
): ISharedFunction { ): ISharedFunction {
if (!callSequence || !callSequence.length) { if (!callSequence || !callSequence.length) {
throw new Error(`missing call sequence in function "${name}"`); throw new Error(`missing call sequence in function "${name}"`);
@@ -38,7 +38,7 @@ class SharedFunction implements ISharedFunction {
constructor( constructor(
public readonly name: string, public readonly name: string,
public readonly parameters: IReadOnlyFunctionParameterCollection, public readonly parameters: IReadOnlyFunctionParameterCollection,
content: IFunctionCode | readonly IFunctionCall[], content: IFunctionCode | readonly FunctionCall[],
bodyType: FunctionBodyType, bodyType: FunctionBodyType,
) { ) {
if (!name) { throw new Error('missing function name'); } if (!name) { throw new Error('missing function name'); }
@@ -46,7 +46,7 @@ class SharedFunction implements ISharedFunction {
this.body = { this.body = {
type: bodyType, type: bodyType,
code: bodyType === FunctionBodyType.Code ? content as IFunctionCode : undefined, code: bodyType === FunctionBodyType.Code ? content as IFunctionCode : undefined,
calls: bodyType === FunctionBodyType.Calls ? content as readonly IFunctionCall[] : undefined, calls: bodyType === FunctionBodyType.Calls ? content as readonly FunctionCall[] : undefined,
}; };
} }
} }

View File

@@ -21,6 +21,15 @@ export class SharedFunctionCollection implements ISharedFunctionCollection {
return func; return func;
} }
public getRequiredParameterNames(functionName: string): string[] {
return this
.getFunctionByName(functionName)
.parameters
.all
.filter((parameter) => !parameter.isOptional)
.map((parameter) => parameter.name);
}
private has(functionName: string) { private has(functionName: string) {
return this.functionsByName.has(functionName); return this.functionsByName.has(functionName);
} }

View File

@@ -7,12 +7,12 @@ import { NoEmptyLines } from '@/application/Parser/Script/Validation/Rules/NoEmp
import { ICodeValidator } from '@/application/Parser/Script/Validation/ICodeValidator'; import { ICodeValidator } from '@/application/Parser/Script/Validation/ICodeValidator';
import { IScriptCompiler } from './IScriptCompiler'; import { IScriptCompiler } from './IScriptCompiler';
import { ISharedFunctionCollection } from './Function/ISharedFunctionCollection'; import { ISharedFunctionCollection } from './Function/ISharedFunctionCollection';
import { IFunctionCallCompiler } from './Function/Call/Compiler/IFunctionCallCompiler'; import { FunctionCallSequenceCompiler } from './Function/Call/Compiler/FunctionCallSequenceCompiler';
import { FunctionCallCompiler } from './Function/Call/Compiler/FunctionCallCompiler'; import { FunctionCallCompiler } from './Function/Call/Compiler/FunctionCallCompiler';
import { ISharedFunctionsParser } from './Function/ISharedFunctionsParser'; import { ISharedFunctionsParser } from './Function/ISharedFunctionsParser';
import { SharedFunctionsParser } from './Function/SharedFunctionsParser'; import { SharedFunctionsParser } from './Function/SharedFunctionsParser';
import { parseFunctionCalls } from './Function/Call/FunctionCallParser'; import { parseFunctionCalls } from './Function/Call/FunctionCallParser';
import { ICompiledCode } from './Function/Call/Compiler/ICompiledCode'; import { CompiledCode } from './Function/Call/Compiler/CompiledCode';
export class ScriptCompiler implements IScriptCompiler { export class ScriptCompiler implements IScriptCompiler {
private readonly functions: ISharedFunctionCollection; private readonly functions: ISharedFunctionCollection;
@@ -21,7 +21,7 @@ export class ScriptCompiler implements IScriptCompiler {
functions: readonly FunctionData[] | undefined, functions: readonly FunctionData[] | undefined,
syntax: ILanguageSyntax, syntax: ILanguageSyntax,
sharedFunctionsParser: ISharedFunctionsParser = SharedFunctionsParser.instance, sharedFunctionsParser: ISharedFunctionsParser = SharedFunctionsParser.instance,
private readonly callCompiler: IFunctionCallCompiler = FunctionCallCompiler.instance, private readonly callCompiler: FunctionCallCompiler = FunctionCallSequenceCompiler.instance,
private readonly codeValidator: ICodeValidator = CodeValidator.instance, private readonly codeValidator: ICodeValidator = CodeValidator.instance,
) { ) {
if (!syntax) { throw new Error('missing syntax'); } if (!syntax) { throw new Error('missing syntax'); }
@@ -40,7 +40,7 @@ export class ScriptCompiler implements IScriptCompiler {
if (!script) { throw new Error('missing script'); } if (!script) { throw new Error('missing script'); }
try { try {
const calls = parseFunctionCalls(script.call); const calls = parseFunctionCalls(script.call);
const compiledCode = this.callCompiler.compileCall(calls, this.functions); const compiledCode = this.callCompiler.compileFunctionCalls(calls, this.functions);
validateCompiledCode(compiledCode, this.codeValidator); validateCompiledCode(compiledCode, this.codeValidator);
return new ScriptCode( return new ScriptCode(
compiledCode.code, compiledCode.code,
@@ -52,7 +52,7 @@ export class ScriptCompiler implements IScriptCompiler {
} }
} }
function validateCompiledCode(compiledCode: ICompiledCode, validator: ICodeValidator): void { function validateCompiledCode(compiledCode: CompiledCode, validator: ICodeValidator): void {
[compiledCode.code, compiledCode.revertCode].forEach( [compiledCode.code, compiledCode.revertCode].forEach(
(code) => validator.throwIfInvalid(code, [new NoEmptyLines()]), (code) => validator.throwIfInvalid(code, [new NoEmptyLines()]),
); );

View File

@@ -1,4 +1,4 @@
# Structure documented in "docs/collection-files.md" # Structure is documented in "docs/collection-files.md"
os: linux os: linux
scripting: scripting:
language: shellscript language: shellscript
@@ -135,7 +135,7 @@ actions:
It uses `~/.ash_history` as the history file [3]. It uses `~/.ash_history` as the history file [3].
[1]: https://web.archive.org/web/20221030142637/https://en.wikipedia.org/wiki/Almquist_shell#Embededed_Linux "Almquist shell - Wikipedia | wikipedia.org" [1]: https://web.archive.org/web/20221030142637/https://en.wikipedia.org/wiki/Almquist_shell#Embedded_Linux "Almquist shell - Wikipedia | wikipedia.org"
[2]: https://web.archive.org/web/20221029135416/https://android.googlesource.com/platform/system/core/+/master/shell_and_utilities/README.md "Android's shell and utilities | android.googlesource.com" [2]: https://web.archive.org/web/20221029135416/https://android.googlesource.com/platform/system/core/+/master/shell_and_utilities/README.md "Android's shell and utilities | android.googlesource.com"
[3]: https://web.archive.org/web/20221029135513/https://github.com/brgl/busybox/blob/abbf17abccbf832365d9acf1c280369ba7d5f8b2/shell/ash.c#L13626 "busybox/ash.c source code | github.com/brgl/busybox" [3]: https://web.archive.org/web/20221029135513/https://github.com/brgl/busybox/blob/abbf17abccbf832365d9acf1c280369ba7d5f8b2/shell/ash.c#L13626 "busybox/ash.c source code | github.com/brgl/busybox"
call: call:
@@ -223,8 +223,8 @@ actions:
1. Temporary Windows files. Wine saves temporary Windows files at `<wine folder>/drive_c/windows/temp/` [1] [2]. 1. Temporary Windows files. Wine saves temporary Windows files at `<wine folder>/drive_c/windows/temp/` [1] [2].
2. Temporary Wine application cache [3] that is not connected to inner Windows files. 2. Temporary Wine application cache [3] that is not connected to inner Windows files.
[1]: https://web.archive.org/web/20180328090608/http://www.zdnet.com/article/keeping-temp-folders-clean/ "Keeping temp folders clean | ZDNet | dnet.com" [1]: https://web.archive.org/web/20180328090608/http://www.zdnet.com/article/keeping-temp-folders-clean/ "Keeping temp folders clean | ZDNet | zdnet.com"
[2]: https://web.archive.org/web/20221029135944/https://ubuntuforums.org/showthread.php?t=1006132 "Why does Wine have its own temp folders? | UbuntuForums | ubuntuforums.org" [2]: https://web.archive.org/web/20221029135944/https://ubuntuforums.org/showthread.php?t=1006132 "Why does Wine have its own temp folders? | UbuntuForums | ubuntuforums.org"
[3]: https://web.archive.org/web/20221029135955/https://wiki.debian.org/Wine#Mono_and_Gecko "Wine - Debian Wiki | wiki.debian.org" [3]: https://web.archive.org/web/20221029135955/https://wiki.debian.org/Wine#Mono_and_Gecko "Wine - Debian Wiki | wiki.debian.org"
code: |- code: |-
# Temporary Windows files for global prefix # Temporary Windows files for global prefix
@@ -232,7 +232,7 @@ actions:
# Wine cache: # Wine cache:
rm -rfv ~/.cache/wine/ rm -rfv ~/.cache/wine/
- -
name: Clear Winetricks downloads cache name: Clear Winetricks cache
recommend: standard recommend: standard
docs: |- docs: |-
Winetricks is a helper script to download and install various redistributable runtime libraries Winetricks is a helper script to download and install various redistributable runtime libraries
@@ -254,7 +254,7 @@ actions:
docs: |- docs: |-
LibreOffice is a free and open-source office productivity software suite, a project of The Document Foundation (TDF) [1]. LibreOffice is a free and open-source office productivity software suite, a project of The Document Foundation (TDF) [1].
`registrymodifications.xcu` is an XML file that contains the user-specified settings [2]. `registrymodifications.xcu` is an XML file that contains the user-specified settings [2].
It is found inside the user settings directory (`~/.config/libreoffice/4/user`) [2]. It is found inside the user settings directory (`~/.config/libreoffice/4/user`) [2].
It includes thumbnails generated [3], and a recent document list [4]. It includes thumbnails generated [3], and a recent document list [4].
@@ -263,7 +263,7 @@ actions:
[1]: https://web.archive.org/web/20221029140306/https://en.wikipedia.org/wiki/LibreOffice "LibreOffice | Wikipedia | en.wikipedia.org" [1]: https://web.archive.org/web/20221029140306/https://en.wikipedia.org/wiki/LibreOffice "LibreOffice | Wikipedia | en.wikipedia.org"
[2]: https://web.archive.org/web/20221029140313/https://wiki.documentfoundation.org/images/b/b0/LibreOffice_config_extension_writing.pdf "Config specification | LibreOffice documentation | wiki.documentfoundation.org" [2]: https://web.archive.org/web/20221029140313/https://wiki.documentfoundation.org/images/b/b0/LibreOffice_config_extension_writing.pdf "Config specification | LibreOffice documentation | wiki.documentfoundation.org"
[3]: https://web.archive.org/web/20221029140438/https://askubuntu.com/questions/996397/where-are-libre-office-thumbnails-stored/996528 "Where are Libre Office thumbnails stored? | Ask Ubuntu Forums | askubuntu.com" [3]: https://web.archive.org/web/20221029140438/https://askubuntu.com/questions/996397/where-are-libre-office-thumbnails-stored/996528 "Where are Libre Office thumbnails stored? | Ask Ubuntu Forums | askubuntu.com"
[4]: https://web.archive.org/web/20221029140501/https://forum.openoffice.org/en/forum/viewtopic.php?f=6&t=102020 "Restoring Recent documents list < Apache OpenOffice Community Forum | forum.openoffice.org" [4]: https://web.archive.org/web/20221029140501/https://forum.openoffice.org/en/forum/viewtopic.php?f=6&t=102020 "Restoring Recent documents list | Apache OpenOffice Community Forum | forum.openoffice.org"
code: |- code: |-
# Global installation # Global installation
rm -f ~/.config/libreoffice/4/user/registrymodifications.xcu rm -f ~/.config/libreoffice/4/user/registrymodifications.xcu
@@ -315,7 +315,7 @@ actions:
parameters: parameters:
file: cookies.sqlite-shm file: cookies.sqlite-shm
- -
name: Clear Thunderbird session restore (open windows and tabs) name: Clear Thunderbird session restoration data (open windows and tabs)
docs: |- docs: |-
The default window layout is saved in the session file (`session.json`) [1]. The default window layout is saved in the session file (`session.json`) [1].
It includes data on what tabs are open [1]. It includes data on what tabs are open [1].
@@ -326,7 +326,7 @@ actions:
parameters: parameters:
file: session.json file: session.json
- -
name: Clear Thunderbird accounts name: Clear Thunderbird passwords
docs: |- docs: |-
`logins.json` stores encrypted passwords [1]. `logins.json` stores encrypted passwords [1].
It replaces `signons.sqlite`, which had replaced `signons.txt` [1]. It replaces `signons.sqlite`, which had replaced `signons.txt` [1].
@@ -397,7 +397,7 @@ actions:
- -
function: DeleteFileFromThunderbirdProfiles function: DeleteFileFromThunderbirdProfiles
parameters: parameters:
file: abook.mab # Thunderbird < v78 file: abook.mab # < Thunderbird v78
- -
name: Clear Thunderbird collected address book name: Clear Thunderbird collected address book
docs: |- docs: |-
@@ -419,9 +419,9 @@ actions:
- -
function: DeleteFileFromThunderbirdProfiles function: DeleteFileFromThunderbirdProfiles
parameters: parameters:
file: history.mab # Thunderbird < v78 file: history.mab # < Thunderbird v78
- -
name: Clear Thunderbird's history of clicked links name: Clear clicked links history in Thunderbird
docs: |- docs: |-
Thunderbird saves annotations, bookmarks, favorite icons, input history, keywords, and browsing history Thunderbird saves annotations, bookmarks, favorite icons, input history, keywords, and browsing history
(a list of pages visited) [1]. (a list of pages visited) [1].
@@ -444,11 +444,11 @@ actions:
parameters: parameters:
file: places.sqlite.wal file: places.sqlite.wal
- -
category: Clear development tools data category: Clear data from development tools
docs: |- docs: |-
This category includes tools that are typically used by developers, also known as "developer tools". This category includes tools that are typically used by developers, also known as "developer tools".
These tools allow a developer to create, test, and debug software. These tools allow a developer to create, test, and debug software.
Their data may leak data about the developer, his/her usage patterns, the environment used for development or the developed project. Their data may leak data about the developer, their usage patterns, the environment used for development or the developed project.
children: children:
- -
name: Clear Python history name: Clear Python history
@@ -479,7 +479,7 @@ actions:
[1]: https://web.archive.org/web/20221029142001/https://en.wikipedia.org/wiki/Visual_Studio_Code "Visual Studio Code | Wikipedia | en.wikipedia.org" [1]: https://web.archive.org/web/20221029142001/https://en.wikipedia.org/wiki/Visual_Studio_Code "Visual Studio Code | Wikipedia | en.wikipedia.org"
children: children:
- -
name: Clear Visual Studio Code Crash Reports name: Clear Visual Studio Code crash reports
recommend: standard recommend: standard
docs: |- docs: |-
Visual Studio Code stores crash reports that later on are uploaded to Microsoft servers by default [1]. Visual Studio Code stores crash reports that later on are uploaded to Microsoft servers by default [1].
@@ -559,7 +559,7 @@ actions:
[1]: https://web.archive.org/web/20221029142932/https://learn.microsoft.com/en-us/cli/azure/ "Azure Command-Line Interface (CLI) - Overview | Microsoft Learn | learn.microsoft.com" [1]: https://web.archive.org/web/20221029142932/https://learn.microsoft.com/en-us/cli/azure/ "Azure Command-Line Interface (CLI) - Overview | Microsoft Learn | learn.microsoft.com"
children: children:
- -
name: Clear Azure CLI telemetry name: Clear Azure CLI telemetry data
recommend: standard recommend: standard
docs: |- docs: |-
The Azure CLI stores telemetry in the `telemetry` directory and `telemetry.txt`, `logs/telemetry.txt` files [1]. The Azure CLI stores telemetry in the `telemetry` directory and `telemetry.txt`, `logs/telemetry.txt` files [1].
@@ -591,7 +591,7 @@ actions:
command: az command: az
code: az cache purge code: az cache purge
- -
name: Clear Azure login data (logs out of the current session) name: Clear Azure login data (this will log you out of the current session)
recommend: strict recommend: strict
docs: |- docs: |-
This script cleans the login data in three steps: This script cleans the login data in three steps:
@@ -652,7 +652,7 @@ actions:
rm -rfv /.cache/epiphany/* rm -rfv /.cache/epiphany/*
# Flatpak installation # Flatpak installation
rm -rfv ~/.var/app/org.gnome.Epiphany/cache/* rm -rfv ~/.var/app/org.gnome.Epiphany/cache/*
# Snap insallation # Snap installation
rm -rfv ~/~/snap/epiphany/common/.cache/* rm -rfv ~/~/snap/epiphany/common/.cache/*
- -
name: Clear GNOME Web browsing history name: Clear GNOME Web browsing history
@@ -733,7 +733,7 @@ actions:
# Snap installation # Snap installation
rm -rfv ~/snap/firefox/common/.cache/* rm -rfv ~/snap/firefox/common/.cache/*
- -
name: Clear Firefox Crash Reports name: Clear Firefox crash reports
recommend: standard recommend: standard
docs: |- docs: |-
Firefox stores crash reports in `~/.mozilla/firefox/Crash Reports/` to submit them later [1]. Firefox stores crash reports in `~/.mozilla/firefox/Crash Reports/` to submit them later [1].
@@ -769,7 +769,7 @@ actions:
parameters: parameters:
path: cookies.sqlite path: cookies.sqlite
- -
name: Clear Firefox URL history (downloads, bookmarks, website visits, annotations, icons, inputs, keywords) name: Clear Firefox browsing history (URLs, downloads, bookmarks, visits, etc.)
docs: |- docs: |-
The file "places.sqlite" stores the annotations, bookmarks, favorite icons, input history, keywords, and browsing history (a record of visited pages) [1]. The file "places.sqlite" stores the annotations, bookmarks, favorite icons, input history, keywords, and browsing history (a record of visited pages) [1].
The tables include [1]: The tables include [1]:
@@ -777,7 +777,7 @@ actions:
- `moz_annos`: Annotations - `moz_annos`: Annotations
- `moz_bookmarks`: Bookmarks - `moz_bookmarks`: Bookmarks
- `moz_bookmarks_roots`: Bookmark roots i.e. places, menu, toolbar, tags, unfiled - `moz_bookmarks_roots`: Bookmark roots i.e. places, menu, toolbar, tags, unfiled
- `moz_favicons`: Favourite icons - including URL of icon - `moz_favicons`: Favorite icons - including URL of icon
- `moz_historyvisits`: A history of the number of times a site has been visited - `moz_historyvisits`: A history of the number of times a site has been visited
- `moz_inputhistory`: A history of URLs typed by the user - `moz_inputhistory`: A history of URLs typed by the user
- `moz_items_annos`: Item annotations - `moz_items_annos`: Item annotations
@@ -860,7 +860,7 @@ actions:
parameters: parameters:
path: formhistory.sqlite path: formhistory.sqlite
- -
name: Clear Firefox multi-account containers data name: Clear Firefox "Multi-Account Containers" data
docs: |- docs: |-
The `containers.json` file stores the details of containers used by the [Container Tabs](https://web.archive.org/web/20221029214648/https://support.mozilla.org/en-US/kb/containers) feature [1]. The `containers.json` file stores the details of containers used by the [Container Tabs](https://web.archive.org/web/20221029214648/https://support.mozilla.org/en-US/kb/containers) feature [1].
@@ -882,7 +882,7 @@ actions:
parameters: parameters:
path: sessionstore.jsonlz4 path: sessionstore.jsonlz4
- -
category: Clear system and/or kernel data category: Clear system and kernel usage data
docs: |- docs: |-
Your system (operating system along with other software on it) and kernel store Your system (operating system along with other software on it) and kernel store
data that may reveal your behavior and can be considered sensitive. These scripts clean data that may reveal your behavior and can be considered sensitive. These scripts clean
@@ -916,7 +916,7 @@ actions:
sudo rm -rfv /var/crash/* sudo rm -rfv /var/crash/*
sudo rm -rfv /var/lib/systemd/coredump/ sudo rm -rfv /var/lib/systemd/coredump/
- -
name: Clear system (journald) logs name: Clear system logs (`journald`)
docs: |- docs: |-
journald is the part of systemd that captures, queries, and removes logs when needed [1]. journald is the part of systemd that captures, queries, and removes logs when needed [1].
It allows removing logs by setting `--vacuum-time` [2]. It allows removing logs by setting `--vacuum-time` [2].
@@ -974,7 +974,7 @@ actions:
and acts like a database of all the recently used files [3]. and acts like a database of all the recently used files [3].
GTK is used by many GUI software. GNOME uses the GTK+ library [2], while KDE uses the QT library [4]. GTK is used by many GUI software. GNOME uses the GTK+ library [2], while KDE uses the QT library [4].
`recently-used.xbel` is also used by other third-party GTK 3 based applications such as Firefox, `recently-used.xbel` is used by other third-party GTK 3 based applications such as Firefox,
Visual Studio Code (and all other Electron applications [5]), Thunderbird… Visual Studio Code (and all other Electron applications [5]), Thunderbird…
See also: ["List of GTK applications | Wikipedia"](https://web.archive.org/web/20221029221112/https://en.wikipedia.org/wiki/List_of_GTK_applications). See also: ["List of GTK applications | Wikipedia"](https://web.archive.org/web/20221029221112/https://en.wikipedia.org/wiki/List_of_GTK_applications).
@@ -995,7 +995,7 @@ actions:
name: Clear KDE-tracked recently used items list name: Clear KDE-tracked recently used items list
recommend: strict recommend: strict
docs: |- docs: |-
`.desktop` files are used by KDE to store recent documents and are exposed as an API [1]. `.desktop` files are used by KDE to store recent documents and are exposed through an API [1].
They are not only specific to the desktop environment, but also used by applications. They are not only specific to the desktop environment, but also used by applications.
Likewise, they are used by the KDE implementation of QT components, such as [QFileDialog](https://web.archive.org/web/20221029221146/https://doc.qt.io/qt-5/qfiledialog.html) Likewise, they are used by the KDE implementation of QT components, such as [QFileDialog](https://web.archive.org/web/20221029221146/https://doc.qt.io/qt-5/qfiledialog.html)
@@ -1013,10 +1013,10 @@ actions:
# From Flatpak packages # From Flatpak packages
rm -rfv ~/.var/app/*/data/*.desktop rm -rfv ~/.var/app/*/data/*.desktop
- -
name: Clear trash name: Empty trash
docs: |- docs: |-
The trash location is standardized by ["The FreeDesktop.org Trash specification"](https://web.archive.org/web/20221029151648/https://specifications.freedesktop.org/trash-spec/trashspec-latest.html) [1]. The trash location is standardized by ["The FreeDesktop.org Trash specification"](https://web.archive.org/web/20221029151648/https://specifications.freedesktop.org/trash-spec/trashspec-latest.html) [1].
It uses the `$XDG_DATA_HOME/Trash` directory, [1] which defaults to `$HOME/.local/share/Trash` [2]. It uses the `$XDG_DATA_HOME/Trash` directory [1], which defaults to `$HOME/.local/share/Trash` [2].
Flatpak applications may use the same trash using special APIs [3]. However, Snap applications do not have an API to Flatpak applications may use the same trash using special APIs [3]. However, Snap applications do not have an API to
use the global trash [4]. use the global trash [4].
@@ -1104,7 +1104,7 @@ actions:
Snap is a software packaging and deployment system developed by Canonical [1]. Snap is a software packaging and deployment system developed by Canonical [1].
The packages are called snaps, and the tool for using them is called snapd [1]. The packages are called snaps, and the tool for using them is called snapd [1].
Snap is available on many distros and is supported out-of-the-box by distros Snap is available on many distros and is supported out-of-the-box by distros
such as Ubuntu, Kubuntu, Xubuntu, Manjoro, Zorin OS, KDE Neon and Solus among such as Ubuntu, Kubuntu, Xubuntu, Manjaro, Zorin OS, KDE Neon and Solus among
others [1]. others [1].
[1]: https://web.archive.org/web/20221029152606/https://en.wikipedia.org/wiki/Snap_%28software%29 "Snap | Wikipedia | en.wikipedia.org" [1]: https://web.archive.org/web/20221029152606/https://en.wikipedia.org/wiki/Snap_%28software%29 "Snap | Wikipedia | en.wikipedia.org"
@@ -1154,7 +1154,7 @@ actions:
[1]: https://web.archive.org/web/20221029153354/https://en.wikipedia.org/wiki/Flatpak "Flatpak - Wikipedia | en.wikipedia.org" [1]: https://web.archive.org/web/20221029153354/https://en.wikipedia.org/wiki/Flatpak "Flatpak - Wikipedia | en.wikipedia.org"
children: children:
- -
name: Uninstall orphaned Flatpak runtimes name: Remove orphaned Flatpak runtimes
recommend: standard recommend: standard
docs: |- docs: |-
This script removes runtimes and extensions that are not used by installed This script removes runtimes and extensions that are not used by installed
@@ -1199,8 +1199,8 @@ actions:
- -
category: Clear APT data category: Clear APT data
docs: |- docs: |-
Advanced Package Tool (or APT) is a free software user interface that works with Advanced Package Tool (APT) is a free software user interface that works with
core libraries to handle the installation and removal of software on Debiana, and core libraries to handle the installation and removal of software on Debian, and
Debian-based Linux distributions [1]. Debian-based Linux distributions [1].
See also: ["APT | Debian Wiki"](https://web.archive.org/web/20221029221422/https://wiki.debian.org/Apt). See also: ["APT | Debian Wiki"](https://web.archive.org/web/20221029221422/https://wiki.debian.org/Apt).
@@ -1222,7 +1222,7 @@ actions:
command: apt-get command: apt-get
code: sudo apt-get autoclean code: sudo apt-get autoclean
- -
name: Clear APT package list name: Clear APT package file lists
docs: |- docs: |-
This script removes package file lists. APT stores a copy of those files (in `/var/lib/apt/lists/`) This script removes package file lists. APT stores a copy of those files (in `/var/lib/apt/lists/`)
and searches are done within those local files to avoid going through the network for every search [1]. and searches are done within those local files to avoid going through the network for every search [1].
@@ -1250,7 +1250,7 @@ actions:
command: apt-get command: apt-get
code: sudo apt-get -y autoremove --purge code: sudo apt-get -y autoremove --purge
- -
name: Clear APT package (.deb file) cache name: Clear cache for APT packages
docs: |- docs: |-
This script runs the `clean` command. It clears out the local repository of retrieved package files [1], This script runs the `clean` command. It clears out the local repository of retrieved package files [1],
meaning that it deletes downloaded packages (`.deb`) already installed (and no longer needed) [2]. meaning that it deletes downloaded packages (`.deb`) already installed (and no longer needed) [2].
@@ -1266,7 +1266,7 @@ actions:
command: apt-get command: apt-get
code: sudo apt-get clean code: sudo apt-get clean
- -
category: Clear all cache category: Clear shared caches
docs: |- docs: |-
The scripts in this category bulk delete all cache entries for the given scope. The scripts in this category bulk delete all cache entries for the given scope.
These scripts are designed to affect more than a single application and do a wide range of cleaning. These scripts are designed to affect more than a single application and do a wide range of cleaning.
@@ -1303,13 +1303,13 @@ actions:
such as [2]: such as [2]:
- Locally-generated fonts (`/var/cache/fonts`) - Locally-generated fonts (`/var/cache/fonts`)
- Locally-formatted manual pages (`/var/cache/man`) - Locally-formatted manual pages (`/var/cache/man`)
- WWW proxy or cache data (`/var/cache/www`) - WWW proxy or cache data (`/var/cache/www`)
[1]: https://web.archive.org/web/20130511220135/http://www.lindevdoc.org/wiki//var/cache "/var/cache - Linux Developer's Documentation" [1]: https://web.archive.org/web/20130511220135/http://www.lindevdoc.org/wiki//var/cache "/var/cache - Linux Developer's Documentation"
[2]: https://web.archive.org/web/20221029154623/https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch05s05.html "5.5. /var/cache : Application cache data | refspecs.linuxfoundation.org" [2]: https://web.archive.org/web/20221029154623/https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch05s05.html "5.5. /var/cache : Application cache data | refspecs.linuxfoundation.org"
code: rm -rf /var/cache/* code: rm -rf /var/cache/*
- -
name: Clear cache from Flatpak applications name: Clear Flatpak application cache
docs: |- docs: |-
Non-essential user-specific data is stored in `~/.var/app/<app-id>/cache` by Flatpak applications [1]. Non-essential user-specific data is stored in `~/.var/app/<app-id>/cache` by Flatpak applications [1].
This directory points to `XDG_CACHE_HOME` [1] from the XDG base directory specification [2]. This directory points to `XDG_CACHE_HOME` [1] from the XDG base directory specification [2].
@@ -1318,7 +1318,7 @@ actions:
[2]: https://web.archive.org/web/20221029151712/https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html "XDG Base Directory Specification | specifications.freedesktop.org" [2]: https://web.archive.org/web/20221029151712/https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html "XDG Base Directory Specification | specifications.freedesktop.org"
code: rm -rfv ~/.var/app/*/cache/* code: rm -rfv ~/.var/app/*/cache/*
- -
name: Clear cache from Snap applications name: Clear Snap application cache
docs: |- docs: |-
Application caches are isolated per application and stored in the `~/snaps/APP-NAME/VERSION/.cache` Application caches are isolated per application and stored in the `~/snaps/APP-NAME/VERSION/.cache`
folders for Snap applications [1]. folders for Snap applications [1].
@@ -1408,7 +1408,7 @@ actions:
[4]: https://web.archive.org/web/20221030133248/https://www.debian.org/legal/privacy "Debian -- Privacy Policy | www.debian.org" [4]: https://web.archive.org/web/20221030133248/https://www.debian.org/legal/privacy "Debian -- Privacy Policy | www.debian.org"
children: children:
- -
category: Disable sending package statistics (popcon) category: Disable sending of package statistics (popcon)
docs: |- docs: |-
Popularity Contest (or popcon) collects statistical data to determine which packages are the most popular [1]. Popularity Contest (or popcon) collects statistical data to determine which packages are the most popular [1].
It sends a list of packages installed and the access and change times of relevant files to the server via email It sends a list of packages installed and the access and change times of relevant files to the server via email
@@ -1433,11 +1433,11 @@ actions:
[4]: https://web.archive.org/web/20221029160841/https://popcon.debian.org/FAQ "FAQ | popcon | popcon.debian.org" [4]: https://web.archive.org/web/20221029160841/https://popcon.debian.org/FAQ "FAQ | popcon | popcon.debian.org"
children: children:
- -
name: Opt-out of the popularity contest name: Disable participation in Popularity Contest
recommend: standard recommend: standard
docs: |- docs: |-
`popularity-contest` checks `/etc/popularity-contest.conf` for the value of `PARTICIPATE` to send data [1]. `popularity-contest` checks `/etc/popularity-contest.conf` for the value of `PARTICIPATE` to send data [1].
Configuring this configuration allows you to opt-out of the submission of data [1]. Configuring this configuration allows you to opt out of the submission of data [1].
[1]: https://web.archive.org/web/20221029161047/https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=172824 "#172824 - popularity-contest: dpkg-reconfigure popularity-contest does not include PARTICIPATE=yes - Debian Bug report logs | bugs.debian.org" [1]: https://web.archive.org/web/20221029161047/https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=172824 "#172824 - popularity-contest: dpkg-reconfigure popularity-contest does not include PARTICIPATE=yes - Debian Bug report logs | bugs.debian.org"
code: |- code: |-
@@ -1455,7 +1455,7 @@ actions:
echo "Skipping because configuration file ($config_file) is not found. Is popcon installed?" echo "Skipping because configuration file ($config_file) is not found. Is popcon installed?"
fi fi
- -
name: Uninstall Popularity Contest (popcon) name: Remove Popularity Contest (`popcon`) package
recommend: strict recommend: strict
docs: |- docs: |-
This script deletes the `popularity-contest` package. This script deletes the `popularity-contest` package.
@@ -1501,7 +1501,7 @@ actions:
[2]: https://web.archive.org/web/20221029161559/https://manpages.ubuntu.com/manpages/trusty/man1/reportbug.1.html "Ubuntu Manpage: reportbug - reports a bug to a debbugs server | manpages.ubuntu.com" [2]: https://web.archive.org/web/20221029161559/https://manpages.ubuntu.com/manpages/trusty/man1/reportbug.1.html "Ubuntu Manpage: reportbug - reports a bug to a debbugs server | manpages.ubuntu.com"
children: children:
- -
name: Uninstall `reportbug` package name: Remove `reportbug` package
recommend: strict recommend: strict
docs: |- docs: |-
This script uninstalls `reportbug` package, which includes the tool itself. This script uninstalls `reportbug` package, which includes the tool itself.
@@ -1513,7 +1513,7 @@ actions:
parameters: parameters:
packageName: reportbug packageName: reportbug
- -
name: Uninstall Python modules for reportbug name: Remove Python modules for `reportbug`
recommend: strict recommend: strict
docs: |- docs: |-
This script uninstalls `python3-reportbug`. This script uninstalls `python3-reportbug`.
@@ -1526,7 +1526,7 @@ actions:
parameters: parameters:
packageName: python3-reportbug packageName: python3-reportbug
- -
name: Uninstall UI for reportbug (`reportbug-gtk` package) name: Remove UI for reportbug (`reportbug-gtk` package)
recommend: strict recommend: strict
docs: |- docs: |-
This script uninstalls `reportbug-gtk`. It consists of a desktop file and an icon, and it has dependencies to enable the This script uninstalls `reportbug-gtk`. It consists of a desktop file and an icon, and it has dependencies to enable the
@@ -1559,7 +1559,7 @@ actions:
[2]: https://web.archive.org/web/20221029161821/https://pkgstats.archlinux.de/privacy-policy "Privacy policy | pkgstats | pkgstats.archlinux.de" [2]: https://web.archive.org/web/20221029161821/https://pkgstats.archlinux.de/privacy-policy "Privacy policy | pkgstats | pkgstats.archlinux.de"
children: children:
- -
name: Uninstall `pkgstats` package name: Remove `pkgstats` package
recommend: strict recommend: strict
docs: |- docs: |-
`pkgstats` package submits a list of installed packages to the Arch Linux project [1]. `pkgstats` package submits a list of installed packages to the Arch Linux project [1].
@@ -1573,7 +1573,7 @@ actions:
parameters: parameters:
packageName: pkgstats packageName: pkgstats
- -
name: Disable weekly pkgstats submission name: Disable weekly `pkgstats` submission
recommend: standard recommend: standard
docs: |- docs: |-
`pkgstats` is set up to automatically run every week using systemd/timers [1]. Once disabled, `pkgstats` is set up to automatically run every week using systemd/timers [1]. Once disabled,
@@ -1609,7 +1609,7 @@ actions:
parameters: parameters:
packageName: zorin-os-census packageName: zorin-os-census
- -
name: Remove the Zorin OS census unique ID name: Remove Zorin OS census unique ID
docs: |- docs: |-
Census submits a unique ID associated with the installation when pings are sent [1]. Census submits a unique ID associated with the installation when pings are sent [1].
Removing the identifier reduces the risk of your computer being identified. Removing the identifier reduces the risk of your computer being identified.
@@ -1634,7 +1634,7 @@ actions:
[2]: https://web.archive.org/web/20221029160241/https://en.wikipedia.org/wiki/List_of_Linux_distributions#Ubuntu-based "List of Linux distributions - Wikipedia | en.wikipedia.org" [2]: https://web.archive.org/web/20221029160241/https://en.wikipedia.org/wiki/List_of_Linux_distributions#Ubuntu-based "List of Linux distributions - Wikipedia | en.wikipedia.org"
children: children:
- -
name: Disable online search results (collects queries) name: Disable online search result collection (collects queries)
recommend: strict recommend: strict
docs: |- docs: |-
Online search is introduced in Ubuntu Lens in Ubuntu 12.10 [1]. It has been known as "Shopping Lens", Online search is introduced in Ubuntu Lens in Ubuntu 12.10 [1]. It has been known as "Shopping Lens",
@@ -1652,7 +1652,7 @@ actions:
code: gsettings set com.canonical.Unity.Lenses remote-content-search none code: gsettings set com.canonical.Unity.Lenses remote-content-search none
revertCode: gsettings set com.canonical.Unity.Lenses remote-content-search all revertCode: gsettings set com.canonical.Unity.Lenses remote-content-search all
- -
category: Disable Ubuntu reporting metrics category: Disable metrics reporting in Ubuntu
docs: |- docs: |-
`ubuntu-report` reports hardware and other collected metrics like installer or upgrade information [1]. `ubuntu-report` reports hardware and other collected metrics like installer or upgrade information [1].
@@ -1682,7 +1682,7 @@ actions:
[2]: https://web.archive.org/web/20221029162538/https://github.com/ubuntu/ubuntu-report/blob/8e6030ff9bbeacacf41a9b58ea638a5c9a6f864d/README.md "More diagnostics data from desktop | lists.ubuntu.com" [2]: https://web.archive.org/web/20221029162538/https://github.com/ubuntu/ubuntu-report/blob/8e6030ff9bbeacacf41a9b58ea638a5c9a6f864d/README.md "More diagnostics data from desktop | lists.ubuntu.com"
children: children:
- -
name: Opt-out of Ubuntu reporting metrics name: Disable participation in metrics reporting in Ubuntu
recommend: standard recommend: standard
docs: |- docs: |-
This script uses the `ubuntu-report` CLI to opt you out of data submission [1]. This script uses the `ubuntu-report` CLI to opt you out of data submission [1].
@@ -1708,7 +1708,7 @@ actions:
>&2 echo 'Failed to opt in.' >&2 echo 'Failed to opt in.'
fi fi
- -
name: Uninstall Ubuntu Report tool (`ubuntu-report`) name: Remove Ubuntu Report tool (`ubuntu-report`)
recommend: strict recommend: strict
docs: |- docs: |-
`ubuntu-report` is installed as an apt package and can be uninstalled in this way [1]. `ubuntu-report` is installed as an apt package and can be uninstalled in this way [1].
@@ -1762,7 +1762,7 @@ actions:
[4]: https://web.archive.org/web/20221029150025/https://support.starlabs.systems/kb/guides/disable-program-problem-reports "Disable program problem reports - Star Labs | support.starlabs.systems" [4]: https://web.archive.org/web/20221029150025/https://support.starlabs.systems/kb/guides/disable-program-problem-reports "Disable program problem reports - Star Labs | support.starlabs.systems"
children: children:
- -
name: Uninstall `apport` package name: Remove `apport` package
recommend: strict recommend: strict
docs: |- docs: |-
The `apport` package is responsible for automatically generating crash reports for debugging [1]. The `apport` package is responsible for automatically generating crash reports for debugging [1].
@@ -1804,7 +1804,7 @@ actions:
parameters: parameters:
serviceName: apport serviceName: apport
- -
name: Opt-out of Apport error messaging system name: Disable participation in Apport error messaging system
recommend: standard recommend: standard
docs: |- docs: |-
Opting out of Apport prevents it from creating crash files after a crash [1]. Opting out of Apport prevents it from creating crash files after a crash [1].
@@ -1853,7 +1853,7 @@ actions:
[3]: https://web.archive.org/web/20221029162829/https://help.ubuntu.com/community/ReportingBugs#A4._Collect_information_about_the_bug "ReportingBugs - Community Help Wiki | help.ubuntu.com" [3]: https://web.archive.org/web/20221029162829/https://help.ubuntu.com/community/ReportingBugs#A4._Collect_information_about_the_bug "ReportingBugs - Community Help Wiki | help.ubuntu.com"
children: children:
- -
name: Uninstall `whoopsie` package name: Remove `whoopsie` package
docs: |- docs: |-
This script uninstalls the `whoopsie` package. This script uninstalls the `whoopsie` package.
This package is used for error tracker submission in Ubuntu [1]. This package is used for error tracker submission in Ubuntu [1].
@@ -1879,7 +1879,7 @@ actions:
parameters: parameters:
serviceName: whoopsie serviceName: whoopsie
- -
name: Opt-out of reporting crashes name: Disable crash report submissions
recommend: standard recommend: standard
docs: |- docs: |-
Once opted-out Whoopsie disables the related service [1]. Once opted-out Whoopsie disables the related service [1].
@@ -1897,7 +1897,7 @@ actions:
sudo sed -i 's/report_crashes=false/report_crashes=true/' /etc/default/whoopsie sudo sed -i 's/report_crashes=false/report_crashes=true/' /etc/default/whoopsie
fi fi
- -
category: Disable Zeitgeist (activity logging framework) category: Disable Zeitgeist activity logging
docs: |- docs: |-
Zeitgeist logs files opened, websites visited, conversations, and emails and provides Zeitgeist logs files opened, websites visited, conversations, and emails and provides
this information over an API to applications [1]. this information over an API to applications [1].
@@ -1931,7 +1931,7 @@ actions:
[7]: https://web.archive.org/web/20221029164405/https://launchpad.net/synapse-project "Synapse in Launchpad | launchpad.net" [7]: https://web.archive.org/web/20221029164405/https://launchpad.net/synapse-project "Synapse in Launchpad | launchpad.net"
children: children:
- -
name: Stop the Zeitgeist deamon name: Kill Zeitgeist daemon process
docs: |- docs: |-
`zeitgeist-daemon` is a daemon providing an activity log [1]. Activity logs consist of a central `zeitgeist-daemon` is a daemon providing an activity log [1]. Activity logs consist of a central
database for events such as file usage, browser history, and calendar entries [1]. database for events such as file usage, browser history, and calendar entries [1].
@@ -1957,7 +1957,7 @@ actions:
# > used, zeitgeist-daemon will refuse to start if another running instance is found. # > used, zeitgeist-daemon will refuse to start if another running instance is found.
revertCode: zeitgeist-daemon --replace revertCode: zeitgeist-daemon --replace
- -
name: Prevent Zeitgeist from running on startup name: Remove Zeitgeist startup entry
docs: |- docs: |-
This script removes the Autostart entry that is used by the Zeitgeist package to start itself [1]. This script removes the Autostart entry that is used by the Zeitgeist package to start itself [1].
[The XDG Autostart specification](https://web.archive.org/web/20221029223114/https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html) [The XDG Autostart specification](https://web.archive.org/web/20221029223114/https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html)
@@ -2001,7 +2001,7 @@ actions:
>&2 echo "Failed to restore access, file does not exist at $file." >&2 echo "Failed to restore access, file does not exist at $file."
fi fi
- -
name: Uninstall the Zeitgeist package (can break integrated software) name: Remove Zeitgeist package (can break integrated software)
docs: |- docs: |-
The main functionality for Zeitgeist is provided by the `zeitgeist` package in Fedora [1] and Arch Linux [2], The main functionality for Zeitgeist is provided by the `zeitgeist` package in Fedora [1] and Arch Linux [2],
and the `zeitgeist-core` package in Debian [3]. and the `zeitgeist-core` package in Debian [3].
@@ -2026,7 +2026,7 @@ actions:
parameters: parameters:
packageName: zeitgeist-core packageName: zeitgeist-core
# Other related packages include (apt list *zeitgeist*): # Other related packages include (apt list *zeitgeist*):
# zeitgeist-core, zeitgeist-datahub, python3-zeitgeist among many others. # zeitgeist-core, zeitgeist-datahub, python3-zeitgeist among many others.
# Packages that use it include e.g. activity-log-manager, activity-log-manager-control-center.. # Packages that use it include e.g. activity-log-manager, activity-log-manager-control-center..
- -
function: UninstallPacmanPackage function: UninstallPacmanPackage
@@ -2073,11 +2073,11 @@ actions:
echo "Skipping, connectivity checks are already disabled through $file." echo "Skipping, connectivity checks are already disabled through $file."
else else
echo -n "$content" | sudo tee "$file" > /dev/null echo -n "$content" | sudo tee "$file" > /dev/null
echo 'Successfully disabled connectivity checks-' echo 'Successfully disabled connectivity checks.'
fi fi
if command -v 'nmcli' &> /dev/null; then if command -v 'nmcli' &> /dev/null; then
sudo nmcli general reload sudo nmcli general reload
echo 'Successfuly reloaded configuration.' echo 'Successfully reloaded configuration.'
else else
echo 'It will take effect after reboot.' echo 'It will take effect after reboot.'
fi fi
@@ -2086,11 +2086,11 @@ actions:
echo 'Skipping, connectivity checks are not disabled.' echo 'Skipping, connectivity checks are not disabled.'
else else
sudo rm -fv "$file" sudo rm -fv "$file"
echo 'Successfuly restored connectivity checks.' echo 'Successfully restored connectivity checks.'
fi fi
if command -v 'nmcli' &> /dev/null; then if command -v 'nmcli' &> /dev/null; then
sudo nmcli general reload sudo nmcli general reload
echo 'Successfuly reloaded configuration.' echo 'Successfully reloaded configuration.'
else else
echo 'It will take effect after reboot.' echo 'It will take effect after reboot.'
fi fi
@@ -2197,7 +2197,7 @@ actions:
jsonValue: >- jsonValue: >-
false false
- -
name: Do not run Microsoft online experiments on Visual Studio Code name: Disable online experiments by Microsoft in Visual Studio Code
recommend: standard recommend: standard
docs: |- docs: |-
VS Code employs experiments to test new features or gradually roll them out [1]. VS Code employs experiments to test new features or gradually roll them out [1].
@@ -2217,7 +2217,7 @@ actions:
jsonValue: >- jsonValue: >-
false false
- -
name: Choose manual Visual Studio Code updates over automatic updates name: Disable Visual Studio Code automatic updates in favor of manual updates
docs: |- docs: |-
By default, VS Code is configured to automatically update when new versions are released [1]. By default, VS Code is configured to automatically update when new versions are released [1].
Automatic updates reduce your control over privacy by sending data to Microsoft servers. Automatic updates reduce your control over privacy by sending data to Microsoft servers.
@@ -2245,7 +2245,7 @@ actions:
jsonValue: >- jsonValue: >-
"none" "none"
- -
name: Prevent fetching Visual Studio Code release notes from Microsoft servers name: Disable fetching release notes from Microsoft servers after an update
docs: |- docs: |-
This script prevents Visual Studio Code from displaying release notes after an update [1]. This script prevents Visual Studio Code from displaying release notes after an update [1].
@@ -2262,7 +2262,7 @@ actions:
jsonValue: >- jsonValue: >-
false false
- -
category: Configure auto-updates from Visual Studio Code extensions category: Configure auto-update settings for Visual Studio Code extensions
docs: |- docs: |-
These scripts control the automatic update behavior of extensions. These scripts control the automatic update behavior of extensions.
Updates are fetched from Microsoft servers [1] [2]. Updates are fetched from Microsoft servers [1] [2].
@@ -2273,7 +2273,7 @@ actions:
[2]: https://web.archive.org/web/20221029171719/https://github.com/microsoft/vscode-docs/blob/9a900b380e11530376104ffc83a004b82553728e/docs/editor/extension-marketplace.md#extension-auto-update "vscode-docs/extension-marketplace.md at 9a900b380e11530376104ffc83a004b82553728e · microsoft/vscode-docs · GitHub | github.com" [2]: https://web.archive.org/web/20221029171719/https://github.com/microsoft/vscode-docs/blob/9a900b380e11530376104ffc83a004b82553728e/docs/editor/extension-marketplace.md#extension-auto-update "vscode-docs/extension-marketplace.md at 9a900b380e11530376104ffc83a004b82553728e · microsoft/vscode-docs · GitHub | github.com"
children: children:
- -
name: Prevent auto-updates of Visual Studio Code extensions name: Disable automatic Visual Studio Code extension updates
docs: |- docs: |-
By default, Visual Studio Code automatically downloads and installs updates for all By default, Visual Studio Code automatically downloads and installs updates for all
extensions [1]. These updates are fetched from a Microsoft online service [1]. extensions [1]. These updates are fetched from a Microsoft online service [1].
@@ -2290,7 +2290,7 @@ actions:
jsonValue: >- jsonValue: >-
false false
- -
name: Prevent automatically checking Visual Studio Code extension updates from Microsoft servers name: Disable Visual Studio Code automatic extension update checks
docs: |- docs: |-
By default, Visual Studio Code automatically checks extensions for updates [1]. By default, Visual Studio Code automatically checks extensions for updates [1].
If an update for an extension is available, it is marked as outdated in the Extensions view [1]. If an update for an extension is available, it is marked as outdated in the Extensions view [1].
@@ -2308,7 +2308,7 @@ actions:
jsonValue: >- jsonValue: >-
false false
- -
name: Disable auto-fetching Microsoft recommendations in Visual Studio Code name: Disable automatic fetching of Microsoft recommendations in Visual Studio Code
recommend: strict recommend: strict
docs: |- docs: |-
Visual Studio Code, by default, fetches recommendations from online Microsoft servers [1]. Visual Studio Code, by default, fetches recommendations from online Microsoft servers [1].
@@ -2328,7 +2328,7 @@ actions:
jsonValue: >- jsonValue: >-
true true
- -
name: Disable automatic fetching remote repository in Visual Studio Code name: Disable automatic fetching of remote repositories in Visual Studio Code
docs: |- docs: |-
Visual Studio Code (VS Code) periodically fetches changes from remote repositories [1]. Visual Studio Code (VS Code) periodically fetches changes from remote repositories [1].
This feature lets VS Code display how many changes your local repository is ahead or behind the remote [1]. This feature lets VS Code display how many changes your local repository is ahead or behind the remote [1].
@@ -2353,7 +2353,7 @@ actions:
jsonValue: >- jsonValue: >-
false false
- -
name: Prevent fetching package information from NPM and Bower in Visual Studio Code name: Disable fetching package information from NPM and Bower in Visual Studio Code
docs: |- docs: |-
Visual Studio Code fetches data from NPM and Bower to provide autocompletion and hover information Visual Studio Code fetches data from NPM and Bower to provide autocompletion and hover information
for npm dependencies [1]. By default, this feature is enabled [2] and fetches data from for npm dependencies [1]. By default, this feature is enabled [2] and fetches data from
@@ -2445,7 +2445,7 @@ actions:
jsonValue: >- jsonValue: >-
false false
- -
category: Disable Visual Studio Code Settings Sync data sharing category: Disable data sharing for Visual Studio Code "Settings Sync"
docs: |- docs: |-
**Settings Sync** is a Visual Studio Code feature that synchronizes settings, keybindings, **Settings Sync** is a Visual Studio Code feature that synchronizes settings, keybindings,
and installed extensions across different machines [1]. This data is stored in the Microsoft and installed extensions across different machines [1]. This data is stored in the Microsoft
@@ -2454,7 +2454,7 @@ actions:
[1]: https://web.archive.org/web/20221029172856/https://github.com/microsoft/vscode-docs/blob/9a900b380e11530376104ffc83a004b82553728e/docs/editor/settings-sync.md "vscode-docs/settings-sync.md at main · microsoft/vscode-docs · GitHub | github.com" [1]: https://web.archive.org/web/20221029172856/https://github.com/microsoft/vscode-docs/blob/9a900b380e11530376104ffc83a004b82553728e/docs/editor/settings-sync.md "vscode-docs/settings-sync.md at main · microsoft/vscode-docs · GitHub | github.com"
children: children:
- -
name: Disable synchronizing Visaul Studio Code keybindings name: Disable synchronization of Visual Studio Code keybindings
docs: |- docs: |-
By default [1], Keyboard Shortcuts are synchronized across platforms [2]. By default [1], Keyboard Shortcuts are synchronized across platforms [2].
You can disable this by setting `settingsSync.keybindingsPerPlatform` to `false` [1] [2]. You can disable this by setting `settingsSync.keybindingsPerPlatform` to `false` [1] [2].
@@ -2468,7 +2468,7 @@ actions:
jsonValue: >- jsonValue: >-
false false
- -
name: Disable synchronizing Visual Studio Code extension name: Disable synchronization of Visual Studio Code extensions
docs: |- docs: |-
Visual Studio Code synchronizes all built-in and installed extensions, along with their Visual Studio Code synchronizes all built-in and installed extensions, along with their
global enablement state, by default [1] [2]. global enablement state, by default [1] [2].
@@ -2487,8 +2487,8 @@ actions:
jsonValue: >- jsonValue: >-
["*"] ["*"]
- -
name: Disable synchronizing Visual Studio Code settings name: Disable synchronization of Visual Studio Code settings
docs: |- docs: |-
By default, Visual Studio Code synchronizes all settings, except for machine settings By default, Visual Studio Code synchronizes all settings, except for machine settings
(those with machine or machine-overridable scopes), as these are specific to a given machine [1] [2]. (those with machine or machine-overridable scopes), as these are specific to a given machine [1] [2].
@@ -2573,22 +2573,131 @@ actions:
See also: [What is browser fingerprinting? | AmIUnique.org](https://web.archive.org/web/20221029223510/https://www.amiunique.org/faq) See also: [What is browser fingerprinting? | AmIUnique.org](https://web.archive.org/web/20221029223510/https://www.amiunique.org/faq)
children: children:
- -
name: Enable Firefox First party isolation category: Enable Firefox state partitioning (Total Cookie Protection)
recommend: strict
docs: |- docs: |-
First-party isolation (also known as "double keying") can prevent third parties from tracking Web browsers, including Firefox, save various data types such as cookies, cache, and site-specific details.
users across multiple sites [1]. While this data helps in providing a faster and personalized browsing experience, it can be exploited by websites to track
your activities across the internet, potentially compromising your privacy.
This script configures `privacy.firstparty.isolate` to be enabled, preventing third parties State partitioning, also known as "Total Cookie Protection" [1], is a feature designed to enhance user privacy in Firefox.
from tracking users across websites, also known as supercookies [2]. It works by allocating different, isolated storage spaces for every website you visit [2]. This means that each website has its own
"compartment" where it saves its data, separate from other sites [2]. This structure limits websites' capabilities to track users
across various domains.
[1]: https://web.archive.org/web/20221025162743/https://wiki.archlinux.org/title/Firefox/Privacy#First_party_isolation "Firefox/Privacy - ArchWiki | wiki.archlinux.org" The underlying technology for state partitioning in Firefox is termed "double-keying" [1]. In this method, when a website intends
[2]: https://web.archive.org/web/20221025200527/https://bugzilla.mozilla.org/show_bug.cgi?id=1397624#c0 to store data, Firefox attaches an extra identifier tied to the site's origin, ensuring unique data storage for each site [1]. For
call: example, if two different sites incorporate content from the same third-party source, each of these sites will have its own unique
function: AddFirefoxPrefs version of the third-party's data (like cookies) due to state partitioning [1]. This impedes the third-party's tracking ability
parameters: between the sites.
prefName: privacy.firstparty.isolate
jsonValue: 'true' This protection isn't just against known trackers [1]. Firefox applies state partitioning to all third-party content on a site,
ensuring a comprehensive privacy coverage, beyond just identifiable tracking sources [1] [3].
A notable misuse by some trackers is the creation of "supercookies" [4]. Contrary to standard cookies, which users can delete easily,
supercookies are harder to eliminate and block, posing a considerable privacy challenge. Through state partitioning, Firefox renders
supercookies ineffective for tracking users across sites [4]. As a part of this feature, Firefox not only ensures site-specific data
but also partitions multiple caches, such as HTTP cache, image cache, and favicon cache [4]. This partitioning prevents any potential
cache exploitation for tracking purposes [4].
In summary, enabling state partitioning in Firefox is a powerful privacy tool, helping to defend users from potential online tracking
and offering a more private browsing experience.
[1]: https://web.archive.org/web/20230918171957/https://hacks.mozilla.org/2021/02/introducing-state-partitioning/ "Introducing State Partitioning - Mozilla Hacks - the Web developer blog"
[2]: https://web.archive.org/web/20230918172155/https://developer.mozilla.org/en-US/docs/Web/Privacy/State_Partitioning "State Partitioning - Privacy on the web | MDN"
[3]: https://web.archive.org/web/20230918172352/https://blog.mozilla.org/security/2021/02/23/total-cookie-protection/ "Firefox 86 Introduces Total Cookie Protection - Mozilla Security Blog"
[4]: https://web.archive.org/web/20230918172503/https://blog.mozilla.org/security/2021/01/26/supercookie-protections/ "Firefox 85 Cracks Down on Supercookies - Mozilla Security Blog"
children:
-
name: Enable dynamic First-Party Isolation (dFPI)
recommend: standard
docs: |-
Dynamic First-Party Isolation, also known as dFPI, is an advanced privacy feature in Firefox. This feature commonly
referred to as:
- Total Cookie Protection [1],
- dFPI (dynamic First-Party Isolation) [2],
- Dynamic storage partitioning [3].
Essentially, dFPI is an enhanced version of a previous privacy tool known as First-Party Isolation (FPI) [4].
The primary purpose of dFPI is to improve user privacy online. It accomplishes this by preventing third-party websites from
accessing or tracking a user's data across different websites [1] [3].
By default, this feature is activated for all Firefox desktop users [5].
Within Firefox's settings, there's an option called `network.cookie.cookieBehavior` which governs how dFPI operates.
This setting has three potential values [3]:
- `5`: The browser will block known trackers and partition storage for third-party content.
- `4`: Only known trackers will be blocked without any partitioning of third-party storage.
- `0`: All trackers and third-party content are allowed.
This script sets the value to `5`, ensuring the highest level of privacy by blocking trackers and partitioning third-party
storage. This aligns with recommended privacy practices because even if you choose the `4` value, the older First-Party
Isolation (FPI) will still be active [6].
[1]: https://web.archive.org/web/20231003094145/https://support.mozilla.org/en-US/kb/total-cookie-protection-and-website-breakage-faq "Total Cookie Protection and website breakage FAQ | Firefox Help"
[2]: https://web.archive.org/web/20231003094154/https://bugzilla.mozilla.org/show_bug.cgi?id=1746646 "1746646 - (tcp-mochitests) [meta] Make mochitests work with TCP enabled (cookieBehavior = 5) | bugzilla.mozilla.org"
[3]: https://web.archive.org/web/20230918172155/https://developer.mozilla.org/en-US/docs/Web/Privacy/State_Partitioning#disable_dynamic_state_partitioning "State Partitioning - Privacy on the web | MDN"
[4]: https://web.archive.org/web/20231003094207/https://bugzilla.mozilla.org/show_bug.cgi?id=1649876#c5 "1649876 - Migrate FPI users to dFPI | bugzilla.mozilla.org"
[5]: https://blog.mozilla.org/en/products/firefox/firefox-rolls-out-total-cookie-protection-by-default-to-all-users-worldwide/ "Firefox Rolls Out Total Cookie Protection By Default"
[6]: https://web.archive.org/web/20231003094350/https://bugzilla.mozilla.org/show_bug.cgi?id=1631676#c25 "1631676 - Disable dfpi when privacy.firstparty.isolate=true | bugzilla.mozilla.org"
call:
function: AddFirefoxPrefs
parameters:
prefName: network.cookie.cookieBehavior
jsonValue: '5'
-
name: Enable Firefox network partitioning
recommend: standard
docs: |-
Network partitioning is a method used by Firefox to enhance user privacy [1]. When enabled, each website you visit has its own
isolated storage location, preventing it from accessing data from another website [1]. This limits the ability of websites to track
users across multiple sites [1].
Network Partitioning, formerly referred to as *cache partitioning* [2], is a subset of state partitioning [1]. While state partitioning
deals with data like cookies, network partitioning deals with networking-related components, such as caches and connection pools [1].
It ensures that these components are isolated to each website, further enhancing user privacy [1].
Firefox has enabled network partitioning by default since version 85 [1]. Once enabled, network partitioning becomes permanent,
meaning websites cannot bypass or weaken its restrictions [1].
Network partitioning can be controlled with the `privacy.partition.network_state` preference [1].
[1]: https://web.archive.org/web/20230918172155/https://developer.mozilla.org/en-US/docs/Web/Privacy/State_Partitioning "State Partitioning - Privacy on the web | MDN"
[2]: https://web.archive.org/web/20231003094417/https://bugzilla.mozilla.org/show_bug.cgi?id=1687569 "1687569 - cache partitioning causes web page saving extensions to waste more time and data | bugzilla.mozilla.org"
call:
function: AddFirefoxPrefs
parameters:
prefName: privacy.partition.network_state
jsonValue: 'true'
-
name: Disable outdated Firefox First-Party Isolation (FPI)
recommend: strict
docs: |-
First-party isolation (FPI) helps in preventing third parties from tracking users across multiple websites [1] [2]. This is sometimes
referred to as "double keying" (double-keying) [1] or supercookies [1] [2].
This script disables FPI in favor of a newer technology called dynamic First-Party Isolation (dFPI) for the following reasons:
1. FPI and dFPI conflict with each other, and they cannot function simultaneously [3] [4]. Additionally, Mozilla doesn't plan to make
them compatible [5].
2. FPI has been marked as deprecated and is expected to be phased out [3], with dFPI set to become the standard in the future [6].
3. dFPI, when combined with network partitioning, offers broader and more effective privacy coverage than FPI, being essentially a
superset of FPI [3] [7].
[1]: https://web.archive.org/web/20221025162743/https://wiki.archlinux.org/title/Firefox/Privacy#First_party_isolation "Firefox/Privacy - ArchWiki | wiki.archlinux.org"
[2]: https://web.archive.org/web/20221025200527/https://bugzilla.mozilla.org/show_bug.cgi?id=1397624#c0 "1397624 - Provide an option for first-party isolation in Private Browsing Mode | bugzilla.mozilla.org"
[3]: https://web.archive.org/web/20231003094207/https://bugzilla.mozilla.org/show_bug.cgi?id=1649876#c0 "1649876 - Migrate FPI users to dFPI | bugzilla.mozilla.org"
[4]: https://web.archive.org/web/20231003094350/https://bugzilla.mozilla.org/show_bug.cgi?id=1631676#c25 "1631676 - Disable dfpi when privacy.firstparty.isolate=true | bugzilla.mozilla.org"
[5]: https://web.archive.org/web/20231003094207/https://bugzilla.mozilla.org/show_bug.cgi?id=1649876#c3 "1649876 - Migrate FPI users to dFPI | bugzilla.mozilla.org"
[6]: https://web.archive.org/web/20231003094207/https://bugzilla.mozilla.org/show_bug.cgi?id=1649876#c5 "1649876 - Migrate FPI users to dFPI | bugzilla.mozilla.org"
[7]: https://web.archive.org/web/20231003094358/https://bugzilla.mozilla.org/show_bug.cgi?id=1637344#c2 "1637344 - Add message to show dFPI is incompatible with FPI | bugzilla.mozilla.org"
call:
function: AddFirefoxPrefs
parameters:
prefName: privacy.firstparty.isolate
jsonValue: 'false'
- -
name: Enable Firefox tracking protection name: Enable Firefox tracking protection
recommend: standard recommend: standard
@@ -2601,7 +2710,7 @@ actions:
This script enables the `privacy.resistFingerprinting` preference, This script enables the `privacy.resistFingerprinting` preference,
activating anti-fingerprinting [1][2]. activating anti-fingerprinting [1][2].
Beyond privacy advantages, enabling tracking protection may reduce load time by 44% [3]. Beyond privacy advantages; enabling tracking protection may reduce load time by 44% [3].
Note: If you are already using an ad blocker with the correct lists, tracking protection might Note: If you are already using an ad blocker with the correct lists, tracking protection might
be redundant. be redundant.
@@ -2618,9 +2727,9 @@ actions:
name: Enable Firefox anti-fingerprinting (may break some websites) name: Enable Firefox anti-fingerprinting (may break some websites)
recommend: strict recommend: strict
docs: |- docs: |-
Fingerprinting Protection is a heavily developing experimental feature in Firefox [1]. Fingerprinting Protection is a heavily developed experimental feature in Firefox [1].
As part of a project to integrate features from the Tor Browser, Mozilla has launched an As part of a project to integrate features from the Tor Browser, Mozilla launched an
anti-fingerprinting project in Firefox [2]. anti-fingerprinting project in Firefox [2].
This script enables the `privacy.resistFingerprinting` preference, activating This script enables the `privacy.resistFingerprinting` preference, activating
anti-fingerprinting [1][2]. anti-fingerprinting [1][2].
@@ -2647,7 +2756,7 @@ actions:
WebRTC (Web Real-Time Communication) is a free and open-source project offering real-time WebRTC (Web Real-Time Communication) is a free and open-source project offering real-time
communication (RTC) via application programming interfaces (APIs) to web browsers and mobile communication (RTC) via application programming interfaces (APIs) to web browsers and mobile
applications [2]. It enables audio and video communication to work inside web pages by facilitating applications [2]. It enables audio and video communication to work inside web pages by facilitating
direct peer-to-peer communication, thereby eliminating the need for plugin installations or native direct peer-to-peer communication, and thereby eliminating the need for plugin installations or native
app downloads [3]. Supported by Apple, Google, Microsoft, Mozilla, and Opera, WebRTC's app downloads [3]. Supported by Apple, Google, Microsoft, Mozilla, and Opera, WebRTC's
specifications have been published by the World Wide Web Consortium (W3C) and the Internet specifications have been published by the World Wide Web Consortium (W3C) and the Internet
Engineering Task Force (IETF) [2]. Engineering Task Force (IETF) [2].
@@ -2667,7 +2776,7 @@ actions:
**interaction data** and **technical data** [1]. **interaction data** and **technical data** [1].
**Interaction data** includes metrics such as the number of open tabs and windows, the number of **Interaction data** includes metrics such as the number of open tabs and windows, the number of
webpages visited, the number and type of installed Firefox Add-ons, session length, and webpages visited, the number and type of installed Firefox Add-ons, session length, and
interactions with features offered by Mozilla or third parties. These features may include interactions with features offered by Mozilla or third parties. These features may include
Firefox search features and search partner referrals [1]. Firefox search features and search partner referrals [1].
@@ -2680,14 +2789,14 @@ actions:
[1]: https://web.archive.org/web/20220901045358/https://support.mozilla.org/en-US/kb/telemetry-clientid "Telemetry collection and deletion | Firefox Help | support.mozilla.org" [1]: https://web.archive.org/web/20220901045358/https://support.mozilla.org/en-US/kb/telemetry-clientid "Telemetry collection and deletion | Firefox Help | support.mozilla.org"
children: children:
- -
name: Disable Firefox technical and interaction data collection name: Disable collection of technical and interaction data in Firefox
recommend: standard recommend: standard
docs: |- docs: |-
This script alters the `datareporting.healthreport.uploadEnabled` preference, effectively This script alters the `datareporting.healthreport.uploadEnabled` preference, effectively
disabling it. This preference regulates whether Firefox sends telemetry data [1]. disabling it. This preference regulates whether Firefox sends telemetry data [1].
The script halts all data transmitted from Firefox to Mozilla via Telemetry [2], preventing The script halts all data transmitted from Firefox to Mozilla via Telemetry [2], preventing
Firefox from sending technical and interaction data to Mozilla [3]. Firefox from sending technical and interaction data to Mozilla [3].
For reasons of military security, it's recommended by the U.S. government (NIST 800-53) that For reasons of military security, it's recommended by the U.S. government (NIST 800-53) that
this data collection is disabled [4]. this data collection is disabled [4].
@@ -2702,7 +2811,7 @@ actions:
prefName: datareporting.healthreport.uploadEnabled prefName: datareporting.healthreport.uploadEnabled
jsonValue: 'false' jsonValue: 'false'
- -
name: Disable verbose Firefox telemetry collection name: Disable detailed telemetry collection in Firefox
recommend: standard recommend: standard
docs: |- docs: |-
This script disables the `toolkit.telemetry.enabled` preference, which controls whether the This script disables the `toolkit.telemetry.enabled` preference, which controls whether the
@@ -2730,7 +2839,7 @@ actions:
prefName: toolkit.telemetry.enabled prefName: toolkit.telemetry.enabled
jsonValue: 'false' jsonValue: 'false'
- -
name: Disable Firefox telemetry archive name: Disable archiving of Firefox telemetry
recommend: standard recommend: standard
docs: |- docs: |-
This script configures the `toolkit.telemetry.archive.enabled` preference to be disabled, thus This script configures the `toolkit.telemetry.archive.enabled` preference to be disabled, thus
@@ -2784,12 +2893,12 @@ actions:
docs: |- docs: |-
This script sets the `toolkit.telemetry.log.level` preference to the least verbosity to minimize logs. This script sets the `toolkit.telemetry.log.level` preference to the least verbosity to minimize logs.
This preference adjusts the telemetry logging verbosity as per `Log.jsm` [1]. This preference adjusts the telemetry logging verbosity as per `Log.jsm` [1].
By default, logging is restricted to the console service [1] By default, logging is restricted to the console service [1].
It displays telemetry information in the browser console [2] and can be used to monitor telemetry activity It displays telemetry information in the browser console [2] and can be used to monitor telemetry activity
in the JavaScript console [3]. This process is also referred to as telemetry tracing [4]. in the JavaScript console [3]. This process is also referred to as telemetry tracing [4].
The output can be accessed via the DevTools console [4], navigable through the menu by selecting The output can be accessed via the DevTools console [4], navigable through the menu by selecting
"Tools" => "Developer Tools" => "Error Console" (CTRL+SHIFT+J). "Tools" => "Developer Tools" => "Error Console" (CTRL+SHIFT+J).
The available verbosity levels, in descending order, are `Trace`, `Debug`, `Config`, `Info`, `Warn` (default), The available verbosity levels, in descending order, are `Trace`, `Debug`, `Config`, `Info`, `Warn` (default),
`Error`, and `Fatal` [1]. `Error`, and `Fatal` [1].
@@ -2804,10 +2913,10 @@ actions:
prefName: toolkit.telemetry.log.level prefName: toolkit.telemetry.log.level
jsonValue: 'Fatal' jsonValue: 'Fatal'
- -
name: Disable dumping Firefox Telemetry log messages to stdout name: Disable Firefox telemetry log output
recommend: standard recommend: standard
docs: |- docs: |-
This script deactivates `toolkit.telemetry.log.dump` as much as possible. This script adjusts the `toolkit.telemetry.log.dump` setting to its most restricted value.
This preference controls whether to dump telemetry log messages to `stdout` [1]. This preference controls whether to dump telemetry log messages to `stdout` [1].
[1]: https://web.archive.org/web/20221015102124/https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/internals/preferences.html "Preferences and Defines — Firefox Source Docs documentation | firefox-source-docs.mozilla.org" [1]: https://web.archive.org/web/20221015102124/https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/internals/preferences.html "Preferences and Defines — Firefox Source Docs documentation | firefox-source-docs.mozilla.org"
@@ -2840,7 +2949,7 @@ actions:
[1]: https://web.archive.org/web/20221025163526/https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/concepts/pings.html "Telemetry pings — Firefox Source Docs documentation | firefox-source-docs.mozilla.org" [1]: https://web.archive.org/web/20221025163526/https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/concepts/pings.html "Telemetry pings — Firefox Source Docs documentation | firefox-source-docs.mozilla.org"
children: children:
- -
name: Disable pinging to Firefox telemetry server name: Disable pings to Firefox telemetry server
recommend: standard recommend: standard
docs: |- docs: |-
This script sets `toolkit.telemetry.server` to be empty. This script sets `toolkit.telemetry.server` to be empty.
@@ -2965,7 +3074,7 @@ actions:
name: Disable Firefox Pioneer study monitoring name: Disable Firefox Pioneer study monitoring
recommend: standard recommend: standard
docs: |- docs: |-
This script configures `toolkit.telemetry.pioneer-new-studies-available` to be disabled to opt-out from This script configures `toolkit.telemetry.pioneer-new-studies-available` to be disabled to opt out.
Firefox Pioneer program. Firefox Pioneer program.
This setting disables availability check for Firefox Pioneer studies [1]. This setting disables availability check for Firefox Pioneer studies [1].
@@ -3020,7 +3129,7 @@ actions:
prefName: network.captive-portal-service.enabled prefName: network.captive-portal-service.enabled
jsonValue: 'false' jsonValue: 'false'
- -
category: Disable Firefox Phishing Protection (decreases your security) category: Disable Firefox Phishing Protection (Safe Browsing) (decreases security)
docs: |- docs: |-
Firefox's Safe Browsing feature, now referred to as Phishing Protection, is still internally Firefox's Safe Browsing feature, now referred to as Phishing Protection, is still internally
recognized as "Safe Browsing" [1]. recognized as "Safe Browsing" [1].
@@ -3044,12 +3153,22 @@ actions:
[3]: https://web.archive.org/web/20221025192516/https://www.usnews.com/opinion/articles/2016-06-22/google-is-the-worlds-biggest-censor-and-its-power-must-be-regulated "Google Is the World's Biggest Censor and Its Power Must Be Regulated | usnews.com" [3]: https://web.archive.org/web/20221025192516/https://www.usnews.com/opinion/articles/2016-06-22/google-is-the-worlds-biggest-censor-and-its-power-must-be-regulated "Google Is the World's Biggest Censor and Its Power Must Be Regulated | usnews.com"
children: children:
- -
name: Disable Firefox plugin stability blocking name: Disable blocking of unstable plugins in Firefox
docs: |- docs: |-
This script sets `browser.safebrowsing.blockedURIs.enabled` to 'false', thereby disabling plugin This script sets `browser.safebrowsing.blockedURIs.enabled` to 'false', thereby disabling plugin
stability blocking [1]. stability blocking [1].
Disabling this feature might have mixed implications:
- Disabling the blocking of unstable plugins can potentially expose the user to vulnerabilities associated with these plugins.
These plugins may include malware, which would compromise user privacy and data.
- It removes the need for data communication via Mozilla servers `addons.mozilla.org` for list retrieval [2]. This feature requires
frequent requests to download large amount of data.
If this blocking is removed, the user should be knowledgeable about the potential risks and will take precautions.
[1]: https://web.archive.org/web/20221025192643/https://wiki.mozilla.org/Security/Safe_Browsing#Prefs "Security/Safe Browsing - MozillaWiki | wiki.mozilla.org" [1]: https://web.archive.org/web/20221025192643/https://wiki.mozilla.org/Security/Safe_Browsing#Prefs "Security/Safe Browsing - MozillaWiki | wiki.mozilla.org"
[2]: https://web.archive.org/web/20230811024650/https://blog.mozilla.org/addons/2020/08/24/introducing-a-scalable-add-ons-blocklist/ "Introducing a scalable add-ons blocklist | Mozilla Add-ons Community Blog"
call: call:
function: AddFirefoxPrefs function: AddFirefoxPrefs
parameters: parameters:
@@ -3183,7 +3302,7 @@ functions:
parameters: parameters:
code: |- code: |-
from pathlib import Path from pathlib import Path
import os, json import os, json, sys
property_name = '{{ $setting }}' property_name = '{{ $setting }}'
target = json.loads('{{ $jsonValue }}') target = json.loads('{{ $jsonValue }}')
home_dir = f'/home/{os.getenv("SUDO_USER", os.getenv("USER"))}' home_dir = f'/home/{os.getenv("SUDO_USER", os.getenv("USER"))}'
@@ -3200,7 +3319,15 @@ functions:
continue continue
print(f'Reading file at "{settings_file}".') print(f'Reading file at "{settings_file}".')
file_content = file.read_text() file_content = file.read_text()
json_object = json.loads(file_content) if not file_content.strip():
print('Settings file is empty. Treating it as default empty JSON object.')
file_content = '{}'
json_object = None
try:
json_object = json.loads(file_content)
except json.JSONDecodeError:
print(f'Error, invalid JSON format in the settings file: "{settings_file}".', file=sys.stderr)
continue
if property_name not in json_object: if property_name not in json_object:
print(f'Settings "{property_name}" is not configured.') print(f'Settings "{property_name}" is not configured.')
else: else:
@@ -3215,12 +3342,12 @@ functions:
print(f'Successfully configured "{property_name}" to {json.dumps(target)}.') print(f'Successfully configured "{property_name}" to {json.dumps(target)}.')
revertCode: |- revertCode: |-
from pathlib import Path from pathlib import Path
import os, json import os, json, sys
property_name = '{{ $setting }}' property_name = '{{ $setting }}'
target = json.loads('{{ $jsonValue }}') target = json.loads('{{ $jsonValue }}')
home_dir = f'/home/{os.getenv("SUDO_USER", os.getenv("USER"))}' home_dir = f'/home/{os.getenv("SUDO_USER", os.getenv("USER"))}'
settings_files = [ settings_files = [
# Global installation (also snap that installs with "--classic" flag) # Global installation (also Snap that installs with "--classic" flag)
f'{home_dir}/.config/Code/User/settings.json', f'{home_dir}/.config/Code/User/settings.json',
# Flatpak installation # Flatpak installation
f'{home_dir}/.var/app/com.visualstudio.code/config/Code/User/settings.json' f'{home_dir}/.var/app/com.visualstudio.code/config/Code/User/settings.json'
@@ -3232,7 +3359,14 @@ functions:
continue continue
print(f'Reading file at "{settings_file}".') print(f'Reading file at "{settings_file}".')
file_content = file.read_text() file_content = file.read_text()
json_object = json.loads(file_content) if not file_content.strip():
print(f'Skipping, no need to revert because settings file is empty: "{settings_file}".')
continue
try:
json_object = json.loads(file_content)
except json.JSONDecodeError:
print(f'Error, invalid JSON format in the settings file: "{settings_file}".', file=sys.stderr)
continue
if property_name not in json_object: if property_name not in json_object:
print(f'Skipping, "{property_name}" is not configured.') print(f'Skipping, "{property_name}" is not configured.')
continue continue
@@ -3327,11 +3461,11 @@ functions:
if ! sudo systemctl stop "$service"; then if ! sudo systemctl stop "$service"; then
>&2 echo "Could not stop $service." >&2 echo "Could not stop $service."
else else
echo 'Successfuly stopped' echo 'Successfully stopped'
fi fi
fi fi
if sudo systemctl disable "$service"; then if sudo systemctl disable "$service"; then
echo "Successfuly disabled $service." echo "Successfully disabled $service."
else else
>&2 echo "Failed to disable $service." >&2 echo "Failed to disable $service."
fi fi
@@ -3348,7 +3482,7 @@ functions:
echo "Skipping, $service is already enabled." echo "Skipping, $service is already enabled."
else else
if sudo systemctl enable "$service"; then if sudo systemctl enable "$service"; then
echo "Successfuly enabled $service, it may require reboot to start." echo "Successfully enabled $service, it may require reboot to start."
else else
>&2 echo "Failed to enable $service." >&2 echo "Failed to enable $service."
fi fi
@@ -3675,7 +3809,7 @@ functions:
echo "Succesfully restored." echo "Succesfully restored."
else else
>&2 echo "Failed to restore, backup file could not be found at $backup_file." >&2 echo "Failed to restore, backup file could not be found at $backup_file."
>&2 echo "Was it change initially applied by privacy.sexy?" >&2 echo "Was the change initially applied by privacy.sexy?"
fi fi
- -
name: ReplaceFileContent name: ReplaceFileContent
@@ -3702,5 +3836,5 @@ functions:
else else
sudo rm -fv "$file" sudo rm -fv "$file"
sudo mv "$file.backup" "$file" sudo mv "$file.backup" "$file"
echo "Successfuly restored from backup: $file" echo "Successfully restored from backup: $file"
fi fi

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M464 160c8.8 0 16 7.2 16 16V336c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V176c0-8.8 7.2-16 16-16H464zM80 96C35.8 96 0 131.8 0 176V336c0 44.2 35.8 80 80 80H464c44.2 0 80-35.8 80-80V320c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32V176c0-44.2-35.8-80-80-80H80zm368 96H96V320H448V192z"/></svg>

After

Width:  |  Height:  |  Size: 392 B

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M464 160c8.8 0 16 7.2 16 16V336c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V176c0-8.8 7.2-16 16-16H464zM80 96C35.8 96 0 131.8 0 176V336c0 44.2 35.8 80 80 80H464c44.2 0 80-35.8 80-80V320c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32V176c0-44.2-35.8-80-80-80H80zm208 96H96V320H288V192z"/></svg>

After

Width:  |  Height:  |  Size: 392 B

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>

After

Width:  |  Height:  |  Size: 363 B

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M208 0H332.1c12.7 0 24.9 5.1 33.9 14.1l67.9 67.9c9 9 14.1 21.2 14.1 33.9V336c0 26.5-21.5 48-48 48H208c-26.5 0-48-21.5-48-48V48c0-26.5 21.5-48 48-48zM48 128h80v64H64V448H256V416h64v48c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V176c0-26.5 21.5-48 48-48z"/></svg>

After

Width:  |  Height:  |  Size: 365 B

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M64 0C28.7 0 0 28.7 0 64V352c0 35.3 28.7 64 64 64H240l-10.7 32H160c-17.7 0-32 14.3-32 32s14.3 32 32 32H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H346.7L336 416H512c35.3 0 64-28.7 64-64V64c0-35.3-28.7-64-64-64H64zM512 64V288H64V64H512z"/></svg>

After

Width:  |  Height:  |  Size: 342 B

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M464 256A208 208 0 1 0 48 256a208 208 0 1 0 416 0zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zm177.6 62.1C192.8 334.5 218.8 352 256 352s63.2-17.5 78.4-33.9c9-9.7 24.2-10.4 33.9-1.4s10.4 24.2 1.4 33.9c-22 23.8-60 49.4-113.6 49.4s-91.7-25.5-113.6-49.4c-9-9.7-8.4-24.9 1.4-33.9s24.9-8.4 33.9 1.4zM144.4 208a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zm192-32a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>

After

Width:  |  Height:  |  Size: 495 B

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M64 0C28.7 0 0 28.7 0 64V448c0 35.3 28.7 64 64 64H320c35.3 0 64-28.7 64-64V160H256c-17.7 0-32-14.3-32-32V0H64zM256 0V128H384L256 0zM216 232V334.1l31-31c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-72 72c-9.4 9.4-24.6 9.4-33.9 0l-72-72c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l31 31V232c0-13.3 10.7-24 24-24s24 10.7 24 24z"/></svg>

After

Width:  |  Height:  |  Size: 428 B

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V173.3c0-17-6.7-33.3-18.7-45.3L352 50.7C340 38.7 323.7 32 306.7 32H64zm0 96c0-17.7 14.3-32 32-32H288c17.7 0 32 14.3 32 32v64c0 17.7-14.3 32-32 32H96c-17.7 0-32-14.3-32-32V128zM224 288a64 64 0 1 1 0 128 64 64 0 1 1 0-128z"/></svg>

After

Width:  |  Height:  |  Size: 407 B

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M384 480h48c11.4 0 21.9-6 27.6-15.9l112-192c5.8-9.9 5.8-22.1 .1-32.1S555.5 224 544 224H144c-11.4 0-21.9 6-27.6 15.9L48 357.1V96c0-8.8 7.2-16 16-16H181.5c4.2 0 8.3 1.7 11.3 4.7l26.5 26.5c21 21 49.5 32.8 79.2 32.8H416c8.8 0 16 7.2 16 16v32h48V160c0-35.3-28.7-64-64-64H298.5c-17 0-33.3-6.7-45.3-18.7L226.7 50.7c-12-12-28.3-18.7-45.3-18.7H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H87.7 384z"/></svg>

After

Width:  |  Height:  |  Size: 503 B

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M0 96C0 60.7 28.7 32 64 32H196.1c19.1 0 37.4 7.6 50.9 21.1L289.9 96H448c35.3 0 64 28.7 64 64V416c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V96zM64 80c-8.8 0-16 7.2-16 16V416c0 8.8 7.2 16 16 16H448c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16H286.6c-10.6 0-20.8-4.2-28.3-11.7L213.1 87c-4.5-4.5-10.6-7-17-7H64z"/></svg>

After

Width:  |  Height:  |  Size: 419 B

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M352 256c0 22.2-1.2 43.6-3.3 64H163.3c-2.2-20.4-3.3-41.8-3.3-64s1.2-43.6 3.3-64H348.7c2.2 20.4 3.3 41.8 3.3 64zm28.8-64H503.9c5.3 20.5 8.1 41.9 8.1 64s-2.8 43.5-8.1 64H380.8c2.1-20.6 3.2-42 3.2-64s-1.1-43.4-3.2-64zm112.6-32H376.7c-10-63.9-29.8-117.4-55.3-151.6c78.3 20.7 142 77.5 171.9 151.6zm-149.1 0H167.7c6.1-36.4 15.5-68.6 27-94.7c10.5-23.6 22.2-40.7 33.5-51.5C239.4 3.2 248.7 0 256 0s16.6 3.2 27.8 13.8c11.3 10.8 23 27.9 33.5 51.5c11.6 26 20.9 58.2 27 94.7zm-209 0H18.6C48.6 85.9 112.2 29.1 190.6 8.4C165.1 42.6 145.3 96.1 135.3 160zM8.1 192H131.2c-2.1 20.6-3.2 42-3.2 64s1.1 43.4 3.2 64H8.1C2.8 299.5 0 278.1 0 256s2.8-43.5 8.1-64zM194.7 446.6c-11.6-26-20.9-58.2-27-94.6H344.3c-6.1 36.4-15.5 68.6-27 94.6c-10.5 23.6-22.2 40.7-33.5 51.5C272.6 508.8 263.3 512 256 512s-16.6-3.2-27.8-13.8c-11.3-10.8-23-27.9-33.5-51.5zM135.3 352c10 63.9 29.8 117.4 55.3 151.6C112.2 482.9 48.6 426.1 18.6 352H135.3zm358.1 0c-30 74.1-93.6 130.9-171.9 151.6c25.5-34.2 45.2-87.7 55.3-151.6H493.4z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M504.3 273.6c4.9-4.5 7.7-10.9 7.7-17.6s-2.8-13-7.7-17.6l-112-104c-7-6.5-17.2-8.2-25.9-4.4s-14.4 12.5-14.4 22l0 56-192 0 0-56c0-9.5-5.7-18.2-14.4-22s-18.9-2.1-25.9 4.4l-112 104C2.8 243 0 249.3 0 256s2.8 13 7.7 17.6l112 104c7 6.5 17.2 8.2 25.9 4.4s14.4-12.5 14.4-22l0-56 192 0 0 56c0 9.5 5.7 18.2 14.4 22s18.9 2.1 25.9-4.4l112-104z"/></svg>

After

Width:  |  Height:  |  Size: 440 B

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/></svg>

After

Width:  |  Height:  |  Size: 343 B

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg>

After

Width:  |  Height:  |  Size: 257 B

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M0 80V229.5c0 17 6.7 33.3 18.7 45.3l176 176c25 25 65.5 25 90.5 0L418.7 317.3c25-25 25-65.5 0-90.5l-176-176c-12-12-28.3-18.7-45.3-18.7H48C21.5 32 0 53.5 0 80zm112 32a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>

After

Width:  |  Height:  |  Size: 310 B

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M224 16c-6.7 0-10.8-2.8-15.5-6.1C201.9 5.4 194 0 176 0c-30.5 0-52 43.7-66 89.4C62.7 98.1 32 112.2 32 128c0 14.3 25 27.1 64.6 35.9c-.4 4-.6 8-.6 12.1c0 17 3.3 33.2 9.3 48H45.4C38 224 32 230 32 237.4c0 1.7 .3 3.4 1 5l38.8 96.9C28.2 371.8 0 423.8 0 482.3C0 498.7 13.3 512 29.7 512H418.3c16.4 0 29.7-13.3 29.7-29.7c0-58.5-28.2-110.4-71.7-143L415 242.4c.6-1.6 1-3.3 1-5c0-7.4-6-13.4-13.4-13.4H342.7c6-14.8 9.3-31 9.3-48c0-4.1-.2-8.1-.6-12.1C391 155.1 416 142.3 416 128c0-15.8-30.7-29.9-78-38.6C324 43.7 302.5 0 272 0c-18 0-25.9 5.4-32.5 9.9c-4.8 3.3-8.8 6.1-15.5 6.1zm56 208H267.6c-16.5 0-31.1-10.6-36.3-26.2c-2.3-7-12.2-7-14.5 0c-5.2 15.6-19.9 26.2-36.3 26.2H168c-22.1 0-40-17.9-40-40V169.6c28.2 4.1 61 6.4 96 6.4s67.8-2.3 96-6.4V184c0 22.1-17.9 40-40 40zm-88 96l16 32L176 480 128 288l64 32zm128-32L272 480 240 352l16-32 64-32z"/></svg>

After

Width:  |  Height:  |  Size: 934 B

View File

@@ -0,0 +1 @@
<!-- Source: Font Awesome 6 --><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></svg>

After

Width:  |  Height:  |  Size: 390 B

View File

@@ -7,5 +7,3 @@
@forward "./mixins"; @forward "./mixins";
@forward "./components/card"; @forward "./components/card";
@forward "./third-party-extensions/tooltip.scss";

View File

@@ -1,45 +0,0 @@
// Based on https://github.com/Akryum/v-tooltip/blob/83615e394c96ca491a4df04b892ae87e833beb97/demo-src/src/App.vue#L179-L303
@use "@/presentation/assets/styles/colors" as *;
.tooltip {
display: block !important;
z-index: 10000;
.tooltip-inner {
background: $color-primary-darkest;
color: $color-on-primary;
border-radius: 16px;
padding: 5px 10px 4px;
}
.tooltip-arrow {
width: 0;
height: 0;
border-style: solid;
position: absolute;
margin: 5px;
border-color: $color-primary-darkest;
z-index: 1;
}
&[x-placement^="top"] {
margin-bottom: 5px;
.tooltip-arrow {
border-width: 5px 5px 0 5px;
border-left-color: transparent !important;
border-right-color: transparent !important;
border-bottom-color: transparent !important;
bottom: -5px;
left: calc(50% - 5px);
margin-top: 0;
margin-bottom: 0;
}
}
&[aria-hidden='true'] {
visibility: hidden;
opacity: 0;
transition: opacity .15s, visibility .15s;
}
&[aria-hidden='false'] {
visibility: visible;
opacity: 1;
transition: opacity .15s;
}
}

View File

@@ -1,7 +1,5 @@
import { IconBootstrapper } from './Modules/IconBootstrapper';
import { VueConstructor, IVueBootstrapper } from './IVueBootstrapper'; import { VueConstructor, IVueBootstrapper } from './IVueBootstrapper';
import { VueBootstrapper } from './Modules/VueBootstrapper'; import { VueBootstrapper } from './Modules/VueBootstrapper';
import { TooltipBootstrapper } from './Modules/TooltipBootstrapper';
import { RuntimeSanityValidator } from './Modules/RuntimeSanityValidator'; import { RuntimeSanityValidator } from './Modules/RuntimeSanityValidator';
import { AppInitializationLogger } from './Modules/AppInitializationLogger'; import { AppInitializationLogger } from './Modules/AppInitializationLogger';
@@ -15,9 +13,7 @@ export class ApplicationBootstrapper implements IVueBootstrapper {
private static getAllBootstrappers(): IVueBootstrapper[] { private static getAllBootstrappers(): IVueBootstrapper[] {
return [ return [
new IconBootstrapper(),
new VueBootstrapper(), new VueBootstrapper(),
new TooltipBootstrapper(),
new RuntimeSanityValidator(), new RuntimeSanityValidator(),
new AppInitializationLogger(), new AppInitializationLogger(),
]; ];

View File

@@ -1,38 +0,0 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faGithub } from '@fortawesome/free-brands-svg-icons';
/** BRAND ICONS (PREFIX: fab) */
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
/** REGULAR ICONS (PREFIX: far) */
import { faFolderOpen, faFolder, faSmile } from '@fortawesome/free-regular-svg-icons';
/** SOLID ICONS (PREFIX: fas (default)) */
import {
faTimes, faFileDownload, faCopy, faSearch, faInfoCircle, faUserSecret, faDesktop, faTag, faGlobe,
faSave, faBatteryFull, faBatteryHalf, faPlay, faArrowsAltH,
} from '@fortawesome/free-solid-svg-icons';
import { IVueBootstrapper, VueConstructor } from '../IVueBootstrapper';
export class IconBootstrapper implements IVueBootstrapper {
public bootstrap(vue: VueConstructor): void {
library.add(
faGithub,
faUserSecret,
faSmile,
faDesktop,
faGlobe,
faTag,
faFolderOpen,
faFolder,
faTimes,
faFileDownload,
faSave,
faCopy,
faPlay,
faSearch,
faBatteryFull,
faBatteryHalf,
faInfoCircle,
faArrowsAltH,
);
vue.component('font-awesome-icon', FontAwesomeIcon);
}
}

View File

@@ -1,8 +0,0 @@
import VTooltip from 'v-tooltip';
import { VueConstructor, IVueBootstrapper } from '../IVueBootstrapper';
export class TooltipBootstrapper implements IVueBootstrapper {
public bootstrap(vue: VueConstructor): void {
vue.use(VTooltip);
}
}

View File

@@ -7,11 +7,12 @@
<TheCodeButtons class="app__row app__code-buttons" /> <TheCodeButtons class="app__row app__code-buttons" />
<TheFooter /> <TheFooter />
</div> </div>
<OptionalDevToolkit />
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineAsyncComponent, defineComponent } from 'vue';
import TheHeader from '@/presentation/components/TheHeader.vue'; import TheHeader from '@/presentation/components/TheHeader.vue';
import TheFooter from '@/presentation/components/TheFooter/TheFooter.vue'; import TheFooter from '@/presentation/components/TheFooter/TheFooter.vue';
import TheCodeButtons from '@/presentation/components/Code/CodeButtons/TheCodeButtons.vue'; import TheCodeButtons from '@/presentation/components/Code/CodeButtons/TheCodeButtons.vue';
@@ -22,6 +23,10 @@ import { provideDependencies } from '../bootstrapping/DependencyProvider';
const singletonAppContext = await buildContext(); const singletonAppContext = await buildContext();
const OptionalDevToolkit = process.env.NODE_ENV !== 'production'
? defineAsyncComponent(() => import('@/presentation/components/DevToolkit/DevToolkit.vue'))
: null;
export default defineComponent({ export default defineComponent({
components: { components: {
TheHeader, TheHeader,
@@ -29,6 +34,7 @@ export default defineComponent({
TheScriptArea, TheScriptArea,
TheSearchBar, TheSearchBar,
TheFooter, TheFooter,
OptionalDevToolkit,
}, },
setup() { setup() {
provideDependencies(singletonAppContext); // In Vue 3.0 we can move it to main.ts provideDependencies(singletonAppContext); // In Vue 3.0 we can move it to main.ts
@@ -59,5 +65,4 @@ export default defineComponent({
} }
} }
} }
</style> </style>

View File

@@ -4,30 +4,30 @@
type="button" type="button"
@click="onClicked" @click="onClicked"
> >
<font-awesome-icon <AppIcon
class="button__icon" class="button__icon"
:icon="[iconPrefix, iconName]" :icon="iconName"
size="2x"
/> />
<div class="button__text">{{text}}</div> <div class="button__text">{{text}}</div>
</button> </button>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent, PropType } from 'vue';
import { IconName } from '@/presentation/components/Shared/Icon/IconName';
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
export default defineComponent({ export default defineComponent({
components: {
AppIcon,
},
props: { props: {
text: { text: {
type: String, type: String,
required: true, required: true,
}, },
iconPrefix: {
type: String,
required: true,
},
iconName: { iconName: {
type: String, type: String as PropType<IconName>,
required: true, required: true,
}, },
}, },
@@ -64,6 +64,10 @@ export default defineComponent({
box-shadow: 0 3px 9px $color-primary-darkest; box-shadow: 0 3px 9px $color-primary-darkest;
border-radius: 4px; border-radius: 4px;
&__icon {
font-size: 2em;
}
@include clickable; @include clickable;
width: 10%; width: 10%;

View File

@@ -3,9 +3,9 @@
<span class="dollar">$</span> <span class="dollar">$</span>
<code><slot /></code> <code><slot /></code>
<TooltipWrapper> <TooltipWrapper>
<font-awesome-icon <AppIcon
class="copy-button" class="copy-button"
:icon="['fas', 'copy']" icon="copy"
@click="copyCode" @click="copyCode"
/> />
<template v-slot:tooltip> <template v-slot:tooltip>
@@ -19,10 +19,12 @@
import { defineComponent, useSlots } from 'vue'; import { defineComponent, useSlots } from 'vue';
import { Clipboard } from '@/infrastructure/Clipboard'; import { Clipboard } from '@/infrastructure/Clipboard';
import TooltipWrapper from '@/presentation/components/Shared/TooltipWrapper.vue'; import TooltipWrapper from '@/presentation/components/Shared/TooltipWrapper.vue';
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
export default defineComponent({ export default defineComponent({
components: { components: {
TooltipWrapper, TooltipWrapper,
AppIcon,
}, },
setup() { setup() {
const slots = useSlots(); const slots = useSlots();

View File

@@ -14,7 +14,7 @@
<p> <p>
<strong>2. The hard (manual) alternative</strong>. This requires you to do additional manual <strong>2. The hard (manual) alternative</strong>. This requires you to do additional manual
steps. If you are unsure how to follow the instructions, hover on information steps. If you are unsure how to follow the instructions, hover on information
(<font-awesome-icon :icon="['fas', 'info-circle']" />) (<AppIcon icon="circle-info" />)
icons near the steps, or follow the easy alternative described above. icons near the steps, or follow the easy alternative described above.
</p> </p>
<p> <p>
@@ -27,9 +27,9 @@
<div class="step__action"> <div class="step__action">
<span>{{ step.action.instruction }}</span> <span>{{ step.action.instruction }}</span>
<TooltipWrapper v-if="step.action.details"> <TooltipWrapper v-if="step.action.details">
<font-awesome-icon <AppIcon
class="explanation" class="explanation"
:icon="['fas', 'info-circle']" icon="circle-info"
/> />
<template v-slot:tooltip> <template v-slot:tooltip>
<div v-html="step.action.details" /> <div v-html="step.action.details" />
@@ -39,9 +39,9 @@
<div v-if="step.code" class="step__code"> <div v-if="step.code" class="step__code">
<CodeInstruction>{{ step.code.instruction }}</CodeInstruction> <CodeInstruction>{{ step.code.instruction }}</CodeInstruction>
<TooltipWrapper v-if="step.code.details"> <TooltipWrapper v-if="step.code.details">
<font-awesome-icon <AppIcon
class="explanation" class="explanation"
:icon="['fas', 'info-circle']" icon="circle-info"
/> />
<template v-slot:tooltip> <template v-slot:tooltip>
<div v-html="step.code.details" /> <div v-html="step.code.details" />
@@ -62,6 +62,7 @@ import {
import { InjectionKeys } from '@/presentation/injectionSymbols'; import { InjectionKeys } from '@/presentation/injectionSymbols';
import { OperatingSystem } from '@/domain/OperatingSystem'; import { OperatingSystem } from '@/domain/OperatingSystem';
import TooltipWrapper from '@/presentation/components/Shared/TooltipWrapper.vue'; import TooltipWrapper from '@/presentation/components/Shared/TooltipWrapper.vue';
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
import CodeInstruction from './CodeInstruction.vue'; import CodeInstruction from './CodeInstruction.vue';
import { IInstructionListData } from './InstructionListData'; import { IInstructionListData } from './InstructionListData';
@@ -69,6 +70,7 @@ export default defineComponent({
components: { components: {
CodeInstruction, CodeInstruction,
TooltipWrapper, TooltipWrapper,
AppIcon,
}, },
props: { props: {
data: { data: {

View File

@@ -4,19 +4,16 @@
v-if="canRun" v-if="canRun"
text="Run" text="Run"
v-on:click="executeCode" v-on:click="executeCode"
icon-prefix="fas"
icon-name="play" icon-name="play"
/> />
<IconButton <IconButton
:text="isDesktopVersion ? 'Save' : 'Download'" :text="isDesktopVersion ? 'Save' : 'Download'"
v-on:click="saveCode" v-on:click="saveCode"
icon-prefix="fas" :icon-name="isDesktopVersion ? 'floppy-disk' : 'file-arrow-down'"
:icon-name="isDesktopVersion ? 'save' : 'file-download'"
/> />
<IconButton <IconButton
text="Copy" text="Copy"
v-on:click="copyCode" v-on:click="copyCode"
icon-prefix="fas"
icon-name="copy" icon-name="copy"
/> />
<ModalDialog v-if="instructions" v-model="areInstructionsVisible"> <ModalDialog v-if="instructions" v-model="areInstructionsVisible">

View File

@@ -0,0 +1,71 @@
<template>
<div class="dev-toolkit">
<div class="title">
Tools
</div>
<hr />
<button
v-for="action in devActions"
@click="action.handler"
:key="action.name"
type="button">
{{ action.name }}
</button>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { dumpNames } from './DumpNames';
export default defineComponent({
setup() {
const devActions: readonly DevAction[] = [
{
name: 'Log script/category names',
handler: async () => {
const names = await dumpNames();
console.log(names);
},
},
];
return {
devActions,
};
},
});
interface DevAction {
readonly name: string;
readonly handler: () => void | Promise<void>;
}
</script>
<style scoped lang="scss">
@use "@/presentation/assets/styles/main" as *;
.dev-toolkit {
position: fixed;
top: 0;
right: 0;
background-color: rgba($color-on-surface, 0.5);
color: $color-on-primary;
padding: 10px;
z-index: 10000;
.title {
font-weight: bold;
text-align: center;
}
button {
display: block;
margin-bottom: 10px;
padding: 5px 10px;
background-color: $color-primary;
color: $color-on-primary;
border: none;
cursor: pointer;
}
}
</style>

View File

@@ -0,0 +1,35 @@
import { IApplication } from '@/domain/IApplication';
import { ApplicationFactory } from '@/application/ApplicationFactory';
export async function dumpNames(): Promise<string> {
const application = await ApplicationFactory.Current.getApp();
const names = collectNames(application);
const output = names.join('\n');
return output;
}
function collectNames(application: IApplication): string[] {
const { collections } = application;
const allNames = [
...collections.flatMap((collection) => collection.getAllCategories().map((c) => c.name)),
...collections.flatMap((collection) => collection.getAllScripts().map((c) => c.name)),
];
const uniqueNames = [...new Set(allNames)];
return shuffle(uniqueNames);
}
/*
Shuffle an array of strings, returning a new array with elements in random order.
Uses the Fisher-Yates (or Durstenfeld) algorithm.
*/
function shuffle(array: readonly string[]): string[] {
const shuffledArray = [...array];
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]];
}
return shuffledArray;
}

View File

@@ -4,9 +4,9 @@
:style="{ cursor: cursorCssValue }" :style="{ cursor: cursorCssValue }"
@mousedown="startResize"> @mousedown="startResize">
<div class="line" /> <div class="line" />
<font-awesome-icon <AppIcon
class="icon" class="icon"
:icon="['fas', 'arrows-alt-h']" icon="left-right"
/> />
<div class="line" /> <div class="line" />
</div> </div>
@@ -14,8 +14,12 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, onUnmounted } from 'vue'; import { defineComponent, onUnmounted } from 'vue';
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
export default defineComponent({ export default defineComponent({
components: {
AppIcon,
},
emits: { emits: {
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
resized: (displacementX: number) => true, resized: (displacementX: number) => true,

View File

@@ -17,18 +17,18 @@
</span> </span>
<span v-else>Oh no 😢</span> <span v-else>Oh no 😢</span>
<!-- Expand icon --> <!-- Expand icon -->
<font-awesome-icon <AppIcon
class="card__inner__expand-icon" class="card__inner__expand-icon"
:icon="['far', isExpanded ? 'folder-open' : 'folder']" :icon="isExpanded ? 'folder-open' : 'folder'"
/> />
<!-- Indeterminate and full states --> <!-- Indeterminate and full states -->
<div class="card__inner__state-icons"> <div class="card__inner__state-icons">
<font-awesome-icon <AppIcon
:icon="['fa', 'battery-half']" icon="battery-half"
v-if="isAnyChildSelected && !areAllChildrenSelected" v-if="isAnyChildSelected && !areAllChildrenSelected"
/> />
<font-awesome-icon <AppIcon
:icon="['fa', 'battery-full']" icon="battery-full"
v-if="areAllChildrenSelected" v-if="areAllChildrenSelected"
/> />
</div> </div>
@@ -38,8 +38,8 @@
<ScriptsTree :categoryId="categoryId" /> <ScriptsTree :categoryId="categoryId" />
</div> </div>
<div class="card__expander__close-button"> <div class="card__expander__close-button">
<font-awesome-icon <AppIcon
:icon="['fas', 'times']" icon="xmark"
v-on:click="collapse()" v-on:click="collapse()"
/> />
</div> </div>
@@ -52,6 +52,7 @@ import {
defineComponent, ref, watch, computed, defineComponent, ref, watch, computed,
inject, inject,
} from 'vue'; } from 'vue';
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
import { InjectionKeys } from '@/presentation/injectionSymbols'; import { InjectionKeys } from '@/presentation/injectionSymbols';
import ScriptsTree from '@/presentation/components/Scripts/View/Tree/ScriptsTree.vue'; import ScriptsTree from '@/presentation/components/Scripts/View/Tree/ScriptsTree.vue';
import { sleep } from '@/infrastructure/Threading/AsyncSleep'; import { sleep } from '@/infrastructure/Threading/AsyncSleep';
@@ -59,6 +60,7 @@ import { sleep } from '@/infrastructure/Threading/AsyncSleep';
export default defineComponent({ export default defineComponent({
components: { components: {
ScriptsTree, ScriptsTree,
AppIcon,
}, },
props: { props: {
categoryId: { categoryId: {

View File

@@ -18,7 +18,7 @@
class="search__query__close-button" class="search__query__close-button"
v-on:click="clearSearchQuery()" v-on:click="clearSearchQuery()"
> >
<font-awesome-icon :icon="['fas', 'times']" /> <AppIcon icon="xmark" />
</div> </div>
</div> </div>
<div v-if="!searchHasMatches" class="search-no-matches"> <div v-if="!searchHasMatches" class="search-no-matches">
@@ -41,6 +41,7 @@ import {
defineComponent, PropType, ref, computed, defineComponent, PropType, ref, computed,
inject, inject,
} from 'vue'; } from 'vue';
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
import { InjectionKeys } from '@/presentation/injectionSymbols'; import { InjectionKeys } from '@/presentation/injectionSymbols';
import ScriptsTree from '@/presentation/components/Scripts/View/Tree/ScriptsTree.vue'; import ScriptsTree from '@/presentation/components/Scripts/View/Tree/ScriptsTree.vue';
import CardList from '@/presentation/components/Scripts/View/Cards/CardList.vue'; import CardList from '@/presentation/components/Scripts/View/Cards/CardList.vue';
@@ -52,6 +53,7 @@ export default defineComponent({
components: { components: {
ScriptsTree, ScriptsTree,
CardList, CardList,
AppIcon,
}, },
props: { props: {
currentView: { currentView: {

View File

@@ -100,20 +100,27 @@ $text-size: 0.75em; // Lower looks bad on Firefox
} }
} }
} }
/* @mixin set-paragraph-vertical-gap($paragraph-vertical-gap) {
Different browsers have different <p>, we should even this out. p {
See CSS 2.1 specification https://www.w3.org/TR/CSS21/sample.html. /*
*/ Remove default browser margin on paragraphs to ensure:
p { 1. A markdown text represented as a list (e.g. <ul>, <ol>) has same vertical spacing as a standalone paragraph (</p>).
2. The first paragraph in a sequence (first `<p>` usage) does not introduce top spacing.
3. Uniformity, so margin can be set consistently across browsers.
*/
margin: 0;
}
/* /*
Remove surrounding padding so a markdown text that is a list (e.g. <ul>) Introduce spacing between successive elements and paragraphs.
has same outer padding as a paragraph (</p>). E.g., spacing between two paragraphs (`p`), paragraphs after lists (<ul>, <ol>)...
*/ */
margin: 0; * {
+ p { + p {
margin-top: 1em; margin-top: $paragraph-vertical-gap;
}
} }
} }
@include set-paragraph-vertical-gap($text-size);
ul { ul {
// CSS default is 40px, if the text is a bulletpoint, it leads to unexpected padding. // CSS default is 40px, if the text is a bulletpoint, it leads to unexpected padding.
padding-inline-start: 1em; padding-inline-start: 1em;

View File

@@ -6,14 +6,18 @@
v-on:click.stop v-on:click.stop
v-on:click="toggle()" v-on:click="toggle()"
> >
<font-awesome-icon :icon="['fas', 'info-circle']" /> <AppIcon icon="circle-info" />
</a> </a>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref } from 'vue'; import { defineComponent, ref } from 'vue';
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
export default defineComponent({ export default defineComponent({
components: {
AppIcon,
},
emits: [ emits: [
'show', 'show',
'hide', 'hide',
@@ -52,5 +56,4 @@ export default defineComponent({
color: $color-primary-light; color: $color-primary-light;
} }
} }
</style> </style>

View File

@@ -1,7 +1,8 @@
import { NodeStateChangedEvent } from '../Node/State/StateAccess'; import { TreeNodeStateDescriptor } from '../Node/State/StateDescriptor';
import { ReadOnlyTreeNode } from '../Node/TreeNode'; import { ReadOnlyTreeNode } from '../Node/TreeNode';
export interface TreeNodeStateChangedEmittedEvent { export interface TreeNodeStateChangedEmittedEvent {
readonly change: NodeStateChangedEvent;
readonly node: ReadOnlyTreeNode; readonly node: ReadOnlyTreeNode;
readonly oldState?: TreeNodeStateDescriptor;
readonly newState: TreeNodeStateDescriptor;
} }

View File

@@ -51,7 +51,7 @@ import {
} from 'vue'; } from 'vue';
import { TreeRoot } from '../TreeRoot/TreeRoot'; import { TreeRoot } from '../TreeRoot/TreeRoot';
import { useCurrentTreeNodes } from '../UseCurrentTreeNodes'; import { useCurrentTreeNodes } from '../UseCurrentTreeNodes';
import { NodeRenderingStrategy } from '../Rendering/NodeRenderingStrategy'; import { NodeRenderingStrategy } from '../Rendering/Scheduling/NodeRenderingStrategy';
import { useNodeState } from './UseNodeState'; import { useNodeState } from './UseNodeState';
import { TreeNode } from './TreeNode'; import { TreeNode } from './TreeNode';
import LeafTreeNode from './LeafTreeNode.vue'; import LeafTreeNode from './LeafTreeNode.vue';

View File

@@ -0,0 +1,3 @@
export interface DelayScheduler {
scheduleNext(callback: () => void, delayInMs: number): void;
}

View File

@@ -0,0 +1,35 @@
import { ReadOnlyTreeNode } from '../../Node/TreeNode';
import { RenderQueueOrderer } from './RenderQueueOrderer';
export class CollapsedParentOrderer implements RenderQueueOrderer {
public orderNodes(nodes: Iterable<ReadOnlyTreeNode>): ReadOnlyTreeNode[] {
return orderNodes(nodes);
}
}
function orderNodes(nodes: Iterable<ReadOnlyTreeNode>): ReadOnlyTreeNode[] {
return [...nodes]
.map((node, index) => ({ node, index }))
.sort((a, b) => {
const [
isANodeOfCollapsedParent,
isBNodeOfCollapsedParent,
] = [isParentCollapsed(a.node), isParentCollapsed(b.node)];
if (isANodeOfCollapsedParent !== isBNodeOfCollapsedParent) {
return (isANodeOfCollapsedParent ? 1 : 0) - (isBNodeOfCollapsedParent ? 1 : 0);
}
return a.index - b.index;
})
.map(({ node }) => node);
}
function isParentCollapsed(node: ReadOnlyTreeNode): boolean {
const parentNode = node.hierarchy.parent;
if (parentNode) {
if (!parentNode.state.current.isExpanded) {
return true;
}
return isParentCollapsed(parentNode);
}
return false;
}

View File

@@ -0,0 +1,5 @@
import { ReadOnlyTreeNode } from '../../Node/TreeNode';
export interface RenderQueueOrderer {
orderNodes(nodes: Iterable<ReadOnlyTreeNode>): ReadOnlyTreeNode[];
}

View File

@@ -1,4 +1,4 @@
import { TreeNode } from '../Node/TreeNode'; import { TreeNode } from '../../Node/TreeNode';
export interface NodeRenderingStrategy { export interface NodeRenderingStrategy {
shouldRender(node: TreeNode): boolean; shouldRender(node: TreeNode): boolean;

View File

@@ -0,0 +1,28 @@
import { DelayScheduler } from '../DelayScheduler';
export class TimeoutDelayScheduler implements DelayScheduler {
private timeoutId: ReturnType<typeof setTimeout> | undefined = undefined;
constructor(private readonly timer: TimeFunctions = {
clearTimeout: globalThis.clearTimeout.bind(globalThis),
setTimeout: globalThis.setTimeout.bind(globalThis),
}) { }
public scheduleNext(callback: () => void, delayInMs: number): void {
this.clear();
this.timeoutId = this.timer.setTimeout(callback, delayInMs);
}
private clear(): void {
if (this.timeoutId === undefined) {
return;
}
this.timer.clearTimeout(this.timeoutId);
this.timeoutId = undefined;
}
}
export interface TimeFunctions {
clearTimeout(id: ReturnType<typeof setTimeout>): void;
setTimeout(callback: () => void, delayInMs: number): ReturnType<typeof setTimeout>;
}

View File

@@ -1,44 +1,43 @@
import { import {
WatchSource, computed, shallowRef, triggerRef, watch, WatchSource, shallowRef, triggerRef, watch,
} from 'vue'; } from 'vue';
import { ReadOnlyTreeNode } from '../Node/TreeNode'; import { ReadOnlyTreeNode } from '../Node/TreeNode';
import { useNodeStateChangeAggregator } from '../UseNodeStateChangeAggregator'; import { useNodeStateChangeAggregator } from '../UseNodeStateChangeAggregator';
import { TreeRoot } from '../TreeRoot/TreeRoot'; import { TreeRoot } from '../TreeRoot/TreeRoot';
import { useCurrentTreeNodes } from '../UseCurrentTreeNodes'; import { useCurrentTreeNodes } from '../UseCurrentTreeNodes';
import { NodeRenderingStrategy } from './NodeRenderingStrategy'; import { NodeRenderingStrategy } from './Scheduling/NodeRenderingStrategy';
import { DelayScheduler } from './DelayScheduler';
import { TimeoutDelayScheduler } from './Scheduling/TimeoutDelayScheduler';
import { RenderQueueOrderer } from './Ordering/RenderQueueOrderer';
import { CollapsedParentOrderer } from './Ordering/CollapsedParentOrderer';
/** /**
* Renders tree nodes gradually to prevent UI freeze when loading large amounts of nodes. * Renders tree nodes gradually to prevent UI freeze when loading large amounts of nodes.
*/ */
export function useGradualNodeRendering( export function useGradualNodeRendering(
treeWatcher: WatchSource<TreeRoot>, treeWatcher: WatchSource<TreeRoot>,
useChangeAggregator = useNodeStateChangeAggregator,
useTreeNodes = useCurrentTreeNodes,
scheduler: DelayScheduler = new TimeoutDelayScheduler(),
initialBatchSize = 30,
subsequentBatchSize = 5,
orderer: RenderQueueOrderer = new CollapsedParentOrderer(),
): NodeRenderingStrategy { ): NodeRenderingStrategy {
const nodesToRender = new Set<ReadOnlyTreeNode>(); const nodesToRender = new Set<ReadOnlyTreeNode>();
const nodesBeingRendered = shallowRef(new Set<ReadOnlyTreeNode>()); const nodesBeingRendered = shallowRef(new Set<ReadOnlyTreeNode>());
let isFirstRender = true;
let isRenderingInProgress = false; let isRenderingInProgress = false;
const renderingDelayInMs = 50; const renderingDelayInMs = 50;
const initialBatchSize = 30;
const subsequentBatchSize = 5;
const { onNodeStateChange } = useNodeStateChangeAggregator(treeWatcher); const { onNodeStateChange } = useChangeAggregator(treeWatcher);
const { nodes } = useCurrentTreeNodes(treeWatcher); const { nodes } = useTreeNodes(treeWatcher);
const orderedNodes = computed<readonly ReadOnlyTreeNode[]>(() => nodes.value.flattenedNodes); function updateNodeRenderQueue(node: ReadOnlyTreeNode, isVisible: boolean) {
if (isVisible
watch(() => orderedNodes.value, (newNodes) => {
newNodes.forEach((node) => updateNodeRenderQueue(node));
}, { immediate: true });
function updateNodeRenderQueue(node: ReadOnlyTreeNode) {
if (node.state.current.isVisible
&& !nodesToRender.has(node) && !nodesToRender.has(node)
&& !nodesBeingRendered.value.has(node)) { && !nodesBeingRendered.value.has(node)) {
nodesToRender.add(node); nodesToRender.add(node);
if (!isRenderingInProgress) { beginRendering();
scheduleRendering(); } else if (!isVisible) {
}
} else if (!node.state.current.isVisible) {
if (nodesToRender.has(node)) { if (nodesToRender.has(node)) {
nodesToRender.delete(node); nodesToRender.delete(node);
} }
@@ -49,47 +48,57 @@ export function useGradualNodeRendering(
} }
} }
onNodeStateChange((node, change) => { watch(() => nodes.value, (newNodes) => {
if (change.newState.isVisible === change.oldState.isVisible) { nodesToRender.clear();
nodesBeingRendered.value.clear();
if (!newNodes || newNodes.flattenedNodes.length === 0) {
triggerRef(nodesBeingRendered);
return; return;
} }
updateNodeRenderQueue(node); newNodes
.flattenedNodes
.filter((node) => node.state.current.isVisible)
.forEach((node) => nodesToRender.add(node));
beginRendering();
}, { immediate: true });
onNodeStateChange((change) => {
if (change.newState.isVisible === change.oldState?.isVisible) {
return;
}
updateNodeRenderQueue(change.node, change.newState.isVisible);
}); });
scheduleRendering(); function beginRendering() {
if (isRenderingInProgress) {
function scheduleRendering() { return;
if (isFirstRender) {
renderNodeBatch();
isFirstRender = false;
} else {
const delayScheduler = new DelayScheduler(renderingDelayInMs);
delayScheduler.schedule(renderNodeBatch);
} }
renderNextBatch(initialBatchSize);
} }
function renderNodeBatch() { function renderNextBatch(batchSize: number) {
if (nodesToRender.size === 0) { if (nodesToRender.size === 0) {
isRenderingInProgress = false; isRenderingInProgress = false;
return; return;
} }
isRenderingInProgress = true; isRenderingInProgress = true;
const batchSize = isFirstRender ? initialBatchSize : subsequentBatchSize; const orderedNodes = orderer.orderNodes(nodesToRender);
const sortedNodes = Array.from(nodesToRender).sort( const currentBatch = orderedNodes.slice(0, batchSize);
(a, b) => orderedNodes.value.indexOf(a) - orderedNodes.value.indexOf(b), if (currentBatch.length === 0) {
); return;
const currentBatch = sortedNodes.slice(0, batchSize); }
currentBatch.forEach((node) => { currentBatch.forEach((node) => {
nodesToRender.delete(node); nodesToRender.delete(node);
nodesBeingRendered.value.add(node); nodesBeingRendered.value.add(node);
}); });
triggerRef(nodesBeingRendered); triggerRef(nodesBeingRendered);
if (nodesToRender.size > 0) { scheduler.scheduleNext(
scheduleRendering(); () => renderNextBatch(subsequentBatchSize),
} renderingDelayInMs,
);
} }
function shouldNodeBeRendered(node: ReadOnlyTreeNode) { function shouldNodeBeRendered(node: ReadOnlyTreeNode): boolean {
return nodesBeingRendered.value.has(node); return nodesBeingRendered.value.has(node);
} }
@@ -97,21 +106,3 @@ export function useGradualNodeRendering(
shouldRender: shouldNodeBeRendered, shouldRender: shouldNodeBeRendered,
}; };
} }
class DelayScheduler {
private timeoutId: ReturnType<typeof setTimeout> = null;
constructor(private delay: number) {}
schedule(callback: () => void) {
this.clear();
this.timeoutId = setTimeout(callback, this.delay);
}
clear() {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
}
}

View File

@@ -22,7 +22,7 @@ import {
} from 'vue'; } from 'vue';
import HierarchicalTreeNode from '../Node/HierarchicalTreeNode.vue'; import HierarchicalTreeNode from '../Node/HierarchicalTreeNode.vue';
import { useCurrentTreeNodes } from '../UseCurrentTreeNodes'; import { useCurrentTreeNodes } from '../UseCurrentTreeNodes';
import { NodeRenderingStrategy } from '../Rendering/NodeRenderingStrategy'; import { NodeRenderingStrategy } from '../Rendering/Scheduling/NodeRenderingStrategy';
import { TreeRoot } from './TreeRoot'; import { TreeRoot } from './TreeRoot';
export default defineComponent({ export default defineComponent({

View File

@@ -68,8 +68,13 @@ export default defineComponent({
const nodeRenderingScheduler = useGradualNodeRendering(() => tree); const nodeRenderingScheduler = useGradualNodeRendering(() => tree);
const { onNodeStateChange } = useNodeStateChangeAggregator(() => tree); const { onNodeStateChange } = useNodeStateChangeAggregator(() => tree);
onNodeStateChange((node, change) => {
emit('nodeStateChanged', { node, change }); onNodeStateChange((change) => {
emit('nodeStateChanged', {
node: change.node,
newState: change.newState,
oldState: change.oldState,
});
}); });
onMounted(() => { onMounted(() => {

View File

@@ -6,14 +6,15 @@ import { TreeNodeCheckState } from './Node/State/CheckState';
export function useAutoUpdateChildrenCheckState( export function useAutoUpdateChildrenCheckState(
treeWatcher: WatchSource<TreeRoot>, treeWatcher: WatchSource<TreeRoot>,
useChangeAggregator = useNodeStateChangeAggregator,
) { ) {
const { onNodeStateChange } = useNodeStateChangeAggregator(treeWatcher); const { onNodeStateChange } = useChangeAggregator(treeWatcher);
onNodeStateChange((node, change) => { onNodeStateChange((change) => {
if (change.newState.checkState === change.oldState.checkState) { if (change.newState.checkState === change.oldState?.checkState) {
return; return;
} }
updateChildrenCheckedState(node.hierarchy, change.newState.checkState); updateChildrenCheckedState(change.node.hierarchy, change.newState.checkState);
}); });
} }

View File

@@ -7,14 +7,15 @@ import { ReadOnlyTreeNode } from './Node/TreeNode';
export function useAutoUpdateParentCheckState( export function useAutoUpdateParentCheckState(
treeWatcher: WatchSource<TreeRoot>, treeWatcher: WatchSource<TreeRoot>,
useChangeAggregator = useNodeStateChangeAggregator,
) { ) {
const { onNodeStateChange } = useNodeStateChangeAggregator(treeWatcher); const { onNodeStateChange } = useChangeAggregator(treeWatcher);
onNodeStateChange((node, change) => { onNodeStateChange((change) => {
if (change.newState.checkState === change.oldState.checkState) { if (change.newState.checkState === change.oldState?.checkState) {
return; return;
} }
updateNodeParentCheckedState(node.hierarchy); updateNodeParentCheckedState(change.node.hierarchy);
}); });
} }

Some files were not shown because too many files have changed in this diff Show More