Add proxy routing and sing-box support

This commit is contained in:
Fabian Freund
2026-05-22 18:16:31 +02:00
parent 51289f1266
commit a5974617aa
262 changed files with 32003 additions and 3962 deletions
@@ -3,7 +3,7 @@ import 'dart:async';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'
as fmc;
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:search_backend/search_backend.dart';
import 'package:search_protocol/search_protocol.dart';
import 'package:search_client/search_client.dart';
import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/features/geckoview/domain/entities/states/tab.dart';
@@ -4,7 +4,7 @@ import 'dart:typed_data';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:search_backend/search_backend.dart';
import 'package:search_protocol/search_protocol.dart';
import 'package:search_client/search_client.dart';
import 'package:weblibre/domain/services/generic_website.dart';
import 'package:weblibre/features/search_credits/domain/controllers/search_token_issuance_controller.dart';
@@ -12,6 +12,7 @@ import 'package:weblibre/features/search_credits/domain/providers.dart';
import 'package:weblibre/features/search_credits/domain/providers/proxy_client.dart';
import 'package:weblibre/features/search_credits/domain/repositories/search_token_stash_repository.dart';
import 'package:weblibre/features/search_credits/domain/repositories/web_search_settings.dart';
import 'package:weblibre/features/tor/domain/extensions/tor_status_x.dart';
import 'package:weblibre/features/tor/domain/services/tor_proxy.dart';
import 'package:weblibre/features/tor/presentation/controllers/start_tor_proxy.dart';
import 'package:weblibre/features/web_search/domain/entities/captured_page_state.dart';
@@ -687,10 +688,7 @@ class MetaSearchController extends _$MetaSearchController {
final completer = Completer<bool>();
final subscription = ref.listen(torProxyServiceProvider, (previous, next) {
final status = next.value;
if (status != null &&
status.isRunning &&
status.bootstrapProgress >= 100) {
if (next.isReady) {
if (!completer.isCompleted) completer.complete(true);
}
});
@@ -42,7 +42,7 @@ final class MetaSearchControllerProvider
}
String _$metaSearchControllerHash() =>
r'b040527dfb49d9407fe0ea22169f5961d1faef13';
r'afb6600f78717df124a6afdb80710eba74666d23';
abstract class _$MetaSearchController extends $Notifier<MetaSearchState> {
MetaSearchState build();
@@ -5,7 +5,7 @@ import 'dart:ui';
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as p;
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:search_backend/search_backend.dart';
import 'package:search_protocol/search_protocol.dart';
import 'package:search_client/search_client.dart';
import 'package:weblibre/core/filesystem.dart';
import 'package:weblibre/features/search_credits/domain/providers.dart';
@@ -125,7 +125,9 @@ class _MethodTile extends ConsumerWidget {
failed: failed,
colorScheme: colorScheme,
),
onTap: busy ? null : () => _handleTap(context, ref, ready, failed, captured),
onTap: busy
? null
: () => _handleTap(context, ref, ready, failed, captured),
);
}
@@ -200,9 +202,6 @@ class _StatusIndicator extends StatelessWidget {
if (failed) {
return Icon(Icons.refresh, color: colorScheme.error);
}
return Icon(
Icons.download_rounded,
color: colorScheme.onSurfaceVariant,
);
return Icon(Icons.download_rounded, color: colorScheme.onSurfaceVariant);
}
}
@@ -3,7 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:intl/intl.dart';
import 'package:nullability/nullability.dart';
import 'package:search_backend/search_backend.dart';
import 'package:search_protocol/search_protocol.dart';
class PageMetadataChips extends HookWidget {
final PageMetadata metadata;
@@ -22,6 +22,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/core/design/app_colors.dart';
import 'package:weblibre/features/search_credits/domain/providers/proxy_client.dart';
import 'package:weblibre/features/search_credits/domain/repositories/web_search_settings.dart';
import 'package:weblibre/features/tor/domain/extensions/tor_status_x.dart';
import 'package:weblibre/features/tor/domain/services/tor_proxy.dart';
import 'package:weblibre/features/tor/presentation/controllers/start_tor_proxy.dart';
import 'package:weblibre/presentation/hooks/on_initialization.dart';
@@ -55,10 +56,9 @@ class RouteThroughTorToggle extends HookConsumerWidget {
final torStatus = ref.watch(torProxyServiceProvider).value;
final activePort = ref.watch(searchProxyPortProvider);
final bootstrapProgress = torStatus?.bootstrapProgress ?? 0;
final showSpinner =
routeThroughTor && (activePort == null || bootstrapProgress < 100);
routeThroughTor &&
(activePort == null || !(torStatus?.isReady ?? false));
// Push the latest native status into the stream on first build so the
// bar reflects real progress even when the user lands on the search
@@ -161,14 +161,13 @@ class WebSearchTorBootstrapProgress extends HookConsumerWidget {
final torAsync = ref.watch(torProxyServiceProvider);
final status = torAsync.value;
final isRunning = status?.isRunning ?? false;
final bootstrapProgress = status?.bootstrapProgress ?? 0;
// Hide once Tor is fully running and bootstrapped — otherwise, mirror
// the Tor settings screen and always show a determinate bar (value
// anchored to the live bootstrap progress, never indeterminate, so the
// user can actually track the percentage).
if (isRunning && bootstrapProgress >= 100) return const SizedBox.shrink();
if (status?.isReady ?? false) return const SizedBox.shrink();
final appColors = AppColors.of(context);
return Padding(
@@ -19,7 +19,7 @@
*/
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:search_backend/search_backend.dart';
import 'package:search_protocol/search_protocol.dart';
import 'package:weblibre/features/search_credits/domain/repositories/web_search_settings.dart';
import 'package:weblibre/features/web_search/data/locale_options.dart';
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:search_backend/search_backend.dart';
import 'package:search_protocol/search_protocol.dart';
import 'package:weblibre/features/search_credits/domain/repositories/web_search_settings.dart';
class SearchModeSelector extends ConsumerWidget {
@@ -6,7 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:intl/intl.dart';
import 'package:nullability/nullability.dart';
import 'package:search_backend/search_backend.dart';
import 'package:search_protocol/search_protocol.dart';
import 'package:weblibre/features/search_credits/domain/repositories/web_search_settings.dart';
import 'package:weblibre/features/web_search/domain/controllers/search_controller.dart';
import 'package:weblibre/features/web_search/domain/entities/captured_page_state.dart';
@@ -4,7 +4,7 @@ import 'package:flutter_material_design_icons/flutter_material_design_icons.dart
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:intl/intl.dart';
import 'package:intl/locale.dart' as intl;
import 'package:search_backend/search_backend.dart';
import 'package:search_protocol/search_protocol.dart';
import 'package:weblibre/domain/repositories/locale_resolver.dart';
const _expandableKeys = {'snippet', 'review', 'question'};
@@ -3,7 +3,7 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:search_backend/search_backend.dart';
import 'package:search_protocol/search_protocol.dart';
import 'package:weblibre/features/web_search/domain/controllers/search_controller.dart';
import 'package:weblibre/presentation/widgets/uri_breadcrumb.dart';
import 'package:weblibre/presentation/widgets/url_icon.dart';