Merge pull request #14 from FaFre/gecko

switch2gecko
This commit is contained in:
Fabian Freund
2025-06-30 17:14:56 +02:00
committed by GitHub
852 changed files with 83474 additions and 21092 deletions
+65 -21
View File
@@ -1,4 +1,4 @@
name: Flutter CICD # action name
name: Flutter CICD
on:
push:
@@ -6,39 +6,83 @@ on:
- "v*"
jobs:
build: # job's name
runs-on: ubuntu-latest # container os
env: # ADD environment variables
build:
runs-on: ubuntu-latest
env:
KEY_JKS: ${{ secrets.KEY_JKS }}
KEY_PATH: ${{ github.workspace }}/key.jks
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
defaults:
run:
working-directory: app
permissions:
contents: write
steps:
- uses: actions/checkout@v2 # cd to current dir
- uses: actions/setup-java@v2
- name: Free Disk Space
uses: jlumbroso/free-disk-space@main
with:
distribution: 'adopt' # See 'Supported distributions' for available options
tool-cache: false
android: false
dotnet: true
haskell: true
large-packages: false
docker-images: false
swap-storage: false
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: Create key file
run: echo $KEY_JKS | base64 -di > $KEY_PATH
- uses: subosito/flutter-action@v1
- uses: ruby/setup-ruby@v1
with:
flutter-version: 3.24.0
- run: flutter pub get
# - run: flutter test
- run: flutter build apk --release --split-per-abi
- run: flutter build appbundle
- name: Create github artifact release # disable this to save storage
ruby-version: '3.3'
bundler-cache: true
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Download bundle dependencies
run: |
gem install bundler
bundle config path vendor/bundle
bundle install
- name: Build container proxy
working-directory: packages/flutter_mozilla_components/javascript/container_proxy
run: |
npm i
npx webpack
- name: Create key file
working-directory: app
run: echo $KEY_JKS | base64 -di > $KEY_PATH
- uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: 3.32.4
cache: true
- name: Activate melos
working-directory: app
run: dart pub global activate melos
- name: Bootstrap melos
working-directory: app
run: melos bs
- name: Build APK
working-directory: app
run: flutter build apk --verbose --release --split-per-abi --no-tree-shake-icons
- name: Build App Bundle
working-directory: app
run: flutter build appbundle --verbose --no-tree-shake-icons
- name: Create github artifact release
uses: ncipollo/release-action@v1
with:
artifacts: "app/build/app/outputs/apk/release/*.apk,app/build/app/outputs/bundle/release/app-release.aab"
- name: Upload app bundle artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: appbundle
path: app/build/app/outputs/bundle/release/app-release.aab
path: app/build/app/outputs/bundle/release/app-release.aab
- name: Release to Google Play (internal)
working-directory: app
env:
SUPPLY_PACKAGE_NAME: ${{ secrets.ANDROID_PACKAGE_NAME }}
SUPPLY_JSON_KEY_DATA: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_KEY }}
run: |
bundle exec fastlane supply \
--aab build/app/outputs/bundle/release/app-release.aab \
--track internal
+13
View File
@@ -0,0 +1,13 @@
# IntelliJ
*.iml
*.ipr
*.iws
.idea/
# Mac
.DS_Store
# Flutter/Dart/Pub related
.dart_tool/
build/
pubspec.lock
+33 -4
View File
@@ -9,7 +9,10 @@
"cwd": "app",
"request": "launch",
"type": "dart",
"args": ["--device-user", "0"]
"args": [
"--device-user",
"0"
]
},
{
"name": "app (profile mode)",
@@ -17,7 +20,10 @@
"request": "launch",
"type": "dart",
"flutterMode": "profile",
"args": ["--device-user", "0"]
"args": [
"--device-user",
"0"
]
},
{
"name": "app (release mode)",
@@ -25,7 +31,30 @@
"request": "launch",
"type": "dart",
"flutterMode": "release",
"args": ["--device-user", "0"]
}
"args": [
"--device-user",
"0"
]
},
{
"name": "flutter_mozilla_components",
"cwd": "packages/flutter_mozilla_components/example",
"request": "launch",
"type": "dart",
"args": [
"--device-user",
"0"
]
},
{
"name": "simple_intent_receiver",
"cwd": "packages/simple_intent_receiver/example",
"request": "launch",
"type": "dart",
"args": [
"--device-user",
"0"
]
},
]
}
+3
View File
@@ -0,0 +1,3 @@
{
"dart.runPubGetOnPubspecChanges": "never"
}
+3
View File
@@ -0,0 +1,3 @@
source "https://rubygems.org"
gem "fastlane"
+227
View File
@@ -0,0 +1,227 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.7)
base64
nkf
rexml
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
artifactory (3.0.17)
atomos (0.1.3)
aws-eventstream (1.4.0)
aws-partitions (1.1116.0)
aws-sdk-core (3.225.2)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
base64
jmespath (~> 1, >= 1.6.1)
logger
aws-sdk-kms (1.105.0)
aws-sdk-core (~> 3, >= 3.225.0)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.189.1)
aws-sdk-core (~> 3, >= 3.225.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.12.1)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
base64 (0.3.0)
claide (1.1.0)
colored (1.2)
colored2 (3.1.2)
commander (4.6.0)
highline (~> 2.0.0)
declarative (0.0.20)
digest-crc (0.7.0)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.6.20240107)
dotenv (2.8.1)
emoji_regex (3.2.3)
excon (0.112.0)
faraday (1.10.4)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.0)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
ruby2_keywords (>= 0.0.4)
faraday-cookie_jar (0.0.7)
faraday (>= 0.8.0)
http-cookie (~> 1.0.0)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.1.0)
multipart-post (~> 2.0)
faraday-net_http (1.0.2)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
faraday_middleware (1.2.1)
faraday (~> 1.0)
fastimage (2.4.0)
fastlane (2.228.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
aws-sdk-s3 (~> 1.0)
babosa (>= 1.0.3, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0)
colored (~> 1.2)
commander (~> 4.6)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0)
excon (>= 0.71.0, < 1.0.0)
faraday (~> 1.0)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
fastlane-sirp (>= 1.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
google-cloud-env (>= 1.6.0, < 2.0.0)
google-cloud-storage (~> 1.31)
highline (~> 2.0)
http-cookie (~> 1.0.5)
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (>= 2.0.0, < 3.0.0)
naturally (~> 2.2)
optparse (>= 0.1.1, < 1.0.0)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.5)
simctl (~> 1.6.3)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (~> 3)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.4.1)
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
fastlane-sirp (1.0.0)
sysrandom (~> 1.0)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.54.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-core (0.11.3)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.a)
rexml
google-apis-iamcredentials_v1 (0.17.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-playcustomapp_v1 (0.13.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-storage_v1 (0.31.0)
google-apis-core (>= 0.11.0, < 2.a)
google-cloud-core (1.8.0)
google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.5.0)
google-cloud-storage (1.47.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.31.0)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (1.8.1)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
highline (2.0.3)
http-cookie (1.0.8)
domain_name (~> 0.5)
httpclient (2.9.0)
mutex_m
jmespath (1.6.2)
json (2.12.2)
jwt (2.10.1)
base64
logger (1.7.0)
mini_magick (4.13.2)
mini_mime (1.1.5)
multi_json (1.15.0)
multipart-post (2.4.1)
mutex_m (0.3.0)
nanaimo (0.4.0)
naturally (2.3.0)
nkf (0.2.0)
optparse (0.6.0)
os (1.1.4)
plist (3.7.2)
public_suffix (6.0.2)
rake (13.3.0)
representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.4.1)
rouge (3.28.0)
ruby2_keywords (0.0.5)
rubyzip (2.4.1)
security (0.1.5)
signet (0.20.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.10)
CFPropertyList
naturally
sysrandom (1.0.5)
terminal-notifier (2.0.0)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
trailblazer-option (0.1.2)
tty-cursor (0.7.1)
tty-screen (0.8.2)
tty-spinner (0.9.3)
tty-cursor (~> 0.7)
uber (0.1.0)
unicode-display_width (2.6.0)
word_wrap (1.0.0)
xcodeproj (1.27.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.4.0)
rexml (>= 3.3.6, < 4.0)
xcpretty (0.4.1)
rouge (~> 3.28.0)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
ruby
x86_64-linux
DEPENDENCIES
fastlane
BUNDLED WITH
2.6.9
+1 -1
View File
@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Lensai Copyright (C) 2024 Fabian Freund
WebLibre Copyright (C) 2024 Fabian Freund
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
+1 -71
View File
@@ -1,71 +1 @@
**Privacy Policy**
This privacy policy applies to the Lensai app (hereby referred to as "Application") for mobile devices that was created by Fabian Freund (hereby referred to as "Service Provider") as an Open Source service. This service is intended for use "AS IS".
**Information Collection and Use**
The Application collects information when you download and use it. This information may include information such as
* Your device's Internet Protocol address (e.g. IP address)
* The pages of the Application that you visit, the time and date of your visit, the time spent on those pages
* The time spent on the Application
* The operating system you use on your mobile device
The Application does not gather precise information about the location of your mobile device.
The Application collects your device's location, which helps the Service Provider determine your approximate geographical location and make use of in below ways:
* Geolocation Services: The Service Provider utilizes location data to provide features such as personalized content, relevant recommendations, and location-based services.
* Analytics and Improvements: Aggregated and anonymized location data helps the Service Provider to analyze user behavior, identify trends, and improve the overall performance and functionality of the Application.
* Third-Party Services: Periodically, the Service Provider may transmit anonymized location data to external services. These services assist them in enhancing the Application and optimizing their offerings.
The Service Provider may use the information you provided to contact you from time to time to provide you with important information, required notices and marketing promotions.
For a better experience, while using the Application, the Service Provider may require you to provide us with certain personally identifiable information, including but not limited to Kagi Session-ID. The information that the Service Provider request will be retained by them and used as described in this privacy policy.
**Third Party Access**
Only aggregated, anonymized data is periodically transmitted to external services to aid the Service Provider in improving the Application and their service. The Service Provider may share your information with third parties in the ways that are described in this privacy statement.
Please note that the Application utilizes third-party services that have their own Privacy Policy about handling data. Below are the links to the Privacy Policy of the third-party service providers used by the Application:
* [Google Play Services](https://www.google.com/policies/privacy/)
* [Kagi](https://kagi.com/privacy)
The Service Provider may disclose User Provided and Automatically Collected Information:
* as required by law, such as to comply with a subpoena, or similar legal process;
* when they believe in good faith that disclosure is necessary to protect their rights, protect your safety or the safety of others, investigate fraud, or respond to a government request;
* with their trusted services providers who work on their behalf, do not have an independent use of the information we disclose to them, and have agreed to adhere to the rules set forth in this privacy statement.
**Opt-Out Rights**
You can stop all collection of information by the Application easily by uninstalling it. You may use the standard uninstall processes as may be available as part of your mobile device or via the mobile application marketplace or network.
**Data Retention Policy**
The Service Provider will retain User Provided data for as long as you use the Application and for a reasonable time thereafter. If you'd like them to delete User Provided Data that you have provided via the Application, please contact them at oss@movenext.me and they will respond in a reasonable time.
**Children**
The Service Provider does not use the Application to knowingly solicit data from or market to children under the age of 13.
The Application does not address anyone under the age of 13\. The Service Provider does not knowingly collect personally identifiable information from children under 13 years of age. In the case the Service Provider discover that a child under 13 has provided personal information, the Service Provider will immediately delete this from their servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact the Service Provider (oss@movenext.me) so that they will be able to take the necessary actions.
**Security**
The Service Provider is concerned about safeguarding the confidentiality of your information. The Service Provider provides physical, electronic, and procedural safeguards to protect information the Service Provider processes and maintains.
**Changes**
This Privacy Policy may be updated from time to time for any reason. The Service Provider will notify you of any changes to the Privacy Policy by updating this page with the new Privacy Policy. You are advised to consult this Privacy Policy regularly for any changes, as continued use is deemed approval of all changes.
This privacy policy is effective as of 2024-05-17
**Your Consent**
By using the Application, you are consenting to the processing of your information as set forth in this Privacy Policy now and as amended by us.
**Contact Us**
If you have any questions regarding privacy while using the Application, or have questions about the practices, please contact the Service Provider via email at oss@movenext.me.
[Privacy Policy](https://docs.weblibre.eu/Privacy-Policy)
+48 -25
View File
@@ -1,37 +1,60 @@
<p align="center">
<img width="250" src="https://raw.githubusercontent.com/FaFre/lensai/main/app/assets/icon/icon.png" alt="Lensai Logo">
<img width="250" src="app/assets/icon/icon.png" alt="WebLibre Logo">
</p>
# Lensai: The Privacy-Focused & AI-Powered Research Browser with Kagi integration
# WebLibre: The Privacy-Focused Browser
<a href="https://liberapay.com/FaFre/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a>
Lensai enhances your mobile research experience while keeping your personal data and identity secure.
Powered by advanced AI tools and browser extensions, Lensai offers modern features designed to elevate your browsing experience.
<p align="center">
<a href='https://play.google.com/store/apps/details?id=me.movenext.bang_navigator&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'>
<img width="240" alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png'/>
</a>
</p>
WebLibre is an independent browser project built on the foundation of [Mozilla's Gecko Engine](https://en.wikipedia.org/wiki/Gecko_(software)) and [Mozilla Android Components](https://mozac.org/).
## Features
Lensai is packed with a variety of features aimed at improving usability and maintaining privacy:
### Privacy & Security
- **No Tracking:** WebLibre does not track or monitor your browsing.
- **Local Data Storage:** All your data stays on your device.
- **Tor integration:** Route container or private tab traffic through the Tor network for greater anonymity.
- **uBlock Origin integration:** Optional content blocker during onboarding
- **Web Engine Hardening**: Comprehensive and easy to use presets. `about:config` access is available for expert users.
- **Container Isolation:** Organize tabs into containers, each with its own cookie context and network settings.
- **Biometric Authentication:** Restrict access to sensitive containers using fingerprint or face recognition.
- **Privacy by Default**: Incognito mode is enabled by default to keep your searches and browsing history private.
- **[Kagi Integration](https://github.com/FaFre/lensai/wiki/Kagi-Tools)**: Seamlessly integrates Kagi's search, assistant, and summarizer tools within a user-friendly interface.
- **[Easy Sharing](https://github.com/FaFre/lensai/wiki/Kagi-Tools)**: Share URLs or text from any app directly into Lensai without the need to copy and paste, for direct use with summarizer/assistant.
- **[Quick Summarization](https://github.com/FaFre/lensai/wiki/Browser)**: Long press any link on a website to get a summary via Kagi's summarizer.
- **[Content Blocking](https://github.com/FaFre/lensai/wiki/Content-Blocking)**: A powerful content blocker is enabled by default to filter out ads, malware, and tracking sites.
- **[Home Widgets](https://github.com/FaFre/lensai/wiki/Home-Widget)**: Includes home screen widgets in three different sizes for quick actions, enhancing productivity.
- **[PDF processing](https://github.com/FaFre/lensai/wiki/Kagi-Tools)**: Open and directly summarize/prompt PDF documents.
- **Reader Mode**: Extract and simplify web pages, enhancing readability by removing ads, sidebars, and other non-essential elements.
- **[Chat Archive](https://github.com/FaFre/lensai/wiki/Chat-Archive)**: Easily archive conversations with the Kagi assistant on your device and search through them locally
- **[Bang Repository](https://github.com/FaFre/lensai/wiki/Bangs)**: Automatically synchronizes all official Kagi Bangs from the official Repository and makes them available through a convenient interface.
- **[Enhanced Text Interaction](https://github.com/FaFre/lensai/wiki/Browser)**: Custom context menu allows for easy passage of marked text to Kagi's search or assistant tools.
- **[Speech Input](https://github.com/FaFre/lensai/wiki/Kagi-Tools)**: Supports voice commands for hands-free operation and accessibility.
### Personal Local Search Engine
## Getting started
- **Unified Search:** Instantly search across:
- **Open Tabs:** By title, address, and full page content (no size limits).
- **Web Feeds:** Aggregate and search your favorite news sites and blogs, with full content indexing.
- **Browsing History:** Quickly find previously visited sites by title or address.
- **Bangs:** Use custom shortcuts for direct on-site search.
- **Local-First Search:** All indexing and search operations happen on your device—your data never leaves your control.
Head over to the [wiki](./wiki/Quickstart-Guide) to learn how to set up the app and explore its features.
### Improved Tab System
- **Tab Containers:** Organize tabs into isolated containers for privacy and workflow management.
- **Container Cookie Contexts:** Separate “cookie jars” for each container.
- **Tor Proxy:** Route container traffic through the Tor Network.
- **Biometric Authentication:** Restrict access to containers with fingerprint or face recognition.
- **Tree View Navigation:** Visualize and manage complex tab hierarchies, making it easy to track your browsing context.
### Bangs
- **Direct On-Site Search:** Use thousands of powerful shortcuts (“bangs”) to search directly on popular websites—no third-party search engine required.
---
## Getting Started
1. **Download & Install** WebLibre on your device.
2. **Organize Tabs:** Use containers for work, personal, or research contexts.
3. **Enable Tor Proxy:** For sensitive browsing, assign Tor to containers or private tabs.
4. **Add Web Feeds:** Aggregate your favorite sites for unified, private search.
5. **Try Bangs:** Search directly on your favorite websites with custom shortcuts.
<p align="center">
<a href='https://play.google.com/store/apps/details?id=eu.weblibre.gecko'>
<img height="100" alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png'/>
</a>
<a href='https://play.google.com/store/apps/details?id=eu.weblibre.gecko'>
<img height="100" alt='Get it on GitHub' src='https://github.com/machiav3lli/oandbackupx/raw/034b226cea5c1b30eb4f6a6f313e4dadcbb0ece4/badge_github.png'/>
</a>
</p>
-34
View File
@@ -1,34 +0,0 @@
**Terms & Conditions**
These terms and conditions applies to the Lensai app (hereby referred to as "Application") for mobile devices that was created by Fabian Freund (hereby referred to as "Service Provider") as an Open Source service.
Upon downloading or utilizing the Application, you are automatically agreeing to the following terms. It is strongly advised that you thoroughly read and understand these terms prior to using the Application. Unauthorized copying, modification of the Application, any part of the Application, or our trademarks is strictly prohibited. Any attempts to extract the source code of the Application, translate the Application into other languages, or create derivative versions are not permitted. All trademarks, copyrights, database rights, and other intellectual property rights related to the Application remain the property of the Service Provider.
The Service Provider is dedicated to ensuring that the Application is as beneficial and efficient as possible. As such, they reserve the right to modify the Application or charge for their services at any time and for any reason. The Service Provider assures you that any charges for the Application or its services will be clearly communicated to you.
The Application stores and processes personal data that you have provided to the Service Provider in order to provide the Service. It is your responsibility to maintain the security of your phone and access to the Application. The Service Provider strongly advise against jailbreaking or rooting your phone, which involves removing software restrictions and limitations imposed by the official operating system of your device. Such actions could expose your phone to malware, viruses, malicious programs, compromise your phone's security features, and may result in the Application not functioning correctly or at all.
Please note that the Application utilizes third-party services that have their own Terms and Conditions. Below are the links to the Terms and Conditions of the third-party service providers used by the Application:
* [Google Play Services](https://policies.google.com/terms)
* [Kagi](https://kagi.com/privacy)
Please be aware that the Service Provider does not assume responsibility for certain aspects. Some functions of the Application require an active internet connection, which can be Wi-Fi or provided by your mobile network provider. The Service Provider cannot be held responsible if the Application does not function at full capacity due to lack of access to Wi-Fi or if you have exhausted your data allowance.
If you are using the application outside of a Wi-Fi area, please be aware that your mobile network provider's agreement terms still apply. Consequently, you may incur charges from your mobile provider for data usage during the connection to the application, or other third-party charges. By using the application, you accept responsibility for any such charges, including roaming data charges if you use the application outside of your home territory (i.e., region or country) without disabling data roaming. If you are not the bill payer for the device on which you are using the application, they assume that you have obtained permission from the bill payer.
Similarly, the Service Provider cannot always assume responsibility for your usage of the application. For instance, it is your responsibility to ensure that your device remains charged. If your device runs out of battery and you are unable to access the Service, the Service Provider cannot be held responsible.
In terms of the Service Provider's responsibility for your use of the application, it is important to note that while they strive to ensure that it is updated and accurate at all times, they do rely on third parties to provide information to them so that they can make it available to you. The Service Provider accepts no liability for any loss, direct or indirect, that you experience as a result of relying entirely on this functionality of the application.
The Service Provider may wish to update the application at some point. The application is currently available as per the requirements for the operating system (and for any additional systems they decide to extend the availability of the application to) may change, and you will need to download the updates if you want to continue using the application. The Service Provider does not guarantee that it will always update the application so that it is relevant to you and/or compatible with the particular operating system version installed on your device. However, you agree to always accept updates to the application when offered to you. The Service Provider may also wish to cease providing the application and may terminate its use at any time without providing termination notice to you. Unless they inform you otherwise, upon any termination, (a) the rights and licenses granted to you in these terms will end; (b) you must cease using the application, and (if necessary) delete it from your device.
**Changes to These Terms and Conditions**
The Service Provider may periodically update their Terms and Conditions. Therefore, you are advised to review this page regularly for any changes. The Service Provider will notify you of any changes by posting the new Terms and Conditions on this page.
These terms and conditions are effective as of 2024-05-17
**Contact Us**
If you have any questions or suggestions about the Terms and Conditions, please do not hesitate to contact the Service Provider at oss@movenext.me.
+1
View File
@@ -0,0 +1 @@
* check al sqlite IS with and conditions to match values with and where both params are optional
+60
View File
@@ -31,6 +31,66 @@ migrate_working_dir/
.pub/
/build/
# Android related
**/android/**/gradle-wrapper.jar
.gradle/
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
**/android/key.properties
*.jks
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/.last_build_id
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/ephemeral
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# macOS
**/Flutter/ephemeral/
**/Pods/
**/macos/Flutter/GeneratedPluginRegistrant.swift
**/macos/Flutter/ephemeral
**/xcuserdata/
# Windows
**/windows/flutter/ephemeral/
**/windows/flutter/generated_plugin_registrant.cc
**/windows/flutter/generated_plugin_registrant.h
**/windows/flutter/generated_plugins.cmake
# Linux
**/linux/flutter/ephemeral/
**/linux/flutter/generated_plugin_registrant.cc
**/linux/flutter/generated_plugin_registrant.h
**/linux/flutter/generated_plugins.cmake
c
# Symbolication related
app.*.symbols
+6 -3
View File
@@ -16,14 +16,17 @@ include: package:lint/strict.yaml
linter:
rules:
- unawaited_futures
- discarded_futures
- collection_methods_unrelated_type
unawaited_futures: true
discarded_futures: true
collection_methods_unrelated_type: true
analyzer:
errors:
overridden_fields: ignore
plugins:
- custom_lint
exclude:
- "**.drift"
- "**.g.dart"
- "**.swagger.dart"
- "**.freezed.dart"
+1
View File
@@ -5,6 +5,7 @@ gradle-wrapper.jar
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.cxx
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+30 -7
View File
@@ -24,9 +24,10 @@ if (flutterVersionName == null) {
}
android {
namespace = "me.movenext.bang_navigator"
compileSdk = 34
ndkVersion = flutter.ndkVersion
namespace = "eu.weblibre.gecko"
compileSdk = 35
//Temporary fix
ndkVersion = "27.0.12077973" //flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
@@ -39,11 +40,11 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "me.movenext.bang_navigator"
applicationId = "eu.weblibre.gecko"
// https://api.flutter.dev/flutter/widgets/AndroidView-class.html
//https://inappwebview.dev/docs/intro/#setup-android
minSdk = 23
targetSdk = 34
targetSdk = 35
versionCode = flutterVersionCode.toInteger()
versionName = flutterVersionName
}
@@ -51,7 +52,7 @@ android {
dependencies {
// implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// implementation 'androidx.glance:glance-appwidget:1.0.0'
implementation 'com.google.android.material:material:1.11.0'
implementation 'com.google.android.material:material:1.12.0'
}
signingConfigs {
@@ -68,6 +69,28 @@ android {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.release
minifyEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
bundle {
// Profiler issues require us to temporarily package native code compressed to
// match the previous APK packaging.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1865634
packagingOptions {
jniLibs {
it.useLegacyPackaging = true
}
}
}
packagingOptions {
jniLibs {
useLegacyPackaging true
}
}
@@ -94,4 +117,4 @@ android.applicationVariants.all { variant ->
output.versionCodeOverride = variant.versionCode * 10 + abiVersionCode
}
}
}
}
+125
View File
@@ -0,0 +1,125 @@
# We do not want to obfuscate - It's just painful to debug without the right mapping file.
# If we update this, we'll have to update our Sentry config to upload ProGuard mappings.
-dontobfuscate
##### Default proguard settings:
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/sebastian/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
####################################################################################################
# Adjust
####################################################################################################
-keep public class com.adjust.sdk.** { *; }
-keep class com.google.android.gms.common.ConnectionResult {
int SUCCESS;
}
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient {
com.google.android.gms.ads.identifier.AdvertisingIdClient$Info getAdvertisingIdInfo(android.content.Context);
}
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient$Info {
java.lang.String getId();
boolean isLimitAdTrackingEnabled();
}
-keep class dalvik.system.VMRuntime {
java.lang.String getRuntime();
}
-keep class android.os.Build {
java.lang.String[] SUPPORTED_ABIS;
java.lang.String CPU_ABI;
}
-keep class android.content.res.Configuration {
android.os.LocaledList getLocales();
java.util.Locale locale;
}
-keep class android.os.LocaledList {
java.util.Locale get(int);
}
####################################################################################################
# Okhttp
####################################################################################################
# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**
# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*
# OkHttp platform used only on JVM and when Conscrypt dependency is available.
-dontwarn okhttp3.internal.platform.ConscryptPlatform
####################################################################################################
# Android architecture components
####################################################################################################
-dontwarn android.**
-dontwarn androidx.**
-dontwarn com.google.**
-dontwarn org.mozilla.geckoview.**
-dontwarn mozilla.components.**
# https://developer.android.com/topic/libraries/architecture/release-notes.html
# According to the docs this won't be needed when 1.0 of the library is released.
-keep class * implements android.arch.lifecycle.GeneratedAdapter {<init>(...);}
# Temporary fix until we can use androidx
-dontwarn mozilla.components.service.fretboard.scheduler.workmanager.**
# Fix for ViewModels
-keep class * extends androidx.lifecycle.ViewModel {
<init>();
}
-keep class * extends androidx.lifecycle.AndroidViewModel {
<init>(android.app.Application);
}
####################################################################################################
# Mozilla Application Services
####################################################################################################
-keep class mozilla.appservices.** { *; }
####################################################################################################
# Kotlinx
####################################################################################################
-dontwarn kotlinx.atomicfu.**
####################################################################################################
# snakeyaml
####################################################################################################
-dontwarn java.beans.PropertyDescriptor
-dontwarn java.beans.Introspector
-dontwarn java.beans.BeanInfo
-dontwarn java.beans.IntrospectionException
-dontwarn java.beans.FeatureDescriptor
####################################################################################################
# kotlinx.coroutines: use the fast service loader to init MainDispatcherLoader by including a rule
# to rewrite this property to return true:
# https://github.com/Kotlin/kotlinx.coroutines/blob/8c98180f177bbe4b26f1ed9685a9280fea648b9c/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt#L19
#
# R8 is expected to optimize the default implementation to avoid a performance issue but a bug in R8
# as bundled with AGP v7.0.0 causes this optimization to fail so we use the fast service loader instead. See:
# https://github.com/mozilla-mobile/focus-android/issues/5102#issuecomment-897854121
#
# The fast service loader appears to be as performant as the R8 optimization so it's not worth the
# churn to later remove this workaround. If needed, the upstream fix is being handled in
# https://issuetracker.google.com/issues/196302685
####################################################################################################
-assumenosideeffects class kotlinx.coroutines.internal.MainDispatcherLoader {
boolean FAST_SERVICE_LOADER_ENABLED return true;
}
+171 -10
View File
@@ -1,19 +1,78 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- This is needed because the android.permission.CAMERA above automatically
adds a requirements for camera hardware and we don't want add those restrictions -->
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.autofocus"
android:required="false" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- Needed for uploading media files on devices with Android 13 and later. -->
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
<!-- Needed to interact with all apps installed on a device -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<!-- Needed to support Credential Manager with Android 14 and later. -->
<uses-permission android:name="android.permission.CREDENTIAL_MANAGER_SET_ORIGIN" />
<uses-permission android:name="android.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS" />
<application
android:label="Lensai"
android:name="${applicationName}"
android:label="WebLibre"
android:name=".MyApplication"
android:icon="@mipmap/launcher_icon"
android:usesCleartextTraffic="true">
android:usesCleartextTraffic="true"
android:enableOnBackInvokedCallback="true">
<activity
android:name="eu.weblibre.flutter_mozilla_components.activities.NotificationActivity"
android:theme="@style/Theme.AppCompat.Translucent"
android:exported="false"
android:launchMode="singleTop" />
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:taskAffinity="eu.weblibre.gecko.task"
android:theme="@style/LaunchTheme"
android:supportsPictureInPicture="true"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- We do want to ahndle "open with" by ourselfs. Prevents automatically pushing routes. -->
<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
@@ -22,30 +81,102 @@
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.APP_BROWSER"/>
</intent-filter>
<intent-filter>
<action android:name="me.movenext.bang_navigator.action.LAUNCH" />
<action android:name="eu.weblibre.gecko.action.LAUNCH" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:mimeType="text/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<!-- Required exactly like this to appear in both default app choosers and when explicitly browsing -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
<!-- like above but specifying additional schemes. Not qualified for app chooser. -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:mimeType="text/html" />
<data android:mimeType="text/plain" />
<data android:mimeType="application/xhtml+xml" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:mimeType="application/pdf" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.WEB_SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:theme="@style/AddonsActivityTheme"
android:name="eu.weblibre.flutter_mozilla_components.addons.AddonsActivity"
android:label="@string/mozac_feature_addons_addons"
android:exported="false"
android:parentActivityName=".MainActivity" />
<activity
android:theme="@style/Theme.AppCompat.Light"
android:name="eu.weblibre.flutter_mozilla_components.addons.AddonDetailsActivity"
android:exported="false"
android:label="@string/mozac_feature_addons_addons" />
<activity android:name="eu.weblibre.flutter_mozilla_components.addons.InstalledAddonDetailsActivity"
android:label="@string/mozac_feature_addons_addons"
android:parentActivityName="eu.weblibre.flutter_mozilla_components.addons.AddonsActivity"
android:exported="false"
android:theme="@style/Theme.AppCompat.Light" />
<activity
android:name="eu.weblibre.flutter_mozilla_components.addons.PermissionsDetailsActivity"
android:label="@string/mozac_feature_addons_addons"
android:exported="false"
android:theme="@style/Theme.AppCompat.Light" />
<activity
android:name="eu.weblibre.flutter_mozilla_components.addons.AddonSettingsActivity"
android:label="@string/mozac_feature_addons_addons"
android:exported="false"
android:theme="@style/Theme.AppCompat.Light" />
<activity
android:name="eu.weblibre.flutter_mozilla_components.addons.WebExtensionActionPopupActivity"
android:label="@string/mozac_feature_addons_addons"
android:theme="@style/Theme.AppCompat.Light"/>
<receiver android:name="FullWidgetProvider" android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
@@ -54,6 +185,7 @@
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/full_widget_info"/>
</receiver>
<receiver android:name="CompactWidgetProvider" android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
@@ -62,6 +194,7 @@
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/compact_widget_info"/>
</receiver>
<receiver android:name="SearchBarWidgetProvider" android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
@@ -70,12 +203,40 @@
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/search_bar_widget_info"/>
</receiver>
<service
android:name="eu.weblibre.flutter_mozilla_components.services.CustomTabsService"
android:exported="true">
<intent-filter>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent-filter>
</service>
<service
android:name="eu.weblibre.flutter_mozilla_components.services.DownloadService"
android:foregroundServiceType="dataSync"
android:exported="false" />
<service android:name="eu.weblibre.flutter_mozilla_components.services.MediaSessionService"
android:foregroundServiceType="mediaPlayback"
android:exported="false" />
<service
android:name="id.flutter.flutter_background_service.BackgroundService"
android:foregroundServiceType="connectedDevice" />
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="*" />
</intent>
</queries>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
@@ -1,4 +1,4 @@
package me.movenext.bang_navigator
package eu.weblibre.gecko
import android.appwidget.AppWidgetManager
import android.content.Context
@@ -1,4 +1,4 @@
package me.movenext.bang_navigator
package eu.weblibre.gecko
import android.appwidget.AppWidgetManager
import android.content.Context
@@ -0,0 +1,32 @@
package eu.weblibre.gecko
import android.content.Context
import android.os.Bundle
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterFragmentActivity() {
private val CHANNEL = "eu.weblibre.flutter_mozilla_components/trim_memory"
private lateinit var channel: MethodChannel
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
channel.invokeMethod("onTrimMemory", level)
}
override fun getCachedEngineId(): String {
return "engine_id"
}
override fun shouldDestroyEngineWithHost(): Boolean {
return false // Keep engine alive when activity is destroyed
}
}
@@ -0,0 +1,46 @@
package eu.weblibre.gecko
import android.app.ActivityManager
import android.app.Application
import android.content.Context
import android.util.Log
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.embedding.engine.dart.DartExecutor
class MyApplication : Application() {
private fun getProcessNameCompat(): String {
// Try to get process name from ActivityManager
val pid = android.os.Process.myPid()
val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
for (processInfo in manager.runningAppProcesses) {
if (processInfo.pid == pid) {
return processInfo.processName
}
}
// Fallback to package name if process name cannot be determined
return packageName
}
override fun onCreate() {
super.onCreate()
val processName = getProcessNameCompat()
// Only initialize Flutter engine in the main process
if (processName == packageName) {
// Initialize the Flutter engine
val flutterEngine = FlutterEngine(this)
// Pre-warm the Flutter engine with your initial route
flutterEngine.navigationChannel.setInitialRoute("/")
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
// Cache the Flutter engine
FlutterEngineCache.getInstance().put("engine_id", flutterEngine)
}
}
}
@@ -1,4 +1,4 @@
package me.movenext.bang_navigator
package eu.weblibre.gecko
import android.appwidget.AppWidgetManager
import android.content.Context
@@ -1,5 +0,0 @@
package me.movenext.bang_navigator
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity()
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@@ -3,7 +3,7 @@
<item android:width="24dp"
android:height="24dp">
<bitmap
android:src="@drawable/launcher_icon"
android:src="@mipmap/launcher_icon"
android:width="24dp"
android:height="24dp"/>
</item>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

@@ -1,10 +1,10 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/Widget.LensaiWidget.AppWidget.Container"
style="@style/Widget.WebLibreWidget.AppWidget.Container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp"
android:theme="@style/Theme.LensaiWidget.AppWidgetContainer"
android:theme="@style/Theme.WebLibreWidget.AppWidgetContainer"
android:gravity="center_vertical">
<Button
@@ -1,10 +1,10 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/Widget.LensaiWidget.AppWidget.Container"
style="@style/Widget.WebLibreWidget.AppWidget.Container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:theme="@style/Theme.LensaiWidget.AppWidgetContainer">
android:theme="@style/Theme.WebLibreWidget.AppWidgetContainer">
<Button
android:id="@+id/search_button"
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground>
<inset
android:drawable="@drawable/ic_launcher_foreground"
android:inset="25%" />
</foreground>
</adaptive-icon>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

@@ -11,4 +11,5 @@
<color name="light_blue_200">#FF81D4FA</color>
<color name="light_blue_600">#FF039BE5</color>
<color name="light_blue_900">#FF01579B</color>
<color name="ic_launcher_background">#FF000000</color>
</resources>
@@ -16,13 +16,17 @@
<item name="android:windowBackground">?android:colorBackground</item>
</style>
<style name="Widget.LensaiWidget.AppWidget.Container" parent="android:Widget">
<style name="Widget.WebLibreWidget.AppWidget.Container" parent="android:Widget">
<item name="android:id">@android:id/background</item>
<item name="android:background">?android:attr/colorBackground</item>
</style>
<style name="Widget.LensaiWidget.AppWidget.InnerView" parent="android:Widget">
<style name="Widget.WebLibreWidget.AppWidget.InnerView" parent="android:Widget">
<item name="android:background">?android:attr/colorBackground</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="AddonsActivityTheme" parent="Theme.AppCompat.Light">
<item name="mozac_primary_text_color">@color/photonDarkGrey90</item>
</style>
</resources>
@@ -1,6 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.LensaiWidget" parent="@style/Theme.Material3.DayNight.NoActionBar">
<style name="Theme.WebLibreWidget" parent="@style/Theme.Material3.DayNight.NoActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
@@ -14,7 +14,7 @@
<!-- Customize your theme here. -->
</style>
<style name="Theme.LensaiWidget.AppWidgetContainerParent" parent="@android:style/Theme.DeviceDefault">
<style name="Theme.WebLibreWidget.AppWidgetContainerParent" parent="@android:style/Theme.DeviceDefault">
<!-- Radius of the outer bound of widgets to make the rounded corners -->
<item name="appWidgetRadius">16dp</item>
<!--
@@ -24,7 +24,7 @@
<item name="appWidgetInnerRadius">8dp</item>
</style>
<style name="Theme.LensaiWidget.AppWidgetContainer" parent="Theme.LensaiWidget.AppWidgetContainerParent">
<style name="Theme.WebLibreWidget.AppWidgetContainer" parent="Theme.WebLibreWidget.AppWidgetContainerParent">
<!-- Apply padding to avoid the content of the widget colliding with the rounded corners -->
<item name="appWidgetPadding">16dp</item>
</style>
+9 -1
View File
@@ -1,11 +1,19 @@
//Required by background_fetch build
ext {
compileSdkVersion = 35
targetSdkVersion = 35
}
buildscript {
ext.kotlin_version = '1.9.22'
ext.kotlin_version = '2.1.20'
}
allprojects {
repositories {
google()
mavenCentral()
//Required by background_fetch build
maven { url "${project(':background_fetch').projectDir}/libs" }
}
}
+1 -2
View File
@@ -2,5 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
distributionSha256Sum=194717442575a6f96e1c1befa2c30e9a4fc90f701d7aee33eb879b79e7ff05c0
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
+2 -2
View File
@@ -18,8 +18,8 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
id "org.jetbrains.kotlin.android" version "1.9.22" apply false
id "com.android.application" version '8.10.1' apply false
id "org.jetbrains.kotlin.android" version "2.1.20" apply false
}
include ":app"
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 23 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg width="147" height="106" viewBox="0 0 147 106" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M146.187 4.16996C146.608 2.00885 144.953 0 142.751 0H116.196C114.521 0 113.081 1.18632 112.761 2.83005L93.5631 101.268C93.1417 103.429 94.7966 105.438 96.9984 105.438H123.554C125.229 105.438 126.669 104.251 126.989 102.608L146.187 4.16996Z" fill="#A7A7A7"/>
<foreignObject x="20.7803" y="18.1592" width="106.094" height="90.7813"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(1.75px);height:100%;width:100%"></div></foreignObject><path data-figma-bg-blur-radius="3.50001" d="M106.53 105.441C106.53 71.9717 98.6042 38.5029 74.1832 38.5029C49.7622 38.5029 41.124 71.3155 41.124 105.441" stroke="#FBDC6B" stroke-opacity="0.8" stroke-width="33.6875"/>
<path d="M0.813229 4.16996C0.391767 2.00885 2.04669 0 4.24852 0H30.8042C32.4789 0 33.9189 1.18632 34.2395 2.83005L53.4369 101.268C53.8583 103.429 52.2034 105.438 50.0016 105.438H23.4459C21.7712 105.438 20.3312 104.251 20.0106 102.608L0.813229 4.16996Z" fill="#9C83F8"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+49
View File
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 150.5 90.7" enable-background="new 0 0 150.5 90.7" xml:space="preserve">
<g>
<g>
<g>
<path fill="#FFFFFF" d="M85.7,71.3c0.1-1.8-0.7-4.6-1.5-7.3c-0.5-2-2.4-4-2.9-5.5c-0.9-2.5-0.5-4.3-0.6-7.8
c0.4,3.2,0.4,6,1.2,7.3c0.7,1.3,1.9,3.1,2.6,4.9c1.3,3.3,2.1,6.9,2.2,8.6c0.1,2.9-0.3,7.5-1.3,11c-0.4,1.3-0.9,2.5-1.5,3.6
c1.3-1.9,2.2-4,2.6-5.7c1.3-6.1,2.4-12.6,1.5-16.4c-0.2-0.9-0.5-3.8-1.7-7.1c-1.8-4.6-4.2-8.5-4.5-9.4c-0.9-2.6-1.2-9.1-1.3-12.6
c0.1,4.2,0.9,10.3,1.5,11.7c0.3,0.9,3,5.2,4.9,9.7c1.3,3,1.6,5.7,1.8,6.5c1,8.4,0.1,11.9-2,19.4c-0.3,1.1-0.8,2.1-1.3,3.1
c1.5-1.8,2.6-3.7,3-5.3c2.2-9.4,2.9-21.4-0.1-30.5c-1.5-4.8-5.2-9-6.1-11.9c-1-3.4-0.3-8.9-0.3-9l-8.1-3.6
c2.1,5.4,2.5,9.6,0.3,11.4c-8.5,7-22.7,14.9-22.7,26.7c0,12.5,7.4,26,26.5,27c-2.3-0.6-4.5-1.3-6.4-2.1c-1.9-0.8-3.6-1.9-4.9-3.4
c0-0.1-0.5-0.5-0.5-0.5c-2.8-3.2-6.3-8.6-7.5-13.7c-0.5-2-0.9-3.6-0.3-5.7c2.3-8.2,7.3-11.5,12.3-14.9c1.1-0.7,2.5-1.4,3.6-2.3
c2.1-1.5,3.2-6.3,4.4-10.1c-0.6,3.1-1.3,8.7-4,10.8c-1.1,0.8-2.3,1.6-3.5,2.4c-4.8,3.3-9.7,6.4-11.9,14.3
c-0.5,1.7-0.1,3.5,0.3,5.2c1.2,4.9,4.6,10.1,7.3,13.2c0,0.1,0.5,0.5,0.5,0.6c1.8,2.1,2.6,3,7.3,4.3c-1.1-0.6-2-1.3-2.8-1.8
c-4.8-3.3-7.2-7.4-7.6-13.5c-0.3-4.7,2.6-9.7,8.3-12.7c3.9-2,5-5.2,6.3-9c-0.9,4.3-1.8,7-5.9,9.4c-4.8,2.8-8.2,8.2-7.9,11.9
c0.4,4.5,3.6,9.7,7.1,12.8c1.5,1.3,3.8,2.1,6,2.7c-0.6-0.5-1.2-1.1-1.8-1.9c-0.7-1-1.2-2.4-1.7-3.7c-0.5-1.3-1-2.6-1-3.8
c0-2.9,0.3-6.5,2.4-8.9c1.9-2.2,2.5-2.5,3.3-5.1c-0.6,3-1,3.2-2.8,5.7c-2.3,3.2-2.4,6.4-2.1,8.4c0.2,1.3,0.7,2.5,1.1,3.5
c0.5,1.3,1.1,2.5,1.9,3.6c0.7,0.8,1,1.4,2.1,1.8c1.1-2,1.8-6.2,1.9-8.6c0.1-2,0.2-4.3-0.1-6.6c-0.4-3.1-1.4-6.1-1.4-8.5
c0.5,2.2,1.6,5.2,2.3,8.3c0.5,2.4,0.6,4.8,0.4,6.5c-0.2,2.1-0.3,3.8-0.6,5.6c-0.2,1.2-0.8,2.4-1.7,3.5c2.2-1.7,3.6-4.3,4.2-7.4
C85.6,77,85.7,73.3,85.7,71.3z"/>
</g>
<g>
<path fill="#FFFFFF" d="M106,53.4c-3.1-5.7-11-11.3-19.3-16.5c-1.9-1.1-2.3-5.3-2-7.1l-1.9-0.9c0,0-0.7,5,0.6,9
c0.9,2.8,3.9,6,5.7,10.5c1.3,3,2.6,10.2,2.7,11c1,8.4,0.5,16.3-2,23c-1.1,3.1-3.5,5.9-5.4,7.2c-0.3,0.2-0.6,0.4-0.9,0.6
C99.3,90.7,116.7,72.8,106,53.4z"/>
</g>
</g>
<g>
<path fill="#FFFFFF" d="M102.7,4.3C96.2,6.5,90.4,9.9,86.1,16c2.6-5.3,6.7-10.6,11.3-16c-6.3,4.5-11.7,9.6-15.2,16.4l2.4-9.6
c-3.7,6.6-6.2,13.4-7,20.1l4.2,1.8C87.2,20.4,94.1,12.1,102.7,4.3z"/>
</g>
<g>
<polygon fill="#FFFFFF" points="80.1,64.5 80.1,64.5 80.1,64.5 "/>
</g>
<g>
<path fill="#FFFFFF" d="M61.6,28.6v-16c0-2.1-1.8-3.8-3.8-3.8H3.8C1.7,8.8,0,10.5,0,12.6v16c0,2.1,1.7,3.8,3.8,3.8H16
c1.1,0,2.6,0.9,2.6,2.6v52.7c0,1.8,1.3,3.1,3,3.1h18.4c1.7,0,3.1-1.3,3.1-3.1V35.4c0-1.6,1.1-3,3.6-3h11.1
C59.9,32.3,61.6,30.6,61.6,28.6z"/>
</g>
<g>
<path fill="#FFFFFF" d="M150.5,50.2V33.1c0-1.7-1.4-3.1-3.2-3.1h-4.8c-0.6,0-2.1,0.1-2.5,0.1c-12.1,1.2-21.7,11-22.6,23.2
c-0.1,0.5-0.1,1.3-0.1,1.9v32.9c0,1.3,1.3,2.5,2.8,2.5h17.2c1.5,0,2.8-1.1,2.8-2.5V59.2c0-3.1,2.4-5.9,7.7-5.9
C150.6,53.3,150.5,52.4,150.5,50.2z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

+44
View File
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
viewBox="0 0 128 128"
height="128"
width="128">
<g
style="display:inline;opacity:1">
<g
style="fill:#800000;fill-opacity:1;stroke:#ffffff;stroke-width:1.62100744;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
transform="matrix(0.6778654,0,0,0.56141828,-241.07537,-247.27712)" />
<g
transform="matrix(-0.6945203,0,0,0.56109687,375.02964,-247.42947)"
style="fill:#800000;fill-opacity:1;stroke:#ffffff;stroke-width:1.60191178000000001;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline;stroke-linejoin:round">
<path
d="m 447.83376,669.09921 c -80.63119,-57.03115 -80.63119,-57.03115 -80.63119,-199.60903 34.55623,0 46.07497,0 80.63119,-28.51558 m 0,228.12461 c 80.6312,-57.03115 80.6312,-57.03115 80.6312,-199.60903 -34.55623,0 -46.07497,0 -80.6312,-28.51558"
style="fill:#800000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:1.60191178;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
</g>
</g>
<g
transform="translate(-17.872548,-17.32535)"
style="display:inline">
<ellipse
transform="matrix(1.3333335,0,0,1.3333334,-42.290862,-31.108461)"
ry="12"
rx="11.999999"
cy="81.325356"
cx="102.12254"
style="fill:none;stroke:#ffffff;stroke-width:5.99999952;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline;" />
<g
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.99999996999999996;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
transform="scale(1.0018026,0.99820067)">
<path
d="m 81.72523,81.471945 c 0,11.019828 -4.991003,16.028841 -15.97121,16.028841 -10.980207,0 -15.97121,-5.009013 -15.97121,-16.028841 l 0,-24.043262 7.985605,0 0,24.043262 c 0,7.012618 0.9982,8.014421 7.985605,8.014421 6.987404,0 7.985605,-1.001803 7.985605,-8.014421 l 0,-24.043262 7.985605,0 z"
style="fill:#ffffff;stroke:#ffffff;stroke-width:0;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

-17
View File
@@ -1,17 +0,0 @@
Lensai enhances your mobile research experience while keeping your personal data and identity secure.
Powered by advanced AI tools and browser extensions, Lensai offers modern features designed to elevate your browsing experience.
Lensai is packed with a variety of features aimed at improving usability and maintaining privacy:
- **Privacy by Default**: Incognito mode is enabled by default to keep your searches and browsing history private.
- **[Kagi Integration](https://github.com/FaFre/lensai/wiki/Kagi-Tools)**: Seamlessly integrates Kagi's search, assistant, and summarizer tools within a user-friendly interface.
- **[Easy Sharing](https://github.com/FaFre/lensai/wiki/Kagi-Tools)**: Share URLs or text from any app directly into Lensai without the need to copy and paste, for direct use with summarizer/assistant.
- **[Quick Summarization](https://github.com/FaFre/lensai/wiki/Browser)**: Long press any link on a website to get a summary via Kagi's summarizer.
- **[Content Blocking](https://github.com/FaFre/lensai/wiki/Content-Blocking)**: A powerful content blocker is enabled by default to filter out ads, malware, and tracking sites.
- **[Home Widgets](https://github.com/FaFre/lensai/wiki/Home-Widget)**: Includes home screen widgets in three different sizes for quick actions, enhancing productivity.
- **[PDF processing](https://github.com/FaFre/lensai/wiki/Kagi-Tools)**: Open and directly summarize/prompt PDF documents.
- **Reader Mode**: Extract and simplify web pages, enhancing readability by removing ads, sidebars, and other non-essential elements.
- **[Chat Archive](https://github.com/FaFre/lensai/wiki/Chat-Archive)**: Easily archive conversations with the Kagi assistant on your device and search through them locally
- **[Bang Repository](https://github.com/FaFre/lensai/wiki/Bangs)**: Automatically synchronizes all official Kagi Bangs from the official Repository and makes them available through a convenient interface.
- **[Enhanced Text Interaction](https://github.com/FaFre/lensai/wiki/Browser)**: Custom context menu allows for easy passage of marked text to Kagi's search or assistant tools.
- **[Speech Input](https://github.com/FaFre/lensai/wiki/Kagi-Tools)**: Supports voice commands for hands-free operation and accessibility.
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
../../js/readability/dist/readability.min.js.gz
+7
View File
@@ -11,6 +11,13 @@ targets:
dialect: sqlite
options:
version: "3.45"
known_functions:
lexo_rank_next: "text (int, text null)"
lexo_rank_previous: "text (int, text null)"
lexo_rank_reorder_after: "text (text null, text null)"
modules:
- json1
- fts5
json_serializable:
options:
explicit_to_json: true
+1
View File
@@ -1,3 +1,4 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
- drift: true
-14
View File
@@ -1,14 +0,0 @@
// This is a generated file; do not edit or check into version control.
FLUTTER_ROOT=/home/fafre/Software/flutter
FLUTTER_APPLICATION_PATH=/home/fafre/Repos/kagi_bang_bang/app
COCOAPODS_PARALLEL_CODE_SIGN=true
FLUTTER_TARGET=lib/main.dart
FLUTTER_BUILD_DIR=build
FLUTTER_BUILD_NAME=0.6.2
FLUTTER_BUILD_NUMBER=14
EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386
EXCLUDED_ARCHS[sdk=iphoneos*]=armv7
DART_OBFUSCATION=false
TRACK_WIDGET_CREATION=true
TREE_SHAKE_ICONS=false
PACKAGE_CONFIG=.dart_tool/package_config.json
@@ -1,13 +0,0 @@
#!/bin/sh
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/home/fafre/Software/flutter"
export "FLUTTER_APPLICATION_PATH=/home/fafre/Repos/kagi_bang_bang/app"
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
export "FLUTTER_TARGET=lib/main.dart"
export "FLUTTER_BUILD_DIR=build"
export "FLUTTER_BUILD_NAME=0.6.2"
export "FLUTTER_BUILD_NUMBER=14"
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TREE_SHAKE_ICONS=false"
export "PACKAGE_CONFIG=.dart_tool/package_config.json"
@@ -1,19 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GeneratedPluginRegistrant_h
#define GeneratedPluginRegistrant_h
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
@interface GeneratedPluginRegistrant : NSObject
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry;
@end
NS_ASSUME_NONNULL_END
#endif /* GeneratedPluginRegistrant_h */
@@ -1,98 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#import "GeneratedPluginRegistrant.h"
#if __has_include(<file_picker/FilePickerPlugin.h>)
#import <file_picker/FilePickerPlugin.h>
#else
@import file_picker;
#endif
#if __has_include(<flutter_inappwebview_ios/InAppWebViewFlutterPlugin.h>)
#import <flutter_inappwebview_ios/InAppWebViewFlutterPlugin.h>
#else
@import flutter_inappwebview_ios;
#endif
#if __has_include(<flutter_pdf_text/PdfTextPlugin.h>)
#import <flutter_pdf_text/PdfTextPlugin.h>
#else
@import flutter_pdf_text;
#endif
#if __has_include(<flutter_secure_storage/FlutterSecureStoragePlugin.h>)
#import <flutter_secure_storage/FlutterSecureStoragePlugin.h>
#else
@import flutter_secure_storage;
#endif
#if __has_include(<flutter_sharing_intent/FlutterSharingIntentPlugin.h>)
#import <flutter_sharing_intent/FlutterSharingIntentPlugin.h>
#else
@import flutter_sharing_intent;
#endif
#if __has_include(<home_widget/HomeWidgetPlugin.h>)
#import <home_widget/HomeWidgetPlugin.h>
#else
@import home_widget;
#endif
#if __has_include(<package_info_plus/FPPPackageInfoPlusPlugin.h>)
#import <package_info_plus/FPPPackageInfoPlusPlugin.h>
#else
@import package_info_plus;
#endif
#if __has_include(<path_provider_foundation/PathProviderPlugin.h>)
#import <path_provider_foundation/PathProviderPlugin.h>
#else
@import path_provider_foundation;
#endif
#if __has_include(<share_plus/FPPSharePlusPlugin.h>)
#import <share_plus/FPPSharePlusPlugin.h>
#else
@import share_plus;
#endif
#if __has_include(<shared_preferences_foundation/SharedPreferencesPlugin.h>)
#import <shared_preferences_foundation/SharedPreferencesPlugin.h>
#else
@import shared_preferences_foundation;
#endif
#if __has_include(<sqlite3_flutter_libs/Sqlite3FlutterLibsPlugin.h>)
#import <sqlite3_flutter_libs/Sqlite3FlutterLibsPlugin.h>
#else
@import sqlite3_flutter_libs;
#endif
#if __has_include(<url_launcher_ios/URLLauncherPlugin.h>)
#import <url_launcher_ios/URLLauncherPlugin.h>
#else
@import url_launcher_ios;
#endif
@implementation GeneratedPluginRegistrant
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
[FilePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FilePickerPlugin"]];
[InAppWebViewFlutterPlugin registerWithRegistrar:[registry registrarForPlugin:@"InAppWebViewFlutterPlugin"]];
[PdfTextPlugin registerWithRegistrar:[registry registrarForPlugin:@"PdfTextPlugin"]];
[FlutterSecureStoragePlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterSecureStoragePlugin"]];
[FlutterSharingIntentPlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterSharingIntentPlugin"]];
[HomeWidgetPlugin registerWithRegistrar:[registry registrarForPlugin:@"HomeWidgetPlugin"]];
[FPPPackageInfoPlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"FPPPackageInfoPlusPlugin"]];
[PathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"PathProviderPlugin"]];
[FPPSharePlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"FPPSharePlusPlugin"]];
[SharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"SharedPreferencesPlugin"]];
[Sqlite3FlutterLibsPlugin registerWithRegistrar:[registry registrarForPlugin:@"Sqlite3FlutterLibsPlugin"]];
[URLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"URLLauncherPlugin"]];
}
@end
File diff suppressed because one or more lines are too long
Binary file not shown.
-1362
View File
File diff suppressed because it is too large Load Diff
-39
View File
@@ -1,39 +0,0 @@
import { Readability, isProbablyReaderable } from '@mozilla/readability';
import DOMPurify from 'dompurify';
function isReaderable() {
return isProbablyReaderable(document);
}
function parseReaderable(doc) {
const reader = new Readability(doc);
const article = reader.parse();
return article;
}
function applyReaderable() {
const clonedDoc = document.cloneNode(true);
const article = parseReaderable(clonedDoc);
const markup = DOMPurify.sanitize(article.content);
// console.log('article title: ', parsed.title);
// console.log('HTML string of processed article content: ', parsed.content);
// console.log('text content of the article, with all the HTML tags removed: ', parsed.textContent);
// console.log('length of an article, in characters: ', parsed.length);
// console.log('article description, or short excerpt from the content: ', parsed.excerpt);
// console.log('author metadata: ', parsed.byline);
// console.log('content direction: ', parsed.dir);
// console.log('name of the site: ', parsed.siteName);
// console.log('content language: ', parsed.lang);
// console.log('published time: ', parsed.publishedTime);
if (markup) {
document.body.innerHTML = '<div style="margin: 4pt;">' + markup + '</div>';
}
}
// Attach functions to the window object
window.isReaderable = isReaderable;
window.applyReaderable = applyReaderable;
export { isReaderable, applyReaderable };
+1 -1
View File
@@ -1,5 +1,5 @@
import 'package:lensai/core/logger.dart';
import 'package:riverpod/riverpod.dart';
import 'package:weblibre/core/logger.dart';
class ErrorObserver extends ProviderObserver {
const ErrorObserver();
-15
View File
@@ -1,15 +0,0 @@
import 'package:exceptions/exceptions.dart';
import 'package:riverpod/riverpod.dart';
extension AsyncExtension<T> on Result<T> {
AsyncValue<T> toAsyncValue() {
return switch (this) {
Success(value: final value) => AsyncValue.data(value),
// TODO: Handle this case.
Failure(
error: final error,
) =>
AsyncError(error.message, error.stackTrace ?? StackTrace.empty),
};
}
}
+14 -20
View File
@@ -2,30 +2,24 @@ import 'package:exceptions/exceptions.dart';
import 'package:http/http.dart';
import 'package:universal_io/io.dart';
ErrorMessage handleHttpError(
Exception exception,
StackTrace stackTrace,
) {
ErrorMessage handleHttpError(Exception exception, StackTrace stackTrace) {
return switch (exception) {
SocketException() => const ErrorMessage(
source: 'http',
message: 'Could not contact remote service',
),
source: 'http',
message: 'Could not contact remote service',
),
HttpException() => const ErrorMessage(
source: 'http',
message: 'Web request returned error',
),
source: 'http',
message: 'Web request returned error',
),
FormatException() => const ErrorMessage(
source: 'http',
message: 'Bad response format',
),
source: 'http',
message: 'Bad response format',
),
ClientException() => const ErrorMessage(
source: 'http',
message: 'Could not contact remote service',
),
_ => ErrorMessage.fromException(
exception,
stackTrace,
)
source: 'http',
message: 'Could not contact remote service',
),
_ => ErrorMessage.fromException(exception, stackTrace),
};
}
+13 -1
View File
@@ -1,3 +1,15 @@
import 'package:logger/logger.dart';
final logger = Logger();
class _DebugLogFilter extends LogFilter {
@override
bool shouldLog(LogEvent event) {
//Log all events
return true;
}
}
final loggerMemory = MemoryOutput(
bufferSize: 255,
secondOutput: ConsoleOutput(),
);
final logger = Logger(filter: _DebugLogFilter(), output: loggerMemory);
-11
View File
@@ -1,11 +0,0 @@
import 'package:go_router/go_router.dart';
import 'package:lensai/core/routing/routes.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'providers.g.dart';
@Riverpod(keepAlive: true)
GoRouter router(RouterRef ref) => GoRouter(
debugLogDiagnostics: true,
routes: $appRoutes,
);
-24
View File
@@ -1,24 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'providers.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$routerHash() => r'4ad45f430b1f43e427e21122d16bd761ca04ae0e';
/// See also [router].
@ProviderFor(router)
final routerProvider = Provider<GoRouter>.internal(
router,
name: r'routerProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$routerHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef RouterRef = ProviderRef<GoRouter>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
+14
View File
@@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'defaults.g.dart';
@Riverpod(keepAlive: true)
Color lightSeedColorFallback(Ref ref) => const Color(0xFF167C80);
@Riverpod(keepAlive: true)
Color darkSeedColorFallback(Ref ref) => const Color(0xFF167C80);
@Riverpod(keepAlive: true)
Uri docsUri(Ref ref) => Uri.parse('https://docs.weblibre.eu/');
+63
View File
@@ -0,0 +1,63 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'defaults.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$lightSeedColorFallbackHash() =>
r'af151d3293d372f5df451aa8c3a75136377b6ce1';
/// See also [lightSeedColorFallback].
@ProviderFor(lightSeedColorFallback)
final lightSeedColorFallbackProvider = Provider<Color>.internal(
lightSeedColorFallback,
name: r'lightSeedColorFallbackProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$lightSeedColorFallbackHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef LightSeedColorFallbackRef = ProviderRef<Color>;
String _$darkSeedColorFallbackHash() =>
r'9418cf71e3d80c8a2142e0d3a8820989b29bff86';
/// See also [darkSeedColorFallback].
@ProviderFor(darkSeedColorFallback)
final darkSeedColorFallbackProvider = Provider<Color>.internal(
darkSeedColorFallback,
name: r'darkSeedColorFallbackProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$darkSeedColorFallbackHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef DarkSeedColorFallbackRef = ProviderRef<Color>;
String _$docsUriHash() => r'6456efcf97ddc7ee87a67e2d3380f7241e3d76ea';
/// See also [docsUri].
@ProviderFor(docsUri)
final docsUriProvider = Provider<Uri>.internal(
docsUri,
name: r'docsUriProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$docsUriHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef DocsUriRef = ProviderRef<Uri>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
+19
View File
@@ -0,0 +1,19 @@
import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'format.g.dart';
@Riverpod(keepAlive: true)
class Format extends _$Format {
String fullDateTimeWithTimezone(DateTime date) {
final pattern = DateFormat('yMMMMd').addPattern('Hm');
return pattern.format(date);
}
@override
Future<void> build() async {
await initializeDateFormatting();
}
}
@@ -1,25 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'host.dart';
part of 'format.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$hostRepositoryHash() => r'b28362146b75391f55fb353fc6fde72dc1d2fcb9';
String _$formatHash() => r'fe7fcdd19b512784a36f3838c57701030475e429';
/// See also [HostRepository].
@ProviderFor(HostRepository)
final hostRepositoryProvider = NotifierProvider<HostRepository, void>.internal(
HostRepository.new,
name: r'hostRepositoryProvider',
/// See also [Format].
@ProviderFor(Format)
final formatProvider = AsyncNotifierProvider<Format, void>.internal(
Format.new,
name: r'formatProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$hostRepositoryHash,
: _$formatHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$HostRepository = Notifier<void>;
typedef _$Format = AsyncNotifier<void>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
+21
View File
@@ -0,0 +1,21 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:weblibre/data/models/drag_data.dart';
part 'global_drop.g.dart';
@Riverpod()
class WillAcceptDrop extends _$WillAcceptDrop {
// ignore: use_setters_to_change_properties api decision
void setData(DropTargetData data) {
state = data;
}
void clear() {
state = null;
}
@override
DropTargetData? build() {
return null;
}
}
+26
View File
@@ -0,0 +1,26 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'global_drop.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$willAcceptDropHash() => r'97b47784ef9101757602b4408d1f545ef2308830';
/// See also [WillAcceptDrop].
@ProviderFor(WillAcceptDrop)
final willAcceptDropProvider =
AutoDisposeNotifierProvider<WillAcceptDrop, DropTargetData?>.internal(
WillAcceptDrop.new,
name: r'willAcceptDropProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$willAcceptDropHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$WillAcceptDrop = AutoDisposeNotifier<DropTargetData?>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
+35
View File
@@ -0,0 +1,35 @@
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/features/user/domain/repositories/onboarding.dart';
part 'router.g.dart';
@Riverpod(keepAlive: true)
Future<GoRouter> router(Ref ref) async {
String? initialLocation;
final onboardingMandatory = await ref
.read(onboardingRepositoryProvider.notifier)
.isOutdated();
if (onboardingMandatory) {
final current = await ref
.read(onboardingRepositoryProvider.notifier)
.getCurrentRevision();
final route = OnboardingRoute(
currentRevision: current ?? -1,
targetRevision: OnboardingRepository.targetRevision,
);
initialLocation = route.location;
}
return GoRouter(
debugLogDiagnostics: true,
routes: $appRoutes,
initialLocation: initialLocation,
);
}
+27
View File
@@ -0,0 +1,27 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'router.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$routerHash() => r'ce7cc5b5f236781a06867a852a08ed03d3582022';
/// See also [router].
@ProviderFor(router)
final routerProvider = FutureProvider<GoRouter>.internal(
router,
name: r'routerProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$routerHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef RouterRef = FutureProviderRef<GoRouter>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
+72
View File
@@ -0,0 +1,72 @@
part of 'routes.dart';
@TypedGoRoute<BangCategoriesRoute>(
name: 'BangRoute',
path: '/bangs',
routes: [
TypedGoRoute<BangSearchRoute>(
name: 'BangSearchRoute',
path: 'search/:searchText',
),
TypedGoRoute<BangCategoryRoute>(
name: 'BangCategoryRoute',
path: 'category/:category',
routes: [
TypedGoRoute<BangSubCategoryRoute>(
name: 'BangSubCategoryRoute',
path: ':subCategory',
),
],
),
],
)
class BangCategoriesRoute extends GoRouteData with _$BangCategoriesRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const BangCategoriesScreen();
}
}
class BangCategoryRoute extends GoRouteData with _$BangCategoryRoute {
final String category;
const BangCategoryRoute({required this.category});
@override
Widget build(BuildContext context, GoRouterState state) {
return BangListScreen(category: category);
}
}
class BangSubCategoryRoute extends GoRouteData with _$BangSubCategoryRoute {
final String category;
final String subCategory;
const BangSubCategoryRoute({
required this.category,
required this.subCategory,
});
@override
Widget build(BuildContext context, GoRouterState state) {
return BangListScreen(category: category, subCategory: subCategory);
}
}
class BangSearchRoute extends GoRouteData with _$BangSearchRoute {
static const String emptySearchText = ' ';
//This should be nullable but isnt allowed by go_router
final String searchText;
const BangSearchRoute({this.searchText = BangSearchRoute.emptySearchText});
@override
Widget build(BuildContext context, GoRouterState state) {
return BangSearchScreen(
initialSearchText: (searchText.isEmpty || searchText == emptySearchText)
? null
: searchText,
);
}
}
+145
View File
@@ -0,0 +1,145 @@
part of 'routes.dart';
@TypedGoRoute<BrowserRoute>(
name: BrowserRoute.name,
path: '/',
routes: [
TypedGoRoute<WebPageRoute>(name: 'WebPageRoute', path: 'page/:url'),
TypedGoRoute<SearchRoute>(
name: 'SearchRoute',
path: 'search/:tabType/:searchText',
),
TypedGoRoute<TorProxyRoute>(name: 'TorProxyRoute', path: 'tor_proxy'),
TypedGoRoute<ContextMenuRoute>(
name: 'ContextMenuRoute',
path: 'context_menu',
),
TypedGoRoute<ContainerListRoute>(
name: 'ContainerListRoute',
path: 'containers',
routes: [
TypedGoRoute<ContainerCreateRoute>(
name: 'ContainerCreateRoute',
path: 'create',
),
TypedGoRoute<ContainerEditRoute>(
name: 'ContainerEditRoute',
path: 'edit',
),
],
),
TypedGoRoute<TabTreeRoute>(
name: 'TabTreeRoute',
path: 'tab_tree/:rootTabId',
),
],
)
class BrowserRoute extends GoRouteData with _$BrowserRoute {
static const name = 'BrowserRoute';
@override
Widget build(BuildContext context, GoRouterState state) {
return const BrowserScreen();
}
}
class WebPageRoute extends GoRouteData with _$WebPageRoute {
final String url;
final WebPageInfo? $extra;
const WebPageRoute({required this.url, required this.$extra});
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
return DialogPage(
builder: (_) => WebPageDialog(url: Uri.parse(url), precachedInfo: $extra),
);
}
}
enum TabType { regular, private, child }
class SearchRoute extends GoRouteData with _$SearchRoute {
static const String emptySearchText = ' ';
final TabType tabType;
//This should be nullable but isnt allowed by go_router
final String searchText;
const SearchRoute({
required this.tabType,
this.searchText = SearchRoute.emptySearchText,
});
@override
Widget build(BuildContext context, GoRouterState state) {
return SearchScreen(
tabType: tabType,
initialSearchText: (searchText.isEmpty || searchText == emptySearchText)
? null
: searchText,
);
}
}
class TorProxyRoute extends GoRouteData with _$TorProxyRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const TorProxyScreen();
}
}
class ContainerListRoute extends GoRouteData with _$ContainerListRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const ContainerListScreen();
}
}
class ContainerEditRoute extends GoRouteData with _$ContainerEditRoute {
final ContainerData $extra;
ContainerEditRoute(this.$extra);
@override
Widget build(BuildContext context, GoRouterState state) {
return ContainerEditScreen.edit(initialContainer: $extra);
}
}
class ContainerCreateRoute extends GoRouteData with _$ContainerCreateRoute {
final ContainerData $extra;
ContainerCreateRoute(this.$extra);
@override
Widget build(BuildContext context, GoRouterState state) {
return ContainerEditScreen.create(initialContainer: $extra);
}
}
class ContextMenuRoute extends GoRouteData with _$ContextMenuRoute {
final String $extra;
const ContextMenuRoute(this.$extra);
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
return DialogPage(
builder: (_) =>
ContextMenuDialog(hitResult: HitResultJson.fromJson($extra)),
);
}
}
class TabTreeRoute extends GoRouteData with _$TabTreeRoute {
final String rootTabId;
const TabTreeRoute(this.rootTabId);
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
return DialogPage(builder: (_) => TabTreeDialog(rootTabId));
}
}
+51 -142
View File
@@ -1,160 +1,69 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:lensai/core/routing/dialog_page.dart';
import 'package:lensai/features/about/presentation/screens/about.dart';
import 'package:lensai/features/bangs/presentation/screens/categories.dart';
import 'package:lensai/features/bangs/presentation/screens/list.dart';
import 'package:lensai/features/bangs/presentation/screens/search.dart';
import 'package:lensai/features/chat_archive/presentation/screens/detail.dart';
import 'package:lensai/features/chat_archive/presentation/screens/list.dart';
import 'package:lensai/features/chat_archive/presentation/screens/search.dart';
import 'package:lensai/features/search_browser/presentation/screens/browser.dart';
import 'package:lensai/features/settings/presentation/screens/settings.dart';
import 'package:lensai/features/topics/presentation/screens/topic_list.dart';
import 'package:weblibre/core/routing/widgets/dialog_page.dart';
import 'package:weblibre/data/models/web_page_info.dart';
import 'package:weblibre/features/about/presentation/screens/about.dart';
import 'package:weblibre/features/bangs/presentation/screens/categories.dart';
import 'package:weblibre/features/bangs/presentation/screens/list.dart';
import 'package:weblibre/features/bangs/presentation/screens/search.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/tab_tree.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/web_page_dialog.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/screens/browser.dart';
import 'package:weblibre/features/geckoview/features/contextmenu/extensions/hit_result.dart';
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/context_menu_dialog.dart';
import 'package:weblibre/features/geckoview/features/search/presentation/screens/search.dart';
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_edit.dart';
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_list.dart';
import 'package:weblibre/features/onboarding/presentation/onboarding.dart';
import 'package:weblibre/features/settings/presentation/screens/bang_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/general_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/settings.dart';
import 'package:weblibre/features/settings/presentation/screens/web_engine_hardening.dart';
import 'package:weblibre/features/settings/presentation/screens/web_engine_hardening_group.dart';
import 'package:weblibre/features/settings/presentation/screens/web_engine_settings.dart';
import 'package:weblibre/features/tor/presentation/screens/tor_proxy.dart';
import 'package:weblibre/features/web_feed/presentation/add_feed_dialog.dart';
import 'package:weblibre/features/web_feed/presentation/screens/feed_article.dart';
import 'package:weblibre/features/web_feed/presentation/screens/feed_article_list.dart';
import 'package:weblibre/features/web_feed/presentation/screens/feed_edit.dart';
import 'package:weblibre/features/web_feed/presentation/screens/feed_list.dart';
import 'package:weblibre/features/web_feed/presentation/select_feed_dialog.dart';
part 'routes.g.dart';
part 'routes.settings.dart';
part 'routes.browser.dart';
part 'routes.bangs.dart';
part 'routes.feeds.dart';
@TypedGoRoute<KagiRoute>(
name: 'KagiRoute',
path: '/',
routes: [
TypedGoRoute<AboutRoute>(
name: 'AboutRoute',
path: 'about',
),
TypedGoRoute<BangCategoriesRoute>(
name: 'BangRoute',
path: 'bangs',
routes: [
TypedGoRoute<BangSearchRoute>(
name: 'BangSearchRoute',
path: 'search',
),
TypedGoRoute<BangCategoryRoute>(
name: 'BangCategoryRoute',
path: 'category/:category',
routes: [
TypedGoRoute<BangSubCategoryRoute>(
name: 'BangSubCategoryRoute',
path: ':subCategory',
),
],
),
],
),
TypedGoRoute<TopicListRoute>(
name: 'TopicsRoute',
path: 'topics',
),
],
)
class KagiRoute extends GoRouteData {
@override
Widget build(BuildContext context, GoRouterState state) {
return const KagiScreen();
}
}
class AboutRoute extends GoRouteData {
@TypedGoRoute<AboutRoute>(name: 'AboutRoute', path: '/about')
class AboutRoute extends GoRouteData with _$AboutRoute {
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
return DialogPage(builder: (_) => const AboutDialogScreen());
}
}
class BangCategoriesRoute extends GoRouteData {
@override
Widget build(BuildContext context, GoRouterState state) {
return const BangCategoriesScreen();
}
}
@TypedGoRoute<OnboardingRoute>(
name: 'OnboardingRoute',
path: '/onboarding/:currentRevision/:targetRevision',
)
class OnboardingRoute extends GoRouteData with _$OnboardingRoute {
final int currentRevision;
final int targetRevision;
class BangCategoryRoute extends GoRouteData {
final String category;
const BangCategoryRoute({required this.category});
@override
Widget build(BuildContext context, GoRouterState state) {
return BangListScreen(category: category);
}
}
class BangSubCategoryRoute extends GoRouteData {
final String category;
final String subCategory;
const BangSubCategoryRoute({
required this.category,
required this.subCategory,
const OnboardingRoute({
required this.currentRevision,
required this.targetRevision,
});
@override
Widget build(BuildContext context, GoRouterState state) {
return BangListScreen(category: category, subCategory: subCategory);
}
}
class BangSearchRoute extends GoRouteData {
@override
Widget build(BuildContext context, GoRouterState state) {
return const BangSearchScreen();
}
}
class TopicListRoute extends GoRouteData {
@override
Widget build(BuildContext context, GoRouterState state) {
return const TopicListScreen();
}
}
@TypedGoRoute<SettingsRoute>(
name: 'SettingsRoute',
path: '/settings',
)
class SettingsRoute extends GoRouteData {
@override
Widget build(BuildContext context, GoRouterState state) {
return const SettingsScreen();
}
}
@TypedGoRoute<ChatArchiveListRoute>(
name: 'ChatArchiveListRoute',
path: '/chat_archive',
routes: [
TypedGoRoute<ChatArchiveSearchRoute>(
name: 'ChatArchiveSearchRoute',
path: 'search',
),
TypedGoRoute<ChatArchiveDetailRoute>(
name: 'ChatArchiveDetailRoute',
path: 'detail/:fileName',
),
],
)
class ChatArchiveListRoute extends GoRouteData {
@override
Widget build(BuildContext context, GoRouterState state) {
return const ChatArchiveListScreen();
}
}
class ChatArchiveSearchRoute extends GoRouteData {
@override
Widget build(BuildContext context, GoRouterState state) {
return const ChatArchiveSearchScreen();
}
}
class ChatArchiveDetailRoute extends GoRouteData {
final String fileName;
const ChatArchiveDetailRoute({required this.fileName});
@override
Widget build(BuildContext context, GoRouterState state) {
return ChatArchiveDetailScreen(fileName);
return OnboardingScreen(
currentRevision: currentRevision,
targetRevision: targetRevision,
);
}
}
+106
View File
@@ -0,0 +1,106 @@
part of 'routes.dart';
@TypedGoRoute<FeedListRoute>(
name: 'FeedListRoute',
path: '/feeds',
routes: [
TypedGoRoute<FeedAddRoute>(name: FeedAddRoute.name, path: 'add'),
TypedGoRoute<FeedArticleListRoute>(
name: 'FeedArticleListRoute',
path: 'articles/:feedId',
),
TypedGoRoute<FeedArticleRoute>(
name: 'FeedArticleRoute',
path: 'article/:articleId',
),
TypedGoRoute<FeedCreateRoute>(
name: 'FeedCreateRoute',
path: 'create/:feedId',
),
TypedGoRoute<SelectFeedDialogRoute>(
name: 'SelectFeedDialogRoute',
path: 'available/:feedsJson',
),
TypedGoRoute<FeedEditRoute>(name: 'FeedEditRoute', path: 'edit/:feedId'),
],
)
class FeedListRoute extends GoRouteData with _$FeedListRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const FeedListScreen();
}
}
class FeedCreateRoute extends GoRouteData with _$FeedCreateRoute {
final Uri feedId;
FeedCreateRoute({required this.feedId});
@override
Widget build(BuildContext context, GoRouterState state) {
return FeedEditScreen.create(feedId: feedId);
}
}
class SelectFeedDialogRoute extends GoRouteData with _$SelectFeedDialogRoute {
final String feedsJson;
const SelectFeedDialogRoute({required this.feedsJson});
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
final feedUris = Set<Uri>.from(
(jsonDecode(feedsJson) as List<dynamic>).map(
(url) => Uri.parse(url as String),
),
);
return DialogPage(builder: (_) => SelectFeedDialog(feedUris: feedUris));
}
}
class FeedEditRoute extends GoRouteData with _$FeedEditRoute {
final Uri feedId;
const FeedEditRoute({required this.feedId});
@override
Widget build(BuildContext context, GoRouterState state) {
return FeedEditScreen.edit(feedId: feedId);
}
}
class FeedAddRoute extends GoRouteData with _$FeedAddRoute {
final Uri? $extra;
static const name = 'FeedAddRoute';
const FeedAddRoute({this.$extra});
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
return DialogPage(builder: (_) => AddFeedDialog(initialUri: $extra));
}
}
class FeedArticleListRoute extends GoRouteData with _$FeedArticleListRoute {
final Uri feedId;
FeedArticleListRoute({required this.feedId});
@override
Widget build(BuildContext context, GoRouterState state) {
return FeedArticleListScreen(feedId: feedId);
}
}
class FeedArticleRoute extends GoRouteData with _$FeedArticleRoute {
final String articleId;
FeedArticleRoute({required this.articleId});
@override
Widget build(BuildContext context, GoRouterState state) {
return FeedArticleScreen(articleId: articleId);
}
}
+715 -110
View File
@@ -7,272 +7,877 @@ part of 'routes.dart';
// **************************************************************************
List<RouteBase> get $appRoutes => [
$kagiRoute,
$settingsRoute,
$chatArchiveListRoute,
];
$aboutRoute,
$onboardingRoute,
$settingsRoute,
$browserRoute,
$bangCategoriesRoute,
$feedListRoute,
];
RouteBase get $kagiRoute => GoRouteData.$route(
path: '/',
name: 'KagiRoute',
factory: $KagiRouteExtension._fromState,
RouteBase get $aboutRoute => GoRouteData.$route(
path: '/about',
name: 'AboutRoute',
factory: _$AboutRoute._fromState,
);
mixin _$AboutRoute on GoRouteData {
static AboutRoute _fromState(GoRouterState state) => AboutRoute();
@override
String get location => GoRouteData.$location('/about');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
RouteBase get $onboardingRoute => GoRouteData.$route(
path: '/onboarding/:currentRevision/:targetRevision',
name: 'OnboardingRoute',
factory: _$OnboardingRoute._fromState,
);
mixin _$OnboardingRoute on GoRouteData {
static OnboardingRoute _fromState(GoRouterState state) => OnboardingRoute(
currentRevision: int.parse(state.pathParameters['currentRevision']!)!,
targetRevision: int.parse(state.pathParameters['targetRevision']!)!,
);
OnboardingRoute get _self => this as OnboardingRoute;
@override
String get location => GoRouteData.$location(
'/onboarding/${Uri.encodeComponent(_self.currentRevision.toString())}/${Uri.encodeComponent(_self.targetRevision.toString())}',
);
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
RouteBase get $settingsRoute => GoRouteData.$route(
path: '/settings',
name: 'SettingsRoute',
factory: _$SettingsRoute._fromState,
routes: [
GoRouteData.$route(
path: 'general',
name: 'GeneralSettingsRoute',
factory: _$GeneralSettingsRoute._fromState,
),
GoRouteData.$route(
path: 'bang',
name: 'BangSettingsRoute',
factory: _$BangSettingsRoute._fromState,
),
GoRouteData.$route(
path: 'web_engine',
name: 'WebEngineSettingsRoute',
factory: _$WebEngineSettingsRoute._fromState,
routes: [
GoRouteData.$route(
path: 'about',
name: 'AboutRoute',
factory: $AboutRouteExtension._fromState,
),
GoRouteData.$route(
path: 'bangs',
name: 'BangRoute',
factory: $BangCategoriesRouteExtension._fromState,
path: 'hardening',
name: 'WebEngineHardeningRoute',
factory: _$WebEngineHardeningRoute._fromState,
routes: [
GoRouteData.$route(
path: 'search',
name: 'BangSearchRoute',
factory: $BangSearchRouteExtension._fromState,
),
GoRouteData.$route(
path: 'category/:category',
name: 'BangCategoryRoute',
factory: $BangCategoryRouteExtension._fromState,
routes: [
GoRouteData.$route(
path: ':subCategory',
name: 'BangSubCategoryRoute',
factory: $BangSubCategoryRouteExtension._fromState,
),
],
path: 'group/:group',
name: 'WebEngineHardeningGroupRoute',
factory: _$WebEngineHardeningGroupRoute._fromState,
),
],
),
],
),
],
);
mixin _$SettingsRoute on GoRouteData {
static SettingsRoute _fromState(GoRouterState state) => SettingsRoute();
@override
String get location => GoRouteData.$location('/settings');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
mixin _$GeneralSettingsRoute on GoRouteData {
static GeneralSettingsRoute _fromState(GoRouterState state) =>
GeneralSettingsRoute();
@override
String get location => GoRouteData.$location('/settings/general');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
mixin _$BangSettingsRoute on GoRouteData {
static BangSettingsRoute _fromState(GoRouterState state) =>
BangSettingsRoute();
@override
String get location => GoRouteData.$location('/settings/bang');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
mixin _$WebEngineSettingsRoute on GoRouteData {
static WebEngineSettingsRoute _fromState(GoRouterState state) =>
WebEngineSettingsRoute();
@override
String get location => GoRouteData.$location('/settings/web_engine');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
mixin _$WebEngineHardeningRoute on GoRouteData {
static WebEngineHardeningRoute _fromState(GoRouterState state) =>
WebEngineHardeningRoute();
@override
String get location =>
GoRouteData.$location('/settings/web_engine/hardening');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
mixin _$WebEngineHardeningGroupRoute on GoRouteData {
static WebEngineHardeningGroupRoute _fromState(GoRouterState state) =>
WebEngineHardeningGroupRoute(group: state.pathParameters['group']!);
WebEngineHardeningGroupRoute get _self =>
this as WebEngineHardeningGroupRoute;
@override
String get location => GoRouteData.$location(
'/settings/web_engine/hardening/group/${Uri.encodeComponent(_self.group)}',
);
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
RouteBase get $browserRoute => GoRouteData.$route(
path: '/',
name: 'BrowserRoute',
factory: _$BrowserRoute._fromState,
routes: [
GoRouteData.$route(
path: 'page/:url',
name: 'WebPageRoute',
factory: _$WebPageRoute._fromState,
),
GoRouteData.$route(
path: 'search/:tabType/:searchText',
name: 'SearchRoute',
factory: _$SearchRoute._fromState,
),
GoRouteData.$route(
path: 'tor_proxy',
name: 'TorProxyRoute',
factory: _$TorProxyRoute._fromState,
),
GoRouteData.$route(
path: 'context_menu',
name: 'ContextMenuRoute',
factory: _$ContextMenuRoute._fromState,
),
GoRouteData.$route(
path: 'containers',
name: 'ContainerListRoute',
factory: _$ContainerListRoute._fromState,
routes: [
GoRouteData.$route(
path: 'topics',
name: 'TopicsRoute',
factory: $TopicListRouteExtension._fromState,
path: 'create',
name: 'ContainerCreateRoute',
factory: _$ContainerCreateRoute._fromState,
),
GoRouteData.$route(
path: 'edit',
name: 'ContainerEditRoute',
factory: _$ContainerEditRoute._fromState,
),
],
);
),
GoRouteData.$route(
path: 'tab_tree/:rootTabId',
name: 'TabTreeRoute',
extension $KagiRouteExtension on KagiRoute {
static KagiRoute _fromState(GoRouterState state) => KagiRoute();
factory: _$TabTreeRoute._fromState,
),
],
);
String get location => GoRouteData.$location(
'/',
);
mixin _$BrowserRoute on GoRouteData {
static BrowserRoute _fromState(GoRouterState state) => BrowserRoute();
@override
String get location => GoRouteData.$location('/');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
extension $AboutRouteExtension on AboutRoute {
static AboutRoute _fromState(GoRouterState state) => AboutRoute();
mixin _$WebPageRoute on GoRouteData {
static WebPageRoute _fromState(GoRouterState state) => WebPageRoute(
url: state.pathParameters['url']!,
$extra: state.extra as WebPageInfo?,
);
WebPageRoute get _self => this as WebPageRoute;
@override
String get location =>
GoRouteData.$location('/page/${Uri.encodeComponent(_self.url)}');
@override
void go(BuildContext context) => context.go(location, extra: _self.$extra);
@override
Future<T?> push<T>(BuildContext context) =>
context.push<T>(location, extra: _self.$extra);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location, extra: _self.$extra);
@override
void replace(BuildContext context) =>
context.replace(location, extra: _self.$extra);
}
mixin _$SearchRoute on GoRouteData {
static SearchRoute _fromState(GoRouterState state) => SearchRoute(
tabType: _$TabTypeEnumMap._$fromName(state.pathParameters['tabType']!)!,
searchText:
state.pathParameters['searchText'] ?? SearchRoute.emptySearchText,
);
SearchRoute get _self => this as SearchRoute;
@override
String get location => GoRouteData.$location(
'/about',
);
'/search/${Uri.encodeComponent(_$TabTypeEnumMap[_self.tabType]!)}/${Uri.encodeComponent(_self.searchText)}',
);
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
extension $BangCategoriesRouteExtension on BangCategoriesRoute {
const _$TabTypeEnumMap = {
TabType.regular: 'regular',
TabType.private: 'private',
TabType.child: 'child',
};
mixin _$TorProxyRoute on GoRouteData {
static TorProxyRoute _fromState(GoRouterState state) => TorProxyRoute();
@override
String get location => GoRouteData.$location('/tor_proxy');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
mixin _$ContextMenuRoute on GoRouteData {
static ContextMenuRoute _fromState(GoRouterState state) =>
ContextMenuRoute(state.extra as String);
ContextMenuRoute get _self => this as ContextMenuRoute;
@override
String get location => GoRouteData.$location('/context_menu');
@override
void go(BuildContext context) => context.go(location, extra: _self.$extra);
@override
Future<T?> push<T>(BuildContext context) =>
context.push<T>(location, extra: _self.$extra);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location, extra: _self.$extra);
@override
void replace(BuildContext context) =>
context.replace(location, extra: _self.$extra);
}
mixin _$ContainerListRoute on GoRouteData {
static ContainerListRoute _fromState(GoRouterState state) =>
ContainerListRoute();
@override
String get location => GoRouteData.$location('/containers');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
mixin _$ContainerCreateRoute on GoRouteData {
static ContainerCreateRoute _fromState(GoRouterState state) =>
ContainerCreateRoute(state.extra as ContainerData);
ContainerCreateRoute get _self => this as ContainerCreateRoute;
@override
String get location => GoRouteData.$location('/containers/create');
@override
void go(BuildContext context) => context.go(location, extra: _self.$extra);
@override
Future<T?> push<T>(BuildContext context) =>
context.push<T>(location, extra: _self.$extra);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location, extra: _self.$extra);
@override
void replace(BuildContext context) =>
context.replace(location, extra: _self.$extra);
}
mixin _$ContainerEditRoute on GoRouteData {
static ContainerEditRoute _fromState(GoRouterState state) =>
ContainerEditRoute(state.extra as ContainerData);
ContainerEditRoute get _self => this as ContainerEditRoute;
@override
String get location => GoRouteData.$location('/containers/edit');
@override
void go(BuildContext context) => context.go(location, extra: _self.$extra);
@override
Future<T?> push<T>(BuildContext context) =>
context.push<T>(location, extra: _self.$extra);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location, extra: _self.$extra);
@override
void replace(BuildContext context) =>
context.replace(location, extra: _self.$extra);
}
mixin _$TabTreeRoute on GoRouteData {
static TabTreeRoute _fromState(GoRouterState state) =>
TabTreeRoute(state.pathParameters['rootTabId']!);
TabTreeRoute get _self => this as TabTreeRoute;
@override
String get location => GoRouteData.$location(
'/tab_tree/${Uri.encodeComponent(_self.rootTabId)}',
);
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
extension<T extends Enum> on Map<T, String> {
T? _$fromName(String? value) =>
entries.where((element) => element.value == value).firstOrNull?.key;
}
RouteBase get $bangCategoriesRoute => GoRouteData.$route(
path: '/bangs',
name: 'BangRoute',
factory: _$BangCategoriesRoute._fromState,
routes: [
GoRouteData.$route(
path: 'search/:searchText',
name: 'BangSearchRoute',
factory: _$BangSearchRoute._fromState,
),
GoRouteData.$route(
path: 'category/:category',
name: 'BangCategoryRoute',
factory: _$BangCategoryRoute._fromState,
routes: [
GoRouteData.$route(
path: ':subCategory',
name: 'BangSubCategoryRoute',
factory: _$BangSubCategoryRoute._fromState,
),
],
),
],
);
mixin _$BangCategoriesRoute on GoRouteData {
static BangCategoriesRoute _fromState(GoRouterState state) =>
BangCategoriesRoute();
String get location => GoRouteData.$location(
'/bangs',
);
@override
String get location => GoRouteData.$location('/bangs');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
extension $BangSearchRouteExtension on BangSearchRoute {
static BangSearchRoute _fromState(GoRouterState state) => BangSearchRoute();
mixin _$BangSearchRoute on GoRouteData {
static BangSearchRoute _fromState(GoRouterState state) => BangSearchRoute(
searchText:
state.pathParameters['searchText'] ?? BangSearchRoute.emptySearchText,
);
BangSearchRoute get _self => this as BangSearchRoute;
@override
String get location => GoRouteData.$location(
'/bangs/search',
);
'/bangs/search/${Uri.encodeComponent(_self.searchText)}',
);
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
extension $BangCategoryRouteExtension on BangCategoryRoute {
static BangCategoryRoute _fromState(GoRouterState state) => BangCategoryRoute(
category: state.pathParameters['category']!,
);
mixin _$BangCategoryRoute on GoRouteData {
static BangCategoryRoute _fromState(GoRouterState state) =>
BangCategoryRoute(category: state.pathParameters['category']!);
BangCategoryRoute get _self => this as BangCategoryRoute;
@override
String get location => GoRouteData.$location(
'/bangs/category/${Uri.encodeComponent(category)}',
);
'/bangs/category/${Uri.encodeComponent(_self.category)}',
);
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
extension $BangSubCategoryRouteExtension on BangSubCategoryRoute {
mixin _$BangSubCategoryRoute on GoRouteData {
static BangSubCategoryRoute _fromState(GoRouterState state) =>
BangSubCategoryRoute(
category: state.pathParameters['category']!,
subCategory: state.pathParameters['subCategory']!,
);
String get location => GoRouteData.$location(
'/bangs/category/${Uri.encodeComponent(category)}/${Uri.encodeComponent(subCategory)}',
);
BangSubCategoryRoute get _self => this as BangSubCategoryRoute;
@override
String get location => GoRouteData.$location(
'/bangs/category/${Uri.encodeComponent(_self.category)}/${Uri.encodeComponent(_self.subCategory)}',
);
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
extension $TopicListRouteExtension on TopicListRoute {
static TopicListRoute _fromState(GoRouterState state) => TopicListRoute();
RouteBase get $feedListRoute => GoRouteData.$route(
path: '/feeds',
name: 'FeedListRoute',
String get location => GoRouteData.$location(
'/topics',
);
factory: _$FeedListRoute._fromState,
routes: [
GoRouteData.$route(
path: 'add',
name: 'FeedAddRoute',
factory: _$FeedAddRoute._fromState,
),
GoRouteData.$route(
path: 'articles/:feedId',
name: 'FeedArticleListRoute',
factory: _$FeedArticleListRoute._fromState,
),
GoRouteData.$route(
path: 'article/:articleId',
name: 'FeedArticleRoute',
factory: _$FeedArticleRoute._fromState,
),
GoRouteData.$route(
path: 'create/:feedId',
name: 'FeedCreateRoute',
factory: _$FeedCreateRoute._fromState,
),
GoRouteData.$route(
path: 'available/:feedsJson',
name: 'SelectFeedDialogRoute',
factory: _$SelectFeedDialogRoute._fromState,
),
GoRouteData.$route(
path: 'edit/:feedId',
name: 'FeedEditRoute',
factory: _$FeedEditRoute._fromState,
),
],
);
mixin _$FeedListRoute on GoRouteData {
static FeedListRoute _fromState(GoRouterState state) => FeedListRoute();
@override
String get location => GoRouteData.$location('/feeds');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
RouteBase get $settingsRoute => GoRouteData.$route(
path: '/settings',
name: 'SettingsRoute',
factory: $SettingsRouteExtension._fromState,
);
mixin _$FeedAddRoute on GoRouteData {
static FeedAddRoute _fromState(GoRouterState state) =>
FeedAddRoute($extra: state.extra as Uri?);
extension $SettingsRouteExtension on SettingsRoute {
static SettingsRoute _fromState(GoRouterState state) => SettingsRoute();
FeedAddRoute get _self => this as FeedAddRoute;
@override
String get location => GoRouteData.$location('/feeds/add');
@override
void go(BuildContext context) => context.go(location, extra: _self.$extra);
@override
Future<T?> push<T>(BuildContext context) =>
context.push<T>(location, extra: _self.$extra);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location, extra: _self.$extra);
@override
void replace(BuildContext context) =>
context.replace(location, extra: _self.$extra);
}
mixin _$FeedArticleListRoute on GoRouteData {
static FeedArticleListRoute _fromState(GoRouterState state) =>
FeedArticleListRoute(feedId: Uri.parse(state.pathParameters['feedId']!)!);
FeedArticleListRoute get _self => this as FeedArticleListRoute;
@override
String get location => GoRouteData.$location(
'/settings',
);
'/feeds/articles/${Uri.encodeComponent(_self.feedId.toString())}',
);
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
RouteBase get $chatArchiveListRoute => GoRouteData.$route(
path: '/chat_archive',
name: 'ChatArchiveListRoute',
factory: $ChatArchiveListRouteExtension._fromState,
routes: [
GoRouteData.$route(
path: 'search',
name: 'ChatArchiveSearchRoute',
factory: $ChatArchiveSearchRouteExtension._fromState,
),
GoRouteData.$route(
path: 'detail/:fileName',
name: 'ChatArchiveDetailRoute',
factory: $ChatArchiveDetailRouteExtension._fromState,
),
],
);
mixin _$FeedArticleRoute on GoRouteData {
static FeedArticleRoute _fromState(GoRouterState state) =>
FeedArticleRoute(articleId: state.pathParameters['articleId']!);
extension $ChatArchiveListRouteExtension on ChatArchiveListRoute {
static ChatArchiveListRoute _fromState(GoRouterState state) =>
ChatArchiveListRoute();
FeedArticleRoute get _self => this as FeedArticleRoute;
@override
String get location => GoRouteData.$location(
'/chat_archive',
);
'/feeds/article/${Uri.encodeComponent(_self.articleId)}',
);
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
extension $ChatArchiveSearchRouteExtension on ChatArchiveSearchRoute {
static ChatArchiveSearchRoute _fromState(GoRouterState state) =>
ChatArchiveSearchRoute();
mixin _$FeedCreateRoute on GoRouteData {
static FeedCreateRoute _fromState(GoRouterState state) =>
FeedCreateRoute(feedId: Uri.parse(state.pathParameters['feedId']!)!);
FeedCreateRoute get _self => this as FeedCreateRoute;
@override
String get location => GoRouteData.$location(
'/chat_archive/search',
);
'/feeds/create/${Uri.encodeComponent(_self.feedId.toString())}',
);
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
extension $ChatArchiveDetailRouteExtension on ChatArchiveDetailRoute {
static ChatArchiveDetailRoute _fromState(GoRouterState state) =>
ChatArchiveDetailRoute(
fileName: state.pathParameters['fileName']!,
);
mixin _$SelectFeedDialogRoute on GoRouteData {
static SelectFeedDialogRoute _fromState(GoRouterState state) =>
SelectFeedDialogRoute(feedsJson: state.pathParameters['feedsJson']!);
SelectFeedDialogRoute get _self => this as SelectFeedDialogRoute;
@override
String get location => GoRouteData.$location(
'/chat_archive/detail/${Uri.encodeComponent(fileName)}',
);
'/feeds/available/${Uri.encodeComponent(_self.feedsJson)}',
);
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
mixin _$FeedEditRoute on GoRouteData {
static FeedEditRoute _fromState(GoRouterState state) =>
FeedEditRoute(feedId: Uri.parse(state.pathParameters['feedId']!)!);
FeedEditRoute get _self => this as FeedEditRoute;
@override
String get location => GoRouteData.$location(
'/feeds/edit/${Uri.encodeComponent(_self.feedId.toString())}',
);
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
+76
View File
@@ -0,0 +1,76 @@
part of 'routes.dart';
@TypedGoRoute<SettingsRoute>(
name: 'SettingsRoute',
path: '/settings',
routes: [
TypedGoRoute<GeneralSettingsRoute>(
name: 'GeneralSettingsRoute',
path: 'general',
),
TypedGoRoute<BangSettingsRoute>(name: 'BangSettingsRoute', path: 'bang'),
TypedGoRoute<WebEngineSettingsRoute>(
name: 'WebEngineSettingsRoute',
path: 'web_engine',
routes: [
TypedGoRoute<WebEngineHardeningRoute>(
name: 'WebEngineHardeningRoute',
path: 'hardening',
routes: [
TypedGoRoute<WebEngineHardeningGroupRoute>(
name: 'WebEngineHardeningGroupRoute',
path: 'group/:group',
),
],
),
],
),
],
)
class SettingsRoute extends GoRouteData with _$SettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const SettingsScreen();
}
}
class GeneralSettingsRoute extends GoRouteData with _$GeneralSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const GeneralSettingsScreen();
}
}
class BangSettingsRoute extends GoRouteData with _$BangSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const BangSettingsScreen();
}
}
class WebEngineSettingsRoute extends GoRouteData with _$WebEngineSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const WebEngineSettingsScreen();
}
}
class WebEngineHardeningRoute extends GoRouteData
with _$WebEngineHardeningRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const WebEngineHardeningScreen();
}
}
class WebEngineHardeningGroupRoute extends GoRouteData
with _$WebEngineHardeningGroupRoute {
final String group;
const WebEngineHardeningGroupRoute({required this.group});
@override
Widget build(BuildContext context, GoRouterState state) {
return WebEngineHardeningGroupScreen(groupName: group);
}
}
@@ -27,14 +27,14 @@ class DialogPage<T> extends Page<T> {
@override
Route<T> createRoute(BuildContext context) => DialogRoute<T>(
context: context,
settings: this,
builder: builder,
anchorPoint: anchorPoint,
barrierColor: barrierColor,
barrierDismissible: barrierDismissible,
barrierLabel: barrierLabel,
useSafeArea: useSafeArea,
themes: themes,
);
context: context,
settings: this,
builder: builder,
anchorPoint: anchorPoint,
barrierColor: barrierColor,
barrierDismissible: barrierDismissible,
barrierLabel: barrierLabel,
useSafeArea: useSafeArea,
themes: themes,
);
}
@@ -0,0 +1,32 @@
import 'dart:ui';
import 'package:drift/drift.dart';
import 'package:json_annotation/json_annotation.dart';
class ColorJsonConverter implements JsonConverter<Color, int> {
const ColorJsonConverter();
@override
Color fromJson(int from) {
return Color(from);
}
@override
int toJson(Color value) {
return value.toARGB32();
}
}
class ColorConverter extends TypeConverter<Color, int> {
const ColorConverter();
@override
Color fromSql(int fromDb) {
return Color(fromDb);
}
@override
int toSql(Color value) {
return value.toARGB32();
}
}
@@ -0,0 +1,48 @@
import 'dart:convert';
import 'package:drift/drift.dart';
import 'package:flutter/widgets.dart';
import 'package:json_annotation/json_annotation.dart';
class IconDataJsonConverter
implements JsonConverter<IconData, Map<String, dynamic>> {
const IconDataJsonConverter();
@override
IconData fromJson(Map<String, dynamic> json) {
return IconData(
json['codePoint'] as int,
fontFamily: json['fontFamily'] as String,
fontPackage: json['fontPackage'] as String,
);
}
@override
Map<String, dynamic> toJson(IconData iconData) {
return <String, dynamic>{
'codePoint': iconData.codePoint,
'fontFamily': iconData.fontFamily,
'fontPackage': iconData.fontPackage,
};
}
}
class IconDataTypeConverter implements TypeConverter<IconData, String> {
const IconDataTypeConverter();
@override
IconData fromSql(String fromDb) {
final json = jsonDecode(fromDb) as Map<String, dynamic>;
return const IconDataJsonConverter().fromJson(json);
}
@override
String toSql(IconData value) {
assert(
value.fontFamily != null,
'Font family must be provided to identify icon',
);
return jsonEncode(const IconDataJsonConverter().toJson(value));
}
}
@@ -0,0 +1,17 @@
import 'dart:convert';
import 'package:drift/drift.dart';
class StringListConverter extends TypeConverter<List<String>, String> {
const StringListConverter();
@override
List<String> fromSql(String fromDb) {
return jsonDecode(fromDb) as List<String>;
}
@override
String toSql(List<String> value) {
return jsonEncode(value);
}
}
+30
View File
@@ -0,0 +1,30 @@
import 'package:drift/drift.dart';
import 'package:weblibre/utils/uri_parser.dart' as uri_parser;
class UriConverter extends TypeConverter<Uri, String> {
const UriConverter();
@override
Uri fromSql(String fromDb) {
return uri_parser.tryParseUrl(fromDb, eagerParsing: true)!;
}
@override
String toSql(Uri value) {
return value.toString();
}
}
class UriConverterNullable extends TypeConverter<Uri?, String?> {
const UriConverterNullable();
@override
Uri? fromSql(String? fromDb) {
return uri_parser.tryParseUrl(fromDb, eagerParsing: true);
}
@override
String? toSql(Uri? value) {
return value?.toString();
}
}
@@ -1,6 +1,6 @@
import 'package:drift/drift.dart';
extension TableSIze on DatabaseConnectionUser {
extension TableSize on DatabaseConnectionUser {
SingleSelectable<double> tableSize(TableInfo table) {
return customSelect(
'SELECT SUM(pgsize) /(1024.0 * 1024.0)AS pgsize_mb FROM dbstat WHERE name = ?1',
@@ -0,0 +1,59 @@
import 'package:lexo_rank/lexo_rank.dart';
import 'package:lexo_rank/lexo_rank/lexo_rank_bucket.dart';
import 'package:nullability/nullability.dart';
import 'package:sqlite3/common.dart';
String _nextRankOrMiddle(List<Object?> args) {
final parsedBucket = LexoRankBucket.resolve(args[0]! as int);
if (args[1] != null) {
final parsedRank = LexoRank.parse(args[1]! as String);
return parsedRank.genNext().value;
} else {
return LexoRank.middle(bucket: parsedBucket).value;
}
}
String _previousRankOrMiddle(List<Object?> args) {
final parsedBucket = LexoRankBucket.resolve(args[0]! as int);
if (args[1] != null) {
final parsedRank = LexoRank.parse(args[1]! as String);
return parsedRank.genPrev().value;
} else {
return LexoRank.middle(bucket: parsedBucket).value;
}
}
String _reorderAfter(List<Object?> args) {
final first = args[0].mapNotNull((arg) => LexoRank.parse(arg as String));
final last = args[1].mapNotNull((arg) => LexoRank.parse(arg as String));
if (first == null) {
throw Exception('Tab not found');
} else if (last == null) {
return first.genNext().value;
} else {
return first.genBetween(last).value;
}
}
void registerLexorankFunctions(CommonDatabase database) {
database.createFunction(
functionName: 'lexo_rank_next',
argumentCount: const AllowedArgumentCount(2),
function: _nextRankOrMiddle,
);
database.createFunction(
functionName: 'lexo_rank_previous',
argumentCount: const AllowedArgumentCount(2),
function: _previousRankOrMiddle,
);
database.createFunction(
functionName: 'lexo_rank_reorder_after',
argumentCount: const AllowedArgumentCount(2),
function: _reorderAfter,
);
}
+32
View File
@@ -0,0 +1,32 @@
import 'package:fast_equatable/fast_equatable.dart';
sealed class DropTargetData with FastEquatable {}
final class ContainerDropData extends DropTargetData {
final String tabId;
ContainerDropData(this.tabId);
@override
List<Object?> get hashParameters => [tabId];
}
final class DeleteDropData extends DropTargetData {
final String tabId;
DeleteDropData(this.tabId);
@override
List<Object?> get hashParameters => [tabId];
}
sealed class DragTargetData with FastEquatable {}
final class TabDragData extends DragTargetData {
final String tabId;
TabDragData(this.tabId);
@override
List<Object?> get hashParameters => [tabId];
}
@@ -0,0 +1,11 @@
import 'package:fast_equatable/fast_equatable.dart';
class ReceivedIntentParameter with FastEquatable {
final String? content;
final String? tool;
ReceivedIntentParameter(this.content, this.tool);
@override
List<Object?> get hashParameters => [content, tool];
}
+22
View File
@@ -0,0 +1,22 @@
import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:fast_equatable/fast_equatable.dart';
import 'package:nullability/nullability.dart';
import 'package:weblibre/features/geckoview/domain/entities/browser_icon.dart';
part 'web_page_info.g.dart';
@CopyWith()
class WebPageInfo with FastEquatable {
final Uri url;
final String? title;
final BrowserIcon? favicon;
final Set<Uri>? feeds;
bool get isPageInfoComplete =>
title.isNotEmpty && favicon != null && feeds != null;
WebPageInfo({required this.url, this.title, this.favicon, this.feeds});
@override
List<Object?> get hashParameters => [url, title, favicon, feeds];
}
+88
View File
@@ -0,0 +1,88 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'web_page_info.dart';
// **************************************************************************
// CopyWithGenerator
// **************************************************************************
abstract class _$WebPageInfoCWProxy {
WebPageInfo url(Uri url);
WebPageInfo title(String? title);
WebPageInfo favicon(BrowserIcon? favicon);
WebPageInfo feeds(Set<Uri>? feeds);
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `WebPageInfo(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
///
/// Usage
/// ```dart
/// WebPageInfo(...).copyWith(id: 12, name: "My name")
/// ````
WebPageInfo call({
Uri url,
String? title,
BrowserIcon? favicon,
Set<Uri>? feeds,
});
}
/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfWebPageInfo.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfWebPageInfo.copyWith.fieldName(...)`
class _$WebPageInfoCWProxyImpl implements _$WebPageInfoCWProxy {
const _$WebPageInfoCWProxyImpl(this._value);
final WebPageInfo _value;
@override
WebPageInfo url(Uri url) => this(url: url);
@override
WebPageInfo title(String? title) => this(title: title);
@override
WebPageInfo favicon(BrowserIcon? favicon) => this(favicon: favicon);
@override
WebPageInfo feeds(Set<Uri>? feeds) => this(feeds: feeds);
@override
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `WebPageInfo(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
///
/// Usage
/// ```dart
/// WebPageInfo(...).copyWith(id: 12, name: "My name")
/// ````
WebPageInfo call({
Object? url = const $CopyWithPlaceholder(),
Object? title = const $CopyWithPlaceholder(),
Object? favicon = const $CopyWithPlaceholder(),
Object? feeds = const $CopyWithPlaceholder(),
}) {
return WebPageInfo(
url: url == const $CopyWithPlaceholder()
? _value.url
// ignore: cast_nullable_to_non_nullable
: url as Uri,
title: title == const $CopyWithPlaceholder()
? _value.title
// ignore: cast_nullable_to_non_nullable
: title as String?,
favicon: favicon == const $CopyWithPlaceholder()
? _value.favicon
// ignore: cast_nullable_to_non_nullable
: favicon as BrowserIcon?,
feeds: feeds == const $CopyWithPlaceholder()
? _value.feeds
// ignore: cast_nullable_to_non_nullable
: feeds as Set<Uri>?,
);
}
}
extension $WebPageInfoCopyWith on WebPageInfo {
/// Returns a callable class that can be used as follows: `instanceOfWebPageInfo.copyWith(...)` or like so:`instanceOfWebPageInfo.copyWith.fieldName(...)`.
// ignore: library_private_types_in_public_api
_$WebPageInfoCWProxy get copyWith => _$WebPageInfoCWProxyImpl(this);
}
@@ -0,0 +1,16 @@
import 'dart:ui';
class EquatableImage {
final Image value;
final int _imageHash;
EquatableImage(this.value, {required int hash}) : _imageHash = hash;
@override
int get hashCode => _imageHash.hashCode;
@override
bool operator ==(Object other) {
return other is EquatableImage && other._imageHash == _imageHash;
}
}
@@ -1,6 +0,0 @@
class ReceivedParameter {
final String? content;
final String? tool;
ReceivedParameter(this.content, this.tool);
}

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