All checks were successful
CI / Windows build (push) Successful in 14m22s
Make Surface remote debugging and classroom workflows viable: always-on structured logs with one-click zip export, a single AppShell chrome, OOXML PPTX/DOCX annotation without LibreOffice, and a first-class sticky board. Also drop spike/legacy ink widgets and tighten pen feel (predictor, PenInfoHistory, page-tile layer). Co-authored-by: Cursor <cursoragent@cursor.com>
100 lines
2.2 KiB
Dart
100 lines
2.2 KiB
Dart
// Frame / hitch sampler for diagnostic packs.
|
|
|
|
import 'badnote_log.dart';
|
|
|
|
class FrameSample {
|
|
FrameSample({
|
|
required this.at,
|
|
required this.label,
|
|
required this.ms,
|
|
this.dropped = false,
|
|
});
|
|
|
|
final DateTime at;
|
|
final String label;
|
|
final double ms;
|
|
final bool dropped;
|
|
|
|
Map<String, Object?> toJson() => {
|
|
'at': at.toIso8601String(),
|
|
'label': label,
|
|
'ms': ms,
|
|
'dropped': dropped,
|
|
};
|
|
}
|
|
|
|
class FrameSampler {
|
|
FrameSampler._();
|
|
static final FrameSampler instance = FrameSampler._();
|
|
|
|
static const int capacity = 500;
|
|
final List<FrameSample> _samples = <FrameSample>[];
|
|
int overBudget = 0;
|
|
int total = 0;
|
|
|
|
/// Budget for a single frame at 60fps.
|
|
static const double budgetMs = 16.6;
|
|
|
|
void record(String label, double ms, {bool dropped = false}) {
|
|
total++;
|
|
final over = ms > budgetMs;
|
|
if (over) overBudget++;
|
|
final sample = FrameSample(
|
|
at: DateTime.now(),
|
|
label: label,
|
|
ms: ms,
|
|
dropped: dropped || over,
|
|
);
|
|
_samples.add(sample);
|
|
if (_samples.length > capacity) {
|
|
_samples.removeRange(0, _samples.length - capacity);
|
|
}
|
|
if (over || dropped) {
|
|
BadNoteLog.instance.warn(
|
|
LogSubsystem.frame,
|
|
'slow_frame',
|
|
fields: {'label': label, 'ms': ms, 'dropped': dropped},
|
|
);
|
|
}
|
|
}
|
|
|
|
void recordZoom({
|
|
required double rawScale,
|
|
required bool scaleDrop,
|
|
required bool focalDrop,
|
|
required double focalJumpPx,
|
|
}) {
|
|
record(
|
|
'zoom',
|
|
scaleDrop || focalDrop ? budgetMs + 1 : 8,
|
|
dropped: scaleDrop || focalDrop,
|
|
);
|
|
BadNoteLog.instance.debug(
|
|
LogSubsystem.frame,
|
|
'zoom',
|
|
fields: {
|
|
'rawScale': rawScale,
|
|
'scaleDrop': scaleDrop,
|
|
'focalDrop': focalDrop,
|
|
'focalJumpPx': focalJumpPx,
|
|
},
|
|
);
|
|
}
|
|
|
|
List<Map<String, Object?>> toJsonList() =>
|
|
_samples.map((s) => s.toJson()).toList(growable: false);
|
|
|
|
Map<String, Object?> summary() => {
|
|
'total': total,
|
|
'overBudget': overBudget,
|
|
'budgetMs': budgetMs,
|
|
'recent': toJsonList(),
|
|
};
|
|
|
|
void reset() {
|
|
_samples.clear();
|
|
overBudget = 0;
|
|
total = 0;
|
|
}
|
|
}
|