From 141229076897c4a41832c9b8816e69c7dd255ed6 Mon Sep 17 00:00:00 2001 From: Strycher Date: Sat, 18 Jul 2026 21:49:00 -0400 Subject: [PATCH 01/60] chore(#298): log path bytes + applied hash width Captures previously logged only a path byte length, making it impossible to tell one 2-byte hop from two 1-byte hops - the exact question #240 and #279 turn on. Bandit's 2026-07-18 log hit this wall. Adds a shared _pathDiag() rendering (len, applied width, derived hops, hop-grouped bytes) to the existing contact path log sites, and the same detail to the repeater/room "Login routing" lines. Also logs the path-hash width transition on every device-info frame, with the frame length, so a short-frame downgrade to width 1 (#240) is visible in any capture without needing the device. Warn on change, info otherwise. Existing log sites only - no new per-frame logging (SAFELANE 11.10). Display labels (#279) and the calculateTimeout unit bug (#299) are out of scope here. --- lib/connector/meshcore_connector.dart | 47 ++++++++++++++++++++++---- lib/widgets/repeater_login_dialog.dart | 9 ++++- lib/widgets/room_login_dialog.dart | 9 ++++- 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 6fba8e9..8e4087b 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -22,6 +22,7 @@ import '../helpers/time_anomaly.dart'; import '../helpers/cyr2lat.dart'; import '../helpers/smaz.dart'; import '../helpers/cayenne_lpp.dart'; +import '../helpers/path_helper.dart'; import '../services/app_debug_log_service.dart'; import '../services/ble_debug_log_service.dart'; import '../services/linux_ble_error_classifier.dart'; @@ -491,6 +492,23 @@ class MeshCoreConnector extends ChangeNotifier { int get pathHashByteWidth => _pathHashByteWidth; + /// Compact path rendering for logs: the raw byte length, the width it is + /// being sliced at, the resulting hop count, and the hop-grouped bytes. + /// + /// Captures previously logged only the byte length, which made it impossible + /// to tell a single 2-byte hop from two 1-byte hops — the exact question + /// #240/#279 turn on. One line, existing log sites only, no new per-frame + /// logging. (#298) + String _pathDiag(List pathBytes, int byteLen) { + if (byteLen < 0) return 'flood'; + final w = _pathHashByteWidth; + final hops = realHopCount(byteLen, w); + final bytes = pathBytes.isEmpty + ? 'none' + : PathHelper.formatPathHex(pathBytes, w); + return 'len=$byteLen w=$w hops=$hops [$bytes]'; + } + CompanionRadioStats? get latestRadioStats => _latestRadioStats; bool get supportsCompanionRadioStats => (_firmwareVerCode ?? 0) >= 8; @@ -4599,7 +4617,8 @@ class MeshCoreConnector extends ChangeNotifier { _firmwareVersion = info.version; _deviceModel = info.model; _appDebugLogService?.info( - 'Device info: ${infoStrings.isEmpty ? '(no strings)' : infoStrings.join(' · ')}', + 'Device info: ${infoStrings.isEmpty ? '(no strings)' : infoStrings.join(' · ')}' + ' (frameLen=${frame.length})', tag: 'Device', ); @@ -4608,12 +4627,26 @@ class MeshCoreConnector extends ChangeNotifier { _clientRepeat = frame[80] != 0; } // Path hash mode v10+ (byte 81): width = mode + 1 byte(s) per hop + final priorPathHashByteWidth = _pathHashByteWidth; if (frame.length >= 82) { final mode = (frame[81] & 0xFF).clamp(0, 2); _pathHashByteWidth = mode + 1; } else { _pathHashByteWidth = 1; } + // A short frame silently downgrades a known-good width to 1 (#240). Log the + // frame length and the before/after width so any capture shows whether that + // happened, without needing the device. (#298) + final widthChanged = _pathHashByteWidth != priorPathHashByteWidth; + final widthNote = + 'Path hash width: $priorPathHashByteWidth -> $_pathHashByteWidth ' + '(device-info frame len=${frame.length}' + '${frame.length < 82 ? ', SHORT: no byte 81, forced to 1' : ''})'; + if (widthChanged) { + _appDebugLogService?.warn(widthNote, tag: 'Device'); + } else { + _appDebugLogService?.info(widthNote, tag: 'Device'); + } // Offband config capability v14+ (byte 82). Extracted + bounds-checked in a // testable helper; offset verified against firmware (see parseOffbandCaps). _offbandCaps = parseOffbandCaps(frame); @@ -4918,7 +4951,7 @@ class MeshCoreConnector extends ChangeNotifier { : contact.lastMessageAt; appLogger.info( - 'Refreshing contact ${contact.name}: devicePath=${contact.pathLength}, existingOverride=${existing.pathOverride}', + 'Refreshing contact ${contact.name}: devicePath=${_pathDiag(contact.path, contact.pathLength)}, existingOverride=${existing.pathOverride}', tag: 'Connector', ); @@ -4933,7 +4966,7 @@ class MeshCoreConnector extends ChangeNotifier { ); appLogger.info( - 'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_contacts[existingIndex].pathLength}', + 'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_pathDiag(_contacts[existingIndex].path, _contacts[existingIndex].pathLength)}', tag: 'Connector', ); } else { @@ -4944,7 +4977,7 @@ class MeshCoreConnector extends ChangeNotifier { isContact) { _contacts.add(contact); appLogger.info( - 'Added new contact ${contact.name}: pathLen=${contact.pathLength}', + 'Added new contact ${contact.name}: pathLen=${_pathDiag(contact.path, contact.pathLength)}', tag: 'Connector', ); } else { @@ -5032,7 +5065,7 @@ class MeshCoreConnector extends ChangeNotifier { ); appLogger.info( - 'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_contacts[existingIndex].pathLength}', + 'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_pathDiag(_contacts[existingIndex].path, _contacts[existingIndex].pathLength)}', tag: 'Connector', ); } else { @@ -7154,7 +7187,7 @@ class MeshCoreConnector extends ChangeNotifier { : existing.lastMessageAt; appLogger.info( - 'Refreshing contact ${existing.name}: devicePath=${existing.pathLength}, existingOverride=${existing.pathOverride}', + 'Refreshing contact ${existing.name}: devicePath=${_pathDiag(existing.path, existing.pathLength)}, existingOverride=${existing.pathOverride}', tag: 'Connector', ); @@ -7180,7 +7213,7 @@ class MeshCoreConnector extends ChangeNotifier { _updateDirectRepeater(_contacts[existingIndex], snr, path); appLogger.info( - 'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_contacts[existingIndex].pathLength}', + 'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_pathDiag(_contacts[existingIndex].path, _contacts[existingIndex].pathLength)}', tag: 'Connector', ); } diff --git a/lib/widgets/repeater_login_dialog.dart b/lib/widgets/repeater_login_dialog.dart index 0973fae..468e1a7 100644 --- a/lib/widgets/repeater_login_dialog.dart +++ b/lib/widgets/repeater_login_dialog.dart @@ -11,6 +11,7 @@ import '../services/storage_service.dart'; import '../connector/meshcore_connector.dart'; import '../connector/meshcore_protocol.dart'; import '../utils/app_logger.dart'; +import '../helpers/path_helper.dart'; import 'path_management_dialog.dart'; class RepeaterLoginDialog extends StatefulWidget { @@ -115,9 +116,15 @@ class _RepeaterLoginDialogState extends State { ); final timeoutSeconds = (timeoutMs / 1000).ceil(); final timeout = Duration(milliseconds: timeoutMs + 2000); + // selection.hopCount is a BYTE count, not hops (#279). Log bytes, the + // applied width, the derived hop count and the hop-grouped bytes so a + // capture is unambiguous. (#298) + final routingWidth = _connector.pathHashByteWidth; final selectionLabel = selection.useFlood ? 'flood' - : '${selection.hopCount} hops'; + : 'bytes=${selection.hopCount} w=$routingWidth ' + 'hops=${realHopCount(selection.hopCount, routingWidth)} ' + '[${PathHelper.formatPathHex(selection.pathBytes, routingWidth)}]'; appLogger.info('Login routing: $selectionLabel', tag: 'RepeaterLogin'); bool? loginResult; bool isAdmin = false; diff --git a/lib/widgets/room_login_dialog.dart b/lib/widgets/room_login_dialog.dart index 2641c02..98156df 100644 --- a/lib/widgets/room_login_dialog.dart +++ b/lib/widgets/room_login_dialog.dart @@ -11,6 +11,7 @@ import '../services/storage_service.dart'; import '../connector/meshcore_connector.dart'; import '../connector/meshcore_protocol.dart'; import '../utils/app_logger.dart'; +import '../helpers/path_helper.dart'; import '../helpers/snack_bar_builder.dart'; import 'path_management_dialog.dart'; @@ -111,9 +112,15 @@ class _RoomLoginDialogState extends State { ); final timeoutSeconds = (timeoutMs / 1000).ceil(); final timeout = Duration(milliseconds: timeoutMs + 2000); + // selection.hopCount is a BYTE count, not hops (#279). Log bytes, the + // applied width, the derived hop count and the hop-grouped bytes so a + // capture is unambiguous. (#298) + final routingWidth = _connector.pathHashByteWidth; final selectionLabel = selection.useFlood ? 'flood' - : '${selection.hopCount} hops'; + : 'bytes=${selection.hopCount} w=$routingWidth ' + 'hops=${realHopCount(selection.hopCount, routingWidth)} ' + '[${PathHelper.formatPathHex(selection.pathBytes, routingWidth)}]'; appLogger.info('Login routing: $selectionLabel', tag: 'RoomLogin'); bool? loginResult; bool isAdmin = false; From af897dc674b65e20d8c8ae70d8126e5b962ede0f Mon Sep 17 00:00:00 2001 From: Strycher Date: Sat, 18 Jul 2026 21:54:54 -0400 Subject: [PATCH 02/60] chore(#298): drop em-dash from _pathDiag doc comment Style rule, comment only. No behaviour change; the handed-over test builds were produced from 060a808 and differ from this commit by one character in a doc comment. --- lib/connector/meshcore_connector.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 8e4087b..2fb3a16 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -496,9 +496,9 @@ class MeshCoreConnector extends ChangeNotifier { /// being sliced at, the resulting hop count, and the hop-grouped bytes. /// /// Captures previously logged only the byte length, which made it impossible - /// to tell a single 2-byte hop from two 1-byte hops — the exact question - /// #240/#279 turn on. One line, existing log sites only, no new per-frame - /// logging. (#298) + /// to tell a single 2-byte hop from two 1-byte hops. That is the exact + /// question #240/#279 turn on. One line, existing log sites only, no new + /// per-frame logging. (#298) String _pathDiag(List pathBytes, int byteLen) { if (byteLen < 0) return 'flood'; final w = _pathHashByteWidth; From ac877ba904138a43d0c0a9fc35700adbf188a901 Mon Sep 17 00:00:00 2001 From: Strycher Date: Sun, 19 Jul 2026 20:45:58 -0400 Subject: [PATCH 03/60] fix(#298): log path units truthfully instead of asserting wrong hop counts The original diagnostic derived hops via realHopCount(), which divides a value that firmware already defines as a hop count (src/Packet.h:79-84: getPathByteLen() == getPathHashCount() * getPathHashSize()). It would have reported hops=1 for a real 2-hop path, misleading the exact investigation this logging exists to serve. _pathDiag now reports the hop count directly and prints held-vs-implied byte counts, flagging TRUNCATED when short. That makes the #309 decode truncation self-evident in any capture without the device present. The login dialogs no longer assert a hop count at all: selection.hopCount is ambiguous by construction (device paths carry hops, user overrides carry bytes, per #279), so they log the raw field, width and actual byte length, which discriminates the two cases. Refs #240, #279, #309 Co-Authored-By: Claude Opus 4.8 --- lib/connector/meshcore_connector.dart | 31 +++++++++++++++++--------- lib/widgets/repeater_login_dialog.dart | 14 +++++++----- lib/widgets/room_login_dialog.dart | 14 +++++++----- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 2fb3a16..90fddc8 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -492,21 +492,30 @@ class MeshCoreConnector extends ChangeNotifier { int get pathHashByteWidth => _pathHashByteWidth; - /// Compact path rendering for logs: the raw byte length, the width it is - /// being sliced at, the resulting hop count, and the hop-grouped bytes. + /// Compact path rendering for logs: hop count, applied width, the bytes we + /// actually hold versus the bytes that hop count implies, and the hex. /// - /// Captures previously logged only the byte length, which made it impossible - /// to tell a single 2-byte hop from two 1-byte hops. That is the exact - /// question #240/#279 turn on. One line, existing log sites only, no new - /// per-frame logging. (#298) - String _pathDiag(List pathBytes, int byteLen) { - if (byteLen < 0) return 'flood'; + /// Captures previously logged only a length, which made it impossible to tell + /// a single 2-byte hop from two 1-byte hops. That is the exact question + /// #240/#279 turn on. + /// + /// [pathLenField] is the firmware path-len field's low 6 bits, which is a HOP + /// COUNT, not a byte length: firmware `src/Packet.h:79-84` defines + /// `getPathByteLen() == getPathHashCount() * getPathHashSize()`. Logging the + /// held byte count next to the implied one makes the #309 decode truncation + /// self-evident in any capture, without the device present. + /// + /// One line, existing log sites only, no new per-frame logging. (#298) + String _pathDiag(List pathBytes, int pathLenField) { + if (pathLenField < 0) return 'flood'; final w = _pathHashByteWidth; - final hops = realHopCount(byteLen, w); - final bytes = pathBytes.isEmpty + final expectedBytes = pathLenField * w; + final hex = pathBytes.isEmpty ? 'none' : PathHelper.formatPathHex(pathBytes, w); - return 'len=$byteLen w=$w hops=$hops [$bytes]'; + final short = pathBytes.length < expectedBytes ? ' TRUNCATED' : ''; + return 'hops=$pathLenField w=$w ' + 'bytes=${pathBytes.length}/$expectedBytes$short [$hex]'; } CompanionRadioStats? get latestRadioStats => _latestRadioStats; diff --git a/lib/widgets/repeater_login_dialog.dart b/lib/widgets/repeater_login_dialog.dart index 468e1a7..d31de4e 100644 --- a/lib/widgets/repeater_login_dialog.dart +++ b/lib/widgets/repeater_login_dialog.dart @@ -116,14 +116,18 @@ class _RepeaterLoginDialogState extends State { ); final timeoutSeconds = (timeoutMs / 1000).ceil(); final timeout = Duration(milliseconds: timeoutMs + 2000); - // selection.hopCount is a BYTE count, not hops (#279). Log bytes, the - // applied width, the derived hop count and the hop-grouped bytes so a - // capture is unambiguous. (#298) + // `selection.hopCount` is ambiguous by construction: a device-discovered + // path carries a HOP count, a user override carries a BYTE count (#279). + // Do not assert hops here. Log the raw field, the width, and the actual + // byte length so a capture discriminates them: + // bytes == field -> byte-count semantics (override) + // bytes == field * w -> hop-count semantics (device) + // (#298) final routingWidth = _connector.pathHashByteWidth; final selectionLabel = selection.useFlood ? 'flood' - : 'bytes=${selection.hopCount} w=$routingWidth ' - 'hops=${realHopCount(selection.hopCount, routingWidth)} ' + : 'field=${selection.hopCount} w=$routingWidth ' + 'bytes=${selection.pathBytes.length} ' '[${PathHelper.formatPathHex(selection.pathBytes, routingWidth)}]'; appLogger.info('Login routing: $selectionLabel', tag: 'RepeaterLogin'); bool? loginResult; diff --git a/lib/widgets/room_login_dialog.dart b/lib/widgets/room_login_dialog.dart index 98156df..a12ea98 100644 --- a/lib/widgets/room_login_dialog.dart +++ b/lib/widgets/room_login_dialog.dart @@ -112,14 +112,18 @@ class _RoomLoginDialogState extends State { ); final timeoutSeconds = (timeoutMs / 1000).ceil(); final timeout = Duration(milliseconds: timeoutMs + 2000); - // selection.hopCount is a BYTE count, not hops (#279). Log bytes, the - // applied width, the derived hop count and the hop-grouped bytes so a - // capture is unambiguous. (#298) + // `selection.hopCount` is ambiguous by construction: a device-discovered + // path carries a HOP count, a user override carries a BYTE count (#279). + // Do not assert hops here. Log the raw field, the width, and the actual + // byte length so a capture discriminates them: + // bytes == field -> byte-count semantics (override) + // bytes == field * w -> hop-count semantics (device) + // (#298) final routingWidth = _connector.pathHashByteWidth; final selectionLabel = selection.useFlood ? 'flood' - : 'bytes=${selection.hopCount} w=$routingWidth ' - 'hops=${realHopCount(selection.hopCount, routingWidth)} ' + : 'field=${selection.hopCount} w=$routingWidth ' + 'bytes=${selection.pathBytes.length} ' '[${PathHelper.formatPathHex(selection.pathBytes, routingWidth)}]'; appLogger.info('Login routing: $selectionLabel', tag: 'RoomLogin'); bool? loginResult; From b28f3b36cb579779b9ef0a8ed0af8fdc784e08e4 Mon Sep 17 00:00:00 2001 From: Strycher Date: Sun, 19 Jul 2026 22:49:05 -0400 Subject: [PATCH 04/60] fix(#309): decode and encode path_len per firmware, not as a byte count The firmware path-len byte is packed and self-describing (src/Packet.h:79-84): high 2 bits = hash size - 1, low 6 bits = hash COUNT (hops), byte length = count * size. The app treated the low 6 bits as a byte count and ignored the width, on the strength of a comment asserting the high bits were "not reliably populated". Both halves of that comment were wrong. Receive: Contact.fromFrame read `count` bytes where firmware meant `count` hops, so at 2-byte width it kept HALF of every path and discarded the rest. DAYTON VA's 2-hop route became a 2-byte fragment, which is why every repeater login in Bandit's capture timed out. Send: buildUpdateContactPathFrame wrote the count raw, leaving mode bits 00. That tells the radio "1-byte hashes" while handing it 2-byte hash data, so it routed to nodes never on the route. This is the likelier cause of the observed "routes via c6 and 5c separately" behaviour. Also fixes a third unit inconsistency: setContactPath wrote customPath.length (bytes) into pathLength (hops) after every path set. Contact now carries pathHashWidth per path, so a stored path can never be re-sliced at a width it was not captured at. realHopCount() is removed rather than adjusted: it divided an already-correct hop count, halving it at 2-byte width, and every call site was compensating for a bug that no longer exists. Migration (Ben's call, option A): records written before this change have no pathHashWidth and hold truncated bytes that cannot be recovered by reinterpretation, so their path is dropped and the contact reverts to flood until the radio re-supplies it. Logged, not silent. Tests asserted the old semantics (expect(realHopCount(2, 2), 1)) and were rewritten against firmware truth rather than adjusted to keep passing. Added send-side encoding coverage, which did not exist. 456 tests pass. Refs #240, #279, #299 Co-Authored-By: Claude Opus 4.8 --- lib/connector/meshcore_connector.dart | 19 +++- lib/connector/meshcore_protocol.dart | 70 ++++++++++---- lib/models/contact.dart | 51 +++++++--- lib/screens/channel_chat_screen.dart | 5 +- lib/screens/channel_message_path_screen.dart | 9 +- lib/screens/chat_screen.dart | 12 +-- lib/storage/contact_store.dart | 23 ++++- .../build_update_contact_path_frame_test.dart | 53 +++++++++++ test/connector/path_hash_test.dart | 95 +++++++++++++++---- test/models/contact_pathlen_test.dart | 39 ++++++-- 10 files changed, 298 insertions(+), 78 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 90fddc8..596462a 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -3192,11 +3192,15 @@ class MeshCoreConnector extends ChangeNotifier { } } + /// Pushes a path to the device. [hopCount] is a HOP count and [hashWidth] the + /// bytes per hop hash; both are needed because the wire path_len byte packs + /// them together, and sending a bare count mislabels the width. (#309) Future setContactPath( Contact contact, Uint8List customPath, - int pathLen, - ) async { + int hopCount, { + int hashWidth = 1, + }) async { // Serialize path operations to prevent interleaved async calls from // leaving in-memory state inconsistent with the device. final prev = _pathOpLock; @@ -3210,7 +3214,8 @@ class MeshCoreConnector extends ChangeNotifier { buildUpdateContactPathFrame( contact.publicKey, customPath, - pathLen, + hopCount, + hashWidth: hashWidth, type: contact.type, flags: contact.flags, name: contact.name, @@ -3225,8 +3230,12 @@ class MeshCoreConnector extends ChangeNotifier { (c) => c.publicKeyHex == contact.publicKeyHex, ); if (idx != -1) { + // pathLength is a HOP count. This wrote customPath.length (a BYTE + // count), which silently redefined the field's unit after any path + // set and doubled it at 2-byte width. (#309) _contacts[idx] = _contacts[idx].copyWith( - pathLength: customPath.length, + pathLength: hopCount, + pathHashWidth: hashWidth, path: customPath, ); notifyListeners(); @@ -3272,6 +3281,7 @@ class MeshCoreConnector extends ChangeNotifier { latestContact.publicKey, latestContact.path, latestContact.pathLength, + hashWidth: latestContact.pathHashWidth, type: latestContact.type, flags: updatedFlags, name: latestContact.name, @@ -3615,6 +3625,7 @@ class MeshCoreConnector extends ChangeNotifier { contact.publicKey, contact.path, contact.pathLength, + hashWidth: contact.pathHashWidth, type: contact.type, flags: contact.flags, name: contact.name, diff --git a/lib/connector/meshcore_protocol.dart b/lib/connector/meshcore_protocol.dart index 13e3a05..516af02 100644 --- a/lib/connector/meshcore_protocol.dart +++ b/lib/connector/meshcore_protocol.dart @@ -514,24 +514,50 @@ int readInt32LE(Uint8List data, int offset) { return val; } -// Path-length byte from the firmware. Low 6 bits = the path length field -// (a BYTE count of the hop-hash array, 0-63); high 2 bits carry an optional -// hash-size mode hint (0..2 -> 1..3 bytes/hop) that is not reliably populated, -// so the device's configured hash width (MeshCoreConnector.pathHashByteWidth) -// is authoritative for slicing the path. realHopCount() converts the byte -// length to a true hop count at that width. (#112) -// TX counterpart: buildSetPathHashModeFrame (CMD_SET_PATH_HASH_MODE). +// Path-length byte from the firmware. This is a PACKED field, and both halves +// are authoritative — the path is self-describing on the wire: +// +// high 2 bits = hash size - 1 (0..2 -> 1..3 bytes per hop hash) +// low 6 bits = hash COUNT (the number of HOPS, 0-63) +// byte length = count * size +// +// Verified against firmware `src/Packet.h:79-84`: +// getPathHashSize() == (path_len >> 6) + 1 +// getPathHashCount() == path_len & 63 +// getPathByteLen() == getPathHashCount() * getPathHashSize() +// setPathHashSizeAndCount(sz, n) { path_len = ((sz - 1) << 6) | (n & 63); } +// +// The high bits are set deliberately by that setter, so the per-path width +// travels with the path. The companion contact frame carries this same encoded +// byte verbatim: `Packet::copyPath()` returns path_len unchanged +// (`src/Packet.cpp:32-35`) into `ContactInfo.out_path_len` +// (`src/helpers/BaseChatMesh.cpp:319`), which `writeContactRespFrame` emits +// as-is (`examples/companion_radio/MyMesh.cpp:205-212`). +// +// A prior comment here claimed the low 6 bits were a BYTE count and that the +// high bits were "not reliably populated". Both were wrong, and #222 plus the +// Contact decode were built on them: the app read `count` bytes where firmware +// meant `count` hops, keeping half of every path at 2-byte width. (#309) +// +// TX counterparts: buildSetPathHashModeFrame (CMD_SET_PATH_HASH_MODE) sets the +// device-wide default width; encodePathLen() packs a per-path value to send. int pathHopCount(int pathLenRaw) => pathLenRaw & 0x3F; int pathHashSizeBytes(int pathLenRaw) => ((pathLenRaw >> 6) & 0x03) + 1; -/// Real hop count from the firmware path byte-length and the device hash width. -/// `pathHopCount` returns the path BYTE length, so at a 2-byte hash width a -/// single 2-byte hop reports 2 — divide by the width to get the true hop count. -/// Null (unknown) and negative (flood) sentinels pass through unchanged. (#112) -int? realHopCount(int? rawByteLen, int hashWidth) { - if (rawByteLen == null || rawByteLen < 0) return rawByteLen; - final w = hashWidth < 1 ? 1 : hashWidth; - return rawByteLen ~/ w; +/// Byte length of the hop-hash array described by a raw path-length byte. +int pathByteLength(int pathLenRaw) => + pathHopCount(pathLenRaw) * pathHashSizeBytes(pathLenRaw); + +/// Packs a hop count and per-hop hash width into the firmware path-length byte. +/// +/// Mirrors firmware `Packet::setPathHashSizeAndCount`. Sending a bare hop count +/// (mode bits 00) tells the radio "1-byte hashes", so a 2-byte path routed that +/// way is read one byte per hop and goes to nodes that were never on the route. +/// Width is clamped to 1..3; mode 3 is reserved by firmware +/// (`isValidPathLen` rejects hash_size == 4). (#309) +int encodePathLen(int hopCount, int hashWidth) { + final w = hashWidth.clamp(1, 3); + return ((w - 1) << 6) | (hopCount & 0x3F); } /// Readable strings from a RESP_CODE_DEVICE_INFO frame: build date, model, and @@ -824,10 +850,19 @@ Uint8List buildResetPathFrame(Uint8List pubKey) { // Build CMD_ADD_UPDATE_CONTACT frame to set custom path // Format: [cmd][pub_key x32][type][flags][path_len][path x64][name x32][Lat? x4, Lon? x4][timestamp? x4] +// +// [hopCount] is a HOP count and [hashWidth] the bytes per hop hash; the two are +// packed into the single wire path_len byte via encodePathLen(). +// +// This previously wrote the count raw, leaving the mode bits 00 — which tells +// the radio "1-byte hashes". On a 2-byte net that handed the firmware 2-byte +// hash data labelled as 1-byte hops, so it routed to nodes that were never on +// the route. That is the send-side half of #240's misrouting. (#309) Uint8List buildUpdateContactPathFrame( Uint8List pubKey, Uint8List path, - int pathLen, { + int hopCount, { + int hashWidth = 1, int type = 1, // ADV_TYPE_CHAT int flags = 0, String name = '', @@ -840,7 +875,8 @@ Uint8List buildUpdateContactPathFrame( writer.writeBytes(pubKey); writer.writeByte(type); writer.writeByte(flags); - writer.writeByte(pathLen); + // Negative = flood sentinel, passed through as the firmware's 0xFF. + writer.writeByte(hopCount < 0 ? 0xFF : encodePathLen(hopCount, hashWidth)); writer.writeBytesPadded(path, maxPathSize); diff --git a/lib/models/contact.dart b/lib/models/contact.dart index 7c690d3..07e2d1c 100644 --- a/lib/models/contact.dart +++ b/lib/models/contact.dart @@ -8,8 +8,22 @@ class Contact { final String name; final int type; final int flags; - final int pathLength; // -1 = flood, 0+ = direct hops (from device) - final Uint8List path; // Path bytes from device + + /// Hop count for [path]: -1 = flood, 0+ = number of hops. + /// + /// This is the firmware path-len field's low 6 bits, which firmware defines + /// as a hash COUNT, not a byte length (`src/Packet.h:79-84`). It was + /// previously treated as a byte count, which truncated every path at widths + /// above 1. (#309) + final int pathLength; + + /// Bytes per hop hash in [path] (1..3), from the path-len byte's high 2 bits. + /// + /// Carried per-path rather than read from the connector's global width, so a + /// stored path can never be re-sliced at a width it was not captured at. + final int pathHashWidth; + + final Uint8List path; // Path bytes from device (pathLength * pathHashWidth) final int? pathOverride; // User's path override: -1 = force flood, null = auto final Uint8List? pathOverrideBytes; // User's path override bytes @@ -28,6 +42,7 @@ class Contact { required this.type, this.flags = 0, required this.pathLength, + this.pathHashWidth = 1, required this.path, this.pathOverride, this.pathOverrideBytes, @@ -80,6 +95,7 @@ class Contact { int? type, int? flags, int? pathLength, + int? pathHashWidth, Uint8List? path, int? pathOverride, Uint8List? pathOverrideBytes, @@ -98,6 +114,7 @@ class Contact { type: type ?? this.type, flags: flags ?? this.flags, pathLength: pathLength ?? this.pathLength, + pathHashWidth: pathHashWidth ?? this.pathHashWidth, path: path ?? this.path, pathOverride: clearPathOverride ? null @@ -133,8 +150,8 @@ class Contact { return parts.join(','); } - /// Default grouping uses legacy single-byte hop hash width. - String get pathIdList => pathFormattedIdList(pathHashSize); + /// Groups by this path's own captured width, not a global or legacy default. + String get pathIdList => pathFormattedIdList(pathHashWidth); String get shortPubKeyHex { return "<${publicKeyHex.substring(0, 8)}...${publicKeyHex.substring(publicKeyHex.length - 8)}>"; @@ -166,14 +183,20 @@ class Contact { final type = reader.readByte(); final flags = reader.readByte(); final pathLen = reader.readByte(); - // The firmware path-len byte packs a hash-mode hint in the high 2 bits and - // the path BYTE length in the low 6 (#222). Decode before use: a direct - // node at 2-byte mode sends 0x40, whose raw value would otherwise read as - // 64 hops and pull 64 junk bytes into the path. 0xFF stays the flood - // sentinel; the device-configured pathHashByteWidth converts bytes->hops - // at display time. - final byteLen = pathLen == 0xFF ? -1 : pathHopCount(pathLen); - final safePathLen = byteLen > 0 ? byteLen : 0; + // The firmware path-len byte packs hash size in the high 2 bits and hash + // COUNT (hops) in the low 6; the byte length is count * size + // (`src/Packet.h:79-84`). 0xFF stays the flood sentinel. + // + // This previously read `count` BYTES, so at 2-byte width it kept half of + // every path and discarded the rest — the truncation behind #240's failed + // repeater logins. The width is taken from the path itself rather than the + // connector's global width, so a path is always sliced at the width it was + // captured at. (#309) + final isFlood = pathLen == 0xFF; + final hopCount = isFlood ? -1 : pathHopCount(pathLen); + final hashWidth = isFlood ? 1 : pathHashSizeBytes(pathLen); + final byteLen = isFlood ? 0 : (hopCount * hashWidth); + final safePathLen = byteLen.clamp(0, maxPathSize); final pathBytes = reader.readBytes(maxPathSize).sublist(0, safePathLen); final name = reader.readCStringGreedy(maxNameSize); @@ -218,7 +241,9 @@ class Contact { name: name.isEmpty ? 'Unknown' : name, type: type, flags: flags, - pathLength: byteLen, // decoded low-6-bit byte length; -1 = flood (#222) + pathLength: + hopCount, // hop count from the low 6 bits; -1 = flood (#309) + pathHashWidth: hashWidth, // bytes/hop from the high 2 bits (#309) path: pathBytes, latitude: lat, longitude: lon, diff --git a/lib/screens/channel_chat_screen.dart b/lib/screens/channel_chat_screen.dart index fb9ff0d..60f4c87 100644 --- a/lib/screens/channel_chat_screen.dart +++ b/lib/screens/channel_chat_screen.dart @@ -1530,8 +1530,9 @@ class _ChannelChatScreenState extends State { } Widget _buildHopBadge(BuildContext context, ChannelMessage message) { - final hashWidth = context.read().pathHashByteWidth; - final hops = realHopCount(message.hopCount, hashWidth) ?? 0; + // message.hopCount is already a hop count (path-len low 6 bits). It used to + // be divided by the device width, which halved it at 2-byte width. (#309) + final hops = message.hopCount ?? 0; final color = Theme.of(context).brightness == Brightness.dark ? Colors.grey[400] : Colors.grey[600]; diff --git a/lib/screens/channel_message_path_screen.dart b/lib/screens/channel_message_path_screen.dart index b328503..274e004 100644 --- a/lib/screens/channel_message_path_screen.dart +++ b/lib/screens/channel_message_path_screen.dart @@ -43,9 +43,11 @@ class ChannelMessagePathScreen extends StatelessWidget { final hashWidth = connector.pathHashByteWidth; final hops = _buildPathHops(primaryPath, connector, l10n, hashWidth); final hasHopDetails = primaryPath.isNotEmpty; + // Observed = bytes we hold / this path's width. Claimed = the hop count + // the path-len byte already carries; dividing it again halved it. (#309) final observedLabel = _formatObservedHops( - primaryPath.length ~/ hashWidth, - realHopCount(message.hopCount, hashWidth), + primaryPath.length ~/ message.pathHashSize, + message.hopCount, l10n, ); final extraPaths = _otherPaths(primaryPath, message.pathVariants); @@ -121,7 +123,6 @@ class ChannelMessagePathScreen extends StatelessWidget { Widget _buildSummaryCard(BuildContext context, {String? observedLabel}) { final l10n = context.l10n; - final hashWidth = context.read().pathHashByteWidth; return Card( child: Padding( padding: const EdgeInsets.all(12), @@ -145,7 +146,7 @@ class ChannelMessagePathScreen extends StatelessWidget { ), _buildDetailRow( l10n.channelPath_pathLabelTitle, - _formatPathLabel(realHopCount(message.hopCount, hashWidth), l10n), + _formatPathLabel(message.hopCount, l10n), ), if (observedLabel != null) _buildDetailRow(l10n.channelPath_observedLabel, observedLabel), diff --git a/lib/screens/chat_screen.dart b/lib/screens/chat_screen.dart index d8c743a..156378e 100644 --- a/lib/screens/chat_screen.dart +++ b/lib/screens/chat_screen.dart @@ -1274,15 +1274,11 @@ class _ChatScreenState extends State { return context.l10n.chat_hopsForced(contact.pathOverride!); } - // Use device's path. pathLength is a BYTE length; divide by the device's - // configured hash width to get the true hop count (#222). + // Use device's path. pathLength is a HOP count straight from the path-len + // byte's low 6 bits; it used to be divided by the device width, which + // halved it at 2-byte width. (#309) if (contact.pathLength < 0) return context.l10n.chat_floodAuto; - final hops = - realHopCount( - contact.pathLength, - context.read().pathHashByteWidth, - ) ?? - 0; + final hops = contact.pathLength; if (hops == 0) return context.l10n.chat_direct; return context.l10n.chat_hopsCount(hops); } diff --git a/lib/storage/contact_store.dart b/lib/storage/contact_store.dart index 7883d88..5d1805d 100644 --- a/lib/storage/contact_store.dart +++ b/lib/storage/contact_store.dart @@ -67,6 +67,7 @@ class ContactStore { 'type': contact.type, 'flags': contact.flags, 'pathLength': contact.pathLength, + 'pathHashWidth': contact.pathHashWidth, 'path': base64Encode(contact.path), 'pathOverride': contact.pathOverride, 'pathOverrideBytes': contact.pathOverrideBytes != null @@ -88,13 +89,31 @@ class ContactStore { final lastSeenMs = json['lastSeen'] as int? ?? 0; final lastMessageMs = json['lastMessageAt'] as int?; final lastModifiedMs = json['lastModified'] as int?; + final isLegacyPathRecord = + !json.containsKey('pathHashWidth') && + ((json['pathLength'] as int? ?? -1) > 0); + if (isLegacyPathRecord) { + appLogger.info( + 'Dropping pre-#309 path for contact ${json['name']} ' + '(decoded with the old count-as-bytes rule, so truncated); ' + 'reverting to flood until the device re-supplies it', + ); + } return Contact( publicKey: Uint8List.fromList(base64Decode(json['publicKey'] as String)), name: json['name'] as String? ?? 'Unknown', type: json['type'] as int? ?? 0, flags: json['flags'] as int? ?? 0, - pathLength: json['pathLength'] as int? ?? -1, - path: json['path'] != null + // Records written before #309 have no 'pathHashWidth' and their path was + // decoded with the old count-as-bytes rule, so at any width above 1 the + // stored bytes are a truncated fragment. Truncated bytes cannot be + // recovered by reinterpreting them, so a legacy record's path is dropped + // and the contact reverts to flood until the radio re-supplies it (the + // device refreshes contacts routinely, so this self-heals). Ben's call: + // re-fetch rather than keep a path we know is wrong. (#309) + pathLength: isLegacyPathRecord ? -1 : (json['pathLength'] as int? ?? -1), + pathHashWidth: json['pathHashWidth'] as int? ?? 1, + path: (!isLegacyPathRecord && json['path'] != null) ? Uint8List.fromList(base64Decode(json['path'] as String)) : Uint8List(0), pathOverride: json['pathOverride'] as int?, diff --git a/test/connector/build_update_contact_path_frame_test.dart b/test/connector/build_update_contact_path_frame_test.dart index da88f9a..dd110d3 100644 --- a/test/connector/build_update_contact_path_frame_test.dart +++ b/test/connector/build_update_contact_path_frame_test.dart @@ -16,6 +16,59 @@ void main() { final pubKey = Uint8List.fromList(List.generate(32, (i) => i)); final path = Uint8List.fromList([0xAA, 0xBB]); + // Byte offset of path_len in the frame: 1 cmd + 32 pubKey + 1 type + 1 flags. + const int pathLenOffset = 35; + + group('buildUpdateContactPathFrame path_len encoding (#309)', () { + test('packs the hash width into the high 2 bits', () { + // One 2-byte hop must go out as 0x41, not a bare 1. Writing the count + // raw leaves mode bits 00, which tells the radio "1-byte hashes" while + // handing it 2-byte hash data, so it routes to nodes that were never on + // the route. That is the send-side half of #240's misrouting. + final frame = buildUpdateContactPathFrame( + pubKey, + Uint8List.fromList([0xC6, 0x5C]), + 1, + hashWidth: 2, + ); + expect(frame[pathLenOffset], 0x41); + expect(pathHopCount(frame[pathLenOffset]), 1); + expect(pathHashSizeBytes(frame[pathLenOffset]), 2); + }); + + test('two 2-byte hops encode as 0x42', () { + final frame = buildUpdateContactPathFrame( + pubKey, + Uint8List.fromList([0xC6, 0x5C, 0xA1, 0xB2]), + 2, + hashWidth: 2, + ); + expect(frame[pathLenOffset], 0x42); + expect(pathByteLength(frame[pathLenOffset]), 4); + }); + + test('1-byte width is byte-identical to the pre-fix encoding', () { + // Legacy 1-byte nets must see no change on the wire. + final frame = buildUpdateContactPathFrame( + pubKey, + Uint8List.fromList([0xAA, 0xBB, 0xCC]), + 3, + hashWidth: 1, + ); + expect(frame[pathLenOffset], 3); + }); + + test('negative hop count emits the 0xFF flood sentinel', () { + final frame = buildUpdateContactPathFrame( + pubKey, + Uint8List(0), + -1, + hashWidth: 2, + ); + expect(frame[pathLenOffset], 0xFF); + }); + }); + group('buildUpdateContactPathFrame', () { test('omits lat/lon and timestamp tail when neither is provided', () { final frame = buildUpdateContactPathFrame( diff --git a/test/connector/path_hash_test.dart b/test/connector/path_hash_test.dart index fbe3224..fe7c3e5 100644 --- a/test/connector/path_hash_test.dart +++ b/test/connector/path_hash_test.dart @@ -1,32 +1,91 @@ -// Path-hash width / hop-count decode (#112). The firmware path-length byte is a -// BYTE count of the hop-hash array, not a hop count, so at a 2-byte hash width a -// single 2-byte hop reports 2. realHopCount divides by the device width to get -// true hops; the Packet Path screen slices and matches at the device width so a -// 2-byte hash like 6A3D resolves to one repeater instead of two 1-byte hops. +// Path-len byte encode/decode (#309, supersedes #112/#222). +// +// The firmware path-len byte is PACKED and self-describing: +// high 2 bits = hash size - 1 (1..3 bytes per hop hash) +// low 6 bits = hash COUNT, i.e. the number of HOPS +// byte length = count * size +// +// Verified against firmware src/Packet.h:79-84: +// getPathHashSize() == (path_len >> 6) + 1 +// getPathHashCount() == path_len & 63 +// getPathByteLen() == getPathHashCount() * getPathHashSize() +// setPathHashSizeAndCount(sz, n) { path_len = ((sz - 1) << 6) | (n & 63); } +// +// This file previously asserted the opposite (low 6 bits = a BYTE count, and a +// realHopCount() that divided by the device width). Those assertions encoded the +// bug: the app read `count` bytes where firmware meant `count` hops, keeping +// half of every path at 2-byte width, and halved every displayed hop count. import 'package:flutter_test/flutter_test.dart'; import 'package:meshcore_open/connector/meshcore_protocol.dart'; void main() { - group('realHopCount', () { - test('divides path byte-length by hash width', () { - // The bug: one 2-byte hop (6A3D) reported byteLen 2 -> shown as "2 hops". - expect(realHopCount(2, 2), 1); - expect(realHopCount(4, 2), 2); - expect(realHopCount(6, 3), 2); + group('path-len decode', () { + test('low 6 bits are a HOP count, not a byte count', () { + // 0x42 = 2-byte hashes, 2 hops. The old model called this "2 bytes". + expect(pathHopCount(0x42), 2); + expect(pathHashSizeBytes(0x42), 2); + expect(pathByteLength(0x42), 4); // 2 hops * 2 bytes }); - test('is a no-op at 1-byte width (no regression for legacy nets)', () { - expect(realHopCount(3, 1), 3); + test('one 2-byte hop is 1 hop occupying 2 bytes', () { + // Bandit's C65C case: ONE hop through a 2-byte-prefixed repeater. + expect(pathHopCount(0x41), 1); + expect(pathHashSizeBytes(0x41), 2); + expect(pathByteLength(0x41), 2); }); - test('treats width < 1 as 1', () { - expect(realHopCount(3, 0), 3); + test('1-byte width is unchanged (legacy nets)', () { + expect(pathHopCount(0x03), 3); + expect(pathHashSizeBytes(0x03), 1); + expect(pathByteLength(0x03), 3); }); - test('passes null (unknown) and negative (flood) sentinels through', () { - expect(realHopCount(null, 2), isNull); - expect(realHopCount(-1, 2), -1); + test('3-byte width', () { + // 0x82 = size 3, 2 hops -> 6 bytes. + expect(pathHashSizeBytes(0x82), 3); + expect(pathHopCount(0x82), 2); + expect(pathByteLength(0x82), 6); + }); + + test('direct (zero-hop) at 2-byte mode carries no path bytes', () { + // 0x40 = size 2, 0 hops. Reading the raw byte as a length would have + // pulled 64 junk bytes into the path. + expect(pathHopCount(0x40), 0); + expect(pathByteLength(0x40), 0); + }); + }); + + group('encodePathLen', () { + test('packs width and hop count the way firmware does', () { + expect(encodePathLen(2, 2), 0x42); + expect(encodePathLen(1, 2), 0x41); + expect(encodePathLen(3, 1), 0x03); + expect(encodePathLen(2, 3), 0x82); + expect(encodePathLen(0, 2), 0x40); + }); + + test('round-trips through the decoders', () { + for (final width in [1, 2, 3]) { + for (final hops in [0, 1, 5, 63]) { + final encoded = encodePathLen(hops, width); + expect(pathHopCount(encoded), hops, reason: 'hops w=$width'); + expect(pathHashSizeBytes(encoded), width, reason: 'width w=$width'); + } + } + }); + + test('clamps width to the 1..3 firmware range (mode 3 is reserved)', () { + expect(pathHashSizeBytes(encodePathLen(1, 0)), 1); + expect(pathHashSizeBytes(encodePathLen(1, 9)), 3); + }); + + test('a bare hop count would mislabel the width as 1 byte', () { + // The send-side bug: writing the count raw leaves mode bits 00, telling + // the radio "1-byte hashes" while handing it 2-byte hash data. + const rawCount = 2; + expect(pathHashSizeBytes(rawCount), 1); // what the radio would have read + expect(pathHashSizeBytes(encodePathLen(2, 2)), 2); // what it must read }); }); } diff --git a/test/models/contact_pathlen_test.dart b/test/models/contact_pathlen_test.dart index c89af0c..a6323c6 100644 --- a/test/models/contact_pathlen_test.dart +++ b/test/models/contact_pathlen_test.dart @@ -46,18 +46,37 @@ void main() { expect(c.path, [0xAA, 0xBB, 0xCC]); }); - test('2-byte-mode multi-hop (0x44) decodes to 4 bytes, not flood', () { - // High bits = mode-1 hint, low bits = 4 bytes = a 2-hop path at 2-byte - // width. Pre-#222 the raw 68 (> maxPathSize) was mis-flagged as flood. + test('2-byte-mode 2-hop (0x42) keeps all FOUR bytes (#309)', () { + // 0x42 = size 2, count 2 -> 2 hops occupying 2*2 = 4 bytes. + // + // Pre-#309 the low 6 bits were read as a byte count, so this kept only + // the first 2 bytes and silently discarded the second hop. That is the + // truncation behind #240's failed repeater logins. final c = Contact.fromFrame( - _frame(pathLen: 0x44, pathBytes: [1, 2, 3, 4]), + _frame(pathLen: 0x42, pathBytes: [0xC6, 0x5C, 0xA1, 0xB2]), )!; - expect(c.pathLength, 4); - expect(c.path, [1, 2, 3, 4]); - expect( - realHopCount(c.pathLength, 2), - 2, - ); // 4 bytes / 2-byte width = 2 hops + expect(c.pathLength, 2); // HOPS, not bytes + expect(c.pathHashWidth, 2); + expect(c.path, [0xC6, 0x5C, 0xA1, 0xB2]); // all 4 bytes retained + }); + + test('2-byte-mode single hop (0x41) is 1 hop of 2 bytes (#309)', () { + // Bandit's C65C case: ONE hop, previously surfaced as "2 hops". + final c = Contact.fromFrame( + _frame(pathLen: 0x41, pathBytes: [0xC6, 0x5C]), + )!; + expect(c.pathLength, 1); + expect(c.pathHashWidth, 2); + expect(c.path, [0xC6, 0x5C]); + }); + + test('3-byte-mode 2-hop (0x82) keeps six bytes (#309)', () { + final c = Contact.fromFrame( + _frame(pathLen: 0x82, pathBytes: [1, 2, 3, 4, 5, 6]), + )!; + expect(c.pathLength, 2); + expect(c.pathHashWidth, 3); + expect(c.path, [1, 2, 3, 4, 5, 6]); }); test('flood sentinel (0xFF) stays -1, empty path', () { From 879523d082b0806b79637f44ed7c693c81965527 Mon Sep 17 00:00:00 2001 From: Strycher Date: Sun, 19 Jul 2026 22:53:50 -0400 Subject: [PATCH 05/60] fix(#309): use each path's captured width in _pathDiag, not the global one Gemini adversarial review finding (minor, accepted). _pathDiag derived the expected byte count from the connector's global _pathHashByteWidth, which is the CONNECTED device's width, while each Contact now carries the width its path was actually captured at. Failure case: a contact discovered on a 1-byte net (3 hops, 3 bytes) viewed while connected to a 2-byte device computes expected = 3 * 2 = 6 and logs "bytes=3/6 TRUNCATED" for a complete, correct path. A false TRUNCATED in the very diagnostic added to investigate truncation would actively mislead. _pathDiag now takes the per-path width; all seven call sites pass contact.pathHashWidth. Refs #298, #309 Co-Authored-By: Claude Opus 4.8 --- lib/connector/meshcore_connector.dart | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 596462a..7ae9333 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -506,9 +506,13 @@ class MeshCoreConnector extends ChangeNotifier { /// self-evident in any capture, without the device present. /// /// One line, existing log sites only, no new per-frame logging. (#298) - String _pathDiag(List pathBytes, int pathLenField) { + /// [pathHashWidth] must be the width the path was CAPTURED at (each Contact + /// carries its own), not the connected device's current global width. Using + /// the global one falsely flags a complete 1-byte-width path as TRUNCATED + /// when the client is connected to a 2-byte device. (#309, Gemini review) + String _pathDiag(List pathBytes, int pathLenField, int pathHashWidth) { if (pathLenField < 0) return 'flood'; - final w = _pathHashByteWidth; + final w = pathHashWidth < 1 ? 1 : pathHashWidth; final expectedBytes = pathLenField * w; final hex = pathBytes.isEmpty ? 'none' @@ -4971,7 +4975,7 @@ class MeshCoreConnector extends ChangeNotifier { : contact.lastMessageAt; appLogger.info( - 'Refreshing contact ${contact.name}: devicePath=${_pathDiag(contact.path, contact.pathLength)}, existingOverride=${existing.pathOverride}', + 'Refreshing contact ${contact.name}: devicePath=${_pathDiag(contact.path, contact.pathLength, contact.pathHashWidth)}, existingOverride=${existing.pathOverride}', tag: 'Connector', ); @@ -4986,7 +4990,7 @@ class MeshCoreConnector extends ChangeNotifier { ); appLogger.info( - 'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_pathDiag(_contacts[existingIndex].path, _contacts[existingIndex].pathLength)}', + 'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_pathDiag(_contacts[existingIndex].path, _contacts[existingIndex].pathLength, _contacts[existingIndex].pathHashWidth)}', tag: 'Connector', ); } else { @@ -4997,7 +5001,7 @@ class MeshCoreConnector extends ChangeNotifier { isContact) { _contacts.add(contact); appLogger.info( - 'Added new contact ${contact.name}: pathLen=${_pathDiag(contact.path, contact.pathLength)}', + 'Added new contact ${contact.name}: pathLen=${_pathDiag(contact.path, contact.pathLength, contact.pathHashWidth)}', tag: 'Connector', ); } else { @@ -5085,7 +5089,7 @@ class MeshCoreConnector extends ChangeNotifier { ); appLogger.info( - 'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_pathDiag(_contacts[existingIndex].path, _contacts[existingIndex].pathLength)}', + 'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_pathDiag(_contacts[existingIndex].path, _contacts[existingIndex].pathLength, _contacts[existingIndex].pathHashWidth)}', tag: 'Connector', ); } else { @@ -7207,7 +7211,7 @@ class MeshCoreConnector extends ChangeNotifier { : existing.lastMessageAt; appLogger.info( - 'Refreshing contact ${existing.name}: devicePath=${_pathDiag(existing.path, existing.pathLength)}, existingOverride=${existing.pathOverride}', + 'Refreshing contact ${existing.name}: devicePath=${_pathDiag(existing.path, existing.pathLength, existing.pathHashWidth)}, existingOverride=${existing.pathOverride}', tag: 'Connector', ); @@ -7233,7 +7237,7 @@ class MeshCoreConnector extends ChangeNotifier { _updateDirectRepeater(_contacts[existingIndex], snr, path); appLogger.info( - 'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_pathDiag(_contacts[existingIndex].path, _contacts[existingIndex].pathLength)}', + 'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_pathDiag(_contacts[existingIndex].path, _contacts[existingIndex].pathLength, _contacts[existingIndex].pathHashWidth)}', tag: 'Connector', ); } From 45bfa33eede0352c495493c617b2a9f3b7999040 Mon Sep 17 00:00:00 2001 From: Strycher Date: Sat, 18 Jul 2026 23:25:20 -0400 Subject: [PATCH 06/60] feat(#304): 0xC3 FEM LNA protocol layer + capability gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the client half of the Heltec V4 FEM LNA control surface at the protocol level: 0xC3 CMD_OFFBAND_FEM_LNA (0x01 SET [value], 0x02 GET), a 3-byte [0xC3][sub][value] reply parser, and the OFFBAND_CAP_FEM_LNA = 0x04 capability gate. Deliberately a fork-private command rather than a sixth byte on the stock CMD_SET_OTHER_PARAMS (38): that frame is shared with upstream MeshCore and is sent to every radio regardless of fork, so widening it would perturb stock firmware. Nothing is emitted unless the capability bit is set. The gate checks the bit only, never model or version — firmware derives it at runtime from the auto-detected FEM chip, so it is a per-unit answer and two Heltec V4s can legitimately disagree. PROVISIONAL: firmware owns the caps byte and has not yet confirmed 0x04 is free, and the 0xC3 setter is not built yet. Spec sent to OffbandMesh/meshcore-firmware#298. Do not merge before firmware confirms the shape as built. Co-Authored-By: Claude Opus 4.8 --- lib/connector/meshcore_protocol.dart | 55 ++++++++++++++++ test/connector/offband_fem_lna_test.dart | 84 ++++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 test/connector/offband_fem_lna_test.dart diff --git a/lib/connector/meshcore_protocol.dart b/lib/connector/meshcore_protocol.dart index 516af02..e5f8821 100644 --- a/lib/connector/meshcore_protocol.dart +++ b/lib/connector/meshcore_protocol.dart @@ -263,6 +263,46 @@ const int respCodeOffbandGps = 0xC1; /// Request frame for [cmdOffbandGps] — a bare 1-byte command, no payload. (#135) Uint8List buildOffbandGpsRequestFrame() => Uint8List.fromList([cmdOffbandGps]); +// --- Offband FEM LNA command (0xC3) — capability-gated. Heltec V4 external +// FEM LNA control; firmware counterpart OffbandMesh/meshcore-firmware#298. +// +// Deliberately a fork-private command rather than an extra byte on the stock +// CMD_SET_OTHER_PARAMS (38): that frame is shared with upstream MeshCore and is +// sent to every radio regardless of fork, so widening it would perturb stock +// firmware. Nothing here is emitted unless the capability bit is set. (#304) +const int cmdOffbandFemLna = 0xC3; +const int offbandFemLnaSet = 0x01; +const int offbandFemLnaGet = 0x02; + +/// `0` = FEM LNA bypassed, `1` = enabled. Firmware default is enabled. +const int femLnaBypass = 0x00; +const int femLnaEnabled = 0x01; + +Uint8List buildOffbandFemLnaSetFrame(bool enabled) => Uint8List.fromList([ + cmdOffbandFemLna, + offbandFemLnaSet, + enabled ? femLnaEnabled : femLnaBypass, +]); + +Uint8List buildOffbandFemLnaGetFrame() => + Uint8List.fromList([cmdOffbandFemLna, offbandFemLnaGet]); + +/// Reply to a `0xC3` request: `[0xC3][sub][value]`. A malformed request draws +/// the generic `[respCodeErr][errCodeIllegalArg]` instead, which is NOT +/// 0xC3-prefixed and so never reaches this parser. +class OffbandFemLnaReply { + const OffbandFemLnaReply(this.subType, this.value); + final int subType; + final int value; + + bool get enabled => value != femLnaBypass; +} + +OffbandFemLnaReply? parseOffbandFemLnaReply(Uint8List frame) { + if (frame.length < 3 || frame[0] != cmdOffbandFemLna) return null; + return OffbandFemLnaReply(frame[1], frame[2]); +} + // --- Offband block command (0xC2) — capability-gated; see // docs/architecture/block-contract-as-built.md §8. Firmware as-built PR #247. --- const int cmdOffbandBlock = 0xC2; @@ -319,6 +359,21 @@ bool firmwareSupportsOffbandGps(int? offbandCaps) => offbandCaps != null; /// `FIRMWARE_VER_CODE >= 15`; absent → app-only mode (no sync, no firmware drop). const int offbandCapBlock = 0x02; +/// `OFFBAND_CAP_FEM_LNA` bit (bit 2) in the `offband_caps` byte: this radio can +/// control its external FEM LNA (firmware #298). +/// +/// PROVISIONAL — firmware owns the caps byte and has not yet confirmed 0x04 as +/// free. Do not ship against this without that confirmation (#304). +/// +/// Gate on the BIT ONLY, never on model or version: firmware derives it at +/// runtime from the auto-detected FEM chip (KCT8103L vs GC1109), so it is a +/// per-unit answer — two Heltec V4s can legitimately disagree, and other +/// FEM-bearing boards report false today. +const int offbandCapFemLna = 0x04; + +bool firmwareSupportsOffbandFemLna(int? offbandCaps) => + offbandCaps != null && (offbandCaps & offbandCapFemLna) != 0; + bool firmwareSupportsOffbandBlock(int? offbandCaps, int? firmwareVerCode) => offbandCaps != null && (offbandCaps & offbandCapBlock) != 0 && diff --git a/test/connector/offband_fem_lna_test.dart b/test/connector/offband_fem_lna_test.dart new file mode 100644 index 0000000..2768dd8 --- /dev/null +++ b/test/connector/offband_fem_lna_test.dart @@ -0,0 +1,84 @@ +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:meshcore_open/connector/meshcore_protocol.dart'; + +void main() { + group('FEM LNA capability gate (#304)', () { + test('requires the explicit bit, never model or version', () { + expect(firmwareSupportsOffbandFemLna(offbandCapFemLna), isTrue); + expect( + firmwareSupportsOffbandFemLna(offbandCapFemLna | offbandCapBlock), + isTrue, + ); + }); + + test('false when the bit is clear, even on an Offband radio', () { + // Offband firmware that supports block but not FEM LNA control. + expect(firmwareSupportsOffbandFemLna(offbandCapBlock), isFalse); + expect(firmwareSupportsOffbandFemLna(0x00), isFalse); + }); + + test('false on stock firmware (no caps byte at all)', () { + expect(firmwareSupportsOffbandFemLna(null), isFalse); + }); + + test('does not collide with the block capability bit', () { + expect(offbandCapFemLna & offbandCapBlock, equals(0)); + }); + }); + + group('FEM LNA frames (#304)', () { + test('SET carries the enable value', () { + expect( + buildOffbandFemLnaSetFrame(true), + equals(Uint8List.fromList([0xC3, 0x01, 0x01])), + ); + expect( + buildOffbandFemLnaSetFrame(false), + equals(Uint8List.fromList([0xC3, 0x01, 0x00])), + ); + }); + + test('GET is a bare 2-byte request', () { + expect( + buildOffbandFemLnaGetFrame(), + equals(Uint8List.fromList([0xC3, 0x02])), + ); + }); + + test('reply parses sub-type and value', () { + final reply = parseOffbandFemLnaReply( + Uint8List.fromList([0xC3, 0x02, 0x01]), + ); + expect(reply, isNotNull); + expect(reply!.subType, equals(offbandFemLnaGet)); + expect(reply.enabled, isTrue); + + final bypassed = parseOffbandFemLnaReply( + Uint8List.fromList([0xC3, 0x02, 0x00]), + ); + expect(bypassed!.enabled, isFalse); + }); + + test('rejects a short frame instead of throwing', () { + expect(parseOffbandFemLnaReply(Uint8List.fromList([0xC3, 0x02])), isNull); + expect(parseOffbandFemLnaReply(Uint8List.fromList([])), isNull); + }); + + test('ignores frames belonging to another command', () { + // The generic error reply is [0x01][0x06] and is NOT 0xC3-prefixed, so it + // must never be mistaken for a FEM LNA reply. + expect( + parseOffbandFemLnaReply( + Uint8List.fromList([respCodeErr, errCodeIllegalArg]), + ), + isNull, + ); + expect( + parseOffbandFemLnaReply(Uint8List.fromList([0xC2, 0x01, 0x01])), + isNull, + ); + }); + }); +} From 856214e4e950d3d2cae1623541d6d67c13b5ee2d Mon Sep 17 00:00:00 2001 From: Strycher Date: Sun, 19 Jul 2026 04:29:02 -0400 Subject: [PATCH 07/60] feat(#304): FEM LNA connector state + gated Radio Settings toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Completes the client half against the firmware as-built (#298): - Device-info offset 83 carries the FEM LNA state on v16+, appended unconditionally (reads 0 on non-capable boards). parseFemLnaState is length-guarded and returns null pre-v16. Byte presence signals firmware version, not capability — the cap BIT gates the UI. - 0xC3 reply handler adopts the reported value verbatim: firmware returns post-apply hardware state, not an echo, so a refused write surfaces as truth rather than a lie. - setFemLna / requestFemLnaState no-op unless the capability bit is set, so a non-capable radio never sees 0xC3 traffic. - Radio Settings toggle rendered only when the bit is set, driven by connector state via ListenableBuilder rather than local optimistic state, so it always shows what the radio reports. - errCodeUnsupportedCmd documented: a mis-gated request draws [0x01][0x01], not [0x01][0x06]. Neither is 0xC3-prefixed, so no new error path. Gating is on the bit alone, never model or version: firmware derives it from a runtime FEM probe, so two Heltec V4s can legitimately disagree and rak3401 reports false by design (its SKY66122 gates LNA and PA together). 13 tests. Not hardware-validated — firmware 0xC3 is still on a branch. Co-Authored-By: Claude Opus 4.8 --- lib/connector/meshcore_connector.dart | 57 ++++++++++++++++++++++++ lib/connector/meshcore_protocol.dart | 20 +++++++-- lib/l10n/app_en.arb | 2 + lib/l10n/app_localizations.dart | 12 +++++ lib/l10n/app_localizations_bg.dart | 7 +++ lib/l10n/app_localizations_de.dart | 7 +++ lib/l10n/app_localizations_en.dart | 7 +++ lib/l10n/app_localizations_es.dart | 7 +++ lib/l10n/app_localizations_fr.dart | 7 +++ lib/l10n/app_localizations_hu.dart | 7 +++ lib/l10n/app_localizations_it.dart | 7 +++ lib/l10n/app_localizations_ja.dart | 7 +++ lib/l10n/app_localizations_ko.dart | 7 +++ lib/l10n/app_localizations_nl.dart | 7 +++ lib/l10n/app_localizations_pl.dart | 7 +++ lib/l10n/app_localizations_pt.dart | 7 +++ lib/l10n/app_localizations_ru.dart | 7 +++ lib/l10n/app_localizations_sk.dart | 7 +++ lib/l10n/app_localizations_sl.dart | 7 +++ lib/l10n/app_localizations_sv.dart | 7 +++ lib/l10n/app_localizations_uk.dart | 7 +++ lib/l10n/app_localizations_zh.dart | 7 +++ lib/screens/settings_screen.dart | 17 +++++++ test/connector/offband_fem_lna_test.dart | 50 +++++++++++++++++++++ untranslated.json | 34 ++++++++++++++ 25 files changed, 315 insertions(+), 3 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 7ae9333..f6fd648 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -282,6 +282,7 @@ class MeshCoreConnector extends ChangeNotifier { String? _firmwareVersion; String? _deviceModel; int? _offbandCaps; + bool? _femLnaEnabled; int _pathHashByteWidth = 1; CompanionRadioStats? _latestRadioStats; Stopwatch? _airtimeBumpStopwatch; @@ -589,6 +590,48 @@ class MeshCoreConnector extends ChangeNotifier { /// True when the radio's block store hit `MAX_BLOCKED_KEYS` (32) and rejected /// an ADD (ok=0). Local block still applies; those keys just aren't portable. bool get blockOffloadStoreFull => _blockOffloadStoreFull; + + /// Whether this specific radio can control its external FEM LNA (#304). + /// + /// Gated on the capability BIT alone — firmware derives it from a runtime FEM + /// probe, so it is a per-unit answer: two Heltec V4s can legitimately disagree + /// depending on the fitted chip, and `rak3401` reports false by design (its + /// SKY66122 gates LNA and PA off one line, so a toggle would kill TX). + /// Never shortcut this to a model or version check. + bool get supportsOffbandFemLna => firmwareSupportsOffbandFemLna(_offbandCaps); + + /// Current FEM LNA state as last reported by the radio, or null if unknown + /// (pre-v16 firmware). Always reflects hardware truth, never a local guess. + bool? get femLnaEnabled => _femLnaEnabled; + + /// Ask the radio to enable or bypass its FEM LNA. No-op unless the capability + /// bit is set, so a non-capable radio never sees `0xC3` traffic. State is + /// updated from the reply, not optimistically. + Future setFemLna(bool enabled) async { + if (!isConnected || !supportsOffbandFemLna) return; + await sendFrame(buildOffbandFemLnaSetFrame(enabled)); + } + + /// Fallback read; device-info (offset 83) is the primary source on connect. + Future requestFemLnaState() async { + if (!isConnected || !supportsOffbandFemLna) return; + await sendFrame(buildOffbandFemLnaGetFrame()); + } + + /// `[0xC3][sub][value]` — the value is the post-apply hardware state, so it is + /// adopted verbatim rather than assuming a SET took effect (#304). + void _handleOffbandFemLnaReply(Uint8List frame) { + final reply = parseOffbandFemLnaReply(frame); + if (reply == null) return; + if (_femLnaEnabled == reply.enabled) return; + _femLnaEnabled = reply.enabled; + appLogger.info( + 'FEM LNA now ${reply.enabled ? 'enabled' : 'bypassed'}', + tag: 'Connector', + ); + notifyListeners(); + } + Map? get currentCustomVars => _currentCustomVars; int? get batteryMillivolts => _batteryMillivolts; int? get storageUsedKb => _storageUsedKb; @@ -4323,6 +4366,9 @@ class MeshCoreConnector extends ChangeNotifier { case cmdOffbandBlock: _handleOffbandBlockFrame(frame); break; + case cmdOffbandFemLna: + _handleOffbandFemLnaReply(frame); + break; case respCodeSelfInfo: debugPrint('Got SELF_INFO'); _handleSelfInfo(frame); @@ -4627,6 +4673,14 @@ class MeshCoreConnector extends ChangeNotifier { static int? parseOffbandCaps(Uint8List frame) => frame.length >= 83 ? frame[82] : null; + /// FEM LNA state byte, appended immediately after the caps byte in device-info + /// v16+ (firmware #298). Appended **unconditionally** — including on + /// non-capable boards, where it reads 0 — so the byte's presence indicates + /// firmware version, not capability. The capability BIT is what decides + /// whether to render the control. Null on v15 and older (shorter frame). + static bool? parseFemLnaState(Uint8List frame) => + frame.length >= 84 ? frame[83] != femLnaBypass : null; + void _handleDeviceInfo(Uint8List frame) { if (frame.length < 4) return; if (_shouldGateInitialChannelSync) { @@ -4674,6 +4728,9 @@ class MeshCoreConnector extends ChangeNotifier { // Offband config capability v14+ (byte 82). Extracted + bounds-checked in a // testable helper; offset verified against firmware (see parseOffbandCaps). _offbandCaps = parseOffbandCaps(frame); + // FEM LNA state rides one byte past the caps byte on v16+ (#304). Primary + // read on connect — a 0xC3 GET is only the fallback. + _femLnaEnabled = parseFemLnaState(frame); // Caps just landed; (re)evaluate GPS polling in case a `gps=1` custom-var // frame arrived before this device-info reply set support. (#144) _reconcileGpsPolling(); diff --git a/lib/connector/meshcore_protocol.dart b/lib/connector/meshcore_protocol.dart index e5f8821..f486e79 100644 --- a/lib/connector/meshcore_protocol.dart +++ b/lib/connector/meshcore_protocol.dart @@ -287,9 +287,17 @@ Uint8List buildOffbandFemLnaSetFrame(bool enabled) => Uint8List.fromList([ Uint8List buildOffbandFemLnaGetFrame() => Uint8List.fromList([cmdOffbandFemLna, offbandFemLnaGet]); -/// Reply to a `0xC3` request: `[0xC3][sub][value]`. A malformed request draws -/// the generic `[respCodeErr][errCodeIllegalArg]` instead, which is NOT -/// 0xC3-prefixed and so never reaches this parser. +/// Reply to a `0xC3` request: `[0xC3][sub][value]`. +/// +/// The value is the **post-apply hardware state, not an echo of the request** +/// (firmware #298 as-built): if the FEM ever refused a write, this reports the +/// truth rather than confirming a change that didn't take. Always render from +/// this value; never assume the written value stuck. +/// +/// Error replies are never 0xC3-prefixed, so they don't reach this parser: +/// malformed → `[respCodeErr][errCodeIllegalArg]`; a request to a non-capable +/// board → `[respCodeErr][errCodeUnsupportedCmd]` (unreachable when gated on +/// the capability bit, but firmware answers it defensively). class OffbandFemLnaReply { const OffbandFemLnaReply(this.subType, this.value); final int subType; @@ -316,6 +324,12 @@ const int offbandBlockClear = 0x04; /// app must recognise the 2-byte error frame and not wait for a 0xC2 echo. const int errCodeIllegalArg = 6; +/// `ERR_CODE_UNSUPPORTED_CMD` — returned for an Offband command the connected +/// board can't service (e.g. a `0xC3` FEM LNA request to a board without FEM +/// control). Unreachable when the capability bit is respected; firmware answers +/// it defensively against a stale or mis-gated client. (#304) +const int errCodeUnsupportedCmd = 1; + Uint8List buildOffbandBlockAddFrame(Uint8List pubKey) => Uint8List.fromList([cmdOffbandBlock, offbandBlockAdd, ...pubKey]); Uint8List buildOffbandBlockRemoveFrame(Uint8List pubKey) => diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 88673f8..f66e71d 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -253,6 +253,8 @@ "settings_txPowerInvalid": "Invalid TX power (0-22 dBm)", "settings_clientRepeat": "Off-Grid Repeat", "settings_clientRepeatSubtitle": "Allow this device to repeat mesh packets for others", + "settings_femLna": "Receive amplifier (FEM LNA)", + "settings_femLnaSubtitle": "Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.", "settings_clientRepeatFreqWarning": "Off-grid repeat requires 433, 869, or 918 MHz frequency", "settings_error": "Error: {message}", "@settings_error": { diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 5ddefeb..c0a16b8 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1276,6 +1276,18 @@ abstract class AppLocalizations { /// **'Allow this device to repeat mesh packets for others'** String get settings_clientRepeatSubtitle; + /// No description provided for @settings_femLna. + /// + /// In en, this message translates to: + /// **'Receive amplifier (FEM LNA)'** + String get settings_femLna; + + /// No description provided for @settings_femLnaSubtitle. + /// + /// In en, this message translates to: + /// **'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'** + String get settings_femLnaSubtitle; + /// No description provided for @settings_clientRepeatFreqWarning. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_bg.dart b/lib/l10n/app_localizations_bg.dart index f1f49ef..263706c 100644 --- a/lib/l10n/app_localizations_bg.dart +++ b/lib/l10n/app_localizations_bg.dart @@ -642,6 +642,13 @@ class AppLocalizationsBg extends AppLocalizations { String get settings_clientRepeatSubtitle => 'Позволете на това устройство да предава пакети към мрежата за други устройства.'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => 'За повторение извън мрежата са необходими честоти от 433, 869 или 918 MHz.'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index eac244b..c90f7d3 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -637,6 +637,13 @@ class AppLocalizationsDe extends AppLocalizations { String get settings_clientRepeatSubtitle => 'Ermöglichen Sie diesem Gerät, Mesh-Pakete für andere zu wiederholen.'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => 'Die Kommunikation ohne Stromversorgung erfordert Frequenzen von 433, 869 oder 918 MHz.'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 3f25139..a274d5b 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -627,6 +627,13 @@ class AppLocalizationsEn extends AppLocalizations { String get settings_clientRepeatSubtitle => 'Allow this device to repeat mesh packets for others'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => 'Off-grid repeat requires 433, 869, or 918 MHz frequency'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index aee1983..127c588 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -638,6 +638,13 @@ class AppLocalizationsEs extends AppLocalizations { String get settings_clientRepeatSubtitle => 'Permita que este dispositivo repita los paquetes de red para otros usuarios.'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => 'Para la comunicación fuera de la red, se requiere una frecuencia de 433, 869 o 918 MHz.'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 04cd4e9..560c8fc 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -642,6 +642,13 @@ class AppLocalizationsFr extends AppLocalizations { String get settings_clientRepeatSubtitle => 'Permettez à cet appareil de répéter les paquets de données pour les autres.'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => 'Pour les transmissions hors réseau, il est nécessaire d\'utiliser les fréquences de 433, 869 ou 918 MHz.'; diff --git a/lib/l10n/app_localizations_hu.dart b/lib/l10n/app_localizations_hu.dart index 48f7f97..3205556 100644 --- a/lib/l10n/app_localizations_hu.dart +++ b/lib/l10n/app_localizations_hu.dart @@ -642,6 +642,13 @@ class AppLocalizationsHu extends AppLocalizations { String get settings_clientRepeatSubtitle => 'Engedje, hogy ez a eszköz mások számára is ismételje a hálózati csomagokat.'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => 'A hálózat nélküli kommunikációhoz 433, 869 vagy 918 MHz frekvenciát igényel.'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 875eedc..d1b43bf 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -640,6 +640,13 @@ class AppLocalizationsIt extends AppLocalizations { String get settings_clientRepeatSubtitle => 'Permetti a questo dispositivo di ripetere i pacchetti di rete per gli altri.'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => 'Per la comunicazione fuori rete, è necessario utilizzare frequenze di 433, 869 o 918 MHz.'; diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index 4546578..73c62b1 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -613,6 +613,13 @@ class AppLocalizationsJa extends AppLocalizations { String get settings_clientRepeatSubtitle => 'このデバイスが、他のデバイスに対してメッシュパケットを繰り返し送信できるようにする。'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => 'オフグリッドでの再送には、433MHz、869MHz、または918MHzの周波数が必要です。'; diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index d45ac1b..4d50ab1 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -613,6 +613,13 @@ class AppLocalizationsKo extends AppLocalizations { String get settings_clientRepeatSubtitle => '이 장치가 다른 사람들을 위해 메시 패킷을 반복하도록 허용합니다.'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => '오프그리드(무전력) 시스템 재연결에는 433MHz, 869MHz, 또는 918MHz 주파수가 필요합니다.'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 5d857ce..7f2d73f 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -634,6 +634,13 @@ class AppLocalizationsNl extends AppLocalizations { String get settings_clientRepeatSubtitle => 'Laat dit apparaat de berichten van andere apparaten doorsturen.'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => 'Om een signaal buiten het netwerk te versturen, zijn frequenties van 433, 869 of 918 MHz vereist.'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 72d7782..10f7852 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -643,6 +643,13 @@ class AppLocalizationsPl extends AppLocalizations { String get settings_clientRepeatSubtitle => 'Pozwól temu urządzeniu powtarzać pakiety danych dla innych urządzeń.'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => 'Powtórka poza siecią wymaga częstotliwości 433, 869 lub 918 MHz.'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index f7f33a4..b929742 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -640,6 +640,13 @@ class AppLocalizationsPt extends AppLocalizations { String get settings_clientRepeatSubtitle => 'Permita que este dispositivo repita pacotes de rede para outros dispositivos.'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => 'A repetição fora da rede requer frequências de 433, 869 ou 918 MHz.'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index c218e2c..e299f98 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -640,6 +640,13 @@ class AppLocalizationsRu extends AppLocalizations { String get settings_clientRepeatSubtitle => 'Позвольте этому устройству повторять пакеты данных для других устройств.'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => 'Для работы в режиме \"без подключения к сети\" требуется частота 433, 869 или 918 МГц.'; diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index 14e8a14..291d587 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -634,6 +634,13 @@ class AppLocalizationsSk extends AppLocalizations { String get settings_clientRepeatSubtitle => 'Umožnite, aby toto zariadenie opakovávalo siete pre ostatných.'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => 'Použitie off-grid systému vyžaduje frekvencie 433, 869 alebo 918 MHz.'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index 518d551..88cbfbd 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -631,6 +631,13 @@ class AppLocalizationsSl extends AppLocalizations { String get settings_clientRepeatSubtitle => 'Omogočite temu naprave, da ponavlja paketne sporočila za druge.'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => 'Za ponovni prenos na brezžični način so potrebne frekvence 433, 869 ali 918 MHz.'; diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index c6f9fed..0262af4 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -629,6 +629,13 @@ class AppLocalizationsSv extends AppLocalizations { String get settings_clientRepeatSubtitle => 'Låt enheten repetera nätpaket för andra användare.'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => 'För att kunna kommunicera utanför elnätet krävs frekvenserna 433, 869 eller 918 MHz.'; diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index f0dab52..68c9be4 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -637,6 +637,13 @@ class AppLocalizationsUk extends AppLocalizations { String get settings_clientRepeatSubtitle => 'Дозвольте цьому пристрою повторювати пакети даних для інших пристроїв.'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => 'Повтор без підключення до мережі вимагає частоти 433, 869 або 918 МГц.'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index d39404c..09b81e5 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -604,6 +604,13 @@ class AppLocalizationsZh extends AppLocalizations { @override String get settings_clientRepeatSubtitle => '允许此设备重复发送网状数据包给其他设备'; + @override + String get settings_femLna => 'Receive amplifier (FEM LNA)'; + + @override + String get settings_femLnaSubtitle => + 'Boosts receive sensitivity. Leave on unless you are troubleshooting a strong nearby signal.'; + @override String get settings_clientRepeatFreqWarning => '离网重复通信需要使用 433、869 或 918 兆赫兹的频率。'; diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index 819687f..16b9e16 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -2192,6 +2192,23 @@ class _RadioSettingsFormState extends State<_RadioSettingsForm> { contentPadding: EdgeInsets.zero, ), ], + // Only this radio's own FEM probe decides whether this appears — never + // model or version (#304). Deliberately not mirrored into local state: + // firmware returns post-apply hardware truth, so the switch renders + // what the radio reports rather than what we asked for. + if (widget.connector.supportsOffbandFemLna) ...[ + const SizedBox(height: 16), + ListenableBuilder( + listenable: widget.connector, + builder: (context, _) => SwitchListTile( + title: Text(l10n.settings_femLna), + subtitle: Text(l10n.settings_femLnaSubtitle), + value: widget.connector.femLnaEnabled ?? true, + onChanged: (value) => widget.connector.setFemLna(value), + contentPadding: EdgeInsets.zero, + ), + ), + ], const SizedBox(height: 16), SizedBox( width: double.infinity, diff --git a/test/connector/offband_fem_lna_test.dart b/test/connector/offband_fem_lna_test.dart index 2768dd8..df56d6a 100644 --- a/test/connector/offband_fem_lna_test.dart +++ b/test/connector/offband_fem_lna_test.dart @@ -1,6 +1,7 @@ import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; +import 'package:meshcore_open/connector/meshcore_connector.dart'; import 'package:meshcore_open/connector/meshcore_protocol.dart'; void main() { @@ -28,6 +29,55 @@ void main() { }); }); + group('FEM LNA device-info state byte, offset 83 (#304)', () { + Uint8List deviceInfo({required int length, int femByte = 0}) { + final frame = Uint8List(length); + frame[0] = respCodeDeviceInfo; + if (length >= 84) frame[83] = femByte; + return frame; + } + + test('reads the byte immediately after caps on v16+', () { + expect( + MeshCoreConnector.parseFemLnaState(deviceInfo(length: 84, femByte: 1)), + isTrue, + ); + expect( + MeshCoreConnector.parseFemLnaState(deviceInfo(length: 84, femByte: 0)), + isFalse, + ); + }); + + test('null on pre-v16 firmware that stops at the caps byte', () { + expect( + MeshCoreConnector.parseFemLnaState(deviceInfo(length: 83)), + isNull, + ); + expect(MeshCoreConnector.parseFemLnaState(Uint8List(0)), isNull); + }); + + test('does not disturb the caps byte at offset 82', () { + final frame = deviceInfo(length: 84, femByte: 1); + frame[82] = offbandCapBlock | offbandCapFemLna; + expect(MeshCoreConnector.parseOffbandCaps(frame), equals(0x06)); + expect(MeshCoreConnector.parseFemLnaState(frame), isTrue); + }); + + test('byte is present on non-capable boards and reads as bypassed', () { + // Firmware appends it unconditionally, so presence indicates version, + // not capability — the cap bit is what gates the UI. + final frame = deviceInfo(length: 84, femByte: 0); + frame[82] = 0x00; + expect(MeshCoreConnector.parseFemLnaState(frame), isFalse); + expect( + firmwareSupportsOffbandFemLna( + MeshCoreConnector.parseOffbandCaps(frame), + ), + isFalse, + ); + }); + }); + group('FEM LNA frames (#304)', () { test('SET carries the enable value', () { expect( diff --git a/untranslated.json b/untranslated.json index 56be62e..31dc9b1 100644 --- a/untranslated.json +++ b/untranslated.json @@ -15,6 +15,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -73,6 +75,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -131,6 +135,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -189,6 +195,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -247,6 +255,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -305,6 +315,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -363,6 +375,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -421,6 +435,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -479,6 +495,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -537,6 +555,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -595,6 +615,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -653,6 +675,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -711,6 +735,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -769,6 +795,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -827,6 +855,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -885,6 +915,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -943,6 +975,8 @@ "settings_infoFirmware", "settings_infoModel", "settings_publicKeyCopied", + "settings_femLna", + "settings_femLnaSubtitle", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", From d6d5dbf846d982ee7f8ecabb6ba0ade0946485e2 Mon Sep 17 00:00:00 2001 From: Strycher Date: Sun, 19 Jul 2026 23:38:18 -0400 Subject: [PATCH 08/60] feat(#304): surface Offband capabilities in Device Info A capability-gated control that doesn't appear is indistinguishable from a broken one. Diagnosing "the FEM LNA toggle is missing" on real hardware required a rebuild, because the caps byte was never surfaced and the app debug log records nothing in a release build unless logging is enabled. Adds an "Offband capabilities" row to Device Info showing the raw caps byte, firmware version code, and which gated features it grants, e.g. "0x02 (v16) - block". That turned an unexplained missing toggle into a one-look answer: firmware v16 running, FEM LNA bit clear, client correct. Also logs the same values at device-info parse for anyone who does have logging on. Co-Authored-By: Claude Opus 4.8 --- lib/connector/meshcore_connector.dart | 10 ++++++ lib/l10n/app_en.arb | 1 + lib/l10n/app_localizations.dart | 6 ++++ lib/l10n/app_localizations_bg.dart | 3 ++ lib/l10n/app_localizations_de.dart | 3 ++ lib/l10n/app_localizations_en.dart | 3 ++ lib/l10n/app_localizations_es.dart | 3 ++ lib/l10n/app_localizations_fr.dart | 3 ++ lib/l10n/app_localizations_hu.dart | 3 ++ lib/l10n/app_localizations_it.dart | 3 ++ lib/l10n/app_localizations_ja.dart | 3 ++ lib/l10n/app_localizations_ko.dart | 3 ++ lib/l10n/app_localizations_nl.dart | 3 ++ lib/l10n/app_localizations_pl.dart | 3 ++ lib/l10n/app_localizations_pt.dart | 3 ++ lib/l10n/app_localizations_ru.dart | 3 ++ lib/l10n/app_localizations_sk.dart | 3 ++ lib/l10n/app_localizations_sl.dart | 3 ++ lib/l10n/app_localizations_sv.dart | 3 ++ lib/l10n/app_localizations_uk.dart | 3 ++ lib/l10n/app_localizations_zh.dart | 3 ++ lib/screens/settings_screen.dart | 11 ++++++ untranslated.json | 51 +++++++++++++++++++++++++++ 23 files changed, 133 insertions(+) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index f6fd648..04cbbbf 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -4731,6 +4731,16 @@ class MeshCoreConnector extends ChangeNotifier { // FEM LNA state rides one byte past the caps byte on v16+ (#304). Primary // read on connect — a 0xC3 GET is only the fallback. _femLnaEnabled = parseFemLnaState(frame); + // Capability-gated features are invisible when a bit is clear, which looks + // identical to a bug. Log the raw inputs so "the toggle didn't appear" can + // be told apart from "this radio says it can't". (#304) + _appDebugLogService?.info( + 'Offband caps=0x${(_offbandCaps ?? 0).toRadixString(16).padLeft(2, '0')} ' + 'verCode=${_firmwareVerCode ?? 0} frameLen=${frame.length} ' + 'femLnaByte=${_femLnaEnabled == null ? 'absent' : (_femLnaEnabled! ? '1' : '0')} ' + 'femCapable=$supportsOffbandFemLna blockCapable=$supportsOffbandBlock', + tag: 'Device', + ); // Caps just landed; (re)evaluate GPS polling in case a `gps=1` custom-var // frame arrived before this device-info reply set support. (#144) _reconcileGpsPolling(); diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index f66e71d..48c6f31 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -236,6 +236,7 @@ "settings_infoStatus": "Status", "settings_infoFirmware": "Firmware", "settings_infoModel": "Model", + "settings_infoOffbandCaps": "Offband capabilities", "settings_infoBattery": "Battery", "settings_infoPublicKey": "Public Key", "settings_publicKeyCopied": "Public key copied", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index c0a16b8..2c425e2 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1174,6 +1174,12 @@ abstract class AppLocalizations { /// **'Model'** String get settings_infoModel; + /// No description provided for @settings_infoOffbandCaps. + /// + /// In en, this message translates to: + /// **'Offband capabilities'** + String get settings_infoOffbandCaps; + /// No description provided for @settings_infoBattery. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_bg.dart b/lib/l10n/app_localizations_bg.dart index 263706c..ee62916 100644 --- a/lib/l10n/app_localizations_bg.dart +++ b/lib/l10n/app_localizations_bg.dart @@ -590,6 +590,9 @@ class AppLocalizationsBg extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => 'Батерия'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index c90f7d3..92c1102 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -585,6 +585,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => 'Akku'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index a274d5b..7989c98 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -575,6 +575,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => 'Battery'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 127c588..df55c77 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -586,6 +586,9 @@ class AppLocalizationsEs extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => 'Batería'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 560c8fc..e02d168 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -590,6 +590,9 @@ class AppLocalizationsFr extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => 'Batterie'; diff --git a/lib/l10n/app_localizations_hu.dart b/lib/l10n/app_localizations_hu.dart index 3205556..3dabd04 100644 --- a/lib/l10n/app_localizations_hu.dart +++ b/lib/l10n/app_localizations_hu.dart @@ -588,6 +588,9 @@ class AppLocalizationsHu extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => 'Akku'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index d1b43bf..c4ad345 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -588,6 +588,9 @@ class AppLocalizationsIt extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => 'Batteria'; diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index 73c62b1..1c2edbf 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -561,6 +561,9 @@ class AppLocalizationsJa extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => 'バッテリー'; diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index 4d50ab1..6403027 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -561,6 +561,9 @@ class AppLocalizationsKo extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => '배터리'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 7f2d73f..8e34076 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -582,6 +582,9 @@ class AppLocalizationsNl extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => 'Batterij'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 10f7852..2a034a7 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -590,6 +590,9 @@ class AppLocalizationsPl extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => 'Bateria'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index b929742..b0851fe 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -588,6 +588,9 @@ class AppLocalizationsPt extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => 'Bateria'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index e299f98..5ae0d11 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -587,6 +587,9 @@ class AppLocalizationsRu extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => 'Батарея'; diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index 291d587..0b885ac 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -582,6 +582,9 @@ class AppLocalizationsSk extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => 'Batéria'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index 88cbfbd..fa6c120 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -579,6 +579,9 @@ class AppLocalizationsSl extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => 'Baterija'; diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index 0262af4..a4e67f6 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -577,6 +577,9 @@ class AppLocalizationsSv extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => 'Batteri'; diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index 68c9be4..00aeeb1 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -585,6 +585,9 @@ class AppLocalizationsUk extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => 'Батарея'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index 09b81e5..051db0e 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -553,6 +553,9 @@ class AppLocalizationsZh extends AppLocalizations { @override String get settings_infoModel => 'Model'; + @override + String get settings_infoOffbandCaps => 'Offband capabilities'; + @override String get settings_infoBattery => '电池'; diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index 16b9e16..e7a030f 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -403,6 +403,17 @@ class _SettingsScreenState extends State { _buildInfoRow(l10n.settings_infoFirmware, firmwareVersion), if (deviceModel != null) _buildInfoRow(l10n.settings_infoModel, deviceModel), + // Capability-gated controls vanish silently when a bit is clear, + // which is indistinguishable from a bug. Surface the raw inputs so + // "missing feature" can be diagnosed without enabling logging. (#304) + if (connector.offbandCaps != null) + _buildInfoRow( + l10n.settings_infoOffbandCaps, + '0x${connector.offbandCaps!.toRadixString(16).padLeft(2, '0')}' + ' (v${connector.firmwareVerCode ?? 0})' + '${connector.supportsOffbandFemLna ? ' · FEM LNA' : ''}' + '${connector.supportsOffbandBlock ? ' · block' : ''}', + ), _buildBatteryInfoRow(context, connector), if (connector.selfName != null) _buildInfoRow(l10n.settings_nodeName, connector.selfName!), diff --git a/untranslated.json b/untranslated.json index 31dc9b1..c6bcee5 100644 --- a/untranslated.json +++ b/untranslated.json @@ -14,6 +14,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -21,6 +22,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -40,6 +42,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", @@ -74,6 +77,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -81,6 +85,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -100,6 +105,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", @@ -134,6 +140,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -141,6 +148,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -160,6 +168,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", @@ -194,6 +203,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -201,6 +211,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -220,6 +231,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", @@ -254,6 +266,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -261,6 +274,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -280,6 +294,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", @@ -314,6 +329,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -321,6 +337,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -340,6 +357,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", @@ -374,6 +392,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -381,6 +400,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -400,6 +420,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", @@ -434,6 +455,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -441,6 +463,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -460,6 +483,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", @@ -494,6 +518,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -501,6 +526,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -520,6 +546,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", @@ -554,6 +581,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -561,6 +589,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -580,6 +609,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", @@ -614,6 +644,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -621,6 +652,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -640,6 +672,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", @@ -674,6 +707,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -681,6 +715,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -700,6 +735,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", @@ -734,6 +770,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -741,6 +778,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -760,6 +798,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", @@ -794,6 +833,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -801,6 +841,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -820,6 +861,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", @@ -854,6 +896,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -861,6 +904,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -880,6 +924,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", @@ -914,6 +959,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -921,6 +967,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -940,6 +987,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", @@ -974,6 +1022,7 @@ "settings_gpsStatusNoResponse", "settings_infoFirmware", "settings_infoModel", + "settings_infoOffbandCaps", "settings_publicKeyCopied", "settings_femLna", "settings_femLnaSubtitle", @@ -981,6 +1030,7 @@ "appSettings_clockSystem", "appSettings_clock12h", "appSettings_clock24h", + "contacts_searchSensors", "channels_notifications", "channels_notifyAll", "channels_notifyMentionsOnly", @@ -1000,6 +1050,7 @@ "channels_qrAddConfirm", "channels_qrAdded", "channels_qrExists", + "listFilter_sensors", "contacts_repeaterPathTraceVia", "snrIndicator_noNeighbors", "block_block", From af6485f2e3565f1dc6f7ac6ffbd214772f574dca Mon Sep 17 00:00:00 2001 From: Strycher Date: Sun, 19 Jul 2026 03:34:43 -0400 Subject: [PATCH 09/60] fix(#284): summarise GIF notifications via GifHelper, not a duplicate regex Notifications summarised a GIF as "Sent a GIF" using their own hand-rolled `^g:[A-Za-z0-9_-]+$` regex rather than GifHelper. #282 changed the payload to `https://giphy.com/gifs/`, which that regex does not match, so GIF notifications regressed to dumping the raw URL into the tray. Detection now goes through `GifHelper.parseGif`, matching how the adjacent reaction case already delegates to ReactionHelper. That restores the summary and covers every form the app can send, including replies (`@[Name] ` + payload), which the old regex never matched even for legacy `g:`. Verified no other hand-rolled `^g:`/`^m:`/`^s:`/`^r:` payload regexes remain outside their owning helpers, so this class of drift is closed. Co-Authored-By: Claude Opus 4.8 --- lib/services/notification_service.dart | 3 +- test/services/notification_text_test.dart | 67 +++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 test/services/notification_text_test.dart diff --git a/lib/services/notification_service.dart b/lib/services/notification_service.dart index 6e41745..d526b81 100644 --- a/lib/services/notification_service.dart +++ b/lib/services/notification_service.dart @@ -4,6 +4,7 @@ import 'dart:ui'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter/foundation.dart'; +import '../helpers/gif_helper.dart'; import '../helpers/reaction_helper.dart'; import '../l10n/app_localizations.dart'; import '../utils/platform_info.dart'; @@ -154,7 +155,7 @@ class NotificationService { if (reaction != null) { return 'Reacted ${reaction.emoji}'; } - if (RegExp(r'^g:[A-Za-z0-9_-]+$').hasMatch(trimmed)) { + if (GifHelper.parseGif(trimmed) != null) { return 'Sent a GIF'; } return text; diff --git a/test/services/notification_text_test.dart b/test/services/notification_text_test.dart new file mode 100644 index 0000000..8875798 --- /dev/null +++ b/test/services/notification_text_test.dart @@ -0,0 +1,67 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:meshcore_open/helpers/gif_helper.dart'; +import 'package:meshcore_open/services/notification_service.dart'; + +// #284: notifications summarised a GIF as "Sent a GIF" using their own +// hand-rolled `^g:$` regex. #282 changed the payload to a Giphy URL, which +// that regex does not match, so notifications regressed to dumping the raw URL. +// Detection must go through GifHelper.parseGif so every payload form the app +// can actually send is summarised, and so the regex cannot drift again. +void main() { + const gifId = 'zaMiq1BvCdAVIiu3Mb'; + + group('GIF payloads are summarised, not dumped raw', () { + test('the current URL payload summarises', () { + expect( + NotificationService.formatNotificationText(GifHelper.encodeGif(gifId)), + 'Sent a GIF', + ); + }); + + test('a reply carrying a GIF summarises', () { + expect( + NotificationService.formatNotificationText( + '@[Some Node] ${GifHelper.encodeGif(gifId)}', + ), + 'Sent a GIF', + ); + }); + + test('a legacy g: payload still summarises', () { + expect( + NotificationService.formatNotificationText('g:$gifId'), + 'Sent a GIF', + ); + }); + + test('a legacy g: reply still summarises', () { + expect( + NotificationService.formatNotificationText('@[Some Node] g:$gifId'), + 'Sent a GIF', + ); + }); + }); + + group('everything else is untouched', () { + test('a reaction still summarises', () { + expect( + NotificationService.formatNotificationText('r:abcd:00'), + startsWith('Reacted '), + ); + }); + + test('ordinary text passes through unchanged', () { + expect( + NotificationService.formatNotificationText('hey are you there'), + 'hey are you there', + ); + }); + + test('a non-GIF URL is not mistaken for a GIF', () { + expect( + NotificationService.formatNotificationText('https://example.com/page'), + 'https://example.com/page', + ); + }); + }); +} From 5b2354905494e5b226a3984dd1a2821ab2de298b Mon Sep 17 00:00:00 2001 From: Strycher Date: Sun, 19 Jul 2026 04:47:30 -0400 Subject: [PATCH 10/60] feat(#283): render pasted Giphy/Tenor URLs inline behind a host allowlist Pasted GIF links rendered as plain text because parseGif only matched the compact forms. Adds GifHelper.resolveGifUrl, which maps any supported payload to the URL that renders it, and widens coverage to the links people actually paste. Allowlist is deliberately narrow, Giphy and Tenor only. Off-list URLs return null and stay plain tap-to-open links, never auto-fetched: auto-loading a stranger's URL leaks the viewer's IP and enables tracking-pixel abuse, and inline-rendering unmoderated hosts is a malware and inappropriate-content vector. Tests pin the rejections, including a lookalike host (giphy.com.evil.example) and a general image host (imgur). Newly rendered: - i.giphy.com/.gif and .webp, the form a browser copy produces - media.tenor.com and c.tenor.com direct assets A tenor.com/view/- PAGE url is deliberately NOT matched. The modern Tenor CDN path uses an opaque hash that cannot be derived from the page id, so resolving one requires the Tenor API and a key. Such a link stays plain rather than silently rendering the wrong image. Supporting it is a separate decision. Also removes the hardcoded 'https://media.giphy.com/media/$gifId/giphy.gif' literal duplicated across all four render call sites. That duplication is the same drift that caused the #284 notification regression, where a second hand-rolled copy of GIF detection went stale. Co-Authored-By: Claude Opus 4.8 --- lib/helpers/gif_helper.dart | 36 ++++++++- lib/screens/channel_chat_screen.dart | 24 +++--- lib/screens/chat_screen.dart | 24 +++--- test/helpers/gif_media_url_test.dart | 105 +++++++++++++++++++++++++++ 4 files changed, 162 insertions(+), 27 deletions(-) create mode 100644 test/helpers/gif_media_url_test.dart diff --git a/lib/helpers/gif_helper.dart b/lib/helpers/gif_helper.dart index f97ee3a..5cbf244 100644 --- a/lib/helpers/gif_helper.dart +++ b/lib/helpers/gif_helper.dart @@ -31,7 +31,14 @@ class GifHelper { final pageMatch = RegExp( r'^(?:https?:\/\/)?giphy\.com\/gifs\/(?:[^/?]*-)?([A-Za-z0-9_]+)\/?$', ).firstMatch(trimmed); - return pageMatch?.group(1); + if (pageMatch != null) { + return pageMatch.group(1); + } + // The CDN form people actually paste out of a browser. (#283) + final cdnMatch = RegExp( + r'^(?:https?:\/\/)?i\.giphy\.com\/([A-Za-z0-9_-]+)\.(?:gif|webp)$', + ).firstMatch(trimmed); + return cdnMatch?.group(1); } /// Encode a GIF as a Giphy page URL, a format parseGif() also parses. @@ -43,4 +50,31 @@ class GifHelper { static String encodeGif(String gifId) { return 'https://giphy.com/gifs/$gifId'; } + + /// Tenor's own CDN, the only Tenor form that is directly renderable. + /// + /// A `tenor.com/view/-` page URL is deliberately NOT matched: the + /// modern CDN path uses an opaque hash that cannot be derived from the page + /// id, so resolving one needs the Tenor API. Such a link stays plain text + /// rather than silently rendering the wrong image. (#283) + static final RegExp _tenorMedia = RegExp( + r'^(?:https?:\/\/)?(?:media|c)\.tenor\.com\/[A-Za-z0-9_\-\/]+\.(?:gif|webp)$', + ); + + /// The URL that renders [text], or null if it is not a GIF we will display. + /// + /// Rendering is restricted to an allowlist of curated GIF platforms (Giphy + /// and Tenor). Anything off-list returns null and stays a plain tap-to-open + /// link: auto-fetching an arbitrary URL leaks the viewer's IP and enables + /// tracking-pixel abuse, and inline-rendering unmoderated hosts is a malware + /// and inappropriate-content vector. Widening the allowlist is a deliberate + /// decision, not a convenience. (#283) + static String? resolveGifUrl(String text) { + final gifId = parseGif(text); + if (gifId != null) { + return 'https://media.giphy.com/media/$gifId/giphy.gif'; + } + final trimmed = text.trim().replaceFirst(RegExp(r'^@\[[^\]]+\]\s+'), ''); + return _tenorMedia.hasMatch(trimmed) ? trimmed : null; + } } diff --git a/lib/screens/channel_chat_screen.dart b/lib/screens/channel_chat_screen.dart index 60f4c87..68cc7dd 100644 --- a/lib/screens/channel_chat_screen.dart +++ b/lib/screens/channel_chat_screen.dart @@ -642,8 +642,8 @@ class _ChannelChatScreenState extends State { final enableTracing = settingsService.settings.enableMessageTracing; final hashWidth = context.read().pathHashByteWidth; final isOutgoing = message.isOutgoing; - final gifId = GifHelper.parseGif(message.text); - final gifReplyName = gifId != null + final gifUrl = GifHelper.resolveGifUrl(message.text); + final gifReplyName = gifUrl != null ? TranslatedMessageContent.leadingReplyName(message.text) : null; final poi = parseMarkerText(message.text); @@ -689,7 +689,7 @@ class _ChannelChatScreenState extends State { ? (_) => _showMessageActions(message) : null, child: Container( - padding: gifId != null + padding: gifUrl != null ? const EdgeInsets.all(4) : const EdgeInsets.symmetric(horizontal: 12, vertical: 8), constraints: BoxConstraints( @@ -706,7 +706,7 @@ class _ChannelChatScreenState extends State { children: [ if (!isOutgoing) ...[ Padding( - padding: gifId != null + padding: gifUrl != null ? const EdgeInsets.only( left: 8, top: 4, @@ -722,7 +722,7 @@ class _ChannelChatScreenState extends State { ), ), ), - if (gifId == null) const SizedBox(height: 4), + if (gifUrl == null) const SizedBox(height: 4), ], if (poi != null) _buildPoiMessage( @@ -732,7 +732,7 @@ class _ChannelChatScreenState extends State { textScale, message.senderName, ) - else if (gifId != null) + else if (gifUrl != null) Column( crossAxisAlignment: isOutgoing ? CrossAxisAlignment.end @@ -752,8 +752,7 @@ class _ChannelChatScreenState extends State { ClipRRect( borderRadius: BorderRadius.circular(8), child: GifMessage( - url: - 'https://media.giphy.com/media/$gifId/giphy.gif', + url: gifUrl, backgroundColor: Colors.transparent, fallbackTextColor: isOutgoing ? Theme.of(context) @@ -797,7 +796,7 @@ class _ChannelChatScreenState extends State { if (enableTracing && displayPath.isNotEmpty) ...[ const SizedBox(height: 2), Padding( - padding: gifId != null + padding: gifUrl != null ? const EdgeInsets.symmetric(horizontal: 8) : EdgeInsets.zero, child: Text( @@ -1161,8 +1160,8 @@ class _ChannelChatScreenState extends State { child: ValueListenableBuilder( valueListenable: _textController, builder: (context, value, child) { - final gifId = GifHelper.parseGif(value.text); - if (gifId != null) { + final gifUrl = GifHelper.resolveGifUrl(value.text); + if (gifUrl != null) { return Focus( autofocus: true, onKeyEvent: (node, event) { @@ -1181,8 +1180,7 @@ class _ChannelChatScreenState extends State { child: ClipRRect( borderRadius: BorderRadius.circular(12), child: GifMessage( - url: - 'https://media.giphy.com/media/$gifId/giphy.gif', + url: gifUrl, backgroundColor: Theme.of( context, ).colorScheme.surfaceContainerHighest, diff --git a/lib/screens/chat_screen.dart b/lib/screens/chat_screen.dart index 156378e..c43886b 100644 --- a/lib/screens/chat_screen.dart +++ b/lib/screens/chat_screen.dart @@ -621,8 +621,8 @@ class _ChatScreenState extends State { child: ValueListenableBuilder( valueListenable: _textController, builder: (context, value, child) { - final gifId = GifHelper.parseGif(value.text); - if (gifId != null) { + final gifUrl = GifHelper.resolveGifUrl(value.text); + if (gifUrl != null) { return Focus( autofocus: true, onKeyEvent: (node, event) { @@ -641,8 +641,7 @@ class _ChatScreenState extends State { child: ClipRRect( borderRadius: BorderRadius.circular(12), child: GifMessage( - url: - 'https://media.giphy.com/media/$gifId/giphy.gif', + url: gifUrl, backgroundColor: colorScheme.surfaceContainerHighest, fallbackTextColor: colorScheme.onSurface @@ -1866,7 +1865,7 @@ class _MessageBubble extends StatelessWidget { final enableTracing = settingsService.settings.enableMessageTracing; final isOutgoing = message.isOutgoing; final colorScheme = Theme.of(context).colorScheme; - final gifId = GifHelper.parseGif(message.text); + final gifUrl = GifHelper.resolveGifUrl(message.text); final poi = parseMarkerText(message.text); final isFailed = message.status == MessageStatus.failed; final bubbleColor = isFailed @@ -1915,7 +1914,7 @@ class _MessageBubble extends StatelessWidget { ], Flexible( child: Container( - padding: gifId != null + padding: gifUrl != null ? const EdgeInsets.all(4) : const EdgeInsets.symmetric( horizontal: 12, @@ -1933,7 +1932,7 @@ class _MessageBubble extends StatelessWidget { children: [ if (!isOutgoing) ...[ Padding( - padding: gifId != null + padding: gifUrl != null ? const EdgeInsets.only( left: 8, top: 4, @@ -1949,7 +1948,7 @@ class _MessageBubble extends StatelessWidget { ), ), ), - if (gifId == null) const SizedBox(height: 4), + if (gifUrl == null) const SizedBox(height: 4), ], if (poi != null) _buildPoiMessage( @@ -1974,14 +1973,13 @@ class _MessageBubble extends StatelessWidget { ) : null, ) - else if (gifId != null) + else if (gifUrl != null) Stack( children: [ ClipRRect( borderRadius: BorderRadius.circular(12), child: GifMessage( - url: - 'https://media.giphy.com/media/$gifId/giphy.gif', + url: gifUrl, backgroundColor: Colors.transparent, fallbackTextColor: textColor.withValues( alpha: 0.7, @@ -2053,7 +2051,7 @@ class _MessageBubble extends StatelessWidget { if (isOutgoing && message.retryCount > 0) ...[ const SizedBox(height: 4), Padding( - padding: gifId != null + padding: gifUrl != null ? const EdgeInsets.symmetric(horizontal: 8) : EdgeInsets.zero, child: Text( @@ -2074,7 +2072,7 @@ class _MessageBubble extends StatelessWidget { ], const SizedBox(height: 4), Padding( - padding: gifId != null + padding: gifUrl != null ? const EdgeInsets.only( left: 8, right: 8, diff --git a/test/helpers/gif_media_url_test.dart b/test/helpers/gif_media_url_test.dart new file mode 100644 index 0000000..7ec3f5b --- /dev/null +++ b/test/helpers/gif_media_url_test.dart @@ -0,0 +1,105 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:meshcore_open/helpers/gif_helper.dart'; + +// #283: render pasted GIF/media URLs inline, but ONLY from an allowlist of +// curated hosts (Giphy + Tenor). Anything off-list must stay a plain link and +// must never be auto-fetched: auto-loading a stranger's URL leaks the viewer's +// IP and enables tracking-pixel / read-receipt abuse, and inline-rendering +// arbitrary hosts is a malware and inappropriate-content vector. +void main() { + const gifId = 'zaMiq1BvCdAVIiu3Mb'; + const giphyRender = 'https://media.giphy.com/media/$gifId/giphy.gif'; + + group('Giphy forms all resolve to a renderable URL', () { + test('the app payload (giphy page URL)', () { + expect( + GifHelper.resolveGifUrl('https://giphy.com/gifs/$gifId'), + giphyRender, + ); + }); + + test('legacy g:', () { + expect(GifHelper.resolveGifUrl('g:$gifId'), giphyRender); + }); + + test('media.giphy.com direct asset', () { + expect(GifHelper.resolveGifUrl(giphyRender), giphyRender); + }); + + test('i.giphy.com webp, the form users actually paste', () { + expect( + GifHelper.resolveGifUrl('https://i.giphy.com/$gifId.webp'), + giphyRender, + ); + }); + + test('i.giphy.com gif', () { + expect( + GifHelper.resolveGifUrl('https://i.giphy.com/$gifId.gif'), + giphyRender, + ); + }); + + test('a reply carrying a pasted Giphy URL', () { + expect( + GifHelper.resolveGifUrl('@[Some Node] https://i.giphy.com/$gifId.webp'), + giphyRender, + ); + }); + }); + + group('Tenor direct media resolves to itself', () { + test('media.tenor.com gif', () { + const url = 'https://media.tenor.com/abc123XYZ/happy-dance.gif'; + expect(GifHelper.resolveGifUrl(url), url); + }); + + test('c.tenor.com gif', () { + const url = 'https://c.tenor.com/abc123XYZ/tenor.gif'; + expect(GifHelper.resolveGifUrl(url), url); + }); + + test('media.tenor.com webp', () { + const url = 'https://media.tenor.com/abc123XYZ/thing.webp'; + expect(GifHelper.resolveGifUrl(url), url); + }); + }); + + group('off-allowlist URLs must NOT render (never auto-fetch)', () { + test('an arbitrary https image', () { + expect( + GifHelper.resolveGifUrl('https://evil.example.com/tracker.gif'), + isNull, + ); + }); + + test('a lookalike host is not trusted', () { + expect( + GifHelper.resolveGifUrl('https://giphy.com.evil.example/$gifId.gif'), + isNull, + ); + }); + + test('a general image host (imgur) is off-list', () { + expect(GifHelper.resolveGifUrl('https://i.imgur.com/abc123.gif'), isNull); + }); + + test('plain text is not a GIF', () { + expect(GifHelper.resolveGifUrl('hey are you there'), isNull); + }); + + test('a non-media URL is not a GIF', () { + expect(GifHelper.resolveGifUrl('https://example.com/page'), isNull); + }); + + test('a tenor PAGE url does not render (needs API resolution)', () { + // Modern Tenor CDN paths use an opaque hash not derivable from the page + // id, so a page URL cannot be resolved without the Tenor API. Stays a + // plain link rather than silently rendering the wrong thing. + expect( + GifHelper.resolveGifUrl('https://tenor.com/view/happy-dance-12345'), + isNull, + ); + }); + }); +} From b4fe9c145b8857f4085e118ecce918701eb2878b Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 01:01:18 -0400 Subject: [PATCH 11/60] fix(#283): summarise the same GIF set the chat renders formatNotificationText used GifHelper.parseGif (Giphy only) while the chat renders via resolveGifUrl (Giphy + allowlisted Tenor CDN). An allowlisted Tenor GIF therefore rendered inline but still dumped a raw URL into the notification tray. Point both at resolveGifUrl so the tray summarises exactly the set the chat displays. Off-allowlist URLs still pass through as plain text, pinned by test. This is the same duplicate-detection drift that caused #284; closing it at the source rather than shipping a known inconsistency. Co-Authored-By: Claude Opus 4.8 --- lib/services/notification_service.dart | 4 +++- test/services/notification_text_test.dart | 29 +++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/services/notification_service.dart b/lib/services/notification_service.dart index d526b81..f08d88a 100644 --- a/lib/services/notification_service.dart +++ b/lib/services/notification_service.dart @@ -155,7 +155,9 @@ class NotificationService { if (reaction != null) { return 'Reacted ${reaction.emoji}'; } - if (GifHelper.parseGif(trimmed) != null) { + // resolveGifUrl, not parseGif: the tray must summarise exactly the set the + // chat renders inline, or an allowlisted Tenor GIF shows as a raw URL. (#283) + if (GifHelper.resolveGifUrl(trimmed) != null) { return 'Sent a GIF'; } return text; diff --git a/test/services/notification_text_test.dart b/test/services/notification_text_test.dart index 8875798..3da21ad 100644 --- a/test/services/notification_text_test.dart +++ b/test/services/notification_text_test.dart @@ -64,4 +64,33 @@ void main() { ); }); }); + + group('allowlisted media URLs summarise too (#283 consistency)', () { + test('a pasted i.giphy.com link summarises', () { + expect( + NotificationService.formatNotificationText( + 'https://i.giphy.com/$gifId.webp', + ), + 'Sent a GIF', + ); + }); + + test('a pasted Tenor CDN link summarises', () { + expect( + NotificationService.formatNotificationText( + 'https://media.tenor.com/abc123XYZ/happy-dance.gif', + ), + 'Sent a GIF', + ); + }); + + test('an off-allowlist image URL does NOT summarise', () { + expect( + NotificationService.formatNotificationText( + 'https://evil.example.com/tracker.gif', + ), + 'https://evil.example.com/tracker.gif', + ); + }); + }); } From 1d6558ef8aaad93e534021d8c383d2170da96b38 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 02:26:43 -0400 Subject: [PATCH 12/60] chore(#331): pin Flutter to 3.44.1 in every workflow Three different Flutter versions were in play and none was the bench: build.yml channel: stable (floating, resolving to 3.44.6) flutter_dart.yml channel: stable (floating) deploy.yml pinned 3.41.2, with a comment claiming it matched local development, which had not been true for months bench 3.44.1 / Dart 3.12.1 A floating toolchain is tolerable for a compile check and not tolerable once CI produces signed release artifacts (#330), so this lands first. Pinned all 8 flutter-action usages to 3.44.1, matching the bench exactly, so "works locally" and "works in CI" mean the same thing. Verified against Flutter's release index that 3.44.1 is a published stable release (2026-06-01, Dart 3.12.1) rather than assuming a locally-installed version was publicly fetchable. Corrected deploy.yml's misleading local-parity comment rather than leaving it to mislead the next reader. Note: CI's green history is on 3.44.6 or later, so pinning DOWN may surface a break the newer toolchain masked. That is the intent. If it breaks, fix the break or move the bench up deliberately; do not re-float the pin. GIPHY_API_KEY as a CI secret was the third item of Phase 3 in #321. It is deliberately NOT included here, is not dropped, and is flagged on #331 for the owner's decision, since it is a credential only they can create. Part of epic #312, plan #321 (Phase 3). --- .github/workflows/build.yml | 12 ++++++------ .github/workflows/deploy.yml | 6 +++--- .github/workflows/flutter_dart.yml | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 58abccd..4720f5e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: java-version: "17" - uses: subosito/flutter-action@v2 with: - channel: "stable" + flutter-version: "3.44.1" cache: true - name: Cache Gradle uses: actions/cache@v4 @@ -45,7 +45,7 @@ jobs: - uses: actions/checkout@v4 - uses: subosito/flutter-action@v2 with: - channel: "stable" + flutter-version: "3.44.1" cache: true - run: flutter pub get - run: flutter build ios --release --no-codesign --no-pub @@ -56,7 +56,7 @@ jobs: - uses: actions/checkout@v4 - uses: subosito/flutter-action@v2 with: - channel: "stable" + flutter-version: "3.44.1" cache: true - name: Install Linux build deps run: sudo apt-get update && sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev @@ -76,7 +76,7 @@ jobs: - uses: actions/checkout@v4 - uses: subosito/flutter-action@v2 with: - channel: "stable" + flutter-version: "3.44.1" cache: true - run: flutter pub get - run: flutter build macos --release --no-pub @@ -87,7 +87,7 @@ jobs: - uses: actions/checkout@v4 - uses: subosito/flutter-action@v2 with: - channel: "stable" + flutter-version: "3.44.1" cache: true - run: flutter pub get - run: flutter build web --release --no-pub @@ -105,7 +105,7 @@ jobs: - uses: actions/checkout@v4 - uses: subosito/flutter-action@v2 with: - channel: "stable" + flutter-version: "3.44.1" cache: true - run: flutter pub get - run: flutter build windows --release --no-pub diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d53a271..42ed701 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -18,9 +18,9 @@ jobs: - name: Setup Flutter uses: subosito/flutter-action@v2 with: - channel: 'stable' - # Match local development version which provides Dart 3.11.0 - flutter-version: '3.41.2' + # Pinned to the bench toolchain (Dart 3.12.1). Was 3.41.2 with a + # comment claiming local parity that had not been true for months. + flutter-version: "3.44.1" - name: Setup Bun uses: oven-sh/setup-bun@v2 diff --git a/.github/workflows/flutter_dart.yml b/.github/workflows/flutter_dart.yml index e267557..bff800d 100644 --- a/.github/workflows/flutter_dart.yml +++ b/.github/workflows/flutter_dart.yml @@ -17,7 +17,7 @@ jobs: - name: Set up Flutter uses: subosito/flutter-action@v2 with: - channel: stable + flutter-version: "3.44.1" - name: Install dependencies run: flutter pub get From e17f539d7ec876f93716dd21b29c2efe7251466f Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 02:30:23 -0400 Subject: [PATCH 13/60] chore(#331): pass GIPHY_API_KEY into artifact builds Owner added GIPHY_API_KEY as a repo secret, resolving the open decision flagged on this issue. The app reads the key at COMPILE time via String.fromEnvironment('GIPHY_API_KEY') (lib/widgets/gif_picker.dart:25), so CI builds produced without it ship a non-functional GIF picker that displays a "needs a Giphy API key" hint. That was harmless while CI output was discarded; it stopped being harmless in #322 when artifacts became real deliverables. Generates dart_defines.json from the secret and passes --dart-define-from-file, matching how the project is built locally rather than inventing a second mechanism. Wired into the four jobs that produce artifacts users run: android, linux, web, windows. NOT wired into ios and macos, which are compile checks producing nothing installable, and where the key would have no effect on whether compilation succeeds. Uses jq on the Linux runners rather than hand-escaped printf, so the value is JSON-escaped correctly whatever it contains. An earlier printf attempt silently mangled an escape and broke the YAML, which is the argument for using the tool instead of string juggling. jq is verified preinstalled on the runner images (Ubuntu 22.04 has 1.6, 24.04 has 1.7.1). The windows runner uses PowerShell ConvertTo-Json, since jq is not guaranteed there. Note: the key is compiled into the binary and is therefore extractable from any published APK. Putting it in CI does not widen exposure beyond what shipping the app already does. Part of epic #312, plan #321 (Phase 3). --- .github/workflows/build.yml | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4720f5e..238f426 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,7 +30,11 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - run: flutter pub get - - run: flutter build apk --release --no-pub + - name: Write dart_defines.json + env: + GIPHY_API_KEY: ${{ secrets.GIPHY_API_KEY }} + run: jq -n --arg k "$GIPHY_API_KEY" '{GIPHY_API_KEY:$k}' > dart_defines.json + - run: flutter build apk --release --no-pub --dart-define-from-file=dart_defines.json - name: Upload APK uses: actions/upload-artifact@v4 with: @@ -61,7 +65,11 @@ jobs: - name: Install Linux build deps run: sudo apt-get update && sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev - run: flutter pub get - - run: flutter build linux --release --no-pub + - name: Write dart_defines.json + env: + GIPHY_API_KEY: ${{ secrets.GIPHY_API_KEY }} + run: jq -n --arg k "$GIPHY_API_KEY" '{GIPHY_API_KEY:$k}' > dart_defines.json + - run: flutter build linux --release --no-pub --dart-define-from-file=dart_defines.json - name: Upload Linux bundle uses: actions/upload-artifact@v4 with: @@ -90,7 +98,11 @@ jobs: flutter-version: "3.44.1" cache: true - run: flutter pub get - - run: flutter build web --release --no-pub + - name: Write dart_defines.json + env: + GIPHY_API_KEY: ${{ secrets.GIPHY_API_KEY }} + run: jq -n --arg k "$GIPHY_API_KEY" '{GIPHY_API_KEY:$k}' > dart_defines.json + - run: flutter build web --release --no-pub --dart-define-from-file=dart_defines.json - name: Upload web build uses: actions/upload-artifact@v4 with: @@ -108,7 +120,15 @@ jobs: flutter-version: "3.44.1" cache: true - run: flutter pub get - - run: flutter build windows --release --no-pub + # Windows runner defaults to PowerShell; jq is not guaranteed there, + # so build the JSON with ConvertTo-Json, which escapes correctly. + - name: Write dart_defines.json + env: + GIPHY_API_KEY: ${{ secrets.GIPHY_API_KEY }} + run: | + @{ GIPHY_API_KEY = $env:GIPHY_API_KEY } | ConvertTo-Json -Compress | + Set-Content -Path dart_defines.json -Encoding utf8 -NoNewline + - run: flutter build windows --release --no-pub --dart-define-from-file=dart_defines.json - name: Upload Windows build uses: actions/upload-artifact@v4 with: From 2c80be32443e3502512958679cb4c6300ee3ad5c Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 04:09:08 -0400 Subject: [PATCH 14/60] fix(#333): load channels before channel history, or history reads empty Channel history is keyed by the channel's PSK (#194), and the resolver set on the store reads that PSK out of _channels. After SELF_INFO, loadCachedChannels() and loadAllChannelMessages() were both fired without awaiting, so the messages could load while _channels was still empty. The resolver then returned null, _storageKey() silently fell back to the legacy slot-index key, and channels read as empty even though their history was intact under the PSK key. Silent because the fallback is a legitimate code path: there is no error, no parse failure and no log line, just an empty list. It is indistinguishable from data loss to the user. Observed on a live radio: Public held 566 messages under channel_messages_psk_8b3387e9..., and the app displayed nothing. loadAllChannelMessages() now runs only after loadCachedChannels() completes. The startup call in main.dart already awaits in the right order; it runs before any radio is connected, which is the source of the "Public key hex is not set" warnings at launch, and is harmless once this post-connect load is correct. Was previously committed against #291 on the nav-redesign branch; split out here under its own issue. --- lib/connector/meshcore_connector.dart | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 04cbbbf..a27c2f4 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -4646,10 +4646,14 @@ class MeshCoreConnector extends ChangeNotifier { _loadChannelOrder(); loadContactCache(); loadChannelSettings(); - loadCachedChannels(); - // Load persisted channel messages - loadAllChannelMessages(); + // Channel history is keyed by the channel's PSK (#194), and the resolver + // set above reads that PSK out of _channels. So the channel list MUST be + // loaded before the messages are: loading them concurrently leaves the + // resolver returning null, _storageKey() silently falls back to the legacy + // slot-index key, and every channel reads as empty even though its history + // is intact under the PSK key. + unawaited(loadCachedChannels().then((_) => loadAllChannelMessages())); loadUnreadState(); _loadDiscoveredContactCache(); From 7f9f6ba63b6fdd065e992e7ba3d7e959c1e7870d Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 03:48:45 -0400 Subject: [PATCH 15/60] perf(#306): batch per-contact setting loads instead of notifying 600 times Diagnosed from the app's own log. After SELF_INFO the client went completely silent for 43.7s, then flushed a burst of radio frames all sharing the identical timestamp - queued while the event loop was blocked, not a slow radio. Cause: loadContactCache() looped over every cached contact calling _ensureContactSmazSettingLoaded and _ensureContactCyr2LatSettingLoaded, and each of those notified on completion. With 300 contacts that is 600 notifyListeners() calls in a burst, each rebuilding the whole widget tree, and each rebuild itself walks the contact list. O(contacts^2) on the UI isolate. Both loaders are now awaitable and take notify: false for bulk use. loadContactCache awaits them all and notifies once. Measured on the reporting device (radio 4f6585264e, TCP): 300 contacts, 1090 discovered contacts, 12 channels holding 1841 messages, 6.9 MB prefs file. Log evidence: 43.7s stall between "Pulled battery" and the first channel response, followed by same-millisecond frame delivery. This is the Windows-visible freeze in #306. The platform asymmetry is consistent with contact-list size rather than anything desktop-specific, so whether Android is genuinely faster or simply has a smaller address book on its paired radio is still open - #306 stays open pending a like-for-like re-measure. flutter analyze clean, dart format clean, 469 tests pass. --- lib/connector/meshcore_connector.dart | 49 ++++++++++++++++++--------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index a27c2f4..d86443b 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -1191,10 +1191,17 @@ class MeshCoreConnector extends ChangeNotifier { _contacts ..clear() ..addAll(cached); - for (final contact in cached) { - _ensureContactSmazSettingLoaded(contact.publicKeyHex); - _ensureContactCyr2LatSettingLoaded(contact.publicKeyHex); - } + // Load every contact's settings without notifying per contact, then + // notify once. Per-contact notification rebuilt the whole tree 2x per + // contact (600 rebuilds for 300 contacts), and each rebuild walks the + // contact list, which is what stalled the UI for ~44s on connect. + await Future.wait([ + for (final contact in cached) ...[ + _ensureContactSmazSettingLoaded(contact.publicKeyHex, notify: false), + _ensureContactCyr2LatSettingLoaded(contact.publicKeyHex, notify: false), + ], + ]); + notifyListeners(); } Future _loadDiscoveredContactCache() async { @@ -5625,22 +5632,32 @@ class MeshCoreConnector extends ChangeNotifier { return frame.sublist(prefixOffset, prefixOffset + prefixLen); } - void _ensureContactSmazSettingLoaded(String contactKeyHex) { + /// [notify] is false for bulk loads, which notify once at the end instead. + /// Notifying per contact rebuilds the whole tree once per contact, and each + /// rebuild itself walks the contact list, so a large address book turns this + /// into O(contacts^2) work on the UI isolate. + Future _ensureContactSmazSettingLoaded( + String contactKeyHex, { + bool notify = true, + }) async { if (_contactSmazEnabled.containsKey(contactKeyHex)) return; - _contactSettingsStore.loadSmazEnabled(contactKeyHex).then((enabled) { - if (_contactSmazEnabled[contactKeyHex] == enabled) return; - _contactSmazEnabled[contactKeyHex] = enabled; - notifyListeners(); - }); + final enabled = await _contactSettingsStore.loadSmazEnabled(contactKeyHex); + if (_contactSmazEnabled[contactKeyHex] == enabled) return; + _contactSmazEnabled[contactKeyHex] = enabled; + if (notify) notifyListeners(); } - void _ensureContactCyr2LatSettingLoaded(String contactKeyHex) { + Future _ensureContactCyr2LatSettingLoaded( + String contactKeyHex, { + bool notify = true, + }) async { if (_contactCyr2LatEnabled.containsKey(contactKeyHex)) return; - _contactSettingsStore.loadCyr2LatEnabled(contactKeyHex).then((enabled) { - if (_contactCyr2LatEnabled[contactKeyHex] == enabled) return; - _contactCyr2LatEnabled[contactKeyHex] = enabled; - notifyListeners(); - }); + final enabled = await _contactSettingsStore.loadCyr2LatEnabled( + contactKeyHex, + ); + if (_contactCyr2LatEnabled[contactKeyHex] == enabled) return; + _contactCyr2LatEnabled[contactKeyHex] = enabled; + if (notify) notifyListeners(); } void _ensureContactCyr2LatProfileLoaded(String contactKeyHex) { From bd8779697a70c4ffec90cf3a3ca0668f5ba2fb97 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 03:57:41 -0400 Subject: [PATCH 16/60] perf(#306): time startup loads and frame handlers to locate the UI stalls Two reported freezes: one shortly after connect, one during channel sync, with the sync progress bar never repainting and then completing all at once. That is the UI isolate being blocked, and the log already shows the signature - a long silence followed by many frames sharing one timestamp, which is queued input flushing after the loop frees up. The previous fix (batching 600 per-contact notifications) moved the stall from 43.6s to 40.1s, which is noise. It was the wrong culprit, so this replaces guessing with measurement: - The post-SELF_INFO cache load is split into named steps, each timed and logged as `startup-load took Nms`. The step order is unchanged, and cachedChannels still runs before channelMessages so PSK-keyed history resolves. - Every RX frame handler is timed, and any that occupies the isolate for more than 100ms logs `slow frame handler: code=N blocked UI for Nms`. This names the handler wherever it is, rather than requiring a guess about which one. No behaviour change. Diagnostics only, kept as Perf-tagged logging because this class of stall is worth catching again. flutter analyze clean, dart format clean, 469 tests pass. --- lib/connector/meshcore_connector.dart | 57 ++++++++++++++++++++------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index d86443b..1d297c8 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -4349,7 +4349,26 @@ class MeshCoreConnector extends ChangeNotifier { } } + /// Any frame handler that occupies the UI isolate long enough to drop + /// frames is a bug: it stalls rendering, so progress bars freeze and input + /// stops responding while sync appears to do nothing and then finish at + /// once. Logged rather than assumed, so the offending code is named. + static const Duration _slowHandlerThreshold = Duration(milliseconds: 100); + void _handleFrame(List data) { + final handlerWatch = Stopwatch()..start(); + _handleFrameInner(data); + handlerWatch.stop(); + if (handlerWatch.elapsed > _slowHandlerThreshold && data.isNotEmpty) { + appLogger.info( + 'slow frame handler: code=${data[0]} blocked UI for ' + '${handlerWatch.elapsedMilliseconds}ms', + tag: 'Perf', + ); + } + } + + void _handleFrameInner(List data) { if (data.isEmpty) return; _lastRxTime = DateTime.now(); @@ -4649,20 +4668,10 @@ class MeshCoreConnector extends ChangeNotifier { _channelStore.setPublicKeyHex = selfPublicKeyHex; _unreadStore.setPublicKeyHex = selfPublicKeyHex; - // Now that we have self info, we can load all the persisted data for this node - _loadChannelOrder(); - loadContactCache(); - loadChannelSettings(); - - // Channel history is keyed by the channel's PSK (#194), and the resolver - // set above reads that PSK out of _channels. So the channel list MUST be - // loaded before the messages are: loading them concurrently leaves the - // resolver returning null, _storageKey() silently falls back to the legacy - // slot-index key, and every channel reads as empty even though its history - // is intact under the PSK key. - unawaited(loadCachedChannels().then((_) => loadAllChannelMessages())); - loadUnreadState(); - _loadDiscoveredContactCache(); + // Now that we have self info, we can load all the persisted data for this + // node. Each step is timed: this sequence has stalled the UI isolate for + // ~40s on a large store, and the timings say which step is responsible. + unawaited(_timedStartupLoad()); _awaitingSelfInfo = false; _selfInfoRetryTimer?.cancel(); @@ -4673,6 +4682,26 @@ class MeshCoreConnector extends ChangeNotifier { _maybeStartInitialChannelSync(); } + Future _timedStartupLoad() async { + Future step(String name, Future Function() body) async { + final sw = Stopwatch()..start(); + await body(); + sw.stop(); + appLogger.info( + 'startup-load $name took ${sw.elapsedMilliseconds}ms', + tag: 'Perf', + ); + } + + await step('channelOrder', () async => _loadChannelOrder()); + await step('contactCache', loadContactCache); + await step('channelSettings', () => loadChannelSettings()); + await step('cachedChannels', loadCachedChannels); + await step('channelMessages', () => loadAllChannelMessages()); + await step('unreadState', loadUnreadState); + await step('discoveredContacts', _loadDiscoveredContactCache); + } + /// Extract the additive `offband_caps` byte from a device-info reply. /// /// Offset verified against firmware MyMesh.cpp: the reply tail is three From 4ee2f507031eec0bcfc1fb03ae6aeac37c100031 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 04:04:36 -0400 Subject: [PATCH 17/60] fix(#306): stop rewriting the whole prefs file per channel on connect The connect freeze is a legacy-key migration that writes even when there is nothing to migrate. loadSmazEnabled called prefs.remove(oldKey) unconditionally whenever the scoped key was absent, which is the normal case for a channel that has never had the setting changed. On Windows shared_preferences re-serialises and rewrites the ENTIRE prefs file on any mutation, so each of those no-op removes cost a full multi-MB write. Repeated across every channel slot on every connect, that is tens of full-file rewrites back to back. Measured with the instrumentation from the previous commit, on the reporting device (6.9 MB prefs, ~42 channel slots): startup-load channelOrder 0ms startup-load contactCache 12ms startup-load channelSettings 37792ms <-- here startup-load cachedChannels 0ms startup-load channelMessages 21ms startup-load unreadState 0ms startup-load discoveredContacts 11ms No frame handler exceeded 100ms, confirming the stall was local storage work and not radio traffic. The remove now happens only when a legacy key actually exists, alongside the write that migrates it. contact_settings_store had the identical pattern per contact and is fixed the same way. This also explains the platform gap in the original report: Android's backend batches into native storage, so the same code costs almost nothing there, while every no-op remove on Windows is a full file rewrite. flutter analyze clean, dart format clean, 469 tests pass. --- lib/storage/channel_settings_store.dart | 7 ++++++- lib/storage/contact_settings_store.dart | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/storage/channel_settings_store.dart b/lib/storage/channel_settings_store.dart index e0b390f..e6af56b 100644 --- a/lib/storage/channel_settings_store.dart +++ b/lib/storage/channel_settings_store.dart @@ -25,13 +25,18 @@ class ChannelSettingsStore { bool? enabled = prefs.getBool(key); if (enabled == null) { // Attempt migration from legacy unscoped key on first load + // Only touch storage when a legacy key actually exists. This ran + // unconditionally, and on Windows shared_preferences re-serialises and + // rewrites the WHOLE prefs file on every mutation, so removing a key + // that was never there still cost a full multi-MB write. Repeated per + // channel/contact on connect, that blocked the UI isolate for ~38s. enabled = prefs.getBool(oldKey); - prefs.remove(oldKey); if (enabled != null) { appLogger.info( 'Migrating channel settings from legacy key $oldKey to scoped key $key', ); await prefs.setBool(key, enabled); + await prefs.remove(oldKey); } } return enabled ?? false; diff --git a/lib/storage/contact_settings_store.dart b/lib/storage/contact_settings_store.dart index 682f1af..109f5eb 100644 --- a/lib/storage/contact_settings_store.dart +++ b/lib/storage/contact_settings_store.dart @@ -25,13 +25,18 @@ class ContactSettingsStore { bool? enabled = prefs.getBool(key); if (enabled == null) { // Attempt migration from legacy unscoped key on first load + // Only touch storage when a legacy key actually exists. This ran + // unconditionally, and on Windows shared_preferences re-serialises and + // rewrites the WHOLE prefs file on every mutation, so removing a key + // that was never there still cost a full multi-MB write. Repeated per + // channel/contact on connect, that blocked the UI isolate for ~38s. enabled = prefs.getBool(oldKey); - prefs.remove(oldKey); if (enabled != null) { appLogger.info( 'Migrating contact settings from legacy key $oldKey to scoped key $key', ); await prefs.setBool(key, enabled); + await prefs.remove(oldKey); } } return prefs.getBool(key) ?? false; From 6ccbc71c208c8b91131d4fd00664f44e425c5770 Mon Sep 17 00:00:00 2001 From: Strycher Date: Sun, 19 Jul 2026 23:53:12 -0400 Subject: [PATCH 18/60] feat(#290): move Disconnect and Settings into the panel footer Epic C, rescoped. The original plan was to eliminate the overflow menu and move everything into the drawer. An inventory showed that premise was wrong: the ellipsis is six different menus sharing an icon, and most of what they hold is screen-level (force flood mode, path management, delete all discovered). Those cannot live in a global nav panel, which has no notion of which contact or channel is meant. Only Disconnect and Settings are app-level, which is exactly why they were duplicated across three screens. Those move; everything else stays put. - AppShell gains optional onDisconnect / onSettings, rendered as a pinned footer in the nav panel. Disconnect keeps the error colour it had as a red menu entry, since it drops the radio connection. - Channels, Contacts and Map pass both and drop those two menu entries. - Map's overflow menu is removed entirely, having nothing left. - Channels keeps its menu only for Manage Communities, which was already conditional on having joined a community. - Contacts keeps its menu for Discovered contacts. - No detail screen menu is touched. Pinned on a wide screen, Disconnect and Settings are now visible without opening anything. flutter analyze clean, dart format clean, 469 tests pass. Not yet run on hardware. --- lib/screens/channels_screen.dart | 45 ++++++------------ lib/screens/contacts_screen.dart | 32 +++---------- lib/screens/map_screen.dart | 35 ++------------ lib/widgets/app_shell.dart | 79 ++++++++++++++++++++++++++++++-- 4 files changed, 101 insertions(+), 90 deletions(-) diff --git a/lib/screens/channels_screen.dart b/lib/screens/channels_screen.dart index f7deb8b..06c514e 100644 --- a/lib/screens/channels_screen.dart +++ b/lib/screens/channels_screen.dart @@ -109,24 +109,22 @@ class _ChannelsScreenState extends State drawerContent: ChannelDrawerList( onChannelSelected: (channel) => _openChannel(context, channel), ), + onDisconnect: () => _disconnect(context), + onSettings: () => Navigator.push( + context, + MaterialPageRoute(builder: (context) => const SettingsScreen()), + ), appBar: AppBar( title: AppBarTitle(context.l10n.channels_title), centerTitle: true, bottom: const SyncProgressAppBarBottom(), actions: [ - PopupMenuButton( - itemBuilder: (context) => [ - PopupMenuItem( - child: Row( - children: [ - const Icon(Icons.logout, color: Colors.red), - const SizedBox(width: 8), - Text(context.l10n.common_disconnect), - ], - ), - onTap: () => _disconnect(context), - ), - if (_communities.isNotEmpty) + // Disconnect and Settings moved to the panel footer (#290); only + // the screen-level Communities entry remains, and it already only + // appears once a community has been joined. + if (_communities.isNotEmpty) + PopupMenuButton( + itemBuilder: (context) => [ PopupMenuItem( child: Row( children: [ @@ -137,24 +135,9 @@ class _ChannelsScreenState extends State ), onTap: () => _showManageCommunitiesDialog(context), ), - PopupMenuItem( - child: Row( - children: [ - const Icon(Icons.settings), - const SizedBox(width: 8), - Text(context.l10n.settings_title), - ], - ), - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SettingsScreen(), - ), - ), - ), - ], - icon: const Icon(Icons.more_vert), - ), + ], + icon: const Icon(Icons.more_vert), + ), ], ), body: RefreshIndicator( diff --git a/lib/screens/contacts_screen.dart b/lib/screens/contacts_screen.dart index 87fc7e4..7d2603b 100644 --- a/lib/screens/contacts_screen.dart +++ b/lib/screens/contacts_screen.dart @@ -326,6 +326,11 @@ class _ContactsScreenState extends State contactsUnreadCount: connector.getTotalContactsUnreadCount(), channelsUnreadCount: connector.getTotalChannelsUnreadCount(), drawerContent: const ContactFilterRail(), + onDisconnect: () => _disconnect(context, connector), + onSettings: () => Navigator.push( + context, + MaterialPageRoute(builder: (context) => const SettingsScreen()), + ), appBar: AppBar( title: AppBarTitle(context.l10n.contacts_title), bottom: const SyncProgressAppBarBottom(), @@ -387,18 +392,10 @@ class _ContactsScreenState extends State ], icon: const Icon(Icons.connect_without_contact), ), + // Disconnect and Settings moved to the panel footer (#290). + // Discovered contacts is screen-level and stays here. PopupMenuButton( itemBuilder: (context) => [ - PopupMenuItem( - child: Row( - children: [ - const Icon(Icons.logout, color: Colors.red), - const SizedBox(width: 8), - Text(context.l10n.common_disconnect), - ], - ), - onTap: () => _disconnect(context, connector), - ), PopupMenuItem( child: Row( children: [ @@ -414,21 +411,6 @@ class _ContactsScreenState extends State ), ), ), - PopupMenuItem( - child: Row( - children: [ - const Icon(Icons.settings), - const SizedBox(width: 8), - Text(context.l10n.settings_title), - ], - ), - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SettingsScreen(), - ), - ), - ), ], icon: const Icon(Icons.more_vert), ), diff --git a/lib/screens/map_screen.dart b/lib/screens/map_screen.dart index 1444f5a..7cb7d27 100644 --- a/lib/screens/map_screen.dart +++ b/lib/screens/map_screen.dart @@ -418,6 +418,11 @@ class _MapScreenState extends State { _handleQuickSwitch(index, context), contactsUnreadCount: connector.getTotalContactsUnreadCount(), channelsUnreadCount: connector.getTotalChannelsUnreadCount(), + onDisconnect: () => _disconnect(context, connector), + onSettings: () => Navigator.push( + context, + MaterialPageRoute(builder: (context) => const SettingsScreen()), + ), appBar: AppBar( title: AppBarTitle(context.l10n.map_title), centerTitle: true, @@ -472,36 +477,6 @@ class _MapScreenState extends State { }, tooltip: context.l10n.map_lineOfSight, ), - PopupMenuButton( - itemBuilder: (context) => [ - PopupMenuItem( - child: Row( - children: [ - const Icon(Icons.logout, color: Colors.red), - const SizedBox(width: 8), - Text(context.l10n.common_disconnect), - ], - ), - onTap: () => _disconnect(context, connector), - ), - PopupMenuItem( - child: Row( - children: [ - const Icon(Icons.settings), - const SizedBox(width: 8), - Text(context.l10n.settings_title), - ], - ), - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SettingsScreen(), - ), - ), - ), - ], - icon: const Icon(Icons.more_vert), - ), ], ), body: Stack( diff --git a/lib/widgets/app_shell.dart b/lib/widgets/app_shell.dart index cfc8ab9..329cb59 100644 --- a/lib/widgets/app_shell.dart +++ b/lib/widgets/app_shell.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../l10n/l10n.dart'; import '../services/ui_view_state_service.dart'; import 'quick_switch_bar.dart'; @@ -36,6 +37,12 @@ class AppShell extends StatelessWidget { /// List content for the active view. Null renders an empty drawer body. final Widget? drawerContent; + /// App-level actions, pinned to the bottom of the panel. These are not + /// contextual, which is why they were duplicated in three screens' overflow + /// menus before. Screen-level actions stay in their own overflow menu. + final VoidCallback? onDisconnect; + final VoidCallback? onSettings; + const AppShell({ super.key, required this.body, @@ -45,6 +52,8 @@ class AppShell extends StatelessWidget { this.appBarBuilder, this.floatingActionButton, this.drawerContent, + this.onDisconnect, + this.onSettings, this.contactsUnreadCount = 0, this.channelsUnreadCount = 0, }); @@ -95,7 +104,12 @@ class AppShell extends StatelessWidget { children: [ SizedBox( width: _drawerWidth, - child: _NavPanel(isWide: true, content: drawerContent), + child: _NavPanel( + isWide: true, + content: drawerContent, + onDisconnect: onDisconnect, + onSettings: onSettings, + ), ), const VerticalDivider(width: 1), Expanded(child: body), @@ -114,7 +128,12 @@ class AppShell extends StatelessWidget { appBar: _appBar(context, false), drawer: Drawer( width: _drawerWidth, - child: _NavPanel(isWide: isWide, content: drawerContent), + child: _NavPanel( + isWide: isWide, + content: drawerContent, + onDisconnect: onDisconnect, + onSettings: onSettings, + ), ), body: body, floatingActionButton: floatingActionButton, @@ -128,8 +147,15 @@ class AppShell extends StatelessWidget { class _NavPanel extends StatelessWidget { final bool isWide; final Widget? content; - - const _NavPanel({required this.isWide, this.content}); + final VoidCallback? onDisconnect; + final VoidCallback? onSettings; + + const _NavPanel({ + required this.isWide, + this.content, + this.onDisconnect, + this.onSettings, + }); @override Widget build(BuildContext context) { @@ -164,6 +190,51 @@ class _NavPanel extends StatelessWidget { ), if (isWide) const Divider(height: 1), Expanded(child: content ?? const SizedBox.shrink()), + if (onDisconnect != null || onSettings != null) ...[ + const Divider(height: 1), + _Footer(onDisconnect: onDisconnect, onSettings: onSettings), + ], + ], + ), + ); + } +} + +/// App-level actions pinned to the bottom of the panel. +class _Footer extends StatelessWidget { + final VoidCallback? onDisconnect; + final VoidCallback? onSettings; + + const _Footer({this.onDisconnect, this.onSettings}); + + @override + Widget build(BuildContext context) { + final l10n = context.l10n; + final colors = Theme.of(context).colorScheme; + + return Padding( + padding: const EdgeInsets.fromLTRB(8, 6, 8, 8), + child: Row( + children: [ + if (onDisconnect != null) + Expanded( + child: TextButton.icon( + // Kept visually distinct: this drops the radio connection, + // and it was a red menu entry before the move. + style: TextButton.styleFrom(foregroundColor: colors.error), + icon: const Icon(Icons.logout, size: 18), + label: Text(l10n.common_disconnect), + onPressed: onDisconnect, + ), + ), + if (onSettings != null) + Expanded( + child: TextButton.icon( + icon: const Icon(Icons.settings, size: 18), + label: Text(l10n.settings_title), + onPressed: onSettings, + ), + ), ], ), ); From 4ece02ceff3b83e2f4cae5f286dd0b8a8ac9ca0a Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 00:13:27 -0400 Subject: [PATCH 19/60] feat(#291): map layer toggles in the nav panel Epic D. The Map view has no list, so its nav panel was empty. It now carries the map's own layer controls. Those six toggles (chat nodes, repeaters, other nodes, discovery contacts, guessed locations, overlaps) previously existed only in a modal behind the floating button, so filtering the map meant covering the map with a dialog to do it. In the panel, and especially pinned on a wide screen, they stay visible and the map updates underneath as they are flipped. Both surfaces read and write the same AppSettings, so a change in one is reflected in the other. The floating-button dialog is deliberately left in place: on a phone it is fewer taps than opening the drawer, and the panel is the pinned-desktop path. If that duplication is unwanted, removing the dialog is a one-line follow-up. No new settings or storage; the existing setters were reused unchanged. flutter analyze clean, dart format clean, 469 tests pass. Not yet run on hardware. --- lib/screens/map_screen.dart | 2 + lib/widgets/map_layer_panel.dart | 99 ++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 lib/widgets/map_layer_panel.dart diff --git a/lib/screens/map_screen.dart b/lib/screens/map_screen.dart index 7cb7d27..c902cb6 100644 --- a/lib/screens/map_screen.dart +++ b/lib/screens/map_screen.dart @@ -24,6 +24,7 @@ import '../services/map_tile_cache_service.dart'; import '../utils/contact_search.dart'; import '../utils/route_transitions.dart'; import '../widgets/app_shell.dart'; +import '../widgets/map_layer_panel.dart'; import '../widgets/sync_progress_overlay.dart'; import '../icons/los_icon.dart'; import 'channels_screen.dart'; @@ -418,6 +419,7 @@ class _MapScreenState extends State { _handleQuickSwitch(index, context), contactsUnreadCount: connector.getTotalContactsUnreadCount(), channelsUnreadCount: connector.getTotalChannelsUnreadCount(), + drawerContent: const MapLayerPanel(), onDisconnect: () => _disconnect(context, connector), onSettings: () => Navigator.push( context, diff --git a/lib/widgets/map_layer_panel.dart b/lib/widgets/map_layer_panel.dart new file mode 100644 index 0000000..13df116 --- /dev/null +++ b/lib/widgets/map_layer_panel.dart @@ -0,0 +1,99 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../l10n/l10n.dart'; +import '../services/app_settings_service.dart'; + +/// Map layer toggles for the nav panel. +/// +/// The Map view has no list to show, so its panel carries the layer controls +/// that otherwise live in a modal behind the floating button. Pinned on a wide +/// screen these stay visible and the map updates underneath as they are +/// flipped, instead of a dialog covering the thing being filtered. +/// +/// The same settings back both surfaces, so a change here shows in the dialog +/// and vice versa. +class MapLayerPanel extends StatelessWidget { + const MapLayerPanel({super.key}); + + @override + Widget build(BuildContext context) { + final service = context.watch(); + final settings = service.settings; + final l10n = context.l10n; + final theme = Theme.of(context); + + return ListView( + padding: EdgeInsets.zero, + children: [ + _sectionHeader(theme, l10n.map_nodeTypes), + _toggle( + label: l10n.map_chatNodes, + icon: Icons.person_outline, + value: settings.mapShowChatNodes, + onChanged: service.setMapShowChatNodes, + ), + _toggle( + label: l10n.map_repeaters, + icon: Icons.cell_tower, + value: settings.mapShowRepeaters, + onChanged: service.setMapShowRepeaters, + ), + _toggle( + label: l10n.map_otherNodes, + icon: Icons.help_outline, + value: settings.mapShowOtherNodes, + onChanged: service.setMapShowOtherNodes, + ), + const Divider(height: 1), + _sectionHeader(theme, l10n.map_filterNodes), + _toggle( + label: l10n.map_showDiscoveryContacts, + icon: Icons.person_search, + value: settings.mapShowDiscoveryContacts, + onChanged: service.setMapShowDiscoveryContacts, + ), + _toggle( + label: l10n.map_showGuessedLocations, + icon: Icons.location_searching, + value: settings.mapShowGuessedLocations, + onChanged: service.setMapShowGuessedLocations, + ), + _toggle( + label: l10n.map_showOverlaps, + icon: Icons.layers, + value: settings.mapShowOverlaps, + onChanged: service.setMapShowOverlaps, + ), + ], + ); + } + + Widget _sectionHeader(ThemeData theme, String label) { + return Padding( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 4), + child: Text( + label, + style: theme.textTheme.labelMedium?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + fontWeight: FontWeight.w700, + ), + ), + ); + } + + Widget _toggle({ + required String label, + required IconData icon, + required bool value, + required ValueChanged onChanged, + }) { + return SwitchListTile( + dense: true, + secondary: Icon(icon, size: 20), + title: Text(label, maxLines: 2, overflow: TextOverflow.ellipsis), + value: value, + onChanged: onChanged, + ); + } +} From 9aaebce77c868df48efdcd7d8c9bc9e91b4c65da Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 00:37:51 -0400 Subject: [PATCH 20/60] fix(#291): map name toggle, panel footer everywhere, and back-button behavior Owner hardware test on an S25 FE and Windows turned up five issues. Three of them shared one root cause. **Map: always show names.** New mapAlwaysShowNames setting, toggled from the map panel. Names were shown only past zoom 14; the override wins at any zoom. Evaluated at build rather than only on camera movement, so flipping the switch repaints immediately instead of waiting for the next pan. **Panel footer on every screen.** Disconnect and Settings now also appear inside a channel. The footer is app-level, so it belongs anywhere the panel is, not just the primary views. **Back button (three reported symptoms, one cause).** Channels, Contacts and Map each wrapped themselves in `PopScope(canPop: !isConnected)`, so while connected the system back press was swallowed whole. That explains all three: an open drawer would not close, Android would not background the app, and on Windows the pinned layout has no Scaffold drawer, so the app bar auto-implied a back arrow whose press PopScope then discarded - a button that visibly did nothing. AppShell now owns back handling for every screen that uses it, in order: close an open drawer, else pop the route (a pushed chat returns to its list), else hand back to the OS via SystemNavigator.pop() so Android returns to the home screen or the previous app. The three per-screen PopScope wrappers are removed. AppShell becomes stateful to hold the scaffold key this needs. flutter analyze clean, dart format clean, 469 tests pass. Not yet re-tested on hardware. --- lib/l10n/app_en.arb | 1 + lib/l10n/app_localizations.dart | 6 + lib/l10n/app_localizations_bg.dart | 3 + lib/l10n/app_localizations_de.dart | 3 + lib/l10n/app_localizations_en.dart | 3 + lib/l10n/app_localizations_es.dart | 3 + lib/l10n/app_localizations_fr.dart | 3 + lib/l10n/app_localizations_hu.dart | 3 + lib/l10n/app_localizations_it.dart | 3 + lib/l10n/app_localizations_ja.dart | 3 + lib/l10n/app_localizations_ko.dart | 3 + lib/l10n/app_localizations_nl.dart | 3 + lib/l10n/app_localizations_pl.dart | 3 + lib/l10n/app_localizations_pt.dart | 3 + lib/l10n/app_localizations_ru.dart | 3 + lib/l10n/app_localizations_sk.dart | 3 + lib/l10n/app_localizations_sl.dart | 3 + lib/l10n/app_localizations_sv.dart | 3 + lib/l10n/app_localizations_uk.dart | 3 + lib/l10n/app_localizations_zh.dart | 3 + lib/models/app_settings.dart | 8 + lib/screens/channel_chat_screen.dart | 14 + lib/screens/channels_screen.dart | 423 +++++++++++------------ lib/screens/contacts_screen.dart | 180 +++++----- lib/screens/map_screen.dart | 461 ++++++++++++------------- lib/services/app_settings_service.dart | 4 + lib/widgets/app_shell.dart | 94 +++-- lib/widgets/map_layer_panel.dart | 7 + 28 files changed, 687 insertions(+), 565 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 48c6f31..a7b3413 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -976,6 +976,7 @@ "map_repeaters": "Repeaters", "map_otherNodes": "Other Nodes", "map_showOverlaps": "Repeater Key Overlaps", + "map_alwaysShowNames": "Always show names", "map_keyPrefix": "Key Prefix", "map_filterByKeyPrefix": "Filter by key prefix", "map_publicKeyPrefix": "Public key prefix", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 2c425e2..0ad7394 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -3412,6 +3412,12 @@ abstract class AppLocalizations { /// **'Repeater Key Overlaps'** String get map_showOverlaps; + /// No description provided for @map_alwaysShowNames. + /// + /// In en, this message translates to: + /// **'Always show names'** + String get map_alwaysShowNames; + /// No description provided for @map_keyPrefix. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_bg.dart b/lib/l10n/app_localizations_bg.dart index ee62916..a379cc6 100644 --- a/lib/l10n/app_localizations_bg.dart +++ b/lib/l10n/app_localizations_bg.dart @@ -1890,6 +1890,9 @@ class AppLocalizationsBg extends AppLocalizations { @override String get map_showOverlaps => 'Покриване на ключа на повтаряча'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => 'Префикс на ключа'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 92c1102..b60bea3 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -1887,6 +1887,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get map_showOverlaps => 'Überlappungen der Repeater-Taste'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => 'Schlüsselpräfix'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 7989c98..db7573f 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -1854,6 +1854,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get map_showOverlaps => 'Repeater Key Overlaps'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => 'Key Prefix'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index df55c77..9253a00 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -1885,6 +1885,9 @@ class AppLocalizationsEs extends AppLocalizations { @override String get map_showOverlaps => 'Superposiciones de tecla repetidora'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => 'Prefijo de clave'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index e02d168..19bd894 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -1895,6 +1895,9 @@ class AppLocalizationsFr extends AppLocalizations { @override String get map_showOverlaps => 'Chevauchement de la touche répétitive'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => 'Préfixe clé'; diff --git a/lib/l10n/app_localizations_hu.dart b/lib/l10n/app_localizations_hu.dart index 3dabd04..98933ef 100644 --- a/lib/l10n/app_localizations_hu.dart +++ b/lib/l10n/app_localizations_hu.dart @@ -1896,6 +1896,9 @@ class AppLocalizationsHu extends AppLocalizations { @override String get map_showOverlaps => 'Az ismétlő kulcsok ütköznek'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => 'Kulcsfontosságú előtag'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index c4ad345..b0197a8 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -1888,6 +1888,9 @@ class AppLocalizationsIt extends AppLocalizations { @override String get map_showOverlaps => 'Sovrapposizioni della chiave ripetitore'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => 'Prefisso Chiave'; diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index 1c2edbf..b244d39 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -1811,6 +1811,9 @@ class AppLocalizationsJa extends AppLocalizations { @override String get map_showOverlaps => 'リピーターキーの重複'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => '主要なプレフィックス'; diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index 6403027..90228a2 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -1807,6 +1807,9 @@ class AppLocalizationsKo extends AppLocalizations { @override String get map_showOverlaps => '반복 키 중복'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => '핵심 접두사'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 8e34076..440530b 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -1874,6 +1874,9 @@ class AppLocalizationsNl extends AppLocalizations { @override String get map_showOverlaps => 'Herhalingssleutel overlapt'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => 'Prefix sleutel'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 2a034a7..be65e06 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -1900,6 +1900,9 @@ class AppLocalizationsPl extends AppLocalizations { @override String get map_showOverlaps => 'Nakładające się klucze przekaźników'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => 'Prefiks klucza'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index b0851fe..efcfad0 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -1885,6 +1885,9 @@ class AppLocalizationsPt extends AppLocalizations { @override String get map_showOverlaps => 'Sobreposições da Chave Repeater'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => 'Prefixo Chave'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index 5ae0d11..63b5679 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -1889,6 +1889,9 @@ class AppLocalizationsRu extends AppLocalizations { @override String get map_showOverlaps => 'Перекрытия ключа повтора'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => 'Префикс ключа'; diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index 0b885ac..be75989 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -1876,6 +1876,9 @@ class AppLocalizationsSk extends AppLocalizations { @override String get map_showOverlaps => 'Prekrývanie opakovača kľúča'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => 'Päťciferné predpona'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index fa6c120..4b9518c 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -1871,6 +1871,9 @@ class AppLocalizationsSl extends AppLocalizations { @override String get map_showOverlaps => 'Prekrivanje ključa ponovnega predvajanja'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => 'Predpona ključa'; diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index a4e67f6..ab1047d 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -1864,6 +1864,9 @@ class AppLocalizationsSv extends AppLocalizations { @override String get map_showOverlaps => 'Repeater-nyckelöverlappningar'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => 'Nyckelprefix'; diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index 00aeeb1..ee32259 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -1884,6 +1884,9 @@ class AppLocalizationsUk extends AppLocalizations { @override String get map_showOverlaps => 'Перекриття ключів ретрансляторів'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => 'Префікс ключа'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index 051db0e..7ae7266 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -1778,6 +1778,9 @@ class AppLocalizationsZh extends AppLocalizations { @override String get map_showOverlaps => '重复键重叠'; + @override + String get map_alwaysShowNames => 'Always show names'; + @override String get map_keyPrefix => '关键字前缀'; diff --git a/lib/models/app_settings.dart b/lib/models/app_settings.dart index 005197f..782e78a 100644 --- a/lib/models/app_settings.dart +++ b/lib/models/app_settings.dart @@ -112,6 +112,9 @@ class AppSettings { final bool mapShowChatNodes; final bool mapShowOtherNodes; final bool mapShowOverlaps; + + /// Show node names at any zoom, instead of only past the zoom threshold. + final bool mapAlwaysShowNames; final double mapTimeFilterHours; // 0 = all time final bool mapKeyPrefixEnabled; final String mapKeyPrefix; @@ -188,6 +191,7 @@ class AppSettings { this.mapShowChatNodes = true, this.mapShowOtherNodes = true, this.mapShowOverlaps = false, + this.mapAlwaysShowNames = false, this.mapTimeFilterHours = 0, // Default to all time this.mapKeyPrefixEnabled = false, this.mapKeyPrefix = '', @@ -252,6 +256,7 @@ class AppSettings { 'map_show_chat_nodes': mapShowChatNodes, 'map_show_other_nodes': mapShowOtherNodes, 'map_show_overlaps': mapShowOverlaps, + 'map_always_show_names': mapAlwaysShowNames, 'map_time_filter_hours': mapTimeFilterHours, 'map_key_prefix_enabled': mapKeyPrefixEnabled, 'map_key_prefix': mapKeyPrefix, @@ -338,6 +343,7 @@ class AppSettings { mapShowChatNodes: json['map_show_chat_nodes'] as bool? ?? true, mapShowOtherNodes: json['map_show_other_nodes'] as bool? ?? true, mapShowOverlaps: json['map_show_overlaps'] as bool? ?? false, + mapAlwaysShowNames: json['map_always_show_names'] as bool? ?? false, mapTimeFilterHours: (json['map_time_filter_hours'] as num?)?.toDouble() ?? 0, mapKeyPrefixEnabled: json['map_key_prefix_enabled'] as bool? ?? false, @@ -462,6 +468,7 @@ class AppSettings { bool? mapShowChatNodes, bool? mapShowOtherNodes, bool? mapShowOverlaps, + bool? mapAlwaysShowNames, double? mapTimeFilterHours, bool? mapKeyPrefixEnabled, String? mapKeyPrefix, @@ -510,6 +517,7 @@ class AppSettings { mapShowChatNodes: mapShowChatNodes ?? this.mapShowChatNodes, mapShowOtherNodes: mapShowOtherNodes ?? this.mapShowOtherNodes, mapShowOverlaps: mapShowOverlaps ?? this.mapShowOverlaps, + mapAlwaysShowNames: mapAlwaysShowNames ?? this.mapAlwaysShowNames, mapTimeFilterHours: mapTimeFilterHours ?? this.mapTimeFilterHours, mapKeyPrefixEnabled: mapKeyPrefixEnabled ?? this.mapKeyPrefixEnabled, mapKeyPrefix: mapKeyPrefix ?? this.mapKeyPrefix, diff --git a/lib/screens/channel_chat_screen.dart b/lib/screens/channel_chat_screen.dart index 68cc7dd..6917817 100644 --- a/lib/screens/channel_chat_screen.dart +++ b/lib/screens/channel_chat_screen.dart @@ -29,6 +29,8 @@ import '../services/chat_text_scale_service.dart'; import '../services/translation_service.dart'; import '../utils/emoji_utils.dart'; import '../utils/route_transitions.dart'; +import 'settings_screen.dart'; +import '../utils/dialog_utils.dart'; import '../widgets/app_shell.dart'; import '../widgets/channel_drawer_list.dart'; import '../widgets/mention_autocomplete.dart'; @@ -287,6 +289,11 @@ class _ChannelChatScreenState extends State { ); } + Future _disconnect(BuildContext context) async { + final connector = context.read(); + await showDisconnectDialog(context, connector); + } + /// Bottom-bar navigation out of an open channel. /// /// Tapping Channels returns to the channel list. Tapping Contacts or Map @@ -351,6 +358,13 @@ class _ChannelChatScreenState extends State { currentChannelIndex: _currentChannel.index, onChannelSelected: _switchChannel, ), + // Present here too: the footer is app-level, so it belongs on every + // screen carrying the panel, not just the primary views. + onDisconnect: () => _disconnect(context), + onSettings: () => Navigator.push( + context, + MaterialPageRoute(builder: (context) => const SettingsScreen()), + ), appBarBuilder: (context, pinned) => AppBar( // Pinned: the panel is already docked, so no hamburger is needed. // Unpinned: this is a pushed route, so Scaffold would put a back arrow diff --git a/lib/screens/channels_screen.dart b/lib/screens/channels_screen.dart index 06c514e..eedfd71 100644 --- a/lib/screens/channels_screen.dart +++ b/lib/screens/channels_screen.dart @@ -97,234 +97,229 @@ class _ChannelsScreenState extends State return const SizedBox.shrink(); } - final allowBack = !connector.isConnected; - - return PopScope( - canPop: allowBack, - child: AppShell( - selectedIndex: 1, - onDestinationSelected: (index) => _handleQuickSwitch(index, context), - contactsUnreadCount: connector.getTotalContactsUnreadCount(), - channelsUnreadCount: connector.getTotalChannelsUnreadCount(), - drawerContent: ChannelDrawerList( - onChannelSelected: (channel) => _openChannel(context, channel), - ), - onDisconnect: () => _disconnect(context), - onSettings: () => Navigator.push( - context, - MaterialPageRoute(builder: (context) => const SettingsScreen()), - ), - appBar: AppBar( - title: AppBarTitle(context.l10n.channels_title), - centerTitle: true, - bottom: const SyncProgressAppBarBottom(), - actions: [ - // Disconnect and Settings moved to the panel footer (#290); only - // the screen-level Communities entry remains, and it already only - // appears once a community has been joined. - if (_communities.isNotEmpty) - PopupMenuButton( - itemBuilder: (context) => [ - PopupMenuItem( - child: Row( - children: [ - const Icon(Icons.groups), - const SizedBox(width: 8), - Text(context.l10n.community_manageCommunities), - ], - ), - onTap: () => _showManageCommunitiesDialog(context), + return AppShell( + selectedIndex: 1, + onDestinationSelected: (index) => _handleQuickSwitch(index, context), + contactsUnreadCount: connector.getTotalContactsUnreadCount(), + channelsUnreadCount: connector.getTotalChannelsUnreadCount(), + drawerContent: ChannelDrawerList( + onChannelSelected: (channel) => _openChannel(context, channel), + ), + onDisconnect: () => _disconnect(context), + onSettings: () => Navigator.push( + context, + MaterialPageRoute(builder: (context) => const SettingsScreen()), + ), + appBar: AppBar( + title: AppBarTitle(context.l10n.channels_title), + centerTitle: true, + bottom: const SyncProgressAppBarBottom(), + actions: [ + // Disconnect and Settings moved to the panel footer (#290); only + // the screen-level Communities entry remains, and it already only + // appears once a community has been joined. + if (_communities.isNotEmpty) + PopupMenuButton( + itemBuilder: (context) => [ + PopupMenuItem( + child: Row( + children: [ + const Icon(Icons.groups), + const SizedBox(width: 8), + Text(context.l10n.community_manageCommunities), + ], ), - ], - icon: const Icon(Icons.more_vert), - ), - ], - ), - body: RefreshIndicator( - onRefresh: () async { - await context.read().getChannels(force: true); - }, - child: () { - final channels = connector.channels; - final waitingForFirstChannel = - connector.isLoadingChannels && channels.isEmpty; - - // Only block the list while the first channel is actively loading. - // If the initial sync aborts, show cached/partial channels instead - // of trapping the user behind an idle spinner. - if (waitingForFirstChannel) { - return const Center(child: CircularProgressIndicator()); - } + onTap: () => _showManageCommunitiesDialog(context), + ), + ], + icon: const Icon(Icons.more_vert), + ), + ], + ), + body: RefreshIndicator( + onRefresh: () async { + await context.read().getChannels(force: true); + }, + child: () { + final channels = connector.channels; + final waitingForFirstChannel = + connector.isLoadingChannels && channels.isEmpty; + + // Only block the list while the first channel is actively loading. + // If the initial sync aborts, show cached/partial channels instead + // of trapping the user behind an idle spinner. + if (waitingForFirstChannel) { + return const Center(child: CircularProgressIndicator()); + } - if (channels.isEmpty) { - return ListView( - children: [ - SizedBox( - height: MediaQuery.of(context).size.height - 200, - child: EmptyState( - icon: Icons.tag, - title: context.l10n.channels_noChannelsConfigured, - action: FilledButton.icon( - onPressed: () => _addPublicChannel(context, connector), - icon: const Icon(Icons.public), - label: Text(context.l10n.channels_addPublicChannel), - ), + if (channels.isEmpty) { + return ListView( + children: [ + SizedBox( + height: MediaQuery.of(context).size.height - 200, + child: EmptyState( + icon: Icons.tag, + title: context.l10n.channels_noChannelsConfigured, + action: FilledButton.icon( + onPressed: () => _addPublicChannel(context, connector), + icon: const Icon(Icons.public), + label: Text(context.l10n.channels_addPublicChannel), ), ), - ], - ); - } - - final filteredChannels = _filterAndSortChannels( - channels, - connector, - viewState, + ), + ], ); + } - return Column( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: TextField( - controller: _searchController, - decoration: InputDecoration( - hintText: context.l10n.channels_searchChannels, - prefixIcon: const Icon(Icons.search), - suffixIcon: Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (viewState.channelsSearchText.isNotEmpty) - IconButton( - icon: const Icon(Icons.clear), - onPressed: () { - _searchDebounce?.cancel(); - _searchDebounce = null; - _searchController.clear(); - context - .read() - .setChannelsSearchText(''); - }, - ), - _buildFilterButton(viewState), - ], - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 12, - ), + final filteredChannels = _filterAndSortChannels( + channels, + connector, + viewState, + ); + + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: _searchController, + decoration: InputDecoration( + hintText: context.l10n.channels_searchChannels, + prefixIcon: const Icon(Icons.search), + suffixIcon: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (viewState.channelsSearchText.isNotEmpty) + IconButton( + icon: const Icon(Icons.clear), + onPressed: () { + _searchDebounce?.cancel(); + _searchDebounce = null; + _searchController.clear(); + context + .read() + .setChannelsSearchText(''); + }, + ), + _buildFilterButton(viewState), + ], + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 12, ), - onChanged: (value) { - _searchDebounce?.cancel(); - _searchDebounce = Timer( - const Duration(milliseconds: 300), - () { - if (!mounted) return; - context - .read() - .setChannelsSearchText(value); - }, - ); - }, ), + onChanged: (value) { + _searchDebounce?.cancel(); + _searchDebounce = Timer( + const Duration(milliseconds: 300), + () { + if (!mounted) return; + context + .read() + .setChannelsSearchText(value); + }, + ); + }, ), - Expanded( - child: filteredChannels.isEmpty - ? ListView( - children: [ - SizedBox( - height: MediaQuery.of(context).size.height - 300, - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.search_off, - size: 64, - color: Colors.grey[400], - ), - const SizedBox(height: 16), - Text( - context.l10n.channels_noChannelsFound, - style: TextStyle( - fontSize: 16, - color: Colors.grey[600], - ), + ), + Expanded( + child: filteredChannels.isEmpty + ? ListView( + children: [ + SizedBox( + height: MediaQuery.of(context).size.height - 300, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.search_off, + size: 64, + color: Colors.grey[400], + ), + const SizedBox(height: 16), + Text( + context.l10n.channels_noChannelsFound, + style: TextStyle( + fontSize: 16, + color: Colors.grey[600], ), - ], - ), + ), + ], ), ), - ], - ) - : (viewState.channelsSortOption == - ChannelSortOption.manual && - viewState.channelsSearchText.isEmpty) - ? ReorderableListView.builder( - padding: const EdgeInsets.only( - left: 16, - right: 16, - top: 8, - bottom: 88, - ), - buildDefaultDragHandles: false, - itemCount: filteredChannels.length, - onReorderItem: (oldIndex, newIndex) { - // onReorderItem already adjusts newIndex after the - // removed item, unlike the deprecated onReorder. - final reordered = List.from( - filteredChannels, - ); - final item = reordered.removeAt(oldIndex); - reordered.insert(newIndex, item); - unawaited( - connector.setChannelOrder( - reordered.map((c) => c.index).toList(), - ), - ); - }, - itemBuilder: (context, index) { - final channel = filteredChannels[index]; - return _buildChannelTile( - context, - connector, - channelMessageStore, - channel, - showDragHandle: true, - dragIndex: index, - ); - }, - ) - : ListView.builder( - padding: const EdgeInsets.only( - left: 16, - right: 16, - top: 8, - bottom: 88, ), - itemCount: filteredChannels.length, - itemBuilder: (context, index) { - final channel = filteredChannels[index]; - return _buildChannelTile( - context, - connector, - channelMessageStore, - channel, - ); - }, + ], + ) + : (viewState.channelsSortOption == + ChannelSortOption.manual && + viewState.channelsSearchText.isEmpty) + ? ReorderableListView.builder( + padding: const EdgeInsets.only( + left: 16, + right: 16, + top: 8, + bottom: 88, ), - ), - ], - ); - }(), - ), - floatingActionButton: FloatingActionButton( - onPressed: () => _showAddChannelDialog(context), - tooltip: context.l10n.channels_addChannel, - child: const Icon(Icons.add), - ), + buildDefaultDragHandles: false, + itemCount: filteredChannels.length, + onReorderItem: (oldIndex, newIndex) { + // onReorderItem already adjusts newIndex after the + // removed item, unlike the deprecated onReorder. + final reordered = List.from( + filteredChannels, + ); + final item = reordered.removeAt(oldIndex); + reordered.insert(newIndex, item); + unawaited( + connector.setChannelOrder( + reordered.map((c) => c.index).toList(), + ), + ); + }, + itemBuilder: (context, index) { + final channel = filteredChannels[index]; + return _buildChannelTile( + context, + connector, + channelMessageStore, + channel, + showDragHandle: true, + dragIndex: index, + ); + }, + ) + : ListView.builder( + padding: const EdgeInsets.only( + left: 16, + right: 16, + top: 8, + bottom: 88, + ), + itemCount: filteredChannels.length, + itemBuilder: (context, index) { + final channel = filteredChannels[index]; + return _buildChannelTile( + context, + connector, + channelMessageStore, + channel, + ); + }, + ), + ), + ], + ); + }(), + ), + floatingActionButton: FloatingActionButton( + onPressed: () => _showAddChannelDialog(context), + tooltip: context.l10n.channels_addChannel, + child: const Icon(Icons.add), ), ); } diff --git a/lib/screens/contacts_screen.dart b/lib/screens/contacts_screen.dart index 7d2603b..ce32f4c 100644 --- a/lib/screens/contacts_screen.dart +++ b/lib/screens/contacts_screen.dart @@ -317,107 +317,103 @@ class _ContactsScreenState extends State return const SizedBox.shrink(); } - final allowBack = !connector.isConnected; - return PopScope( - canPop: allowBack, - child: AppShell( - selectedIndex: 0, - onDestinationSelected: (index) => _handleQuickSwitch(index, context), - contactsUnreadCount: connector.getTotalContactsUnreadCount(), - channelsUnreadCount: connector.getTotalChannelsUnreadCount(), - drawerContent: const ContactFilterRail(), - onDisconnect: () => _disconnect(context, connector), - onSettings: () => Navigator.push( - context, - MaterialPageRoute(builder: (context) => const SettingsScreen()), - ), - appBar: AppBar( - title: AppBarTitle(context.l10n.contacts_title), - bottom: const SyncProgressAppBarBottom(), - actions: [ - PopupMenuButton( - itemBuilder: (context) => [ - PopupMenuItem( - child: Row( - children: [ - const Icon(Icons.connect_without_contact), - const SizedBox(width: 8), - Text(context.l10n.contacts_zeroHopAdvert), - ], - ), - onTap: () => { - connector.sendSelfAdvert(flood: false), - showDismissibleSnackBar( - context, - content: Text(context.l10n.settings_advertisementSent), - ), - }, + return AppShell( + selectedIndex: 0, + onDestinationSelected: (index) => _handleQuickSwitch(index, context), + contactsUnreadCount: connector.getTotalContactsUnreadCount(), + channelsUnreadCount: connector.getTotalChannelsUnreadCount(), + drawerContent: const ContactFilterRail(), + onDisconnect: () => _disconnect(context, connector), + onSettings: () => Navigator.push( + context, + MaterialPageRoute(builder: (context) => const SettingsScreen()), + ), + appBar: AppBar( + title: AppBarTitle(context.l10n.contacts_title), + bottom: const SyncProgressAppBarBottom(), + actions: [ + PopupMenuButton( + itemBuilder: (context) => [ + PopupMenuItem( + child: Row( + children: [ + const Icon(Icons.connect_without_contact), + const SizedBox(width: 8), + Text(context.l10n.contacts_zeroHopAdvert), + ], ), - PopupMenuItem( - child: Row( - children: [ - const Icon(Icons.cell_tower), - const SizedBox(width: 8), - Text(context.l10n.contacts_floodAdvert), - ], + onTap: () => { + connector.sendSelfAdvert(flood: false), + showDismissibleSnackBar( + context, + content: Text(context.l10n.settings_advertisementSent), ), - onTap: () => { - connector.sendSelfAdvert(flood: true), - showDismissibleSnackBar( - context, - content: Text(context.l10n.settings_advertisementSent), - ), - }, + }, + ), + PopupMenuItem( + child: Row( + children: [ + const Icon(Icons.cell_tower), + const SizedBox(width: 8), + Text(context.l10n.contacts_floodAdvert), + ], ), - PopupMenuItem( - child: Row( - children: [ - const Icon(Icons.copy), - const SizedBox(width: 8), - Text(context.l10n.contacts_copyAdvertToClipboard), - ], + onTap: () => { + connector.sendSelfAdvert(flood: true), + showDismissibleSnackBar( + context, + content: Text(context.l10n.settings_advertisementSent), ), - onTap: () => _contactExport(Uint8List.fromList([])), + }, + ), + PopupMenuItem( + child: Row( + children: [ + const Icon(Icons.copy), + const SizedBox(width: 8), + Text(context.l10n.contacts_copyAdvertToClipboard), + ], ), - PopupMenuItem( - child: Row( - children: [ - const Icon(Icons.paste), - const SizedBox(width: 8), - Text(context.l10n.contacts_addContactFromClipboard), - ], - ), - onTap: () => _contactImport(), + onTap: () => _contactExport(Uint8List.fromList([])), + ), + PopupMenuItem( + child: Row( + children: [ + const Icon(Icons.paste), + const SizedBox(width: 8), + Text(context.l10n.contacts_addContactFromClipboard), + ], ), - ], - icon: const Icon(Icons.connect_without_contact), - ), - // Disconnect and Settings moved to the panel footer (#290). - // Discovered contacts is screen-level and stays here. - PopupMenuButton( - itemBuilder: (context) => [ - PopupMenuItem( - child: Row( - children: [ - const Icon(Icons.person_add_rounded), - const SizedBox(width: 8), - Text(context.l10n.discoveredContacts_Title), - ], - ), - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const DiscoveryScreen(), - ), + onTap: () => _contactImport(), + ), + ], + icon: const Icon(Icons.connect_without_contact), + ), + // Disconnect and Settings moved to the panel footer (#290). + // Discovered contacts is screen-level and stays here. + PopupMenuButton( + itemBuilder: (context) => [ + PopupMenuItem( + child: Row( + children: [ + const Icon(Icons.person_add_rounded), + const SizedBox(width: 8), + Text(context.l10n.discoveredContacts_Title), + ], + ), + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const DiscoveryScreen(), ), ), - ], - icon: const Icon(Icons.more_vert), - ), - ], - ), - body: _buildContactsBody(context, connector), + ), + ], + icon: const Icon(Icons.more_vert), + ), + ], ), + body: _buildContactsBody(context, connector), ); } diff --git a/lib/screens/map_screen.dart b/lib/screens/map_screen.dart index c902cb6..2b9ade1 100644 --- a/lib/screens/map_screen.dart +++ b/lib/screens/map_screen.dart @@ -399,7 +399,8 @@ class _MapScreenState extends State { // Re center map after removed markers have loaded if (!_hasInitializedMap && _removedMarkersLoaded) { _hasInitializedMap = true; - _showNodeLabels = initialZoom >= _labelZoomThreshold; + _showNodeLabels = + settings.mapAlwaysShowNames || initialZoom >= _labelZoomThreshold; if (hasMapContent) { WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { @@ -409,257 +410,253 @@ class _MapScreenState extends State { } } - final allowBack = !connector.isConnected; - - return PopScope( - canPop: allowBack, - child: AppShell( - selectedIndex: 2, - onDestinationSelected: (index) => - _handleQuickSwitch(index, context), - contactsUnreadCount: connector.getTotalContactsUnreadCount(), - channelsUnreadCount: connector.getTotalChannelsUnreadCount(), - drawerContent: const MapLayerPanel(), - onDisconnect: () => _disconnect(context, connector), - onSettings: () => Navigator.push( - context, - MaterialPageRoute(builder: (context) => const SettingsScreen()), - ), - appBar: AppBar( - title: AppBarTitle(context.l10n.map_title), - centerTitle: true, - bottom: const SyncProgressAppBarBottom(), - actions: [ - if (!_isBuildingPathTrace) - IconButton( - icon: const Icon(Icons.radar), - onPressed: () => _startPath( - LatLng(connector.selfLatitude!, connector.selfLongitude!), - ), - tooltip: context.l10n.contacts_pathTrace, + return AppShell( + selectedIndex: 2, + onDestinationSelected: (index) => _handleQuickSwitch(index, context), + contactsUnreadCount: connector.getTotalContactsUnreadCount(), + channelsUnreadCount: connector.getTotalChannelsUnreadCount(), + drawerContent: const MapLayerPanel(), + onDisconnect: () => _disconnect(context, connector), + onSettings: () => Navigator.push( + context, + MaterialPageRoute(builder: (context) => const SettingsScreen()), + ), + appBar: AppBar( + title: AppBarTitle(context.l10n.map_title), + centerTitle: true, + bottom: const SyncProgressAppBarBottom(), + actions: [ + if (!_isBuildingPathTrace) + IconButton( + icon: const Icon(Icons.radar), + onPressed: () => _startPath( + LatLng(connector.selfLatitude!, connector.selfLongitude!), ), - if (!_isBuildingPathTrace) - IconButton( - icon: const LosIcon(), - onPressed: () { - final candidates = []; - if (connector.selfLatitude != null && - connector.selfLongitude != null) { - candidates.add( - LineOfSightEndpoint( - label: context.l10n.pathTrace_you, - point: LatLng( - connector.selfLatitude!, - connector.selfLongitude!, - ), - color: Colors.teal, - icon: Icons.person_pin_circle, - ), - ); - } - for (final c in contactsWithLocation) { - candidates.add( - LineOfSightEndpoint( - label: c.name, - point: LatLng(c.latitude!, c.longitude!), - color: _getNodeColor(c.type), - icon: _getNodeIcon(c.type), - ), - ); - } - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => LineOfSightMapScreen( - title: context.l10n.map_losScreenTitle, - candidates: candidates, + tooltip: context.l10n.contacts_pathTrace, + ), + if (!_isBuildingPathTrace) + IconButton( + icon: const LosIcon(), + onPressed: () { + final candidates = []; + if (connector.selfLatitude != null && + connector.selfLongitude != null) { + candidates.add( + LineOfSightEndpoint( + label: context.l10n.pathTrace_you, + point: LatLng( + connector.selfLatitude!, + connector.selfLongitude!, ), + color: Colors.teal, + icon: Icons.person_pin_circle, ), ); - }, - tooltip: context.l10n.map_lineOfSight, + } + for (final c in contactsWithLocation) { + candidates.add( + LineOfSightEndpoint( + label: c.name, + point: LatLng(c.latitude!, c.longitude!), + color: _getNodeColor(c.type), + icon: _getNodeIcon(c.type), + ), + ); + } + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => LineOfSightMapScreen( + title: context.l10n.map_losScreenTitle, + candidates: candidates, + ), + ), + ); + }, + tooltip: context.l10n.map_lineOfSight, + ), + ], + ), + body: Stack( + children: [ + FlutterMap( + mapController: _mapController, + options: MapOptions( + initialCenter: center, + initialZoom: initialZoom, + minZoom: _mapMinZoom, + maxZoom: _mapMaxZoom, + interactionOptions: InteractionOptions( + flags: ~InteractiveFlag.rotate, + scrollWheelVelocity: isDesktop ? 0.012 : 0.005, + cursorKeyboardRotationOptions: + CursorKeyboardRotationOptions.disabled(), + keyboardOptions: isDesktop + ? const KeyboardOptions( + enableArrowKeysPanning: true, + enableWASDPanning: true, + enableRFZooming: true, + ) + : const KeyboardOptions.disabled(), ), - ], - ), - body: Stack( - children: [ - FlutterMap( - mapController: _mapController, - options: MapOptions( - initialCenter: center, - initialZoom: initialZoom, - minZoom: _mapMinZoom, - maxZoom: _mapMaxZoom, - interactionOptions: InteractionOptions( - flags: ~InteractiveFlag.rotate, - scrollWheelVelocity: isDesktop ? 0.012 : 0.005, - cursorKeyboardRotationOptions: - CursorKeyboardRotationOptions.disabled(), - keyboardOptions: isDesktop - ? const KeyboardOptions( - enableArrowKeysPanning: true, - enableWASDPanning: true, - enableRFZooming: true, - ) - : const KeyboardOptions.disabled(), - ), - onTap: (_, latLng) { - if (_isSelectingPoi) { - setState(() { - _isSelectingPoi = false; - }); - _shareMarker( - context: context, - connector: connector, - position: latLng, - defaultLabel: context.l10n.map_pointOfInterest, - flags: 'poi', - ); - } - }, - onLongPress: (_, latLng) { - if (_isSelectingPoi) { - setState(() { - _isSelectingPoi = false; - }); - _shareMarker( - context: context, - connector: connector, - position: latLng, - defaultLabel: context.l10n.map_pointOfInterest, - flags: 'poi', - ); - return; - } - _showShareMarkerAtPositionSheet( + onTap: (_, latLng) { + if (_isSelectingPoi) { + setState(() { + _isSelectingPoi = false; + }); + _shareMarker( context: context, connector: connector, position: latLng, + defaultLabel: context.l10n.map_pointOfInterest, + flags: 'poi', ); - }, - onPositionChanged: (camera, hasGesture) { - final shouldShow = camera.zoom >= _labelZoomThreshold; - if (shouldShow != _showNodeLabels && mounted) { - setState(() { - _showNodeLabels = shouldShow; - }); - } - }, + } + }, + onLongPress: (_, latLng) { + if (_isSelectingPoi) { + setState(() { + _isSelectingPoi = false; + }); + _shareMarker( + context: context, + connector: connector, + position: latLng, + defaultLabel: context.l10n.map_pointOfInterest, + flags: 'poi', + ); + return; + } + _showShareMarkerAtPositionSheet( + context: context, + connector: connector, + position: latLng, + ); + }, + onPositionChanged: (camera, hasGesture) { + final shouldShow = + settings.mapAlwaysShowNames || + camera.zoom >= _labelZoomThreshold; + if (shouldShow != _showNodeLabels && mounted) { + setState(() { + _showNodeLabels = shouldShow; + }); + } + }, + ), + children: [ + TileLayer( + urlTemplate: kMapTileUrlTemplate, + tileProvider: tileCache.tileProvider, + userAgentPackageName: + MapTileCacheService.userAgentPackageName, + maxZoom: 19, ), - children: [ - TileLayer( - urlTemplate: kMapTileUrlTemplate, - tileProvider: tileCache.tileProvider, - userAgentPackageName: - MapTileCacheService.userAgentPackageName, - maxZoom: 19, - ), - if (_polylines.isNotEmpty && _isBuildingPathTrace) - PolylineLayer(polylines: _polylines), - if (sharedMarkerPolylines.isNotEmpty) - PolylineLayer(polylines: sharedMarkerPolylines), - MarkerLayer( - markers: [ - if (highlightPosition != null) - Marker( - point: highlightPosition, - width: 40, - height: 40, - child: IgnorePointer( - child: Icon( - Icons.location_on_outlined, - color: Colors.red[600], - size: 34, - ), + if (_polylines.isNotEmpty && _isBuildingPathTrace) + PolylineLayer(polylines: _polylines), + if (sharedMarkerPolylines.isNotEmpty) + PolylineLayer(polylines: sharedMarkerPolylines), + MarkerLayer( + markers: [ + if (highlightPosition != null) + Marker( + point: highlightPosition, + width: 40, + height: 40, + child: IgnorePointer( + child: Icon( + Icons.location_on_outlined, + color: Colors.red[600], + size: 34, ), ), - if (!settings.mapShowOverlaps) - ..._buildGuessedMarker( - guessedLocations, - showLabels: _showNodeLabels, - ), - ..._buildMarkers( - contactsWithLocation, - settings, - showLabels: _showNodeLabels, ), - ...sharedMarkers.map(_buildSharedMarker), - if (connector.selfLatitude != null && - connector.selfLongitude != null) - Marker( - point: LatLng( - connector.selfLatitude!, - connector.selfLongitude!, - ), - width: 40, - height: 40, - child: IgnorePointer( - ignoring: true, - child: Container( - padding: const EdgeInsets.all(4), - decoration: BoxDecoration( - color: Colors.teal, - shape: BoxShape.circle, - border: Border.all( - color: Colors.white, - width: 2, - ), - boxShadow: [ - BoxShadow( - color: Colors.black.withValues( - alpha: 0.3, - ), - blurRadius: 4, - offset: const Offset(0, 2), - ), - ], - ), - alignment: Alignment.center, - child: const Icon( - Icons.person_pin_circle, + if (!settings.mapShowOverlaps) + ..._buildGuessedMarker( + guessedLocations, + showLabels: + settings.mapAlwaysShowNames || _showNodeLabels, + ), + ..._buildMarkers( + contactsWithLocation, + settings, + showLabels: + settings.mapAlwaysShowNames || _showNodeLabels, + ), + ...sharedMarkers.map(_buildSharedMarker), + if (connector.selfLatitude != null && + connector.selfLongitude != null) + Marker( + point: LatLng( + connector.selfLatitude!, + connector.selfLongitude!, + ), + width: 40, + height: 40, + child: IgnorePointer( + ignoring: true, + child: Container( + padding: const EdgeInsets.all(4), + decoration: BoxDecoration( + color: Colors.teal, + shape: BoxShape.circle, + border: Border.all( color: Colors.white, - size: 20, + width: 2, ), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.3), + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], + ), + alignment: Alignment.center, + child: const Icon( + Icons.person_pin_circle, + color: Colors.white, + size: 20, ), ), ), - if (_showNodeLabels && - connector.selfLatitude != null && - connector.selfLongitude != null) - _buildNodeLabelMarker( - point: LatLng( - connector.selfLatitude!, - connector.selfLongitude!, - ), - label: context.l10n.pathTrace_you, + ), + if (_showNodeLabels && + connector.selfLatitude != null && + connector.selfLongitude != null) + _buildNodeLabelMarker( + point: LatLng( + connector.selfLatitude!, + connector.selfLongitude!, ), - ], - ), - ], - ), - if (!_isBuildingPathTrace) - _buildLegend( - contacts, - contactsWithLocation, - settings, - sharedMarkers.length, - guessedLocations.length, - ), - if (isDesktop) - _buildDesktopMapControls( - context, - center: center, - zoom: initialZoom, - hasPathSelector: _isBuildingPathTrace, + label: context.l10n.pathTrace_you, + ), + ], ), - if (_isBuildingPathTrace) _buildPathTraceOverlay(), - ], - ), - floatingActionButton: FloatingActionButton( - onPressed: () => _showFilterDialog(context, settingsService), - tooltip: context.l10n.map_filterNodes, - child: const Icon(Icons.filter_list), - ), + ], + ), + if (!_isBuildingPathTrace) + _buildLegend( + contacts, + contactsWithLocation, + settings, + sharedMarkers.length, + guessedLocations.length, + ), + if (isDesktop) + _buildDesktopMapControls( + context, + center: center, + zoom: initialZoom, + hasPathSelector: _isBuildingPathTrace, + ), + if (_isBuildingPathTrace) _buildPathTraceOverlay(), + ], + ), + floatingActionButton: FloatingActionButton( + onPressed: () => _showFilterDialog(context, settingsService), + tooltip: context.l10n.map_filterNodes, + child: const Icon(Icons.filter_list), ), ); }, diff --git a/lib/services/app_settings_service.dart b/lib/services/app_settings_service.dart index 5e3d8c3..d0ea235 100644 --- a/lib/services/app_settings_service.dart +++ b/lib/services/app_settings_service.dart @@ -76,6 +76,10 @@ class AppSettingsService extends ChangeNotifier { await updateSettings(_settings.copyWith(mapShowOverlaps: value)); } + Future setMapAlwaysShowNames(bool value) async { + await updateSettings(_settings.copyWith(mapAlwaysShowNames: value)); + } + Future setMapTimeFilterHours(double value) async { await updateSettings(_settings.copyWith(mapTimeFilterHours: value)); } diff --git a/lib/widgets/app_shell.dart b/lib/widgets/app_shell.dart index 329cb59..689caa3 100644 --- a/lib/widgets/app_shell.dart +++ b/lib/widgets/app_shell.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import '../l10n/l10n.dart'; @@ -12,7 +13,7 @@ import 'quick_switch_bar.dart'; /// dockable pane that can be pinned open on wide ones. /// /// The bottom bar stays a bottom bar at every width; it never becomes a rail. -class AppShell extends StatelessWidget { +class AppShell extends StatefulWidget { static const double wideBreakpoint = 720; static const double _drawerWidth = 300; @@ -58,44 +59,82 @@ class AppShell extends StatelessWidget { this.channelsUnreadCount = 0, }); + @override + State createState() => _AppShellState(); +} + +class _AppShellState extends State { + final GlobalKey _scaffoldKey = GlobalKey(); + + /// System back, in priority order: + /// 1. an open drawer closes, + /// 2. otherwise pop the route (a pushed chat returns to its list), + /// 3. otherwise hand back to the OS, so Android returns to the home + /// screen or the previous app rather than sitting on a dead press. + /// + /// Screens previously did this with `PopScope(canPop: !isConnected)`, which + /// swallowed back entirely while connected: the drawer would not close and + /// the app would never background. + void _handleBack() { + final scaffold = _scaffoldKey.currentState; + if (scaffold?.isDrawerOpen ?? false) { + scaffold!.closeDrawer(); + return; + } + final navigator = Navigator.of(context); + if (navigator.canPop()) { + navigator.pop(); + return; + } + SystemNavigator.pop(); + } + @override Widget build(BuildContext context) { - return LayoutBuilder( - builder: (context, constraints) { - final isWide = constraints.maxWidth >= wideBreakpoint; - final pinned = - isWide && context.watch().navDrawerPinned; - - return pinned - ? _buildPinned(context) - : _buildTransient(context, isWide); + return PopScope( + canPop: false, + onPopInvokedWithResult: (didPop, _) { + if (didPop) return; + _handleBack(); }, + child: LayoutBuilder( + builder: (context, constraints) { + final isWide = constraints.maxWidth >= AppShell.wideBreakpoint; + final pinned = + isWide && context.watch().navDrawerPinned; + + return pinned + ? _buildPinned(context) + : _buildTransient(context, isWide); + }, + ), ); } /// Null on detail screens, which show the panel but no bottom bar. Widget? _bottomBar() { - final index = selectedIndex; - final onSelected = onDestinationSelected; + final index = widget.selectedIndex; + final onSelected = widget.onDestinationSelected; if (index == null || onSelected == null) return null; return SafeArea( top: false, child: QuickSwitchBar( selectedIndex: index, onDestinationSelected: onSelected, - contactsUnreadCount: contactsUnreadCount, - channelsUnreadCount: channelsUnreadCount, + contactsUnreadCount: widget.contactsUnreadCount, + channelsUnreadCount: widget.channelsUnreadCount, ), ); } PreferredSizeWidget? _appBar(BuildContext context, bool pinned) { - return appBarBuilder?.call(context, pinned) ?? appBar; + return widget.appBarBuilder?.call(context, pinned) ?? widget.appBar; } /// Wide + pinned: the panel is laid out beside the body, not overlaid. Widget _buildPinned(BuildContext context) { return Scaffold( + key: _scaffoldKey, appBar: _appBar(context, true), body: SafeArea( top: false, @@ -103,20 +142,20 @@ class AppShell extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SizedBox( - width: _drawerWidth, + width: AppShell._drawerWidth, child: _NavPanel( isWide: true, - content: drawerContent, - onDisconnect: onDisconnect, - onSettings: onSettings, + content: widget.drawerContent, + onDisconnect: widget.onDisconnect, + onSettings: widget.onSettings, ), ), const VerticalDivider(width: 1), - Expanded(child: body), + Expanded(child: widget.body), ], ), ), - floatingActionButton: floatingActionButton, + floatingActionButton: widget.floatingActionButton, bottomNavigationBar: _bottomBar(), ); } @@ -125,18 +164,19 @@ class AppShell extends StatelessWidget { /// the hamburger into the app bar automatically. Widget _buildTransient(BuildContext context, bool isWide) { return Scaffold( + key: _scaffoldKey, appBar: _appBar(context, false), drawer: Drawer( - width: _drawerWidth, + width: AppShell._drawerWidth, child: _NavPanel( isWide: isWide, - content: drawerContent, - onDisconnect: onDisconnect, - onSettings: onSettings, + content: widget.drawerContent, + onDisconnect: widget.onDisconnect, + onSettings: widget.onSettings, ), ), - body: body, - floatingActionButton: floatingActionButton, + body: widget.body, + floatingActionButton: widget.floatingActionButton, bottomNavigationBar: _bottomBar(), ); } diff --git a/lib/widgets/map_layer_panel.dart b/lib/widgets/map_layer_panel.dart index 13df116..29c18ea 100644 --- a/lib/widgets/map_layer_panel.dart +++ b/lib/widgets/map_layer_panel.dart @@ -46,6 +46,13 @@ class MapLayerPanel extends StatelessWidget { onChanged: service.setMapShowOtherNodes, ), const Divider(height: 1), + _toggle( + label: l10n.map_alwaysShowNames, + icon: Icons.label_outline, + value: settings.mapAlwaysShowNames, + onChanged: service.setMapAlwaysShowNames, + ), + const Divider(height: 1), _sectionHeader(theme, l10n.map_filterNodes), _toggle( label: l10n.map_showDiscoveryContacts, From 4dbceb3366c48f35c7d392f4e7ce1206226e9fb6 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 00:48:16 -0400 Subject: [PATCH 21/60] fix(#291): back must not drop a connected user on the radio list Two bugs, reported together from an S25 FE test. **Back popped to the scanner.** The previous commit made back pop whenever the route could be popped. The primary views sit on top of the scanner, so from the channel list back landed on the radio-connect screen. That is a regression I introduced: the `PopScope(canPop: !isConnected)` I removed existed to prevent exactly this, and I removed it without establishing why it was there. Back now distinguishes the two cases. A primary view (one carrying the bottom bar) hands back to the OS and backgrounds the app. A detail screen (a channel chat, which has no selectedIndex and is genuinely pushed) pops to its list. Reaching the scanner is what Disconnect is for, not what Back is for. **The scanner was a dead end while connected.** It only routed into the app on a connection transition, behind a one-shot flag that never reset while connected. So once showing with a live connection it stayed there, and pressing Connect did nothing, since there was nothing left to connect. It now routes in whenever it is the visible route and a connection is live, guarded against double-push by a dedicated flag rather than reusing _changedNavigation, which separately gates the disconnect-on-dispose cleanup. Known limitation, pre-existing and unchanged: this auto-entry is bluetooth only. USB and TCP connect from their own screens, which navigate themselves. flutter analyze clean, dart format clean, 469 tests pass. --- lib/screens/scanner_screen.dart | 22 ++++++++++++++++++---- lib/widgets/app_shell.dart | 20 +++++++++++++------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/lib/screens/scanner_screen.dart b/lib/screens/scanner_screen.dart index 3222168..f8eece9 100644 --- a/lib/screens/scanner_screen.dart +++ b/lib/screens/scanner_screen.dart @@ -25,6 +25,11 @@ class ScannerScreen extends StatefulWidget { class _ScannerScreenState extends State { bool _changedNavigation = false; + + /// Re-entrancy guard for routing into the app. Distinct from + /// [_changedNavigation], which records that we entered at least once and + /// gates the disconnect-on-dispose cleanup. + bool _routingIntoApp = false; late final MeshCoreConnector _connector; late final VoidCallback _connectionListener; BluetoothAdapterState _bluetoothState = BluetoothAdapterState.unknown; @@ -42,12 +47,21 @@ class _ScannerScreenState extends State { } else if (_connector.state == MeshCoreConnectionState.connected && _connector.activeTransport == MeshCoreTransportType.bluetooth && isCurrentRoute && - !_changedNavigation) { + !_routingIntoApp) { + // Route in whenever this screen is showing while connected, not just + // on the first connect. Otherwise landing back here with a live + // connection is a dead end: Connect does nothing, because there is + // nothing left to connect. _changedNavigation = true; + _routingIntoApp = true; if (mounted) { - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => const ChannelsScreen()), - ); + Navigator.of(context) + .push( + MaterialPageRoute(builder: (context) => const ChannelsScreen()), + ) + .then((_) { + if (mounted) _routingIntoApp = false; + }); } } }; diff --git a/lib/widgets/app_shell.dart b/lib/widgets/app_shell.dart index 689caa3..b95ba8d 100644 --- a/lib/widgets/app_shell.dart +++ b/lib/widgets/app_shell.dart @@ -68,21 +68,27 @@ class _AppShellState extends State { /// System back, in priority order: /// 1. an open drawer closes, - /// 2. otherwise pop the route (a pushed chat returns to its list), - /// 3. otherwise hand back to the OS, so Android returns to the home - /// screen or the previous app rather than sitting on a dead press. + /// 2. on a detail screen, pop back to the list it came from, + /// 3. on a primary view, hand back to the OS so Android returns to the + /// home screen or the previous app. /// - /// Screens previously did this with `PopScope(canPop: !isConnected)`, which - /// swallowed back entirely while connected: the drawer would not close and - /// the app would never background. + /// Step 3 must NOT pop, even though the route below can be popped. The + /// primary views sit on top of the scanner, so popping would dump a + /// connected user back onto the radio-connect list. Reaching the scanner is + /// what Disconnect is for, not what Back is for. + /// + /// A primary view is one carrying the bottom bar; a detail screen (a channel + /// chat) has no [selectedIndex] and is genuinely pushed. void _handleBack() { final scaffold = _scaffoldKey.currentState; if (scaffold?.isDrawerOpen ?? false) { scaffold!.closeDrawer(); return; } + + final isPrimaryView = widget.selectedIndex != null; final navigator = Navigator.of(context); - if (navigator.canPop()) { + if (!isPrimaryView && navigator.canPop()) { navigator.pop(); return; } From c8b36b056d510a1cad5dbc9160cb05be6877cb05 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 00:56:52 -0400 Subject: [PATCH 22/60] fix(#291): route into the app from the scanner on any transport The scanner's auto-entry was gated on activeTransport == bluetooth, so the dead end fixed in the previous commit still applied to USB and TCP: a user landing on the scanner with a live serial or network connection stayed stuck, and pressing Connect did nothing, since there was nothing left to connect. Both are real deployment paths, so the gate is removed and the check is now transport-agnostic. The first-connect path for USB and TCP is unaffected. Those screens are pushed on top of the scanner and navigate themselves with pushReplacement; while they are connecting they are the current route, so the scanner's isCurrentRoute guard is false and it does not also push. That guard, not the transport check, is what prevents a double navigation. flutter analyze clean, dart format clean, 469 tests pass. --- lib/screens/scanner_screen.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/screens/scanner_screen.dart b/lib/screens/scanner_screen.dart index f8eece9..e2f635a 100644 --- a/lib/screens/scanner_screen.dart +++ b/lib/screens/scanner_screen.dart @@ -45,13 +45,18 @@ class _ScannerScreenState extends State { if (_connector.state == MeshCoreConnectionState.disconnected) { _changedNavigation = false; } else if (_connector.state == MeshCoreConnectionState.connected && - _connector.activeTransport == MeshCoreTransportType.bluetooth && isCurrentRoute && !_routingIntoApp) { // Route in whenever this screen is showing while connected, not just // on the first connect. Otherwise landing back here with a live // connection is a dead end: Connect does nothing, because there is // nothing left to connect. + // + // Deliberately transport-agnostic. This was bluetooth-only, which left + // the same dead end for USB and TCP users, who cannot connect their way + // out of it either. The first-connect path for those transports is not + // affected: their own screen sits on top while connecting, so + // isCurrentRoute is false here and they still navigate themselves. _changedNavigation = true; _routingIntoApp = true; if (mounted) { From c7fc4dca7b5769adcf99bde83173cf276bf79650 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 02:22:34 -0400 Subject: [PATCH 23/60] fix(#291): background the app on back instead of killing it Back at the root was calling SystemNavigator.pop(). That is not "background the app": on Android it calls finish() on the activity, which tears down the Flutter engine and drops the radio connection. Backing out and reopening therefore landed on a disconnected radio needing a fresh connect. Wrong call for the job, and worse than the behaviour it replaced. Backgrounding without finishing needs moveTaskToBack, which has no Flutter equivalent, so it goes over a method channel: - MainActivity exposes meshcore_open/app_lifecycle with a moveTaskToBack method, alongside the existing USB channel. - AppBackgrounder wraps it and is a no-op off Android, where programmatic backgrounding either does not apply (desktop windows close by their own chrome) or is forbidden (iOS). On those platforms back at the root stays unhandled rather than doing something destructive. - AppShell awaits it instead of calling SystemNavigator.pop(). The activity stays alive, so the connection survives and reopening returns to where the user was. flutter analyze clean, dart format clean, 469 tests pass. --- .../meshcore/meshcore_open/MainActivity.kt | 19 +++++++++++ lib/utils/app_backgrounder.dart | 33 +++++++++++++++++++ lib/widgets/app_shell.dart | 14 +++++--- 3 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 lib/utils/app_backgrounder.dart diff --git a/android/app/src/main/kotlin/com/meshcore/meshcore_open/MainActivity.kt b/android/app/src/main/kotlin/com/meshcore/meshcore_open/MainActivity.kt index 9022c8b..27136ea 100644 --- a/android/app/src/main/kotlin/com/meshcore/meshcore_open/MainActivity.kt +++ b/android/app/src/main/kotlin/com/meshcore/meshcore_open/MainActivity.kt @@ -2,13 +2,32 @@ package com.meshcore.meshcore_open import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodChannel class MainActivity : FlutterActivity() { private val usbFunctions by lazy { MeshcoreUsbFunctions(this) } + private val appLifecycleChannelName = "meshcore_open/app_lifecycle" + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) usbFunctions.configureFlutterEngine(flutterEngine) + + MethodChannel( + flutterEngine.dartExecutor.binaryMessenger, + appLifecycleChannelName + ).setMethodCallHandler { call, result -> + when (call.method) { + // Send the task to the background WITHOUT finishing the + // activity. SystemNavigator.pop() calls finish(), which tears + // down the Flutter engine and drops the radio connection, so + // reopening the app lands on a disconnected radio. + "moveTaskToBack" -> { + result.success(moveTaskToBack(true)) + } + else -> result.notImplemented() + } + } } override fun onDestroy() { diff --git a/lib/utils/app_backgrounder.dart b/lib/utils/app_backgrounder.dart new file mode 100644 index 0000000..2a01908 --- /dev/null +++ b/lib/utils/app_backgrounder.dart @@ -0,0 +1,33 @@ +import 'package:flutter/services.dart'; + +import 'platform_info.dart'; + +/// Sends the app to the background without tearing it down. +/// +/// `SystemNavigator.pop()` is NOT this: on Android it calls `finish()` on the +/// activity, which destroys the Flutter engine and drops the radio connection, +/// so reopening the app finds itself disconnected. `moveTaskToBack` leaves the +/// activity alive and simply hands focus back to whatever was in front before. +class AppBackgrounder { + static const MethodChannel _channel = MethodChannel( + 'meshcore_open/app_lifecycle', + ); + + /// Returns true when the app was actually backgrounded. + /// + /// Android only. Desktop windows are closed by their own chrome and iOS + /// forbids programmatic backgrounding, so elsewhere this is a no-op and the + /// caller should leave the press unhandled rather than act on it. + static Future moveToBackground() async { + if (!PlatformInfo.isAndroid) return false; + try { + return await _channel.invokeMethod('moveTaskToBack') ?? false; + } on PlatformException catch (_) { + // Surface nothing to the user: the fallback is simply that back does + // nothing at the root, which is the pre-existing behaviour. + return false; + } on MissingPluginException catch (_) { + return false; + } + } +} diff --git a/lib/widgets/app_shell.dart b/lib/widgets/app_shell.dart index b95ba8d..d07c3ec 100644 --- a/lib/widgets/app_shell.dart +++ b/lib/widgets/app_shell.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import '../l10n/l10n.dart'; import '../services/ui_view_state_service.dart'; +import '../utils/app_backgrounder.dart'; import 'quick_switch_bar.dart'; /// Shared shell for the primary views (Contacts / Channels / Map). @@ -69,8 +69,8 @@ class _AppShellState extends State { /// System back, in priority order: /// 1. an open drawer closes, /// 2. on a detail screen, pop back to the list it came from, - /// 3. on a primary view, hand back to the OS so Android returns to the - /// home screen or the previous app. + /// 3. on a primary view, send the app to the background so Android + /// returns to the home screen or the previous app. /// /// Step 3 must NOT pop, even though the route below can be popped. The /// primary views sit on top of the scanner, so popping would dump a @@ -79,7 +79,7 @@ class _AppShellState extends State { /// /// A primary view is one carrying the bottom bar; a detail screen (a channel /// chat) has no [selectedIndex] and is genuinely pushed. - void _handleBack() { + Future _handleBack() async { final scaffold = _scaffoldKey.currentState; if (scaffold?.isDrawerOpen ?? false) { scaffold!.closeDrawer(); @@ -92,7 +92,11 @@ class _AppShellState extends State { navigator.pop(); return; } - SystemNavigator.pop(); + + // Background, do NOT finish. SystemNavigator.pop() would call finish() on + // the activity, tearing down the Flutter engine and dropping the radio + // connection, so reopening the app would show a disconnected radio. + await AppBackgrounder.moveToBackground(); } @override From 5eded9e33ca59cce2b986a347f3aa0996e82bcb3 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 04:21:45 -0400 Subject: [PATCH 24/60] fix(#306): guard every legacy-key removal, not just the channel-settings one The previous commit fixed loadSmazEnabled and killed the post-connect stall (startup-load channelSettings: 37792ms -> 0ms, measured twice). The contact pull still choked, in multi-second bursts, each landing immediately after an "Added new contact" line. No frame handler exceeded the 100ms threshold, because the work is not in a handler: the contact handler fires _loadMessagesForContact unawaited, so it runs after the handler returns and escapes that timing. message_store.loadMessages had the same unconditional prefs.remove(oldKey), and it runs ONCE PER CONTACT during a pull. On Windows every prefs mutation rewrites the entire file, so a 234-contact pull meant 234 full multi-MB writes for legacy keys that do not exist. A sweep found the identical pattern in six more stores: contact_store, unread_store, channel_store, community_store, contact_group_store and channel_order_store. Those run once per load rather than per item, so they are far cheaper, but it is the same bug and they are fixed the same way. In every case the removal now happens only when a legacy key actually exists, paired with the write that migrates it, so the cleanup still occurs on a real migration. flutter analyze clean, dart format clean, 494 tests pass. --- lib/storage/channel_order_store.dart | 4 +++- lib/storage/channel_store.dart | 4 +++- lib/storage/community_store.dart | 4 +++- lib/storage/contact_group_store.dart | 4 +++- lib/storage/contact_store.dart | 4 +++- lib/storage/message_store.dart | 7 ++++++- lib/storage/unread_store.dart | 4 +++- 7 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/storage/channel_order_store.dart b/lib/storage/channel_order_store.dart index 88d3f7a..4e268b2 100644 --- a/lib/storage/channel_order_store.dart +++ b/lib/storage/channel_order_store.dart @@ -29,13 +29,15 @@ class ChannelOrderStore { String? jsonString = prefs.getString(keyFor); if (jsonString == null || jsonString.isEmpty) { // Attempt migration from legacy unscoped key on first load + // Only remove the legacy key when it actually exists: every prefs + // mutation rewrites the whole file on Windows. final legacyJsonString = prefs.getString(_keyPrefix); - prefs.remove(_keyPrefix); if (legacyJsonString != null && legacyJsonString.isNotEmpty) { appLogger.info( 'Migrating channel order from legacy key $_keyPrefix to scoped key $keyFor', ); await prefs.setString(keyFor, legacyJsonString); + await prefs.remove(_keyPrefix); jsonString = legacyJsonString; } } diff --git a/lib/storage/channel_store.dart b/lib/storage/channel_store.dart index 4f40482..b52b5b7 100644 --- a/lib/storage/channel_store.dart +++ b/lib/storage/channel_store.dart @@ -22,13 +22,15 @@ class ChannelStore { String? jsonString = prefs.getString(keyFor); if (jsonString == null || jsonString.isEmpty) { // Attempt migration from legacy unscoped key on first load + // Only remove the legacy key when it actually exists: every prefs + // mutation rewrites the whole file on Windows. final legacyJsonString = prefs.getString(_keyPrefix); - prefs.remove(_keyPrefix); if (legacyJsonString != null && legacyJsonString.isNotEmpty) { appLogger.info( 'Migrating channel messages from legacy key $_keyPrefix to scoped key $keyFor', ); await prefs.setString(keyFor, legacyJsonString); + await prefs.remove(_keyPrefix); jsonString = legacyJsonString; } } diff --git a/lib/storage/community_store.dart b/lib/storage/community_store.dart index c69d0b8..a8010a1 100644 --- a/lib/storage/community_store.dart +++ b/lib/storage/community_store.dart @@ -28,13 +28,15 @@ class CommunityStore { String? jsonString = prefs.getString(keyFor); if (jsonString == null || jsonString.isEmpty) { // Attempt migration from legacy unscoped key on first load + // Only remove the legacy key when it actually exists: every prefs + // mutation rewrites the whole file on Windows. final legacyJsonString = prefs.getString(_keyPrefix); - prefs.remove(_keyPrefix); if (legacyJsonString != null && legacyJsonString.isNotEmpty) { appLogger.info( 'Migrating communities from legacy key $_keyPrefix to scoped key $keyFor', ); await prefs.setString(keyFor, legacyJsonString); + await prefs.remove(_keyPrefix); jsonString = legacyJsonString; } } diff --git a/lib/storage/contact_group_store.dart b/lib/storage/contact_group_store.dart index ce6a0c6..0813274 100644 --- a/lib/storage/contact_group_store.dart +++ b/lib/storage/contact_group_store.dart @@ -21,13 +21,15 @@ class ContactGroupStore { String? jsonString = prefs.getString(keyFor); if (jsonString == null || jsonString.isEmpty) { // Attempt migration from legacy unscoped key on first load + // Only remove the legacy key when it actually exists: every prefs + // mutation rewrites the whole file on Windows. final legacyJsonString = prefs.getString(_keyPrefix); - prefs.remove(_keyPrefix); if (legacyJsonString != null && legacyJsonString.isNotEmpty) { appLogger.info( 'Migrating channel messages from legacy key $_keyPrefix to scoped key $keyFor', ); await prefs.setString(keyFor, legacyJsonString); + await prefs.remove(_keyPrefix); jsonString = legacyJsonString; } } diff --git a/lib/storage/contact_store.dart b/lib/storage/contact_store.dart index 5d1805d..9085d4e 100644 --- a/lib/storage/contact_store.dart +++ b/lib/storage/contact_store.dart @@ -23,13 +23,15 @@ class ContactStore { String? jsonString = prefs.getString(keyFor); if (jsonString == null || jsonString.isEmpty) { // Attempt migration from legacy unscoped key on first load + // Only remove the legacy key when it actually exists: every prefs + // mutation rewrites the whole file on Windows. final legacyJsonString = prefs.getString(_keyPrefix); - prefs.remove(_keyPrefix); if (legacyJsonString != null && legacyJsonString.isNotEmpty) { appLogger.info( 'Migrating contacts from legacy key $_keyPrefix to scoped key $keyFor', ); await prefs.setString(keyFor, legacyJsonString); + await prefs.remove(_keyPrefix); jsonString = legacyJsonString; } } diff --git a/lib/storage/message_store.dart b/lib/storage/message_store.dart index ccaf9ea..6045342 100644 --- a/lib/storage/message_store.dart +++ b/lib/storage/message_store.dart @@ -40,13 +40,18 @@ class MessageStore { String? jsonString = prefs.getString(key); if (jsonString == null || jsonString.isEmpty) { // Attempt migration from legacy unscoped key on first load + // Only touch storage when a legacy key actually exists. This ran + // unconditionally, and loadMessages is called once per contact during a + // contact pull. On Windows shared_preferences rewrites the WHOLE prefs + // file on every mutation, so each no-op remove cost a full multi-MB + // write - hundreds of them back to back on a large address book. final legacyJsonString = prefs.getString(oldKey); - prefs.remove(oldKey); if (legacyJsonString != null && legacyJsonString.isNotEmpty) { appLogger.info( 'Migrating messages from legacy key $oldKey to scoped key $key', ); await prefs.setString(key, legacyJsonString); + await prefs.remove(oldKey); jsonString = legacyJsonString; } } diff --git a/lib/storage/unread_store.dart b/lib/storage/unread_store.dart index 3b615b1..cc1f377 100644 --- a/lib/storage/unread_store.dart +++ b/lib/storage/unread_store.dart @@ -35,13 +35,15 @@ class UnreadStore { String? jsonString = prefs.getString(keyFor); if (jsonString == null || jsonString.isEmpty) { // Attempt migration from legacy unscoped key on first load + // Only remove the legacy key when it actually exists: every prefs + // mutation rewrites the whole file on Windows. final legacyJsonString = prefs.getString(_keyPrefix); - prefs.remove(_keyPrefix); if (legacyJsonString != null && legacyJsonString.isNotEmpty) { appLogger.info( 'Migrating channel messages from legacy key $_keyPrefix to scoped key $keyFor', ); await prefs.setString(keyFor, legacyJsonString); + await prefs.remove(_keyPrefix); jsonString = legacyJsonString; } } From a7ef7b33257bd1c1e0eba87522fd1701dcc3d9af Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 04:40:00 -0400 Subject: [PATCH 25/60] perf(#306): measure the per-contact conversation load The startup loads are now clean (all steps ~0ms, down from 37792ms), but the contact pull still stalls in multi-second bursts: 8.0s and 7.7s at +25s..+52s on the last run, matching the reported freeze from ~15s to ~49s. No frame handler exceeds 100ms, because the work is not in one. _loadMessagesForContact is fired unawaited from the contact handler, so it runs after the handler returns and escapes that timing entirely. This times it directly and reports cumulative store time versus merge+notify time every 25 contacts, so the next run says which half is responsible rather than requiring another guess. Two candidates it will discriminate: - store: prefs read + jsonDecode per contact - merge+notify: the per-contact notifyListeners, i.e. one full widget tree rebuild per contact, each of which walks the contact list Diagnostics only, no behaviour change. flutter analyze clean, dart format clean. --- lib/connector/meshcore_connector.dart | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 1d297c8..2bfb1ca 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -703,11 +703,30 @@ class MeshCoreConnector extends ChangeNotifier { notifyListeners(); } + /// Aggregate cost of the per-contact conversation load, which runs once per + /// contact during a pull. It is fired unawaited from the contact handler, so + /// it escapes the frame-handler timing and has to be measured here. + int _convLoadCount = 0; + int _convLoadStoreMs = 0; + int _convLoadMergeMs = 0; + Future _loadMessagesForContact(String contactKeyHex) async { if (_loadedConversationKeys.contains(contactKeyHex)) return; _loadedConversationKeys.add(contactKeyHex); + final storeWatch = Stopwatch()..start(); final allMessages = await _messageStore.loadMessages(contactKeyHex); + storeWatch.stop(); + final mergeWatch = Stopwatch()..start(); + _convLoadCount++; + _convLoadStoreMs += storeWatch.elapsedMilliseconds; + if (_convLoadCount % 25 == 0) { + appLogger.info( + 'conversation loads: $_convLoadCount contacts, ' + 'store=${_convLoadStoreMs}ms merge=${_convLoadMergeMs}ms cumulative', + tag: 'Perf', + ); + } if (allMessages.isNotEmpty) { // Keep only the most recent N messages in memory to bound memory usage final windowedMessages = allMessages.length > _messageWindowSize @@ -748,6 +767,8 @@ class MeshCoreConnector extends ChangeNotifier { _conversations[contactKeyHex] = windowedMergedMessages; notifyListeners(); } + mergeWatch.stop(); + _convLoadMergeMs += mergeWatch.elapsedMilliseconds; } String _messageMergeKey(Message message) { From 30ab360d66f1fbdd3b906ae1431da48723e64ca6 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 04:43:42 -0400 Subject: [PATCH 26/60] fix(#306): surface silent storage failures per SAFELANE 6 The stores violated the no-silent-failures rule, and that is why tonight's diagnosis took as long as it did. Two classes: 1. Ten catch blocks swallowed decode errors and returned an empty collection. `catch (_) { return []; }` means a corrupt or unreadable store presents to the caller as "no data", so the user sees no contacts, no channels or no history with nothing anywhere explaining why. Indistinguishable from data loss. Every one now logs what failed, with the store named, before returning. 2. ChannelMessageStore._storageKey fell back from the PSK-identity key to the legacy slot-index key without a word. That fallback is correct before a channel list exists, but once history has been migrated to the PSK key it reads a DIFFERENT key and returns empty. That is exactly #333: a user's 566 Public messages were intact on disk while the app showed nothing, with no error, no parse failure and no log line to follow. It now warns when it falls back while a resolver is installed, since that combination specifically means the channel list was not loaded first. Had either of these been loud, #333 would have been a one-line log read instead of a multi-hour investigation that looked like data loss. flutter analyze clean, dart format clean, 494 tests pass. --- lib/storage/channel_message_store.dart | 14 ++++++++++++++ lib/storage/channel_store.dart | 6 +++++- lib/storage/community_store.dart | 12 ++++++++++-- lib/storage/contact_discovery_store.dart | 10 +++++++++- lib/storage/contact_store.dart | 6 +++++- lib/storage/message_store.dart | 7 +++++++ lib/storage/unread_store.dart | 6 +++++- 7 files changed, 55 insertions(+), 6 deletions(-) diff --git a/lib/storage/channel_message_store.dart b/lib/storage/channel_message_store.dart index dfef8c3..63dc24a 100644 --- a/lib/storage/channel_message_store.dart +++ b/lib/storage/channel_message_store.dart @@ -32,11 +32,25 @@ class ChannelMessageStore { String _indexKey(int channelIndex) => '$keyFor$channelIndex'; /// Active storage key: PSK identity when known, else the slot index. + /// + /// The index fallback is legitimate before a channel list has loaded, but it + /// reads a DIFFERENT key: if history was already migrated to the PSK key, the + /// caller gets an empty list that is indistinguishable from data loss. + /// SAFELANE 6 - this must never be silent. Falling back where a resolver + /// exists means the channel list was not ready, which is the #333 race. String _storageKey(int channelIndex) { final pskHex = channelPskResolver?.call(channelIndex); if (pskHex != null && pskHex.isNotEmpty) { return '$keyFor$_pskMarker$pskHex'; } + if (channelPskResolver != null) { + appLogger.warn( + 'Channel $channelIndex has no PSK yet; falling back to the slot-index ' + 'key. Any history already migrated to the PSK key will read as EMPTY. ' + 'This means the channel list was not loaded first (#333).', + tag: 'Storage', + ); + } return _indexKey(channelIndex); } diff --git a/lib/storage/channel_store.dart b/lib/storage/channel_store.dart index b52b5b7..596d3bc 100644 --- a/lib/storage/channel_store.dart +++ b/lib/storage/channel_store.dart @@ -47,7 +47,11 @@ class ChannelStore { return jsonList .map((entry) => _fromJson(entry as Map)) .toList(); - } catch (_) { + } catch (e) { + // SAFELANE 6: never swallow. A decode failure here is + // indistinguishable from 'no data' to the caller, which reads + // to the user as data loss. + appLogger.error('Failed to decode channels: $e', tag: 'Storage'); return []; } } diff --git a/lib/storage/community_store.dart b/lib/storage/community_store.dart index a8010a1..732ff47 100644 --- a/lib/storage/community_store.dart +++ b/lib/storage/community_store.dart @@ -107,7 +107,11 @@ class CommunityStore { final communities = await loadCommunities(); try { return communities.firstWhere((c) => c.id == communityId); - } catch (_) { + } catch (e) { + // SAFELANE 6: never swallow. A decode failure here is + // indistinguishable from 'no data' to the caller, which reads + // to the user as data loss. + appLogger.error('Failed to decode communities: $e', tag: 'Storage'); return null; } } @@ -118,7 +122,11 @@ class CommunityStore { final communities = await loadCommunities(); try { return communities.firstWhere((c) => c.communityId == cid); - } catch (_) { + } catch (e) { + // SAFELANE 6: never swallow. A decode failure here is + // indistinguishable from 'no data' to the caller, which reads + // to the user as data loss. + appLogger.error('Failed to decode communities: $e', tag: 'Storage'); return null; } } diff --git a/lib/storage/contact_discovery_store.dart b/lib/storage/contact_discovery_store.dart index 3f6f171..796652e 100644 --- a/lib/storage/contact_discovery_store.dart +++ b/lib/storage/contact_discovery_store.dart @@ -3,6 +3,7 @@ import 'dart:typed_data'; import '../models/contact.dart'; import 'prefs_manager.dart'; +import '../utils/app_logger.dart'; class ContactDiscoveryStore { static const String _keyPrefix = 'discovered_contacts'; @@ -17,7 +18,14 @@ class ContactDiscoveryStore { return jsonList .map((entry) => _fromJson(entry as Map)) .toList(); - } catch (_) { + } catch (e) { + // SAFELANE 6: never swallow. A decode failure here is + // indistinguishable from 'no data' to the caller, which reads + // to the user as data loss. + appLogger.error( + 'Failed to decode discovered contacts: $e', + tag: 'Storage', + ); return []; } } diff --git a/lib/storage/contact_store.dart b/lib/storage/contact_store.dart index 9085d4e..9149046 100644 --- a/lib/storage/contact_store.dart +++ b/lib/storage/contact_store.dart @@ -47,7 +47,11 @@ class ContactStore { return jsonList .map((entry) => _fromJson(entry as Map)) .toList(); - } catch (_) { + } catch (e) { + // SAFELANE 6: never swallow. A decode failure here is + // indistinguishable from 'no data' to the caller, which reads + // to the user as data loss. + appLogger.error('Failed to decode contacts: $e', tag: 'Storage'); return []; } } diff --git a/lib/storage/message_store.dart b/lib/storage/message_store.dart index 6045342..96595e4 100644 --- a/lib/storage/message_store.dart +++ b/lib/storage/message_store.dart @@ -66,6 +66,13 @@ class MessageStore { final jsonList = jsonDecode(jsonString) as List; return jsonList.map((json) => _messageFromJson(json)).toList(); } catch (e) { + // SAFELANE 6: never swallow. A decode failure here is + // indistinguishable from 'no data' to the caller, which reads + // to the user as data loss. + appLogger.error( + 'Failed to decode messages for $contactKeyHex: $e', + tag: 'Storage', + ); return []; } } diff --git a/lib/storage/unread_store.dart b/lib/storage/unread_store.dart index cc1f377..a9cd4df 100644 --- a/lib/storage/unread_store.dart +++ b/lib/storage/unread_store.dart @@ -57,7 +57,11 @@ class UnreadStore { try { final json = jsonDecode(jsonString) as Map; return json.map((key, value) => MapEntry(key, value as int)); - } catch (_) { + } catch (e) { + // SAFELANE 6: never swallow. A decode failure here is + // indistinguishable from 'no data' to the caller, which reads + // to the user as data loss. + appLogger.error('Failed to decode unread state: $e', tag: 'Storage'); return {}; } } From 77625db575a2724aa6e79f03ca4887d578c7fae5 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 13:31:04 -0400 Subject: [PATCH 27/60] perf(#306): measure cumulative sync time per frame code The conversation-load metric was misread, and nearly caused a fix to the wrong thing. It reported store=30138ms across 300 contacts, which looked like loadMessages costing ~100ms each. It is not: - The device-wide fallback key `messages_` does not exist on the reporting machine, and only 11 per-contact message keys exist in total, so loadMessages does three in-memory map lookups and returns empty. - storeWatch spans an `await`, so it measures WALL time including event-loop queueing, not work. merge, which is synchronous and has no await, measures 0ms - consistent with the isolate being busy elsewhere while those awaits wait their turn. So the 30s is ~300 awaits each waiting for a busy isolate, not 30s of storage work. The metric label now says so, rather than inviting the same misreading. The per-call 100ms threshold cannot see the actual shape here: a handler that takes 40ms and runs 234 times owns ~9s of isolate time and never trips it. Frame handlers now accumulate synchronous time per frame code and report the top codes by total, which names the owner regardless of per-call cost. Diagnostics only, no behaviour change. flutter analyze clean, dart format clean, 494 tests pass. --- lib/connector/meshcore_connector.dart | 45 +++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 2bfb1ca..af5b8a3 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -723,7 +723,8 @@ class MeshCoreConnector extends ChangeNotifier { if (_convLoadCount % 25 == 0) { appLogger.info( 'conversation loads: $_convLoadCount contacts, ' - 'store=${_convLoadStoreMs}ms merge=${_convLoadMergeMs}ms cumulative', + 'store=${_convLoadStoreMs}ms(WALL, spans await - includes event-loop ' + 'queueing, NOT pure work) merge=${_convLoadMergeMs}ms(sync work)', tag: 'Perf', ); } @@ -4376,17 +4377,55 @@ class MeshCoreConnector extends ChangeNotifier { /// once. Logged rather than assumed, so the offending code is named. static const Duration _slowHandlerThreshold = Duration(milliseconds: 100); + /// Cumulative synchronous time per frame code. A single handler under the + /// slow threshold can still dominate if it runs hundreds of times, which a + /// per-call threshold cannot see. + final Map _frameCodeMicros = {}; + final Map _frameCodeCount = {}; + int _frameTotalMicros = 0; + void _handleFrame(List data) { final handlerWatch = Stopwatch()..start(); _handleFrameInner(data); handlerWatch.stop(); - if (handlerWatch.elapsed > _slowHandlerThreshold && data.isNotEmpty) { + if (data.isEmpty) return; + + final code = data[0]; + final micros = handlerWatch.elapsedMicroseconds; + _frameCodeMicros[code] = (_frameCodeMicros[code] ?? 0) + micros; + _frameCodeCount[code] = (_frameCodeCount[code] ?? 0) + 1; + _frameTotalMicros += micros; + + if (handlerWatch.elapsed > _slowHandlerThreshold) { appLogger.info( - 'slow frame handler: code=${data[0]} blocked UI for ' + 'slow frame handler: code=$code blocked UI for ' '${handlerWatch.elapsedMilliseconds}ms', tag: 'Perf', ); } + + // Periodic cumulative report: names the code that owns the most isolate + // time even when no single call is slow. + if (_frameTotalMicros > 2000000) { + final ranked = _frameCodeMicros.entries.toList() + ..sort((a, b) => b.value.compareTo(a.value)); + final top = ranked + .take(4) + .map( + (e) => + 'code=${e.key} ${(e.value / 1000).round()}ms' + '/${_frameCodeCount[e.key]}calls', + ) + .join(' '); + appLogger.info( + 'frame handler cumulative (${(_frameTotalMicros / 1000).round()}ms ' + 'total sync): $top', + tag: 'Perf', + ); + _frameTotalMicros = 0; + _frameCodeMicros.clear(); + _frameCodeCount.clear(); + } } void _handleFrameInner(List data) { From 2004459556ce4bb669446c6162963ff185c9e79d Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 13:45:29 -0400 Subject: [PATCH 28/60] perf(#306): throttle the per-contact notify during a bulk pull Measured, not guessed. Cumulative per-code frame timing named the owner: frame handler cumulative (2002ms total sync): code=3 2002ms/105calls code=3 is RESP_CODE_CONTACT, and it accounted for ALL of the synchronous frame time at ~19ms per contact. That is why a per-call 100ms threshold never fired: no single call is slow, the volume is. The 19ms is _handleContact calling notifyListeners() for every contact. Each notification is a synchronous full-tree rebuild, and each rebuild walks the contact list, so the cost grows with the address book. The channel handler directly alongside already guards its notify with _isLoadingChannels; the contact handler had no equivalent. Notifications are now throttled to one per 250ms while _isLoadingContacts is set, and remain immediate outside a pull so single adverts still update live. Throttled rather than suppressed so the sync progress bar keeps advancing. The existing notifyListeners() when the pull completes still fires, so the final state cannot be stale. Scope of what this accounts for, stated honestly: measured synchronous frame work was ~2s per report window and the pull showed ~4s total, against a ~30s observed freeze. This removes the largest measured contributor. If the stall persists, the remainder is outside the frame handlers and the cumulative counters will show frame time has dropped, which narrows it further. flutter analyze clean, dart format clean, 494 tests pass. --- lib/connector/meshcore_connector.dart | 31 ++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index af5b8a3..fdcfc98 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -5101,6 +5101,28 @@ class MeshCoreConnector extends ChangeNotifier { return physicsMax; } + /// Coalesces notifications during a bulk contact pull. + /// + /// Outside a pull this notifies immediately, preserving live-update + /// behaviour for adverts arriving one at a time. + DateTime? _lastContactPullNotify; + static const Duration _contactPullNotifyInterval = Duration( + milliseconds: 250, + ); + + void _notifyContactPullThrottled() { + if (!_isLoadingContacts) { + notifyListeners(); + return; + } + final now = DateTime.now(); + final last = _lastContactPullNotify; + if (last == null || now.difference(last) >= _contactPullNotifyInterval) { + _lastContactPullNotify = now; + notifyListeners(); + } + } + void _handleContact(Uint8List frame, {bool isContact = true}) { final contactTmp = Contact.fromFrame(frame); if (contactTmp != null) { @@ -5188,7 +5210,14 @@ class MeshCoreConnector extends ChangeNotifier { _pathHistoryService!.handlePathUpdated(contact); } - notifyListeners(); + // During a bulk pull this fired once per contact, and each notification + // is a synchronous full-tree rebuild that itself walks the contact list. + // Measured at ~19ms per contact, which never tripped a per-call slow + // threshold but dominated total isolate time. The channel handler + // already guards its notify with _isLoadingChannels; this is the same + // guard, throttled rather than suppressed so the sync progress bar still + // advances while the pull runs. + _notifyContactPullThrottled(); // Show notification for new contact (advertisement) if (isNewContact && _appSettingsService != null) { From 19858698ee127b3f2d9fae5a7ca573986d200091 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 14:29:06 -0400 Subject: [PATCH 29/60] feat(#335): add drift and prove it opens on the targets buildable here Step 1 of #335, deliberately before any data migration: prove the store works before trusting it with message history. Adds drift + drift_flutter, a key/value StoredBlobs table mapping 1:1 onto the existing SharedPreferences keys, and verifyReadWrite() as a runtime probe. Key/value rather than relational tables on purpose: it maps directly onto the current store interfaces, so callers do not change and the migration can be verified row-for-row against the old keys. Relational schemas come later, once this is proven and querying is actually wanted. Verified here: - 3 tests pass, including a 6 MiB round-trip - larger than the 5 MiB localStorage cap that makes the web build impossible today - flutter analyze clean - Windows release build succeeds - Web release build compiles Two findings from the gate that a plan-on-paper would have missed: 1. sqlite3_flutter_libs 0.6.0+eol is a DEPRECATED no-op. From sqlite3 v3.x it is unnecessary and drift_flutter already covers it, so it is not a direct dependency here. 2. The web build COMPILES BUT WOULD FAIL AT RUNTIME: drift needs sqlite3.wasm and drift_worker.dart.js shipped in web/, and neither is present. Per drift docs they are downloaded from GitHub releases, version-matched to pubspec.lock (drift 2.34.2, sqlite3 3.5.0), and the server must serve .wasm as Content-Type: application/wasm. Not done here - downloading files needs explicit human approval. Also noted: the drift_dev CLI does not compile at these versions (allSchemaEntities missing from the drift3_preview GeneratedDatabase), so its asset tooling is unavailable. build_runner code generation is unaffected. No user data is touched. No store is switched over. The migration is the next step and remains gated on the web assets question. --- lib/storage/drift/offband_database.dart | 76 +++++++++++++++++++++++++ pubspec.yaml | 6 +- test/storage/offband_database_test.dart | 56 ++++++++++++++++++ 3 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 lib/storage/drift/offband_database.dart create mode 100644 test/storage/offband_database_test.dart diff --git a/lib/storage/drift/offband_database.dart b/lib/storage/drift/offband_database.dart new file mode 100644 index 0000000..c065662 --- /dev/null +++ b/lib/storage/drift/offband_database.dart @@ -0,0 +1,76 @@ +import 'package:drift/drift.dart'; +import 'package:drift_flutter/drift_flutter.dart'; + +part 'offband_database.g.dart'; + +/// Bulk data store (#335). Replaces SharedPreferences for message history, +/// contacts and discovered contacts. +/// +/// SharedPreferences is a settings store, and using it for bulk data is what +/// causes #306: on Windows every mutation re-encodes and rewrites the entire +/// file synchronously, and on web it is backed by `localStorage`, which is +/// capped at 5 MiB per origin and is also synchronous. This install already +/// holds ~7 MB, so the web build cannot function at all today. +/// +/// drift is used because it is the only option verified to cover all six +/// targets: native SQLite on Android, iOS, macOS, Windows and Linux, and +/// SQLite compiled to WebAssembly on web, where it stores through OPFS or +/// IndexedDB and runs in a worker rather than on the UI thread. +/// +/// Settings stay in SharedPreferences. Only bulk data moves here. +@DriftDatabase(tables: [StoredBlobs]) +class OffbandDatabase extends _$OffbandDatabase { + OffbandDatabase([QueryExecutor? executor]) + : super(executor ?? _defaultExecutor()); + + /// `drift_flutter` selects the platform backend: native SQLite on desktop + /// and mobile, WASM on web. `web:` names the assets that must be shipped for + /// the web build (`sqlite3.wasm`, `drift_worker.dart.js`). + static QueryExecutor _defaultExecutor() { + return driftDatabase( + name: 'offband_store', + web: DriftWebOptions( + sqlite3Wasm: Uri.parse('sqlite3.wasm'), + driftWorker: Uri.parse('drift_worker.dart.js'), + ), + ); + } + + @override + int get schemaVersion => 1; + + /// Step-1 gate for #335: proves the database opens, writes and reads back on + /// the current platform. Deliberately trivial - no user data is involved, so + /// this can be run on every target before any migration is written. + Future verifyReadWrite() async { + const probeKey = '__offband_probe__'; + final probeValue = 'ok-${DateTime.now().microsecondsSinceEpoch}'; + + await into(storedBlobs).insertOnConflictUpdate( + StoredBlobsCompanion.insert(key: probeKey, value: probeValue), + ); + + final row = await (select( + storedBlobs, + )..where((t) => t.key.equals(probeKey))).getSingleOrNull(); + + await (delete(storedBlobs)..where((t) => t.key.equals(probeKey))).go(); + + return row?.value == probeValue; + } +} + +/// One row per stored blob, replacing one SharedPreferences key each. +/// +/// Deliberately key/value for the first migration: it maps 1:1 onto the +/// existing store interfaces, so callers do not change and the migration is +/// verifiable row-for-row against the old keys. Relational tables for messages +/// and contacts are a later step, once this is proven and querying is wanted. +class StoredBlobs extends Table { + TextColumn get key => text()(); + TextColumn get value => text()(); + DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); + + @override + Set get primaryKey => {key}; +} diff --git a/pubspec.yaml b/pubspec.yaml index 0415a36..90f04ac 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -62,7 +62,7 @@ dependencies: url_launcher: ^6.3.0 # Launch URLs in system browser flutter_linkify: ^6.0.0 # Auto-detect and linkify URLs in text gpx: ^2.3.0 - path_provider: ^2.1.5 + path_provider: ^2.1.6 share_plus: ^12.0.1 build_pipe: ^0.3.1 material_symbols_icons: ^4.2906.0 @@ -73,6 +73,8 @@ dependencies: ml_dataframe: ^1.0.0 llamadart: '>=0.6.8 <0.7.0' flutter_langdetect: ^0.0.1 + drift: ^2.34.2 + drift_flutter: ^0.3.1 hooks: user_defines: @@ -95,6 +97,8 @@ dev_dependencies: # rules and activating additional ones. flutter_lints: ^6.0.0 flutter_launcher_icons: ^0.14.4 + drift_dev: ^2.34.0 + build_runner: ^2.15.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/test/storage/offband_database_test.dart b/test/storage/offband_database_test.dart new file mode 100644 index 0000000..4e750bc --- /dev/null +++ b/test/storage/offband_database_test.dart @@ -0,0 +1,56 @@ +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:meshcore_open/storage/drift/offband_database.dart'; + +/// Step-1 gate for #335: the database must open, write and read back before +/// any user data is migrated into it. +/// +/// This runs against an in-memory SQLite instance so it exercises the drift +/// layer and generated code on any host. It does NOT prove the per-platform +/// backends (native libs on desktop/mobile, WASM on web) — those are proven by +/// building each target and by `verifyReadWrite()` at runtime. +void main() { + late OffbandDatabase db; + + setUp(() => db = OffbandDatabase(NativeDatabase.memory())); + tearDown(() => db.close()); + + test('opens, writes and reads back (the #335 gate)', () async { + expect(await db.verifyReadWrite(), isTrue); + }); + + test('round-trips a payload larger than the 5 MiB localStorage cap', () async { + // The web build currently stores through localStorage, capped at 5 MiB per + // origin, while this install already holds ~7 MB. Proving a >5 MiB value + // survives is the whole point of the migration. + final big = 'x' * (6 * 1024 * 1024); + await db + .into(db.storedBlobs) + .insertOnConflictUpdate( + StoredBlobsCompanion.insert(key: 'big', value: big), + ); + + final row = await (db.select( + db.storedBlobs, + )..where((t) => t.key.equals('big'))).getSingle(); + + expect(row.value.length, big.length); + }); + + test('upsert replaces rather than duplicating a key', () async { + for (final v in ['first', 'second']) { + await db + .into(db.storedBlobs) + .insertOnConflictUpdate( + StoredBlobsCompanion.insert(key: 'k', value: v), + ); + } + + final rows = await (db.select( + db.storedBlobs, + )..where((t) => t.key.equals('k'))).get(); + + expect(rows, hasLength(1)); + expect(rows.single.value, 'second'); + }); +} From 847a28df3e273bb08c2dddf3628468f4face4d9a Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 14:48:36 -0400 Subject: [PATCH 30/60] feat(#335): prove drift/SQLite-WASM works in a real browser Step 1 gate, web half. Building is not evidence, so this was run in a browser. Assets: sqlite3.wasm (748 KB) and drift_worker.js (355 KB), both taken from the SAME drift-2.34.2 release so they are built against each other. Validated before use - the wasm has correct WebAssembly magic (0061 736d) and carries SQLite 3.53.3; the worker is genuine compiled Dart JS. Note the filename: the release ships `drift_worker.js`, not the `drift_worker.dart.js` the docs name. The database URI was corrected to match, which would otherwise have been a silent 404 at runtime. Browser result, served over HTTP with COOP/COEP and Content-Type: application/wasm: Using WasmStorageImplementation.opfsLocks PROBE-OK open+write+read PROBE-OK 6MiB round-trip (localStorage cap is 5MiB) drift selected OPFS, the top storage tier, and round-tripped 6 MiB - data the current localStorage-backed web build physically cannot hold. No exceptions. Adds web/_headers for Cloudflare Pages: forces application/wasm on the wasm asset and sets COOP/COEP for cross-origin isolation. Verified to ship into build/web. Without COOP/COEP drift still works, falling back to IndexedDB, which is slower but still unbounded next to localStorage - a performance tier, not a correctness requirement. web_probe/ is a standalone entrypoint that exercises ONLY the drift stack, so a pass or fail is unambiguous without BLE and the rest of the app in the way. It is not part of the app build. Still no user data touched and no store switched over. The migration is the next step. flutter analyze clean, dart format clean, 497 tests pass. --- lib/storage/drift/offband_database.dart | 6 +- web/_headers | 28 + web/drift_worker.js | 13377 ++++++++++++++++++++++ web/sqlite3.wasm | Bin 0 -> 748424 bytes web_probe/main.dart | 87 + 5 files changed, 13497 insertions(+), 1 deletion(-) create mode 100644 web/_headers create mode 100644 web/drift_worker.js create mode 100644 web/sqlite3.wasm create mode 100644 web_probe/main.dart diff --git a/lib/storage/drift/offband_database.dart b/lib/storage/drift/offband_database.dart index c065662..9ca9eff 100644 --- a/lib/storage/drift/offband_database.dart +++ b/lib/storage/drift/offband_database.dart @@ -31,7 +31,11 @@ class OffbandDatabase extends _$OffbandDatabase { name: 'offband_store', web: DriftWebOptions( sqlite3Wasm: Uri.parse('sqlite3.wasm'), - driftWorker: Uri.parse('drift_worker.dart.js'), + // Filename matches the asset published by the drift release, which is + // `drift_worker.js` — not the `drift_worker.dart.js` the older docs + // name. Both assets are taken from the SAME drift release so they are + // built against each other. + driftWorker: Uri.parse('drift_worker.js'), ), ); } diff --git a/web/_headers b/web/_headers new file mode 100644 index 0000000..e4f84b9 --- /dev/null +++ b/web/_headers @@ -0,0 +1,28 @@ +# Cloudflare Pages response headers (#335, web storage via drift/SQLite-WASM). +# +# Syntax: a URL pattern, then indented headers. Cloudflare Pages reads this +# file from the build output and can add, override or remove response headers. +# +# Two things drift needs on the web: +# +# 1. sqlite3.wasm MUST be served as Content-Type: application/wasm, or +# WebAssembly streaming compilation refuses it. +# +# 2. COOP + COEP (cross-origin isolation) unlock drift's best storage backend, +# OPFS (`opfsLocks`). Verified locally: with these headers drift logs +# "Using WasmStorageImplementation.opfsLocks". WITHOUT them it still works, +# falling back to IndexedDB, which is slower but still unbounded compared to +# localStorage's 5 MiB cap. So these are a performance tier, not a +# correctness requirement. +# +# Verify after the first deploy: +# curl -sI https:///sqlite3.wasm | grep -i 'content-type' +# -> expect: content-type: application/wasm +# and check the browser console for the opfsLocks line above. + +/sqlite3.wasm + Content-Type: application/wasm + +/* + Cross-Origin-Opener-Policy: same-origin + Cross-Origin-Embedder-Policy: require-corp diff --git a/web/drift_worker.js b/web/drift_worker.js new file mode 100644 index 0000000..a1978e2 --- /dev/null +++ b/web/drift_worker.js @@ -0,0 +1,13377 @@ +(function dartProgram(){function copyProperties(a,b){var s=Object.keys(a) +for(var r=0;r=0)return true +if(typeof version=="function"&&version.length==0){var q=version() +if(/^\d+\.\d+\.\d+\.\d+$/.test(q))return true}}catch(p){}return false}() +function inherit(a,b){a.prototype.constructor=a +a.prototype["$i"+a.name]=a +if(b!=null){if(z){Object.setPrototypeOf(a.prototype,b.prototype) +return}var s=Object.create(b.prototype) +copyProperties(a.prototype,s) +a.prototype=s}}function inheritMany(a,b){for(var s=0;s4294967295)throw A.b(A.W(a,0,4294967295,"length",null)) +return J.ue(new Array(a),b)}, +pR(a,b){if(a<0)throw A.b(A.J("Length must be a non-negative integer: "+a,null)) +return A.f(new Array(a),b.h("u<0>"))}, +ue(a,b){var s=A.f(a,b.h("u<0>")) +s.$flags=1 +return s}, +uf(a,b){return J.tE(a,b)}, +pS(a){if(a<256)switch(a){case 9:case 10:case 11:case 12:case 13:case 32:case 133:case 160:return!0 +default:return!1}switch(a){case 5760:case 8192:case 8193:case 8194:case 8195:case 8196:case 8197:case 8198:case 8199:case 8200:case 8201:case 8202:case 8232:case 8233:case 8239:case 8287:case 12288:case 65279:return!0 +default:return!1}}, +ug(a,b){var s,r +for(s=a.length;b0;b=s){s=b-1 +r=a.charCodeAt(s) +if(r!==32&&r!==13&&!J.pS(r))break}return b}, +cX(a){if(typeof a=="number"){if(Math.floor(a)==a)return J.es.prototype +return J.hl.prototype}if(typeof a=="string")return J.bX.prototype +if(a==null)return J.et.prototype +if(typeof a=="boolean")return J.hk.prototype +if(Array.isArray(a))return J.u.prototype +if(typeof a!="object"){if(typeof a=="function")return J.bz.prototype +if(typeof a=="symbol")return J.d9.prototype +if(typeof a=="bigint")return J.aN.prototype +return a}if(a instanceof A.e)return a +return J.nQ(a)}, +a3(a){if(typeof a=="string")return J.bX.prototype +if(a==null)return a +if(Array.isArray(a))return J.u.prototype +if(typeof a!="object"){if(typeof a=="function")return J.bz.prototype +if(typeof a=="symbol")return J.d9.prototype +if(typeof a=="bigint")return J.aN.prototype +return a}if(a instanceof A.e)return a +return J.nQ(a)}, +aT(a){if(a==null)return a +if(Array.isArray(a))return J.u.prototype +if(typeof a!="object"){if(typeof a=="function")return J.bz.prototype +if(typeof a=="symbol")return J.d9.prototype +if(typeof a=="bigint")return J.aN.prototype +return a}if(a instanceof A.e)return a +return J.nQ(a)}, +xg(a){if(typeof a=="number")return J.d8.prototype +if(typeof a=="string")return J.bX.prototype +if(a==null)return a +if(!(a instanceof A.e))return J.cF.prototype +return a}, +nP(a){if(typeof a=="string")return J.bX.prototype +if(a==null)return a +if(!(a instanceof A.e))return J.cF.prototype +return a}, +rB(a){if(a==null)return a +if(typeof a!="object"){if(typeof a=="function")return J.bz.prototype +if(typeof a=="symbol")return J.d9.prototype +if(typeof a=="bigint")return J.aN.prototype +return a}if(a instanceof A.e)return a +return J.nQ(a)}, +am(a,b){if(a==null)return b==null +if(typeof a!="object")return b!=null&&a===b +return J.cX(a).T(a,b)}, +aM(a,b){if(typeof b==="number")if(Array.isArray(a)||typeof a=="string"||A.rF(a,a[v.dispatchPropertyName]))if(b>>>0===b&&b>>0===b&&b").G(c).h("f1<1,2>")) +return new A.cp(a,b.h("@<0>").G(c).h("cp<1,2>"))}, +pT(a){return new A.da("Field '"+a+"' has been assigned during initialization.")}, +pU(a){return new A.da("Field '"+a+"' has not been initialized.")}, +ui(a){return new A.da("Field '"+a+"' has already been initialized.")}, +nR(a){var s,r=a^48 +if(r<=9)return r +s=a|32 +if(97<=s&&s<=102)return s-87 +return-1}, +c9(a,b){a=a+b&536870911 +a=a+((a&524287)<<10)&536870911 +return a^a>>>6}, +oy(a){a=a+((a&67108863)<<3)&536870911 +a^=a>>>11 +return a+((a&16383)<<15)&536870911}, +cV(a,b,c){return a}, +p9(a){var s,r +for(s=$.cU.length,r=0;rc)A.E(A.W(b,0,c,"start",null))}return new A.cD(a,b,c,d.h("cD<0>"))}, +ht(a,b,c,d){if(t.Q.b(a))return new A.cu(a,b,c.h("@<0>").G(d).h("cu<1,2>")) +return new A.aG(a,b,c.h("@<0>").G(d).h("aG<1,2>"))}, +oz(a,b,c){var s="takeCount" +A.bT(b,s) +A.ab(b,s) +if(t.Q.b(a))return new A.ek(a,b,c.h("ek<0>")) +return new A.cE(a,b,c.h("cE<0>"))}, +qe(a,b,c){var s="count" +if(t.Q.b(a)){A.bT(b,s) +A.ab(b,s) +return new A.d5(a,b,c.h("d5<0>"))}A.bT(b,s) +A.ab(b,s) +return new A.bJ(a,b,c.h("bJ<0>"))}, +uc(a,b,c){return new A.ct(a,b,c.h("ct<0>"))}, +aw(){return new A.aI("No element")}, +pP(){return new A.aI("Too few elements")}, +ce:function ce(){}, +fU:function fU(a,b){this.a=a +this.$ti=b}, +cp:function cp(a,b){this.a=a +this.$ti=b}, +f1:function f1(a,b){this.a=a +this.$ti=b}, +eW:function eW(){}, +ai:function ai(a,b){this.a=a +this.$ti=b}, +da:function da(a){this.a=a}, +fV:function fV(a){this.a=a}, +nY:function nY(){}, +kN:function kN(){}, +q:function q(){}, +Q:function Q(){}, +cD:function cD(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.$ti=d}, +b4:function b4(a,b,c){var _=this +_.a=a +_.b=b +_.c=0 +_.d=null +_.$ti=c}, +aG:function aG(a,b,c){this.a=a +this.b=b +this.$ti=c}, +cu:function cu(a,b,c){this.a=a +this.b=b +this.$ti=c}, +dc:function dc(a,b,c){var _=this +_.a=null +_.b=a +_.c=b +_.$ti=c}, +D:function D(a,b,c){this.a=a +this.b=b +this.$ti=c}, +aK:function aK(a,b,c){this.a=a +this.b=b +this.$ti=c}, +cG:function cG(a,b){this.a=a +this.b=b}, +em:function em(a,b,c){this.a=a +this.b=b +this.$ti=c}, +ha:function ha(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.d=null +_.$ti=d}, +cE:function cE(a,b,c){this.a=a +this.b=b +this.$ti=c}, +ek:function ek(a,b,c){this.a=a +this.b=b +this.$ti=c}, +hR:function hR(a,b,c){this.a=a +this.b=b +this.$ti=c}, +bJ:function bJ(a,b,c){this.a=a +this.b=b +this.$ti=c}, +d5:function d5(a,b,c){this.a=a +this.b=b +this.$ti=c}, +hM:function hM(a,b){this.a=a +this.b=b}, +eI:function eI(a,b,c){this.a=a +this.b=b +this.$ti=c}, +hN:function hN(a,b){this.a=a +this.b=b +this.c=!1}, +cv:function cv(a){this.$ti=a}, +h7:function h7(){}, +eR:function eR(a,b){this.a=a +this.$ti=b}, +i8:function i8(a,b){this.a=a +this.$ti=b}, +by:function by(a,b,c){this.a=a +this.b=b +this.$ti=c}, +ct:function ct(a,b,c){this.a=a +this.b=b +this.$ti=c}, +eq:function eq(a,b){this.a=a +this.b=b +this.c=-1}, +en:function en(){}, +hV:function hV(){}, +du:function du(){}, +eG:function eG(a,b){this.a=a +this.$ti=b}, +hQ:function hQ(a){this.a=a}, +fA:function fA(){}, +rO(a){var s=v.mangledGlobalNames[a] +if(s!=null)return s +return"minified:"+a}, +rF(a,b){var s +if(b!=null){s=b.x +if(s!=null)return s}return t.aU.b(a)}, +t(a){var s +if(typeof a=="string")return a +if(typeof a=="number"){if(a!==0)return""+a}else if(!0===a)return"true" +else if(!1===a)return"false" +else if(a==null)return"null" +s=J.b1(a) +return s}, +eE(a){var s,r=$.pZ +if(r==null)r=$.pZ=Symbol("identityHashCode") +s=a[r] +if(s==null){s=Math.random()*0x3fffffff|0 +a[r]=s}return s}, +q5(a,b){var s,r,q,p,o,n=null,m=/^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i.exec(a) +if(m==null)return n +s=m[3] +if(b==null){if(s!=null)return parseInt(a,10) +if(m[2]!=null)return parseInt(a,16) +return n}if(b<2||b>36)throw A.b(A.W(b,2,36,"radix",n)) +if(b===10&&s!=null)return parseInt(a,10) +if(b<10||s==null){r=b<=10?47+b:86+b +q=m[1] +for(p=q.length,o=0;or)return n}return parseInt(a,b)}, +hH(a){var s,r,q,p +if(a instanceof A.e)return A.aZ(A.aU(a),null) +s=J.cX(a) +if(s===B.as||s===B.av||t.ak.b(a)){r=B.H(a) +if(r!=="Object"&&r!=="")return r +q=a.constructor +if(typeof q=="function"){p=q.name +if(typeof p=="string"&&p!=="Object"&&p!=="")return p}}return A.aZ(A.aU(a),null)}, +q6(a){var s,r,q +if(a==null||typeof a=="number"||A.bQ(a))return J.b1(a) +if(typeof a=="string")return JSON.stringify(a) +if(a instanceof A.cq)return a.i(0) +if(a instanceof A.fi)return a.fP(!0) +s=$.tr() +for(r=0;r<1;++r){q=s[r].ll(a) +if(q!=null)return q}return"Instance of '"+A.hH(a)+"'"}, +us(){if(!!self.location)return self.location.href +return null}, +pY(a){var s,r,q,p,o=a.length +if(o<=500)return String.fromCharCode.apply(null,a) +for(s="",r=0;r65535)return A.uw(a)}return A.pY(a)}, +ux(a,b,c){var s,r,q,p +if(c<=500&&b===0&&c===a.length)return String.fromCharCode.apply(null,a) +for(s=b,r="";s>>0,s&1023|56320)}}throw A.b(A.W(a,0,1114111,null,null))}, +aH(a){if(a.date===void 0)a.date=new Date(a.a) +return a.date}, +q4(a){return a.c?A.aH(a).getUTCFullYear()+0:A.aH(a).getFullYear()+0}, +q2(a){return a.c?A.aH(a).getUTCMonth()+1:A.aH(a).getMonth()+1}, +q_(a){return a.c?A.aH(a).getUTCDate()+0:A.aH(a).getDate()+0}, +q0(a){return a.c?A.aH(a).getUTCHours()+0:A.aH(a).getHours()+0}, +q1(a){return a.c?A.aH(a).getUTCMinutes()+0:A.aH(a).getMinutes()+0}, +q3(a){return a.c?A.aH(a).getUTCSeconds()+0:A.aH(a).getSeconds()+0}, +uu(a){return a.c?A.aH(a).getUTCMilliseconds()+0:A.aH(a).getMilliseconds()+0}, +uv(a){return B.b.ab((a.c?A.aH(a).getUTCDay()+0:A.aH(a).getDay()+0)+6,7)+1}, +ut(a){var s=a.$thrownJsError +if(s==null)return null +return A.a5(s)}, +eF(a,b){var s +if(a.$thrownJsError==null){s=new Error() +A.aa(a,s) +a.$thrownJsError=s +s.stack=b.i(0)}}, +iX(a,b){var s,r="index" +if(!A.bv(b))return new A.bc(!0,b,r,null) +s=J.aC(a) +if(b<0||b>=s)return A.hf(b,s,a,null,r) +return A.kJ(b,r)}, +xa(a,b,c){if(a>c)return A.W(a,0,c,"start",null) +if(b!=null)if(bc)return A.W(b,a,c,"end",null) +return new A.bc(!0,b,"end",null)}, +e2(a){return new A.bc(!0,a,null,null)}, +b(a){return A.aa(a,new Error())}, +aa(a,b){var s +if(a==null)a=new A.bL() +b.dartException=a +s=A.xO +if("defineProperty" in Object){Object.defineProperty(b,"message",{get:s}) +b.name=""}else b.toString=s +return b}, +xO(){return J.b1(this.dartException)}, +E(a,b){throw A.aa(a,b==null?new Error():b)}, +y(a,b,c){var s +if(b==null)b=0 +if(c==null)c=0 +s=Error() +A.E(A.vY(a,b,c),s)}, +vY(a,b,c){var s,r,q,p,o,n,m,l,k +if(typeof b=="string")s=b +else{r="[]=;add;removeWhere;retainWhere;removeRange;setRange;setInt8;setInt16;setInt32;setUint8;setUint16;setUint32;setFloat32;setFloat64".split(";") +q=r.length +p=b +if(p>q){c=p/q|0 +p%=q}s=r[p]}o=typeof c=="string"?c:"modify;remove from;add to".split(";")[c] +n=t.j.b(a)?"list":"ByteData" +m=a.$flags|0 +l="a " +if((m&4)!==0)k="constant " +else if((m&2)!==0){k="unmodifiable " +l="an "}else k=(m&1)!==0?"fixed-length ":"" +return new A.eP("'"+s+"': Cannot "+o+" "+l+k+n)}, +P(a){throw A.b(A.an(a))}, +bM(a){var s,r,q,p,o,n +a=A.rM(a.replace(String({}),"$receiver$")) +s=a.match(/\\\$[a-zA-Z]+\\\$/g) +if(s==null)s=A.f([],t.s) +r=s.indexOf("\\$arguments\\$") +q=s.indexOf("\\$argumentsExpr\\$") +p=s.indexOf("\\$expr\\$") +o=s.indexOf("\\$method\\$") +n=s.indexOf("\\$receiver\\$") +return new A.lt(a.replace(new RegExp("\\\\\\$arguments\\\\\\$","g"),"((?:x|[^x])*)").replace(new RegExp("\\\\\\$argumentsExpr\\\\\\$","g"),"((?:x|[^x])*)").replace(new RegExp("\\\\\\$expr\\\\\\$","g"),"((?:x|[^x])*)").replace(new RegExp("\\\\\\$method\\\\\\$","g"),"((?:x|[^x])*)").replace(new RegExp("\\\\\\$receiver\\\\\\$","g"),"((?:x|[^x])*)"),r,q,p,o,n)}, +lu(a){return function($expr$){var $argumentsExpr$="$arguments$" +try{$expr$.$method$($argumentsExpr$)}catch(s){return s.message}}(a)}, +qn(a){return function($expr$){try{$expr$.$method$}catch(s){return s.message}}(a)}, +oo(a,b){var s=b==null,r=s?null:b.method +return new A.hn(a,r,s?null:b.receiver)}, +H(a){if(a==null)return new A.hD(a) +if(a instanceof A.el)return A.cl(a,a.a) +if(typeof a!=="object")return a +if("dartException" in a)return A.cl(a,a.dartException) +return A.wH(a)}, +cl(a,b){if(t.C.b(b))if(b.$thrownJsError==null)b.$thrownJsError=a +return b}, +wH(a){var s,r,q,p,o,n,m,l,k,j,i,h,g +if(!("message" in a))return a +s=a.message +if("number" in a&&typeof a.number=="number"){r=a.number +q=r&65535 +if((B.b.M(r,16)&8191)===10)switch(q){case 438:return A.cl(a,A.oo(A.t(s)+" (Error "+q+")",null)) +case 445:case 5007:A.t(s) +return A.cl(a,new A.eA())}}if(a instanceof TypeError){p=$.rX() +o=$.rY() +n=$.rZ() +m=$.t_() +l=$.t2() +k=$.t3() +j=$.t1() +$.t0() +i=$.t5() +h=$.t4() +g=p.aw(s) +if(g!=null)return A.cl(a,A.oo(s,g)) +else{g=o.aw(s) +if(g!=null){g.method="call" +return A.cl(a,A.oo(s,g))}else if(n.aw(s)!=null||m.aw(s)!=null||l.aw(s)!=null||k.aw(s)!=null||j.aw(s)!=null||m.aw(s)!=null||i.aw(s)!=null||h.aw(s)!=null)return A.cl(a,new A.eA())}return A.cl(a,new A.hU(typeof s=="string"?s:""))}if(a instanceof RangeError){if(typeof s=="string"&&s.indexOf("call stack")!==-1)return new A.eK() +s=function(b){try{return String(b)}catch(f){}return null}(a) +return A.cl(a,new A.bc(!1,null,null,typeof s=="string"?s.replace(/^RangeError:\s*/,""):s))}if(typeof InternalError=="function"&&a instanceof InternalError)if(typeof s=="string"&&s==="too much recursion")return new A.eK() +return a}, +a5(a){var s +if(a instanceof A.el)return a.b +if(a==null)return new A.fm(a) +s=a.$cachedTrace +if(s!=null)return s +s=new A.fm(a) +if(typeof a==="object")a.$cachedTrace=s +return s}, +pb(a){if(a==null)return J.aE(a) +if(typeof a=="object")return A.eE(a) +return J.aE(a)}, +xc(a,b){var s,r,q,p=a.length +for(s=0;s=0 +else if(b instanceof A.cx){s=B.a.L(a,c) +return b.b.test(s)}else return!J.o9(b,B.a.L(a,c)).gB(0)}, +p6(a){if(a.indexOf("$",0)>=0)return a.replace(/\$/g,"$$$$") +return a}, +xK(a,b,c,d){var s=b.ff(a,d) +if(s==null)return a +return A.ph(a,s.b.index,s.gbz(),c)}, +rM(a){if(/[[\]{}()*+?.\\^$|]/.test(a))return a.replace(/[[\]{}()*+?.\\^$|]/g,"\\$&") +return a}, +bk(a,b,c){var s +if(typeof b=="string")return A.xJ(a,b,c) +if(b instanceof A.cx){s=b.gfq() +s.lastIndex=0 +return a.replace(s,A.p6(c))}return A.xI(a,b,c)}, +xI(a,b,c){var s,r,q,p +for(s=J.o9(b,a),s=s.gq(s),r=0,q="";s.k();){p=s.gm() +q=q+a.substring(r,p.gcv())+c +r=p.gbz()}s=q+a.substring(r) +return s.charCodeAt(0)==0?s:s}, +xJ(a,b,c){var s,r,q +if(b===""){if(a==="")return c +s=a.length +for(r=c,q=0;q=0)return a.split(b).join(c) +return a.replace(new RegExp(A.rM(b),"g"),A.p6(c))}, +xL(a,b,c,d){var s,r,q,p +if(typeof b=="string"){s=a.indexOf(b,d) +if(s<0)return a +return A.ph(a,s,s+b.length,c)}if(b instanceof A.cx)return d===0?a.replace(b.b,A.p6(c)):A.xK(a,b,c,d) +r=J.tC(b,a,d) +q=r.gq(r) +if(!q.k())return a +p=q.gm() +return B.a.aO(a,p.gcv(),p.gbz(),c)}, +ph(a,b,c,d){return a.substring(0,b)+d+a.substring(c)}, +ag:function ag(a,b){this.a=a +this.b=b}, +cR:function cR(a,b){this.a=a +this.b=b}, +iE:function iE(a,b){this.a=a +this.b=b}, +ef:function ef(){}, +eg:function eg(a,b,c){this.a=a +this.b=b +this.$ti=c}, +cP:function cP(a,b){this.a=a +this.$ti=b}, +ix:function ix(a,b,c){var _=this +_.a=a +_.b=b +_.c=0 +_.d=null +_.$ti=c}, +kl:function kl(){}, +er:function er(a,b){this.a=a +this.$ti=b}, +eH:function eH(){}, +lt:function lt(a,b,c,d,e,f){var _=this +_.a=a +_.b=b +_.c=c +_.d=d +_.e=e +_.f=f}, +eA:function eA(){}, +hn:function hn(a,b,c){this.a=a +this.b=b +this.c=c}, +hU:function hU(a){this.a=a}, +hD:function hD(a){this.a=a}, +el:function el(a,b){this.a=a +this.b=b}, +fm:function fm(a){this.a=a +this.b=null}, +cq:function cq(){}, +jg:function jg(){}, +jh:function jh(){}, +lj:function lj(){}, +l9:function l9(){}, +eb:function eb(a,b){this.a=a +this.b=b}, +hK:function hK(a){this.a=a}, +bA:function bA(a){var _=this +_.a=0 +_.f=_.e=_.d=_.c=_.b=null +_.r=0 +_.$ti=a}, +ks:function ks(a){this.a=a}, +kv:function kv(a,b){var _=this +_.a=a +_.b=b +_.d=_.c=null}, +bB:function bB(a,b){this.a=a +this.$ti=b}, +hr:function hr(a,b,c){var _=this +_.a=a +_.b=b +_.c=c +_.d=null}, +ew:function ew(a,b){this.a=a +this.$ti=b}, +db:function db(a,b,c){var _=this +_.a=a +_.b=b +_.c=c +_.d=null}, +ev:function ev(a,b){this.a=a +this.$ti=b}, +hq:function hq(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.d=null +_.$ti=d}, +nS:function nS(a){this.a=a}, +nT:function nT(a){this.a=a}, +nU:function nU(a){this.a=a}, +fi:function fi(){}, +iD:function iD(){}, +cx:function cx(a,b){var _=this +_.a=a +_.b=b +_.e=_.d=_.c=null}, +dJ:function dJ(a){this.b=a}, +i9:function i9(a,b,c){this.a=a +this.b=b +this.c=c}, +m5:function m5(a,b,c){var _=this +_.a=a +_.b=b +_.c=c +_.d=null}, +ds:function ds(a,b){this.a=a +this.c=b}, +iM:function iM(a,b,c){this.a=a +this.b=b +this.c=c}, +ne:function ne(a,b,c){var _=this +_.a=a +_.b=b +_.c=c +_.d=null}, +xN(a){throw A.aa(A.pT(a),new Error())}, +x(){throw A.aa(A.pU(""),new Error())}, +iZ(){throw A.aa(A.ui(""),new Error())}, +pj(){throw A.aa(A.pT(""),new Error())}, +mm(a){var s=new A.ml(a) +return s.b=s}, +ml:function ml(a){this.a=a +this.b=null}, +vW(a){return a}, +fB(a,b,c){}, +fC(a){var s,r,q +if(t.aP.b(a))return a +s=J.a3(a) +r=A.b5(s.gl(a),null,!1,t.z) +for(q=0;q>>0!==a||a>=c)throw A.b(A.iX(b,a))}, +ci(a,b,c){var s +if(!(a>>>0!==a))s=b>>>0!==b||a>b||b>c +else s=!0 +if(s)throw A.b(A.xa(a,b,c)) +return b}, +de:function de(){}, +dd:function dd(){}, +ey:function ey(){}, +iS:function iS(a){this.a=a}, +cz:function cz(){}, +dg:function dg(){}, +c_:function c_(){}, +aX:function aX(){}, +hu:function hu(){}, +hv:function hv(){}, +hw:function hw(){}, +df:function df(){}, +hx:function hx(){}, +hy:function hy(){}, +hz:function hz(){}, +ez:function ez(){}, +c0:function c0(){}, +fd:function fd(){}, +fe:function fe(){}, +ff:function ff(){}, +fg:function fg(){}, +ou(a,b){var s=b.c +return s==null?b.c=A.fs(a,"C",[b.x]):s}, +qc(a){var s=a.w +if(s===6||s===7)return A.qc(a.x) +return s===11||s===12}, +uB(a){return a.as}, +aB(a){return A.nl(v.typeUniverse,a,!1)}, +xn(a,b){var s,r,q,p,o +if(a==null)return null +s=b.y +r=a.Q +if(r==null)r=a.Q=new Map() +q=b.as +p=r.get(q) +if(p!=null)return p +o=A.cj(v.typeUniverse,a.x,s,0) +r.set(q,o) +return o}, +cj(a1,a2,a3,a4){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0=a2.w +switch(a0){case 5:case 1:case 2:case 3:case 4:return a2 +case 6:s=a2.x +r=A.cj(a1,s,a3,a4) +if(r===s)return a2 +return A.qP(a1,r,!0) +case 7:s=a2.x +r=A.cj(a1,s,a3,a4) +if(r===s)return a2 +return A.qO(a1,r,!0) +case 8:q=a2.y +p=A.e_(a1,q,a3,a4) +if(p===q)return a2 +return A.fs(a1,a2.x,p) +case 9:o=a2.x +n=A.cj(a1,o,a3,a4) +m=a2.y +l=A.e_(a1,m,a3,a4) +if(n===o&&l===m)return a2 +return A.oO(a1,n,l) +case 10:k=a2.x +j=a2.y +i=A.e_(a1,j,a3,a4) +if(i===j)return a2 +return A.qQ(a1,k,i) +case 11:h=a2.x +g=A.cj(a1,h,a3,a4) +f=a2.y +e=A.wE(a1,f,a3,a4) +if(g===h&&e===f)return a2 +return A.qN(a1,g,e) +case 12:d=a2.y +a4+=d.length +c=A.e_(a1,d,a3,a4) +o=a2.x +n=A.cj(a1,o,a3,a4) +if(c===d&&n===o)return a2 +return A.oP(a1,n,c,!0) +case 13:b=a2.x +if(b") +for(r=1;r=0)p+=" "+r[q];++q}return p+"})"}, +re(a1,a2,a3){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a=", ",a0=null +if(a3!=null){s=a3.length +if(a2==null)a2=A.f([],t.s) +else a0=a2.length +r=a2.length +for(q=s;q>0;--q)a2.push("T"+(r+q)) +for(p=t.X,o="<",n="",q=0;q0){c+=b+"[" +for(b="",q=0;q0){c+=b+"{" +for(b="",q=0;q "+d}, +aZ(a,b){var s,r,q,p,o,n,m=a.w +if(m===5)return"erased" +if(m===2)return"dynamic" +if(m===3)return"void" +if(m===1)return"Never" +if(m===4)return"any" +if(m===6){s=a.x +r=A.aZ(s,b) +q=s.w +return(q===11||q===12?"("+r+")":r)+"?"}if(m===7)return"FutureOr<"+A.aZ(a.x,b)+">" +if(m===8){p=A.wG(a.x) +o=a.y +return o.length>0?p+("<"+A.rp(o,b)+">"):p}if(m===10)return A.wq(a,b) +if(m===11)return A.re(a,b,null) +if(m===12)return A.re(a.x,b,a.y) +if(m===13){n=a.x +return b[b.length-1-n]}return"?"}, +wG(a){var s=v.mangledGlobalNames[a] +if(s!=null)return s +return"minified:"+a}, +vr(a,b){var s=a.tR[b] +while(typeof s=="string")s=a.tR[s] +return s}, +vq(a,b){var s,r,q,p,o,n=a.eT,m=n[b] +if(m==null)return A.nl(a,b,!1) +else if(typeof m=="number"){s=m +r=A.ft(a,5,"#") +q=A.nt(s) +for(p=0;p0)p+="<"+A.fr(c)+">" +s=a.eC.get(p) +if(s!=null)return s +r=new A.be(null,null) +r.w=8 +r.x=b +r.y=c +if(c.length>0)r.c=c[0] +r.as=p +q=A.ch(a,r) +a.eC.set(p,q) +return q}, +oO(a,b,c){var s,r,q,p,o,n +if(b.w===9){s=b.x +r=b.y.concat(c)}else{r=c +s=b}q=s.as+(";<"+A.fr(r)+">") +p=a.eC.get(q) +if(p!=null)return p +o=new A.be(null,null) +o.w=9 +o.x=s +o.y=r +o.as=q +n=A.ch(a,o) +a.eC.set(q,n) +return n}, +qQ(a,b,c){var s,r,q="+"+(b+"("+A.fr(c)+")"),p=a.eC.get(q) +if(p!=null)return p +s=new A.be(null,null) +s.w=10 +s.x=b +s.y=c +s.as=q +r=A.ch(a,s) +a.eC.set(q,r) +return r}, +qN(a,b,c){var s,r,q,p,o,n=b.as,m=c.a,l=m.length,k=c.b,j=k.length,i=c.c,h=i.length,g="("+A.fr(m) +if(j>0){s=l>0?",":"" +g+=s+"["+A.fr(k)+"]"}if(h>0){s=l>0?",":"" +g+=s+"{"+A.vj(i)+"}"}r=n+(g+")") +q=a.eC.get(r) +if(q!=null)return q +p=new A.be(null,null) +p.w=11 +p.x=b +p.y=c +p.as=r +o=A.ch(a,p) +a.eC.set(r,o) +return o}, +oP(a,b,c,d){var s,r=b.as+("<"+A.fr(c)+">"),q=a.eC.get(r) +if(q!=null)return q +s=A.vl(a,b,c,r,d) +a.eC.set(r,s) +return s}, +vl(a,b,c,d,e){var s,r,q,p,o,n,m,l +if(e){s=c.length +r=A.nt(s) +for(q=0,p=0;p0){n=A.cj(a,b,r,0) +m=A.e_(a,c,r,0) +return A.oP(a,n,m,c!==m)}}l=new A.be(null,null) +l.w=12 +l.x=b +l.y=c +l.as=d +return A.ch(a,l)}, +qH(a,b,c,d){return{u:a,e:b,r:c,s:[],p:0,n:d}}, +qJ(a){var s,r,q,p,o,n,m,l=a.r,k=a.s +for(s=l.length,r=0;r=48&&q<=57)r=A.vb(r+1,q,l,k) +else if((((q|32)>>>0)-97&65535)<26||q===95||q===36||q===124)r=A.qI(a,r,l,k,!1) +else if(q===46)r=A.qI(a,r,l,k,!0) +else{++r +switch(q){case 44:break +case 58:k.push(!1) +break +case 33:k.push(!0) +break +case 59:k.push(A.cQ(a.u,a.e,k.pop())) +break +case 94:k.push(A.vn(a.u,k.pop())) +break +case 35:k.push(A.ft(a.u,5,"#")) +break +case 64:k.push(A.ft(a.u,2,"@")) +break +case 126:k.push(A.ft(a.u,3,"~")) +break +case 60:k.push(a.p) +a.p=k.length +break +case 62:A.vd(a,k) +break +case 38:A.vc(a,k) +break +case 63:p=a.u +k.push(A.qP(p,A.cQ(p,a.e,k.pop()),a.n)) +break +case 47:p=a.u +k.push(A.qO(p,A.cQ(p,a.e,k.pop()),a.n)) +break +case 40:k.push(-3) +k.push(a.p) +a.p=k.length +break +case 41:A.va(a,k) +break +case 91:k.push(a.p) +a.p=k.length +break +case 93:o=k.splice(a.p) +A.qK(a.u,a.e,o) +a.p=k.pop() +k.push(o) +k.push(-1) +break +case 123:k.push(a.p) +a.p=k.length +break +case 125:o=k.splice(a.p) +A.vf(a.u,a.e,o) +a.p=k.pop() +k.push(o) +k.push(-2) +break +case 43:n=l.indexOf("(",r) +k.push(l.substring(r,n)) +k.push(-4) +k.push(a.p) +a.p=k.length +r=n+1 +break +default:throw"Bad character "+q}}}m=k.pop() +return A.cQ(a.u,a.e,m)}, +vb(a,b,c,d){var s,r,q=b-48 +for(s=c.length;a=48&&r<=57))break +q=q*10+(r-48)}d.push(q) +return a}, +qI(a,b,c,d,e){var s,r,q,p,o,n,m=b+1 +for(s=c.length;m>>0)-97&65535)<26||r===95||r===36||r===124))q=r>=48&&r<=57 +else q=!0 +if(!q)break}}p=c.substring(b,m) +if(e){s=a.u +o=a.e +if(o.w===9)o=o.x +n=A.vr(s,o.x)[p] +if(n==null)A.E('No "'+p+'" in "'+A.uB(o)+'"') +d.push(A.fu(s,o,n))}else d.push(p) +return m}, +vd(a,b){var s,r=a.u,q=A.qG(a,b),p=b.pop() +if(typeof p=="string")b.push(A.fs(r,p,q)) +else{s=A.cQ(r,a.e,p) +switch(s.w){case 11:b.push(A.oP(r,s,q,a.n)) +break +default:b.push(A.oO(r,s,q)) +break}}}, +va(a,b){var s,r,q,p=a.u,o=b.pop(),n=null,m=null +if(typeof o=="number")switch(o){case-1:n=b.pop() +break +case-2:m=b.pop() +break +default:b.push(o) +break}else b.push(o) +s=A.qG(a,b) +o=b.pop() +switch(o){case-3:o=b.pop() +if(n==null)n=p.sEA +if(m==null)m=p.sEA +r=A.cQ(p,a.e,o) +q=new A.ir() +q.a=s +q.b=n +q.c=m +b.push(A.qN(p,r,q)) +return +case-4:b.push(A.qQ(p,b.pop(),s)) +return +default:throw A.b(A.e8("Unexpected state under `()`: "+A.t(o)))}}, +vc(a,b){var s=b.pop() +if(0===s){b.push(A.ft(a.u,1,"0&")) +return}if(1===s){b.push(A.ft(a.u,4,"1&")) +return}throw A.b(A.e8("Unexpected extended operation "+A.t(s)))}, +qG(a,b){var s=b.splice(a.p) +A.qK(a.u,a.e,s) +a.p=b.pop() +return s}, +cQ(a,b,c){if(typeof c=="string")return A.fs(a,c,a.sEA) +else if(typeof c=="number"){b.toString +return A.ve(a,b,c)}else return c}, +qK(a,b,c){var s,r=c.length +for(s=0;sn)return!1 +m=n-o +l=s.b +k=r.b +j=l.length +i=k.length +if(o+j=d)return!1 +a1=f[b] +b+=3 +if(a00?new Array(q):v.typeUniverse.sEA +for(o=0;o0?new Array(a):v.typeUniverse.sEA}, +be:function be(a,b){var _=this +_.a=a +_.b=b +_.r=_.f=_.d=_.c=null +_.w=0 +_.as=_.Q=_.z=_.y=_.x=null}, +ir:function ir(){this.c=this.b=this.a=null}, +nk:function nk(a){this.a=a}, +im:function im(){}, +fq:function fq(a){this.a=a}, +uW(){var s,r,q +if(self.scheduleImmediate!=null)return A.wK() +if(self.MutationObserver!=null&&self.document!=null){s={} +r=self.document.createElement("div") +q=self.document.createElement("span") +s.a=null +new self.MutationObserver(A.ck(new A.m7(s),1)).observe(r,{childList:true}) +return new A.m6(s,r,q)}else if(self.setImmediate!=null)return A.wL() +return A.wM()}, +uX(a){self.scheduleImmediate(A.ck(new A.m8(a),0))}, +uY(a){self.setImmediate(A.ck(new A.m9(a),0))}, +uZ(a){A.oA(B.J,a)}, +oA(a,b){var s=B.b.I(a.a,1000) +return A.vh(s<0?0:s,b)}, +vh(a,b){var s=new A.iP() +s.i2(a,b) +return s}, +vi(a,b){var s=new A.iP() +s.i3(a,b) +return s}, +k(a){return new A.ia(new A.n($.m,a.h("n<0>")),a.h("ia<0>"))}, +j(a,b){a.$2(0,null) +b.b=!0 +return b.a}, +c(a,b){A.vM(a,b)}, +i(a,b){b.O(a)}, +h(a,b){b.by(A.H(a),A.a5(a))}, +vM(a,b){var s,r,q=new A.nu(b),p=new A.nv(b) +if(a instanceof A.n)a.fN(q,p,t.z) +else{s=t.z +if(a instanceof A.n)a.bf(q,p,s) +else{r=new A.n($.m,t.eI) +r.a=8 +r.c=a +r.fN(q,p,s)}}}, +l(a){var s=function(b,c){return function(d,e){while(true){try{b(d,e) +break}catch(r){e=r +d=c}}}}(a,1) +return $.m.dc(new A.nJ(s),t.H,t.S,t.z)}, +qM(a,b,c){return 0}, +fP(a){var s +if(t.C.b(a)){s=a.gaP() +if(s!=null)return s}return B.t}, +oi(a,b){var s,r,q,p,o,n,m,l=null +try{l=a.$0()}catch(q){s=A.H(q) +r=A.a5(q) +p=new A.n($.m,b.h("n<0>")) +o=s +n=r +m=A.dY(o,n) +if(m==null)o=new A.U(o,n==null?A.fP(o):n) +else o=m +p.aR(o) +return p}return b.h("C<0>").b(l)?l:A.cL(l,b)}, +b3(a,b){var s=a==null?b.a(a):a,r=new A.n($.m,b.h("n<0>")) +r.b3(s) +return r}, +pK(a,b){var s +if(!b.b(null))throw A.b(A.ad(null,"computation","The type parameter is not nullable")) +s=new A.n($.m,b.h("n<0>")) +A.uH(a,new A.kc(null,s,b)) +return s}, +pL(a,b){var s,r,q,p,o,n,m,l,k,j,i={},h=null,g=!1,f=new A.n($.m,b.h("n>")) +i.a=null +i.b=0 +i.c=i.d=null +s=new A.ke(i,h,g,f) +try{for(n=J.Z(a),m=t.P;n.k();){r=n.gm() +q=i.b +r.bf(new A.kd(i,q,f,b,h,g),s,m);++i.b}n=i.b +if(n===0){n=f +n.bL(A.f([],b.h("u<0>"))) +return n}i.a=A.b5(n,null,!1,b.h("0?"))}catch(l){p=A.H(l) +o=A.a5(l) +if(i.b===0||g){n=f +m=p +k=o +j=A.dY(m,k) +if(j==null)m=new A.U(m,k==null?A.fP(m):k) +else m=j +n.aR(m) +return n}else{i.d=p +i.c=o}}return f}, +ua(a,b){var s,r,q,p=A.f([],b.h("u>")) +for(s=a.length,r=b.h("f7<0>"),q=0;q")),b.h("o<0>")) +s=new A.n($.m,b.h("n>")) +A.v8(p,new A.kb(new A.a1(s,b.h("a1>")),p,b)) +return s}, +wk(a){return a!=null}, +v8(a,b){var s,r={},q=r.a=r.b=0,p=new A.mC(r,a,b) +for(s=a.length;q")) +s.a=8 +s.c=a +return s}, +cL(a,b){var s=new A.n($.m,b.h("n<0>")) +s.a=8 +s.c=a +return s}, +mI(a,b,c){var s,r,q,p={},o=p.a=a +while(s=o.a,(s&4)!==0){o=o.c +p.a=o}if(o===b){s=A.l8() +b.aR(new A.U(new A.bc(!0,o,null,"Cannot complete a future with itself"),s)) +return}r=b.a&1 +s=o.a=s|r +if((s&24)===0){q=b.c +b.a=b.a&1|4 +b.c=o +o.ft(q) +return}if(!c)if(b.c==null)o=(s&16)===0||r!==0 +else o=!1 +else o=!0 +if(o){q=b.bS() +b.cC(p.a) +A.cM(b,q) +return}b.a^=2 +b.b.b1(new A.mJ(p,b))}, +cM(a,b){var s,r,q,p,o,n,m,l,k,j,i,h,g={},f=g.a=a +for(;;){s={} +r=f.a +q=(r&16)===0 +p=!q +if(b==null){if(p&&(r&1)===0){r=f.c +f.b.c7(r.a,r.b)}return}s.a=b +o=b.a +for(f=b;o!=null;f=o,o=n){f.a=null +A.cM(g.a,f) +s.a=o +n=o.a}r=g.a +m=r.c +s.b=p +s.c=m +if(q){l=f.c +l=(l&1)!==0||(l&15)===8}else l=!0 +if(l){k=f.b.b +if(p){f=r.b +f=!(f===k||f.gaK()===k.gaK())}else f=!1 +if(f){f=g.a +r=f.c +f.b.c7(r.a,r.b) +return}j=$.m +if(j!==k)$.m=k +else j=null +f=s.a.c +if((f&15)===8)new A.mN(s,g,p).$0() +else if(q){if((f&1)!==0)new A.mM(s,m).$0()}else if((f&2)!==0)new A.mL(g,s).$0() +if(j!=null)$.m=j +f=s.c +if(f instanceof A.n){r=s.a.$ti +r=r.h("C<2>").b(f)||!r.y[1].b(f)}else r=!1 +if(r){i=s.a.b +if((f.a&24)!==0){h=i.c +i.c=null +b=i.cJ(h) +i.a=f.a&30|i.a&1 +i.c=f.c +g.a=f +continue}else A.mI(f,i,!0) +return}}i=s.a.b +h=i.c +i.c=null +b=i.cJ(h) +f=s.b +r=s.c +if(!f){i.a=8 +i.c=r}else{i.a=i.a&1|16 +i.c=r}g.a=i +f=i}}, +ws(a,b){if(t._.b(a))return b.dc(a,t.z,t.K,t.l) +if(t.bI.b(a))return b.bb(a,t.z,t.K) +throw A.b(A.ad(a,"onError",u.c))}, +wj(){var s,r +for(s=$.dZ;s!=null;s=$.dZ){$.fE=null +r=s.b +$.dZ=r +if(r==null)$.fD=null +s.a.$0()}}, +wD(){$.oZ=!0 +try{A.wj()}finally{$.fE=null +$.oZ=!1 +if($.dZ!=null)$.pm().$1(A.rx())}}, +rr(a){var s=new A.ib(a),r=$.fD +if(r==null){$.dZ=$.fD=s +if(!$.oZ)$.pm().$1(A.rx())}else $.fD=r.b=s}, +wA(a){var s,r,q,p=$.dZ +if(p==null){A.rr(a) +$.fE=$.fD +return}s=new A.ib(a) +r=$.fE +if(r==null){s.b=p +$.dZ=$.fE=s}else{q=r.b +s.b=q +$.fE=r.b=s +if(q==null)$.fD=s}}, +pe(a){var s,r=null,q=$.m +if(B.d===q){A.nG(r,r,B.d,a) +return}if(B.d===q.ge3().a)s=B.d.gaK()===q.gaK() +else s=!1 +if(s){A.nG(r,r,q,q.az(a,t.H)) +return}s=$.m +s.b1(s.c2(a))}, +y5(a){return new A.dO(A.cV(a,"stream",t.K))}, +eN(a,b,c,d){var s=null +return c?new A.dS(b,s,s,a,d.h("dS<0>")):new A.dA(b,s,s,a,d.h("dA<0>"))}, +iV(a){var s,r,q +if(a==null)return +try{a.$0()}catch(q){s=A.H(q) +r=A.a5(q) +$.m.c7(s,r)}}, +v6(a,b,c,d,e,f){var s=$.m,r=e?1:0,q=c!=null?32:0,p=A.ih(s,b,f),o=A.ii(s,c),n=d==null?A.rw():d +return new A.cf(a,p,o,s.az(n,t.H),s,r|q,f.h("cf<0>"))}, +ih(a,b,c){var s=b==null?A.wO():b +return a.bb(s,t.H,c)}, +ii(a,b){if(b==null)b=A.wP() +if(t.da.b(b))return a.dc(b,t.z,t.K,t.l) +if(t.d5.b(b))return a.bb(b,t.z,t.K) +throw A.b(A.J("handleError callback must take either an Object (the error), or both an Object (the error) and a StackTrace.",null))}, +wl(a){}, +wn(a,b){$.m.c7(a,b)}, +wm(){}, +wy(a,b,c){var s,r,q,p +try{b.$1(a.$0())}catch(p){s=A.H(p) +r=A.a5(p) +q=A.dY(s,r) +if(q!=null)c.$2(q.a,q.b) +else c.$2(s,r)}}, +vT(a,b,c){var s=a.J() +if(s!==$.cm())s.ai(new A.nx(b,c)) +else b.V(c)}, +vU(a,b){return new A.nw(a,b)}, +r7(a,b,c){var s=a.J() +if(s!==$.cm())s.ai(new A.ny(b,c)) +else b.b4(c)}, +vg(a,b,c){return new A.dM(new A.nd(null,null,a,c,b),b.h("@<0>").G(c).h("dM<1,2>"))}, +uH(a,b){var s=$.m +if(s===B.d)return s.ei(a,b) +return s.ei(a,s.c2(b))}, +rN(a,b,c,d){return A.wz(a,c,b,d)}, +wz(a,b,c,d){return $.m.h8(c,b).bd(a,d)}, +ww(a,b,c,d,e){A.fF(d,e)}, +fF(a,b){A.wA(new A.nC(a,b))}, +nD(a,b,c,d){var s,r=$.m +if(r===c)return d.$0() +$.m=c +s=r +try{r=d.$0() +return r}finally{$.m=s}}, +nF(a,b,c,d,e){var s,r=$.m +if(r===c)return d.$1(e) +$.m=c +s=r +try{r=d.$1(e) +return r}finally{$.m=s}}, +nE(a,b,c,d,e,f){var s,r=$.m +if(r===c)return d.$2(e,f) +$.m=c +s=r +try{r=d.$2(e,f) +return r}finally{$.m=s}}, +rn(a,b,c,d){return d}, +ro(a,b,c,d){return d}, +rm(a,b,c,d){return d}, +wv(a,b,c,d,e){return null}, +nG(a,b,c,d){var s,r +if(B.d!==c){s=B.d.gaK() +r=c.gaK() +d=s!==r?c.c2(d):c.ef(d,t.H)}A.rr(d)}, +wu(a,b,c,d,e){return A.oA(d,B.d!==c?c.ef(e,t.H):e)}, +wt(a,b,c,d,e){var s +if(B.d!==c)e=c.fX(e,t.H,t.aF) +s=B.b.I(d.a,1000) +return A.vi(s<0?0:s,e)}, +wx(a,b,c,d){A.pd(d)}, +wp(a){$.m.hj(a)}, +rl(a,b,c,d,e){var s,r,q,p +$.rk=A.wQ() +if(d==null)d=B.bu +if(e==null)s=c.gfn() +else{r=t.X +s=A.ub(e,r,r)}r=new A.ij(c.gfF(),c.gfH(),c.gfG(),c.gfB(),c.gfC(),c.gfA(),c.gfe(),c.ge3(),c.gf9(),c.gf8(),c.gfu(),c.gfh(),c.gdW(),c,s) +q=d.x +if(q!=null)r.w=new A.av(r,q) +p=d.a +if(p!=null)r.as=new A.av(r,p) +return r}, +m7:function m7(a){this.a=a}, +m6:function m6(a,b,c){this.a=a +this.b=b +this.c=c}, +m8:function m8(a){this.a=a}, +m9:function m9(a){this.a=a}, +iP:function iP(){this.c=0}, +nj:function nj(a,b){this.a=a +this.b=b}, +ni:function ni(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.d=d}, +ia:function ia(a,b){this.a=a +this.b=!1 +this.$ti=b}, +nu:function nu(a){this.a=a}, +nv:function nv(a){this.a=a}, +nJ:function nJ(a){this.a=a}, +iN:function iN(a){var _=this +_.a=a +_.e=_.d=_.c=_.b=null}, +dR:function dR(a,b){this.a=a +this.$ti=b}, +U:function U(a,b){this.a=a +this.b=b}, +eV:function eV(a,b){this.a=a +this.$ti=b}, +cJ:function cJ(a,b,c,d,e,f,g){var _=this +_.ay=0 +_.CW=_.ch=null +_.w=a +_.a=b +_.b=c +_.c=d +_.d=e +_.e=f +_.r=_.f=null +_.$ti=g}, +cI:function cI(){}, +fp:function fp(a,b,c){var _=this +_.a=a +_.b=b +_.c=0 +_.r=_.f=_.e=_.d=null +_.$ti=c}, +nf:function nf(a,b){this.a=a +this.b=b}, +nh:function nh(a,b,c){this.a=a +this.b=b +this.c=c}, +ng:function ng(a){this.a=a}, +kc:function kc(a,b,c){this.a=a +this.b=b +this.c=c}, +ke:function ke(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.d=d}, +kd:function kd(a,b,c,d,e,f){var _=this +_.a=a +_.b=b +_.c=c +_.d=d +_.e=e +_.f=f}, +kb:function kb(a,b,c){this.a=a +this.b=b +this.c=c}, +eD:function eD(a,b){this.c=a +this.d=b}, +f7:function f7(a,b){var _=this +_.a=a +_.c=_.b=null +_.$ti=b}, +mD:function mD(a,b){this.a=a +this.b=b}, +mE:function mE(a,b){this.a=a +this.b=b}, +mC:function mC(a,b,c){this.a=a +this.b=b +this.c=c}, +dB:function dB(){}, +a7:function a7(a,b){this.a=a +this.$ti=b}, +a1:function a1(a,b){this.a=a +this.$ti=b}, +cg:function cg(a,b,c,d,e){var _=this +_.a=null +_.b=a +_.c=b +_.d=c +_.e=d +_.$ti=e}, +n:function n(a,b){var _=this +_.a=0 +_.b=a +_.c=null +_.$ti=b}, +mF:function mF(a,b){this.a=a +this.b=b}, +mK:function mK(a,b){this.a=a +this.b=b}, +mJ:function mJ(a,b){this.a=a +this.b=b}, +mH:function mH(a,b){this.a=a +this.b=b}, +mG:function mG(a,b){this.a=a +this.b=b}, +mN:function mN(a,b,c){this.a=a +this.b=b +this.c=c}, +mO:function mO(a,b){this.a=a +this.b=b}, +mP:function mP(a){this.a=a}, +mM:function mM(a,b){this.a=a +this.b=b}, +mL:function mL(a,b){this.a=a +this.b=b}, +ib:function ib(a){this.a=a +this.b=null}, +X:function X(){}, +lg:function lg(a,b){this.a=a +this.b=b}, +lh:function lh(a,b){this.a=a +this.b=b}, +le:function le(a){this.a=a}, +lf:function lf(a,b,c){this.a=a +this.b=b +this.c=c}, +lc:function lc(a,b){this.a=a +this.b=b}, +ld:function ld(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.d=d}, +la:function la(a,b){this.a=a +this.b=b}, +lb:function lb(a,b,c){this.a=a +this.b=b +this.c=c}, +hP:function hP(){}, +cS:function cS(){}, +nc:function nc(a){this.a=a}, +nb:function nb(a){this.a=a}, +iO:function iO(){}, +ic:function ic(){}, +dA:function dA(a,b,c,d,e){var _=this +_.a=null +_.b=0 +_.c=null +_.d=a +_.e=b +_.f=c +_.r=d +_.$ti=e}, +dS:function dS(a,b,c,d,e){var _=this +_.a=null +_.b=0 +_.c=null +_.d=a +_.e=b +_.f=c +_.r=d +_.$ti=e}, +at:function at(a,b){this.a=a +this.$ti=b}, +cf:function cf(a,b,c,d,e,f,g){var _=this +_.w=a +_.a=b +_.b=c +_.c=d +_.d=e +_.e=f +_.r=_.f=null +_.$ti=g}, +dP:function dP(a){this.a=a}, +af:function af(){}, +mk:function mk(a,b,c){this.a=a +this.b=b +this.c=c}, +mj:function mj(a){this.a=a}, +dN:function dN(){}, +il:function il(){}, +dD:function dD(a){this.b=a +this.a=null}, +eZ:function eZ(a,b){this.b=a +this.c=b +this.a=null}, +mu:function mu(){}, +fh:function fh(){this.a=0 +this.c=this.b=null}, +n1:function n1(a,b){this.a=a +this.b=b}, +f0:function f0(a){this.a=1 +this.b=a +this.c=null}, +dO:function dO(a){this.a=null +this.b=a +this.c=!1}, +nx:function nx(a,b){this.a=a +this.b=b}, +nw:function nw(a,b){this.a=a +this.b=b}, +ny:function ny(a,b){this.a=a +this.b=b}, +f5:function f5(){}, +dE:function dE(a,b,c,d,e,f,g){var _=this +_.w=a +_.x=null +_.a=b +_.b=c +_.c=d +_.d=e +_.e=f +_.r=_.f=null +_.$ti=g}, +fc:function fc(a,b,c){this.b=a +this.a=b +this.$ti=c}, +f2:function f2(a){this.a=a}, +dL:function dL(a,b,c,d,e,f){var _=this +_.w=$ +_.x=null +_.a=a +_.b=b +_.c=c +_.d=d +_.e=e +_.r=_.f=null +_.$ti=f}, +fo:function fo(){}, +eU:function eU(a,b,c){this.a=a +this.b=b +this.$ti=c}, +dF:function dF(a,b,c,d,e){var _=this +_.a=a +_.b=b +_.c=c +_.d=d +_.$ti=e}, +dM:function dM(a,b){this.a=a +this.$ti=b}, +nd:function nd(a,b,c,d,e){var _=this +_.a=a +_.b=b +_.c=c +_.d=d +_.e=e}, +av:function av(a,b){this.a=a +this.b=b}, +iU:function iU(){}, +ij:function ij(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){var _=this +_.a=a +_.b=b +_.c=c +_.d=d +_.e=e +_.f=f +_.r=g +_.w=h +_.x=i +_.y=j +_.z=k +_.Q=l +_.as=m +_.at=null +_.ax=n +_.ay=o}, +mr:function mr(a,b,c){this.a=a +this.b=b +this.c=c}, +mt:function mt(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.d=d}, +mq:function mq(a,b){this.a=a +this.b=b}, +ms:function ms(a,b,c){this.a=a +this.b=b +this.c=c}, +iI:function iI(){}, +n6:function n6(a,b,c){this.a=a +this.b=b +this.c=c}, +n8:function n8(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.d=d}, +n5:function n5(a,b){this.a=a +this.b=b}, +n7:function n7(a,b,c){this.a=a +this.b=b +this.c=c}, +dV:function dV(a){this.a=a}, +nC:function nC(a,b){this.a=a +this.b=b}, +fz:function fz(a,b,c,d,e,f,g,h,i,j,k,l,m){var _=this +_.a=a +_.b=b +_.c=c +_.d=d +_.e=e +_.f=f +_.r=g +_.w=h +_.x=i +_.y=j +_.z=k +_.Q=l +_.as=m}, +pN(a,b){return new A.cN(a.h("@<0>").G(b).h("cN<1,2>"))}, +qF(a,b){var s=a[b] +return s===a?null:s}, +oM(a,b,c){if(c==null)a[b]=a +else a[b]=c}, +oL(){var s=Object.create(null) +A.oM(s,"",s) +delete s[""] +return s}, +uj(a,b){return new A.bA(a.h("@<0>").G(b).h("bA<1,2>"))}, +uk(a,b,c){return A.xc(a,new A.bA(b.h("@<0>").G(c).h("bA<1,2>")))}, +ao(a,b){return new A.bA(a.h("@<0>").G(b).h("bA<1,2>"))}, +op(a){return new A.fa(a.h("fa<0>"))}, +oN(){var s=Object.create(null) +s[""]=s +delete s[""] +return s}, +iy(a,b,c){var s=new A.dI(a,b,c.h("dI<0>")) +s.c=a.e +return s}, +ub(a,b,c){var s=A.pN(b,c) +a.ar(0,new A.kh(s,b,c)) +return s}, +oq(a){var s,r +if(A.p9(a))return"{...}" +s=new A.aD("") +try{r={} +$.cU.push(a) +s.a+="{" +r.a=!0 +a.ar(0,new A.kA(r,s)) +s.a+="}"}finally{$.cU.pop()}r=s.a +return r.charCodeAt(0)==0?r:r}, +cN:function cN(a){var _=this +_.a=0 +_.e=_.d=_.c=_.b=null +_.$ti=a}, +mQ:function mQ(a){this.a=a}, +dG:function dG(a){var _=this +_.a=0 +_.e=_.d=_.c=_.b=null +_.$ti=a}, +cO:function cO(a,b){this.a=a +this.$ti=b}, +is:function is(a,b,c){var _=this +_.a=a +_.b=b +_.c=0 +_.d=null +_.$ti=c}, +fa:function fa(a){var _=this +_.a=0 +_.f=_.e=_.d=_.c=_.b=null +_.r=0 +_.$ti=a}, +n_:function n_(a){this.a=a +this.c=this.b=null}, +dI:function dI(a,b,c){var _=this +_.a=a +_.b=b +_.d=_.c=null +_.$ti=c}, +kh:function kh(a,b,c){this.a=a +this.b=b +this.c=c}, +cy:function cy(a){var _=this +_.b=_.a=0 +_.c=null +_.$ti=a}, +iz:function iz(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=null +_.d=c +_.e=!1 +_.$ti=d}, +ay:function ay(){}, +w:function w(){}, +S:function S(){}, +kz:function kz(a){this.a=a}, +kA:function kA(a,b){this.a=a +this.b=b}, +fb:function fb(a,b){this.a=a +this.$ti=b}, +iA:function iA(a,b,c){var _=this +_.a=a +_.b=b +_.c=null +_.$ti=c}, +dp:function dp(){}, +fk:function fk(){}, +vE(a,b,c){var s,r,q,p,o=c-b +if(o<=4096)s=$.tg() +else s=new Uint8Array(o) +for(r=J.a3(a),q=0;q=16)return null +r=r*16+o}n=h-1 +i[h]=r +for(;s=16)return null +r=r*16+o}m=n-1 +i[n]=r}if(j===1&&i[0]===0)return $.bb() +l=A.aS(j,i) +return new A.a8(l===0?!1:c,i,l)}, +v5(a,b){var s,r,q,p,o +if(a==="")return null +s=$.t9().a8(a) +if(s==null)return null +r=s.b +q=r[1]==="-" +p=r[4] +o=r[3] +if(p!=null)return A.v2(p,q) +if(o!=null)return A.v3(o,2,q) +return null}, +aS(a,b){for(;;){if(!(a>0&&b[a-1]===0))break;--a}return a}, +oI(a,b,c,d){var s,r=new Uint16Array(d),q=c-b +for(s=0;s>>0)+(o>>>4)-1075 +m=new Uint16Array(4) +m[0]=(r[1]<<8>>>0)+r[0] +m[1]=(r[3]<<8>>>0)+r[2] +m[2]=(r[5]<<8>>>0)+r[4] +m[3]=o&15|16 +l=new A.a8(!1,m,4) +if(n<0)k=l.bl(0,-n) +else k=n>0?l.aE(0,n):l +if(s)return k.aj(0) +return k}, +oJ(a,b,c,d){var s,r,q +if(b===0)return 0 +if(c===0&&d===a)return b +for(s=b-1,r=d.$flags|0;s>=0;--s){q=a[s] +r&2&&A.y(d) +d[s+c]=q}for(s=c-1;s>=0;--s){r&2&&A.y(d) +d[s]=0}return b+c}, +qC(a,b,c,d){var s,r,q,p,o,n=B.b.I(c,16),m=B.b.ab(c,16),l=16-m,k=B.b.aE(1,l)-1 +for(s=b-1,r=d.$flags|0,q=0;s>=0;--s){p=a[s] +o=B.b.bl(p,l) +r&2&&A.y(d) +d[s+n+1]=(o|q)>>>0 +q=B.b.aE((p&k)>>>0,m)}r&2&&A.y(d) +d[n]=q}, +qx(a,b,c,d){var s,r,q,p,o=B.b.I(c,16) +if(B.b.ab(c,16)===0)return A.oJ(a,b,o,d) +s=b+o+1 +A.qC(a,b,c,d) +for(r=d.$flags|0,q=o;--q,q>=0;){r&2&&A.y(d) +d[q]=0}p=s-1 +return d[p]===0?p:s}, +v4(a,b,c,d){var s,r,q,p,o=B.b.I(c,16),n=B.b.ab(c,16),m=16-n,l=B.b.aE(1,n)-1,k=B.b.bl(a[o],n),j=b-o-1 +for(s=d.$flags|0,r=0;r>>0,m) +s&2&&A.y(d) +d[r]=(p|k)>>>0 +k=B.b.bl(q,n)}s&2&&A.y(d) +d[j]=k}, +mg(a,b,c,d){var s,r=b-d +if(r===0)for(s=b-1;s>=0;--s){r=a[s]-c[s] +if(r!==0)return r}return r}, +v0(a,b,c,d,e){var s,r,q +for(s=e.$flags|0,r=0,q=0;q=0;e=o,c=q){q=c+1 +p=a*b[c]+d[e]+r +o=e+1 +s&2&&A.y(d) +d[e]=p&65535 +r=B.b.I(p,65536)}for(;r!==0;e=o){n=d[e]+r +o=e+1 +s&2&&A.y(d) +d[e]=n&65535 +r=B.b.I(n,65536)}}, +v1(a,b,c){var s,r=b[c] +if(r===a)return 65535 +s=B.b.eY((r<<16|b[c-1])>>>0,a) +if(s>65535)return 65535 +return s}, +u1(a){throw A.b(A.ad(a,"object","Expandos are not allowed on strings, numbers, bools, records or null"))}, +mB(a,b){var s=$.tb() +s=s==null?null:new s(A.ck(A.xR(a,b),1)) +return new A.iq(s,b.h("iq<0>"))}, +bj(a,b){var s=A.q5(a,b) +if(s!=null)return s +throw A.b(A.aj(a,null,null))}, +u0(a,b){a=A.aa(a,new Error()) +a.stack=b.i(0) +throw a}, +b5(a,b,c,d){var s,r=c?J.pR(a,d):J.pQ(a,d) +if(a!==0&&b!=null)for(s=0;s")) +for(s=J.Z(a);s.k();)r.push(s.gm()) +r.$flags=1 +return r}, +ak(a,b){var s,r +if(Array.isArray(a))return A.f(a.slice(0),b.h("u<0>")) +s=A.f([],b.h("u<0>")) +for(r=J.Z(a);r.k();)s.push(r.gm()) +return s}, +aO(a,b){var s=A.um(a,!1,b) +s.$flags=3 +return s}, +qh(a,b,c){var s,r,q,p,o +A.ab(b,"start") +s=c==null +r=!s +if(r){q=c-b +if(q<0)throw A.b(A.W(c,b,null,"end",null)) +if(q===0)return""}if(Array.isArray(a)){p=a +o=p.length +if(s)c=o +return A.q7(b>0||c0)a=J.e7(a,b) +s=A.ak(a,t.S) +return A.q7(s)}, +qg(a){return A.aR(a)}, +uF(a,b,c){var s=a.length +if(b>=s)return"" +return A.ux(a,b,c==null||c>s?s:c)}, +G(a,b,c,d,e){return new A.cx(a,A.om(a,d,b,e,c,""))}, +ox(a,b,c){var s=J.Z(b) +if(!s.k())return a +if(c.length===0){do a+=A.t(s.gm()) +while(s.k())}else{a+=A.t(s.gm()) +while(s.k())a=a+c+A.t(s.gm())}return a}, +i_(){var s,r,q=A.us() +if(q==null)throw A.b(A.a4("'Uri.base' is not supported")) +s=$.qs +if(s!=null&&q===$.qr)return s +r=A.bu(q) +$.qs=r +$.qr=q +return r}, +vC(a,b,c,d){var s,r,q,p,o,n="0123456789ABCDEF" +if(c===B.j){s=$.td() +s=s.b.test(b)}else s=!1 +if(s)return b +r=B.i.a4(b) +for(s=r.length,q=0,p="";q>>4&15]+n[o&15]}return p.charCodeAt(0)==0?p:p}, +l8(){return A.a5(new Error())}, +pD(a,b,c){var s="microsecond" +if(b>999)throw A.b(A.W(b,0,999,s,null)) +if(a<-864e13||a>864e13)throw A.b(A.W(a,-864e13,864e13,"millisecondsSinceEpoch",null)) +if(a===864e13&&b!==0)throw A.b(A.ad(b,s,"Time including microseconds is outside valid range")) +A.cV(c,"isUtc",t.y) +return a}, +tX(a){var s=Math.abs(a),r=a<0?"-":"" +if(s>=1000)return""+a +if(s>=100)return r+"0"+s +if(s>=10)return r+"00"+s +return r+"000"+s}, +pC(a){if(a>=100)return""+a +if(a>=10)return"0"+a +return"00"+a}, +h0(a){if(a>=10)return""+a +return"0"+a}, +pE(a,b){return new A.bx(a+1000*b)}, +oe(a,b){var s,r +for(s=0;s<5;++s){r=a[s] +if(r.b===b)return r}throw A.b(A.ad(b,"name","No enum value with that name"))}, +u_(a,b){var s,r,q=A.ao(t.N,b) +for(s=0;s<2;++s){r=a[s] +q.t(0,r.b,r)}return q}, +h9(a){if(typeof a=="number"||A.bQ(a)||a==null)return J.b1(a) +if(typeof a=="string")return JSON.stringify(a) +return A.q6(a)}, +pH(a,b){A.cV(a,"error",t.K) +A.cV(b,"stackTrace",t.l) +A.u0(a,b)}, +e8(a){return new A.fO(a)}, +J(a,b){return new A.bc(!1,null,b,a)}, +ad(a,b,c){return new A.bc(!0,a,b,c)}, +bT(a,b){return a}, +kJ(a,b){return new A.dk(null,null,!0,a,b,"Value not in range")}, +W(a,b,c,d,e){return new A.dk(b,c,!0,a,d,"Invalid value")}, +qa(a,b,c,d){if(ac)throw A.b(A.W(a,b,c,d,null)) +return a}, +uz(a,b,c,d){if(0>a||a>=d)A.E(A.hf(a,d,b,null,c)) +return a}, +bd(a,b,c){if(0>a||a>c)throw A.b(A.W(a,0,c,"start",null)) +if(b!=null){if(a>b||b>c)throw A.b(A.W(b,a,c,"end",null)) +return b}return c}, +ab(a,b){if(a<0)throw A.b(A.W(a,0,null,b,null)) +return a}, +pO(a,b){var s=b.b +return new A.ep(s,!0,a,null,"Index out of range")}, +hf(a,b,c,d,e){return new A.ep(b,!0,a,e,"Index out of range")}, +a4(a){return new A.eP(a)}, +qo(a){return new A.hT(a)}, +A(a){return new A.aI(a)}, +an(a){return new A.fW(a)}, +k2(a){return new A.ip(a)}, +aj(a,b,c){return new A.aF(a,b,c)}, +ud(a,b,c){var s,r +if(A.p9(a)){if(b==="("&&c===")")return"(...)" +return b+"..."+c}s=A.f([],t.s) +$.cU.push(a) +try{A.wi(a,s)}finally{$.cU.pop()}r=A.ox(b,s,", ")+c +return r.charCodeAt(0)==0?r:r}, +ol(a,b,c){var s,r +if(A.p9(a))return b+"..."+c +s=new A.aD(b) +$.cU.push(a) +try{r=s +r.a=A.ox(r.a,a,", ")}finally{$.cU.pop()}s.a+=c +r=s.a +return r.charCodeAt(0)==0?r:r}, +wi(a,b){var s,r,q,p,o,n,m,l=a.gq(a),k=0,j=0 +for(;;){if(!(k<80||j<3))break +if(!l.k())return +s=A.t(l.gm()) +b.push(s) +k+=s.length+2;++j}if(!l.k()){if(j<=5)return +r=b.pop() +q=b.pop()}else{p=l.gm();++j +if(!l.k()){if(j<=4){b.push(A.t(p)) +return}r=A.t(p) +q=b.pop() +k+=r.length+2}else{o=l.gm();++j +for(;l.k();p=o,o=n){n=l.gm();++j +if(j>100){for(;;){if(!(k>75&&j>3))break +k-=b.pop().length+2;--j}b.push("...") +return}}q=A.t(p) +r=A.t(o) +k+=r.length+q.length+4}}if(j>b.length+2){k+=5 +m="..."}else m=null +for(;;){if(!(k>80&&b.length>3))break +k-=b.pop().length+2 +if(m==null){k+=5 +m="..."}}if(m!=null)b.push(m) +b.push(q) +b.push(r)}, +eB(a,b,c,d){var s +if(B.f===c){s=J.aE(a) +b=J.aE(b) +return A.oy(A.c9(A.c9($.o7(),s),b))}if(B.f===d){s=J.aE(a) +b=J.aE(b) +c=J.aE(c) +return A.oy(A.c9(A.c9(A.c9($.o7(),s),b),c))}s=J.aE(a) +b=J.aE(b) +c=J.aE(c) +d=J.aE(d) +d=A.oy(A.c9(A.c9(A.c9(A.c9($.o7(),s),b),c),d)) +return d}, +xC(a){var s=A.t(a),r=$.rk +if(r==null)A.pd(s) +else r.$1(s)}, +qq(a){var s,r=null,q=new A.aD(""),p=A.f([-1],t.t) +A.uP(r,r,r,q,p) +p.push(q.a.length) +q.a+="," +A.uO(256,B.ad.kB(a),q) +s=q.a +return new A.hY(s.charCodeAt(0)==0?s:s,p,r).geN()}, +bu(a5){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1,a2,a3=null,a4=a5.length +if(a4>=5){s=((a5.charCodeAt(4)^58)*3|a5.charCodeAt(0)^100|a5.charCodeAt(1)^97|a5.charCodeAt(2)^116|a5.charCodeAt(3)^97)>>>0 +if(s===0)return A.qp(a4=14)r[7]=a4 +q=r[1] +if(q>=0)if(A.rq(a5,0,q,20,r)===20)r[7]=q +p=r[2]+1 +o=r[3] +n=r[4] +m=r[5] +l=r[6] +if(lq+3)){i=o>0 +if(!(i&&o+1===n)){if(!B.a.C(a5,"\\",n))if(p>0)h=B.a.C(a5,"\\",p-1)||B.a.C(a5,"\\",p-2) +else h=!1 +else h=!0 +if(!h){if(!(mn+2&&B.a.C(a5,"/..",m-3) +else h=!0 +if(!h)if(q===4){if(B.a.C(a5,"file",0)){if(p<=0){if(!B.a.C(a5,"/",n)){g="file:///" +s=3}else{g="file://" +s=2}a5=g+B.a.p(a5,n,a4) +m+=s +l+=s +a4=a5.length +p=7 +o=7 +n=7}else if(n===m){++l +f=m+1 +a5=B.a.aO(a5,n,m,"/");++a4 +m=f}j="file"}else if(B.a.C(a5,"http",0)){if(i&&o+3===n&&B.a.C(a5,"80",o+1)){l-=3 +e=n-3 +m-=3 +a5=B.a.aO(a5,o,n,"") +a4-=3 +n=e}j="http"}}else if(q===5&&B.a.C(a5,"https",0)){if(i&&o+4===n&&B.a.C(a5,"443",o+1)){l-=4 +e=n-4 +m-=4 +a5=B.a.aO(a5,o,n,"") +a4-=3 +n=e}j="https"}k=!h}}}}if(k)return new A.b7(a40)j=A.np(a5,0,q) +else{if(q===0)A.dT(a5,0,"Invalid empty scheme") +j=""}d=a3 +if(p>0){c=q+3 +b=c=c?0:a.charCodeAt(q) +m=n^48 +if(m<=9){if(o!==0||q===r){o=o*10+m +if(o<=255){++q +continue}A.hZ("each part must be in the range 0..255",a,r)}A.hZ("parts must not have leading zeros",a,r)}if(q===r){if(q===c)break +A.hZ(k,a,q)}l=p+1 +s&2&&A.y(d) +d[e+p]=o +if(n===46){if(l<4){++q +p=l +r=q +o=0 +continue}break}if(q===c){if(l===4)return +break}A.hZ(k,a,q) +p=l}A.hZ("IPv4 address should contain exactly 4 parts",a,q)}, +uR(a,b,c){var s +if(b===c)throw A.b(A.aj("Empty IP address",a,b)) +if(a.charCodeAt(b)===118){s=A.uS(a,b,c) +if(s!=null)throw A.b(s) +return!1}A.qt(a,b,c) +return!0}, +uS(a,b,c){var s,r,q,p,o="Missing hex-digit in IPvFuture address";++b +for(s=b;;s=r){if(s=97&&p<=102)continue +if(q===46){if(r-1===b)return new A.aF(o,a,r) +s=r +break}return new A.aF("Unexpected character",a,r-1)}if(s-1===b)return new A.aF(o,a,s) +return new A.aF("Missing '.' in IPvFuture address",a,s)}if(s===c)return new A.aF("Missing address in IPvFuture address, host, cursor",null,null) +for(;;){if((u.v.charCodeAt(a.charCodeAt(s))&16)!==0){++s +if(s=a3?0:a1.charCodeAt(p) +A:{k=l^48 +j=!1 +if(k<=9)i=k +else{h=l|32 +if(h>=97&&h<=102)i=h-87 +else break A +m=j}if(po){if(l===46){if(m){if(q<=6){A.uQ(a1,o,a3,s,q*2) +q+=2 +p=a3 +break}a0.$2(a,o)}break}g=q*2 +s[g]=B.b.M(n,8) +s[g+1]=n&255;++q +if(l===58){if(q<8){++p +o=p +n=0 +m=!0 +continue}a0.$2(a,p)}break}if(l===58){if(r<0){f=q+1;++p +r=q +q=f +o=p +continue}a0.$2("only one wildcard `::` is allowed",p)}if(r!==q-1)a0.$2("missing part",p) +break}if(p0){c=e*2 +b=16-d*2 +B.e.N(s,b,16,s,c) +B.e.em(s,c,b,0)}}return s}, +fw(a,b,c,d,e,f,g){return new A.fv(a,b,c,d,e,f,g)}, +al(a,b,c,d){var s,r,q,p,o,n,m,l,k=null +d=d==null?"":A.np(d,0,d.length) +s=A.r_(k,0,0) +a=A.qX(a,0,a==null?0:a.length,!1) +r=A.qZ(k,0,0,k) +q=A.qW(k,0,0) +p=A.no(k,d) +o=d==="file" +if(a==null)n=s.length!==0||p!=null||o +else n=!1 +if(n)a="" +n=a==null +m=!n +b=A.qY(b,0,b==null?0:b.length,c,d,m) +l=d.length===0 +if(l&&n&&!B.a.u(b,"/"))b=A.oS(b,!l||m) +else b=A.cT(b) +return A.fw(d,s,n&&B.a.u(b,"//")?"":a,p,b,r,q)}, +qT(a){if(a==="http")return 80 +if(a==="https")return 443 +return 0}, +dT(a,b,c){throw A.b(A.aj(c,a,b))}, +qS(a,b){return b?A.vy(a,!1):A.vx(a,!1)}, +vt(a,b){var s,r,q +for(s=a.length,r=0;r")),r=r.h("Q.E");s.k();){q=s.d +if(q==null)q=r.a(q) +if(B.a.H(q,A.G('["*/:<>?\\\\|]',!0,!1,!1,!1)))if(b)throw A.b(A.J("Illegal character in path",null)) +else throw A.b(A.a4("Illegal character in path: "+q))}}, +vu(a,b){var s,r="Illegal drive letter " +if(!(65<=a&&a<=90))s=97<=a&&a<=122 +else s=!0 +if(s)return +if(b)throw A.b(A.J(r+A.qg(a),null)) +else throw A.b(A.a4(r+A.qg(a)))}, +vx(a,b){var s=null,r=A.f(a.split("/"),t.s) +if(B.a.u(a,"/"))return A.al(s,s,r,"file") +else return A.al(s,s,r,s)}, +vy(a,b){var s,r,q,p,o="\\",n=null,m="file" +if(B.a.u(a,"\\\\?\\"))if(B.a.C(a,"UNC\\",4))a=B.a.aO(a,0,7,o) +else{a=B.a.L(a,4) +if(a.length<3||a.charCodeAt(1)!==58||a.charCodeAt(2)!==92)throw A.b(A.ad(a,"path","Windows paths with \\\\?\\ prefix must be absolute"))}else a=A.bk(a,"/",o) +s=a.length +if(s>1&&a.charCodeAt(1)===58){A.vu(a.charCodeAt(0),!0) +if(s===2||a.charCodeAt(2)!==92)throw A.b(A.ad(a,"path","Windows paths with drive letter must be absolute")) +r=A.f(a.split(o),t.s) +A.nm(r,!0,1) +return A.al(n,n,r,m)}if(B.a.u(a,o))if(B.a.C(a,o,1)){q=B.a.aY(a,o,2) +s=q<0 +p=s?B.a.L(a,2):B.a.p(a,2,q) +r=A.f((s?"":B.a.L(a,q+1)).split(o),t.s) +A.nm(r,!0,0) +return A.al(p,n,r,m)}else{r=A.f(a.split(o),t.s) +A.nm(r,!0,0) +return A.al(n,n,r,m)}else{r=A.f(a.split(o),t.s) +A.nm(r,!0,0) +return A.al(n,n,r,n)}}, +no(a,b){if(a!=null&&a===A.qT(b))return null +return a}, +qX(a,b,c,d){var s,r,q,p,o,n,m,l +if(a==null)return null +if(b===c)return"" +if(a.charCodeAt(b)===91){s=c-1 +if(a.charCodeAt(s)!==93)A.dT(a,b,"Missing end `]` to match `[` in host") +r=b+1 +q="" +if(a.charCodeAt(r)!==118){p=A.vv(a,r,s) +if(p=b&&s=b&&s=p){if(i==null)i=new A.aD("") +if(r=o){if(q==null)q=new A.aD("") +if(r")).av(0,"/")}else if(d!=null)throw A.b(A.J("Both path and pathSegments specified",null)) +else s=A.fx(a,b,c,128,!0,!0) +if(s.length===0){if(r)return"/"}else if(q&&!B.a.u(s,"/"))s="/"+s +return A.vz(s,e,f)}, +vz(a,b,c){var s=b.length===0 +if(s&&!c&&!B.a.u(a,"/")&&!B.a.u(a,"\\"))return A.oS(a,!s||c) +return A.cT(a)}, +qZ(a,b,c,d){if(a!=null)return A.fx(a,b,c,256,!0,!1) +return null}, +qW(a,b,c){if(a==null)return null +return A.fx(a,b,c,256,!0,!1)}, +oR(a,b,c){var s,r,q,p,o,n=b+2 +if(n>=a.length)return"%" +s=a.charCodeAt(b+1) +r=a.charCodeAt(n) +q=A.nR(s) +p=A.nR(r) +if(q<0||p<0)return"%" +o=q*16+p +if(o<127&&(u.v.charCodeAt(o)&1)!==0)return A.aR(c&&65<=o&&90>=o?(o|32)>>>0:o) +if(s>=97||r>=97)return B.a.p(a,b,b+3).toUpperCase() +return null}, +oQ(a){var s,r,q,p,o,n="0123456789ABCDEF" +if(a<=127){s=new Uint8Array(3) +s[0]=37 +s[1]=n.charCodeAt(a>>>4) +s[2]=n.charCodeAt(a&15)}else{if(a>2047)if(a>65535){r=240 +q=4}else{r=224 +q=3}else{r=192 +q=2}s=new Uint8Array(3*q) +for(p=0;--q,q>=0;r=128){o=B.b.jt(a,6*q)&63|r +s[p]=37 +s[p+1]=n.charCodeAt(o>>>4) +s[p+2]=n.charCodeAt(o&15) +p+=3}}return A.qh(s,0,null)}, +fx(a,b,c,d,e,f){var s=A.r1(a,b,c,d,e,f) +return s==null?B.a.p(a,b,c):s}, +r1(a,b,c,d,e,f){var s,r,q,p,o,n,m,l,k,j=null,i=u.v +for(s=!e,r=b,q=r,p=j;r=2&&A.qV(a.charCodeAt(0)))for(s=1;s127||(u.v.charCodeAt(r)&8)===0)break}return a}, +vB(a,b){if(a.kL("package")&&a.c==null)return A.rs(b,0,b.length) +return-1}, +vw(a,b){var s,r,q +for(s=0,r=0;r<2;++r){q=a.charCodeAt(b+r) +if(48<=q&&q<=57)s=s*16+q-48 +else{q|=32 +if(97<=q&&q<=102)s=s*16+q-87 +else throw A.b(A.J("Invalid URL encoding",null))}}return s}, +oT(a,b,c,d,e){var s,r,q,p,o=b +for(;;){if(!(o127)throw A.b(A.J("Illegal percent encoding in URI",null)) +if(r===37){if(o+3>q)throw A.b(A.J("Truncated URI",null)) +p.push(A.vw(a,o+1)) +o+=2}else p.push(r)}}return d.cX(p)}, +qV(a){var s=a|32 +return 97<=s&&s<=122}, +uP(a,b,c,d,e){d.a=d.a}, +qp(a,b,c){var s,r,q,p,o,n,m,l,k="Invalid MIME type",j=A.f([b-1],t.t) +for(s=a.length,r=b,q=-1,p=null;rb)throw A.b(A.aj(k,a,r)) +while(p!==44){j.push(r);++r +for(o=-1;r=0)j.push(o) +else{n=B.c.gD(j) +if(p!==44||r!==n+7||!B.a.C(a,"base64",n+1))throw A.b(A.aj("Expecting '='",a,r)) +break}}j.push(r) +m=r+1 +if((j.length&1)===1)a=B.ae.kV(a,m,s) +else{l=A.r1(a,m,s,256,!0,!1) +if(l!=null)a=B.a.aO(a,m,s,l)}return new A.hY(a,j,c)}, +uO(a,b,c){var s,r,q,p,o,n="0123456789ABCDEF" +for(s=b.length,r=0,q=0;q>>4)) +c.a+=o +o=A.aR(n.charCodeAt(p&15)) +c.a+=o}}if((r&4294967040)!==0)for(q=0;q255)throw A.b(A.ad(p,"non-byte value",null))}}, +rq(a,b,c,d,e){var s,r,q +for(s=b;s95)r=31 +q='\xe1\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\xe1\xe1\xe1\x01\xe1\xe1\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\xe1\xe3\xe1\xe1\x01\xe1\x01\xe1\xcd\x01\xe1\x01\x01\x01\x01\x01\x01\x01\x01\x0e\x03\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"\x01\xe1\x01\xe1\xac\xe1\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\xe1\xe1\xe1\x01\xe1\xe1\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\xe1\xea\xe1\xe1\x01\xe1\x01\xe1\xcd\x01\xe1\x01\x01\x01\x01\x01\x01\x01\x01\x01\n\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"\x01\xe1\x01\xe1\xac\xeb\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\xeb\xeb\xeb\x8b\xeb\xeb\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\xeb\x83\xeb\xeb\x8b\xeb\x8b\xeb\xcd\x8b\xeb\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x92\x83\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\xeb\x8b\xeb\x8b\xeb\xac\xeb\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\xeb\xeb\xeb\v\xeb\xeb\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\xebD\xeb\xeb\v\xeb\v\xeb\xcd\v\xeb\v\v\v\v\v\v\v\v\x12D\v\v\v\v\v\v\v\v\v\v\xeb\v\xeb\v\xeb\xac\xe5\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\xe5\xe5\xe5\x05\xe5D\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe8\x8a\xe5\xe5\x05\xe5\x05\xe5\xcd\x05\xe5\x05\x05\x05\x05\x05\x05\x05\x05\x05\x8a\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05f\x05\xe5\x05\xe5\xac\xe5\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\xe5\xe5\xe5\x05\xe5D\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\x8a\xe5\xe5\x05\xe5\x05\xe5\xcd\x05\xe5\x05\x05\x05\x05\x05\x05\x05\x05\x05\x8a\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05f\x05\xe5\x05\xe5\xac\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7D\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\x8a\xe7\xe7\xe7\xe7\xe7\xe7\xcd\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\x8a\xe7\x07\x07\x07\x07\x07\x07\x07\x07\x07\xe7\xe7\xe7\xe7\xe7\xac\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7D\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\x8a\xe7\xe7\xe7\xe7\xe7\xe7\xcd\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\x8a\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\xe7\xe7\xe7\xe7\xe7\xac\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\x05\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\xeb\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\xeb\xeb\xeb\v\xeb\xeb\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\xeb\xea\xeb\xeb\v\xeb\v\xeb\xcd\v\xeb\v\v\v\v\v\v\v\v\x10\xea\v\v\v\v\v\v\v\v\v\v\xeb\v\xeb\v\xeb\xac\xeb\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\xeb\xeb\xeb\v\xeb\xeb\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\xeb\xea\xeb\xeb\v\xeb\v\xeb\xcd\v\xeb\v\v\v\v\v\v\v\v\x12\n\v\v\v\v\v\v\v\v\v\v\xeb\v\xeb\v\xeb\xac\xeb\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\xeb\xeb\xeb\v\xeb\xeb\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\xeb\xea\xeb\xeb\v\xeb\v\xeb\xcd\v\xeb\v\v\v\v\v\v\v\v\v\n\v\v\v\v\v\v\v\v\v\v\xeb\v\xeb\v\xeb\xac\xec\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\xec\xec\xec\f\xec\xec\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\xec\xec\xec\xec\f\xec\f\xec\xcd\f\xec\f\f\f\f\f\f\f\f\f\xec\f\f\f\f\f\f\f\f\f\f\xec\f\xec\f\xec\f\xed\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\xed\xed\xed\r\xed\xed\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\xed\xed\xed\xed\r\xed\r\xed\xed\r\xed\r\r\r\r\r\r\r\r\r\xed\r\r\r\r\r\r\r\r\r\r\xed\r\xed\r\xed\r\xe1\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\xe1\xe1\xe1\x01\xe1\xe1\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\xe1\xea\xe1\xe1\x01\xe1\x01\xe1\xcd\x01\xe1\x01\x01\x01\x01\x01\x01\x01\x01\x0f\xea\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"\x01\xe1\x01\xe1\xac\xe1\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\xe1\xe1\xe1\x01\xe1\xe1\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\xe1\xe9\xe1\xe1\x01\xe1\x01\xe1\xcd\x01\xe1\x01\x01\x01\x01\x01\x01\x01\x01\x01\t\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"\x01\xe1\x01\xe1\xac\xeb\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\xeb\xeb\xeb\v\xeb\xeb\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\xeb\xea\xeb\xeb\v\xeb\v\xeb\xcd\v\xeb\v\v\v\v\v\v\v\v\x11\xea\v\v\v\v\v\v\v\v\v\v\xeb\v\xeb\v\xeb\xac\xeb\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\xeb\xeb\xeb\v\xeb\xeb\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\xeb\xe9\xeb\xeb\v\xeb\v\xeb\xcd\v\xeb\v\v\v\v\v\v\v\v\v\t\v\v\v\v\v\v\v\v\v\v\xeb\v\xeb\v\xeb\xac\xeb\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\xeb\xeb\xeb\v\xeb\xeb\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\xeb\xea\xeb\xeb\v\xeb\v\xeb\xcd\v\xeb\v\v\v\v\v\v\v\v\x13\xea\v\v\v\v\v\v\v\v\v\v\xeb\v\xeb\v\xeb\xac\xeb\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\xeb\xeb\xeb\v\xeb\xeb\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\v\xeb\xea\xeb\xeb\v\xeb\v\xeb\xcd\v\xeb\v\v\v\v\v\v\v\v\v\xea\v\v\v\v\v\v\v\v\v\v\xeb\v\xeb\v\xeb\xac\xf5\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\x15\xf5\x15\x15\xf5\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\xf5\xf5\xf5\xf5\xf5\xf5'.charCodeAt(d*96+r) +d=q&31 +e[q>>>5]=s}return d}, +qL(a){if(a.b===7&&B.a.u(a.a,"package")&&a.c<=0)return A.rs(a.a,a.e,a.f) +return-1}, +rs(a,b,c){var s,r,q +for(s=b,r=0;s=1)return a.$1(b) +return a.$0()}, +vP(a,b,c,d){if(d>=2)return a.$2(b,c) +if(d===1)return a.$1(b) +return a.$0()}, +vQ(a,b,c,d,e){if(e>=3)return a.$3(b,c,d) +if(e===2)return a.$2(b,c) +if(e===1)return a.$1(b) +return a.$0()}, +vR(a,b,c,d,e,f){if(f>=4)return a.$4(b,c,d,e) +if(f===3)return a.$3(b,c,d) +if(f===2)return a.$2(b,c) +if(f===1)return a.$1(b) +return a.$0()}, +vS(a,b,c,d,e,f,g){if(g>=5)return a.$5(b,c,d,e,f) +if(g===4)return a.$4(b,c,d,e) +if(g===3)return a.$3(b,c,d) +if(g===2)return a.$2(b,c) +if(g===1)return a.$1(b) +return a.$0()}, +rj(a){return a==null||A.bQ(a)||typeof a=="number"||typeof a=="string"||t.gj.b(a)||t.E.b(a)||t.go.b(a)||t.dQ.b(a)||t.h7.b(a)||t.an.b(a)||t.bv.b(a)||t.h4.b(a)||t.gN.b(a)||t.w.b(a)||t.fd.b(a)}, +xp(a){if(A.rj(a))return a +return new A.nW(new A.dG(t.hg)).$1(a)}, +p1(a,b,c){return a[b].apply(a,c)}, +e3(a,b){var s,r +if(b==null)return new a() +if(b instanceof Array)switch(b.length){case 0:return new a() +case 1:return new a(b[0]) +case 2:return new a(b[0],b[1]) +case 3:return new a(b[0],b[1],b[2]) +case 4:return new a(b[0],b[1],b[2],b[3])}s=[null] +B.c.aI(s,b) +r=a.bind.apply(a,s) +String(r) +return new r()}, +V(a,b){var s=new A.n($.m,b.h("n<0>")),r=new A.a7(s,b.h("a7<0>")) +a.then(A.ck(new A.o0(r),1),A.ck(new A.o1(r),1)) +return s}, +ri(a){return a==null||typeof a==="boolean"||typeof a==="number"||typeof a==="string"||a instanceof Int8Array||a instanceof Uint8Array||a instanceof Uint8ClampedArray||a instanceof Int16Array||a instanceof Uint16Array||a instanceof Int32Array||a instanceof Uint32Array||a instanceof Float32Array||a instanceof Float64Array||a instanceof ArrayBuffer||a instanceof DataView}, +ry(a){if(A.ri(a))return a +return new A.nM(new A.dG(t.hg)).$1(a)}, +nW:function nW(a){this.a=a}, +o0:function o0(a){this.a=a}, +o1:function o1(a){this.a=a}, +nM:function nM(a){this.a=a}, +rG(a,b){return Math.max(a,b)}, +xG(a){return Math.sqrt(a)}, +xF(a){return Math.sin(a)}, +x7(a){return Math.cos(a)}, +xM(a){return Math.tan(a)}, +wI(a){return Math.acos(a)}, +wJ(a){return Math.asin(a)}, +x3(a){return Math.atan(a)}, +mY:function mY(a){this.a=a}, +d4:function d4(){}, +h1:function h1(){}, +hs:function hs(){}, +hB:function hB(){}, +hW:function hW(){}, +tY(a,b){var s=new A.ej(a,b,A.ao(t.S,t.aR),A.eN(null,null,!0,t.al),new A.a7(new A.n($.m,t.D),t.h)) +s.hW(a,!1,b) +return s}, +ej:function ej(a,b,c,d,e){var _=this +_.a=a +_.c=b +_.d=0 +_.e=c +_.f=d +_.r=!1 +_.w=e}, +jS:function jS(a){this.a=a}, +jT:function jT(a,b){this.a=a +this.b=b}, +iC:function iC(a,b){this.a=a +this.b=b}, +fX:function fX(){}, +h5:function h5(a){this.a=a}, +h4:function h4(){}, +jU:function jU(a){this.a=a}, +jV:function jV(a){this.a=a}, +bZ:function bZ(){}, +ar:function ar(a,b){this.a=a +this.b=b}, +bf:function bf(a,b){this.a=a +this.b=b}, +aQ:function aQ(a){this.a=a}, +bo:function bo(a,b,c){this.a=a +this.b=b +this.c=c}, +bw:function bw(a){this.a=a}, +dh:function dh(a,b){this.a=a +this.b=b}, +cC:function cC(a,b){this.a=a +this.b=b}, +bW:function bW(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.d=d}, +c2:function c2(a){this.a=a}, +bp:function bp(a,b){this.a=a +this.b=b}, +c1:function c1(a,b){this.a=a +this.b=b}, +c4:function c4(a,b){this.a=a +this.b=b}, +bV:function bV(a,b){this.a=a +this.b=b}, +c5:function c5(a){this.a=a}, +c3:function c3(a,b){this.a=a +this.b=b}, +bF:function bF(a){this.a=a}, +bI:function bI(a){this.a=a}, +uC(a,b,c){var s=null,r=t.S,q=A.f([],t.t) +r=new A.kO(a,!1,!0,A.ao(r,t.x),A.ao(r,t.g1),q,new A.fp(s,s,t.dn),A.op(t.gw),new A.a7(new A.n($.m,t.D),t.h),A.eN(s,s,!1,t.bw)) +r.hY(a,!1,!0) +return r}, +kO:function kO(a,b,c,d,e,f,g,h,i,j){var _=this +_.a=a +_.b=b +_.c=c +_.d=d +_.f=_.e=0 +_.r=e +_.w=f +_.x=g +_.y=!1 +_.z=h +_.Q=i +_.as=j}, +kT:function kT(a){this.a=a}, +kU:function kU(a,b){this.a=a +this.b=b}, +kV:function kV(a,b){this.a=a +this.b=b}, +kP:function kP(a,b){this.a=a +this.b=b}, +kQ:function kQ(a,b){this.a=a +this.b=b}, +kS:function kS(a,b){this.a=a +this.b=b}, +kR:function kR(a){this.a=a}, +fj:function fj(a,b,c){this.a=a +this.b=b +this.c=c}, +i7:function i7(a){this.a=a}, +m1:function m1(a,b){this.a=a +this.b=b}, +m2:function m2(a,b){this.a=a +this.b=b}, +m_:function m_(){}, +lW:function lW(a,b){this.a=a +this.b=b}, +lX:function lX(){}, +lY:function lY(){}, +lV:function lV(){}, +m0:function m0(){}, +lZ:function lZ(){}, +dv:function dv(a,b){this.a=a +this.b=b}, +bK:function bK(a,b){this.a=a +this.b=b}, +xD(a,b){var s,r,q={} +q.a=s +q.a=null +s=new A.bU(new A.a1(new A.n($.m,b.h("n<0>")),b.h("a1<0>")),A.f([],t.bT),b.h("bU<0>")) +q.a=s +r=t.X +A.rN(new A.o2(q,a,b),null,A.uk([B.U,s],r,r),t.H) +return q.a}, +p2(){var s=$.m.j(0,B.U) +if(s instanceof A.bU&&s.c)throw A.b(B.E)}, +o2:function o2(a,b,c){this.a=a +this.b=b +this.c=c}, +bU:function bU(a,b,c){var _=this +_.a=a +_.b=b +_.c=!1 +_.$ti=c}, +ec:function ec(){}, +aq:function aq(){}, +ea:function ea(a,b){this.a=a +this.b=b}, +d2:function d2(a,b){this.a=a +this.b=b}, +rb(a){return"SAVEPOINT s"+a}, +r9(a){return"RELEASE s"+a}, +ra(a){return"ROLLBACK TO s"+a}, +jJ:function jJ(){}, +kG:function kG(){}, +ls:function ls(){}, +kB:function kB(){}, +jM:function jM(){}, +hA:function hA(){}, +k0:function k0(){}, +id:function id(){}, +ma:function ma(a,b,c){this.a=a +this.b=b +this.c=c}, +mf:function mf(a,b,c){this.a=a +this.b=b +this.c=c}, +md:function md(a,b,c){this.a=a +this.b=b +this.c=c}, +me:function me(a,b,c){this.a=a +this.b=b +this.c=c}, +mc:function mc(a,b,c){this.a=a +this.b=b +this.c=c}, +mb:function mb(a,b){this.a=a +this.b=b}, +iQ:function iQ(){}, +fn:function fn(a,b,c,d,e,f,g,h,i){var _=this +_.y=a +_.z=null +_.Q=b +_.as=c +_.at=d +_.ax=e +_.ay=f +_.ch=g +_.e=h +_.a=i +_.b=0 +_.d=_.c=!1}, +n9:function n9(a){this.a=a}, +na:function na(a){this.a=a}, +h2:function h2(){}, +jR:function jR(a,b){this.a=a +this.b=b}, +jQ:function jQ(a){this.a=a}, +ie:function ie(a,b){var _=this +_.e=a +_.a=b +_.b=0 +_.d=_.c=!1}, +f4:function f4(a,b,c){var _=this +_.e=a +_.f=null +_.r=b +_.a=c +_.b=0 +_.d=_.c=!1}, +my:function my(a,b){this.a=a +this.b=b}, +q9(a,b){var s,r,q,p=A.ao(t.N,t.S) +for(s=a.length,r=0;r3)throw A.b("Expected two or three arguments to regexp") +s=a.j(0,0) +q=a.j(0,1) +if(s==null||q==null)return null +if(typeof s!="string"||typeof q!="string")throw A.b("Expected two strings as parameters to regexp") +if(g===3){p=a.j(0,2) +if(A.bv(p)){k=(p&1)===1 +j=(p&2)!==2 +i=(p&4)===4 +h=(p&8)===8}}r=null +try{o=k +n=j +m=i +r=A.G(s,n,h,o,m)}catch(l){if(A.H(l) instanceof A.aF)throw A.b("Invalid regex") +else throw l}o=r.b +return o.test(q)}, +vX(a){var s,r,q=a.a.b +if(q<2||q>3)throw A.b("Expected 2 or 3 arguments to moor_contains") +s=a.j(0,0) +r=a.j(0,1) +if(s==null||r==null)return null +if(typeof s!="string"||typeof r!="string")throw A.b("First two args to contains must be strings") +return q===3&&a.j(0,2)===1?B.a.H(s,r):B.a.H(s.toLowerCase(),r.toLowerCase())}, +k1:function k1(){}, +nH:function nH(a){this.a=a}, +ho:function ho(a){var _=this +_.a=$ +_.b=!1 +_.d=null +_.e=a}, +kt:function kt(a,b){this.a=a +this.b=b}, +ku:function ku(a,b){this.a=a +this.b=b}, +bq:function bq(){this.a=null}, +kw:function kw(a,b,c,d,e){var _=this +_.a=a +_.b=b +_.c=c +_.d=d +_.e=e}, +kx:function kx(a,b,c){this.a=a +this.b=b +this.c=c}, +ky:function ky(a,b){this.a=a +this.b=b}, +uV(a,b,c,d){var s,r=null,q=new A.hO(t.a7),p=t.X,o=A.eN(r,r,!1,p),n=A.eN(r,r,!1,p),m=A.pM(new A.at(n,A.r(n).h("at<1>")),new A.dP(o),!0,p) +q.a=m +p=A.pM(new A.at(o,A.r(o).h("at<1>")),new A.dP(n),!0,p) +q.b=p +s=new A.i7(A.or(c)) +a.onmessage=A.bi(new A.lS(b,q,d,s)) +m=m.b +m===$&&A.x() +new A.at(m,A.r(m).h("at<1>")).eB(new A.lT(d,s,a),new A.lU(b,a)) +return p}, +lS:function lS(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.d=d}, +lT:function lT(a,b,c){this.a=a +this.b=b +this.c=c}, +lU:function lU(a,b){this.a=a +this.b=b}, +jN:function jN(a,b,c){var _=this +_.a=a +_.b=b +_.c=c +_.d=null}, +jP:function jP(a){this.a=a}, +jO:function jO(a,b){this.a=a +this.b=b}, +or(a){var s +A:{if(a<=0){s=B.p +break A}if(1===a){s=B.aJ +break A}if(2===a){s=B.aK +break A}if(3===a){s=B.aL +break A}if(a>3){s=B.q +break A}s=A.E(A.e8(null))}return s}, +q8(a){if("v" in a)return A.or(A.B(A.Y(a.v))) +else return B.p}, +oB(a){var s,r,q,p,o,n,m,l,k,j=A.a2(a.type),i=a.payload +A:{if("Error"===j){s=new A.dz(A.a2(A.a9(i))) +break A}if("ServeDriftDatabase"===j){A.a9(i) +r=A.q8(i) +s=A.bu(A.a2(i.sqlite)) +q=A.a9(i.port) +p=A.oe(B.ax,A.a2(i.storage)) +o=A.a2(i.database) +n=A.oU(i.initPort) +m=r.c +l=m<2||A.bh(i.migrations) +s=new A.dn(s,q,p,o,n,r,l,m<3||A.bh(i.new_serialization)) +break A}if("StartFileSystemServer"===j){s=new A.eL(A.a9(i)) +break A}if("RequestCompatibilityCheck"===j){s=new A.dl(A.a2(i)) +break A}if("DedicatedWorkerCompatibilityResult"===j){A.a9(i) +k=A.f([],t.L) +if("existing" in i)B.c.aI(k,A.pG(t.c.a(i.existing))) +s=A.bh(i.supportsNestedWorkers) +q=A.bh(i.canAccessOpfs) +p=A.bh(i.supportsSharedArrayBuffers) +o=A.bh(i.supportsIndexedDb) +n=A.bh(i.indexedDbExists) +m=A.bh(i.opfsExists) +m=new A.ei(s,q,p,o,k,A.q8(i),n,m) +s=m +break A}if("SharedWorkerCompatibilityResult"===j){s=A.uD(t.c.a(i)) +break A}if("DeleteDatabase"===j){s=i==null?A.oV(i):i +t.c.a(s) +q=$.pl().j(0,A.a2(s[0])) +q.toString +s=new A.h3(new A.ag(q,A.a2(s[1]))) +break A}s=A.E(A.J("Unknown type "+j,null))}return s}, +uD(a){var s,r,q=new A.l1(a) +if(a.length>5){s=A.pG(t.c.a(a[5])) +r=a.length>6?A.or(A.B(A.Y(a[6]))):B.p}else{s=B.y +r=B.p}return new A.c6(q.$1(0),q.$1(1),q.$1(2),s,r,q.$1(3),q.$1(4))}, +pG(a){var s,r,q=A.f([],t.L),p=B.c.bx(a,t.m),o=p.$ti +p=new A.b4(p,p.gl(0),o.h("b4")) +o=o.h("w.E") +while(p.k()){s=p.d +if(s==null)s=o.a(s) +r=$.pl().j(0,A.a2(s.l)) +r.toString +q.push(new A.ag(r,A.a2(s.n)))}return q}, +pF(a){var s,r,q,p,o=A.f([],t.W) +for(s=a.length,r=0;r"))) +while(i.k()){l=i.gm() +if(J.am(l.name,a)){q=!0 +s=1 +break A}}q=!1 +s=1 +break +case 8:k=n.open(a,1) +k.onupgradeneeded=A.bi(new A.nK(g,k)) +s=10 +return A.c(A.jj(k,t.m),$async$e4) +case 10:j=c +if(g.a==null)g.a=!0 +j.close() +s=g.a===!1?11:12 +break +case 11:s=13 +return A.c(A.jj(n.deleteDatabase(a),t.X),$async$e4) +case 13:case 12:p=2 +s=6 +break +case 4:p=3 +f=o.pop() +s=6 +break +case 3:s=2 +break +case 6:i=g.a +q=i===!0 +s=1 +break +case 1:return A.i(q,r) +case 2:return A.h(o.at(-1),r)}}) +return A.j($async$e4,r)}, +nN(a){var s=0,r=A.k(t.H),q +var $async$nN=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:q=v.G +s="indexedDB" in q?2:3 +break +case 2:s=4 +return A.c(A.jj(A.a9(q.indexedDB).deleteDatabase(a),t.X),$async$nN) +case 4:case 3:return A.i(null,r)}}) +return A.j($async$nN,r)}, +iY(){var s=null +return A.xB()}, +xB(){var s=0,r=A.k(t.A),q,p=2,o=[],n,m,l,k,j,i,h +var $async$iY=A.l(function(a,b){if(a===1){o.push(b) +s=p}for(;;)switch(s){case 0:j=null +i=A.pg() +if(i==null){q=null +s=1 +break}m=t.m +s=3 +return A.c(A.V(i.getDirectory(),m),$async$iY) +case 3:n=b +p=5 +l=j +if(l==null)l={} +s=8 +return A.c(A.V(n.getDirectoryHandle("drift_db",l),m),$async$iY) +case 8:m=b +q=m +s=1 +break +p=2 +s=7 +break +case 5:p=4 +h=o.pop() +q=null +s=1 +break +s=7 +break +case 4:s=2 +break +case 7:case 1:return A.i(q,r) +case 2:return A.h(o.at(-1),r)}}) +return A.j($async$iY,r)}, +e6(){var s=0,r=A.k(t.q),q,p=2,o=[],n=[],m,l,k,j,i,h,g,f +var $async$e6=A.l(function(a,b){if(a===1){o.push(b) +s=p}for(;;)switch(s){case 0:s=3 +return A.c(A.iY(),$async$e6) +case 3:g=b +if(g==null){q=B.x +s=1 +break}j=t.cO +if(!(v.G.Symbol.asyncIterator in g))A.E(A.J("Target object does not implement the async iterable interface",null)) +m=new A.fc(new A.nZ(),new A.e9(g,j),j.h("fc")) +l=A.f([],t.s) +j=new A.dO(A.cV(m,"stream",t.K)) +p=4 +i=t.m +case 7:s=9 +return A.c(j.k(),$async$e6) +case 9:if(!b){s=8 +break}k=j.gm() +s=J.am(k.kind,"directory")?10:11 +break +case 10:p=13 +s=16 +return A.c(A.V(k.getFileHandle("database"),i),$async$e6) +case 16:J.o8(l,k.name) +p=4 +s=15 +break +case 13:p=12 +f=o.pop() +s=15 +break +case 12:s=4 +break +case 15:case 11:s=7 +break +case 8:n.push(6) +s=5 +break +case 4:n=[2] +case 5:p=2 +s=17 +return A.c(j.J(),$async$e6) +case 17:s=n.pop() +break +case 6:q=l +s=1 +break +case 1:return A.i(q,r) +case 2:return A.h(o.at(-1),r)}}) +return A.j($async$e6,r)}, +fG(a){return A.x9(a)}, +x9(a){var s=0,r=A.k(t.H),q,p=2,o=[],n,m,l,k,j +var $async$fG=A.l(function(b,c){if(b===1){o.push(c) +s=p}for(;;)switch(s){case 0:k=A.pg() +if(k==null){s=1 +break}m=t.m +s=3 +return A.c(A.V(k.getDirectory(),m),$async$fG) +case 3:n=c +p=5 +s=8 +return A.c(A.V(n.getDirectoryHandle("drift_db"),m),$async$fG) +case 8:n=c +s=9 +return A.c(A.V(n.removeEntry(a,{recursive:!0}),t.X),$async$fG) +case 9:p=2 +s=7 +break +case 5:p=4 +j=o.pop() +s=7 +break +case 4:s=2 +break +case 7:case 1:return A.i(q,r) +case 2:return A.h(o.at(-1),r)}}) +return A.j($async$fG,r)}, +jj(a,b){var s=new A.n($.m,b.h("n<0>")),r=new A.a1(s,b.h("a1<0>")) +A.aL(a,"success",new A.jm(r,a,b),!1) +A.aL(a,"error",new A.jn(r,a),!1) +A.aL(a,"blocked",new A.jo(r,a),!1) +return s}, +nK:function nK(a,b){this.a=a +this.b=b}, +nZ:function nZ(){}, +h6:function h6(a,b){this.a=a +this.b=b}, +k_:function k_(a,b){this.a=a +this.b=b}, +jX:function jX(a){this.a=a}, +jW:function jW(a){this.a=a}, +jY:function jY(a,b,c){this.a=a +this.b=b +this.c=c}, +jZ:function jZ(a,b,c){this.a=a +this.b=b +this.c=c}, +mn:function mn(a,b){this.a=a +this.b=b}, +dm:function dm(a,b,c){var _=this +_.a=a +_.b=b +_.c=0 +_.d=c}, +kM:function kM(a){this.a=a}, +lF:function lF(a,b){this.a=a +this.b=b}, +jm:function jm(a,b,c){this.a=a +this.b=b +this.c=c}, +jn:function jn(a,b){this.a=a +this.b=b}, +jo:function jo(a,b){this.a=a +this.b=b}, +kW:function kW(a,b){this.a=a +this.b=null +this.c=b}, +l0:function l0(a){this.a=a}, +kX:function kX(a,b){this.a=a +this.b=b}, +l_:function l_(a,b,c){this.a=a +this.b=b +this.c=c}, +kY:function kY(a){this.a=a}, +kZ:function kZ(a,b,c){this.a=a +this.b=b +this.c=c}, +cc:function cc(a,b){this.a=a +this.b=b}, +bO:function bO(a,b){this.a=a +this.b=b}, +i4:function i4(a,b,c,d,e){var _=this +_.e=a +_.f=null +_.r=b +_.w=c +_.x=d +_.a=e +_.b=0 +_.d=_.c=!1}, +iT:function iT(a,b,c,d,e,f,g){var _=this +_.Q=a +_.as=b +_.at=c +_.b=null +_.d=_.c=!1 +_.e=d +_.f=e +_.r=f +_.x=g +_.y=$ +_.a=!1}, +pB(a){return new A.fY(a,".")}, +p_(a){return a}, +rt(a,b){var s,r,q,p,o,n,m,l +for(s=b.length,r=1;r=1;s=q){q=s-1 +if(b[q]!=null)break}p=new A.aD("") +o=a+"(" +p.a=o +n=A.O(b) +m=n.h("cD<1>") +l=new A.cD(b,0,s,m) +l.hZ(b,0,s,n.c) +m=o+new A.D(l,new A.nI(),m.h("D")).av(0,", ") +p.a=m +p.a=m+("): part "+(r-1)+" was null, but part "+r+" was not.") +throw A.b(A.J(p.i(0),null))}}, +fY:function fY(a,b){this.a=a +this.b=b}, +js:function js(){}, +jt:function jt(){}, +nI:function nI(){}, +kp:function kp(){}, +di(a,b){var s,r,q,p,o,n=b.hF(a) +b.aZ(a) +if(n!=null)a=B.a.L(a,n.length) +s=t.s +r=A.f([],s) +q=A.f([],s) +s=a.length +if(s!==0&&b.au(a.charCodeAt(0))){q.push(a[0]) +p=1}else{q.push("") +p=0}for(o=p;o"))}, +o_:function o_(){}, +ju:function ju(){}, +hJ:function hJ(a,b,c){this.d=a +this.a=b +this.c=c}, +bs:function bs(a,b){this.a=a +this.b=b}, +n3:function n3(a){this.a=a +this.b=-1}, +iG:function iG(){}, +iH:function iH(){}, +iJ:function iJ(){}, +iK:function iK(){}, +kC:function kC(a,b){this.a=a +this.b=b}, +d3:function d3(){}, +cw:function cw(a){this.a=a}, +ca(a){return new A.aJ(a)}, +pu(a,b){var s,r,q,p +if(b==null)b=$.fI() +for(s=a.length,r=a.$flags|0,q=0;q")),r=new A.a1(s,b.h("a1<0>")) +A.aL(a,"success",new A.jk(r,a,b),!1) +A.aL(a,"error",new A.jl(r,a),!1) +return s}, +tW(a,b){var s=new A.n($.m,b.h("n<0>")),r=new A.a1(s,b.h("a1<0>")) +A.aL(a,"success",new A.jp(r,a,b),!1) +A.aL(a,"error",new A.jq(r,a),!1) +A.aL(a,"blocked",new A.jr(r),!1) +return s}, +cK:function cK(a,b){var _=this +_.c=_.b=_.a=null +_.d=a +_.$ti=b}, +mo:function mo(a,b){this.a=a +this.b=b}, +mp:function mp(a,b){this.a=a +this.b=b}, +jk:function jk(a,b,c){this.a=a +this.b=b +this.c=c}, +jl:function jl(a,b){this.a=a +this.b=b}, +jp:function jp(a,b,c){this.a=a +this.b=b +this.c=c}, +jq:function jq(a,b){this.a=a +this.b=b}, +jr:function jr(a){this.a=a}, +lL:function lL(a){this.a=a}, +lM:function lM(a){this.a=a}, +lO(a){var s=0,r=A.k(t.ab),q,p,o +var $async$lO=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:p=v.G +o=A +s=3 +return A.c(A.V(p.fetch(new p.URL(a,A.a9(p.location).href),null),t.m),$async$lO) +case 3:q=o.lN(c,null) +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$lO,r)}, +lN(a,b){var s=0,r=A.k(t.ab),q,p,o,n,m +var $async$lN=A.l(function(c,d){if(c===1)return A.h(d,r) +for(;;)switch(s){case 0:p=new A.fZ(A.ao(t.S,t.b9)) +o=A +n=A +m=A +s=3 +return A.c(new A.lL(p).d7(a),$async$lN) +case 3:q=new o.i6(new n.lP(m.uU(d,p))) +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$lN,r)}, +i6:function i6(a){this.a=a}, +dy:function dy(a,b,c,d){var _=this +_.d=a +_.e=b +_.b=c +_.a=d}, +i5:function i5(a,b){this.a=a +this.b=b +this.c=0}, +qb(a){var s=J.am(a.byteLength,8) +if(!s)throw A.b(A.J("Must be 8 in length",null)) +s=v.G.Int32Array +return new A.kL(t.ha.a(A.e3(s,[a])))}, +un(a){return B.h}, +uo(a){var s=a.b +return new A.R(s.getInt32(0,!1),s.getInt32(4,!1),s.getInt32(8,!1))}, +up(a){var s=a.b +return new A.aW(B.j.cX(new Uint8Array(A.fC(A.ow(a.a,16,s.getInt32(12,!1))))),s.getInt32(0,!1),s.getInt32(4,!1),s.getInt32(8,!1))}, +kL:function kL(a){this.b=a}, +br:function br(a,b,c){this.a=a +this.b=b +this.c=c}, +ac:function ac(a,b,c,d,e){var _=this +_.c=a +_.d=b +_.a=c +_.b=d +_.$ti=e}, +bC:function bC(){}, +b2:function b2(){}, +R:function R(a,b,c){this.a=a +this.b=b +this.c=c}, +aW:function aW(a,b,c,d){var _=this +_.d=a +_.a=b +_.b=c +_.c=d}, +i3(a){var s=0,r=A.k(t.ei),q,p,o,n,m,l,k,j +var $async$i3=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:l=t.m +s=3 +return A.c(A.V(A.pf().getDirectory(),l),$async$i3) +case 3:k=c +j=A.pc(a.root) +p=J.Z(j.a),o=new A.cG(p,j.b) +case 4:if(!o.k()){s=5 +break}s=6 +return A.c(A.V(k.getDirectoryHandle(p.gm(),{create:!0}),l),$async$i3) +case 6:k=c +s=4 +break +case 5:l=t.cT +p=A.qb(a.synchronizationBuffer) +o=a.communicationBuffer +n=A.qd(o,65536,2048) +m=v.G.Uint8Array +q=new A.eQ(p,new A.br(o,n,t.Z.a(A.e3(m,[o]))),k,A.ao(t.S,l),A.op(l)) +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$i3,r)}, +iF:function iF(a,b,c){this.a=a +this.b=b +this.c=c}, +eQ:function eQ(a,b,c,d,e){var _=this +_.a=a +_.b=b +_.c=c +_.d=0 +_.e=!1 +_.f=d +_.r=e}, +dK:function dK(a,b,c,d,e,f,g){var _=this +_.a=a +_.b=b +_.c=c +_.d=d +_.e=e +_.f=f +_.r=g +_.w=!1 +_.x=null}, +v9(a){var s=new A.f9(a,new A.a1(new A.n($.m,t.D),t.F),a.objectStore("files"),a.objectStore("blocks")) +s.i0(a) +return s}, +hg(a,b){var s=0,r=A.k(t.bd),q,p,o,n,m,l +var $async$hg=A.l(function(c,d){if(c===1)return A.h(d,r) +for(;;)switch(s){case 0:p=t.N +o=new A.j6(a) +n=A.ok(null) +m=$.fI() +l=new A.d7(o,n,new A.cy(t.au),A.op(p),A.ao(p,t.S),m,"indexeddb") +l.r=!1 +s=3 +return A.c(o.d8(),$async$hg) +case 3:s=4 +return A.c(l.bR(),$async$hg) +case 4:q=l +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$hg,r)}, +j6:function j6(a){this.a=null +this.b=a}, +j9:function j9(a){this.a=a}, +j8:function j8(a,b,c){this.a=a +this.b=b +this.c=c}, +j7:function j7(a){this.a=a}, +f9:function f9(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=!1 +_.d=c +_.e=d}, +mT:function mT(a){this.a=a}, +mU:function mU(a){this.a=a}, +mS:function mS(a){this.a=a}, +mV:function mV(a,b,c){this.a=a +this.b=b +this.c=c}, +mX:function mX(a,b){this.a=a +this.b=b}, +mW:function mW(a,b){this.a=a +this.b=b}, +mz:function mz(a,b,c){this.a=a +this.b=b +this.c=c}, +mA:function mA(a,b){this.a=a +this.b=b}, +iB:function iB(a,b){this.a=a +this.b=b}, +d7:function d7(a,b,c,d,e,f,g){var _=this +_.d=a +_.f=_.e=!1 +_.r=!0 +_.w=b +_.x=c +_.y=d +_.z=e +_.b=f +_.a=g}, +kj:function kj(a,b,c){this.a=a +this.b=b +this.c=c}, +kk:function kk(){}, +ki:function ki(a,b){this.a=a +this.b=b}, +iu:function iu(a,b,c){this.a=a +this.b=b +this.c=c}, +mR:function mR(a,b){this.a=a +this.b=b}, +au:function au(){}, +f6:function f6(a,b){var _=this +_.w=a +_.d=b +_.c=_.b=_.a=null}, +f_:function f_(a,b,c){var _=this +_.w=a +_.x=b +_.d=c +_.c=_.b=_.a=null}, +dC:function dC(a,b,c){var _=this +_.w=a +_.x=b +_.d=c +_.c=_.b=_.a=null}, +dU:function dU(a,b,c,d,e){var _=this +_.w=a +_.x=b +_.y=c +_.z=d +_.d=e +_.c=_.b=_.a=null}, +hL(a,b){var s=0,r=A.k(t.e1),q,p,o,n,m,l,k,j +var $async$hL=A.l(function(c,d){if(c===1)return A.h(d,r) +for(;;)switch(s){case 0:j=A.pf() +if(j==null)throw A.b(A.ca(1)) +p=t.m +s=3 +return A.c(A.V(j.getDirectory(),p),$async$hL) +case 3:o=d +n=A.pc(a),m=J.Z(n.a),n=new A.cG(m,n.b),l=null +case 4:if(!n.k()){s=6 +break}s=7 +return A.c(A.V(o.getDirectoryHandle(m.gm(),{create:!0}),p),$async$hL) +case 7:k=d +case 5:l=o,o=k +s=4 +break +case 6:q=new A.ag(l,o) +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$hL,r)}, +l5(a){var s=0,r=A.k(t.m),q +var $async$l5=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:s=3 +return A.c(A.hL(a,!0),$async$l5) +case 3:q=c.b +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$l5,r)}, +l3(a){var s=0,r=A.k(t.gW),q,p +var $async$l3=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:if(A.pf()==null)throw A.b(A.ca(1)) +p=A +s=3 +return A.c(A.l5(a),$async$l3) +case 3:q=p.l2(c,!1,"simple-opfs") +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$l3,r)}, +l2(a,b,c){var s=0,r=A.k(t.gW),q,p,o,n +var $async$l2=A.l(function(d,e){if(d===1)return A.h(e,r) +for(;;)switch(s){case 0:p=A.ok(null) +o=$.fI() +n=new A.dq(p,o,c) +s=3 +return A.c(n.bC(a,!1),$async$l2) +case 3:q=n +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$l2,r)}, +d6:function d6(a,b,c){this.c=a +this.a=b +this.b=c}, +dq:function dq(a,b,c){var _=this +_.d=null +_.e=a +_.b=b +_.a=c}, +l4:function l4(a,b){this.a=a +this.b=b}, +iL:function iL(a,b,c){var _=this +_.a=a +_.b=b +_.c=c +_.d=0}, +n0:function n0(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.d=d}, +uU(a,b){var s=A.a9(a.exports.memory) +b.b!==$&&A.iZ() +b.b=s +s=new A.lA(s,b,a.exports) +s.i_(a,b) +return s}, +oD(a,b){var s,r=A.bE(a.buffer,b,null) +for(s=0;r[s]!==0;)++s +return s}, +cd(a,b,c){var s=a.buffer +return B.j.cX(A.bE(s,b,c==null?A.oD(a,b):c))}, +oC(a,b,c){var s +if(b===0)return null +s=a.buffer +return B.j.cX(A.bE(s,b,c==null?A.oD(a,b):c))}, +qu(a,b,c){var s=new Uint8Array(c) +B.e.b2(s,0,A.bE(a.buffer,b,c)) +return s}, +lA:function lA(a,b,c){var _=this +_.b=a +_.c=b +_.d=c +_.w=_.r=null}, +lB:function lB(a){this.a=a}, +lC:function lC(a){this.a=a}, +lD:function lD(a){this.a=a}, +lE:function lE(a){this.a=a}, +tQ(a){var s,r,q=u.q +if(a.length===0)return new A.bm(A.aO(A.f([],t.J),t.a)) +s=$.pq() +if(B.a.H(a,s)){s=B.a.bm(a,s) +r=A.O(s) +return new A.bm(A.aO(new A.aG(new A.aK(s,new A.ja(),r.h("aK<1>")),A.xQ(),r.h("aG<1,a0>")),t.a))}if(!B.a.H(a,q))return new A.bm(A.aO(A.f([A.qm(a)],t.J),t.a)) +return new A.bm(A.aO(new A.D(A.f(a.split(q),t.s),A.xP(),t.fe),t.a))}, +bm:function bm(a){this.a=a}, +ja:function ja(){}, +jf:function jf(){}, +je:function je(){}, +jc:function jc(){}, +jd:function jd(a){this.a=a}, +jb:function jb(a){this.a=a}, +u9(a){return A.pJ(a)}, +pJ(a){return A.hc(a,new A.ka(a))}, +u8(a){return A.u5(a)}, +u5(a){return A.hc(a,new A.k8(a))}, +u2(a){return A.hc(a,new A.k5(a))}, +u6(a){return A.u3(a)}, +u3(a){return A.hc(a,new A.k6(a))}, +u7(a){return A.u4(a)}, +u4(a){return A.hc(a,new A.k7(a))}, +hd(a){if(B.a.H(a,$.rS()))return A.bu(a) +else if(B.a.H(a,$.rT()))return A.qS(a,!0) +else if(B.a.u(a,"/"))return A.qS(a,!1) +if(B.a.H(a,"\\"))return $.tB().ht(a) +return A.bu(a)}, +hc(a,b){var s,r +try{s=b.$0() +return s}catch(r){if(A.H(r) instanceof A.aF)return new A.bt(A.al(null,"unparsed",null,null),a) +else throw r}}, +M:function M(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.d=d}, +ka:function ka(a){this.a=a}, +k8:function k8(a){this.a=a}, +k9:function k9(a){this.a=a}, +k5:function k5(a){this.a=a}, +k6:function k6(a){this.a=a}, +k7:function k7(a){this.a=a}, +hp:function hp(a){this.a=a +this.b=$}, +ql(a){if(t.a.b(a))return a +if(a instanceof A.bm)return a.hs() +return new A.hp(new A.lo(a))}, +qm(a){var s,r,q +try{if(a.length===0){r=A.qi(A.f([],t.e),null) +return r}if(B.a.H(a,$.tw())){r=A.uK(a) +return r}if(B.a.H(a,"\tat ")){r=A.uJ(a) +return r}if(B.a.H(a,$.tl())||B.a.H(a,$.tj())){r=A.uI(a) +return r}if(B.a.H(a,u.q)){r=A.tQ(a).hs() +return r}if(B.a.H(a,$.to())){r=A.qj(a) +return r}r=A.qk(a) +return r}catch(q){r=A.H(q) +if(r instanceof A.aF){s=r +throw A.b(A.aj(s.a+"\nStack trace:\n"+a,null,null))}else throw q}}, +uM(a){return A.qk(a)}, +qk(a){var s=A.aO(A.uN(a),t.B) +return new A.a0(s)}, +uN(a){var s,r=B.a.eM(a),q=$.pq(),p=t.U,o=new A.aK(A.f(A.bk(r,q,"").split("\n"),t.s),new A.lp(),p) +if(!o.gq(0).k())return A.f([],t.e) +r=A.oz(o,o.gl(0)-1,p.h("d.E")) +r=A.ht(r,A.xf(),A.r(r).h("d.E"),t.B) +s=A.ak(r,A.r(r).h("d.E")) +if(!B.a.ek(o.gD(0),".da"))s.push(A.pJ(o.gD(0))) +return s}, +uK(a){var s=A.b6(A.f(a.split("\n"),t.s),1,null,t.N).hR(0,new A.ln()),r=t.B +r=A.aO(A.ht(s,A.rA(),s.$ti.h("d.E"),r),r) +return new A.a0(r)}, +uJ(a){var s=A.aO(new A.aG(new A.aK(A.f(a.split("\n"),t.s),new A.lm(),t.U),A.rA(),t.M),t.B) +return new A.a0(s)}, +uI(a){var s=A.aO(new A.aG(new A.aK(A.f(B.a.eM(a).split("\n"),t.s),new A.lk(),t.U),A.xd(),t.M),t.B) +return new A.a0(s)}, +uL(a){return A.qj(a)}, +qj(a){var s=a.length===0?A.f([],t.e):new A.aG(new A.aK(A.f(B.a.eM(a).split("\n"),t.s),new A.ll(),t.U),A.xe(),t.M) +s=A.aO(s,t.B) +return new A.a0(s)}, +qi(a,b){var s=A.aO(a,t.B) +return new A.a0(s)}, +a0:function a0(a){this.a=a}, +lo:function lo(a){this.a=a}, +lp:function lp(){}, +ln:function ln(){}, +lm:function lm(){}, +lk:function lk(){}, +ll:function ll(){}, +lr:function lr(){}, +lq:function lq(a){this.a=a}, +bt:function bt(a,b){this.a=a +this.w=b}, +ee:function ee(a){var _=this +_.b=_.a=$ +_.c=null +_.d=!1 +_.$ti=a}, +eY:function eY(a,b,c){this.a=a +this.b=b +this.$ti=c}, +eX:function eX(a,b){this.b=a +this.a=b}, +pM(a,b,c,d){var s,r={} +r.a=a +s=new A.eo(d.h("eo<0>")) +s.hX(b,!0,r,d) +return s}, +eo:function eo(a){var _=this +_.b=_.a=$ +_.c=null +_.d=!1 +_.$ti=a}, +kg:function kg(a,b){this.a=a +this.b=b}, +kf:function kf(a){this.a=a}, +f8:function f8(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.e=_.d=!1 +_.r=_.f=null +_.w=d}, +hO:function hO(a){this.b=this.a=$ +this.$ti=a}, +eM:function eM(){}, +dt:function dt(){}, +iv:function iv(){}, +bg:function bg(a,b){this.a=a +this.b=b}, +aL(a,b,c,d){var s +if(c==null)s=null +else{s=A.ru(new A.mw(c),t.m) +s=s==null?null:A.bi(s)}s=new A.io(a,b,s,!1) +s.e5() +return s}, +ru(a,b){var s=$.m +if(s===B.d)return a +return s.eg(a,b)}, +of:function of(a,b){this.a=a +this.$ti=b}, +f3:function f3(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.$ti=d}, +io:function io(a,b,c,d){var _=this +_.a=0 +_.b=a +_.c=b +_.d=c +_.e=d}, +mw:function mw(a){this.a=a}, +mx:function mx(a){this.a=a}, +pd(a){if(typeof dartPrint=="function"){dartPrint(a) +return}if(typeof console=="object"&&typeof console.log!="undefined"){console.log(a) +return}if(typeof print=="function"){print(a) +return}throw"Unable to print message: "+String(a)}, +hm(a,b,c,d,e,f){var s +if(c==null)return a[b]() +else if(d==null)return a[b](c) +else if(e==null)return a[b](c,d) +else{s=a[b](c,d,e) +return s}}, +p5(){var s,r,q,p,o=null +try{o=A.i_()}catch(s){if(t.g8.b(A.H(s))){r=$.nz +if(r!=null)return r +throw s}else throw s}if(J.am(o,$.r8)){r=$.nz +r.toString +return r}$.r8=o +if($.pk()===$.fJ())r=$.nz=o.hq(".").i(0) +else{q=o.eL() +p=q.length-1 +r=$.nz=p===0?q:B.a.p(q,0,p)}return r}, +rE(a){var s +if(!(a>=65&&a<=90))s=a>=97&&a<=122 +else s=!0 +return s}, +rz(a,b){var s,r,q=null,p=a.length,o=b+2 +if(p0)throw A.b(A.k2("BigInt value exceeds the range of 64 bits")) +return a}, +uA(a){var s,r=a.a,q=a.b,p=r.d,o=p.sqlite3_value_type(q) +A:{s=null +if(1===o){r=A.B(v.G.Number(p.sqlite3_value_int64(q))) +break A}if(2===o){r=p.sqlite3_value_double(q) +break A}if(3===o){o=p.sqlite3_value_bytes(q) +o=A.cd(r.b,p.sqlite3_value_text(q),o) +r=o +break A}if(4===o){o=p.sqlite3_value_bytes(q) +o=A.qu(r.b,p.sqlite3_value_blob(q),o) +r=o +break A}r=s +break A}return r}, +oj(a,b){var s,r +for(s=b,r=0;r<16;++r)s+=A.aR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012346789".charCodeAt(a.hg(61))) +return s.charCodeAt(0)==0?s:s}, +kK(a){var s=0,r=A.k(t.w),q +var $async$kK=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:s=3 +return A.c(A.V(a.arrayBuffer(),t.u),$async$kK) +case 3:q=c +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$kK,r)}, +qd(a,b,c){var s=v.G.DataView,r=[a] +r.push(b) +r.push(c) +return t.gT.a(A.e3(s,r))}, +ow(a,b,c){var s=v.G.Uint8Array,r=[a] +r.push(b) +r.push(c) +return t.Z.a(A.e3(s,r))}, +tN(a,b){v.G.Atomics.notify(a,b,1/0)}, +pf(){var s=v.G.navigator +if("storage" in s)return s.storage +return null}, +og(a,b,c){var s=a.read(b,c) +return s}, +oh(a,b,c){var s=a.write(b,c) +return s}, +pI(a,b){return A.V(a.removeEntry(b,{recursive:!1}),t.X)}, +xr(){var s=v.G +if(A.kq(s,"DedicatedWorkerGlobalScope"))new A.jN(s,new A.bq(),new A.h6(A.ao(t.N,t.fE),null)).R() +else if(A.kq(s,"SharedWorkerGlobalScope"))new A.kW(s,new A.h6(A.ao(t.N,t.fE),null)).R()}},B={} +var w=[A,J,B] +var $={} +A.on.prototype={} +J.hi.prototype={ +T(a,b){return a===b}, +gA(a){return A.eE(a)}, +i(a){return"Instance of '"+A.hH(a)+"'"}, +gS(a){return A.bR(A.oY(this))}} +J.hk.prototype={ +i(a){return String(a)}, +gA(a){return a?519018:218159}, +gS(a){return A.bR(t.y)}, +$iK:1, +$iI:1} +J.et.prototype={ +T(a,b){return null==b}, +i(a){return"null"}, +gA(a){return 0}, +$iK:1, +$iN:1} +J.eu.prototype={$iz:1} +J.bY.prototype={ +gA(a){return 0}, +i(a){return String(a)}} +J.hG.prototype={} +J.cF.prototype={} +J.bz.prototype={ +i(a){var s=a[$.rR()] +if(s==null)s=a[$.cZ()] +if(s==null)return this.hS(a) +return"JavaScript function for "+J.b1(s)}} +J.aN.prototype={ +gA(a){return 0}, +i(a){return String(a)}} +J.d9.prototype={ +gA(a){return 0}, +i(a){return String(a)}} +J.u.prototype={ +bx(a,b){return new A.ai(a,A.O(a).h("@<1>").G(b).h("ai<1,2>"))}, +v(a,b){a.$flags&1&&A.y(a,29) +a.push(b)}, +dd(a,b){var s +a.$flags&1&&A.y(a,"removeAt",1) +s=a.length +if(b>=s)throw A.b(A.kJ(b,null)) +return a.splice(b,1)[0]}, +d2(a,b,c){var s +a.$flags&1&&A.y(a,"insert",2) +s=a.length +if(b>s)throw A.b(A.kJ(b,null)) +a.splice(b,0,c)}, +ev(a,b,c){var s,r +a.$flags&1&&A.y(a,"insertAll",2) +A.qa(b,0,a.length,"index") +if(!t.Q.b(c))c=J.j2(c) +s=J.aC(c) +a.length=a.length+s +r=b+s +this.N(a,r,a.length,a,b) +this.ac(a,b,r,c)}, +hm(a){a.$flags&1&&A.y(a,"removeLast",1) +if(a.length===0)throw A.b(A.iX(a,-1)) +return a.pop()}, +F(a,b){var s +a.$flags&1&&A.y(a,"remove",1) +for(s=0;s").G(c).h("D<1,2>"))}, +av(a,b){var s,r=A.b5(a.length,"",!1,t.N) +for(s=0;ss)throw A.b(A.W(b,0,s,"start",null)) +if(cs)throw A.b(A.W(c,b,s,"end",null)) +if(b===c)return A.f([],A.O(a)) +return A.f(a.slice(b,c),A.O(a))}, +ct(a,b,c){A.bd(b,c,a.length) +return A.b6(a,b,c,A.O(a).c)}, +gE(a){if(a.length>0)return a[0] +throw A.b(A.aw())}, +gD(a){var s=a.length +if(s>0)return a[s-1] +throw A.b(A.aw())}, +N(a,b,c,d,e){var s,r,q,p,o +a.$flags&2&&A.y(a,5) +A.bd(b,c,a.length) +s=c-b +if(s===0)return +A.ab(e,"skipCount") +if(t.j.b(d)){r=d +q=e}else{r=J.e7(d,e).aC(0,!1) +q=0}p=J.a3(r) +if(q+s>p.gl(r))throw A.b(A.pP()) +if(q=0;--o)a[b+o]=p.j(r,q+o) +else for(o=0;o0){a[0]=q +a[1]=r}return}p=0 +if(A.O(a).c.b(null))for(o=0;o0)this.jd(a,p)}, +hM(a){return this.hN(a,null)}, +jd(a,b){var s,r=a.length +for(;s=r-1,r>0;r=s)if(a[s]===null){a[s]=void 0;--b +if(b===0)break}}, +d5(a,b){var s,r=a.length,q=r-1 +if(q<0)return-1 +q=0;--s)if(J.am(a[s],b))return s +return-1}, +gB(a){return a.length===0}, +i(a){return A.ol(a,"[","]")}, +aC(a,b){var s=A.f(a.slice(0),A.O(a)) +return s}, +cn(a){return this.aC(a,!0)}, +gq(a){return new J.fL(a,a.length,A.O(a).h("fL<1>"))}, +gA(a){return A.eE(a)}, +gl(a){return a.length}, +j(a,b){if(!(b>=0&&b=0&&b=p){r.d=null +return!1}r.d=q[s] +r.c=s+1 +return!0}} +J.d8.prototype={ +af(a,b){var s +if(ab)return 1 +else if(a===b){if(a===0){s=this.gey(b) +if(this.gey(a)===s)return 0 +if(this.gey(a))return-1 +return 1}return 0}else if(isNaN(a)){if(isNaN(b))return 0 +return 1}else return-1}, +gey(a){return a===0?1/a<0:a<0}, +lj(a){var s +if(a>=-2147483648&&a<=2147483647)return a|0 +if(isFinite(a)){s=a<0?Math.ceil(a):Math.floor(a) +return s+0}throw A.b(A.a4(""+a+".toInt()"))}, +k_(a){var s,r +if(a>=0){if(a<=2147483647){s=a|0 +return a===s?s:s+1}}else if(a>=-2147483648)return a|0 +r=Math.ceil(a) +if(isFinite(r))return r +throw A.b(A.a4(""+a+".ceil()"))}, +i(a){if(a===0&&1/a<0)return"-0.0" +else return""+a}, +gA(a){var s,r,q,p,o=a|0 +if(a===o)return o&536870911 +s=Math.abs(a) +r=Math.log(s)/0.6931471805599453|0 +q=Math.pow(2,r) +p=s<1?s/q:q/s +return((p*9007199254740992|0)+(p*3542243181176521|0))*599197+r*1259&536870911}, +ab(a,b){var s=a%b +if(s===0)return 0 +if(s>0)return s +return s+b}, +eY(a,b){if((a|0)===a)if(b>=1||b<-1)return a/b|0 +return this.fL(a,b)}, +I(a,b){return(a|0)===a?a/b|0:this.fL(a,b)}, +fL(a,b){var s=a/b +if(s>=-2147483648&&s<=2147483647)return s|0 +if(s>0){if(s!==1/0)return Math.floor(s)}else if(s>-1/0)return Math.ceil(s) +throw A.b(A.a4("Result of truncating division is "+A.t(s)+": "+A.t(a)+" ~/ "+b))}, +aE(a,b){if(b<0)throw A.b(A.e2(b)) +return b>31?0:a<>>0}, +bl(a,b){var s +if(b<0)throw A.b(A.e2(b)) +if(a>0)s=this.e4(a,b) +else{s=b>31?31:b +s=a>>s>>>0}return s}, +M(a,b){var s +if(a>0)s=this.e4(a,b) +else{s=b>31?31:b +s=a>>s>>>0}return s}, +jt(a,b){if(0>b)throw A.b(A.e2(b)) +return this.e4(a,b)}, +e4(a,b){return b>31?0:a>>>b}, +gS(a){return A.bR(t.o)}, +$iF:1, +$ib0:1} +J.es.prototype={ +gfY(a){var s,r=a<0?-a-1:a,q=r +for(s=32;q>=4294967296;){q=this.I(q,4294967296) +s+=32}return s-Math.clz32(q)}, +gS(a){return A.bR(t.S)}, +$iK:1, +$ia:1} +J.hl.prototype={ +gS(a){return A.bR(t.i)}, +$iK:1} +J.bX.prototype={ +cS(a,b,c){var s=b.length +if(c>s)throw A.b(A.W(c,0,s,null,null)) +return new A.iM(b,a,c)}, +ed(a,b){return this.cS(a,b,0)}, +he(a,b,c){var s,r,q=null +if(c<0||c>b.length)throw A.b(A.W(c,0,b.length,q,q)) +s=a.length +if(c+s>b.length)return q +for(r=0;rr)return!1 +return b===this.L(a,r-s)}, +hp(a,b,c){A.qa(0,0,a.length,"startIndex") +return A.xL(a,b,c,0)}, +bm(a,b){var s +if(typeof b=="string")return A.f(a.split(b),t.s) +else{if(b instanceof A.cx){s=b.e +s=!(s==null?b.e=b.ij():s)}else s=!1 +if(s)return A.f(a.split(b.b),t.s) +else return this.ir(a,b)}}, +aO(a,b,c,d){var s=A.bd(b,c,a.length) +return A.ph(a,b,s,d)}, +ir(a,b){var s,r,q,p,o,n,m=A.f([],t.s) +for(s=J.o9(b,a),s=s.gq(s),r=0,q=1;s.k();){p=s.gm() +o=p.gcv() +n=p.gbz() +q=n-o +if(q===0&&r===o)continue +m.push(this.p(a,r,o)) +r=n}if(r0)m.push(this.L(a,r)) +return m}, +C(a,b,c){var s +if(c<0||c>a.length)throw A.b(A.W(c,0,a.length,null,null)) +if(typeof b=="string"){s=c+b.length +if(s>a.length)return!1 +return b===a.substring(c,s)}return J.tH(b,a,c)!=null}, +u(a,b){return this.C(a,b,0)}, +p(a,b,c){return a.substring(b,A.bd(b,c,a.length))}, +L(a,b){return this.p(a,b,null)}, +eM(a){var s,r,q,p=a.trim(),o=p.length +if(o===0)return p +if(p.charCodeAt(0)===133){s=J.ug(p,1) +if(s===o)return""}else s=0 +r=o-1 +q=p.charCodeAt(r)===133?J.uh(p,r):o +if(s===0&&q===o)return p +return p.substring(s,q)}, +bI(a,b){var s,r +if(0>=b)return"" +if(b===1||a.length===0)return a +if(b!==b>>>0)throw A.b(B.ap) +for(s=a,r="";;){if((b&1)===1)r=s+r +b=b>>>1 +if(b===0)break +s+=s}return r}, +l0(a,b,c){var s=b-a.length +if(s<=0)return a +return this.bI(c,s)+a}, +hh(a,b){var s=b-a.length +if(s<=0)return a +return a+this.bI(" ",s)}, +aY(a,b,c){var s +if(c<0||c>a.length)throw A.b(A.W(c,0,a.length,null,null)) +s=a.indexOf(b,c) +return s}, +kG(a,b){return this.aY(a,b,0)}, +hd(a,b,c){var s,r +if(c==null)c=a.length +else if(c<0||c>a.length)throw A.b(A.W(c,0,a.length,null,null)) +s=b.length +r=a.length +if(c+s>r)c=r-s +return a.lastIndexOf(b,c)}, +d5(a,b){return this.hd(a,b,null)}, +H(a,b){return A.xH(a,b,0)}, +af(a,b){var s +if(a===b)s=0 +else s=a>6}r=r+((r&67108863)<<3)&536870911 +r^=r>>11 +return r+((r&16383)<<15)&536870911}, +gS(a){return A.bR(t.N)}, +gl(a){return a.length}, +j(a,b){if(!(b>=0&&b"))}, +gl(a){return J.aC(this.gao())}, +gB(a){return J.oa(this.gao())}, +U(a,b){var s=A.r(this) +return A.ed(J.e7(this.gao(),b),s.c,s.y[1])}, +ah(a,b){var s=A.r(this) +return A.ed(J.j1(this.gao(),b),s.c,s.y[1])}, +K(a,b){return A.r(this).y[1].a(J.j_(this.gao(),b))}, +gE(a){return A.r(this).y[1].a(J.j0(this.gao()))}, +gD(a){return A.r(this).y[1].a(J.ob(this.gao()))}, +i(a){return J.b1(this.gao())}} +A.fU.prototype={ +k(){return this.a.k()}, +gm(){return this.$ti.y[1].a(this.a.gm())}} +A.cp.prototype={ +gao(){return this.a}} +A.f1.prototype={$iq:1} +A.eW.prototype={ +j(a,b){return this.$ti.y[1].a(J.aM(this.a,b))}, +t(a,b,c){J.pr(this.a,b,this.$ti.c.a(c))}, +ct(a,b,c){var s=this.$ti +return A.ed(J.tG(this.a,b,c),s.c,s.y[1])}, +N(a,b,c,d,e){var s=this.$ti +J.tI(this.a,b,c,A.ed(d,s.y[1],s.c),e)}, +ac(a,b,c,d){return this.N(0,b,c,d,0)}, +$iq:1, +$io:1} +A.ai.prototype={ +bx(a,b){return new A.ai(this.a,this.$ti.h("@<1>").G(b).h("ai<1,2>"))}, +gao(){return this.a}} +A.da.prototype={ +i(a){return"LateInitializationError: "+this.a}} +A.fV.prototype={ +gl(a){return this.a.length}, +j(a,b){return this.a.charCodeAt(b)}} +A.nY.prototype={ +$0(){return A.b3(null,t.H)}, +$S:10} +A.kN.prototype={} +A.q.prototype={} +A.Q.prototype={ +gq(a){var s=this +return new A.b4(s,s.gl(s),A.r(s).h("b4"))}, +gB(a){return this.gl(this)===0}, +gE(a){if(this.gl(this)===0)throw A.b(A.aw()) +return this.K(0,0)}, +gD(a){var s=this +if(s.gl(s)===0)throw A.b(A.aw()) +return s.K(0,s.gl(s)-1)}, +av(a,b){var s,r,q,p=this,o=p.gl(p) +if(b.length!==0){if(o===0)return"" +s=A.t(p.K(0,0)) +if(o!==p.gl(p))throw A.b(A.an(p)) +for(r=s,q=1;q").G(c).h("D<1,2>"))}, +kE(a,b,c){var s,r,q=this,p=q.gl(q) +for(s=b,r=0;rs)throw A.b(A.W(r,0,s,"start",null))}}, +giy(){var s=J.aC(this.a),r=this.c +if(r==null||r>s)return s +return r}, +gjy(){var s=J.aC(this.a),r=this.b +if(r>s)return s +return r}, +gl(a){var s,r=J.aC(this.a),q=this.b +if(q>=r)return 0 +s=this.c +if(s==null||s>=r)return r-q +return s-q}, +K(a,b){var s=this,r=s.gjy()+b +if(b<0||r>=s.giy())throw A.b(A.hf(b,s.gl(0),s,null,"index")) +return J.j_(s.a,r)}, +U(a,b){var s,r,q=this +A.ab(b,"count") +s=q.b+b +r=q.c +if(r!=null&&s>=r)return new A.cv(q.$ti.h("cv<1>")) +return A.b6(q.a,s,r,q.$ti.c)}, +ah(a,b){var s,r,q,p=this +A.ab(b,"count") +s=p.c +r=p.b +q=r+b +if(s==null)return A.b6(p.a,r,q,p.$ti.c) +else{if(s=o){r.d=null +return!1}r.d=p.K(q,s);++r.c +return!0}} +A.aG.prototype={ +gq(a){var s=this.a +return new A.dc(s.gq(s),this.b,A.r(this).h("dc<1,2>"))}, +gl(a){var s=this.a +return s.gl(s)}, +gB(a){var s=this.a +return s.gB(s)}, +gE(a){var s=this.a +return this.b.$1(s.gE(s))}, +gD(a){var s=this.a +return this.b.$1(s.gD(s))}, +K(a,b){var s=this.a +return this.b.$1(s.K(s,b))}} +A.cu.prototype={$iq:1} +A.dc.prototype={ +k(){var s=this,r=s.b +if(r.k()){s.a=s.c.$1(r.gm()) +return!0}s.a=null +return!1}, +gm(){var s=this.a +return s==null?this.$ti.y[1].a(s):s}} +A.D.prototype={ +gl(a){return J.aC(this.a)}, +K(a,b){return this.b.$1(J.j_(this.a,b))}} +A.aK.prototype={ +gq(a){return new A.cG(J.Z(this.a),this.b)}, +ba(a,b,c){return new A.aG(this,b,this.$ti.h("@<1>").G(c).h("aG<1,2>"))}} +A.cG.prototype={ +k(){var s,r +for(s=this.a,r=this.b;s.k();)if(r.$1(s.gm()))return!0 +return!1}, +gm(){return this.a.gm()}} +A.em.prototype={ +gq(a){return new A.ha(J.Z(this.a),this.b,B.G,this.$ti.h("ha<1,2>"))}} +A.ha.prototype={ +gm(){var s=this.d +return s==null?this.$ti.y[1].a(s):s}, +k(){var s,r,q=this,p=q.c +if(p==null)return!1 +for(s=q.a,r=q.b;!p.k();){q.d=null +if(s.k()){q.c=null +p=J.Z(r.$1(s.gm())) +q.c=p}else return!1}q.d=q.c.gm() +return!0}} +A.cE.prototype={ +gq(a){var s=this.a +return new A.hR(s.gq(s),this.b,A.r(this).h("hR<1>"))}} +A.ek.prototype={ +gl(a){var s=this.a,r=s.gl(s) +s=this.b +if(r>s)return s +return r}, +$iq:1} +A.hR.prototype={ +k(){if(--this.b>=0)return this.a.k() +this.b=-1 +return!1}, +gm(){if(this.b<0){this.$ti.c.a(null) +return null}return this.a.gm()}} +A.bJ.prototype={ +U(a,b){A.bT(b,"count") +A.ab(b,"count") +return new A.bJ(this.a,this.b+b,A.r(this).h("bJ<1>"))}, +gq(a){var s=this.a +return new A.hM(s.gq(s),this.b)}} +A.d5.prototype={ +gl(a){var s=this.a,r=s.gl(s)-this.b +if(r>=0)return r +return 0}, +U(a,b){A.bT(b,"count") +A.ab(b,"count") +return new A.d5(this.a,this.b+b,this.$ti)}, +$iq:1} +A.hM.prototype={ +k(){var s,r +for(s=this.a,r=0;r"))}, +U(a,b){A.ab(b,"count") +return this}, +ah(a,b){A.ab(b,"count") +return this}} +A.h7.prototype={ +k(){return!1}, +gm(){throw A.b(A.aw())}} +A.eR.prototype={ +gq(a){return new A.i8(J.Z(this.a),this.$ti.h("i8<1>"))}} +A.i8.prototype={ +k(){var s,r +for(s=this.a,r=this.$ti.c;s.k();)if(r.b(s.gm()))return!0 +return!1}, +gm(){return this.$ti.c.a(this.a.gm())}} +A.by.prototype={ +gl(a){return J.aC(this.a)}, +gB(a){return J.oa(this.a)}, +gE(a){return new A.ag(this.b,J.j0(this.a))}, +K(a,b){return new A.ag(b+this.b,J.j_(this.a,b))}, +ah(a,b){A.bT(b,"count") +A.ab(b,"count") +return new A.by(J.j1(this.a,b),this.b,A.r(this).h("by<1>"))}, +U(a,b){A.bT(b,"count") +A.ab(b,"count") +return new A.by(J.e7(this.a,b),b+this.b,A.r(this).h("by<1>"))}, +gq(a){return new A.eq(J.Z(this.a),this.b)}} +A.ct.prototype={ +gD(a){var s,r=this.a,q=J.a3(r),p=q.gl(r) +if(p<=0)throw A.b(A.aw()) +s=q.gD(r) +if(p!==q.gl(r))throw A.b(A.an(this)) +return new A.ag(p-1+this.b,s)}, +ah(a,b){A.bT(b,"count") +A.ab(b,"count") +return new A.ct(J.j1(this.a,b),this.b,this.$ti)}, +U(a,b){A.bT(b,"count") +A.ab(b,"count") +return new A.ct(J.e7(this.a,b),this.b+b,this.$ti)}, +$iq:1} +A.eq.prototype={ +k(){if(++this.c>=0&&this.a.k())return!0 +this.c=-2 +return!1}, +gm(){var s=this.c +return s>=0?new A.ag(this.b+s,this.a.gm()):A.E(A.aw())}} +A.en.prototype={} +A.hV.prototype={ +t(a,b,c){throw A.b(A.a4("Cannot modify an unmodifiable list"))}, +N(a,b,c,d,e){throw A.b(A.a4("Cannot modify an unmodifiable list"))}, +ac(a,b,c,d){return this.N(0,b,c,d,0)}} +A.du.prototype={} +A.eG.prototype={ +gl(a){return J.aC(this.a)}, +K(a,b){var s=this.a,r=J.a3(s) +return r.K(s,r.gl(s)-1-b)}} +A.hQ.prototype={ +gA(a){var s=this._hashCode +if(s!=null)return s +s=664597*B.a.gA(this.a)&536870911 +this._hashCode=s +return s}, +i(a){return'Symbol("'+this.a+'")'}, +T(a,b){if(b==null)return!1 +return b instanceof A.hQ&&this.a===b.a}} +A.fA.prototype={} +A.ag.prototype={$r:"+(1,2)",$s:1} +A.cR.prototype={$r:"+file,outFlags(1,2)",$s:2} +A.iE.prototype={$r:"+result,resultCode(1,2)",$s:3} +A.ef.prototype={ +i(a){return A.oq(this)}, +gcZ(){return new A.dR(this.kC(),A.r(this).h("dR>"))}, +kC(){var s=this +return function(){var r=0,q=1,p=[],o,n,m +return function $async$gcZ(a,b,c){if(b===1){p.push(c) +r=q}for(;;)switch(r){case 0:o=s.gX(),o=o.gq(o),n=A.r(s).h("aP<1,2>") +case 2:if(!o.k()){r=3 +break}m=o.gm() +r=4 +return a.b=new A.aP(m,s.j(0,m),n),1 +case 4:r=2 +break +case 3:return 0 +case 1:return a.c=p.at(-1),3}}}}, +$iap:1} +A.eg.prototype={ +gl(a){return this.b.length}, +gfm(){var s=this.$keys +if(s==null){s=Object.keys(this.a) +this.$keys=s}return s}, +a3(a){if(typeof a!="string")return!1 +if("__proto__"===a)return!1 +return this.a.hasOwnProperty(a)}, +j(a,b){if(!this.a3(b))return null +return this.b[this.a[b]]}, +ar(a,b){var s,r,q=this.gfm(),p=this.b +for(s=q.length,r=0;r"))}, +gbH(){return new A.cP(this.b,this.$ti.h("cP<2>"))}} +A.cP.prototype={ +gl(a){return this.a.length}, +gB(a){return 0===this.a.length}, +gq(a){var s=this.a +return new A.ix(s,s.length,this.$ti.h("ix<1>"))}} +A.ix.prototype={ +gm(){var s=this.d +return s==null?this.$ti.c.a(s):s}, +k(){var s=this,r=s.c +if(r>=s.b){s.d=null +return!1}s.d=s.a[r] +s.c=r+1 +return!0}} +A.kl.prototype={ +T(a,b){if(b==null)return!1 +return b instanceof A.er&&this.a.T(0,b.a)&&A.p7(this)===A.p7(b)}, +gA(a){return A.eB(this.a,A.p7(this),B.f,B.f)}, +i(a){var s=B.c.av([A.bR(this.$ti.c)],", ") +return this.a.i(0)+" with "+("<"+s+">")}} +A.er.prototype={ +$2(a,b){return this.a.$1$2(a,b,this.$ti.y[0])}, +$4(a,b,c,d){return this.a.$1$4(a,b,c,d,this.$ti.y[0])}, +$S(){return A.xn(A.nL(this.a),this.$ti)}} +A.eH.prototype={} +A.lt.prototype={ +aw(a){var s,r,q=this,p=new RegExp(q.a).exec(a) +if(p==null)return null +s=Object.create(null) +r=q.b +if(r!==-1)s.arguments=p[r+1] +r=q.c +if(r!==-1)s.argumentsExpr=p[r+1] +r=q.d +if(r!==-1)s.expr=p[r+1] +r=q.e +if(r!==-1)s.method=p[r+1] +r=q.f +if(r!==-1)s.receiver=p[r+1] +return s}} +A.eA.prototype={ +i(a){return"Null check operator used on a null value"}} +A.hn.prototype={ +i(a){var s,r=this,q="NoSuchMethodError: method not found: '",p=r.b +if(p==null)return"NoSuchMethodError: "+r.a +s=r.c +if(s==null)return q+p+"' ("+r.a+")" +return q+p+"' on '"+s+"' ("+r.a+")"}} +A.hU.prototype={ +i(a){var s=this.a +return s.length===0?"Error":"Error: "+s}} +A.hD.prototype={ +i(a){return"Throw of null ('"+(this.a===null?"null":"undefined")+"' from JavaScript)"}, +$ia6:1} +A.el.prototype={} +A.fm.prototype={ +i(a){var s,r=this.b +if(r!=null)return r +r=this.a +s=r!==null&&typeof r==="object"?r.stack:null +return this.b=s==null?"":s}, +$ia_:1} +A.cq.prototype={ +i(a){var s=this.constructor,r=s==null?null:s.name +return"Closure '"+A.rO(r==null?"unknown":r)+"'"}, +glY(){return this}, +$C:"$1", +$R:1, +$D:null} +A.jg.prototype={$C:"$0",$R:0} +A.jh.prototype={$C:"$2",$R:2} +A.lj.prototype={} +A.l9.prototype={ +i(a){var s=this.$static_name +if(s==null)return"Closure of unknown static method" +return"Closure '"+A.rO(s)+"'"}} +A.eb.prototype={ +T(a,b){if(b==null)return!1 +if(this===b)return!0 +if(!(b instanceof A.eb))return!1 +return this.$_target===b.$_target&&this.a===b.a}, +gA(a){return(A.pb(this.a)^A.eE(this.$_target))>>>0}, +i(a){return"Closure '"+this.$_name+"' of "+("Instance of '"+A.hH(this.a)+"'")}} +A.hK.prototype={ +i(a){return"RuntimeError: "+this.a}} +A.bA.prototype={ +gl(a){return this.a}, +gB(a){return this.a===0}, +gX(){return new A.bB(this,A.r(this).h("bB<1>"))}, +gbH(){return new A.ew(this,A.r(this).h("ew<2>"))}, +gcZ(){return new A.ev(this,A.r(this).h("ev<1,2>"))}, +a3(a){var s,r +if(typeof a=="string"){s=this.b +if(s==null)return!1 +return s[a]!=null}else if(typeof a=="number"&&(a&0x3fffffff)===a){r=this.c +if(r==null)return!1 +return r[a]!=null}else return this.kH(a)}, +kH(a){var s=this.d +if(s==null)return!1 +return this.d4(s[this.d3(a)],a)>=0}, +aI(a,b){b.ar(0,new A.ks(this))}, +j(a,b){var s,r,q,p,o=null +if(typeof b=="string"){s=this.b +if(s==null)return o +r=s[b] +q=r==null?o:r.b +return q}else if(typeof b=="number"&&(b&0x3fffffff)===b){p=this.c +if(p==null)return o +r=p[b] +q=r==null?o:r.b +return q}else return this.kI(b)}, +kI(a){var s,r,q=this.d +if(q==null)return null +s=q[this.d3(a)] +r=this.d4(s,a) +if(r<0)return null +return s[r].b}, +t(a,b,c){var s,r,q=this +if(typeof b=="string"){s=q.b +q.eZ(s==null?q.b=q.dY():s,b,c)}else if(typeof b=="number"&&(b&0x3fffffff)===b){r=q.c +q.eZ(r==null?q.c=q.dY():r,b,c)}else q.kK(b,c)}, +kK(a,b){var s,r,q,p=this,o=p.d +if(o==null)o=p.d=p.dY() +s=p.d3(a) +r=o[s] +if(r==null)o[s]=[p.du(a,b)] +else{q=p.d4(r,a) +if(q>=0)r[q].b=b +else r.push(p.du(a,b))}}, +hk(a,b){var s,r,q=this +if(q.a3(a)){s=q.j(0,a) +return s==null?A.r(q).y[1].a(s):s}r=b.$0() +q.t(0,a,r) +return r}, +F(a,b){var s=this +if(typeof b=="string")return s.f_(s.b,b) +else if(typeof b=="number"&&(b&0x3fffffff)===b)return s.f_(s.c,b) +else return s.kJ(b)}, +kJ(a){var s,r,q,p,o=this,n=o.d +if(n==null)return null +s=o.d3(a) +r=n[s] +q=o.d4(r,a) +if(q<0)return null +p=r.splice(q,1)[0] +o.f0(p) +if(r.length===0)delete n[s] +return p.b}, +c3(a){var s=this +if(s.a>0){s.b=s.c=s.d=s.e=s.f=null +s.a=0 +s.dt()}}, +ar(a,b){var s=this,r=s.e,q=s.r +while(r!=null){b.$2(r.a,r.b) +if(q!==s.r)throw A.b(A.an(s)) +r=r.c}}, +eZ(a,b,c){var s=a[b] +if(s==null)a[b]=this.du(b,c) +else s.b=c}, +f_(a,b){var s +if(a==null)return null +s=a[b] +if(s==null)return null +this.f0(s) +delete a[b] +return s.b}, +dt(){this.r=this.r+1&1073741823}, +du(a,b){var s,r=this,q=new A.kv(a,b) +if(r.e==null)r.e=r.f=q +else{s=r.f +s.toString +q.d=s +r.f=s.c=q}++r.a +r.dt() +return q}, +f0(a){var s=this,r=a.d,q=a.c +if(r==null)s.e=q +else r.c=q +if(q==null)s.f=r +else q.d=r;--s.a +s.dt()}, +d3(a){return J.aE(a)&1073741823}, +d4(a,b){var s,r +if(a==null)return-1 +s=a.length +for(r=0;r"]=s +delete s[""] +return s}} +A.ks.prototype={ +$2(a,b){this.a.t(0,a,b)}, +$S(){return A.r(this.a).h("~(1,2)")}} +A.kv.prototype={} +A.bB.prototype={ +gl(a){return this.a.a}, +gB(a){return this.a.a===0}, +gq(a){var s=this.a +return new A.hr(s,s.r,s.e)}} +A.hr.prototype={ +gm(){return this.d}, +k(){var s,r=this,q=r.a +if(r.b!==q.r)throw A.b(A.an(q)) +s=r.c +if(s==null){r.d=null +return!1}else{r.d=s.a +r.c=s.c +return!0}}} +A.ew.prototype={ +gl(a){return this.a.a}, +gB(a){return this.a.a===0}, +gq(a){var s=this.a +return new A.db(s,s.r,s.e)}} +A.db.prototype={ +gm(){return this.d}, +k(){var s,r=this,q=r.a +if(r.b!==q.r)throw A.b(A.an(q)) +s=r.c +if(s==null){r.d=null +return!1}else{r.d=s.b +r.c=s.c +return!0}}} +A.ev.prototype={ +gl(a){return this.a.a}, +gB(a){return this.a.a===0}, +gq(a){var s=this.a +return new A.hq(s,s.r,s.e,this.$ti.h("hq<1,2>"))}} +A.hq.prototype={ +gm(){var s=this.d +s.toString +return s}, +k(){var s,r=this,q=r.a +if(r.b!==q.r)throw A.b(A.an(q)) +s=r.c +if(s==null){r.d=null +return!1}else{r.d=new A.aP(s.a,s.b,r.$ti.h("aP<1,2>")) +r.c=s.c +return!0}}} +A.nS.prototype={ +$1(a){return this.a(a)}, +$S:74} +A.nT.prototype={ +$2(a,b){return this.a(a,b)}, +$S:118} +A.nU.prototype={ +$1(a){return this.a(a)}, +$S:59} +A.fi.prototype={ +i(a){return this.fP(!1)}, +fP(a){var s,r,q,p,o,n=this.iA(),m=this.fj(),l=(a?"Record ":"")+"(" +for(s=n.length,r="",q=0;q0;){--q;--s +k[q]=r[s]}}return A.aO(k,t.K)}} +A.iD.prototype={ +fj(){return[this.a,this.b]}, +T(a,b){if(b==null)return!1 +return b instanceof A.iD&&this.$s===b.$s&&J.am(this.a,b.a)&&J.am(this.b,b.b)}, +gA(a){return A.eB(this.$s,this.a,this.b,B.f)}} +A.cx.prototype={ +i(a){return"RegExp/"+this.a+"/"+this.b.flags}, +gfq(){var s=this,r=s.c +if(r!=null)return r +r=s.b +return s.c=A.om(s.a,r.multiline,!r.ignoreCase,r.unicode,r.dotAll,"g")}, +giQ(){var s=this,r=s.d +if(r!=null)return r +r=s.b +return s.d=A.om(s.a,r.multiline,!r.ignoreCase,r.unicode,r.dotAll,"y")}, +ij(){var s,r=this.a +if(!B.a.H(r,"("))return!1 +s=this.b.unicode?"u":"" +return new RegExp("(?:)|"+r,s).exec("").length>1}, +a8(a){var s=this.b.exec(a) +if(s==null)return null +return new A.dJ(s)}, +cS(a,b,c){var s=b.length +if(c>s)throw A.b(A.W(c,0,s,null,null)) +return new A.i9(this,b,c)}, +ed(a,b){return this.cS(0,b,0)}, +ff(a,b){var s,r=this.gfq() +r.lastIndex=b +s=r.exec(a) +if(s==null)return null +return new A.dJ(s)}, +iz(a,b){var s,r=this.giQ() +r.lastIndex=b +s=r.exec(a) +if(s==null)return null +return new A.dJ(s)}, +he(a,b,c){if(c<0||c>b.length)throw A.b(A.W(c,0,b.length,null,null)) +return this.iz(b,c)}} +A.dJ.prototype={ +gcv(){return this.b.index}, +gbz(){var s=this.b +return s.index+s[0].length}, +j(a,b){return this.b[b]}, +aM(a){var s,r=this.b.groups +if(r!=null){s=r[a] +if(s!=null||a in r)return s}throw A.b(A.ad(a,"name","Not a capture group name"))}, +$iex:1, +$ihI:1} +A.i9.prototype={ +gq(a){return new A.m5(this.a,this.b,this.c)}} +A.m5.prototype={ +gm(){var s=this.d +return s==null?t.cz.a(s):s}, +k(){var s,r,q,p,o,n,m=this,l=m.b +if(l==null)return!1 +s=m.c +r=l.length +if(s<=r){q=m.a +p=q.ff(l,s) +if(p!=null){m.d=p +o=p.gbz() +if(p.b.index===o){s=!1 +if(q.b.unicode){q=m.c +n=q+1 +if(n=55296&&r<=56319){s=l.charCodeAt(n) +s=s>=56320&&s<=57343}}}o=(s?o+1:o)+1}m.c=o +return!0}}m.b=m.d=null +return!1}} +A.ds.prototype={ +gbz(){return this.a+this.c.length}, +j(a,b){if(b!==0)throw A.b(A.kJ(b,null)) +return this.c}, +$iex:1, +gcv(){return this.a}} +A.iM.prototype={ +gq(a){return new A.ne(this.a,this.b,this.c)}, +gE(a){var s=this.b,r=this.a.indexOf(s,this.c) +if(r>=0)return new A.ds(r,s) +throw A.b(A.aw())}} +A.ne.prototype={ +k(){var s,r,q=this,p=q.c,o=q.b,n=o.length,m=q.a,l=m.length +if(p+n>l){q.d=null +return!1}s=m.indexOf(o,p) +if(s<0){q.c=l+1 +q.d=null +return!1}r=s+n +q.d=new A.ds(s,o) +q.c=r===q.c?r+1:r +return!0}, +gm(){var s=this.d +s.toString +return s}} +A.ml.prototype={ +ae(){var s=this.b +if(s===this)throw A.b(A.pU(this.a)) +return s}} +A.de.prototype={ +gS(a){return B.aV}, +fV(a,b,c){A.fB(a,b,c) +return c==null?new Uint8Array(a,b):new Uint8Array(a,b,c)}, +jW(a,b,c){var s +A.fB(a,b,c) +s=new DataView(a,b) +return s}, +fU(a){return this.jW(a,0,null)}, +$iK:1, +$ico:1} +A.dd.prototype={$idd:1} +A.ey.prototype={ +gaX(a){if(((a.$flags|0)&2)!==0)return new A.iS(a.buffer) +else return a.buffer}, +iM(a,b,c,d){var s=A.W(b,0,c,d,null) +throw A.b(s)}, +f6(a,b,c,d){if(b>>>0!==b||b>c)this.iM(a,b,c,d)}} +A.iS.prototype={ +fV(a,b,c){var s=A.bE(this.a,b,c) +s.$flags=3 +return s}, +fU(a){var s=A.pV(this.a,0,null) +s.$flags=3 +return s}, +$ico:1} +A.cz.prototype={ +gS(a){return B.aW}, +$iK:1, +$icz:1, +$ioc:1} +A.dg.prototype={ +gl(a){return a.length}, +fI(a,b,c,d,e){var s,r,q=a.length +this.f6(a,b,q,"start") +this.f6(a,c,q,"end") +if(b>c)throw A.b(A.W(b,0,c,null,null)) +s=c-b +if(e<0)throw A.b(A.J(e,null)) +r=d.length +if(r-e0){s=Date.now()-r.c +if(s>(p+1)*o)p=B.b.eY(s,o)}q.c=p +r.d.$1(q)}, +$S:3} +A.ia.prototype={ +O(a){var s,r=this +if(a==null)a=r.$ti.c.a(a) +if(!r.b)r.a.b3(a) +else{s=r.a +if(r.$ti.h("C<1>").b(a))s.f5(a) +else s.bL(a)}}, +by(a,b){var s=this.a +if(this.b)s.V(new A.U(a,b)) +else s.aR(new A.U(a,b))}} +A.nu.prototype={ +$1(a){return this.a.$2(0,a)}, +$S:15} +A.nv.prototype={ +$2(a,b){this.a.$2(1,new A.el(a,b))}, +$S:40} +A.nJ.prototype={ +$2(a,b){this.a(a,b)}, +$S:41} +A.iN.prototype={ +gm(){return this.b}, +jf(a,b){var s,r,q +a=a +b=b +s=this.a +for(;;)try{r=s(this,a,b) +return r}catch(q){b=q +a=1}}, +k(){var s,r,q,p,o=this,n=null,m=0 +for(;;){s=o.d +if(s!=null)try{if(s.k()){o.b=s.gm() +return!0}else o.d=null}catch(r){n=r +m=1 +o.d=null}q=o.jf(m,n) +if(1===q)return!0 +if(0===q){o.b=null +p=o.e +if(p==null||p.length===0){o.a=A.qM +return!1}o.a=p.pop() +m=0 +n=null +continue}if(2===q){m=0 +n=null +continue}if(3===q){n=o.c +o.c=null +p=o.e +if(p==null||p.length===0){o.b=null +o.a=A.qM +throw n +return!1}o.a=p.pop() +m=1 +continue}throw A.b(A.A("sync*"))}return!1}, +lZ(a){var s,r,q=this +if(a instanceof A.dR){s=a.a() +r=q.e +if(r==null)r=q.e=[] +r.push(q.a) +q.a=s +return 2}else{q.d=J.Z(a) +return 2}}} +A.dR.prototype={ +gq(a){return new A.iN(this.a())}} +A.U.prototype={ +i(a){return A.t(this.a)}, +$iL:1, +gaP(){return this.b}} +A.eV.prototype={} +A.cJ.prototype={ +al(){}, +am(){}} +A.cI.prototype={ +gbN(){return this.c<4}, +fD(a){var s=a.CW,r=a.ch +if(s==null)this.d=r +else s.ch=r +if(r==null)this.e=s +else r.CW=s +a.CW=a +a.ch=a}, +fJ(a,b,c,d){var s,r,q,p,o,n,m,l,k,j=this +if((j.c&4)!==0){s=$.m +r=new A.f0(s) +A.pe(r.gfs()) +if(c!=null)r.c=s.az(c,t.H) +return r}s=A.r(j) +r=$.m +q=d?1:0 +p=b!=null?32:0 +o=A.ih(r,a,s.c) +n=A.ii(r,b) +m=c==null?A.rw():c +l=new A.cJ(j,o,n,r.az(m,t.H),r,q|p,s.h("cJ<1>")) +l.CW=l +l.ch=l +l.ay=j.c&1 +k=j.e +j.e=l +l.ch=null +l.CW=k +if(k==null)j.d=l +else k.ch=l +if(j.d===l)A.iV(j.a) +return l}, +fv(a){var s,r=this +A.r(r).h("cJ<1>").a(a) +if(a.ch===a)return null +s=a.ay +if((s&2)!==0)a.ay=s|4 +else{r.fD(a) +if((r.c&2)===0&&r.d==null)r.dA()}return null}, +fw(a){}, +fz(a){}, +bK(){if((this.c&4)!==0)return new A.aI("Cannot add new events after calling close") +return new A.aI("Cannot add new events while doing an addStream")}, +v(a,b){if(!this.gbN())throw A.b(this.bK()) +this.b5(b)}, +a2(a,b){var s +if(!this.gbN())throw A.b(this.bK()) +s=A.nB(a,b) +this.b7(s.a,s.b)}, +n(){var s,r,q=this +if((q.c&4)!==0){s=q.r +s.toString +return s}if(!q.gbN())throw A.b(q.bK()) +q.c|=4 +r=q.r +if(r==null)r=q.r=new A.n($.m,t.D) +q.b6() +return r}, +dO(a){var s,r,q,p=this,o=p.c +if((o&2)!==0)throw A.b(A.A(u.o)) +s=p.d +if(s==null)return +r=o&1 +p.c=o^3 +while(s!=null){o=s.ay +if((o&1)===r){s.ay=o|2 +a.$1(s) +o=s.ay^=1 +q=s.ch +if((o&4)!==0)p.fD(s) +s.ay&=4294967293 +s=q}else s=s.ch}p.c&=4294967293 +if(p.d==null)p.dA()}, +dA(){if((this.c&4)!==0){var s=this.r +if((s.a&30)===0)s.b3(null)}A.iV(this.b)}, +$iae:1} +A.fp.prototype={ +gbN(){return A.cI.prototype.gbN.call(this)&&(this.c&2)===0}, +bK(){if((this.c&2)!==0)return new A.aI(u.o) +return this.hU()}, +b5(a){var s=this,r=s.d +if(r==null)return +if(r===s.e){s.c|=2 +r.aQ(a) +s.c&=4294967293 +if(s.d==null)s.dA() +return}s.dO(new A.nf(s,a))}, +b7(a,b){if(this.d==null)return +this.dO(new A.nh(this,a,b))}, +b6(){var s=this +if(s.d!=null)s.dO(new A.ng(s)) +else s.r.b3(null)}} +A.nf.prototype={ +$1(a){a.aQ(this.b)}, +$S(){return this.a.$ti.h("~(af<1>)")}} +A.nh.prototype={ +$1(a){a.a7(this.b,this.c)}, +$S(){return this.a.$ti.h("~(af<1>)")}} +A.ng.prototype={ +$1(a){a.bo()}, +$S(){return this.a.$ti.h("~(af<1>)")}} +A.kc.prototype={ +$0(){this.c.a(null) +this.b.b4(null)}, +$S:0} +A.ke.prototype={ +$2(a,b){var s=this,r=s.a,q=--r.b +if(r.a!=null){r.a=null +r.d=a +r.c=b +if(q===0||s.c)s.d.V(new A.U(a,b))}else if(q===0&&!s.c){q=r.d +q.toString +r=r.c +r.toString +s.d.V(new A.U(q,r))}}, +$S:6} +A.kd.prototype={ +$1(a){var s,r,q,p,o,n,m=this,l=m.a,k=--l.b,j=l.a +if(j!=null){J.pr(j,m.b,a) +if(J.am(k,0)){l=m.d +s=A.f([],l.h("u<0>")) +for(q=j,p=q.length,o=0;o")) +for(r=m.b,q=r.length,p=0;p")) +for(n=r.length,p=0;p1 +if(r)s="("+s+" errors)" +else s="" +return q+s+": "+A.t(p.a)}, +gaP(){var s=this.c +s=s==null?null:s.b +return s==null?A.L.prototype.gaP.call(this):s}} +A.f7.prototype={ +jD(a){this.a.bf(new A.mD(this,a),new A.mE(this,a),t.P)}} +A.mD.prototype={ +$1(a){this.a.b=a +this.b.$1(0)}, +$S(){return this.a.$ti.h("N(1)")}} +A.mE.prototype={ +$2(a,b){this.a.c=new A.U(a,b) +this.b.$1(1)}, +$S:29} +A.mC.prototype={ +$1(a){var s=this.a,r=s.a+=a +if(++s.b===this.b.length)this.c.$1(r)}, +$S:4} +A.dB.prototype={ +by(a,b){if((this.a.a&30)!==0)throw A.b(A.A("Future already completed")) +this.V(A.nB(a,b))}, +ag(a){return this.by(a,null)}} +A.a7.prototype={ +O(a){var s=this.a +if((s.a&30)!==0)throw A.b(A.A("Future already completed")) +s.b3(a)}, +aJ(){return this.O(null)}, +V(a){this.a.aR(a)}} +A.a1.prototype={ +O(a){var s=this.a +if((s.a&30)!==0)throw A.b(A.A("Future already completed")) +s.b4(a)}, +aJ(){return this.O(null)}, +V(a){this.a.V(a)}} +A.cg.prototype={ +kU(a){if((this.c&15)!==6)return!0 +return this.b.b.be(this.d,a.a,t.y,t.K)}, +kF(a){var s,r=this.e,q=null,p=t.z,o=t.K,n=a.a,m=this.b.b +if(t._.b(r))q=m.eK(r,n,a.b,p,o,t.l) +else q=m.be(r,n,p,o) +try{p=q +return p}catch(s){if(t.eK.b(A.H(s))){if((this.c&1)!==0)throw A.b(A.J("The error handler of Future.then must return a value of the returned future's type","onError")) +throw A.b(A.J("The error handler of Future.catchError must return a value of the future's type","onError"))}else throw s}}} +A.n.prototype={ +bf(a,b,c){var s,r,q=$.m +if(q===B.d){if(b!=null&&!t._.b(b)&&!t.bI.b(b))throw A.b(A.ad(b,"onError",u.c))}else{a=q.bb(a,c.h("0/"),this.$ti.c) +if(b!=null)b=A.ws(b,q)}s=new A.n($.m,c.h("n<0>")) +r=b==null?1:3 +this.cB(new A.cg(s,r,a,b,this.$ti.h("@<1>").G(c).h("cg<1,2>"))) +return s}, +bG(a,b){return this.bf(a,null,b)}, +fN(a,b,c){var s=new A.n($.m,c.h("n<0>")) +this.cB(new A.cg(s,19,a,b,this.$ti.h("@<1>").G(c).h("cg<1,2>"))) +return s}, +ai(a){var s=this.$ti,r=$.m,q=new A.n(r,s) +if(r!==B.d)a=r.az(a,t.z) +this.cB(new A.cg(q,8,a,null,s.h("cg<1,1>"))) +return q}, +jr(a){this.a=this.a&1|16 +this.c=a}, +cC(a){this.a=a.a&30|this.a&1 +this.c=a.c}, +cB(a){var s=this,r=s.a +if(r<=3){a.a=s.c +s.c=a}else{if((r&4)!==0){r=s.c +if((r.a&24)===0){r.cB(a) +return}s.cC(r)}s.b.b1(new A.mF(s,a))}}, +ft(a){var s,r,q,p,o,n=this,m={} +m.a=a +if(a==null)return +s=n.a +if(s<=3){r=n.c +n.c=a +if(r!=null){q=a.a +for(p=a;q!=null;p=q,q=o)o=q.a +p.a=r}}else{if((s&4)!==0){s=n.c +if((s.a&24)===0){s.ft(a) +return}n.cC(s)}m.a=n.cJ(a) +n.b.b1(new A.mK(m,n))}}, +bS(){var s=this.c +this.c=null +return this.cJ(s)}, +cJ(a){var s,r,q +for(s=a,r=null;s!=null;r=s,s=q){q=s.a +s.a=r}return r}, +b4(a){var s,r=this +if(r.$ti.h("C<1>").b(a))A.mI(a,r,!0) +else{s=r.bS() +r.a=8 +r.c=a +A.cM(r,s)}}, +bL(a){var s=this,r=s.bS() +s.a=8 +s.c=a +A.cM(s,r)}, +ih(a){var s,r,q,p=this +if((a.a&16)!==0){s=p.b +r=a.b +s=!(s===r||s.gaK()===r.gaK())}else s=!1 +if(s)return +q=p.bS() +p.cC(a) +A.cM(p,q)}, +V(a){var s=this.bS() +this.jr(a) +A.cM(this,s)}, +ig(a,b){this.V(new A.U(a,b))}, +b3(a){if(this.$ti.h("C<1>").b(a)){this.f5(a) +return}this.f4(a)}, +f4(a){this.a^=2 +this.b.b1(new A.mH(this,a))}, +f5(a){A.mI(a,this,!1) +return}, +aR(a){this.a^=2 +this.b.b1(new A.mG(this,a))}, +$iC:1} +A.mF.prototype={ +$0(){A.cM(this.a,this.b)}, +$S:0} +A.mK.prototype={ +$0(){A.cM(this.b,this.a.a)}, +$S:0} +A.mJ.prototype={ +$0(){A.mI(this.a.a,this.b,!0)}, +$S:0} +A.mH.prototype={ +$0(){this.a.bL(this.b)}, +$S:0} +A.mG.prototype={ +$0(){this.a.V(this.b)}, +$S:0} +A.mN.prototype={ +$0(){var s,r,q,p,o,n,m,l,k=this,j=null +try{q=k.a.a +j=q.b.b.bd(q.d,t.z)}catch(p){s=A.H(p) +r=A.a5(p) +if(k.c&&k.b.a.c.a===s){q=k.a +q.c=k.b.a.c}else{q=s +o=r +if(o==null)o=A.fP(q) +n=k.a +n.c=new A.U(q,o) +q=n}q.b=!0 +return}if(j instanceof A.n&&(j.a&24)!==0){if((j.a&16)!==0){q=k.a +q.c=j.c +q.b=!0}return}if(j instanceof A.n){m=k.b.a +l=new A.n(m.b,m.$ti) +j.bf(new A.mO(l,m),new A.mP(l),t.H) +q=k.a +q.c=l +q.b=!1}}, +$S:0} +A.mO.prototype={ +$1(a){this.a.ih(this.b)}, +$S:27} +A.mP.prototype={ +$2(a,b){this.a.V(new A.U(a,b))}, +$S:29} +A.mM.prototype={ +$0(){var s,r,q,p,o,n +try{q=this.a +p=q.a +o=p.$ti +q.c=p.b.b.be(p.d,this.b,o.h("2/"),o.c)}catch(n){s=A.H(n) +r=A.a5(n) +q=s +p=r +if(p==null)p=A.fP(q) +o=this.a +o.c=new A.U(q,p) +o.b=!0}}, +$S:0} +A.mL.prototype={ +$0(){var s,r,q,p,o,n,m,l=this +try{s=l.a.a.c +p=l.b +if(p.a.kU(s)&&p.a.e!=null){p.c=p.a.kF(s) +p.b=!1}}catch(o){r=A.H(o) +q=A.a5(o) +p=l.a.a.c +if(p.a===r){n=l.b +n.c=p +p=n}else{p=r +n=q +if(n==null)n=A.fP(p) +m=l.b +m.c=new A.U(p,n) +p=m}p.b=!0}}, +$S:0} +A.ib.prototype={} +A.X.prototype={ +gl(a){var s={},r=new A.n($.m,t.gR) +s.a=0 +this.P(new A.lg(s,this),!0,new A.lh(s,r),r.gdF()) +return r}, +gE(a){var s=new A.n($.m,A.r(this).h("n")),r=this.P(null,!0,new A.le(s),s.gdF()) +r.cd(new A.lf(this,r,s)) +return s}, +en(a,b){var s=new A.n($.m,A.r(this).h("n")),r=this.P(null,!0,new A.lc(null,s),s.gdF()) +r.cd(new A.ld(this,b,r,s)) +return s}} +A.lg.prototype={ +$1(a){++this.a.a}, +$S(){return A.r(this.b).h("~(X.T)")}} +A.lh.prototype={ +$0(){this.b.b4(this.a.a)}, +$S:0} +A.le.prototype={ +$0(){var s,r=A.l8(),q=new A.aI("No element") +A.eF(q,r) +s=A.dY(q,r) +if(s==null)s=new A.U(q,r) +this.a.V(s)}, +$S:0} +A.lf.prototype={ +$1(a){A.r7(this.b,this.c,a)}, +$S(){return A.r(this.a).h("~(X.T)")}} +A.lc.prototype={ +$0(){var s,r=A.l8(),q=new A.aI("No element") +A.eF(q,r) +s=A.dY(q,r) +if(s==null)s=new A.U(q,r) +this.b.V(s)}, +$S:0} +A.ld.prototype={ +$1(a){var s=this.c,r=this.d +A.wy(new A.la(this.b,a),new A.lb(s,r,a),A.vU(s,r))}, +$S(){return A.r(this.a).h("~(X.T)")}} +A.la.prototype={ +$0(){return this.a.$1(this.b)}, +$S:31} +A.lb.prototype={ +$1(a){if(a)A.r7(this.a,this.b,this.c)}, +$S:71} +A.hP.prototype={} +A.cS.prototype={ +gj2(){if((this.b&8)===0)return this.a +return this.a.ge8()}, +dL(){var s,r=this +if((r.b&8)===0){s=r.a +return s==null?r.a=new A.fh():s}s=r.a.ge8() +return s}, +gaV(){var s=this.a +return(this.b&8)!==0?s.ge8():s}, +dw(){if((this.b&4)!==0)return new A.aI("Cannot add event after closing") +return new A.aI("Cannot add event while adding a stream")}, +fc(){var s=this.c +if(s==null)s=this.c=(this.b&2)!==0?$.cm():new A.n($.m,t.D) +return s}, +v(a,b){var s=this,r=s.b +if(r>=4)throw A.b(s.dw()) +if((r&1)!==0)s.b5(b) +else if((r&3)===0)s.dL().v(0,new A.dD(b))}, +a2(a,b){var s,r,q=this +if(q.b>=4)throw A.b(q.dw()) +s=A.nB(a,b) +a=s.a +b=s.b +r=q.b +if((r&1)!==0)q.b7(a,b) +else if((r&3)===0)q.dL().v(0,new A.eZ(a,b))}, +jU(a){return this.a2(a,null)}, +n(){var s=this,r=s.b +if((r&4)!==0)return s.fc() +if(r>=4)throw A.b(s.dw()) +r=s.b=r|4 +if((r&1)!==0)s.b6() +else if((r&3)===0)s.dL().v(0,B.v) +return s.fc()}, +fJ(a,b,c,d){var s,r,q,p=this +if((p.b&3)!==0)throw A.b(A.A("Stream has already been listened to.")) +s=A.v6(p,a,b,c,d,A.r(p).c) +r=p.gj2() +if(((p.b|=1)&8)!==0){q=p.a +q.se8(s) +q.bc()}else p.a=s +s.js(r) +s.dP(new A.nc(p)) +return s}, +fv(a){var s,r,q,p,o,n,m,l=this,k=null +if((l.b&8)!==0)k=l.a.J() +l.a=null +l.b=l.b&4294967286|2 +s=l.r +if(s!=null)if(k==null)try{r=s.$0() +if(r instanceof A.n)k=r}catch(o){q=A.H(o) +p=A.a5(o) +n=new A.n($.m,t.D) +n.aR(new A.U(q,p)) +k=n}else k=k.ai(s) +m=new A.nb(l) +if(k!=null)k=k.ai(m) +else m.$0() +return k}, +fw(a){if((this.b&8)!==0)this.a.bD() +A.iV(this.e)}, +fz(a){if((this.b&8)!==0)this.a.bc() +A.iV(this.f)}, +$iae:1} +A.nc.prototype={ +$0(){A.iV(this.a.d)}, +$S:0} +A.nb.prototype={ +$0(){var s=this.a.c +if(s!=null&&(s.a&30)===0)s.b3(null)}, +$S:0} +A.iO.prototype={ +b5(a){this.gaV().aQ(a)}, +b7(a,b){this.gaV().a7(a,b)}, +b6(){this.gaV().bo()}} +A.ic.prototype={ +b5(a){this.gaV().bn(new A.dD(a))}, +b7(a,b){this.gaV().bn(new A.eZ(a,b))}, +b6(){this.gaV().bn(B.v)}} +A.dA.prototype={} +A.dS.prototype={} +A.at.prototype={ +gA(a){return(A.eE(this.a)^892482866)>>>0}, +T(a,b){if(b==null)return!1 +if(this===b)return!0 +return b instanceof A.at&&b.a===this.a}} +A.cf.prototype={ +cH(){return this.w.fv(this)}, +al(){this.w.fw(this)}, +am(){this.w.fz(this)}} +A.dP.prototype={ +v(a,b){this.a.v(0,b)}, +a2(a,b){this.a.a2(a,b)}, +n(){return this.a.n()}, +$iae:1} +A.af.prototype={ +js(a){var s=this +if(a==null)return +s.r=a +if(a.c!=null){s.e=(s.e|128)>>>0 +a.cu(s)}}, +cd(a){this.a=A.ih(this.d,a,A.r(this).h("af.T"))}, +eG(a){var s=this +s.e=(s.e&4294967263)>>>0 +s.b=A.ii(s.d,a)}, +bD(){var s,r,q=this,p=q.e +if((p&8)!==0)return +s=(p+256|4)>>>0 +q.e=s +if(p<256){r=q.r +if(r!=null)if(r.a===1)r.a=3}if((p&4)===0&&(s&64)===0)q.dP(q.gbO())}, +bc(){var s=this,r=s.e +if((r&8)!==0)return +if(r>=256){r=s.e=r-256 +if(r<256)if((r&128)!==0&&s.r.c!=null)s.r.cu(s) +else{r=(r&4294967291)>>>0 +s.e=r +if((r&64)===0)s.dP(s.gbP())}}}, +J(){var s=this,r=(s.e&4294967279)>>>0 +s.e=r +if((r&8)===0)s.dB() +r=s.f +return r==null?$.cm():r}, +dB(){var s,r=this,q=r.e=(r.e|8)>>>0 +if((q&128)!==0){s=r.r +if(s.a===1)s.a=3}if((q&64)===0)r.r=null +r.f=r.cH()}, +aQ(a){var s=this.e +if((s&8)!==0)return +if(s<64)this.b5(a) +else this.bn(new A.dD(a))}, +a7(a,b){var s +if(t.C.b(a))A.eF(a,b) +s=this.e +if((s&8)!==0)return +if(s<64)this.b7(a,b) +else this.bn(new A.eZ(a,b))}, +bo(){var s=this,r=s.e +if((r&8)!==0)return +r=(r|2)>>>0 +s.e=r +if(r<64)s.b6() +else s.bn(B.v)}, +al(){}, +am(){}, +cH(){return null}, +bn(a){var s,r=this,q=r.r +if(q==null)q=r.r=new A.fh() +q.v(0,a) +s=r.e +if((s&128)===0){s=(s|128)>>>0 +r.e=s +if(s<256)q.cu(r)}}, +b5(a){var s=this,r=s.e +s.e=(r|64)>>>0 +s.d.cm(s.a,a,A.r(s).h("af.T")) +s.e=(s.e&4294967231)>>>0 +s.dC((r&4)!==0)}, +b7(a,b){var s,r=this,q=r.e,p=new A.mk(r,a,b) +if((q&1)!==0){r.e=(q|16)>>>0 +r.dB() +s=r.f +if(s!=null&&s!==$.cm())s.ai(p) +else p.$0()}else{p.$0() +r.dC((q&4)!==0)}}, +b6(){var s,r=this,q=new A.mj(r) +r.dB() +r.e=(r.e|16)>>>0 +s=r.f +if(s!=null&&s!==$.cm())s.ai(q) +else q.$0()}, +dP(a){var s=this,r=s.e +s.e=(r|64)>>>0 +a.$0() +s.e=(s.e&4294967231)>>>0 +s.dC((r&4)!==0)}, +dC(a){var s,r,q=this,p=q.e +if((p&128)!==0&&q.r.c==null){p=q.e=(p&4294967167)>>>0 +s=!1 +if((p&4)!==0)if(p<256){s=q.r +s=s==null?null:s.c==null +s=s!==!1}if(s){p=(p&4294967291)>>>0 +q.e=p}}for(;;a=r){if((p&8)!==0){q.r=null +return}r=(p&4)!==0 +if(a===r)break +q.e=(p^64)>>>0 +if(r)q.al() +else q.am() +p=(q.e&4294967231)>>>0 +q.e=p}if((p&128)!==0&&p<256)q.r.cu(q)}} +A.mk.prototype={ +$0(){var s,r,q,p=this.a,o=p.e +if((o&8)!==0&&(o&16)===0)return +p.e=(o|64)>>>0 +s=p.b +o=this.b +r=t.K +q=p.d +if(t.da.b(s))q.hr(s,o,this.c,r,t.l) +else q.cm(s,o,r) +p.e=(p.e&4294967231)>>>0}, +$S:0} +A.mj.prototype={ +$0(){var s=this.a,r=s.e +if((r&16)===0)return +s.e=(r|74)>>>0 +s.d.cl(s.c) +s.e=(s.e&4294967231)>>>0}, +$S:0} +A.dN.prototype={ +P(a,b,c,d){return this.a.fJ(a,d,c,b===!0)}, +b_(a,b,c){return this.P(a,null,b,c)}, +kO(a){return this.P(a,null,null,null)}, +eB(a,b){return this.P(a,null,b,null)}} +A.il.prototype={ +gcc(){return this.a}, +scc(a){return this.a=a}} +A.dD.prototype={ +eI(a){a.b5(this.b)}} +A.eZ.prototype={ +eI(a){a.b7(this.b,this.c)}} +A.mu.prototype={ +eI(a){a.b6()}, +gcc(){return null}, +scc(a){throw A.b(A.A("No events after a done."))}} +A.fh.prototype={ +cu(a){var s=this,r=s.a +if(r===1)return +if(r>=1){s.a=1 +return}A.pe(new A.n1(s,a)) +s.a=1}, +v(a,b){var s=this,r=s.c +if(r==null)s.b=s.c=b +else{r.scc(b) +s.c=b}}} +A.n1.prototype={ +$0(){var s,r,q=this.a,p=q.a +q.a=0 +if(p===3)return +s=q.b +r=s.gcc() +q.b=r +if(r==null)q.c=null +s.eI(this.b)}, +$S:0} +A.f0.prototype={ +cd(a){}, +eG(a){}, +bD(){var s=this.a +if(s>=0)this.a=s+2}, +bc(){var s=this,r=s.a-2 +if(r<0)return +if(r===0){s.a=1 +A.pe(s.gfs())}else s.a=r}, +J(){this.a=-1 +this.c=null +return $.cm()}, +iZ(){var s,r=this,q=r.a-1 +if(q===0){r.a=-1 +s=r.c +if(s!=null){r.c=null +r.b.cl(s)}}else r.a=q}} +A.dO.prototype={ +gm(){if(this.c)return this.b +return null}, +k(){var s,r=this,q=r.a +if(q!=null){if(r.c){s=new A.n($.m,t.k) +r.b=s +r.c=!1 +q.bc() +return s}throw A.b(A.A("Already waiting for next."))}return r.iL()}, +iL(){var s,r,q=this,p=q.b +if(p!=null){s=new A.n($.m,t.k) +q.b=s +r=p.P(q.giT(),!0,q.giV(),q.giX()) +if(q.b!=null)q.a=r +return s}return $.rU()}, +J(){var s=this,r=s.a,q=s.b +s.b=null +if(r!=null){s.a=null +if(!s.c)q.b3(!1) +else s.c=!1 +return r.J()}return $.cm()}, +iU(a){var s,r,q=this +if(q.a==null)return +s=q.b +q.b=a +q.c=!0 +s.b4(!0) +if(q.c){r=q.a +if(r!=null)r.bD()}}, +iY(a,b){var s=this,r=s.a,q=s.b +s.b=s.a=null +if(r!=null)q.V(new A.U(a,b)) +else q.aR(new A.U(a,b))}, +iW(){var s=this,r=s.a,q=s.b +s.b=s.a=null +if(r!=null)q.bL(!1) +else q.f4(!1)}} +A.nx.prototype={ +$0(){return this.a.V(this.b)}, +$S:0} +A.nw.prototype={ +$2(a,b){A.vT(this.a,this.b,new A.U(a,b))}, +$S:6} +A.ny.prototype={ +$0(){return this.a.b4(this.b)}, +$S:0} +A.f5.prototype={ +P(a,b,c,d){var s=this.$ti,r=$.m,q=b===!0?1:0,p=d!=null?32:0,o=A.ih(r,a,s.y[1]),n=A.ii(r,d) +s=new A.dE(this,o,n,r.az(c,t.H),r,q|p,s.h("dE<1,2>")) +s.x=this.a.b_(s.gdQ(),s.gdS(),s.gdU()) +return s}, +b_(a,b,c){return this.P(a,null,b,c)}} +A.dE.prototype={ +aQ(a){if((this.e&2)!==0)return +this.ds(a)}, +a7(a,b){if((this.e&2)!==0)return +this.eW(a,b)}, +al(){var s=this.x +if(s!=null)s.bD()}, +am(){var s=this.x +if(s!=null)s.bc()}, +cH(){var s=this.x +if(s!=null){this.x=null +return s.J()}return null}, +dR(a){this.w.iF(a,this)}, +dV(a,b){this.a7(a,b)}, +dT(){this.bo()}} +A.fc.prototype={ +iF(a,b){var s,r,q,p,o,n,m=null +try{m=this.b.$1(a)}catch(q){s=A.H(q) +r=A.a5(q) +p=s +o=r +n=A.dY(p,o) +if(n!=null){p=n.a +o=n.b}b.a7(p,o) +return}b.aQ(m)}} +A.f2.prototype={ +v(a,b){var s=this.a +if((s.e&2)!==0)A.E(A.A("Stream is already closed")) +s.ds(b)}, +a2(a,b){this.a.a7(a,b)}, +n(){var s=this.a +if((s.e&2)!==0)A.E(A.A("Stream is already closed")) +s.eX()}, +$iae:1} +A.dL.prototype={ +aQ(a){if((this.e&2)!==0)throw A.b(A.A("Stream is already closed")) +this.ds(a)}, +a7(a,b){if((this.e&2)!==0)throw A.b(A.A("Stream is already closed")) +this.eW(a,b)}, +bo(){if((this.e&2)!==0)throw A.b(A.A("Stream is already closed")) +this.eX()}, +al(){var s=this.x +if(s!=null)s.bD()}, +am(){var s=this.x +if(s!=null)s.bc()}, +cH(){var s=this.x +if(s!=null){this.x=null +return s.J()}return null}, +dR(a){var s,r,q,p +try{q=this.w +q===$&&A.x() +q.v(0,a)}catch(p){s=A.H(p) +r=A.a5(p) +this.a7(s,r)}}, +dV(a,b){var s,r,q,p +try{q=this.w +q===$&&A.x() +q.a2(a,b)}catch(p){s=A.H(p) +r=A.a5(p) +if(s===a)this.a7(a,b) +else this.a7(s,r)}}, +dT(){var s,r,q,p +try{this.x=null +q=this.w +q===$&&A.x() +q.n()}catch(p){s=A.H(p) +r=A.a5(p) +this.a7(s,r)}}} +A.fo.prototype={ +ee(a){return new A.eU(this.a,a,this.$ti.h("eU<1,2>"))}} +A.eU.prototype={ +P(a,b,c,d){var s=this.$ti,r=$.m,q=b===!0?1:0,p=d!=null?32:0,o=A.ih(r,a,s.y[1]),n=A.ii(r,d),m=new A.dL(o,n,r.az(c,t.H),r,q|p,s.h("dL<1,2>")) +m.w=this.a.$1(new A.f2(m)) +m.x=this.b.b_(m.gdQ(),m.gdS(),m.gdU()) +return m}, +b_(a,b,c){return this.P(a,null,b,c)}} +A.dF.prototype={ +v(a,b){var s=this.d +if(s==null)throw A.b(A.A("Sink is closed")) +this.$ti.y[1].a(b) +s.a.aQ(b)}, +a2(a,b){var s=this.d +if(s==null)throw A.b(A.A("Sink is closed")) +s.a2(a,b)}, +n(){var s=this.d +if(s==null)return +this.d=null +this.c.$1(s)}, +$iae:1} +A.dM.prototype={ +ee(a){return this.hV(a)}} +A.nd.prototype={ +$1(a){var s=this +return new A.dF(s.a,s.b,s.c,a,s.e.h("@<0>").G(s.d).h("dF<1,2>"))}, +$S(){return this.e.h("@<0>").G(this.d).h("dF<1,2>(ae<2>)")}} +A.av.prototype={} +A.iU.prototype={ +bQ(a,b,c){var s,r,q,p,o,n,m,l,k=this.gdW(),j=k.a +if(j===B.d){A.fF(b,c) +return}s=k.b +r=j.ga0() +m=j.ghi() +m.toString +q=m +p=$.m +try{$.m=q +s.$5(j,r,a,b,c) +$.m=p}catch(l){o=A.H(l) +n=A.a5(l) +$.m=p +m=b===o?c:n +q.bQ(j,o,m)}}, +$iv:1} +A.ij.prototype={ +gf3(){var s=this.at +return s==null?this.at=new A.dV(this):s}, +ga0(){return this.ax.gf3()}, +gaK(){return this.as.a}, +cl(a){var s,r,q +try{this.bd(a,t.H)}catch(q){s=A.H(q) +r=A.a5(q) +this.bQ(this,s,r)}}, +cm(a,b,c){var s,r,q +try{this.be(a,b,t.H,c)}catch(q){s=A.H(q) +r=A.a5(q) +this.bQ(this,s,r)}}, +hr(a,b,c,d,e){var s,r,q +try{this.eK(a,b,c,t.H,d,e)}catch(q){s=A.H(q) +r=A.a5(q) +this.bQ(this,s,r)}}, +ef(a,b){return new A.mr(this,this.az(a,b),b)}, +fX(a,b,c){return new A.mt(this,this.bb(a,b,c),c,b)}, +c2(a){return new A.mq(this,this.az(a,t.H))}, +eg(a,b){return new A.ms(this,this.bb(a,t.H,b),b)}, +j(a,b){var s,r=this.ay,q=r.j(0,b) +if(q!=null||r.a3(b))return q +s=this.ax.j(0,b) +if(s!=null)r.t(0,b,s) +return s}, +c7(a,b){this.bQ(this,a,b)}, +h8(a,b){var s=this.Q,r=s.a +return s.b.$5(r,r.ga0(),this,a,b)}, +bd(a){var s=this.a,r=s.a +return s.b.$4(r,r.ga0(),this,a)}, +be(a,b){var s=this.b,r=s.a +return s.b.$5(r,r.ga0(),this,a,b)}, +eK(a,b,c){var s=this.c,r=s.a +return s.b.$6(r,r.ga0(),this,a,b,c)}, +az(a){var s=this.d,r=s.a +return s.b.$4(r,r.ga0(),this,a)}, +bb(a){var s=this.e,r=s.a +return s.b.$4(r,r.ga0(),this,a)}, +dc(a){var s=this.f,r=s.a +return s.b.$4(r,r.ga0(),this,a)}, +h4(a,b){var s=this.r,r=s.a +if(r===B.d)return null +return s.b.$5(r,r.ga0(),this,a,b)}, +b1(a){var s=this.w,r=s.a +return s.b.$4(r,r.ga0(),this,a)}, +ei(a,b){var s=this.x,r=s.a +return s.b.$5(r,r.ga0(),this,a,b)}, +hj(a){var s=this.z,r=s.a +return s.b.$4(r,r.ga0(),this,a)}, +gfF(){return this.a}, +gfH(){return this.b}, +gfG(){return this.c}, +gfB(){return this.d}, +gfC(){return this.e}, +gfA(){return this.f}, +gfe(){return this.r}, +ge3(){return this.w}, +gf9(){return this.x}, +gf8(){return this.y}, +gfu(){return this.z}, +gfh(){return this.Q}, +gdW(){return this.as}, +ghi(){return this.ax}, +gfn(){return this.ay}} +A.mr.prototype={ +$0(){return this.a.bd(this.b,this.c)}, +$S(){return this.c.h("0()")}} +A.mt.prototype={ +$1(a){var s=this +return s.a.be(s.b,a,s.d,s.c)}, +$S(){return this.d.h("@<0>").G(this.c).h("1(2)")}} +A.mq.prototype={ +$0(){return this.a.cl(this.b)}, +$S:0} +A.ms.prototype={ +$1(a){return this.a.cm(this.b,a,this.c)}, +$S(){return this.c.h("~(0)")}} +A.iI.prototype={ +gfF(){return B.bp}, +gfH(){return B.br}, +gfG(){return B.bq}, +gfB(){return B.bo}, +gfC(){return B.bj}, +gfA(){return B.bt}, +gfe(){return B.bl}, +ge3(){return B.bs}, +gf9(){return B.bk}, +gf8(){return B.bi}, +gfu(){return B.bn}, +gfh(){return B.bm}, +gdW(){return B.bh}, +ghi(){return null}, +gfn(){return $.tc()}, +gf3(){var s=$.n4 +return s==null?$.n4=new A.dV(this):s}, +ga0(){var s=$.n4 +return s==null?$.n4=new A.dV(this):s}, +gaK(){return this}, +cl(a){var s,r,q +try{if(B.d===$.m){a.$0() +return}A.nD(null,null,this,a)}catch(q){s=A.H(q) +r=A.a5(q) +A.fF(s,r)}}, +cm(a,b){var s,r,q +try{if(B.d===$.m){a.$1(b) +return}A.nF(null,null,this,a,b)}catch(q){s=A.H(q) +r=A.a5(q) +A.fF(s,r)}}, +hr(a,b,c){var s,r,q +try{if(B.d===$.m){a.$2(b,c) +return}A.nE(null,null,this,a,b,c)}catch(q){s=A.H(q) +r=A.a5(q) +A.fF(s,r)}}, +ef(a,b){return new A.n6(this,a,b)}, +fX(a,b,c){return new A.n8(this,a,c,b)}, +c2(a){return new A.n5(this,a)}, +eg(a,b){return new A.n7(this,a,b)}, +j(a,b){return null}, +c7(a,b){A.fF(a,b)}, +h8(a,b){return A.rl(null,null,this,a,b)}, +bd(a){if($.m===B.d)return a.$0() +return A.nD(null,null,this,a)}, +be(a,b){if($.m===B.d)return a.$1(b) +return A.nF(null,null,this,a,b)}, +eK(a,b,c){if($.m===B.d)return a.$2(b,c) +return A.nE(null,null,this,a,b,c)}, +az(a){return a}, +bb(a){return a}, +dc(a){return a}, +h4(a,b){return null}, +b1(a){A.nG(null,null,this,a)}, +ei(a,b){return A.oA(a,b)}, +hj(a){A.pd(a)}} +A.n6.prototype={ +$0(){return this.a.bd(this.b,this.c)}, +$S(){return this.c.h("0()")}} +A.n8.prototype={ +$1(a){var s=this +return s.a.be(s.b,a,s.d,s.c)}, +$S(){return this.d.h("@<0>").G(this.c).h("1(2)")}} +A.n5.prototype={ +$0(){return this.a.cl(this.b)}, +$S:0} +A.n7.prototype={ +$1(a){return this.a.cm(this.b,a,this.c)}, +$S(){return this.c.h("~(0)")}} +A.dV.prototype={$iT:1} +A.nC.prototype={ +$0(){A.pH(this.a,this.b)}, +$S:0} +A.fz.prototype={$ioE:1} +A.cN.prototype={ +gl(a){return this.a}, +gB(a){return this.a===0}, +gX(){return new A.cO(this,A.r(this).h("cO<1>"))}, +gbH(){var s=A.r(this) +return A.ht(new A.cO(this,s.h("cO<1>")),new A.mQ(this),s.c,s.y[1])}, +a3(a){var s,r +if(typeof a=="string"&&a!=="__proto__"){s=this.b +return s==null?!1:s[a]!=null}else if(typeof a=="number"&&(a&1073741823)===a){r=this.c +return r==null?!1:r[a]!=null}else return this.im(a)}, +im(a){var s=this.d +if(s==null)return!1 +return this.aS(this.fi(s,a),a)>=0}, +j(a,b){var s,r,q +if(typeof b=="string"&&b!=="__proto__"){s=this.b +r=s==null?null:A.qF(s,b) +return r}else if(typeof b=="number"&&(b&1073741823)===b){q=this.c +r=q==null?null:A.qF(q,b) +return r}else return this.iD(b)}, +iD(a){var s,r,q=this.d +if(q==null)return null +s=this.fi(q,a) +r=this.aS(s,a) +return r<0?null:s[r+1]}, +t(a,b,c){var s,r,q=this +if(typeof b=="string"&&b!=="__proto__"){s=q.b +q.f2(s==null?q.b=A.oL():s,b,c)}else if(typeof b=="number"&&(b&1073741823)===b){r=q.c +q.f2(r==null?q.c=A.oL():r,b,c)}else q.jq(b,c)}, +jq(a,b){var s,r,q,p=this,o=p.d +if(o==null)o=p.d=A.oL() +s=p.dG(a) +r=o[s] +if(r==null){A.oM(o,s,[a,b]);++p.a +p.e=null}else{q=p.aS(r,a) +if(q>=0)r[q+1]=b +else{r.push(a,b);++p.a +p.e=null}}}, +ar(a,b){var s,r,q,p,o,n=this,m=n.f7() +for(s=m.length,r=A.r(n).y[1],q=0;q"))}} +A.is.prototype={ +gm(){var s=this.d +return s==null?this.$ti.c.a(s):s}, +k(){var s=this,r=s.b,q=s.c,p=s.a +if(r!==p.e)throw A.b(A.an(p)) +else if(q>=r.length){s.d=null +return!1}else{s.d=r[q] +s.c=q+1 +return!0}}} +A.fa.prototype={ +gq(a){var s=this,r=new A.dI(s,s.r,s.$ti.h("dI<1>")) +r.c=s.e +return r}, +gl(a){return this.a}, +gB(a){return this.a===0}, +H(a,b){var s,r +if(b!=="__proto__"){s=this.b +if(s==null)return!1 +return s[b]!=null}else{r=this.il(b) +return r}}, +il(a){var s=this.d +if(s==null)return!1 +return this.aS(s[B.a.gA(a)&1073741823],a)>=0}, +gE(a){var s=this.e +if(s==null)throw A.b(A.A("No elements")) +return s.a}, +gD(a){var s=this.f +if(s==null)throw A.b(A.A("No elements")) +return s.a}, +v(a,b){var s,r,q=this +if(typeof b=="string"&&b!=="__proto__"){s=q.b +return q.f1(s==null?q.b=A.oN():s,b)}else if(typeof b=="number"&&(b&1073741823)===b){r=q.c +return q.f1(r==null?q.c=A.oN():r,b)}else return q.i4(b)}, +i4(a){var s,r,q=this,p=q.d +if(p==null)p=q.d=A.oN() +s=J.aE(a)&1073741823 +r=p[s] +if(r==null)p[s]=[q.dZ(a)] +else{if(q.aS(r,a)>=0)return!1 +r.push(q.dZ(a))}return!0}, +F(a,b){var s +if(typeof b=="string"&&b!=="__proto__")return this.jc(this.b,b) +else{s=this.jb(b) +return s}}, +jb(a){var s,r,q,p,o=this.d +if(o==null)return!1 +s=J.aE(a)&1073741823 +r=o[s] +q=this.aS(r,a) +if(q<0)return!1 +p=r.splice(q,1)[0] +if(0===r.length)delete o[s] +this.fR(p) +return!0}, +f1(a,b){if(a[b]!=null)return!1 +a[b]=this.dZ(b) +return!0}, +jc(a,b){var s +if(a==null)return!1 +s=a[b] +if(s==null)return!1 +this.fR(s) +delete a[b] +return!0}, +fp(){this.r=this.r+1&1073741823}, +dZ(a){var s,r=this,q=new A.n_(a) +if(r.e==null)r.e=r.f=q +else{s=r.f +s.toString +q.c=s +r.f=s.b=q}++r.a +r.fp() +return q}, +fR(a){var s=this,r=a.c,q=a.b +if(r==null)s.e=q +else r.b=q +if(q==null)s.f=r +else q.c=r;--s.a +s.fp()}, +aS(a,b){var s,r +if(a==null)return-1 +s=a.length +for(r=0;r"))}, +gl(a){return this.b}, +c3(a){var s,r,q,p=this;++p.a +if(p.b===0)return +s=p.c +s.toString +r=s +do{q=r.b +q.toString +r.b=r.c=r.a=null +if(q!==s){r=q +continue}else break}while(!0) +p.c=null +p.b=0}, +gE(a){var s +if(this.b===0)throw A.b(A.A("No such element")) +s=this.c +s.toString +return s}, +gD(a){var s +if(this.b===0)throw A.b(A.A("No such element")) +s=this.c.c +s.toString +return s}, +gB(a){return this.b===0}, +cD(a,b,c){var s,r,q=this +if(b.a!=null)throw A.b(A.A("LinkedListEntry is already in a LinkedList"));++q.a +b.a=q +s=q.b +if(s===0){b.b=b +q.c=b.c=b +q.b=s+1 +return}r=a.c +r.toString +b.c=r +b.b=a +a.c=r.b=b +q.b=s+1}, +e6(a){var s,r,q=this;++q.a +s=a.b +s.c=a.c +a.c.b=s +r=--q.b +a.a=a.b=a.c=null +if(r===0)q.c=null +else if(a===q.c)q.c=s}} +A.iz.prototype={ +gm(){var s=this.c +return s==null?this.$ti.c.a(s):s}, +k(){var s=this,r=s.a +if(s.b!==r.a)throw A.b(A.an(s)) +if(r.b!==0)r=s.e&&s.d===r.gE(0) +else r=!0 +if(r){s.c=null +return!1}s.e=!0 +r=s.d +s.c=r +s.d=r.b +return!0}} +A.ay.prototype={ +gcf(){var s=this.a +if(s==null||this===s.gE(0))return null +return this.c}} +A.w.prototype={ +gq(a){return new A.b4(a,this.gl(a),A.aU(a).h("b4"))}, +K(a,b){return this.j(a,b)}, +gB(a){return this.gl(a)===0}, +gE(a){if(this.gl(a)===0)throw A.b(A.aw()) +return this.j(a,0)}, +gD(a){if(this.gl(a)===0)throw A.b(A.aw()) +return this.j(a,this.gl(a)-1)}, +ba(a,b,c){return new A.D(a,b,A.aU(a).h("@").G(c).h("D<1,2>"))}, +U(a,b){return A.b6(a,b,null,A.aU(a).h("w.E"))}, +ah(a,b){return A.b6(a,0,A.cV(b,"count",t.S),A.aU(a).h("w.E"))}, +aC(a,b){var s,r,q,p,o=this +if(o.gB(a)){s=J.pR(0,A.aU(a).h("w.E")) +return s}r=o.j(a,0) +q=A.b5(o.gl(a),r,!0,A.aU(a).h("w.E")) +for(p=1;p").G(b).h("ai<1,2>"))}, +a_(a,b,c){var s,r=this.gl(a) +A.bd(b,c,r) +s=A.ak(this.ct(a,b,c),A.aU(a).h("w.E")) +return s}, +ct(a,b,c){A.bd(b,c,this.gl(a)) +return A.b6(a,b,c,A.aU(a).h("w.E"))}, +em(a,b,c,d){var s +A.bd(b,c,this.gl(a)) +for(s=b;sp.gl(q))throw A.b(A.pP()) +if(r=0;--o)this.t(a,b+o,p.j(q,r+o)) +else for(o=0;o"))}, +gl(a){return J.aC(this.gX())}, +gB(a){return J.oa(this.gX())}, +gbH(){return new A.fb(this,A.r(this).h("fb"))}, +i(a){return A.oq(this)}, +$iap:1} +A.kz.prototype={ +$1(a){var s=this.a,r=s.j(0,a) +if(r==null)r=A.r(s).h("S.V").a(r) +return new A.aP(a,r,A.r(s).h("aP"))}, +$S(){return A.r(this.a).h("aP(S.K)")}} +A.kA.prototype={ +$2(a,b){var s,r=this.a +if(!r.a)this.b.a+=", " +r.a=!1 +r=this.b +s=A.t(a) +r.a=(r.a+=s)+": " +s=A.t(b) +r.a+=s}, +$S:98} +A.fb.prototype={ +gl(a){var s=this.a +return s.gl(s)}, +gB(a){var s=this.a +return s.gB(s)}, +gE(a){var s=this.a +s=s.j(0,J.j0(s.gX())) +return s==null?this.$ti.y[1].a(s):s}, +gD(a){var s=this.a +s=s.j(0,J.ob(s.gX())) +return s==null?this.$ti.y[1].a(s):s}, +gq(a){var s=this.a +return new A.iA(J.Z(s.gX()),s,this.$ti.h("iA<1,2>"))}} +A.iA.prototype={ +k(){var s=this,r=s.a +if(r.k()){s.c=s.b.j(0,r.gm()) +return!0}s.c=null +return!1}, +gm(){var s=this.c +return s==null?this.$ti.y[1].a(s):s}} +A.dp.prototype={ +gB(a){return this.a===0}, +ba(a,b,c){return new A.cu(this,b,this.$ti.h("@<1>").G(c).h("cu<1,2>"))}, +i(a){return A.ol(this,"{","}")}, +ah(a,b){return A.oz(this,b,this.$ti.c)}, +U(a,b){return A.qe(this,b,this.$ti.c)}, +gE(a){var s,r=A.iy(this,this.r,this.$ti.c) +if(!r.k())throw A.b(A.aw()) +s=r.d +return s==null?r.$ti.c.a(s):s}, +gD(a){var s,r,q=A.iy(this,this.r,this.$ti.c) +if(!q.k())throw A.b(A.aw()) +s=q.$ti.c +do{r=q.d +if(r==null)r=s.a(r)}while(q.k()) +return r}, +K(a,b){var s,r,q,p=this +A.ab(b,"index") +s=A.iy(p,p.r,p.$ti.c) +for(r=b;s.k();){if(r===0){q=s.d +return q==null?s.$ti.c.a(q):q}--r}throw A.b(A.hf(b,b-r,p,null,"index"))}, +$iq:1, +$id:1} +A.fk.prototype={} +A.nr.prototype={ +$0(){var s,r +try{s=new TextDecoder("utf-8",{fatal:true}) +return s}catch(r){}return null}, +$S:23} +A.nq.prototype={ +$0(){var s,r +try{s=new TextDecoder("utf-8",{fatal:false}) +return s}catch(r){}return null}, +$S:23} +A.fM.prototype={ +kB(a){return B.ac.a4(a)}} +A.iR.prototype={ +a4(a){var s,r,q,p=A.bd(0,null,a.length),o=new Uint8Array(p) +for(s=~this.a,r=0;r=0){g="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charCodeAt(f) +if(g===k)continue +k=g}else{if(f===-1){if(o<0){e=p==null?null:p.a.length +if(e==null)e=0 +o=e+(r-q) +n=r}++m +if(k===61)continue}k=g}if(f!==-2){if(p==null){p=new A.aD("") +e=p}else e=p +e.a+=B.a.p(a0,q,r) +d=A.aR(k) +e.a+=d +q=l +continue}}throw A.b(A.aj("Invalid base64 data",a0,r))}if(p!=null){e=B.a.p(a0,q,a2) +e=p.a+=e +d=e.length +if(o>=0)A.pt(a0,n,a2,o,m,d) +else{c=B.b.ab(d-1,4)+1 +if(c===1)throw A.b(A.aj(a,a0,a2)) +while(c<4){e+="=" +p.a=e;++c}}e=p.a +return B.a.aO(a0,a1,a2,e.charCodeAt(0)==0?e:e)}b=a2-a1 +if(o>=0)A.pt(a0,n,a2,o,m,b) +else{c=B.b.ab(b,4) +if(c===1)throw A.b(A.aj(a,a0,a2)) +if(c>1)a0=B.a.aO(a0,a2,a2,c===2?"==":"=")}return a0}} +A.fR.prototype={} +A.cr.prototype={} +A.cs.prototype={} +A.h8.prototype={} +A.i0.prototype={ +cX(a){return new A.fy(!1).dH(a,0,null,!0)}} +A.i1.prototype={ +a4(a){var s,r,q=A.bd(0,null,a.length) +if(q===0)return new Uint8Array(0) +s=new Uint8Array(q*3) +r=new A.ns(s) +if(r.iC(a,0,q)!==q)r.e9() +return B.e.a_(s,0,r.b)}} +A.ns.prototype={ +e9(){var s=this,r=s.c,q=s.b,p=s.b=q+1 +r.$flags&2&&A.y(r) +r[q]=239 +q=s.b=p+1 +r[p]=191 +s.b=q+1 +r[q]=189}, +jG(a,b){var s,r,q,p,o=this +if((b&64512)===56320){s=65536+((a&1023)<<10)|b&1023 +r=o.c +q=o.b +p=o.b=q+1 +r.$flags&2&&A.y(r) +r[q]=s>>>18|240 +q=o.b=p+1 +r[p]=s>>>12&63|128 +p=o.b=q+1 +r[q]=s>>>6&63|128 +o.b=p+1 +r[p]=s&63|128 +return!0}else{o.e9() +return!1}}, +iC(a,b,c){var s,r,q,p,o,n,m,l,k=this +if(b!==c&&(a.charCodeAt(c-1)&64512)===55296)--c +for(s=k.c,r=s.$flags|0,q=s.length,p=b;p=q)break +k.b=n+1 +r&2&&A.y(s) +s[n]=o}else{n=o&64512 +if(n===55296){if(k.b+4>q)break +m=p+1 +if(k.jG(o,a.charCodeAt(m)))p=m}else if(n===56320){if(k.b+3>q)break +k.e9()}else if(o<=2047){n=k.b +l=n+1 +if(l>=q)break +k.b=l +r&2&&A.y(s) +s[n]=o>>>6|192 +k.b=l+1 +s[l]=o&63|128}else{n=k.b +if(n+2>=q)break +l=k.b=n+1 +r&2&&A.y(s) +s[n]=o>>>12|224 +n=k.b=l+1 +s[l]=o>>>6&63|128 +k.b=n+1 +s[n]=o&63|128}}}return p}} +A.fy.prototype={ +dH(a,b,c,d){var s,r,q,p,o,n,m=this,l=A.bd(b,c,J.aC(a)) +if(b===l)return"" +if(a instanceof Uint8Array){s=a +r=s +q=0}else{r=A.vE(a,b,l) +l-=b +q=b +b=0}if(d&&l-b>=15){p=m.a +o=A.vD(p,r,b,l) +if(o!=null){if(!p)return o +if(o.indexOf("\ufffd")<0)return o}}o=m.dJ(r,b,l,d) +p=m.b +if((p&1)!==0){n=A.vF(p) +m.b=0 +throw A.b(A.aj(n,a,q+m.c))}return o}, +dJ(a,b,c,d){var s,r,q=this +if(c-b>1000){s=B.b.I(b+c,2) +r=q.dJ(a,b,s,!1) +if((q.b&1)!==0)return r +return r+q.dJ(a,s,c,d)}return q.k9(a,b,c,d)}, +k9(a,b,c,d){var s,r,q,p,o,n,m,l=this,k=65533,j=l.b,i=l.c,h=new A.aD(""),g=b+1,f=a[b] +A:for(s=l.a;;){for(;;g=p){r="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHIHHHJEEBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBKCCCCCCCCCCCCDCLONNNMEEEEEEEEEEE".charCodeAt(f)&31 +i=j<=32?f&61694>>>r:(f&63|i<<6)>>>0 +j=" \x000:XECCCCCN:lDb \x000:XECCCCCNvlDb \x000:XECCCCCN:lDb AAAAA\x00\x00\x00\x00\x00AAAAA00000AAAAA:::::AAAAAGG000AAAAA00KKKAAAAAG::::AAAAA:IIIIAAAAA000\x800AAAAA\x00\x00\x00\x00 AAAAA".charCodeAt(j+r) +if(j===0){q=A.aR(i) +h.a+=q +if(g===c)break A +break}else if((j&1)!==0){if(s)switch(j){case 69:case 67:q=A.aR(k) +h.a+=q +break +case 65:q=A.aR(k) +h.a+=q;--g +break +default:q=A.aR(k) +h.a=(h.a+=q)+q +break}else{l.b=j +l.c=g-1 +return""}j=0}if(g===c)break A +p=g+1 +f=a[g]}p=g+1 +f=a[g] +if(f<128){for(;;){if(!(p=128){o=n-1 +p=n +break}p=n}if(o-g<20)for(m=g;m32)if(s){s=A.aR(k) +h.a+=s}else{l.b=77 +l.c=c +return""}l.b=j +l.c=i +s=h.a +return s.charCodeAt(0)==0?s:s}} +A.a8.prototype={ +aj(a){var s,r,q=this,p=q.c +if(p===0)return q +s=!q.a +r=q.b +p=A.aS(p,r) +return new A.a8(p===0?!1:s,r,p)}, +iw(a){var s,r,q,p,o,n,m=this.c +if(m===0)return $.bb() +s=m+a +r=this.b +q=new Uint16Array(s) +for(p=m-1;p>=0;--p)q[p+a]=r[p] +o=this.a +n=A.aS(s,q) +return new A.a8(n===0?!1:o,q,n)}, +ix(a){var s,r,q,p,o,n,m,l=this,k=l.c +if(k===0)return $.bb() +s=k-a +if(s<=0)return l.a?$.po():$.bb() +r=l.b +q=new Uint16Array(s) +for(p=a;p>>0!==0)return l.cw(0,$.d_()) +for(k=0;k=0)return q.cA(b,r) +return b.cA(q,!r)}, +cw(a,b){var s,r,q=this,p=q.c +if(p===0)return b.aj(0) +s=b.c +if(s===0)return q +r=q.a +if(r!==b.a)return q.dv(b,r) +if(A.mg(q.b,p,b.b,s)>=0)return q.cA(b,r) +return b.cA(q,!r)}, +bI(a,b){var s,r,q,p,o,n,m,l=this.c,k=b.c +if(l===0||k===0)return $.bb() +s=l+k +r=this.b +q=b.b +p=new Uint16Array(s) +for(o=0;o0?p.aj(0):p}, +ja(a){var s,r,q,p=this +if(p.c0)q=q.bl(0,$.oH.ae()) +return p.a&&q.c>0?q.aj(0):q}, +fb(a){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c=this,b=c.c +if(b===$.qz&&a.c===$.qB&&c.b===$.qy&&a.b===$.qA)return +s=a.b +r=a.c +q=16-B.b.gfY(s[r-1]) +if(q>0){p=new Uint16Array(r+5) +o=A.qx(s,r,q,p) +n=new Uint16Array(b+5) +m=A.qx(c.b,b,q,n)}else{n=A.oI(c.b,0,b,b+2) +o=r +p=s +m=b}l=p[o-1] +k=m-o +j=new Uint16Array(m) +i=A.oJ(p,o,k,j) +h=m+1 +g=n.$flags|0 +if(A.mg(n,m,j,i)>=0){g&2&&A.y(n) +n[m]=1 +A.ig(n,h,j,i,n)}else{g&2&&A.y(n) +n[m]=0}f=new Uint16Array(o+2) +f[o]=1 +A.ig(f,o+1,p,o,f) +e=m-1 +while(k>0){d=A.v1(l,n,e);--k +A.qD(d,f,0,n,k,o) +if(n[e]1){q=$.pn() +if(q.c===0)A.E(B.ag) +p=r.ja(q).i(0) +s.push(p) +o=p.length +if(o===1)s.push("000") +if(o===2)s.push("00") +if(o===3)s.push("0") +r=r.iv(q)}s.push(B.b.i(r.b[0])) +if(m)s.push("-") +return new A.eG(s,t.bJ).c8(0)}} +A.mh.prototype={ +$2(a,b){a=a+b&536870911 +a=a+((a&524287)<<10)&536870911 +return a^a>>>6}, +$S:119} +A.mi.prototype={ +$1(a){a=a+((a&67108863)<<3)&536870911 +a^=a>>>11 +return a+((a&16383)<<15)&536870911}, +$S:37} +A.iq.prototype={ +fW(a,b,c){var s=this.a +if(s!=null)s.register(a,b,c)}, +h2(a){var s=this.a +if(s!=null)s.unregister(a)}} +A.eh.prototype={ +T(a,b){if(b==null)return!1 +return b instanceof A.eh&&this.a===b.a&&this.b===b.b&&this.c===b.c}, +gA(a){return A.eB(this.a,this.b,B.f,B.f)}, +af(a,b){var s=B.b.af(this.a,b.a) +if(s!==0)return s +return B.b.af(this.b,b.b)}, +i(a){var s=this,r=A.tX(A.q4(s)),q=A.h0(A.q2(s)),p=A.h0(A.q_(s)),o=A.h0(A.q0(s)),n=A.h0(A.q1(s)),m=A.h0(A.q3(s)),l=A.pC(A.uu(s)),k=s.b,j=k===0?"":A.pC(k) +k=r+"-"+q +if(s.c)return k+"-"+p+" "+o+":"+n+":"+m+"."+l+j+"Z" +else return k+"-"+p+" "+o+":"+n+":"+m+"."+l+j}} +A.bx.prototype={ +T(a,b){if(b==null)return!1 +return b instanceof A.bx&&this.a===b.a}, +gA(a){return B.b.gA(this.a)}, +af(a,b){return B.b.af(this.a,b.a)}, +i(a){var s,r,q,p,o,n=this.a,m=B.b.I(n,36e8),l=n%36e8 +if(n<0){m=0-m +n=0-l +s="-"}else{n=l +s=""}r=B.b.I(n,6e7) +n%=6e7 +q=r<10?"0":"" +p=B.b.I(n,1e6) +o=p<10?"0":"" +return s+m+":"+q+r+":"+o+p+"."+B.a.l0(B.b.i(n%1e6),6,"0")}} +A.mv.prototype={ +i(a){return this.ad()}} +A.L.prototype={ +gaP(){return A.ut(this)}} +A.fO.prototype={ +i(a){var s=this.a +if(s!=null)return"Assertion failed: "+A.h9(s) +return"Assertion failed"}} +A.bL.prototype={} +A.bc.prototype={ +gdN(){return"Invalid argument"+(!this.a?"(s)":"")}, +gdM(){return""}, +i(a){var s=this,r=s.c,q=r==null?"":" ("+r+")",p=s.d,o=p==null?"":": "+A.t(p),n=s.gdN()+q+o +if(!s.a)return n +return n+s.gdM()+": "+A.h9(s.gex())}, +gex(){return this.b}} +A.dk.prototype={ +gex(){return this.b}, +gdN(){return"RangeError"}, +gdM(){var s,r=this.e,q=this.f +if(r==null)s=q!=null?": Not less than or equal to "+A.t(q):"" +else if(q==null)s=": Not greater than or equal to "+A.t(r) +else if(q>r)s=": Not in inclusive range "+A.t(r)+".."+A.t(q) +else s=qe.length +else s=!1 +if(s)f=null +if(f==null){if(e.length>78)e=B.a.p(e,0,75)+"..." +return g+"\n"+e}for(r=1,q=0,p=!1,o=0;o1?g+(" (at line "+r+", character "+(f-q+1)+")\n"):g+(" (at character "+(f+1)+")\n") +m=e.length +for(o=f;o78){k="..." +if(f-q<75){j=q+75 +i=q}else{if(m-f<75){i=m-75 +j=m +k=""}else{i=f-36 +j=f+36}l="..."}}else{j=m +i=q +k=""}return g+l+B.a.p(e,i,j)+k+"\n"+B.a.bI(" ",f-i+l.length)+"^\n"}else return f!=null?g+(" (at offset "+A.t(f)+")"):g}, +$ia6:1} +A.hh.prototype={ +gaP(){return null}, +i(a){return"IntegerDivisionByZeroException"}, +$iL:1, +$ia6:1} +A.d.prototype={ +bx(a,b){return A.ed(this,A.r(this).h("d.E"),b)}, +ba(a,b,c){return A.ht(this,b,A.r(this).h("d.E"),c)}, +aC(a,b){var s=A.r(this).h("d.E") +if(b)s=A.ak(this,s) +else{s=A.ak(this,s) +s.$flags=1 +s=s}return s}, +cn(a){return this.aC(0,!0)}, +gl(a){var s,r=this.gq(this) +for(s=0;r.k();)++s +return s}, +gB(a){return!this.gq(this).k()}, +ah(a,b){return A.oz(this,b,A.r(this).h("d.E"))}, +U(a,b){return A.qe(this,b,A.r(this).h("d.E"))}, +hL(a,b){return new A.eI(this,b,A.r(this).h("eI"))}, +gE(a){var s=this.gq(this) +if(!s.k())throw A.b(A.aw()) +return s.gm()}, +gD(a){var s,r=this.gq(this) +if(!r.k())throw A.b(A.aw()) +do s=r.gm() +while(r.k()) +return s}, +K(a,b){var s,r +A.ab(b,"index") +s=this.gq(this) +for(r=b;s.k();){if(r===0)return s.gm();--r}throw A.b(A.hf(b,b-r,this,null,"index"))}, +i(a){return A.ud(this,"(",")")}} +A.aP.prototype={ +i(a){return"MapEntry("+A.t(this.a)+": "+A.t(this.b)+")"}} +A.N.prototype={ +gA(a){return A.e.prototype.gA.call(this,0)}, +i(a){return"null"}} +A.e.prototype={$ie:1, +T(a,b){return this===b}, +gA(a){return A.eE(this)}, +i(a){return"Instance of '"+A.hH(this)+"'"}, +gS(a){return A.xh(this)}, +toString(){return this.i(this)}} +A.dQ.prototype={ +i(a){return this.a}, +$ia_:1} +A.aD.prototype={ +gl(a){return this.a.length}, +i(a){var s=this.a +return s.charCodeAt(0)==0?s:s}} +A.ly.prototype={ +$2(a,b){throw A.b(A.aj("Illegal IPv6 address, "+a,this.a,b))}, +$S:57} +A.fv.prototype={ +gfM(){var s,r,q,p,o=this,n=o.w +if(n===$){s=o.a +r=s.length!==0?s+":":"" +q=o.c +p=q==null +if(!p||s==="file"){s=r+"//" +r=o.b +if(r.length!==0)s=s+r+"@" +if(!p)s+=q +r=o.d +if(r!=null)s=s+":"+A.t(r)}else s=r +s+=o.e +r=o.f +if(r!=null)s=s+"?"+r +r=o.r +if(r!=null)s=s+"#"+r +n=o.w=s.charCodeAt(0)==0?s:s}return n}, +gl1(){var s,r,q=this,p=q.x +if(p===$){s=q.e +if(s.length!==0&&s.charCodeAt(0)===47)s=B.a.L(s,1) +r=s.length===0?B.x:A.aO(new A.D(A.f(s.split("/"),t.s),A.x6(),t.do),t.N) +q.x!==$&&A.pj() +p=q.x=r}return p}, +gA(a){var s,r=this,q=r.y +if(q===$){s=B.a.gA(r.gfM()) +r.y!==$&&A.pj() +r.y=s +q=s}return q}, +geO(){return this.b}, +gb9(){var s=this.c +if(s==null)return"" +if(B.a.u(s,"[")&&!B.a.C(s,"v",1))return B.a.p(s,1,s.length-1) +return s}, +gce(){var s=this.d +return s==null?A.qT(this.a):s}, +gcg(){var s=this.f +return s==null?"":s}, +gd0(){var s=this.r +return s==null?"":s}, +kL(a){var s=this.a +if(a.length!==s.length)return!1 +return A.vV(a,s,0)>=0}, +ho(a){var s,r,q,p,o,n,m,l=this +a=A.np(a,0,a.length) +s=a==="file" +r=l.b +q=l.d +if(a!==l.a)q=A.no(q,a) +p=l.c +if(!(p!=null))p=r.length!==0||q!=null||s?"":null +o=l.e +if(!s)n=p!=null&&o.length!==0 +else n=!0 +if(n&&!B.a.u(o,"/"))o="/"+o +m=o +return A.fw(a,r,p,q,m,l.f,l.r)}, +fo(a,b){var s,r,q,p,o,n,m +for(s=0,r=0;B.a.C(b,"../",r);){r+=3;++s}q=B.a.d5(a,"/") +for(;;){if(!(q>0&&s>0))break +p=B.a.hd(a,"/",q-1) +if(p<0)break +o=q-p +n=o!==2 +m=!1 +if(!n||o===3)if(a.charCodeAt(p+1)===46)n=!n||a.charCodeAt(p+2)===46 +else n=m +else n=m +if(n)break;--s +q=p}return B.a.aO(a,q+1,null,B.a.L(b,r-3*s))}, +hq(a){return this.cj(A.bu(a))}, +cj(a){var s,r,q,p,o,n,m,l,k,j,i,h=this +if(a.gW().length!==0)return a +else{s=h.a +if(a.geq()){r=a.ho(s) +return r}else{q=h.b +p=h.c +o=h.d +n=h.e +if(a.gh9())m=a.gd1()?a.gcg():h.f +else{l=A.vB(h,n) +if(l>0){k=B.a.p(n,0,l) +n=a.gep()?k+A.cT(a.ga9()):k+A.cT(h.fo(B.a.L(n,k.length),a.ga9()))}else if(a.gep())n=A.cT(a.ga9()) +else if(n.length===0)if(p==null)n=s.length===0?a.ga9():A.cT(a.ga9()) +else n=A.cT("/"+a.ga9()) +else{j=h.fo(n,a.ga9()) +r=s.length===0 +if(!r||p!=null||B.a.u(n,"/"))n=A.cT(j) +else n=A.oS(j,!r||p!=null)}m=a.gd1()?a.gcg():null}}}i=a.ger()?a.gd0():null +return A.fw(s,q,p,o,n,m,i)}, +geq(){return this.c!=null}, +gd1(){return this.f!=null}, +ger(){return this.r!=null}, +gh9(){return this.e.length===0}, +gep(){return B.a.u(this.e,"/")}, +eL(){var s,r=this,q=r.a +if(q!==""&&q!=="file")throw A.b(A.a4("Cannot extract a file path from a "+q+" URI")) +q=r.f +if((q==null?"":q)!=="")throw A.b(A.a4(u.y)) +q=r.r +if((q==null?"":q)!=="")throw A.b(A.a4(u.l)) +if(r.c!=null&&r.gb9()!=="")A.E(A.a4(u.j)) +s=r.gl1() +A.vt(s,!1) +q=A.ox(B.a.u(r.e,"/")?"/":"",s,"/") +q=q.charCodeAt(0)==0?q:q +return q}, +i(a){return this.gfM()}, +T(a,b){var s,r,q,p=this +if(b==null)return!1 +if(p===b)return!0 +s=!1 +if(t.dD.b(b))if(p.a===b.gW())if(p.c!=null===b.geq())if(p.b===b.geO())if(p.gb9()===b.gb9())if(p.gce()===b.gce())if(p.e===b.ga9()){r=p.f +q=r==null +if(!q===b.gd1()){if(q)r="" +if(r===b.gcg()){r=p.r +q=r==null +if(!q===b.ger()){s=q?"":r +s=s===b.gd0()}}}}return s}, +$ihX:1, +gW(){return this.a}, +ga9(){return this.e}} +A.nn.prototype={ +$1(a){return A.vC(64,a,B.j,!1)}, +$S:8} +A.hY.prototype={ +geN(){var s,r,q,p,o=this,n=null,m=o.c +if(m==null){m=o.a +s=o.b[0]+1 +r=B.a.aY(m,"?",s) +q=m.length +if(r>=0){p=A.fx(m,r+1,q,256,!1,!1) +q=r}else p=n +m=o.c=new A.ik("data","",n,n,A.fx(m,s,q,128,!1,!1),p,n)}return m}, +i(a){var s=this.a +return this.b[0]===-1?"data:"+s:s}} +A.b7.prototype={ +geq(){return this.c>0}, +ges(){return this.c>0&&this.d+1r?B.a.p(this.a,r,s-1):""}, +gb9(){var s=this.c +return s>0?B.a.p(this.a,s,this.d):""}, +gce(){var s,r=this +if(r.ges())return A.bj(B.a.p(r.a,r.d+1,r.e),null) +s=r.b +if(s===4&&B.a.u(r.a,"http"))return 80 +if(s===5&&B.a.u(r.a,"https"))return 443 +return 0}, +ga9(){return B.a.p(this.a,this.e,this.f)}, +gcg(){var s=this.f,r=this.r +return s=q.length)return s +return new A.b7(B.a.p(q,0,r),s.b,s.c,s.d,s.e,s.f,r,s.w)}, +ho(a){var s,r,q,p,o,n,m,l,k,j,i,h=this,g=null +a=A.np(a,0,a.length) +s=!(h.b===a.length&&B.a.u(h.a,a)) +r=a==="file" +q=h.c +p=q>0?B.a.p(h.a,h.b+3,q):"" +o=h.ges()?h.gce():g +if(s)o=A.no(o,a) +q=h.c +if(q>0)n=B.a.p(h.a,q,h.d) +else n=p.length!==0||o!=null||r?"":g +q=h.a +m=h.f +l=B.a.p(q,h.e,m) +if(!r)k=n!=null&&l.length!==0 +else k=!0 +if(k&&!B.a.u(l,"/"))l="/"+l +k=h.r +j=m0)return b +s=b.c +if(s>0){r=a.b +if(r<=0)return b +q=r===4 +if(q&&B.a.u(a.a,"file"))p=b.e!==b.f +else if(q&&B.a.u(a.a,"http"))p=!b.fl("80") +else p=!(r===5&&B.a.u(a.a,"https"))||!b.fl("443") +if(p){o=r+1 +return new A.b7(B.a.p(a.a,0,o)+B.a.L(b.a,c+1),r,s+o,b.d+o,b.e+o,b.f+o,b.r+o,a.w)}else return this.fO().cj(b)}n=b.e +c=b.f +if(n===c){s=b.r +if(c0?l:m +o=k-n +return new A.b7(B.a.p(a.a,0,k)+B.a.L(s,n),a.b,a.c,a.d,m,c+o,b.r+o,a.w)}j=a.e +i=a.f +if(j===i&&a.c>0){while(B.a.C(s,"../",n))n+=3 +o=j-n+1 +return new A.b7(B.a.p(a.a,0,j)+"/"+B.a.L(s,n),a.b,a.c,a.d,j,c+o,b.r+o,a.w)}h=a.a +l=A.qL(this) +if(l>=0)g=l +else for(g=j;B.a.C(h,"../",g);)g+=3 +f=0 +for(;;){e=n+3 +if(!(e<=c&&B.a.C(s,"../",n)))break;++f +n=e}for(d="";i>g;){--i +if(h.charCodeAt(i)===47){if(f===0){d="/" +break}--f +d="/"}}if(i===g&&a.b<=0&&!B.a.C(h,"/",j)){n-=f*3 +d=""}o=i-n+d.length +return new A.b7(B.a.p(h,0,i)+d+B.a.L(s,n),a.b,a.c,a.d,j,c+o,b.r+o,a.w)}, +eL(){var s,r=this,q=r.b +if(q>=0){s=!(q===4&&B.a.u(r.a,"file")) +q=s}else q=!1 +if(q)throw A.b(A.a4("Cannot extract a file path from a "+r.gW()+" URI")) +q=r.f +s=r.a +if(q0?s.gb9():r,n=s.ges()?s.gce():r,m=s.a,l=s.f,k=B.a.p(m,s.e,l),j=s.r +l=l4294967296)throw A.b(new A.dk(k,k,!1,k,k,"max must be in range 0 < max \u2264 2^32, was "+a)) +if(a>255)if(a>65535)s=a>16777215?4:3 +else s=2 +else s=1 +r=this.a +r.$flags&2&&A.y(r,11) +r.setUint32(0,0,!1) +q=4-s +p=A.B(Math.pow(256,s)) +for(o=a-1,n=(a&o)===0;;){crypto.getRandomValues(J.d0(B.aF.gaX(r),q,s)) +m=r.getUint32(0,!1) +if(n)return(m&o)>>>0 +l=m%a +if(m-l+a>>0)&2147483647 +r^=r>>>6}r=r+(r<<3>>>0)&2147483647 +r^=r>>>11 +return r+(r<<15>>>0)&2147483647}} +A.hB.prototype={} +A.hW.prototype={} +A.ej.prototype={ +hW(a,b,c){var s=this.a.a +s===$&&A.x() +s.eB(this.giH(),new A.jS(this))}, +hf(){return this.d++}, +n(){var s=0,r=A.k(t.H),q,p=this,o +var $async$n=A.l(function(a,b){if(a===1)return A.h(b,r) +for(;;)switch(s){case 0:if(p.r||(p.w.a.a&30)!==0){s=1 +break}p.r=!0 +o=p.a.b +o===$&&A.x() +o.n() +s=3 +return A.c(p.w.a,$async$n) +case 3:case 1:return A.i(q,r)}}) +return A.j($async$n,r)}, +iI(a){var s,r=this +if(r.c){a.toString +a=B.F.ej(a)}if(a instanceof A.bf){s=r.e.F(0,a.a) +if(s!=null)s.a.O(a.b)}else if(a instanceof A.bo){s=r.e.F(0,a.a) +if(s!=null)s.h_(new A.h5(a.b),a.c)}else if(a instanceof A.ar)r.f.v(0,a) +else if(a instanceof A.bw){s=r.e.F(0,a.a) +if(s!=null)s.fZ(B.E)}}, +bu(a){var s,r,q=this +if(q.r||(q.w.a.a&30)!==0)throw A.b(A.A("Tried to send "+a.i(0)+" over isolate channel, but the connection was closed!")) +s=q.a.b +s===$&&A.x() +r=q.c?B.F.dr(a):a +s.a.v(0,r)}, +l6(a,b,c){var s,r=this +if(r.r||(r.w.a.a&30)!==0)return +s=a.a +if(b instanceof A.ec)r.bu(new A.bw(s)) +else r.bu(new A.bo(s,b,c))}, +hI(a){var s=this.f +new A.at(s,A.r(s).h("at<1>")).kO(new A.jT(this,a))}} +A.jS.prototype={ +$0(){var s,r,q +for(s=this.a,r=s.e,q=new A.db(r,r.r,r.e);q.k();)q.d.fZ(B.af) +r.c3(0) +s.w.aJ()}, +$S:0} +A.jT.prototype={ +$1(a){return this.hy(a)}, +hy(a){var s=0,r=A.k(t.H),q,p=2,o=[],n=this,m,l,k,j,i,h +var $async$$1=A.l(function(b,c){if(b===1){o.push(c) +s=p}for(;;)switch(s){case 0:i=null +p=4 +k=n.b.$1(a) +s=7 +return A.c(t.cG.b(k)?k:A.cL(k,t.O),$async$$1) +case 7:i=c +p=2 +s=6 +break +case 4:p=3 +h=o.pop() +m=A.H(h) +l=A.a5(h) +k=n.a.l6(a,m,l) +q=k +s=1 +break +s=6 +break +case 3:s=2 +break +case 6:k=n.a +if(!(k.r||(k.w.a.a&30)!==0))k.bu(new A.bf(a.a,i)) +case 1:return A.i(q,r) +case 2:return A.h(o.at(-1),r)}}) +return A.j($async$$1,r)}, +$S:42} +A.iC.prototype={ +h_(a,b){var s +if(b==null)s=this.b +else{s=A.f([],t.J) +if(b instanceof A.bm)B.c.aI(s,b.a) +else s.push(A.ql(b)) +s.push(A.ql(this.b)) +s=new A.bm(A.aO(s,t.a))}this.a.by(a,s)}, +fZ(a){return this.h_(a,null)}} +A.fX.prototype={ +i(a){return"Channel was closed before receiving a response"}, +$ia6:1} +A.h5.prototype={ +i(a){return J.b1(this.a)}, +$ia6:1} +A.h4.prototype={ +dr(a){var s,r +if(a instanceof A.ar)return[0,a.a,this.h3(a.b)] +else if(a instanceof A.bo){s=J.b1(a.b) +r=a.c +r=r==null?null:r.i(0) +return[2,a.a,s,r]}else if(a instanceof A.bf)return[1,a.a,this.h3(a.b)] +else if(a instanceof A.bw)return A.f([3,a.a],t.t) +else return null}, +ej(a){var s,r,q,p +if(!t.j.b(a))throw A.b(B.ar) +s=J.a3(a) +r=A.B(s.j(a,0)) +q=A.B(s.j(a,1)) +switch(r){case 0:return new A.ar(q,t.ah.a(this.h1(s.j(a,2)))) +case 2:p=A.r6(s.j(a,3)) +s=s.j(a,2) +if(s==null)s=A.oV(s) +return new A.bo(q,s,p!=null?new A.dQ(p):null) +case 1:return new A.bf(q,t.O.a(this.h1(s.j(a,2)))) +case 3:return new A.bw(q)}throw A.b(B.aq)}, +h3(a){var s,r,q,p,o,n,m,l,k,j,i,h,g,f +if(a==null)return a +if(a instanceof A.dh)return a.a +else if(a instanceof A.bW){s=a.a +r=a.b +q=[] +for(p=a.c,o=p.length,n=0;n")),g=g.h("Q.E");s.k();){a8=s.d +h.push(this.dI(a8==null?g.a(a8):a8))}m.push(new A.d2(i,h))}f=J.ob(a7.a) +A:{if(f==null){s=a6 +break A}A.B(f) +s=f +break A}return new A.bp(new A.ea(n,m),s) +case 5:return new A.c4(B.P[q.$1(1)],p.$1(2)) +case 6:return new A.bV(q.$1(1),p.$1(2)) +case 13:s.toString +return new A.c5(A.oe(B.N,A.a2(J.aM(s,1)))) +case 7:return new A.c3(new A.eC(p.$1(1),q.$1(2)),q.$1(3)) +case 8:e=A.f([],t.be) +s=t.j +k=1 +for(;;){l=a7.a +l.toString +if(!(k")).en(0,new A.kR(r))}, +kj(a,b){var s,r,q +for(s=this.z,s=A.iy(s,s.r,s.$ti.c),r=s.$ti.c;s.k();){q=s.d +if(q==null)q=r.a(q) +if(q!==b)q.bu(new A.ar(q.d++,a))}}} +A.kT.prototype={ +$1(a){var s=this.a +s.ic() +s.as.n()}, +$S:50} +A.kU.prototype={ +$1(a){return this.a.iK(this.b,a)}, +$S:51} +A.kV.prototype={ +$1(a){return this.a.z.F(0,this.b)}, +$S:25} +A.kP.prototype={ +$0(){var s=this.b +return this.a.aG(s.a,s.b,s.c,s.d)}, +$S:68} +A.kQ.prototype={ +$0(){return this.a.r.F(0,this.b.a)}, +$S:70} +A.kS.prototype={ +$0(){var s,r=this.b +if(r==null)return this.a.w.length===0 +else{s=this.a.w +return s.length!==0&&B.c.gE(s)===r}}, +$S:31} +A.kR.prototype={ +$1(a){return this.a.$0()}, +$S:25} +A.fj.prototype={ +cT(a,b){return this.jY(a,b)}, +jY(a,b){var s=0,r=A.k(t.H),q=1,p=[],o=[],n=this,m,l,k,j,i +var $async$cT=A.l(function(c,d){if(c===1){p.push(d) +s=q}for(;;)switch(s){case 0:j=n.a +i=j.e_(a,!0) +q=2 +m=n.b +l=m.hf() +k=new A.n($.m,t.D) +m.e.t(0,l,new A.iC(new A.a7(k,t.h),A.l8())) +m.bu(new A.ar(l,new A.c3(b,i))) +s=5 +return A.c(k,$async$cT) +case 5:o.push(4) +s=3 +break +case 2:o=[1] +case 3:q=1 +j.cI(i) +s=o.pop() +break +case 4:return A.i(null,r) +case 1:return A.h(p.at(-1),r)}}) +return A.j($async$cT,r)}} +A.i7.prototype={ +dr(a1){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a=this,a0=null +A:{if(a1 instanceof A.ar){s=new A.ag(0,{i:a1.a,p:a.jn(a1.b)}) +break A}if(a1 instanceof A.bf){s=new A.ag(1,{i:a1.a,p:a.jo(a1.b)}) +break A}r=a1 instanceof A.bo +q=a0 +p=a0 +o=!1 +n=a0 +m=a0 +s=!1 +if(r){l=a1.a +q=a1.b +o=q instanceof A.c8 +if(o){t.f_.a(q) +p=a1.c +s=a.a.c>=4 +m=p +n=q}k=l}else{k=a0 +l=k}if(s){s=m==null?a0:m.i(0) +j=n.a +i=n.b +if(i==null)i=a0 +h=n.c +g=n.e +if(g==null)g=a0 +f=n.f +if(f==null)f=a0 +e=n.r +B:{if(e==null){d=a0 +break B}d=[] +for(c=e.length,b=0;b") +s=A.ak(new A.D(s,new A.lV(),q),q.h("Q.E")) +s=new A.bF(s) +break A}s=A.E(A.J("Unknown request tag "+r,m))}return s}, +jo(a){var s,r +A:{s=null +if(a==null)break A +if(a instanceof A.aQ){r=a.a +s=A.bQ(r)?r:A.B(r) +break A}if(a instanceof A.bI){s=this.jp(a) +break A}}return s}, +jp(a){var s,r,q,p=a.a,o=J.a3(p) +if(o.gB(p)){p=v.G +return{c:new p.Array(),r:new p.Array()}}else{s=J.d1(o.gE(p).gX(),new A.m0(),t.N).cn(0) +r=A.f([],t.fk) +for(p=o.gq(p);p.k();){q=[] +for(o=J.Z(p.gm().gbH());o.k();)q.push(this.cL(o.gm())) +r.push(q)}return{c:s,r:r}}}, +iu(a){var s,r,q,p,o,n,m,l,k,j +if(a==null)return null +else if(typeof a==="boolean")return new A.aQ(A.bh(a)) +else if(typeof a==="number")return new A.aQ(A.B(A.Y(a))) +else{A.a9(a) +s=a.c +s=t.q.b(s)?s:new A.ai(s,A.O(s).h("ai<1,p>")) +r=t.N +s=J.d1(s,new A.lZ(),r) +q=A.ak(s,s.$ti.h("Q.E")) +p=A.f([],t.d) +s=a.r +s=J.Z(t.e9.b(s)?s:new A.ai(s,A.O(s).h("ai<1,u>"))) +o=t.X +while(s.k()){n=s.gm() +m=A.ao(r,o) +n=A.uc(n,0,o) +l=J.Z(n.a) +n=n.b +k=new A.eq(l,n) +while(k.k()){j=k.c +j=j>=0?new A.ag(n+j,l.gm()):A.E(A.aw()) +m.t(0,q[j.a],this.cK(j.b))}p.push(m)}return new A.bI(p)}}, +cL(a){var s +A:{if(a==null){s=null +break A}if(A.bv(a)){s=a +break A}if(A.bQ(a)){s=a +break A}if(typeof a=="string"){s=a +break A}if(typeof a=="number"){s=A.f([15,a],t.n) +break A}if(a instanceof A.a8){s=A.f([14,a.i(0)],t.f) +break A}if(t.I.b(a)){s=new Uint8Array(A.fC(a)) +break A}s=A.E(A.J("Unknown db value: "+A.t(a),null))}return s}, +cK(a){var s,r,q,p=null +if(a!=null)if(typeof a==="number")return A.B(A.Y(a)) +else if(typeof a==="boolean")return A.bh(a) +else if(typeof a==="string")return A.a2(a) +else if(A.kq(a,"Uint8Array"))return t.Z.a(a) +else{t.c.a(a) +s=a.length===2 +if(s){r=a[0] +q=a[1]}else{q=p +r=q}if(!s)throw A.b(A.A("Pattern matching error")) +if(r==14)return A.oK(A.a2(q),p) +else return A.Y(q)}else return p}, +fa(a){var s,r=a!=null?A.a2(a):null +A:{if(r!=null){s=new A.dQ(r) +break A}s=null +break A}return s}, +iq(a){var s,r,q,p,o=null,n=a.length>=8,m=o,l=o,k=o,j=o,i=o,h=o,g=o +if(n){s=a[0] +m=a[1] +l=a[2] +k=a[3] +j=a[4] +i=a[5] +h=a[6] +g=a[7]}else s=o +if(!n)throw A.b(A.A("Pattern matching error")) +s=A.B(A.Y(s)) +j=A.B(A.Y(j)) +A.a2(l) +n=k!=null?A.a2(k):o +r=h!=null?A.a2(h):o +if(g!=null){q=[] +t.c.a(g) +p=B.c.gq(g) +while(p.k())q.push(this.cK(p.gm()))}else q=o +p=i!=null?A.a2(i):o +return new A.bo(s,new A.c8(l,n,j,o,p,r,q),this.fa(m))}} +A.m1.prototype={ +$0(){var s=A.a9(this.a.a) +return new A.ar(s.i,this.b.it(s.p))}, +$S:72} +A.m2.prototype={ +$0(){var s=A.a9(this.a.a) +return new A.bf(s.i,this.b.iu(s.p))}, +$S:79} +A.m_.prototype={ +$1(a){return a}, +$S:8} +A.lW.prototype={ +$0(){var s,r,q,p,o,n,m=this.b,l=J.a3(m),k=t.c,j=k.a(l.j(m,1)),i=t.q.b(j)?j:new A.ai(j,A.O(j).h("ai<1,p>")) +i=J.d1(i,new A.lX(),t.N) +s=A.ak(i,i.$ti.h("Q.E")) +i=l.gl(m) +r=A.f([],t.b) +for(i=l.U(m,2).ah(0,i-3),k=A.ed(i,i.$ti.h("d.E"),k),k=A.ht(k,new A.lY(),A.r(k).h("d.E"),t.ee),i=k.a,q=A.r(k),k=new A.dc(i.gq(i),k.b,q.h("dc<1,2>")),i=this.a.gjF(),q=q.y[1];k.k();){p=k.a +if(p==null)p=q.a(p) +o=J.a3(p) +n=A.B(A.Y(o.j(p,0))) +p=o.U(p,1) +o=p.$ti.h("D") +p=A.ak(new A.D(p,i,o),o.h("Q.E")) +r.push(new A.d2(n,p))}m=l.j(m,l.gl(m)-1) +m=m==null?null:A.B(A.Y(m)) +return new A.bp(new A.ea(s,r),m)}, +$S:84} +A.lX.prototype={ +$1(a){return a}, +$S:8} +A.lY.prototype={ +$1(a){return a}, +$S:91} +A.lV.prototype={ +$1(a){var s,r,q +t.c.a(a) +s=a.length===2 +if(s){r=a[0] +q=a[1]}else{r=null +q=null}if(!s)throw A.b(A.A("Pattern matching error")) +A.a2(r) +return new A.bK(q==null?null:B.M[A.B(A.Y(q))],r)}, +$S:95} +A.m0.prototype={ +$1(a){return a}, +$S:8} +A.lZ.prototype={ +$1(a){return a}, +$S:8} +A.dv.prototype={ +ad(){return"UpdateKind."+this.b}} +A.bK.prototype={ +gA(a){return A.eB(this.a,this.b,B.f,B.f)}, +T(a,b){if(b==null)return!1 +return b instanceof A.bK&&b.a==this.a&&b.b===this.b}, +i(a){return"TableUpdate("+this.b+", kind: "+A.t(this.a)+")"}} +A.o2.prototype={ +$0(){return this.a.a.a.O(A.oi(this.b,this.c))}, +$S:0} +A.bU.prototype={ +J(){var s,r +if(this.c)return +for(s=this.b,r=0;!1;++r)s[r].$0() +this.c=!0}} +A.ec.prototype={ +i(a){return"Operation was cancelled"}, +$ia6:1} +A.aq.prototype={ +n(){var s=0,r=A.k(t.H) +var $async$n=A.l(function(a,b){if(a===1)return A.h(b,r) +for(;;)switch(s){case 0:return A.i(null,r)}}) +return A.j($async$n,r)}} +A.ea.prototype={ +gA(a){return A.eB(B.m.ha(this.a),B.m.ha(this.b),B.f,B.f)}, +T(a,b){if(b==null)return!1 +return b instanceof A.ea&&B.m.el(b.a,this.a)&&B.m.el(b.b,this.b)}, +i(a){return"BatchedStatements("+A.t(this.a)+", "+A.t(this.b)+")"}} +A.d2.prototype={ +gA(a){return A.eB(this.a,B.m,B.f,B.f)}, +T(a,b){if(b==null)return!1 +return b instanceof A.d2&&b.a===this.a&&B.m.el(b.b,this.b)}, +i(a){return"ArgumentsForBatchedStatement("+this.a+", "+A.t(this.b)+")"}} +A.jJ.prototype={} +A.kG.prototype={} +A.ls.prototype={} +A.kB.prototype={} +A.jM.prototype={} +A.hA.prototype={} +A.k0.prototype={} +A.id.prototype={ +gez(){return!1}, +gc9(){return!1}, +fK(a,b,c){if(this.gez()||this.b>0)return this.a.cz(new A.ma(b,a,c),c) +else return a.$0()}, +bv(a,b){return this.fK(a,!0,b)}, +cF(a,b){this.gc9()}, +aa(a,b){return this.lg(a,b)}, +lg(a,b){var s=0,r=A.k(t.aS),q,p=this,o +var $async$aa=A.l(function(c,d){if(c===1)return A.h(d,r) +for(;;)switch(s){case 0:s=3 +return A.c(p.bv(new A.mf(p,a,b),t.aj),$async$aa) +case 3:o=d.gjX(0) +o=A.ak(o,o.$ti.h("Q.E")) +q=o +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$aa,r)}, +ck(a,b){return this.bv(new A.md(this,a,b),t.S)}, +aB(a,b){return this.bv(new A.me(this,a,b),t.S)}, +a6(a,b){return this.bv(new A.mc(this,b,a),t.H)}, +lc(a){return this.a6(a,null)}, +aA(a){return this.bv(new A.mb(this,a),t.H)}, +cU(){return new A.f4(this,new A.a7(new A.n($.m,t.D),t.h),new A.bq())}, +cV(){return this.aW(this)}} +A.ma.prototype={ +$0(){return this.hC(this.c)}, +hC(a){var s=0,r=A.k(a),q,p=this +var $async$$0=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:if(p.a)A.p2() +s=3 +return A.c(p.b.$0(),$async$$0) +case 3:q=c +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$$0,r)}, +$S(){return this.c.h("C<0>()")}} +A.mf.prototype={ +$0(){var s=this.a,r=this.b,q=this.c +s.cF(r,q) +return s.gaL().aa(r,q)}, +$S:97} +A.md.prototype={ +$0(){var s=this.a,r=this.b,q=this.c +s.cF(r,q) +return s.gaL().de(r,q)}, +$S:26} +A.me.prototype={ +$0(){var s=this.a,r=this.b,q=this.c +s.cF(r,q) +return s.gaL().aB(r,q)}, +$S:26} +A.mc.prototype={ +$0(){var s,r,q=this.b +if(q==null)q=B.o +s=this.a +r=this.c +s.cF(r,q) +return s.gaL().a6(r,q)}, +$S:10} +A.mb.prototype={ +$0(){var s=this.a +s.gc9() +return s.gaL().aA(this.b)}, +$S:10} +A.iQ.prototype={ +ib(){this.c=!0 +if(this.d)throw A.b(A.A("A transaction was used after being closed. Please check that you're awaiting all database operations inside a `transaction` block."))}, +aW(a){throw A.b(A.a4("Nested transactions aren't supported."))}, +gap(){return B.l}, +gc9(){return!1}, +gez(){return!0}, +$ihS:1} +A.fn.prototype={ +aq(a){var s,r,q=this +q.ib() +s=q.z +if(s==null){s=q.z=new A.a7(new A.n($.m,t.k),t.co) +r=q.as;++r.b +r.fK(new A.n9(q),!1,t.P).ai(new A.na(r))}return s.a}, +gaL(){return this.e.e}, +aW(a){var s=this.at+1 +return new A.fn(this.y,new A.a7(new A.n($.m,t.D),t.h),a,s,A.rb(s),A.r9(s),A.ra(s),this.e,new A.bq())}, +bj(){var s=0,r=A.k(t.H),q,p=this +var $async$bj=A.l(function(a,b){if(a===1)return A.h(b,r) +for(;;)switch(s){case 0:if(!p.c){s=1 +break}s=3 +return A.c(p.a6(p.ay,B.o),$async$bj) +case 3:p.e2() +case 1:return A.i(q,r)}}) +return A.j($async$bj,r)}, +bE(){var s=0,r=A.k(t.H),q,p=2,o=[],n=[],m=this +var $async$bE=A.l(function(a,b){if(a===1){o.push(b) +s=p}for(;;)switch(s){case 0:if(!m.c){s=1 +break}p=3 +s=6 +return A.c(m.a6(m.ch,B.o),$async$bE) +case 6:n.push(5) +s=4 +break +case 3:n=[2] +case 4:p=2 +m.e2() +s=n.pop() +break +case 5:case 1:return A.i(q,r) +case 2:return A.h(o.at(-1),r)}}) +return A.j($async$bE,r)}, +e2(){var s=this +if(s.at===0)s.e.e.a=!1 +s.Q.aJ() +s.d=!0}} +A.n9.prototype={ +$0(){var s=0,r=A.k(t.P),q=1,p=[],o=this,n,m,l,k,j +var $async$$0=A.l(function(a,b){if(a===1){p.push(b) +s=q}for(;;)switch(s){case 0:q=3 +A.p2() +l=o.a +s=6 +return A.c(l.lc(l.ax),$async$$0) +case 6:l.e.e.a=!0 +l.z.O(!0) +q=1 +s=5 +break +case 3:q=2 +j=p.pop() +n=A.H(j) +m=A.a5(j) +l=o.a +l.z.by(n,m) +l.e2() +s=5 +break +case 2:s=1 +break +case 5:s=7 +return A.c(o.a.Q.a,$async$$0) +case 7:return A.i(null,r) +case 1:return A.h(p.at(-1),r)}}) +return A.j($async$$0,r)}, +$S:17} +A.na.prototype={ +$0(){return this.a.b--}, +$S:63} +A.h2.prototype={ +gaL(){return this.e}, +gap(){return B.l}, +aq(a){return this.x.cz(new A.jR(this,a),t.y)}, +br(a){return this.ji(a)}, +ji(a){var s=0,r=A.k(t.H),q=this,p,o,n,m +var $async$br=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:n=q.e +m=n.y +m===$&&A.x() +p=a.c +s=m instanceof A.hA?2:4 +break +case 2:o=p +s=3 +break +case 4:s=m instanceof A.fl?5:7 +break +case 5:s=8 +return A.c(A.b3(m.a.glm(),t.S),$async$br) +case 8:o=c +s=6 +break +case 7:throw A.b(A.k2("Invalid delegate: "+n.i(0)+". The versionDelegate getter must not subclass DBVersionDelegate directly")) +case 6:case 3:if(o===0)o=null +s=9 +return A.c(a.cT(new A.ie(q,new A.bq()),new A.eC(o,p)),$async$br) +case 9:s=m instanceof A.fl&&o!==p?10:11 +break +case 10:m.a.h5("PRAGMA user_version = "+p+";") +s=12 +return A.c(A.b3(null,t.H),$async$br) +case 12:case 11:return A.i(null,r)}}) +return A.j($async$br,r)}, +aW(a){var s=$.m +return new A.fn(B.an,new A.a7(new A.n(s,t.D),t.h),a,0,"BEGIN IMMEDIATE","COMMIT TRANSACTION","ROLLBACK TRANSACTION",this,new A.bq())}, +n(){return this.x.cz(new A.jQ(this),t.H)}, +gc9(){return this.r}, +gez(){return this.w}} +A.jR.prototype={ +$0(){var s=0,r=A.k(t.y),q,p=2,o=[],n=this,m,l,k,j,i,h,g,f,e +var $async$$0=A.l(function(a,b){if(a===1){o.push(b) +s=p}for(;;)switch(s){case 0:f=n.a +if(f.d){f=A.nB(new A.aI("Can't re-open a database after closing it. Please create a new database connection and open that instead."),null) +k=new A.n($.m,t.k) +k.aR(f) +q=k +s=1 +break}j=f.f +if(j!=null)A.pH(j.a,j.b) +k=f.e +i=t.y +h=A.b3(k.d,i) +s=3 +return A.c(t.bF.b(h)?h:A.cL(h,i),$async$$0) +case 3:if(b){q=f.c=!0 +s=1 +break}i=n.b +s=4 +return A.c(k.bB(i),$async$$0) +case 4:f.c=!0 +p=6 +s=9 +return A.c(f.br(i),$async$$0) +case 9:q=!0 +s=1 +break +p=2 +s=8 +break +case 6:p=5 +e=o.pop() +m=A.H(e) +l=A.a5(e) +f.f=new A.ag(m,l) +throw e +s=8 +break +case 5:s=2 +break +case 8:case 1:return A.i(q,r) +case 2:return A.h(o.at(-1),r)}}) +return A.j($async$$0,r)}, +$S:43} +A.jQ.prototype={ +$0(){var s=this.a +if(s.c&&!s.d){s.d=!0 +s.c=!1 +return s.e.n()}else return A.b3(null,t.H)}, +$S:10} +A.ie.prototype={ +aW(a){return this.e.aW(a)}, +aq(a){this.c=!0 +return A.b3(!0,t.y)}, +gaL(){return this.e.e}, +gc9(){return!1}, +gap(){return B.l}} +A.f4.prototype={ +gap(){return this.e.gap()}, +aq(a){var s,r,q,p=this,o=p.f +if(o!=null)return o.a +else{p.c=!0 +s=new A.n($.m,t.k) +r=new A.a7(s,t.co) +p.f=r +q=p.e;++q.b +q.bv(new A.my(p,r),t.P) +return s}}, +gaL(){return this.e.gaL()}, +aW(a){return this.e.aW(a)}, +n(){this.r.aJ() +return A.b3(null,t.H)}} +A.my.prototype={ +$0(){var s=0,r=A.k(t.P),q=this,p +var $async$$0=A.l(function(a,b){if(a===1)return A.h(b,r) +for(;;)switch(s){case 0:q.b.O(!0) +p=q.a +s=2 +return A.c(p.r.a,$async$$0) +case 2:--p.e.b +return A.i(null,r)}}) +return A.j($async$$0,r)}, +$S:17} +A.dj.prototype={ +gjX(a){var s=this.b +return new A.D(s,new A.kI(this),A.O(s).h("D<1,ap>"))}} +A.kI.prototype={ +$1(a){var s,r,q,p,o,n,m,l=A.ao(t.N,t.z) +for(s=this.a,r=s.a,q=r.length,s=s.c,p=J.a3(a),o=0;o")).gE(0)).n() +q.t(0,a,s)}return new A.ag(s,o.sqlite3_stmt_isexplain(r)===0)}} +A.fl.prototype={} +A.kF.prototype={ +kA(){var s,r,q,p +for(s=this.b,r=new A.db(s,s.r,s.e);r.k();){q=r.d +if(!q.r){q.r=!0 +if(!q.f){p=q.a +p.c.d.sqlite3_reset(p.b) +q.f=!0}q=q.a +p=q.c +p.d.sqlite3_finalize(q.b) +p=p.w +if(p!=null){p=p.a +if(p!=null)p.unregister(q.d)}}}s.c3(0)}} +A.k1.prototype={ +$1(a){return Date.now()}, +$S:45} +A.nH.prototype={ +$1(a){var s=a.j(0,0) +if(typeof s=="number")return this.a.$1(s) +else return null}, +$S:28} +A.ho.prototype={ +gis(){var s=this.a +s===$&&A.x() +return s}, +gap(){if(this.b){var s=this.a +s===$&&A.x() +s=B.l!==s.gap()}else s=!1 +if(s)throw A.b(A.k2("LazyDatabase created with "+B.l.i(0)+", but underlying database is "+this.gis().gap().i(0)+".")) +return B.l}, +i6(){var s,r,q=this +if(q.b)return A.b3(null,t.H) +else{s=q.d +if(s!=null)return s.a +else{s=new A.n($.m,t.D) +r=q.d=new A.a7(s,t.h) +A.oi(q.e,t.x).bf(new A.kt(q,r),r.gk6(),t.P) +return s}}}, +cU(){var s=this.a +s===$&&A.x() +return s.cU()}, +cV(){var s=this.a +s===$&&A.x() +return s.cV()}, +aq(a){return this.i6().bG(new A.ku(this,a),t.y)}, +aA(a){var s=this.a +s===$&&A.x() +return s.aA(a)}, +a6(a,b){var s=this.a +s===$&&A.x() +return s.a6(a,b)}, +ck(a,b){var s=this.a +s===$&&A.x() +return s.ck(a,b)}, +aB(a,b){var s=this.a +s===$&&A.x() +return s.aB(a,b)}, +aa(a,b){var s=this.a +s===$&&A.x() +return s.aa(a,b)}, +n(){var s=0,r=A.k(t.H),q,p=this,o,n +var $async$n=A.l(function(a,b){if(a===1)return A.h(b,r) +for(;;)switch(s){case 0:s=p.b?3:5 +break +case 3:o=p.a +o===$&&A.x() +s=6 +return A.c(o.n(),$async$n) +case 6:q=b +s=1 +break +s=4 +break +case 5:n=p.d +s=n!=null?7:8 +break +case 7:s=9 +return A.c(n.a,$async$n) +case 9:o=p.a +o===$&&A.x() +s=10 +return A.c(o.n(),$async$n) +case 10:case 8:case 4:case 1:return A.i(q,r)}}) +return A.j($async$n,r)}} +A.kt.prototype={ +$1(a){var s=this.a +s.a!==$&&A.iZ() +s.a=a +s.b=!0 +this.b.aJ()}, +$S:47} +A.ku.prototype={ +$1(a){var s=this.a.a +s===$&&A.x() +return s.aq(this.b)}, +$S:48} +A.bq.prototype={ +cz(a,b){var s,r=this.a,q=new A.n($.m,t.D) +this.a=q +s=new A.kw(this,a,new A.a7(q,t.h),q,b) +if(r!=null)return r.bG(new A.ky(s,b),b) +else return s.$0()}} +A.kw.prototype={ +$0(){var s=this +return A.oi(s.b,s.e).ai(new A.kx(s.a,s.c,s.d))}, +$S(){return this.e.h("C<0>()")}} +A.kx.prototype={ +$0(){this.b.aJ() +var s=this.a +if(s.a===this.c)s.a=null}, +$S:3} +A.ky.prototype={ +$1(a){return this.a.$0()}, +$S(){return this.b.h("C<0>(~)")}} +A.lS.prototype={ +$1(a){var s,r=this,q=a.data +if(r.a&&J.am(q,"_disconnect")){s=r.b.a +s===$&&A.x() +s=s.a +s===$&&A.x() +s.n()}else{s=r.b.a +if(r.c){s===$&&A.x() +s=s.a +s===$&&A.x() +s.v(0,r.d.ej(t.c.a(q)))}else{s===$&&A.x() +s=s.a +s===$&&A.x() +s.v(0,A.ry(q))}}}, +$S:11} +A.lT.prototype={ +$1(a){var s=this.c +if(this.a)s.postMessage(this.b.dr(t.fJ.a(a))) +else s.postMessage(A.xp(a))}, +$S:7} +A.lU.prototype={ +$0(){if(this.a)this.b.postMessage("_disconnect") +this.b.close()}, +$S:0} +A.jN.prototype={ +R(){A.aL(this.a,"message",new A.jP(this),!1)}, +ak(a){return this.iJ(a)}, +iJ(a6){var s=0,r=A.k(t.H),q=1,p=[],o=this,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1,a2,a3,a4,a5 +var $async$ak=A.l(function(a7,a8){if(a7===1){p.push(a8) +s=q}for(;;)switch(s){case 0:k=a6 instanceof A.dl +j=k?a6.a:null +s=k?3:4 +break +case 3:i={} +i.a=i.b=!1 +s=5 +return A.c(o.b.cz(new A.jO(i,o),t.P),$async$ak) +case 5:h=o.c.a.j(0,j) +g=A.f([],t.L) +f=!1 +s=i.b?6:7 +break +case 6:a5=J +s=8 +return A.c(A.e6(),$async$ak) +case 8:k=a5.Z(a8) +case 9:if(!k.k()){s=10 +break}e=k.gm() +g.push(new A.ag(B.C,e)) +if(e===j)f=!0 +s=9 +break +case 10:case 7:s=h!=null?11:13 +break +case 11:k=h.a +d=k===B.r||k===B.B +f=k===B.W||k===B.X +s=12 +break +case 13:a5=i.a +if(a5){s=14 +break}else a8=a5 +s=15 +break +case 14:s=16 +return A.c(A.e4(j),$async$ak) +case 16:case 15:d=a8 +case 12:k=v.G +c="Worker" in k +e=i.b +b=i.a +new A.ei(c,e,"SharedArrayBuffer" in k,b,g,B.q,d,f).dn(o.a) +s=2 +break +case 4:if(a6 instanceof A.dn){o.c.eS(a6) +s=2 +break}k=a6 instanceof A.eL +a=k?a6.a:null +s=k?17:18 +break +case 17:s=19 +return A.c(A.i3(a),$async$ak) +case 19:a0=a8 +o.a.postMessage(!0) +s=20 +return A.c(a0.R(),$async$ak) +case 20:s=2 +break +case 18:n=null +m=null +a1=a6 instanceof A.h3 +if(a1){a2=a6.a +n=a2.a +m=a2.b}s=a1?21:22 +break +case 21:q=24 +case 27:switch(n){case B.Y:s=29 +break +case B.C:s=30 +break +default:s=28 +break}break +case 29:s=31 +return A.c(A.nN(m),$async$ak) +case 31:s=28 +break +case 30:s=32 +return A.c(A.fG(m),$async$ak) +case 32:s=28 +break +case 28:a6.dn(o.a) +q=1 +s=26 +break +case 24:q=23 +a4=p.pop() +l=A.H(a4) +new A.dz(J.b1(l)).dn(o.a) +s=26 +break +case 23:s=1 +break +case 26:s=2 +break +case 22:s=2 +break +case 2:return A.i(null,r) +case 1:return A.h(p.at(-1),r)}}) +return A.j($async$ak,r)}} +A.jP.prototype={ +$1(a){this.a.ak(A.oB(A.a9(a.data)))}, +$S:1} +A.jO.prototype={ +$0(){var s=0,r=A.k(t.P),q=this,p,o,n,m,l +var $async$$0=A.l(function(a,b){if(a===1)return A.h(b,r) +for(;;)switch(s){case 0:o=q.b +n=o.d +m=q.a +s=n!=null?2:4 +break +case 2:m.b=n.b +m.a=n.a +s=3 +break +case 4:l=m +s=5 +return A.c(A.cW(),$async$$0) +case 5:l.b=b +s=6 +return A.c(A.iW(),$async$$0) +case 6:p=b +m.a=p +o.d=new A.lF(p,m.b) +case 3:return A.i(null,r)}}) +return A.j($async$$0,r)}, +$S:17} +A.cA.prototype={ +ad(){return"ProtocolVersion."+this.b}} +A.lH.prototype={ +dq(a){this.aD(new A.lK(a))}, +eR(a){this.aD(new A.lJ(a))}, +dn(a){this.aD(new A.lI(a))}} +A.lK.prototype={ +$2(a,b){var s=b==null?B.w:b +this.a.postMessage(a,s)}, +$S:19} +A.lJ.prototype={ +$2(a,b){var s=b==null?B.w:b +this.a.postMessage(a,s)}, +$S:19} +A.lI.prototype={ +$2(a,b){var s=b==null?B.w:b +this.a.postMessage(a,s)}, +$S:19} +A.ji.prototype={} +A.c6.prototype={ +aD(a){var s=this +A.dW(a,"SharedWorkerCompatibilityResult",A.f([s.e,s.f,s.r,s.c,s.d,A.pF(s.a),s.b.c],t.f),null)}} +A.l1.prototype={ +$1(a){return A.bh(J.aM(this.a,a))}, +$S:52} +A.dz.prototype={ +aD(a){A.dW(a,"Error",this.a,null)}, +i(a){return"Error in worker: "+this.a}, +$ia6:1} +A.dn.prototype={ +aD(a){var s,r,q=this,p={} +p.sqlite=q.a.i(0) +s=q.b +p.port=s +p.storage=q.c.b +p.database=q.d +r=q.e +p.initPort=r +p.migrations=q.r +p.new_serialization=q.w +p.v=q.f.c +s=A.f([s],t.W) +if(r!=null)s.push(r) +A.dW(a,"ServeDriftDatabase",p,s)}} +A.dl.prototype={ +aD(a){A.dW(a,"RequestCompatibilityCheck",this.a,null)}} +A.ei.prototype={ +aD(a){var s=this,r={} +r.supportsNestedWorkers=s.e +r.canAccessOpfs=s.f +r.supportsIndexedDb=s.w +r.supportsSharedArrayBuffers=s.r +r.indexedDbExists=s.c +r.opfsExists=s.d +r.existing=A.pF(s.a) +r.v=s.b.c +A.dW(a,"DedicatedWorkerCompatibilityResult",r,null)}} +A.eL.prototype={ +aD(a){A.dW(a,"StartFileSystemServer",this.a,null)}} +A.h3.prototype={ +aD(a){var s=this.a +A.dW(a,"DeleteDatabase",A.f([s.a.b,s.b],t.s),null)}} +A.nK.prototype={ +$1(a){this.b.transaction.abort() +this.a.a=!1}, +$S:11} +A.nZ.prototype={ +$1(a){return A.a9(a[1])}, +$S:53} +A.h6.prototype={ +eS(a){var s=a.f.c,r=a.w +this.a.hk(a.d,new A.k_(this,a)).hG(A.uV(a.b,s>=1,s,r),!r)}, +aN(a,b,c,d,e){return this.kZ(a,b,c,d,e)}, +kZ(a,b,c,d,e){var s=0,r=A.k(t.x),q,p=this,o,n,m,l,k,j,i,h,g +var $async$aN=A.l(function(f,a0){if(f===1)return A.h(a0,r) +for(;;)switch(s){case 0:s=3 +return A.c(A.lO(d.i(0)),$async$aN) +case 3:i=a0 +h=null +g=null +case 4:switch(e.a){case 0:s=6 +break +case 1:s=7 +break +case 3:s=8 +break +case 2:s=9 +break +case 4:s=10 +break +default:s=11 +break}break +case 6:s=12 +return A.c(A.l3("drift_db/"+a),$async$aN) +case 12:o=a0 +g=o.gc4() +s=5 +break +case 7:s=13 +return A.c(p.cE(a),$async$aN) +case 13:o=a0 +g=o.gc4() +s=5 +break +case 8:case 9:s=14 +return A.c(A.hg(a,!1),$async$aN) +case 14:o=a0 +g=o.gc4() +h=o +s=5 +break +case 10:o=A.ok(null) +s=5 +break +case 11:o=null +case 5:s=c!=null&&o.co("/database",0)===0?15:16 +break +case 15:n=c.$0() +s=17 +return A.c(t.eY.b(n)?n:A.cL(n,t.aD),$async$aN) +case 17:m=a0 +if(m!=null){l=o.b0(new A.eJ("/database"),4).a +l.bi(m,0) +l.cp()}n=h==null?null:h.aU(!1) +s=18 +return A.c(n instanceof A.n?n:A.cL(n,t.H),$async$aN) +case 18:case 16:i.hb() +n=i.a +n=n.a +k=n.d.dart_sqlite3_register_vfs(n.c1(B.i.a4(o.a),1),o,1) +if(k===0)A.E(A.A("could not register vfs")) +n=$.t6() +n.a.set(o,k) +n=A.uj(t.N,t.eT) +j=new A.i4(new A.iT(i,"/database",h,p.b,!0,b,new A.kF(n)),!1,!0,new A.bq(),new A.bq()) +if(g!=null){q=A.tM(j,new A.mn(g,j)) +s=1 +break}else{q=j +s=1 +break}case 1:return A.i(q,r)}}) +return A.j($async$aN,r)}, +cE(a){return this.iO(a)}, +iO(a){var s=0,r=A.k(t.aT),q,p,o,n,m,l,k,j +var $async$cE=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:l=v.G +k=new l.SharedArrayBuffer(8) +j=l.Int32Array +j=t.ha.a(A.e3(j,[k])) +l.Atomics.store(j,0,-1) +j={clientVersion:1,root:"drift_db/"+a,synchronizationBuffer:k,communicationBuffer:new l.SharedArrayBuffer(67584)} +p=new l.Worker(A.i_().i(0)) +new A.eL(j).dq(p) +s=3 +return A.c(new A.f3(p,"message",!1,t.fF).gE(0),$async$cE) +case 3:o=A.qb(j.synchronizationBuffer) +j=j.communicationBuffer +n=A.qd(j,65536,2048) +l=l.Uint8Array +l=t.Z.a(A.e3(l,[j])) +m=$.fI() +q=new A.dy(o,new A.br(j,n,l),m,"dart-sqlite3-vfs") +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$cE,r)}} +A.k_.prototype={ +$0(){var s=this.b,r=s.e,q=r!=null?new A.jX(r):null,p=this.a,o=A.uC(new A.ho(new A.jY(p,s,q)),!1,!0),n=new A.n($.m,t.D),m=new A.dm(s.c,o,new A.a1(n,t.F)) +n.ai(new A.jZ(p,s,m)) +return m}, +$S:54} +A.jX.prototype={ +$0(){var s=new A.n($.m,t.fX),r=this.a +r.postMessage(!0) +r.onmessage=A.bi(new A.jW(new A.a7(s,t.fu))) +return s}, +$S:55} +A.jW.prototype={ +$1(a){var s=t.dE.a(a.data),r=s==null?null:s +this.a.O(r)}, +$S:11} +A.jY.prototype={ +$0(){var s=this.b +return this.a.aN(s.d,s.r,this.c,s.a,s.c)}, +$S:56} +A.jZ.prototype={ +$0(){this.a.a.F(0,this.b.d) +this.c.b.hJ()}, +$S:3} +A.mn.prototype={ +c5(a){return this.k0(a)}, +k0(a){var s=0,r=A.k(t.H),q=this,p +var $async$c5=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:s=2 +return A.c(a.n(),$async$c5) +case 2:s=q.b===a?3:4 +break +case 3:p=q.a.$0() +s=5 +return A.c(p instanceof A.n?p:A.cL(p,t.H),$async$c5) +case 5:case 4:return A.i(null,r)}}) +return A.j($async$c5,r)}} +A.dm.prototype={ +hG(a,b){var s,r,q;++this.c +s=t.X +s=A.vg(new A.kM(this),s,s).gjZ().$1(a.ghP()) +r=a.$ti +q=new A.ee(r.h("ee<1>")) +q.b=new A.eX(q,a.ghK()) +q.a=new A.eY(s,q,r.h("eY<1>")) +this.b.hH(q,b)}} +A.kM.prototype={ +$1(a){var s=this.a +if(--s.c===0)s.d.aJ() +a.a.bo()}, +$S:39} +A.lF.prototype={} +A.jm.prototype={ +$1(a){this.a.O(this.c.a(this.b.result))}, +$S:1} +A.jn.prototype={ +$1(a){var s=this.b.error +if(s==null)s=a +this.a.ag(s)}, +$S:1} +A.jo.prototype={ +$1(a){var s=this.b.error +if(s==null)s=a +this.a.ag(s)}, +$S:1} +A.kW.prototype={ +R(){A.aL(this.a,"connect",new A.l0(this),!1)}, +dX(a){return this.iS(a)}, +iS(a){var s=0,r=A.k(t.H),q=this,p,o +var $async$dX=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:p=a.ports +o=J.aM(t.cl.b(p)?p:new A.ai(p,A.O(p).h("ai<1,z>")),0) +o.start() +A.aL(o,"message",new A.kX(q,o),!1) +return A.i(null,r)}}) +return A.j($async$dX,r)}, +cG(a,b){return this.iP(a,b)}, +iP(a,b){var s=0,r=A.k(t.H),q=1,p=[],o=this,n,m,l,k,j,i,h,g +var $async$cG=A.l(function(c,d){if(c===1){p.push(d) +s=q}for(;;)switch(s){case 0:q=3 +n=A.oB(A.a9(b.data)) +m=n +l=null +i=m instanceof A.dl +if(i)l=m.a +s=i?7:8 +break +case 7:s=9 +return A.c(o.bX(l),$async$cG) +case 9:k=d +k.eR(a) +s=6 +break +case 8:if(m instanceof A.dn&&B.r===m.c){o.c.eS(n) +s=6 +break}if(m instanceof A.dn){i=o.b +i.toString +n.dq(i) +s=6 +break}i=A.J("Unknown message",null) +throw A.b(i) +case 6:q=1 +s=5 +break +case 3:q=2 +g=p.pop() +j=A.H(g) +new A.dz(J.b1(j)).eR(a) +a.close() +s=5 +break +case 2:s=1 +break +case 5:return A.i(null,r) +case 1:return A.h(p.at(-1),r)}}) +return A.j($async$cG,r)}, +bX(a){return this.jx(a)}, +jx(a){var s=0,r=A.k(t.fL),q,p=this,o,n,m,l,k,j,i,h,g,f,e,d,c +var $async$bX=A.l(function(b,a0){if(b===1)return A.h(a0,r) +for(;;)switch(s){case 0:k=v.G +j="Worker" in k +s=3 +return A.c(A.iW(),$async$bX) +case 3:i=a0 +s=!j?4:6 +break +case 4:k=p.c.a.j(0,a) +if(k==null)o=null +else{k=k.a +k=k===B.r||k===B.B +o=k}h=A +g=!1 +f=!1 +e=i +d=B.y +c=B.q +s=o==null?7:9 +break +case 7:s=10 +return A.c(A.e4(a),$async$bX) +case 10:s=8 +break +case 9:a0=o +case 8:q=new h.c6(g,f,e,d,c,a0,!1) +s=1 +break +s=5 +break +case 6:n={} +m=p.b +if(m==null)m=p.b=new k.Worker(A.i_().i(0)) +new A.dl(a).dq(m) +k=new A.n($.m,t.a9) +n.a=n.b=null +l=new A.l_(n,new A.a7(k,t.bi),i) +n.b=A.aL(m,"message",new A.kY(l),!1) +n.a=A.aL(m,"error",new A.kZ(p,l,m),!1) +q=k +s=1 +break +case 5:case 1:return A.i(q,r)}}) +return A.j($async$bX,r)}} +A.l0.prototype={ +$1(a){return this.a.dX(a)}, +$S:1} +A.kX.prototype={ +$1(a){return this.a.cG(this.b,a)}, +$S:1} +A.l_.prototype={ +$4(a,b,c,d){var s,r=this.b +if((r.a.a&30)===0){r.O(new A.c6(!0,a,this.c,d,B.q,c,b)) +r=this.a +s=r.b +if(s!=null)s.J() +r=r.a +if(r!=null)r.J()}}, +$S:58} +A.kY.prototype={ +$1(a){var s=t.ed.a(A.oB(A.a9(a.data))) +this.a.$4(s.f,s.d,s.c,s.a)}, +$S:1} +A.kZ.prototype={ +$1(a){this.b.$4(!1,!1,!1,B.y) +this.c.terminate() +this.a.b=null}, +$S:1} +A.cc.prototype={ +ad(){return"WasmStorageImplementation."+this.b}} +A.bO.prototype={ +ad(){return"WebStorageApi."+this.b}} +A.i4.prototype={} +A.iT.prototype={ +l_(){var s=this.Q.bB(this.as) +return s}, +bq(){var s=0,r=A.k(t.H),q=this,p +var $async$bq=A.l(function(a,b){if(a===1)return A.h(b,r) +for(;;)switch(s){case 0:p=q.at +p=p==null?null:p.aU(!1) +s=2 +return A.c(p instanceof A.n?p:A.cL(p,t.H),$async$bq) +case 2:return A.i(null,r)}}) +return A.j($async$bq,r)}, +bt(a,b){return this.jl(a,b)}, +jl(a,b){var s=0,r=A.k(t.z),q=this +var $async$bt=A.l(function(c,d){if(c===1)return A.h(d,r) +for(;;)switch(s){case 0:q.li(a,b) +s=!q.a?2:3 +break +case 2:s=4 +return A.c(q.bq(),$async$bt) +case 4:case 3:return A.i(null,r)}}) +return A.j($async$bt,r)}, +a6(a,b){return this.ld(a,b)}, +ld(a,b){var s=0,r=A.k(t.H),q=this +var $async$a6=A.l(function(c,d){if(c===1)return A.h(d,r) +for(;;)switch(s){case 0:s=2 +return A.c(q.bt(a,b),$async$a6) +case 2:return A.i(null,r)}}) +return A.j($async$a6,r)}, +aB(a,b){return this.le(a,b)}, +le(a,b){var s=0,r=A.k(t.S),q,p=this,o +var $async$aB=A.l(function(c,d){if(c===1)return A.h(d,r) +for(;;)switch(s){case 0:s=3 +return A.c(p.bt(a,b),$async$aB) +case 3:o=p.b.b +q=A.B(v.G.Number(o.a.d.sqlite3_last_insert_rowid(o.b))) +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$aB,r)}, +de(a,b){return this.lh(a,b)}, +lh(a,b){var s=0,r=A.k(t.S),q,p=this,o +var $async$de=A.l(function(c,d){if(c===1)return A.h(d,r) +for(;;)switch(s){case 0:s=3 +return A.c(p.bt(a,b),$async$de) +case 3:o=p.b.b +q=o.a.d.sqlite3_changes(o.b) +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$de,r)}, +aA(a){return this.lb(a)}, +lb(a){var s=0,r=A.k(t.H),q=this +var $async$aA=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:q.la(a) +s=!q.a?2:3 +break +case 2:s=4 +return A.c(q.bq(),$async$aA) +case 4:case 3:return A.i(null,r)}}) +return A.j($async$aA,r)}, +n(){var s=0,r=A.k(t.H),q=this +var $async$n=A.l(function(a,b){if(a===1)return A.h(b,r) +for(;;)switch(s){case 0:s=2 +return A.c(q.hT(),$async$n) +case 2:q.b.n() +s=3 +return A.c(q.bq(),$async$n) +case 3:return A.i(null,r)}}) +return A.j($async$n,r)}} +A.fY.prototype={ +fS(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){var s +A.rt("absolute",A.f([a,b,c,d,e,f,g,h,i,j,k,l,m,n,o],t.d4)) +s=this.a +s=s.Y(a)>0&&!s.aZ(a) +if(s)return a +s=this.b +return this.hc(0,s==null?A.p5():s,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o)}, +jS(a){var s=null +return this.fS(a,s,s,s,s,s,s,s,s,s,s,s,s,s,s)}, +hc(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q){var s=A.f([b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q],t.d4) +A.rt("join",s) +return this.kN(new A.eR(s,t.eJ))}, +kM(a,b,c){var s=null +return this.hc(0,b,c,s,s,s,s,s,s,s,s,s,s,s,s,s,s)}, +kN(a){var s,r,q,p,o,n,m,l,k +for(s=a.gq(0),r=new A.cG(s,new A.js()),q=this.a,p=!1,o=!1,n="";r.k();){m=s.gm() +if(q.aZ(m)&&o){l=A.di(m,q) +k=n.charCodeAt(0)==0?n:n +n=B.a.p(k,0,q.bF(k,!0)) +l.b=n +if(q.cb(n))l.e[0]=q.gbk() +n=l.i(0)}else if(q.Y(m)>0){o=!q.aZ(m) +n=m}else{if(!(m.length!==0&&q.eh(m[0])))if(p)n+=q.gbk() +n+=m}p=q.cb(m)}return n.charCodeAt(0)==0?n:n}, +bm(a,b){var s=A.di(b,this.a),r=s.d,q=A.O(r).h("aK<1>") +r=A.ak(new A.aK(r,new A.jt(),q),q.h("d.E")) +s.d=r +q=s.b +if(q!=null)B.c.d2(r,0,q) +return s.d}, +eF(a){var s +if(!this.iR(a))return a +s=A.di(a,this.a) +s.eE() +return s.i(0)}, +iR(a){var s,r,q,p,o,n,m,l=this.a,k=l.Y(a) +if(k!==0){if(l===$.fK())for(s=0;s0)return o.eF(a) +if(m.Y(a)<=0||m.aZ(a))a=o.jS(a) +if(m.Y(a)<=0&&m.Y(s)>0)throw A.b(A.pX(n+a+'" from "'+s+'".')) +r=A.di(s,m) +r.eE() +q=A.di(a,m) +q.eE() +l=r.d +if(l.length!==0&&l[0]===".")return q.i(0) +l=r.b +p=q.b +if(l!=p)l=l==null||p==null||!m.eH(l,p) +else l=!1 +if(l)return q.i(0) +for(;;){l=r.d +if(l.length!==0){p=q.d +l=p.length!==0&&m.eH(l[0],p[0])}else l=!1 +if(!l)break +B.c.dd(r.d,0) +B.c.dd(r.e,1) +B.c.dd(q.d,0) +B.c.dd(q.e,1)}l=r.d +p=l.length +if(p!==0&&l[0]==="..")throw A.b(A.pX(n+a+'" from "'+s+'".')) +l=t.N +B.c.ev(q.d,0,A.b5(p,"..",!1,l)) +p=q.e +p[0]="" +B.c.ev(p,1,A.b5(r.d.length,m.gbk(),!1,l)) +m=q.d +l=m.length +if(l===0)return"." +if(l>1&&B.c.gD(m)==="."){B.c.hm(q.d) +m=q.e +m.pop() +m.pop() +m.push("")}q.b="" +q.hn() +return q.i(0)}, +ht(a){var s,r=this.a +if(r.Y(a)<=0)return r.hl(a) +else{s=this.b +return r.ec(this.kM(0,s==null?A.p5():s,a))}}, +l3(a){var s,r,q=this,p=A.p_(a) +if(p.gW()==="file"&&q.a===$.fJ())return p.i(0) +else if(p.gW()!=="file"&&p.gW()!==""&&q.a!==$.fJ())return p.i(0) +s=q.eF(q.a.d9(A.p_(p))) +r=q.l4(s) +return q.bm(0,r).length>q.bm(0,s).length?s:r}} +A.js.prototype={ +$1(a){return a!==""}, +$S:2} +A.jt.prototype={ +$1(a){return a.length!==0}, +$S:2} +A.nI.prototype={ +$1(a){return a==null?"null":'"'+a+'"'}, +$S:60} +A.kp.prototype={ +hF(a){var s=this.Y(a) +if(s>0)return B.a.p(a,0,s) +return this.aZ(a)?a[0]:null}, +hl(a){var s,r=null,q=a.length +if(q===0)return A.al(r,r,r,r) +s=A.pB(this).bm(0,a) +if(this.au(a.charCodeAt(q-1)))B.c.v(s,"") +return A.al(r,r,s,r)}, +eH(a,b){return a===b}} +A.kD.prototype={ +geu(){var s=this.d +if(s.length!==0)s=B.c.gD(s)===""||B.c.gD(this.e)!=="" +else s=!1 +return s}, +hn(){var s,r,q=this +for(;;){s=q.d +if(!(s.length!==0&&B.c.gD(s)===""))break +B.c.hm(q.d) +q.e.pop()}s=q.e +r=s.length +if(r!==0)s[r-1]=""}, +eE(){var s,r,q,p,o,n=this,m=A.f([],t.s) +for(s=n.d,r=s.length,q=0,p=0;p0){s=B.a.aY(a,"\\",s+1) +if(s>0)return s}return r}if(r<3)return 0 +if(!A.rE(a.charCodeAt(0)))return 0 +if(a.charCodeAt(1)!==58)return 0 +r=a.charCodeAt(2) +if(!(r===47||r===92))return 0 +return 3}, +Y(a){return this.bF(a,!1)}, +aZ(a){return this.Y(a)===1}, +d9(a){var s,r +if(a.gW()!==""&&a.gW()!=="file")throw A.b(A.J("Uri "+a.i(0)+" must have scheme 'file:'.",null)) +s=a.ga9() +if(a.gb9()===""){if(s.length>=3&&B.a.u(s,"/")&&A.rz(s,1)!=null)s=B.a.hp(s,"/","")}else s="\\\\"+a.gb9()+s +r=A.bk(s,"/","\\") +return A.oT(r,0,r.length,B.j,!1)}, +ec(a){var s,r,q=A.di(a,this),p=q.b +p.toString +if(B.a.u(p,"\\\\")){s=new A.aK(A.f(p.split("\\"),t.s),new A.m4(),t.U) +B.c.d2(q.d,0,s.gD(0)) +if(q.geu())B.c.v(q.d,"") +return A.al(s.gE(0),null,q.d,"file")}else{if(q.d.length===0||q.geu())B.c.v(q.d,"") +p=q.d +r=q.b +r.toString +r=A.bk(r,"/","") +B.c.d2(p,0,A.bk(r,"\\","")) +return A.al(null,null,q.d,"file")}}, +k5(a,b){var s +if(a===b)return!0 +if(a===47)return b===92 +if(a===92)return b===47 +if((a^b)!==32)return!1 +s=a|32 +return s>=97&&s<=122}, +eH(a,b){var s,r +if(a===b)return!0 +s=a.length +if(s!==b.length)return!1 +for(r=0;r")).av(0,", ")):s}return p.charCodeAt(0)==0?p:p}, +$ia6:1} +A.l7.prototype={ +$1(a){if(t.E.b(a))return"blob ("+a.length+" bytes)" +else return J.b1(a)}, +$S:61} +A.cn.prototype={} +A.h_.prototype={ +glm(){var s,r,q=this.l2("PRAGMA user_version;") +try{s=q.eQ(new A.cw(B.aA)) +r=A.B(J.j0(s).b[0]) +return r}finally{q.n()}}, +h0(a,b,c,d,e){var s,r,q,p,o,n=null,m=this.b,l=B.i.a4(e) +if(l.length>255)A.E(A.ad(e,"functionName","Must not exceed 255 bytes when utf-8 encoded")) +s=new Uint8Array(A.fC(l)) +r=c?526337:2049 +q=m.a +p=q.c1(s,1) +s=q.d +o=A.p1(s,"dart_sqlite3_create_function_v2",[m.b,p,a.a,r,0,new A.bG(new A.jL(d),n,n)]) +s.dart_sqlite3_free(p) +if(o!==0)A.fH(this,o,n,n,n)}, +a5(a,b,c,d){return this.h0(a,b,!0,c,d)}, +n(){var s,r,q,p=this +if(p.r)return +p.r=!0 +s=p.b +r=s.eT() +q=r!==0?A.p4(p.a,s,r,"closing database",null,null):null +if(q!=null)throw A.b(q)}, +h5(a){var s,r,q,p=this,o=B.o +if(J.aC(o)===0){if(p.r)A.E(A.A("This database has already been closed")) +r=p.b +q=r.a +s=q.c1(B.i.a4(a),1) +q=q.d +r=A.p1(q,"sqlite3_exec",[r.b,s,0,0,0]) +q.dart_sqlite3_free(s) +if(r!==0)A.fH(p,r,"executing",a,o)}else{s=p.da(a,!0) +try{s.h6(new A.cw(o))}finally{s.n()}}}, +j4(a,b,c,d,a0){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e=this +if(e.r)A.E(A.A("This database has already been closed")) +s=B.i.a4(a) +r=e.b +q=r.a +p=q.bw(s) +o=q.d +n=o.dart_sqlite3_malloc(4) +o=o.dart_sqlite3_malloc(4) +m=new A.lR(r,p,n,o) +l=A.f([],t.bb) +k=new A.jK(m,l) +for(r=s.length,q=q.b,j=0;j."))}, +dz(a){A:{this.i8(a.a) +break A}}, +ci(){if(!this.f){var s=this.a +s.c.d.sqlite3_reset(s.b) +this.f=!0}}, +n(){var s,r,q=this +if(!q.r){q.r=!0 +q.ci() +s=q.a +r=s.c +r.d.sqlite3_finalize(s.b) +r=r.w +if(r!=null)r.h2(s.d)}}, +eQ(a){var s=this +s.fd() +s.ci() +s.dz(a) +return s.jm()}, +h6(a){var s=this +s.fd() +s.ci() +s.dz(a) +s.fg()}} +A.he.prototype={ +co(a,b){return this.d.a3(a)?1:0}, +dg(a,b){this.d.F(0,a)}, +dh(a){return new v.G.URL(a,"file:///").pathname}, +b0(a,b){var s,r=a.a +if(r==null)r=A.oj(this.b,"/") +s=this.d +if(!s.a3(r))if((b&4)!==0)s.t(0,r,new A.bg(new Uint8Array(0),0)) +else throw A.b(A.ca(14)) +return new A.cR(new A.it(this,r,(b&8)!==0),0)}, +dk(a){}} +A.it.prototype={ +eJ(a,b){var s,r=this.a.d.j(0,this.b) +if(r==null||r.b<=b)return 0 +s=Math.min(a.length,r.b-b) +B.e.N(a,0,s,J.d0(B.e.gaX(r.a),0,r.b),b) +return s}, +df(){return this.d>=2?1:0}, +cp(){if(this.c)this.a.d.F(0,this.b)}, +cr(){return this.a.d.j(0,this.b).b}, +di(a){this.d=a}, +dl(a){}, +cs(a){var s=this.a.d,r=this.b,q=s.j(0,r) +if(q==null){s.t(0,r,new A.bg(new Uint8Array(0),0)) +s.j(0,r).sl(0,a)}else q.sl(0,a)}, +dm(a){this.d=a}, +bi(a,b){var s,r=this.a.d,q=this.b,p=r.j(0,q) +if(p==null){p=new A.bg(new Uint8Array(0),0) +r.t(0,q,p)}s=b+a.length +if(s>p.b)p.sl(0,s) +p.ac(0,b,s,a)}} +A.o_.prototype={ +$1(a){return a.length!==0}, +$S:2} +A.ju.prototype={ +ia(){var s,r,q,p,o=A.ao(t.N,t.S) +for(s=this.a,r=s.length,q=0;qq.d)throw A.b(A.ca(14)) +s=q.a.b +s===$&&A.x() +s=A.bE(s.buffer,0,null) +r=q.e +B.e.b2(s,r,p) +s.$flags&2&&A.y(s) +s[r+o]=0}, +$S:0} +A.jC.prototype={ +$0(){var s,r=this,q=r.a.b +q===$&&A.x() +s=A.bE(q.buffer,r.b,r.c) +q=r.d +if(q!=null)A.pu(s,q.b) +else return A.pu(s,null)}, +$S:0} +A.jE.prototype={ +$0(){this.a.dk(A.pE(this.b,0))}, +$S:0} +A.jx.prototype={ +$0(){return this.a.cp()}, +$S:0} +A.jD.prototype={ +$0(){var s=this,r=s.a.b +r===$&&A.x() +s.b.eP(A.bE(r.buffer,s.c,s.d),A.B(v.G.Number(s.e)))}, +$S:0} +A.jI.prototype={ +$0(){var s=this,r=s.a.b +r===$&&A.x() +s.b.bi(A.bE(r.buffer,s.c,s.d),A.B(v.G.Number(s.e)))}, +$S:0} +A.jG.prototype={ +$0(){return this.a.cs(A.B(v.G.Number(this.b)))}, +$S:0} +A.jF.prototype={ +$0(){return this.a.dl(this.b)}, +$S:0} +A.jz.prototype={ +$0(){var s,r=this.b.cr(),q=this.a.b +q===$&&A.x() +q=A.bD(q.buffer,0,null) +s=B.b.M(this.c,2) +q.$flags&2&&A.y(q) +q[s]=r}, +$S:0} +A.jB.prototype={ +$0(){return this.a.di(this.b)}, +$S:0} +A.jH.prototype={ +$0(){return this.a.dm(this.b)}, +$S:0} +A.jw.prototype={ +$0(){var s,r=this.b.df(),q=this.a.b +q===$&&A.x() +q=A.bD(q.buffer,0,null) +s=B.b.M(this.c,2) +q.$flags&2&&A.y(q) +q[s]=r}, +$S:0} +A.bG.prototype={} +A.e9.prototype={ +P(a,b,c,d){var s,r=null,q={},p=A.a9(A.hm(this.a,v.G.Symbol.asyncIterator,r,r,r,r)),o=A.eN(r,r,!0,this.$ti.c) +q.a=null +s=new A.j3(q,this,p,o) +o.d=s +o.f=new A.j4(q,o,s) +return new A.at(o,A.r(o).h("at<1>")).P(a,b,c,d)}, +b_(a,b,c){return this.P(a,null,b,c)}} +A.j3.prototype={ +$0(){var s,r=this,q=r.c.next(),p=r.a +p.a=q +s=r.d +A.V(q,t.m).bf(new A.j5(p,r.b,s,r),s.gfT(),t.P)}, +$S:0} +A.j5.prototype={ +$1(a){var s,r,q=this,p=a.done +if(p==null)p=null +s=a.value +r=q.c +if(p===!0){r.n() +q.a.a=null}else{r.v(0,s==null?q.b.$ti.c.a(s):s) +q.a.a=null +p=r.b +if(!((p&1)!==0?(r.gaV().e&4)!==0:(p&2)===0))q.d.$0()}}, +$S:11} +A.j4.prototype={ +$0(){var s,r +if(this.a.a==null){s=this.b +r=s.b +s=!((r&1)!==0?(s.gaV().e&4)!==0:(r&2)===0)}else s=!1 +if(s)this.c.$0()}, +$S:0} +A.cK.prototype={ +J(){var s=0,r=A.k(t.H),q=this,p +var $async$J=A.l(function(a,b){if(a===1)return A.h(b,r) +for(;;)switch(s){case 0:p=q.b +if(p!=null)p.J() +p=q.c +if(p!=null)p.J() +q.c=q.b=null +return A.i(null,r)}}) +return A.j($async$J,r)}, +gm(){var s=this.a +return s==null?A.E(A.A("Await moveNext() first")):s}, +k(){var s,r,q=this,p=q.a +if(p!=null)p.continue() +p=new A.n($.m,t.k) +s=new A.a1(p,t.fa) +r=q.d +q.b=A.aL(r,"success",new A.mo(q,s),!1) +q.c=A.aL(r,"error",new A.mp(q,s),!1) +return p}} +A.mo.prototype={ +$1(a){var s,r=this.a +r.J() +s=r.$ti.h("1?").a(r.d.result) +r.a=s +this.b.O(s!=null)}, +$S:1} +A.mp.prototype={ +$1(a){var s=this.a +s.J() +s=s.d.error +if(s==null)s=a +this.b.ag(s)}, +$S:1} +A.jk.prototype={ +$1(a){this.a.O(this.c.a(this.b.result))}, +$S:1} +A.jl.prototype={ +$1(a){var s=this.b.error +if(s==null)s=a +this.a.ag(s)}, +$S:1} +A.jp.prototype={ +$1(a){this.a.O(this.c.a(this.b.result))}, +$S:1} +A.jq.prototype={ +$1(a){var s=this.b.error +if(s==null)s=a +this.a.ag(s)}, +$S:1} +A.jr.prototype={ +$1(a){this.a.ag(new A.aI("IndexedDB open blocked"))}, +$S:1} +A.lL.prototype={ +k8(){var s={} +s.dart=new A.lM(this).$0() +return s}, +d7(a){return this.kP(a)}, +kP(a){var s=0,r=A.k(t.m),q,p=this,o,n +var $async$d7=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:s=3 +return A.c(A.V(v.G.WebAssembly.instantiateStreaming(a,p.k8()),t.m),$async$d7) +case 3:o=c +n=o.instance.exports +if("_initialize" in n)t.g.a(n._initialize).call() +q=o.instance +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$d7,r)}} +A.lM.prototype={ +$0(){var s=this.a.a,r=A.a9(v.G.Object),q=A.a9(r.create.apply(r,[null])) +q.error_log=A.bi(s.gkS()) +q.localtime=A.b9(s.gkQ()) +q.xOpen=A.oX(s.glH()) +q.xDelete=A.nA(s.glw()) +q.xAccess=A.dX(s.glo()) +q.xFullPathname=A.dX(s.glD()) +q.xRandomness=A.nA(s.glJ()) +q.xSleep=A.b9(s.glO()) +q.xCurrentTimeInt64=A.b9(s.glu()) +q.xClose=A.bi(s.gls()) +q.xRead=A.dX(s.glL()) +q.xWrite=A.dX(s.glW()) +q.xTruncate=A.b9(s.glS()) +q.xSync=A.b9(s.glQ()) +q.xFileSize=A.b9(s.glB()) +q.xLock=A.b9(s.glF()) +q.xUnlock=A.b9(s.glU()) +q.xCheckReservedLock=A.b9(s.glq()) +q.xDeviceCharacteristics=A.bi(s.gcq()) +q.xFileControl=A.nA(s.glz()) +q.xSectorSize=A.bi(s.gdj()) +q["dispatch_()v"]=A.bi(s.gkm()) +q["dispatch_()i"]=A.bi(s.gkh()) +q.dispatch_update=A.oX(s.gkk()) +q.dispatch_xFunc=A.dX(s.gks()) +q.dispatch_xStep=A.dX(s.gkw()) +q.dispatch_xInverse=A.dX(s.gku()) +q.dispatch_xValue=A.b9(s.gky()) +q.dispatch_xFinal=A.b9(s.gkq()) +q.dispatch_compare=A.oX(s.gko()) +q.dispatch_busy=A.b9(s.gkf()) +q.changeset_apply_filter=A.b9(s.gkd()) +q.changeset_apply_conflict=A.nA(s.gkb()) +return q}, +$S:126} +A.i6.prototype={} +A.dy.prototype={ +jh(a,b){var s,r,q=this.e +q.hu(b) +s=this.d.b +r=v.G +r.Atomics.store(s,1,-1) +r.Atomics.store(s,0,a.a) +A.tN(s,0) +r.Atomics.wait(s,1,-1) +s=r.Atomics.load(s,1) +if(s!==0)throw A.b(A.ca(s)) +return a.d.$1(q)}, +a1(a,b){var s=t.cb +return this.jh(a,b,s,s)}, +co(a,b){return this.a1(B.Z,new A.aW(a,b,0,0)).a}, +dg(a,b){this.a1(B.a_,new A.aW(a,b,0,0))}, +dh(a){return new v.G.URL(a,"file:///").pathname}, +b0(a,b){var s=a.a,r=this.a1(B.aa,new A.aW(s==null?A.oj(this.b,"/"):s,b,0,0)) +return new A.cR(new A.i5(this,r.b),r.a)}, +dk(a){this.a1(B.a4,new A.R(B.b.I(a.a,1000),0,0))}, +n(){this.a1(B.a0,B.h)}} +A.i5.prototype={ +gcq(){return 2048}, +eJ(a,b){var s,r,q,p,o,n,m,l,k,j,i=a.length +for(s=this.a,r=this.b,q=s.e.a,p=v.G,o=t.Z,n=0;i>0;){m=Math.min(65536,i) +i-=m +l=s.a1(B.a8,new A.R(r,b+n,m)).a +k=p.Uint8Array +j=[q] +j.push(0) +j.push(l) +A.hm(a,"set",o.a(A.e3(k,j)),n,null,null) +n+=l +if(l0;){o=Math.min(65536,n) +A.hm(r,"set",o===n&&p===0?a:J.d0(B.e.gaX(a),a.byteOffset+p,o),0,null,null) +s.a1(B.a3,new A.R(q,b+p,o)) +p+=o +n-=o}}} +A.kL.prototype={} +A.br.prototype={ +hu(a){var s,r +if(!(a instanceof A.b2))if(a instanceof A.R){s=this.b +s.$flags&2&&A.y(s,8) +s.setInt32(0,a.a,!1) +s.setInt32(4,a.b,!1) +s.setInt32(8,a.c,!1) +if(a instanceof A.aW){r=B.i.a4(a.d) +s.setInt32(12,r.length,!1) +B.e.b2(this.c,16,r)}}else throw A.b(A.a4("Message "+a.i(0)))}} +A.ac.prototype={ +ad(){return"WorkerOperation."+this.b}} +A.bC.prototype={} +A.b2.prototype={} +A.R.prototype={} +A.aW.prototype={} +A.iF.prototype={} +A.eQ.prototype={ +bT(a,b){return this.je(a,b)}, +fE(a){return this.bT(a,!1)}, +je(a,b){var s=0,r=A.k(t.eg),q,p=this,o,n,m,l,k,j,i,h +var $async$bT=A.l(function(c,d){if(c===1)return A.h(d,r) +for(;;)switch(s){case 0:k=A.ak(A.pc(a),t.N) +j=k.length +i=j>=1 +h=null +if(i){o=j-1 +n=B.c.a_(k,0,o) +h=k[o]}else n=null +if(!i)throw A.b(A.A("Pattern matching error")) +m=p.c +k=n.length,i=t.m,l=0 +case 3:if(!(l=i.length){s=5 +break}j=new A.mV(g,k,Math.min(4096,i.length-k)) +if(A.kq(m.value,"Blob"))b.push(A.kK(A.a9(m.value)).bG(j,n)) +else j.$1(h.a(m.value)) +s=4 +break +case 5:q=g +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$bJ,r)}, +cW(a){return this.k7(a)}, +k7(a){var s=0,r=A.k(t.S),q,p=this,o +var $async$cW=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:if((p.b.a.a&30)!==0)A.E(A.A("IDB transaction already completed")) +o=A +s=3 +return A.c(A.bn(p.d.put({name:a,length:0}),t.i),$async$cW) +case 3:q=o.B(c) +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$cW,r)}, +bh(a,b){return this.ln(a,b)}, +ln(a,b){var s=0,r=A.k(t.H),q=this,p,o,n,m,l +var $async$bh=A.l(function(c,d){if(c===1)return A.h(d,r) +for(;;)switch(s){case 0:if((q.b.a.a&30)!==0)A.E(A.A("IDB transaction already completed")) +s=2 +return A.c(q.e1(a),$async$bh) +case 2:p=d +o=b.b +n=A.r(o).h("bB<1>") +m=A.ak(new A.bB(o,n),n.h("d.E")) +B.c.hM(m) +s=3 +return A.c(A.pL(new A.D(m,new A.mW(new A.mX(q,a),b),A.O(m).h("D<1,C<~>>")),t.H),$async$bh) +case 3:s=b.c!==p.length?4:5 +break +case 4:l=new A.cK(q.d.openCursor(a),t.V) +s=6 +return A.c(l.k(),$async$bh) +case 6:s=7 +return A.c(A.bn(l.gm().update({name:p.name,length:b.c}),t.X),$async$bh) +case 7:case 5:return A.i(null,r)}}) +return A.j($async$bh,r)}, +bg(a,b,c){return this.lk(0,b,c)}, +lk(a,b,c){var s=0,r=A.k(t.H),q=this,p,o +var $async$bg=A.l(function(d,e){if(d===1)return A.h(e,r) +for(;;)switch(s){case 0:if((q.b.a.a&30)!==0)A.E(A.A("IDB transaction already completed")) +s=2 +return A.c(q.e1(b),$async$bg) +case 2:p=e +s=p.length>c?3:4 +break +case 3:s=5 +return A.c(A.bn(q.e.delete(q.j6(b,B.b.I(c,4096)*4096)),t.X),$async$bg) +case 5:case 4:o=new A.cK(q.d.openCursor(b),t.V) +s=6 +return A.c(o.k(),$async$bg) +case 6:s=7 +return A.c(A.bn(o.gm().update({name:p.name,length:c}),t.X),$async$bg) +case 7:return A.i(null,r)}}) +return A.j($async$bg,r)}, +cY(a){return this.ka(a)}, +ka(a){var s=0,r=A.k(t.H),q=this,p +var $async$cY=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:if((q.b.a.a&30)!==0)A.E(A.A("IDB transaction already completed")) +p=t.X +s=2 +return A.c(A.pL(A.f([A.bn(q.e.delete(q.e0(a,9007199254740992,0)),p),A.bn(q.d.delete(a),p)],t.fG),t.H),$async$cY) +case 2:return A.i(null,r)}}) +return A.j($async$cY,r)}} +A.mT.prototype={ +$0(){this.a.b.aJ()}, +$S:3} +A.mU.prototype={ +$0(){var s=this.a,r=s.a.error +if(r==null)r=new v.G.DOMException("IDB transaction error") +s.b.ag(r)}, +$S:3} +A.mS.prototype={ +$1(a){if(a==null)throw A.b(A.ad(this.a,"fileId","File not found in database")) +else return a}, +$S:87} +A.mV.prototype={ +$1(a){var s=this.a +s.b2(s,this.b,J.d0(a,0,this.c))}, +$S:88} +A.mX.prototype={ +hE(a,b){var s=0,r=A.k(t.H),q=this,p,o,n,m,l,k +var $async$$2=A.l(function(c,d){if(c===1)return A.h(d,r) +for(;;)switch(s){case 0:p=q.a.e +o=q.b +n=t.n +s=2 +return A.c(A.bn(p.openCursor(v.G.IDBKeyRange.only(A.f([o,a],n))),t.A),$async$$2) +case 2:m=d +l=t.u.a(B.e.gaX(b)) +k=t.X +s=m==null?3:5 +break +case 3:s=6 +return A.c(A.bn(p.put(l,A.f([o,a],n)),k),$async$$2) +case 6:s=4 +break +case 5:s=7 +return A.c(A.bn(m.update(l),k),$async$$2) +case 7:case 4:return A.i(null,r)}}) +return A.j($async$$2,r)}, +$2(a,b){return this.hE(a,b)}, +$S:89} +A.mW.prototype={ +$1(a){var s=this.b.b.j(0,a) +s.toString +return this.a.$2(a,s)}, +$S:90} +A.mz.prototype={ +jC(a,b,c){B.e.b2(this.b.hk(a,new A.mA(this,a)),b,c)}, +jV(a,b){var s,r,q,p,o,n,m,l +for(s=b.length,r=0;rp)B.e.b2(s,0,J.d0(B.e.gaX(r),r.byteOffset+p,Math.min(4096,q-p))) +return s}, +$S:125} +A.iB.prototype={} +A.d7.prototype={ +bY(a){var s=this +if(s.e||s.d.a==null)A.E(A.ca(10)) +if(a.ew(s.x)){s.aU(!0) +return a.d.a}else return A.b3(null,t.H)}, +aU(a){return this.jz(a)}, +jz(a){var s=0,r=A.k(t.H),q,p=this,o,n +var $async$aU=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:if(a&&!p.r){s=1 +break}s=!p.f&&!p.x.gB(0)?3:4 +break +case 3:p.f=!0 +o=p.x +n=A.ak(o,o.$ti.h("d.E")) +o.c3(0) +s=5 +return A.c(p.d.j3(n).ai(new A.kj(p,n,a)),$async$aU) +case 5:case 4:case 1:return A.i(q,r)}}) +return A.j($async$aU,r)}, +n(){var s=0,r=A.k(t.H),q,p=this,o,n +var $async$n=A.l(function(a,b){if(a===1)return A.h(b,r) +for(;;)switch(s){case 0:if(!p.e){o=p.bY(new A.f6(new A.kk(),new A.a1(new A.n($.m,t.D),t.F))) +p.e=!0 +p.aU(!1) +q=o +s=1 +break}else{n=p.x +if(!n.gB(0)){q=n.gD(0).d.a +s=1 +break}}case 1:return A.i(q,r)}}) +return A.j($async$n,r)}, +bp(a,b){return this.iB(a,b)}, +iB(a,b){var s=0,r=A.k(t.S),q,p=this,o,n +var $async$bp=A.l(function(c,d){if(c===1)return A.h(d,r) +for(;;)switch(s){case 0:n=p.z +s=n.a3(b)?3:5 +break +case 3:n=n.j(0,b) +n.toString +q=n +s=1 +break +s=4 +break +case 5:s=6 +return A.c(a.d_(b),$async$bp) +case 6:o=d +o.toString +n.t(0,b,o) +q=o +s=1 +break +case 4:case 1:return A.i(q,r)}}) +return A.j($async$bp,r)}, +bR(){var s=0,r=A.k(t.H),q=this,p +var $async$bR=A.l(function(a,b){if(a===1)return A.h(b,r) +for(;;)switch(s){case 0:p=A.f([],t.fG) +s=2 +return A.c(q.d.bs(new A.ki(q,p),"readonly"),$async$bR) +case 2:s=3 +return A.c(A.ua(p,t.H),$async$bR) +case 3:return A.i(null,r)}}) +return A.j($async$bR,r)}, +co(a,b){return this.w.d.a3(a)?1:0}, +dg(a,b){var s=this +s.w.d.F(0,a) +if(!s.y.F(0,a))s.bY(new A.f_(s,a,new A.a1(new A.n($.m,t.D),t.F)))}, +dh(a){return new v.G.URL(a,"file:///").pathname}, +b0(a,b){var s,r,q,p=this,o=a.a +if(o==null)o=A.oj(p.b,"/") +s=p.w +r=s.d.a3(o)?1:0 +q=s.b0(new A.eJ(o),b) +if(r===0)if((b&8)!==0)p.y.v(0,o) +else p.bY(new A.dC(p,o,new A.a1(new A.n($.m,t.D),t.F))) +return new A.cR(new A.iu(p,q.a,o),0)}, +dk(a){}} +A.kj.prototype={ +$0(){var s,r,q,p,o=this.a +o.f=!1 +for(s=this.b,r=s.length,q=0;q=2?1:0}, +cp(){}, +cr(){return this.b.cr()}, +di(a){this.b.d=a +return null}, +dl(a){}, +hv(a,b){return 12}, +cs(a){var s=this,r=s.a +if(r.e||r.d.a==null)A.E(A.ca(10)) +s.b.cs(a) +if(!r.y.H(0,s.c))r.bY(new A.f6(new A.mR(s,a),new A.a1(new A.n($.m,t.D),t.F)))}, +dm(a){this.b.d=a +return null}, +bi(a,b){var s,r,q,p,o,n,m=this,l=m.a +if(l.e||l.d.a==null)A.E(A.ca(10)) +s=m.c +if(l.y.H(0,s)){m.b.bi(a,b) +return}r=l.w.d.j(0,s) +if(r==null)r=new A.bg(new Uint8Array(0),0) +q=J.d0(B.e.gaX(r.a),0,r.b) +m.b.bi(a,b) +p=new Uint8Array(a.length) +B.e.b2(p,0,a) +o=A.f([],t.gQ) +n=$.m +o.push(new A.iB(b,p)) +l.bY(new A.dU(l,s,q,o,new A.a1(new A.n(n,t.D),t.F)))}, +$iaA:1, +$idw:1} +A.mR.prototype={ +$1(a){return this.hD(a)}, +hD(a){var s=0,r=A.k(t.H),q,p=this,o,n +var $async$$1=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:o=p.a +n=a +s=3 +return A.c(o.a.bp(a,o.c),$async$$1) +case 3:q=n.bg(0,c,p.b) +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$$1,r)}, +$S:18} +A.au.prototype={ +ew(a){a.cD(a.c,this,!1) +return!0}} +A.f6.prototype={ +Z(a){return this.w.$1(a)}} +A.f_.prototype={ +ew(a){var s,r,q,p +if(!a.gB(0)){s=a.gD(0) +for(r=this.x;s!=null;)if(s instanceof A.f_)if(s.x===r)return!1 +else s=s.gcf() +else if(s instanceof A.dU){q=s.gcf() +if(s.x===r){p=s.a +p.toString +p.e6(A.r(s).h("ay.E").a(s))}s=q}else if(s instanceof A.dC){if(s.x===r){r=s.a +r.toString +r.e6(A.r(s).h("ay.E").a(s)) +return!1}s=s.gcf()}else break}a.cD(a.c,this,!1) +return!0}, +Z(a){return this.l8(a)}, +l8(a){var s=0,r=A.k(t.H),q=this,p,o,n +var $async$Z=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:p=q.w +o=q.x +s=2 +return A.c(p.bp(a,o),$async$Z) +case 2:n=c +p.z.F(0,o) +s=3 +return A.c(a.cY(n),$async$Z) +case 3:return A.i(null,r)}}) +return A.j($async$Z,r)}} +A.dC.prototype={ +Z(a){return this.l7(a)}, +l7(a){var s=0,r=A.k(t.H),q=this,p,o,n +var $async$Z=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:p=q.x +o=q.w.z +n=p +s=2 +return A.c(a.cW(p),$async$Z) +case 2:o.t(0,n,c) +return A.i(null,r)}}) +return A.j($async$Z,r)}} +A.dU.prototype={ +ew(a){var s,r=a.b===0?null:a.gD(0) +for(s=this.x;r!=null;)if(r instanceof A.dU)if(r.x===s){B.c.aI(r.z,this.z) +return!1}else r=r.gcf() +else if(r instanceof A.dC){if(r.x===s)break +r=r.gcf()}else break +a.cD(a.c,this,!1) +return!0}, +Z(a){return this.l9(a)}, +l9(a){var s=0,r=A.k(t.H),q=this,p,o,n,m,l,k +var $async$Z=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:m=q.y +l=new A.mz(m,A.ao(t.S,t.E),m.length) +for(m=q.z,p=m.length,o=0;o0) +n.ca(B.L,o.getSize()>0)}return A.i(null,r)}}) +return A.j($async$bC,r)}} +A.l4.prototype={ +hB(a){var s=0,r=A.k(t.m),q,p=this,o,n +var $async$$1=A.l(function(b,c){if(b===1)return A.h(c,r) +for(;;)switch(s){case 0:o=t.m +s=3 +return A.c(A.V(p.a.getFileHandle(a,{create:!0}),o),$async$$1) +case 3:n=c.createSyncAccessHandle() +s=4 +return A.c(A.V(n,o),$async$$1) +case 4:q=c +s=1 +break +case 1:return A.i(q,r)}}) +return A.j($async$$1,r)}, +$1(a){return this.hB(a)}, +$S:92} +A.iL.prototype={ +eJ(a,b){return A.og(this.a.an().b8(this.b),a,{at:b})}, +df(){return this.d>=2?1:0}, +cp(){var s=this.a,r=this.b +s.an().b8(r).flush() +if(this.c)s.an().ca(r,!1)}, +cr(){return this.a.an().b8(this.b).getSize()}, +di(a){this.d=a}, +dl(a){this.a.an().b8(this.b).flush()}, +cs(a){this.a.an().b8(this.b).truncate(a)}, +dm(a){this.d=a}, +bi(a,b){if(A.oh(this.a.an().b8(this.b),a,{at:b})")),null)}, +i(a){var s=this.a,r=A.O(s) +return new A.D(s,new A.jd(new A.D(s,new A.je(),r.h("D<1,a>")).eo(0,0,B.u)),r.h("D<1,p>")).av(0,u.q)}, +$ia_:1} +A.ja.prototype={ +$1(a){return a.length!==0}, +$S:2} +A.jf.prototype={ +$1(a){return a.gc6()}, +$S:93} +A.je.prototype={ +$1(a){var s=a.gc6() +return new A.D(s,new A.jc(),A.O(s).h("D<1,a>")).eo(0,0,B.u)}, +$S:94} +A.jc.prototype={ +$1(a){return a.gbA().length}, +$S:38} +A.jd.prototype={ +$1(a){var s=a.gc6() +return new A.D(s,new A.jb(this.a),A.O(s).h("D<1,p>")).c8(0)}, +$S:96} +A.jb.prototype={ +$1(a){return B.a.hh(a.gbA(),this.a)+" "+A.t(a.geC())+"\n"}, +$S:24} +A.M.prototype={ +geA(){var s=this.a +if(s.gW()==="data")return"data:..." +return $.pp().l3(s)}, +gbA(){var s,r=this,q=r.b +if(q==null)return r.geA() +s=r.c +if(s==null)return r.geA()+" "+A.t(q) +return r.geA()+" "+A.t(q)+":"+A.t(s)}, +i(a){return this.gbA()+" in "+A.t(this.d)}, +geC(){return this.d}} +A.ka.prototype={ +$0(){var s,r,q,p,o,n,m,l=null,k=this.a +if(k==="...")return new A.M(A.al(l,l,l,l),l,l,"...") +s=$.tz().a8(k) +if(s==null)return new A.bt(A.al(l,"unparsed",l,l),k) +k=s.b +r=k[1] +r.toString +q=$.th() +r=A.bk(r,q,"") +p=A.bk(r,"","") +r=k[2] +q=r +q.toString +if(B.a.u(q,"1?A.bj(n[1],l):l +return new A.M(o,m,k>2?A.bj(n[2],l):l,p)}, +$S:13} +A.k8.prototype={ +$0(){var s,r,q,p,o,n="",m=this.a,l=$.ty().a8(m) +if(l!=null){s=l.aM("member") +m=l.aM("uri") +m.toString +r=A.hd(m) +m=l.aM("index") +m.toString +q=l.aM("offset") +q.toString +p=A.bj(q,16) +if(!(s==null))m=s +return new A.M(r,1,p+1,m)}l=$.tu().a8(m) +if(l!=null){m=new A.k9(m) +q=l.b +o=q[2] +if(o!=null){o=o +o.toString +q=q[1] +q.toString +q=A.bk(q,"",n) +q=A.bk(q,"Anonymous function",n) +return m.$2(o,A.bk(q,"(anonymous function)",n))}else{q=q[3] +q.toString +return m.$2(q,n)}}return new A.bt(A.al(null,"unparsed",null,null),m)}, +$S:13} +A.k9.prototype={ +$2(a,b){var s,r,q,p,o,n=null,m=$.tt(),l=m.a8(a) +for(;l!=null;a=s){s=l.b[1] +s.toString +l=m.a8(s)}if(a==="native")return new A.M(A.bu("native"),n,n,b) +r=$.tv().a8(a) +if(r==null)return new A.bt(A.al(n,"unparsed",n,n),this.a) +m=r.b +s=m[1] +s.toString +q=A.hd(s) +s=m[2] +s.toString +p=A.bj(s,n) +o=m[3] +return new A.M(q,p,o!=null?A.bj(o,n):n,b)}, +$S:99} +A.k5.prototype={ +$0(){var s,r,q,p,o=null,n=this.a,m=$.ti().a8(n) +if(m==null)return new A.bt(A.al(o,"unparsed",o,o),n) +n=m.b +s=n[1] +s.toString +r=A.bk(s,"/<","") +s=n[2] +s.toString +q=A.hd(s) +n=n[3] +n.toString +p=A.bj(n,o) +return new A.M(q,p,o,r.length===0||r==="anonymous"?"":r)}, +$S:13} +A.k6.prototype={ +$0(){var s,r,q,p,o,n,m,l,k=null,j=this.a,i=$.tk().a8(j) +if(i!=null){s=i.b +r=s[3] +q=r +q.toString +if(B.a.H(q," line "))return A.u2(j) +j=r +j.toString +p=A.hd(j) +o=s[1] +if(o!=null){j=s[2] +j.toString +o+=B.c.c8(A.b5(B.a.ed("/",j).gl(0),".",!1,t.N)) +if(o==="")o="" +o=B.a.hp(o,$.tp(),"")}else o="" +j=s[4] +if(j==="")n=k +else{j=j +j.toString +n=A.bj(j,k)}j=s[5] +if(j==null||j==="")m=k +else{j=j +j.toString +m=A.bj(j,k)}return new A.M(p,n,m,o)}i=$.tm().a8(j) +if(i!=null){j=i.aM("member") +j.toString +s=i.aM("uri") +s.toString +p=A.hd(s) +s=i.aM("index") +s.toString +r=i.aM("offset") +r.toString +l=A.bj(r,16) +if(!(j.length!==0))j=s +return new A.M(p,1,l+1,j)}i=$.tq().a8(j) +if(i!=null){j=i.aM("member") +j.toString +return new A.M(A.al(k,"wasm code",k,k),k,k,j)}return new A.bt(A.al(k,"unparsed",k,k),j)}, +$S:13} +A.k7.prototype={ +$0(){var s,r,q,p,o=null,n=this.a,m=$.tn().a8(n) +if(m==null)throw A.b(A.aj("Couldn't parse package:stack_trace stack trace line '"+n+"'.",o,o)) +n=m.b +s=n[1] +if(s==="data:...")r=A.qq("") +else{s=s +s.toString +r=A.bu(s)}if(r.gW()===""){s=$.pp() +r=s.ht(s.fS(s.a.d9(A.p_(r)),o,o,o,o,o,o,o,o,o,o,o,o,o,o))}s=n[2] +if(s==null)q=o +else{s=s +s.toString +q=A.bj(s,o)}s=n[3] +if(s==null)p=o +else{s=s +s.toString +p=A.bj(s,o)}return new A.M(r,q,p,n[4])}, +$S:13} +A.hp.prototype={ +gfQ(){var s,r=this,q=r.b +if(q===$){s=r.a.$0() +r.b!==$&&A.pj() +r.b=s +q=s}return q}, +gc6(){return this.gfQ().gc6()}, +i(a){return this.gfQ().i(0)}, +$ia_:1, +$ia0:1} +A.a0.prototype={ +i(a){var s=this.a,r=A.O(s) +return new A.D(s,new A.lq(new A.D(s,new A.lr(),r.h("D<1,a>")).eo(0,0,B.u)),r.h("D<1,p>")).c8(0)}, +$ia_:1, +gc6(){return this.a}} +A.lo.prototype={ +$0(){return A.qm(this.a.i(0))}, +$S:100} +A.lp.prototype={ +$1(a){return a.length!==0}, +$S:2} +A.ln.prototype={ +$1(a){return!B.a.u(a,$.tx())}, +$S:2} +A.lm.prototype={ +$1(a){return a!=="\tat "}, +$S:2} +A.lk.prototype={ +$1(a){return a.length!==0&&a!=="[native code]"}, +$S:2} +A.ll.prototype={ +$1(a){return!B.a.u(a,"=====")}, +$S:2} +A.lr.prototype={ +$1(a){return a.gbA().length}, +$S:38} +A.lq.prototype={ +$1(a){if(a instanceof A.bt)return a.i(0)+"\n" +return B.a.hh(a.gbA(),this.a)+" "+A.t(a.geC())+"\n"}, +$S:24} +A.bt.prototype={ +i(a){return this.w}, +$iM:1, +gbA(){return"unparsed"}, +geC(){return this.w}} +A.ee.prototype={} +A.eY.prototype={ +P(a,b,c,d){var s,r=this.b +if(r.d){a=null +d=null}s=this.a.P(a,b,c,d) +if(!r.d)r.c=s +return s}, +b_(a,b,c){return this.P(a,null,b,c)}, +eB(a,b){return this.P(a,null,b,null)}} +A.eX.prototype={ +n(){var s,r=this.hQ(),q=this.b +q.d=!0 +s=q.c +if(s!=null){s.cd(null) +s.eG(null)}return r}} +A.eo.prototype={ +ghP(){var s=this.b +s===$&&A.x() +return new A.at(s,A.r(s).h("at<1>"))}, +ghK(){var s=this.a +s===$&&A.x() +return s}, +hX(a,b,c,d){var s=this,r=$.m +s.a!==$&&A.iZ() +s.a=new A.f8(a,s,new A.a7(new A.n(r,t.D),t.h),!0) +r=A.eN(null,new A.kg(c,s),!0,d) +s.b!==$&&A.iZ() +s.b=r}, +j_(){var s,r +this.d=!0 +s=this.c +if(s!=null)s.J() +r=this.b +r===$&&A.x() +r.n()}} +A.kg.prototype={ +$0(){var s,r,q=this.b +if(q.d)return +s=this.a.a +r=q.b +r===$&&A.x() +q.c=s.b_(r.gjT(r),new A.kf(q),r.gfT())}, +$S:0} +A.kf.prototype={ +$0(){var s=this.a,r=s.a +r===$&&A.x() +r.j0() +s=s.b +s===$&&A.x() +s.n()}, +$S:0} +A.f8.prototype={ +v(a,b){if(this.e)throw A.b(A.A("Cannot add event after closing.")) +if(this.d)return +this.a.a.v(0,b)}, +a2(a,b){if(this.e)throw A.b(A.A("Cannot add event after closing.")) +if(this.d)return +this.iE(a,b)}, +iE(a,b){this.a.a.a2(a,b) +return}, +n(){var s=this +if(s.e)return s.c.a +s.e=!0 +if(!s.d){s.b.j_() +s.c.O(s.a.a.n())}return s.c.a}, +j0(){this.d=!0 +var s=this.c +if((s.a.a&30)===0)s.aJ() +return}, +$iae:1} +A.hO.prototype={} +A.eM.prototype={} +A.dt.prototype={ +gl(a){return this.b}, +j(a,b){if(b>=this.b)throw A.b(A.pO(b,this)) +return this.a[b]}, +t(a,b,c){var s +if(b>=this.b)throw A.b(A.pO(b,this)) +s=this.a +s.$flags&2&&A.y(s) +s[b]=c}, +sl(a,b){var s,r,q,p,o=this,n=o.b +if(bn){if(n===0)p=new Uint8Array(b) +else p=o.io(b) +B.e.ac(p,0,o.b,o.a) +o.a=p}}o.b=b}, +io(a){var s=this.a.length*2 +if(a!=null&&ss)throw A.b(A.W(c,0,s,null,null)) +s=this.a +if(d instanceof A.bg)B.e.N(s,b,c,d.a,e) +else B.e.N(s,b,c,d,e)}, +ac(a,b,c,d){return this.N(0,b,c,d,0)}} +A.iv.prototype={} +A.bg.prototype={} +A.of.prototype={} +A.f3.prototype={ +P(a,b,c,d){return A.aL(this.a,this.b,a,!1)}, +b_(a,b,c){return this.P(a,null,b,c)}} +A.io.prototype={ +J(){var s=this,r=A.b3(null,t.H) +if(s.b==null)return r +s.e7() +s.d=s.b=null +return r}, +cd(a){var s,r=this +if(r.b==null)throw A.b(A.A("Subscription has been canceled.")) +r.e7() +if(a==null)s=null +else{s=A.ru(new A.mx(a),t.m) +s=s==null?null:A.bi(s)}r.d=s +r.e5()}, +eG(a){}, +bD(){if(this.b==null)return;++this.a +this.e7()}, +bc(){var s=this +if(s.b==null||s.a<=0)return;--s.a +s.e5()}, +e5(){var s=this,r=s.d +if(r!=null&&s.a<=0)s.b.addEventListener(s.c,r,!1)}, +e7(){var s=this.d +if(s!=null)this.b.removeEventListener(this.c,s,!1)}} +A.mw.prototype={ +$1(a){return this.a.$1(a)}, +$S:1} +A.mx.prototype={ +$1(a){return this.a.$1(a)}, +$S:1};(function aliases(){var s=J.bY.prototype +s.hS=s.i +s=A.cI.prototype +s.hU=s.bK +s=A.af.prototype +s.ds=s.aQ +s.eW=s.a7 +s.eX=s.bo +s=A.fo.prototype +s.hV=s.ee +s=A.w.prototype +s.eV=s.N +s=A.d.prototype +s.hR=s.hL +s=A.d4.prototype +s.hQ=s.n +s=A.cB.prototype +s.hT=s.n})();(function installTearOffs(){var s=hunkHelpers._static_2,r=hunkHelpers._static_1,q=hunkHelpers._static_0,p=hunkHelpers.installStaticTearOff,o=hunkHelpers._instance_0u,n=hunkHelpers.installInstanceTearOff,m=hunkHelpers._instance_2u,l=hunkHelpers._instance_1i,k=hunkHelpers._instance_1u +s(J,"w6","uf",101) +r(A,"wK","uX",9) +r(A,"wL","uY",9) +r(A,"wM","uZ",9) +r(A,"wN","wk",102) +q(A,"rx","wD",0) +r(A,"wO","wl",15) +s(A,"wP","wn",6) +q(A,"rw","wm",0) +p(A,"wV",5,null,["$5"],["ww"],103,0) +p(A,"x_",4,null,["$1$4","$4"],["nD",function(a,b,c,d){return A.nD(a,b,c,d,t.z)}],104,0) +p(A,"x1",5,null,["$2$5","$5"],["nF",function(a,b,c,d,e){var i=t.z +return A.nF(a,b,c,d,e,i,i)}],105,0) +p(A,"x0",6,null,["$3$6","$6"],["nE",function(a,b,c,d,e,f){var i=t.z +return A.nE(a,b,c,d,e,f,i,i,i)}],106,0) +p(A,"wY",4,null,["$1$4","$4"],["rn",function(a,b,c,d){return A.rn(a,b,c,d,t.z)}],107,0) +p(A,"wZ",4,null,["$2$4","$4"],["ro",function(a,b,c,d){var i=t.z +return A.ro(a,b,c,d,i,i)}],108,0) +p(A,"wX",4,null,["$3$4","$4"],["rm",function(a,b,c,d){var i=t.z +return A.rm(a,b,c,d,i,i,i)}],109,0) +p(A,"wT",5,null,["$5"],["wv"],110,0) +p(A,"x2",4,null,["$4"],["nG"],111,0) +p(A,"wS",5,null,["$5"],["wu"],112,0) +p(A,"wR",5,null,["$5"],["wt"],113,0) +p(A,"wW",4,null,["$4"],["wx"],114,0) +r(A,"wQ","wp",115) +p(A,"wU",5,null,["$5"],["rl"],116,0) +var j +o(j=A.cJ.prototype,"gbO","al",0) +o(j,"gbP","am",0) +n(A.dB.prototype,"gk6",0,1,null,["$2","$1"],["by","ag"],30,0,0) +m(A.n.prototype,"gdF","ig",6) +l(j=A.cS.prototype,"gjT","v",7) +n(j,"gfT",0,1,null,["$2","$1"],["a2","jU"],30,0,0) +o(j=A.cf.prototype,"gbO","al",0) +o(j,"gbP","am",0) +o(j=A.af.prototype,"gbO","al",0) +o(j,"gbP","am",0) +o(A.f0.prototype,"gfs","iZ",0) +k(j=A.dO.prototype,"giT","iU",7) +m(j,"giX","iY",6) +o(j,"giV","iW",0) +o(j=A.dE.prototype,"gbO","al",0) +o(j,"gbP","am",0) +k(j,"gdQ","dR",7) +m(j,"gdU","dV",78) +o(j,"gdS","dT",0) +o(j=A.dL.prototype,"gbO","al",0) +o(j,"gbP","am",0) +k(j,"gdQ","dR",7) +m(j,"gdU","dV",6) +o(j,"gdS","dT",0) +k(A.dM.prototype,"gjZ","ee","X<2>(e?)") +r(A,"x6","uT",8) +p(A,"xx",2,null,["$1$2","$2"],["rG",function(a,b){return A.rG(a,b,t.o)}],117,0) +r(A,"xz","xG",5) +r(A,"xy","xF",5) +r(A,"xw","x7",5) +r(A,"xA","xM",5) +r(A,"xt","wI",5) +r(A,"xu","wJ",5) +r(A,"xv","x3",5) +k(A.ej.prototype,"giH","iI",7) +k(A.h4.prototype,"gip","dI",16) +k(A.i7.prototype,"gjF","cK",16) +r(A,"yZ","rb",22) +r(A,"yX","r9",22) +r(A,"yY","ra",22) +r(A,"rI","wo",28) +r(A,"rJ","wr",120) +r(A,"rH","vX",121) +k(j=A.fZ.prototype,"gkS","kT",4) +m(j,"gkQ","kR",65) +n(j,"glH",0,5,null,["$5"],["lI"],66,0,0) +n(j,"glw",0,3,null,["$3"],["lx"],67,0,0) +n(j,"glo",0,4,null,["$4"],["lp"],32,0,0) +n(j,"glD",0,4,null,["$4"],["lE"],32,0,0) +n(j,"glJ",0,3,null,["$3"],["lK"],69,0,0) +m(j,"glO","lP",33) +m(j,"glu","lv",33) +k(j,"gls","lt",20) +n(j,"glL",0,4,null,["$4"],["lM"],34,0,0) +n(j,"glW",0,4,null,["$4"],["lX"],34,0,0) +m(j,"glS","lT",73) +m(j,"glQ","lR",12) +m(j,"glB","lC",12) +m(j,"glF","lG",12) +m(j,"glU","lV",12) +m(j,"glq","lr",12) +k(j,"gcq","ly",20) +n(j,"glz",0,3,null,["$3"],["lA"],75,0,0) +k(j,"gdj","lN",20) +k(j,"gkm","kn",9) +k(j,"gkh","ki",76) +n(j,"gkk",0,5,null,["$5"],["kl"],77,0,0) +n(j,"gks",0,4,null,["$4"],["kt"],21,0,0) +n(j,"gkw",0,4,null,["$4"],["kx"],21,0,0) +n(j,"gku",0,4,null,["$4"],["kv"],21,0,0) +m(j,"gky","kz",35) +m(j,"gkq","kr",35) +n(j,"gko",0,5,null,["$5"],["kp"],80,0,0) +m(j,"gkf","kg",81) +m(j,"gkd","ke",82) +n(j,"gkb",0,3,null,["$3"],["kc"],83,0,0) +o(A.dy.prototype,"gc4","n",0) +r(A,"bS","un",122) +r(A,"ba","uo",123) +r(A,"pi","up",124) +k(A.eQ.prototype,"gj8","j9",85) +o(A.d7.prototype,"gc4","n",10) +o(A.dq.prototype,"gc4","n",0) +r(A,"xf","u9",14) +r(A,"rA","u8",14) +r(A,"xd","u6",14) +r(A,"xe","u7",14) +r(A,"xQ","uM",36) +r(A,"xP","uL",36)})();(function inheritance(){var s=hunkHelpers.mixin,r=hunkHelpers.inherit,q=hunkHelpers.inheritMany +r(A.e,null) +q(A.e,[A.on,J.hi,A.eH,J.fL,A.d,A.fU,A.L,A.w,A.cq,A.kN,A.b4,A.dc,A.cG,A.ha,A.hR,A.hM,A.hN,A.h7,A.i8,A.eq,A.en,A.hV,A.hQ,A.fi,A.ef,A.ix,A.lt,A.hD,A.el,A.fm,A.S,A.kv,A.hr,A.db,A.hq,A.cx,A.dJ,A.m5,A.ds,A.ne,A.ml,A.iS,A.be,A.ir,A.nk,A.iP,A.ia,A.iN,A.U,A.X,A.af,A.cI,A.f7,A.dB,A.cg,A.n,A.ib,A.hP,A.cS,A.iO,A.ic,A.dP,A.il,A.mu,A.fh,A.f0,A.dO,A.f2,A.dF,A.av,A.iU,A.dV,A.fz,A.is,A.dp,A.n_,A.dI,A.iz,A.ay,A.iA,A.cr,A.cs,A.ns,A.fy,A.a8,A.iq,A.eh,A.bx,A.mv,A.hE,A.eK,A.ip,A.aF,A.hh,A.aP,A.N,A.dQ,A.aD,A.fv,A.hY,A.b7,A.hb,A.hC,A.mY,A.d4,A.h1,A.hs,A.hB,A.hW,A.ej,A.iC,A.fX,A.h5,A.h4,A.bZ,A.aQ,A.bW,A.c2,A.bp,A.c4,A.bV,A.c5,A.c3,A.bF,A.bI,A.kO,A.fj,A.i7,A.bK,A.bU,A.ec,A.aq,A.ea,A.d2,A.kG,A.ls,A.jM,A.dj,A.kH,A.eC,A.kF,A.bq,A.jN,A.lH,A.h6,A.dm,A.lF,A.kW,A.fY,A.li,A.kD,A.hF,A.c8,A.cn,A.h_,A.l6,A.d3,A.as,A.fS,A.ju,A.iJ,A.n3,A.cw,A.aJ,A.eJ,A.lP,A.lG,A.lR,A.lQ,A.cb,A.bN,A.fZ,A.bG,A.cK,A.lL,A.kL,A.br,A.bC,A.iF,A.eQ,A.dK,A.j6,A.f9,A.mz,A.iB,A.iu,A.n0,A.lA,A.bm,A.M,A.hp,A.a0,A.bt,A.eM,A.f8,A.hO,A.of,A.io]) +q(J.hi,[J.hk,J.et,J.eu,J.aN,J.d9,J.d8,J.bX]) +q(J.eu,[J.bY,J.u,A.de,A.ey]) +q(J.bY,[J.hG,J.cF,J.bz]) +r(J.hj,A.eH) +r(J.kr,J.u) +q(J.d8,[J.es,J.hl]) +q(A.d,[A.ce,A.q,A.aG,A.aK,A.em,A.cE,A.bJ,A.eI,A.eR,A.by,A.cP,A.i9,A.iM,A.dR,A.cy]) +q(A.ce,[A.cp,A.fA]) +r(A.f1,A.cp) +r(A.eW,A.fA) +r(A.ai,A.eW) +q(A.L,[A.da,A.bL,A.hn,A.hU,A.hK,A.im,A.eD,A.fO,A.bc,A.eP,A.hT,A.aI,A.fW]) +q(A.w,[A.du,A.i2,A.dx,A.dt]) +r(A.fV,A.du) +q(A.cq,[A.jg,A.kl,A.jh,A.lj,A.nS,A.nU,A.m7,A.m6,A.nu,A.nf,A.nh,A.ng,A.kd,A.kb,A.mD,A.mC,A.mO,A.lg,A.lf,A.ld,A.lb,A.nd,A.mt,A.ms,A.n8,A.n7,A.mQ,A.kz,A.mi,A.nn,A.nW,A.o0,A.o1,A.nM,A.jT,A.jU,A.jV,A.kT,A.kU,A.kV,A.kR,A.m_,A.lX,A.lY,A.lV,A.m0,A.lZ,A.kI,A.k1,A.nH,A.kt,A.ku,A.ky,A.lS,A.lT,A.jP,A.l1,A.nK,A.nZ,A.jW,A.kM,A.jm,A.jn,A.jo,A.l0,A.kX,A.l_,A.kY,A.kZ,A.js,A.jt,A.nI,A.m4,A.l7,A.o_,A.o3,A.o4,A.j5,A.mo,A.mp,A.jk,A.jl,A.jp,A.jq,A.jr,A.j9,A.j7,A.mS,A.mV,A.mW,A.kk,A.ki,A.mR,A.l4,A.lB,A.lC,A.lD,A.lE,A.ja,A.jf,A.je,A.jc,A.jd,A.jb,A.lp,A.ln,A.lm,A.lk,A.ll,A.lr,A.lq,A.mw,A.mx]) +q(A.jg,[A.nY,A.m8,A.m9,A.nj,A.ni,A.kc,A.mF,A.mK,A.mJ,A.mH,A.mG,A.mN,A.mM,A.mL,A.lh,A.le,A.lc,A.la,A.nc,A.nb,A.mk,A.mj,A.n1,A.nx,A.ny,A.mr,A.mq,A.n6,A.n5,A.nC,A.nr,A.nq,A.jS,A.kP,A.kQ,A.kS,A.m1,A.m2,A.lW,A.o2,A.ma,A.mf,A.md,A.me,A.mc,A.mb,A.n9,A.na,A.jR,A.jQ,A.my,A.kw,A.kx,A.lU,A.jO,A.k_,A.jX,A.jY,A.jZ,A.jK,A.o5,A.jy,A.jv,A.jA,A.jC,A.jE,A.jx,A.jD,A.jI,A.jG,A.jF,A.jz,A.jB,A.jH,A.jw,A.j3,A.j4,A.lM,A.j8,A.mT,A.mU,A.mA,A.kj,A.ka,A.k8,A.k5,A.k6,A.k7,A.lo,A.kg,A.kf]) +q(A.q,[A.Q,A.cv,A.bB,A.ew,A.ev,A.cO,A.fb]) +q(A.Q,[A.cD,A.D,A.eG]) +r(A.cu,A.aG) +r(A.ek,A.cE) +r(A.d5,A.bJ) +r(A.ct,A.by) +r(A.iD,A.fi) +q(A.iD,[A.ag,A.cR,A.iE]) +r(A.eg,A.ef) +r(A.er,A.kl) +r(A.eA,A.bL) +q(A.lj,[A.l9,A.eb]) +q(A.S,[A.bA,A.cN]) +q(A.jh,[A.ks,A.nT,A.nv,A.nJ,A.ke,A.mE,A.mP,A.nw,A.kh,A.kA,A.mh,A.ly,A.lK,A.lJ,A.lI,A.jL,A.mX,A.k9]) +r(A.dd,A.de) +q(A.ey,[A.cz,A.dg]) +q(A.dg,[A.fd,A.ff]) +r(A.fe,A.fd) +r(A.c_,A.fe) +r(A.fg,A.ff) +r(A.aX,A.fg) +q(A.c_,[A.hu,A.hv]) +q(A.aX,[A.hw,A.df,A.hx,A.hy,A.hz,A.ez,A.c0]) +r(A.fq,A.im) +q(A.X,[A.dN,A.f5,A.eU,A.e9,A.eY,A.f3]) +r(A.at,A.dN) +r(A.eV,A.at) +q(A.af,[A.cf,A.dE,A.dL]) +r(A.cJ,A.cf) +r(A.fp,A.cI) +q(A.dB,[A.a7,A.a1]) +q(A.cS,[A.dA,A.dS]) +q(A.il,[A.dD,A.eZ]) +r(A.fc,A.f5) +r(A.fo,A.hP) +r(A.dM,A.fo) +q(A.iU,[A.ij,A.iI]) +r(A.dG,A.cN) +r(A.fk,A.dp) +r(A.fa,A.fk) +q(A.cr,[A.h8,A.fQ]) +q(A.h8,[A.fM,A.i0]) +q(A.cs,[A.iR,A.fR,A.i1]) +r(A.fN,A.iR) +q(A.bc,[A.dk,A.ep]) +r(A.ik,A.fv) +q(A.bZ,[A.ar,A.bf,A.bo,A.bw]) +q(A.mv,[A.dh,A.cC,A.c1,A.dv,A.c7,A.cA,A.cc,A.bO,A.kC,A.ac,A.d6]) +r(A.jJ,A.kG) +r(A.kB,A.ls) +q(A.jM,[A.hA,A.k0]) +q(A.aq,[A.id,A.dH,A.ho]) +q(A.id,[A.iQ,A.h2,A.ie,A.f4]) +r(A.fn,A.iQ) +r(A.iw,A.dH) +r(A.cB,A.jJ) +r(A.fl,A.k0) +q(A.lH,[A.ji,A.dz,A.dn,A.dl,A.eL,A.h3]) +q(A.ji,[A.c6,A.ei]) +r(A.mn,A.kH) +r(A.i4,A.h2) +r(A.iT,A.cB) +r(A.kp,A.li) +q(A.kp,[A.kE,A.lz,A.m3]) +r(A.dr,A.d3) +r(A.fT,A.as) +q(A.fT,[A.he,A.dy,A.d7,A.dq]) +q(A.fS,[A.it,A.i5,A.iL]) +r(A.iG,A.ju) +r(A.iH,A.iG) +r(A.hJ,A.iH) +r(A.iK,A.iJ) +r(A.bs,A.iK) +q(A.ay,[A.cH,A.au]) +r(A.i6,A.l6) +q(A.bC,[A.b2,A.R]) +r(A.aW,A.R) +q(A.au,[A.f6,A.f_,A.dC,A.dU]) +q(A.eM,[A.ee,A.eo]) +r(A.eX,A.d4) +r(A.iv,A.dt) +r(A.bg,A.iv) +s(A.du,A.hV) +s(A.fA,A.w) +s(A.fd,A.w) +s(A.fe,A.en) +s(A.ff,A.w) +s(A.fg,A.en) +s(A.dA,A.ic) +s(A.dS,A.iO) +s(A.iG,A.w) +s(A.iH,A.hB) +s(A.iJ,A.hW) +s(A.iK,A.S)})() +var v={G:typeof self!="undefined"?self:globalThis,typeUniverse:{eC:new Map(),tR:{},eT:{},tPV:{},sEA:[]},mangledGlobalNames:{a:"int",F:"double",b0:"num",p:"String",I:"bool",N:"Null",o:"List",e:"Object",ap:"Map",z:"JSObject"},mangledNames:{},types:["~()","~(z)","I(p)","N()","~(a)","F(b0)","~(e,a_)","~(e?)","p(p)","~(~())","C<~>()","N(z)","a(aA,a)","M()","M(p)","~(@)","e?(e?)","C()","C<~>(f9)","~(z?,o?)","a(aA)","~(bG,a,a,a)","p(a)","@()","p(M)","I(~)","C()","N(@)","b0?(o)","N(e,a_)","~(e[a_?])","I()","a(as,a,a,a)","a(as,a)","a(aA,a,a,aN)","~(bG,a)","a0(p)","a(a)","a(M)","~(ae)","N(@,a_)","~(a,@)","C<~>(ar)","C()","ap(o)","a(o)","a?(a)","N(aq)","C(~)","N(~())","N(~)","bH?/(ar)","I(a)","z(u)","dm()","C()","C()","0&(p,a?)","~(I,I,I,o<+(bO,p)>)","@(p)","p(p?)","p(e?)","~(os,o)","a()","~(v,T,v,~())","~(aN,a)","aA?(as,a,a,a,a)","a(as,a,a)","C()","a(as?,a,a)","bU<@>?()","N(I)","ar()","a(aA,aN)","@(@)","a(aA,a,a)","a(a())","~(~(a,p,a),a,a,a,aN)","~(@,a_)","bf()","a(bG,a,a,a,a)","a(a(a),a)","a(ov,a)","a(ov,a,a)","bp()","~(dK)","~(@,@)","z(z?)","~(co)","C<~>(a,aY)","C<~>(a)","o(u)","C(p)","o(a0)","a(a0)","bK(e?)","p(a0)","C()","~(e?,e?)","M(p,p)","a0()","a(@,@)","I(e?)","~(v?,T?,v,e,a_)","0^(v?,T?,v,0^())","0^(v?,T?,v,0^(1^),1^)","0^(v?,T?,v,0^(1^,2^),1^,2^)","0^()(v,T,v,0^())","0^(1^)(v,T,v,0^(1^))","0^(1^,2^)(v,T,v,0^(1^,2^))","U?(v,T,v,e,a_?)","~(v?,T?,v,~())","eO(v,T,v,bx,~())","eO(v,T,v,bx,~(eO))","~(v,T,v,p)","~(p)","v(v?,T?,v,oE?,ap?)","0^(0^,0^)","@(@,p)","a(a,a)","I?(o)","I?(o<@>)","b2(br)","R(br)","aW(br)","aY()","z()"],interceptorsByTag:null,leafTags:null,arrayRti:Symbol("$ti"),rttc:{"2;":(a,b)=>c=>c instanceof A.ag&&a.b(c.a)&&b.b(c.b),"2;file,outFlags":(a,b)=>c=>c instanceof A.cR&&a.b(c.a)&&b.b(c.b),"2;result,resultCode":(a,b)=>c=>c instanceof A.iE&&a.b(c.a)&&b.b(c.b)}} +A.vp(v.typeUniverse,JSON.parse('{"hG":"bY","cF":"bY","bz":"bY","y2":"de","u":{"o":["1"],"q":["1"],"z":[],"d":["1"],"ax":["1"]},"hk":{"I":[],"K":[]},"et":{"N":[],"K":[]},"eu":{"z":[]},"bY":{"z":[]},"hj":{"eH":[]},"kr":{"u":["1"],"o":["1"],"q":["1"],"z":[],"d":["1"],"ax":["1"]},"d8":{"F":[],"b0":[]},"es":{"F":[],"a":[],"b0":[],"K":[]},"hl":{"F":[],"b0":[],"K":[]},"bX":{"p":[],"ax":["@"],"K":[]},"ce":{"d":["2"]},"cp":{"ce":["1","2"],"d":["2"],"d.E":"2"},"f1":{"cp":["1","2"],"ce":["1","2"],"q":["2"],"d":["2"],"d.E":"2"},"eW":{"w":["2"],"o":["2"],"ce":["1","2"],"q":["2"],"d":["2"]},"ai":{"eW":["1","2"],"w":["2"],"o":["2"],"ce":["1","2"],"q":["2"],"d":["2"],"w.E":"2","d.E":"2"},"da":{"L":[]},"fV":{"w":["a"],"o":["a"],"q":["a"],"d":["a"],"w.E":"a"},"q":{"d":["1"]},"Q":{"q":["1"],"d":["1"]},"cD":{"Q":["1"],"q":["1"],"d":["1"],"d.E":"1","Q.E":"1"},"aG":{"d":["2"],"d.E":"2"},"cu":{"aG":["1","2"],"q":["2"],"d":["2"],"d.E":"2"},"D":{"Q":["2"],"q":["2"],"d":["2"],"d.E":"2","Q.E":"2"},"aK":{"d":["1"],"d.E":"1"},"em":{"d":["2"],"d.E":"2"},"cE":{"d":["1"],"d.E":"1"},"ek":{"cE":["1"],"q":["1"],"d":["1"],"d.E":"1"},"bJ":{"d":["1"],"d.E":"1"},"d5":{"bJ":["1"],"q":["1"],"d":["1"],"d.E":"1"},"eI":{"d":["1"],"d.E":"1"},"cv":{"q":["1"],"d":["1"],"d.E":"1"},"eR":{"d":["1"],"d.E":"1"},"by":{"d":["+(a,1)"],"d.E":"+(a,1)"},"ct":{"by":["1"],"q":["+(a,1)"],"d":["+(a,1)"],"d.E":"+(a,1)"},"du":{"w":["1"],"o":["1"],"q":["1"],"d":["1"]},"eG":{"Q":["1"],"q":["1"],"d":["1"],"d.E":"1","Q.E":"1"},"ef":{"ap":["1","2"]},"eg":{"ef":["1","2"],"ap":["1","2"]},"cP":{"d":["1"],"d.E":"1"},"eA":{"bL":[],"L":[]},"hn":{"L":[]},"hU":{"L":[]},"hD":{"a6":[]},"fm":{"a_":[]},"hK":{"L":[]},"bA":{"S":["1","2"],"ap":["1","2"],"S.V":"2","S.K":"1"},"bB":{"q":["1"],"d":["1"],"d.E":"1"},"ew":{"q":["1"],"d":["1"],"d.E":"1"},"ev":{"q":["aP<1,2>"],"d":["aP<1,2>"],"d.E":"aP<1,2>"},"dJ":{"hI":[],"ex":[]},"i9":{"d":["hI"],"d.E":"hI"},"ds":{"ex":[]},"iM":{"d":["ex"],"d.E":"ex"},"dd":{"z":[],"co":[],"K":[]},"cz":{"oc":[],"z":[],"K":[]},"df":{"aX":[],"kn":[],"w":["a"],"o":["a"],"aV":["a"],"q":["a"],"z":[],"ax":["a"],"d":["a"],"K":[],"w.E":"a"},"c0":{"aX":[],"aY":[],"w":["a"],"o":["a"],"aV":["a"],"q":["a"],"z":[],"ax":["a"],"d":["a"],"K":[],"w.E":"a"},"de":{"z":[],"co":[],"K":[]},"ey":{"z":[]},"iS":{"co":[]},"dg":{"aV":["1"],"z":[],"ax":["1"]},"c_":{"w":["F"],"o":["F"],"aV":["F"],"q":["F"],"z":[],"ax":["F"],"d":["F"]},"aX":{"w":["a"],"o":["a"],"aV":["a"],"q":["a"],"z":[],"ax":["a"],"d":["a"]},"hu":{"c_":[],"k3":[],"w":["F"],"o":["F"],"aV":["F"],"q":["F"],"z":[],"ax":["F"],"d":["F"],"K":[],"w.E":"F"},"hv":{"c_":[],"k4":[],"w":["F"],"o":["F"],"aV":["F"],"q":["F"],"z":[],"ax":["F"],"d":["F"],"K":[],"w.E":"F"},"hw":{"aX":[],"km":[],"w":["a"],"o":["a"],"aV":["a"],"q":["a"],"z":[],"ax":["a"],"d":["a"],"K":[],"w.E":"a"},"hx":{"aX":[],"ko":[],"w":["a"],"o":["a"],"aV":["a"],"q":["a"],"z":[],"ax":["a"],"d":["a"],"K":[],"w.E":"a"},"hy":{"aX":[],"lv":[],"w":["a"],"o":["a"],"aV":["a"],"q":["a"],"z":[],"ax":["a"],"d":["a"],"K":[],"w.E":"a"},"hz":{"aX":[],"lw":[],"w":["a"],"o":["a"],"aV":["a"],"q":["a"],"z":[],"ax":["a"],"d":["a"],"K":[],"w.E":"a"},"ez":{"aX":[],"lx":[],"w":["a"],"o":["a"],"aV":["a"],"q":["a"],"z":[],"ax":["a"],"d":["a"],"K":[],"w.E":"a"},"im":{"L":[]},"fq":{"bL":[],"L":[]},"U":{"L":[]},"af":{"af.T":"1"},"dF":{"ae":["1"]},"dR":{"d":["1"],"d.E":"1"},"eV":{"at":["1"],"dN":["1"],"X":["1"],"X.T":"1"},"cJ":{"cf":["1"],"af":["1"],"af.T":"1"},"cI":{"ae":["1"]},"fp":{"cI":["1"],"ae":["1"]},"eD":{"L":[]},"a7":{"dB":["1"]},"a1":{"dB":["1"]},"n":{"C":["1"]},"cS":{"ae":["1"]},"dA":{"cS":["1"],"ae":["1"]},"dS":{"cS":["1"],"ae":["1"]},"at":{"dN":["1"],"X":["1"],"X.T":"1"},"cf":{"af":["1"],"af.T":"1"},"dP":{"ae":["1"]},"dN":{"X":["1"]},"f5":{"X":["2"]},"dE":{"af":["2"],"af.T":"2"},"fc":{"f5":["1","2"],"X":["2"],"X.T":"2"},"f2":{"ae":["1"]},"dL":{"af":["2"],"af.T":"2"},"eU":{"X":["2"],"X.T":"2"},"dM":{"fo":["1","2"]},"iU":{"v":[]},"ij":{"v":[]},"iI":{"v":[]},"dV":{"T":[]},"fz":{"oE":[]},"cN":{"S":["1","2"],"ap":["1","2"],"S.V":"2","S.K":"1"},"dG":{"cN":["1","2"],"S":["1","2"],"ap":["1","2"],"S.V":"2","S.K":"1"},"cO":{"q":["1"],"d":["1"],"d.E":"1"},"fa":{"fk":["1"],"dp":["1"],"q":["1"],"d":["1"]},"cy":{"d":["1"],"d.E":"1"},"w":{"o":["1"],"q":["1"],"d":["1"]},"S":{"ap":["1","2"]},"fb":{"q":["2"],"d":["2"],"d.E":"2"},"dp":{"q":["1"],"d":["1"]},"fk":{"dp":["1"],"q":["1"],"d":["1"]},"fM":{"cr":["p","o"]},"iR":{"cs":["p","o"]},"fN":{"cs":["p","o"]},"fQ":{"cr":["o","p"]},"fR":{"cs":["o","p"]},"h8":{"cr":["p","o"]},"i0":{"cr":["p","o"]},"i1":{"cs":["p","o"]},"F":{"b0":[]},"a":{"b0":[]},"o":{"q":["1"],"d":["1"]},"hI":{"ex":[]},"fO":{"L":[]},"bL":{"L":[]},"bc":{"L":[]},"dk":{"L":[]},"ep":{"L":[]},"eP":{"L":[]},"hT":{"L":[]},"aI":{"L":[]},"fW":{"L":[]},"hE":{"L":[]},"eK":{"L":[]},"ip":{"a6":[]},"aF":{"a6":[]},"hh":{"a6":[],"L":[]},"dQ":{"a_":[]},"fv":{"hX":[]},"b7":{"hX":[]},"ik":{"hX":[]},"hC":{"a6":[]},"d4":{"ae":["1"]},"fX":{"a6":[]},"h5":{"a6":[]},"ar":{"bZ":[]},"bf":{"bZ":[]},"bp":{"az":[]},"bF":{"az":[]},"aQ":{"bH":[]},"bo":{"bZ":[]},"bw":{"bZ":[]},"dh":{"az":[]},"bW":{"az":[]},"c2":{"az":[]},"c4":{"az":[]},"bV":{"az":[]},"c5":{"az":[]},"c3":{"az":[]},"bI":{"bH":[]},"ec":{"a6":[]},"id":{"aq":[]},"iQ":{"hS":[],"aq":[]},"fn":{"hS":[],"aq":[]},"h2":{"aq":[]},"ie":{"aq":[]},"f4":{"aq":[]},"dH":{"aq":[]},"iw":{"hS":[],"aq":[]},"ho":{"aq":[]},"dz":{"a6":[]},"i4":{"aq":[]},"iT":{"cB":["od"],"cB.0":"od"},"hF":{"a6":[]},"c8":{"a6":[]},"h_":{"od":[]},"i2":{"w":["e?"],"o":["e?"],"q":["e?"],"d":["e?"],"w.E":"e?"},"dr":{"d3":[]},"he":{"as":[]},"it":{"dw":[],"aA":[]},"bs":{"S":["p","@"],"ap":["p","@"],"S.V":"@","S.K":"p"},"hJ":{"w":["bs"],"o":["bs"],"q":["bs"],"d":["bs"],"w.E":"bs"},"aJ":{"a6":[]},"fT":{"as":[]},"fS":{"dw":[],"aA":[]},"cH":{"ay":["cH"],"ay.E":"cH"},"bN":{"ot":[]},"cb":{"os":[]},"dx":{"w":["bN"],"o":["bN"],"q":["bN"],"d":["bN"],"w.E":"bN"},"e9":{"X":["1"],"X.T":"1"},"dy":{"as":[]},"i5":{"dw":[],"aA":[]},"b2":{"bC":[]},"R":{"bC":[]},"aW":{"R":[],"bC":[]},"d7":{"as":[]},"au":{"ay":["au"]},"iu":{"dw":[],"aA":[]},"f6":{"au":[],"ay":["au"],"ay.E":"au"},"f_":{"au":[],"ay":["au"],"ay.E":"au"},"dC":{"au":[],"ay":["au"],"ay.E":"au"},"dU":{"au":[],"ay":["au"],"ay.E":"au"},"dq":{"as":[]},"iL":{"dw":[],"aA":[]},"bm":{"a_":[]},"hp":{"a0":[],"a_":[]},"a0":{"a_":[]},"bt":{"M":[]},"ee":{"eM":["1"]},"eY":{"X":["1"],"X.T":"1"},"eX":{"ae":["1"]},"eo":{"eM":["1"]},"f8":{"ae":["1"]},"bg":{"dt":["a"],"w":["a"],"o":["a"],"q":["a"],"d":["a"],"w.E":"a"},"dt":{"w":["1"],"o":["1"],"q":["1"],"d":["1"]},"iv":{"dt":["a"],"w":["a"],"o":["a"],"q":["a"],"d":["a"]},"f3":{"X":["1"],"X.T":"1"},"ko":{"o":["a"],"q":["a"],"d":["a"]},"aY":{"o":["a"],"q":["a"],"d":["a"]},"lx":{"o":["a"],"q":["a"],"d":["a"]},"km":{"o":["a"],"q":["a"],"d":["a"]},"lv":{"o":["a"],"q":["a"],"d":["a"]},"kn":{"o":["a"],"q":["a"],"d":["a"]},"lw":{"o":["a"],"q":["a"],"d":["a"]},"k3":{"o":["F"],"q":["F"],"d":["F"]},"k4":{"o":["F"],"q":["F"],"d":["F"]}}')) +A.vo(v.typeUniverse,JSON.parse('{"cG":1,"hM":1,"hN":1,"h7":1,"eq":1,"en":1,"hV":1,"du":1,"fA":2,"hr":1,"db":1,"dg":1,"ae":1,"iN":1,"eD":2,"hP":2,"iO":1,"ic":1,"dP":1,"il":1,"dD":1,"fh":1,"f0":1,"dO":1,"f2":1,"av":1,"hb":1,"d4":1,"h1":1,"hs":1,"hB":1,"hW":2,"tL":1,"eX":1,"f8":1,"io":1}')) +var u={v:"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\u03f6\x00\u0404\u03f4 \u03f4\u03f6\u01f6\u01f6\u03f6\u03fc\u01f4\u03ff\u03ff\u0584\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u05d4\u01f4\x00\u01f4\x00\u0504\u05c4\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u0400\x00\u0400\u0200\u03f7\u0200\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u03ff\u0200\u0200\u0200\u03f7\x00",q:"===== asynchronous gap ===========================\n",l:"Cannot extract a file path from a URI with a fragment component",y:"Cannot extract a file path from a URI with a query component",j:"Cannot extract a non-Windows file path from a file URI with an authority",o:"Cannot fire new event. Controller is already firing an event",c:"Error handler must accept one Object or one Object and a StackTrace as arguments, and return a value of the returned future's type",D:"Tried to operate on a released prepared statement"} +var t=(function rtii(){var s=A.aB +return{b9:s("tL"),cO:s("e9>"),w:s("co"),fd:s("oc"),g1:s("bU<@>"),eT:s("d3"),ed:s("ei"),gw:s("ej"),Q:s("q<@>"),p:s("b2"),C:s("L"),g8:s("a6"),G:s("R"),h4:s("k3"),gN:s("k4"),B:s("M"),b8:s("y_"),aQ:s("C"),bF:s("C"),cG:s("C"),eY:s("C"),bd:s("d7"),dQ:s("km"),an:s("kn"),gj:s("ko"),hf:s("d<@>"),b:s("u"),cf:s("u"),e:s("u"),fG:s("u>"),fk:s("u>"),W:s("u"),gP:s("u>"),gz:s("u>"),d:s("u>"),f:s("u"),L:s("u<+(bO,p)>"),bb:s("u"),s:s("u

"),j:s("o<@>"),I:s("o"),ee:s("o"),g6:s("ap"),eO:s("ap<@,@>"),M:s("aG"),fe:s("D"),do:s("D"),fJ:s("bZ"),cb:s("bC"),eN:s("aW"),u:s("dd"),gT:s("cz"),ha:s("df"),aV:s("c_"),eB:s("aX"),Z:s("c0"),bw:s("bF"),P:s("N"),K:s("e"),x:s("aq"),aj:s("dj"),fl:s("y4"),bQ:s("+()"),e1:s("+(z?,z)"),cV:s("+(e?,a)"),cz:s("hI"),al:s("ar"),cc:s("bH"),bJ:s("eG

"),fE:s("dm"),fL:s("c6"),gW:s("dq"),f_:s("c8"),l:s("a_"),a7:s("hO"),N:s("p"),aF:s("eO"),a:s("a0"),v:s("hS"),dm:s("K"),eK:s("bL"),h7:s("lv"),bv:s("lw"),fQ:s("bg"),go:s("lx"),E:s("aY"),ak:s("cF"),dD:s("hX"),ei:s("eQ"),gh:s("dw"),ab:s("i6"),aT:s("dy"),U:s("aK

"),eJ:s("eR

"),R:s("ac"),dx:s("ac"),b0:s("ac"),bi:s("a7"),co:s("a7"),fu:s("a7"),h:s("a7<~>"),V:s("cK"),fF:s("f3"),et:s("n"),a9:s("n"),k:s("n"),eI:s("n<@>"),gR:s("n"),fX:s("n"),D:s("n<~>"),hg:s("dG"),cT:s("dK"),aR:s("iC"),eg:s("iF"),dn:s("fp<~>"),eC:s("a1"),fa:s("a1"),F:s("a1<~>"),y:s("I"),i:s("F"),z:s("@"),bI:s("@(e)"),_:s("@(e,a_)"),S:s("a"),eH:s("C?"),A:s("z?"),dE:s("c0?"),X:s("e?"),ah:s("az?"),O:s("bH?"),dk:s("p?"),fN:s("bg?"),aD:s("aY?"),a6:s("I?"),cD:s("F?"),h6:s("a?"),cg:s("b0?"),o:s("b0"),H:s("~"),d5:s("~(e)"),da:s("~(e,a_)")}})();(function constants(){var s=hunkHelpers.makeConstList +B.as=J.hi.prototype +B.c=J.u.prototype +B.b=J.es.prototype +B.at=J.d8.prototype +B.a=J.bX.prototype +B.au=J.bz.prototype +B.av=J.eu.prototype +B.aF=A.cz.prototype +B.e=A.c0.prototype +B.T=J.hG.prototype +B.A=J.cF.prototype +B.ab=new A.cn(0) +B.k=new A.cn(1) +B.n=new A.cn(2) +B.D=new A.cn(3) +B.bv=new A.cn(-1) +B.ac=new A.fN(127) +B.u=new A.er(A.xx(),A.aB("er")) +B.ad=new A.fM() +B.bw=new A.fR() +B.ae=new A.fQ() +B.E=new A.ec() +B.af=new A.fX() +B.bx=new A.h1() +B.F=new A.h4() +B.G=new A.h7() +B.h=new A.b2() +B.ag=new A.hh() +B.H=function getTagFallback(o) { + var s = Object.prototype.toString.call(o); + return s.substring(8, s.length - 1); +} +B.ah=function() { + var toStringFunction = Object.prototype.toString; + function getTag(o) { + var s = toStringFunction.call(o); + return s.substring(8, s.length - 1); + } + function getUnknownTag(object, tag) { + if (/^HTML[A-Z].*Element$/.test(tag)) { + var name = toStringFunction.call(object); + if (name == "[object Object]") return null; + return "HTMLElement"; + } + } + function getUnknownTagGenericBrowser(object, tag) { + if (object instanceof HTMLElement) return "HTMLElement"; + return getUnknownTag(object, tag); + } + function prototypeForTag(tag) { + if (typeof window == "undefined") return null; + if (typeof window[tag] == "undefined") return null; + var constructor = window[tag]; + if (typeof constructor != "function") return null; + return constructor.prototype; + } + function discriminator(tag) { return null; } + var isBrowser = typeof HTMLElement == "function"; + return { + getTag: getTag, + getUnknownTag: isBrowser ? getUnknownTagGenericBrowser : getUnknownTag, + prototypeForTag: prototypeForTag, + discriminator: discriminator }; +} +B.am=function(getTagFallback) { + return function(hooks) { + if (typeof navigator != "object") return hooks; + var userAgent = navigator.userAgent; + if (typeof userAgent != "string") return hooks; + if (userAgent.indexOf("DumpRenderTree") >= 0) return hooks; + if (userAgent.indexOf("Chrome") >= 0) { + function confirm(p) { + return typeof window == "object" && window[p] && window[p].name == p; + } + if (confirm("Window") && confirm("HTMLElement")) return hooks; + } + hooks.getTag = getTagFallback; + }; +} +B.ai=function(hooks) { + if (typeof dartExperimentalFixupGetTag != "function") return hooks; + hooks.getTag = dartExperimentalFixupGetTag(hooks.getTag); +} +B.al=function(hooks) { + if (typeof navigator != "object") return hooks; + var userAgent = navigator.userAgent; + if (typeof userAgent != "string") return hooks; + if (userAgent.indexOf("Firefox") == -1) return hooks; + var getTag = hooks.getTag; + var quickMap = { + "BeforeUnloadEvent": "Event", + "DataTransfer": "Clipboard", + "GeoGeolocation": "Geolocation", + "Location": "!Location", + "WorkerMessageEvent": "MessageEvent", + "XMLDocument": "!Document"}; + function getTagFirefox(o) { + var tag = getTag(o); + return quickMap[tag] || tag; + } + hooks.getTag = getTagFirefox; +} +B.ak=function(hooks) { + if (typeof navigator != "object") return hooks; + var userAgent = navigator.userAgent; + if (typeof userAgent != "string") return hooks; + if (userAgent.indexOf("Trident/") == -1) return hooks; + var getTag = hooks.getTag; + var quickMap = { + "BeforeUnloadEvent": "Event", + "DataTransfer": "Clipboard", + "HTMLDDElement": "HTMLElement", + "HTMLDTElement": "HTMLElement", + "HTMLPhraseElement": "HTMLElement", + "Position": "Geoposition" + }; + function getTagIE(o) { + var tag = getTag(o); + var newTag = quickMap[tag]; + if (newTag) return newTag; + if (tag == "Object") { + if (window.DataView && (o instanceof window.DataView)) return "DataView"; + } + return tag; + } + function prototypeForTagIE(tag) { + var constructor = window[tag]; + if (constructor == null) return null; + return constructor.prototype; + } + hooks.getTag = getTagIE; + hooks.prototypeForTag = prototypeForTagIE; +} +B.aj=function(hooks) { + var getTag = hooks.getTag; + var prototypeForTag = hooks.prototypeForTag; + function getTagFixed(o) { + var tag = getTag(o); + if (tag == "Document") { + if (!!o.xmlVersion) return "!Document"; + return "!HTMLDocument"; + } + return tag; + } + function prototypeForTagFixed(tag) { + if (tag == "Document") return null; + return prototypeForTag(tag); + } + hooks.getTag = getTagFixed; + hooks.prototypeForTag = prototypeForTagFixed; +} +B.I=function(hooks) { return hooks; } + +B.m=new A.hs() +B.an=new A.kB() +B.ao=new A.hA() +B.ap=new A.hE() +B.f=new A.kN() +B.j=new A.i0() +B.i=new A.i1() +B.v=new A.mu() +B.d=new A.iI() +B.J=new A.bx(0) +B.K=new A.d6("/database",0,"database") +B.L=new A.d6("/database-journal",1,"journal") +B.aq=new A.aF("Unknown tag",null,null) +B.ar=new A.aF("Cannot read message",null,null) +B.aw=s([11],t.t) +B.C=new A.bO(0,"opfs") +B.W=new A.cc(0,"opfsShared") +B.X=new A.cc(1,"opfsLocks") +B.Y=new A.bO(1,"indexedDb") +B.r=new A.cc(2,"sharedIndexedDb") +B.B=new A.cc(3,"unsafeIndexedDb") +B.bg=new A.cc(4,"inMemory") +B.ax=s([B.W,B.X,B.r,B.B,B.bg],A.aB("u")) +B.b6=new A.dv(0,"insert") +B.b7=new A.dv(1,"update") +B.b8=new A.dv(2,"delete") +B.M=s([B.b6,B.b7,B.b8],A.aB("u")) +B.ay=s([B.C,B.Y],A.aB("u")) +B.w=s([],t.W) +B.az=s([],t.gz) +B.aA=s([],t.f) +B.x=s([],t.s) +B.o=s([],t.c) +B.y=s([],t.L) +B.aC=s([B.K,B.L],A.aB("u")) +B.Z=new A.ac(A.pi(),A.ba(),0,"xAccess",t.b0) +B.a_=new A.ac(A.pi(),A.bS(),1,"xDelete",A.aB("ac")) +B.aa=new A.ac(A.pi(),A.ba(),2,"xOpen",t.b0) +B.a8=new A.ac(A.ba(),A.ba(),3,"xRead",t.dx) +B.a3=new A.ac(A.ba(),A.bS(),4,"xWrite",t.R) +B.a4=new A.ac(A.ba(),A.bS(),5,"xSleep",t.R) +B.a5=new A.ac(A.ba(),A.bS(),6,"xClose",t.R) +B.a9=new A.ac(A.ba(),A.ba(),7,"xFileSize",t.dx) +B.a6=new A.ac(A.ba(),A.bS(),8,"xSync",t.R) +B.a7=new A.ac(A.ba(),A.bS(),9,"xTruncate",t.R) +B.a1=new A.ac(A.ba(),A.bS(),10,"xLock",t.R) +B.a2=new A.ac(A.ba(),A.bS(),11,"xUnlock",t.R) +B.a0=new A.ac(A.bS(),A.bS(),12,"stopServer",A.aB("ac")) +B.aD=s([B.Z,B.a_,B.aa,B.a8,B.a3,B.a4,B.a5,B.a9,B.a6,B.a7,B.a1,B.a2,B.a0],A.aB("u>")) +B.l=new A.c7(0,"sqlite") +B.aN=new A.c7(1,"mysql") +B.aO=new A.c7(2,"postgres") +B.aP=new A.c7(3,"duckdb") +B.aQ=new A.c7(4,"mariadb") +B.N=s([B.l,B.aN,B.aO,B.aP,B.aQ],A.aB("u")) +B.aR=new A.cC(0,"custom") +B.aS=new A.cC(1,"deleteOrUpdate") +B.aT=new A.cC(2,"insert") +B.aU=new A.cC(3,"select") +B.O=s([B.aR,B.aS,B.aT,B.aU],A.aB("u")) +B.Q=new A.c1(0,"beginTransaction") +B.aG=new A.c1(1,"commit") +B.aH=new A.c1(2,"rollback") +B.R=new A.c1(3,"startExclusive") +B.S=new A.c1(4,"endExclusive") +B.P=s([B.Q,B.aG,B.aH,B.R,B.S],A.aB("u")) +B.aI={} +B.aE=new A.eg(B.aI,[],A.aB("eg")) +B.z=new A.dh(0,"terminateAll") +B.by=new A.kC(2,"readWriteCreate") +B.p=new A.cA(0,0,"legacy") +B.aJ=new A.cA(1,1,"v1") +B.aK=new A.cA(2,2,"v2") +B.aL=new A.cA(3,3,"v3") +B.q=new A.cA(4,4,"v4") +B.aB=s([],t.d) +B.aM=new A.bI(B.aB) +B.U=new A.hQ("drift.runtime.cancellation") +B.aV=A.bl("co") +B.aW=A.bl("oc") +B.aX=A.bl("k3") +B.aY=A.bl("k4") +B.aZ=A.bl("km") +B.b_=A.bl("kn") +B.b0=A.bl("ko") +B.b1=A.bl("e") +B.b2=A.bl("lv") +B.b3=A.bl("lw") +B.b4=A.bl("lx") +B.b5=A.bl("aY") +B.b9=new A.aJ(10) +B.ba=new A.aJ(12) +B.bb=new A.aJ(14) +B.bc=new A.aJ(2570) +B.bd=new A.aJ(3850) +B.be=new A.aJ(522) +B.V=new A.aJ(778) +B.bf=new A.aJ(8) +B.t=new A.dQ("") +B.bh=new A.av(B.d,A.wV()) +B.bi=new A.av(B.d,A.wR()) +B.bj=new A.av(B.d,A.wZ()) +B.bk=new A.av(B.d,A.wS()) +B.bl=new A.av(B.d,A.wT()) +B.bm=new A.av(B.d,A.wU()) +B.bn=new A.av(B.d,A.wW()) +B.bo=new A.av(B.d,A.wY()) +B.bp=new A.av(B.d,A.x_()) +B.bq=new A.av(B.d,A.x0()) +B.br=new A.av(B.d,A.x1()) +B.bs=new A.av(B.d,A.x2()) +B.bt=new A.av(B.d,A.wX()) +B.bu=new A.fz(null,null,null,null,null,null,null,null,null,null,null,null,null)})();(function staticFields(){$.mZ=null +$.cU=A.f([],t.f) +$.rk=null +$.pZ=null +$.py=null +$.px=null +$.rD=null +$.rv=null +$.rL=null +$.nO=null +$.nV=null +$.p8=null +$.n2=A.f([],A.aB("u?>")) +$.dZ=null +$.fD=null +$.fE=null +$.oZ=!1 +$.m=B.d +$.n4=null +$.qy=null +$.qz=null +$.qA=null +$.qB=null +$.oF=A.mm("_lastQuoRemDigits") +$.oG=A.mm("_lastQuoRemUsed") +$.eT=A.mm("_lastRemUsed") +$.oH=A.mm("_lastRem_nsh") +$.qr="" +$.qs=null +$.r8=null +$.nz=null})();(function lazyInitializers(){var s=hunkHelpers.lazyFinal,r=hunkHelpers.lazy +s($,"xW","rR",()=>A.rC("_$dart_dartClosure")) +s($,"xV","cZ",()=>A.rC("_$dart_dartClosure_dartJSInterop")) +s($,"z_","tA",()=>B.d.bd(new A.nY(),A.aB("C<~>"))) +s($,"yM","tr",()=>A.f([new J.hj()],A.aB("u"))) +s($,"ya","rX",()=>A.bM(A.lu({ +toString:function(){return"$receiver$"}}))) +s($,"yb","rY",()=>A.bM(A.lu({$method$:null, +toString:function(){return"$receiver$"}}))) +s($,"yc","rZ",()=>A.bM(A.lu(null))) +s($,"yd","t_",()=>A.bM(function(){var $argumentsExpr$="$arguments$" +try{null.$method$($argumentsExpr$)}catch(q){return q.message}}())) +s($,"yg","t2",()=>A.bM(A.lu(void 0))) +s($,"yh","t3",()=>A.bM(function(){var $argumentsExpr$="$arguments$" +try{(void 0).$method$($argumentsExpr$)}catch(q){return q.message}}())) +s($,"yf","t1",()=>A.bM(A.qn(null))) +s($,"ye","t0",()=>A.bM(function(){try{null.$method$}catch(q){return q.message}}())) +s($,"yj","t5",()=>A.bM(A.qn(void 0))) +s($,"yi","t4",()=>A.bM(function(){try{(void 0).$method$}catch(q){return q.message}}())) +s($,"ym","pm",()=>A.uW()) +s($,"y1","cm",()=>$.tA()) +s($,"y0","rU",()=>A.v7(!1,B.d,t.y)) +s($,"yw","tc",()=>{var q=t.z +return A.pN(q,q)}) +s($,"yA","tg",()=>A.pW(4096)) +s($,"yy","te",()=>new A.nr().$0()) +s($,"yz","tf",()=>new A.nq().$0()) +s($,"yn","t7",()=>A.uq(A.fC(A.f([-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-1,-2,-2,-2,-2,-2,62,-2,62,-2,63,52,53,54,55,56,57,58,59,60,61,-2,-2,-2,-1,-2,-2,-2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-2,-2,-2,-2,63,-2,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-2,-2,-2,-2,-2],t.t)))) +s($,"yu","bb",()=>A.eS(0)) +s($,"ys","d_",()=>A.eS(1)) +s($,"yt","ta",()=>A.eS(2)) +s($,"yq","po",()=>$.d_().aj(0)) +s($,"yo","pn",()=>A.eS(1e4)) +r($,"yr","t9",()=>A.G("^\\s*([+-]?)((0x[a-f0-9]+)|(\\d+)|([a-z0-9]+))\\s*$",!1,!1,!1,!1)) +s($,"yp","t8",()=>A.pW(8)) +s($,"yv","tb",()=>typeof FinalizationRegistry=="function"?FinalizationRegistry:null) +s($,"yx","td",()=>A.G("^[\\-\\.0-9A-Z_a-z~]*$",!0,!1,!1,!1)) +s($,"yJ","o7",()=>A.pb(B.b1)) +s($,"y3","rV",()=>{var q=new A.mY(new DataView(new ArrayBuffer(A.vW(8)))) +q.i1() +return q}) +s($,"yl","pl",()=>A.u_(B.ay,A.aB("bO"))) +s($,"z1","tB",()=>A.pB($.fK())) +s($,"yV","pp",()=>new A.fY($.pk(),null)) +s($,"y7","rW",()=>new A.kE(A.G("/",!0,!1,!1,!1),A.G("[^/]$",!0,!1,!1,!1),A.G("^/",!0,!1,!1,!1))) +s($,"y9","fK",()=>new A.m3(A.G("[/\\\\]",!0,!1,!1,!1),A.G("[^/\\\\]$",!0,!1,!1,!1),A.G("^(\\\\\\\\[^\\\\]+\\\\[^\\\\/]+|[a-zA-Z]:[/\\\\])",!0,!1,!1,!1),A.G("^[/\\\\](?![/\\\\])",!0,!1,!1,!1))) +s($,"y8","fJ",()=>new A.lz(A.G("/",!0,!1,!1,!1),A.G("(^[a-zA-Z][-+.a-zA-Z\\d]*://|[^/])$",!0,!1,!1,!1),A.G("[a-zA-Z][-+.a-zA-Z\\d]*://[^/]*",!0,!1,!1,!1),A.G("^/",!0,!1,!1,!1))) +s($,"y6","pk",()=>A.uG()) +s($,"xU","rQ",()=>$.d_().aE(0,63).aj(0)) +s($,"xT","rP",()=>{var q=$.d_() +return q.aE(0,63).cw(0,q)}) +s($,"xS","fI",()=>$.rV()) +s($,"yk","t6",()=>new A.hb(new WeakMap())) +s($,"yN","ts",()=>A.ul(A.f([A.qf("files"),A.qf("blocks")],t.s))) +s($,"xX","o6",()=>{var q,p,o=A.ao(t.N,A.aB("d6")) +for(q=0;q<2;++q){p=B.aC[q] +o.t(0,p.c,p)}return o}) +s($,"yU","tz",()=>A.G("^#\\d+\\s+(\\S.*) \\((.+?)((?::\\d+){0,2})\\)$",!0,!1,!1,!1)) +s($,"yP","tu",()=>A.G("^\\s*at (?:(\\S.*?)(?: \\[as [^\\]]+\\])? \\((.*)\\)|(.*))$",!0,!1,!1,!1)) +s($,"yQ","tv",()=>A.G("^(.*?):(\\d+)(?::(\\d+))?$|native$",!0,!1,!1,!1)) +s($,"yT","ty",()=>A.G("^\\s*at (?:(?.+) )?(?:\\(?(?:(?\\S+):wasm-function\\[(?\\d+)\\]\\:0x(?[0-9a-fA-F]+))\\)?)$",!0,!1,!1,!1)) +s($,"yO","tt",()=>A.G("^eval at (?:\\S.*?) \\((.*)\\)(?:, .*?:\\d+:\\d+)?$",!0,!1,!1,!1)) +s($,"yC","ti",()=>A.G("(\\S+)@(\\S+) line (\\d+) >.* (Function|eval):\\d+:\\d+",!0,!1,!1,!1)) +s($,"yE","tk",()=>A.G("^(?:([^@(/]*)(?:\\(.*\\))?((?:/[^/]*)*)(?:\\(.*\\))?@)?(.*?):(\\d*)(?::(\\d*))?$",!0,!1,!1,!1)) +s($,"yG","tm",()=>A.G("^(?.*?)@(?:(?\\S+).*?:wasm-function\\[(?\\d+)\\]:0x(?[0-9a-fA-F]+))$",!0,!1,!1,!1)) +s($,"yL","tq",()=>A.G("^.*?wasm-function\\[(?.*)\\]@\\[wasm code\\]$",!0,!1,!1,!1)) +s($,"yH","tn",()=>A.G("^(\\S+)(?: (\\d+)(?::(\\d+))?)?\\s+([^\\d].*)$",!0,!1,!1,!1)) +s($,"yB","th",()=>A.G("<(|[^>]+)_async_body>",!0,!1,!1,!1)) +s($,"yK","tp",()=>A.G("^\\.",!0,!1,!1,!1)) +s($,"xY","rS",()=>A.G("^[a-zA-Z][-+.a-zA-Z\\d]*://",!0,!1,!1,!1)) +s($,"xZ","rT",()=>A.G("^([a-zA-Z]:[\\\\/]|\\\\\\\\)",!0,!1,!1,!1)) +s($,"yR","tw",()=>A.G("\\n ?at ",!0,!1,!1,!1)) +s($,"yS","tx",()=>A.G(" ?at ",!0,!1,!1,!1)) +s($,"yD","tj",()=>A.G("@\\S+ line \\d+ >.* (Function|eval):\\d+:\\d+",!0,!1,!1,!1)) +s($,"yF","tl",()=>A.G("^(([.0-9A-Za-z_$/<]|\\(.*\\))*@)?[^\\s]*:\\d*$",!0,!1,!0,!1)) +s($,"yI","to",()=>A.G("^[^\\s<][^\\s]*( \\d+(:\\d+)?)?[ \\t]+[^\\s]+$",!0,!1,!0,!1)) +s($,"z0","pq",()=>A.G("^\\n?$",!0,!1,!0,!1))})();(function nativeSupport(){!function(){var s=function(a){var m={} +m[a]=1 +return Object.keys(hunkHelpers.convertToFastObject(m))[0]} +v.getIsolateTag=function(a){return s("___dart_"+a+v.isolateTag)} +var r="___dart_isolate_tags_" +var q=Object[r]||(Object[r]=Object.create(null)) +var p="_ZxYxX" +for(var o=0;;o++){var n=s(p+"_"+o+"_") +if(!(n in q)){q[n]=1 +v.isolateTag=n +break}}v.dispatchPropertyName=v.getIsolateTag("dispatch_record")}() +hunkHelpers.setOrUpdateInterceptorsByTag({SharedArrayBuffer:A.de,ArrayBuffer:A.dd,ArrayBufferView:A.ey,DataView:A.cz,Float32Array:A.hu,Float64Array:A.hv,Int16Array:A.hw,Int32Array:A.df,Int8Array:A.hx,Uint16Array:A.hy,Uint32Array:A.hz,Uint8ClampedArray:A.ez,CanvasPixelArray:A.ez,Uint8Array:A.c0}) +hunkHelpers.setOrUpdateLeafTags({SharedArrayBuffer:true,ArrayBuffer:true,ArrayBufferView:false,DataView:true,Float32Array:true,Float64Array:true,Int16Array:true,Int32Array:true,Int8Array:true,Uint16Array:true,Uint32Array:true,Uint8ClampedArray:true,CanvasPixelArray:true,Uint8Array:false}) +A.dg.$nativeSuperclassTag="ArrayBufferView" +A.fd.$nativeSuperclassTag="ArrayBufferView" +A.fe.$nativeSuperclassTag="ArrayBufferView" +A.c_.$nativeSuperclassTag="ArrayBufferView" +A.ff.$nativeSuperclassTag="ArrayBufferView" +A.fg.$nativeSuperclassTag="ArrayBufferView" +A.aX.$nativeSuperclassTag="ArrayBufferView"})() +Function.prototype.$0=function(){return this()} +Function.prototype.$1=function(a){return this(a)} +Function.prototype.$2=function(a,b){return this(a,b)} +Function.prototype.$1$1=function(a){return this(a)} +Function.prototype.$3=function(a,b,c){return this(a,b,c)} +Function.prototype.$4=function(a,b,c,d){return this(a,b,c,d)} +Function.prototype.$3$1=function(a){return this(a)} +Function.prototype.$2$1=function(a){return this(a)} +Function.prototype.$3$3=function(a,b,c){return this(a,b,c)} +Function.prototype.$2$2=function(a,b){return this(a,b)} +Function.prototype.$2$3=function(a,b,c){return this(a,b,c)} +Function.prototype.$1$2=function(a,b){return this(a,b)} +Function.prototype.$5=function(a,b,c,d,e){return this(a,b,c,d,e)} +Function.prototype.$6=function(a,b,c,d,e,f){return this(a,b,c,d,e,f)} +Function.prototype.$1$0=function(){return this()} +convertAllToFastObject(w) +convertToFastObject($);(function(a){if(typeof document==="undefined"){a(null) +return}if(typeof document.currentScript!="undefined"){a(document.currentScript) +return}var s=document.scripts +function onLoad(b){for(var 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�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

qm>PC!Oj!VG1!=v}}P z!vsa$D5x>LUN@^Sf+1zBDF(k1PM6fkh zh}I+txNB9lf-bl~lMoI*Mq`>2^`}@U$z9Q()VL_(IgvC5>`8AZTBD=TlI5rn&KRtS zVc{6TlX5y6;3csj5`4v3`OUH67`MmZEYxQJA0A~l+_-~~BE=iR?55vW8u)fgAOns) zv*?iZD|i7|LEYa}M!Xl!vpHA;I0t%ZHxL<21WPA*fO>0&IIG{w_`|6RLK}=^o|FjT zAxtUFG%cznDlkTsdW}3%u2CSJg%L?G(Pl9i0BD2z!14K?QFKU1S1pf*5@2#8gN)s#zsk;+@Tu$d(SDlSUrj%AIv5?Hz2j^j0WGOzzl z7Yi?qI>a7;|B&Bj{s1nK9*-96$OxcfVjE;HKTb^%X3V%g?EDN3fcfqf_9~shk+vRSIKi{7tIFFuN1Tk3WwXxS42*P}^LLOJ-hfO#SLX2Vm$WvG9*J!B4 zSjUz$dMUlq)bFxwd!eZ{AYS5?JDTX>izcD_Zg+p~IyFQU&yX$uF36?GEd9zP;k1{9 z^b2$-=3HGM5#|@3;y`HdQOtHK9BdNWp}0od=q6kf2nO4x{T=AnM>MEkmIARX2df9*`iv6qTC%sd9XGG%=oiq zu6$)GsnMe3m{YJW2FnhbY{6yY@?mydn7hL=;C+0n8crn^)3!pqPj0UNfPH9j!jr#1 z)j!_D$eYKCi+6fvaF4=nrJoIMj^eZb0id5iol-o)st!{2I*oK7-3ccE-}P49Pgf1fcZchPYW}^ z!3VC1!WWc(*Wv%i#gM`g0=_O(DXHo$d+OF+svZCqY)2G-g*xVU5Cg#j)cf+a*dq3k zmnd%mnCtlzY-?&wJ9p)A-uqm6GX`kF8Sw-*MOmS9$TM?1v3&swXosJ_BW+7>>a&1S z8jlaecwT-E5bLTV%2Z#m=2ZSpoElCa@x{(ito2Uh^^4=Uw^zg=QC=Mum}HoOqAMo` z%=0W@bXnqJ#l=hhRMnY8{a3WRL(FyPKKwRfb0FYC`><(ONKCmAw$e^X^tVkF{S?)K zt5DL9@opbZ9P|{7rT=1fsLSYZz^?x0i&U=2VPaatw`m^?QSpoD0F9o;wNUA z-NE%|IccL{1yxv1X!O?c_%=H+SXC3_r#oP-$BU+T>x86}q;9HJ#Lp;+9lgy?Jdr`A z6D?*-2R2)BrGDE>T#<>nda|4ih+0c=0_~4g55rmC4R;-X>!j%~zKeB`*OJXZ5CKo+`Hkv=mU`W^>>i1?xag zk}zHv$hXQ?5Jc^mk1&qsBlLcxJTfPn=$e~+R#p52_D z-Bh<2=J#**S$NaJv$$85?SV+ZJOWB|rypfRxr?hLigoH=J(VjDC4Z38{;Y9uL7>Uv zfd4MMa3$Gs$?8LS{AQK=991MruB`BXj8>7$U^;OXIjaa`KNIz1cMvNKRG1JI_Vg7J z1{FgjG^@Uv)`qLEW#Qvy2or3+Rh*>~m{5(28$LBy?W?xn7B zVS*U;AprPVJaZOd#QW<7&avP_Z{xPzu+Q=CcUI=h^&H)kMU7M%bxVzL1^#(3WCnNm z$$N_O6!j>94Tr)JajNHH#+I^BP1*Cacd%Tfj6^fYbmCmRy5sfuxmBAxj@`jc6x-08 z?#DW_u z&Rt6^Vn22|i%Vdy7Xn~U+If#&6uuuglY8(%9bT)(Pg{GCBhN90xw$n%Z`vM()fLat6@KCHcghq@B!X2Q|d)5rFL&Mb(Bb0Sc{ovoLuk7uiNrGn-5(DFT#}NCBm*WgjHE?WSb}OuelB z@lRUykNzbpIV>|Tt}7atKCm$(L@*XJ?;`9oHjq(zh#-7;`GK868=d;z^zX=MoEGedrS+l9Sb zR1Ked(^EWfT3qbM_fxp-H|#RvjbFiDZ1j>Q2p`Nb3Ju>2xq<@<2}t$Q!Q26X;SD{>S@)yjc#tLjO)<$K^GGsWxFd9%}F z9Emn!O&m)7iLd14>xfJBc_)S%Bdp)}uU=N^2gezbRV`&h?1-%|?3|o&q+tw2jEUiT z%qWWTPcTL7N7pN>5M|)nSPpU8Xaw4+mC5g~lc3|?N`#&&G!=c1XCCbOI(PflY=+$) z7wYNiTJ*U+dpBQ83qLc{@}*-cwSrzC0JaQI3uD*tXafWtQLaTia2N4$y%eClcvzo* zVsUjW*47Ltny$4neMmH`wq|TTta@8B#DhBDp6x@P5Mduuz5!)*_JTOca-3tWPR?IW zJHS`4!5i94W2#z_N}6yq#MTU3)s0mr7I(nAQij2J{jmDsj(Y=BzteHBIcU>Kt;>~^ z>_zr~R@z&fSSlUD&AZtzqkYp^F(p&KA{hEapE==^NDOm%pNwb@H8O}3#HpnC`!txhN z9NIQn6sfb5{B@##9_63Sd-qbE;#8^*rrW>f#aaK<{@FIV&5?B>2@^`YYIN8_PNDe4$K8`>}#oC%Zo}1m3nNzhFd&9H*hRNIAU(=xRuGZ6L6+Qf&;5lEy+nz?w6N^XORb(!`IrzQ;P0)bGBJk z?7j(!M3ZDs;8Yo9ENOAsA{=hghEL8?l9m^c$^f}I1EsgdkHvTjPTX{YVgYO;xoTr^ zlHg$r2hD+&7%+H7qD`FK+lQ^VEXra*n#({wSAmCRYc`cS!4tF# zVSBGLugRyO;1IndW7bk69SxD`*5nNZtm~h#b7X6_y8G}zAcxl(0=gi`a7o3GtGYNX zz;x3XWoyrDs-U!`wgatHbE>JvczsK^lX_IV_%1!M`HIXUynu41xW2{8aYP?R=k?6A z)!o5!pvfR>Z4=D=h;o~P3f+Q{#J5Np@>>ioEhO&%6<44=*ZtXeswt$c(}pBikFD8z z&>h8^XY=umTcGH(`SMR6=F8Lha`(T3{5$z_=ulpN=@7_O8e)8H%PrvC`in_KXtC_1 zaj!%>i!8U~h75-w+Q>S1UVcJ;R!EJrL<^Y_4t=^!bPcRpE;%}ei~(}G1|P6&179z_ z|F|gM5&XnGwMPnJ z^!jNTyhFC-Ry+X5aq2sz=$6bi_K3;q1PGUc8#i3HY20@i2>G{Lvoje;atp+1N=%n) zHb14iF!lRSuU^`(3P3wH3;@t5WqaPm=u;Zp!Y}^6$#e+TT}NBW#5G7G_Ougf@Fo*F zNKJ7)ra%=2suBjTD-xBB1pKI5s zygI=(rCc;kWIVfu*0{1{Xg!1iTVDcXFJtvr-1aB3zg8Qfm^;g-8`q8khxsb=t6n%z?U5~S)f(2~lTL9LHdS-g7 zJYaeQgIq&hd)7$|dcs~rI>V-YZj0b!9Ei%bH>L++FQHc*6i5+P#3=uOOx-s-0p zWy$_x-sIk9JPLy?-#MAaR_}i)nkNR5#8qCDAE(yNU_d;~VrJ;u&2P{HIHFZJnvymX z{+6$tXm*A(>2JT#NykF)tV;n1HzWu;dI1Mclgz^R0O%Q-Jb)rE|G~PSix7=OwblYu zL(DBe)mP{x-W)AIECrTCNtg&z)$~+EMmcEK$|kFL!yhP#HI~OVk=uDutnz7+#rJ&Cn8Z9w9NW|MGq}%=!Tqbkh5(N z+X%+Lb07i$Z{}QIRz!bJ9A^5dN%T7xvKiD5(>Pt7O~Ec1O_{8ZmR3f;wGBu!ga8Cj zMX1ZQEI(~ig!*gpMHAv)Ls3buaM$F$g85DH#*obUEfCCQ@?CIgQ zD#D89VO}Jop&Blq)d;D}X4v->g@+Pe3{_>w(gEvlU{->wcqm-n4xG|~*T8FpZHGhRyS zyBx9D`tvpYuvU~Ufhx&c#{5CW47A8<@Lm+hy2ZCH*Vz!Lh^^@10FVzJ3BwWW)GKcW zlY$B&)e7UM`~n6cNDX>%KMYdU3AcXJ5(c1v9TP^F$9^DtTjlpgFR^0MZ=z3n4k`bS`?+Ph%CR9dC8^UQ)Gaky9>zbBMt4tFUC`TqQ>`Q3DiFfi5V7d|5 zBF}1MCIt@GN>aAZ3Q7^s$CgA7J9v{{rG?@VT}M= z2-em#)Ai64tSZ9)NUFGFj2SYs`%{~Aoe`T3gVl_Xdbw!BSO`|fm?9TA04*>H_K=)3 z?JS@jD#X)g#d<%P558Dv6&>Q8*!^Ta!XiK0?!Km2P*K`RMOjeV(2TV2LqVpqWPxf5 zB}9y9yj}xjCrC7G9VOw0CoTDrcI%%M&jzBA!Rag(y29{-)&+RM^sL_oY^c1KHfwB* zqHtkTFpv65?uOdvi6AI>iSU%m2#c`DQ6dADR#s_&yN8XQoT-x(A;<=-+Gg)W$`DEV zT*^zsjz50146!~g0!UtdOzL+P`2O)kb={X~umj+*@zi-^c0>I*3;JaT!Z^!?Q2lE% z8J3tdaa-Mk`)p+q7IiN`8tAdgvWfw+BK#}h^I0ghQfB$L&_*m3sHDA;JTl#@ilUV= zlE{)~V~}mLc?K(7c)$>-yMPXjB;t3|y2kt%jpiioX#R+g8~7O4WvVJyoKt`AnwCS5 z4Ylk4^*4H8BEex01BB{8z1l^gLwc#;f~-@w{?^5ljkJ*RLcs!*^~Vk}Y&@4LTUUHS zDH|C`feOWTCC|oWdBERh%(MZ!&5Ki3E@X_M&PW$OeDb zBhE0FoI7U}7SGDV3P7?kduE^>YHw5;4k{J(yeYj-2J%H1R3BtG`6LV+I!Wb95z_SV zNO=yx@W?;hf@Gm)x8Z1Gxa6=Qj_~g$4+ zV$Rup9Fmfis0t6liyT5h0*iP{+pG!t6ueb?cY-TSqS<2Py&^6Ot9S`Zw~etkPG1Cu z(y~P01JJw&(eAZ%!$8Z_Dq%<%lK}j4uyHNW{hB#rYd)jd&12t0&E9Ni`JS*8l4&^}rAwBFP~0hoatL2EBKskM#SfWI@v)rBe+ z-IYH`yTR&X>Px;3g9Uv+=VQuqBVi$07>0Yde6I^9@MwXLHF23GPX5tOeGlxygwkjs zo-mH+g5Ws*vk(-#c2#c2Jn2j!zH|_l2otFdB1!o^0!GlcIzXrUEQEyQmqeGa#-&$( zEt9vD@3RmNb?;yDsl~eY(WT6n**Mj0yc2BrYQRl`ZK#S~@8YcrO|bjZycmO%vx;2b zh3%HRbsJoW1iFbRHp87~gEM0k*ZI1kQXbHKHFgdhiv>Yx9y=$9gY~c4upcLmCh%+- zst=sTkr^Nox*a+O>tN^}3`ib4JZ~&i8o(*6JBy9)XXkK&6K%qy@+iWA`sqQU{<;}})!=!18qaAIAZ={FDw}5rVICVf6%Pf#3u-E`LH5O>FyNanYzyco1&>5fg z!(Vr7{y^F6d~0C`Q27D;7XbUJua7MpGLUwwWc4+m9i>@~iBgd?t1svGN(V@eP?PM% zj5cPkgn`r;rdexS!9Vzu8YG$8t&Ck6LASkJ(N7|YYJ6h?6fIItQFr&?Kltra_2K19nAYyvhBN@nZCtH2KZ zh30@0{qv2RVZ7YGId{(k2T=nhM;G_z)4h3-TvOteJmgR+mo_Uum|SSDq@w`1GmcqD zOLcEgsC*3XH-{d%+iY&R`WH-Lg)TYw#6q~(Vud3@pO?zoB%9LqO+th7>*Rng*L@WC27 zAs@lRpD9ZdzI;JB4VPwypvD8jl{YmEnfaVyZfteT80lMEhG%qw(XXIj5mnBDrkNujlW^ zTGN3-4O!k;0Qdt*zX82;J?rrjWC{Gt$}{DgNx>e$%w&+4$zv--=I76^h{FsBpb`E_#sy&Mj~f_$6L5GVWR-~FkRPb%*6H?fI3 z^7`UCLEJ(VZ-Q6x-QP?8jmGEZ>zw`AyO*tCbJ1jbyhB=D(VLoDHkPANVw^MS;RKc2 z6);782a4TKu0-G~Vm|L8vE{PmdVx4TR^^_-iipALQ8<7|=ndk;6y!X`m&on!QlsPg zw{sAM)>?TAiZR(@_18a|vi;fYw1-Qm9Q9~J4tW$_lB+s~*Z*a%T^R(rWC4JUC3}dB%zV+qazboC3O_uij@K*LKtrs+peC%%Lf|uSVU^!_rAoY-m%RRwD^PGUg zv_An&3;|^z^^wNF#*d}}I7IVY^s|5TTF-6ehAn zOP++j0)_8-U2FCNY?G|~0V&jz?AVVNn?`}K%`}Z3O22HBCtp=?@0ar;{hCJy{y|>f zgM5YQSTHQLyC&MjGkRk22QVl(GZOKsLk(J(s=Hb5Mn0ecn6tA@y&!QlvSg!N?Na~* zuCVfNQUHl&3bhVYe;Bf&MU8>uCUaV+h3U{9N9sn#i>e!;CeVB+G$DY?b3F@|`|9$D zV}wkjq^RccJbt*UO+dU}fpnlS^Do)o3izp-fn+^aY)k*5(InLnovhJDO`VKYy&Z0n zp^T|%8*BsX=oDkefDG-#y5rr%2D+VCH~D$y$)Ulaho!F>uZ*rL(!ou;1IO?^!Yr76 zTtE@$n|cz$Kdu}?YAM3KdMDq>d*Zu83rmcK!^x%n%+yU|;pt;ReKf&(I>xbD(X^VI z^mv1Es)<;&8>Y=*9+8)rR6anooHb9M<%cy~QHa{o*+{%qpfC1V-CzqiVBt>-(f{?Ll`#A~w-#IB7KVw#}0Ly6(w+uL7Rt)os z)lHbOPk7I_G)mg;cXSIe!;5lHiLAnT5S!s(Z?z%>9`OW5B^kGH9OB!#>Mh~hF~>z> zyefZ!NF*skxKFjqh0}f0B;g9&sM--CDDOnm6$&kLU|56lMhre|8s;-Usn0WAgVV+p zPX)T`vPf;{zN)~GLZQEnT!rMU3M*UtSvCT`ffs%{8b=3u=pytUGvzJ?VN|w#4hsSo zNh)CtI&!)=Hvqh#OAW+K*fl=U9i*nQk_;j-!c3RtzZ)&ePJ+<^hF@ZpeGT=seDCDw z5f8NF39e5E5Pg7C+fL3O%J5**>(GwOm!eBZlv5@8MsU?L#ov^sW2FiGT>1wTSpdN zyo})weHZc$ZVzH{@+OH6pP!)Or@yQ$$IN-Au67W0i|6$31Bwq`fKM=H8cjrpSVbw| zT>tAw0a1AinJo2%H>a-x8$n9HtvODu9XjLUO!Cg{ZWElL{%-S9-y}Ig;rNN`_TFxE z!vcjG-^{nJ_A(?|Y!5OojphRDsv;zaUoLr(;$940>#=KjBLY9jykQa$cORONk3fA60p&X8GEB+KKF-+7OPxUMReqgx#aO*9aWV6& z^0myDXRo|sa>49$?=Jh-ifxXV<`9=~U@SHw-b7e=9vL32Ht~f?eHQhFTHdVdm*Wt@$+;v;uVG+U<}b-F z%`eF>&M(R@%=hOPoPXZFKmMcVK4s8M^fPby$(@USxN7BZtG@pa{ng|K=dp92OoXwJAzuQJ&V9{~`8{jf*<;J}C>LXr| zOCY`I9D3Si|D(58&{NB9c-0RNfgbB|LfT;iYX-mA%)%T-uk(^y9%42KwRUlt0b=A^Oz;kGt*<7IZ@qx?CSCvw|6rR* zIs?w4a<1f^Vsrzl(FF3{V?_KC)L96k%R0W#8y@J{0sQ0cn!%}qd!$<;6X6SwkX0N)Z} zR_s2N|ET!G4b|Gsd=M?#6fT_if4~G0jBK}uM;0`B`k;|^Je%C`d5>$f--1aO<5vo; zC~uqXlsKPNTwl}4eau%5LFCN!Fr~>gxQWLVH4wF7E zx*3gZ)QdJjUOixI=2l<81k8H9_Z-=0L^jHgV%I2-(Aet>i#k0|CTz4ub%8-#L5Bx7 z@!ug#+9JYxLs{63wa9yHQ%I^p#cL8AMg#Uy=r_s7y_ASm67j2AhfOc?+J3@>^m|ATC{1tphdERWntc-l_NAZL^v(LLg9e2A#Y7QPICH zPjuKxoX13!tHPaZ97eomYduZ!AwgoMF{i4P)|tDbA`hNvgBgMjbK@J?rZ|s=$Mk!D zMH4aKXr5UtP9DobT8d-?4M;4koRbd`0jP&M3y2})eUWVehGBoT0HsO8-Ty-4BtGTk zPkW%xLo?sNJaR+-%4Ka#!dPTS zM*3a^G8QMjWXjDjdha|8DoY*!tT-Qf2f3e>H=&aCUg}quZXF0hBDne2xDyuEQ6~iY z*Hil(_{Zln5DBQ7v5`yrxJF%+N3vxQWPlwcfr9!2x6n^f1T92+z2RYt)ENeA9%8Fd zaCu3W!?stZ_QJ7al?d0p!{{`So2rkAItq|Q`Q5ly!4?RHgIS{p>|i{Z9VpSMRx zJKqB8ygk{x-8$@TRwKRL8olj1zK#E+g-57r^Jm`>=D)f4gQxW0#A;USuLV~4vwJ&+ z9UR`S75{uMeAuRWQL9hc0xdIfCjzJU7rySaw~qudo{B`C!=La>;w8_q?bz1V&;Be< z6haC;fV{l5*BZ3rF&rjt;7rXUB1XSQJPAPqze$5*kF8AL?SLpPi9~rA)f~1QiDq@p z52yB69{VkXVXIh~{d6QcG;fn8Lwd~y?p=4(zEe*E z{zybviUz*RnUVQmJxk5U%t+5nnw>e{)?dnT6s@mjE*`Ac9^%_2eB;%bokTmHRJC?v zI?c>xPa3u<5HdTD!BEYd$CYhQU0acWvUYep7oVmr3|}%>_nquiz@I&7&%yIjOQXw* zx%JiYm!+$GyL>R?le}Eb#KqYQXsbH@yvt$~@=H_;R%;2eq&2yA1eZodD_??S4kvN= z6Fuar_tr?#SR~G&7>Cu$%FrbF+2x<`(voW|LZ^RDy$Uk?OWK#A1b(J2U8bdtsU=Ob zkgRfEUOC>o=6?5A1o?+RU_Xy+B-XrBJ)pdt`0$JJ-Wx{mP}(#&zH5rP-XO%XxYl6Q zaCvG@ZV!2N{Fu>fY!fxTtkvHet+9SjO4nZGHL5%x8zbYPKjp%n-s|X6{bw*a3c@g| zslBk5U?9-9pLI^OHnvIvDR#Q6_|@6q*qTOknxp11bqdbVJXk|A2|wSI_kJ&N?7z+Q zY<@H|PnL)w_e|V<1ZP(#0~>yXMb2C!CKfmJTL!C%Wbk+i_}|2~f89$BKa1Fao1#Cb zcPN#jo~k#*X$!`pm>bqR%#O2}XuY0)_8uPoo3(rP4yg#fmzk12%5EC#l(vkSM!V>M z^3KSnESEN&H%+6v+B6a+ns(sPG$*Iiv~wUyRvIqLmlgmn5jKZmdKPcDW+epDu@OB? zAml1*=6h5LgZCw-r6CXjZ{_ax{$J$1^L*{is6oK_`37@RB`2*P+j}#C z(pHL&K&Xoj-T*+3S1AJ^_*7{s?1%`@Cc_7-usT*5(ij*E7LgWJT#NHP(!+1$+9ENN z1$>Lep%x(*SJLm0yDUtrZedg0^X69lzuw%6CH6G3xkq9ouji;gI$p}AlKyBnKbMCs z5Pz)IlHtViGNM@3-!-bzd;MstIY(o4UfUj}Y?!9I-*Wm3VU~KQdChwiI zbk*X{cHq>1b>92krFR;D^hqz;s~6fHBo}@8e5dNE138zHis4A^>M?L`f_v~>@cxx~&k8w9 zR(o;v78@8%U<8R+b(X95=Z9TMI)ZVDcs1bFtm7+k_(htfvmZ?q6$9&;mBF{qm&G1! z{I`yHA=w0-L9d07fdjZhL+-E**|Ci|@HGl|u50p8^cE6)!|QC019wT((<0+LJr#jx z1P9ox$A)aK@o{E~7EJXnV-mbE4T{o4nD_qfq}qWB5tb$GY%YR%3}95{$0iZ1Z_eZG zLVdqi_03;0AQ(yA`ru4vCZLz9E?anLt5^*I3jqddR%WRclJO_H>E2TUD1_zIZS)7vKp*e(*1V|(e z)NY%_ney2o4J^r!>bwOFC`Xdz1Wkr{0>$|5htjVP<-KPvMLJ#rmDriXxcCg~v^*3k zkv$??2QESf4V8QSZ0qtU(xpw%)bJO8w!M%@W!rIOKJ_-A_pTZ(SK#=6u{&YnT*DB7 zP05MElne9^+6vup#2%xMAsUk$k4pL+nmigHb<5hX@<-qA^jxY!=y_I$fQgM(Lm;(*r8cfQ|CQN3AQ%#<}me^4j z`~f;Z(Ac!PP3SE_%KW-IPvQyP%#;w8>7XL;ivP|2>HSd!)jzV{pYX=G@s)X^DS8E7 znnZ?d4Uff!=vr2@K`ywNBYOHTi6UXIhl;7I|SJzbXEPfW>0tC6(v$?@+qoGOBXS;iaIn;hvINw5tNppxd?nv>E(C^ ze^P5E2ohH>)yDYc;)uWnJ%`D+(o_dtDxn$Jr1wKYgriktT1Fmk27@ikeZVuN6Sqgs zFW>bbr)GP9$_4VV-Iuq1mgN8H5_dh=E+7llV^Pi}jRld)I9sOG9}`lT`M~#&VdrJv z)>vp>mG=O#GgoBJH#HsgMN?<9cnY4G_E2ZgBtT$PktX@mFf&jiI0>8v&JMsV$WlPs zzbv`qhJkQ z@j|9#+Y6l)oIPIR!*KQ4zj%@#cmdP5kU!V4cSKlrPAZ5FkINh8xs*`gL<%JoEH_;= ztWKSfktS@SB2coixOO=4r(w{@buF@V1Tg^EK+&1SDU;#yAp}ce05Ed05Xk0UWB^ws z1`v)2T;Rw> z9pu!3ra&KD;0k$>C=~2>$t2}XN8xuc^e`FO^6pA~hoze4^B#Iz$>)WewI^vaJni*Y zt_0{=?_%&~+*O4WEfIQcS6lMZ;l5c;^g=-CQsNE3C)o?_4Dopcj|J{P(Kkn|BoWxz z2`DyJ;>l)d=L_ip8V%?$-OEGfL`qnsZuKq~r76eKdr|O{BO#?EfhZNT3S3FjIqk_vmn!$TpP!IQGQ}Fvi8XX zb2FP@YbE;kjt#G4<&TRg!C7XCTa*662e@Io&{|aVC2ja{sm1YAEyoFDH)ao) zdj*U)6ItkIE!1yt6z-yE3zBlGZiwOZmd62rs6 z^D%I0g9Ozw;PedbM$(rbn_*gd4{cD}abYrq_HG43w)Zd1lXMfJU}RR$xSxt0JCwhH z$t^*$@?}NLJ8^(m&JYa`q1F2%RwNX$bv2f){48~Fgvp4xBHR7R$XBUqv%MlVXPHTT z*GF96{qU5C!_^Qc@*w&spvqO{o5nE}(&DOVUmY7XCdyB0L1fM_E|#~FjUuQ5SIRf# zz26RcDb)~S+$`Mpie%UD8XJ@de#C0f8w?jgV7*9-PG*&xsNw>t>r8n=6^Dw^6UzRM*fpxK29^9Ea z2z@5@ts)TVwrU3%*fs-ao`G^5NJ2{xnSh-v_Mmc1>oh$-P2vd5#juW@%@+kH;Y@V_ zkhH4Mt^mHILXZdFur&b!BYnn-149&o1P51KEDxtr3TwXt|BNNABPM$3sNleCUr8=LHYoQ3_n(}&_a7*<6eE-AgWFr_TJOD7Tx* zi^a|4#lmKC?xfA+NP9DE`#Y`odVb^C46v}rC4q@%jN#KcyDXTZWv*90gwwIeewv!j z7$SK26!BB?|NiYwiJU|#VW@@;?# z?9aaRv-^Iq=i=E5FQJoVq2I>x;@qduJ}3{5OMVUsg2H!E1cOhiIg|A-O`yG)STT6G zAG-z{@J>zDH-N71W79%4b$siFoScjLVO-r0BK;%yJr%k3?`+{8p*M;d^B@H-pUnpn z?QDj(s{Y>lea^J~Yp0N-FcPLECV&7AX}Hc2fdzat^fv8+8@e_J^;qlpRPJI6UR3gP zWlU-{lk%|nQM^Gm>c6VQIOBLV=j6<{QU7}?%%$87E!prvtN@qld6?pZlGMhR#02L} znB!~nvSv{u;S$*EjLVA`9$1VBYwotz-^Mh-O@@-SdDU%Yjn&alz^7b`KF0X`F zENV1*_=eE5?mI;nkWWhUXPc|sbmYh!7f($GHGif<8-1p_FmP`J&S#Hq6CVJzpW4Ug zS50il{u_iDC+s#Iv-k~0D5z9l;6n_uMhC?ZBZY0S-cJ+Z%M6ePH4R;Uig;}`vQqM4LUQY zHJlw0h{2qr>FM-B6)$}5i@rdvPU?Z_==ki_k<^RTTwg|4i?D z6kLh14iP5hHB=%i524T56A%7W-5Yx0>m}^b`52eoC>$4ls%MnP-xvG*5JJ{sn9}Fr z#;?47Avr(R1aWIS=(gmY=#Tffs1U{Lsly-PiT;R=XvGWASrv%;l1^9Owu2zT!P$Bd z_vAE5o}=4AoBvh@MCdnB5|7@gJcghGAfM@wU&~JJXd()fANFpK;;wYC9E#(KLyekR zK-5$9t#BHsnn#!%eAz{CQEqF7r`fB6d`XvJp6e3?zhk&ki3ZwQFtryM)_yCy+vQc1 zk%}-RUx~H)7ywvV;VLt%p`3ixYZoM4r(5Iq2QE$+6kC|&CIm6=lKUnG%UkmB<)S4A zKF1D|;&dPuO15~X7-c34uTDP?y{0Noh3g;V8i}^d8{T)P88Ms{AW(wa#|qvnSV6Iu zk0lhlmlH{F>ql2(Y)huKKv=@my93YwmJ#(rv%oM99HIF9x5br(A?}XX?iRE9XN<;* zPH(BIfHwOW10kDLg(PQ+vrjBg4IMMYgunza^wgIk1NG6gz$uw6#OW3kDH!nfLG*o|PoFR~7F<73-^(I(zX2P--3E{%|B{JaJj z7iZQqD8u&!l;H-T12a%VgAX-G0#|SsyS~r%f#LC0|G@c!nJVd0fQl0?d7c~>I6@40 z?f@wjx~-D%7n7_3(SaDC&`BJ&lW&WWJ`n8tF!we+ztq{Fdzc^VZ9FsPM)6DN5N_qj1i6J-rTH?<@`u@OKK}~7sAC} zEw7KPR`x_yt>;$#=a`koWo~?xLvI>%It0m6E(a_nxfGo`4TKH3MYT@C6C*yx#wSL1 zXL_{xt^fx|VBx`PlBk#fEIm%^A(w)kcXtH>BP)0rv{ju5FOtDgcyvtpd$}kBAXG8K z?}qz}pBRP|0tG)3acS{_Su}Ky0%3t%(g+#j-vvv#E5}fX-qec0C;t0Jb}HHiSkzBE zsi3GtdZv7PQ1pEGl7fb<|Laxcz=AMtko`XrQK7X^I0I`Hyh7u$K-)-a@{NHD2(=w~ zsZxj0G)Rxf3I1!%tMW?sPC#vqGSr28t1XTCO~ytk-9qQ}fa}q`mIGAqBdl1Obebqg?9OM^ z__Crd+&~ouGEriP^MV~7w8n#{@T|H5;kKQ#Zso&-H096~JFq#tGfAfhgsr-W;*>-j zcmNjy3a=Gn#^|PnqZ{jUj3}0=MZVMagD$kIS-ThksF|wHv$}zYJl7Qdi(&KVIvsX8 zySPAHlx{7$o_swj5l$!qP~^0tcwP|(I zR+&d~3L;g?cZ}IfrNnBN@0RvsUW;>HGMVkP3#O{YoAh7mLJG_o<2w6UlsLxh7u zEN^KE;0Q4C$MY>f-iWkt5#12F*xujQ+YgY#WKW@44|2o5LDem0!IyH7SJgsDqDqko zI7-R}&;d<3cXS?Q7!|sn9=CVAH zQ;0eV6(E$t3Vl@Zlzfe^hMoYU@rb)}ur(#=-<2Fb7f3o0w*rgKsUI@Oi5L&wgO77& z?w|+o5v1EEcTJh>li~f8rf}dzvDoCDC)}ty+svQjrjv=361q(Zzv`{=g!OoE7d((( zL0FRxkyCDMo^ldeM#MJjw|tHFqY6?0It79;U~mMQPe)+(I8qu|2biei;)|i*i0T^9 zr5jex42m^YBjX2hUg;GkkVevIoe-DbN^+bU}$YOyf7 z@fZl>9IMcYT&p0!+{D@V9SFcC1b#Oerh=?#+}=qi@-HNrMgxl45+A99_FMyT0j{irpN1F9F9nLa3>RR-Gc+)E5 zTW4-=IK^UsKvoHm>&&^|=U_F-7N6OPeY2el$fxIs1m(@|;WB=cgZVBjV%mj&3{!@T zQ@ro0%J3ihljohbVi#@dg`D{ll@g0Ai1MHd7%Ma97euHpCBB_4>xpk`QUyZp?urPJ z&!J;-(071J=GSt-!BucmC=wf97(0I&jv@ygu@V7*O5g&SLca~#12-Q%n39Rn{2e6o z%BJ|?RM=H2$HwwP$K>{H-bUWW9yAzOsA3}5j7p9P1z=RBD-i|X6!O!2>+X0<){6BP zZ_OkCq~Q;#bpxgWT62SAR=!1RyDa{iK_)H}3}25atTC}vf36Y`ZBI|?JU8hNHKS`v zd~quqQmJYeP578cSglA|sG`4+2!*EvAc<6>m#IZ`nJRCa?C+KMgA)#WDah?M36xH@ zccU~^9VK@FR1yW*P%G2!kMi#(&AMQx9n8PjR&0YS0BtmH(b}bmF_RP!>?mUTxE%o%XT1ZC^79n>7ldFbf0BfRH^^Lpc6yd!r~$C_ zW)v_)f*i3g3hZ3hj4@|?OeASyEh9e*SCj|A$=NsXBpe(aji{NY^&eBCKx6TlRw}(Q z;`9}N>1qAvR0(3CqZ7$5zc5KcN1xunI4cu!tlg9uBD?jkc3GQr?z2I70W<3cmdo$ z8>E37xfU89p^s^d1kYTGF39!2r+hU##;F|jm=MZ^f;#x@lG#WL3^JJlu-lP44v?q3 zEnZC=*$&&oI1_z>j$uL~WD!V)2R;hX$RlGs^`&^v@zT zmdyA0oV{D=A9bl(cX=QE2Cj00Uy~R6K=XBzbbY-)f-3NuLOMpPjB=ab%!>yj6iJaE zZ-sw0TA3~{FNXM??jcnqupzk<>|pwN!ze=4j*d|(&6CDQurWh(;aZcN$l@v{#_LaC z2t^%Y6z19R1WklzC)Z8=MO%ql!{r^GmjMtC0V6k!=y<|EImj8KOCWWgYKLQ2L0>a7RP6GAyg^OL8kb;02;ZNm|nXI*#odasYVjQD;V|ltur{` zpx8EAVC-7gooRBDvIwkwFd2Aac?U}xJ7icc3+#hxQ9~+0^>ddARclCn5sMzr0mCVwnU6nZ)q+Tp$g*j8kr5hpGWMs~peV|Q3PB1RbK zs{~4HkR=__%O;luIp=OAZh>Jf1u1DWO2rNY#!{n%ghMLS6X**7p`(?54hwS(q|z8@ z?EUwnjEM4^GT3HBQL6<)4`HjEh-IdOP`R|-7yzoeeS-sGJw?x>M^>et-K%<7*UxT# zFHM2%A^#RTCxhqkSDxi73OXG3FK zv$IpLOo~g-fFcC{5DsE`d9?Bjp2zdr0_g|_#bqBtj@=0Xrlc|CTQlA`q$eWucbf8p zbc>9G=x%p(2jx-Xpke}jkKP1I`}1mKbngdB7~MJ4l*aoz7%L&Qk7~jSFo%9P(aAA2 zUm0buywhOxu4?>IJgBhuB@}jV4RMKy#Ho)h63Nx<>xsRw6vWVthH` zrO#qiiwV?C?}Sbxgcl%8L*Q{%%yCH3+d#lDoAKyt3Yf6WQseAc@Ff5kwaBn2!;U}7 zwDC(vo+Y!MXQi9?j2;shlj+Od$oi80nvwM|YvdU* zYM3-HB0W(`lr8bN$nNTuU%`FKyIuuhihe6`@KKxe>d~OrljVob5dDUI$NlSz;WA}v zjPeGHSuC@mg8MPU>-mh=(~F6ufa8y08LmUe`VgGyk+IrB)e95FTGU7Za4|? zFwzF0?mg(@kLz#l!PV|R%#9gh?rCtJkGWDOALWr&`49Gdw(*rwx?q|S9sZ{SPs9GL zG}vK#Lxu{Fwv3L5;_GU5QUo4`ka{R&r%Pl`a)=t`4+OE1&EcdCFh%zVs%AqAWz)d* zq_*nHp4h>ksonMObB+)6APhHVSN{drH%ECtVJ)cvr*8d;zdwx3e>=#WJ(L8-6I{!? z>E!sg~uPc1bTrI01eKG}yWx)~W<7h=8T7km0qUcZPGX#UjSeCv4q`Zxp0v{met z@|%G?=(n1>YkK%YM?f15Z^<-=REBIe>h~yV;i#lU%q(0}8?qeoCwVlnTLoBf<=87K zx~G+G&|zM5DBxBJA@bdICxY9|sJ$^e1+v(hog!;e@NegDHyz{G+%879+o~0(L@7&L zGbnSz{z0&zb)ftfdOr@R1A`gXxu7ffOG=7P-#5tnHxv3lekW|Wa{%wi@%x?>2geyX z+e9S1bKoi9=RI^3=(ZfDmsLE4}qQmHk$46`(Hx8$(57#p!mW$Ccsy2gU*iTxE z{N}zfdAy%rXCb!t&!#&pxZPAiom{&>)zPhRWlR;&!ZpK&B?9k{!3oNG>A!S9Zgn@c zrd2PWBXJW(x&>mLQqd1r7#~%KfVKWDTr-^c4q83~>%@t1xml5%xwtBUBURKVF$=x^q;0)i9icSR5c6;%AVBzKzYd5b$a6HO+Qhj zi_k`UJMTZ2kgABc?fqXyczu5o{400#hdPCJ_G+GY_6IbokDwEAn+a;u*oL!QJP8BXk#@;4dDY&lrZ^OGnm(3)8$D8AOeTtC}Rm0G~L#%IWc zC1my)HTV?Vy!@f~6I`5>u{cl>vzJoLWqHr!u!l9pbaW>-+mcBf9CYX|4WTN}#7ZIM zZ6z^8lCXEXJau`N#Uc?I{KYLp7sH6?=7XH>>Fb7(xcrfwFGJZPoE{-{ltlOdnz%0D zC{bUfvQ|VvpP)gM>ZU@bO6up<<@4(;0Ax+1t_%AZcJ$U*kYxN-YsgX=t|@<%@Rf%F zvlvr8ml1)Cq|q@gF5%Tc^@A1wn}ThSySkz-7)_T=e{#<_pf)f64s^!BIoIua`T{Yc zxj~tnF(f~iKX!hrBMKscO_iVI@dhU#mr>)r6g%m~Z8G9GEF!hBnuk?c{>gS48VQn+ z)cQ|ykk=P+aIi>dO9Tz$rwiXl>mnNe%bU^odLCt3YCpdvvL>KG$xa(Gn z^MRf)W@_-Uor6VK0H$2SJ6ZqKAnNiHW4#@8c7HVsroS+Rg#n=BiXCBY@*egYZABK! z@vSXDHW;hT*HJx3wh`%rB3=SW@H6n2lraF4u>@}9Wp_o`!P2*=!`Y`U*Ar}hNslLR zhbGI{kNIGiRwHDVML_p|YYH(S%ne=*OZgAZ=%38c^!wdqkB=WMMz4Q*g-XAorlj0< zzn9+z+^6@>NLnZF9|;|rZr1!?Q-PKg0c#}2knGC5e+E5ac`cOfT5>Li(`0h^6{_N= zQ(2Zg4Wmv*Ds&)H@3drLB}>n&mC<7(rO^#_0r?8OfQC`9;L+tzBJ5Qe_Bw-pa2nvN zlMi635z-ssEaT^^M8YMECe)akm8wU(!D_l}p-k282NNIQDTK6m0^e(_ew5AWt)=vM z#^~r9ZW43DF#p~hNf}RZIAw`?Jpb(`#(9DQN z(G^O+j3YFDQ2+Gmf%3oy@}OWN(7Qyx3i!oDwChggFxKsT`rnwXt0{iPSMJ@DGX{Ex z(We0vE08DK6AM8^0l>(p@qYXoVM&aX(PRK&u)e__PI#Ruo;(OLF3UHL zLGSEj-2g|rFH6~P@GvnWp9`w3S_jL2XU21g2PMr5#66>!TqMq^$T%SO<-_)Fq@k3}37W0b%j4ea6~IQd{H>Ql{1blr8Bp#Ly-9NL3>W<|Xu#s43WyMeAWL za0MUSD|n31+Xxur%58rAzofkjv}Jc$;JF{?b?-j6YFE`wQb|>keKrNT!Bi3@gc2gu zzUEDkzyQ*$)&=U!F!!cuQkGe+UPV$N0V4z&mF}TiN5aeCm?*wLMrTa{jeydQ4a$hN z8WfOe5CNf$3e^05-+!NT?@fxwT9aD2=bZicKfd?({lEYBcSQ^xK2KWiBX=-Hyrl;R zvDO9%wfi2F-clb#Bl@=lB)EH|1;$rGv23;yYT(hK6PK6* z$SM1yKrpSH(mMtzTs@$>H*YE6#=*@yJR6usA%;&QqtWUkh4HKuv0>tFiK--2(rQ%j zceu5DBa`J(Qc6vq2S&j}NUJCes40Nm1$|tERi?y=2=AADT3v!9xp7?u{?H-=;R)eX)Sm|XX&hPj3GVdJb?u2^b z3wnu?d+*D+^6z_l-iOoYKEWBRoWRN>5!SLI$?2tOsWYi{_@e&GQacDV1TV;2>+D`U zl_I$6H}!?2zyvSawmSPP9%VOOw5d6!s2mwhAWU&8d^l`zXa{k`lc{|5H_nF(WlhNR zHKT4t?;*%($@Vr_Qvko@TImP`w^#9wDOgszyevWm|Uev4F5x_(@A zBm$)EiumW)2blI6&3s4$bdgQh&uFPWis4$a0&x}5#|g-kSoQ6zdaKEF8>l}Zo@_Mq zsRc3>b50ZmRZ=AYBaGpu+MA=x2slG&*Q@{xA)4BV*TDcPbBn&;@eRC%9yad)l3(5K z6{Mgjv`$QvF+*MNV84OZyq2{>Wo3+N{K=o+5f=v*gdx61Gl@s)M*)FT#BN$4va>x1 zV`wlEW_+V1QJ0v^BZ)uc0ScbPGD$}0M@SWhC$I!*^JN~R$vv$5Q_MwZ;QI6&Us!LK zc&x94&3VnrQ=Qg-Lb_`T@&`qxT^S61c4`uxGH(t;u=G4;IR9x}4FU*>7FF7=3m+y? z2f{GJ968^B1mM^;z0j9dg00K$Jc{|`8Mp?0e^h8?p&uNFj%!|`BEy8eW~EVHiERzo zBnvCgJPUJ)(9~#pK{BU=_1t_?DnBVal$9q$5v!{3(dOwX_X=KmoDXqLq`lV~|{>K^lN<$}olY-QX+OG6VZC9-NRCw6C=rJOz=A}-Q2N+i*`mSJqb!R^v=|JcM34m## zfFz9%5koEd`pa4^FBoi4a73do8rl{(vwL{5Q!Cm zbQAcyGDX~Q5ClTf5-8PT1%}XNj^0;N41p&k{IFc$!fut;%L5$HGQBjY!0@XSko+5t zbDFb^GLuK^|Fd5PCHt1g%+D2##8jn{Y)ZrjQ%Zx8KNSA!<0*%szlf1=)%3=E5l`vE zEXK|cP9JwB@=HnwWQJyEyDzGZ4EblT@bn`Ky2ZJASxK>P9=q4x-8irW{JVn0bjZ$?3$V=j-@0MI*aiT?!r6indfUs zT?QXRYi`5!DS8<&mf$ZRkzA?&fq6PPAdU7J*8>x$rAR2TEg(TSE>A(mdo?1Ec)RY#0k2_N-85i|Fpl9$(I{aG}GG zT1GY4HjA=1A%!x_t?KsEVI086A!W>sz5d}uDI$Zkqx?G<6U7PML{;<)+iCZ;JUSug z^&bdlu?-7)#!iFx9lSq~eLXEdZVL^ZGXs&xtHL=5+!Dq^;K*9v0$vg3O!x^uls*!H zDUHLcELql#!;I$;Az7t|M}%>iL#iYYe8!(?q_7l6{TC2J^YwN5g^ZW@gOf%$MnTbaE>1itAPX zF#hFzNW$x%DKVKEWDk&jOwfYmZ6pag7cTK4d?|Ft$cg$C zQ-udXN@yHlK?z`%o@llE^c&R>95GE|ne@W3I{;$ku}xmFDfx_NC($TmOZww;8P_&c zNp1Ce=nH#E;Ki{@04VF;^8e=DKYa!!KhT~2DDPTUhmBjr?-}-1$+wsTN?%FUT(HG4 zBzs4p3N52kOo8yH8!R=6^or8@TR$r`an^n9=XcJkbXP6=zCe4T1pa;olT!4r90JUP zZkD#4(!8PudJEzSGJ$GnRrhufW(a_s)1TVs2W%H;DCR;+d{WY5_Y*h|-2MsXfTgL> zmwyr#k^B0dRHAj~v=k>0N145sGWyT{w!tP(Z3akZi8nNCaQk-5y<n)T2A-yzB#aX#9*fGA z@PRFI^450cni)I!XKXGvfQa~8k|Qa#MA+5BGJFy*#H59MOC_kb0Q5?JW&`;=eGN8w@CnNP=5;cKzgMn8TN`65wlQyMnds+0Uz=+N06m?{s7af z%^bgg5yEB5i8qlJ?AZgQpjNhCARmSC$=J&gss-c)SlmQ5e}37t=2{>mf2|Nrn0pRPJI!@sj$E1ROTkyisB&@4KBk&#|Ym=LCFfjJQq%V@u z2?C>p6}&bD00S10qcjFs$RV_W`4&VNu3gF2vq&8($h-hkZzp{uW`km5A>(is#|U9J z@7K7-0$?t9l$^$Ty1Xk@+u(QcgnU0gfZTw@tk8?~*Fj>x7_wiJ{j!|Xik=~6H2d?7 zr{t{Zy1PByfiw)~2tlMW`FB)7WdC2R*`y2X4f9rT2@2oA_*B+Sjvu>qqaxN{pH6lw z?Z*68X7H->Q#hp)@bJ2)ieNj}tj|HI-6_%SOuiz`S8tC4Ff}r$p3d z=@jSPFd8p+@5l33hH#u1Tg5ZXB*SnSZq2m&GlhA>o;Ig$@WxH4aELFqO8|sGU(~t+ zEcDLC9Rc+GTOz%Anc;I7U2k?9kiB^QX+P0i{EGJR>!-8l4{ZM8_568Cjws4MxPh9mb?4tEz+s)JE;w~c6SIg~pPmdS3Ls60`v*Y&Z z(-$rtKwSC$sDI$1^$FjYT_YfNHy6MAyGxE&wQUO#;yBTWk%m8^T)5sToc*$)6mGapD_3WkIwrH zGw62Ru%cBFCpR!+tS`(LW{TWMf~U7F;zB&t69&_db?I0zT{NH!`IfJv>mnvhMnA

h)KvoRP5=XTLm%} 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 OIo`O=P+npzEBqh$J#3Hw literal 0 HcmV?d00001 diff --git a/web_probe/main.dart b/web_probe/main.dart new file mode 100644 index 0000000..8b4e6bc --- /dev/null +++ b/web_probe/main.dart @@ -0,0 +1,87 @@ +// Standalone web probe for #335. +// +// Loads ONLY the drift stack, so a pass or fail is unambiguous: it exercises +// sqlite3.wasm + drift_worker.js in a real browser without the rest of the app +// (BLE, permissions, radio) confusing the result. +// +// Renders the outcome into the DOM so the result is readable without a +// debugger attached. +import 'package:flutter/material.dart'; +import 'package:meshcore_open/storage/drift/offband_database.dart'; + +void main() => runApp(const _ProbeApp()); + +class _ProbeApp extends StatelessWidget { + const _ProbeApp(); + + @override + Widget build(BuildContext context) => const MaterialApp( + home: Scaffold(body: Center(child: _Probe())), + ); +} + +class _Probe extends StatefulWidget { + const _Probe(); + + @override + State<_Probe> createState() => _ProbeState(); +} + +class _ProbeState extends State<_Probe> { + String _status = 'running…'; + + @override + void initState() { + super.initState(); + _run(); + } + + Future _run() async { + final lines = []; + OffbandDatabase? db; + try { + db = OffbandDatabase(); + + final ok = await db.verifyReadWrite(); + final r1 = ok + ? 'PROBE-OK open+write+read' + : 'PROBE-FAIL readback mismatch'; + lines.add(r1); + // ignore: avoid_print + print(r1); + + // The point of the migration: exceed the 5 MiB localStorage ceiling. + final big = 'x' * (6 * 1024 * 1024); + await db + .into(db.storedBlobs) + .insertOnConflictUpdate( + StoredBlobsCompanion.insert(key: 'big', value: big), + ); + final row = await (db.select( + db.storedBlobs, + )..where((t) => t.key.equals('big'))).getSingle(); + final r2 = row.value.length == big.length + ? 'PROBE-OK 6MiB round-trip (localStorage cap is 5MiB)' + : 'PROBE-FAIL 6MiB truncated to ${row.value.length}'; + lines.add(r2); + // ignore: avoid_print + print(r2); + + await (db.delete(db.storedBlobs)..where((t) => t.key.equals('big'))).go(); + } catch (e) { + lines.add('PROBE-FAIL $e'); + // ignore: avoid_print + print('PROBE-FAIL $e'); + } finally { + await db?.close(); + } + if (mounted) setState(() => _status = lines.join('\n')); + } + + @override + Widget build(BuildContext context) => Text( + _status, + key: const Key('probe-result'), + textAlign: TextAlign.center, + ); +} From c25b7ccbdfa486b21409a0b7e40a7165cf8cdcf2 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 15:19:41 -0400 Subject: [PATCH 31/60] chore(#335): pin drift, commit pubspec.lock, guard the web asset version Owner asked whether the web assets must be re-downloaded on every drift bump. They do not - the version can be pinned - but doing that safely needed three changes, because the failure mode is silent. 1. drift and drift_flutter are pinned EXACTLY (2.34.2 / 0.3.1), no caret. The committed web assets are built for a specific drift release, so a caret would let pub resolve a newer drift than the shipped binaries and break the web build with no signal at compile time. 2. pubspec.lock is now committed (removed from .gitignore). It was ignored, which for an application means resolution can differ between machines and CI. That undermines the pin: the assets are matched against the RESOLVED version, so resolution has to be reproducible. Owner-authorized, and it is a repo-wide change rather than a storage-only one. 3. New test asserting the shipped assets match the resolved drift version, plus that both files exist and the wasm really starts with the WebAssembly magic bytes. Verified in both directions: it passes when matched and fails loudly with re-download instructions when skewed. Without (3) a stale asset compiles, deploys, and only fails in the user's browser - the exact silent-failure class SAFELANE 6 forbids and that produced #333 earlier. Upgrading drift is now a deliberate step: bump the pin, re-download both assets, update web/drift_assets.version. The test fails until you do. flutter analyze clean, tests pass. --- .gitignore | 1 - pubspec.lock | 1624 ++++++++++++++++++++ pubspec.yaml | 8 +- test/storage/drift_asset_version_test.dart | 74 + web/drift_assets.version | 1 + 5 files changed, 1705 insertions(+), 3 deletions(-) create mode 100644 pubspec.lock create mode 100644 test/storage/drift_asset_version_test.dart create mode 100644 web/drift_assets.version diff --git a/.gitignore b/.gitignore index 2c71b17..31e840c 100644 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,6 @@ migrate_working_dir/ .flutter-plugins-dependencies .pub-cache/ .pub/ -pubspec.lock /build/ /coverage/ # fvm project files diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..be2ed1c --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,1624 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _discoveryapis_commons: + dependency: transitive + description: + name: _discoveryapis_commons + sha256: "113c4100b90a5b70a983541782431b82168b3cae166ab130649c36eb3559d498" + url: "https://pub.dev" + source: hosted + version: "1.0.7" + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: a49d6cf99e8d8e7a8e93668d09ced0bbdb954d0b4fccc2f5f9241c6b87fad95c + url: "https://pub.dev" + source: hosted + version: "99.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "663efa951fb8a45e06f491223a604c93820598f20e6a99c25617a1576065e8b7" + url: "https://pub.dev" + source: hosted + version: "12.1.0" + archive: + dependency: transitive + description: + name: archive + sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff + url: "https://pub.dev" + source: hosted + version: "4.0.9" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + url: "https://pub.dev" + source: hosted + version: "2.13.1" + bluez: + dependency: transitive + description: + name: bluez + sha256: "61a7204381925896a374301498f2f5399e59827c6498ae1e924aaa598751b545" + url: "https://pub.dev" + source: hosted + version: "0.8.3" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + build: + dependency: transitive + description: + name: build + sha256: "45d14a0fb23e018d8287c32fc98d726ce466b231928ed9b9200f29bd3ccd39ae" + url: "https://pub.dev" + source: hosted + version: "4.0.7" + build_config: + dependency: transitive + description: + name: build_config + sha256: f2c223156a26eea323e6244b85141d76413a80aeee9fe0b380773789fabaf8ae + url: "https://pub.dev" + source: hosted + version: "1.3.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: fd754058c342243718d5171a95f352cfc9fcf0cba8cfa26df67cb13a5836db78 + url: "https://pub.dev" + source: hosted + version: "4.1.2" + build_pipe: + dependency: "direct main" + description: + name: build_pipe + sha256: cf851728764dd87c0a5011a209be64ac04dbb3836c244dbdb31684d7e4aff79f + url: "https://pub.dev" + source: hosted + version: "0.3.2" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "5367e521935b102bdf1e735d2aab461e36b2edca6517662d088dd04cc39f8d16" + url: "https://pub.dev" + source: hosted + version: "2.15.1" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "34e4067d30ce212937df995f03b69992eea683539ceeac7f679a1f1eba055b56" + url: "https://pub.dev" + source: hosted + version: "8.12.6" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" + url: "https://pub.dev" + source: hosted + version: "3.4.1" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" + url: "https://pub.dev" + source: hosted + version: "4.1.1" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + characters: + dependency: "direct main" + description: + name: characters + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + url: "https://pub.dev" + source: hosted + version: "1.4.1" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a + url: "https://pub.dev" + source: hosted + version: "1.4.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" + url: "https://pub.dev" + source: hosted + version: "2.0.4" + cli_config: + dependency: transitive + description: + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec + url: "https://pub.dev" + source: hosted + version: "0.2.0" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + url: "https://pub.dev" + source: hosted + version: "0.4.2" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + code_assets: + dependency: transitive + description: + name: code_assets + sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + coverage: + dependency: transitive + description: + name: coverage + sha256: "956a3de0725ca232ad353565a8290d3357592bf4250f6f298a185e2d949c5d3d" + url: "https://pub.dev" + source: hosted + version: "1.15.1" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "92c9c43c383bfa1c32079d3bc492d55d6d4318044b7b47edaff8971cbb555c51" + url: "https://pub.dev" + source: hosted + version: "0.3.5+4" + crypto: + dependency: "direct main" + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" + csv: + dependency: transitive + description: + name: csv + sha256: "63ed2871dd6471193dffc52c0e6c76fb86269c00244d244297abbb355c84a86e" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" + url: "https://pub.dev" + source: hosted + version: "1.0.9" + dart_earcut: + dependency: transitive + description: + name: dart_earcut + sha256: e485001bfc05dcbc437d7bfb666316182e3522d4c3f9668048e004d0eb2ce43b + url: "https://pub.dev" + source: hosted + version: "1.2.0" + dart_polylabel2: + dependency: transitive + description: + name: dart_polylabel2 + sha256: "7eeab15ce72894e4bdba6a8765712231fc81be0bd95247de4ad9966abc57adc6" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: a4c1ccfee44c7e75ed80484071a5c142a385345e658fd8bd7c4b5c97e7198f98 + url: "https://pub.dev" + source: hosted + version: "3.1.8" + dbus: + dependency: transitive + description: + name: dbus + sha256: "792974a4007974fbc5c1b5433eb2330a9db3e368c3f906253af4c007d0f49a91" + url: "https://pub.dev" + source: hosted + version: "0.7.13" + dinja: + dependency: transitive + description: + name: dinja + sha256: "34d4e569ceb3d900ab061f16cae947223968cc63d2484a8a4710d5ecfa4ae3a2" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + drift: + dependency: "direct main" + description: + name: drift + sha256: "84688491040b0ceb26575709a84d701c84ad4df4d8aff020adf6d85f155fb0dd" + url: "https://pub.dev" + source: hosted + version: "2.34.2" + drift_dev: + dependency: "direct dev" + description: + name: drift_dev + sha256: "9cfff1576b49725da0d32c040651a41ae195e8c4af8d8da301593e41d7abc2f7" + url: "https://pub.dev" + source: hosted + version: "2.34.0" + drift_flutter: + dependency: "direct main" + description: + name: drift_flutter + sha256: "91acf4bee7c3c84467cba46455aa70e5292a3b889f4582645d74f2e5a8c106f2" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flserial: + dependency: "direct main" + description: + path: "." + ref: "48216310061efc8d5d217cc18014fc2cb501646e" + resolved-ref: "48216310061efc8d5d217cc18014fc2cb501646e" + url: "https://github.com/MeshEnvy/flserial.git" + source: git + version: "0.3.5" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_blue_plus: + dependency: "direct main" + description: + name: flutter_blue_plus + sha256: "4d9ff93a197cf64925652414f11a22cd160a6e500997020ffec044544b9ddd38" + url: "https://pub.dev" + source: hosted + version: "2.2.3" + flutter_blue_plus_android: + dependency: transitive + description: + name: flutter_blue_plus_android + sha256: d66bdcb0438e643d5de4af914851bdf7322448384795666efe92236626484740 + url: "https://pub.dev" + source: hosted + version: "8.2.2" + flutter_blue_plus_darwin: + dependency: transitive + description: + name: flutter_blue_plus_darwin + sha256: bb433d8c614964be3023d63f0460f1e2fe1a436cdbabd9bcc88438b509f4f045 + url: "https://pub.dev" + source: hosted + version: "8.2.2" + flutter_blue_plus_linux: + dependency: transitive + description: + name: flutter_blue_plus_linux + sha256: c08563ccef620be5a06dd6b895ee51ecd986d1fcab1edd2dbc3fa125518abb21 + url: "https://pub.dev" + source: hosted + version: "8.2.2" + flutter_blue_plus_platform_interface: + dependency: "direct main" + description: + name: flutter_blue_plus_platform_interface + sha256: "55abf2bdae442f2ed4cace044d491cbb9dd833729585541354e4ec42c227989c" + url: "https://pub.dev" + source: hosted + version: "8.2.2" + flutter_blue_plus_web: + dependency: transitive + description: + name: flutter_blue_plus_web + sha256: f6ed6bedf7568a3c3f91f0d668ca974c2a6d95f397e58e0d8cd6ad8681355819 + url: "https://pub.dev" + source: hosted + version: "8.2.3" + flutter_blue_plus_winrt: + dependency: transitive + description: + name: flutter_blue_plus_winrt + sha256: dce19eb095c5ed70de997c02911afd255d867627fceff9c68c6f8b88792d597e + url: "https://pub.dev" + source: hosted + version: "0.0.19" + flutter_cache_manager: + dependency: "direct main" + description: + name: flutter_cache_manager + sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" + url: "https://pub.dev" + source: hosted + version: "3.4.1" + flutter_foreground_task: + dependency: "direct main" + description: + name: flutter_foreground_task + sha256: fc5c01a5e1b8f7bb51d0c737714f0c50440dbdf1aeddc5f8cbba313aa6fd4856 + url: "https://pub.dev" + source: hosted + version: "9.2.2" + flutter_langdetect: + dependency: "direct main" + description: + name: flutter_langdetect + sha256: "93bd865c7d5723eac614744abb32234ee4f593505a293bc17ef097bd55fbdf38" + url: "https://pub.dev" + source: hosted + version: "0.0.2" + flutter_launcher_icons: + dependency: "direct dev" + description: + name: flutter_launcher_icons + sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7" + url: "https://pub.dev" + source: hosted + version: "0.14.4" + flutter_linkify: + dependency: "direct main" + description: + name: flutter_linkify + sha256: "74669e06a8f358fee4512b4320c0b80e51cffc496607931de68d28f099254073" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + sha256: "40c6a69189a622bda89ddcf50a139f4ba0f0eb9c0fef6718845b1f8b95452ed6" + url: "https://pub.dev" + source: hosted + version: "22.1.0" + flutter_local_notifications_linux: + dependency: transitive + description: + name: flutter_local_notifications_linux + sha256: "9ca97e63776f29ab1b955725c09999fc2c150523269db150c39274f2a43c5a8b" + url: "https://pub.dev" + source: hosted + version: "8.0.1" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + sha256: "4dfbae10debab9ac975d8b01943fed94c0c19dad43f825964b29f3c3f9a8a852" + url: "https://pub.dev" + source: hosted + version: "12.0.1" + flutter_local_notifications_web: + dependency: transitive + description: + name: flutter_local_notifications_web + sha256: "516afaf97a2d1e67a036c6617321b00d205d72f7a67b6eccf936cd565f985878" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + flutter_local_notifications_windows: + dependency: transitive + description: + name: flutter_local_notifications_windows + sha256: "6f43bdd03b171b7a90f22647506fea33e2bb12294b7c7c7a3d690e960a382945" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_map: + dependency: "direct main" + description: + name: flutter_map + sha256: ce1debbb29cade61964334b2a19c7c76e5bb2ef7dacf126c6424ee44036232f8 + url: "https://pub.dev" + source: hosted + version: "8.3.1" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + sha256: "35882981abcbfb8c15b286f0cd690ff25bac12d95eff3e25ee207f37d4c42e7f" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" + google_cloud: + dependency: transitive + description: + name: google_cloud + sha256: b385e20726ef5315d302c5933bfb728103116c5be2d3d17094b01a82da538c1f + url: "https://pub.dev" + source: hosted + version: "0.5.0" + google_identity_services_web: + dependency: transitive + description: + name: google_identity_services_web + sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454" + url: "https://pub.dev" + source: hosted + version: "0.3.3+1" + googleapis: + dependency: transitive + description: + name: googleapis + sha256: "692fb9e90c321b61a7a2123de0353ec8a20691cd979db2553d8d732f710f6535" + url: "https://pub.dev" + source: hosted + version: "15.0.0" + googleapis_auth: + dependency: transitive + description: + name: googleapis_auth + sha256: ea53b290aaf1fdff615c8d79091b02c9150ff81c48f7f3b7460598c444f3c50a + url: "https://pub.dev" + source: hosted + version: "2.3.3" + gpx: + dependency: "direct main" + description: + name: gpx + sha256: "1ab97d83824ce5d853b98c3ad13867ffa7f0a7aa175babb4f5c3d038c156961c" + url: "https://pub.dev" + source: hosted + version: "2.5.0" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + hooks: + dependency: transitive + description: + name: hooks + sha256: "025f060e86d2d4c3c47b56e33caf7f93bf9283340f26d23424ebcfccf34f621e" + url: "https://pub.dev" + source: hosted + version: "1.0.3" + http: + dependency: "direct main" + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + image: + dependency: transitive + description: + name: image + sha256: "6300175e00616bbc832e2fc91bfa4d776af5402c81c7151bee6905bb08473c52" + url: "https://pub.dev" + source: hosted + version: "4.9.1" + injector: + dependency: transitive + description: + name: injector + sha256: ed389bed5b48a699d5b9561c985023d0d5cc88dd5ff2237aadcce5a5ab433e4e + url: "https://pub.dev" + source: hosted + version: "3.0.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" + io: + dependency: transitive + description: + name: io + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.dev" + source: hosted + version: "1.0.5" + jni: + dependency: transitive + description: + name: jni + sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f + url: "https://pub.dev" + source: hosted + version: "1.0.0" + jni_flutter: + dependency: transitive + description: + name: jni_flutter + sha256: "8b59e590786050b1cd866677dddaf76b1ade5e7bc751abe04b86e84d379d3ba6" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "2a743920d81b7910627f68ee2c9ac1fc0bfee32b9fc3403587d7c6791ca12f80" + url: "https://pub.dev" + source: hosted + version: "4.12.0" + latlong2: + dependency: "direct main" + description: + name: latlong2 + sha256: "98227922caf49e6056f91b6c56945ea1c7b166f28ffcd5fb8e72fc0b453cc8fe" + url: "https://pub.dev" + source: hosted + version: "0.9.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.dev" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.dev" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + linkify: + dependency: transitive + description: + name: linkify + sha256: "4139ea77f4651ab9c315b577da2dd108d9aa0bd84b5d03d33323f1970c645832" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + lints: + dependency: transitive + description: + name: lints + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" + url: "https://pub.dev" + source: hosted + version: "6.1.0" + llamadart: + dependency: "direct main" + description: + name: llamadart + sha256: "71d7986483b0e48402bcc8f9e0e32a16eab2983f5cd243e77d1603cb1a8ac669" + url: "https://pub.dev" + source: hosted + version: "0.6.17" + logger: + dependency: transitive + description: + name: logger + sha256: "7ad7215c15420a102ec687bb320a7312afd449bac63bfb1c60d9787c27b9767f" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + url: "https://pub.dev" + source: hosted + version: "0.12.19" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + url: "https://pub.dev" + source: hosted + version: "0.13.0" + material_symbols_icons: + dependency: "direct main" + description: + name: material_symbols_icons + sha256: "49c532dd0b74544e9d8d93ec0f821d52ec532e7c9263c889ebe71b1be4f34ba7" + url: "https://pub.dev" + source: hosted + version: "4.2951.0" + meta: + dependency: transitive + description: + name: meta + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" + url: "https://pub.dev" + source: hosted + version: "1.18.0" + mgrs_dart: + dependency: transitive + description: + name: mgrs_dart + sha256: "385e7168ecc77eb545220223c49eef8ab249da7bf57f22781c40a04d23fb196f" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + ml_algo: + dependency: "direct main" + description: + name: ml_algo + sha256: d076bec9f5059c39b2878b046259e13987fa79e03ed81d99af9477708126e09c + url: "https://pub.dev" + source: hosted + version: "16.18.0" + ml_dataframe: + dependency: "direct main" + description: + name: ml_dataframe + sha256: "75434865e7ff85edcf8b006cb7c580e65a7863bb853010f41e12597b69d55db7" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + ml_linalg: + dependency: transitive + description: + name: ml_linalg + sha256: beaebe2b85e9f1f6233a19098883f1f1596ce6ed05a03bc51ac552420507265e + url: "https://pub.dev" + source: hosted + version: "13.12.7" + ml_preprocessing: + dependency: transitive + description: + name: ml_preprocessing + sha256: fdc7bdf65e53bf377db2ac6ad017d88f08d81827e1c6062b9348893c0bab9f26 + url: "https://pub.dev" + source: hosted + version: "7.0.2" + mobile_scanner: + dependency: "direct main" + description: + name: mobile_scanner + sha256: ce3f059ebd6dbfab7292bba0e893e354b46730636820d3c9ef69005ce2d55bce + url: "https://pub.dev" + source: hosted + version: "7.4.0" + native_toolchain_c: + dependency: transitive + description: + name: native_toolchain_c + sha256: "8aaead321425bd3f03bd5894aa27c8ea6993eab95531da7e59f5d39c6e5708ec" + url: "https://pub.dev" + source: hosted + version: "0.18.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + octo_image: + dependency: transitive + description: + name: octo_image + sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: "468c26b4254ab01979fa5e4a98cb343ea3631b9acee6f21028997419a80e1a20" + url: "https://pub.dev" + source: hosted + version: "9.0.1" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825 + url: "https://pub.dev" + source: hosted + version: "2.1.6" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4" + url: "https://pub.dev" + source: hosted + version: "2.5.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675" + url: "https://pub.dev" + source: hosted + version: "7.0.2" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pointycastle: + dependency: "direct main" + description: + name: pointycastle + sha256: "92aa3841d083cc4b0f4709b5c74fd6409a3e6ba833ffc7dc6a8fee096366acf5" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + pool: + dependency: transitive + description: + name: pool + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" + url: "https://pub.dev" + source: hosted + version: "1.5.2" + posix: + dependency: transitive + description: + name: posix + sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07" + url: "https://pub.dev" + source: hosted + version: "6.5.0" + proj4dart: + dependency: transitive + description: + name: proj4dart + sha256: ddcedc1f7876e62717de43ab3491e2829bdad0b028261805f94aa080967e5859 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + provider: + dependency: "direct main" + description: + name: provider + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" + url: "https://pub.dev" + source: hosted + version: "6.1.5+1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + qr: + dependency: transitive + description: + name: qr + sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + qr_flutter: + dependency: "direct main" + description: + name: qr_flutter + sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + quiver: + dependency: transitive + description: + name: quiver + sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + recase: + dependency: transitive + description: + name: recase + sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 + url: "https://pub.dev" + source: hosted + version: "4.1.0" + record_use: + dependency: transitive + description: + name: record_use + sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + url: "https://pub.dev" + source: hosted + version: "0.28.0" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: "223873d106614442ea6f20db5a038685cc5b32a2fba81cdecaefbbae0523f7fa" + url: "https://pub.dev" + source: hosted + version: "12.0.2" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a" + url: "https://pub.dev" + source: hosted + version: "6.1.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf + url: "https://pub.dev" + source: hosted + version: "2.5.5" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "0634e64bd719f89c012f392938e173521f535d3ecaf66558fa94a056d22b5cc7" + url: "https://pub.dev" + source: hosted + version: "2.4.27" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" + url: "https://pub.dev" + source: hosted + version: "2.5.6" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" + 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: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + simple_sparse_list: + dependency: transitive + description: + name: simple_sparse_list + sha256: aa648fd240fa39b49dcd11c19c266990006006de6699a412de485695910fbc1f + url: "https://pub.dev" + source: hosted + version: "0.1.4" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: ec37cc0e6694374cbef59ed79685572c870a54ede6fa30a3e420feb3adffea02 + url: "https://pub.dev" + source: hosted + version: "4.2.3" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" + url: "https://pub.dev" + source: hosted + version: "0.10.13" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.dev" + source: hosted + version: "1.10.2" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: "58a799e6ac17dd32fbab93813d39ed835a75ccc0f8f85b8955fe318c6712b082" + url: "https://pub.dev" + source: hosted + version: "2.4.3" + sqflite_android: + dependency: transitive + description: + name: sqflite_android + sha256: d0548f9d7422a2dae99ec6f8b0a3074463b132d216fa5ba0d230eeefc901983b + url: "https://pub.dev" + source: hosted + version: "2.4.3" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "5bf6a55c166e73bf651ba7ec3ed486e577620e3dc8f3a9c6a258a8031b624590" + url: "https://pub.dev" + source: hosted + version: "2.5.11" + sqflite_darwin: + dependency: transitive + description: + name: sqflite_darwin + sha256: c86ca18b8f666bbf903924687fe21cc16fc385d086005067e26619ca530bef9f + url: "https://pub.dev" + source: hosted + version: "2.4.3+1" + sqflite_platform_interface: + dependency: transitive + description: + name: sqflite_platform_interface + sha256: f84939f84350d92d04416f8bc4dc52d3896aec7716cc9e80cf0146342139dc50 + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sqlcipher_flutter_libs: + dependency: transitive + description: + name: sqlcipher_flutter_libs + sha256: "38d62d659d2fb8739bf25a42c9a350d1fdd6c29a5a61f13a946778ec75d27929" + url: "https://pub.dev" + source: hosted + version: "0.7.0+eol" + sqlite3: + dependency: transitive + description: + name: sqlite3 + sha256: c73fd75df1332d76a6257f4823ae4df9c791f522b97e4a60cbcad214de1becf4 + url: "https://pub.dev" + source: hosted + version: "3.5.0" + sqlite3_flutter_libs: + dependency: transitive + description: + name: sqlite3_flutter_libs + sha256: "3ed7553eee7bb368f8950f58ba29f634e06e813c029aff6a0d60862b96de8454" + url: "https://pub.dev" + source: hosted + version: "0.6.0+eol" + sqlparser: + dependency: transitive + description: + name: sqlparser + sha256: "40bdddb306a727be9ce510bd2d2b9a6c9db6c586d846ef7b22e3990a2b24f02d" + url: "https://pub.dev" + source: hosted + version: "0.44.5" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "93b153dcb6a26dcddee6ca087dd634b53e38c10b5aa163e8e49501a776456153" + url: "https://pub.dev" + source: hosted + version: "3.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test: + dependency: transitive + description: + name: test + sha256: "8d9ceddbab833f180fbefed08afa76d7c03513dfdba87ffcec2718b02bbcbf20" + url: "https://pub.dev" + source: hosted + version: "1.31.0" + test_api: + dependency: transitive + description: + name: test_api + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" + url: "https://pub.dev" + source: hosted + version: "0.7.11" + test_core: + dependency: transitive + description: + name: test_core + sha256: "1991d4cfe85d5043241acac92962c3977c8d2f2add1ee73130c7b286417d1d34" + url: "https://pub.dev" + source: hosted + version: "0.6.17" + timezone: + dependency: transitive + description: + name: timezone + sha256: "981d1020d6ef8fe1e7b3de5054e5b25579ae7c403d7734adc508ffc47668e9cb" + url: "https://pub.dev" + source: hosted + version: "0.11.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + unicode: + dependency: transitive + description: + name: unicode + sha256: a6f7bcfc8ea1d5ce1f6c0b1c39117a9919f4953edd9fd7a64090a9796c499b57 + url: "https://pub.dev" + source: hosted + version: "1.1.9" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 + url: "https://pub.dev" + source: hosted + version: "6.3.2" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: b413d49b73867ac08dd2f9890efd3cc11f2a0e577618d50843440a1fb3776c32 + url: "https://pub.dev" + source: hosted + version: "6.3.32" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0" + url: "https://pub.dev" + source: hosted + version: "6.4.1" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a + url: "https://pub.dev" + source: hosted + version: "3.2.2" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18" + url: "https://pub.dev" + source: hosted + version: "3.2.5" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34" + url: "https://pub.dev" + source: hosted + version: "2.4.3" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f" + url: "https://pub.dev" + source: hosted + version: "3.1.5" + uuid: + dependency: "direct main" + description: + name: uuid + sha256: "9b129329f58692f6e6578329498a8fe9fbe98f090beb764ffbb8ee2eadd01dcd" + url: "https://pub.dev" + source: hosted + version: "4.6.0" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: "2306c03da2ba81724afeb589c351ebbc0aa7d86005925be8f8735856dbe5e42d" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" + url: "https://pub.dev" + source: hosted + version: "1.1.13" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "142a9146f447d15b10bdc00e21d5f4d83e5b32bb5f8f8f5a04c75311344923a3" + url: "https://pub.dev" + source: hosted + version: "1.2.6" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.dev" + source: hosted + version: "2.2.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360" + url: "https://pub.dev" + source: hosted + version: "15.2.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + web: + dependency: "direct main" + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + win32: + dependency: transitive + description: + name: win32 + sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e + url: "https://pub.dev" + source: hosted + version: "5.15.0" + wkt_parser: + dependency: transitive + description: + name: wkt_parser + sha256: "8a555fc60de3116c00aad67891bcab20f81a958e4219cc106e3c037aa3937f13" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: "67f0aff7be013d107995e9b75bf4e7f2c3ef2dfdb2c8e68024bba0a7fd5756a4" + url: "https://pub.dev" + source: hosted + version: "7.0.1" + xrange: + dependency: transitive + description: + name: xrange + sha256: "17bb75bd5eeae7152680f9b748880bf88a670b61f360266df56f7bd239cb6343" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" +sdks: + dart: ">=3.12.0 <4.0.0" + flutter: ">=3.44.0" diff --git a/pubspec.yaml b/pubspec.yaml index 90f04ac..eb704d5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -73,8 +73,12 @@ dependencies: ml_dataframe: ^1.0.0 llamadart: '>=0.6.8 <0.7.0' flutter_langdetect: ^0.0.1 - drift: ^2.34.2 - drift_flutter: ^0.3.1 + # PINNED (#335): web ships version-matched sqlite3.wasm + drift_worker.js + # from the drift release. A caret here would let pub resolve a newer drift + # than the committed assets, breaking web silently. Bump deliberately, then + # re-download both assets. tool/check_drift_assets.dart enforces the match. + drift: 2.34.2 + drift_flutter: 0.3.1 hooks: user_defines: diff --git a/test/storage/drift_asset_version_test.dart b/test/storage/drift_asset_version_test.dart new file mode 100644 index 0000000..c8760db --- /dev/null +++ b/test/storage/drift_asset_version_test.dart @@ -0,0 +1,74 @@ +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; + +/// Guards the one way the web build can break silently (#335). +/// +/// drift on the web needs two binaries shipped in `web/`, and they must match +/// the resolved `drift` package. If someone bumps drift without re-downloading +/// them, nothing fails at build time: the app compiles, deploys, and then +/// fails in the browser. SAFELANE 6 says that must not be silent, so this +/// turns it into a red test with instructions. +/// +/// Update `web/drift_assets.version` whenever the assets are re-downloaded. +void main() { + test('shipped drift web assets match the pinned drift version', () { + final root = Directory.current.path; + + final lock = File('$root/pubspec.lock'); + expect( + lock.existsSync(), + isTrue, + reason: + 'pubspec.lock must be committed so dependency resolution is ' + 'reproducible; the drift web assets are matched against it.', + ); + + final resolved = RegExp( + r'^ drift:\n(?:.*\n)*? version: "([^"]+)"', + multiLine: true, + ).firstMatch(lock.readAsStringSync())?.group(1); + expect(resolved, isNotNull, reason: 'drift not found in pubspec.lock'); + + final stamp = File('$root/web/drift_assets.version'); + expect( + stamp.existsSync(), + isTrue, + reason: + 'web/drift_assets.version is missing. It records which drift release ' + 'web/sqlite3.wasm and web/drift_worker.js came from.', + ); + + expect( + stamp.readAsStringSync().trim(), + resolved, + reason: + '\nDrift web assets are STALE.\n' + 'pubspec.lock resolves drift $resolved, but the shipped assets are ' + 'from ${stamp.readAsStringSync().trim()}.\n' + 'The web build would compile and then fail in the browser.\n\n' + 'Fix:\n' + ' gh release download drift-$resolved --repo simolus3/drift \\\n' + ' --pattern drift_worker.js --pattern sqlite3.wasm --clobber\n' + ' (run inside web/, then write $resolved into web/drift_assets.version)\n', + ); + + for (final name in ['sqlite3.wasm', 'drift_worker.js']) { + expect( + File('$root/web/$name').existsSync(), + isTrue, + reason: 'web/$name is missing; the web build would fail at runtime.', + ); + } + + // Cheap integrity check: the wasm must actually be WebAssembly. + final magic = File('$root/web/sqlite3.wasm').openSync().readSync(4); + expect( + magic, + orderedEquals([0x00, 0x61, 0x73, 0x6d]), + reason: + 'web/sqlite3.wasm does not start with the WebAssembly magic ' + 'bytes (\\0asm) — the download is corrupt or is not a wasm file.', + ); + }); +} diff --git a/web/drift_assets.version b/web/drift_assets.version new file mode 100644 index 0000000..b0c0861 --- /dev/null +++ b/web/drift_assets.version @@ -0,0 +1 @@ +2.34.2 From a2c7302aad9fc8811ac4dd4fd6ae2dc5b5f14b5f Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 15:24:43 -0400 Subject: [PATCH 32/60] feat(#335): migrate bulk data from SharedPreferences into drift Moves message history, contacts and discovered contacts out of the settings store. Settings stay in SharedPreferences, which is what it is for. Ordering is the whole safety argument: WRITE, VERIFY BY READING BACK, and only then remove the source. #333 was a storage path that chose a key silently and made 566 real messages read as empty; deleting before verifying would make that class of mistake permanent instead of cosmetic. On any failure the source is left intact and the error is logged - never a silent drop (SAFELANE 6). Idempotent by construction: a key already present in drift is not overwritten, so re-running is a no-op. If an older build re-writes a migrated key into prefs, the migrated copy wins and the stale prefs copy is discarded rather than promoted. Rehearsed against a COPY of a real 7 MB store, as the plan required before touching live data: REHEARSAL: 69 migrated, 0 failed, 5.21 MB, 69 bulk keys expected Every key checked for exact length, confirmed removed from prefs, and every settings key confirmed untouched. The live store was never opened. Two things the tests caught that review would not have: 1. getString THROWS on a non-string value rather than returning null, so a non-string under a bulk prefix was counted as a migration FAILURE. It now type-checks with prefs.get() and skips. Alarming falsely is its own bug. 2. The `contacts` prefix was checked against the real store rather than assumed: it matches only the 6 bulk contact blobs, and correctly does NOT match contact_unread_count*. Not yet wired into app startup - that is the switchover, and it is deliberately a separate commit so this can be reviewed on its own. flutter analyze clean, dart format clean, 504 tests pass. --- lib/storage/drift/blob_store.dart | 166 ++++++++++++++++++++ test/storage/blob_store_migration_test.dart | 118 ++++++++++++++ test/storage/real_store_rehearsal_test.dart | 91 +++++++++++ 3 files changed, 375 insertions(+) create mode 100644 lib/storage/drift/blob_store.dart create mode 100644 test/storage/blob_store_migration_test.dart create mode 100644 test/storage/real_store_rehearsal_test.dart diff --git a/lib/storage/drift/blob_store.dart b/lib/storage/drift/blob_store.dart new file mode 100644 index 0000000..d92f73f --- /dev/null +++ b/lib/storage/drift/blob_store.dart @@ -0,0 +1,166 @@ +import '../../utils/app_logger.dart'; +import '../prefs_manager.dart'; +import 'offband_database.dart'; + +/// Bulk-data store backed by drift, replacing SharedPreferences for anything +/// large (#335). +/// +/// Why this exists: SharedPreferences is a settings store. On Windows every +/// mutation re-encodes and rewrites the WHOLE file synchronously, and on web it +/// is backed by `localStorage`, which is capped at 5 MiB per origin and is also +/// synchronous. This install holds ~5.2 MB of bulk data, so the web build +/// cannot function at all today and Windows stalls for tens of seconds (#306). +/// +/// Each key becomes one row, so a write touches one row rather than the entire +/// store. +class BlobStore { + BlobStore(this._db); + + final OffbandDatabase _db; + + static OffbandDatabase? _sharedDb; + + /// Process-wide instance. The database must be opened once; opening it twice + /// is an error on the web backends. + static BlobStore get instance => BlobStore(_sharedDb ??= OffbandDatabase()); + + /// Key families that hold bulk data. Everything else stays in + /// SharedPreferences, which is what it is for. + static const List migratedPrefixes = [ + 'channel_messages_', + 'messages_', + 'contacts', + 'discovered_contacts', + ]; + + static bool isBulkKey(String key) => migratedPrefixes.any(key.startsWith); + + Future read(String key) async { + final row = await (_db.select( + _db.storedBlobs, + )..where((t) => t.key.equals(key))).getSingleOrNull(); + return row?.value; + } + + Future write(String key, String value) async { + await _db + .into(_db.storedBlobs) + .insertOnConflictUpdate( + StoredBlobsCompanion.insert(key: key, value: value), + ); + } + + Future delete(String key) async { + await (_db.delete(_db.storedBlobs)..where((t) => t.key.equals(key))).go(); + } + + /// Moves bulk keys out of SharedPreferences into drift. + /// + /// Ordering is deliberate and non-negotiable: **write, verify by reading + /// back, and only then remove the source.** #333 was caused by a storage path + /// that chose a key silently and made 566 real messages read as empty; a + /// migration that deleted before verifying could do that permanently rather + /// than cosmetically. + /// + /// Idempotent: keys already migrated are skipped, so re-running is a no-op + /// rather than a duplicate or an overwrite of newer data. + /// + /// Every outcome is logged (SAFELANE 6). A failure never silently drops a + /// key: the source is left intact and the error surfaces. + Future migrateFromPrefs() async { + final prefs = PrefsManager.instance; + final report = MigrationReport(); + + final bulkKeys = prefs.getKeys().where(isBulkKey).toList(); + if (bulkKeys.isEmpty) { + appLogger.info( + 'Blob migration: nothing to migrate (already done or fresh install)', + tag: 'Storage', + ); + return report; + } + + appLogger.info( + 'Blob migration: ${bulkKeys.length} key(s) to move out of prefs', + tag: 'Storage', + ); + + for (final key in bulkKeys) { + try { + // Type-check rather than calling getString directly: getString THROWS + // on a non-string value, which would be counted as a migration failure + // and cry wolf. A non-string under a bulk prefix is simply not bulk + // data. + final raw = prefs.get(key); + if (raw is! String || raw.isEmpty) { + report.skipped++; + continue; + } + final source = raw; + + if (await read(key) != null) { + // Already migrated on a previous run. Leave the prefs copy for the + // sweep below rather than assuming; the read-back proved the data is + // present in drift. + report.alreadyPresent++; + await prefs.remove(key); + continue; + } + + await write(key, source); + + // Verify BEFORE removing the source. Length is compared rather than + // full equality to keep a multi-MB comparison cheap while still + // catching truncation, which is the realistic corruption here. + final readBack = await read(key); + if (readBack == null || readBack.length != source.length) { + report.failed++; + appLogger.error( + 'Blob migration FAILED for $key: wrote ${source.length} chars, ' + 'read back ${readBack?.length ?? "null"}. Source left intact.', + tag: 'Storage', + ); + continue; + } + + await prefs.remove(key); + report.migrated++; + report.bytes += source.length; + } catch (e) { + report.failed++; + appLogger.error( + 'Blob migration FAILED for $key: $e. Source left intact.', + tag: 'Storage', + ); + } + } + + final level = report.failed > 0 ? 'WITH FAILURES' : 'ok'; + appLogger.info( + 'Blob migration complete ($level): ${report.migrated} moved, ' + '${report.alreadyPresent} already present, ${report.skipped} skipped, ' + '${report.failed} failed, ' + '${(report.bytes / 1024 / 1024).toStringAsFixed(2)} MB', + tag: 'Storage', + ); + if (report.failed > 0) { + appLogger.error( + 'Blob migration left ${report.failed} key(s) in SharedPreferences. ' + 'No data was lost, but those keys still carry the old cost.', + tag: 'Storage', + ); + } + return report; + } +} + +/// Mutable tally of a migration run; surfaced in the log and used by tests. +class MigrationReport { + int migrated = 0; + int alreadyPresent = 0; + int skipped = 0; + int failed = 0; + int bytes = 0; + + bool get hadFailures => failed > 0; +} diff --git a/test/storage/blob_store_migration_test.dart b/test/storage/blob_store_migration_test.dart new file mode 100644 index 0000000..4d96333 --- /dev/null +++ b/test/storage/blob_store_migration_test.dart @@ -0,0 +1,118 @@ +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:meshcore_open/storage/drift/blob_store.dart'; +import 'package:meshcore_open/storage/drift/offband_database.dart'; +import 'package:meshcore_open/storage/prefs_manager.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +/// Migration safety for #335. +/// +/// The bar here is set by #333: a storage path chose a key silently and 566 +/// real messages read as empty. A migration that gets this wrong loses data +/// permanently rather than cosmetically, so the failure paths are tested, not +/// just the happy one. +void main() { + late OffbandDatabase db; + late BlobStore store; + + setUp(() async { + db = OffbandDatabase(NativeDatabase.memory()); + store = BlobStore(db); + PrefsManager.reset(); + }); + + tearDown(() => db.close()); + + Future seedPrefs(Map values) async { + SharedPreferences.setMockInitialValues(values); + await PrefsManager.initialize(); + } + + test('moves bulk keys and removes them from prefs', () async { + await seedPrefs({ + 'channel_messages_devpsk_abc': '[{"m":1}]', + 'messages_devcontact': '[{"m":2}]', + 'contacts_dev': '[{"c":1}]', + 'discovered_contacts': '[{"d":1}]', + }); + + final report = await store.migrateFromPrefs(); + + expect(report.migrated, 4); + expect(report.failed, 0); + expect(await store.read('channel_messages_devpsk_abc'), '[{"m":1}]'); + expect(await store.read('discovered_contacts'), '[{"d":1}]'); + + final prefs = PrefsManager.instance; + expect(prefs.getString('channel_messages_devpsk_abc'), isNull); + expect(prefs.getString('discovered_contacts'), isNull); + }); + + test('leaves settings alone', () async { + await seedPrefs({ + 'ui_channels_sort_option': 'manual', + 'app_settings': '{"theme":"dark"}', + 'contacts_dev': '[{"c":1}]', + }); + + final report = await store.migrateFromPrefs(); + + expect(report.migrated, 1, reason: 'only the bulk key should move'); + final prefs = PrefsManager.instance; + expect(prefs.getString('ui_channels_sort_option'), 'manual'); + expect(prefs.getString('app_settings'), '{"theme":"dark"}'); + }); + + test('is idempotent: a second run moves nothing and loses nothing', () async { + await seedPrefs({'contacts_dev': '[{"c":1}]'}); + + final first = await store.migrateFromPrefs(); + expect(first.migrated, 1); + + final second = await store.migrateFromPrefs(); + expect(second.migrated, 0); + expect(second.failed, 0); + expect(await store.read('contacts_dev'), '[{"c":1}]'); + }); + + test('a re-added prefs key does not clobber migrated data', () async { + // Simulates an older build writing the key again after migration. The + // migrated copy must win; the stale prefs copy is discarded, not promoted. + await seedPrefs({'contacts_dev': '[{"c":"new"}]'}); + await store.write('contacts_dev', '[{"c":"migrated"}]'); + + final report = await store.migrateFromPrefs(); + + expect(report.alreadyPresent, 1); + expect(await store.read('contacts_dev'), '[{"c":"migrated"}]'); + expect(PrefsManager.instance.getString('contacts_dev'), isNull); + }); + + test('preserves a payload larger than the 5 MiB localStorage cap', () async { + // 4 chars per element, so >1.4M elements clears 5 MiB. + final big = '[${'"x",' * 1400000}"end"]'; + expect(big.length, greaterThan(5 * 1024 * 1024)); + await seedPrefs({'channel_messages_devpsk_big': big}); + + final report = await store.migrateFromPrefs(); + + expect(report.failed, 0); + expect( + (await store.read('channel_messages_devpsk_big'))!.length, + big.length, + ); + }); + + test('non-string values are skipped, not failed', () async { + // getString THROWS on a non-string, so this must be type-checked, not + // caught as a failure. Verified against the real store: 'contacts' only + // ever matches bulk blobs there, but the store must not mis-report if a + // non-string ever lands under a bulk prefix. + await seedPrefs({'contacts_probe': 42}); + + final report = await store.migrateFromPrefs(); + + expect(report.failed, 0); + expect(report.skipped, 1); + }); +} diff --git a/test/storage/real_store_rehearsal_test.dart b/test/storage/real_store_rehearsal_test.dart new file mode 100644 index 0000000..678aab8 --- /dev/null +++ b/test/storage/real_store_rehearsal_test.dart @@ -0,0 +1,91 @@ +@Tags(['rehearsal']) +library; + +import 'dart:convert'; +import 'dart:io'; + +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:meshcore_open/storage/drift/blob_store.dart'; +import 'package:meshcore_open/storage/drift/offband_database.dart'; +import 'package:meshcore_open/storage/prefs_manager.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +/// Rehearses the #335 migration against a COPY of a real 7 MB store. +/// +/// Required by the plan before the migration may touch live data. Reads a +/// snapshot from disk; it never opens the user's actual store. Skipped when +/// the snapshot is absent, so CI does not depend on one machine's data. +void main() { + final path = Platform.environment['OFFBAND_REAL_STORE']; + + test( + 'migrates a real 7 MB store with zero loss', + () async { + if (path == null || !File(path).existsSync()) { + markTestSkipped( + 'set OFFBAND_REAL_STORE to a shared_preferences.json copy', + ); + return; + } + + final raw = + jsonDecode(File(path).readAsStringSync()) as Map; + // Strip the `flutter.` prefix the plugin adds on disk. + final seed = { + for (final e in raw.entries) + if (e.value is String || e.value is int || e.value is bool) + e.key.replaceFirst('flutter.', ''): e.value as Object, + }; + + final expected = { + for (final e in seed.entries) + if (e.value is String && BlobStore.isBulkKey(e.key)) + e.key: (e.value as String).length, + }; + + SharedPreferences.setMockInitialValues(seed); + PrefsManager.reset(); + await PrefsManager.initialize(); + + final db = OffbandDatabase(NativeDatabase.memory()); + final store = BlobStore(db); + final report = await store.migrateFromPrefs(); + + // ignore: avoid_print + print( + 'REHEARSAL: ${report.migrated} migrated, ${report.failed} failed, ' + '${(report.bytes / 1024 / 1024).toStringAsFixed(2)} MB, ' + '${expected.length} bulk keys expected', + ); + + expect(report.failed, 0, reason: 'no key may fail on real data'); + expect(report.migrated, expected.length); + + // Every byte accounted for, per key. + for (final e in expected.entries) { + final got = await store.read(e.key); + expect(got, isNotNull, reason: '${e.key} missing after migration'); + expect(got!.length, e.value, reason: '${e.key} changed length'); + expect( + PrefsManager.instance.get(e.key), + isNull, + reason: '${e.key} should be gone from prefs', + ); + } + + // Settings must survive untouched. + final settings = seed.keys.where((k) => !BlobStore.isBulkKey(k)); + for (final k in settings) { + expect( + PrefsManager.instance.get(k), + isNotNull, + reason: 'setting $k was wrongly removed', + ); + } + + await db.close(); + }, + timeout: const Timeout(Duration(minutes: 5)), + ); +} From dd67391a0e8afd1ce298094f6fd710a662ac4f23 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 15:41:34 -0400 Subject: [PATCH 33/60] feat(#335): switch bulk stores over to drift Wires the migration into startup and points the four bulk stores at drift. SharedPreferences keeps the settings, which is what it is for. Startup runs the migration after prefs are up and BEFORE any store reads, and awaits it. Letting stores race a half-finished migration is precisely the shape of #333, where a storage path silently chose the wrong key and 566 real messages read as empty. Converted: message_store, channel_message_store, contact_store, contact_discovery_store. All four now have ZERO prefs get/set for bulk data. Three safety properties, deliberately built in: 1. readWithPrefsFallback - if a key is somehow not in drift, the prefs copy is still served rather than reading as empty. It logs a WARNING when it fires, because a fallback during normal operation means the migration is incomplete and someone needs to know. Silence here is what made #333 look like data loss. 2. deleteEverywhere / keysWithPrefix span BOTH backends. A clear that only removed the drift row would leave a pre-migration prefs copy to reappear through the fallback, resurrecting deleted history. 3. The legacy-key migrations inside the stores now check both backends and only touch prefs when a legacy key actually exists. This also carries the #306 fix into this branch: the unconditional prefs.remove was still present here, since this branched from dev rather than from the #306 work. Verified: analyze clean, format clean, 504 tests pass, Windows and web both build. Migration rehearsed earlier against a copy of a real 7 MB store: 69 keys, 5.21 MB, zero failures. NOT yet run against live data - that happens on first launch of this build, and the owner should have a prefs backup before that. --- lib/main.dart | 9 +++++ lib/storage/channel_message_store.dart | 35 ++++++++++-------- lib/storage/contact_discovery_store.dart | 18 ++++++--- lib/storage/contact_store.dart | 32 +++++++++------- lib/storage/drift/blob_store.dart | 47 ++++++++++++++++++++++++ lib/storage/message_store.dart | 36 +++++++++++------- 6 files changed, 128 insertions(+), 49 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 52f3a6f..4e0c17e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -27,6 +27,7 @@ import 'services/ui_view_state_service.dart'; import 'services/timeout_prediction_service.dart'; import 'services/observer_config_service.dart'; import 'services/block_service.dart'; +import 'storage/drift/blob_store.dart'; import 'storage/prefs_manager.dart'; import 'utils/app_logger.dart'; @@ -36,6 +37,14 @@ void main() async { // Initialize SharedPreferences cache await PrefsManager.initialize(); + // Move bulk data (message history, contacts) out of SharedPreferences into + // drift (#335). Must run after prefs are up and BEFORE any store reads, so + // no code sees a half-migrated state. Idempotent: a no-op once done. + // + // Deliberately awaited: the alternative is stores racing the migration, and + // this is the failure mode that produced #333. + await BlobStore.instance.migrateFromPrefs(); + // Start always-on file logging (#97); no-op on web. await FileLogService.instance.init(); diff --git a/lib/storage/channel_message_store.dart b/lib/storage/channel_message_store.dart index dfef8c3..39507fe 100644 --- a/lib/storage/channel_message_store.dart +++ b/lib/storage/channel_message_store.dart @@ -5,7 +5,7 @@ import 'package:meshcore_open/utils/app_logger.dart'; import '../models/channel_message.dart'; import '../models/translation_support.dart'; import '../helpers/smaz.dart'; -import 'prefs_manager.dart'; +import 'drift/blob_store.dart'; class ChannelMessageStore { static const String _keyPrefix = 'channel_messages_'; @@ -51,9 +51,11 @@ class ChannelMessageStore { ); return; } - final prefs = PrefsManager.instance; final jsonList = messages.map((msg) => _messageToJson(msg)).toList(); - await prefs.setString(_storageKey(channelIndex), jsonEncode(jsonList)); + await BlobStore.instance.write( + _storageKey(channelIndex), + jsonEncode(jsonList), + ); } /// Load messages for a specific channel @@ -64,9 +66,11 @@ class ChannelMessageStore { ); return []; } - final prefs = PrefsManager.instance; + final blobs = BlobStore.instance; final key = _storageKey(channelIndex); - String? jsonString = prefs.getString(key); + // Bulk data lives in drift (#335); the fallback covers an unmigrated key + // and logs loudly if it fires. + String? jsonString = await blobs.readWithPrefsFallback(key); // One-time migration into the PSK-identity key. Only runs when the PSK is // known (key != index key). Adopts pre-#194 history keyed by slot index — @@ -79,13 +83,15 @@ class ChannelMessageStore { _indexKey(channelIndex), '$_keyPrefix$channelIndex', // pre-device-scoping, unscoped index key ]) { - final legacy = prefs.getString(legacyKey); + // Legacy keys may sit in either backend depending on when this + // install last ran, so check both. + final legacy = await blobs.readWithPrefsFallback(legacyKey); if (legacy != null && legacy.isNotEmpty) { appLogger.info( 'Migrating channel messages $legacyKey -> $key (PSK-keyed, #194)', ); - await prefs.setString(key, legacy); - await prefs.remove(legacyKey); + await blobs.write(key, legacy); + await blobs.deleteEverywhere(legacyKey); jsonString = legacy; break; } @@ -108,17 +114,16 @@ class ChannelMessageStore { /// history gone, and setChannel's reuse-clear (#193) needs any stale /// slot-index history gone so it can't be migrated onto the new occupant. Future clearChannelMessages(int channelIndex) async { - final prefs = PrefsManager.instance; - await prefs.remove(_storageKey(channelIndex)); - await prefs.remove(_indexKey(channelIndex)); + final blobs = BlobStore.instance; + await blobs.deleteEverywhere(_storageKey(channelIndex)); + await blobs.deleteEverywhere(_indexKey(channelIndex)); } /// Clear all channel messages Future clearAllChannelMessages() async { - final prefs = PrefsManager.instance; - final keys = prefs.getKeys().where((k) => k.startsWith(keyFor)).toList(); - for (var key in keys) { - await prefs.remove(key); + final blobs = BlobStore.instance; + for (final key in await blobs.keysWithPrefix(keyFor)) { + await blobs.deleteEverywhere(key); } } diff --git a/lib/storage/contact_discovery_store.dart b/lib/storage/contact_discovery_store.dart index 3f6f171..7622fdb 100644 --- a/lib/storage/contact_discovery_store.dart +++ b/lib/storage/contact_discovery_store.dart @@ -2,14 +2,16 @@ import 'dart:convert'; import 'dart:typed_data'; import '../models/contact.dart'; -import 'prefs_manager.dart'; +import '../utils/app_logger.dart'; +import 'drift/blob_store.dart'; class ContactDiscoveryStore { static const String _keyPrefix = 'discovered_contacts'; Future> loadContacts() async { - final prefs = PrefsManager.instance; - final jsonStr = prefs.getString(_keyPrefix); + // Bulk data lives in drift (#335), not SharedPreferences. The fallback + // covers a key the migration has not moved yet and logs loudly if it fires. + final jsonStr = await BlobStore.instance.readWithPrefsFallback(_keyPrefix); if (jsonStr == null) return []; try { @@ -17,15 +19,19 @@ class ContactDiscoveryStore { return jsonList .map((entry) => _fromJson(entry as Map)) .toList(); - } catch (_) { + } catch (e) { + // SAFELANE 6: a decode failure is not "no data". + appLogger.error( + 'Failed to decode discovered contacts: $e', + tag: 'Storage', + ); return []; } } Future saveContacts(List contacts) async { - final prefs = PrefsManager.instance; final jsonList = contacts.map(_toJson).toList(); - await prefs.setString(_keyPrefix, jsonEncode(jsonList)); + await BlobStore.instance.write(_keyPrefix, jsonEncode(jsonList)); } Map _toJson(Contact contact) { diff --git a/lib/storage/contact_store.dart b/lib/storage/contact_store.dart index 5d1805d..d907752 100644 --- a/lib/storage/contact_store.dart +++ b/lib/storage/contact_store.dart @@ -3,6 +3,7 @@ import 'dart:typed_data'; import '../models/contact.dart'; import '../utils/app_logger.dart'; +import 'drift/blob_store.dart'; import 'prefs_manager.dart'; class ContactStore { @@ -19,23 +20,27 @@ class ContactStore { appLogger.warn('Public key hex is not set. Cannot load contacts.'); return []; } - final prefs = PrefsManager.instance; - String? jsonString = prefs.getString(keyFor); + // Bulk data lives in drift (#335). The fallback covers a key the migration + // has not moved yet, and logs loudly if it fires. + final blobs = BlobStore.instance; + String? jsonString = await blobs.readWithPrefsFallback(keyFor); + if (jsonString == null || jsonString.isEmpty) { - // Attempt migration from legacy unscoped key on first load - final legacyJsonString = prefs.getString(_keyPrefix); - prefs.remove(_keyPrefix); - if (legacyJsonString != null && legacyJsonString.isNotEmpty) { + // Pre-device-scoping data still sits under the legacy unscoped key in + // SharedPreferences. Only touch prefs when it actually exists: an + // unconditional remove costs a full-file rewrite on Windows and a + // 5 MiB-capped synchronous write on web (#306). + final prefs = PrefsManager.instance; + final legacy = prefs.get(_keyPrefix); + if (legacy is String && legacy.isNotEmpty) { appLogger.info( - 'Migrating contacts from legacy key $_keyPrefix to scoped key $keyFor', + 'Migrating contacts from legacy key $_keyPrefix to $keyFor (drift)', ); - await prefs.setString(keyFor, legacyJsonString); - jsonString = legacyJsonString; + await blobs.write(keyFor, legacy); + await prefs.remove(_keyPrefix); + jsonString = legacy; } } - if (jsonString == null || jsonString.isEmpty) { - jsonString = prefs.getString(keyFor); - } if (jsonString == null || jsonString.isEmpty) { return []; } @@ -55,9 +60,8 @@ class ContactStore { appLogger.warn('Public key hex is not set. Cannot save contacts.'); return; } - final prefs = PrefsManager.instance; final jsonList = contacts.map(_toJson).toList(); - await prefs.setString(keyFor, jsonEncode(jsonList)); + await BlobStore.instance.write(keyFor, jsonEncode(jsonList)); } Map _toJson(Contact contact) { diff --git a/lib/storage/drift/blob_store.dart b/lib/storage/drift/blob_store.dart index d92f73f..6cab09b 100644 --- a/lib/storage/drift/blob_store.dart +++ b/lib/storage/drift/blob_store.dart @@ -35,6 +35,29 @@ class BlobStore { static bool isBulkKey(String key) => migratedPrefixes.any(key.startsWith); + /// Reads a bulk key, falling back to SharedPreferences if drift does not + /// have it. + /// + /// Belt and braces for the switchover: if migration ever missed a key, the + /// data must still be reachable rather than silently reading as empty, which + /// is exactly how #333 presented. The fallback is LOUD, because a fallback + /// that fires in normal operation means the migration is incomplete and + /// somebody needs to know. + Future readWithPrefsFallback(String key) async { + final fromDrift = await read(key); + if (fromDrift != null) return fromDrift; + + final raw = PrefsManager.instance.get(key); + if (raw is! String || raw.isEmpty) return null; + + appLogger.warn( + 'Blob read for $key fell back to SharedPreferences: it is NOT in drift. ' + 'Migration is incomplete for this key; serving the prefs copy.', + tag: 'Storage', + ); + return raw; + } + Future read(String key) async { final row = await (_db.select( _db.storedBlobs, @@ -50,6 +73,30 @@ class BlobStore { ); } + /// Keys beginning with [prefix], across BOTH backends. + /// + /// Callers that clear a family of keys must see prefs-resident keys too, or + /// a pre-migration copy survives the clear and reappears through the read + /// fallback. + Future> keysWithPrefix(String prefix) async { + // Filtered in Dart rather than SQL: the table holds tens of rows, so the + // cost is irrelevant and it avoids depending on a version-specific LIKE + // API for a pinned dependency. + final rows = await _db.select(_db.storedBlobs).get(); + + return { + ...rows.map((r) => r.key).where((k) => k.startsWith(prefix)), + ...PrefsManager.instance.getKeys().where((k) => k.startsWith(prefix)), + }.toList(); + } + + /// Removes a key from BOTH backends, so a clear cannot be undone by a + /// leftover prefs copy surfacing through the fallback. + Future deleteEverywhere(String key) async { + await delete(key); + await PrefsManager.instance.remove(key); + } + Future delete(String key) async { await (_db.delete(_db.storedBlobs)..where((t) => t.key.equals(key))).go(); } diff --git a/lib/storage/message_store.dart b/lib/storage/message_store.dart index ccaf9ea..2d8b02d 100644 --- a/lib/storage/message_store.dart +++ b/lib/storage/message_store.dart @@ -4,6 +4,7 @@ import '../models/message.dart'; import '../models/translation_support.dart'; import '../helpers/smaz.dart'; import '../utils/app_logger.dart'; +import 'drift/blob_store.dart'; import 'prefs_manager.dart'; class MessageStore { @@ -23,10 +24,9 @@ class MessageStore { appLogger.warn('Public key hex is not set. Cannot save messages.'); return; } - final prefs = PrefsManager.instance; final key = '$keyFor$contactKeyHex'; final jsonList = messages.map(_messageToJson).toList(); - await prefs.setString(key, jsonEncode(jsonList)); + await BlobStore.instance.write(key, jsonEncode(jsonList)); } Future> loadMessages(String contactKeyHex) async { @@ -34,24 +34,30 @@ class MessageStore { appLogger.warn('Public key hex is not set. Cannot load messages.'); return []; } - final prefs = PrefsManager.instance; final key = '$keyFor$contactKeyHex'; final oldKey = '$_keyPrefix$contactKeyHex'; - String? jsonString = prefs.getString(key); + // Bulk data lives in drift (#335); fallback covers an unmigrated key and + // logs loudly if it fires. + final blobs = BlobStore.instance; + String? jsonString = await blobs.readWithPrefsFallback(key); + if (jsonString == null || jsonString.isEmpty) { - // Attempt migration from legacy unscoped key on first load - final legacyJsonString = prefs.getString(oldKey); - prefs.remove(oldKey); - if (legacyJsonString != null && legacyJsonString.isNotEmpty) { + // Only touch prefs when the legacy key actually exists. An unconditional + // remove here ran once per contact and cost a full-file rewrite each + // time on Windows (#306). + final prefs = PrefsManager.instance; + final legacy = prefs.get(oldKey); + if (legacy is String && legacy.isNotEmpty) { appLogger.info( - 'Migrating messages from legacy key $oldKey to scoped key $key', + 'Migrating messages from legacy key $oldKey to $key (drift)', ); - await prefs.setString(key, legacyJsonString); - jsonString = legacyJsonString; + await blobs.write(key, legacy); + await prefs.remove(oldKey); + jsonString = legacy; } } if (jsonString == null || jsonString.isEmpty) { - jsonString = prefs.getString(keyFor); + jsonString = await blobs.readWithPrefsFallback(keyFor); } if (jsonString == null || jsonString.isEmpty) { return []; @@ -70,9 +76,11 @@ class MessageStore { appLogger.warn('Public key hex is not set. Cannot clear messages.'); return; } - final prefs = PrefsManager.instance; final key = '$keyFor$contactKeyHex'; - await prefs.remove(key); + // Clear both backends: drift is authoritative, but a pre-migration prefs + // copy must not survive a clear and reappear via the read fallback. + await BlobStore.instance.delete(key); + await PrefsManager.instance.remove(key); } Map _messageToJson(Message msg) { From 83c965ef44bfec7dcf96e46e2dffcbb06c9b909b Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 15:07:03 -0400 Subject: [PATCH 34/60] feat(#339): deploy web client to offband.app via Cloudflare Pages Publishes the Flutter web client. offband.org stays the Hugo marketing site in OffbandMesh/offband-site, a separate Pages project, untouched. push to main -> --branch=main -> production -> offband.app push to dev -> --branch=dev -> dev tier -> dev.offband.app Direct upload only. Cloudflare's dashboard "Connect to Git" must not be enabled for this project; it would create a second git-integrated project racing this workflow. Matches how offband-site deploys. Because direct-upload projects cannot configure production branch controls in the Cloudflare dashboard, the "never auto-deploy arbitrary branches to prod" requirement from #122 is enforced by the workflow's trigger list instead. That is the safety mechanism, and it is better placed here than in dashboard state: reviewable and version controlled. Builds via build_pipe rather than a plain `flutter build web`, because build_pipe appends ?v= to the bootstrap and manifest files. Without it, browsers serve stale bundles after a deploy. Two fixes folded in: - pubspec.yaml build_pipe build_command did not pass --dart-define-from-file, so a deployed site would have shipped a dead GIF picker even though #331 wired the key into build.yml. The deploy path and the CI path used different build commands. - Retired the inherited zjs81 Workers deploy: removed deploy.yml and wrangler.toml (which targeted THEIR account's "meshcore" project) and dropped the now-dead `deploy` script from package.json. A root wrangler.toml carrying Workers config can also interfere with `wrangler pages deploy`. Verified locally by running the real build with the bench toolchain: build_pipe accepts the new flag, cache busting is applied (flutter_bootstrap.js?v=e2019703...), output is 46 files with a 10.2 MB largest asset (Pages limits are 25 MiB per asset and 20000 files), and the Giphy key is confirmed compiled into main.dart.js. Not verifiable locally: whether the repo's CLOUDFLARE_API_TOKEN carries Pages:Edit scope. The workstation token is DNS-only and 403s on the Pages API. A 403 on first deploy would indicate wrong scope; it fails safely. Part of epic #312, plan #321 (Phase 4). --- .github/workflows/configure-web-domains.yml | 94 +++++++++++++++++ .github/workflows/deploy-web.yml | 111 ++++++++++++++++++++ .github/workflows/deploy.yml | 41 -------- package.json | 3 +- pubspec.yaml | 2 +- wrangler.toml | 7 -- 6 files changed, 207 insertions(+), 51 deletions(-) create mode 100644 .github/workflows/configure-web-domains.yml create mode 100644 .github/workflows/deploy-web.yml delete mode 100644 .github/workflows/deploy.yml delete mode 100644 wrangler.toml diff --git a/.github/workflows/configure-web-domains.yml b/.github/workflows/configure-web-domains.yml new file mode 100644 index 0000000..f0cb672 --- /dev/null +++ b/.github/workflows/configure-web-domains.yml @@ -0,0 +1,94 @@ +name: Configure offband.app domains + +# One-time (idempotent) setup: attaches custom domains to the offband-app +# Pages project and creates the proxied CNAME records. +# +# offband.app -> offband-app.pages.dev (production branch) +# dev.offband.app -> dev.offband-app.pages.dev (dev branch alias) +# +# The dev record deliberately targets the BRANCH ALIAS rather than the +# project root. Cloudflare's documented behaviour: a custom domain CNAME'd +# to .pages.dev always serves production, so pointing dev at +# dev..pages.dev is what makes it serve the dev branch. This only +# works with Cloudflare-managed DNS and a PROXIED record; an unproxied or +# external record silently falls through to production. +# +# Manual trigger only. Domains do not need reconfiguring on every deploy, +# and this touches DNS. +# +# Mirrors OffbandMesh/offband-site/.github/workflows/configure-domains.yml, +# which is the working reference for offband.org. +# +# NOTE: offband.org belongs to a DIFFERENT Pages project (offband-site) in +# the same Cloudflare account. This workflow only ever touches the +# offband.app zone. See #339. + +on: + workflow_dispatch: + +permissions: + contents: read + +jobs: + domains: + runs-on: ubuntu-latest + env: + API: https://api.cloudflare.com/client/v4 + ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + PAGES_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + DNS_TOKEN: ${{ secrets.CLOUDFLARE_DNS_ZONE_TOKEN }} + PROJECT: offband-app + ZONE: offband.app + steps: + - name: Attach custom domains and create DNS records + run: | + set -euo pipefail + + ZID=$(curl -s -H "Authorization: Bearer $DNS_TOKEN" \ + "$API/zones?name=$ZONE" | jq -r '.result[0].id // empty') + if [ -z "$ZID" ]; then + echo "::error::Could not resolve zone id for $ZONE. Check CLOUDFLARE_DNS_ZONE_TOKEN has Zone:Read." + exit 1 + fi + echo "zone $ZONE -> ${ZID:0:6}…" + + # name -> CNAME target. Apex serves production; dev serves the + # dev branch alias. Cloudflare flattens the apex CNAME. + attach() { + NAME="$1"; TARGET="$2" + echo "── $NAME -> $TARGET ──" + + REC=$(curl -s -H "Authorization: Bearer $DNS_TOKEN" \ + "$API/zones/$ZID/dns_records?type=CNAME&name=$NAME" \ + | jq -r '.result[0].id // empty') + BODY=$(jq -n --arg n "$NAME" --arg c "$TARGET" \ + '{type:"CNAME",name:$n,content:$c,proxied:true}') + + if [ -n "$REC" ]; then + curl -s -X PUT -H "Authorization: Bearer $DNS_TOKEN" \ + -H "Content-Type: application/json" \ + "$API/zones/$ZID/dns_records/$REC" --data "$BODY" \ + | jq -r 'if .success then " DNS: updated" else " DNS ERR: " + (.errors|tostring) end' + else + curl -s -X POST -H "Authorization: Bearer $DNS_TOKEN" \ + -H "Content-Type: application/json" \ + "$API/zones/$ZID/dns_records" --data "$BODY" \ + | jq -r 'if .success then " DNS: created" else " DNS ERR: " + (.errors|tostring) end' + fi + + curl -s -X POST -H "Authorization: Bearer $PAGES_TOKEN" \ + -H "Content-Type: application/json" \ + "$API/accounts/$ACCOUNT_ID/pages/projects/$PROJECT/domains" \ + --data "$(jq -n --arg n "$NAME" '{name:$n}')" \ + | jq -r 'if .success then " Pages: attached (" + (.result.status // "pending") + ")" else " Pages: " + (.errors|tostring) end' + } + + attach "$ZONE" "${PROJECT}.pages.dev" + attach "dev.$ZONE" "dev.${PROJECT}.pages.dev" + + curl -s -X PATCH -H "Authorization: Bearer $DNS_TOKEN" \ + -H "Content-Type: application/json" \ + "$API/zones/$ZID/settings/always_use_https" --data '{"value":"on"}' \ + | jq -r 'if .success then "AlwaysHTTPS: on" else "AlwaysHTTPS (skip): " + (.errors|tostring) end' + + echo "configure-web-domains complete" diff --git a/.github/workflows/deploy-web.yml b/.github/workflows/deploy-web.yml new file mode 100644 index 0000000..21a5c8a --- /dev/null +++ b/.github/workflows/deploy-web.yml @@ -0,0 +1,111 @@ +name: Deploy web (Cloudflare Pages) + +# Publishes the Flutter web client to Cloudflare Pages. +# +# push to main -> --branch=main -> PRODUCTION -> offband.app +# push to dev -> --branch=dev -> dev tier -> dev.offband.app +# +# WHY THE TRIGGER LIST IS THE SAFETY MECHANISM: +# Direct-upload Pages projects cannot configure production branch controls +# in the Cloudflare dashboard, so the "never auto-deploy arbitrary branches +# to prod" requirement (#122) is enforced HERE, by the trigger scope. Adding +# a branch to `on.push.branches` is what grants it deploy rights. Do not add +# one casually, and never add `pull_request`. +# +# DO NOT enable Cloudflare's dashboard "Connect to Git" for this project. It +# creates a second, git-integrated project that races with this workflow. +# Deploys are Actions direct-upload only, matching offband-site. +# +# The marketing site (offband.org) is a SEPARATE Pages project in the same +# account, deployed from OffbandMesh/offband-site. Nothing here touches it. +# +# See #339, epic #312. + +on: + push: + branches: + - main + - dev + workflow_dispatch: + +concurrency: + # Serialise per target so two pushes cannot race the same environment, + # but let dev and main deploy independently. + group: deploy-web-${{ github.ref_name }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # Pinned to the bench toolchain, same as every other workflow (#331). + - uses: subosito/flutter-action@v2 + with: + flutter-version: "3.44.1" + cache: true + + - run: flutter pub get + + # Same mechanism as build.yml (#331). build_pipe's build_command in + # pubspec.yaml passes --dart-define-from-file, so this file must exist + # before the build runs or the GIF picker ships dead. + - name: Write dart_defines.json + env: + GIPHY_API_KEY: ${{ secrets.GIPHY_API_KEY }} + run: jq -n --arg k "$GIPHY_API_KEY" '{GIPHY_API_KEY:$k}' > dart_defines.json + + # build_pipe, NOT a plain `flutter build web`. It appends ?v= to the bootstrap/JS files, which is what stops browsers + # serving stale code after a deploy. A plain flutter build has no cache + # busting and would leave users on old bundles. + - name: Build web (versioned, cache-busted) + run: dart run build_pipe:build + + - name: Verify build output + run: | + set -eu + test -f build/web/index.html || { echo "::error::build/web/index.html missing"; exit 1; } + COUNT=$(find build/web -type f | wc -l) + BIGGEST=$(find build/web -type f -printf '%s\n' | sort -rn | head -1) + echo "files: ${COUNT}, largest: $((BIGGEST / 1048576)) MB" + # Cloudflare Pages hard limits: 25 MiB per asset, 20000 files. + # Fail here with a clear message rather than mid-upload. + if [ "${BIGGEST}" -gt 26214400 ]; then + echo "::error::An asset exceeds the Cloudflare Pages 25 MiB limit." + find build/web -type f -size +25M -printf ' %s %p\n' + exit 1 + fi + if [ "${COUNT}" -gt 20000 ]; then + echo "::error::More than 20000 files; exceeds the Cloudflare Pages limit." + exit 1 + fi + grep -q 'v=' build/web/index.html \ + && echo "cache-busting query params present" \ + || echo "::warning::no ?v= found in index.html; check build_pipe config" + + - name: Deploy to Cloudflare Pages + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + run: | + set -eu + npx --yes wrangler@3 pages project create offband-app \ + --production-branch=main \ + || echo "project already exists, continuing" + npx --yes wrangler@3 pages deploy build/web \ + --project-name=offband-app \ + --branch="${GITHUB_REF_NAME}" \ + --commit-dirty=true + + - name: Report target + run: | + if [ "${GITHUB_REF_NAME}" = "main" ]; then + echo "Deployed PRODUCTION -> https://offband.app" + else + echo "Deployed dev tier -> https://dev.offband.app" + fi diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 42ed701..0000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Deploy to Cloudflare Workers - -# Manual-only. This workflow was inherited from upstream zjs81 and deploys the web -# build to *its* Cloudflare account; this fork has no Cloudflare secrets, so the old -# `push: tags: ['*']` trigger failed on every tag (missing CLOUDFLARE_API_TOKEN). -# Disabled the auto-trigger to stop the per-tag failures (chore #120). The job is -# preserved for the deliberate Offband web-app launch (Feature #121 / Epic #122), -# which will define the proper trigger scope + secrets. -on: - workflow_dispatch: - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Setup Flutter - uses: subosito/flutter-action@v2 - with: - # Pinned to the bench toolchain (Dart 3.12.1). Was 3.41.2 with a - # comment claiming local parity that had not been true for months. - flutter-version: "3.44.1" - - - name: Setup Bun - uses: oven-sh/setup-bun@v2 - with: - bun-version: latest - - - name: Get dependencies - run: flutter pub get - - - name: Build Web - run: bun run build - - - name: Deploy to Cloudflare - uses: cloudflare/wrangler-action@v3 - with: - apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - command: deploy diff --git a/package.json b/package.json index 1684721..1f818b0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,6 @@ { "name": "meshcore-open", "scripts": { - "build": "dart run build_pipe:build", - "deploy": "bun x wrangler deploy" + "build": "dart run build_pipe:build" } } diff --git a/pubspec.yaml b/pubspec.yaml index 0415a36..c458c79 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -163,7 +163,7 @@ build_pipe: platforms: web: build: - build_command: flutter build web --release --pwa-strategy=none + build_command: flutter build web --release --pwa-strategy=none --dart-define-from-file=dart_defines.json # Strongly recommended: disables the default service worker which often causes more cache headaches add_version_query_param: true # This is the key flag! It appends ?v= to bootstrap/JS files diff --git a/wrangler.toml b/wrangler.toml deleted file mode 100644 index f3c57d9..0000000 --- a/wrangler.toml +++ /dev/null @@ -1,7 +0,0 @@ -#:schema node_modules/wrangler/config-schema.json -name = "meshcore" -compatibility_date = "2025-10-08" - -[assets] -directory = "build/web" - From 8d60b00b1b8fb1025eafa7401c569e6d486170be Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 01:48:23 -0400 Subject: [PATCH 35/60] feat(#330): release-signed APK + AAB behind an approval-gated environment CI has only ever produced debug-signed Android artifacts, because build.gradle.kts:63-70 falls back to signingConfigs "debug" when key.properties is absent, which is exactly the CI condition (#111). Debug-signed builds cannot be installed over a Play install or over a properly signed GitHub release, so users hit "App not installed". Adds a separate workflow that signs with the real keystore. Security posture, given this repo is PUBLIC and the keystore is the one credential that cannot be replaced if leaked: - No pull_request trigger. A PR, including from a fork, must never run a job that can read these secrets. - Pinned to the release-signing Environment, which carries a required reviewer and only accepts main or v* tags. The signing secrets are scoped to that environment, NOT to the repo, so a workflow that omits the environment key cannot read them at all. - Keystore decoded to disk only for the build, removed in a step with if: always() so a failed build leaves nothing behind. - Secrets passed via env: and printf'd into a file, never placed on a command line where they would appear in process listings, and never echoed. Includes an apksigner check that fails the job if the built APK carries CN=Android Debug. A green build alone does not prove release signing, because the gradle fallback is silent; that check is what makes this verifiable rather than assumed. The keystore is PKCS12 despite its .jks extension, and PKCS12 cannot carry a key password distinct from the store password, so one secret correctly populates both fields. Part of epic #312, plan #321 (Phase 5). --- .github/workflows/release-signed.yml | 150 +++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 .github/workflows/release-signed.yml diff --git a/.github/workflows/release-signed.yml b/.github/workflows/release-signed.yml new file mode 100644 index 0000000..719401c --- /dev/null +++ b/.github/workflows/release-signed.yml @@ -0,0 +1,150 @@ +name: Release (signed) + +# Produces RELEASE-SIGNED Android artifacts using the project's real keystore. +# +# SECURITY: this repo is PUBLIC and this workflow has access to the signing +# keystore, which is the single most irreplaceable credential in the project. +# If it leaks, the app can never be updated again on any channel (GitHub, +# F-Droid, Play all require a matching signature). +# +# Therefore: +# - NEVER add a `pull_request` trigger here. A PR (including from a fork) +# must never be able to run a job that can read these secrets. +# - The job is pinned to the `release-signing` Environment, which carries a +# required reviewer, so a signing run cannot proceed unattended. +# - The keystore is written to disk only for the duration of the build and +# deleted in a step that runs even when the build fails. +# - Signing values are passed via `env:`, never interpolated into a command +# line (where they would be visible in process listings) and never echoed. +# +# Debug-signed artifacts for day-to-day PR testing continue to come from +# build.yml and are unaffected by this workflow. See #330, epic #312. + +on: + push: + branches: + - main + tags: + - "v*" + workflow_dispatch: + +concurrency: + group: release-signed-${{ github.ref }} + cancel-in-progress: false + +permissions: + contents: read + +jobs: + android-signed: + runs-on: ubuntu-latest + # Required-reviewer gate. Do not remove. + environment: release-signing + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-java@v4 + with: + distribution: "temurin" + java-version: "17" + + - uses: subosito/flutter-action@v2 + with: + channel: "stable" + cache: true + + - name: Cache Gradle + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('android/gradle/wrapper/gradle-wrapper.properties', 'android/build.gradle', 'android/settings.gradle', 'android/app/build.gradle', 'pubspec.lock') }} + restore-keys: | + ${{ runner.os }}-gradle- + + # The keystore is PKCS12 (despite the .jks extension), which does not + # support a key password distinct from the store password. One secret + # therefore correctly populates both fields. See #330. + - name: Decode signing keystore + env: + KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + run: | + set -eu + if [ -z "${KEYSTORE_BASE64}" ] || [ -z "${KEYSTORE_PASSWORD}" ]; then + echo "::error::Signing secrets are not available to this run." + exit 1 + fi + printf '%s' "${KEYSTORE_BASE64}" | base64 -d > android/app/release.jks + # Fail loudly if the decode produced something implausible rather + # than letting Gradle fall back to debug signing silently. + SIZE=$(stat -c%s android/app/release.jks) + if [ "${SIZE}" -lt 1000 ]; then + echo "::error::Decoded keystore is ${SIZE} bytes; expected ~2.8 KB. Secret is malformed." + exit 1 + fi + umask 077 + cat > android/key.properties <<'PROPS' + storeFile=release.jks + keyAlias=meshcore + PROPS + { + printf 'storePassword=%s\n' "${KEYSTORE_PASSWORD}" + printf 'keyPassword=%s\n' "${KEYSTORE_PASSWORD}" + } >> android/key.properties + echo "Keystore decoded (${SIZE} bytes), key.properties written." + + - run: flutter pub get + + - name: Build signed APK + run: flutter build apk --release --no-pub + + - name: Build signed AAB + run: flutter build appbundle --release --no-pub + + # The load-bearing check. A green build does NOT prove the artifact is + # release-signed: build.gradle.kts silently falls back to the debug + # signing config when key.properties is missing or malformed (#111), + # which is exactly the failure this workflow exists to prevent. So we + # read the certificate off the built APK and fail if it is the debug key. + - name: Verify APK is signed with the release certificate + run: | + set -eu + APK=build/app/outputs/flutter-apk/app-release.apk + BUILD_TOOLS=$(ls -d "${ANDROID_HOME}"/build-tools/* | sort -V | tail -1) + APKSIGNER="${BUILD_TOOLS}/apksigner" + echo "Using ${APKSIGNER}" + CERTS=$("${APKSIGNER}" verify --print-certs "${APK}") + echo "${CERTS}" + if echo "${CERTS}" | grep -qi "CN=Android Debug"; then + echo "::error::APK is DEBUG-SIGNED. key.properties was not picked up; see #111." + exit 1 + fi + echo "${CERTS}" | grep -i "SHA-256 digest" | head -1 + echo "Release certificate confirmed (not the Android Debug key)." + + - name: Upload signed APK + uses: actions/upload-artifact@v4 + with: + name: offband-signed-apk-r${{ github.run_number }}-${{ github.sha }} + path: build/app/outputs/flutter-apk/app-release.apk + if-no-files-found: error + retention-days: 30 + + - name: Upload signed AAB + uses: actions/upload-artifact@v4 + with: + name: offband-signed-aab-r${{ github.run_number }}-${{ github.sha }} + path: build/app/outputs/bundle/release/app-release.aab + if-no-files-found: error + retention-days: 30 + + # Runs even when an earlier step failed. Without `if: always()` a build + # failure would leave the keystore and its password on the runner. + - name: Remove keystore material + if: always() + run: | + rm -f android/app/release.jks android/key.properties + echo "Keystore material removed." From 29ca744667a18bb11232aab86bbda175110f3f0b Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 02:00:01 -0400 Subject: [PATCH 36/60] feat(#330): pin the expected signing certificate SHA-256 Rejecting only CN=Android Debug catches the #111 fallback but not a DIFFERENT key being substituted, which breaks cross-channel updates against Play just as badly. Now asserts the exact fingerprint. Established from artifacts users have already installed, with no access to the keystore password (reading a certificate off a signed APK needs none): release b55 (2026-07-06) e7da8cd5... release b58 (2026-07-10) e7da8cd5... local release build e7da8cd5... Three independent sources agree, and the owner confirmed the same strycher-personal.jks is enrolled for Play App Signing, so this is the fingerprint that must hold for a GitHub download to update over a Play install. Found while establishing it: published release b59 (2026-07-20) is DEBUG-SIGNED (765fb469, CN=Android Debug), unlike b55 and b58. That is #111 occurring in production. Filed separately. Part of epic #312, plan #321 (Phase 5). --- .github/workflows/release-signed.yml | 39 +++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-signed.yml b/.github/workflows/release-signed.yml index 719401c..8d0c595 100644 --- a/.github/workflows/release-signed.yml +++ b/.github/workflows/release-signed.yml @@ -110,6 +110,17 @@ jobs: # which is exactly the failure this workflow exists to prevent. So we # read the certificate off the built APK and fail if it is the debug key. - name: Verify APK is signed with the release certificate + env: + # SHA-256 of the strycher-personal.jks signing certificate, taken + # from artifacts already published and installed by users: releases + # b55 and b58, and a local release build. Established without the + # keystore password, since reading a certificate off a signed APK + # needs no password. + # + # Pinning the exact value (rather than only rejecting the debug key) + # also catches a DIFFERENT key being substituted, which would break + # cross-channel updates against Play just as badly as debug signing. + EXPECTED_SHA256: e7da8cd5bf22ac3eda7cb954b8b120a18b6c4382d1b6e6fdd204ddedddaf5488 run: | set -eu APK=build/app/outputs/flutter-apk/app-release.apk @@ -117,13 +128,35 @@ jobs: APKSIGNER="${BUILD_TOOLS}/apksigner" echo "Using ${APKSIGNER}" CERTS=$("${APKSIGNER}" verify --print-certs "${APK}") - echo "${CERTS}" + + # Debug fallback is the #111 failure mode and the reason this check + # exists: build.gradle.kts silently signs with the debug config when + # key.properties is absent, and a green build hides it completely. if echo "${CERTS}" | grep -qi "CN=Android Debug"; then echo "::error::APK is DEBUG-SIGNED. key.properties was not picked up; see #111." exit 1 fi - echo "${CERTS}" | grep -i "SHA-256 digest" | head -1 - echo "Release certificate confirmed (not the Android Debug key)." + + ACTUAL=$(echo "${CERTS}" \ + | grep -i "Signer #1 certificate SHA-256 digest" \ + | head -1 \ + | awk -F': ' '{print $2}' \ + | tr -d '[:space:]') + + if [ -z "${ACTUAL}" ]; then + echo "::error::Could not read a certificate SHA-256 from the APK." + exit 1 + fi + + if [ "${ACTUAL}" != "${EXPECTED_SHA256}" ]; then + echo "::error::Signing certificate MISMATCH." + echo "::error::expected ${EXPECTED_SHA256}" + echo "::error::actual ${ACTUAL}" + echo "::error::This APK would not install over an existing Offband install." + exit 1 + fi + + echo "Signing certificate verified: ${ACTUAL}" - name: Upload signed APK uses: actions/upload-artifact@v4 From 1f128174b759046b0fcdb79f025360804607bd76 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 03:31:32 -0400 Subject: [PATCH 37/60] feat(#330): pin Flutter and pass GIPHY_API_KEY in the signed build Rebased onto dev after #331 landed. Brings the signing workflow in line with the rest of the matrix: - Pinned to Flutter 3.44.1, matching the bench and every other workflow. A release artifact in particular must not be built by whatever `stable` happens to point at that day, which was the whole argument for #331. - Generates dart_defines.json and passes --dart-define-from-file, so the signed release does not ship with a dead GIF picker. - Cleanup step now removes dart_defines.json alongside the keystore material, since it holds the Giphy key. Verified that a job pinned to an environment still receives repository secrets (precedence is org < repo < environment, environment winning on a name collision), so the repo-scoped GIPHY_API_KEY reaches this job while the keystore secrets stay environment-scoped behind the reviewer gate. Part of epic #312, plan #321 (Phase 5). --- .github/workflows/release-signed.yml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release-signed.yml b/.github/workflows/release-signed.yml index 8d0c595..6a255b0 100644 --- a/.github/workflows/release-signed.yml +++ b/.github/workflows/release-signed.yml @@ -49,9 +49,12 @@ jobs: distribution: "temurin" java-version: "17" + # Pinned to the bench toolchain, matching every other workflow (#331). + # A release artifact in particular must not be built by whatever + # version `stable` happens to point at on the day. - uses: subosito/flutter-action@v2 with: - channel: "stable" + flutter-version: "3.44.1" cache: true - name: Cache Gradle @@ -98,11 +101,18 @@ jobs: - run: flutter pub get + # Same mechanism as build.yml (#331). A signed release that shipped + # without the Giphy key would have a dead GIF picker. + - name: Write dart_defines.json + env: + GIPHY_API_KEY: ${{ secrets.GIPHY_API_KEY }} + run: jq -n --arg k "$GIPHY_API_KEY" '{GIPHY_API_KEY:$k}' > dart_defines.json + - name: Build signed APK - run: flutter build apk --release --no-pub + run: flutter build apk --release --no-pub --dart-define-from-file=dart_defines.json - name: Build signed AAB - run: flutter build appbundle --release --no-pub + run: flutter build appbundle --release --no-pub --dart-define-from-file=dart_defines.json # The load-bearing check. A green build does NOT prove the artifact is # release-signed: build.gradle.kts silently falls back to the debug @@ -179,5 +189,5 @@ jobs: - name: Remove keystore material if: always() run: | - rm -f android/app/release.jks android/key.properties - echo "Keystore material removed." + rm -f android/app/release.jks android/key.properties dart_defines.json + echo "Keystore material and dart_defines removed." From 06d3f027dd68b56633591f7b83c32c0fc39a49e0 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 17:54:37 -0400 Subject: [PATCH 38/60] test(#335): make the drift asset-version guard line-ending agnostic The guard read pubspec.lock with a newline-sensitive regex, so it passed on LF (CI) and failed on CRLF (Windows) for the same, correct assets. A guard that is itself platform-fragile is worse than none. Normalise CRLF->LF before matching. Surfaced by the 290+306+335 integration merge, where the lockfile came through with CRLF endings. --- test/storage/drift_asset_version_test.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/storage/drift_asset_version_test.dart b/test/storage/drift_asset_version_test.dart index c8760db..e917fe1 100644 --- a/test/storage/drift_asset_version_test.dart +++ b/test/storage/drift_asset_version_test.dart @@ -24,10 +24,14 @@ void main() { 'reproducible; the drift web assets are matched against it.', ); + // Normalise line endings first: pubspec.lock is CRLF on Windows and LF in + // CI, and a newline-sensitive pattern would pass on one and fail on the + // other. A guard that is itself platform-fragile is worse than none. + final lockText = lock.readAsStringSync().replaceAll('\r\n', '\n'); final resolved = RegExp( r'^ drift:\n(?:.*\n)*? version: "([^"]+)"', multiLine: true, - ).firstMatch(lock.readAsStringSync())?.group(1); + ).firstMatch(lockText)?.group(1); expect(resolved, isNotNull, reason: 'drift not found in pubspec.lock'); final stamp = File('$root/web/drift_assets.version'); From fa470c144209b114cb26a6baef13f231dbae5890 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 18:23:29 -0400 Subject: [PATCH 39/60] fix(#335): store the drift DB in app-support, not OneDrive-synced Documents drift_flutter's native default is getApplicationDocumentsDirectory(), which on Windows resolves to the user's Documents folder - redirected into OneDrive on most machines (verified: drift_flutter 0.3.1 connect.dart). A live SQLite file syncing to OneDrive risks lock contention and corruption, and it is simply the wrong place for app data. The DB now opens in getApplicationSupportDirectory() (%APPDATA% on Windows), the same place SharedPreferences already lives, so all app data sits together. Existing installs already have a DB in the old location, so opening a fresh one there would abandon their migrated history. _appSupportDatabaseDirectory() relocates it once on first run: it moves offband_store.sqlite and its -wal/-shm sidecars from the documents dir to the support dir before drift opens, and never deletes a source without a successful copy. If relocation fails it is logged loudly and a fresh DB is created, with the migration re-running from SharedPreferences rather than silently losing anything. Web is unaffected: path_provider has no web backend and drift ignores databaseDirectory there, using OPFS/IndexedDB. Adds `path` as a direct dependency (was transitive). flutter analyze clean, tests pass. --- lib/storage/drift/offband_database.dart | 68 ++++++++++++++++++++++++- pubspec.lock | 2 +- pubspec.yaml | 1 + 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/lib/storage/drift/offband_database.dart b/lib/storage/drift/offband_database.dart index 9ca9eff..2d14954 100644 --- a/lib/storage/drift/offband_database.dart +++ b/lib/storage/drift/offband_database.dart @@ -1,5 +1,11 @@ +import 'dart:io'; + import 'package:drift/drift.dart'; import 'package:drift_flutter/drift_flutter.dart'; +import 'package:path/path.dart' as p; +import 'package:path_provider/path_provider.dart'; + +import '../../utils/app_logger.dart'; part 'offband_database.g.dart'; @@ -23,12 +29,25 @@ class OffbandDatabase extends _$OffbandDatabase { OffbandDatabase([QueryExecutor? executor]) : super(executor ?? _defaultExecutor()); + static const String _dbName = 'offband_store'; + /// `drift_flutter` selects the platform backend: native SQLite on desktop /// and mobile, WASM on web. `web:` names the assets that must be shipped for - /// the web build (`sqlite3.wasm`, `drift_worker.dart.js`). + /// the web build (`sqlite3.wasm`, `drift_worker.js`). static QueryExecutor _defaultExecutor() { return driftDatabase( - name: 'offband_store', + name: _dbName, + // Native default is getApplicationDocumentsDirectory(), which on Windows + // is the user's Documents folder — redirected into OneDrive on most + // machines. A live SQLite file syncing to OneDrive risks lock contention + // and corruption, and it is the wrong place for app data. Use the + // application-support dir instead (%APPDATA% on Windows), where + // SharedPreferences already lives. path_provider has no web + // implementation, so this only applies off web; web ignores + // databaseDirectory and uses OPFS/IndexedDB. + native: DriftNativeOptions( + databaseDirectory: _appSupportDatabaseDirectory, + ), web: DriftWebOptions( sqlite3Wasm: Uri.parse('sqlite3.wasm'), // Filename matches the asset published by the drift release, which is @@ -40,6 +59,51 @@ class OffbandDatabase extends _$OffbandDatabase { ); } + /// Resolves the application-support directory, and relocates a database left + /// in the old documents location by an earlier build. + /// + /// Builds before this fix opened the DB under getApplicationDocumentsDirectory + /// (OneDrive on Windows). Existing installs already have data there, so this + /// moves the file - and its `-wal` / `-shm` sidecars - to the new location on + /// first run, once, before drift opens it. Never deletes the source without a + /// successful move; a failed move leaves the old file so no data is stranded. + static Future _appSupportDatabaseDirectory() async { + final support = await getApplicationSupportDirectory(); + final newPath = p.join(support.path, '$_dbName.sqlite'); + + if (!File(newPath).existsSync()) { + try { + final docs = await getApplicationDocumentsDirectory(); + final oldPath = p.join(docs.path, '$_dbName.sqlite'); + if (File(oldPath).existsSync() && oldPath != newPath) { + for (final suffix in ['', '-wal', '-shm']) { + final src = File('$oldPath$suffix'); + if (src.existsSync()) { + src.copySync('$newPath$suffix'); + src.deleteSync(); + } + } + appLogger.info( + 'Relocated drift DB out of the documents dir (OneDrive on ' + 'Windows) into the app-support dir: $oldPath -> $newPath', + tag: 'Storage', + ); + } + } catch (e) { + // Relocation is best-effort: if it fails, drift opens a fresh DB at the + // correct location and the migration re-runs from SharedPreferences. + // Loud, never silent (SAFELANE 6). + appLogger.error( + 'Failed to relocate the drift DB from the documents dir: $e. ' + 'A fresh DB will be created at the app-support location.', + tag: 'Storage', + ); + } + } + + return support; + } + @override int get schemaVersion => 1; diff --git a/pubspec.lock b/pubspec.lock index be2ed1c..e31ec5c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -911,7 +911,7 @@ packages: source: hosted version: "3.2.1" path: - dependency: transitive + dependency: "direct main" description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" diff --git a/pubspec.yaml b/pubspec.yaml index e93e05a..d385dd6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -79,6 +79,7 @@ dependencies: # re-download both assets. tool/check_drift_assets.dart enforces the match. drift: 2.34.2 drift_flutter: 0.3.1 + path: ^1.9.1 hooks: user_defines: From 2fefb6aa91d64a150ed1a267b6d195990ba61c2d Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 18:38:33 -0400 Subject: [PATCH 40/60] fix(#343): keep full message history; window only in memory The persisted store was being overwritten with the windowed in-memory list, so any channel or DM with more than _messageWindowSize (200) messages lost everything older than the newest 200 on the first save after load. Observed live: Public went 232 -> 201 in one session on a build that already had the #333 fix, so this was not the race - it was windowing truncating the store. This is the slow-erosion cause behind the whole 566 -> 231 -> 206 -> 201 history. saveChannelMessages / saveMessages now MERGE into the persisted set instead of overwriting: upsert by message identity so older persisted messages are kept, new ones added, and the in-memory copy wins for edits/reactions/status. If the existing history fails to decode, the save aborts loudly rather than merging into an empty base and truncating (SAFELANE 6). Deletion is now an explicit path - removeChannelMessage / removeMessage - since the app deletes individual messages. Routing delete through the merging save would resurrect them; the connector's deleteChannelMessage / deleteMessage now call the explicit remove. Windowing stays for display and memory; it no longer dictates what is stored. Tests: 250-message history survives a 200-window save; new message appends; edit is captured not duplicated; delete does not resurrect. 508 pass, analyze and format clean. Cost: a save now re-reads and re-encodes the channel/contact history. Cheap with drift's per-key writes (#335); a future append-only schema removes even that. --- lib/connector/meshcore_connector.dart | 8 +- lib/storage/channel_message_store.dart | 79 +++++++++++++- lib/storage/drift/blob_store.dart | 13 ++- lib/storage/message_store.dart | 66 +++++++++++- test/storage/channel_message_merge_test.dart | 103 +++++++++++++++++++ 5 files changed, 261 insertions(+), 8 deletions(-) create mode 100644 test/storage/channel_message_merge_test.dart diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index fdcfc98..c8e434b 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -699,7 +699,8 @@ class MeshCoreConnector extends ChangeNotifier { if (messages == null) return; final removed = messages.remove(message); if (!removed) return; - await _messageStore.saveMessages(contactKeyHex, messages); + // Explicit delete: saveMessages now merges and never removes (#343). + await _messageStore.removeMessage(contactKeyHex, message); notifyListeners(); } @@ -833,7 +834,10 @@ class MeshCoreConnector extends ChangeNotifier { if (messages == null) return; final removed = messages.remove(message); if (!removed) return; - await _channelMessageStore.saveChannelMessages(channelIndex, messages); + // Explicit delete path: saveChannelMessages now MERGES and never removes + // (#343), so deletion must go through removeChannelMessage or the message + // would be resurrected on the next save. + await _channelMessageStore.removeChannelMessage(channelIndex, message); notifyListeners(); } diff --git a/lib/storage/channel_message_store.dart b/lib/storage/channel_message_store.dart index e829b69..842a2d5 100644 --- a/lib/storage/channel_message_store.dart +++ b/lib/storage/channel_message_store.dart @@ -65,10 +65,83 @@ class ChannelMessageStore { ); return; } - final jsonList = messages.map((msg) => _messageToJson(msg)).toList(); + // Merge into the persisted full history rather than overwriting it. The + // in-memory list is windowed to the most recent N for memory, so a plain + // overwrite would truncate the store to N and erode old history (#343). + // Upsert by identity: keep older persisted messages, add new ones, and let + // the in-memory copy win so edits/reactions/status updates are captured. + // Deletion has its own path (removeChannelMessage) so this never + // resurrects a message the user deleted. + final key = _storageKey(channelIndex); + final byKey = {}; + + final existing = await BlobStore.instance.readWithPrefsFallback(key); + if (existing != null && existing.isNotEmpty) { + try { + for (final e in jsonDecode(existing) as List) { + final m = _messageFromJson(e as Map); + byKey[_mergeKey(m)] = m; + } + } catch (e) { + // SAFELANE 6: a decode failure here must be loud, not silently drop the + // persisted history by merging into an empty base. + appLogger.error( + 'Failed to decode existing channel $channelIndex history before ' + 'merge; aborting save to avoid truncation: $e', + tag: 'Storage', + ); + return; + } + } + for (final m in messages) { + byKey[_mergeKey(m)] = m; + } + + final merged = byKey.values.toList() + ..sort((a, b) => a.timestamp.compareTo(b.timestamp)); + await BlobStore.instance.write( + key, + jsonEncode(merged.map(_messageToJson).toList()), + ); + } + + /// Stable identity for merge/dedupe. messageId when present, else a composite + /// that distinguishes distinct messages that share no id. + String _mergeKey(ChannelMessage m) { + if (m.messageId.isNotEmpty) return 'id:${m.messageId}'; + return 'x:${m.packetHash ?? ''}:${m.timestamp.millisecondsSinceEpoch}:${m.text}'; + } + + /// Removes a single message from the persisted history. The explicit delete + /// path (#343): save merges and never removes, so deletion cannot go through + /// save. + Future removeChannelMessage( + int channelIndex, + ChannelMessage message, + ) async { + if (publicKeyHex.isEmpty) return; + final key = _storageKey(channelIndex); + final existing = await BlobStore.instance.readWithPrefsFallback(key); + if (existing == null || existing.isEmpty) return; + + final List raw; + try { + raw = jsonDecode(existing) as List; + } catch (e) { + appLogger.error( + 'Failed to decode channel $channelIndex history for delete: $e', + tag: 'Storage', + ); + return; + } + final target = _mergeKey(message); + final kept = raw + .map((e) => _messageFromJson(e as Map)) + .where((m) => _mergeKey(m) != target) + .toList(); await BlobStore.instance.write( - _storageKey(channelIndex), - jsonEncode(jsonList), + key, + jsonEncode(kept.map(_messageToJson).toList()), ); } diff --git a/lib/storage/drift/blob_store.dart b/lib/storage/drift/blob_store.dart index 6cab09b..8239c72 100644 --- a/lib/storage/drift/blob_store.dart +++ b/lib/storage/drift/blob_store.dart @@ -1,3 +1,5 @@ +import 'package:flutter/foundation.dart'; + import '../../utils/app_logger.dart'; import '../prefs_manager.dart'; import 'offband_database.dart'; @@ -19,10 +21,19 @@ class BlobStore { final OffbandDatabase _db; static OffbandDatabase? _sharedDb; + static BlobStore? _override; /// Process-wide instance. The database must be opened once; opening it twice /// is an error on the web backends. - static BlobStore get instance => BlobStore(_sharedDb ??= OffbandDatabase()); + static BlobStore get instance => + _override ?? BlobStore(_sharedDb ??= OffbandDatabase()); + + /// Test seam: point the singleton at an in-memory database. + @visibleForTesting + static void overrideForTest(BlobStore store) => _override = store; + + @visibleForTesting + static void clearTestOverride() => _override = null; /// Key families that hold bulk data. Everything else stays in /// SharedPreferences, which is what it is for. diff --git a/lib/storage/message_store.dart b/lib/storage/message_store.dart index 2d8b02d..b912d0e 100644 --- a/lib/storage/message_store.dart +++ b/lib/storage/message_store.dart @@ -24,9 +24,71 @@ class MessageStore { appLogger.warn('Public key hex is not set. Cannot save messages.'); return; } + // Merge into the persisted full history rather than overwriting it (#343). + // The in-memory list is windowed for memory, so a plain overwrite would + // truncate the store. Upsert by identity; deletion is explicit + // (removeMessage). final key = '$keyFor$contactKeyHex'; - final jsonList = messages.map(_messageToJson).toList(); - await BlobStore.instance.write(key, jsonEncode(jsonList)); + final byKey = {}; + + final existing = await BlobStore.instance.readWithPrefsFallback(key); + if (existing != null && existing.isNotEmpty) { + try { + for (final e in jsonDecode(existing) as List) { + final m = _messageFromJson(e as Map); + byKey[_mergeKey(m)] = m; + } + } catch (e) { + appLogger.error( + 'Failed to decode existing DM history before merge; aborting save ' + 'to avoid truncation: $e', + tag: 'Storage', + ); + return; + } + } + for (final m in messages) { + byKey[_mergeKey(m)] = m; + } + + final merged = byKey.values.toList() + ..sort((a, b) => a.timestamp.compareTo(b.timestamp)); + await BlobStore.instance.write( + key, + jsonEncode(merged.map(_messageToJson).toList()), + ); + } + + String _mergeKey(Message m) { + if (m.messageId.isNotEmpty) return 'id:${m.messageId}'; + return 'x:${m.senderKeyHex}:${m.timestamp.millisecondsSinceEpoch}:${m.text}'; + } + + /// Explicit delete path (#343): save merges and never removes. + Future removeMessage(String contactKeyHex, Message message) async { + if (publicKeyHex.isEmpty) return; + final key = '$keyFor$contactKeyHex'; + final existing = await BlobStore.instance.readWithPrefsFallback(key); + if (existing == null || existing.isEmpty) return; + final List raw; + try { + raw = jsonDecode(existing) as List; + } catch (e) { + appLogger.error( + 'Failed to decode DM history for delete: $e', + tag: 'Storage', + ); + return; + } + final target = _mergeKey(message); + final kept = raw + .map((e) => _messageFromJson(e as Map)) + .where((m) => _mergeKey(m) != target) + .toList(); + await BlobStore.instance.write( + key, + jsonEncode(kept.map(_messageToJson).toList()), + ); } Future> loadMessages(String contactKeyHex) async { diff --git a/test/storage/channel_message_merge_test.dart b/test/storage/channel_message_merge_test.dart new file mode 100644 index 0000000..83f6144 --- /dev/null +++ b/test/storage/channel_message_merge_test.dart @@ -0,0 +1,103 @@ +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:meshcore_open/models/channel_message.dart'; +import 'package:meshcore_open/storage/channel_message_store.dart'; +import 'package:meshcore_open/storage/drift/blob_store.dart'; +import 'package:meshcore_open/storage/drift/offband_database.dart'; +import 'package:meshcore_open/storage/prefs_manager.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +/// #343: saving a windowed in-memory list must not truncate the persisted +/// history. The store keeps full history; deletion is explicit. +void main() { + late OffbandDatabase db; + late ChannelMessageStore store; + + setUp(() async { + SharedPreferences.setMockInitialValues({}); + PrefsManager.reset(); + await PrefsManager.initialize(); + db = OffbandDatabase(NativeDatabase.memory()); + BlobStore.overrideForTest(BlobStore(db)); + store = ChannelMessageStore(); + store.setPublicKeyHex = 'a' * 20; + // No PSK resolver -> uses the slot-index key, which is fine for the test. + }); + + tearDown(() async { + await db.close(); + BlobStore.clearTestOverride(); + }); + + ChannelMessage msg(int i) => ChannelMessage( + senderName: 's', + text: 'm$i', + timestamp: DateTime.fromMillisecondsSinceEpoch(1000 + i), + isOutgoing: false, + channelIndex: 0, + messageId: 'id$i', + ); + + test('saving a 200-window does not truncate a 250-message history', () async { + // Seed 250 messages, as if a full history were persisted. + await store.saveChannelMessages(0, [for (var i = 0; i < 250; i++) msg(i)]); + expect((await store.loadChannelMessages(0)).length, 250); + + // The app now saves only the most-recent 200 (its in-memory window). + final window = [for (var i = 50; i < 250; i++) msg(i)]; + await store.saveChannelMessages(0, window); + + // Full history must survive: the older 50 are still there. + final all = await store.loadChannelMessages(0); + expect(all.length, 250, reason: 'the older 50 must not be dropped'); + expect(all.first.text, 'm0'); + expect(all.last.text, 'm249'); + }); + + test('a new message appends without dropping old history', () async { + await store.saveChannelMessages(0, [for (var i = 0; i < 10; i++) msg(i)]); + await store.saveChannelMessages(0, [msg(10)]); + final all = await store.loadChannelMessages(0); + expect(all.length, 11); + expect(all.last.text, 'm10'); + }); + + test('an edit to a message is captured, not duplicated', () async { + await store.saveChannelMessages(0, [msg(1)]); + final edited = ChannelMessage( + senderName: 's', + text: 'm1', + timestamp: DateTime.fromMillisecondsSinceEpoch(1001), + isOutgoing: false, + channelIndex: 0, + messageId: 'id1', + reactions: {'thumbsup': 2}, + ); + await store.saveChannelMessages(0, [edited]); + final all = await store.loadChannelMessages(0); + expect(all, hasLength(1)); + expect(all.single.reactions['thumbsup'], 2); + }); + + test('deleting a message does NOT resurrect it on the next save', () async { + await store.saveChannelMessages(0, [for (var i = 0; i < 5; i++) msg(i)]); + + // Delete via the explicit path. + await store.removeChannelMessage(0, msg(2)); + expect( + (await store.loadChannelMessages(0)).map((m) => m.text), + isNot(contains('m2')), + ); + + // A later save of the remaining in-memory list must not bring it back. + final remaining = [ + for (var i = 0; i < 5; i++) + if (i != 2) msg(i), + ]; + await store.saveChannelMessages(0, remaining); + + final all = await store.loadChannelMessages(0); + expect(all.map((m) => m.text), isNot(contains('m2'))); + expect(all, hasLength(4)); + }); +} From 602776c601c3214dcee31630b4e4dfedf48ad556 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 18:31:59 -0400 Subject: [PATCH 41/60] feat(#342): full-platform release automation + four-file notes gate Today a tag builds and signs an APK/AAB but creates no release and attaches nothing. That is the same partial-release failure mode as rc.2/rc.3 (Play-only, no changelog). This closes it. release-signed.yml becomes a complete release pipeline off a v* tag: gate -> release-notes gate (backstop; fails in seconds) android -> signed APK + AAB, cert-verified (environment-gated) windows -> zipped Release build linux -> tarred bundle release -> create GitHub release, body = release notes, attach all four Security invariant preserved: only the android job references the release-signing Environment, so only it receives the keystore. The windows/linux/release jobs never see it. Still no pull_request trigger. The release job runs ONLY after every build passes, so a signing failure yields no release rather than an empty one. Published directly because the notes were reviewed in the cut PR. Four-file notes gate (.github/scripts/release-gate.sh), run on every PR (release-gate.yml) and as job one of the tag workflow: CHANGELOG.md section for the pubspec version release-notes/.md non-empty -> GitHub release body play/.txt non-empty, <=500 chars (Play limit) discord/.md non-empty play and discord are human-pasted; CI validates, never sends. Any missing/oversize file fails the build, so a noteless release (rc.2/rc.3) is unmergeable. Gate script lives in .github/scripts/ because /scripts is in .git/info/exclude (would not commit). Verified locally: fail surfaces all four problems at once, pass is clean, 500 passes, 501 fails. Backfills the combined rc.2/rc.3 CHANGELOG entry the two versions shipped without. release-notes/, play/, discord/ seeded with README conventions. No ref input needed: the rc.3 backfill branch will carry these workflow files, so tagging that branch runs them at that commit. Part of epic #312, plan #321. Agent: SapphireCompass (session 8d755b5e) --- .github/scripts/release-gate.sh | 75 ++++++++ .github/workflows/release-gate.yml | 27 +++ .github/workflows/release-signed.yml | 245 ++++++++++++++++----------- CHANGELOG.md | 45 +++++ discord/README.md | 14 ++ play/README.md | 16 ++ release-notes/README.md | 22 +++ 7 files changed, 349 insertions(+), 95 deletions(-) create mode 100644 .github/scripts/release-gate.sh create mode 100644 .github/workflows/release-gate.yml create mode 100644 discord/README.md create mode 100644 play/README.md create mode 100644 release-notes/README.md diff --git a/.github/scripts/release-gate.sh b/.github/scripts/release-gate.sh new file mode 100644 index 0000000..118565d --- /dev/null +++ b/.github/scripts/release-gate.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# Release-notes gate. Verifies that every human-authored release text exists and +# is well-formed for the version currently in pubspec.yaml. Run BOTH on the cut +# PR (so it goes red before merge) and as the first job of the tag workflow (a +# cheap backstop before build/sign). +# +# rc.2 and rc.3 shipped with only a pubspec bump and no notes. This makes that +# state impossible: no notes, no release. +# +# Exit 0 = all present and valid. Exit 1 = a hard failure (prints every problem +# before exiting, so one run surfaces all of them, not one at a time). +# +# #342, epic #312. + +set -uo pipefail + +PLAY_MAX=500 + +fail=0 +note() { printf ' %s\n' "$1"; } +bad() { printf '::error::%s\n' "$1"; fail=1; } + +# --- version from pubspec (strip the +build suffix) ------------------------- +VERSION=$(grep -E '^version:' pubspec.yaml | head -1 | sed 's/version:[[:space:]]*//' | sed 's/+.*//' | tr -d '[:space:]') +if [ -z "${VERSION}" ]; then + bad "Could not read a version from pubspec.yaml." + exit 1 +fi +echo "Release gate for version: ${VERSION}" +echo + +# --- 1. CHANGELOG has a section for this version ---------------------------- +# Matches a markdown heading containing the version, e.g. "## [1.1.2-rc.4]". +if grep -qE "^#+.*\[?${VERSION//./\\.}\]?" CHANGELOG.md; then + note "CHANGELOG.md: section for ${VERSION} present" +else + bad "CHANGELOG.md has no section for ${VERSION}. Add one before releasing." +fi + +# --- 2. GitHub release notes exist and are non-empty ------------------------ +RN="release-notes/${VERSION}.md" +if [ -s "${RN}" ]; then + note "release-notes: ${RN} present" +else + bad "${RN} is missing or empty. This becomes the GitHub release body." +fi + +# --- 3. Play 'What's New' exists and is within the length limit ------------- +PLAY="play/${VERSION}.txt" +if [ ! -s "${PLAY}" ]; then + bad "${PLAY} is missing or empty. Required for the Play Console 'What's new'." +else + # Unicode-aware count (wc -m), matching Google's per-character limit. + LEN=$(wc -m < "${PLAY}" | tr -d '[:space:]') + if [ "${LEN}" -gt "${PLAY_MAX}" ]; then + bad "${PLAY} is ${LEN} chars; Play limit is ${PLAY_MAX}. Trim it." + else + note "play: ${PLAY} present (${LEN}/${PLAY_MAX} chars)" + fi +fi + +# --- 4. Discord announcement exists and is non-empty ------------------------ +DISCORD="discord/${VERSION}.md" +if [ -s "${DISCORD}" ]; then + note "discord: ${DISCORD} present" +else + bad "${DISCORD} is missing or empty. Paste-ready community announcement." +fi + +echo +if [ "${fail}" -ne 0 ]; then + echo "Release gate FAILED for ${VERSION}. Fix the above before tagging." + exit 1 +fi +echo "Release gate passed for ${VERSION}." diff --git a/.github/workflows/release-gate.yml b/.github/workflows/release-gate.yml new file mode 100644 index 0000000..d6ef81f --- /dev/null +++ b/.github/workflows/release-gate.yml @@ -0,0 +1,27 @@ +name: Release gate + +# Fails a PR whose pubspec version lacks any of the four human-authored release +# texts (CHANGELOG section, release-notes, play blurb <=500 chars, discord). +# +# This runs on EVERY PR so a release-cut PR goes red before merge if a note is +# missing. It is cheap and platform-agnostic. The tag workflow runs the same +# script as its first job (a backstop before build/sign). +# +# rc.2 and rc.3 shipped with only a pubspec bump. This makes that unmergeable. +# +# #342, epic #312. + +on: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + gate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check release texts for the pubspec version + run: bash .github/scripts/release-gate.sh diff --git a/.github/workflows/release-signed.yml b/.github/workflows/release-signed.yml index 6a255b0..f363a11 100644 --- a/.github/workflows/release-signed.yml +++ b/.github/workflows/release-signed.yml @@ -1,62 +1,63 @@ -name: Release (signed) +name: Release (signed, all platforms) -# Produces RELEASE-SIGNED Android artifacts using the project's real keystore. +# Produces a COMPLETE, published release from a `v*` tag: signed Android APK + +# AAB, Windows, and Linux, attached to a GitHub release whose body is the +# human-authored release notes. This is what makes a partial release (the +# rc.2/rc.3 failure) impossible. # -# SECURITY: this repo is PUBLIC and this workflow has access to the signing -# keystore, which is the single most irreplaceable credential in the project. -# If it leaks, the app can never be updated again on any channel (GitHub, -# F-Droid, Play all require a matching signature). +# SECURITY: this repo is PUBLIC and the `android` job has the signing keystore, +# the single most irreplaceable credential in the project. If it leaks the app +# can never be updated again on any channel. # -# Therefore: -# - NEVER add a `pull_request` trigger here. A PR (including from a fork) -# must never be able to run a job that can read these secrets. -# - The job is pinned to the `release-signing` Environment, which carries a -# required reviewer, so a signing run cannot proceed unattended. -# - The keystore is written to disk only for the duration of the build and -# deleted in a step that runs even when the build fails. -# - Signing values are passed via `env:`, never interpolated into a command -# line (where they would be visible in process listings) and never echoed. +# - NEVER add a `pull_request` trigger. A PR (incl. from a fork) must never +# run a job that can read the keystore. +# - Only the `android` job references the `release-signing` Environment, so +# ONLY it receives the keystore secrets, and only after the required +# reviewer approves. The windows/linux/release jobs never see them. +# - Keystore written to disk only for the build, deleted with `if: always()`. +# - Signing values passed via env:, never on a command line, never echoed. # -# Debug-signed artifacts for day-to-day PR testing continue to come from -# build.yml and are unaffected by this workflow. See #330, epic #312. +# Debug-signed artifacts for day-to-day PR testing still come from build.yml +# and are unaffected. See #330 / #342, epic #312. on: push: - branches: - - main tags: - "v*" workflow_dispatch: concurrency: - group: release-signed-${{ github.ref }} + group: release-${{ github.ref }} cancel-in-progress: false permissions: contents: read jobs: - android-signed: + # Cheap backstop: the same gate that runs on the cut PR, re-run here so a tag + # can never publish without the four release texts. Fails in seconds, before + # any expensive build. + gate: runs-on: ubuntu-latest - # Required-reviewer gate. Do not remove. - environment: release-signing - steps: - uses: actions/checkout@v4 + - name: Release-notes gate + run: bash .github/scripts/release-gate.sh + android: + needs: gate + runs-on: ubuntu-latest + environment: release-signing # required-reviewer gate; do not remove + steps: + - uses: actions/checkout@v4 - uses: actions/setup-java@v4 with: distribution: "temurin" java-version: "17" - - # Pinned to the bench toolchain, matching every other workflow (#331). - # A release artifact in particular must not be built by whatever - # version `stable` happens to point at on the day. - uses: subosito/flutter-action@v2 with: flutter-version: "3.44.1" cache: true - - name: Cache Gradle uses: actions/cache@v4 with: @@ -67,9 +68,8 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - # The keystore is PKCS12 (despite the .jks extension), which does not - # support a key password distinct from the store password. One secret - # therefore correctly populates both fields. See #330. + # PKCS12 keystore (despite the .jks extension) has one password for both + # store and key. See #330. - name: Decode signing keystore env: KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} @@ -81,8 +81,6 @@ jobs: exit 1 fi printf '%s' "${KEYSTORE_BASE64}" | base64 -d > android/app/release.jks - # Fail loudly if the decode produced something implausible rather - # than letting Gradle fall back to debug signing silently. SIZE=$(stat -c%s android/app/release.jks) if [ "${SIZE}" -lt 1000 ]; then echo "::error::Decoded keystore is ${SIZE} bytes; expected ~2.8 KB. Secret is malformed." @@ -100,94 +98,151 @@ jobs: echo "Keystore decoded (${SIZE} bytes), key.properties written." - run: flutter pub get - - # Same mechanism as build.yml (#331). A signed release that shipped - # without the Giphy key would have a dead GIF picker. - name: Write dart_defines.json env: GIPHY_API_KEY: ${{ secrets.GIPHY_API_KEY }} run: jq -n --arg k "$GIPHY_API_KEY" '{GIPHY_API_KEY:$k}' > dart_defines.json - - name: Build signed APK run: flutter build apk --release --no-pub --dart-define-from-file=dart_defines.json - - name: Build signed AAB run: flutter build appbundle --release --no-pub --dart-define-from-file=dart_defines.json - # The load-bearing check. A green build does NOT prove the artifact is - # release-signed: build.gradle.kts silently falls back to the debug - # signing config when key.properties is missing or malformed (#111), - # which is exactly the failure this workflow exists to prevent. So we - # read the certificate off the built APK and fail if it is the debug key. - - name: Verify APK is signed with the release certificate + # A green build does NOT prove release signing: build.gradle.kts silently + # falls back to the debug config when key.properties is absent (#111). Read + # the cert off the APK and fail on the debug key or any mismatch. + - name: Verify APK signing certificate env: - # SHA-256 of the strycher-personal.jks signing certificate, taken - # from artifacts already published and installed by users: releases - # b55 and b58, and a local release build. Established without the - # keystore password, since reading a certificate off a signed APK - # needs no password. - # - # Pinning the exact value (rather than only rejecting the debug key) - # also catches a DIFFERENT key being substituted, which would break - # cross-channel updates against Play just as badly as debug signing. EXPECTED_SHA256: e7da8cd5bf22ac3eda7cb954b8b120a18b6c4382d1b6e6fdd204ddedddaf5488 run: | set -eu APK=build/app/outputs/flutter-apk/app-release.apk BUILD_TOOLS=$(ls -d "${ANDROID_HOME}"/build-tools/* | sort -V | tail -1) - APKSIGNER="${BUILD_TOOLS}/apksigner" - echo "Using ${APKSIGNER}" - CERTS=$("${APKSIGNER}" verify --print-certs "${APK}") - - # Debug fallback is the #111 failure mode and the reason this check - # exists: build.gradle.kts silently signs with the debug config when - # key.properties is absent, and a green build hides it completely. + CERTS=$("${BUILD_TOOLS}/apksigner" verify --print-certs "${APK}") if echo "${CERTS}" | grep -qi "CN=Android Debug"; then - echo "::error::APK is DEBUG-SIGNED. key.properties was not picked up; see #111." - exit 1 - fi - - ACTUAL=$(echo "${CERTS}" \ - | grep -i "Signer #1 certificate SHA-256 digest" \ - | head -1 \ - | awk -F': ' '{print $2}' \ - | tr -d '[:space:]') - - if [ -z "${ACTUAL}" ]; then - echo "::error::Could not read a certificate SHA-256 from the APK." + echo "::error::APK is DEBUG-SIGNED. key.properties not picked up; see #111." exit 1 fi - + ACTUAL=$(echo "${CERTS}" | grep -i "Signer #1 certificate SHA-256 digest" | head -1 | awk -F': ' '{print $2}' | tr -d '[:space:]') if [ "${ACTUAL}" != "${EXPECTED_SHA256}" ]; then - echo "::error::Signing certificate MISMATCH." - echo "::error::expected ${EXPECTED_SHA256}" - echo "::error::actual ${ACTUAL}" - echo "::error::This APK would not install over an existing Offband install." + echo "::error::Signing cert MISMATCH. expected ${EXPECTED_SHA256} actual ${ACTUAL}" exit 1 fi - echo "Signing certificate verified: ${ACTUAL}" - - name: Upload signed APK - uses: actions/upload-artifact@v4 - with: - name: offband-signed-apk-r${{ github.run_number }}-${{ github.sha }} - path: build/app/outputs/flutter-apk/app-release.apk - if-no-files-found: error - retention-days: 30 - - - name: Upload signed AAB - uses: actions/upload-artifact@v4 + - name: Stage signed artifacts + run: | + mkdir -p dist + cp build/app/outputs/flutter-apk/app-release.apk dist/ + cp build/app/outputs/bundle/release/app-release.aab dist/ + - uses: actions/upload-artifact@v4 with: - name: offband-signed-aab-r${{ github.run_number }}-${{ github.sha }} - path: build/app/outputs/bundle/release/app-release.aab + name: release-android + path: dist/* if-no-files-found: error - retention-days: 30 + retention-days: 7 - # Runs even when an earlier step failed. Without `if: always()` a build - # failure would leave the keystore and its password on the runner. - name: Remove keystore material if: always() run: | rm -f android/app/release.jks android/key.properties dart_defines.json - echo "Keystore material and dart_defines removed." + echo "Keystore material removed." + + windows: + needs: gate + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + flutter-version: "3.44.1" + cache: true + - run: flutter pub get + - name: Write dart_defines.json + env: + GIPHY_API_KEY: ${{ secrets.GIPHY_API_KEY }} + run: | + @{ GIPHY_API_KEY = $env:GIPHY_API_KEY } | ConvertTo-Json -Compress | + Set-Content -Path dart_defines.json -Encoding utf8 -NoNewline + - run: flutter build windows --release --no-pub --dart-define-from-file=dart_defines.json + - name: Zip Windows build + run: Compress-Archive -Path build/windows/x64/runner/Release/* -DestinationPath offband-windows-x64.zip + - uses: actions/upload-artifact@v4 + with: + name: release-windows + path: offband-windows-x64.zip + if-no-files-found: error + retention-days: 7 + + linux: + needs: gate + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + flutter-version: "3.44.1" + cache: true + - name: Install Linux build deps + run: sudo apt-get update && sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev + - run: flutter pub get + - name: Write dart_defines.json + env: + GIPHY_API_KEY: ${{ secrets.GIPHY_API_KEY }} + run: jq -n --arg k "$GIPHY_API_KEY" '{GIPHY_API_KEY:$k}' > dart_defines.json + - run: flutter build linux --release --no-pub --dart-define-from-file=dart_defines.json + - name: Tar Linux bundle + run: tar -czf offband-linux-x64.tar.gz -C build/linux/x64/release/bundle . + - uses: actions/upload-artifact@v4 + with: + name: release-linux + path: offband-linux-x64.tar.gz + if-no-files-found: error + retention-days: 7 + + # Creates the release ONLY after all platform builds (and the signing cert + # check) have passed, so a signing failure yields no release rather than an + # empty one. Published directly: the notes were reviewed in the cut PR. + release: + needs: [android, windows, linux] + runs-on: ubuntu-latest + permissions: + contents: write # create the release + upload assets + steps: + - uses: actions/checkout@v4 + - name: Resolve version + notes + id: v + run: | + set -eu + VERSION=$(grep -E '^version:' pubspec.yaml | head -1 | sed 's/version:[[:space:]]*//' | sed 's/+.*//' | tr -d '[:space:]') + NOTES="release-notes/${VERSION}.md" + test -s "${NOTES}" || { echo "::error::${NOTES} missing at release time"; exit 1; } + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "notes=${NOTES}" >> "$GITHUB_OUTPUT" + - uses: actions/download-artifact@v4 + with: + path: incoming + - name: Collect assets + run: | + mkdir -p out + cp incoming/release-android/* out/ + cp incoming/release-windows/* out/ + cp incoming/release-linux/* out/ + echo "Assets for the release:" + ls -la out/ + - name: Create GitHub release + env: + GH_TOKEN: ${{ github.token }} + run: | + set -eu + TAG="${GITHUB_REF_NAME}" + # Idempotent: if a run is retried, update rather than fail. + if gh release view "${TAG}" >/dev/null 2>&1; then + gh release upload "${TAG}" out/* --clobber + gh release edit "${TAG}" --notes-file "${{ steps.v.outputs.notes }}" + else + gh release create "${TAG}" out/* \ + --title "Offband Meshcore ${{ steps.v.outputs.version }}" \ + --notes-file "${{ steps.v.outputs.notes }}" \ + --prerelease + fi + echo "Release ${TAG} published with $(ls out | wc -l) assets." diff --git a/CHANGELOG.md b/CHANGELOG.md index 76aaf62..b36c288 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,51 @@ All notable changes to Offband Meshcore. Pre-releases are tagged `-beta.N` / `-rc.N`. +## [1.1.2-rc.3] - 2026-07-17 + +Combined entry for rc.2 (versionCode 59) and rc.3 (versionCode 60). Both shipped +to the Play closed test with only a version bump; this backfills the changelog +they should have carried. + +### Added +- Block / mute system: block a contact or a channel sender from contacts, DMs, + discovery, or a channel message; blocked DMs and adverts are suppressed with a + visible marker, and a name-only block promotes to the sender's public key on + the next advert or DM and ages out after 30 days. Managed from Settings → + Blocked. Synced to firmware on connect where the radio supports it (#168–#180, + #172–#174, #169, #170). +- Per-channel notification modes: a 3-way selector (all / mentions / mute) on the + Channels screen, applied at the notification gate and stored keyed to the + channel PSK (#260, #261, #262). +- Path trace and topology: width-matched path tracing, a topology service and + readout, RX ingestion, and a route-builder UX that groups hops by hash width + rather than raw bytes (#150, #186, #156, #155, #224). +- Channel QR: share a channel as a scannable QR and scan one to add it, unified + with the community scanner and using the reference-app format (#163). +- Own device public key is now full, selectable, and one-tap copyable (#234). +- Reply-to chip shown above a reply-gif, with inline `@[Name]` mention chips + (#235). +- GPS refresh falls back to stock-firmware self-telemetry when the fork-specific + query is unavailable, chosen by firmware capability (#123). +- Queue-sync diagnostics: a file log of queue-drain activity plus a manual + force-drain action (#51). + +### Fixed +- USB open no longer wedges ESP32-C6 boards: the DTR open pulse is gated by USB + vendor ID, so it fires only for nRF52 reconnect and is skipped for chips it + would reset into ROM download mode (#248). +- Outgoing DMs to a blocked contact are dropped at the send path (#252). +- Contact path-length byte decodes at the device's hash width instead of being + read as a raw byte count (#224). +- Stale channel slot history is cleared on channel reassignment, and the message + store's device-key scope is aligned (#193, #194). +- Enter selects the highlighted emoji or mention in autocomplete (#238). +- Manual path entry honors the configured path-hash width (#155). + +### Changed +- The fork-only GPS query is gated on firmware capability, so stock and + non-Offband radios degrade gracefully instead of erroring (#123, #144). + ## [1.1.2-rc.1] - 2026-06-28 ### Added diff --git a/discord/README.md b/discord/README.md new file mode 100644 index 0000000..84bad3e --- /dev/null +++ b/discord/README.md @@ -0,0 +1,14 @@ +# Discord announcement + +One file per release: `discord/.md`, e.g. `discord/1.1.2-rc.4.md`. + +## Style + +Casual, paste-ready community announcement. What landed, in plain excited-but-honest +terms, with the download link. Emoji fine. This is the message the owner posts to Discord. + +**The owner pastes this into Discord manually.** CI validates the file exists and is +non-empty; it never posts. Posting to a community is an external, human-triggered action. + +The release gate (`scripts/release-gate.sh`) fails the build if this file is missing or +empty for the version being tagged. diff --git a/play/README.md b/play/README.md new file mode 100644 index 0000000..e6c339d --- /dev/null +++ b/play/README.md @@ -0,0 +1,16 @@ +# Play Console "What's new" + +One file per release: `play/.txt`, e.g. `play/1.1.2-rc.4.txt`. + +**Hard limit: 500 Unicode characters.** The release gate (`scripts/release-gate.sh`) +fails the build if this file is missing, empty, or over 500 chars — so the limit is caught +here, not as a Console rejection at upload time. + +## Style + +Plain language for end users. No issue numbers, no internal terms. Not promotional +(Google's Metadata policy forbids "free", "#1", "best", etc. and ALL-CAPS except a brand +name). + +**The owner pastes this into Play Console manually.** CI validates the file; it never +uploads. Play distribution is an external, human-triggered action. diff --git a/release-notes/README.md b/release-notes/README.md new file mode 100644 index 0000000..1d70330 --- /dev/null +++ b/release-notes/README.md @@ -0,0 +1,22 @@ +# GitHub release notes + +One file per release: `release-notes/.md`, where `` is the marketing +version from `pubspec.yaml` (the part before `+`), e.g. `release-notes/1.1.2-rc.4.md`. + +CI sets the GitHub release body from this file verbatim. It is **published directly** — +the notes are reviewed in the release-cut PR, so there is no draft step. What is in this +file at tag time is what the world sees. + +## Style + +Narrative prose, not a bulleted changelog. Written for someone deciding whether to install +or update: what's new, what got fixed, anything they should know. Full Markdown is fine. + +This is distinct from: +- **`CHANGELOG.md`** — cumulative, terse, issue-referenced, for developers. +- **`play/.txt`** — plain, ≤500 chars, for the store. +- **`discord/.md`** — casual community announcement. + +Same underlying changes, four different voices. The release gate +(`scripts/release-gate.sh`) fails the build if this file is missing or empty for the +version being tagged. From 5f01f91dbd97decac7eccc053d9fb1daa2291bc4 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 18:42:59 -0400 Subject: [PATCH 42/60] =?UTF-8?q?fix(#342):=20Gemini=20review=20=E2=80=94?= =?UTF-8?q?=20atomic=20release=20publish=20+=20verify=20the=20AAB=20cert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both findings from the adversarial review, verified before applying. 1. Draft-then-publish (was: create-then-upload). Creating the release first made it publicly visible with zero assets until uploads finished; a stalled upload or dead runner would strand a partial public release — the exact failure this pipeline exists to prevent. Now: create --draft → upload → edit --draft=false, which `gh release create --help` documents as the correct pattern. Both the create and retry branches end published, so a run retried after a mid-way death can't get stuck as a draft. gh flags verified via --help. 2. Verify the AAB certificate, not just the APK. The APK check was a proxy; the AAB is what Play actually receives. apksigner can't read an AAB, so this uses keytool -printcert -jarfile and pins the same e7da8cd5… fingerprint. Verified locally: built a debug AAB, confirmed the extraction command yields 765fb469… matching the known debug fingerprint, so the check would correctly FAIL a debug-signed AAB. Part of epic #312, plan #321. Agent: SapphireCompass (session 8d755b5e) --- .github/workflows/release-signed.yml | 51 ++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release-signed.yml b/.github/workflows/release-signed.yml index f363a11..5d6ae8b 100644 --- a/.github/workflows/release-signed.yml +++ b/.github/workflows/release-signed.yml @@ -127,7 +127,32 @@ jobs: echo "::error::Signing cert MISMATCH. expected ${EXPECTED_SHA256} actual ${ACTUAL}" exit 1 fi - echo "Signing certificate verified: ${ACTUAL}" + echo "APK signing certificate verified: ${ACTUAL}" + + # The AAB is the artifact that goes to Play, so verify IT directly rather + # than trusting the APK as a proxy. apksigner cannot read an AAB; keytool + # can, and reports the same SHA-256 fingerprint. Same pin as the APK. + # (Gemini review, #342.) + - name: Verify AAB signing certificate + env: + EXPECTED_SHA256: e7da8cd5bf22ac3eda7cb954b8b120a18b6c4382d1b6e6fdd204ddedddaf5488 + run: | + set -eu + AAB=build/app/outputs/bundle/release/app-release.aab + # keytool comes from the JDK that setup-java put on PATH in this job. + AAB_SHA=$(keytool -printcert -jarfile "${AAB}" \ + | grep -i 'SHA256:' | head -1 \ + | sed 's/.*SHA256:[[:space:]]*//' | tr -d ': ' | tr 'A-F' 'a-f') + if [ -z "${AAB_SHA}" ]; then + echo "::error::Could not read a SHA-256 from the AAB signing cert." + exit 1 + fi + if [ "${AAB_SHA}" != "${EXPECTED_SHA256}" ]; then + echo "::error::AAB signing cert MISMATCH. expected ${EXPECTED_SHA256} actual ${AAB_SHA}" + echo "::error::This AAB would be rejected by Play App Signing or break cross-channel updates." + exit 1 + fi + echo "AAB signing certificate verified: ${AAB_SHA}" - name: Stage signed artifacts run: | @@ -229,20 +254,32 @@ jobs: cp incoming/release-linux/* out/ echo "Assets for the release:" ls -la out/ - - name: Create GitHub release + # Draft-first so publication is ATOMIC: the release is not visible to + # anyone until every asset is uploaded. Creating-then-uploading would + # leave a public release with missing assets if an upload stalls or the + # runner dies mid-way — the exact partial-release failure this pipeline + # exists to prevent. `gh release create --help` documents this pattern. + # The final `--draft=false` is applied in BOTH branches so a run retried + # after a mid-way death still ends published, never stuck as a draft. + - name: Create GitHub release (draft → upload → publish) env: GH_TOKEN: ${{ github.token }} + NOTES: ${{ steps.v.outputs.notes }} + VERSION: ${{ steps.v.outputs.version }} run: | set -eu TAG="${GITHUB_REF_NAME}" - # Idempotent: if a run is retried, update rather than fail. if gh release view "${TAG}" >/dev/null 2>&1; then + # Retry / re-run: refresh assets and notes, then ensure published. gh release upload "${TAG}" out/* --clobber - gh release edit "${TAG}" --notes-file "${{ steps.v.outputs.notes }}" + gh release edit "${TAG}" --notes-file "${NOTES}" --prerelease --draft=false else - gh release create "${TAG}" out/* \ - --title "Offband Meshcore ${{ steps.v.outputs.version }}" \ - --notes-file "${{ steps.v.outputs.notes }}" \ + gh release create "${TAG}" \ + --draft \ + --title "Offband Meshcore ${VERSION}" \ + --notes-file "${NOTES}" \ --prerelease + gh release upload "${TAG}" out/* + gh release edit "${TAG}" --draft=false fi echo "Release ${TAG} published with $(ls out | wc -l) assets." From 4f8b4c373641c6d01598cf6e3fb715c299a21d4f Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 18:45:52 -0400 Subject: [PATCH 43/60] feat(#342): author rc.3 release notes (GitHub / Play / Discord) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gate introduced in this PR correctly FAILED on its own PR in 8s: pubspec is rc.3, which had a CHANGELOG section but no notes files. That is the gate working exactly as intended, demonstrated live. Fix is the work we needed regardless: author rc.3's three per-audience notes. This also seeds dev so every future dev PR passes the gate (the current version now has all four files), and the rc.3 backfill branch will carry these to build the actual release. DRAFTS for owner review — the prose is mine, the judgement is Ben's. Play blurb is 403/500 chars. Part of epic #312, plan #321. Agent: SapphireCompass (session 8d755b5e) --- discord/1.1.2-rc.3.md | 13 +++++++++++++ play/1.1.2-rc.3.txt | 9 +++++++++ release-notes/1.1.2-rc.3.md | 19 +++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 discord/1.1.2-rc.3.md create mode 100644 play/1.1.2-rc.3.txt create mode 100644 release-notes/1.1.2-rc.3.md diff --git a/discord/1.1.2-rc.3.md b/discord/1.1.2-rc.3.md new file mode 100644 index 0000000..8df3296 --- /dev/null +++ b/discord/1.1.2-rc.3.md @@ -0,0 +1,13 @@ +**Offband Meshcore 1.1.2-rc.3 is out** 📡 + +Big one this round: + +🚫 **Block & mute** — block contacts or channel senders from just about anywhere (contacts, DMs, discovery, a channel message). Blocked stuff is hidden with a marker, name-only blocks upgrade to a real key when the sender shows up again, and there's a Blocked screen in Settings to manage it all. Syncs to the radio where firmware supports it. + +🔔 **Per-channel notifications** — set each channel to all / mentions only / muted. + +🗺️ **Path trace + topology** — width-matched traces, a topology readout, and a route builder that groups hops properly. Plus share/add channels by QR. + +🔧 Fixes: USB no longer wedges some ESP32-C6 boards, DMs to a blocked contact actually stop sending, and more radios report GPS without flooding the link. + +Download for your platform below. Full changelog in the repo. diff --git a/play/1.1.2-rc.3.txt b/play/1.1.2-rc.3.txt new file mode 100644 index 0000000..1e5ae05 --- /dev/null +++ b/play/1.1.2-rc.3.txt @@ -0,0 +1,9 @@ +What's new in 1.1.2-rc.3 + +- Block or mute contacts and channel senders, with a Blocked management screen +- Per-channel notifications: all, mentions only, or muted +- Better path tracing and mesh topology readout +- Share and add channels by QR code +- Fix: USB no longer wedges some ESP32 boards on connect +- Fix: messages to a blocked contact are no longer sent +- More radios report GPS position reliably diff --git a/release-notes/1.1.2-rc.3.md b/release-notes/1.1.2-rc.3.md new file mode 100644 index 0000000..c5c07c2 --- /dev/null +++ b/release-notes/1.1.2-rc.3.md @@ -0,0 +1,19 @@ +## Offband Meshcore 1.1.2-rc.3 + +This release brings a full **block / mute system**, richer **path tracing and topology**, and **per-channel notification control**, along with a batch of connectivity and UX fixes. It combines the rc.2 and rc.3 closed-test builds. + +### Blocking and muting +You can now block a contact or a channel sender from contacts, DMs, discovery, or directly from a channel message. Blocked DMs and adverts are suppressed with a visible marker, a name-only block is promoted to the sender's public key the next time they advert or DM you, and stale blocks age out after 30 days. Everything is managed from Settings → Blocked, and where the radio's firmware supports it, your block list syncs to the device on connect. + +### Notifications, your way +Each channel gets a three-way notification selector — all messages, mentions only, or muted — applied right at the notification gate and remembered per channel. + +### Path tracing and topology +Path traces are now width-matched to the mesh, with a topology readout and a route-builder that groups hops the way the network actually addresses them rather than as raw bytes. Channel QR codes let you share a channel or add one by scanning, using the reference-app format. + +### Fixes worth calling out +- USB no longer wedges ESP32-C6 boards: the open-time DTR pulse is gated by device type, so it only fires where it's needed. +- Outgoing DMs to a blocked contact are dropped at the send path. +- GPS refresh falls back to stock-firmware telemetry when the fork-specific query isn't available, so more radios report position without flooding the link. + +See `CHANGELOG.md` for the full itemized list. From 3ff272ad8c5cb5fda796ad730301aaf64435f82b Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 20:09:21 -0400 Subject: [PATCH 44/60] fix(#343): close the data-loss holes found by Gemini review Adversarial review (standards#145) of the storage/migration surface found four real defects, all data-integrity. All confirmed against the code and fixed; no false positives. BLOCKER - concurrent save race. saveChannelMessages / saveMessages are read-modify-write with an await gap, so two saves to the same key raced and the second clobbered the first, silently losing messages (e.g. a message and its delivery ack arriving together). BlobStore now provides synchronized(key, ...), a per-key operation chain; every RMW - merge-save, remove, and the load-path legacy migration - runs through it. Different keys stay concurrent. New test fires two concurrent saves and asserts the union survives. BLOCKER - non-atomic DB relocation. The documents->support move copied and deleted each file (.sqlite/-wal/-shm) in turn, so a failure after the main file moved stranded the DB across two locations and corrupted it. Now copies all files, verifies each by size, and only then deletes the sources; on any failure it rolls back the destination and leaves the original intact. MAJOR - load-path legacy migration raced save. The #194 index->PSK adoption did a blind write to the PSK key that could clobber a save that landed first. It now runs under the key lock and MERGES (union) instead of overwriting, so both the adopted history and any fresh message survive. MINOR - channel merge key lacked a sender. Two senders posting identical text at the same timestamp without a messageId would collide and lose one. The key now includes the sender, matching MessageStore. flutter analyze clean, dart format clean, 509 tests pass. Full Gemini log in docs/llm-consultations/. --- lib/storage/channel_message_store.dart | 132 +++++++++++-------- lib/storage/drift/blob_store.dart | 20 +++ lib/storage/drift/offband_database.dart | 63 ++++++--- lib/storage/message_store.dart | 50 ++++--- test/storage/channel_message_merge_test.dart | 19 +++ 5 files changed, 190 insertions(+), 94 deletions(-) diff --git a/lib/storage/channel_message_store.dart b/lib/storage/channel_message_store.dart index 842a2d5..b80016f 100644 --- a/lib/storage/channel_message_store.dart +++ b/lib/storage/channel_message_store.dart @@ -73,43 +73,49 @@ class ChannelMessageStore { // Deletion has its own path (removeChannelMessage) so this never // resurrects a message the user deleted. final key = _storageKey(channelIndex); - final byKey = {}; - - final existing = await BlobStore.instance.readWithPrefsFallback(key); - if (existing != null && existing.isNotEmpty) { - try { - for (final e in jsonDecode(existing) as List) { - final m = _messageFromJson(e as Map); - byKey[_mergeKey(m)] = m; + final blobs = BlobStore.instance; + // Serialise the whole read-modify-write against other saves/deletes on this + // key so two concurrent saves cannot clobber each other (Gemini review). + await blobs.synchronized(key, () async { + final byKey = {}; + final existing = await blobs.readWithPrefsFallback(key); + if (existing != null && existing.isNotEmpty) { + try { + for (final e in jsonDecode(existing) as List) { + final m = _messageFromJson(e as Map); + byKey[_mergeKey(m)] = m; + } + } catch (e) { + // SAFELANE 6: never merge into an empty base and truncate silently. + appLogger.error( + 'Failed to decode existing channel $channelIndex history before ' + 'merge; aborting save to avoid truncation: $e', + tag: 'Storage', + ); + return; } - } catch (e) { - // SAFELANE 6: a decode failure here must be loud, not silently drop the - // persisted history by merging into an empty base. - appLogger.error( - 'Failed to decode existing channel $channelIndex history before ' - 'merge; aborting save to avoid truncation: $e', - tag: 'Storage', - ); - return; } - } - for (final m in messages) { - byKey[_mergeKey(m)] = m; - } - - final merged = byKey.values.toList() - ..sort((a, b) => a.timestamp.compareTo(b.timestamp)); - await BlobStore.instance.write( - key, - jsonEncode(merged.map(_messageToJson).toList()), - ); + for (final m in messages) { + byKey[_mergeKey(m)] = m; + } + final merged = byKey.values.toList() + ..sort((a, b) => a.timestamp.compareTo(b.timestamp)); + await blobs.write(key, jsonEncode(merged.map(_messageToJson).toList())); + }); } /// Stable identity for merge/dedupe. messageId when present, else a composite /// that distinguishes distinct messages that share no id. String _mergeKey(ChannelMessage m) { if (m.messageId.isNotEmpty) return 'id:${m.messageId}'; - return 'x:${m.packetHash ?? ''}:${m.timestamp.millisecondsSinceEpoch}:${m.text}'; + // Include the sender: two different senders can post identical text at the + // same timestamp without a messageId, and would otherwise collide and lose + // one (Gemini review, 2026-07-20). + final sender = m.senderKey == null + ? '' + : m.senderKey!.map((b) => b.toRadixString(16)).join(); + return 'x:$sender:${m.packetHash ?? ''}:' + '${m.timestamp.millisecondsSinceEpoch}:${m.text}'; } /// Removes a single message from the persisted history. The explicit delete @@ -121,28 +127,27 @@ class ChannelMessageStore { ) async { if (publicKeyHex.isEmpty) return; final key = _storageKey(channelIndex); - final existing = await BlobStore.instance.readWithPrefsFallback(key); - if (existing == null || existing.isEmpty) return; - - final List raw; - try { - raw = jsonDecode(existing) as List; - } catch (e) { - appLogger.error( - 'Failed to decode channel $channelIndex history for delete: $e', - tag: 'Storage', - ); - return; - } - final target = _mergeKey(message); - final kept = raw - .map((e) => _messageFromJson(e as Map)) - .where((m) => _mergeKey(m) != target) - .toList(); - await BlobStore.instance.write( - key, - jsonEncode(kept.map(_messageToJson).toList()), - ); + final blobs = BlobStore.instance; + await blobs.synchronized(key, () async { + final existing = await blobs.readWithPrefsFallback(key); + if (existing == null || existing.isEmpty) return; + final List raw; + try { + raw = jsonDecode(existing) as List; + } catch (e) { + appLogger.error( + 'Failed to decode channel $channelIndex history for delete: $e', + tag: 'Storage', + ); + return; + } + final target = _mergeKey(message); + final kept = raw + .map((e) => _messageFromJson(e as Map)) + .where((m) => _mergeKey(m) != target) + .toList(); + await blobs.write(key, jsonEncode(kept.map(_messageToJson).toList())); + }); } /// Load messages for a specific channel @@ -177,9 +182,30 @@ class ChannelMessageStore { appLogger.info( 'Migrating channel messages $legacyKey -> $key (PSK-keyed, #194)', ); - await blobs.write(key, legacy); + // Under the key lock, and MERGE rather than overwrite: a save may + // have landed on the PSK key between the read above and here, and a + // blind write would clobber it (Gemini review). Union keeps both the + // adopted legacy history and any freshly-saved message. + jsonString = await blobs.synchronized(key, () async { + final byKey = {}; + for (final srcJson in [await blobs.read(key), legacy]) { + if (srcJson == null || srcJson.isEmpty) continue; + try { + for (final e in jsonDecode(srcJson) as List) { + final m = _messageFromJson(e as Map); + byKey[_mergeKey(m)] = m; + } + } catch (_) { + // Skip an undecodable source rather than aborting the adoption. + } + } + final merged = byKey.values.toList() + ..sort((a, b) => a.timestamp.compareTo(b.timestamp)); + final encoded = jsonEncode(merged.map(_messageToJson).toList()); + await blobs.write(key, encoded); + return encoded; + }); await blobs.deleteEverywhere(legacyKey); - jsonString = legacy; break; } } diff --git a/lib/storage/drift/blob_store.dart b/lib/storage/drift/blob_store.dart index 8239c72..c8ccf9f 100644 --- a/lib/storage/drift/blob_store.dart +++ b/lib/storage/drift/blob_store.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/foundation.dart'; import '../../utils/app_logger.dart'; @@ -46,6 +48,24 @@ class BlobStore { static bool isBulkKey(String key) => migratedPrefixes.any(key.startsWith); + /// Per-key operation chain. Merge-on-save and the legacy-key migration are + /// read-modify-write sequences with an await gap; two of them racing on the + /// same key would let the second clobber the first and silently lose + /// messages (Gemini review, 2026-07-20). Every RMW on a key runs through + /// [synchronized], which serialises operations per key while leaving + /// different keys concurrent. + final Map> _keyChains = {}; + + /// Serialises [action] against other synchronized actions on the same [key]. + Future synchronized(String key, Future Function() action) { + final prior = _keyChains[key] ?? Future.value(); + final result = prior.then((_) => action()); + // Next op waits for this one; swallow errors so one failure does not wedge + // the chain for the key. + _keyChains[key] = result.then((_) {}, onError: (_) {}); + return result; + } + /// Reads a bulk key, falling back to SharedPreferences if drift does not /// have it. /// diff --git a/lib/storage/drift/offband_database.dart b/lib/storage/drift/offband_database.dart index 2d14954..2d7d209 100644 --- a/lib/storage/drift/offband_database.dart +++ b/lib/storage/drift/offband_database.dart @@ -76,26 +76,59 @@ class OffbandDatabase extends _$OffbandDatabase { final docs = await getApplicationDocumentsDirectory(); final oldPath = p.join(docs.path, '$_dbName.sqlite'); if (File(oldPath).existsSync() && oldPath != newPath) { - for (final suffix in ['', '-wal', '-shm']) { - final src = File('$oldPath$suffix'); - if (src.existsSync()) { - src.copySync('$newPath$suffix'); - src.deleteSync(); + // Copy ALL files first, verify, and only then delete the sources. + // Deleting per-file mid-loop (Gemini review) could strand the main + // .sqlite in one place and its -wal in another if a later copy + // failed, corrupting the DB. Copy-all-then-delete-all makes a + // partial failure recoverable: the original stays intact and the + // half-written destination is cleaned up. + final suffixes = ['', '-wal', '-shm']; + final present = suffixes + .where((s) => File('$oldPath$s').existsSync()) + .toList(); + try { + for (final s in present) { + File('$oldPath$s').copySync('$newPath$s'); } + // Verify every copy exists with a matching size before deleting. + for (final s in present) { + final src = File('$oldPath$s'); + final dst = File('$newPath$s'); + if (!dst.existsSync() || dst.lengthSync() != src.lengthSync()) { + throw StateError('copy verification failed for $newPath$s'); + } + } + for (final s in present) { + File('$oldPath$s').deleteSync(); + } + appLogger.info( + 'Relocated drift DB out of the documents dir (OneDrive on ' + 'Windows) into the app-support dir: $oldPath -> $newPath', + tag: 'Storage', + ); + } catch (e) { + // Roll back any partial destination so drift does not open a + // half-copied DB; the original in the documents dir is untouched. + for (final s in present) { + final dst = File('$newPath$s'); + if (dst.existsSync()) { + try { + dst.deleteSync(); + } catch (_) {} + } + } + rethrow; } - appLogger.info( - 'Relocated drift DB out of the documents dir (OneDrive on ' - 'Windows) into the app-support dir: $oldPath -> $newPath', - tag: 'Storage', - ); } } catch (e) { - // Relocation is best-effort: if it fails, drift opens a fresh DB at the - // correct location and the migration re-runs from SharedPreferences. - // Loud, never silent (SAFELANE 6). + // Relocation is best-effort. On failure the ORIGINAL DB is left intact + // in the documents dir (the destination was rolled back), so no data is + // lost - drift opens a fresh DB at the support location and the user's + // history remains recoverable from the old path. Loud, never silent. appLogger.error( - 'Failed to relocate the drift DB from the documents dir: $e. ' - 'A fresh DB will be created at the app-support location.', + 'Failed to relocate the drift DB from the documents dir: $e. The ' + 'original at the old path is intact; a fresh DB will open at the ' + 'app-support location. Recover the old DB manually if needed.', tag: 'Storage', ); } diff --git a/lib/storage/message_store.dart b/lib/storage/message_store.dart index b912d0e..37b0575 100644 --- a/lib/storage/message_store.dart +++ b/lib/storage/message_store.dart @@ -29,34 +29,32 @@ class MessageStore { // truncate the store. Upsert by identity; deletion is explicit // (removeMessage). final key = '$keyFor$contactKeyHex'; - final byKey = {}; - - final existing = await BlobStore.instance.readWithPrefsFallback(key); - if (existing != null && existing.isNotEmpty) { - try { - for (final e in jsonDecode(existing) as List) { - final m = _messageFromJson(e as Map); - byKey[_mergeKey(m)] = m; + final blobs = BlobStore.instance; + await blobs.synchronized(key, () async { + final byKey = {}; + final existing = await blobs.readWithPrefsFallback(key); + if (existing != null && existing.isNotEmpty) { + try { + for (final e in jsonDecode(existing) as List) { + final m = _messageFromJson(e as Map); + byKey[_mergeKey(m)] = m; + } + } catch (e) { + appLogger.error( + 'Failed to decode existing DM history before merge; aborting save ' + 'to avoid truncation: $e', + tag: 'Storage', + ); + return; } - } catch (e) { - appLogger.error( - 'Failed to decode existing DM history before merge; aborting save ' - 'to avoid truncation: $e', - tag: 'Storage', - ); - return; } - } - for (final m in messages) { - byKey[_mergeKey(m)] = m; - } - - final merged = byKey.values.toList() - ..sort((a, b) => a.timestamp.compareTo(b.timestamp)); - await BlobStore.instance.write( - key, - jsonEncode(merged.map(_messageToJson).toList()), - ); + for (final m in messages) { + byKey[_mergeKey(m)] = m; + } + final merged = byKey.values.toList() + ..sort((a, b) => a.timestamp.compareTo(b.timestamp)); + await blobs.write(key, jsonEncode(merged.map(_messageToJson).toList())); + }); } String _mergeKey(Message m) { diff --git a/test/storage/channel_message_merge_test.dart b/test/storage/channel_message_merge_test.dart index 83f6144..3cba6e2 100644 --- a/test/storage/channel_message_merge_test.dart +++ b/test/storage/channel_message_merge_test.dart @@ -79,6 +79,25 @@ void main() { expect(all.single.reactions['thumbsup'], 2); }); + test( + 'concurrent saves to one channel do not lose messages (Gemini)', + () async { + await store.saveChannelMessages(0, [msg(0)]); + // Fire two saves without awaiting between them: they race on the same key. + // Serialisation must make the result the union, not last-writer-wins. + final a = store.saveChannelMessages(0, [msg(1)]); + final b = store.saveChannelMessages(0, [msg(2)]); + await Future.wait([a, b]); + final all = await store.loadChannelMessages(0); + expect( + all.map((m) => m.text), + containsAll(['m0', 'm1', 'm2']), + reason: 'neither concurrent save may clobber the other', + ); + expect(all, hasLength(3)); + }, + ); + test('deleting a message does NOT resurrect it on the next save', () async { await store.saveChannelMessages(0, [for (var i = 0; i < 5; i++) msg(i)]); From 53f87f51e3b71b6f4db5ca8733a4f7e415fe339e Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 20:18:41 -0400 Subject: [PATCH 45/60] build(#335): commit the drift generated code so CI can analyze/build CI failed on every job: offband_database.g.dart (drift's build_runner output) was gitignored and no workflow runs build_runner, so analyze and all six platform builds hit "Target of URI hasn't been generated". drift is the first code in this repo needing codegen, which is why dev's CI never had to. The workflows are not matrixed - six separate build jobs plus analyze, web deploy and release-signed, each with its own `flutter pub get`. Adding a codegen step to all of them is wide and easy to miss one. Instead the generated file is committed (drift supports this): one file, works across every job and workflow with no CI edits, and reproducible. drift_dev is pinned (2.34.0) alongside the already-pinned drift, so a future regeneration produces the same file rather than drifting from the committed copy. Regenerated against the pinned versions before committing. Follow-up worth having: a CI check that regenerates and diffs, so a schema change without regen fails loudly rather than shipping a stale .g.dart. --- .gitignore | 3 + lib/storage/drift/offband_database.g.dart | 445 ++++++++++++++++++++++ pubspec.yaml | 2 +- 3 files changed, 449 insertions(+), 1 deletion(-) create mode 100644 lib/storage/drift/offband_database.g.dart diff --git a/.gitignore b/.gitignore index 31e840c..a2b2a17 100644 --- a/.gitignore +++ b/.gitignore @@ -93,3 +93,6 @@ keystore.properties # Local build secrets (Giphy etc.) — injected via --dart-define-from-file dart_defines.json + +# drift generated code is committed so CI needs no build_runner step (#335) +!/lib/storage/drift/offband_database.g.dart diff --git a/lib/storage/drift/offband_database.g.dart b/lib/storage/drift/offband_database.g.dart new file mode 100644 index 0000000..38c23ac --- /dev/null +++ b/lib/storage/drift/offband_database.g.dart @@ -0,0 +1,445 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'offband_database.dart'; + +// ignore_for_file: type=lint +class $StoredBlobsTable extends StoredBlobs + with TableInfo<$StoredBlobsTable, StoredBlob> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $StoredBlobsTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _keyMeta = const VerificationMeta('key'); + @override + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _valueMeta = const VerificationMeta('value'); + @override + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _updatedAtMeta = const VerificationMeta( + 'updatedAt', + ); + @override + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: currentDateAndTime, + ); + @override + List get $columns => [key, value, updatedAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stored_blobs'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('key')) { + context.handle( + _keyMeta, + key.isAcceptableOrUnknown(data['key']!, _keyMeta), + ); + } else if (isInserting) { + context.missing(_keyMeta); + } + if (data.containsKey('value')) { + context.handle( + _valueMeta, + value.isAcceptableOrUnknown(data['value']!, _valueMeta), + ); + } else if (isInserting) { + context.missing(_valueMeta); + } + if (data.containsKey('updated_at')) { + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {key}; + @override + StoredBlob map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StoredBlob( + key: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}value'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ); + } + + @override + $StoredBlobsTable createAlias(String alias) { + return $StoredBlobsTable(attachedDatabase, alias); + } +} + +class StoredBlob extends DataClass implements Insertable { + final String key; + final String value; + final DateTime updatedAt; + const StoredBlob({ + required this.key, + required this.value, + required this.updatedAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['key'] = Variable(key); + map['value'] = Variable(value); + map['updated_at'] = Variable(updatedAt); + return map; + } + + StoredBlobsCompanion toCompanion(bool nullToAbsent) { + return StoredBlobsCompanion( + key: Value(key), + value: Value(value), + updatedAt: Value(updatedAt), + ); + } + + factory StoredBlob.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StoredBlob( + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + updatedAt: serializer.fromJson(json['updatedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + 'updatedAt': serializer.toJson(updatedAt), + }; + } + + StoredBlob copyWith({String? key, String? value, DateTime? updatedAt}) => + StoredBlob( + key: key ?? this.key, + value: value ?? this.value, + updatedAt: updatedAt ?? this.updatedAt, + ); + StoredBlob copyWithCompanion(StoredBlobsCompanion data) { + return StoredBlob( + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ); + } + + @override + String toString() { + return (StringBuffer('StoredBlob(') + ..write('key: $key, ') + ..write('value: $value, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(key, value, updatedAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StoredBlob && + other.key == this.key && + other.value == this.value && + other.updatedAt == this.updatedAt); +} + +class StoredBlobsCompanion extends UpdateCompanion { + final Value key; + final Value value; + final Value updatedAt; + final Value rowid; + const StoredBlobsCompanion({ + this.key = const Value.absent(), + this.value = const Value.absent(), + this.updatedAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + StoredBlobsCompanion.insert({ + required String key, + required String value, + this.updatedAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? key, + Expression? value, + Expression? updatedAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (key != null) 'key': key, + if (value != null) 'value': value, + if (updatedAt != null) 'updated_at': updatedAt, + if (rowid != null) 'rowid': rowid, + }); + } + + StoredBlobsCompanion copyWith({ + Value? key, + Value? value, + Value? updatedAt, + Value? rowid, + }) { + return StoredBlobsCompanion( + key: key ?? this.key, + value: value ?? this.value, + updatedAt: updatedAt ?? this.updatedAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StoredBlobsCompanion(') + ..write('key: $key, ') + ..write('value: $value, ') + ..write('updatedAt: $updatedAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +abstract class _$OffbandDatabase extends GeneratedDatabase { + _$OffbandDatabase(QueryExecutor e) : super(e); + $OffbandDatabaseManager get managers => $OffbandDatabaseManager(this); + late final $StoredBlobsTable storedBlobs = $StoredBlobsTable(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [storedBlobs]; +} + +typedef $$StoredBlobsTableCreateCompanionBuilder = + StoredBlobsCompanion Function({ + required String key, + required String value, + Value updatedAt, + Value rowid, + }); +typedef $$StoredBlobsTableUpdateCompanionBuilder = + StoredBlobsCompanion Function({ + Value key, + Value value, + Value updatedAt, + Value rowid, + }); + +class $$StoredBlobsTableFilterComposer + extends Composer<_$OffbandDatabase, $StoredBlobsTable> { + $$StoredBlobsTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get key => $composableBuilder( + column: $table.key, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get value => $composableBuilder( + column: $table.value, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get updatedAt => $composableBuilder( + column: $table.updatedAt, + builder: (column) => ColumnFilters(column), + ); +} + +class $$StoredBlobsTableOrderingComposer + extends Composer<_$OffbandDatabase, $StoredBlobsTable> { + $$StoredBlobsTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get key => $composableBuilder( + column: $table.key, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get value => $composableBuilder( + column: $table.value, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get updatedAt => $composableBuilder( + column: $table.updatedAt, + builder: (column) => ColumnOrderings(column), + ); +} + +class $$StoredBlobsTableAnnotationComposer + extends Composer<_$OffbandDatabase, $StoredBlobsTable> { + $$StoredBlobsTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get key => + $composableBuilder(column: $table.key, builder: (column) => column); + + GeneratedColumn get value => + $composableBuilder(column: $table.value, builder: (column) => column); + + GeneratedColumn get updatedAt => + $composableBuilder(column: $table.updatedAt, builder: (column) => column); +} + +class $$StoredBlobsTableTableManager + extends + RootTableManager< + _$OffbandDatabase, + $StoredBlobsTable, + StoredBlob, + $$StoredBlobsTableFilterComposer, + $$StoredBlobsTableOrderingComposer, + $$StoredBlobsTableAnnotationComposer, + $$StoredBlobsTableCreateCompanionBuilder, + $$StoredBlobsTableUpdateCompanionBuilder, + ( + StoredBlob, + BaseReferences<_$OffbandDatabase, $StoredBlobsTable, StoredBlob>, + ), + StoredBlob, + PrefetchHooks Function() + > { + $$StoredBlobsTableTableManager(_$OffbandDatabase db, $StoredBlobsTable table) + : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$StoredBlobsTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$StoredBlobsTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$StoredBlobsTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value key = const Value.absent(), + Value value = const Value.absent(), + Value updatedAt = const Value.absent(), + Value rowid = const Value.absent(), + }) => StoredBlobsCompanion( + key: key, + value: value, + updatedAt: updatedAt, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String key, + required String value, + Value updatedAt = const Value.absent(), + Value rowid = const Value.absent(), + }) => StoredBlobsCompanion.insert( + key: key, + value: value, + updatedAt: updatedAt, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map((e) => (e.readTable(table), BaseReferences(db, table, e))) + .toList(), + prefetchHooksCallback: null, + ), + ); +} + +typedef $$StoredBlobsTableProcessedTableManager = + ProcessedTableManager< + _$OffbandDatabase, + $StoredBlobsTable, + StoredBlob, + $$StoredBlobsTableFilterComposer, + $$StoredBlobsTableOrderingComposer, + $$StoredBlobsTableAnnotationComposer, + $$StoredBlobsTableCreateCompanionBuilder, + $$StoredBlobsTableUpdateCompanionBuilder, + ( + StoredBlob, + BaseReferences<_$OffbandDatabase, $StoredBlobsTable, StoredBlob>, + ), + StoredBlob, + PrefetchHooks Function() + >; + +class $OffbandDatabaseManager { + final _$OffbandDatabase _db; + $OffbandDatabaseManager(this._db); + $$StoredBlobsTableTableManager get storedBlobs => + $$StoredBlobsTableTableManager(_db, _db.storedBlobs); +} diff --git a/pubspec.yaml b/pubspec.yaml index d385dd6..3a9ab39 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -102,7 +102,7 @@ dev_dependencies: # rules and activating additional ones. flutter_lints: ^6.0.0 flutter_launcher_icons: ^0.14.4 - drift_dev: ^2.34.0 + drift_dev: 2.34.0 build_runner: ^2.15.1 # For information on the generic Dart part of this file, see the From b7584bd0c233895ee6d9d83dca36018be7584731 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 20:58:29 -0400 Subject: [PATCH 46/60] chore(#352): generate drift code in CI instead of committing it Replaces the stopgap (53f87f5) that committed lib/storage/drift/offband_database.g.dart to green CI. Restores the repo's "generated files are not committed" convention: - dropped the .gitignore negation; the .g.dart is gitignored + untracked (git rm --cached, working copy kept) - added `dart run build_runner build --delete-conflicting-outputs` immediately after `flutter pub get` in every Dart job: flutter_dart (analyze), build.yml (all 6), release-signed (android-signed), deploy-web (before build_pipe) - keeps the drift_dev 2.34.0 / drift 2.34.2 / build_runner ^2.15.1 pins from the stopgap for deterministic generation Verified locally on the bench toolchain (3.44.1): deleted the working .g.dart, ran build_runner from scratch (regenerated cleanly), confirmed the generated file passes `dart format --set-exit-if-changed` and full `flutter analyze --fatal-infos --fatal-warnings` is clean with the file generated (not tracked). For #335 / PR #348. Agent: SapphireCompass (session 8d755b5e) --- .github/workflows/build.yml | 6 + .github/workflows/deploy-web.yml | 1 + .github/workflows/flutter_dart.yml | 3 + .github/workflows/release-signed.yml | 1 + .gitignore | 3 - lib/storage/drift/offband_database.g.dart | 445 ---------------------- 6 files changed, 11 insertions(+), 448 deletions(-) delete mode 100644 lib/storage/drift/offband_database.g.dart diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 238f426..1c1ba57 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,6 +30,7 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - run: flutter pub get + - run: dart run build_runner build --delete-conflicting-outputs - name: Write dart_defines.json env: GIPHY_API_KEY: ${{ secrets.GIPHY_API_KEY }} @@ -52,6 +53,7 @@ jobs: flutter-version: "3.44.1" cache: true - run: flutter pub get + - run: dart run build_runner build --delete-conflicting-outputs - run: flutter build ios --release --no-codesign --no-pub linux: @@ -65,6 +67,7 @@ jobs: - name: Install Linux build deps run: sudo apt-get update && sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev - run: flutter pub get + - run: dart run build_runner build --delete-conflicting-outputs - name: Write dart_defines.json env: GIPHY_API_KEY: ${{ secrets.GIPHY_API_KEY }} @@ -87,6 +90,7 @@ jobs: flutter-version: "3.44.1" cache: true - run: flutter pub get + - run: dart run build_runner build --delete-conflicting-outputs - run: flutter build macos --release --no-pub web: @@ -98,6 +102,7 @@ jobs: flutter-version: "3.44.1" cache: true - run: flutter pub get + - run: dart run build_runner build --delete-conflicting-outputs - name: Write dart_defines.json env: GIPHY_API_KEY: ${{ secrets.GIPHY_API_KEY }} @@ -120,6 +125,7 @@ jobs: flutter-version: "3.44.1" cache: true - run: flutter pub get + - run: dart run build_runner build --delete-conflicting-outputs # Windows runner defaults to PowerShell; jq is not guaranteed there, # so build the JSON with ConvertTo-Json, which escapes correctly. - name: Write dart_defines.json diff --git a/.github/workflows/deploy-web.yml b/.github/workflows/deploy-web.yml index 21a5c8a..3fb45c9 100644 --- a/.github/workflows/deploy-web.yml +++ b/.github/workflows/deploy-web.yml @@ -50,6 +50,7 @@ jobs: cache: true - run: flutter pub get + - run: dart run build_runner build --delete-conflicting-outputs # Same mechanism as build.yml (#331). build_pipe's build_command in # pubspec.yaml passes --dart-define-from-file, so this file must exist diff --git a/.github/workflows/flutter_dart.yml b/.github/workflows/flutter_dart.yml index bff800d..4076ea5 100644 --- a/.github/workflows/flutter_dart.yml +++ b/.github/workflows/flutter_dart.yml @@ -22,6 +22,9 @@ jobs: - name: Install dependencies run: flutter pub get + - name: Generate code (drift / build_runner) + run: dart run build_runner build --delete-conflicting-outputs + - name: Analyze code run: flutter analyze --fatal-infos --fatal-warnings diff --git a/.github/workflows/release-signed.yml b/.github/workflows/release-signed.yml index 6a255b0..2f1b806 100644 --- a/.github/workflows/release-signed.yml +++ b/.github/workflows/release-signed.yml @@ -100,6 +100,7 @@ jobs: echo "Keystore decoded (${SIZE} bytes), key.properties written." - run: flutter pub get + - run: dart run build_runner build --delete-conflicting-outputs # Same mechanism as build.yml (#331). A signed release that shipped # without the Giphy key would have a dead GIF picker. diff --git a/.gitignore b/.gitignore index a2b2a17..31e840c 100644 --- a/.gitignore +++ b/.gitignore @@ -93,6 +93,3 @@ keystore.properties # Local build secrets (Giphy etc.) — injected via --dart-define-from-file dart_defines.json - -# drift generated code is committed so CI needs no build_runner step (#335) -!/lib/storage/drift/offband_database.g.dart diff --git a/lib/storage/drift/offband_database.g.dart b/lib/storage/drift/offband_database.g.dart deleted file mode 100644 index 38c23ac..0000000 --- a/lib/storage/drift/offband_database.g.dart +++ /dev/null @@ -1,445 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'offband_database.dart'; - -// ignore_for_file: type=lint -class $StoredBlobsTable extends StoredBlobs - with TableInfo<$StoredBlobsTable, StoredBlob> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $StoredBlobsTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _keyMeta = const VerificationMeta('key'); - @override - late final GeneratedColumn key = GeneratedColumn( - 'key', - aliasedName, - false, - type: DriftSqlType.string, - requiredDuringInsert: true, - ); - static const VerificationMeta _valueMeta = const VerificationMeta('value'); - @override - late final GeneratedColumn value = GeneratedColumn( - 'value', - aliasedName, - false, - type: DriftSqlType.string, - requiredDuringInsert: true, - ); - static const VerificationMeta _updatedAtMeta = const VerificationMeta( - 'updatedAt', - ); - @override - late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', - aliasedName, - false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: currentDateAndTime, - ); - @override - List get $columns => [key, value, updatedAt]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'stored_blobs'; - @override - VerificationContext validateIntegrity( - Insertable instance, { - bool isInserting = false, - }) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('key')) { - context.handle( - _keyMeta, - key.isAcceptableOrUnknown(data['key']!, _keyMeta), - ); - } else if (isInserting) { - context.missing(_keyMeta); - } - if (data.containsKey('value')) { - context.handle( - _valueMeta, - value.isAcceptableOrUnknown(data['value']!, _valueMeta), - ); - } else if (isInserting) { - context.missing(_valueMeta); - } - if (data.containsKey('updated_at')) { - context.handle( - _updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), - ); - } - return context; - } - - @override - Set get $primaryKey => {key}; - @override - StoredBlob map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return StoredBlob( - key: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}key'], - )!, - value: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}value'], - )!, - updatedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, - data['${effectivePrefix}updated_at'], - )!, - ); - } - - @override - $StoredBlobsTable createAlias(String alias) { - return $StoredBlobsTable(attachedDatabase, alias); - } -} - -class StoredBlob extends DataClass implements Insertable { - final String key; - final String value; - final DateTime updatedAt; - const StoredBlob({ - required this.key, - required this.value, - required this.updatedAt, - }); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['key'] = Variable(key); - map['value'] = Variable(value); - map['updated_at'] = Variable(updatedAt); - return map; - } - - StoredBlobsCompanion toCompanion(bool nullToAbsent) { - return StoredBlobsCompanion( - key: Value(key), - value: Value(value), - updatedAt: Value(updatedAt), - ); - } - - factory StoredBlob.fromJson( - Map json, { - ValueSerializer? serializer, - }) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return StoredBlob( - key: serializer.fromJson(json['key']), - value: serializer.fromJson(json['value']), - updatedAt: serializer.fromJson(json['updatedAt']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'key': serializer.toJson(key), - 'value': serializer.toJson(value), - 'updatedAt': serializer.toJson(updatedAt), - }; - } - - StoredBlob copyWith({String? key, String? value, DateTime? updatedAt}) => - StoredBlob( - key: key ?? this.key, - value: value ?? this.value, - updatedAt: updatedAt ?? this.updatedAt, - ); - StoredBlob copyWithCompanion(StoredBlobsCompanion data) { - return StoredBlob( - key: data.key.present ? data.key.value : this.key, - value: data.value.present ? data.value.value : this.value, - updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - ); - } - - @override - String toString() { - return (StringBuffer('StoredBlob(') - ..write('key: $key, ') - ..write('value: $value, ') - ..write('updatedAt: $updatedAt') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(key, value, updatedAt); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is StoredBlob && - other.key == this.key && - other.value == this.value && - other.updatedAt == this.updatedAt); -} - -class StoredBlobsCompanion extends UpdateCompanion { - final Value key; - final Value value; - final Value updatedAt; - final Value rowid; - const StoredBlobsCompanion({ - this.key = const Value.absent(), - this.value = const Value.absent(), - this.updatedAt = const Value.absent(), - this.rowid = const Value.absent(), - }); - StoredBlobsCompanion.insert({ - required String key, - required String value, - this.updatedAt = const Value.absent(), - this.rowid = const Value.absent(), - }) : key = Value(key), - value = Value(value); - static Insertable custom({ - Expression? key, - Expression? value, - Expression? updatedAt, - Expression? rowid, - }) { - return RawValuesInsertable({ - if (key != null) 'key': key, - if (value != null) 'value': value, - if (updatedAt != null) 'updated_at': updatedAt, - if (rowid != null) 'rowid': rowid, - }); - } - - StoredBlobsCompanion copyWith({ - Value? key, - Value? value, - Value? updatedAt, - Value? rowid, - }) { - return StoredBlobsCompanion( - key: key ?? this.key, - value: value ?? this.value, - updatedAt: updatedAt ?? this.updatedAt, - rowid: rowid ?? this.rowid, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (key.present) { - map['key'] = Variable(key.value); - } - if (value.present) { - map['value'] = Variable(value.value); - } - if (updatedAt.present) { - map['updated_at'] = Variable(updatedAt.value); - } - if (rowid.present) { - map['rowid'] = Variable(rowid.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('StoredBlobsCompanion(') - ..write('key: $key, ') - ..write('value: $value, ') - ..write('updatedAt: $updatedAt, ') - ..write('rowid: $rowid') - ..write(')')) - .toString(); - } -} - -abstract class _$OffbandDatabase extends GeneratedDatabase { - _$OffbandDatabase(QueryExecutor e) : super(e); - $OffbandDatabaseManager get managers => $OffbandDatabaseManager(this); - late final $StoredBlobsTable storedBlobs = $StoredBlobsTable(this); - @override - Iterable> get allTables => - allSchemaEntities.whereType>(); - @override - List get allSchemaEntities => [storedBlobs]; -} - -typedef $$StoredBlobsTableCreateCompanionBuilder = - StoredBlobsCompanion Function({ - required String key, - required String value, - Value updatedAt, - Value rowid, - }); -typedef $$StoredBlobsTableUpdateCompanionBuilder = - StoredBlobsCompanion Function({ - Value key, - Value value, - Value updatedAt, - Value rowid, - }); - -class $$StoredBlobsTableFilterComposer - extends Composer<_$OffbandDatabase, $StoredBlobsTable> { - $$StoredBlobsTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnFilters get key => $composableBuilder( - column: $table.key, - builder: (column) => ColumnFilters(column), - ); - - ColumnFilters get value => $composableBuilder( - column: $table.value, - builder: (column) => ColumnFilters(column), - ); - - ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => ColumnFilters(column), - ); -} - -class $$StoredBlobsTableOrderingComposer - extends Composer<_$OffbandDatabase, $StoredBlobsTable> { - $$StoredBlobsTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnOrderings get key => $composableBuilder( - column: $table.key, - builder: (column) => ColumnOrderings(column), - ); - - ColumnOrderings get value => $composableBuilder( - column: $table.value, - builder: (column) => ColumnOrderings(column), - ); - - ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => ColumnOrderings(column), - ); -} - -class $$StoredBlobsTableAnnotationComposer - extends Composer<_$OffbandDatabase, $StoredBlobsTable> { - $$StoredBlobsTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get key => - $composableBuilder(column: $table.key, builder: (column) => column); - - GeneratedColumn get value => - $composableBuilder(column: $table.value, builder: (column) => column); - - GeneratedColumn get updatedAt => - $composableBuilder(column: $table.updatedAt, builder: (column) => column); -} - -class $$StoredBlobsTableTableManager - extends - RootTableManager< - _$OffbandDatabase, - $StoredBlobsTable, - StoredBlob, - $$StoredBlobsTableFilterComposer, - $$StoredBlobsTableOrderingComposer, - $$StoredBlobsTableAnnotationComposer, - $$StoredBlobsTableCreateCompanionBuilder, - $$StoredBlobsTableUpdateCompanionBuilder, - ( - StoredBlob, - BaseReferences<_$OffbandDatabase, $StoredBlobsTable, StoredBlob>, - ), - StoredBlob, - PrefetchHooks Function() - > { - $$StoredBlobsTableTableManager(_$OffbandDatabase db, $StoredBlobsTable table) - : super( - TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$StoredBlobsTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$StoredBlobsTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$StoredBlobsTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: - ({ - Value key = const Value.absent(), - Value value = const Value.absent(), - Value updatedAt = const Value.absent(), - Value rowid = const Value.absent(), - }) => StoredBlobsCompanion( - key: key, - value: value, - updatedAt: updatedAt, - rowid: rowid, - ), - createCompanionCallback: - ({ - required String key, - required String value, - Value updatedAt = const Value.absent(), - Value rowid = const Value.absent(), - }) => StoredBlobsCompanion.insert( - key: key, - value: value, - updatedAt: updatedAt, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map((e) => (e.readTable(table), BaseReferences(db, table, e))) - .toList(), - prefetchHooksCallback: null, - ), - ); -} - -typedef $$StoredBlobsTableProcessedTableManager = - ProcessedTableManager< - _$OffbandDatabase, - $StoredBlobsTable, - StoredBlob, - $$StoredBlobsTableFilterComposer, - $$StoredBlobsTableOrderingComposer, - $$StoredBlobsTableAnnotationComposer, - $$StoredBlobsTableCreateCompanionBuilder, - $$StoredBlobsTableUpdateCompanionBuilder, - ( - StoredBlob, - BaseReferences<_$OffbandDatabase, $StoredBlobsTable, StoredBlob>, - ), - StoredBlob, - PrefetchHooks Function() - >; - -class $OffbandDatabaseManager { - final _$OffbandDatabase _db; - $OffbandDatabaseManager(this._db); - $$StoredBlobsTableTableManager get storedBlobs => - $$StoredBlobsTableTableManager(_db, _db.storedBlobs); -} From c347966825fbf035f868bfd7e8a75732ea0d7b73 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 21:06:10 -0400 Subject: [PATCH 47/60] chore(#352): revert release-signed.yml codegen (belongs on dev's version) My previous commit added the codegen step to this branch's OLD android-signed release-signed.yml. But dev has since replaced that file with a 5-job pipeline (#342/#346), so editing the stale copy made both sides diverge -> PR #348 went CONFLICTING and CI stopped running. release-signed.yml only runs on v* tags, never on this PR, so it does not belong in this PR at all. Restored to the merge-base version so it merges cleanly (dev's version wins). The codegen for the release pipeline will be added to DEV's release-signed.yml in a follow-up, AFTER this merges (build_runner isn't a dep on dev until then). The PR-relevant codegen (build.yml, flutter_dart.yml, deploy-web.yml) + the .g.dart untracking are unchanged and still green the PR. For #335 / PR #348. Agent: SapphireCompass (session 8d755b5e) --- .github/workflows/release-signed.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release-signed.yml b/.github/workflows/release-signed.yml index 2f1b806..6a255b0 100644 --- a/.github/workflows/release-signed.yml +++ b/.github/workflows/release-signed.yml @@ -100,7 +100,6 @@ jobs: echo "Keystore decoded (${SIZE} bytes), key.properties written." - run: flutter pub get - - run: dart run build_runner build --delete-conflicting-outputs # Same mechanism as build.yml (#331). A signed release that shipped # without the Giphy key would have a dead GIF picker. From 1267b9177071a15a7171d475cf28fd35987941c1 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 19:53:15 -0400 Subject: [PATCH 48/60] fix(#346): extract signing cert SHA-256 by shape, not label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First live signing run (#345) failed at APK cert verification. The APK signed correctly, but the label parse (grep "Signer #1 certificate SHA-256 digest" | awk) returned empty on the runner's apksigner, whose output labels that line differently than the local build-tools. Empty != expected produced a false mismatch. The release correctly did NOT publish (fail-safe held). Now extracts by shape: the cert SHA-256 is the only 64-hex string in the output (SHA-1 is 40, MD5 is 32), so grep -ioE '[0-9a-f]{64}' is label-independent. Verified against a real signed APK (e7da8cd5…). Echoes the raw apksigner + keytool output for diagnosability, and hardens the AAB parse the same way (it was skipped last run, so its runner format is still unconfirmed — the echo will show it). Part of epic #312. Agent: SapphireCompass (session 8d755b5e) --- .github/workflows/release-signed.yml | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-signed.yml b/.github/workflows/release-signed.yml index 5d6ae8b..f1eda06 100644 --- a/.github/workflows/release-signed.yml +++ b/.github/workflows/release-signed.yml @@ -118,11 +118,23 @@ jobs: APK=build/app/outputs/flutter-apk/app-release.apk BUILD_TOOLS=$(ls -d "${ANDROID_HOME}"/build-tools/* | sort -V | tail -1) CERTS=$("${BUILD_TOOLS}/apksigner" verify --print-certs "${APK}") + echo "--- apksigner --print-certs output ---" + echo "${CERTS}" + echo "--------------------------------------" if echo "${CERTS}" | grep -qi "CN=Android Debug"; then echo "::error::APK is DEBUG-SIGNED. key.properties not picked up; see #111." exit 1 fi - ACTUAL=$(echo "${CERTS}" | grep -i "Signer #1 certificate SHA-256 digest" | head -1 | awk -F': ' '{print $2}' | tr -d '[:space:]') + # Extract the fingerprint by shape, not by label: only the cert's + # SHA-256 digest is 64 hex chars (SHA-1 is 40, MD5 is 32), so this is + # unambiguous and independent of how a given apksigner version phrases + # the line. The label-based awk parse broke on the runner's apksigner + # (#345 first live run: label differed, ACTUAL came back empty). + ACTUAL=$(echo "${CERTS}" | grep -ioE '[0-9a-f]{64}' | head -1 | tr 'A-F' 'a-f') + if [ -z "${ACTUAL}" ]; then + echo "::error::Could not read a SHA-256 fingerprint from the APK (see output above)." + exit 1 + fi if [ "${ACTUAL}" != "${EXPECTED_SHA256}" ]; then echo "::error::Signing cert MISMATCH. expected ${EXPECTED_SHA256} actual ${ACTUAL}" exit 1 @@ -140,11 +152,15 @@ jobs: set -eu AAB=build/app/outputs/bundle/release/app-release.aab # keytool comes from the JDK that setup-java put on PATH in this job. + echo "--- keytool -printcert -jarfile output ---" + keytool -printcert -jarfile "${AAB}" | grep -iE 'Owner|SHA256' || true + echo "------------------------------------------" + # Strip the label, keep only hex (robust to spacing/case), lowercase. AAB_SHA=$(keytool -printcert -jarfile "${AAB}" \ | grep -i 'SHA256:' | head -1 \ - | sed 's/.*SHA256:[[:space:]]*//' | tr -d ': ' | tr 'A-F' 'a-f') + | sed 's/.*SHA256://' | tr -cd '0-9a-fA-F' | tr 'A-F' 'a-f') if [ -z "${AAB_SHA}" ]; then - echo "::error::Could not read a SHA-256 from the AAB signing cert." + echo "::error::Could not read a SHA-256 from the AAB signing cert (see output above)." exit 1 fi if [ "${AAB_SHA}" != "${EXPECTED_SHA256}" ]; then From 4108f9fecccddd6e4e1e05c5cadeb941c156b858 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 21:29:54 -0400 Subject: [PATCH 49/60] chore(#353): add drift codegen to the release pipeline's build jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to #348 (now merged): dev has drift, but release-signed.yml had no build_runner step, so the next v* release would fail its android/windows/linux builds on the missing offband_database.g.dart. Adds `dart run build_runner build --delete-conflicting-outputs` after `flutter pub get` in the three jobs that build (android, windows, linux). gate and release don't compile Dart, so no codegen there. Matches the step #348 added to build.yml/flutter_dart/deploy-web. This one couldn't ride #348 because release-signed.yml had diverged on dev (#342/#346) and editing the stale copy caused a merge conflict — see #348 discussion. Part of epic #312. Agent: SapphireCompass (session 8d755b5e) --- .github/workflows/release-signed.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release-signed.yml b/.github/workflows/release-signed.yml index f1eda06..fd8e32a 100644 --- a/.github/workflows/release-signed.yml +++ b/.github/workflows/release-signed.yml @@ -98,6 +98,7 @@ jobs: echo "Keystore decoded (${SIZE} bytes), key.properties written." - run: flutter pub get + - run: dart run build_runner build --delete-conflicting-outputs - name: Write dart_defines.json env: GIPHY_API_KEY: ${{ secrets.GIPHY_API_KEY }} @@ -198,6 +199,7 @@ jobs: flutter-version: "3.44.1" cache: true - run: flutter pub get + - run: dart run build_runner build --delete-conflicting-outputs - name: Write dart_defines.json env: GIPHY_API_KEY: ${{ secrets.GIPHY_API_KEY }} @@ -226,6 +228,7 @@ jobs: - name: Install Linux build deps run: sudo apt-get update && sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev - run: flutter pub get + - run: dart run build_runner build --delete-conflicting-outputs - name: Write dart_defines.json env: GIPHY_API_KEY: ${{ secrets.GIPHY_API_KEY }} From 8af0e0d5dced57e584af733e1320c03e83a5c6f8 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 22:22:19 -0400 Subject: [PATCH 50/60] fix(#355): merge prefs into drift on migration instead of discarding The migration's already-present branch called prefs.remove(key) and threw the prefs copy away when drift already held the key. A build that writes to SharedPreferences (a non-drift build, or any in-between test build) collects new messages there, so the next drift run silently gapped that history out. It cost 574 real channel messages, recovered from backups. Union the prefs copy into drift by element identity (messageId, else publicKey, else canonical JSON), keeping drift's live copy on a collision and appending prefs-only elements. Verify the merged write before removing the source. New `merged` counter in the report. Co-Authored-By: Claude Opus 4.8 --- lib/storage/drift/blob_store.dart | 88 +++++++++++++++++++-- test/storage/blob_store_migration_test.dart | 84 +++++++++++++++++--- 2 files changed, 155 insertions(+), 17 deletions(-) diff --git a/lib/storage/drift/blob_store.dart b/lib/storage/drift/blob_store.dart index c8ccf9f..76b5034 100644 --- a/lib/storage/drift/blob_store.dart +++ b/lib/storage/drift/blob_store.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:flutter/foundation.dart'; @@ -176,12 +177,41 @@ class BlobStore { } final source = raw; - if (await read(key) != null) { - // Already migrated on a previous run. Leave the prefs copy for the - // sweep below rather than assuming; the read-back proved the data is - // present in drift. - report.alreadyPresent++; + final existing = await read(key); + if (existing != null) { + // Drift already holds this key. Do NOT discard the prefs copy: a + // build that writes to SharedPreferences (a non-drift build, or any + // in-between test build) accumulates NEW messages there, and throwing + // them away silently gaps the history across test cycles (#355). Union + // the prefs copy into drift by message identity, keeping drift's live + // entries, then verify before removing the source. + final merged = _mergeBulk(existing, source); + if (merged == null) { + // Not a JSON list (e.g. a scalar under a bulk prefix): drift's copy + // stands, nothing to union. Safe to drop the prefs duplicate. + report.alreadyPresent++; + await prefs.remove(key); + continue; + } + if (merged == existing) { + // Prefs added nothing new; drift is already a superset. + report.alreadyPresent++; + await prefs.remove(key); + continue; + } + await write(key, merged); + final readBack = await read(key); + if (readBack != merged) { + report.failed++; + appLogger.error( + 'Blob merge FAILED for $key: wrote ${merged.length} chars, ' + 'read back ${readBack?.length ?? "null"}. Prefs copy left intact.', + tag: 'Storage', + ); + continue; + } await prefs.remove(key); + report.merged++; continue; } @@ -216,6 +246,7 @@ class BlobStore { final level = report.failed > 0 ? 'WITH FAILURES' : 'ok'; appLogger.info( 'Blob migration complete ($level): ${report.migrated} moved, ' + '${report.merged} merged, ' '${report.alreadyPresent} already present, ${report.skipped} skipped, ' '${report.failed} failed, ' '${(report.bytes / 1024 / 1024).toStringAsFixed(2)} MB', @@ -230,11 +261,58 @@ class BlobStore { } return report; } + + /// Unions the [prefs] copy of a bulk key into the [drift] copy by element + /// identity, keeping drift's entries where both hold the same one. + /// + /// All bulk families store a JSON list of objects (messages keyed by + /// `messageId`, contacts by `publicKey`); identity falls back to the object's + /// canonical form so an id-less element is never dropped. Order is drift's + /// list followed by the prefs-only elements, which mirrors how the stores + /// append on save. + /// + /// Returns null when either side is not a JSON list of objects (a scalar + /// under a bulk prefix), signalling the caller to leave drift's copy as-is. + /// Returns the drift JSON unchanged when prefs contributes nothing new. + static String? _mergeBulk(String drift, String prefs) { + final List driftList; + final List prefsList; + try { + final d = jsonDecode(drift); + final p = jsonDecode(prefs); + if (d is! List || p is! List) return null; + driftList = d; + prefsList = p; + } catch (_) { + return null; + } + + String idOf(dynamic e) { + if (e is Map) { + final id = e['messageId']; + if (id is String && id.isNotEmpty) return 'm:$id'; + final pk = e['publicKey']; + if (pk is String && pk.isNotEmpty) return 'p:$pk'; + } + // No stable id: fall back to the element's canonical JSON so distinct + // elements stay distinct and true duplicates collapse. + return 'j:${jsonEncode(e)}'; + } + + final seen = {for (final e in driftList) idOf(e)}; + final result = List.from(driftList); + for (final e in prefsList) { + if (seen.add(idOf(e))) result.add(e); + } + if (result.length == driftList.length) return drift; + return jsonEncode(result); + } } /// Mutable tally of a migration run; surfaced in the log and used by tests. class MigrationReport { int migrated = 0; + int merged = 0; int alreadyPresent = 0; int skipped = 0; int failed = 0; diff --git a/test/storage/blob_store_migration_test.dart b/test/storage/blob_store_migration_test.dart index 4d96333..d869314 100644 --- a/test/storage/blob_store_migration_test.dart +++ b/test/storage/blob_store_migration_test.dart @@ -75,18 +75,78 @@ void main() { expect(await store.read('contacts_dev'), '[{"c":1}]'); }); - test('a re-added prefs key does not clobber migrated data', () async { - // Simulates an older build writing the key again after migration. The - // migrated copy must win; the stale prefs copy is discarded, not promoted. - await seedPrefs({'contacts_dev': '[{"c":"new"}]'}); - await store.write('contacts_dev', '[{"c":"migrated"}]'); - - final report = await store.migrateFromPrefs(); - - expect(report.alreadyPresent, 1); - expect(await store.read('contacts_dev'), '[{"c":"migrated"}]'); - expect(PrefsManager.instance.getString('contacts_dev'), isNull); - }); + test( + '#355: prefs messages absent from drift are merged, not discarded', + () async { + // The bug: an in-between build (non-drift, or any build that writes prefs) + // accumulates NEW messages in prefs. On the next drift run the key is + // "already present", so the old code discarded the prefs copy and the new + // messages were gapped out of history. They must be UNIONED in instead. + await store.write( + 'channel_messages_devpsk_abc', + '[{"messageId":"a"},{"messageId":"b"}]', + ); + await seedPrefs({ + 'channel_messages_devpsk_abc': + '[{"messageId":"a"},{"messageId":"c"},{"messageId":"d"}]', + }); + + final report = await store.migrateFromPrefs(); + + expect(report.merged, 1); + expect(report.failed, 0); + final ids = (await store.read('channel_messages_devpsk_abc'))!; + // Drift's a,b kept; prefs-only c,d appended; shared a not duplicated. + expect( + ids, + '[{"messageId":"a"},{"messageId":"b"},' + '{"messageId":"c"},{"messageId":"d"}]', + ); + expect( + PrefsManager.instance.getString('channel_messages_devpsk_abc'), + isNull, + ); + }, + ); + + test( + 'merge keeps the drift copy of a shared entity, adds prefs-only ones', + () async { + // Contacts collide by publicKey: the live drift copy wins for a shared key, + // and a contact seen only on the in-between build is still added. + await store.write('contacts_dev', '[{"publicKey":"A","name":"drift"}]'); + await seedPrefs({ + 'contacts_dev': + '[{"publicKey":"A","name":"stale"},{"publicKey":"B","name":"new"}]', + }); + + final report = await store.migrateFromPrefs(); + + expect(report.merged, 1); + expect( + await store.read('contacts_dev'), + '[{"publicKey":"A","name":"drift"},{"publicKey":"B","name":"new"}]', + ); + expect(PrefsManager.instance.getString('contacts_dev'), isNull); + }, + ); + + test( + 'already-present with nothing new to add reports alreadyPresent', + () async { + // Prefs is a subset of drift: union changes nothing, and the stale prefs + // copy is dropped without a needless rewrite. + await store.write('contacts_dev', '[{"publicKey":"A"}]'); + await seedPrefs({'contacts_dev': '[{"publicKey":"A"}]'}); + + final report = await store.migrateFromPrefs(); + + expect(report.alreadyPresent, 1); + expect(report.merged, 0); + expect(await store.read('contacts_dev'), '[{"publicKey":"A"}]'); + expect(PrefsManager.instance.getString('contacts_dev'), isNull); + }, + ); test('preserves a payload larger than the 5 MiB localStorage cap', () async { // 4 chars per element, so >1.4M elements clears 5 MiB. From 07ccd28afda65d13c6325407581a1d509f21eff3 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 21:00:29 -0400 Subject: [PATCH 51/60] feat(#349): persist and restore desktop window geometry The stock Flutter desktop runner never saved the window frame, so the window always reopened at a default position and size (#349). Adds WindowGeometryService (window_manager + screen_retriever): it saves the frame on move/resize, debounced, and restores it on launch. Restore is clamped against the connected displays. A window last placed on a monitor that is now unplugged must not reopen off-screen where it cannot be grabbed; if the saved frame does not overlap any display by at least 80px on both axes it is discarded and the window opens at the default. A too-small or undecodable saved value also falls through to the default (logged, not swallowed). Gated on PlatformInfo.isDesktop, so it is a no-op on mobile and web. Scoped past just Windows deliberately: window_manager is desktop-only and the same gap exists on Linux/macOS, so all three get the fix rather than Windows needing an extra guard. Known limitation, not addressed here: without a native runner change to start the window hidden, restore repositions AFTER the window is shown, so there can be a brief flash at the default position before it jumps to the saved frame. Eliminating that needs a windows/runner edit; flagged for the owner to judge during hardware test. flutter analyze clean, dart format clean, tests pass. Windows-run verification is the owner's (geometry is a real-desktop behaviour). --- lib/main.dart | 4 + lib/services/window_geometry_service.dart | 124 ++++++++++++++++++++++ pubspec.yaml | 6 +- 3 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 lib/services/window_geometry_service.dart diff --git a/lib/main.dart b/lib/main.dart index 4e0c17e..481334c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -27,6 +27,7 @@ import 'services/ui_view_state_service.dart'; import 'services/timeout_prediction_service.dart'; import 'services/observer_config_service.dart'; import 'services/block_service.dart'; +import 'services/window_geometry_service.dart'; import 'storage/drift/blob_store.dart'; import 'storage/prefs_manager.dart'; import 'utils/app_logger.dart'; @@ -48,6 +49,9 @@ void main() async { // Start always-on file logging (#97); no-op on web. await FileLogService.instance.init(); + // Restore the desktop window's saved position/size (#349); no-op off desktop. + await WindowGeometryService.instance.initialize(); + // Initialize services final storage = StorageService(); final connector = MeshCoreConnector(); diff --git a/lib/services/window_geometry_service.dart b/lib/services/window_geometry_service.dart new file mode 100644 index 0000000..a9023dd --- /dev/null +++ b/lib/services/window_geometry_service.dart @@ -0,0 +1,124 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:ui'; + +import 'package:screen_retriever/screen_retriever.dart'; +import 'package:window_manager/window_manager.dart'; + +import '../storage/prefs_manager.dart'; +import '../utils/app_logger.dart'; +import '../utils/platform_info.dart'; + +/// Persists and restores the desktop window's position and size (#349). +/// +/// The stock Flutter desktop runner never saved geometry, so the window always +/// reopened at a default frame. This service saves the frame on move/resize and +/// restores it on launch. +/// +/// The restore is clamped against the currently-connected displays: a window +/// last placed on a monitor that is now unplugged must not reopen off-screen +/// where the user cannot reach it. If the saved frame does not overlap any +/// display enough to grab, it is discarded and the window opens at the default. +/// +/// Desktop only (window_manager has no mobile/web backend); a no-op elsewhere. +class WindowGeometryService with WindowListener { + WindowGeometryService._(); + static final WindowGeometryService instance = WindowGeometryService._(); + + static const _prefsKey = 'window_geometry'; + static const _minWidth = 400.0; + static const _minHeight = 300.0; + // A window counts as reachable if at least this much of it overlaps a + // display on both axes, so a sliver still on screen is recoverable. + static const _minVisible = 80.0; + + Timer? _saveDebounce; + + /// Call once in main() before runApp, after PrefsManager.initialize(). + Future initialize() async { + if (!PlatformInfo.isDesktop) return; + + await windowManager.ensureInitialized(); + await windowManager.waitUntilReadyToShow(null, () async { + await _restore(); + await windowManager.show(); + await windowManager.focus(); + }); + windowManager.addListener(this); + } + + Future _restore() async { + final raw = PrefsManager.instance.getString(_prefsKey); + if (raw == null || raw.isEmpty) return; + + Rect saved; + try { + final m = jsonDecode(raw) as Map; + saved = Rect.fromLTWH( + (m['x'] as num).toDouble(), + (m['y'] as num).toDouble(), + (m['w'] as num).toDouble(), + (m['h'] as num).toDouble(), + ); + } catch (e) { + // SAFELANE 6: never swallow. A corrupt value falls through to the default. + appLogger.error( + 'Failed to decode saved window geometry; using default: $e', + tag: 'Window', + ); + return; + } + + if (saved.width < _minWidth || saved.height < _minHeight) return; + + if (!await _isReachable(saved)) { + appLogger.info( + 'Saved window geometry is off-screen (monitor likely unplugged); ' + 'opening at the default position instead.', + tag: 'Window', + ); + return; + } + + await windowManager.setBounds(saved); + } + + /// True when [saved] overlaps some connected display's visible area by at + /// least [_minVisible] on both axes. + Future _isReachable(Rect saved) async { + final displays = await screenRetriever.getAllDisplays(); + for (final d in displays) { + final origin = d.visiblePosition ?? Offset.zero; + final size = d.visibleSize ?? d.size; + final display = origin & size; // Rect from an Offset and a Size. + final overlap = saved.intersect(display); + if (overlap.width >= _minVisible && overlap.height >= _minVisible) { + return true; + } + } + return false; + } + + void _scheduleSave() { + _saveDebounce?.cancel(); + _saveDebounce = Timer(const Duration(milliseconds: 400), _save); + } + + Future _save() async { + try { + final b = await windowManager.getBounds(); + await PrefsManager.instance.setString( + _prefsKey, + jsonEncode({'x': b.left, 'y': b.top, 'w': b.width, 'h': b.height}), + ); + } catch (e) { + appLogger.error('Failed to save window geometry: $e', tag: 'Window'); + } + } + + @override + void onWindowMoved() => _scheduleSave(); + + @override + void onWindowResized() => _scheduleSave(); +} diff --git a/pubspec.yaml b/pubspec.yaml index 3a9ab39..bd175d8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -74,12 +74,12 @@ dependencies: llamadart: '>=0.6.8 <0.7.0' flutter_langdetect: ^0.0.1 # PINNED (#335): web ships version-matched sqlite3.wasm + drift_worker.js - # from the drift release. A caret here would let pub resolve a newer drift - # than the committed assets, breaking web silently. Bump deliberately, then - # re-download both assets. tool/check_drift_assets.dart enforces the match. + # from the drift release. Bump deliberately, then re-download both assets. drift: 2.34.2 drift_flutter: 0.3.1 path: ^1.9.1 + window_manager: ^0.5.2 + screen_retriever: ^0.2.2 hooks: user_defines: From 615c0ed3b5df78d2f22d9cf31c0a9bf403391c6a Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 21:57:33 -0400 Subject: [PATCH 52/60] fix(#349): address Gemini review of window geometry Three findings from the adversarial review (standards#145), all real, all fixed: - Move-then-close inside the 400ms debounce lost the final position. The service now setPreventClose(true) and saves on onWindowClose before destroying the window, so the closing frame is always persisted. - _isReachable mixed a display's visible ORIGIN with its total SIZE when visibleSize was null, producing a rect offset from the real area. It now uses the visible pair when both are known, else the total pair. Negative visiblePosition (a monitor left of/above primary) is handled by intersect(). - The window listener and debounce timer are now released in onWindowClose. flutter analyze clean, dart format clean, 509 tests pass. --- lib/services/window_geometry_service.dart | 32 ++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/lib/services/window_geometry_service.dart b/lib/services/window_geometry_service.dart index a9023dd..1d76c78 100644 --- a/lib/services/window_geometry_service.dart +++ b/lib/services/window_geometry_service.dart @@ -45,6 +45,10 @@ class WindowGeometryService with WindowListener { await windowManager.focus(); }); windowManager.addListener(this); + // Intercept close so the final geometry is saved even if the user moves + // then closes inside the debounce window; onWindowClose does the save and + // then destroys the window (Gemini review). + await windowManager.setPreventClose(true); } Future _restore() async { @@ -88,10 +92,19 @@ class WindowGeometryService with WindowListener { Future _isReachable(Rect saved) async { final displays = await screenRetriever.getAllDisplays(); for (final d in displays) { - final origin = d.visiblePosition ?? Offset.zero; - final size = d.visibleSize ?? d.size; - final display = origin & size; // Rect from an Offset and a Size. + // Use the visible pair when BOTH are known, else the total pair. Mixing a + // visible origin with a total size makes a rect offset from the real + // area (Gemini review). visiblePosition can be negative for a monitor + // left of / above the primary, which intersect handles correctly. + final Rect display; + if (d.visiblePosition != null && d.visibleSize != null) { + display = d.visiblePosition! & d.visibleSize!; + } else { + display = Offset.zero & d.size; + } final overlap = saved.intersect(display); + // intersect() can return negative width/height when the rects miss; only + // a genuine overlap on both axes counts. if (overlap.width >= _minVisible && overlap.height >= _minVisible) { return true; } @@ -121,4 +134,17 @@ class WindowGeometryService with WindowListener { @override void onWindowResized() => _scheduleSave(); + + /// Fires because setPreventClose(true) intercepted the close. Save the final + /// geometry, release resources, then actually close the window. + @override + Future onWindowClose() async { + _saveDebounce?.cancel(); + try { + await _save(); + } finally { + windowManager.removeListener(this); + await windowManager.destroy(); + } + } } From 6c8d9ad2db649cd80f5d35d2428fa875e760f253 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 22:56:57 -0400 Subject: [PATCH 53/60] feat(#351): reach contact settings from the long-press menu The per-contact settings dialog (compression + telemetry grants) was reachable only from a contact's chat via ellipsis -> Contact settings. Extract it to a shared showContactSettingsDialog() and add a "Contact settings" entry as the first item of the contact long-press/right-click menu, so it opens the same surface from both places. chat_screen's _showContactSettings now delegates to the shared dialog; its _buildInfoRow stays (still used by the contact-info dialog). Co-Authored-By: Claude Opus 4.8 --- lib/screens/chat_screen.dart | 162 +------------------- lib/screens/contacts_screen.dart | 11 ++ lib/widgets/contact_settings_dialog.dart | 180 +++++++++++++++++++++++ 3 files changed, 193 insertions(+), 160 deletions(-) create mode 100644 lib/widgets/contact_settings_dialog.dart diff --git a/lib/screens/chat_screen.dart b/lib/screens/chat_screen.dart index c43886b..0e73350 100644 --- a/lib/screens/chat_screen.dart +++ b/lib/screens/chat_screen.dart @@ -32,6 +32,7 @@ import '../services/chat_text_scale_service.dart'; import '../services/path_history_service.dart'; import '../services/translation_service.dart'; import '../widgets/chat_zoom_wrapper.dart'; +import '../widgets/contact_settings_dialog.dart'; import '../widgets/elements_ui.dart'; import '../widgets/mention_autocomplete.dart'; import '../helpers/emoji_shortcodes.dart'; @@ -1349,166 +1350,7 @@ class _ChatScreenState extends State { } void _showContactSettings(BuildContext context) { - final connector = Provider.of(context, listen: false); - final appSettingsService = Provider.of( - context, - listen: false, - ); - connector.ensureContactSmazSettingLoaded(widget.contact.publicKeyHex); - connector.ensureContactCyr2LatSettingLoaded(widget.contact.publicKeyHex); - final contact = widget.contact; - bool smazEnabled = connector.isContactSmazEnabled(contact.publicKeyHex); - bool cyr2latEnabled = connector.isContactCyr2LatEnabled( - contact.publicKeyHex, - ); - String? selectedCyr2LatProfileId = connector.getContactCyr2LatProfileId( - contact.publicKeyHex, - ); - bool teleBaseEnabled = contact.teleBaseEnabled; - bool teleLocEnabled = contact.teleLocEnabled; - bool teleEnvEnabled = contact.teleEnvEnabled; - showDialog( - context: context, - builder: (context) => StatefulBuilder( - builder: (context, setDialogState) => AlertDialog( - title: Text(context.l10n.contact_settings), - content: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (contact.hasLocation) ...[ - _buildInfoRow( - context.l10n.chat_location, - '${contact.latitude?.toStringAsFixed(4)}, ${contact.longitude?.toStringAsFixed(4)}', - ), - const Divider(height: 8), - ], - SwitchListTile( - contentPadding: EdgeInsets.zero, - title: Text(context.l10n.channels_smazCompression), - subtitle: Text(context.l10n.chat_compressOutgoingMessages), - value: smazEnabled, - onChanged: (value) { - connector.setContactSmazEnabled( - contact.publicKeyHex, - value, - ); - connector.setContactCyr2LatEnabled( - contact.publicKeyHex, - false, - ); - setDialogState(() { - smazEnabled = value; - if (smazEnabled) { - cyr2latEnabled = false; - } - }); - }, - ), - const Divider(height: 8), - SwitchListTile( - contentPadding: EdgeInsets.zero, - title: Text(context.l10n.channels_cyr2latCompression), - subtitle: Text(context.l10n.channels_cyr2latCompressionDscr), - value: cyr2latEnabled, - onChanged: (value) { - connector.setContactCyr2LatEnabled( - contact.publicKeyHex, - value, - ); - connector.setContactSmazEnabled( - contact.publicKeyHex, - false, - ); - setDialogState(() { - cyr2latEnabled = value; - if (cyr2latEnabled) { - smazEnabled = false; - } - }); - }, - ), - if (cyr2latEnabled) ...[ - Padding( - padding: const EdgeInsets.fromLTRB(0, 8, 0, 8), - child: DropdownButtonFormField( - initialValue: selectedCyr2LatProfileId, - decoration: InputDecoration( - labelText: - context.l10n.channels_cyr2latSettingsSubheading, - border: const OutlineInputBorder(), - ), - items: appSettingsService.settings.cyr2latProfiles.map(( - profile, - ) { - return DropdownMenuItem( - value: profile.id, - child: Text(profile.name), - ); - }).toList(), - onChanged: (value) { - connector.setContactCyr2LatProfileId( - contact.publicKeyHex, - value, - ); - setDialogState(() { - selectedCyr2LatProfileId = value; - }); - }, - ), - ), - ], - const Divider(height: 8), - SwitchListTile( - contentPadding: EdgeInsets.zero, - title: Text(context.l10n.contact_teleBase), - subtitle: Text(context.l10n.contact_teleBaseSubtitle), - value: teleBaseEnabled, - onChanged: (value) { - setDialogState(() => teleBaseEnabled = value); - }, - ), - const Divider(height: 8), - SwitchListTile( - contentPadding: EdgeInsets.zero, - title: Text(context.l10n.contact_teleLoc), - subtitle: Text(context.l10n.contact_teleLocSubtitle), - value: teleLocEnabled, - onChanged: (value) { - setDialogState(() => teleLocEnabled = value); - }, - ), - const Divider(height: 8), - SwitchListTile( - contentPadding: EdgeInsets.zero, - title: Text(context.l10n.contact_teleEnv), - subtitle: Text(context.l10n.contact_teleEnvSubtitle), - value: teleEnvEnabled, - onChanged: (value) { - setDialogState(() => teleEnvEnabled = value); - }, - ), - ], - ), - ), - actions: [ - TextButton( - onPressed: () { - connector.setContactFlags( - contact, - teleBase: teleBaseEnabled, - teleLoc: teleLocEnabled, - teleEnv: teleEnvEnabled, - ); - Navigator.pop(context); - }, - child: Text(context.l10n.common_close), - ), - ], - ), - ), - ); + showContactSettingsDialog(context, widget.contact); } Widget _buildInfoRow(String label, String value) { diff --git a/lib/screens/contacts_screen.dart b/lib/screens/contacts_screen.dart index ce32f4c..9ffbae6 100644 --- a/lib/screens/contacts_screen.dart +++ b/lib/screens/contacts_screen.dart @@ -29,6 +29,7 @@ import '../widgets/empty_state.dart'; import '../widgets/blocked_badge.dart'; import '../widgets/app_shell.dart'; import '../widgets/contact_filter_rail.dart'; +import '../widgets/contact_settings_dialog.dart'; import '../widgets/path_selection_dialog.dart'; import '../widgets/repeater_login_dialog.dart'; import '../widgets/room_login_dialog.dart'; @@ -1252,6 +1253,16 @@ class _ContactsScreenState extends State child: Column( mainAxisSize: MainAxisSize.min, children: [ + // Same destination as the chat ellipsis -> Contact settings (#351). + // First item, so the destructive Block tile stays separated below. + ListTile( + leading: const Icon(Icons.settings_outlined), + title: Text(context.l10n.contact_settings), + onTap: () { + Navigator.pop(sheetContext); + showContactSettingsDialog(context, contact); + }, + ), // Blocking your own node is never meaningful (#250). if (!isSelf) ListTile( diff --git a/lib/widgets/contact_settings_dialog.dart b/lib/widgets/contact_settings_dialog.dart new file mode 100644 index 0000000..d22777e --- /dev/null +++ b/lib/widgets/contact_settings_dialog.dart @@ -0,0 +1,180 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../connector/meshcore_connector.dart'; +import '../l10n/l10n.dart'; +import '../models/contact.dart'; +import '../services/app_settings_service.dart'; + +/// The per-contact settings dialog (Smaz/Cyr2Lat compression + telemetry +/// grants). Shared so it can be opened from the chat ellipsis AND the contacts +/// long-press menu without duplicating the body (#351). +void showContactSettingsDialog(BuildContext context, Contact contact) { + final connector = Provider.of(context, listen: false); + final appSettingsService = Provider.of( + context, + listen: false, + ); + connector.ensureContactSmazSettingLoaded(contact.publicKeyHex); + connector.ensureContactCyr2LatSettingLoaded(contact.publicKeyHex); + bool smazEnabled = connector.isContactSmazEnabled(contact.publicKeyHex); + bool cyr2latEnabled = connector.isContactCyr2LatEnabled(contact.publicKeyHex); + String? selectedCyr2LatProfileId = connector.getContactCyr2LatProfileId( + contact.publicKeyHex, + ); + bool teleBaseEnabled = contact.teleBaseEnabled; + bool teleLocEnabled = contact.teleLocEnabled; + bool teleEnvEnabled = contact.teleEnvEnabled; + showDialog( + context: context, + builder: (context) => StatefulBuilder( + builder: (context, setDialogState) => AlertDialog( + title: Text(context.l10n.contact_settings), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (contact.hasLocation) ...[ + _infoRow( + context.l10n.chat_location, + '${contact.latitude?.toStringAsFixed(4)}, ${contact.longitude?.toStringAsFixed(4)}', + ), + const Divider(height: 8), + ], + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: Text(context.l10n.channels_smazCompression), + subtitle: Text(context.l10n.chat_compressOutgoingMessages), + value: smazEnabled, + onChanged: (value) { + connector.setContactSmazEnabled(contact.publicKeyHex, value); + connector.setContactCyr2LatEnabled( + contact.publicKeyHex, + false, + ); + setDialogState(() { + smazEnabled = value; + if (smazEnabled) { + cyr2latEnabled = false; + } + }); + }, + ), + const Divider(height: 8), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: Text(context.l10n.channels_cyr2latCompression), + subtitle: Text(context.l10n.channels_cyr2latCompressionDscr), + value: cyr2latEnabled, + onChanged: (value) { + connector.setContactCyr2LatEnabled( + contact.publicKeyHex, + value, + ); + connector.setContactSmazEnabled(contact.publicKeyHex, false); + setDialogState(() { + cyr2latEnabled = value; + if (cyr2latEnabled) { + smazEnabled = false; + } + }); + }, + ), + if (cyr2latEnabled) ...[ + Padding( + padding: const EdgeInsets.fromLTRB(0, 8, 0, 8), + child: DropdownButtonFormField( + initialValue: selectedCyr2LatProfileId, + decoration: InputDecoration( + labelText: + context.l10n.channels_cyr2latSettingsSubheading, + border: const OutlineInputBorder(), + ), + items: appSettingsService.settings.cyr2latProfiles.map(( + profile, + ) { + return DropdownMenuItem( + value: profile.id, + child: Text(profile.name), + ); + }).toList(), + onChanged: (value) { + connector.setContactCyr2LatProfileId( + contact.publicKeyHex, + value, + ); + setDialogState(() { + selectedCyr2LatProfileId = value; + }); + }, + ), + ), + ], + const Divider(height: 8), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: Text(context.l10n.contact_teleBase), + subtitle: Text(context.l10n.contact_teleBaseSubtitle), + value: teleBaseEnabled, + onChanged: (value) { + setDialogState(() => teleBaseEnabled = value); + }, + ), + const Divider(height: 8), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: Text(context.l10n.contact_teleLoc), + subtitle: Text(context.l10n.contact_teleLocSubtitle), + value: teleLocEnabled, + onChanged: (value) { + setDialogState(() => teleLocEnabled = value); + }, + ), + const Divider(height: 8), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: Text(context.l10n.contact_teleEnv), + subtitle: Text(context.l10n.contact_teleEnvSubtitle), + value: teleEnvEnabled, + onChanged: (value) { + setDialogState(() => teleEnvEnabled = value); + }, + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () { + connector.setContactFlags( + contact, + teleBase: teleBaseEnabled, + teleLoc: teleLocEnabled, + teleEnv: teleEnvEnabled, + ); + Navigator.pop(context); + }, + child: Text(context.l10n.common_close), + ), + ], + ), + ), + ); +} + +Widget _infoRow(String label, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 80, + child: Text(label, style: TextStyle(color: Colors.grey[600])), + ), + Expanded(child: SelectableText(value)), + ], + ), + ); +} From b7606de536e190291a328ebfb6ef5e616750dccb Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 23:54:49 -0400 Subject: [PATCH 54/60] chore(#359): cut 1.2.0+61 (version bump + release notes) Marketing version 1.2.0 (Ben's call: minor bump for a storage-engine migration + nav overhaul, not a patch). versionCode 61 (next after 60, current on Play). - pubspec 1.1.2-rc.3+60 -> 1.2.0+61 - CHANGELOG [1.2.0] section - release-notes/1.2.0.md (GitHub release body) - play/1.2.0.txt (498/500 chars) - discord/1.2.0.md Release-gate passes (all four texts present, play within limit). No em-dashes in the new copy. Headlines: drift/SQLite storage migration (auto-migrates existing data), new nav shell, cross-client GIF links. First release cut end-to-end through the #312 pipeline. Part of Play epic #160. Agent: SapphireCompass (session 8d755b5e) --- CHANGELOG.md | 58 ++++++++++++++++++++++++++++++++++++++++++ discord/1.2.0.md | 13 ++++++++++ play/1.2.0.txt | 8 ++++++ pubspec.yaml | 2 +- release-notes/1.2.0.md | 44 ++++++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 discord/1.2.0.md create mode 100644 play/1.2.0.txt create mode 100644 release-notes/1.2.0.md diff --git a/CHANGELOG.md b/CHANGELOG.md index b36c288..6930a08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,64 @@ All notable changes to Offband Meshcore. Pre-releases are tagged `-beta.N` / `-rc.N`. +## [1.2.0] - 2026-07-21 + +A major uplift: the storage engine moved to a real database, the app got a new +navigation shell, and GIFs are now shareable across MeshCore clients. First +release cut end-to-end through CI (signed, all platforms). + +### Storage + +- **Bulk data (messages, contacts, channels, and more) now lives in a drift / + SQLite database** instead of individual SharedPreferences entries. Existing + data is **migrated automatically on first launch** and preserved, not + discarded (#335, #355, #348). +- The database is stored in the app-support directory, so it is not caught up in + OneDrive/Documents sync on desktop (#335). +- Message history is kept in full; only the in-memory window is bounded, so + reopening a conversation no longer risks losing older messages (#343). +- Storage failures that used to fail silently now surface (#306, #333). + +### Navigation + +- **New app shell with a hamburger nav drawer that can be pinned open** (#292). +- The channel list lives in the drawer and is switchable from inside a channel; + channels swap in place so a pinned panel stays put (#289). +- Map layer toggles moved into the nav panel; Disconnect and Settings moved into + the panel footer (#291, #290). +- Contacts gained a prebuilt filter rail, including a Sensors type (#307, #308). +- Back-button behavior fixed: back now backgrounds the app instead of killing + it, and no longer drops a user off a connected radio (#291). + +### GIFs + +- GIFs are now shared as a **Giphy URL** instead of the Offband-only `g:`, + so other MeshCore clients can open them (#282). +- Pasted Giphy / Tenor URLs render inline, behind a trusted-host allowlist + (#283, #284). + +### Other features + +- Desktop window position and size are remembered across launches (#349). +- Reach a contact's settings from its long-press menu (#351). +- FEM LNA toggle in Radio Settings and Offband capabilities shown in Device Info, + gated on firmware support (#304). +- Message arrival time (rxTime) is logged and persisted, distinct from the + claimed send time (#285). + +### Fixes + +- You can no longer accidentally block your own node (#250). +- Path length decodes per the firmware contract (width-aware), and path + diagnostics report units truthfully (#309, #298). +- Connecting no longer rewrites the entire preferences file per channel (#306). + +### Under the hood + +- Offband is now buildable and deployable as a web app (offband.app), and every + release is built, signed, and published across Android, Windows, Linux, and web + through CI. + ## [1.1.2-rc.3] - 2026-07-17 Combined entry for rc.2 (versionCode 59) and rc.3 (versionCode 60). Both shipped diff --git a/discord/1.2.0.md b/discord/1.2.0.md new file mode 100644 index 0000000..3700c95 --- /dev/null +++ b/discord/1.2.0.md @@ -0,0 +1,13 @@ +**Offband Meshcore 1.2.0 is out** 🚀 + +This is a big one, and the first release built + signed + shipped for every platform automatically through our new CI pipeline. + +🗄️ **New storage engine.** Your data now lives in a real SQLite database instead of scattered preference files. Existing messages and contacts migrate automatically on first launch, nothing gets dropped, and full message history is preserved. On desktop it no longer fights with OneDrive/Documents sync. + +🧭 **New navigation.** A hamburger drawer you can **pin open**, with the channel list built in and in-place channel switching so you never lose your place. Map layer toggles and a contacts filter rail moved into the panel, and the back button finally behaves. + +🎞️ **Cross-client GIFs.** GIFs now send as a Giphy link instead of an Offband-only code, so folks on other MeshCore clients can actually see them. Pasted Giphy/Tenor links render inline. + +Plus: desktop window size/position is remembered, contact settings from the long-press menu, FEM LNA control where the radio supports it, and you can't accidentally block your own node anymore. + +Grab it for your platform below. Full changelog in the repo. diff --git a/play/1.2.0.txt b/play/1.2.0.txt new file mode 100644 index 0000000..92d6e69 --- /dev/null +++ b/play/1.2.0.txt @@ -0,0 +1,8 @@ +What's new in 1.2.0. A major update. + +- Your data now lives in a real database. Existing messages and contacts migrate automatically on first launch, nothing lost. +- New navigation drawer you can pin open, with the channel list built in and in-place channel switching. +- GIFs share as links now, so people on other MeshCore apps can see them. +- Desktop remembers its window size and position. +- Reach contact settings from the long-press menu. +- Back-button, path decoding, and connect-speed fixes. \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index bd175d8..0da6b94 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.1.2-rc.3+60 +version: 1.2.0+61 environment: sdk: ^3.9.2 diff --git a/release-notes/1.2.0.md b/release-notes/1.2.0.md new file mode 100644 index 0000000..85038df --- /dev/null +++ b/release-notes/1.2.0.md @@ -0,0 +1,44 @@ +## Offband Meshcore 1.2.0 + +A big one. This release rebuilds how Offband stores your data, gives the app a +new navigation shell, and makes GIFs work across every MeshCore client. It is +also the first Offband release built, signed, and published across all platforms +automatically through CI. + +### Your data now lives in a real database + +Messages, contacts, channels, and the rest have moved from scattered preference +entries to a proper drift / SQLite database. **Your existing data is migrated +automatically the first time you open this version, and nothing is thrown away.** +Message history is kept in full, storage errors no longer fail silently, and on +desktop the database no longer gets tangled up in OneDrive/Documents sync. + +Because this is a one-time on-device migration, it is worth backing up nothing in +particular but simply being aware that the first launch does a little more work +than usual. + +### A new way to get around + +There is a new navigation drawer you open from the hamburger button, and you can +pin it open so it stays alongside your chat. The channel list lives there and you +can switch channels without leaving the one you are in. Map layer toggles moved +into the panel, Disconnect and Settings moved to its footer, and Contacts gained +a filter rail. The back button now behaves the way you would expect: it +backgrounds the app instead of killing it, and it no longer knocks you off a +connected radio. + +### GIFs that work everywhere + +GIFs are now sent as a plain Giphy link instead of an Offband-only code, so +people on other MeshCore clients can actually see them. Pasted Giphy and Tenor +links render inline, behind a trusted-host allowlist. + +### Also in this release + +- Desktop remembers your window position and size. +- Reach a contact's settings straight from its long-press menu. +- FEM LNA control and Offband capability readout in Device Info, where the radio + supports it. +- You can no longer accidentally block your own node. + +Full details in the changelog. From 755c13ab68a916af7871e42bbf6dd6efe277ad9f Mon Sep 17 00:00:00 2001 From: Strycher Date: Tue, 21 Jul 2026 00:17:59 -0400 Subject: [PATCH 55/60] docs(#359): restructure 1.2.0 release notes per Ben's review Reordered + marketed per direction: platforms-first (Android/Windows/ Linux/Web now shipped every release), then the interface redesign as the headline (left rail, pinned rail, in-place channel nav), storage, GIFs (cross-client tap-to-view + Tenor render), a big FEM/LNA callout, deeper diagnostics (persisted rxTime, honest path units), and performance. Added placeholders at the hero, pinned-rail, and FEM-LNA spots for Ben to drop captures into the GitHub release editor (real screenshots must be captured from the running app; not faking them). No em-dashes. Part of #160. Agent: SapphireCompass (session 8d755b5e) --- release-notes/1.2.0.md | 117 ++++++++++++++++++++++++++++++----------- 1 file changed, 87 insertions(+), 30 deletions(-) diff --git a/release-notes/1.2.0.md b/release-notes/1.2.0.md index 85038df..2ac176c 100644 --- a/release-notes/1.2.0.md +++ b/release-notes/1.2.0.md @@ -1,44 +1,101 @@ ## Offband Meshcore 1.2.0 -A big one. This release rebuilds how Offband stores your data, gives the app a -new navigation shell, and makes GIFs work across every MeshCore client. It is -also the first Offband release built, signed, and published across all platforms -automatically through CI. +Our biggest release yet. A ground-up interface redesign, a real database under the +hood, GIFs that work across every MeshCore client, and hands-on control of your +radio's RF front end. This is also the first Offband release built, signed, and +shipped to every platform automatically. -### Your data now lives in a real database + -Messages, contacts, channels, and the rest have moved from scattered preference -entries to a proper drift / SQLite database. **Your existing data is migrated -automatically the first time you open this version, and nothing is thrown away.** -Message history is kept in full, storage errors no longer fail silently, and on -desktop the database no longer gets tangled up in OneDrive/Documents sync. +### One app, every platform -Because this is a one-time on-device migration, it is worth backing up nothing in -particular but simply being aware that the first launch does a little more work -than usual. +From this release on, every Offband build ships everywhere at once, signed with +the same key so updates just work: -### A new way to get around +- **Android**: install from Google Play or sideload the signed APK +- **Windows**: desktop build +- **Linux**: desktop build +- **Web**: run it right in your browser, no install, at offband.app -There is a new navigation drawer you open from the hamburger button, and you can -pin it open so it stays alongside your chat. The channel list lives there and you -can switch channels without leaving the one you are in. Map layer toggles moved -into the panel, Disconnect and Settings moved to its footer, and Contacts gained -a filter rail. The back button now behaves the way you would expect: it -backgrounds the app instead of killing it, and it no longer knocks you off a -connected radio. +Pick your platform below. -### GIFs that work everywhere +### A brand-new interface -GIFs are now sent as a plain Giphy link instead of an Offband-only code, so -people on other MeshCore clients can actually see them. Pasted Giphy and Tenor -links render inline, behind a trusted-host allowlist. +The headline feature. Offband has been rebuilt around a modern navigation shell +that finally gets out of your way. -### Also in this release +- A **left rail** you open from the hamburger button, and can **pin open** so it + lives right alongside your conversation instead of covering it. +- Your **channels live in the rail** and switch **in place**, so jumping between + channels never loses your spot or your scroll position. +- Map layer toggles, Disconnect, and Settings all found sensible new homes in the + panel, and the back button finally behaves the way it should: it backgrounds + the app instead of killing it, and never knocks you off a connected radio. -- Desktop remembers your window position and size. + + +### Your data, on solid ground + +Under the surface, Offband now keeps your messages, contacts, and channels in a +real SQLite database instead of a pile of loose preference files. + +- Your **existing data migrates automatically** the first time you open this + version. Nothing is thrown away, and your **full message history is preserved**. +- Storage problems that used to fail silently now surface, so nothing goes wrong + in the dark. +- On desktop, your data no longer gets swept into OneDrive/Documents sync. + +### GIFs everyone can see + +GIFs are no longer an Offband-only party trick. + +- Send a GIF and it goes out as a **plain Giphy link**, so people on **stock + MeshCore and other clients can tap it and watch it** too, not just Offband + users. +- Paste a **Giphy or Tenor** link and it renders inline, behind a trusted-host + safety allowlist. + +### Take command of your radio's front end + +Power users, this one's for you. On supported hardware, Offband now gives you +direct control of your radio's **FEM / LNA** (the front-end module and low-noise +amplifier that shape receive sensitivity and transmit path). + +- Toggle **FEM LNA** right from Radio Settings. +- Device Info now reads out your radio's Offband capabilities, so you can see at a + glance what your hardware supports. + +Both are gated on firmware capability, so they appear only where the radio +actually supports them. + + + +### Deeper diagnostics + +More signal, less guesswork when you need to see what your mesh is doing. + +- Message **arrival time is now logged and persisted** separately from the claimed + send time, so you can tell when a message really reached you versus when the + sender says it left (#285). +- Path diagnostics report **honest, width-aware units** instead of mislabeled hop + counts, and path lengths decode exactly per the firmware contract. + +### Faster across the board + +The new storage engine and a round of under-the-hood work add up to a noticeably +snappier app. + +- Connecting no longer rewrites your entire preferences file for every channel, so + connect time is quicker and lighter on storage. +- Bulk reads and writes go through the database now, which scales far better as + your message and contact history grows. + +### And a few more + +- Desktop remembers your window size and position between launches. - Reach a contact's settings straight from its long-press menu. -- FEM LNA control and Offband capability readout in Device Info, where the radio - supports it. - You can no longer accidentally block your own node. -Full details in the changelog. +--- + +See the full, itemized changelog in the repository. From fdf31a05e06e41c45804fc9a25f1fdbc1f4a1353 Mon Sep 17 00:00:00 2001 From: Strycher Date: Tue, 21 Jul 2026 01:00:28 -0400 Subject: [PATCH 56/60] docs(#359): add redacted redesign hero screenshot for 1.2.0 release notes Windows screenshot of the redesigned UI (pinned left rail, channel nav). Callsigns (KM8BE, KB8PMY) blurred; handles, message content, repeater ID, and channels kept. Ben-approved. Agent: SapphireCompass (session 8d755b5e) --- docs/releases/1.2.0/redesign.png | Bin 0 -> 206218 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/releases/1.2.0/redesign.png diff --git a/docs/releases/1.2.0/redesign.png b/docs/releases/1.2.0/redesign.png new file mode 100644 index 0000000000000000000000000000000000000000..7ac7ff722c85e254284d23d57f9774642344761b GIT binary patch literal 206218 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­tiV)a8>!;jEI2XM~Fdzc;Ry z1IY;7Gb!lg`?&>N`KO3oUcdMh*N_m>T>!^}stm(25Ygj06al?>q4b~Cu2I;?r)sWQ z-^7wbRqgSO-=RE_1)WH{H^&b$%ftwn(Qe!fkRUNTM5nYf$(Dy*b|W!g5CsD^r$}y8 z2Bb|sq1gGYpkS)_h|nH)vqA%)mA^SthRtQxSHJrGPEH;w)VLb^`7O!V$GfLM*rYvq z1fVZDFq?;s-%YqaP#*oGT{swpy<~iBl2`j;uHQoS)ji2F{yH6Q3f6nqQ4N46Vp9DW zsdNd{RIIOmRc5P1z;8buF3`1v%LYC#0jQ;8h>?+M{rp=x64hfEKhc}O3UET)5FcHq7Dk;x( zAlBN-OKpBd9URnSweD{f6_+Q7Jrrh$i%`;Im z9|_kQ1_bvrTa>LB8l&T~@FWA^R8Cfvb`R&CYLt-i$BQ=Z6@__1pZGZTCKxT7LV>v9 z#f;a+8otb*-z$t3UU5inBYM}1P$#-x(Se2s+Z_3`5ZafX87e0A?r)LE+4)%10$$SF z4lGupDlbN4&_pHqCdS=;uDou{=}3~b5D+@DH&uOjnW_BOfS z-*3xltQiz2kK8-NnMw}C|F+}Y-MCYVICAjW(fB3K(d3=NUECvdIrGA$6FEe;KVa#F zugAhD_@6CV|D;c+m*A=Bs}l1Zc<{Wcebxwu{(doh=UY3a)(79K<^$7-_GX#`SWL1d=q!s>lphv zu&rdr2f~OI0JxCJHr~b|bJ6kX!b);(K4ZD9_Pe3oUP{YH+goEgnl%QHg&d1s<55~d z#o+fN_jgJ0=WI-g0)o*@@?vPX4$(7td%;R;NH@0UgR&WS;B)bQ-u{|k0hxiC3$Yld z_cnwF(^0SWa*=KWg#AGrM3BtD>q?x%wGW$5q87NV=$vQ$X0DMzAh!h6loSlik*sB) zBHT){ktXUAJnRS~G*C~^^MWFu%EOhl%t#6GvI+gR_WTdj0(YP;fqh!7I zr)QucbZCk=8=XYvnc+O0R#prtdS3kzMMh=9O=5Y>d#BZNrx2Jhv1z{Dr$o>N@kz?d zEQu=-)cM|YX%$Y2l!#tHd+zdQr)kBLuL7Ct_)(^{g!�I9K>~f}MRo;^`X#+kemA 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 Date: Tue, 21 Jul 2026 01:01:23 -0400 Subject: [PATCH 57/60] docs(#359): wire redesign hero into 1.2.0 notes, drop other placeholders Hero image (SHA-pinned raw URL, resolves now + at release) at the top; removed the pinned-rail and FEM-LNA placeholders per Ben (one deliberate hero, no empty slots). Agent: SapphireCompass (session 8d755b5e) --- release-notes/1.2.0.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/release-notes/1.2.0.md b/release-notes/1.2.0.md index 2ac176c..ae1922b 100644 --- a/release-notes/1.2.0.md +++ b/release-notes/1.2.0.md @@ -5,7 +5,7 @@ hood, GIFs that work across every MeshCore client, and hands-on control of your radio's RF front end. This is also the first Offband release built, signed, and shipped to every platform automatically. - +![Offband Meshcore 1.2.0 on Windows: the redesigned interface with the navigation rail pinned open alongside a channel](https://raw.githubusercontent.com/OffbandMesh/meshcore-client/3826b7372122f812cc54d4f5bae438581ad78240/docs/releases/1.2.0/redesign.png) ### One app, every platform @@ -32,8 +32,6 @@ that finally gets out of your way. panel, and the back button finally behaves the way it should: it backgrounds the app instead of killing it, and never knocks you off a connected radio. - - ### Your data, on solid ground Under the surface, Offband now keeps your messages, contacts, and channels in a @@ -68,8 +66,6 @@ amplifier that shape receive sensitivity and transmit path). Both are gated on firmware capability, so they appear only where the radio actually supports them. - - ### Deeper diagnostics More signal, less guesswork when you need to see what your mesh is doing. From 1b0d9732cd3f82f10a266ba7603868142a54bcdd Mon Sep 17 00:00:00 2001 From: Strycher Date: Tue, 21 Jul 2026 01:05:53 -0400 Subject: [PATCH 58/60] docs(#359): donate line, richer Discord w/ links, web set to coming-soon - Release notes: added a Support Offband donate section (offband.org/donate, verified live), framed toward funding iOS. - Discord: fully rewritten, richer per-feature copy + real links (download/ release, project site, donate). Was thin and link-less. - Web handling: offband.app is NOT deployed (404) and untested, so pulled the live web claim from the platform list, changelog, and Discord; replaced with 'browser version coming soon'. dev.offband.app works but is not a release URL. Gate passes; no em-dashes in the new copy. Part of #160. Agent: SapphireCompass (session 8d755b5e) --- CHANGELOG.md | 5 ++--- discord/1.2.0.md | 21 ++++++++++++++------- release-notes/1.2.0.md | 16 +++++++++++++--- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6930a08..23d683a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,9 +56,8 @@ release cut end-to-end through CI (signed, all platforms). ### Under the hood -- Offband is now buildable and deployable as a web app (offband.app), and every - release is built, signed, and published across Android, Windows, Linux, and web - through CI. +- Every release is now built, signed, and published across Android, Windows, and + Linux automatically through CI. A web build is wired up and coming soon. ## [1.1.2-rc.3] - 2026-07-17 diff --git a/discord/1.2.0.md b/discord/1.2.0.md index 3700c95..7aff21e 100644 --- a/discord/1.2.0.md +++ b/discord/1.2.0.md @@ -1,13 +1,20 @@ -**Offband Meshcore 1.2.0 is out** 🚀 +🚀 **Offband Meshcore 1.2.0 is here, and it's our biggest release yet.** -This is a big one, and the first release built + signed + shipped for every platform automatically through our new CI pipeline. +A ground-up interface redesign, a real database under the hood, GIFs that work across every MeshCore client, and hands-on control of your radio's RF front end. This is also the first Offband release built, signed, and shipped to every platform automatically. -🗄️ **New storage engine.** Your data now lives in a real SQLite database instead of scattered preference files. Existing messages and contacts migrate automatically on first launch, nothing gets dropped, and full message history is preserved. On desktop it no longer fights with OneDrive/Documents sync. +🎨 **A brand-new interface.** The headline feature. There's a new **left rail** you open from the hamburger button and can **pin open**, so it lives right alongside your chat instead of covering it. Your channels live in the rail and switch **in place**, so you never lose your spot. Map toggles, Disconnect, and Settings all found sensible new homes, and the back button finally behaves. -🧭 **New navigation.** A hamburger drawer you can **pin open**, with the channel list built in and in-place channel switching so you never lose your place. Map layer toggles and a contacts filter rail moved into the panel, and the back button finally behaves. +🗄️ **Your data on solid ground.** Messages, contacts, and channels now live in a real **SQLite database** instead of scattered preference files. Your existing data migrates automatically on first launch, nothing is thrown away, and full message history is preserved. On desktop it no longer fights with OneDrive/Documents sync. -🎞️ **Cross-client GIFs.** GIFs now send as a Giphy link instead of an Offband-only code, so folks on other MeshCore clients can actually see them. Pasted Giphy/Tenor links render inline. +🎞️ **GIFs everyone can see.** GIFs now send as a **Giphy link** instead of an Offband-only code, so people on **stock MeshCore and other clients can tap and watch them** too. Pasted **Giphy and Tenor** links render inline. -Plus: desktop window size/position is remembered, contact settings from the long-press menu, FEM LNA control where the radio supports it, and you can't accidentally block your own node anymore. +📡 **Take command of your radio's front end.** On supported hardware, you can now toggle **FEM / LNA** right from Radio Settings, and Device Info reads out your radio's Offband capabilities. -Grab it for your platform below. Full changelog in the repo. +⚡ Plus a **snappier app** from the new storage engine, **desktop window position** remembered between launches, contact settings from the long-press menu, deeper mesh diagnostics, and you can't accidentally block your own node anymore. + +**📥 Download for your platform:** https://github.com/OffbandMesh/meshcore-client/releases/tag/v1.2.0 +🌐 A browser version is coming soon. +🔗 Project home: https://offband.org +📝 Full changelog is in the release notes at the link above. + +❤️ **Offband is free and open source, built by a tiny team.** If it's useful to you, you can help keep it going, and help fund getting it onto the iOS App Store, at https://offband.org/donate. Thank you for being part of it. diff --git a/release-notes/1.2.0.md b/release-notes/1.2.0.md index ae1922b..361bbc6 100644 --- a/release-notes/1.2.0.md +++ b/release-notes/1.2.0.md @@ -9,13 +9,15 @@ shipped to every platform automatically. ### One app, every platform -From this release on, every Offband build ships everywhere at once, signed with -the same key so updates just work: +From this release on, every Offband build ships at once, signed with the same key +so updates just work: - **Android**: install from Google Play or sideload the signed APK - **Windows**: desktop build - **Linux**: desktop build -- **Web**: run it right in your browser, no install, at offband.app + +A **browser version is on the way** as well, so you will be able to run Offband +with no install at all. More on that soon. Pick your platform below. @@ -95,3 +97,11 @@ snappier app. --- See the full, itemized changelog in the repository. + +### Support Offband + +Offband is free and open source, built by a very small team. If it is useful to +you, please consider chipping in at +[offband.org/donate](https://offband.org/donate). Donations go directly toward +keeping the project going and toward getting Offband onto the iOS App Store, which +carries its own ongoing Apple costs. Thank you for being part of it. From 8c61e8afda53900e0797789cdfd83d5bbc103dc5 Mon Sep 17 00:00:00 2001 From: Strycher Date: Tue, 21 Jul 2026 01:22:56 -0400 Subject: [PATCH 59/60] docs(#359): put the offband.app URLs back in Owner's call: include the live web URLs and sequence the main promotion (which deploys offband.app) BEFORE the release tag, so offband.app is up and verified before the notes go public. Avoids a pointless second push just to add URLs. Restored offband.app in the platform list, changelog, and Discord link. Part of #160. Agent: SapphireCompass (session 8d755b5e) --- CHANGELOG.md | 4 ++-- discord/1.2.0.md | 2 +- release-notes/1.2.0.md | 8 +++----- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23d683a..ff39c4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,8 +56,8 @@ release cut end-to-end through CI (signed, all platforms). ### Under the hood -- Every release is now built, signed, and published across Android, Windows, and - Linux automatically through CI. A web build is wired up and coming soon. +- Offband is now available as a web app at offband.app, and every release is + built, signed, and published across Android, Windows, Linux, and web through CI. ## [1.1.2-rc.3] - 2026-07-17 diff --git a/discord/1.2.0.md b/discord/1.2.0.md index 7aff21e..9fc316e 100644 --- a/discord/1.2.0.md +++ b/discord/1.2.0.md @@ -13,7 +13,7 @@ A ground-up interface redesign, a real database under the hood, GIFs that work a ⚡ Plus a **snappier app** from the new storage engine, **desktop window position** remembered between launches, contact settings from the long-press menu, deeper mesh diagnostics, and you can't accidentally block your own node anymore. **📥 Download for your platform:** https://github.com/OffbandMesh/meshcore-client/releases/tag/v1.2.0 -🌐 A browser version is coming soon. +🌐 **Or run it right in your browser, no install:** https://offband.app 🔗 Project home: https://offband.org 📝 Full changelog is in the release notes at the link above. diff --git a/release-notes/1.2.0.md b/release-notes/1.2.0.md index 361bbc6..319f7f9 100644 --- a/release-notes/1.2.0.md +++ b/release-notes/1.2.0.md @@ -9,15 +9,13 @@ shipped to every platform automatically. ### One app, every platform -From this release on, every Offband build ships at once, signed with the same key -so updates just work: +From this release on, every Offband build ships everywhere at once, signed with +the same key so updates just work: - **Android**: install from Google Play or sideload the signed APK - **Windows**: desktop build - **Linux**: desktop build - -A **browser version is on the way** as well, so you will be able to run Offband -with no install at all. More on that soon. +- **Web**: run it right in your browser, no install, at [offband.app](https://offband.app) Pick your platform below. From 5fc41e9808eb94466d295dd79ce906dbf2fff67f Mon Sep 17 00:00:00 2001 From: Strycher Date: Tue, 21 Jul 2026 01:24:46 -0400 Subject: [PATCH 60/60] docs(#359): reframe Discord donation as community-requested, not solicited Owner's call: 'a few of you asked how to chip in, so I put up a page' reads as responsive/humble and implies traction, vs. asking for money. Community-request framing fits Discord (talking to the folks who asked); release-notes donation line stays broader-audience. Part of #160. Agent: SapphireCompass (session 8d755b5e) --- discord/1.2.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/1.2.0.md b/discord/1.2.0.md index 9fc316e..e8157c6 100644 --- a/discord/1.2.0.md +++ b/discord/1.2.0.md @@ -17,4 +17,4 @@ A ground-up interface redesign, a real database under the hood, GIFs that work a 🔗 Project home: https://offband.org 📝 Full changelog is in the release notes at the link above. -❤️ **Offband is free and open source, built by a tiny team.** If it's useful to you, you can help keep it going, and help fund getting it onto the iOS App Store, at https://offband.org/donate. Thank you for being part of it. +❤️ **A few of you have asked how to chip in, so I've put up a donation page:** https://offband.org/donate. Offband is free and open source and always will be. Anything there just helps keep the project going, and helps cover getting it onto the iOS App Store. Thank you all for being part of this.

"),be:s("u"),J:s("u"),gQ:s("u"),n:s("u"),gn:s("u<@>"),t:s("u"),dL:s("u"),c:s("u"),d4:s("u"),r:s("u"),Y:s("u"),bT:s("u<~()>"),aP:s("ax<@>"),T:s("et"),m:s("z"),g:s("bz"),aU:s("aV<@>"),bN:s("cy"),au:s("cy"),e9:s("o>"),cl:s("o"),aS:s("o>"),q:s("o