react-os-shell 4.3.1 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/{Browser-674YMWMK.js → Browser-ISK7W7TB.js} +5 -5
  2. package/dist/{Browser-674YMWMK.js.map → Browser-ISK7W7TB.js.map} +1 -1
  3. package/dist/{Calculator-3ULKK5ZA.js → Calculator-GBC3FLAJ.js} +4 -4
  4. package/dist/{Calculator-3ULKK5ZA.js.map → Calculator-GBC3FLAJ.js.map} +1 -1
  5. package/dist/ConfirmDialog-R7NZVI2V.js +4 -0
  6. package/dist/{ConfirmDialog-XJP67AHR.js.map → ConfirmDialog-R7NZVI2V.js.map} +1 -1
  7. package/dist/{CurrencyConverter-JC2N7AY3.js → CurrencyConverter-URPZQBNV.js} +4 -4
  8. package/dist/{CurrencyConverter-JC2N7AY3.js.map → CurrencyConverter-URPZQBNV.js.map} +1 -1
  9. package/dist/{Documents-MJKEUOOL.js → Documents-KUAQ3EKC.js} +5 -5
  10. package/dist/{Documents-MJKEUOOL.js.map → Documents-KUAQ3EKC.js.map} +1 -1
  11. package/dist/{Files-BTZMPTL6.js → Files-OTPHB4BP.js} +5 -5
  12. package/dist/{Files-BTZMPTL6.js.map → Files-OTPHB4BP.js.map} +1 -1
  13. package/dist/{Notepad-NGLFEJLA.js → Notepad-3NVFGEO5.js} +5 -5
  14. package/dist/{Notepad-NGLFEJLA.js.map → Notepad-3NVFGEO5.js.map} +1 -1
  15. package/dist/{PomodoroTimer-RPCRTUKC.js → PomodoroTimer-QQOMBVP3.js} +5 -5
  16. package/dist/{PomodoroTimer-RPCRTUKC.js.map → PomodoroTimer-QQOMBVP3.js.map} +1 -1
  17. package/dist/{Preview-H4S427VB.js → Preview-7WQHKQ6O.js} +5 -5
  18. package/dist/{Preview-H4S427VB.js.map → Preview-7WQHKQ6O.js.map} +1 -1
  19. package/dist/{Spreadsheet-2YYUKKSY.js → Spreadsheet-YVL2IZA6.js} +5 -5
  20. package/dist/{Spreadsheet-2YYUKKSY.js.map → Spreadsheet-YVL2IZA6.js.map} +1 -1
  21. package/dist/{Stock-YTQ2OYQ7.js → Stock-33C4U7CR.js} +4 -4
  22. package/dist/{Stock-YTQ2OYQ7.js.map → Stock-33C4U7CR.js.map} +1 -1
  23. package/dist/{Weather-4I4EFUJO.js → Weather-G3XHTB75.js} +4 -4
  24. package/dist/{Weather-4I4EFUJO.js.map → Weather-G3XHTB75.js.map} +1 -1
  25. package/dist/{WorldClock-7PZI7VRE.js → WorldClock-ZUVI3LBN.js} +4 -4
  26. package/dist/{WorldClock-7PZI7VRE.js.map → WorldClock-ZUVI3LBN.js.map} +1 -1
  27. package/dist/apps/index.js +15 -15
  28. package/dist/{chunk-PMPJLVZV.js → chunk-5HCDJPX7.js} +3 -3
  29. package/dist/{chunk-PMPJLVZV.js.map → chunk-5HCDJPX7.js.map} +1 -1
  30. package/dist/chunk-DLVSSR77.js +7 -0
  31. package/dist/{chunk-K6MB36UJ.js.map → chunk-DLVSSR77.js.map} +1 -1
  32. package/dist/{chunk-VA6M46NL.js → chunk-EF5W3EXL.js} +47 -10
  33. package/dist/chunk-EF5W3EXL.js.map +1 -0
  34. package/dist/{chunk-YF2XNVGN.js → chunk-LSFZJZTQ.js} +3 -3
  35. package/dist/{chunk-YF2XNVGN.js.map → chunk-LSFZJZTQ.js.map} +1 -1
  36. package/dist/{chunk-ASRXGCSQ.js → chunk-NIOJDCAU.js} +4 -4
  37. package/dist/{chunk-ASRXGCSQ.js.map → chunk-NIOJDCAU.js.map} +1 -1
  38. package/dist/index.d.ts +411 -155
  39. package/dist/index.js +357 -81
  40. package/dist/index.js.map +1 -1
  41. package/package.json +1 -1
  42. package/dist/ConfirmDialog-XJP67AHR.js +0 -4
  43. package/dist/chunk-K6MB36UJ.js +0 -7
  44. package/dist/chunk-VA6M46NL.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -881,6 +881,377 @@ declare function WindowManagerProvider({ children, windowAccentForRoute }: {
881
881
  windowAccentForRoute?: (route: string) => string | undefined;
882
882
  }): react_jsx_runtime.JSX.Element;
883
883
 
884
+ /**
885
+ * Frame-timing maths + bottleneck attribution for the desktop perf HUD.
886
+ *
887
+ * Split out from `PerfStats.tsx` because the interesting part is the
888
+ * *attribution*, and attribution is pure: given a frame rate and how much of
889
+ * the wall clock the main thread spent blocked, decide whether a janky UI is
890
+ * the GPU's fault or JavaScript's. That decision is what the HUD exists for —
891
+ * "the UI feels laggy" is not actionable, "you are GPU-bound" is — so it lives
892
+ * where a test can pin it.
893
+ *
894
+ * The discriminator: frames are landing late, but the main thread is idle.
895
+ * If JS were the problem the main thread would be busy, so late frames plus an
896
+ * idle thread means the cost is downstream of script — compositing, paint,
897
+ * backdrop-filter. The shell leans hard on frosted glass (`utils/glass.ts`
898
+ * blurs at a 40px radius), which is exactly the kind of work that shows up
899
+ * here and nowhere in a JS profile.
900
+ */
901
+ /** Frame rate at or above which we call the UI smooth. Below 60 because
902
+ * rAF sampling is noisy and a display may be capped at 60Hz anyway — a
903
+ * steady 55 is not a complaint anyone would file. */
904
+ declare const SMOOTH_FPS = 50;
905
+ /** Share of wall-clock time in long tasks that makes JS the prime suspect.
906
+ * `longtask` entries are >50ms by definition, so even 20% means the thread
907
+ * is stalling several times a second — visible as jank on its own. */
908
+ declare const BLOCKED_PCT_CPU = 20;
909
+ type BottleneckKind = 'smooth' | 'gpu' | 'cpu' | 'unknown';
910
+ interface PerfReading {
911
+ /** Frames per second across the sample window. */
912
+ fps: number;
913
+ /** Mean frame interval, ms. */
914
+ frameMs: number;
915
+ /** Slowest single frame in the window, ms — where the jank actually lives. */
916
+ worstMs: number;
917
+ /** Percentage of wall-clock time the main thread spent inside long tasks
918
+ * (0–100), or null when the browser exposes no `longtask` observer.
919
+ * Null is load-bearing: without it we cannot separate GPU from CPU, and
920
+ * guessing would defeat the point of the HUD. */
921
+ blockedPct: number | null;
922
+ }
923
+ interface Verdict {
924
+ kind: BottleneckKind;
925
+ /** Headline, for the HUD's verdict row. */
926
+ label: string;
927
+ /** One line of what to do about it. */
928
+ detail: string;
929
+ }
930
+ /** Turn a window of rAF timestamps into a frame-rate reading.
931
+ * Needs two timestamps to describe one interval; anything less is not yet a
932
+ * measurement and reports zeroes rather than a fabricated 0 fps. */
933
+ declare function summariseFrames(timestamps: number[]): Pick<PerfReading, 'fps' | 'frameMs' | 'worstMs'>;
934
+ /**
935
+ * Attribute a reading to a bottleneck.
936
+ *
937
+ * Order matters: smooth wins outright (nobody cares what a healthy frame rate
938
+ * is *not* bound by), and an unknown block share can only ever downgrade to
939
+ * `unknown` — never to `gpu`. Blaming the GPU because the browser withheld
940
+ * long-task timing would be a confident wrong answer, which is worse than no
941
+ * answer when someone is about to go change settings on the strength of it.
942
+ */
943
+ declare function classify(reading: PerfReading): Verdict;
944
+
945
+ /**
946
+ * Session log for the desktop perf HUD — and the analysis that turns it into
947
+ * a conclusion.
948
+ *
949
+ * A live frame rate tells you the UI is slow *now*. It does not tell you what
950
+ * made it slow, and the person watching it is usually the last person able to
951
+ * say. So while the HUD is on, every reading is stamped with what was
952
+ * happening around it: how many windows were open, which one was on top,
953
+ * whether the user was clicking, typing, scrolling or dragging. The log is
954
+ * exportable, so a laggy machine somewhere else can produce evidence rather
955
+ * than an adjective.
956
+ *
957
+ * The maths here is deliberately non-clever. Median rather than mean, because
958
+ * one 400ms stall would drag a mean somewhere no frame ever was. Buckets
959
+ * rather than a fitted curve, because "6 windows open halves the frame rate"
960
+ * is a sentence someone can act on and a correlation coefficient is not.
961
+ */
962
+
963
+ /** One flush interval's worth of measurement plus its context. Keys are short
964
+ * because thousands of these get JSON-serialised into localStorage and into
965
+ * whatever the user sends afterwards. */
966
+ interface PerfLogRecord {
967
+ /** Milliseconds since logging began. */
968
+ t: number;
969
+ fps: number;
970
+ frameMs: number;
971
+ worstMs: number;
972
+ blockedPct: number | null;
973
+ heapMB: number | null;
974
+ verdict: BottleneckKind;
975
+ /** Open shell windows at the moment of the reading. */
976
+ windows: number;
977
+ /** Identity of the topmost window, when there is one — so a summary can name
978
+ * the screen that was slow rather than just the count. */
979
+ active: string | null;
980
+ clicks: number;
981
+ keys: number;
982
+ scrolls: number;
983
+ /** Milliseconds spent with the pointer down and moving. Dragging is the
984
+ * single most compositing-heavy thing a user does in a window shell, so it
985
+ * gets its own axis rather than being lumped in with clicks. Covers any
986
+ * drag — a window gesture, a desktop icon, a text selection; `moveMs` and
987
+ * `resizeMs` below are the window subset of it. */
988
+ dragMs: number;
989
+ /** Start-menu opens (`perfEvents`). */
990
+ menus: number;
991
+ /** Flyout opens — 2nd- and 3rd-level menus. Hover-opened flyouts fire no
992
+ * click and no keypress, so before this axis existed the frame a submenu
993
+ * painted on was filed as *idle*, which is where the jank people actually
994
+ * report was going missing. */
995
+ submenus: number;
996
+ /** Last menu or flyout opened in the interval, so one can be named. */
997
+ menuKey: string | null;
998
+ /** Milliseconds spent dragging a window by its title bar. */
999
+ moveMs: number;
1000
+ /** Milliseconds spent dragging a window's resize handle. */
1001
+ resizeMs: number;
1002
+ }
1003
+ interface FpsGroup {
1004
+ samples: number;
1005
+ /** Median across the group's *measurable* samples — see `summariseLog`. Zero
1006
+ * when every sample in the group stalled. */
1007
+ medianFps: number;
1008
+ /** Slowest single frame anywhere in the group. For a group of brief
1009
+ * interactions this is the number that matters: a flyout that costs one
1010
+ * 300ms frame barely moves a median but is exactly what the user saw. */
1011
+ worstMs: number;
1012
+ /** Samples too blocked to report a frame rate at all. Kept beside the median
1013
+ * rather than folded into it, so a group that is entirely stalls reads as
1014
+ * the emergency it is instead of as a 0 fps reading. */
1015
+ stalls: number;
1016
+ }
1017
+ /**
1018
+ * What the user was doing during a sample, reduced to one label.
1019
+ *
1020
+ * A single interval routinely carries several axes at once — opening a flyout
1021
+ * involves a click, a pointer move and a menu mark — so the ranking below
1022
+ * decides which one the sample is filed under. It runs most-specific first:
1023
+ * the deliberate, expensive gesture beats the incidental click that came with
1024
+ * it, because filing a janky submenu open under "click" is how you end up
1025
+ * optimising the wrong thing.
1026
+ */
1027
+ type ActivityKind = 'submenu' | 'menu' | 'resize' | 'move' | 'scroll' | 'type' | 'click' | 'idle';
1028
+ /** File a sample under the one thing most likely to have cost it. */
1029
+ declare function classifyActivity(r: PerfLogRecord): ActivityKind;
1030
+ interface LogSummary {
1031
+ samples: number;
1032
+ durationMs: number;
1033
+ medianFps: number;
1034
+ worstFrameMs: number;
1035
+ /** Fraction of samples in each verdict, 0–1. */
1036
+ verdictShare: Record<BottleneckKind, number>;
1037
+ /** Split by whether the user was doing anything. The gap between these two
1038
+ * is the headline: a desktop that is smooth at rest and janky in use has a
1039
+ * rendering cost that only shows up under interaction. */
1040
+ interacting: FpsGroup | null;
1041
+ idle: FpsGroup | null;
1042
+ /** Frame rate per kind of interaction, worst-first — the answer to "which
1043
+ * gesture is slow", which is the question a report is usually filed to
1044
+ * ask. */
1045
+ byActivity: (FpsGroup & {
1046
+ kind: ActivityKind;
1047
+ })[];
1048
+ /** Frame rate against how many windows were open. */
1049
+ byWindowCount: (FpsGroup & {
1050
+ windows: number;
1051
+ })[];
1052
+ /** Windows ranked worst-first, so the slowest screen names itself. */
1053
+ worstWindows: (FpsGroup & {
1054
+ key: string;
1055
+ })[];
1056
+ /** Menus and flyouts ranked worst-first, same idea one layer up. */
1057
+ worstMenus: (FpsGroup & {
1058
+ key: string;
1059
+ })[];
1060
+ }
1061
+ /** Records kept in memory before the oldest are dropped. At one record per
1062
+ * 500ms this is about 20 minutes — long enough to catch an intermittent
1063
+ * stall, small enough to serialise without thinking about it. */
1064
+ declare const LOG_CAP = 2400;
1065
+ /** Minimum samples before a group is reported. One unlucky reading is not a
1066
+ * finding, and a summary that names a window off a single sample invites
1067
+ * someone to go optimise the wrong screen. */
1068
+ declare const MIN_GROUP_SAMPLES = 4;
1069
+ /** The floor for menu and gesture groups. Lower because these events are rare
1070
+ * by construction — a flyout opens inside a single 500ms interval, not across
1071
+ * twenty of them, so holding them to the window floor would suppress exactly
1072
+ * the findings the axes were added for. Two still rules out a one-off, and
1073
+ * every group carries its own `samples` for whoever reads it. */
1074
+ declare const MIN_EVENT_SAMPLES = 2;
1075
+ /** Append with a cap, oldest dropped first. Returns a new array — callers hold
1076
+ * this in React state, where mutation would not re-render. */
1077
+ declare function appendRecord(log: PerfLogRecord[], record: PerfLogRecord, cap?: number): PerfLogRecord[];
1078
+ /** True when the user was doing something during the interval. */
1079
+ declare function isInteracting(r: PerfLogRecord): boolean;
1080
+ /** Reduce a log to the handful of statements worth acting on. */
1081
+ declare function summariseLog(log: PerfLogRecord[]): LogSummary;
1082
+ /** Flat CSV, for opening in a spreadsheet without writing any code. */
1083
+ declare function toCsv(log: PerfLogRecord[]): string;
1084
+
1085
+ /**
1086
+ * What machine the readings came off.
1087
+ *
1088
+ * A frame rate without a machine attached is half a report. "GPU-bound
1089
+ * (compositing)" means something very different on an Intel UHD 620 driving a
1090
+ * 4K panel than on an M3 Max, and the first question anyone triaging a
1091
+ * slowdown asks is which one they are looking at — a question the reporter is
1092
+ * usually the worst-placed person to answer, and often simply cannot.
1093
+ *
1094
+ * The user agent alone does not settle it. Chromium freezes its UA string, so
1095
+ * Windows 11 reports itself as Windows 10 and an ARM machine looks like an x86
1096
+ * one; and no UA string has ever named the GPU, which is the part that matters
1097
+ * most here. So this reads the specifics directly, from four sources:
1098
+ *
1099
+ * - **Synchronous navigator and screen fields** — cores, memory, panel,
1100
+ * network, display settings. Cheap, and occasionally withheld.
1101
+ * - **A WebGL context**, purely to ask the driver its own name. Created and
1102
+ * destroyed inside one call, at report time and never during measurement,
1103
+ * because a live context is exactly the sort of thing that would show up in
1104
+ * the numbers it is meant to explain.
1105
+ * - **Client hints and the battery**, both async, both unchanging, so both
1106
+ * are resolved once when the HUD mounts rather than while a report is being
1107
+ * assembled.
1108
+ * - **Media queries**, for the settings that change how much compositing the
1109
+ * browser is being asked to do in the first place.
1110
+ *
1111
+ * Everything is best-effort and every field degrades to null. Firefox and
1112
+ * Safari withhold `deviceMemory`; Safari has no client hints; a blocklisted
1113
+ * driver refuses WebGL. A report missing one line is a far smaller loss than a
1114
+ * report that failed to send, so nothing here is allowed to throw.
1115
+ */
1116
+ /** Async platform detail from `navigator.userAgentData`. */
1117
+ interface PlatformHints {
1118
+ /** 'macOS', 'Windows', 'Linux', … */
1119
+ platform: string | null;
1120
+ /** The real OS version, which the frozen UA string no longer carries. */
1121
+ platformVersion: string | null;
1122
+ /** 'arm' | 'x86' — an Intel Mac and an Apple Silicon Mac are not the same
1123
+ * machine and do not have the same compositing story. */
1124
+ architecture: string | null;
1125
+ /** Device model, on mobile. */
1126
+ model: string | null;
1127
+ /** Browser name and full version, e.g. 'Chrome 141.0.7390.55'. */
1128
+ browser: string | null;
1129
+ }
1130
+ /** Power state. A laptop in Low Power Mode is capped at 30 fps by the OS, which
1131
+ * is the single most common cause of "it halved this afternoon". */
1132
+ interface BatteryInfo {
1133
+ charging: boolean;
1134
+ /** 0–1. */
1135
+ level: number;
1136
+ }
1137
+ /** The parts that cannot be read synchronously, resolved once on mount. */
1138
+ interface AsyncEnvironment {
1139
+ hints: PlatformHints | null;
1140
+ battery: BatteryInfo | null;
1141
+ }
1142
+ interface PerfEnvironment {
1143
+ userAgent: string;
1144
+ /** Browser name and version from client hints; null outside Chromium, where
1145
+ * `userAgent` above is the fallback. */
1146
+ browser: string | null;
1147
+ platform: string | null;
1148
+ platformVersion: string | null;
1149
+ architecture: string | null;
1150
+ model: string | null;
1151
+ /** Logical cores. The shell is single-threaded, so this mostly says how much
1152
+ * else the machine can be doing at once. */
1153
+ cpuCores: number | null;
1154
+ /** Approximate RAM in GB. Deliberately coarse — the spec has browsers round
1155
+ * to a power of two and permits clamping to 8 to blunt fingerprinting, so
1156
+ * treat it as a floor ("this much or more"), not a measurement. Chromium
1157
+ * only; Firefox and Safari publish nothing. */
1158
+ deviceMemoryGB: number | null;
1159
+ /** The driver's own name for itself, e.g. 'Apple M3 Max' or
1160
+ * 'ANGLE (Intel, Intel(R) UHD Graphics 620 …)'. */
1161
+ gpu: string | null;
1162
+ gpuVendor: string | null;
1163
+ /** False when the browser fell back to software rendering — which explains a
1164
+ * GPU-bound verdict outright. */
1165
+ webglAvailable: boolean;
1166
+ screen: {
1167
+ width: number;
1168
+ height: number;
1169
+ dpr: number;
1170
+ colorDepth: number | null;
1171
+ };
1172
+ /** The window, which is what actually gets composited — a maximised window on
1173
+ * a 4K panel and a small one are different amounts of work. */
1174
+ viewport: {
1175
+ width: number;
1176
+ height: number;
1177
+ };
1178
+ /** Heap ceiling this tab was given, MB. Chromium only. */
1179
+ heapLimitMB: number | null;
1180
+ /** Effective connection class, downlink Mb/s and round-trip ms. Separates a
1181
+ * UI that is slow from a UI that is waiting. */
1182
+ network: {
1183
+ effectiveType: string | null;
1184
+ downlinkMbps: number | null;
1185
+ rttMs: number | null;
1186
+ saveData: boolean;
1187
+ } | null;
1188
+ battery: BatteryInfo | null;
1189
+ /** Settings that change how much work the browser is being asked to do. */
1190
+ prefersReducedMotion: boolean;
1191
+ forcedColors: boolean;
1192
+ /** The shell's own "reduce transparency" — the first thing to suggest, and
1193
+ * the first thing to check has not already been tried. */
1194
+ reducedTransparency: boolean;
1195
+ /** Screens can be scaled; a report from a 4K panel at 200% is not the same as
1196
+ * one at 100%. */
1197
+ touchPoints: number | null;
1198
+ }
1199
+ /**
1200
+ * Resolve everything that needs a promise. Call once, keep the result — the
1201
+ * values cannot change for the life of the page, and the prompt some browsers
1202
+ * attach to high-entropy hints must not fire while someone is filing a report.
1203
+ *
1204
+ * Never rejects. A browser with no client hints and no battery API is the
1205
+ * normal case outside Chromium, not an error worth handling at the call site.
1206
+ */
1207
+ declare function requestAsyncEnvironment(): Promise<AsyncEnvironment>;
1208
+ /** Snapshot the machine. Synchronous — pass what `requestAsyncEnvironment`
1209
+ * resolved earlier, or null if it never arrived. */
1210
+ declare function readEnvironment(async_: AsyncEnvironment | null): PerfEnvironment;
1211
+ /**
1212
+ * One line naming the machine, for a report title or a bug-tracker
1213
+ * description — the place where a reader decides whether this report is about
1214
+ * a slow machine or a slow application. The full record is in the attachment;
1215
+ * this is the digest.
1216
+ *
1217
+ * Omits what it does not know rather than printing "unknown": a line of
1218
+ * absences reads as a broken probe, when in truth Firefox simply does not
1219
+ * publish RAM.
1220
+ */
1221
+ declare function describeMachine(env: PerfEnvironment): string;
1222
+
1223
+ /** A finished report, handed to the host to file. The JSON is pre-serialised
1224
+ * because the host's job is to attach it, not to re-derive it; `summary` and
1225
+ * `verdict` come along so a host can title or route the report without
1226
+ * parsing the attachment back apart. */
1227
+ interface PerfReport {
1228
+ /** What the user typed about what they were doing. May be empty. */
1229
+ message: string;
1230
+ /** Suggested attachment filename, stamped with the local time. */
1231
+ filename: string;
1232
+ /** The whole report — environment, summary and every raw record. */
1233
+ json: string;
1234
+ summary: LogSummary;
1235
+ /** The HUD's current headline, e.g. "GPU-bound (compositing)". */
1236
+ verdict: string;
1237
+ /** The machine the readings came off — GPU, cores, memory, OS, screen. A
1238
+ * verdict of "GPU-bound" is not actionable until you know which GPU. */
1239
+ environment: PerfEnvironment;
1240
+ }
1241
+ interface PerfStatsProps {
1242
+ /** Dismiss handler — the shell wires this to turn the pref back off, so the
1243
+ * HUD can be closed from itself rather than only from Preferences. */
1244
+ onClose?: () => void;
1245
+ /** File the report through the host's own feedback channel — the whole point
1246
+ * of the HUD, since a log that reaches nobody fixes nothing. Rejecting
1247
+ * surfaces the error and keeps the composer open with the text intact.
1248
+ * Without it the button downloads the same JSON instead, so the shell stays
1249
+ * usable standalone. */
1250
+ onSubmit?: (report: PerfReport) => void | Promise<void>;
1251
+ className?: string;
1252
+ }
1253
+ declare function PerfStats({ onClose, onSubmit, className }: PerfStatsProps): react_jsx_runtime.JSX.Element;
1254
+
884
1255
  /**
885
1256
  * INTERNAL stub — Desktop's About modal references the consumer-side
886
1257
  * changelog. The package ships no built-in changelog; consumer wires their
@@ -969,6 +1340,12 @@ interface DesktopHostConfig {
969
1340
  * calls this. Lets a consumer that files feedback natively (the shell itself
970
1341
  * dropped bug reporting in v3.0.0) surface the familiar right-click entry. */
971
1342
  onReportBug?: () => void;
1343
+ /** File a performance report from the desktop perf HUD through the same
1344
+ * feedback channel, with the session log attached. Unset, the HUD's button
1345
+ * downloads the report instead — which is a strictly worse outcome, because
1346
+ * a file on someone's Downloads folder is not a bug report. Rejecting shows
1347
+ * the error in the HUD and keeps what the user typed. */
1348
+ onSubmitPerfReport?: (report: PerfReport) => void | Promise<void>;
972
1349
  }
973
1350
  declare function DesktopHostProvider({ value, children }: {
974
1351
  value: DesktopHostConfig;
@@ -2178,168 +2555,47 @@ interface MetricBarProps {
2178
2555
  }
2179
2556
  declare function MetricBar({ label, value, max, warn, crit, severity, detail, formatValue, emptyLabel, size, ariaLabel, className, }: MetricBarProps): react_jsx_runtime.JSX.Element;
2180
2557
 
2181
- interface PerfStatsProps {
2182
- /** Dismiss handler — the shell wires this to turn the pref back off, so the
2183
- * HUD can be closed from itself rather than only from Preferences. */
2184
- onClose?: () => void;
2185
- className?: string;
2186
- }
2187
- declare function PerfStats({ onClose, className }: PerfStatsProps): react_jsx_runtime.JSX.Element;
2188
-
2189
2558
  /**
2190
- * Frame-timing maths + bottleneck attribution for the desktop perf HUD.
2559
+ * Shell interaction marks, folded into every perf-log record.
2191
2560
  *
2192
- * Split out from `PerfStats.tsx` because the interesting part is the
2193
- * *attribution*, and attribution is pure: given a frame rate and how much of
2194
- * the wall clock the main thread spent blocked, decide whether a janky UI is
2195
- * the GPU's fault or JavaScript's. That decision is what the HUD exists for —
2196
- * "the UI feels laggy" is not actionable, "you are GPU-bound" is so it lives
2197
- * where a test can pin it.
2561
+ * The HUD can see that frames were dropped and it can see that the pointer
2562
+ * moved, but "the pointer moved" covers wandering across the desktop and
2563
+ * opening a third-level flyout, and only one of those repaints a frosted
2564
+ * surface. The first version of the log conflated them, so the readings that
2565
+ * mattered most the frame the start menu opened on, the frame a window was
2566
+ * dragged across arrived indistinguishable from an idle mouse. A report full
2567
+ * of anonymous samples points at nothing.
2198
2568
  *
2199
- * The discriminator: frames are landing late, but the main thread is idle.
2200
- * If JS were the problem the main thread would be busy, so late frames plus an
2201
- * idle thread means the cost is downstream of scriptcompositing, paint,
2202
- * backdrop-filter. The shell leans hard on frosted glass (`utils/glass.ts`
2203
- * blurs at a 40px radius), which is exactly the kind of work that shows up
2204
- * here and nowhere in a JS profile.
2205
- */
2206
- /** Frame rate at or above which we call the UI smooth. Below 60 because
2207
- * rAF sampling is noisy and a display may be capped at 60Hz anyway — a
2208
- * steady 55 is not a complaint anyone would file. */
2209
- declare const SMOOTH_FPS = 50;
2210
- /** Share of wall-clock time in long tasks that makes JS the prime suspect.
2211
- * `longtask` entries are >50ms by definition, so even 20% means the thread
2212
- * is stalling several times a second — visible as jank on its own. */
2213
- declare const BLOCKED_PCT_CPU = 20;
2214
- type BottleneckKind = 'smooth' | 'gpu' | 'cpu' | 'unknown';
2215
- interface PerfReading {
2216
- /** Frames per second across the sample window. */
2217
- fps: number;
2218
- /** Mean frame interval, ms. */
2219
- frameMs: number;
2220
- /** Slowest single frame in the window, ms — where the jank actually lives. */
2221
- worstMs: number;
2222
- /** Percentage of wall-clock time the main thread spent inside long tasks
2223
- * (0–100), or null when the browser exposes no `longtask` observer.
2224
- * Null is load-bearing: without it we cannot separate GPU from CPU, and
2225
- * guessing would defeat the point of the HUD. */
2226
- blockedPct: number | null;
2227
- }
2228
- interface Verdict {
2229
- kind: BottleneckKind;
2230
- /** Headline, for the HUD's verdict row. */
2231
- label: string;
2232
- /** One line of what to do about it. */
2233
- detail: string;
2234
- }
2235
- /** Turn a window of rAF timestamps into a frame-rate reading.
2236
- * Needs two timestamps to describe one interval; anything less is not yet a
2237
- * measurement and reports zeroes rather than a fabricated 0 fps. */
2238
- declare function summariseFrames(timestamps: number[]): Pick<PerfReading, 'fps' | 'frameMs' | 'worstMs'>;
2239
- /**
2240
- * Attribute a reading to a bottleneck.
2569
+ * So the places that do the expensive things say so. Menus mark themselves when
2570
+ * they open; window drags and resizes mark themselves for as long as they run.
2571
+ * Everything here is a plain counter drained once per flush interval no
2572
+ * subscriptions, no allocation per event, nothing that would make marking an
2573
+ * event cost more than the event.
2241
2574
  *
2242
- * Order matters: smooth wins outright (nobody cares what a healthy frame rate
2243
- * is *not* bound by), and an unknown block share can only ever downgrade to
2244
- * `unknown` never to `gpu`. Blaming the GPU because the browser withheld
2245
- * long-task timing would be a confident wrong answer, which is worse than no
2246
- * answer when someone is about to go change settings on the strength of it.
2575
+ * Marks accumulate only while the HUD is mounted (`setPerfCollecting`). That is
2576
+ * not really about cost incrementing a number is free but about honesty: a
2577
+ * counter that had been climbing since page load would attribute a morning's
2578
+ * worth of menu opens to whichever 500ms interval happened to drain it first.
2247
2579
  */
2248
- declare function classify(reading: PerfReading): Verdict;
2249
-
2580
+ /** Which menu layer opened. The distinction is the point: the root menu is one
2581
+ * surface appearing, a flyout is a second one appearing *over* it, and the
2582
+ * cost of the second is what people report. */
2583
+ type MenuLayer = 'menu' | 'submenu';
2584
+ /** Record that a menu surface opened. `key` names it (a section label, a route)
2585
+ * so the summary can rank flyouts against each other. */
2586
+ declare function markMenuOpen(layer: MenuLayer, key: string): void;
2250
2587
  /**
2251
- * Session log for the desktop perf HUD and the analysis that turns it into
2252
- * a conclusion.
2588
+ * Record a window drag or resize for as long as it runs. Returns the end
2589
+ * function; calling it twice adds the span once.
2253
2590
  *
2254
- * A live frame rate tells you the UI is slow *now*. It does not tell you what
2255
- * made it slow, and the person watching it is usually the last person able to
2256
- * say. So while the HUD is on, every reading is stamped with what was
2257
- * happening around it: how many windows were open, which one was on top,
2258
- * whether the user was clicking, typing, scrolling or dragging. The log is
2259
- * exportable, so a laggy machine somewhere else can produce evidence rather
2260
- * than an adjective.
2261
- *
2262
- * The maths here is deliberately non-clever. Median rather than mean, because
2263
- * one 400ms stall would drag a mean somewhere no frame ever was. Buckets
2264
- * rather than a fitted curve, because "6 windows open halves the frame rate"
2265
- * is a sentence someone can act on and a correlation coefficient is not.
2266
- */
2267
-
2268
- /** One flush interval's worth of measurement plus its context. Keys are short
2269
- * because thousands of these get JSON-serialised into localStorage and into
2270
- * whatever the user emails afterwards. */
2271
- interface PerfLogRecord {
2272
- /** Milliseconds since logging began. */
2273
- t: number;
2274
- fps: number;
2275
- frameMs: number;
2276
- worstMs: number;
2277
- blockedPct: number | null;
2278
- heapMB: number | null;
2279
- verdict: BottleneckKind;
2280
- /** Open shell windows at the moment of the reading. */
2281
- windows: number;
2282
- /** Identity of the topmost window, when there is one — so a summary can name
2283
- * the screen that was slow rather than just the count. */
2284
- active: string | null;
2285
- clicks: number;
2286
- keys: number;
2287
- scrolls: number;
2288
- /** Milliseconds spent mid-drag during the interval. Dragging is the single
2289
- * most compositing-heavy thing a user does in a window shell, so it gets
2290
- * its own axis rather than being lumped in with clicks. */
2291
- dragMs: number;
2292
- }
2293
- interface FpsGroup {
2294
- samples: number;
2295
- medianFps: number;
2296
- }
2297
- interface LogSummary {
2298
- samples: number;
2299
- durationMs: number;
2300
- medianFps: number;
2301
- worstFrameMs: number;
2302
- /** Fraction of samples in each verdict, 0–1. */
2303
- verdictShare: Record<BottleneckKind, number>;
2304
- /** Split by whether the user was doing anything. The gap between these two
2305
- * is the headline: a desktop that is smooth at rest and janky in use has a
2306
- * rendering cost that only shows up under interaction. */
2307
- interacting: FpsGroup | null;
2308
- idle: FpsGroup | null;
2309
- /** Frame rate against how many windows were open. */
2310
- byWindowCount: (FpsGroup & {
2311
- windows: number;
2312
- })[];
2313
- /** Windows ranked worst-first, so the slowest screen names itself. */
2314
- worstWindows: (FpsGroup & {
2315
- key: string;
2316
- })[];
2317
- }
2318
- /** Records kept in memory before the oldest are dropped. At one record per
2319
- * 500ms this is about 20 minutes — long enough to catch an intermittent
2320
- * stall, small enough to serialise without thinking about it. */
2321
- declare const LOG_CAP = 2400;
2322
- /** Minimum samples before a group is reported. One unlucky reading is not a
2323
- * finding, and a summary that names a window off a single sample invites
2324
- * someone to go optimise the wrong screen. */
2325
- declare const MIN_GROUP_SAMPLES = 4;
2326
- /** Append with a cap, oldest dropped first. Returns a new array — callers hold
2327
- * this in React state, where mutation would not re-render. */
2328
- declare function appendRecord(log: PerfLogRecord[], record: PerfLogRecord, cap?: number): PerfLogRecord[];
2329
- /** True when the user was doing something during the interval. */
2330
- declare function isInteracting(r: PerfLogRecord): boolean;
2331
- /**
2332
- * Reduce a log to the handful of statements worth acting on.
2333
- *
2334
- * Only samples with a real frame rate are measured — a reading taken while the
2335
- * main thread was too blocked to deliver frames carries fps 0, and letting
2336
- * those into a median would report a frame rate the display never showed.
2337
- * They still count toward `verdictShare`, which is where that condition
2338
- * belongs.
2591
+ * Duration rather than a count because these are the gestures whose *cost is
2592
+ * their length* a drag that stutters for four seconds and a drag that lasted
2593
+ * one frame are the same event and very different reports. The span is credited
2594
+ * to the interval it ends in, which can straddle a flush boundary; that is
2595
+ * accepted rather than apportioned, because a drag long enough to straddle two
2596
+ * intervals is already the thing being investigated.
2339
2597
  */
2340
- declare function summariseLog(log: PerfLogRecord[]): LogSummary;
2341
- /** Flat CSV, for opening in a spreadsheet without writing any code. */
2342
- declare function toCsv(log: PerfLogRecord[]): string;
2598
+ declare function beginWindowGesture(kind: 'move' | 'resize'): () => void;
2343
2599
 
2344
2600
  /**
2345
2601
  * Semantic role of a column, used to auto-map CSV columns to fields and to
@@ -3116,4 +3372,4 @@ interface UndoState {
3116
3372
  future: UndoStep[];
3117
3373
  }
3118
3374
 
3119
- export { ALT, ALT_SHIFT_D, ALT_SHIFT_E, ALT_SHIFT_N, Accordion, type AccordionItem, type AccordionProps, AuthScreen, type AuthScreenProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, type AvatarSize, type AvatarStatus, BLOCKED_PCT_CPU, Banner, type BannerProps, type BannerTone, BarChart, type BarChartProps, BehaviorPanel, type BottleneckKind, type BreadcrumbItem, Breadcrumbs, type BreadcrumbsProps, type BulkColumn, type BulkColumnKind, BulkImportGrid, type BulkImportGridProps, type BulkRow, Button, type ButtonProps, type ButtonSize, type ButtonVariant, CMD_A, CMD_DOT, CMD_ENTER, CMD_K, CMD_S, CancelButton, Card, type CardProps, type CellStyle, ChangePasswordForm, type ChangePasswordFormProps, type ChangelogEntry, ChatTemplate, Checkbox, type CheckboxProps, CheckoutTemplate, type ClockCalendarConfig, ColoredBadge, type ColoredBadgeProps, type ColumnDef, ConfirmProvider, ContainerFillChart, type ContainerFillChartProps, type ContainerFillItem, CopyButton, Customization, type CustomizationOmitSection, type CustomizationProps, type CustomizationSection, DEV_BANNER_TEXT, DashboardTemplate, DataTablePage, DateRangePicker, type DateRangePickerProps, Desktop, type DesktopContextMenuItem, type DesktopHostConfig, DesktopHostProvider, DevIndicator, DocFavStar, DonutChart, type DonutChartProps, type DonutSegment, type DuplicateGroup, ENTER, EditableGrid, type EditableGridProps, EmailTemplate, EmptyState, type EmptyStateProps, type EntityFetcher, EntityList, type EntityListColumn, type EntityListContextAction, type EntityListProps, ErrorPage, type ErrorPageProps, FilterBar, type FilterOption, FormField, type FormFieldProps, FormLayoutPage, type FpsGroup, GLASS_DIVIDER, GLASS_INPUT_BG, GalleryTemplate, GlobalSearch, type GridColumn, type HealthCheckResult, HelpCenter, type HelpCenterDoc, type HelpCenterProps, INPUT_BASE, ImageAnnotator, type ImageAnnotatorHandle, type ImageAnnotatorProps, Input, type InputProps, Kanban, type KanbanColumn, type KanbanProps, LOG_CAP, Label, type LabelProps, Layout, type LayoutProps, ListFooter, ListLoadError, type ListLoadErrorProps, LoadingSpinner, type LoadingSpinnerProps, type LogSummary, MIN_GROUP_SAMPLES, MOD, Markdown, type MarkdownProps, MediaUploadField, type MediaUploadFieldProps, MediaUploadGrid, type MediaUploadGridItem, type MediaUploadGridProps, type MergeBulkOptions, type MergeBulkResult, MetricBar, type MetricBarProps, type Milestone, type MilestoneKind, MilestoneTimeline, type MilestoneTimelineProps, type MobileAppConfig, Modal, ModalActions, NativeSelect, NotificationBell, type NotificationsConfig, PageHeader, type PageHeaderProps, type PaginatedResponse, Pagination, type PaginationProps, PdfActionButton, type PdfActionButtonProps, type PerfLogRecord, type PerfReading, PerfStats, type PerfStatsProps, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, Radio, type RadioProps, ResizableTable, SHIFT, SMOOTH_FPS, type SearchConfig, type SearchProvider, type SearchResult, type SearchableOption, SearchableSelect, type SearchableSelectProps, Select, type SelectOption, type SelectProps, type SemanticGroup, ServerStatusIndicator, type ServerStatusIndicatorProps, type ServerStatusUser, type SeverityTone, type ShellAuth, ShellAuthProvider, ShellEntityFetcherProvider, type ShellNotification, type ShellPrefsAdapter, ShellPrefsProvider, ShortcutHelp, SidebarActionButton, type SidebarActionButtonProps, SidebarGroupLabel, SidebarLayout, type SidebarLayoutProps, SidebarNavItem, type SortState, SoundsPanel, Sparkline, type SparklineProps, StartMenu, StatCard, type StatCardProps, StatusBadge, StatusBadgeProvider, type StickyEntityRef, type StickyResolver, SystemPreferences, type SystemPreferencesProps, type SystemPreferencesSection, type TabItem, Tabs, type TabsProps, Textarea, type TextareaProps, type TodoProvider, type TodoTask, Tooltip, type TooltipProps, TopNav, type TopNavItem, type TopNavProps, UndoControls, type UndoControlsApi, type UndoControlsProps, UndoProvider, type UndoProviderProps, type UndoSnapshot, type UndoState, type UndoStep, type UndoableOptions, VERSION, type Verdict, WidgetManager, WindowCrashedFallback, WindowErrorBoundary, WindowManagerProvider, WindowRegistry, WindowTitle, appendRecord as appendPerfRecord, applyDevTitle, classify as classifyPerf, commitExposeHighlight, confirm, confirmDestructive, createWindowRegistry, exitExposeMode, findDuplicateKeys, formatDate, getActiveWindowRoute, getExposeHighlight, getWindowPosition, glassStyle, inputClasses, isDevEnv, isInteracting, isMac, isSeverityTone, mediaFileName, mergeBulkItems, toCsv as perfLogToCsv, prompt, registerModalEscapeInterceptor, setExposeHighlight, setShellApiClient, setShellAuthBridge, setShellNavIcons, setShellTodoProvider, setWindowDefaultPosition, setWindowPosition, severityOf, subscribeExposeHighlight, summariseFrames, summariseLog as summarisePerfLog, toISODate, toast, toggleExposeMode, useClickOutside, useColumnConfig, useDesktopHost, useEditHotkey, useFilters, useInfiniteScroll, useIsActiveWindow, useLocalStoragePrefs, useModalActive, useNewHotkey, useShellAuth, useShellEntityFetcher, useShellPrefs, useSort, useTableNav, useUndo, useUndoable, useUndoableState, useWidgetSettings, useWindowManager, useWindowMenuItem, useWindowTitle };
3375
+ export { ALT, ALT_SHIFT_D, ALT_SHIFT_E, ALT_SHIFT_N, Accordion, type AccordionItem, type AccordionProps, type ActivityKind, type AsyncEnvironment, AuthScreen, type AuthScreenProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, type AvatarSize, type AvatarStatus, BLOCKED_PCT_CPU, Banner, type BannerProps, type BannerTone, BarChart, type BarChartProps, type BatteryInfo, BehaviorPanel, type BottleneckKind, type BreadcrumbItem, Breadcrumbs, type BreadcrumbsProps, type BulkColumn, type BulkColumnKind, BulkImportGrid, type BulkImportGridProps, type BulkRow, Button, type ButtonProps, type ButtonSize, type ButtonVariant, CMD_A, CMD_DOT, CMD_ENTER, CMD_K, CMD_S, CancelButton, Card, type CardProps, type CellStyle, ChangePasswordForm, type ChangePasswordFormProps, type ChangelogEntry, ChatTemplate, Checkbox, type CheckboxProps, CheckoutTemplate, type ClockCalendarConfig, ColoredBadge, type ColoredBadgeProps, type ColumnDef, ConfirmProvider, ContainerFillChart, type ContainerFillChartProps, type ContainerFillItem, CopyButton, Customization, type CustomizationOmitSection, type CustomizationProps, type CustomizationSection, DEV_BANNER_TEXT, DashboardTemplate, DataTablePage, DateRangePicker, type DateRangePickerProps, Desktop, type DesktopContextMenuItem, type DesktopHostConfig, DesktopHostProvider, DevIndicator, DocFavStar, DonutChart, type DonutChartProps, type DonutSegment, type DuplicateGroup, ENTER, EditableGrid, type EditableGridProps, EmailTemplate, EmptyState, type EmptyStateProps, type EntityFetcher, EntityList, type EntityListColumn, type EntityListContextAction, type EntityListProps, ErrorPage, type ErrorPageProps, FilterBar, type FilterOption, FormField, type FormFieldProps, FormLayoutPage, type FpsGroup, GLASS_DIVIDER, GLASS_INPUT_BG, GalleryTemplate, GlobalSearch, type GridColumn, type HealthCheckResult, HelpCenter, type HelpCenterDoc, type HelpCenterProps, INPUT_BASE, ImageAnnotator, type ImageAnnotatorHandle, type ImageAnnotatorProps, Input, type InputProps, Kanban, type KanbanColumn, type KanbanProps, LOG_CAP, Label, type LabelProps, Layout, type LayoutProps, ListFooter, ListLoadError, type ListLoadErrorProps, LoadingSpinner, type LoadingSpinnerProps, type LogSummary, MIN_EVENT_SAMPLES, MIN_GROUP_SAMPLES, MOD, Markdown, type MarkdownProps, MediaUploadField, type MediaUploadFieldProps, MediaUploadGrid, type MediaUploadGridItem, type MediaUploadGridProps, type MenuLayer, type MergeBulkOptions, type MergeBulkResult, MetricBar, type MetricBarProps, type Milestone, type MilestoneKind, MilestoneTimeline, type MilestoneTimelineProps, type MobileAppConfig, Modal, ModalActions, NativeSelect, NotificationBell, type NotificationsConfig, PageHeader, type PageHeaderProps, type PaginatedResponse, Pagination, type PaginationProps, PdfActionButton, type PdfActionButtonProps, type PerfEnvironment, type PerfLogRecord, type PerfReading, type PerfReport, PerfStats, type PerfStatsProps, type PlatformHints, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, Radio, type RadioProps, ResizableTable, SHIFT, SMOOTH_FPS, type SearchConfig, type SearchProvider, type SearchResult, type SearchableOption, SearchableSelect, type SearchableSelectProps, Select, type SelectOption, type SelectProps, type SemanticGroup, ServerStatusIndicator, type ServerStatusIndicatorProps, type ServerStatusUser, type SeverityTone, type ShellAuth, ShellAuthProvider, ShellEntityFetcherProvider, type ShellNotification, type ShellPrefsAdapter, ShellPrefsProvider, ShortcutHelp, SidebarActionButton, type SidebarActionButtonProps, SidebarGroupLabel, SidebarLayout, type SidebarLayoutProps, SidebarNavItem, type SortState, SoundsPanel, Sparkline, type SparklineProps, StartMenu, StatCard, type StatCardProps, StatusBadge, StatusBadgeProvider, type StickyEntityRef, type StickyResolver, SystemPreferences, type SystemPreferencesProps, type SystemPreferencesSection, type TabItem, Tabs, type TabsProps, Textarea, type TextareaProps, type TodoProvider, type TodoTask, Tooltip, type TooltipProps, TopNav, type TopNavItem, type TopNavProps, UndoControls, type UndoControlsApi, type UndoControlsProps, UndoProvider, type UndoProviderProps, type UndoSnapshot, type UndoState, type UndoStep, type UndoableOptions, VERSION, type Verdict, WidgetManager, WindowCrashedFallback, WindowErrorBoundary, WindowManagerProvider, WindowRegistry, WindowTitle, appendRecord as appendPerfRecord, applyDevTitle, beginWindowGesture, classifyActivity, classify as classifyPerf, commitExposeHighlight, confirm, confirmDestructive, createWindowRegistry, describeMachine, exitExposeMode, findDuplicateKeys, formatDate, getActiveWindowRoute, getExposeHighlight, getWindowPosition, glassStyle, inputClasses, isDevEnv, isInteracting, isMac, isSeverityTone, markMenuOpen, mediaFileName, mergeBulkItems, toCsv as perfLogToCsv, prompt, readEnvironment as readPerfEnvironment, registerModalEscapeInterceptor, requestAsyncEnvironment, setExposeHighlight, setShellApiClient, setShellAuthBridge, setShellNavIcons, setShellTodoProvider, setWindowDefaultPosition, setWindowPosition, severityOf, subscribeExposeHighlight, summariseFrames, summariseLog as summarisePerfLog, toISODate, toast, toggleExposeMode, useClickOutside, useColumnConfig, useDesktopHost, useEditHotkey, useFilters, useInfiniteScroll, useIsActiveWindow, useLocalStoragePrefs, useModalActive, useNewHotkey, useShellAuth, useShellEntityFetcher, useShellPrefs, useSort, useTableNav, useUndo, useUndoable, useUndoableState, useWidgetSettings, useWindowManager, useWindowMenuItem, useWindowTitle };