perf: async startup cache sweep, memoize chat name color
the orphaned-cache-file sweep ran a synchronous recursive listSync on the UI isolate at startup against a cache that can hold 10k files; use the async listing instead. memoize adjustChatNameColor keyed by (color, background, target contrast) since chat repeats the same handful of palette colors thousands of times and low-contrast ones pay a 16-step search per call. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
979f46edfe
commit
16f2722a5b
3 changed files with 61 additions and 1 deletions
|
|
@ -23,7 +23,10 @@ class CustomCacheManager {
|
|||
return;
|
||||
}
|
||||
|
||||
final allFiles = cacheDir.listSync(recursive: true).toList();
|
||||
// Async listing — the cache can hold up to maxNrOfCacheObjects (10k)
|
||||
// files, and a synchronous recursive listing that large would block the
|
||||
// UI isolate during startup.
|
||||
final allFiles = await cacheDir.list(recursive: true).toList();
|
||||
|
||||
await _repo.open();
|
||||
final cachedObjects = await _repo.getAllObjects();
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@ double _contrastRatio(Color a, Color b) {
|
|||
return (hi + 0.05) / (lo + 0.05);
|
||||
}
|
||||
|
||||
/// Memoized results for [adjustChatNameColor]. Chat renders the same handful
|
||||
/// of Twitch palette colors thousands of times per session, and every call
|
||||
/// costs luminance math (plus a 16-step search for low-contrast colors) — the
|
||||
/// answer only depends on this key, so compute it once per combination.
|
||||
final _adjustedNameColorCache = <(int, int, double), Color>{};
|
||||
|
||||
/// Adjusts [color] just enough to reach [targetContrast] on the app's background.
|
||||
/// Defaults to Theme.of(context).scaffoldBackgroundColor; if transparent, falls
|
||||
/// back to theme.colorScheme.surface.
|
||||
|
|
@ -44,6 +50,18 @@ Color adjustChatNameColor(
|
|||
bg = theme.colorScheme.surface;
|
||||
}
|
||||
|
||||
final cacheKey = (color.toARGB32(), bg.toARGB32(), targetContrast);
|
||||
final cached = _adjustedNameColorCache[cacheKey];
|
||||
if (cached != null) return cached;
|
||||
|
||||
final adjusted = _adjustNameColorUncached(color, bg, targetContrast);
|
||||
// Custom hex name colors are unbounded in theory; keep the cache small.
|
||||
if (_adjustedNameColorCache.length >= 1024) _adjustedNameColorCache.clear();
|
||||
_adjustedNameColorCache[cacheKey] = adjusted;
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
Color _adjustNameColorUncached(Color color, Color bg, double targetContrast) {
|
||||
if (_contrastRatio(color, bg) >= targetContrast) return color;
|
||||
|
||||
final hsl0 = HSLColor.fromColor(color);
|
||||
|
|
|
|||
|
|
@ -198,6 +198,45 @@ void main() {
|
|||
expect(adjustedColor, isNot(const Color(0xFF00FF00)));
|
||||
});
|
||||
|
||||
testWidgets('memoized results are stable and keyed by background',
|
||||
(tester) async {
|
||||
late Color first;
|
||||
late Color second;
|
||||
late Color onLightBackground;
|
||||
const darkBackground = Color(0xFF121212);
|
||||
const lowContrastColor = Color(0xFF1F1F1F); // Near-black on dark bg
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildTestWidget(
|
||||
scaffoldBackground: darkBackground,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
// Repeated calls with identical inputs must return the same
|
||||
// adjusted color (second call is served from the memo cache).
|
||||
first = adjustChatNameColor(context, lowContrastColor);
|
||||
second = adjustChatNameColor(context, lowContrastColor);
|
||||
// A different background must not be served the dark-background
|
||||
// cache entry: near-black already passes on white, so it should
|
||||
// come back unchanged.
|
||||
onLightBackground = adjustChatNameColor(
|
||||
context,
|
||||
lowContrastColor,
|
||||
background: const Color(0xFFFFFFFF),
|
||||
);
|
||||
return const SizedBox();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(second, first);
|
||||
expect(
|
||||
first.computeLuminance(),
|
||||
greaterThan(lowContrastColor.computeLuminance()),
|
||||
);
|
||||
expect(onLightBackground, lowContrastColor);
|
||||
});
|
||||
|
||||
testWidgets('handles transparent scaffold background', (tester) async {
|
||||
late Color adjustedColor;
|
||||
const darkColor = Color(0xFF000000);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue