Function bodies 677 total
SqlDbRepo class · dart · L72-L245 (174 LOC)lib/module/history/sql_db_repo.dart
class SqlDbRepo extends StateViewModel<SqlDbRepoState> {
SqlDbRepo({int pageSize = 500})
: _pageSize = pageSize,
super(state: SqlDbRepoState.initial);
DriftHistoryRepository get _repository =>
viewModelBinding.read(historyRepositorySpec);
final int _pageSize;
int _reloadToken = 0;
bool _loadingMoreInternal = false;
HistoryPageCursor? _nextPageCursor;
@override
void onCreate(InstanceArg arg) {
super.onCreate(arg);
unawaited(refresh());
}
Future<void> refresh() async {
final token = ++_reloadToken;
setState(state.copyWith(loading: true, clearError: true));
try {
final groups = await _repository.loadGroups();
final selectedGroupId = _sanitizeGroupId(state.selectedGroupId, groups);
final page = await _repository.queryPage(
query: state.query,
groupId: selectedGroupId,
pageSize: _pageSize,
);
if (token != _reloadToken) {
return;
}
_nextPageCursor = page.nextCQuickPanelAutoPasteService class · dart · L8-L60 (53 LOC)lib/module/hotkey/quick_panel_auto_paste_service.dart
class QuickPanelAutoPasteService with ViewModel {
QuickPanelAutoPasteService();
static const String _enabledKey = 'quick_panel_auto_paste_enabled_v1';
static const String _firstLaunchGuideShownKey =
'quick_panel_accessibility_guide_shown_v1';
static const String _quickPanelGuideShownKey =
'quick_panel_shortcut_guide_shown_v1';
static const String _logScope = 'auto_paste.service';
AppLogService get _log => viewModelBinding.read(appLogServiceSpec);
Future<bool> isEnabled() async {
final enabled =
await AppKvRepository.instance.getBool(_enabledKey) ?? false;
unawaited(
_log.info(_logScope, 'is_enabled_read', fields: {'enabled': enabled}),
);
return enabled;
}
Future<void> setEnabled(bool enabled) async {
await AppKvRepository.instance.setBool(_enabledKey, enabled);
unawaited(
_log.info(_logScope, 'set_enabled', fields: {'enabled': enabled}),
);
}
/// 首次启动时消费快捷面板引导标记,返回 true 表示需要展示引导。
Future<bool> consQuickPanelShortcutService class · dart · L15-L407 (393 LOC)lib/module/hotkey/quick_panel_shortcut_service.dart
class QuickPanelShortcutService with ViewModel {
QuickPanelShortcutService._();
static const String _storageKey = 'quick_panel_hotkey_v1';
static const String _searchStorageKey = 'quick_panel_search_hotkey_v1';
static final HotKey defaultHotKey = HotKey(
key: PhysicalKeyboardKey.keyV,
modifiers: <HotKeyModifier>[
defaultTargetPlatform == TargetPlatform.macOS
? HotKeyModifier.meta
: HotKeyModifier.control,
HotKeyModifier.shift,
],
scope: HotKeyScope.system,
);
static final HotKey defaultSearchHotKey = HotKey(
key: PhysicalKeyboardKey.keyF,
modifiers: <HotKeyModifier>[HotKeyModifier.alt, HotKeyModifier.shift],
scope: HotKeyScope.system,
);
static const List<HotKeyModifier> _allowedModifiers = <HotKeyModifier>[
HotKeyModifier.meta,
HotKeyModifier.control,
HotKeyModifier.alt,
HotKeyModifier.shift,
];
final StreamController<void> _triggerController =
StreamController<void>.broadcast();ShortcutRegistrationException class · dart · L409-L411 (3 LOC)lib/module/hotkey/quick_panel_shortcut_service.dart
class ShortcutRegistrationException implements Exception {
const ShortcutRegistrationException();
}AppShutdownViewModel class · dart · L7-L35 (29 LOC)lib/module/init/app_shutdown_view_model.dart
class AppShutdownViewModel with ViewModel {
DriftHistoryRepository get _historyRepository =>
viewModelBinding.read(historyRepositorySpec);
AppLogService get _appLogService => viewModelBinding.read(appLogServiceSpec);
Future<void>? _shutdownFuture;
Future<void> shutdown() {
return _shutdownFuture ??= _shutdown();
}
Future<void> _shutdown() async {
try {
await _historyRepository.close();
} catch (_) {
// 应用即将退出时优先继续收尾,避免单点失败阻断后续关闭。
}
try {
await AppKvRepository.instance.close();
} catch (_) {
// Ignore shutdown errors during app exit.
}
try {
await _appLogService.shutdown();
} catch (_) {
// Ignore shutdown errors during app exit.
}
}
}StartInit class · dart · L13-L56 (44 LOC)lib/module/init/start_init.dart
class StartInit with ViewModelBinding {
StartInit._();
static final StartInit instance = StartInit._();
QuickPanelShortcutService get _quickPanelShortcutService =>
viewModelBinding.read(quickPanelShortcutServiceSpec);
AppLogService get _appLogService =>
viewModelBinding.read(appLogServiceSpec);
MainWindowViewModel get _mainWindowViewModel =>
viewModelBinding.read(mainWindowViewModelSpec);
ClipboardIngestionService get _clipboardIngestionService =>
viewModelBinding.read(clipboardIngestionServiceSpec);
PanelCoordinatorViewModel get _panelCoordinator =>
viewModelBinding.read(panelCoordinatorViewModelSpec);
@override
Future<void> init() async {
WidgetsFlutterBinding.ensureInitialized();
if (!kIsWeb && isDesktopPlatform(defaultTargetPlatform)) {
await windowManager.ensureInitialized();
final options = _mainWindowViewModel.initialWindowOptions;
await windowManager.waitUntilReadyToShow(options, () async {
awAppLogExportSaveLocationPicker class · dart · L23-L87 (65 LOC)lib/module/log/app_log_export_save_location_picker.dart
class AppLogExportSaveLocationPicker with ViewModel {
AppLogExportSaveLocationPicker({
GetSaveLocationCallback? getSaveLocationCallback,
PrepareWindowForDialogCallback? prepareWindowForDialogCallback,
}) : _getSaveLocation = getSaveLocationCallback ?? getSaveLocation,
_prepareWindowForDialog =
prepareWindowForDialogCallback ?? _focusWindowForDialog;
static const XTypeGroup _logTypeGroup = XTypeGroup(
label: 'Log files',
extensions: <String>['log'],
);
final GetSaveLocationCallback _getSaveLocation;
final PrepareWindowForDialogCallback _prepareWindowForDialog;
Future<String?> pickSaveLocation({String? lastExportPath}) async {
await _prepareWindowForDialogSafely();
final location = await _getSaveLocation(
acceptedTypeGroups: const <XTypeGroup>[_logTypeGroup],
initialDirectory: _resolveInitialDirectory(lastExportPath),
suggestedName: buildAppLogExportFileName(),
confirmButtonText: 'Export',
canCreateDRepobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
AppLogExportResult class · dart · L15-L23 (9 LOC)lib/module/log/app_log_service.dart
class AppLogExportResult {
const AppLogExportResult({
required this.exportPath,
required this.revealedInFinder,
});
final String exportPath;
final bool revealedInFinder;
}DiagnosticLogs class · dart · L10-L24 (15 LOC)lib/module/log/data/app_log_database.dart
class DiagnosticLogs extends Table {
IntColumn get id => integer().autoIncrement()();
IntColumn get createdAtMs => integer()();
TextColumn get level => text()();
TextColumn get origin => text()();
TextColumn get scope => text()();
TextColumn get message => text()();
TextColumn get fieldsJson => text().nullable()();
}AppLogDatabase class · dart · L27-L50 (24 LOC)lib/module/log/data/app_log_database.dart
class AppLogDatabase extends _$AppLogDatabase {
AppLogDatabase() : super(_openConnection());
@override
int get schemaVersion => 1;
@override
MigrationStrategy get migration => MigrationStrategy(
onCreate: (Migrator m) async {
await m.createAll();
await _createIndexes();
},
onUpgrade: (Migrator m, int from, int to) async {
await _createIndexes();
},
);
Future<void> _createIndexes() async {
await customStatement(
'CREATE INDEX IF NOT EXISTS idx_diagnostic_logs_created_at '
'ON diagnostic_logs(created_at_ms DESC, id DESC)',
);
}
}DiagnosticLog class · dart · L205-L341 (137 LOC)lib/module/log/data/app_log_database.g.dart
class DiagnosticLog extends DataClass implements Insertable<DiagnosticLog> {
final int id;
final int createdAtMs;
final String level;
final String origin;
final String scope;
final String message;
final String? fieldsJson;
const DiagnosticLog({
required this.id,
required this.createdAtMs,
required this.level,
required this.origin,
required this.scope,
required this.message,
this.fieldsJson,
});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<int>(id);
map['created_at_ms'] = Variable<int>(createdAtMs);
map['level'] = Variable<String>(level);
map['origin'] = Variable<String>(origin);
map['scope'] = Variable<String>(scope);
map['message'] = Variable<String>(message);
if (!nullToAbsent || fieldsJson != null) {
map['fields_json'] = Variable<String>(fieldsJson);
}
return map;
}
DiagnosticLogsCompanion toCompanion(bool nDiagnosticLogsCompanion class · dart · L343-L453 (111 LOC)lib/module/log/data/app_log_database.g.dart
class DiagnosticLogsCompanion extends UpdateCompanion<DiagnosticLog> {
final Value<int> id;
final Value<int> createdAtMs;
final Value<String> level;
final Value<String> origin;
final Value<String> scope;
final Value<String> message;
final Value<String?> fieldsJson;
const DiagnosticLogsCompanion({
this.id = const Value.absent(),
this.createdAtMs = const Value.absent(),
this.level = const Value.absent(),
this.origin = const Value.absent(),
this.scope = const Value.absent(),
this.message = const Value.absent(),
this.fieldsJson = const Value.absent(),
});
DiagnosticLogsCompanion.insert({
this.id = const Value.absent(),
required int createdAtMs,
required String level,
required String origin,
required String scope,
required String message,
this.fieldsJson = const Value.absent(),
}) : createdAtMs = Value(createdAtMs),
level = Value(level),
origin = Value(origin),
scope = Value(scope),
mes_ class · dart · L455-L464 (10 LOC)lib/module/log/data/app_log_database.g.dart
abstract class _$AppLogDatabase extends GeneratedDatabase {
_$AppLogDatabase(QueryExecutor e) : super(e);
$AppLogDatabaseManager get managers => $AppLogDatabaseManager(this);
late final $DiagnosticLogsTable diagnosticLogs = $DiagnosticLogsTable(this);
@override
Iterable<TableInfo<Table, Object?>> get allTables =>
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
@override
List<DatabaseSchemaEntity> get allSchemaEntities => [diagnosticLogs];
}StoredDiagnosticLogEntry class · dart · L8-L24 (17 LOC)lib/module/log/data/app_log_repository.dart
class StoredDiagnosticLogEntry {
const StoredDiagnosticLogEntry({
required this.createdAtMs,
required this.level,
required this.origin,
required this.scope,
required this.message,
required this.fieldsJson,
});
final int createdAtMs;
final String level;
final String origin;
final String scope;
final String message;
final String? fieldsJson;
}DriftAppLogRepository class · dart · L26-L163 (138 LOC)lib/module/log/data/app_log_repository.dart
class DriftAppLogRepository with ViewModel {
DriftAppLogRepository({AppLogDatabase? database})
: _db = database ?? AppLogDatabase();
final AppLogDatabase _db;
bool _closed = false;
Future<void> close() async {
if (_closed) {
return;
}
_closed = true;
await _db.close();
}
Future<void> insert({
required int createdAtMs,
required String level,
required String origin,
required String scope,
required String message,
Map<String, Object?> fields = const <String, Object?>{},
}) {
return _db
.into(_db.diagnosticLogs)
.insert(
DiagnosticLogsCompanion.insert(
createdAtMs: createdAtMs,
level: level,
origin: origin,
scope: scope,
message: message,
fieldsJson: Value(fields.isEmpty ? null : jsonEncode(fields)),
),
);
}
Future<int> count() async {
final countExpression = _db.diagnosticLogs.id.count();
finWant this analysis on your repo? https://repobility.com/scan/
ToggleInAppPanelEvent class · dart · L7-L11 (5 LOC)lib/module/panel/panel_coordinator_event.dart
class ToggleInAppPanelEvent extends PanelCoordinatorEvent {
const ToggleInAppPanelEvent({required this.minimizeWindowOnDismiss});
final bool minimizeWindowOnDismiss;
}ShowErrorToastEvent class · dart · L14-L18 (5 LOC)lib/module/panel/panel_coordinator_event.dart
class ShowErrorToastEvent extends PanelCoordinatorEvent {
const ShowErrorToastEvent(this.message);
final String message;
}RequestMainRefreshEvent class · dart · L22-L24 (3 LOC)lib/module/panel/panel_coordinator_event.dart
class RequestMainRefreshEvent extends PanelCoordinatorEvent {
const RequestMainRefreshEvent();
}TogglePanelWithSearchEvent class · dart · L28-L30 (3 LOC)lib/module/panel/panel_coordinator_event.dart
class TogglePanelWithSearchEvent extends PanelCoordinatorEvent {
const TogglePanelWithSearchEvent();
}_ResolvedPanelRequest class · dart · L751-L767 (17 LOC)lib/module/panel/panel_coordinator_view_model.dart
class _ResolvedPanelRequest {
const _ResolvedPanelRequest({
required this.key,
required this.groups,
required this.requestedGroupId,
required this.selectedGroupId,
required this.query,
required this.pageSize,
});
final _PanelRequestKey key;
final List<ClipboardGroup> groups;
final String? requestedGroupId;
final String? selectedGroupId;
final String query;
final int pageSize;
}_PanelRequestKey class · dart · L769-L784 (16 LOC)lib/module/panel/panel_coordinator_view_model.dart
class _PanelRequestKey {
const _PanelRequestKey({required this.groupId, required this.query});
final String? groupId;
final String query;
@override
bool operator ==(Object other) {
return other is _PanelRequestKey &&
other.groupId == groupId &&
other.query == query;
}
@override
int get hashCode => Object.hash(groupId, query);
}_PanelPageCacheEntry class · dart · L786-L886 (101 LOC)lib/module/panel/panel_coordinator_view_model.dart
class _PanelPageCacheEntry {
const _PanelPageCacheEntry({
required this.key,
required this.groups,
required this.items,
required this.requestedGroupId,
required this.selectedGroupId,
required this.query,
required this.pageSize,
required this.totalCount,
});
final _PanelRequestKey key;
final List<ClipboardGroup> groups;
final List<ClipboardItem> items;
final String? requestedGroupId;
final String? selectedGroupId;
final String query;
final int pageSize;
final int totalCount;
_PanelPageCacheEntry copyWith({
_PanelRequestKey? key,
List<ClipboardGroup>? groups,
List<ClipboardItem>? items,
String? requestedGroupId,
bool clearRequestedGroupId = false,
String? selectedGroupId,
bool clearSelectedGroupId = false,
String? query,
int? pageSize,
int? totalCount,
}) {
return _PanelPageCacheEntry(
key: key ?? this.key,
groups: groups ?? this.groups,
items: items ?? this.items,
_PanelActionFlutterHandler class · dart · L905-L923 (19 LOC)lib/module/panel/panel_coordinator_view_model.dart
class _PanelActionFlutterHandler implements QuickPanelActionFlutterApi {
_PanelActionFlutterHandler({
required this.onItemPicked,
required this.onItemAction,
});
final Future<void> Function(PanelItemPickedMessage payload) onItemPicked;
final Future<bool> Function(PanelItemActionMessage action) onItemAction;
@override
void onPanelItemPicked(PanelItemPickedMessage payload) {
unawaited(onItemPicked(payload));
}
@override
Future<bool> onPanelItemAction(PanelItemActionMessage action) {
return onItemAction(action);
}
}Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
_PanelDataFlutterHandler class · dart · L925-L945 (21 LOC)lib/module/panel/panel_coordinator_view_model.dart
class _PanelDataFlutterHandler implements QuickPanelDataFlutterApi {
_PanelDataFlutterHandler({
required this.onRequestPage,
required this.onRequestItemContent,
});
final Future<PanelPageMessage> Function(PanelPageRequestMessage request)
onRequestPage;
final Future<PanelItemContentMessage> Function(String itemId)
onRequestItemContent;
@override
Future<PanelPageMessage> requestPanelPage(PanelPageRequestMessage request) {
return onRequestPage(request);
}
@override
Future<PanelItemContentMessage> requestPanelItemContent(String itemId) {
return onRequestItemContent(itemId);
}
}_PanelSettingsFlutterHandler class · dart · L947-L956 (10 LOC)lib/module/panel/panel_coordinator_view_model.dart
class _PanelSettingsFlutterHandler implements QuickPanelSettingsFlutterApi {
_PanelSettingsFlutterHandler({required this.onRequestWindowSettings});
final Future<PanelWindowSettingsMessage> Function() onRequestWindowSettings;
@override
Future<PanelWindowSettingsMessage> requestPanelWindowSettings() {
return onRequestWindowSettings();
}
}MacosAccessibilityService class · dart · L9-L100 (92 LOC)lib/module/permission/macos_accessibility_service.dart
class MacosAccessibilityService with ViewModel {
MacosAccessibilityService();
static final pigeon.AccessibilityHostApi _hostApi =
pigeon.AccessibilityHostApi();
static bool get isSupported => isNativeBridgePlatform;
AppLogService get _appLogService =>
viewModelBinding.read(appLogServiceSpec);
Future<bool> isAccessibilityGranted() {
return _invokeBool(
() => _hostApi.isAccessibilityGranted(),
method: 'isAccessibilityGranted',
scope: 'accessibility.bridge',
);
}
Future<bool> requestAccessibilityPermission() {
return _invokeBool(
() => _hostApi.requestAccessibilityPermission(),
method: 'requestAccessibilityPermission',
scope: 'accessibility.permission',
);
}
Future<bool> openAccessibilitySettings() {
return _invokeBool(
() => _hostApi.openAccessibilitySettings(),
method: 'openAccessibilitySettings',
scope: 'accessibility.permission',
);
}
Future<bool> performQuickPanelAutFileTypeInfo class · dart · L6-L22 (17 LOC)lib/module/recognition/file_type_detector.dart
class FileTypeInfo {
const FileTypeInfo({
required this.mimeType,
required this.extension,
required this.fileSizeBytes,
required this.isTextLike,
this.textPreview,
});
final String mimeType;
final String extension;
final int fileSizeBytes;
final bool isTextLike;
/// 文本类文件的前 N 个字符预览。
final String? textPreview;
}FileTypeDetector class · dart · L24-L301 (278 LOC)lib/module/recognition/file_type_detector.dart
class FileTypeDetector {
const FileTypeDetector._();
/// 文本预览最大字符数。
static const int _maxPreviewChars = 500;
/// 检测文件类型,优先用后缀,后缀不可识别时用 magic number 兜底。
static Future<FileTypeInfo> detect(String filePath) async {
final file = File(filePath);
final sizeBytes = await file.length();
final ext = _extractExtension(filePath);
// 后缀优先查找。
final mimeByExt = _extensionToMime[ext];
if (mimeByExt != null) {
final isText = _textLikeExtensions.contains(ext);
final preview = isText ? await _readTextPreview(file) : null;
return FileTypeInfo(
mimeType: mimeByExt,
extension: ext,
fileSizeBytes: sizeBytes,
isTextLike: isText,
textPreview: preview,
);
}
// Magic number 兜底。
final magicMime = await _detectByMagicNumber(filePath);
if (magicMime != null) {
return FileTypeInfo(
mimeType: magicMime,
extension: ext,
fileSizeBytes: sizeBytes,
isTextLike: false,RecognitionResult class · dart · L3-L13 (11 LOC)lib/module/recognition/recognition_service.dart
class RecognitionResult {
const RecognitionResult({
required this.type,
required this.confidence,
required this.normalizedText,
});
final SmartCardType type;
final double confidence;
final String normalizedText;
}RecognitionService class · dart · L15-L17 (3 LOC)lib/module/recognition/recognition_service.dart
abstract class RecognitionService {
Future<RecognitionResult> recognize(String rawInput);
}RuleBasedRecognitionService class · dart · L8-L170 (163 LOC)lib/module/recognition/rule_based_recognition_service.dart
class RuleBasedRecognitionService with ViewModel implements RecognitionService {
RuleBasedRecognitionService();
static final RegExp _emailRegExp = RegExp(
r'^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$',
);
static final RegExp _urlRegExp = RegExp(
r'^(https?:\/\/)?([A-Za-z0-9-]+\.)+[A-Za-z]{2,}(:\d+)?(\/\S*)?$',
);
static final RegExp _phoneRegExp = RegExp(r'^(\+?\d[\d -]{6,}\d)$');
static final RegExp _identityRegExp = RegExp(r'^(\d{15}|\d{17}[\dXx])$');
static final RegExp _bankRegExp = RegExp(r'^\d{16,19}$');
static final RegExp _shipmentRegExp = RegExp(
r'^[A-Z]{0,2}\d{8,20}[A-Z]{0,2}$',
);
static final RegExp _englishAddressMarkerRegExp = RegExp(
r'\b(Street|St\.?|Road|Rd\.?|Avenue|Ave\.?|Lane|Ln\.?|Boulevard|Blvd\.?)\b',
caseSensitive: false,
);
static final RegExp _chineseAddressMarkerRegExp = RegExp(
r'(省|市|区(?!域)|县|路|街|道|号|镇|乡|村)',
);
static final RegExp _addressNumberRegExp = RegExp(
r'\d{1,5}\s*(号|弄|室|单元|楼|栋|#)?'Repobility · open methodology · https://repobility.com/research/
RecommendationContext class · dart · L3-L11 (9 LOC)lib/module/recommendation/recommendation_service.dart
class RecommendationContext {
const RecommendationContext({
required this.currentApp,
required this.currentHour,
});
final String currentApp;
final int currentHour;
}RecommendationItem class · dart · L13-L23 (11 LOC)lib/module/recommendation/recommendation_service.dart
class RecommendationItem {
const RecommendationItem({
required this.card,
required this.score,
required this.reason,
});
final SmartCard card;
final double score;
final String reason;
}RecommendationService class · dart · L25-L31 (7 LOC)lib/module/recommendation/recommendation_service.dart
abstract class RecommendationService {
Future<List<RecommendationItem>> recommend({
required RecommendationContext context,
required List<SmartCard> candidates,
int limit = 5,
});
}AliyunOssProvider class · dart · L11-L136 (126 LOC)lib/module/s3_store/aliyun_oss_provider.dart
class AliyunOssProvider extends S3Provider {
AliyunOssProvider(this._config) {
_initClient();
}
final S3StoreConfig _config;
late final oss.Client _client;
@override
S3StoreConfig get config => _config;
void _initClient() {
oss.Client.init(
ossEndpoint: _config.endpoint,
bucketName: _config.bucket,
authGetter: _authGetter,
);
_client = oss.Client();
}
Future<oss.Auth> _authGetter() async {
return oss.Auth(
accessKey: _config.accessKeyId,
accessSecret: _config.secretAccessKey,
// 非 STS 场景:expire 设远期,secureToken 留空。
expire: '2099-12-31T23:59:59Z',
secureToken: '',
);
}
@override
Future<void> testConnection() async {
// 列举根前缀下最多 1 个对象来验证连接。
await _client.listObjects({
'prefix': config.prefix.isEmpty ? '' : '${config.prefix}/',
'max-keys': '1',
});
}
@override
Future<void> putObject({
required String key,
required Uint8List data,
String? contentTypS3BackupConfig class · dart · L15-L33 (19 LOC)lib/module/s3_store/s3_backup_service.dart
class S3BackupConfig {
const S3BackupConfig({
required this.endpoint,
required this.bucket,
required this.region,
required this.accessKeyId,
required this.secretAccessKey,
required this.enabled,
});
final String endpoint;
final String bucket;
final String region;
final String accessKeyId;
final String secretAccessKey;
final bool enabled;
bool get hasConfig => endpoint.isNotEmpty && bucket.isNotEmpty;
}S3StoreConfig class · dart · L6-L42 (37 LOC)lib/module/s3_store/s3_provider.dart
class S3StoreConfig {
const S3StoreConfig({
required this.endpoint,
required this.bucket,
required this.accessKeyId,
required this.secretAccessKey,
this.region = '',
this.prefix = 'AcopyBackup',
});
/// 服务端点,如 `https://s3.amazonaws.com`、`https://oss-cn-hangzhou.aliyuncs.com`。
final String endpoint;
/// 存储桶名称。
final String bucket;
final String accessKeyId;
final String secretAccessKey;
/// 区域,部分服务商签名需要(如 AWS)。
final String region;
/// 对象 key 前缀,默认 `AcopyBackup`。
final String prefix;
bool get isValid =>
endpoint.isNotEmpty &&
bucket.isNotEmpty &&
accessKeyId.isNotEmpty &&
secretAccessKey.isNotEmpty;
/// 拼接完整 object key:`{prefix}/{path}`。
String objectKey(String path) {
if (prefix.isEmpty) return path;
return '$prefix/$path';
}
}S3Provider class · dart · L47-L72 (26 LOC)lib/module/s3_store/s3_provider.dart
abstract class S3Provider {
/// 当前使用的配置。
S3StoreConfig get config;
/// 测试连接是否可用(通常 HEAD bucket 或 list 一个空前缀)。
Future<void> testConnection();
/// 上传对象。
Future<void> putObject({
required String key,
required Uint8List data,
String? contentType,
});
/// 下载对象,不存在时返回 null。
Future<Uint8List?> getObject(String key);
/// 列出指定前缀下的所有对象 key。
Future<List<String>> listObjects(String prefix);
/// 删除对象(可选实现,同步场景暂不需要)。
Future<void> deleteObject(String key) async {}
/// 释放资源(HTTP client 等)。
void dispose() {}
}LocalSearchService class · dart · L6-L31 (26 LOC)lib/module/search/local_search_service.dart
class LocalSearchService implements SearchService {
const LocalSearchService({required CardSource source}) : _source = source;
final CardSource _source;
@override
Future<List<SmartCard>> search(String query, {int limit = 30}) async {
final normalized = query.trim().toLowerCase();
final cards = _source();
if (normalized.isEmpty) {
return cards.take(limit).toList(growable: false);
}
final matched = cards.where((card) {
if (card.title.toLowerCase().contains(normalized)) {
return true;
}
if (card.content.toLowerCase().contains(normalized)) {
return true;
}
return card.fields.values.any(
(value) => value.toLowerCase().contains(normalized),
);
});
return matched.take(limit).toList(growable: false);
}
}Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
SearchService class · dart · L3-L5 (3 LOC)lib/module/search/search_service.dart
abstract class SearchService {
Future<List<SmartCard>> search(String query, {int limit = 30});
}SecureSecretStore class · dart · L7-L13 (7 LOC)lib/module/security/secure_secret_store.dart
abstract class SecureSecretStore {
Future<String?> read(String key);
Future<void> write(String key, String value);
Future<void> delete(String key);
}KeychainSecureSecretStore class · dart · L23-L49 (27 LOC)lib/module/security/secure_secret_store.dart
class KeychainSecureSecretStore implements SecureSecretStore {
KeychainSecureSecretStore({SecureStorageHostApi? hostApi})
: _hostApi = hostApi ?? SecureStorageHostApi();
final SecureStorageHostApi _hostApi;
@override
Future<String?> read(String key) {
return _hostApi.readSecret(key);
}
@override
Future<void> write(String key, String value) async {
final ok = await _hostApi.writeSecret(key, value);
if (!ok) {
throw StateError('写入安全存储失败: $key');
}
}
@override
Future<void> delete(String key) async {
final ok = await _hostApi.deleteSecret(key);
if (!ok) {
throw StateError('删除安全存储失败: $key');
}
}
}AppKvSecureSecretStore class · dart · L51-L72 (22 LOC)lib/module/security/secure_secret_store.dart
class AppKvSecureSecretStore implements SecureSecretStore {
const AppKvSecureSecretStore();
static const String _keyPrefix = 'secure_secret.';
String _storageKey(String key) => '$_keyPrefix$key';
@override
Future<void> delete(String key) {
return AppKvRepository.instance.remove(_storageKey(key));
}
@override
Future<String?> read(String key) {
return AppKvRepository.instance.getString(_storageKey(key));
}
@override
Future<void> write(String key, String value) {
return AppKvRepository.instance.setString(_storageKey(key), value);
}
}SensitiveMasker class · dart · L1-L13 (13 LOC)lib/module/security/sensitive_masker.dart
class SensitiveMasker {
const SensitiveMasker._();
static String mask(String input, {int keepLast = 4}) {
if (input.length <= keepLast) {
return '*' * input.length;
}
final visible = input.substring(input.length - keepLast);
final hidden = '*' * (input.length - keepLast);
return '$hidden$visible';
}
}TaskDag class · dart · L13-L79 (67 LOC)lib/module/shared/task_dag.dart
class TaskDag {
final Map<String, _DagNode> _nodes = {};
/// 添加任务。[dependsOn] 列出必须先完成的任务名。
void add<T>(
String name,
Future<T> Function() task, {
List<String> dependsOn = const [],
}) {
if (_nodes.containsKey(name)) {
throw ArgumentError('任务 "$name" 已存在');
}
_nodes[name] = _DagNode(name: name, task: task, dependsOn: dependsOn);
}
/// 执行所有任务,返回 {任务名: 返回值} 的结果 Map。
/// 无依赖的任务并行执行,有依赖的等上游完成后立即开始。
Future<Map<String, Object?>> execute() async {
// 验证依赖关系。
for (final node in _nodes.values) {
for (final dep in node.dependsOn) {
if (!_nodes.containsKey(dep)) {
throw StateError('任务 "${node.name}" 依赖不存在的任务 "$dep"');
}
}
}
_detectCycle();
final results = <String, Object?>{};
final futures = <String, Future<void>>{};
Future<void> run(String name) {
return futures.putIfAbsent(name, () async {
final node = _nodes[name]!;
// 等待所有上游任务完成。
await Future.wait([f_DagNode class · dart · L81-L91 (11 LOC)lib/module/shared/task_dag.dart
class _DagNode {
_DagNode({
required this.name,
required this.task,
required this.dependsOn,
});
final String name;
final Future<Object?> Function() task;
final List<String> dependsOn;
}DailyFileStore class · dart · L10-L74 (65 LOC)lib/module/sync/daily_file_store.dart
abstract class DailyFileStore {
/// 已成功写入的 payload hash 缓存,key 为 dateKey。
/// 写入前比较 hash,内容未变更时跳过实际 I/O;仅在写入成功后缓存。
final Map<String, String> _writeCache = {};
/// 存储后端是否可用。
Future<bool> isAvailable();
/// 将某天的 payload 写入 days/{dateKey}.json。
/// 内置 hash 去重:内容未变更时直接返回 true,跳过实际 I/O。
Future<bool> writeDailyFile(String dateKey, String payload) async {
final digest = sha256.convert(utf8.encode(payload)).toString();
if (_writeCache[dateKey] == digest) return true;
final ok = await doWriteDailyFile(dateKey, payload);
if (ok) _writeCache[dateKey] = digest;
return ok;
}
/// 子类实现的实际写入逻辑。
@protected
Future<bool> doWriteDailyFile(String dateKey, String payload);
/// 读取某天的 payload。
Future<String?> readDailyFile(String dateKey);
/// 列出所有已存储的日期 key。
Future<List<String>> listDailyFiles();
/// 将图片文件写入 images/ 子目录。
Future<bool> writeImageFile(String filename, Uint8List data);
/// 从 images/ 子目录读取图片文件。
Future<Uint8List?> readImageFile(StrWant this analysis on your repo? https://repobility.com/scan/
LocalBackupService class · dart · L9-L144 (136 LOC)lib/module/sync/local_backup_service.dart
class LocalBackupService extends DailyFileStore with ViewModel {
LocalBackupService({LocalBackupHostApi? hostApi})
: _hostApi = hostApi ?? LocalBackupHostApi();
final LocalBackupHostApi _hostApi;
String? _resolvedPath;
/// 当前已配置的备份文件夹路径,未配置时为 null。
String? get backupPath => _resolvedPath;
/// 是否已配置备份文件夹位置。
bool get hasBackupLocation => _resolvedPath != null;
/// 尝试从已保存的 bookmark 恢复文件路径。
Future<String?> resolveLocation() async {
final previousPath = _resolvedPath;
try {
_resolvedPath = await _hostApi.resolveBookmarkedBackupLocation();
} catch (_) {
_resolvedPath = null;
}
if (_resolvedPath != previousPath) {
clearWriteCache();
}
return _resolvedPath;
}
/// 弹出选择文件夹面板让用户选择备份文件夹,成功后持久化 bookmark。
Future<String?> pickLocation() async {
final previousPath = _resolvedPath;
final path = await _hostApi.pickAndBookmarkBackupLocation(
'clipboard-backup.json',
);
_resolvedPath = path;
if (_resoLocalHistoryStore class · dart · L3-L16 (14 LOC)lib/module/sync/local_history_store.dart
class LocalHistoryStore {
const LocalHistoryStore();
static const String _historyPayloadKey =
'quickvault.clipboard.history.local.payload.v1';
Future<String?> loadPayload() async {
return AppKvRepository.instance.getString(_historyPayloadKey);
}
Future<void> savePayload(String payload) async {
await AppKvRepository.instance.setString(_historyPayloadKey, payload);
}
}SyncSignal class · dart · L5-L46 (42 LOC)lib/module/sync/sync_signal.dart
class SyncSignal {
const SyncSignal({
required this.version,
required this.timestamp,
required this.deviceId,
this.changedDateKeys = const <String>[],
}) : assert(version >= 0),
assert(timestamp > 0),
assert(deviceId != '');
/// 单调递增的版本号。
final int version;
/// 推送时的毫秒时间戳。
final int timestamp;
/// 推送设备的唯一标识。
final String deviceId;
/// 本次推送变更的日期 key 列表(如 `["2026-03-30Z"]`)。
/// 拉取端可据此只拉变更的天,不用全量遍历。为空时回退到全量拉取。
final List<String> changedDateKeys;
Map<String, Object> toJson() => {
'v': version,
't': timestamp,
'd': deviceId,
if (changedDateKeys.isNotEmpty) 'k': changedDateKeys,
};
static SyncSignal? tryParse(Map<String, Object?> json) {
final v = json['v'];
final t = json['t'];
final d = json['d'];
if (v is! int || t is! int || d is! String) return null;
final k = json['k'];
final keys = k is List
? k.whereType<String>().toList(growable: false)
: const <String>[];
retu