From 447deb9df5b9bfe401c232da4090ab0d9f05b6a3 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 23 Oct 2026 03:34:21 +0000 Subject: [PATCH 1/2] Remove unused pose aliases and NFC adapter exports Drop deprecated avatar pose re-exports and test-only animation builders that production never called. Remove sharing's unused NDEF encounter decoder, ingest stream, and related tests; the app reads tags via amiroLinkTextFromNdef and ManagerNfcReader. Co-authored-by: Uday Chauhan --- TODOS.md | 6 +- .../lib/src/avatar_body_pose.dart | 65 -------- .../test/avatar_body_pose_test.dart | 31 +--- packages/sharing/lib/src/ndef_encounter.dart | 99 ------------ .../sharing/test/ndef_encounter_test.dart | 152 ------------------ 5 files changed, 7 insertions(+), 346 deletions(-) diff --git a/TODOS.md b/TODOS.md index 3144521..1f4af7d 100644 --- a/TODOS.md +++ b/TODOS.md @@ -99,10 +99,10 @@ PR4 decisions (`sharing`: `encodeEncounterUri` / `decodeEncounterUri`, `ContactC - Links over 2000 characters are refused on encode and decode. Encode throws rather than silently dropping the contact card. A malformed contact card on receive is dropped without rejecting the identity. - The legacy `amiro://share` link is untouched; the scanner should try encounter first, then legacy, until the app is migrated. -PR5 decisions (`sharing`: `NdefRecordData`, `decodeEncounterFromNdef`, `ingestNfcReads`, library only): +PR5 decisions (`sharing`: `NdefRecordData`, NDEF text/URI decode + `amiroLinkTextFromNdef`, library only): - The boundary is plugin-independent bytes in, validated `EncounterQr` out. The `nfc` package should convert its platform records to `NdefRecordData` when wired. - **Existing `nfc` reader bugs the adapter fixes (not yet migrated):** `ManagerNfcReader` reads only the first record (an Android Application Record first would hide the card) and decodes text with `String.fromCharCodes` (Latin-1, and it ignores the UTF-16 flag). The adapter scans all records, decodes strict UTF-8/UTF-16, and skips URI records with abbreviation prefixes. -- `ingestNfcReads`: reads become `NfcEncounterRead` or `NfcReadRejected`; a hardware dropout (stream error) becomes a rejection instead of an exception and the stream survives; identical reads within a 3 s window are reported once (continuous contact refreshes the window). +- ~~`ingestNfcReads`~~ removed (2026-10-23): never wired in the app; receive screen handles one read per session. - Remaining before NFC works end to end: wire `ManagerNfcReader` into this adapter and have the emulator write the encounter link (`encodeNdefTextPayload`), both in the app-wiring PRs. PR6 decisions (Identity Card, `app/lib/sharing/`): @@ -114,7 +114,7 @@ PR6 decisions (Identity Card, `app/lib/sharing/`): PR7-9 decisions (shipped together as one PR; taken autonomously while the owner was away, so worth a review): - **Cutover.** The Share screen now emits `amiro://encounter` for both QR and NFC. Every receive path (QR scan, NFC receive, deep link) resolves through one `resolveScannedText` -> `ScanResult`, so encounter links open the comparison screen and **legacy `amiro://share` links from older builds still open the profile** (no passport entry, since they carry no collection). Android's manifest gained the `encounter` host (iOS already covers the whole scheme). -- **NFC.** `ManagerNfcReader` now uses the PR5 adapter (all records, strict UTF-8/UTF-16), fixing the first-record-only and Latin-1 bugs; `decodeNdefTextPayload` and its test were removed as superseded. `nfc` now depends on `sharing`. The native HCE tag (2 KB, UTF-8 text record) fits a 2000-char encounter link. **Not exercised on real hardware.** `ingestNfcReads` (dedupe/dropout) is not used by the app: the receive screen already handles one read per session and errors itself. +- **NFC.** `ManagerNfcReader` now uses the PR5 adapter (all records, strict UTF-8/UTF-16), fixing the first-record-only and Latin-1 bugs; `decodeNdefTextPayload` and its test were removed as superseded. `nfc` now depends on `sharing`. The native HCE tag (2 KB, UTF-8 text record) fits a 2000-char encounter link. **Not exercised on real hardware.** - **Encounter screen** is two-phase: showing a card writes nothing; "Save to passport" / "Update passport" commits via `EncounterProcessor.save`. Backing out changes nothing, including a known person's count. Your own card is `SelfEncounter`. The screen waits for the identity to load first so a cold-start deep link can't miss the own-card check. - **Only their avatar is 3D** on the comparison screen; yours is a text summary. Two live Filament views side by side is unverified on a device. - **Contact fallback.** If the contact card is what pushes the link past the QR size limit, `buildOwnEncounterUri` drops it and shares the identity alone rather than failing to share (the library's `encodeEncounterUri` still throws; only the app chooses to fall back). diff --git a/packages/avatar_renderer/lib/src/avatar_body_pose.dart b/packages/avatar_renderer/lib/src/avatar_body_pose.dart index 6012a69..2c07f8e 100644 --- a/packages/avatar_renderer/lib/src/avatar_body_pose.dart +++ b/packages/avatar_renderer/lib/src/avatar_body_pose.dart @@ -16,13 +16,6 @@ bool shouldApplySkinnedHumanoidProceduralMotion(String assetPath) { base.startsWith('shoes_'); } -/// Whether [assetPath] points at a skinned humanoid body that ships in bind -/// (T) pose with no embedded idle animation. -@Deprecated('Use shouldApplySkinnedHumanoidProceduralMotion') -bool shouldApplyRelaxedArmPose(String assetPath) { - return shouldApplySkinnedHumanoidProceduralMotion(assetPath); -} - /// Bone names on the Quaternius Superhero / Mixamo-compatible skeleton shipped /// as [body_superhero_male.glb]. const relaxedArmPoseBones = ['upperarm_l', 'upperarm_r']; @@ -49,29 +42,6 @@ Quaternion _boneLocalRotation(Vector3 axis, double radians) { /// vector_math 2.2+ — use operator `*` (mutable [Quaternion.multiply] removed). Quaternion _combineBoneRotations(Quaternion a, Quaternion b) => a * b; -/// Builds a single-frame skeletal hold that drops the upper arms from the glTF -/// bind (T) pose. Rotations are in [Space.Bone] — deltas applied on top of -/// each bone's rest local transform inside thermion's [addBoneAnimation]. -/// -/// Sign convention verified against `body_superhero_male.glb` on device (PR #35): -/// **left −π/2**, **right +π/2** about bone-local +Z — the opposite pairing -/// swings arms toward world +Y (overhead). -thermion.BoneAnimationData buildRelaxedArmPoseAnimation({ - double armDownRadians = math.pi / 2, -}) { - final frame = confidentFashionArmPoseFrame( - armDownRadians: armDownRadians, - ); - return thermion.BoneAnimationData( - relaxedArmPoseBones, - [ - [frame.left, frame.right], - ], - frameLengthInMs: 60000, - space: thermion.Space.Bone, - ); -} - /// Boutique idle arm hold: **only** the device-verified upper-arm Z twist. /// /// Extra X/Y deltas on `upperarm_*` stacked a second limb on front camera @@ -89,14 +59,6 @@ thermion.BoneAnimationData buildRelaxedArmPoseAnimation({ ); } -/// Arm-space deltas reused in every frame of the presentation loop. -@Deprecated('Use confidentFashionArmPoseFrame') -({thermion.Transform left, thermion.Transform right}) relaxedArmPoseFrame({ - double armDownRadians = math.pi / 2, -}) { - return confidentFashionArmPoseFrame(armDownRadians: armDownRadians); -} - /// Static contrapposto / open-chest offsets layered under breath and sway. thermion.SkeletonTransform confidentFashionTorsoFrame({ required double breath, @@ -165,28 +127,6 @@ thermion.BoneAnimationData buildSkinnedHumanoidPresentationAnimation({ ); } -/// Procedural "living statue" loop (idle bones only). Prefer -/// [buildSkinnedHumanoidPresentationAnimation] at runtime. -thermion.BoneAnimationData buildLivingStatueIdleAnimation({ - int numFrames = 120, - double loopDurationSeconds = 5.5, -}) { - final full = buildSkinnedHumanoidPresentationAnimation( - numFrames: numFrames, - loopDurationSeconds: loopDurationSeconds, - ); - return thermion.BoneAnimationData( - livingStatueIdleBones, - full.frameData - .map( - (frame) => frame.sublist(relaxedArmPoseBones.length), - ) - .toList(), - frameLengthInMs: full.frameLengthInMs, - space: thermion.Space.Bone, - ); -} - /// Applies relaxed arms plus looping idle motion to a loaded skinned asset. Future applySkinnedHumanoidProceduralMotion( thermion.ThermionAsset asset, @@ -203,8 +143,3 @@ Future applySkinnedHumanoidProceduralMotion( loop: true, ); } - -@Deprecated('Use applySkinnedHumanoidProceduralMotion') -Future applyRelaxedArmPose(thermion.ThermionAsset asset) async { - await applySkinnedHumanoidProceduralMotion(asset); -} diff --git a/packages/avatar_renderer/test/avatar_body_pose_test.dart b/packages/avatar_renderer/test/avatar_body_pose_test.dart index 2c3a1ac..ecefaae 100644 --- a/packages/avatar_renderer/test/avatar_body_pose_test.dart +++ b/packages/avatar_renderer/test/avatar_body_pose_test.dart @@ -2,7 +2,6 @@ import 'dart:math' as math; import 'package:avatar_renderer/src/avatar_body_pose.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:thermion_flutter/thermion_flutter.dart' as thermion; void main() { group('shouldApplySkinnedHumanoidProceduralMotion', () { @@ -49,24 +48,6 @@ void main() { }); }); - group('buildRelaxedArmPoseAnimation', () { - test('uses device-verified −Z/+Z twist pairing for left/right arms', () { - final animation = buildRelaxedArmPoseAnimation( - armDownRadians: math.pi / 2, - ); - - expect(animation.bones, relaxedArmPoseBones); - expect(animation.space, thermion.Space.Bone); - expect(animation.numFrames, 1); - - final frame = animation.frameData.single; - final left = frame[0].rotation; - final right = frame[1].rotation; - expect(left, isNot(equals(right))); - expect(left, equals(confidentFashionArmPoseFrame().left.rotation)); - }); - }); - group('buildSkinnedHumanoidPresentationAnimation', () { test('includes arm hold on every idle frame', () { final animation = buildSkinnedHumanoidPresentationAnimation( @@ -87,6 +68,10 @@ void main() { final hold = confidentFashionArmPoseFrame(armDownRadians: math.pi / 2); expect(hold.left.rotation.z, lessThan(0)); expect(hold.right.rotation.z, greaterThan(0)); + expect( + hold.left.rotation, + isNot(equals(hold.right.rotation)), + ); }); test('loop wrap has only a tiny discontinuity at frame 0', () { @@ -103,12 +88,4 @@ void main() { } }); }); - - group('buildLivingStatueIdleAnimation', () { - test('targets spine and pelvis only', () { - final animation = buildLivingStatueIdleAnimation(numFrames: 60); - expect(animation.bones, livingStatueIdleBones); - expect(animation.frameData.first.length, livingStatueIdleBones.length); - }); - }); } diff --git a/packages/sharing/lib/src/ndef_encounter.dart b/packages/sharing/lib/src/ndef_encounter.dart index 4d6d139..50fbaa7 100644 --- a/packages/sharing/lib/src/ndef_encounter.dart +++ b/packages/sharing/lib/src/ndef_encounter.dart @@ -1,10 +1,6 @@ -import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; -import 'amiro_sharing_payload.dart'; -import 'encounter_qr.dart'; - /// NDEF type-name-format for well-known records (text `T`, URI `U`). const ndefTnfWellKnown = 0x01; @@ -12,8 +8,6 @@ const ndefTnfWellKnown = 0x01; /// capped well below this; anything bigger is not an Amiro card. const maxNdefRecordBytes = 4096; -const _kEncounterPrefix = 'amiro://encounter'; - /// One NDEF record as raw bytes, independent of any NFC plugin. The `nfc` /// package converts its platform records into this; nothing here knows how /// they were read. @@ -86,19 +80,6 @@ String _utf16(List bytes) { ]); } -/// The first record that carries an `amiro://encounter` link, skipping -/// anything else on the tag (an Android Application Record, say). The first -/// such record decides: a bad one is not papered over by a later good one. -/// Throws [SharingPayloadException] (malformed) if there is none. -String encounterTextFromNdef(List records) { - for (final record in records) { - final text = ndefRecordText(record); - if (text != null && text.startsWith(_kEncounterPrefix)) return text; - } - throw const SharingPayloadException( - PayloadErrorKind.malformed, 'no Amiro encounter record on the tag'); -} - /// The first record on the tag whose text is any `amiro://` link, encounter /// or legacy share, or null. For callers that hand the text on to a resolver /// that understands both formats. @@ -109,83 +90,3 @@ String? amiroLinkTextFromNdef(List records) { } return null; } - -/// NFC boundary: tag records in, validated encounter out. Throws -/// [SharingPayloadException] for anything that isn't a valid card. -EncounterQr decodeEncounterFromNdef(List records) { - return decodeEncounterUri(encounterTextFromNdef(records)); -} - -/// What a tag read turned into. Downstream code sees only these two: never -/// raw bytes, partial data or an exception. -sealed class NfcRead { - const NfcRead(); -} - -final class NfcEncounterRead extends NfcRead { - final EncounterQr encounter; - - const NfcEncounterRead(this.encounter); -} - -final class NfcReadRejected extends NfcRead { - final SharingPayloadException error; - - const NfcReadRejected(this.error); -} - -/// Adapts a stream of raw tag reads into [NfcRead]s. -/// -/// - Every read is validated; invalid ones become [NfcReadRejected]. -/// - A hardware dropout (an error on [source]) becomes a rejection and the -/// stream keeps going, so a flaky connection can't crash or wedge callers. -/// - A tag held against the phone reads many times; an identical read -/// inside [duplicateWindow] of the previous one is dropped. Continuous -/// contact keeps refreshing the window, so it is reported once. -Stream ingestNfcReads( - Stream> source, { - DateTime Function()? clock, - Duration duplicateWindow = const Duration(seconds: 3), -}) { - final now = clock ?? DateTime.now; - final lastSeen = {}; - - void emit(EventSink sink, String key, NfcRead read) { - final at = now(); - lastSeen - .removeWhere((_, seenAt) => at.difference(seenAt) >= duplicateWindow); - final previous = lastSeen[key]; - lastSeen[key] = at; - if (previous == null) sink.add(read); - } - - return source.transform( - StreamTransformer, NfcRead>.fromHandlers( - handleData: (records, sink) { - String? key; - try { - final text = encounterTextFromNdef(records); - key = 'link:$text'; - emit(sink, key, NfcEncounterRead(decodeEncounterUri(text))); - } on SharingPayloadException catch (e) { - emit(sink, key ?? 'rejected:${e.message}', NfcReadRejected(e)); - } catch (_) { - emit( - sink, - 'rejected:unreadable', - const NfcReadRejected(SharingPayloadException( - PayloadErrorKind.malformed, 'the tag could not be read')), - ); - } - }, - handleError: (error, stackTrace, sink) { - emit( - sink, - 'dropout', - const NfcReadRejected(SharingPayloadException( - PayloadErrorKind.malformed, 'the NFC read was interrupted')), - ); - }, - ), - ); -} diff --git a/packages/sharing/test/ndef_encounter_test.dart b/packages/sharing/test/ndef_encounter_test.dart index c7caad8..0cf1b9f 100644 --- a/packages/sharing/test/ndef_encounter_test.dart +++ b/packages/sharing/test/ndef_encounter_test.dart @@ -1,4 +1,3 @@ -import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; @@ -157,63 +156,6 @@ void main() { }); }); - group('decodeEncounterFromNdef', () { - test('a text record round-trips payload and contact card', () { - final decoded = decodeEncounterFromNdef([_text(_link)]); - - expect(decoded.payload, _payload()); - expect(decoded.contact, const ContactCard(username: 'uday')); - }); - - test('a URI record round-trips too', () { - expect(decodeEncounterFromNdef([_uri(_link)]).payload, _payload()); - }); - - test('finds the encounter behind an Android Application Record', () { - expect( - decodeEncounterFromNdef([_androidAppRecord(), _text(_link)]).payload, - _payload()); - }); - - test('no records, or none that is an encounter, is malformed', () { - for (final records in [ - [], - [_androidAppRecord()], - [_text('just some text')], - [ - _text('amiro://share?d=abc') - ], // a legacy share link is not an encounter - ]) { - expect( - () => decodeEncounterFromNdef(records), - throwsA(isA() - .having((e) => e.kind, 'kind', PayloadErrorKind.malformed)), - ); - } - }); - - test( - 'an encounter record with a bad payload is rejected with the payload error', - () { - final future = - 'amiro://encounter?d=${base64Url.encode(utf8.encode('{"schemaVersion":2}')).replaceAll('=', '')}'; - - expect( - () => decodeEncounterFromNdef([_text(future)]), - throwsA(isA().having( - (e) => e.kind, 'kind', PayloadErrorKind.unsupportedVersion)), - ); - }); - - test('an invalid encounter record is not papered over by a later valid one', - () { - final bad = 'amiro://encounter?d=***'; - - expect(() => decodeEncounterFromNdef([_text(bad), _text(_link)]), - throwsA(isA())); - }); - }); - group('encodeNdefTextPayload', () { test('writes a status byte, the language code, then UTF-8 text', () { expect(encodeNdefTextPayload('hi'), @@ -221,100 +163,6 @@ void main() { }); }); - group('ingestNfcReads', () { - late StreamController> source; - late DateTime now; - late Stream reads; - late List seen; - - setUp(() { - source = StreamController>(); - now = DateTime.utc(2026, 9, 26, 10); - reads = ingestNfcReads(source.stream, - clock: () => now, duplicateWindow: const Duration(seconds: 3)); - seen = []; - reads.listen(seen.add); - }); - - tearDown(() => source.close()); - - Future tick() => Future.delayed(Duration.zero); - - test('a valid tag becomes an NfcEncounterRead', () async { - source.add([_text(_link)]); - await tick(); - - expect(seen.single, isA()); - expect((seen.single as NfcEncounterRead).encounter.payload, _payload()); - }); - - test('garbage becomes a rejection, never an exception', () async { - source.add([_text('hello')]); - await tick(); - - expect(seen.single, isA()); - }); - - test('a hardware dropout becomes a rejection and the stream keeps working', - () async { - source.addError(StateError('tag lost')); - await tick(); - now = now.add(const Duration(seconds: 10)); - source.add([_text(_link)]); - await tick(); - - expect(seen.first, isA()); - expect(seen.last, isA()); - expect(seen, hasLength(2)); - }); - - test('the same tag read repeatedly inside the window is reported once', - () async { - for (var i = 0; i < 4; i++) { - source.add([_text(_link)]); - now = now.add(const Duration(milliseconds: 400)); - await tick(); - } - - expect(seen, hasLength(1)); - }); - - test('the same tag again after the window is reported again', () async { - source.add([_text(_link)]); - await tick(); - now = now.add(const Duration(seconds: 4)); - source.add([_text(_link)]); - await tick(); - - expect(seen, hasLength(2)); - }); - - test('two different tags close together are both reported', () async { - source.add([_text(_link)]); - source.add([_text(encodeEncounterUri(_payload(name: 'Ada')))]); - await tick(); - - expect(seen, hasLength(2)); - }); - - test('repeated identical garbage inside the window is one rejection', - () async { - source.add([_text('hello')]); - source.add([_text('hello')]); - await tick(); - - expect(seen, hasLength(1)); - }); - - test('closing the source closes the stream', () async { - final done = Completer(); - ingestNfcReads(Stream>.empty()) - .listen(null, onDone: done.complete); - - await done.future; - }); - }); - group('amiroLinkTextFromNdef', () { test('finds an encounter link, a legacy share link, or nothing', () { expect(amiroLinkTextFromNdef([_text(_link)]), _link); From a4b42f7bd751fc7265c3f5f277ab639da730ad81 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 23 Oct 2026 04:01:20 +0000 Subject: [PATCH 2/2] Tighten avatar_renderer public exports and refresh TODOS Stop exporting internal asset paths and Filament test seams from the package barrel; tests import src paths directly. Align TODOS with the shipped encounter NFC path and removed NDEF adapter APIs. Co-authored-by: Uday Chauhan --- TODOS.md | 9 +++------ packages/avatar_renderer/lib/avatar_renderer.dart | 3 +-- .../avatar_renderer/test/avatar_asset_resolver_test.dart | 2 +- .../test/thermion_avatar_renderer_test.dart | 4 ++-- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/TODOS.md b/TODOS.md index 1f4af7d..8d19e6a 100644 --- a/TODOS.md +++ b/TODOS.md @@ -97,18 +97,15 @@ PR4 decisions (`sharing`: `encodeEncounterUri` / `decodeEncounterUri`, `ContactC - Decision (2026-09-26): keep the discovery payload minimal and carry contact details on a separate `c` parameter of the same link, `amiro://encounter?d=[&c=]`. Adding optional contact fields to the payload later stays open (additive under schema v1); nothing here prevents it. - `ContactCard` follows `Identity.publicFields()` strictly (`username` only if flagged public, unlike the legacy `amiro://share` codec which always sends it); fields capped at 256 characters. - Links over 2000 characters are refused on encode and decode. Encode throws rather than silently dropping the contact card. A malformed contact card on receive is dropped without rejecting the identity. -- The legacy `amiro://share` link is untouched; the scanner should try encounter first, then legacy, until the app is migrated. +- Receive paths try encounter first, then legacy `amiro://share` (see PR7 cutover below). PR5 decisions (`sharing`: `NdefRecordData`, NDEF text/URI decode + `amiroLinkTextFromNdef`, library only): -- The boundary is plugin-independent bytes in, validated `EncounterQr` out. The `nfc` package should convert its platform records to `NdefRecordData` when wired. -- **Existing `nfc` reader bugs the adapter fixes (not yet migrated):** `ManagerNfcReader` reads only the first record (an Android Application Record first would hide the card) and decodes text with `String.fromCharCodes` (Latin-1, and it ignores the UTF-16 flag). The adapter scans all records, decodes strict UTF-8/UTF-16, and skips URI records with abbreviation prefixes. -- ~~`ingestNfcReads`~~ removed (2026-10-23): never wired in the app; receive screen handles one read per session. -- Remaining before NFC works end to end: wire `ManagerNfcReader` into this adapter and have the emulator write the encounter link (`encodeNdefTextPayload`), both in the app-wiring PRs. +- Plugin-independent bytes in; `ManagerNfcReader` converts platform records to `NdefRecordData` and reads the first `amiro://` link via `amiroLinkTextFromNdef` (all records, strict UTF-8/UTF-16). Android HCE builds matching NDEF text records in Kotlin (`AmiroHceService.buildNdefTextMessage`). +- ~~`ingestNfcReads`~~ / ~~`decodeEncounterFromNdef`~~ removed (2026-10-23): never used in the app; tag text is resolved in `scan_result.dart` instead. PR6 decisions (Identity Card, `app/lib/sharing/`): - `IdentityCard` is purely presentational (no providers, no discovery): it draws an `AmiroSharingPayload` plus an avatar widget and a QR string, so what the card shows is exactly what the payload carries. `buildOwnEncounterPayload` composes that payload from identity, the live avatar and purchases; `progressFromCompletion` resolves series for display and skips series this app version doesn't know. - Card content follows DESIGN.md: serif name, up to 3 standout (above-Common) items with rarity labels (brass from Rare up), series progress in mono, real offset shadow, no glow. Common items are deliberately not listed. -- **The QR/NFC link is still the legacy `amiro://share` link.** The card describes the identity with the encounter payload, but switching the emitted link to `amiro://encounter` waits for PR7, when the scanner, NFC receive and deep links learn to read it (otherwise a new build's QR would be unreadable by the current scanner). PR7 must also migrate the NFC emulator/reader onto `encodeNdefTextPayload` / `decodeEncounterFromNdef`. - `CosmeticSeries.id` (`series_1`) and `CosmeticRarity.wireName` / `fromWireName` are the wire spellings; an unknown rarity is ignored, never guessed. - Not eyeballed on a device yet. diff --git a/packages/avatar_renderer/lib/avatar_renderer.dart b/packages/avatar_renderer/lib/avatar_renderer.dart index 882abed..8aa20fc 100644 --- a/packages/avatar_renderer/lib/avatar_renderer.dart +++ b/packages/avatar_renderer/lib/avatar_renderer.dart @@ -1,4 +1,3 @@ export 'src/avatar_renderer_interface.dart'; -export 'src/avatar_asset_resolver.dart'; export 'src/thermion_view_detach_gate.dart'; -export 'src/thermion_avatar_renderer.dart' show ThermionAvatarRenderer, ThermionFilamentSurface, FilamentSurface; +export 'src/thermion_avatar_renderer.dart' show ThermionAvatarRenderer; diff --git a/packages/avatar_renderer/test/avatar_asset_resolver_test.dart b/packages/avatar_renderer/test/avatar_asset_resolver_test.dart index 33f0d7d..35829f8 100644 --- a/packages/avatar_renderer/test/avatar_asset_resolver_test.dart +++ b/packages/avatar_renderer/test/avatar_asset_resolver_test.dart @@ -1,4 +1,4 @@ -import 'package:avatar_renderer/avatar_renderer.dart'; +import 'package:avatar_renderer/src/avatar_asset_resolver.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { diff --git a/packages/avatar_renderer/test/thermion_avatar_renderer_test.dart b/packages/avatar_renderer/test/thermion_avatar_renderer_test.dart index fc1a18f..8f58033 100644 --- a/packages/avatar_renderer/test/thermion_avatar_renderer_test.dart +++ b/packages/avatar_renderer/test/thermion_avatar_renderer_test.dart @@ -1,6 +1,6 @@ import 'package:avatar_core/avatar_core.dart'; -import 'package:avatar_renderer/avatar_renderer.dart'; -import 'package:avatar_renderer/src/thermion_avatar_renderer.dart'; +import 'package:avatar_renderer/src/thermion_avatar_renderer.dart' + show FilamentSurface, ThermionAvatarRenderer; import 'package:flutter_test/flutter_test.dart'; /// Fakes the Filament engine calls so this test doesn't require a live