From 9599ec32369beba5ead227558bc30e23f6b4e5f0 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 31 Mar 2025 15:34:48 +0530 Subject: [PATCH 001/156] dart format --- mobile/integration_test/app_init_test.dart | 7 +- mobile/lib/core/cache/image_cache.dart | 1 - mobile/lib/core/errors.dart | 2 +- .../generated/protos/ente/common/box.pb.dart | 60 +++++---- .../protos/ente/common/box.pbenum.dart | 1 - .../protos/ente/common/box.pbjson.dart | 1 - .../protos/ente/common/box.pbserver.dart | 1 - .../protos/ente/common/point.pb.dart | 50 +++++--- .../protos/ente/common/point.pbenum.dart | 1 - .../protos/ente/common/point.pbjson.dart | 1 - .../protos/ente/common/point.pbserver.dart | 1 - .../protos/ente/common/vector.pb.dart | 40 +++--- .../protos/ente/common/vector.pbenum.dart | 1 - .../protos/ente/common/vector.pbjson.dart | 5 +- .../protos/ente/common/vector.pbserver.dart | 1 - .../lib/generated/protos/ente/ml/face.pb.dart | 113 ++++++++++------- .../generated/protos/ente/ml/face.pbenum.dart | 1 - .../generated/protos/ente/ml/face.pbjson.dart | 44 ++++++- .../protos/ente/ml/face.pbserver.dart | 1 - .../generated/protos/ente/ml/fileml.pb.dart | 104 ++++++++++------ .../protos/ente/ml/fileml.pbenum.dart | 1 - .../protos/ente/ml/fileml.pbjson.dart | 20 ++- .../protos/ente/ml/fileml.pbserver.dart | 1 - mobile/lib/models/api/user/srp.dart | 2 +- .../face_clustering_service.dart | 3 +- .../face_detection_service.dart | 6 +- .../machine_learning/ml_constants.dart | 2 +- .../machine_learning/ml_exceptions.dart | 3 +- .../semantic_search/query_result.dart | 2 +- mobile/lib/services/magic_cache_service.dart | 6 +- .../lib/services/memories_cache_service.dart | 6 +- .../lib/ui/collections/album/column_item.dart | 4 +- .../bottom_of_title_bar_widget.dart | 5 +- .../lib/ui/components/home_header_widget.dart | 1 + mobile/lib/ui/extents_page_view.dart | 4 +- mobile/lib/ui/home/header_error_widget.dart | 3 +- .../ui/settings/ml/ml_user_dev_screen.dart | 114 ++++++++++-------- .../ui/tools/collage/collage_item_widget.dart | 1 - .../file_selection_actions_widget.dart | 1 - .../lib/ui/viewer/date/date_time_picker.dart | 3 +- .../lib/ui/viewer/file/exif_info_dialog.dart | 1 - .../creation_time_item_widget.dart | 1 + mobile/lib/ui/viewer/people/people_util.dart | 22 ++-- mobile/lib/utils/face/face_box_crop.dart | 3 +- mobile/lib/utils/file_uploader.dart | 1 - mobile/lib/utils/wakelock_util.dart | 14 +-- 46 files changed, 404 insertions(+), 262 deletions(-) diff --git a/mobile/integration_test/app_init_test.dart b/mobile/integration_test/app_init_test.dart index e3313670c9..3d5160ee16 100644 --- a/mobile/integration_test/app_init_test.dart +++ b/mobile/integration_test/app_init_test.dart @@ -91,10 +91,9 @@ Future dismissUpdateAppDialog(WidgetTester tester) async { await tester.pumpAndSettle(); } - ///Use this widget as floating action buttom in HomeWidget so that frames -///are built and rendered continuously so that timeline trace has continuous -///data. Change the duraiton in `_startTimer()` to control the duraiton of +///are built and rendered continuously so that timeline trace has continuous +///data. Change the duraiton in `_startTimer()` to control the duraiton of ///test on app init. // class TempWidget extends StatefulWidget { @@ -127,4 +126,4 @@ Future dismissUpdateAppDialog(WidgetTester tester) async { // ? const CircularProgressIndicator() // : const SizedBox.shrink(); // } -// } \ No newline at end of file +// } diff --git a/mobile/lib/core/cache/image_cache.dart b/mobile/lib/core/cache/image_cache.dart index c489b019cd..7bcdfab60e 100644 --- a/mobile/lib/core/cache/image_cache.dart +++ b/mobile/lib/core/cache/image_cache.dart @@ -1,4 +1,3 @@ - import "dart:io"; import 'package:photos/core/cache/lru_map.dart'; diff --git a/mobile/lib/core/errors.dart b/mobile/lib/core/errors.dart index 0c0d16949a..f2bfad2f6d 100644 --- a/mobile/lib/core/errors.dart +++ b/mobile/lib/core/errors.dart @@ -58,7 +58,7 @@ bool isHandledSyncError(Object errObj) { class LockAlreadyAcquiredError extends Error {} -class LockFreedError extends Error{} +class LockFreedError extends Error {} class UnauthorizedError extends Error {} diff --git a/mobile/lib/generated/protos/ente/common/box.pb.dart b/mobile/lib/generated/protos/ente/common/box.pb.dart index 41518e9ae0..526c489137 100644 --- a/mobile/lib/generated/protos/ente/common/box.pb.dart +++ b/mobile/lib/generated/protos/ente/common/box.pb.dart @@ -37,27 +37,32 @@ class CenterBox extends $pb.GeneratedMessage { return $result; } CenterBox._() : super(); - factory CenterBox.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory CenterBox.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + factory CenterBox.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory CenterBox.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'CenterBox', package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.common'), createEmptyInstance: create) + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'CenterBox', + package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.common'), + createEmptyInstance: create) ..a<$core.double>(1, _omitFieldNames ? '' : 'x', $pb.PbFieldType.OF) ..a<$core.double>(2, _omitFieldNames ? '' : 'y', $pb.PbFieldType.OF) ..a<$core.double>(3, _omitFieldNames ? '' : 'height', $pb.PbFieldType.OF) ..a<$core.double>(4, _omitFieldNames ? '' : 'width', $pb.PbFieldType.OF) - ..hasRequiredFields = false - ; + ..hasRequiredFields = false; - @$core.Deprecated( - 'Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') CenterBox clone() => CenterBox()..mergeFromMessage(this); - @$core.Deprecated( - 'Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - CenterBox copyWith(void Function(CenterBox) updates) => super.copyWith((message) => updates(message as CenterBox)) as CenterBox; + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + CenterBox copyWith(void Function(CenterBox) updates) => + super.copyWith((message) => updates(message as CenterBox)) as CenterBox; $pb.BuilderInfo get info_ => _i; @@ -66,13 +71,17 @@ class CenterBox extends $pb.GeneratedMessage { CenterBox createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static CenterBox getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static CenterBox getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static CenterBox? _defaultInstance; @$pb.TagNumber(1) $core.double get x => $_getN(0); @$pb.TagNumber(1) - set x($core.double v) { $_setFloat(0, v); } + set x($core.double v) { + $_setFloat(0, v); + } + @$pb.TagNumber(1) $core.bool hasX() => $_has(0); @$pb.TagNumber(1) @@ -81,7 +90,10 @@ class CenterBox extends $pb.GeneratedMessage { @$pb.TagNumber(2) $core.double get y => $_getN(1); @$pb.TagNumber(2) - set y($core.double v) { $_setFloat(1, v); } + set y($core.double v) { + $_setFloat(1, v); + } + @$pb.TagNumber(2) $core.bool hasY() => $_has(1); @$pb.TagNumber(2) @@ -90,7 +102,10 @@ class CenterBox extends $pb.GeneratedMessage { @$pb.TagNumber(3) $core.double get height => $_getN(2); @$pb.TagNumber(3) - set height($core.double v) { $_setFloat(2, v); } + set height($core.double v) { + $_setFloat(2, v); + } + @$pb.TagNumber(3) $core.bool hasHeight() => $_has(2); @$pb.TagNumber(3) @@ -99,13 +114,16 @@ class CenterBox extends $pb.GeneratedMessage { @$pb.TagNumber(4) $core.double get width => $_getN(3); @$pb.TagNumber(4) - set width($core.double v) { $_setFloat(3, v); } + set width($core.double v) { + $_setFloat(3, v); + } + @$pb.TagNumber(4) $core.bool hasWidth() => $_has(3); @$pb.TagNumber(4) void clearWidth() => clearField(4); } - const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); +const _omitMessageNames = + $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/mobile/lib/generated/protos/ente/common/box.pbenum.dart b/mobile/lib/generated/protos/ente/common/box.pbenum.dart index 7310e57a03..4148fda29e 100644 --- a/mobile/lib/generated/protos/ente/common/box.pbenum.dart +++ b/mobile/lib/generated/protos/ente/common/box.pbenum.dart @@ -8,4 +8,3 @@ // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import - diff --git a/mobile/lib/generated/protos/ente/common/box.pbjson.dart b/mobile/lib/generated/protos/ente/common/box.pbjson.dart index 6c9ab3cb27..4c4a41442f 100644 --- a/mobile/lib/generated/protos/ente/common/box.pbjson.dart +++ b/mobile/lib/generated/protos/ente/common/box.pbjson.dart @@ -35,4 +35,3 @@ final $typed_data.Uint8List centerBoxDescriptor = $convert.base64Decode( 'CglDZW50ZXJCb3gSEQoBeBgBIAEoAkgAUgF4iAEBEhEKAXkYAiABKAJIAVIBeYgBARIbCgZoZW' 'lnaHQYAyABKAJIAlIGaGVpZ2h0iAEBEhkKBXdpZHRoGAQgASgCSANSBXdpZHRoiAEBQgQKAl94' 'QgQKAl95QgkKB19oZWlnaHRCCAoGX3dpZHRo'); - diff --git a/mobile/lib/generated/protos/ente/common/box.pbserver.dart b/mobile/lib/generated/protos/ente/common/box.pbserver.dart index 1e8625388d..79d3c2c86f 100644 --- a/mobile/lib/generated/protos/ente/common/box.pbserver.dart +++ b/mobile/lib/generated/protos/ente/common/box.pbserver.dart @@ -11,4 +11,3 @@ // ignore_for_file: unnecessary_import, unnecessary_this, unused_import export 'box.pb.dart'; - diff --git a/mobile/lib/generated/protos/ente/common/point.pb.dart b/mobile/lib/generated/protos/ente/common/point.pb.dart index 47f9b87ce3..be82db12db 100644 --- a/mobile/lib/generated/protos/ente/common/point.pb.dart +++ b/mobile/lib/generated/protos/ente/common/point.pb.dart @@ -29,25 +29,30 @@ class EPoint extends $pb.GeneratedMessage { return $result; } EPoint._() : super(); - factory EPoint.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory EPoint.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + factory EPoint.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory EPoint.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EPoint', package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.common'), createEmptyInstance: create) + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'EPoint', + package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.common'), + createEmptyInstance: create) ..a<$core.double>(1, _omitFieldNames ? '' : 'x', $pb.PbFieldType.OF) ..a<$core.double>(2, _omitFieldNames ? '' : 'y', $pb.PbFieldType.OF) - ..hasRequiredFields = false - ; + ..hasRequiredFields = false; - @$core.Deprecated( - 'Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') EPoint clone() => EPoint()..mergeFromMessage(this); - @$core.Deprecated( - 'Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - EPoint copyWith(void Function(EPoint) updates) => super.copyWith((message) => updates(message as EPoint)) as EPoint; + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + EPoint copyWith(void Function(EPoint) updates) => + super.copyWith((message) => updates(message as EPoint)) as EPoint; $pb.BuilderInfo get info_ => _i; @@ -56,13 +61,17 @@ class EPoint extends $pb.GeneratedMessage { EPoint createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static EPoint getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static EPoint getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static EPoint? _defaultInstance; @$pb.TagNumber(1) $core.double get x => $_getN(0); @$pb.TagNumber(1) - set x($core.double v) { $_setFloat(0, v); } + set x($core.double v) { + $_setFloat(0, v); + } + @$pb.TagNumber(1) $core.bool hasX() => $_has(0); @$pb.TagNumber(1) @@ -71,13 +80,16 @@ class EPoint extends $pb.GeneratedMessage { @$pb.TagNumber(2) $core.double get y => $_getN(1); @$pb.TagNumber(2) - set y($core.double v) { $_setFloat(1, v); } + set y($core.double v) { + $_setFloat(1, v); + } + @$pb.TagNumber(2) $core.bool hasY() => $_has(1); @$pb.TagNumber(2) void clearY() => clearField(2); } - const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); +const _omitMessageNames = + $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/mobile/lib/generated/protos/ente/common/point.pbenum.dart b/mobile/lib/generated/protos/ente/common/point.pbenum.dart index 3c242a2fcd..8de70fe0e7 100644 --- a/mobile/lib/generated/protos/ente/common/point.pbenum.dart +++ b/mobile/lib/generated/protos/ente/common/point.pbenum.dart @@ -8,4 +8,3 @@ // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import - diff --git a/mobile/lib/generated/protos/ente/common/point.pbjson.dart b/mobile/lib/generated/protos/ente/common/point.pbjson.dart index 44d2d0712a..aeb17b4a2c 100644 --- a/mobile/lib/generated/protos/ente/common/point.pbjson.dart +++ b/mobile/lib/generated/protos/ente/common/point.pbjson.dart @@ -30,4 +30,3 @@ const EPoint$json = { final $typed_data.Uint8List ePointDescriptor = $convert.base64Decode( 'CgZFUG9pbnQSEQoBeBgBIAEoAkgAUgF4iAEBEhEKAXkYAiABKAJIAVIBeYgBAUIECgJfeEIECg' 'JfeQ=='); - diff --git a/mobile/lib/generated/protos/ente/common/point.pbserver.dart b/mobile/lib/generated/protos/ente/common/point.pbserver.dart index 66728e123a..490cae6a97 100644 --- a/mobile/lib/generated/protos/ente/common/point.pbserver.dart +++ b/mobile/lib/generated/protos/ente/common/point.pbserver.dart @@ -11,4 +11,3 @@ // ignore_for_file: unnecessary_import, unnecessary_this, unused_import export 'point.pb.dart'; - diff --git a/mobile/lib/generated/protos/ente/common/vector.pb.dart b/mobile/lib/generated/protos/ente/common/vector.pb.dart index 44aa7d7485..1b66675202 100644 --- a/mobile/lib/generated/protos/ente/common/vector.pb.dart +++ b/mobile/lib/generated/protos/ente/common/vector.pb.dart @@ -26,24 +26,29 @@ class EVector extends $pb.GeneratedMessage { return $result; } EVector._() : super(); - factory EVector.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory EVector.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + factory EVector.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory EVector.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EVector', package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.common'), createEmptyInstance: create) + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'EVector', + package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.common'), + createEmptyInstance: create) ..p<$core.double>(1, _omitFieldNames ? '' : 'values', $pb.PbFieldType.KD) - ..hasRequiredFields = false - ; + ..hasRequiredFields = false; - @$core.Deprecated( - 'Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') EVector clone() => EVector()..mergeFromMessage(this); - @$core.Deprecated( - 'Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - EVector copyWith(void Function(EVector) updates) => super.copyWith((message) => updates(message as EVector)) as EVector; + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + EVector copyWith(void Function(EVector) updates) => + super.copyWith((message) => updates(message as EVector)) as EVector; $pb.BuilderInfo get info_ => _i; @@ -52,13 +57,14 @@ class EVector extends $pb.GeneratedMessage { EVector createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static EVector getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static EVector getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static EVector? _defaultInstance; @$pb.TagNumber(1) $core.List<$core.double> get values => $_getList(0); } - const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); +const _omitMessageNames = + $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/mobile/lib/generated/protos/ente/common/vector.pbenum.dart b/mobile/lib/generated/protos/ente/common/vector.pbenum.dart index c88d2648a1..e0d964fe0f 100644 --- a/mobile/lib/generated/protos/ente/common/vector.pbenum.dart +++ b/mobile/lib/generated/protos/ente/common/vector.pbenum.dart @@ -8,4 +8,3 @@ // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import - diff --git a/mobile/lib/generated/protos/ente/common/vector.pbjson.dart b/mobile/lib/generated/protos/ente/common/vector.pbjson.dart index 1aff5cb290..8afe4568e0 100644 --- a/mobile/lib/generated/protos/ente/common/vector.pbjson.dart +++ b/mobile/lib/generated/protos/ente/common/vector.pbjson.dart @@ -22,6 +22,5 @@ const EVector$json = { }; /// Descriptor for `EVector`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List eVectorDescriptor = $convert.base64Decode( - 'CgdFVmVjdG9yEhYKBnZhbHVlcxgBIAMoAVIGdmFsdWVz'); - +final $typed_data.Uint8List eVectorDescriptor = + $convert.base64Decode('CgdFVmVjdG9yEhYKBnZhbHVlcxgBIAMoAVIGdmFsdWVz'); diff --git a/mobile/lib/generated/protos/ente/common/vector.pbserver.dart b/mobile/lib/generated/protos/ente/common/vector.pbserver.dart index dbf5ac36fa..2ffc744d6c 100644 --- a/mobile/lib/generated/protos/ente/common/vector.pbserver.dart +++ b/mobile/lib/generated/protos/ente/common/vector.pbserver.dart @@ -11,4 +11,3 @@ // ignore_for_file: unnecessary_import, unnecessary_this, unused_import export 'vector.pb.dart'; - diff --git a/mobile/lib/generated/protos/ente/ml/face.pb.dart b/mobile/lib/generated/protos/ente/ml/face.pb.dart index 55d512b664..050bcf1955 100644 --- a/mobile/lib/generated/protos/ente/ml/face.pb.dart +++ b/mobile/lib/generated/protos/ente/ml/face.pb.dart @@ -31,25 +31,32 @@ class Detection extends $pb.GeneratedMessage { return $result; } Detection._() : super(); - factory Detection.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory Detection.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + factory Detection.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory Detection.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Detection', package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.ml'), createEmptyInstance: create) - ..aOM<$0.CenterBox>(1, _omitFieldNames ? '' : 'box', subBuilder: $0.CenterBox.create) - ..aOM<$1.EPoint>(2, _omitFieldNames ? '' : 'landmarks', subBuilder: $1.EPoint.create) - ..hasRequiredFields = false - ; + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Detection', + package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.ml'), + createEmptyInstance: create) + ..aOM<$0.CenterBox>(1, _omitFieldNames ? '' : 'box', + subBuilder: $0.CenterBox.create) + ..aOM<$1.EPoint>(2, _omitFieldNames ? '' : 'landmarks', + subBuilder: $1.EPoint.create) + ..hasRequiredFields = false; - @$core.Deprecated( - 'Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') Detection clone() => Detection()..mergeFromMessage(this); - @$core.Deprecated( - 'Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Detection copyWith(void Function(Detection) updates) => super.copyWith((message) => updates(message as Detection)) as Detection; + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + Detection copyWith(void Function(Detection) updates) => + super.copyWith((message) => updates(message as Detection)) as Detection; $pb.BuilderInfo get info_ => _i; @@ -58,13 +65,17 @@ class Detection extends $pb.GeneratedMessage { Detection createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static Detection getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Detection getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Detection? _defaultInstance; @$pb.TagNumber(1) $0.CenterBox get box => $_getN(0); @$pb.TagNumber(1) - set box($0.CenterBox v) { setField(1, v); } + set box($0.CenterBox v) { + setField(1, v); + } + @$pb.TagNumber(1) $core.bool hasBox() => $_has(0); @$pb.TagNumber(1) @@ -75,7 +86,10 @@ class Detection extends $pb.GeneratedMessage { @$pb.TagNumber(2) $1.EPoint get landmarks => $_getN(1); @$pb.TagNumber(2) - set landmarks($1.EPoint v) { setField(2, v); } + set landmarks($1.EPoint v) { + setField(2, v); + } + @$pb.TagNumber(2) $core.bool hasLandmarks() => $_has(1); @$pb.TagNumber(2) @@ -103,26 +117,33 @@ class Face extends $pb.GeneratedMessage { return $result; } Face._() : super(); - factory Face.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory Face.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + factory Face.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory Face.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Face', package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.ml'), createEmptyInstance: create) + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Face', + package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.ml'), + createEmptyInstance: create) ..aOS(1, _omitFieldNames ? '' : 'id') - ..aOM(2, _omitFieldNames ? '' : 'detection', subBuilder: Detection.create) - ..a<$core.double>(3, _omitFieldNames ? '' : 'confidence', $pb.PbFieldType.OF) - ..hasRequiredFields = false - ; + ..aOM(2, _omitFieldNames ? '' : 'detection', + subBuilder: Detection.create) + ..a<$core.double>( + 3, _omitFieldNames ? '' : 'confidence', $pb.PbFieldType.OF) + ..hasRequiredFields = false; - @$core.Deprecated( - 'Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') Face clone() => Face()..mergeFromMessage(this); - @$core.Deprecated( - 'Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Face copyWith(void Function(Face) updates) => super.copyWith((message) => updates(message as Face)) as Face; + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + Face copyWith(void Function(Face) updates) => + super.copyWith((message) => updates(message as Face)) as Face; $pb.BuilderInfo get info_ => _i; @@ -131,13 +152,17 @@ class Face extends $pb.GeneratedMessage { Face createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static Face getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Face getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Face? _defaultInstance; @$pb.TagNumber(1) $core.String get id => $_getSZ(0); @$pb.TagNumber(1) - set id($core.String v) { $_setString(0, v); } + set id($core.String v) { + $_setString(0, v); + } + @$pb.TagNumber(1) $core.bool hasId() => $_has(0); @$pb.TagNumber(1) @@ -146,7 +171,10 @@ class Face extends $pb.GeneratedMessage { @$pb.TagNumber(2) Detection get detection => $_getN(1); @$pb.TagNumber(2) - set detection(Detection v) { setField(2, v); } + set detection(Detection v) { + setField(2, v); + } + @$pb.TagNumber(2) $core.bool hasDetection() => $_has(1); @$pb.TagNumber(2) @@ -157,13 +185,16 @@ class Face extends $pb.GeneratedMessage { @$pb.TagNumber(3) $core.double get confidence => $_getN(2); @$pb.TagNumber(3) - set confidence($core.double v) { $_setFloat(2, v); } + set confidence($core.double v) { + $_setFloat(2, v); + } + @$pb.TagNumber(3) $core.bool hasConfidence() => $_has(2); @$pb.TagNumber(3) void clearConfidence() => clearField(3); } - const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); +const _omitMessageNames = + $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/mobile/lib/generated/protos/ente/ml/face.pbenum.dart b/mobile/lib/generated/protos/ente/ml/face.pbenum.dart index 2eefe1f447..9691e4bb9d 100644 --- a/mobile/lib/generated/protos/ente/ml/face.pbenum.dart +++ b/mobile/lib/generated/protos/ente/ml/face.pbenum.dart @@ -8,4 +8,3 @@ // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import - diff --git a/mobile/lib/generated/protos/ente/ml/face.pbjson.dart b/mobile/lib/generated/protos/ente/ml/face.pbjson.dart index 5aa614a8b8..59e6d41b14 100644 --- a/mobile/lib/generated/protos/ente/ml/face.pbjson.dart +++ b/mobile/lib/generated/protos/ente/ml/face.pbjson.dart @@ -17,8 +17,26 @@ import 'dart:typed_data' as $typed_data; const Detection$json = { '1': 'Detection', '2': [ - {'1': 'box', '3': 1, '4': 1, '5': 11, '6': '.ente.common.CenterBox', '9': 0, '10': 'box', '17': true}, - {'1': 'landmarks', '3': 2, '4': 1, '5': 11, '6': '.ente.common.EPoint', '9': 1, '10': 'landmarks', '17': true}, + { + '1': 'box', + '3': 1, + '4': 1, + '5': 11, + '6': '.ente.common.CenterBox', + '9': 0, + '10': 'box', + '17': true + }, + { + '1': 'landmarks', + '3': 2, + '4': 1, + '5': 11, + '6': '.ente.common.EPoint', + '9': 1, + '10': 'landmarks', + '17': true + }, ], '8': [ {'1': '_box'}, @@ -37,8 +55,25 @@ const Face$json = { '1': 'Face', '2': [ {'1': 'id', '3': 1, '4': 1, '5': 9, '9': 0, '10': 'id', '17': true}, - {'1': 'detection', '3': 2, '4': 1, '5': 11, '6': '.ente.ml.Detection', '9': 1, '10': 'detection', '17': true}, - {'1': 'confidence', '3': 3, '4': 1, '5': 2, '9': 2, '10': 'confidence', '17': true}, + { + '1': 'detection', + '3': 2, + '4': 1, + '5': 11, + '6': '.ente.ml.Detection', + '9': 1, + '10': 'detection', + '17': true + }, + { + '1': 'confidence', + '3': 3, + '4': 1, + '5': 2, + '9': 2, + '10': 'confidence', + '17': true + }, ], '8': [ {'1': '_id'}, @@ -52,4 +87,3 @@ final $typed_data.Uint8List faceDescriptor = $convert.base64Decode( 'CgRGYWNlEhMKAmlkGAEgASgJSABSAmlkiAEBEjUKCWRldGVjdGlvbhgCIAEoCzISLmVudGUubW' 'wuRGV0ZWN0aW9uSAFSCWRldGVjdGlvbogBARIjCgpjb25maWRlbmNlGAMgASgCSAJSCmNvbmZp' 'ZGVuY2WIAQFCBQoDX2lkQgwKCl9kZXRlY3Rpb25CDQoLX2NvbmZpZGVuY2U='); - diff --git a/mobile/lib/generated/protos/ente/ml/face.pbserver.dart b/mobile/lib/generated/protos/ente/ml/face.pbserver.dart index a2cd6ff853..9983bc998d 100644 --- a/mobile/lib/generated/protos/ente/ml/face.pbserver.dart +++ b/mobile/lib/generated/protos/ente/ml/face.pbserver.dart @@ -11,4 +11,3 @@ // ignore_for_file: unnecessary_import, unnecessary_this, unused_import export 'face.pb.dart'; - diff --git a/mobile/lib/generated/protos/ente/ml/fileml.pb.dart b/mobile/lib/generated/protos/ente/ml/fileml.pb.dart index 853f89bac4..b717748ccb 100644 --- a/mobile/lib/generated/protos/ente/ml/fileml.pb.dart +++ b/mobile/lib/generated/protos/ente/ml/fileml.pb.dart @@ -31,25 +31,30 @@ class FileML extends $pb.GeneratedMessage { return $result; } FileML._() : super(); - factory FileML.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory FileML.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + factory FileML.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory FileML.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'FileML', package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.ml'), createEmptyInstance: create) + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'FileML', + package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.ml'), + createEmptyInstance: create) ..aInt64(1, _omitFieldNames ? '' : 'id') ..p<$core.double>(2, _omitFieldNames ? '' : 'clip', $pb.PbFieldType.KD) - ..hasRequiredFields = false - ; + ..hasRequiredFields = false; - @$core.Deprecated( - 'Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') FileML clone() => FileML()..mergeFromMessage(this); - @$core.Deprecated( - 'Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - FileML copyWith(void Function(FileML) updates) => super.copyWith((message) => updates(message as FileML)) as FileML; + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + FileML copyWith(void Function(FileML) updates) => + super.copyWith((message) => updates(message as FileML)) as FileML; $pb.BuilderInfo get info_ => _i; @@ -58,13 +63,17 @@ class FileML extends $pb.GeneratedMessage { FileML createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static FileML getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static FileML getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static FileML? _defaultInstance; @$pb.TagNumber(1) $fixnum.Int64 get id => $_getI64(0); @$pb.TagNumber(1) - set id($fixnum.Int64 v) { $_setInt64(0, v); } + set id($fixnum.Int64 v) { + $_setInt64(0, v); + } + @$pb.TagNumber(1) $core.bool hasId() => $_has(0); @$pb.TagNumber(1) @@ -101,28 +110,34 @@ class FileFaces extends $pb.GeneratedMessage { return $result; } FileFaces._() : super(); - factory FileFaces.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory FileFaces.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + factory FileFaces.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory FileFaces.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'FileFaces', package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.ml'), createEmptyInstance: create) - ..pc<$2.Face>(1, _omitFieldNames ? '' : 'faces', $pb.PbFieldType.PM, subBuilder: $2.Face.create) + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'FileFaces', + package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.ml'), + createEmptyInstance: create) + ..pc<$2.Face>(1, _omitFieldNames ? '' : 'faces', $pb.PbFieldType.PM, + subBuilder: $2.Face.create) ..a<$core.int>(2, _omitFieldNames ? '' : 'height', $pb.PbFieldType.O3) ..a<$core.int>(3, _omitFieldNames ? '' : 'width', $pb.PbFieldType.O3) ..a<$core.int>(4, _omitFieldNames ? '' : 'version', $pb.PbFieldType.O3) ..aOS(5, _omitFieldNames ? '' : 'error') - ..hasRequiredFields = false - ; + ..hasRequiredFields = false; - @$core.Deprecated( - 'Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') FileFaces clone() => FileFaces()..mergeFromMessage(this); - @$core.Deprecated( - 'Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - FileFaces copyWith(void Function(FileFaces) updates) => super.copyWith((message) => updates(message as FileFaces)) as FileFaces; + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + FileFaces copyWith(void Function(FileFaces) updates) => + super.copyWith((message) => updates(message as FileFaces)) as FileFaces; $pb.BuilderInfo get info_ => _i; @@ -131,7 +146,8 @@ class FileFaces extends $pb.GeneratedMessage { FileFaces createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static FileFaces getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static FileFaces getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static FileFaces? _defaultInstance; @$pb.TagNumber(1) @@ -140,7 +156,10 @@ class FileFaces extends $pb.GeneratedMessage { @$pb.TagNumber(2) $core.int get height => $_getIZ(1); @$pb.TagNumber(2) - set height($core.int v) { $_setSignedInt32(1, v); } + set height($core.int v) { + $_setSignedInt32(1, v); + } + @$pb.TagNumber(2) $core.bool hasHeight() => $_has(1); @$pb.TagNumber(2) @@ -149,7 +168,10 @@ class FileFaces extends $pb.GeneratedMessage { @$pb.TagNumber(3) $core.int get width => $_getIZ(2); @$pb.TagNumber(3) - set width($core.int v) { $_setSignedInt32(2, v); } + set width($core.int v) { + $_setSignedInt32(2, v); + } + @$pb.TagNumber(3) $core.bool hasWidth() => $_has(2); @$pb.TagNumber(3) @@ -158,7 +180,10 @@ class FileFaces extends $pb.GeneratedMessage { @$pb.TagNumber(4) $core.int get version => $_getIZ(3); @$pb.TagNumber(4) - set version($core.int v) { $_setSignedInt32(3, v); } + set version($core.int v) { + $_setSignedInt32(3, v); + } + @$pb.TagNumber(4) $core.bool hasVersion() => $_has(3); @$pb.TagNumber(4) @@ -167,13 +192,16 @@ class FileFaces extends $pb.GeneratedMessage { @$pb.TagNumber(5) $core.String get error => $_getSZ(4); @$pb.TagNumber(5) - set error($core.String v) { $_setString(4, v); } + set error($core.String v) { + $_setString(4, v); + } + @$pb.TagNumber(5) $core.bool hasError() => $_has(4); @$pb.TagNumber(5) void clearError() => clearField(5); } - const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); +const _omitMessageNames = + $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/mobile/lib/generated/protos/ente/ml/fileml.pbenum.dart b/mobile/lib/generated/protos/ente/ml/fileml.pbenum.dart index 71d796efe9..3ced4ad5f4 100644 --- a/mobile/lib/generated/protos/ente/ml/fileml.pbenum.dart +++ b/mobile/lib/generated/protos/ente/ml/fileml.pbenum.dart @@ -8,4 +8,3 @@ // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import - diff --git a/mobile/lib/generated/protos/ente/ml/fileml.pbjson.dart b/mobile/lib/generated/protos/ente/ml/fileml.pbjson.dart index 824741733e..314d85226b 100644 --- a/mobile/lib/generated/protos/ente/ml/fileml.pbjson.dart +++ b/mobile/lib/generated/protos/ente/ml/fileml.pbjson.dart @@ -34,10 +34,25 @@ final $typed_data.Uint8List fileMLDescriptor = $convert.base64Decode( const FileFaces$json = { '1': 'FileFaces', '2': [ - {'1': 'faces', '3': 1, '4': 3, '5': 11, '6': '.ente.ml.Face', '10': 'faces'}, + { + '1': 'faces', + '3': 1, + '4': 3, + '5': 11, + '6': '.ente.ml.Face', + '10': 'faces' + }, {'1': 'height', '3': 2, '4': 1, '5': 5, '9': 0, '10': 'height', '17': true}, {'1': 'width', '3': 3, '4': 1, '5': 5, '9': 1, '10': 'width', '17': true}, - {'1': 'version', '3': 4, '4': 1, '5': 5, '9': 2, '10': 'version', '17': true}, + { + '1': 'version', + '3': 4, + '4': 1, + '5': 5, + '9': 2, + '10': 'version', + '17': true + }, {'1': 'error', '3': 5, '4': 1, '5': 9, '9': 3, '10': 'error', '17': true}, ], '8': [ @@ -54,4 +69,3 @@ final $typed_data.Uint8List fileFacesDescriptor = $convert.base64Decode( 'dodBgCIAEoBUgAUgZoZWlnaHSIAQESGQoFd2lkdGgYAyABKAVIAVIFd2lkdGiIAQESHQoHdmVy' 'c2lvbhgEIAEoBUgCUgd2ZXJzaW9uiAEBEhkKBWVycm9yGAUgASgJSANSBWVycm9yiAEBQgkKB1' '9oZWlnaHRCCAoGX3dpZHRoQgoKCF92ZXJzaW9uQggKBl9lcnJvcg=='); - diff --git a/mobile/lib/generated/protos/ente/ml/fileml.pbserver.dart b/mobile/lib/generated/protos/ente/ml/fileml.pbserver.dart index 4cb208d271..aff0b1e960 100644 --- a/mobile/lib/generated/protos/ente/ml/fileml.pbserver.dart +++ b/mobile/lib/generated/protos/ente/ml/fileml.pbserver.dart @@ -11,4 +11,3 @@ // ignore_for_file: unnecessary_import, unnecessary_this, unused_import export 'fileml.pb.dart'; - diff --git a/mobile/lib/models/api/user/srp.dart b/mobile/lib/models/api/user/srp.dart index 1456052947..56b19c1317 100644 --- a/mobile/lib/models/api/user/srp.dart +++ b/mobile/lib/models/api/user/srp.dart @@ -5,7 +5,6 @@ class SetupSRPRequest { final String srpA; final bool isUpdate; - SetupSRPRequest({ required this.srpUserID, required this.srpSalt, @@ -82,6 +81,7 @@ class CompleteSRPSetupRequest { ); } } + class SrpAttributes { final String srpUserID; final String srpSalt; diff --git a/mobile/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart b/mobile/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart index 2e79215cf1..3a5f14e706 100644 --- a/mobile/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart +++ b/mobile/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart @@ -507,7 +507,8 @@ ClusteringResult _runCompleteClustering(Map args) { EVector.fromBuffer(entry.value).values, dtype: DType.float32, ), - fileCreationTime: fileIDToCreationTime?[getFileIdFromFaceId(entry.key)], + fileCreationTime: + fileIDToCreationTime?[getFileIdFromFaceId(entry.key)], ), ); } diff --git a/mobile/lib/services/machine_learning/face_ml/face_detection/face_detection_service.dart b/mobile/lib/services/machine_learning/face_ml/face_detection/face_detection_service.dart index 5f2458c629..17d57d943d 100644 --- a/mobile/lib/services/machine_learning/face_ml/face_detection/face_detection_service.dart +++ b/mobile/lib/services/machine_learning/face_ml/face_detection/face_detection_service.dart @@ -82,7 +82,11 @@ class FaceDetectionService extends MlModel { 'Face detection is finished, in ${inferenceTime.difference(startTime).inMilliseconds} ms (preprocessing: $preprocessingMs ms, inference: $inferenceMs ms)', ); } catch (e, s) { - _logger.severe('Error while running inference (PlatformPlugin: ${MlModel.usePlatformPlugin})', e, s); + _logger.severe( + 'Error while running inference (PlatformPlugin: ${MlModel.usePlatformPlugin})', + e, + s, + ); throw YOLOFaceInterpreterRunException(); } try { diff --git a/mobile/lib/services/machine_learning/ml_constants.dart b/mobile/lib/services/machine_learning/ml_constants.dart index 424cf584fc..19b9844af2 100644 --- a/mobile/lib/services/machine_learning/ml_constants.dart +++ b/mobile/lib/services/machine_learning/ml_constants.dart @@ -1 +1 @@ -const imageEmbeddingsKey = "imageEmbeddings"; \ No newline at end of file +const imageEmbeddingsKey = "imageEmbeddings"; diff --git a/mobile/lib/services/machine_learning/ml_exceptions.dart b/mobile/lib/services/machine_learning/ml_exceptions.dart index 224be2fd6f..3c056b9514 100644 --- a/mobile/lib/services/machine_learning/ml_exceptions.dart +++ b/mobile/lib/services/machine_learning/ml_exceptions.dart @@ -1,4 +1,3 @@ - class GeneralFaceMlException implements Exception { final String message; @@ -26,4 +25,4 @@ class CouldNotRunFaceDetector implements Exception {} class CouldNotWarpAffine implements Exception {} -class CouldNotRunFaceEmbeddor implements Exception {} \ No newline at end of file +class CouldNotRunFaceEmbeddor implements Exception {} diff --git a/mobile/lib/services/machine_learning/semantic_search/query_result.dart b/mobile/lib/services/machine_learning/semantic_search/query_result.dart index aa48ab9f85..9e0d6e5455 100644 --- a/mobile/lib/services/machine_learning/semantic_search/query_result.dart +++ b/mobile/lib/services/machine_learning/semantic_search/query_result.dart @@ -3,4 +3,4 @@ class QueryResult { final double score; QueryResult(this.id, this.score); -} \ No newline at end of file +} diff --git a/mobile/lib/services/magic_cache_service.dart b/mobile/lib/services/magic_cache_service.dart index f97fbcb37b..9bda19a98f 100644 --- a/mobile/lib/services/magic_cache_service.dart +++ b/mobile/lib/services/magic_cache_service.dart @@ -311,9 +311,9 @@ class MagicCacheService { Future clearMagicCache() async { final file = File(await _getCachePath()); - if (file.existsSync()) { - await file.delete(); - } + if (file.existsSync()) { + await file.delete(); + } } Future> getMagicGenericSearchResult( diff --git a/mobile/lib/services/memories_cache_service.dart b/mobile/lib/services/memories_cache_service.dart index a7a01de641..bd446c7047 100644 --- a/mobile/lib/services/memories_cache_service.dart +++ b/mobile/lib/services/memories_cache_service.dart @@ -434,9 +434,9 @@ class MemoriesCacheService { Future clearMemoriesCache() async { final file = File(await _getCachePath()); - if (file.existsSync()) { - await file.delete(); - } + if (file.existsSync()) { + await file.delete(); + } _cachedMemories = null; } } diff --git a/mobile/lib/ui/collections/album/column_item.dart b/mobile/lib/ui/collections/album/column_item.dart index a81d71273e..e3d62b01a3 100644 --- a/mobile/lib/ui/collections/album/column_item.dart +++ b/mobile/lib/ui/collections/album/column_item.dart @@ -64,8 +64,8 @@ class AlbumColumnItemWidget extends StatelessWidget { children: [ Text(collection.displayName), FutureBuilder( - future: CollectionsService.instance.getFileCount - (collection), + future: CollectionsService.instance + .getFileCount(collection), builder: (context, snapshot) { if (snapshot.hasData) { return Text( diff --git a/mobile/lib/ui/components/bottom_of_title_bar_widget.dart b/mobile/lib/ui/components/bottom_of_title_bar_widget.dart index 6d4a5d962f..be03babc47 100644 --- a/mobile/lib/ui/components/bottom_of_title_bar_widget.dart +++ b/mobile/lib/ui/components/bottom_of_title_bar_widget.dart @@ -18,8 +18,9 @@ class BottomOfTitleBarWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( - mainAxisAlignment: showCloseButton ? MainAxisAlignment.spaceBetween : - MainAxisAlignment.start, + mainAxisAlignment: showCloseButton + ? MainAxisAlignment.spaceBetween + : MainAxisAlignment.start, children: [ Flexible( child: Padding( diff --git a/mobile/lib/ui/components/home_header_widget.dart b/mobile/lib/ui/components/home_header_widget.dart index 3e37078cc5..836418136f 100644 --- a/mobile/lib/ui/components/home_header_widget.dart +++ b/mobile/lib/ui/components/home_header_widget.dart @@ -10,6 +10,7 @@ import 'package:photos/ui/components/buttons/icon_button_widget.dart'; import "package:photos/ui/settings/backup/backup_folder_selection_page.dart"; import "package:photos/utils/dialog_util.dart"; import "package:photos/utils/navigation_util.dart"; + class HomeHeaderWidget extends StatefulWidget { final Widget centerWidget; const HomeHeaderWidget({required this.centerWidget, super.key}); diff --git a/mobile/lib/ui/extents_page_view.dart b/mobile/lib/ui/extents_page_view.dart index 1ed3572c9e..25f9200639 100644 --- a/mobile/lib/ui/extents_page_view.dart +++ b/mobile/lib/ui/extents_page_view.dart @@ -100,7 +100,7 @@ class ExtentsPageView extends StatefulWidget { int? itemCount, this.dragStartBehavior = DragStartBehavior.start, this.openDrawer, - }) : childrenDelegate = SliverChildBuilderDelegate( + }) : childrenDelegate = SliverChildBuilderDelegate( itemBuilder, childCount: itemCount, addAutomaticKeepAlives: false, @@ -198,7 +198,7 @@ class ExtentsPageView extends StatefulWidget { required this.childrenDelegate, this.dragStartBehavior = DragStartBehavior.start, this.openDrawer, - }) : extents = 0; + }) : extents = 0; /// The number of pages to build off screen. /// diff --git a/mobile/lib/ui/home/header_error_widget.dart b/mobile/lib/ui/home/header_error_widget.dart index 381d1c1a95..aab3aa91ba 100644 --- a/mobile/lib/ui/home/header_error_widget.dart +++ b/mobile/lib/ui/home/header_error_widget.dart @@ -9,8 +9,7 @@ import "package:photos/utils/navigation_util.dart"; class HeaderErrorWidget extends StatelessWidget { final Error? _error; - const HeaderErrorWidget({super.key, required Error? error}) - : _error = error; + const HeaderErrorWidget({super.key, required Error? error}) : _error = error; @override Widget build(BuildContext context) { diff --git a/mobile/lib/ui/settings/ml/ml_user_dev_screen.dart b/mobile/lib/ui/settings/ml/ml_user_dev_screen.dart index 07a46131e1..db9e7a7964 100644 --- a/mobile/lib/ui/settings/ml/ml_user_dev_screen.dart +++ b/mobile/lib/ui/settings/ml/ml_user_dev_screen.dart @@ -57,57 +57,69 @@ class _MLUserDeveloperOptionsState extends State { ), ), const SizedBox(height: 48), - widget.mlIsEnabled ? ButtonWidget( - buttonType: ButtonType.neutral, - labelText: "Purge empty indices", - onTap: () async { - await deleteEmptyIndices(context); - }, - ) : const SizedBox(), - widget.mlIsEnabled ? const SizedBox(height: 24) : const SizedBox(), - widget.mlIsEnabled ? ButtonWidget( - buttonType: ButtonType.neutral, - labelText: "Reset all local ML", - onTap: () async { - await deleteAllLocalML(context); - }, - ) : const SizedBox(), - widget.mlIsEnabled ? const SizedBox(height: 24) : const SizedBox(), - widget.mlIsEnabled ? MenuItemWidget( - captionedTextWidget: const CaptionedTextWidget( - title: "Remote fetch", - ), - menuItemColor: colorScheme.fillFaint, - trailingWidget: ToggleSwitchWidget( - value: () => localSettings.remoteFetchEnabled, - onChanged: () async { - try { - await localSettings.toggleRemoteFetch(); - _logger.info( - 'Remote fetch is turned ${localSettings.remoteFetchEnabled ? 'on' : 'off'}', - ); - if (mounted) { - setState(() {}); - } - } catch (e, s) { - _logger.warning( - 'Remote fetch toggle failed ', - e, - s, - ); - await showGenericErrorDialog( - context: context, - error: e, - ); - } - }, - ), - singleBorderRadius: 8, - alignCaptionedTextToLeft: true, - isBottomBorderRadiusRemoved: true, - isGestureDetectorDisabled: true, - ) : const SizedBox(), - widget.mlIsEnabled ? const SizedBox(height: 24) : const SizedBox.shrink(), + widget.mlIsEnabled + ? ButtonWidget( + buttonType: ButtonType.neutral, + labelText: "Purge empty indices", + onTap: () async { + await deleteEmptyIndices(context); + }, + ) + : const SizedBox(), + widget.mlIsEnabled + ? const SizedBox(height: 24) + : const SizedBox(), + widget.mlIsEnabled + ? ButtonWidget( + buttonType: ButtonType.neutral, + labelText: "Reset all local ML", + onTap: () async { + await deleteAllLocalML(context); + }, + ) + : const SizedBox(), + widget.mlIsEnabled + ? const SizedBox(height: 24) + : const SizedBox(), + widget.mlIsEnabled + ? MenuItemWidget( + captionedTextWidget: const CaptionedTextWidget( + title: "Remote fetch", + ), + menuItemColor: colorScheme.fillFaint, + trailingWidget: ToggleSwitchWidget( + value: () => localSettings.remoteFetchEnabled, + onChanged: () async { + try { + await localSettings.toggleRemoteFetch(); + _logger.info( + 'Remote fetch is turned ${localSettings.remoteFetchEnabled ? 'on' : 'off'}', + ); + if (mounted) { + setState(() {}); + } + } catch (e, s) { + _logger.warning( + 'Remote fetch toggle failed ', + e, + s, + ); + await showGenericErrorDialog( + context: context, + error: e, + ); + } + }, + ), + singleBorderRadius: 8, + alignCaptionedTextToLeft: true, + isBottomBorderRadiusRemoved: true, + isGestureDetectorDisabled: true, + ) + : const SizedBox(), + widget.mlIsEnabled + ? const SizedBox(height: 24) + : const SizedBox.shrink(), ButtonWidget( buttonType: ButtonType.neutral, labelText: "Load face detection model", diff --git a/mobile/lib/ui/tools/collage/collage_item_widget.dart b/mobile/lib/ui/tools/collage/collage_item_widget.dart index 4351954d0b..66a45a55b7 100644 --- a/mobile/lib/ui/tools/collage/collage_item_widget.dart +++ b/mobile/lib/ui/tools/collage/collage_item_widget.dart @@ -1,4 +1,3 @@ - import "package:flutter/material.dart"; import 'package:photos/models/file/file.dart'; import "package:photos/ui/viewer/file/zoomable_image.dart"; diff --git a/mobile/lib/ui/viewer/actions/file_selection_actions_widget.dart b/mobile/lib/ui/viewer/actions/file_selection_actions_widget.dart index 8b56d1d010..4100abbef0 100644 --- a/mobile/lib/ui/viewer/actions/file_selection_actions_widget.dart +++ b/mobile/lib/ui/viewer/actions/file_selection_actions_widget.dart @@ -1,7 +1,6 @@ import "dart:async"; import 'package:fast_base58/fast_base58.dart'; -import "package:flutter/cupertino.dart"; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import "package:local_auth/local_auth.dart"; diff --git a/mobile/lib/ui/viewer/date/date_time_picker.dart b/mobile/lib/ui/viewer/date/date_time_picker.dart index dfdb7a5c9d..25dfcc95c0 100644 --- a/mobile/lib/ui/viewer/date/date_time_picker.dart +++ b/mobile/lib/ui/viewer/date/date_time_picker.dart @@ -3,7 +3,8 @@ import "package:flutter/material.dart"; import "package:photos/generated/l10n.dart"; import "package:photos/theme/ente_theme.dart"; -Future showDatePickerSheet(BuildContext context, { +Future showDatePickerSheet( + BuildContext context, { required DateTime initialDate, DateTime? maxDate, DateTime? minDate, diff --git a/mobile/lib/ui/viewer/file/exif_info_dialog.dart b/mobile/lib/ui/viewer/file/exif_info_dialog.dart index 2b286b5601..12aaf42930 100644 --- a/mobile/lib/ui/viewer/file/exif_info_dialog.dart +++ b/mobile/lib/ui/viewer/file/exif_info_dialog.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import "package:photos/generated/l10n.dart"; import 'package:photos/models/file/file.dart'; diff --git a/mobile/lib/ui/viewer/file_details/creation_time_item_widget.dart b/mobile/lib/ui/viewer/file_details/creation_time_item_widget.dart index bc19247340..735b1ac07b 100644 --- a/mobile/lib/ui/viewer/file_details/creation_time_item_widget.dart +++ b/mobile/lib/ui/viewer/file_details/creation_time_item_widget.dart @@ -5,6 +5,7 @@ import "package:photos/theme/ente_theme.dart"; import "package:photos/ui/components/info_item_widget.dart"; import "package:photos/ui/viewer/date/edit_date_sheet.dart"; import "package:photos/utils/standalone/date_time.dart"; + class CreationTimeItem extends StatefulWidget { final EnteFile file; final int currentUserID; diff --git a/mobile/lib/ui/viewer/people/people_util.dart b/mobile/lib/ui/viewer/people/people_util.dart index 55ac269a4d..1270f1d30a 100644 --- a/mobile/lib/ui/viewer/people/people_util.dart +++ b/mobile/lib/ui/viewer/people/people_util.dart @@ -1,18 +1,16 @@ - - import "package:photos/core/configuration.dart"; import "package:photos/services/machine_learning/face_ml/person/person_service.dart"; Future isMeAssigned() async { - final personEntities = await PersonService.instance.getPersons(); - final currentUserEmail = Configuration.instance.getEmail(); + final personEntities = await PersonService.instance.getPersons(); + final currentUserEmail = Configuration.instance.getEmail(); - bool isAssigned = false; - for (final personEntity in personEntities) { - if (personEntity.data.email == currentUserEmail) { - isAssigned = true; - break; - } + bool isAssigned = false; + for (final personEntity in personEntities) { + if (personEntity.data.email == currentUserEmail) { + isAssigned = true; + break; } - return isAssigned; - } \ No newline at end of file + } + return isAssigned; +} diff --git a/mobile/lib/utils/face/face_box_crop.dart b/mobile/lib/utils/face/face_box_crop.dart index dd31884ea1..bd33026cdc 100644 --- a/mobile/lib/utils/face/face_box_crop.dart +++ b/mobile/lib/utils/face/face_box_crop.dart @@ -147,7 +147,8 @@ Future precomputeClusterFaceCrop( required bool useFullFile, }) async { try { - final w = (kDebugMode ? EnteWatch('precomputeClusterFaceCrop') : null)?..start(); + final w = (kDebugMode ? EnteWatch('precomputeClusterFaceCrop') : null) + ?..start(); final Face? face = await MLDataDB.instance.getCoverFaceForPerson( recentFileID: file.uploadedFileID!, clusterID: clusterID, diff --git a/mobile/lib/utils/file_uploader.dart b/mobile/lib/utils/file_uploader.dart index 5d78af6a06..1d16471560 100644 --- a/mobile/lib/utils/file_uploader.dart +++ b/mobile/lib/utils/file_uploader.dart @@ -23,7 +23,6 @@ import "package:photos/events/file_uploaded_event.dart"; import 'package:photos/events/files_updated_event.dart'; import 'package:photos/events/local_photos_updated_event.dart'; import 'package:photos/events/subscription_purchased_event.dart'; -import 'package:photos/main.dart'; import "package:photos/models/api/metadata.dart"; import "package:photos/models/backup/backup_item.dart"; import "package:photos/models/backup/backup_item_status.dart"; diff --git a/mobile/lib/utils/wakelock_util.dart b/mobile/lib/utils/wakelock_util.dart index 7e810dc0b3..f5851f1599 100644 --- a/mobile/lib/utils/wakelock_util.dart +++ b/mobile/lib/utils/wakelock_util.dart @@ -7,13 +7,13 @@ class EnteWakeLock { void enable() { WakelockPlus.enabled.then((value) { - if (value == false) { - WakelockPlus.enable(); - //wakeLockEnabledHere will not be set to true if wakeLock is already enabled from settings on iOS. - //We shouldn't disable when video is not playing if it was enabled manually by the user from ente settings by user. - _wakeLockEnabledHere = true; - } - }); + if (value == false) { + WakelockPlus.enable(); + //wakeLockEnabledHere will not be set to true if wakeLock is already enabled from settings on iOS. + //We shouldn't disable when video is not playing if it was enabled manually by the user from ente settings by user. + _wakeLockEnabledHere = true; + } + }); } void disable() { From 778822b12d15be46c1bd30f95609f0a529fab99f Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 31 Mar 2025 15:47:51 +0530 Subject: [PATCH 002/156] Upgrade sdk --- mobile/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index f13e088f89..29ecbc6e2a 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -16,7 +16,7 @@ version: 1.0.0+1031 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.3.0 <4.0.0" dependencies: adaptive_theme: ^3.1.0 From ddb44d8fd76f75be87e3bc104888b61e65a395ac Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 31 Mar 2025 15:56:03 +0530 Subject: [PATCH 003/156] Integrate flutter_rust_bridge --- mobile/flutter_rust_bridge.yaml | 3 + mobile/integration_test/simple_test.dart | 13 + mobile/lib/main.dart | 3 + mobile/lib/src/rust/api/simple.dart | 10 + mobile/lib/src/rust/frb_generated.dart | 249 +++++++ mobile/lib/src/rust/frb_generated.io.dart | 87 +++ mobile/lib/src/rust/frb_generated.web.dart | 87 +++ mobile/lib/utils/file_uploader.dart | 1 + mobile/pubspec.lock | 31 +- mobile/pubspec.yaml | 3 + mobile/rust/.gitignore | 1 + mobile/rust/Cargo.lock | 655 ++++++++++++++++++ mobile/rust/Cargo.toml | 13 + mobile/rust/src/api/mod.rs | 1 + mobile/rust/src/api/simple.rs | 10 + mobile/rust/src/frb_generated.rs | 276 ++++++++ mobile/rust/src/lib.rs | 2 + mobile/rust_builder/.gitignore | 29 + mobile/rust_builder/README.md | 1 + mobile/rust_builder/android/.gitignore | 9 + mobile/rust_builder/android/build.gradle | 56 ++ mobile/rust_builder/android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 3 + mobile/rust_builder/cargokit/.gitignore | 4 + mobile/rust_builder/cargokit/LICENSE | 42 ++ mobile/rust_builder/cargokit/README | 11 + mobile/rust_builder/cargokit/build_pod.sh | 58 ++ .../cargokit/build_tool/README.md | 5 + .../cargokit/build_tool/analysis_options.yaml | 34 + .../cargokit/build_tool/bin/build_tool.dart | 8 + .../cargokit/build_tool/lib/build_tool.dart | 8 + .../lib/src/android_environment.dart | 195 ++++++ .../lib/src/artifacts_provider.dart | 266 +++++++ .../build_tool/lib/src/build_cmake.dart | 40 ++ .../build_tool/lib/src/build_gradle.dart | 49 ++ .../build_tool/lib/src/build_pod.dart | 89 +++ .../build_tool/lib/src/build_tool.dart | 271 ++++++++ .../cargokit/build_tool/lib/src/builder.dart | 198 ++++++ .../cargokit/build_tool/lib/src/cargo.dart | 48 ++ .../build_tool/lib/src/crate_hash.dart | 124 ++++ .../build_tool/lib/src/environment.dart | 68 ++ .../cargokit/build_tool/lib/src/logging.dart | 52 ++ .../cargokit/build_tool/lib/src/options.dart | 309 +++++++++ .../lib/src/precompile_binaries.dart | 202 ++++++ .../cargokit/build_tool/lib/src/rustup.dart | 136 ++++ .../cargokit/build_tool/lib/src/target.dart | 140 ++++ .../cargokit/build_tool/lib/src/util.dart | 172 +++++ .../build_tool/lib/src/verify_binaries.dart | 84 +++ .../cargokit/build_tool/pubspec.lock | 453 ++++++++++++ .../cargokit/build_tool/pubspec.yaml | 33 + .../cargokit/cmake/cargokit.cmake | 99 +++ .../cargokit/cmake/resolve_symlinks.ps1 | 27 + .../cargokit/gradle/plugin.gradle | 179 +++++ .../rust_builder/cargokit/run_build_tool.cmd | 91 +++ .../rust_builder/cargokit/run_build_tool.sh | 94 +++ mobile/rust_builder/ios/Classes/dummy_file.c | 1 + .../rust_builder/ios/rust_lib_photos.podspec | 45 ++ mobile/rust_builder/linux/CMakeLists.txt | 19 + .../rust_builder/macos/Classes/dummy_file.c | 1 + .../macos/rust_lib_photos.podspec | 44 ++ mobile/rust_builder/pubspec.yaml | 34 + mobile/rust_builder/windows/.gitignore | 17 + mobile/rust_builder/windows/CMakeLists.txt | 20 + mobile/test_driver/integration_test.dart | 3 + 64 files changed, 5309 insertions(+), 8 deletions(-) create mode 100644 mobile/flutter_rust_bridge.yaml create mode 100644 mobile/integration_test/simple_test.dart create mode 100644 mobile/lib/src/rust/api/simple.dart create mode 100644 mobile/lib/src/rust/frb_generated.dart create mode 100644 mobile/lib/src/rust/frb_generated.io.dart create mode 100644 mobile/lib/src/rust/frb_generated.web.dart create mode 100644 mobile/rust/.gitignore create mode 100644 mobile/rust/Cargo.lock create mode 100644 mobile/rust/Cargo.toml create mode 100644 mobile/rust/src/api/mod.rs create mode 100644 mobile/rust/src/api/simple.rs create mode 100644 mobile/rust/src/frb_generated.rs create mode 100644 mobile/rust/src/lib.rs create mode 100644 mobile/rust_builder/.gitignore create mode 100644 mobile/rust_builder/README.md create mode 100644 mobile/rust_builder/android/.gitignore create mode 100644 mobile/rust_builder/android/build.gradle create mode 100644 mobile/rust_builder/android/settings.gradle create mode 100644 mobile/rust_builder/android/src/main/AndroidManifest.xml create mode 100644 mobile/rust_builder/cargokit/.gitignore create mode 100644 mobile/rust_builder/cargokit/LICENSE create mode 100644 mobile/rust_builder/cargokit/README create mode 100755 mobile/rust_builder/cargokit/build_pod.sh create mode 100644 mobile/rust_builder/cargokit/build_tool/README.md create mode 100644 mobile/rust_builder/cargokit/build_tool/analysis_options.yaml create mode 100644 mobile/rust_builder/cargokit/build_tool/bin/build_tool.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/build_tool.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/android_environment.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/build_pod.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/build_tool.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/builder.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/cargo.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/environment.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/logging.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/options.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/rustup.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/target.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/util.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart create mode 100644 mobile/rust_builder/cargokit/build_tool/pubspec.lock create mode 100644 mobile/rust_builder/cargokit/build_tool/pubspec.yaml create mode 100644 mobile/rust_builder/cargokit/cmake/cargokit.cmake create mode 100644 mobile/rust_builder/cargokit/cmake/resolve_symlinks.ps1 create mode 100644 mobile/rust_builder/cargokit/gradle/plugin.gradle create mode 100755 mobile/rust_builder/cargokit/run_build_tool.cmd create mode 100755 mobile/rust_builder/cargokit/run_build_tool.sh create mode 100644 mobile/rust_builder/ios/Classes/dummy_file.c create mode 100644 mobile/rust_builder/ios/rust_lib_photos.podspec create mode 100644 mobile/rust_builder/linux/CMakeLists.txt create mode 100644 mobile/rust_builder/macos/Classes/dummy_file.c create mode 100644 mobile/rust_builder/macos/rust_lib_photos.podspec create mode 100644 mobile/rust_builder/pubspec.yaml create mode 100644 mobile/rust_builder/windows/.gitignore create mode 100644 mobile/rust_builder/windows/CMakeLists.txt create mode 100644 mobile/test_driver/integration_test.dart diff --git a/mobile/flutter_rust_bridge.yaml b/mobile/flutter_rust_bridge.yaml new file mode 100644 index 0000000000..e15ed9167d --- /dev/null +++ b/mobile/flutter_rust_bridge.yaml @@ -0,0 +1,3 @@ +rust_input: crate::api +rust_root: rust/ +dart_output: lib/src/rust \ No newline at end of file diff --git a/mobile/integration_test/simple_test.dart b/mobile/integration_test/simple_test.dart new file mode 100644 index 0000000000..d55a2badac --- /dev/null +++ b/mobile/integration_test/simple_test.dart @@ -0,0 +1,13 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import "package:photos/src/rust/api/simple.dart"; +import 'package:photos/src/rust/frb_generated.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + setUpAll(() async => await RustLib.init()); + testWidgets('Can call rust function', (WidgetTester tester) async { + final testString = greet(name: "Tom"); + expect(testString.contains('Tom'), true); + }); +} diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index f3db763290..8b034b7ba3 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -45,6 +45,7 @@ import 'package:photos/services/search_service.dart'; import 'package:photos/services/sync/local_sync_service.dart'; import 'package:photos/services/sync/remote_sync_service.dart'; import "package:photos/services/sync/sync_service.dart"; +import "package:photos/src/rust/frb_generated.dart"; import 'package:photos/ui/tools/app_lock.dart'; import 'package:photos/ui/tools/lock_screen.dart'; import "package:photos/utils/email_util.dart"; @@ -66,6 +67,7 @@ const kFGTaskDeathTimeoutInMicroseconds = 5000000; void main() async { debugRepaintRainbowEnabled = false; + await RustLib.init(); WidgetsFlutterBinding.ensureInitialized(); //For audio to work on vidoes in iOS when in silent mode. if (Platform.isIOS) { @@ -442,3 +444,4 @@ void _scheduleSuicide(Duration duration, [String? taskID]) { _killBGTask(taskID); }); } + diff --git a/mobile/lib/src/rust/api/simple.dart b/mobile/lib/src/rust/api/simple.dart new file mode 100644 index 0000000000..5f6b233313 --- /dev/null +++ b/mobile/lib/src/rust/api/simple.dart @@ -0,0 +1,10 @@ +// This file is automatically generated, so please do not edit it. +// @generated by `flutter_rust_bridge`@ 2.9.0. + +// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import + +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; +import 'package:photos/src/rust/frb_generated.dart'; + +String greet({required String name}) => + RustLib.instance.api.crateApiSimpleGreet(name: name); diff --git a/mobile/lib/src/rust/frb_generated.dart b/mobile/lib/src/rust/frb_generated.dart new file mode 100644 index 0000000000..ee5f0f4ec9 --- /dev/null +++ b/mobile/lib/src/rust/frb_generated.dart @@ -0,0 +1,249 @@ +// This file is automatically generated, so please do not edit it. +// @generated by `flutter_rust_bridge`@ 2.9.0. + +// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field + +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; +import 'package:photos/src/rust/api/simple.dart'; +import 'package:photos/src/rust/frb_generated.dart'; +import 'package:photos/src/rust/frb_generated.io.dart' + if (dart.library.js_interop) 'frb_generated.web.dart'; + +/// Main entrypoint of the Rust API +class RustLib extends BaseEntrypoint { + @internal + static final instance = RustLib._(); + + RustLib._(); + + /// Initialize flutter_rust_bridge + static Future init({ + RustLibApi? api, + BaseHandler? handler, + ExternalLibrary? externalLibrary, + }) async { + await instance.initImpl( + api: api, + handler: handler, + externalLibrary: externalLibrary, + ); + } + + /// Initialize flutter_rust_bridge in mock mode. + /// No libraries for FFI are loaded. + static void initMock({ + required RustLibApi api, + }) { + instance.initMockImpl( + api: api, + ); + } + + /// Dispose flutter_rust_bridge + /// + /// The call to this function is optional, since flutter_rust_bridge (and everything else) + /// is automatically disposed when the app stops. + static void dispose() => instance.disposeImpl(); + + @override + ApiImplConstructor get apiImplConstructor => + RustLibApiImpl.new; + + @override + WireConstructor get wireConstructor => + RustLibWire.fromExternalLibrary; + + @override + Future executeRustInitializers() async { + await api.crateApiSimpleInitApp(); + } + + @override + ExternalLibraryLoaderConfig get defaultExternalLibraryLoaderConfig => + kDefaultExternalLibraryLoaderConfig; + + @override + String get codegenVersion => '2.9.0'; + + @override + int get rustContentHash => -1918914929; + + static const kDefaultExternalLibraryLoaderConfig = + ExternalLibraryLoaderConfig( + stem: 'rust_lib_photos', + ioDirectory: 'rust/target/release/', + webPrefix: 'pkg/', + ); +} + +abstract class RustLibApi extends BaseApi { + String crateApiSimpleGreet({required String name}); + + Future crateApiSimpleInitApp(); +} + +class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { + RustLibApiImpl({ + required super.handler, + required super.wire, + required super.generalizedFrbRustBinding, + required super.portManager, + }); + + @override + String crateApiSimpleGreet({required String name}) { + return handler.executeSync( + SyncTask( + callFfi: () { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_String(name, serializer); + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 1)!; + }, + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: null, + ), + constMeta: kCrateApiSimpleGreetConstMeta, + argValues: [name], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiSimpleGreetConstMeta => const TaskConstMeta( + debugName: "greet", + argNames: ["name"], + ); + + @override + Future crateApiSimpleInitApp() { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 2, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: null, + ), + constMeta: kCrateApiSimpleInitAppConstMeta, + argValues: [], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiSimpleInitAppConstMeta => const TaskConstMeta( + debugName: "init_app", + argNames: [], + ); + + @protected + String dco_decode_String(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw as String; + } + + @protected + Uint8List dco_decode_list_prim_u_8_strict(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw as Uint8List; + } + + @protected + int dco_decode_u_8(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw as int; + } + + @protected + void dco_decode_unit(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return; + } + + @protected + String sse_decode_String(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + final inner = sse_decode_list_prim_u_8_strict(deserializer); + return utf8.decoder.convert(inner); + } + + @protected + Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + final len_ = sse_decode_i_32(deserializer); + return deserializer.buffer.getUint8List(len_); + } + + @protected + int sse_decode_u_8(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return deserializer.buffer.getUint8(); + } + + @protected + void sse_decode_unit(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + } + + @protected + int sse_decode_i_32(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return deserializer.buffer.getInt32(); + } + + @protected + bool sse_decode_bool(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return deserializer.buffer.getUint8() != 0; + } + + @protected + void sse_encode_String(String self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_list_prim_u_8_strict(utf8.encoder.convert(self), serializer); + } + + @protected + void sse_encode_list_prim_u_8_strict( + Uint8List self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_i_32(self.length, serializer); + serializer.buffer.putUint8List(self); + } + + @protected + void sse_encode_u_8(int self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + serializer.buffer.putUint8(self); + } + + @protected + void sse_encode_unit(void self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + } + + @protected + void sse_encode_i_32(int self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + serializer.buffer.putInt32(self); + } + + @protected + void sse_encode_bool(bool self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + serializer.buffer.putUint8(self ? 1 : 0); + } +} diff --git a/mobile/lib/src/rust/frb_generated.io.dart b/mobile/lib/src/rust/frb_generated.io.dart new file mode 100644 index 0000000000..99f64b1ce5 --- /dev/null +++ b/mobile/lib/src/rust/frb_generated.io.dart @@ -0,0 +1,87 @@ +// This file is automatically generated, so please do not edit it. +// @generated by `flutter_rust_bridge`@ 2.9.0. + +// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field + +import 'dart:async'; +import 'dart:convert'; +import 'dart:ffi' as ffi; + +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_io.dart'; +import 'package:photos/src/rust/api/simple.dart'; +import 'package:photos/src/rust/frb_generated.dart'; + +abstract class RustLibApiImplPlatform extends BaseApiImpl { + RustLibApiImplPlatform({ + required super.handler, + required super.wire, + required super.generalizedFrbRustBinding, + required super.portManager, + }); + + @protected + String dco_decode_String(dynamic raw); + + @protected + Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); + + @protected + int dco_decode_u_8(dynamic raw); + + @protected + void dco_decode_unit(dynamic raw); + + @protected + String sse_decode_String(SseDeserializer deserializer); + + @protected + Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); + + @protected + int sse_decode_u_8(SseDeserializer deserializer); + + @protected + void sse_decode_unit(SseDeserializer deserializer); + + @protected + int sse_decode_i_32(SseDeserializer deserializer); + + @protected + bool sse_decode_bool(SseDeserializer deserializer); + + @protected + void sse_encode_String(String self, SseSerializer serializer); + + @protected + void sse_encode_list_prim_u_8_strict( + Uint8List self, + SseSerializer serializer, + ); + + @protected + void sse_encode_u_8(int self, SseSerializer serializer); + + @protected + void sse_encode_unit(void self, SseSerializer serializer); + + @protected + void sse_encode_i_32(int self, SseSerializer serializer); + + @protected + void sse_encode_bool(bool self, SseSerializer serializer); +} + +// Section: wire_class + +class RustLibWire implements BaseWire { + factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) => + RustLibWire(lib.ffiDynamicLibrary); + + /// Holds the symbol lookup function. + final ffi.Pointer Function(String symbolName) + _lookup; + + /// The symbols are looked up in [dynamicLibrary]. + RustLibWire(ffi.DynamicLibrary dynamicLibrary) + : _lookup = dynamicLibrary.lookup; +} diff --git a/mobile/lib/src/rust/frb_generated.web.dart b/mobile/lib/src/rust/frb_generated.web.dart new file mode 100644 index 0000000000..7c87e7b38c --- /dev/null +++ b/mobile/lib/src/rust/frb_generated.web.dart @@ -0,0 +1,87 @@ +// This file is automatically generated, so please do not edit it. +// @generated by `flutter_rust_bridge`@ 2.9.0. + +// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field + +// Static analysis wrongly picks the IO variant, thus ignore this +// ignore_for_file: argument_type_not_assignable + +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_web.dart'; +import 'package:photos/src/rust/api/simple.dart'; +import 'package:photos/src/rust/frb_generated.dart'; + +abstract class RustLibApiImplPlatform extends BaseApiImpl { + RustLibApiImplPlatform({ + required super.handler, + required super.wire, + required super.generalizedFrbRustBinding, + required super.portManager, + }); + + @protected + String dco_decode_String(dynamic raw); + + @protected + Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); + + @protected + int dco_decode_u_8(dynamic raw); + + @protected + void dco_decode_unit(dynamic raw); + + @protected + String sse_decode_String(SseDeserializer deserializer); + + @protected + Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); + + @protected + int sse_decode_u_8(SseDeserializer deserializer); + + @protected + void sse_decode_unit(SseDeserializer deserializer); + + @protected + int sse_decode_i_32(SseDeserializer deserializer); + + @protected + bool sse_decode_bool(SseDeserializer deserializer); + + @protected + void sse_encode_String(String self, SseSerializer serializer); + + @protected + void sse_encode_list_prim_u_8_strict( + Uint8List self, + SseSerializer serializer, + ); + + @protected + void sse_encode_u_8(int self, SseSerializer serializer); + + @protected + void sse_encode_unit(void self, SseSerializer serializer); + + @protected + void sse_encode_i_32(int self, SseSerializer serializer); + + @protected + void sse_encode_bool(bool self, SseSerializer serializer); +} + +// Section: wire_class + +class RustLibWire implements BaseWire { + RustLibWire.fromExternalLibrary(ExternalLibrary lib); +} + +@JS('wasm_bindgen') +external RustLibWasmModule get wasmModule; + +@JS() +@anonymous +extension type RustLibWasmModule._(JSObject _) implements JSObject {} diff --git a/mobile/lib/utils/file_uploader.dart b/mobile/lib/utils/file_uploader.dart index 1d16471560..e7f43e370e 100644 --- a/mobile/lib/utils/file_uploader.dart +++ b/mobile/lib/utils/file_uploader.dart @@ -23,6 +23,7 @@ import "package:photos/events/file_uploaded_event.dart"; import 'package:photos/events/files_updated_event.dart'; import 'package:photos/events/local_photos_updated_event.dart'; import 'package:photos/events/subscription_purchased_event.dart'; +import "package:photos/main.dart"; import "package:photos/models/api/metadata.dart"; import "package:photos/models/backup/backup_item.dart"; import "package:photos/models/backup/backup_item_status.dart"; diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 5c0b18ab16..8eb7823784 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -151,6 +151,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.1" + build_cli_annotations: + dependency: transitive + description: + name: build_cli_annotations + sha256: b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172 + url: "https://pub.dev" + source: hosted + version: "2.1.0" build_config: dependency: transitive description: @@ -780,14 +788,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.1" - flutter_datetime_picker_bdaya: - dependency: "direct main" - description: - name: flutter_datetime_picker_bdaya - sha256: "6cb42a7d659cb9a10afa0a390b81677e15fd2f7e340e7eaffabf770e6b6709c0" - url: "https://pub.dev" - source: hosted - version: "3.0.2" flutter_displaymode: dependency: "direct main" description: @@ -1022,6 +1022,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.24" + flutter_rust_bridge: + dependency: "direct main" + description: + name: flutter_rust_bridge + sha256: "5a5c7a5deeef2cc2ffe6076a33b0429f4a20ceac22a397297aed2b1eb067e611" + url: "https://pub.dev" + source: hosted + version: "2.9.0" flutter_secure_storage: dependency: "direct main" description: @@ -2216,6 +2224,13 @@ packages: url: "https://github.com/KasemJaffer/receive_sharing_intent.git" source: git version: "1.8.1" + rust_lib_photos: + dependency: "direct main" + description: + path: rust_builder + relative: true + source: path + version: "0.0.1" rxdart: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 29ecbc6e2a..5da8e4f789 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -93,6 +93,7 @@ dependencies: flutter_map_marker_cluster: ^1.3.6 flutter_native_splash: ^2.4.4 flutter_password_strength: ^0.1.6 + flutter_rust_bridge: 2.9.0 flutter_secure_storage: ^9.2.4 flutter_sodium: flutter_staggered_grid_view: ^0.6.2 @@ -173,6 +174,8 @@ dependencies: receive_sharing_intent: # update source if there is any update git: url: https://github.com/KasemJaffer/receive_sharing_intent.git + rust_lib_photos: + path: rust_builder screenshot: ^3.0.0 scrollable_positioned_list: ^0.3.5 sentry: ^8.13.2 diff --git a/mobile/rust/.gitignore b/mobile/rust/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/mobile/rust/.gitignore @@ -0,0 +1 @@ +/target diff --git a/mobile/rust/Cargo.lock b/mobile/rust/Cargo.lock new file mode 100644 index 0000000000..430c41215d --- /dev/null +++ b/mobile/rust/Cargo.lock @@ -0,0 +1,655 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "allo-isolate" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b6d794345b06592d0ebeed8e477e41b71e5a0a49df4fc0e4184d5938b99509" +dependencies = [ + "anyhow", + "atomic", + "backtrace", +] + +[[package]] +name = "android_log-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937" + +[[package]] +name = "android_logger" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c494134f746c14dc653a35a4ea5aca24ac368529da5370ecf41fe0341c35772f" +dependencies = [ + "android_log-sys", + "env_logger", + "log", + "once_cell", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "build-target" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "832133bbabbbaa9fbdba793456a2827627a7d2b8fb96032fa1e7666d7895832b" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytemuck" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "dart-sys-fork" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "933dafff26172b719bb9695dd3715a1e7792f62dcdc8a5d4c740db7e0fedee8b" +dependencies = [ + "cc", +] + +[[package]] +name = "dashmap" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +dependencies = [ + "cfg-if", + "num_cpus", +] + +[[package]] +name = "delegate-attr" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51aac4c99b2e6775164b412ea33ae8441b2fde2dbf05a20bc0052a63d08c475b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "env_logger" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "flutter_rust_bridge" +version = "2.9.0" +dependencies = [ + "allo-isolate", + "android_logger", + "anyhow", + "build-target", + "bytemuck", + "byteorder", + "console_error_panic_hook", + "dart-sys-fork", + "delegate-attr", + "flutter_rust_bridge_macros", + "futures", + "js-sys", + "lazy_static", + "log", + "oslog", + "threadpool", + "tokio", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "flutter_rust_bridge_macros" +version = "2.9.0" +dependencies = [ + "hex", + "md-5", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-executor" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-macro" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "oslog" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8343ce955f18e7e68c0207dd0ea776ec453035685395ababd2ea651c569728b3" +dependencies = [ + "cc", + "dashmap", + "log", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rust_lib_photos" +version = "0.1.0" +dependencies = [ + "flutter_rust_bridge", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "tokio" +version = "1.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +dependencies = [ + "backtrace", + "num_cpus", + "pin-project-lite", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] diff --git a/mobile/rust/Cargo.toml b/mobile/rust/Cargo.toml new file mode 100644 index 0000000000..688aa65364 --- /dev/null +++ b/mobile/rust/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rust_lib_photos" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib", "staticlib"] + +[dependencies] +flutter_rust_bridge = "=2.9.0" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] } diff --git a/mobile/rust/src/api/mod.rs b/mobile/rust/src/api/mod.rs new file mode 100644 index 0000000000..b252f36bf9 --- /dev/null +++ b/mobile/rust/src/api/mod.rs @@ -0,0 +1 @@ +pub mod simple; diff --git a/mobile/rust/src/api/simple.rs b/mobile/rust/src/api/simple.rs new file mode 100644 index 0000000000..4360c82ae0 --- /dev/null +++ b/mobile/rust/src/api/simple.rs @@ -0,0 +1,10 @@ +#[flutter_rust_bridge::frb(sync)] // Synchronous mode for simplicity of the demo +pub fn greet(name: String) -> String { + format!("Hello, {name}!") +} + +#[flutter_rust_bridge::frb(init)] +pub fn init_app() { + // Default utilities - feel free to customize + flutter_rust_bridge::setup_default_user_utils(); +} diff --git a/mobile/rust/src/frb_generated.rs b/mobile/rust/src/frb_generated.rs new file mode 100644 index 0000000000..81c7f476de --- /dev/null +++ b/mobile/rust/src/frb_generated.rs @@ -0,0 +1,276 @@ +// This file is automatically generated, so please do not edit it. +// @generated by `flutter_rust_bridge`@ 2.9.0. + +#![allow( + non_camel_case_types, + unused, + non_snake_case, + clippy::needless_return, + clippy::redundant_closure_call, + clippy::redundant_closure, + clippy::useless_conversion, + clippy::unit_arg, + clippy::unused_unit, + clippy::double_parens, + clippy::let_and_return, + clippy::too_many_arguments, + clippy::match_single_binding, + clippy::clone_on_copy, + clippy::let_unit_value, + clippy::deref_addrof, + clippy::explicit_auto_deref, + clippy::borrow_deref_ref, + clippy::needless_borrow +)] + +// Section: imports + +use flutter_rust_bridge::for_generated::byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; +use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable}; +use flutter_rust_bridge::{Handler, IntoIntoDart}; + +// Section: boilerplate + +flutter_rust_bridge::frb_generated_boilerplate!( + default_stream_sink_codec = SseCodec, + default_rust_opaque = RustOpaqueMoi, + default_rust_auto_opaque = RustAutoOpaqueMoi, +); +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.9.0"; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1918914929; + +// Section: executor + +flutter_rust_bridge::frb_generated_default_handler!(); + +// Section: wire_funcs + +fn wire__crate__api__simple__greet_impl( + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_sync::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "greet", + port: None, + mode: flutter_rust_bridge::for_generated::FfiCallMode::Sync, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_name = ::sse_decode(&mut deserializer); + deserializer.end(); + transform_result_sse::<_, ()>((move || { + let output_ok = Result::<_, ()>::Ok(crate::api::simple::greet(api_name))?; + Ok(output_ok) + })()) + }, + ) +} +fn wire__crate__api__simple__init_app_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "init_app", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + deserializer.end(); + move |context| { + transform_result_sse::<_, ()>((move || { + let output_ok = Result::<_, ()>::Ok({ + crate::api::simple::init_app(); + })?; + Ok(output_ok) + })()) + } + }, + ) +} + +// Section: dart2rust + +impl SseDecode for String { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut inner = >::sse_decode(deserializer); + return String::from_utf8(inner).unwrap(); + } +} + +impl SseDecode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut len_ = ::sse_decode(deserializer); + let mut ans_ = vec![]; + for idx_ in 0..len_ { + ans_.push(::sse_decode(deserializer)); + } + return ans_; + } +} + +impl SseDecode for u8 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_u8().unwrap() + } +} + +impl SseDecode for () { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {} +} + +impl SseDecode for i32 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_i32::().unwrap() + } +} + +impl SseDecode for bool { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_u8().unwrap() != 0 + } +} + +fn pde_ffi_dispatcher_primary_impl( + func_id: i32, + port: flutter_rust_bridge::for_generated::MessagePort, + ptr: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len: i32, + data_len: i32, +) { + // Codec=Pde (Serialization + dispatch), see doc to use other codecs + match func_id { + 2 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), + _ => unreachable!(), + } +} + +fn pde_ffi_dispatcher_sync_impl( + func_id: i32, + ptr: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len: i32, + data_len: i32, +) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { + // Codec=Pde (Serialization + dispatch), see doc to use other codecs + match func_id { + 1 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), + _ => unreachable!(), + } +} + +// Section: rust2dart + +impl SseEncode for String { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + >::sse_encode(self.into_bytes(), serializer); + } +} + +impl SseEncode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.len() as _, serializer); + for item in self { + ::sse_encode(item, serializer); + } + } +} + +impl SseEncode for u8 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_u8(self).unwrap(); + } +} + +impl SseEncode for () { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {} +} + +impl SseEncode for i32 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_i32::(self).unwrap(); + } +} + +impl SseEncode for bool { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_u8(self as _).unwrap(); + } +} + +#[cfg(not(target_family = "wasm"))] +mod io { + // This file is automatically generated, so please do not edit it. + // @generated by `flutter_rust_bridge`@ 2.9.0. + + // Section: imports + + use super::*; + use flutter_rust_bridge::for_generated::byteorder::{ + NativeEndian, ReadBytesExt, WriteBytesExt, + }; + use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable}; + use flutter_rust_bridge::{Handler, IntoIntoDart}; + + // Section: boilerplate + + flutter_rust_bridge::frb_generated_boilerplate_io!(); +} +#[cfg(not(target_family = "wasm"))] +pub use io::*; + +/// cbindgen:ignore +#[cfg(target_family = "wasm")] +mod web { + // This file is automatically generated, so please do not edit it. + // @generated by `flutter_rust_bridge`@ 2.9.0. + + // Section: imports + + use super::*; + use flutter_rust_bridge::for_generated::byteorder::{ + NativeEndian, ReadBytesExt, WriteBytesExt, + }; + use flutter_rust_bridge::for_generated::wasm_bindgen; + use flutter_rust_bridge::for_generated::wasm_bindgen::prelude::*; + use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable}; + use flutter_rust_bridge::{Handler, IntoIntoDart}; + + // Section: boilerplate + + flutter_rust_bridge::frb_generated_boilerplate_web!(); +} +#[cfg(target_family = "wasm")] +pub use web::*; diff --git a/mobile/rust/src/lib.rs b/mobile/rust/src/lib.rs new file mode 100644 index 0000000000..cbb071f8bf --- /dev/null +++ b/mobile/rust/src/lib.rs @@ -0,0 +1,2 @@ +pub mod api; +mod frb_generated; diff --git a/mobile/rust_builder/.gitignore b/mobile/rust_builder/.gitignore new file mode 100644 index 0000000000..ac5aa9893e --- /dev/null +++ b/mobile/rust_builder/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/mobile/rust_builder/README.md b/mobile/rust_builder/README.md new file mode 100644 index 0000000000..922615f9c1 --- /dev/null +++ b/mobile/rust_builder/README.md @@ -0,0 +1 @@ +Please ignore this folder, which is just glue to build Rust with Flutter. \ No newline at end of file diff --git a/mobile/rust_builder/android/.gitignore b/mobile/rust_builder/android/.gitignore new file mode 100644 index 0000000000..161bdcdaf8 --- /dev/null +++ b/mobile/rust_builder/android/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.cxx diff --git a/mobile/rust_builder/android/build.gradle b/mobile/rust_builder/android/build.gradle new file mode 100644 index 0000000000..5239261a2e --- /dev/null +++ b/mobile/rust_builder/android/build.gradle @@ -0,0 +1,56 @@ +// The Android Gradle Plugin builds the native code with the Android NDK. + +group 'com.flutter_rust_bridge.rust_lib_photos' +version '1.0' + +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + // The Android Gradle Plugin knows how to build native code with the NDK. + classpath 'com.android.tools.build:gradle:7.3.0' + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' + +android { + if (project.android.hasProperty("namespace")) { + namespace 'com.flutter_rust_bridge.rust_lib_photos' + } + + // Bumping the plugin compileSdkVersion requires all clients of this plugin + // to bump the version in their app. + compileSdkVersion 33 + + // Use the NDK version + // declared in /android/app/build.gradle file of the Flutter project. + // Replace it with a version number if this plugin requires a specfic NDK version. + // (e.g. ndkVersion "23.1.7779620") + ndkVersion android.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + minSdkVersion 19 + } +} + +apply from: "../cargokit/gradle/plugin.gradle" +cargokit { + manifestDir = "../../rust" + libname = "rust_lib_photos" +} diff --git a/mobile/rust_builder/android/settings.gradle b/mobile/rust_builder/android/settings.gradle new file mode 100644 index 0000000000..48e7a64ca7 --- /dev/null +++ b/mobile/rust_builder/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'rust_lib_photos' diff --git a/mobile/rust_builder/android/src/main/AndroidManifest.xml b/mobile/rust_builder/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..b80e4df687 --- /dev/null +++ b/mobile/rust_builder/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/mobile/rust_builder/cargokit/.gitignore b/mobile/rust_builder/cargokit/.gitignore new file mode 100644 index 0000000000..cf7bb868c0 --- /dev/null +++ b/mobile/rust_builder/cargokit/.gitignore @@ -0,0 +1,4 @@ +target +.dart_tool +*.iml +!pubspec.lock diff --git a/mobile/rust_builder/cargokit/LICENSE b/mobile/rust_builder/cargokit/LICENSE new file mode 100644 index 0000000000..d33a5fea52 --- /dev/null +++ b/mobile/rust_builder/cargokit/LICENSE @@ -0,0 +1,42 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +Copyright 2022 Matej Knopp + +================================================================================ + +MIT LICENSE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +================================================================================ + +APACHE LICENSE, VERSION 2.0 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/mobile/rust_builder/cargokit/README b/mobile/rust_builder/cargokit/README new file mode 100644 index 0000000000..398474dbc8 --- /dev/null +++ b/mobile/rust_builder/cargokit/README @@ -0,0 +1,11 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +Experimental repository to provide glue for seamlessly integrating cargo build +with flutter plugins and packages. + +See https://matejknopp.com/post/flutter_plugin_in_rust_with_no_prebuilt_binaries/ +for a tutorial on how to use Cargokit. + +Example plugin available at https://github.com/irondash/hello_rust_ffi_plugin. + diff --git a/mobile/rust_builder/cargokit/build_pod.sh b/mobile/rust_builder/cargokit/build_pod.sh new file mode 100755 index 0000000000..ed0e0d987d --- /dev/null +++ b/mobile/rust_builder/cargokit/build_pod.sh @@ -0,0 +1,58 @@ +#!/bin/sh +set -e + +BASEDIR=$(dirname "$0") + +# Workaround for https://github.com/dart-lang/pub/issues/4010 +BASEDIR=$(cd "$BASEDIR" ; pwd -P) + +# Remove XCode SDK from path. Otherwise this breaks tool compilation when building iOS project +NEW_PATH=`echo $PATH | tr ":" "\n" | grep -v "Contents/Developer/" | tr "\n" ":"` + +export PATH=${NEW_PATH%?} # remove trailing : + +env + +# Platform name (macosx, iphoneos, iphonesimulator) +export CARGOKIT_DARWIN_PLATFORM_NAME=$PLATFORM_NAME + +# Arctive architectures (arm64, armv7, x86_64), space separated. +export CARGOKIT_DARWIN_ARCHS=$ARCHS + +# Current build configuration (Debug, Release) +export CARGOKIT_CONFIGURATION=$CONFIGURATION + +# Path to directory containing Cargo.toml. +export CARGOKIT_MANIFEST_DIR=$PODS_TARGET_SRCROOT/$1 + +# Temporary directory for build artifacts. +export CARGOKIT_TARGET_TEMP_DIR=$TARGET_TEMP_DIR + +# Output directory for final artifacts. +export CARGOKIT_OUTPUT_DIR=$PODS_CONFIGURATION_BUILD_DIR/$PRODUCT_NAME + +# Directory to store built tool artifacts. +export CARGOKIT_TOOL_TEMP_DIR=$TARGET_TEMP_DIR/build_tool + +# Directory inside root project. Not necessarily the top level directory of root project. +export CARGOKIT_ROOT_PROJECT_DIR=$SRCROOT + +FLUTTER_EXPORT_BUILD_ENVIRONMENT=( + "$PODS_ROOT/../Flutter/ephemeral/flutter_export_environment.sh" # macOS + "$PODS_ROOT/../Flutter/flutter_export_environment.sh" # iOS +) + +for path in "${FLUTTER_EXPORT_BUILD_ENVIRONMENT[@]}" +do + if [[ -f "$path" ]]; then + source "$path" + fi +done + +sh "$BASEDIR/run_build_tool.sh" build-pod "$@" + +# Make a symlink from built framework to phony file, which will be used as input to +# build script. This should force rebuild (podspec currently doesn't support alwaysOutOfDate +# attribute on custom build phase) +ln -fs "$OBJROOT/XCBuildData/build.db" "${BUILT_PRODUCTS_DIR}/cargokit_phony" +ln -fs "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}" "${BUILT_PRODUCTS_DIR}/cargokit_phony_out" diff --git a/mobile/rust_builder/cargokit/build_tool/README.md b/mobile/rust_builder/cargokit/build_tool/README.md new file mode 100644 index 0000000000..a878c27964 --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/README.md @@ -0,0 +1,5 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +A sample command-line application with an entrypoint in `bin/`, library code +in `lib/`, and example unit test in `test/`. diff --git a/mobile/rust_builder/cargokit/build_tool/analysis_options.yaml b/mobile/rust_builder/cargokit/build_tool/analysis_options.yaml new file mode 100644 index 0000000000..0e16a8b092 --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/analysis_options.yaml @@ -0,0 +1,34 @@ +# This is copied from Cargokit (which is the official way to use it currently) +# Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +linter: + rules: + - prefer_relative_imports + - directives_ordering + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/mobile/rust_builder/cargokit/build_tool/bin/build_tool.dart b/mobile/rust_builder/cargokit/build_tool/bin/build_tool.dart new file mode 100644 index 0000000000..268eb524dc --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/bin/build_tool.dart @@ -0,0 +1,8 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'package:build_tool/build_tool.dart' as build_tool; + +void main(List arguments) { + build_tool.runMain(arguments); +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/build_tool.dart b/mobile/rust_builder/cargokit/build_tool/lib/build_tool.dart new file mode 100644 index 0000000000..7c1bb750a4 --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/build_tool.dart @@ -0,0 +1,8 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'src/build_tool.dart' as build_tool; + +Future runMain(List args) async { + return build_tool.runMain(args); +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/android_environment.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/android_environment.dart new file mode 100644 index 0000000000..15fc9eedac --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/android_environment.dart @@ -0,0 +1,195 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; +import 'dart:isolate'; +import 'dart:math' as math; + +import 'package:collection/collection.dart'; +import 'package:path/path.dart' as path; +import 'package:version/version.dart'; + +import 'target.dart'; +import 'util.dart'; + +class AndroidEnvironment { + AndroidEnvironment({ + required this.sdkPath, + required this.ndkVersion, + required this.minSdkVersion, + required this.targetTempDir, + required this.target, + }); + + static void clangLinkerWrapper(List args) { + final clang = Platform.environment['_CARGOKIT_NDK_LINK_CLANG']; + if (clang == null) { + throw Exception( + "cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_CLANG env var"); + } + final target = Platform.environment['_CARGOKIT_NDK_LINK_TARGET']; + if (target == null) { + throw Exception( + "cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_TARGET env var"); + } + + runCommand(clang, [ + target, + ...args, + ]); + } + + /// Full path to Android SDK. + final String sdkPath; + + /// Full version of Android NDK. + final String ndkVersion; + + /// Minimum supported SDK version. + final int minSdkVersion; + + /// Target directory for build artifacts. + final String targetTempDir; + + /// Target being built. + final Target target; + + bool ndkIsInstalled() { + final ndkPath = path.join(sdkPath, 'ndk', ndkVersion); + final ndkPackageXml = File(path.join(ndkPath, 'package.xml')); + return ndkPackageXml.existsSync(); + } + + void installNdk({ + required String javaHome, + }) { + final sdkManagerExtension = Platform.isWindows ? '.bat' : ''; + final sdkManager = path.join( + sdkPath, + 'cmdline-tools', + 'latest', + 'bin', + 'sdkmanager$sdkManagerExtension', + ); + + log.info('Installing NDK $ndkVersion'); + runCommand(sdkManager, [ + '--install', + 'ndk;$ndkVersion', + ], environment: { + 'JAVA_HOME': javaHome, + }); + } + + Future> buildEnvironment() async { + final hostArch = Platform.isMacOS + ? "darwin-x86_64" + : (Platform.isLinux ? "linux-x86_64" : "windows-x86_64"); + + final ndkPath = path.join(sdkPath, 'ndk', ndkVersion); + final toolchainPath = path.join( + ndkPath, + 'toolchains', + 'llvm', + 'prebuilt', + hostArch, + 'bin', + ); + + final minSdkVersion = + math.max(target.androidMinSdkVersion!, this.minSdkVersion); + + final exe = Platform.isWindows ? '.exe' : ''; + + final arKey = 'AR_${target.rust}'; + final arValue = ['${target.rust}-ar', 'llvm-ar', 'llvm-ar.exe'] + .map((e) => path.join(toolchainPath, e)) + .firstWhereOrNull((element) => File(element).existsSync()); + if (arValue == null) { + throw Exception('Failed to find ar for $target in $toolchainPath'); + } + + final targetArg = '--target=${target.rust}$minSdkVersion'; + + final ccKey = 'CC_${target.rust}'; + final ccValue = path.join(toolchainPath, 'clang$exe'); + final cfFlagsKey = 'CFLAGS_${target.rust}'; + final cFlagsValue = targetArg; + + final cxxKey = 'CXX_${target.rust}'; + final cxxValue = path.join(toolchainPath, 'clang++$exe'); + final cxxFlagsKey = 'CXXFLAGS_${target.rust}'; + final cxxFlagsValue = targetArg; + + final linkerKey = + 'cargo_target_${target.rust.replaceAll('-', '_')}_linker'.toUpperCase(); + + final ranlibKey = 'RANLIB_${target.rust}'; + final ranlibValue = path.join(toolchainPath, 'llvm-ranlib$exe'); + + final ndkVersionParsed = Version.parse(ndkVersion); + final rustFlagsKey = 'CARGO_ENCODED_RUSTFLAGS'; + final rustFlagsValue = _libGccWorkaround(targetTempDir, ndkVersionParsed); + + final runRustTool = + Platform.isWindows ? 'run_build_tool.cmd' : 'run_build_tool.sh'; + + final packagePath = (await Isolate.resolvePackageUri( + Uri.parse('package:build_tool/buildtool.dart')))! + .toFilePath(); + final selfPath = path.canonicalize(path.join( + packagePath, + '..', + '..', + '..', + runRustTool, + )); + + // Make sure that run_build_tool is working properly even initially launched directly + // through dart run. + final toolTempDir = + Platform.environment['CARGOKIT_TOOL_TEMP_DIR'] ?? targetTempDir; + + return { + arKey: arValue, + ccKey: ccValue, + cfFlagsKey: cFlagsValue, + cxxKey: cxxValue, + cxxFlagsKey: cxxFlagsValue, + ranlibKey: ranlibValue, + rustFlagsKey: rustFlagsValue, + linkerKey: selfPath, + // Recognized by main() so we know when we're acting as a wrapper + '_CARGOKIT_NDK_LINK_TARGET': targetArg, + '_CARGOKIT_NDK_LINK_CLANG': ccValue, + 'CARGOKIT_TOOL_TEMP_DIR': toolTempDir, + }; + } + + // Workaround for libgcc missing in NDK23, inspired by cargo-ndk + String _libGccWorkaround(String buildDir, Version ndkVersion) { + final workaroundDir = path.join( + buildDir, + 'cargokit', + 'libgcc_workaround', + '${ndkVersion.major}', + ); + Directory(workaroundDir).createSync(recursive: true); + if (ndkVersion.major >= 23) { + File(path.join(workaroundDir, 'libgcc.a')) + .writeAsStringSync('INPUT(-lunwind)'); + } else { + // Other way around, untested, forward libgcc.a from libunwind once Rust + // gets updated for NDK23+. + File(path.join(workaroundDir, 'libunwind.a')) + .writeAsStringSync('INPUT(-lgcc)'); + } + + var rustFlags = Platform.environment['CARGO_ENCODED_RUSTFLAGS'] ?? ''; + if (rustFlags.isNotEmpty) { + rustFlags = '$rustFlags\x1f'; + } + rustFlags = '$rustFlags-L\x1f$workaroundDir'; + return rustFlags; + } +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart new file mode 100644 index 0000000000..e608cece73 --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart @@ -0,0 +1,266 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:http/http.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'builder.dart'; +import 'crate_hash.dart'; +import 'options.dart'; +import 'precompile_binaries.dart'; +import 'rustup.dart'; +import 'target.dart'; + +class Artifact { + /// File system location of the artifact. + final String path; + + /// Actual file name that the artifact should have in destination folder. + final String finalFileName; + + AritifactType get type { + if (finalFileName.endsWith('.dll') || + finalFileName.endsWith('.dll.lib') || + finalFileName.endsWith('.pdb') || + finalFileName.endsWith('.so') || + finalFileName.endsWith('.dylib')) { + return AritifactType.dylib; + } else if (finalFileName.endsWith('.lib') || finalFileName.endsWith('.a')) { + return AritifactType.staticlib; + } else { + throw Exception('Unknown artifact type for $finalFileName'); + } + } + + Artifact({ + required this.path, + required this.finalFileName, + }); +} + +final _log = Logger('artifacts_provider'); + +class ArtifactProvider { + ArtifactProvider({ + required this.environment, + required this.userOptions, + }); + + final BuildEnvironment environment; + final CargokitUserOptions userOptions; + + Future>> getArtifacts(List targets) async { + final result = await _getPrecompiledArtifacts(targets); + + final pendingTargets = List.of(targets); + pendingTargets.removeWhere((element) => result.containsKey(element)); + + if (pendingTargets.isEmpty) { + return result; + } + + final rustup = Rustup(); + for (final target in targets) { + final builder = RustBuilder(target: target, environment: environment); + builder.prepare(rustup); + _log.info('Building ${environment.crateInfo.packageName} for $target'); + final targetDir = await builder.build(); + // For local build accept both static and dynamic libraries. + final artifactNames = { + ...getArtifactNames( + target: target, + libraryName: environment.crateInfo.packageName, + aritifactType: AritifactType.dylib, + remote: false, + ), + ...getArtifactNames( + target: target, + libraryName: environment.crateInfo.packageName, + aritifactType: AritifactType.staticlib, + remote: false, + ) + }; + final artifacts = artifactNames + .map((artifactName) => Artifact( + path: path.join(targetDir, artifactName), + finalFileName: artifactName, + )) + .where((element) => File(element.path).existsSync()) + .toList(); + result[target] = artifacts; + } + return result; + } + + Future>> _getPrecompiledArtifacts( + List targets) async { + if (userOptions.usePrecompiledBinaries == false) { + _log.info('Precompiled binaries are disabled'); + return {}; + } + if (environment.crateOptions.precompiledBinaries == null) { + _log.fine('Precompiled binaries not enabled for this crate'); + return {}; + } + + final start = Stopwatch()..start(); + final crateHash = CrateHash.compute(environment.manifestDir, + tempStorage: environment.targetTempDir); + _log.fine( + 'Computed crate hash $crateHash in ${start.elapsedMilliseconds}ms'); + + final downloadedArtifactsDir = + path.join(environment.targetTempDir, 'precompiled', crateHash); + Directory(downloadedArtifactsDir).createSync(recursive: true); + + final res = >{}; + + for (final target in targets) { + final requiredArtifacts = getArtifactNames( + target: target, + libraryName: environment.crateInfo.packageName, + remote: true, + ); + final artifactsForTarget = []; + + for (final artifact in requiredArtifacts) { + final fileName = PrecompileBinaries.fileName(target, artifact); + final downloadedPath = path.join(downloadedArtifactsDir, fileName); + if (!File(downloadedPath).existsSync()) { + final signatureFileName = + PrecompileBinaries.signatureFileName(target, artifact); + await _tryDownloadArtifacts( + crateHash: crateHash, + fileName: fileName, + signatureFileName: signatureFileName, + finalPath: downloadedPath, + ); + } + if (File(downloadedPath).existsSync()) { + artifactsForTarget.add(Artifact( + path: downloadedPath, + finalFileName: artifact, + )); + } else { + break; + } + } + + // Only provide complete set of artifacts. + if (artifactsForTarget.length == requiredArtifacts.length) { + _log.fine('Found precompiled artifacts for $target'); + res[target] = artifactsForTarget; + } + } + + return res; + } + + static Future _get(Uri url, {Map? headers}) async { + int attempt = 0; + const maxAttempts = 10; + while (true) { + try { + return await get(url, headers: headers); + } on SocketException catch (e) { + // Try to detect reset by peer error and retry. + if (attempt++ < maxAttempts && + (e.osError?.errorCode == 54 || e.osError?.errorCode == 10054)) { + _log.severe( + 'Failed to download $url: $e, attempt $attempt of $maxAttempts, will retry...'); + await Future.delayed(Duration(seconds: 1)); + continue; + } else { + rethrow; + } + } + } + } + + Future _tryDownloadArtifacts({ + required String crateHash, + required String fileName, + required String signatureFileName, + required String finalPath, + }) async { + final precompiledBinaries = environment.crateOptions.precompiledBinaries!; + final prefix = precompiledBinaries.uriPrefix; + final url = Uri.parse('$prefix$crateHash/$fileName'); + final signatureUrl = Uri.parse('$prefix$crateHash/$signatureFileName'); + _log.fine('Downloading signature from $signatureUrl'); + final signature = await _get(signatureUrl); + if (signature.statusCode == 404) { + _log.warning( + 'Precompiled binaries not available for crate hash $crateHash ($fileName)'); + return; + } + if (signature.statusCode != 200) { + _log.severe( + 'Failed to download signature $signatureUrl: status ${signature.statusCode}'); + return; + } + _log.fine('Downloading binary from $url'); + final res = await _get(url); + if (res.statusCode != 200) { + _log.severe('Failed to download binary $url: status ${res.statusCode}'); + return; + } + if (verify( + precompiledBinaries.publicKey, res.bodyBytes, signature.bodyBytes)) { + File(finalPath).writeAsBytesSync(res.bodyBytes); + } else { + _log.shout('Signature verification failed! Ignoring binary.'); + } + } +} + +enum AritifactType { + staticlib, + dylib, +} + +AritifactType artifactTypeForTarget(Target target) { + if (target.darwinPlatform != null) { + return AritifactType.staticlib; + } else { + return AritifactType.dylib; + } +} + +List getArtifactNames({ + required Target target, + required String libraryName, + required bool remote, + AritifactType? aritifactType, +}) { + aritifactType ??= artifactTypeForTarget(target); + if (target.darwinArch != null) { + if (aritifactType == AritifactType.staticlib) { + return ['lib$libraryName.a']; + } else { + return ['lib$libraryName.dylib']; + } + } else if (target.rust.contains('-windows-')) { + if (aritifactType == AritifactType.staticlib) { + return ['$libraryName.lib']; + } else { + return [ + '$libraryName.dll', + '$libraryName.dll.lib', + if (!remote) '$libraryName.pdb' + ]; + } + } else if (target.rust.contains('-linux-')) { + if (aritifactType == AritifactType.staticlib) { + return ['lib$libraryName.a']; + } else { + return ['lib$libraryName.so']; + } + } else { + throw Exception("Unsupported target: ${target.rust}"); + } +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart new file mode 100644 index 0000000000..6f3b2a4ec1 --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart @@ -0,0 +1,40 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:path/path.dart' as path; + +import 'artifacts_provider.dart'; +import 'builder.dart'; +import 'environment.dart'; +import 'options.dart'; +import 'target.dart'; + +class BuildCMake { + final CargokitUserOptions userOptions; + + BuildCMake({required this.userOptions}); + + Future build() async { + final targetPlatform = Environment.targetPlatform; + final target = Target.forFlutterName(Environment.targetPlatform); + if (target == null) { + throw Exception("Unknown target platform: $targetPlatform"); + } + + final environment = BuildEnvironment.fromEnvironment(isAndroid: false); + final provider = + ArtifactProvider(environment: environment, userOptions: userOptions); + final artifacts = await provider.getArtifacts([target]); + + final libs = artifacts[target]!; + + for (final lib in libs) { + if (lib.type == AritifactType.dylib) { + File(lib.path) + .copySync(path.join(Environment.outputDir, lib.finalFileName)); + } + } + } +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart new file mode 100644 index 0000000000..7e61fcbb7c --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart @@ -0,0 +1,49 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'artifacts_provider.dart'; +import 'builder.dart'; +import 'environment.dart'; +import 'options.dart'; +import 'target.dart'; + +final log = Logger('build_gradle'); + +class BuildGradle { + BuildGradle({required this.userOptions}); + + final CargokitUserOptions userOptions; + + Future build() async { + final targets = Environment.targetPlatforms.map((arch) { + final target = Target.forFlutterName(arch); + if (target == null) { + throw Exception( + "Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}"); + } + return target; + }).toList(); + + final environment = BuildEnvironment.fromEnvironment(isAndroid: true); + final provider = + ArtifactProvider(environment: environment, userOptions: userOptions); + final artifacts = await provider.getArtifacts(targets); + + for (final target in targets) { + final libs = artifacts[target]!; + final outputDir = path.join(Environment.outputDir, target.android!); + Directory(outputDir).createSync(recursive: true); + + for (final lib in libs) { + if (lib.type == AritifactType.dylib) { + File(lib.path).copySync(path.join(outputDir, lib.finalFileName)); + } + } + } + } +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/build_pod.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/build_pod.dart new file mode 100644 index 0000000000..8a9c0db5de --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/build_pod.dart @@ -0,0 +1,89 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:path/path.dart' as path; + +import 'artifacts_provider.dart'; +import 'builder.dart'; +import 'environment.dart'; +import 'options.dart'; +import 'target.dart'; +import 'util.dart'; + +class BuildPod { + BuildPod({required this.userOptions}); + + final CargokitUserOptions userOptions; + + Future build() async { + final targets = Environment.darwinArchs.map((arch) { + final target = Target.forDarwin( + platformName: Environment.darwinPlatformName, darwinAarch: arch); + if (target == null) { + throw Exception( + "Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}"); + } + return target; + }).toList(); + + final environment = BuildEnvironment.fromEnvironment(isAndroid: false); + final provider = + ArtifactProvider(environment: environment, userOptions: userOptions); + final artifacts = await provider.getArtifacts(targets); + + void performLipo(String targetFile, Iterable sourceFiles) { + runCommand("lipo", [ + '-create', + ...sourceFiles, + '-output', + targetFile, + ]); + } + + final outputDir = Environment.outputDir; + + Directory(outputDir).createSync(recursive: true); + + final staticLibs = artifacts.values + .expand((element) => element) + .where((element) => element.type == AritifactType.staticlib) + .toList(); + final dynamicLibs = artifacts.values + .expand((element) => element) + .where((element) => element.type == AritifactType.dylib) + .toList(); + + final libName = environment.crateInfo.packageName; + + // If there is static lib, use it and link it with pod + if (staticLibs.isNotEmpty) { + final finalTargetFile = path.join(outputDir, "lib$libName.a"); + performLipo(finalTargetFile, staticLibs.map((e) => e.path)); + } else { + // Otherwise try to replace bundle dylib with our dylib + final bundlePaths = [ + '$libName.framework/Versions/A/$libName', + '$libName.framework/$libName', + ]; + + for (final bundlePath in bundlePaths) { + final targetFile = path.join(outputDir, bundlePath); + if (File(targetFile).existsSync()) { + performLipo(targetFile, dynamicLibs.map((e) => e.path)); + + // Replace absolute id with @rpath one so that it works properly + // when moved to Frameworks. + runCommand("install_name_tool", [ + '-id', + '@rpath/$bundlePath', + targetFile, + ]); + return; + } + } + throw Exception('Unable to find bundle for dynamic library'); + } + } +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/build_tool.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/build_tool.dart new file mode 100644 index 0000000000..c8f36981b5 --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/build_tool.dart @@ -0,0 +1,271 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:args/command_runner.dart'; +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:github/github.dart'; +import 'package:hex/hex.dart'; +import 'package:logging/logging.dart'; + +import 'android_environment.dart'; +import 'build_cmake.dart'; +import 'build_gradle.dart'; +import 'build_pod.dart'; +import 'logging.dart'; +import 'options.dart'; +import 'precompile_binaries.dart'; +import 'target.dart'; +import 'util.dart'; +import 'verify_binaries.dart'; + +final log = Logger('build_tool'); + +abstract class BuildCommand extends Command { + Future runBuildCommand(CargokitUserOptions options); + + @override + Future run() async { + final options = CargokitUserOptions.load(); + + if (options.verboseLogging || + Platform.environment['CARGOKIT_VERBOSE'] == '1') { + enableVerboseLogging(); + } + + await runBuildCommand(options); + } +} + +class BuildPodCommand extends BuildCommand { + @override + final name = 'build-pod'; + + @override + final description = 'Build cocoa pod library'; + + @override + Future runBuildCommand(CargokitUserOptions options) async { + final build = BuildPod(userOptions: options); + await build.build(); + } +} + +class BuildGradleCommand extends BuildCommand { + @override + final name = 'build-gradle'; + + @override + final description = 'Build android library'; + + @override + Future runBuildCommand(CargokitUserOptions options) async { + final build = BuildGradle(userOptions: options); + await build.build(); + } +} + +class BuildCMakeCommand extends BuildCommand { + @override + final name = 'build-cmake'; + + @override + final description = 'Build CMake library'; + + @override + Future runBuildCommand(CargokitUserOptions options) async { + final build = BuildCMake(userOptions: options); + await build.build(); + } +} + +class GenKeyCommand extends Command { + @override + final name = 'gen-key'; + + @override + final description = 'Generate key pair for signing precompiled binaries'; + + @override + void run() { + final kp = generateKey(); + final private = HEX.encode(kp.privateKey.bytes); + final public = HEX.encode(kp.publicKey.bytes); + print("Private Key: $private"); + print("Public Key: $public"); + } +} + +class PrecompileBinariesCommand extends Command { + PrecompileBinariesCommand() { + argParser + ..addOption( + 'repository', + mandatory: true, + help: 'Github repository slug in format owner/name', + ) + ..addOption( + 'manifest-dir', + mandatory: true, + help: 'Directory containing Cargo.toml', + ) + ..addMultiOption('target', + help: 'Rust target triple of artifact to build.\n' + 'Can be specified multiple times or omitted in which case\n' + 'all targets for current platform will be built.') + ..addOption( + 'android-sdk-location', + help: 'Location of Android SDK (if available)', + ) + ..addOption( + 'android-ndk-version', + help: 'Android NDK version (if available)', + ) + ..addOption( + 'android-min-sdk-version', + help: 'Android minimum rquired version (if available)', + ) + ..addOption( + 'temp-dir', + help: 'Directory to store temporary build artifacts', + ) + ..addFlag( + "verbose", + abbr: "v", + defaultsTo: false, + help: "Enable verbose logging", + ); + } + + @override + final name = 'precompile-binaries'; + + @override + final description = 'Prebuild and upload binaries\n' + 'Private key must be passed through PRIVATE_KEY environment variable. ' + 'Use gen_key through generate priave key.\n' + 'Github token must be passed as GITHUB_TOKEN environment variable.\n'; + + @override + Future run() async { + final verbose = argResults!['verbose'] as bool; + if (verbose) { + enableVerboseLogging(); + } + + final privateKeyString = Platform.environment['PRIVATE_KEY']; + if (privateKeyString == null) { + throw ArgumentError('Missing PRIVATE_KEY environment variable'); + } + final githubToken = Platform.environment['GITHUB_TOKEN']; + if (githubToken == null) { + throw ArgumentError('Missing GITHUB_TOKEN environment variable'); + } + final privateKey = HEX.decode(privateKeyString); + if (privateKey.length != 64) { + throw ArgumentError('Private key must be 64 bytes long'); + } + final manifestDir = argResults!['manifest-dir'] as String; + if (!Directory(manifestDir).existsSync()) { + throw ArgumentError('Manifest directory does not exist: $manifestDir'); + } + String? androidMinSdkVersionString = + argResults!['android-min-sdk-version'] as String?; + int? androidMinSdkVersion; + if (androidMinSdkVersionString != null) { + androidMinSdkVersion = int.tryParse(androidMinSdkVersionString); + if (androidMinSdkVersion == null) { + throw ArgumentError( + 'Invalid android-min-sdk-version: $androidMinSdkVersionString'); + } + } + final targetStrigns = argResults!['target'] as List; + final targets = targetStrigns.map((target) { + final res = Target.forRustTriple(target); + if (res == null) { + throw ArgumentError('Invalid target: $target'); + } + return res; + }).toList(growable: false); + final precompileBinaries = PrecompileBinaries( + privateKey: PrivateKey(privateKey), + githubToken: githubToken, + manifestDir: manifestDir, + repositorySlug: RepositorySlug.full(argResults!['repository'] as String), + targets: targets, + androidSdkLocation: argResults!['android-sdk-location'] as String?, + androidNdkVersion: argResults!['android-ndk-version'] as String?, + androidMinSdkVersion: androidMinSdkVersion, + tempDir: argResults!['temp-dir'] as String?, + ); + + await precompileBinaries.run(); + } +} + +class VerifyBinariesCommand extends Command { + VerifyBinariesCommand() { + argParser.addOption( + 'manifest-dir', + mandatory: true, + help: 'Directory containing Cargo.toml', + ); + } + + @override + final name = "verify-binaries"; + + @override + final description = 'Verifies published binaries\n' + 'Checks whether there is a binary published for each targets\n' + 'and checks the signature.'; + + @override + Future run() async { + final manifestDir = argResults!['manifest-dir'] as String; + final verifyBinaries = VerifyBinaries( + manifestDir: manifestDir, + ); + await verifyBinaries.run(); + } +} + +Future runMain(List args) async { + try { + // Init logging before options are loaded + initLogging(); + + if (Platform.environment['_CARGOKIT_NDK_LINK_TARGET'] != null) { + return AndroidEnvironment.clangLinkerWrapper(args); + } + + final runner = CommandRunner('build_tool', 'Cargokit built_tool') + ..addCommand(BuildPodCommand()) + ..addCommand(BuildGradleCommand()) + ..addCommand(BuildCMakeCommand()) + ..addCommand(GenKeyCommand()) + ..addCommand(PrecompileBinariesCommand()) + ..addCommand(VerifyBinariesCommand()); + + await runner.run(args); + } on ArgumentError catch (e) { + stderr.writeln(e.toString()); + exit(1); + } catch (e, s) { + log.severe(kDoubleSeparator); + log.severe('Cargokit BuildTool failed with error:'); + log.severe(kSeparator); + log.severe(e); + // This tells user to install Rust, there's no need to pollute the log with + // stack trace. + if (e is! RustupNotFoundException) { + log.severe(kSeparator); + log.severe(s); + log.severe(kSeparator); + log.severe('BuildTool arguments: $args'); + } + log.severe(kDoubleSeparator); + exit(1); + } +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/builder.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/builder.dart new file mode 100644 index 0000000000..84c46e4f54 --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/builder.dart @@ -0,0 +1,198 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'package:collection/collection.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'android_environment.dart'; +import 'cargo.dart'; +import 'environment.dart'; +import 'options.dart'; +import 'rustup.dart'; +import 'target.dart'; +import 'util.dart'; + +final _log = Logger('builder'); + +enum BuildConfiguration { + debug, + release, + profile, +} + +extension on BuildConfiguration { + bool get isDebug => this == BuildConfiguration.debug; + String get rustName => switch (this) { + BuildConfiguration.debug => 'debug', + BuildConfiguration.release => 'release', + BuildConfiguration.profile => 'release', + }; +} + +class BuildException implements Exception { + final String message; + + BuildException(this.message); + + @override + String toString() { + return 'BuildException: $message'; + } +} + +class BuildEnvironment { + final BuildConfiguration configuration; + final CargokitCrateOptions crateOptions; + final String targetTempDir; + final String manifestDir; + final CrateInfo crateInfo; + + final bool isAndroid; + final String? androidSdkPath; + final String? androidNdkVersion; + final int? androidMinSdkVersion; + final String? javaHome; + + BuildEnvironment({ + required this.configuration, + required this.crateOptions, + required this.targetTempDir, + required this.manifestDir, + required this.crateInfo, + required this.isAndroid, + this.androidSdkPath, + this.androidNdkVersion, + this.androidMinSdkVersion, + this.javaHome, + }); + + static BuildConfiguration parseBuildConfiguration(String value) { + // XCode configuration adds the flavor to configuration name. + final firstSegment = value.split('-').first; + final buildConfiguration = BuildConfiguration.values.firstWhereOrNull( + (e) => e.name == firstSegment, + ); + if (buildConfiguration == null) { + _log.warning('Unknown build configuraiton $value, will assume release'); + return BuildConfiguration.release; + } + return buildConfiguration; + } + + static BuildEnvironment fromEnvironment({ + required bool isAndroid, + }) { + final buildConfiguration = + parseBuildConfiguration(Environment.configuration); + final manifestDir = Environment.manifestDir; + final crateOptions = CargokitCrateOptions.load( + manifestDir: manifestDir, + ); + final crateInfo = CrateInfo.load(manifestDir); + return BuildEnvironment( + configuration: buildConfiguration, + crateOptions: crateOptions, + targetTempDir: Environment.targetTempDir, + manifestDir: manifestDir, + crateInfo: crateInfo, + isAndroid: isAndroid, + androidSdkPath: isAndroid ? Environment.sdkPath : null, + androidNdkVersion: isAndroid ? Environment.ndkVersion : null, + androidMinSdkVersion: + isAndroid ? int.parse(Environment.minSdkVersion) : null, + javaHome: isAndroid ? Environment.javaHome : null, + ); + } +} + +class RustBuilder { + final Target target; + final BuildEnvironment environment; + + RustBuilder({ + required this.target, + required this.environment, + }); + + void prepare( + Rustup rustup, + ) { + final toolchain = _toolchain; + if (rustup.installedTargets(toolchain) == null) { + rustup.installToolchain(toolchain); + } + if (toolchain == 'nightly') { + rustup.installRustSrcForNightly(); + } + if (!rustup.installedTargets(toolchain)!.contains(target.rust)) { + rustup.installTarget(target.rust, toolchain: toolchain); + } + } + + CargoBuildOptions? get _buildOptions => + environment.crateOptions.cargo[environment.configuration]; + + String get _toolchain => _buildOptions?.toolchain.name ?? 'stable'; + + /// Returns the path of directory containing build artifacts. + Future build() async { + final extraArgs = _buildOptions?.flags ?? []; + final manifestPath = path.join(environment.manifestDir, 'Cargo.toml'); + runCommand( + 'rustup', + [ + 'run', + _toolchain, + 'cargo', + 'build', + ...extraArgs, + '--manifest-path', + manifestPath, + '-p', + environment.crateInfo.packageName, + if (!environment.configuration.isDebug) '--release', + '--target', + target.rust, + '--target-dir', + environment.targetTempDir, + ], + environment: await _buildEnvironment(), + ); + return path.join( + environment.targetTempDir, + target.rust, + environment.configuration.rustName, + ); + } + + Future> _buildEnvironment() async { + if (target.android == null) { + return {}; + } else { + final sdkPath = environment.androidSdkPath; + final ndkVersion = environment.androidNdkVersion; + final minSdkVersion = environment.androidMinSdkVersion; + if (sdkPath == null) { + throw BuildException('androidSdkPath is not set'); + } + if (ndkVersion == null) { + throw BuildException('androidNdkVersion is not set'); + } + if (minSdkVersion == null) { + throw BuildException('androidMinSdkVersion is not set'); + } + final env = AndroidEnvironment( + sdkPath: sdkPath, + ndkVersion: ndkVersion, + minSdkVersion: minSdkVersion, + targetTempDir: environment.targetTempDir, + target: target, + ); + if (!env.ndkIsInstalled() && environment.javaHome != null) { + env.installNdk(javaHome: environment.javaHome!); + } + return env.buildEnvironment(); + } + } +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/cargo.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/cargo.dart new file mode 100644 index 0000000000..0d8958ff2e --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/cargo.dart @@ -0,0 +1,48 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:path/path.dart' as path; +import 'package:toml/toml.dart'; + +class ManifestException { + ManifestException(this.message, {required this.fileName}); + + final String? fileName; + final String message; + + @override + String toString() { + if (fileName != null) { + return 'Failed to parse package manifest at $fileName: $message'; + } else { + return 'Failed to parse package manifest: $message'; + } + } +} + +class CrateInfo { + CrateInfo({required this.packageName}); + + final String packageName; + + static CrateInfo parseManifest(String manifest, {final String? fileName}) { + final toml = TomlDocument.parse(manifest); + final package = toml.toMap()['package']; + if (package == null) { + throw ManifestException('Missing package section', fileName: fileName); + } + final name = package['name']; + if (name == null) { + throw ManifestException('Missing package name', fileName: fileName); + } + return CrateInfo(packageName: name); + } + + static CrateInfo load(String manifestDir) { + final manifestFile = File(path.join(manifestDir, 'Cargo.toml')); + final manifest = manifestFile.readAsStringSync(); + return parseManifest(manifest, fileName: manifestFile.path); + } +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart new file mode 100644 index 0000000000..0c4d88d16b --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart @@ -0,0 +1,124 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:collection/collection.dart'; +import 'package:convert/convert.dart'; +import 'package:crypto/crypto.dart'; +import 'package:path/path.dart' as path; + +class CrateHash { + /// Computes a hash uniquely identifying crate content. This takes into account + /// content all all .rs files inside the src directory, as well as Cargo.toml, + /// Cargo.lock, build.rs and cargokit.yaml. + /// + /// If [tempStorage] is provided, computed hash is stored in a file in that directory + /// and reused on subsequent calls if the crate content hasn't changed. + static String compute(String manifestDir, {String? tempStorage}) { + return CrateHash._( + manifestDir: manifestDir, + tempStorage: tempStorage, + )._compute(); + } + + CrateHash._({ + required this.manifestDir, + required this.tempStorage, + }); + + String _compute() { + final files = getFiles(); + final tempStorage = this.tempStorage; + if (tempStorage != null) { + final quickHash = _computeQuickHash(files); + final quickHashFolder = Directory(path.join(tempStorage, 'crate_hash')); + quickHashFolder.createSync(recursive: true); + final quickHashFile = File(path.join(quickHashFolder.path, quickHash)); + if (quickHashFile.existsSync()) { + return quickHashFile.readAsStringSync(); + } + final hash = _computeHash(files); + quickHashFile.writeAsStringSync(hash); + return hash; + } else { + return _computeHash(files); + } + } + + /// Computes a quick hash based on files stat (without reading contents). This + /// is used to cache the real hash, which is slower to compute since it involves + /// reading every single file. + String _computeQuickHash(List files) { + final output = AccumulatorSink(); + final input = sha256.startChunkedConversion(output); + + final data = ByteData(8); + for (final file in files) { + input.add(utf8.encode(file.path)); + final stat = file.statSync(); + data.setUint64(0, stat.size); + input.add(data.buffer.asUint8List()); + data.setUint64(0, stat.modified.millisecondsSinceEpoch); + input.add(data.buffer.asUint8List()); + } + + input.close(); + return base64Url.encode(output.events.single.bytes); + } + + String _computeHash(List files) { + final output = AccumulatorSink(); + final input = sha256.startChunkedConversion(output); + + void addTextFile(File file) { + // text Files are hashed by lines in case we're dealing with github checkout + // that auto-converts line endings. + final splitter = LineSplitter(); + if (file.existsSync()) { + final data = file.readAsStringSync(); + final lines = splitter.convert(data); + for (final line in lines) { + input.add(utf8.encode(line)); + } + } + } + + for (final file in files) { + addTextFile(file); + } + + input.close(); + final res = output.events.single; + + // Truncate to 128bits. + final hash = res.bytes.sublist(0, 16); + return hex.encode(hash); + } + + List getFiles() { + final src = Directory(path.join(manifestDir, 'src')); + final files = src + .listSync(recursive: true, followLinks: false) + .whereType() + .toList(); + files.sortBy((element) => element.path); + void addFile(String relative) { + final file = File(path.join(manifestDir, relative)); + if (file.existsSync()) { + files.add(file); + } + } + + addFile('Cargo.toml'); + addFile('Cargo.lock'); + addFile('build.rs'); + addFile('cargokit.yaml'); + return files; + } + + final String manifestDir; + final String? tempStorage; +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/environment.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/environment.dart new file mode 100644 index 0000000000..996483a180 --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/environment.dart @@ -0,0 +1,68 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +extension on String { + String resolveSymlink() => File(this).resolveSymbolicLinksSync(); +} + +class Environment { + /// Current build configuration (debug or release). + static String get configuration => + _getEnv("CARGOKIT_CONFIGURATION").toLowerCase(); + + static bool get isDebug => configuration == 'debug'; + static bool get isRelease => configuration == 'release'; + + /// Temporary directory where Rust build artifacts are placed. + static String get targetTempDir => _getEnv("CARGOKIT_TARGET_TEMP_DIR"); + + /// Final output directory where the build artifacts are placed. + static String get outputDir => _getEnvPath('CARGOKIT_OUTPUT_DIR'); + + /// Path to the crate manifest (containing Cargo.toml). + static String get manifestDir => _getEnvPath('CARGOKIT_MANIFEST_DIR'); + + /// Directory inside root project. Not necessarily root folder. Symlinks are + /// not resolved on purpose. + static String get rootProjectDir => _getEnv('CARGOKIT_ROOT_PROJECT_DIR'); + + // Pod + + /// Platform name (macosx, iphoneos, iphonesimulator). + static String get darwinPlatformName => + _getEnv("CARGOKIT_DARWIN_PLATFORM_NAME"); + + /// List of architectures to build for (arm64, armv7, x86_64). + static List get darwinArchs => + _getEnv("CARGOKIT_DARWIN_ARCHS").split(' '); + + // Gradle + static String get minSdkVersion => _getEnv("CARGOKIT_MIN_SDK_VERSION"); + static String get ndkVersion => _getEnv("CARGOKIT_NDK_VERSION"); + static String get sdkPath => _getEnvPath("CARGOKIT_SDK_DIR"); + static String get javaHome => _getEnvPath("CARGOKIT_JAVA_HOME"); + static List get targetPlatforms => + _getEnv("CARGOKIT_TARGET_PLATFORMS").split(','); + + // CMAKE + static String get targetPlatform => _getEnv("CARGOKIT_TARGET_PLATFORM"); + + static String _getEnv(String key) { + final res = Platform.environment[key]; + if (res == null) { + throw Exception("Missing environment variable $key"); + } + return res; + } + + static String _getEnvPath(String key) { + final res = _getEnv(key); + if (Directory(res).existsSync()) { + return res.resolveSymlink(); + } else { + return res; + } + } +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/logging.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/logging.dart new file mode 100644 index 0000000000..5edd4fd184 --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/logging.dart @@ -0,0 +1,52 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:logging/logging.dart'; + +const String kSeparator = "--"; +const String kDoubleSeparator = "=="; + +bool _lastMessageWasSeparator = false; + +void _log(LogRecord rec) { + final prefix = '${rec.level.name}: '; + final out = rec.level == Level.SEVERE ? stderr : stdout; + if (rec.message == kSeparator) { + if (!_lastMessageWasSeparator) { + out.write(prefix); + out.writeln('-' * 80); + _lastMessageWasSeparator = true; + } + return; + } else if (rec.message == kDoubleSeparator) { + out.write(prefix); + out.writeln('=' * 80); + _lastMessageWasSeparator = true; + return; + } + out.write(prefix); + out.writeln(rec.message); + _lastMessageWasSeparator = false; +} + +void initLogging() { + Logger.root.level = Level.INFO; + Logger.root.onRecord.listen((LogRecord rec) { + final lines = rec.message.split('\n'); + for (final line in lines) { + if (line.isNotEmpty || lines.length == 1 || line != lines.last) { + _log(LogRecord( + rec.level, + line, + rec.loggerName, + )); + } + } + }); +} + +void enableVerboseLogging() { + Logger.root.level = Level.ALL; +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/options.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/options.dart new file mode 100644 index 0000000000..22aef1d371 --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/options.dart @@ -0,0 +1,309 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:collection/collection.dart'; +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:hex/hex.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; +import 'package:source_span/source_span.dart'; +import 'package:yaml/yaml.dart'; + +import 'builder.dart'; +import 'environment.dart'; +import 'rustup.dart'; + +final _log = Logger('options'); + +/// A class for exceptions that have source span information attached. +class SourceSpanException implements Exception { + // This is a getter so that subclasses can override it. + /// A message describing the exception. + String get message => _message; + final String _message; + + // This is a getter so that subclasses can override it. + /// The span associated with this exception. + /// + /// This may be `null` if the source location can't be determined. + SourceSpan? get span => _span; + final SourceSpan? _span; + + SourceSpanException(this._message, this._span); + + /// Returns a string representation of `this`. + /// + /// [color] may either be a [String], a [bool], or `null`. If it's a string, + /// it indicates an ANSI terminal color escape that should be used to + /// highlight the span's text. If it's `true`, it indicates that the text + /// should be highlighted using the default color. If it's `false` or `null`, + /// it indicates that the text shouldn't be highlighted. + @override + String toString({Object? color}) { + if (span == null) return message; + return 'Error on ${span!.message(message, color: color)}'; + } +} + +enum Toolchain { + stable, + beta, + nightly, +} + +class CargoBuildOptions { + final Toolchain toolchain; + final List flags; + + CargoBuildOptions({ + required this.toolchain, + required this.flags, + }); + + static Toolchain _toolchainFromNode(YamlNode node) { + if (node case YamlScalar(value: String name)) { + final toolchain = + Toolchain.values.firstWhereOrNull((element) => element.name == name); + if (toolchain != null) { + return toolchain; + } + } + throw SourceSpanException( + 'Unknown toolchain. Must be one of ${Toolchain.values.map((e) => e.name)}.', + node.span); + } + + static CargoBuildOptions parse(YamlNode node) { + if (node is! YamlMap) { + throw SourceSpanException('Cargo options must be a map', node.span); + } + Toolchain toolchain = Toolchain.stable; + List flags = []; + for (final MapEntry(:key, :value) in node.nodes.entries) { + if (key case YamlScalar(value: 'toolchain')) { + toolchain = _toolchainFromNode(value); + } else if (key case YamlScalar(value: 'extra_flags')) { + if (value case YamlList(nodes: List list)) { + if (list.every((element) { + if (element case YamlScalar(value: String _)) { + return true; + } + return false; + })) { + flags = list.map((e) => e.value as String).toList(); + continue; + } + } + throw SourceSpanException( + 'Extra flags must be a list of strings', value.span); + } else { + throw SourceSpanException( + 'Unknown cargo option type. Must be "toolchain" or "extra_flags".', + key.span); + } + } + return CargoBuildOptions(toolchain: toolchain, flags: flags); + } +} + +extension on YamlMap { + /// Map that extracts keys so that we can do map case check on them. + Map get valueMap => + nodes.map((key, value) => MapEntry(key.value, value)); +} + +class PrecompiledBinaries { + final String uriPrefix; + final PublicKey publicKey; + + PrecompiledBinaries({ + required this.uriPrefix, + required this.publicKey, + }); + + static PublicKey _publicKeyFromHex(String key, SourceSpan? span) { + final bytes = HEX.decode(key); + if (bytes.length != 32) { + throw SourceSpanException( + 'Invalid public key. Must be 32 bytes long.', span); + } + return PublicKey(bytes); + } + + static PrecompiledBinaries parse(YamlNode node) { + if (node case YamlMap(valueMap: Map map)) { + if (map + case { + 'url_prefix': YamlNode urlPrefixNode, + 'public_key': YamlNode publicKeyNode, + }) { + final urlPrefix = switch (urlPrefixNode) { + YamlScalar(value: String urlPrefix) => urlPrefix, + _ => throw SourceSpanException( + 'Invalid URL prefix value.', urlPrefixNode.span), + }; + final publicKey = switch (publicKeyNode) { + YamlScalar(value: String publicKey) => + _publicKeyFromHex(publicKey, publicKeyNode.span), + _ => throw SourceSpanException( + 'Invalid public key value.', publicKeyNode.span), + }; + return PrecompiledBinaries( + uriPrefix: urlPrefix, + publicKey: publicKey, + ); + } + } + throw SourceSpanException( + 'Invalid precompiled binaries value. ' + 'Expected Map with "url_prefix" and "public_key".', + node.span); + } +} + +/// Cargokit options specified for Rust crate. +class CargokitCrateOptions { + CargokitCrateOptions({ + this.cargo = const {}, + this.precompiledBinaries, + }); + + final Map cargo; + final PrecompiledBinaries? precompiledBinaries; + + static CargokitCrateOptions parse(YamlNode node) { + if (node is! YamlMap) { + throw SourceSpanException('Cargokit options must be a map', node.span); + } + final options = {}; + PrecompiledBinaries? precompiledBinaries; + + for (final entry in node.nodes.entries) { + if (entry + case MapEntry( + key: YamlScalar(value: 'cargo'), + value: YamlNode node, + )) { + if (node is! YamlMap) { + throw SourceSpanException('Cargo options must be a map', node.span); + } + for (final MapEntry(:YamlNode key, :value) in node.nodes.entries) { + if (key case YamlScalar(value: String name)) { + final configuration = BuildConfiguration.values + .firstWhereOrNull((element) => element.name == name); + if (configuration != null) { + options[configuration] = CargoBuildOptions.parse(value); + continue; + } + } + throw SourceSpanException( + 'Unknown build configuration. Must be one of ${BuildConfiguration.values.map((e) => e.name)}.', + key.span); + } + } else if (entry.key case YamlScalar(value: 'precompiled_binaries')) { + precompiledBinaries = PrecompiledBinaries.parse(entry.value); + } else { + throw SourceSpanException( + 'Unknown cargokit option type. Must be "cargo" or "precompiled_binaries".', + entry.key.span); + } + } + return CargokitCrateOptions( + cargo: options, + precompiledBinaries: precompiledBinaries, + ); + } + + static CargokitCrateOptions load({ + required String manifestDir, + }) { + final uri = Uri.file(path.join(manifestDir, "cargokit.yaml")); + final file = File.fromUri(uri); + if (file.existsSync()) { + final contents = loadYamlNode(file.readAsStringSync(), sourceUrl: uri); + return parse(contents); + } else { + return CargokitCrateOptions(); + } + } +} + +class CargokitUserOptions { + // When Rustup is installed always build locally unless user opts into + // using precompiled binaries. + static bool defaultUsePrecompiledBinaries() { + return Rustup.executablePath() == null; + } + + CargokitUserOptions({ + required this.usePrecompiledBinaries, + required this.verboseLogging, + }); + + CargokitUserOptions._() + : usePrecompiledBinaries = defaultUsePrecompiledBinaries(), + verboseLogging = false; + + static CargokitUserOptions parse(YamlNode node) { + if (node is! YamlMap) { + throw SourceSpanException('Cargokit options must be a map', node.span); + } + bool usePrecompiledBinaries = defaultUsePrecompiledBinaries(); + bool verboseLogging = false; + + for (final entry in node.nodes.entries) { + if (entry.key case YamlScalar(value: 'use_precompiled_binaries')) { + if (entry.value case YamlScalar(value: bool value)) { + usePrecompiledBinaries = value; + continue; + } + throw SourceSpanException( + 'Invalid value for "use_precompiled_binaries". Must be a boolean.', + entry.value.span); + } else if (entry.key case YamlScalar(value: 'verbose_logging')) { + if (entry.value case YamlScalar(value: bool value)) { + verboseLogging = value; + continue; + } + throw SourceSpanException( + 'Invalid value for "verbose_logging". Must be a boolean.', + entry.value.span); + } else { + throw SourceSpanException( + 'Unknown cargokit option type. Must be "use_precompiled_binaries" or "verbose_logging".', + entry.key.span); + } + } + return CargokitUserOptions( + usePrecompiledBinaries: usePrecompiledBinaries, + verboseLogging: verboseLogging, + ); + } + + static CargokitUserOptions load() { + String fileName = "cargokit_options.yaml"; + var userProjectDir = Directory(Environment.rootProjectDir); + + while (userProjectDir.parent.path != userProjectDir.path) { + final configFile = File(path.join(userProjectDir.path, fileName)); + if (configFile.existsSync()) { + final contents = loadYamlNode( + configFile.readAsStringSync(), + sourceUrl: configFile.uri, + ); + final res = parse(contents); + if (res.verboseLogging) { + _log.info('Found user options file at ${configFile.path}'); + } + return res; + } + userProjectDir = userProjectDir.parent; + } + return CargokitUserOptions._(); + } + + final bool usePrecompiledBinaries; + final bool verboseLogging; +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart new file mode 100644 index 0000000000..c27f4195dd --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart @@ -0,0 +1,202 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:github/github.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'artifacts_provider.dart'; +import 'builder.dart'; +import 'cargo.dart'; +import 'crate_hash.dart'; +import 'options.dart'; +import 'rustup.dart'; +import 'target.dart'; + +final _log = Logger('precompile_binaries'); + +class PrecompileBinaries { + PrecompileBinaries({ + required this.privateKey, + required this.githubToken, + required this.repositorySlug, + required this.manifestDir, + required this.targets, + this.androidSdkLocation, + this.androidNdkVersion, + this.androidMinSdkVersion, + this.tempDir, + }); + + final PrivateKey privateKey; + final String githubToken; + final RepositorySlug repositorySlug; + final String manifestDir; + final List targets; + final String? androidSdkLocation; + final String? androidNdkVersion; + final int? androidMinSdkVersion; + final String? tempDir; + + static String fileName(Target target, String name) { + return '${target.rust}_$name'; + } + + static String signatureFileName(Target target, String name) { + return '${target.rust}_$name.sig'; + } + + Future run() async { + final crateInfo = CrateInfo.load(manifestDir); + + final targets = List.of(this.targets); + if (targets.isEmpty) { + targets.addAll([ + ...Target.buildableTargets(), + if (androidSdkLocation != null) ...Target.androidTargets(), + ]); + } + + _log.info('Precompiling binaries for $targets'); + + final hash = CrateHash.compute(manifestDir); + _log.info('Computed crate hash: $hash'); + + final String tagName = 'precompiled_$hash'; + + final github = GitHub(auth: Authentication.withToken(githubToken)); + final repo = github.repositories; + final release = await _getOrCreateRelease( + repo: repo, + tagName: tagName, + packageName: crateInfo.packageName, + hash: hash, + ); + + final tempDir = this.tempDir != null + ? Directory(this.tempDir!) + : Directory.systemTemp.createTempSync('precompiled_'); + + tempDir.createSync(recursive: true); + + final crateOptions = CargokitCrateOptions.load( + manifestDir: manifestDir, + ); + + final buildEnvironment = BuildEnvironment( + configuration: BuildConfiguration.release, + crateOptions: crateOptions, + targetTempDir: tempDir.path, + manifestDir: manifestDir, + crateInfo: crateInfo, + isAndroid: androidSdkLocation != null, + androidSdkPath: androidSdkLocation, + androidNdkVersion: androidNdkVersion, + androidMinSdkVersion: androidMinSdkVersion, + ); + + final rustup = Rustup(); + + for (final target in targets) { + final artifactNames = getArtifactNames( + target: target, + libraryName: crateInfo.packageName, + remote: true, + ); + + if (artifactNames.every((name) { + final fileName = PrecompileBinaries.fileName(target, name); + return (release.assets ?? []).any((e) => e.name == fileName); + })) { + _log.info("All artifacts for $target already exist - skipping"); + continue; + } + + _log.info('Building for $target'); + + final builder = + RustBuilder(target: target, environment: buildEnvironment); + builder.prepare(rustup); + final res = await builder.build(); + + final assets = []; + for (final name in artifactNames) { + final file = File(path.join(res, name)); + if (!file.existsSync()) { + throw Exception('Missing artifact: ${file.path}'); + } + + final data = file.readAsBytesSync(); + final create = CreateReleaseAsset( + name: PrecompileBinaries.fileName(target, name), + contentType: "application/octet-stream", + assetData: data, + ); + final signature = sign(privateKey, data); + final signatureCreate = CreateReleaseAsset( + name: signatureFileName(target, name), + contentType: "application/octet-stream", + assetData: signature, + ); + bool verified = verify(public(privateKey), data, signature); + if (!verified) { + throw Exception('Signature verification failed'); + } + assets.add(create); + assets.add(signatureCreate); + } + _log.info('Uploading assets: ${assets.map((e) => e.name)}'); + for (final asset in assets) { + // This seems to be failing on CI so do it one by one + int retryCount = 0; + while (true) { + try { + await repo.uploadReleaseAssets(release, [asset]); + break; + } on Exception catch (e) { + if (retryCount == 10) { + rethrow; + } + ++retryCount; + _log.shout( + 'Upload failed (attempt $retryCount, will retry): ${e.toString()}'); + await Future.delayed(Duration(seconds: 2)); + } + } + } + } + + _log.info('Cleaning up'); + tempDir.deleteSync(recursive: true); + } + + Future _getOrCreateRelease({ + required RepositoriesService repo, + required String tagName, + required String packageName, + required String hash, + }) async { + Release release; + try { + _log.info('Fetching release $tagName'); + release = await repo.getReleaseByTagName(repositorySlug, tagName); + } on ReleaseNotFound { + _log.info('Release not found - creating release $tagName'); + release = await repo.createRelease( + repositorySlug, + CreateRelease.from( + tagName: tagName, + name: 'Precompiled binaries ${hash.substring(0, 8)}', + targetCommitish: null, + isDraft: false, + isPrerelease: false, + body: 'Precompiled binaries for crate $packageName, ' + 'crate hash $hash.', + )); + } + return release; + } +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/rustup.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/rustup.dart new file mode 100644 index 0000000000..0ac8d08616 --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/rustup.dart @@ -0,0 +1,136 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:collection/collection.dart'; +import 'package:path/path.dart' as path; + +import 'util.dart'; + +class _Toolchain { + _Toolchain( + this.name, + this.targets, + ); + + final String name; + final List targets; +} + +class Rustup { + List? installedTargets(String toolchain) { + final targets = _installedTargets(toolchain); + return targets != null ? List.unmodifiable(targets) : null; + } + + void installToolchain(String toolchain) { + log.info("Installing Rust toolchain: $toolchain"); + runCommand("rustup", ['toolchain', 'install', toolchain]); + _installedToolchains + .add(_Toolchain(toolchain, _getInstalledTargets(toolchain))); + } + + void installTarget( + String target, { + required String toolchain, + }) { + log.info("Installing Rust target: $target"); + runCommand("rustup", [ + 'target', + 'add', + '--toolchain', + toolchain, + target, + ]); + _installedTargets(toolchain)?.add(target); + } + + final List<_Toolchain> _installedToolchains; + + Rustup() : _installedToolchains = _getInstalledToolchains(); + + List? _installedTargets(String toolchain) => _installedToolchains + .firstWhereOrNull( + (e) => e.name == toolchain || e.name.startsWith('$toolchain-')) + ?.targets; + + static List<_Toolchain> _getInstalledToolchains() { + String extractToolchainName(String line) { + // ignore (default) after toolchain name + final parts = line.split(' '); + return parts[0]; + } + + final res = runCommand("rustup", ['toolchain', 'list']); + + // To list all non-custom toolchains, we need to filter out lines that + // don't start with "stable", "beta", or "nightly". + Pattern nonCustom = RegExp(r"^(stable|beta|nightly)"); + final lines = res.stdout + .toString() + .split('\n') + .where((e) => e.isNotEmpty && e.startsWith(nonCustom)) + .map(extractToolchainName) + .toList(growable: true); + + return lines + .map( + (name) => _Toolchain( + name, + _getInstalledTargets(name), + ), + ) + .toList(growable: true); + } + + static List _getInstalledTargets(String toolchain) { + final res = runCommand("rustup", [ + 'target', + 'list', + '--toolchain', + toolchain, + '--installed', + ]); + final lines = res.stdout + .toString() + .split('\n') + .where((e) => e.isNotEmpty) + .toList(growable: true); + return lines; + } + + bool _didInstallRustSrcForNightly = false; + + void installRustSrcForNightly() { + if (_didInstallRustSrcForNightly) { + return; + } + // Useful for -Z build-std + runCommand( + "rustup", + ['component', 'add', 'rust-src', '--toolchain', 'nightly'], + ); + _didInstallRustSrcForNightly = true; + } + + static String? executablePath() { + final envPath = Platform.environment['PATH']; + final envPathSeparator = Platform.isWindows ? ';' : ':'; + final home = Platform.isWindows + ? Platform.environment['USERPROFILE'] + : Platform.environment['HOME']; + final paths = [ + if (home != null) path.join(home, '.cargo', 'bin'), + if (envPath != null) ...envPath.split(envPathSeparator), + ]; + for (final p in paths) { + final rustup = Platform.isWindows ? 'rustup.exe' : 'rustup'; + final rustupPath = path.join(p, rustup); + if (File(rustupPath).existsSync()) { + return rustupPath; + } + } + return null; + } +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/target.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/target.dart new file mode 100644 index 0000000000..6fbc58b64f --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/target.dart @@ -0,0 +1,140 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:collection/collection.dart'; + +import 'util.dart'; + +class Target { + Target({ + required this.rust, + this.flutter, + this.android, + this.androidMinSdkVersion, + this.darwinPlatform, + this.darwinArch, + }); + + static final all = [ + Target( + rust: 'armv7-linux-androideabi', + flutter: 'android-arm', + android: 'armeabi-v7a', + androidMinSdkVersion: 16, + ), + Target( + rust: 'aarch64-linux-android', + flutter: 'android-arm64', + android: 'arm64-v8a', + androidMinSdkVersion: 21, + ), + Target( + rust: 'i686-linux-android', + flutter: 'android-x86', + android: 'x86', + androidMinSdkVersion: 16, + ), + Target( + rust: 'x86_64-linux-android', + flutter: 'android-x64', + android: 'x86_64', + androidMinSdkVersion: 21, + ), + Target( + rust: 'x86_64-pc-windows-msvc', + flutter: 'windows-x64', + ), + Target( + rust: 'x86_64-unknown-linux-gnu', + flutter: 'linux-x64', + ), + Target( + rust: 'aarch64-unknown-linux-gnu', + flutter: 'linux-arm64', + ), + Target( + rust: 'x86_64-apple-darwin', + darwinPlatform: 'macosx', + darwinArch: 'x86_64', + ), + Target( + rust: 'aarch64-apple-darwin', + darwinPlatform: 'macosx', + darwinArch: 'arm64', + ), + Target( + rust: 'aarch64-apple-ios', + darwinPlatform: 'iphoneos', + darwinArch: 'arm64', + ), + Target( + rust: 'aarch64-apple-ios-sim', + darwinPlatform: 'iphonesimulator', + darwinArch: 'arm64', + ), + Target( + rust: 'x86_64-apple-ios', + darwinPlatform: 'iphonesimulator', + darwinArch: 'x86_64', + ), + ]; + + static Target? forFlutterName(String flutterName) { + return all.firstWhereOrNull((element) => element.flutter == flutterName); + } + + static Target? forDarwin({ + required String platformName, + required String darwinAarch, + }) { + return all.firstWhereOrNull((element) => // + element.darwinPlatform == platformName && + element.darwinArch == darwinAarch); + } + + static Target? forRustTriple(String triple) { + return all.firstWhereOrNull((element) => element.rust == triple); + } + + static List androidTargets() { + return all + .where((element) => element.android != null) + .toList(growable: false); + } + + /// Returns buildable targets on current host platform ignoring Android targets. + static List buildableTargets() { + if (Platform.isLinux) { + // Right now we don't support cross-compiling on Linux. So we just return + // the host target. + final arch = runCommand('arch', []).stdout as String; + if (arch.trim() == 'aarch64') { + return [Target.forRustTriple('aarch64-unknown-linux-gnu')!]; + } else { + return [Target.forRustTriple('x86_64-unknown-linux-gnu')!]; + } + } + return all.where((target) { + if (Platform.isWindows) { + return target.rust.contains('-windows-'); + } else if (Platform.isMacOS) { + return target.darwinPlatform != null; + } + return false; + }).toList(growable: false); + } + + @override + String toString() { + return rust; + } + + final String? flutter; + final String rust; + final String? android; + final int? androidMinSdkVersion; + final String? darwinPlatform; + final String? darwinArch; +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/util.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/util.dart new file mode 100644 index 0000000000..8bb6a8724f --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/util.dart @@ -0,0 +1,172 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:convert'; +import 'dart:io'; + +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'logging.dart'; +import 'rustup.dart'; + +final log = Logger("process"); + +class CommandFailedException implements Exception { + final String executable; + final List arguments; + final ProcessResult result; + + CommandFailedException({ + required this.executable, + required this.arguments, + required this.result, + }); + + @override + String toString() { + final stdout = result.stdout.toString().trim(); + final stderr = result.stderr.toString().trim(); + return [ + "External Command: $executable ${arguments.map((e) => '"$e"').join(' ')}", + "Returned Exit Code: ${result.exitCode}", + kSeparator, + "STDOUT:", + if (stdout.isNotEmpty) stdout, + kSeparator, + "STDERR:", + if (stderr.isNotEmpty) stderr, + ].join('\n'); + } +} + +class TestRunCommandArgs { + final String executable; + final List arguments; + final String? workingDirectory; + final Map? environment; + final bool includeParentEnvironment; + final bool runInShell; + final Encoding? stdoutEncoding; + final Encoding? stderrEncoding; + + TestRunCommandArgs({ + required this.executable, + required this.arguments, + this.workingDirectory, + this.environment, + this.includeParentEnvironment = true, + this.runInShell = false, + this.stdoutEncoding, + this.stderrEncoding, + }); +} + +class TestRunCommandResult { + TestRunCommandResult({ + this.pid = 1, + this.exitCode = 0, + this.stdout = '', + this.stderr = '', + }); + + final int pid; + final int exitCode; + final String stdout; + final String stderr; +} + +TestRunCommandResult Function(TestRunCommandArgs args)? testRunCommandOverride; + +ProcessResult runCommand( + String executable, + List arguments, { + String? workingDirectory, + Map? environment, + bool includeParentEnvironment = true, + bool runInShell = false, + Encoding? stdoutEncoding = systemEncoding, + Encoding? stderrEncoding = systemEncoding, +}) { + if (testRunCommandOverride != null) { + final result = testRunCommandOverride!(TestRunCommandArgs( + executable: executable, + arguments: arguments, + workingDirectory: workingDirectory, + environment: environment, + includeParentEnvironment: includeParentEnvironment, + runInShell: runInShell, + stdoutEncoding: stdoutEncoding, + stderrEncoding: stderrEncoding, + )); + return ProcessResult( + result.pid, + result.exitCode, + result.stdout, + result.stderr, + ); + } + log.finer('Running command $executable ${arguments.join(' ')}'); + final res = Process.runSync( + _resolveExecutable(executable), + arguments, + workingDirectory: workingDirectory, + environment: environment, + includeParentEnvironment: includeParentEnvironment, + runInShell: runInShell, + stderrEncoding: stderrEncoding, + stdoutEncoding: stdoutEncoding, + ); + if (res.exitCode != 0) { + throw CommandFailedException( + executable: executable, + arguments: arguments, + result: res, + ); + } else { + return res; + } +} + +class RustupNotFoundException implements Exception { + @override + String toString() { + return [ + ' ', + 'rustup not found in PATH.', + ' ', + 'Maybe you need to install Rust? It only takes a minute:', + ' ', + if (Platform.isWindows) 'https://www.rust-lang.org/tools/install', + if (hasHomebrewRustInPath()) ...[ + '\$ brew unlink rust # Unlink homebrew Rust from PATH', + ], + if (!Platform.isWindows) + "\$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh", + ' ', + ].join('\n'); + } + + static bool hasHomebrewRustInPath() { + if (!Platform.isMacOS) { + return false; + } + final envPath = Platform.environment['PATH'] ?? ''; + final paths = envPath.split(':'); + return paths.any((p) { + return p.contains('homebrew') && File(path.join(p, 'rustc')).existsSync(); + }); + } +} + +String _resolveExecutable(String executable) { + if (executable == 'rustup') { + final resolved = Rustup.executablePath(); + if (resolved != null) { + return resolved; + } + throw RustupNotFoundException(); + } else { + return executable; + } +} diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart b/mobile/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart new file mode 100644 index 0000000000..2366b57bfd --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart @@ -0,0 +1,84 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:http/http.dart'; + +import 'artifacts_provider.dart'; +import 'cargo.dart'; +import 'crate_hash.dart'; +import 'options.dart'; +import 'precompile_binaries.dart'; +import 'target.dart'; + +class VerifyBinaries { + VerifyBinaries({ + required this.manifestDir, + }); + + final String manifestDir; + + Future run() async { + final crateInfo = CrateInfo.load(manifestDir); + + final config = CargokitCrateOptions.load(manifestDir: manifestDir); + final precompiledBinaries = config.precompiledBinaries; + if (precompiledBinaries == null) { + stdout.writeln('Crate does not support precompiled binaries.'); + } else { + final crateHash = CrateHash.compute(manifestDir); + stdout.writeln('Crate hash: $crateHash'); + + for (final target in Target.all) { + final message = 'Checking ${target.rust}...'; + stdout.write(message.padRight(40)); + stdout.flush(); + + final artifacts = getArtifactNames( + target: target, + libraryName: crateInfo.packageName, + remote: true, + ); + + final prefix = precompiledBinaries.uriPrefix; + + bool ok = true; + + for (final artifact in artifacts) { + final fileName = PrecompileBinaries.fileName(target, artifact); + final signatureFileName = + PrecompileBinaries.signatureFileName(target, artifact); + + final url = Uri.parse('$prefix$crateHash/$fileName'); + final signatureUrl = + Uri.parse('$prefix$crateHash/$signatureFileName'); + + final signature = await get(signatureUrl); + if (signature.statusCode != 200) { + stdout.writeln('MISSING'); + ok = false; + break; + } + final asset = await get(url); + if (asset.statusCode != 200) { + stdout.writeln('MISSING'); + ok = false; + break; + } + + if (!verify(precompiledBinaries.publicKey, asset.bodyBytes, + signature.bodyBytes)) { + stdout.writeln('INVALID SIGNATURE'); + ok = false; + } + } + + if (ok) { + stdout.writeln('OK'); + } + } + } + } +} diff --git a/mobile/rust_builder/cargokit/build_tool/pubspec.lock b/mobile/rust_builder/cargokit/build_tool/pubspec.lock new file mode 100644 index 0000000000..343bdd3694 --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/pubspec.lock @@ -0,0 +1,453 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 + url: "https://pub.dev" + source: hosted + version: "64.0.0" + adaptive_number: + dependency: transitive + description: + name: adaptive_number + sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" + url: "https://pub.dev" + source: hosted + version: "6.2.0" + args: + dependency: "direct main" + description: + name: args + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + url: "https://pub.dev" + source: hosted + version: "2.4.2" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + collection: + dependency: "direct main" + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + convert: + dependency: "direct main" + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + coverage: + dependency: transitive + description: + name: coverage + sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" + url: "https://pub.dev" + source: hosted + version: "1.6.3" + crypto: + dependency: "direct main" + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" + ed25519_edwards: + dependency: "direct main" + description: + name: ed25519_edwards + sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + github: + dependency: "direct main" + description: + name: github + sha256: "9966bc13bf612342e916b0a343e95e5f046c88f602a14476440e9b75d2295411" + url: "https://pub.dev" + source: hosted + version: "9.17.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + hex: + dependency: "direct main" + description: + name: hex + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + http: + dependency: "direct main" + description: + name: http + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + url: "https://pub.dev" + source: hosted + version: "4.8.1" + lints: + dependency: "direct dev" + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + logging: + dependency: "direct main" + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + meta: + dependency: transitive + description: + name: meta + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + mime: + dependency: transitive + description: + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" + source: hosted + version: "1.0.4" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + path: + dependency: "direct main" + description: + name: path + sha256: "2ad4cddff7f5cc0e2d13069f2a3f7a73ca18f66abd6f5ecf215219cdb3638edb" + url: "https://pub.dev" + source: hosted + version: "1.8.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + url: "https://pub.dev" + source: hosted + version: "5.4.0" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + url: "https://pub.dev" + source: hosted + version: "1.1.2" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" + source_span: + dependency: "direct main" + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test: + dependency: "direct dev" + description: + name: test + sha256: "9b0dd8e36af4a5b1569029949d50a52cb2a2a2fdaa20cebb96e6603b9ae241f9" + url: "https://pub.dev" + source: hosted + version: "1.24.6" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.dev" + source: hosted + version: "0.6.1" + test_core: + dependency: transitive + description: + name: test_core + sha256: "4bef837e56375537055fdbbbf6dd458b1859881f4c7e6da936158f77d61ab265" + url: "https://pub.dev" + source: hosted + version: "0.5.6" + toml: + dependency: "direct main" + description: + name: toml + sha256: "157c5dca5160fced243f3ce984117f729c788bb5e475504f3dbcda881accee44" + url: "https://pub.dev" + source: hosted + version: "0.14.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + version: + dependency: "direct main" + description: + name: version + sha256: "2307e23a45b43f96469eeab946208ed63293e8afca9c28cd8b5241ff31c55f55" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0fae432c85c4ea880b33b497d32824b97795b04cdaa74d270219572a1f50268d" + url: "https://pub.dev" + source: hosted + version: "11.9.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + yaml: + dependency: "direct main" + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.0.0 <4.0.0" diff --git a/mobile/rust_builder/cargokit/build_tool/pubspec.yaml b/mobile/rust_builder/cargokit/build_tool/pubspec.yaml new file mode 100644 index 0000000000..18c61e3386 --- /dev/null +++ b/mobile/rust_builder/cargokit/build_tool/pubspec.yaml @@ -0,0 +1,33 @@ +# This is copied from Cargokit (which is the official way to use it currently) +# Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +name: build_tool +description: Cargokit build_tool. Facilitates the build of Rust crate during Flutter application build. +publish_to: none +version: 1.0.0 + +environment: + sdk: ">=3.0.0 <4.0.0" + +# Add regular dependencies here. +dependencies: + # these are pinned on purpose because the bundle_tool_runner doesn't have + # pubspec.lock. See run_build_tool.sh + logging: 1.2.0 + path: 1.8.0 + version: 3.0.0 + collection: 1.18.0 + ed25519_edwards: 0.3.1 + hex: 0.2.0 + yaml: 3.1.2 + source_span: 1.10.0 + github: 9.17.0 + args: 2.4.2 + crypto: 3.0.3 + convert: 3.1.1 + http: 1.1.0 + toml: 0.14.0 + +dev_dependencies: + lints: ^2.1.0 + test: ^1.24.0 diff --git a/mobile/rust_builder/cargokit/cmake/cargokit.cmake b/mobile/rust_builder/cargokit/cmake/cargokit.cmake new file mode 100644 index 0000000000..ddd05df9b4 --- /dev/null +++ b/mobile/rust_builder/cargokit/cmake/cargokit.cmake @@ -0,0 +1,99 @@ +SET(cargokit_cmake_root "${CMAKE_CURRENT_LIST_DIR}/..") + +# Workaround for https://github.com/dart-lang/pub/issues/4010 +get_filename_component(cargokit_cmake_root "${cargokit_cmake_root}" REALPATH) + +if(WIN32) + # REALPATH does not properly resolve symlinks on windows :-/ + execute_process(COMMAND powershell -ExecutionPolicy Bypass -File "${CMAKE_CURRENT_LIST_DIR}/resolve_symlinks.ps1" "${cargokit_cmake_root}" OUTPUT_VARIABLE cargokit_cmake_root OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() + +# Arguments +# - target: CMAKE target to which rust library is linked +# - manifest_dir: relative path from current folder to directory containing cargo manifest +# - lib_name: cargo package name +# - any_symbol_name: name of any exported symbol from the library. +# used on windows to force linking with library. +function(apply_cargokit target manifest_dir lib_name any_symbol_name) + + set(CARGOKIT_LIB_NAME "${lib_name}") + set(CARGOKIT_LIB_FULL_NAME "${CMAKE_SHARED_MODULE_PREFIX}${CARGOKIT_LIB_NAME}${CMAKE_SHARED_MODULE_SUFFIX}") + if (CMAKE_CONFIGURATION_TYPES) + set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/$") + set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/$/${CARGOKIT_LIB_FULL_NAME}") + else() + set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") + set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/${CARGOKIT_LIB_FULL_NAME}") + endif() + set(CARGOKIT_TEMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/cargokit_build") + + if (FLUTTER_TARGET_PLATFORM) + set(CARGOKIT_TARGET_PLATFORM "${FLUTTER_TARGET_PLATFORM}") + else() + set(CARGOKIT_TARGET_PLATFORM "windows-x64") + endif() + + set(CARGOKIT_ENV + "CARGOKIT_CMAKE=${CMAKE_COMMAND}" + "CARGOKIT_CONFIGURATION=$" + "CARGOKIT_MANIFEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/${manifest_dir}" + "CARGOKIT_TARGET_TEMP_DIR=${CARGOKIT_TEMP_DIR}" + "CARGOKIT_OUTPUT_DIR=${CARGOKIT_OUTPUT_DIR}" + "CARGOKIT_TARGET_PLATFORM=${CARGOKIT_TARGET_PLATFORM}" + "CARGOKIT_TOOL_TEMP_DIR=${CARGOKIT_TEMP_DIR}/tool" + "CARGOKIT_ROOT_PROJECT_DIR=${CMAKE_SOURCE_DIR}" + ) + + if (WIN32) + set(SCRIPT_EXTENSION ".cmd") + set(IMPORT_LIB_EXTENSION ".lib") + else() + set(SCRIPT_EXTENSION ".sh") + set(IMPORT_LIB_EXTENSION "") + execute_process(COMMAND chmod +x "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}") + endif() + + # Using generators in custom command is only supported in CMake 3.20+ + if (CMAKE_CONFIGURATION_TYPES AND ${CMAKE_VERSION} VERSION_LESS "3.20.0") + foreach(CONFIG IN LISTS CMAKE_CONFIGURATION_TYPES) + add_custom_command( + OUTPUT + "${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}/${CARGOKIT_LIB_FULL_NAME}" + "${CMAKE_CURRENT_BINARY_DIR}/_phony_" + COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV} + "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake + VERBATIM + ) + endforeach() + else() + add_custom_command( + OUTPUT + ${OUTPUT_LIB} + "${CMAKE_CURRENT_BINARY_DIR}/_phony_" + COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV} + "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake + VERBATIM + ) + endif() + + + set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/_phony_" PROPERTIES SYMBOLIC TRUE) + + if (TARGET ${target}) + # If we have actual cmake target provided create target and make existing + # target depend on it + add_custom_target("${target}_cargokit" DEPENDS ${OUTPUT_LIB}) + add_dependencies("${target}" "${target}_cargokit") + target_link_libraries("${target}" PRIVATE "${OUTPUT_LIB}${IMPORT_LIB_EXTENSION}") + if(WIN32) + target_link_options(${target} PRIVATE "/INCLUDE:${any_symbol_name}") + endif() + else() + # Otherwise (FFI) just use ALL to force building always + add_custom_target("${target}_cargokit" ALL DEPENDS ${OUTPUT_LIB}) + endif() + + # Allow adding the output library to plugin bundled libraries + set("${target}_cargokit_lib" ${OUTPUT_LIB} PARENT_SCOPE) + +endfunction() diff --git a/mobile/rust_builder/cargokit/cmake/resolve_symlinks.ps1 b/mobile/rust_builder/cargokit/cmake/resolve_symlinks.ps1 new file mode 100644 index 0000000000..3d10d283c2 --- /dev/null +++ b/mobile/rust_builder/cargokit/cmake/resolve_symlinks.ps1 @@ -0,0 +1,27 @@ +function Resolve-Symlinks { + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [string] $Path + ) + + [string] $separator = '/' + [string[]] $parts = $Path.Split($separator) + + [string] $realPath = '' + foreach ($part in $parts) { + if ($realPath -and !$realPath.EndsWith($separator)) { + $realPath += $separator + } + $realPath += $part + $item = Get-Item $realPath + if ($item.Target) { + $realPath = $item.Target.Replace('\', '/') + } + } + $realPath +} + +$path=Resolve-Symlinks -Path $args[0] +Write-Host $path diff --git a/mobile/rust_builder/cargokit/gradle/plugin.gradle b/mobile/rust_builder/cargokit/gradle/plugin.gradle new file mode 100644 index 0000000000..1aead89136 --- /dev/null +++ b/mobile/rust_builder/cargokit/gradle/plugin.gradle @@ -0,0 +1,179 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import java.nio.file.Paths +import org.apache.tools.ant.taskdefs.condition.Os + +CargoKitPlugin.file = buildscript.sourceFile + +apply plugin: CargoKitPlugin + +class CargoKitExtension { + String manifestDir; // Relative path to folder containing Cargo.toml + String libname; // Library name within Cargo.toml. Must be a cdylib +} + +abstract class CargoKitBuildTask extends DefaultTask { + + @Input + String buildMode + + @Input + String buildDir + + @Input + String outputDir + + @Input + String ndkVersion + + @Input + String sdkDirectory + + @Input + int compileSdkVersion; + + @Input + int minSdkVersion; + + @Input + String pluginFile + + @Input + List targetPlatforms + + @TaskAction + def build() { + if (project.cargokit.manifestDir == null) { + throw new GradleException("Property 'manifestDir' must be set on cargokit extension"); + } + + if (project.cargokit.libname == null) { + throw new GradleException("Property 'libname' must be set on cargokit extension"); + } + + def executableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "run_build_tool.cmd" : "run_build_tool.sh" + def path = Paths.get(new File(pluginFile).parent, "..", executableName); + + def manifestDir = Paths.get(project.buildscript.sourceFile.parent, project.cargokit.manifestDir) + + def rootProjectDir = project.rootProject.projectDir + + if (!Os.isFamily(Os.FAMILY_WINDOWS)) { + project.exec { + commandLine 'chmod', '+x', path + } + } + + project.exec { + executable path + args "build-gradle" + environment "CARGOKIT_ROOT_PROJECT_DIR", rootProjectDir + environment "CARGOKIT_TOOL_TEMP_DIR", "${buildDir}/build_tool" + environment "CARGOKIT_MANIFEST_DIR", manifestDir + environment "CARGOKIT_CONFIGURATION", buildMode + environment "CARGOKIT_TARGET_TEMP_DIR", buildDir + environment "CARGOKIT_OUTPUT_DIR", outputDir + environment "CARGOKIT_NDK_VERSION", ndkVersion + environment "CARGOKIT_SDK_DIR", sdkDirectory + environment "CARGOKIT_COMPILE_SDK_VERSION", compileSdkVersion + environment "CARGOKIT_MIN_SDK_VERSION", minSdkVersion + environment "CARGOKIT_TARGET_PLATFORMS", targetPlatforms.join(",") + environment "CARGOKIT_JAVA_HOME", System.properties['java.home'] + } + } +} + +class CargoKitPlugin implements Plugin { + + static String file; + + private Plugin findFlutterPlugin(Project rootProject) { + _findFlutterPlugin(rootProject.childProjects) + } + + private Plugin _findFlutterPlugin(Map projects) { + for (project in projects) { + for (plugin in project.value.getPlugins()) { + if (plugin.class.name == "FlutterPlugin") { + return plugin; + } + } + def plugin = _findFlutterPlugin(project.value.childProjects); + if (plugin != null) { + return plugin; + } + } + return null; + } + + @Override + void apply(Project project) { + def plugin = findFlutterPlugin(project.rootProject); + + project.extensions.create("cargokit", CargoKitExtension) + + if (plugin == null) { + print("Flutter plugin not found, CargoKit plugin will not be applied.") + return; + } + + def cargoBuildDir = "${project.buildDir}/build" + + // Determine if the project is an application or library + def isApplication = plugin.project.plugins.hasPlugin('com.android.application') + def variants = isApplication ? plugin.project.android.applicationVariants : plugin.project.android.libraryVariants + + variants.all { variant -> + + final buildType = variant.buildType.name + + def cargoOutputDir = "${project.buildDir}/jniLibs/${buildType}"; + def jniLibs = project.android.sourceSets.maybeCreate(buildType).jniLibs; + jniLibs.srcDir(new File(cargoOutputDir)) + + def platforms = plugin.getTargetPlatforms().collect() + + // Same thing addFlutterDependencies does in flutter.gradle + if (buildType == "debug") { + platforms.add("android-x86") + platforms.add("android-x64") + } + + // The task name depends on plugin properties, which are not available + // at this point + project.getGradle().afterProject { + def taskName = "cargokitCargoBuild${project.cargokit.libname.capitalize()}${buildType.capitalize()}"; + + if (project.tasks.findByName(taskName)) { + return + } + + if (plugin.project.android.ndkVersion == null) { + throw new GradleException("Please set 'android.ndkVersion' in 'app/build.gradle'.") + } + + def task = project.tasks.create(taskName, CargoKitBuildTask.class) { + buildMode = variant.buildType.name + buildDir = cargoBuildDir + outputDir = cargoOutputDir + ndkVersion = plugin.project.android.ndkVersion + sdkDirectory = plugin.project.android.sdkDirectory + minSdkVersion = plugin.project.android.defaultConfig.minSdkVersion.apiLevel as int + compileSdkVersion = plugin.project.android.compileSdkVersion.substring(8) as int + targetPlatforms = platforms + pluginFile = CargoKitPlugin.file + } + def onTask = { newTask -> + if (newTask.name == "merge${buildType.capitalize()}NativeLibs") { + newTask.dependsOn task + // Fix gradle 7.4.2 not picking up JNI library changes + newTask.outputs.upToDateWhen { false } + } + } + project.tasks.each onTask + project.tasks.whenTaskAdded onTask + } + } + } +} diff --git a/mobile/rust_builder/cargokit/run_build_tool.cmd b/mobile/rust_builder/cargokit/run_build_tool.cmd new file mode 100755 index 0000000000..c45d0aa8b5 --- /dev/null +++ b/mobile/rust_builder/cargokit/run_build_tool.cmd @@ -0,0 +1,91 @@ +@echo off +setlocal + +setlocal ENABLEDELAYEDEXPANSION + +SET BASEDIR=%~dp0 + +if not exist "%CARGOKIT_TOOL_TEMP_DIR%" ( + mkdir "%CARGOKIT_TOOL_TEMP_DIR%" +) +cd /D "%CARGOKIT_TOOL_TEMP_DIR%" + +SET BUILD_TOOL_PKG_DIR=%BASEDIR%build_tool +SET DART=%FLUTTER_ROOT%\bin\cache\dart-sdk\bin\dart + +set BUILD_TOOL_PKG_DIR_POSIX=%BUILD_TOOL_PKG_DIR:\=/% + +( + echo name: build_tool_runner + echo version: 1.0.0 + echo publish_to: none + echo. + echo environment: + echo sdk: '^>=3.0.0 ^<4.0.0' + echo. + echo dependencies: + echo build_tool: + echo path: %BUILD_TOOL_PKG_DIR_POSIX% +) >pubspec.yaml + +if not exist bin ( + mkdir bin +) + +( + echo import 'package:build_tool/build_tool.dart' as build_tool; + echo void main^(List^ args^) ^{ + echo build_tool.runMain^(args^); + echo ^} +) >bin\build_tool_runner.dart + +SET PRECOMPILED=bin\build_tool_runner.dill + +REM To detect changes in package we compare output of DIR /s (recursive) +set PREV_PACKAGE_INFO=.dart_tool\package_info.prev +set CUR_PACKAGE_INFO=.dart_tool\package_info.cur + +DIR "%BUILD_TOOL_PKG_DIR%" /s > "%CUR_PACKAGE_INFO%_orig" + +REM Last line in dir output is free space on harddrive. That is bound to +REM change between invocation so we need to remove it +( + Set "Line=" + For /F "UseBackQ Delims=" %%A In ("%CUR_PACKAGE_INFO%_orig") Do ( + SetLocal EnableDelayedExpansion + If Defined Line Echo !Line! + EndLocal + Set "Line=%%A") +) >"%CUR_PACKAGE_INFO%" +DEL "%CUR_PACKAGE_INFO%_orig" + +REM Compare current directory listing with previous +FC /B "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" > nul 2>&1 + +If %ERRORLEVEL% neq 0 ( + REM Changed - copy current to previous and remove precompiled kernel + if exist "%PREV_PACKAGE_INFO%" ( + DEL "%PREV_PACKAGE_INFO%" + ) + MOVE /Y "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" + if exist "%PRECOMPILED%" ( + DEL "%PRECOMPILED%" + ) +) + +REM There is no CUR_PACKAGE_INFO it was renamed in previous step to %PREV_PACKAGE_INFO% +REM which means we need to do pub get and precompile +if not exist "%PRECOMPILED%" ( + echo Running pub get in "%cd%" + "%DART%" pub get --no-precompile + "%DART%" compile kernel bin/build_tool_runner.dart +) + +"%DART%" "%PRECOMPILED%" %* + +REM 253 means invalid snapshot version. +If %ERRORLEVEL% equ 253 ( + "%DART%" pub get --no-precompile + "%DART%" compile kernel bin/build_tool_runner.dart + "%DART%" "%PRECOMPILED%" %* +) diff --git a/mobile/rust_builder/cargokit/run_build_tool.sh b/mobile/rust_builder/cargokit/run_build_tool.sh new file mode 100755 index 0000000000..6e594a23d4 --- /dev/null +++ b/mobile/rust_builder/cargokit/run_build_tool.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash + +set -e + +BASEDIR=$(dirname "$0") + +mkdir -p "$CARGOKIT_TOOL_TEMP_DIR" + +cd "$CARGOKIT_TOOL_TEMP_DIR" + +# Write a very simple bin package in temp folder that depends on build_tool package +# from Cargokit. This is done to ensure that we don't pollute Cargokit folder +# with .dart_tool contents. + +BUILD_TOOL_PKG_DIR="$BASEDIR/build_tool" + +if [[ -z $FLUTTER_ROOT ]]; then # not defined + DART=dart +else + DART="$FLUTTER_ROOT/bin/cache/dart-sdk/bin/dart" +fi + +cat << EOF > "pubspec.yaml" +name: build_tool_runner +version: 1.0.0 +publish_to: none + +environment: + sdk: '>=3.0.0 <4.0.0' + +dependencies: + build_tool: + path: "$BUILD_TOOL_PKG_DIR" +EOF + +mkdir -p "bin" + +cat << EOF > "bin/build_tool_runner.dart" +import 'package:build_tool/build_tool.dart' as build_tool; +void main(List args) { + build_tool.runMain(args); +} +EOF + +# Create alias for `shasum` if it does not exist and `sha1sum` exists +if ! [ -x "$(command -v shasum)" ] && [ -x "$(command -v sha1sum)" ]; then + shopt -s expand_aliases + alias shasum="sha1sum" +fi + +# Dart run will not cache any package that has a path dependency, which +# is the case for our build_tool_runner. So instead we precompile the package +# ourselves. +# To invalidate the cached kernel we use the hash of ls -LR of the build_tool +# package directory. This should be good enough, as the build_tool package +# itself is not meant to have any path dependencies. + +if [[ "$OSTYPE" == "darwin"* ]]; then + PACKAGE_HASH=$(ls -lTR "$BUILD_TOOL_PKG_DIR" | shasum) +else + PACKAGE_HASH=$(ls -lR --full-time "$BUILD_TOOL_PKG_DIR" | shasum) +fi + +PACKAGE_HASH_FILE=".package_hash" + +if [ -f "$PACKAGE_HASH_FILE" ]; then + EXISTING_HASH=$(cat "$PACKAGE_HASH_FILE") + if [ "$PACKAGE_HASH" != "$EXISTING_HASH" ]; then + rm "$PACKAGE_HASH_FILE" + fi +fi + +# Run pub get if needed. +if [ ! -f "$PACKAGE_HASH_FILE" ]; then + "$DART" pub get --no-precompile + "$DART" compile kernel bin/build_tool_runner.dart + echo "$PACKAGE_HASH" > "$PACKAGE_HASH_FILE" +fi + +set +e + +"$DART" bin/build_tool_runner.dill "$@" + +exit_code=$? + +# 253 means invalid snapshot version. +if [ $exit_code == 253 ]; then + "$DART" pub get --no-precompile + "$DART" compile kernel bin/build_tool_runner.dart + "$DART" bin/build_tool_runner.dill "$@" + exit_code=$? +fi + +exit $exit_code diff --git a/mobile/rust_builder/ios/Classes/dummy_file.c b/mobile/rust_builder/ios/Classes/dummy_file.c new file mode 100644 index 0000000000..e06dab9968 --- /dev/null +++ b/mobile/rust_builder/ios/Classes/dummy_file.c @@ -0,0 +1 @@ +// This is an empty file to force CocoaPods to create a framework. diff --git a/mobile/rust_builder/ios/rust_lib_photos.podspec b/mobile/rust_builder/ios/rust_lib_photos.podspec new file mode 100644 index 0000000000..2cd1e62b79 --- /dev/null +++ b/mobile/rust_builder/ios/rust_lib_photos.podspec @@ -0,0 +1,45 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint rust_lib_photos.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'rust_lib_photos' + s.version = '0.0.1' + s.summary = 'A new Flutter FFI plugin project.' + s.description = <<-DESC +A new Flutter FFI plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + + # This will ensure the source files in Classes/ are included in the native + # builds of apps using this FFI plugin. Podspec does not support relative + # paths, so Classes contains a forwarder C file that relatively imports + # `../src/*` so that the C sources can be shared among all target platforms. + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'Flutter' + s.platform = :ios, '11.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } + s.swift_version = '5.0' + + s.script_phase = { + :name => 'Build Rust library', + # First argument is relative path to the `rust` folder, second is name of rust library + :script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../../rust rust_lib_photos', + :execution_position => :before_compile, + :input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'], + # Let XCode know that the static library referenced in -force_load below is + # created by this build step. + :output_files => ["${BUILT_PRODUCTS_DIR}/librust_lib_photos.a"], + } + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + # Flutter.framework does not contain a i386 slice. + 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', + 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/librust_lib_photos.a', + } +end \ No newline at end of file diff --git a/mobile/rust_builder/linux/CMakeLists.txt b/mobile/rust_builder/linux/CMakeLists.txt new file mode 100644 index 0000000000..e6656c9227 --- /dev/null +++ b/mobile/rust_builder/linux/CMakeLists.txt @@ -0,0 +1,19 @@ +# The Flutter tooling requires that developers have CMake 3.10 or later +# installed. You should not increase this version, as doing so will cause +# the plugin to fail to compile for some customers of the plugin. +cmake_minimum_required(VERSION 3.10) + +# Project-level configuration. +set(PROJECT_NAME "rust_lib_photos") +project(${PROJECT_NAME} LANGUAGES CXX) + +include("../cargokit/cmake/cargokit.cmake") +apply_cargokit(${PROJECT_NAME} ../../rust rust_lib_photos "") + +# List of absolute paths to libraries that should be bundled with the plugin. +# This list could contain prebuilt libraries, or libraries created by an +# external build triggered from this build file. +set(rust_lib_photos_bundled_libraries + "${${PROJECT_NAME}_cargokit_lib}" + PARENT_SCOPE +) diff --git a/mobile/rust_builder/macos/Classes/dummy_file.c b/mobile/rust_builder/macos/Classes/dummy_file.c new file mode 100644 index 0000000000..e06dab9968 --- /dev/null +++ b/mobile/rust_builder/macos/Classes/dummy_file.c @@ -0,0 +1 @@ +// This is an empty file to force CocoaPods to create a framework. diff --git a/mobile/rust_builder/macos/rust_lib_photos.podspec b/mobile/rust_builder/macos/rust_lib_photos.podspec new file mode 100644 index 0000000000..74ffd45ed4 --- /dev/null +++ b/mobile/rust_builder/macos/rust_lib_photos.podspec @@ -0,0 +1,44 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint rust_lib_photos.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'rust_lib_photos' + s.version = '0.0.1' + s.summary = 'A new Flutter FFI plugin project.' + s.description = <<-DESC +A new Flutter FFI plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + + # This will ensure the source files in Classes/ are included in the native + # builds of apps using this FFI plugin. Podspec does not support relative + # paths, so Classes contains a forwarder C file that relatively imports + # `../src/*` so that the C sources can be shared among all target platforms. + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'FlutterMacOS' + + s.platform = :osx, '10.11' + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + s.swift_version = '5.0' + + s.script_phase = { + :name => 'Build Rust library', + # First argument is relative path to the `rust` folder, second is name of rust library + :script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../../rust rust_lib_photos', + :execution_position => :before_compile, + :input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'], + # Let XCode know that the static library referenced in -force_load below is + # created by this build step. + :output_files => ["${BUILT_PRODUCTS_DIR}/librust_lib_photos.a"], + } + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + # Flutter.framework does not contain a i386 slice. + 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', + 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/librust_lib_photos.a', + } +end \ No newline at end of file diff --git a/mobile/rust_builder/pubspec.yaml b/mobile/rust_builder/pubspec.yaml new file mode 100644 index 0000000000..31b699e0a5 --- /dev/null +++ b/mobile/rust_builder/pubspec.yaml @@ -0,0 +1,34 @@ +name: rust_lib_photos +description: "Utility to build Rust code" +version: 0.0.1 +publish_to: none + +environment: + sdk: '>=3.3.0 <4.0.0' + flutter: '>=3.3.0' + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + ffi: ^2.0.2 + ffigen: ^11.0.0 + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + +flutter: + plugin: + platforms: + android: + ffiPlugin: true + ios: + ffiPlugin: true + linux: + ffiPlugin: true + macos: + ffiPlugin: true + windows: + ffiPlugin: true diff --git a/mobile/rust_builder/windows/.gitignore b/mobile/rust_builder/windows/.gitignore new file mode 100644 index 0000000000..b3eb2be169 --- /dev/null +++ b/mobile/rust_builder/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/mobile/rust_builder/windows/CMakeLists.txt b/mobile/rust_builder/windows/CMakeLists.txt new file mode 100644 index 0000000000..4640016d5c --- /dev/null +++ b/mobile/rust_builder/windows/CMakeLists.txt @@ -0,0 +1,20 @@ +# The Flutter tooling requires that developers have a version of Visual Studio +# installed that includes CMake 3.14 or later. You should not increase this +# version, as doing so will cause the plugin to fail to compile for some +# customers of the plugin. +cmake_minimum_required(VERSION 3.14) + +# Project-level configuration. +set(PROJECT_NAME "rust_lib_photos") +project(${PROJECT_NAME} LANGUAGES CXX) + +include("../cargokit/cmake/cargokit.cmake") +apply_cargokit(${PROJECT_NAME} ../../../../../../rust rust_lib_photos "") + +# List of absolute paths to libraries that should be bundled with the plugin. +# This list could contain prebuilt libraries, or libraries created by an +# external build triggered from this build file. +set(rust_lib_photos_bundled_libraries + "${${PROJECT_NAME}_cargokit_lib}" + PARENT_SCOPE +) diff --git a/mobile/test_driver/integration_test.dart b/mobile/test_driver/integration_test.dart new file mode 100644 index 0000000000..b38629cca9 --- /dev/null +++ b/mobile/test_driver/integration_test.dart @@ -0,0 +1,3 @@ +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); From af817ec4394057879bfe6b2681a6afe66ec70d49 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Fri, 4 Apr 2025 11:49:54 +0530 Subject: [PATCH 004/156] Test rust --- .../debug/ml_debug_section_widget.dart | 22 ++++++++++++++++ mobile/rust/Cargo.lock | 25 +++++++++++++------ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index 1a78f7c851..e7bf9e9b18 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -12,6 +12,7 @@ import "package:photos/services/machine_learning/ml_indexing_isolate.dart"; import 'package:photos/services/machine_learning/ml_service.dart'; import "package:photos/services/machine_learning/semantic_search/semantic_search_service.dart"; import "package:photos/services/memory_home_widget_service.dart"; +import "package:photos/src/rust/api/simple.dart"; import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/expandable_menu_item_widget.dart'; @@ -66,6 +67,27 @@ class _MLDebugSectionWidgetState extends State { logger.info("Building ML Debug section options"); return Column( children: [ + sectionOptionSpacing, + MenuItemWidget( + captionedTextWidget: const CaptionedTextWidget( + title: "Test rust bridge", + ), + pressedColor: getEnteColorScheme(context).fillFaint, + trailingIcon: Icons.chevron_right_outlined, + trailingIconIsMuted: true, + onTap: () async { + try { + final String greetings = greet(name: "Tom"); + const String expected = "Hello, Tom!"; + assert(greetings == expected); + debugPrint("String from rust: $greetings"); + showShortToast(context, greetings); + } catch (e, s) { + logger.warning('Rust bridge failed ', e, s); + await showGenericErrorDialog(context: context, error: e); + } + }, + ), sectionOptionSpacing, MenuItemWidget( captionedTextWidget: FutureBuilder( diff --git a/mobile/rust/Cargo.lock b/mobile/rust/Cargo.lock index 430c41215d..8f8129c255 100644 --- a/mobile/rust/Cargo.lock +++ b/mobile/rust/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "allo-isolate" -version = "0.1.25" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b6d794345b06592d0ebeed8e477e41b71e5a0a49df4fc0e4184d5938b99509" +checksum = "449e356a4864c017286dbbec0e12767ea07efba29e3b7d984194c2a7ff3c4550" dependencies = [ "anyhow", "atomic", @@ -157,10 +157,10 @@ dependencies = [ ] [[package]] -name = "dart-sys-fork" -version = "4.1.1" +name = "dart-sys" +version = "4.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "933dafff26172b719bb9695dd3715a1e7792f62dcdc8a5d4c740db7e0fedee8b" +checksum = "57967e4b200d767d091b961d6ab42cc7d0cc14fe9e052e75d0d3cf9eb732d895" dependencies = [ "cc", ] @@ -209,6 +209,8 @@ dependencies = [ [[package]] name = "flutter_rust_bridge" version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f8c0dee6249225e815dcff3f3a39b98d9f66fdb3c392a432715b646bfa4da02" dependencies = [ "allo-isolate", "android_logger", @@ -217,7 +219,7 @@ dependencies = [ "bytemuck", "byteorder", "console_error_panic_hook", - "dart-sys-fork", + "dart-sys", "delegate-attr", "flutter_rust_bridge_macros", "futures", @@ -225,6 +227,7 @@ dependencies = [ "lazy_static", "log", "oslog", + "portable-atomic", "threadpool", "tokio", "wasm-bindgen", @@ -235,6 +238,8 @@ dependencies = [ [[package]] name = "flutter_rust_bridge_macros" version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e88d604908d9eccb4ca9c26640ce41033165cbef041460e704ae28bd5208bce" dependencies = [ "hex", "md-5", @@ -460,6 +465,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + [[package]] name = "proc-macro2" version = "1.0.70" From e707e24da969f9eed2b3cca46d50b8dcb9d88413 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Sat, 5 Apr 2025 16:10:39 +0530 Subject: [PATCH 005/156] first integration of usearch --- mobile/lib/src/rust/api/usearch_api.dart | 61 ++ mobile/lib/src/rust/frb_generated.dart | 525 +++++++++++++++++- mobile/lib/src/rust/frb_generated.io.dart | 112 ++++ mobile/lib/src/rust/frb_generated.web.dart | 112 ++++ .../debug/ml_debug_section_widget.dart | 45 ++ .../.gradle/8.5/checksums/checksums.lock | Bin 17 -> 17 bytes .../.gradle/8.5/checksums/sha1-checksums.bin | Bin 0 -> 28757 bytes .../.gradle/8.5/fileHashes/fileHashes.lock | Bin 17 -> 17 bytes .../buildOutputCleanup.lock | Bin 17 -> 17 bytes .../buildOutputCleanup/outputFiles.bin | Bin 18803 -> 19991 bytes mobile/rust/Cargo.lock | 177 ++++++ mobile/rust/Cargo.toml | 1 + mobile/rust/src/api/mod.rs | 1 + mobile/rust/src/api/usearch_api.rs | 116 ++++ mobile/rust/src/frb_generated.rs | 427 +++++++++++++- 15 files changed, 1571 insertions(+), 6 deletions(-) create mode 100644 mobile/lib/src/rust/api/usearch_api.dart create mode 100644 mobile/plugins/onnx_dart/android/.gradle/8.5/checksums/sha1-checksums.bin create mode 100644 mobile/rust/src/api/usearch_api.rs diff --git a/mobile/lib/src/rust/api/usearch_api.dart b/mobile/lib/src/rust/api/usearch_api.dart new file mode 100644 index 0000000000..0f68ea774d --- /dev/null +++ b/mobile/lib/src/rust/api/usearch_api.dart @@ -0,0 +1,61 @@ +// This file is automatically generated, so please do not edit it. +// @generated by `flutter_rust_bridge`@ 2.9.0. + +// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import + +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; +import 'package:photos/src/rust/frb_generated.dart'; + +// These functions are ignored because they are not marked as `pub`: `create_index`, `ensure_capacity`, `get_index`, `load_index`, `save_index` + +Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> getIndexStats({ + required String indexPath, +}) => + RustLib.instance.api.crateApiUsearchApiGetIndexStats(indexPath: indexPath); + +Future addVector({ + required String indexPath, + required BigInt key, + required List vector, +}) => + RustLib.instance.api.crateApiUsearchApiAddVector( + indexPath: indexPath, + key: key, + vector: vector, + ); + +Future bulkAddVectors({ + required String indexPath, + required Uint64List keys, + required List vectors, +}) => + RustLib.instance.api.crateApiUsearchApiBulkAddVectors( + indexPath: indexPath, + keys: keys, + vectors: vectors, + ); + +Future<(Uint64List, Float32List)> searchVectors({ + required String indexPath, + required List query, + required BigInt count, +}) => + RustLib.instance.api.crateApiUsearchApiSearchVectors( + indexPath: indexPath, + query: query, + count: count, + ); + +Future getVector({ + required String indexPath, + required BigInt key, +}) => + RustLib.instance.api + .crateApiUsearchApiGetVector(indexPath: indexPath, key: key); + +Future removeVector({required String indexPath, required BigInt key}) => + RustLib.instance.api + .crateApiUsearchApiRemoveVector(indexPath: indexPath, key: key); + +Future resetIndex({required String indexPath}) => + RustLib.instance.api.crateApiUsearchApiResetIndex(indexPath: indexPath); diff --git a/mobile/lib/src/rust/frb_generated.dart b/mobile/lib/src/rust/frb_generated.dart index ee5f0f4ec9..09ccbbe9bc 100644 --- a/mobile/lib/src/rust/frb_generated.dart +++ b/mobile/lib/src/rust/frb_generated.dart @@ -8,6 +8,7 @@ import 'dart:convert'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; import 'package:photos/src/rust/api/simple.dart'; +import 'package:photos/src/rust/api/usearch_api.dart'; import 'package:photos/src/rust/frb_generated.dart'; import 'package:photos/src/rust/frb_generated.io.dart' if (dart.library.js_interop) 'frb_generated.web.dart'; @@ -69,7 +70,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.9.0'; @override - int get rustContentHash => -1918914929; + int get rustContentHash => 2006111302; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( @@ -80,9 +81,42 @@ class RustLib extends BaseEntrypoint { } abstract class RustLibApi extends BaseApi { + Future crateApiUsearchApiAddVector({ + required String indexPath, + required BigInt key, + required List vector, + }); + + Future crateApiUsearchApiBulkAddVectors({ + required String indexPath, + required Uint64List keys, + required List vectors, + }); + + Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> + crateApiUsearchApiGetIndexStats({required String indexPath}); + + Future crateApiUsearchApiGetVector({ + required String indexPath, + required BigInt key, + }); + String crateApiSimpleGreet({required String name}); Future crateApiSimpleInitApp(); + + Future crateApiUsearchApiRemoveVector({ + required String indexPath, + required BigInt key, + }); + + Future crateApiUsearchApiResetIndex({required String indexPath}); + + Future<(Uint64List, Float32List)> crateApiUsearchApiSearchVectors({ + required String indexPath, + required List query, + required BigInt count, + }); } class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @@ -93,6 +127,147 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { required super.portManager, }); + @override + Future crateApiUsearchApiAddVector({ + required String indexPath, + required BigInt key, + required List vector, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_String(indexPath, serializer); + sse_encode_u_64(key, serializer); + sse_encode_list_prim_f_32_loose(vector, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 1, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: null, + ), + constMeta: kCrateApiUsearchApiAddVectorConstMeta, + argValues: [indexPath, key, vector], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiUsearchApiAddVectorConstMeta => + const TaskConstMeta( + debugName: "add_vector", + argNames: ["indexPath", "key", "vector"], + ); + + @override + Future crateApiUsearchApiBulkAddVectors({ + required String indexPath, + required Uint64List keys, + required List vectors, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_String(indexPath, serializer); + sse_encode_list_prim_u_64_strict(keys, serializer); + sse_encode_list_list_prim_f_32_strict(vectors, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 2, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: null, + ), + constMeta: kCrateApiUsearchApiBulkAddVectorsConstMeta, + argValues: [indexPath, keys, vectors], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiUsearchApiBulkAddVectorsConstMeta => + const TaskConstMeta( + debugName: "bulk_add_vectors", + argNames: ["indexPath", "keys", "vectors"], + ); + + @override + Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> + crateApiUsearchApiGetIndexStats({required String indexPath}) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_String(indexPath, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 3, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_record_usize_usize_usize_usize_usize, + decodeErrorData: null, + ), + constMeta: kCrateApiUsearchApiGetIndexStatsConstMeta, + argValues: [indexPath], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiUsearchApiGetIndexStatsConstMeta => + const TaskConstMeta( + debugName: "get_index_stats", + argNames: ["indexPath"], + ); + + @override + Future crateApiUsearchApiGetVector({ + required String indexPath, + required BigInt key, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_String(indexPath, serializer); + sse_encode_u_64(key, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 4, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_f_32_strict, + decodeErrorData: null, + ), + constMeta: kCrateApiUsearchApiGetVectorConstMeta, + argValues: [indexPath, key], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiUsearchApiGetVectorConstMeta => + const TaskConstMeta( + debugName: "get_vector", + argNames: ["indexPath", "key"], + ); + @override String crateApiSimpleGreet({required String name}) { return handler.executeSync( @@ -100,7 +275,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(name, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 1)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 5)!; }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -127,7 +302,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 2, + funcId: 6, port: port_, ); }, @@ -147,18 +322,193 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: [], ); + @override + Future crateApiUsearchApiRemoveVector({ + required String indexPath, + required BigInt key, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_String(indexPath, serializer); + sse_encode_u_64(key, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 7, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_usize, + decodeErrorData: null, + ), + constMeta: kCrateApiUsearchApiRemoveVectorConstMeta, + argValues: [indexPath, key], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiUsearchApiRemoveVectorConstMeta => + const TaskConstMeta( + debugName: "remove_vector", + argNames: ["indexPath", "key"], + ); + + @override + Future crateApiUsearchApiResetIndex({required String indexPath}) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_String(indexPath, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 8, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: null, + ), + constMeta: kCrateApiUsearchApiResetIndexConstMeta, + argValues: [indexPath], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiUsearchApiResetIndexConstMeta => + const TaskConstMeta( + debugName: "reset_index", + argNames: ["indexPath"], + ); + + @override + Future<(Uint64List, Float32List)> crateApiUsearchApiSearchVectors({ + required String indexPath, + required List query, + required BigInt count, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_String(indexPath, serializer); + sse_encode_list_prim_f_32_loose(query, serializer); + sse_encode_usize(count, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 9, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: + sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict, + decodeErrorData: null, + ), + constMeta: kCrateApiUsearchApiSearchVectorsConstMeta, + argValues: [indexPath, query, count], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiUsearchApiSearchVectorsConstMeta => + const TaskConstMeta( + debugName: "search_vectors", + argNames: ["indexPath", "query", "count"], + ); + @protected String dco_decode_String(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs return raw as String; } + @protected + double dco_decode_f_32(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw as double; + } + + @protected + List dco_decode_list_list_prim_f_32_strict(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return (raw as List) + .map(dco_decode_list_prim_f_32_strict) + .toList(); + } + + @protected + List dco_decode_list_prim_f_32_loose(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw as List; + } + + @protected + Float32List dco_decode_list_prim_f_32_strict(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw as Float32List; + } + + @protected + Uint64List dco_decode_list_prim_u_64_strict(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return dcoDecodeUint64List(raw); + } + @protected Uint8List dco_decode_list_prim_u_8_strict(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs return raw as Uint8List; } + @protected + ( + Uint64List, + Float32List + ) dco_decode_record_list_prim_u_64_strict_list_prim_f_32_strict(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 2) { + throw Exception('Expected 2 elements, got ${arr.length}'); + } + return ( + dco_decode_list_prim_u_64_strict(arr[0]), + dco_decode_list_prim_f_32_strict(arr[1]), + ); + } + + @protected + (BigInt, BigInt, BigInt, BigInt, BigInt) + dco_decode_record_usize_usize_usize_usize_usize(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 5) { + throw Exception('Expected 5 elements, got ${arr.length}'); + } + return ( + dco_decode_usize(arr[0]), + dco_decode_usize(arr[1]), + dco_decode_usize(arr[2]), + dco_decode_usize(arr[3]), + dco_decode_usize(arr[4]), + ); + } + + @protected + BigInt dco_decode_u_64(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return dcoDecodeU64(raw); + } + @protected int dco_decode_u_8(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -171,6 +521,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return; } + @protected + BigInt dco_decode_usize(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return dcoDecodeU64(raw); + } + @protected String sse_decode_String(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -178,6 +534,47 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return utf8.decoder.convert(inner); } + @protected + double sse_decode_f_32(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return deserializer.buffer.getFloat32(); + } + + @protected + List sse_decode_list_list_prim_f_32_strict( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + + final len_ = sse_decode_i_32(deserializer); + final ans_ = []; + for (var idx_ = 0; idx_ < len_; ++idx_) { + ans_.add(sse_decode_list_prim_f_32_strict(deserializer)); + } + return ans_; + } + + @protected + List sse_decode_list_prim_f_32_loose(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + final len_ = sse_decode_i_32(deserializer); + return deserializer.buffer.getFloat32List(len_); + } + + @protected + Float32List sse_decode_list_prim_f_32_strict(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + final len_ = sse_decode_i_32(deserializer); + return deserializer.buffer.getFloat32List(len_); + } + + @protected + Uint64List sse_decode_list_prim_u_64_strict(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + final len_ = sse_decode_i_32(deserializer); + return deserializer.buffer.getUint64List(len_); + } + @protected Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -185,6 +582,37 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return deserializer.buffer.getUint8List(len_); } + @protected + (Uint64List, Float32List) + sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + final var_field0 = sse_decode_list_prim_u_64_strict(deserializer); + final var_field1 = sse_decode_list_prim_f_32_strict(deserializer); + return (var_field0, var_field1); + } + + @protected + (BigInt, BigInt, BigInt, BigInt, BigInt) + sse_decode_record_usize_usize_usize_usize_usize( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + final var_field0 = sse_decode_usize(deserializer); + final var_field1 = sse_decode_usize(deserializer); + final var_field2 = sse_decode_usize(deserializer); + final var_field3 = sse_decode_usize(deserializer); + final var_field4 = sse_decode_usize(deserializer); + return (var_field0, var_field1, var_field2, var_field3, var_field4); + } + + @protected + BigInt sse_decode_u_64(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return deserializer.buffer.getBigUint64(); + } + @protected int sse_decode_u_8(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -196,6 +624,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Sse (Serialization based), see doc to use other codecs } + @protected + BigInt sse_decode_usize(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return deserializer.buffer.getBigUint64(); + } + @protected int sse_decode_i_32(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -214,6 +648,56 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_strict(utf8.encoder.convert(self), serializer); } + @protected + void sse_encode_f_32(double self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + serializer.buffer.putFloat32(self); + } + + @protected + void sse_encode_list_list_prim_f_32_strict( + List self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_i_32(self.length, serializer); + for (final item in self) { + sse_encode_list_prim_f_32_strict(item, serializer); + } + } + + @protected + void sse_encode_list_prim_f_32_loose( + List self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_i_32(self.length, serializer); + serializer.buffer.putFloat32List( + self is Float32List ? self : Float32List.fromList(self), + ); + } + + @protected + void sse_encode_list_prim_f_32_strict( + Float32List self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_i_32(self.length, serializer); + serializer.buffer.putFloat32List(self); + } + + @protected + void sse_encode_list_prim_u_64_strict( + Uint64List self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_i_32(self.length, serializer); + serializer.buffer.putUint64List(self); + } + @protected void sse_encode_list_prim_u_8_strict( Uint8List self, @@ -224,6 +708,35 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { serializer.buffer.putUint8List(self); } + @protected + void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( + (Uint64List, Float32List) self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_list_prim_u_64_strict(self.$1, serializer); + sse_encode_list_prim_f_32_strict(self.$2, serializer); + } + + @protected + void sse_encode_record_usize_usize_usize_usize_usize( + (BigInt, BigInt, BigInt, BigInt, BigInt) self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_usize(self.$1, serializer); + sse_encode_usize(self.$2, serializer); + sse_encode_usize(self.$3, serializer); + sse_encode_usize(self.$4, serializer); + sse_encode_usize(self.$5, serializer); + } + + @protected + void sse_encode_u_64(BigInt self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + serializer.buffer.putBigUint64(self); + } + @protected void sse_encode_u_8(int self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -235,6 +748,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Sse (Serialization based), see doc to use other codecs } + @protected + void sse_encode_usize(BigInt self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + serializer.buffer.putBigUint64(self); + } + @protected void sse_encode_i_32(int self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs diff --git a/mobile/lib/src/rust/frb_generated.io.dart b/mobile/lib/src/rust/frb_generated.io.dart index 99f64b1ce5..47d45ebfad 100644 --- a/mobile/lib/src/rust/frb_generated.io.dart +++ b/mobile/lib/src/rust/frb_generated.io.dart @@ -9,6 +9,7 @@ import 'dart:ffi' as ffi; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_io.dart'; import 'package:photos/src/rust/api/simple.dart'; +import 'package:photos/src/rust/api/usearch_api.dart'; import 'package:photos/src/rust/frb_generated.dart'; abstract class RustLibApiImplPlatform extends BaseApiImpl { @@ -22,27 +23,93 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected String dco_decode_String(dynamic raw); + @protected + double dco_decode_f_32(dynamic raw); + + @protected + List dco_decode_list_list_prim_f_32_strict(dynamic raw); + + @protected + List dco_decode_list_prim_f_32_loose(dynamic raw); + + @protected + Float32List dco_decode_list_prim_f_32_strict(dynamic raw); + + @protected + Uint64List dco_decode_list_prim_u_64_strict(dynamic raw); + @protected Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); + @protected + ( + Uint64List, + Float32List + ) dco_decode_record_list_prim_u_64_strict_list_prim_f_32_strict(dynamic raw); + + @protected + (BigInt, BigInt, BigInt, BigInt, BigInt) + dco_decode_record_usize_usize_usize_usize_usize(dynamic raw); + + @protected + BigInt dco_decode_u_64(dynamic raw); + @protected int dco_decode_u_8(dynamic raw); @protected void dco_decode_unit(dynamic raw); + @protected + BigInt dco_decode_usize(dynamic raw); + @protected String sse_decode_String(SseDeserializer deserializer); + @protected + double sse_decode_f_32(SseDeserializer deserializer); + + @protected + List sse_decode_list_list_prim_f_32_strict( + SseDeserializer deserializer, + ); + + @protected + List sse_decode_list_prim_f_32_loose(SseDeserializer deserializer); + + @protected + Float32List sse_decode_list_prim_f_32_strict(SseDeserializer deserializer); + + @protected + Uint64List sse_decode_list_prim_u_64_strict(SseDeserializer deserializer); + @protected Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); + @protected + (Uint64List, Float32List) + sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( + SseDeserializer deserializer, + ); + + @protected + (BigInt, BigInt, BigInt, BigInt, BigInt) + sse_decode_record_usize_usize_usize_usize_usize( + SseDeserializer deserializer, + ); + + @protected + BigInt sse_decode_u_64(SseDeserializer deserializer); + @protected int sse_decode_u_8(SseDeserializer deserializer); @protected void sse_decode_unit(SseDeserializer deserializer); + @protected + BigInt sse_decode_usize(SseDeserializer deserializer); + @protected int sse_decode_i_32(SseDeserializer deserializer); @@ -52,18 +119,63 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_String(String self, SseSerializer serializer); + @protected + void sse_encode_f_32(double self, SseSerializer serializer); + + @protected + void sse_encode_list_list_prim_f_32_strict( + List self, + SseSerializer serializer, + ); + + @protected + void sse_encode_list_prim_f_32_loose( + List self, + SseSerializer serializer, + ); + + @protected + void sse_encode_list_prim_f_32_strict( + Float32List self, + SseSerializer serializer, + ); + + @protected + void sse_encode_list_prim_u_64_strict( + Uint64List self, + SseSerializer serializer, + ); + @protected void sse_encode_list_prim_u_8_strict( Uint8List self, SseSerializer serializer, ); + @protected + void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( + (Uint64List, Float32List) self, + SseSerializer serializer, + ); + + @protected + void sse_encode_record_usize_usize_usize_usize_usize( + (BigInt, BigInt, BigInt, BigInt, BigInt) self, + SseSerializer serializer, + ); + + @protected + void sse_encode_u_64(BigInt self, SseSerializer serializer); + @protected void sse_encode_u_8(int self, SseSerializer serializer); @protected void sse_encode_unit(void self, SseSerializer serializer); + @protected + void sse_encode_usize(BigInt self, SseSerializer serializer); + @protected void sse_encode_i_32(int self, SseSerializer serializer); diff --git a/mobile/lib/src/rust/frb_generated.web.dart b/mobile/lib/src/rust/frb_generated.web.dart index 7c87e7b38c..10a1839fe4 100644 --- a/mobile/lib/src/rust/frb_generated.web.dart +++ b/mobile/lib/src/rust/frb_generated.web.dart @@ -11,6 +11,7 @@ import 'dart:convert'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_web.dart'; import 'package:photos/src/rust/api/simple.dart'; +import 'package:photos/src/rust/api/usearch_api.dart'; import 'package:photos/src/rust/frb_generated.dart'; abstract class RustLibApiImplPlatform extends BaseApiImpl { @@ -24,27 +25,93 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected String dco_decode_String(dynamic raw); + @protected + double dco_decode_f_32(dynamic raw); + + @protected + List dco_decode_list_list_prim_f_32_strict(dynamic raw); + + @protected + List dco_decode_list_prim_f_32_loose(dynamic raw); + + @protected + Float32List dco_decode_list_prim_f_32_strict(dynamic raw); + + @protected + Uint64List dco_decode_list_prim_u_64_strict(dynamic raw); + @protected Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); + @protected + ( + Uint64List, + Float32List + ) dco_decode_record_list_prim_u_64_strict_list_prim_f_32_strict(dynamic raw); + + @protected + (BigInt, BigInt, BigInt, BigInt, BigInt) + dco_decode_record_usize_usize_usize_usize_usize(dynamic raw); + + @protected + BigInt dco_decode_u_64(dynamic raw); + @protected int dco_decode_u_8(dynamic raw); @protected void dco_decode_unit(dynamic raw); + @protected + BigInt dco_decode_usize(dynamic raw); + @protected String sse_decode_String(SseDeserializer deserializer); + @protected + double sse_decode_f_32(SseDeserializer deserializer); + + @protected + List sse_decode_list_list_prim_f_32_strict( + SseDeserializer deserializer, + ); + + @protected + List sse_decode_list_prim_f_32_loose(SseDeserializer deserializer); + + @protected + Float32List sse_decode_list_prim_f_32_strict(SseDeserializer deserializer); + + @protected + Uint64List sse_decode_list_prim_u_64_strict(SseDeserializer deserializer); + @protected Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); + @protected + (Uint64List, Float32List) + sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( + SseDeserializer deserializer, + ); + + @protected + (BigInt, BigInt, BigInt, BigInt, BigInt) + sse_decode_record_usize_usize_usize_usize_usize( + SseDeserializer deserializer, + ); + + @protected + BigInt sse_decode_u_64(SseDeserializer deserializer); + @protected int sse_decode_u_8(SseDeserializer deserializer); @protected void sse_decode_unit(SseDeserializer deserializer); + @protected + BigInt sse_decode_usize(SseDeserializer deserializer); + @protected int sse_decode_i_32(SseDeserializer deserializer); @@ -54,18 +121,63 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_String(String self, SseSerializer serializer); + @protected + void sse_encode_f_32(double self, SseSerializer serializer); + + @protected + void sse_encode_list_list_prim_f_32_strict( + List self, + SseSerializer serializer, + ); + + @protected + void sse_encode_list_prim_f_32_loose( + List self, + SseSerializer serializer, + ); + + @protected + void sse_encode_list_prim_f_32_strict( + Float32List self, + SseSerializer serializer, + ); + + @protected + void sse_encode_list_prim_u_64_strict( + Uint64List self, + SseSerializer serializer, + ); + @protected void sse_encode_list_prim_u_8_strict( Uint8List self, SseSerializer serializer, ); + @protected + void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( + (Uint64List, Float32List) self, + SseSerializer serializer, + ); + + @protected + void sse_encode_record_usize_usize_usize_usize_usize( + (BigInt, BigInt, BigInt, BigInt, BigInt) self, + SseSerializer serializer, + ); + + @protected + void sse_encode_u_64(BigInt self, SseSerializer serializer); + @protected void sse_encode_u_8(int self, SseSerializer serializer); @protected void sse_encode_unit(void self, SseSerializer serializer); + @protected + void sse_encode_usize(BigInt self, SseSerializer serializer); + @protected void sse_encode_i_32(int self, SseSerializer serializer); diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index e7bf9e9b18..809249ff3d 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -1,7 +1,10 @@ import "dart:async"; +import "dart:typed_data" show Float32List; import 'package:flutter/material.dart'; +import "package:flutter_rust_bridge/flutter_rust_bridge.dart"; import "package:logging/logging.dart"; +import "package:path_provider/path_provider.dart"; import "package:photos/core/event_bus.dart"; import "package:photos/db/ml/db.dart"; import "package:photos/events/people_changed_event.dart"; @@ -13,6 +16,7 @@ import 'package:photos/services/machine_learning/ml_service.dart'; import "package:photos/services/machine_learning/semantic_search/semantic_search_service.dart"; import "package:photos/services/memory_home_widget_service.dart"; import "package:photos/src/rust/api/simple.dart"; +import "package:photos/src/rust/api/usearch_api.dart"; import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/expandable_menu_item_widget.dart'; @@ -67,6 +71,47 @@ class _MLDebugSectionWidgetState extends State { logger.info("Building ML Debug section options"); return Column( children: [ + sectionOptionSpacing, + MenuItemWidget( + captionedTextWidget: const CaptionedTextWidget( + title: "Do some usearch", + ), + pressedColor: getEnteColorScheme(context).fillFaint, + trailingIcon: Icons.chevron_right_outlined, + trailingIconIsMuted: true, + onTap: () async { + try { + final allImageEmbeddings = await mlDataDB.getAllClipVectors(); + final tenVectors = allImageEmbeddings.sublist(0, 10); + final tenEmbeddings = tenVectors + .map((e) => Float32List.fromList(e.vector.toList())) + .toList(); + final tenKeys = + Uint64List.fromList(tenVectors.map((e) => e.fileID).toList()); + final indexPath = (await getApplicationSupportDirectory()).path + + "/ml/test/vector_db"; + final stats = await getIndexStats(indexPath: indexPath); + logger.info("vector_db stats: $stats"); + await bulkAddVectors( + indexPath: indexPath, + keys: tenKeys, + vectors: tenEmbeddings, + ); + final statsAgain = await getIndexStats(indexPath: indexPath); + logger.info("vector_db stats again: $statsAgain"); + final size = statsAgain.$1; + final capacity = statsAgain.$2; + final dimensions = statsAgain.$3; + showShortToast( + context, + "Size: $size, Capacity: $capacity, Dimensions: $dimensions", + ); + } catch (e, s) { + logger.warning('Rust bridge failed ', e, s); + await showGenericErrorDialog(context: context, error: e); + } + }, + ), sectionOptionSpacing, MenuItemWidget( captionedTextWidget: const CaptionedTextWidget( diff --git a/mobile/plugins/onnx_dart/android/.gradle/8.5/checksums/checksums.lock b/mobile/plugins/onnx_dart/android/.gradle/8.5/checksums/checksums.lock index eda644daa6677ab14f682a0381856df2a14f0266..4170ac40d0b243e218cf9d41fc421b45dcab1664 100644 GIT binary patch literal 17 UcmZR6XxjSgdG35&1_(F+06ldDc>n+a literal 17 TcmZR6XxjSgdG35&1}FdkJhcS5 diff --git a/mobile/plugins/onnx_dart/android/.gradle/8.5/checksums/sha1-checksums.bin b/mobile/plugins/onnx_dart/android/.gradle/8.5/checksums/sha1-checksums.bin new file mode 100644 index 0000000000000000000000000000000000000000..ce391d4fab858064d816ada7a46eefa9161f2c6c GIT binary patch literal 28757 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 YpNzi9E=jxLaTv?>8dz=cy8`X_A07}ZlK=n! literal 0 HcmV?d00001 diff --git a/mobile/plugins/onnx_dart/android/.gradle/8.5/fileHashes/fileHashes.lock b/mobile/plugins/onnx_dart/android/.gradle/8.5/fileHashes/fileHashes.lock index 4e4d51aaa98d5771c3e2529e7fa8709d0653905f..5d34963075f1959294af5afd004f598323243294 100644 GIT binary patch literal 17 UcmZSf+J8sOBH}_P0|f8@05PNl2mk;8 literal 17 UcmZSf+J8sOBH}_P0|amZ05PHj1^@s6 diff --git a/mobile/plugins/onnx_dart/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/mobile/plugins/onnx_dart/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock index ca921a15e6f7603cd357ea4d15bd18c5b5f9e0d5..a77c685bd872acfe8cfccda6647d487d7f19c380 100644 GIT binary patch literal 17 UcmZSHaO=SBr8}5286Y4606}yGN&o-= literal 17 UcmZSHaO=SBr8}5286ZFe06{PX1^@s6 diff --git a/mobile/plugins/onnx_dart/android/.gradle/buildOutputCleanup/outputFiles.bin b/mobile/plugins/onnx_dart/android/.gradle/buildOutputCleanup/outputFiles.bin index a49084dcd8cd5f9a8034b4efd86efbc49cce6092..4455dbfbfee0dd02c533b8619b17c46f1e8f5e3c 100644 GIT binary patch literal 19991 zcmeI&eNfYN9Ki9(kY_{)Y)D0bm!cp~V)7&cGd8xLfUpRXcoHe)WP(bdSz2=;B?QNV zaHJ{N`K)0F4FUm4Fvtw#K?^3%j3soNq7|vZeZQZ1-L5;1^N-FxyW7~k_W6Cb-+K)H z04|Hgnf-`fSdTAOkMH6=EPw^D02aUkSO5!P0W5$8umBdo0$2bGU;!+E1+V}Xzyes{ zX(=F@I1wXqG8`wJ$vbA5ES9Uu#GQzoCB1g{B`GQ7VZku`{h+A8vO6xLI-9uA4ZL{2 zG&S*7mN}iz1228M-%OdDCne5n1TSk>7A2bp@`#7b*Z3dZB|#rAeSmJxXLjSI2dk;?)Od4@F}sg5PF(N~ z`0;Psw#^Mtbra{;fy?BzabqvUv=HZwf!8nfH62O}>!Kb2-jeHnbY0u~o9X^>@bmE} z52(hCcT+b9Z~gv|U&r%|DRE&6_{ATiuk5$kU`?Fa2j02p^p^>u%3X9%EBMusR1?na z&+pMaS>V^zvY1WknrG>JG`PYvK5+T!(YwTXIp9ji4E3HyQ!nb}8b1@vR%fe|i8E60 zk@jOYyON3=i3=;hRW(Dh%F&bd)I}Qa3T;!myUwJ0Y{2in>0zIF+#-p%pc?$%_Ve6Z zZQKF6Cl~zp+^Zh5#Bw%qekk~O-8gHW!CCr!!pj8z>lxdDLeJUv=^j(?2TFJ8fyKGM z5D#ewpJ6P1*(k;*kT??#uHz;NlMMeBPyHA;J6-nPVOaz1%v=IDT)FX*chr_LGS7 zNdk}3wLZcAAwG@H*MLXwb^A_Qn)o4cL9oVaU3OPioT?@+>;hlYx;bH?uZ0hBz8`p8 zSn&#O`{{hTU!d`PQJ_IYvH@{s3wVOvH=F{e^lfzh8hGN^k#XmKD?jR9;7I`m9$V9_ z`Na918fWyraBOgOp!=_buOB^=(3IF!LHDGAZ&dSUganmY(mg8hO~ZAUYZadwQ`ZO2 zk`(Ky<)QR_WbS}(H}tg0bj+kZc{jj!6c&092nEZ@9)Tw~d}Xop>F-W5fwx!y3t#~( zfCaDs7Qg~n01IFNEPw^D02aUkSO5!P0W5$8umBdo0$2bGU;!+E1+V}XzyeqR3t#~( zfCaF?(_LWlpFIsHt}`aDlQ*;eArtTv$n?%j`>3hNlPWg7=lVY%HI;MMp3#24v}d$M r`zUSEK1y4ra@162rng!9C~cX_vrlDadYhlrQPbP=-#@Q^d4+!iqk=}q delta 93 zcmbO}hw<|y#tkMCjDnMEB_t=WlMtUADrqoTSIS`WTOgh)Eiu_vMq=_`Al8+Yn7kK= oS>+@q9|hvM@)DEJ%1iM4hXMu$#+4fte~521C=lE1=wZYN055nW+W-In diff --git a/mobile/rust/Cargo.lock b/mobile/rust/Cargo.lock index 8f8129c255..067a4e8476 100644 --- a/mobile/rust/Cargo.lock +++ b/mobile/rust/Cargo.lock @@ -136,6 +136,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -156,6 +166,50 @@ dependencies = [ "typenum", ] +[[package]] +name = "cxx" +version = "1.0.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ab30434ea0ff6aa640a08dda5284026a366d47565496fd40b6cbfbdd7e31a2" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b649d7dfae8268450d53d109388b337b9352c7cba1fc10db4a1bc23c3dc189fb" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42281b20eba5218c539295c667c18e2f50211bb11902419194c6ed1ae808e547" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45506e3c66512b0a65d291a6b452128b7b1dd9841e20d1e151addbd2c00ea50" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dart-sys" version = "4.1.5" @@ -386,6 +440,15 @@ version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +[[package]] +name = "link-cplusplus" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212" +dependencies = [ + "cc", +] + [[package]] name = "log" version = "0.4.20" @@ -523,6 +586,7 @@ name = "rust_lib_photos" version = "0.1.0" dependencies = [ "flutter_rust_bridge", + "usearch", ] [[package]] @@ -531,6 +595,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "scratch" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52" + [[package]] name = "slab" version = "0.4.9" @@ -551,6 +621,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "threadpool" version = "1.8.1" @@ -583,6 +662,22 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "usearch" +version = "2.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d405433f45fd46c88c7499e68381e0a27b742f6cd0daf8bfc186ee6d8195a387" +dependencies = [ + "cxx", + "cxx-build", +] + [[package]] name = "version_check" version = "0.9.4" @@ -664,3 +759,85 @@ dependencies = [ "js-sys", "wasm-bindgen", ] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/mobile/rust/Cargo.toml b/mobile/rust/Cargo.toml index 688aa65364..b35ca7bc7e 100644 --- a/mobile/rust/Cargo.toml +++ b/mobile/rust/Cargo.toml @@ -8,6 +8,7 @@ crate-type = ["cdylib", "staticlib"] [dependencies] flutter_rust_bridge = "=2.9.0" +usearch = "2.17.2" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] } diff --git a/mobile/rust/src/api/mod.rs b/mobile/rust/src/api/mod.rs index b252f36bf9..3f601e97bc 100644 --- a/mobile/rust/src/api/mod.rs +++ b/mobile/rust/src/api/mod.rs @@ -1 +1,2 @@ pub mod simple; +pub mod usearch_api; \ No newline at end of file diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs new file mode 100644 index 0000000000..f105ff2c61 --- /dev/null +++ b/mobile/rust/src/api/usearch_api.rs @@ -0,0 +1,116 @@ +use usearch::{Index, IndexOptions, MetricKind, ScalarKind}; + +// Create DB index +fn create_index() -> Index { + let mut options = IndexOptions::default(); + options.dimensions = 192; // Set the number of dimensions for vectors + options.metric = MetricKind::Cos; // Use cosine similarity for distance measurement + options.quantization = ScalarKind::F32; // Use 32-bit floating point numbers + options.connectivity = 0; // zero for auto + options.expansion_add = 0; // zero for auto + options.expansion_search = 0; // zero for auto + + let index = Index::new(&options).expect("Failed to create index."); + index.reserve(1000).expect("Failed to reserve capacity."); + index +} + +// Get the DB +fn get_index(file_path: &str) -> Index { + let file_exists: bool = std::path::Path::new(file_path).try_exists().unwrap(); + let index = create_index(); + if file_exists { + let index = load_index(index, file_path); + index + } else { + save_index(&index, file_path); + index + } +} + +// Save to disk +fn save_index(index: &Index, file_path: &str) { + index.save(file_path).expect("Failed to save index."); +} + +// Load from disk +fn load_index(index: Index, file_path: &str) -> Index { + index.load(file_path).expect("Failed to load index."); + index +} + +// Changes to DB index +fn ensure_capacity(index: &Index, margin: usize) { + let current_size = index.size(); + let capacity = index.capacity(); + if current_size + margin >= capacity { + index + .reserve(current_size + margin) + .expect("Failed to reserve capacity."); + } +} + +pub fn get_index_stats(index_path: &str) -> (usize, usize, usize, usize, usize) { + let index = get_index(index_path); + let size = index.size(); + let capacity = index.capacity(); + let dimensions = index.dimensions(); + let expansion_add = index.expansion_add(); + let expansion_search = index.expansion_search(); + + (size, capacity, dimensions, expansion_add, expansion_search) +} + +// Add to index +pub fn add_vector(index_path: &str, key: u64, vector: &Vec) { + let index = get_index(index_path); + ensure_capacity(&index, 1); + index.add(key, vector).expect("Failed to add vector."); + save_index(&index, index_path); +} + +// Bulk add to index +pub fn bulk_add_vectors(index_path: &str, keys: Vec, vectors: &Vec>) { + let index = get_index(index_path); + ensure_capacity(&index, keys.len()); + for (key, vector) in keys.iter().zip(vectors.iter()) { + index.add(*key, vector).expect("Failed to add vector."); + } + save_index(&index, index_path); +} + +// Search in index +pub fn search_vectors(index_path: &str, query: &Vec, count: usize) -> (Vec, Vec) { + let index: Index = get_index(index_path); + let results = index.search(query, count).expect("Search failed."); + + (results.keys, results.distances) +} + +// Read from index +pub fn get_vector(index_path: &str, key: u64) -> Vec { + let index = get_index(index_path); + let mut vector: Vec = vec![0.0; index.dimensions()]; + let _ = index + .get(key, &mut vector) + .expect("Failed to export vector."); + + vector +} + +// Delete from index +pub fn remove_vector(index_path: &str, key: u64) -> usize { + let index = get_index(index_path); + let removed_count = index.remove(key).expect("Failed to remove vector."); + save_index(&index, index_path); + + removed_count +} + +// Reset index +pub fn reset_index(index_path: &str) { + let index = get_index(index_path); + index.reset().expect("Failed to clear index."); + + save_index(&index, index_path); +} diff --git a/mobile/rust/src/frb_generated.rs b/mobile/rust/src/frb_generated.rs index 81c7f476de..848c851d3d 100644 --- a/mobile/rust/src/frb_generated.rs +++ b/mobile/rust/src/frb_generated.rs @@ -37,7 +37,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.9.0"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1918914929; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 2006111302; // Section: executor @@ -45,6 +45,156 @@ flutter_rust_bridge::frb_generated_default_handler!(); // Section: wire_funcs +fn wire__crate__api__usearch_api__add_vector_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "add_vector", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_index_path = ::sse_decode(&mut deserializer); + let api_key = ::sse_decode(&mut deserializer); + let api_vector = >::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, ()>((move || { + let output_ok = Result::<_, ()>::Ok({ + crate::api::usearch_api::add_vector(&api_index_path, api_key, &api_vector); + })?; + Ok(output_ok) + })()) + } + }, + ) +} +fn wire__crate__api__usearch_api__bulk_add_vectors_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "bulk_add_vectors", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_index_path = ::sse_decode(&mut deserializer); + let api_keys = >::sse_decode(&mut deserializer); + let api_vectors = >>::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, ()>((move || { + let output_ok = Result::<_, ()>::Ok({ + crate::api::usearch_api::bulk_add_vectors( + &api_index_path, + api_keys, + &api_vectors, + ); + })?; + Ok(output_ok) + })()) + } + }, + ) +} +fn wire__crate__api__usearch_api__get_index_stats_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "get_index_stats", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_index_path = ::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, ()>((move || { + let output_ok = Result::<_, ()>::Ok(crate::api::usearch_api::get_index_stats( + &api_index_path, + ))?; + Ok(output_ok) + })()) + } + }, + ) +} +fn wire__crate__api__usearch_api__get_vector_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "get_vector", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_index_path = ::sse_decode(&mut deserializer); + let api_key = ::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, ()>((move || { + let output_ok = Result::<_, ()>::Ok(crate::api::usearch_api::get_vector( + &api_index_path, + api_key, + ))?; + Ok(output_ok) + })()) + } + }, + ) +} fn wire__crate__api__simple__greet_impl( ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len_: i32, @@ -109,6 +259,117 @@ fn wire__crate__api__simple__init_app_impl( }, ) } +fn wire__crate__api__usearch_api__remove_vector_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "remove_vector", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_index_path = ::sse_decode(&mut deserializer); + let api_key = ::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, ()>((move || { + let output_ok = Result::<_, ()>::Ok(crate::api::usearch_api::remove_vector( + &api_index_path, + api_key, + ))?; + Ok(output_ok) + })()) + } + }, + ) +} +fn wire__crate__api__usearch_api__reset_index_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "reset_index", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_index_path = ::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, ()>((move || { + let output_ok = Result::<_, ()>::Ok({ + crate::api::usearch_api::reset_index(&api_index_path); + })?; + Ok(output_ok) + })()) + } + }, + ) +} +fn wire__crate__api__usearch_api__search_vectors_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "search_vectors", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_index_path = ::sse_decode(&mut deserializer); + let api_query = >::sse_decode(&mut deserializer); + let api_count = ::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, ()>((move || { + let output_ok = Result::<_, ()>::Ok(crate::api::usearch_api::search_vectors( + &api_index_path, + &api_query, + api_count, + ))?; + Ok(output_ok) + })()) + } + }, + ) +} // Section: dart2rust @@ -120,6 +381,49 @@ impl SseDecode for String { } } +impl SseDecode for f32 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_f32::().unwrap() + } +} + +impl SseDecode for Vec> { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut len_ = ::sse_decode(deserializer); + let mut ans_ = vec![]; + for idx_ in 0..len_ { + ans_.push(>::sse_decode(deserializer)); + } + return ans_; + } +} + +impl SseDecode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut len_ = ::sse_decode(deserializer); + let mut ans_ = vec![]; + for idx_ in 0..len_ { + ans_.push(::sse_decode(deserializer)); + } + return ans_; + } +} + +impl SseDecode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut len_ = ::sse_decode(deserializer); + let mut ans_ = vec![]; + for idx_ in 0..len_ { + ans_.push(::sse_decode(deserializer)); + } + return ans_; + } +} + impl SseDecode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -132,6 +436,34 @@ impl SseDecode for Vec { } } +impl SseDecode for (Vec, Vec) { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_field0 = >::sse_decode(deserializer); + let mut var_field1 = >::sse_decode(deserializer); + return (var_field0, var_field1); + } +} + +impl SseDecode for (usize, usize, usize, usize, usize) { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_field0 = ::sse_decode(deserializer); + let mut var_field1 = ::sse_decode(deserializer); + let mut var_field2 = ::sse_decode(deserializer); + let mut var_field3 = ::sse_decode(deserializer); + let mut var_field4 = ::sse_decode(deserializer); + return (var_field0, var_field1, var_field2, var_field3, var_field4); + } +} + +impl SseDecode for u64 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_u64::().unwrap() + } +} + impl SseDecode for u8 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -144,6 +476,13 @@ impl SseDecode for () { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {} } +impl SseDecode for usize { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_u64::().unwrap() as _ + } +} + impl SseDecode for i32 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -167,7 +506,16 @@ fn pde_ffi_dispatcher_primary_impl( ) { // Codec=Pde (Serialization + dispatch), see doc to use other codecs match func_id { - 2 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), + 1 => wire__crate__api__usearch_api__add_vector_impl(port, ptr, rust_vec_len, data_len), + 2 => { + wire__crate__api__usearch_api__bulk_add_vectors_impl(port, ptr, rust_vec_len, data_len) + } + 3 => wire__crate__api__usearch_api__get_index_stats_impl(port, ptr, rust_vec_len, data_len), + 4 => wire__crate__api__usearch_api__get_vector_impl(port, ptr, rust_vec_len, data_len), + 6 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), + 7 => wire__crate__api__usearch_api__remove_vector_impl(port, ptr, rust_vec_len, data_len), + 8 => wire__crate__api__usearch_api__reset_index_impl(port, ptr, rust_vec_len, data_len), + 9 => wire__crate__api__usearch_api__search_vectors_impl(port, ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -180,7 +528,7 @@ fn pde_ffi_dispatcher_sync_impl( ) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { // Codec=Pde (Serialization + dispatch), see doc to use other codecs match func_id { - 1 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), + 5 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -194,6 +542,43 @@ impl SseEncode for String { } } +impl SseEncode for f32 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_f32::(self).unwrap(); + } +} + +impl SseEncode for Vec> { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.len() as _, serializer); + for item in self { + >::sse_encode(item, serializer); + } + } +} + +impl SseEncode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.len() as _, serializer); + for item in self { + ::sse_encode(item, serializer); + } + } +} + +impl SseEncode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.len() as _, serializer); + for item in self { + ::sse_encode(item, serializer); + } + } +} + impl SseEncode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -204,6 +589,32 @@ impl SseEncode for Vec { } } +impl SseEncode for (Vec, Vec) { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + >::sse_encode(self.0, serializer); + >::sse_encode(self.1, serializer); + } +} + +impl SseEncode for (usize, usize, usize, usize, usize) { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.0, serializer); + ::sse_encode(self.1, serializer); + ::sse_encode(self.2, serializer); + ::sse_encode(self.3, serializer); + ::sse_encode(self.4, serializer); + } +} + +impl SseEncode for u64 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_u64::(self).unwrap(); + } +} + impl SseEncode for u8 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -216,6 +627,16 @@ impl SseEncode for () { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {} } +impl SseEncode for usize { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer + .cursor + .write_u64::(self as _) + .unwrap(); + } +} + impl SseEncode for i32 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { From c42807487b5b20d2d1b51b7b89f31785d66e63f2 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Sat, 5 Apr 2025 16:11:03 +0530 Subject: [PATCH 006/156] Upgrade Android NDK to r28 latest stable --- mobile/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle index 8c3148fae8..893718f438 100644 --- a/mobile/android/app/build.gradle +++ b/mobile/android/app/build.gradle @@ -31,7 +31,7 @@ if (keystorePropertiesFile.exists()) { android { namespace = "io.ente.photos" compileSdk = 35 - ndkVersion = flutter.ndkVersion + ndkVersion = "28.0.13004108" compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 From 120dbeb4fcc6d0a5e0736a98e6dfec3ed6b81980 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Sat, 5 Apr 2025 16:56:14 +0530 Subject: [PATCH 007/156] Fix null pointer crash --- .../debug/ml_debug_section_widget.dart | 2 +- mobile/rust/src/api/usearch_api.rs | 18 ++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index 809249ff3d..cb616decc0 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -89,7 +89,7 @@ class _MLDebugSectionWidgetState extends State { final tenKeys = Uint64List.fromList(tenVectors.map((e) => e.fileID).toList()); final indexPath = (await getApplicationSupportDirectory()).path + - "/ml/test/vector_db"; + "/ml/test1/vector_db"; final stats = await getIndexStats(indexPath: indexPath); logger.info("vector_db stats: $stats"); await bulkAddVectors( diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index f105ff2c61..7405bb0b26 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -3,7 +3,7 @@ use usearch::{Index, IndexOptions, MetricKind, ScalarKind}; // Create DB index fn create_index() -> Index { let mut options = IndexOptions::default(); - options.dimensions = 192; // Set the number of dimensions for vectors + options.dimensions = 512; // Set the number of dimensions for vectors options.metric = MetricKind::Cos; // Use cosine similarity for distance measurement options.quantization = ScalarKind::F32; // Use 32-bit floating point numbers options.connectivity = 0; // zero for auto @@ -20,25 +20,23 @@ fn get_index(file_path: &str) -> Index { let file_exists: bool = std::path::Path::new(file_path).try_exists().unwrap(); let index = create_index(); if file_exists { - let index = load_index(index, file_path); - index + // Load into the existing index instead of creating a new variable + index.load(file_path).expect("Failed to load index."); } else { save_index(&index, file_path); - index } + index } // Save to disk fn save_index(index: &Index, file_path: &str) { + // Ensure directory exists + if let Some(parent) = std::path::Path::new(file_path).parent() { + std::fs::create_dir_all(parent).expect("Failed to create directory for index."); + } index.save(file_path).expect("Failed to save index."); } -// Load from disk -fn load_index(index: Index, file_path: &str) -> Index { - index.load(file_path).expect("Failed to load index."); - index -} - // Changes to DB index fn ensure_capacity(index: &Index, margin: usize) { let current_size = index.size(); From 6500748c5a1639a30ab87cdea3b0b60a55756a18 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 8 Apr 2025 14:48:30 +0530 Subject: [PATCH 008/156] Make vector db stateful in rust --- mobile/lib/src/rust/api/usearch_api.dart | 77 +- mobile/lib/src/rust/frb_generated.dart | 880 ++++++++++++++---- mobile/lib/src/rust/frb_generated.io.dart | 281 +++++- mobile/lib/src/rust/frb_generated.web.dart | 265 +++++- .../debug/ml_debug_section_widget.dart | 15 +- mobile/rust/src/api/usearch_api.rs | 218 ++--- mobile/rust/src/frb_generated.rs | 777 ++++++++++++---- 7 files changed, 1955 insertions(+), 558 deletions(-) diff --git a/mobile/lib/src/rust/api/usearch_api.dart b/mobile/lib/src/rust/api/usearch_api.dart index 0f68ea774d..67a5f1ad9b 100644 --- a/mobile/lib/src/rust/api/usearch_api.dart +++ b/mobile/lib/src/rust/api/usearch_api.dart @@ -6,56 +6,41 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; import 'package:photos/src/rust/frb_generated.dart'; -// These functions are ignored because they are not marked as `pub`: `create_index`, `ensure_capacity`, `get_index`, `load_index`, `save_index` +// These functions are ignored because they are not marked as `pub`: `ensure_capacity`, `save_index` -Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> getIndexStats({ - required String indexPath, -}) => - RustLib.instance.api.crateApiUsearchApiGetIndexStats(indexPath: indexPath); +// Rust type: RustOpaqueMoi>> +abstract class BoxError implements RustOpaqueInterface {} -Future addVector({ - required String indexPath, - required BigInt key, - required List vector, -}) => - RustLib.instance.api.crateApiUsearchApiAddVector( - indexPath: indexPath, - key: key, - vector: vector, - ); +// Rust type: RustOpaqueMoi> +abstract class Matches implements RustOpaqueInterface {} -Future bulkAddVectors({ - required String indexPath, - required Uint64List keys, - required List vectors, -}) => - RustLib.instance.api.crateApiUsearchApiBulkAddVectors( - indexPath: indexPath, - keys: keys, - vectors: vectors, - ); +// Rust type: RustOpaqueMoi> +abstract class VectorDb implements RustOpaqueInterface { + Future addVector({required BigInt key, required List vector}); -Future<(Uint64List, Float32List)> searchVectors({ - required String indexPath, - required List query, - required BigInt count, -}) => - RustLib.instance.api.crateApiUsearchApiSearchVectors( - indexPath: indexPath, - query: query, - count: count, - ); + Future bulkAddVectors({ + required Uint64List keys, + required List vectors, + }); -Future getVector({ - required String indexPath, - required BigInt key, -}) => - RustLib.instance.api - .crateApiUsearchApiGetVector(indexPath: indexPath, key: key); + Future deleteIndex(); -Future removeVector({required String indexPath, required BigInt key}) => - RustLib.instance.api - .crateApiUsearchApiRemoveVector(indexPath: indexPath, key: key); + Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> getIndexStats(); -Future resetIndex({required String indexPath}) => - RustLib.instance.api.crateApiUsearchApiResetIndex(indexPath: indexPath); + Future getVector({required BigInt key}); + + factory VectorDb({required String filePath, required BigInt dimensions}) => + RustLib.instance.api.crateApiUsearchApiVectorDbNew( + filePath: filePath, + dimensions: dimensions, + ); + + Future removeVector({required BigInt key}); + + Future resetIndex(); + + Future searchVectors({ + required List query, + required BigInt count, + }); +} diff --git a/mobile/lib/src/rust/frb_generated.dart b/mobile/lib/src/rust/frb_generated.dart index 09ccbbe9bc..328ff572b4 100644 --- a/mobile/lib/src/rust/frb_generated.dart +++ b/mobile/lib/src/rust/frb_generated.dart @@ -70,7 +70,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.9.0'; @override - int get rustContentHash => 2006111302; + int get rustContentHash => 862168794; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( @@ -81,42 +81,71 @@ class RustLib extends BaseEntrypoint { } abstract class RustLibApi extends BaseApi { - Future crateApiUsearchApiAddVector({ - required String indexPath, + Future crateApiUsearchApiVectorDbAddVector({ + required VectorDb that, required BigInt key, required List vector, }); - Future crateApiUsearchApiBulkAddVectors({ - required String indexPath, + Future crateApiUsearchApiVectorDbBulkAddVectors({ + required VectorDb that, required Uint64List keys, required List vectors, }); - Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> - crateApiUsearchApiGetIndexStats({required String indexPath}); + Future crateApiUsearchApiVectorDbDeleteIndex({required VectorDb that}); - Future crateApiUsearchApiGetVector({ - required String indexPath, + Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> + crateApiUsearchApiVectorDbGetIndexStats({required VectorDb that}); + + Future crateApiUsearchApiVectorDbGetVector({ + required VectorDb that, required BigInt key, }); + VectorDb crateApiUsearchApiVectorDbNew({ + required String filePath, + required BigInt dimensions, + }); + + Future crateApiUsearchApiVectorDbRemoveVector({ + required VectorDb that, + required BigInt key, + }); + + Future crateApiUsearchApiVectorDbResetIndex({required VectorDb that}); + + Future crateApiUsearchApiVectorDbSearchVectors({ + required VectorDb that, + required List query, + required BigInt count, + }); + String crateApiSimpleGreet({required String name}); Future crateApiSimpleInitApp(); - Future crateApiUsearchApiRemoveVector({ - required String indexPath, - required BigInt key, - }); + RustArcIncrementStrongCountFnType + get rust_arc_increment_strong_count_BoxError; - Future crateApiUsearchApiResetIndex({required String indexPath}); + RustArcDecrementStrongCountFnType + get rust_arc_decrement_strong_count_BoxError; - Future<(Uint64List, Float32List)> crateApiUsearchApiSearchVectors({ - required String indexPath, - required List query, - required BigInt count, - }); + CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_BoxErrorPtr; + + RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_Matches; + + RustArcDecrementStrongCountFnType get rust_arc_decrement_strong_count_Matches; + + CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_MatchesPtr; + + RustArcIncrementStrongCountFnType + get rust_arc_increment_strong_count_VectorDb; + + RustArcDecrementStrongCountFnType + get rust_arc_decrement_strong_count_VectorDb; + + CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_VectorDbPtr; } class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @@ -128,8 +157,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { }); @override - Future crateApiUsearchApiAddVector({ - required String indexPath, + Future crateApiUsearchApiVectorDbAddVector({ + required VectorDb that, required BigInt key, required List vector, }) { @@ -137,7 +166,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_String(indexPath, serializer); + sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, + serializer, + ); sse_encode_u_64(key, serializer); sse_encode_list_prim_f_32_loose(vector, serializer); pdeCallFfi( @@ -149,24 +181,25 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { }, codec: SseCodec( decodeSuccessData: sse_decode_unit, - decodeErrorData: null, + decodeErrorData: + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError, ), - constMeta: kCrateApiUsearchApiAddVectorConstMeta, - argValues: [indexPath, key, vector], + constMeta: kCrateApiUsearchApiVectorDbAddVectorConstMeta, + argValues: [that, key, vector], apiImpl: this, ), ); } - TaskConstMeta get kCrateApiUsearchApiAddVectorConstMeta => + TaskConstMeta get kCrateApiUsearchApiVectorDbAddVectorConstMeta => const TaskConstMeta( - debugName: "add_vector", - argNames: ["indexPath", "key", "vector"], + debugName: "VectorDb_add_vector", + argNames: ["that", "key", "vector"], ); @override - Future crateApiUsearchApiBulkAddVectors({ - required String indexPath, + Future crateApiUsearchApiVectorDbBulkAddVectors({ + required VectorDb that, required Uint64List keys, required List vectors, }) { @@ -174,7 +207,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_String(indexPath, serializer); + sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, + serializer, + ); sse_encode_list_prim_u_64_strict(keys, serializer); sse_encode_list_list_prim_f_32_strict(vectors, serializer); pdeCallFfi( @@ -186,29 +222,32 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { }, codec: SseCodec( decodeSuccessData: sse_decode_unit, - decodeErrorData: null, + decodeErrorData: + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError, ), - constMeta: kCrateApiUsearchApiBulkAddVectorsConstMeta, - argValues: [indexPath, keys, vectors], + constMeta: kCrateApiUsearchApiVectorDbBulkAddVectorsConstMeta, + argValues: [that, keys, vectors], apiImpl: this, ), ); } - TaskConstMeta get kCrateApiUsearchApiBulkAddVectorsConstMeta => + TaskConstMeta get kCrateApiUsearchApiVectorDbBulkAddVectorsConstMeta => const TaskConstMeta( - debugName: "bulk_add_vectors", - argNames: ["indexPath", "keys", "vectors"], + debugName: "VectorDb_bulk_add_vectors", + argNames: ["that", "keys", "vectors"], ); @override - Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> - crateApiUsearchApiGetIndexStats({required String indexPath}) { + Future crateApiUsearchApiVectorDbDeleteIndex({required VectorDb that}) { return handler.executeNormal( NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_String(indexPath, serializer); + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, + serializer, + ); pdeCallFfi( generalizedFrbRustBinding, serializer, @@ -217,33 +256,34 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); }, codec: SseCodec( - decodeSuccessData: sse_decode_record_usize_usize_usize_usize_usize, - decodeErrorData: null, + decodeSuccessData: sse_decode_unit, + decodeErrorData: + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError, ), - constMeta: kCrateApiUsearchApiGetIndexStatsConstMeta, - argValues: [indexPath], + constMeta: kCrateApiUsearchApiVectorDbDeleteIndexConstMeta, + argValues: [that], apiImpl: this, ), ); } - TaskConstMeta get kCrateApiUsearchApiGetIndexStatsConstMeta => + TaskConstMeta get kCrateApiUsearchApiVectorDbDeleteIndexConstMeta => const TaskConstMeta( - debugName: "get_index_stats", - argNames: ["indexPath"], + debugName: "VectorDb_delete_index", + argNames: ["that"], ); @override - Future crateApiUsearchApiGetVector({ - required String indexPath, - required BigInt key, - }) { + Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> + crateApiUsearchApiVectorDbGetIndexStats({required VectorDb that}) { return handler.executeNormal( NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_String(indexPath, serializer); - sse_encode_u_64(key, serializer); + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, + serializer, + ); pdeCallFfi( generalizedFrbRustBinding, serializer, @@ -252,20 +292,207 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); }, codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_f_32_strict, + decodeSuccessData: sse_decode_record_usize_usize_usize_usize_usize, decodeErrorData: null, ), - constMeta: kCrateApiUsearchApiGetVectorConstMeta, - argValues: [indexPath, key], + constMeta: kCrateApiUsearchApiVectorDbGetIndexStatsConstMeta, + argValues: [that], apiImpl: this, ), ); } - TaskConstMeta get kCrateApiUsearchApiGetVectorConstMeta => + TaskConstMeta get kCrateApiUsearchApiVectorDbGetIndexStatsConstMeta => const TaskConstMeta( - debugName: "get_vector", - argNames: ["indexPath", "key"], + debugName: "VectorDb_get_index_stats", + argNames: ["that"], + ); + + @override + Future crateApiUsearchApiVectorDbGetVector({ + required VectorDb that, + required BigInt key, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, + serializer, + ); + sse_encode_u_64(key, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 5, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_f_32_strict, + decodeErrorData: + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError, + ), + constMeta: kCrateApiUsearchApiVectorDbGetVectorConstMeta, + argValues: [that, key], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiUsearchApiVectorDbGetVectorConstMeta => + const TaskConstMeta( + debugName: "VectorDb_get_vector", + argNames: ["that", "key"], + ); + + @override + VectorDb crateApiUsearchApiVectorDbNew({ + required String filePath, + required BigInt dimensions, + }) { + return handler.executeSync( + SyncTask( + callFfi: () { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_String(filePath, serializer); + sse_encode_usize(dimensions, serializer); + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 6)!; + }, + codec: SseCodec( + decodeSuccessData: + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB, + decodeErrorData: + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError, + ), + constMeta: kCrateApiUsearchApiVectorDbNewConstMeta, + argValues: [filePath, dimensions], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiUsearchApiVectorDbNewConstMeta => + const TaskConstMeta( + debugName: "VectorDb_new", + argNames: ["filePath", "dimensions"], + ); + + @override + Future crateApiUsearchApiVectorDbRemoveVector({ + required VectorDb that, + required BigInt key, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, + serializer, + ); + sse_encode_u_64(key, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 7, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_usize, + decodeErrorData: + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError, + ), + constMeta: kCrateApiUsearchApiVectorDbRemoveVectorConstMeta, + argValues: [that, key], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiUsearchApiVectorDbRemoveVectorConstMeta => + const TaskConstMeta( + debugName: "VectorDb_remove_vector", + argNames: ["that", "key"], + ); + + @override + Future crateApiUsearchApiVectorDbResetIndex({required VectorDb that}) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, + serializer, + ); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 8, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError, + ), + constMeta: kCrateApiUsearchApiVectorDbResetIndexConstMeta, + argValues: [that], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiUsearchApiVectorDbResetIndexConstMeta => + const TaskConstMeta( + debugName: "VectorDb_reset_index", + argNames: ["that"], + ); + + @override + Future crateApiUsearchApiVectorDbSearchVectors({ + required VectorDb that, + required List query, + required BigInt count, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, + serializer, + ); + sse_encode_list_prim_f_32_loose(query, serializer); + sse_encode_usize(count, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 9, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches, + decodeErrorData: + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError, + ), + constMeta: kCrateApiUsearchApiVectorDbSearchVectorsConstMeta, + argValues: [that, query, count], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiUsearchApiVectorDbSearchVectorsConstMeta => + const TaskConstMeta( + debugName: "VectorDb_search_vectors", + argNames: ["that", "query", "count"], ); @override @@ -275,7 +502,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(name, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 5)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 10)!; }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -302,7 +529,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 6, + funcId: 11, port: port_, ); }, @@ -322,109 +549,101 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: [], ); - @override - Future crateApiUsearchApiRemoveVector({ - required String indexPath, - required BigInt key, - }) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_String(indexPath, serializer); - sse_encode_u_64(key, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 7, - port: port_, - ); - }, - codec: SseCodec( - decodeSuccessData: sse_decode_usize, - decodeErrorData: null, - ), - constMeta: kCrateApiUsearchApiRemoveVectorConstMeta, - argValues: [indexPath, key], - apiImpl: this, - ), - ); + RustArcIncrementStrongCountFnType + get rust_arc_increment_strong_count_BoxError => wire + .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError; + + RustArcDecrementStrongCountFnType + get rust_arc_decrement_strong_count_BoxError => wire + .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError; + + RustArcIncrementStrongCountFnType + get rust_arc_increment_strong_count_Matches => wire + .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches; + + RustArcDecrementStrongCountFnType + get rust_arc_decrement_strong_count_Matches => wire + .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches; + + RustArcIncrementStrongCountFnType + get rust_arc_increment_strong_count_VectorDb => wire + .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB; + + RustArcDecrementStrongCountFnType + get rust_arc_decrement_strong_count_VectorDb => wire + .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB; + + @protected + BoxError + dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + dynamic raw, + ) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return BoxErrorImpl.frbInternalDcoDecode(raw as List); } - TaskConstMeta get kCrateApiUsearchApiRemoveVectorConstMeta => - const TaskConstMeta( - debugName: "remove_vector", - argNames: ["indexPath", "key"], - ); - - @override - Future crateApiUsearchApiResetIndex({required String indexPath}) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_String(indexPath, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 8, - port: port_, - ); - }, - codec: SseCodec( - decodeSuccessData: sse_decode_unit, - decodeErrorData: null, - ), - constMeta: kCrateApiUsearchApiResetIndexConstMeta, - argValues: [indexPath], - apiImpl: this, - ), - ); + @protected + Matches + dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + dynamic raw, + ) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return MatchesImpl.frbInternalDcoDecode(raw as List); } - TaskConstMeta get kCrateApiUsearchApiResetIndexConstMeta => - const TaskConstMeta( - debugName: "reset_index", - argNames: ["indexPath"], - ); - - @override - Future<(Uint64List, Float32List)> crateApiUsearchApiSearchVectors({ - required String indexPath, - required List query, - required BigInt count, - }) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_String(indexPath, serializer); - sse_encode_list_prim_f_32_loose(query, serializer); - sse_encode_usize(count, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 9, - port: port_, - ); - }, - codec: SseCodec( - decodeSuccessData: - sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict, - decodeErrorData: null, - ), - constMeta: kCrateApiUsearchApiSearchVectorsConstMeta, - argValues: [indexPath, query, count], - apiImpl: this, - ), - ); + @protected + VectorDb + dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + dynamic raw, + ) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return VectorDbImpl.frbInternalDcoDecode(raw as List); } - TaskConstMeta get kCrateApiUsearchApiSearchVectorsConstMeta => - const TaskConstMeta( - debugName: "search_vectors", - argNames: ["indexPath", "query", "count"], - ); + @protected + VectorDb + dco_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + dynamic raw, + ) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return VectorDbImpl.frbInternalDcoDecode(raw as List); + } + + @protected + VectorDb + dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + dynamic raw, + ) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return VectorDbImpl.frbInternalDcoDecode(raw as List); + } + + @protected + BoxError + dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + dynamic raw, + ) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return BoxErrorImpl.frbInternalDcoDecode(raw as List); + } + + @protected + Matches + dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + dynamic raw, + ) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return MatchesImpl.frbInternalDcoDecode(raw as List); + } + + @protected + VectorDb + dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + dynamic raw, + ) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return VectorDbImpl.frbInternalDcoDecode(raw as List); + } @protected String dco_decode_String(dynamic raw) { @@ -470,22 +689,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw as Uint8List; } - @protected - ( - Uint64List, - Float32List - ) dco_decode_record_list_prim_u_64_strict_list_prim_f_32_strict(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - final arr = raw as List; - if (arr.length != 2) { - throw Exception('Expected 2 elements, got ${arr.length}'); - } - return ( - dco_decode_list_prim_u_64_strict(arr[0]), - dco_decode_list_prim_f_32_strict(arr[1]), - ); - } - @protected (BigInt, BigInt, BigInt, BigInt, BigInt) dco_decode_record_usize_usize_usize_usize_usize(dynamic raw) { @@ -527,6 +730,102 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return dcoDecodeU64(raw); } + @protected + BoxError + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + return BoxErrorImpl.frbInternalSseDecode( + sse_decode_usize(deserializer), + sse_decode_i_32(deserializer), + ); + } + + @protected + Matches + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + return MatchesImpl.frbInternalSseDecode( + sse_decode_usize(deserializer), + sse_decode_i_32(deserializer), + ); + } + + @protected + VectorDb + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + return VectorDbImpl.frbInternalSseDecode( + sse_decode_usize(deserializer), + sse_decode_i_32(deserializer), + ); + } + + @protected + VectorDb + sse_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + return VectorDbImpl.frbInternalSseDecode( + sse_decode_usize(deserializer), + sse_decode_i_32(deserializer), + ); + } + + @protected + VectorDb + sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + return VectorDbImpl.frbInternalSseDecode( + sse_decode_usize(deserializer), + sse_decode_i_32(deserializer), + ); + } + + @protected + BoxError + sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + return BoxErrorImpl.frbInternalSseDecode( + sse_decode_usize(deserializer), + sse_decode_i_32(deserializer), + ); + } + + @protected + Matches + sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + return MatchesImpl.frbInternalSseDecode( + sse_decode_usize(deserializer), + sse_decode_i_32(deserializer), + ); + } + + @protected + VectorDb + sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + return VectorDbImpl.frbInternalSseDecode( + sse_decode_usize(deserializer), + sse_decode_i_32(deserializer), + ); + } + @protected String sse_decode_String(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -582,17 +881,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return deserializer.buffer.getUint8List(len_); } - @protected - (Uint64List, Float32List) - sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( - SseDeserializer deserializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - final var_field0 = sse_decode_list_prim_u_64_strict(deserializer); - final var_field1 = sse_decode_list_prim_f_32_strict(deserializer); - return (var_field0, var_field1); - } - @protected (BigInt, BigInt, BigInt, BigInt, BigInt) sse_decode_record_usize_usize_usize_usize_usize( @@ -642,6 +930,110 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return deserializer.buffer.getUint8() != 0; } + @protected + void + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + BoxError self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_usize( + (self as BoxErrorImpl).frbInternalSseEncode(move: true), + serializer, + ); + } + + @protected + void + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + Matches self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_usize( + (self as MatchesImpl).frbInternalSseEncode(move: true), + serializer, + ); + } + + @protected + void + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + VectorDb self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_usize( + (self as VectorDbImpl).frbInternalSseEncode(move: true), + serializer, + ); + } + + @protected + void + sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + VectorDb self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_usize( + (self as VectorDbImpl).frbInternalSseEncode(move: false), + serializer, + ); + } + + @protected + void + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + VectorDb self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_usize( + (self as VectorDbImpl).frbInternalSseEncode(move: false), + serializer, + ); + } + + @protected + void + sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + BoxError self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_usize( + (self as BoxErrorImpl).frbInternalSseEncode(move: null), + serializer, + ); + } + + @protected + void + sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + Matches self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_usize( + (self as MatchesImpl).frbInternalSseEncode(move: null), + serializer, + ); + } + + @protected + void + sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + VectorDb self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_usize( + (self as VectorDbImpl).frbInternalSseEncode(move: null), + serializer, + ); + } + @protected void sse_encode_String(String self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -708,16 +1100,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { serializer.buffer.putUint8List(self); } - @protected - void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( - (Uint64List, Float32List) self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_list_prim_u_64_strict(self.$1, serializer); - sse_encode_list_prim_f_32_strict(self.$2, serializer); - } - @protected void sse_encode_record_usize_usize_usize_usize_usize( (BigInt, BigInt, BigInt, BigInt, BigInt) self, @@ -766,3 +1148,111 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { serializer.buffer.putUint8(self ? 1 : 0); } } + +@sealed +class BoxErrorImpl extends RustOpaque implements BoxError { + // Not to be used by end users + BoxErrorImpl.frbInternalDcoDecode(List wire) + : super.frbInternalDcoDecode(wire, _kStaticData); + + // Not to be used by end users + BoxErrorImpl.frbInternalSseDecode(BigInt ptr, int externalSizeOnNative) + : super.frbInternalSseDecode(ptr, externalSizeOnNative, _kStaticData); + + static final _kStaticData = RustArcStaticData( + rustArcIncrementStrongCount: + RustLib.instance.api.rust_arc_increment_strong_count_BoxError, + rustArcDecrementStrongCount: + RustLib.instance.api.rust_arc_decrement_strong_count_BoxError, + rustArcDecrementStrongCountPtr: + RustLib.instance.api.rust_arc_decrement_strong_count_BoxErrorPtr, + ); +} + +@sealed +class MatchesImpl extends RustOpaque implements Matches { + // Not to be used by end users + MatchesImpl.frbInternalDcoDecode(List wire) + : super.frbInternalDcoDecode(wire, _kStaticData); + + // Not to be used by end users + MatchesImpl.frbInternalSseDecode(BigInt ptr, int externalSizeOnNative) + : super.frbInternalSseDecode(ptr, externalSizeOnNative, _kStaticData); + + static final _kStaticData = RustArcStaticData( + rustArcIncrementStrongCount: + RustLib.instance.api.rust_arc_increment_strong_count_Matches, + rustArcDecrementStrongCount: + RustLib.instance.api.rust_arc_decrement_strong_count_Matches, + rustArcDecrementStrongCountPtr: + RustLib.instance.api.rust_arc_decrement_strong_count_MatchesPtr, + ); +} + +@sealed +class VectorDbImpl extends RustOpaque implements VectorDb { + // Not to be used by end users + VectorDbImpl.frbInternalDcoDecode(List wire) + : super.frbInternalDcoDecode(wire, _kStaticData); + + // Not to be used by end users + VectorDbImpl.frbInternalSseDecode(BigInt ptr, int externalSizeOnNative) + : super.frbInternalSseDecode(ptr, externalSizeOnNative, _kStaticData); + + static final _kStaticData = RustArcStaticData( + rustArcIncrementStrongCount: + RustLib.instance.api.rust_arc_increment_strong_count_VectorDb, + rustArcDecrementStrongCount: + RustLib.instance.api.rust_arc_decrement_strong_count_VectorDb, + rustArcDecrementStrongCountPtr: + RustLib.instance.api.rust_arc_decrement_strong_count_VectorDbPtr, + ); + + Future addVector({required BigInt key, required List vector}) => + RustLib.instance.api.crateApiUsearchApiVectorDbAddVector( + that: this, + key: key, + vector: vector, + ); + + Future bulkAddVectors({ + required Uint64List keys, + required List vectors, + }) => + RustLib.instance.api.crateApiUsearchApiVectorDbBulkAddVectors( + that: this, + keys: keys, + vectors: vectors, + ); + + Future deleteIndex() => + RustLib.instance.api.crateApiUsearchApiVectorDbDeleteIndex( + that: this, + ); + + Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> getIndexStats() => + RustLib.instance.api.crateApiUsearchApiVectorDbGetIndexStats( + that: this, + ); + + Future getVector({required BigInt key}) => RustLib.instance.api + .crateApiUsearchApiVectorDbGetVector(that: this, key: key); + + Future removeVector({required BigInt key}) => RustLib.instance.api + .crateApiUsearchApiVectorDbRemoveVector(that: this, key: key); + + Future resetIndex() => + RustLib.instance.api.crateApiUsearchApiVectorDbResetIndex( + that: this, + ); + + Future searchVectors({ + required List query, + required BigInt count, + }) => + RustLib.instance.api.crateApiUsearchApiVectorDbSearchVectors( + that: this, + query: query, + count: count, + ); +} diff --git a/mobile/lib/src/rust/frb_generated.io.dart b/mobile/lib/src/rust/frb_generated.io.dart index 47d45ebfad..7153fb2de4 100644 --- a/mobile/lib/src/rust/frb_generated.io.dart +++ b/mobile/lib/src/rust/frb_generated.io.dart @@ -20,6 +20,63 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { required super.portManager, }); + CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_BoxErrorPtr => wire + ._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynErrorPtr; + + CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_MatchesPtr => wire + ._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatchesPtr; + + CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_VectorDbPtr => wire + ._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDBPtr; + + @protected + BoxError + dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + dynamic raw, + ); + + @protected + Matches + dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + dynamic raw, + ); + + @protected + VectorDb + dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + dynamic raw, + ); + + @protected + VectorDb + dco_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + dynamic raw, + ); + + @protected + VectorDb + dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + dynamic raw, + ); + + @protected + BoxError + dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + dynamic raw, + ); + + @protected + Matches + dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + dynamic raw, + ); + + @protected + VectorDb + dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + dynamic raw, + ); + @protected String dco_decode_String(dynamic raw); @@ -41,12 +98,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); - @protected - ( - Uint64List, - Float32List - ) dco_decode_record_list_prim_u_64_strict_list_prim_f_32_strict(dynamic raw); - @protected (BigInt, BigInt, BigInt, BigInt, BigInt) dco_decode_record_usize_usize_usize_usize_usize(dynamic raw); @@ -63,6 +114,54 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected BigInt dco_decode_usize(dynamic raw); + @protected + BoxError + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + SseDeserializer deserializer, + ); + + @protected + Matches + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + SseDeserializer deserializer, + ); + + @protected + VectorDb + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + SseDeserializer deserializer, + ); + + @protected + VectorDb + sse_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + SseDeserializer deserializer, + ); + + @protected + VectorDb + sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + SseDeserializer deserializer, + ); + + @protected + BoxError + sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + SseDeserializer deserializer, + ); + + @protected + Matches + sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + SseDeserializer deserializer, + ); + + @protected + VectorDb + sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + SseDeserializer deserializer, + ); + @protected String sse_decode_String(SseDeserializer deserializer); @@ -86,12 +185,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); - @protected - (Uint64List, Float32List) - sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( - SseDeserializer deserializer, - ); - @protected (BigInt, BigInt, BigInt, BigInt, BigInt) sse_decode_record_usize_usize_usize_usize_usize( @@ -116,6 +209,62 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected bool sse_decode_bool(SseDeserializer deserializer); + @protected + void + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + BoxError self, + SseSerializer serializer, + ); + + @protected + void + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + Matches self, + SseSerializer serializer, + ); + + @protected + void + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + VectorDb self, + SseSerializer serializer, + ); + + @protected + void + sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + VectorDb self, + SseSerializer serializer, + ); + + @protected + void + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + VectorDb self, + SseSerializer serializer, + ); + + @protected + void + sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + BoxError self, + SseSerializer serializer, + ); + + @protected + void + sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + Matches self, + SseSerializer serializer, + ); + + @protected + void + sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + VectorDb self, + SseSerializer serializer, + ); + @protected void sse_encode_String(String self, SseSerializer serializer); @@ -152,12 +301,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseSerializer serializer, ); - @protected - void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( - (Uint64List, Float32List) self, - SseSerializer serializer, - ); - @protected void sse_encode_record_usize_usize_usize_usize_usize( (BigInt, BigInt, BigInt, BigInt, BigInt) self, @@ -196,4 +339,106 @@ class RustLibWire implements BaseWire { /// The symbols are looked up in [dynamicLibrary]. RustLibWire(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; + + void + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + ffi.Pointer ptr, + ) { + return _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + ptr, + ); + } + + late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynErrorPtr = + _lookup)>>( + 'frbgen_photos_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError', + ); + late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError = + _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynErrorPtr + .asFunction)>(); + + void + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + ffi.Pointer ptr, + ) { + return _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + ptr, + ); + } + + late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynErrorPtr = + _lookup)>>( + 'frbgen_photos_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError', + ); + late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError = + _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynErrorPtr + .asFunction)>(); + + void + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + ffi.Pointer ptr, + ) { + return _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + ptr, + ); + } + + late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatchesPtr = + _lookup)>>( + 'frbgen_photos_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches', + ); + late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches = + _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatchesPtr + .asFunction)>(); + + void + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + ffi.Pointer ptr, + ) { + return _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + ptr, + ); + } + + late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatchesPtr = + _lookup)>>( + 'frbgen_photos_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches', + ); + late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches = + _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatchesPtr + .asFunction)>(); + + void + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + ffi.Pointer ptr, + ) { + return _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + ptr, + ); + } + + late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDBPtr = + _lookup)>>( + 'frbgen_photos_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB', + ); + late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB = + _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDBPtr + .asFunction)>(); + + void + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + ffi.Pointer ptr, + ) { + return _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + ptr, + ); + } + + late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDBPtr = + _lookup)>>( + 'frbgen_photos_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB', + ); + late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB = + _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDBPtr + .asFunction)>(); } diff --git a/mobile/lib/src/rust/frb_generated.web.dart b/mobile/lib/src/rust/frb_generated.web.dart index 10a1839fe4..b91911a6dd 100644 --- a/mobile/lib/src/rust/frb_generated.web.dart +++ b/mobile/lib/src/rust/frb_generated.web.dart @@ -22,6 +22,63 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { required super.portManager, }); + CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_BoxErrorPtr => wire + .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError; + + CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_MatchesPtr => wire + .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches; + + CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_VectorDbPtr => wire + .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB; + + @protected + BoxError + dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + dynamic raw, + ); + + @protected + Matches + dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + dynamic raw, + ); + + @protected + VectorDb + dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + dynamic raw, + ); + + @protected + VectorDb + dco_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + dynamic raw, + ); + + @protected + VectorDb + dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + dynamic raw, + ); + + @protected + BoxError + dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + dynamic raw, + ); + + @protected + Matches + dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + dynamic raw, + ); + + @protected + VectorDb + dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + dynamic raw, + ); + @protected String dco_decode_String(dynamic raw); @@ -43,12 +100,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); - @protected - ( - Uint64List, - Float32List - ) dco_decode_record_list_prim_u_64_strict_list_prim_f_32_strict(dynamic raw); - @protected (BigInt, BigInt, BigInt, BigInt, BigInt) dco_decode_record_usize_usize_usize_usize_usize(dynamic raw); @@ -65,6 +116,54 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected BigInt dco_decode_usize(dynamic raw); + @protected + BoxError + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + SseDeserializer deserializer, + ); + + @protected + Matches + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + SseDeserializer deserializer, + ); + + @protected + VectorDb + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + SseDeserializer deserializer, + ); + + @protected + VectorDb + sse_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + SseDeserializer deserializer, + ); + + @protected + VectorDb + sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + SseDeserializer deserializer, + ); + + @protected + BoxError + sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + SseDeserializer deserializer, + ); + + @protected + Matches + sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + SseDeserializer deserializer, + ); + + @protected + VectorDb + sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + SseDeserializer deserializer, + ); + @protected String sse_decode_String(SseDeserializer deserializer); @@ -88,12 +187,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); - @protected - (Uint64List, Float32List) - sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( - SseDeserializer deserializer, - ); - @protected (BigInt, BigInt, BigInt, BigInt, BigInt) sse_decode_record_usize_usize_usize_usize_usize( @@ -118,6 +211,62 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected bool sse_decode_bool(SseDeserializer deserializer); + @protected + void + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + BoxError self, + SseSerializer serializer, + ); + + @protected + void + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + Matches self, + SseSerializer serializer, + ); + + @protected + void + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + VectorDb self, + SseSerializer serializer, + ); + + @protected + void + sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + VectorDb self, + SseSerializer serializer, + ); + + @protected + void + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + VectorDb self, + SseSerializer serializer, + ); + + @protected + void + sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + BoxError self, + SseSerializer serializer, + ); + + @protected + void + sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + Matches self, + SseSerializer serializer, + ); + + @protected + void + sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + VectorDb self, + SseSerializer serializer, + ); + @protected void sse_encode_String(String self, SseSerializer serializer); @@ -154,12 +303,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseSerializer serializer, ); - @protected - void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( - (Uint64List, Float32List) self, - SseSerializer serializer, - ); - @protected void sse_encode_record_usize_usize_usize_usize_usize( (BigInt, BigInt, BigInt, BigInt, BigInt) self, @@ -189,6 +332,60 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { class RustLibWire implements BaseWire { RustLibWire.fromExternalLibrary(ExternalLibrary lib); + + void + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + int ptr, + ) => + wasmModule + .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + ptr, + ); + + void + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + int ptr, + ) => + wasmModule + .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + ptr, + ); + + void + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + int ptr, + ) => + wasmModule + .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + ptr, + ); + + void + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + int ptr, + ) => + wasmModule + .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + ptr, + ); + + void + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + int ptr, + ) => + wasmModule + .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + ptr, + ); + + void + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + int ptr, + ) => + wasmModule + .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + ptr, + ); } @JS('wasm_bindgen') @@ -196,4 +393,34 @@ external RustLibWasmModule get wasmModule; @JS() @anonymous -extension type RustLibWasmModule._(JSObject _) implements JSObject {} +extension type RustLibWasmModule._(JSObject _) implements JSObject { + external void + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + int ptr, + ); + + external void + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + int ptr, + ); + + external void + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + int ptr, + ); + + external void + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + int ptr, + ); + + external void + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + int ptr, + ); + + external void + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + int ptr, + ); +} diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index cb616decc0..34683b1fda 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -89,15 +89,19 @@ class _MLDebugSectionWidgetState extends State { final tenKeys = Uint64List.fromList(tenVectors.map((e) => e.fileID).toList()); final indexPath = (await getApplicationSupportDirectory()).path + - "/ml/test1/vector_db"; - final stats = await getIndexStats(indexPath: indexPath); + "/ml/test/vector_db_index.usearch"; + final rustVectorDB = VectorDb( + filePath: indexPath, + dimensions: BigInt.from(tenEmbeddings.first.length), + ); + await rustVectorDB.resetIndex(); + final stats = await rustVectorDB.getIndexStats(); logger.info("vector_db stats: $stats"); - await bulkAddVectors( - indexPath: indexPath, + await rustVectorDB.bulkAddVectors( keys: tenKeys, vectors: tenEmbeddings, ); - final statsAgain = await getIndexStats(indexPath: indexPath); + final statsAgain = await rustVectorDB.getIndexStats(); logger.info("vector_db stats again: $statsAgain"); final size = statsAgain.$1; final capacity = statsAgain.$2; @@ -106,6 +110,7 @@ class _MLDebugSectionWidgetState extends State { context, "Size: $size, Capacity: $capacity, Dimensions: $dimensions", ); + await rustVectorDB.deleteIndex(); } catch (e, s) { logger.warning('Rust bridge failed ', e, s); await showGenericErrorDialog(context: context, error: e); diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index 7405bb0b26..9a2e8f69de 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -1,114 +1,124 @@ -use usearch::{Index, IndexOptions, MetricKind, ScalarKind}; +use flutter_rust_bridge::frb; +use usearch::{ffi::Matches, Index, IndexOptions, MetricKind, ScalarKind}; -// Create DB index -fn create_index() -> Index { - let mut options = IndexOptions::default(); - options.dimensions = 512; // Set the number of dimensions for vectors - options.metric = MetricKind::Cos; // Use cosine similarity for distance measurement - options.quantization = ScalarKind::F32; // Use 32-bit floating point numbers - options.connectivity = 0; // zero for auto - options.expansion_add = 0; // zero for auto - options.expansion_search = 0; // zero for auto +use std::error::Error; +use std::path::PathBuf; - let index = Index::new(&options).expect("Failed to create index."); - index.reserve(1000).expect("Failed to reserve capacity."); - index +#[frb(opaque)] +pub struct VectorDB { + index: Index, + path: PathBuf, } -// Get the DB -fn get_index(file_path: &str) -> Index { - let file_exists: bool = std::path::Path::new(file_path).try_exists().unwrap(); - let index = create_index(); - if file_exists { - // Load into the existing index instead of creating a new variable - index.load(file_path).expect("Failed to load index."); - } else { - save_index(&index, file_path); +impl VectorDB { + #[frb(sync)] + pub fn new(file_path: &str, dimensions: usize) -> Result> { + let path = PathBuf::from(file_path); + let file_exists = path.try_exists().unwrap_or(false); + + let mut options = IndexOptions::default(); + options.dimensions = dimensions; + options.metric = MetricKind::IP; + options.quantization = ScalarKind::F32; + options.connectivity = 0; // auto + options.expansion_add = 0; // auto + options.expansion_search = 0; // auto + + let index = Index::new(&options)?; + index.reserve(1000)?; + + let db = Self { index, path }; + + if file_exists { + println!("Loading index from disk."); + db.index.load(file_path)?; + } else { + println!("Creating new index."); + db.save_index()?; + } + Ok(db) } - index -} -// Save to disk -fn save_index(index: &Index, file_path: &str) { - // Ensure directory exists - if let Some(parent) = std::path::Path::new(file_path).parent() { - std::fs::create_dir_all(parent).expect("Failed to create directory for index."); + fn save_index(&self) -> Result<(), Box> { + // Ensure directory exists + if let Some(parent) = self.path.parent() { + std::fs::create_dir_all(parent)?; + } + self.index.save(self.path.to_str().unwrap())?; + Ok(()) } - index.save(file_path).expect("Failed to save index."); -} -// Changes to DB index -fn ensure_capacity(index: &Index, margin: usize) { - let current_size = index.size(); - let capacity = index.capacity(); - if current_size + margin >= capacity { - index - .reserve(current_size + margin) - .expect("Failed to reserve capacity."); + fn ensure_capacity(&self, margin: usize) -> Result<(), Box> { + let current_size = self.index.size(); + let capacity = self.index.capacity(); + if current_size + margin >= capacity { + self.index.reserve(current_size + margin)?; + } + Ok(()) + } + + pub fn add_vector(&mut self, key: u64, vector: &Vec) -> Result<(), Box> { + self.ensure_capacity(1)?; + self.index.add(key, vector)?; + self.save_index()?; + Ok(()) + } + + pub fn bulk_add_vectors( + &mut self, + keys: Vec, + vectors: &Vec>, + ) -> Result<(), Box> { + self.ensure_capacity(keys.len())?; + for (key, vector) in keys.iter().zip(vectors.iter()) { + self.index.add(*key, vector)?; + } + self.save_index()?; + Ok(()) + } + + pub fn search_vectors( + &self, + query: &Vec, + count: usize, + ) -> Result> { + Ok(self.index.search(query, count)?) + } + + pub fn get_vector(&self, key: u64) -> Result, Box> { + let mut vector: Vec = vec![0.0; self.index.dimensions()]; + self.index.get(key, &mut vector)?; + Ok(vector) + } + + pub fn remove_vector(&mut self, key: u64) -> Result> { + let removed_count = self.index.remove(key)?; + self.save_index()?; + Ok(removed_count) + } + + pub fn reset_index(&mut self) -> Result<(), Box> { + self.index.reset()?; + self.save_index()?; + Ok(()) + } + + pub fn delete_index(self) -> Result<(), Box> { + if self.path.exists() { + std::fs::remove_file(&self.path)?; + } else { + println!("Index file does not exist."); + } + Ok(()) + } + + pub fn get_index_stats(self) -> (usize, usize, usize, usize, usize) { + let size = self.index.size(); + let capacity = self.index.capacity(); + let dimensions = self.index.dimensions(); + let expansion_add = self.index.expansion_add(); + let expansion_search = self.index.expansion_search(); + + (size, capacity, dimensions, expansion_add, expansion_search) } } - -pub fn get_index_stats(index_path: &str) -> (usize, usize, usize, usize, usize) { - let index = get_index(index_path); - let size = index.size(); - let capacity = index.capacity(); - let dimensions = index.dimensions(); - let expansion_add = index.expansion_add(); - let expansion_search = index.expansion_search(); - - (size, capacity, dimensions, expansion_add, expansion_search) -} - -// Add to index -pub fn add_vector(index_path: &str, key: u64, vector: &Vec) { - let index = get_index(index_path); - ensure_capacity(&index, 1); - index.add(key, vector).expect("Failed to add vector."); - save_index(&index, index_path); -} - -// Bulk add to index -pub fn bulk_add_vectors(index_path: &str, keys: Vec, vectors: &Vec>) { - let index = get_index(index_path); - ensure_capacity(&index, keys.len()); - for (key, vector) in keys.iter().zip(vectors.iter()) { - index.add(*key, vector).expect("Failed to add vector."); - } - save_index(&index, index_path); -} - -// Search in index -pub fn search_vectors(index_path: &str, query: &Vec, count: usize) -> (Vec, Vec) { - let index: Index = get_index(index_path); - let results = index.search(query, count).expect("Search failed."); - - (results.keys, results.distances) -} - -// Read from index -pub fn get_vector(index_path: &str, key: u64) -> Vec { - let index = get_index(index_path); - let mut vector: Vec = vec![0.0; index.dimensions()]; - let _ = index - .get(key, &mut vector) - .expect("Failed to export vector."); - - vector -} - -// Delete from index -pub fn remove_vector(index_path: &str, key: u64) -> usize { - let index = get_index(index_path); - let removed_count = index.remove(key).expect("Failed to remove vector."); - save_index(&index, index_path); - - removed_count -} - -// Reset index -pub fn reset_index(index_path: &str) { - let index = get_index(index_path); - index.reset().expect("Failed to clear index."); - - save_index(&index, index_path); -} diff --git a/mobile/rust/src/frb_generated.rs b/mobile/rust/src/frb_generated.rs index 848c851d3d..4e0837627f 100644 --- a/mobile/rust/src/frb_generated.rs +++ b/mobile/rust/src/frb_generated.rs @@ -25,6 +25,7 @@ // Section: imports +use crate::api::usearch_api::*; use flutter_rust_bridge::for_generated::byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable}; use flutter_rust_bridge::{Handler, IntoIntoDart}; @@ -37,7 +38,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.9.0"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 2006111302; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 862168794; // Section: executor @@ -45,7 +46,7 @@ flutter_rust_bridge::frb_generated_default_handler!(); // Section: wire_funcs -fn wire__crate__api__usearch_api__add_vector_impl( +fn wire__crate__api__usearch_api__VectorDb_add_vector_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len_: i32, @@ -53,7 +54,7 @@ fn wire__crate__api__usearch_api__add_vector_impl( ) { FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( flutter_rust_bridge::for_generated::TaskInfo { - debug_name: "add_vector", + debug_name: "VectorDb_add_vector", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, }, @@ -67,22 +68,40 @@ fn wire__crate__api__usearch_api__add_vector_impl( }; let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); - let api_index_path = ::sse_decode(&mut deserializer); + let api_that = , + >>::sse_decode(&mut deserializer); let api_key = ::sse_decode(&mut deserializer); let api_vector = >::sse_decode(&mut deserializer); deserializer.end(); move |context| { - transform_result_sse::<_, ()>((move || { - let output_ok = Result::<_, ()>::Ok({ - crate::api::usearch_api::add_vector(&api_index_path, api_key, &api_vector); - })?; + transform_result_sse::<_, Box>((move || { + let mut api_that_guard = None; + let decode_indices_ = + flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ + flutter_rust_bridge::for_generated::LockableOrderInfo::new( + &api_that, 0, true, + ), + ]); + for i in decode_indices_ { + match i { + 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref_mut()), + _ => unreachable!(), + } + } + let mut api_that_guard = api_that_guard.unwrap(); + let output_ok = crate::api::usearch_api::VectorDB::add_vector( + &mut *api_that_guard, + api_key, + &api_vector, + )?; Ok(output_ok) })()) } }, ) } -fn wire__crate__api__usearch_api__bulk_add_vectors_impl( +fn wire__crate__api__usearch_api__VectorDb_bulk_add_vectors_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len_: i32, @@ -90,7 +109,7 @@ fn wire__crate__api__usearch_api__bulk_add_vectors_impl( ) { FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( flutter_rust_bridge::for_generated::TaskInfo { - debug_name: "bulk_add_vectors", + debug_name: "VectorDb_bulk_add_vectors", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, }, @@ -104,26 +123,40 @@ fn wire__crate__api__usearch_api__bulk_add_vectors_impl( }; let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); - let api_index_path = ::sse_decode(&mut deserializer); + let api_that = , + >>::sse_decode(&mut deserializer); let api_keys = >::sse_decode(&mut deserializer); let api_vectors = >>::sse_decode(&mut deserializer); deserializer.end(); move |context| { - transform_result_sse::<_, ()>((move || { - let output_ok = Result::<_, ()>::Ok({ - crate::api::usearch_api::bulk_add_vectors( - &api_index_path, - api_keys, - &api_vectors, - ); - })?; + transform_result_sse::<_, Box>((move || { + let mut api_that_guard = None; + let decode_indices_ = + flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ + flutter_rust_bridge::for_generated::LockableOrderInfo::new( + &api_that, 0, true, + ), + ]); + for i in decode_indices_ { + match i { + 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref_mut()), + _ => unreachable!(), + } + } + let mut api_that_guard = api_that_guard.unwrap(); + let output_ok = crate::api::usearch_api::VectorDB::bulk_add_vectors( + &mut *api_that_guard, + api_keys, + &api_vectors, + )?; Ok(output_ok) })()) } }, ) } -fn wire__crate__api__usearch_api__get_index_stats_impl( +fn wire__crate__api__usearch_api__VectorDb_delete_index_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len_: i32, @@ -131,7 +164,7 @@ fn wire__crate__api__usearch_api__get_index_stats_impl( ) { FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( flutter_rust_bridge::for_generated::TaskInfo { - debug_name: "get_index_stats", + debug_name: "VectorDb_delete_index", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, }, @@ -145,20 +178,53 @@ fn wire__crate__api__usearch_api__get_index_stats_impl( }; let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); - let api_index_path = ::sse_decode(&mut deserializer); + let api_that = ::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, Box>((move || { + let output_ok = crate::api::usearch_api::VectorDB::delete_index(api_that)?; + Ok(output_ok) + })()) + } + }, + ) +} +fn wire__crate__api__usearch_api__VectorDb_get_index_stats_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "VectorDb_get_index_stats", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer); deserializer.end(); move |context| { transform_result_sse::<_, ()>((move || { - let output_ok = Result::<_, ()>::Ok(crate::api::usearch_api::get_index_stats( - &api_index_path, - ))?; + let output_ok = Result::<_, ()>::Ok( + crate::api::usearch_api::VectorDB::get_index_stats(api_that), + )?; Ok(output_ok) })()) } }, ) } -fn wire__crate__api__usearch_api__get_vector_impl( +fn wire__crate__api__usearch_api__VectorDb_get_vector_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len_: i32, @@ -166,7 +232,7 @@ fn wire__crate__api__usearch_api__get_vector_impl( ) { FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( flutter_rust_bridge::for_generated::TaskInfo { - debug_name: "get_vector", + debug_name: "VectorDb_get_vector", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, }, @@ -180,15 +246,219 @@ fn wire__crate__api__usearch_api__get_vector_impl( }; let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); - let api_index_path = ::sse_decode(&mut deserializer); + let api_that = , + >>::sse_decode(&mut deserializer); let api_key = ::sse_decode(&mut deserializer); deserializer.end(); move |context| { - transform_result_sse::<_, ()>((move || { - let output_ok = Result::<_, ()>::Ok(crate::api::usearch_api::get_vector( - &api_index_path, + transform_result_sse::<_, Box>((move || { + let mut api_that_guard = None; + let decode_indices_ = + flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ + flutter_rust_bridge::for_generated::LockableOrderInfo::new( + &api_that, 0, false, + ), + ]); + for i in decode_indices_ { + match i { + 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref()), + _ => unreachable!(), + } + } + let api_that_guard = api_that_guard.unwrap(); + let output_ok = + crate::api::usearch_api::VectorDB::get_vector(&*api_that_guard, api_key)?; + Ok(output_ok) + })()) + } + }, + ) +} +fn wire__crate__api__usearch_api__VectorDb_new_impl( + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_sync::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "VectorDb_new", + port: None, + mode: flutter_rust_bridge::for_generated::FfiCallMode::Sync, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_file_path = ::sse_decode(&mut deserializer); + let api_dimensions = ::sse_decode(&mut deserializer); + deserializer.end(); + transform_result_sse::<_, Box>((move || { + let output_ok = + crate::api::usearch_api::VectorDB::new(&api_file_path, api_dimensions)?; + Ok(output_ok) + })()) + }, + ) +} +fn wire__crate__api__usearch_api__VectorDb_remove_vector_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "VectorDb_remove_vector", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = , + >>::sse_decode(&mut deserializer); + let api_key = ::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, Box>((move || { + let mut api_that_guard = None; + let decode_indices_ = + flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ + flutter_rust_bridge::for_generated::LockableOrderInfo::new( + &api_that, 0, true, + ), + ]); + for i in decode_indices_ { + match i { + 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref_mut()), + _ => unreachable!(), + } + } + let mut api_that_guard = api_that_guard.unwrap(); + let output_ok = crate::api::usearch_api::VectorDB::remove_vector( + &mut *api_that_guard, api_key, - ))?; + )?; + Ok(output_ok) + })()) + } + }, + ) +} +fn wire__crate__api__usearch_api__VectorDb_reset_index_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "VectorDb_reset_index", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = , + >>::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, Box>((move || { + let mut api_that_guard = None; + let decode_indices_ = + flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ + flutter_rust_bridge::for_generated::LockableOrderInfo::new( + &api_that, 0, true, + ), + ]); + for i in decode_indices_ { + match i { + 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref_mut()), + _ => unreachable!(), + } + } + let mut api_that_guard = api_that_guard.unwrap(); + let output_ok = + crate::api::usearch_api::VectorDB::reset_index(&mut *api_that_guard)?; + Ok(output_ok) + })()) + } + }, + ) +} +fn wire__crate__api__usearch_api__VectorDb_search_vectors_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "VectorDb_search_vectors", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = , + >>::sse_decode(&mut deserializer); + let api_query = >::sse_decode(&mut deserializer); + let api_count = ::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, Box>((move || { + let mut api_that_guard = None; + let decode_indices_ = + flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ + flutter_rust_bridge::for_generated::LockableOrderInfo::new( + &api_that, 0, false, + ), + ]); + for i in decode_indices_ { + match i { + 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref()), + _ => unreachable!(), + } + } + let api_that_guard = api_that_guard.unwrap(); + let output_ok = crate::api::usearch_api::VectorDB::search_vectors( + &*api_that_guard, + &api_query, + api_count, + )?; Ok(output_ok) })()) } @@ -259,120 +529,79 @@ fn wire__crate__api__simple__init_app_impl( }, ) } -fn wire__crate__api__usearch_api__remove_vector_impl( - port_: flutter_rust_bridge::for_generated::MessagePort, - ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, - rust_vec_len_: i32, - data_len_: i32, -) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( - flutter_rust_bridge::for_generated::TaskInfo { - debug_name: "remove_vector", - port: Some(port_), - mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, - }, - move || { - let message = unsafe { - flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( - ptr_, - rust_vec_len_, - data_len_, - ) - }; - let mut deserializer = - flutter_rust_bridge::for_generated::SseDeserializer::new(message); - let api_index_path = ::sse_decode(&mut deserializer); - let api_key = ::sse_decode(&mut deserializer); - deserializer.end(); - move |context| { - transform_result_sse::<_, ()>((move || { - let output_ok = Result::<_, ()>::Ok(crate::api::usearch_api::remove_vector( - &api_index_path, - api_key, - ))?; - Ok(output_ok) - })()) - } - }, - ) -} -fn wire__crate__api__usearch_api__reset_index_impl( - port_: flutter_rust_bridge::for_generated::MessagePort, - ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, - rust_vec_len_: i32, - data_len_: i32, -) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( - flutter_rust_bridge::for_generated::TaskInfo { - debug_name: "reset_index", - port: Some(port_), - mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, - }, - move || { - let message = unsafe { - flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( - ptr_, - rust_vec_len_, - data_len_, - ) - }; - let mut deserializer = - flutter_rust_bridge::for_generated::SseDeserializer::new(message); - let api_index_path = ::sse_decode(&mut deserializer); - deserializer.end(); - move |context| { - transform_result_sse::<_, ()>((move || { - let output_ok = Result::<_, ()>::Ok({ - crate::api::usearch_api::reset_index(&api_index_path); - })?; - Ok(output_ok) - })()) - } - }, - ) -} -fn wire__crate__api__usearch_api__search_vectors_impl( - port_: flutter_rust_bridge::for_generated::MessagePort, - ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, - rust_vec_len_: i32, - data_len_: i32, -) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( - flutter_rust_bridge::for_generated::TaskInfo { - debug_name: "search_vectors", - port: Some(port_), - mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, - }, - move || { - let message = unsafe { - flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( - ptr_, - rust_vec_len_, - data_len_, - ) - }; - let mut deserializer = - flutter_rust_bridge::for_generated::SseDeserializer::new(message); - let api_index_path = ::sse_decode(&mut deserializer); - let api_query = >::sse_decode(&mut deserializer); - let api_count = ::sse_decode(&mut deserializer); - deserializer.end(); - move |context| { - transform_result_sse::<_, ()>((move || { - let output_ok = Result::<_, ()>::Ok(crate::api::usearch_api::search_vectors( - &api_index_path, - &api_query, - api_count, - ))?; - Ok(output_ok) - })()) - } - }, - ) -} + +// Section: related_funcs + +flutter_rust_bridge::frb_generated_moi_arc_impl_value!( + flutter_rust_bridge::for_generated::RustAutoOpaqueInner> +); +flutter_rust_bridge::frb_generated_moi_arc_impl_value!( + flutter_rust_bridge::for_generated::RustAutoOpaqueInner +); +flutter_rust_bridge::frb_generated_moi_arc_impl_value!( + flutter_rust_bridge::for_generated::RustAutoOpaqueInner +); // Section: dart2rust +impl SseDecode for Box { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut inner = >, + >>::sse_decode(deserializer); + return flutter_rust_bridge::for_generated::rust_auto_opaque_decode_owned(inner); + } +} + +impl SseDecode for Matches { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut inner = , + >>::sse_decode(deserializer); + return flutter_rust_bridge::for_generated::rust_auto_opaque_decode_owned(inner); + } +} + +impl SseDecode for VectorDB { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut inner = , + >>::sse_decode(deserializer); + return flutter_rust_bridge::for_generated::rust_auto_opaque_decode_owned(inner); + } +} + +impl SseDecode + for RustOpaqueMoi>> +{ + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut inner = ::sse_decode(deserializer); + return decode_rust_opaque_moi(inner); + } +} + +impl SseDecode for RustOpaqueMoi> { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut inner = ::sse_decode(deserializer); + return decode_rust_opaque_moi(inner); + } +} + +impl SseDecode + for RustOpaqueMoi> +{ + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut inner = ::sse_decode(deserializer); + return decode_rust_opaque_moi(inner); + } +} + impl SseDecode for String { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -436,15 +665,6 @@ impl SseDecode for Vec { } } -impl SseDecode for (Vec, Vec) { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - let mut var_field0 = >::sse_decode(deserializer); - let mut var_field1 = >::sse_decode(deserializer); - return (var_field0, var_field1); - } -} - impl SseDecode for (usize, usize, usize, usize, usize) { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -506,16 +726,55 @@ fn pde_ffi_dispatcher_primary_impl( ) { // Codec=Pde (Serialization + dispatch), see doc to use other codecs match func_id { - 1 => wire__crate__api__usearch_api__add_vector_impl(port, ptr, rust_vec_len, data_len), - 2 => { - wire__crate__api__usearch_api__bulk_add_vectors_impl(port, ptr, rust_vec_len, data_len) - } - 3 => wire__crate__api__usearch_api__get_index_stats_impl(port, ptr, rust_vec_len, data_len), - 4 => wire__crate__api__usearch_api__get_vector_impl(port, ptr, rust_vec_len, data_len), - 6 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), - 7 => wire__crate__api__usearch_api__remove_vector_impl(port, ptr, rust_vec_len, data_len), - 8 => wire__crate__api__usearch_api__reset_index_impl(port, ptr, rust_vec_len, data_len), - 9 => wire__crate__api__usearch_api__search_vectors_impl(port, ptr, rust_vec_len, data_len), + 1 => wire__crate__api__usearch_api__VectorDb_add_vector_impl( + port, + ptr, + rust_vec_len, + data_len, + ), + 2 => wire__crate__api__usearch_api__VectorDb_bulk_add_vectors_impl( + port, + ptr, + rust_vec_len, + data_len, + ), + 3 => wire__crate__api__usearch_api__VectorDb_delete_index_impl( + port, + ptr, + rust_vec_len, + data_len, + ), + 4 => wire__crate__api__usearch_api__VectorDb_get_index_stats_impl( + port, + ptr, + rust_vec_len, + data_len, + ), + 5 => wire__crate__api__usearch_api__VectorDb_get_vector_impl( + port, + ptr, + rust_vec_len, + data_len, + ), + 7 => wire__crate__api__usearch_api__VectorDb_remove_vector_impl( + port, + ptr, + rust_vec_len, + data_len, + ), + 8 => wire__crate__api__usearch_api__VectorDb_reset_index_impl( + port, + ptr, + rust_vec_len, + data_len, + ), + 9 => wire__crate__api__usearch_api__VectorDb_search_vectors_impl( + port, + ptr, + rust_vec_len, + data_len, + ), + 11 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -528,13 +787,111 @@ fn pde_ffi_dispatcher_sync_impl( ) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { // Codec=Pde (Serialization + dispatch), see doc to use other codecs match func_id { - 5 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), + 6 => wire__crate__api__usearch_api__VectorDb_new_impl(ptr, rust_vec_len, data_len), + 10 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), _ => unreachable!(), } } // Section: rust2dart +// Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for FrbWrapper> { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + flutter_rust_bridge::for_generated::rust_auto_opaque_encode::<_, MoiArc<_>>(self.0) + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for FrbWrapper> {} + +impl flutter_rust_bridge::IntoIntoDart>> for Box { + fn into_into_dart(self) -> FrbWrapper> { + self.into() + } +} + +// Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for FrbWrapper { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + flutter_rust_bridge::for_generated::rust_auto_opaque_encode::<_, MoiArc<_>>(self.0) + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for FrbWrapper {} + +impl flutter_rust_bridge::IntoIntoDart> for Matches { + fn into_into_dart(self) -> FrbWrapper { + self.into() + } +} + +// Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for FrbWrapper { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + flutter_rust_bridge::for_generated::rust_auto_opaque_encode::<_, MoiArc<_>>(self.0) + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for FrbWrapper {} + +impl flutter_rust_bridge::IntoIntoDart> for VectorDB { + fn into_into_dart(self) -> FrbWrapper { + self.into() + } +} + +impl SseEncode for Box { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + >>>::sse_encode(flutter_rust_bridge::for_generated::rust_auto_opaque_encode::<_, MoiArc<_>>(self), serializer); + } +} + +impl SseEncode for Matches { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + >>::sse_encode(flutter_rust_bridge::for_generated::rust_auto_opaque_encode::<_, MoiArc<_>>(self), serializer); + } +} + +impl SseEncode for VectorDB { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + >>::sse_encode(flutter_rust_bridge::for_generated::rust_auto_opaque_encode::<_, MoiArc<_>>(self), serializer); + } +} + +impl SseEncode + for RustOpaqueMoi>> +{ + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + let (ptr, size) = self.sse_encode_raw(); + ::sse_encode(ptr, serializer); + ::sse_encode(size, serializer); + } +} + +impl SseEncode for RustOpaqueMoi> { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + let (ptr, size) = self.sse_encode_raw(); + ::sse_encode(ptr, serializer); + ::sse_encode(size, serializer); + } +} + +impl SseEncode + for RustOpaqueMoi> +{ + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + let (ptr, size) = self.sse_encode_raw(); + ::sse_encode(ptr, serializer); + ::sse_encode(size, serializer); + } +} + impl SseEncode for String { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -589,14 +946,6 @@ impl SseEncode for Vec { } } -impl SseEncode for (Vec, Vec) { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - >::sse_encode(self.0, serializer); - >::sse_encode(self.1, serializer); - } -} - impl SseEncode for (usize, usize, usize, usize, usize) { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -659,6 +1008,7 @@ mod io { // Section: imports use super::*; + use crate::api::usearch_api::*; use flutter_rust_bridge::for_generated::byteorder::{ NativeEndian, ReadBytesExt, WriteBytesExt, }; @@ -668,6 +1018,48 @@ mod io { // Section: boilerplate flutter_rust_bridge::frb_generated_boilerplate_io!(); + + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_photos_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + ptr: *const std::ffi::c_void, + ) { + MoiArc::>>::increment_strong_count(ptr as _); + } + + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_photos_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + ptr: *const std::ffi::c_void, + ) { + MoiArc::>>::decrement_strong_count(ptr as _); + } + + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_photos_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + ptr: *const std::ffi::c_void, + ) { + MoiArc::>::increment_strong_count(ptr as _); + } + + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_photos_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + ptr: *const std::ffi::c_void, + ) { + MoiArc::>::decrement_strong_count(ptr as _); + } + + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_photos_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + ptr: *const std::ffi::c_void, + ) { + MoiArc::>::increment_strong_count(ptr as _); + } + + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_photos_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + ptr: *const std::ffi::c_void, + ) { + MoiArc::>::decrement_strong_count(ptr as _); + } } #[cfg(not(target_family = "wasm"))] pub use io::*; @@ -681,6 +1073,7 @@ mod web { // Section: imports use super::*; + use crate::api::usearch_api::*; use flutter_rust_bridge::for_generated::byteorder::{ NativeEndian, ReadBytesExt, WriteBytesExt, }; @@ -692,6 +1085,48 @@ mod web { // Section: boilerplate flutter_rust_bridge::frb_generated_boilerplate_web!(); + + #[wasm_bindgen] + pub fn rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + ptr: *const std::ffi::c_void, + ) { + MoiArc::>>::increment_strong_count(ptr as _); + } + + #[wasm_bindgen] + pub fn rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( + ptr: *const std::ffi::c_void, + ) { + MoiArc::>>::decrement_strong_count(ptr as _); + } + + #[wasm_bindgen] + pub fn rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + ptr: *const std::ffi::c_void, + ) { + MoiArc::>::increment_strong_count(ptr as _); + } + + #[wasm_bindgen] + pub fn rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( + ptr: *const std::ffi::c_void, + ) { + MoiArc::>::decrement_strong_count(ptr as _); + } + + #[wasm_bindgen] + pub fn rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + ptr: *const std::ffi::c_void, + ) { + MoiArc::>::increment_strong_count(ptr as _); + } + + #[wasm_bindgen] + pub fn rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + ptr: *const std::ffi::c_void, + ) { + MoiArc::>::decrement_strong_count(ptr as _); + } } #[cfg(target_family = "wasm")] pub use web::*; From 6d2f53b86cb33ddb68f3516abaf6311cf642195b Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 8 Apr 2025 14:56:47 +0530 Subject: [PATCH 009/156] Update usearch --- mobile/rust/Cargo.lock | 4 ++-- mobile/rust/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile/rust/Cargo.lock b/mobile/rust/Cargo.lock index 067a4e8476..c4a221f057 100644 --- a/mobile/rust/Cargo.lock +++ b/mobile/rust/Cargo.lock @@ -670,9 +670,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "usearch" -version = "2.17.2" +version = "2.17.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d405433f45fd46c88c7499e68381e0a27b742f6cd0daf8bfc186ee6d8195a387" +checksum = "2feb45cdbc2d9b39ded81be910b505d062654725a0024ce5a509fef0eaaa3f0e" dependencies = [ "cxx", "cxx-build", diff --git a/mobile/rust/Cargo.toml b/mobile/rust/Cargo.toml index b35ca7bc7e..615f48d482 100644 --- a/mobile/rust/Cargo.toml +++ b/mobile/rust/Cargo.toml @@ -8,7 +8,7 @@ crate-type = ["cdylib", "staticlib"] [dependencies] flutter_rust_bridge = "=2.9.0" -usearch = "2.17.2" +usearch = "2.17.6" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] } From e6ee09ca30c98aafdb9fa51dc2d29e87ec598af1 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 8 Apr 2025 17:03:41 +0530 Subject: [PATCH 010/156] Back to basic error handling --- mobile/lib/src/rust/api/usearch_api.dart | 8 +- mobile/lib/src/rust/frb_generated.dart | 275 ++++----------------- mobile/lib/src/rust/frb_generated.io.dart | 168 ++----------- mobile/lib/src/rust/frb_generated.web.dart | 156 ++---------- mobile/rust/src/api/usearch_api.rs | 97 ++++---- mobile/rust/src/frb_generated.rs | 261 +++++-------------- 6 files changed, 197 insertions(+), 768 deletions(-) diff --git a/mobile/lib/src/rust/api/usearch_api.dart b/mobile/lib/src/rust/api/usearch_api.dart index 67a5f1ad9b..777d513f9f 100644 --- a/mobile/lib/src/rust/api/usearch_api.dart +++ b/mobile/lib/src/rust/api/usearch_api.dart @@ -8,12 +8,6 @@ import 'package:photos/src/rust/frb_generated.dart'; // These functions are ignored because they are not marked as `pub`: `ensure_capacity`, `save_index` -// Rust type: RustOpaqueMoi>> -abstract class BoxError implements RustOpaqueInterface {} - -// Rust type: RustOpaqueMoi> -abstract class Matches implements RustOpaqueInterface {} - // Rust type: RustOpaqueMoi> abstract class VectorDb implements RustOpaqueInterface { Future addVector({required BigInt key, required List vector}); @@ -39,7 +33,7 @@ abstract class VectorDb implements RustOpaqueInterface { Future resetIndex(); - Future searchVectors({ + Future<(Uint64List, Float32List)> searchVectors({ required List query, required BigInt count, }); diff --git a/mobile/lib/src/rust/frb_generated.dart b/mobile/lib/src/rust/frb_generated.dart index 328ff572b4..3d182962f4 100644 --- a/mobile/lib/src/rust/frb_generated.dart +++ b/mobile/lib/src/rust/frb_generated.dart @@ -115,7 +115,7 @@ abstract class RustLibApi extends BaseApi { Future crateApiUsearchApiVectorDbResetIndex({required VectorDb that}); - Future crateApiUsearchApiVectorDbSearchVectors({ + Future<(Uint64List, Float32List)> crateApiUsearchApiVectorDbSearchVectors({ required VectorDb that, required List query, required BigInt count, @@ -125,20 +125,6 @@ abstract class RustLibApi extends BaseApi { Future crateApiSimpleInitApp(); - RustArcIncrementStrongCountFnType - get rust_arc_increment_strong_count_BoxError; - - RustArcDecrementStrongCountFnType - get rust_arc_decrement_strong_count_BoxError; - - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_BoxErrorPtr; - - RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_Matches; - - RustArcDecrementStrongCountFnType get rust_arc_decrement_strong_count_Matches; - - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_MatchesPtr; - RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_VectorDb; @@ -181,8 +167,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { }, codec: SseCodec( decodeSuccessData: sse_decode_unit, - decodeErrorData: - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError, + decodeErrorData: null, ), constMeta: kCrateApiUsearchApiVectorDbAddVectorConstMeta, argValues: [that, key, vector], @@ -222,8 +207,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { }, codec: SseCodec( decodeSuccessData: sse_decode_unit, - decodeErrorData: - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError, + decodeErrorData: null, ), constMeta: kCrateApiUsearchApiVectorDbBulkAddVectorsConstMeta, argValues: [that, keys, vectors], @@ -257,8 +241,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { }, codec: SseCodec( decodeSuccessData: sse_decode_unit, - decodeErrorData: - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError, + decodeErrorData: null, ), constMeta: kCrateApiUsearchApiVectorDbDeleteIndexConstMeta, argValues: [that], @@ -331,8 +314,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { }, codec: SseCodec( decodeSuccessData: sse_decode_list_prim_f_32_strict, - decodeErrorData: - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError, + decodeErrorData: null, ), constMeta: kCrateApiUsearchApiVectorDbGetVectorConstMeta, argValues: [that, key], @@ -363,8 +345,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { codec: SseCodec( decodeSuccessData: sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB, - decodeErrorData: - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError, + decodeErrorData: null, ), constMeta: kCrateApiUsearchApiVectorDbNewConstMeta, argValues: [filePath, dimensions], @@ -402,8 +383,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { }, codec: SseCodec( decodeSuccessData: sse_decode_usize, - decodeErrorData: - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError, + decodeErrorData: null, ), constMeta: kCrateApiUsearchApiVectorDbRemoveVectorConstMeta, argValues: [that, key], @@ -437,8 +417,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { }, codec: SseCodec( decodeSuccessData: sse_decode_unit, - decodeErrorData: - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError, + decodeErrorData: null, ), constMeta: kCrateApiUsearchApiVectorDbResetIndexConstMeta, argValues: [that], @@ -454,7 +433,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); @override - Future crateApiUsearchApiVectorDbSearchVectors({ + Future<(Uint64List, Float32List)> crateApiUsearchApiVectorDbSearchVectors({ required VectorDb that, required List query, required BigInt count, @@ -478,9 +457,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { }, codec: SseCodec( decodeSuccessData: - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches, - decodeErrorData: - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError, + sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict, + decodeErrorData: null, ), constMeta: kCrateApiUsearchApiVectorDbSearchVectorsConstMeta, argValues: [that, query, count], @@ -549,22 +527,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: [], ); - RustArcIncrementStrongCountFnType - get rust_arc_increment_strong_count_BoxError => wire - .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError; - - RustArcDecrementStrongCountFnType - get rust_arc_decrement_strong_count_BoxError => wire - .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError; - - RustArcIncrementStrongCountFnType - get rust_arc_increment_strong_count_Matches => wire - .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches; - - RustArcDecrementStrongCountFnType - get rust_arc_decrement_strong_count_Matches => wire - .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches; - RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_VectorDb => wire .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB; @@ -573,24 +535,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { get rust_arc_decrement_strong_count_VectorDb => wire .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB; - @protected - BoxError - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - dynamic raw, - ) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return BoxErrorImpl.frbInternalDcoDecode(raw as List); - } - - @protected - Matches - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - dynamic raw, - ) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return MatchesImpl.frbInternalDcoDecode(raw as List); - } - @protected VectorDb dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -618,24 +562,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return VectorDbImpl.frbInternalDcoDecode(raw as List); } - @protected - BoxError - dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - dynamic raw, - ) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return BoxErrorImpl.frbInternalDcoDecode(raw as List); - } - - @protected - Matches - dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - dynamic raw, - ) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return MatchesImpl.frbInternalDcoDecode(raw as List); - } - @protected VectorDb dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -689,6 +615,22 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw as Uint8List; } + @protected + ( + Uint64List, + Float32List + ) dco_decode_record_list_prim_u_64_strict_list_prim_f_32_strict(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 2) { + throw Exception('Expected 2 elements, got ${arr.length}'); + } + return ( + dco_decode_list_prim_u_64_strict(arr[0]), + dco_decode_list_prim_f_32_strict(arr[1]), + ); + } + @protected (BigInt, BigInt, BigInt, BigInt, BigInt) dco_decode_record_usize_usize_usize_usize_usize(dynamic raw) { @@ -730,30 +672,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return dcoDecodeU64(raw); } - @protected - BoxError - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - SseDeserializer deserializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - return BoxErrorImpl.frbInternalSseDecode( - sse_decode_usize(deserializer), - sse_decode_i_32(deserializer), - ); - } - - @protected - Matches - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - SseDeserializer deserializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - return MatchesImpl.frbInternalSseDecode( - sse_decode_usize(deserializer), - sse_decode_i_32(deserializer), - ); - } - @protected VectorDb sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -790,30 +708,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - @protected - BoxError - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - SseDeserializer deserializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - return BoxErrorImpl.frbInternalSseDecode( - sse_decode_usize(deserializer), - sse_decode_i_32(deserializer), - ); - } - - @protected - Matches - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - SseDeserializer deserializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - return MatchesImpl.frbInternalSseDecode( - sse_decode_usize(deserializer), - sse_decode_i_32(deserializer), - ); - } - @protected VectorDb sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -881,6 +775,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return deserializer.buffer.getUint8List(len_); } + @protected + (Uint64List, Float32List) + sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + final var_field0 = sse_decode_list_prim_u_64_strict(deserializer); + final var_field1 = sse_decode_list_prim_f_32_strict(deserializer); + return (var_field0, var_field1); + } + @protected (BigInt, BigInt, BigInt, BigInt, BigInt) sse_decode_record_usize_usize_usize_usize_usize( @@ -930,32 +835,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return deserializer.buffer.getUint8() != 0; } - @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - BoxError self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize( - (self as BoxErrorImpl).frbInternalSseEncode(move: true), - serializer, - ); - } - - @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - Matches self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize( - (self as MatchesImpl).frbInternalSseEncode(move: true), - serializer, - ); - } - @protected void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -995,32 +874,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - @protected - void - sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - BoxError self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize( - (self as BoxErrorImpl).frbInternalSseEncode(move: null), - serializer, - ); - } - - @protected - void - sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - Matches self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize( - (self as MatchesImpl).frbInternalSseEncode(move: null), - serializer, - ); - } - @protected void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -1100,6 +953,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { serializer.buffer.putUint8List(self); } + @protected + void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( + (Uint64List, Float32List) self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_list_prim_u_64_strict(self.$1, serializer); + sse_encode_list_prim_f_32_strict(self.$2, serializer); + } + @protected void sse_encode_record_usize_usize_usize_usize_usize( (BigInt, BigInt, BigInt, BigInt, BigInt) self, @@ -1149,46 +1012,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } -@sealed -class BoxErrorImpl extends RustOpaque implements BoxError { - // Not to be used by end users - BoxErrorImpl.frbInternalDcoDecode(List wire) - : super.frbInternalDcoDecode(wire, _kStaticData); - - // Not to be used by end users - BoxErrorImpl.frbInternalSseDecode(BigInt ptr, int externalSizeOnNative) - : super.frbInternalSseDecode(ptr, externalSizeOnNative, _kStaticData); - - static final _kStaticData = RustArcStaticData( - rustArcIncrementStrongCount: - RustLib.instance.api.rust_arc_increment_strong_count_BoxError, - rustArcDecrementStrongCount: - RustLib.instance.api.rust_arc_decrement_strong_count_BoxError, - rustArcDecrementStrongCountPtr: - RustLib.instance.api.rust_arc_decrement_strong_count_BoxErrorPtr, - ); -} - -@sealed -class MatchesImpl extends RustOpaque implements Matches { - // Not to be used by end users - MatchesImpl.frbInternalDcoDecode(List wire) - : super.frbInternalDcoDecode(wire, _kStaticData); - - // Not to be used by end users - MatchesImpl.frbInternalSseDecode(BigInt ptr, int externalSizeOnNative) - : super.frbInternalSseDecode(ptr, externalSizeOnNative, _kStaticData); - - static final _kStaticData = RustArcStaticData( - rustArcIncrementStrongCount: - RustLib.instance.api.rust_arc_increment_strong_count_Matches, - rustArcDecrementStrongCount: - RustLib.instance.api.rust_arc_decrement_strong_count_Matches, - rustArcDecrementStrongCountPtr: - RustLib.instance.api.rust_arc_decrement_strong_count_MatchesPtr, - ); -} - @sealed class VectorDbImpl extends RustOpaque implements VectorDb { // Not to be used by end users @@ -1246,7 +1069,7 @@ class VectorDbImpl extends RustOpaque implements VectorDb { that: this, ); - Future searchVectors({ + Future<(Uint64List, Float32List)> searchVectors({ required List query, required BigInt count, }) => diff --git a/mobile/lib/src/rust/frb_generated.io.dart b/mobile/lib/src/rust/frb_generated.io.dart index 7153fb2de4..3421eab7dd 100644 --- a/mobile/lib/src/rust/frb_generated.io.dart +++ b/mobile/lib/src/rust/frb_generated.io.dart @@ -20,27 +20,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { required super.portManager, }); - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_BoxErrorPtr => wire - ._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynErrorPtr; - - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_MatchesPtr => wire - ._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatchesPtr; - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_VectorDbPtr => wire ._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDBPtr; - @protected - BoxError - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - dynamic raw, - ); - - @protected - Matches - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - dynamic raw, - ); - @protected VectorDb dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -59,18 +41,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { dynamic raw, ); - @protected - BoxError - dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - dynamic raw, - ); - - @protected - Matches - dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - dynamic raw, - ); - @protected VectorDb dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -98,6 +68,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); + @protected + ( + Uint64List, + Float32List + ) dco_decode_record_list_prim_u_64_strict_list_prim_f_32_strict(dynamic raw); + @protected (BigInt, BigInt, BigInt, BigInt, BigInt) dco_decode_record_usize_usize_usize_usize_usize(dynamic raw); @@ -114,18 +90,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected BigInt dco_decode_usize(dynamic raw); - @protected - BoxError - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - SseDeserializer deserializer, - ); - - @protected - Matches - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - SseDeserializer deserializer, - ); - @protected VectorDb sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -144,18 +108,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseDeserializer deserializer, ); - @protected - BoxError - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - SseDeserializer deserializer, - ); - - @protected - Matches - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - SseDeserializer deserializer, - ); - @protected VectorDb sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -185,6 +137,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); + @protected + (Uint64List, Float32List) + sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( + SseDeserializer deserializer, + ); + @protected (BigInt, BigInt, BigInt, BigInt, BigInt) sse_decode_record_usize_usize_usize_usize_usize( @@ -209,20 +167,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected bool sse_decode_bool(SseDeserializer deserializer); - @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - BoxError self, - SseSerializer serializer, - ); - - @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - Matches self, - SseSerializer serializer, - ); - @protected void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -244,20 +188,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseSerializer serializer, ); - @protected - void - sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - BoxError self, - SseSerializer serializer, - ); - - @protected - void - sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - Matches self, - SseSerializer serializer, - ); - @protected void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -301,6 +231,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseSerializer serializer, ); + @protected + void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( + (Uint64List, Float32List) self, + SseSerializer serializer, + ); + @protected void sse_encode_record_usize_usize_usize_usize_usize( (BigInt, BigInt, BigInt, BigInt, BigInt) self, @@ -340,74 +276,6 @@ class RustLibWire implements BaseWire { RustLibWire(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; - void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - ffi.Pointer ptr, - ) { - return _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - ptr, - ); - } - - late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynErrorPtr = - _lookup)>>( - 'frbgen_photos_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError', - ); - late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError = - _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynErrorPtr - .asFunction)>(); - - void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - ffi.Pointer ptr, - ) { - return _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - ptr, - ); - } - - late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynErrorPtr = - _lookup)>>( - 'frbgen_photos_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError', - ); - late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError = - _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynErrorPtr - .asFunction)>(); - - void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - ffi.Pointer ptr, - ) { - return _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - ptr, - ); - } - - late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatchesPtr = - _lookup)>>( - 'frbgen_photos_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches', - ); - late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches = - _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatchesPtr - .asFunction)>(); - - void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - ffi.Pointer ptr, - ) { - return _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - ptr, - ); - } - - late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatchesPtr = - _lookup)>>( - 'frbgen_photos_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches', - ); - late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches = - _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatchesPtr - .asFunction)>(); - void rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( ffi.Pointer ptr, diff --git a/mobile/lib/src/rust/frb_generated.web.dart b/mobile/lib/src/rust/frb_generated.web.dart index b91911a6dd..36d5abfc75 100644 --- a/mobile/lib/src/rust/frb_generated.web.dart +++ b/mobile/lib/src/rust/frb_generated.web.dart @@ -22,27 +22,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { required super.portManager, }); - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_BoxErrorPtr => wire - .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError; - - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_MatchesPtr => wire - .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches; - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_VectorDbPtr => wire .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB; - @protected - BoxError - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - dynamic raw, - ); - - @protected - Matches - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - dynamic raw, - ); - @protected VectorDb dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -61,18 +43,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { dynamic raw, ); - @protected - BoxError - dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - dynamic raw, - ); - - @protected - Matches - dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - dynamic raw, - ); - @protected VectorDb dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -100,6 +70,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); + @protected + ( + Uint64List, + Float32List + ) dco_decode_record_list_prim_u_64_strict_list_prim_f_32_strict(dynamic raw); + @protected (BigInt, BigInt, BigInt, BigInt, BigInt) dco_decode_record_usize_usize_usize_usize_usize(dynamic raw); @@ -116,18 +92,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected BigInt dco_decode_usize(dynamic raw); - @protected - BoxError - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - SseDeserializer deserializer, - ); - - @protected - Matches - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - SseDeserializer deserializer, - ); - @protected VectorDb sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -146,18 +110,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseDeserializer deserializer, ); - @protected - BoxError - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - SseDeserializer deserializer, - ); - - @protected - Matches - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - SseDeserializer deserializer, - ); - @protected VectorDb sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -187,6 +139,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); + @protected + (Uint64List, Float32List) + sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( + SseDeserializer deserializer, + ); + @protected (BigInt, BigInt, BigInt, BigInt, BigInt) sse_decode_record_usize_usize_usize_usize_usize( @@ -211,20 +169,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected bool sse_decode_bool(SseDeserializer deserializer); - @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - BoxError self, - SseSerializer serializer, - ); - - @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - Matches self, - SseSerializer serializer, - ); - @protected void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -246,20 +190,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseSerializer serializer, ); - @protected - void - sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - BoxError self, - SseSerializer serializer, - ); - - @protected - void - sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - Matches self, - SseSerializer serializer, - ); - @protected void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -303,6 +233,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseSerializer serializer, ); + @protected + void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( + (Uint64List, Float32List) self, + SseSerializer serializer, + ); + @protected void sse_encode_record_usize_usize_usize_usize_usize( (BigInt, BigInt, BigInt, BigInt, BigInt) self, @@ -333,42 +269,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { class RustLibWire implements BaseWire { RustLibWire.fromExternalLibrary(ExternalLibrary lib); - void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - int ptr, - ) => - wasmModule - .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - ptr, - ); - - void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - int ptr, - ) => - wasmModule - .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - ptr, - ); - - void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - int ptr, - ) => - wasmModule - .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - ptr, - ); - - void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - int ptr, - ) => - wasmModule - .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - ptr, - ); - void rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( int ptr, @@ -394,26 +294,6 @@ external RustLibWasmModule get wasmModule; @JS() @anonymous extension type RustLibWasmModule._(JSObject _) implements JSObject { - external void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - int ptr, - ); - - external void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - int ptr, - ); - - external void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - int ptr, - ); - - external void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - int ptr, - ); - external void rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( int ptr, diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index 9a2e8f69de..b0912cfa75 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -1,7 +1,6 @@ use flutter_rust_bridge::frb; -use usearch::{ffi::Matches, Index, IndexOptions, MetricKind, ScalarKind}; +use usearch::{Index, IndexOptions, MetricKind, ScalarKind}; -use std::error::Error; use std::path::PathBuf; #[frb(opaque)] @@ -12,7 +11,7 @@ pub struct VectorDB { impl VectorDB { #[frb(sync)] - pub fn new(file_path: &str, dimensions: usize) -> Result> { + pub fn new(file_path: &str, dimensions: usize) -> Self { let path = PathBuf::from(file_path); let file_exists = path.try_exists().unwrap_or(false); @@ -24,92 +23,92 @@ impl VectorDB { options.expansion_add = 0; // auto options.expansion_search = 0; // auto - let index = Index::new(&options)?; - index.reserve(1000)?; + let index = Index::new(&options).expect("Failed to create index"); + index + .reserve(1000) + .expect("Failed to reserve space in index"); let db = Self { index, path }; if file_exists { println!("Loading index from disk."); - db.index.load(file_path)?; + db.index.load(file_path).expect("Failed to load index"); } else { println!("Creating new index."); - db.save_index()?; + db.save_index(); } - Ok(db) + db } - fn save_index(&self) -> Result<(), Box> { + fn save_index(&self) { // Ensure directory exists if let Some(parent) = self.path.parent() { - std::fs::create_dir_all(parent)?; + std::fs::create_dir_all(parent).expect("Failed to create directory"); } - self.index.save(self.path.to_str().unwrap())?; - Ok(()) + self.index + .save(self.path.to_str().expect("Invalid path")) + .expect("Failed to save index"); } - fn ensure_capacity(&self, margin: usize) -> Result<(), Box> { + fn ensure_capacity(&self, margin: usize) { let current_size = self.index.size(); let capacity = self.index.capacity(); if current_size + margin >= capacity { - self.index.reserve(current_size + margin)?; + self.index + .reserve(current_size + margin) + .expect("Failed to reserve space in index"); } - Ok(()) } - pub fn add_vector(&mut self, key: u64, vector: &Vec) -> Result<(), Box> { - self.ensure_capacity(1)?; - self.index.add(key, vector)?; - self.save_index()?; - Ok(()) + pub fn add_vector(&mut self, key: u64, vector: &Vec) { + self.ensure_capacity(1); + self.index.add(key, vector).expect("Failed to add vector"); + self.save_index(); } - pub fn bulk_add_vectors( - &mut self, - keys: Vec, - vectors: &Vec>, - ) -> Result<(), Box> { - self.ensure_capacity(keys.len())?; + pub fn bulk_add_vectors(&mut self, keys: Vec, vectors: &Vec>) { + self.ensure_capacity(keys.len()); for (key, vector) in keys.iter().zip(vectors.iter()) { - self.index.add(*key, vector)?; + self.index + .add(*key, vector) + .expect("Failed to (bulk) add vector"); } - self.save_index()?; - Ok(()) + self.save_index(); } - pub fn search_vectors( - &self, - query: &Vec, - count: usize, - ) -> Result> { - Ok(self.index.search(query, count)?) + pub fn search_vectors(&self, query: &Vec, count: usize) -> (Vec, Vec) { + let matches = self + .index + .search(query, count) + .expect("Failed to search vectors"); + (matches.keys, matches.distances) } - pub fn get_vector(&self, key: u64) -> Result, Box> { + pub fn get_vector(&self, key: u64) -> Vec { let mut vector: Vec = vec![0.0; self.index.dimensions()]; - self.index.get(key, &mut vector)?; - Ok(vector) + self.index + .get(key, &mut vector) + .expect("Failed to get vector"); + vector } - pub fn remove_vector(&mut self, key: u64) -> Result> { - let removed_count = self.index.remove(key)?; - self.save_index()?; - Ok(removed_count) + pub fn remove_vector(&mut self, key: u64) -> usize { + let removed_count = self.index.remove(key).expect("Failed to remove vector"); + self.save_index(); + removed_count } - pub fn reset_index(&mut self) -> Result<(), Box> { - self.index.reset()?; - self.save_index()?; - Ok(()) + pub fn reset_index(&mut self) { + self.index.reset(); + self.save_index(); } - pub fn delete_index(self) -> Result<(), Box> { + pub fn delete_index(self) { if self.path.exists() { - std::fs::remove_file(&self.path)?; + std::fs::remove_file(&self.path).expect("Failed to delete index file"); } else { println!("Index file does not exist."); } - Ok(()) } pub fn get_index_stats(self) -> (usize, usize, usize, usize, usize) { diff --git a/mobile/rust/src/frb_generated.rs b/mobile/rust/src/frb_generated.rs index 4e0837627f..3e87600e9d 100644 --- a/mobile/rust/src/frb_generated.rs +++ b/mobile/rust/src/frb_generated.rs @@ -75,7 +75,7 @@ fn wire__crate__api__usearch_api__VectorDb_add_vector_impl( let api_vector = >::sse_decode(&mut deserializer); deserializer.end(); move |context| { - transform_result_sse::<_, Box>((move || { + transform_result_sse::<_, ()>((move || { let mut api_that_guard = None; let decode_indices_ = flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ @@ -90,11 +90,13 @@ fn wire__crate__api__usearch_api__VectorDb_add_vector_impl( } } let mut api_that_guard = api_that_guard.unwrap(); - let output_ok = crate::api::usearch_api::VectorDB::add_vector( - &mut *api_that_guard, - api_key, - &api_vector, - )?; + let output_ok = Result::<_, ()>::Ok({ + crate::api::usearch_api::VectorDB::add_vector( + &mut *api_that_guard, + api_key, + &api_vector, + ); + })?; Ok(output_ok) })()) } @@ -130,7 +132,7 @@ fn wire__crate__api__usearch_api__VectorDb_bulk_add_vectors_impl( let api_vectors = >>::sse_decode(&mut deserializer); deserializer.end(); move |context| { - transform_result_sse::<_, Box>((move || { + transform_result_sse::<_, ()>((move || { let mut api_that_guard = None; let decode_indices_ = flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ @@ -145,11 +147,13 @@ fn wire__crate__api__usearch_api__VectorDb_bulk_add_vectors_impl( } } let mut api_that_guard = api_that_guard.unwrap(); - let output_ok = crate::api::usearch_api::VectorDB::bulk_add_vectors( - &mut *api_that_guard, - api_keys, - &api_vectors, - )?; + let output_ok = Result::<_, ()>::Ok({ + crate::api::usearch_api::VectorDB::bulk_add_vectors( + &mut *api_that_guard, + api_keys, + &api_vectors, + ); + })?; Ok(output_ok) })()) } @@ -181,8 +185,10 @@ fn wire__crate__api__usearch_api__VectorDb_delete_index_impl( let api_that = ::sse_decode(&mut deserializer); deserializer.end(); move |context| { - transform_result_sse::<_, Box>((move || { - let output_ok = crate::api::usearch_api::VectorDB::delete_index(api_that)?; + transform_result_sse::<_, ()>((move || { + let output_ok = Result::<_, ()>::Ok({ + crate::api::usearch_api::VectorDB::delete_index(api_that); + })?; Ok(output_ok) })()) } @@ -252,7 +258,7 @@ fn wire__crate__api__usearch_api__VectorDb_get_vector_impl( let api_key = ::sse_decode(&mut deserializer); deserializer.end(); move |context| { - transform_result_sse::<_, Box>((move || { + transform_result_sse::<_, ()>((move || { let mut api_that_guard = None; let decode_indices_ = flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ @@ -267,8 +273,9 @@ fn wire__crate__api__usearch_api__VectorDb_get_vector_impl( } } let api_that_guard = api_that_guard.unwrap(); - let output_ok = - crate::api::usearch_api::VectorDB::get_vector(&*api_that_guard, api_key)?; + let output_ok = Result::<_, ()>::Ok( + crate::api::usearch_api::VectorDB::get_vector(&*api_that_guard, api_key), + )?; Ok(output_ok) })()) } @@ -299,9 +306,11 @@ fn wire__crate__api__usearch_api__VectorDb_new_impl( let api_file_path = ::sse_decode(&mut deserializer); let api_dimensions = ::sse_decode(&mut deserializer); deserializer.end(); - transform_result_sse::<_, Box>((move || { - let output_ok = - crate::api::usearch_api::VectorDB::new(&api_file_path, api_dimensions)?; + transform_result_sse::<_, ()>((move || { + let output_ok = Result::<_, ()>::Ok(crate::api::usearch_api::VectorDB::new( + &api_file_path, + api_dimensions, + ))?; Ok(output_ok) })()) }, @@ -335,7 +344,7 @@ fn wire__crate__api__usearch_api__VectorDb_remove_vector_impl( let api_key = ::sse_decode(&mut deserializer); deserializer.end(); move |context| { - transform_result_sse::<_, Box>((move || { + transform_result_sse::<_, ()>((move || { let mut api_that_guard = None; let decode_indices_ = flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ @@ -350,10 +359,11 @@ fn wire__crate__api__usearch_api__VectorDb_remove_vector_impl( } } let mut api_that_guard = api_that_guard.unwrap(); - let output_ok = crate::api::usearch_api::VectorDB::remove_vector( - &mut *api_that_guard, - api_key, - )?; + let output_ok = + Result::<_, ()>::Ok(crate::api::usearch_api::VectorDB::remove_vector( + &mut *api_that_guard, + api_key, + ))?; Ok(output_ok) })()) } @@ -387,7 +397,7 @@ fn wire__crate__api__usearch_api__VectorDb_reset_index_impl( >>::sse_decode(&mut deserializer); deserializer.end(); move |context| { - transform_result_sse::<_, Box>((move || { + transform_result_sse::<_, ()>((move || { let mut api_that_guard = None; let decode_indices_ = flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ @@ -402,8 +412,9 @@ fn wire__crate__api__usearch_api__VectorDb_reset_index_impl( } } let mut api_that_guard = api_that_guard.unwrap(); - let output_ok = - crate::api::usearch_api::VectorDB::reset_index(&mut *api_that_guard)?; + let output_ok = Result::<_, ()>::Ok({ + crate::api::usearch_api::VectorDB::reset_index(&mut *api_that_guard); + })?; Ok(output_ok) })()) } @@ -439,7 +450,7 @@ fn wire__crate__api__usearch_api__VectorDb_search_vectors_impl( let api_count = ::sse_decode(&mut deserializer); deserializer.end(); move |context| { - transform_result_sse::<_, Box>((move || { + transform_result_sse::<_, ()>((move || { let mut api_that_guard = None; let decode_indices_ = flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ @@ -454,11 +465,12 @@ fn wire__crate__api__usearch_api__VectorDb_search_vectors_impl( } } let api_that_guard = api_that_guard.unwrap(); - let output_ok = crate::api::usearch_api::VectorDB::search_vectors( - &*api_that_guard, - &api_query, - api_count, - )?; + let output_ok = + Result::<_, ()>::Ok(crate::api::usearch_api::VectorDB::search_vectors( + &*api_that_guard, + &api_query, + api_count, + ))?; Ok(output_ok) })()) } @@ -532,38 +544,12 @@ fn wire__crate__api__simple__init_app_impl( // Section: related_funcs -flutter_rust_bridge::frb_generated_moi_arc_impl_value!( - flutter_rust_bridge::for_generated::RustAutoOpaqueInner> -); -flutter_rust_bridge::frb_generated_moi_arc_impl_value!( - flutter_rust_bridge::for_generated::RustAutoOpaqueInner -); flutter_rust_bridge::frb_generated_moi_arc_impl_value!( flutter_rust_bridge::for_generated::RustAutoOpaqueInner ); // Section: dart2rust -impl SseDecode for Box { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - let mut inner = >, - >>::sse_decode(deserializer); - return flutter_rust_bridge::for_generated::rust_auto_opaque_decode_owned(inner); - } -} - -impl SseDecode for Matches { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - let mut inner = , - >>::sse_decode(deserializer); - return flutter_rust_bridge::for_generated::rust_auto_opaque_decode_owned(inner); - } -} - impl SseDecode for VectorDB { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -574,24 +560,6 @@ impl SseDecode for VectorDB { } } -impl SseDecode - for RustOpaqueMoi>> -{ - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - let mut inner = ::sse_decode(deserializer); - return decode_rust_opaque_moi(inner); - } -} - -impl SseDecode for RustOpaqueMoi> { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - let mut inner = ::sse_decode(deserializer); - return decode_rust_opaque_moi(inner); - } -} - impl SseDecode for RustOpaqueMoi> { @@ -665,6 +633,15 @@ impl SseDecode for Vec { } } +impl SseDecode for (Vec, Vec) { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_field0 = >::sse_decode(deserializer); + let mut var_field1 = >::sse_decode(deserializer); + return (var_field0, var_field1); + } +} + impl SseDecode for (usize, usize, usize, usize, usize) { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -795,36 +772,6 @@ fn pde_ffi_dispatcher_sync_impl( // Section: rust2dart -// Codec=Dco (DartCObject based), see doc to use other codecs -impl flutter_rust_bridge::IntoDart for FrbWrapper> { - fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { - flutter_rust_bridge::for_generated::rust_auto_opaque_encode::<_, MoiArc<_>>(self.0) - .into_dart() - } -} -impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for FrbWrapper> {} - -impl flutter_rust_bridge::IntoIntoDart>> for Box { - fn into_into_dart(self) -> FrbWrapper> { - self.into() - } -} - -// Codec=Dco (DartCObject based), see doc to use other codecs -impl flutter_rust_bridge::IntoDart for FrbWrapper { - fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { - flutter_rust_bridge::for_generated::rust_auto_opaque_encode::<_, MoiArc<_>>(self.0) - .into_dart() - } -} -impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for FrbWrapper {} - -impl flutter_rust_bridge::IntoIntoDart> for Matches { - fn into_into_dart(self) -> FrbWrapper { - self.into() - } -} - // Codec=Dco (DartCObject based), see doc to use other codecs impl flutter_rust_bridge::IntoDart for FrbWrapper { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { @@ -840,20 +787,6 @@ impl flutter_rust_bridge::IntoIntoDart> for VectorDB { } } -impl SseEncode for Box { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - >>>::sse_encode(flutter_rust_bridge::for_generated::rust_auto_opaque_encode::<_, MoiArc<_>>(self), serializer); - } -} - -impl SseEncode for Matches { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - >>::sse_encode(flutter_rust_bridge::for_generated::rust_auto_opaque_encode::<_, MoiArc<_>>(self), serializer); - } -} - impl SseEncode for VectorDB { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -861,26 +794,6 @@ impl SseEncode for VectorDB { } } -impl SseEncode - for RustOpaqueMoi>> -{ - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - let (ptr, size) = self.sse_encode_raw(); - ::sse_encode(ptr, serializer); - ::sse_encode(size, serializer); - } -} - -impl SseEncode for RustOpaqueMoi> { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - let (ptr, size) = self.sse_encode_raw(); - ::sse_encode(ptr, serializer); - ::sse_encode(size, serializer); - } -} - impl SseEncode for RustOpaqueMoi> { @@ -946,6 +859,14 @@ impl SseEncode for Vec { } } +impl SseEncode for (Vec, Vec) { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + >::sse_encode(self.0, serializer); + >::sse_encode(self.1, serializer); + } +} + impl SseEncode for (usize, usize, usize, usize, usize) { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -1019,34 +940,6 @@ mod io { flutter_rust_bridge::frb_generated_boilerplate_io!(); - #[unsafe(no_mangle)] - pub extern "C" fn frbgen_photos_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - ptr: *const std::ffi::c_void, - ) { - MoiArc::>>::increment_strong_count(ptr as _); - } - - #[unsafe(no_mangle)] - pub extern "C" fn frbgen_photos_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - ptr: *const std::ffi::c_void, - ) { - MoiArc::>>::decrement_strong_count(ptr as _); - } - - #[unsafe(no_mangle)] - pub extern "C" fn frbgen_photos_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - ptr: *const std::ffi::c_void, - ) { - MoiArc::>::increment_strong_count(ptr as _); - } - - #[unsafe(no_mangle)] - pub extern "C" fn frbgen_photos_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - ptr: *const std::ffi::c_void, - ) { - MoiArc::>::decrement_strong_count(ptr as _); - } - #[unsafe(no_mangle)] pub extern "C" fn frbgen_photos_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( ptr: *const std::ffi::c_void, @@ -1086,34 +979,6 @@ mod web { flutter_rust_bridge::frb_generated_boilerplate_web!(); - #[wasm_bindgen] - pub fn rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - ptr: *const std::ffi::c_void, - ) { - MoiArc::>>::increment_strong_count(ptr as _); - } - - #[wasm_bindgen] - pub fn rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBoxdynError( - ptr: *const std::ffi::c_void, - ) { - MoiArc::>>::decrement_strong_count(ptr as _); - } - - #[wasm_bindgen] - pub fn rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - ptr: *const std::ffi::c_void, - ) { - MoiArc::>::increment_strong_count(ptr as _); - } - - #[wasm_bindgen] - pub fn rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMatches( - ptr: *const std::ffi::c_void, - ) { - MoiArc::>::decrement_strong_count(ptr as _); - } - #[wasm_bindgen] pub fn rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( ptr: *const std::ffi::c_void, From 6cf4530f7d8bdab0724906c64b403eff4b5e02f3 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 9 Apr 2025 10:06:46 +0530 Subject: [PATCH 011/156] Remove reset index --- mobile/lib/src/rust/api/usearch_api.dart | 2 - mobile/lib/src/rust/frb_generated.dart | 49 ++------------ .../debug/ml_debug_section_widget.dart | 1 - mobile/rust/src/api/usearch_api.rs | 5 -- mobile/rust/src/frb_generated.rs | 65 ++----------------- 5 files changed, 8 insertions(+), 114 deletions(-) diff --git a/mobile/lib/src/rust/api/usearch_api.dart b/mobile/lib/src/rust/api/usearch_api.dart index 777d513f9f..ab9c98171d 100644 --- a/mobile/lib/src/rust/api/usearch_api.dart +++ b/mobile/lib/src/rust/api/usearch_api.dart @@ -31,8 +31,6 @@ abstract class VectorDb implements RustOpaqueInterface { Future removeVector({required BigInt key}); - Future resetIndex(); - Future<(Uint64List, Float32List)> searchVectors({ required List query, required BigInt count, diff --git a/mobile/lib/src/rust/frb_generated.dart b/mobile/lib/src/rust/frb_generated.dart index 3d182962f4..84b85ea727 100644 --- a/mobile/lib/src/rust/frb_generated.dart +++ b/mobile/lib/src/rust/frb_generated.dart @@ -70,7 +70,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.9.0'; @override - int get rustContentHash => 862168794; + int get rustContentHash => 846037902; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( @@ -113,8 +113,6 @@ abstract class RustLibApi extends BaseApi { required BigInt key, }); - Future crateApiUsearchApiVectorDbResetIndex({required VectorDb that}); - Future<(Uint64List, Float32List)> crateApiUsearchApiVectorDbSearchVectors({ required VectorDb that, required List query, @@ -398,40 +396,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["that", "key"], ); - @override - Future crateApiUsearchApiVectorDbResetIndex({required VectorDb that}) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - that, - serializer, - ); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 8, - port: port_, - ); - }, - codec: SseCodec( - decodeSuccessData: sse_decode_unit, - decodeErrorData: null, - ), - constMeta: kCrateApiUsearchApiVectorDbResetIndexConstMeta, - argValues: [that], - apiImpl: this, - ), - ); - } - - TaskConstMeta get kCrateApiUsearchApiVectorDbResetIndexConstMeta => - const TaskConstMeta( - debugName: "VectorDb_reset_index", - argNames: ["that"], - ); - @override Future<(Uint64List, Float32List)> crateApiUsearchApiVectorDbSearchVectors({ required VectorDb that, @@ -451,7 +415,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 9, + funcId: 8, port: port_, ); }, @@ -480,7 +444,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(name, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 10)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 9)!; }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -507,7 +471,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 11, + funcId: 10, port: port_, ); }, @@ -1064,11 +1028,6 @@ class VectorDbImpl extends RustOpaque implements VectorDb { Future removeVector({required BigInt key}) => RustLib.instance.api .crateApiUsearchApiVectorDbRemoveVector(that: this, key: key); - Future resetIndex() => - RustLib.instance.api.crateApiUsearchApiVectorDbResetIndex( - that: this, - ); - Future<(Uint64List, Float32List)> searchVectors({ required List query, required BigInt count, diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index 34683b1fda..c311c19237 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -94,7 +94,6 @@ class _MLDebugSectionWidgetState extends State { filePath: indexPath, dimensions: BigInt.from(tenEmbeddings.first.length), ); - await rustVectorDB.resetIndex(); final stats = await rustVectorDB.getIndexStats(); logger.info("vector_db stats: $stats"); await rustVectorDB.bulkAddVectors( diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index b0912cfa75..30e25d064f 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -98,11 +98,6 @@ impl VectorDB { removed_count } - pub fn reset_index(&mut self) { - self.index.reset(); - self.save_index(); - } - pub fn delete_index(self) { if self.path.exists() { std::fs::remove_file(&self.path).expect("Failed to delete index file"); diff --git a/mobile/rust/src/frb_generated.rs b/mobile/rust/src/frb_generated.rs index 3e87600e9d..3d40cf5d38 100644 --- a/mobile/rust/src/frb_generated.rs +++ b/mobile/rust/src/frb_generated.rs @@ -38,7 +38,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.9.0"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 862168794; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 846037902; // Section: executor @@ -370,57 +370,6 @@ fn wire__crate__api__usearch_api__VectorDb_remove_vector_impl( }, ) } -fn wire__crate__api__usearch_api__VectorDb_reset_index_impl( - port_: flutter_rust_bridge::for_generated::MessagePort, - ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, - rust_vec_len_: i32, - data_len_: i32, -) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( - flutter_rust_bridge::for_generated::TaskInfo { - debug_name: "VectorDb_reset_index", - port: Some(port_), - mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, - }, - move || { - let message = unsafe { - flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( - ptr_, - rust_vec_len_, - data_len_, - ) - }; - let mut deserializer = - flutter_rust_bridge::for_generated::SseDeserializer::new(message); - let api_that = , - >>::sse_decode(&mut deserializer); - deserializer.end(); - move |context| { - transform_result_sse::<_, ()>((move || { - let mut api_that_guard = None; - let decode_indices_ = - flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ - flutter_rust_bridge::for_generated::LockableOrderInfo::new( - &api_that, 0, true, - ), - ]); - for i in decode_indices_ { - match i { - 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref_mut()), - _ => unreachable!(), - } - } - let mut api_that_guard = api_that_guard.unwrap(); - let output_ok = Result::<_, ()>::Ok({ - crate::api::usearch_api::VectorDB::reset_index(&mut *api_that_guard); - })?; - Ok(output_ok) - })()) - } - }, - ) -} fn wire__crate__api__usearch_api__VectorDb_search_vectors_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, @@ -739,19 +688,13 @@ fn pde_ffi_dispatcher_primary_impl( rust_vec_len, data_len, ), - 8 => wire__crate__api__usearch_api__VectorDb_reset_index_impl( + 8 => wire__crate__api__usearch_api__VectorDb_search_vectors_impl( port, ptr, rust_vec_len, data_len, ), - 9 => wire__crate__api__usearch_api__VectorDb_search_vectors_impl( - port, - ptr, - rust_vec_len, - data_len, - ), - 11 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), + 10 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -765,7 +708,7 @@ fn pde_ffi_dispatcher_sync_impl( // Codec=Pde (Serialization + dispatch), see doc to use other codecs match func_id { 6 => wire__crate__api__usearch_api__VectorDb_new_impl(ptr, rust_vec_len, data_len), - 10 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), + 9 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), _ => unreachable!(), } } From ee5efbcfcc5e3c345cb6f461c5f9c284218a9460 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 9 Apr 2025 10:43:07 +0530 Subject: [PATCH 012/156] Don't consume index if not needed --- mobile/lib/src/rust/frb_generated.dart | 42 ++-------------- mobile/lib/src/rust/frb_generated.io.dart | 19 ------- mobile/lib/src/rust/frb_generated.web.dart | 19 ------- .../debug/ml_debug_section_widget.dart | 7 +-- mobile/rust/src/api/usearch_api.rs | 8 +-- mobile/rust/src/frb_generated.rs | 50 ++++++++++++------- 6 files changed, 44 insertions(+), 101 deletions(-) diff --git a/mobile/lib/src/rust/frb_generated.dart b/mobile/lib/src/rust/frb_generated.dart index 84b85ea727..2f30bea6b3 100644 --- a/mobile/lib/src/rust/frb_generated.dart +++ b/mobile/lib/src/rust/frb_generated.dart @@ -150,7 +150,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( that, serializer, ); @@ -190,7 +190,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( that, serializer, ); @@ -261,7 +261,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( that, serializer, ); @@ -367,7 +367,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( that, serializer, ); @@ -508,15 +508,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return VectorDbImpl.frbInternalDcoDecode(raw as List); } - @protected - VectorDb - dco_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - dynamic raw, - ) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return VectorDbImpl.frbInternalDcoDecode(raw as List); - } - @protected VectorDb dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -648,18 +639,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - @protected - VectorDb - sse_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - SseDeserializer deserializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - return VectorDbImpl.frbInternalSseDecode( - sse_decode_usize(deserializer), - sse_decode_i_32(deserializer), - ); - } - @protected VectorDb sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -812,19 +791,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - @protected - void - sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - VectorDb self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize( - (self as VectorDbImpl).frbInternalSseEncode(move: false), - serializer, - ); - } - @protected void sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( diff --git a/mobile/lib/src/rust/frb_generated.io.dart b/mobile/lib/src/rust/frb_generated.io.dart index 3421eab7dd..418d7cdae7 100644 --- a/mobile/lib/src/rust/frb_generated.io.dart +++ b/mobile/lib/src/rust/frb_generated.io.dart @@ -29,12 +29,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { dynamic raw, ); - @protected - VectorDb - dco_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - dynamic raw, - ); - @protected VectorDb dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -96,12 +90,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseDeserializer deserializer, ); - @protected - VectorDb - sse_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - SseDeserializer deserializer, - ); - @protected VectorDb sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -174,13 +162,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseSerializer serializer, ); - @protected - void - sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - VectorDb self, - SseSerializer serializer, - ); - @protected void sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( diff --git a/mobile/lib/src/rust/frb_generated.web.dart b/mobile/lib/src/rust/frb_generated.web.dart index 36d5abfc75..aee8265004 100644 --- a/mobile/lib/src/rust/frb_generated.web.dart +++ b/mobile/lib/src/rust/frb_generated.web.dart @@ -31,12 +31,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { dynamic raw, ); - @protected - VectorDb - dco_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - dynamic raw, - ); - @protected VectorDb dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -98,12 +92,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseDeserializer deserializer, ); - @protected - VectorDb - sse_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - SseDeserializer deserializer, - ); - @protected VectorDb sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -176,13 +164,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseSerializer serializer, ); - @protected - void - sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - VectorDb self, - SseSerializer serializer, - ); - @protected void sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index c311c19237..d9c7ce30a0 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -88,14 +88,15 @@ class _MLDebugSectionWidgetState extends State { .toList(); final tenKeys = Uint64List.fromList(tenVectors.map((e) => e.fileID).toList()); + final embedDimensions = BigInt.from(tenEmbeddings.first.length); final indexPath = (await getApplicationSupportDirectory()).path + "/ml/test/vector_db_index.usearch"; final rustVectorDB = VectorDb( filePath: indexPath, - dimensions: BigInt.from(tenEmbeddings.first.length), + dimensions: embedDimensions, ); - final stats = await rustVectorDB.getIndexStats(); - logger.info("vector_db stats: $stats"); + // final stats = await rustVectorDB.getIndexStats(); + // logger.info("vector_db stats: $stats"); await rustVectorDB.bulkAddVectors( keys: tenKeys, vectors: tenEmbeddings, diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index 30e25d064f..1015374af2 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -60,13 +60,13 @@ impl VectorDB { } } - pub fn add_vector(&mut self, key: u64, vector: &Vec) { + pub fn add_vector(&self, key: u64, vector: &Vec) { self.ensure_capacity(1); self.index.add(key, vector).expect("Failed to add vector"); self.save_index(); } - pub fn bulk_add_vectors(&mut self, keys: Vec, vectors: &Vec>) { + pub fn bulk_add_vectors(&self, keys: Vec, vectors: &Vec>) { self.ensure_capacity(keys.len()); for (key, vector) in keys.iter().zip(vectors.iter()) { self.index @@ -92,7 +92,7 @@ impl VectorDB { vector } - pub fn remove_vector(&mut self, key: u64) -> usize { + pub fn remove_vector(&self, key: u64) -> usize { let removed_count = self.index.remove(key).expect("Failed to remove vector"); self.save_index(); removed_count @@ -106,7 +106,7 @@ impl VectorDB { } } - pub fn get_index_stats(self) -> (usize, usize, usize, usize, usize) { + pub fn get_index_stats(&self) -> (usize, usize, usize, usize, usize) { let size = self.index.size(); let capacity = self.index.capacity(); let dimensions = self.index.dimensions(); diff --git a/mobile/rust/src/frb_generated.rs b/mobile/rust/src/frb_generated.rs index 3d40cf5d38..b3657676a6 100644 --- a/mobile/rust/src/frb_generated.rs +++ b/mobile/rust/src/frb_generated.rs @@ -80,19 +80,19 @@ fn wire__crate__api__usearch_api__VectorDb_add_vector_impl( let decode_indices_ = flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ flutter_rust_bridge::for_generated::LockableOrderInfo::new( - &api_that, 0, true, + &api_that, 0, false, ), ]); for i in decode_indices_ { match i { - 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref_mut()), + 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref()), _ => unreachable!(), } } - let mut api_that_guard = api_that_guard.unwrap(); + let api_that_guard = api_that_guard.unwrap(); let output_ok = Result::<_, ()>::Ok({ crate::api::usearch_api::VectorDB::add_vector( - &mut *api_that_guard, + &*api_that_guard, api_key, &api_vector, ); @@ -137,19 +137,19 @@ fn wire__crate__api__usearch_api__VectorDb_bulk_add_vectors_impl( let decode_indices_ = flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ flutter_rust_bridge::for_generated::LockableOrderInfo::new( - &api_that, 0, true, + &api_that, 0, false, ), ]); for i in decode_indices_ { match i { - 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref_mut()), + 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref()), _ => unreachable!(), } } - let mut api_that_guard = api_that_guard.unwrap(); + let api_that_guard = api_that_guard.unwrap(); let output_ok = Result::<_, ()>::Ok({ crate::api::usearch_api::VectorDB::bulk_add_vectors( - &mut *api_that_guard, + &*api_that_guard, api_keys, &api_vectors, ); @@ -217,12 +217,28 @@ fn wire__crate__api__usearch_api__VectorDb_get_index_stats_impl( }; let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); - let api_that = ::sse_decode(&mut deserializer); + let api_that = , + >>::sse_decode(&mut deserializer); deserializer.end(); move |context| { transform_result_sse::<_, ()>((move || { + let mut api_that_guard = None; + let decode_indices_ = + flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ + flutter_rust_bridge::for_generated::LockableOrderInfo::new( + &api_that, 0, false, + ), + ]); + for i in decode_indices_ { + match i { + 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref()), + _ => unreachable!(), + } + } + let api_that_guard = api_that_guard.unwrap(); let output_ok = Result::<_, ()>::Ok( - crate::api::usearch_api::VectorDB::get_index_stats(api_that), + crate::api::usearch_api::VectorDB::get_index_stats(&*api_that_guard), )?; Ok(output_ok) })()) @@ -349,21 +365,19 @@ fn wire__crate__api__usearch_api__VectorDb_remove_vector_impl( let decode_indices_ = flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ flutter_rust_bridge::for_generated::LockableOrderInfo::new( - &api_that, 0, true, + &api_that, 0, false, ), ]); for i in decode_indices_ { match i { - 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref_mut()), + 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref()), _ => unreachable!(), } } - let mut api_that_guard = api_that_guard.unwrap(); - let output_ok = - Result::<_, ()>::Ok(crate::api::usearch_api::VectorDB::remove_vector( - &mut *api_that_guard, - api_key, - ))?; + let api_that_guard = api_that_guard.unwrap(); + let output_ok = Result::<_, ()>::Ok( + crate::api::usearch_api::VectorDB::remove_vector(&*api_that_guard, api_key), + )?; Ok(output_ok) })()) } From 65e71e3cafac46efa8da80d2652b98fb89e72382 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 9 Apr 2025 10:49:50 +0530 Subject: [PATCH 013/156] Reintroduce reset_index method --- mobile/lib/src/rust/api/usearch_api.dart | 2 + mobile/lib/src/rust/frb_generated.dart | 49 ++++++++++++-- .../debug/ml_debug_section_widget.dart | 5 +- mobile/rust/src/api/usearch_api.rs | 5 ++ mobile/rust/src/frb_generated.rs | 65 +++++++++++++++++-- 5 files changed, 116 insertions(+), 10 deletions(-) diff --git a/mobile/lib/src/rust/api/usearch_api.dart b/mobile/lib/src/rust/api/usearch_api.dart index ab9c98171d..777d513f9f 100644 --- a/mobile/lib/src/rust/api/usearch_api.dart +++ b/mobile/lib/src/rust/api/usearch_api.dart @@ -31,6 +31,8 @@ abstract class VectorDb implements RustOpaqueInterface { Future removeVector({required BigInt key}); + Future resetIndex(); + Future<(Uint64List, Float32List)> searchVectors({ required List query, required BigInt count, diff --git a/mobile/lib/src/rust/frb_generated.dart b/mobile/lib/src/rust/frb_generated.dart index 2f30bea6b3..43a9c90c4b 100644 --- a/mobile/lib/src/rust/frb_generated.dart +++ b/mobile/lib/src/rust/frb_generated.dart @@ -70,7 +70,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.9.0'; @override - int get rustContentHash => 846037902; + int get rustContentHash => 862168794; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( @@ -113,6 +113,8 @@ abstract class RustLibApi extends BaseApi { required BigInt key, }); + Future crateApiUsearchApiVectorDbResetIndex({required VectorDb that}); + Future<(Uint64List, Float32List)> crateApiUsearchApiVectorDbSearchVectors({ required VectorDb that, required List query, @@ -396,6 +398,40 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["that", "key"], ); + @override + Future crateApiUsearchApiVectorDbResetIndex({required VectorDb that}) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, + serializer, + ); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 8, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: null, + ), + constMeta: kCrateApiUsearchApiVectorDbResetIndexConstMeta, + argValues: [that], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiUsearchApiVectorDbResetIndexConstMeta => + const TaskConstMeta( + debugName: "VectorDb_reset_index", + argNames: ["that"], + ); + @override Future<(Uint64List, Float32List)> crateApiUsearchApiVectorDbSearchVectors({ required VectorDb that, @@ -415,7 +451,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 8, + funcId: 9, port: port_, ); }, @@ -444,7 +480,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(name, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 9)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 10)!; }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -471,7 +507,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 10, + funcId: 11, port: port_, ); }, @@ -994,6 +1030,11 @@ class VectorDbImpl extends RustOpaque implements VectorDb { Future removeVector({required BigInt key}) => RustLib.instance.api .crateApiUsearchApiVectorDbRemoveVector(that: this, key: key); + Future resetIndex() => + RustLib.instance.api.crateApiUsearchApiVectorDbResetIndex( + that: this, + ); + Future<(Uint64List, Float32List)> searchVectors({ required List query, required BigInt count, diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index d9c7ce30a0..b461eeb86c 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -95,8 +95,9 @@ class _MLDebugSectionWidgetState extends State { filePath: indexPath, dimensions: embedDimensions, ); - // final stats = await rustVectorDB.getIndexStats(); - // logger.info("vector_db stats: $stats"); + await rustVectorDB.resetIndex(); + final stats = await rustVectorDB.getIndexStats(); + logger.info("vector_db stats: $stats"); await rustVectorDB.bulkAddVectors( keys: tenKeys, vectors: tenEmbeddings, diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index 1015374af2..891e034526 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -98,6 +98,11 @@ impl VectorDB { removed_count } + pub fn reset_index(&self) { + self.index.reset().expect("Failed to reset index"); + self.save_index(); + } + pub fn delete_index(self) { if self.path.exists() { std::fs::remove_file(&self.path).expect("Failed to delete index file"); diff --git a/mobile/rust/src/frb_generated.rs b/mobile/rust/src/frb_generated.rs index b3657676a6..6a8d79612b 100644 --- a/mobile/rust/src/frb_generated.rs +++ b/mobile/rust/src/frb_generated.rs @@ -38,7 +38,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.9.0"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 846037902; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 862168794; // Section: executor @@ -384,6 +384,57 @@ fn wire__crate__api__usearch_api__VectorDb_remove_vector_impl( }, ) } +fn wire__crate__api__usearch_api__VectorDb_reset_index_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "VectorDb_reset_index", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = , + >>::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, ()>((move || { + let mut api_that_guard = None; + let decode_indices_ = + flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ + flutter_rust_bridge::for_generated::LockableOrderInfo::new( + &api_that, 0, false, + ), + ]); + for i in decode_indices_ { + match i { + 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref()), + _ => unreachable!(), + } + } + let api_that_guard = api_that_guard.unwrap(); + let output_ok = Result::<_, ()>::Ok({ + crate::api::usearch_api::VectorDB::reset_index(&*api_that_guard); + })?; + Ok(output_ok) + })()) + } + }, + ) +} fn wire__crate__api__usearch_api__VectorDb_search_vectors_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, @@ -702,13 +753,19 @@ fn pde_ffi_dispatcher_primary_impl( rust_vec_len, data_len, ), - 8 => wire__crate__api__usearch_api__VectorDb_search_vectors_impl( + 8 => wire__crate__api__usearch_api__VectorDb_reset_index_impl( port, ptr, rust_vec_len, data_len, ), - 10 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), + 9 => wire__crate__api__usearch_api__VectorDb_search_vectors_impl( + port, + ptr, + rust_vec_len, + data_len, + ), + 11 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -722,7 +779,7 @@ fn pde_ffi_dispatcher_sync_impl( // Codec=Pde (Serialization + dispatch), see doc to use other codecs match func_id { 6 => wire__crate__api__usearch_api__VectorDb_new_impl(ptr, rust_vec_len, data_len), - 9 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), + 10 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), _ => unreachable!(), } } From 2ed155ab474b60a42df82bb9f58ede4a16652d61 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 9 Apr 2025 13:14:26 +0530 Subject: [PATCH 014/156] ignore trailing commas in generated code --- mobile/flutter_rust_bridge.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mobile/flutter_rust_bridge.yaml b/mobile/flutter_rust_bridge.yaml index e15ed9167d..80ba8ed767 100644 --- a/mobile/flutter_rust_bridge.yaml +++ b/mobile/flutter_rust_bridge.yaml @@ -1,3 +1,6 @@ rust_input: crate::api rust_root: rust/ -dart_output: lib/src/rust \ No newline at end of file +dart_output: lib/src/rust + +dart_preamble: | + // ignore_for_file: require_trailing_commas From 4e5ca3dca6d39e0184c7c39c087c68f2fa212a5c Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 9 Apr 2025 13:43:39 +0530 Subject: [PATCH 015/156] Benchmark face embeddings --- mobile/lib/src/rust/api/simple.dart | 2 + mobile/lib/src/rust/api/usearch_api.dart | 21 +- mobile/lib/src/rust/frb_generated.dart | 703 ++++++++---------- mobile/lib/src/rust/frb_generated.io.dart | 86 +-- mobile/lib/src/rust/frb_generated.web.dart | 112 ++- .../debug/ml_debug_section_widget.dart | 103 +++ mobile/rust/src/api/usearch_api.rs | 13 + mobile/rust/src/frb_generated.rs | 105 ++- 8 files changed, 616 insertions(+), 529 deletions(-) diff --git a/mobile/lib/src/rust/api/simple.dart b/mobile/lib/src/rust/api/simple.dart index 5f6b233313..17c34a531c 100644 --- a/mobile/lib/src/rust/api/simple.dart +++ b/mobile/lib/src/rust/api/simple.dart @@ -1,6 +1,8 @@ // This file is automatically generated, so please do not edit it. // @generated by `flutter_rust_bridge`@ 2.9.0. +// ignore_for_file: require_trailing_commas + // ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; diff --git a/mobile/lib/src/rust/api/usearch_api.dart b/mobile/lib/src/rust/api/usearch_api.dart index 777d513f9f..7e3fa3d780 100644 --- a/mobile/lib/src/rust/api/usearch_api.dart +++ b/mobile/lib/src/rust/api/usearch_api.dart @@ -1,6 +1,8 @@ // This file is automatically generated, so please do not edit it. // @generated by `flutter_rust_bridge`@ 2.9.0. +// ignore_for_file: require_trailing_commas + // ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; @@ -12,10 +14,11 @@ import 'package:photos/src/rust/frb_generated.dart'; abstract class VectorDb implements RustOpaqueInterface { Future addVector({required BigInt key, required List vector}); - Future bulkAddVectors({ - required Uint64List keys, - required List vectors, - }); + Future bulkAddVectors( + {required Uint64List keys, required List vectors}); + + Future> bulkSearchVectors( + {required List queries, required BigInt count}); Future deleteIndex(); @@ -25,16 +28,12 @@ abstract class VectorDb implements RustOpaqueInterface { factory VectorDb({required String filePath, required BigInt dimensions}) => RustLib.instance.api.crateApiUsearchApiVectorDbNew( - filePath: filePath, - dimensions: dimensions, - ); + filePath: filePath, dimensions: dimensions); Future removeVector({required BigInt key}); Future resetIndex(); - Future<(Uint64List, Float32List)> searchVectors({ - required List query, - required BigInt count, - }); + Future<(Uint64List, Float32List)> searchVectors( + {required List query, required BigInt count}); } diff --git a/mobile/lib/src/rust/frb_generated.dart b/mobile/lib/src/rust/frb_generated.dart index 43a9c90c4b..bf3551a6ee 100644 --- a/mobile/lib/src/rust/frb_generated.dart +++ b/mobile/lib/src/rust/frb_generated.dart @@ -1,6 +1,8 @@ // This file is automatically generated, so please do not edit it. // @generated by `flutter_rust_bridge`@ 2.9.0. +// ignore_for_file: require_trailing_commas + // ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field import 'dart:async'; @@ -70,7 +72,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.9.0'; @override - int get rustContentHash => 862168794; + int get rustContentHash => -674813457; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( @@ -81,45 +83,41 @@ class RustLib extends BaseEntrypoint { } abstract class RustLibApi extends BaseApi { - Future crateApiUsearchApiVectorDbAddVector({ - required VectorDb that, - required BigInt key, - required List vector, - }); + Future crateApiUsearchApiVectorDbAddVector( + {required VectorDb that, + required BigInt key, + required List vector}); - Future crateApiUsearchApiVectorDbBulkAddVectors({ - required VectorDb that, - required Uint64List keys, - required List vectors, - }); + Future crateApiUsearchApiVectorDbBulkAddVectors( + {required VectorDb that, + required Uint64List keys, + required List vectors}); + + Future> crateApiUsearchApiVectorDbBulkSearchVectors( + {required VectorDb that, + required List queries, + required BigInt count}); Future crateApiUsearchApiVectorDbDeleteIndex({required VectorDb that}); Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> crateApiUsearchApiVectorDbGetIndexStats({required VectorDb that}); - Future crateApiUsearchApiVectorDbGetVector({ - required VectorDb that, - required BigInt key, - }); + Future crateApiUsearchApiVectorDbGetVector( + {required VectorDb that, required BigInt key}); - VectorDb crateApiUsearchApiVectorDbNew({ - required String filePath, - required BigInt dimensions, - }); + VectorDb crateApiUsearchApiVectorDbNew( + {required String filePath, required BigInt dimensions}); - Future crateApiUsearchApiVectorDbRemoveVector({ - required VectorDb that, - required BigInt key, - }); + Future crateApiUsearchApiVectorDbRemoveVector( + {required VectorDb that, required BigInt key}); Future crateApiUsearchApiVectorDbResetIndex({required VectorDb that}); - Future<(Uint64List, Float32List)> crateApiUsearchApiVectorDbSearchVectors({ - required VectorDb that, - required List query, - required BigInt count, - }); + Future<(Uint64List, Float32List)> crateApiUsearchApiVectorDbSearchVectors( + {required VectorDb that, + required List query, + required BigInt count}); String crateApiSimpleGreet({required String name}); @@ -143,37 +141,28 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { }); @override - Future crateApiUsearchApiVectorDbAddVector({ - required VectorDb that, - required BigInt key, - required List vector, - }) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - that, - serializer, - ); - sse_encode_u_64(key, serializer); - sse_encode_list_prim_f_32_loose(vector, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 1, - port: port_, - ); - }, - codec: SseCodec( - decodeSuccessData: sse_decode_unit, - decodeErrorData: null, - ), - constMeta: kCrateApiUsearchApiVectorDbAddVectorConstMeta, - argValues: [that, key, vector], - apiImpl: this, + Future crateApiUsearchApiVectorDbAddVector( + {required VectorDb that, + required BigInt key, + required List vector}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, serializer); + sse_encode_u_64(key, serializer); + sse_encode_list_prim_f_32_loose(vector, serializer); + pdeCallFfi(generalizedFrbRustBinding, serializer, + funcId: 1, port: port_); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: null, ), - ); + constMeta: kCrateApiUsearchApiVectorDbAddVectorConstMeta, + argValues: [that, key, vector], + apiImpl: this, + )); } TaskConstMeta get kCrateApiUsearchApiVectorDbAddVectorConstMeta => @@ -183,37 +172,28 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); @override - Future crateApiUsearchApiVectorDbBulkAddVectors({ - required VectorDb that, - required Uint64List keys, - required List vectors, - }) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - that, - serializer, - ); - sse_encode_list_prim_u_64_strict(keys, serializer); - sse_encode_list_list_prim_f_32_strict(vectors, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 2, - port: port_, - ); - }, - codec: SseCodec( - decodeSuccessData: sse_decode_unit, - decodeErrorData: null, - ), - constMeta: kCrateApiUsearchApiVectorDbBulkAddVectorsConstMeta, - argValues: [that, keys, vectors], - apiImpl: this, + Future crateApiUsearchApiVectorDbBulkAddVectors( + {required VectorDb that, + required Uint64List keys, + required List vectors}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, serializer); + sse_encode_list_prim_u_64_strict(keys, serializer); + sse_encode_list_list_prim_f_32_strict(vectors, serializer); + pdeCallFfi(generalizedFrbRustBinding, serializer, + funcId: 2, port: port_); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: null, ), - ); + constMeta: kCrateApiUsearchApiVectorDbBulkAddVectorsConstMeta, + argValues: [that, keys, vectors], + apiImpl: this, + )); } TaskConstMeta get kCrateApiUsearchApiVectorDbBulkAddVectorsConstMeta => @@ -223,31 +203,54 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); @override - Future crateApiUsearchApiVectorDbDeleteIndex({required VectorDb that}) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - that, - serializer, - ); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 3, - port: port_, - ); - }, - codec: SseCodec( - decodeSuccessData: sse_decode_unit, - decodeErrorData: null, - ), - constMeta: kCrateApiUsearchApiVectorDbDeleteIndexConstMeta, - argValues: [that], - apiImpl: this, + Future> crateApiUsearchApiVectorDbBulkSearchVectors( + {required VectorDb that, + required List queries, + required BigInt count}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, serializer); + sse_encode_list_list_prim_f_32_strict(queries, serializer); + sse_encode_usize(count, serializer); + pdeCallFfi(generalizedFrbRustBinding, serializer, + funcId: 3, port: port_); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_list_list_prim_u_64_strict, + decodeErrorData: null, ), - ); + constMeta: kCrateApiUsearchApiVectorDbBulkSearchVectorsConstMeta, + argValues: [that, queries, count], + apiImpl: this, + )); + } + + TaskConstMeta get kCrateApiUsearchApiVectorDbBulkSearchVectorsConstMeta => + const TaskConstMeta( + debugName: "VectorDb_bulk_search_vectors", + argNames: ["that", "queries", "count"], + ); + + @override + Future crateApiUsearchApiVectorDbDeleteIndex({required VectorDb that}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, serializer); + pdeCallFfi(generalizedFrbRustBinding, serializer, + funcId: 4, port: port_); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: null, + ), + constMeta: kCrateApiUsearchApiVectorDbDeleteIndexConstMeta, + argValues: [that], + apiImpl: this, + )); } TaskConstMeta get kCrateApiUsearchApiVectorDbDeleteIndexConstMeta => @@ -259,30 +262,22 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @override Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> crateApiUsearchApiVectorDbGetIndexStats({required VectorDb that}) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - that, - serializer, - ); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 4, - port: port_, - ); - }, - codec: SseCodec( - decodeSuccessData: sse_decode_record_usize_usize_usize_usize_usize, - decodeErrorData: null, - ), - constMeta: kCrateApiUsearchApiVectorDbGetIndexStatsConstMeta, - argValues: [that], - apiImpl: this, + return handler.executeNormal(NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, serializer); + pdeCallFfi(generalizedFrbRustBinding, serializer, + funcId: 5, port: port_); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_record_usize_usize_usize_usize_usize, + decodeErrorData: null, ), - ); + constMeta: kCrateApiUsearchApiVectorDbGetIndexStatsConstMeta, + argValues: [that], + apiImpl: this, + )); } TaskConstMeta get kCrateApiUsearchApiVectorDbGetIndexStatsConstMeta => @@ -292,35 +287,25 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); @override - Future crateApiUsearchApiVectorDbGetVector({ - required VectorDb that, - required BigInt key, - }) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - that, - serializer, - ); - sse_encode_u_64(key, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 5, - port: port_, - ); - }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_f_32_strict, - decodeErrorData: null, - ), - constMeta: kCrateApiUsearchApiVectorDbGetVectorConstMeta, - argValues: [that, key], - apiImpl: this, + Future crateApiUsearchApiVectorDbGetVector( + {required VectorDb that, required BigInt key}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, serializer); + sse_encode_u_64(key, serializer); + pdeCallFfi(generalizedFrbRustBinding, serializer, + funcId: 6, port: port_); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_f_32_strict, + decodeErrorData: null, ), - ); + constMeta: kCrateApiUsearchApiVectorDbGetVectorConstMeta, + argValues: [that, key], + apiImpl: this, + )); } TaskConstMeta get kCrateApiUsearchApiVectorDbGetVectorConstMeta => @@ -330,28 +315,24 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); @override - VectorDb crateApiUsearchApiVectorDbNew({ - required String filePath, - required BigInt dimensions, - }) { - return handler.executeSync( - SyncTask( - callFfi: () { - final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_String(filePath, serializer); - sse_encode_usize(dimensions, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 6)!; - }, - codec: SseCodec( - decodeSuccessData: - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB, - decodeErrorData: null, - ), - constMeta: kCrateApiUsearchApiVectorDbNewConstMeta, - argValues: [filePath, dimensions], - apiImpl: this, + VectorDb crateApiUsearchApiVectorDbNew( + {required String filePath, required BigInt dimensions}) { + return handler.executeSync(SyncTask( + callFfi: () { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_String(filePath, serializer); + sse_encode_usize(dimensions, serializer); + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 7)!; + }, + codec: SseCodec( + decodeSuccessData: + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB, + decodeErrorData: null, ), - ); + constMeta: kCrateApiUsearchApiVectorDbNewConstMeta, + argValues: [filePath, dimensions], + apiImpl: this, + )); } TaskConstMeta get kCrateApiUsearchApiVectorDbNewConstMeta => @@ -361,35 +342,25 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); @override - Future crateApiUsearchApiVectorDbRemoveVector({ - required VectorDb that, - required BigInt key, - }) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - that, - serializer, - ); - sse_encode_u_64(key, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 7, - port: port_, - ); - }, - codec: SseCodec( - decodeSuccessData: sse_decode_usize, - decodeErrorData: null, - ), - constMeta: kCrateApiUsearchApiVectorDbRemoveVectorConstMeta, - argValues: [that, key], - apiImpl: this, + Future crateApiUsearchApiVectorDbRemoveVector( + {required VectorDb that, required BigInt key}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, serializer); + sse_encode_u_64(key, serializer); + pdeCallFfi(generalizedFrbRustBinding, serializer, + funcId: 8, port: port_); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_usize, + decodeErrorData: null, ), - ); + constMeta: kCrateApiUsearchApiVectorDbRemoveVectorConstMeta, + argValues: [that, key], + apiImpl: this, + )); } TaskConstMeta get kCrateApiUsearchApiVectorDbRemoveVectorConstMeta => @@ -400,30 +371,22 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @override Future crateApiUsearchApiVectorDbResetIndex({required VectorDb that}) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - that, - serializer, - ); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 8, - port: port_, - ); - }, - codec: SseCodec( - decodeSuccessData: sse_decode_unit, - decodeErrorData: null, - ), - constMeta: kCrateApiUsearchApiVectorDbResetIndexConstMeta, - argValues: [that], - apiImpl: this, + return handler.executeNormal(NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, serializer); + pdeCallFfi(generalizedFrbRustBinding, serializer, + funcId: 9, port: port_); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: null, ), - ); + constMeta: kCrateApiUsearchApiVectorDbResetIndexConstMeta, + argValues: [that], + apiImpl: this, + )); } TaskConstMeta get kCrateApiUsearchApiVectorDbResetIndexConstMeta => @@ -433,38 +396,29 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); @override - Future<(Uint64List, Float32List)> crateApiUsearchApiVectorDbSearchVectors({ - required VectorDb that, - required List query, - required BigInt count, - }) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - that, - serializer, - ); - sse_encode_list_prim_f_32_loose(query, serializer); - sse_encode_usize(count, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 9, - port: port_, - ); - }, - codec: SseCodec( - decodeSuccessData: - sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict, - decodeErrorData: null, - ), - constMeta: kCrateApiUsearchApiVectorDbSearchVectorsConstMeta, - argValues: [that, query, count], - apiImpl: this, + Future<(Uint64List, Float32List)> crateApiUsearchApiVectorDbSearchVectors( + {required VectorDb that, + required List query, + required BigInt count}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, serializer); + sse_encode_list_prim_f_32_loose(query, serializer); + sse_encode_usize(count, serializer); + pdeCallFfi(generalizedFrbRustBinding, serializer, + funcId: 10, port: port_); + }, + codec: SseCodec( + decodeSuccessData: + sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict, + decodeErrorData: null, ), - ); + constMeta: kCrateApiUsearchApiVectorDbSearchVectorsConstMeta, + argValues: [that, query, count], + apiImpl: this, + )); } TaskConstMeta get kCrateApiUsearchApiVectorDbSearchVectorsConstMeta => @@ -475,22 +429,20 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @override String crateApiSimpleGreet({required String name}) { - return handler.executeSync( - SyncTask( - callFfi: () { - final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_String(name, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 10)!; - }, - codec: SseCodec( - decodeSuccessData: sse_decode_String, - decodeErrorData: null, - ), - constMeta: kCrateApiSimpleGreetConstMeta, - argValues: [name], - apiImpl: this, + return handler.executeSync(SyncTask( + callFfi: () { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_String(name, serializer); + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 11)!; + }, + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: null, ), - ); + constMeta: kCrateApiSimpleGreetConstMeta, + argValues: [name], + apiImpl: this, + )); } TaskConstMeta get kCrateApiSimpleGreetConstMeta => const TaskConstMeta( @@ -500,26 +452,20 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @override Future crateApiSimpleInitApp() { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - final serializer = SseSerializer(generalizedFrbRustBinding); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 11, - port: port_, - ); - }, - codec: SseCodec( - decodeSuccessData: sse_decode_unit, - decodeErrorData: null, - ), - constMeta: kCrateApiSimpleInitAppConstMeta, - argValues: [], - apiImpl: this, + return handler.executeNormal(NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + pdeCallFfi(generalizedFrbRustBinding, serializer, + funcId: 12, port: port_); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: null, ), - ); + constMeta: kCrateApiSimpleInitAppConstMeta, + argValues: [], + apiImpl: this, + )); } TaskConstMeta get kCrateApiSimpleInitAppConstMeta => const TaskConstMeta( @@ -538,8 +484,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected VectorDb dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - dynamic raw, - ) { + dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs return VectorDbImpl.frbInternalDcoDecode(raw as List); } @@ -547,8 +492,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected VectorDb dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - dynamic raw, - ) { + dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs return VectorDbImpl.frbInternalDcoDecode(raw as List); } @@ -556,8 +500,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected VectorDb dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - dynamic raw, - ) { + dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs return VectorDbImpl.frbInternalDcoDecode(raw as List); } @@ -582,6 +525,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { .toList(); } + @protected + List dco_decode_list_list_prim_u_64_strict(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return (raw as List) + .map(dco_decode_list_prim_u_64_strict) + .toList(); + } + @protected List dco_decode_list_prim_f_32_loose(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -666,37 +617,28 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected VectorDb sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - SseDeserializer deserializer, - ) { + SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs return VectorDbImpl.frbInternalSseDecode( - sse_decode_usize(deserializer), - sse_decode_i_32(deserializer), - ); + sse_decode_usize(deserializer), sse_decode_i_32(deserializer)); } @protected VectorDb sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - SseDeserializer deserializer, - ) { + SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs return VectorDbImpl.frbInternalSseDecode( - sse_decode_usize(deserializer), - sse_decode_i_32(deserializer), - ); + sse_decode_usize(deserializer), sse_decode_i_32(deserializer)); } @protected VectorDb sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - SseDeserializer deserializer, - ) { + SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs return VectorDbImpl.frbInternalSseDecode( - sse_decode_usize(deserializer), - sse_decode_i_32(deserializer), - ); + sse_decode_usize(deserializer), sse_decode_i_32(deserializer)); } @protected @@ -714,8 +656,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected List sse_decode_list_list_prim_f_32_strict( - SseDeserializer deserializer, - ) { + SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs final len_ = sse_decode_i_32(deserializer); @@ -726,6 +667,19 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return ans_; } + @protected + List sse_decode_list_list_prim_u_64_strict( + SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + final len_ = sse_decode_i_32(deserializer); + final ans_ = []; + for (var idx_ = 0; idx_ < len_; ++idx_) { + ans_.add(sse_decode_list_prim_u_64_strict(deserializer)); + } + return ans_; + } + @protected List sse_decode_list_prim_f_32_loose(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -757,8 +711,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected (Uint64List, Float32List) sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( - SseDeserializer deserializer, - ) { + SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs final var_field0 = sse_decode_list_prim_u_64_strict(deserializer); final var_field1 = sse_decode_list_prim_f_32_strict(deserializer); @@ -768,8 +721,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected (BigInt, BigInt, BigInt, BigInt, BigInt) sse_decode_record_usize_usize_usize_usize_usize( - SseDeserializer deserializer, - ) { + SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs final var_field0 = sse_decode_usize(deserializer); final var_field1 = sse_decode_usize(deserializer); @@ -817,40 +769,28 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - VectorDb self, - SseSerializer serializer, - ) { + VectorDb self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_usize( - (self as VectorDbImpl).frbInternalSseEncode(move: true), - serializer, - ); + (self as VectorDbImpl).frbInternalSseEncode(move: true), serializer); } @protected void sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - VectorDb self, - SseSerializer serializer, - ) { + VectorDb self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_usize( - (self as VectorDbImpl).frbInternalSseEncode(move: false), - serializer, - ); + (self as VectorDbImpl).frbInternalSseEncode(move: false), serializer); } @protected void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - VectorDb self, - SseSerializer serializer, - ) { + VectorDb self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_usize( - (self as VectorDbImpl).frbInternalSseEncode(move: null), - serializer, - ); + (self as VectorDbImpl).frbInternalSseEncode(move: null), serializer); } @protected @@ -867,9 +807,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected void sse_encode_list_list_prim_f_32_strict( - List self, - SseSerializer serializer, - ) { + List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); for (final item in self) { @@ -877,23 +815,28 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + void sse_encode_list_list_prim_u_64_strict( + List self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_i_32(self.length, serializer); + for (final item in self) { + sse_encode_list_prim_u_64_strict(item, serializer); + } + } + @protected void sse_encode_list_prim_f_32_loose( - List self, - SseSerializer serializer, - ) { + List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); serializer.buffer.putFloat32List( - self is Float32List ? self : Float32List.fromList(self), - ); + self is Float32List ? self : Float32List.fromList(self)); } @protected void sse_encode_list_prim_f_32_strict( - Float32List self, - SseSerializer serializer, - ) { + Float32List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); serializer.buffer.putFloat32List(self); @@ -901,9 +844,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected void sse_encode_list_prim_u_64_strict( - Uint64List self, - SseSerializer serializer, - ) { + Uint64List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); serializer.buffer.putUint64List(self); @@ -911,9 +852,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected void sse_encode_list_prim_u_8_strict( - Uint8List self, - SseSerializer serializer, - ) { + Uint8List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); serializer.buffer.putUint8List(self); @@ -921,9 +860,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( - (Uint64List, Float32List) self, - SseSerializer serializer, - ) { + (Uint64List, Float32List) self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_list_prim_u_64_strict(self.$1, serializer); sse_encode_list_prim_f_32_strict(self.$2, serializer); @@ -931,9 +868,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected void sse_encode_record_usize_usize_usize_usize_usize( - (BigInt, BigInt, BigInt, BigInt, BigInt) self, - SseSerializer serializer, - ) { + (BigInt, BigInt, BigInt, BigInt, BigInt) self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_usize(self.$1, serializer); sse_encode_usize(self.$2, serializer); @@ -999,20 +934,17 @@ class VectorDbImpl extends RustOpaque implements VectorDb { Future addVector({required BigInt key, required List vector}) => RustLib.instance.api.crateApiUsearchApiVectorDbAddVector( - that: this, - key: key, - vector: vector, - ); + that: this, key: key, vector: vector); - Future bulkAddVectors({ - required Uint64List keys, - required List vectors, - }) => + Future bulkAddVectors( + {required Uint64List keys, required List vectors}) => RustLib.instance.api.crateApiUsearchApiVectorDbBulkAddVectors( - that: this, - keys: keys, - vectors: vectors, - ); + that: this, keys: keys, vectors: vectors); + + Future> bulkSearchVectors( + {required List queries, required BigInt count}) => + RustLib.instance.api.crateApiUsearchApiVectorDbBulkSearchVectors( + that: this, queries: queries, count: count); Future deleteIndex() => RustLib.instance.api.crateApiUsearchApiVectorDbDeleteIndex( @@ -1035,13 +967,8 @@ class VectorDbImpl extends RustOpaque implements VectorDb { that: this, ); - Future<(Uint64List, Float32List)> searchVectors({ - required List query, - required BigInt count, - }) => + Future<(Uint64List, Float32List)> searchVectors( + {required List query, required BigInt count}) => RustLib.instance.api.crateApiUsearchApiVectorDbSearchVectors( - that: this, - query: query, - count: count, - ); + that: this, query: query, count: count); } diff --git a/mobile/lib/src/rust/frb_generated.io.dart b/mobile/lib/src/rust/frb_generated.io.dart index 418d7cdae7..440796cf32 100644 --- a/mobile/lib/src/rust/frb_generated.io.dart +++ b/mobile/lib/src/rust/frb_generated.io.dart @@ -1,6 +1,8 @@ // This file is automatically generated, so please do not edit it. // @generated by `flutter_rust_bridge`@ 2.9.0. +// ignore_for_file: require_trailing_commas + // ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field import 'dart:async'; @@ -26,20 +28,17 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected VectorDb dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - dynamic raw, - ); + dynamic raw); @protected VectorDb dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - dynamic raw, - ); + dynamic raw); @protected VectorDb dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - dynamic raw, - ); + dynamic raw); @protected String dco_decode_String(dynamic raw); @@ -50,6 +49,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected List dco_decode_list_list_prim_f_32_strict(dynamic raw); + @protected + List dco_decode_list_list_prim_u_64_strict(dynamic raw); + @protected List dco_decode_list_prim_f_32_loose(dynamic raw); @@ -87,20 +89,17 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected VectorDb sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - SseDeserializer deserializer, - ); + SseDeserializer deserializer); @protected VectorDb sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - SseDeserializer deserializer, - ); + SseDeserializer deserializer); @protected VectorDb sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - SseDeserializer deserializer, - ); + SseDeserializer deserializer); @protected String sse_decode_String(SseDeserializer deserializer); @@ -110,8 +109,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected List sse_decode_list_list_prim_f_32_strict( - SseDeserializer deserializer, - ); + SseDeserializer deserializer); + + @protected + List sse_decode_list_list_prim_u_64_strict( + SseDeserializer deserializer); @protected List sse_decode_list_prim_f_32_loose(SseDeserializer deserializer); @@ -128,14 +130,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected (Uint64List, Float32List) sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( - SseDeserializer deserializer, - ); + SseDeserializer deserializer); @protected (BigInt, BigInt, BigInt, BigInt, BigInt) sse_decode_record_usize_usize_usize_usize_usize( - SseDeserializer deserializer, - ); + SseDeserializer deserializer); @protected BigInt sse_decode_u_64(SseDeserializer deserializer); @@ -158,23 +158,17 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - VectorDb self, - SseSerializer serializer, - ); + VectorDb self, SseSerializer serializer); @protected void sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - VectorDb self, - SseSerializer serializer, - ); + VectorDb self, SseSerializer serializer); @protected void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - VectorDb self, - SseSerializer serializer, - ); + VectorDb self, SseSerializer serializer); @protected void sse_encode_String(String self, SseSerializer serializer); @@ -184,45 +178,35 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_list_list_prim_f_32_strict( - List self, - SseSerializer serializer, - ); + List self, SseSerializer serializer); + + @protected + void sse_encode_list_list_prim_u_64_strict( + List self, SseSerializer serializer); @protected void sse_encode_list_prim_f_32_loose( - List self, - SseSerializer serializer, - ); + List self, SseSerializer serializer); @protected void sse_encode_list_prim_f_32_strict( - Float32List self, - SseSerializer serializer, - ); + Float32List self, SseSerializer serializer); @protected void sse_encode_list_prim_u_64_strict( - Uint64List self, - SseSerializer serializer, - ); + Uint64List self, SseSerializer serializer); @protected void sse_encode_list_prim_u_8_strict( - Uint8List self, - SseSerializer serializer, - ); + Uint8List self, SseSerializer serializer); @protected void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( - (Uint64List, Float32List) self, - SseSerializer serializer, - ); + (Uint64List, Float32List) self, SseSerializer serializer); @protected void sse_encode_record_usize_usize_usize_usize_usize( - (BigInt, BigInt, BigInt, BigInt, BigInt) self, - SseSerializer serializer, - ); + (BigInt, BigInt, BigInt, BigInt, BigInt) self, SseSerializer serializer); @protected void sse_encode_u_64(BigInt self, SseSerializer serializer); @@ -268,8 +252,7 @@ class RustLibWire implements BaseWire { late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDBPtr = _lookup)>>( - 'frbgen_photos_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB', - ); + 'frbgen_photos_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB'); late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB = _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDBPtr .asFunction)>(); @@ -285,8 +268,7 @@ class RustLibWire implements BaseWire { late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDBPtr = _lookup)>>( - 'frbgen_photos_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB', - ); + 'frbgen_photos_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB'); late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB = _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDBPtr .asFunction)>(); diff --git a/mobile/lib/src/rust/frb_generated.web.dart b/mobile/lib/src/rust/frb_generated.web.dart index aee8265004..e3f81919d0 100644 --- a/mobile/lib/src/rust/frb_generated.web.dart +++ b/mobile/lib/src/rust/frb_generated.web.dart @@ -1,6 +1,8 @@ // This file is automatically generated, so please do not edit it. // @generated by `flutter_rust_bridge`@ 2.9.0. +// ignore_for_file: require_trailing_commas + // ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field // Static analysis wrongly picks the IO variant, thus ignore this @@ -28,20 +30,17 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected VectorDb dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - dynamic raw, - ); + dynamic raw); @protected VectorDb dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - dynamic raw, - ); + dynamic raw); @protected VectorDb dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - dynamic raw, - ); + dynamic raw); @protected String dco_decode_String(dynamic raw); @@ -52,6 +51,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected List dco_decode_list_list_prim_f_32_strict(dynamic raw); + @protected + List dco_decode_list_list_prim_u_64_strict(dynamic raw); + @protected List dco_decode_list_prim_f_32_loose(dynamic raw); @@ -89,20 +91,17 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected VectorDb sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - SseDeserializer deserializer, - ); + SseDeserializer deserializer); @protected VectorDb sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - SseDeserializer deserializer, - ); + SseDeserializer deserializer); @protected VectorDb sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - SseDeserializer deserializer, - ); + SseDeserializer deserializer); @protected String sse_decode_String(SseDeserializer deserializer); @@ -112,8 +111,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected List sse_decode_list_list_prim_f_32_strict( - SseDeserializer deserializer, - ); + SseDeserializer deserializer); + + @protected + List sse_decode_list_list_prim_u_64_strict( + SseDeserializer deserializer); @protected List sse_decode_list_prim_f_32_loose(SseDeserializer deserializer); @@ -130,14 +132,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected (Uint64List, Float32List) sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( - SseDeserializer deserializer, - ); + SseDeserializer deserializer); @protected (BigInt, BigInt, BigInt, BigInt, BigInt) sse_decode_record_usize_usize_usize_usize_usize( - SseDeserializer deserializer, - ); + SseDeserializer deserializer); @protected BigInt sse_decode_u_64(SseDeserializer deserializer); @@ -160,23 +160,17 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - VectorDb self, - SseSerializer serializer, - ); + VectorDb self, SseSerializer serializer); @protected void sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - VectorDb self, - SseSerializer serializer, - ); + VectorDb self, SseSerializer serializer); @protected void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - VectorDb self, - SseSerializer serializer, - ); + VectorDb self, SseSerializer serializer); @protected void sse_encode_String(String self, SseSerializer serializer); @@ -186,45 +180,35 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_list_list_prim_f_32_strict( - List self, - SseSerializer serializer, - ); + List self, SseSerializer serializer); + + @protected + void sse_encode_list_list_prim_u_64_strict( + List self, SseSerializer serializer); @protected void sse_encode_list_prim_f_32_loose( - List self, - SseSerializer serializer, - ); + List self, SseSerializer serializer); @protected void sse_encode_list_prim_f_32_strict( - Float32List self, - SseSerializer serializer, - ); + Float32List self, SseSerializer serializer); @protected void sse_encode_list_prim_u_64_strict( - Uint64List self, - SseSerializer serializer, - ); + Uint64List self, SseSerializer serializer); @protected void sse_encode_list_prim_u_8_strict( - Uint8List self, - SseSerializer serializer, - ); + Uint8List self, SseSerializer serializer); @protected void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( - (Uint64List, Float32List) self, - SseSerializer serializer, - ); + (Uint64List, Float32List) self, SseSerializer serializer); @protected void sse_encode_record_usize_usize_usize_usize_usize( - (BigInt, BigInt, BigInt, BigInt, BigInt) self, - SseSerializer serializer, - ); + (BigInt, BigInt, BigInt, BigInt, BigInt) self, SseSerializer serializer); @protected void sse_encode_u_64(BigInt self, SseSerializer serializer); @@ -250,23 +234,17 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { class RustLibWire implements BaseWire { RustLibWire.fromExternalLibrary(ExternalLibrary lib); - void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - int ptr, - ) => - wasmModule - .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - ptr, - ); + void rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + int ptr) => + wasmModule + .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + ptr); - void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - int ptr, - ) => - wasmModule - .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - ptr, - ); + void rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + int ptr) => + wasmModule + .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + ptr); } @JS('wasm_bindgen') @@ -277,11 +255,9 @@ external RustLibWasmModule get wasmModule; extension type RustLibWasmModule._(JSObject _) implements JSObject { external void rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - int ptr, - ); + int ptr); external void rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - int ptr, - ); + int ptr); } diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index b461eeb86c..f6e82be513 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -1,13 +1,17 @@ import "dart:async"; import "dart:typed_data" show Float32List; +import "package:flutter/foundation.dart" show kDebugMode; import 'package:flutter/material.dart'; import "package:flutter_rust_bridge/flutter_rust_bridge.dart"; import "package:logging/logging.dart"; +import "package:ml_linalg/linalg.dart"; import "package:path_provider/path_provider.dart"; import "package:photos/core/event_bus.dart"; import "package:photos/db/ml/db.dart"; import "package:photos/events/people_changed_event.dart"; +import "package:photos/extensions/stop_watch.dart"; +import "package:photos/generated/protos/ente/common/vector.pb.dart"; import "package:photos/models/ml/face/person.dart"; import "package:photos/service_locator.dart"; import "package:photos/services/machine_learning/face_ml/person/person_service.dart"; @@ -119,6 +123,105 @@ class _MLDebugSectionWidgetState extends State { }, ), sectionOptionSpacing, + MenuItemWidget( + captionedTextWidget: const CaptionedTextWidget( + title: "Benchmark Vector DB Face", + ), + pressedColor: getEnteColorScheme(context).fillFaint, + trailingIcon: Icons.chevron_right_outlined, + trailingIconIsMuted: true, + onTap: () async { + try { + final w = (kDebugMode ? EnteWatch('MLDebugSectionWidget') : null) + ?..start(); + final persons = await PersonService.instance.getPersons(); + w?.log('get all persons for ${persons.length} persons'); + String laurensID = ''; + for (final person in persons) { + if (person.data.name.toLowerCase().contains('laurens')) { + laurensID = person.remoteID; + } + } + if (laurensID.isEmpty) { + throw Exception('Laurens not found'); + } + final laurensFaceIDs = + await MLDataDB.instance.getFaceIDsForPerson(laurensID); + w?.log( + 'getting all face ids for laurens (${laurensFaceIDs.length} faces)', + ); + final laurensFaceIdToEmbeddingData = await MLDataDB.instance + .getFaceEmbeddingMapForFaces(laurensFaceIDs); + + // Fill the vector DB with all embeddings + final laurensFaceIdToFloat32 = laurensFaceIdToEmbeddingData.map( + (key, value) => MapEntry( + key, + Float32List.fromList(EVector.fromBuffer(value).values), + ), + ); + final keys = Uint64List.fromList( + List.generate( + laurensFaceIdToFloat32.length, + (index) => BigInt.from(index + 1), + ), + ); + final vectorDB = VectorDb( + filePath: (await getApplicationSupportDirectory()).path + + "/ml/test/vector_db_face_index.usearch", + dimensions: BigInt.from( + laurensFaceIdToFloat32.values.first.length, + ), + ); + await vectorDB.resetIndex(); + await vectorDB.bulkAddVectors( + keys: keys, + vectors: laurensFaceIdToFloat32.values.toList(), + ); + + // Benchmarking the vector DB + final queries = laurensFaceIdToFloat32.values.toList(); + final count = BigInt.from(10); + w?.reset(); + final results = await vectorDB.bulkSearchVectors( + queries: queries, + count: count, + ); + + w?.log( + 'Done with ${queries.length * queries.length} (${queries.length} x ${queries.length}}) embeddings comparisons in vector DB', + ); + logger.info( + 'vector db results: ${results.length} results, first: ${results.first}, hundredth: ${results[99]}', + ); + + // Benchmarking our own vector comparisons + final laurensFaceIdToEmbeddingVectors = + laurensFaceIdToEmbeddingData.map( + (key, value) => MapEntry( + key, + Vector.fromList(EVector.fromBuffer(value).values), + ), + ); + final faceVectors = laurensFaceIdToEmbeddingVectors.values; + w?.reset(); + for (final faceVector in faceVectors) { + for (final otherFaceVector in faceVectors) { + final _ = 1 - faceVector.dot(otherFaceVector); + } + } + + w?.log( + 'Done with ${faceVectors.length * faceVectors.length} (${faceVectors.length} x ${faceVectors.length}}) embeddings comparisons in own method', + ); + } catch (e, s) { + logger.warning('vector DB search failed ', e, s); + + await showGenericErrorDialog(context: context, error: e); + } + }, + ), + sectionOptionSpacing, MenuItemWidget( captionedTextWidget: const CaptionedTextWidget( title: "Test rust bridge", diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index 891e034526..7deb4b421c 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -84,6 +84,19 @@ impl VectorDB { (matches.keys, matches.distances) } + pub fn bulk_search_vectors(&self, queries: &Vec>, count: usize) -> Vec> { + let mut keys = Vec::new(); + + for query in queries { + let matches = self + .index + .search(query, count) + .expect("Failed to search vectors"); + keys.push(matches.keys); + } + keys + } + pub fn get_vector(&self, key: u64) -> Vec { let mut vector: Vec = vec![0.0; self.index.dimensions()]; self.index diff --git a/mobile/rust/src/frb_generated.rs b/mobile/rust/src/frb_generated.rs index 6a8d79612b..d27f77f862 100644 --- a/mobile/rust/src/frb_generated.rs +++ b/mobile/rust/src/frb_generated.rs @@ -38,7 +38,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.9.0"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 862168794; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -674813457; // Section: executor @@ -160,6 +160,63 @@ fn wire__crate__api__usearch_api__VectorDb_bulk_add_vectors_impl( }, ) } +fn wire__crate__api__usearch_api__VectorDb_bulk_search_vectors_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "VectorDb_bulk_search_vectors", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = , + >>::sse_decode(&mut deserializer); + let api_queries = >>::sse_decode(&mut deserializer); + let api_count = ::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, ()>((move || { + let mut api_that_guard = None; + let decode_indices_ = + flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ + flutter_rust_bridge::for_generated::LockableOrderInfo::new( + &api_that, 0, false, + ), + ]); + for i in decode_indices_ { + match i { + 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref()), + _ => unreachable!(), + } + } + let api_that_guard = api_that_guard.unwrap(); + let output_ok = Result::<_, ()>::Ok( + crate::api::usearch_api::VectorDB::bulk_search_vectors( + &*api_that_guard, + &api_queries, + api_count, + ), + )?; + Ok(output_ok) + })()) + } + }, + ) +} fn wire__crate__api__usearch_api__VectorDb_delete_index_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, @@ -611,6 +668,18 @@ impl SseDecode for Vec> { } } +impl SseDecode for Vec> { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut len_ = ::sse_decode(deserializer); + let mut ans_ = vec![]; + for idx_ in 0..len_ { + ans_.push(>::sse_decode(deserializer)); + } + return ans_; + } +} + impl SseDecode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -729,43 +798,49 @@ fn pde_ffi_dispatcher_primary_impl( rust_vec_len, data_len, ), - 3 => wire__crate__api__usearch_api__VectorDb_delete_index_impl( + 3 => wire__crate__api__usearch_api__VectorDb_bulk_search_vectors_impl( port, ptr, rust_vec_len, data_len, ), - 4 => wire__crate__api__usearch_api__VectorDb_get_index_stats_impl( + 4 => wire__crate__api__usearch_api__VectorDb_delete_index_impl( port, ptr, rust_vec_len, data_len, ), - 5 => wire__crate__api__usearch_api__VectorDb_get_vector_impl( + 5 => wire__crate__api__usearch_api__VectorDb_get_index_stats_impl( port, ptr, rust_vec_len, data_len, ), - 7 => wire__crate__api__usearch_api__VectorDb_remove_vector_impl( + 6 => wire__crate__api__usearch_api__VectorDb_get_vector_impl( port, ptr, rust_vec_len, data_len, ), - 8 => wire__crate__api__usearch_api__VectorDb_reset_index_impl( + 8 => wire__crate__api__usearch_api__VectorDb_remove_vector_impl( port, ptr, rust_vec_len, data_len, ), - 9 => wire__crate__api__usearch_api__VectorDb_search_vectors_impl( + 9 => wire__crate__api__usearch_api__VectorDb_reset_index_impl( port, ptr, rust_vec_len, data_len, ), - 11 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), + 10 => wire__crate__api__usearch_api__VectorDb_search_vectors_impl( + port, + ptr, + rust_vec_len, + data_len, + ), + 12 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -778,8 +853,8 @@ fn pde_ffi_dispatcher_sync_impl( ) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { // Codec=Pde (Serialization + dispatch), see doc to use other codecs match func_id { - 6 => wire__crate__api__usearch_api__VectorDb_new_impl(ptr, rust_vec_len, data_len), - 10 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), + 7 => wire__crate__api__usearch_api__VectorDb_new_impl(ptr, rust_vec_len, data_len), + 11 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -843,6 +918,16 @@ impl SseEncode for Vec> { } } +impl SseEncode for Vec> { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.len() as _, serializer); + for item in self { + >::sse_encode(item, serializer); + } + } +} + impl SseEncode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { From 4ce24e080afd3438dc65b54b0d9e3f40990d724e Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 9 Apr 2025 14:22:39 +0530 Subject: [PATCH 016/156] logging of benchmarking --- .../debug/ml_debug_section_widget.dart | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index f6e82be513..df1b6cf8e7 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -214,6 +214,98 @@ class _MLDebugSectionWidgetState extends State { w?.log( 'Done with ${faceVectors.length * faceVectors.length} (${faceVectors.length} x ${faceVectors.length}}) embeddings comparisons in own method', ); + await vectorDB.deleteIndex(); + } catch (e, s) { + logger.warning('vector DB search failed ', e, s); + + await showGenericErrorDialog(context: context, error: e); + } + }, + ), + sectionOptionSpacing, + MenuItemWidget( + captionedTextWidget: const CaptionedTextWidget( + title: "Benchmark Vector DB CLIP", + ), + pressedColor: getEnteColorScheme(context).fillFaint, + trailingIcon: Icons.chevron_right_outlined, + trailingIconIsMuted: true, + onTap: () async { + try { + final w = (kDebugMode ? EnteWatch('MLDebugSectionWidget') : null) + ?..start(); + final clipEmbeddings = await mlDataDB.getAllClipVectors(); + w?.log( + 'getting all clip embeddings (${clipEmbeddings.length} embeddings)', + ); + + // Fill the vector DB with all embeddings + final clipFloat32 = clipEmbeddings + .map( + (value) => Float32List.fromList(value.vector.toList()), + ) + .toList(); + final keys = Uint64List.fromList( + List.generate( + clipFloat32.length, + (index) => BigInt.from(index + 1), + ), + ); + final vectorDB = VectorDb( + filePath: (await getApplicationSupportDirectory()).path + + "/ml/test/vector_db_clip_index.usearch", + dimensions: BigInt.from( + clipFloat32.first.length, + ), + ); + await vectorDB.resetIndex(); + await vectorDB.bulkAddVectors( + keys: keys, + vectors: clipFloat32, + ); + + // Benchmarking the vector DB + final count = BigInt.from(10); + w?.reset(); + final results = await vectorDB.bulkSearchVectors( + queries: clipFloat32, + count: count, + ); + + w?.log( + 'Done with ${clipFloat32.length * clipFloat32.length} (${clipFloat32.length} x ${clipFloat32.length}}) embeddings comparisons in vector DB', + ); + logger.info( + 'vector db results: ${results.length} results, first: ${results.first}, hundredth: ${results[99]}', + ); + + // Benchmarking our own vector comparisons + final clipVectors = clipEmbeddings + .map( + (value) => value.vector, + ) + .toList(); + w?.reset(); + int compared = 0; + int ms = DateTime.now().millisecondsSinceEpoch; + for (final faceVector in clipVectors) { + for (final otherFaceVector in clipVectors) { + final _ = 1 - faceVector.dot(otherFaceVector); + } + compared++; + if (compared % 100 == 0) { + final now = DateTime.now().millisecondsSinceEpoch; + logger.info( + 'Compared next 100 in ${now - ms} ms, progress: ($compared / ${clipVectors.length})', + ); + ms = now; + } + } + + w?.log( + 'Done with ${clipVectors.length * clipVectors.length} (${clipVectors.length} x ${clipVectors.length}}) embeddings comparisons in own method', + ); + await vectorDB.deleteIndex(); } catch (e, s) { logger.warning('vector DB search failed ', e, s); From 77e2bb1d46d9f1662a654ffbe9425adaa3980280 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 9 Apr 2025 15:21:20 +0530 Subject: [PATCH 017/156] Stop our own vector comparisons in benchmark --- .../debug/ml_debug_section_widget.dart | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index df1b6cf8e7..acf4690af4 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -279,32 +279,32 @@ class _MLDebugSectionWidgetState extends State { 'vector db results: ${results.length} results, first: ${results.first}, hundredth: ${results[99]}', ); - // Benchmarking our own vector comparisons - final clipVectors = clipEmbeddings - .map( - (value) => value.vector, - ) - .toList(); - w?.reset(); - int compared = 0; - int ms = DateTime.now().millisecondsSinceEpoch; - for (final faceVector in clipVectors) { - for (final otherFaceVector in clipVectors) { - final _ = 1 - faceVector.dot(otherFaceVector); - } - compared++; - if (compared % 100 == 0) { - final now = DateTime.now().millisecondsSinceEpoch; - logger.info( - 'Compared next 100 in ${now - ms} ms, progress: ($compared / ${clipVectors.length})', - ); - ms = now; - } - } + // // Benchmarking our own vector comparisons + // final clipVectors = clipEmbeddings + // .map( + // (value) => value.vector, + // ) + // .toList(); + // w?.reset(); + // int compared = 0; + // int ms = DateTime.now().millisecondsSinceEpoch; + // for (final faceVector in clipVectors) { + // for (final otherFaceVector in clipVectors) { + // final _ = 1 - faceVector.dot(otherFaceVector); + // } + // compared++; + // if (compared % 100 == 0) { + // final now = DateTime.now().millisecondsSinceEpoch; + // logger.info( + // 'Compared next 100 in ${now - ms} ms, progress: ($compared / ${clipVectors.length})', + // ); + // ms = now; + // } + // } + // w?.log( + // 'Done with ${clipVectors.length * clipVectors.length} (${clipVectors.length} x ${clipVectors.length}}) embeddings comparisons in own method', + // ); - w?.log( - 'Done with ${clipVectors.length * clipVectors.length} (${clipVectors.length} x ${clipVectors.length}}) embeddings comparisons in own method', - ); await vectorDB.deleteIndex(); } catch (e, s) { logger.warning('vector DB search failed ', e, s); From 8b489e9ced804ac9ac1a4bcf705ed53b3b995bdc Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 9 Apr 2025 15:31:03 +0530 Subject: [PATCH 018/156] Give distances in bulk search --- mobile/lib/src/rust/api/usearch_api.dart | 2 +- mobile/lib/src/rust/frb_generated.dart | 56 +++++++++++++++---- mobile/lib/src/rust/frb_generated.io.dart | 14 +++++ mobile/lib/src/rust/frb_generated.web.dart | 14 +++++ .../debug/ml_debug_section_widget.dart | 8 +-- mobile/rust/src/api/usearch_api.rs | 13 ++--- mobile/rust/src/frb_generated.rs | 17 ++++++ 7 files changed, 102 insertions(+), 22 deletions(-) diff --git a/mobile/lib/src/rust/api/usearch_api.dart b/mobile/lib/src/rust/api/usearch_api.dart index 7e3fa3d780..7630098f59 100644 --- a/mobile/lib/src/rust/api/usearch_api.dart +++ b/mobile/lib/src/rust/api/usearch_api.dart @@ -17,7 +17,7 @@ abstract class VectorDb implements RustOpaqueInterface { Future bulkAddVectors( {required Uint64List keys, required List vectors}); - Future> bulkSearchVectors( + Future<(List, List)> bulkSearchVectors( {required List queries, required BigInt count}); Future deleteIndex(); diff --git a/mobile/lib/src/rust/frb_generated.dart b/mobile/lib/src/rust/frb_generated.dart index bf3551a6ee..4e2ee761ca 100644 --- a/mobile/lib/src/rust/frb_generated.dart +++ b/mobile/lib/src/rust/frb_generated.dart @@ -93,10 +93,11 @@ abstract class RustLibApi extends BaseApi { required Uint64List keys, required List vectors}); - Future> crateApiUsearchApiVectorDbBulkSearchVectors( - {required VectorDb that, - required List queries, - required BigInt count}); + Future<(List, List)> + crateApiUsearchApiVectorDbBulkSearchVectors( + {required VectorDb that, + required List queries, + required BigInt count}); Future crateApiUsearchApiVectorDbDeleteIndex({required VectorDb that}); @@ -203,10 +204,11 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); @override - Future> crateApiUsearchApiVectorDbBulkSearchVectors( - {required VectorDb that, - required List queries, - required BigInt count}) { + Future<(List, List)> + crateApiUsearchApiVectorDbBulkSearchVectors( + {required VectorDb that, + required List queries, + required BigInt count}) { return handler.executeNormal(NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); @@ -218,7 +220,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { funcId: 3, port: port_); }, codec: SseCodec( - decodeSuccessData: sse_decode_list_list_prim_u_64_strict, + decodeSuccessData: + sse_decode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict, decodeErrorData: null, ), constMeta: kCrateApiUsearchApiVectorDbBulkSearchVectorsConstMeta, @@ -557,6 +560,21 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw as Uint8List; } + @protected + (List, List) + dco_decode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict( + dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 2) { + throw Exception('Expected 2 elements, got ${arr.length}'); + } + return ( + dco_decode_list_list_prim_u_64_strict(arr[0]), + dco_decode_list_list_prim_f_32_strict(arr[1]), + ); + } + @protected ( Uint64List, @@ -708,6 +726,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return deserializer.buffer.getUint8List(len_); } + @protected + (List, List) + sse_decode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict( + SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + final var_field0 = sse_decode_list_list_prim_u_64_strict(deserializer); + final var_field1 = sse_decode_list_list_prim_f_32_strict(deserializer); + return (var_field0, var_field1); + } + @protected (Uint64List, Float32List) sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( @@ -858,6 +886,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { serializer.buffer.putUint8List(self); } + @protected + void sse_encode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict( + (List, List) self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_list_list_prim_u_64_strict(self.$1, serializer); + sse_encode_list_list_prim_f_32_strict(self.$2, serializer); + } + @protected void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( (Uint64List, Float32List) self, SseSerializer serializer) { @@ -941,7 +977,7 @@ class VectorDbImpl extends RustOpaque implements VectorDb { RustLib.instance.api.crateApiUsearchApiVectorDbBulkAddVectors( that: this, keys: keys, vectors: vectors); - Future> bulkSearchVectors( + Future<(List, List)> bulkSearchVectors( {required List queries, required BigInt count}) => RustLib.instance.api.crateApiUsearchApiVectorDbBulkSearchVectors( that: this, queries: queries, count: count); diff --git a/mobile/lib/src/rust/frb_generated.io.dart b/mobile/lib/src/rust/frb_generated.io.dart index 440796cf32..34a84063ae 100644 --- a/mobile/lib/src/rust/frb_generated.io.dart +++ b/mobile/lib/src/rust/frb_generated.io.dart @@ -64,6 +64,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); + @protected + (List, List) + dco_decode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict( + dynamic raw); + @protected ( Uint64List, @@ -127,6 +132,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); + @protected + (List, List) + sse_decode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict( + SseDeserializer deserializer); + @protected (Uint64List, Float32List) sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( @@ -200,6 +210,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_list_prim_u_8_strict( Uint8List self, SseSerializer serializer); + @protected + void sse_encode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict( + (List, List) self, SseSerializer serializer); + @protected void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( (Uint64List, Float32List) self, SseSerializer serializer); diff --git a/mobile/lib/src/rust/frb_generated.web.dart b/mobile/lib/src/rust/frb_generated.web.dart index e3f81919d0..20c7329814 100644 --- a/mobile/lib/src/rust/frb_generated.web.dart +++ b/mobile/lib/src/rust/frb_generated.web.dart @@ -66,6 +66,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); + @protected + (List, List) + dco_decode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict( + dynamic raw); + @protected ( Uint64List, @@ -129,6 +134,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); + @protected + (List, List) + sse_decode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict( + SseDeserializer deserializer); + @protected (Uint64List, Float32List) sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( @@ -202,6 +212,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_list_prim_u_8_strict( Uint8List self, SseSerializer serializer); + @protected + void sse_encode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict( + (List, List) self, SseSerializer serializer); + @protected void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( (Uint64List, Float32List) self, SseSerializer serializer); diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index acf4690af4..421ba6f57e 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -183,7 +183,7 @@ class _MLDebugSectionWidgetState extends State { final queries = laurensFaceIdToFloat32.values.toList(); final count = BigInt.from(10); w?.reset(); - final results = await vectorDB.bulkSearchVectors( + final (vectorKeys, distances) = await vectorDB.bulkSearchVectors( queries: queries, count: count, ); @@ -192,7 +192,7 @@ class _MLDebugSectionWidgetState extends State { 'Done with ${queries.length * queries.length} (${queries.length} x ${queries.length}}) embeddings comparisons in vector DB', ); logger.info( - 'vector db results: ${results.length} results, first: ${results.first}, hundredth: ${results[99]}', + 'vector db results: ${vectorKeys.length} results, first: ${vectorKeys.first}, hundredth: ${vectorKeys[99]}', ); // Benchmarking our own vector comparisons @@ -267,7 +267,7 @@ class _MLDebugSectionWidgetState extends State { // Benchmarking the vector DB final count = BigInt.from(10); w?.reset(); - final results = await vectorDB.bulkSearchVectors( + final (vectorKeys, distances) = await vectorDB.bulkSearchVectors( queries: clipFloat32, count: count, ); @@ -276,7 +276,7 @@ class _MLDebugSectionWidgetState extends State { 'Done with ${clipFloat32.length * clipFloat32.length} (${clipFloat32.length} x ${clipFloat32.length}}) embeddings comparisons in vector DB', ); logger.info( - 'vector db results: ${results.length} results, first: ${results.first}, hundredth: ${results[99]}', + 'vector db results: ${vectorKeys.length} results, first: ${vectorKeys.first} with distances ${distances.first}, hundredth: ${vectorKeys[99]} with distances ${distances[99]}', ); // // Benchmarking our own vector comparisons diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index 7deb4b421c..d5d6ce318e 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -84,17 +84,16 @@ impl VectorDB { (matches.keys, matches.distances) } - pub fn bulk_search_vectors(&self, queries: &Vec>, count: usize) -> Vec> { + pub fn bulk_search_vectors(&self, queries: &Vec>, count: usize) -> (Vec>, Vec>) { let mut keys = Vec::new(); + let mut distances = Vec::new(); for query in queries { - let matches = self - .index - .search(query, count) - .expect("Failed to search vectors"); - keys.push(matches.keys); + let (keys_result, distances_result) = self.search_vectors(query, count); + keys.push(keys_result); + distances.push(distances_result); } - keys + (keys, distances) } pub fn get_vector(&self, key: u64) -> Vec { diff --git a/mobile/rust/src/frb_generated.rs b/mobile/rust/src/frb_generated.rs index d27f77f862..8e80d305db 100644 --- a/mobile/rust/src/frb_generated.rs +++ b/mobile/rust/src/frb_generated.rs @@ -716,6 +716,15 @@ impl SseDecode for Vec { } } +impl SseDecode for (Vec>, Vec>) { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_field0 = >>::sse_decode(deserializer); + let mut var_field1 = >>::sse_decode(deserializer); + return (var_field0, var_field1); + } +} + impl SseDecode for (Vec, Vec) { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -958,6 +967,14 @@ impl SseEncode for Vec { } } +impl SseEncode for (Vec>, Vec>) { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + >>::sse_encode(self.0, serializer); + >>::sse_encode(self.1, serializer); + } +} + impl SseEncode for (Vec, Vec) { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { From a07e8477fb37e5daf556026ae588ff8328659c32 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 9 Apr 2025 15:34:06 +0530 Subject: [PATCH 019/156] format --- mobile/rust/src/api/usearch_api.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index d5d6ce318e..107495dbda 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -84,7 +84,11 @@ impl VectorDB { (matches.keys, matches.distances) } - pub fn bulk_search_vectors(&self, queries: &Vec>, count: usize) -> (Vec>, Vec>) { + pub fn bulk_search_vectors( + &self, + queries: &Vec>, + count: usize, + ) -> (Vec>, Vec>) { let mut keys = Vec::new(); let mut distances = Vec::new(); From 8c65a21b86169a1f54497b01a27da939f53dbeb7 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 10 Apr 2025 13:03:52 +0530 Subject: [PATCH 020/156] don't generate for web --- mobile/flutter_rust_bridge.yaml | 2 + mobile/lib/src/rust/frb_generated.web.dart | 277 --------------------- mobile/rust/src/frb_generated.rs | 39 --- 3 files changed, 2 insertions(+), 316 deletions(-) delete mode 100644 mobile/lib/src/rust/frb_generated.web.dart diff --git a/mobile/flutter_rust_bridge.yaml b/mobile/flutter_rust_bridge.yaml index 80ba8ed767..fde31a2f2b 100644 --- a/mobile/flutter_rust_bridge.yaml +++ b/mobile/flutter_rust_bridge.yaml @@ -4,3 +4,5 @@ dart_output: lib/src/rust dart_preamble: | // ignore_for_file: require_trailing_commas + +web: false \ No newline at end of file diff --git a/mobile/lib/src/rust/frb_generated.web.dart b/mobile/lib/src/rust/frb_generated.web.dart deleted file mode 100644 index 20c7329814..0000000000 --- a/mobile/lib/src/rust/frb_generated.web.dart +++ /dev/null @@ -1,277 +0,0 @@ -// This file is automatically generated, so please do not edit it. -// @generated by `flutter_rust_bridge`@ 2.9.0. - -// ignore_for_file: require_trailing_commas - -// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field - -// Static analysis wrongly picks the IO variant, thus ignore this -// ignore_for_file: argument_type_not_assignable - -import 'dart:async'; -import 'dart:convert'; - -import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_web.dart'; -import 'package:photos/src/rust/api/simple.dart'; -import 'package:photos/src/rust/api/usearch_api.dart'; -import 'package:photos/src/rust/frb_generated.dart'; - -abstract class RustLibApiImplPlatform extends BaseApiImpl { - RustLibApiImplPlatform({ - required super.handler, - required super.wire, - required super.generalizedFrbRustBinding, - required super.portManager, - }); - - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_VectorDbPtr => wire - .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB; - - @protected - VectorDb - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - dynamic raw); - - @protected - VectorDb - dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - dynamic raw); - - @protected - VectorDb - dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - dynamic raw); - - @protected - String dco_decode_String(dynamic raw); - - @protected - double dco_decode_f_32(dynamic raw); - - @protected - List dco_decode_list_list_prim_f_32_strict(dynamic raw); - - @protected - List dco_decode_list_list_prim_u_64_strict(dynamic raw); - - @protected - List dco_decode_list_prim_f_32_loose(dynamic raw); - - @protected - Float32List dco_decode_list_prim_f_32_strict(dynamic raw); - - @protected - Uint64List dco_decode_list_prim_u_64_strict(dynamic raw); - - @protected - Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); - - @protected - (List, List) - dco_decode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict( - dynamic raw); - - @protected - ( - Uint64List, - Float32List - ) dco_decode_record_list_prim_u_64_strict_list_prim_f_32_strict(dynamic raw); - - @protected - (BigInt, BigInt, BigInt, BigInt, BigInt) - dco_decode_record_usize_usize_usize_usize_usize(dynamic raw); - - @protected - BigInt dco_decode_u_64(dynamic raw); - - @protected - int dco_decode_u_8(dynamic raw); - - @protected - void dco_decode_unit(dynamic raw); - - @protected - BigInt dco_decode_usize(dynamic raw); - - @protected - VectorDb - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - SseDeserializer deserializer); - - @protected - VectorDb - sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - SseDeserializer deserializer); - - @protected - VectorDb - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - SseDeserializer deserializer); - - @protected - String sse_decode_String(SseDeserializer deserializer); - - @protected - double sse_decode_f_32(SseDeserializer deserializer); - - @protected - List sse_decode_list_list_prim_f_32_strict( - SseDeserializer deserializer); - - @protected - List sse_decode_list_list_prim_u_64_strict( - SseDeserializer deserializer); - - @protected - List sse_decode_list_prim_f_32_loose(SseDeserializer deserializer); - - @protected - Float32List sse_decode_list_prim_f_32_strict(SseDeserializer deserializer); - - @protected - Uint64List sse_decode_list_prim_u_64_strict(SseDeserializer deserializer); - - @protected - Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); - - @protected - (List, List) - sse_decode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict( - SseDeserializer deserializer); - - @protected - (Uint64List, Float32List) - sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( - SseDeserializer deserializer); - - @protected - (BigInt, BigInt, BigInt, BigInt, BigInt) - sse_decode_record_usize_usize_usize_usize_usize( - SseDeserializer deserializer); - - @protected - BigInt sse_decode_u_64(SseDeserializer deserializer); - - @protected - int sse_decode_u_8(SseDeserializer deserializer); - - @protected - void sse_decode_unit(SseDeserializer deserializer); - - @protected - BigInt sse_decode_usize(SseDeserializer deserializer); - - @protected - int sse_decode_i_32(SseDeserializer deserializer); - - @protected - bool sse_decode_bool(SseDeserializer deserializer); - - @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - VectorDb self, SseSerializer serializer); - - @protected - void - sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - VectorDb self, SseSerializer serializer); - - @protected - void - sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - VectorDb self, SseSerializer serializer); - - @protected - void sse_encode_String(String self, SseSerializer serializer); - - @protected - void sse_encode_f_32(double self, SseSerializer serializer); - - @protected - void sse_encode_list_list_prim_f_32_strict( - List self, SseSerializer serializer); - - @protected - void sse_encode_list_list_prim_u_64_strict( - List self, SseSerializer serializer); - - @protected - void sse_encode_list_prim_f_32_loose( - List self, SseSerializer serializer); - - @protected - void sse_encode_list_prim_f_32_strict( - Float32List self, SseSerializer serializer); - - @protected - void sse_encode_list_prim_u_64_strict( - Uint64List self, SseSerializer serializer); - - @protected - void sse_encode_list_prim_u_8_strict( - Uint8List self, SseSerializer serializer); - - @protected - void sse_encode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict( - (List, List) self, SseSerializer serializer); - - @protected - void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( - (Uint64List, Float32List) self, SseSerializer serializer); - - @protected - void sse_encode_record_usize_usize_usize_usize_usize( - (BigInt, BigInt, BigInt, BigInt, BigInt) self, SseSerializer serializer); - - @protected - void sse_encode_u_64(BigInt self, SseSerializer serializer); - - @protected - void sse_encode_u_8(int self, SseSerializer serializer); - - @protected - void sse_encode_unit(void self, SseSerializer serializer); - - @protected - void sse_encode_usize(BigInt self, SseSerializer serializer); - - @protected - void sse_encode_i_32(int self, SseSerializer serializer); - - @protected - void sse_encode_bool(bool self, SseSerializer serializer); -} - -// Section: wire_class - -class RustLibWire implements BaseWire { - RustLibWire.fromExternalLibrary(ExternalLibrary lib); - - void rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - int ptr) => - wasmModule - .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - ptr); - - void rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - int ptr) => - wasmModule - .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - ptr); -} - -@JS('wasm_bindgen') -external RustLibWasmModule get wasmModule; - -@JS() -@anonymous -extension type RustLibWasmModule._(JSObject _) implements JSObject { - external void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - int ptr); - - external void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - int ptr); -} diff --git a/mobile/rust/src/frb_generated.rs b/mobile/rust/src/frb_generated.rs index 8e80d305db..0356753532 100644 --- a/mobile/rust/src/frb_generated.rs +++ b/mobile/rust/src/frb_generated.rs @@ -1072,42 +1072,3 @@ mod io { } #[cfg(not(target_family = "wasm"))] pub use io::*; - -/// cbindgen:ignore -#[cfg(target_family = "wasm")] -mod web { - // This file is automatically generated, so please do not edit it. - // @generated by `flutter_rust_bridge`@ 2.9.0. - - // Section: imports - - use super::*; - use crate::api::usearch_api::*; - use flutter_rust_bridge::for_generated::byteorder::{ - NativeEndian, ReadBytesExt, WriteBytesExt, - }; - use flutter_rust_bridge::for_generated::wasm_bindgen; - use flutter_rust_bridge::for_generated::wasm_bindgen::prelude::*; - use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable}; - use flutter_rust_bridge::{Handler, IntoIntoDart}; - - // Section: boilerplate - - flutter_rust_bridge::frb_generated_boilerplate_web!(); - - #[wasm_bindgen] - pub fn rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - ptr: *const std::ffi::c_void, - ) { - MoiArc::>::increment_strong_count(ptr as _); - } - - #[wasm_bindgen] - pub fn rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( - ptr: *const std::ffi::c_void, - ) { - MoiArc::>::decrement_strong_count(ptr as _); - } -} -#[cfg(target_family = "wasm")] -pub use web::*; From 4d9bfb89ae97c1513ad91d16ab9ed0902480b7a6 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 7 May 2025 10:36:17 +0530 Subject: [PATCH 021/156] macos config --- mobile/ios/Podfile.lock | 2 ++ mobile/ios/Runner.xcodeproj/project.pbxproj | 1 + 2 files changed, 3 insertions(+) diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock index d76dd0c63d..1a356eb084 100644 --- a/mobile/ios/Podfile.lock +++ b/mobile/ios/Podfile.lock @@ -184,6 +184,8 @@ PODS: - PromisesObjC (2.4.0) - receive_sharing_intent (1.8.1): - Flutter + - rust_lib_photos (0.0.1) + - Flutter - SDWebImage (5.21.0): - SDWebImage/Core (= 5.21.0) - SDWebImage/Core (5.21.0) diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index 678d1e3446..b77c01c9ca 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -414,6 +414,7 @@ "${BUILT_PRODUCTS_DIR}/photo_manager/photo_manager.framework", "${BUILT_PRODUCTS_DIR}/privacy_screen/privacy_screen.framework", "${BUILT_PRODUCTS_DIR}/receive_sharing_intent/receive_sharing_intent.framework", + "${BUILT_PRODUCTS_DIR}/rust_lib_photos/rust_lib_photos.framework", "${BUILT_PRODUCTS_DIR}/sentry_flutter/sentry_flutter.framework", "${BUILT_PRODUCTS_DIR}/share_plus/share_plus.framework", "${BUILT_PRODUCTS_DIR}/shared_preferences_foundation/shared_preferences_foundation.framework", From a4afecef3d76bce7ae7ca24b024d6a4d8dc14f7c Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 7 May 2025 10:50:39 +0530 Subject: [PATCH 022/156] Fix ios config --- mobile/ios/Podfile.lock | 2 +- mobile/ios/Runner.xcodeproj/project.pbxproj | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock index 1a356eb084..52ea715498 100644 --- a/mobile/ios/Podfile.lock +++ b/mobile/ios/Podfile.lock @@ -184,7 +184,7 @@ PODS: - PromisesObjC (2.4.0) - receive_sharing_intent (1.8.1): - Flutter - - rust_lib_photos (0.0.1) + - rust_lib_photos (0.0.1): - Flutter - SDWebImage (5.21.0): - SDWebImage/Core (= 5.21.0) diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index b77c01c9ca..4cd76b0faf 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -509,6 +509,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/photo_manager.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/privacy_screen.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/receive_sharing_intent.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/rust_lib_photos.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sentry_flutter.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share_plus.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_foundation.framework", From 7b9d6df2fd131fed34b348e510f95ab6f60db21f Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 7 May 2025 11:32:53 +0530 Subject: [PATCH 023/156] Fix ios build issue --- mobile/ios/Podfile.lock | 4 ++++ mobile/rust_builder/ios/rust_lib_photos.podspec | 2 +- mobile/rust_builder/macos/rust_lib_photos.podspec | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock index 52ea715498..9d91a4c09e 100644 --- a/mobile/ios/Podfile.lock +++ b/mobile/ios/Podfile.lock @@ -286,6 +286,7 @@ DEPENDENCIES: - photo_manager (from `.symlinks/plugins/photo_manager/ios`) - privacy_screen (from `.symlinks/plugins/privacy_screen/ios`) - receive_sharing_intent (from `.symlinks/plugins/receive_sharing_intent/ios`) + - rust_lib_photos (from `.symlinks/plugins/rust_lib_photos/ios`) - sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) @@ -411,6 +412,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/privacy_screen/ios" receive_sharing_intent: :path: ".symlinks/plugins/receive_sharing_intent/ios" + rust_lib_photos: + :path: ".symlinks/plugins/rust_lib_photos/ios" sentry_flutter: :path: ".symlinks/plugins/sentry_flutter/ios" share_plus: @@ -496,6 +499,7 @@ SPEC CHECKSUMS: privacy_screen: 3159a541f5d3a31bea916cfd4e58f9dc722b3fd4 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00 + rust_lib_photos: 04d3901908d2972192944083310b65abf410774c SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868 SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854 diff --git a/mobile/rust_builder/ios/rust_lib_photos.podspec b/mobile/rust_builder/ios/rust_lib_photos.podspec index 2cd1e62b79..16d34d0103 100644 --- a/mobile/rust_builder/ios/rust_lib_photos.podspec +++ b/mobile/rust_builder/ios/rust_lib_photos.podspec @@ -40,6 +40,6 @@ A new Flutter FFI plugin project. 'DEFINES_MODULE' => 'YES', # Flutter.framework does not contain a i386 slice. 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', - 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/librust_lib_photos.a', + 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/librust_lib_photos.a -lc++', } end \ No newline at end of file diff --git a/mobile/rust_builder/macos/rust_lib_photos.podspec b/mobile/rust_builder/macos/rust_lib_photos.podspec index 74ffd45ed4..e929e5848e 100644 --- a/mobile/rust_builder/macos/rust_lib_photos.podspec +++ b/mobile/rust_builder/macos/rust_lib_photos.podspec @@ -39,6 +39,6 @@ A new Flutter FFI plugin project. 'DEFINES_MODULE' => 'YES', # Flutter.framework does not contain a i386 slice. 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', - 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/librust_lib_photos.a', + 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/librust_lib_photos.a -lc++', } end \ No newline at end of file From 603c275c0969b26d88d53e50d321addbaee09fdc Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 7 May 2025 12:01:45 +0530 Subject: [PATCH 024/156] Update basic usearch test --- .../debug/ml_debug_section_widget.dart | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index 421ba6f57e..31fb9eb9af 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -1,4 +1,5 @@ import "dart:async"; +import "dart:math" show Random; import "dart:typed_data" show Float32List; import "package:flutter/foundation.dart" show kDebugMode; @@ -78,20 +79,32 @@ class _MLDebugSectionWidgetState extends State { sectionOptionSpacing, MenuItemWidget( captionedTextWidget: const CaptionedTextWidget( - title: "Do some usearch", + title: "Do some basic usearch", ), pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, onTap: () async { try { - final allImageEmbeddings = await mlDataDB.getAllClipVectors(); - final tenVectors = allImageEmbeddings.sublist(0, 10); - final tenEmbeddings = tenVectors - .map((e) => Float32List.fromList(e.vector.toList())) - .toList(); - final tenKeys = - Uint64List.fromList(tenVectors.map((e) => e.fileID).toList()); + // randomly generate some vectors and keys + final random = Random(); + final tenEmbeddings = List.generate( + 10, + (index) { + final randomList = List.generate( + 192, + (_) => random.nextDouble(), // Values between 0 and 1 + ); + final randomVector = Vector.fromList(randomList).normalize(); + return Float32List.fromList(randomVector.toList()); + }, + ); + final tenKeys = Uint64List.fromList( + List.generate( + tenEmbeddings.length, + (index) => BigInt.from(index + 1), + ), + ); final embedDimensions = BigInt.from(tenEmbeddings.first.length); final indexPath = (await getApplicationSupportDirectory()).path + "/ml/test/vector_db_index.usearch"; From e9c2e40a438d0631e8d9ff89595863302611b52c Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 7 May 2025 13:25:40 +0530 Subject: [PATCH 025/156] Update to latest usearch --- mobile/rust/Cargo.lock | 4 ++-- mobile/rust/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile/rust/Cargo.lock b/mobile/rust/Cargo.lock index c4a221f057..7f4c444b5b 100644 --- a/mobile/rust/Cargo.lock +++ b/mobile/rust/Cargo.lock @@ -670,9 +670,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "usearch" -version = "2.17.6" +version = "2.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2feb45cdbc2d9b39ded81be910b505d062654725a0024ce5a509fef0eaaa3f0e" +checksum = "b64a1f5433c83756330d079ba276476b279728da042057dc23a002f27ca32326" dependencies = [ "cxx", "cxx-build", diff --git a/mobile/rust/Cargo.toml b/mobile/rust/Cargo.toml index 615f48d482..56d45a437c 100644 --- a/mobile/rust/Cargo.toml +++ b/mobile/rust/Cargo.toml @@ -8,7 +8,7 @@ crate-type = ["cdylib", "staticlib"] [dependencies] flutter_rust_bridge = "=2.9.0" -usearch = "2.17.6" +usearch = "2.17.7" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] } From 715c7c23a735433bdad80ae7eb3084a3115ebed7 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 8 May 2025 10:29:25 +0530 Subject: [PATCH 026/156] Add bulk remove embeddings api --- mobile/lib/src/rust/api/usearch_api.dart | 2 + mobile/lib/src/rust/frb_generated.dart | 57 ++++++++++++---- mobile/rust/src/api/usearch_api.rs | 12 ++++ mobile/rust/src/frb_generated.rs | 83 ++++++++++++++++++++---- 4 files changed, 132 insertions(+), 22 deletions(-) diff --git a/mobile/lib/src/rust/api/usearch_api.dart b/mobile/lib/src/rust/api/usearch_api.dart index 7630098f59..8260600b32 100644 --- a/mobile/lib/src/rust/api/usearch_api.dart +++ b/mobile/lib/src/rust/api/usearch_api.dart @@ -17,6 +17,8 @@ abstract class VectorDb implements RustOpaqueInterface { Future bulkAddVectors( {required Uint64List keys, required List vectors}); + Future bulkRemoveVectors({required Uint64List keys}); + Future<(List, List)> bulkSearchVectors( {required List queries, required BigInt count}); diff --git a/mobile/lib/src/rust/frb_generated.dart b/mobile/lib/src/rust/frb_generated.dart index 4e2ee761ca..6a044372fd 100644 --- a/mobile/lib/src/rust/frb_generated.dart +++ b/mobile/lib/src/rust/frb_generated.dart @@ -72,7 +72,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.9.0'; @override - int get rustContentHash => -674813457; + int get rustContentHash => -1131116360; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( @@ -93,6 +93,9 @@ abstract class RustLibApi extends BaseApi { required Uint64List keys, required List vectors}); + Future crateApiUsearchApiVectorDbBulkRemoveVectors( + {required VectorDb that, required Uint64List keys}); + Future<(List, List)> crateApiUsearchApiVectorDbBulkSearchVectors( {required VectorDb that, @@ -203,6 +206,34 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["that", "keys", "vectors"], ); + @override + Future crateApiUsearchApiVectorDbBulkRemoveVectors( + {required VectorDb that, required Uint64List keys}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, serializer); + sse_encode_list_prim_u_64_strict(keys, serializer); + pdeCallFfi(generalizedFrbRustBinding, serializer, + funcId: 3, port: port_); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_usize, + decodeErrorData: null, + ), + constMeta: kCrateApiUsearchApiVectorDbBulkRemoveVectorsConstMeta, + argValues: [that, keys], + apiImpl: this, + )); + } + + TaskConstMeta get kCrateApiUsearchApiVectorDbBulkRemoveVectorsConstMeta => + const TaskConstMeta( + debugName: "VectorDb_bulk_remove_vectors", + argNames: ["that", "keys"], + ); + @override Future<(List, List)> crateApiUsearchApiVectorDbBulkSearchVectors( @@ -217,7 +248,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_list_prim_f_32_strict(queries, serializer); sse_encode_usize(count, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 3, port: port_); + funcId: 4, port: port_); }, codec: SseCodec( decodeSuccessData: @@ -244,7 +275,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( that, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 4, port: port_); + funcId: 5, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -271,7 +302,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( that, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 5, port: port_); + funcId: 6, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_record_usize_usize_usize_usize_usize, @@ -299,7 +330,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { that, serializer); sse_encode_u_64(key, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 6, port: port_); + funcId: 7, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_list_prim_f_32_strict, @@ -325,7 +356,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(filePath, serializer); sse_encode_usize(dimensions, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 7)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 8)!; }, codec: SseCodec( decodeSuccessData: @@ -354,7 +385,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { that, serializer); sse_encode_u_64(key, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 8, port: port_); + funcId: 9, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_usize, @@ -380,7 +411,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( that, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 9, port: port_); + funcId: 10, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -411,7 +442,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_f_32_loose(query, serializer); sse_encode_usize(count, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 10, port: port_); + funcId: 11, port: port_); }, codec: SseCodec( decodeSuccessData: @@ -436,7 +467,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(name, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 11)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 12)!; }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -459,7 +490,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 12, port: port_); + funcId: 13, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -977,6 +1008,10 @@ class VectorDbImpl extends RustOpaque implements VectorDb { RustLib.instance.api.crateApiUsearchApiVectorDbBulkAddVectors( that: this, keys: keys, vectors: vectors); + Future bulkRemoveVectors({required Uint64List keys}) => + RustLib.instance.api + .crateApiUsearchApiVectorDbBulkRemoveVectors(that: this, keys: keys); + Future<(List, List)> bulkSearchVectors( {required List queries, required BigInt count}) => RustLib.instance.api.crateApiUsearchApiVectorDbBulkSearchVectors( diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index 107495dbda..2c32870d41 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -114,6 +114,18 @@ impl VectorDB { removed_count } + pub fn bulk_remove_vectors(&self, keys: Vec) -> usize { + let mut removed_count = 0; + for key in keys { + removed_count += self + .index + .remove(key) + .expect("Failed to (bulk) remove vector"); + } + self.save_index(); + removed_count + } + pub fn reset_index(&self) { self.index.reset().expect("Failed to reset index"); self.save_index(); diff --git a/mobile/rust/src/frb_generated.rs b/mobile/rust/src/frb_generated.rs index 0356753532..4ec616d979 100644 --- a/mobile/rust/src/frb_generated.rs +++ b/mobile/rust/src/frb_generated.rs @@ -38,7 +38,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.9.0"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -674813457; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1131116360; // Section: executor @@ -160,6 +160,61 @@ fn wire__crate__api__usearch_api__VectorDb_bulk_add_vectors_impl( }, ) } +fn wire__crate__api__usearch_api__VectorDb_bulk_remove_vectors_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "VectorDb_bulk_remove_vectors", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = , + >>::sse_decode(&mut deserializer); + let api_keys = >::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, ()>((move || { + let mut api_that_guard = None; + let decode_indices_ = + flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ + flutter_rust_bridge::for_generated::LockableOrderInfo::new( + &api_that, 0, false, + ), + ]); + for i in decode_indices_ { + match i { + 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref()), + _ => unreachable!(), + } + } + let api_that_guard = api_that_guard.unwrap(); + let output_ok = Result::<_, ()>::Ok( + crate::api::usearch_api::VectorDB::bulk_remove_vectors( + &*api_that_guard, + api_keys, + ), + )?; + Ok(output_ok) + })()) + } + }, + ) +} fn wire__crate__api__usearch_api__VectorDb_bulk_search_vectors_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, @@ -807,49 +862,55 @@ fn pde_ffi_dispatcher_primary_impl( rust_vec_len, data_len, ), - 3 => wire__crate__api__usearch_api__VectorDb_bulk_search_vectors_impl( + 3 => wire__crate__api__usearch_api__VectorDb_bulk_remove_vectors_impl( port, ptr, rust_vec_len, data_len, ), - 4 => wire__crate__api__usearch_api__VectorDb_delete_index_impl( + 4 => wire__crate__api__usearch_api__VectorDb_bulk_search_vectors_impl( port, ptr, rust_vec_len, data_len, ), - 5 => wire__crate__api__usearch_api__VectorDb_get_index_stats_impl( + 5 => wire__crate__api__usearch_api__VectorDb_delete_index_impl( port, ptr, rust_vec_len, data_len, ), - 6 => wire__crate__api__usearch_api__VectorDb_get_vector_impl( + 6 => wire__crate__api__usearch_api__VectorDb_get_index_stats_impl( port, ptr, rust_vec_len, data_len, ), - 8 => wire__crate__api__usearch_api__VectorDb_remove_vector_impl( + 7 => wire__crate__api__usearch_api__VectorDb_get_vector_impl( port, ptr, rust_vec_len, data_len, ), - 9 => wire__crate__api__usearch_api__VectorDb_reset_index_impl( + 9 => wire__crate__api__usearch_api__VectorDb_remove_vector_impl( port, ptr, rust_vec_len, data_len, ), - 10 => wire__crate__api__usearch_api__VectorDb_search_vectors_impl( + 10 => wire__crate__api__usearch_api__VectorDb_reset_index_impl( port, ptr, rust_vec_len, data_len, ), - 12 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), + 11 => wire__crate__api__usearch_api__VectorDb_search_vectors_impl( + port, + ptr, + rust_vec_len, + data_len, + ), + 13 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -862,8 +923,8 @@ fn pde_ffi_dispatcher_sync_impl( ) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { // Codec=Pde (Serialization + dispatch), see doc to use other codecs match func_id { - 7 => wire__crate__api__usearch_api__VectorDb_new_impl(ptr, rust_vec_len, data_len), - 11 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), + 8 => wire__crate__api__usearch_api__VectorDb_new_impl(ptr, rust_vec_len, data_len), + 12 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), _ => unreachable!(), } } From 832f2c451e914bb99cb3b46a6c25ccf7aa128eab Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 8 May 2025 11:47:50 +0530 Subject: [PATCH 027/156] Add bulk get method to vector db api --- mobile/lib/src/rust/api/usearch_api.dart | 2 + mobile/lib/src/rust/frb_generated.dart | 59 +++++++++++++---- mobile/rust/src/api/usearch_api.rs | 9 +++ mobile/rust/src/frb_generated.rs | 84 ++++++++++++++++++++---- 4 files changed, 130 insertions(+), 24 deletions(-) diff --git a/mobile/lib/src/rust/api/usearch_api.dart b/mobile/lib/src/rust/api/usearch_api.dart index 8260600b32..0ff5ada833 100644 --- a/mobile/lib/src/rust/api/usearch_api.dart +++ b/mobile/lib/src/rust/api/usearch_api.dart @@ -17,6 +17,8 @@ abstract class VectorDb implements RustOpaqueInterface { Future bulkAddVectors( {required Uint64List keys, required List vectors}); + Future> bulkGetVectors({required Uint64List keys}); + Future bulkRemoveVectors({required Uint64List keys}); Future<(List, List)> bulkSearchVectors( diff --git a/mobile/lib/src/rust/frb_generated.dart b/mobile/lib/src/rust/frb_generated.dart index 6a044372fd..247866b139 100644 --- a/mobile/lib/src/rust/frb_generated.dart +++ b/mobile/lib/src/rust/frb_generated.dart @@ -72,7 +72,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.9.0'; @override - int get rustContentHash => -1131116360; + int get rustContentHash => 382419186; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( @@ -93,6 +93,9 @@ abstract class RustLibApi extends BaseApi { required Uint64List keys, required List vectors}); + Future> crateApiUsearchApiVectorDbBulkGetVectors( + {required VectorDb that, required Uint64List keys}); + Future crateApiUsearchApiVectorDbBulkRemoveVectors( {required VectorDb that, required Uint64List keys}); @@ -207,7 +210,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); @override - Future crateApiUsearchApiVectorDbBulkRemoveVectors( + Future> crateApiUsearchApiVectorDbBulkGetVectors( {required VectorDb that, required Uint64List keys}) { return handler.executeNormal(NormalTask( callFfi: (port_) { @@ -218,6 +221,34 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 3, port: port_); }, + codec: SseCodec( + decodeSuccessData: sse_decode_list_list_prim_f_32_strict, + decodeErrorData: null, + ), + constMeta: kCrateApiUsearchApiVectorDbBulkGetVectorsConstMeta, + argValues: [that, keys], + apiImpl: this, + )); + } + + TaskConstMeta get kCrateApiUsearchApiVectorDbBulkGetVectorsConstMeta => + const TaskConstMeta( + debugName: "VectorDb_bulk_get_vectors", + argNames: ["that", "keys"], + ); + + @override + Future crateApiUsearchApiVectorDbBulkRemoveVectors( + {required VectorDb that, required Uint64List keys}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, serializer); + sse_encode_list_prim_u_64_strict(keys, serializer); + pdeCallFfi(generalizedFrbRustBinding, serializer, + funcId: 4, port: port_); + }, codec: SseCodec( decodeSuccessData: sse_decode_usize, decodeErrorData: null, @@ -248,7 +279,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_list_prim_f_32_strict(queries, serializer); sse_encode_usize(count, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 4, port: port_); + funcId: 5, port: port_); }, codec: SseCodec( decodeSuccessData: @@ -275,7 +306,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( that, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 5, port: port_); + funcId: 6, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -302,7 +333,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( that, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 6, port: port_); + funcId: 7, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_record_usize_usize_usize_usize_usize, @@ -330,7 +361,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { that, serializer); sse_encode_u_64(key, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 7, port: port_); + funcId: 8, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_list_prim_f_32_strict, @@ -356,7 +387,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(filePath, serializer); sse_encode_usize(dimensions, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 8)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 9)!; }, codec: SseCodec( decodeSuccessData: @@ -385,7 +416,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { that, serializer); sse_encode_u_64(key, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 9, port: port_); + funcId: 10, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_usize, @@ -411,7 +442,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( that, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 10, port: port_); + funcId: 11, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -442,7 +473,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_f_32_loose(query, serializer); sse_encode_usize(count, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 11, port: port_); + funcId: 12, port: port_); }, codec: SseCodec( decodeSuccessData: @@ -467,7 +498,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(name, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 12)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 13)!; }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -490,7 +521,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 13, port: port_); + funcId: 14, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -1008,6 +1039,10 @@ class VectorDbImpl extends RustOpaque implements VectorDb { RustLib.instance.api.crateApiUsearchApiVectorDbBulkAddVectors( that: this, keys: keys, vectors: vectors); + Future> bulkGetVectors({required Uint64List keys}) => + RustLib.instance.api + .crateApiUsearchApiVectorDbBulkGetVectors(that: this, keys: keys); + Future bulkRemoveVectors({required Uint64List keys}) => RustLib.instance.api .crateApiUsearchApiVectorDbBulkRemoveVectors(that: this, keys: keys); diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index 2c32870d41..47a948cff1 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -108,6 +108,15 @@ impl VectorDB { vector } + pub fn bulk_get_vectors(&self, keys: Vec) -> Vec> { + let mut vectors = Vec::new(); + for key in keys { + let vector = self.get_vector(key); + vectors.push(vector); + } + vectors + } + pub fn remove_vector(&self, key: u64) -> usize { let removed_count = self.index.remove(key).expect("Failed to remove vector"); self.save_index(); diff --git a/mobile/rust/src/frb_generated.rs b/mobile/rust/src/frb_generated.rs index 4ec616d979..94670bc0b3 100644 --- a/mobile/rust/src/frb_generated.rs +++ b/mobile/rust/src/frb_generated.rs @@ -38,7 +38,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.9.0"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1131116360; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 382419186; // Section: executor @@ -160,6 +160,60 @@ fn wire__crate__api__usearch_api__VectorDb_bulk_add_vectors_impl( }, ) } +fn wire__crate__api__usearch_api__VectorDb_bulk_get_vectors_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "VectorDb_bulk_get_vectors", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = , + >>::sse_decode(&mut deserializer); + let api_keys = >::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, ()>((move || { + let mut api_that_guard = None; + let decode_indices_ = + flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ + flutter_rust_bridge::for_generated::LockableOrderInfo::new( + &api_that, 0, false, + ), + ]); + for i in decode_indices_ { + match i { + 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref()), + _ => unreachable!(), + } + } + let api_that_guard = api_that_guard.unwrap(); + let output_ok = + Result::<_, ()>::Ok(crate::api::usearch_api::VectorDB::bulk_get_vectors( + &*api_that_guard, + api_keys, + ))?; + Ok(output_ok) + })()) + } + }, + ) +} fn wire__crate__api__usearch_api__VectorDb_bulk_remove_vectors_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, @@ -862,55 +916,61 @@ fn pde_ffi_dispatcher_primary_impl( rust_vec_len, data_len, ), - 3 => wire__crate__api__usearch_api__VectorDb_bulk_remove_vectors_impl( + 3 => wire__crate__api__usearch_api__VectorDb_bulk_get_vectors_impl( port, ptr, rust_vec_len, data_len, ), - 4 => wire__crate__api__usearch_api__VectorDb_bulk_search_vectors_impl( + 4 => wire__crate__api__usearch_api__VectorDb_bulk_remove_vectors_impl( port, ptr, rust_vec_len, data_len, ), - 5 => wire__crate__api__usearch_api__VectorDb_delete_index_impl( + 5 => wire__crate__api__usearch_api__VectorDb_bulk_search_vectors_impl( port, ptr, rust_vec_len, data_len, ), - 6 => wire__crate__api__usearch_api__VectorDb_get_index_stats_impl( + 6 => wire__crate__api__usearch_api__VectorDb_delete_index_impl( port, ptr, rust_vec_len, data_len, ), - 7 => wire__crate__api__usearch_api__VectorDb_get_vector_impl( + 7 => wire__crate__api__usearch_api__VectorDb_get_index_stats_impl( port, ptr, rust_vec_len, data_len, ), - 9 => wire__crate__api__usearch_api__VectorDb_remove_vector_impl( + 8 => wire__crate__api__usearch_api__VectorDb_get_vector_impl( port, ptr, rust_vec_len, data_len, ), - 10 => wire__crate__api__usearch_api__VectorDb_reset_index_impl( + 10 => wire__crate__api__usearch_api__VectorDb_remove_vector_impl( port, ptr, rust_vec_len, data_len, ), - 11 => wire__crate__api__usearch_api__VectorDb_search_vectors_impl( + 11 => wire__crate__api__usearch_api__VectorDb_reset_index_impl( port, ptr, rust_vec_len, data_len, ), - 13 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), + 12 => wire__crate__api__usearch_api__VectorDb_search_vectors_impl( + port, + ptr, + rust_vec_len, + data_len, + ), + 14 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -923,8 +983,8 @@ fn pde_ffi_dispatcher_sync_impl( ) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { // Codec=Pde (Serialization + dispatch), see doc to use other codecs match func_id { - 8 => wire__crate__api__usearch_api__VectorDb_new_impl(ptr, rust_vec_len, data_len), - 12 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), + 9 => wire__crate__api__usearch_api__VectorDb_new_impl(ptr, rust_vec_len, data_len), + 13 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), _ => unreachable!(), } } From ea1a2960bfd72e0cf7c440bb9073c5d05ef038ee Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 8 May 2025 12:08:55 +0530 Subject: [PATCH 028/156] First implementation of clip vector db --- mobile/lib/db/ml/clip_vector_db.dart | 139 +++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 mobile/lib/db/ml/clip_vector_db.dart diff --git a/mobile/lib/db/ml/clip_vector_db.dart b/mobile/lib/db/ml/clip_vector_db.dart new file mode 100644 index 0000000000..ae9b7be348 --- /dev/null +++ b/mobile/lib/db/ml/clip_vector_db.dart @@ -0,0 +1,139 @@ +import "dart:typed_data" show Float32List; + +import "package:flutter_rust_bridge/flutter_rust_bridge.dart" show Uint64List; +import "package:logging/logging.dart"; +import "package:path/path.dart"; +import "package:path_provider/path_provider.dart"; +import "package:photos/models/ml/vector.dart"; +import "package:photos/src/rust/api/usearch_api.dart"; + +class ClipVectorDB { + static final Logger _logger = Logger("ClipVectorDB"); + + static const _databaseName = "ente.ml.vectordb.clip"; + + static final BigInt _embeddingDimension = BigInt.from(512); + + static Logger get logger => _logger; + + // Singleton pattern + ClipVectorDB._privateConstructor(); + static final instance = ClipVectorDB._privateConstructor(); + factory ClipVectorDB() => instance; + + // only have a single app-wide reference to the database + static Future? _vectorDbFuture; + + Future get _vectorDB async { + _vectorDbFuture ??= _initVectorDB(); + return _vectorDbFuture!; + } + + Future _initVectorDB() async { + final documentsDirectory = await getApplicationDocumentsDirectory(); + final String databaseDirectory = + join(documentsDirectory.path, _databaseName); + _logger.info("Opening vectorDB access: DB path " + databaseDirectory); + final vectorDB = VectorDb( + filePath: databaseDirectory, + dimensions: _embeddingDimension, + ); + + return vectorDB; + } + + Future bulkInsertEmbeddings({ + required Uint64List keys, + required List embeddings, + }) async { + final db = await _vectorDB; + try { + await db.bulkAddVectors(keys: keys, vectors: embeddings); + } catch (e, s) { + _logger.severe("Error bulk inserting embeddings", e, s); + rethrow; + } + } + + Future insertEmbeddings({ + required BigInt key, + required List embedding, + }) async { + final db = await _vectorDB; + try { + await db.addVector(key: key, vector: embedding); + } catch (e, s) { + _logger.severe("Error inserting embedding", e, s); + rethrow; + } + } + + Future> getVectors(List fileIds) async { + final db = await _vectorDB; + try { + final keys = Uint64List.fromList(fileIds); + final vectors = await db.bulkGetVectors(keys: keys); + return List.generate( + vectors.length, + (index) => EmbeddingVector( + fileID: fileIds[index], + embedding: vectors[index], + ), + ); + } catch (e, s) { + _logger.severe("Error getting embeddings", e, s); + rethrow; + } + } + + Future deleteEmbeddings(List keys) async { + final db = await _vectorDB; + try { + final deletedCount = + await db.bulkRemoveVectors(keys: Uint64List.fromList(keys)); + _logger + .info("Deleted $deletedCount embeddings, from ${keys.length} keys"); + } catch (e, s) { + _logger.severe("Error bulk deleting specific embeddings", e, s); + rethrow; + } + } + + Future deleteAllEmbeddings() async { + final db = await _vectorDB; + try { + await db.resetIndex(); + } catch (e, s) { + _logger.severe("Error deleting all embeddings", e, s); + rethrow; + } + } + + Future<(Uint64List, Float32List)> searchClosestVectors( + List query, + int count, + ) async { + final db = await _vectorDB; + try { + final result = + await db.searchVectors(query: query, count: BigInt.from(count)); + return result; + } catch (e, s) { + _logger.severe("Error searching closest vectors", e, s); + rethrow; + } + } + + Future<(BigInt, double)> searchClosestVector( + List query, + ) async { + final db = await _vectorDB; + try { + final result = await db.searchVectors(query: query, count: BigInt.one); + return (result.$1[0], result.$2[0]); + } catch (e, s) { + _logger.severe("Error searching closest vector", e, s); + rethrow; + } + } +} From 5447350ab14e384f01a31ce64cd314248311ba4a Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 8 May 2025 12:29:41 +0530 Subject: [PATCH 029/156] vector db api add check for key --- mobile/lib/src/rust/api/usearch_api.dart | 2 + mobile/lib/src/rust/frb_generated.dart | 84 ++++++++++++----- mobile/lib/src/rust/frb_generated.io.dart | 15 +-- mobile/rust/src/api/usearch_api.rs | 4 + mobile/rust/src/frb_generated.rs | 108 +++++++++++++++++----- 5 files changed, 161 insertions(+), 52 deletions(-) diff --git a/mobile/lib/src/rust/api/usearch_api.dart b/mobile/lib/src/rust/api/usearch_api.dart index 0ff5ada833..c7f47800ff 100644 --- a/mobile/lib/src/rust/api/usearch_api.dart +++ b/mobile/lib/src/rust/api/usearch_api.dart @@ -24,6 +24,8 @@ abstract class VectorDb implements RustOpaqueInterface { Future<(List, List)> bulkSearchVectors( {required List queries, required BigInt count}); + Future containsVector({required BigInt key}); + Future deleteIndex(); Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> getIndexStats(); diff --git a/mobile/lib/src/rust/frb_generated.dart b/mobile/lib/src/rust/frb_generated.dart index 247866b139..9a841e1e4f 100644 --- a/mobile/lib/src/rust/frb_generated.dart +++ b/mobile/lib/src/rust/frb_generated.dart @@ -72,7 +72,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.9.0'; @override - int get rustContentHash => 382419186; + int get rustContentHash => 1543559173; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( @@ -105,6 +105,9 @@ abstract class RustLibApi extends BaseApi { required List queries, required BigInt count}); + Future crateApiUsearchApiVectorDbContainsVector( + {required VectorDb that, required BigInt key}); + Future crateApiUsearchApiVectorDbDeleteIndex({required VectorDb that}); Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> @@ -298,6 +301,34 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["that", "queries", "count"], ); + @override + Future crateApiUsearchApiVectorDbContainsVector( + {required VectorDb that, required BigInt key}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, serializer); + sse_encode_u_64(key, serializer); + pdeCallFfi(generalizedFrbRustBinding, serializer, + funcId: 6, port: port_); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_bool, + decodeErrorData: null, + ), + constMeta: kCrateApiUsearchApiVectorDbContainsVectorConstMeta, + argValues: [that, key], + apiImpl: this, + )); + } + + TaskConstMeta get kCrateApiUsearchApiVectorDbContainsVectorConstMeta => + const TaskConstMeta( + debugName: "VectorDb_contains_vector", + argNames: ["that", "key"], + ); + @override Future crateApiUsearchApiVectorDbDeleteIndex({required VectorDb that}) { return handler.executeNormal(NormalTask( @@ -306,7 +337,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( that, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 6, port: port_); + funcId: 7, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -333,7 +364,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( that, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 7, port: port_); + funcId: 8, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_record_usize_usize_usize_usize_usize, @@ -361,7 +392,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { that, serializer); sse_encode_u_64(key, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 8, port: port_); + funcId: 9, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_list_prim_f_32_strict, @@ -387,7 +418,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(filePath, serializer); sse_encode_usize(dimensions, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 9)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 10)!; }, codec: SseCodec( decodeSuccessData: @@ -416,7 +447,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { that, serializer); sse_encode_u_64(key, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 10, port: port_); + funcId: 11, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_usize, @@ -442,7 +473,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( that, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 11, port: port_); + funcId: 12, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -473,7 +504,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_f_32_loose(query, serializer); sse_encode_usize(count, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 12, port: port_); + funcId: 13, port: port_); }, codec: SseCodec( decodeSuccessData: @@ -498,7 +529,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(name, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 13)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 14)!; }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -521,7 +552,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 14, port: port_); + funcId: 15, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -576,6 +607,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw as String; } + @protected + bool dco_decode_bool(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw as bool; + } + @protected double dco_decode_f_32(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -728,6 +765,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return utf8.decoder.convert(inner); } + @protected + bool sse_decode_bool(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return deserializer.buffer.getUint8() != 0; + } + @protected double sse_decode_f_32(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -850,12 +893,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return deserializer.buffer.getInt32(); } - @protected - bool sse_decode_bool(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return deserializer.buffer.getUint8() != 0; - } - @protected void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -889,6 +926,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_strict(utf8.encoder.convert(self), serializer); } + @protected + void sse_encode_bool(bool self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + serializer.buffer.putUint8(self ? 1 : 0); + } + @protected void sse_encode_f_32(double self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1003,12 +1046,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Sse (Serialization based), see doc to use other codecs serializer.buffer.putInt32(self); } - - @protected - void sse_encode_bool(bool self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - serializer.buffer.putUint8(self ? 1 : 0); - } } @sealed @@ -1052,6 +1089,9 @@ class VectorDbImpl extends RustOpaque implements VectorDb { RustLib.instance.api.crateApiUsearchApiVectorDbBulkSearchVectors( that: this, queries: queries, count: count); + Future containsVector({required BigInt key}) => RustLib.instance.api + .crateApiUsearchApiVectorDbContainsVector(that: this, key: key); + Future deleteIndex() => RustLib.instance.api.crateApiUsearchApiVectorDbDeleteIndex( that: this, diff --git a/mobile/lib/src/rust/frb_generated.io.dart b/mobile/lib/src/rust/frb_generated.io.dart index 34a84063ae..7e44cd6c83 100644 --- a/mobile/lib/src/rust/frb_generated.io.dart +++ b/mobile/lib/src/rust/frb_generated.io.dart @@ -43,6 +43,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected String dco_decode_String(dynamic raw); + @protected + bool dco_decode_bool(dynamic raw); + @protected double dco_decode_f_32(dynamic raw); @@ -109,6 +112,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected String sse_decode_String(SseDeserializer deserializer); + @protected + bool sse_decode_bool(SseDeserializer deserializer); + @protected double sse_decode_f_32(SseDeserializer deserializer); @@ -162,9 +168,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected int sse_decode_i_32(SseDeserializer deserializer); - @protected - bool sse_decode_bool(SseDeserializer deserializer); - @protected void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( @@ -183,6 +186,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_String(String self, SseSerializer serializer); + @protected + void sse_encode_bool(bool self, SseSerializer serializer); + @protected void sse_encode_f_32(double self, SseSerializer serializer); @@ -236,9 +242,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_i_32(int self, SseSerializer serializer); - - @protected - void sse_encode_bool(bool self, SseSerializer serializer); } // Section: wire_class diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index 47a948cff1..742a39ba85 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -100,6 +100,10 @@ impl VectorDB { (keys, distances) } + pub fn contains_vector(&self, key: u64) -> bool { + self.index.contains(key) + } + pub fn get_vector(&self, key: u64) -> Vec { let mut vector: Vec = vec![0.0; self.index.dimensions()]; self.index diff --git a/mobile/rust/src/frb_generated.rs b/mobile/rust/src/frb_generated.rs index 94670bc0b3..219c86123e 100644 --- a/mobile/rust/src/frb_generated.rs +++ b/mobile/rust/src/frb_generated.rs @@ -38,7 +38,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.9.0"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 382419186; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1543559173; // Section: executor @@ -326,6 +326,60 @@ fn wire__crate__api__usearch_api__VectorDb_bulk_search_vectors_impl( }, ) } +fn wire__crate__api__usearch_api__VectorDb_contains_vector_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "VectorDb_contains_vector", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = , + >>::sse_decode(&mut deserializer); + let api_key = ::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, ()>((move || { + let mut api_that_guard = None; + let decode_indices_ = + flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ + flutter_rust_bridge::for_generated::LockableOrderInfo::new( + &api_that, 0, false, + ), + ]); + for i in decode_indices_ { + match i { + 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref()), + _ => unreachable!(), + } + } + let api_that_guard = api_that_guard.unwrap(); + let output_ok = + Result::<_, ()>::Ok(crate::api::usearch_api::VectorDB::contains_vector( + &*api_that_guard, + api_key, + ))?; + Ok(output_ok) + })()) + } + }, + ) +} fn wire__crate__api__usearch_api__VectorDb_delete_index_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, @@ -758,6 +812,13 @@ impl SseDecode for String { } } +impl SseDecode for bool { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_u8().unwrap() != 0 + } +} + impl SseDecode for f32 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -888,13 +949,6 @@ impl SseDecode for i32 { } } -impl SseDecode for bool { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - deserializer.cursor.read_u8().unwrap() != 0 - } -} - fn pde_ffi_dispatcher_primary_impl( func_id: i32, port: flutter_rust_bridge::for_generated::MessagePort, @@ -934,43 +988,49 @@ fn pde_ffi_dispatcher_primary_impl( rust_vec_len, data_len, ), - 6 => wire__crate__api__usearch_api__VectorDb_delete_index_impl( + 6 => wire__crate__api__usearch_api__VectorDb_contains_vector_impl( port, ptr, rust_vec_len, data_len, ), - 7 => wire__crate__api__usearch_api__VectorDb_get_index_stats_impl( + 7 => wire__crate__api__usearch_api__VectorDb_delete_index_impl( port, ptr, rust_vec_len, data_len, ), - 8 => wire__crate__api__usearch_api__VectorDb_get_vector_impl( + 8 => wire__crate__api__usearch_api__VectorDb_get_index_stats_impl( port, ptr, rust_vec_len, data_len, ), - 10 => wire__crate__api__usearch_api__VectorDb_remove_vector_impl( + 9 => wire__crate__api__usearch_api__VectorDb_get_vector_impl( port, ptr, rust_vec_len, data_len, ), - 11 => wire__crate__api__usearch_api__VectorDb_reset_index_impl( + 11 => wire__crate__api__usearch_api__VectorDb_remove_vector_impl( port, ptr, rust_vec_len, data_len, ), - 12 => wire__crate__api__usearch_api__VectorDb_search_vectors_impl( + 12 => wire__crate__api__usearch_api__VectorDb_reset_index_impl( port, ptr, rust_vec_len, data_len, ), - 14 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), + 13 => wire__crate__api__usearch_api__VectorDb_search_vectors_impl( + port, + ptr, + rust_vec_len, + data_len, + ), + 15 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -983,8 +1043,8 @@ fn pde_ffi_dispatcher_sync_impl( ) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { // Codec=Pde (Serialization + dispatch), see doc to use other codecs match func_id { - 9 => wire__crate__api__usearch_api__VectorDb_new_impl(ptr, rust_vec_len, data_len), - 13 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), + 10 => wire__crate__api__usearch_api__VectorDb_new_impl(ptr, rust_vec_len, data_len), + 14 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -1031,6 +1091,13 @@ impl SseEncode for String { } } +impl SseEncode for bool { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_u8(self as _).unwrap(); + } +} + impl SseEncode for f32 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -1151,13 +1218,6 @@ impl SseEncode for i32 { } } -impl SseEncode for bool { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - serializer.cursor.write_u8(self as _).unwrap(); - } -} - #[cfg(not(target_family = "wasm"))] mod io { // This file is automatically generated, so please do not edit it. From d87069eb4c3e6808048b78eb4f39e7c76f1d6397 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 8 May 2025 12:31:09 +0530 Subject: [PATCH 030/156] vectordb api add documentation --- mobile/lib/src/rust/api/usearch_api.dart | 2 ++ mobile/lib/src/rust/frb_generated.dart | 2 ++ mobile/rust/src/api/usearch_api.rs | 2 ++ 3 files changed, 6 insertions(+) diff --git a/mobile/lib/src/rust/api/usearch_api.dart b/mobile/lib/src/rust/api/usearch_api.dart index c7f47800ff..80f9a40fe5 100644 --- a/mobile/lib/src/rust/api/usearch_api.dart +++ b/mobile/lib/src/rust/api/usearch_api.dart @@ -24,6 +24,8 @@ abstract class VectorDb implements RustOpaqueInterface { Future<(List, List)> bulkSearchVectors( {required List queries, required BigInt count}); + /// Check if a vector with the given key exists in the index. + /// `true` if the index contains the vector with the given key, `false` otherwise. Future containsVector({required BigInt key}); Future deleteIndex(); diff --git a/mobile/lib/src/rust/frb_generated.dart b/mobile/lib/src/rust/frb_generated.dart index 9a841e1e4f..ef200514f3 100644 --- a/mobile/lib/src/rust/frb_generated.dart +++ b/mobile/lib/src/rust/frb_generated.dart @@ -1089,6 +1089,8 @@ class VectorDbImpl extends RustOpaque implements VectorDb { RustLib.instance.api.crateApiUsearchApiVectorDbBulkSearchVectors( that: this, queries: queries, count: count); + /// Check if a vector with the given key exists in the index. + /// `true` if the index contains the vector with the given key, `false` otherwise. Future containsVector({required BigInt key}) => RustLib.instance.api .crateApiUsearchApiVectorDbContainsVector(that: this, key: key); diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index 742a39ba85..8d4d8f6cae 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -100,6 +100,8 @@ impl VectorDB { (keys, distances) } + /// Check if a vector with the given key exists in the index. + /// `true` if the index contains the vector with the given key, `false` otherwise. pub fn contains_vector(&self, key: u64) -> bool { self.index.contains(key) } From 6478d438b5eb98004bb287eec4fa5fd473ec4e0f Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 8 May 2025 14:30:51 +0530 Subject: [PATCH 031/156] vector db api ensure never duplicate keys --- mobile/rust/src/api/usearch_api.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index 8d4d8f6cae..8eb13ab717 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -61,7 +61,11 @@ impl VectorDB { } pub fn add_vector(&self, key: u64, vector: &Vec) { - self.ensure_capacity(1); + if self.contains_vector(key) { + self.remove_vector(key); + } else { + self.ensure_capacity(1); + } self.index.add(key, vector).expect("Failed to add vector"); self.save_index(); } @@ -69,6 +73,9 @@ impl VectorDB { pub fn bulk_add_vectors(&self, keys: Vec, vectors: &Vec>) { self.ensure_capacity(keys.len()); for (key, vector) in keys.iter().zip(vectors.iter()) { + if self.contains_vector(*key) { + self.remove_vector(*key); + } self.index .add(*key, vector) .expect("Failed to (bulk) add vector"); From 2ffb73d053ca2c3f338fa0e91022003ac892e75e Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 8 May 2025 15:07:50 +0530 Subject: [PATCH 032/156] Consistent method parameters --- mobile/lib/db/ml/clip_vector_db.dart | 48 +++++++++++++++------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/mobile/lib/db/ml/clip_vector_db.dart b/mobile/lib/db/ml/clip_vector_db.dart index ae9b7be348..760fe38627 100644 --- a/mobile/lib/db/ml/clip_vector_db.dart +++ b/mobile/lib/db/ml/clip_vector_db.dart @@ -42,41 +42,42 @@ class ClipVectorDB { return vectorDB; } - Future bulkInsertEmbeddings({ - required Uint64List keys, - required List embeddings, - }) async { - final db = await _vectorDB; - try { - await db.bulkAddVectors(keys: keys, vectors: embeddings); - } catch (e, s) { - _logger.severe("Error bulk inserting embeddings", e, s); - rethrow; - } - } - - Future insertEmbeddings({ - required BigInt key, + Future insertEmbedding({ + required int fileID, required List embedding, }) async { final db = await _vectorDB; try { - await db.addVector(key: key, vector: embedding); + await db.addVector(key: BigInt.from(fileID), vector: embedding); } catch (e, s) { _logger.severe("Error inserting embedding", e, s); rethrow; } } - Future> getVectors(List fileIds) async { + Future bulkInsertEmbeddings({ + required List fileIDs, + required List embeddings, + }) async { + final db = await _vectorDB; + final bigKeys = Uint64List.fromList(fileIDs); + try { + await db.bulkAddVectors(keys: bigKeys, vectors: embeddings); + } catch (e, s) { + _logger.severe("Error bulk inserting embeddings", e, s); + rethrow; + } + } + + Future> getEmbeddings(List fileIDs) async { final db = await _vectorDB; try { - final keys = Uint64List.fromList(fileIds); + final keys = Uint64List.fromList(fileIDs); final vectors = await db.bulkGetVectors(keys: keys); return List.generate( vectors.length, (index) => EmbeddingVector( - fileID: fileIds[index], + fileID: fileIDs[index], embedding: vectors[index], ), ); @@ -86,13 +87,14 @@ class ClipVectorDB { } } - Future deleteEmbeddings(List keys) async { + Future deleteEmbeddings(List fileIDs) async { final db = await _vectorDB; try { final deletedCount = - await db.bulkRemoveVectors(keys: Uint64List.fromList(keys)); - _logger - .info("Deleted $deletedCount embeddings, from ${keys.length} keys"); + await db.bulkRemoveVectors(keys: Uint64List.fromList(fileIDs)); + _logger.info( + "Deleted $deletedCount embeddings, from ${fileIDs.length} keys", + ); } catch (e, s) { _logger.severe("Error bulk deleting specific embeddings", e, s); rethrow; From ce112bd4d79637e4d5aacad47f84aa2e74cf9b04 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 8 May 2025 15:23:23 +0530 Subject: [PATCH 033/156] Index stats method --- mobile/lib/db/ml/clip_vector_db.dart | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mobile/lib/db/ml/clip_vector_db.dart b/mobile/lib/db/ml/clip_vector_db.dart index 760fe38627..eb28181d3c 100644 --- a/mobile/lib/db/ml/clip_vector_db.dart +++ b/mobile/lib/db/ml/clip_vector_db.dart @@ -111,6 +111,21 @@ class ClipVectorDB { } } + Future<(int, int, int)> getIndexStats() async { + final db = await _vectorDB; + try { + final stats = await db.getIndexStats(); + return ( + stats.$1.toInt(), + stats.$2.toInt(), + stats.$3.toInt(), + ); + } catch (e, s) { + _logger.severe("Error getting index stats", e, s); + rethrow; + } + } + Future<(Uint64List, Float32List)> searchClosestVectors( List query, int count, From 27ad020adc6268e52e5f8856ec5d2406b2827130 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 8 May 2025 17:40:01 +0530 Subject: [PATCH 034/156] Testing clip migration to vector DB --- .../debug/ml_debug_section_widget.dart | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index 31fb9eb9af..117f706735 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -9,6 +9,7 @@ import "package:logging/logging.dart"; import "package:ml_linalg/linalg.dart"; import "package:path_provider/path_provider.dart"; import "package:photos/core/event_bus.dart"; +import "package:photos/db/ml/clip_vector_db.dart"; import "package:photos/db/ml/db.dart"; import "package:photos/events/people_changed_event.dart"; import "package:photos/extensions/stop_watch.dart"; @@ -136,6 +137,111 @@ class _MLDebugSectionWidgetState extends State { }, ), sectionOptionSpacing, + MenuItemWidget( + captionedTextWidget: const CaptionedTextWidget( + title: "Fill ClipVectorDB", + ), + pressedColor: getEnteColorScheme(context).fillFaint, + trailingIcon: Icons.chevron_right_outlined, + trailingIconIsMuted: true, + onTap: () async { + try { + final allClip = await MLDataDB.instance.getAllClipVectors(); + + final clipVectorDB = ClipVectorDB.instance; + await clipVectorDB.deleteAllEmbeddings(); + final stats = await clipVectorDB.getIndexStats(); + logger.info("ClipVectorDB stats: size: ${stats.$1}, " + "capacity: ${stats.$2}, dimensions: ${stats.$3}"); + showShortToast( + context, + "ClipVectorDB stats: size: ${stats.$1}, " + "capacity: ${stats.$2}, dimensions: ${stats.$3}"); + + final fileIDs = allClip.map((e) => e.fileID).toList(); + final embeddings = allClip + .map((e) => Float32List.fromList(e.vector.toList())) + .toList(); + final now = DateTime.now(); + await clipVectorDB.bulkInsertEmbeddings( + fileIDs: fileIDs, + embeddings: embeddings, + ); + final duration = DateTime.now().difference(now); + logger.info( + "ClipVectorDB bulk insert took ${duration.inMilliseconds} ms for ${fileIDs.length} embeddings", + ); + final statsAfter = await clipVectorDB.getIndexStats(); + logger.info("ClipVectorDB stats after: size: ${statsAfter.$1}, " + "capacity: ${statsAfter.$2}, dimensions: ${statsAfter.$3}"); + showShortToast( + context, + "ClipVectorDB stats after: size: ${statsAfter.$1}, " + "capacity: ${statsAfter.$2}, dimensions: ${statsAfter.$3}"); + } catch (e, s) { + logger.warning('ClipVectorDB migration failed ', e, s); + await showGenericErrorDialog(context: context, error: e); + } + }, + ), + sectionOptionSpacing, + MenuItemWidget( + captionedTextWidget: const CaptionedTextWidget( + title: "Show ClipVectorDB stats", + ), + pressedColor: getEnteColorScheme(context).fillFaint, + trailingIcon: Icons.chevron_right_outlined, + trailingIconIsMuted: true, + onTap: () async { + try { + final clipVectorDB = ClipVectorDB.instance; + final stats = await clipVectorDB.getIndexStats(); + logger.info("ClipVectorDB stats: size: ${stats.$1}, " + "capacity: ${stats.$2}, dimensions: ${stats.$3}"); + showShortToast( + context, + "ClipVectorDB stats: size: ${stats.$1}, " + "capacity: ${stats.$2}, dimensions: ${stats.$3}"); + } catch (e, s) { + logger.warning('ClipVectorDB stats failed ', e, s); + await showGenericErrorDialog(context: context, error: e); + } + }, + ), + sectionOptionSpacing, + MenuItemWidget( + captionedTextWidget: const CaptionedTextWidget( + title: "Empty ClipVectorDB", + ), + pressedColor: getEnteColorScheme(context).fillFaint, + trailingIcon: Icons.chevron_right_outlined, + trailingIconIsMuted: true, + onTap: () async { + try { + final clipVectorDB = ClipVectorDB.instance; + final stats = await clipVectorDB.getIndexStats(); + logger.info("ClipVectorDB stats: size: ${stats.$1}, " + "capacity: ${stats.$2}, dimensions: ${stats.$3}"); + showShortToast( + context, + "ClipVectorDB stats: size: ${stats.$1}, " + "capacity: ${stats.$2}, dimensions: ${stats.$3}"); + + await clipVectorDB.deleteAllEmbeddings(); + final statsAfter = await clipVectorDB.getIndexStats(); + logger.info("ClipVectorDB stats after: size: ${statsAfter.$1}, " + "capacity: ${statsAfter.$2}, dimensions: ${statsAfter.$3}"); + showShortToast( + context, + "ClipVectorDB stats after: size: ${statsAfter.$1}, " + "capacity: ${statsAfter.$2}, dimensions: ${statsAfter.$3}"); + } catch (e, s) { + logger.warning('ClipVectorDB cleanup failed ', e, s); + await showGenericErrorDialog(context: context, error: e); + } + }, + ), + sectionOptionSpacing, MenuItemWidget( captionedTextWidget: const CaptionedTextWidget( title: "Benchmark Vector DB Face", From 54b712953a770b6c5b3be64f0573c1509bd54be4 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Fri, 9 May 2025 10:49:03 +0530 Subject: [PATCH 035/156] vector db api let clear include capacity reset --- mobile/rust/src/api/usearch_api.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index 8eb13ab717..c845d69c7e 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -150,6 +150,9 @@ impl VectorDB { pub fn reset_index(&self) { self.index.reset().expect("Failed to reset index"); + self.index + .reserve(1000) + .expect("Failed to reserve space in index"); self.save_index(); } From d411d91966b850de084d086ad6aa70242ef95932 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Fri, 9 May 2025 12:56:59 +0530 Subject: [PATCH 036/156] vector db api ensure capacity safety --- mobile/rust/src/api/usearch_api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index c845d69c7e..24975f86fd 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -53,7 +53,7 @@ impl VectorDB { fn ensure_capacity(&self, margin: usize) { let current_size = self.index.size(); let capacity = self.index.capacity(); - if current_size + margin >= capacity { + if current_size + margin + 1000 >= capacity { self.index .reserve(current_size + margin) .expect("Failed to reserve space in index"); From 2f5a02ec430b5c0872d54bad69bd04e543bc723f Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Fri, 9 May 2025 12:57:44 +0530 Subject: [PATCH 037/156] delete table option --- mobile/lib/db/ml/clip_vector_db.dart | 11 +++++++++++ .../debug/ml_debug_section_widget.dart | 19 ++----------------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/mobile/lib/db/ml/clip_vector_db.dart b/mobile/lib/db/ml/clip_vector_db.dart index eb28181d3c..84d3261460 100644 --- a/mobile/lib/db/ml/clip_vector_db.dart +++ b/mobile/lib/db/ml/clip_vector_db.dart @@ -111,6 +111,17 @@ class ClipVectorDB { } } + Future deleteIndex() async { + final db = await _vectorDB; + try { + await db.deleteIndex(); + _vectorDbFuture = null; + } catch (e, s) { + _logger.severe("Error deleting index", e, s); + rethrow; + } + } + Future<(int, int, int)> getIndexStats() async { final db = await _vectorDB; try { diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index 117f706735..c2c3406bc5 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -211,7 +211,7 @@ class _MLDebugSectionWidgetState extends State { sectionOptionSpacing, MenuItemWidget( captionedTextWidget: const CaptionedTextWidget( - title: "Empty ClipVectorDB", + title: "Delete/Empty ClipVectorDB", ), pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, @@ -219,22 +219,7 @@ class _MLDebugSectionWidgetState extends State { onTap: () async { try { final clipVectorDB = ClipVectorDB.instance; - final stats = await clipVectorDB.getIndexStats(); - logger.info("ClipVectorDB stats: size: ${stats.$1}, " - "capacity: ${stats.$2}, dimensions: ${stats.$3}"); - showShortToast( - context, - "ClipVectorDB stats: size: ${stats.$1}, " - "capacity: ${stats.$2}, dimensions: ${stats.$3}"); - - await clipVectorDB.deleteAllEmbeddings(); - final statsAfter = await clipVectorDB.getIndexStats(); - logger.info("ClipVectorDB stats after: size: ${statsAfter.$1}, " - "capacity: ${statsAfter.$2}, dimensions: ${statsAfter.$3}"); - showShortToast( - context, - "ClipVectorDB stats after: size: ${statsAfter.$1}, " - "capacity: ${statsAfter.$2}, dimensions: ${statsAfter.$3}"); + await clipVectorDB.deleteIndex(); } catch (e, s) { logger.warning('ClipVectorDB cleanup failed ', e, s); await showGenericErrorDialog(context: context, error: e); From bb7f8a5eef9f793e9e2197b4566218e774b22a4d Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Fri, 9 May 2025 15:59:46 +0530 Subject: [PATCH 038/156] More testing --- .../debug/ml_debug_section_widget.dart | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index c2c3406bc5..7ccdd26f56 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -147,9 +147,13 @@ class _MLDebugSectionWidgetState extends State { onTap: () async { try { final allClip = await MLDataDB.instance.getAllClipVectors(); + final allClipSmall = allClip.sublist(0, 15000); + showShortToast(context, "Got all embeddings"); + logger.info("Got all embeddings"); final clipVectorDB = ClipVectorDB.instance; await clipVectorDB.deleteAllEmbeddings(); + logger.info("Clean vector DB"); final stats = await clipVectorDB.getIndexStats(); logger.info("ClipVectorDB stats: size: ${stats.$1}, " "capacity: ${stats.$2}, dimensions: ${stats.$3}"); @@ -158,10 +162,13 @@ class _MLDebugSectionWidgetState extends State { "ClipVectorDB stats: size: ${stats.$1}, " "capacity: ${stats.$2}, dimensions: ${stats.$3}"); - final fileIDs = allClip.map((e) => e.fileID).toList(); - final embeddings = allClip + final fileIDs = allClipSmall.map((e) => e.fileID).toList(); + final embeddings = allClipSmall .map((e) => Float32List.fromList(e.vector.toList())) .toList(); + showShortToast(context, "Reshaped embeddings data"); + logger.info("Reshaped embeddings data"); + final now = DateTime.now(); await clipVectorDB.bulkInsertEmbeddings( fileIDs: fileIDs, @@ -185,6 +192,24 @@ class _MLDebugSectionWidgetState extends State { }, ), sectionOptionSpacing, + MenuItemWidget( + captionedTextWidget: const CaptionedTextWidget( + title: "Migrate to ClipVectorDB", + ), + pressedColor: getEnteColorScheme(context).fillFaint, + trailingIcon: Icons.chevron_right_outlined, + trailingIconIsMuted: true, + onTap: () async { + try { + await MLDataDB.instance.migrateFillClipVectorDB(); + showShortToast(context, "Migration done!"); + } catch (e, s) { + logger.warning('ClipVectorDB migration failed ', e, s); + await showGenericErrorDialog(context: context, error: e); + } + }, + ), + sectionOptionSpacing, MenuItemWidget( captionedTextWidget: const CaptionedTextWidget( title: "Show ClipVectorDB stats", From 63d90ea275f6d851d38fb37c48b27a36d2a8a552 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Fri, 9 May 2025 16:36:39 +0530 Subject: [PATCH 039/156] Class for vector db stats --- mobile/lib/db/ml/clip_vector_db.dart | 34 ++++++++++++++++--- .../debug/ml_debug_section_widget.dart | 26 ++++---------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/mobile/lib/db/ml/clip_vector_db.dart b/mobile/lib/db/ml/clip_vector_db.dart index 84d3261460..9454a7a128 100644 --- a/mobile/lib/db/ml/clip_vector_db.dart +++ b/mobile/lib/db/ml/clip_vector_db.dart @@ -122,14 +122,16 @@ class ClipVectorDB { } } - Future<(int, int, int)> getIndexStats() async { + Future getIndexStats() async { final db = await _vectorDB; try { final stats = await db.getIndexStats(); - return ( - stats.$1.toInt(), - stats.$2.toInt(), - stats.$3.toInt(), + return VectorDbStats( + size: stats.$1.toInt(), + capacity: stats.$2.toInt(), + dimensions: stats.$3.toInt(), + expansionAdd: stats.$4.toInt(), + expansionSearch: stats.$5.toInt(), ); } catch (e, s) { _logger.severe("Error getting index stats", e, s); @@ -165,3 +167,25 @@ class ClipVectorDB { } } } + +class VectorDbStats { + final int size; + final int capacity; + final int dimensions; + + final int expansionAdd; + final int expansionSearch; + + VectorDbStats({ + required this.size, + required this.capacity, + required this.dimensions, + required this.expansionAdd, + required this.expansionSearch, + }); + + @override + String toString() { + return "VectorDbStats(size: $size, capacity: $capacity, dimensions: $dimensions, expansionAdd: $expansionAdd, expansionSearch: $expansionSearch)"; + } +} diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index 7ccdd26f56..7010860b97 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -155,12 +155,8 @@ class _MLDebugSectionWidgetState extends State { await clipVectorDB.deleteAllEmbeddings(); logger.info("Clean vector DB"); final stats = await clipVectorDB.getIndexStats(); - logger.info("ClipVectorDB stats: size: ${stats.$1}, " - "capacity: ${stats.$2}, dimensions: ${stats.$3}"); - showShortToast( - context, - "ClipVectorDB stats: size: ${stats.$1}, " - "capacity: ${stats.$2}, dimensions: ${stats.$3}"); + logger.info(stats.toString()); + showShortToast(context, stats.toString()); final fileIDs = allClipSmall.map((e) => e.fileID).toList(); final embeddings = allClipSmall @@ -179,12 +175,8 @@ class _MLDebugSectionWidgetState extends State { "ClipVectorDB bulk insert took ${duration.inMilliseconds} ms for ${fileIDs.length} embeddings", ); final statsAfter = await clipVectorDB.getIndexStats(); - logger.info("ClipVectorDB stats after: size: ${statsAfter.$1}, " - "capacity: ${statsAfter.$2}, dimensions: ${statsAfter.$3}"); - showShortToast( - context, - "ClipVectorDB stats after: size: ${statsAfter.$1}, " - "capacity: ${statsAfter.$2}, dimensions: ${statsAfter.$3}"); + logger.info(statsAfter.toString()); + showShortToast(context, statsAfter.toString()); } catch (e, s) { logger.warning('ClipVectorDB migration failed ', e, s); await showGenericErrorDialog(context: context, error: e); @@ -201,7 +193,7 @@ class _MLDebugSectionWidgetState extends State { trailingIconIsMuted: true, onTap: () async { try { - await MLDataDB.instance.migrateFillClipVectorDB(); + await MLDataDB.instance.checkMigrateFillClipVectorDB(); showShortToast(context, "Migration done!"); } catch (e, s) { logger.warning('ClipVectorDB migration failed ', e, s); @@ -221,12 +213,8 @@ class _MLDebugSectionWidgetState extends State { try { final clipVectorDB = ClipVectorDB.instance; final stats = await clipVectorDB.getIndexStats(); - logger.info("ClipVectorDB stats: size: ${stats.$1}, " - "capacity: ${stats.$2}, dimensions: ${stats.$3}"); - showShortToast( - context, - "ClipVectorDB stats: size: ${stats.$1}, " - "capacity: ${stats.$2}, dimensions: ${stats.$3}"); + logger.info(stats.toString()); + showShortToast(context, stats.toString()); } catch (e, s) { logger.warning('ClipVectorDB stats failed ', e, s); await showGenericErrorDialog(context: context, error: e); From fc36b87965d6b3490138ab1bf26c0a2806b4dfd3 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Sun, 11 May 2025 13:09:20 +0530 Subject: [PATCH 040/156] Clip migration method --- mobile/lib/db/ml/db.dart | 89 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/mobile/lib/db/ml/db.dart b/mobile/lib/db/ml/db.dart index 0186ead274..e24103129d 100644 --- a/mobile/lib/db/ml/db.dart +++ b/mobile/lib/db/ml/db.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import "dart:io" show File; import "dart:math"; import "package:collection/collection.dart"; @@ -9,6 +10,7 @@ import 'package:path_provider/path_provider.dart'; import "package:photos/core/event_bus.dart"; import "package:photos/db/common/base.dart"; import "package:photos/db/ml/base.dart"; +import "package:photos/db/ml/clip_vector_db.dart"; import "package:photos/db/ml/db_model_mappers.dart"; import 'package:photos/db/ml/schema.dart'; import "package:photos/events/embedding_updated_event.dart"; @@ -81,6 +83,7 @@ class MLDataDB with SqlDbBase implements IMLDataDB { "MLDataDB Migration took ${stopwatch.elapsedMilliseconds} ms", ); stopwatch.stop(); + unawaited(checkMigrateFillClipVectorDB()); return asyncDBConnection; } @@ -1183,6 +1186,92 @@ class MLDataDB with SqlDbBase implements IMLDataDB { return embeddings; } + Future checkMigrateFillClipVectorDB({bool force = false}) async { + await Future.delayed(const Duration(milliseconds: 100)); + _logger.info("Checking if ClipVectorDB migration is needed"); + + // Check if vector DB migration has run + final documentsDirectory = await getApplicationDocumentsDirectory(); + final migrationFlagFile = + File(join(documentsDirectory.path, 'clip_vector_migration_done')); + if (await migrationFlagFile.exists() && !force) { + _logger.info("ClipVectorDB migration not needed, already done"); + return; + } + + // Get total count first to track progress + final db = await instance.asyncDB; + final countResult = + await db.getAll('SELECT COUNT($fileIDColumn) as total FROM $clipTable'); + final totalCount = countResult.first['total'] as int; + if (totalCount == 0) { + _logger.info("No clip embeddings to migrate"); + await migrationFlagFile.create(); + return; + } + + final clipVectorDB = ClipVectorDB.instance; + await clipVectorDB.deleteAllEmbeddings(); + + _logger + .info("Starting migration of $totalCount clip embeddings to vector DB"); + const batchSize = 5000; + int offset = 0; + int processedCount = 0; + int weirdCount = 0; + final stopwatch = Stopwatch()..start(); + try { + while (true) { + final List> results = await db.getAll(''' + SELECT $fileIDColumn, $embeddingColumn + FROM $clipTable + ORDER BY $fileIDColumn DESC + LIMIT $batchSize OFFSET $offset + '''); + if (results.isEmpty) { + break; + } + final List fileIDs = []; + final List embeddings = []; + for (final result in results) { + final embedding = + Float32List.view((result[embeddingColumn] as Uint8List).buffer); + if (embedding.length == 512) { + fileIDs.add(result[fileIDColumn] as int); + embeddings.add(Float32List.view(result[embeddingColumn].buffer)); + } else { + weirdCount++; + } + } + + await ClipVectorDB.instance + .bulkInsertEmbeddings(fileIDs: fileIDs, embeddings: embeddings); + processedCount += fileIDs.length; + offset += batchSize; + _logger.info( + "migrated $processedCount/$totalCount embeddings to ClipVectorDB", + ); + if (processedCount >= totalCount) { + break; + } + await Future.delayed(const Duration(milliseconds: 100)); + } + _logger.info( + "migrated all $totalCount embeddings to ClipVectorDB in ${stopwatch.elapsed.inMilliseconds} ms, with $weirdCount weird embeddings not migrated", + ); + await migrationFlagFile.create(); + } catch (e) { + _logger.severe( + "Error migrating ClipVectorDB after ${stopwatch.elapsed.inMilliseconds} ms, clearing out DB again", + e, + ); + await clipVectorDB.deleteAllEmbeddings(); + rethrow; + } finally { + stopwatch.stop(); + } + } + // Get indexed FileIDs @override Future> clipIndexedFileWithVersion() async { From df0d9137a6f7861b6a5eef95f81e2100c1349424 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Sun, 11 May 2025 13:09:56 +0530 Subject: [PATCH 041/156] Integration clip embeddings in vector db --- mobile/lib/db/ml/db.dart | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mobile/lib/db/ml/db.dart b/mobile/lib/db/ml/db.dart index e24103129d..daf4eace36 100644 --- a/mobile/lib/db/ml/db.dart +++ b/mobile/lib/db/ml/db.dart @@ -1305,12 +1305,21 @@ class MLDataDB with SqlDbBase implements IMLDataDB { 'INSERT OR REPLACE INTO $clipTable ($fileIDColumn, $embeddingColumn, $mlVersionColumn) VALUES (?, ?, ?)', _getRowFromEmbedding(embeddings.first), ); + await ClipVectorDB.instance.insertEmbedding( + fileID: embeddings.first.fileID, + embedding: embeddings.first.embedding, + ); } else { final inputs = embeddings.map((e) => _getRowFromEmbedding(e)).toList(); await db.executeBatch( 'INSERT OR REPLACE INTO $clipTable ($fileIDColumn, $embeddingColumn, $mlVersionColumn) values(?, ?, ?)', inputs, ); + await ClipVectorDB.instance.bulkInsertEmbeddings( + fileIDs: embeddings.map((e) => e.fileID).toList(), + embeddings: + embeddings.map((e) => Float32List.fromList(e.embedding)).toList(), + ); } Bus.instance.fire(EmbeddingUpdatedEvent()); } @@ -1321,6 +1330,7 @@ class MLDataDB with SqlDbBase implements IMLDataDB { await db.execute( 'DELETE FROM $clipTable WHERE $fileIDColumn IN (${fileIDs.join(", ")})', ); + await ClipVectorDB.instance.deleteEmbeddings(fileIDs); Bus.instance.fire(EmbeddingUpdatedEvent()); } @@ -1328,6 +1338,7 @@ class MLDataDB with SqlDbBase implements IMLDataDB { Future deleteClipIndexes() async { final db = await instance.asyncDB; await db.execute('DELETE FROM $clipTable'); + await ClipVectorDB.instance.deleteAllEmbeddings(); Bus.instance.fire(EmbeddingUpdatedEvent()); } From eb1916e3a383ade11ce9793c45da79e9a2d0b969 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 12 May 2025 11:39:02 +0530 Subject: [PATCH 042/156] integrate vector db in magic search --- mobile/lib/db/ml/clip_vector_db.dart | 37 +++++++++++++++++++ .../semantic_search_service.dart | 10 +++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/mobile/lib/db/ml/clip_vector_db.dart b/mobile/lib/db/ml/clip_vector_db.dart index 9454a7a128..a71c375eb6 100644 --- a/mobile/lib/db/ml/clip_vector_db.dart +++ b/mobile/lib/db/ml/clip_vector_db.dart @@ -5,6 +5,7 @@ import "package:logging/logging.dart"; import "package:path/path.dart"; import "package:path_provider/path_provider.dart"; import "package:photos/models/ml/vector.dart"; +import "package:photos/services/machine_learning/semantic_search/query_result.dart"; import "package:photos/src/rust/api/usearch_api.dart"; class ClipVectorDB { @@ -166,6 +167,42 @@ class ClipVectorDB { rethrow; } } + + Future>> computeBulkSimilarities( + Map> textQueryToEmbeddingMap, + Map minimumSimilarityMap, + ) async { + try { + final queryToResults = >{}; + for (final MapEntry> entry + in textQueryToEmbeddingMap.entries) { + final query = entry.key; + final minimumSimilarity = minimumSimilarityMap[query]!; + final textEmbedding = entry.value; + final (potentialFileIDs, distances) = + await searchClosestVectors(textEmbedding, 1000); + final queryResults = []; + for (var i = 0; i < potentialFileIDs.length; i++) { + final similarity = 1 - distances[i]; + if (similarity >= minimumSimilarity) { + queryResults + .add(QueryResult(potentialFileIDs[i].toInt(), similarity)); + } else { + break; + } + } + queryToResults[query] = queryResults; + } + return queryToResults; + } catch (e, s) { + _logger.severe( + "Could not bulk find embeddings similarities using vector DB", + e, + s, + ); + rethrow; + } + } } class VectorDbStats { diff --git a/mobile/lib/services/machine_learning/semantic_search/semantic_search_service.dart b/mobile/lib/services/machine_learning/semantic_search/semantic_search_service.dart index cf3930a782..5923ccc7b7 100644 --- a/mobile/lib/services/machine_learning/semantic_search/semantic_search_service.dart +++ b/mobile/lib/services/machine_learning/semantic_search/semantic_search_service.dart @@ -8,6 +8,7 @@ import "package:logging/logging.dart"; import "package:photos/core/cache/lru_map.dart"; import "package:photos/core/event_bus.dart"; import "package:photos/db/files_db.dart"; +import "package:photos/db/ml/clip_vector_db.dart"; import "package:photos/db/ml/db.dart"; import 'package:photos/events/embedding_updated_event.dart'; import "package:photos/models/file/file.dart"; @@ -265,9 +266,12 @@ class SemanticSearchService { required Map minimumSimilarityMap, }) async { final startTime = DateTime.now(); - await _cacheClipVectors(); - final Map> queryResults = await MLComputer - .instance + // TODO: lau: remove this when we feel confident about vector DB + // await _cacheClipVectors(); + // final Map> queryResults = await MLComputer + // .instance + // .computeBulkSimilarities(textQueryToEmbeddingMap, minimumSimilarityMap); + final queryResults = await ClipVectorDB.instance .computeBulkSimilarities(textQueryToEmbeddingMap, minimumSimilarityMap); final endTime = DateTime.now(); _logger.info( From 2422dba4d49d5d26e4337dd39dffedd9588f9105 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 12 May 2025 14:23:16 +0530 Subject: [PATCH 043/156] vector db more stats logging --- mobile/lib/db/ml/clip_vector_db.dart | 14 +++++-- mobile/lib/src/rust/api/usearch_api.dart | 3 +- mobile/lib/src/rust/frb_generated.dart | 49 +++++++++++++++-------- mobile/lib/src/rust/frb_generated.io.dart | 13 +++--- mobile/rust/src/api/usearch_api.rs | 16 +++++++- mobile/rust/src/frb_generated.rs | 12 ++++-- 6 files changed, 76 insertions(+), 31 deletions(-) diff --git a/mobile/lib/db/ml/clip_vector_db.dart b/mobile/lib/db/ml/clip_vector_db.dart index a71c375eb6..d3dd98d93c 100644 --- a/mobile/lib/db/ml/clip_vector_db.dart +++ b/mobile/lib/db/ml/clip_vector_db.dart @@ -131,8 +131,10 @@ class ClipVectorDB { size: stats.$1.toInt(), capacity: stats.$2.toInt(), dimensions: stats.$3.toInt(), - expansionAdd: stats.$4.toInt(), - expansionSearch: stats.$5.toInt(), + fileSize: stats.$4.toInt(), + memoryUsage: stats.$5.toInt(), + expansionAdd: stats.$6.toInt(), + expansionSearch: stats.$7.toInt(), ); } catch (e, s) { _logger.severe("Error getting index stats", e, s); @@ -210,6 +212,10 @@ class VectorDbStats { final int capacity; final int dimensions; + // in bytes + final int fileSize; + final int memoryUsage; + final int expansionAdd; final int expansionSearch; @@ -217,12 +223,14 @@ class VectorDbStats { required this.size, required this.capacity, required this.dimensions, + required this.fileSize, + required this.memoryUsage, required this.expansionAdd, required this.expansionSearch, }); @override String toString() { - return "VectorDbStats(size: $size, capacity: $capacity, dimensions: $dimensions, expansionAdd: $expansionAdd, expansionSearch: $expansionSearch)"; + return "VectorDbStats(size: $size, capacity: $capacity, dimensions: $dimensions, file size (bytes): $fileSize, memory usage (bytes): $memoryUsage, expansionAdd: $expansionAdd, expansionSearch: $expansionSearch)"; } } diff --git a/mobile/lib/src/rust/api/usearch_api.dart b/mobile/lib/src/rust/api/usearch_api.dart index 80f9a40fe5..bfb75345ea 100644 --- a/mobile/lib/src/rust/api/usearch_api.dart +++ b/mobile/lib/src/rust/api/usearch_api.dart @@ -30,7 +30,8 @@ abstract class VectorDb implements RustOpaqueInterface { Future deleteIndex(); - Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> getIndexStats(); + Future<(BigInt, BigInt, BigInt, BigInt, BigInt, BigInt, BigInt)> + getIndexStats(); Future getVector({required BigInt key}); diff --git a/mobile/lib/src/rust/frb_generated.dart b/mobile/lib/src/rust/frb_generated.dart index ef200514f3..599adba41d 100644 --- a/mobile/lib/src/rust/frb_generated.dart +++ b/mobile/lib/src/rust/frb_generated.dart @@ -110,7 +110,7 @@ abstract class RustLibApi extends BaseApi { Future crateApiUsearchApiVectorDbDeleteIndex({required VectorDb that}); - Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> + Future<(BigInt, BigInt, BigInt, BigInt, BigInt, BigInt, BigInt)> crateApiUsearchApiVectorDbGetIndexStats({required VectorDb that}); Future crateApiUsearchApiVectorDbGetVector( @@ -356,7 +356,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); @override - Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> + Future<(BigInt, BigInt, BigInt, BigInt, BigInt, BigInt, BigInt)> crateApiUsearchApiVectorDbGetIndexStats({required VectorDb that}) { return handler.executeNormal(NormalTask( callFfi: (port_) { @@ -367,7 +367,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { funcId: 8, port: port_); }, codec: SseCodec( - decodeSuccessData: sse_decode_record_usize_usize_usize_usize_usize, + decodeSuccessData: + sse_decode_record_usize_usize_usize_usize_usize_usize_usize, decodeErrorData: null, ), constMeta: kCrateApiUsearchApiVectorDbGetIndexStatsConstMeta, @@ -691,12 +692,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - (BigInt, BigInt, BigInt, BigInt, BigInt) - dco_decode_record_usize_usize_usize_usize_usize(dynamic raw) { + (BigInt, BigInt, BigInt, BigInt, BigInt, BigInt, BigInt) + dco_decode_record_usize_usize_usize_usize_usize_usize_usize(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 5) { - throw Exception('Expected 5 elements, got ${arr.length}'); + if (arr.length != 7) { + throw Exception('Expected 7 elements, got ${arr.length}'); } return ( dco_decode_usize(arr[0]), @@ -704,6 +705,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { dco_decode_usize(arr[2]), dco_decode_usize(arr[3]), dco_decode_usize(arr[4]), + dco_decode_usize(arr[5]), + dco_decode_usize(arr[6]), ); } @@ -852,8 +855,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - (BigInt, BigInt, BigInt, BigInt, BigInt) - sse_decode_record_usize_usize_usize_usize_usize( + (BigInt, BigInt, BigInt, BigInt, BigInt, BigInt, BigInt) + sse_decode_record_usize_usize_usize_usize_usize_usize_usize( SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs final var_field0 = sse_decode_usize(deserializer); @@ -861,7 +864,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final var_field2 = sse_decode_usize(deserializer); final var_field3 = sse_decode_usize(deserializer); final var_field4 = sse_decode_usize(deserializer); - return (var_field0, var_field1, var_field2, var_field3, var_field4); + final var_field5 = sse_decode_usize(deserializer); + final var_field6 = sse_decode_usize(deserializer); + return ( + var_field0, + var_field1, + var_field2, + var_field3, + var_field4, + var_field5, + var_field6 + ); } @protected @@ -1008,14 +1021,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_record_usize_usize_usize_usize_usize( - (BigInt, BigInt, BigInt, BigInt, BigInt) self, SseSerializer serializer) { + void sse_encode_record_usize_usize_usize_usize_usize_usize_usize( + (BigInt, BigInt, BigInt, BigInt, BigInt, BigInt, BigInt) self, + SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_usize(self.$1, serializer); sse_encode_usize(self.$2, serializer); sse_encode_usize(self.$3, serializer); sse_encode_usize(self.$4, serializer); sse_encode_usize(self.$5, serializer); + sse_encode_usize(self.$6, serializer); + sse_encode_usize(self.$7, serializer); } @protected @@ -1099,10 +1115,11 @@ class VectorDbImpl extends RustOpaque implements VectorDb { that: this, ); - Future<(BigInt, BigInt, BigInt, BigInt, BigInt)> getIndexStats() => - RustLib.instance.api.crateApiUsearchApiVectorDbGetIndexStats( - that: this, - ); + Future<(BigInt, BigInt, BigInt, BigInt, BigInt, BigInt, BigInt)> + getIndexStats() => + RustLib.instance.api.crateApiUsearchApiVectorDbGetIndexStats( + that: this, + ); Future getVector({required BigInt key}) => RustLib.instance.api .crateApiUsearchApiVectorDbGetVector(that: this, key: key); diff --git a/mobile/lib/src/rust/frb_generated.io.dart b/mobile/lib/src/rust/frb_generated.io.dart index 7e44cd6c83..cd17c917bc 100644 --- a/mobile/lib/src/rust/frb_generated.io.dart +++ b/mobile/lib/src/rust/frb_generated.io.dart @@ -79,8 +79,8 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { ) dco_decode_record_list_prim_u_64_strict_list_prim_f_32_strict(dynamic raw); @protected - (BigInt, BigInt, BigInt, BigInt, BigInt) - dco_decode_record_usize_usize_usize_usize_usize(dynamic raw); + (BigInt, BigInt, BigInt, BigInt, BigInt, BigInt, BigInt) + dco_decode_record_usize_usize_usize_usize_usize_usize_usize(dynamic raw); @protected BigInt dco_decode_u_64(dynamic raw); @@ -149,8 +149,8 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseDeserializer deserializer); @protected - (BigInt, BigInt, BigInt, BigInt, BigInt) - sse_decode_record_usize_usize_usize_usize_usize( + (BigInt, BigInt, BigInt, BigInt, BigInt, BigInt, BigInt) + sse_decode_record_usize_usize_usize_usize_usize_usize_usize( SseDeserializer deserializer); @protected @@ -225,8 +225,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { (Uint64List, Float32List) self, SseSerializer serializer); @protected - void sse_encode_record_usize_usize_usize_usize_usize( - (BigInt, BigInt, BigInt, BigInt, BigInt) self, SseSerializer serializer); + void sse_encode_record_usize_usize_usize_usize_usize_usize_usize( + (BigInt, BigInt, BigInt, BigInt, BigInt, BigInt, BigInt) self, + SseSerializer serializer); @protected void sse_encode_u_64(BigInt self, SseSerializer serializer); diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/rust/src/api/usearch_api.rs index 24975f86fd..457b45f7ff 100644 --- a/mobile/rust/src/api/usearch_api.rs +++ b/mobile/rust/src/api/usearch_api.rs @@ -164,13 +164,25 @@ impl VectorDB { } } - pub fn get_index_stats(&self) -> (usize, usize, usize, usize, usize) { + pub fn get_index_stats(&self) -> (usize, usize, usize, usize, usize, usize, usize) { let size = self.index.size(); let capacity = self.index.capacity(); let dimensions = self.index.dimensions(); + + let file_size = self.index.serialized_length(); + let memory_usage = self.index.memory_usage(); + let expansion_add = self.index.expansion_add(); let expansion_search = self.index.expansion_search(); - (size, capacity, dimensions, expansion_add, expansion_search) + ( + size, + capacity, + dimensions, + file_size, + memory_usage, + expansion_add, + expansion_search, + ) } } diff --git a/mobile/rust/src/frb_generated.rs b/mobile/rust/src/frb_generated.rs index 219c86123e..860f6e4fd3 100644 --- a/mobile/rust/src/frb_generated.rs +++ b/mobile/rust/src/frb_generated.rs @@ -904,7 +904,7 @@ impl SseDecode for (Vec, Vec) { } } -impl SseDecode for (usize, usize, usize, usize, usize) { +impl SseDecode for (usize, usize, usize, usize, usize, usize, usize) { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { let mut var_field0 = ::sse_decode(deserializer); @@ -912,7 +912,11 @@ impl SseDecode for (usize, usize, usize, usize, usize) { let mut var_field2 = ::sse_decode(deserializer); let mut var_field3 = ::sse_decode(deserializer); let mut var_field4 = ::sse_decode(deserializer); - return (var_field0, var_field1, var_field2, var_field3, var_field4); + let mut var_field5 = ::sse_decode(deserializer); + let mut var_field6 = ::sse_decode(deserializer); + return ( + var_field0, var_field1, var_field2, var_field3, var_field4, var_field5, var_field6, + ); } } @@ -1171,7 +1175,7 @@ impl SseEncode for (Vec, Vec) { } } -impl SseEncode for (usize, usize, usize, usize, usize) { +impl SseEncode for (usize, usize, usize, usize, usize, usize, usize) { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { ::sse_encode(self.0, serializer); @@ -1179,6 +1183,8 @@ impl SseEncode for (usize, usize, usize, usize, usize) { ::sse_encode(self.2, serializer); ::sse_encode(self.3, serializer); ::sse_encode(self.4, serializer); + ::sse_encode(self.5, serializer); + ::sse_encode(self.6, serializer); } } From 62ed8b697517f95794104c06ba112e052aa39a7e Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 12 May 2025 14:46:55 +0530 Subject: [PATCH 044/156] Log vector db stats when opening connection --- mobile/lib/db/ml/clip_vector_db.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mobile/lib/db/ml/clip_vector_db.dart b/mobile/lib/db/ml/clip_vector_db.dart index d3dd98d93c..dc5597d07d 100644 --- a/mobile/lib/db/ml/clip_vector_db.dart +++ b/mobile/lib/db/ml/clip_vector_db.dart @@ -39,6 +39,8 @@ class ClipVectorDB { filePath: databaseDirectory, dimensions: _embeddingDimension, ); + final stats = await getIndexStats(vectorDB); + _logger.info("VectorDB connection opened with stats: ${stats.toString()}"); return vectorDB; } @@ -123,8 +125,8 @@ class ClipVectorDB { } } - Future getIndexStats() async { - final db = await _vectorDB; + Future getIndexStats([VectorDb? db]) async { + db ??= await _vectorDB; try { final stats = await db.getIndexStats(); return VectorDbStats( @@ -231,6 +233,6 @@ class VectorDbStats { @override String toString() { - return "VectorDbStats(size: $size, capacity: $capacity, dimensions: $dimensions, file size (bytes): $fileSize, memory usage (bytes): $memoryUsage, expansionAdd: $expansionAdd, expansionSearch: $expansionSearch)"; + return "VectorDbStats(size: $size, capacity: $capacity, dimensions: $dimensions, file size on disk (bytes): $fileSize, memory usage (bytes): $memoryUsage, expansionAdd: $expansionAdd, expansionSearch: $expansionSearch)"; } } From 3b9c76649d7430b57e2996ac1fa4fdae2d963cfb Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 12 May 2025 15:12:31 +0530 Subject: [PATCH 045/156] Update readme to include rust --- mobile/README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/mobile/README.md b/mobile/README.md index 958f7c4215..849abaa44d 100644 --- a/mobile/README.md +++ b/mobile/README.md @@ -46,25 +46,27 @@ You can alternatively install the build from PlayStore or F-Droid. ## 🧑‍💻 Building from source -1. [Install Flutter v3.24.3](https://flutter.dev/docs/get-started/install). +1. Install [Flutter v3.24.3](https://flutter.dev/docs/get-started/install) and [Rust](https://www.rust-lang.org/tools/install). -2. Pull in all submodules with `git submodule update --init --recursive` +2. Install [Flutter Rust Bridge](https://cjycode.com/flutter_rust_bridge/) with `cargo install flutter_rust_bridge_codegen` -3. Enable repo git hooks `git config core.hooksPath hooks` +3. Pull in all submodules with `git submodule update --init --recursive` -4. If using Visual Studio Code, add the [Flutter +4. Enable repo git hooks `git config core.hooksPath hooks` + +5. If using Visual Studio Code, add the [Flutter Intl](https://marketplace.visualstudio.com/items?itemName=localizely.flutter-intl) extension -5. On Android: +6. On Android: - * For development, run `flutter run -t lib/main.dart --flavor independent` + - For development, run `flutter run -t lib/main.dart --flavor independent` - * For building APK, [setup your + - For building APK, [setup your keystore](https://docs.flutter.dev/deployment/android#create-an-upload-keystore) and run `flutter build apk --release --flavor independent` -6. For iOS, run `flutter build ios` +7. For iOS, run `flutter build ios` Some common issues and troubleshooting tips are in [docs/dev](docs/dev.md). @@ -88,11 +90,12 @@ issue](https://github.com/ente-io/ente/issues/new?title=Request+for+New+Language to have it added. ## Certificate Fingerprints - + - **SHA1**: E1:60:10:18:B6:B0:2E:A3:74:6F:90:67:50:30:29:75:0E:EF:6D:39 - **SHA256**: 35:ED:56:81:B7:0B:B3:BD:35:D9:0D:85:6A:F5:69:4C:50:4D:EF:46:AA:D8:3F:77:7B:1C:67:5C:F4:51:35:0B To verify these fingerprints, use the following command: + ```bash apksigner verify --print-certs ``` From b76d41b84d99d2e8edc4d8c16cc4fb07cd3aecfb Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 12 May 2025 15:48:27 +0530 Subject: [PATCH 046/156] Specify rust version in readme --- mobile/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/README.md b/mobile/README.md index 849abaa44d..5afc19e009 100644 --- a/mobile/README.md +++ b/mobile/README.md @@ -46,7 +46,7 @@ You can alternatively install the build from PlayStore or F-Droid. ## 🧑‍💻 Building from source -1. Install [Flutter v3.24.3](https://flutter.dev/docs/get-started/install) and [Rust](https://www.rust-lang.org/tools/install). +1. Install [Flutter v3.24.3](https://flutter.dev/docs/get-started/install) and [Rust v1.85.1](https://www.rust-lang.org/tools/install). 2. Install [Flutter Rust Bridge](https://cjycode.com/flutter_rust_bridge/) with `cargo install flutter_rust_bridge_codegen` From 7021c9fe0211ea78eddfa904124741fc57a5181f Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 12 May 2025 17:05:31 +0530 Subject: [PATCH 047/156] Bump for internal release --- mobile/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index f51773166d..5cb34c06dc 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -12,7 +12,7 @@ description: ente photos application # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.10+1040 +version: 1.0.11+1042 publish_to: none environment: From beb049f8176bdf522f80d356ea1ffef4b4390ca5 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 22 May 2025 10:38:43 +0530 Subject: [PATCH 048/156] Reduce batch size in migration --- mobile/lib/db/ml/db.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/lib/db/ml/db.dart b/mobile/lib/db/ml/db.dart index daf4eace36..ab0a7759fa 100644 --- a/mobile/lib/db/ml/db.dart +++ b/mobile/lib/db/ml/db.dart @@ -1215,7 +1215,7 @@ class MLDataDB with SqlDbBase implements IMLDataDB { _logger .info("Starting migration of $totalCount clip embeddings to vector DB"); - const batchSize = 5000; + const batchSize = 1000; int offset = 0; int processedCount = 0; int weirdCount = 0; From e60c2b1192eeb87b5d3746454e774a6b3b8c3d2b Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 22 May 2025 11:19:19 +0530 Subject: [PATCH 049/156] GC hints --- mobile/lib/db/ml/db.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mobile/lib/db/ml/db.dart b/mobile/lib/db/ml/db.dart index ab0a7759fa..4891d0de92 100644 --- a/mobile/lib/db/ml/db.dart +++ b/mobile/lib/db/ml/db.dart @@ -1222,6 +1222,9 @@ class MLDataDB with SqlDbBase implements IMLDataDB { final stopwatch = Stopwatch()..start(); try { while (true) { + // Allow some time for any GC to finish + await Future.delayed(const Duration(milliseconds: 100)); + final List> results = await db.getAll(''' SELECT $fileIDColumn, $embeddingColumn FROM $clipTable @@ -1254,6 +1257,10 @@ class MLDataDB with SqlDbBase implements IMLDataDB { if (processedCount >= totalCount) { break; } + embeddings.clear(); + fileIDs.clear(); + results.clear(); + // Allow some time for any GC to finish await Future.delayed(const Duration(milliseconds: 100)); } _logger.info( From c068f2660449375818111730604c3553f75fbeeb Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 22 May 2025 11:32:36 +0530 Subject: [PATCH 050/156] Aggressive logging of vectorDB migration --- mobile/lib/db/ml/db.dart | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/mobile/lib/db/ml/db.dart b/mobile/lib/db/ml/db.dart index 4891d0de92..f33e1ef3ef 100644 --- a/mobile/lib/db/ml/db.dart +++ b/mobile/lib/db/ml/db.dart @@ -83,6 +83,7 @@ class MLDataDB with SqlDbBase implements IMLDataDB { "MLDataDB Migration took ${stopwatch.elapsedMilliseconds} ms", ); stopwatch.stop(); + _logger.info("Starting CLIP vector DB migration check unawaited"); unawaited(checkMigrateFillClipVectorDB()); return asyncDBConnection; @@ -1187,10 +1188,12 @@ class MLDataDB with SqlDbBase implements IMLDataDB { } Future checkMigrateFillClipVectorDB({bool force = false}) async { + _logger.info("Waiting for ClipVectorDB to be ready"); await Future.delayed(const Duration(milliseconds: 100)); _logger.info("Checking if ClipVectorDB migration is needed"); // Check if vector DB migration has run + _logger.info("Checking if ClipVectorDB migration has run"); final documentsDirectory = await getApplicationDocumentsDirectory(); final migrationFlagFile = File(join(documentsDirectory.path, 'clip_vector_migration_done')); @@ -1200,6 +1203,7 @@ class MLDataDB with SqlDbBase implements IMLDataDB { } // Get total count first to track progress + _logger.info("Getting total count of clip embeddings"); final db = await instance.asyncDB; final countResult = await db.getAll('SELECT COUNT($fileIDColumn) as total FROM $clipTable'); @@ -1209,9 +1213,13 @@ class MLDataDB with SqlDbBase implements IMLDataDB { await migrationFlagFile.create(); return; } + _logger.info("Total count of clip embeddings: $totalCount"); + _logger.info("First time referencing ClipVectorDB in migration"); final clipVectorDB = ClipVectorDB.instance; + _logger.info("ClipVectorDB referenced"); await clipVectorDB.deleteAllEmbeddings(); + _logger.info("ClipVectorDB all embeddings cleared"); _logger .info("Starting migration of $totalCount clip embeddings to vector DB"); @@ -1219,21 +1227,28 @@ class MLDataDB with SqlDbBase implements IMLDataDB { int offset = 0; int processedCount = 0; int weirdCount = 0; + int whileCount = 0; final stopwatch = Stopwatch()..start(); try { while (true) { + whileCount++; + _logger.info("$whileCount st round of while loop"); // Allow some time for any GC to finish await Future.delayed(const Duration(milliseconds: 100)); + _logger.info("Reading $batchSize rows from DB"); final List> results = await db.getAll(''' SELECT $fileIDColumn, $embeddingColumn FROM $clipTable ORDER BY $fileIDColumn DESC LIMIT $batchSize OFFSET $offset '''); + _logger.info("Got ${results.length} results from DB"); if (results.isEmpty) { + _logger.info("No more results, breaking out of while loop"); break; } + _logger.info("Processing ${results.length} results"); final List fileIDs = []; final List embeddings = []; for (final result in results) { @@ -1246,27 +1261,35 @@ class MLDataDB with SqlDbBase implements IMLDataDB { weirdCount++; } } + _logger.info( + "Got ${fileIDs.length} valid embeddings, $weirdCount weird embeddings", + ); await ClipVectorDB.instance .bulkInsertEmbeddings(fileIDs: fileIDs, embeddings: embeddings); + _logger.info("Inserted ${fileIDs.length} embeddings to ClipVectorDB"); processedCount += fileIDs.length; offset += batchSize; _logger.info( "migrated $processedCount/$totalCount embeddings to ClipVectorDB", ); if (processedCount >= totalCount) { + _logger.info("All embeddings migrated, breaking out of while loop"); break; } + _logger.info("Clearing out embeddings and fileIDs"); embeddings.clear(); fileIDs.clear(); results.clear(); // Allow some time for any GC to finish + _logger.info("Waiting for 100ms for GC to finish"); await Future.delayed(const Duration(milliseconds: 100)); } _logger.info( "migrated all $totalCount embeddings to ClipVectorDB in ${stopwatch.elapsed.inMilliseconds} ms, with $weirdCount weird embeddings not migrated", ); await migrationFlagFile.create(); + _logger.info("ClipVectorDB migration done, flag file created"); } catch (e) { _logger.severe( "Error migrating ClipVectorDB after ${stopwatch.elapsed.inMilliseconds} ms, clearing out DB again", From 726425bbb606bfe55cea4a70e5802a1997a96580 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 27 May 2025 14:07:34 +0530 Subject: [PATCH 051/156] Put vector db behind feature flag internal --- mobile/lib/db/ml/db.dart | 33 ++++++++++++------- .../semantic_search_service.dart | 26 +++++++++++---- .../ente_feature_flag/lib/src/service.dart | 2 ++ 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/mobile/lib/db/ml/db.dart b/mobile/lib/db/ml/db.dart index f33e1ef3ef..cc598da25c 100644 --- a/mobile/lib/db/ml/db.dart +++ b/mobile/lib/db/ml/db.dart @@ -20,6 +20,7 @@ import "package:photos/models/ml/face/face.dart"; import "package:photos/models/ml/face/face_with_embedding.dart"; import "package:photos/models/ml/ml_versions.dart"; import "package:photos/models/ml/vector.dart"; +import "package:photos/service_locator.dart"; import "package:photos/services/machine_learning/face_ml/face_clustering/face_db_info_for_clustering.dart"; import 'package:photos/services/machine_learning/face_ml/face_filtering/face_filtering_constants.dart'; import "package:photos/services/machine_learning/ml_result.dart"; @@ -84,7 +85,7 @@ class MLDataDB with SqlDbBase implements IMLDataDB { ); stopwatch.stop(); _logger.info("Starting CLIP vector DB migration check unawaited"); - unawaited(checkMigrateFillClipVectorDB()); + if (flagService.enableVectorDb) unawaited(checkMigrateFillClipVectorDB()); return asyncDBConnection; } @@ -1335,21 +1336,25 @@ class MLDataDB with SqlDbBase implements IMLDataDB { 'INSERT OR REPLACE INTO $clipTable ($fileIDColumn, $embeddingColumn, $mlVersionColumn) VALUES (?, ?, ?)', _getRowFromEmbedding(embeddings.first), ); - await ClipVectorDB.instance.insertEmbedding( - fileID: embeddings.first.fileID, - embedding: embeddings.first.embedding, - ); + if (flagService.enableVectorDb) { + await ClipVectorDB.instance.insertEmbedding( + fileID: embeddings.first.fileID, + embedding: embeddings.first.embedding, + ); + } } else { final inputs = embeddings.map((e) => _getRowFromEmbedding(e)).toList(); await db.executeBatch( 'INSERT OR REPLACE INTO $clipTable ($fileIDColumn, $embeddingColumn, $mlVersionColumn) values(?, ?, ?)', inputs, ); - await ClipVectorDB.instance.bulkInsertEmbeddings( - fileIDs: embeddings.map((e) => e.fileID).toList(), - embeddings: - embeddings.map((e) => Float32List.fromList(e.embedding)).toList(), - ); + if (flagService.enableVectorDb) { + await ClipVectorDB.instance.bulkInsertEmbeddings( + fileIDs: embeddings.map((e) => e.fileID).toList(), + embeddings: + embeddings.map((e) => Float32List.fromList(e.embedding)).toList(), + ); + } } Bus.instance.fire(EmbeddingUpdatedEvent()); } @@ -1360,7 +1365,9 @@ class MLDataDB with SqlDbBase implements IMLDataDB { await db.execute( 'DELETE FROM $clipTable WHERE $fileIDColumn IN (${fileIDs.join(", ")})', ); - await ClipVectorDB.instance.deleteEmbeddings(fileIDs); + if (flagService.enableVectorDb) { + await ClipVectorDB.instance.deleteEmbeddings(fileIDs); + } Bus.instance.fire(EmbeddingUpdatedEvent()); } @@ -1368,7 +1375,9 @@ class MLDataDB with SqlDbBase implements IMLDataDB { Future deleteClipIndexes() async { final db = await instance.asyncDB; await db.execute('DELETE FROM $clipTable'); - await ClipVectorDB.instance.deleteAllEmbeddings(); + if (flagService.enableVectorDb) { + await ClipVectorDB.instance.deleteAllEmbeddings(); + } Bus.instance.fire(EmbeddingUpdatedEvent()); } diff --git a/mobile/lib/services/machine_learning/semantic_search/semantic_search_service.dart b/mobile/lib/services/machine_learning/semantic_search/semantic_search_service.dart index 5923ccc7b7..f2b18e515b 100644 --- a/mobile/lib/services/machine_learning/semantic_search/semantic_search_service.dart +++ b/mobile/lib/services/machine_learning/semantic_search/semantic_search_service.dart @@ -266,13 +266,25 @@ class SemanticSearchService { required Map minimumSimilarityMap, }) async { final startTime = DateTime.now(); - // TODO: lau: remove this when we feel confident about vector DB - // await _cacheClipVectors(); - // final Map> queryResults = await MLComputer - // .instance - // .computeBulkSimilarities(textQueryToEmbeddingMap, minimumSimilarityMap); - final queryResults = await ClipVectorDB.instance - .computeBulkSimilarities(textQueryToEmbeddingMap, minimumSimilarityMap); + if (kDebugMode) { + for (final queryText in textQueryToEmbeddingMap.keys) { + final embedding = textQueryToEmbeddingMap[queryText]!; + dev.log("CLIPTEXT Query: $queryText, embedding: $embedding"); + } + } + late final Map> queryResults; + if (flagService.enableVectorDb) { + queryResults = await ClipVectorDB.instance.computeBulkSimilarities( + textQueryToEmbeddingMap, + minimumSimilarityMap, + ); + } else { + await _cacheClipVectors(); + queryResults = await MLComputer.instance.computeBulkSimilarities( + textQueryToEmbeddingMap, + minimumSimilarityMap, + ); + } final endTime = DateTime.now(); _logger.info( "computingSimilarities took for ${textQueryToEmbeddingMap.length} queries " + diff --git a/mobile/plugins/ente_feature_flag/lib/src/service.dart b/mobile/plugins/ente_feature_flag/lib/src/service.dart index 376f6b6e48..dddd32aab4 100644 --- a/mobile/plugins/ente_feature_flag/lib/src/service.dart +++ b/mobile/plugins/ente_feature_flag/lib/src/service.dart @@ -58,6 +58,8 @@ class FlagService { bool get enableMobMultiPart => flags.enableMobMultiPart || internalUser; + bool get enableVectorDb => flags.internalUser; + String get castUrl => flags.castUrl; Future setMapEnabled(bool isEnabled) async { From ecfa640c2861b16b6edac8f904c73a173ec8a0dc Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Fri, 30 May 2025 15:44:46 +0530 Subject: [PATCH 052/156] Bump version --- mobile/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index f4f4f5abee..513aca5754 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -12,7 +12,7 @@ description: ente photos application # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.0+1053 +version: 1.1.1+1054 publish_to: none environment: From 7c4e7758726e18b7712191525262d479dd511109 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Mon, 2 Jun 2025 14:56:04 +0530 Subject: [PATCH 053/156] Bump build number --- mobile/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 513aca5754..ce951b8a44 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -12,7 +12,7 @@ description: ente photos application # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.1+1054 +version: 1.1.1+1055 publish_to: none environment: From 655be76428892256b561db4d778073eabe1c2a0a Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 2 Jun 2025 17:55:06 +0530 Subject: [PATCH 054/156] Bump version --- mobile/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 513aca5754..caddc515ac 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -12,7 +12,7 @@ description: ente photos application # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.1+1054 +version: 1.1.2+1055 publish_to: none environment: From 8b3b20aa93766135ebdd65e254e6f1d050a5ee38 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 2 Jun 2025 18:38:45 +0530 Subject: [PATCH 055/156] Remove unsued type --- mobile/lib/ui/collections/collection_action_sheet.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/mobile/lib/ui/collections/collection_action_sheet.dart b/mobile/lib/ui/collections/collection_action_sheet.dart index 19ce4762f4..b0441c21d3 100644 --- a/mobile/lib/ui/collections/collection_action_sheet.dart +++ b/mobile/lib/ui/collections/collection_action_sheet.dart @@ -56,9 +56,6 @@ String _actionName( case CollectionActionType.shareCollection: text = S.of(context).share; break; - case CollectionActionType.collectPhotos: - text = S.of(context).share; - break; case CollectionActionType.addToHiddenAlbum: text = S.of(context).addToHiddenAlbum; break; From cc1660d9af7616d3e3bf5e1d1a8dc253c37907f5 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Wed, 4 Jun 2025 18:24:59 +0530 Subject: [PATCH 056/156] bump up build number" --- mobile/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index caddc515ac..20047e62ca 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -12,7 +12,7 @@ description: ente photos application # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.2+1055 +version: 1.1.2+1056 publish_to: none environment: From 94098d8a0761863a47f5626ad6dd3974ab054b98 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:35:30 +0530 Subject: [PATCH 057/156] Bump version --- mobile/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 5cf59e8361..33556c06e1 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -12,7 +12,7 @@ description: ente photos application # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.2+1056 +version: 1.1.3+1057 publish_to: none environment: From 1f1304ca5be1f2541983901611b0487b8a699fd5 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 9 Jun 2025 12:31:38 +0530 Subject: [PATCH 058/156] Upgrade usearch to fix Armv8-R issues --- mobile/rust/Cargo.lock | 4 ++-- mobile/rust/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile/rust/Cargo.lock b/mobile/rust/Cargo.lock index 7f4c444b5b..4bc9161bd8 100644 --- a/mobile/rust/Cargo.lock +++ b/mobile/rust/Cargo.lock @@ -670,9 +670,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "usearch" -version = "2.17.7" +version = "2.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b64a1f5433c83756330d079ba276476b279728da042057dc23a002f27ca32326" +checksum = "908331accde6ff6bfe83e1f2dfd4cc77343d107bfacecd2f19b7a14d87cdb2df" dependencies = [ "cxx", "cxx-build", diff --git a/mobile/rust/Cargo.toml b/mobile/rust/Cargo.toml index 56d45a437c..e0e3a5c0c0 100644 --- a/mobile/rust/Cargo.toml +++ b/mobile/rust/Cargo.toml @@ -8,7 +8,7 @@ crate-type = ["cdylib", "staticlib"] [dependencies] flutter_rust_bridge = "=2.9.0" -usearch = "2.17.7" +usearch = "2.17.11" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] } From 889aed602439ae66c587e9e72960c1dc9fd7efdb Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 9 Jun 2025 12:34:29 +0530 Subject: [PATCH 059/156] Bump for internal release --- mobile/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 33556c06e1..06a3a18b4d 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -12,7 +12,7 @@ description: ente photos application # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.3+1057 +version: 1.1.31+1058 publish_to: none environment: From 3ad94f362abccb6128f6a0e13ed1a133181f9b7a Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 23 Jul 2025 17:06:54 +0200 Subject: [PATCH 060/156] Resolve merge conflicts --- auth/assets/simple-icons | 1 + auth/flutter | 1 + .../photos}/flutter_rust_bridge.yaml | 0 .../photos}/lib/src/rust/api/simple.dart | 0 .../photos}/lib/src/rust/api/usearch_api.dart | 0 .../photos}/lib/src/rust/frb_generated.dart | 0 .../lib/src/rust/frb_generated.io.dart | 0 mobile/apps/photos/pubspec.lock | 114 +++++++++--------- mobile/{ => apps/photos}/rust/.gitignore | 0 mobile/{ => apps/photos}/rust/Cargo.lock | 0 mobile/{ => apps/photos}/rust/Cargo.toml | 0 mobile/{ => apps/photos}/rust/src/api/mod.rs | 0 .../{ => apps/photos}/rust/src/api/simple.rs | 0 .../photos}/rust/src/api/usearch_api.rs | 0 .../photos}/rust/src/frb_generated.rs | 0 mobile/{ => apps/photos}/rust/src/lib.rs | 0 .../{ => apps/photos}/rust_builder/.gitignore | 0 .../{ => apps/photos}/rust_builder/README.md | 0 .../photos}/rust_builder/android/.gitignore | 0 .../photos}/rust_builder/android/build.gradle | 0 .../rust_builder/android/settings.gradle | 0 .../android/src/main/AndroidManifest.xml | 0 .../photos}/rust_builder/cargokit/.gitignore | 0 .../photos}/rust_builder/cargokit/LICENSE | 0 .../photos}/rust_builder/cargokit/README | 0 .../rust_builder/cargokit/build_pod.sh | 0 .../cargokit/build_tool/README.md | 0 .../cargokit/build_tool/analysis_options.yaml | 0 .../cargokit/build_tool/bin/build_tool.dart | 0 .../cargokit/build_tool/lib/build_tool.dart | 0 .../lib/src/android_environment.dart | 0 .../lib/src/artifacts_provider.dart | 0 .../build_tool/lib/src/build_cmake.dart | 0 .../build_tool/lib/src/build_gradle.dart | 0 .../build_tool/lib/src/build_pod.dart | 0 .../build_tool/lib/src/build_tool.dart | 0 .../cargokit/build_tool/lib/src/builder.dart | 0 .../cargokit/build_tool/lib/src/cargo.dart | 0 .../build_tool/lib/src/crate_hash.dart | 0 .../build_tool/lib/src/environment.dart | 0 .../cargokit/build_tool/lib/src/logging.dart | 0 .../cargokit/build_tool/lib/src/options.dart | 0 .../lib/src/precompile_binaries.dart | 0 .../cargokit/build_tool/lib/src/rustup.dart | 0 .../cargokit/build_tool/lib/src/target.dart | 0 .../cargokit/build_tool/lib/src/util.dart | 0 .../build_tool/lib/src/verify_binaries.dart | 0 .../cargokit/build_tool/pubspec.lock | 0 .../cargokit/build_tool/pubspec.yaml | 0 .../cargokit/cmake/cargokit.cmake | 0 .../cargokit/cmake/resolve_symlinks.ps1 | 0 .../cargokit/gradle/plugin.gradle | 0 .../rust_builder/cargokit/run_build_tool.cmd | 0 .../rust_builder/cargokit/run_build_tool.sh | 0 .../rust_builder/ios/Classes/dummy_file.c | 0 .../rust_builder/ios/rust_lib_photos.podspec | 0 .../photos}/rust_builder/linux/CMakeLists.txt | 0 .../rust_builder/macos/Classes/dummy_file.c | 0 .../macos/rust_lib_photos.podspec | 0 .../photos}/rust_builder/pubspec.yaml | 0 .../photos}/rust_builder/windows/.gitignore | 0 .../rust_builder/windows/CMakeLists.txt | 0 web/apps/photos/thirdparty/ffmpeg-wasm | 1 + web/apps/photos/thirdparty/photoswipe | 1 + 64 files changed, 61 insertions(+), 57 deletions(-) create mode 160000 auth/assets/simple-icons create mode 160000 auth/flutter rename mobile/{ => apps/photos}/flutter_rust_bridge.yaml (100%) rename mobile/{ => apps/photos}/lib/src/rust/api/simple.dart (100%) rename mobile/{ => apps/photos}/lib/src/rust/api/usearch_api.dart (100%) rename mobile/{ => apps/photos}/lib/src/rust/frb_generated.dart (100%) rename mobile/{ => apps/photos}/lib/src/rust/frb_generated.io.dart (100%) rename mobile/{ => apps/photos}/rust/.gitignore (100%) rename mobile/{ => apps/photos}/rust/Cargo.lock (100%) rename mobile/{ => apps/photos}/rust/Cargo.toml (100%) rename mobile/{ => apps/photos}/rust/src/api/mod.rs (100%) rename mobile/{ => apps/photos}/rust/src/api/simple.rs (100%) rename mobile/{ => apps/photos}/rust/src/api/usearch_api.rs (100%) rename mobile/{ => apps/photos}/rust/src/frb_generated.rs (100%) rename mobile/{ => apps/photos}/rust/src/lib.rs (100%) rename mobile/{ => apps/photos}/rust_builder/.gitignore (100%) rename mobile/{ => apps/photos}/rust_builder/README.md (100%) rename mobile/{ => apps/photos}/rust_builder/android/.gitignore (100%) rename mobile/{ => apps/photos}/rust_builder/android/build.gradle (100%) rename mobile/{ => apps/photos}/rust_builder/android/settings.gradle (100%) rename mobile/{ => apps/photos}/rust_builder/android/src/main/AndroidManifest.xml (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/.gitignore (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/LICENSE (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/README (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_pod.sh (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/README.md (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/analysis_options.yaml (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/bin/build_tool.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/build_tool.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/android_environment.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/build_pod.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/build_tool.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/builder.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/cargo.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/environment.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/logging.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/options.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/rustup.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/target.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/util.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/pubspec.lock (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/build_tool/pubspec.yaml (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/cmake/cargokit.cmake (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/cmake/resolve_symlinks.ps1 (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/gradle/plugin.gradle (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/run_build_tool.cmd (100%) rename mobile/{ => apps/photos}/rust_builder/cargokit/run_build_tool.sh (100%) rename mobile/{ => apps/photos}/rust_builder/ios/Classes/dummy_file.c (100%) rename mobile/{ => apps/photos}/rust_builder/ios/rust_lib_photos.podspec (100%) rename mobile/{ => apps/photos}/rust_builder/linux/CMakeLists.txt (100%) rename mobile/{ => apps/photos}/rust_builder/macos/Classes/dummy_file.c (100%) rename mobile/{ => apps/photos}/rust_builder/macos/rust_lib_photos.podspec (100%) rename mobile/{ => apps/photos}/rust_builder/pubspec.yaml (100%) rename mobile/{ => apps/photos}/rust_builder/windows/.gitignore (100%) rename mobile/{ => apps/photos}/rust_builder/windows/CMakeLists.txt (100%) create mode 160000 web/apps/photos/thirdparty/ffmpeg-wasm create mode 160000 web/apps/photos/thirdparty/photoswipe diff --git a/auth/assets/simple-icons b/auth/assets/simple-icons new file mode 160000 index 0000000000..6dcfdc2f58 --- /dev/null +++ b/auth/assets/simple-icons @@ -0,0 +1 @@ +Subproject commit 6dcfdc2f58f6150d7b097b5cab158d27413f6366 diff --git a/auth/flutter b/auth/flutter new file mode 160000 index 0000000000..2663184aa7 --- /dev/null +++ b/auth/flutter @@ -0,0 +1 @@ +Subproject commit 2663184aa79047d0a33a14a3b607954f8fdd8730 diff --git a/mobile/flutter_rust_bridge.yaml b/mobile/apps/photos/flutter_rust_bridge.yaml similarity index 100% rename from mobile/flutter_rust_bridge.yaml rename to mobile/apps/photos/flutter_rust_bridge.yaml diff --git a/mobile/lib/src/rust/api/simple.dart b/mobile/apps/photos/lib/src/rust/api/simple.dart similarity index 100% rename from mobile/lib/src/rust/api/simple.dart rename to mobile/apps/photos/lib/src/rust/api/simple.dart diff --git a/mobile/lib/src/rust/api/usearch_api.dart b/mobile/apps/photos/lib/src/rust/api/usearch_api.dart similarity index 100% rename from mobile/lib/src/rust/api/usearch_api.dart rename to mobile/apps/photos/lib/src/rust/api/usearch_api.dart diff --git a/mobile/lib/src/rust/frb_generated.dart b/mobile/apps/photos/lib/src/rust/frb_generated.dart similarity index 100% rename from mobile/lib/src/rust/frb_generated.dart rename to mobile/apps/photos/lib/src/rust/frb_generated.dart diff --git a/mobile/lib/src/rust/frb_generated.io.dart b/mobile/apps/photos/lib/src/rust/frb_generated.io.dart similarity index 100% rename from mobile/lib/src/rust/frb_generated.io.dart rename to mobile/apps/photos/lib/src/rust/frb_generated.io.dart diff --git a/mobile/apps/photos/pubspec.lock b/mobile/apps/photos/pubspec.lock index 23ca0baa97..69b979d425 100644 --- a/mobile/apps/photos/pubspec.lock +++ b/mobile/apps/photos/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 + sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" url: "https://pub.dev" source: hosted - version: "72.0.0" + version: "76.0.0" _flutterfire_internals: dependency: transitive description: @@ -21,7 +21,7 @@ packages: dependency: transitive description: dart source: sdk - version: "0.3.2" + version: "0.3.3" adaptive_theme: dependency: "direct main" description: @@ -34,10 +34,10 @@ packages: dependency: transitive description: name: analyzer - sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 + sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" url: "https://pub.dev" source: hosted - version: "6.7.0" + version: "6.11.0" android_intent_plus: dependency: "direct main" description: @@ -130,10 +130,10 @@ packages: dependency: "direct main" description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" battery_info: dependency: "direct main" description: @@ -155,10 +155,10 @@ packages: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" brotli: dependency: transitive description: @@ -276,10 +276,10 @@ packages: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" checked_yaml: dependency: transitive description: @@ -309,10 +309,10 @@ packages: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" code_builder: dependency: transitive description: @@ -325,10 +325,10 @@ packages: dependency: "direct main" description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" computer: dependency: "direct main" description: @@ -627,10 +627,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" fast_base58: dependency: "direct main" description: @@ -676,10 +676,10 @@ packages: dependency: transitive description: name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "7.0.1" file_saver: dependency: "direct main" description: @@ -1432,18 +1432,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "10.0.8" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: @@ -1552,10 +1552,10 @@ packages: dependency: transitive description: name: macros - sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" + sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" url: "https://pub.dev" source: hosted - version: "0.1.2-main.4" + version: "0.1.3-main.0" maps_launcher: dependency: "direct main" description: @@ -1568,10 +1568,10 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -1661,10 +1661,10 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.16.0" mgrs_dart: dependency: transitive description: @@ -1875,10 +1875,10 @@ packages: dependency: "direct main" description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" path_drawing: dependency: transitive description: @@ -2035,10 +2035,10 @@ packages: dependency: transitive description: name: platform - sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" url: "https://pub.dev" source: hosted - version: "3.1.5" + version: "3.1.6" plugin_platform_interface: dependency: transitive description: @@ -2084,10 +2084,10 @@ packages: dependency: transitive description: name: process - sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" + sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d" url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "5.0.3" proj4dart: dependency: transitive description: @@ -2332,7 +2332,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_gen: dependency: transitive description: @@ -2369,10 +2369,10 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" sprintf: dependency: transitive description: @@ -2457,10 +2457,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.1" step_progress_indicator: dependency: "direct main" description: @@ -2473,10 +2473,10 @@ packages: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" stream_transform: dependency: transitive description: @@ -2489,10 +2489,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" styled_text: dependency: "direct main" description: @@ -2545,34 +2545,34 @@ packages: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test: dependency: "direct dev" description: name: test - sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" + sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e" url: "https://pub.dev" source: hosted - version: "1.25.7" + version: "1.25.15" test_api: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.4" test_core: dependency: transitive description: name: test_core - sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" + sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa" url: "https://pub.dev" source: hosted - version: "0.6.4" + version: "0.6.8" thermal: dependency: "direct main" description: @@ -2836,10 +2836,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.3.1" volume_controller: dependency: transitive description: @@ -2900,10 +2900,10 @@ packages: dependency: transitive description: name: webdriver - sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" + sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.4" webkit_inspection_protocol: dependency: transitive description: @@ -3001,5 +3001,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.5.0 <4.0.0" + dart: ">=3.7.0-0 <4.0.0" flutter: ">=3.24.0" diff --git a/mobile/rust/.gitignore b/mobile/apps/photos/rust/.gitignore similarity index 100% rename from mobile/rust/.gitignore rename to mobile/apps/photos/rust/.gitignore diff --git a/mobile/rust/Cargo.lock b/mobile/apps/photos/rust/Cargo.lock similarity index 100% rename from mobile/rust/Cargo.lock rename to mobile/apps/photos/rust/Cargo.lock diff --git a/mobile/rust/Cargo.toml b/mobile/apps/photos/rust/Cargo.toml similarity index 100% rename from mobile/rust/Cargo.toml rename to mobile/apps/photos/rust/Cargo.toml diff --git a/mobile/rust/src/api/mod.rs b/mobile/apps/photos/rust/src/api/mod.rs similarity index 100% rename from mobile/rust/src/api/mod.rs rename to mobile/apps/photos/rust/src/api/mod.rs diff --git a/mobile/rust/src/api/simple.rs b/mobile/apps/photos/rust/src/api/simple.rs similarity index 100% rename from mobile/rust/src/api/simple.rs rename to mobile/apps/photos/rust/src/api/simple.rs diff --git a/mobile/rust/src/api/usearch_api.rs b/mobile/apps/photos/rust/src/api/usearch_api.rs similarity index 100% rename from mobile/rust/src/api/usearch_api.rs rename to mobile/apps/photos/rust/src/api/usearch_api.rs diff --git a/mobile/rust/src/frb_generated.rs b/mobile/apps/photos/rust/src/frb_generated.rs similarity index 100% rename from mobile/rust/src/frb_generated.rs rename to mobile/apps/photos/rust/src/frb_generated.rs diff --git a/mobile/rust/src/lib.rs b/mobile/apps/photos/rust/src/lib.rs similarity index 100% rename from mobile/rust/src/lib.rs rename to mobile/apps/photos/rust/src/lib.rs diff --git a/mobile/rust_builder/.gitignore b/mobile/apps/photos/rust_builder/.gitignore similarity index 100% rename from mobile/rust_builder/.gitignore rename to mobile/apps/photos/rust_builder/.gitignore diff --git a/mobile/rust_builder/README.md b/mobile/apps/photos/rust_builder/README.md similarity index 100% rename from mobile/rust_builder/README.md rename to mobile/apps/photos/rust_builder/README.md diff --git a/mobile/rust_builder/android/.gitignore b/mobile/apps/photos/rust_builder/android/.gitignore similarity index 100% rename from mobile/rust_builder/android/.gitignore rename to mobile/apps/photos/rust_builder/android/.gitignore diff --git a/mobile/rust_builder/android/build.gradle b/mobile/apps/photos/rust_builder/android/build.gradle similarity index 100% rename from mobile/rust_builder/android/build.gradle rename to mobile/apps/photos/rust_builder/android/build.gradle diff --git a/mobile/rust_builder/android/settings.gradle b/mobile/apps/photos/rust_builder/android/settings.gradle similarity index 100% rename from mobile/rust_builder/android/settings.gradle rename to mobile/apps/photos/rust_builder/android/settings.gradle diff --git a/mobile/rust_builder/android/src/main/AndroidManifest.xml b/mobile/apps/photos/rust_builder/android/src/main/AndroidManifest.xml similarity index 100% rename from mobile/rust_builder/android/src/main/AndroidManifest.xml rename to mobile/apps/photos/rust_builder/android/src/main/AndroidManifest.xml diff --git a/mobile/rust_builder/cargokit/.gitignore b/mobile/apps/photos/rust_builder/cargokit/.gitignore similarity index 100% rename from mobile/rust_builder/cargokit/.gitignore rename to mobile/apps/photos/rust_builder/cargokit/.gitignore diff --git a/mobile/rust_builder/cargokit/LICENSE b/mobile/apps/photos/rust_builder/cargokit/LICENSE similarity index 100% rename from mobile/rust_builder/cargokit/LICENSE rename to mobile/apps/photos/rust_builder/cargokit/LICENSE diff --git a/mobile/rust_builder/cargokit/README b/mobile/apps/photos/rust_builder/cargokit/README similarity index 100% rename from mobile/rust_builder/cargokit/README rename to mobile/apps/photos/rust_builder/cargokit/README diff --git a/mobile/rust_builder/cargokit/build_pod.sh b/mobile/apps/photos/rust_builder/cargokit/build_pod.sh similarity index 100% rename from mobile/rust_builder/cargokit/build_pod.sh rename to mobile/apps/photos/rust_builder/cargokit/build_pod.sh diff --git a/mobile/rust_builder/cargokit/build_tool/README.md b/mobile/apps/photos/rust_builder/cargokit/build_tool/README.md similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/README.md rename to mobile/apps/photos/rust_builder/cargokit/build_tool/README.md diff --git a/mobile/rust_builder/cargokit/build_tool/analysis_options.yaml b/mobile/apps/photos/rust_builder/cargokit/build_tool/analysis_options.yaml similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/analysis_options.yaml rename to mobile/apps/photos/rust_builder/cargokit/build_tool/analysis_options.yaml diff --git a/mobile/rust_builder/cargokit/build_tool/bin/build_tool.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/bin/build_tool.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/bin/build_tool.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/bin/build_tool.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/build_tool.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/build_tool.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/build_tool.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/build_tool.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/android_environment.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/android_environment.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/android_environment.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/android_environment.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/build_pod.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_pod.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/build_pod.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_pod.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/build_tool.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_tool.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/build_tool.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_tool.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/builder.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/builder.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/builder.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/builder.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/cargo.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/cargo.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/cargo.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/cargo.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/environment.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/environment.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/environment.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/environment.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/logging.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/logging.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/logging.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/logging.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/options.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/options.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/options.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/options.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/rustup.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/rustup.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/rustup.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/rustup.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/target.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/target.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/target.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/target.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/util.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/util.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/util.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/util.dart diff --git a/mobile/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart rename to mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart diff --git a/mobile/rust_builder/cargokit/build_tool/pubspec.lock b/mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.lock similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/pubspec.lock rename to mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.lock diff --git a/mobile/rust_builder/cargokit/build_tool/pubspec.yaml b/mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.yaml similarity index 100% rename from mobile/rust_builder/cargokit/build_tool/pubspec.yaml rename to mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.yaml diff --git a/mobile/rust_builder/cargokit/cmake/cargokit.cmake b/mobile/apps/photos/rust_builder/cargokit/cmake/cargokit.cmake similarity index 100% rename from mobile/rust_builder/cargokit/cmake/cargokit.cmake rename to mobile/apps/photos/rust_builder/cargokit/cmake/cargokit.cmake diff --git a/mobile/rust_builder/cargokit/cmake/resolve_symlinks.ps1 b/mobile/apps/photos/rust_builder/cargokit/cmake/resolve_symlinks.ps1 similarity index 100% rename from mobile/rust_builder/cargokit/cmake/resolve_symlinks.ps1 rename to mobile/apps/photos/rust_builder/cargokit/cmake/resolve_symlinks.ps1 diff --git a/mobile/rust_builder/cargokit/gradle/plugin.gradle b/mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle similarity index 100% rename from mobile/rust_builder/cargokit/gradle/plugin.gradle rename to mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle diff --git a/mobile/rust_builder/cargokit/run_build_tool.cmd b/mobile/apps/photos/rust_builder/cargokit/run_build_tool.cmd similarity index 100% rename from mobile/rust_builder/cargokit/run_build_tool.cmd rename to mobile/apps/photos/rust_builder/cargokit/run_build_tool.cmd diff --git a/mobile/rust_builder/cargokit/run_build_tool.sh b/mobile/apps/photos/rust_builder/cargokit/run_build_tool.sh similarity index 100% rename from mobile/rust_builder/cargokit/run_build_tool.sh rename to mobile/apps/photos/rust_builder/cargokit/run_build_tool.sh diff --git a/mobile/rust_builder/ios/Classes/dummy_file.c b/mobile/apps/photos/rust_builder/ios/Classes/dummy_file.c similarity index 100% rename from mobile/rust_builder/ios/Classes/dummy_file.c rename to mobile/apps/photos/rust_builder/ios/Classes/dummy_file.c diff --git a/mobile/rust_builder/ios/rust_lib_photos.podspec b/mobile/apps/photos/rust_builder/ios/rust_lib_photos.podspec similarity index 100% rename from mobile/rust_builder/ios/rust_lib_photos.podspec rename to mobile/apps/photos/rust_builder/ios/rust_lib_photos.podspec diff --git a/mobile/rust_builder/linux/CMakeLists.txt b/mobile/apps/photos/rust_builder/linux/CMakeLists.txt similarity index 100% rename from mobile/rust_builder/linux/CMakeLists.txt rename to mobile/apps/photos/rust_builder/linux/CMakeLists.txt diff --git a/mobile/rust_builder/macos/Classes/dummy_file.c b/mobile/apps/photos/rust_builder/macos/Classes/dummy_file.c similarity index 100% rename from mobile/rust_builder/macos/Classes/dummy_file.c rename to mobile/apps/photos/rust_builder/macos/Classes/dummy_file.c diff --git a/mobile/rust_builder/macos/rust_lib_photos.podspec b/mobile/apps/photos/rust_builder/macos/rust_lib_photos.podspec similarity index 100% rename from mobile/rust_builder/macos/rust_lib_photos.podspec rename to mobile/apps/photos/rust_builder/macos/rust_lib_photos.podspec diff --git a/mobile/rust_builder/pubspec.yaml b/mobile/apps/photos/rust_builder/pubspec.yaml similarity index 100% rename from mobile/rust_builder/pubspec.yaml rename to mobile/apps/photos/rust_builder/pubspec.yaml diff --git a/mobile/rust_builder/windows/.gitignore b/mobile/apps/photos/rust_builder/windows/.gitignore similarity index 100% rename from mobile/rust_builder/windows/.gitignore rename to mobile/apps/photos/rust_builder/windows/.gitignore diff --git a/mobile/rust_builder/windows/CMakeLists.txt b/mobile/apps/photos/rust_builder/windows/CMakeLists.txt similarity index 100% rename from mobile/rust_builder/windows/CMakeLists.txt rename to mobile/apps/photos/rust_builder/windows/CMakeLists.txt diff --git a/web/apps/photos/thirdparty/ffmpeg-wasm b/web/apps/photos/thirdparty/ffmpeg-wasm new file mode 160000 index 0000000000..8493ad48b1 --- /dev/null +++ b/web/apps/photos/thirdparty/ffmpeg-wasm @@ -0,0 +1 @@ +Subproject commit 8493ad48b12f83f881a59b84b003974ef23f9e96 diff --git a/web/apps/photos/thirdparty/photoswipe b/web/apps/photos/thirdparty/photoswipe new file mode 160000 index 0000000000..bf4a072503 --- /dev/null +++ b/web/apps/photos/thirdparty/photoswipe @@ -0,0 +1 @@ +Subproject commit bf4a072503df18c8d6b047e66a437534c5c05bc5 From 2703c6a33a6f72a86066b8d5288ac424229b8ed0 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 23 Jul 2025 17:39:19 +0200 Subject: [PATCH 061/156] Bump to be up to date with internal branch --- mobile/apps/photos/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/apps/photos/pubspec.yaml b/mobile/apps/photos/pubspec.yaml index 7419d9b985..f9d417a978 100644 --- a/mobile/apps/photos/pubspec.yaml +++ b/mobile/apps/photos/pubspec.yaml @@ -12,7 +12,7 @@ description: ente photos application # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.54+1084 +version: 1.1.63+1097 publish_to: none environment: From bfcfa691a2ad523467ad08e99b9ac0e940c49215 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 23 Jul 2025 17:55:07 +0200 Subject: [PATCH 062/156] upgrade frb and run frb gen command --- .../apps/photos/lib/src/rust/api/simple.dart | 2 +- .../photos/lib/src/rust/api/usearch_api.dart | 2 +- .../photos/lib/src/rust/frb_generated.dart | 6 +- .../photos/lib/src/rust/frb_generated.io.dart | 2 +- .../debug/ml_debug_section_widget.dart | 2 +- mobile/apps/photos/pubspec.lock | 4 +- mobile/apps/photos/pubspec.yaml | 2 +- mobile/apps/photos/rust/Cargo.lock | 94 +++++++++++++++---- mobile/apps/photos/rust/Cargo.toml | 2 +- mobile/apps/photos/rust/src/frb_generated.rs | 6 +- 10 files changed, 91 insertions(+), 31 deletions(-) diff --git a/mobile/apps/photos/lib/src/rust/api/simple.dart b/mobile/apps/photos/lib/src/rust/api/simple.dart index 17c34a531c..a6a07cfa68 100644 --- a/mobile/apps/photos/lib/src/rust/api/simple.dart +++ b/mobile/apps/photos/lib/src/rust/api/simple.dart @@ -1,5 +1,5 @@ // This file is automatically generated, so please do not edit it. -// @generated by `flutter_rust_bridge`@ 2.9.0. +// @generated by `flutter_rust_bridge`@ 2.11.1. // ignore_for_file: require_trailing_commas diff --git a/mobile/apps/photos/lib/src/rust/api/usearch_api.dart b/mobile/apps/photos/lib/src/rust/api/usearch_api.dart index bfb75345ea..e24c3e1188 100644 --- a/mobile/apps/photos/lib/src/rust/api/usearch_api.dart +++ b/mobile/apps/photos/lib/src/rust/api/usearch_api.dart @@ -1,5 +1,5 @@ // This file is automatically generated, so please do not edit it. -// @generated by `flutter_rust_bridge`@ 2.9.0. +// @generated by `flutter_rust_bridge`@ 2.11.1. // ignore_for_file: require_trailing_commas diff --git a/mobile/apps/photos/lib/src/rust/frb_generated.dart b/mobile/apps/photos/lib/src/rust/frb_generated.dart index 599adba41d..69c4023808 100644 --- a/mobile/apps/photos/lib/src/rust/frb_generated.dart +++ b/mobile/apps/photos/lib/src/rust/frb_generated.dart @@ -1,5 +1,5 @@ // This file is automatically generated, so please do not edit it. -// @generated by `flutter_rust_bridge`@ 2.9.0. +// @generated by `flutter_rust_bridge`@ 2.11.1. // ignore_for_file: require_trailing_commas @@ -27,11 +27,13 @@ class RustLib extends BaseEntrypoint { RustLibApi? api, BaseHandler? handler, ExternalLibrary? externalLibrary, + bool forceSameCodegenVersion = true, }) async { await instance.initImpl( api: api, handler: handler, externalLibrary: externalLibrary, + forceSameCodegenVersion: forceSameCodegenVersion, ); } @@ -69,7 +71,7 @@ class RustLib extends BaseEntrypoint { kDefaultExternalLibraryLoaderConfig; @override - String get codegenVersion => '2.9.0'; + String get codegenVersion => '2.11.1'; @override int get rustContentHash => 1543559173; diff --git a/mobile/apps/photos/lib/src/rust/frb_generated.io.dart b/mobile/apps/photos/lib/src/rust/frb_generated.io.dart index cd17c917bc..6ab196d6e2 100644 --- a/mobile/apps/photos/lib/src/rust/frb_generated.io.dart +++ b/mobile/apps/photos/lib/src/rust/frb_generated.io.dart @@ -1,5 +1,5 @@ // This file is automatically generated, so please do not edit it. -// @generated by `flutter_rust_bridge`@ 2.9.0. +// @generated by `flutter_rust_bridge`@ 2.11.1. // ignore_for_file: require_trailing_commas diff --git a/mobile/apps/photos/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/apps/photos/lib/ui/settings/debug/ml_debug_section_widget.dart index d824d0788d..706994cf1c 100644 --- a/mobile/apps/photos/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/apps/photos/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -23,9 +23,9 @@ import "package:photos/services/machine_learning/ml_indexing_isolate.dart"; import 'package:photos/services/machine_learning/ml_service.dart'; import "package:photos/services/machine_learning/semantic_search/semantic_search_service.dart"; import "package:photos/services/notification_service.dart"; +import "package:photos/services/search_service.dart"; import "package:photos/src/rust/api/simple.dart"; import "package:photos/src/rust/api/usearch_api.dart"; -import "package:photos/services/search_service.dart"; import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/expandable_menu_item_widget.dart'; diff --git a/mobile/apps/photos/pubspec.lock b/mobile/apps/photos/pubspec.lock index 69b979d425..6fcbe10e54 100644 --- a/mobile/apps/photos/pubspec.lock +++ b/mobile/apps/photos/pubspec.lock @@ -1019,10 +1019,10 @@ packages: dependency: "direct main" description: name: flutter_rust_bridge - sha256: "5a5c7a5deeef2cc2ffe6076a33b0429f4a20ceac22a397297aed2b1eb067e611" + sha256: "37ef40bc6f863652e865f0b2563ea07f0d3c58d8efad803cc01933a4b2ee067e" url: "https://pub.dev" source: hosted - version: "2.9.0" + version: "2.11.1" flutter_secure_storage: dependency: "direct main" description: diff --git a/mobile/apps/photos/pubspec.yaml b/mobile/apps/photos/pubspec.yaml index f9d417a978..3795aa0bb2 100644 --- a/mobile/apps/photos/pubspec.yaml +++ b/mobile/apps/photos/pubspec.yaml @@ -95,7 +95,7 @@ dependencies: flutter_map: ^6.2.0 flutter_map_marker_cluster: ^1.3.6 flutter_password_strength: ^0.1.6 - flutter_rust_bridge: 2.9.0 + flutter_rust_bridge: 2.11.1 # Do not upgrade this package unless this issue is resolved: # https://github.com/juliansteenbakker/flutter_secure_storage/issues/870 # On v9.2.4, keys related to lockscreen persist even after reintsall. For context see: diff --git a/mobile/apps/photos/rust/Cargo.lock b/mobile/apps/photos/rust/Cargo.lock index 4bc9161bd8..ca61af7f02 100644 --- a/mobile/apps/photos/rust/Cargo.lock +++ b/mobile/apps/photos/rust/Cargo.lock @@ -39,20 +39,19 @@ dependencies = [ [[package]] name = "android_log-sys" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937" +checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" [[package]] name = "android_logger" -version = "0.13.3" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c494134f746c14dc653a35a4ea5aca24ac368529da5370ecf41fe0341c35772f" +checksum = "dbb4e440d04be07da1f1bf44fb4495ebd58669372fe0cffa6e48595ac5bd88a3" dependencies = [ "android_log-sys", - "env_logger", + "env_filter", "log", - "once_cell", ] [[package]] @@ -88,6 +87,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + [[package]] name = "block-buffer" version = "0.10.4" @@ -221,12 +226,15 @@ dependencies = [ [[package]] name = "dashmap" -version = "4.0.2" +version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "num_cpus", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", ] [[package]] @@ -251,10 +259,10 @@ dependencies = [ ] [[package]] -name = "env_logger" -version = "0.10.1" +name = "env_filter" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", @@ -262,9 +270,9 @@ dependencies = [ [[package]] name = "flutter_rust_bridge" -version = "2.9.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f8c0dee6249225e815dcff3f3a39b98d9f66fdb3c392a432715b646bfa4da02" +checksum = "dde126295b2acc5f0a712e265e91b6fdc0ed38767496483e592ae7134db83725" dependencies = [ "allo-isolate", "android_logger", @@ -291,9 +299,9 @@ dependencies = [ [[package]] name = "flutter_rust_bridge_macros" -version = "2.9.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e88d604908d9eccb4ca9c26640ce41033165cbef041460e704ae28bd5208bce" +checksum = "d5f0420326b13675321b194928bb7830043b68cf8b810e1c651285c747abb080" dependencies = [ "hex", "md-5", @@ -407,6 +415,12 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hermit-abi" version = "0.3.3" @@ -449,6 +463,16 @@ dependencies = [ "cc", ] +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.20" @@ -507,15 +531,28 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "oslog" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8343ce955f18e7e68c0207dd0ea776ec453035685395ababd2ea651c569728b3" +checksum = "80d2043d1f61d77cb2f4b1f7b7b2295f40507f5f8e9d1c8bf10a1ca5f97a3969" dependencies = [ "cc", "dashmap", "log", ] +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -552,6 +589,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.10.2" @@ -595,6 +641,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "scratch" version = "1.0.8" @@ -610,6 +662,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + [[package]] name = "syn" version = "2.0.39" diff --git a/mobile/apps/photos/rust/Cargo.toml b/mobile/apps/photos/rust/Cargo.toml index e0e3a5c0c0..791462ba55 100644 --- a/mobile/apps/photos/rust/Cargo.toml +++ b/mobile/apps/photos/rust/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" crate-type = ["cdylib", "staticlib"] [dependencies] -flutter_rust_bridge = "=2.9.0" +flutter_rust_bridge = "=2.11.1" usearch = "2.17.11" [lints.rust] diff --git a/mobile/apps/photos/rust/src/frb_generated.rs b/mobile/apps/photos/rust/src/frb_generated.rs index 860f6e4fd3..3fe26fe80f 100644 --- a/mobile/apps/photos/rust/src/frb_generated.rs +++ b/mobile/apps/photos/rust/src/frb_generated.rs @@ -1,5 +1,5 @@ // This file is automatically generated, so please do not edit it. -// @generated by `flutter_rust_bridge`@ 2.9.0. +// @generated by `flutter_rust_bridge`@ 2.11.1. #![allow( non_camel_case_types, @@ -37,7 +37,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_opaque = RustOpaqueMoi, default_rust_auto_opaque = RustAutoOpaqueMoi, ); -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.9.0"; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.11.1"; pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1543559173; // Section: executor @@ -1227,7 +1227,7 @@ impl SseEncode for i32 { #[cfg(not(target_family = "wasm"))] mod io { // This file is automatically generated, so please do not edit it. - // @generated by `flutter_rust_bridge`@ 2.9.0. + // @generated by `flutter_rust_bridge`@ 2.11.1. // Section: imports From a694bf9b6c5eab6b074eb7c99ddf03df0a123548 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 24 Jul 2025 09:02:05 +0200 Subject: [PATCH 063/156] flutter downgrade dependencies --- auth/flutter | 2 +- mobile/apps/photos/pubspec.lock | 114 ++++++++++++++++---------------- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/auth/flutter b/auth/flutter index 2663184aa7..5874a72aa4 160000 --- a/auth/flutter +++ b/auth/flutter @@ -1 +1 @@ -Subproject commit 2663184aa79047d0a33a14a3b607954f8fdd8730 +Subproject commit 5874a72aa4c779a02553007c47dacbefba2374dc diff --git a/mobile/apps/photos/pubspec.lock b/mobile/apps/photos/pubspec.lock index 6fcbe10e54..ad64287bce 100644 --- a/mobile/apps/photos/pubspec.lock +++ b/mobile/apps/photos/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" + sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 url: "https://pub.dev" source: hosted - version: "76.0.0" + version: "72.0.0" _flutterfire_internals: dependency: transitive description: @@ -21,7 +21,7 @@ packages: dependency: transitive description: dart source: sdk - version: "0.3.3" + version: "0.3.2" adaptive_theme: dependency: "direct main" description: @@ -34,10 +34,10 @@ packages: dependency: transitive description: name: analyzer - sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" + sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 url: "https://pub.dev" source: hosted - version: "6.11.0" + version: "6.7.0" android_intent_plus: dependency: "direct main" description: @@ -130,10 +130,10 @@ packages: dependency: "direct main" description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.11.0" battery_info: dependency: "direct main" description: @@ -155,10 +155,10 @@ packages: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" brotli: dependency: transitive description: @@ -276,10 +276,10 @@ packages: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" checked_yaml: dependency: transitive description: @@ -309,10 +309,10 @@ packages: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" code_builder: dependency: transitive description: @@ -325,10 +325,10 @@ packages: dependency: "direct main" description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" computer: dependency: "direct main" description: @@ -627,10 +627,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.1" fast_base58: dependency: "direct main" description: @@ -676,10 +676,10 @@ packages: dependency: transitive description: name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "7.0.1" + version: "7.0.0" file_saver: dependency: "direct main" description: @@ -1432,18 +1432,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.8" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -1552,10 +1552,10 @@ packages: dependency: transitive description: name: macros - sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" + sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" url: "https://pub.dev" source: hosted - version: "0.1.3-main.0" + version: "0.1.2-main.4" maps_launcher: dependency: "direct main" description: @@ -1568,10 +1568,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -1661,10 +1661,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" mgrs_dart: dependency: transitive description: @@ -1875,10 +1875,10 @@ packages: dependency: "direct main" description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" path_drawing: dependency: transitive description: @@ -2035,10 +2035,10 @@ packages: dependency: transitive description: name: platform - sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted - version: "3.1.6" + version: "3.1.5" plugin_platform_interface: dependency: transitive description: @@ -2084,10 +2084,10 @@ packages: dependency: transitive description: name: process - sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d" + sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" url: "https://pub.dev" source: hosted - version: "5.0.3" + version: "5.0.2" proj4dart: dependency: transitive description: @@ -2332,7 +2332,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" source_gen: dependency: transitive description: @@ -2369,10 +2369,10 @@ packages: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" sprintf: dependency: transitive description: @@ -2457,10 +2457,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" step_progress_indicator: dependency: "direct main" description: @@ -2473,10 +2473,10 @@ packages: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -2489,10 +2489,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" styled_text: dependency: "direct main" description: @@ -2545,34 +2545,34 @@ packages: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test: dependency: "direct dev" description: name: test - sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e" + sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" url: "https://pub.dev" source: hosted - version: "1.25.15" + version: "1.25.7" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.2" test_core: dependency: transitive description: name: test_core - sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa" + sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" url: "https://pub.dev" source: hosted - version: "0.6.8" + version: "0.6.4" thermal: dependency: "direct main" description: @@ -2836,10 +2836,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.3.1" + version: "14.2.5" volume_controller: dependency: transitive description: @@ -2900,10 +2900,10 @@ packages: dependency: transitive description: name: webdriver - sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8" + sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" url: "https://pub.dev" source: hosted - version: "3.0.4" + version: "3.0.3" webkit_inspection_protocol: dependency: transitive description: @@ -3001,5 +3001,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.7.0-0 <4.0.0" + dart: ">=3.5.0 <4.0.0" flutter: ">=3.24.0" From a88b43dd1178df2f4b99140f4be808ca44d354bc Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 24 Jul 2025 10:49:13 +0200 Subject: [PATCH 064/156] Debug line --- .../photos/lib/ui/settings/debug/ml_debug_section_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/apps/photos/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/apps/photos/lib/ui/settings/debug/ml_debug_section_widget.dart index 706994cf1c..93fb519185 100644 --- a/mobile/apps/photos/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/apps/photos/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -550,7 +550,7 @@ class _MLDebugSectionWidgetState extends State { sectionOptionSpacing, MenuItemWidget( captionedTextWidget: const CaptionedTextWidget( - title: "Test rust bridge", + title: "Test rust bridge (without vectorDB)", ), pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, From 1d19d69db965b1ed810cef55de02578f47bd1067 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 24 Jul 2025 10:51:45 +0200 Subject: [PATCH 065/156] Resolve warnings --- mobile/apps/photos/lib/core/cache/lru_map.dart | 2 +- mobile/apps/photos/rust_builder/pubspec.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/apps/photos/lib/core/cache/lru_map.dart b/mobile/apps/photos/lib/core/cache/lru_map.dart index 9e28c84c09..d3760d5809 100644 --- a/mobile/apps/photos/lib/core/cache/lru_map.dart +++ b/mobile/apps/photos/lib/core/cache/lru_map.dart @@ -24,7 +24,7 @@ class LRUMap { final K evictedKey = _map.keys.first; final V? evictedValue = _map.remove(evictedKey); if (_handler != null) { - _handler!(evictedKey, evictedValue); + _handler(evictedKey, evictedValue); } } } diff --git a/mobile/apps/photos/rust_builder/pubspec.yaml b/mobile/apps/photos/rust_builder/pubspec.yaml index 31b699e0a5..2be7b960ab 100644 --- a/mobile/apps/photos/rust_builder/pubspec.yaml +++ b/mobile/apps/photos/rust_builder/pubspec.yaml @@ -4,8 +4,8 @@ version: 0.0.1 publish_to: none environment: - sdk: '>=3.3.0 <4.0.0' - flutter: '>=3.3.0' + sdk: ">=3.3.0 <4.0.0" + flutter: ">=3.3.0" dependencies: flutter: @@ -15,9 +15,9 @@ dependencies: dev_dependencies: ffi: ^2.0.2 ffigen: ^11.0.0 + flutter_lints: ^2.0.0 flutter_test: sdk: flutter - flutter_lints: ^2.0.0 flutter: plugin: From 072e5b492be7e9735815a36f6edf0137779fcb5a Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 24 Jul 2025 11:22:22 +0200 Subject: [PATCH 066/156] Dependencies for 3.27 --- mobile/apps/photos/pubspec.lock | 56 ++++++++++++++++----------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/mobile/apps/photos/pubspec.lock b/mobile/apps/photos/pubspec.lock index ad64287bce..d828206f92 100644 --- a/mobile/apps/photos/pubspec.lock +++ b/mobile/apps/photos/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 + sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" url: "https://pub.dev" source: hosted - version: "72.0.0" + version: "76.0.0" _flutterfire_internals: dependency: transitive description: @@ -21,7 +21,7 @@ packages: dependency: transitive description: dart source: sdk - version: "0.3.2" + version: "0.3.3" adaptive_theme: dependency: "direct main" description: @@ -34,10 +34,10 @@ packages: dependency: transitive description: name: analyzer - sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 + sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" url: "https://pub.dev" source: hosted - version: "6.7.0" + version: "6.11.0" android_intent_plus: dependency: "direct main" description: @@ -325,10 +325,10 @@ packages: dependency: "direct main" description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" computer: dependency: "direct main" description: @@ -1432,18 +1432,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "10.0.7" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.8" leak_tracker_testing: dependency: transitive description: @@ -1552,10 +1552,10 @@ packages: dependency: transitive description: name: macros - sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" + sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" url: "https://pub.dev" source: hosted - version: "0.1.2-main.4" + version: "0.1.3-main.0" maps_launcher: dependency: "direct main" description: @@ -2332,7 +2332,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_gen: dependency: transitive description: @@ -2457,10 +2457,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.0" step_progress_indicator: dependency: "direct main" description: @@ -2489,10 +2489,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" styled_text: dependency: "direct main" description: @@ -2553,26 +2553,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" + sha256: "713a8789d62f3233c46b4a90b174737b2c04cb6ae4500f2aa8b1be8f03f5e67f" url: "https://pub.dev" source: hosted - version: "1.25.7" + version: "1.25.8" test_api: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.3" test_core: dependency: transitive description: name: test_core - sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" + sha256: "12391302411737c176b0b5d6491f466b0dd56d4763e347b6714efbaa74d7953d" url: "https://pub.dev" source: hosted - version: "0.6.4" + version: "0.6.5" thermal: dependency: "direct main" description: @@ -2836,10 +2836,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.3.0" volume_controller: dependency: transitive description: @@ -2900,10 +2900,10 @@ packages: dependency: transitive description: name: webdriver - sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" + sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.4" webkit_inspection_protocol: dependency: transitive description: From 09668c2f67a6d4539e70d749607d08afc9570f3f Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 24 Jul 2025 11:58:10 +0200 Subject: [PATCH 067/156] Don't use vectorDB for magic search --- .../semantic_search_service.dart | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/mobile/apps/photos/lib/services/machine_learning/semantic_search/semantic_search_service.dart b/mobile/apps/photos/lib/services/machine_learning/semantic_search/semantic_search_service.dart index f2b18e515b..05df3a17dc 100644 --- a/mobile/apps/photos/lib/services/machine_learning/semantic_search/semantic_search_service.dart +++ b/mobile/apps/photos/lib/services/machine_learning/semantic_search/semantic_search_service.dart @@ -8,7 +8,6 @@ import "package:logging/logging.dart"; import "package:photos/core/cache/lru_map.dart"; import "package:photos/core/event_bus.dart"; import "package:photos/db/files_db.dart"; -import "package:photos/db/ml/clip_vector_db.dart"; import "package:photos/db/ml/db.dart"; import 'package:photos/events/embedding_updated_event.dart'; import "package:photos/models/file/file.dart"; @@ -273,18 +272,11 @@ class SemanticSearchService { } } late final Map> queryResults; - if (flagService.enableVectorDb) { - queryResults = await ClipVectorDB.instance.computeBulkSimilarities( - textQueryToEmbeddingMap, - minimumSimilarityMap, - ); - } else { - await _cacheClipVectors(); - queryResults = await MLComputer.instance.computeBulkSimilarities( - textQueryToEmbeddingMap, - minimumSimilarityMap, - ); - } + await _cacheClipVectors(); + queryResults = await MLComputer.instance.computeBulkSimilarities( + textQueryToEmbeddingMap, + minimumSimilarityMap, + ); final endTime = DateTime.now(); _logger.info( "computingSimilarities took for ${textQueryToEmbeddingMap.length} queries " + From 5e84774737c59f5f90a5333dd55a45fa7ac4c5b5 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 24 Jul 2025 12:21:32 +0200 Subject: [PATCH 068/156] Dependencies after merging main --- mobile/apps/photos/pubspec.lock | 94 ++++++++++++++++----------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/mobile/apps/photos/pubspec.lock b/mobile/apps/photos/pubspec.lock index 6fcbe10e54..d828206f92 100644 --- a/mobile/apps/photos/pubspec.lock +++ b/mobile/apps/photos/pubspec.lock @@ -130,10 +130,10 @@ packages: dependency: "direct main" description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.11.0" battery_info: dependency: "direct main" description: @@ -155,10 +155,10 @@ packages: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" brotli: dependency: transitive description: @@ -276,10 +276,10 @@ packages: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" checked_yaml: dependency: transitive description: @@ -309,10 +309,10 @@ packages: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" code_builder: dependency: transitive description: @@ -325,10 +325,10 @@ packages: dependency: "direct main" description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.19.0" computer: dependency: "direct main" description: @@ -627,10 +627,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.1" fast_base58: dependency: "direct main" description: @@ -676,10 +676,10 @@ packages: dependency: transitive description: name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "7.0.1" + version: "7.0.0" file_saver: dependency: "direct main" description: @@ -1432,18 +1432,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" url: "https://pub.dev" source: hosted - version: "10.0.8" + version: "10.0.7" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.8" leak_tracker_testing: dependency: transitive description: @@ -1568,10 +1568,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -1661,10 +1661,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" mgrs_dart: dependency: transitive description: @@ -1875,10 +1875,10 @@ packages: dependency: "direct main" description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" path_drawing: dependency: transitive description: @@ -2035,10 +2035,10 @@ packages: dependency: transitive description: name: platform - sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted - version: "3.1.6" + version: "3.1.5" plugin_platform_interface: dependency: transitive description: @@ -2084,10 +2084,10 @@ packages: dependency: transitive description: name: process - sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d" + sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" url: "https://pub.dev" source: hosted - version: "5.0.3" + version: "5.0.2" proj4dart: dependency: transitive description: @@ -2369,10 +2369,10 @@ packages: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" sprintf: dependency: transitive description: @@ -2457,10 +2457,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.12.0" step_progress_indicator: dependency: "direct main" description: @@ -2473,10 +2473,10 @@ packages: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -2489,10 +2489,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.3.0" styled_text: dependency: "direct main" description: @@ -2545,34 +2545,34 @@ packages: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test: dependency: "direct dev" description: name: test - sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e" + sha256: "713a8789d62f3233c46b4a90b174737b2c04cb6ae4500f2aa8b1be8f03f5e67f" url: "https://pub.dev" source: hosted - version: "1.25.15" + version: "1.25.8" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.3" test_core: dependency: transitive description: name: test_core - sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa" + sha256: "12391302411737c176b0b5d6491f466b0dd56d4763e347b6714efbaa74d7953d" url: "https://pub.dev" source: hosted - version: "0.6.8" + version: "0.6.5" thermal: dependency: "direct main" description: @@ -2836,10 +2836,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "14.3.1" + version: "14.3.0" volume_controller: dependency: transitive description: @@ -3001,5 +3001,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.7.0-0 <4.0.0" + dart: ">=3.5.0 <4.0.0" flutter: ">=3.24.0" From 5b291de28fb509feddb3c5aa1f3a6a112c6ef191 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 24 Jul 2025 13:19:53 +0200 Subject: [PATCH 069/156] Don't migrate or call VectorDB automatically --- .../apps/photos/lib/db/ml/clip_vector_db.dart | 29 +++++++++++++++++++ mobile/apps/photos/lib/db/ml/db.dart | 22 +++++++++----- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/mobile/apps/photos/lib/db/ml/clip_vector_db.dart b/mobile/apps/photos/lib/db/ml/clip_vector_db.dart index dc5597d07d..9962f55cb1 100644 --- a/mobile/apps/photos/lib/db/ml/clip_vector_db.dart +++ b/mobile/apps/photos/lib/db/ml/clip_vector_db.dart @@ -1,3 +1,4 @@ +import "dart:io" show File; import "dart:typed_data" show Float32List; import "package:flutter_rust_bridge/flutter_rust_bridge.dart" show Uint64List; @@ -30,6 +31,8 @@ class ClipVectorDB { return _vectorDbFuture!; } + bool? _migrationDone; + Future _initVectorDB() async { final documentsDirectory = await getApplicationDocumentsDirectory(); final String databaseDirectory = @@ -45,6 +48,32 @@ class ClipVectorDB { return vectorDB; } + Future checkIfMigrationDone() async { + if (_migrationDone != null) return _migrationDone!; + _logger.info("Checking if ClipVectorDB migration has run"); + final documentsDirectory = await getApplicationDocumentsDirectory(); + final migrationFlagFile = + File(join(documentsDirectory.path, 'clip_vector_migration_done')); + if (await migrationFlagFile.exists()) { + _logger.info("ClipVectorDB migration already done"); + _migrationDone = true; + return _migrationDone!; + } else { + _logger.info("ClipVectorDB migration not done"); + _migrationDone = false; + return _migrationDone!; + } + } + + Future setMigrationDone() async { + _logger.info("Setting ClipVectorDB migration done"); + final documentsDirectory = await getApplicationDocumentsDirectory(); + final migrationFlagFile = + File(join(documentsDirectory.path, 'clip_vector_migration_done')); + await migrationFlagFile.create(recursive: true); + _migrationDone = true; + } + Future insertEmbedding({ required int fileID, required List embedding, diff --git a/mobile/apps/photos/lib/db/ml/db.dart b/mobile/apps/photos/lib/db/ml/db.dart index 2165e45336..316963fe55 100644 --- a/mobile/apps/photos/lib/db/ml/db.dart +++ b/mobile/apps/photos/lib/db/ml/db.dart @@ -87,8 +87,6 @@ class MLDataDB with SqlDbBase implements IMLDataDB { "MLDataDB Migration took ${stopwatch.elapsedMilliseconds} ms", ); stopwatch.stop(); - _logger.info("Starting CLIP vector DB migration check unawaited"); - if (flagService.enableVectorDb) unawaited(checkMigrateFillClipVectorDB()); return asyncDBConnection; } @@ -1263,6 +1261,12 @@ class MLDataDB with SqlDbBase implements IMLDataDB { await Future.delayed(const Duration(milliseconds: 100)); _logger.info("Checking if ClipVectorDB migration is needed"); + final migrationDone = await ClipVectorDB.instance.checkIfMigrationDone(); + if (migrationDone && !force) { + _logger.info("ClipVectorDB migration not needed, already done"); + return; + } + // Check if vector DB migration has run _logger.info("Checking if ClipVectorDB migration has run"); final documentsDirectory = await getApplicationDocumentsDirectory(); @@ -1309,7 +1313,7 @@ class MLDataDB with SqlDbBase implements IMLDataDB { _logger.info("Reading $batchSize rows from DB"); final List> results = await db.getAll(''' - SELECT $fileIDColumn, $embeddingColumn + SELECT $fileIDColumn, $embeddingColumn FROM $clipTable ORDER BY $fileIDColumn DESC LIMIT $batchSize OFFSET $offset @@ -1406,7 +1410,8 @@ class MLDataDB with SqlDbBase implements IMLDataDB { 'INSERT OR REPLACE INTO $clipTable ($fileIDColumn, $embeddingColumn, $mlVersionColumn) VALUES (?, ?, ?)', _getRowFromEmbedding(embeddings.first), ); - if (flagService.enableVectorDb) { + if (flagService.enableVectorDb && + await ClipVectorDB.instance.checkIfMigrationDone()) { await ClipVectorDB.instance.insertEmbedding( fileID: embeddings.first.fileID, embedding: embeddings.first.embedding, @@ -1418,7 +1423,8 @@ class MLDataDB with SqlDbBase implements IMLDataDB { 'INSERT OR REPLACE INTO $clipTable ($fileIDColumn, $embeddingColumn, $mlVersionColumn) values(?, ?, ?)', inputs, ); - if (flagService.enableVectorDb) { + if (flagService.enableVectorDb && + await ClipVectorDB.instance.checkIfMigrationDone()) { await ClipVectorDB.instance.bulkInsertEmbeddings( fileIDs: embeddings.map((e) => e.fileID).toList(), embeddings: @@ -1435,7 +1441,8 @@ class MLDataDB with SqlDbBase implements IMLDataDB { await db.execute( 'DELETE FROM $clipTable WHERE $fileIDColumn IN (${fileIDs.join(", ")})', ); - if (flagService.enableVectorDb) { + if (flagService.enableVectorDb && + await ClipVectorDB.instance.checkIfMigrationDone()) { await ClipVectorDB.instance.deleteEmbeddings(fileIDs); } Bus.instance.fire(EmbeddingUpdatedEvent()); @@ -1445,7 +1452,8 @@ class MLDataDB with SqlDbBase implements IMLDataDB { Future deleteClipIndexes() async { final db = await instance.asyncDB; await db.execute('DELETE FROM $clipTable'); - if (flagService.enableVectorDb) { + if (flagService.enableVectorDb && + await ClipVectorDB.instance.checkIfMigrationDone()) { await ClipVectorDB.instance.deleteAllEmbeddings(); } Bus.instance.fire(EmbeddingUpdatedEvent()); From ba76c858240d0b6a1d987f250aafa0d5fb3e8004 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 24 Jul 2025 13:44:59 +0200 Subject: [PATCH 070/156] Faster migration --- mobile/apps/photos/lib/db/ml/db.dart | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/mobile/apps/photos/lib/db/ml/db.dart b/mobile/apps/photos/lib/db/ml/db.dart index 316963fe55..31cf942fbc 100644 --- a/mobile/apps/photos/lib/db/ml/db.dart +++ b/mobile/apps/photos/lib/db/ml/db.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import "dart:io" show File; import "dart:math"; import "package:collection/collection.dart"; @@ -1257,25 +1256,12 @@ class MLDataDB with SqlDbBase implements IMLDataDB { } Future checkMigrateFillClipVectorDB({bool force = false}) async { - _logger.info("Waiting for ClipVectorDB to be ready"); - await Future.delayed(const Duration(milliseconds: 100)); - _logger.info("Checking if ClipVectorDB migration is needed"); - final migrationDone = await ClipVectorDB.instance.checkIfMigrationDone(); if (migrationDone && !force) { _logger.info("ClipVectorDB migration not needed, already done"); return; } - - // Check if vector DB migration has run - _logger.info("Checking if ClipVectorDB migration has run"); - final documentsDirectory = await getApplicationDocumentsDirectory(); - final migrationFlagFile = - File(join(documentsDirectory.path, 'clip_vector_migration_done')); - if (await migrationFlagFile.exists() && !force) { - _logger.info("ClipVectorDB migration not needed, already done"); - return; - } + _logger.info("Starting ClipVectorDB migration"); // Get total count first to track progress _logger.info("Getting total count of clip embeddings"); @@ -1285,20 +1271,20 @@ class MLDataDB with SqlDbBase implements IMLDataDB { final totalCount = countResult.first['total'] as int; if (totalCount == 0) { _logger.info("No clip embeddings to migrate"); - await migrationFlagFile.create(); + await ClipVectorDB.instance.setMigrationDone(); return; } _logger.info("Total count of clip embeddings: $totalCount"); - _logger.info("First time referencing ClipVectorDB in migration"); + _logger.info("First time referencing ClipVectorDB rust index in migration"); final clipVectorDB = ClipVectorDB.instance; - _logger.info("ClipVectorDB referenced"); await clipVectorDB.deleteAllEmbeddings(); + _logger.info("ClipVectorDB rust index referenced"); _logger.info("ClipVectorDB all embeddings cleared"); _logger .info("Starting migration of $totalCount clip embeddings to vector DB"); - const batchSize = 1000; + const batchSize = 5000; int offset = 0; int processedCount = 0; int weirdCount = 0; @@ -1363,7 +1349,7 @@ class MLDataDB with SqlDbBase implements IMLDataDB { _logger.info( "migrated all $totalCount embeddings to ClipVectorDB in ${stopwatch.elapsed.inMilliseconds} ms, with $weirdCount weird embeddings not migrated", ); - await migrationFlagFile.create(); + await ClipVectorDB.instance.setMigrationDone(); _logger.info("ClipVectorDB migration done, flag file created"); } catch (e) { _logger.severe( From b68150a00797ad709cf1f804e4e0e1727b5bbf33 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Sun, 27 Jul 2025 17:38:15 +0200 Subject: [PATCH 071/156] Bulk search first API --- .../apps/photos/lib/db/ml/clip_vector_db.dart | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/mobile/apps/photos/lib/db/ml/clip_vector_db.dart b/mobile/apps/photos/lib/db/ml/clip_vector_db.dart index 9962f55cb1..ce8d7eb027 100644 --- a/mobile/apps/photos/lib/db/ml/clip_vector_db.dart +++ b/mobile/apps/photos/lib/db/ml/clip_vector_db.dart @@ -201,6 +201,24 @@ class ClipVectorDB { } } + Future<(List, List)> bulkSearchVectors( + List queries, + BigInt count, + double thresholdThreshold, + ) async { + final db = await _vectorDB; + try { + final result = await db.bulkSearchVectors( + queries: queries, + count: count, + ); + return result; + } catch (e, s) { + _logger.severe("Error bulk searching vectors", e, s); + rethrow; + } + } + Future>> computeBulkSimilarities( Map> textQueryToEmbeddingMap, Map minimumSimilarityMap, From 05b435049604f6e2fd371b7a468eddbf23f49cb5 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 28 Jul 2025 10:41:36 +0200 Subject: [PATCH 072/156] Similar images service mvp --- .../apps/photos/lib/db/ml/clip_vector_db.dart | 1 - .../apps/photos/lib/models/similar_files.dart | 16 +++ .../similar_images_service.dart | 118 ++++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 mobile/apps/photos/lib/models/similar_files.dart create mode 100644 mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart diff --git a/mobile/apps/photos/lib/db/ml/clip_vector_db.dart b/mobile/apps/photos/lib/db/ml/clip_vector_db.dart index ce8d7eb027..e78c100067 100644 --- a/mobile/apps/photos/lib/db/ml/clip_vector_db.dart +++ b/mobile/apps/photos/lib/db/ml/clip_vector_db.dart @@ -204,7 +204,6 @@ class ClipVectorDB { Future<(List, List)> bulkSearchVectors( List queries, BigInt count, - double thresholdThreshold, ) async { final db = await _vectorDB; try { diff --git a/mobile/apps/photos/lib/models/similar_files.dart b/mobile/apps/photos/lib/models/similar_files.dart new file mode 100644 index 0000000000..77ded03415 --- /dev/null +++ b/mobile/apps/photos/lib/models/similar_files.dart @@ -0,0 +1,16 @@ +import "package:photos/models/file/file.dart"; + +class SimilarFiles { + final List files; + final int totalSize; + final double similarityScore; + + SimilarFiles( + this.files, + this.totalSize, + this.similarityScore, + ); + + @override + String toString() => 'SimilarFiles(files: $files, size: $totalSize)'; +} diff --git a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart new file mode 100644 index 0000000000..d2f116e07f --- /dev/null +++ b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart @@ -0,0 +1,118 @@ +import "dart:math" show max; +import "dart:typed_data" show Float32List; + +import "package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart" + show Uint64List; +import 'package:logging/logging.dart'; +import "package:photos/db/ml/clip_vector_db.dart"; +import "package:photos/db/ml/db.dart"; +import 'package:photos/models/file/file.dart'; +import "package:photos/models/ml/vector.dart"; +import "package:photos/models/similar_files.dart"; +import "package:photos/services/search_service.dart"; + +class SimilarImagesService { + final _logger = Logger("SimilarImagesService"); + + SimilarImagesService._privateConstructor(); + static final SimilarImagesService instance = + SimilarImagesService._privateConstructor(); + + /// Returns a list of SimilarFiles, where each SimilarFiles object contains + /// a list of files that are perceptually similar + Future> getSimilarFiles(double distanceThreshold) async { + try { + final List result = + await _getSimilarFiles(distanceThreshold); + return result; + } catch (e, s) { + _logger.severe("failed to get similar files", e, s); + rethrow; + } + } + + Future> _getSimilarFiles(double distanceThreshold) async { + final mlDataDB = MLDataDB.instance; + _logger.info("Checking migration and filling clip vector DB"); + await mlDataDB.checkMigrateFillClipVectorDB(); + + // Get the embeddings ready for vector search + final List allImageEmbeddings = + await MLDataDB.instance.getAllClipVectors(); + final clipFloat32 = allImageEmbeddings + .map( + (value) => Float32List.fromList(value.vector.toList()), + ) + .toList(); + final keys = Uint64List.fromList( + allImageEmbeddings.map((e) => BigInt.from(e.fileID)).toList(), + ); + + // Run bulk vector search + final (vectorKeys, distances) = + await ClipVectorDB.instance.bulkSearchVectors( + clipFloat32, + BigInt.from(100), + ); + + // Get all files, and create a map of fileID to file + final allFiles = Set.from( + await SearchService.instance.getAllFilesForSearch(), + ); + final allFileIdsToFile = {}; + for (final file in allFiles) { + if (file.uploadedFileID != null) { + allFileIdsToFile[file.uploadedFileID!] = file; + } + } + + // Run through the vector search results and create SimilarFiles objects + final alreadyUsedFileIDs = {}; + final allSimilarFiles = []; + for (int i = 0; i < keys.length; i++) { + final fileID = keys[i].toInt(); + if (alreadyUsedFileIDs.contains(fileID)) continue; + final firstLoopFile = allFileIdsToFile[fileID]; + if (firstLoopFile == null || firstLoopFile.uploadedFileID == null) { + continue; + } + final otherFileIDs = vectorKeys[i]; + final distancesToFiles = distances[i]; + final similarFilesList = []; + double furthestDistance = 0.0; + for (int j = 0; j < otherFileIDs.length; j++) { + final distance = distancesToFiles[j]; + if (distance > distanceThreshold) { + break; + } else { + furthestDistance = max(furthestDistance, distance); + } + final otherFileID = otherFileIDs[j].toInt(); + if (alreadyUsedFileIDs.contains(otherFileID)) continue; + final otherFile = allFileIdsToFile[otherFileID]; + if (otherFile != null && otherFile.uploadedFileID != null) { + similarFilesList.add(otherFile); + } + } + if (similarFilesList.isNotEmpty) { + similarFilesList.add(firstLoopFile); + int totalSize = 0; + for (final file in similarFilesList) { + alreadyUsedFileIDs.add(file.uploadedFileID!); + totalSize += file.fileSize ?? 0; + } + final similarFiles = SimilarFiles( + similarFilesList, + totalSize, + furthestDistance, + ); + allSimilarFiles.add(similarFiles); + } + } + + // Sort the similar files by total size in descending order + allSimilarFiles.sort((a, b) => b.totalSize.compareTo(a.totalSize)); + + return allSimilarFiles; + } +} From ee55002bf1a4ba9939b150c8257e4c75e083cada Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 28 Jul 2025 11:05:31 +0200 Subject: [PATCH 073/156] Similar files change --- mobile/apps/photos/lib/models/similar_files.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mobile/apps/photos/lib/models/similar_files.dart b/mobile/apps/photos/lib/models/similar_files.dart index 77ded03415..8f11340b6e 100644 --- a/mobile/apps/photos/lib/models/similar_files.dart +++ b/mobile/apps/photos/lib/models/similar_files.dart @@ -3,14 +3,15 @@ import "package:photos/models/file/file.dart"; class SimilarFiles { final List files; final int totalSize; - final double similarityScore; + final double furthestDistance; SimilarFiles( this.files, this.totalSize, - this.similarityScore, + this.furthestDistance, ); @override - String toString() => 'SimilarFiles(files: $files, size: $totalSize)'; + String toString() => + 'SimilarFiles(files: $files, size: $totalSize, distance: $furthestDistance)'; } From 0cff1642c526b3bf23e49676c7e58ae454580ccf Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 28 Jul 2025 14:21:38 +0200 Subject: [PATCH 074/156] Similar files page mvp --- .../settings/backup/free_space_options.dart | 33 ++ .../lib/ui/tools/similar_images_page.dart | 479 ++++++++++++++++++ 2 files changed, 512 insertions(+) create mode 100644 mobile/apps/photos/lib/ui/tools/similar_images_page.dart diff --git a/mobile/apps/photos/lib/ui/settings/backup/free_space_options.dart b/mobile/apps/photos/lib/ui/settings/backup/free_space_options.dart index 6d42e2040c..9583d26193 100644 --- a/mobile/apps/photos/lib/ui/settings/backup/free_space_options.dart +++ b/mobile/apps/photos/lib/ui/settings/backup/free_space_options.dart @@ -22,6 +22,7 @@ import "package:photos/ui/notification/toast.dart"; import "package:photos/ui/tools/debug/app_storage_viewer.dart"; import "package:photos/ui/tools/deduplicate_page.dart"; import "package:photos/ui/tools/free_space_page.dart"; +import "package:photos/ui/tools/similar_images_page.dart"; import "package:photos/ui/viewer/gallery/large_files_page.dart"; import "package:photos/utils/dialog_util.dart"; import 'package:photos/utils/navigation_util.dart'; @@ -187,6 +188,38 @@ class _FreeUpSpaceOptionsScreenState extends State { const SizedBox( height: 24, ), + MenuItemWidget( + captionedTextWidget: + const CaptionedTextWidget( + title: + "Similar images", // TODO: lau: extract string + ), + menuItemColor: colorScheme.fillFaint, + trailingWidget: Icon( + Icons.chevron_right_outlined, + color: colorScheme.strokeBase, + ), + singleBorderRadius: 8, + alignCaptionedTextToLeft: true, + trailingIconIsMuted: true, + showOnlyLoadingState: true, + onTap: () async { + await routeToPage( + context, + const SimilarImagesPage(), + ); + }, + ), + const Align( + alignment: Alignment.centerLeft, + child: MenuSectionDescriptionWidget( + content: + "Use AI to find images that look similar to each other.", // TODO: lau: extract string + ), + ), + const SizedBox( + height: 24, + ), MenuItemWidget( captionedTextWidget: CaptionedTextWidget( title: S.of(context).viewLargeFiles, diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart new file mode 100644 index 0000000000..cc68ef874a --- /dev/null +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -0,0 +1,479 @@ +import "dart:async"; + +import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; + +import "package:photos/models/file/file.dart"; +import "package:photos/models/similar_files.dart"; +import "package:photos/service_locator.dart"; +import "package:photos/services/machine_learning/similar_images_service.dart"; +import 'package:photos/theme/ente_theme.dart'; +import "package:photos/ui/common/loading_widget.dart"; +import 'package:photos/ui/components/buttons/button_widget.dart'; +import "package:photos/ui/components/models/button_type.dart"; +import "package:photos/ui/viewer/file/detail_page.dart"; +import "package:photos/ui/viewer/file/file_widget.dart"; +import "package:photos/utils/dialog_util.dart"; +import "package:photos/utils/navigation_util.dart"; +import "package:photos/utils/standalone/data.dart"; + +enum SimilarImagesPageState { + setup, + loading, + results, +} + +enum SortKey { + size, + distance, + count, +} + +class SimilarImagesPage extends StatefulWidget { + const SimilarImagesPage({super.key}); + + @override + State createState() => _SimilarImagesPageState(); +} + +class _SimilarImagesPageState extends State { + final _logger = Logger("SimilarImagesPage"); + + SimilarImagesPageState _pageState = SimilarImagesPageState.setup; + double _distanceThreshold = 0.05; // Default value + List _similarFilesList = []; + SortKey _sortKey = SortKey.size; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + title: const Text("Similar Images"), // TODO: lau: extract string + actions: _pageState == SimilarImagesPageState.results + ? [ + _getSortMenu(), + ] + : null, + ), + body: _getBody(), + ); + } + + Widget _getBody() { + switch (_pageState) { + case SimilarImagesPageState.setup: + return _getSetupView(); + case SimilarImagesPageState.loading: + return _getLoadingView(); + case SimilarImagesPageState.results: + return _getResultsView(); + } + } + + Widget _getSetupView() { + final colorScheme = getEnteColorScheme(context); + + return Padding( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Icon( + Icons.auto_awesome_outlined, + size: 72, + color: colorScheme.primary500, + ), + const SizedBox(height: 32), + Text( + "Find similar images", // TODO: lau: extract string + style: getEnteTextTheme(context).h3Bold, + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + Text( + "Use AI to find images that look similar to each other. Adjust the distance threshold below.", // TODO: lau: extract string + style: getEnteTextTheme(context).body, + textAlign: TextAlign.center, + ), + const SizedBox(height: 48), + Text( + "Similarity threshold", // TODO: lau: extract string + style: getEnteTextTheme(context).bodyBold, + ), + const SizedBox(height: 8), + Text( + "Lower values mean a closer match.", // TODO: lau: extract string + style: getEnteTextTheme(context).mini.copyWith( + color: colorScheme.textMuted, + ), + ), + const SizedBox(height: 16), + Row( + children: [ + Text( + "0.01", + style: getEnteTextTheme(context).mini, + ), + Expanded( + child: Slider( + value: _distanceThreshold, + min: 0.01, + max: 0.15, + divisions: 14, + onChanged: (value) { + setState(() { + _distanceThreshold = (value * 100).round() / 100; + }); + }, + ), + ), + Text( + "0.15", + style: getEnteTextTheme(context).mini, + ), + ], + ), + Text( + "Current: ${_distanceThreshold.toStringAsFixed(2)}", // TODO: lau: extract string + style: getEnteTextTheme(context).body, + textAlign: TextAlign.center, + ), + const SizedBox(height: 48), + ButtonWidget( + labelText: "Find similar images", // TODO: lau: extract string + buttonType: ButtonType.primary, + onTap: () async { + await _findSimilarImages(); + }, + ), + ], + ), + ); + } + + Widget _getLoadingView() { + return const Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + EnteLoadingWidget(), + SizedBox(height: 16), + Text("Analyzing images..."), // TODO: lau: extract string + ], + ), + ); + } + + Widget _getResultsView() { + if (_similarFilesList.isEmpty) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.check_circle_outline, + size: 72, + color: getEnteColorScheme(context).primary500, + ), + const SizedBox(height: 16), + Text( + "No Similar Images Found", // TODO: lau: extract string + style: getEnteTextTheme(context).h3Bold, + ), + const SizedBox(height: 8), + Text( + "Try adjusting the similarity threshold", // TODO: lau: extract string + style: getEnteTextTheme(context).body, + ), + const SizedBox(height: 32), + ButtonWidget( + labelText: "Try Again", // TODO: lau: extract string + buttonType: ButtonType.secondary, + onTap: () async { + setState(() { + _pageState = SimilarImagesPageState.setup; + }); + }, + ), + ], + ), + ); + } + + return Column( + children: [ + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: getEnteColorScheme(context).fillFaint, + border: Border( + bottom: BorderSide( + color: getEnteColorScheme(context).strokeFaint, + width: 1, + ), + ), + ), + child: Column( + children: [ + Text( + "Found ${_similarFilesList.length} groups of similar images", // TODO: lau: extract string + style: getEnteTextTheme(context).bodyBold, + ), + const SizedBox(height: 4), + Text( + "Threshold: ${_distanceThreshold.toStringAsFixed(2)}", // TODO: lau: extract string + style: getEnteTextTheme(context).mini.copyWith( + color: getEnteColorScheme(context).textMuted, + ), + ), + ], + ), + ), + Expanded( + child: _getGridView(), + ), + ], + ); + } + + Future _findSimilarImages() async { + setState(() { + _pageState = SimilarImagesPageState.loading; + }); + + try { + final similarFiles = await SimilarImagesService.instance + .getSimilarFiles(_distanceThreshold); + _sortSimilarFiles(); + + setState(() { + _similarFilesList = similarFiles; + _pageState = SimilarImagesPageState.results; + }); + + return; + } catch (e, s) { + _logger.severe("Failed to get similar files", e, s); + if (flagService.internalUser) { + await showGenericErrorDialog(context: context, error: e); + } + setState(() { + _pageState = SimilarImagesPageState.setup; + }); + return; + } + } + + void _sortSimilarFiles() { + switch (_sortKey) { + case SortKey.size: + _similarFilesList.sort((a, b) => b.totalSize.compareTo(a.totalSize)); + break; + case SortKey.distance: + _similarFilesList + .sort((a, b) => a.furthestDistance.compareTo(b.furthestDistance)); + break; + case SortKey.count: + _similarFilesList + .sort((a, b) => b.files.length.compareTo(a.files.length)); + break; + } + setState(() {}); + } + + Widget _getGridView() { + return ListView.builder( + itemCount: _similarFilesList.length, + itemBuilder: (context, index) { + final similarFiles = _similarFilesList[index]; + return _buildSimilarFilesGroup(similarFiles, index); + }, + ); + } + + Widget _buildSimilarFilesGroup(SimilarFiles similarFiles, int index) { + final colorScheme = getEnteColorScheme(context); + + return Container( + margin: const EdgeInsets.all(8), + decoration: BoxDecoration( + border: Border.all(color: colorScheme.strokeFaint), + borderRadius: BorderRadius.circular(8), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: colorScheme.fillFaint, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(8), + topRight: Radius.circular(8), + ), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "${similarFiles.files.length} similar images", // TODO: lau: extract string + style: getEnteTextTheme(context).bodyBold, + ), + const SizedBox(height: 4), + Text( + "Total size: ${formatBytes(similarFiles.totalSize)}", // TODO: lau: extract string + style: getEnteTextTheme(context).mini, + ), + Text( + "Distance: ${similarFiles.furthestDistance.toStringAsFixed(3)}", // TODO: lau: extract string + style: getEnteTextTheme(context).mini.copyWith( + color: colorScheme.textMuted, + ), + ), + ], + ), + ), + ], + ), + ), + GridView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.all(8), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + crossAxisSpacing: 4, + mainAxisSpacing: 4, + ), + itemCount: similarFiles.files.length, + itemBuilder: (context, fileIndex) { + return _buildFile( + context, + similarFiles.files[fileIndex], + similarFiles.files, + fileIndex, + ); + }, + ), + ], + ), + ); + } + + Widget _buildFile( + BuildContext context, + EnteFile file, + List allFiles, + int index, + ) { + return GestureDetector( + onTap: () { + routeToPage( + context, + DetailPage( + DetailPageConfiguration( + allFiles, + index, + "similar_images_", + mode: DetailPageMode.minimalistic, + ), + ), + ); + }, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: getEnteColorScheme(context).strokeFaint, + width: 1, + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(4), + child: FileWidget( + file, + tagPrefix: "similar_images", + ), + ), + ), + ); + } + + Widget _getSortMenu() { + Text sortOptionText(SortKey key) { + String text = key.toString(); + switch (key) { + case SortKey.size: + text = "Size"; // TODO: lau: extract string + break; + case SortKey.distance: + text = "Similarity"; // TODO: lau: extract string + break; + case SortKey.count: + text = "Count"; // TODO: lau: extract string + break; + } + return Text( + text, + style: Theme.of(context).textTheme.titleMedium!.copyWith( + fontSize: 14, + color: Theme.of(context).iconTheme.color!.withOpacity(0.7), + ), + ); + } + + return PopupMenuButton( + initialValue: _sortKey.index, + child: Padding( + padding: const EdgeInsets.fromLTRB(24, 6, 24, 6), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + sortOptionText(_sortKey), + const Padding(padding: EdgeInsets.only(left: 4)), + Icon( + Icons.sort, + color: getEnteColorScheme(context).strokeBase, + size: 20, + ), + ], + ), + ), + onSelected: (int index) { + setState(() { + _sortKey = SortKey.values[index]; + }); + _sortSimilarFiles(); + }, + itemBuilder: (context) { + return List.generate(SortKey.values.length, (index) { + return PopupMenuItem( + value: index, + child: Text( + sortOptionText(SortKey.values[index]).data!, + style: Theme.of(context).textTheme.titleMedium!.copyWith( + fontSize: 14, + ), + ), + ); + }); + }, + ); + } +} From 159dd57f0cb5312f9efce44f67570e5ed3f9289c Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 28 Jul 2025 14:43:48 +0200 Subject: [PATCH 075/156] Fix migration issue --- mobile/apps/photos/lib/db/ml/db.dart | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/mobile/apps/photos/lib/db/ml/db.dart b/mobile/apps/photos/lib/db/ml/db.dart index 31cf942fbc..322b73fdc8 100644 --- a/mobile/apps/photos/lib/db/ml/db.dart +++ b/mobile/apps/photos/lib/db/ml/db.dart @@ -1338,12 +1338,8 @@ class MLDataDB with SqlDbBase implements IMLDataDB { _logger.info("All embeddings migrated, breaking out of while loop"); break; } - _logger.info("Clearing out embeddings and fileIDs"); - embeddings.clear(); - fileIDs.clear(); - results.clear(); // Allow some time for any GC to finish - _logger.info("Waiting for 100ms for GC to finish"); + _logger.info("Waiting for 100ms out of precaution, for GC to finish"); await Future.delayed(const Duration(milliseconds: 100)); } _logger.info( @@ -1351,10 +1347,11 @@ class MLDataDB with SqlDbBase implements IMLDataDB { ); await ClipVectorDB.instance.setMigrationDone(); _logger.info("ClipVectorDB migration done, flag file created"); - } catch (e) { + } catch (e, s) { _logger.severe( "Error migrating ClipVectorDB after ${stopwatch.elapsed.inMilliseconds} ms, clearing out DB again", e, + s, ); await clipVectorDB.deleteAllEmbeddings(); rethrow; From a6c163a7050831a32e1b0d665e986e2367d69492 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 28 Jul 2025 16:39:05 +0200 Subject: [PATCH 076/156] Exclude videos from similar dedupe --- .../lib/services/machine_learning/similar_images_service.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart index d2f116e07f..685ebd93b0 100644 --- a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart +++ b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart @@ -7,6 +7,7 @@ import 'package:logging/logging.dart'; import "package:photos/db/ml/clip_vector_db.dart"; import "package:photos/db/ml/db.dart"; import 'package:photos/models/file/file.dart'; +import "package:photos/models/file/file_type.dart"; import "package:photos/models/ml/vector.dart"; import "package:photos/models/similar_files.dart"; import "package:photos/services/search_service.dart"; @@ -61,7 +62,7 @@ class SimilarImagesService { ); final allFileIdsToFile = {}; for (final file in allFiles) { - if (file.uploadedFileID != null) { + if (file.uploadedFileID != null && file.fileType != FileType.video) { allFileIdsToFile[file.uploadedFileID!] = file; } } From dde67479be297bfa816e89f2c1bfbd45d6ec0d91 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 28 Jul 2025 16:59:09 +0200 Subject: [PATCH 077/156] Logging --- mobile/apps/photos/lib/ui/tools/similar_images_page.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index cc68ef874a..e037d78839 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -256,7 +256,13 @@ class _SimilarImagesPageState extends State { try { final similarFiles = await SimilarImagesService.instance .getSimilarFiles(_distanceThreshold); + _logger.info( + "Found ${similarFiles.length} groups of similar images", + ); _sortSimilarFiles(); + _logger.fine( + "Sorted similar files by $_sortKey", + ); setState(() { _similarFilesList = similarFiles; From 492b959204ce0a7a81e91842f14254d6896ef65e Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 30 Jul 2025 15:47:32 +0200 Subject: [PATCH 078/156] Lower default --- mobile/apps/photos/lib/ui/tools/similar_images_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index e037d78839..244f608d60 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -40,7 +40,7 @@ class _SimilarImagesPageState extends State { final _logger = Logger("SimilarImagesPage"); SimilarImagesPageState _pageState = SimilarImagesPageState.setup; - double _distanceThreshold = 0.05; // Default value + double _distanceThreshold = 0.04; // Default value List _similarFilesList = []; SortKey _sortKey = SortKey.size; From a7a23e9e979011e3824e4c1ac4c82c9e5f93421c Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 30 Jul 2025 16:05:27 +0200 Subject: [PATCH 079/156] UI improvements --- .../lib/ui/tools/similar_images_page.dart | 235 +++++++++--------- 1 file changed, 120 insertions(+), 115 deletions(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 244f608d60..0d9ae336b2 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -2,6 +2,7 @@ import "dart:async"; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; +import 'package:photos/core/constants.dart'; import "package:photos/models/file/file.dart"; import "package:photos/models/similar_files.dart"; @@ -12,7 +13,7 @@ import "package:photos/ui/common/loading_widget.dart"; import 'package:photos/ui/components/buttons/button_widget.dart'; import "package:photos/ui/components/models/button_type.dart"; import "package:photos/ui/viewer/file/detail_page.dart"; -import "package:photos/ui/viewer/file/file_widget.dart"; +import "package:photos/ui/viewer/file/thumbnail_widget.dart"; import "package:photos/utils/dialog_util.dart"; import "package:photos/utils/navigation_util.dart"; import "package:photos/utils/standalone/data.dart"; @@ -37,7 +38,11 @@ class SimilarImagesPage extends StatefulWidget { } class _SimilarImagesPageState extends State { + static const crossAxisCount = 4; + static const crossAxisSpacing = 4.0; + final _logger = Logger("SimilarImagesPage"); + bool _isDisposed = false; SimilarImagesPageState _pageState = SimilarImagesPageState.setup; double _distanceThreshold = 0.04; // Default value @@ -51,6 +56,7 @@ class _SimilarImagesPageState extends State { @override void dispose() { + _isDisposed = true; super.dispose(); } @@ -133,6 +139,7 @@ class _SimilarImagesPageState extends State { max: 0.15, divisions: 14, onChanged: (value) { + if (_isDisposed) return; setState(() { _distanceThreshold = (value * 100).round() / 100; }); @@ -202,6 +209,7 @@ class _SimilarImagesPageState extends State { labelText: "Try Again", // TODO: lau: extract string buttonType: ButtonType.secondary, onTap: () async { + if (_isDisposed) return; setState(() { _pageState = SimilarImagesPageState.setup; }); @@ -212,43 +220,49 @@ class _SimilarImagesPageState extends State { ); } - return Column( - children: [ - Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: getEnteColorScheme(context).fillFaint, - border: Border( - bottom: BorderSide( - color: getEnteColorScheme(context).strokeFaint, - width: 1, + return ListView.builder( + itemCount: _similarFilesList.length + 1, // +1 for header + itemBuilder: (context, index) { + if (index == 0) { + // Header item + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: getEnteColorScheme(context).fillFaint, + border: Border( + bottom: BorderSide( + color: getEnteColorScheme(context).strokeFaint, + width: 1, + ), ), ), - ), - child: Column( - children: [ - Text( - "Found ${_similarFilesList.length} groups of similar images", // TODO: lau: extract string - style: getEnteTextTheme(context).bodyBold, - ), - const SizedBox(height: 4), - Text( - "Threshold: ${_distanceThreshold.toStringAsFixed(2)}", // TODO: lau: extract string - style: getEnteTextTheme(context).mini.copyWith( - color: getEnteColorScheme(context).textMuted, - ), - ), - ], - ), - ), - Expanded( - child: _getGridView(), - ), - ], + child: Column( + children: [ + Text( + "Found ${_similarFilesList.length} groups of similar images", // TODO: lau: extract string + style: getEnteTextTheme(context).bodyBold, + ), + const SizedBox(height: 4), + Text( + "Threshold: ${_distanceThreshold.toStringAsFixed(2)}", // TODO: lau: extract string + style: getEnteTextTheme(context).mini.copyWith( + color: getEnteColorScheme(context).textMuted, + ), + ), + ], + ), + ); + } + + // Similar files groups (index - 1 because first item is header) + final similarFiles = _similarFilesList[index - 1]; + return _buildSimilarFilesGroup(similarFiles); + }, ); } Future _findSimilarImages() async { + if (_isDisposed) return; setState(() { _pageState = SimilarImagesPageState.loading; }); @@ -259,11 +273,14 @@ class _SimilarImagesPageState extends State { _logger.info( "Found ${similarFiles.length} groups of similar images", ); + + if (_isDisposed) return; _sortSimilarFiles(); _logger.fine( "Sorted similar files by $_sortKey", ); + if (_isDisposed) return; setState(() { _similarFilesList = similarFiles; _pageState = SimilarImagesPageState.results; @@ -272,9 +289,11 @@ class _SimilarImagesPageState extends State { return; } catch (e, s) { _logger.severe("Failed to get similar files", e, s); + if (_isDisposed) return; if (flagService.internalUser) { await showGenericErrorDialog(context: context, error: e); } + if (_isDisposed) return; setState(() { _pageState = SimilarImagesPageState.setup; }); @@ -296,88 +315,65 @@ class _SimilarImagesPageState extends State { .sort((a, b) => b.files.length.compareTo(a.files.length)); break; } + if (_isDisposed) return; setState(() {}); } - Widget _getGridView() { - return ListView.builder( - itemCount: _similarFilesList.length, - itemBuilder: (context, index) { - final similarFiles = _similarFilesList[index]; - return _buildSimilarFilesGroup(similarFiles, index); - }, - ); - } - - Widget _buildSimilarFilesGroup(SimilarFiles similarFiles, int index) { - final colorScheme = getEnteColorScheme(context); - - return Container( - margin: const EdgeInsets.all(8), - decoration: BoxDecoration( - border: Border.all(color: colorScheme.strokeFaint), - borderRadius: BorderRadius.circular(8), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: colorScheme.fillFaint, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(8), - topRight: Radius.circular(8), - ), - ), - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "${similarFiles.files.length} similar images", // TODO: lau: extract string - style: getEnteTextTheme(context).bodyBold, - ), - const SizedBox(height: 4), - Text( - "Total size: ${formatBytes(similarFiles.totalSize)}", // TODO: lau: extract string - style: getEnteTextTheme(context).mini, - ), - Text( - "Distance: ${similarFiles.furthestDistance.toStringAsFixed(3)}", // TODO: lau: extract string - style: getEnteTextTheme(context).mini.copyWith( - color: colorScheme.textMuted, - ), - ), - ], - ), + Widget _buildSimilarFilesGroup(SimilarFiles similarFiles) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(2, 4, 2, 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "${similarFiles.files.length} similar images • ${formatBytes(similarFiles.totalSize)}", // TODO: lau: extract string + style: Theme.of(context).textTheme.titleSmall, + ), + const SizedBox(height: 2), + Text( + "Distance: ${similarFiles.furthestDistance.toStringAsFixed(3)}", // TODO: lau: extract string + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: getEnteColorScheme(context).textMuted, + fontSize: 12, + ), + ), + ], ), - ], - ), + ), + ], ), - GridView.builder( + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: crossAxisSpacing / 2), + child: GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), - padding: const EdgeInsets.all(8), - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - crossAxisSpacing: 4, - mainAxisSpacing: 4, - ), - itemCount: similarFiles.files.length, - itemBuilder: (context, fileIndex) { + itemBuilder: (context, index) { return _buildFile( context, - similarFiles.files[fileIndex], + similarFiles.files[index], similarFiles.files, - fileIndex, + index, ); }, + itemCount: similarFiles.files.length, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: crossAxisCount, + crossAxisSpacing: crossAxisSpacing, + childAspectRatio: 0.75, + ), + padding: const EdgeInsets.all(0), ), - ], - ), + ), + const SizedBox(height: 16), // Add spacing between groups + ], ); } @@ -401,21 +397,29 @@ class _SimilarImagesPageState extends State { ), ); }, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: getEnteColorScheme(context).strokeFaint, - width: 1, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: (MediaQuery.of(context).size.width - + (crossAxisSpacing * crossAxisCount)) / + crossAxisCount, + child: Hero( + tag: "similar_images_" + file.tag, + child: ClipRRect( + borderRadius: BorderRadius.circular(4), + child: ThumbnailWidget( + file, + diskLoadDeferDuration: galleryThumbnailDiskLoadDeferDuration, + serverLoadDeferDuration: + galleryThumbnailServerLoadDeferDuration, + shouldShowLivePhotoOverlay: true, + key: Key("similar_images_" + file.tag), + ), + ), + ), ), - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(4), - child: FileWidget( - file, - tagPrefix: "similar_images", - ), - ), + ], ), ); } @@ -462,6 +466,7 @@ class _SimilarImagesPageState extends State { ), ), onSelected: (int index) { + if (_isDisposed) return; setState(() { _sortKey = SortKey.values[index]; }); From ec502f3f4e24ea56a4021fec22dc9e8bb8af5aee Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 31 Jul 2025 08:27:11 +0200 Subject: [PATCH 080/156] Copy --- mobile/apps/photos/lib/ui/tools/similar_images_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 0d9ae336b2..8a315b0bd3 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -65,7 +65,7 @@ class _SimilarImagesPageState extends State { return Scaffold( appBar: AppBar( elevation: 0, - title: const Text("Similar Images"), // TODO: lau: extract string + title: const Text("Similar images"), // TODO: lau: extract string actions: _pageState == SimilarImagesPageState.results ? [ _getSortMenu(), From d6c3cf3b8ff40dd7bdf12665268ac757234f95c0 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 31 Jul 2025 15:40:04 +0200 Subject: [PATCH 081/156] Better frequency for people memories --- .../apps/photos/lib/models/memories/memories_cache.dart | 2 +- .../lib/models/memories/smart_memory_constants.dart | 3 ++- .../apps/photos/lib/services/smart_memories_service.dart | 9 ++++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/mobile/apps/photos/lib/models/memories/memories_cache.dart b/mobile/apps/photos/lib/models/memories/memories_cache.dart index f45282d30c..622c3b8485 100644 --- a/mobile/apps/photos/lib/models/memories/memories_cache.dart +++ b/mobile/apps/photos/lib/models/memories/memories_cache.dart @@ -9,7 +9,7 @@ import "package:photos/models/memories/smart_memory.dart"; import "package:photos/models/memories/smart_memory_constants.dart"; import "package:photos/models/memories/trip_memory.dart"; -const kPersonShowTimeout = Duration(days: 7 * 10); +const kPersonShowTimeout = Duration(days: 16 * kMemoriesUpdateFrequencyDays); const kPersonAndTypeShowTimeout = Duration(days: 7 * 26); const kClipShowTimeout = Duration(days: 3 * 10); const kTripShowTimeout = Duration(days: 7 * 25); diff --git a/mobile/apps/photos/lib/models/memories/smart_memory_constants.dart b/mobile/apps/photos/lib/models/memories/smart_memory_constants.dart index 2d9885f90e..35a8b4130c 100644 --- a/mobile/apps/photos/lib/models/memories/smart_memory_constants.dart +++ b/mobile/apps/photos/lib/models/memories/smart_memory_constants.dart @@ -1,4 +1,5 @@ // Constants for computing smart memories -const kMemoriesUpdateFrequency = Duration(days: 3); +const kMemoriesUpdateFrequencyDays = 3; +const kMemoriesUpdateFrequency = Duration(days: kMemoriesUpdateFrequencyDays); const kMemoriesMargin = Duration(days: 1); const kDayItself = Duration(days: 1); diff --git a/mobile/apps/photos/lib/services/smart_memories_service.dart b/mobile/apps/photos/lib/services/smart_memories_service.dart index ce20f14cd2..9875df1402 100644 --- a/mobile/apps/photos/lib/services/smart_memories_service.dart +++ b/mobile/apps/photos/lib/services/smart_memories_service.dart @@ -422,6 +422,7 @@ class SmartMemoriesService { .map((p) => p.remoteID) .toList(); orderedImportantPersonsID.shuffle(Random()); + final amountOfPersons = orderedImportantPersonsID.length; w?.log('orderedImportantPersonsID setup'); // Check if the user has assignmed "me" @@ -709,6 +710,12 @@ class SmartMemoriesService { w?.log('relevancy setup'); // Loop through the people (and memory types) and add based on rotation + final shownPersonTimeout = Duration( + days: min( + kPersonShowTimeout.inDays, + max(1, amountOfPersons) * kMemoriesUpdateFrequencyDays, + ), + ); peopleRotationLoop: for (final personID in orderedImportantPersonsID) { for (final memory in memoryResults) { @@ -721,7 +728,7 @@ class SmartMemoriesService { final shownDate = DateTime.fromMicrosecondsSinceEpoch(shownLog.lastTimeShown); final bool seenPersonRecently = - currentTime.difference(shownDate) < kPersonShowTimeout; + currentTime.difference(shownDate) < shownPersonTimeout; if (seenPersonRecently) continue peopleRotationLoop; } if (personToMemories[personID] == null) continue peopleRotationLoop; From a6b29525d2979fe990ad8399c5adce79937600ab Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 31 Jul 2025 16:51:49 +0200 Subject: [PATCH 082/156] Better frequency for people type --- .../photos/lib/models/memories/memories_cache.dart | 2 -- .../photos/lib/services/smart_memories_service.dart | 10 ++++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/mobile/apps/photos/lib/models/memories/memories_cache.dart b/mobile/apps/photos/lib/models/memories/memories_cache.dart index 622c3b8485..1c1c01b5bc 100644 --- a/mobile/apps/photos/lib/models/memories/memories_cache.dart +++ b/mobile/apps/photos/lib/models/memories/memories_cache.dart @@ -10,13 +10,11 @@ import "package:photos/models/memories/smart_memory_constants.dart"; import "package:photos/models/memories/trip_memory.dart"; const kPersonShowTimeout = Duration(days: 16 * kMemoriesUpdateFrequencyDays); -const kPersonAndTypeShowTimeout = Duration(days: 7 * 26); const kClipShowTimeout = Duration(days: 3 * 10); const kTripShowTimeout = Duration(days: 7 * 25); final maxShowTimeout = [ kPersonShowTimeout, - kPersonAndTypeShowTimeout, kTripShowTimeout, ].reduce((value, element) => value > element ? value : element) * 3; diff --git a/mobile/apps/photos/lib/services/smart_memories_service.dart b/mobile/apps/photos/lib/services/smart_memories_service.dart index 9875df1402..9583bbc38d 100644 --- a/mobile/apps/photos/lib/services/smart_memories_service.dart +++ b/mobile/apps/photos/lib/services/smart_memories_service.dart @@ -716,6 +716,8 @@ class SmartMemoriesService { max(1, amountOfPersons) * kMemoriesUpdateFrequencyDays, ), ); + final shownPersonAndTypeTimeout = + Duration(days: shownPersonTimeout.inDays * 2); peopleRotationLoop: for (final personID in orderedImportantPersonsID) { for (final memory in memoryResults) { @@ -733,6 +735,8 @@ class SmartMemoriesService { } if (personToMemories[personID] == null) continue peopleRotationLoop; int added = 0; + final amountOfMemoryTypesForPerson = personToMemories[personID]!.length; + final bool manyMemoryTypes = amountOfMemoryTypesForPerson > 2; potentialMemoryLoop: for (final memoriesForCategory in personToMemories[personID]!.values) { PeopleMemory potentialMemory = memoriesForCategory.first; @@ -755,8 +759,10 @@ class SmartMemoriesService { final shownTypeDate = DateTime.fromMicrosecondsSinceEpoch(shownLog.lastTimeShown); final bool seenPersonTypeRecently = - currentTime.difference(shownTypeDate) < kPersonAndTypeShowTimeout; - if (seenPersonTypeRecently) continue potentialMemoryLoop; + currentTime.difference(shownTypeDate) < shownPersonAndTypeTimeout; + if (manyMemoryTypes && seenPersonTypeRecently) { + continue potentialMemoryLoop; + } } memoryResults.add(potentialMemory); added++; From 86967175f7794482cb247833a289e85909cc6511 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 31 Jul 2025 16:58:26 +0200 Subject: [PATCH 083/156] Better values --- mobile/apps/photos/lib/models/memories/memories_cache.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/apps/photos/lib/models/memories/memories_cache.dart b/mobile/apps/photos/lib/models/memories/memories_cache.dart index 1c1c01b5bc..f49a1a1014 100644 --- a/mobile/apps/photos/lib/models/memories/memories_cache.dart +++ b/mobile/apps/photos/lib/models/memories/memories_cache.dart @@ -10,8 +10,8 @@ import "package:photos/models/memories/smart_memory_constants.dart"; import "package:photos/models/memories/trip_memory.dart"; const kPersonShowTimeout = Duration(days: 16 * kMemoriesUpdateFrequencyDays); -const kClipShowTimeout = Duration(days: 3 * 10); -const kTripShowTimeout = Duration(days: 7 * 25); +const kClipShowTimeout = Duration(days: 10 * kMemoriesUpdateFrequencyDays); +const kTripShowTimeout = Duration(days: 50 * kMemoriesUpdateFrequencyDays); final maxShowTimeout = [ kPersonShowTimeout, From 79d3c7f9a2ac9ad7fb0d647565061ad464e22070 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Fri, 1 Aug 2025 12:40:15 +0200 Subject: [PATCH 084/156] Merge conflict left --- mobile/apps/photos/pubspec.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mobile/apps/photos/pubspec.yaml b/mobile/apps/photos/pubspec.yaml index fd1cb9478c..c2858a59a2 100644 --- a/mobile/apps/photos/pubspec.yaml +++ b/mobile/apps/photos/pubspec.yaml @@ -12,11 +12,7 @@ description: ente photos application # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -<<<<<<< HEAD -version: 1.1.63+1097 -======= version: 1.2.0+1200 ->>>>>>> main publish_to: none environment: From 73928092c4c87be924a5cc967aa4331f0d2e6cb7 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Fri, 1 Aug 2025 13:03:13 +0200 Subject: [PATCH 085/156] Sort distance descending --- .../photos/lib/ui/tools/similar_images_page.dart | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 8a315b0bd3..cf289e3c17 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -26,7 +26,8 @@ enum SimilarImagesPageState { enum SortKey { size, - distance, + distanceAsc, + distanceDesc, count, } @@ -306,10 +307,14 @@ class _SimilarImagesPageState extends State { case SortKey.size: _similarFilesList.sort((a, b) => b.totalSize.compareTo(a.totalSize)); break; - case SortKey.distance: + case SortKey.distanceAsc: _similarFilesList .sort((a, b) => a.furthestDistance.compareTo(b.furthestDistance)); break; + case SortKey.distanceDesc: + _similarFilesList + .sort((a, b) => b.furthestDistance.compareTo(a.furthestDistance)); + break; case SortKey.count: _similarFilesList .sort((a, b) => b.files.length.compareTo(a.files.length)); @@ -431,8 +436,11 @@ class _SimilarImagesPageState extends State { case SortKey.size: text = "Size"; // TODO: lau: extract string break; - case SortKey.distance: - text = "Similarity"; // TODO: lau: extract string + case SortKey.distanceAsc: + text = "Distance ascending"; // TODO: lau: extract string + break; + case SortKey.distanceDesc: + text = "Distance descending"; // TODO: lau: extract string break; case SortKey.count: text = "Count"; // TODO: lau: extract string From 9528b4ce8dd3ced472ec693f48455d043a1419ce Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Fri, 1 Aug 2025 16:21:43 +0200 Subject: [PATCH 086/156] Fix showing the same file twice --- .../lib/services/machine_learning/similar_images_service.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart index 685ebd93b0..ee56868162 100644 --- a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart +++ b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart @@ -82,13 +82,14 @@ class SimilarImagesService { final similarFilesList = []; double furthestDistance = 0.0; for (int j = 0; j < otherFileIDs.length; j++) { + final otherFileID = otherFileIDs[j].toInt(); + if (otherFileID == fileID) continue; final distance = distancesToFiles[j]; if (distance > distanceThreshold) { break; } else { furthestDistance = max(furthestDistance, distance); } - final otherFileID = otherFileIDs[j].toInt(); if (alreadyUsedFileIDs.contains(otherFileID)) continue; final otherFile = allFileIdsToFile[otherFileID]; if (otherFile != null && otherFile.uploadedFileID != null) { From 6dfcc5814404788b27d27f27d600abe7408d190e Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Fri, 1 Aug 2025 16:55:02 +0200 Subject: [PATCH 087/156] Add exact search to vectorDB --- .../photos/lib/src/rust/api/usearch_api.dart | 8 +- .../photos/lib/src/rust/frb_generated.dart | 34 ++-- mobile/apps/photos/rust/Cargo.lock | 157 +++++++++++++++--- mobile/apps/photos/rust/Cargo.toml | 2 +- .../apps/photos/rust/src/api/usearch_api.rs | 23 ++- mobile/apps/photos/rust/src/frb_generated.rs | 4 + 6 files changed, 184 insertions(+), 44 deletions(-) diff --git a/mobile/apps/photos/lib/src/rust/api/usearch_api.dart b/mobile/apps/photos/lib/src/rust/api/usearch_api.dart index e24c3e1188..7d370723cd 100644 --- a/mobile/apps/photos/lib/src/rust/api/usearch_api.dart +++ b/mobile/apps/photos/lib/src/rust/api/usearch_api.dart @@ -22,7 +22,9 @@ abstract class VectorDb implements RustOpaqueInterface { Future bulkRemoveVectors({required Uint64List keys}); Future<(List, List)> bulkSearchVectors( - {required List queries, required BigInt count}); + {required List queries, + required BigInt count, + required bool exact}); /// Check if a vector with the given key exists in the index. /// `true` if the index contains the vector with the given key, `false` otherwise. @@ -44,5 +46,7 @@ abstract class VectorDb implements RustOpaqueInterface { Future resetIndex(); Future<(Uint64List, Float32List)> searchVectors( - {required List query, required BigInt count}); + {required List query, + required BigInt count, + required bool exact}); } diff --git a/mobile/apps/photos/lib/src/rust/frb_generated.dart b/mobile/apps/photos/lib/src/rust/frb_generated.dart index 69c4023808..c9496ecf9e 100644 --- a/mobile/apps/photos/lib/src/rust/frb_generated.dart +++ b/mobile/apps/photos/lib/src/rust/frb_generated.dart @@ -105,7 +105,8 @@ abstract class RustLibApi extends BaseApi { crateApiUsearchApiVectorDbBulkSearchVectors( {required VectorDb that, required List queries, - required BigInt count}); + required BigInt count, + required bool exact}); Future crateApiUsearchApiVectorDbContainsVector( {required VectorDb that, required BigInt key}); @@ -129,7 +130,8 @@ abstract class RustLibApi extends BaseApi { Future<(Uint64List, Float32List)> crateApiUsearchApiVectorDbSearchVectors( {required VectorDb that, required List query, - required BigInt count}); + required BigInt count, + required bool exact}); String crateApiSimpleGreet({required String name}); @@ -275,7 +277,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { crateApiUsearchApiVectorDbBulkSearchVectors( {required VectorDb that, required List queries, - required BigInt count}) { + required BigInt count, + required bool exact}) { return handler.executeNormal(NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); @@ -283,6 +286,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { that, serializer); sse_encode_list_list_prim_f_32_strict(queries, serializer); sse_encode_usize(count, serializer); + sse_encode_bool(exact, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 5, port: port_); }, @@ -292,7 +296,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { decodeErrorData: null, ), constMeta: kCrateApiUsearchApiVectorDbBulkSearchVectorsConstMeta, - argValues: [that, queries, count], + argValues: [that, queries, count, exact], apiImpl: this, )); } @@ -300,7 +304,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { TaskConstMeta get kCrateApiUsearchApiVectorDbBulkSearchVectorsConstMeta => const TaskConstMeta( debugName: "VectorDb_bulk_search_vectors", - argNames: ["that", "queries", "count"], + argNames: ["that", "queries", "count", "exact"], ); @override @@ -498,7 +502,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { Future<(Uint64List, Float32List)> crateApiUsearchApiVectorDbSearchVectors( {required VectorDb that, required List query, - required BigInt count}) { + required BigInt count, + required bool exact}) { return handler.executeNormal(NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); @@ -506,6 +511,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { that, serializer); sse_encode_list_prim_f_32_loose(query, serializer); sse_encode_usize(count, serializer); + sse_encode_bool(exact, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 13, port: port_); }, @@ -515,7 +521,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { decodeErrorData: null, ), constMeta: kCrateApiUsearchApiVectorDbSearchVectorsConstMeta, - argValues: [that, query, count], + argValues: [that, query, count, exact], apiImpl: this, )); } @@ -523,7 +529,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { TaskConstMeta get kCrateApiUsearchApiVectorDbSearchVectorsConstMeta => const TaskConstMeta( debugName: "VectorDb_search_vectors", - argNames: ["that", "query", "count"], + argNames: ["that", "query", "count", "exact"], ); @override @@ -1103,9 +1109,11 @@ class VectorDbImpl extends RustOpaque implements VectorDb { .crateApiUsearchApiVectorDbBulkRemoveVectors(that: this, keys: keys); Future<(List, List)> bulkSearchVectors( - {required List queries, required BigInt count}) => + {required List queries, + required BigInt count, + required bool exact}) => RustLib.instance.api.crateApiUsearchApiVectorDbBulkSearchVectors( - that: this, queries: queries, count: count); + that: this, queries: queries, count: count, exact: exact); /// Check if a vector with the given key exists in the index. /// `true` if the index contains the vector with the given key, `false` otherwise. @@ -1135,7 +1143,9 @@ class VectorDbImpl extends RustOpaque implements VectorDb { ); Future<(Uint64List, Float32List)> searchVectors( - {required List query, required BigInt count}) => + {required List query, + required BigInt count, + required bool exact}) => RustLib.instance.api.crateApiUsearchApiVectorDbSearchVectors( - that: this, query: query, count: count); + that: this, query: query, count: count, exact: exact); } diff --git a/mobile/apps/photos/rust/Cargo.lock b/mobile/apps/photos/rust/Cargo.lock index ca61af7f02..e091b5defd 100644 --- a/mobile/apps/photos/rust/Cargo.lock +++ b/mobile/apps/photos/rust/Cargo.lock @@ -54,6 +54,12 @@ dependencies = [ "log", ] +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + [[package]] name = "anyhow" version = "1.0.75" @@ -142,11 +148,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "codespan-reporting" -version = "0.11.1" +name = "clap" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +dependencies = [ + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "codespan-reporting" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +dependencies = [ + "serde", "termcolor", "unicode-width", ] @@ -173,25 +206,27 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.112" +version = "1.0.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ab30434ea0ff6aa640a08dda5284026a366d47565496fd40b6cbfbdd7e31a2" +checksum = "a3523cc02ad831111491dd64b27ad999f1ae189986728e477604e61b81f828df" dependencies = [ "cc", + "cxxbridge-cmd", "cxxbridge-flags", "cxxbridge-macro", + "foldhash", "link-cplusplus", ] [[package]] name = "cxx-build" -version = "1.0.112" +version = "1.0.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b649d7dfae8268450d53d109388b337b9352c7cba1fc10db4a1bc23c3dc189fb" +checksum = "212b754247a6f07b10fa626628c157593f0abf640a3dd04cce2760eca970f909" dependencies = [ "cc", "codespan-reporting", - "once_cell", + "indexmap", "proc-macro2", "quote", "scratch", @@ -199,19 +234,35 @@ dependencies = [ ] [[package]] -name = "cxxbridge-flags" -version = "1.0.112" +name = "cxxbridge-cmd" +version = "1.0.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42281b20eba5218c539295c667c18e2f50211bb11902419194c6ed1ae808e547" +checksum = "f426a20413ec2e742520ba6837c9324b55ffac24ead47491a6e29f933c5b135a" +dependencies = [ + "clap", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258b6069020b4e5da6415df94a50ee4f586a6c38b037a180e940a43d06a070d" [[package]] name = "cxxbridge-macro" -version = "1.0.112" +version = "1.0.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45506e3c66512b0a65d291a6b452128b7b1dd9841e20d1e151addbd2c00ea50" +checksum = "e8dec184b52be5008d6eaf7e62fc1802caf1ad1227d11b3b7df2c409c7ffc3f4" dependencies = [ + "indexmap", "proc-macro2", "quote", + "rustversion", "syn", ] @@ -231,7 +282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -268,6 +319,12 @@ dependencies = [ "regex", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "flutter_rust_bridge" version = "2.11.1" @@ -310,6 +367,12 @@ dependencies = [ "syn", ] +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "futures" version = "0.3.29" @@ -421,6 +484,12 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" + [[package]] name = "hermit-abi" version = "0.3.3" @@ -433,6 +502,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown 0.15.4", +] + [[package]] name = "js-sys" version = "0.3.69" @@ -573,18 +652,18 @@ checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -641,6 +720,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + [[package]] name = "scopeguard" version = "1.2.0" @@ -653,6 +738,26 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52" +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "slab" version = "0.4.9" @@ -669,10 +774,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] -name = "syn" -version = "2.0.39" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -728,9 +839,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "usearch" -version = "2.17.11" +version = "2.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "908331accde6ff6bfe83e1f2dfd4cc77343d107bfacecd2f19b7a14d87cdb2df" +checksum = "afa869be4baf62146b39700da30b235b06f58d2c03b7e073a8e2b57e0f511aa9" dependencies = [ "cxx", "cxx-build", diff --git a/mobile/apps/photos/rust/Cargo.toml b/mobile/apps/photos/rust/Cargo.toml index 791462ba55..2a861a57c5 100644 --- a/mobile/apps/photos/rust/Cargo.toml +++ b/mobile/apps/photos/rust/Cargo.toml @@ -8,7 +8,7 @@ crate-type = ["cdylib", "staticlib"] [dependencies] flutter_rust_bridge = "=2.11.1" -usearch = "2.17.11" +usearch = "2.19.1" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] } diff --git a/mobile/apps/photos/rust/src/api/usearch_api.rs b/mobile/apps/photos/rust/src/api/usearch_api.rs index 457b45f7ff..78503c9dd5 100644 --- a/mobile/apps/photos/rust/src/api/usearch_api.rs +++ b/mobile/apps/photos/rust/src/api/usearch_api.rs @@ -83,11 +83,21 @@ impl VectorDB { self.save_index(); } - pub fn search_vectors(&self, query: &Vec, count: usize) -> (Vec, Vec) { - let matches = self - .index - .search(query, count) - .expect("Failed to search vectors"); + pub fn search_vectors( + &self, + query: &Vec, + count: usize, + exact: bool, + ) -> (Vec, Vec) { + let matches = if exact { + self.index + .exact_search(query, count) + .expect("Failed to exact search vectors") + } else { + self.index + .search(query, count) + .expect("Failed to search vectors") + }; (matches.keys, matches.distances) } @@ -95,12 +105,13 @@ impl VectorDB { &self, queries: &Vec>, count: usize, + exact: bool, ) -> (Vec>, Vec>) { let mut keys = Vec::new(); let mut distances = Vec::new(); for query in queries { - let (keys_result, distances_result) = self.search_vectors(query, count); + let (keys_result, distances_result) = self.search_vectors(query, count, exact); keys.push(keys_result); distances.push(distances_result); } diff --git a/mobile/apps/photos/rust/src/frb_generated.rs b/mobile/apps/photos/rust/src/frb_generated.rs index 3fe26fe80f..6b114469a7 100644 --- a/mobile/apps/photos/rust/src/frb_generated.rs +++ b/mobile/apps/photos/rust/src/frb_generated.rs @@ -296,6 +296,7 @@ fn wire__crate__api__usearch_api__VectorDb_bulk_search_vectors_impl( >>::sse_decode(&mut deserializer); let api_queries = >>::sse_decode(&mut deserializer); let api_count = ::sse_decode(&mut deserializer); + let api_exact = ::sse_decode(&mut deserializer); deserializer.end(); move |context| { transform_result_sse::<_, ()>((move || { @@ -318,6 +319,7 @@ fn wire__crate__api__usearch_api__VectorDb_bulk_search_vectors_impl( &*api_that_guard, &api_queries, api_count, + api_exact, ), )?; Ok(output_ok) @@ -682,6 +684,7 @@ fn wire__crate__api__usearch_api__VectorDb_search_vectors_impl( >>::sse_decode(&mut deserializer); let api_query = >::sse_decode(&mut deserializer); let api_count = ::sse_decode(&mut deserializer); + let api_exact = ::sse_decode(&mut deserializer); deserializer.end(); move |context| { transform_result_sse::<_, ()>((move || { @@ -704,6 +707,7 @@ fn wire__crate__api__usearch_api__VectorDb_search_vectors_impl( &*api_that_guard, &api_query, api_count, + api_exact, ))?; Ok(output_ok) })()) From 8304bca71c18fc8c58ee08fce3a4587f07f52824 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 4 Aug 2025 14:19:04 +0530 Subject: [PATCH 088/156] empty list error --- .../lib/ui/viewer/people/person_gallery_suggestion.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mobile/apps/photos/lib/ui/viewer/people/person_gallery_suggestion.dart b/mobile/apps/photos/lib/ui/viewer/people/person_gallery_suggestion.dart index 7311e81de5..854c69e4c0 100644 --- a/mobile/apps/photos/lib/ui/viewer/people/person_gallery_suggestion.dart +++ b/mobile/apps/photos/lib/ui/viewer/people/person_gallery_suggestion.dart @@ -101,7 +101,9 @@ class _PersonGallerySuggestionState extends State } else { suggestions = await ClusterFeedbackService.instance .getAllLargePersonSuggestions(); - person = suggestions.first.person; + if (suggestions.isNotEmpty) { + person = suggestions.first.person; + } } if (suggestions.isNotEmpty && mounted) { From a41b7f5535bda1324178153a4357f2dc35774992 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 4 Aug 2025 14:24:36 +0530 Subject: [PATCH 089/156] null check error --- .../lib/ui/viewer/people/person_gallery_suggestion.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/apps/photos/lib/ui/viewer/people/person_gallery_suggestion.dart b/mobile/apps/photos/lib/ui/viewer/people/person_gallery_suggestion.dart index 854c69e4c0..59aa4a869e 100644 --- a/mobile/apps/photos/lib/ui/viewer/people/person_gallery_suggestion.dart +++ b/mobile/apps/photos/lib/ui/viewer/people/person_gallery_suggestion.dart @@ -124,8 +124,8 @@ class _PersonGallerySuggestionState extends State } if (mounted && _fadeController != null && _slideController != null) { - unawaited(_fadeController!.forward()); - unawaited(_slideController!.forward()); + unawaited(_fadeController?.forward()); + unawaited(_slideController?.forward()); } unawaited(_precomputeNextSuggestions()); @@ -385,8 +385,8 @@ class _PersonGallerySuggestionState extends State Future _animateIn() async { if (mounted && _fadeController != null && _slideController != null) { - _slideController!.reset(); - _fadeController!.reset(); + _slideController?.reset(); + _fadeController?.reset(); await Future.wait([ _fadeController!.forward(), _slideController!.forward(), From b5113dd4205043fbe8637c84e62632014e1615bc Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 5 Aug 2025 11:52:03 +0530 Subject: [PATCH 090/156] Add option for exact search of similar files --- .../apps/photos/lib/db/ml/clip_vector_db.dart | 16 ++++++++--- .../similar_images_service.dart | 27 ++++++++++++++++--- .../debug/ml_debug_section_widget.dart | 2 ++ .../lib/ui/tools/similar_images_page.dart | 26 +++++++++++++++++- 4 files changed, 63 insertions(+), 8 deletions(-) diff --git a/mobile/apps/photos/lib/db/ml/clip_vector_db.dart b/mobile/apps/photos/lib/db/ml/clip_vector_db.dart index e78c100067..9fcffecf30 100644 --- a/mobile/apps/photos/lib/db/ml/clip_vector_db.dart +++ b/mobile/apps/photos/lib/db/ml/clip_vector_db.dart @@ -176,11 +176,15 @@ class ClipVectorDB { Future<(Uint64List, Float32List)> searchClosestVectors( List query, int count, + bool exact, ) async { final db = await _vectorDB; try { - final result = - await db.searchVectors(query: query, count: BigInt.from(count)); + final result = await db.searchVectors( + query: query, + count: BigInt.from(count), + exact: exact, + ); return result; } catch (e, s) { _logger.severe("Error searching closest vectors", e, s); @@ -190,10 +194,12 @@ class ClipVectorDB { Future<(BigInt, double)> searchClosestVector( List query, + bool exact, ) async { final db = await _vectorDB; try { - final result = await db.searchVectors(query: query, count: BigInt.one); + final result = + await db.searchVectors(query: query, count: BigInt.one, exact: exact); return (result.$1[0], result.$2[0]); } catch (e, s) { _logger.severe("Error searching closest vector", e, s); @@ -204,12 +210,14 @@ class ClipVectorDB { Future<(List, List)> bulkSearchVectors( List queries, BigInt count, + bool exact, ) async { final db = await _vectorDB; try { final result = await db.bulkSearchVectors( queries: queries, count: count, + exact: exact, ); return result; } catch (e, s) { @@ -230,7 +238,7 @@ class ClipVectorDB { final minimumSimilarity = minimumSimilarityMap[query]!; final textEmbedding = entry.value; final (potentialFileIDs, distances) = - await searchClosestVectors(textEmbedding, 1000); + await searchClosestVectors(textEmbedding, 1000, true); final queryResults = []; for (var i = 0; i < potentialFileIDs.length; i++) { final similarity = 1 - distances[i]; diff --git a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart index ee56868162..880ab3884d 100644 --- a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart +++ b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart @@ -1,11 +1,13 @@ import "dart:math" show max; import "dart:typed_data" show Float32List; +import "package:flutter/foundation.dart" show kDebugMode; import "package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart" show Uint64List; import 'package:logging/logging.dart'; import "package:photos/db/ml/clip_vector_db.dart"; import "package:photos/db/ml/db.dart"; +import "package:photos/extensions/stop_watch.dart"; import 'package:photos/models/file/file.dart'; import "package:photos/models/file/file_type.dart"; import "package:photos/models/ml/vector.dart"; @@ -21,10 +23,18 @@ class SimilarImagesService { /// Returns a list of SimilarFiles, where each SimilarFiles object contains /// a list of files that are perceptually similar - Future> getSimilarFiles(double distanceThreshold) async { + Future> getSimilarFiles( + double distanceThreshold, + bool exact, + ) async { try { + final now = DateTime.now(); final List result = - await _getSimilarFiles(distanceThreshold); + await _getSimilarFiles(distanceThreshold, exact); + final duration = DateTime.now().difference(now); + _logger.info( + "Found ${result.length} similar files in ${duration.inSeconds} seconds for threshold $distanceThreshold and exact $exact", + ); return result; } catch (e, s) { _logger.severe("failed to get similar files", e, s); @@ -32,10 +42,15 @@ class SimilarImagesService { } } - Future> _getSimilarFiles(double distanceThreshold) async { + Future> _getSimilarFiles( + double distanceThreshold, + bool exact, + ) async { + final w = (kDebugMode ? EnteWatch('getSimilarFiles') : null)?..start(); final mlDataDB = MLDataDB.instance; _logger.info("Checking migration and filling clip vector DB"); await mlDataDB.checkMigrateFillClipVectorDB(); + w?.log("checkMigrateFillClipVectorDB"); // Get the embeddings ready for vector search final List allImageEmbeddings = @@ -48,13 +63,16 @@ class SimilarImagesService { final keys = Uint64List.fromList( allImageEmbeddings.map((e) => BigInt.from(e.fileID)).toList(), ); + w?.log("getAllClipVectors"); // Run bulk vector search final (vectorKeys, distances) = await ClipVectorDB.instance.bulkSearchVectors( clipFloat32, BigInt.from(100), + exact, ); + w?.log("bulkSearchVectors"); // Get all files, and create a map of fileID to file final allFiles = Set.from( @@ -66,6 +84,7 @@ class SimilarImagesService { allFileIdsToFile[file.uploadedFileID!] = file; } } + w?.log("getAllFilesForSearch"); // Run through the vector search results and create SimilarFiles objects final alreadyUsedFileIDs = {}; @@ -111,9 +130,11 @@ class SimilarImagesService { allSimilarFiles.add(similarFiles); } } + w?.log("going through files"); // Sort the similar files by total size in descending order allSimilarFiles.sort((a, b) => b.totalSize.compareTo(a.totalSize)); + w?.log("sort similar files"); return allSimilarFiles; } diff --git a/mobile/apps/photos/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/apps/photos/lib/ui/settings/debug/ml_debug_section_widget.dart index 93fb519185..b905228ee8 100644 --- a/mobile/apps/photos/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/apps/photos/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -420,6 +420,7 @@ class _MLDebugSectionWidgetState extends State { final (vectorKeys, distances) = await vectorDB.bulkSearchVectors( queries: queries, count: count, + exact: false, ); w?.log( @@ -504,6 +505,7 @@ class _MLDebugSectionWidgetState extends State { final (vectorKeys, distances) = await vectorDB.bulkSearchVectors( queries: clipFloat32, count: count, + exact: false, ); w?.log( diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index cf289e3c17..1c6447a0a3 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -12,6 +12,7 @@ import 'package:photos/theme/ente_theme.dart'; import "package:photos/ui/common/loading_widget.dart"; import 'package:photos/ui/components/buttons/button_widget.dart'; import "package:photos/ui/components/models/button_type.dart"; +import "package:photos/ui/components/toggle_switch_widget.dart"; import "package:photos/ui/viewer/file/detail_page.dart"; import "package:photos/ui/viewer/file/thumbnail_widget.dart"; import "package:photos/utils/dialog_util.dart"; @@ -49,6 +50,7 @@ class _SimilarImagesPageState extends State { double _distanceThreshold = 0.04; // Default value List _similarFilesList = []; SortKey _sortKey = SortKey.size; + bool _exactSearch = false; @override void initState() { @@ -159,6 +161,25 @@ class _SimilarImagesPageState extends State { textAlign: TextAlign.center, ), const SizedBox(height: 48), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Exact search", // TODO: lau: extract string + style: getEnteTextTheme(context).bodyBold, + ), + ToggleSwitchWidget( + value: () => _exactSearch, + onChanged: () async { + if (_isDisposed) return; + setState(() { + _exactSearch = !_exactSearch; + }); + }, + ), + ], + ), + const SizedBox(height: 32), ButtonWidget( labelText: "Find similar images", // TODO: lau: extract string buttonType: ButtonType.primary, @@ -269,8 +290,11 @@ class _SimilarImagesPageState extends State { }); try { + // You can use _toggleValue here for advanced mode features + _logger.info("exact mode: $_exactSearch"); + final similarFiles = await SimilarImagesService.instance - .getSimilarFiles(_distanceThreshold); + .getSimilarFiles(_distanceThreshold, _exactSearch); _logger.info( "Found ${similarFiles.length} groups of similar images", ); From d8b40c1a5536f9511d240050db3b26b04370f949 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 5 Aug 2025 11:59:22 +0530 Subject: [PATCH 091/156] Exact search off by default --- .../apps/photos/lib/db/ml/clip_vector_db.dart | 20 +++++++++---------- .../similar_images_service.dart | 4 ++-- .../lib/ui/tools/similar_images_page.dart | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/mobile/apps/photos/lib/db/ml/clip_vector_db.dart b/mobile/apps/photos/lib/db/ml/clip_vector_db.dart index 9fcffecf30..ce06a01cbe 100644 --- a/mobile/apps/photos/lib/db/ml/clip_vector_db.dart +++ b/mobile/apps/photos/lib/db/ml/clip_vector_db.dart @@ -175,9 +175,9 @@ class ClipVectorDB { Future<(Uint64List, Float32List)> searchClosestVectors( List query, - int count, - bool exact, - ) async { + int count, { + bool exact = false, + }) async { final db = await _vectorDB; try { final result = await db.searchVectors( @@ -193,9 +193,9 @@ class ClipVectorDB { } Future<(BigInt, double)> searchClosestVector( - List query, - bool exact, - ) async { + List query, { + bool exact = false, + }) async { final db = await _vectorDB; try { final result = @@ -209,9 +209,9 @@ class ClipVectorDB { Future<(List, List)> bulkSearchVectors( List queries, - BigInt count, - bool exact, - ) async { + BigInt count, { + bool exact = false, + }) async { final db = await _vectorDB; try { final result = await db.bulkSearchVectors( @@ -238,7 +238,7 @@ class ClipVectorDB { final minimumSimilarity = minimumSimilarityMap[query]!; final textEmbedding = entry.value; final (potentialFileIDs, distances) = - await searchClosestVectors(textEmbedding, 1000, true); + await searchClosestVectors(textEmbedding, 1000); final queryResults = []; for (var i = 0; i < potentialFileIDs.length; i++) { final similarity = 1 - distances[i]; diff --git a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart index 880ab3884d..f87375f6ba 100644 --- a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart +++ b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart @@ -25,7 +25,7 @@ class SimilarImagesService { /// a list of files that are perceptually similar Future> getSimilarFiles( double distanceThreshold, - bool exact, + {bool exact = false,} ) async { try { final now = DateTime.now(); @@ -70,7 +70,7 @@ class SimilarImagesService { await ClipVectorDB.instance.bulkSearchVectors( clipFloat32, BigInt.from(100), - exact, + exact: exact, ); w?.log("bulkSearchVectors"); diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 1c6447a0a3..1d65d37713 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -294,7 +294,7 @@ class _SimilarImagesPageState extends State { _logger.info("exact mode: $_exactSearch"); final similarFiles = await SimilarImagesService.instance - .getSimilarFiles(_distanceThreshold, _exactSearch); + .getSimilarFiles(_distanceThreshold, exact: _exactSearch); _logger.info( "Found ${similarFiles.length} groups of similar images", ); From a3330705b3aef6b57dfdc0fc10605884709e6ea6 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 5 Aug 2025 12:05:00 +0530 Subject: [PATCH 092/156] Fix deprecated method --- mobile/apps/photos/lib/ui/tools/similar_images_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 1d65d37713..7a37fc4f11 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -474,7 +474,7 @@ class _SimilarImagesPageState extends State { text, style: Theme.of(context).textTheme.titleMedium!.copyWith( fontSize: 14, - color: Theme.of(context).iconTheme.color!.withOpacity(0.7), + color: Theme.of(context).iconTheme.color!.withValues(alpha: 0.7), ), ); } From e8f7f9ad6248942a9efde664ef031fa54bfc765b Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 5 Aug 2025 13:31:33 +0530 Subject: [PATCH 093/156] Grid of three --- mobile/apps/photos/lib/ui/tools/similar_images_page.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 7a37fc4f11..d6eb3a2990 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -40,8 +40,8 @@ class SimilarImagesPage extends StatefulWidget { } class _SimilarImagesPageState extends State { - static const crossAxisCount = 4; - static const crossAxisSpacing = 4.0; + static const crossAxisCount = 3; + static const crossAxisSpacing = 16.0; final _logger = Logger("SimilarImagesPage"); bool _isDisposed = false; From c82cd54b7b962b3d15df05d7b08aefd6a28f3dbb Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 6 Aug 2025 12:52:03 +0530 Subject: [PATCH 094/156] Figma design --- .../lib/ui/tools/similar_images_page.dart | 136 +++++++++--------- 1 file changed, 69 insertions(+), 67 deletions(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index d6eb3a2990..9b3aae7d09 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -17,7 +17,6 @@ import "package:photos/ui/viewer/file/detail_page.dart"; import "package:photos/ui/viewer/file/thumbnail_widget.dart"; import "package:photos/utils/dialog_util.dart"; import "package:photos/utils/navigation_util.dart"; -import "package:photos/utils/standalone/data.dart"; enum SimilarImagesPageState { setup, @@ -247,33 +246,37 @@ class _SimilarImagesPageState extends State { itemBuilder: (context, index) { if (index == 0) { // Header item - return Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: getEnteColorScheme(context).fillFaint, - border: Border( - bottom: BorderSide( - color: getEnteColorScheme(context).strokeFaint, - width: 1, - ), + if (flagService.internalUser) { + return Container( + padding: const EdgeInsets.all(16), + // decoration: BoxDecoration( + // color: getEnteColorScheme(context).fillFaint, + // border: Border( + // bottom: BorderSide( + // color: getEnteColorScheme(context).strokeFaint, + // width: 1, + // ), + // ), + // ), + child: Column( + children: [ + Text( + "(I) Found ${_similarFilesList.length} groups of similar images", // TODO: lau: extract string + style: getEnteTextTheme(context).bodyBold, + ), + const SizedBox(height: 4), + Text( + "(I) Threshold: ${_distanceThreshold.toStringAsFixed(2)}", // TODO: lau: extract string + style: getEnteTextTheme(context).mini.copyWith( + color: getEnteColorScheme(context).textMuted, + ), + ), + ], ), - ), - child: Column( - children: [ - Text( - "Found ${_similarFilesList.length} groups of similar images", // TODO: lau: extract string - style: getEnteTextTheme(context).bodyBold, - ), - const SizedBox(height: 4), - Text( - "Threshold: ${_distanceThreshold.toStringAsFixed(2)}", // TODO: lau: extract string - style: getEnteTextTheme(context).mini.copyWith( - color: getEnteColorScheme(context).textMuted, - ), - ), - ], - ), - ); + ); + } else { + return const SizedBox.shrink(); + } } // Similar files groups (index - 1 because first item is header) @@ -349,39 +352,23 @@ class _SimilarImagesPageState extends State { } Widget _buildSimilarFilesGroup(SimilarFiles similarFiles) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(2, 4, 2, 12), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "${similarFiles.files.length} similar images • ${formatBytes(similarFiles.totalSize)}", // TODO: lau: extract string - style: Theme.of(context).textTheme.titleSmall, - ), - const SizedBox(height: 2), - Text( - "Distance: ${similarFiles.furthestDistance.toStringAsFixed(3)}", // TODO: lau: extract string - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: getEnteColorScheme(context).textMuted, - fontSize: 12, - ), - ), - ], + return Padding( + padding: const EdgeInsets.symmetric(horizontal: crossAxisSpacing / 2), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "${similarFiles.files.length} similar images" + + (flagService.internalUser + ? "(I: d: ${similarFiles.furthestDistance.toStringAsFixed(3)})" + : ""), // TODO: lau: extract string + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: getEnteColorScheme(context).textMuted, + // fontSize: 12, ), - ), - ], ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: crossAxisSpacing / 2), - child: GridView.builder( + const SizedBox(height: 16), + GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) { @@ -396,13 +383,13 @@ class _SimilarImagesPageState extends State { gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: crossAxisCount, crossAxisSpacing: crossAxisSpacing, - childAspectRatio: 0.75, + childAspectRatio: 0.70, ), padding: const EdgeInsets.all(0), ), - ), - const SizedBox(height: 16), // Add spacing between groups - ], + const SizedBox(height: 16), // Add spacing between groups + ], + ), ); } @@ -429,14 +416,11 @@ class _SimilarImagesPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( - height: (MediaQuery.of(context).size.width - - (crossAxisSpacing * crossAxisCount)) / - crossAxisCount, + Expanded( child: Hero( tag: "similar_images_" + file.tag, child: ClipRRect( - borderRadius: BorderRadius.circular(4), + borderRadius: BorderRadius.circular(8), child: ThumbnailWidget( file, diskLoadDeferDuration: galleryThumbnailDiskLoadDeferDuration, @@ -448,6 +432,24 @@ class _SimilarImagesPageState extends State { ), ), ), + const SizedBox(height: 4), + Text( + file.displayName, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + fontSize: 12, + color: getEnteColorScheme(context).textBase, + ), + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 2), + Text( + "${file.fileSize! ~/ (1024 * 1024)}MB", // TODO: lau: extract string + style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontSize: 10, + color: getEnteColorScheme(context).textMuted, + ), + ), + const SizedBox(height: 16), ], ), ); From 30641a2df6685fc95e1518fe15845fbf09ea72be Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 6 Aug 2025 14:01:49 +0530 Subject: [PATCH 095/156] Empty delete button --- .../lib/ui/tools/similar_images_page.dart | 58 ++++++++++++++++--- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 9b3aae7d09..17de2ac577 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -357,15 +357,20 @@ class _SimilarImagesPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "${similarFiles.files.length} similar images" + - (flagService.internalUser - ? "(I: d: ${similarFiles.furthestDistance.toStringAsFixed(3)})" - : ""), // TODO: lau: extract string - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: getEnteColorScheme(context).textMuted, - // fontSize: 12, - ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "${similarFiles.files.length} similar images" + + (flagService.internalUser + ? "(I: d: ${similarFiles.furthestDistance.toStringAsFixed(3)})" + : ""), // TODO: lau: extract string + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: getEnteColorScheme(context).textMuted, + ), + ), + _getDeleteButton([]), + ], ), const SizedBox(height: 16), GridView.builder( @@ -455,6 +460,41 @@ class _SimilarImagesPageState extends State { ); } + Widget _getDeleteButton(List files) { + final colorScheme = getEnteColorScheme(context); + + return GestureDetector( + onTap: () async { + // TODO: Implement delete functionality + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), + decoration: BoxDecoration( + color: colorScheme.warning500.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.delete_outline, + size: 12, + color: colorScheme.warning500, + ), + const SizedBox(width: 4), + Text( + "Delete (${files.length})", // TODO: lau: extract string + style: getEnteTextTheme(context).small.copyWith( + color: colorScheme.warning500, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ); + } + Widget _getSortMenu() { Text sortOptionText(SortKey key) { String text = key.toString(); From 8046c6837c0cd428431ffd15521f3c1e501c9f03 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 6 Aug 2025 14:07:09 +0530 Subject: [PATCH 096/156] Better spacing --- mobile/apps/photos/lib/ui/tools/similar_images_page.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 17de2ac577..6d2d96c618 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -40,7 +40,7 @@ class SimilarImagesPage extends StatefulWidget { class _SimilarImagesPageState extends State { static const crossAxisCount = 3; - static const crossAxisSpacing = 16.0; + static const crossAxisSpacing = 12.0; final _logger = Logger("SimilarImagesPage"); bool _isDisposed = false; @@ -353,7 +353,7 @@ class _SimilarImagesPageState extends State { Widget _buildSimilarFilesGroup(SimilarFiles similarFiles) { return Padding( - padding: const EdgeInsets.symmetric(horizontal: crossAxisSpacing / 2), + padding: const EdgeInsets.symmetric(horizontal: crossAxisSpacing), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ From 98598053c75722e015dae47c2c9c8598c3de3644 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 6 Aug 2025 15:04:48 +0530 Subject: [PATCH 097/156] Proper text scheme --- .../lib/ui/tools/similar_images_page.dart | 90 ++++++++----------- 1 file changed, 37 insertions(+), 53 deletions(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 6d2d96c618..8f1505056d 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -91,6 +91,7 @@ class _SimilarImagesPageState extends State { Widget _getSetupView() { final colorScheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); return Padding( padding: const EdgeInsets.all(24), @@ -106,33 +107,31 @@ class _SimilarImagesPageState extends State { const SizedBox(height: 32), Text( "Find similar images", // TODO: lau: extract string - style: getEnteTextTheme(context).h3Bold, + style: textTheme.h3Bold, textAlign: TextAlign.center, ), const SizedBox(height: 16), Text( "Use AI to find images that look similar to each other. Adjust the distance threshold below.", // TODO: lau: extract string - style: getEnteTextTheme(context).body, + style: textTheme.body, textAlign: TextAlign.center, ), const SizedBox(height: 48), Text( "Similarity threshold", // TODO: lau: extract string - style: getEnteTextTheme(context).bodyBold, + style: textTheme.bodyBold, ), const SizedBox(height: 8), Text( "Lower values mean a closer match.", // TODO: lau: extract string - style: getEnteTextTheme(context).mini.copyWith( - color: colorScheme.textMuted, - ), + style: textTheme.miniMuted, ), const SizedBox(height: 16), Row( children: [ Text( "0.01", - style: getEnteTextTheme(context).mini, + style: textTheme.mini, ), Expanded( child: Slider( @@ -150,13 +149,13 @@ class _SimilarImagesPageState extends State { ), Text( "0.15", - style: getEnteTextTheme(context).mini, + style: textTheme.mini, ), ], ), Text( "Current: ${_distanceThreshold.toStringAsFixed(2)}", // TODO: lau: extract string - style: getEnteTextTheme(context).body, + style: textTheme.body, textAlign: TextAlign.center, ), const SizedBox(height: 48), @@ -165,7 +164,7 @@ class _SimilarImagesPageState extends State { children: [ Text( "Exact search", // TODO: lau: extract string - style: getEnteTextTheme(context).bodyBold, + style: textTheme.bodyBold, ), ToggleSwitchWidget( value: () => _exactSearch, @@ -205,6 +204,8 @@ class _SimilarImagesPageState extends State { } Widget _getResultsView() { + final textTheme = getEnteTextTheme(context); + final colorScheme = getEnteColorScheme(context); if (_similarFilesList.isEmpty) { return Center( child: Column( @@ -213,17 +214,17 @@ class _SimilarImagesPageState extends State { Icon( Icons.check_circle_outline, size: 72, - color: getEnteColorScheme(context).primary500, + color: colorScheme.primary500, ), const SizedBox(height: 16), Text( "No Similar Images Found", // TODO: lau: extract string - style: getEnteTextTheme(context).h3Bold, + style: textTheme.h3Bold, ), const SizedBox(height: 8), Text( "Try adjusting the similarity threshold", // TODO: lau: extract string - style: getEnteTextTheme(context).body, + style: textTheme.body, ), const SizedBox(height: 32), ButtonWidget( @@ -249,27 +250,16 @@ class _SimilarImagesPageState extends State { if (flagService.internalUser) { return Container( padding: const EdgeInsets.all(16), - // decoration: BoxDecoration( - // color: getEnteColorScheme(context).fillFaint, - // border: Border( - // bottom: BorderSide( - // color: getEnteColorScheme(context).strokeFaint, - // width: 1, - // ), - // ), - // ), child: Column( children: [ Text( "(I) Found ${_similarFilesList.length} groups of similar images", // TODO: lau: extract string - style: getEnteTextTheme(context).bodyBold, + style: textTheme.bodyBold, ), const SizedBox(height: 4), Text( "(I) Threshold: ${_distanceThreshold.toStringAsFixed(2)}", // TODO: lau: extract string - style: getEnteTextTheme(context).mini.copyWith( - color: getEnteColorScheme(context).textMuted, - ), + style: textTheme.miniMuted, ), ], ), @@ -352,6 +342,7 @@ class _SimilarImagesPageState extends State { } Widget _buildSimilarFilesGroup(SimilarFiles similarFiles) { + final textTheme = getEnteTextTheme(context); return Padding( padding: const EdgeInsets.symmetric(horizontal: crossAxisSpacing), child: Column( @@ -363,13 +354,13 @@ class _SimilarImagesPageState extends State { Text( "${similarFiles.files.length} similar images" + (flagService.internalUser - ? "(I: d: ${similarFiles.furthestDistance.toStringAsFixed(3)})" + ? " (I: d: ${similarFiles.furthestDistance.toStringAsFixed(3)})" : ""), // TODO: lau: extract string - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: getEnteColorScheme(context).textMuted, - ), + style: textTheme.smallMuted.copyWith( + fontWeight: FontWeight.w600, + ), ), - _getDeleteButton([]), + _getSmallDeleteButton([]), ], ), const SizedBox(height: 16), @@ -404,6 +395,7 @@ class _SimilarImagesPageState extends State { List allFiles, int index, ) { + final textTheme = getEnteTextTheme(context); return GestureDetector( onTap: () { routeToPage( @@ -437,22 +429,17 @@ class _SimilarImagesPageState extends State { ), ), ), - const SizedBox(height: 4), + const SizedBox(height: 6), Text( file.displayName, - style: Theme.of(context).textTheme.bodyMedium!.copyWith( - fontSize: 12, - color: getEnteColorScheme(context).textBase, - ), + style: textTheme.small, + maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 2), Text( "${file.fileSize! ~/ (1024 * 1024)}MB", // TODO: lau: extract string - style: Theme.of(context).textTheme.bodySmall!.copyWith( - fontSize: 10, - color: getEnteColorScheme(context).textMuted, - ), + style: textTheme.miniMuted, ), const SizedBox(height: 16), ], @@ -460,8 +447,9 @@ class _SimilarImagesPageState extends State { ); } - Widget _getDeleteButton(List files) { + Widget _getSmallDeleteButton(List files) { final colorScheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); return GestureDetector( onTap: () async { @@ -484,10 +472,9 @@ class _SimilarImagesPageState extends State { const SizedBox(width: 4), Text( "Delete (${files.length})", // TODO: lau: extract string - style: getEnteTextTheme(context).small.copyWith( - color: colorScheme.warning500, - fontWeight: FontWeight.w600, - ), + style: textTheme.smallBold.copyWith( + color: colorScheme.warning500, + ), ), ], ), @@ -496,6 +483,8 @@ class _SimilarImagesPageState extends State { } Widget _getSortMenu() { + final textTheme = getEnteTextTheme(context); + final colorScheme = getEnteColorScheme(context); Text sortOptionText(SortKey key) { String text = key.toString(); switch (key) { @@ -514,10 +503,7 @@ class _SimilarImagesPageState extends State { } return Text( text, - style: Theme.of(context).textTheme.titleMedium!.copyWith( - fontSize: 14, - color: Theme.of(context).iconTheme.color!.withValues(alpha: 0.7), - ), + style: textTheme.miniBold, ); } @@ -533,7 +519,7 @@ class _SimilarImagesPageState extends State { const Padding(padding: EdgeInsets.only(left: 4)), Icon( Icons.sort, - color: getEnteColorScheme(context).strokeBase, + color: colorScheme.strokeBase, size: 20, ), ], @@ -552,9 +538,7 @@ class _SimilarImagesPageState extends State { value: index, child: Text( sortOptionText(SortKey.values[index]).data!, - style: Theme.of(context).textTheme.titleMedium!.copyWith( - fontSize: 14, - ), + style: textTheme.miniBold, ), ); }); From d58f96fb60ec02c67ed2a1e89f050a80a441580f Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 6 Aug 2025 21:30:08 +0530 Subject: [PATCH 098/156] Change default sort to shortest distance --- mobile/apps/photos/lib/ui/tools/similar_images_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 8f1505056d..03af5aaa62 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -48,7 +48,7 @@ class _SimilarImagesPageState extends State { SimilarImagesPageState _pageState = SimilarImagesPageState.setup; double _distanceThreshold = 0.04; // Default value List _similarFilesList = []; - SortKey _sortKey = SortKey.size; + SortKey _sortKey = SortKey.distanceAsc; bool _exactSearch = false; @override From 184ea915fce134f866d1b659df8a8109982f9e4e Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 6 Aug 2025 21:51:05 +0530 Subject: [PATCH 099/156] Make files selectable --- .../photos/lib/models/selected_files.dart | 6 +- .../lib/ui/tools/similar_images_page.dart | 250 ++++++++++++++---- 2 files changed, 207 insertions(+), 49 deletions(-) diff --git a/mobile/apps/photos/lib/models/selected_files.dart b/mobile/apps/photos/lib/models/selected_files.dart index e37368a89c..cdc37ce76f 100644 --- a/mobile/apps/photos/lib/models/selected_files.dart +++ b/mobile/apps/photos/lib/models/selected_files.dart @@ -79,8 +79,10 @@ class SelectedFiles extends ChangeNotifier { return false; } - void clearAll() { - Bus.instance.fire(ClearSelectionsEvent()); + void clearAll({bool fireEvent = true}) { + if (fireEvent) { + Bus.instance.fire(ClearSelectionsEvent()); + } lastSelectionOperationFiles.addAll(files); files.clear(); notifyListeners(); diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 03af5aaa62..34fcfc8b01 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -5,6 +5,7 @@ import 'package:logging/logging.dart'; import 'package:photos/core/constants.dart'; import "package:photos/models/file/file.dart"; +import "package:photos/models/selected_files.dart"; import "package:photos/models/similar_files.dart"; import "package:photos/service_locator.dart"; import "package:photos/services/machine_learning/similar_images_service.dart"; @@ -51,14 +52,18 @@ class _SimilarImagesPageState extends State { SortKey _sortKey = SortKey.distanceAsc; bool _exactSearch = false; + late SelectedFiles _selectedFiles; + @override void initState() { super.initState(); + _selectedFiles = SelectedFiles(); } @override void dispose() { _isDisposed = true; + _selectedFiles.dispose(); super.dispose(); } @@ -70,7 +75,22 @@ class _SimilarImagesPageState extends State { title: const Text("Similar images"), // TODO: lau: extract string actions: _pageState == SimilarImagesPageState.results ? [ - _getSortMenu(), + ListenableBuilder( + listenable: _selectedFiles, + builder: (context, _) { + final selectedCount = _selectedFiles.files.length; + if (selectedCount > 0) { + return IconButton( + onPressed: () { + _selectedFiles.clearAll(fireEvent: false); + }, + icon: const Icon(Icons.clear), + ); + } else { + return _getSortMenu(); + } + }, + ), ] : null, ), @@ -360,7 +380,40 @@ class _SimilarImagesPageState extends State { fontWeight: FontWeight.w600, ), ), - _getSmallDeleteButton([]), + SizedBox( + height: 34, + child: ListenableBuilder( + listenable: _selectedFiles, + builder: (context, _) { + final groupSelectedFiles = similarFiles.files + .where((file) => _selectedFiles.isFileSelected(file)) + .toList(); + final hasAnySelection = _selectedFiles.files.isNotEmpty; + final allGroupFilesSelected = similarFiles.files.every( + (file) => _selectedFiles.isFileSelected(file), + ); + + if (groupSelectedFiles.isNotEmpty) { + return _getSmallDeleteButton(groupSelectedFiles); + } else if (hasAnySelection) { + return _getSmallSelectButton(allGroupFilesSelected, () { + if (allGroupFilesSelected) { + // Unselect all files in this group + _selectedFiles.unSelectAll( + similarFiles.files.toSet(), + ); + } else { + // Select all files in this group + _selectedFiles.selectAll( + similarFiles.files.toSet(), + ); + } + }); + } + return const SizedBox.shrink(); + }, + ), + ), ], ), const SizedBox(height: 16), @@ -396,54 +449,124 @@ class _SimilarImagesPageState extends State { int index, ) { final textTheme = getEnteTextTheme(context); - return GestureDetector( - onTap: () { - routeToPage( - context, - DetailPage( - DetailPageConfiguration( - allFiles, - index, - "similar_images_", - mode: DetailPageMode.minimalistic, - ), + return ListenableBuilder( + listenable: _selectedFiles, + builder: (context, _) { + final bool isSelected = _selectedFiles.isFileSelected(file); + final bool hasAnySelection = _selectedFiles.files.isNotEmpty; + + return GestureDetector( + onTap: () { + if (hasAnySelection) { + // If files are selected, tap should toggle selection + _selectedFiles.toggleSelection(file); + } else { + // If no files selected, tap opens detail page + routeToPage( + context, + DetailPage( + DetailPageConfiguration( + allFiles, + index, + "similar_images_", + mode: DetailPageMode.minimalistic, + ), + ), + ); + } + }, + onLongPress: () { + if (hasAnySelection) { + // If files are selected, long press opens detail page + routeToPage( + context, + DetailPage( + DetailPageConfiguration( + allFiles, + index, + "similar_images_", + mode: DetailPageMode.minimalistic, + ), + ), + ); + } else { + // If no files selected, long press starts selection + _selectedFiles.toggleSelection(file); + } + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Stack( + children: [ + Hero( + tag: "similar_images_" + file.tag, + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: isSelected + ? ColorFiltered( + colorFilter: ColorFilter.mode( + Colors.black.withValues(alpha: 0.4), + BlendMode.darken, + ), + child: ThumbnailWidget( + file, + diskLoadDeferDuration: + galleryThumbnailDiskLoadDeferDuration, + serverLoadDeferDuration: + galleryThumbnailServerLoadDeferDuration, + shouldShowLivePhotoOverlay: true, + key: Key("similar_images_" + file.tag), + ), + ) + : ThumbnailWidget( + file, + diskLoadDeferDuration: + galleryThumbnailDiskLoadDeferDuration, + serverLoadDeferDuration: + galleryThumbnailServerLoadDeferDuration, + shouldShowLivePhotoOverlay: true, + key: Key("similar_images_" + file.tag), + ), + ), + ), + Positioned( + top: 5, + right: 5, + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + switchInCurve: Curves.easeOut, + switchOutCurve: Curves.easeIn, + child: isSelected + ? const Icon( + Icons.check_circle_rounded, + color: Colors.white, + size: 22, + ) + : null, + ), + ), + ], + ), + ), + const SizedBox(height: 6), + Text( + file.displayName, + style: textTheme.small, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 2), + Text( + "${file.fileSize! ~/ (1024 * 1024)}MB", // TODO: lau: extract string + style: textTheme.miniMuted, + ), + const SizedBox(height: 16), + ], ), ); }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Hero( - tag: "similar_images_" + file.tag, - child: ClipRRect( - borderRadius: BorderRadius.circular(8), - child: ThumbnailWidget( - file, - diskLoadDeferDuration: galleryThumbnailDiskLoadDeferDuration, - serverLoadDeferDuration: - galleryThumbnailServerLoadDeferDuration, - shouldShowLivePhotoOverlay: true, - key: Key("similar_images_" + file.tag), - ), - ), - ), - ), - const SizedBox(height: 6), - Text( - file.displayName, - style: textTheme.small, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 2), - Text( - "${file.fileSize! ~/ (1024 * 1024)}MB", // TODO: lau: extract string - style: textTheme.miniMuted, - ), - const SizedBox(height: 16), - ], - ), ); } @@ -451,6 +574,10 @@ class _SimilarImagesPageState extends State { final colorScheme = getEnteColorScheme(context); final textTheme = getEnteTextTheme(context); + if (files.isEmpty) { + return const SizedBox.shrink(); + } + return GestureDetector( onTap: () async { // TODO: Implement delete functionality @@ -482,6 +609,35 @@ class _SimilarImagesPageState extends State { ); } + Widget _getSmallSelectButton(bool unselectAll, void Function() onTap) { + final colorScheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); + + return GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 8, + ), + decoration: BoxDecoration( + color: unselectAll + ? getEnteColorScheme(context).primary500 + : getEnteColorScheme(context).strokeFaint, + borderRadius: BorderRadius.circular(4), + ), + child: Text( + unselectAll + ? "Unselect all" // TODO: lau: extract string + : "Select all", // TODO: lau: extract string + style: textTheme.smallMuted.copyWith( + color: unselectAll ? Colors.white : colorScheme.textMuted, + ), + ), + ), + ); + } + Widget _getSortMenu() { final textTheme = getEnteTextTheme(context); final colorScheme = getEnteColorScheme(context); From 901e1a73dca24458ae12d9b53bc19bd3b72ea6b1 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 6 Aug 2025 22:13:59 +0530 Subject: [PATCH 100/156] auto select files --- .../lib/ui/tools/similar_images_page.dart | 41 +++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 34fcfc8b01..91ca7631f1 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -42,6 +42,7 @@ class SimilarImagesPage extends StatefulWidget { class _SimilarImagesPageState extends State { static const crossAxisCount = 3; static const crossAxisSpacing = 12.0; + static const autoSelectDistanceThreshold = 0.01; final _logger = Logger("SimilarImagesPage"); bool _isDisposed = false; @@ -312,17 +313,13 @@ class _SimilarImagesPageState extends State { "Found ${similarFiles.length} groups of similar images", ); - if (_isDisposed) return; + _similarFilesList = similarFiles; + _pageState = SimilarImagesPageState.results; _sortSimilarFiles(); - _logger.fine( - "Sorted similar files by $_sortKey", - ); + _autoSelectSimilarFiles(); if (_isDisposed) return; - setState(() { - _similarFilesList = similarFiles; - _pageState = SimilarImagesPageState.results; - }); + setState(() {}); return; } catch (e, s) { @@ -361,6 +358,34 @@ class _SimilarImagesPageState extends State { setState(() {}); } + void _autoSelectSimilarFiles() { + final filesToSelect = {}; + int groupsProcessed = 0; + int groupsAutoSelected = 0; + + for (final similarFilesGroup in _similarFilesList) { + groupsProcessed++; + if (similarFilesGroup.furthestDistance < autoSelectDistanceThreshold) { + groupsAutoSelected++; + // Skip the first file (keep it unselected) and select the rest + for (int i = 1; i < similarFilesGroup.files.length; i++) { + filesToSelect.add(similarFilesGroup.files[i]); + } + } + } + + if (filesToSelect.isNotEmpty) { + _selectedFiles.selectAll(filesToSelect); + _logger.info( + "Auto-selected ${filesToSelect.length} files from $groupsAutoSelected/$groupsProcessed groups (threshold: $autoSelectDistanceThreshold)", + ); + } else { + _logger.info( + "No files auto-selected from $groupsProcessed groups (threshold: $autoSelectDistanceThreshold)", + ); + } + } + Widget _buildSimilarFilesGroup(SimilarFiles similarFiles) { final textTheme = getEnteTextTheme(context); return Padding( From a9e31aec8f1c9e863f10e278113a888f16c59377 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 6 Aug 2025 22:21:30 +0530 Subject: [PATCH 101/156] Show more accurate size --- mobile/apps/photos/lib/ui/tools/similar_images_page.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 91ca7631f1..1c546a5337 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -18,6 +18,7 @@ import "package:photos/ui/viewer/file/detail_page.dart"; import "package:photos/ui/viewer/file/thumbnail_widget.dart"; import "package:photos/utils/dialog_util.dart"; import "package:photos/utils/navigation_util.dart"; +import "package:photos/utils/standalone/data.dart"; enum SimilarImagesPageState { setup, @@ -584,7 +585,7 @@ class _SimilarImagesPageState extends State { ), const SizedBox(height: 2), Text( - "${file.fileSize! ~/ (1024 * 1024)}MB", // TODO: lau: extract string + formatBytes(file.fileSize!), style: textTheme.miniMuted, ), const SizedBox(height: 16), From 44e910394286f161e9039d9e7a22220a4364739c Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 7 Aug 2025 10:29:14 +0530 Subject: [PATCH 102/156] bottom buttons --- .../lib/ui/tools/similar_images_page.dart | 136 ++++++++++++------ 1 file changed, 90 insertions(+), 46 deletions(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 1c546a5337..bba156a079 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -76,24 +76,7 @@ class _SimilarImagesPageState extends State { elevation: 0, title: const Text("Similar images"), // TODO: lau: extract string actions: _pageState == SimilarImagesPageState.results - ? [ - ListenableBuilder( - listenable: _selectedFiles, - builder: (context, _) { - final selectedCount = _selectedFiles.files.length; - if (selectedCount > 0) { - return IconButton( - onPressed: () { - _selectedFiles.clearAll(fireEvent: false); - }, - icon: const Icon(Icons.clear), - ); - } else { - return _getSortMenu(); - } - }, - ), - ] + ? [_getSortMenu()] : null, ), body: _getBody(), @@ -264,36 +247,97 @@ class _SimilarImagesPageState extends State { ); } - return ListView.builder( - itemCount: _similarFilesList.length + 1, // +1 for header - itemBuilder: (context, index) { - if (index == 0) { - // Header item - if (flagService.internalUser) { - return Container( - padding: const EdgeInsets.all(16), - child: Column( - children: [ - Text( - "(I) Found ${_similarFilesList.length} groups of similar images", // TODO: lau: extract string - style: textTheme.bodyBold, - ), - const SizedBox(height: 4), - Text( - "(I) Threshold: ${_distanceThreshold.toStringAsFixed(2)}", // TODO: lau: extract string - style: textTheme.miniMuted, - ), - ], - ), - ); - } else { - return const SizedBox.shrink(); - } + return Column( + children: [ + Expanded( + child: ListView.builder( + itemCount: _similarFilesList.length + 1, // +1 for header + itemBuilder: (context, index) { + if (index == 0) { + // Header item + if (flagService.internalUser) { + return Container( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + Text( + "(I) Found ${_similarFilesList.length} groups of similar images", // TODO: lau: extract string + style: textTheme.bodyBold, + ), + const SizedBox(height: 4), + Text( + "(I) Threshold: ${_distanceThreshold.toStringAsFixed(2)}", // TODO: lau: extract string + style: textTheme.miniMuted, + ), + ], + ), + ); + } else { + return const SizedBox.shrink(); + } + } + + // Similar files groups (index - 1 because first item is header) + final similarFiles = _similarFilesList[index - 1]; + return _buildSimilarFilesGroup(similarFiles); + }, + ), + ), + _getBottomActionButtons(), + ], + ); + } + + Widget _getBottomActionButtons() { + return ListenableBuilder( + listenable: _selectedFiles, + builder: (context, _) { + final selectedCount = _selectedFiles.files.length; + final hasSelectedFiles = selectedCount > 0; + + if (!hasSelectedFiles) { + return const SizedBox.shrink(); } - // Similar files groups (index - 1 because first item is header) - final similarFiles = _similarFilesList[index - 1]; - return _buildSimilarFilesGroup(similarFiles); + final colorScheme = getEnteColorScheme(context); + + return SafeArea( + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: crossAxisSpacing, + vertical: 8, + ), + decoration: BoxDecoration( + color: colorScheme.backgroundBase, + ), + child: Column( + children: [ + SizedBox( + width: double.infinity, + child: ButtonWidget( + labelText: + "Delete $selectedCount photos", // TODO: lau: extract string + buttonType: ButtonType.critical, + onTap: () async { + // TODO: Implement delete functionality + }, + ), + ), + const SizedBox(height: 8), + SizedBox( + width: double.infinity, + child: ButtonWidget( + labelText: "Unselect all", // TODO: lau: extract string + buttonType: ButtonType.secondary, + onTap: () async { + _selectedFiles.clearAll(fireEvent: false); + }, + ), + ), + ], + ), + ), + ); }, ); } From 019141ef3be81de88c933b2014780782544b32ba Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 7 Aug 2025 10:34:53 +0530 Subject: [PATCH 103/156] animation --- .../lib/ui/tools/similar_images_page.dart | 85 ++++++++++--------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index bba156a079..652b5102dd 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -295,48 +295,51 @@ class _SimilarImagesPageState extends State { final selectedCount = _selectedFiles.files.length; final hasSelectedFiles = selectedCount > 0; - if (!hasSelectedFiles) { - return const SizedBox.shrink(); - } - - final colorScheme = getEnteColorScheme(context); - - return SafeArea( - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: crossAxisSpacing, - vertical: 8, - ), - decoration: BoxDecoration( - color: colorScheme.backgroundBase, - ), - child: Column( - children: [ - SizedBox( - width: double.infinity, - child: ButtonWidget( - labelText: - "Delete $selectedCount photos", // TODO: lau: extract string - buttonType: ButtonType.critical, - onTap: () async { - // TODO: Implement delete functionality - }, + return AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + switchInCurve: Curves.easeOut, + switchOutCurve: Curves.easeIn, + child: hasSelectedFiles + ? SafeArea( + child: Container( + key: const ValueKey('bottom_buttons'), + padding: const EdgeInsets.symmetric( + horizontal: crossAxisSpacing, + vertical: 8, + ), + decoration: BoxDecoration( + color: getEnteColorScheme(context).backgroundBase, + ), + child: Column( + children: [ + SizedBox( + width: double.infinity, + child: ButtonWidget( + labelText: + "Delete $selectedCount photos", // TODO: lau: extract string + buttonType: ButtonType.critical, + onTap: () async { + // TODO: Implement delete functionality + }, + ), + ), + const SizedBox(height: 8), + SizedBox( + width: double.infinity, + child: ButtonWidget( + labelText: + "Unselect all", // TODO: lau: extract string + buttonType: ButtonType.secondary, + onTap: () async { + _selectedFiles.clearAll(fireEvent: false); + }, + ), + ), + ], + ), ), - ), - const SizedBox(height: 8), - SizedBox( - width: double.infinity, - child: ButtonWidget( - labelText: "Unselect all", // TODO: lau: extract string - buttonType: ButtonType.secondary, - onTap: () async { - _selectedFiles.clearAll(fireEvent: false); - }, - ), - ), - ], - ), - ), + ) + : const SizedBox.shrink(key: ValueKey('empty')), ); }, ); From 2a407b59287c7cf1b03ab35c6438f1ae9dc94c7e Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 7 Aug 2025 11:12:47 +0530 Subject: [PATCH 104/156] track fileIDs, not size for similar files group --- mobile/apps/photos/lib/models/similar_files.dart | 13 ++++++++++--- .../machine_learning/similar_images_service.dart | 3 --- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/mobile/apps/photos/lib/models/similar_files.dart b/mobile/apps/photos/lib/models/similar_files.dart index 8f11340b6e..727ce03682 100644 --- a/mobile/apps/photos/lib/models/similar_files.dart +++ b/mobile/apps/photos/lib/models/similar_files.dart @@ -2,16 +2,23 @@ import "package:photos/models/file/file.dart"; class SimilarFiles { final List files; - final int totalSize; + final Set fileIds; final double furthestDistance; SimilarFiles( this.files, - this.totalSize, this.furthestDistance, - ); + ) : fileIds = files.map((file) => file.uploadedFileID!).toSet(); + + int get totalSize => + files.fold(0, (sum, file) => sum + (file.fileSize ?? 0)); @override String toString() => 'SimilarFiles(files: $files, size: $totalSize, distance: $furthestDistance)'; + + void removeFile(EnteFile file) { + files.remove(file); + fileIds.remove(file.uploadedFileID); + } } diff --git a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart index f87375f6ba..284ea7b4fa 100644 --- a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart +++ b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart @@ -117,14 +117,11 @@ class SimilarImagesService { } if (similarFilesList.isNotEmpty) { similarFilesList.add(firstLoopFile); - int totalSize = 0; for (final file in similarFilesList) { alreadyUsedFileIDs.add(file.uploadedFileID!); - totalSize += file.fileSize ?? 0; } final similarFiles = SimilarFiles( similarFilesList, - totalSize, furthestDistance, ); allSimilarFiles.add(similarFiles); From 2560e5934fa25033f48dde73d2c162379e7105d8 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 7 Aug 2025 13:07:16 +0530 Subject: [PATCH 105/156] Delete method --- .../apps/photos/lib/models/similar_files.dart | 8 +++ .../lib/ui/tools/similar_images_page.dart | 65 +++++++++++++++++-- 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/mobile/apps/photos/lib/models/similar_files.dart b/mobile/apps/photos/lib/models/similar_files.dart index 727ce03682..ee946c499a 100644 --- a/mobile/apps/photos/lib/models/similar_files.dart +++ b/mobile/apps/photos/lib/models/similar_files.dart @@ -13,6 +13,10 @@ class SimilarFiles { int get totalSize => files.fold(0, (sum, file) => sum + (file.fileSize ?? 0)); + bool get isEmpty => files.isEmpty; + + int get length => files.length; + @override String toString() => 'SimilarFiles(files: $files, size: $totalSize, distance: $furthestDistance)'; @@ -21,4 +25,8 @@ class SimilarFiles { files.remove(file); fileIds.remove(file.uploadedFileID); } + + bool containsFile(EnteFile file) { + return fileIds.contains(file.uploadedFileID); + } } diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 652b5102dd..d6406b9af0 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -3,6 +3,7 @@ import "dart:async"; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:photos/core/constants.dart'; +import "package:photos/generated/l10n.dart"; import "package:photos/models/file/file.dart"; import "package:photos/models/selected_files.dart"; @@ -16,6 +17,7 @@ import "package:photos/ui/components/models/button_type.dart"; import "package:photos/ui/components/toggle_switch_widget.dart"; import "package:photos/ui/viewer/file/detail_page.dart"; import "package:photos/ui/viewer/file/thumbnail_widget.dart"; +import "package:photos/utils/delete_file_util.dart"; import "package:photos/utils/dialog_util.dart"; import "package:photos/utils/navigation_util.dart"; import "package:photos/utils/standalone/data.dart"; @@ -319,7 +321,10 @@ class _SimilarImagesPageState extends State { "Delete $selectedCount photos", // TODO: lau: extract string buttonType: ButtonType.critical, onTap: () async { - // TODO: Implement delete functionality + await _deleteFiles( + _selectedFiles.files, + showDialog: true, + ); }, ), ), @@ -460,14 +465,19 @@ class _SimilarImagesPageState extends State { builder: (context, _) { final groupSelectedFiles = similarFiles.files .where((file) => _selectedFiles.isFileSelected(file)) - .toList(); + .toSet(); + final bool allFilesFromGroupSelected = + groupSelectedFiles.length == similarFiles.length; final hasAnySelection = _selectedFiles.files.isNotEmpty; final allGroupFilesSelected = similarFiles.files.every( (file) => _selectedFiles.isFileSelected(file), ); if (groupSelectedFiles.isNotEmpty) { - return _getSmallDeleteButton(groupSelectedFiles); + return _getSmallDeleteButton( + groupSelectedFiles, + allFilesFromGroupSelected, + ); } else if (hasAnySelection) { return _getSmallSelectButton(allGroupFilesSelected, () { if (allGroupFilesSelected) { @@ -643,7 +653,7 @@ class _SimilarImagesPageState extends State { ); } - Widget _getSmallDeleteButton(List files) { + Widget _getSmallDeleteButton(Set files, bool showDialog) { final colorScheme = getEnteColorScheme(context); final textTheme = getEnteTextTheme(context); @@ -653,7 +663,7 @@ class _SimilarImagesPageState extends State { return GestureDetector( onTap: () async { - // TODO: Implement delete functionality + await _deleteFiles(files, showDialog: showDialog); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), @@ -682,6 +692,51 @@ class _SimilarImagesPageState extends State { ); } + Future _deleteFiles( + Set filesToDelete, { + bool showDialog = true, + }) async { + if (filesToDelete.isEmpty) return; + if (showDialog) { + final actionResult = await showChoiceActionSheet( + context, + title: "Delete files", // TODO: lau: extract string + body: + "Are you sure you want to delete these files?", // TODO: lau: extract string + firstButtonLabel: S.of(context).delete, + isCritical: true, + firstButtonOnTap: () async { + try { + await _deleteFilesLogic(filesToDelete); + } catch (e, s) { + _logger.severe("Failed to delete files", e, s); + if (flagService.internalUser) { + await showGenericErrorDialog(context: context, error: e); + } + } + }, + ); + } else { + await _deleteFilesLogic(filesToDelete); + } + } + + Future _deleteFilesLogic(Set filesToDelete) async { + _selectedFiles.unSelectAll(filesToDelete); + for (final file in filesToDelete) { + for (final similarGroup in _similarFilesList) { + if (similarGroup.containsFile(file)) { + similarGroup.removeFile(file); + if (similarGroup.isEmpty) { + _similarFilesList.remove(similarGroup); + } + } + } + } + setState(() {}); + await deleteFilesFromRemoteOnly(context, filesToDelete.toList()); + } + Widget _getSmallSelectButton(bool unselectAll, void Function() onTap) { final colorScheme = getEnteColorScheme(context); final textTheme = getEnteTextTheme(context); From cf81d5860408bd9b270125838b51a19b4f829227 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 7 Aug 2025 13:13:58 +0530 Subject: [PATCH 106/156] Prettify --- .../apps/photos/lib/ui/tools/deduplicate_page.dart | 13 +++++++------ .../photos/lib/ui/tools/similar_images_page.dart | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/mobile/apps/photos/lib/ui/tools/deduplicate_page.dart b/mobile/apps/photos/lib/ui/tools/deduplicate_page.dart index 91fb3d6ed6..a526123505 100644 --- a/mobile/apps/photos/lib/ui/tools/deduplicate_page.dart +++ b/mobile/apps/photos/lib/ui/tools/deduplicate_page.dart @@ -29,8 +29,8 @@ class DeduplicatePage extends StatefulWidget { } class _DeduplicatePageState extends State { - static const crossAxisCount = 4; - static const crossAxisSpacing = 4.0; + static const crossAxisCount = 3; + static const crossAxisSpacing = 12.0; static const headerRowCount = 3; final Set selectedGrids = {}; @@ -277,7 +277,7 @@ class _DeduplicatePageState extends State { width: double.infinity, child: SafeArea( child: Padding( - padding: const EdgeInsets.symmetric(horizontal: crossAxisSpacing / 2), + padding: const EdgeInsets.symmetric(horizontal: crossAxisSpacing), child: TextButton( style: OutlinedButton.styleFrom( backgroundColor: @@ -375,7 +375,8 @@ class _DeduplicatePageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: const EdgeInsets.fromLTRB(2, 4, 2, 12), + padding: const EdgeInsets.fromLTRB( + crossAxisSpacing, 4, crossAxisSpacing, 12,), child: GestureDetector( onTap: () { if (selectedGrids.contains(itemIndex)) { @@ -410,7 +411,7 @@ class _DeduplicatePageState extends State { ), ), Padding( - padding: const EdgeInsets.symmetric(horizontal: crossAxisSpacing / 2), + padding: const EdgeInsets.symmetric(horizontal: crossAxisSpacing), child: GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -475,7 +476,7 @@ class _DeduplicatePageState extends State { child: Hero( tag: "deduplicate_" + file.tag, child: ClipRRect( - borderRadius: BorderRadius.circular(4), + borderRadius: BorderRadius.circular(8), child: ThumbnailWidget( file, diskLoadDeferDuration: galleryThumbnailDiskLoadDeferDuration, diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index d6406b9af0..304b28bd72 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -698,7 +698,7 @@ class _SimilarImagesPageState extends State { }) async { if (filesToDelete.isEmpty) return; if (showDialog) { - final actionResult = await showChoiceActionSheet( + final _ = await showChoiceActionSheet( context, title: "Delete files", // TODO: lau: extract string body: From 6bd5327d50939fa4f8896b2d071429cc0d6656c9 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Fri, 8 Aug 2025 20:00:14 +0530 Subject: [PATCH 107/156] refactor: show black color as default background color in hideAppContent --- mobile/apps/auth/lib/utils/lock_screen_settings.dart | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/mobile/apps/auth/lib/utils/lock_screen_settings.dart b/mobile/apps/auth/lib/utils/lock_screen_settings.dart index 247f7ae4b2..5831efc7e8 100644 --- a/mobile/apps/auth/lib/utils/lock_screen_settings.dart +++ b/mobile/apps/auth/lib/utils/lock_screen_settings.dart @@ -93,7 +93,6 @@ class LockScreenSettings { Future setHideAppContent(bool hideContent) async { if (PlatformUtil.isDesktop()) return; - final bool isLightMode = _preferences.getBool(kIsLightMode) ?? true; !hideContent ? PrivacyScreen.instance.disable() : await PrivacyScreen.instance.enable( @@ -103,11 +102,8 @@ class LockScreenSettings { androidOptions: const PrivacyAndroidOptions( enableSecure: true, ), - backgroundColor: - isLightMode ? const Color(0xffffffff) : const Color(0xff000000), - blurEffect: isLightMode - ? PrivacyBlurEffect.extraLight - : PrivacyBlurEffect.extraLight, + backgroundColor: const Color(0xff000000), + blurEffect: PrivacyBlurEffect.extraLight, ); await _preferences.setBool(keyHideAppContent, hideContent); } From a4e8a70c31b0aeb9a09f9b3626e09579451055a6 Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Mon, 11 Aug 2025 01:05:16 +0000 Subject: [PATCH 108/156] New Crowdin translations by GitHub Action --- .../metadata/android/nn/full_description.txt | 36 ++ .../metadata/android/nn/short_description.txt | 1 + .../fastlane/metadata/android/nn/title.txt | 1 + .../metadata/android/sv/short_description.txt | 2 +- .../fastlane/metadata/android/sv/title.txt | 2 +- .../fastlane/metadata/ios/nn/description.txt | 33 ++ .../fastlane/metadata/ios/nn/keywords.txt | 1 + .../photos/fastlane/metadata/ios/nn/name.txt | 1 + .../fastlane/metadata/ios/nn/subtitle.txt | 1 + .../fastlane/metadata/ios/sv/description.txt | 40 +- .../fastlane/metadata/ios/sv/keywords.txt | 2 +- .../playstore/nn/full_description.txt | 30 ++ .../playstore/nn/short_description.txt | 1 + .../fastlane/metadata/playstore/nn/title.txt | 1 + .../playstore/sv/short_description.txt | 2 +- .../fastlane/metadata/playstore/sv/title.txt | 2 +- mobile/apps/photos/lib/l10n/intl_ar.arb | 55 ++- mobile/apps/photos/lib/l10n/intl_de.arb | 21 +- mobile/apps/photos/lib/l10n/intl_lt.arb | 33 +- mobile/apps/photos/lib/l10n/intl_nn.arb | 313 ++++++++++++++ mobile/apps/photos/lib/l10n/intl_no.arb | 20 + mobile/apps/photos/lib/l10n/intl_pl.arb | 132 +++++- mobile/apps/photos/lib/l10n/intl_ru.arb | 9 +- mobile/apps/photos/lib/l10n/intl_sr.arb | 407 ++++++++++++++++++ mobile/apps/photos/lib/l10n/intl_sv.arb | 13 + mobile/apps/photos/lib/l10n/intl_vi.arb | 14 +- 26 files changed, 1136 insertions(+), 37 deletions(-) create mode 100644 mobile/apps/photos/fastlane/metadata/android/nn/full_description.txt create mode 100644 mobile/apps/photos/fastlane/metadata/android/nn/short_description.txt create mode 100644 mobile/apps/photos/fastlane/metadata/android/nn/title.txt create mode 100644 mobile/apps/photos/fastlane/metadata/ios/nn/description.txt create mode 100644 mobile/apps/photos/fastlane/metadata/ios/nn/keywords.txt create mode 100644 mobile/apps/photos/fastlane/metadata/ios/nn/name.txt create mode 100644 mobile/apps/photos/fastlane/metadata/ios/nn/subtitle.txt create mode 100644 mobile/apps/photos/fastlane/metadata/playstore/nn/full_description.txt create mode 100644 mobile/apps/photos/fastlane/metadata/playstore/nn/short_description.txt create mode 100644 mobile/apps/photos/fastlane/metadata/playstore/nn/title.txt create mode 100644 mobile/apps/photos/lib/l10n/intl_nn.arb diff --git a/mobile/apps/photos/fastlane/metadata/android/nn/full_description.txt b/mobile/apps/photos/fastlane/metadata/android/nn/full_description.txt new file mode 100644 index 0000000000..9ba4fe3143 --- /dev/null +++ b/mobile/apps/photos/fastlane/metadata/android/nn/full_description.txt @@ -0,0 +1,36 @@ +ente is a simple app to backup and share your photos and videos. + +If you've been looking for a privacy-friendly alternative to Google Photos, you've come to the right place. With ente, they are stored end-to-end encrypted (e2ee). This means that only you can view them. + +We have open-source apps across Android, iOS, web and desktop, and your photos will seamlessly sync between all of them in an end-to-end encrypted (e2ee) manner. + +ente also makes it simple to share your albums with your loved ones, even if they aren't on ente. You can share publicly viewable links, where they can view your album and collaborate by adding photos to it, even without an account or app. + +Your encrypted data is replicated to 3 different locations, including a fall-out shelter in Paris. We take posterity seriously and make it easy to ensure that your memories outlive you. + +We are here to make the safest photos app ever, come join our journey! + +FEATURES +- Original quality backups, because every pixel is important +- Family plans, so you can share storage with your family +- Collaborative albums, so you can pool together photos after a trip +- Shared folders, in case you want your partner to enjoy your "Camera" clicks +- Album links, that can be protected with a password +- Ability to free up space, by removing files that have been safely backed up +- Human support, because you're worth it +- Descriptions, so you can caption your memories and find them easily +- Image editor, to add finishing touches +- Favorite, hide and relive your memories, for they are precious +- One-click import from Google, Apple, your hard drive and more +- Dark theme, because your photos look good in it +- 2FA, 3FA, biometric auth +- and a LOT more! + +PERMISSIONS +ente requests for certain permissions to serve the purpose of a photo storage provider, which can be reviewed here: https://github.com/ente-io/ente/blob/f-droid/mobile/android/permissions.md + +PRICING +We don't offer forever free plans, because it is important to us that we remain sustainable and withstand the test of time. Instead we offer affordable plans that you can freely share with your family. You can find more information at ente.io. + +SUPPORT +We take pride in offering human support. If you are our paid customer, you can reach out to team@ente.io and expect a response from our team within 24 hours. diff --git a/mobile/apps/photos/fastlane/metadata/android/nn/short_description.txt b/mobile/apps/photos/fastlane/metadata/android/nn/short_description.txt new file mode 100644 index 0000000000..7a5fe973db --- /dev/null +++ b/mobile/apps/photos/fastlane/metadata/android/nn/short_description.txt @@ -0,0 +1 @@ +ente is an end-to-end encrypted photo storage app \ No newline at end of file diff --git a/mobile/apps/photos/fastlane/metadata/android/nn/title.txt b/mobile/apps/photos/fastlane/metadata/android/nn/title.txt new file mode 100644 index 0000000000..3a4fed48fe --- /dev/null +++ b/mobile/apps/photos/fastlane/metadata/android/nn/title.txt @@ -0,0 +1 @@ +ente - encrypted photo storage \ No newline at end of file diff --git a/mobile/apps/photos/fastlane/metadata/android/sv/short_description.txt b/mobile/apps/photos/fastlane/metadata/android/sv/short_description.txt index 7a5fe973db..fa42784b97 100644 --- a/mobile/apps/photos/fastlane/metadata/android/sv/short_description.txt +++ b/mobile/apps/photos/fastlane/metadata/android/sv/short_description.txt @@ -1 +1 @@ -ente is an end-to-end encrypted photo storage app \ No newline at end of file +ente är en end-to-end-krypterad fotolagringsapp \ No newline at end of file diff --git a/mobile/apps/photos/fastlane/metadata/android/sv/title.txt b/mobile/apps/photos/fastlane/metadata/android/sv/title.txt index 3a4fed48fe..8555fdbe27 100644 --- a/mobile/apps/photos/fastlane/metadata/android/sv/title.txt +++ b/mobile/apps/photos/fastlane/metadata/android/sv/title.txt @@ -1 +1 @@ -ente - encrypted photo storage \ No newline at end of file +ente - krypterad fotolagring \ No newline at end of file diff --git a/mobile/apps/photos/fastlane/metadata/ios/nn/description.txt b/mobile/apps/photos/fastlane/metadata/ios/nn/description.txt new file mode 100644 index 0000000000..a98a74300a --- /dev/null +++ b/mobile/apps/photos/fastlane/metadata/ios/nn/description.txt @@ -0,0 +1,33 @@ +Ente is a simple app to automatically backup and organize your photos and videos. + +If you've been looking for a privacy-friendly alternative to preserve your memories, you've come to the right place. With Ente, they are stored end-to-end encrypted (e2ee). This means that only you can view them. + +We have apps across all platforms, and your photos will seamlessly sync between all your devices in an end-to-end encrypted (e2ee) manner. + +Ente also makes it simple to share your albums with your loved ones. You can either share them directly with other Ente users, end-to-end encrypted; or with publicly viewable links. + +Your encrypted data is stored across multiple locations, including a fall-out shelter in Paris. We take posterity seriously and make it easy to ensure that your memories outlive you. + +We are here to make the safest photos app ever, come join our journey! + +FEATURES +- Original quality backups, because every pixel is important +- Family plans, so you can share storage with your family +- Shared folders, in case you want your partner to enjoy your "Camera" clicks +- Album links, that can be protected with a password and set to expire +- Ability to free up space, by removing files that have been safely backed up +- Image editor, to add finishing touches +- Favorite, hide and relive your memories, for they are precious +- One-click import from all major storage providers +- Dark theme, because your photos look good in it +- 2FA, 3FA, biometric auth +- and a LOT more! + +PRICING +We don't offer forever free plans, because it is important to us that we remain sustainable and withstand the test of time. Instead we offer affordable plans that you can freely share with your family. You can find more information at ente.io. + +SUPPORT +We take pride in offering human support. If you are our paid customer, you can reach out to team@ente.io and expect a response from our team within 24 hours. + +TERMS +https://ente.io/terms diff --git a/mobile/apps/photos/fastlane/metadata/ios/nn/keywords.txt b/mobile/apps/photos/fastlane/metadata/ios/nn/keywords.txt new file mode 100644 index 0000000000..e1462baf51 --- /dev/null +++ b/mobile/apps/photos/fastlane/metadata/ios/nn/keywords.txt @@ -0,0 +1 @@ +photos,photography,family,privacy,cloud,backup,videos,photo,encryption,storage,album,alternative diff --git a/mobile/apps/photos/fastlane/metadata/ios/nn/name.txt b/mobile/apps/photos/fastlane/metadata/ios/nn/name.txt new file mode 100644 index 0000000000..3a991c4abc --- /dev/null +++ b/mobile/apps/photos/fastlane/metadata/ios/nn/name.txt @@ -0,0 +1 @@ +Ente Photos diff --git a/mobile/apps/photos/fastlane/metadata/ios/nn/subtitle.txt b/mobile/apps/photos/fastlane/metadata/ios/nn/subtitle.txt new file mode 100644 index 0000000000..958a35f1c9 --- /dev/null +++ b/mobile/apps/photos/fastlane/metadata/ios/nn/subtitle.txt @@ -0,0 +1 @@ +Encrypted photo storage diff --git a/mobile/apps/photos/fastlane/metadata/ios/sv/description.txt b/mobile/apps/photos/fastlane/metadata/ios/sv/description.txt index 3e64345d9a..c44978ba4e 100644 --- a/mobile/apps/photos/fastlane/metadata/ios/sv/description.txt +++ b/mobile/apps/photos/fastlane/metadata/ios/sv/description.txt @@ -1,33 +1,33 @@ -Ente är en enkel app för att automatiskt säkerhetskopiera och organisera dina foton och videor. +Ente är en smidig app för att automatiskt säkerhetskopiera och organisera dina foton och videor. Om du har letat efter ett integritetsvänligt alternativ för att bevara dina minnen, har du kommit rätt. Med Ente lagras de end-to-end-krypterat (e2ee). Det betyder att endast du kan se dem. -We have apps across all platforms, and your photos will seamlessly sync between all your devices in an end-to-end encrypted (e2ee) manner. +Vi har appar på alla plattformar och dina foton synkroniseras sömlöst mellan alla dina enheter via end-to-end-kryptering (e2ee). -Ente also makes it simple to share your albums with your loved ones. You can either share them directly with other Ente users, end-to-end encrypted; or with publicly viewable links. +Ente gör det också enkelt att dela dina album med dina nära och kära. Du kan antingen dela dem direkt med andra Ente användare (end-to-end-krypterade), eller med publikt synliga länkar. -Your encrypted data is stored across multiple locations, including a fall-out shelter in Paris. We take posterity seriously and make it easy to ensure that your memories outlive you. +Din krypterade data lagras på flera platser, inklusive ett skyddsrum i Paris. Vi tar eftervärlden på allvar och gör det enkelt att se till att dina minnen överlever dig. -We are here to make the safest photos app ever, come join our journey! +Vi är här för att skapa den säkraste foto-appen någonsin, kom och häng med på vår resa! -FEATURES -- Original quality backups, because every pixel is important -- Family plans, so you can share storage with your family -- Shared folders, in case you want your partner to enjoy your "Camera" clicks -- Album links, that can be protected with a password and set to expire -- Ability to free up space, by removing files that have been safely backed up -- Image editor, to add finishing touches -- Favorite, hide and relive your memories, for they are precious -- One-click import from all major storage providers -- Dark theme, because your photos look good in it -- 2FA, 3FA, biometric auth -- and a LOT more! +FUNKTIONER +- Säkerhetskopior av originalkvalitet, eftersom varje pixel är viktig +- Familjeabonnemang, så att du kan dela lagring med din familj +- Delade mappar, om du vill att din partner ska njuta av dina "Kamera-klick" +- Albumlänkar, som kan skyddas med ett lösenord och automatiska förfallodatum +- Möjlighet att frigöra utrymme, genom att ta bort filer som redan har säkerhetskopierats +- Bildredigerare, för att lägga till en finishing touch +- Favoritmarkera, göm, och återupplev dina värdefulla minnen +- Import med ett klick från alla större lagringsleverantörer +- Mörkt tema, eftersom dina bilder ser bra ut i det +- 2FA, 3FA, biometrisk autentisering +- och MYCKET mer! -PRICING -We don't offer forever free plans, because it is important to us that we remain sustainable and withstand the test of time. Instead we offer affordable plans that you can freely share with your family. You can find more information at ente.io. +PRISER +Vi erbjuder inte gratisabonnemang för alltid, eftersom det är viktigt för oss att vi förblir hållbara och tål tidens prövning. Istället erbjuder vi prisvärda abonnemang som du fritt kan dela med din familj. Mer information hittar du på ente.io. SUPPORT -We take pride in offering human support. If you are our paid customer, you can reach out to team@ente.io and expect a response from our team within 24 hours. +Vi är stolta över att erbjuda support med riktiga personer. Om du är en betalande kund, kan du nå ut till team@ente.io och förvänta dig ett svar från vårt team inom 24 timmar. VILLKOR https://ente.io/terms diff --git a/mobile/apps/photos/fastlane/metadata/ios/sv/keywords.txt b/mobile/apps/photos/fastlane/metadata/ios/sv/keywords.txt index 234eb04e17..42a5f0277d 100644 --- a/mobile/apps/photos/fastlane/metadata/ios/sv/keywords.txt +++ b/mobile/apps/photos/fastlane/metadata/ios/sv/keywords.txt @@ -1 +1 @@ -foto, fotografi, familj, integritet, moln, säkerhetskopia, videor, foto, kryptering, lagring, album, alternativ +foton, fotografi, familj, integritet, moln, säkerhetskopia, videor, foto, kryptering, lagring, album, alternativ diff --git a/mobile/apps/photos/fastlane/metadata/playstore/nn/full_description.txt b/mobile/apps/photos/fastlane/metadata/playstore/nn/full_description.txt new file mode 100644 index 0000000000..ec999a783c --- /dev/null +++ b/mobile/apps/photos/fastlane/metadata/playstore/nn/full_description.txt @@ -0,0 +1,30 @@ +Ente is a simple app to automatically backup and organize your photos and videos. + +If you've been looking for a privacy-friendly alternative to preserve your memories, you've come to the right place. With Ente, they are stored end-to-end encrypted (e2ee). This means that only you can view them. + +We have apps across Android, iOS, web and Desktop, and your photos will seamlessly sync between all your devices in an end-to-end encrypted (e2ee) manner. + +Ente also makes it simple to share your albums with your loved ones. You can either share them directly with other Ente users, end-to-end encrypted; or with publicly viewable links. + +Your encrypted data is stored across multiple locations, including a fall-out shelter in Paris. We take posterity seriously and make it easy to ensure that your memories outlive you. + +We are here to make the safest photos app ever, come join our journey! + +✨ FEATURES +- Original quality backups, because every pixel is important +- Family plans, so you can share storage with your family +- Shared folders, in case you want your partner to enjoy your "Camera" clicks +- Album links, that can be protected with a password and set to expire +- Ability to free up space, by removing files that have been safely backed up +- Image editor, to add finishing touches +- Favorite, hide and relive your memories, for they are precious +- One-click import from Google, Apple, your hard drive and more +- Dark theme, because your photos look good in it +- 2FA, 3FA, biometric auth +- and a LOT more! + +💲 PRICING +We don't offer forever free plans, because it is important to us that we remain sustainable and withstand the test of time. Instead we offer affordable plans that you can freely share with your family. You can find more information at ente.io. + +🙋 SUPPORT +We take pride in offering human support. If you are our paid customer, you can reach out to team@ente.io and expect a response from our team within 24 hours. \ No newline at end of file diff --git a/mobile/apps/photos/fastlane/metadata/playstore/nn/short_description.txt b/mobile/apps/photos/fastlane/metadata/playstore/nn/short_description.txt new file mode 100644 index 0000000000..6c00229894 --- /dev/null +++ b/mobile/apps/photos/fastlane/metadata/playstore/nn/short_description.txt @@ -0,0 +1 @@ +Encrypted photo storage - backup, organize and share your photos and videos \ No newline at end of file diff --git a/mobile/apps/photos/fastlane/metadata/playstore/nn/title.txt b/mobile/apps/photos/fastlane/metadata/playstore/nn/title.txt new file mode 100644 index 0000000000..97fdef3be7 --- /dev/null +++ b/mobile/apps/photos/fastlane/metadata/playstore/nn/title.txt @@ -0,0 +1 @@ +Ente Photos \ No newline at end of file diff --git a/mobile/apps/photos/fastlane/metadata/playstore/sv/short_description.txt b/mobile/apps/photos/fastlane/metadata/playstore/sv/short_description.txt index 6c00229894..69e11293d5 100644 --- a/mobile/apps/photos/fastlane/metadata/playstore/sv/short_description.txt +++ b/mobile/apps/photos/fastlane/metadata/playstore/sv/short_description.txt @@ -1 +1 @@ -Encrypted photo storage - backup, organize and share your photos and videos \ No newline at end of file +Krypterad lagring av foton - säkerhetskopiera, organisera och dela dina foton och videor \ No newline at end of file diff --git a/mobile/apps/photos/fastlane/metadata/playstore/sv/title.txt b/mobile/apps/photos/fastlane/metadata/playstore/sv/title.txt index 97fdef3be7..5101b0c2c8 100644 --- a/mobile/apps/photos/fastlane/metadata/playstore/sv/title.txt +++ b/mobile/apps/photos/fastlane/metadata/playstore/sv/title.txt @@ -1 +1 @@ -Ente Photos \ No newline at end of file +Ente Foton \ No newline at end of file diff --git a/mobile/apps/photos/lib/l10n/intl_ar.arb b/mobile/apps/photos/lib/l10n/intl_ar.arb index e6363c83ec..521b19360e 100644 --- a/mobile/apps/photos/lib/l10n/intl_ar.arb +++ b/mobile/apps/photos/lib/l10n/intl_ar.arb @@ -1735,15 +1735,23 @@ "peopleWidgetDesc": "حدد الأشخاص الذين ترغب في ظهورهم على شاشتك الرئيسية.", "albumsWidgetDesc": "حدد الألبومات التي تريد ظهورها على شاشتك الرئيسية.", "memoriesWidgetDesc": "اختر نوع الذكريات التي ترغب في رؤيتها على شاشتك الرئيسية.", + "smartMemories": "ذكريات ذكية", + "pastYearsMemories": "ذكريات السنوات الماضية", "deleteMultipleAlbumDialog": "هل تريد أيضًا حذف الصور (والمقاطع) الموجودة في هذه الألبومات {count} من كافة الألبومات الأخرى التي تشترك فيها؟", "addParticipants": "إضافة مشاركين", "selectedAlbums": "{count} تم تحديد", "actionNotSupportedOnFavouritesAlbum": "الإجراء غير مدعوم في ألبوم المفضلة", + "onThisDayMemories": "ذكريات هذا اليوم", "onThisDay": "في هذا اليوم", + "lookBackOnYourMemories": "استرجع ذكرياتك 🌄", "newPhotosEmoji": " جديد 📸", + "sorryWeHadToPauseYourBackups": "عذراً، لقد اضطررنا إلى إيقاف النسخ الاحتياطي الخاصة بك", "clickToInstallOurBestVersionYet": "انقر لتثبيت أفضل إصدار لنا حتى الآن", "onThisDayNotificationExplanation": "تلقي تذكيرات حول ذكريات مثل اليوم في السنوات السابقة.", + "addMemoriesWidgetPrompt": "أضف أداة ذكريات إلى شاشتك الرئيسية، ثم عد إلى هنا لتخصيصها.", + "addAlbumWidgetPrompt": "أضف أداة ألبوم إلى شاشتك الرئيسية، ثم عد إلى هنا لتخصيصها.", "addPeopleWidgetPrompt": "أضف عنصر واجهة الأشخاص إلى شاشتك الرئيسية ثم عد إلى هنا لتخصيصه.", + "birthdayNotifications": "إشعارات عيد الميلاد", "receiveRemindersOnBirthdays": "استلم تذكيرات عندما يحين عيد ميلاد أحدهم. النقر على الإشعار سينقلك إلى صور الشخص المحتفل بعيد ميلاده.", "happyBirthday": "عيد ميلاد سعيد! 🥳", "birthdays": "أعياد الميلاد", @@ -1758,14 +1766,59 @@ "ignore": "تجاهل", "merge": "دمج", "reset": "إعادة تعيين", + "areYouSureYouWantToIgnoreThisPerson": "هل أنت متأكد من أنك تريد تجاهل هذا الشخص؟", + "areYouSureYouWantToIgnoreThesePersons": "هل أنت متأكد أنك تريد تجاهل هؤلاء الأشخاص؟", "thePersonGroupsWillNotBeDisplayed": "لن تظهر مجموعات الأشخاص في قسم الأشخاص بعد الآن. ستظل الصور دون تغيير.", "thePersonWillNotBeDisplayed": "لن يتم عرض هذا الشخص في قسم الأشخاص بعد الآن. الصور ستبقى كما هي دون تغيير.", "areYouSureYouWantToMergeThem": "هل أنت متأكد من رغبتك في دمجهم؟", "allUnnamedGroupsWillBeMergedIntoTheSelectedPerson": "سيتم دمج جميع المجموعات غير المسماة مع الشخص المحدد. يمكن التراجع عن هذا الإجراء لاحقًا من خلال نظرة عامة على سجل الاقتراحات التابع لهذا الشخص.", "yesIgnore": "نعم، تجاهل", + "same": "نفس", + "different": "مختلف", + "sameperson": "نفس الشخص؟", + "cLTitle1": "محرر الصور المتقدم", + "cLDesc1": "نحن بصدد إطلاق محرر صور جديد ومتقدم يضيف المزيد من إطارات الاقتصاص، والإعدادات المسبقة للفلاتر من أجل تعديلات سريعة، وخيارات الضبط الدقيق التي تشمل التشبع، والتباين، والسطوع، ودرجة الحرارة، وغير ذلك الكثير. يتضمن المحرر الجديد أيضا القدرة على الرسم على صورك وإضافة الرموز التعبيرية كملصقات.", + "cLTitle2": "ألبومات ذكية", + "cLTitle3": "معرض محسن", + "cLTitle4": "تمرير أسرع", "thisWeek": "هذا الأسبوع", "lastWeek": "الأسبوع الماضي", "thisMonth": "هذا الشهر", "thisYear": "هذه السنة", - "day": "اليوم" + "groupBy": "تجميع حسب", + "faceThumbnailGenerationFailed": "تعذر إنشاء الصور المصغرة للوجه", + "fileAnalysisFailed": "تعذر تحليل الملف", + "editAutoAddPeople": "تحرير إضافة الأشخاص التلقائية", + "autoAddPeople": "إضافة الأشخاص التلقائية", + "autoAddToAlbum": "إضافة تلقائية إلى الألبوم", + "addingPhotos": "جاري إضافة الصور", + "gettingReady": "جاري الإعداد", + "addSomePhotosDesc2": "وجوه مألوفة", + "addSomePhotosDesc3": "\nللبدء بـ", + "ignorePerson": "تجاهل الشخص", + "analysis": "تحليل", + "doesGroupContainMultiplePeople": "هل هذه المجموعة تحتوي على أشخاص متعددين؟", + "automaticallyAnalyzeAndSplitGrouping": "سنقوم تلقائيا بتحليل المجموعة لتحديد ما إذا كان هناك عدة أشخاص، وفصلهم مرة أخرى. قد يستغرق هذا بضع ثوان.", + "layout": "التخطيط", + "day": "اليوم", + "peopleAutoAddDesc": "حدد الأشخاص الذين تريد إضافتهم تلقائيا إلى الألبوم", + "undo": "تراجع", + "redo": "إعادة", + "filter": "فلتر", + "adjust": "تعديل", + "draw": "رسم", + "sticker": "ملصق", + "brushColor": "لون الفرشاة", + "font": "الخط", + "background": "خلفية", + "align": "محاذاة", + "addedToAlbums": "{count, plural, =1{تمت الإضافة إلى ألبوم واحد بنجاح} other{تم الإضافة إلى {count} ألبومات بنجاح}}", + "@addedToAlbums": { + "description": "Message shown when items are added to albums", + "placeholders": { + "count": { + "type": "int" + } + } + } } \ No newline at end of file diff --git a/mobile/apps/photos/lib/l10n/intl_de.arb b/mobile/apps/photos/lib/l10n/intl_de.arb index 82240e31b2..afba351302 100644 --- a/mobile/apps/photos/lib/l10n/intl_de.arb +++ b/mobile/apps/photos/lib/l10n/intl_de.arb @@ -1800,5 +1800,24 @@ "automaticallyAnalyzeAndSplitGrouping": "Wir analysieren die Gruppierung automatisch, um festzustellen, ob mehrere Personen vorhanden sind, und trennen sie erneut. Dies kann ein paar Sekunden dauern.", "layout": "Layout", "day": "Tag", - "peopleAutoAddDesc": "Wähle die Personen, die du automatisch zum Album hinzufügen möchtest" + "peopleAutoAddDesc": "Wähle die Personen, die du automatisch zum Album hinzufügen möchtest", + "undo": "Rückgängig", + "redo": "Wiederherstellen", + "filter": "Filter", + "adjust": "Anpassen", + "draw": "Zeichnen", + "sticker": "Sticker", + "brushColor": "Pinselfarbe", + "font": "Schriftart", + "background": "Hintergrund", + "align": "Ausrichten", + "addedToAlbums": "{count, plural, =1{Erfolgreich zu einem Album hinzugefügt} other{Erfolgreich zu {count} Alben hinzugefügt}}", + "@addedToAlbums": { + "description": "Message shown when items are added to albums", + "placeholders": { + "count": { + "type": "int" + } + } + } } \ No newline at end of file diff --git a/mobile/apps/photos/lib/l10n/intl_lt.arb b/mobile/apps/photos/lib/l10n/intl_lt.arb index 20266b1cc2..ae339750e2 100644 --- a/mobile/apps/photos/lib/l10n/intl_lt.arb +++ b/mobile/apps/photos/lib/l10n/intl_lt.arb @@ -1776,6 +1776,14 @@ "same": "Tas pats", "different": "Skirtingas", "sameperson": "Tas pats asmuo?", + "cLTitle1": "Pažangi vaizdų rengyklė", + "cLDesc1": "Mes išleidžiame naują ir pažangią vaizdų rengyklę, kurioje yra daugiau apkirpimo rėmelių, filtro nustatymų sparčiams redagavimams, tikslaus sureguliavimo parinkčių, įskaitant sodrumą, kontrastą, skaistį, temperatūrą ir daug daugiau. Naujoji rengyklė taip pat suteikia galimybę piešti ant nuotraukų ir pridėti jaustukus kaip lipdukus.", + "cLTitle2": "Išmanieji albumai", + "cLDesc2": "Dabar galite automatiškai įtraukti pasirinktų asmenų nuotraukas į bet kurį albumą. Tiesiog eikite į albumą ir iš išskleidžiamojo meniu pasirinkite „Automatiškai įtraukti asmenis“. Jei naudojama kartu su bendrinimu albumu, nuotraukas galite bendrinti be jokių paspaudimų.", + "cLTitle3": "Patobulinta galerija", + "cLDesc3": "Pridėjome galimybę sugrupuoti galeriją pagal savaites, mėnesius ir metus. Dabar galite pritaikyti galeriją taip, kad ji atrodytų būtent taip, kaip norite su šiomis naujomis grupavimo parinktimis ir pasirinktiniais tinkleliais.", + "cLTitle4": "Spartesnis slinkimas", + "cLDesc4": "Kartu su daugybe vidinių patobulinimų pagerinti galerijos slinkimo patirtį, mes taip pat pertvarkėme slinkties juostą, kad joje būtų rodomi žymekliai, leidžiantys sparčiai pereiti per laiko juostą.", "indexingPausedStatusDescription": "Indeksavimas pristabdytas. Jis bus automatiškai tęsiamas, kai įrenginys bus parengtas. Įrenginys laikomas parengtu, kai jo akumuliatoriaus įkrovos lygis, akumuliatoriaus būklė ir terminė būklė yra normos ribose.", "thisWeek": "Šią savaitę", "lastWeek": "Praėjusią savaitę", @@ -1784,8 +1792,31 @@ "groupBy": "Grupuoti pagal", "faceThumbnailGenerationFailed": "Nepavyksta sugeneruoti veido miniatiūrų.", "fileAnalysisFailed": "Nepavyksta išanalizuoti failo.", + "editAutoAddPeople": "Redaguoti automatiškai įtrauktus asmenis", + "autoAddPeople": "Automatiškai įtraukti asmenis", + "autoAddToAlbum": "Automatiškai įtraukti į albumą", + "shouldRemoveFilesSmartAlbumsDesc": "Ar reikia pašalinti failus, susijusius su asmeniu, kuris anksčiau buvo atrinktas išmaniuosiuose albumuose?", + "addingPhotos": "Įtraukiamos nuotraukos", + "gettingReady": "Pasirengiama", + "addSomePhotosDesc1": "Įtraukite šiek tiek nuotraukų arba pasirinkite ", + "addSomePhotosDesc2": "pažįstamus veidus", + "addSomePhotosDesc3": "\niš pradžių.", "ignorePerson": "Ignoruoti asmenį", + "mixedGrouping": "Sumaišytas grupavimas?", "analysis": "Analizė", + "doesGroupContainMultiplePeople": "Ar šiame grupavime yra keli asmenys?", + "automaticallyAnalyzeAndSplitGrouping": "Mes automatiškai išanalizuosime grupavimą, kad nustatytume, ar yra keli asmenys, ir vėl juos atskirsime. Tai gali užtrukti keletą sekundžių.", "layout": "Išdėstymas", - "day": "Diena" + "day": "Diena", + "peopleAutoAddDesc": "Pasirinkite asmenis, kuriuos norite automatiškai įtraukti į albumą.", + "undo": "Anuliuoti", + "redo": "Grąžinti", + "filter": "Filtruoti", + "adjust": "Priderinti", + "draw": "Piešti", + "sticker": "Lipdukas", + "brushColor": "Teptuko spalva", + "font": "Šriftas", + "background": "Fonas", + "align": "Lygiuoti" } \ No newline at end of file diff --git a/mobile/apps/photos/lib/l10n/intl_nn.arb b/mobile/apps/photos/lib/l10n/intl_nn.arb new file mode 100644 index 0000000000..1d48d2df40 --- /dev/null +++ b/mobile/apps/photos/lib/l10n/intl_nn.arb @@ -0,0 +1,313 @@ +{ + "@@locale ": "en", + "enterYourEmailAddress": "Skriv inn e-postadressa di", + "enterYourNewEmailAddress": "Skriv inn den nye e-postadressa di", + "accountWelcomeBack": "Velkommen tilbake!", + "emailAlreadyRegistered": "E-postadressa er registrert frå før.", + "emailNotRegistered": "E-postadressa er ikkje registrert.", + "email": "E-post", + "cancel": "Avbryt", + "verify": "Stadfest", + "invalidEmailAddress": "Ugyldig e-postadresse", + "enterValidEmail": "Skriv inn ei gyldig e-postadresse.", + "deleteAccount": "Slett konto", + "askDeleteReason": "Kva er hovudgrunnen til at du vil sletta kontoen din?", + "feedback": "Tilbakemelding", + "confirmDeletePrompt": "Ja, eg vil sletta denne kontoen og alle tilhøyrande data frå alle appar permanent.", + "confirmAccountDeletion": "Stadfest sletting av konto", + "deleteAccountPermanentlyButton": "Slett konto permanent", + "yourAccountHasBeenDeleted": "Kontoen er sletta", + "sendEmail": "Send e-post", + "entePhotosPerm": "Ente treng løyve til å kunna ta vare på fotoa dine", + "ok": "OK", + "createAccount": "Opprett konto", + "createNewAccount": "Opprett konto", + "password": "Passord", + "confirmPassword": "Stadfest passord", + "activeSessions": "Aktive økter", + "oops": "Uff då", + "somethingWentWrongPleaseTryAgain": "Det oppstod ein feil. Prøv på nytt.", + "thisWillLogYouOutOfThisDevice": "Dette loggar deg ut frå denne eininga.", + "thisWillLogYouOutOfTheFollowingDevice": "Dette loggar deg ut frå dei følgjande einingane:", + "terminateSession": "Avslutta økt?", + "terminate": "Avslutt", + "thisDevice": "Denne eininga", + "recoverButton": "Gjenopprett", + "recoverySuccessful": "Gjenoppretting var vellukka.", + "decrypting": "Dekrypterer …", + "incorrectRecoveryKeyTitle": "Gjenopprettingsnøkkelen er feil", + "incorrectRecoveryKeyBody": "Gjenopprettingsnøkkelen som du skreiv inn er feil", + "forgotPassword": "Gløymt passordet", + "enterYourRecoveryKey": "Skriv inn gjenopprettingsnøkkelen", + "noRecoveryKey": "Ingen gjenopprettingsnøkkel?", + "sorry": "Orsak", + "verifyEmail": "Stadfest e-postadresse", + "resendEmail": "Send e-posten på nytt", + "weHaveSendEmailTo": "Vi har sendt ein e-post til {email} ", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "setPasswordTitle": "Vel passord", + "changePasswordTitle": "Endra passord", + "resetPasswordTitle": "Tilbakestill passord", + "encryptionKeys": "Krypteringsnøklar", + "passwordWarning": "Vi lagrar ikkje dette passordet, så dersom du gløymer det kan vi ikkje dekryptera dataa dine", + "enterPasswordToEncrypt": "Skriv inn eit passord me kan kryptera dataa dine med", + "enterNewPasswordToEncrypt": "Skriv inn eit nytt passord me kan kryptera dataa dine med", + "weakStrength": "Svakt", + "strongStrength": "Sterkt", + "moderateStrength": "Middels sterkt", + "passwordStrength": "Passordstyrke: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + }, + "message": "Password Strength: {passwordStrengthText}" + }, + "passwordChangedSuccessfully": "Passordet er endra", + "generatingEncryptionKeys": "Lagar krypteringsnøklar …", + "pleaseWait": "Vent litt …", + "continueLabel": "Fortsett", + "insecureDevice": "Usikker eining", + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Klarte ikkje laga tryggleiksnøklar på denne eininga.\n\nBruk ei anna eining for å registrera deg.", + "howItWorks": "Slik fungerer det", + "encryption": "Kryptering", + "ackPasswordLostWarning": "Eg forstår at dersom eg gløymer passordet mitt kan alle dataa mine gå tapt, fordi dei er ende-til-ende-krypterte.", + "privacyPolicyTitle": "Personvernerklæring", + "termsOfServicesTitle": "Vilkår", + "signUpTerms": "Eg godtek tenestevilkåra og personvernerklæringa", + "logInLabel": "Logg på", + "changeEmail": "Endra e-postadresse", + "enterYourPassword": "Skriv inn passordet ditt", + "welcomeBack": "Velkommen tilbake!", + "incorrectPasswordTitle": "Ugyldig passord", + "pleaseTryAgain": "Prøv på nytt", + "recreatePasswordTitle": "Opprett passord på nytt", + "useRecoveryKey": "Bruk gjenopprettingsnøkkel", + "recreatePasswordBody": "Den gjeldande eininga er ikkje kraftig nok til å kunna stadfesta passordet ditt, men me kan laga det på nytt ved å bruka ein metode som vil fungere på alle einingar.\n\nLogg på ved å bruka gjenopprettingsnøkkelen din og lag passordet på nytt (du kan gjenbruka passordet viss du vil).", + "verifyPassword": "Stadfest passord", + "recoveryKey": "Gjenopprettingsnøkkel", + "recoveryKeyOnForgotPassword": "Viss du gløymer passordet, er det berre ved å bruka denne nøkkelen at du kan gjenoppretta dataa dine.", + "recoveryKeySaveDescription": "Me tek ikkje vare på denne nøkkelen. Denne nøkkelen på 24 ord må du oppbevara på ein trygg stad.", + "doThisLater": "Gjer dette seinare", + "saveKey": "Lagra nøkkel", + "recoveryKeyCopiedToClipboard": "Gjenopprettingsnøkkel kopiert til utklippstavla", + "recoverAccount": "Gjenopprett konto", + "recover": "Gjenopprett", + "dropSupportEmail": "Send ein e-post til {supportEmail} frå e-postadressa du registrerte deg med", + "@dropSupportEmail": { + "placeholders": { + "supportEmail": { + "description": "The support email address", + "type": "String", + "example": "support@ente.io" + } + } + }, + "twofactorSetup": "Oppsett av tofaktor", + "enterCode": "Skriv inn kode", + "scanCode": "Skann kode", + "codeCopiedToClipboard": "Kode kopiert til utklippstavla", + "copypasteThisCodentoYourAuthenticatorApp": "Kopier og lim inn koden\ni autentikator-appen", + "tapToCopy": "trykk for å kopiera", + "scanThisBarcodeWithnyourAuthenticatorApp": "Skann denne strekkoden med\nautentikator-appen", + "enterThe6digitCodeFromnyourAuthenticatorApp": "Skriv inn den seks-sifra koden\nfrå autentikator-appen", + "confirm": "Stadfest", + "setupComplete": "Oppsettet er fullført", + "saveYourRecoveryKeyIfYouHaventAlready": "Lagra gjenopprettingsnøkkelen viss du ikkje alt har gjort det", + "twofactorAuthenticationPageTitle": "Tofaktorautentisering", + "verifyingRecoveryKey": "Stadfestar gjenopprettingsnøkkel …", + "recoveryKeyVerified": "Gjenopprettingsnøkkelen er stadfesta", + "recoveryKeySuccessBody": "Flott! Gjenopprettingsnøkkelen er gyldig. Takk for at du stadfesta han.\n\nHugs å oppbevara gjenopprettingsnøkkelen på ein trygg stad.", + "invalidRecoveryKey": "Gjenopprettingsnøkkelen er ikkje gyldig. Pass på at han inneheld 24 ord og er stava riktig.\n\nViss du skreiv inn ein eldre gjenopprettingskode, pass på at han inneheld 64 teikn.", + "invalidKey": "Ugyldig nøkkel", + "tryAgain": "Prøv på nytt", + "viewRecoveryKey": "Vis gjenopprettingsnøkkel", + "confirmRecoveryKey": "Stadfest gjenopprettingsnøkkel", + "recoveryKeyVerifyReason": "Viss du gløymer passordet, er det berre ved å bruka gjenopprettingsnøkkelen at du kan gjenoppretta fotoa dine. Vel «Konto» frå «Innstillingar»-menyen for å finna gjenopprettingsnøkkelen.\n\nSkriv inn gjenopprettingsnøkkelen her for å stadfesta at du har lagra han på riktig måte.", + "confirmYourRecoveryKey": "Stadfest gjenopprettingsnøkkelen din", + "addANewEmail": "Legg til ei ny e-postadresse", + "enterEmail": "Skriv inn e-postadresse", + "albumOwner": "Eigar", + "@albumOwner": { + "description": "Role of the album owner" + }, + "you": "Deg", + "enterPassword": "Skriv inn passord", + "removeLink": "Fjern lenkje", + "manageLink": "Handsam lenkje", + "done": "Ferdig", + "details": "Detaljar", + "faq": "Spørsmål og svar", + "help": "Hjelp", + "oopsSomethingWentWrong": "Uff då. Noko gjekk gale.", + "addingToFavorites": "Legger til i favorittar …", + "removingFromFavorites": "Fjernar frå favorittar …", + "sorryCouldNotAddToFavorites": "Klarte ikkje leggja til i favorittar.", + "sorryCouldNotRemoveFromFavorites": "Klarte ikkje fjerna frå favorittar.", + "archive": "Arkiv", + "createAlbumActionHint": "Trykk lenge for å velja foto, og trykk på pluss-ikonet for å oppretta eit album", + "importing": "Importerer …", + "failedToLoadAlbums": "Feil ved lasting av album", + "hidden": "Gøymde", + "authToViewYourHiddenFiles": "Autentiser deg for å visa gøymde filer", + "authToViewTrashedFiles": "Autentiser deg for å visa filer i papirkorga", + "trash": "Papirkorg", + "uncategorized": "Ikkje-kategoriserte", + "videoSmallCase": "video", + "photoSmallCase": "foto", + "deleteFromBoth": "Slett frå begge", + "newAlbum": "Nytt album", + "albums": "Albums", + "memoryCount": "{count, plural, =0{ingen minne} one{{formattedCount} minne} other{{formattedCount} minne}}", + "@memoryCount": { + "description": "The text to display the number of memories", + "type": "text", + "placeholders": { + "count": { + "example": "1", + "type": "int" + }, + "formattedCount": { + "type": "String", + "example": "11.513, 11,511" + } + } + }, + "selectedPhotos": "{count} valde", + "@selectedPhotos": { + "description": "Display the number of selected photos", + "type": "text", + "placeholders": { + "count": { + "example": "5", + "type": "int" + } + } + }, + "selectedPhotosWithYours": "{count} valde ({yourCount} av desse er dine)", + "@selectedPhotosWithYours": { + "description": "Display the number of selected photos, including the number of selected photos owned by the user", + "type": "text", + "placeholders": { + "count": { + "example": "12", + "type": "int" + }, + "yourCount": { + "example": "2", + "type": "int" + } + } + }, + "advancedSettings": "Avansert", + "@advancedSettings": { + "description": "The text to display in the advanced settings section" + }, + "discover_babies": "Babyar", + "discover_pets": "Kjæledyr", + "discover_selfies": "Selfiar", + "discover_food": "Mat", + "discover_celebrations": "Feiringar", + "discover_sunset": "Solnedgang", + "discover_hills": "Bakkar", + "loadingModel": "Lastar ned modellar  …", + "waitingForWifi": "Ventar på Wi-Fi …", + "status": "Status", + "indexedItems": "Indekserte element", + "selectedFoldersWillBeEncryptedAndBackedUp": "Dei valde mappene vert krypterte og reservekopierte", + "unselectAll": "Fjern all merking", + "selectAll": "Merk alle", + "skip": "Hopp over", + "backupSettings": "Innstillingar for reservekopiering", + "backupStatus": "Status for reservekopiering", + "backupStatusDescription": "Reservekopierte element vert viste her", + "backupOverMobileData": "Ta reservekopi via mobildata", + "backupVideos": "Ta reservekopi av videoar", + "youAreOnTheLatestVersion": "Du har siste versjon", + "account": "Konto", + "manageSubscription": "Handsam abonnement", + "authToChangeYourEmail": "Autentiser for å endra e-postadresse", + "changePassword": "Endra passord", + "authToChangeYourPassword": "Autentiser for å endra passord", + "exportYourData": "Eksporter dataa dine", + "logout": "Logg ut", + "areYouSureYouWantToLogout": "Er du sikker på at du vil logga ut?", + "yesLogout": "Ja, logg ut", + "aNewVersionOfEnteIsAvailable": "Ein ny versjon av Ente er tilgjengeleg.", + "update": "Oppdater", + "installManually": "Installer manuelt", + "criticalUpdateAvailable": "Kritisk oppdatering tilgjengeleg", + "updateAvailable": "Oppdatering tilgjengeleg", + "ignoreUpdate": "Ignorer", + "downloading": "Lastar ned …", + "cannotDeleteSharedFiles": "Kan ikkje sletta delte filer", + "theDownloadCouldNotBeCompleted": "Klarte ikkje fullføra nedlastinga", + "retry": "Prøv på nytt", + "backup": "Reservekopiering", + "freeUpDeviceSpace": "Frigjer plass på eininga", + "freeUpDeviceSpaceDesc": "Frigjer plass på eininga ved å fjerna filer som allereie er reservekopierte.", + "removeDuplicates": "Fjern duplikat", + "removeDuplicatesDesc": "Sjå gjennom og fjern duplikate filer.", + "viewLargeFiles": "Store filer", + "viewLargeFilesDesc": "Vis filer som tek opp mest plass.", + "youHaveSuccessfullyFreedUp": "Du har frigjort {storageSaved}!", + "@youHaveSuccessfullyFreedUp": { + "description": "The text to display when the user has successfully freed up storage", + "type": "text", + "placeholders": { + "storageSaved": { + "example": "1.2 GB", + "type": "String" + } + } + }, + "advanced": "Avansert", + "general": "Generelt", + "security": "Tryggleik", + "faqs": "Spørsmål og svar", + "subscription": "Abonnement", + "startBackup": "Start reservekopiering", + "privateBackups": "Private reservekopiar", + "backupFailed": "Feil ved reservekopiering", + "sorryBackupFailedDesc": "Klarte ikkje reservekopiera fila. Me prøver på nytt seinare.", + "couldNotBackUpTryLater": "Klarte ikkje reservekopiera dataa dine.\nMe prøver på nytt seinare.", + "renameAlbum": "Endra namn på album", + "rename": "Endra namn", + "noExifData": "Ingen Exif-data", + "thisImageHasNoExifData": "Biletet har ingen exif-data", + "exif": "Exif", + "encryptingBackup": "Krypterer reservekopi …", + "renameFile": "Endra namn på fil", + "rotateLeft": "Roter til venstre", + "flip": "Spegelvend", + "rotateRight": "Roter til høgre", + "saveCopy": "Lagra kopi", + "fileInfoAddDescHint": "Legg til skildring …", + "androidCancelButton": "Avbryt", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "crop": "Skjer av", + "rotate": "Roter", + "next": "Neste", + "noFacesFound": "Fann ingen ansikt", + "selectDate": "Vel dato", + "areThey": "Er dette ", + "filter": "Filter", + "adjust": "Juster", + "draw": "Klistremerke", + "brushColor": "Penselfarge" +} \ No newline at end of file diff --git a/mobile/apps/photos/lib/l10n/intl_no.arb b/mobile/apps/photos/lib/l10n/intl_no.arb index a0003c7bf4..00306d6d95 100644 --- a/mobile/apps/photos/lib/l10n/intl_no.arb +++ b/mobile/apps/photos/lib/l10n/intl_no.arb @@ -1,6 +1,7 @@ { "@@locale ": "en", "enterYourEmailAddress": "Skriv inn e-postadressen din", + "enterYourNewEmailAddress": "Skriv inn den nye e-postadressen din", "accountWelcomeBack": "Velkommen tilbake!", "emailAlreadyRegistered": "E-postadressen er allerede registrert.", "emailNotRegistered": "E-postadressen er ikke registrert.", @@ -721,6 +722,7 @@ "type": "text" }, "backupFailed": "Sikkerhetskopiering mislyktes", + "sorryBackupFailedDesc": "Beklager, vi kunne ikke sikkerhetskopiere denne filen akkurat nå, vil vi prøve på nytt senere.", "couldNotBackUpTryLater": "Vi kunne ikke sikkerhetskopiere dine data.\nVi vil prøve på nytt senere.", "enteCanEncryptAndPreserveFilesOnlyIfYouGrant": "Ente kan bare kryptere og bevare filer hvis du gir tilgang til dem", "pleaseGrantPermissions": "Vennligst gi tillatelser", @@ -897,6 +899,7 @@ "authToViewYourMemories": "Vennligst autentiser deg for å se minnene dine", "unlock": "Lås opp", "freeUpSpace": "Frigjør lagringsplass", + "freeUpSpaceSaving": "{count, plural, =1 {Det kan slettes fra enheten for å frigi {formattedSize}} other {De kan slettes fra enheten for å frigjøre {formattedSize}}}", "filesBackedUpInAlbum": "{count, plural, one {1 fil} other {{formattedNumber} filer}} I dette albumet har blitt sikkerhetskopiert", "@filesBackedUpInAlbum": { "description": "Text to tell user how many files have been backed up in the album", @@ -927,6 +930,18 @@ } } }, + "@freeUpSpaceSaving": { + "description": "Text to tell user how much space they can free up by deleting items from the device" + }, + "freeUpAccessPostDelete": "Du kan fortsatt få tilgang til {count, plural, =1 {det} other {dem}} på Ente så lenge du har et aktivt abonnement", + "@freeUpAccessPostDelete": { + "placeholders": { + "count": { + "example": "1", + "type": "int" + } + } + }, "freeUpAmount": "Frigjør {sizeInMBorGB}", "thisEmailIsAlreadyInUse": "Denne e-postadressen er allerede i bruk", "incorrectCode": "Feil kode", @@ -1016,6 +1031,7 @@ "didYouKnow": "Visste du at?", "loadingMessage": "Laster bildene dine...", "loadMessage1": "Du kan dele abonnementet med familien din", + "loadMessage2": "Vi har bevart over 200 millioner minner så langt", "loadMessage3": "Vi beholder 3 kopier av dine data, en i en underjordisk bunker", "loadMessage4": "Alle våre apper har åpen kildekode", "loadMessage5": "Vår kildekode og kryptografi har blitt revidert eksternt", @@ -1253,6 +1269,8 @@ "description": "Subtitle to indicate that the user can find people quickly by name" }, "findPeopleByName": "Finn folk raskt med navn", + "addViewers": "{count, plural, =0 {Legg til seer} =1 {Legg til seer} other {Legg til seere}}", + "addCollaborators": "{count, plural, =0 {Legg til samarbeidspartner} =1 {Legg til samarbeidspartner} other {Legg til samarbeidspartnere}}", "longPressAnEmailToVerifyEndToEndEncryption": "Trykk og hold på en e-post for å bekrefte ende-til-ende-kryptering.", "developerSettingsWarning": "Er du sikker på at du vil endre utviklerinnstillingene?", "developerSettings": "Utviklerinnstillinger", @@ -1264,6 +1282,8 @@ "createCollaborativeLink": "Samarbeidslenke", "search": "Søk", "enterPersonName": "Angi personnavn", + "editEmailAlreadyLinked": "Denne e-postadressen er allerede koblet til {name}.", + "viewPersonToUnlink": "Vis {name} for å fjerne koblingen", "enterName": "Angi navn", "savePerson": "Lagre person", "editPerson": "Rediger person", diff --git a/mobile/apps/photos/lib/l10n/intl_pl.arb b/mobile/apps/photos/lib/l10n/intl_pl.arb index 836add0ac0..d6c9af626c 100644 --- a/mobile/apps/photos/lib/l10n/intl_pl.arb +++ b/mobile/apps/photos/lib/l10n/intl_pl.arb @@ -372,6 +372,21 @@ "deleteFromBoth": "Usuń z obu", "newAlbum": "Nowy album", "albums": "Albumy", + "memoryCount": "{count, plural, =0{brak wspomnień} one{{formattedCount} wspomnienie} few{{formattedCount} wspomnienia} many{{formattedCount} wspomnień} other{{formattedCount} wspomnień}}", + "@memoryCount": { + "description": "The text to display the number of memories", + "type": "text", + "placeholders": { + "count": { + "example": "1", + "type": "int" + }, + "formattedCount": { + "type": "String", + "example": "11.513, 11,511" + } + } + }, "selectedPhotos": "Wybrano {count}", "@selectedPhotos": { "description": "Display the number of selected photos", @@ -779,6 +794,14 @@ "share": "Udostępnij", "unhideToAlbum": "Odkryj do albumu", "restoreToAlbum": "Przywróć do albumu", + "moveItem": "{count, plural, =1 {Przenieś element} few {Przenieś elementy} many {Przenieś elementów} other {Przenieś elementów}}", + "@moveItem": { + "description": "Page title while moving one or more items to an album" + }, + "addItem": "{count, plural, =1 {Dodaj element} few {Dodaj elementy} many {Dodaj elementów} other {Dodaj elementów}}", + "@addItem": { + "description": "Page title while adding one or more items to album" + }, "createOrSelectAlbum": "Utwórz lub wybierz album", "selectAlbum": "Wybierz album", "searchByAlbumNameHint": "Nazwa albumu", @@ -876,6 +899,7 @@ "authToViewYourMemories": "Prosimy uwierzytelnić się, aby wyświetlić swoje wspomnienia", "unlock": "Odblokuj", "freeUpSpace": "Zwolnij miejsce", + "freeUpSpaceSaving": "{count, plural, =1 {Może zostać usunięty z urządzenia, aby zwolnić {formattedSize}} many {Może być usuniętych z urządzenia, aby zwolnić {formattedSize}} other {Mogą być usunięte z urządzenia, aby zwolnić {formattedSize}}}", "filesBackedUpInAlbum": "{count, plural, one {1 plikowi} other {{formattedNumber} plikom}} w tym albumie została bezpiecznie utworzona kopia zapasowa", "@filesBackedUpInAlbum": { "description": "Text to tell user how many files have been backed up in the album", @@ -906,6 +930,18 @@ } } }, + "@freeUpSpaceSaving": { + "description": "Text to tell user how much space they can free up by deleting items from the device" + }, + "freeUpAccessPostDelete": "Nadal możesz mieć dostęp {count, plural, =1 {do tego} other {do tych}} na Ente tak długo, jak masz aktywną subskrypcję", + "@freeUpAccessPostDelete": { + "placeholders": { + "count": { + "example": "1", + "type": "int" + } + } + }, "freeUpAmount": "Zwolnij {sizeInMBorGB}", "thisEmailIsAlreadyInUse": "Ten e-mail jest już używany", "incorrectCode": "Nieprawidłowy kod", @@ -1233,6 +1269,8 @@ "description": "Subtitle to indicate that the user can find people quickly by name" }, "findPeopleByName": "Szybko szukaj osób po imieniu", + "addViewers": "{count, plural, =0 {Dodaj widza} =1 {Dodaj widza} other {Dodaj widzów}}", + "addCollaborators": "{count, plural, =0 {Dodaj współuczestnika} =1 {Dodaj współuczestnika} other {Dodaj współuczestników}}", "longPressAnEmailToVerifyEndToEndEncryption": "Naciśnij i przytrzymaj e-mail, aby zweryfikować szyfrowanie end-to-end.", "developerSettingsWarning": "Czy na pewno chcesz zmodyfikować ustawienia programisty?", "developerSettings": "Ustawienia dla programistów", @@ -1365,6 +1403,16 @@ "enableMachineLearningBanner": "Włącz nauczanie maszynowe dla magicznego wyszukiwania i rozpoznawania twarzy", "searchDiscoverEmptySection": "Obrazy będą wyświetlane tutaj po zakończeniu przetwarzania i synchronizacji", "searchPersonsEmptySection": "Osoby będą wyświetlane tutaj po zakończeniu przetwarzania i synchronizacji", + "viewersSuccessfullyAdded": "{count, plural, =0 {Dodano 0 widzów} =1 {Dodano 1 widza} other {Dodano {count} widzów}}", + "@viewersSuccessfullyAdded": { + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + }, + "description": "Number of viewers that were successfully added to an album." + }, "collaboratorsSuccessfullyAdded": "{count, plural, =0 {Dodano 0 współuczestników} =1 {Dodano 1 współuczestnika} other {Dodano {count} współuczestników}}", "@collaboratorsSuccessfullyAdded": { "placeholders": { @@ -1440,6 +1488,15 @@ }, "currentlyRunning": "aktualnie uruchomiony", "ignored": "ignorowane", + "photosCount": "{count, plural, =0 {0 zdjęć} =1 {1 zdjęcie} few {{count} zdjęcia} many {{count} zdjęć} other {{count} zdjęć}}", + "@photosCount": { + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + } + }, "file": "Plik", "searchSectionsLengthMismatch": "Niezgodność długości sekcji: {snapshotLength} != {searchLength}", "@searchSectionsLengthMismatch": { @@ -1539,6 +1596,7 @@ "joinAlbumSubtextViewer": "aby dodać to do udostępnionych albumów", "join": "Dołącz", "linkEmail": "Połącz adres e-mail", + "link": "Połącz", "noEnteAccountExclamation": "Brak konta Ente!", "orPickFromYourContacts": "lub wybierz ze swoich kontaktów", "emailDoesNotHaveEnteAccount": "{email} nie posiada konta Ente.", @@ -1559,6 +1617,7 @@ } } }, + "reassignMe": "Przypisz \"Mnie\" ponownie", "me": "Ja", "linkEmailToContactBannerCaption": "aby szybciej udostępniać", "@linkEmailToContactBannerCaption": { @@ -1586,6 +1645,7 @@ } }, "selectYourFace": "Wybierz swoją twarz", + "reassigningLoading": "Ponowne przypisywanie...", "reassignedToName": "Ponownie przypisano cię do {name}", "@reassignedToName": { "placeholders": { @@ -1597,12 +1657,19 @@ "saveChangesBeforeLeavingQuestion": "Zapisać zmiany przed wyjściem?", "dontSave": "Nie zapisuj", "thisIsMeExclamation": "To ja!", + "linkPerson": "Połącz osobę", + "linkPersonCaption": "dla lepszych doświadczeń podczas udostępniania", + "@linkPersonCaption": { + "description": "Caption for the 'Link person' title. It should be a continuation of the 'Link person' title. Just like how 'Link person' + 'for better sharing experience' forms a proper sentence in English, the combination of these two strings should also be a proper sentence in other languages." + }, "videoStreaming": "Streamowalne wideo", "processingVideos": "Przetwarzanie wideo", + "streamDetails": "Szczegóły transmisji", "processing": "Przetwarzanie", "queued": "W kolejce", "ineligible": "Nie kwalifikuje się", "failed": "Nie powiodło się", + "playStream": "Odtwórz transmisję", "playOriginal": "Odtwórz oryginał", "joinAlbumConfirmationDialogBody": "Dołączenie do albumu sprawi, że Twój e-mail będzie widoczny dla jego uczestników.", "pleaseWaitThisWillTakeAWhile": "Prosimy czekać, to może zająć chwilę.", @@ -1619,13 +1686,25 @@ "moveSelectedPhotosToOneDate": "Przenieś wybrane zdjęcia na jedną datę", "shiftDatesAndTime": "Zmień daty i czas", "photosKeepRelativeTimeDifference": "Zdjęcia zachowują względną różnicę czasu", + "photocountPhotos": "{count, plural, =0 {Brak zdjęć} =1 {1 zdjęcie} few {{count} zdjęcia} many {{count} zdjęć} other {{count} zdjęć}}", + "@photocountPhotos": { + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + } + }, "appIcon": "Ikona aplikacji", "notThisPerson": "Nie ta osoba?", "selectedItemsWillBeRemovedFromThisPerson": "Wybrane elementy zostaną usunięte z tej osoby, ale nie zostaną usunięte z Twojej biblioteki.", "throughTheYears": "{dateFormat} przez lata", "thisWeekThroughTheYears": "Ten tydzień przez lata", + "thisWeekXYearsAgo": "{count, plural, =1 {W tym tygodniu, {count} rok temu} few {W tym tygodniu, {count} lata temu} many {W tym tygodniu, {count} lat temu} other {W tym tygodniu, {count} lat temu}}", "youAndThem": "Ty i {name}", "admiringThem": "Podziwianie {name}", + "embracingThem": "Obejmowanie {name}", + "partyWithThem": "Impreza z {name}", "hikingWithThem": "Wędrówka z {name}", "feastingWithThem": "Ucztowanie z {name}", "selfiesWithThem": "Selfie z {name}", @@ -1638,6 +1717,7 @@ "personIsAge": "{name} ma {age} lat!", "personTurningAge": "{name} wkrótce będzie mieć {age} lat", "lastTimeWithThem": "Ostatnio z {name}", + "tripToLocation": "Wyjazd do {location}", "tripInYear": "Podróż w {year}", "lastYearsTrip": "Zeszłoroczna podróż", "sunrise": "Na horyzoncie", @@ -1655,6 +1735,7 @@ "peopleWidgetDesc": "Wybierz osoby, które chcesz zobaczyć na ekranie głównym.", "albumsWidgetDesc": "Wybierz albumy, które chcesz zobaczyć na ekranie głównym.", "memoriesWidgetDesc": "Wybierz rodzaj wspomnień, które chcesz zobaczyć na ekranie głównym.", + "smartMemories": "Inteligentne wspomnienia", "pastYearsMemories": "Wspomnienia z ubiegłych lat", "deleteMultipleAlbumDialog": "Usunąć również zdjęcia (i filmy) obecne w tych albumach {count} z wszystkich innych albumów, których są częścią?", "addParticipants": "Dodaj uczestników", @@ -1695,7 +1776,56 @@ "same": "Identyczne", "different": "Inne", "sameperson": "Ta sama osoba?", + "cLTitle1": "Zaawansowany Edytor Obrazów", + "cLDesc1": "Wydajemy nowy i zaawansowany edytor obrazów, który dodaje więcej klatek przycinania, filtry dla szybkich edycji, precyzyjne opcje dostrajania, w tym nasycenie, kontrast, jasność, temperatura i wiele więcej. Nowy edytor zawiera również możliwość rysowania zdjęć i dodawania emotikonów jako naklejki.", + "cLTitle2": "Inteligentne Albumy", + "cLDesc2": "Teraz możesz automatycznie dodawać zdjęcia wybranych osób do dowolnego albumu. Po prostu przejdź do albumu i wybierz \"automatycznie dodaj osoby\" z menu przepełnienia. Jeśli używane razem z udostępnionym albumem, możesz udostępniać zdjęcia bez żadnych kliknięć.", + "cLTitle3": "Ulepszona Galeria", + "cLDesc3": "Dodaliśmy możliwość grupowania Twojej galerii po tygodniach, miesiącach i latach. Możesz teraz spersonalizować swoją galerię, aby dokładnie wyglądać w ten sposób z nowymi opcjami grupowania, wraz z niestandardowymi siatkami", + "cLTitle4": "Szybsze Przewijanie", + "cLDesc4": "Wraz z kilkoma ulepszeniami w celu poprawy doświadczenia galerii, przeprojektowaliśmy również pasek przewijania, aby pokazywać znaczniki, umożliwiając szybki skok po osi czasu.", "indexingPausedStatusDescription": "Indeksowanie zostało wstrzymane. Zostanie automatycznie wznowione, gdy urządzenie będzie gotowe. Urządzenie uznaje się za gotowe, gdy poziom baterii, stan jej zdrowia oraz status termiczny znajdują się w bezpiecznym zakresie.", + "thisWeek": "Ten tydzień", + "lastWeek": "Zeszły tydzień", + "thisMonth": "Ten miesiąc", + "thisYear": "Ten rok", + "groupBy": "Grupuj według", "faceThumbnailGenerationFailed": "Nie można wygenerować miniaturek twarzy", - "fileAnalysisFailed": "Nie można przeanalizować pliku" + "fileAnalysisFailed": "Nie można przeanalizować pliku", + "editAutoAddPeople": "Edytuj automatyczne dodawanie osób", + "autoAddPeople": "Automatycznie dodaj osoby", + "autoAddToAlbum": "Automatycznie dodaj do albumu", + "shouldRemoveFilesSmartAlbumsDesc": "Czy pliki związane z osobą, które zostały wcześniej wybrane w inteligentnych albumach, powinny zostać usunięte?", + "addingPhotos": "Dodawanie zdjęć", + "gettingReady": "Przygotowywanie", + "addSomePhotosDesc1": "Dodaj jakieś zdjęcia lub wybierz ", + "addSomePhotosDesc2": "znane twarze", + "addSomePhotosDesc3": "\nzaczynając od", + "ignorePerson": "Ignoruj osobę", + "mixedGrouping": "Grupowanie mieszane?", + "analysis": "Analiza", + "doesGroupContainMultiplePeople": "Czy ta grupa zawiera wiele osób?", + "automaticallyAnalyzeAndSplitGrouping": "Automatycznie przeanalizujemy grupę, aby ustalić, czy jest wiele osób i oddzielimy ich ponownie. Może to potrwać kilka sekund.", + "layout": "Układ", + "day": "Dzień", + "peopleAutoAddDesc": "Wybierz osoby, które chcesz automatycznie dodać do albumu", + "undo": "Cofnij", + "redo": "Ponów", + "filter": "Filtruj", + "adjust": "Dostosuj", + "draw": "Rysuj", + "sticker": "Naklejka", + "brushColor": "Kolor Pędzla", + "font": "Czcionka", + "background": "Tło", + "align": "Wyrównaj", + "addedToAlbums": "{count, plural, =1{Pomyślnie dodano do 1 albumu} other{Pomyślnie dodano do {count} albumów}}", + "@addedToAlbums": { + "description": "Message shown when items are added to albums", + "placeholders": { + "count": { + "type": "int" + } + } + } } \ No newline at end of file diff --git a/mobile/apps/photos/lib/l10n/intl_ru.arb b/mobile/apps/photos/lib/l10n/intl_ru.arb index 7378594bf4..d3bc0d412a 100644 --- a/mobile/apps/photos/lib/l10n/intl_ru.arb +++ b/mobile/apps/photos/lib/l10n/intl_ru.arb @@ -1778,5 +1778,12 @@ "sameperson": "Тот же человек?", "indexingPausedStatusDescription": "Индексирование приостановлено. Оно автоматически возобновится, когда устройство будет готово. Устройство считается готовым, когда уровень заряда батареи, её состояние и температура находятся в пределах нормы.", "faceThumbnailGenerationFailed": "Не удалось создать миниатюры лиц", - "fileAnalysisFailed": "Не удалось проанализировать файл" + "fileAnalysisFailed": "Не удалось проанализировать файл", + "addingPhotos": "Добавление фото", + "gettingReady": "Идет подготовка", + "addSomePhotosDesc2": "знакомые лица", + "analysis": "Анализ", + "day": "День", + "filter": "Фильтр", + "font": "Шрифт" } \ No newline at end of file diff --git a/mobile/apps/photos/lib/l10n/intl_sr.arb b/mobile/apps/photos/lib/l10n/intl_sr.arb index e875286038..d516e7cff7 100644 --- a/mobile/apps/photos/lib/l10n/intl_sr.arb +++ b/mobile/apps/photos/lib/l10n/intl_sr.arb @@ -141,6 +141,56 @@ "enterThe6digitCodeFromnyourAuthenticatorApp": "Унесите 6-цифрени кôд из\nапликације за аутентификацију", "confirm": "Потврди", "setupComplete": "Постављање завршено", + "lostDevice": "Изгубили сте уређај?", + "invalidKey": "Неисправан кључ", + "tryAgain": "Покушај поново", + "viewRecoveryKey": "Погледај кључ за опоравак", + "confirmRecoveryKey": "Потврди кључ за опоравак", + "recoveryKeyVerifyReason": "Кључ за опоравак рачуна је једини начин да повратите фотографије ако заборавите лозинку. Можете га пронаћи под Подешавања > Рачун.\n\nУнесите ваш кључ за опоравак рачуна како бисмо били сигурни да сте га правилно сачували.", + "confirmYourRecoveryKey": "Потврдите кључ за опоравак", + "addViewer": "Додај посматрача", + "addCollaborator": "Додај сарадника", + "addANewEmail": "Додај нови имејл", + "orPickAnExistingOne": "Или изабери већ постојећи", + "collaboratorsCanAddPhotosAndVideosToTheSharedAlbum": "Сарадници могу додати фотографије и клипове у заједнички албум.", + "enterEmail": "Унеси имејл", + "albumOwner": "Власник", + "@albumOwner": { + "description": "Role of the album owner" + }, + "you": "Ти", + "collaborator": "Сарадник", + "addMore": "Додај још", + "@addMore": { + "description": "Button text to add more collaborators/viewers" + }, + "viewer": "Посматрач", + "remove": "Уклони", + "removeParticipant": "Уклони учесника", + "@removeParticipant": { + "description": "menuSectionTitle for removing a participant" + }, + "manage": "Управљај", + "addedAs": "Додат као", + "changePermissions": "Измените дозволе?", + "yesConvertToViewer": "Да, претвори у посматрача", + "cannotAddMorePhotosAfterBecomingViewer": "{user} неће више моћи да додаје фотографије у овај албум\nИ даље ће моћи да уклони фотографије које је претходно додао", + "allowAddingPhotos": "Дозволи додавање фотографија", + "@allowAddingPhotos": { + "description": "Switch button to enable uploading photos to a public link" + }, + "allowAddPhotosDescription": "Дозволи људима са линком да додају фотографије у заједнички албум.", + "passwordLock": "Закључај помоћу лозинке", + "canNotOpenTitle": "Не могу да отворим овај албум", + "canNotOpenBody": "Жао ми је, овај албум се не може отворити у апликацији.", + "disableDownloadWarningTitle": "Молим, обратите пажњу", + "disableDownloadWarningBody": "Људи и даље могу да направе снимак екрана или да скину ваше фотографије користећи друге апликације", + "allowDownloads": "Дозволи преузимање", + "linkExpired": "Истекао", + "linkEnabled": "Омогућено", + "linkNeverExpires": "Никада", + "setAPassword": "Постави лозинку", + "enterPassword": "Унеси лозинку", "removeLink": "Уклони везу", "manageLink": "Управљај везом", "linkExpiresOn": "Веза ће истећи {expiryTime}", @@ -219,6 +269,38 @@ "codeChangeLimitReached": "Жао нам је, достигли сте максимум броја промена кôда.", "onlyFamilyAdminCanChangeCode": "Молимо вас да контактирате {familyAdminEmail} да бисте променили свој код.", "storageInGB": "{storageAmountInGB} ГБ", + "details": "Више детаља", + "claimMore": "Освоји још!", + "claimFreeStorage": "Освоји бесплатан простор на диску", + "inviteYourFriends": "Позови своје пријатеље", + "referralStep1": "1. Дај овај код својим пријатељима", + "referralsAreCurrentlyPaused": "Препоручивање је тренутно паузирано", + "youCanAtMaxDoubleYourStorage": "* У најбољем случају можете удвостручити ваш простор", + "faq": "Честа питања", + "help": "Помоћ", + "oopsSomethingWentWrong": "Упс, дошло је до грешке", + "peopleUsingYourCode": "Људи који користе ваш позивни код", + "total": "укупно", + "codeUsedByYou": "Код који ви користите", + "freeStorageClaimed": "Освојен бесплатан простор", + "freeStorageUsable": "Бесплатан простор који је могуће искористити", + "removeFromAlbumTitle": "Уклони из албума?", + "removeFromAlbum": "Уклони из албума", + "itemsWillBeRemovedFromAlbum": "Изабране ставке ће бити уклоњене из овог албума", + "addingToFavorites": "Додајем у омиљене", + "removingFromFavorites": "Уклањам из омиљених", + "sorryCouldNotAddToFavorites": "Извини, није додато у омиљене!", + "sorryCouldNotRemoveFromFavorites": "Извини, није уклоњено из омиљених!", + "subscribe": "Претплати се", + "deleteSharedAlbum": "Иѕбриши дељени албум?", + "deleteAlbum": "Избриши албум", + "deleteSharedAlbumDialogBody": "Албум ће бити обрисан за све", + "yesRemove": "Да, уклони", + "removeWithQuestionMark": "Уклони?", + "keepPhotos": "Задржи фотографије", + "deletePhotos": "Обриши фотгорафије", + "inviteToEnte": "Позови у Енте", + "sharing": "Делим...", "youCannotShareWithYourself": "Не можеш делити сам са собом", "archive": "Архивирај", "createAlbumActionHint": "Дуго притисните да бисте изабрали фотографије и кликните на + да бисте направили албум", @@ -286,5 +368,330 @@ "advancedSettings": "Напредно", "@advancedSettings": { "description": "The text to display in the advanced settings section" + }, + "photoGridSize": "Величина мреже за приказ фотографија", + "manageDeviceStorage": "Управљај кеш меморијом уређаја", + "manageDeviceStorageDesc": "Прегледај и очисти локалну кеш меморију.", + "machineLearning": "Машинско учење", + "mlConsent": "Омогући машинско учење", + "mlConsentTitle": "Омогући машинско учење?", + "mlConsentPrivacy": "Кликните како бисте сазнали више о овоме у нашим правилима приватности", + "mlConsentConfirmation": "Разумем, и желим да укључим машинско учење", + "magicSearch": "Магична претрага", + "discover": "Истражи", + "@discover": { + "description": "The text to display for the discover section under which we show receipts, screenshots, sunsets, greenery, etc." + }, + "discover_identity": "Идентитет", + "discover_screenshots": "Снимци екрана", + "discover_receipts": "Признанице", + "discover_notes": "Белешке", + "discover_memes": "Мимови", + "discover_visiting_cards": "Разгледнице", + "discover_babies": "Бебе", + "discover_pets": "Љубимци", + "discover_selfies": "Селфији", + "discover_wallpapers": "Позадине", + "discover_food": "Храна", + "discover_celebrations": "Прославе", + "discover_sunset": "Заласци сунца", + "discover_hills": "Брда", + "discover_greenery": "Зеленило", + "status": "Статус", + "indexedItems": "Индексиране ставке", + "pendingItems": "На чекању", + "clearIndexes": "Очисти индексе", + "selectFoldersForBackup": "Изабери фолдере за копирање", + "selectedFoldersWillBeEncryptedAndBackedUp": "Иѕабрани фолдери ће бити енкриптовани и сигурно похрањени", + "unselectAll": "Деселектуј све", + "selectAll": "Означи све", + "skip": "Прескочи", + "about": "О нама", + "privacy": "Приватност", + "terms": "Услови", + "checkForUpdates": "Провери да ли постоје новине", + "checkStatus": "Провери статус", + "checking": "Проверавам...", + "youAreOnTheLatestVersion": "Користите најновију верзију", + "manageSubscription": "Управљај претплатом", + "changePassword": "Измени лозинку", + "aNewVersionOfEnteIsAvailable": "Нова Енте верзија је доступна.", + "retry": "Покушај поново", + "freeUpDeviceSpace": "Ослободи простор на уређају", + "noDeviceThatCanBeDeleted": "Не постоје фајлови на овом уређају који би могли бити обрисани", + "removeDuplicates": "Уклони дупликате", + "viewLargeFiles": "Велики фајлови", + "youveNoDuplicateFilesThatCanBeCleared": "Немаш дупликата које бисмо могли да обришемо", + "rateUs": "Оцени нас", + "notifications": "Обавештења", + "security": "Сигурност", + "no": "Не", + "yes": "Да", + "rateUsOnStore": "Оцени нас на {storeName}", + "blog": "Блог", + "merchandise": "Роба", + "twitter": "Твитер", + "mastodon": "Мастодон", + "matrix": "Матрикс", + "discord": "Дискорд", + "reddit": "Редит", + "reportABug": "Пријави грешку", + "reportBug": "Пријави грешку", + "support": "Подршка", + "theme": "Тема", + "lightTheme": "Светла", + "darkTheme": "Тамна", + "systemTheme": "Системска", + "freeTrial": "Бесплатна проба", + "currentUsageIs": "Тренутно искоришћено", + "@currentUsageIs": { + "description": "This text is followed by storage usage", + "examples": { + "0": "Current usage is 1.2 GB" + }, + "type": "text" + }, + "faqs": "Често постављана питања", + "freeTrialValidTill": "Бесплатан пробни период важи до {endDate}", + "subscription": "Претплата", + "paymentDetails": "Детаљи плаћања", + "manageFamily": "Управљај породицом", + "renewSubscription": "Однови претплату", + "cancelSubscription": "Откажи претплату", + "areYouSureYouWantToRenew": "Да ли сте сигурни да желите да обновите претплату?", + "yesRenew": "Да, обнови претплату", + "areYouSureYouWantToCancel": "Да ли сте сигурни да желите да откажете?", + "yesCancel": "Да, откажи", + "openSettings": "Отвори подешавања", + "selectMorePhotos": "Изабери још фотографија", + "existingUser": "Постојећи корисник", + "forYourMemories": "за твоје успомене", + "atAFalloutShelter": "у подземном бункеру", + "deleteEmptyAlbums": "Избриши празне албуме", + "createCollage": "Направи колаж", + "saveCollage": "Сачувај колаж", + "addToEnte": "Додај у Енте", + "addToAlbum": "Додај у албум", + "delete": "Обриши", + "hide": "Сакриј", + "share": "Подели", + "rename": "Преименуј", + "thisEmailIsAlreadyInUse": "Имејл је већ у употреби", + "yourVerificationCodeHasExpired": "Ваш код ѕа верификацију је истекао", + "emailChangedTo": "Имејл промењен у {newEmail}", + "allMemoriesPreserved": "Све успомене су сачуване", + "loadingGallery": "Учитавам галерију", + "syncing": "Синхронизујем...", + "syncStopped": "Синхронизација прекинута", + "archiving": "Архивирам...", + "unarchiving": "Премештам из архиве...", + "successfullyArchived": "Успешно архивирано", + "successfullyUnarchived": "Успешно премештено из архиве", + "renameFile": "Преименуј фајл", + "enterFileName": "Именуј фајл", + "filesDeleted": "Обрисани фајлови", + "permanentlyDeleteFromDevice": "Трајно обриши са уређаја?", + "language": "Језик", + "selectLanguage": "Изабери језик", + "locationName": "Назив локације", + "addLocation": "Додај локацију", + "groupNearbyPhotos": "Групиши околне фотографије", + "kiloMeterUnit": "км", + "addLocationButton": "Додај", + "radius": "Пречник", + "save": "Сачувај", + "centerPoint": "Централна тачка", + "pickCenterPoint": "Изабери централну тачку", + "useSelectedPhoto": "Корисити изабрану фотографију", + "edit": "Измени", + "deleteLocation": "Обриши локацију", + "rotateLeft": "Заротирај у лево", + "flip": "Обрни у огледалу", + "rotateRight": "Заротирај у десно", + "saveCopy": "Сачувај копију", + "light": "Осветљење", + "color": "Боја", + "yesDiscardChanges": "Да, одбаци измене", + "doYouWantToDiscardTheEditsYouHaveMade": "Да ли желиш да одбациш направљене измене?", + "saving": "Чувам...", + "editsSaved": "Измене сачуване", + "distanceInKMUnit": "км", + "@distanceInKMUnit": { + "description": "Unit for distance in km" + }, + "dayToday": "Данас", + "dayYesterday": "Јуче", + "usedSpace": "Искоришћен простор", + "storageBreakupFamily": "Породица", + "storageBreakupYou": "Ти", + "@storageBreakupYou": { + "description": "Label to indicate how much storage you are using when you are part of a family plan" + }, + "fileInfoAddDescHint": "Додај опис", + "editLocationTagTitle": "Измени локацију", + "setRadius": "Задај пречник", + "map": "Мапа", + "@map": { + "description": "Label for the map view" + }, + "maps": "Мапе", + "enableMaps": "Омогући мапе", + "addPhotos": "Додај фотографије", + "zoomOutToSeePhotos": "Одзумирај да видиш фотографије", + "inviteYourFriendsToEnte": "Позови твоје пријатеље на Енте", + "addToHiddenAlbum": "Додај у тајни албум", + "viewAddOnButton": "Погледај додатке", + "addOns": "Додаци", + "addOnPageSubtitle": "Детаљи додатака", + "yourMap": "Твоја мапа", + "searchHint4": "Локација", + "faces": "Лица", + "people": "Људи", + "contacts": "Контакти", + "signOutFromOtherDevices": "Излогујте се са осталих уређаја", + "signOutOtherDevices": "Излогуј друге уређаје", + "selectALocation": "Изабери локацију", + "selectALocationFirst": "Прво изабери локацију", + "changeLocationOfSelectedItems": "Измени локацију изабраних ставки?", + "playOnTv": "Гледај албум на телевизору", + "pair": "Упари", + "deviceNotFound": "Уређај није пронађен", + "joinDiscord": "Прикључи се Дискорду", + "locations": "Локације", + "developerSettings": "с", + "rotate": "Ротирај", + "left": "Лево", + "right": "Десно", + "whatsNew": "Шта је ново", + "useAsCover": "Користи за позадину", + "notPersonLabel": "Није {name}?", + "@notPersonLabel": { + "description": "Label to indicate that the person in the photo is not the person whose name is mentioned", + "placeholders": { + "name": { + "content": "{name}", + "type": "String" + } + } + }, + "panorama": "Панорама", + "reenterPassword": "Поново унеси лозинку", + "reenterPin": "Поново унеси пин", + "deviceLock": "Закључавање уређаја", + "setNewPassword": "Постави нову лозинку", + "enterPin": "Унеси пин", + "setNewPin": "Постави нови пин", + "appLock": "Закључавање апликације", + "tapToUnlock": "Додирните за откључавање", + "tooManyIncorrectAttempts": "Превише погрешних покушаја", + "autoLock": "Аутоматско закључавање", + "immediately": "Одмах", + "hideContent": "Сакриј садржај", + "nameTheAlbum": "Именуј албум", + "showPerson": "Покажи особу", + "sort": "Сортирај", + "mostRecent": "Најновије", + "mostRelevant": "Најрелевантније", + "personName": "Име особе", + "addNewPerson": "Додај нову особу", + "addNameOrMerge": "Додај име или споји", + "mergeWithExisting": "Споји са постојећом", + "newPerson": "Нова особа", + "addName": "Додај име", + "add": "Додај", + "localIndexing": "Локално индексирање", + "resetPerson": "Уклони", + "month": "месец", + "yearShort": "год", + "@yearShort": { + "description": "Appears in pricing page (/yr)" + }, + "allow": "Дозволи", + "acceptTrustInvite": "Прихвати позивницу", + "declineTrustInvite": "Одбиј позивницу", + "legacy": "Наслеђе", + "trustedContacts": "Контакти од поверења", + "warning": "Упозорење", + "proceed": "Настави", + "gallery": "Галерија", + "linkEmail": "Повежи имејл", + "me": "Ја", + "thisIsMeExclamation": "Ово сам ја!", + "playOriginal": "Пусти оригинал", + "selectTime": "Изабери време", + "selectDate": "Изабери датум", + "appIcon": "Иконица апликације", + "notThisPerson": "То није та особа?", + "throughTheYears": "{dateFormat} кроз године", + "thisWeekThroughTheYears": "Тренутна недеља кроз године", + "youAndThem": "Ти и {name}", + "partyWithThem": "Журка са {name}", + "hikingWithThem": "Походништво са {name}", + "selfiesWithThem": "Селфији са {name}", + "posingWithThem": "Позирање са {name}", + "backgroundWithThem": "Прелепи призори са {name}", + "sportsWithThem": "Спорт са {name}", + "roadtripWithThem": "Путовање са {name}", + "spotlightOnYourself": "Сва светла на теби", + "spotlightOnThem": "Сва светла на {name}", + "personTurningAge": "{name} ускоро навршава {age}", + "lastTimeWithThem": "Последњи пут са {name}", + "tripToLocation": "Пут у {location}", + "tripInYear": "Пут из {year}", + "lastYearsTrip": "Прошлогодишње путовање", + "sunrise": "На хирозонту", + "mountains": "Преко брда", + "greenery": "Зеленило", + "beach": "Песак и море", + "city": "У граду", + "moon": "Под светлом Месеца", + "onTheRoad": "На путу", + "food": "Кулинарски ужици", + "pets": "Чупави другари", + "curatedMemories": "Пробране успомене", + "widgets": "Виџети", + "memories": "Успомене", + "pastYearsMemories": "Прошлогодишње успомене", + "addParticipants": "Додај учеснике", + "onThisDayMemories": "Успомене на данашњи дан", + "onThisDay": "На данашњи дан", + "birthdayNotifications": "Обавешења о рођенданима", + "happyBirthday": "Срећан рођендан! 🥳", + "birthdays": "Рођендани", + "wishThemAHappyBirthday": "Пожели {name} срећан рођендан! 🎉", + "otherDetectedFaces": "Друга препозната лица", + "areThey": "Да ли је то", + "questionmark": "?", + "saveAsAnotherPerson": "Сачувај као другу особу", + "showLessFaces": "Прикажи мање лица", + "showMoreFaces": "Прикажи још лица", + "ignore": "Игнориши", + "merge": "Споји", + "reset": "Ресетуј", + "areYouSureYouWantToMergeThem": "Да ли си сигуран да желиш да их спојиш?", + "addSomePhotosDesc1": "Додај фотографије или изабери", + "addSomePhotosDesc2": "позната лица", + "addSomePhotosDesc3": "за почетак", + "ignorePerson": "Игнориши особу", + "analysis": "Анализа", + "layout": "Распоред", + "day": "Дан", + "peopleAutoAddDesc": "Изабери људе за које желиш да буду аутоматски додати у албум", + "filter": "Филтер", + "adjust": "Прилагоди", + "draw": "Нацртај", + "sticker": "Налепница", + "brushColor": "Боја четкице", + "font": "Фонт", + "background": "Позадина", + "align": "Поравнај", + "addedToAlbums": "{count, plural, =1{Успешно додано у 1 албум} other{Успешно додано у {count} албума}}", + "@addedToAlbums": { + "description": "Message shown when items are added to albums", + "placeholders": { + "count": { + "type": "int" + } + } } } \ No newline at end of file diff --git a/mobile/apps/photos/lib/l10n/intl_sv.arb b/mobile/apps/photos/lib/l10n/intl_sv.arb index ccc0d6f882..2d4c753d42 100644 --- a/mobile/apps/photos/lib/l10n/intl_sv.arb +++ b/mobile/apps/photos/lib/l10n/intl_sv.arb @@ -1,6 +1,7 @@ { "@@locale ": "en", "enterYourEmailAddress": "Ange din e-postadress", + "enterYourNewEmailAddress": "Ange din nya e-postadress", "accountWelcomeBack": "Välkommen tillbaka!", "emailAlreadyRegistered": "E-postadress redan registrerad.", "emailNotRegistered": "E-postadressen är inte registrerad.", @@ -345,8 +346,20 @@ "keepPhotos": "Behåll foton", "deletePhotos": "Radera foton", "inviteToEnte": "Bjud in till Ente", + "removePublicLink": "Ta bort publik länk", + "sharing": "Delar...", + "youCannotShareWithYourself": "Du kan inte dela med dig själv", + "archive": "Arkiv", + "importing": "Importerar....", + "failedToLoadAlbums": "Det gick inte att läsa in album", + "hidden": "Dold", + "authToViewYourHiddenFiles": "Vänligen autentisera för att visa dina dolda filer", + "authToViewTrashedFiles": "Vänligen autentisera för att se dina kastade filer", "trash": "Papperskorg", + "uncategorized": "Okategoriserade", + "videoSmallCase": "video", "photoSmallCase": "foto", + "singleFileDeleteHighlight": "Det kommer att tas bort från alla album.", "yesDelete": "Ja, radera", "deleteFromDevice": "Radera från enhet", "newAlbum": "Nytt album", diff --git a/mobile/apps/photos/lib/l10n/intl_vi.arb b/mobile/apps/photos/lib/l10n/intl_vi.arb index 8d64a0f3f6..d609d51994 100644 --- a/mobile/apps/photos/lib/l10n/intl_vi.arb +++ b/mobile/apps/photos/lib/l10n/intl_vi.arb @@ -15,13 +15,13 @@ "deleteAccountFeedbackPrompt": "Chúng tôi rất tiếc khi thấy bạn rời đi. Vui lòng chia sẻ phản hồi của bạn để giúp chúng tôi cải thiện.", "feedback": "Phản hồi", "kindlyHelpUsWithThisInformation": "Mong bạn giúp chúng tôi thông tin này", - "confirmDeletePrompt": "Có, tôi muốn xóa vĩnh viễn tài khoản này và tất cả dữ liệu của nó.", + "confirmDeletePrompt": "Có, tôi muốn xóa vĩnh viễn tài khoản này và tất cả dữ liệu.", "confirmAccountDeletion": "Xác nhận xóa tài khoản", "deleteAccountPermanentlyButton": "Xóa tài khoản vĩnh viễn", "yourAccountHasBeenDeleted": "Tài khoản của bạn đã bị xóa", "selectReason": "Chọn lý do", "deleteReason1": "Nó thiếu một tính năng quan trọng mà tôi cần", - "deleteReason2": "Ứng dụng hoặc một tính năng nhất định không hoạt động như tôi muốn", + "deleteReason2": "Ứng dụng hoặc một tính năng không hoạt động như tôi muốn", "deleteReason3": "Tôi tìm thấy một dịch vụ khác mà tôi thích hơn", "deleteReason4": "Lý do không có trong danh sách", "sendEmail": "Gửi email", @@ -562,7 +562,7 @@ "referrals": "Giới thiệu", "notifications": "Thông báo", "sharedPhotoNotifications": "Ảnh chia sẻ mới", - "sharedPhotoNotificationsExplanation": "Nhận thông báo khi ai đó thêm ảnh vào album chia sẻ mà bạn tham gia", + "sharedPhotoNotificationsExplanation": "Nhận thông báo khi ai đó thêm ảnh vào album chia sẻ mà bạn tham gia.", "advanced": "Nâng cao", "general": "Chung", "security": "Bảo mật", @@ -822,8 +822,8 @@ "sharedWith": "Chia sẻ với {emailIDs}", "sharedWithMe": "Chia sẻ với tôi", "sharedByMe": "Chia sẻ bởi tôi", - "doubleYourStorage": "Gấp đôi dung lượng lưu trữ của bạn", - "referFriendsAnd2xYourPlan": "Giới thiệu bạn bè và ×2 gói của bạn", + "doubleYourStorage": "Nhân đôi dung lượng", + "referFriendsAnd2xYourPlan": "Giới thiệu bạn bè để được ×2 dung lượng gói của bạn", "shareAlbumHint": "Mở album và nhấn nút chia sẻ ở góc trên bên phải để chia sẻ.", "itemsShowTheNumberOfDaysRemainingBeforePermanentDeletion": "Trên các mục là số ngày còn lại trước khi xóa vĩnh viễn", "trashDaysLeft": "{count, plural, =0 {Sắp xóa} =1 {1 ngày} other {{count} ngày}}", @@ -1330,7 +1330,7 @@ }, "enable": "Bật", "enabled": "Bật", - "moreDetails": "Thêm chi tiết", + "moreDetails": "Thông tin thêm", "enableMLIndexingDesc": "Ente hỗ trợ học máy trên-thiết-bị nhằm nhận diện khuôn mặt, tìm kiếm vi diệu và các tính năng tìm kiếm nâng cao khác", "magicSearchHint": "Tìm kiếm vi diệu cho phép tìm ảnh theo nội dung của chúng, ví dụ: 'xe hơi', 'xe hơi đỏ', 'Ferrari'", "panorama": "Panorama", @@ -1752,7 +1752,7 @@ "addAlbumWidgetPrompt": "Thêm tiện ích album vào màn hình chính và quay lại đây để tùy chỉnh.", "addPeopleWidgetPrompt": "Thêm tiện ích người vào màn hình chính và quay lại đây để tùy chỉnh.", "birthdayNotifications": "Thông báo sinh nhật", - "receiveRemindersOnBirthdays": "Nhắc khi đến sinh nhật của ai đó. Chạm vào thông báo sẽ đưa bạn đến ảnh của người sinh nhật.", + "receiveRemindersOnBirthdays": "Nhắc khi đến sinh nhật của ai đó. Nhấn vào thông báo sẽ đưa bạn đến ảnh của người sinh nhật.", "happyBirthday": "Chúc mừng sinh nhật! 🥳", "birthdays": "Sinh nhật", "wishThemAHappyBirthday": "Chúc {name} sinh nhật vui vẻ! 🎉", From 67e493b27f1abaff9a2a4562986b32e16f54f117 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 11 Aug 2025 11:00:17 +0530 Subject: [PATCH 109/156] use different deprecated method replacement --- .../lib/ui/tools/similar_images_page.dart | 4 +- mobile/apps/photos/pubspec.lock | 56 +++++++++---------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 304b28bd72..146f46aeb1 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -590,7 +590,7 @@ class _SimilarImagesPageState extends State { child: isSelected ? ColorFiltered( colorFilter: ColorFilter.mode( - Colors.black.withValues(alpha: 0.4), + Colors.black.withAlpha((0.4 * 255).toInt()), BlendMode.darken, ), child: ThumbnailWidget( @@ -668,7 +668,7 @@ class _SimilarImagesPageState extends State { child: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), decoration: BoxDecoration( - color: colorScheme.warning500.withValues(alpha: 0.1), + color: colorScheme.warning500.withAlpha((0.1 * 255).toInt()), borderRadius: BorderRadius.circular(8), ), child: Row( diff --git a/mobile/apps/photos/pubspec.lock b/mobile/apps/photos/pubspec.lock index 53fe27451b..8f8898af81 100644 --- a/mobile/apps/photos/pubspec.lock +++ b/mobile/apps/photos/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" + sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 url: "https://pub.dev" source: hosted - version: "76.0.0" + version: "72.0.0" _flutterfire_internals: dependency: transitive description: @@ -21,7 +21,7 @@ packages: dependency: transitive description: dart source: sdk - version: "0.3.3" + version: "0.3.2" adaptive_theme: dependency: "direct main" description: @@ -34,10 +34,10 @@ packages: dependency: transitive description: name: analyzer - sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" + sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 url: "https://pub.dev" source: hosted - version: "6.11.0" + version: "6.7.0" android_intent_plus: dependency: "direct main" description: @@ -325,10 +325,10 @@ packages: dependency: "direct main" description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.18.0" computer: dependency: "direct main" description: @@ -1408,18 +1408,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.7" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -1528,10 +1528,10 @@ packages: dependency: transitive description: name: macros - sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" + sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" url: "https://pub.dev" source: hosted - version: "0.1.3-main.0" + version: "0.1.2-main.4" maps_launcher: dependency: "direct main" description: @@ -2316,7 +2316,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" source_gen: dependency: transitive description: @@ -2441,10 +2441,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.11.1" step_progress_indicator: dependency: "direct main" description: @@ -2473,10 +2473,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.2.0" styled_text: dependency: "direct main" description: @@ -2537,26 +2537,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "713a8789d62f3233c46b4a90b174737b2c04cb6ae4500f2aa8b1be8f03f5e67f" + sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" url: "https://pub.dev" source: hosted - version: "1.25.8" + version: "1.25.7" test_api: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.2" test_core: dependency: transitive description: name: test_core - sha256: "12391302411737c176b0b5d6491f466b0dd56d4763e347b6714efbaa74d7953d" + sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.4" thermal: dependency: "direct main" description: @@ -2836,10 +2836,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.3.0" + version: "14.2.5" volume_controller: dependency: transitive description: @@ -2900,10 +2900,10 @@ packages: dependency: transitive description: name: webdriver - sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8" + sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" url: "https://pub.dev" source: hosted - version: "3.0.4" + version: "3.0.3" webkit_inspection_protocol: dependency: transitive description: From 6223a1529f517d2bff782bf62b3fa6cb708a1aa6 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 11 Aug 2025 14:11:00 +0530 Subject: [PATCH 110/156] bulk vector search in MLComputer isolate --- .../machine_learning/ml_computer.dart | 28 +++++++++++++++++++ .../similar_images_service.dart | 14 ++++------ .../lib/utils/isolate/isolate_operations.dart | 19 ++++++++++++- 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/mobile/apps/photos/lib/services/machine_learning/ml_computer.dart b/mobile/apps/photos/lib/services/machine_learning/ml_computer.dart index 939d486f64..4856134fdd 100644 --- a/mobile/apps/photos/lib/services/machine_learning/ml_computer.dart +++ b/mobile/apps/photos/lib/services/machine_learning/ml_computer.dart @@ -1,5 +1,8 @@ import 'dart:async'; +import "dart:typed_data" show Float32List; +import "package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart" + show Uint64List; import "package:logging/logging.dart"; import "package:photos/models/ml/vector.dart"; import "package:photos/services/machine_learning/ml_constants.dart"; @@ -26,11 +29,31 @@ class MLComputer extends SuperIsolate { @override bool get shouldAutomaticDispose => false; + bool _isRustInit = false; + // Singleton pattern MLComputer._privateConstructor(); static final MLComputer instance = MLComputer._privateConstructor(); factory MLComputer() => instance; + Future<(List, List)> bulkVectorSearch( + List clipFloat32, + bool exact, + ) async { + try { + final result = await runInIsolate(IsolateOperation.bulkVectorSearch, { + "clipFloat32": clipFloat32, + "exact": exact, + "initRust": !_isRustInit, + }); + _isRustInit = true; + return result; + } catch (e, s) { + _logger.severe("Could not run bulk vector search in MLComputer", e, s); + rethrow; + } + } + Future> runClipText(String query) async { try { await _ensureLoadedClipTextModel(); @@ -138,4 +161,9 @@ class MLComputer extends SuperIsolate { rethrow; } } + + @override + Future onDispose() async { + _isRustInit = false; + } } diff --git a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart index 284ea7b4fa..e439666d6b 100644 --- a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart +++ b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart @@ -5,13 +5,13 @@ import "package:flutter/foundation.dart" show kDebugMode; import "package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart" show Uint64List; import 'package:logging/logging.dart'; -import "package:photos/db/ml/clip_vector_db.dart"; import "package:photos/db/ml/db.dart"; import "package:photos/extensions/stop_watch.dart"; import 'package:photos/models/file/file.dart'; import "package:photos/models/file/file_type.dart"; import "package:photos/models/ml/vector.dart"; import "package:photos/models/similar_files.dart"; +import "package:photos/services/machine_learning/ml_computer.dart"; import "package:photos/services/search_service.dart"; class SimilarImagesService { @@ -24,9 +24,9 @@ class SimilarImagesService { /// Returns a list of SimilarFiles, where each SimilarFiles object contains /// a list of files that are perceptually similar Future> getSimilarFiles( - double distanceThreshold, - {bool exact = false,} - ) async { + double distanceThreshold, { + bool exact = false, + }) async { try { final now = DateTime.now(); final List result = @@ -66,11 +66,9 @@ class SimilarImagesService { w?.log("getAllClipVectors"); // Run bulk vector search - final (vectorKeys, distances) = - await ClipVectorDB.instance.bulkSearchVectors( + final (vectorKeys, distances) = await MLComputer.instance.bulkVectorSearch( clipFloat32, - BigInt.from(100), - exact: exact, + exact, ); w?.log("bulkSearchVectors"); diff --git a/mobile/apps/photos/lib/utils/isolate/isolate_operations.dart b/mobile/apps/photos/lib/utils/isolate/isolate_operations.dart index ae4e1e8851..c82143f55e 100644 --- a/mobile/apps/photos/lib/utils/isolate/isolate_operations.dart +++ b/mobile/apps/photos/lib/utils/isolate/isolate_operations.dart @@ -1,6 +1,7 @@ -import 'dart:typed_data' show Uint8List; +import 'dart:typed_data' show Uint8List, Float32List; import "package:ml_linalg/linalg.dart"; +import "package:photos/db/ml/clip_vector_db.dart"; import "package:photos/models/ml/face/box.dart"; import "package:photos/models/ml/vector.dart"; import "package:photos/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart"; @@ -10,6 +11,7 @@ import "package:photos/services/machine_learning/ml_result.dart"; import "package:photos/services/machine_learning/semantic_search/clip/clip_text_encoder.dart"; import "package:photos/services/machine_learning/semantic_search/clip/clip_text_tokenizer.dart"; import "package:photos/services/machine_learning/semantic_search/query_result.dart"; +import "package:photos/src/rust/frb_generated.dart" show RustLib; import "package:photos/utils/image_ml_util.dart"; import "package:photos/utils/ml_util.dart"; @@ -40,6 +42,9 @@ enum IsolateOperation { /// [MLComputer] computeBulkSimilarities, + /// [MLComputer] + bulkVectorSearch, + /// [FaceClusteringService] linearIncrementalClustering, @@ -57,6 +62,18 @@ Future isolateFunction( Map args, ) async { switch (function) { + case IsolateOperation.bulkVectorSearch: + final initRust = args["initRust"] as bool? ?? false; + if (initRust) await RustLib.init(); + final clipFloat32 = args["clipFloat32"] as List; + final exact = args["exact"] as bool; + + return ClipVectorDB.instance.bulkSearchVectors( + clipFloat32, + BigInt.from(100), + exact: exact, + ); + /// Cases for MLIndexingIsolate start here /// MLIndexingIsolate From ad90d2e37a0f4147c04b417c3cd24995ff16276d Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 11 Aug 2025 15:43:30 +0530 Subject: [PATCH 111/156] Fix delete group bug --- mobile/apps/photos/lib/ui/tools/similar_images_page.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 146f46aeb1..93df29d542 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -723,16 +723,21 @@ class _SimilarImagesPageState extends State { Future _deleteFilesLogic(Set filesToDelete) async { _selectedFiles.unSelectAll(filesToDelete); + final groupsToRemove = {}; for (final file in filesToDelete) { for (final similarGroup in _similarFilesList) { if (similarGroup.containsFile(file)) { similarGroup.removeFile(file); if (similarGroup.isEmpty) { - _similarFilesList.remove(similarGroup); + groupsToRemove.add(similarGroup); } } } } + for (final group in groupsToRemove) { + _similarFilesList.remove(group); + } + setState(() {}); await deleteFilesFromRemoteOnly(context, filesToDelete.toList()); } From dbf6f6aa37e44268f7f2eb7a99343edffce8d144 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 11 Aug 2025 17:24:36 +0530 Subject: [PATCH 112/156] Proper rust init in MLComputer isolate --- .../machine_learning/ml_computer.dart | 9 --------- .../lib/utils/isolate/isolate_operations.dart | 20 +++++++++++++++++-- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/mobile/apps/photos/lib/services/machine_learning/ml_computer.dart b/mobile/apps/photos/lib/services/machine_learning/ml_computer.dart index 4856134fdd..d89b5d5a4a 100644 --- a/mobile/apps/photos/lib/services/machine_learning/ml_computer.dart +++ b/mobile/apps/photos/lib/services/machine_learning/ml_computer.dart @@ -29,8 +29,6 @@ class MLComputer extends SuperIsolate { @override bool get shouldAutomaticDispose => false; - bool _isRustInit = false; - // Singleton pattern MLComputer._privateConstructor(); static final MLComputer instance = MLComputer._privateConstructor(); @@ -44,9 +42,7 @@ class MLComputer extends SuperIsolate { final result = await runInIsolate(IsolateOperation.bulkVectorSearch, { "clipFloat32": clipFloat32, "exact": exact, - "initRust": !_isRustInit, }); - _isRustInit = true; return result; } catch (e, s) { _logger.severe("Could not run bulk vector search in MLComputer", e, s); @@ -161,9 +157,4 @@ class MLComputer extends SuperIsolate { rethrow; } } - - @override - Future onDispose() async { - _isRustInit = false; - } } diff --git a/mobile/apps/photos/lib/utils/isolate/isolate_operations.dart b/mobile/apps/photos/lib/utils/isolate/isolate_operations.dart index c82143f55e..7ace292438 100644 --- a/mobile/apps/photos/lib/utils/isolate/isolate_operations.dart +++ b/mobile/apps/photos/lib/utils/isolate/isolate_operations.dart @@ -63,8 +63,7 @@ Future isolateFunction( ) async { switch (function) { case IsolateOperation.bulkVectorSearch: - final initRust = args["initRust"] as bool? ?? false; - if (initRust) await RustLib.init(); + await _ensureRustLoaded(); final clipFloat32 = args["clipFloat32"] as List; final exact = args["exact"] as bool; @@ -199,9 +198,26 @@ Future isolateFunction( /// Caching case IsolateOperation.clearAllIsolateCache: + _ensureRustDisposed(); _isolateCache.clear(); return true; /// Cases for Caching stop here } } + +Future _ensureRustLoaded() async { + final bool loaded = _isolateCache["rustLibLoaded"] as bool? ?? false; + if (!loaded) { + await RustLib.init(); + _isolateCache["rustLibLoaded"] = true; + } +} + +void _ensureRustDisposed() { + final bool loaded = _isolateCache["rustLibLoaded"] as bool? ?? false; + if (loaded) { + RustLib.dispose(); + _isolateCache.remove("rustLibLoaded"); + } +} From 856a87f01c0998b6334665e0a99b3e7c277fcdda Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 12 Aug 2025 11:36:38 +0530 Subject: [PATCH 113/156] Rust api to do entire search with potential keys in rust --- .../photos/lib/src/rust/api/usearch_api.dart | 5 + .../photos/lib/src/rust/frb_generated.dart | 117 ++++++++++++++++-- .../photos/lib/src/rust/frb_generated.io.dart | 22 ++++ .../apps/photos/rust/src/api/usearch_api.rs | 32 +++++ mobile/apps/photos/rust/src/frb_generated.rs | 107 ++++++++++++++-- 5 files changed, 259 insertions(+), 24 deletions(-) diff --git a/mobile/apps/photos/lib/src/rust/api/usearch_api.dart b/mobile/apps/photos/lib/src/rust/api/usearch_api.dart index 7d370723cd..118cb1b38c 100644 --- a/mobile/apps/photos/lib/src/rust/api/usearch_api.dart +++ b/mobile/apps/photos/lib/src/rust/api/usearch_api.dart @@ -21,6 +21,11 @@ abstract class VectorDb implements RustOpaqueInterface { Future bulkRemoveVectors({required Uint64List keys}); + Future<(Uint64List, List, List)> bulkSearchKeys( + {required Uint64List potentialKeys, + required BigInt count, + required bool exact}); + Future<(List, List)> bulkSearchVectors( {required List queries, required BigInt count, diff --git a/mobile/apps/photos/lib/src/rust/frb_generated.dart b/mobile/apps/photos/lib/src/rust/frb_generated.dart index c9496ecf9e..931d5ffb3a 100644 --- a/mobile/apps/photos/lib/src/rust/frb_generated.dart +++ b/mobile/apps/photos/lib/src/rust/frb_generated.dart @@ -74,7 +74,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.11.1'; @override - int get rustContentHash => 1543559173; + int get rustContentHash => 1360671619; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( @@ -101,6 +101,13 @@ abstract class RustLibApi extends BaseApi { Future crateApiUsearchApiVectorDbBulkRemoveVectors( {required VectorDb that, required Uint64List keys}); + Future<(Uint64List, List, List)> + crateApiUsearchApiVectorDbBulkSearchKeys( + {required VectorDb that, + required Uint64List potentialKeys, + required BigInt count, + required bool exact}); + Future<(List, List)> crateApiUsearchApiVectorDbBulkSearchVectors( {required VectorDb that, @@ -272,6 +279,41 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["that", "keys"], ); + @override + Future<(Uint64List, List, List)> + crateApiUsearchApiVectorDbBulkSearchKeys( + {required VectorDb that, + required Uint64List potentialKeys, + required BigInt count, + required bool exact}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( + that, serializer); + sse_encode_list_prim_u_64_strict(potentialKeys, serializer); + sse_encode_usize(count, serializer); + sse_encode_bool(exact, serializer); + pdeCallFfi(generalizedFrbRustBinding, serializer, + funcId: 5, port: port_); + }, + codec: SseCodec( + decodeSuccessData: + sse_decode_record_list_prim_u_64_strict_list_list_prim_u_64_strict_list_list_prim_f_32_strict, + decodeErrorData: null, + ), + constMeta: kCrateApiUsearchApiVectorDbBulkSearchKeysConstMeta, + argValues: [that, potentialKeys, count, exact], + apiImpl: this, + )); + } + + TaskConstMeta get kCrateApiUsearchApiVectorDbBulkSearchKeysConstMeta => + const TaskConstMeta( + debugName: "VectorDb_bulk_search_keys", + argNames: ["that", "potentialKeys", "count", "exact"], + ); + @override Future<(List, List)> crateApiUsearchApiVectorDbBulkSearchVectors( @@ -288,7 +330,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_usize(count, serializer); sse_encode_bool(exact, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 5, port: port_); + funcId: 6, port: port_); }, codec: SseCodec( decodeSuccessData: @@ -317,7 +359,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { that, serializer); sse_encode_u_64(key, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 6, port: port_); + funcId: 7, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_bool, @@ -343,7 +385,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( that, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 7, port: port_); + funcId: 8, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -370,7 +412,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( that, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 8, port: port_); + funcId: 9, port: port_); }, codec: SseCodec( decodeSuccessData: @@ -399,7 +441,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { that, serializer); sse_encode_u_64(key, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 9, port: port_); + funcId: 10, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_list_prim_f_32_strict, @@ -425,7 +467,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(filePath, serializer); sse_encode_usize(dimensions, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 10)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 11)!; }, codec: SseCodec( decodeSuccessData: @@ -454,7 +496,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { that, serializer); sse_encode_u_64(key, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 11, port: port_); + funcId: 12, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_usize, @@ -480,7 +522,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB( that, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 12, port: port_); + funcId: 13, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -513,7 +555,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_usize(count, serializer); sse_encode_bool(exact, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 13, port: port_); + funcId: 14, port: port_); }, codec: SseCodec( decodeSuccessData: @@ -538,7 +580,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(name, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 14)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 15)!; }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -561,7 +603,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 15, port: port_); + funcId: 16, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -683,6 +725,25 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } + @protected + ( + Uint64List, + List, + List + ) dco_decode_record_list_prim_u_64_strict_list_list_prim_u_64_strict_list_list_prim_f_32_strict( + dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 3) { + throw Exception('Expected 3 elements, got ${arr.length}'); + } + return ( + dco_decode_list_prim_u_64_strict(arr[0]), + dco_decode_list_list_prim_u_64_strict(arr[1]), + dco_decode_list_list_prim_f_32_strict(arr[2]), + ); + } + @protected ( Uint64List, @@ -852,6 +913,20 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return (var_field0, var_field1); } + @protected + ( + Uint64List, + List, + List + ) sse_decode_record_list_prim_u_64_strict_list_list_prim_u_64_strict_list_list_prim_f_32_strict( + SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + final var_field0 = sse_decode_list_prim_u_64_strict(deserializer); + final var_field1 = sse_decode_list_list_prim_u_64_strict(deserializer); + final var_field2 = sse_decode_list_list_prim_f_32_strict(deserializer); + return (var_field0, var_field1, var_field2); + } + @protected (Uint64List, Float32List) sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( @@ -1020,6 +1095,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_list_prim_f_32_strict(self.$2, serializer); } + @protected + void + sse_encode_record_list_prim_u_64_strict_list_list_prim_u_64_strict_list_list_prim_f_32_strict( + (Uint64List, List, List) self, + SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_list_prim_u_64_strict(self.$1, serializer); + sse_encode_list_list_prim_u_64_strict(self.$2, serializer); + sse_encode_list_list_prim_f_32_strict(self.$3, serializer); + } + @protected void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( (Uint64List, Float32List) self, SseSerializer serializer) { @@ -1108,6 +1194,13 @@ class VectorDbImpl extends RustOpaque implements VectorDb { RustLib.instance.api .crateApiUsearchApiVectorDbBulkRemoveVectors(that: this, keys: keys); + Future<(Uint64List, List, List)> bulkSearchKeys( + {required Uint64List potentialKeys, + required BigInt count, + required bool exact}) => + RustLib.instance.api.crateApiUsearchApiVectorDbBulkSearchKeys( + that: this, potentialKeys: potentialKeys, count: count, exact: exact); + Future<(List, List)> bulkSearchVectors( {required List queries, required BigInt count, diff --git a/mobile/apps/photos/lib/src/rust/frb_generated.io.dart b/mobile/apps/photos/lib/src/rust/frb_generated.io.dart index 6ab196d6e2..f7b687f7d8 100644 --- a/mobile/apps/photos/lib/src/rust/frb_generated.io.dart +++ b/mobile/apps/photos/lib/src/rust/frb_generated.io.dart @@ -72,6 +72,14 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { dco_decode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict( dynamic raw); + @protected + ( + Uint64List, + List, + List + ) dco_decode_record_list_prim_u_64_strict_list_list_prim_u_64_strict_list_list_prim_f_32_strict( + dynamic raw); + @protected ( Uint64List, @@ -143,6 +151,14 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { sse_decode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict( SseDeserializer deserializer); + @protected + ( + Uint64List, + List, + List + ) sse_decode_record_list_prim_u_64_strict_list_list_prim_u_64_strict_list_list_prim_f_32_strict( + SseDeserializer deserializer); + @protected (Uint64List, Float32List) sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict( @@ -220,6 +236,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict( (List, List) self, SseSerializer serializer); + @protected + void + sse_encode_record_list_prim_u_64_strict_list_list_prim_u_64_strict_list_list_prim_f_32_strict( + (Uint64List, List, List) self, + SseSerializer serializer); + @protected void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict( (Uint64List, Float32List) self, SseSerializer serializer); diff --git a/mobile/apps/photos/rust/src/api/usearch_api.rs b/mobile/apps/photos/rust/src/api/usearch_api.rs index 78503c9dd5..28957d7114 100644 --- a/mobile/apps/photos/rust/src/api/usearch_api.rs +++ b/mobile/apps/photos/rust/src/api/usearch_api.rs @@ -118,6 +118,38 @@ impl VectorDB { (keys, distances) } + pub fn bulk_search_keys( + &self, + potential_keys: &Vec, + count: usize, + exact: bool, + ) -> (Vec, Vec>, Vec>) { + // let max_contained_keys = potential_keys.len(); + let mut contained_keys = Vec::new(); + let mut queries = Vec::new(); + + for key in potential_keys { + let contains: bool = self.index.contains(*key); + if contains { + let embedding = self.get_vector(*key); + contained_keys.push(*key); + queries.push(embedding); + } + } + + let mut closeby_keys = Vec::new(); + let mut distances = Vec::new(); + for query in &queries { + let (keys_result, distances_result) = self.search_vectors(query, count, exact); + closeby_keys.push(keys_result); + distances.push(distances_result); + } + if contained_keys.len() != closeby_keys.len() { + panic!("The number of contained keys does not match the number of keys"); + } + (contained_keys, closeby_keys, distances) + } + /// Check if a vector with the given key exists in the index. /// `true` if the index contains the vector with the given key, `false` otherwise. pub fn contains_vector(&self, key: u64) -> bool { diff --git a/mobile/apps/photos/rust/src/frb_generated.rs b/mobile/apps/photos/rust/src/frb_generated.rs index 6b114469a7..8ccbd11c20 100644 --- a/mobile/apps/photos/rust/src/frb_generated.rs +++ b/mobile/apps/photos/rust/src/frb_generated.rs @@ -38,7 +38,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.11.1"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1543559173; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1360671619; // Section: executor @@ -269,6 +269,64 @@ fn wire__crate__api__usearch_api__VectorDb_bulk_remove_vectors_impl( }, ) } +fn wire__crate__api__usearch_api__VectorDb_bulk_search_keys_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "VectorDb_bulk_search_keys", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = , + >>::sse_decode(&mut deserializer); + let api_potential_keys = >::sse_decode(&mut deserializer); + let api_count = ::sse_decode(&mut deserializer); + let api_exact = ::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, ()>((move || { + let mut api_that_guard = None; + let decode_indices_ = + flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![ + flutter_rust_bridge::for_generated::LockableOrderInfo::new( + &api_that, 0, false, + ), + ]); + for i in decode_indices_ { + match i { + 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref()), + _ => unreachable!(), + } + } + let api_that_guard = api_that_guard.unwrap(); + let output_ok = + Result::<_, ()>::Ok(crate::api::usearch_api::VectorDB::bulk_search_keys( + &*api_that_guard, + &api_potential_keys, + api_count, + api_exact, + ))?; + Ok(output_ok) + })()) + } + }, + ) +} fn wire__crate__api__usearch_api__VectorDb_bulk_search_vectors_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, @@ -899,6 +957,16 @@ impl SseDecode for (Vec>, Vec>) { } } +impl SseDecode for (Vec, Vec>, Vec>) { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_field0 = >::sse_decode(deserializer); + let mut var_field1 = >>::sse_decode(deserializer); + let mut var_field2 = >>::sse_decode(deserializer); + return (var_field0, var_field1, var_field2); + } +} + impl SseDecode for (Vec, Vec) { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -990,55 +1058,61 @@ fn pde_ffi_dispatcher_primary_impl( rust_vec_len, data_len, ), - 5 => wire__crate__api__usearch_api__VectorDb_bulk_search_vectors_impl( + 5 => wire__crate__api__usearch_api__VectorDb_bulk_search_keys_impl( port, ptr, rust_vec_len, data_len, ), - 6 => wire__crate__api__usearch_api__VectorDb_contains_vector_impl( + 6 => wire__crate__api__usearch_api__VectorDb_bulk_search_vectors_impl( port, ptr, rust_vec_len, data_len, ), - 7 => wire__crate__api__usearch_api__VectorDb_delete_index_impl( + 7 => wire__crate__api__usearch_api__VectorDb_contains_vector_impl( port, ptr, rust_vec_len, data_len, ), - 8 => wire__crate__api__usearch_api__VectorDb_get_index_stats_impl( + 8 => wire__crate__api__usearch_api__VectorDb_delete_index_impl( port, ptr, rust_vec_len, data_len, ), - 9 => wire__crate__api__usearch_api__VectorDb_get_vector_impl( + 9 => wire__crate__api__usearch_api__VectorDb_get_index_stats_impl( port, ptr, rust_vec_len, data_len, ), - 11 => wire__crate__api__usearch_api__VectorDb_remove_vector_impl( + 10 => wire__crate__api__usearch_api__VectorDb_get_vector_impl( port, ptr, rust_vec_len, data_len, ), - 12 => wire__crate__api__usearch_api__VectorDb_reset_index_impl( + 12 => wire__crate__api__usearch_api__VectorDb_remove_vector_impl( port, ptr, rust_vec_len, data_len, ), - 13 => wire__crate__api__usearch_api__VectorDb_search_vectors_impl( + 13 => wire__crate__api__usearch_api__VectorDb_reset_index_impl( port, ptr, rust_vec_len, data_len, ), - 15 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), + 14 => wire__crate__api__usearch_api__VectorDb_search_vectors_impl( + port, + ptr, + rust_vec_len, + data_len, + ), + 16 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -1051,8 +1125,8 @@ fn pde_ffi_dispatcher_sync_impl( ) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { // Codec=Pde (Serialization + dispatch), see doc to use other codecs match func_id { - 10 => wire__crate__api__usearch_api__VectorDb_new_impl(ptr, rust_vec_len, data_len), - 14 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), + 11 => wire__crate__api__usearch_api__VectorDb_new_impl(ptr, rust_vec_len, data_len), + 15 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -1171,6 +1245,15 @@ impl SseEncode for (Vec>, Vec>) { } } +impl SseEncode for (Vec, Vec>, Vec>) { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + >::sse_encode(self.0, serializer); + >>::sse_encode(self.1, serializer); + >>::sse_encode(self.2, serializer); + } +} + impl SseEncode for (Vec, Vec) { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { From cb2b33eedbea3689383eb4c3b96f74bfa6bcb0c2 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 12 Aug 2025 11:38:03 +0530 Subject: [PATCH 114/156] use rust search with potential keys in clip vector db --- .../apps/photos/lib/db/ml/clip_vector_db.dart | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/mobile/apps/photos/lib/db/ml/clip_vector_db.dart b/mobile/apps/photos/lib/db/ml/clip_vector_db.dart index ce06a01cbe..348cd40b6d 100644 --- a/mobile/apps/photos/lib/db/ml/clip_vector_db.dart +++ b/mobile/apps/photos/lib/db/ml/clip_vector_db.dart @@ -226,6 +226,25 @@ class ClipVectorDB { } } + Future<(Uint64List, List, List)> bulkSearchWithKeys( + Uint64List potentialKeys, + BigInt count, { + bool exact = false, + }) async { + final db = await _vectorDB; + try { + final result = await db.bulkSearchKeys( + potentialKeys: potentialKeys, + count: count, + exact: exact, + ); + return result; + } catch (e, s) { + _logger.severe("Error bulk searching vectors with potential keys", e, s); + rethrow; + } + } + Future>> computeBulkSimilarities( Map> textQueryToEmbeddingMap, Map minimumSimilarityMap, From 5f4cf302a1d9fa6c273e2e1b702b79d65debad4c Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 12 Aug 2025 11:38:25 +0530 Subject: [PATCH 115/156] use search with keys in ml computer --- .../services/machine_learning/ml_computer.dart | 18 ++++++++++++++++++ .../lib/utils/isolate/isolate_operations.dart | 15 +++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/mobile/apps/photos/lib/services/machine_learning/ml_computer.dart b/mobile/apps/photos/lib/services/machine_learning/ml_computer.dart index d89b5d5a4a..443cb5308b 100644 --- a/mobile/apps/photos/lib/services/machine_learning/ml_computer.dart +++ b/mobile/apps/photos/lib/services/machine_learning/ml_computer.dart @@ -50,6 +50,24 @@ class MLComputer extends SuperIsolate { } } + Future<(Uint64List, List, List)> + bulkVectorSearchWithKeys( + Uint64List potentialKeys, + bool exact, + ) async { + try { + final result = + await runInIsolate(IsolateOperation.bulkVectorSearchWithKeys, { + "potentialKeys": potentialKeys, + "exact": exact, + }); + return result; + } catch (e, s) { + _logger.severe("Could not run bulk vector search in MLComputer", e, s); + rethrow; + } + } + Future> runClipText(String query) async { try { await _ensureLoadedClipTextModel(); diff --git a/mobile/apps/photos/lib/utils/isolate/isolate_operations.dart b/mobile/apps/photos/lib/utils/isolate/isolate_operations.dart index 7ace292438..6deba3d97a 100644 --- a/mobile/apps/photos/lib/utils/isolate/isolate_operations.dart +++ b/mobile/apps/photos/lib/utils/isolate/isolate_operations.dart @@ -1,5 +1,6 @@ import 'dart:typed_data' show Uint8List, Float32List; +import "package:flutter_rust_bridge/flutter_rust_bridge.dart" show Uint64List; import "package:ml_linalg/linalg.dart"; import "package:photos/db/ml/clip_vector_db.dart"; import "package:photos/models/ml/face/box.dart"; @@ -45,6 +46,9 @@ enum IsolateOperation { /// [MLComputer] bulkVectorSearch, + /// [MLComputer] + bulkVectorSearchWithKeys, + /// [FaceClusteringService] linearIncrementalClustering, @@ -62,6 +66,17 @@ Future isolateFunction( Map args, ) async { switch (function) { + case IsolateOperation.bulkVectorSearchWithKeys: + await _ensureRustLoaded(); + final potentialKeys = args["potentialKeys"] as Uint64List; + final exact = args["exact"] as bool; + + return ClipVectorDB.instance.bulkSearchWithKeys( + potentialKeys, + BigInt.from(100), + exact: exact, + ); + case IsolateOperation.bulkVectorSearch: await _ensureRustLoaded(); final clipFloat32 = args["clipFloat32"] as List; From d61c18a6ef6a24d347548079173bea295d3766c9 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 12 Aug 2025 11:38:44 +0530 Subject: [PATCH 116/156] Use search with keys in similar images service --- .../similar_images_service.dart | 35 +++++++------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart index e439666d6b..0d8212b659 100644 --- a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart +++ b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart @@ -1,5 +1,4 @@ import "dart:math" show max; -import "dart:typed_data" show Float32List; import "package:flutter/foundation.dart" show kDebugMode; import "package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart" @@ -9,7 +8,6 @@ import "package:photos/db/ml/db.dart"; import "package:photos/extensions/stop_watch.dart"; import 'package:photos/models/file/file.dart'; import "package:photos/models/file/file_type.dart"; -import "package:photos/models/ml/vector.dart"; import "package:photos/models/similar_files.dart"; import "package:photos/services/machine_learning/ml_computer.dart"; import "package:photos/services/search_service.dart"; @@ -52,38 +50,29 @@ class SimilarImagesService { await mlDataDB.checkMigrateFillClipVectorDB(); w?.log("checkMigrateFillClipVectorDB"); - // Get the embeddings ready for vector search - final List allImageEmbeddings = - await MLDataDB.instance.getAllClipVectors(); - final clipFloat32 = allImageEmbeddings - .map( - (value) => Float32List.fromList(value.vector.toList()), - ) - .toList(); - final keys = Uint64List.fromList( - allImageEmbeddings.map((e) => BigInt.from(e.fileID)).toList(), - ); - w?.log("getAllClipVectors"); - - // Run bulk vector search - final (vectorKeys, distances) = await MLComputer.instance.bulkVectorSearch( - clipFloat32, - exact, - ); - w?.log("bulkSearchVectors"); - - // Get all files, and create a map of fileID to file + // Get all files, and all potential embedding IDs, and create a map of fileID to file final allFiles = Set.from( await SearchService.instance.getAllFilesForSearch(), ); final allFileIdsToFile = {}; + final fileIDs = []; for (final file in allFiles) { if (file.uploadedFileID != null && file.fileType != FileType.video) { allFileIdsToFile[file.uploadedFileID!] = file; + fileIDs.add(file.uploadedFileID!); } } + final Uint64List potentialKeys = Uint64List.fromList(fileIDs); w?.log("getAllFilesForSearch"); + // Run bulk vector search + final (keys, vectorKeys, distances) = + await MLComputer.instance.bulkVectorSearchWithKeys( + potentialKeys, + exact, + ); + w?.log("bulkSearchVectors"); + // Run through the vector search results and create SimilarFiles objects final alreadyUsedFileIDs = {}; final allSimilarFiles = []; From 5e52036f7afa8a4b66e0d624db80b5b79adfb0cc Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 12 Aug 2025 13:38:29 +0530 Subject: [PATCH 117/156] Better memory management --- .../apps/photos/rust/src/api/usearch_api.rs | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/mobile/apps/photos/rust/src/api/usearch_api.rs b/mobile/apps/photos/rust/src/api/usearch_api.rs index 28957d7114..34e9fa216d 100644 --- a/mobile/apps/photos/rust/src/api/usearch_api.rs +++ b/mobile/apps/photos/rust/src/api/usearch_api.rs @@ -83,12 +83,7 @@ impl VectorDB { self.save_index(); } - pub fn search_vectors( - &self, - query: &Vec, - count: usize, - exact: bool, - ) -> (Vec, Vec) { + pub fn search_vectors(&self, query: &[f32], count: usize, exact: bool) -> (Vec, Vec) { let matches = if exact { self.index .exact_search(query, count) @@ -124,29 +119,41 @@ impl VectorDB { count: usize, exact: bool, ) -> (Vec, Vec>, Vec>) { - // let max_contained_keys = potential_keys.len(); - let mut contained_keys = Vec::new(); - let mut queries = Vec::new(); + let dimensions = self.index.dimensions(); + let mut embedding_data = vec![0.0f32; potential_keys.len() * dimensions]; + let mut contained_keys = Vec::with_capacity(potential_keys.len()); + let mut actual_query_count = 0; + // Fill embeddings directly into flat storage using slices for key in potential_keys { - let contains: bool = self.index.contains(*key); - if contains { - let embedding = self.get_vector(*key); + if self.index.contains(*key) { + let start_idx = actual_query_count * dimensions; + let end_idx = start_idx + dimensions; + let embedding_slice = &mut embedding_data[start_idx..end_idx]; + + self.index + .get(*key, embedding_slice) + .expect("Failed to get vector"); contained_keys.push(*key); - queries.push(embedding); + actual_query_count += 1; } } + embedding_data.truncate(actual_query_count * dimensions); - let mut closeby_keys = Vec::new(); - let mut distances = Vec::new(); - for query in &queries { - let (keys_result, distances_result) = self.search_vectors(query, count, exact); - closeby_keys.push(keys_result); - distances.push(distances_result); - } - if contained_keys.len() != closeby_keys.len() { - panic!("The number of contained keys does not match the number of keys"); + let max_result_size = std::cmp::min(self.index.size(), count); + let mut closeby_keys = vec![Vec::with_capacity(max_result_size); actual_query_count]; + let mut distances = vec![Vec::with_capacity(max_result_size); actual_query_count]; + + // Search using slices and fill pre-allocated containers + for i in 0..actual_query_count { + let start_idx = i * dimensions; + let end_idx = start_idx + dimensions; + let query_slice = &embedding_data[start_idx..end_idx]; + let (keys_result, distances_result) = self.search_vectors(query_slice, count, exact); + closeby_keys[i] = keys_result; + distances[i] = distances_result; } + (contained_keys, closeby_keys, distances) } From e9feed59cdd78b1128a663bdb44f16f3da3b9031 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 12 Aug 2025 15:26:11 +0530 Subject: [PATCH 118/156] Use person faces to differentiate too --- .../similar_images_service.dart | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart index 0d8212b659..fa1341dba4 100644 --- a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart +++ b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart @@ -10,6 +10,7 @@ import 'package:photos/models/file/file.dart'; import "package:photos/models/file/file_type.dart"; import "package:photos/models/similar_files.dart"; import "package:photos/services/machine_learning/ml_computer.dart"; +import "package:photos/services/machine_learning/ml_result.dart"; import "package:photos/services/search_service.dart"; class SimilarImagesService { @@ -65,6 +66,24 @@ class SimilarImagesService { final Uint64List potentialKeys = Uint64List.fromList(fileIDs); w?.log("getAllFilesForSearch"); + // Get mapping of fileIDs to corresponding personIDs + final fileIDToPersonIDs = >{}; + final dbPersonClusterInfo = await mlDataDB.getPersonToClusterIdToFaceIds(); + for (final personID in dbPersonClusterInfo.keys) { + final clusterInfo = dbPersonClusterInfo[personID]!; + for (final faceIDs in clusterInfo.values) { + for (final faceID in faceIDs) { + final fileID = getFileIdFromFaceId(faceID); + if (allFileIdsToFile.containsKey(fileID)) { + fileIDToPersonIDs + .putIfAbsent(fileID, () => {}) + .add(personID); + } + } + } + } + w?.log("getFileIDToPersonIDs"); + // Run bulk vector search final (keys, vectorKeys, distances) = await MLComputer.instance.bulkVectorSearchWithKeys( @@ -86,21 +105,22 @@ class SimilarImagesService { final otherFileIDs = vectorKeys[i]; final distancesToFiles = distances[i]; final similarFilesList = []; + final personIDs = fileIDToPersonIDs[fileID] ?? {}; double furthestDistance = 0.0; for (int j = 0; j < otherFileIDs.length; j++) { final otherFileID = otherFileIDs[j].toInt(); if (otherFileID == fileID) continue; - final distance = distancesToFiles[j]; - if (distance > distanceThreshold) { - break; - } else { - furthestDistance = max(furthestDistance, distance); - } if (alreadyUsedFileIDs.contains(otherFileID)) continue; + final distance = distancesToFiles[j]; + if (distance > distanceThreshold) break; final otherFile = allFileIdsToFile[otherFileID]; - if (otherFile != null && otherFile.uploadedFileID != null) { - similarFilesList.add(otherFile); + if (otherFile == null || otherFile.uploadedFileID == null) { + continue; } + final otherPersonIDs = fileIDToPersonIDs[otherFileID] ?? {}; + if (!setsAreEqual(personIDs, otherPersonIDs)) continue; + similarFilesList.add(otherFile); + furthestDistance = max(furthestDistance, distance); } if (similarFilesList.isNotEmpty) { similarFilesList.add(firstLoopFile); @@ -123,3 +143,7 @@ class SimilarImagesService { return allSimilarFiles; } } + +bool setsAreEqual(Set set1, Set set2) { + return set1.length == set2.length && set1.containsAll(set2); +} From 11ef667433ecb0fbc7c16af604d49da6ded1cd46 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 12 Aug 2025 15:29:41 +0530 Subject: [PATCH 119/156] Select all to select extra --- mobile/apps/photos/lib/ui/tools/similar_images_page.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 93df29d542..2c042cb92e 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -488,7 +488,7 @@ class _SimilarImagesPageState extends State { } else { // Select all files in this group _selectedFiles.selectAll( - similarFiles.files.toSet(), + similarFiles.files.sublist(1).toSet(), ); } }); @@ -762,7 +762,7 @@ class _SimilarImagesPageState extends State { child: Text( unselectAll ? "Unselect all" // TODO: lau: extract string - : "Select all", // TODO: lau: extract string + : "Select extra", // TODO: lau: extract string style: textTheme.smallMuted.copyWith( color: unselectAll ? Colors.white : colorScheme.textMuted, ), From de44b1813edc6c60c55103622f9020bf8085003b Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Tue, 12 Aug 2025 17:34:56 +0530 Subject: [PATCH 120/156] chore: update target api & compile sdk to 36 (android 16) --- mobile/apps/auth/android/app/build.gradle | 4 ++-- mobile/apps/photos/android/app/build.gradle | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/apps/auth/android/app/build.gradle b/mobile/apps/auth/android/app/build.gradle index d8ffec49e8..4ccb47e6c7 100644 --- a/mobile/apps/auth/android/app/build.gradle +++ b/mobile/apps/auth/android/app/build.gradle @@ -30,7 +30,7 @@ if (keystorePropertiesFile.exists()) { android { namespace "io.ente.auth" - compileSdk 34 + compileSdk 36 ndkVersion flutter.ndkVersion compileOptions { @@ -57,7 +57,7 @@ android { // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion 21 - targetSdkVersion 34 + targetSdkVersion 36 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/mobile/apps/photos/android/app/build.gradle b/mobile/apps/photos/android/app/build.gradle index f1b1ef57a7..8c47591b14 100644 --- a/mobile/apps/photos/android/app/build.gradle +++ b/mobile/apps/photos/android/app/build.gradle @@ -30,7 +30,7 @@ if (keystorePropertiesFile.exists()) { android { namespace = "io.ente.photos" - compileSdk = 35 + compileSdk = 36 ndkVersion = flutter.ndkVersion compileOptions { @@ -56,7 +56,7 @@ android { defaultConfig { applicationId = "io.ente.photos" minSdk = 26 - targetSdk = flutter.targetSdkVersion + targetSdk = 36 versionCode = flutter.versionCode versionName = flutter.versionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" From b11d0e7c946053d6c7620ea675b2578e2ced6a48 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 13 Aug 2025 09:04:53 +0530 Subject: [PATCH 121/156] [web] Update yarn.lock --- web/yarn.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/yarn.lock b/web/yarn.lock index 6553105924..622a5cd80a 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -1227,7 +1227,7 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.15.tgz#e6e5a86d602beaca71ce5163fadf5f95d70931c7" integrity sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw== -"@types/react-dom@^19.1.1", "@types/react-dom@^19.1.6": +"@types/react-dom@^19.1.6": version "19.1.6" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.1.6.tgz#4af629da0e9f9c0f506fc4d1caa610399c595d64" integrity sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw== @@ -1244,7 +1244,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^19.1.0", "@types/react@^19.1.8": +"@types/react@*", "@types/react@^19.1.8": version "19.1.8" resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.8.tgz#ff8395f2afb764597265ced15f8dddb0720ae1c3" integrity sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g== From f3005736049b8f74d67c9de2b795c32639987910 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 12 Aug 2025 17:45:34 +0530 Subject: [PATCH 122/156] Simplify --- web/apps/photos/src/components/Sidebar.tsx | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/web/apps/photos/src/components/Sidebar.tsx b/web/apps/photos/src/components/Sidebar.tsx index 27f6aca66c..8aca5f326e 100644 --- a/web/apps/photos/src/components/Sidebar.tsx +++ b/web/apps/photos/src/components/Sidebar.tsx @@ -1105,13 +1105,6 @@ const DomainSettingsContents: React.FC = () => { {customDomainCNAME} - - - - - Within 1 hour, your public albums will be accessible via - your domain! - For more information, see @@ -1126,13 +1119,11 @@ const DomainSettingsContents: React.FC = () => { interface DomainSectionProps { title: string; ordinal: string; - isEmoji?: boolean; } const DomainItem: React.FC> = ({ title, ordinal, - isEmoji, children, }) => ( @@ -1140,7 +1131,7 @@ const DomainItem: React.FC> = ({ direction="row" sx={{ alignItems: "center", justifyContent: "space-between" }} > - {title} + {title} Date: Tue, 12 Aug 2025 19:08:21 +0530 Subject: [PATCH 123/156] Subscription check --- web/apps/photos/src/components/Sidebar.tsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/web/apps/photos/src/components/Sidebar.tsx b/web/apps/photos/src/components/Sidebar.tsx index 8aca5f326e..f163f04920 100644 --- a/web/apps/photos/src/components/Sidebar.tsx +++ b/web/apps/photos/src/components/Sidebar.tsx @@ -9,6 +9,7 @@ import LockOutlinedIcon from "@mui/icons-material/LockOutlined"; import NorthEastIcon from "@mui/icons-material/NorthEast"; import VisibilityOffIcon from "@mui/icons-material/VisibilityOff"; import { + Alert, Box, Dialog, DialogContent, @@ -1026,7 +1027,6 @@ const DomainSettings: React.FC = ({ onRootClose={handleRootClose} // TODO: CD: Translations title={pt("Custom domains")} - // caption={pt("Your albums, your domain")} caption="Use your own domain when sharing" > @@ -1034,8 +1034,9 @@ const DomainSettings: React.FC = ({ ); }; -// Separate component to reset state on back. +// Separate component to reset state on going back. const DomainSettingsContents: React.FC = () => { + const userDetails = useUserDetailsSnapshot(); const { customDomain, customDomainCNAME } = useSettingsSnapshot(); const formik = useFormik({ @@ -1060,10 +1061,18 @@ const DomainSettingsContents: React.FC = () => { }, }); + const disableShare = + !userDetails || !isSubscriptionActivePaid(userDetails.subscription); + // TODO: CD: help return ( + {disableShare && ( + + {t("sharing_disabled_for_free_accounts")} + + )}
{ fullWidth autoFocus={true} margin="dense" - disabled={formik.isSubmitting} + disabled={disableShare || formik.isSubmitting} error={!!formik.errors.domain} helperText={ formik.errors.domain ?? @@ -1087,10 +1096,11 @@ const DomainSettingsContents: React.FC = () => { - {customDomain ? pt("Update") : pt("Save")} + {customDomain ? t("update") : t("save")}
From d7f88c189041f64700622a9277fe18c7eafc2535 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 13 Aug 2025 09:25:45 +0530 Subject: [PATCH 124/156] use rust in gh actions internal releases --- .github/workflows/mobile-daily-internal.yml | 8 ++ .../mobile-internal-release-rust.yml | 77 ------------------- .github/workflows/mobile-internal-release.yml | 8 ++ 3 files changed, 16 insertions(+), 77 deletions(-) delete mode 100644 .github/workflows/mobile-internal-release-rust.yml diff --git a/.github/workflows/mobile-daily-internal.yml b/.github/workflows/mobile-daily-internal.yml index e05600e312..0429b8c001 100644 --- a/.github/workflows/mobile-daily-internal.yml +++ b/.github/workflows/mobile-daily-internal.yml @@ -38,6 +38,14 @@ jobs: flutter-version: ${{ env.FLUTTER_VERSION }} cache: true + - name: Install Rust ${{ env.RUST_VERSION }} + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_VERSION }} + + - name: Install Flutter Rust Bridge + run: cargo install flutter_rust_bridge_codegen + - name: Increment version code for build run: | CURRENT_VERSION=$(grep '^version:' pubspec.yaml | sed 's/version: //') diff --git a/.github/workflows/mobile-internal-release-rust.yml b/.github/workflows/mobile-internal-release-rust.yml deleted file mode 100644 index e5c85f1e37..0000000000 --- a/.github/workflows/mobile-internal-release-rust.yml +++ /dev/null @@ -1,77 +0,0 @@ -name: "Internal release (photos with rust)" - -on: - workflow_dispatch: # Allow manually running the action - -env: - FLUTTER_VERSION: "3.32.8" - RUST_VERSION: "1.85.1" - -permissions: - contents: write - -jobs: - build: - runs-on: ubuntu-latest - - defaults: - run: - working-directory: mobile/apps/photos - - steps: - - name: Checkout code and submodules - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Setup JDK 17 - uses: actions/setup-java@v1 - with: - java-version: 17 - - - name: Install Flutter ${{ env.FLUTTER_VERSION }} - uses: subosito/flutter-action@v2 - with: - channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} - cache: true - - - name: Install Rust ${{ env.RUST_VERSION }} - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ env.RUST_VERSION }} - - - name: Install Flutter Rust Bridge - run: cargo install flutter_rust_bridge_codegen - - - name: Setup keys - uses: timheuer/base64-to-file@v1 - with: - fileName: "keystore/ente_photos_key.jks" - encodedString: ${{ secrets.SIGNING_KEY_PHOTOS }} - - - name: Build PlayStore AAB - run: | - flutter build appbundle --dart-define=cronetHttpNoPlay=true --release --flavor playstore - env: - SIGNING_KEY_PATH: "/home/runner/work/_temp/keystore/ente_photos_key.jks" - SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS_PHOTOS }} - SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD_PHOTOS }} - SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD_PHOTOS }} - - - name: Upload AAB to PlayStore - uses: r0adkll/upload-google-play@v1 - with: - serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }} - packageName: io.ente.photos - releaseFiles: mobile/apps/photos/build/app/outputs/bundle/playstoreRelease/app-playstore-release.aab - track: internal - - - name: Notify Discord - uses: sarisia/actions-status-discord@v1 - with: - webhook: ${{ secrets.DISCORD_INTERNAL_RELEASE_WEBHOOK }} - nodetail: true - title: "🏆 Internal release available for Photos" - description: "[Download](https://play.google.com/store/apps/details?id=io.ente.photos)" - color: 0x00ff00 diff --git a/.github/workflows/mobile-internal-release.yml b/.github/workflows/mobile-internal-release.yml index 8ce8febf9f..43b2124cb8 100644 --- a/.github/workflows/mobile-internal-release.yml +++ b/.github/workflows/mobile-internal-release.yml @@ -35,6 +35,14 @@ jobs: flutter-version: ${{ env.FLUTTER_VERSION }} cache: true + - name: Install Rust ${{ env.RUST_VERSION }} + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_VERSION }} + + - name: Install Flutter Rust Bridge + run: cargo install flutter_rust_bridge_codegen + - name: Setup keys uses: timheuer/base64-to-file@v1 with: From 68639dfd550e3dbca81c950af0a1d05f3be432fd Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 13 Aug 2025 09:49:42 +0530 Subject: [PATCH 125/156] Update cargokit with upgraded flutter version --- mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle b/mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle index 1aead89136..d139d04f66 100644 --- a/mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle +++ b/mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle @@ -95,7 +95,7 @@ class CargoKitPlugin implements Plugin { private Plugin _findFlutterPlugin(Map projects) { for (project in projects) { for (plugin in project.value.getPlugins()) { - if (plugin.class.name == "FlutterPlugin") { + if (plugin.class.name == "com.flutter.gradle.FlutterPlugin") { return plugin; } } @@ -132,7 +132,7 @@ class CargoKitPlugin implements Plugin { def jniLibs = project.android.sourceSets.maybeCreate(buildType).jniLibs; jniLibs.srcDir(new File(cargoOutputDir)) - def platforms = plugin.getTargetPlatforms().collect() + def platforms = com.flutter.gradle.FlutterPluginUtils.getTargetPlatforms(project).collect() // Same thing addFlutterDependencies does in flutter.gradle if (buildType == "debug") { From a98385c3a31d7cca143716827a76220e9b68b840 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 13 Aug 2025 09:49:51 +0530 Subject: [PATCH 126/156] Dependencies --- mobile/apps/photos/pubspec.lock | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/mobile/apps/photos/pubspec.lock b/mobile/apps/photos/pubspec.lock index 884788fa86..d9ef339a3a 100644 --- a/mobile/apps/photos/pubspec.lock +++ b/mobile/apps/photos/pubspec.lock @@ -170,6 +170,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.5.4" + build_cli_annotations: + dependency: transitive + description: + name: build_cli_annotations + sha256: b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172 + url: "https://pub.dev" + source: hosted + version: "2.1.0" build_config: dependency: transitive description: @@ -2328,7 +2336,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_gen: dependency: transitive description: From 8c5f7e62bef4d8fa9462c071b15fd2a161cb9d55 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 13 Aug 2025 10:55:23 +0530 Subject: [PATCH 127/156] Don't log clip queries --- .../semantic_search_service.dart | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/mobile/apps/photos/lib/services/machine_learning/semantic_search/semantic_search_service.dart b/mobile/apps/photos/lib/services/machine_learning/semantic_search/semantic_search_service.dart index cf3930a782..71ab953b6d 100644 --- a/mobile/apps/photos/lib/services/machine_learning/semantic_search/semantic_search_service.dart +++ b/mobile/apps/photos/lib/services/machine_learning/semantic_search/semantic_search_service.dart @@ -1,6 +1,4 @@ import "dart:async" show Timer, unawaited; -import "dart:developer" as dev show log; -import "dart:math" show min; import "dart:ui" show Image; import "package:flutter/foundation.dart"; @@ -149,11 +147,13 @@ class SemanticSearchService { }, ); final queryResults = similarityResults[query]!; - // print query for top ten scores - for (int i = 0; i < min(10, queryResults.length); i++) { - final result = queryResults[i]; - dev.log("Query: $query, Score: ${result.score}, index $i"); - } + // Uncomment if needed for debugging: print query for top ten scores + // if (kDebugMode) { + // for (int i = 0; i < min(10, queryResults.length); i++) { + // final result = queryResults[i]; + // dev.log("Query: $query, Score: ${result.score}, index $i"); + // } + // } final Map fileIDToScoreMap = {}; for (final result in queryResults) { @@ -265,6 +265,13 @@ class SemanticSearchService { required Map minimumSimilarityMap, }) async { final startTime = DateTime.now(); + // Uncomment if needed for debugging: print query embeddings + // if (kDebugMode) { + // for (final queryText in textQueryToEmbeddingMap.keys) { + // final embedding = textQueryToEmbeddingMap[queryText]!; + // dev.log("CLIPTEXT Query: $queryText, embedding: $embedding"); + // } + // } await _cacheClipVectors(); final Map> queryResults = await MLComputer .instance From 4db135d5d013c05890ca19ca90f2360a2fffbae5 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 13 Aug 2025 10:55:54 +0530 Subject: [PATCH 128/156] Don't log clip queries --- .../semantic_search_service.dart | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/mobile/apps/photos/lib/services/machine_learning/semantic_search/semantic_search_service.dart b/mobile/apps/photos/lib/services/machine_learning/semantic_search/semantic_search_service.dart index 05df3a17dc..040c87af77 100644 --- a/mobile/apps/photos/lib/services/machine_learning/semantic_search/semantic_search_service.dart +++ b/mobile/apps/photos/lib/services/machine_learning/semantic_search/semantic_search_service.dart @@ -1,6 +1,4 @@ import "dart:async" show Timer, unawaited; -import "dart:developer" as dev show log; -import "dart:math" show min; import "dart:ui" show Image; import "package:flutter/foundation.dart"; @@ -149,11 +147,13 @@ class SemanticSearchService { }, ); final queryResults = similarityResults[query]!; - // print query for top ten scores - for (int i = 0; i < min(10, queryResults.length); i++) { - final result = queryResults[i]; - dev.log("Query: $query, Score: ${result.score}, index $i"); - } + // Uncomment if needed for debugging: print query for top ten scores + // if (kDebugMode) { + // for (int i = 0; i < min(10, queryResults.length); i++) { + // final result = queryResults[i]; + // dev.log("Query: $query, Score: ${result.score}, index $i"); + // } + // } final Map fileIDToScoreMap = {}; for (final result in queryResults) { @@ -265,15 +265,15 @@ class SemanticSearchService { required Map minimumSimilarityMap, }) async { final startTime = DateTime.now(); - if (kDebugMode) { - for (final queryText in textQueryToEmbeddingMap.keys) { - final embedding = textQueryToEmbeddingMap[queryText]!; - dev.log("CLIPTEXT Query: $queryText, embedding: $embedding"); - } - } - late final Map> queryResults; + // if (kDebugMode) { + // for (final queryText in textQueryToEmbeddingMap.keys) { + // final embedding = textQueryToEmbeddingMap[queryText]!; + // dev.log("CLIPTEXT Query: $queryText, embedding: $embedding"); + // } + // } await _cacheClipVectors(); - queryResults = await MLComputer.instance.computeBulkSimilarities( + final Map> queryResults = + await MLComputer.instance.computeBulkSimilarities( textQueryToEmbeddingMap, minimumSimilarityMap, ); From 0c9f9a60b7052c8591d1f5e69d575905683f83f7 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 13 Aug 2025 09:13:07 +0530 Subject: [PATCH 129/156] Help --- docs/docs/.vitepress/sidebar.ts | 4 + docs/docs/photos/features/custom-domains.md | 91 +++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 docs/docs/photos/features/custom-domains.md diff --git a/docs/docs/.vitepress/sidebar.ts b/docs/docs/.vitepress/sidebar.ts index 4cd620992e..5cc6b177cc 100644 --- a/docs/docs/.vitepress/sidebar.ts +++ b/docs/docs/.vitepress/sidebar.ts @@ -26,6 +26,10 @@ export const sidebar = [ text: "Collecting photos", link: "/photos/features/collect", }, + { + text: "Custom domains", + link: "/photos/features/custom-domains", + }, { text: "Deduplicate", link: "/photos/features/deduplicate", diff --git a/docs/docs/photos/features/custom-domains.md b/docs/docs/photos/features/custom-domains.md new file mode 100644 index 0000000000..899dd052dd --- /dev/null +++ b/docs/docs/photos/features/custom-domains.md @@ -0,0 +1,91 @@ +--- +title: Custom domains +description: Use your own domain when sharing photos and videos stored in Ente Photos +--- + +# Custom domains + +Custom domains allow you to serve your public links with your own personalized domain. + +For example, if I have an Ente album and wish to share it with my friends, I can go to the album's sharing settings and create a public link. When I copy this link, it will of the form of + +``` +https://albums.ente.io/?t=... +``` + +The custom domains feature allows you to instead create a link that uses your own domain, say + +``` +https://pics.example.org/?t=... +``` + +You don't need to run any servers or manage any services, Ente will still host and serve your album for you, the only thing that changes is that you can serve your links using your personalized domain. + +## Availability + +The custom domains feature requires the ability to publicly share albums which for abuse prevention reasons can only be done by people with an active Ente subscription. + +## Setup + +The setup involves two steps: + +1. Letting Ente know about the domain you wish to use for serving your public links. +2. Updating your DNS settings to point your domain (or subdomain) to **my.ente.io**. + +For people who are comfortable with changing DNS settings on their domain provider, this entire process is very simple and should be done in a few minutes. For people who are not comfortable with changing DNS, we will provide a more detailed breakdown below. + +Let's dive in. + +To make the process concrete, let's assume we're trying to use `pics.example.org` as our custom domain. Note that there is no restriction to use a subdomain, a top level domain can be used as a custom domain too. That is, either of `example.org` or `subdomain.example.org` is fine, Ente will work with both. + +### Step 1 - Link your domain + +The first step is to let Ente know about the domain or subdomain you wish to use by linking it to your account. + +> [!WARNING] +> +> Currently (Aug 2025) the ability to link a custom domain is only present in Ente's web app, [web.ente.io](https://web.ente.io). It will come to Ente mobile and desktop when their next versions get released. + +Head over to Preferences > Custom domains, in the domain field enter "pics.example.org" (replace with your subdomain) and press "Save". That's it. The linking is done. + +### Step 2 - Add DNS entry + +The second step is to add a CNAME entry in your DNS provider that forwards requests for pics.example.org (replace with your subdomain) to **my.ente.io**. + +Specifically, you need to add a `CNAME record` from the domain (or subdomain) of your choice to `my.ente.io`. You can leave the TTL at its default. + +|---|---| +| Record Type | CNAME | +| Key | The subdomain you're using, e.g `pics` | +| Value | `my.ente.io` | +| TTL | Auto (or your provider's default) | + +The exact steps for doing this depend on the DNS provider that you're using. + +> Your DNS provider usually is the service from which you bought your domain. The domain name seller will provide some sort of an admin panel where you can configure your DNS settings. + +As concrete examples, here is how this step would look for Cloudflare: + +And here is how it would look for Namecheap: + +The time it takes for DNS records to update is dependent on your DNS provider. Usually the changes should start reflecting within a few minutes, and should almost always reflect within an hour. + +Once the DNS changes have been applied, then you can take any public link to your shared albums, replace `albums.ente.io` with your choice (e.g. `pics.example.org`), and the link will still work. + +You don't need to do this manually though, the apps will do it for you. More on this in the next section. + +## Using + +Using is trivial. When you go to an album's sharing options and copy the link to it, Ente will automatically copy the link that uses your configured domain. + +> [!WARNING] +> +> Currently (Aug 2025) the ability to automatically substitute your custom domain is only present in Ente's web app, [web.ente.io](https://web.ente.io). It will come to Ente mobile and desktop when their next versions get released. + +## Unsetting + +To stop using your custom domain, we need to undo the two steps we did during setup. + +1. Unlink your domain in Ente. This can be done just by going to Preferences > Custom Domains, clearing the value in the Domain input and pressing Save. + +2. Remove the CNAME record you added during setup in your DNS provider. From fdc3cb8f853ceb6748988b6933e29df882fb63c1 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 13 Aug 2025 11:50:38 +0530 Subject: [PATCH 130/156] Images --- docs/docs/.vitepress/sidebar.ts | 2 +- .../photos/features/custom-domains/cf.png | Bin 0 -> 75480 bytes .../index.md} | 28 +++++++++++------- .../photos/features/custom-domains/nc.png | Bin 0 -> 41545 bytes 4 files changed, 18 insertions(+), 12 deletions(-) create mode 100644 docs/docs/photos/features/custom-domains/cf.png rename docs/docs/photos/features/{custom-domains.md => custom-domains/index.md} (76%) create mode 100644 docs/docs/photos/features/custom-domains/nc.png diff --git a/docs/docs/.vitepress/sidebar.ts b/docs/docs/.vitepress/sidebar.ts index 5cc6b177cc..cc83fb3d69 100644 --- a/docs/docs/.vitepress/sidebar.ts +++ b/docs/docs/.vitepress/sidebar.ts @@ -28,7 +28,7 @@ export const sidebar = [ }, { text: "Custom domains", - link: "/photos/features/custom-domains", + link: "/photos/features/custom-domains/", }, { text: "Deduplicate", diff --git a/docs/docs/photos/features/custom-domains/cf.png b/docs/docs/photos/features/custom-domains/cf.png new file mode 100644 index 0000000000000000000000000000000000000000..0bb47d867836e0ac54388563d4738a6b07d3e2d6 GIT binary patch literal 75480 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�<{TmcbijTS>|#r7FuMWmQb1 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 C Custom domains, in the domain field enter "pics.examp The second step is to add a CNAME entry in your DNS provider that forwards requests for pics.example.org (replace with your subdomain) to **my.ente.io**. -Specifically, you need to add a `CNAME record` from the domain (or subdomain) of your choice to `my.ente.io`. You can leave the TTL at its default. +Specifically, you need to add a `CNAME record` from the domain (or subdomain) of your choice to `my.ente.io`. You can leave the `TTL` at its default. -|---|---| -| Record Type | CNAME | -| Key | The subdomain you're using, e.g `pics` | -| Value | `my.ente.io` | -| TTL | Auto (or your provider's default) | +| Record Type | Name | Value | TTL | +| ----------- | :------------------------: | -----------: | -------------- | +| CNAME | Your subdomain, e.g `pics` | `my.ente.io` | Auto (default) | The exact steps for doing this depend on the DNS provider that you're using. @@ -66,8 +64,16 @@ The exact steps for doing this depend on the DNS provider that you're using. As concrete examples, here is how this step would look for Cloudflare: +![Adding a CNAME for custom domain in Cloudflare](cf.png) + And here is how it would look for Namecheap: +![Adding a CNAME for custom domain in Namecheap](nc.png) + +> [!NOTE] +> +> The examples are using "pics" as the subdomain, but that's just an example, you can use anything you like (or use "@" if you'd like to use the root domain itself). + The time it takes for DNS records to update is dependent on your DNS provider. Usually the changes should start reflecting within a few minutes, and should almost always reflect within an hour. Once the DNS changes have been applied, then you can take any public link to your shared albums, replace `albums.ente.io` with your choice (e.g. `pics.example.org`), and the link will still work. @@ -86,6 +92,6 @@ Using is trivial. When you go to an album's sharing options and copy the link to To stop using your custom domain, we need to undo the two steps we did during setup. -1. Unlink your domain in Ente. This can be done just by going to Preferences > Custom Domains, clearing the value in the Domain input and pressing Save. +1. Unlink your domain in Ente. This can be done just by going to Preferences > Custom Domains, clearing the value in the "Domain" input and pressing "Update". 2. Remove the CNAME record you added during setup in your DNS provider. diff --git a/docs/docs/photos/features/custom-domains/nc.png b/docs/docs/photos/features/custom-domains/nc.png new file mode 100644 index 0000000000000000000000000000000000000000..b6a34a008136d0536090958144dc11282d961707 GIT binary patch literal 41545 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 literal 0 HcmV?d00001 From eb9f8021ae480fff4ab14d0c498fb18f2fc7a2de Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 13 Aug 2025 12:23:25 +0530 Subject: [PATCH 131/156] Integrate --- web/apps/photos/src/components/Sidebar.tsx | 101 ++++++++++----------- 1 file changed, 49 insertions(+), 52 deletions(-) diff --git a/web/apps/photos/src/components/Sidebar.tsx b/web/apps/photos/src/components/Sidebar.tsx index f163f04920..729b332ed3 100644 --- a/web/apps/photos/src/components/Sidebar.tsx +++ b/web/apps/photos/src/components/Sidebar.tsx @@ -15,6 +15,7 @@ import { DialogContent, Divider, IconButton, + Link, Skeleton, Stack, styled, @@ -825,50 +826,43 @@ const Preferences: React.FC = ({ /> )} - { - /* TODO: CD */ process.env.NEXT_PUBLIC_ENTE_WIP_CD && ( - - - - - -
- } - onClick={showDomainSettings} - /> - ) - } + + + + + +
+ } + onClick={showDomainSettings} + /> } label={t("map")} @@ -1027,7 +1021,7 @@ const DomainSettings: React.FC = ({ onRootClose={handleRootClose} // TODO: CD: Translations title={pt("Custom domains")} - caption="Use your own domain when sharing" + caption={pt("Use your own domain when sharing")} > @@ -1064,8 +1058,6 @@ const DomainSettingsContents: React.FC = () => { const disableShare = !userDetails || !isSubscriptionActivePaid(userDetails.subscription); - // TODO: CD: help - return ( {disableShare && ( @@ -1089,7 +1081,7 @@ const DomainSettingsContents: React.FC = () => { formik.errors.domain ?? pt("Any domain or subdomain you own") } - label={t("Domain")} + label={pt("Domain")} placeholder={ut("photos.example.org")} sx={{ mb: 2 }} /> @@ -1116,10 +1108,15 @@ const DomainSettingsContents: React.FC = () => {
- For more information, see - - {" help "} - + For more information, see{" "} + + {t("help").toLocaleLowerCase()} +
From 0eb56a84376c2306baff877a7814374c6fda390b Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 13 Aug 2025 12:31:43 +0530 Subject: [PATCH 132/156] yarn pretty --- docs/docs/photos/faq/desktop.md | 2 +- .../troubleshooting/desktop-install/index.md | 2 +- web/apps/photos/src/components/Sidebar.tsx | 15 +++------------ 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/docs/docs/photos/faq/desktop.md b/docs/docs/photos/faq/desktop.md index f590c956a9..b705bedfc4 100644 --- a/docs/docs/photos/faq/desktop.md +++ b/docs/docs/photos/faq/desktop.md @@ -35,4 +35,4 @@ be specific to your distro (e.g. `xdg-desktop-menu forceupdate`). > [!NOTE] > > If you're using an AppImage and not seeing the icon, you'll need to -> [enable AppImage desktop integration](/photos/troubleshooting/desktop-install/#appimage-desktop-integration). \ No newline at end of file +> [enable AppImage desktop integration](/photos/troubleshooting/desktop-install/#appimage-desktop-integration). diff --git a/docs/docs/photos/troubleshooting/desktop-install/index.md b/docs/docs/photos/troubleshooting/desktop-install/index.md index 85e6cb5e62..8543b78e34 100644 --- a/docs/docs/photos/troubleshooting/desktop-install/index.md +++ b/docs/docs/photos/troubleshooting/desktop-install/index.md @@ -112,4 +112,4 @@ ip addr add 10.10.10.1/24 dev dummy0 ip link set dummy0 up ``` -Once the interface is up, Ente correctly detects that the system is online. \ No newline at end of file +Once the interface is up, Ente correctly detects that the system is online. diff --git a/web/apps/photos/src/components/Sidebar.tsx b/web/apps/photos/src/components/Sidebar.tsx index 729b332ed3..11abfe7e9e 100644 --- a/web/apps/photos/src/components/Sidebar.tsx +++ b/web/apps/photos/src/components/Sidebar.tsx @@ -9,7 +9,6 @@ import LockOutlinedIcon from "@mui/icons-material/LockOutlined"; import NorthEastIcon from "@mui/icons-material/NorthEast"; import VisibilityOffIcon from "@mui/icons-material/VisibilityOff"; import { - Alert, Box, Dialog, DialogContent, @@ -1030,7 +1029,6 @@ const DomainSettings: React.FC = ({ // Separate component to reset state on going back. const DomainSettingsContents: React.FC = () => { - const userDetails = useUserDetailsSnapshot(); const { customDomain, customDomainCNAME } = useSettingsSnapshot(); const formik = useFormik({ @@ -1046,6 +1044,8 @@ const DomainSettingsContents: React.FC = () => { log.error(`Failed to submit input ${domain}`, e); if (isHTTPErrorWithStatus(e, 400)) { setValueFieldError(pt("Invalid domain")); + } else if (isHTTPErrorWithStatus(e, 402)) { + setValueFieldError(t("sharing_disabled_for_free_accounts")); } else if (isHTTPErrorWithStatus(e, 409)) { setValueFieldError(pt("Domain already linked by a user")); } else { @@ -1055,16 +1055,8 @@ const DomainSettingsContents: React.FC = () => { }, }); - const disableShare = - !userDetails || !isSubscriptionActivePaid(userDetails.subscription); - return ( - {disableShare && ( - - {t("sharing_disabled_for_free_accounts")} - - )}
{ fullWidth autoFocus={true} margin="dense" - disabled={disableShare || formik.isSubmitting} + disabled={formik.isSubmitting} error={!!formik.errors.domain} helperText={ formik.errors.domain ?? @@ -1088,7 +1080,6 @@ const DomainSettingsContents: React.FC = () => { From f95410a5b10968757dbd38de3cd8f63f56fd76e3 Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Wed, 13 Aug 2025 13:13:37 +0530 Subject: [PATCH 133/156] fix: only bump target sdk to 35 --- mobile/apps/photos/android/app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/apps/photos/android/app/build.gradle b/mobile/apps/photos/android/app/build.gradle index 8c47591b14..a2100a6e24 100644 --- a/mobile/apps/photos/android/app/build.gradle +++ b/mobile/apps/photos/android/app/build.gradle @@ -56,7 +56,7 @@ android { defaultConfig { applicationId = "io.ente.photos" minSdk = 26 - targetSdk = 36 + targetSdk = 35 versionCode = flutter.versionCode versionName = flutter.versionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -153,4 +153,4 @@ dependencies { because("Align work-runtime-ktx versions") } } -} \ No newline at end of file +} From e265d4b4d278072c294848ae461beff5bb976bcc Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Wed, 13 Aug 2025 13:14:01 +0530 Subject: [PATCH 134/156] fix: only bump target sdk to 35 --- mobile/apps/auth/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/apps/auth/android/app/build.gradle b/mobile/apps/auth/android/app/build.gradle index 4ccb47e6c7..3da027f155 100644 --- a/mobile/apps/auth/android/app/build.gradle +++ b/mobile/apps/auth/android/app/build.gradle @@ -57,7 +57,7 @@ android { // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion 21 - targetSdkVersion 36 + targetSdkVersion 35 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" From b34a7697734355f32aebc53d379e64e71ea0f977 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 13 Aug 2025 14:18:13 +0530 Subject: [PATCH 135/156] Sort on name within individual files group --- .../services/machine_learning/similar_images_service.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart index fa1341dba4..fd1b0e9d1c 100644 --- a/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart +++ b/mobile/apps/photos/lib/services/machine_learning/similar_images_service.dart @@ -127,6 +127,9 @@ class SimilarImagesService { for (final file in similarFilesList) { alreadyUsedFileIDs.add(file.uploadedFileID!); } + similarFilesList.sort((a, b) { + return a.displayName.length.compareTo(b.displayName.length); + }); final similarFiles = SimilarFiles( similarFilesList, furthestDistance, @@ -136,10 +139,6 @@ class SimilarImagesService { } w?.log("going through files"); - // Sort the similar files by total size in descending order - allSimilarFiles.sort((a, b) => b.totalSize.compareTo(a.totalSize)); - w?.log("sort similar files"); - return allSimilarFiles; } } From cb3f1a5edc98eebeda6bce5c581dd5b059971d2a Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 13 Aug 2025 14:24:48 +0530 Subject: [PATCH 136/156] sort menu --- .../photos/lib/ui/tools/similar_images_page.dart | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index 2c042cb92e..f5a6896c96 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -800,18 +800,10 @@ class _SimilarImagesPageState extends State { initialValue: _sortKey.index, child: Padding( padding: const EdgeInsets.fromLTRB(24, 6, 24, 6), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - sortOptionText(_sortKey), - const Padding(padding: EdgeInsets.only(left: 4)), - Icon( - Icons.sort, - color: colorScheme.strokeBase, - size: 20, - ), - ], + child: Icon( + Icons.sort, + color: colorScheme.strokeBase, + size: 20, ), ), onSelected: (int index) { From 39c1e54cc9ae2db6d9656968da6d65f7fdbd4ab8 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 13 Aug 2025 14:56:53 +0530 Subject: [PATCH 137/156] Delete with symlink --- .../lib/ui/tools/similar_images_page.dart | 58 ++++++++++++++++--- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index f5a6896c96..89d50f6a2d 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -9,6 +9,7 @@ import "package:photos/models/file/file.dart"; import "package:photos/models/selected_files.dart"; import "package:photos/models/similar_files.dart"; import "package:photos/service_locator.dart"; +import "package:photos/services/collections_service.dart"; import "package:photos/services/machine_learning/similar_images_service.dart"; import 'package:photos/theme/ente_theme.dart'; import "package:photos/ui/common/loading_widget.dart"; @@ -707,7 +708,7 @@ class _SimilarImagesPageState extends State { isCritical: true, firstButtonOnTap: () async { try { - await _deleteFilesLogic(filesToDelete); + await _deleteFilesLogic(filesToDelete, true); } catch (e, s) { _logger.severe("Failed to delete files", e, s); if (flagService.internalUser) { @@ -717,29 +718,70 @@ class _SimilarImagesPageState extends State { }, ); } else { - await _deleteFilesLogic(filesToDelete); + await _deleteFilesLogic(filesToDelete, true); } } - Future _deleteFilesLogic(Set filesToDelete) async { - _selectedFiles.unSelectAll(filesToDelete); + Future _deleteFilesLogic( + Set filesToDelete, + bool createSymlink, + ) async { + if (filesToDelete.isEmpty) { + return; + } + final Map> collectionToFilesToAddMap = {}; + final allDeleteFiles = {}; final groupsToRemove = {}; - for (final file in filesToDelete) { - for (final similarGroup in _similarFilesList) { + for (final similarGroup in _similarFilesList) { + final groupDeleteFiles = {}; + for (final file in filesToDelete) { if (similarGroup.containsFile(file)) { similarGroup.removeFile(file); + groupDeleteFiles.add(file); + allDeleteFiles.add(file); if (similarGroup.isEmpty) { - groupsToRemove.add(similarGroup); + break; } } } + if (similarGroup.files.length <= 1) { + groupsToRemove.add(similarGroup); + } + if (groupDeleteFiles.isNotEmpty) { + filesToDelete.removeAll(groupDeleteFiles); + } + if (!similarGroup.isEmpty && createSymlink) { + final filesToKeep = similarGroup.files; + final collectionIDs = + filesToKeep.map((file) => file.collectionID).toSet(); + for (final deletedFile in groupDeleteFiles) { + final collectionID = deletedFile.collectionID; + if (collectionIDs.contains(collectionID) || collectionID == null) { + continue; + } + if (!collectionToFilesToAddMap.containsKey(collectionID)) { + collectionToFilesToAddMap[collectionID] = []; + } + collectionToFilesToAddMap[collectionID]!.addAll(filesToKeep); + } + } } for (final group in groupsToRemove) { _similarFilesList.remove(group); } + if (createSymlink) { + for (final collectionID in collectionToFilesToAddMap.keys) { + await CollectionsService.instance.addSilentlyToCollection( + collectionID, + collectionToFilesToAddMap[collectionID]!, + ); + } + } + + _selectedFiles.unSelectAll(allDeleteFiles); setState(() {}); - await deleteFilesFromRemoteOnly(context, filesToDelete.toList()); + await deleteFilesFromRemoteOnly(context, allDeleteFiles.toList()); } Widget _getSmallSelectButton(bool unselectAll, void Function() onTap) { From 6b93125b5dc417968e3e76d2f490ca0c5f6182d8 Mon Sep 17 00:00:00 2001 From: sayimburak Date: Wed, 13 Aug 2025 16:44:01 +0300 Subject: [PATCH 138/156] [auth] add custom icon for RaiderIO - Added Raider.IO SVG icon. - Updated custom-icons.json to include the new icon. Raider.IO is a platform that tracks World of Warcraft players' performance. --- .../assets/custom-icons/_data/custom-icons.json | 7 +++++++ .../assets/custom-icons/icons/raider_io.svg | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 mobile/apps/auth/assets/custom-icons/icons/raider_io.svg diff --git a/mobile/apps/auth/assets/custom-icons/_data/custom-icons.json b/mobile/apps/auth/assets/custom-icons/_data/custom-icons.json index 4ef6f6694c..1bc6884c6b 100644 --- a/mobile/apps/auth/assets/custom-icons/_data/custom-icons.json +++ b/mobile/apps/auth/assets/custom-icons/_data/custom-icons.json @@ -1358,6 +1358,13 @@ "r10.net" ] }, + { + "title": "RaiderIO", + "slug": "raider_io", + "altNames": [ + "raider.io" + ] + }, { "title": "Raindrop.io", "slug": "raindrop_io", diff --git a/mobile/apps/auth/assets/custom-icons/icons/raider_io.svg b/mobile/apps/auth/assets/custom-icons/icons/raider_io.svg new file mode 100644 index 0000000000..56b9849f6f --- /dev/null +++ b/mobile/apps/auth/assets/custom-icons/icons/raider_io.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file From 556c04aca27b56ccb14e3e2cfed8672a4fa96824 Mon Sep 17 00:00:00 2001 From: gfrcsd <23112101+gfrcsd@users.noreply.github.com> Date: Wed, 13 Aug 2025 22:12:50 +0100 Subject: [PATCH 139/156] Add custom icon for Skyscanner --- mobile/apps/auth/assets/custom-icons/_data/custom-icons.json | 4 ++++ mobile/apps/auth/assets/custom-icons/icons/skyscanner.svg | 1 + 2 files changed, 5 insertions(+) create mode 100644 mobile/apps/auth/assets/custom-icons/icons/skyscanner.svg diff --git a/mobile/apps/auth/assets/custom-icons/_data/custom-icons.json b/mobile/apps/auth/assets/custom-icons/_data/custom-icons.json index 1bc6884c6b..2e85ad557d 100644 --- a/mobile/apps/auth/assets/custom-icons/_data/custom-icons.json +++ b/mobile/apps/auth/assets/custom-icons/_data/custom-icons.json @@ -1508,6 +1508,10 @@ { "title": "Skinport" }, + { + "title": "Skyscanner", + "hex": "0770E3" + }, { "title": "SMSPool", "slug": "sms_pool_net", diff --git a/mobile/apps/auth/assets/custom-icons/icons/skyscanner.svg b/mobile/apps/auth/assets/custom-icons/icons/skyscanner.svg new file mode 100644 index 0000000000..69fa35c34c --- /dev/null +++ b/mobile/apps/auth/assets/custom-icons/icons/skyscanner.svg @@ -0,0 +1 @@ + \ No newline at end of file From b72b118299fbdd51f236ae0a02cd2b7b9e02b44e Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 14 Aug 2025 08:41:04 +0530 Subject: [PATCH 140/156] Note change --- mobile/apps/photos/scripts/store_changes.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile/apps/photos/scripts/store_changes.txt b/mobile/apps/photos/scripts/store_changes.txt index e6c8911ffe..1d6ce14986 100644 --- a/mobile/apps/photos/scripts/store_changes.txt +++ b/mobile/apps/photos/scripts/store_changes.txt @@ -1,4 +1,5 @@ -- Added support for custom domain links +- Added similar images debug screen (Settings > Backup > Free up space > Similar images) +- Added support for custom domain links - Image editor fixes: - Fixed bottom navigation bar color in light theme - Resolved initial color issue in paint editor From eb967709dddf052f166453588b37c45d1ae318b2 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 14 Aug 2025 08:53:38 +0530 Subject: [PATCH 141/156] Put vectorDB behind feature flag --- .../settings/backup/free_space_options.dart | 61 ++++++++++--------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/mobile/apps/photos/lib/ui/settings/backup/free_space_options.dart b/mobile/apps/photos/lib/ui/settings/backup/free_space_options.dart index 9583d26193..95f61344b8 100644 --- a/mobile/apps/photos/lib/ui/settings/backup/free_space_options.dart +++ b/mobile/apps/photos/lib/ui/settings/backup/free_space_options.dart @@ -188,38 +188,41 @@ class _FreeUpSpaceOptionsScreenState extends State { const SizedBox( height: 24, ), - MenuItemWidget( - captionedTextWidget: - const CaptionedTextWidget( - title: - "Similar images", // TODO: lau: extract string + if (flagService.enableVectorDb) + MenuItemWidget( + captionedTextWidget: + const CaptionedTextWidget( + title: + "Similar images", // TODO: lau: extract string + ), + menuItemColor: colorScheme.fillFaint, + trailingWidget: Icon( + Icons.chevron_right_outlined, + color: colorScheme.strokeBase, + ), + singleBorderRadius: 8, + alignCaptionedTextToLeft: true, + trailingIconIsMuted: true, + showOnlyLoadingState: true, + onTap: () async { + await routeToPage( + context, + const SimilarImagesPage(), + ); + }, ), - menuItemColor: colorScheme.fillFaint, - trailingWidget: Icon( - Icons.chevron_right_outlined, - color: colorScheme.strokeBase, + if (flagService.enableVectorDb) + const Align( + alignment: Alignment.centerLeft, + child: MenuSectionDescriptionWidget( + content: + "Use AI to find images that look similar to each other.", // TODO: lau: extract string + ), ), - singleBorderRadius: 8, - alignCaptionedTextToLeft: true, - trailingIconIsMuted: true, - showOnlyLoadingState: true, - onTap: () async { - await routeToPage( - context, - const SimilarImagesPage(), - ); - }, - ), - const Align( - alignment: Alignment.centerLeft, - child: MenuSectionDescriptionWidget( - content: - "Use AI to find images that look similar to each other.", // TODO: lau: extract string + if (flagService.enableVectorDb) + const SizedBox( + height: 24, ), - ), - const SizedBox( - height: 24, - ), MenuItemWidget( captionedTextWidget: CaptionedTextWidget( title: S.of(context).viewLargeFiles, From f4c0899e0255b3a015466929d56bdb7efd8838fb Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 14 Aug 2025 09:30:59 +0530 Subject: [PATCH 142/156] Use slicing --- mobile/apps/photos/rust/src/api/usearch_api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/apps/photos/rust/src/api/usearch_api.rs b/mobile/apps/photos/rust/src/api/usearch_api.rs index 34e9fa216d..49e8301375 100644 --- a/mobile/apps/photos/rust/src/api/usearch_api.rs +++ b/mobile/apps/photos/rust/src/api/usearch_api.rs @@ -60,7 +60,7 @@ impl VectorDB { } } - pub fn add_vector(&self, key: u64, vector: &Vec) { + pub fn add_vector(&self, key: u64, vector: &[f32]) { if self.contains_vector(key) { self.remove_vector(key); } else { From 0e9556603e2d5503340a014ad04513e7990fd7c0 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Thu, 14 Aug 2025 10:32:17 +0530 Subject: [PATCH 143/156] [docs] Custom domains troubleshooting --- docs/docs/photos/features/custom-domains/index.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/docs/photos/features/custom-domains/index.md b/docs/docs/photos/features/custom-domains/index.md index 3a3b4cab8e..444ea00d44 100644 --- a/docs/docs/photos/features/custom-domains/index.md +++ b/docs/docs/photos/features/custom-domains/index.md @@ -66,7 +66,7 @@ As concrete examples, here is how this step would look for Cloudflare: ![Adding a CNAME for custom domain in Cloudflare](cf.png) -And here is how it would look for Namecheap: +Note that orange proxy option is off. And here is how it would look for Namecheap: ![Adding a CNAME for custom domain in Namecheap](nc.png) @@ -78,7 +78,15 @@ The time it takes for DNS records to update is dependent on your DNS provider. U Once the DNS changes have been applied, then you can take any public link to your shared albums, replace `albums.ente.io` with your choice (e.g. `pics.example.org`), and the link will still work. -You don't need to do this manually though, the apps will do it for you. More on this in the next section. +You don't need to do this manually though, the apps will do it for you. More on this in the next section. But first, some troubleshooting tips. + +### Troubleshooting + +If your domain is not working, go through the following checklist. + +- The CNAME should be from your domain to my.ente.io, not the other way around. That is, `pics.example.org => my.ente.io`. + +- If you're using Cloudflare DNS, make sure that the "Orange" proxy status toggle is off, and the Proxy status is the "Grey" DNS only. ## Using From d76c9ce6db03ffc7d8d702649d07ab74c93a02ec Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Thu, 14 Aug 2025 11:58:22 +0530 Subject: [PATCH 144/156] [web] Move custom domain related strings to translations --- web/apps/photos/src/components/Sidebar.tsx | 67 ++++++++++--------- .../base/locales/en-US/translation.json | 14 +++- 2 files changed, 50 insertions(+), 31 deletions(-) diff --git a/web/apps/photos/src/components/Sidebar.tsx b/web/apps/photos/src/components/Sidebar.tsx index 11abfe7e9e..f2b4cc7b12 100644 --- a/web/apps/photos/src/components/Sidebar.tsx +++ b/web/apps/photos/src/components/Sidebar.tsx @@ -55,7 +55,6 @@ import { useBaseContext } from "ente-base/context"; import { isHTTPErrorWithStatus } from "ente-base/http"; import { getLocaleInUse, - pt, setLocaleInUse, supportedLocales, ut, @@ -826,7 +825,7 @@ const Preferences: React.FC = ({ )} = ({ @@ -1043,11 +1041,11 @@ const DomainSettingsContents: React.FC = () => { } catch (e) { log.error(`Failed to submit input ${domain}`, e); if (isHTTPErrorWithStatus(e, 400)) { - setValueFieldError(pt("Invalid domain")); + setValueFieldError(t("invalid_domain")); } else if (isHTTPErrorWithStatus(e, 402)) { setValueFieldError(t("sharing_disabled_for_free_accounts")); } else if (isHTTPErrorWithStatus(e, 409)) { - setValueFieldError(pt("Domain already linked by a user")); + setValueFieldError(t("already_linked_domain")); } else { setValueFieldError(t("generic_error")); } @@ -1057,7 +1055,7 @@ const DomainSettingsContents: React.FC = () => { return ( - + { margin="dense" disabled={formik.isSubmitting} error={!!formik.errors.domain} - helperText={ - formik.errors.domain ?? - pt("Any domain or subdomain you own") - } - label={pt("Domain")} + helperText={formik.errors.domain ?? t("domain_help")} + label={t("domain")} placeholder={ut("photos.example.org")} sx={{ mb: 2 }} /> @@ -1088,26 +1083,38 @@ const DomainSettingsContents: React.FC = () => { - + - On your DNS provider, add a CNAME from your domain to{" "} - - {customDomainCNAME} - + + ), + }} + values={{ host: customDomainCNAME }} + /> - For more information, see{" "} - - {t("help").toLocaleLowerCase()} - + + ), + }} + /> diff --git a/web/packages/base/locales/en-US/translation.json b/web/packages/base/locales/en-US/translation.json index 2e469fc329..0266ed04a2 100644 --- a/web/packages/base/locales/en-US/translation.json +++ b/web/packages/base/locales/en-US/translation.json @@ -685,5 +685,17 @@ "person_favorites": "{{name}}'s favorites", "shared_favorites": "Shared favorites", "added_by_name": "Added by {{name}}", - "unowned_files_not_processed": "Files added by other users were not processed" + "unowned_files_not_processed": "Files added by other users were not processed", + "custom_domains": "Custom domains", + "custom_domains_desc": "Use your own domain when sharing", + "link_your_domain": "Link your domain", + "domain": "Domain", + "domain_help": "Any domain or subdomain you own", + "invalid_domain": "Invalid domain", + "already_linked_domain": "Domain already linked by a user", + "add_dns_entry": "Add DNS entry", + "add_dns_entry_hint": "On your DNS provider, add a CNAME from your domain to {{host}}", + "custom_domains_help": "For more information, see
help", + "num_1": "1", + "num_2": "2" } From 9899298500e9ee9bb946a1c67a2414cb538e8b38 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 14 Aug 2025 12:21:41 +0530 Subject: [PATCH 145/156] Remove old gitsubmodule refs --- auth/assets/simple-icons | 1 - auth/flutter | 1 - 2 files changed, 2 deletions(-) delete mode 160000 auth/assets/simple-icons delete mode 160000 auth/flutter diff --git a/auth/assets/simple-icons b/auth/assets/simple-icons deleted file mode 160000 index 6dcfdc2f58..0000000000 --- a/auth/assets/simple-icons +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6dcfdc2f58f6150d7b097b5cab158d27413f6366 diff --git a/auth/flutter b/auth/flutter deleted file mode 160000 index 5874a72aa4..0000000000 --- a/auth/flutter +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5874a72aa4c779a02553007c47dacbefba2374dc From 9afa40764e2f14e233a5187c6aa599e3166831d7 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 14 Aug 2025 12:25:46 +0530 Subject: [PATCH 146/156] Remove more old refs --- web/apps/photos/thirdparty/ffmpeg-wasm | 1 - web/apps/photos/thirdparty/photoswipe | 1 - 2 files changed, 2 deletions(-) delete mode 160000 web/apps/photos/thirdparty/ffmpeg-wasm delete mode 160000 web/apps/photos/thirdparty/photoswipe diff --git a/web/apps/photos/thirdparty/ffmpeg-wasm b/web/apps/photos/thirdparty/ffmpeg-wasm deleted file mode 160000 index 8493ad48b1..0000000000 --- a/web/apps/photos/thirdparty/ffmpeg-wasm +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8493ad48b12f83f881a59b84b003974ef23f9e96 diff --git a/web/apps/photos/thirdparty/photoswipe b/web/apps/photos/thirdparty/photoswipe deleted file mode 160000 index bf4a072503..0000000000 --- a/web/apps/photos/thirdparty/photoswipe +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bf4a072503df18c8d6b047e66a437534c5c05bc5 From 6db71d79959bada9b21046bbeb29bb13060deafc Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Thu, 14 Aug 2025 07:04:33 +0000 Subject: [PATCH 147/156] New Crowdin translations by GitHub Action --- web/packages/base/locales/ar-SA/translation.json | 14 +++++++++++++- web/packages/base/locales/be-BY/translation.json | 14 +++++++++++++- web/packages/base/locales/bg-BG/translation.json | 14 +++++++++++++- web/packages/base/locales/ca-ES/translation.json | 14 +++++++++++++- web/packages/base/locales/cs-CZ/translation.json | 14 +++++++++++++- web/packages/base/locales/da-DK/translation.json | 14 +++++++++++++- web/packages/base/locales/de-DE/translation.json | 14 +++++++++++++- web/packages/base/locales/el-GR/translation.json | 14 +++++++++++++- web/packages/base/locales/es-ES/translation.json | 14 +++++++++++++- web/packages/base/locales/et-EE/translation.json | 14 +++++++++++++- web/packages/base/locales/fa-IR/translation.json | 14 +++++++++++++- web/packages/base/locales/fi-FI/translation.json | 14 +++++++++++++- web/packages/base/locales/fr-FR/translation.json | 16 ++++++++++++++-- web/packages/base/locales/gu-IN/translation.json | 14 +++++++++++++- web/packages/base/locales/hi-IN/translation.json | 14 +++++++++++++- web/packages/base/locales/hu-HU/translation.json | 14 +++++++++++++- web/packages/base/locales/id-ID/translation.json | 14 +++++++++++++- web/packages/base/locales/is-IS/translation.json | 14 +++++++++++++- web/packages/base/locales/it-IT/translation.json | 14 +++++++++++++- web/packages/base/locales/ja-JP/translation.json | 14 +++++++++++++- web/packages/base/locales/km-KH/translation.json | 14 +++++++++++++- web/packages/base/locales/ko-KR/translation.json | 14 +++++++++++++- web/packages/base/locales/lt-LT/translation.json | 14 +++++++++++++- web/packages/base/locales/lv-LV/translation.json | 14 +++++++++++++- web/packages/base/locales/ml-IN/translation.json | 14 +++++++++++++- web/packages/base/locales/ms-BN/translation.json | 14 +++++++++++++- web/packages/base/locales/ms-MY/translation.json | 14 +++++++++++++- web/packages/base/locales/nl-NL/translation.json | 14 +++++++++++++- web/packages/base/locales/pl-PL/translation.json | 14 +++++++++++++- web/packages/base/locales/pt-BR/translation.json | 14 +++++++++++++- web/packages/base/locales/pt-PT/translation.json | 14 +++++++++++++- web/packages/base/locales/ro-RO/translation.json | 14 +++++++++++++- web/packages/base/locales/ru-RU/translation.json | 14 +++++++++++++- web/packages/base/locales/sl-SI/translation.json | 14 +++++++++++++- web/packages/base/locales/sr-SP/translation.json | 14 +++++++++++++- web/packages/base/locales/sv-SE/translation.json | 14 +++++++++++++- web/packages/base/locales/ta-IN/translation.json | 14 +++++++++++++- web/packages/base/locales/te-IN/translation.json | 14 +++++++++++++- web/packages/base/locales/th-TH/translation.json | 14 +++++++++++++- web/packages/base/locales/ti-ER/translation.json | 14 +++++++++++++- web/packages/base/locales/tr-TR/translation.json | 14 +++++++++++++- web/packages/base/locales/uk-UA/translation.json | 14 +++++++++++++- web/packages/base/locales/vi-VN/translation.json | 14 +++++++++++++- web/packages/base/locales/zh-CN/translation.json | 14 +++++++++++++- web/packages/base/locales/zh-HK/translation.json | 14 +++++++++++++- 45 files changed, 586 insertions(+), 46 deletions(-) diff --git a/web/packages/base/locales/ar-SA/translation.json b/web/packages/base/locales/ar-SA/translation.json index 13e6a75b41..7c5ebcb013 100644 --- a/web/packages/base/locales/ar-SA/translation.json +++ b/web/packages/base/locales/ar-SA/translation.json @@ -685,5 +685,17 @@ "person_favorites": "مفضلات {{name}}", "shared_favorites": "", "added_by_name": "أضيفت بواسطة {{name}}", - "unowned_files_not_processed": "لم تتم معالجة الملفات المضافة من قبل مستخدمين آخرين" + "unowned_files_not_processed": "لم تتم معالجة الملفات المضافة من قبل مستخدمين آخرين", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/be-BY/translation.json b/web/packages/base/locales/be-BY/translation.json index 1b9971ec42..33cfe6db0a 100644 --- a/web/packages/base/locales/be-BY/translation.json +++ b/web/packages/base/locales/be-BY/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/bg-BG/translation.json b/web/packages/base/locales/bg-BG/translation.json index cc20675afc..d034887296 100644 --- a/web/packages/base/locales/bg-BG/translation.json +++ b/web/packages/base/locales/bg-BG/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/ca-ES/translation.json b/web/packages/base/locales/ca-ES/translation.json index 23a6018ed0..1030b4dea7 100644 --- a/web/packages/base/locales/ca-ES/translation.json +++ b/web/packages/base/locales/ca-ES/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/cs-CZ/translation.json b/web/packages/base/locales/cs-CZ/translation.json index 4e72fdb30e..98dfaf97e8 100644 --- a/web/packages/base/locales/cs-CZ/translation.json +++ b/web/packages/base/locales/cs-CZ/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/da-DK/translation.json b/web/packages/base/locales/da-DK/translation.json index a01d03f7b3..2f42cbbed3 100644 --- a/web/packages/base/locales/da-DK/translation.json +++ b/web/packages/base/locales/da-DK/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/de-DE/translation.json b/web/packages/base/locales/de-DE/translation.json index d38ba3147f..444eacde12 100644 --- a/web/packages/base/locales/de-DE/translation.json +++ b/web/packages/base/locales/de-DE/translation.json @@ -685,5 +685,17 @@ "person_favorites": "{{name}}s Favoriten", "shared_favorites": "Geteilte Favoriten", "added_by_name": "Von {{name}} hinzugefügt", - "unowned_files_not_processed": "Von anderen Benutzern hinzugefügte Dateien wurden nicht verarbeitet" + "unowned_files_not_processed": "Von anderen Benutzern hinzugefügte Dateien wurden nicht verarbeitet", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/el-GR/translation.json b/web/packages/base/locales/el-GR/translation.json index 72393365df..d2ad2cf782 100644 --- a/web/packages/base/locales/el-GR/translation.json +++ b/web/packages/base/locales/el-GR/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/es-ES/translation.json b/web/packages/base/locales/es-ES/translation.json index 1d3d91cf93..15fd1bfbd9 100644 --- a/web/packages/base/locales/es-ES/translation.json +++ b/web/packages/base/locales/es-ES/translation.json @@ -685,5 +685,17 @@ "person_favorites": "Los favoritos de {{name}}", "shared_favorites": "Favoritos compartidos", "added_by_name": "Añadido por {{name}}", - "unowned_files_not_processed": "Los archivos añadidos por otros usuarios no han sido procesados" + "unowned_files_not_processed": "Los archivos añadidos por otros usuarios no han sido procesados", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/et-EE/translation.json b/web/packages/base/locales/et-EE/translation.json index 23a6018ed0..1030b4dea7 100644 --- a/web/packages/base/locales/et-EE/translation.json +++ b/web/packages/base/locales/et-EE/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/fa-IR/translation.json b/web/packages/base/locales/fa-IR/translation.json index b1956bcf24..c626e82841 100644 --- a/web/packages/base/locales/fa-IR/translation.json +++ b/web/packages/base/locales/fa-IR/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/fi-FI/translation.json b/web/packages/base/locales/fi-FI/translation.json index 70a7dc4a9b..23b9a09ff6 100644 --- a/web/packages/base/locales/fi-FI/translation.json +++ b/web/packages/base/locales/fi-FI/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/fr-FR/translation.json b/web/packages/base/locales/fr-FR/translation.json index 564cd4528b..49522f9ec7 100644 --- a/web/packages/base/locales/fr-FR/translation.json +++ b/web/packages/base/locales/fr-FR/translation.json @@ -162,7 +162,7 @@ "ok": "Ok", "success": "Parfait", "error": "Erreur", - "note": "", + "note": "Note", "offline_message": "Vous êtes hors ligne, les souvenirs en cache sont affichés", "install": "Installer", "install_mobile_app": "Installez notre application Android or iOS pour sauvegarder automatiquement toutes vos photos", @@ -685,5 +685,17 @@ "person_favorites": "Favoris de {{name}}", "shared_favorites": "Favoris partagés", "added_by_name": "Ajouté par {{name}}", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "Les fichiers ajoutés par d'autres utilisateurs n'ont pas été traités", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/gu-IN/translation.json b/web/packages/base/locales/gu-IN/translation.json index 23a6018ed0..1030b4dea7 100644 --- a/web/packages/base/locales/gu-IN/translation.json +++ b/web/packages/base/locales/gu-IN/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/hi-IN/translation.json b/web/packages/base/locales/hi-IN/translation.json index 23a6018ed0..1030b4dea7 100644 --- a/web/packages/base/locales/hi-IN/translation.json +++ b/web/packages/base/locales/hi-IN/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/hu-HU/translation.json b/web/packages/base/locales/hu-HU/translation.json index 43a3f09dc5..28cff66abb 100644 --- a/web/packages/base/locales/hu-HU/translation.json +++ b/web/packages/base/locales/hu-HU/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/id-ID/translation.json b/web/packages/base/locales/id-ID/translation.json index a6338d7cc7..b174f2f2ea 100644 --- a/web/packages/base/locales/id-ID/translation.json +++ b/web/packages/base/locales/id-ID/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/is-IS/translation.json b/web/packages/base/locales/is-IS/translation.json index 1eb86e3776..c1330d2ebc 100644 --- a/web/packages/base/locales/is-IS/translation.json +++ b/web/packages/base/locales/is-IS/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/it-IT/translation.json b/web/packages/base/locales/it-IT/translation.json index 0dbe77e990..0d537b7e25 100644 --- a/web/packages/base/locales/it-IT/translation.json +++ b/web/packages/base/locales/it-IT/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/ja-JP/translation.json b/web/packages/base/locales/ja-JP/translation.json index c99792db1b..420e96c069 100644 --- a/web/packages/base/locales/ja-JP/translation.json +++ b/web/packages/base/locales/ja-JP/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/km-KH/translation.json b/web/packages/base/locales/km-KH/translation.json index 23a6018ed0..1030b4dea7 100644 --- a/web/packages/base/locales/km-KH/translation.json +++ b/web/packages/base/locales/km-KH/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/ko-KR/translation.json b/web/packages/base/locales/ko-KR/translation.json index c149854bde..fb420c812a 100644 --- a/web/packages/base/locales/ko-KR/translation.json +++ b/web/packages/base/locales/ko-KR/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/lt-LT/translation.json b/web/packages/base/locales/lt-LT/translation.json index b26aebb96d..924af32e1e 100644 --- a/web/packages/base/locales/lt-LT/translation.json +++ b/web/packages/base/locales/lt-LT/translation.json @@ -685,5 +685,17 @@ "person_favorites": "{{name}} mėgstami", "shared_favorites": "Bendrinami mėgstami", "added_by_name": "Įtraukė {{name}}", - "unowned_files_not_processed": "Kiti naudotojai pridėti failai nebuvo apdoroti" + "unowned_files_not_processed": "Kiti naudotojai pridėti failai nebuvo apdoroti", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/lv-LV/translation.json b/web/packages/base/locales/lv-LV/translation.json index 23a6018ed0..1030b4dea7 100644 --- a/web/packages/base/locales/lv-LV/translation.json +++ b/web/packages/base/locales/lv-LV/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/ml-IN/translation.json b/web/packages/base/locales/ml-IN/translation.json index 23a6018ed0..1030b4dea7 100644 --- a/web/packages/base/locales/ml-IN/translation.json +++ b/web/packages/base/locales/ml-IN/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/ms-BN/translation.json b/web/packages/base/locales/ms-BN/translation.json index 23a6018ed0..1030b4dea7 100644 --- a/web/packages/base/locales/ms-BN/translation.json +++ b/web/packages/base/locales/ms-BN/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/ms-MY/translation.json b/web/packages/base/locales/ms-MY/translation.json index 23a6018ed0..1030b4dea7 100644 --- a/web/packages/base/locales/ms-MY/translation.json +++ b/web/packages/base/locales/ms-MY/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/nl-NL/translation.json b/web/packages/base/locales/nl-NL/translation.json index 5f93685250..44b4f47d3f 100644 --- a/web/packages/base/locales/nl-NL/translation.json +++ b/web/packages/base/locales/nl-NL/translation.json @@ -685,5 +685,17 @@ "person_favorites": "Favorieten van {{name}}", "shared_favorites": "Gedeelde favorieten", "added_by_name": "Toegevoegd door {{name}}", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/pl-PL/translation.json b/web/packages/base/locales/pl-PL/translation.json index 064add62dc..bdae07f1a1 100644 --- a/web/packages/base/locales/pl-PL/translation.json +++ b/web/packages/base/locales/pl-PL/translation.json @@ -685,5 +685,17 @@ "person_favorites": "Ulubione użytkownika {{name}}", "shared_favorites": "Udostępnione ulubione", "added_by_name": "Dodane przez {{name}}", - "unowned_files_not_processed": "Pliki dodane przez innych użytkowników nie były przetwarzane" + "unowned_files_not_processed": "Pliki dodane przez innych użytkowników nie były przetwarzane", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/pt-BR/translation.json b/web/packages/base/locales/pt-BR/translation.json index ffb09b5b4a..9852b1f35d 100644 --- a/web/packages/base/locales/pt-BR/translation.json +++ b/web/packages/base/locales/pt-BR/translation.json @@ -685,5 +685,17 @@ "person_favorites": "Favoritos de {{name}}", "shared_favorites": "Favoritos compartilhados", "added_by_name": "Adicionado por {{name}}", - "unowned_files_not_processed": "Não processou os arquivos adicionados por outros usuários" + "unowned_files_not_processed": "Não processou os arquivos adicionados por outros usuários", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/pt-PT/translation.json b/web/packages/base/locales/pt-PT/translation.json index 4a85e90c62..446f8319fe 100644 --- a/web/packages/base/locales/pt-PT/translation.json +++ b/web/packages/base/locales/pt-PT/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/ro-RO/translation.json b/web/packages/base/locales/ro-RO/translation.json index 23a6018ed0..1030b4dea7 100644 --- a/web/packages/base/locales/ro-RO/translation.json +++ b/web/packages/base/locales/ro-RO/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/ru-RU/translation.json b/web/packages/base/locales/ru-RU/translation.json index 39401daefc..7218c486f7 100644 --- a/web/packages/base/locales/ru-RU/translation.json +++ b/web/packages/base/locales/ru-RU/translation.json @@ -685,5 +685,17 @@ "person_favorites": "{{name}} избранных", "shared_favorites": "Общие избранные", "added_by_name": "Добавлено {{name}}", - "unowned_files_not_processed": "Файлы, добавленные другими пользователями, не были обработаны" + "unowned_files_not_processed": "Файлы, добавленные другими пользователями, не были обработаны", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/sl-SI/translation.json b/web/packages/base/locales/sl-SI/translation.json index 23a6018ed0..1030b4dea7 100644 --- a/web/packages/base/locales/sl-SI/translation.json +++ b/web/packages/base/locales/sl-SI/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/sr-SP/translation.json b/web/packages/base/locales/sr-SP/translation.json index 23a6018ed0..1030b4dea7 100644 --- a/web/packages/base/locales/sr-SP/translation.json +++ b/web/packages/base/locales/sr-SP/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/sv-SE/translation.json b/web/packages/base/locales/sv-SE/translation.json index 3380397e3e..a8fde78805 100644 --- a/web/packages/base/locales/sv-SE/translation.json +++ b/web/packages/base/locales/sv-SE/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/ta-IN/translation.json b/web/packages/base/locales/ta-IN/translation.json index 23a6018ed0..1030b4dea7 100644 --- a/web/packages/base/locales/ta-IN/translation.json +++ b/web/packages/base/locales/ta-IN/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/te-IN/translation.json b/web/packages/base/locales/te-IN/translation.json index 23a6018ed0..1030b4dea7 100644 --- a/web/packages/base/locales/te-IN/translation.json +++ b/web/packages/base/locales/te-IN/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/th-TH/translation.json b/web/packages/base/locales/th-TH/translation.json index 23a6018ed0..1030b4dea7 100644 --- a/web/packages/base/locales/th-TH/translation.json +++ b/web/packages/base/locales/th-TH/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/ti-ER/translation.json b/web/packages/base/locales/ti-ER/translation.json index 23a6018ed0..1030b4dea7 100644 --- a/web/packages/base/locales/ti-ER/translation.json +++ b/web/packages/base/locales/ti-ER/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/tr-TR/translation.json b/web/packages/base/locales/tr-TR/translation.json index 627ac8aa91..c957b3906d 100644 --- a/web/packages/base/locales/tr-TR/translation.json +++ b/web/packages/base/locales/tr-TR/translation.json @@ -685,5 +685,17 @@ "person_favorites": "{{name}}'in favorileri", "shared_favorites": "Paylaşılan favoriler", "added_by_name": "{{name}} tarafından eklendi", - "unowned_files_not_processed": "Diğer kullanıcılar tarafından eklenen dosyalar işlenmedi" + "unowned_files_not_processed": "Diğer kullanıcılar tarafından eklenen dosyalar işlenmedi", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/uk-UA/translation.json b/web/packages/base/locales/uk-UA/translation.json index cc7ffca77a..ab3b64fb34 100644 --- a/web/packages/base/locales/uk-UA/translation.json +++ b/web/packages/base/locales/uk-UA/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/vi-VN/translation.json b/web/packages/base/locales/vi-VN/translation.json index fdd39566fb..6cf4877fd5 100644 --- a/web/packages/base/locales/vi-VN/translation.json +++ b/web/packages/base/locales/vi-VN/translation.json @@ -685,5 +685,17 @@ "person_favorites": "{{name}} đã thích", "shared_favorites": "Những mục thích đã chia sẻ", "added_by_name": "Được thêm bởi {{name}}", - "unowned_files_not_processed": "Các tệp được thêm bởi người dùng khác không được xử lý" + "unowned_files_not_processed": "Các tệp được thêm bởi người dùng khác không được xử lý", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/zh-CN/translation.json b/web/packages/base/locales/zh-CN/translation.json index 0bfb3cce8e..655aeae950 100644 --- a/web/packages/base/locales/zh-CN/translation.json +++ b/web/packages/base/locales/zh-CN/translation.json @@ -685,5 +685,17 @@ "person_favorites": "{{name}}的收藏", "shared_favorites": "已共享的收藏", "added_by_name": "由{{name}}添加", - "unowned_files_not_processed": "由其他用户添加的文件未被处理" + "unowned_files_not_processed": "由其他用户添加的文件未被处理", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } diff --git a/web/packages/base/locales/zh-HK/translation.json b/web/packages/base/locales/zh-HK/translation.json index bdfed3f70f..bd35a6b091 100644 --- a/web/packages/base/locales/zh-HK/translation.json +++ b/web/packages/base/locales/zh-HK/translation.json @@ -685,5 +685,17 @@ "person_favorites": "", "shared_favorites": "", "added_by_name": "", - "unowned_files_not_processed": "" + "unowned_files_not_processed": "", + "custom_domains": "", + "custom_domains_desc": "", + "link_your_domain": "", + "domain": "", + "domain_help": "", + "invalid_domain": "", + "already_linked_domain": "", + "add_dns_entry": "", + "add_dns_entry_hint": "", + "custom_domains_help": "", + "num_1": "", + "num_2": "" } From 64dde88b636a8295f7e7a071750f338118a424e7 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 14 Aug 2025 13:05:09 +0530 Subject: [PATCH 148/156] Delete rust_builder for PR issue --- mobile/apps/photos/rust_builder/.gitignore | 29 -- mobile/apps/photos/rust_builder/README.md | 1 - .../photos/rust_builder/android/.gitignore | 9 - .../photos/rust_builder/android/build.gradle | 56 --- .../rust_builder/android/settings.gradle | 1 - .../android/src/main/AndroidManifest.xml | 3 - .../photos/rust_builder/cargokit/.gitignore | 4 - .../apps/photos/rust_builder/cargokit/LICENSE | 42 -- .../apps/photos/rust_builder/cargokit/README | 11 - .../photos/rust_builder/cargokit/build_pod.sh | 58 --- .../cargokit/build_tool/README.md | 5 - .../cargokit/build_tool/analysis_options.yaml | 34 -- .../cargokit/build_tool/bin/build_tool.dart | 8 - .../cargokit/build_tool/lib/build_tool.dart | 8 - .../lib/src/android_environment.dart | 195 -------- .../lib/src/artifacts_provider.dart | 266 ---------- .../build_tool/lib/src/build_cmake.dart | 40 -- .../build_tool/lib/src/build_gradle.dart | 49 -- .../build_tool/lib/src/build_pod.dart | 89 ---- .../build_tool/lib/src/build_tool.dart | 271 ----------- .../cargokit/build_tool/lib/src/builder.dart | 198 -------- .../cargokit/build_tool/lib/src/cargo.dart | 48 -- .../build_tool/lib/src/crate_hash.dart | 124 ----- .../build_tool/lib/src/environment.dart | 68 --- .../cargokit/build_tool/lib/src/logging.dart | 52 -- .../cargokit/build_tool/lib/src/options.dart | 309 ------------ .../lib/src/precompile_binaries.dart | 202 -------- .../cargokit/build_tool/lib/src/rustup.dart | 136 ------ .../cargokit/build_tool/lib/src/target.dart | 140 ------ .../cargokit/build_tool/lib/src/util.dart | 172 ------- .../build_tool/lib/src/verify_binaries.dart | 84 ---- .../cargokit/build_tool/pubspec.lock | 453 ------------------ .../cargokit/build_tool/pubspec.yaml | 33 -- .../cargokit/cmake/cargokit.cmake | 99 ---- .../cargokit/cmake/resolve_symlinks.ps1 | 27 -- .../cargokit/gradle/plugin.gradle | 179 ------- .../rust_builder/cargokit/run_build_tool.cmd | 91 ---- .../rust_builder/cargokit/run_build_tool.sh | 94 ---- .../rust_builder/ios/Classes/dummy_file.c | 1 - .../rust_builder/ios/rust_lib_photos.podspec | 45 -- .../photos/rust_builder/linux/CMakeLists.txt | 19 - .../rust_builder/macos/Classes/dummy_file.c | 1 - .../macos/rust_lib_photos.podspec | 44 -- mobile/apps/photos/rust_builder/pubspec.yaml | 34 -- .../photos/rust_builder/windows/.gitignore | 17 - .../rust_builder/windows/CMakeLists.txt | 20 - 46 files changed, 3869 deletions(-) delete mode 100644 mobile/apps/photos/rust_builder/.gitignore delete mode 100644 mobile/apps/photos/rust_builder/README.md delete mode 100644 mobile/apps/photos/rust_builder/android/.gitignore delete mode 100644 mobile/apps/photos/rust_builder/android/build.gradle delete mode 100644 mobile/apps/photos/rust_builder/android/settings.gradle delete mode 100644 mobile/apps/photos/rust_builder/android/src/main/AndroidManifest.xml delete mode 100644 mobile/apps/photos/rust_builder/cargokit/.gitignore delete mode 100644 mobile/apps/photos/rust_builder/cargokit/LICENSE delete mode 100644 mobile/apps/photos/rust_builder/cargokit/README delete mode 100755 mobile/apps/photos/rust_builder/cargokit/build_pod.sh delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/README.md delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/analysis_options.yaml delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/bin/build_tool.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/build_tool.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/android_environment.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_pod.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_tool.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/builder.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/cargo.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/environment.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/logging.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/options.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/rustup.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/target.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/util.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.lock delete mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.yaml delete mode 100644 mobile/apps/photos/rust_builder/cargokit/cmake/cargokit.cmake delete mode 100644 mobile/apps/photos/rust_builder/cargokit/cmake/resolve_symlinks.ps1 delete mode 100644 mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle delete mode 100755 mobile/apps/photos/rust_builder/cargokit/run_build_tool.cmd delete mode 100755 mobile/apps/photos/rust_builder/cargokit/run_build_tool.sh delete mode 100644 mobile/apps/photos/rust_builder/ios/Classes/dummy_file.c delete mode 100644 mobile/apps/photos/rust_builder/ios/rust_lib_photos.podspec delete mode 100644 mobile/apps/photos/rust_builder/linux/CMakeLists.txt delete mode 100644 mobile/apps/photos/rust_builder/macos/Classes/dummy_file.c delete mode 100644 mobile/apps/photos/rust_builder/macos/rust_lib_photos.podspec delete mode 100644 mobile/apps/photos/rust_builder/pubspec.yaml delete mode 100644 mobile/apps/photos/rust_builder/windows/.gitignore delete mode 100644 mobile/apps/photos/rust_builder/windows/CMakeLists.txt diff --git a/mobile/apps/photos/rust_builder/.gitignore b/mobile/apps/photos/rust_builder/.gitignore deleted file mode 100644 index ac5aa9893e..0000000000 --- a/mobile/apps/photos/rust_builder/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. -/pubspec.lock -**/doc/api/ -.dart_tool/ -build/ diff --git a/mobile/apps/photos/rust_builder/README.md b/mobile/apps/photos/rust_builder/README.md deleted file mode 100644 index 922615f9c1..0000000000 --- a/mobile/apps/photos/rust_builder/README.md +++ /dev/null @@ -1 +0,0 @@ -Please ignore this folder, which is just glue to build Rust with Flutter. \ No newline at end of file diff --git a/mobile/apps/photos/rust_builder/android/.gitignore b/mobile/apps/photos/rust_builder/android/.gitignore deleted file mode 100644 index 161bdcdaf8..0000000000 --- a/mobile/apps/photos/rust_builder/android/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/workspace.xml -/.idea/libraries -.DS_Store -/build -/captures -.cxx diff --git a/mobile/apps/photos/rust_builder/android/build.gradle b/mobile/apps/photos/rust_builder/android/build.gradle deleted file mode 100644 index 5239261a2e..0000000000 --- a/mobile/apps/photos/rust_builder/android/build.gradle +++ /dev/null @@ -1,56 +0,0 @@ -// The Android Gradle Plugin builds the native code with the Android NDK. - -group 'com.flutter_rust_bridge.rust_lib_photos' -version '1.0' - -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - // The Android Gradle Plugin knows how to build native code with the NDK. - classpath 'com.android.tools.build:gradle:7.3.0' - } -} - -rootProject.allprojects { - repositories { - google() - mavenCentral() - } -} - -apply plugin: 'com.android.library' - -android { - if (project.android.hasProperty("namespace")) { - namespace 'com.flutter_rust_bridge.rust_lib_photos' - } - - // Bumping the plugin compileSdkVersion requires all clients of this plugin - // to bump the version in their app. - compileSdkVersion 33 - - // Use the NDK version - // declared in /android/app/build.gradle file of the Flutter project. - // Replace it with a version number if this plugin requires a specfic NDK version. - // (e.g. ndkVersion "23.1.7779620") - ndkVersion android.ndkVersion - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - defaultConfig { - minSdkVersion 19 - } -} - -apply from: "../cargokit/gradle/plugin.gradle" -cargokit { - manifestDir = "../../rust" - libname = "rust_lib_photos" -} diff --git a/mobile/apps/photos/rust_builder/android/settings.gradle b/mobile/apps/photos/rust_builder/android/settings.gradle deleted file mode 100644 index 48e7a64ca7..0000000000 --- a/mobile/apps/photos/rust_builder/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'rust_lib_photos' diff --git a/mobile/apps/photos/rust_builder/android/src/main/AndroidManifest.xml b/mobile/apps/photos/rust_builder/android/src/main/AndroidManifest.xml deleted file mode 100644 index b80e4df687..0000000000 --- a/mobile/apps/photos/rust_builder/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - diff --git a/mobile/apps/photos/rust_builder/cargokit/.gitignore b/mobile/apps/photos/rust_builder/cargokit/.gitignore deleted file mode 100644 index cf7bb868c0..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -target -.dart_tool -*.iml -!pubspec.lock diff --git a/mobile/apps/photos/rust_builder/cargokit/LICENSE b/mobile/apps/photos/rust_builder/cargokit/LICENSE deleted file mode 100644 index d33a5fea52..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/LICENSE +++ /dev/null @@ -1,42 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -Copyright 2022 Matej Knopp - -================================================================================ - -MIT LICENSE - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS -OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -================================================================================ - -APACHE LICENSE, VERSION 2.0 - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - diff --git a/mobile/apps/photos/rust_builder/cargokit/README b/mobile/apps/photos/rust_builder/cargokit/README deleted file mode 100644 index 398474dbc8..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/README +++ /dev/null @@ -1,11 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -Experimental repository to provide glue for seamlessly integrating cargo build -with flutter plugins and packages. - -See https://matejknopp.com/post/flutter_plugin_in_rust_with_no_prebuilt_binaries/ -for a tutorial on how to use Cargokit. - -Example plugin available at https://github.com/irondash/hello_rust_ffi_plugin. - diff --git a/mobile/apps/photos/rust_builder/cargokit/build_pod.sh b/mobile/apps/photos/rust_builder/cargokit/build_pod.sh deleted file mode 100755 index ed0e0d987d..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_pod.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/sh -set -e - -BASEDIR=$(dirname "$0") - -# Workaround for https://github.com/dart-lang/pub/issues/4010 -BASEDIR=$(cd "$BASEDIR" ; pwd -P) - -# Remove XCode SDK from path. Otherwise this breaks tool compilation when building iOS project -NEW_PATH=`echo $PATH | tr ":" "\n" | grep -v "Contents/Developer/" | tr "\n" ":"` - -export PATH=${NEW_PATH%?} # remove trailing : - -env - -# Platform name (macosx, iphoneos, iphonesimulator) -export CARGOKIT_DARWIN_PLATFORM_NAME=$PLATFORM_NAME - -# Arctive architectures (arm64, armv7, x86_64), space separated. -export CARGOKIT_DARWIN_ARCHS=$ARCHS - -# Current build configuration (Debug, Release) -export CARGOKIT_CONFIGURATION=$CONFIGURATION - -# Path to directory containing Cargo.toml. -export CARGOKIT_MANIFEST_DIR=$PODS_TARGET_SRCROOT/$1 - -# Temporary directory for build artifacts. -export CARGOKIT_TARGET_TEMP_DIR=$TARGET_TEMP_DIR - -# Output directory for final artifacts. -export CARGOKIT_OUTPUT_DIR=$PODS_CONFIGURATION_BUILD_DIR/$PRODUCT_NAME - -# Directory to store built tool artifacts. -export CARGOKIT_TOOL_TEMP_DIR=$TARGET_TEMP_DIR/build_tool - -# Directory inside root project. Not necessarily the top level directory of root project. -export CARGOKIT_ROOT_PROJECT_DIR=$SRCROOT - -FLUTTER_EXPORT_BUILD_ENVIRONMENT=( - "$PODS_ROOT/../Flutter/ephemeral/flutter_export_environment.sh" # macOS - "$PODS_ROOT/../Flutter/flutter_export_environment.sh" # iOS -) - -for path in "${FLUTTER_EXPORT_BUILD_ENVIRONMENT[@]}" -do - if [[ -f "$path" ]]; then - source "$path" - fi -done - -sh "$BASEDIR/run_build_tool.sh" build-pod "$@" - -# Make a symlink from built framework to phony file, which will be used as input to -# build script. This should force rebuild (podspec currently doesn't support alwaysOutOfDate -# attribute on custom build phase) -ln -fs "$OBJROOT/XCBuildData/build.db" "${BUILT_PRODUCTS_DIR}/cargokit_phony" -ln -fs "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}" "${BUILT_PRODUCTS_DIR}/cargokit_phony_out" diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/README.md b/mobile/apps/photos/rust_builder/cargokit/build_tool/README.md deleted file mode 100644 index a878c27964..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/README.md +++ /dev/null @@ -1,5 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -A sample command-line application with an entrypoint in `bin/`, library code -in `lib/`, and example unit test in `test/`. diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/analysis_options.yaml b/mobile/apps/photos/rust_builder/cargokit/build_tool/analysis_options.yaml deleted file mode 100644 index 0e16a8b092..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/analysis_options.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# This is copied from Cargokit (which is the official way to use it currently) -# Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -# This file configures the static analysis results for your project (errors, -# warnings, and lints). -# -# This enables the 'recommended' set of lints from `package:lints`. -# This set helps identify many issues that may lead to problems when running -# or consuming Dart code, and enforces writing Dart using a single, idiomatic -# style and format. -# -# If you want a smaller set of lints you can change this to specify -# 'package:lints/core.yaml'. These are just the most critical lints -# (the recommended set includes the core lints). -# The core lints are also what is used by pub.dev for scoring packages. - -include: package:lints/recommended.yaml - -# Uncomment the following section to specify additional rules. - -linter: - rules: - - prefer_relative_imports - - directives_ordering - -# analyzer: -# exclude: -# - path/to/excluded/files/** - -# For more information about the core and recommended set of lints, see -# https://dart.dev/go/core-lints - -# For additional information about configuring this file, see -# https://dart.dev/guides/language/analysis-options diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/bin/build_tool.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/bin/build_tool.dart deleted file mode 100644 index 268eb524dc..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/bin/build_tool.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'package:build_tool/build_tool.dart' as build_tool; - -void main(List arguments) { - build_tool.runMain(arguments); -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/build_tool.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/build_tool.dart deleted file mode 100644 index 7c1bb750a4..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/build_tool.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'src/build_tool.dart' as build_tool; - -Future runMain(List args) async { - return build_tool.runMain(args); -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/android_environment.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/android_environment.dart deleted file mode 100644 index 15fc9eedac..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/android_environment.dart +++ /dev/null @@ -1,195 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'dart:io'; -import 'dart:isolate'; -import 'dart:math' as math; - -import 'package:collection/collection.dart'; -import 'package:path/path.dart' as path; -import 'package:version/version.dart'; - -import 'target.dart'; -import 'util.dart'; - -class AndroidEnvironment { - AndroidEnvironment({ - required this.sdkPath, - required this.ndkVersion, - required this.minSdkVersion, - required this.targetTempDir, - required this.target, - }); - - static void clangLinkerWrapper(List args) { - final clang = Platform.environment['_CARGOKIT_NDK_LINK_CLANG']; - if (clang == null) { - throw Exception( - "cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_CLANG env var"); - } - final target = Platform.environment['_CARGOKIT_NDK_LINK_TARGET']; - if (target == null) { - throw Exception( - "cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_TARGET env var"); - } - - runCommand(clang, [ - target, - ...args, - ]); - } - - /// Full path to Android SDK. - final String sdkPath; - - /// Full version of Android NDK. - final String ndkVersion; - - /// Minimum supported SDK version. - final int minSdkVersion; - - /// Target directory for build artifacts. - final String targetTempDir; - - /// Target being built. - final Target target; - - bool ndkIsInstalled() { - final ndkPath = path.join(sdkPath, 'ndk', ndkVersion); - final ndkPackageXml = File(path.join(ndkPath, 'package.xml')); - return ndkPackageXml.existsSync(); - } - - void installNdk({ - required String javaHome, - }) { - final sdkManagerExtension = Platform.isWindows ? '.bat' : ''; - final sdkManager = path.join( - sdkPath, - 'cmdline-tools', - 'latest', - 'bin', - 'sdkmanager$sdkManagerExtension', - ); - - log.info('Installing NDK $ndkVersion'); - runCommand(sdkManager, [ - '--install', - 'ndk;$ndkVersion', - ], environment: { - 'JAVA_HOME': javaHome, - }); - } - - Future> buildEnvironment() async { - final hostArch = Platform.isMacOS - ? "darwin-x86_64" - : (Platform.isLinux ? "linux-x86_64" : "windows-x86_64"); - - final ndkPath = path.join(sdkPath, 'ndk', ndkVersion); - final toolchainPath = path.join( - ndkPath, - 'toolchains', - 'llvm', - 'prebuilt', - hostArch, - 'bin', - ); - - final minSdkVersion = - math.max(target.androidMinSdkVersion!, this.minSdkVersion); - - final exe = Platform.isWindows ? '.exe' : ''; - - final arKey = 'AR_${target.rust}'; - final arValue = ['${target.rust}-ar', 'llvm-ar', 'llvm-ar.exe'] - .map((e) => path.join(toolchainPath, e)) - .firstWhereOrNull((element) => File(element).existsSync()); - if (arValue == null) { - throw Exception('Failed to find ar for $target in $toolchainPath'); - } - - final targetArg = '--target=${target.rust}$minSdkVersion'; - - final ccKey = 'CC_${target.rust}'; - final ccValue = path.join(toolchainPath, 'clang$exe'); - final cfFlagsKey = 'CFLAGS_${target.rust}'; - final cFlagsValue = targetArg; - - final cxxKey = 'CXX_${target.rust}'; - final cxxValue = path.join(toolchainPath, 'clang++$exe'); - final cxxFlagsKey = 'CXXFLAGS_${target.rust}'; - final cxxFlagsValue = targetArg; - - final linkerKey = - 'cargo_target_${target.rust.replaceAll('-', '_')}_linker'.toUpperCase(); - - final ranlibKey = 'RANLIB_${target.rust}'; - final ranlibValue = path.join(toolchainPath, 'llvm-ranlib$exe'); - - final ndkVersionParsed = Version.parse(ndkVersion); - final rustFlagsKey = 'CARGO_ENCODED_RUSTFLAGS'; - final rustFlagsValue = _libGccWorkaround(targetTempDir, ndkVersionParsed); - - final runRustTool = - Platform.isWindows ? 'run_build_tool.cmd' : 'run_build_tool.sh'; - - final packagePath = (await Isolate.resolvePackageUri( - Uri.parse('package:build_tool/buildtool.dart')))! - .toFilePath(); - final selfPath = path.canonicalize(path.join( - packagePath, - '..', - '..', - '..', - runRustTool, - )); - - // Make sure that run_build_tool is working properly even initially launched directly - // through dart run. - final toolTempDir = - Platform.environment['CARGOKIT_TOOL_TEMP_DIR'] ?? targetTempDir; - - return { - arKey: arValue, - ccKey: ccValue, - cfFlagsKey: cFlagsValue, - cxxKey: cxxValue, - cxxFlagsKey: cxxFlagsValue, - ranlibKey: ranlibValue, - rustFlagsKey: rustFlagsValue, - linkerKey: selfPath, - // Recognized by main() so we know when we're acting as a wrapper - '_CARGOKIT_NDK_LINK_TARGET': targetArg, - '_CARGOKIT_NDK_LINK_CLANG': ccValue, - 'CARGOKIT_TOOL_TEMP_DIR': toolTempDir, - }; - } - - // Workaround for libgcc missing in NDK23, inspired by cargo-ndk - String _libGccWorkaround(String buildDir, Version ndkVersion) { - final workaroundDir = path.join( - buildDir, - 'cargokit', - 'libgcc_workaround', - '${ndkVersion.major}', - ); - Directory(workaroundDir).createSync(recursive: true); - if (ndkVersion.major >= 23) { - File(path.join(workaroundDir, 'libgcc.a')) - .writeAsStringSync('INPUT(-lunwind)'); - } else { - // Other way around, untested, forward libgcc.a from libunwind once Rust - // gets updated for NDK23+. - File(path.join(workaroundDir, 'libunwind.a')) - .writeAsStringSync('INPUT(-lgcc)'); - } - - var rustFlags = Platform.environment['CARGO_ENCODED_RUSTFLAGS'] ?? ''; - if (rustFlags.isNotEmpty) { - rustFlags = '$rustFlags\x1f'; - } - rustFlags = '$rustFlags-L\x1f$workaroundDir'; - return rustFlags; - } -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart deleted file mode 100644 index e608cece73..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart +++ /dev/null @@ -1,266 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'dart:io'; - -import 'package:ed25519_edwards/ed25519_edwards.dart'; -import 'package:http/http.dart'; -import 'package:logging/logging.dart'; -import 'package:path/path.dart' as path; - -import 'builder.dart'; -import 'crate_hash.dart'; -import 'options.dart'; -import 'precompile_binaries.dart'; -import 'rustup.dart'; -import 'target.dart'; - -class Artifact { - /// File system location of the artifact. - final String path; - - /// Actual file name that the artifact should have in destination folder. - final String finalFileName; - - AritifactType get type { - if (finalFileName.endsWith('.dll') || - finalFileName.endsWith('.dll.lib') || - finalFileName.endsWith('.pdb') || - finalFileName.endsWith('.so') || - finalFileName.endsWith('.dylib')) { - return AritifactType.dylib; - } else if (finalFileName.endsWith('.lib') || finalFileName.endsWith('.a')) { - return AritifactType.staticlib; - } else { - throw Exception('Unknown artifact type for $finalFileName'); - } - } - - Artifact({ - required this.path, - required this.finalFileName, - }); -} - -final _log = Logger('artifacts_provider'); - -class ArtifactProvider { - ArtifactProvider({ - required this.environment, - required this.userOptions, - }); - - final BuildEnvironment environment; - final CargokitUserOptions userOptions; - - Future>> getArtifacts(List targets) async { - final result = await _getPrecompiledArtifacts(targets); - - final pendingTargets = List.of(targets); - pendingTargets.removeWhere((element) => result.containsKey(element)); - - if (pendingTargets.isEmpty) { - return result; - } - - final rustup = Rustup(); - for (final target in targets) { - final builder = RustBuilder(target: target, environment: environment); - builder.prepare(rustup); - _log.info('Building ${environment.crateInfo.packageName} for $target'); - final targetDir = await builder.build(); - // For local build accept both static and dynamic libraries. - final artifactNames = { - ...getArtifactNames( - target: target, - libraryName: environment.crateInfo.packageName, - aritifactType: AritifactType.dylib, - remote: false, - ), - ...getArtifactNames( - target: target, - libraryName: environment.crateInfo.packageName, - aritifactType: AritifactType.staticlib, - remote: false, - ) - }; - final artifacts = artifactNames - .map((artifactName) => Artifact( - path: path.join(targetDir, artifactName), - finalFileName: artifactName, - )) - .where((element) => File(element.path).existsSync()) - .toList(); - result[target] = artifacts; - } - return result; - } - - Future>> _getPrecompiledArtifacts( - List targets) async { - if (userOptions.usePrecompiledBinaries == false) { - _log.info('Precompiled binaries are disabled'); - return {}; - } - if (environment.crateOptions.precompiledBinaries == null) { - _log.fine('Precompiled binaries not enabled for this crate'); - return {}; - } - - final start = Stopwatch()..start(); - final crateHash = CrateHash.compute(environment.manifestDir, - tempStorage: environment.targetTempDir); - _log.fine( - 'Computed crate hash $crateHash in ${start.elapsedMilliseconds}ms'); - - final downloadedArtifactsDir = - path.join(environment.targetTempDir, 'precompiled', crateHash); - Directory(downloadedArtifactsDir).createSync(recursive: true); - - final res = >{}; - - for (final target in targets) { - final requiredArtifacts = getArtifactNames( - target: target, - libraryName: environment.crateInfo.packageName, - remote: true, - ); - final artifactsForTarget = []; - - for (final artifact in requiredArtifacts) { - final fileName = PrecompileBinaries.fileName(target, artifact); - final downloadedPath = path.join(downloadedArtifactsDir, fileName); - if (!File(downloadedPath).existsSync()) { - final signatureFileName = - PrecompileBinaries.signatureFileName(target, artifact); - await _tryDownloadArtifacts( - crateHash: crateHash, - fileName: fileName, - signatureFileName: signatureFileName, - finalPath: downloadedPath, - ); - } - if (File(downloadedPath).existsSync()) { - artifactsForTarget.add(Artifact( - path: downloadedPath, - finalFileName: artifact, - )); - } else { - break; - } - } - - // Only provide complete set of artifacts. - if (artifactsForTarget.length == requiredArtifacts.length) { - _log.fine('Found precompiled artifacts for $target'); - res[target] = artifactsForTarget; - } - } - - return res; - } - - static Future _get(Uri url, {Map? headers}) async { - int attempt = 0; - const maxAttempts = 10; - while (true) { - try { - return await get(url, headers: headers); - } on SocketException catch (e) { - // Try to detect reset by peer error and retry. - if (attempt++ < maxAttempts && - (e.osError?.errorCode == 54 || e.osError?.errorCode == 10054)) { - _log.severe( - 'Failed to download $url: $e, attempt $attempt of $maxAttempts, will retry...'); - await Future.delayed(Duration(seconds: 1)); - continue; - } else { - rethrow; - } - } - } - } - - Future _tryDownloadArtifacts({ - required String crateHash, - required String fileName, - required String signatureFileName, - required String finalPath, - }) async { - final precompiledBinaries = environment.crateOptions.precompiledBinaries!; - final prefix = precompiledBinaries.uriPrefix; - final url = Uri.parse('$prefix$crateHash/$fileName'); - final signatureUrl = Uri.parse('$prefix$crateHash/$signatureFileName'); - _log.fine('Downloading signature from $signatureUrl'); - final signature = await _get(signatureUrl); - if (signature.statusCode == 404) { - _log.warning( - 'Precompiled binaries not available for crate hash $crateHash ($fileName)'); - return; - } - if (signature.statusCode != 200) { - _log.severe( - 'Failed to download signature $signatureUrl: status ${signature.statusCode}'); - return; - } - _log.fine('Downloading binary from $url'); - final res = await _get(url); - if (res.statusCode != 200) { - _log.severe('Failed to download binary $url: status ${res.statusCode}'); - return; - } - if (verify( - precompiledBinaries.publicKey, res.bodyBytes, signature.bodyBytes)) { - File(finalPath).writeAsBytesSync(res.bodyBytes); - } else { - _log.shout('Signature verification failed! Ignoring binary.'); - } - } -} - -enum AritifactType { - staticlib, - dylib, -} - -AritifactType artifactTypeForTarget(Target target) { - if (target.darwinPlatform != null) { - return AritifactType.staticlib; - } else { - return AritifactType.dylib; - } -} - -List getArtifactNames({ - required Target target, - required String libraryName, - required bool remote, - AritifactType? aritifactType, -}) { - aritifactType ??= artifactTypeForTarget(target); - if (target.darwinArch != null) { - if (aritifactType == AritifactType.staticlib) { - return ['lib$libraryName.a']; - } else { - return ['lib$libraryName.dylib']; - } - } else if (target.rust.contains('-windows-')) { - if (aritifactType == AritifactType.staticlib) { - return ['$libraryName.lib']; - } else { - return [ - '$libraryName.dll', - '$libraryName.dll.lib', - if (!remote) '$libraryName.pdb' - ]; - } - } else if (target.rust.contains('-linux-')) { - if (aritifactType == AritifactType.staticlib) { - return ['lib$libraryName.a']; - } else { - return ['lib$libraryName.so']; - } - } else { - throw Exception("Unsupported target: ${target.rust}"); - } -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart deleted file mode 100644 index 6f3b2a4ec1..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart +++ /dev/null @@ -1,40 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'dart:io'; - -import 'package:path/path.dart' as path; - -import 'artifacts_provider.dart'; -import 'builder.dart'; -import 'environment.dart'; -import 'options.dart'; -import 'target.dart'; - -class BuildCMake { - final CargokitUserOptions userOptions; - - BuildCMake({required this.userOptions}); - - Future build() async { - final targetPlatform = Environment.targetPlatform; - final target = Target.forFlutterName(Environment.targetPlatform); - if (target == null) { - throw Exception("Unknown target platform: $targetPlatform"); - } - - final environment = BuildEnvironment.fromEnvironment(isAndroid: false); - final provider = - ArtifactProvider(environment: environment, userOptions: userOptions); - final artifacts = await provider.getArtifacts([target]); - - final libs = artifacts[target]!; - - for (final lib in libs) { - if (lib.type == AritifactType.dylib) { - File(lib.path) - .copySync(path.join(Environment.outputDir, lib.finalFileName)); - } - } - } -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart deleted file mode 100644 index 7e61fcbb7c..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart +++ /dev/null @@ -1,49 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'dart:io'; - -import 'package:logging/logging.dart'; -import 'package:path/path.dart' as path; - -import 'artifacts_provider.dart'; -import 'builder.dart'; -import 'environment.dart'; -import 'options.dart'; -import 'target.dart'; - -final log = Logger('build_gradle'); - -class BuildGradle { - BuildGradle({required this.userOptions}); - - final CargokitUserOptions userOptions; - - Future build() async { - final targets = Environment.targetPlatforms.map((arch) { - final target = Target.forFlutterName(arch); - if (target == null) { - throw Exception( - "Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}"); - } - return target; - }).toList(); - - final environment = BuildEnvironment.fromEnvironment(isAndroid: true); - final provider = - ArtifactProvider(environment: environment, userOptions: userOptions); - final artifacts = await provider.getArtifacts(targets); - - for (final target in targets) { - final libs = artifacts[target]!; - final outputDir = path.join(Environment.outputDir, target.android!); - Directory(outputDir).createSync(recursive: true); - - for (final lib in libs) { - if (lib.type == AritifactType.dylib) { - File(lib.path).copySync(path.join(outputDir, lib.finalFileName)); - } - } - } - } -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_pod.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_pod.dart deleted file mode 100644 index 8a9c0db5de..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_pod.dart +++ /dev/null @@ -1,89 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'dart:io'; - -import 'package:path/path.dart' as path; - -import 'artifacts_provider.dart'; -import 'builder.dart'; -import 'environment.dart'; -import 'options.dart'; -import 'target.dart'; -import 'util.dart'; - -class BuildPod { - BuildPod({required this.userOptions}); - - final CargokitUserOptions userOptions; - - Future build() async { - final targets = Environment.darwinArchs.map((arch) { - final target = Target.forDarwin( - platformName: Environment.darwinPlatformName, darwinAarch: arch); - if (target == null) { - throw Exception( - "Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}"); - } - return target; - }).toList(); - - final environment = BuildEnvironment.fromEnvironment(isAndroid: false); - final provider = - ArtifactProvider(environment: environment, userOptions: userOptions); - final artifacts = await provider.getArtifacts(targets); - - void performLipo(String targetFile, Iterable sourceFiles) { - runCommand("lipo", [ - '-create', - ...sourceFiles, - '-output', - targetFile, - ]); - } - - final outputDir = Environment.outputDir; - - Directory(outputDir).createSync(recursive: true); - - final staticLibs = artifacts.values - .expand((element) => element) - .where((element) => element.type == AritifactType.staticlib) - .toList(); - final dynamicLibs = artifacts.values - .expand((element) => element) - .where((element) => element.type == AritifactType.dylib) - .toList(); - - final libName = environment.crateInfo.packageName; - - // If there is static lib, use it and link it with pod - if (staticLibs.isNotEmpty) { - final finalTargetFile = path.join(outputDir, "lib$libName.a"); - performLipo(finalTargetFile, staticLibs.map((e) => e.path)); - } else { - // Otherwise try to replace bundle dylib with our dylib - final bundlePaths = [ - '$libName.framework/Versions/A/$libName', - '$libName.framework/$libName', - ]; - - for (final bundlePath in bundlePaths) { - final targetFile = path.join(outputDir, bundlePath); - if (File(targetFile).existsSync()) { - performLipo(targetFile, dynamicLibs.map((e) => e.path)); - - // Replace absolute id with @rpath one so that it works properly - // when moved to Frameworks. - runCommand("install_name_tool", [ - '-id', - '@rpath/$bundlePath', - targetFile, - ]); - return; - } - } - throw Exception('Unable to find bundle for dynamic library'); - } - } -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_tool.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_tool.dart deleted file mode 100644 index c8f36981b5..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_tool.dart +++ /dev/null @@ -1,271 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'dart:io'; - -import 'package:args/command_runner.dart'; -import 'package:ed25519_edwards/ed25519_edwards.dart'; -import 'package:github/github.dart'; -import 'package:hex/hex.dart'; -import 'package:logging/logging.dart'; - -import 'android_environment.dart'; -import 'build_cmake.dart'; -import 'build_gradle.dart'; -import 'build_pod.dart'; -import 'logging.dart'; -import 'options.dart'; -import 'precompile_binaries.dart'; -import 'target.dart'; -import 'util.dart'; -import 'verify_binaries.dart'; - -final log = Logger('build_tool'); - -abstract class BuildCommand extends Command { - Future runBuildCommand(CargokitUserOptions options); - - @override - Future run() async { - final options = CargokitUserOptions.load(); - - if (options.verboseLogging || - Platform.environment['CARGOKIT_VERBOSE'] == '1') { - enableVerboseLogging(); - } - - await runBuildCommand(options); - } -} - -class BuildPodCommand extends BuildCommand { - @override - final name = 'build-pod'; - - @override - final description = 'Build cocoa pod library'; - - @override - Future runBuildCommand(CargokitUserOptions options) async { - final build = BuildPod(userOptions: options); - await build.build(); - } -} - -class BuildGradleCommand extends BuildCommand { - @override - final name = 'build-gradle'; - - @override - final description = 'Build android library'; - - @override - Future runBuildCommand(CargokitUserOptions options) async { - final build = BuildGradle(userOptions: options); - await build.build(); - } -} - -class BuildCMakeCommand extends BuildCommand { - @override - final name = 'build-cmake'; - - @override - final description = 'Build CMake library'; - - @override - Future runBuildCommand(CargokitUserOptions options) async { - final build = BuildCMake(userOptions: options); - await build.build(); - } -} - -class GenKeyCommand extends Command { - @override - final name = 'gen-key'; - - @override - final description = 'Generate key pair for signing precompiled binaries'; - - @override - void run() { - final kp = generateKey(); - final private = HEX.encode(kp.privateKey.bytes); - final public = HEX.encode(kp.publicKey.bytes); - print("Private Key: $private"); - print("Public Key: $public"); - } -} - -class PrecompileBinariesCommand extends Command { - PrecompileBinariesCommand() { - argParser - ..addOption( - 'repository', - mandatory: true, - help: 'Github repository slug in format owner/name', - ) - ..addOption( - 'manifest-dir', - mandatory: true, - help: 'Directory containing Cargo.toml', - ) - ..addMultiOption('target', - help: 'Rust target triple of artifact to build.\n' - 'Can be specified multiple times or omitted in which case\n' - 'all targets for current platform will be built.') - ..addOption( - 'android-sdk-location', - help: 'Location of Android SDK (if available)', - ) - ..addOption( - 'android-ndk-version', - help: 'Android NDK version (if available)', - ) - ..addOption( - 'android-min-sdk-version', - help: 'Android minimum rquired version (if available)', - ) - ..addOption( - 'temp-dir', - help: 'Directory to store temporary build artifacts', - ) - ..addFlag( - "verbose", - abbr: "v", - defaultsTo: false, - help: "Enable verbose logging", - ); - } - - @override - final name = 'precompile-binaries'; - - @override - final description = 'Prebuild and upload binaries\n' - 'Private key must be passed through PRIVATE_KEY environment variable. ' - 'Use gen_key through generate priave key.\n' - 'Github token must be passed as GITHUB_TOKEN environment variable.\n'; - - @override - Future run() async { - final verbose = argResults!['verbose'] as bool; - if (verbose) { - enableVerboseLogging(); - } - - final privateKeyString = Platform.environment['PRIVATE_KEY']; - if (privateKeyString == null) { - throw ArgumentError('Missing PRIVATE_KEY environment variable'); - } - final githubToken = Platform.environment['GITHUB_TOKEN']; - if (githubToken == null) { - throw ArgumentError('Missing GITHUB_TOKEN environment variable'); - } - final privateKey = HEX.decode(privateKeyString); - if (privateKey.length != 64) { - throw ArgumentError('Private key must be 64 bytes long'); - } - final manifestDir = argResults!['manifest-dir'] as String; - if (!Directory(manifestDir).existsSync()) { - throw ArgumentError('Manifest directory does not exist: $manifestDir'); - } - String? androidMinSdkVersionString = - argResults!['android-min-sdk-version'] as String?; - int? androidMinSdkVersion; - if (androidMinSdkVersionString != null) { - androidMinSdkVersion = int.tryParse(androidMinSdkVersionString); - if (androidMinSdkVersion == null) { - throw ArgumentError( - 'Invalid android-min-sdk-version: $androidMinSdkVersionString'); - } - } - final targetStrigns = argResults!['target'] as List; - final targets = targetStrigns.map((target) { - final res = Target.forRustTriple(target); - if (res == null) { - throw ArgumentError('Invalid target: $target'); - } - return res; - }).toList(growable: false); - final precompileBinaries = PrecompileBinaries( - privateKey: PrivateKey(privateKey), - githubToken: githubToken, - manifestDir: manifestDir, - repositorySlug: RepositorySlug.full(argResults!['repository'] as String), - targets: targets, - androidSdkLocation: argResults!['android-sdk-location'] as String?, - androidNdkVersion: argResults!['android-ndk-version'] as String?, - androidMinSdkVersion: androidMinSdkVersion, - tempDir: argResults!['temp-dir'] as String?, - ); - - await precompileBinaries.run(); - } -} - -class VerifyBinariesCommand extends Command { - VerifyBinariesCommand() { - argParser.addOption( - 'manifest-dir', - mandatory: true, - help: 'Directory containing Cargo.toml', - ); - } - - @override - final name = "verify-binaries"; - - @override - final description = 'Verifies published binaries\n' - 'Checks whether there is a binary published for each targets\n' - 'and checks the signature.'; - - @override - Future run() async { - final manifestDir = argResults!['manifest-dir'] as String; - final verifyBinaries = VerifyBinaries( - manifestDir: manifestDir, - ); - await verifyBinaries.run(); - } -} - -Future runMain(List args) async { - try { - // Init logging before options are loaded - initLogging(); - - if (Platform.environment['_CARGOKIT_NDK_LINK_TARGET'] != null) { - return AndroidEnvironment.clangLinkerWrapper(args); - } - - final runner = CommandRunner('build_tool', 'Cargokit built_tool') - ..addCommand(BuildPodCommand()) - ..addCommand(BuildGradleCommand()) - ..addCommand(BuildCMakeCommand()) - ..addCommand(GenKeyCommand()) - ..addCommand(PrecompileBinariesCommand()) - ..addCommand(VerifyBinariesCommand()); - - await runner.run(args); - } on ArgumentError catch (e) { - stderr.writeln(e.toString()); - exit(1); - } catch (e, s) { - log.severe(kDoubleSeparator); - log.severe('Cargokit BuildTool failed with error:'); - log.severe(kSeparator); - log.severe(e); - // This tells user to install Rust, there's no need to pollute the log with - // stack trace. - if (e is! RustupNotFoundException) { - log.severe(kSeparator); - log.severe(s); - log.severe(kSeparator); - log.severe('BuildTool arguments: $args'); - } - log.severe(kDoubleSeparator); - exit(1); - } -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/builder.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/builder.dart deleted file mode 100644 index 84c46e4f54..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/builder.dart +++ /dev/null @@ -1,198 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'package:collection/collection.dart'; -import 'package:logging/logging.dart'; -import 'package:path/path.dart' as path; - -import 'android_environment.dart'; -import 'cargo.dart'; -import 'environment.dart'; -import 'options.dart'; -import 'rustup.dart'; -import 'target.dart'; -import 'util.dart'; - -final _log = Logger('builder'); - -enum BuildConfiguration { - debug, - release, - profile, -} - -extension on BuildConfiguration { - bool get isDebug => this == BuildConfiguration.debug; - String get rustName => switch (this) { - BuildConfiguration.debug => 'debug', - BuildConfiguration.release => 'release', - BuildConfiguration.profile => 'release', - }; -} - -class BuildException implements Exception { - final String message; - - BuildException(this.message); - - @override - String toString() { - return 'BuildException: $message'; - } -} - -class BuildEnvironment { - final BuildConfiguration configuration; - final CargokitCrateOptions crateOptions; - final String targetTempDir; - final String manifestDir; - final CrateInfo crateInfo; - - final bool isAndroid; - final String? androidSdkPath; - final String? androidNdkVersion; - final int? androidMinSdkVersion; - final String? javaHome; - - BuildEnvironment({ - required this.configuration, - required this.crateOptions, - required this.targetTempDir, - required this.manifestDir, - required this.crateInfo, - required this.isAndroid, - this.androidSdkPath, - this.androidNdkVersion, - this.androidMinSdkVersion, - this.javaHome, - }); - - static BuildConfiguration parseBuildConfiguration(String value) { - // XCode configuration adds the flavor to configuration name. - final firstSegment = value.split('-').first; - final buildConfiguration = BuildConfiguration.values.firstWhereOrNull( - (e) => e.name == firstSegment, - ); - if (buildConfiguration == null) { - _log.warning('Unknown build configuraiton $value, will assume release'); - return BuildConfiguration.release; - } - return buildConfiguration; - } - - static BuildEnvironment fromEnvironment({ - required bool isAndroid, - }) { - final buildConfiguration = - parseBuildConfiguration(Environment.configuration); - final manifestDir = Environment.manifestDir; - final crateOptions = CargokitCrateOptions.load( - manifestDir: manifestDir, - ); - final crateInfo = CrateInfo.load(manifestDir); - return BuildEnvironment( - configuration: buildConfiguration, - crateOptions: crateOptions, - targetTempDir: Environment.targetTempDir, - manifestDir: manifestDir, - crateInfo: crateInfo, - isAndroid: isAndroid, - androidSdkPath: isAndroid ? Environment.sdkPath : null, - androidNdkVersion: isAndroid ? Environment.ndkVersion : null, - androidMinSdkVersion: - isAndroid ? int.parse(Environment.minSdkVersion) : null, - javaHome: isAndroid ? Environment.javaHome : null, - ); - } -} - -class RustBuilder { - final Target target; - final BuildEnvironment environment; - - RustBuilder({ - required this.target, - required this.environment, - }); - - void prepare( - Rustup rustup, - ) { - final toolchain = _toolchain; - if (rustup.installedTargets(toolchain) == null) { - rustup.installToolchain(toolchain); - } - if (toolchain == 'nightly') { - rustup.installRustSrcForNightly(); - } - if (!rustup.installedTargets(toolchain)!.contains(target.rust)) { - rustup.installTarget(target.rust, toolchain: toolchain); - } - } - - CargoBuildOptions? get _buildOptions => - environment.crateOptions.cargo[environment.configuration]; - - String get _toolchain => _buildOptions?.toolchain.name ?? 'stable'; - - /// Returns the path of directory containing build artifacts. - Future build() async { - final extraArgs = _buildOptions?.flags ?? []; - final manifestPath = path.join(environment.manifestDir, 'Cargo.toml'); - runCommand( - 'rustup', - [ - 'run', - _toolchain, - 'cargo', - 'build', - ...extraArgs, - '--manifest-path', - manifestPath, - '-p', - environment.crateInfo.packageName, - if (!environment.configuration.isDebug) '--release', - '--target', - target.rust, - '--target-dir', - environment.targetTempDir, - ], - environment: await _buildEnvironment(), - ); - return path.join( - environment.targetTempDir, - target.rust, - environment.configuration.rustName, - ); - } - - Future> _buildEnvironment() async { - if (target.android == null) { - return {}; - } else { - final sdkPath = environment.androidSdkPath; - final ndkVersion = environment.androidNdkVersion; - final minSdkVersion = environment.androidMinSdkVersion; - if (sdkPath == null) { - throw BuildException('androidSdkPath is not set'); - } - if (ndkVersion == null) { - throw BuildException('androidNdkVersion is not set'); - } - if (minSdkVersion == null) { - throw BuildException('androidMinSdkVersion is not set'); - } - final env = AndroidEnvironment( - sdkPath: sdkPath, - ndkVersion: ndkVersion, - minSdkVersion: minSdkVersion, - targetTempDir: environment.targetTempDir, - target: target, - ); - if (!env.ndkIsInstalled() && environment.javaHome != null) { - env.installNdk(javaHome: environment.javaHome!); - } - return env.buildEnvironment(); - } - } -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/cargo.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/cargo.dart deleted file mode 100644 index 0d8958ff2e..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/cargo.dart +++ /dev/null @@ -1,48 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'dart:io'; - -import 'package:path/path.dart' as path; -import 'package:toml/toml.dart'; - -class ManifestException { - ManifestException(this.message, {required this.fileName}); - - final String? fileName; - final String message; - - @override - String toString() { - if (fileName != null) { - return 'Failed to parse package manifest at $fileName: $message'; - } else { - return 'Failed to parse package manifest: $message'; - } - } -} - -class CrateInfo { - CrateInfo({required this.packageName}); - - final String packageName; - - static CrateInfo parseManifest(String manifest, {final String? fileName}) { - final toml = TomlDocument.parse(manifest); - final package = toml.toMap()['package']; - if (package == null) { - throw ManifestException('Missing package section', fileName: fileName); - } - final name = package['name']; - if (name == null) { - throw ManifestException('Missing package name', fileName: fileName); - } - return CrateInfo(packageName: name); - } - - static CrateInfo load(String manifestDir) { - final manifestFile = File(path.join(manifestDir, 'Cargo.toml')); - final manifest = manifestFile.readAsStringSync(); - return parseManifest(manifest, fileName: manifestFile.path); - } -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart deleted file mode 100644 index 0c4d88d16b..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart +++ /dev/null @@ -1,124 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'dart:convert'; -import 'dart:io'; -import 'dart:typed_data'; - -import 'package:collection/collection.dart'; -import 'package:convert/convert.dart'; -import 'package:crypto/crypto.dart'; -import 'package:path/path.dart' as path; - -class CrateHash { - /// Computes a hash uniquely identifying crate content. This takes into account - /// content all all .rs files inside the src directory, as well as Cargo.toml, - /// Cargo.lock, build.rs and cargokit.yaml. - /// - /// If [tempStorage] is provided, computed hash is stored in a file in that directory - /// and reused on subsequent calls if the crate content hasn't changed. - static String compute(String manifestDir, {String? tempStorage}) { - return CrateHash._( - manifestDir: manifestDir, - tempStorage: tempStorage, - )._compute(); - } - - CrateHash._({ - required this.manifestDir, - required this.tempStorage, - }); - - String _compute() { - final files = getFiles(); - final tempStorage = this.tempStorage; - if (tempStorage != null) { - final quickHash = _computeQuickHash(files); - final quickHashFolder = Directory(path.join(tempStorage, 'crate_hash')); - quickHashFolder.createSync(recursive: true); - final quickHashFile = File(path.join(quickHashFolder.path, quickHash)); - if (quickHashFile.existsSync()) { - return quickHashFile.readAsStringSync(); - } - final hash = _computeHash(files); - quickHashFile.writeAsStringSync(hash); - return hash; - } else { - return _computeHash(files); - } - } - - /// Computes a quick hash based on files stat (without reading contents). This - /// is used to cache the real hash, which is slower to compute since it involves - /// reading every single file. - String _computeQuickHash(List files) { - final output = AccumulatorSink(); - final input = sha256.startChunkedConversion(output); - - final data = ByteData(8); - for (final file in files) { - input.add(utf8.encode(file.path)); - final stat = file.statSync(); - data.setUint64(0, stat.size); - input.add(data.buffer.asUint8List()); - data.setUint64(0, stat.modified.millisecondsSinceEpoch); - input.add(data.buffer.asUint8List()); - } - - input.close(); - return base64Url.encode(output.events.single.bytes); - } - - String _computeHash(List files) { - final output = AccumulatorSink(); - final input = sha256.startChunkedConversion(output); - - void addTextFile(File file) { - // text Files are hashed by lines in case we're dealing with github checkout - // that auto-converts line endings. - final splitter = LineSplitter(); - if (file.existsSync()) { - final data = file.readAsStringSync(); - final lines = splitter.convert(data); - for (final line in lines) { - input.add(utf8.encode(line)); - } - } - } - - for (final file in files) { - addTextFile(file); - } - - input.close(); - final res = output.events.single; - - // Truncate to 128bits. - final hash = res.bytes.sublist(0, 16); - return hex.encode(hash); - } - - List getFiles() { - final src = Directory(path.join(manifestDir, 'src')); - final files = src - .listSync(recursive: true, followLinks: false) - .whereType() - .toList(); - files.sortBy((element) => element.path); - void addFile(String relative) { - final file = File(path.join(manifestDir, relative)); - if (file.existsSync()) { - files.add(file); - } - } - - addFile('Cargo.toml'); - addFile('Cargo.lock'); - addFile('build.rs'); - addFile('cargokit.yaml'); - return files; - } - - final String manifestDir; - final String? tempStorage; -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/environment.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/environment.dart deleted file mode 100644 index 996483a180..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/environment.dart +++ /dev/null @@ -1,68 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'dart:io'; - -extension on String { - String resolveSymlink() => File(this).resolveSymbolicLinksSync(); -} - -class Environment { - /// Current build configuration (debug or release). - static String get configuration => - _getEnv("CARGOKIT_CONFIGURATION").toLowerCase(); - - static bool get isDebug => configuration == 'debug'; - static bool get isRelease => configuration == 'release'; - - /// Temporary directory where Rust build artifacts are placed. - static String get targetTempDir => _getEnv("CARGOKIT_TARGET_TEMP_DIR"); - - /// Final output directory where the build artifacts are placed. - static String get outputDir => _getEnvPath('CARGOKIT_OUTPUT_DIR'); - - /// Path to the crate manifest (containing Cargo.toml). - static String get manifestDir => _getEnvPath('CARGOKIT_MANIFEST_DIR'); - - /// Directory inside root project. Not necessarily root folder. Symlinks are - /// not resolved on purpose. - static String get rootProjectDir => _getEnv('CARGOKIT_ROOT_PROJECT_DIR'); - - // Pod - - /// Platform name (macosx, iphoneos, iphonesimulator). - static String get darwinPlatformName => - _getEnv("CARGOKIT_DARWIN_PLATFORM_NAME"); - - /// List of architectures to build for (arm64, armv7, x86_64). - static List get darwinArchs => - _getEnv("CARGOKIT_DARWIN_ARCHS").split(' '); - - // Gradle - static String get minSdkVersion => _getEnv("CARGOKIT_MIN_SDK_VERSION"); - static String get ndkVersion => _getEnv("CARGOKIT_NDK_VERSION"); - static String get sdkPath => _getEnvPath("CARGOKIT_SDK_DIR"); - static String get javaHome => _getEnvPath("CARGOKIT_JAVA_HOME"); - static List get targetPlatforms => - _getEnv("CARGOKIT_TARGET_PLATFORMS").split(','); - - // CMAKE - static String get targetPlatform => _getEnv("CARGOKIT_TARGET_PLATFORM"); - - static String _getEnv(String key) { - final res = Platform.environment[key]; - if (res == null) { - throw Exception("Missing environment variable $key"); - } - return res; - } - - static String _getEnvPath(String key) { - final res = _getEnv(key); - if (Directory(res).existsSync()) { - return res.resolveSymlink(); - } else { - return res; - } - } -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/logging.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/logging.dart deleted file mode 100644 index 5edd4fd184..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/logging.dart +++ /dev/null @@ -1,52 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'dart:io'; - -import 'package:logging/logging.dart'; - -const String kSeparator = "--"; -const String kDoubleSeparator = "=="; - -bool _lastMessageWasSeparator = false; - -void _log(LogRecord rec) { - final prefix = '${rec.level.name}: '; - final out = rec.level == Level.SEVERE ? stderr : stdout; - if (rec.message == kSeparator) { - if (!_lastMessageWasSeparator) { - out.write(prefix); - out.writeln('-' * 80); - _lastMessageWasSeparator = true; - } - return; - } else if (rec.message == kDoubleSeparator) { - out.write(prefix); - out.writeln('=' * 80); - _lastMessageWasSeparator = true; - return; - } - out.write(prefix); - out.writeln(rec.message); - _lastMessageWasSeparator = false; -} - -void initLogging() { - Logger.root.level = Level.INFO; - Logger.root.onRecord.listen((LogRecord rec) { - final lines = rec.message.split('\n'); - for (final line in lines) { - if (line.isNotEmpty || lines.length == 1 || line != lines.last) { - _log(LogRecord( - rec.level, - line, - rec.loggerName, - )); - } - } - }); -} - -void enableVerboseLogging() { - Logger.root.level = Level.ALL; -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/options.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/options.dart deleted file mode 100644 index 22aef1d371..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/options.dart +++ /dev/null @@ -1,309 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'dart:io'; - -import 'package:collection/collection.dart'; -import 'package:ed25519_edwards/ed25519_edwards.dart'; -import 'package:hex/hex.dart'; -import 'package:logging/logging.dart'; -import 'package:path/path.dart' as path; -import 'package:source_span/source_span.dart'; -import 'package:yaml/yaml.dart'; - -import 'builder.dart'; -import 'environment.dart'; -import 'rustup.dart'; - -final _log = Logger('options'); - -/// A class for exceptions that have source span information attached. -class SourceSpanException implements Exception { - // This is a getter so that subclasses can override it. - /// A message describing the exception. - String get message => _message; - final String _message; - - // This is a getter so that subclasses can override it. - /// The span associated with this exception. - /// - /// This may be `null` if the source location can't be determined. - SourceSpan? get span => _span; - final SourceSpan? _span; - - SourceSpanException(this._message, this._span); - - /// Returns a string representation of `this`. - /// - /// [color] may either be a [String], a [bool], or `null`. If it's a string, - /// it indicates an ANSI terminal color escape that should be used to - /// highlight the span's text. If it's `true`, it indicates that the text - /// should be highlighted using the default color. If it's `false` or `null`, - /// it indicates that the text shouldn't be highlighted. - @override - String toString({Object? color}) { - if (span == null) return message; - return 'Error on ${span!.message(message, color: color)}'; - } -} - -enum Toolchain { - stable, - beta, - nightly, -} - -class CargoBuildOptions { - final Toolchain toolchain; - final List flags; - - CargoBuildOptions({ - required this.toolchain, - required this.flags, - }); - - static Toolchain _toolchainFromNode(YamlNode node) { - if (node case YamlScalar(value: String name)) { - final toolchain = - Toolchain.values.firstWhereOrNull((element) => element.name == name); - if (toolchain != null) { - return toolchain; - } - } - throw SourceSpanException( - 'Unknown toolchain. Must be one of ${Toolchain.values.map((e) => e.name)}.', - node.span); - } - - static CargoBuildOptions parse(YamlNode node) { - if (node is! YamlMap) { - throw SourceSpanException('Cargo options must be a map', node.span); - } - Toolchain toolchain = Toolchain.stable; - List flags = []; - for (final MapEntry(:key, :value) in node.nodes.entries) { - if (key case YamlScalar(value: 'toolchain')) { - toolchain = _toolchainFromNode(value); - } else if (key case YamlScalar(value: 'extra_flags')) { - if (value case YamlList(nodes: List list)) { - if (list.every((element) { - if (element case YamlScalar(value: String _)) { - return true; - } - return false; - })) { - flags = list.map((e) => e.value as String).toList(); - continue; - } - } - throw SourceSpanException( - 'Extra flags must be a list of strings', value.span); - } else { - throw SourceSpanException( - 'Unknown cargo option type. Must be "toolchain" or "extra_flags".', - key.span); - } - } - return CargoBuildOptions(toolchain: toolchain, flags: flags); - } -} - -extension on YamlMap { - /// Map that extracts keys so that we can do map case check on them. - Map get valueMap => - nodes.map((key, value) => MapEntry(key.value, value)); -} - -class PrecompiledBinaries { - final String uriPrefix; - final PublicKey publicKey; - - PrecompiledBinaries({ - required this.uriPrefix, - required this.publicKey, - }); - - static PublicKey _publicKeyFromHex(String key, SourceSpan? span) { - final bytes = HEX.decode(key); - if (bytes.length != 32) { - throw SourceSpanException( - 'Invalid public key. Must be 32 bytes long.', span); - } - return PublicKey(bytes); - } - - static PrecompiledBinaries parse(YamlNode node) { - if (node case YamlMap(valueMap: Map map)) { - if (map - case { - 'url_prefix': YamlNode urlPrefixNode, - 'public_key': YamlNode publicKeyNode, - }) { - final urlPrefix = switch (urlPrefixNode) { - YamlScalar(value: String urlPrefix) => urlPrefix, - _ => throw SourceSpanException( - 'Invalid URL prefix value.', urlPrefixNode.span), - }; - final publicKey = switch (publicKeyNode) { - YamlScalar(value: String publicKey) => - _publicKeyFromHex(publicKey, publicKeyNode.span), - _ => throw SourceSpanException( - 'Invalid public key value.', publicKeyNode.span), - }; - return PrecompiledBinaries( - uriPrefix: urlPrefix, - publicKey: publicKey, - ); - } - } - throw SourceSpanException( - 'Invalid precompiled binaries value. ' - 'Expected Map with "url_prefix" and "public_key".', - node.span); - } -} - -/// Cargokit options specified for Rust crate. -class CargokitCrateOptions { - CargokitCrateOptions({ - this.cargo = const {}, - this.precompiledBinaries, - }); - - final Map cargo; - final PrecompiledBinaries? precompiledBinaries; - - static CargokitCrateOptions parse(YamlNode node) { - if (node is! YamlMap) { - throw SourceSpanException('Cargokit options must be a map', node.span); - } - final options = {}; - PrecompiledBinaries? precompiledBinaries; - - for (final entry in node.nodes.entries) { - if (entry - case MapEntry( - key: YamlScalar(value: 'cargo'), - value: YamlNode node, - )) { - if (node is! YamlMap) { - throw SourceSpanException('Cargo options must be a map', node.span); - } - for (final MapEntry(:YamlNode key, :value) in node.nodes.entries) { - if (key case YamlScalar(value: String name)) { - final configuration = BuildConfiguration.values - .firstWhereOrNull((element) => element.name == name); - if (configuration != null) { - options[configuration] = CargoBuildOptions.parse(value); - continue; - } - } - throw SourceSpanException( - 'Unknown build configuration. Must be one of ${BuildConfiguration.values.map((e) => e.name)}.', - key.span); - } - } else if (entry.key case YamlScalar(value: 'precompiled_binaries')) { - precompiledBinaries = PrecompiledBinaries.parse(entry.value); - } else { - throw SourceSpanException( - 'Unknown cargokit option type. Must be "cargo" or "precompiled_binaries".', - entry.key.span); - } - } - return CargokitCrateOptions( - cargo: options, - precompiledBinaries: precompiledBinaries, - ); - } - - static CargokitCrateOptions load({ - required String manifestDir, - }) { - final uri = Uri.file(path.join(manifestDir, "cargokit.yaml")); - final file = File.fromUri(uri); - if (file.existsSync()) { - final contents = loadYamlNode(file.readAsStringSync(), sourceUrl: uri); - return parse(contents); - } else { - return CargokitCrateOptions(); - } - } -} - -class CargokitUserOptions { - // When Rustup is installed always build locally unless user opts into - // using precompiled binaries. - static bool defaultUsePrecompiledBinaries() { - return Rustup.executablePath() == null; - } - - CargokitUserOptions({ - required this.usePrecompiledBinaries, - required this.verboseLogging, - }); - - CargokitUserOptions._() - : usePrecompiledBinaries = defaultUsePrecompiledBinaries(), - verboseLogging = false; - - static CargokitUserOptions parse(YamlNode node) { - if (node is! YamlMap) { - throw SourceSpanException('Cargokit options must be a map', node.span); - } - bool usePrecompiledBinaries = defaultUsePrecompiledBinaries(); - bool verboseLogging = false; - - for (final entry in node.nodes.entries) { - if (entry.key case YamlScalar(value: 'use_precompiled_binaries')) { - if (entry.value case YamlScalar(value: bool value)) { - usePrecompiledBinaries = value; - continue; - } - throw SourceSpanException( - 'Invalid value for "use_precompiled_binaries". Must be a boolean.', - entry.value.span); - } else if (entry.key case YamlScalar(value: 'verbose_logging')) { - if (entry.value case YamlScalar(value: bool value)) { - verboseLogging = value; - continue; - } - throw SourceSpanException( - 'Invalid value for "verbose_logging". Must be a boolean.', - entry.value.span); - } else { - throw SourceSpanException( - 'Unknown cargokit option type. Must be "use_precompiled_binaries" or "verbose_logging".', - entry.key.span); - } - } - return CargokitUserOptions( - usePrecompiledBinaries: usePrecompiledBinaries, - verboseLogging: verboseLogging, - ); - } - - static CargokitUserOptions load() { - String fileName = "cargokit_options.yaml"; - var userProjectDir = Directory(Environment.rootProjectDir); - - while (userProjectDir.parent.path != userProjectDir.path) { - final configFile = File(path.join(userProjectDir.path, fileName)); - if (configFile.existsSync()) { - final contents = loadYamlNode( - configFile.readAsStringSync(), - sourceUrl: configFile.uri, - ); - final res = parse(contents); - if (res.verboseLogging) { - _log.info('Found user options file at ${configFile.path}'); - } - return res; - } - userProjectDir = userProjectDir.parent; - } - return CargokitUserOptions._(); - } - - final bool usePrecompiledBinaries; - final bool verboseLogging; -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart deleted file mode 100644 index c27f4195dd..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart +++ /dev/null @@ -1,202 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'dart:io'; - -import 'package:ed25519_edwards/ed25519_edwards.dart'; -import 'package:github/github.dart'; -import 'package:logging/logging.dart'; -import 'package:path/path.dart' as path; - -import 'artifacts_provider.dart'; -import 'builder.dart'; -import 'cargo.dart'; -import 'crate_hash.dart'; -import 'options.dart'; -import 'rustup.dart'; -import 'target.dart'; - -final _log = Logger('precompile_binaries'); - -class PrecompileBinaries { - PrecompileBinaries({ - required this.privateKey, - required this.githubToken, - required this.repositorySlug, - required this.manifestDir, - required this.targets, - this.androidSdkLocation, - this.androidNdkVersion, - this.androidMinSdkVersion, - this.tempDir, - }); - - final PrivateKey privateKey; - final String githubToken; - final RepositorySlug repositorySlug; - final String manifestDir; - final List targets; - final String? androidSdkLocation; - final String? androidNdkVersion; - final int? androidMinSdkVersion; - final String? tempDir; - - static String fileName(Target target, String name) { - return '${target.rust}_$name'; - } - - static String signatureFileName(Target target, String name) { - return '${target.rust}_$name.sig'; - } - - Future run() async { - final crateInfo = CrateInfo.load(manifestDir); - - final targets = List.of(this.targets); - if (targets.isEmpty) { - targets.addAll([ - ...Target.buildableTargets(), - if (androidSdkLocation != null) ...Target.androidTargets(), - ]); - } - - _log.info('Precompiling binaries for $targets'); - - final hash = CrateHash.compute(manifestDir); - _log.info('Computed crate hash: $hash'); - - final String tagName = 'precompiled_$hash'; - - final github = GitHub(auth: Authentication.withToken(githubToken)); - final repo = github.repositories; - final release = await _getOrCreateRelease( - repo: repo, - tagName: tagName, - packageName: crateInfo.packageName, - hash: hash, - ); - - final tempDir = this.tempDir != null - ? Directory(this.tempDir!) - : Directory.systemTemp.createTempSync('precompiled_'); - - tempDir.createSync(recursive: true); - - final crateOptions = CargokitCrateOptions.load( - manifestDir: manifestDir, - ); - - final buildEnvironment = BuildEnvironment( - configuration: BuildConfiguration.release, - crateOptions: crateOptions, - targetTempDir: tempDir.path, - manifestDir: manifestDir, - crateInfo: crateInfo, - isAndroid: androidSdkLocation != null, - androidSdkPath: androidSdkLocation, - androidNdkVersion: androidNdkVersion, - androidMinSdkVersion: androidMinSdkVersion, - ); - - final rustup = Rustup(); - - for (final target in targets) { - final artifactNames = getArtifactNames( - target: target, - libraryName: crateInfo.packageName, - remote: true, - ); - - if (artifactNames.every((name) { - final fileName = PrecompileBinaries.fileName(target, name); - return (release.assets ?? []).any((e) => e.name == fileName); - })) { - _log.info("All artifacts for $target already exist - skipping"); - continue; - } - - _log.info('Building for $target'); - - final builder = - RustBuilder(target: target, environment: buildEnvironment); - builder.prepare(rustup); - final res = await builder.build(); - - final assets = []; - for (final name in artifactNames) { - final file = File(path.join(res, name)); - if (!file.existsSync()) { - throw Exception('Missing artifact: ${file.path}'); - } - - final data = file.readAsBytesSync(); - final create = CreateReleaseAsset( - name: PrecompileBinaries.fileName(target, name), - contentType: "application/octet-stream", - assetData: data, - ); - final signature = sign(privateKey, data); - final signatureCreate = CreateReleaseAsset( - name: signatureFileName(target, name), - contentType: "application/octet-stream", - assetData: signature, - ); - bool verified = verify(public(privateKey), data, signature); - if (!verified) { - throw Exception('Signature verification failed'); - } - assets.add(create); - assets.add(signatureCreate); - } - _log.info('Uploading assets: ${assets.map((e) => e.name)}'); - for (final asset in assets) { - // This seems to be failing on CI so do it one by one - int retryCount = 0; - while (true) { - try { - await repo.uploadReleaseAssets(release, [asset]); - break; - } on Exception catch (e) { - if (retryCount == 10) { - rethrow; - } - ++retryCount; - _log.shout( - 'Upload failed (attempt $retryCount, will retry): ${e.toString()}'); - await Future.delayed(Duration(seconds: 2)); - } - } - } - } - - _log.info('Cleaning up'); - tempDir.deleteSync(recursive: true); - } - - Future _getOrCreateRelease({ - required RepositoriesService repo, - required String tagName, - required String packageName, - required String hash, - }) async { - Release release; - try { - _log.info('Fetching release $tagName'); - release = await repo.getReleaseByTagName(repositorySlug, tagName); - } on ReleaseNotFound { - _log.info('Release not found - creating release $tagName'); - release = await repo.createRelease( - repositorySlug, - CreateRelease.from( - tagName: tagName, - name: 'Precompiled binaries ${hash.substring(0, 8)}', - targetCommitish: null, - isDraft: false, - isPrerelease: false, - body: 'Precompiled binaries for crate $packageName, ' - 'crate hash $hash.', - )); - } - return release; - } -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/rustup.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/rustup.dart deleted file mode 100644 index 0ac8d08616..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/rustup.dart +++ /dev/null @@ -1,136 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'dart:io'; - -import 'package:collection/collection.dart'; -import 'package:path/path.dart' as path; - -import 'util.dart'; - -class _Toolchain { - _Toolchain( - this.name, - this.targets, - ); - - final String name; - final List targets; -} - -class Rustup { - List? installedTargets(String toolchain) { - final targets = _installedTargets(toolchain); - return targets != null ? List.unmodifiable(targets) : null; - } - - void installToolchain(String toolchain) { - log.info("Installing Rust toolchain: $toolchain"); - runCommand("rustup", ['toolchain', 'install', toolchain]); - _installedToolchains - .add(_Toolchain(toolchain, _getInstalledTargets(toolchain))); - } - - void installTarget( - String target, { - required String toolchain, - }) { - log.info("Installing Rust target: $target"); - runCommand("rustup", [ - 'target', - 'add', - '--toolchain', - toolchain, - target, - ]); - _installedTargets(toolchain)?.add(target); - } - - final List<_Toolchain> _installedToolchains; - - Rustup() : _installedToolchains = _getInstalledToolchains(); - - List? _installedTargets(String toolchain) => _installedToolchains - .firstWhereOrNull( - (e) => e.name == toolchain || e.name.startsWith('$toolchain-')) - ?.targets; - - static List<_Toolchain> _getInstalledToolchains() { - String extractToolchainName(String line) { - // ignore (default) after toolchain name - final parts = line.split(' '); - return parts[0]; - } - - final res = runCommand("rustup", ['toolchain', 'list']); - - // To list all non-custom toolchains, we need to filter out lines that - // don't start with "stable", "beta", or "nightly". - Pattern nonCustom = RegExp(r"^(stable|beta|nightly)"); - final lines = res.stdout - .toString() - .split('\n') - .where((e) => e.isNotEmpty && e.startsWith(nonCustom)) - .map(extractToolchainName) - .toList(growable: true); - - return lines - .map( - (name) => _Toolchain( - name, - _getInstalledTargets(name), - ), - ) - .toList(growable: true); - } - - static List _getInstalledTargets(String toolchain) { - final res = runCommand("rustup", [ - 'target', - 'list', - '--toolchain', - toolchain, - '--installed', - ]); - final lines = res.stdout - .toString() - .split('\n') - .where((e) => e.isNotEmpty) - .toList(growable: true); - return lines; - } - - bool _didInstallRustSrcForNightly = false; - - void installRustSrcForNightly() { - if (_didInstallRustSrcForNightly) { - return; - } - // Useful for -Z build-std - runCommand( - "rustup", - ['component', 'add', 'rust-src', '--toolchain', 'nightly'], - ); - _didInstallRustSrcForNightly = true; - } - - static String? executablePath() { - final envPath = Platform.environment['PATH']; - final envPathSeparator = Platform.isWindows ? ';' : ':'; - final home = Platform.isWindows - ? Platform.environment['USERPROFILE'] - : Platform.environment['HOME']; - final paths = [ - if (home != null) path.join(home, '.cargo', 'bin'), - if (envPath != null) ...envPath.split(envPathSeparator), - ]; - for (final p in paths) { - final rustup = Platform.isWindows ? 'rustup.exe' : 'rustup'; - final rustupPath = path.join(p, rustup); - if (File(rustupPath).existsSync()) { - return rustupPath; - } - } - return null; - } -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/target.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/target.dart deleted file mode 100644 index 6fbc58b64f..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/target.dart +++ /dev/null @@ -1,140 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'dart:io'; - -import 'package:collection/collection.dart'; - -import 'util.dart'; - -class Target { - Target({ - required this.rust, - this.flutter, - this.android, - this.androidMinSdkVersion, - this.darwinPlatform, - this.darwinArch, - }); - - static final all = [ - Target( - rust: 'armv7-linux-androideabi', - flutter: 'android-arm', - android: 'armeabi-v7a', - androidMinSdkVersion: 16, - ), - Target( - rust: 'aarch64-linux-android', - flutter: 'android-arm64', - android: 'arm64-v8a', - androidMinSdkVersion: 21, - ), - Target( - rust: 'i686-linux-android', - flutter: 'android-x86', - android: 'x86', - androidMinSdkVersion: 16, - ), - Target( - rust: 'x86_64-linux-android', - flutter: 'android-x64', - android: 'x86_64', - androidMinSdkVersion: 21, - ), - Target( - rust: 'x86_64-pc-windows-msvc', - flutter: 'windows-x64', - ), - Target( - rust: 'x86_64-unknown-linux-gnu', - flutter: 'linux-x64', - ), - Target( - rust: 'aarch64-unknown-linux-gnu', - flutter: 'linux-arm64', - ), - Target( - rust: 'x86_64-apple-darwin', - darwinPlatform: 'macosx', - darwinArch: 'x86_64', - ), - Target( - rust: 'aarch64-apple-darwin', - darwinPlatform: 'macosx', - darwinArch: 'arm64', - ), - Target( - rust: 'aarch64-apple-ios', - darwinPlatform: 'iphoneos', - darwinArch: 'arm64', - ), - Target( - rust: 'aarch64-apple-ios-sim', - darwinPlatform: 'iphonesimulator', - darwinArch: 'arm64', - ), - Target( - rust: 'x86_64-apple-ios', - darwinPlatform: 'iphonesimulator', - darwinArch: 'x86_64', - ), - ]; - - static Target? forFlutterName(String flutterName) { - return all.firstWhereOrNull((element) => element.flutter == flutterName); - } - - static Target? forDarwin({ - required String platformName, - required String darwinAarch, - }) { - return all.firstWhereOrNull((element) => // - element.darwinPlatform == platformName && - element.darwinArch == darwinAarch); - } - - static Target? forRustTriple(String triple) { - return all.firstWhereOrNull((element) => element.rust == triple); - } - - static List androidTargets() { - return all - .where((element) => element.android != null) - .toList(growable: false); - } - - /// Returns buildable targets on current host platform ignoring Android targets. - static List buildableTargets() { - if (Platform.isLinux) { - // Right now we don't support cross-compiling on Linux. So we just return - // the host target. - final arch = runCommand('arch', []).stdout as String; - if (arch.trim() == 'aarch64') { - return [Target.forRustTriple('aarch64-unknown-linux-gnu')!]; - } else { - return [Target.forRustTriple('x86_64-unknown-linux-gnu')!]; - } - } - return all.where((target) { - if (Platform.isWindows) { - return target.rust.contains('-windows-'); - } else if (Platform.isMacOS) { - return target.darwinPlatform != null; - } - return false; - }).toList(growable: false); - } - - @override - String toString() { - return rust; - } - - final String? flutter; - final String rust; - final String? android; - final int? androidMinSdkVersion; - final String? darwinPlatform; - final String? darwinArch; -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/util.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/util.dart deleted file mode 100644 index 8bb6a8724f..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/util.dart +++ /dev/null @@ -1,172 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'dart:convert'; -import 'dart:io'; - -import 'package:logging/logging.dart'; -import 'package:path/path.dart' as path; - -import 'logging.dart'; -import 'rustup.dart'; - -final log = Logger("process"); - -class CommandFailedException implements Exception { - final String executable; - final List arguments; - final ProcessResult result; - - CommandFailedException({ - required this.executable, - required this.arguments, - required this.result, - }); - - @override - String toString() { - final stdout = result.stdout.toString().trim(); - final stderr = result.stderr.toString().trim(); - return [ - "External Command: $executable ${arguments.map((e) => '"$e"').join(' ')}", - "Returned Exit Code: ${result.exitCode}", - kSeparator, - "STDOUT:", - if (stdout.isNotEmpty) stdout, - kSeparator, - "STDERR:", - if (stderr.isNotEmpty) stderr, - ].join('\n'); - } -} - -class TestRunCommandArgs { - final String executable; - final List arguments; - final String? workingDirectory; - final Map? environment; - final bool includeParentEnvironment; - final bool runInShell; - final Encoding? stdoutEncoding; - final Encoding? stderrEncoding; - - TestRunCommandArgs({ - required this.executable, - required this.arguments, - this.workingDirectory, - this.environment, - this.includeParentEnvironment = true, - this.runInShell = false, - this.stdoutEncoding, - this.stderrEncoding, - }); -} - -class TestRunCommandResult { - TestRunCommandResult({ - this.pid = 1, - this.exitCode = 0, - this.stdout = '', - this.stderr = '', - }); - - final int pid; - final int exitCode; - final String stdout; - final String stderr; -} - -TestRunCommandResult Function(TestRunCommandArgs args)? testRunCommandOverride; - -ProcessResult runCommand( - String executable, - List arguments, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false, - Encoding? stdoutEncoding = systemEncoding, - Encoding? stderrEncoding = systemEncoding, -}) { - if (testRunCommandOverride != null) { - final result = testRunCommandOverride!(TestRunCommandArgs( - executable: executable, - arguments: arguments, - workingDirectory: workingDirectory, - environment: environment, - includeParentEnvironment: includeParentEnvironment, - runInShell: runInShell, - stdoutEncoding: stdoutEncoding, - stderrEncoding: stderrEncoding, - )); - return ProcessResult( - result.pid, - result.exitCode, - result.stdout, - result.stderr, - ); - } - log.finer('Running command $executable ${arguments.join(' ')}'); - final res = Process.runSync( - _resolveExecutable(executable), - arguments, - workingDirectory: workingDirectory, - environment: environment, - includeParentEnvironment: includeParentEnvironment, - runInShell: runInShell, - stderrEncoding: stderrEncoding, - stdoutEncoding: stdoutEncoding, - ); - if (res.exitCode != 0) { - throw CommandFailedException( - executable: executable, - arguments: arguments, - result: res, - ); - } else { - return res; - } -} - -class RustupNotFoundException implements Exception { - @override - String toString() { - return [ - ' ', - 'rustup not found in PATH.', - ' ', - 'Maybe you need to install Rust? It only takes a minute:', - ' ', - if (Platform.isWindows) 'https://www.rust-lang.org/tools/install', - if (hasHomebrewRustInPath()) ...[ - '\$ brew unlink rust # Unlink homebrew Rust from PATH', - ], - if (!Platform.isWindows) - "\$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh", - ' ', - ].join('\n'); - } - - static bool hasHomebrewRustInPath() { - if (!Platform.isMacOS) { - return false; - } - final envPath = Platform.environment['PATH'] ?? ''; - final paths = envPath.split(':'); - return paths.any((p) { - return p.contains('homebrew') && File(path.join(p, 'rustc')).existsSync(); - }); - } -} - -String _resolveExecutable(String executable) { - if (executable == 'rustup') { - final resolved = Rustup.executablePath(); - if (resolved != null) { - return resolved; - } - throw RustupNotFoundException(); - } else { - return executable; - } -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart deleted file mode 100644 index 2366b57bfd..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart +++ /dev/null @@ -1,84 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import 'dart:io'; - -import 'package:ed25519_edwards/ed25519_edwards.dart'; -import 'package:http/http.dart'; - -import 'artifacts_provider.dart'; -import 'cargo.dart'; -import 'crate_hash.dart'; -import 'options.dart'; -import 'precompile_binaries.dart'; -import 'target.dart'; - -class VerifyBinaries { - VerifyBinaries({ - required this.manifestDir, - }); - - final String manifestDir; - - Future run() async { - final crateInfo = CrateInfo.load(manifestDir); - - final config = CargokitCrateOptions.load(manifestDir: manifestDir); - final precompiledBinaries = config.precompiledBinaries; - if (precompiledBinaries == null) { - stdout.writeln('Crate does not support precompiled binaries.'); - } else { - final crateHash = CrateHash.compute(manifestDir); - stdout.writeln('Crate hash: $crateHash'); - - for (final target in Target.all) { - final message = 'Checking ${target.rust}...'; - stdout.write(message.padRight(40)); - stdout.flush(); - - final artifacts = getArtifactNames( - target: target, - libraryName: crateInfo.packageName, - remote: true, - ); - - final prefix = precompiledBinaries.uriPrefix; - - bool ok = true; - - for (final artifact in artifacts) { - final fileName = PrecompileBinaries.fileName(target, artifact); - final signatureFileName = - PrecompileBinaries.signatureFileName(target, artifact); - - final url = Uri.parse('$prefix$crateHash/$fileName'); - final signatureUrl = - Uri.parse('$prefix$crateHash/$signatureFileName'); - - final signature = await get(signatureUrl); - if (signature.statusCode != 200) { - stdout.writeln('MISSING'); - ok = false; - break; - } - final asset = await get(url); - if (asset.statusCode != 200) { - stdout.writeln('MISSING'); - ok = false; - break; - } - - if (!verify(precompiledBinaries.publicKey, asset.bodyBytes, - signature.bodyBytes)) { - stdout.writeln('INVALID SIGNATURE'); - ok = false; - } - } - - if (ok) { - stdout.writeln('OK'); - } - } - } - } -} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.lock b/mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.lock deleted file mode 100644 index 343bdd3694..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.lock +++ /dev/null @@ -1,453 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 - url: "https://pub.dev" - source: hosted - version: "64.0.0" - adaptive_number: - dependency: transitive - description: - name: adaptive_number - sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" - url: "https://pub.dev" - source: hosted - version: "6.2.0" - args: - dependency: "direct main" - description: - name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 - url: "https://pub.dev" - source: hosted - version: "2.4.2" - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - collection: - dependency: "direct main" - description: - name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.dev" - source: hosted - version: "1.18.0" - convert: - dependency: "direct main" - description: - name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" - source: hosted - version: "3.1.1" - coverage: - dependency: transitive - description: - name: coverage - sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" - url: "https://pub.dev" - source: hosted - version: "1.6.3" - crypto: - dependency: "direct main" - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" - ed25519_edwards: - dependency: "direct main" - description: - name: ed25519_edwards - sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd" - url: "https://pub.dev" - source: hosted - version: "0.3.1" - file: - dependency: transitive - description: - name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" - url: "https://pub.dev" - source: hosted - version: "6.1.4" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" - url: "https://pub.dev" - source: hosted - version: "3.2.0" - github: - dependency: "direct main" - description: - name: github - sha256: "9966bc13bf612342e916b0a343e95e5f046c88f602a14476440e9b75d2295411" - url: "https://pub.dev" - source: hosted - version: "9.17.0" - glob: - dependency: transitive - description: - name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - hex: - dependency: "direct main" - description: - name: hex - sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" - url: "https://pub.dev" - source: hosted - version: "0.2.0" - http: - dependency: "direct main" - description: - name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" - url: "https://pub.dev" - source: hosted - version: "3.2.1" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - io: - dependency: transitive - description: - name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 - url: "https://pub.dev" - source: hosted - version: "4.8.1" - lints: - dependency: "direct dev" - description: - name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - logging: - dependency: "direct main" - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" - url: "https://pub.dev" - source: hosted - version: "0.12.16" - meta: - dependency: transitive - description: - name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - mime: - dependency: transitive - description: - name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e - url: "https://pub.dev" - source: hosted - version: "1.0.4" - node_preamble: - dependency: transitive - description: - name: node_preamble - sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" - url: "https://pub.dev" - source: hosted - version: "2.0.2" - package_config: - dependency: transitive - description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - path: - dependency: "direct main" - description: - name: path - sha256: "2ad4cddff7f5cc0e2d13069f2a3f7a73ca18f66abd6f5ecf215219cdb3638edb" - url: "https://pub.dev" - source: hosted - version: "1.8.0" - petitparser: - dependency: transitive - description: - name: petitparser - sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 - url: "https://pub.dev" - source: hosted - version: "5.4.0" - pool: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - shelf: - dependency: transitive - description: - name: shelf - sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 - url: "https://pub.dev" - source: hosted - version: "1.4.1" - shelf_packages_handler: - dependency: transitive - description: - name: shelf_packages_handler - sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - shelf_static: - dependency: transitive - description: - name: shelf_static - sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e - url: "https://pub.dev" - source: hosted - version: "1.1.2" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - source_map_stack_trace: - dependency: transitive - description: - name: source_map_stack_trace - sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - source_maps: - dependency: transitive - description: - name: source_maps - sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" - url: "https://pub.dev" - source: hosted - version: "0.10.12" - source_span: - dependency: "direct main" - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" - source: hosted - version: "1.11.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test: - dependency: "direct dev" - description: - name: test - sha256: "9b0dd8e36af4a5b1569029949d50a52cb2a2a2fdaa20cebb96e6603b9ae241f9" - url: "https://pub.dev" - source: hosted - version: "1.24.6" - test_api: - dependency: transitive - description: - name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" - url: "https://pub.dev" - source: hosted - version: "0.6.1" - test_core: - dependency: transitive - description: - name: test_core - sha256: "4bef837e56375537055fdbbbf6dd458b1859881f4c7e6da936158f77d61ab265" - url: "https://pub.dev" - source: hosted - version: "0.5.6" - toml: - dependency: "direct main" - description: - name: toml - sha256: "157c5dca5160fced243f3ce984117f729c788bb5e475504f3dbcda881accee44" - url: "https://pub.dev" - source: hosted - version: "0.14.0" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - version: - dependency: "direct main" - description: - name: version - sha256: "2307e23a45b43f96469eeab946208ed63293e8afca9c28cd8b5241ff31c55f55" - url: "https://pub.dev" - source: hosted - version: "3.0.0" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: "0fae432c85c4ea880b33b497d32824b97795b04cdaa74d270219572a1f50268d" - url: "https://pub.dev" - source: hosted - version: "11.9.0" - watcher: - dependency: transitive - description: - name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b - url: "https://pub.dev" - source: hosted - version: "2.4.0" - webkit_inspection_protocol: - dependency: transitive - description: - name: webkit_inspection_protocol - sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - yaml: - dependency: "direct main" - description: - name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.dev" - source: hosted - version: "3.1.2" -sdks: - dart: ">=3.0.0 <4.0.0" diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.yaml b/mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.yaml deleted file mode 100644 index 18c61e3386..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.yaml +++ /dev/null @@ -1,33 +0,0 @@ -# This is copied from Cargokit (which is the official way to use it currently) -# Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -name: build_tool -description: Cargokit build_tool. Facilitates the build of Rust crate during Flutter application build. -publish_to: none -version: 1.0.0 - -environment: - sdk: ">=3.0.0 <4.0.0" - -# Add regular dependencies here. -dependencies: - # these are pinned on purpose because the bundle_tool_runner doesn't have - # pubspec.lock. See run_build_tool.sh - logging: 1.2.0 - path: 1.8.0 - version: 3.0.0 - collection: 1.18.0 - ed25519_edwards: 0.3.1 - hex: 0.2.0 - yaml: 3.1.2 - source_span: 1.10.0 - github: 9.17.0 - args: 2.4.2 - crypto: 3.0.3 - convert: 3.1.1 - http: 1.1.0 - toml: 0.14.0 - -dev_dependencies: - lints: ^2.1.0 - test: ^1.24.0 diff --git a/mobile/apps/photos/rust_builder/cargokit/cmake/cargokit.cmake b/mobile/apps/photos/rust_builder/cargokit/cmake/cargokit.cmake deleted file mode 100644 index ddd05df9b4..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/cmake/cargokit.cmake +++ /dev/null @@ -1,99 +0,0 @@ -SET(cargokit_cmake_root "${CMAKE_CURRENT_LIST_DIR}/..") - -# Workaround for https://github.com/dart-lang/pub/issues/4010 -get_filename_component(cargokit_cmake_root "${cargokit_cmake_root}" REALPATH) - -if(WIN32) - # REALPATH does not properly resolve symlinks on windows :-/ - execute_process(COMMAND powershell -ExecutionPolicy Bypass -File "${CMAKE_CURRENT_LIST_DIR}/resolve_symlinks.ps1" "${cargokit_cmake_root}" OUTPUT_VARIABLE cargokit_cmake_root OUTPUT_STRIP_TRAILING_WHITESPACE) -endif() - -# Arguments -# - target: CMAKE target to which rust library is linked -# - manifest_dir: relative path from current folder to directory containing cargo manifest -# - lib_name: cargo package name -# - any_symbol_name: name of any exported symbol from the library. -# used on windows to force linking with library. -function(apply_cargokit target manifest_dir lib_name any_symbol_name) - - set(CARGOKIT_LIB_NAME "${lib_name}") - set(CARGOKIT_LIB_FULL_NAME "${CMAKE_SHARED_MODULE_PREFIX}${CARGOKIT_LIB_NAME}${CMAKE_SHARED_MODULE_SUFFIX}") - if (CMAKE_CONFIGURATION_TYPES) - set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/$") - set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/$/${CARGOKIT_LIB_FULL_NAME}") - else() - set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") - set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/${CARGOKIT_LIB_FULL_NAME}") - endif() - set(CARGOKIT_TEMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/cargokit_build") - - if (FLUTTER_TARGET_PLATFORM) - set(CARGOKIT_TARGET_PLATFORM "${FLUTTER_TARGET_PLATFORM}") - else() - set(CARGOKIT_TARGET_PLATFORM "windows-x64") - endif() - - set(CARGOKIT_ENV - "CARGOKIT_CMAKE=${CMAKE_COMMAND}" - "CARGOKIT_CONFIGURATION=$" - "CARGOKIT_MANIFEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/${manifest_dir}" - "CARGOKIT_TARGET_TEMP_DIR=${CARGOKIT_TEMP_DIR}" - "CARGOKIT_OUTPUT_DIR=${CARGOKIT_OUTPUT_DIR}" - "CARGOKIT_TARGET_PLATFORM=${CARGOKIT_TARGET_PLATFORM}" - "CARGOKIT_TOOL_TEMP_DIR=${CARGOKIT_TEMP_DIR}/tool" - "CARGOKIT_ROOT_PROJECT_DIR=${CMAKE_SOURCE_DIR}" - ) - - if (WIN32) - set(SCRIPT_EXTENSION ".cmd") - set(IMPORT_LIB_EXTENSION ".lib") - else() - set(SCRIPT_EXTENSION ".sh") - set(IMPORT_LIB_EXTENSION "") - execute_process(COMMAND chmod +x "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}") - endif() - - # Using generators in custom command is only supported in CMake 3.20+ - if (CMAKE_CONFIGURATION_TYPES AND ${CMAKE_VERSION} VERSION_LESS "3.20.0") - foreach(CONFIG IN LISTS CMAKE_CONFIGURATION_TYPES) - add_custom_command( - OUTPUT - "${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}/${CARGOKIT_LIB_FULL_NAME}" - "${CMAKE_CURRENT_BINARY_DIR}/_phony_" - COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV} - "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake - VERBATIM - ) - endforeach() - else() - add_custom_command( - OUTPUT - ${OUTPUT_LIB} - "${CMAKE_CURRENT_BINARY_DIR}/_phony_" - COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV} - "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake - VERBATIM - ) - endif() - - - set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/_phony_" PROPERTIES SYMBOLIC TRUE) - - if (TARGET ${target}) - # If we have actual cmake target provided create target and make existing - # target depend on it - add_custom_target("${target}_cargokit" DEPENDS ${OUTPUT_LIB}) - add_dependencies("${target}" "${target}_cargokit") - target_link_libraries("${target}" PRIVATE "${OUTPUT_LIB}${IMPORT_LIB_EXTENSION}") - if(WIN32) - target_link_options(${target} PRIVATE "/INCLUDE:${any_symbol_name}") - endif() - else() - # Otherwise (FFI) just use ALL to force building always - add_custom_target("${target}_cargokit" ALL DEPENDS ${OUTPUT_LIB}) - endif() - - # Allow adding the output library to plugin bundled libraries - set("${target}_cargokit_lib" ${OUTPUT_LIB} PARENT_SCOPE) - -endfunction() diff --git a/mobile/apps/photos/rust_builder/cargokit/cmake/resolve_symlinks.ps1 b/mobile/apps/photos/rust_builder/cargokit/cmake/resolve_symlinks.ps1 deleted file mode 100644 index 3d10d283c2..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/cmake/resolve_symlinks.ps1 +++ /dev/null @@ -1,27 +0,0 @@ -function Resolve-Symlinks { - [CmdletBinding()] - [OutputType([string])] - param( - [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] - [string] $Path - ) - - [string] $separator = '/' - [string[]] $parts = $Path.Split($separator) - - [string] $realPath = '' - foreach ($part in $parts) { - if ($realPath -and !$realPath.EndsWith($separator)) { - $realPath += $separator - } - $realPath += $part - $item = Get-Item $realPath - if ($item.Target) { - $realPath = $item.Target.Replace('\', '/') - } - } - $realPath -} - -$path=Resolve-Symlinks -Path $args[0] -Write-Host $path diff --git a/mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle b/mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle deleted file mode 100644 index d139d04f66..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle +++ /dev/null @@ -1,179 +0,0 @@ -/// This is copied from Cargokit (which is the official way to use it currently) -/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin - -import java.nio.file.Paths -import org.apache.tools.ant.taskdefs.condition.Os - -CargoKitPlugin.file = buildscript.sourceFile - -apply plugin: CargoKitPlugin - -class CargoKitExtension { - String manifestDir; // Relative path to folder containing Cargo.toml - String libname; // Library name within Cargo.toml. Must be a cdylib -} - -abstract class CargoKitBuildTask extends DefaultTask { - - @Input - String buildMode - - @Input - String buildDir - - @Input - String outputDir - - @Input - String ndkVersion - - @Input - String sdkDirectory - - @Input - int compileSdkVersion; - - @Input - int minSdkVersion; - - @Input - String pluginFile - - @Input - List targetPlatforms - - @TaskAction - def build() { - if (project.cargokit.manifestDir == null) { - throw new GradleException("Property 'manifestDir' must be set on cargokit extension"); - } - - if (project.cargokit.libname == null) { - throw new GradleException("Property 'libname' must be set on cargokit extension"); - } - - def executableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "run_build_tool.cmd" : "run_build_tool.sh" - def path = Paths.get(new File(pluginFile).parent, "..", executableName); - - def manifestDir = Paths.get(project.buildscript.sourceFile.parent, project.cargokit.manifestDir) - - def rootProjectDir = project.rootProject.projectDir - - if (!Os.isFamily(Os.FAMILY_WINDOWS)) { - project.exec { - commandLine 'chmod', '+x', path - } - } - - project.exec { - executable path - args "build-gradle" - environment "CARGOKIT_ROOT_PROJECT_DIR", rootProjectDir - environment "CARGOKIT_TOOL_TEMP_DIR", "${buildDir}/build_tool" - environment "CARGOKIT_MANIFEST_DIR", manifestDir - environment "CARGOKIT_CONFIGURATION", buildMode - environment "CARGOKIT_TARGET_TEMP_DIR", buildDir - environment "CARGOKIT_OUTPUT_DIR", outputDir - environment "CARGOKIT_NDK_VERSION", ndkVersion - environment "CARGOKIT_SDK_DIR", sdkDirectory - environment "CARGOKIT_COMPILE_SDK_VERSION", compileSdkVersion - environment "CARGOKIT_MIN_SDK_VERSION", minSdkVersion - environment "CARGOKIT_TARGET_PLATFORMS", targetPlatforms.join(",") - environment "CARGOKIT_JAVA_HOME", System.properties['java.home'] - } - } -} - -class CargoKitPlugin implements Plugin { - - static String file; - - private Plugin findFlutterPlugin(Project rootProject) { - _findFlutterPlugin(rootProject.childProjects) - } - - private Plugin _findFlutterPlugin(Map projects) { - for (project in projects) { - for (plugin in project.value.getPlugins()) { - if (plugin.class.name == "com.flutter.gradle.FlutterPlugin") { - return plugin; - } - } - def plugin = _findFlutterPlugin(project.value.childProjects); - if (plugin != null) { - return plugin; - } - } - return null; - } - - @Override - void apply(Project project) { - def plugin = findFlutterPlugin(project.rootProject); - - project.extensions.create("cargokit", CargoKitExtension) - - if (plugin == null) { - print("Flutter plugin not found, CargoKit plugin will not be applied.") - return; - } - - def cargoBuildDir = "${project.buildDir}/build" - - // Determine if the project is an application or library - def isApplication = plugin.project.plugins.hasPlugin('com.android.application') - def variants = isApplication ? plugin.project.android.applicationVariants : plugin.project.android.libraryVariants - - variants.all { variant -> - - final buildType = variant.buildType.name - - def cargoOutputDir = "${project.buildDir}/jniLibs/${buildType}"; - def jniLibs = project.android.sourceSets.maybeCreate(buildType).jniLibs; - jniLibs.srcDir(new File(cargoOutputDir)) - - def platforms = com.flutter.gradle.FlutterPluginUtils.getTargetPlatforms(project).collect() - - // Same thing addFlutterDependencies does in flutter.gradle - if (buildType == "debug") { - platforms.add("android-x86") - platforms.add("android-x64") - } - - // The task name depends on plugin properties, which are not available - // at this point - project.getGradle().afterProject { - def taskName = "cargokitCargoBuild${project.cargokit.libname.capitalize()}${buildType.capitalize()}"; - - if (project.tasks.findByName(taskName)) { - return - } - - if (plugin.project.android.ndkVersion == null) { - throw new GradleException("Please set 'android.ndkVersion' in 'app/build.gradle'.") - } - - def task = project.tasks.create(taskName, CargoKitBuildTask.class) { - buildMode = variant.buildType.name - buildDir = cargoBuildDir - outputDir = cargoOutputDir - ndkVersion = plugin.project.android.ndkVersion - sdkDirectory = plugin.project.android.sdkDirectory - minSdkVersion = plugin.project.android.defaultConfig.minSdkVersion.apiLevel as int - compileSdkVersion = plugin.project.android.compileSdkVersion.substring(8) as int - targetPlatforms = platforms - pluginFile = CargoKitPlugin.file - } - def onTask = { newTask -> - if (newTask.name == "merge${buildType.capitalize()}NativeLibs") { - newTask.dependsOn task - // Fix gradle 7.4.2 not picking up JNI library changes - newTask.outputs.upToDateWhen { false } - } - } - project.tasks.each onTask - project.tasks.whenTaskAdded onTask - } - } - } -} diff --git a/mobile/apps/photos/rust_builder/cargokit/run_build_tool.cmd b/mobile/apps/photos/rust_builder/cargokit/run_build_tool.cmd deleted file mode 100755 index c45d0aa8b5..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/run_build_tool.cmd +++ /dev/null @@ -1,91 +0,0 @@ -@echo off -setlocal - -setlocal ENABLEDELAYEDEXPANSION - -SET BASEDIR=%~dp0 - -if not exist "%CARGOKIT_TOOL_TEMP_DIR%" ( - mkdir "%CARGOKIT_TOOL_TEMP_DIR%" -) -cd /D "%CARGOKIT_TOOL_TEMP_DIR%" - -SET BUILD_TOOL_PKG_DIR=%BASEDIR%build_tool -SET DART=%FLUTTER_ROOT%\bin\cache\dart-sdk\bin\dart - -set BUILD_TOOL_PKG_DIR_POSIX=%BUILD_TOOL_PKG_DIR:\=/% - -( - echo name: build_tool_runner - echo version: 1.0.0 - echo publish_to: none - echo. - echo environment: - echo sdk: '^>=3.0.0 ^<4.0.0' - echo. - echo dependencies: - echo build_tool: - echo path: %BUILD_TOOL_PKG_DIR_POSIX% -) >pubspec.yaml - -if not exist bin ( - mkdir bin -) - -( - echo import 'package:build_tool/build_tool.dart' as build_tool; - echo void main^(List^ args^) ^{ - echo build_tool.runMain^(args^); - echo ^} -) >bin\build_tool_runner.dart - -SET PRECOMPILED=bin\build_tool_runner.dill - -REM To detect changes in package we compare output of DIR /s (recursive) -set PREV_PACKAGE_INFO=.dart_tool\package_info.prev -set CUR_PACKAGE_INFO=.dart_tool\package_info.cur - -DIR "%BUILD_TOOL_PKG_DIR%" /s > "%CUR_PACKAGE_INFO%_orig" - -REM Last line in dir output is free space on harddrive. That is bound to -REM change between invocation so we need to remove it -( - Set "Line=" - For /F "UseBackQ Delims=" %%A In ("%CUR_PACKAGE_INFO%_orig") Do ( - SetLocal EnableDelayedExpansion - If Defined Line Echo !Line! - EndLocal - Set "Line=%%A") -) >"%CUR_PACKAGE_INFO%" -DEL "%CUR_PACKAGE_INFO%_orig" - -REM Compare current directory listing with previous -FC /B "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" > nul 2>&1 - -If %ERRORLEVEL% neq 0 ( - REM Changed - copy current to previous and remove precompiled kernel - if exist "%PREV_PACKAGE_INFO%" ( - DEL "%PREV_PACKAGE_INFO%" - ) - MOVE /Y "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" - if exist "%PRECOMPILED%" ( - DEL "%PRECOMPILED%" - ) -) - -REM There is no CUR_PACKAGE_INFO it was renamed in previous step to %PREV_PACKAGE_INFO% -REM which means we need to do pub get and precompile -if not exist "%PRECOMPILED%" ( - echo Running pub get in "%cd%" - "%DART%" pub get --no-precompile - "%DART%" compile kernel bin/build_tool_runner.dart -) - -"%DART%" "%PRECOMPILED%" %* - -REM 253 means invalid snapshot version. -If %ERRORLEVEL% equ 253 ( - "%DART%" pub get --no-precompile - "%DART%" compile kernel bin/build_tool_runner.dart - "%DART%" "%PRECOMPILED%" %* -) diff --git a/mobile/apps/photos/rust_builder/cargokit/run_build_tool.sh b/mobile/apps/photos/rust_builder/cargokit/run_build_tool.sh deleted file mode 100755 index 6e594a23d4..0000000000 --- a/mobile/apps/photos/rust_builder/cargokit/run_build_tool.sh +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env bash - -set -e - -BASEDIR=$(dirname "$0") - -mkdir -p "$CARGOKIT_TOOL_TEMP_DIR" - -cd "$CARGOKIT_TOOL_TEMP_DIR" - -# Write a very simple bin package in temp folder that depends on build_tool package -# from Cargokit. This is done to ensure that we don't pollute Cargokit folder -# with .dart_tool contents. - -BUILD_TOOL_PKG_DIR="$BASEDIR/build_tool" - -if [[ -z $FLUTTER_ROOT ]]; then # not defined - DART=dart -else - DART="$FLUTTER_ROOT/bin/cache/dart-sdk/bin/dart" -fi - -cat << EOF > "pubspec.yaml" -name: build_tool_runner -version: 1.0.0 -publish_to: none - -environment: - sdk: '>=3.0.0 <4.0.0' - -dependencies: - build_tool: - path: "$BUILD_TOOL_PKG_DIR" -EOF - -mkdir -p "bin" - -cat << EOF > "bin/build_tool_runner.dart" -import 'package:build_tool/build_tool.dart' as build_tool; -void main(List args) { - build_tool.runMain(args); -} -EOF - -# Create alias for `shasum` if it does not exist and `sha1sum` exists -if ! [ -x "$(command -v shasum)" ] && [ -x "$(command -v sha1sum)" ]; then - shopt -s expand_aliases - alias shasum="sha1sum" -fi - -# Dart run will not cache any package that has a path dependency, which -# is the case for our build_tool_runner. So instead we precompile the package -# ourselves. -# To invalidate the cached kernel we use the hash of ls -LR of the build_tool -# package directory. This should be good enough, as the build_tool package -# itself is not meant to have any path dependencies. - -if [[ "$OSTYPE" == "darwin"* ]]; then - PACKAGE_HASH=$(ls -lTR "$BUILD_TOOL_PKG_DIR" | shasum) -else - PACKAGE_HASH=$(ls -lR --full-time "$BUILD_TOOL_PKG_DIR" | shasum) -fi - -PACKAGE_HASH_FILE=".package_hash" - -if [ -f "$PACKAGE_HASH_FILE" ]; then - EXISTING_HASH=$(cat "$PACKAGE_HASH_FILE") - if [ "$PACKAGE_HASH" != "$EXISTING_HASH" ]; then - rm "$PACKAGE_HASH_FILE" - fi -fi - -# Run pub get if needed. -if [ ! -f "$PACKAGE_HASH_FILE" ]; then - "$DART" pub get --no-precompile - "$DART" compile kernel bin/build_tool_runner.dart - echo "$PACKAGE_HASH" > "$PACKAGE_HASH_FILE" -fi - -set +e - -"$DART" bin/build_tool_runner.dill "$@" - -exit_code=$? - -# 253 means invalid snapshot version. -if [ $exit_code == 253 ]; then - "$DART" pub get --no-precompile - "$DART" compile kernel bin/build_tool_runner.dart - "$DART" bin/build_tool_runner.dill "$@" - exit_code=$? -fi - -exit $exit_code diff --git a/mobile/apps/photos/rust_builder/ios/Classes/dummy_file.c b/mobile/apps/photos/rust_builder/ios/Classes/dummy_file.c deleted file mode 100644 index e06dab9968..0000000000 --- a/mobile/apps/photos/rust_builder/ios/Classes/dummy_file.c +++ /dev/null @@ -1 +0,0 @@ -// This is an empty file to force CocoaPods to create a framework. diff --git a/mobile/apps/photos/rust_builder/ios/rust_lib_photos.podspec b/mobile/apps/photos/rust_builder/ios/rust_lib_photos.podspec deleted file mode 100644 index 16d34d0103..0000000000 --- a/mobile/apps/photos/rust_builder/ios/rust_lib_photos.podspec +++ /dev/null @@ -1,45 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint rust_lib_photos.podspec` to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'rust_lib_photos' - s.version = '0.0.1' - s.summary = 'A new Flutter FFI plugin project.' - s.description = <<-DESC -A new Flutter FFI plugin project. - DESC - s.homepage = 'http://example.com' - s.license = { :file => '../LICENSE' } - s.author = { 'Your Company' => 'email@example.com' } - - # This will ensure the source files in Classes/ are included in the native - # builds of apps using this FFI plugin. Podspec does not support relative - # paths, so Classes contains a forwarder C file that relatively imports - # `../src/*` so that the C sources can be shared among all target platforms. - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.dependency 'Flutter' - s.platform = :ios, '11.0' - - # Flutter.framework does not contain a i386 slice. - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } - s.swift_version = '5.0' - - s.script_phase = { - :name => 'Build Rust library', - # First argument is relative path to the `rust` folder, second is name of rust library - :script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../../rust rust_lib_photos', - :execution_position => :before_compile, - :input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'], - # Let XCode know that the static library referenced in -force_load below is - # created by this build step. - :output_files => ["${BUILT_PRODUCTS_DIR}/librust_lib_photos.a"], - } - s.pod_target_xcconfig = { - 'DEFINES_MODULE' => 'YES', - # Flutter.framework does not contain a i386 slice. - 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', - 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/librust_lib_photos.a -lc++', - } -end \ No newline at end of file diff --git a/mobile/apps/photos/rust_builder/linux/CMakeLists.txt b/mobile/apps/photos/rust_builder/linux/CMakeLists.txt deleted file mode 100644 index e6656c9227..0000000000 --- a/mobile/apps/photos/rust_builder/linux/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -# The Flutter tooling requires that developers have CMake 3.10 or later -# installed. You should not increase this version, as doing so will cause -# the plugin to fail to compile for some customers of the plugin. -cmake_minimum_required(VERSION 3.10) - -# Project-level configuration. -set(PROJECT_NAME "rust_lib_photos") -project(${PROJECT_NAME} LANGUAGES CXX) - -include("../cargokit/cmake/cargokit.cmake") -apply_cargokit(${PROJECT_NAME} ../../rust rust_lib_photos "") - -# List of absolute paths to libraries that should be bundled with the plugin. -# This list could contain prebuilt libraries, or libraries created by an -# external build triggered from this build file. -set(rust_lib_photos_bundled_libraries - "${${PROJECT_NAME}_cargokit_lib}" - PARENT_SCOPE -) diff --git a/mobile/apps/photos/rust_builder/macos/Classes/dummy_file.c b/mobile/apps/photos/rust_builder/macos/Classes/dummy_file.c deleted file mode 100644 index e06dab9968..0000000000 --- a/mobile/apps/photos/rust_builder/macos/Classes/dummy_file.c +++ /dev/null @@ -1 +0,0 @@ -// This is an empty file to force CocoaPods to create a framework. diff --git a/mobile/apps/photos/rust_builder/macos/rust_lib_photos.podspec b/mobile/apps/photos/rust_builder/macos/rust_lib_photos.podspec deleted file mode 100644 index e929e5848e..0000000000 --- a/mobile/apps/photos/rust_builder/macos/rust_lib_photos.podspec +++ /dev/null @@ -1,44 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint rust_lib_photos.podspec` to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'rust_lib_photos' - s.version = '0.0.1' - s.summary = 'A new Flutter FFI plugin project.' - s.description = <<-DESC -A new Flutter FFI plugin project. - DESC - s.homepage = 'http://example.com' - s.license = { :file => '../LICENSE' } - s.author = { 'Your Company' => 'email@example.com' } - - # This will ensure the source files in Classes/ are included in the native - # builds of apps using this FFI plugin. Podspec does not support relative - # paths, so Classes contains a forwarder C file that relatively imports - # `../src/*` so that the C sources can be shared among all target platforms. - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.dependency 'FlutterMacOS' - - s.platform = :osx, '10.11' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } - s.swift_version = '5.0' - - s.script_phase = { - :name => 'Build Rust library', - # First argument is relative path to the `rust` folder, second is name of rust library - :script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../../rust rust_lib_photos', - :execution_position => :before_compile, - :input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'], - # Let XCode know that the static library referenced in -force_load below is - # created by this build step. - :output_files => ["${BUILT_PRODUCTS_DIR}/librust_lib_photos.a"], - } - s.pod_target_xcconfig = { - 'DEFINES_MODULE' => 'YES', - # Flutter.framework does not contain a i386 slice. - 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', - 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/librust_lib_photos.a -lc++', - } -end \ No newline at end of file diff --git a/mobile/apps/photos/rust_builder/pubspec.yaml b/mobile/apps/photos/rust_builder/pubspec.yaml deleted file mode 100644 index 2be7b960ab..0000000000 --- a/mobile/apps/photos/rust_builder/pubspec.yaml +++ /dev/null @@ -1,34 +0,0 @@ -name: rust_lib_photos -description: "Utility to build Rust code" -version: 0.0.1 -publish_to: none - -environment: - sdk: ">=3.3.0 <4.0.0" - flutter: ">=3.3.0" - -dependencies: - flutter: - sdk: flutter - plugin_platform_interface: ^2.0.2 - -dev_dependencies: - ffi: ^2.0.2 - ffigen: ^11.0.0 - flutter_lints: ^2.0.0 - flutter_test: - sdk: flutter - -flutter: - plugin: - platforms: - android: - ffiPlugin: true - ios: - ffiPlugin: true - linux: - ffiPlugin: true - macos: - ffiPlugin: true - windows: - ffiPlugin: true diff --git a/mobile/apps/photos/rust_builder/windows/.gitignore b/mobile/apps/photos/rust_builder/windows/.gitignore deleted file mode 100644 index b3eb2be169..0000000000 --- a/mobile/apps/photos/rust_builder/windows/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -flutter/ - -# Visual Studio user-specific files. -*.suo -*.user -*.userosscache -*.sln.docstates - -# Visual Studio build-related files. -x64/ -x86/ - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ diff --git a/mobile/apps/photos/rust_builder/windows/CMakeLists.txt b/mobile/apps/photos/rust_builder/windows/CMakeLists.txt deleted file mode 100644 index 4640016d5c..0000000000 --- a/mobile/apps/photos/rust_builder/windows/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -# The Flutter tooling requires that developers have a version of Visual Studio -# installed that includes CMake 3.14 or later. You should not increase this -# version, as doing so will cause the plugin to fail to compile for some -# customers of the plugin. -cmake_minimum_required(VERSION 3.14) - -# Project-level configuration. -set(PROJECT_NAME "rust_lib_photos") -project(${PROJECT_NAME} LANGUAGES CXX) - -include("../cargokit/cmake/cargokit.cmake") -apply_cargokit(${PROJECT_NAME} ../../../../../../rust rust_lib_photos "") - -# List of absolute paths to libraries that should be bundled with the plugin. -# This list could contain prebuilt libraries, or libraries created by an -# external build triggered from this build file. -set(rust_lib_photos_bundled_libraries - "${${PROJECT_NAME}_cargokit_lib}" - PARENT_SCOPE -) From 38ed141d0e4b4dec41aa0d522b639f90a2dadfab Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 14 Aug 2025 13:05:43 +0530 Subject: [PATCH 149/156] Re-add rust builder --- mobile/apps/photos/rust_builder/.gitignore | 29 ++ mobile/apps/photos/rust_builder/README.md | 1 + .../photos/rust_builder/android/.gitignore | 9 + .../photos/rust_builder/android/build.gradle | 56 +++ .../rust_builder/android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 3 + .../photos/rust_builder/cargokit/.gitignore | 4 + .../apps/photos/rust_builder/cargokit/LICENSE | 42 ++ .../apps/photos/rust_builder/cargokit/README | 11 + .../photos/rust_builder/cargokit/build_pod.sh | 58 +++ .../cargokit/build_tool/README.md | 5 + .../cargokit/build_tool/analysis_options.yaml | 34 ++ .../cargokit/build_tool/bin/build_tool.dart | 8 + .../cargokit/build_tool/lib/build_tool.dart | 8 + .../lib/src/android_environment.dart | 195 ++++++++ .../lib/src/artifacts_provider.dart | 266 ++++++++++ .../build_tool/lib/src/build_cmake.dart | 40 ++ .../build_tool/lib/src/build_gradle.dart | 49 ++ .../build_tool/lib/src/build_pod.dart | 89 ++++ .../build_tool/lib/src/build_tool.dart | 271 +++++++++++ .../cargokit/build_tool/lib/src/builder.dart | 198 ++++++++ .../cargokit/build_tool/lib/src/cargo.dart | 48 ++ .../build_tool/lib/src/crate_hash.dart | 124 +++++ .../build_tool/lib/src/environment.dart | 68 +++ .../cargokit/build_tool/lib/src/logging.dart | 52 ++ .../cargokit/build_tool/lib/src/options.dart | 309 ++++++++++++ .../lib/src/precompile_binaries.dart | 202 ++++++++ .../cargokit/build_tool/lib/src/rustup.dart | 136 ++++++ .../cargokit/build_tool/lib/src/target.dart | 140 ++++++ .../cargokit/build_tool/lib/src/util.dart | 172 +++++++ .../build_tool/lib/src/verify_binaries.dart | 84 ++++ .../cargokit/build_tool/pubspec.lock | 453 ++++++++++++++++++ .../cargokit/build_tool/pubspec.yaml | 33 ++ .../cargokit/cmake/cargokit.cmake | 99 ++++ .../cargokit/cmake/resolve_symlinks.ps1 | 27 ++ .../cargokit/gradle/plugin.gradle | 179 +++++++ .../rust_builder/cargokit/run_build_tool.cmd | 91 ++++ .../rust_builder/cargokit/run_build_tool.sh | 94 ++++ .../rust_builder/ios/Classes/dummy_file.c | 1 + .../rust_builder/ios/rust_lib_photos.podspec | 45 ++ .../photos/rust_builder/linux/CMakeLists.txt | 19 + .../rust_builder/macos/Classes/dummy_file.c | 1 + .../macos/rust_lib_photos.podspec | 44 ++ mobile/apps/photos/rust_builder/pubspec.yaml | 34 ++ .../photos/rust_builder/windows/.gitignore | 17 + .../rust_builder/windows/CMakeLists.txt | 20 + 46 files changed, 3869 insertions(+) create mode 100644 mobile/apps/photos/rust_builder/.gitignore create mode 100644 mobile/apps/photos/rust_builder/README.md create mode 100644 mobile/apps/photos/rust_builder/android/.gitignore create mode 100644 mobile/apps/photos/rust_builder/android/build.gradle create mode 100644 mobile/apps/photos/rust_builder/android/settings.gradle create mode 100644 mobile/apps/photos/rust_builder/android/src/main/AndroidManifest.xml create mode 100644 mobile/apps/photos/rust_builder/cargokit/.gitignore create mode 100644 mobile/apps/photos/rust_builder/cargokit/LICENSE create mode 100644 mobile/apps/photos/rust_builder/cargokit/README create mode 100755 mobile/apps/photos/rust_builder/cargokit/build_pod.sh create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/README.md create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/analysis_options.yaml create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/bin/build_tool.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/build_tool.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/android_environment.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_pod.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_tool.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/builder.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/cargo.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/environment.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/logging.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/options.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/rustup.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/target.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/util.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.lock create mode 100644 mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.yaml create mode 100644 mobile/apps/photos/rust_builder/cargokit/cmake/cargokit.cmake create mode 100644 mobile/apps/photos/rust_builder/cargokit/cmake/resolve_symlinks.ps1 create mode 100644 mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle create mode 100755 mobile/apps/photos/rust_builder/cargokit/run_build_tool.cmd create mode 100755 mobile/apps/photos/rust_builder/cargokit/run_build_tool.sh create mode 100644 mobile/apps/photos/rust_builder/ios/Classes/dummy_file.c create mode 100644 mobile/apps/photos/rust_builder/ios/rust_lib_photos.podspec create mode 100644 mobile/apps/photos/rust_builder/linux/CMakeLists.txt create mode 100644 mobile/apps/photos/rust_builder/macos/Classes/dummy_file.c create mode 100644 mobile/apps/photos/rust_builder/macos/rust_lib_photos.podspec create mode 100644 mobile/apps/photos/rust_builder/pubspec.yaml create mode 100644 mobile/apps/photos/rust_builder/windows/.gitignore create mode 100644 mobile/apps/photos/rust_builder/windows/CMakeLists.txt diff --git a/mobile/apps/photos/rust_builder/.gitignore b/mobile/apps/photos/rust_builder/.gitignore new file mode 100644 index 0000000000..ac5aa9893e --- /dev/null +++ b/mobile/apps/photos/rust_builder/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/mobile/apps/photos/rust_builder/README.md b/mobile/apps/photos/rust_builder/README.md new file mode 100644 index 0000000000..922615f9c1 --- /dev/null +++ b/mobile/apps/photos/rust_builder/README.md @@ -0,0 +1 @@ +Please ignore this folder, which is just glue to build Rust with Flutter. \ No newline at end of file diff --git a/mobile/apps/photos/rust_builder/android/.gitignore b/mobile/apps/photos/rust_builder/android/.gitignore new file mode 100644 index 0000000000..161bdcdaf8 --- /dev/null +++ b/mobile/apps/photos/rust_builder/android/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.cxx diff --git a/mobile/apps/photos/rust_builder/android/build.gradle b/mobile/apps/photos/rust_builder/android/build.gradle new file mode 100644 index 0000000000..5239261a2e --- /dev/null +++ b/mobile/apps/photos/rust_builder/android/build.gradle @@ -0,0 +1,56 @@ +// The Android Gradle Plugin builds the native code with the Android NDK. + +group 'com.flutter_rust_bridge.rust_lib_photos' +version '1.0' + +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + // The Android Gradle Plugin knows how to build native code with the NDK. + classpath 'com.android.tools.build:gradle:7.3.0' + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' + +android { + if (project.android.hasProperty("namespace")) { + namespace 'com.flutter_rust_bridge.rust_lib_photos' + } + + // Bumping the plugin compileSdkVersion requires all clients of this plugin + // to bump the version in their app. + compileSdkVersion 33 + + // Use the NDK version + // declared in /android/app/build.gradle file of the Flutter project. + // Replace it with a version number if this plugin requires a specfic NDK version. + // (e.g. ndkVersion "23.1.7779620") + ndkVersion android.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + minSdkVersion 19 + } +} + +apply from: "../cargokit/gradle/plugin.gradle" +cargokit { + manifestDir = "../../rust" + libname = "rust_lib_photos" +} diff --git a/mobile/apps/photos/rust_builder/android/settings.gradle b/mobile/apps/photos/rust_builder/android/settings.gradle new file mode 100644 index 0000000000..48e7a64ca7 --- /dev/null +++ b/mobile/apps/photos/rust_builder/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'rust_lib_photos' diff --git a/mobile/apps/photos/rust_builder/android/src/main/AndroidManifest.xml b/mobile/apps/photos/rust_builder/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..b80e4df687 --- /dev/null +++ b/mobile/apps/photos/rust_builder/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/mobile/apps/photos/rust_builder/cargokit/.gitignore b/mobile/apps/photos/rust_builder/cargokit/.gitignore new file mode 100644 index 0000000000..cf7bb868c0 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/.gitignore @@ -0,0 +1,4 @@ +target +.dart_tool +*.iml +!pubspec.lock diff --git a/mobile/apps/photos/rust_builder/cargokit/LICENSE b/mobile/apps/photos/rust_builder/cargokit/LICENSE new file mode 100644 index 0000000000..d33a5fea52 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/LICENSE @@ -0,0 +1,42 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +Copyright 2022 Matej Knopp + +================================================================================ + +MIT LICENSE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +================================================================================ + +APACHE LICENSE, VERSION 2.0 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/mobile/apps/photos/rust_builder/cargokit/README b/mobile/apps/photos/rust_builder/cargokit/README new file mode 100644 index 0000000000..398474dbc8 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/README @@ -0,0 +1,11 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +Experimental repository to provide glue for seamlessly integrating cargo build +with flutter plugins and packages. + +See https://matejknopp.com/post/flutter_plugin_in_rust_with_no_prebuilt_binaries/ +for a tutorial on how to use Cargokit. + +Example plugin available at https://github.com/irondash/hello_rust_ffi_plugin. + diff --git a/mobile/apps/photos/rust_builder/cargokit/build_pod.sh b/mobile/apps/photos/rust_builder/cargokit/build_pod.sh new file mode 100755 index 0000000000..ed0e0d987d --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_pod.sh @@ -0,0 +1,58 @@ +#!/bin/sh +set -e + +BASEDIR=$(dirname "$0") + +# Workaround for https://github.com/dart-lang/pub/issues/4010 +BASEDIR=$(cd "$BASEDIR" ; pwd -P) + +# Remove XCode SDK from path. Otherwise this breaks tool compilation when building iOS project +NEW_PATH=`echo $PATH | tr ":" "\n" | grep -v "Contents/Developer/" | tr "\n" ":"` + +export PATH=${NEW_PATH%?} # remove trailing : + +env + +# Platform name (macosx, iphoneos, iphonesimulator) +export CARGOKIT_DARWIN_PLATFORM_NAME=$PLATFORM_NAME + +# Arctive architectures (arm64, armv7, x86_64), space separated. +export CARGOKIT_DARWIN_ARCHS=$ARCHS + +# Current build configuration (Debug, Release) +export CARGOKIT_CONFIGURATION=$CONFIGURATION + +# Path to directory containing Cargo.toml. +export CARGOKIT_MANIFEST_DIR=$PODS_TARGET_SRCROOT/$1 + +# Temporary directory for build artifacts. +export CARGOKIT_TARGET_TEMP_DIR=$TARGET_TEMP_DIR + +# Output directory for final artifacts. +export CARGOKIT_OUTPUT_DIR=$PODS_CONFIGURATION_BUILD_DIR/$PRODUCT_NAME + +# Directory to store built tool artifacts. +export CARGOKIT_TOOL_TEMP_DIR=$TARGET_TEMP_DIR/build_tool + +# Directory inside root project. Not necessarily the top level directory of root project. +export CARGOKIT_ROOT_PROJECT_DIR=$SRCROOT + +FLUTTER_EXPORT_BUILD_ENVIRONMENT=( + "$PODS_ROOT/../Flutter/ephemeral/flutter_export_environment.sh" # macOS + "$PODS_ROOT/../Flutter/flutter_export_environment.sh" # iOS +) + +for path in "${FLUTTER_EXPORT_BUILD_ENVIRONMENT[@]}" +do + if [[ -f "$path" ]]; then + source "$path" + fi +done + +sh "$BASEDIR/run_build_tool.sh" build-pod "$@" + +# Make a symlink from built framework to phony file, which will be used as input to +# build script. This should force rebuild (podspec currently doesn't support alwaysOutOfDate +# attribute on custom build phase) +ln -fs "$OBJROOT/XCBuildData/build.db" "${BUILT_PRODUCTS_DIR}/cargokit_phony" +ln -fs "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}" "${BUILT_PRODUCTS_DIR}/cargokit_phony_out" diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/README.md b/mobile/apps/photos/rust_builder/cargokit/build_tool/README.md new file mode 100644 index 0000000000..a878c27964 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/README.md @@ -0,0 +1,5 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +A sample command-line application with an entrypoint in `bin/`, library code +in `lib/`, and example unit test in `test/`. diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/analysis_options.yaml b/mobile/apps/photos/rust_builder/cargokit/build_tool/analysis_options.yaml new file mode 100644 index 0000000000..0e16a8b092 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/analysis_options.yaml @@ -0,0 +1,34 @@ +# This is copied from Cargokit (which is the official way to use it currently) +# Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +linter: + rules: + - prefer_relative_imports + - directives_ordering + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/bin/build_tool.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/bin/build_tool.dart new file mode 100644 index 0000000000..268eb524dc --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/bin/build_tool.dart @@ -0,0 +1,8 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'package:build_tool/build_tool.dart' as build_tool; + +void main(List arguments) { + build_tool.runMain(arguments); +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/build_tool.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/build_tool.dart new file mode 100644 index 0000000000..7c1bb750a4 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/build_tool.dart @@ -0,0 +1,8 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'src/build_tool.dart' as build_tool; + +Future runMain(List args) async { + return build_tool.runMain(args); +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/android_environment.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/android_environment.dart new file mode 100644 index 0000000000..15fc9eedac --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/android_environment.dart @@ -0,0 +1,195 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; +import 'dart:isolate'; +import 'dart:math' as math; + +import 'package:collection/collection.dart'; +import 'package:path/path.dart' as path; +import 'package:version/version.dart'; + +import 'target.dart'; +import 'util.dart'; + +class AndroidEnvironment { + AndroidEnvironment({ + required this.sdkPath, + required this.ndkVersion, + required this.minSdkVersion, + required this.targetTempDir, + required this.target, + }); + + static void clangLinkerWrapper(List args) { + final clang = Platform.environment['_CARGOKIT_NDK_LINK_CLANG']; + if (clang == null) { + throw Exception( + "cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_CLANG env var"); + } + final target = Platform.environment['_CARGOKIT_NDK_LINK_TARGET']; + if (target == null) { + throw Exception( + "cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_TARGET env var"); + } + + runCommand(clang, [ + target, + ...args, + ]); + } + + /// Full path to Android SDK. + final String sdkPath; + + /// Full version of Android NDK. + final String ndkVersion; + + /// Minimum supported SDK version. + final int minSdkVersion; + + /// Target directory for build artifacts. + final String targetTempDir; + + /// Target being built. + final Target target; + + bool ndkIsInstalled() { + final ndkPath = path.join(sdkPath, 'ndk', ndkVersion); + final ndkPackageXml = File(path.join(ndkPath, 'package.xml')); + return ndkPackageXml.existsSync(); + } + + void installNdk({ + required String javaHome, + }) { + final sdkManagerExtension = Platform.isWindows ? '.bat' : ''; + final sdkManager = path.join( + sdkPath, + 'cmdline-tools', + 'latest', + 'bin', + 'sdkmanager$sdkManagerExtension', + ); + + log.info('Installing NDK $ndkVersion'); + runCommand(sdkManager, [ + '--install', + 'ndk;$ndkVersion', + ], environment: { + 'JAVA_HOME': javaHome, + }); + } + + Future> buildEnvironment() async { + final hostArch = Platform.isMacOS + ? "darwin-x86_64" + : (Platform.isLinux ? "linux-x86_64" : "windows-x86_64"); + + final ndkPath = path.join(sdkPath, 'ndk', ndkVersion); + final toolchainPath = path.join( + ndkPath, + 'toolchains', + 'llvm', + 'prebuilt', + hostArch, + 'bin', + ); + + final minSdkVersion = + math.max(target.androidMinSdkVersion!, this.minSdkVersion); + + final exe = Platform.isWindows ? '.exe' : ''; + + final arKey = 'AR_${target.rust}'; + final arValue = ['${target.rust}-ar', 'llvm-ar', 'llvm-ar.exe'] + .map((e) => path.join(toolchainPath, e)) + .firstWhereOrNull((element) => File(element).existsSync()); + if (arValue == null) { + throw Exception('Failed to find ar for $target in $toolchainPath'); + } + + final targetArg = '--target=${target.rust}$minSdkVersion'; + + final ccKey = 'CC_${target.rust}'; + final ccValue = path.join(toolchainPath, 'clang$exe'); + final cfFlagsKey = 'CFLAGS_${target.rust}'; + final cFlagsValue = targetArg; + + final cxxKey = 'CXX_${target.rust}'; + final cxxValue = path.join(toolchainPath, 'clang++$exe'); + final cxxFlagsKey = 'CXXFLAGS_${target.rust}'; + final cxxFlagsValue = targetArg; + + final linkerKey = + 'cargo_target_${target.rust.replaceAll('-', '_')}_linker'.toUpperCase(); + + final ranlibKey = 'RANLIB_${target.rust}'; + final ranlibValue = path.join(toolchainPath, 'llvm-ranlib$exe'); + + final ndkVersionParsed = Version.parse(ndkVersion); + final rustFlagsKey = 'CARGO_ENCODED_RUSTFLAGS'; + final rustFlagsValue = _libGccWorkaround(targetTempDir, ndkVersionParsed); + + final runRustTool = + Platform.isWindows ? 'run_build_tool.cmd' : 'run_build_tool.sh'; + + final packagePath = (await Isolate.resolvePackageUri( + Uri.parse('package:build_tool/buildtool.dart')))! + .toFilePath(); + final selfPath = path.canonicalize(path.join( + packagePath, + '..', + '..', + '..', + runRustTool, + )); + + // Make sure that run_build_tool is working properly even initially launched directly + // through dart run. + final toolTempDir = + Platform.environment['CARGOKIT_TOOL_TEMP_DIR'] ?? targetTempDir; + + return { + arKey: arValue, + ccKey: ccValue, + cfFlagsKey: cFlagsValue, + cxxKey: cxxValue, + cxxFlagsKey: cxxFlagsValue, + ranlibKey: ranlibValue, + rustFlagsKey: rustFlagsValue, + linkerKey: selfPath, + // Recognized by main() so we know when we're acting as a wrapper + '_CARGOKIT_NDK_LINK_TARGET': targetArg, + '_CARGOKIT_NDK_LINK_CLANG': ccValue, + 'CARGOKIT_TOOL_TEMP_DIR': toolTempDir, + }; + } + + // Workaround for libgcc missing in NDK23, inspired by cargo-ndk + String _libGccWorkaround(String buildDir, Version ndkVersion) { + final workaroundDir = path.join( + buildDir, + 'cargokit', + 'libgcc_workaround', + '${ndkVersion.major}', + ); + Directory(workaroundDir).createSync(recursive: true); + if (ndkVersion.major >= 23) { + File(path.join(workaroundDir, 'libgcc.a')) + .writeAsStringSync('INPUT(-lunwind)'); + } else { + // Other way around, untested, forward libgcc.a from libunwind once Rust + // gets updated for NDK23+. + File(path.join(workaroundDir, 'libunwind.a')) + .writeAsStringSync('INPUT(-lgcc)'); + } + + var rustFlags = Platform.environment['CARGO_ENCODED_RUSTFLAGS'] ?? ''; + if (rustFlags.isNotEmpty) { + rustFlags = '$rustFlags\x1f'; + } + rustFlags = '$rustFlags-L\x1f$workaroundDir'; + return rustFlags; + } +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart new file mode 100644 index 0000000000..e608cece73 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart @@ -0,0 +1,266 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:http/http.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'builder.dart'; +import 'crate_hash.dart'; +import 'options.dart'; +import 'precompile_binaries.dart'; +import 'rustup.dart'; +import 'target.dart'; + +class Artifact { + /// File system location of the artifact. + final String path; + + /// Actual file name that the artifact should have in destination folder. + final String finalFileName; + + AritifactType get type { + if (finalFileName.endsWith('.dll') || + finalFileName.endsWith('.dll.lib') || + finalFileName.endsWith('.pdb') || + finalFileName.endsWith('.so') || + finalFileName.endsWith('.dylib')) { + return AritifactType.dylib; + } else if (finalFileName.endsWith('.lib') || finalFileName.endsWith('.a')) { + return AritifactType.staticlib; + } else { + throw Exception('Unknown artifact type for $finalFileName'); + } + } + + Artifact({ + required this.path, + required this.finalFileName, + }); +} + +final _log = Logger('artifacts_provider'); + +class ArtifactProvider { + ArtifactProvider({ + required this.environment, + required this.userOptions, + }); + + final BuildEnvironment environment; + final CargokitUserOptions userOptions; + + Future>> getArtifacts(List targets) async { + final result = await _getPrecompiledArtifacts(targets); + + final pendingTargets = List.of(targets); + pendingTargets.removeWhere((element) => result.containsKey(element)); + + if (pendingTargets.isEmpty) { + return result; + } + + final rustup = Rustup(); + for (final target in targets) { + final builder = RustBuilder(target: target, environment: environment); + builder.prepare(rustup); + _log.info('Building ${environment.crateInfo.packageName} for $target'); + final targetDir = await builder.build(); + // For local build accept both static and dynamic libraries. + final artifactNames = { + ...getArtifactNames( + target: target, + libraryName: environment.crateInfo.packageName, + aritifactType: AritifactType.dylib, + remote: false, + ), + ...getArtifactNames( + target: target, + libraryName: environment.crateInfo.packageName, + aritifactType: AritifactType.staticlib, + remote: false, + ) + }; + final artifacts = artifactNames + .map((artifactName) => Artifact( + path: path.join(targetDir, artifactName), + finalFileName: artifactName, + )) + .where((element) => File(element.path).existsSync()) + .toList(); + result[target] = artifacts; + } + return result; + } + + Future>> _getPrecompiledArtifacts( + List targets) async { + if (userOptions.usePrecompiledBinaries == false) { + _log.info('Precompiled binaries are disabled'); + return {}; + } + if (environment.crateOptions.precompiledBinaries == null) { + _log.fine('Precompiled binaries not enabled for this crate'); + return {}; + } + + final start = Stopwatch()..start(); + final crateHash = CrateHash.compute(environment.manifestDir, + tempStorage: environment.targetTempDir); + _log.fine( + 'Computed crate hash $crateHash in ${start.elapsedMilliseconds}ms'); + + final downloadedArtifactsDir = + path.join(environment.targetTempDir, 'precompiled', crateHash); + Directory(downloadedArtifactsDir).createSync(recursive: true); + + final res = >{}; + + for (final target in targets) { + final requiredArtifacts = getArtifactNames( + target: target, + libraryName: environment.crateInfo.packageName, + remote: true, + ); + final artifactsForTarget = []; + + for (final artifact in requiredArtifacts) { + final fileName = PrecompileBinaries.fileName(target, artifact); + final downloadedPath = path.join(downloadedArtifactsDir, fileName); + if (!File(downloadedPath).existsSync()) { + final signatureFileName = + PrecompileBinaries.signatureFileName(target, artifact); + await _tryDownloadArtifacts( + crateHash: crateHash, + fileName: fileName, + signatureFileName: signatureFileName, + finalPath: downloadedPath, + ); + } + if (File(downloadedPath).existsSync()) { + artifactsForTarget.add(Artifact( + path: downloadedPath, + finalFileName: artifact, + )); + } else { + break; + } + } + + // Only provide complete set of artifacts. + if (artifactsForTarget.length == requiredArtifacts.length) { + _log.fine('Found precompiled artifacts for $target'); + res[target] = artifactsForTarget; + } + } + + return res; + } + + static Future _get(Uri url, {Map? headers}) async { + int attempt = 0; + const maxAttempts = 10; + while (true) { + try { + return await get(url, headers: headers); + } on SocketException catch (e) { + // Try to detect reset by peer error and retry. + if (attempt++ < maxAttempts && + (e.osError?.errorCode == 54 || e.osError?.errorCode == 10054)) { + _log.severe( + 'Failed to download $url: $e, attempt $attempt of $maxAttempts, will retry...'); + await Future.delayed(Duration(seconds: 1)); + continue; + } else { + rethrow; + } + } + } + } + + Future _tryDownloadArtifacts({ + required String crateHash, + required String fileName, + required String signatureFileName, + required String finalPath, + }) async { + final precompiledBinaries = environment.crateOptions.precompiledBinaries!; + final prefix = precompiledBinaries.uriPrefix; + final url = Uri.parse('$prefix$crateHash/$fileName'); + final signatureUrl = Uri.parse('$prefix$crateHash/$signatureFileName'); + _log.fine('Downloading signature from $signatureUrl'); + final signature = await _get(signatureUrl); + if (signature.statusCode == 404) { + _log.warning( + 'Precompiled binaries not available for crate hash $crateHash ($fileName)'); + return; + } + if (signature.statusCode != 200) { + _log.severe( + 'Failed to download signature $signatureUrl: status ${signature.statusCode}'); + return; + } + _log.fine('Downloading binary from $url'); + final res = await _get(url); + if (res.statusCode != 200) { + _log.severe('Failed to download binary $url: status ${res.statusCode}'); + return; + } + if (verify( + precompiledBinaries.publicKey, res.bodyBytes, signature.bodyBytes)) { + File(finalPath).writeAsBytesSync(res.bodyBytes); + } else { + _log.shout('Signature verification failed! Ignoring binary.'); + } + } +} + +enum AritifactType { + staticlib, + dylib, +} + +AritifactType artifactTypeForTarget(Target target) { + if (target.darwinPlatform != null) { + return AritifactType.staticlib; + } else { + return AritifactType.dylib; + } +} + +List getArtifactNames({ + required Target target, + required String libraryName, + required bool remote, + AritifactType? aritifactType, +}) { + aritifactType ??= artifactTypeForTarget(target); + if (target.darwinArch != null) { + if (aritifactType == AritifactType.staticlib) { + return ['lib$libraryName.a']; + } else { + return ['lib$libraryName.dylib']; + } + } else if (target.rust.contains('-windows-')) { + if (aritifactType == AritifactType.staticlib) { + return ['$libraryName.lib']; + } else { + return [ + '$libraryName.dll', + '$libraryName.dll.lib', + if (!remote) '$libraryName.pdb' + ]; + } + } else if (target.rust.contains('-linux-')) { + if (aritifactType == AritifactType.staticlib) { + return ['lib$libraryName.a']; + } else { + return ['lib$libraryName.so']; + } + } else { + throw Exception("Unsupported target: ${target.rust}"); + } +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart new file mode 100644 index 0000000000..6f3b2a4ec1 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart @@ -0,0 +1,40 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:path/path.dart' as path; + +import 'artifacts_provider.dart'; +import 'builder.dart'; +import 'environment.dart'; +import 'options.dart'; +import 'target.dart'; + +class BuildCMake { + final CargokitUserOptions userOptions; + + BuildCMake({required this.userOptions}); + + Future build() async { + final targetPlatform = Environment.targetPlatform; + final target = Target.forFlutterName(Environment.targetPlatform); + if (target == null) { + throw Exception("Unknown target platform: $targetPlatform"); + } + + final environment = BuildEnvironment.fromEnvironment(isAndroid: false); + final provider = + ArtifactProvider(environment: environment, userOptions: userOptions); + final artifacts = await provider.getArtifacts([target]); + + final libs = artifacts[target]!; + + for (final lib in libs) { + if (lib.type == AritifactType.dylib) { + File(lib.path) + .copySync(path.join(Environment.outputDir, lib.finalFileName)); + } + } + } +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart new file mode 100644 index 0000000000..7e61fcbb7c --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart @@ -0,0 +1,49 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'artifacts_provider.dart'; +import 'builder.dart'; +import 'environment.dart'; +import 'options.dart'; +import 'target.dart'; + +final log = Logger('build_gradle'); + +class BuildGradle { + BuildGradle({required this.userOptions}); + + final CargokitUserOptions userOptions; + + Future build() async { + final targets = Environment.targetPlatforms.map((arch) { + final target = Target.forFlutterName(arch); + if (target == null) { + throw Exception( + "Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}"); + } + return target; + }).toList(); + + final environment = BuildEnvironment.fromEnvironment(isAndroid: true); + final provider = + ArtifactProvider(environment: environment, userOptions: userOptions); + final artifacts = await provider.getArtifacts(targets); + + for (final target in targets) { + final libs = artifacts[target]!; + final outputDir = path.join(Environment.outputDir, target.android!); + Directory(outputDir).createSync(recursive: true); + + for (final lib in libs) { + if (lib.type == AritifactType.dylib) { + File(lib.path).copySync(path.join(outputDir, lib.finalFileName)); + } + } + } + } +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_pod.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_pod.dart new file mode 100644 index 0000000000..8a9c0db5de --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_pod.dart @@ -0,0 +1,89 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:path/path.dart' as path; + +import 'artifacts_provider.dart'; +import 'builder.dart'; +import 'environment.dart'; +import 'options.dart'; +import 'target.dart'; +import 'util.dart'; + +class BuildPod { + BuildPod({required this.userOptions}); + + final CargokitUserOptions userOptions; + + Future build() async { + final targets = Environment.darwinArchs.map((arch) { + final target = Target.forDarwin( + platformName: Environment.darwinPlatformName, darwinAarch: arch); + if (target == null) { + throw Exception( + "Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}"); + } + return target; + }).toList(); + + final environment = BuildEnvironment.fromEnvironment(isAndroid: false); + final provider = + ArtifactProvider(environment: environment, userOptions: userOptions); + final artifacts = await provider.getArtifacts(targets); + + void performLipo(String targetFile, Iterable sourceFiles) { + runCommand("lipo", [ + '-create', + ...sourceFiles, + '-output', + targetFile, + ]); + } + + final outputDir = Environment.outputDir; + + Directory(outputDir).createSync(recursive: true); + + final staticLibs = artifacts.values + .expand((element) => element) + .where((element) => element.type == AritifactType.staticlib) + .toList(); + final dynamicLibs = artifacts.values + .expand((element) => element) + .where((element) => element.type == AritifactType.dylib) + .toList(); + + final libName = environment.crateInfo.packageName; + + // If there is static lib, use it and link it with pod + if (staticLibs.isNotEmpty) { + final finalTargetFile = path.join(outputDir, "lib$libName.a"); + performLipo(finalTargetFile, staticLibs.map((e) => e.path)); + } else { + // Otherwise try to replace bundle dylib with our dylib + final bundlePaths = [ + '$libName.framework/Versions/A/$libName', + '$libName.framework/$libName', + ]; + + for (final bundlePath in bundlePaths) { + final targetFile = path.join(outputDir, bundlePath); + if (File(targetFile).existsSync()) { + performLipo(targetFile, dynamicLibs.map((e) => e.path)); + + // Replace absolute id with @rpath one so that it works properly + // when moved to Frameworks. + runCommand("install_name_tool", [ + '-id', + '@rpath/$bundlePath', + targetFile, + ]); + return; + } + } + throw Exception('Unable to find bundle for dynamic library'); + } + } +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_tool.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_tool.dart new file mode 100644 index 0000000000..c8f36981b5 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/build_tool.dart @@ -0,0 +1,271 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:args/command_runner.dart'; +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:github/github.dart'; +import 'package:hex/hex.dart'; +import 'package:logging/logging.dart'; + +import 'android_environment.dart'; +import 'build_cmake.dart'; +import 'build_gradle.dart'; +import 'build_pod.dart'; +import 'logging.dart'; +import 'options.dart'; +import 'precompile_binaries.dart'; +import 'target.dart'; +import 'util.dart'; +import 'verify_binaries.dart'; + +final log = Logger('build_tool'); + +abstract class BuildCommand extends Command { + Future runBuildCommand(CargokitUserOptions options); + + @override + Future run() async { + final options = CargokitUserOptions.load(); + + if (options.verboseLogging || + Platform.environment['CARGOKIT_VERBOSE'] == '1') { + enableVerboseLogging(); + } + + await runBuildCommand(options); + } +} + +class BuildPodCommand extends BuildCommand { + @override + final name = 'build-pod'; + + @override + final description = 'Build cocoa pod library'; + + @override + Future runBuildCommand(CargokitUserOptions options) async { + final build = BuildPod(userOptions: options); + await build.build(); + } +} + +class BuildGradleCommand extends BuildCommand { + @override + final name = 'build-gradle'; + + @override + final description = 'Build android library'; + + @override + Future runBuildCommand(CargokitUserOptions options) async { + final build = BuildGradle(userOptions: options); + await build.build(); + } +} + +class BuildCMakeCommand extends BuildCommand { + @override + final name = 'build-cmake'; + + @override + final description = 'Build CMake library'; + + @override + Future runBuildCommand(CargokitUserOptions options) async { + final build = BuildCMake(userOptions: options); + await build.build(); + } +} + +class GenKeyCommand extends Command { + @override + final name = 'gen-key'; + + @override + final description = 'Generate key pair for signing precompiled binaries'; + + @override + void run() { + final kp = generateKey(); + final private = HEX.encode(kp.privateKey.bytes); + final public = HEX.encode(kp.publicKey.bytes); + print("Private Key: $private"); + print("Public Key: $public"); + } +} + +class PrecompileBinariesCommand extends Command { + PrecompileBinariesCommand() { + argParser + ..addOption( + 'repository', + mandatory: true, + help: 'Github repository slug in format owner/name', + ) + ..addOption( + 'manifest-dir', + mandatory: true, + help: 'Directory containing Cargo.toml', + ) + ..addMultiOption('target', + help: 'Rust target triple of artifact to build.\n' + 'Can be specified multiple times or omitted in which case\n' + 'all targets for current platform will be built.') + ..addOption( + 'android-sdk-location', + help: 'Location of Android SDK (if available)', + ) + ..addOption( + 'android-ndk-version', + help: 'Android NDK version (if available)', + ) + ..addOption( + 'android-min-sdk-version', + help: 'Android minimum rquired version (if available)', + ) + ..addOption( + 'temp-dir', + help: 'Directory to store temporary build artifacts', + ) + ..addFlag( + "verbose", + abbr: "v", + defaultsTo: false, + help: "Enable verbose logging", + ); + } + + @override + final name = 'precompile-binaries'; + + @override + final description = 'Prebuild and upload binaries\n' + 'Private key must be passed through PRIVATE_KEY environment variable. ' + 'Use gen_key through generate priave key.\n' + 'Github token must be passed as GITHUB_TOKEN environment variable.\n'; + + @override + Future run() async { + final verbose = argResults!['verbose'] as bool; + if (verbose) { + enableVerboseLogging(); + } + + final privateKeyString = Platform.environment['PRIVATE_KEY']; + if (privateKeyString == null) { + throw ArgumentError('Missing PRIVATE_KEY environment variable'); + } + final githubToken = Platform.environment['GITHUB_TOKEN']; + if (githubToken == null) { + throw ArgumentError('Missing GITHUB_TOKEN environment variable'); + } + final privateKey = HEX.decode(privateKeyString); + if (privateKey.length != 64) { + throw ArgumentError('Private key must be 64 bytes long'); + } + final manifestDir = argResults!['manifest-dir'] as String; + if (!Directory(manifestDir).existsSync()) { + throw ArgumentError('Manifest directory does not exist: $manifestDir'); + } + String? androidMinSdkVersionString = + argResults!['android-min-sdk-version'] as String?; + int? androidMinSdkVersion; + if (androidMinSdkVersionString != null) { + androidMinSdkVersion = int.tryParse(androidMinSdkVersionString); + if (androidMinSdkVersion == null) { + throw ArgumentError( + 'Invalid android-min-sdk-version: $androidMinSdkVersionString'); + } + } + final targetStrigns = argResults!['target'] as List; + final targets = targetStrigns.map((target) { + final res = Target.forRustTriple(target); + if (res == null) { + throw ArgumentError('Invalid target: $target'); + } + return res; + }).toList(growable: false); + final precompileBinaries = PrecompileBinaries( + privateKey: PrivateKey(privateKey), + githubToken: githubToken, + manifestDir: manifestDir, + repositorySlug: RepositorySlug.full(argResults!['repository'] as String), + targets: targets, + androidSdkLocation: argResults!['android-sdk-location'] as String?, + androidNdkVersion: argResults!['android-ndk-version'] as String?, + androidMinSdkVersion: androidMinSdkVersion, + tempDir: argResults!['temp-dir'] as String?, + ); + + await precompileBinaries.run(); + } +} + +class VerifyBinariesCommand extends Command { + VerifyBinariesCommand() { + argParser.addOption( + 'manifest-dir', + mandatory: true, + help: 'Directory containing Cargo.toml', + ); + } + + @override + final name = "verify-binaries"; + + @override + final description = 'Verifies published binaries\n' + 'Checks whether there is a binary published for each targets\n' + 'and checks the signature.'; + + @override + Future run() async { + final manifestDir = argResults!['manifest-dir'] as String; + final verifyBinaries = VerifyBinaries( + manifestDir: manifestDir, + ); + await verifyBinaries.run(); + } +} + +Future runMain(List args) async { + try { + // Init logging before options are loaded + initLogging(); + + if (Platform.environment['_CARGOKIT_NDK_LINK_TARGET'] != null) { + return AndroidEnvironment.clangLinkerWrapper(args); + } + + final runner = CommandRunner('build_tool', 'Cargokit built_tool') + ..addCommand(BuildPodCommand()) + ..addCommand(BuildGradleCommand()) + ..addCommand(BuildCMakeCommand()) + ..addCommand(GenKeyCommand()) + ..addCommand(PrecompileBinariesCommand()) + ..addCommand(VerifyBinariesCommand()); + + await runner.run(args); + } on ArgumentError catch (e) { + stderr.writeln(e.toString()); + exit(1); + } catch (e, s) { + log.severe(kDoubleSeparator); + log.severe('Cargokit BuildTool failed with error:'); + log.severe(kSeparator); + log.severe(e); + // This tells user to install Rust, there's no need to pollute the log with + // stack trace. + if (e is! RustupNotFoundException) { + log.severe(kSeparator); + log.severe(s); + log.severe(kSeparator); + log.severe('BuildTool arguments: $args'); + } + log.severe(kDoubleSeparator); + exit(1); + } +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/builder.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/builder.dart new file mode 100644 index 0000000000..84c46e4f54 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/builder.dart @@ -0,0 +1,198 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'package:collection/collection.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'android_environment.dart'; +import 'cargo.dart'; +import 'environment.dart'; +import 'options.dart'; +import 'rustup.dart'; +import 'target.dart'; +import 'util.dart'; + +final _log = Logger('builder'); + +enum BuildConfiguration { + debug, + release, + profile, +} + +extension on BuildConfiguration { + bool get isDebug => this == BuildConfiguration.debug; + String get rustName => switch (this) { + BuildConfiguration.debug => 'debug', + BuildConfiguration.release => 'release', + BuildConfiguration.profile => 'release', + }; +} + +class BuildException implements Exception { + final String message; + + BuildException(this.message); + + @override + String toString() { + return 'BuildException: $message'; + } +} + +class BuildEnvironment { + final BuildConfiguration configuration; + final CargokitCrateOptions crateOptions; + final String targetTempDir; + final String manifestDir; + final CrateInfo crateInfo; + + final bool isAndroid; + final String? androidSdkPath; + final String? androidNdkVersion; + final int? androidMinSdkVersion; + final String? javaHome; + + BuildEnvironment({ + required this.configuration, + required this.crateOptions, + required this.targetTempDir, + required this.manifestDir, + required this.crateInfo, + required this.isAndroid, + this.androidSdkPath, + this.androidNdkVersion, + this.androidMinSdkVersion, + this.javaHome, + }); + + static BuildConfiguration parseBuildConfiguration(String value) { + // XCode configuration adds the flavor to configuration name. + final firstSegment = value.split('-').first; + final buildConfiguration = BuildConfiguration.values.firstWhereOrNull( + (e) => e.name == firstSegment, + ); + if (buildConfiguration == null) { + _log.warning('Unknown build configuraiton $value, will assume release'); + return BuildConfiguration.release; + } + return buildConfiguration; + } + + static BuildEnvironment fromEnvironment({ + required bool isAndroid, + }) { + final buildConfiguration = + parseBuildConfiguration(Environment.configuration); + final manifestDir = Environment.manifestDir; + final crateOptions = CargokitCrateOptions.load( + manifestDir: manifestDir, + ); + final crateInfo = CrateInfo.load(manifestDir); + return BuildEnvironment( + configuration: buildConfiguration, + crateOptions: crateOptions, + targetTempDir: Environment.targetTempDir, + manifestDir: manifestDir, + crateInfo: crateInfo, + isAndroid: isAndroid, + androidSdkPath: isAndroid ? Environment.sdkPath : null, + androidNdkVersion: isAndroid ? Environment.ndkVersion : null, + androidMinSdkVersion: + isAndroid ? int.parse(Environment.minSdkVersion) : null, + javaHome: isAndroid ? Environment.javaHome : null, + ); + } +} + +class RustBuilder { + final Target target; + final BuildEnvironment environment; + + RustBuilder({ + required this.target, + required this.environment, + }); + + void prepare( + Rustup rustup, + ) { + final toolchain = _toolchain; + if (rustup.installedTargets(toolchain) == null) { + rustup.installToolchain(toolchain); + } + if (toolchain == 'nightly') { + rustup.installRustSrcForNightly(); + } + if (!rustup.installedTargets(toolchain)!.contains(target.rust)) { + rustup.installTarget(target.rust, toolchain: toolchain); + } + } + + CargoBuildOptions? get _buildOptions => + environment.crateOptions.cargo[environment.configuration]; + + String get _toolchain => _buildOptions?.toolchain.name ?? 'stable'; + + /// Returns the path of directory containing build artifacts. + Future build() async { + final extraArgs = _buildOptions?.flags ?? []; + final manifestPath = path.join(environment.manifestDir, 'Cargo.toml'); + runCommand( + 'rustup', + [ + 'run', + _toolchain, + 'cargo', + 'build', + ...extraArgs, + '--manifest-path', + manifestPath, + '-p', + environment.crateInfo.packageName, + if (!environment.configuration.isDebug) '--release', + '--target', + target.rust, + '--target-dir', + environment.targetTempDir, + ], + environment: await _buildEnvironment(), + ); + return path.join( + environment.targetTempDir, + target.rust, + environment.configuration.rustName, + ); + } + + Future> _buildEnvironment() async { + if (target.android == null) { + return {}; + } else { + final sdkPath = environment.androidSdkPath; + final ndkVersion = environment.androidNdkVersion; + final minSdkVersion = environment.androidMinSdkVersion; + if (sdkPath == null) { + throw BuildException('androidSdkPath is not set'); + } + if (ndkVersion == null) { + throw BuildException('androidNdkVersion is not set'); + } + if (minSdkVersion == null) { + throw BuildException('androidMinSdkVersion is not set'); + } + final env = AndroidEnvironment( + sdkPath: sdkPath, + ndkVersion: ndkVersion, + minSdkVersion: minSdkVersion, + targetTempDir: environment.targetTempDir, + target: target, + ); + if (!env.ndkIsInstalled() && environment.javaHome != null) { + env.installNdk(javaHome: environment.javaHome!); + } + return env.buildEnvironment(); + } + } +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/cargo.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/cargo.dart new file mode 100644 index 0000000000..0d8958ff2e --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/cargo.dart @@ -0,0 +1,48 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:path/path.dart' as path; +import 'package:toml/toml.dart'; + +class ManifestException { + ManifestException(this.message, {required this.fileName}); + + final String? fileName; + final String message; + + @override + String toString() { + if (fileName != null) { + return 'Failed to parse package manifest at $fileName: $message'; + } else { + return 'Failed to parse package manifest: $message'; + } + } +} + +class CrateInfo { + CrateInfo({required this.packageName}); + + final String packageName; + + static CrateInfo parseManifest(String manifest, {final String? fileName}) { + final toml = TomlDocument.parse(manifest); + final package = toml.toMap()['package']; + if (package == null) { + throw ManifestException('Missing package section', fileName: fileName); + } + final name = package['name']; + if (name == null) { + throw ManifestException('Missing package name', fileName: fileName); + } + return CrateInfo(packageName: name); + } + + static CrateInfo load(String manifestDir) { + final manifestFile = File(path.join(manifestDir, 'Cargo.toml')); + final manifest = manifestFile.readAsStringSync(); + return parseManifest(manifest, fileName: manifestFile.path); + } +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart new file mode 100644 index 0000000000..0c4d88d16b --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart @@ -0,0 +1,124 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:collection/collection.dart'; +import 'package:convert/convert.dart'; +import 'package:crypto/crypto.dart'; +import 'package:path/path.dart' as path; + +class CrateHash { + /// Computes a hash uniquely identifying crate content. This takes into account + /// content all all .rs files inside the src directory, as well as Cargo.toml, + /// Cargo.lock, build.rs and cargokit.yaml. + /// + /// If [tempStorage] is provided, computed hash is stored in a file in that directory + /// and reused on subsequent calls if the crate content hasn't changed. + static String compute(String manifestDir, {String? tempStorage}) { + return CrateHash._( + manifestDir: manifestDir, + tempStorage: tempStorage, + )._compute(); + } + + CrateHash._({ + required this.manifestDir, + required this.tempStorage, + }); + + String _compute() { + final files = getFiles(); + final tempStorage = this.tempStorage; + if (tempStorage != null) { + final quickHash = _computeQuickHash(files); + final quickHashFolder = Directory(path.join(tempStorage, 'crate_hash')); + quickHashFolder.createSync(recursive: true); + final quickHashFile = File(path.join(quickHashFolder.path, quickHash)); + if (quickHashFile.existsSync()) { + return quickHashFile.readAsStringSync(); + } + final hash = _computeHash(files); + quickHashFile.writeAsStringSync(hash); + return hash; + } else { + return _computeHash(files); + } + } + + /// Computes a quick hash based on files stat (without reading contents). This + /// is used to cache the real hash, which is slower to compute since it involves + /// reading every single file. + String _computeQuickHash(List files) { + final output = AccumulatorSink(); + final input = sha256.startChunkedConversion(output); + + final data = ByteData(8); + for (final file in files) { + input.add(utf8.encode(file.path)); + final stat = file.statSync(); + data.setUint64(0, stat.size); + input.add(data.buffer.asUint8List()); + data.setUint64(0, stat.modified.millisecondsSinceEpoch); + input.add(data.buffer.asUint8List()); + } + + input.close(); + return base64Url.encode(output.events.single.bytes); + } + + String _computeHash(List files) { + final output = AccumulatorSink(); + final input = sha256.startChunkedConversion(output); + + void addTextFile(File file) { + // text Files are hashed by lines in case we're dealing with github checkout + // that auto-converts line endings. + final splitter = LineSplitter(); + if (file.existsSync()) { + final data = file.readAsStringSync(); + final lines = splitter.convert(data); + for (final line in lines) { + input.add(utf8.encode(line)); + } + } + } + + for (final file in files) { + addTextFile(file); + } + + input.close(); + final res = output.events.single; + + // Truncate to 128bits. + final hash = res.bytes.sublist(0, 16); + return hex.encode(hash); + } + + List getFiles() { + final src = Directory(path.join(manifestDir, 'src')); + final files = src + .listSync(recursive: true, followLinks: false) + .whereType() + .toList(); + files.sortBy((element) => element.path); + void addFile(String relative) { + final file = File(path.join(manifestDir, relative)); + if (file.existsSync()) { + files.add(file); + } + } + + addFile('Cargo.toml'); + addFile('Cargo.lock'); + addFile('build.rs'); + addFile('cargokit.yaml'); + return files; + } + + final String manifestDir; + final String? tempStorage; +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/environment.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/environment.dart new file mode 100644 index 0000000000..996483a180 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/environment.dart @@ -0,0 +1,68 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +extension on String { + String resolveSymlink() => File(this).resolveSymbolicLinksSync(); +} + +class Environment { + /// Current build configuration (debug or release). + static String get configuration => + _getEnv("CARGOKIT_CONFIGURATION").toLowerCase(); + + static bool get isDebug => configuration == 'debug'; + static bool get isRelease => configuration == 'release'; + + /// Temporary directory where Rust build artifacts are placed. + static String get targetTempDir => _getEnv("CARGOKIT_TARGET_TEMP_DIR"); + + /// Final output directory where the build artifacts are placed. + static String get outputDir => _getEnvPath('CARGOKIT_OUTPUT_DIR'); + + /// Path to the crate manifest (containing Cargo.toml). + static String get manifestDir => _getEnvPath('CARGOKIT_MANIFEST_DIR'); + + /// Directory inside root project. Not necessarily root folder. Symlinks are + /// not resolved on purpose. + static String get rootProjectDir => _getEnv('CARGOKIT_ROOT_PROJECT_DIR'); + + // Pod + + /// Platform name (macosx, iphoneos, iphonesimulator). + static String get darwinPlatformName => + _getEnv("CARGOKIT_DARWIN_PLATFORM_NAME"); + + /// List of architectures to build for (arm64, armv7, x86_64). + static List get darwinArchs => + _getEnv("CARGOKIT_DARWIN_ARCHS").split(' '); + + // Gradle + static String get minSdkVersion => _getEnv("CARGOKIT_MIN_SDK_VERSION"); + static String get ndkVersion => _getEnv("CARGOKIT_NDK_VERSION"); + static String get sdkPath => _getEnvPath("CARGOKIT_SDK_DIR"); + static String get javaHome => _getEnvPath("CARGOKIT_JAVA_HOME"); + static List get targetPlatforms => + _getEnv("CARGOKIT_TARGET_PLATFORMS").split(','); + + // CMAKE + static String get targetPlatform => _getEnv("CARGOKIT_TARGET_PLATFORM"); + + static String _getEnv(String key) { + final res = Platform.environment[key]; + if (res == null) { + throw Exception("Missing environment variable $key"); + } + return res; + } + + static String _getEnvPath(String key) { + final res = _getEnv(key); + if (Directory(res).existsSync()) { + return res.resolveSymlink(); + } else { + return res; + } + } +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/logging.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/logging.dart new file mode 100644 index 0000000000..5edd4fd184 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/logging.dart @@ -0,0 +1,52 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:logging/logging.dart'; + +const String kSeparator = "--"; +const String kDoubleSeparator = "=="; + +bool _lastMessageWasSeparator = false; + +void _log(LogRecord rec) { + final prefix = '${rec.level.name}: '; + final out = rec.level == Level.SEVERE ? stderr : stdout; + if (rec.message == kSeparator) { + if (!_lastMessageWasSeparator) { + out.write(prefix); + out.writeln('-' * 80); + _lastMessageWasSeparator = true; + } + return; + } else if (rec.message == kDoubleSeparator) { + out.write(prefix); + out.writeln('=' * 80); + _lastMessageWasSeparator = true; + return; + } + out.write(prefix); + out.writeln(rec.message); + _lastMessageWasSeparator = false; +} + +void initLogging() { + Logger.root.level = Level.INFO; + Logger.root.onRecord.listen((LogRecord rec) { + final lines = rec.message.split('\n'); + for (final line in lines) { + if (line.isNotEmpty || lines.length == 1 || line != lines.last) { + _log(LogRecord( + rec.level, + line, + rec.loggerName, + )); + } + } + }); +} + +void enableVerboseLogging() { + Logger.root.level = Level.ALL; +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/options.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/options.dart new file mode 100644 index 0000000000..22aef1d371 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/options.dart @@ -0,0 +1,309 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:collection/collection.dart'; +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:hex/hex.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; +import 'package:source_span/source_span.dart'; +import 'package:yaml/yaml.dart'; + +import 'builder.dart'; +import 'environment.dart'; +import 'rustup.dart'; + +final _log = Logger('options'); + +/// A class for exceptions that have source span information attached. +class SourceSpanException implements Exception { + // This is a getter so that subclasses can override it. + /// A message describing the exception. + String get message => _message; + final String _message; + + // This is a getter so that subclasses can override it. + /// The span associated with this exception. + /// + /// This may be `null` if the source location can't be determined. + SourceSpan? get span => _span; + final SourceSpan? _span; + + SourceSpanException(this._message, this._span); + + /// Returns a string representation of `this`. + /// + /// [color] may either be a [String], a [bool], or `null`. If it's a string, + /// it indicates an ANSI terminal color escape that should be used to + /// highlight the span's text. If it's `true`, it indicates that the text + /// should be highlighted using the default color. If it's `false` or `null`, + /// it indicates that the text shouldn't be highlighted. + @override + String toString({Object? color}) { + if (span == null) return message; + return 'Error on ${span!.message(message, color: color)}'; + } +} + +enum Toolchain { + stable, + beta, + nightly, +} + +class CargoBuildOptions { + final Toolchain toolchain; + final List flags; + + CargoBuildOptions({ + required this.toolchain, + required this.flags, + }); + + static Toolchain _toolchainFromNode(YamlNode node) { + if (node case YamlScalar(value: String name)) { + final toolchain = + Toolchain.values.firstWhereOrNull((element) => element.name == name); + if (toolchain != null) { + return toolchain; + } + } + throw SourceSpanException( + 'Unknown toolchain. Must be one of ${Toolchain.values.map((e) => e.name)}.', + node.span); + } + + static CargoBuildOptions parse(YamlNode node) { + if (node is! YamlMap) { + throw SourceSpanException('Cargo options must be a map', node.span); + } + Toolchain toolchain = Toolchain.stable; + List flags = []; + for (final MapEntry(:key, :value) in node.nodes.entries) { + if (key case YamlScalar(value: 'toolchain')) { + toolchain = _toolchainFromNode(value); + } else if (key case YamlScalar(value: 'extra_flags')) { + if (value case YamlList(nodes: List list)) { + if (list.every((element) { + if (element case YamlScalar(value: String _)) { + return true; + } + return false; + })) { + flags = list.map((e) => e.value as String).toList(); + continue; + } + } + throw SourceSpanException( + 'Extra flags must be a list of strings', value.span); + } else { + throw SourceSpanException( + 'Unknown cargo option type. Must be "toolchain" or "extra_flags".', + key.span); + } + } + return CargoBuildOptions(toolchain: toolchain, flags: flags); + } +} + +extension on YamlMap { + /// Map that extracts keys so that we can do map case check on them. + Map get valueMap => + nodes.map((key, value) => MapEntry(key.value, value)); +} + +class PrecompiledBinaries { + final String uriPrefix; + final PublicKey publicKey; + + PrecompiledBinaries({ + required this.uriPrefix, + required this.publicKey, + }); + + static PublicKey _publicKeyFromHex(String key, SourceSpan? span) { + final bytes = HEX.decode(key); + if (bytes.length != 32) { + throw SourceSpanException( + 'Invalid public key. Must be 32 bytes long.', span); + } + return PublicKey(bytes); + } + + static PrecompiledBinaries parse(YamlNode node) { + if (node case YamlMap(valueMap: Map map)) { + if (map + case { + 'url_prefix': YamlNode urlPrefixNode, + 'public_key': YamlNode publicKeyNode, + }) { + final urlPrefix = switch (urlPrefixNode) { + YamlScalar(value: String urlPrefix) => urlPrefix, + _ => throw SourceSpanException( + 'Invalid URL prefix value.', urlPrefixNode.span), + }; + final publicKey = switch (publicKeyNode) { + YamlScalar(value: String publicKey) => + _publicKeyFromHex(publicKey, publicKeyNode.span), + _ => throw SourceSpanException( + 'Invalid public key value.', publicKeyNode.span), + }; + return PrecompiledBinaries( + uriPrefix: urlPrefix, + publicKey: publicKey, + ); + } + } + throw SourceSpanException( + 'Invalid precompiled binaries value. ' + 'Expected Map with "url_prefix" and "public_key".', + node.span); + } +} + +/// Cargokit options specified for Rust crate. +class CargokitCrateOptions { + CargokitCrateOptions({ + this.cargo = const {}, + this.precompiledBinaries, + }); + + final Map cargo; + final PrecompiledBinaries? precompiledBinaries; + + static CargokitCrateOptions parse(YamlNode node) { + if (node is! YamlMap) { + throw SourceSpanException('Cargokit options must be a map', node.span); + } + final options = {}; + PrecompiledBinaries? precompiledBinaries; + + for (final entry in node.nodes.entries) { + if (entry + case MapEntry( + key: YamlScalar(value: 'cargo'), + value: YamlNode node, + )) { + if (node is! YamlMap) { + throw SourceSpanException('Cargo options must be a map', node.span); + } + for (final MapEntry(:YamlNode key, :value) in node.nodes.entries) { + if (key case YamlScalar(value: String name)) { + final configuration = BuildConfiguration.values + .firstWhereOrNull((element) => element.name == name); + if (configuration != null) { + options[configuration] = CargoBuildOptions.parse(value); + continue; + } + } + throw SourceSpanException( + 'Unknown build configuration. Must be one of ${BuildConfiguration.values.map((e) => e.name)}.', + key.span); + } + } else if (entry.key case YamlScalar(value: 'precompiled_binaries')) { + precompiledBinaries = PrecompiledBinaries.parse(entry.value); + } else { + throw SourceSpanException( + 'Unknown cargokit option type. Must be "cargo" or "precompiled_binaries".', + entry.key.span); + } + } + return CargokitCrateOptions( + cargo: options, + precompiledBinaries: precompiledBinaries, + ); + } + + static CargokitCrateOptions load({ + required String manifestDir, + }) { + final uri = Uri.file(path.join(manifestDir, "cargokit.yaml")); + final file = File.fromUri(uri); + if (file.existsSync()) { + final contents = loadYamlNode(file.readAsStringSync(), sourceUrl: uri); + return parse(contents); + } else { + return CargokitCrateOptions(); + } + } +} + +class CargokitUserOptions { + // When Rustup is installed always build locally unless user opts into + // using precompiled binaries. + static bool defaultUsePrecompiledBinaries() { + return Rustup.executablePath() == null; + } + + CargokitUserOptions({ + required this.usePrecompiledBinaries, + required this.verboseLogging, + }); + + CargokitUserOptions._() + : usePrecompiledBinaries = defaultUsePrecompiledBinaries(), + verboseLogging = false; + + static CargokitUserOptions parse(YamlNode node) { + if (node is! YamlMap) { + throw SourceSpanException('Cargokit options must be a map', node.span); + } + bool usePrecompiledBinaries = defaultUsePrecompiledBinaries(); + bool verboseLogging = false; + + for (final entry in node.nodes.entries) { + if (entry.key case YamlScalar(value: 'use_precompiled_binaries')) { + if (entry.value case YamlScalar(value: bool value)) { + usePrecompiledBinaries = value; + continue; + } + throw SourceSpanException( + 'Invalid value for "use_precompiled_binaries". Must be a boolean.', + entry.value.span); + } else if (entry.key case YamlScalar(value: 'verbose_logging')) { + if (entry.value case YamlScalar(value: bool value)) { + verboseLogging = value; + continue; + } + throw SourceSpanException( + 'Invalid value for "verbose_logging". Must be a boolean.', + entry.value.span); + } else { + throw SourceSpanException( + 'Unknown cargokit option type. Must be "use_precompiled_binaries" or "verbose_logging".', + entry.key.span); + } + } + return CargokitUserOptions( + usePrecompiledBinaries: usePrecompiledBinaries, + verboseLogging: verboseLogging, + ); + } + + static CargokitUserOptions load() { + String fileName = "cargokit_options.yaml"; + var userProjectDir = Directory(Environment.rootProjectDir); + + while (userProjectDir.parent.path != userProjectDir.path) { + final configFile = File(path.join(userProjectDir.path, fileName)); + if (configFile.existsSync()) { + final contents = loadYamlNode( + configFile.readAsStringSync(), + sourceUrl: configFile.uri, + ); + final res = parse(contents); + if (res.verboseLogging) { + _log.info('Found user options file at ${configFile.path}'); + } + return res; + } + userProjectDir = userProjectDir.parent; + } + return CargokitUserOptions._(); + } + + final bool usePrecompiledBinaries; + final bool verboseLogging; +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart new file mode 100644 index 0000000000..c27f4195dd --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart @@ -0,0 +1,202 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:github/github.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'artifacts_provider.dart'; +import 'builder.dart'; +import 'cargo.dart'; +import 'crate_hash.dart'; +import 'options.dart'; +import 'rustup.dart'; +import 'target.dart'; + +final _log = Logger('precompile_binaries'); + +class PrecompileBinaries { + PrecompileBinaries({ + required this.privateKey, + required this.githubToken, + required this.repositorySlug, + required this.manifestDir, + required this.targets, + this.androidSdkLocation, + this.androidNdkVersion, + this.androidMinSdkVersion, + this.tempDir, + }); + + final PrivateKey privateKey; + final String githubToken; + final RepositorySlug repositorySlug; + final String manifestDir; + final List targets; + final String? androidSdkLocation; + final String? androidNdkVersion; + final int? androidMinSdkVersion; + final String? tempDir; + + static String fileName(Target target, String name) { + return '${target.rust}_$name'; + } + + static String signatureFileName(Target target, String name) { + return '${target.rust}_$name.sig'; + } + + Future run() async { + final crateInfo = CrateInfo.load(manifestDir); + + final targets = List.of(this.targets); + if (targets.isEmpty) { + targets.addAll([ + ...Target.buildableTargets(), + if (androidSdkLocation != null) ...Target.androidTargets(), + ]); + } + + _log.info('Precompiling binaries for $targets'); + + final hash = CrateHash.compute(manifestDir); + _log.info('Computed crate hash: $hash'); + + final String tagName = 'precompiled_$hash'; + + final github = GitHub(auth: Authentication.withToken(githubToken)); + final repo = github.repositories; + final release = await _getOrCreateRelease( + repo: repo, + tagName: tagName, + packageName: crateInfo.packageName, + hash: hash, + ); + + final tempDir = this.tempDir != null + ? Directory(this.tempDir!) + : Directory.systemTemp.createTempSync('precompiled_'); + + tempDir.createSync(recursive: true); + + final crateOptions = CargokitCrateOptions.load( + manifestDir: manifestDir, + ); + + final buildEnvironment = BuildEnvironment( + configuration: BuildConfiguration.release, + crateOptions: crateOptions, + targetTempDir: tempDir.path, + manifestDir: manifestDir, + crateInfo: crateInfo, + isAndroid: androidSdkLocation != null, + androidSdkPath: androidSdkLocation, + androidNdkVersion: androidNdkVersion, + androidMinSdkVersion: androidMinSdkVersion, + ); + + final rustup = Rustup(); + + for (final target in targets) { + final artifactNames = getArtifactNames( + target: target, + libraryName: crateInfo.packageName, + remote: true, + ); + + if (artifactNames.every((name) { + final fileName = PrecompileBinaries.fileName(target, name); + return (release.assets ?? []).any((e) => e.name == fileName); + })) { + _log.info("All artifacts for $target already exist - skipping"); + continue; + } + + _log.info('Building for $target'); + + final builder = + RustBuilder(target: target, environment: buildEnvironment); + builder.prepare(rustup); + final res = await builder.build(); + + final assets = []; + for (final name in artifactNames) { + final file = File(path.join(res, name)); + if (!file.existsSync()) { + throw Exception('Missing artifact: ${file.path}'); + } + + final data = file.readAsBytesSync(); + final create = CreateReleaseAsset( + name: PrecompileBinaries.fileName(target, name), + contentType: "application/octet-stream", + assetData: data, + ); + final signature = sign(privateKey, data); + final signatureCreate = CreateReleaseAsset( + name: signatureFileName(target, name), + contentType: "application/octet-stream", + assetData: signature, + ); + bool verified = verify(public(privateKey), data, signature); + if (!verified) { + throw Exception('Signature verification failed'); + } + assets.add(create); + assets.add(signatureCreate); + } + _log.info('Uploading assets: ${assets.map((e) => e.name)}'); + for (final asset in assets) { + // This seems to be failing on CI so do it one by one + int retryCount = 0; + while (true) { + try { + await repo.uploadReleaseAssets(release, [asset]); + break; + } on Exception catch (e) { + if (retryCount == 10) { + rethrow; + } + ++retryCount; + _log.shout( + 'Upload failed (attempt $retryCount, will retry): ${e.toString()}'); + await Future.delayed(Duration(seconds: 2)); + } + } + } + } + + _log.info('Cleaning up'); + tempDir.deleteSync(recursive: true); + } + + Future _getOrCreateRelease({ + required RepositoriesService repo, + required String tagName, + required String packageName, + required String hash, + }) async { + Release release; + try { + _log.info('Fetching release $tagName'); + release = await repo.getReleaseByTagName(repositorySlug, tagName); + } on ReleaseNotFound { + _log.info('Release not found - creating release $tagName'); + release = await repo.createRelease( + repositorySlug, + CreateRelease.from( + tagName: tagName, + name: 'Precompiled binaries ${hash.substring(0, 8)}', + targetCommitish: null, + isDraft: false, + isPrerelease: false, + body: 'Precompiled binaries for crate $packageName, ' + 'crate hash $hash.', + )); + } + return release; + } +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/rustup.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/rustup.dart new file mode 100644 index 0000000000..0ac8d08616 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/rustup.dart @@ -0,0 +1,136 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:collection/collection.dart'; +import 'package:path/path.dart' as path; + +import 'util.dart'; + +class _Toolchain { + _Toolchain( + this.name, + this.targets, + ); + + final String name; + final List targets; +} + +class Rustup { + List? installedTargets(String toolchain) { + final targets = _installedTargets(toolchain); + return targets != null ? List.unmodifiable(targets) : null; + } + + void installToolchain(String toolchain) { + log.info("Installing Rust toolchain: $toolchain"); + runCommand("rustup", ['toolchain', 'install', toolchain]); + _installedToolchains + .add(_Toolchain(toolchain, _getInstalledTargets(toolchain))); + } + + void installTarget( + String target, { + required String toolchain, + }) { + log.info("Installing Rust target: $target"); + runCommand("rustup", [ + 'target', + 'add', + '--toolchain', + toolchain, + target, + ]); + _installedTargets(toolchain)?.add(target); + } + + final List<_Toolchain> _installedToolchains; + + Rustup() : _installedToolchains = _getInstalledToolchains(); + + List? _installedTargets(String toolchain) => _installedToolchains + .firstWhereOrNull( + (e) => e.name == toolchain || e.name.startsWith('$toolchain-')) + ?.targets; + + static List<_Toolchain> _getInstalledToolchains() { + String extractToolchainName(String line) { + // ignore (default) after toolchain name + final parts = line.split(' '); + return parts[0]; + } + + final res = runCommand("rustup", ['toolchain', 'list']); + + // To list all non-custom toolchains, we need to filter out lines that + // don't start with "stable", "beta", or "nightly". + Pattern nonCustom = RegExp(r"^(stable|beta|nightly)"); + final lines = res.stdout + .toString() + .split('\n') + .where((e) => e.isNotEmpty && e.startsWith(nonCustom)) + .map(extractToolchainName) + .toList(growable: true); + + return lines + .map( + (name) => _Toolchain( + name, + _getInstalledTargets(name), + ), + ) + .toList(growable: true); + } + + static List _getInstalledTargets(String toolchain) { + final res = runCommand("rustup", [ + 'target', + 'list', + '--toolchain', + toolchain, + '--installed', + ]); + final lines = res.stdout + .toString() + .split('\n') + .where((e) => e.isNotEmpty) + .toList(growable: true); + return lines; + } + + bool _didInstallRustSrcForNightly = false; + + void installRustSrcForNightly() { + if (_didInstallRustSrcForNightly) { + return; + } + // Useful for -Z build-std + runCommand( + "rustup", + ['component', 'add', 'rust-src', '--toolchain', 'nightly'], + ); + _didInstallRustSrcForNightly = true; + } + + static String? executablePath() { + final envPath = Platform.environment['PATH']; + final envPathSeparator = Platform.isWindows ? ';' : ':'; + final home = Platform.isWindows + ? Platform.environment['USERPROFILE'] + : Platform.environment['HOME']; + final paths = [ + if (home != null) path.join(home, '.cargo', 'bin'), + if (envPath != null) ...envPath.split(envPathSeparator), + ]; + for (final p in paths) { + final rustup = Platform.isWindows ? 'rustup.exe' : 'rustup'; + final rustupPath = path.join(p, rustup); + if (File(rustupPath).existsSync()) { + return rustupPath; + } + } + return null; + } +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/target.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/target.dart new file mode 100644 index 0000000000..6fbc58b64f --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/target.dart @@ -0,0 +1,140 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:collection/collection.dart'; + +import 'util.dart'; + +class Target { + Target({ + required this.rust, + this.flutter, + this.android, + this.androidMinSdkVersion, + this.darwinPlatform, + this.darwinArch, + }); + + static final all = [ + Target( + rust: 'armv7-linux-androideabi', + flutter: 'android-arm', + android: 'armeabi-v7a', + androidMinSdkVersion: 16, + ), + Target( + rust: 'aarch64-linux-android', + flutter: 'android-arm64', + android: 'arm64-v8a', + androidMinSdkVersion: 21, + ), + Target( + rust: 'i686-linux-android', + flutter: 'android-x86', + android: 'x86', + androidMinSdkVersion: 16, + ), + Target( + rust: 'x86_64-linux-android', + flutter: 'android-x64', + android: 'x86_64', + androidMinSdkVersion: 21, + ), + Target( + rust: 'x86_64-pc-windows-msvc', + flutter: 'windows-x64', + ), + Target( + rust: 'x86_64-unknown-linux-gnu', + flutter: 'linux-x64', + ), + Target( + rust: 'aarch64-unknown-linux-gnu', + flutter: 'linux-arm64', + ), + Target( + rust: 'x86_64-apple-darwin', + darwinPlatform: 'macosx', + darwinArch: 'x86_64', + ), + Target( + rust: 'aarch64-apple-darwin', + darwinPlatform: 'macosx', + darwinArch: 'arm64', + ), + Target( + rust: 'aarch64-apple-ios', + darwinPlatform: 'iphoneos', + darwinArch: 'arm64', + ), + Target( + rust: 'aarch64-apple-ios-sim', + darwinPlatform: 'iphonesimulator', + darwinArch: 'arm64', + ), + Target( + rust: 'x86_64-apple-ios', + darwinPlatform: 'iphonesimulator', + darwinArch: 'x86_64', + ), + ]; + + static Target? forFlutterName(String flutterName) { + return all.firstWhereOrNull((element) => element.flutter == flutterName); + } + + static Target? forDarwin({ + required String platformName, + required String darwinAarch, + }) { + return all.firstWhereOrNull((element) => // + element.darwinPlatform == platformName && + element.darwinArch == darwinAarch); + } + + static Target? forRustTriple(String triple) { + return all.firstWhereOrNull((element) => element.rust == triple); + } + + static List androidTargets() { + return all + .where((element) => element.android != null) + .toList(growable: false); + } + + /// Returns buildable targets on current host platform ignoring Android targets. + static List buildableTargets() { + if (Platform.isLinux) { + // Right now we don't support cross-compiling on Linux. So we just return + // the host target. + final arch = runCommand('arch', []).stdout as String; + if (arch.trim() == 'aarch64') { + return [Target.forRustTriple('aarch64-unknown-linux-gnu')!]; + } else { + return [Target.forRustTriple('x86_64-unknown-linux-gnu')!]; + } + } + return all.where((target) { + if (Platform.isWindows) { + return target.rust.contains('-windows-'); + } else if (Platform.isMacOS) { + return target.darwinPlatform != null; + } + return false; + }).toList(growable: false); + } + + @override + String toString() { + return rust; + } + + final String? flutter; + final String rust; + final String? android; + final int? androidMinSdkVersion; + final String? darwinPlatform; + final String? darwinArch; +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/util.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/util.dart new file mode 100644 index 0000000000..8bb6a8724f --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/util.dart @@ -0,0 +1,172 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:convert'; +import 'dart:io'; + +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'logging.dart'; +import 'rustup.dart'; + +final log = Logger("process"); + +class CommandFailedException implements Exception { + final String executable; + final List arguments; + final ProcessResult result; + + CommandFailedException({ + required this.executable, + required this.arguments, + required this.result, + }); + + @override + String toString() { + final stdout = result.stdout.toString().trim(); + final stderr = result.stderr.toString().trim(); + return [ + "External Command: $executable ${arguments.map((e) => '"$e"').join(' ')}", + "Returned Exit Code: ${result.exitCode}", + kSeparator, + "STDOUT:", + if (stdout.isNotEmpty) stdout, + kSeparator, + "STDERR:", + if (stderr.isNotEmpty) stderr, + ].join('\n'); + } +} + +class TestRunCommandArgs { + final String executable; + final List arguments; + final String? workingDirectory; + final Map? environment; + final bool includeParentEnvironment; + final bool runInShell; + final Encoding? stdoutEncoding; + final Encoding? stderrEncoding; + + TestRunCommandArgs({ + required this.executable, + required this.arguments, + this.workingDirectory, + this.environment, + this.includeParentEnvironment = true, + this.runInShell = false, + this.stdoutEncoding, + this.stderrEncoding, + }); +} + +class TestRunCommandResult { + TestRunCommandResult({ + this.pid = 1, + this.exitCode = 0, + this.stdout = '', + this.stderr = '', + }); + + final int pid; + final int exitCode; + final String stdout; + final String stderr; +} + +TestRunCommandResult Function(TestRunCommandArgs args)? testRunCommandOverride; + +ProcessResult runCommand( + String executable, + List arguments, { + String? workingDirectory, + Map? environment, + bool includeParentEnvironment = true, + bool runInShell = false, + Encoding? stdoutEncoding = systemEncoding, + Encoding? stderrEncoding = systemEncoding, +}) { + if (testRunCommandOverride != null) { + final result = testRunCommandOverride!(TestRunCommandArgs( + executable: executable, + arguments: arguments, + workingDirectory: workingDirectory, + environment: environment, + includeParentEnvironment: includeParentEnvironment, + runInShell: runInShell, + stdoutEncoding: stdoutEncoding, + stderrEncoding: stderrEncoding, + )); + return ProcessResult( + result.pid, + result.exitCode, + result.stdout, + result.stderr, + ); + } + log.finer('Running command $executable ${arguments.join(' ')}'); + final res = Process.runSync( + _resolveExecutable(executable), + arguments, + workingDirectory: workingDirectory, + environment: environment, + includeParentEnvironment: includeParentEnvironment, + runInShell: runInShell, + stderrEncoding: stderrEncoding, + stdoutEncoding: stdoutEncoding, + ); + if (res.exitCode != 0) { + throw CommandFailedException( + executable: executable, + arguments: arguments, + result: res, + ); + } else { + return res; + } +} + +class RustupNotFoundException implements Exception { + @override + String toString() { + return [ + ' ', + 'rustup not found in PATH.', + ' ', + 'Maybe you need to install Rust? It only takes a minute:', + ' ', + if (Platform.isWindows) 'https://www.rust-lang.org/tools/install', + if (hasHomebrewRustInPath()) ...[ + '\$ brew unlink rust # Unlink homebrew Rust from PATH', + ], + if (!Platform.isWindows) + "\$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh", + ' ', + ].join('\n'); + } + + static bool hasHomebrewRustInPath() { + if (!Platform.isMacOS) { + return false; + } + final envPath = Platform.environment['PATH'] ?? ''; + final paths = envPath.split(':'); + return paths.any((p) { + return p.contains('homebrew') && File(path.join(p, 'rustc')).existsSync(); + }); + } +} + +String _resolveExecutable(String executable) { + if (executable == 'rustup') { + final resolved = Rustup.executablePath(); + if (resolved != null) { + return resolved; + } + throw RustupNotFoundException(); + } else { + return executable; + } +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart new file mode 100644 index 0000000000..2366b57bfd --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart @@ -0,0 +1,84 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:http/http.dart'; + +import 'artifacts_provider.dart'; +import 'cargo.dart'; +import 'crate_hash.dart'; +import 'options.dart'; +import 'precompile_binaries.dart'; +import 'target.dart'; + +class VerifyBinaries { + VerifyBinaries({ + required this.manifestDir, + }); + + final String manifestDir; + + Future run() async { + final crateInfo = CrateInfo.load(manifestDir); + + final config = CargokitCrateOptions.load(manifestDir: manifestDir); + final precompiledBinaries = config.precompiledBinaries; + if (precompiledBinaries == null) { + stdout.writeln('Crate does not support precompiled binaries.'); + } else { + final crateHash = CrateHash.compute(manifestDir); + stdout.writeln('Crate hash: $crateHash'); + + for (final target in Target.all) { + final message = 'Checking ${target.rust}...'; + stdout.write(message.padRight(40)); + stdout.flush(); + + final artifacts = getArtifactNames( + target: target, + libraryName: crateInfo.packageName, + remote: true, + ); + + final prefix = precompiledBinaries.uriPrefix; + + bool ok = true; + + for (final artifact in artifacts) { + final fileName = PrecompileBinaries.fileName(target, artifact); + final signatureFileName = + PrecompileBinaries.signatureFileName(target, artifact); + + final url = Uri.parse('$prefix$crateHash/$fileName'); + final signatureUrl = + Uri.parse('$prefix$crateHash/$signatureFileName'); + + final signature = await get(signatureUrl); + if (signature.statusCode != 200) { + stdout.writeln('MISSING'); + ok = false; + break; + } + final asset = await get(url); + if (asset.statusCode != 200) { + stdout.writeln('MISSING'); + ok = false; + break; + } + + if (!verify(precompiledBinaries.publicKey, asset.bodyBytes, + signature.bodyBytes)) { + stdout.writeln('INVALID SIGNATURE'); + ok = false; + } + } + + if (ok) { + stdout.writeln('OK'); + } + } + } + } +} diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.lock b/mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.lock new file mode 100644 index 0000000000..343bdd3694 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.lock @@ -0,0 +1,453 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 + url: "https://pub.dev" + source: hosted + version: "64.0.0" + adaptive_number: + dependency: transitive + description: + name: adaptive_number + sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" + url: "https://pub.dev" + source: hosted + version: "6.2.0" + args: + dependency: "direct main" + description: + name: args + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + url: "https://pub.dev" + source: hosted + version: "2.4.2" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + collection: + dependency: "direct main" + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + convert: + dependency: "direct main" + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + coverage: + dependency: transitive + description: + name: coverage + sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" + url: "https://pub.dev" + source: hosted + version: "1.6.3" + crypto: + dependency: "direct main" + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" + ed25519_edwards: + dependency: "direct main" + description: + name: ed25519_edwards + sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + github: + dependency: "direct main" + description: + name: github + sha256: "9966bc13bf612342e916b0a343e95e5f046c88f602a14476440e9b75d2295411" + url: "https://pub.dev" + source: hosted + version: "9.17.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + hex: + dependency: "direct main" + description: + name: hex + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + http: + dependency: "direct main" + description: + name: http + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + url: "https://pub.dev" + source: hosted + version: "4.8.1" + lints: + dependency: "direct dev" + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + logging: + dependency: "direct main" + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + meta: + dependency: transitive + description: + name: meta + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + mime: + dependency: transitive + description: + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" + source: hosted + version: "1.0.4" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + path: + dependency: "direct main" + description: + name: path + sha256: "2ad4cddff7f5cc0e2d13069f2a3f7a73ca18f66abd6f5ecf215219cdb3638edb" + url: "https://pub.dev" + source: hosted + version: "1.8.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + url: "https://pub.dev" + source: hosted + version: "5.4.0" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + url: "https://pub.dev" + source: hosted + version: "1.1.2" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" + source_span: + dependency: "direct main" + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test: + dependency: "direct dev" + description: + name: test + sha256: "9b0dd8e36af4a5b1569029949d50a52cb2a2a2fdaa20cebb96e6603b9ae241f9" + url: "https://pub.dev" + source: hosted + version: "1.24.6" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.dev" + source: hosted + version: "0.6.1" + test_core: + dependency: transitive + description: + name: test_core + sha256: "4bef837e56375537055fdbbbf6dd458b1859881f4c7e6da936158f77d61ab265" + url: "https://pub.dev" + source: hosted + version: "0.5.6" + toml: + dependency: "direct main" + description: + name: toml + sha256: "157c5dca5160fced243f3ce984117f729c788bb5e475504f3dbcda881accee44" + url: "https://pub.dev" + source: hosted + version: "0.14.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + version: + dependency: "direct main" + description: + name: version + sha256: "2307e23a45b43f96469eeab946208ed63293e8afca9c28cd8b5241ff31c55f55" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0fae432c85c4ea880b33b497d32824b97795b04cdaa74d270219572a1f50268d" + url: "https://pub.dev" + source: hosted + version: "11.9.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + yaml: + dependency: "direct main" + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.0.0 <4.0.0" diff --git a/mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.yaml b/mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.yaml new file mode 100644 index 0000000000..18c61e3386 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.yaml @@ -0,0 +1,33 @@ +# This is copied from Cargokit (which is the official way to use it currently) +# Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +name: build_tool +description: Cargokit build_tool. Facilitates the build of Rust crate during Flutter application build. +publish_to: none +version: 1.0.0 + +environment: + sdk: ">=3.0.0 <4.0.0" + +# Add regular dependencies here. +dependencies: + # these are pinned on purpose because the bundle_tool_runner doesn't have + # pubspec.lock. See run_build_tool.sh + logging: 1.2.0 + path: 1.8.0 + version: 3.0.0 + collection: 1.18.0 + ed25519_edwards: 0.3.1 + hex: 0.2.0 + yaml: 3.1.2 + source_span: 1.10.0 + github: 9.17.0 + args: 2.4.2 + crypto: 3.0.3 + convert: 3.1.1 + http: 1.1.0 + toml: 0.14.0 + +dev_dependencies: + lints: ^2.1.0 + test: ^1.24.0 diff --git a/mobile/apps/photos/rust_builder/cargokit/cmake/cargokit.cmake b/mobile/apps/photos/rust_builder/cargokit/cmake/cargokit.cmake new file mode 100644 index 0000000000..ddd05df9b4 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/cmake/cargokit.cmake @@ -0,0 +1,99 @@ +SET(cargokit_cmake_root "${CMAKE_CURRENT_LIST_DIR}/..") + +# Workaround for https://github.com/dart-lang/pub/issues/4010 +get_filename_component(cargokit_cmake_root "${cargokit_cmake_root}" REALPATH) + +if(WIN32) + # REALPATH does not properly resolve symlinks on windows :-/ + execute_process(COMMAND powershell -ExecutionPolicy Bypass -File "${CMAKE_CURRENT_LIST_DIR}/resolve_symlinks.ps1" "${cargokit_cmake_root}" OUTPUT_VARIABLE cargokit_cmake_root OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() + +# Arguments +# - target: CMAKE target to which rust library is linked +# - manifest_dir: relative path from current folder to directory containing cargo manifest +# - lib_name: cargo package name +# - any_symbol_name: name of any exported symbol from the library. +# used on windows to force linking with library. +function(apply_cargokit target manifest_dir lib_name any_symbol_name) + + set(CARGOKIT_LIB_NAME "${lib_name}") + set(CARGOKIT_LIB_FULL_NAME "${CMAKE_SHARED_MODULE_PREFIX}${CARGOKIT_LIB_NAME}${CMAKE_SHARED_MODULE_SUFFIX}") + if (CMAKE_CONFIGURATION_TYPES) + set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/$") + set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/$/${CARGOKIT_LIB_FULL_NAME}") + else() + set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") + set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/${CARGOKIT_LIB_FULL_NAME}") + endif() + set(CARGOKIT_TEMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/cargokit_build") + + if (FLUTTER_TARGET_PLATFORM) + set(CARGOKIT_TARGET_PLATFORM "${FLUTTER_TARGET_PLATFORM}") + else() + set(CARGOKIT_TARGET_PLATFORM "windows-x64") + endif() + + set(CARGOKIT_ENV + "CARGOKIT_CMAKE=${CMAKE_COMMAND}" + "CARGOKIT_CONFIGURATION=$" + "CARGOKIT_MANIFEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/${manifest_dir}" + "CARGOKIT_TARGET_TEMP_DIR=${CARGOKIT_TEMP_DIR}" + "CARGOKIT_OUTPUT_DIR=${CARGOKIT_OUTPUT_DIR}" + "CARGOKIT_TARGET_PLATFORM=${CARGOKIT_TARGET_PLATFORM}" + "CARGOKIT_TOOL_TEMP_DIR=${CARGOKIT_TEMP_DIR}/tool" + "CARGOKIT_ROOT_PROJECT_DIR=${CMAKE_SOURCE_DIR}" + ) + + if (WIN32) + set(SCRIPT_EXTENSION ".cmd") + set(IMPORT_LIB_EXTENSION ".lib") + else() + set(SCRIPT_EXTENSION ".sh") + set(IMPORT_LIB_EXTENSION "") + execute_process(COMMAND chmod +x "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}") + endif() + + # Using generators in custom command is only supported in CMake 3.20+ + if (CMAKE_CONFIGURATION_TYPES AND ${CMAKE_VERSION} VERSION_LESS "3.20.0") + foreach(CONFIG IN LISTS CMAKE_CONFIGURATION_TYPES) + add_custom_command( + OUTPUT + "${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}/${CARGOKIT_LIB_FULL_NAME}" + "${CMAKE_CURRENT_BINARY_DIR}/_phony_" + COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV} + "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake + VERBATIM + ) + endforeach() + else() + add_custom_command( + OUTPUT + ${OUTPUT_LIB} + "${CMAKE_CURRENT_BINARY_DIR}/_phony_" + COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV} + "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake + VERBATIM + ) + endif() + + + set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/_phony_" PROPERTIES SYMBOLIC TRUE) + + if (TARGET ${target}) + # If we have actual cmake target provided create target and make existing + # target depend on it + add_custom_target("${target}_cargokit" DEPENDS ${OUTPUT_LIB}) + add_dependencies("${target}" "${target}_cargokit") + target_link_libraries("${target}" PRIVATE "${OUTPUT_LIB}${IMPORT_LIB_EXTENSION}") + if(WIN32) + target_link_options(${target} PRIVATE "/INCLUDE:${any_symbol_name}") + endif() + else() + # Otherwise (FFI) just use ALL to force building always + add_custom_target("${target}_cargokit" ALL DEPENDS ${OUTPUT_LIB}) + endif() + + # Allow adding the output library to plugin bundled libraries + set("${target}_cargokit_lib" ${OUTPUT_LIB} PARENT_SCOPE) + +endfunction() diff --git a/mobile/apps/photos/rust_builder/cargokit/cmake/resolve_symlinks.ps1 b/mobile/apps/photos/rust_builder/cargokit/cmake/resolve_symlinks.ps1 new file mode 100644 index 0000000000..3d10d283c2 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/cmake/resolve_symlinks.ps1 @@ -0,0 +1,27 @@ +function Resolve-Symlinks { + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [string] $Path + ) + + [string] $separator = '/' + [string[]] $parts = $Path.Split($separator) + + [string] $realPath = '' + foreach ($part in $parts) { + if ($realPath -and !$realPath.EndsWith($separator)) { + $realPath += $separator + } + $realPath += $part + $item = Get-Item $realPath + if ($item.Target) { + $realPath = $item.Target.Replace('\', '/') + } + } + $realPath +} + +$path=Resolve-Symlinks -Path $args[0] +Write-Host $path diff --git a/mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle b/mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle new file mode 100644 index 0000000000..d139d04f66 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle @@ -0,0 +1,179 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import java.nio.file.Paths +import org.apache.tools.ant.taskdefs.condition.Os + +CargoKitPlugin.file = buildscript.sourceFile + +apply plugin: CargoKitPlugin + +class CargoKitExtension { + String manifestDir; // Relative path to folder containing Cargo.toml + String libname; // Library name within Cargo.toml. Must be a cdylib +} + +abstract class CargoKitBuildTask extends DefaultTask { + + @Input + String buildMode + + @Input + String buildDir + + @Input + String outputDir + + @Input + String ndkVersion + + @Input + String sdkDirectory + + @Input + int compileSdkVersion; + + @Input + int minSdkVersion; + + @Input + String pluginFile + + @Input + List targetPlatforms + + @TaskAction + def build() { + if (project.cargokit.manifestDir == null) { + throw new GradleException("Property 'manifestDir' must be set on cargokit extension"); + } + + if (project.cargokit.libname == null) { + throw new GradleException("Property 'libname' must be set on cargokit extension"); + } + + def executableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "run_build_tool.cmd" : "run_build_tool.sh" + def path = Paths.get(new File(pluginFile).parent, "..", executableName); + + def manifestDir = Paths.get(project.buildscript.sourceFile.parent, project.cargokit.manifestDir) + + def rootProjectDir = project.rootProject.projectDir + + if (!Os.isFamily(Os.FAMILY_WINDOWS)) { + project.exec { + commandLine 'chmod', '+x', path + } + } + + project.exec { + executable path + args "build-gradle" + environment "CARGOKIT_ROOT_PROJECT_DIR", rootProjectDir + environment "CARGOKIT_TOOL_TEMP_DIR", "${buildDir}/build_tool" + environment "CARGOKIT_MANIFEST_DIR", manifestDir + environment "CARGOKIT_CONFIGURATION", buildMode + environment "CARGOKIT_TARGET_TEMP_DIR", buildDir + environment "CARGOKIT_OUTPUT_DIR", outputDir + environment "CARGOKIT_NDK_VERSION", ndkVersion + environment "CARGOKIT_SDK_DIR", sdkDirectory + environment "CARGOKIT_COMPILE_SDK_VERSION", compileSdkVersion + environment "CARGOKIT_MIN_SDK_VERSION", minSdkVersion + environment "CARGOKIT_TARGET_PLATFORMS", targetPlatforms.join(",") + environment "CARGOKIT_JAVA_HOME", System.properties['java.home'] + } + } +} + +class CargoKitPlugin implements Plugin { + + static String file; + + private Plugin findFlutterPlugin(Project rootProject) { + _findFlutterPlugin(rootProject.childProjects) + } + + private Plugin _findFlutterPlugin(Map projects) { + for (project in projects) { + for (plugin in project.value.getPlugins()) { + if (plugin.class.name == "com.flutter.gradle.FlutterPlugin") { + return plugin; + } + } + def plugin = _findFlutterPlugin(project.value.childProjects); + if (plugin != null) { + return plugin; + } + } + return null; + } + + @Override + void apply(Project project) { + def plugin = findFlutterPlugin(project.rootProject); + + project.extensions.create("cargokit", CargoKitExtension) + + if (plugin == null) { + print("Flutter plugin not found, CargoKit plugin will not be applied.") + return; + } + + def cargoBuildDir = "${project.buildDir}/build" + + // Determine if the project is an application or library + def isApplication = plugin.project.plugins.hasPlugin('com.android.application') + def variants = isApplication ? plugin.project.android.applicationVariants : plugin.project.android.libraryVariants + + variants.all { variant -> + + final buildType = variant.buildType.name + + def cargoOutputDir = "${project.buildDir}/jniLibs/${buildType}"; + def jniLibs = project.android.sourceSets.maybeCreate(buildType).jniLibs; + jniLibs.srcDir(new File(cargoOutputDir)) + + def platforms = com.flutter.gradle.FlutterPluginUtils.getTargetPlatforms(project).collect() + + // Same thing addFlutterDependencies does in flutter.gradle + if (buildType == "debug") { + platforms.add("android-x86") + platforms.add("android-x64") + } + + // The task name depends on plugin properties, which are not available + // at this point + project.getGradle().afterProject { + def taskName = "cargokitCargoBuild${project.cargokit.libname.capitalize()}${buildType.capitalize()}"; + + if (project.tasks.findByName(taskName)) { + return + } + + if (plugin.project.android.ndkVersion == null) { + throw new GradleException("Please set 'android.ndkVersion' in 'app/build.gradle'.") + } + + def task = project.tasks.create(taskName, CargoKitBuildTask.class) { + buildMode = variant.buildType.name + buildDir = cargoBuildDir + outputDir = cargoOutputDir + ndkVersion = plugin.project.android.ndkVersion + sdkDirectory = plugin.project.android.sdkDirectory + minSdkVersion = plugin.project.android.defaultConfig.minSdkVersion.apiLevel as int + compileSdkVersion = plugin.project.android.compileSdkVersion.substring(8) as int + targetPlatforms = platforms + pluginFile = CargoKitPlugin.file + } + def onTask = { newTask -> + if (newTask.name == "merge${buildType.capitalize()}NativeLibs") { + newTask.dependsOn task + // Fix gradle 7.4.2 not picking up JNI library changes + newTask.outputs.upToDateWhen { false } + } + } + project.tasks.each onTask + project.tasks.whenTaskAdded onTask + } + } + } +} diff --git a/mobile/apps/photos/rust_builder/cargokit/run_build_tool.cmd b/mobile/apps/photos/rust_builder/cargokit/run_build_tool.cmd new file mode 100755 index 0000000000..c45d0aa8b5 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/run_build_tool.cmd @@ -0,0 +1,91 @@ +@echo off +setlocal + +setlocal ENABLEDELAYEDEXPANSION + +SET BASEDIR=%~dp0 + +if not exist "%CARGOKIT_TOOL_TEMP_DIR%" ( + mkdir "%CARGOKIT_TOOL_TEMP_DIR%" +) +cd /D "%CARGOKIT_TOOL_TEMP_DIR%" + +SET BUILD_TOOL_PKG_DIR=%BASEDIR%build_tool +SET DART=%FLUTTER_ROOT%\bin\cache\dart-sdk\bin\dart + +set BUILD_TOOL_PKG_DIR_POSIX=%BUILD_TOOL_PKG_DIR:\=/% + +( + echo name: build_tool_runner + echo version: 1.0.0 + echo publish_to: none + echo. + echo environment: + echo sdk: '^>=3.0.0 ^<4.0.0' + echo. + echo dependencies: + echo build_tool: + echo path: %BUILD_TOOL_PKG_DIR_POSIX% +) >pubspec.yaml + +if not exist bin ( + mkdir bin +) + +( + echo import 'package:build_tool/build_tool.dart' as build_tool; + echo void main^(List^ args^) ^{ + echo build_tool.runMain^(args^); + echo ^} +) >bin\build_tool_runner.dart + +SET PRECOMPILED=bin\build_tool_runner.dill + +REM To detect changes in package we compare output of DIR /s (recursive) +set PREV_PACKAGE_INFO=.dart_tool\package_info.prev +set CUR_PACKAGE_INFO=.dart_tool\package_info.cur + +DIR "%BUILD_TOOL_PKG_DIR%" /s > "%CUR_PACKAGE_INFO%_orig" + +REM Last line in dir output is free space on harddrive. That is bound to +REM change between invocation so we need to remove it +( + Set "Line=" + For /F "UseBackQ Delims=" %%A In ("%CUR_PACKAGE_INFO%_orig") Do ( + SetLocal EnableDelayedExpansion + If Defined Line Echo !Line! + EndLocal + Set "Line=%%A") +) >"%CUR_PACKAGE_INFO%" +DEL "%CUR_PACKAGE_INFO%_orig" + +REM Compare current directory listing with previous +FC /B "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" > nul 2>&1 + +If %ERRORLEVEL% neq 0 ( + REM Changed - copy current to previous and remove precompiled kernel + if exist "%PREV_PACKAGE_INFO%" ( + DEL "%PREV_PACKAGE_INFO%" + ) + MOVE /Y "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" + if exist "%PRECOMPILED%" ( + DEL "%PRECOMPILED%" + ) +) + +REM There is no CUR_PACKAGE_INFO it was renamed in previous step to %PREV_PACKAGE_INFO% +REM which means we need to do pub get and precompile +if not exist "%PRECOMPILED%" ( + echo Running pub get in "%cd%" + "%DART%" pub get --no-precompile + "%DART%" compile kernel bin/build_tool_runner.dart +) + +"%DART%" "%PRECOMPILED%" %* + +REM 253 means invalid snapshot version. +If %ERRORLEVEL% equ 253 ( + "%DART%" pub get --no-precompile + "%DART%" compile kernel bin/build_tool_runner.dart + "%DART%" "%PRECOMPILED%" %* +) diff --git a/mobile/apps/photos/rust_builder/cargokit/run_build_tool.sh b/mobile/apps/photos/rust_builder/cargokit/run_build_tool.sh new file mode 100755 index 0000000000..6e594a23d4 --- /dev/null +++ b/mobile/apps/photos/rust_builder/cargokit/run_build_tool.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash + +set -e + +BASEDIR=$(dirname "$0") + +mkdir -p "$CARGOKIT_TOOL_TEMP_DIR" + +cd "$CARGOKIT_TOOL_TEMP_DIR" + +# Write a very simple bin package in temp folder that depends on build_tool package +# from Cargokit. This is done to ensure that we don't pollute Cargokit folder +# with .dart_tool contents. + +BUILD_TOOL_PKG_DIR="$BASEDIR/build_tool" + +if [[ -z $FLUTTER_ROOT ]]; then # not defined + DART=dart +else + DART="$FLUTTER_ROOT/bin/cache/dart-sdk/bin/dart" +fi + +cat << EOF > "pubspec.yaml" +name: build_tool_runner +version: 1.0.0 +publish_to: none + +environment: + sdk: '>=3.0.0 <4.0.0' + +dependencies: + build_tool: + path: "$BUILD_TOOL_PKG_DIR" +EOF + +mkdir -p "bin" + +cat << EOF > "bin/build_tool_runner.dart" +import 'package:build_tool/build_tool.dart' as build_tool; +void main(List args) { + build_tool.runMain(args); +} +EOF + +# Create alias for `shasum` if it does not exist and `sha1sum` exists +if ! [ -x "$(command -v shasum)" ] && [ -x "$(command -v sha1sum)" ]; then + shopt -s expand_aliases + alias shasum="sha1sum" +fi + +# Dart run will not cache any package that has a path dependency, which +# is the case for our build_tool_runner. So instead we precompile the package +# ourselves. +# To invalidate the cached kernel we use the hash of ls -LR of the build_tool +# package directory. This should be good enough, as the build_tool package +# itself is not meant to have any path dependencies. + +if [[ "$OSTYPE" == "darwin"* ]]; then + PACKAGE_HASH=$(ls -lTR "$BUILD_TOOL_PKG_DIR" | shasum) +else + PACKAGE_HASH=$(ls -lR --full-time "$BUILD_TOOL_PKG_DIR" | shasum) +fi + +PACKAGE_HASH_FILE=".package_hash" + +if [ -f "$PACKAGE_HASH_FILE" ]; then + EXISTING_HASH=$(cat "$PACKAGE_HASH_FILE") + if [ "$PACKAGE_HASH" != "$EXISTING_HASH" ]; then + rm "$PACKAGE_HASH_FILE" + fi +fi + +# Run pub get if needed. +if [ ! -f "$PACKAGE_HASH_FILE" ]; then + "$DART" pub get --no-precompile + "$DART" compile kernel bin/build_tool_runner.dart + echo "$PACKAGE_HASH" > "$PACKAGE_HASH_FILE" +fi + +set +e + +"$DART" bin/build_tool_runner.dill "$@" + +exit_code=$? + +# 253 means invalid snapshot version. +if [ $exit_code == 253 ]; then + "$DART" pub get --no-precompile + "$DART" compile kernel bin/build_tool_runner.dart + "$DART" bin/build_tool_runner.dill "$@" + exit_code=$? +fi + +exit $exit_code diff --git a/mobile/apps/photos/rust_builder/ios/Classes/dummy_file.c b/mobile/apps/photos/rust_builder/ios/Classes/dummy_file.c new file mode 100644 index 0000000000..e06dab9968 --- /dev/null +++ b/mobile/apps/photos/rust_builder/ios/Classes/dummy_file.c @@ -0,0 +1 @@ +// This is an empty file to force CocoaPods to create a framework. diff --git a/mobile/apps/photos/rust_builder/ios/rust_lib_photos.podspec b/mobile/apps/photos/rust_builder/ios/rust_lib_photos.podspec new file mode 100644 index 0000000000..16d34d0103 --- /dev/null +++ b/mobile/apps/photos/rust_builder/ios/rust_lib_photos.podspec @@ -0,0 +1,45 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint rust_lib_photos.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'rust_lib_photos' + s.version = '0.0.1' + s.summary = 'A new Flutter FFI plugin project.' + s.description = <<-DESC +A new Flutter FFI plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + + # This will ensure the source files in Classes/ are included in the native + # builds of apps using this FFI plugin. Podspec does not support relative + # paths, so Classes contains a forwarder C file that relatively imports + # `../src/*` so that the C sources can be shared among all target platforms. + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'Flutter' + s.platform = :ios, '11.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } + s.swift_version = '5.0' + + s.script_phase = { + :name => 'Build Rust library', + # First argument is relative path to the `rust` folder, second is name of rust library + :script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../../rust rust_lib_photos', + :execution_position => :before_compile, + :input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'], + # Let XCode know that the static library referenced in -force_load below is + # created by this build step. + :output_files => ["${BUILT_PRODUCTS_DIR}/librust_lib_photos.a"], + } + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + # Flutter.framework does not contain a i386 slice. + 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', + 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/librust_lib_photos.a -lc++', + } +end \ No newline at end of file diff --git a/mobile/apps/photos/rust_builder/linux/CMakeLists.txt b/mobile/apps/photos/rust_builder/linux/CMakeLists.txt new file mode 100644 index 0000000000..e6656c9227 --- /dev/null +++ b/mobile/apps/photos/rust_builder/linux/CMakeLists.txt @@ -0,0 +1,19 @@ +# The Flutter tooling requires that developers have CMake 3.10 or later +# installed. You should not increase this version, as doing so will cause +# the plugin to fail to compile for some customers of the plugin. +cmake_minimum_required(VERSION 3.10) + +# Project-level configuration. +set(PROJECT_NAME "rust_lib_photos") +project(${PROJECT_NAME} LANGUAGES CXX) + +include("../cargokit/cmake/cargokit.cmake") +apply_cargokit(${PROJECT_NAME} ../../rust rust_lib_photos "") + +# List of absolute paths to libraries that should be bundled with the plugin. +# This list could contain prebuilt libraries, or libraries created by an +# external build triggered from this build file. +set(rust_lib_photos_bundled_libraries + "${${PROJECT_NAME}_cargokit_lib}" + PARENT_SCOPE +) diff --git a/mobile/apps/photos/rust_builder/macos/Classes/dummy_file.c b/mobile/apps/photos/rust_builder/macos/Classes/dummy_file.c new file mode 100644 index 0000000000..e06dab9968 --- /dev/null +++ b/mobile/apps/photos/rust_builder/macos/Classes/dummy_file.c @@ -0,0 +1 @@ +// This is an empty file to force CocoaPods to create a framework. diff --git a/mobile/apps/photos/rust_builder/macos/rust_lib_photos.podspec b/mobile/apps/photos/rust_builder/macos/rust_lib_photos.podspec new file mode 100644 index 0000000000..e929e5848e --- /dev/null +++ b/mobile/apps/photos/rust_builder/macos/rust_lib_photos.podspec @@ -0,0 +1,44 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint rust_lib_photos.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'rust_lib_photos' + s.version = '0.0.1' + s.summary = 'A new Flutter FFI plugin project.' + s.description = <<-DESC +A new Flutter FFI plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + + # This will ensure the source files in Classes/ are included in the native + # builds of apps using this FFI plugin. Podspec does not support relative + # paths, so Classes contains a forwarder C file that relatively imports + # `../src/*` so that the C sources can be shared among all target platforms. + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'FlutterMacOS' + + s.platform = :osx, '10.11' + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + s.swift_version = '5.0' + + s.script_phase = { + :name => 'Build Rust library', + # First argument is relative path to the `rust` folder, second is name of rust library + :script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../../rust rust_lib_photos', + :execution_position => :before_compile, + :input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'], + # Let XCode know that the static library referenced in -force_load below is + # created by this build step. + :output_files => ["${BUILT_PRODUCTS_DIR}/librust_lib_photos.a"], + } + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + # Flutter.framework does not contain a i386 slice. + 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', + 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/librust_lib_photos.a -lc++', + } +end \ No newline at end of file diff --git a/mobile/apps/photos/rust_builder/pubspec.yaml b/mobile/apps/photos/rust_builder/pubspec.yaml new file mode 100644 index 0000000000..2be7b960ab --- /dev/null +++ b/mobile/apps/photos/rust_builder/pubspec.yaml @@ -0,0 +1,34 @@ +name: rust_lib_photos +description: "Utility to build Rust code" +version: 0.0.1 +publish_to: none + +environment: + sdk: ">=3.3.0 <4.0.0" + flutter: ">=3.3.0" + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + ffi: ^2.0.2 + ffigen: ^11.0.0 + flutter_lints: ^2.0.0 + flutter_test: + sdk: flutter + +flutter: + plugin: + platforms: + android: + ffiPlugin: true + ios: + ffiPlugin: true + linux: + ffiPlugin: true + macos: + ffiPlugin: true + windows: + ffiPlugin: true diff --git a/mobile/apps/photos/rust_builder/windows/.gitignore b/mobile/apps/photos/rust_builder/windows/.gitignore new file mode 100644 index 0000000000..b3eb2be169 --- /dev/null +++ b/mobile/apps/photos/rust_builder/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/mobile/apps/photos/rust_builder/windows/CMakeLists.txt b/mobile/apps/photos/rust_builder/windows/CMakeLists.txt new file mode 100644 index 0000000000..4640016d5c --- /dev/null +++ b/mobile/apps/photos/rust_builder/windows/CMakeLists.txt @@ -0,0 +1,20 @@ +# The Flutter tooling requires that developers have a version of Visual Studio +# installed that includes CMake 3.14 or later. You should not increase this +# version, as doing so will cause the plugin to fail to compile for some +# customers of the plugin. +cmake_minimum_required(VERSION 3.14) + +# Project-level configuration. +set(PROJECT_NAME "rust_lib_photos") +project(${PROJECT_NAME} LANGUAGES CXX) + +include("../cargokit/cmake/cargokit.cmake") +apply_cargokit(${PROJECT_NAME} ../../../../../../rust rust_lib_photos "") + +# List of absolute paths to libraries that should be bundled with the plugin. +# This list could contain prebuilt libraries, or libraries created by an +# external build triggered from this build file. +set(rust_lib_photos_bundled_libraries + "${${PROJECT_NAME}_cargokit_lib}" + PARENT_SCOPE +) From b91759290107d4e4b2ed04b67820df63a60e1545 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 14 Aug 2025 13:47:30 +0530 Subject: [PATCH 150/156] specify rust version --- .github/workflows/mobile-daily-internal.yml | 1 + .github/workflows/mobile-internal-release.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/mobile-daily-internal.yml b/.github/workflows/mobile-daily-internal.yml index 0429b8c001..2dee4c4ef4 100644 --- a/.github/workflows/mobile-daily-internal.yml +++ b/.github/workflows/mobile-daily-internal.yml @@ -8,6 +8,7 @@ on: env: FLUTTER_VERSION: "3.32.8" + RUST_VERSION: "1.85.1" permissions: contents: read diff --git a/.github/workflows/mobile-internal-release.yml b/.github/workflows/mobile-internal-release.yml index 43b2124cb8..768609dfa3 100644 --- a/.github/workflows/mobile-internal-release.yml +++ b/.github/workflows/mobile-internal-release.yml @@ -5,6 +5,7 @@ on: env: FLUTTER_VERSION: "3.32.8" + RUST_VERSION: "1.85.1" permissions: contents: write From 9b23ec59537bd3a203915c08f69a795ed56e2b23 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 14 Aug 2025 13:55:34 +0530 Subject: [PATCH 151/156] No dependency on rust version specifically --- mobile/apps/photos/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/apps/photos/README.md b/mobile/apps/photos/README.md index 4b5f015838..8558c8ba30 100644 --- a/mobile/apps/photos/README.md +++ b/mobile/apps/photos/README.md @@ -46,7 +46,7 @@ You can alternatively install the build from PlayStore or F-Droid. ## 🧑‍💻 Building from source -1. Install [Flutter v3.32.8](https://flutter.dev/docs/get-started/install) and [Rust v1.85.1](https://www.rust-lang.org/tools/install). +1. Install [Flutter v3.32.8](https://flutter.dev/docs/get-started/install) and [Rust](https://www.rust-lang.org/tools/install). 2. Install [Flutter Rust Bridge](https://cjycode.com/flutter_rust_bridge/) with `cargo install flutter_rust_bridge_codegen` From 00b9d277d2535016d4412560a1d6b94284fe88d3 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 14 Aug 2025 17:29:37 +0530 Subject: [PATCH 152/156] load text embeddings for memories from assets --- .../photos/assets/ml/text_embeddings.json | 3530 +++++++++++++++++ .../lib/services/smart_memories_service.dart | 28 +- .../lib/utils/text_embeddings_util.dart | 172 + mobile/apps/photos/pubspec.yaml | 1 + 4 files changed, 3714 insertions(+), 17 deletions(-) create mode 100644 mobile/apps/photos/assets/ml/text_embeddings.json create mode 100644 mobile/apps/photos/lib/utils/text_embeddings_util.dart diff --git a/mobile/apps/photos/assets/ml/text_embeddings.json b/mobile/apps/photos/assets/ml/text_embeddings.json new file mode 100644 index 0000000000..5714cbdc67 --- /dev/null +++ b/mobile/apps/photos/assets/ml/text_embeddings.json @@ -0,0 +1,3530 @@ +{ + "version": "1.0.0", + "embeddings": { + "clip_positive": { + "prompt": "Photo of a precious and nostalgic memory radiating warmth, vibrant energy, or quiet beauty — alive with color, light, or emotion", + "vector": [ + -0.00029399772756733, -0.0024989808443933725, 0.0030610354151576757, + -0.04166121035814285, 0.03460526093840599, 0.050619494169950485, + -0.0188677366822958, 0.017285337671637535, -0.00911393016576767, + -0.013402838259935379, 0.0120625551789999, 0.0006917593418620527, + -0.02497251331806183, -0.022732943296432495, 0.029347892850637436, + 0.5122391581535339, -0.01061850693076849, -0.003770088544115424, + 0.012226847000420094, -0.05049843713641167, 0.0034501501359045506, + -0.02298370562493801, -0.03317851200699806, 0.0007177003426477313, + 0.03588501736521721, -0.05409558489918709, 0.038496412336826324, + 0.02857831120491028, -0.01419836189597845, 0.016654107719659805, + -0.010687682777643204, -0.0005015255883336067, 0.011595616117119789, + -0.020847897976636887, -0.011708027683198452, -0.020311785861849785, + -0.007099180947989225, -0.008629699237644672, -0.025595098733901978, + -0.028958778828382492, 0.014060010202229023, -0.020164787769317627, + 0.01764851063489914, -0.006684124935418367, -0.0515793077647686, + -0.034864671528339386, 0.006900300271809101, -0.015357058495283127, + -0.010073745623230934, 0.02983212284743786, 0.02481686696410179, + 0.05888601765036583, 0.008344347588717937, -0.04051980748772621, + -0.023208526894450188, -0.008880460634827614, -0.02613985724747181, + -0.017466925084590912, -0.02899336628615856, 0.007574765477329493, + 0.005715662147849798, 0.0017726332880556583, 0.03105134889483452, + 0.0461922325193882, -0.07244450598955154, 0.03219275176525116, + -0.013783305883407593, -0.008759403601288795, 0.041064564138650894, + 0.1308203488588333, 0.010998974554240704, 0.002741096541285515, + -0.006476596929132938, -0.007246179506182671, -0.003692265832796693, + 0.0019196323119103909, -0.05541857331991196, -0.024730399250984192, + -0.013100193813443184, -0.022196829319000244, -0.028197843581438065, + -0.02845725230872631, 0.05446740612387657, 0.025188688188791275, + 0.00250762770883739, 0.000709053419996053, 0.005534074734896421, + -0.01690487004816532, 0.036066606640815735, -0.02184230275452137, + 0.026598148047924042, -0.004738552030175924, 0.011768556199967861, + 0.0007177003426477313, -0.006554420106112957, 0.012996429577469826, + -0.012676490470767021, 0.008292465470731258, -0.013281780295073986, + -0.014008128084242344, 0.024574752897024155, 0.02172989211976528, + -0.026814322918653488, 0.0041851443238556385, 0.020839251577854156, + -0.004401318728923798, -0.06879547238349915, -0.01034180261194706, + 0.0008992872317321599, 0.010436920449137688, -0.014950649812817574, + -0.020406901836395264, 0.13213469088077545, -0.009822983294725418, + -0.014103244990110397, -0.01761392317712307, 0.018366212025284767, + 0.013108840212225914, -0.0133509561419487, 0.0009944040793925524, + 0.013437426649034023, -0.025984210893511772, -0.011457265354692936, + 0.008110878989100456, 0.02050201967358589, 0.03446691110730171, + 0.021470481529831886, 0.002836213679984212, -0.00470396364107728, + 0.003147505223751068, -0.04847504198551178, 0.002239570952951908, + 0.016213109716773033, -0.013394190929830074, -0.005931836552917957, + -0.033143918961286545, -0.02115054242312908, -0.0012451668735593557, + -0.002792978659272194, 0.007669882383197546, 0.019818907603621483, + -0.007133768871426582, 0.01491606142371893, -0.023805169388651848, + 0.005404370371252298, -0.0260793287307024, -0.019075265154242516, + 0.010350449942052364, -0.009710571728646755, 0.022041182965040207, + -0.031284816563129425, -0.012953193858265877, -0.025223277509212494, + -0.018997443839907646, -0.042716141790151596, -0.05844501778483391, + 0.03895470127463341, -0.01744963228702545, 0.024635281413793564, + 0.023364173248410225, -0.01995725743472576, 0.009010165929794312, + -0.003735500853508711, -0.006571713835000992, -0.039144936949014664, + -0.030774645507335663, 0.02484280988574028, -0.5175830125808716, + -0.00013835188292432576, -0.029460303485393524, -0.04680616781115532, + 0.07913727313280106, 0.001184638007543981, -0.0010376391001045704, + 0.0103677436709404, 0.013930304907262325, -0.03406050056219101, + -0.022041182965040207, -0.0009165811934508383, 0.038513705134391785, + 0.007324002683162689, -0.0007522883242927492, 0.0077649992890655994, + -0.0019109854474663734, -0.013212604448199272, -0.035461317747831345, + 0.014717180281877518, -0.016334168612957, 0.02303558774292469, + -0.028232431039214134, -0.019723789766430855, -0.039118994027376175, + 0.04071004316210747, -0.03521054983139038, 0.03808135166764259, + -0.033066097646951675, 0.022058477625250816, -0.056828033179044724, + -0.03027312271296978, -0.016766518354415894, 0.04006151854991913, + -0.06835447251796722, 0.03892011195421219, 0.0032945042476058006, + -0.014587475918233395, 0.010289921425282955, 0.05095672607421875, + -0.015063060447573662, -0.008396229706704617, -0.017302630469202995, + -0.02613985724747181, 0.023173939436674118, -0.058012671768665314, + 0.038505055010318756, -0.025067631155252457, -0.017795508727431297, + 0.03590231016278267, 0.004807727411389351, 0.022231418639421463, + -0.029304657131433487, -0.021980654448270798, -0.016558989882469177, + -0.005413017235696316, 0.0230528824031353, 0.025819920003414154, + 0.012693784199655056, -0.010921151377260685, 0.0011586970649659634, + 0.026165800169110298, -0.04114238917827606, -0.02717749774456024, + 0.017155632376670837, -0.010549331083893776, -0.019057970494031906, + -0.007254826370626688, -0.024756338447332382, -0.0039170878008008, + -0.022854000329971313, -0.007220238912850618, -0.00874210987240076, + 0.001642928458750248, 0.017216162756085396, -0.03951675444841385, + 0.006787888705730438, -0.08730003982782364, -0.004505082964897156, + -0.04747198894619942, 0.03839264437556267, -0.0714760422706604, + -0.02231788821518421, 0.02241300418972969, -0.01946437917649746, + -0.037977591156959534, 0.05524563416838646, -0.05752843618392944, + -0.0018331623869016767, -0.022577296942472458, -0.02236112207174301, + -0.00878534372895956, 0.0025595095939934254, 0.014976590871810913, + -0.03655948489904404, -0.005629192106425762, 0.002792978659272194, + 0.020303137600421906, -0.03268563002347946, 0.006104776635766029, + -0.03560831397771835, 0.014475065283477306, 0.03400862216949463, + -0.028379429131746292, 0.002991859335452318, -0.007384531665593386, + -0.02546539157629013, 0.028102725744247437, 0.02373599447309971, + 0.019879436120390892, -0.013644954189658165, -0.031820930540561676, + -0.05662915110588074, -0.008214643225073814, -0.03750200942158699, + -0.009260928258299828, 0.005101725459098816, 0.012287376448512077, + -0.0027583905030041933, -0.029373833909630775, 0.013316367752850056, + -0.0017380454810336232, -0.037839241325855255, -0.026969969272613525, + -0.017345866188406944, -0.03754524141550064, -0.026451149955391884, + -0.000432349625043571, 0.020432842895388603, -0.05502945929765701, + 0.12296022474765778, 0.03770088404417038, -0.00823193695396185, + -0.01582399755716324, -0.002083925064653158, -0.003778735874220729, + -0.021487776190042496, 0.033636800944805145, 0.012218199670314789, + 0.026831617578864098, 0.03803811967372894, -0.047506578266620636, + -0.006044247653335333, 0.08365964889526367, 0.02974565327167511, + 0.04988449811935425, -0.009900806471705437, 0.019628673791885376, + -0.041557442396879196, -0.051933836191892624, -0.03379244729876518, + -0.036023370921611786, -0.02920953929424286, 0.0035193259827792645, + 0.03687077388167381, -0.051933836191892624, 0.03700912743806839, + -0.046927228569984436, -0.0038652056828141212, 0.0414709746837616, + 0.012909960001707077, -0.032296519726514816, -0.037899766117334366, + 0.023312291130423546, 0.0060356007888913155, 0.02307017706334591, + 0.04563017934560776, -0.01060121227055788, -0.008672933094203472, + -0.028673425316810608, 0.06280311197042465, 0.016109347343444824, + -0.011050855740904808, 0.007799586746841669, 0.013878422789275646, + 0.026503032073378563, 0.015443528071045876, 0.034890614449977875, + 0.0038652056828141212, -0.01761392317712307, 0.0453534759581089, + -0.022611886262893677, -0.010203450918197632, -0.021435894072055817, + 0.03638654574751854, -0.01030721515417099, -0.008880460634827614, + -0.0024643929209560156, 0.01574617438018322, 0.048907388001680374, + -0.007324002683162689, -0.01999184675514698, -0.0032772100530564785, + 0.006148011423647404, -0.017821451649069786, 0.022888587787747383, + -0.00996998231858015, 0.00530060613527894, -0.0028535074088722467, + 0.012174965813755989, -0.007263473235070705, -0.0059664249420166016, + 0.027722256258130074, -0.03201116621494293, 0.012140377424657345, + -0.012339258566498756, -0.03099946863949299, 0.02184230275452137, + -0.023355526849627495, 0.04307067021727562, 0.019663261249661446, + -0.15298259258270264, 0.009044754318892956, 0.0033896209206432104, + 0.0019369263900443912, -0.026433855295181274, -0.002127160085365176, + 0.0015564586501568556, -0.010557977482676506, -0.0012711079325526953, + 0.00004323495886637829, 0.03380109369754791, -0.06299334019422531, + 0.040390100330114365, -0.05773596838116646, 0.023208526894450188, + 0.027644433081150055, 0.005490840412676334, 0.02726396732032299, + -0.006433362141251564, -0.026814322918653488, -0.015296529047191143, + -0.014207008294761181, 0.008915049023926258, -0.034328557550907135, + 0.0038392646238207817, -0.023441996425390244, -0.0007177003426477313, + 0.030688177794218063, -0.06485243886709213, -0.06524156033992767, + -0.010350449942052364, -0.0582980215549469, 0.02074413374066353, + -0.012166318483650684, -0.010497448965907097, -0.06057218462228775, + 0.01513223722577095, -0.008854520507156849, 0.03753659501671791, + -0.06895976513624191, 0.011431324295699596, 0.004055439494550228, + 0.05822884663939476, -0.0021876890677958727, -0.017743628472089767, + -0.008845873177051544, -0.010142921470105648, -0.0071164751425385475, + 0.03211492672562599, 0.008889107964932919, 0.01885044202208519, + 0.014673946425318718, 0.02731584943830967, 0.016636813059449196, + 0.011647499166429043, -0.008370288647711277, -0.06652131676673889, + 0.03981940075755119, -0.012806195765733719, 0.001418106839992106, + 0.01152644120156765, 0.001720751402899623, 0.008889107964932919, + -0.04371919482946396, -0.013307721354067326, 0.004686669912189245, + 0.0047644926235079765, -0.019144441932439804, -0.00824058335274458, + -0.00530060613527894, -0.00408138008788228, -0.04931379854679108, + 0.04058033600449562, 0.0005620545125566423, 0.02373599447309971, + 0.023874346166849136, 0.0047644926235079765, -0.005854013841599226, + -0.01065309438854456, 0.00815411377698183, -0.0022655120119452477, + -0.003303151112049818, 0.012347905896604061, -0.005646485835313797, + -0.005075784400105476, -0.0066149490885436535, 0.01877262070775032, + -0.05195113271474838, -0.000821464229375124, 0.030852466821670532, + 0.061609819531440735, -0.02860425040125847, -0.003311798209324479, + 0.0008560522692278028, 0.00019023384083993733, -0.03269427642226219, + -0.03091299906373024, -0.01030721515417099, 0.012598667293787003, + -0.024453694000840187, 0.017138339579105377, 0.016775164753198624, + -0.04474818706512451, 0.0442812480032444, 0.045647475868463516, + -0.02374464087188244, -0.07572171837091446, -0.0068311239592731, + -0.16558989882469177, 0.021409953013062477, -0.004202438518404961, + 0.03092164546251297, -0.01999184675514698, 0.046512171626091, + -0.00654577324166894, 0.029901299625635147, 0.007998468354344368, + -0.004193791188299656, -0.022542709484696388, -0.04104727506637573, + -0.010238038375973701, -0.01581534929573536, 0.014379948377609253, + -0.014959297142922878, -0.017890628427267075, 0.0078774094581604, + 0.01431941892951727, 0.002749743638560176, -0.034916553646326065, + 0.03393079712986946, 0.00498931435868144, 0.014258889481425285, + 0.007609352935105562, -0.03220139816403389 + ] + }, + "people_activities": { + "admiring": { + "prompt": "Photo of two people admiring or looking at each other in a loving but non-intimate and non-physical way", + "vector": [ + -0.03514610603451729, -0.02812313660979271, -0.03186924010515213, + 0.0643484964966774, 0.015219029039144516, 0.025284234434366226, + 0.007867596112191677, 0.0054588294588029385, -0.03783641383051872, + -0.061493948101997375, 0.027771208435297012, -0.01790934056043625, + -0.013334247283637524, -0.014499526470899582, 0.025425007566809654, + 0.5175095200538635, 0.023548046126961708, 0.02630092203617096, + -0.013521943241357803, -0.004700224380940199, -0.0021350437309592962, + -0.00952557846903801, -0.008117858320474625, 0.000656936492305249, + -0.0020646576303988695, -0.011770112439990044, -0.0049817683175206184, + -0.021170560270547867, -0.021561594679951668, 0.035865604877471924, + -0.001986450981348753, -0.00030500622233375907, 0.03502097353339195, + -0.04644697532057762, -0.026433873921632767, -0.00983840599656105, + -0.04971601441502571, 0.03028164431452751, -0.013514121994376183, + -0.043123189359903336, 0.010245081037282944, 0.0002502615097910166, + -0.012333200313150883, 0.0380319319665432, -0.011332154273986816, + -0.026715418323874474, -0.016024557873606682, -0.053931355476379395, + -0.011457285843789577, 0.03927541896700859, -0.0016579825896769762, + 0.03470032289624214, 0.005028692539781332, 0.02120966464281082, + 0.018581919372081757, 0.008375939913094044, 0.03340991586446762, + 0.0073123290203511715, -0.033738382160663605, -0.01745574176311493, + -0.023508941754698753, 0.026191432029008865, -0.01151202991604805, + 0.06702316552400589, -0.015672627836465836, 0.0440773107111454, + 0.0014468245208263397, -0.0020020920783281326, 0.011918704956769943, + 0.08666869252920151, -0.033965181559324265, 0.03676498308777809, + 0.02721594087779522, 0.040456339716911316, -0.004653300624340773, + -0.01441350020468235, -0.09551387280225754, 0.03524777293205261, + 0.02456473372876644, -0.0435924269258976, -0.03026600182056427, + -0.03004702553153038, 0.019105903804302216, -0.01288846880197525, + 0.005091257859021425, 0.0017909340094774961, -0.0007586052524857223, + -0.021913524717092514, -0.004708044696599245, 0.03254964202642441, + 0.013279502280056477, 0.02537808194756508, -0.01927795819938183, + 0.0407300628721714, -0.033065807074308395, 0.05871760845184326, + 0.0424584299325943, -0.012638206593692303, 0.0023696639109402895, + 0.020411955192685127, 0.0015719551593065262, -0.02852199412882328, + 0.0037695644423365593, -0.01983322575688362, 0.0449688695371151, + 0.06520094722509384, -0.06771138310432434, 0.036256637424230576, + -0.001133997575379908, -0.007437459193170071, 0.01277115847915411, + -0.008985953405499458, 0.15607717633247375, 0.03256528079509735, + 0.018472427502274513, -0.037468839436769485, 0.005607421975582838, + -0.000656936492305249, -0.010049563832581043, 0.030602293089032173, + 0.007640797644853592, -0.028170062229037285, 0.0023774844594299793, + 0.0002033374912571162, 0.010229440405964851, -0.0038164884317666292, + -0.000383212958695367, -0.018433324992656708, 0.001313872984610498, + -0.04119930416345596, 0.015125180594623089, 0.005896787159144878, + -0.0011496389051899314, 0.0011418182402849197, 0.05330570414662361, + 0.0010870734695345163, 0.0002502615097910166, 0.01318565383553505, + 0.038243088871240616, -0.004215342458337545, 0.00880607683211565, + -0.0019395267590880394, -0.024056388065218925, 0.0031908343080431223, + -0.002705952851101756, -0.03058665059506893, -0.022445330396294594, + 0.03086819313466549, -0.05810759961605072, -0.033957358449697495, + -0.0183003731071949, 0.028185702860355377, -0.04283382371068001, + 0.03940054774284363, 0.03269823268055916, 0.013287322595715523, + 0.02150684967637062, 0.01159023679792881, 0.01299795787781477, + -0.030156515538692474, -0.005013051442801952, 0.011339975520968437, + -0.058287475258111954, 0.024901021271944046, -0.04110545292496681, + 0.031525131314992905, 0.021233126521110535, -0.5212634801864624, + 0.004629838280379772, -0.018581919372081757, -0.012685131281614304, + 0.08338400721549988, -0.0017987547907978296, 0.018628841266036034, + -0.013091806322336197, 0.0011261767940595746, 0.022773798555135727, + -0.034129414707422256, 0.022398406639695168, -0.01181703619658947, + -0.0018143960041925311, 0.014280547387897968, -0.02295367419719696, + -0.027935443446040154, 0.010315467603504658, -0.020005280151963234, + -0.03294849395751953, -0.010722142644226551, 0.01478889212012291, + -0.01216114591807127, -0.025135641917586327, -0.0015250311698764563, + 0.045164383947849274, -0.02295367419719696, -0.01304488256573677, + -0.039541322737932205, 0.013576687313616276, 0.05171810835599899, + -0.03906426206231117, 0.01678316295146942, -0.017354073002934456, + 0.02462729811668396, -0.003417633706703782, 0.03437967598438263, + 0.010440598241984844, -0.026777982711791992, 0.001618879148736596, + -0.031994372606277466, -0.01502351276576519, 0.012434869073331356, + 0.01632174476981163, -0.03318311646580696, -0.055472031235694885, + -0.002095940290018916, -0.0010557908099144697, -0.0758214220404625, + -0.009236213751137257, -0.03192398324608803, -0.007320149801671505, + -0.0029874970205128193, 0.0059046074748039246, -0.024955766275525093, + -0.013709639199078083, 0.051647722721099854, -0.01044841855764389, + -0.03086819313466549, 0.021929167211055756, -0.024134594947099686, + -0.01303706131875515, -0.010683038271963596, -0.03432493284344673, + -0.02806839346885681, 0.007140273693948984, -0.04942664876580238, + -0.019567323848605156, 0.035200849175453186, -0.01064393576234579, + -0.04413987696170807, -0.03332388401031494, -0.02324303798377514, + 0.01822216622531414, 0.0031830137595534325, -0.03575611487030983, + 0.021225305274128914, 0.004246625117957592, 0.006279999855905771, + -0.01353758480399847, -0.007335790432989597, -0.07419472187757492, + -0.04053454473614693, 0.006381668616086245, -0.04254445806145668, + 0.027333252131938934, 0.01059701107442379, 0.02210904285311699, + -0.012520897202193737, -0.005599601659923792, 0.054330214858055115, + -0.02067003771662712, 0.037703461945056915, -0.027638258412480354, + -0.013787846080958843, 0.03139999881386757, 0.061705105006694794, + -0.02394689992070198, -0.0218900628387928, 0.01943437196314335, + 0.027192478999495506, 0.009595965966582298, -0.005404084920883179, + 0.0025182566605508327, -0.014585554599761963, -0.0285845585167408, + -0.031665902584791183, 0.04905908182263374, 0.014202342368662357, + -0.01342027448117733, -0.006068842019885778, -0.0059202490374445915, + -0.033214397728443146, 0.05482291802763939, 0.04771392419934273, + -0.026011556386947632, 0.016220074146986008, 0.05205439776182175, + 0.04306844249367714, -0.02675452083349228, 0.016525082290172577, + -0.04948921501636505, -0.035357262939214706, 0.006913474760949612, + -0.006960398517549038, -0.05734899267554283, -0.018941668793559074, + 0.01866794563829899, 0.004614196717739105, -0.03132961690425873, + 0.015195567160844803, 0.048081494867801666, 0.021240947768092155, + 0.0073279705829918385, 0.002627745969220996, 0.013553226366639137, + 0.026410412043333054, 0.003980722278356552, -0.028248269110918045, + -0.031611159443855286, 0.007492204196751118, -0.0015406724996864796, + 0.03597509488463402, 0.032831184566020966, -0.0025730011984705925, + -0.009658530354499817, -0.03331606462597847, -0.015563137829303741, + 0.004801893141120672, -0.06827446818351746, 0.02409549243748188, + -0.05810759961605072, -0.0327138751745224, 0.04088647663593292, + 0.05888184532523155, 0.014085031114518642, 0.04640787094831467, + -0.0346846841275692, -0.013561046682298183, -0.019864508882164955, + 0.04460911825299263, -0.019262315705418587, -0.0020411955192685127, + 0.020028743892908096, -0.02737235277891159, 0.04531297832727432, + 0.044327571988105774, -0.0480189323425293, -0.005888966377824545, + 0.0035349440295249224, -0.021006327122449875, -0.030696140602231026, + -0.02301623858511448, -0.01360797043889761, -0.013013599440455437, + 0.03806321322917938, 0.010870734229683876, -0.014311830513179302, + -0.016634570434689522, 0.0050208717584609985, -0.005302416160702705, + -0.04763571545481682, -0.0004535990010481328, -0.027028243988752365, + 0.038462068885564804, -0.00024244084488600492, -0.05312582850456238, + 0.016110586002469063, 0.04937972500920296, -0.05849863216280937, + -0.03499750792980194, 0.010815990157425404, 0.029616886749863625, + -0.018636662513017654, -0.019629888236522675, -0.001094894134439528, + 0.01528941374272108, 0.03591252863407135, -0.03255745768547058, + -0.00044577830703929067, -0.037007421255111694, 0.03003138303756714, + 0.006577185820788145, -0.027880696579813957, -0.04398346319794655, + -0.03299541771411896, -0.010033923201262951, 0.04370191693305969, + -0.01159023679792881, 0.03227591514587402, -0.04453872889280319, + -0.07761235535144806, 0.0002971855574287474, -0.049590885639190674, + -0.015680449083447456, -0.029718557372689247, 0.008438506163656712, + 0.02980458363890648, 0.008485429920256138, -0.005411905702203512, + -0.007202839478850365, 0.026402590796351433, -0.04015133157372475, + -0.07478127628564835, 0.007476563099771738, 0.01861320249736309, + -0.011379078961908817, -0.02295367419719696, -0.0015406724996864796, + -0.001290410989895463, -0.031063711270689964, -0.03436403349041939, + -0.11970321834087372, -0.02407984994351864, -0.0426461286842823, + -0.023657534271478653, -0.014968767762184143, -0.030633574351668358, + -0.018511531874537468, 0.020232081413269043, 0.011371258646249771, + 0.006788343656808138, -0.02462729811668396, -0.024431781843304634, + 0.01577429659664631, 0.01730714924633503, 0.011050610803067684, + 0.04086301475763321, 0.05634012445807457, 0.011191382072865963, + -0.028170062229037285, 0.007828493602573872, 0.027489662170410156, + 0.045492853969335556, -0.03988543152809143, -0.01239576656371355, + 0.0060923038981854916, -0.022586101666092873, -0.007562590297311544, + 0.006506799720227718, -0.023892154917120934, 0.04185623675584793, + -0.025659628212451935, -0.02623053640127182, -0.021780572831630707, + 0.0010323288151994348, 0.0032221172004938126, -0.05886620655655861, + 0.024204982444643974, 0.032526176422834396, -0.002189788268879056, + -0.013733101077377796, -0.01262256596237421, -0.002056836849078536, + -0.0275991540402174, 0.024197161197662354, 0.01943437196314335, + -0.0056856293231248856, 0.03224463388323784, -0.008915566839277744, + 0.030477160587906837, -0.0035896888002753258, -0.05702834203839302, + -0.03987761214375496, -0.015602242201566696, 0.010385853238403797, + -0.021084534004330635, 0.006827447097748518, -0.001994271529838443, + 0.02142864279448986, 0.03279989957809448, 0.014929663389921188, + 0.037703461945056915, -0.0015719551593065262, 0.008712229318916798, + -0.027739927172660828, -0.03369927778840065, 0.007468742318451405, + 0.04313100874423981, -0.03158769756555557, 0.0424036867916584, + -0.0005318057374097407, 0.02143646404147148, -0.016603287309408188, + 0.013443736359477043, -0.03951003775000572, 0.02334470860660076, + -0.0018769614398479462, -0.010995865799486637, 0.0017127272440120578, + -0.005333698354661465, -0.008939028717577457, 0.018355118110775948, + 0.018574098125100136, 0.10098834335803986, 0.0284359659999609, + -0.016517261043190956, 0.023070983588695526, -0.026715418323874474, + -0.10452328622341156, -0.018808718770742416, 0.006491158157587051, + 0.002627745969220996, 0.025417186319828033, 0.03117320127785206, + 0.035106997936964035, 0.003480199258774519, 0.006592826917767525, + 0.01292757224291563, 0.021029789000749588, -0.029085082933306694, + 0.009580324403941631, -0.00019551682635210454, -0.021905705332756042, + 0.004966127220541239, 0.0443197526037693, 0.007953624241054058, + 0.0203650314360857, 0.02370445802807808, -0.000766425917390734, + 0.01683790795505047, 0.010628294199705124, 0.002705952851101756, + 0.005896787159144878, -0.011989091522991657 + ] + }, + "embracing": { + "prompt": "Photo of people hugging or embracing each other lovingly, without inappropriately kissing or other intimate actions", + "vector": [ + 0.03190479800105095, 0.012113233096897602, -0.022465744987130165, + 0.020599115639925003, 0.026688827201724052, -0.00448123412206769, + 0.016892332583665848, 0.035790298134088516, -0.07396353781223297, + -0.04633476957678795, 0.010511374101042747, -0.010220126248896122, + 0.014443210326135159, 0.01785212568938732, 0.06136709451675415, + 0.5228084325790405, -0.00482543557882309, -0.001628336263820529, + -0.03713400661945343, -0.01580677554011345, -0.032546866685152054, + -0.02028800919651985, -0.060705170035362244, 0.004487853497266769, + 0.010140695609152317, 0.011874939315021038, -0.04372016713023186, + 0.00482543557882309, -0.009644251316785812, 0.041899871081113815, + -0.039265409111976624, -0.016250265762209892, 0.0030117600690573454, + -0.05602535605430603, -0.022598128765821457, -0.0026146050076931715, + -0.0355321504175663, 0.041357092559337616, 0.016131119802594185, + -0.024047747254371643, 0.029846210032701492, -0.009472151286900043, + -0.01952679641544819, 0.04841983690857887, -0.003971552010625601, + -0.05485374853014946, -0.02879374846816063, -0.05014083907008171, + -0.014535878784954548, 0.012867826968431473, -0.028178159147500992, + 0.08048349618911743, -0.0032235761173069477, 0.0004103936953470111, + -0.029998455196619034, -0.017263010144233704, 0.03011760115623474, + 0.020751357078552246, 0.0015224282396957278, -0.0069435965269804, + -0.026126191020011902, 0.006460390519350767, 0.006824449636042118, + 0.015098516829311848, -0.03204380348324776, 0.024511093273758888, + -0.005977185443043709, 0.01679966412484646, 0.00009266954293707386, + 0.04995550215244293, -0.017514541745185852, 0.030223509296774864, + 0.04506387189030647, 0.009313289076089859, 0.002720512915402651, + -0.026251958683133125, -0.0866791158914566, 0.035022467374801636, + 0.02159862220287323, -0.015065419487655163, -0.047625523060560226, + -0.024173511192202568, -0.004501091782003641, -0.027092603966593742, + -0.0005030632601119578, -0.0006751638138666749, -0.013264983892440796, + -0.0020718262530863285, 0.024703051894903183, 0.000052954022976337, + -0.02320048026740551, 0.00013900430349167436, -0.021863391622900963, + 0.04317738488316536, 0.03924554958939552, 0.05359609052538872, + 0.025940852239727974, -0.01834856905043125, 0.00448123412206769, + 0.006056616548448801, 0.006797972600907087, -0.01000831089913845, + -0.024696432054042816, -0.033784665167331696, 0.033579468727111816, + 0.000105908045952674, -0.061559051275253296, 0.034195058047771454, + -0.0034552502911537886, -0.005672699771821499, -0.020228436216711998, + -0.014595451764762402, 0.1076025739312172, -0.028734175488352776, + -0.012219141237437725, -0.04081431403756142, 0.015131612308323383, + -0.016806283965706825, 0.008889656513929367, 0.025762131437659264, + 0.03790184110403061, -0.006811210885643959, -0.04401141032576561, + -0.013119358569383621, 0.023597635328769684, 0.011120345443487167, + 0.03059418871998787, -0.02412055805325508, 0.02811196632683277, + 0.014039434492588043, -0.016395889222621918, -0.008532216772437096, + 0.0004699669370893389, 0.013364271260797977, 0.09090881794691086, + -0.043428920209407806, -0.03463193029165268, 0.022988665848970413, + 0.03298373892903328, -0.020506445318460464, -0.015171327628195286, + -0.031130345538258553, -0.0036803046241402626, -0.043462011963129044, + -0.04213154315948486, -0.017481446266174316, -0.031805507838726044, + 0.006612633820623159, -0.0343870185315609, -0.0011252729455009103, + -0.04397169500589371, -0.00045010916073806584, -0.05562158301472664, + 0.02931005321443081, 0.002177734160795808, -0.026728542521595955, + 0.007737906649708748, -0.008671221323311329, 0.0023564540315419436, + -0.0002581508597359061, -0.013212029822170734, -0.03402295708656311, + -0.05244433879852295, 0.017329204827547073, -0.023498348891735077, + 0.015482432208955288, 0.031825367361307144, -0.5259922742843628, + 0.005176255479454994, 0.014251251704990864, -0.0660402849316597, + 0.07855729013681412, 0.0002912471245508641, 0.004295895341783762, + -0.016124499961733818, 0.005341737065464258, 0.02462361939251423, + 0.01058418583124876, 0.0008538836264051497, -0.015608198940753937, + 0.0002382930979365483, 0.010994579643011093, -0.005679319147020578, + -0.020420394837856293, 0.018613338470458984, -0.001542285899631679, + -0.01722329668700695, -0.027430184185504913, 0.008280685171484947, + -0.017124006524682045, 0.02515977993607521, -0.025808466598391533, + -0.020374059677124023, -0.03683614358305931, -0.03654489666223526, + 0.0013503276277333498, -0.005626365076750517, 0.03961622714996338, + -0.005308640655130148, 0.03429434821009636, 0.003104429692029953, + 0.009088234044611454, -0.002799944020807743, 0.018785439431667328, + 0.009756778366863728, 0.018434619531035423, -0.005910992622375488, + 0.012801635079085827, -0.03344046697020531, -0.020466729998588562, + 0.011537358164787292, -0.03674347326159477, -0.006725160870701075, + 0.016912192106246948, -0.02935638464987278, -0.007545948028564453, + 0.04967087507247925, -0.0028528980910778046, -0.014959512278437614, + 0.007115696556866169, -0.038292378187179565, -0.01070995070040226, + -0.0024623621720820665, 0.028264211490750313, -0.028806986287236214, + 0.00784381479024887, 0.017263010144233704, 0.011821985244750977, + 0.005771988537162542, 0.006149285938590765, -0.05329160392284393, + -0.02453095093369484, 0.05043208971619606, -0.08943934738636017, + -0.01077614352107048, 0.004527568817138672, 0.007757764775305986, + -0.017600594088435173, -0.028284067288041115, -0.015965638682246208, + 0.020771216601133347, -0.0007943103555589914, -0.051775798201560974, + 0.0035677773412317038, 0.03422153741121292, -0.0027469899505376816, + -0.026927120983600616, 0.02281656488776207, -0.050988104194402695, + -0.07974876463413239, -0.005169636569917202, -0.05307317152619362, + -0.005043870769441128, -0.03931836411356926, -0.024358851835131645, + 0.00539469113573432, -0.03316907584667206, 0.05090205371379852, + -0.013079644180834293, 0.015469194389879704, -0.037478212267160416, + -0.03344046697020531, 0.08413070440292358, 0.0449976809322834, + -0.0311104878783226, -0.038385048508644104, 0.023233577609062195, + 0.053238652646541595, -0.023835929110646248, -0.02187001146376133, + -0.023114431649446487, 0.008863179944455624, -0.01641574688255787, + -0.05323203280568123, 0.012192663736641407, 0.03041546605527401, + -0.0014496163930743933, -0.008221112191677094, -0.010279699228703976, + -0.007188509218394756, 0.047671858221292496, 0.03461207449436188, + -0.03889473155140877, -0.0011252729455009103, 0.04734089598059654, + 0.02732427604496479, 0.008935990743339062, 0.02318062260746956, + -0.02584156207740307, -0.04824111610651016, 0.012278714217245579, + -0.025199495255947113, -0.05007464811205864, -0.002343215513974428, + 0.010273081250488758, 0.012656011618673801, 0.002171115018427372, + 0.030355893075466156, 0.030475042760372162, -0.020499825477600098, + -0.004666573368012905, 0.006215478293597698, 0.0042429412715137005, + -0.00016548130952287465, 0.015992116183042526, -0.008234350942075253, + 0.018653053790330887, -0.014079151675105095, 0.01740863546729088, + 0.008088726550340652, 0.04458728805184364, 0.006791353691369295, + 0.0020916839130222797, 0.017554258927702904, -0.0005229209782555699, + 0.05415872484445572, -0.06349187344312668, -0.0031838605646044016, + -0.02958144061267376, 0.006738399155437946, 0.04008619859814644, + 0.015866348519921303, -0.00421646423637867, 0.047850579023361206, + -0.02380945347249508, -0.014628549106419086, 0.007744526024907827, + 0.006592776160687208, -0.022055350244045258, 0.025351738557219505, + -0.04393859952688217, 0.005540314596146345, 0.03927202895283699, + 0.06421337276697159, -0.02761552296578884, -0.012788397260010242, + 0.032990358769893646, -0.04202563688158989, 0.0013768046628683805, + 0.0024425042793154716, 0.005593268666416407, -0.01525075826793909, + 0.033513277769088745, -0.0014099008403718472, 0.04309795796871185, + 0.026748400181531906, -0.02103598602116108, 0.007850433699786663, + -0.05066376179456711, 0.039821427315473557, 0.027675095945596695, + 0.01834856905043125, 0.0060963318683207035, -0.03812689706683159, + 0.013814380392432213, 0.04603028669953346, -0.03898078203201294, + -0.02425956167280674, 0.011239491403102875, 0.011782269924879074, + 0.004527568817138672, -0.010756285861134529, -0.018163230270147324, + 0.013642280362546444, 0.03339413180947304, 0.008379973471164703, + 0.020844027400016785, -0.010570947080850601, -0.0039847902953624725, + 0.029945500195026398, -0.0016680517001077533, 0.027608904987573624, + -0.01834856905043125, -0.0027469899505376816, -0.012278714217245579, + 0.005076967179775238, 0.022373074665665627, -0.027423566207289696, + -0.07132244855165482, 0.013827620074152946, -0.025292163714766502, + -0.011716078035533428, -0.051193300634622574, -0.0197253730148077, + 0.02865474671125412, -0.019718755036592484, -0.05842152610421181, + -0.01763368956744671, 0.014390256255865097, -0.027966342866420746, + -0.09944765269756317, -0.054483070969581604, 0.01760721392929554, + 0.004534188192337751, -0.03393691033124924, -0.005275544710457325, + 0.006420675665140152, -0.03853728994727135, -0.03293078392744064, + -0.1083240807056427, 0.01916273683309555, -0.04758581146597862, + -0.007353989407420158, -0.04179396107792854, -0.009816352277994156, + -0.00542778754606843, 0.008379973471164703, -0.0049776784144341946, + 0.02719189040362835, -0.03471798077225685, -0.04080107435584068, + -0.013681996613740921, 0.027469897642731667, -0.004606999922543764, + 0.04767847806215286, 0.01767340488731861, -0.009968595579266548, + -0.011318922974169254, -0.018427999690175056, 0.0280259158462286, + 0.11136892437934875, -0.005540314596146345, -0.0007479756022803485, + 0.015184566378593445, 0.022465744987130165, 0.032725587487220764, + 0.022399552166461945, -0.06292261928319931, -0.010637139901518822, + -0.029885927215218544, -0.004501091782003641, -0.008022534660995007, + 0.017501303926110268, -0.02910485491156578, -0.0008274066494777799, + 0.04789029434323311, 0.005639603361487389, 0.0008207873906940222, + 0.03124949149787426, 0.017885221168398857, -0.03352651372551918, + 0.03794817626476288, 0.0012973735574632883, 0.0005692557897418737, + -0.001979156630113721, 0.02885994128882885, -0.02382931113243103, + 0.014529259875416756, -0.04982311651110649, -0.07375171780586243, + -0.026046760380268097, -0.004606999922543764, 0.020665308460593224, + -0.020870503038167953, 0.0, -0.013940147124230862, + 0.020387299358844757, 0.024828817695379257, -0.0035082038957625628, + -0.009074995294213295, -0.00007281178113771603, + -0.0051630171947181225, -0.016309838742017746, -0.005851419642567635, + -0.012404480017721653, 0.0197452325373888, -0.015820013359189034, + 0.009505246765911579, -0.0018335330532863736, 0.012920781038701534, + 0.006208859384059906, 0.006586156319826841, 0.014151962473988533, + -0.0014099008403718472, -0.02750299498438835, -0.015052181668579578, + 0.001542285899631679, 0.015045562759041786, 0.011610169894993305, + 0.025424549356102943, -0.0015224282396957278, 0.08096670359373093, + 0.020519685000181198, 0.014403493143618107, -0.01109386794269085, + -0.05837519094347954, -0.08014591038227081, 0.011603550054132938, + -0.003686923999339342, 0.009551581926643848, 0.021049223840236664, + -0.012841351330280304, 0.013463560491800308, -0.02214140072464943, + 0.016601087525486946, 0.018249280750751495, -0.009617773815989494, + -0.003931836225092411, 0.017931556329131126, 0.007042884826660156, + -0.049743685871362686, 0.006930357776582241, 0.006222097668796778, + 0.009114711545407772, 0.01834856905043125, -0.012656011618673801, + -0.0026410820428282022, 0.05188170447945595, 0.04044363647699356, + 0.02556355483829975, 0.036624327301979065, -0.049227382987737656 + ] + }, + "party": { + "prompt": "Photo of people celebrating together", + "vector": [ + 0.021657669916749, 0.02267414890229702, -0.03314313665032387, + -0.04715124890208244, -0.000511949067004025, -0.0014542320277541876, + -0.0359848253428936, 0.02541196346282959, -0.016916576772928238, + -0.03479769825935364, 0.015351051464676857, -0.030872752889990807, + -0.013614876195788383, 0.027786219492554665, 0.01966923102736473, + 0.5486461520195007, 0.01717626117169857, 0.006358555518090725, + 0.004251402802765369, -0.031265988945961, 0.010513504035770893, + -0.023905795067548752, -0.013889400288462639, 0.008072471246123314, + -0.00276007317006588, 0.0044517312198877335, -0.019639553502202034, + 0.019068246707320213, 0.0030494355596601963, 0.0061804866418242455, + 0.011262878775596619, -0.019105345010757446, 0.011819345876574516, + -0.030338546261191368, 0.026970067992806435, 0.004889484494924545, + -0.01432715356349945, -0.0034723500721156597, 0.032045044004917145, + 0.021961871534585953, 0.050638437271118164, -0.015625575557351112, + 0.0026710384991019964, 0.03608127683401108, -0.01342196948826313, + -0.04062946140766144, -0.04177207499742508, -0.021271854639053345, + 0.007694074884057045, 0.020099565386772156, -0.00014839103096164763, + 0.03710517659783363, 0.03411509469151497, 0.03371443971991539, + -0.005497887264937162, -0.0050081973895430565, -0.003316539339721203, + 0.003598482348024845, -0.00483012804761529, 0.0036429997999221087, + -0.03180761635303497, -0.01874178647994995, -0.010439308360219002, + -0.07591685652732849, -0.05038617178797722, 0.015766547992825508, + 0.008658615872263908, 0.018140802159905434, 0.0036504194140434265, + 0.03667484223842621, 0.02351255901157856, 0.023327069357037544, + -0.020492801442742348, 0.0028416882269084454, 0.03282409533858299, + 0.005416272673755884, -0.05294591560959816, 0.03551739454269409, + 0.038648445159196854, 0.0064624291844666, -0.015907518565654755, + -0.015202660113573074, -0.02860979177057743, 0.03502028435468674, + -0.027934610843658447, -0.0004525926196947694, 0.014750069007277489, + -0.012813565321266651, 0.0009497025748714805, 0.0012910020304843783, + -0.0749448910355568, -0.005668537225574255, -0.01867501251399517, + 0.0038433277513831854, 0.035465456545352936, 0.039842989295721054, + -0.06788889318704605, 0.024388065561652184, -0.021627992391586304, + 0.0019661812111735344, -0.014987492933869362, 0.017035290598869324, + 0.0014097146922722459, -0.03465672582387924, 0.016983352601528168, + 0.01953567937016487, -0.05583954229950905, 0.015009752474725246, + 0.008176345378160477, -0.002255543600767851, 0.006900182459503412, + -0.03729808330535889, 0.06202002987265587, -0.0035836431197822094, + -0.0038878447376191616, -0.07182125747203827, 0.021071525290608406, + -0.02357933484017849, 0.003858166979625821, 0.013926498591899872, + 0.04360470175743103, -0.02247382141649723, 0.004414633382111788, + -0.011218362487852573, 0.008220863528549671, -0.0069966367445886135, + -0.0021590893156826496, -0.05723441764712334, 0.025790361687541008, + -0.0016916577005758882, -0.018734367564320564, 0.03210439905524254, + -0.005178846884518862, 0.009422830305993557, 0.009838324971497059, + 0.003464930457994342, -0.009445088915526867, -0.009044433012604713, + 0.013681653887033463, 0.006299199070781469, -0.02753395587205887, + 0.004214304964989424, 0.008703134022653103, -0.04569701850414276, + -0.00042291442514397204, 0.026851356029510498, -0.030234670266509056, + 0.0006529205129481852, -0.010632217861711979, -0.004333017859607935, + -0.02006988599896431, -0.02068571001291275, -0.07169512659311295, + 0.027934610843658447, -0.020982490852475166, -0.010098009370267391, + -0.009007335640490055, 0.004844967275857925, 0.022303171455860138, + -0.026153918355703354, -0.020166339352726936, -0.023304810747504234, + -0.013763267546892166, 0.0, 0.011366752907633781, + -0.023230616003274918, 0.02135346829891205, -0.5525710582733154, + 0.010120267979800701, -0.026547156274318695, -0.007575361989438534, + 0.054407574236392975, -0.012294196523725986, 0.014431027695536613, + 0.006425331346690655, 0.011099648661911488, 0.026302309706807137, + 0.0069966367445886135, -0.019884398207068443, -0.008027954958379269, + 0.000022258655008045025, 0.007961179129779339, 0.03180019557476044, + 0.010216722264885902, 0.03761712461709976, -0.009504444897174835, + -0.010847384110093117, -0.00271555595099926, 0.03042016178369522, + -0.009556381963193417, 0.03968718275427818, 0.013948756270110607, + -0.004592702724039555, -0.01942438632249832, 0.004889484494924545, + -0.027563635259866714, -0.0013280997518450022, 0.0203518308699131, + -0.014831682667136192, 0.054207246750593185, -0.010290917940437794, + 0.029329488053917885, -0.01600397191941738, -0.02216220088303089, + 0.016946256160736084, 0.029908213764429092, -0.033261850476264954, + -0.008799588307738304, 0.015195241197943687, -0.00010387371730757877, + -0.00605435436591506, 0.003924942575395107, 0.04178691282868385, + -0.049599699676036835, -0.02055215835571289, -0.014193601906299591, + 0.018860500305891037, -0.03188923001289368, -0.037253569811582565, + -0.014816843904554844, -0.02642844244837761, 0.04197240248322487, + 0.0062546818517148495, 0.0545337051153183, 0.0038433277513831854, + 0.02878785878419876, 0.005304979160428047, 0.013607458211481571, + 0.0035094479098916054, 0.0005490467883646488, -0.0031162116210907698, + -0.010980935767292976, 0.006796309258788824, -0.02159089408814907, + -0.016642054542899132, -0.02638392336666584, -0.022110262885689735, + 0.0011055131908506155, -0.025864554569125175, -0.0061433888040483, + -0.024224834516644478, -0.009519284591078758, -0.02364611066877842, + 0.014935556799173355, -0.027571052312850952, 0.046246062964200974, + -0.010357693769037724, 0.02444000169634819, -0.03789164870977402, + -0.00968993455171585, -0.0007122769602574408, -0.018793722614645958, + -0.038336820900440216, 0.009170565754175186, 0.010847384110093117, + -0.009749290533363819, -0.028275910764932632, 0.006506946869194508, + -0.01429747510701418, 0.021538957953453064, -0.027867835015058517, + -0.026242952793836594, 0.018133383244276047, 0.04194272309541702, + 0.02547873929142952, -0.030895013362169266, 0.021895095705986023, + 0.0123832318931818, 0.006685015745460987, -0.01955793797969818, + -0.016211720183491707, 0.003086533397436142, -0.018289193511009216, + -0.03897490352392197, 0.03490899130702019, -0.019832462072372437, + 0.01676076650619507, -0.010060911998152733, 0.0003264602564740926, + 0.006640498526394367, -0.004563024267554283, 0.019565356895327568, + 0.025975851342082024, 0.0015803645364940166, 0.001528427586890757, + 0.0447324737906456, -0.025582613423466682, 0.0214350838214159, + -0.02679941989481449, 0.018519200384616852, -0.03084307350218296, + -0.03813649341464043, -0.03435994312167168, 0.0012539041927084327, + 0.025107761844992638, -0.0014022953109815717, -0.0023223196621984243, + 0.0049191624857485294, 0.025352606549859047, -0.011938057839870453, + 0.003976879641413689, 0.0017584336455911398, 0.025419384241104126, + -0.018400488421320915, 0.02435096725821495, 0.00271555595099926, + 0.01448296383023262, 0.014445867389440536, 0.016901738941669464, + 0.005312399007380009, -0.013043572194874287, 0.017391428351402283, + -0.00848796684294939, 0.011952897533774376, -0.020960234105587006, + -0.0005045294528827071, -0.05082392692565918, 0.004770771600306034, + -0.025419384241104126, 0.007456648629158735, 0.04484377056360245, + 0.0020997331012040377, -0.034582529217004776, 0.010550602339208126, + -0.011099648661911488, 0.01111448835581541, -0.008339575491845608, + 0.048338379710912704, -0.0051046512089669704, 0.009927359409630299, + 0.02717781625688076, -0.005557244177907705, 0.029997248202562332, + 0.0941837877035141, -0.0033536371774971485, 0.0005416272324509919, + 0.019899237900972366, -0.034078001976013184, 0.01092899963259697, + 0.006751792039722204, 0.0010387372458353639, -0.018712108954787254, + 0.09233631193637848, 0.0031458898447453976, -0.012665174901485443, + -0.0008606679039075971, -0.0024336129426956177, -0.011789667420089245, + -0.1166427731513977, -0.007790528703480959, -0.0031681484542787075, + 0.0247664637863636, -0.011307395994663239, -0.05511242896318436, + 0.012820985168218613, 0.01980278268456459, -0.12772756814956665, + 0.014490384608507156, 0.008228282444179058, -0.06526979058980942, + 0.04523700475692749, -0.007894402369856834, -0.04649832844734192, + -0.016048489138484, 0.04359728470444679, 0.027281688526272774, + 0.02774912118911743, 0.0015581058105453849, 0.008154086768627167, + 0.025048404932022095, 0.02400224842131138, 0.034582529217004776, + -0.008027954958379269, 0.008495386689901352, -0.010936419479548931, + 0.017866279929876328, 0.017799504101276398, -0.0154104083776474, + -0.09984490275382996, 0.0003042016178369522, -0.037698738276958466, + -0.005534985102713108, -0.04093366488814354, -0.014260378666222095, + 0.036036763340234756, -0.02932206727564335, -0.031473737210035324, + -0.025842297822237015, 0.033588312566280365, 0.018712108954787254, + -0.08542871475219727, -0.04044397547841072, 0.014156504534184933, + -0.00941541139036417, 0.007130189333111048, 0.007708914112299681, + 0.00834699533879757, -0.077052041888237, 0.020700549706816673, + -0.11950672417879105, 0.043901484459638596, -0.002745233941823244, + -0.05080166831612587, -0.040369778871536255, 0.003962040413171053, + 0.02371288649737835, 0.016901738941669464, 0.030368225648999214, + 0.0167978648096323, -0.04108205810189247, 0.006907602306455374, + 0.006284359842538834, -0.010943838395178318, 0.0029381425119936466, + 0.020084725692868233, 0.01667173206806183, -0.01258355937898159, + -0.0014171343063935637, 0.01907566748559475, 0.004488828592002392, + 0.052448805421590805, -0.048649996519088745, -0.014883620664477348, + 0.00984574481844902, 0.07379485666751862, 0.004614960867911577, + 0.004243983421474695, -0.0037023562472313643, -0.010498665273189545, + -0.027778800576925278, -0.01744336634874344, -0.03314313665032387, + 0.00989026203751564, 0.0028045906219631433, -0.032905708998441696, + 0.051499105989933014, 0.012858081609010696, 0.009118628688156605, + -0.030345967039465904, 0.007070832885801792, -0.02464032918214798, + 0.006173066794872284, -0.05717506259679794, 0.017821762710809708, + 0.03601450473070145, 0.0081911850720644, 0.009229921735823154, + 0.002574584446847439, 0.0038952643517404795, -0.02324545569717884, + -0.026257792487740517, 0.005045294761657715, 0.004362696316093206, + 0.014000694267451763, -0.020989911630749702, 0.012049351818859577, + 0.004703995771706104, 0.027734283357858658, -0.015172983519732952, + -0.00726374052464962, 0.0008458288502879441, -0.011277717538177967, + -0.011351913213729858, -0.010172205045819283, -0.016285916790366173, + 0.05145459249615669, -0.002567164832726121, -0.006314038299024105, + -0.031362444162368774, -0.014534901827573776, 0.007597620598971844, + -0.03720904886722565, 0.032245371490716934, 0.009927359409630299, + -0.006202745251357555, 0.02470710687339306, 0.004941421095281839, + 0.01927599497139454, 0.009422830305993557, 0.018956953659653664, + 0.013926498591899872, -0.0029900791123509407, 0.02242930419743061, + -0.027385564520955086, -0.013956177048385143, -0.047099314630031586, + -0.11699148267507553, 0.0574495866894722, -0.007597620598971844, + 0.03949427232146263, 0.02099733054637909, 0.01011284813284874, + 0.01009058952331543, 0.004674317315220833, 0.01161159761250019, + 0.006729532964527607, -0.012271937914192677, -0.004384954925626516, + 0.010342855006456375, 0.012323874980211258, 0.007545683532953262, + 0.005282720550894737, -0.037542931735515594, 0.012368392199277878, + -0.0074789077043533325, -0.026020366698503494, -0.007627299055457115, + 0.019335351884365082, 0.062405843287706375, -0.004058494698256254, + -0.009370893239974976, -0.06802986562252045 + ] + }, + "hiking": { + "prompt": "Photo of people hiking or walking together in nature", + "vector": [ + 0.002063393360003829, 0.0015020288992673159, -0.013541018590331078, + -0.007646692916750908, 0.026111029088497162, 0.0011834166944026947, + -0.02757512778043747, 0.007176360581070185, 0.019071215763688087, + -0.006736371666193008, -0.005340547300875187, -0.00493848929181695, + 0.0006903264438733459, 0.02783305011689663, 0.05169862136244774, + 0.5473757982254028, 0.010901088826358318, 0.008154954761266708, + 0.010863158851861954, -0.02394143119454384, 0.018168481066823006, + -0.0024426935706287622, -0.02306145429611206, -0.009588710032403469, + 0.01046110037714243, 0.00219235522672534, -0.029919203370809555, + -0.009262511506676674, 0.02251526340842247, 0.007934961467981339, + 0.028439931571483612, -0.0003868862404488027, 0.011454867199063301, + -0.01567268557846546, 0.031299855560064316, -0.016143018379807472, + 0.004331608768552542, -0.03375013545155525, 0.006872919853776693, + 0.0354645736515522, 0.0011682447511702776, -0.002533725695684552, + 0.0012441047001630068, -0.01750091277062893, -0.05769156292080879, + -0.022204237058758736, 0.03236947953701019, -0.047382187098264694, + 0.004976418800652027, 0.022613881155848503, 0.020360836759209633, + 0.008807350881397724, -0.0078970305621624, 0.029555076733231544, + -0.023433169350028038, -0.018456749618053436, -0.004399883095175028, + -0.04163958132266998, -0.007092914544045925, -0.0014640989247709513, + -0.04121476411819458, -0.0076922085136175156, -0.02539035677909851, + 0.026065511628985405, -0.026725493371486664, 0.015361659228801727, + -0.027309618890285492, 0.004134372808039188, 0.03704246133565903, + 0.015437520109117031, 0.004862628877162933, 0.014974773861467838, + -0.00012137607700424269, -0.03316601365804672, -0.03244534134864807, + 0.008905969560146332, -0.07830274105072021, 0.008481153286993504, + 0.06572514027357101, 0.028963366523385048, -0.021453222259879112, + -0.015354073606431484, -0.005780535284429789, -0.0006675684126093984, + 0.022181479260325432, 0.018722258508205414, -0.03245292976498604, + -0.0019192592008039355, 0.010499031282961369, 0.025853104889392853, + 0.005105380900204182, -0.01422375999391079, 0.00827633123844862, + -0.012342429719865322, -0.003967480733990669, 0.019033284857869148, + -0.08297571539878845, 0.03469838574528694, -0.013412055559456348, + 0.017940901219844818, -0.030533669516444206, -0.013503088615834713, + -0.01873743161559105, -0.06481482833623886, 0.0069411941803991795, + 0.020633932203054428, -0.05540059506893158, 0.014603059738874435, + 0.004559189081192017, -0.03791484981775284, -0.01018800400197506, + 0.0024730374570935965, -0.03339359536767006, -0.009528021328151226, + -0.023736609145998955, -0.013707909733057022, 0.009300441481173038, + -0.012926552444696426, 0.035995591431856155, -0.0003793002397287637, + 0.03805140033364296, -0.008162541314959526, 0.002298559295013547, + -0.014542371965944767, -0.012539665214717388, 0.00940664578229189, + 0.02364557795226574, -0.045121558010578156, -0.001008938648737967, + 0.00028068217216059566, 0.0036792121827602386, 0.02761305682361126, + 0.0065012057311832905, 0.020907029509544373, 0.06558859348297119, + 0.0028144079260528088, -0.01451202668249607, -0.005530197639018297, + -0.0006144663784652948, -0.0013199648819863796, 0.00856459978967905, + -0.0002958541736006737, -0.008079095743596554, 0.024707617238163948, + -0.007957719266414642, -0.003110261866822839, -0.002966127824038267, + 0.02645239792764187, -0.02149873785674572, 0.011318319477140903, + -0.04584981128573418, 0.014557542279362679, -0.005317789502441883, + 0.04957453906536102, -0.010324552655220032, 0.030859868973493576, + 0.024131080135703087, -0.002784063806757331, 0.0037702443078160286, + -0.024692445993423462, 0.0030950899235904217, 0.017834696918725967, + 0.009232167154550552, 0.012243811041116714, -0.0712856873869896, + 0.05383029207587242, 0.014056866988539696, -0.551009476184845, + 0.02320558950304985, -0.027984770014882088, -0.02834131382405758, + 0.05802535265684128, 0.0036033522337675095, -0.0293881818652153, + -0.04008444771170616, 0.0006068804068490863, -0.0058260513469576836, + -0.0210663340985775, 0.02102840505540371, 0.04547051340341568, + 0.006994296796619892, 0.03524458035826683, -0.0028523379005491734, + -0.01225898414850235, 0.042170602828264236, -0.015574067831039429, + -0.0283716581761837, -0.015346487052738667, 0.014436166733503342, + -0.012023817747831345, -0.0017220231238752604, 0.025845518335700035, + 0.03774796053767204, -0.050879333168268204, -0.026035169139504433, + -0.006121905520558357, 0.017622290179133415, -0.0962739810347557, + 0.00798806268721819, 0.011826581321656704, 0.04728356748819351, + 0.09593261033296585, 0.04386227950453758, 0.014322377741336823, + 0.03848380222916603, -0.01191002782434225, 0.017265748232603073, + -0.0007661865092813969, -0.013943076133728027, 0.01731126382946968, + 0.02589862048625946, -0.00002275801307405345, 0.0014640989247709513, + -0.009528021328151226, -0.011735549196600914, -0.011477624997496605, + 0.058154311031103134, -0.029744725674390793, -0.004635049030184746, + -0.020163599401712418, -0.02444210648536682, 0.009581124410033226, + 0.0047943550162017345, 0.05439165234565735, 0.020869098603725433, + 0.008041164837777615, 0.011098324321210384, -0.029911616817116737, + -0.023653162643313408, -0.08817972242832184, -0.010347310453653336, + -0.03296118974685669, 0.01143969502300024, -0.03907550871372223, + 0.0034440462477505207, -0.03758106753230095, -0.023251105099916458, + 0.02484416589140892, -0.011758306995034218, -0.034068744629621506, + -0.00598535779863596, 0.0004551603051368147, -0.03244534134864807, + -0.002495795488357544, 0.008503912016749382, 0.07327321916818619, + -0.026505500078201294, 0.013798942789435387, -0.08673837780952454, + -0.018540196120738983, -0.007790826726704836, -0.042034052312374115, + -0.055635757744312286, -0.029524730518460274, -0.0075860051438212395, + 0.007714967243373394, -0.0019799470901489258, -0.06055907532572746, + -0.0005386063130572438, 0.0027157897129654884, -0.04244369640946388, + 0.003686798270791769, 0.014519614167511463, 0.010984535329043865, + -0.0255344919860363, -0.021741488948464394, 0.005355719476938248, + 0.022014586254954338, -0.01018800400197506, -0.012941723689436913, + -0.0000910320522962138, 0.0032923261169344187, -0.0024654516018927097, + 0.02062634751200676, 0.03338600695133209, 0.005014349240809679, + 0.00467297900468111, 0.004202646669000387, 0.02229526825249195, + 0.00722187664359808, 0.014595472253859043, 0.009300441481173038, + 0.009376302361488342, -0.01757677271962166, -0.042360249906778336, + 0.0059019117616117, 0.009072861634194851, 0.009649398736655712, + -0.009679742157459259, 0.008132196962833405, -0.007889444939792156, + -0.005598471499979496, 0.03568456694483757, -0.0014489268651232123, + 0.007009468041360378, 0.028508204966783524, -0.03474390134215355, + -0.10007457435131073, 0.015376831404864788, -0.0013047928223386407, + -0.03398530185222626, 0.013578948564827442, 0.016431285068392754, + 0.017963659018278122, 0.027415819466114044, -0.01756918616592884, + -0.013442400842905045, 0.016363011673092842, 0.013783770613372326, + 0.04548568278551102, 0.05770673602819443, -0.000963422644417733, + -0.018100207671523094, -0.002875095698982477, 0.010051456280052662, + 0.05289721488952637, -0.03294602036476135, 0.016939548775553703, + -0.05000694468617439, -0.030321259051561356, 0.04122234880924225, + 0.008208057843148708, 0.03441770374774933, -0.008746663108468056, + 0.003239224199205637, -0.009725257754325867, -0.043528493493795395, + 0.0073811826296150684, 0.002905439818277955, 0.021946312859654427, + 0.020679449662566185, 0.0052874451503157616, -0.03149709105491638, + 0.05969427153468132, 0.0024882094003260136, -0.01367756724357605, + 0.024798648431897163, -0.026027580723166466, -0.018380889669060707, + -0.06441276520490646, 0.007593590300530195, -0.002723375800997019, + 0.026975832879543304, -0.010309380479156971, 0.004232990555465221, + -0.0185477826744318, 0.010787298902869225, 0.006175007671117783, + 0.00040964424260891974, 0.01695472002029419, -0.00923975370824337, + 0.002556483494117856, 0.02156701311469078, -0.07915237545967102, + 0.01619611866772175, 0.004278506617993116, -0.019078800454735756, + 0.02157459780573845, -0.0014792709844186902, -0.0033302560914307833, + 0.020641518756747246, -0.019526375457644463, -0.0314212329685688, + -0.013078272342681885, -0.005461923312395811, -0.00219235522672534, + -0.027734432369470596, -0.02193872444331646, 0.01945810206234455, + 0.020239459350705147, -0.008344604633748531, 0.019518790766596794, + -0.04682840779423714, 0.006834989879280329, 0.007237048353999853, + -0.01417065691202879, 0.028690271079540253, -0.014921671710908413, + -0.061385948210954666, -0.015703029930591583, 0.003117847954854369, + -0.007358424365520477, -0.02138494700193405, -0.007343252655118704, + 0.05872325971722603, -0.01699265092611313, 0.020307734608650208, + -0.016605764627456665, 0.02196907065808773, 0.006599824409931898, + 0.02062634751200676, 0.0405016764998436, -0.0021771832834929228, + 0.04533396288752556, 0.015134080313146114, -0.020535314455628395, + -0.006250868085771799, -0.015217525884509087, 0.02823510952293873, + -0.1144576445221901, 0.023304205387830734, 0.03866586834192276, + 0.0056212292984128, 0.012175537645816803, -0.02510209009051323, + -0.012592768296599388, -0.002336489502340555, 0.06109009310603142, + -0.004953661002218723, 0.009482505731284618, -0.03818794712424278, + -0.008177713491022587, 0.00503710750490427, -0.0011758307227864861, + 0.014542371965944767, 0.0022378715220838785, -0.0035274922847747803, + 0.0026854455936700106, 0.023380065336823463, 0.02768133208155632, + 0.11998025327920914, -0.07625452429056168, -0.06897195428609848, + 0.02488209493458271, 0.015012702904641628, -0.020057396963238716, + -0.010756954550743103, 0.01931396685540676, 0.03437977284193039, + -0.002108909422531724, -0.004710908979177475, 0.012069333344697952, + 0.008147369138896465, -0.027150310575962067, -0.06366175413131714, + 0.01267621386796236, 0.023304205387830734, 0.03529009595513344, + -0.016309909522533417, -0.0025792415253818035, -0.004415054805576801, + 0.00925492588430643, 0.007745311129838228, -0.026497915387153625, + 0.0015247869305312634, 0.020679449662566185, -0.019943606108427048, + 0.022644223645329475, 0.0021013233345001936, -0.0057729496620595455, + -0.057418469339609146, 0.016567833721637726, -0.040615469217300415, + 0.021111849695444107, -0.016051985323429108, -0.017409881576895714, + -0.027529612183570862, 0.00851149670779705, -0.016271980479359627, + -0.01257000956684351, 0.01160658709704876, -0.03044263832271099, + -0.011447281576693058, 0.03321152925491333, 0.010286622680723667, + 0.05867016315460205, -0.0005006763385608792, 0.005651573650538921, + 0.02270491234958172, 0.019670508801937103, 0.009103205054998398, + 0.0033909440971910954, 0.007828757166862488, 0.012486563995480537, + -0.06490585952997208, 0.014314791187644005, -0.02128632925450802, + 0.01449685450643301, 0.02833372727036476, -0.006053632125258446, + -0.001084798714146018, 0.03882517293095589, 0.021870451048016548, + -0.016522318124771118, 0.0028826817870140076, -0.049089036881923676, + -0.06481482833623886, 0.0018206412205472589, 0.023296620696783066, + 0.019404999911785126, 0.02746892161667347, 0.031079862266778946, + 0.01899535581469536, -0.040478918701410294, 0.04027410224080086, + 0.01311620231717825, -0.001866157166659832, -0.055635757744312286, + -0.016234049573540688, 0.0015323730185627937, -0.021407704800367355, + -0.009050103835761547, -0.007494972553104162, 0.011614173650741577, + -0.00951285008341074, -0.018889151513576508, -0.01571820117533207, + 0.012501736171543598, 0.06539894640445709, -0.006281211972236633, + 0.011727963574230671, -0.04220853000879288 + ] + }, + "feast": { + "prompt": "Photo of people having a big feast together", + "vector": [ + -0.029508370906114578, 0.006410595495253801, -0.022999616339802742, + -0.04990297555923462, 0.017683882266283035, -0.010110467672348022, + -0.023958561941981316, 0.004115164279937744, -0.03147157281637192, + -0.0791093111038208, -0.01019352674484253, 0.03028610162436962, + -0.005285532213747501, 0.016543716192245483, 0.01726858876645565, + 0.5275111794471741, -0.016181280836462975, -0.005934897810220718, + -0.017427153885364532, -0.01871078461408615, -0.007905646227300167, + -0.03548102453351021, 0.024102026596665382, 0.005323286168277264, + -0.00561021501198411, -0.002605012385174632, -0.01120532862842083, + -0.006410595495253801, 0.030542826279997826, 0.03679485619068146, + -0.009483755566179752, -0.035435717552900314, 0.0168155450373888, + 0.012134072370827198, -0.004001902882009745, 0.04106858745217323, + -0.013289338909089565, -0.009597016498446465, -0.026314400136470795, + 0.014791940338909626, 0.07266851514577866, -0.017094921320676804, + -0.00034733497886918485, 0.020719286054372787, -0.03684015944600105, + -0.051322516053915024, -0.026714589446783066, -0.0372932069003582, + -0.004938197322189808, 0.017162878066301346, 0.013908501714468002, + 0.011575316078960896, 0.03738381713628769, -0.004032106138765812, + -0.01874098740518093, -0.011099617928266525, -0.01941300556063652, + 0.003224174724891782, -0.03239276260137558, 0.008056661114096642, + -0.029923664405941963, 0.008396445773541927, -0.012330392375588417, + -0.0709695965051651, -0.04053248465061188, 0.006886293180286884, + -0.005647968500852585, -0.04843812808394432, 0.004855139181017876, + 0.009838640689849854, 0.00040774105582386255, -0.025453614071011543, + -0.017955707386136055, 0.02265983261168003, 0.029281847178936005, + -0.01099390722811222, -0.06056464836001396, 0.009732929989695549, + 0.09662707895040512, 0.01938280090689659, 0.00030203041387721896, + -0.02985570766031742, -0.011424301192164421, -0.007709326688200235, + 0.019480960443615913, 0.047864269465208054, 0.0035111038014292717, + -0.006252029910683632, 0.0017517763189971447, -0.007497905287891626, + -0.016649426892399788, 0.015033564530313015, -0.02813413366675377, + 0.0017895301571115851, -0.012141622602939606, 0.022909006103873253, + -0.13033367693424225, 0.0323852114379406, -0.02679009921848774, + 0.015373348258435726, -0.0010118018835783005, 0.024849552661180496, + -0.010050062090158463, 0.05398038774728775, 0.021149680018424988, + 0.008532359264791012, -0.04978971555829048, 0.012164275161921978, + 0.03536776453256607, -0.009438450448215008, 0.006908946204930544, + -0.028670238330960274, 0.04793977737426758, -0.03107893094420433, + -0.00043039332376793027, -0.09445246309041977, 0.041287556290626526, + -0.03239276260137558, 0.013568716123700142, 0.031305454671382904, + -0.010352092795073986, -0.00006795684748794883, -0.019397905096411705, + -0.045115794986486435, 0.005270430818200111, -0.006206724792718887, + 0.04941217601299286, -0.031645238399505615, 0.0459916815161705, + 0.006365290842950344, -0.017872650176286697, 0.029508370906114578, + -0.00622937735170126, 0.005776331759989262, -0.014505011029541492, + -0.014248285442590714, -0.009763133712112904, -0.009702727198600769, + 0.01652861386537552, 0.03707423433661461, -0.00672017689794302, + 0.025831151753664017, -0.009242130443453789, -0.044255007058382034, + 0.006395494099706411, -0.018363449722528458, 0.00662201689556241, + 0.030399363487958908, -0.015713132917881012, 0.018167128786444664, + -0.015833944082260132, 0.0032543777488172054, -0.06430982798337936, + 0.02250126749277115, 0.014172777533531189, 0.03633425757288933, + 0.02984815463423729, -0.01769143156707287, 0.0005814085598103702, + -0.01925443857908249, -0.037919919937849045, -0.012254884466528893, + -0.008396445773541927, 0.009959452785551548, 0.057582102715969086, + -0.04155183583498001, 0.013508310541510582, -0.5311808586120605, + -0.0006191623397171497, 0.002084009815007448, -0.008177473209798336, + 0.04822670668363571, -0.02510627917945385, 0.027484769001603127, + 0.003480900777503848, -0.0014119921252131462, 0.040977977216243744, + -0.010004756972193718, -0.01550926174968481, -0.002846636576578021, + 0.023565921932458878, 0.016400251537561417, 0.014384198933839798, + 0.009068462997674942, 0.04809079319238663, -0.013712181709706783, + 0.0087739834561944, -0.02348286472260952, -0.010978804901242256, + -0.02327144332230091, 0.024630580097436905, 0.004923095460981131, + -0.0450025349855423, -0.018431406468153, -0.02188965491950512, + -0.029259197413921356, -0.020983561873435974, 0.023807547986507416, + -0.02648051828145981, 0.05487137287855148, 0.0007550760637968779, + 0.003443146590143442, -0.00040019029984250665, -0.02216148190200329, + -0.008992955088615417, -0.01047290489077568, -0.0007324237376451492, + -0.027545172721147537, 0.024086926132440567, -0.008917448110878468, + 0.01448235847055912, 0.0016838194569572806, -0.004462499171495438, + -0.013025062158703804, 0.022674933075904846, 0.023867953568696976, + -0.09708012640476227, -0.013727283105254173, -0.031886860728263855, + 0.008034009486436844, -0.0067352778278291225, 0.05508279800415039, + 0.010352092795073986, 0.02363388054072857, 0.004024555440992117, + -0.010676775127649307, 0.0008456851937808096, -0.016800440847873688, + 0.00014346445095725358, -0.0004152918409090489, 0.01708737015724182, + 0.03860703855752945, -0.011877345852553844, -0.02216903120279312, + -0.02170843631029129, 0.056842122226953506, 0.003428045427426696, + -0.005051458720117807, -0.0659709945321083, -0.0037753803189843893, + -0.004175570793449879, 0.011620620265603065, -0.0223502516746521, + 0.011507358402013779, -0.07293279469013214, 0.0331176333129406, + 0.006810786202549934, -0.00946110300719738, -0.03183400630950928, + 0.0026276647113263607, -0.021610276773571968, -0.04296382516622543, + -0.059968139976263046, 0.02210862748324871, -0.022448411211371422, + 0.00127607851754874, -0.030225694179534912, -0.02345266193151474, + 0.008215227164328098, 0.03343476727604866, -0.016249235719442368, + -0.030784450471401215, 0.017955707386136055, 0.014965606853365898, + 0.009159073233604431, -0.015214783139526844, 0.044353168457746506, + -0.012813640758395195, -0.011673475615680218, 0.004084961488842964, + 0.010276584886014462, 0.03187175840139389, 0.006093463860452175, + 0.0021972714457660913, 0.004681471269577742, -0.02412468008697033, + -0.005330836866050959, 0.008260532282292843, 0.013968906365334988, + 0.030852409079670906, 0.012488958425819874, 0.03156217932701111, + 0.028957165777683258, -0.0005889593157917261, 0.01711002178490162, + 0.026835402473807335, 0.019216684624552727, 0.004704123828560114, + -0.029644286260008812, -0.006319986190646887, -0.041868966072797775, + -0.017321443185210228, -0.015086418949067593, -0.04332626610994339, + 0.011439401656389236, 0.0019707484170794487, -0.012889147736132145, + -0.047192253172397614, 0.017525315284729004, 0.016543716192245483, + 0.030225694179534912, -0.014301139861345291, 0.02335450053215027, + -0.005693273153156042, 0.039082735776901245, -0.011937752366065979, + 0.02835310623049736, 0.019110973924398422, -0.002778679830953479, + -0.010654122568666935, -0.05033336952328682, -0.018997713923454285, + -0.03303457424044609, 0.0017593271331861615, 0.015071317553520203, + 0.05412385240197182, -0.0390600822865963, 0.01020862814038992, + -0.04464764520525932, 0.005149618722498417, 0.021353550255298615, + 0.014633373357355595, -0.008940099738538265, 0.050348468124866486, + 0.003941496834158897, 0.003722524968907237, -0.026095427572727203, + 0.006168971303850412, 0.018514463678002357, 0.009989656507968903, + 0.003216624027118087, -0.0018952408572658896, 0.010231280699372292, + 0.09087339788675308, -0.020960910245776176, -0.004696573130786419, + 0.01754041574895382, -0.02033419720828533, -0.027296001091599464, + -0.018869351595640182, -0.036387115716934204, 0.028859006240963936, + 0.058525945991277695, -0.015373348258435726, 0.00786789320409298, + -0.03333660960197449, 0.024056723341345787, -0.004628615919500589, + -0.06604650616645813, 0.022116176784038544, -0.010404948145151138, + 0.0031713193748146296, 0.019692381843924522, -0.08060436695814133, + 0.015637625008821487, 0.017261039465665817, -0.11375976353883743, + 0.008041559718549252, 0.00019631977193057537, -0.05997568741440773, + 0.041045933961868286, -0.005315735470503569, -0.002378489589318633, + 0.01956401951611042, 0.05740842968225479, 0.005942448507994413, + 0.022486163303256035, 0.002099111443385482, 0.02795291505753994, + -0.03366883844137192, -0.014572967775166035, -0.0296065304428339, + 0.018793843686580658, -0.016490861773490906, -0.0028390861116349697, + 0.021549871191382408, 0.04615779593586922, -0.019088322296738625, + -0.12028361111879349, -0.010571064427495003, -0.032709892839193344, + -0.026865605264902115, -0.02486465498805046, 0.006221826653927565, + 0.013802791014313698, -0.0009060912416316569, 0.01171122957020998, + 0.0067352778278291225, 0.05201718956232071, 0.01818978041410446, + -0.09494326263666153, -0.004756979178637266, -0.014452156610786915, + 0.014799490571022034, 0.005647968500852585, 0.058926135301589966, + 0.006916496902704239, 0.014051965437829494, -0.012874046340584755, + -0.06671097129583359, 0.023679183796048164, 0.028405960649251938, + -0.01400666031986475, -0.03927905485033989, -0.015849046409130096, + 0.005126966163516045, 0.04285811632871628, 0.04158203676342964, + 0.014505011029541492, -0.053074296563863754, -0.00996700394898653, + 0.021210087463259697, 0.008940099738538265, 0.02851167321205139, + 0.01449745986610651, 0.008185024373233318, 0.00274092610925436, + 0.022425759583711624, 0.009408247657120228, -0.007052409928292036, + 0.03719504550099373, -0.02506852336227894, 0.016490861773490906, + -0.011046762578189373, 0.052311670035123825, 0.018756089732050896, + -0.009415797889232635, 0.0008683374035172164, 0.022403106093406677, + -0.03511858731508255, 0.0030807103030383587, -0.01668718084692955, + -0.00672017689794302, -0.015562117099761963, -0.005376141518354416, + 0.021595174446702003, 0.027409261092543602, -0.00037753803189843893, + -0.004605963826179504, 0.016294540837407112, -0.030135085806250572, + 0.0011477156076580286, -0.03647017478942871, -0.044413574039936066, + 0.010835341177880764, 0.02777169644832611, -0.00030203041387721896, + 0.04723755642771721, -0.004636167082935572, -0.0037602786906063557, + -0.06221826747059822, 0.003178870305418968, -0.011628171429038048, + 0.004175570793449879, -0.02207842469215393, -0.02427569404244423, + 0.007361991330981255, 0.01446725707501173, 0.02302226796746254, + -0.004930646624416113, -0.0018423855071887374, -0.008653171360492706, + 0.0005436547799035907, 0.0017593271331861615, 0.012300188653171062, + 0.026533372700214386, -0.03156972676515579, 0.021655581891536713, + -0.05373876169323921, -0.005013705231249332, -0.009204376488924026, + 0.00019631977193057537, 0.04528946056962013, 0.02777169644832611, + 0.022116176784038544, -0.024577725678682327, -0.02348286472260952, + -0.004855139181017876, 0.007445049937814474, 0.010246382094919682, + -0.0012911800295114517, -0.0016309642232954502, 0.0021897205151617527, + -0.044776007533073425, -0.06371331959962845, -0.00727138202637434, + -0.09274598956108093, 0.03334415704011917, 0.030724044889211655, + -0.006418146193027496, 0.02210862748324871, -0.019345048815011978, + -0.019178932532668114, -0.041159193962812424, -0.009159073233604431, + 0.008290735073387623, -0.014746634289622307, 0.000823032867629081, + 0.023520618677139282, -0.008502155542373657, 0.01005761232227087, + -0.0026654184330254793, 0.0026427661068737507, 0.006297334562987089, + 0.02388305403292179, -0.011371445842087269, -0.014286038465797901, + 0.016279440373182297, 0.045025184750556946, 0.00844930112361908, + 0.024653233587741852, -0.13621573150157928 + ] + }, + "selfies": { + "prompt": "Happy and nostalgic selfie with people, clearly taken from the front camera of a phone", + "vector": [ + 0.015917416661977768, 0.050739727914333344, -0.02502211555838585, + -0.087443046271801, 0.03248291090130806, 0.0026871508453041315, + -0.00765837961807847, 0.007144659757614136, 0.03526490181684494, + 0.001857295399531722, -0.003943788819015026, -0.007460794877260923, + -0.03395294025540352, -0.0075793457217514515, -0.004188793711364269, + 0.4699036478996277, 0.030467547476291656, -0.014897879213094711, + 0.0670839250087738, -0.05165652185678482, -0.015071753412485123, + -0.02868928574025631, 0.025488415732979774, -0.010851346887648106, + 0.017822131514549255, 0.01167329866439104, -0.01135716401040554, + -0.009808100759983063, 0.06773991137742996, 0.01942651905119419, + 0.02820718102157116, -0.041832614690065384, -0.005722050555050373, + 0.01917361095547676, 0.0193316787481308, -0.03930353373289108, 0.0, + 0.0002133913803845644, 0.0028136048931628466, -0.049388252198696136, + -0.035889267921447754, 0.004259924404323101, -0.04313667118549347, + 0.04614786058664322, 0.04571317881345749, -0.05501545965671539, + -0.0005216234130784869, -0.025361960753798485, 0.03127369284629822, + -0.012866709381341934, -0.056959692388772964, 0.07071158289909363, + 0.015150788240134716, -0.027337808161973953, -0.055307887494564056, + -0.046590451151132584, 0.00572995375841856, 0.042409561574459076, + -0.03867916390299797, 0.011823463253676891, -0.01588580198585987, + 0.006188350263983011, -0.03316260129213333, -0.004797354806214571, + -0.08805950731039047, 0.010337627492845058, 0.017490191385149956, + 0.008749046362936497, 0.025891486555337906, 0.049546319991350174, + 0.014629164710640907, 0.054177701473236084, -0.0517592653632164, + 0.015150788240134716, -0.008662109263241291, -0.001414705766364932, + -0.069162517786026, -0.012455734424293041, 0.06672036647796631, + 0.008843887597322464, 0.048005156219005585, -0.04151647910475731, + 0.019766364246606827, 0.020738480612635612, 0.014992720447480679, + 0.04920647293329239, -0.0025211796164512634, 0.019797977060079575, + 0.035960398614406586, 0.0034300689585506916, 0.0030586100183427334, + 0.027100706472992897, 0.024808725342154503, 0.012179115787148476, + -0.013111715205013752, 0.013894150033593178, -0.003643460338935256, + 0.033249542117118835, -0.04278102144598961, -0.020232664421200752, + -0.007176273502409458, 0.01348317414522171, -0.043500229716300964, + -0.01609129086136818, -0.0025369864888489246, 0.0075872489251196384, + -0.042788922786712646, 0.04044952243566513, -0.01958458684384823, + 0.003667170647531748, -0.01606758125126362, -0.023465149104595184, + 0.015032238326966763, -0.059915561228990555, 0.008970341645181179, + 0.03035689890384674, 0.010685376822948456, -0.05084247514605522, + -0.016533881425857544, 0.005737856961786747, 0.03273581713438034, + -0.010369240306317806, 0.014692391268908978, -0.0033431316260248423, + 0.020959775894880295, 0.0011143771698698401, 0.050478916615247726, + -0.04680384323000908, -0.01737954281270504, -0.012890420854091644, + -0.021394461393356323, 0.014945301227271557, -0.023710153996944427, + -0.009657936170697212, 0.008053549565374851, 0.027456358075141907, + 0.0069075580686330795, 0.02016153372824192, 0.0330914705991745, + -0.07644153386354446, -0.02246141992509365, -0.00044258954585529864, + -0.0005216234130784869, -0.026428917422890663, -0.0035091026220470667, + -0.0034853925462812185, -0.0336921289563179, -0.022382386028766632, + 0.031281597912311554, -0.08310408890247345, 0.009657936170697212, + 0.02995382808148861, -0.017901165410876274, 0.0012171212583780289, + 0.022777553647756577, -0.0060223788022994995, -0.0018414886435493827, + -0.01603596843779087, -0.0593465156853199, -0.04250440001487732, + 0.027709266170859337, 0.03439553081989288, -0.03536764532327652, + 0.011199096217751503, 0.03919288516044617, -0.008986148051917553, + 0.017110828310251236, -0.4741714596748352, 0.05406705290079117, + 0.017442768439650536, -0.004923808388411999, 0.05942555144429207, + 0.027914753183722496, -0.009246960282325745, 0.0069154612720012665, + -0.04851888120174408, 0.02397886849939823, 0.0008693723357282579, + 0.022121572867035866, 0.13283218443393707, -0.015245628543198109, + -0.006488678976893425, 0.01965571753680706, -0.012218632735311985, + 0.04632173851132393, 0.057386476546525955, 0.002450049389153719, + -0.02909236028790474, 0.004354765173047781, -0.059259574860334396, + 0.026958445087075233, -0.019473940134048462, 0.0340556837618351, + -0.03817334771156311, 0.004117663484066725, -0.018738925457000732, + 0.0018731020390987396, -0.018154073506593704, -0.03981725126504898, + 0.0010432468261569738, 0.026326173916459084, 0.04975971207022667, + -0.059109412133693695, -0.029021229594945908, -0.02719554677605629, + -0.012756062671542168, -0.02954285219311714, 0.03445085510611534, + 0.050146978348493576, 0.05016278102993965, -0.008511945605278015, + -0.006512389052659273, 0.0041097598150372505, -0.018936509266495705, + -0.024397749453783035, 0.004615576937794685, 0.032064031809568405, + 0.039959512650966644, 0.02782781794667244, 0.0013198652304708958, + 0.015972739085555077, 0.015332565642893314, 0.016280973330140114, + 0.011570555157959461, 0.022659003734588623, 0.010179559700191021, + -0.011910400353372097, 0.015672411769628525, 0.01436835341155529, + -0.017316315323114395, -0.02405790239572525, -0.006046089343726635, + 0.0014779329067096114, -0.07849641144275665, 0.0059828623197972775, + 0.03033319115638733, 0.016968566924333572, -0.03571539372205734, + -0.001509546535089612, -0.020951872691512108, -0.012171212583780289, + 0.004180890507996082, -0.01209217868745327, 0.02031169831752777, + -0.041706159710884094, 0.049570027738809586, 0.015538053587079048, + -0.05229669809341431, -0.019845400005578995, -0.04116082563996315, + 0.011025221087038517, -0.08250343799591064, 0.008551462553441525, + 0.003888465464115143, -0.02114945650100708, -0.025899391621351242, + -0.03126579150557518, -0.061259131878614426, -0.04042581096291542, + 0.015893707051873207, -0.07852012664079666, -0.01596483774483204, + 0.013467367738485336, 0.009444545023143291, 0.040868401527404785, + -0.028807837516069412, -0.027914753183722496, -0.035280708223581314, + 0.016842111945152283, 0.01143619790673256, -0.0288157407194376, + 0.022295447066426277, 0.016178227961063385, -0.031811121851205826, + -0.02838895656168461, 0.025899391621351242, 0.0017466479912400246, + 0.008662109263241291, -0.00922324974089861, 0.001983749447390437, + 0.027764590457081795, -0.026247140020132065, 0.000545333547051996, + -0.061132680624723434, -0.014905783347785473, 0.03196919336915016, + -0.0200824998319149, 0.00834597460925579, -0.02823088876903057, + 0.004931711591780186, -0.03958805650472641, -0.03649783134460449, + -0.021916085854172707, 0.03401616960763931, 0.002126010600477457, + 0.014921589754521847, -0.033557772636413574, 0.08977454900741577, + 0.03425326943397522, 0.003366841934621334, 0.005113489460200071, + -0.015617088414728642, -0.025306636467576027, 0.03321792557835579, + -0.007460794877260923, 0.0021655273158103228, 0.006828524172306061, + 0.020201051607728004, -0.016612913459539413, -0.04017290472984314, + -0.04703304171562195, -0.018146172165870667, -0.008290650323033333, + 0.0038252382073551416, 0.018960220739245415, 0.04915114864706993, + -0.049799226224422455, 0.07689203321933746, -0.027377326041460037, + 0.015553861856460571, 0.022303352132439613, -0.06472081691026688, + -0.022706422954797745, 0.036545250564813614, 0.09203491359949112, + -0.020849129185080528, 0.007049818988889456, 0.06066638231277466, + -0.02196350507438183, 0.008496138267219067, 0.03311518207192421, + -0.00007903384539531544, 0.021694790571928024, 0.04487541690468788, + 0.007895481772720814, -0.03597620874643326, -0.0013751889346167445, + 0.015648702159523964, -0.0345061756670475, 0.01453432347625494, + -0.01897602714598179, -0.031155141070485115, 0.026492144912481308, + -0.0359208844602108, 0.008164196275174618, -0.042354241013526917, + -0.021268006414175034, -0.014613358303904533, -0.13114877045154572, + 0.018509726971387863, 0.004489122424274683, -0.013570111244916916, + 0.01238460373133421, -0.030949654057621956, 0.025646483525633812, + 0.015846285969018936, -0.09424786269664764, 0.024674367159605026, + -0.044029753655195236, 0.01856505125761032, -0.01834375597536564, + 0.02671344019472599, -0.005263654049485922, 0.0069075580686330795, + 0.020398635417222977, 0.013720275834202766, 0.0563037134706974, + 0.044614605605602264, -0.000987923122011125, -0.008369684219360352, + 0.036972030997276306, -0.03273581713438034, 0.01824101060628891, + -0.03617379069328308, 0.014423677697777748, -0.02711651287972927, + 0.07519280165433884, -0.03147917985916138, 0.05080295726656914, + 0.0008061452535912395, -0.021220587193965912, -0.0074449884705245495, + 0.007531925570219755, -0.0281518567353487, 0.03056238777935505, + -0.03332066908478737, -0.017387446016073227, -0.004196696914732456, + 0.06400161236524582, -0.10398483276367188, -0.061053644865751266, + -0.07527974247932434, -0.008646302856504917, 0.022255931049585342, + 0.01205266173928976, 0.026223430410027504, -0.03162934258580208, + -0.031613539904356, 0.0017071310430765152, -0.08489025384187698, + 0.0036592669785022736, -0.07139917463064194, 0.0009009858476929367, + -0.0031139336060732603, 0.043626684695482254, 0.003651363542303443, + 0.019900722429156303, 0.03483021631836891, -0.007500311825424433, + 0.02757490798830986, -0.04568946361541748, 0.028302019461989403, + -0.015119175426661968, -0.008203713223338127, -0.012835096567869186, + 0.042615048587322235, -0.039279818534851074, 0.02332288771867752, + -0.0243898443877697, -0.03396874666213989, 0.052881546318531036, + -0.07279808074235916, -0.03365261107683182, 0.02646053023636341, + 0.039121754467487335, 0.015893707051873207, 0.007421278394758701, + 0.025788743048906326, 0.020675254985690117, 0.010637955740094185, + 0.011657492257654667, -0.03491715341806412, -0.03268839791417122, + 0.0022682715207338333, 0.019813785329461098, 0.01329349260777235, + -0.004133470356464386, 0.0031139336060732603, 0.012961551547050476, + -0.023204337805509567, -0.032166775315999985, -0.06231028214097023, + -0.08763272315263748, -0.0237259604036808, -0.0020074595231562853, + -0.034245364367961884, -0.04824225977063179, -0.017703581601381302, + -0.015640798956155777, -0.023836607113480568, -0.019015545025467873, + -0.014605454169213772, 0.06023959815502167, -0.028136050328612328, + -0.0054454319179058075, 0.009989878162741661, 0.0352570004761219, + -0.0196320079267025, 0.023267565295100212, 0.017624547705054283, + -0.00889130774885416, -0.020422345027327538, 0.010313916951417923, + 0.011823463253676891, -0.06701279431581497, 0.0280333049595356, + -0.03893997520208359, -0.01917361095547676, -0.027179740369319916, + -0.043421193957328796, -0.02129962295293808, -0.027582813054323196, + -0.005413818638771772, -0.0025923100765794516, 0.006164640188217163, + -0.02005879022181034, 0.03063351847231388, 0.05219395086169243, + -0.021995117887854576, 0.04875598102807999, -0.02876041643321514, + -0.005168813746422529, 0.04773644357919693, 0.015134981833398342, + 0.000940502795856446, 0.02046186290681362, -0.08118356764316559, + 0.013554304838180542, -0.005737856961786747, 0.02631036750972271, + 0.04408508166670799, 0.027203449979424477, -0.02306997962296009, + 0.003619750263169408, -0.03498038277029991, 0.014012700878083706, + -0.0035011994186788797, -0.008021934889256954, + -0.000023710153982392512, 0.0044337990693748, 0.012487347237765789, + -0.01917361095547676, 0.015142884105443954, -0.02519599162042141, + 0.005413818638771772, -0.0033826485741883516, 0.0006796910893172026, + 0.047341275960206985, 0.039556439965963364, -0.018130365759134293, + -0.027353614568710327, -0.05560821294784546 + ] + }, + "posing": { + "prompt": "Photo of people posing together in a funny manner for the camera", + "vector": [ + 0.010862172581255436, -0.002259705914184451, -0.019589310511946678, + -0.02353210747241974, 0.011734886094927788, -0.021841226145625114, + 0.0055947196669876575, 0.027217766270041466, 0.024350278079509735, + -0.04710317775607109, 0.01493743434548378, -0.04109547659754753, + 0.028659304603934288, 0.005968740209937096, -0.009934913367033005, + 0.568651020526886, 0.019573727622628212, -0.05230829492211342, + 0.009732319042086601, -0.033996883779764175, -0.004534995649009943, + -0.017352983355522156, -0.023680157959461212, -0.019028281792998314, + 0.007347939535975456, 0.011236192658543587, -0.006218086928129196, + -0.007293395232409239, 0.0030700829811394215, -0.0027506074402481318, + 0.003638905705884099, -0.00935829896479845, -0.007698584347963333, + 0.03275015205144882, 0.013737453147768974, -0.009693358093500137, + -0.007371315732598305, 0.004075262695550919, 0.0011064766440540552, + 0.02505156584084034, 0.02505156584084034, 0.003989549353718758, + -0.0027817757800221443, -0.013114085420966148, 0.01835816167294979, + -0.022285373881459236, -0.02734243869781494, -0.021069807931780815, + 0.038516294211149216, -0.008438833057880402, -0.03540725260972977, + 0.041056517511606216, 0.008960902690887451, -0.00017921804101206362, + -0.014890681952238083, -0.04561488702893257, 0.01084658782929182, + -0.00024155476421583444, -0.0013480314519256353, + -0.0008415456395596266, -0.010643993504345417, 0.0029609936755150557, + -0.037277352064847946, -0.016129624098539352, 0.0026804786175489426, + 0.002703854814171791, -0.009732319042086601, -0.0028441124595701694, + -0.00356877688318491, 0.07399367541074753, 0.011415409855544567, + 0.015599762089550495, -0.0187165979295969, 0.012638768181204796, + -0.009264794178307056, 0.01648026891052723, -0.05869780480861664, + -0.005649264436215162, 0.03279690444469452, 0.028955401852726936, + -0.011197231709957123, -0.027981389313936234, 0.004449282772839069, + -0.010519320145249367, -0.01870880462229252, -0.007340147625654936, + -0.0010207636514678597, -0.0014804968377575278, -0.008002475835382938, + 0.005890819244086742, 0.005602512042969465, -0.00751157384365797, + 0.02130357176065445, -0.015358206816017628, 0.02491910010576248, + 0.022565890103578568, -0.020968511700630188, -0.010106339119374752, + 0.0006467433995567262, 0.006779117044061422, -0.01658935658633709, + 0.0009506348869763315, -0.019472431391477585, 0.01172709371894598, + -0.0008415456395596266, 0.03326442837715149, -0.038874730467796326, + -0.008968694135546684, -0.00034285191213712096, + -0.0005688224919140339, -0.020898383110761642, -0.02009579725563526, + 0.08457533270120621, -0.007698584347963333, 0.00875051598995924, + -0.0446486696600914, 0.011633588932454586, -0.01245955005288124, + -0.00289086508564651, 0.004145391285419464, 0.05244075879454613, + -0.011571251787245274, 0.015389375388622284, -0.00446486659348011, + -0.01843608170747757, -0.017368566244840622, 0.02174772135913372, + -0.013371225446462631, 0.0169244185090065, -0.012412797659635544, + -0.01758674345910549, 0.005228491500020027, -0.03711371868848801, + 0.015623139217495918, 0.05841728672385216, 0.001667506992816925, + -0.026095706969499588, 0.0027272312436252832, 0.002477884292602539, + -0.022908741608262062, -0.023773664608597755, 0.022191869094967842, + 0.0031480039469897747, -0.013238758780062199, 0.019519183784723282, + 0.02527753636240959, 0.005290828179568052, -0.014056929387152195, + 0.0027272312436252832, -0.0545835867524147, -0.009023238904774189, + 0.028893064707517624, -0.04298895597457886, 0.0009194665472023189, + -0.027482695877552032, -0.008134940639138222, -0.0074726128950715065, + -0.011220607906579971, -0.037963055074214935, -0.029111245647072792, + -0.020578907802700996, 0.019550351426005363, -0.02579960599541664, + 0.006779117044061422, -0.02009579725563526, 0.037822797894477844, + -0.018038686364889145, -0.5727107524871826, 0.025674933567643166, + 0.01623092219233513, -0.028682678937911987, 0.05233946070075035, + 0.015288078226149082, 0.0012545263161882758, 0.0124439662322402, + -0.015132236294448376, 0.012771233916282654, 0.0011454371269792318, + -0.004254480358213186, 0.03370078653097153, -0.003444103291258216, + 0.038072146475315094, 0.020789293572306633, -0.02684374526143074, + 0.021202275529503822, 0.008197277784347534, -0.022978870198130608, + -0.024404821917414665, 0.027825549244880676, 0.0008415456395596266, + 0.02039968967437744, -0.015420544892549515, 0.054692670702934265, + -0.044461656361818314, -0.005851858761161566, 0.007231058552861214, + -0.021326947957277298, 0.00887518934905529, 0.0011376449838280678, + 0.0008882981492206454, 0.05418618768453598, 0.03594490885734558, + -0.05126415193080902, -0.014914058148860931, 0.006646651774644852, + -0.009334922768175602, -0.025900904089212418, -0.006108997855335474, + 0.025791816413402557, 0.03403584286570549, -0.0022674978245049715, + -0.0010129715083166957, 0.01477380096912384, -0.026391804218292236, + -0.022830819711089134, 0.04309804365038872, -0.012825778685510159, + 0.020984094589948654, 0.014984187670052052, 0.012217995710670948, + 0.002127240179106593, 0.03755787014961243, -0.006911583244800568, + 0.021342530846595764, 0.0182179044932127, 0.00843103975057602, + -0.0045583718456327915, 0.0017532199854031205, -0.009038823656737804, + -0.03983315825462341, -0.02408534660935402, -0.03221249580383301, + 0.006171334534883499, -0.016293257474899292, -0.0228230282664299, + 0.01149333082139492, -0.007223266176879406, 0.007893386296927929, + -0.015116652473807335, -0.0013012788258492947, -0.002251913771033287, + 0.0013090709690004587, -0.06694962829351425, 0.014999771490693092, + -0.05226153880357742, 0.015124444849789143, -0.0295398086309433, + -0.009015446528792381, -0.01918412186205387, -0.02356327697634697, + 0.017758170142769814, -0.048490170389413834, 0.0064206812530756, + 0.01907503418624401, -0.018062062561511993, -0.002470092149451375, + -0.03808772936463356, 0.011158271692693233, -0.04685383290052414, + -0.006560939364135265, -0.014025759883224964, 0.0011376449838280678, + -0.005859650671482086, 0.01603611931204796, 0.027334649115800858, + 0.03127744421362877, -0.0018934777472168207, 0.015747811645269394, + 0.01681532710790634, 0.0050960262306034565, -0.01601274311542511, + -0.005072650033980608, -0.014329652301967144, -0.029080074280500412, + 0.006864830385893583, -0.017204932868480682, 0.03066966123878956, + 0.00846220925450325, -0.01464912761002779, -0.04285648837685585, + 0.021599670872092247, -0.023687949404120445, -0.00855571310967207, + -0.07308200001716614, -0.01865426078438759, 0.01531145442277193, + 0.0014181601582095027, 0.009810240007936954, -0.04577852413058281, + 0.02196589857339859, 0.002251913771033287, -0.0351189449429512, + 0.0013480314519256353, 0.02700738050043583, 0.021623047068715096, + -0.012997204437851906, -0.012864738702774048, 0.04727460443973541, + 0.014477700926363468, -0.012927074916660786, 0.00814273301512003, + -0.005610303953289986, 0.00518173910677433, 0.011072558350861073, + -0.005750561598688364, -0.009693358093500137, -0.009428427554666996, + -0.003638905705884099, 0.0159036535769701, -0.005485630594193935, + -0.003701242385432124, -0.019558142870664597, -0.004316817037761211, + 0.022994454950094223, 0.03232158347964287, 0.04605124890804291, + -0.04007471352815628, 0.01918412186205387, -0.036615025252103806, + -0.01489847432821989, 0.05593161657452583, -0.018155567348003387, + -0.03229821100831032, 0.02337626740336418, 0.005649264436215162, + 0.0005610304069705307, -0.041601963341236115, 0.05302516743540764, + 0.027381401509046555, 0.017127012833952904, 0.0003506439970806241, + -0.010527112521231174, 0.020703580230474472, 0.13976670801639557, + -0.013464730232954025, 0.0022129532881081104, -0.002766191493719816, + -0.03004629537463188, -0.0017610121285542846, -0.014049137011170387, + 0.00998945813626051, -0.019215291365981102, 0.04412660002708435, + -0.02331393025815487, 0.014158225618302822, 0.007277811411768198, + -0.012763441540300846, -0.01570885069668293, -0.05846404284238815, + 0.0004207728197798133, -0.011968648061156273, 0.011337489821016788, + -0.03463583439588547, -0.06321722269058228, 0.017376359552145004, + 0.01678415946662426, -0.08861164003610611, -0.00014804970123805106, + 0.005111610516905785, 0.014345236122608185, 0.015093276277184486, + 0.011267360299825668, -0.005158362910151482, 0.002189577091485262, + 0.03795526549220085, 0.0333501435816288, 0.0015194573206827044, + 0.05777055025100708, -0.005859650671482086, -0.02086721360683441, + 0.041407160460948944, 0.027981389313936234, 0.011392033658921719, + -0.011734886094927788, 0.009997250512242317, -0.0025713893119245768, + 0.0315345823764801, -0.006451849360018969, -0.03194756433367729, + -0.025815190747380257, -0.02221524529159069, -0.010262181051075459, + -0.021093184128403664, -0.00021817849483340979, 0.023103544488549232, + -0.021311363205313683, -0.014127057045698166, -0.03778383880853653, + 0.035095568746328354, -0.002080487785860896, -0.09577256441116333, + -0.01047256775200367, 0.018724389374256134, 0.024069763720035553, + -0.00024934683460742235, -0.006475226022303104, -0.0117972232401371, + -0.019885409623384476, 0.02056332305073738, -0.09992574900388718, + -0.013223174959421158, -0.0048233033157885075, 0.0032882613595575094, + -0.006498602218925953, 0.029781363904476166, 0.0034129349514842033, + 0.004301233217120171, 0.030957968905568123, -0.02788788452744484, + 0.003864876227453351, -0.049043409526348114, 0.020298391580581665, + 0.02144382894039154, -0.02499702200293541, -0.006895998492836952, + 0.011142686940729618, -0.022838613018393517, 0.016682861372828484, + -0.0020259430166333914, -0.020711371675133705, 0.07982216030359268, + -0.059983499348163605, 0.0027350231539458036, 0.018584132194519043, + 0.04217857867479324, 0.014945225790143013, -0.04072924703359604, + 0.025784023106098175, 0.004909015726298094, 0.012779026292264462, + -0.02458404004573822, 0.006584315560758114, 0.0033739744685590267, + -0.006740157026797533, 0.003498647827655077, 0.016745198518037796, + 0.005431086290627718, 0.0233684740960598, -0.01979190669953823, + 0.004199936054646969, -0.0400669202208519, 0.003405143041163683, + -0.02759178727865219, 0.017454279586672783, -0.013456937856972218, + -0.015132236294448376, -0.021342530846595764, 0.027404775843024254, + -0.04580968990921974, -0.016667278483510017, -0.026446349918842316, + -0.005220699589699507, 0.003296053735539317, -0.008384287357330322, + -0.047781091183423996, -0.01192189659923315, -0.03818902745842934, + 0.02091396600008011, 0.001558417803607881, 0.0072466423735022545, + 0.010301141999661922, -0.02191135287284851, 0.0022207454312592745, + 0.005220699589699507, 0.007644039113074541, 0.08062474429607391, + -0.016916625201702118, 0.002470092149451375, -0.01870880462229252, + -0.016971170902252197, -0.02560480497777462, -0.014602375216782093, + 0.0215295422822237, -0.005625888239592314, 0.01019984483718872, + 0.006327176000922918, 0.009498557075858116, -0.021326947957277298, + 0.01393225509673357, 0.005353164859116077, 0.007036256603896618, + 0.005555759649723768, 0.03422285243868828, 0.01172709371894598, + 0.05642251297831535, -0.026228170841932297, -0.1293564736843109, + 0.006514186505228281, -0.007121969014406204, 0.023243801668286324, + 0.028394371271133423, 0.014197185635566711, 0.009646606631577015, + -0.012038777582347393, 0.014656919054687023, 0.020321767777204514, + -0.014960811473429203, 0.004262272734194994, 0.051965437829494476, + 0.014298483729362488, -0.0017298436723649502, 0.0006779117393307388, + -0.005119402427226305, -0.02624375745654106, -0.011937480419874191, + -0.014797177165746689, -0.0035921530798077583, 0.039622772485017776, + 0.05270569026470184, 0.01697896234691143, 0.01007517147809267, + -0.0711028128862381 + ] + }, + "background": { + "prompt": "Photo of people with an absolutely stunning or interesting background", + "vector": [ + -0.014705854468047619, 0.001741824671626091, 0.005927403923124075, + 0.010000327602028847, -0.0043155658058822155, -0.010771583765745163, + 0.02611004002392292, -0.00355297583155334, 0.030156968161463737, + 0.03655232489109039, 0.005528777372092009, 0.025919392704963684, + 0.02458486147224903, 0.0015945063205435872, 0.0063086990267038345, + 0.5186651945114136, -0.012028124183416367, -0.009879006072878838, + -0.0039082737639546394, -0.02218443527817726, 0.004506213590502739, + -0.02672531269490719, 0.0031110206618905067, -0.01181147899478674, + 0.005953401327133179, 0.01045094896107912, -0.007365925703197718, + 0.006889307405799627, 0.04576406255364418, 0.03521779179573059, + -0.025555429980158806, 0.0023137673269957304, 0.018926095217466354, + 0.006438686046749353, -0.0043068998493254185, 0.0019931329879909754, + -0.020711250603199005, 0.014021256938576698, 0.0008059189422056079, + -0.021716482937335968, -0.00024264225794468075, -0.009047090075910091, + 0.021248528733849525, -0.03074624016880989, -0.003864944912493229, + -0.020468607544898987, 0.018232833594083786, -0.01973201520740986, + 0.021499838680028915, 0.009463047608733177, 0.012669392861425877, + 0.033042676746845245, 0.02818116545677185, 0.005927403923124075, + -0.018068183213472366, -0.02570274844765663, 0.07050491124391556, + -0.010112982243299484, -0.02271305024623871, 0.008076520636677742, + 0.05238473042845726, -0.010745585896074772, -0.03836347535252571, + 0.014974493533372879, -0.029784338548779488, 0.0220631156116724, + 0.0046448661014437675, -0.035989049822092056, 0.0028250492177903652, + 0.06036592647433281, -0.00710595166310668, 0.026664651930332184, + -0.026629988104104996, 0.023718280717730522, -0.024810170754790306, + 0.011768150143325329, -0.08320896327495575, 0.0017331590643152595, + 0.10377290099859238, 0.04410889744758606, -0.01850147359073162, + -0.07739421725273132, 0.008449150249361992, 0.0003206344263162464, + 0.029567692428827286, 0.024463539943099022, -0.003795618424192071, + 0.00887377467006445, -0.03964601084589958, -0.009168410673737526, + -0.018683454021811485, 0.018024854362010956, -0.01592773012816906, + -0.00891710352152586, -0.00811118446290493, 0.01965402439236641, + 0.026803303509950638, 0.007989862933754921, 0.017738882452249527, + -0.01795552857220173, 0.009437051601707935, -0.03222808986902237, + -0.024402879178524017, 0.06099853292107582, 0.035859059542417526, + 0.03632701188325882, -0.05632766708731651, -0.006845978554338217, + -0.009428384713828564, 0.016369687393307686, 0.0005632766988128424, + -0.015329791232943535, 0.10203107446432114, -0.046647973358631134, + -0.009272401221096516, 0.02673397958278656, -0.01079758070409298, + 0.013042021542787552, -0.006118051242083311, 0.027340583503246307, + 0.000372629176126793, -0.031101539731025696, 0.04186445474624634, + 0.012964029796421528, 0.021673154085874557, 0.01696762628853321, + 0.09339127689599991, -0.022669721394777298, -0.003986265975981951, + -0.012808045372366905, -0.016291694715619087, 0.021621158346533775, + -0.017314258962869644, 0.03532177954912186, 0.026335351169109344, + 0.004714192356914282, -0.02821582928299904, 0.02479284070432186, + 0.002209777943789959, -0.0157284177839756, 0.01334532443434, + -0.017392251640558243, -0.004956834949553013, 0.004835513886064291, + 0.013267332687973976, 0.0009359058458358049, -0.012868705205619335, + -0.010650262236595154, -0.030269624665379524, -0.03364061564207077, + 0.015269131399691105, -0.007556573487818241, -0.06311298906803131, + -0.013631295412778854, -0.04714192450046539, 0.012028124183416367, + -0.006499346345663071, -0.006663996260613203, 0.023865601047873497, + -0.00870912428945303, -0.05077289417386055, 0.025720080360770226, + 0.007816547527909279, 0.012392086908221245, -0.025538098067045212, + 0.01365729235112667, 0.004142250400036573, -0.5228767395019531, + -0.021447842940688133, -0.0018544801278039813, -0.0165516696870327, + 0.07093819975852966, 0.03947269544005394, -0.024229563772678375, + 0.010849575512111187, 0.008041857741773129, -0.0050954874604940414, + -0.016976293176412582, -0.0007019293843768537, 0.05097220838069916, + -0.004497547633945942, 0.028293821960687637, -0.01125686801970005, + -0.01136085670441389, 0.005468116607517004, -0.003431654768064618, + -0.019350720569491386, -0.021612493321299553, -0.0026084042619913816, + 0.014610530808568, -0.030962886288762093, -0.030148301273584366, + 0.04546942561864853, -0.10783714801073074, -0.003925605211406946, + -0.03533044829964638, 0.021629825234413147, 0.003284336533397436, + -0.01100555993616581, -0.02330232411623001, 0.024844834581017494, + 0.012591400183737278, -0.009965664707124233, -0.004722858313471079, + 0.02870977856218815, -0.013908601365983486, 0.00006932636460987851, + -0.017886200919747353, -0.005277469288557768, -0.016083715483546257, + 0.004956834949553013, 0.027409909293055534, -0.020061315968632698, + -0.003535644384101033, 0.004705526866018772, 0.04721991717815399, + -0.0024437541142106056, -0.03354529291391373, 0.010606933385133743, + -0.015347123146057129, 0.022565731778740883, 0.03150016441941261, + 0.02127452753484249, 0.03058158978819847, -0.021586496382951736, + -0.01185480784624815, 0.018388817086815834, 0.005173479672521353, + -0.0035789732355624437, -0.0014471877366304398, -0.003864944912493229, + 0.022739047184586525, -0.008249836973845959, 0.027461905032396317, + -0.022028449922800064, 0.017825540155172348, -0.020061315968632698, + -0.00854447390884161, -0.007027959916740656, -0.02036461792886257, + -0.034524526447057724, 0.0005632766988128424, -0.09423185139894485, + -0.012756050564348698, -0.07407521456480026, -0.0036482997238636017, + -0.03629234805703163, -0.06251505017280579, -0.04469817131757736, + -0.013189340010285378, -0.005156148225069046, -0.02992299012839794, + 0.0010918901534751058, 0.006984631065279245, -0.01935938559472561, + -0.023137671872973442, -0.008189177140593529, -0.04037394002079964, + -0.03426455333828926, 0.011204873211681843, -0.007617233786731958, + -0.004523545037955046, 0.0020711252000182867, -0.0011958797695115209, + -0.016040386632084846, 0.019541367888450623, -0.007088620215654373, + 0.00893443450331688, -0.018258830532431602, 0.020130641758441925, + 0.028805101290345192, -0.009827012196183205, -0.02884843200445175, + -0.02470618113875389, -0.007989862933754921, 0.017149608582258224, + -0.0020711252000182867, -0.0030936887487769127, -0.028432471677660942, + -0.027990518137812614, -0.03516579791903496, -0.002495748922228813, + -0.002781720133498311, -0.059612005949020386, 0.01388260442763567, + -0.004428221378475428, 0.005797416903078556, 0.006404022686183453, + -0.013024689629673958, -0.013908601365983486, 0.013267332687973976, + -0.00565876392647624, -0.014428549446165562, 0.009073087014257908, + 0.006057390943169594, -0.013995259068906307, -0.04328564926981926, + 0.06733322888612747, 0.012903369031846523, 0.012669392861425877, + 0.006430020090192556, -0.002686396474018693, -0.003934271167963743, + 0.005780085455626249, -0.008674461394548416, 0.002036461839452386, + 0.006741988472640514, 0.013068018481135368, 0.0038736106362193823, + 0.0034143230877816677, 0.0636935904622078, 0.01019964087754488, + -0.028623120859265327, -0.003682962851598859, 0.027115274220705032, + 0.06764519959688187, -0.05486314743757248, -0.017036952078342438, + -0.04303433746099472, 0.003241007449105382, 0.03711559996008873, + 0.006499346345663071, 0.0020451275631785393, 0.03806883841753006, + 0.030390942469239235, 0.018215501680970192, -0.0263180211186409, + 0.05684761330485344, 0.005026161205023527, -0.02772187814116478, + 0.0043155658058822155, 0.00480085052549839, 0.009376389905810356, + 0.09580903500318527, 0.019628025591373444, 0.006586004514247179, + -0.03949002921581268, -0.018146174028515816, -0.02420356497168541, + -0.030217627063393593, -0.005303466692566872, 0.02084990404546261, + 0.002625735942274332, -0.005953401327133179, 0.01721893437206745, + -0.026785971596837044, -0.022123774513602257, -0.021257195621728897, + -0.07881540805101395, -0.013891268521547318, 0.007747221272438765, + -0.014610530808568, -0.00005199477163841948, -0.07542707771062851, + 0.036396339535713196, 0.029706347733736038, -0.007383257150650024, + 0.001325866673141718, -0.021040551364421844, -0.017738882452249527, + 0.003145683789625764, -0.012071453034877777, 0.03554708883166313, + 0.003596305148676038, 0.029117072001099586, 0.006915304809808731, + -0.01483584102243185, -0.011187541298568249, -0.008163179270923138, + 0.008267167955636978, -0.014740517362952232, 0.003466318128630519, + -0.011343525722622871, -0.02442021109163761, 0.054941143840551376, + -0.040573254227638245, 0.01577174663543701, -0.020017987117171288, + -0.061795786023139954, -0.004280902910977602, -0.010520275682210922, + -0.04650065675377846, -0.021031884476542473, 0.0007885873783379793, + 0.022938359528779984, 0.013293329626321793, -0.001325866673141718, + -0.05081622302532196, 0.02681197039783001, -0.03442053869366646, + -0.10379889607429504, -0.03584172949194908, 0.035191792994737625, + 0.0017678221920505166, 0.009922335855662823, -0.008969098329544067, + -0.0127473846077919, -0.06911838054656982, 0.01127419900149107, + -0.12100916355848312, -0.021031884476542473, -0.00002599738581920974, + 0.01791219785809517, -0.0077992151491343975, -0.027990518137812614, + 0.008882440626621246, 0.00002599738581920974, 0.043432965874671936, + -0.013778614811599255, -0.045339442789554596, 0.006447351537644863, + -0.004584205336868763, 0.02495748922228813, -0.0038909418508410454, + 0.0056500984355807304, 0.005494114011526108, -0.012418084777891636, + 0.035191792994737625, -0.012634729035198689, -0.01898675598204136, + 0.09869474172592163, -0.08138915151357651, -0.03569440916180611, + 0.01558110024780035, -0.015147809870541096, 0.0030936887487769127, + -0.014064585790038109, 0.028276490047574043, 0.008631131611764431, + -0.018154840916395187, -0.013206670992076397, -0.0013691956410184503, + -0.02682063728570938, 0.0019498037872835994, -0.01621370203793049, + 0.010095651261508465, 0.009740353561937809, 0.0278865285217762, + -0.01866612210869789, 0.016569001600146294, -0.034082572907209396, + -0.03928204998373985, -0.03595438227057457, 0.04257505014538765, + 0.03925605118274689, -0.007686560042202473, -0.007209941744804382, + 0.04557341709733009, -0.013024689629673958, -0.005364127457141876, + -0.02342364378273487, 0.004688194952905178, -0.018423479050397873, + -0.011846141889691353, -0.03744490072131157, 0.02342364378273487, + -0.0050954874604940414, 0.02503548189997673, 0.02118786983191967, + 0.019350720569491386, 0.006178712006658316, -0.047159258276224136, + 0.006005396135151386, -0.01779954321682453, 0.030962886288762093, + 0.1288430541753769, -0.017946861684322357, 0.011066220700740814, + 0.013475310988724232, -0.0294550359249115, -0.0018804774153977633, + 0.002530412282794714, 0.008041857741773129, -0.027583226561546326, + -0.04444686323404312, 0.009151079691946507, -0.041769132018089294, + -0.011724821291863918, -0.004904840141534805, 0.010320961475372314, + -0.013033355586230755, -0.013293329626321793, 0.04311233386397362, + -0.02163849025964737, 0.08099918812513351, -0.023241661489009857, + -0.18601128458976746, 0.027739210054278374, 0.013232668861746788, + 0.043337639421224594, 0.020009320229291916, -0.03266138210892677, + -0.0010138980578631163, 0.0072446041740477085, 0.011724821291863918, + 0.0009359058458358049, -0.034663181751966476, -0.02565941959619522, + 0.021534500643610954, -0.031352847814559937, 0.017444245517253876, + -0.007071288768202066, 0.009047090075910091, -0.020667921751737595, + -0.015468443743884563, -0.0004766187339555472, -0.017262263223528862, + 0.021993787959218025, 0.012357424013316631, -0.00497416639700532, + -0.022149773314595222, -0.017392251640558243 + ] + }, + "sports": { + "prompt": "Photo of people joyfully playing sports together", + "vector": [ + -0.005494717042893171, 0.014157154597342014, -0.007176160346716642, + -0.06306163221597672, -0.02043253928422928, 0.015883635729551315, + -0.017932895570993423, 0.008880123496055603, -0.03749468922615051, + -0.036038439720869064, -0.010418944992125034, -0.015861116349697113, + 0.010839304886758327, 0.020199840888381004, 0.01848086528480053, + 0.5462063550949097, 0.013301420025527477, 0.017745234072208405, + 0.022181542590260506, -0.061507806181907654, 0.010126193054020405, + -0.0285244882106781, 0.01592867448925972, -0.021588532254099846, + 0.024313373491168022, -0.03855309635400772, -0.03058875910937786, + 0.00425615394487977, -0.030160890892148018, 0.02802155539393425, + 0.0383354127407074, -0.011372262611985207, 0.027969012036919594, + -0.0023945558350533247, 0.012610825709998608, -0.004083505365997553, + 0.009570715948939323, -0.010929382406175137, 0.011529898270964622, + 0.02537928707897663, 0.013316432014107704, -0.01292609702795744, + 0.0003377899993211031, 0.012032830156385899, -0.011297198012471199, + -0.025033991783857346, 0.0018315722700208426, -0.008910148404538631, + -0.006342945154756308, 0.019621845334768295, 0.01180013082921505, + 0.03567812964320183, 0.004196102265268564, 0.008797552436590195, + 0.008519813418388367, -0.010914369486272335, 0.01219797134399414, + -0.01037390623241663, 0.0065831514075398445, 0.02144591137766838, + -0.045661699026823044, -0.0022969720885157585, -0.03618856891989708, + -0.029072457924485207, -0.01758759841322899, 0.010741720907390118, + 0.005885052029043436, 0.0196593776345253, 0.010156218893826008, + 0.026677902787923813, 0.02429835870862007, 0.034169334918260574, + -0.012858538888394833, -0.03154958412051201, -0.019306574016809464, + 0.009345523081719875, -0.058467693626880646, 0.06812848895788193, + 0.048529159277677536, 0.023134861141443253, -0.0144949434325099, + -0.024658668786287308, -0.015253094024956226, 0.02557445503771305, + -0.01504291407763958, 0.05085615813732147, -0.012265530414879322, + 0.02958289533853531, 0.030363567173480988, 0.010148712433874607, + -0.04953502491116524, 0.0010358892614021897, -0.018240658566355705, + -0.0057799615897238255, 0.019937116652727127, 0.04628473520278931, + -0.09602243453264236, -0.0007431379635818303, -0.013699259608983994, + 0.029042433947324753, -0.0016814435366541147, 0.03136942908167839, + 0.014419878832995892, -0.03038608655333519, 0.039776645600795746, + 0.0373820923268795, -0.0472155325114727, 0.043604932725429535, + -0.0056523522362113, 0.0011259665479883552, 0.0028824745677411556, + -0.0006980992620810866, 0.06918689608573914, -0.0010208763414993882, + 0.013166302815079689, -0.07102597504854202, -0.008249581791460514, + -0.015883635729551315, 0.007619041018188, 0.04771095886826515, + 0.07880265265703201, -0.03757726028561592, 0.006523100193589926, + -0.01739243045449257, -0.0012460697907954454, -0.013383990153670311, + 0.04241140931844711, -0.008865110576152802, 0.0324128232896328, + 0.023127354681491852, -0.058385126292705536, 0.008444749750196934, + -0.005592301022261381, -0.009713338688015938, 0.012881058268249035, + -0.007011018693447113, -0.00820454303175211, -0.008925162255764008, + 0.027075743302702904, 0.02810412459075451, 0.021903803572058678, + 0.019404157996177673, -0.009540691040456295, -0.05063847079873085, + 0.012866045348346233, 0.010906863026320934, -0.015906155109405518, + 0.016048777848482132, -0.026189984753727913, -0.0010358892614021897, + 0.004714047070592642, 0.019839532673358917, -0.04477594047784805, + 0.06558380275964737, -0.00998357031494379, 0.02143840305507183, + 0.03393663465976715, -0.007829220965504646, 0.014900291338562965, + -0.021986374631524086, -0.017752740532159805, -0.01670183800160885, + -0.031609635800123215, 0.03666897863149643, -0.026017336174845695, + 0.007341302931308746, 0.017880350351333618, -0.5495617985725403, + -0.010741720907390118, -0.027683766558766365, -0.014374840073287487, + 0.054324135184288025, 0.03813273459672928, 0.028156671673059464, + -0.016273971647024155, 0.012858538888394833, -0.007371328305453062, + 0.03422938287258148, -0.04352236166596413, 0.013203836046159267, + 0.014637566171586514, 0.0077091180719435215, 0.02092796564102173, + -0.0005930090555921197, -0.010366398841142654, -0.012798487208783627, + -0.040970172733068466, -0.038800809532403946, 0.021490950137376785, + 0.004714047070592642, 0.024628642946481705, 0.011927739717066288, + 0.06314420700073242, -0.013368976302444935, -0.02664787508547306, + -0.002281959168612957, 0.0007281251018866897, 0.0177001953125, + -0.03793756663799286, 0.058355093002319336, 0.025011472404003143, + 0.016641788184642792, -0.008737500756978989, 0.00806192122399807, + 0.02567203901708126, 0.018345749005675316, -0.004308698698878288, + -0.020237373188138008, 0.014284763485193253, 0.019899582490324974, + -0.025296716019511223, 0.013871908187866211, -0.015163017436861992, + -0.015478287823498249, -0.011650001630187035, -0.0025521910283714533, + -0.002664787694811821, 0.009916013106703758, 0.009383055381476879, + 0.0029575389344245195, 0.0007731637451797724, 0.05487960949540138, + -0.03497252240777016, 0.0012085374910384417, -0.025131573900580406, + 0.0034754835069179535, 0.019877063110470772, 0.002056765602901578, + -0.0093380156904459, -0.07027532905340195, 0.004045973066240549, + -0.03432697057723999, 0.015313146635890007, -0.04633728042244911, + 0.009345523081719875, -0.07399101555347443, -0.03498753532767296, + -0.002154349349439144, -0.01072670891880989, -0.024463500827550888, + -0.01210038736462593, -0.012460697442293167, -0.0177077017724514, + 0.0077916886657476425, -0.04800371080636978, 0.018630994483828545, + 0.00602016830816865, -0.004000934772193432, -0.028171684592962265, + -0.0006755799986422062, 0.0035730674862861633, -0.06694997847080231, + -0.004083505365997553, -0.01709968037903309, -0.01528311986476183, + -0.01997464708983898, 0.01439735945314169, -0.016446620225906372, + -0.029470300301909447, 0.016101323068141937, 0.0025822166353464127, + 0.003452964359894395, -0.01640908606350422, 0.004473840352147818, + 0.014254736714065075, -0.019726933911442757, 0.0068533835001289845, + -0.019509248435497284, 0.01494533009827137, 0.007836727425456047, + -0.03067133203148842, 0.020522618666291237, -0.0002927513269241899, + -0.019103901460766792, 0.011026966385543346, 0.011815142817795277, + 0.031331900507211685, 0.020605187863111496, -0.009593235328793526, + -0.0004503866657614708, -0.0006380477570928633, 0.0030476164538413286, + 0.02152848243713379, -0.012468203902244568, 0.0038207799661904573, + 0.0017640143632888794, 0.006733280140906572, 0.02990567311644554, + -0.03845551237463951, -0.006800837814807892, -0.013759312219917774, + -0.05141913890838623, -0.03612851724028587, 0.021798713132739067, + 0.011214627884328365, 0.017354898154735565, -0.00008257088484242558, + -0.03302835300564766, 0.043011926114559174, 0.00012010310456389561, + -0.02448602020740509, 0.03386157006025314, 0.013399002142250538, + 0.04048225283622742, 0.028764694929122925, -0.019546780735254288, + 0.03656388819217682, 0.02310483530163765, 0.010749228298664093, + 0.00043537377496249974, 0.012956122867763042, 0.012610825709998608, + -0.027608701959252357, 0.0018315722700208426, 0.008902642875909805, + 0.00983344204723835, -0.038305383175611496, -0.0017715208232402802, + -0.02928263694047928, -0.018360761925578117, 0.013691754080355167, + -0.0030250968411564827, -0.050210606306791306, 0.056485991925001144, + -0.02546936459839344, 0.01750502735376358, -0.0015463274903595448, + 0.06896920502185822, 0.011199614964425564, 0.0015313145704567432, + -0.019524261355400085, -0.01533566601574421, 0.05636589229106903, + 0.08704472333192825, -0.023119846358895302, -0.0022594397887587547, + 0.003993428312242031, 0.0024771266616880894, -0.016897005960345268, + -0.03360635042190552, -0.007191173732280731, -0.021956348791718483, + 0.0373445600271225, 0.02390802465379238, 0.010486502200365067, + -0.03097158670425415, -0.010809279978275299, -0.024088179692626, + -0.04976021870970726, 0.017152225598692894, -0.010741720907390118, + -0.004226128105074167, -0.004976772237569094, -0.08818570524454117, + 0.0011935245711356401, 0.039288729429244995, -0.051389116793870926, + -0.007123615127056837, 0.017377417534589767, -0.018218139186501503, + -0.0009533183765597641, -0.018345749005675316, -0.03401920571923256, + 0.013901934027671814, 0.03311843052506447, 0.0007131121819838881, + 0.015808571130037308, -0.01238563284277916, 0.01092187687754631, + 0.015418236143887043, 0.03311843052506447, -0.039093561470508575, + 0.006673228461295366, -0.0020342464558780193, 0.0029275130946189165, + -0.03431195393204689, 0.029192563146352768, -0.0120553495362401, + -0.06441279500722885, 0.016183892264962196, -0.03513015806674957, + -0.020124776288866997, -0.030558735132217407, -0.004383763298392296, + 0.01282851304858923, -0.01631150208413601, 0.008872617036104202, + -0.033185988664627075, 0.02941024675965309, 0.06830864399671555, + -0.031121715903282166, 0.012265530414879322, 0.013008668087422848, + -0.01798544079065323, 0.0006455541588366032, -0.012212984263896942, + -0.02881723828613758, -0.09747868776321411, 0.09067033976316452, + -0.13611434400081635, 0.0013511599972844124, -0.03847803175449371, + -0.00820454303175211, -0.009112823754549026, 0.03943134844303131, + -0.013016173616051674, 0.01494533009827137, 0.012340594083070755, + 0.01140979491174221, 0.01633402146399021, -0.022046426311135292, + 0.017362404614686966, -0.009127836674451828, -0.012535762041807175, + 0.0019141433294862509, -0.01858595572412014, -0.011019459925591946, + 0.037922557443380356, 0.0324353463947773, -0.006763305980712175, + 0.0619506798684597, -0.023855479434132576, 0.0074914307333528996, + 0.01439735945314169, 0.05958615615963936, 0.019839532673358917, + -0.00004503866512095556, -0.01229555532336235, 0.009593235328793526, + -0.013931960798799992, -0.010809279978275299, -0.02095799148082733, + -0.006402996834367514, -0.027360988780856133, -0.03969407454133034, + 0.039086051285266876, -0.002116817282512784, 0.04214868322014809, + -0.006950967013835907, -0.0016814435366541147, -0.061410218477249146, + 0.030904032289981842, -0.03679658845067024, -0.032773133367300034, + 0.017857830971479416, 0.0015988725936040282, 0.009525677189230919, + 0.007041044998914003, -0.01429977547377348, -0.022241592407226562, + -0.003385406220331788, 0.010201257653534412, 0.010846812278032303, + 0.028967367485165596, -0.02282709628343582, -0.004413789138197899, + -0.04995538666844368, -0.009323003701865673, 0.0046014501713216305, + -0.04801872372627258, -0.015425742603838444, -0.018661020323634148, + -0.021581025794148445, 0.01170254684984684, -0.011777611449360847, + 0.014337308704853058, -0.013263886794447899, 0.007123615127056837, + -0.026272553950548172, 0.01780528575181961, -0.034747328609228134, + -0.019103901460766792, 0.05255261808633804, 0.017264820635318756, + -0.0270532239228487, 0.024230802431702614, 0.02319491095840931, + -0.009855961427092552, -0.008024388924241066, -0.02516160160303116, + 0.008677449077367783, -0.0028224231209605932, 0.028449421748518944, + 0.00840721745043993, 0.08323144912719727, -0.009713338688015938, + -0.04470087215304375, -0.017940400168299675, 0.011529898270964622, + 0.006215335801243782, 0.016641788184642792, 0.0046915276907384396, + 0.008887629956007004, 0.02921508252620697, -0.008437243290245533, + -0.013113757595419884, 0.013301420025527477, 0.010148712433874607, + -0.004211115185171366, 0.009548196569085121, 0.004623969551175833, + 0.0022969720885157585, -0.00997606385499239, 0.017362404614686966, + -0.016138853505253792, -0.017947906628251076, -0.0021768687292933464, + 0.028246749192476273, 0.03518270328640938, -0.0038508058059960604, + 0.0022294139489531517, -0.10991685837507248 + ] + }, + "roadtrip": { + "prompt": "Photo of people on a road trip together", + "vector": [ + -0.004912007600069046, 0.0011731297709047794, 0.0019375563133507967, + 0.02452976442873478, 0.015061472542583942, -0.02907847985625267, + -0.0028987659607082605, 0.01586374267935753, -0.020556261762976646, + -0.016181621700525284, -0.03766882047057152, -0.02740582451224327, + -0.009059589356184006, 0.021070925518870354, -0.002868491690605879, + 0.5225347280502319, -0.007871323265135288, 0.010830637067556381, + 0.017354751005768776, -0.0008022694382816553, 0.032658420503139496, + -0.018527882173657417, -0.0190047025680542, 0.0072355614975094795, + 0.025120114907622337, 0.00825731921941042, -0.0001665087474975735, + 0.018641412258148193, 0.03140203654766083, 0.03611725941300392, + 0.0012261098017916083, 0.00574455177411437, -0.02322797104716301, + -0.0031031176913529634, 0.007129601668566465, -0.007651833817362785, + -0.030705727636814117, -0.002891197334975004, -0.010179739445447922, + -0.01948152296245098, 0.031053880229592323, 0.012442744337022305, + -0.017438005656003952, 0.01353261899203062, -0.043587446212768555, + -0.051943160593509674, -0.04392046853899956, -0.008302731439471245, + 0.011148517020046711, 0.04066597670316696, 0.0070009357295930386, + -0.009914839640259743, 0.012972544878721237, 0.01281360536813736, + -0.007311247289180756, -0.046047236770391464, -0.02214566245675087, + -0.0011201497400179505, -0.009771035984158516, -0.007818342186510563, + -0.0008249751408584416, 0.052904367446899414, -0.030183492228388786, + 0.04566124081611633, -0.08333005756139755, 0.01936042681336403, + -0.02107849344611168, 0.0004238404508214444, 0.03165179863572121, + 0.03735850751399994, -0.012041609734296799, 0.02590724639594555, + -0.020253518596291542, -0.005517494399100542, -0.050595950335264206, + 0.022032134234905243, -0.06305383890867233, 0.0010066210525110364, + 0.11184089630842209, 0.06904814392328262, -0.016052957624197006, + -0.011193929240107536, 0.02701982855796814, 0.01279089879244566, + 0.0417180098593235, 0.0484994575381279, -0.018777644261717796, + 0.0058126687072217464, -0.023439889773726463, 0.026868455111980438, + -0.014274340122938156, -0.013456934131681919, 0.02573316916823387, + -0.025786150246858597, 0.005313142668455839, 0.03297629952430725, + -0.055485256016254425, 0.04085519164800644, 0.015462607145309448, + 0.05586368218064308, -0.034747347235679626, -0.0027246885001659393, + -0.002020810730755329, -0.07925816625356674, -0.011322595179080963, + 0.03522416949272156, -0.06007181107997894, -0.009831584990024567, + -0.012041609734296799, 0.014047283679246902, 0.020450301468372345, + -0.013441797345876694, 0.05773312598466873, -0.02776155062019825, + -0.02318255975842476, -0.03757042810320854, 0.0023386909160763025, + -0.0450330451130867, -0.00011352869478287175, 0.007364227436482906, + 0.0449119508266449, -0.057589318603277206, -0.012828742153942585, + -0.02518066205084324, -0.025543956086039543, -0.04224780946969986, + 0.048923294991254807, -0.026096461340785027, 0.000628192035946995, + -0.0224711112678051, -0.017475849017500877, 0.03426295891404152, + 0.004927145317196846, 0.050686776638031006, 0.030804118141531944, + 0.004283815622329712, -0.024257296696305275, -0.02782209776341915, + -0.005721846129745245, -0.004450324922800064, 0.01995077356696129, + 0.07054673135280609, 0.03744933009147644, -0.0414758138358593, + -0.025665052235126495, -0.038879793137311935, -0.006849563680589199, + 0.00522231962531805, -0.047856125980615616, -0.04316360875964165, + -0.016022682189941406, 0.03517875447869301, 0.0066981930285692215, + 0.0018088903743773699, 0.042686786502599716, 0.029086049646139145, + -0.04600939154624939, 0.020359478890895844, 0.0003557232266757637, + -0.008075674064457417, -0.019640464335680008, -0.020972533151507378, + 0.0026868456043303013, 0.005434240214526653, 0.055190082639455795, + -0.032923322170972824, 0.017248792573809624, -0.5268185138702393, + 0.0002573317033238709, 0.03440675884485245, 0.021328255534172058, + 0.04710683599114418, -0.008976335637271404, 0.008590337820351124, + 0.0345429964363575, -0.020654652267694473, -0.020215675234794617, + 0.01556099858134985, 0.006645212881267071, 0.02641434222459793, + 0.019859952852129936, -0.0069706616923213005, 0.033248770982027054, + -0.023031188175082207, 0.07042562961578369, -0.010823068208992481, + 0.030138082802295685, 0.0007114464533515275, -0.010807931423187256, + -0.007462618872523308, 0.014092694967985153, -0.03826673701405525, + 0.0121475700289011, 0.03548149764537811, 0.014796572737395763, + -0.01946638524532318, 0.017218517139554024, -0.09220042824745178, + -0.012639527209103107, 0.010104053653776646, 0.02154017798602581, + 0.02644461579620838, -0.01799808256328106, -0.02150990255177021, + 0.0053434171713888645, -0.007099327631294727, -0.0258769728243351, + 0.01148153468966484, 0.026391636580228806, -0.006350038107484579, + -0.030713295564055443, -0.018558157607913017, 0.024552471935749054, + -0.017074715346097946, 0.005146633833646774, -0.012510862201452255, + 0.00226300535723567, -0.018671685829758644, 0.02938879281282425, + -0.002633865689858794, -0.0011428555008023977, -0.0028609230648726225, + -0.03088737092912197, 0.04949093982577324, 0.031000901013612747, + 0.004018915817141533, 0.005456945393234491, 0.0007568579749204218, + -0.016227034851908684, -0.04377666488289833, -0.024143768474459648, + -0.034611113369464874, -0.014766298234462738, -0.04628186300396919, + -0.001967830816283822, -0.0005600748700089753, -0.019973481073975563, + 0.027352845296263695, 0.007409639656543732, 0.009483429603278637, + -0.02866221033036709, 0.016022682189941406, -0.017839141190052032, + 0.0030804118141531944, -0.056567560881376266, 0.05664324760437012, + -0.02036704681813717, 0.04857514426112175, -0.03882681205868721, + 0.01077765692025423, -0.042936552315950394, -0.042611103504896164, + -0.03981829434633255, 0.06430265307426453, 0.001150424126535654, + 0.002346259541809559, -0.04794694855809212, -0.06702733784914017, + 0.013419090770184994, -0.00125638407189399, 0.008582768961787224, + -0.03512577712535858, -0.008151359856128693, 0.010368953458964825, + 0.03081168793141842, -0.005948903504759073, 0.01629515178501606, + -0.007091759238392115, 0.014122968539595604, -0.03186371922492981, + -0.03385425731539726, 0.01768776960670948, -0.03588263317942619, + -0.06287219375371933, -0.011875101365149021, -0.015333942137658596, + -0.00028003743500448763, -0.018020788207650185, 0.020806023851037025, + -0.008787120692431927, 0.009876996278762817, 0.014804141595959663, + 0.05313899368047714, -0.04797722399234772, -0.002611159812659025, + -0.0019753992091864347, -0.0010293268132954836, 0.006675486918538809, + -0.025279054418206215, -0.011965923942625523, -0.015470176003873348, + -0.028942245990037918, -0.029517458751797676, 0.007878891192376614, + 0.02733014151453972, 0.01634056307375431, -0.018020788207650185, + -0.10486266762018204, 0.01142855454236269, 0.02041245810687542, + -0.009990525431931019, -0.029070913791656494, 0.02555152401328087, + 0.018807919695973396, 0.020344341173768044, -0.02085900492966175, + 0.004647107794880867, -0.005517494399100542, 0.03657137602567673, + 0.0006963092600926757, 0.03387695923447609, 0.007379364687949419, + -0.01353261899203062, -0.02150990255177021, -0.01656005159020424, + 0.041203346103429794, -0.04540390521287918, 0.020927121862769127, + -0.04470759630203247, -0.05180692300200462, 0.022758718580007553, + 0.06918437778949738, -0.0031182547099888325, -0.006864701863378286, + 0.010800362564623356, -0.020314067602157593, -0.03897061571478844, + 0.0869629755616188, 0.002891197334975004, -0.0037615839391946793, + 0.02598293125629425, -0.013956460170447826, 0.00941531267017126, + 0.051958296447992325, -0.0029063343536108732, 0.0208741407841444, + 0.02943420596420765, -0.034656524658203125, -0.03898575156927109, + 0.006569527089595795, -0.021305551752448082, 0.014440849423408508, + 0.07355902343988419, -0.0035875067114830017, 0.010626285336911678, + -0.03583722561597824, -0.01316175889223814, -0.01621946506202221, + -0.0011958355316892266, 0.008620611391961575, -0.02355341799557209, + 0.01207945216447115, 0.040537308901548386, -0.07472458481788635, + -0.02253166027367115, 0.011171223595738411, -0.048226986080408096, + -0.020450301468372345, 0.002346259541809559, 0.018921447917819023, + -0.0008855237392708659, -0.030539218336343765, -0.012162706814706326, + -0.011125811375677586, 0.007326385006308556, 0.006804152857512236, + 0.026331087574362755, 0.009899700991809368, 0.007553441915661097, + -0.011958355084061623, 0.008310300298035145, 0.004889302421361208, + -0.03930363059043884, 0.02355341799557209, 0.019738854840397835, + -0.022493818774819374, 0.02474168688058853, -0.013267719186842442, + -0.08479836583137512, 0.030357571318745613, -0.01624973863363266, + -0.03583722561597824, 0.008045399561524391, -0.025309329852461815, + 0.034997113049030304, -0.0174228698015213, 0.009286646731197834, + -0.027042534202337265, 0.02798103727400303, -0.043292272835969925, + -0.05818723887205124, 0.007606422062963247, -0.019352857023477554, + 0.021971585229039192, -0.005926197394728661, 0.02698955498635769, + -0.017074715346097946, -0.008151359856128693, 0.07210585474967957, + -0.08330735564231873, 0.03763097524642944, 0.008211908861994743, + -0.029948867857456207, -0.0052980054169893265, 0.023931847885251045, + -0.001922419061884284, 0.03935661166906357, 0.09029315412044525, + -0.03508793190121651, 0.015053904615342617, -0.02816268429160118, + 0.004964988213032484, -0.0021192021667957306, 0.002020810730755329, + 0.02364424243569374, 0.04837078973650932, -0.005161771085113287, + 0.01001323014497757, -0.010959303006529808, 0.02909361943602562, + 0.10385604947805405, -0.07229506969451904, -0.026853321120142937, + 0.0004314090183470398, 0.007167445030063391, -0.046864643692970276, + -0.037547722458839417, 0.0433603897690773, 0.0007492893491871655, + -0.008590337820351124, -0.0056158858351409435, 0.000983915408141911, + 0.01420622318983078, 0.02777668461203575, -0.03675302118062973, + 0.007318815682083368, -0.005721846129745245, 0.022387858480215073, + -0.028185389935970306, 0.00484389066696167, -0.020480575039982796, + 0.011875101365149021, -0.0156291164457798, 0.011345300823450089, + 0.01621946506202221, 0.004480598960071802, 0.013752108439803123, + 0.0014683044282719493, -0.005494788289070129, -0.021116336807608604, + -0.071303591132164, 0.0020662222523242235, -0.02361396700143814, + -0.008726571686565876, 0.0031560976058244705, -0.0024900624994188547, + 0.0008855237392708659, 0.03772936761379242, 0.009089863859117031, + 0.0033982922323048115, 0.04201318323612213, -0.02085900492966175, + 0.002278142375871539, 0.0008552494109608233, 0.035383109003305435, + 0.03050137497484684, 0.020049167796969414, 0.011262046173214912, + -0.03053164854645729, -0.055840980261564255, 0.016317857429385185, + 0.01570480316877365, 0.04692519083619118, 0.024340549483895302, + -0.00008325437374878675, 0.003148528980091214, -0.00414001289755106, + 0.019246896728873253, -0.003254489041864872, -0.004026484210044146, + 0.0005222319741733372, 0.006191097665578127, 0.047871265560388565, + -0.021146610379219055, 0.013055799528956413, -0.02017783187329769, + -0.07355145364999771, -0.019935637712478638, 0.00869629718363285, + -0.0010520325740799308, 0.015886448323726654, 0.04725820943713188, + 0.03541338071227074, 0.005631023086607456, 0.004034052602946758, + -0.010361384600400925, 0.007515599485486746, -0.015825899317860603, + 0.009082295000553131, -0.005911060608923435, -0.009967818856239319, + 0.01814945414662361, -0.010247856378555298, -0.021562881767749786, + -0.0366092175245285, 0.004238404333591461, 0.04177099093794823, + 0.0060851373709738255, 0.04944552853703499, -0.007666971068829298, + 0.022804129868745804, -0.055992353707551956 + ] + } + }, + "clip_memory_types": { + "sunrise": { + "prompt": "Photo of an absolutely stunning sunrise or sunset", + "vector": [ + 0.015730585902929306, 0.01055071223527193, 0.01939469203352928, + -0.019281307235360146, 0.012806463055312634, 0.006988056469708681, + -0.018081819638609886, 0.04163592681288719, 0.0742190033197403, + 0.01209631934762001, -0.002148335101082921, -0.03029748983681202, + -0.03369305282831192, -0.011308596469461918, -0.01473399717360735, + 0.5191869139671326, -0.023709263652563095, -0.020820945501327515, + 0.014399810694158077, -0.007214825134724379, -0.005866148043423891, + -0.00647484278306365, -0.003377659944817424, 0.02588743530213833, + 0.011099730618298054, -0.04701869562268257, 0.031532783061265945, + 0.013230162672698498, 0.009476544335484505, -0.030768930912017822, + -0.000035805580409942195, -0.012388731352984905, -0.01604686863720417, + 0.015098019503057003, -0.03659927472472191, 0.034164492040872574, + 0.004624887835234404, 0.0031270210165530443, -0.04144496098160744, + 0.025583088397979736, 0.041230130940675735, -0.016422826796770096, + 0.004636823199689388, -0.023184115067124367, -0.006450972519814968, + -0.03528043255209923, 0.0026615483220666647, 0.041188355535268784, + -0.007155148778110743, 0.019484205171465874, 0.006892574951052666, + 0.042525097727775574, -0.047812387347221375, 0.034898508340120316, + -0.014883186668157578, 0.01724635623395443, 0.04145689681172371, + 0.0011517462553456426, -0.014799641445279121, 0.013128713704645634, + 0.018666643649339676, -0.0030434743966907263, 0.005376805085688829, + 0.014960765838623047, 0.007292403373867273, 0.028608661144971848, + 0.04778851941227913, -0.008903655223548412, 0.04611162468791008, + 0.023643620312213898, 0.013701601885259151, -0.024174736812710762, + -0.04187462851405144, -0.025075843557715416, 0.019740810617804527, + 0.030040884390473366, 0.0019454365829005837, 0.04633242264389992, + 0.0022915571462363005, -0.012394699268043041, -0.012448407709598541, + -0.025970982387661934, 0.031562622636556625, -0.010216525755822659, + -0.02288573607802391, -0.045198578387498856, 0.015903646126389503, + -0.022080110386013985, 0.010950540192425251, 0.023184115067124367, + 0.030351197347044945, 0.011887453496456146, -0.02117900364100933, + 0.02244413271546364, -0.002118496922776103, -0.027277885004878044, + -0.03205793350934982, -0.00811593234539032, 0.03711845353245735, + 0.006695643533021212, -0.013528542593121529, -0.009279613383114338, + -0.019621457904577255, 0.02236655354499817, 0.05651910975575447, + -0.002482520416378975, -0.047746747732162476, 0.03063167631626129, + -0.014268524944782257, -0.03448674455285072, 0.022312846034765244, + -0.012018740177154541, 0.04589679092168808, -0.01978258416056633, + -0.04169560223817825, -0.03168197348713875, -0.001808181987144053, + -0.017144907265901566, -0.02877575345337391, -0.011195212602615356, + 0.01444755308330059, -0.045335836708545685, -0.002822673413902521, + 0.012991459108889103, 0.029909595847129822, 0.01564704068005085, + 0.03973226249217987, 0.010467165149748325, 0.01981242187321186, + 0.010109109804034233, 0.014184977859258652, -0.0038550677709281445, + -0.02150125242769718, 0.027779165655374527, -0.06068449467420578, + -0.041063033044338226, -0.05965806543827057, 0.01097441092133522, + 0.006158560514450073, -0.02341088280081749, 0.0131645193323493, + 0.016536211594939232, -0.005472286604344845, -0.05319515988230705, + 0.05447222664952278, -0.05522414296865463, -0.015760423615574837, + -0.0232557263225317, -0.03621137887239456, 0.0044100540690124035, + -0.0027212242130190134, -0.02476552687585354, 0.024353764951229095, + -0.03384821116924286, -0.02725401520729065, -0.04609968513250351, + 0.009279613383114338, -0.07156343013048172, -0.034164492040872574, + -0.008682853542268276, 0.006588227115571499, -0.0029181549325585365, + 0.057515699416399, 0.00784739013761282, -0.022121882066130638, + -0.07184987515211105, 0.004069901071488857, -0.5235790610313416, + 0.027546430006623268, 0.005042619537562132, -0.02073740027844906, + 0.04766916483640671, -0.019621457904577255, -0.046302586793899536, + -0.024711819365620613, 0.018756156787276268, -0.0039863549172878265, + -0.023005086928606033, -0.015849938616156578, -0.012663241475820541, + 0.02015257440507412, 0.015885744243860245, 0.050837960094213486, + 0.04939976707100868, 0.010592484846711159, -0.004505535587668419, + -0.0480152890086174, -0.029109938070178032, 0.016178155317902565, + 0.03152681514620781, -0.004929235205054283, -0.015277048572897911, + -0.0026973539497703314, -0.07340145111083984, -0.004702466540038586, + -0.021184969693422318, -0.005663249641656876, -0.0696597620844841, + -0.020940298214554787, -0.028447536751627922, 0.058261651545763016, + -0.06172285974025726, 0.03253534063696861, 0.02317814715206623, + 0.016792817041277885, 0.025672603398561478, 0.0007161116227507591, + 0.013242097571492195, -0.027337562292814255, 0.0019693071953952312, + 0.013838857412338257, 0.02570243924856186, -0.013677733018994331, + 0.01089086476713419, 0.004135544877499342, -0.021877212449908257, + -0.02387038804590702, -0.0032583081629127264, 0.02525487169623375, + -0.020098866894841194, -0.03658733889460564, 0.007525139953941107, + 0.02305879443883896, -0.03377063199877739, 0.005030684173107147, + -0.034039173275232315, -0.002482520416378975, 0.004923267289996147, + 0.056495241820812225, -0.016333313658833504, 0.00896929856389761, + 0.0319385789334774, -0.030315391719341278, -0.02822076715528965, + -0.02798803150653839, 0.0642889216542244, 0.017359739169478416, + 0.030237814411520958, 0.04572969675064087, 0.015551558695733547, + 0.0075370753183960915, 0.015581395477056503, -0.04787803068757057, + 0.024819236248731613, -0.07242872565984726, 0.017019586637616158, + -0.05419175326824188, -0.028584789484739304, -0.0478004552423954, + -0.025833727791905403, 0.027904484421014786, -0.02554728277027607, + -0.0031270210165530443, 0.06221219524741173, -0.03958307206630707, + 0.02147141471505165, 0.012155994772911072, -0.026895960792899132, + -0.012388731352984905, 0.009267677552998066, 0.0232557263225317, + 0.01873825490474701, -0.024150865152478218, -0.012490181252360344, + 0.013128713704645634, 0.04901784285902977, -0.008772367611527443, + 0.03884309157729149, -0.0010204591089859605, 0.04531793296337128, + 0.02846544049680233, -0.02359587885439396, 0.00020289829990360886, + -0.010616355575621128, 0.004069901071488857, 0.060063865035772324, + 0.011982935480773449, -0.014059659093618393, -0.008676886558532715, + -0.01345096342265606, -0.01842794008553028, -0.023607812821865082, + 0.027993997558951378, -0.0009488479699939489, -0.006242106202989817, + 0.017532799392938614, -0.03568026423454285, 0.052837107330560684, + 0.0027391270268708467, 0.012931782752275467, -0.028196895495057106, + -0.0020707561634480953, 0.013940306380391121, 0.023106535896658897, + 0.007017894182354212, 0.04989507794380188, -0.032821785658597946, + 0.08213800936937332, 0.002106561791151762, 0.0013606121065095067, + -0.033329032361507416, -0.014757867902517319, -0.026525970548391342, + -0.015945419669151306, -0.019203728064894676, 0.020928362384438515, + 0.022927507758140564, 0.035328175872564316, -0.04256686940789223, + 0.009589928202331066, 0.1263817697763443, -0.014907057397067547, + 0.007602719124406576, 0.00035805581137537956, 0.022491874173283577, + 0.03591896593570709, -0.03532220795750618, 0.015599298290908337, + -0.07282259315252304, 0.00899913627654314, 0.015396400354802608, + -0.03627702593803406, 0.010562647134065628, 0.026794511824846268, + -0.04384990409016609, 0.015306886285543442, 0.020647887140512466, + 0.07979274541139603, -0.015378497540950775, 0.06526760756969452, + -0.015736553817987442, -0.007471431512385607, -0.04404086619615555, + 0.06075610592961311, -0.021214807406067848, 0.04585501551628113, + -0.017174743115901947, -0.0015873807715252042, 0.056328147649765015, + 0.017234420403838158, -0.017634248360991478, 0.022098012268543243, + 0.027248049154877663, -0.027242079377174377, 0.005257453303784132, + 0.008921558037400246, 0.022038336843252182, 0.013176454231142998, + 0.004380216356366873, 0.03504173085093498, -0.016643628478050232, + 0.0297246016561985, 0.011791972443461418, -0.001861890428699553, + 0.016166219487786293, -0.03747054189443588, 0.057521671056747437, + -0.0038431326393038034, -0.013594185933470726, 0.02111932635307312, + -0.0290801003575325, 0.011768100783228874, 0.01912018097937107, + -0.01493092905730009, 0.014662385918200016, 0.039523396641016006, + 0.02468794956803322, 0.0033120163716375828, 0.01601702906191349, + -0.010031530633568764, -0.015020442195236683, 0.013051135465502739, + -0.01444755308330059, -0.023816680535674095, 0.02502213604748249, + 0.012454375624656677, 0.025863565504550934, -0.006773222703486681, + -0.06847818195819855, -0.03054216131567955, 0.001545607578009367, + -0.03384821116924286, -0.0265438724309206, -0.003395562758669257, + 0.0016351215308532119, 0.018421972170472145, 0.005895986221730709, + -0.03442706912755966, -0.003222502302378416, 0.014190945774316788, + 0.04217897355556488, -0.05589848384261131, 0.07648072391748428, + -0.03790617734193802, -0.018111657351255417, -0.03603235259652138, + 0.004421989433467388, -0.0255771204829216, 0.02341088280081749, + -0.028089478611946106, 0.002977831056341529, -0.006874672137200832, + -0.021835438907146454, -0.02285589650273323, -0.007948839105665684, + 0.024371666833758354, -0.05104682594537735, 0.004875526763498783, + -0.033102262765169144, -0.012639370746910572, -0.07154552638530731, + -0.025404062122106552, 0.02471778728067875, -0.035035762935876846, + 0.02645435929298401, 0.023452656343579292, 0.04376635700464249, + 0.010747642256319523, -0.021053681150078773, -0.01824294403195381, + 0.031085213646292686, -0.03658733889460564, -0.006558389402925968, + 0.012239541858434677, 0.030560065060853958, -0.00466069346293807, + -0.03072715923190117, 0.019818389788269997, 0.0128422686830163, + 0.009774924255907536, -0.0230408925563097, 0.04861801490187645, + -0.03312016278505325, -0.008855913765728474, -0.071014404296875, + 0.019925806671380997, 0.008121899329125881, 0.017240388318896294, + -0.016118479892611504, -0.02033757045865059, -0.008867849595844746, + -0.024276185780763626, -0.038496967405080795, -0.0337885357439518, + 0.03429577872157097, -0.02477746270596981, 0.015945419669151306, + 0.07845599949359894, 0.0008473987691104412, -0.034838832914829254, + 0.012925814837217331, 0.031610362231731415, 0.010437327437102795, + 0.02356604114174843, 0.007173051591962576, -0.02039724588394165, + 0.029366547241806984, 0.07196921855211258, 0.03641427680850029, + 0.010377652011811733, -0.05049780756235123, -0.017210550606250763, + -0.018732286989688873, 0.03511931002140045, 0.008265122771263123, + 0.016804754734039307, -0.05049780756235123, 0.012018740177154541, + -0.009028973989188671, 0.0064629074186086655, -0.02353620156645775, + 0.02248590625822544, 0.01842794008553028, -0.00395054928958416, + 0.0018260846845805645, 0.03070925548672676, -0.0533025786280632, + 0.019424527883529663, 0.0476512610912323, -0.0015635105082765222, + -0.00694031547755003, -0.02583969570696354, 0.010789415799081326, + 0.014358039014041424, 0.02073740027844906, -0.043706681579351425, + -0.1288284808397293, 0.023858454078435898, -0.023721197620034218, + 0.007256597746163607, -0.030583932995796204, 0.011236985214054585, + -0.03825826570391655, -0.00011338433978380635, 0.02393006533384323, + 0.016649596393108368, -0.006325652822852135, 0.0010741675505414605, + -0.02848334237933159, -0.05317725986242294, -0.005633411929011345, + -0.0004117641947232187, -0.016655564308166504, -0.0066240327432751656, + -0.018947120755910873, -0.032887428998947144, -0.009339289739727974, + -0.00281073828227818, -0.0724346935749054, 0.006809028331190348, + 0.028817525133490562, 0.011236985214054585 + ] + }, + "mountains": { + "prompt": "Photo of a beautiful mountain range", + "vector": [ + 0.028191275894641876, 0.017811166122555733, 0.016408821567893028, + -0.01227052602916956, -0.007121717091649771, 0.0009211487486027181, + 0.01934412308037281, 0.01587950438261032, 0.060747694224119186, + 0.03486616536974907, -0.007527296897023916, -0.034934911876916885, + 0.016594424843788147, -0.015742018818855286, 0.016945011913776398, + 0.5609795451164246, -0.02234816737473011, 0.01763930916786194, + 0.022746874019503593, 0.007767895702272654, -0.02878246083855629, + -0.020210279151797295, -0.026953911408782005, 0.0035608585458248854, + 0.009871414862573147, -0.014834619127213955, 0.007898506708443165, + 0.005911849904805422, -0.019674086943268776, 0.06762880831956863, + 0.012765471823513508, 0.006894866470247507, -0.030885977670550346, + 0.023221196606755257, -0.004729480016976595, 0.04720543324947357, + -0.0018697944469749928, -0.016367575153708458, -0.026534583419561386, + -0.014552775770425797, 0.05517268180847168, 0.012256776914000511, + -0.013940966688096523, -0.0313534289598465, -0.017467455938458443, + -0.023915493860840797, -0.009713307023048401, -0.015418929047882557, + 0.03967126086354256, -0.010758192278444767, 0.015714522451162338, + 0.019323501735925674, -0.03575294464826584, 0.01787990890443325, + -0.010187629610300064, -0.022086946293711662, -0.01875981315970421, + 0.014277804642915726, -0.012524873949587345, 0.05602509155869484, + -0.006626770831644535, 0.01602386310696602, 0.005052569787949324, + -0.004777600057423115, 0.011748083867132664, 0.038976967334747314, + -0.006778004579246044, -0.011775580234825611, 0.03699030727148056, + 0.0200384221971035, 0.02092519775032997, 0.05463649332523346, + -0.029153671115636826, -0.008297212421894073, 0.006503034848719835, + 0.018780436366796494, -0.024458561092615128, -0.0017666807398200035, + 0.05913224816322327, 0.04763163626194, 0.014085326343774796, + -0.019818445667624474, 0.04759039357304573, 0.006757382303476334, + -0.0011342503130435944, -0.04072989523410797, 0.006331178825348616, + -0.007616662420332432, -0.027950676158070564, -0.00705984840169549, + 0.04116297513246536, 0.040283069014549255, -0.0158382598310709, + -0.00376708572730422, -0.01117752119898796, 0.02269187942147255, + -0.009974528104066849, 0.03630288317799568, 0.002378488425165415, + 0.009369594044983387, -0.022526897490024567, -0.009837043471634388, + 0.03181400150060654, -0.03449495509266853, 0.03248080238699913, + -0.001739183790050447, -0.07973435521125793, 0.02408735267817974, + 0.028761839494109154, -0.0025847158394753933, 0.002447230974212289, + -0.011748083867132664, 0.011225640773773193, -0.016511933878064156, + 0.006723010912537575, -0.007499800529330969, 0.009665187448263168, + 0.010256372392177582, -0.022224431857466698, 0.01949535682797432, + -0.012545496225357056, -0.03436434641480446, 0.02019652910530567, + 0.012353016994893551, 0.00017185609613079578, 0.006846747361123562, + 0.08900771290063858, -0.05629318580031395, 0.03059038706123829, + 0.015460175462067127, 0.016078857704997063, -0.007726650685071945, + -0.021275784820318222, 0.0033958766143769026, -0.0042620315216481686, + -0.015858881175518036, -0.035587962716817856, 0.010373233817517757, + -0.038317035883665085, -0.005774364806711674, 0.011074407026171684, + -0.005361910443753004, 0.03155965358018875, -0.030576638877391815, + 0.008214721456170082, -0.061325132846832275, 0.016003241762518883, + 0.007149213925004005, -0.03575294464826584, -0.006083706393837929, + -0.03567732870578766, 0.004674485884606838, 0.031195318326354027, + 0.002089770045131445, -0.0007630411419086158, -0.024018609896302223, + 0.0002337243058718741, 0.020258398726582527, -0.014724631793797016, + -0.024836644530296326, 0.0029971706680953503, 0.013466645032167435, + 0.029263656586408615, -0.02874808944761753, -0.046380527317523956, + -0.0888427272439003, -0.01563890650868416, -0.5659977793693542, + 0.007719775661826134, -0.0003299637173768133, -0.021715737879276276, + 0.07681968063116074, -0.004983827006071806, -0.008393452502787113, + -0.0009898912394419312, -0.013535386882722378, -0.009802672080695629, + -0.002502224873751402, -0.003457744838669896, -0.034274980425834656, + 0.003457744838669896, 0.008757786825299263, -0.027648210525512695, + 0.015006475150585175, -0.018711691722273827, 0.01304044108837843, + -0.021667618304491043, -0.003925193566828966, -0.0010723820887506008, + 0.021970083937048912, -0.026108378544449806, -0.014133445918560028, + -0.02007966674864292, -0.08935829997062683, -0.006619896739721298, + -0.03663284704089165, 0.005801862105727196, -0.04136919975280762, + -0.0002337243058718741, 0.03229519724845886, 0.016120102256536484, + -0.01900041103363037, 0.040063098073005676, 0.00628993334248662, + 0.01516458299010992, 0.017556820064783096, 0.015006475150585175, + 0.01216053869575262, 0.006152448244392872, 0.02019652910530567, + 0.011218766681849957, -0.003004044760018587, 0.027263253927230835, + -0.015515169128775597, -0.011363125406205654, 0.0010036396561190486, + 0.016285084187984467, -0.020141534507274628, 0.008297212421894073, + -0.0020760218612849712, 0.03229519724845886, -0.001251112436875701, + 0.01255924440920353, 0.02153700776398182, -0.007980997674167156, + -0.004798222333192825, 0.04441449046134949, -0.012380514293909073, + -0.04238659143447876, -0.12834900617599487, -0.01211929228156805, + 0.022018203511834145, 0.0024884764570742846, -0.04711607098579407, + -0.022169437259435654, -0.04123859107494354, -0.026768306270241737, + -0.02793005295097828, 0.022685006260871887, 0.001230489695444703, + 0.018966039642691612, 0.006475538015365601, -0.04930895194411278, + -0.01402345858514309, -0.016890017315745354, 0.04635303094983101, + -0.02617024816572666, -0.020175905898213387, -0.09306351840496063, + 0.025744043290615082, 0.0014779624762013555, -0.013769110664725304, + -0.031119702383875847, 0.0618063323199749, -0.02091832458972931, + -0.019447235390543938, 0.004681359976530075, -0.037313397973775864, + 0.019055403769016266, 0.009005260653793812, 0.011493736878037453, + 0.0022135067265480757, 0.00628993334248662, 0.029682986438274384, + -0.009142744354903698, 0.0007767896167933941, -0.0029078051447868347, + 0.03637849912047386, 0.005781239364296198, -0.017591191455721855, + 0.005788113921880722, 0.03366317227482796, -0.008194099180400372, + 0.014112822711467743, 0.012263651937246323, 0.07155400514602661, + 0.014820870012044907, 0.015212701633572578, 0.023399926722049713, + -0.039863742887973785, -0.007905380800366402, -0.011919938959181309, + -0.00044682587031275034, -0.008104734122753143, 0.01003639679402113, + -0.00793975219130516, 0.014188440516591072, 0.017268100753426552, + 0.029717355966567993, -0.007334818597882986, -0.018010521307587624, + -0.0061180773191154, -0.00934209767729044, -0.01255924440920353, + -0.014167816378176212, 0.005836233496665955, 0.010778814554214478, + 0.024025484919548035, 0.022300047799944878, -0.0062211910262703896, + 0.004825719632208347, 0.013452896848320961, 0.006468663923442364, + 0.006908615585416555, -0.0013336034025996923, -0.006303681991994381, + -0.001196118537336588, -0.008785284124314785, 0.0030796611681580544, + -0.009314601309597492, 0.12067047506570816, 0.002900931052863598, + 0.005478772800415754, 0.000006874244263599394, 0.03638537600636482, + 0.03177963197231293, -0.05441651493310928, 0.019323501735925674, + -0.05815610662102699, 0.01652568206191063, -0.01169308926910162, + 0.0001099879082175903, -0.005547515116631985, 0.03231582045555115, + -0.048649027943611145, 0.013315411284565926, -0.04281279072165489, + 0.02011403813958168, 0.009280229918658733, 0.006186820100992918, + -0.012724226340651512, 0.021688241511583328, -0.01447028387337923, + 0.03657785430550575, -0.02612212672829628, 0.03006106987595558, + -0.011143149808049202, -0.007589165586978197, 0.008352206088602543, + 0.0020004052203148603, 0.01792115531861782, -0.0023234945256263018, + 0.010118887759745121, -0.005774364806711674, 0.0017666807398200035, + 0.00853781122714281, 0.007974122650921345, 0.0260465107858181, + -0.025668427348136902, -0.00188354286365211, -0.0027565720956772566, + -0.02824626863002777, 0.0030590386595577, -0.02823939360678196, + 0.017164988443255424, -0.008874649181962013, 0.011129401624202728, + 0.049879513680934906, 0.023399926722049713, 0.018347356468439102, + 0.03853701055049896, -0.011012539267539978, -0.01439466793090105, + -0.020712098106741905, -0.025070369243621826, 0.015975743532180786, + 0.02597089484333992, -0.008393452502787113, -0.0273594930768013, + 0.013665997423231602, -0.008056613616645336, 0.0016566927079111338, + -0.01768055558204651, -0.01298544742166996, 0.03688719496130943, + -0.032879509031772614, 0.02878246083855629, 0.023255567997694016, + -0.11043473333120346, 0.015996364876627922, -0.030157307162880898, + 0.015267697162926197, 0.0006599274347536266, -0.00775414751842618, + 0.007486051879823208, -0.010187629610300064, -0.016429442912340164, + -0.022671258077025414, 0.0036433495115488768, 0.022588767111301422, + -0.08365267515182495, 0.016553180292248726, -0.004014558624476194, + 0.0021310157608240843, 0.010118887759745121, -0.014710881747305393, + -0.010703197680413723, -0.008702793158590794, -0.0023166202008724213, + 0.006049334537237883, 0.007369189988821745, 0.004853216465562582, + 0.01109503023326397, 0.004097049590200186, 0.03899071365594864, + 0.015398306772112846, -0.05756492167711258, 0.024266080930829048, + -0.014676511287689209, 0.005547515116631985, -0.03958189859986305, + -0.07155400514602661, -0.00853781122714281, -0.013975338079035282, + 0.011961184442043304, -0.006503034848719835, 0.017206232994794846, + -0.010297617875039577, 0.01186494529247284, 0.02049212157726288, + 0.03522362560033798, 0.0033408827148377895, -0.07305946946144104, + 0.016346951946616173, -0.014772751368582249, -0.022685006260871887, + -0.010029522702097893, 0.014140320010483265, -0.028528112918138504, + 0.04158230498433113, -0.0033958766143769026, 0.021055810153484344, + -0.01737808994948864, 0.024568548426032066, -0.016250712797045708, + 0.0015192078426480293, -0.03339507803320885, 0.026672067120671272, + -0.02617024816572666, -0.008585930801928043, 0.0057124970480799675, + 0.023839877918362617, 0.020835833624005318, 0.02408047765493393, + 0.05790863186120987, 0.01695876009762287, 0.04110110551118851, + -0.03364942595362663, 0.0041520437225699425, -0.008895271457731724, + -0.046909842640161514, -0.025902152061462402, -0.0009142744820564985, + -0.03301699459552765, 0.0013817230938002467, -0.018979787826538086, + 0.018773561343550682, 0.06643269956111908, 0.02223818004131317, + -0.011088155210018158, 0.005485646892338991, -0.009135870262980461, + 0.0015879502752795815, 0.018670447170734406, 0.03186212107539177, + 0.07482614368200302, 0.016044486314058304, 0.031380925327539444, + 0.019275380298495293, 0.003258391749113798, -0.019426614046096802, + 0.04843592271208763, 0.000653053168207407, 0.002935302210971713, + -0.02835625782608986, 0.022018203511834145, -0.012284274213016033, + 0.0032927629072219133, 0.005623131524771452, 0.01645006611943245, + -0.01730247400701046, -0.015027097426354885, 0.009727055206894875, + 0.04178165644407272, -0.003045290242880583, 0.0011686214711517096, + -0.060933299362659454, -0.013129807077348232, 0.01575576700270176, + -0.009424588643014431, -0.02685767039656639, 0.003175900550559163, + -0.0057606166228652, -0.0051900544203817844, -0.01298544742166996, + -0.004502630326896906, -0.05713871866464615, -0.0021378900855779648, + -0.00451637851074338, 0.0018560459138825536, -0.007444805931299925, + -0.011954310350120068, 0.001340477610938251, -0.020251523703336716, + -0.006063083186745644, -0.02438981831073761, 0.010318240150809288, + -0.02720825746655464, 0.0004399516328703612, -0.014387793838977814, + 0.01875981315970421, -0.018704818561673164 + ] + }, + "greenery": { + "prompt": "Photo of lush greenery", + "vector": [ + -0.0024188209790736437, 0.01026985514909029, -0.003033661050722003, + -0.026080025359988213, -0.0058240885846316814, 0.01385079137980938, + -0.00429712375625968, 0.006621353793889284, -0.027606990188360214, + -0.01551288552582264, 0.02874883823096752, -0.009891491383314133, + 0.050639841705560684, 0.016256099566817284, 0.01855330355465412, + 0.5217559337615967, -0.01582368277013302, 0.004567382857203484, + -0.0029998787213116884, -0.03520803526043892, -0.025161145254969597, + -0.03884977847337723, -0.025248978286981583, -0.023296354338526726, + -0.011242789216339588, -0.006371363997459412, 0.015810171142220497, + -0.049180444329977036, -0.019958652555942535, 0.043504998087882996, + -0.018661407753825188, -0.01766820438206196, 0.03996460139751434, + 0.00032431120052933693, 0.03405267745256424, 0.017087146639823914, + 0.01268867589533329, 0.01987757533788681, -0.032836511731147766, + 0.015175062231719494, 0.012208965606987476, -0.013506210409104824, + 0.013877816498279572, -0.0033444594591856003, 0.06924044340848923, + -0.012499494478106499, 0.024073351174592972, -0.01617502234876156, + 0.016803374513983727, 0.04507249966263771, -0.009715823456645012, + 0.030127162113785744, -0.013742687180638313, -0.036971479654312134, + -0.04819399490952492, -0.0032431120052933693, -0.012877857312560081, + -0.0001891815336421132, -0.021620746701955795, 0.01589125022292137, + -0.02408010885119438, -0.022796375676989555, -0.002952583134174347, + -0.0651325061917305, 0.04060646519064903, 0.027938058599829674, + -0.026154346764087677, -0.009945543482899666, 0.001378322602249682, + 0.06817292422056198, 0.0241882111877203, -0.009175305254757404, + -0.0001486426335759461, 0.014904802665114403, 0.022316664457321167, + -0.01551964320242405, -0.11554938554763794, 0.008310474455356598, + -0.06501764059066772, -0.010364445857703686, -0.03290407359600067, + 0.000945907726418227, 0.044180646538734436, 0.02203965000808239, + -0.0035809362307190895, 0.02187073789536953, -0.008040214888751507, + 0.0019053284777328372, -0.0007229437469504774, 0.04820075258612633, + 0.07287543267011642, 0.027829956263303757, 0.01050633192062378, + -0.004222802352160215, -0.02354634366929531, 0.022451795637607574, + -0.11383998394012451, 0.024654408916831017, 0.028411012142896652, + 0.0018242505611851811, -0.00024323341494891793, 0.005445725750178099, + 0.011060363613069057, -0.08840858936309814, 0.009080713614821434, + -0.019992433488368988, -0.0661730021238327, -0.012965692207217216, + -0.00035133716301061213, -0.007580774370580912, -0.042998261749744415, + -0.037802524864673615, 0.02318825200200081, 0.030681191012263298, + -0.0008648298680782318, 0.029519077390432358, -0.012708946131169796, + -0.007553748786449432, -0.000020269450033083558, + 0.0035539104137569666, -0.0003378241672180593, -0.03653906285762787, + 0.009688797406852245, -0.0014256180729717016, -0.000493223313242197, + 0.005526803433895111, 0.03970109671354294, 0.046153537929058075, + 0.025924628600478172, 0.037998463958501816, -0.03274191915988922, + 0.018303314223885536, -0.03343108296394348, 0.0015607477398589253, + 0.06490277498960495, -0.007168628741055727, 0.0011215762933716178, + -0.021816685795783997, 0.011594126001000404, -0.07243625819683075, + 0.005648420192301273, 0.003675527172163129, 0.0022499088663607836, + -0.0335054025053978, -0.011431969702243805, -0.027282679453492165, + 0.033451348543167114, 0.0036417446099221706, 0.012567059136927128, + 0.01567504182457924, 0.013479184359312057, -0.008580734021961689, + 0.024309827014803886, 0.015620989724993706, 0.020796455442905426, + -0.017560100182890892, -0.011479265987873077, 0.0016620948445051908, + 0.014148076064884663, -0.04395768418908119, -0.061193469911813736, + -0.004128211177885532, -0.010053647682070732, -0.0012567059602588415, + 0.04771428927779198, -0.00859424751251936, -0.018499253317713737, + -0.5263165235519409, 0.0033444594591856003, -0.04816021770238876, + -0.004574139136821032, 0.07895626127719879, 0.005851114634424448, + -0.015526398085057735, 0.006472711451351643, 0.0330527164041996, + -0.004364688415080309, -0.0242219939827919, 0.02620839886367321, + 0.07151062041521072, 0.0031350082717835903, -0.018789781257510185, + 0.017310110852122307, -0.014188614673912525, -0.03284326568245888, + -0.01953299343585968, -0.040653761476278305, -0.03721471130847931, + -0.00692539568990469, -0.0022499088663607836, -0.008763158693909645, + -0.027154305949807167, -0.008472629822790623, -0.028451552614569664, + -0.016499333083629608, -0.026823241263628006, 0.023303112015128136, + -0.0004391714173834771, -0.016762835904955864, -0.009182061068713665, + 0.03387025371193886, -0.03335675969719887, 0.0240598376840353, + 0.022296395152807236, 0.011067119427025318, 0.006114617455750704, + 0.020776188001036644, 0.010323906317353249, -0.0014458874939009547, + -0.030167698860168457, 0.03322163224220276, 0.018411418423056602, + -0.011573855765163898, 0.003628231817856431, -0.010026621632277966, + -0.025181414559483528, 0.10141481459140778, -0.01784387417137623, + 0.007533479016274214, -0.025181414559483528, 0.00885774940252304, + -0.0226950291544199, 0.020303232595324516, -0.027255654335021973, + 0.0032160861883312464, -0.024073351174592972, -0.005979487672448158, + -0.023911194875836372, -0.028958288952708244, -0.12885290384292603, + -0.006067322101444006, 0.004905207082629204, -0.028329934924840927, + -0.02904612384736538, 0.00012837319809477776, -0.05159250646829605, + -0.025431403890252113, 0.005729498341679573, -0.009587449952960014, + 0.019505968317389488, 0.0365593321621418, -0.011965733021497726, + -0.04020783305168152, -0.01583719812333584, -0.0029390703421086073, + 0.053862687200307846, -0.02485034614801407, -0.04088348150253296, + -0.1935124397277832, -0.03687012940645218, 0.013898086734116077, + -0.0021350488532334566, -0.008040214888751507, -0.06161237135529518, + -0.0033849983010441065, 0.028910990804433823, 0.004648460540920496, + -0.1203937828540802, -0.01955326274037361, 0.03089739941060543, + -0.00010810373350977898, -0.03483642637729645, -0.00107428093906492, + -0.004526843782514334, 0.004243071656674147, -0.0019931625574827194, + 0.0381808876991272, -0.024809807538986206, 0.01412105094641447, + -0.00003378242035978474, -0.0023917951621115208, -0.00417550653219223, + -0.0034255371429026127, -0.019411375746130943, 0.013404862955212593, + -0.004513331223279238, 0.020404580980539322, 0.003418780630454421, + 0.02103968895971775, -0.017256058752536774, 0.016073673963546753, + -0.07371999323368073, -0.006222721189260483, -0.011040094308555126, + -0.00810778047889471, 0.024316584691405296, 0.009830683469772339, + 0.00512817082926631, 0.062058303505182266, -0.009290164336562157, + -0.04211992025375366, -0.012445442378520966, -0.012540033087134361, + -0.01118198037147522, 0.048356153070926666, 0.010938746854662895, + -0.02669486589729786, 0.024627381935715675, 0.04824129119515419, + -0.007965893484652042, -0.026674598455429077, 0.020343773066997528, + 0.017107415944337845, 0.004371444694697857, -0.013621071353554726, + -0.0003716066130436957, -0.005344378296285868, 0.011175223626196384, + -0.0035539104137569666, 0.006580814719200134, 0.004459279123693705, + -0.01477643009275198, 0.05755172669887543, -0.022093700245022774, + 0.016647975891828537, 0.010546870529651642, -0.027275923639535904, + 0.02605975791811943, 0.021080227568745613, -0.019505968317389488, + -0.035120200365781784, -0.005965975113213062, -0.016492577269673347, + 0.01226301770657301, -0.11921814829111099, 0.0170128270983696, + -0.010060404427349567, -0.04658595100045204, 0.03541748598217964, + -0.040309179574251175, -0.016729053109884262, -0.0359107106924057, + 0.030937936156988144, 0.009384755976498127, -0.01067524403333664, + 0.025627342984080315, -0.014452118426561356, -0.00844560470432043, + 0.050329048186540604, -0.053862687200307846, -0.0006148400134406984, + 0.01387106068432331, 0.00809426698833704, 0.013681878335773945, + 0.03153926506638527, -0.029667718335986137, 0.010094186291098595, + 0.005884896963834763, -0.028275884687900543, -0.006013270001858473, + 0.018107375130057335, 0.0240598376840353, -0.0007905085803940892, + 0.034295909106731415, 0.003891734639182687, 0.02855965495109558, + 0.00927665177732706, 0.02070862241089344, 0.02420172281563282, + -0.06411226838827133, -0.015107497572898865, -0.0016147996066138148, + -0.026593517512083054, 0.01109414640814066, -0.0091888178139925, + 0.02204640582203865, -0.01387106068432331, 0.017303355038166046, + -0.0324040949344635, 0.027302948758006096, 0.04524141550064087, + 0.008803698234260082, -0.0058173323050141335, -0.024005787447094917, + 0.010540113784372807, 0.037829551845788956, 0.03985649719834328, + 0.00512817082926631, -0.02721511386334896, -0.018445199355483055, + -0.01351296715438366, 0.03370809555053711, 0.00960096251219511, + 0.003080956405028701, 0.007202411536127329, -0.004655216820538044, + -0.025154387578368187, 0.007864546962082386, 0.0020134320948272943, + 0.011290083639323711, -0.02401929907500744, -0.001033741980791092, + 0.02604624256491661, 0.0003378241672180593, 0.015039931982755661, + -0.01285083219408989, 0.024316584691405296, -0.014073754660785198, + -0.022255856543779373, 0.011195492930710316, -0.016965530812740326, + 0.011918436735868454, -0.007188898511230946, -0.041951004415750504, + -0.03319460153579712, 0.00554707320407033, -0.012661649845540524, + -0.024303071200847626, -0.008790184743702412, -0.022485578432679176, + -0.018066836521029472, -0.0007769956137053668, 0.023654447868466377, + -0.01251976378262043, 0.001331027247942984, 0.016769591718912125, + -0.0048849377781152725, -0.019978921860456467, -0.021262653172016144, + -0.004830885678529739, 0.03161358833312988, 0.029438000172376633, + -0.02486385963857174, 0.031005503609776497, -0.002533681457862258, + -0.007790225557982922, -0.015769632533192635, 0.005743010900914669, + 0.0035403973888605833, -0.001141845714300871, -0.027174577116966248, + 0.017316866666078568, 0.002533681457862258, 0.015303434804081917, + -0.013992678374052048, 0.016810130327939987, -0.04673459380865097, + 0.015154791995882988, -0.028323179110884666, -0.022086946293711662, + -0.027755632996559143, -0.02791779115796089, 0.05092361569404602, + 0.05913950130343437, 0.008830724284052849, -0.02551248110830784, + 0.002256665611639619, -0.0037498483434319496, 0.01850600726902485, + 0.018397903069853783, -0.046849459409713745, -0.018735729157924652, + -0.03820791468024254, 0.002466116566210985, 0.008425334468483925, + -0.029140714555978775, -0.022086946293711662, 0.058990854769945145, + 0.00925638247281313, 0.01327649038285017, -0.0157155804336071, + 0.04408605396747589, -0.0026958370581269264, 0.022749079391360283, + 0.029269086197018623, 0.037322815507650375, 0.03983622416853905, + -0.018641138449311256, 0.011668447405099869, 0.006871343590319157, + -0.008310474455356598, 0.048998016864061356, -0.005040336865931749, + -0.00425658468157053, 0.0035876925103366375, 0.010067160241305828, + -0.014046729542315006, -0.014843993820250034, 0.007655095774680376, + -0.01109414640814066, 0.005324108991771936, -0.0329243466258049, + 0.03744443133473396, -0.010560383088886738, 0.023167982697486877, + -0.01920192502439022, -0.09798252582550049, 0.04036998748779297, + -0.011472509242594242, -0.0017972246278077364, -0.03458644077181816, + -0.019411375746130943, 0.016587166115641594, -0.008067241869866848, + -0.037457942962646484, 0.0022228830493986607, -0.029242059215903282, + -0.044153619557619095, 0.010648217983543873, -0.011398187838494778, + -0.022147752344608307, 0.014594004489481449, 0.004871424287557602, + -0.0006486224010586739, 0.0050065540708601475, -0.01685742661356926, + -0.033640533685684204, -0.006567301694303751, -0.020323501899838448, + 0.026296233758330345, 0.028992071747779846, 0.04254557564854622 + ] + }, + "beach": { + "prompt": "Photo of a beautiful beach", + "vector": [ + -0.014337359927594662, 0.008085873909294605, 0.004238295368850231, + -0.04446237161755562, -0.008224942721426487, 0.03859497979283333, + 0.022509323433041573, 0.008933532051742077, 0.003609173698350787, + 0.029283974319696426, 0.0250787902623415, -0.01219172216951847, + -0.04351537674665451, 0.011549355462193489, -0.0035694397520273924, + 0.5811630487442017, 0.0009138825116679072, 0.005066087935119867, + -0.02306560054421425, 0.028462804853916168, -0.015257864259183407, + -0.03599240258336067, 0.03354213759303093, -0.04589279368519783, + 0.017860442399978638, -0.0337805412709713, 0.03242295980453491, + -0.018317384645342827, 0.015886986628174782, 0.015211507678031921, + -0.011284462176263332, -0.02511190064251423, 0.021277567371726036, + -0.027005890384316444, -0.0029005836695432663, 0.00967523455619812, + -0.007960048504173756, -0.016794247552752495, -0.037469182163476944, + -0.022012649103999138, 0.006648826412856579, 0.018112091347575188, + -0.003728376002982259, -0.03072764351963997, 0.02964157983660698, + 0.014211534522473812, -0.0009072601678781211, -0.003980024252086878, + 0.018098846077919006, -0.00018542543693911284, 0.02858862839639187, + 0.05057479068636894, 0.013999620452523232, 0.010562627576291561, + 0.006728294305503368, -0.02783368155360222, -0.0021125255152583122, + 0.016271082684397697, -0.030118387192487717, 0.030005808919668198, + -0.000715212372597307, -0.02084711566567421, -0.01541017834097147, + 0.0136089026927948, -0.004278029780834913, 0.028621740639209747, + 0.00102646229788661, -0.030999161303043365, 0.01017853245139122, + -0.03440966084599495, 0.04332995042204857, 0.0114698875695467, + -0.004337630700320005, -0.002880716696381569, 0.03437655046582222, + 0.02301924303174019, -0.05721036717295647, 0.006158773321658373, + 0.04152205213904381, 0.026813842356204987, 0.004033003468066454, + -0.035277191549539566, 0.04628351330757141, 0.023575520142912865, + 0.007695155218243599, 0.002331062685698271, -0.008575926534831524, + 0.015383689664304256, -0.016853848472237587, -0.009536165744066238, + 0.027442965656518936, 0.02937006577849388, 0.029072057455778122, + -0.006390554830431938, -0.028582006692886353, 0.010105686262249947, + -0.05311776325106621, 0.010145420208573341, 0.007761379238218069, + -0.001927100121974945, -0.02185371145606041, -0.012906935065984726, + 0.0002119147829944268, 0.006741539109498262, -0.033409688621759415, + -0.03160179406404495, -0.0758257582783699, 0.02498607710003853, + 0.01603267714381218, -0.023515919223427773, -0.024443047121167183, + 0.00582103431224823, 0.008443479426205158, -0.04087306559085846, + 0.006907097529619932, -0.026899931952357292, -0.0013046003878116608, + 0.006681937724351883, -0.04248891398310661, 0.008714995346963406, + -0.02203913778066635, 0.0157479178160429, 0.02273448370397091, + 0.013747971504926682, 0.0025628444273024797, 0.010734807699918747, + 0.062263209372758865, -0.02872769720852375, -0.008344144560396671, + 0.009390474297106266, -0.0065031349658966064, 0.016449885442852974, + 0.009642122313380241, -0.010681829415261745, -0.024760916829109192, + -0.013589034788310528, -0.026330411434173584, 0.004072736948728561, + -0.0035628171171993017, 0.005973347928375006, 0.006417044438421726, + 0.015886986628174782, 0.0069137196987867355, -0.04590604081749916, + 0.006152151618152857, -0.01846969872713089, -0.012642040848731995, + 0.013456588611006737, -0.03586657717823982, 0.00468861497938633, + 0.004417098592966795, -0.00984079297631979, 0.03658178821206093, + -0.040555190294981, -0.009370607323944569, -0.0043641203083097935, + 0.012165232561528683, 0.006079305429011583, 0.021754376590251923, + -0.017310788854956627, -0.02872769720852375, 0.0004238295659888536, + 0.020304085686802864, 0.00770177785307169, -0.03493282571434975, + -0.055217042565345764, 0.019814031198620796, -0.5854675769805908, + 0.01785382069647312, -0.03142298758029938, -0.011244728229939938, + 0.0694948062300682, -0.009244781918823719, 0.008522948250174522, + 0.00046356357051990926, 0.01653597503900528, -0.025747647508978844, + 0.012787732295691967, -0.017727995291352272, 0.014509540051221848, + 0.012721509672701359, 0.01629757136106491, 0.03692615032196045, + 0.010701696388423443, 0.002225105185061693, 0.006364066153764725, + -0.008264676667749882, 0.01925775595009327, -0.009211670607328415, + 0.032257404178380966, 0.027409851551055908, -0.0293568205088377, + 0.05248864367604256, -0.03565466031432152, -0.0049733747728168964, + -0.04603186249732971, -0.00925140455365181, -0.07718995958566666, + -0.003615796100348234, 0.0067746504209935665, 0.023946372792124748, + 0.024979455396533012, 0.04461468383669853, 0.020747780799865723, + 0.013681747950613499, 0.017330655828118324, 0.007840846665203571, + 0.0059137470088899136, 0.02589995786547661, 0.0298601184040308, + 0.025058923289179802, -0.010589116252958775, -0.006079305429011583, + -0.015032704919576645, 0.011099036782979965, 0.005119066219776869, + -0.025025812909007072, -0.011979807168245316, 0.006324331741780043, + -0.0008277921588160098, -0.019184909760951996, -0.00837725680321455, + 0.002331062685698271, -0.0025495998561382294, -0.016734644770622253, + -0.011046057567000389, 0.03548910468816757, 0.00333765777759254, + 0.004721726290881634, -0.11840076744556427, -0.02340996079146862, + 0.052018459886312485, -0.035422880202531815, -0.06260757148265839, + -0.0006357443635351956, 0.015900231897830963, -0.02202589437365532, + 0.005675342865288258, -0.003993269056081772, -0.0024039081763476133, + -0.01565520465373993, 0.0036422854755073786, -0.06417044252157211, + 0.029144903644919395, -0.05456143245100975, 0.0021323924884200096, + 0.014939992688596249, 0.019516026601195335, -0.10220915079116821, + -0.007814357057213783, 0.007979915477335453, -0.021780867129564285, + -0.010748052969574928, 0.03157530352473259, 0.010489782318472862, + -0.01360228005796671, 0.0098341703414917, -0.08183883875608444, + -0.011363930068910122, 0.00719847995787859, 0.04788611829280853, + -0.01499959360808134, 0.002337685087695718, 0.008642150089144707, + 0.006039571017026901, 0.008940154686570168, -0.0023111954797059298, + 0.034244101494550705, -0.0005231646355241537, -0.019244510680437088, + 0.006880608387291431, -0.009019623510539532, -0.014045977033674717, + 0.02710522525012493, -0.005874013062566519, 0.039998915046453476, + 0.031442854553461075, 0.010701696388423443, 0.0038144660647958517, + -0.005827656481415033, 0.016734644770622253, -0.018098846077919006, + -0.015377067029476166, 0.01251621637493372, 0.011516244150698185, + 0.00788058154284954, -0.0015496269334107637, 0.014542651362717152, + 0.028078708797693253, 0.001377446111291647, -0.030767379328608513, + -0.008383878506720066, -0.0006291220197454095, 0.0060130818746984005, + -0.004278029780834913, 0.020853739231824875, -0.025933071970939636, + -0.016277704387903214, 0.02117823250591755, -0.00002648934787430335, + 0.047144416719675064, -0.015939965844154358, 0.0052647581323981285, + 0.010754674673080444, 0.017251187935471535, 0.0027615143917500973, + 0.027284028008580208, -0.011264595203101635, 0.015608848072588444, + 0.017191587015986443, 0.12382446229457855, 0.007834224961698055, + 0.01678762398660183, 0.014423450455069542, 0.052773404866456985, + 0.02674761787056923, -0.04421072080731392, 0.002198615809902549, + -0.037575140595436096, 0.008469969034194946, -0.010211643762886524, + -0.015112172812223434, -0.02072129212319851, -0.01097321230918169, + -0.043634578585624695, 0.009483186528086662, -0.03866782784461975, + -0.0001059573914972134, -0.00037747318856418133, -0.01737038977444172, + -0.007887203246355057, 0.016588954254984856, -0.01361552532762289, + -0.004311141092330217, -0.011873849667608738, 0.016747890040278435, + 0.0027615143917500973, 0.001258244039490819, -0.0365486778318882, + 0.03971415385603905, 0.027118470519781113, 0.009536165744066238, + 0.022727860137820244, -0.004562790039926767, -0.02871445193886757, + -0.018198180943727493, -0.011430153623223305, 0.015165151096880436, + -0.03127729892730713, -0.00659584766253829, -0.00333765777759254, + 0.005330981221050024, -0.004648880567401648, -0.023688098415732384, + 0.00942358560860157, -0.015641959384083748, 0.032118331640958786, + -0.01956900581717491, 0.03055546246469021, 0.0069137196987867355, + 0.017946533858776093, -0.005701832007616758, -0.0027350252494215965, + 0.007748133968561888, -0.004423721227794886, 0.01821804977953434, + 0.004940263461321592, -0.026946287602186203, -0.021277567371726036, + 0.0175094585865736, 0.011297707445919514, -0.003609173698350787, + -0.02441655658185482, -0.015489645302295685, -0.02902570180594921, + -0.05147542431950569, 0.017708130180835724, -0.013231429271399975, + -0.03809168189764023, -0.021376904100179672, -0.022237807512283325, + -0.015834007412195206, -0.00043045193888247013, -0.023217912763357162, + 0.03719104453921318, -0.009774569422006607, -0.009046112187206745, + 0.0021390148904174566, 0.004907151684165001, 0.02403908409178257, + -0.022251052781939507, -0.0006357443635351956, 0.01812533661723137, + 0.005357470363378525, 0.02306560054421425, 0.01927100121974945, + -0.010211643762886524, -0.039939314126968384, 0.01617174781858921, + -0.013112226501107216, -0.024363579228520393, 0.009158692322671413, + 0.0050131091848015785, -0.029634958133101463, 0.013648636639118195, + 0.0024039081763476133, 0.0124764833599329, 0.01138379704207182, + 0.0015628715045750141, -0.008569303900003433, 0.01697305031120777, + -0.03660827875137329, 0.01063547283411026, -0.002880716696381569, + -0.0035760619211941957, 0.014343982562422752, 0.005681965034455061, + -0.0039998916909098625, -0.010132175870239735, -0.005973347928375006, + 0.07218346744775772, -0.028707832098007202, -0.04050883278250694, + 0.010820899158716202, 0.01771475002169609, 0.0035429501440376043, + -0.016555842012166977, 0.0013906907988712192, 0.007092522922903299, + 0.014151934534311295, -0.009880526922643185, 0.02850916050374508, + 0.01268839742988348, 0.018979618325829506, -0.018429962918162346, + -0.012761243619024754, 0.00421180622652173, 0.013238051906228065, + -0.0040992265567183495, -0.0006092549883760512, 0.010463292710483074, + 0.008814330212771893, -0.013502945192158222, 0.01138379704207182, + 0.027277406305074692, 0.007860713638365269, 0.013787705451250076, + -0.022217940539121628, 0.013622147962450981, -0.02139677107334137, + -0.05273367092013359, 0.030330302193760872, -0.006655448582023382, + -0.00335090234875679, -0.03293950483202934, -0.001927100121974945, + -0.03336995840072632, 0.02331724762916565, 0.002284706337377429, + -0.024913230910897255, -0.008145473897457123, -0.029648203402757645, + -0.0158737413585186, 0.03517785295844078, 0.04156840965151787, + 0.06958751380443573, 0.024350333958864212, 0.014291003346443176, + -0.014973104000091553, -0.02298613078892231, -0.003072764491662383, + 0.028734320774674416, 0.034634821116924286, 0.004953507799655199, + 0.0064038001000881195, 0.001430424745194614, -0.04593915119767189, + 0.009648744948208332, 0.022330520674586296, -0.0023045733105391264, + -0.02199278026819229, 0.006549491547048092, 0.020747780799865723, + 0.006132283713668585, 0.022549057379364967, -0.012582440860569477, + -0.10984470695257187, -0.00737066101282835, 0.006854118779301643, + 0.028151554986834526, -0.029807137325406075, 0.007483240682631731, + 0.02718469314277172, 0.01227119006216526, -0.0034237480722367764, + -0.00837725680321455, -0.024476157501339912, 0.006834251806139946, + -0.0037879766896367073, -0.003827710635960102, 0.012456615455448627, + -0.04129689559340477, 0.011277839541435242, -0.00726470397785306, + 0.008728240616619587, -0.017986267805099487, 0.013675126247107983, + 0.022820573300123215, 0.014423450455069542, -0.008238187991082668, + 0.02880716510117054, -0.06673328578472137 + ] + }, + "city": { + "prompt": "Beautiful photo showing a metropolitan city", + "vector": [ + -0.02022508904337883, 0.0024308236315846443, -0.024840475991368294, + -0.01621343567967415, -0.018501268699765205, 0.014394289813935757, + -0.004027541261166334, 0.010938706807792187, 0.018445663154125214, + 0.0036859549582004547, 0.0027088590431958437, -0.01409242209047079, + -0.05214355140924454, 0.004583612084388733, 0.031870801001787186, + 0.5370293259620667, -0.007220976520329714, -0.005624258890748024, + 0.015101294033229351, -0.03704225644469261, -0.0075943381525576115, + -0.006609298288822174, -0.03983850032091141, 0.004742489196360111, + 0.011693374253809452, -0.02916988544166088, 0.017015766352415085, + -0.018954068422317505, -0.041300173848867416, 0.004543892573565245, + -0.0018429774791002274, 0.006267712451517582, 0.03637497127056122, + 0.04597910866141319, -0.03643852472305298, 0.003113996470347047, + 0.007316302508115768, -0.012535424903035164, -0.04145904630422592, + -0.01633259281516075, 0.024808701127767563, -0.03063950128853321, + -0.0009453203529119492, -0.043651554733514786, 0.0011359731433913112, + -0.06291543692350388, -0.010231702588498592, -0.005346223711967468, + 0.016547076404094696, 0.0009691519662737846, 0.04871974512934685, + 0.023418523371219635, -0.0007943868404254317, 0.05904677137732506, + -0.012170006521046162, -0.010048992931842804, -0.009111616760492325, + -0.024419451132416725, 0.0007864429499022663, 0.049530018121004105, + 0.020924149081110954, 0.013679341413080692, 0.021392837166786194, + -0.024562440812587738, -0.07822327315807343, 0.024403564631938934, + 0.023760110139846802, 0.003431751159951091, -0.0020177424885332584, + 0.037240855395793915, 0.027882976457476616, 0.0327446274459362, + -0.044167909771203995, 0.025261500850319862, -0.006069115363061428, + 0.004488285630941391, -0.02721569500863552, 0.0396399050951004, + 0.07103406637907028, 0.08251295983791351, 0.014576999470591545, + -0.003503246232867241, 0.03790019452571869, 0.02272740751504898, + 0.03453994169831276, -0.00006355094956234097, -0.05225476622581482, + -0.008023306727409363, 0.006402757950127125, -0.011296181008219719, + 0.02713625505566597, 0.031886689364910126, -0.0186760351061821, + 0.005568651482462883, 0.0008182184537872672, 0.013655510731041431, + -0.05238186940550804, -0.03210911527276039, -0.015625588595867157, + 0.0030583892948925495, -0.01081160455942154, 0.00936582125723362, + 0.025404492393136024, 0.0222031120210886, 0.011423283256590366, + -0.009977499023079872, -0.055519696325063705, 0.010906931944191456, + 0.026516633108258247, 0.011177022941410542, 0.01776248961687088, + -0.05265196040272713, -0.0033920318819582462, -0.05242953449487686, + 0.014608773402869701, 0.02619093470275402, -0.004813984502106905, + -0.0286058709025383, -0.0017079317476600409, -0.0365656279027462, + -0.02855820581316948, 0.012440097518265247, 0.03320537135004997, + 0.02806568518280983, 0.059038832783699036, 0.06320936232805252, + 0.01823117770254612, -0.026921769604086876, 0.00046074436977505684, + 0.0030425016302615404, -0.016960158944129944, -0.005465381313115358, + 0.0365656279027462, 0.016983991488814354, 0.006982659921050072, + -0.032752569764852524, -0.014719988219439983, -0.0045200614258646965, + -0.03741561993956566, -0.04035485163331032, 0.014457839541137218, + 0.00039719342021271586, 0.0050523001700639725, -0.011661598458886147, + -0.007729384116828442, -0.015403159894049168, 0.04833843931555748, + 0.01579241082072258, -0.0315609872341156, 0.0256110318005085, + 0.02179797552525997, -0.022997498512268066, 0.06011919677257538, + -0.024292349815368652, -0.006188273895531893, -0.058022018522024155, + -0.013401305302977562, 0.0030742769595235586, 0.01505363080650568, + 0.020582562312483788, -0.03634319826960564, -0.002605588873848319, + 0.03014698065817356, -0.020963868126273155, -0.009373764507472515, + -0.02958296611905098, -0.020940037444233894, -0.5408344864845276, + -0.026413362473249435, -0.008031250908970833, -0.04113335162401199, + 0.052993547171354294, 0.007681720890104771, 0.015959231182932854, + 0.03058389574289322, -0.041284285485744476, -0.023926932364702225, + 0.00962796900421381, -0.004385015461593866, 0.004933142103254795, + 0.03788430988788605, -0.015371385030448437, -0.00006355094956234097, + 0.021972740069031715, 0.042150165885686874, 0.024530665948987007, + -0.004964917898178101, -0.022830678150057793, -0.02320403978228569, + 0.017031654715538025, -0.009945723228156567, -0.0012710189912468195, + 0.00988217256963253, -0.06859530508518219, -0.026786724105477333, + -0.06705419719219208, -0.0025499816983938217, -0.0145690543577075, + 0.03293528035283089, 0.018080245703458786, 0.03054417483508587, + 0.0023196095135062933, -0.003821000689640641, 0.010891043581068516, + -0.0018112020334228873, 0.021964795887470245, -0.007729384116828442, + -0.014998022466897964, 0.029980158433318138, -0.063360296189785, + 0.016038671135902405, 0.027970358729362488, -0.047059476375579834, + 0.010001330636441708, 0.008960683830082417, -0.009786846116185188, + -0.06726867705583572, -0.03569180145859718, 0.004861647263169289, + -0.020113874226808548, 0.040672607719898224, 0.010954594239592552, + 0.015093350782990456, -0.020415741950273514, -0.02970212511718273, + 0.010334973223507404, 0.011026089079678059, -0.005536876618862152, + -0.027509616687893867, -0.08001858741044998, -0.0019462477648630738, + 0.06487756967544556, -0.021980684250593185, -0.03148949518799782, + -0.01865220256149769, -0.033689945936203, -0.014497559517621994, + -0.012559256516397, 0.0024546554777771235, -0.031441833823919296, + -0.05378793179988861, 0.0027882978320121765, -0.01620549149811268, + -0.021933021023869514, -0.060548167675733566, -0.0010247590253129601, + -0.051984675228595734, -0.016626516357064247, -0.06469486653804779, + 0.005656034220010042, 0.003034557681530714, -0.03561235964298248, + 0.0037415619008243084, 0.053057096898555756, -0.016904551535844803, + -0.008936851285398006, 0.006672849413007498, -0.09151336550712585, + 0.00577519228681922, 0.017873702570796013, 0.013663453981280327, + 0.016435863450169563, -0.027040928602218628, 0.026270372793078423, + -0.02910633571445942, 0.03319742530584335, -0.02999604493379593, + -0.03689132630825043, 0.021392837166786194, 0.01628493145108223, + 0.04634452611207962, 0.037510946393013, -0.018135851249098778, + -0.0022322270087897778, 0.022965723648667336, 0.04292071983218193, + -0.010620951652526855, 0.018985845148563385, 0.005894350353628397, + -0.018374167382717133, -0.04233287647366524, -0.008523771539330482, + -0.0110816964879632, -0.02910633571445942, 0.03639880567789078, + -0.03484180569648743, -0.004369127564132214, 0.02375216595828533, + 0.0009294326300732791, 0.006045283749699593, -0.012432154268026352, + -0.003963990602642298, 0.022910118103027344, -0.014672324992716312, + 0.003868664149194956, -0.0008817694033496082, -0.0002780354116111994, + 0.0354217104613781, 0.03109230101108551, -0.013369530439376831, + -0.02374422363936901, -0.013981208205223083, 0.04003709554672241, + 0.009810677729547024, -0.001469615614041686, 0.004067260771989822, + 0.001199524151161313, -0.0004130811430513859, -0.00815040897578001, + -0.006100891157984734, 0.058482762426137924, -0.0038051127921789885, + -0.015887737274169922, -0.007848542183637619, -0.0013425137149170041, + -0.0055527640506625175, -0.04345295950770378, 0.03109230101108551, + -0.04988749325275421, 0.003821000689640641, 0.05859397351741791, + 0.025380657985806465, 0.013925601728260517, 0.04548659175634384, + -0.05572623386979103, 0.029511472210288048, -0.0542169027030468, + 0.036287590861320496, 0.01865220256149769, -0.028296060860157013, + 0.01213028747588396, -0.010994314216077328, 0.043262310326099396, + 0.032760512083768845, -0.004837815649807453, 0.04880712926387787, + -0.01974051259458065, -0.014918585307896137, -0.02765260450541973, + 0.05876079201698303, -0.014434007927775383, -0.004281744826585054, + -0.010184039361774921, -0.01478353887796402, 0.011439170688390732, + -0.023005442693829536, -0.005187346134334803, 0.037828702479600906, + -0.019518084824085236, -0.004043429158627987, -0.01927182450890541, + -0.0241334717720747, -0.013957376591861248, -0.004575667902827263, + 0.016562964767217636, -0.03171192482113838, -0.013480745255947113, + -0.01922416314482689, -0.024300292134284973, -0.051079075783491135, + -0.028367552906274796, -0.003407919779419899, 0.015919512137770653, + -0.007348078303039074, -0.03191052004694939, 0.021829750388860703, + 0.00914339255541563, -0.008420499972999096, -0.001835033530369401, + -0.02218722365796566, -0.001850921311415732, 0.02117040939629078, + -0.014505504630506039, -0.001517278840765357, 0.04524827376008034, + 0.01832650601863861, 0.010422355495393276, -0.00021448444749694318, + -0.06717334687709808, 0.034690871834754944, 0.007975644432008266, + -0.01573680341243744, 0.002859792672097683, -0.0015887736808508635, + -0.011184967122972012, 0.026747005060315132, -0.04824311286211014, + -0.053875312209129333, -0.001835033530369401, -0.020383967086672783, + 0.0008023307309485972, -0.014195693656802177, 0.0006513972184620798, + 0.011296181008219719, 0.03794785961508751, 0.023378804326057434, + -0.03809085115790367, -0.04352445527911186, 0.023950763046741486, + -0.034253958612680435, 0.04487491399049759, 0.04095064103603363, + 0.0024784868583083153, -0.016928382217884064, 0.006227992940694094, + -0.019931165501475334, 0.010009273886680603, 0.01927976869046688, + 0.02655635215342045, -0.05090431123971939, 0.02217928133904934, + -0.004369127564132214, 0.027255410328507423, -0.017651276662945747, + 0.029868947342038155, 0.01428307592868805, -0.01884285733103752, + 0.04993515834212303, 0.020042380318045616, -0.001644380739890039, + 0.012575143948197365, -0.038750190287828445, -0.032323598861694336, + 0.031251177191734314, -0.03403947502374649, -0.026349812746047974, + 0.001644380739890039, 0.04008476063609123, -0.010295253247022629, + 0.0118204765021801, 0.0033920318819582462, 0.007578450720757246, + -0.001437840168364346, 0.03484180569648743, -0.0423567034304142, + 0.01086721196770668, -0.016110165044665337, -0.0004686882020905614, + 0.006672849413007498, -0.020431628450751305, -0.03305443376302719, + -0.00284390477463603, -0.03195818141102791, 0.03586656600236893, + 0.024276461452245712, 0.02707270160317421, 0.04344501718878746, + 0.01232888363301754, -0.001549054286442697, -0.0779770165681839, + -0.03746328502893448, -0.015299891121685505, -0.015363441780209541, + -0.008237791247665882, 0.010795717127621174, 0.014028871431946754, + -0.007228919770568609, 0.005187346134334803, 0.0533113032579422, + 0.013155045919120312, 0.00529856001958251, -0.06317758560180664, + -0.012519536539912224, 0.0317198671400547, 0.04698003828525543, + 0.08506294339895248, 0.020074155181646347, 0.0237203910946846, + -0.03108435869216919, 0.01625315472483635, -0.004607443697750568, + 0.05777575448155403, -0.01980406418442726, -0.04331791400909424, + -0.031322672963142395, 0.018866688013076782, -0.012908786535263062, + 0.013552239164710045, 0.002859792672097683, 0.038758132606744766, + -0.0037177305202931166, 0.001294850604608655, 0.0073798540979623795, + 0.022004514932632446, 0.027779707685112953, 0.008086858317255974, + -0.12062764167785645, -0.04426323622465134, 0.021035363897681236, + 0.04329408332705498, -0.025285333395004272, 0.030250251293182373, + -0.0031696034129709005, 0.0241334717720747, -0.02515028789639473, + 0.004528005141764879, -0.06219254434108734, 0.01232888363301754, + -0.008023306727409363, 0.02171853743493557, 0.03409508615732193, + 0.02372833527624607, -0.00884946994483471, -0.011065809056162834, + -0.00729247136041522, -0.04056933522224426, -0.005838742945343256, + 0.001421952387318015, -0.023307310417294502, -0.017389127984642982, + 0.014998022466897964, -0.024014314636588097 + ] + }, + "moon": { + "prompt": "Photo of a beautiful moon", + "vector": [ + 0.02025710977613926, 0.018633542582392693, 0.006263218354433775, + -0.03596823289990425, 0.01744708977639675, -0.014811917208135128, + 0.025377586483955383, 0.027844157069921494, 0.0332893468439579, + 0.03457571193575859, -0.019520258530974388, -0.03809135779738426, + -0.03466938063502312, 0.030654177069664, 0.018358785659074783, + 0.5304065346717834, -0.03515645116567612, 0.039383962750434875, + -0.004383628722280264, -0.007081246003508568, 0.0023042147513478994, + 0.026564037427306175, -0.028431138023734093, -0.023691575974225998, + 0.0031971761491149664, -0.05539482459425926, -0.001954523613676429, + -0.022355254739522934, 0.00783682893961668, 0.0347505584359169, + -0.004302449990063906, -0.02996104024350643, -0.03302083536982536, + 0.014168735593557358, -0.017503291368484497, 0.011108938604593277, + -0.00493314303457737, 0.021318672224879265, -0.002660150406882167, + -0.03516269475221634, 0.03971492499113083, 0.05899789184331894, + -0.04307445511221886, -0.0264141708612442, 0.018964501097798347, + -0.017497045919299126, 0.0006494264234788716, -0.012607615441083908, + 0.019201790913939476, -0.0244658924639225, 0.026663949713110924, + -0.008835946209728718, -0.015923436731100082, -0.011358717456459999, + -0.0240787323564291, 0.01980126090347767, 0.0007743160822428763, + 0.010103576816618443, 0.018927032127976418, 0.055344872176647186, + 0.0006993822753429413, -0.003534378483891487, -0.015892215073108673, + -0.0073934695683419704, -0.021100115031003952, 0.03495662659406662, + 0.037054769694805145, 0.007218623999506235, -0.01709739863872528, + -0.027057351544499397, 0.03955256566405296, 0.05627529323101044, + -0.01544885616749525, -0.010003664530813694, 0.016173215582966805, + 0.0055888136848807335, -0.01970134861767292, 0.030410639941692352, + 0.0495624765753746, 0.02790660224854946, -0.03941518813371658, + 0.027250930666923523, 0.012588880956172943, 0.010559423826634884, + -0.012207968160510063, 0.014861873351037502, 0.0033720219507813454, + 0.03432593494653702, -0.04916282743215561, -0.0025227719452232122, + -0.012726260349154472, 0.07261711359024048, 0.003134731436148286, + 0.033969998359680176, -0.01705993339419365, 0.019058167934417725, + -0.039565056562423706, -0.03470684587955475, -0.003278354648500681, + 0.007356002926826477, -0.011421162635087967, 0.026051988825201988, + -0.03685494884848595, -0.05667494237422943, -0.0015111652901396155, + -0.009273060597479343, -0.05809244140982628, 0.0107717365026474, + 0.020725445821881294, -0.024522092193365097, -0.018358785659074783, + -0.006744043901562691, -0.018227651715278625, -0.029786191880702972, + 0.04217524826526642, 0.0341198667883873, 0.03138478100299835, + 0.03781035542488098, -0.03432593494653702, 0.06708449870347977, + 0.03763550892472267, -0.005395234562456608, 0.01049073413014412, + 0.017034955322742462, 0.025102829560637474, 0.049081649631261826, + 0.0868607833981514, 0.02744450978934765, 0.02859349548816681, + 0.04921278357505798, -0.004989343229681253, 0.028019003570079803, + -0.002722595352679491, 0.02356044016778469, -0.0471833273768425, + -0.0012801194097846746, -0.011015270836651325, -0.02095649018883705, + -0.0052765896543860435, 0.016391772776842117, 0.006344396620988846, + -0.013800310902297497, -0.0002560238935984671, -0.037747908383607864, + -0.034457068890333176, 0.005901038181036711, -0.007918006740510464, + -0.027400799095630646, -0.06263842433691025, -0.03289594501256943, + 0.0012613859726116061, 0.0014362315414473414, -0.0148181626573205, + -0.022605035454034805, 0.012951061129570007, 0.034488290548324585, + 0.009497861377894878, 0.011833298951387405, 0.015105409547686577, + -0.014249914325773716, 0.005120477639138699, -0.03333306312561035, + 0.03558731824159622, -0.005526368971914053, -0.02112509310245514, + -0.06097739562392235, -0.003890313906595111, -0.5348338484764099, + -0.01283866073936224, -0.0047832755371928215, -0.025621119886636734, + 0.05942251905798912, 0.0257397647947073, -0.02598954737186432, + -0.012314124032855034, 0.01257639192044735, -0.026695171371102333, + -0.038309913128614426, 0.020163441076874733, 0.005008076783269644, + 0.016597840934991837, 0.026076968759298325, 0.011596008203923702, + 0.015142875723540783, -0.02873087301850319, -0.013787822797894478, + -0.012145522981882095, -0.033826377242803574, 0.011596008203923702, + 0.04476671293377876, 0.022286565974354744, -0.0026289280503988266, + 0.05146079882979393, -0.07879915088415146, -0.0283187385648489, + -0.03071662038564682, 0.0016610330203548074, -0.025077851489186287, + -0.008298920467495918, -0.007468403782695532, 0.03574967756867409, + -0.017940403893589973, 0.059191472828388214, 0.02376650832593441, + 0.0012176745804026723, 0.04235009849071503, 0.03725459426641464, + -0.027669312432408333, -0.049668632447719574, 0.022505123168230057, + 0.03690490499138832, 0.02529640682041645, -0.0446043536067009, + 0.014786939136683941, -0.015261520631611347, 0.01775307022035122, + 0.022018054500222206, -0.032989609986543655, 0.016010858118534088, + -0.03268988057971001, 0.0076120272278785706, -0.03703603893518448, + 0.0003684246039483696, -0.024853048846125603, 0.020937757566571236, + -0.027544422075152397, -0.025658588856458664, -0.022111721336841583, + -0.017084911465644836, -0.037017304450273514, -0.005195411387830973, + 0.05747423693537712, -0.042481228709220886, -0.011614741757512093, + -0.04056417569518089, -0.01143365167081356, -0.010852914303541183, + 0.03895933926105499, 0.013575509190559387, -0.010721780359745026, + 0.0158859696239233, -0.0040464261546730995, 0.00031846872298046947, + -0.0031971761491149664, -0.005083010531961918, -0.011458629742264748, + -0.018683498725295067, -0.039733655750751495, -0.10297779738903046, + -0.013163373805582523, -0.01376284472644329, -0.04125106707215309, + 0.02078164555132389, 0.10747382044792175, -0.0015611212002113461, + 0.02255507931113243, 0.007786872796714306, -0.025589898228645325, + 0.015536277554929256, 0.019538993015885353, -0.011439896188676357, + 0.006381863262504339, 0.0032596210949122906, 0.014568382874131203, + 0.005876060109585524, -0.011508584953844547, 0.018777165561914444, + -0.008967080153524876, 0.012089322321116924, 0.007849317975342274, + -0.0010365844937041402, 0.008342632092535496, -0.011221338994801044, + -0.033514149487018585, -0.02644539251923561, 0.045822031795978546, + 0.01873345486819744, 0.0034407111816108227, -0.04611552134156227, + -0.027506954967975616, -0.006256973836570978, 0.03975863382220268, + -0.007774383760988712, 0.002535260980948806, 0.014549649320542812, + -0.014768206514418125, 0.047795288264751434, -0.024172401055693626, + -0.014649561606347561, 0.011290028691291809, 0.00993497483432293, + 0.011908232234418392, 0.01636679470539093, 0.004002714529633522, + 0.005164189264178276, 0.0002872463082894683, -0.02679508365690708, + 0.10626864433288574, 0.06183912977576256, 0.03245258703827858, + 0.06080879643559456, -0.007705694064497948, -0.0011427407152950764, + -0.0320279635488987, -0.014268646948039532, -0.011271295137703419, + -0.039839811623096466, -0.054364487528800964, 0.02366659790277481, + 0.006981334183365107, 0.054327018558979034, 0.021874429658055305, + -0.03973989933729172, 0.00262268353253603, 0.011302517727017403, + -0.012982283718883991, -0.054183389991521835, 0.016141993924975395, + -0.0916752815246582, -0.013007261790335178, -0.0034094888251274824, + 0.00017484556883573532, -0.06137079745531082, 0.014193713665008545, + -0.008823457174003124, 0.009791351854801178, 0.005189166869968176, + 0.024478379637002945, 0.016323082149028778, 0.018720965832471848, + -0.04423592984676361, 0.006556709297001362, 0.0031222424004226923, + 0.057836420834064484, 0.0016235660295933485, 0.03446955606341362, + 0.005638769827783108, -0.008929613046348095, 0.019538993015885353, + 0.03247756510972977, 0.048176199197769165, 0.003877825103700161, + 0.049949631094932556, 0.004121359903365374, 0.020269596949219704, + -0.027269665151834488, 0.003521889680996537, 0.003290843451395631, + -0.06808985769748688, 0.0010990293230861425, -0.034525755792856216, + 0.014736983925104141, -0.00008117830293485895, -0.0181339830160141, + 0.018995722755789757, -0.001049073413014412, 0.03494413569569588, + -0.025277674198150635, 0.02605823427438736, 0.01771560311317444, + -0.055544693022966385, -0.006893911398947239, -0.004327428061515093, + -0.02199932001531124, 0.03206542879343033, -0.0031472202390432358, + 0.05089879408478737, -0.003315821522846818, -0.06154564023017883, + -0.020163441076874733, -0.0001998235093196854, -0.023429308086633682, + -0.010084843263030052, -0.0064318194054067135, 0.05909780412912369, + -0.015130387619137764, 0.026332993060350418, -0.008698566816747189, + -0.03749188780784607, 0.022018054500222206, 0.014274892397224903, + 0.042031627148389816, -0.026070723310112953, -0.04804506525397301, + -0.03628045693039894, -0.0060633947141468525, -0.025939591228961945, + -0.0007743160822428763, -0.0039402698166668415, -0.008642367087304592, + 0.05587564781308174, 0.02682630717754364, 0.02716975286602974, + -0.015592479147017002, -0.020200908184051514, 0.003771668765693903, + -0.015117897652089596, 0.0006619153427891433, -0.06335654109716415, + -0.00936672743409872, -0.001161474152468145, -0.015642434358596802, + 0.04652765393257141, -0.03859715908765793, 0.033514149487018585, + -0.039871037006378174, -0.06976962834596634, 0.04631534591317177, + -0.042268916964530945, -0.0018421229906380177, 0.019620170816779137, + -0.047620441764593124, 0.017284734174609184, 0.03624923527240753, + -0.00670657679438591, 0.0035281339660286903, -0.02095649018883705, + 0.011683430522680283, 0.006793999578803778, -0.004820742178708315, + -0.028749607503414154, 0.004777031019330025, -0.04409230500459671, + -0.005563836079090834, 0.0033345548436045647, -0.011240072548389435, + 0.007337269838899374, 0.014905585907399654, 0.022911015897989273, + -0.004008959047496319, -0.005513879936188459, 0.0387907400727272, + -0.03369523957371712, 0.002728839870542288, -0.011427407152950764, + 0.017041198909282684, 0.0006931378156878054, -0.04941260814666748, + -0.014318603090941906, -0.03224651888012886, -0.01539265550673008, + -0.02693246304988861, -0.008623633533716202, -0.027456998825073242, + -0.010409556329250336, 0.005969727877527475, 0.031022600829601288, + 0.009460395202040672, -0.056375205516815186, 0.01032213307917118, + -0.009404193609952927, -0.011558541096746922, -0.03071662038564682, + -0.0077993618324398994, -0.02721346542239189, 0.014968030154705048, + -0.0036904902663081884, 0.013144641183316708, -0.01914559118449688, + -0.03774166852235794, -0.05164188891649246, -0.027456998825073242, + -0.030810287222266197, -0.004283716902136803, 0.004964365623891354, + -0.012451502494513988, -0.021137580275535583, 0.013550532050430775, + -0.025770988315343857, 0.01650417409837246, 0.005757415201514959, + 0.06817728281021118, -0.02665146067738533, 0.008592410944402218, + -0.027007395401597023, 0.049050427973270416, -0.027919093146920204, + 0.002316703787073493, 0.028830785304307938, -0.036124344915151596, + -0.008823457174003124, -0.012239189818501472, 0.015386410057544708, + -0.04643398895859718, 0.02435348927974701, -0.020625533536076546, + -0.11875761300325394, 0.026626484468579292, 0.010403310880064964, + 0.020625533536076546, -0.025521209463477135, -0.01702870987355709, + 0.018052807077765465, -0.009091969579458237, 0.008061629720032215, + 0.018571097403764725, -0.01370664406567812, -0.010059865191578865, + 0.006606664974242449, 0.023860175162553787, 0.006987578235566616, + 0.014043846167623997, 0.002360415179282427, -0.01977003924548626, + -0.03860964998602867, -0.020413219928741455, 0.05174804478883743, + 0.012345346622169018, 0.0019482792122289538, 0.024259822443127632, + -0.005020565818995237, -0.01080920360982418 + ] + }, + "onTheRoad": { + "prompt": "Photo of a nostalgic road trip", + "vector": [ + -0.028973080217838287, 0.012647041119635105, -0.008081350475549698, + -0.004977925214916468, 0.04188457131385803, -0.005273489281535149, + 0.004659026395529509, -0.015703797340393066, -0.002621188759803772, + 0.03169538080692291, -0.007015763316303492, -0.02328735589981079, + 0.0030256451573222876, 0.031788717955350876, -0.004262348171323538, + 0.5301334857940674, 0.015089335851371288, 0.018737221136689186, + 0.020642833784222603, -0.030870914459228516, 0.018169425427913666, + -0.0017811637371778488, -0.04613914340734482, -0.020533941686153412, + 0.011495895683765411, -0.019196122884750366, 0.008594698272645473, + 0.011052549816668034, 0.017586076632142067, 0.03496992215514183, + 0.004534578416496515, 0.011340335942804813, -0.028241947293281555, + -0.0016411596443504095, -0.019297238439321518, 0.007552445400506258, + -0.03332098573446274, -0.017827194184064865, -0.017189396545290947, + -0.0345110185444355, 0.028420841321349144, 0.016100475564599037, + -0.03083980083465576, 0.0034534353762865067, -0.037661112844944, + -0.05464828386902809, -0.028024161234498024, 0.027215249836444855, + -0.023940708488225937, 0.014544875361025333, -0.014015969820320606, + -0.025667425245046616, 0.01144922710955143, 0.03073868528008461, + 0.03635440766811371, -0.03593439608812332, -0.040157854557037354, + 0.009139158762991428, -0.022081764414906502, 0.008921375498175621, + -0.013292615301907063, 0.03142315149307251, -0.01682383008301258, + 0.05030815303325653, -0.07767118513584137, 0.010313638485968113, + -0.03102647140622139, -0.006440190598368645, 0.04801364243030548, + 0.06619862467050552, 0.016644936054944992, 0.04493355005979538, + -0.05138152092695236, -0.0172905120998621, -0.04947590455412865, + 0.0029789770487695932, -0.0670386478304863, 0.022462885826826096, + 0.06652530282735825, 0.05179375410079956, 0.0031112032011151314, + -0.012755932286381721, 0.03672775253653526, 0.009388054721057415, + 0.041814569383859634, 0.011145885102450848, -0.02598632499575615, + 0.021902868524193764, -0.03352321311831474, -0.011729235760867596, + 0.05675612390041351, 0.02020726352930069, 0.018114980310201645, + -0.037062209099531174, -0.0000077780077845091, 0.006541304290294647, + -0.04171345755457878, 0.012118136510252953, 0.015524904243648052, + 0.02827305719256401, -0.01118477527052164, -0.00269119068980217, + 0.019258346408605576, -0.06276852637529373, -0.029486428946256638, + 0.03668108582496643, -0.06087847054004669, -0.021941760554909706, + -0.03342209756374359, 0.037871118634939194, -0.014202643185853958, + -0.002006725873798132, 0.07768674194812775, -0.018791666254401207, + -0.022750671952962875, -0.011425893753767014, 0.01854277029633522, + -0.026235220953822136, -0.041231222450733185, 0.012017021887004375, + 0.03198316693305969, -0.03062201477587223, -0.02393293008208275, + -0.009224717505276203, -0.02770526520907879, -0.025216301903128624, + 0.06107291951775551, 0.003430101554840803, -0.028747517615556717, + -0.013035940937697887, -0.012009243480861187, 0.0052034868858754635, + 0.0017578297993168235, 0.039854515343904495, 0.03901448845863342, + -0.012864825315773487, -0.02987532690167427, -0.027619706466794014, + -0.038158904761075974, -0.017368290573358536, 0.04195457324385643, + 0.08762703835964203, 0.03662663698196411, -0.019266124814748764, + 0.0031189811415970325, -0.04515133425593376, -0.012195915915071964, + 0.010196967981755733, -0.022245101630687714, -0.05293712019920349, + -0.001345595344901085, 0.020075038075447083, 0.02769748494029045, + -0.0164349302649498, 0.027339696884155273, -0.0029400868806988, + -0.03755221888422966, 0.011254777200520039, 0.010725872591137886, + 0.008291356265544891, 0.0013844853965565562, -0.007311326917260885, + -0.0008711368427611887, 0.005172375123947859, 0.05788393318653107, + -0.015532681718468666, 0.03155537694692612, -0.5350958108901978, + 0.005615721456706524, 0.03021756000816822, 0.00598128791898489, + 0.047414734959602356, -0.05283600836992264, 0.041231222450733185, + 0.01253814809024334, -0.02467184141278267, -0.03131425753235817, + 0.019903922453522682, 0.0127870449796319, 0.034487687051296234, + 0.028234168887138367, -0.009108047001063824, 0.024228494614362717, + -0.018698330968618393, 0.04494910687208176, -0.014887107536196709, + 0.02439183183014393, -0.01256148237735033, -0.025340748950839043, + -0.013432620093226433, 0.031399816274642944, -0.05150596797466278, + 0.012499258853495121, 0.06501636654138565, 0.025885211303830147, + -0.03263652324676514, 0.03205316886305809, -0.11994465440511703, + -0.022525111213326454, 0.02953309379518032, 0.02495962753891945, + -0.001703383750282228, -0.02562853693962097, -0.006650196388363838, + -0.010593647137284279, 0.0019678359385579824, -0.006984651554375887, + 0.03988562524318695, 0.019157234579324722, -0.03468991443514824, + -0.03913893550634384, 0.007435775361955166, 0.021739531308412552, + -0.02215954288840294, 0.00023334022262133658, 0.0072335475124418736, + 0.007435775361955166, 0.001571157481521368, 0.01637270487844944, + -0.0014933774946257472, 0.008128018118441105, -0.010632536374032497, + -0.03667330741882324, 0.01813831366598606, 0.034378793090581894, + 0.0035934397019445896, 0.0032200952991843224, 0.0061446260660886765, + 0.006525748409330845, -0.06574749946594238, -0.038151130080223083, + -0.011488117277622223, -0.030147558078169823, -0.08026126027107239, + -0.02649189531803131, -0.04459131881594658, -0.029851993545889854, + 0.016956057399511337, 0.009325831197202206, 0.013689294457435608, + -0.030139779672026634, 0.03228650987148285, -0.0043556843884289265, + 0.023847371339797974, -0.10854987800121307, 0.008586919866502285, + -0.011721457354724407, 0.04104454815387726, -0.06776978075504303, + 0.03004644252359867, -0.03847780451178551, -0.026764124631881714, + -0.04989591985940933, 0.04401574656367302, -0.0460146926343441, + -0.0022245103027671576, -0.0211250688880682, -0.07163545489311218, + 0.012491480447351933, -0.004977925214916468, 0.00010889210534514859, + -0.01981058530509472, -0.05246266350150108, -0.016691604629158974, + 0.03320431336760521, -0.0081824641674757, -0.0058179497718811035, + -0.02873195894062519, 0.014568207785487175, -0.011091439053416252, + -0.04647359624505043, -0.006300186738371849, -0.02443072386085987, + -0.03892115131020546, -0.008859150111675262, 0.026258554309606552, + 0.00787134375423193, -0.028607511892914772, -0.02695857547223568, + -0.007490221876651049, 0.01284926850348711, -0.03438657149672508, + 0.04619358852505684, -0.014474872499704361, 0.02334180288016796, + 0.006665752734988928, -0.029463093727827072, 0.018293874338269234, + -0.034254346042871475, -0.007311326917260885, -0.03447213023900986, + -0.004806808661669493, -0.03904559835791588, -0.023940708488225937, + 0.013728183694183826, 0.020557275041937828, -0.0432768352329731, + -0.05826505646109581, 0.025791874155402184, 0.010578090324997902, + 0.00437901820987463, -0.01825498417019844, 0.034767694771289825, + 0.026826348155736923, 0.022112876176834106, 0.004254570230841637, + 0.014093750156462193, 0.0049701472744345665, 0.01739940233528614, + 0.006463524419814348, 0.06456524133682251, 0.009730287827551365, + 0.005825727712363005, -0.018457211554050446, 0.0001866721868282184, + -0.005887951701879501, -0.03931005299091339, 0.030015332624316216, + -0.017889417707920074, -0.034425463527441025, -0.03558438643813133, + 0.018962783738970757, -0.029890885576605797, 0.013098164461553097, + 0.014871550723910332, -0.032815415412187576, -0.0028934190049767494, + 0.05481162294745445, -0.01762496493756771, 0.0022011762484908104, + 0.03169538080692291, -0.009123602882027626, 0.010998102836310863, + 0.042390141636133194, 0.006160182412713766, 0.02678745985031128, + 0.012281473726034164, -0.016893833875656128, -0.0006844646413810551, + 0.052851561456918716, -0.018986117094755173, 0.020230598747730255, + 0.04578135535120964, 0.008151352405548096, 0.027977492660284042, + -0.024578504264354706, -0.02076728083193302, -0.017531629651784897, + 0.011527007445693016, 0.0008711368427611887, -0.025846319273114204, + 0.004386796150356531, 0.04838698357343674, -0.008423582650721073, + -0.005374603439122438, 0.004410130437463522, -0.014599321410059929, + 0.007692449726164341, -0.00984695740044117, 0.016279369592666626, + -0.03129870444536209, -0.021591750904917717, -0.008485806174576283, + -0.030482012778520584, -0.016341593116521835, 0.026172997429966927, + 0.016932722181081772, -0.024111824110150337, 0.0011200332082808018, + -0.009551393799483776, -0.0358099490404129, 0.010896989144384861, + -0.047080282121896744, -0.007357995491474867, 0.00798801425844431, + 0.000575572601519525, 0.016046030446887016, -0.00489236693829298, + -0.10168967396020889, 0.02358292043209076, 0.0017811637371778488, + -0.02100062184035778, 0.014350424520671368, -0.05210487172007561, + 0.028871964663267136, 0.009131381288170815, -0.015066001564264297, + -0.004589024931192398, 0.039691176265478134, -0.0756644606590271, + 0.04063231125473976, 0.004775696899741888, -0.02403404377400875, + 0.01830943115055561, -0.004013451747596264, 0.01505044475197792, + 0.015066001564264297, -0.02248622104525566, 0.05625055357813835, + -0.010313638485968113, 0.04159678518772125, -0.0014467095024883747, + -0.02043282613158226, -0.008820260874927044, 0.020572829991579056, + 0.015447122976183891, 0.0064324126578867435, 0.06067623943090439, + -0.04028230160474777, 0.02981310337781906, 0.004526800476014614, + 0.0035623274743556976, -0.01305149681866169, -0.010313638485968113, + 0.01038364041596651, 0.030474234372377396, -0.018986117094755173, + 0.005584609694778919, -0.013261503539979458, 0.015509347431361675, + 0.04229680448770523, -0.01998170092701912, -0.028716403990983963, + -0.006556860636919737, -0.04452909529209137, -0.05336491018533707, + -0.03118981048464775, 0.05527830123901367, -0.006113514304161072, + -0.011744791641831398, 0.0033523214515298605, -0.010391417890787125, + 0.018970560282468796, 0.019266124814748764, -0.03332098573446274, + 0.013152611441910267, -0.030808690935373306, -0.015641573816537857, + -0.0258152075111866, -0.007357995491474867, -0.005989065859466791, + 0.02151397056877613, -0.010461420752108097, 0.023668477311730385, + 0.014124861918389797, 0.014319312758743763, 0.020176151767373085, + -0.023334022611379623, 0.01225036196410656, -0.04085009917616844, + -0.01138700358569622, -0.01356484554708004, -0.011239221319556236, + -0.02076728083193302, 0.007101321592926979, -0.030233116820454597, + 0.007505777757614851, 0.03353099152445793, 0.01745384931564331, + 0.00538238137960434, 0.026141883805394173, -0.03178093954920769, + 0.01402374729514122, 0.03107313998043537, 0.03692220151424408, + -0.03624551743268967, -0.008081350475549698, 0.026414114981889725, + -0.03165648877620697, -0.031500931829214096, -0.005716835614293814, + 0.05883285030722618, 0.06266740709543228, 0.00887470692396164, + 0.008913597092032433, -0.0015089336084201932, -0.005623499397188425, + 0.029377536848187447, 0.003305653342977166, 0.023995155468583107, + -0.019787251949310303, -0.001804497791454196, 0.0464969277381897, + -0.009528059512376785, 0.02586965449154377, 0.013572623021900654, + -0.09108047187328339, 0.0117603475227952, -0.003056757152080536, + -0.016092699021100998, -0.0019211678300052881, 0.033056534826755524, + 0.028241947293281555, 0.026188552379608154, -0.020518384873867035, + -0.02048727311193943, -0.0033134312834590673, -0.014474872499704361, + 0.01888500340282917, -0.028514178469777107, 0.0027689707931131124, + 0.00927138514816761, -0.024119602516293526, -0.005856839939951897, + -0.017267178744077682, 0.0059268418699502945, 0.04030563682317734, + -0.015556016005575657, 0.021249517798423767, -0.017407182604074478, + 0.01837943308055401, 0.00439457455649972 + ] + }, + "food": { + "prompt": "Photo of delicious looking food", + "vector": [ + -0.03325869143009186, 0.019341137260198593, -0.013895140029489994, + -0.03983273357152939, -0.03497690334916115, 0.013626201078295708, + 0.01961754634976387, -0.015852412208914757, -0.01781715452671051, + -0.032451875507831573, 0.02271033637225628, 0.03895868360996246, + 0.0020319772884249687, 0.021567348390817642, 0.0059166401624679565, + 0.5388176441192627, -0.009412836283445358, 0.0056551722809672356, + -0.003092789091169834, -0.05278659239411354, 0.026774289086461067, + -0.007037215866148472, 0.01334232185035944, -0.018474560230970383, + -0.022807452827692032, -0.0056551722809672356, -0.03127153590321541, + 0.024988839402794838, 0.009196192026138306, 0.0060585797764360905, + -0.014388193376362324, -0.006036167964339256, 0.041065365076065063, + -0.011175875551998615, -0.030292898416519165, 0.0198715440928936, + 0.006290165241807699, -0.0056925248354673386, 0.016285700723528862, + 0.0009114016429521143, 0.05141201615333557, -0.029986606910824776, + -0.01359631959348917, 0.022740216925740242, -0.039907436817884445, + -0.04615277796983719, -0.021769052371382713, 0.0008964606677182019, + -0.02143287844955921, -0.023248212411999702, 0.038293808698654175, + 0.016278231516480446, 0.01688334159553051, -0.0026819114573299885, + -0.03274322301149368, 0.010608118027448654, -0.04260428994894028, + -0.010877056047320366, -0.005221882835030556, 0.05776941776275635, + -0.016225937753915787, -0.0020618592388927937, -0.0018526853527873755, + -0.025810595601797104, -0.08053204417228699, 0.03278804570436478, + 0.016659226268529892, -0.014141666702926159, 0.058658406138420105, + 0.038801804184913635, 0.04038555175065994, -0.02264310047030449, + 0.006320047192275524, -0.015307065099477768, 0.005162118934094906, + 0.015986880287528038, -0.046683184802532196, 0.005931580904871225, + 0.01092934887856245, 0.0175482165068388, -0.01203498337417841, + -0.02484690025448799, 0.02037953771650791, 0.04440468177199364, + -0.010764997452497482, 0.02353956177830696, -0.003369197715073824, + 0.02812645211815834, -0.003204846754670143, -0.02384585328400135, + 0.016173643991351128, 0.05445251241326332, -0.030449779704213142, + 0.019453195855021477, 0.002510089660063386, -0.013842846266925335, + -0.11064565181732178, 0.017436157912015915, -0.015172596089541912, + -0.010899467393755913, -0.019371019676327705, -0.006260283291339874, + 0.022531043738126755, 0.07672955840826035, 0.009509953670203686, + 0.010451236739754677, -0.0635366439819336, 0.02775292657315731, + 0.007963558658957481, 0.008501434698700905, -0.006006286013871431, + -0.008972076699137688, 0.10791891813278198, -0.06624843925237656, + 0.012453332543373108, -0.021500114351511, 0.00781414844095707, + 0.005924110766500235, -0.05336181819438934, 0.0006275224150158465, + -0.019079670310020447, -0.021888580173254013, 0.01606905646622181, + -0.02204546146094799, -0.013730787672102451, 0.03783810883760452, + 0.06955787539482117, -0.004198423586785793, 0.002838792046532035, + 0.00573734799399972, -0.014881246723234653, 0.009076663292944431, + 0.007784266024827957, 0.01649487391114235, -0.014156606048345566, + -0.02323327027261257, 0.008352024480700493, 0.026975994929671288, + 0.008172732777893543, 0.019796838983893394, -0.009495011530816555, + -0.007067097816616297, -0.0028238508384674788, -0.04082631319761276, + 0.004952944815158844, 0.005550585221499205, 0.0071791550144553185, + 0.022822393104434013, -0.018713615834712982, -0.018907848745584488, + -0.012879150919616222, 0.044188037514686584, 0.054788682609796524, + -0.021507585421204567, 0.007433152757585049, -0.0055729965679347515, + 0.02680417336523533, -0.005162118934094906, 0.020237598568201065, + -0.01447036862373352, 0.015045597217977047, 0.008202614262700081, + 0.03884662687778473, 0.03740482032299042, 0.02749892883002758, + -0.08252666890621185, -0.020626064389944077, -0.5434120297431946, + 0.004355304408818483, 0.022000636905431747, 0.013252676464617252, + 0.06556862592697144, 0.007866442203521729, 0.005595408380031586, + 0.004317952319979668, 0.003795016324147582, -0.008150320500135422, + 0.03689682483673096, 0.001180339721031487, 0.0028238508384674788, + -0.00079187355004251, 0.027416754513978958, 0.0027342047542333603, + -0.002689381828531623, -0.0005154648097231984, 0.033213865011930466, + -0.04651883617043495, -0.02916485257446766, 0.0355297215282917, + -0.0036381359677761793, -0.013118207454681396, -0.06837005913257599, + -0.04045278578996658, -0.01098164264112711, -0.002308386145159602, + -0.041267070919275284, -0.0024577961303293705, 0.030001547187566757, + 0.019341137260198593, 0.025960005819797516, 0.03824898600578308, + -0.003966838121414185, 0.01657705195248127, -0.00017929212481249124, + 0.01197521947324276, 0.023569442331790924, 0.045973487198352814, + -0.019333666190505028, 0.02383091114461422, -0.034999314695596695, + -0.002046918496489525, 0.004452420864254236, 0.005259235389530659, + 0.00938295479863882, -0.015971940010786057, 0.011459754779934883, + -0.07136573642492294, -0.00008217555296141654, 0.003354256972670555, + 0.011960278265178204, -0.006925158202648163, 0.022015579044818878, + -0.00782161857932806, -0.005565526429563761, 0.0022785039618611336, + -0.02211269550025463, 0.027215048670768738, -0.030121076852083206, + -0.002756616333499551, -0.05776941776275635, -0.034961964935064316, + 0.05590179190039635, -0.01955031231045723, -0.051270075142383575, + -0.029695259407162666, 0.004639183636754751, -0.016539698466658592, + -0.011616635136306286, 0.015165125951170921, -0.012124629691243172, + -0.01593458652496338, -0.003697899868711829, -0.024772195145487785, + 0.039907436817884445, 0.019206669181585312, 0.0040639545768499374, + 0.03631412610411644, -0.015419123694300652, -0.03551478311419487, + 0.01601676270365715, -0.00938295479863882, -0.02476472407579422, + -0.014948480762541294, 0.05316758155822754, -0.031839292496442795, + -0.042066413909196854, -0.00469147739931941, 0.006529221311211586, + 0.005535644479095936, -0.0032870222348719835, -0.001830273657105863, + -0.0017032751347869635, -0.006387282162904739, 0.010794879868626595, + -0.020954767242074013, -0.015419123694300652, 0.011960278265178204, + -0.01377561129629612, 0.014276135712862015, -0.028201157227158546, + -0.002046918496489525, -0.00322725810110569, 0.015874823555350304, + 0.021798934787511826, -0.010705234482884407, -0.0036754885222762823, + 0.007194096688181162, 0.0313163585960865, 0.003062907140702009, + -0.015314536169171333, 0.02692370116710663, 0.042962875217199326, + 0.00938295479863882, -0.04484543949365616, 0.046578601002693176, + 0.026236414909362793, -0.018355030566453934, -0.00047811231343075633, + 0.019214140251278877, -0.00611834367737174, -0.0389288030564785, + -0.002793968887999654, -0.012931443750858307, -0.010107593610882759, + -0.0041909534484148026, 0.011840750463306904, -0.0026968521997332573, + -0.031346239149570465, 0.03468555584549904, 0.003197376150637865, + 0.023547032848000526, 0.0007246389868669212, 0.028118979185819626, + 0.007134332321584225, 0.017346512526273727, -0.01574782468378544, + 0.027827631682157516, -0.002913497155532241, 0.0016360405134037137, + -0.011198286898434162, 0.023255683481693268, -0.006745866034179926, + 0.032481756061315536, 0.018355030566453934, 0.016173643991351128, + -0.013140617869794369, -0.05315264314413071, -0.0056103491224348545, + -0.06217701733112335, -0.008479023352265358, -0.043373752385377884, + 0.02527271769940853, -0.039264973253011703, 0.048737574368715286, + -0.016517287120223045, -0.0036007834132760763, -0.008411789312958717, + -0.0382564552128315, 0.017660275101661682, 0.015703001990914345, + -0.0026072063483297825, -0.004138659685850143, -0.01969972252845764, + 0.08213820308446884, -0.03620953857898712, -0.012998678721487522, + -0.011594223789870739, 0.01292397454380989, -0.05907675623893738, + 0.009218603372573853, -0.02578071318566799, 0.020521478727459908, + 0.032758165150880814, -0.010764997452497482, 0.009315719828009605, + -0.017129868268966675, 0.0033841386903077364, -0.011280463077127934, + -0.04391910135746002, 0.025534186512231827, -0.006880335509777069, + 0.007440623361617327, -0.019468136131763458, 0.025713477283716202, + -0.012460802681744099, -0.0057672299444675446, 0.050388555973768234, + -0.0032571402844041586, 0.012251628562808037, -0.0466383621096611, + -0.005998815875500441, 0.0018078621942549944, -0.011116111651062965, + 0.004960415419191122, 0.019610077142715454, -0.006842982489615679, + -0.006566573400050402, 0.01104887668043375, 0.0034588437993079424, + 0.015486356802284718, -0.010137475095689297, -0.005094884429126978, + -0.014007197692990303, -0.01934860832989216, 0.029493553563952446, + -0.05791882425546646, 0.01922907866537571, -0.0032944928389042616, + -0.09949218481779099, -0.02040194906294346, -0.010144946165382862, + -0.022770099341869354, 0.0027192640118300915, -0.012849269434809685, + 0.05047820508480072, 0.032011114060878754, -0.008359495550394058, + -0.010578235611319542, 0.02046918496489525, 0.04862551763653755, + -0.045801665633916855, 0.03130141645669937, 0.03137611970305443, + -0.019811779260635376, 0.02223222330212593, -0.017757391557097435, + 0.01899002306163311, 0.025115838274359703, -0.009599599055945873, + 0.07309142500162125, 0.01588229462504387, 0.008404318243265152, + 0.034775201231241226, -0.027760395780205727, 0.004056484438478947, + -0.0008217555587179959, 0.005326470360159874, 0.0442926250398159, + 0.007119391579180956, -0.03139106184244156, -0.022441396489739418, + 0.008635904639959335, -0.005498291924595833, 0.00540117546916008, + 0.0027491459622979164, -0.02805921621620655, -0.033333394676446915, + -0.03194388002157211, -0.017249396070837975, 0.004758711438626051, + 0.07516822218894958, -0.009278367273509502, 0.020715711638331413, + -0.019953718408942223, 0.01878831908106804, 0.014313487336039543, + -0.022740216925740242, 0.0008740490884520113, 0.019707193598151207, + 0.01625581830739975, -0.0039967200718820095, 0.04726588353514671, + -0.0011653987457975745, -0.015590944327414036, -0.01895267143845558, + 0.02831321209669113, 0.006222930736839771, 0.04343351721763611, + -0.001942331320606172, 0.005498291924595833, -0.022822393104434013, + -0.017503393813967705, 0.00837443582713604, -0.0072239781729876995, + 0.016412699595093727, -0.008366965688765049, 0.027028288692235947, + 0.03430455923080444, 0.025145720690488815, 0.021320821717381477, + -0.09243255853652954, 0.00502764992415905, -0.07131344079971313, + 0.009778891690075397, -0.02291204035282135, -0.00046317133819684386, + -0.004325422458350658, 0.01776486076414585, -0.018743496388196945, + -0.02123117446899414, -0.02457796223461628, -0.03160770609974861, + -0.012879150919616222, -0.027640867978334427, 0.010189768858253956, + 0.06280454248189926, -0.0057224067859351635, 0.010839702561497688, + -0.02601976878941059, 0.00540117546916008, -0.006207989528775215, + 0.05945027619600296, 0.0041909534484148026, -0.02009565941989422, + 0.02253851294517517, 0.015755295753479004, -0.028597094118595123, + -0.004796063993126154, 0.03963850066065788, -0.021036941558122635, + 0.00008217555296141654, -0.0003735252539627254, 0.02180640399456024, + -0.044240329414606094, -0.1282835155725479, -0.027775337919592857, + -0.13690447807312012, 0.026587529107928276, 0.003234728705137968, + -0.00243538455106318, -0.026064591482281685, 0.04545802250504494, + -0.017085043713450432, 0.026161709800362587, -0.005035120528191328, + -0.016076527535915375, -0.020274950191378593, -0.008142850361764431, + 0.016860930249094963, -0.025332482531666756, 0.0396459698677063, + -0.047856055200099945, 0.012565389275550842, 0.0017854507314041257, + -0.010712703689932823, -0.03399079665541649, -0.009808773174881935, + 0.018810732290148735, -0.016412699595093727, -0.0073808589950203896, + 0.0021515055559575558, -0.045577552169561386 + ] + }, + "pets": { + "prompt": "Photo of cute pets", + "vector": [ + -0.018412098288536072, 0.015965646132826805, -0.006039677653461695, + -0.006294516380876303, 0.0009683871758170426, 0.0048355646431446075, + -0.01478064525872469, 0.0024400807451456785, 0.019100161269307137, + -0.009696613065898418, 0.016220485791563988, 0.0063645970076322556, + -0.07371847331523895, -0.009652016684412956, -0.012397903949022293, + 0.5433799028396606, -0.030102822929620743, -0.007033549249172211, + 0.0409589558839798, -0.03465806692838669, 0.02250226028263569, + 0.04019443690776825, 0.03446056693792343, -0.03670952096581459, + 0.011556935496628284, -0.05243943631649017, -0.019686290994286537, + 0.023438790813088417, 0.029172662645578384, 0.004740000236779451, + 0.007995565421879292, -0.03016653284430504, 0.01606758125126362, + 0.015060968697071075, 0.006772339344024658, -0.04906919598579407, + -0.004141129087656736, -0.006090645678341389, -0.016711048781871796, + 0.004548871424049139, 0.05233750119805336, -0.028446370735764503, + -0.00853709690272808, -0.039780326187610626, -0.017061451449990273, + -0.024241533130407333, 0.01281838770955801, 0.004765484016388655, + 0.025745080783963203, -0.011263871565461159, 0.061562661081552505, + 0.016252340748906136, 0.002796855056658387, -0.0019240323454141617, + 0.03179113194346428, 0.022610565647482872, -0.04005427658557892, + -0.02130451612174511, 0.0006753226043656468, -0.01755838841199875, + -0.030956534668803215, 0.019405968487262726, -0.019246693700551987, + 0.04778863117098808, -0.049668069928884506, 0.02699379250407219, + 0.0064537907019257545, 0.04073597118258476, 0.015022742561995983, + 0.01813814602792263, 0.002898790407925844, 0.044456616044044495, + -0.003644193522632122, -0.025394678115844727, -0.0014079839456826448, + -0.006358225829899311, -0.09420750290155411, 0.002554758219048381, + -0.01680024154484272, 0.008728226646780968, 0.0413985475897789, + 0.0026120967231690884, 0.019533388316631317, 0.009550080634653568, + -0.029631372541189194, 0.02370637282729149, -0.0016118548810482025, + 0.014251855202019215, -0.03925790637731552, -0.01651354879140854, + 0.027656372636556625, 0.013888711109757423, -0.004982097074389458, + 0.029771532863378525, -0.03363233804702759, 0.00021661292703356594, + -0.07534944266080856, -0.01572354882955551, 0.0008154839160852134, + -0.036212582141160965, -0.0546119399368763, 0.018373873084783554, + -0.015678953379392624, 0.03827040269970894, -0.006937984377145767, + -0.021221693605184555, -0.06097016483545303, 0.005115887615829706, + 0.003408468095585704, -0.013156048953533173, 0.002592983888462186, + -0.009314354509115219, 0.10348363220691681, -0.01737362891435623, + -0.010684113018214703, -0.00946088694036007, -0.014194516465067863, + 0.0004905645619146526, 0.010199920274317265, -0.0007772581302560866, + -0.009989677928388119, -0.02426701784133911, 0.06381798535585403, + -0.009492741897702217, 0.02280169539153576, -0.003121774410828948, + 0.0017456453060731292, 0.00788088794797659, 0.028548305854201317, + 0.02487863041460514, 0.024279760196805, 0.050165001302957535, + 0.006613065022975206, -0.007555968128144741, -0.024540970101952553, + -0.03653113171458244, -0.02502516098320484, 0.011346694082021713, + -0.007849032990634441, 0.007097258232533932, -0.0324983075261116, + -0.0029242741875350475, 0.02594895288348198, 0.0070526618510484695, + 0.004994838964194059, 0.020590970292687416, 0.021954355761408806, + 0.024916857481002808, -0.0013251613127067685, -0.00966475810855627, + 0.01769217848777771, -0.008530725724995136, -0.007262903265655041, + -0.032918792217969894, -0.00833322573453188, -0.020043065771460533, + -0.009702984243631363, 0.0010766936466097832, 0.015156532637774944, + -0.009104114025831223, -0.058128710836172104, 0.03610427677631378, + 0.005963225848972797, 0.02354072593152523, 0.03976758196949959, + 0.02101145125925541, -0.00396911334246397, -0.5477248430252075, + 0.0277455672621727, -0.005084032658487558, -0.017813226208090782, + 0.06590129435062408, 0.015819113701581955, 0.03328193724155426, + 0.029057985171675682, 0.05862564593553543, -0.004217580892145634, + 0.0317656472325325, 0.003930887207388878, -0.027325082570314407, + -0.01633516140282154, 0.013882339932024479, 0.027847502380609512, + 0.005950483959168196, -0.000700806500390172, 0.003962742164731026, + 0.03163185715675354, 0.007619677577167749, -0.018787983804941177, + -0.0214319359511137, 0.007817178033292294, -0.0603075847029686, + -0.0012805645819753408, -0.012984032742679119, -0.022565970197319984, + -0.03361322730779648, 0.014239112846553326, 0.010633145458996296, + 0.02683451771736145, 0.007785323075950146, 0.061683714389801025, + -0.021183468401432037, -0.012461613863706589, -0.021323630586266518, + -0.028860483318567276, 0.01320701651275158, -0.018214598298072815, + 0.028707580640912056, -0.042660001665353775, 0.020329760387539864, + -0.0006753226043656468, -0.010849758982658386, 0.005351613275706768, + -0.04668645188212395, -0.014258225448429585, 0.01813177578151226, + -0.04093347117304802, -0.03286782279610634, 0.017144273966550827, + -0.00764516182243824, 0.012391532771289349, 0.04016895592212677, + -0.0038990324828773737, 0.007237419486045837, -0.02127903327345848, + -0.01976911351084709, 0.008129355497658253, 0.021846048533916473, + -0.011652500368654728, -0.07947145402431488, -0.0050521777011454105, + 0.00004459677802515216, -0.022846290841698647, -0.06710540503263474, + -0.006294516380876303, -0.015405001118779182, -0.030497824773192406, + -0.013385403901338577, 0.037703387439250946, -0.032326292246580124, + -0.012041130103170872, -0.03310991823673248, -0.015303065069019794, + 0.002389112953096628, -0.03407830744981766, -0.007345725782215595, + 0.0293892752379179, -0.03459435701370239, -0.08546654134988785, + -0.006415564566850662, 0.013009516522288322, -0.04607484117150307, + -0.010849758982658386, 0.0468011312186718, -0.013308952562510967, + -0.005848548375070095, -0.010868871584534645, -0.049559760838747025, + -0.007466774433851242, 0.020801210775971413, -0.012920322827994823, + -0.00475911283865571, 0.02626113034784794, 0.020438065752387047, + 0.006396451964974403, 0.026509597897529602, 0.03451153263449669, + -0.0045934682711958885, 0.0024400807451456785, 0.011671612970530987, + -0.012251371517777443, 0.0025675001088529825, -0.010467500425875187, + 0.01026363018900156, 0.05339508131146431, 0.030045485123991966, + 0.04553968086838722, 0.025088870897889137, -0.0013633872149512172, + -0.042672742158174515, 0.015596129931509495, -0.012404275126755238, + -0.01755201630294323, -0.02296096831560135, 0.030472340062260628, + 0.029886210337281227, -0.0198200810700655, 0.04711968079209328, + 0.006218065042048693, -0.004077419172972441, -0.06715000420808792, + 0.007606935687363148, -0.00536435516551137, 0.029344677925109863, + 0.009938710369169712, 0.008581694215536118, 0.012780161574482918, + 0.02591072767972946, 0.016889436170458794, -0.04043653607368469, + -0.02471298538148403, -0.0017265323549509048, 0.003605967853218317, + 0.02171863056719303, 0.02534371055662632, -0.014455726370215416, + 0.0554911345243454, -0.01784508116543293, 0.0418381467461586, + -0.004599838983267546, 0.04076782613992691, 0.03344758227467537, + 0.04495355114340782, -0.00823766179382801, 0.011805403977632523, + -0.023285888135433197, -0.05006306618452072, 0.0018539517186582088, + -0.05221645534038544, -0.01827193610370159, 0.019278548657894135, + 0.03916871175169945, -0.002223467919975519, 0.016112178564071655, + -0.07009975612163544, -0.023763710632920265, -0.000860080705024302, + -0.011862742714583874, 0.014143547974526882, 0.024993307888507843, + -0.010008790530264378, -0.001688306569121778, -0.010633145458996296, + 0.0553446002304554, -0.007396693807095289, 0.024228790774941444, + -0.02114524319767952, -0.02173137106001377, 0.02101145125925541, + 0.023808307945728302, -0.01453854888677597, 0.00426854845136404, + -0.010798790492117405, -0.013079597614705563, -0.005001210141927004, + 0.0015991129912436008, 0.015131048858165741, -0.013920566067099571, + 0.03167008236050606, 0.00898943655192852, -0.011130081489682198, + 0.0063263713382184505, -0.02939564548432827, -0.053458791226148605, + 0.03219250217080116, 0.011875484138727188, 0.02474484033882618, + -0.04773129150271416, -0.033078063279390335, 0.04755927622318268, + 0.014825242571532726, 0.00277137104421854, -0.028102342039346695, + 0.00401371018961072, 0.039347097277641296, 0.032033227384090424, + -0.005969597026705742, -0.016711048781871796, -0.004899274557828903, + 0.02397395297884941, 0.015264839865267277, -0.02385290339589119, + 0.03400822728872299, -0.027452502399683, 0.02171863056719303, + -0.06312992423772812, 0.01647532358765602, -0.013977904804050922, + -0.05996992439031601, -0.004567984025925398, -0.00021661292703356594, + 0.010301855392754078, -0.007078145164996386, -0.024470888078212738, + 0.030676212161779404, 0.009957822971045971, -0.0411054864525795, + -0.033065322786569595, 0.035607341676950455, 0.010894355364143848, + -0.07190274447202682, -0.03331379219889641, 0.022540485486388206, + 0.008887500502169132, 0.005485403351485729, -0.01666645146906376, + -0.0017010484589263797, 0.038002822548151016, -0.028733065351843834, + -0.07052024453878403, 0.033078063279390335, -0.0019877420272678137, + -0.005663790740072727, -0.027675487101078033, 0.024407178163528442, + 0.02864387072622776, 0.009473629295825958, -0.043851371854543686, + 0.05092315003275871, 0.015398629941046238, -0.02924911305308342, + -0.023126613348722458, 0.03863992169499397, -0.00818669330328703, + 0.00558733893558383, 0.02818516455590725, -0.02622290514409542, + -0.02606363035738468, -0.011894597671926022, -0.01845669560134411, + 0.05558669567108154, -0.06443597376346588, 0.07635605335235596, + 0.010945322923362255, 0.028134196996688843, -0.016163146123290062, + -0.005517258308827877, 0.02263605035841465, 0.011773549020290375, + 0.0020896773785352707, -0.02352161332964897, 0.03303983807563782, + 0.008970323018729687, -0.025076130405068398, -0.04618314653635025, + -0.015252097509801388, -0.010958065278828144, 0.03614887222647667, + -0.018647823482751846, 0.02625476010143757, -0.03058064728975296, + 0.015080081298947334, 0.023171210661530495, -0.032587502151727676, + 0.03588129207491875, -0.019527016207575798, 0.009014920331537724, + -0.0513245165348053, 0.040130726993083954, 0.010212661698460579, + -0.07171798497438431, 0.04889081045985222, -0.018832581117749214, + 0.017915163189172745, -0.010187177918851376, -0.039359841495752335, + -0.05723040550947189, -0.022871773689985275, -0.017335403710603714, + -0.0351995974779129, -0.023725485429167747, -0.027255002409219742, + -0.01102177519351244, 0.019858308136463165, 0.02341330796480179, + 0.02624838799238205, 0.054662905633449554, -0.009282500483095646, + -0.020622823387384415, 0.04590919613838196, -0.05874669924378395, + 0.008250403217971325, 0.019450565800070763, -0.015347662381827831, + 0.01154419407248497, 0.011518710292875767, 0.013614758849143982, + 0.023158468306064606, 0.015226613730192184, -0.03371516242623329, + -0.029478468000888824, 0.018647823482751846, 0.010633145458996296, + 0.0005479032406583428, -0.025146210566163063, -0.03495113179087639, + -0.07967532426118851, 0.01184362918138504, 0.015245726332068443, + 0.006307258270680904, 0.02591709792613983, 0.023298630490899086, + 0.021533871069550514, 0.006549355108290911, -0.010798790492117405, + 0.011047258973121643, -0.019527016207575798, -0.0348237119615078, + -0.009620161727070808, -0.009913226589560509, -0.016322419047355652, + -0.015564274974167347, -0.009199677966535091, -0.023336855694651604, + -0.026057260110974312, -0.0160293560475111, -0.02277621068060398, + 0.005351613275706768, 0.022438550367951393, 0.003586854785680771, + 0.01966717839241028, -0.05842814967036247 + ] + } + } + } +} diff --git a/mobile/apps/photos/lib/services/smart_memories_service.dart b/mobile/apps/photos/lib/services/smart_memories_service.dart index 9583bbc38d..117f334253 100644 --- a/mobile/apps/photos/lib/services/smart_memories_service.dart +++ b/mobile/apps/photos/lib/services/smart_memories_service.dart @@ -35,9 +35,9 @@ import "package:photos/services/collections_service.dart"; import "package:photos/services/language_service.dart"; import "package:photos/services/location_service.dart"; import "package:photos/services/machine_learning/face_ml/person/person_service.dart"; -import "package:photos/services/machine_learning/ml_computer.dart"; import "package:photos/services/machine_learning/ml_result.dart"; import "package:photos/services/search_service.dart"; +import "package:photos/utils/text_embeddings_util.dart"; class MemoriesResult { final List memories; @@ -103,24 +103,18 @@ class SmartMemoriesService { 'allImageEmbeddings has ${allImageEmbeddings.length} entries $t', ); - const String clipPositiveQuery = - 'Photo of a precious and nostalgic memory radiating warmth, vibrant energy, or quiet beauty — alive with color, light, or emotion'; - final clipPositiveTextVector = Vector.fromList( - await MLComputer.instance.runClipText(clipPositiveQuery), - ); - final Map clipPeopleActivityVectors = {}; - for (final peopleActivity in PeopleActivity.values) { - clipPeopleActivityVectors[peopleActivity] ??= Vector.fromList( - await MLComputer.instance.runClipText(activityQuery(peopleActivity)), + // Load pre-computed text embeddings from assets + final textEmbeddings = await loadTextEmbeddingsFromAssets(); + if (textEmbeddings == null) { + _logger.severe('Failed to load pre-computed text embeddings'); + throw Exception( + 'Failed to load pre-computed text embeddings', ); } - final Map clipMemoryTypeVectors = {}; - for (final clipMemoryType in ClipMemoryType.values) { - clipMemoryTypeVectors[clipMemoryType] ??= Vector.fromList( - await MLComputer.instance.runClipText(clipQuery(clipMemoryType)), - ); - } - _logger.info('clipPositiveTextVector and clipPeopleActivityVectors $t'); + _logger.info('Using pre-computed text embeddings from assets'); + final clipPositiveTextVector = textEmbeddings.clipPositiveVector; + final clipPeopleActivityVectors = textEmbeddings.peopleActivityVectors; + final clipMemoryTypeVectors = textEmbeddings.clipMemoryTypeVectors; final local = await getLocale(); final languageCode = local?.languageCode ?? "en"; diff --git a/mobile/apps/photos/lib/utils/text_embeddings_util.dart b/mobile/apps/photos/lib/utils/text_embeddings_util.dart new file mode 100644 index 0000000000..349c284ed4 --- /dev/null +++ b/mobile/apps/photos/lib/utils/text_embeddings_util.dart @@ -0,0 +1,172 @@ +import 'dart:convert'; +import "dart:developer" as dev show log; +import "dart:io" show File; + +import 'package:flutter/services.dart'; +import 'package:logging/logging.dart'; +import 'package:ml_linalg/vector.dart'; +import "package:path_provider/path_provider.dart" + show getExternalStorageDirectory; +import 'package:photos/models/memories/clip_memory.dart'; +import 'package:photos/models/memories/people_memory.dart'; +import "package:photos/services/machine_learning/ml_computer.dart" + show MLComputer; + +final _logger = Logger('TextEmbeddingsUtil'); + +/// Loads pre-computed text embeddings from assets +Future loadTextEmbeddingsFromAssets() async { + try { + _logger.info('Loading text embeddings from assets'); + final jsonString = + await rootBundle.loadString('assets/ml/text_embeddings.json'); + final data = json.decode(jsonString) as Map; + + final embeddings = data['embeddings'] as Map; + + // Parse clip positive embedding + Vector? clipPositiveVector; + final clipPositive = embeddings['clip_positive'] as Map; + final clipPositiveVectorData = + (clipPositive['vector'] as List).cast(); + if (clipPositiveVectorData.isNotEmpty) { + clipPositiveVector = Vector.fromList(clipPositiveVectorData); + } + + // Parse people activities embeddings + final Map peopleActivityVectors = {}; + final peopleActivities = + embeddings['people_activities'] as Map; + for (final activity in PeopleActivity.values) { + final activityName = activity.toString().split('.').last; + if (peopleActivities.containsKey(activityName)) { + final activityData = + peopleActivities[activityName] as Map; + final vector = (activityData['vector'] as List).cast(); + if (vector.isNotEmpty) { + peopleActivityVectors[activity] = Vector.fromList(vector); + } + } + } + + // Parse clip memory types embeddings + final Map clipMemoryTypeVectors = {}; + final clipMemoryTypes = + embeddings['clip_memory_types'] as Map; + for (final memoryType in ClipMemoryType.values) { + final typeName = memoryType.toString().split('.').last; + if (clipMemoryTypes.containsKey(typeName)) { + final typeData = clipMemoryTypes[typeName] as Map; + final vector = (typeData['vector'] as List).cast(); + if (vector.isNotEmpty) { + clipMemoryTypeVectors[memoryType] = Vector.fromList(vector); + } + } + } + + // Check if we have all required embeddings + if (clipPositiveVector == null) { + _logger.severe('Clip positive vector is missing'); + throw Exception('Clip positive vector is missing'); + } + + if (peopleActivityVectors.length != PeopleActivity.values.length) { + _logger.severe('Some people activity vectors are missing'); + throw Exception('Some people activity vectors are missing'); + } + + if (clipMemoryTypeVectors.length != ClipMemoryType.values.length) { + _logger.severe('Some clip memory type vectors are missing'); + throw Exception('Some clip memory type vectors are missing'); + } + + _logger.info('Text embeddings loaded successfully from JSON assets'); + return TextEmbeddings( + clipPositiveVector: clipPositiveVector, + peopleActivityVectors: peopleActivityVectors, + clipMemoryTypeVectors: clipMemoryTypeVectors, + ); + } catch (e, stackTrace) { + _logger.severe('Failed to load text embeddings from JSON', e, stackTrace); + return null; + } +} + +class TextEmbeddings { + final Vector clipPositiveVector; + final Map peopleActivityVectors; + final Map clipMemoryTypeVectors; + + const TextEmbeddings({ + required this.clipPositiveVector, + required this.peopleActivityVectors, + required this.clipMemoryTypeVectors, + }); +} + +/// Helper function to generate text embeddings and save them to a JSON file +/// Run this once to generate the embeddings, then copy the output +/// to assets/ml/text_embeddings.json +Future generateAndSaveTextEmbeddings() async { + final Map embeddingsData = { + 'version': '1.0.0', + 'embeddings': { + 'clip_positive': {}, + 'people_activities': {}, + 'clip_memory_types': {}, + }, + }; + + // Generate clip positive embedding + const String clipPositiveQuery = + 'Photo of a precious and nostalgic memory radiating warmth, vibrant energy, or quiet beauty — alive with color, light, or emotion'; + final clipPositiveVector = + await MLComputer.instance.runClipText(clipPositiveQuery); + embeddingsData['embeddings']['clip_positive'] = { + 'prompt': clipPositiveQuery, + 'vector': clipPositiveVector, + }; + + // Generate people activity embeddings + final peopleActivities = {}; + for (final activity in PeopleActivity.values) { + final activityName = activity.toString().split('.').last; + final prompt = activityQuery(activity); + final vector = await MLComputer.instance.runClipText(prompt); + peopleActivities[activityName] = { + 'prompt': prompt, + 'vector': vector, + }; + } + embeddingsData['embeddings']['people_activities'] = peopleActivities; + + // Generate clip memory type embeddings + final clipMemoryTypes = {}; + for (final memoryType in ClipMemoryType.values) { + final typeName = memoryType.toString().split('.').last; + final prompt = clipQuery(memoryType); + final vector = await MLComputer.instance.runClipText(prompt); + clipMemoryTypes[typeName] = { + 'prompt': prompt, + 'vector': vector, + }; + } + embeddingsData['embeddings']['clip_memory_types'] = clipMemoryTypes; + + // Convert to JSON and log it + final jsonString = const JsonEncoder.withIndent(' ').convert(embeddingsData); + dev.log( + '_generateAndSaveTextEmbeddings: Generated text embeddings JSON', + ); + + final tempDir = await getExternalStorageDirectory(); + final file = File('${tempDir!.path}/text_embeddings.json'); + await file.writeAsString(jsonString); + dev.log( + '_generateAndSaveTextEmbeddings: Saved text embeddings to ${file.path}', + ); + + dev.log( + '_generateAndSaveTextEmbeddings: Text embeddings generation complete! Copy the JSON output above to assets/ml/text_embeddings.json', + ); +} diff --git a/mobile/apps/photos/pubspec.yaml b/mobile/apps/photos/pubspec.yaml index e9315cc414..d051633455 100644 --- a/mobile/apps/photos/pubspec.yaml +++ b/mobile/apps/photos/pubspec.yaml @@ -344,6 +344,7 @@ flutter: - assets/image-editor/ - assets/icons/ - assets/launcher_icon/ + - assets/ml/ fonts: - family: Inter fonts: From 5b4ff1d01ab2d3cbd524b4f9c999530185c14aa2 Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Fri, 15 Aug 2025 17:34:04 +0530 Subject: [PATCH 153/156] chore: update gradle and kotlin --- .../auth/android/gradle/wrapper/gradle-wrapper.properties | 2 +- mobile/apps/auth/android/settings.gradle | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile/apps/auth/android/gradle/wrapper/gradle-wrapper.properties b/mobile/apps/auth/android/gradle/wrapper/gradle-wrapper.properties index e1ca574ef0..3c85cfe057 100644 --- a/mobile/apps/auth/android/gradle/wrapper/gradle-wrapper.properties +++ b/mobile/apps/auth/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip diff --git a/mobile/apps/auth/android/settings.gradle b/mobile/apps/auth/android/settings.gradle index 748caceba7..3ddaae5aa3 100644 --- a/mobile/apps/auth/android/settings.gradle +++ b/mobile/apps/auth/android/settings.gradle @@ -19,8 +19,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.8.22" apply false + id "com.android.application" version "8.6.0" apply false + id "org.jetbrains.kotlin.android" version "2.1.10" apply false } include ":app" From 3132373c2627af17298a665f742472ca3561f465 Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Fri, 15 Aug 2025 18:13:08 +0530 Subject: [PATCH 154/156] chore: update plugins & add desugaring --- mobile/apps/auth/android/app/build.gradle | 8 ++++- mobile/apps/auth/lib/utils/platform_util.dart | 4 +-- mobile/apps/auth/pubspec.lock | 33 ++++++++++--------- mobile/apps/auth/pubspec.yaml | 27 +++++++++------ 4 files changed, 44 insertions(+), 28 deletions(-) diff --git a/mobile/apps/auth/android/app/build.gradle b/mobile/apps/auth/android/app/build.gradle index 3da027f155..4633c502f3 100644 --- a/mobile/apps/auth/android/app/build.gradle +++ b/mobile/apps/auth/android/app/build.gradle @@ -34,6 +34,9 @@ android { ndkVersion flutter.ndkVersion compileOptions { + // Flag to enable support for the new language APIs + coreLibraryDesugaringEnabled true + // Sets Java compatibility to Java 8 sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } @@ -115,4 +118,7 @@ flutter { source '../..' } -dependencies {} +dependencies { + // For AGP 7.4+ + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4") +} diff --git a/mobile/apps/auth/lib/utils/platform_util.dart b/mobile/apps/auth/lib/utils/platform_util.dart index bad3c8e738..40ae35ae53 100644 --- a/mobile/apps/auth/lib/utils/platform_util.dart +++ b/mobile/apps/auth/lib/utils/platform_util.dart @@ -59,14 +59,14 @@ class PlatformUtil { if (Platform.isAndroid || Platform.isIOS) { await FileSaver.instance.saveAs( name: fileName, - ext: extension, + fileExtension: extension, bytes: bytes, mimeType: type, ); } else { await FileSaver.instance.saveFile( name: fileName, - ext: extension, + fileExtension: extension, bytes: bytes, mimeType: type, ); diff --git a/mobile/apps/auth/pubspec.lock b/mobile/apps/auth/pubspec.lock index b8a9a9f6f0..24dcf85ffe 100644 --- a/mobile/apps/auth/pubspec.lock +++ b/mobile/apps/auth/pubspec.lock @@ -470,10 +470,10 @@ packages: dependency: "direct main" description: name: file_saver - sha256: "448b1e30142cffe52f37ee085ea9ca50670d5425bb09b649d193549b2dcf6e26" + sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.3.1" fixnum: dependency: "direct main" description: @@ -485,10 +485,11 @@ packages: fk_user_agent: dependency: "direct main" description: - name: fk_user_agent - sha256: fd6c94e120786985a292d12f61422a581f4e851148d5940af38b819357b8ad0d - url: "https://pub.dev" - source: hosted + path: "." + ref: "458046cd9a88924e5074d96ba45397219d53b230" + resolved-ref: "458046cd9a88924e5074d96ba45397219d53b230" + url: "https://github.com/flutter-fast-kit/fk_user_agent" + source: git version: "2.1.0" flutter: dependency: "direct main" @@ -1081,10 +1082,11 @@ packages: move_to_background: dependency: "direct main" description: - name: move_to_background - sha256: "00caad17a6ce149910777131503f43f8ed80025681f94684e3a6a87d979b914c" - url: "https://pub.dev" - source: hosted + path: "." + ref: "91e4d1a9c55b28bf93425d1f12faf410efc1e48d" + resolved-ref: "91e4d1a9c55b28bf93425d1f12faf410efc1e48d" + url: "https://github.com/Sayegh7/move_to_background" + source: git version: "1.0.2" native_dio_adapter: dependency: "direct main" @@ -1329,11 +1331,12 @@ packages: qr_code_scanner: dependency: "direct main" description: - name: qr_code_scanner - sha256: f23b68d893505a424f0bd2e324ebea71ed88465d572d26bb8d2e78a4749591fd - url: "https://pub.dev" - source: hosted - version: "1.0.1" + path: "." + ref: a2d31633d4744f72ada87cfa85d221358ab082af + resolved-ref: a2d31633d4744f72ada87cfa85d221358ab082af + url: "https://github.com/juliuscanute/qr_code_scanner" + source: git + version: "1.0.0" qr_flutter: dependency: "direct main" description: diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index 4feeb58eb4..03afdaad57 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -1,4 +1,3 @@ - name: ente_auth description: ente two-factor authenticator version: 4.4.3+443 @@ -8,12 +7,12 @@ environment: sdk: ">=3.0.0 <4.0.0" dependencies: - adaptive_theme: ^3.1.0 # done + adaptive_theme: ^3.1.0 app_links: ^6.3.3 archive: ^4.0.7 auto_size_text: ^3.0.0 base32: ^2.1.3 - bip39: ^1.0.6 #done + bip39: ^1.0.6 bloc: ^9.0.0 clipboard: ^0.1.3 collection: ^1.18.0 # dart @@ -34,10 +33,12 @@ dependencies: ffi: ^2.1.0 figma_squircle: ^0.6.3 file_picker: ^10.2.0 - # https://github.com/incrediblezayed/file_saver/issues/86 - file_saver: ^0.3.0 + file_saver: ^0.3.1 fixnum: ^1.1.0 - fk_user_agent: ^2.1.0 + fk_user_agent: # no package updates on pub.dev + git: + url: https://github.com/flutter-fast-kit/fk_user_agent + ref: 458046cd9a88924e5074d96ba45397219d53b230 flutter: sdk: flutter flutter_animate: ^4.1.0 @@ -53,7 +54,7 @@ dependencies: path: flutter_inappwebview ref: 3e6c4c4a25340cd363af9d38891d88498b90be26 flutter_launcher_icons: ^0.14.1 - flutter_local_authentication: + flutter_local_authentication: # linux fprintd fix is not published on pub.dev git: url: https://github.com/eaceto/flutter_local_authentication ref: 1ac346a04592a05fd75acccf2e01fa3c7e955d96 @@ -77,7 +78,10 @@ dependencies: local_auth_darwin: ^1.2.2 logging: ^1.0.1 modal_bottom_sheet: ^3.0.0 - move_to_background: ^1.0.2 + move_to_background: # no package updates on pub.dev + git: + url: https://github.com/Sayegh7/move_to_background + ref: 91e4d1a9c55b28bf93425d1f12faf410efc1e48d native_dio_adapter: ^1.4.0 otp: ^3.1.1 package_info_plus: ^8.0.2 @@ -88,8 +92,11 @@ dependencies: pointycastle: ^3.7.3 privacy_screen: ^0.0.6 protobuf: ^4.1.0 - qr_code_scanner: ^1.0.1 - qr_flutter: ^4.1.0 + qr_code_scanner: # no package updates on pub.dev + git: + url: https://github.com/juliuscanute/qr_code_scanner + ref: a2d31633d4744f72ada87cfa85d221358ab082af + qr_flutter: ^4.1.0 sentry: ^8.14.2 sentry_flutter: ^8.14.2 share_plus: ^11.0.0 From eecd7ed355b574b1b73970a89508fa96bad33c1a Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Mon, 18 Aug 2025 00:43:39 +0000 Subject: [PATCH 155/156] New Crowdin translations by GitHub Action --- .../base/locales/fr-FR/translation.json | 24 ++++++++--------- .../base/locales/lt-LT/translation.json | 24 ++++++++--------- .../base/locales/ru-RU/translation.json | 24 ++++++++--------- .../base/locales/vi-VN/translation.json | 24 ++++++++--------- .../base/locales/zh-CN/translation.json | 26 +++++++++---------- 5 files changed, 61 insertions(+), 61 deletions(-) diff --git a/web/packages/base/locales/fr-FR/translation.json b/web/packages/base/locales/fr-FR/translation.json index 49522f9ec7..11b5a723c4 100644 --- a/web/packages/base/locales/fr-FR/translation.json +++ b/web/packages/base/locales/fr-FR/translation.json @@ -686,16 +686,16 @@ "shared_favorites": "Favoris partagés", "added_by_name": "Ajouté par {{name}}", "unowned_files_not_processed": "Les fichiers ajoutés par d'autres utilisateurs n'ont pas été traités", - "custom_domains": "", - "custom_domains_desc": "", - "link_your_domain": "", - "domain": "", - "domain_help": "", - "invalid_domain": "", - "already_linked_domain": "", - "add_dns_entry": "", - "add_dns_entry_hint": "", - "custom_domains_help": "", - "num_1": "", - "num_2": "" + "custom_domains": "Domaines personnalisés", + "custom_domains_desc": "Utiliser votre propre domaine lors du partage", + "link_your_domain": "Lier votre domaine", + "domain": "Domaine", + "domain_help": "N'importe quel domaine ou sous-domaine que vous possédez", + "invalid_domain": "Domaine non valide", + "already_linked_domain": "Le domaine déjà lié par un utilisateur", + "add_dns_entry": "Ajouter l'entrée DNS", + "add_dns_entry_hint": "Chez votre fournisseur DNS, ajoutez un CNAME de votre domaine {{host}}", + "custom_domains_help": "Pour plus d'informations, voir l'help", + "num_1": "1", + "num_2": "2" } diff --git a/web/packages/base/locales/lt-LT/translation.json b/web/packages/base/locales/lt-LT/translation.json index 924af32e1e..91d3494e06 100644 --- a/web/packages/base/locales/lt-LT/translation.json +++ b/web/packages/base/locales/lt-LT/translation.json @@ -686,16 +686,16 @@ "shared_favorites": "Bendrinami mėgstami", "added_by_name": "Įtraukė {{name}}", "unowned_files_not_processed": "Kiti naudotojai pridėti failai nebuvo apdoroti", - "custom_domains": "", - "custom_domains_desc": "", - "link_your_domain": "", - "domain": "", - "domain_help": "", - "invalid_domain": "", - "already_linked_domain": "", - "add_dns_entry": "", - "add_dns_entry_hint": "", - "custom_domains_help": "", - "num_1": "", - "num_2": "" + "custom_domains": "Pasirinktiniai domenai", + "custom_domains_desc": "Naudokite savo domeną, kai bendrinate.", + "link_your_domain": "Susieti savo domeną", + "domain": "Domenas", + "domain_help": "Bet kuris jūsų turimas domenas arba subdomenas", + "invalid_domain": "Netinkamas domenas.", + "already_linked_domain": "Domenas jau susietas su naudotoju.", + "add_dns_entry": "Pridėti DNS elementą", + "add_dns_entry_hint": "Savo DNS teikėjoje pridėkite „CNAME“ iš savo domeno į {{host}}.", + "custom_domains_help": "Dėl daugiau informacijos žiūrėkite pagalbą.", + "num_1": "1", + "num_2": "2" } diff --git a/web/packages/base/locales/ru-RU/translation.json b/web/packages/base/locales/ru-RU/translation.json index 7218c486f7..fe0728c1f1 100644 --- a/web/packages/base/locales/ru-RU/translation.json +++ b/web/packages/base/locales/ru-RU/translation.json @@ -686,16 +686,16 @@ "shared_favorites": "Общие избранные", "added_by_name": "Добавлено {{name}}", "unowned_files_not_processed": "Файлы, добавленные другими пользователями, не были обработаны", - "custom_domains": "", - "custom_domains_desc": "", - "link_your_domain": "", - "domain": "", - "domain_help": "", - "invalid_domain": "", - "already_linked_domain": "", - "add_dns_entry": "", - "add_dns_entry_hint": "", - "custom_domains_help": "", - "num_1": "", - "num_2": "" + "custom_domains": "Пользовательские домены", + "custom_domains_desc": "Используйте свой собственный домен при публикации", + "link_your_domain": "Привязать ваш домен", + "domain": "Домен", + "domain_help": "Любой домен или поддомен, которым вы владеете", + "invalid_domain": "Неверный домен", + "already_linked_domain": "Домен уже связан пользователем", + "add_dns_entry": "Добавить DNS-запись", + "add_dns_entry_hint": "На стороне вашего DNS-провайдера добавьте CNAME из вашего домена в {{host}}", + "custom_domains_help": "Для получения дополнительной информации см. помощь", + "num_1": "1", + "num_2": "2" } diff --git a/web/packages/base/locales/vi-VN/translation.json b/web/packages/base/locales/vi-VN/translation.json index 6cf4877fd5..af041df4a8 100644 --- a/web/packages/base/locales/vi-VN/translation.json +++ b/web/packages/base/locales/vi-VN/translation.json @@ -686,16 +686,16 @@ "shared_favorites": "Những mục thích đã chia sẻ", "added_by_name": "Được thêm bởi {{name}}", "unowned_files_not_processed": "Các tệp được thêm bởi người dùng khác không được xử lý", - "custom_domains": "", - "custom_domains_desc": "", - "link_your_domain": "", - "domain": "", - "domain_help": "", - "invalid_domain": "", - "already_linked_domain": "", - "add_dns_entry": "", - "add_dns_entry_hint": "", - "custom_domains_help": "", - "num_1": "", - "num_2": "" + "custom_domains": "Tùy chỉnh tên miền", + "custom_domains_desc": "Dùng tên miền của bạn khi chia sẻ", + "link_your_domain": "Liên kết với tên miền", + "domain": "Tên miền", + "domain_help": "Bất kỳ tên miền hoặc tên miền phụ nào của bạn", + "invalid_domain": "Tên miền không hợp lệ", + "already_linked_domain": "Tên miền đã được liên kết với người dùng khác", + "add_dns_entry": "Thêm entry DNS", + "add_dns_entry_hint": "Trên trình quản lý của nhà cung cấp DNS, thêm một CNAME từ tên miền của bạn đến {{host}}", + "custom_domains_help": "Để biết thêm chi tiết, xem trợ giúp", + "num_1": "1", + "num_2": "2" } diff --git a/web/packages/base/locales/zh-CN/translation.json b/web/packages/base/locales/zh-CN/translation.json index 655aeae950..16b109545f 100644 --- a/web/packages/base/locales/zh-CN/translation.json +++ b/web/packages/base/locales/zh-CN/translation.json @@ -1,5 +1,5 @@ { - "intro_slide_1_title": "私人备份
为您的回忆", + "intro_slide_1_title": "为您的回忆
私密备份", "intro_slide_1": "默认端到端加密", "intro_slide_2_title": "安全地存放
在一个掩护所中", "intro_slide_2": "经久耐用", @@ -686,16 +686,16 @@ "shared_favorites": "已共享的收藏", "added_by_name": "由{{name}}添加", "unowned_files_not_processed": "由其他用户添加的文件未被处理", - "custom_domains": "", - "custom_domains_desc": "", - "link_your_domain": "", - "domain": "", - "domain_help": "", - "invalid_domain": "", - "already_linked_domain": "", - "add_dns_entry": "", - "add_dns_entry_hint": "", - "custom_domains_help": "", - "num_1": "", - "num_2": "" + "custom_domains": "自定义域名", + "custom_domains_desc": "分享时使用您自己的域名", + "link_your_domain": "链接您的域名", + "domain": "域名", + "domain_help": "您拥有的任何域名或子域名", + "invalid_domain": "无效的域名", + "already_linked_domain": "域名已被其他用户链接", + "add_dns_entry": "添加 DNS 条目", + "add_dns_entry_hint": "在您的 DNS 提供商上,将您域中的 CNAME 添加到 {{host}}", + "custom_domains_help": "欲了解更多信息,请参阅 help", + "num_1": "1", + "num_2": "2" } From e82ba882d654eeeee0099f23f58cd220a677f6ba Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Mon, 18 Aug 2025 01:05:17 +0000 Subject: [PATCH 156/156] New Crowdin translations by GitHub Action --- .../metadata/android/id/full_description.txt | 4 +- mobile/apps/photos/lib/l10n/intl_de.arb | 8 + mobile/apps/photos/lib/l10n/intl_fr.arb | 53 +++- mobile/apps/photos/lib/l10n/intl_no.arb | 30 ++- mobile/apps/photos/lib/l10n/intl_sr.arb | 227 ++++++++++++++++++ mobile/apps/photos/lib/l10n/intl_vi.arb | 4 +- mobile/apps/photos/lib/l10n/intl_zh.arb | 51 +++- 7 files changed, 369 insertions(+), 8 deletions(-) diff --git a/mobile/apps/photos/fastlane/metadata/android/id/full_description.txt b/mobile/apps/photos/fastlane/metadata/android/id/full_description.txt index 7b3deb7985..1f79440d32 100644 --- a/mobile/apps/photos/fastlane/metadata/android/id/full_description.txt +++ b/mobile/apps/photos/fastlane/metadata/android/id/full_description.txt @@ -16,7 +16,7 @@ FITUR - Collaborative albums, so you can pool together photos after a trip - Shared folders, in case you want your partner to enjoy your "Camera" clicks - Link album, yang bisa dilindungi dengan sandi -- Ability to free up space, by removing files that have been safely backed up +Kemampuan untuk membebaskan kapasitas, dengan menghilangkan files yang sudah di back-up dengan aman - Human support, because you're worth it - Descriptions, so you can caption your memories and find them easily - Editor gambar, untuk menyempurnakan fotomu @@ -33,4 +33,4 @@ HARGA Kami tidak menyediakan paket yang gratis seumur hidup, karena penting bagi kami untuk tetap berdiri dan bertahan hingga masa depan. Namun, kami menyediakan paket yang terjangkau, yang bisa kamu bagikan dengan keluargamu. Kamu bisa menemukan informasi lebih lanjut di ente.io. DUKUNGAN -We take pride in offering human support. Jika kamu adalah pelanggan berbayar, kamu bisa menghubungi team@ente.io dan menunggu balasan dari tim kami dalam 24 jam. +Kita bangga untuk menyediakan support berbasis manusia. Jika kamu adalah pelanggan berbayar, kamu bisa menghubungi team@ente.io dan menunggu balasan dari tim kami dalam 24 jam. diff --git a/mobile/apps/photos/lib/l10n/intl_de.arb b/mobile/apps/photos/lib/l10n/intl_de.arb index afba351302..8849255874 100644 --- a/mobile/apps/photos/lib/l10n/intl_de.arb +++ b/mobile/apps/photos/lib/l10n/intl_de.arb @@ -1776,6 +1776,14 @@ "same": "Gleich", "different": "Verschieden", "sameperson": "Dieselbe Person?", + "cLTitle1": "Erweiterte Bildbearbeitung", + "cLDesc1": "Wir veröffentlichen eine neue und erweiterte Bildbearbeitung, die mehr Bildzuschnitte ermöglicht, vordefinierte Filter für schnelleres Bearbeiten bietet, sowie die Feinabstimmung von Sättigung, Kontrast, Helligkeit und vielem mehr erlaubt. Der neue Editor erlaubt außerdem das Zeichnen auf den Fotos oder das Hinzufügen von Emojis als Sticker.", + "cLTitle2": "Intelligente Alben", + "cLDesc2": "Du kannst jetzt automatisch Fotos von ausgewählten Personen zu jedem Album hinzufügen. Öffne einfach das Album und wähle \"Personen automatisch hinzufügen\" aus dem Menü. Zusammen mit einem geteilten Album kannst Du Fotos mit null Klicks teilen.", + "cLTitle3": "Verbesserte Galerie", + "cLDesc3": "Wir haben die Möglichkeit hinzugefügt, Alben nach Wochen, Monaten und Jahren zu gruppieren. Du kannst jetzt die Galerie mit diesen neuen Gruppierungs-Optionen so anpassen, dass sie genau so aussieht, wie Du möchtest, zusammen mit angepassten Rastern", + "cLTitle4": "Schnelleres Scrollen", + "cLDesc4": "Zusammen mit einem Schwung Änderungen unter der Haube, um das Erlebnis beim Scrollen der Galerie zu verbessern, haben wir außerdem den Scrollbalken mit Markern neu gestaltet, um es Dir zu ermöglichen, schnell in der Zeitleiste zu springen.", "indexingPausedStatusDescription": "Die Indizierung ist pausiert. Sie wird automatisch fortgesetzt, wenn das Gerät bereit ist. Das Gerät wird als bereit angesehen, wenn sich der Akkustand, die Akkugesundheit und der thermische Zustand in einem gesunden Bereich befinden.", "thisWeek": "Diese Woche", "lastWeek": "Letzte Woche", diff --git a/mobile/apps/photos/lib/l10n/intl_fr.arb b/mobile/apps/photos/lib/l10n/intl_fr.arb index 8f73c4baa1..7bdb63cbd4 100644 --- a/mobile/apps/photos/lib/l10n/intl_fr.arb +++ b/mobile/apps/photos/lib/l10n/intl_fr.arb @@ -1758,7 +1758,7 @@ "wishThemAHappyBirthday": "Souhaitez à {name} un joyeux anniversaire ! 🎉", "areYouSureRemoveThisFaceFromPerson": "Êtes-vous sûr de vouloir retirer ce visage de cette personne ?", "otherDetectedFaces": "Autres visages détectés", - "areThey": "Vraiment", + "areThey": "S'agit-il de ", "questionmark": "?", "saveAsAnotherPerson": "Enregistrer comme une autre personne", "showLessFaces": "Afficher moins de visages", @@ -1776,7 +1776,56 @@ "same": "Identique", "different": "Différent(e)", "sameperson": "Même personne ?", + "cLTitle1": "Éditeur d'image avancé", + "cLDesc1": "Nous déployons un nouvel éditeur d'image avancé qui ajoute plus d'options de rognage, des filtres, des préréglages pour des modifications rapides ainsi que des options de réglage fin (la saturation, le contraste, la luminosité, la température et beaucoup plus). Le nouvel éditeur inclut également la possibilité de dessiner sur vos photos et d'ajouter des emojis en tant qu'autocollants.", + "cLTitle2": "Albums Intelligents", + "cLDesc2": "Vous pouvez maintenant ajouter automatiquement des photos de personnes sélectionnées à n'importe quel album. Allez simplement à l'album et sélectionnez \"Ajouter automatiquement des personnes\" dans le menu déroulant. Couplé avec un album partagé, vous pouvez partager des photos en zéro clic.", + "cLTitle3": "Galerie améliorée", + "cLDesc3": "Nous avons ajouté la possibilité de regrouper votre galerie par semaines, mois et années. Vous pouvez maintenant personnaliser votre galerie pour qu'elle soit exactement comme vous le souhaitez avec ces nouvelles options de regroupement, ainsi que des grilles personnalisées", + "cLTitle4": "Défilement plus rapide", + "cLDesc4": "En plus des quelques améliorations pour améliorer l'expérience de défilement de la galerie, nous avons également redessiné la barre de défilement pour afficher des marqueurs, ce qui vous permet de sauter rapidement dans la chronologie.", "indexingPausedStatusDescription": "L'indexation est en pause. Elle reprendra automatiquement lorsque l'appareil sera prêt. Celui-ci est considéré comme prêt lorsque le niveau de batterie, sa santé et son état thermique sont dans une plage saine.", + "thisWeek": "Cette semaine", + "lastWeek": "La semaine dernière", + "thisMonth": "Ce mois", + "thisYear": "Cette année", + "groupBy": "Grouper par", "faceThumbnailGenerationFailed": "Impossible de créer des miniatures de visage", - "fileAnalysisFailed": "Impossible d'analyser le fichier" + "fileAnalysisFailed": "Impossible d'analyser le fichier", + "editAutoAddPeople": "Modifier les personnes auto-ajoutées", + "autoAddPeople": "Ajouter automatiquement des personnes", + "autoAddToAlbum": "Ajouter automatiquement à l'album", + "shouldRemoveFilesSmartAlbumsDesc": "Les fichiers liés à la personne qui ont été précédemment sélectionnés dans les albums intelligents doivent-ils être supprimés ?", + "addingPhotos": "Ajout des photos", + "gettingReady": "Préparation", + "addSomePhotosDesc1": "Ajoutez quelques photos ou choisissez des ", + "addSomePhotosDesc2": "visages familiers", + "addSomePhotosDesc3": "\npour commencer", + "ignorePerson": "Ignorer la personne", + "mixedGrouping": "Plusieurs personnes?", + "analysis": "Analyse", + "doesGroupContainMultiplePeople": "Ce groupement contient-il plusieurs personnes ?", + "automaticallyAnalyzeAndSplitGrouping": "Nous analyserons automatiquement le regroupement pour déterminer s'il y a plusieurs personnes présentes et nous les séparerons à nouveau si tel est le cas. Cela peut prendre quelques secondes.", + "layout": "Disposition", + "day": "Jour", + "peopleAutoAddDesc": "Sélectionnez les personnes que vous souhaitez ajouter automatiquement à l'album", + "undo": "Annuler", + "redo": "Rétablir", + "filter": "Filtres", + "adjust": "Ajuster", + "draw": "Dessiner", + "sticker": "Autocollant", + "brushColor": "Couleur du pinceau", + "font": "Police", + "background": "Arrière-plan", + "align": "Aligner", + "addedToAlbums": "{count, plural, one {}=1{Ajouté avec succès à 1 album} other{Ajouté avec succès à {count} albums}}", + "@addedToAlbums": { + "description": "Message shown when items are added to albums", + "placeholders": { + "count": { + "type": "int" + } + } + } } \ No newline at end of file diff --git a/mobile/apps/photos/lib/l10n/intl_no.arb b/mobile/apps/photos/lib/l10n/intl_no.arb index 00306d6d95..4dbf2967c0 100644 --- a/mobile/apps/photos/lib/l10n/intl_no.arb +++ b/mobile/apps/photos/lib/l10n/intl_no.arb @@ -1403,6 +1403,16 @@ "enableMachineLearningBanner": "Aktiver maskinlæring for magisk søk og ansiktsgjenkjenning", "searchDiscoverEmptySection": "Bilder vil vises her når behandlingen og synkronisering er fullført", "searchPersonsEmptySection": "Folk vil vises her når behandling og synkronisering er fullført", + "viewersSuccessfullyAdded": "{count, plural, =0 {La til 0 tilskuere} =1 {La til 1 tilskuer} other {La til {count} tilskuer}}", + "@viewersSuccessfullyAdded": { + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + }, + "description": "Number of viewers that were successfully added to an album." + }, "collaboratorsSuccessfullyAdded": "{count, plural, =0 {La til 0 samarbeidspartner} =1 {La til 1 samarbeidspartner} other {Lagt til {count} samarbeidspartnere}}", "@collaboratorsSuccessfullyAdded": { "placeholders": { @@ -1478,6 +1488,15 @@ }, "currentlyRunning": "Kjører for øyeblikket", "ignored": "ignorert", + "photosCount": "{count, plural, =0 {0 bilder} =1 {1 bilde} other {{count} bilder}}", + "@photosCount": { + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + } + }, "file": "Fil", "searchSectionsLengthMismatch": "Uoverensstemmelse i seksjonslengde: {snapshotLength} != {searchLength}", "@searchSectionsLengthMismatch": { @@ -1643,6 +1662,7 @@ "@linkPersonCaption": { "description": "Caption for the 'Link person' title. It should be a continuation of the 'Link person' title. Just like how 'Link person' + 'for better sharing experience' forms a proper sentence in English, the combination of these two strings should also be a proper sentence in other languages." }, + "videoStreaming": "Strømbare videoer", "processingVideos": "Behandler videoer", "streamDetails": "Strømmedetaljer", "processing": "Behandler", @@ -1708,5 +1728,13 @@ "moon": "I månelyset", "onTheRoad": "På veien igjen", "food": "Kulinær glede", - "pets": "Pelsvenner" + "pets": "Pelsvenner", + "curatedMemories": "Kuraterte minner", + "widgets": "Moduler", + "memories": "Minner", + "peopleWidgetDesc": "Velg folkene du ønsker å se på din hjemskjerm.", + "albumsWidgetDesc": "Velg albumene du ønsker å se på din hjemskjerm.", + "memoriesWidgetDesc": "Velg typen minner du ønsker å se på din hjemskjerm.", + "smartMemories": "Smarte minner", + "pastYearsMemories": "Tidligere års minner" } \ No newline at end of file diff --git a/mobile/apps/photos/lib/l10n/intl_sr.arb b/mobile/apps/photos/lib/l10n/intl_sr.arb index d516e7cff7..52f1bd951d 100644 --- a/mobile/apps/photos/lib/l10n/intl_sr.arb +++ b/mobile/apps/photos/lib/l10n/intl_sr.arb @@ -141,7 +141,14 @@ "enterThe6digitCodeFromnyourAuthenticatorApp": "Унесите 6-цифрени кôд из\nапликације за аутентификацију", "confirm": "Потврди", "setupComplete": "Постављање завршено", + "saveYourRecoveryKeyIfYouHaventAlready": "Уколико већ нисте, сачувајте кључ за опоравак", + "thisCanBeUsedToRecoverYourAccountIfYou": "Може се користити за опоравак рачуна уколико заборавите други ниво провере идентитета", + "twofactorAuthenticationPageTitle": "Двострука провера идентитета", "lostDevice": "Изгубили сте уређај?", + "verifyingRecoveryKey": "Проверавам кључ за опоравак...", + "recoveryKeyVerified": "Кључ за опоравак је потврђен", + "recoveryKeySuccessBody": "Сјајно! Ваш кључ за опоравак је исправан. Хвала за потврду.\n\nНе заборавите да овај кључ чувате на сигурном месту.", + "invalidRecoveryKey": "Кључ за опоравак који сте унели није исправан. Постарајте се да садржи 24 речи, и проверите да ли сте их правилно уписали.\n\nУколико сте унели стари формат кључа, постарајте се да садржи 64 карактера и додатно проверите да ли је сваки исправно уписан.", "invalidKey": "Неисправан кључ", "tryAgain": "Покушај поново", "viewRecoveryKey": "Погледај кључ за опоравак", @@ -186,10 +193,13 @@ "disableDownloadWarningTitle": "Молим, обратите пажњу", "disableDownloadWarningBody": "Људи и даље могу да направе снимак екрана или да скину ваше фотографије користећи друге апликације", "allowDownloads": "Дозволи преузимање", + "linkExpiry": "Рок важења линка", "linkExpired": "Истекао", "linkEnabled": "Омогућено", "linkNeverExpires": "Никада", + "expiredLinkInfo": "Линк је истекао. Поставите ново време за престанак важности линка или онемогућите његово истицање.", "setAPassword": "Постави лозинку", + "lockButtonLabel": "Закључај", "enterPassword": "Унеси лозинку", "removeLink": "Уклони везу", "manageLink": "Управљај везом", @@ -269,11 +279,19 @@ "codeChangeLimitReached": "Жао нам је, достигли сте максимум броја промена кôда.", "onlyFamilyAdminCanChangeCode": "Молимо вас да контактирате {familyAdminEmail} да бисте променили свој код.", "storageInGB": "{storageAmountInGB} ГБ", + "claimed": "Освојено", + "@claimed": { + "description": "Used to indicate storage claimed, like 10GB Claimed" + }, "details": "Више детаља", "claimMore": "Освоји још!", + "theyAlsoGetXGb": "Они ће такође добити {storageAmountInGB} ГБ", + "freeStorageOnReferralSuccess": "{storageAmountInGB} ГБ сваки пут када се неко претплати на неку од наших понуда и при том унесе ваш код", "claimFreeStorage": "Освоји бесплатан простор на диску", "inviteYourFriends": "Позови своје пријатеље", "referralStep1": "1. Дај овај код својим пријатељима", + "referralStep2": "2. Они се претплате на понуду која се плаћа", + "referralStep3": "3. Обоје добијате {storageInGB} ГБ* бесплатно", "referralsAreCurrentlyPaused": "Препоручивање је тренутно паузирано", "youCanAtMaxDoubleYourStorage": "* У најбољем случају можете удвостручити ваш простор", "faq": "Честа питања", @@ -296,10 +314,13 @@ "deleteAlbum": "Избриши албум", "deleteSharedAlbumDialogBody": "Албум ће бити обрисан за све", "yesRemove": "Да, уклони", + "creatingLink": "Креирам линк...", "removeWithQuestionMark": "Уклони?", + "removeParticipantBody": "{userEmail} ће бити уклоњен из овог дељеног албума.\n\nСве фотографије које су они додали ће такође бити уклоњене", "keepPhotos": "Задржи фотографије", "deletePhotos": "Обриши фотгорафије", "inviteToEnte": "Позови у Енте", + "disableLinkMessage": "Ово ће уклонити јавни линк за приступ \"{albumName}\".", "sharing": "Делим...", "youCannotShareWithYourself": "Не можеш делити сам са собом", "archive": "Архивирај", @@ -397,6 +418,7 @@ "discover_sunset": "Заласци сунца", "discover_hills": "Брда", "discover_greenery": "Зеленило", + "waitingForWifi": "Чекам на бежичну мрежу...", "status": "Статус", "indexedItems": "Индексиране ставке", "pendingItems": "На чекању", @@ -406,6 +428,28 @@ "unselectAll": "Деселектуј све", "selectAll": "Означи све", "skip": "Прескочи", + "updatingFolderSelection": "Освежавам избор фолдера...", + "duplicateItemsGroup": "{count} фајлова, {formattedSize} сваки", + "@duplicateItemsGroup": { + "description": "Display the number of duplicate files and their size", + "type": "text", + "placeholders": { + "count": { + "example": "12", + "type": "int" + }, + "formattedSize": { + "example": "2.3 MB", + "type": "String" + } + } + }, + "showMemories": "Прикажи успомене", + "yearsAgo": "{count, plural, one{{count} година уназад} few {{count} године уназад} other{{count} година уназад}}", + "backupStatus": "Статус резервних копија", + "backupOverMobileData": "Копирај користећи мобилни интернет", + "backupVideos": "Копирај видео снимке", + "disableAutoLock": "Онемогући закључавање екрана", "about": "О нама", "privacy": "Приватност", "terms": "Услови", @@ -413,17 +457,67 @@ "checkStatus": "Провери статус", "checking": "Проверавам...", "youAreOnTheLatestVersion": "Користите најновију верзију", + "account": "Рачун", "manageSubscription": "Управљај претплатом", "changePassword": "Измени лозинку", + "authToChangeYourPassword": "Потврдите идентитет како бисте променили лозинку", + "emailVerificationToggle": "Провера имејла", + "exportYourData": "Извези своје податке", + "logout": "Одјави се", + "authToInitiateAccountDeletion": "Потврдите идентитет како бисте покренули брисање рачуна", + "areYouSureYouWantToLogout": "Да ли сигурно желите да се одјавите?", + "yesLogout": "Да, одјави ме", "aNewVersionOfEnteIsAvailable": "Нова Енте верзија је доступна.", + "update": "Ажурирај", + "installManually": "Инсталирај ручно", + "criticalUpdateAvailable": "Критично ажурирање је доступно", + "updateAvailable": "Ажурирање доступно", + "ignoreUpdate": "Игнориши", + "cannotDeleteSharedFiles": "Не могу да обришем дељене фајлове", "retry": "Покушај поново", "freeUpDeviceSpace": "Ослободи простор на уређају", + "allClear": "✨ Све је чисто", "noDeviceThatCanBeDeleted": "Не постоје фајлови на овом уређају који би могли бити обрисани", "removeDuplicates": "Уклони дупликате", + "removeDuplicatesDesc": "Прегледај и уклони фајлове који су дупликати.", "viewLargeFiles": "Велики фајлови", + "viewLargeFilesDesc": "Погледај фајлове који заузимају највише простора.", + "noDuplicates": "✨ Нема дупликата", "youveNoDuplicateFilesThatCanBeCleared": "Немаш дупликата које бисмо могли да обришемо", "rateUs": "Оцени нас", + "youHaveSuccessfullyFreedUp": "Успешно сте ослободили {storageSaved}!", + "@youHaveSuccessfullyFreedUp": { + "description": "The text to display when the user has successfully freed up storage", + "type": "text", + "placeholders": { + "storageSaved": { + "example": "1.2 GB", + "type": "String" + } + } + }, + "duplicateFileCountWithStorageSaved": "Обрисали сте {count, plural, one{{count} дупликат} few {{count} дупликата} other{{count} дупликата}}, ослобађам ({storageSaved}!)", + "@duplicateFileCountWithStorageSaved": { + "description": "The text to display when the user has successfully cleaned up duplicate files", + "type": "text", + "placeholders": { + "count": { + "example": "1", + "type": "int" + }, + "storageSaved": { + "example": "1.2 GB", + "type": "String" + } + } + }, + "familyPlans": "Породичне понуде", + "referrals": "Препоруке", "notifications": "Обавештења", + "sharedPhotoNotifications": "Нове дељене фотографије", + "sharedPhotoNotificationsExplanation": "Добиј обавештење када неко дода фотографију у албум у којем ви учествујете", + "advanced": "Напредно", + "general": "Опште", "security": "Сигурност", "no": "Не", "yes": "Да", @@ -435,14 +529,19 @@ "matrix": "Матрикс", "discord": "Дискорд", "reddit": "Редит", + "yourStorageDetailsCouldNotBeFetched": "Подаци о вашем простору на диску нису могли бити освежени", "reportABug": "Пријави грешку", "reportBug": "Пријави грешку", + "suggestFeatures": "Предложи нову функционалност", "support": "Подршка", "theme": "Тема", "lightTheme": "Светла", "darkTheme": "Тамна", "systemTheme": "Системска", "freeTrial": "Бесплатна проба", + "selectYourPlan": "Изабери пакет", + "enteSubscriptionPitch": "Енте чува твоје успомене тако да су увек са тобом, чак и ако изгубиш уређај.", + "enteSubscriptionShareWithFamily": "Можеш додати и породицу у оквиру овог пакета.", "currentUsageIs": "Тренутно искоришћено", "@currentUsageIs": { "description": "This text is followed by storage usage", @@ -452,7 +551,12 @@ "type": "text" }, "faqs": "Често постављана питања", + "renewsOn": "Претплата се обнавља {endDate}", "freeTrialValidTill": "Бесплатан пробни период важи до {endDate}", + "validTill": "Важи до {endDate}", + "addOnValidTill": "Ваш {storageAmount} додатак важи до {endDate}", + "playStoreFreeTrialValidTill": "Бесплатни пробни период важи до {endDate}.\nМожете изабрати неки од планова након тога.", + "subWillBeCancelledOn": "Ваша претплата ће бити отказана {endDate}", "subscription": "Претплата", "paymentDetails": "Детаљи плаћања", "manageFamily": "Управљај породицом", @@ -462,12 +566,119 @@ "yesRenew": "Да, обнови претплату", "areYouSureYouWantToCancel": "Да ли сте сигурни да желите да откажете?", "yesCancel": "Да, откажи", + "failedToCancel": "Неуспешно отказивање", + "twoMonthsFreeOnYearlyPlans": "2 месеца гратис уз годишњу претплату", + "monthly": "Месечно", + "@monthly": { + "description": "The text to display for monthly plans", + "type": "text" + }, + "yearly": "Годишње", + "@yearly": { + "description": "The text to display for yearly plans", + "type": "text" + }, + "confirmPlanChange": "Потврди промену пакета", + "areYouSureYouWantToChangeYourPlan": "Да ли сигурно желиш да промениш пакет?", + "youCannotDowngradeToThisPlan": "Не можете изабрати овај пакет", + "cancelOtherSubscription": "Молимо, прво откажите претплау на {paymentProvider}", + "@cancelOtherSubscription": { + "description": "The text to display when the user has an existing subscription from a different payment provider", + "type": "text", + "placeholders": { + "paymentProvider": { + "example": "Apple", + "type": "String" + } + } + }, + "optionalAsShortAsYouLike": "Опционо, коико год кратко желите...", + "send": "Пошаљи", + "askCancelReason": "Отказали сте претплату. Да ли бисте нам рекли разлог?", + "thankYouForSubscribing": "Хвала што сте се претплатили!", + "yourPurchaseWasSuccessful": "Куповина успешно закључена", + "yourPlanWasSuccessfullyUpgraded": "Успешно сте прешли на јачи пакет", + "yourPlanWasSuccessfullyDowngraded": "Успешно сте прешли на слабији пакет", + "yourSubscriptionWasUpdatedSuccessfully": "Претплата успешно ажурирана", + "googlePlayId": "Гугл плеј идентификација", + "appleId": "Епл идентификација", + "playstoreSubscription": "Претплата путем Гугл продавнице", + "appstoreSubscription": "Претплата путем Епл продавнице", + "visitWebToManage": "Претплатом можете управљати на страници web.ente.io", + "couldNotUpdateSubscription": "Неуспешно ажурирање претплате", + "pleaseContactSupportAndWeWillBeHappyToHelp": "Контактирајте подршку на support@ente.io и радо ћемо вам помоћи!", + "paymentFailed": "Неуспела уплата", + "continueOnFreeTrial": "Настави бесплатни пробни период", + "areYouSureYouWantToExit": "Сигурно желите да изађете?", + "thankYou": "Хвала вам", + "pleaseWaitForSometimeBeforeRetrying": "Мало сачекајте пре него што покушате поново", + "youAreOnAFamilyPlan": "Ви сте на породичном пакету!", + "leaveFamily": "Напусти породицу", + "areYouSureThatYouWantToLeaveTheFamily": "Да ли заиста желиш да напустиш породични пакет?", + "leave": "Напусти", + "rateTheApp": "Оцените апликацију", + "startBackup": "Започни сигурносно копирање", + "preserveMore": "Сачувај још", + "grantFullAccessPrompt": "Дозволите приступ свим фотографијама у подешавањима уређаја", + "allowPermTitle": "Дозволи приступ фотографијама", "openSettings": "Отвори подешавања", "selectMorePhotos": "Изабери још фотографија", "existingUser": "Постојећи корисник", + "privateBackups": "Приватни бекапи", "forYourMemories": "за твоје успомене", + "safelyStored": "Сигурно похрањено", "atAFalloutShelter": "у подземном бункеру", + "designedToOutlive": "Направљено да надживи", + "available": "Доступно", + "everywhere": "свуда", + "newToEnte": "Нови на Енте", + "pleaseLoginAgain": "Молимо, улогујте се поново", + "autoLogoutMessage": "Излоговани сте услед техничке грешке. Извињавамо се за непријатност.", + "yourSubscriptionHasExpired": "Ваша претплата је истекла", + "storageLimitExceeded": "Расположив простор је прекорачен", + "upgrade": "Ажурирај", + "raiseTicket": "Пошаљи упит", + "@raiseTicket": { + "description": "Button text for raising a support tickets in case of unhandled errors during backup", + "type": "text" + }, + "backupFailed": "Неуспешна израда резервне копије", + "sorryBackupFailedDesc": "Извините, нисмо успели да копирамо овај фајл тренутно. Покушаћемо касније.", + "couldNotBackUpTryLater": "Нисмо могли да бекапујемо ваше податке.\nПокушаћемо касније.", + "enteCanEncryptAndPreserveFilesOnlyIfYouGrant": "Енте може да екриптује и сачува фајлове једино ако дозволите приступ њима", + "pleaseGrantPermissions": "Молимо, дозволите приступ", + "grantPermission": "Дозволите приступ", + "privateSharing": "Приватно дељење", + "shareOnlyWithThePeopleYouWant": "Дели само са људима са којима желиш", + "usePublicLinksForPeopleNotOnEnte": "Користи јавне линкове за људима који нису на Енте", + "allowPeopleToAddPhotos": "Дозволи људима да додају фотографије", + "shareAnAlbumNow": "Подели албум сада", + "collectEventPhotos": "Прикупи фотографије са догађаја", + "@onDevice": { + "description": "The text displayed above folders/albums stored on device", + "type": "text" + }, + "onDevice": "На уређају", + "@onEnte": { + "description": "The text displayed above albums backed up to Ente", + "type": "text" + }, + "onEnte": "У Енте", + "name": "Име", + "newest": "Најновије", + "lastUpdated": "Последње ажурирано", "deleteEmptyAlbums": "Избриши празне албуме", + "deleteEmptyAlbumsWithQuestionMark": "Обриши празне албуме?", + "permanentlyDelete": "Трајно избриши", + "canOnlyCreateLinkForFilesOwnedByYou": "Можете направити линк само за фајлове који су ваше власништво", + "linkCopiedToClipboard": "Линк је копиран у меморију", + "restore": "Поврати", + "@restore": { + "description": "Display text for an action which triggers a restore of item from trash", + "type": "text" + }, + "moveToAlbum": "Премести у албум", + "unarchive": "Врати из архиве", "createCollage": "Направи колаж", "saveCollage": "Сачувај колаж", "addToEnte": "Додај у Енте", @@ -475,6 +686,22 @@ "delete": "Обриши", "hide": "Сакриј", "share": "Подели", + "searchByAlbumNameHint": "Име албума", + "albumTitle": "Наслов албума", + "enterAlbumName": "Унесите име албума", + "movingFilesToAlbum": "Премештам фајлове у албум...", + "addedSuccessfullyTo": "Успешно додато у {albumName}", + "movedSuccessfullyTo": "Успешно премештено у {albumName}", + "thisAlbumAlreadyHDACollaborativeLink": "Овај албум већ има линк за сарадњу", + "collaborativeLinkCreatedFor": "Линк за сарадњу креиран за {albumName}", + "askYourLovedOnesToShare": "Замолите ваше вољене да деле", + "invite": "Позови", + "shareYourFirstAlbum": "Подели твој први албум", + "sharedWith": "Подељено са {emailIDs}", + "sharedWithMe": "Подељено са мном", + "sharedByMe": "Ја поделио", + "doubleYourStorage": "Удвостручи простор", + "referFriendsAnd2xYourPlan": "Препоручи пријатељима и удвостручи тренутни пакет", "rename": "Преименуј", "thisEmailIsAlreadyInUse": "Имејл је већ у употреби", "yourVerificationCodeHasExpired": "Ваш код ѕа верификацију је истекао", diff --git a/mobile/apps/photos/lib/l10n/intl_vi.arb b/mobile/apps/photos/lib/l10n/intl_vi.arb index d609d51994..6392a8957d 100644 --- a/mobile/apps/photos/lib/l10n/intl_vi.arb +++ b/mobile/apps/photos/lib/l10n/intl_vi.arb @@ -241,7 +241,7 @@ "linkHasExpired": "Liên kết đã hết hạn", "publicLinkEnabled": "Liên kết công khai đã được bật", "shareALink": "Chia sẻ một liên kết", - "sharedAlbumSectionDescription": "Tạo album chia sẻ và cộng tác với người dùng Ente khác, bao gồm cả người dùng các gói miễn phí.", + "sharedAlbumSectionDescription": "Tạo album chia sẻ và cộng tác với người dùng Ente khác, bao gồm người dùng gói miễn phí.", "shareWithPeopleSectionTitle": "{numberOfPeople, plural, =0 {Chia sẻ với những người cụ thể} =1 {Chia sẻ với 1 người} other {Chia sẻ với {numberOfPeople} người}}", "@shareWithPeopleSectionTitle": { "placeholders": { @@ -293,7 +293,7 @@ "theyAlsoGetXGb": "Họ cũng nhận được {storageAmountInGB} GB", "freeStorageOnReferralSuccess": "{storageAmountInGB} GB mỗi khi ai đó đăng ký gói trả phí và áp dụng mã của bạn", "shareTextReferralCode": "Mã giới thiệu Ente: {referralCode} \n\nÁp dụng nó trong Cài đặt → Chung → Giới thiệu để nhận thêm {referralStorageInGB} GB miễn phí sau khi bạn đăng ký gói trả phí\n\nhttps://ente.io", - "claimFreeStorage": "Nhận thêm dung lượng miễn phí", + "claimFreeStorage": "Nhận thêm dung lượng", "inviteYourFriends": "Mời bạn bè của bạn", "failedToFetchReferralDetails": "Không thể lấy thông tin giới thiệu. Vui lòng thử lại sau.", "referralStep1": "1. Đưa mã này cho bạn bè của bạn", diff --git a/mobile/apps/photos/lib/l10n/intl_zh.arb b/mobile/apps/photos/lib/l10n/intl_zh.arb index bb6219ced3..5ddb76d98f 100644 --- a/mobile/apps/photos/lib/l10n/intl_zh.arb +++ b/mobile/apps/photos/lib/l10n/intl_zh.arb @@ -1776,7 +1776,56 @@ "same": "相同", "different": "不同", "sameperson": "是同一个人?", + "cLTitle1": "高级图像编辑器", + "cLDesc1": "我们正在发布一款全新且高级的图像编辑器,新增更多裁剪框架、快速编辑的滤镜预设,以及包括饱和度、对比度、亮度、色温等在内的精细调整选项。新的编辑器还支持在照片上绘制和添加表情符号作为贴纸。", + "cLTitle2": "智能相册", + "cLDesc2": "您现在可以将所选人物的照片自动添加到任何相册。只需进入相册,从溢出菜单中选择“自动添加人物”。如果与共享相册一起使用,您可以零点击分享照片。", + "cLTitle3": "改进的相册", + "cLDesc3": "我们新增了按周、月、年对图库进行分组的功能。您现在可以通过这些新的分组选项以及自定义网格,定制图库的外观,完全按照您的喜好进行设置", + "cLTitle4": "更快滚动", + "cLDesc4": "除了多项后台改进以提升图库滚动体验外,我们还重新设计了滚动条,添加了标记功能,让您可以快速跳转到时间轴上的不同位置。", "indexingPausedStatusDescription": "索引已暂停。待设备准备就绪后,索引将自动恢复。当设备的电池电量、电池健康度和温度状态处于健康范围内时,设备即被视为准备就绪。", + "thisWeek": "本周", + "lastWeek": "上周", + "thisMonth": "本月", + "thisYear": "今年", + "groupBy": "分组依据", "faceThumbnailGenerationFailed": "无法生成人脸缩略图", - "fileAnalysisFailed": "无法分析文件" + "fileAnalysisFailed": "无法分析文件", + "editAutoAddPeople": "编辑自动添加人物", + "autoAddPeople": "自动添加人物", + "autoAddToAlbum": "自动添加到相册", + "shouldRemoveFilesSmartAlbumsDesc": "应该移除之前在智能相册中选择的与该人相关的文件吗?", + "addingPhotos": "正在添加照片", + "gettingReady": "准备就绪", + "addSomePhotosDesc1": "添加一些照片或者选择 ", + "addSomePhotosDesc2": "熟悉的面孔", + "addSomePhotosDesc3": "首先", + "ignorePerson": "忽略此人", + "mixedGrouping": "混合分组?", + "analysis": "分析", + "doesGroupContainMultiplePeople": "这个分组包含多个人吗?", + "automaticallyAnalyzeAndSplitGrouping": "我们将自动分析分组以确定是否有多人在场,并再次将他们分开。这可能需要几秒钟。", + "layout": "布局", + "day": "日", + "peopleAutoAddDesc": "选择您想要自动添加到相册的人", + "undo": "撤销", + "redo": "重做", + "filter": "筛选", + "adjust": "调整", + "draw": "绘制", + "sticker": "贴纸", + "brushColor": "笔刷颜色", + "font": "字体", + "background": "背景", + "align": "对齐", + "addedToAlbums": "{count, plural, =1{已成功添加到 1 个相册} other{已成功添加到 {count} 个相册}}", + "@addedToAlbums": { + "description": "Message shown when items are added to albums", + "placeholders": { + "count": { + "type": "int" + } + } + } } \ No newline at end of file