Add userStyles support (wip)

This commit is contained in:
oxmc
2024-12-27 14:01:09 -08:00
parent dcbbfde4a5
commit 77dcea14cc
12 changed files with 832 additions and 47 deletions

View File

@@ -223,8 +223,8 @@ jobs:
- name: Extract checksum from sha256sum.txt and change build version - name: Extract checksum from sha256sum.txt and change build version
run: | run: |
new_checksum=$(awk 'NR==1 { print $1 }' ./dist/linux/sha256sum.txt) new_checksum=$(awk 'NR==1 { print $1 }' ./dist/linux/sha256sum.txt)
sed -i "s|sha256sums=('SKIP' 'SKIP')|sha256sums=('$new_checksum' 'SKIP')|" ./build/PKGBUILD sed -i "s|sha256sums=('SKIP' 'SKIP')|sha256sums=('$new_checksum' 'SKIP')|" ./build/arch-pkg/PKGBUILD
sed -i "s/^pkgver=.*$/pkgver=${{ steps.version.outputs.version }}/" ./build/PKGBUILD sed -i "s/^pkgver=.*$/pkgver=${{ steps.version.outputs.version }}/" ./build/arch-pkg/PKGBUILD
- name: Publish AUR package - name: Publish AUR package
uses: KSXGitHub/github-actions-deploy-aur@v3.0.1 uses: KSXGitHub/github-actions-deploy-aur@v3.0.1

View File

@@ -1,3 +1,44 @@
# Bsky Desktop # Bsky Desktop
Bsky Desktop is an application for Bsky built using Electron. It allows users to manage their Bsky account and feeds from the app instead of the web. Bsky Desktop is an Electron-based application for Bsky that allows users to manage their accounts and feeds directly from the app, rather than through the web interface.
### Features:
- Support for user styles (work in progress; currently only LESS preprocessor is supported)
- Compatibility with both Manifest V2 and V3 Chrome extensions, though only a limited set of Chrome extension APIs are supported. For more information, visit: [Electron Extensions API Documentation](https://www.electronjs.org/docs/latest/api/extensions#supported-extensions-apis)
### Working on:
- Auto updates (for all platforms)
### Build and release status:
[![Build and Release bsky-desktop](https://github.com/oxmc/bsky-desktop/actions/workflows/build-and-release.yml/badge.svg)](https://github.com/oxmc/bsky-desktop/actions/workflows/build-and-release.yml)
[![Packaging status](https://repology.org/badge/vertical-allrepos/bskydesktop.svg?columns=4&exclude_unsupported=1)](https://repology.org/project/bskydesktop/versions)
### Build Instructions for Bsky Desktop
To build and run Bsky Desktop locally, follow these steps:
1. **Clone the repository:**
```sh
git clone https://github.com/oxmc/bsky-desktop.git
cd bsky-desktop
```
2. **Install dependencies:**
```sh
npm install
```
**(Optional) Run the application locally:**
If you want to test the application locally before building it, use the following command:
```sh
npm run start
```
This step is **not required for building** but is useful if you want to see the app in action during development.
3. **Build the application:**
To compile the application, run:
```sh
npm run build
```
This will generate the necessary files for the app.

295
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "bsky-desktop", "name": "bsky-desktop",
"version": "1.0.6", "version": "1.1.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "bsky-desktop", "name": "bsky-desktop",
"version": "1.0.6", "version": "1.1.0",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"dependencies": { "dependencies": {
"@electron/asar": "^3.2.17", "@electron/asar": "^3.2.17",
@@ -14,9 +14,11 @@
"adm-zip": "^0.5.12", "adm-zip": "^0.5.12",
"axios": "^1.6.8", "axios": "^1.6.8",
"electron-window-state": "^5.0.3", "electron-window-state": "^5.0.3",
"less": "^4.2.1",
"log4js": "^6.9.1", "log4js": "^6.9.1",
"node-notifier": "^10.0.0", "node-notifier": "^10.0.0",
"semver": "^7.6.3", "semver": "^7.6.3",
"usercss-meta": "^0.12.0",
"v8-compile-cache": "^2.3.0" "v8-compile-cache": "^2.3.0"
}, },
"devDependencies": { "devDependencies": {
@@ -1324,6 +1326,18 @@
"node": ">=16 || 14 >=14.17" "node": ">=16 || 14 >=14.17"
} }
}, },
"node_modules/copy-anything": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
"integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
"license": "MIT",
"dependencies": {
"is-what": "^3.14.1"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/core-util-is": { "node_modules/core-util-is": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -1711,6 +1725,19 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/errno": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
"license": "MIT",
"optional": true,
"dependencies": {
"prr": "~1.0.1"
},
"bin": {
"errno": "cli.js"
}
},
"node_modules/es-define-property": { "node_modules/es-define-property": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
@@ -2265,7 +2292,7 @@
}, },
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.6.3", "version": "0.6.3",
"dev": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0" "safer-buffer": ">= 2.1.2 < 3.0.0"
@@ -2295,6 +2322,19 @@
], ],
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/image-size": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
"integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
"license": "MIT",
"optional": true,
"bin": {
"image-size": "bin/image-size.js"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inflight": { "node_modules/inflight": {
"version": "1.0.6", "version": "1.0.6",
"license": "ISC", "license": "ISC",
@@ -2343,6 +2383,12 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/is-what": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
"integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
"license": "MIT"
},
"node_modules/is-wsl": { "node_modules/is-wsl": {
"version": "2.2.0", "version": "2.2.0",
"license": "MIT", "license": "MIT",
@@ -2522,6 +2568,45 @@
"safe-buffer": "~5.1.0" "safe-buffer": "~5.1.0"
} }
}, },
"node_modules/less": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/less/-/less-4.2.1.tgz",
"integrity": "sha512-CasaJidTIhWmjcqv0Uj5vccMI7pJgfD9lMkKtlnTHAdJdYK/7l8pM9tumLyJ0zhbD4KJLo/YvTj+xznQd5NBhg==",
"license": "Apache-2.0",
"dependencies": {
"copy-anything": "^2.0.1",
"parse-node-version": "^1.0.1",
"tslib": "^2.3.0"
},
"bin": {
"lessc": "bin/lessc"
},
"engines": {
"node": ">=6"
},
"optionalDependencies": {
"errno": "^0.1.1",
"graceful-fs": "^4.1.2",
"image-size": "~0.5.0",
"make-dir": "^2.1.0",
"mime": "^1.4.1",
"needle": "^3.1.0",
"source-map": "~0.6.0"
}
},
"node_modules/less/node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"license": "MIT",
"optional": true,
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@@ -2604,6 +2689,30 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"license": "MIT",
"optional": true,
"dependencies": {
"pify": "^4.0.1",
"semver": "^5.6.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/make-dir/node_modules/semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"license": "ISC",
"optional": true,
"bin": {
"semver": "bin/semver"
}
},
"node_modules/matcher": { "node_modules/matcher": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
@@ -2723,6 +2832,23 @@
"version": "2.1.2", "version": "2.1.2",
"license": "MIT" "license": "MIT"
}, },
"node_modules/needle": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz",
"integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==",
"license": "MIT",
"optional": true,
"dependencies": {
"iconv-lite": "^0.6.3",
"sax": "^1.2.4"
},
"bin": {
"needle": "bin/needle"
},
"engines": {
"node": ">= 4.4.x"
}
},
"node_modules/node-addon-api": { "node_modules/node-addon-api": {
"version": "1.7.2", "version": "1.7.2",
"dev": true, "dev": true,
@@ -2797,6 +2923,15 @@
"dev": true, "dev": true,
"license": "BlueOak-1.0.0" "license": "BlueOak-1.0.0"
}, },
"node_modules/parse-node-version": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
"integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
"license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/path-is-absolute": { "node_modules/path-is-absolute": {
"version": "1.0.1", "version": "1.0.1",
"license": "MIT", "license": "MIT",
@@ -2842,6 +2977,16 @@
"version": "1.2.0", "version": "1.2.0",
"license": "MIT" "license": "MIT"
}, },
"node_modules/pify": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=6"
}
},
"node_modules/plist": { "node_modules/plist": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz",
@@ -2894,6 +3039,13 @@
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
"license": "MIT",
"optional": true
},
"node_modules/pump": { "node_modules/pump": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
@@ -3063,7 +3215,7 @@
}, },
"node_modules/safer-buffer": { "node_modules/safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"dev": true, "devOptional": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/sanitize-filename": { "node_modules/sanitize-filename": {
@@ -3080,7 +3232,7 @@
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
"dev": true, "devOptional": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/semver": { "node_modules/semver": {
@@ -3189,7 +3341,7 @@
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true, "devOptional": true,
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@@ -3466,6 +3618,12 @@
"utf8-byte-length": "^1.0.1" "utf8-byte-length": "^1.0.1"
} }
}, },
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/type-fest": { "node_modules/type-fest": {
"version": "0.13.1", "version": "0.13.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
@@ -3517,6 +3675,15 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/usercss-meta": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/usercss-meta/-/usercss-meta-0.12.0.tgz",
"integrity": "sha512-zKrXCKdpeIwtVe87omxGo9URf+7mbozduMZEg79dmT4KB3XJwfIkEi/Uk0PcTwR/nZLtAK1+k7isgbGB/g6E7Q==",
"license": "MIT",
"engines": {
"node": ">=8.3"
}
},
"node_modules/utf8-byte-length": { "node_modules/utf8-byte-length": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
@@ -4647,6 +4814,14 @@
} }
} }
}, },
"copy-anything": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
"integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
"requires": {
"is-what": "^3.14.1"
}
},
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -4914,6 +5089,15 @@
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
"dev": true "dev": true
}, },
"errno": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
"optional": true,
"requires": {
"prr": "~1.0.1"
}
},
"es-define-property": { "es-define-property": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
@@ -5289,7 +5473,7 @@
}, },
"iconv-lite": { "iconv-lite": {
"version": "0.6.3", "version": "0.6.3",
"dev": true, "devOptional": true,
"requires": { "requires": {
"safer-buffer": ">= 2.1.2 < 3.0.0" "safer-buffer": ">= 2.1.2 < 3.0.0"
} }
@@ -5300,6 +5484,12 @@
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"dev": true "dev": true
}, },
"image-size": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
"integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
"optional": true
},
"inflight": { "inflight": {
"version": "1.0.6", "version": "1.0.6",
"requires": { "requires": {
@@ -5328,6 +5518,11 @@
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true "dev": true
}, },
"is-what": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
"integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA=="
},
"is-wsl": { "is-wsl": {
"version": "2.2.0", "version": "2.2.0",
"requires": { "requires": {
@@ -5462,6 +5657,31 @@
} }
} }
}, },
"less": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/less/-/less-4.2.1.tgz",
"integrity": "sha512-CasaJidTIhWmjcqv0Uj5vccMI7pJgfD9lMkKtlnTHAdJdYK/7l8pM9tumLyJ0zhbD4KJLo/YvTj+xznQd5NBhg==",
"requires": {
"copy-anything": "^2.0.1",
"errno": "^0.1.1",
"graceful-fs": "^4.1.2",
"image-size": "~0.5.0",
"make-dir": "^2.1.0",
"mime": "^1.4.1",
"needle": "^3.1.0",
"parse-node-version": "^1.0.1",
"source-map": "~0.6.0",
"tslib": "^2.3.0"
},
"dependencies": {
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"optional": true
}
}
},
"lodash": { "lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@@ -5527,6 +5747,24 @@
"yallist": "^4.0.0" "yallist": "^4.0.0"
} }
}, },
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"optional": true,
"requires": {
"pify": "^4.0.1",
"semver": "^5.6.0"
},
"dependencies": {
"semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"optional": true
}
}
},
"matcher": { "matcher": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
@@ -5605,6 +5843,16 @@
"ms": { "ms": {
"version": "2.1.2" "version": "2.1.2"
}, },
"needle": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz",
"integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==",
"optional": true,
"requires": {
"iconv-lite": "^0.6.3",
"sax": "^1.2.4"
}
},
"node-addon-api": { "node-addon-api": {
"version": "1.7.2", "version": "1.7.2",
"dev": true, "dev": true,
@@ -5656,6 +5904,11 @@
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
"dev": true "dev": true
}, },
"parse-node-version": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
"integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA=="
},
"path-is-absolute": { "path-is-absolute": {
"version": "1.0.1" "version": "1.0.1"
}, },
@@ -5686,6 +5939,12 @@
"pend": { "pend": {
"version": "1.2.0" "version": "1.2.0"
}, },
"pify": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"optional": true
},
"plist": { "plist": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz",
@@ -5724,6 +5983,12 @@
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
}, },
"prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
"optional": true
},
"pump": { "pump": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
@@ -5851,7 +6116,7 @@
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"dev": true "devOptional": true
}, },
"sanitize-filename": { "sanitize-filename": {
"version": "1.6.3", "version": "1.6.3",
@@ -5866,7 +6131,7 @@
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
"dev": true "devOptional": true
}, },
"semver": { "semver": {
"version": "7.6.3", "version": "7.6.3",
@@ -5938,7 +6203,7 @@
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true "devOptional": true
}, },
"source-map-support": { "source-map-support": {
"version": "0.5.21", "version": "0.5.21",
@@ -6135,6 +6400,11 @@
"utf8-byte-length": "^1.0.1" "utf8-byte-length": "^1.0.1"
} }
}, },
"tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
},
"type-fest": { "type-fest": {
"version": "0.13.1", "version": "0.13.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
@@ -6167,6 +6437,11 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"usercss-meta": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/usercss-meta/-/usercss-meta-0.12.0.tgz",
"integrity": "sha512-zKrXCKdpeIwtVe87omxGo9URf+7mbozduMZEg79dmT4KB3XJwfIkEi/Uk0PcTwR/nZLtAK1+k7isgbGB/g6E7Q=="
},
"utf8-byte-length": { "utf8-byte-length": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",

View File

@@ -1,6 +1,6 @@
{ {
"name": "bsky-desktop", "name": "bsky-desktop",
"version": "1.0.9", "version": "1.1.0",
"description": "A desktop app of bsky.app", "description": "A desktop app of bsky.app",
"main": "src/app/main.js", "main": "src/app/main.js",
"scripts": { "scripts": {
@@ -14,6 +14,20 @@
"name": "oxmc", "name": "oxmc",
"email": "oxmc7769.mail@gmail.com" "email": "oxmc7769.mail@gmail.com"
}, },
"contributors": [
{
"name": "oxmc",
"email": "contact@oxmc.is-a.dev"
},
{
"name": "PlOszukiwaczDEV",
"email": "ploszukiwacz1@duck.com"
},
{
"name": "GizzyUwU",
"email": "me@gizzy.pro"
}
],
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"devDependencies": { "devDependencies": {
"electron": "^29.4.6", "electron": "^29.4.6",
@@ -25,9 +39,11 @@
"adm-zip": "^0.5.12", "adm-zip": "^0.5.12",
"axios": "^1.6.8", "axios": "^1.6.8",
"electron-window-state": "^5.0.3", "electron-window-state": "^5.0.3",
"less": "^4.2.1",
"log4js": "^6.9.1", "log4js": "^6.9.1",
"node-notifier": "^10.0.0", "node-notifier": "^10.0.0",
"semver": "^7.6.3", "semver": "^7.6.3",
"usercss-meta": "^0.12.0",
"v8-compile-cache": "^2.3.0" "v8-compile-cache": "^2.3.0"
}, },
"build": { "build": {
@@ -42,9 +58,19 @@
"dmg", "dmg",
"pkg" "pkg"
], ],
"icon": "src/ui/images/logo.icns", "icon": "build/icons/mac-icon.icns",
"category": "Network" "category": "Network"
}, },
"pkg": {
"scripts": "build/mac-pkg/scripts",
"installLocation": "/Applications",
"allowAnywhere": true,
"allowCurrentUserHome": true,
"allowRootDirectory": true,
"isVersionChecked": true,
"isRelocatable": false,
"overwriteAction": "upgrade"
},
"linux": { "linux": {
"target": [ "target": [
"appimage" "appimage"

View File

@@ -8,6 +8,7 @@ const badge = require('./badge');
const contextMenu = require('./context-menu'); const contextMenu = require('./context-menu');
const autoUpdater = require('./utils/auto-update'); const autoUpdater = require('./utils/auto-update');
const loadCRX = require('./utils/loadCRX'); const loadCRX = require('./utils/loadCRX');
const userStyles = require('./utils/userStyles');
const log4js = require("log4js"); const log4js = require("log4js");
const path = require("path"); const path = require("path");
const fs = require("fs"); const fs = require("fs");
@@ -37,6 +38,7 @@ global.paths = {
}; };
global.paths.updateDir = path.join(global.paths.data, 'update'); global.paths.updateDir = path.join(global.paths.data, 'update');
global.paths.extensions = path.join(global.paths.data, 'extensions'); global.paths.extensions = path.join(global.paths.data, 'extensions');
global.paths.userstyles = path.join(global.paths.data, 'userstyles');
// URLs: // URLs:
global.urls = { global.urls = {
@@ -126,6 +128,12 @@ if (!fs.existsSync(global.paths.extensions)) {
fs.mkdirSync(global.paths.extensions, { recursive: true }); fs.mkdirSync(global.paths.extensions, { recursive: true });
}; };
// Create userstyles directory if it does not exist:
if (!fs.existsSync(global.paths.userstyles)) {
logger.info("Creating Userstyles Directory");
fs.mkdirSync(global.paths.userstyles, { recursive: true });
};
// improve performance on linux? // improve performance on linux?
if (process.platform !== "win32" && process.platform !== "darwin") { if (process.platform !== "win32" && process.platform !== "darwin") {
logger.log("Disabling Hardware Acceleration and Transparent Visuals"); logger.log("Disabling Hardware Acceleration and Transparent Visuals");
@@ -247,34 +255,6 @@ function createWindow() {
// Badge count: (use mainWindow as that shows the badge on the taskbar) // Badge count: (use mainWindow as that shows the badge on the taskbar)
new badge(mainWindow, badgeOptions); new badge(mainWindow, badgeOptions);
// Load extensions (.crx files):
logger.log("Checking for extensions");
const extensions = fs.readdirSync(global.paths.extensions).filter((file) => file.endsWith('.crx'));
if (extensions.length > 0) {
logger.log(`Unpacking ${extensions.length} extensions and loading them`);
extensions.forEach((extension) => {
loadCRX(path.join(global.paths.extensions, extension));
});
} else {
// Check for unpacked extensions:
const unpackedExtensions = fs.readdirSync(global.paths.extensions).filter((file) => fs.lstatSync(path.join(global.paths.extensions, file)).isDirectory());
// Check if the directory contains a manifest.json file
unpackedExtensions.forEach((extension) => {
const manifestPath = path.join(global.paths.extensions, extension, 'manifest.json');
if (fs.existsSync(manifestPath)) {
logger.log(`Loading unpacked extension: ${extension}`);
session.defaultSession.loadExtension(path.join(global.paths.extensions, extension)).then(({ id }) => {
logger.log(`Extension loaded with ID: ${id}`);
}).catch((error) => {
logger.error(`Failed to load extension: ${error}`);
});
} else {
logger.warn(`Skipping directory ${extension} as it does not contain a manifest.json file`);
};
});
};
logger.log("Main Window Created, Showing splashscreen"); logger.log("Main Window Created, Showing splashscreen");
splash.show(); splash.show();
//mainWindow.show(); //mainWindow.show();
@@ -302,7 +282,7 @@ function createWindow() {
}; };
}); });
PageView.webContents.setWindowOpenHandler(({ url }) => { PageView.webContents.setWindowOpenHandler(({ url }) => {
new BrowserWindow({ show: true, autoHideMenuBar: true }).loadURL(url); new BrowserWindow({ show: true, autoHideMenuBar: true, icon: path.join(global.paths.app, 'ui', 'images', 'logo.png') }).loadURL(url);
return { action: 'deny' }; return { action: 'deny' };
}); });
// Log PageView navigations: // Log PageView navigations:
@@ -322,6 +302,7 @@ function showAboutWindow() {
//open_devtools: process.env.NODE_ENV !== 'production', //open_devtools: process.env.NODE_ENV !== 'production',
use_version_info: [ use_version_info: [
['Application Version', `${global.appInfo.version}`], ['Application Version', `${global.appInfo.version}`],
['Contributors', packageJson.contributors.map((contributor) => contributor.name).join(', ')],
], ],
license: `MIT, GPL-2.0, GPL-3.0, ${global.appInfo.license}`, license: `MIT, GPL-2.0, GPL-3.0, ${global.appInfo.license}`,
}); });
@@ -405,7 +386,7 @@ function handleDeeplink(commandLine) {
break; break;
case "notiftest": case "notiftest":
global.PageView.webContents.send('ui:notif', { title: 'Updater', message: 'Update downloaded', options: { position: 'topRight', timeout: 5000, layout: 2, color: 'blue' } }); global.PageView.webContents.send('ui:notif', { title: 'Updater', message: 'Update downloaded', options: { izitoast: { position: 'topRight', timeout: 5000, layout: 2, color: 'blue' } } });
break; break;
default: default:
@@ -527,6 +508,70 @@ app.whenReady().then(() => {
createWindow(); createWindow();
createTray(); createTray();
// Load extensions (.crx files):
logger.log("Checking for extensions");
const extensions = fs.readdirSync(global.paths.extensions).filter((file) => file.endsWith('.crx'));
if (extensions.length > 0) {
logger.log(`Unpacking ${extensions.length} extensions and loading them`);
extensions.forEach((extension) => {
loadCRX(path.join(global.paths.extensions, extension));
});
} else {
// Check for unpacked extensions:
const unpackedExtensions = fs.readdirSync(global.paths.extensions).filter((file) => fs.lstatSync(path.join(global.paths.extensions, file)).isDirectory());
// Check if the directory contains a manifest.json file
unpackedExtensions.forEach((extension) => {
const manifestPath = path.join(global.paths.extensions, extension, 'manifest.json');
if (fs.existsSync(manifestPath)) {
logger.log(`Loading unpacked extension: ${extension}`);
session.defaultSession.loadExtension(path.join(global.paths.extensions, extension)).then(({ id }) => {
logger.log(`Extension loaded with ID: ${id}`);
}).catch((error) => {
logger.error(`Failed to load extension: ${error}`);
});
} else {
logger.warn(`Skipping directory ${extension} as it does not contain a manifest.json file`);
};
});
};
// Load userstyles
logger.log("Checking for userstyles");
const userStylesDir = path.join(global.paths.userstyles);
if (fs.existsSync(userStylesDir)) {
const files = fs.readdirSync(userStylesDir);
if (files.length > 0) {
const userStylePromises = files.map(async file => {
const cssFile = path.join(userStylesDir, file);
// Parse the CSS file
try {
const cssContent = fs.readFileSync(cssFile, 'utf-8');
const result = await userStyles.parseCSS(cssContent);
logger.info(`Loaded userstyle: ${result.metadata.name}`);
// Compile the userstyle
const compiled = await userStyles.compileStyle(result.css, result.metadata);
// Check if the site 'bsky.app' is defined
if (compiled.sites && compiled.sites['bsky.app']) {
// Apply the userstyle to the PageView
await PageView.webContents.insertCSS(compiled.sites['bsky.app']);
logger.info(`Applied userstyle: ${result.metadata.name}`);
} else {
logger.warn(`Userstyle ${result.metadata.name} does not target 'bsky.app'`);
}
} catch (error) {
logger.error(`Error loading userstyle: ${file}`, error);
}
});
Promise.all(userStylePromises);
}
}
} else { } else {
logger.log("Failed to get singleInstanceLock, Quitting"); logger.log("Failed to get singleInstanceLock, Quitting");
app.quit(); app.quit();

210
src/app/utils/userStyles.js Normal file
View File

@@ -0,0 +1,210 @@
const usercssMeta = require('usercss-meta');
const less = require('less');
/**
* Extracts all global variable and mixin definitions from CSS.
* @param {string} css - The CSS string.
* @returns {string} The extracted global definitions.
*/
function extractGlobalDefinitions(css) {
const globalDefinitionRegex = /(@[\w-]+\s*(?:{[^}]*}|;))/g;
return (css.match(globalDefinitionRegex) || []).join('\n');
}
/**
* Extracts variable definitions from metadata.
* @param {object} metadata - Metadata containing variable definitions.
* @returns {object} A map of variable names to their default values.
*/
function extractMetadataVars(metadata) {
if (!metadata?.vars) return {};
return Object.fromEntries(
Object.entries(metadata.vars).map(([key, value]) => [key, value.default || value.value || null])
);
}
/**
* Extracts content enclosed within matching braces starting from a given position.
* @param {string} css - The CSS string.
* @param {number} startPos - The starting position to search for braces.
* @returns {object|null} The content and ending position of the matched braces.
*/
function extractBracedContent(css, startPos) {
const braceMatch = matchBraces(css, startPos);
if (!braceMatch) return null;
return {
content: css.substring(braceMatch.start + 1, braceMatch.end - 1).trim(),
end: braceMatch.end,
};
}
/**
* Matches a pair of braces in a string starting from a given position.
* @param {string} content - The string content.
* @param {number} start - The starting position to search for braces.
* @returns {object|null} The start and end positions of the matched braces.
*/
function matchBraces(content, start) {
const openBrace = content.indexOf('{', start);
if (openBrace === -1) return null;
let braceCount = 1, pos = openBrace + 1;
while (braceCount > 0 && pos < content.length) {
if (content[pos] === '{') braceCount++;
if (content[pos] === '}') braceCount--;
pos++;
}
return braceCount === 0 ? { start: openBrace, end: pos } : null;
}
/**
* Parses domain rules from the provided CSS string.
* @param {string} css - The CSS string.
* @param {number} startPos - The starting position to search for domain rules.
* @returns {object|null} The domains and the rule start position.
*/
function parseDomainRule(css, startPos) {
const domainRuleRegex = /@-moz-document\s+domain\(\s*'([^']+)'(?:\s*,\s*'([^']+)')*\s*\)/g;
domainRuleRegex.lastIndex = startPos;
const match = domainRuleRegex.exec(css);
if (!match) return null;
const domains = match[0]
.match(/'[^']+'/g) // Extract all single-quoted domain values
.map(domain => domain.replace(/'/g, '').trim());
return {
domains,
ruleStart: match.index + match[0].length - 1,
};
}
/**
* Parses @-moz-document rules and extracts domain-specific CSS.
* @param {string} css - The CSS string.
* @returns {object} A map of domains to their associated CSS.
*/
function parseMozRules(css) {
const rules = {};
let currentPos = 0;
const globalCode = extractGlobalDefinitions(css);
while (true) {
const domainRule = parseDomainRule(css, currentPos);
if (!domainRule) break;
const bracedContent = extractBracedContent(css, domainRule.ruleStart);
if (!bracedContent) break;
// Add the CSS to all matched domains
for (const domain of domainRule.domains) {
rules[domain] = `${globalCode}\n${bracedContent.content}`;
}
currentPos = bracedContent.end;
}
return rules;
}
/**
* Parses metadata from the provided CSS string.
* @param {string} css - The CSS string.
* @returns {object} Parsed metadata.
*/
function parseCSS(css) {
try {
const normalizedCss = css.replace(/\r\n?/g, '\n');
const nocommentsCss = normalizedCss.replace(/\/\*[\s\S]*?\*\//g, ''); // Remove comments
return {
...usercssMeta.parse(normalizedCss),
css: nocommentsCss,
};
} catch (error) {
throw new Error(`Failed to parse CSS metadata: ${error.message}`);
}
}
/**
* Compiles CSS code with a preprocessor if specified in the metadata.
* @param {string} code - The CSS code.
* @param {object} metadata - Metadata containing preprocessor information.
* @param {object} [userVars={}] - User-defined variables to override defaults.
* @returns {Promise<{compiledCss: string, sites: object}>} The compiled CSS code and domain-specific mapping.
*/
async function compileStyle(code, metadata, userVars = {}) {
try {
// Extract and merge variables
const vars = {
...extractMetadataVars(metadata),
...userVars
};
// Generate full code with user variables
const fullCode = [
'// User variables',
Object.entries(vars).map(([key, value]) => `@${key}: ${value};`).join('\n'),
'// Main code',
code
].join('\n\n');
let compiledCode;
switch (metadata?.preprocessor?.toLowerCase()) {
case 'less':
compiledCode = await compileLess(fullCode);
break;
default:
compiledCode = code; // Return unmodified for unknown preprocessors
}
// Parse domain rules
const domainRules = parseMozRules(compiledCode);
// Compile each domain's CSS if needed
const compiledRules = {};
for (const [domain, css] of Object.entries(domainRules)) {
if (metadata.preprocessor === 'less') {
compiledRules[domain] = await compileLess(css, vars);
} else {
compiledRules[domain] = css;
}
}
// Combine all CSS
const combinedCss = Object.entries(compiledRules)
.map(([domain, compiledCss]) => `/* ${domain} */\n${compiledCss}`)
.join('\n\n');
return {
compiledCss: combinedCss,
sites: compiledRules // Map of domains to their CSS
};
} catch (error) {
console.error('Style compilation error:', error);
throw error;
}
}
/**
* Compiles LESS code to CSS.
* @param {string} code - The LESS code.
* @returns {Promise<string>} The compiled CSS.
*/
async function compileLess(code) {
const { css } = await less.render(code, {
math: 'parens-division',
javascriptEnabled: true,
compress: false
});
return css;
}
module.exports = {
parseCSS,
compileStyle
};

File diff suppressed because one or more lines are too long

11
src/ui/lib/css/stylelint-bundle.min.js vendored Normal file

File diff suppressed because one or more lines are too long

63
src/ui/lib/css/stylus-renderer.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -47,10 +47,14 @@ document.addEventListener('DOMContentLoaded', () => {
// Load jQuery first // Load jQuery first
loadScript('ui:///lib/jquery-3.3.1.min.js', () => { loadScript('ui:///lib/jquery-3.3.1.min.js', () => {
// Load iziToast after jQuery // Load confetti and iziToast after jQuery (ui libs)
loadScript('ui:///lib/confetti-1.9.3-browser.min.js');
loadScript('ui:///lib/izitoast.min.js', () => { loadScript('ui:///lib/izitoast.min.js', () => {
// Load app specific scripts
loadScript('ui:///rend/register-handles.js'); loadScript('ui:///rend/register-handles.js');
// Load custom implementations
loadScript('ui:///rend/bsky-ext.js'); loadScript('ui:///rend/bsky-ext.js');
loadScript('ui:///rend/specialAnimations.js');
}); });
}); });
}); });

View File

@@ -20,7 +20,7 @@ document.addEventListener('DOMContentLoaded', () => {
message: event.message, message: event.message,
position: 'topRight', position: 'topRight',
timeout: 5000, timeout: 5000,
...event.options, ...event.options.izitoast,
}); });
} catch (error) { } catch (error) {
console.error('Failed to display notification:', error); console.error('Failed to display notification:', error);

View File

@@ -0,0 +1,109 @@
const anims = {
imgs: {
// tree shape from https://thenounproject.com/icon/pine-tree-1471679/
tree: confetti.shapeFromPath({
path: 'M120 240c-41,14 -91,18 -120,1 29,-10 57,-22 81,-40 -18,2 -37,3 -55,-3 25,-14 48,-30 66,-51 -11,5 -26,8 -45,7 20,-14 40,-30 57,-49 -13,1 -26,2 -38,-1 18,-11 35,-25 51,-43 -13,3 -24,5 -35,6 21,-19 40,-41 53,-67 14,26 32,48 54,67 -11,-1 -23,-3 -35,-6 15,18 32,32 51,43 -13,3 -26,2 -38,1 17,19 36,35 56,49 -19,1 -33,-2 -45,-7 19,21 42,37 67,51 -19,6 -37,5 -56,3 25,18 53,30 82,40 -30,17 -79,13 -120,-1l0 41 -31 0 0 -41z',
matrix: [0.03597122302158273, 0, 0, 0.03597122302158273, -4.856115107913669, -5.071942446043165]
}),
// pumpkin shape from https://thenounproject.com/icon/pumpkin-5253388/
pumpkin: confetti.shapeFromPath({
path: 'M449.4 142c-5 0-10 .3-15 1a183 183 0 0 0-66.9-19.1V87.5a17.5 17.5 0 1 0-35 0v36.4a183 183 0 0 0-67 19c-4.9-.6-9.9-1-14.8-1C170.3 142 105 219.6 105 315s65.3 173 145.7 173c5 0 10-.3 14.8-1a184.7 184.7 0 0 0 169 0c4.9.7 9.9 1 14.9 1 80.3 0 145.6-77.6 145.6-173s-65.3-173-145.7-173zm-220 138 27.4-40.4a11.6 11.6 0 0 1 16.4-2.7l54.7 40.3a11.3 11.3 0 0 1-7 20.3H239a11.3 11.3 0 0 1-9.6-17.5zM444 383.8l-43.7 17.5a17.7 17.7 0 0 1-13 0l-37.3-15-37.2 15a17.8 17.8 0 0 1-13 0L256 383.8a17.5 17.5 0 0 1 13-32.6l37.3 15 37.2-15c4.2-1.6 8.8-1.6 13 0l37.3 15 37.2-15a17.5 17.5 0 0 1 13 32.6zm17-86.3h-82a11.3 11.3 0 0 1-6.9-20.4l54.7-40.3a11.6 11.6 0 0 1 16.4 2.8l27.4 40.4a11.3 11.3 0 0 1-9.6 17.5z',
matrix: [0.020491803278688523, 0, 0, 0.020491803278688523, -7.172131147540983, -5.9016393442622945]
}),
// heart shape from https://thenounproject.com/icon/heart-1545381/
heart: confetti.shapeFromPath({
path: 'M167 72c19,-38 37,-56 75,-56 42,0 76,33 76,75 0,76 -76,151 -151,227 -76,-76 -151,-151 -151,-227 0,-42 33,-75 75,-75 38,0 57,18 76,56z',
matrix: [0.03333333333333333, 0, 0, 0.03333333333333333, -5.566666666666666, -5.533333333333333]
})
},
// Function to animate the snow effect
snow: async function () {
var duration = 15 * 1000;
var animationEnd = Date.now() + duration;
var skew = 1;
function randomInRange(min, max) {
return Math.random() * (max - min) + min;
}
// Function to animate the first snow particle
async function snowAnimation() {
return new Promise(resolve => {
var firstSnowDuration = 3000; // 3 seconds
var firstAnimationEnd = Date.now() + firstSnowDuration;
(function firstSnowFrame() {
var timeLeft = firstAnimationEnd - Date.now();
confetti({
particleCount: 1,
startVelocity: 0,
ticks: Math.max(200, 500 * (timeLeft / firstSnowDuration)),
origin: {
x: Math.random(),
y: randomInRange(0.2, 0.4) * skew - 0.4
},
colors: ['#ffffff'],
shapes: ['circle'],
gravity: randomInRange(0.4, 0.6),
scalar: randomInRange(0.4, 1),
drift: randomInRange(-0.4, 0.4)
});
if (timeLeft > 0) {
requestAnimationFrame(firstSnowFrame);
} else {
resolve(); // Resolve the promise when the animation is complete
}
})();
});
}
// Function to animate the continuous snow particles
function mainSnowAnimation() {
(function frame() {
var timeLeft = animationEnd - Date.now();
var ticks = Math.max(200, 500 * (timeLeft / duration));
skew = Math.max(0.8, skew - 0.001);
confetti({
particleCount: 1,
startVelocity: 0,
ticks: ticks,
origin: {
x: Math.random(),
y: randomInRange(0.2, 0.4) * skew - 0.4
},
colors: ['#ffffff'],
shapes: ['circle'],
gravity: randomInRange(0.4, 0.6),
scalar: randomInRange(0.4, 1),
drift: randomInRange(-0.4, 0.4)
});
confetti({
particleCount: 1,
startVelocity: 0,
ticks: ticks,
origin: {
x: Math.random(),
y: randomInRange(0.2, 0.4) * skew - 0.2
},
colors: ['#ffffff'],
shapes: ['circle'],
gravity: randomInRange(0.4, 0.6),
scalar: randomInRange(0.4, 1),
drift: randomInRange(-0.4, 0.4)
});
if (timeLeft > 0) {
requestAnimationFrame(frame);
}
})();
}
// Start the animations
await snowAnimation(); // Wait for the first snow animation to complete
mainSnowAnimation(); // Start the continuous snow animation
}
};