react-os-shell 4.1.5 → 4.2.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.
- package/dist/{Browser-KHY7MXD4.js → Browser-XWYM2PEL.js} +4 -4
- package/dist/{Browser-KHY7MXD4.js.map → Browser-XWYM2PEL.js.map} +1 -1
- package/dist/{Documents-KI6IZA6A.js → Documents-XA3VM4VJ.js} +4 -4
- package/dist/{Documents-KI6IZA6A.js.map → Documents-XA3VM4VJ.js.map} +1 -1
- package/dist/{Files-XDN7W7S7.js → Files-42ON7ANG.js} +4 -4
- package/dist/{Files-XDN7W7S7.js.map → Files-42ON7ANG.js.map} +1 -1
- package/dist/{Notepad-STWMTAB6.js → Notepad-QQINWDCQ.js} +4 -4
- package/dist/{Notepad-STWMTAB6.js.map → Notepad-QQINWDCQ.js.map} +1 -1
- package/dist/{Preview-73KK2YVP.js → Preview-NKLLQUKW.js} +4 -4
- package/dist/{Preview-73KK2YVP.js.map → Preview-NKLLQUKW.js.map} +1 -1
- package/dist/{Spreadsheet-YLVGNQ43.js → Spreadsheet-BDBABITI.js} +4 -4
- package/dist/{Spreadsheet-YLVGNQ43.js.map → Spreadsheet-BDBABITI.js.map} +1 -1
- package/dist/apps/index.js +8 -8
- package/dist/{chunk-DF4MTEZD.js → chunk-3ORJ4J4J.js} +3 -3
- package/dist/{chunk-DF4MTEZD.js.map → chunk-3ORJ4J4J.js.map} +1 -1
- package/dist/chunk-ITOOPTGP.js +7 -0
- package/dist/{chunk-IMKD43E3.js.map → chunk-ITOOPTGP.js.map} +1 -1
- package/dist/index.d.ts +164 -1
- package/dist/index.js +473 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-IMKD43E3.js +0 -7
package/dist/index.js
CHANGED
|
@@ -15,8 +15,8 @@ export { ShellAuthProvider, setShellAuthBridge, useShellAuth } from './chunk-ADJ
|
|
|
15
15
|
import { EditableGrid } from './chunk-GP4Y3VCB.js';
|
|
16
16
|
export { EditableGrid } from './chunk-GP4Y3VCB.js';
|
|
17
17
|
import './chunk-4SQU5YV6.js';
|
|
18
|
-
import { APP_VERSION, VERSION } from './chunk-
|
|
19
|
-
export { VERSION } from './chunk-
|
|
18
|
+
import { APP_VERSION, VERSION } from './chunk-ITOOPTGP.js';
|
|
19
|
+
export { VERSION } from './chunk-ITOOPTGP.js';
|
|
20
20
|
import { registerModalEscapeInterceptor, useIsMobile, useWindowManager, PopupMenu, PopupMenuLabel, PopupMenuDivider, PopupMenuItem, WINDOW_REGISTRY, isPageEntry, Modal, useShellPrefs, SIDEBAR_STRIP_W, forgetMaximizedWindowBoxes, ModalActions, useModalActive, client_default, LoadingSpinner, isShellApiClientConfigured, CancelButton, setWindowPosition } from './chunk-URHWXWZE.js';
|
|
21
21
|
export { CancelButton, ConfirmProvider, CopyButton, DocFavStar, Modal, ModalActions, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, ShellPrefsProvider, WindowCrashedFallback, WindowErrorBoundary, WindowManagerProvider, WindowTitle, commitExposeHighlight, confirm, confirmDestructive, exitExposeMode, getActiveWindowRoute, getExposeHighlight, getWindowPosition, isEntityEntry, isPageEntry, prompt, registerModalEscapeInterceptor, setExposeHighlight, setShellApiClient, setShellWindowRegistry, setWindowDefaultPosition, setWindowPosition, subscribeExposeHighlight, toggleExposeMode, useLocalStoragePrefs, useModalActive, useShellPrefs, useWidgetSettings, useWindowManager, useWindowMenuItem, useWindowTitle } from './chunk-URHWXWZE.js';
|
|
22
22
|
import { glassStyle, startMenuCategories, navSections, isSection, GLASS_INPUT_BG, navIcons, isReachable, sectionIcons, visibleChildren } from './chunk-6Q4KYGMH.js';
|
|
@@ -506,22 +506,22 @@ function HelpCenter({
|
|
|
506
506
|
}
|
|
507
507
|
)
|
|
508
508
|
] }) }),
|
|
509
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto py-1", children: loading ? /* @__PURE__ */ jsx("p", { className: "px-3 py-6 text-center text-xs text-gray-400", children: "Loading\u2026" }) : visibleGroups.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-3 py-6 text-center text-xs text-gray-500", children: q ? "No articles match your search." : emptyMessage }) : visibleGroups.map((
|
|
510
|
-
const open = isGroupOpen(
|
|
509
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto py-1", children: loading ? /* @__PURE__ */ jsx("p", { className: "px-3 py-6 text-center text-xs text-gray-400", children: "Loading\u2026" }) : visibleGroups.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-3 py-6 text-center text-xs text-gray-500", children: q ? "No articles match your search." : emptyMessage }) : visibleGroups.map((group2) => {
|
|
510
|
+
const open = isGroupOpen(group2.key);
|
|
511
511
|
return /* @__PURE__ */ jsxs("div", { className: "py-0.5", children: [
|
|
512
512
|
/* @__PURE__ */ jsxs(
|
|
513
513
|
"button",
|
|
514
514
|
{
|
|
515
515
|
type: "button",
|
|
516
|
-
onClick: () => toggleGroup(
|
|
516
|
+
onClick: () => toggleGroup(group2.key),
|
|
517
517
|
className: "w-full flex items-center gap-1.5 px-3 py-1.5 text-left text-[10px] font-semibold uppercase tracking-wide text-gray-400 hover:text-gray-600",
|
|
518
518
|
children: [
|
|
519
519
|
/* @__PURE__ */ jsx(Chevron, { open }),
|
|
520
|
-
/* @__PURE__ */ jsx("span", { className: "truncate", children:
|
|
520
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: group2.label })
|
|
521
521
|
]
|
|
522
522
|
}
|
|
523
523
|
),
|
|
524
|
-
open &&
|
|
524
|
+
open && group2.docs.map((doc) => {
|
|
525
525
|
const active = selected?.slug === doc.slug;
|
|
526
526
|
return /* @__PURE__ */ jsxs(
|
|
527
527
|
"button",
|
|
@@ -537,7 +537,7 @@ function HelpCenter({
|
|
|
537
537
|
doc.slug
|
|
538
538
|
);
|
|
539
539
|
})
|
|
540
|
-
] },
|
|
540
|
+
] }, group2.key);
|
|
541
541
|
}) })
|
|
542
542
|
] }),
|
|
543
543
|
/* @__PURE__ */ jsx("main", { className: "flex-1 min-w-0 bg-white rounded-lg shadow overflow-hidden flex flex-col", children: selected ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -1183,8 +1183,8 @@ function StatusBadgeProvider({
|
|
|
1183
1183
|
}
|
|
1184
1184
|
function StatusBadge({ status }) {
|
|
1185
1185
|
const groups = useContext(StatusGroupsContext);
|
|
1186
|
-
const
|
|
1187
|
-
const color = GROUP_COLORS[
|
|
1186
|
+
const group2 = groups[status] ?? "neutral";
|
|
1187
|
+
const color = GROUP_COLORS[group2];
|
|
1188
1188
|
const label = status.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
1189
1189
|
return /* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${color}`, children: label });
|
|
1190
1190
|
}
|
|
@@ -1370,6 +1370,437 @@ function WidgetManager({ open, onClose }) {
|
|
|
1370
1370
|
] }) });
|
|
1371
1371
|
}
|
|
1372
1372
|
|
|
1373
|
+
// src/shell/perfVerdict.ts
|
|
1374
|
+
var SMOOTH_FPS = 50;
|
|
1375
|
+
var BLOCKED_PCT_CPU = 20;
|
|
1376
|
+
var CPU_BOUND = {
|
|
1377
|
+
kind: "cpu",
|
|
1378
|
+
label: "CPU-bound (JavaScript)",
|
|
1379
|
+
detail: "The main thread is blocked. Rendering settings will not help \u2014 this is script or data work."
|
|
1380
|
+
};
|
|
1381
|
+
function summariseFrames(timestamps) {
|
|
1382
|
+
if (timestamps.length < 2) return { fps: 0, frameMs: 0, worstMs: 0 };
|
|
1383
|
+
let worstMs = 0;
|
|
1384
|
+
for (let i = 1; i < timestamps.length; i++) {
|
|
1385
|
+
const delta = timestamps[i] - timestamps[i - 1];
|
|
1386
|
+
if (delta > worstMs) worstMs = delta;
|
|
1387
|
+
}
|
|
1388
|
+
const spanMs = timestamps[timestamps.length - 1] - timestamps[0];
|
|
1389
|
+
if (spanMs <= 0) return { fps: 0, frameMs: 0, worstMs: 0 };
|
|
1390
|
+
const intervals = timestamps.length - 1;
|
|
1391
|
+
return {
|
|
1392
|
+
fps: intervals / spanMs * 1e3,
|
|
1393
|
+
frameMs: spanMs / intervals,
|
|
1394
|
+
worstMs
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
function classify(reading) {
|
|
1398
|
+
const { fps, blockedPct } = reading;
|
|
1399
|
+
const threadBlocked = blockedPct !== null && blockedPct >= BLOCKED_PCT_CPU;
|
|
1400
|
+
if (!Number.isFinite(fps) || fps <= 0) {
|
|
1401
|
+
if (threadBlocked) return CPU_BOUND;
|
|
1402
|
+
return { kind: "unknown", label: "Measuring\u2026", detail: "Collecting frame timings." };
|
|
1403
|
+
}
|
|
1404
|
+
if (fps >= SMOOTH_FPS) {
|
|
1405
|
+
return {
|
|
1406
|
+
kind: "smooth",
|
|
1407
|
+
label: "Smooth",
|
|
1408
|
+
detail: "Frames are landing on time. No rendering bottleneck here."
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
if (blockedPct === null) {
|
|
1412
|
+
return {
|
|
1413
|
+
kind: "unknown",
|
|
1414
|
+
label: "Janky \u2014 cause unknown",
|
|
1415
|
+
detail: "This browser reports no long-task timing, so GPU and JS cannot be told apart. Try Chrome or Edge."
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
if (threadBlocked) return CPU_BOUND;
|
|
1419
|
+
return {
|
|
1420
|
+
kind: "gpu",
|
|
1421
|
+
label: "GPU-bound (compositing)",
|
|
1422
|
+
detail: "Frames are late while the main thread sits idle. Try Preferences \u2192 Customization \u2192 Reduce transparency."
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
// src/shell/perfLog.ts
|
|
1427
|
+
var LOG_CAP = 2400;
|
|
1428
|
+
var MIN_GROUP_SAMPLES = 4;
|
|
1429
|
+
function appendRecord(log, record, cap = LOG_CAP) {
|
|
1430
|
+
const next = log.length >= cap ? log.slice(log.length - cap + 1) : log.slice();
|
|
1431
|
+
next.push(record);
|
|
1432
|
+
return next;
|
|
1433
|
+
}
|
|
1434
|
+
function isInteracting(r) {
|
|
1435
|
+
return r.clicks > 0 || r.keys > 0 || r.scrolls > 0 || r.dragMs > 0;
|
|
1436
|
+
}
|
|
1437
|
+
function median(values) {
|
|
1438
|
+
if (!values.length) return 0;
|
|
1439
|
+
const sorted = values.slice().sort((a, b) => a - b);
|
|
1440
|
+
const mid = Math.floor(sorted.length / 2);
|
|
1441
|
+
return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
|
|
1442
|
+
}
|
|
1443
|
+
function group(records) {
|
|
1444
|
+
return { samples: records.length, medianFps: median(records.map((r) => r.fps)) };
|
|
1445
|
+
}
|
|
1446
|
+
function summariseLog(log) {
|
|
1447
|
+
const empty = {
|
|
1448
|
+
samples: 0,
|
|
1449
|
+
durationMs: 0,
|
|
1450
|
+
medianFps: 0,
|
|
1451
|
+
worstFrameMs: 0,
|
|
1452
|
+
verdictShare: { smooth: 0, gpu: 0, cpu: 0, unknown: 0 },
|
|
1453
|
+
interacting: null,
|
|
1454
|
+
idle: null,
|
|
1455
|
+
byWindowCount: [],
|
|
1456
|
+
worstWindows: []
|
|
1457
|
+
};
|
|
1458
|
+
if (!log.length) return empty;
|
|
1459
|
+
const measured = log.filter((r) => r.fps > 0);
|
|
1460
|
+
const verdictShare = { ...empty.verdictShare };
|
|
1461
|
+
for (const r of log) verdictShare[r.verdict] += 1 / log.length;
|
|
1462
|
+
const interacting = measured.filter(isInteracting);
|
|
1463
|
+
const idle = measured.filter((r) => !isInteracting(r));
|
|
1464
|
+
const byWindows = /* @__PURE__ */ new Map();
|
|
1465
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
1466
|
+
for (const r of measured) {
|
|
1467
|
+
const bucket = byWindows.get(r.windows);
|
|
1468
|
+
if (bucket) bucket.push(r);
|
|
1469
|
+
else byWindows.set(r.windows, [r]);
|
|
1470
|
+
if (r.active) {
|
|
1471
|
+
const keyed = byKey.get(r.active);
|
|
1472
|
+
if (keyed) keyed.push(r);
|
|
1473
|
+
else byKey.set(r.active, [r]);
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
return {
|
|
1477
|
+
samples: log.length,
|
|
1478
|
+
durationMs: log[log.length - 1].t - log[0].t,
|
|
1479
|
+
medianFps: median(measured.map((r) => r.fps)),
|
|
1480
|
+
worstFrameMs: log.reduce((worst, r) => r.worstMs > worst ? r.worstMs : worst, 0),
|
|
1481
|
+
verdictShare,
|
|
1482
|
+
interacting: interacting.length >= MIN_GROUP_SAMPLES ? group(interacting) : null,
|
|
1483
|
+
idle: idle.length >= MIN_GROUP_SAMPLES ? group(idle) : null,
|
|
1484
|
+
byWindowCount: [...byWindows.entries()].filter(([, rs]) => rs.length >= MIN_GROUP_SAMPLES).map(([windows, rs]) => ({ windows, ...group(rs) })).sort((a, b) => a.windows - b.windows),
|
|
1485
|
+
worstWindows: [...byKey.entries()].filter(([, rs]) => rs.length >= MIN_GROUP_SAMPLES).map(([key, rs]) => ({ key, ...group(rs) })).sort((a, b) => a.medianFps - b.medianFps)
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
var CSV_COLUMNS = [
|
|
1489
|
+
"t",
|
|
1490
|
+
"fps",
|
|
1491
|
+
"frameMs",
|
|
1492
|
+
"worstMs",
|
|
1493
|
+
"blockedPct",
|
|
1494
|
+
"heapMB",
|
|
1495
|
+
"verdict",
|
|
1496
|
+
"windows",
|
|
1497
|
+
"active",
|
|
1498
|
+
"clicks",
|
|
1499
|
+
"keys",
|
|
1500
|
+
"scrolls",
|
|
1501
|
+
"dragMs"
|
|
1502
|
+
];
|
|
1503
|
+
function toCsv(log) {
|
|
1504
|
+
const cell = (value) => {
|
|
1505
|
+
if (value === null || value === void 0) return "";
|
|
1506
|
+
const text = typeof value === "number" ? String(Math.round(value * 10) / 10) : String(value);
|
|
1507
|
+
return /[",\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
|
|
1508
|
+
};
|
|
1509
|
+
return [
|
|
1510
|
+
CSV_COLUMNS.join(","),
|
|
1511
|
+
...log.map((r) => CSV_COLUMNS.map((c) => cell(r[c])).join(","))
|
|
1512
|
+
].join("\n");
|
|
1513
|
+
}
|
|
1514
|
+
var FLUSH_MS = 500;
|
|
1515
|
+
var WINDOW_MS = 1e3;
|
|
1516
|
+
var HISTORY = 40;
|
|
1517
|
+
var PERSIST_EVERY_MS = 1e4;
|
|
1518
|
+
var LOG_STORAGE_KEY = "react-os-shell:perf-log";
|
|
1519
|
+
var VERDICT_STYLES = {
|
|
1520
|
+
smooth: { dot: "bg-emerald-400", text: "text-emerald-300" },
|
|
1521
|
+
gpu: { dot: "bg-amber-400", text: "text-amber-300" },
|
|
1522
|
+
cpu: { dot: "bg-sky-400", text: "text-sky-300" },
|
|
1523
|
+
unknown: { dot: "bg-gray-400", text: "text-gray-300" }
|
|
1524
|
+
};
|
|
1525
|
+
function readWindows() {
|
|
1526
|
+
if (typeof document === "undefined") return { windows: 0, active: null };
|
|
1527
|
+
const panels = document.querySelectorAll("[data-modal-panel]");
|
|
1528
|
+
let active = null;
|
|
1529
|
+
let topZ = -Infinity;
|
|
1530
|
+
panels.forEach((panel) => {
|
|
1531
|
+
const z = parseInt(window.getComputedStyle(panel).zIndex, 10);
|
|
1532
|
+
const rank = Number.isFinite(z) ? z : 0;
|
|
1533
|
+
if (rank >= topZ) {
|
|
1534
|
+
topZ = rank;
|
|
1535
|
+
active = panel.dataset.windowKey ?? panel.dataset.modalId ?? null;
|
|
1536
|
+
}
|
|
1537
|
+
});
|
|
1538
|
+
return { windows: panels.length, active };
|
|
1539
|
+
}
|
|
1540
|
+
function PerfStats({ onClose, className = "" }) {
|
|
1541
|
+
const framesRef = useRef([]);
|
|
1542
|
+
const blockedMsRef = useRef(0);
|
|
1543
|
+
const activityRef = useRef({ clicks: 0, keys: 0, scrolls: 0, dragMs: 0 });
|
|
1544
|
+
const dragRef = useRef({ down: false, since: 0 });
|
|
1545
|
+
const logRef = useRef([]);
|
|
1546
|
+
const startedAtRef = useRef(0);
|
|
1547
|
+
const persistedAtRef = useRef(0);
|
|
1548
|
+
const [longTaskSupported, setLongTaskSupported] = useState(false);
|
|
1549
|
+
const [reading, setReading] = useState({ fps: 0, frameMs: 0, worstMs: 0, blockedPct: null });
|
|
1550
|
+
const [history, setHistory] = useState([]);
|
|
1551
|
+
const [heap, setHeap] = useState(null);
|
|
1552
|
+
const [logSize, setLogSize] = useState(0);
|
|
1553
|
+
useEffect(() => {
|
|
1554
|
+
let raf = 0;
|
|
1555
|
+
const tick = (now) => {
|
|
1556
|
+
const frames = framesRef.current;
|
|
1557
|
+
frames.push(now);
|
|
1558
|
+
const cutoff = now - WINDOW_MS;
|
|
1559
|
+
while (frames.length && frames[0] < cutoff) frames.shift();
|
|
1560
|
+
raf = requestAnimationFrame(tick);
|
|
1561
|
+
};
|
|
1562
|
+
raf = requestAnimationFrame(tick);
|
|
1563
|
+
return () => cancelAnimationFrame(raf);
|
|
1564
|
+
}, []);
|
|
1565
|
+
useEffect(() => {
|
|
1566
|
+
if (typeof PerformanceObserver === "undefined") return;
|
|
1567
|
+
let observer;
|
|
1568
|
+
try {
|
|
1569
|
+
observer = new PerformanceObserver((list) => {
|
|
1570
|
+
for (const entry of list.getEntries()) blockedMsRef.current += entry.duration;
|
|
1571
|
+
});
|
|
1572
|
+
observer.observe({ entryTypes: ["longtask"] });
|
|
1573
|
+
setLongTaskSupported(true);
|
|
1574
|
+
} catch {
|
|
1575
|
+
return;
|
|
1576
|
+
}
|
|
1577
|
+
return () => observer?.disconnect();
|
|
1578
|
+
}, []);
|
|
1579
|
+
useEffect(() => {
|
|
1580
|
+
const onPointerDown = () => {
|
|
1581
|
+
activityRef.current.clicks++;
|
|
1582
|
+
dragRef.current = { down: true, since: performance.now() };
|
|
1583
|
+
};
|
|
1584
|
+
const onPointerMove = () => {
|
|
1585
|
+
if (!dragRef.current.down) return;
|
|
1586
|
+
const now = performance.now();
|
|
1587
|
+
activityRef.current.dragMs += now - dragRef.current.since;
|
|
1588
|
+
dragRef.current.since = now;
|
|
1589
|
+
};
|
|
1590
|
+
const onPointerUp = () => {
|
|
1591
|
+
dragRef.current.down = false;
|
|
1592
|
+
};
|
|
1593
|
+
const onKeyDown = () => {
|
|
1594
|
+
activityRef.current.keys++;
|
|
1595
|
+
};
|
|
1596
|
+
const onWheel = () => {
|
|
1597
|
+
activityRef.current.scrolls++;
|
|
1598
|
+
};
|
|
1599
|
+
const opts = { capture: true, passive: true };
|
|
1600
|
+
window.addEventListener("pointerdown", onPointerDown, opts);
|
|
1601
|
+
window.addEventListener("pointermove", onPointerMove, opts);
|
|
1602
|
+
window.addEventListener("pointerup", onPointerUp, opts);
|
|
1603
|
+
window.addEventListener("pointercancel", onPointerUp, opts);
|
|
1604
|
+
window.addEventListener("keydown", onKeyDown, opts);
|
|
1605
|
+
window.addEventListener("wheel", onWheel, opts);
|
|
1606
|
+
return () => {
|
|
1607
|
+
window.removeEventListener("pointerdown", onPointerDown, opts);
|
|
1608
|
+
window.removeEventListener("pointermove", onPointerMove, opts);
|
|
1609
|
+
window.removeEventListener("pointerup", onPointerUp, opts);
|
|
1610
|
+
window.removeEventListener("pointercancel", onPointerUp, opts);
|
|
1611
|
+
window.removeEventListener("keydown", onKeyDown, opts);
|
|
1612
|
+
window.removeEventListener("wheel", onWheel, opts);
|
|
1613
|
+
};
|
|
1614
|
+
}, []);
|
|
1615
|
+
const persist = useCallback(() => {
|
|
1616
|
+
try {
|
|
1617
|
+
localStorage.setItem(LOG_STORAGE_KEY, JSON.stringify(logRef.current));
|
|
1618
|
+
} catch {
|
|
1619
|
+
}
|
|
1620
|
+
}, []);
|
|
1621
|
+
useEffect(() => {
|
|
1622
|
+
const onHide = () => persist();
|
|
1623
|
+
window.addEventListener("pagehide", onHide);
|
|
1624
|
+
document.addEventListener("visibilitychange", onHide);
|
|
1625
|
+
return () => {
|
|
1626
|
+
window.removeEventListener("pagehide", onHide);
|
|
1627
|
+
document.removeEventListener("visibilitychange", onHide);
|
|
1628
|
+
persist();
|
|
1629
|
+
};
|
|
1630
|
+
}, [persist]);
|
|
1631
|
+
useEffect(() => {
|
|
1632
|
+
const id = setInterval(() => {
|
|
1633
|
+
const now = performance.now();
|
|
1634
|
+
if (!startedAtRef.current) startedAtRef.current = now;
|
|
1635
|
+
const frames = summariseFrames(framesRef.current);
|
|
1636
|
+
const blockedPct = longTaskSupported ? Math.min(100, blockedMsRef.current / FLUSH_MS * 100) : null;
|
|
1637
|
+
blockedMsRef.current = 0;
|
|
1638
|
+
const memory = performance.memory;
|
|
1639
|
+
const heapMB = memory ? memory.usedJSHeapSize / 1048576 : null;
|
|
1640
|
+
const nextReading = { ...frames, blockedPct };
|
|
1641
|
+
const activity = activityRef.current;
|
|
1642
|
+
const { windows, active } = readWindows();
|
|
1643
|
+
logRef.current = appendRecord(logRef.current, {
|
|
1644
|
+
t: Math.round(now - startedAtRef.current),
|
|
1645
|
+
...frames,
|
|
1646
|
+
blockedPct,
|
|
1647
|
+
heapMB,
|
|
1648
|
+
verdict: classify(nextReading).kind,
|
|
1649
|
+
windows,
|
|
1650
|
+
active,
|
|
1651
|
+
clicks: activity.clicks,
|
|
1652
|
+
keys: activity.keys,
|
|
1653
|
+
scrolls: activity.scrolls,
|
|
1654
|
+
dragMs: Math.round(activity.dragMs)
|
|
1655
|
+
});
|
|
1656
|
+
activity.clicks = 0;
|
|
1657
|
+
activity.keys = 0;
|
|
1658
|
+
activity.scrolls = 0;
|
|
1659
|
+
activity.dragMs = 0;
|
|
1660
|
+
if (!persistedAtRef.current || now - persistedAtRef.current >= PERSIST_EVERY_MS) {
|
|
1661
|
+
persistedAtRef.current = now;
|
|
1662
|
+
persist();
|
|
1663
|
+
}
|
|
1664
|
+
setReading(nextReading);
|
|
1665
|
+
setHistory((prev) => [...prev, frames.fps].slice(-HISTORY));
|
|
1666
|
+
setHeap(memory ? { usedMB: memory.usedJSHeapSize / 1048576, limitMB: memory.jsHeapSizeLimit / 1048576 } : null);
|
|
1667
|
+
setLogSize(logRef.current.length);
|
|
1668
|
+
}, FLUSH_MS);
|
|
1669
|
+
return () => clearInterval(id);
|
|
1670
|
+
}, [longTaskSupported, persist]);
|
|
1671
|
+
const download = useCallback((body, type, extension) => {
|
|
1672
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
1673
|
+
const url = URL.createObjectURL(new Blob([body], { type }));
|
|
1674
|
+
const link = document.createElement("a");
|
|
1675
|
+
link.href = url;
|
|
1676
|
+
link.download = `perf-log-${stamp}.${extension}`;
|
|
1677
|
+
link.click();
|
|
1678
|
+
URL.revokeObjectURL(url);
|
|
1679
|
+
}, []);
|
|
1680
|
+
const exportJson = useCallback(() => {
|
|
1681
|
+
download(
|
|
1682
|
+
JSON.stringify(
|
|
1683
|
+
{
|
|
1684
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1685
|
+
userAgent: navigator.userAgent,
|
|
1686
|
+
screen: { width: window.screen.width, height: window.screen.height, dpr: window.devicePixelRatio },
|
|
1687
|
+
reducedTransparency: document.documentElement.classList.contains("rosh-reduce-transparency"),
|
|
1688
|
+
summary: summariseLog(logRef.current),
|
|
1689
|
+
records: logRef.current
|
|
1690
|
+
},
|
|
1691
|
+
null,
|
|
1692
|
+
2
|
|
1693
|
+
),
|
|
1694
|
+
"application/json",
|
|
1695
|
+
"json"
|
|
1696
|
+
);
|
|
1697
|
+
}, [download]);
|
|
1698
|
+
const exportCsv = useCallback(() => download(toCsv(logRef.current), "text/csv", "csv"), [download]);
|
|
1699
|
+
const verdict = classify(reading);
|
|
1700
|
+
const styles = VERDICT_STYLES[verdict.kind];
|
|
1701
|
+
const peak = Math.max(60, ...history);
|
|
1702
|
+
const loggedSeconds = Math.round(logSize * FLUSH_MS / 1e3);
|
|
1703
|
+
return /* @__PURE__ */ jsxs(
|
|
1704
|
+
"div",
|
|
1705
|
+
{
|
|
1706
|
+
className: `w-56 rounded-lg border border-white/15 bg-[#11111b]/95 px-3 py-2 font-mono text-[10px] text-white/80 shadow-lg select-none ${className}`,
|
|
1707
|
+
role: "status",
|
|
1708
|
+
"aria-live": "off",
|
|
1709
|
+
"aria-label": "Performance statistics",
|
|
1710
|
+
children: [
|
|
1711
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-1.5 flex items-center justify-between", children: [
|
|
1712
|
+
/* @__PURE__ */ jsx("span", { className: "text-[9px] uppercase tracking-wider text-white/40", children: "Performance" }),
|
|
1713
|
+
onClose && /* @__PURE__ */ jsx(
|
|
1714
|
+
"button",
|
|
1715
|
+
{
|
|
1716
|
+
onClick: onClose,
|
|
1717
|
+
className: "-mr-1 px-1 leading-none text-white/40 transition-colors hover:text-white/90",
|
|
1718
|
+
"aria-label": "Hide performance statistics",
|
|
1719
|
+
children: "\xD7"
|
|
1720
|
+
}
|
|
1721
|
+
)
|
|
1722
|
+
] }),
|
|
1723
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between", children: [
|
|
1724
|
+
/* @__PURE__ */ jsxs("span", { className: `text-base font-semibold tabular-nums ${styles.text}`, children: [
|
|
1725
|
+
reading.fps ? reading.fps.toFixed(0) : "\u2014",
|
|
1726
|
+
/* @__PURE__ */ jsx("span", { className: "ml-1 text-[10px] font-normal text-white/40", children: "fps" })
|
|
1727
|
+
] }),
|
|
1728
|
+
/* @__PURE__ */ jsx("span", { className: "tabular-nums text-white/50", children: reading.frameMs ? `${reading.frameMs.toFixed(1)} ms` : "\u2014" })
|
|
1729
|
+
] }),
|
|
1730
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1.5 flex h-5 items-end gap-px", "aria-hidden": "true", children: Array.from({ length: HISTORY }, (_, i) => {
|
|
1731
|
+
const value = history[history.length - HISTORY + i];
|
|
1732
|
+
return /* @__PURE__ */ jsx(
|
|
1733
|
+
"div",
|
|
1734
|
+
{
|
|
1735
|
+
className: `flex-1 rounded-sm ${value === void 0 ? "bg-white/5" : styles.dot}`,
|
|
1736
|
+
style: { height: value === void 0 ? "2px" : `${Math.max(6, value / peak * 100)}%` }
|
|
1737
|
+
},
|
|
1738
|
+
i
|
|
1739
|
+
);
|
|
1740
|
+
}) }),
|
|
1741
|
+
/* @__PURE__ */ jsxs("dl", { className: "mt-2 space-y-0.5", children: [
|
|
1742
|
+
/* @__PURE__ */ jsx(Row, { label: "worst frame", value: reading.worstMs ? `${reading.worstMs.toFixed(0)} ms` : "\u2014" }),
|
|
1743
|
+
/* @__PURE__ */ jsx(
|
|
1744
|
+
Row,
|
|
1745
|
+
{
|
|
1746
|
+
label: "main thread",
|
|
1747
|
+
value: reading.blockedPct === null ? "n/a" : `${reading.blockedPct.toFixed(0)}% blocked`
|
|
1748
|
+
}
|
|
1749
|
+
),
|
|
1750
|
+
/* @__PURE__ */ jsx(
|
|
1751
|
+
Row,
|
|
1752
|
+
{
|
|
1753
|
+
label: "JS heap",
|
|
1754
|
+
value: heap ? `${heap.usedMB.toFixed(0)} / ${heap.limitMB.toFixed(0)} MB` : "n/a"
|
|
1755
|
+
}
|
|
1756
|
+
)
|
|
1757
|
+
] }),
|
|
1758
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 border-t border-white/10 pt-1.5", children: [
|
|
1759
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
1760
|
+
/* @__PURE__ */ jsx("span", { className: `h-1.5 w-1.5 shrink-0 rounded-full ${styles.dot}`, "aria-hidden": "true" }),
|
|
1761
|
+
/* @__PURE__ */ jsx("span", { className: `font-semibold ${styles.text}`, children: verdict.label })
|
|
1762
|
+
] }),
|
|
1763
|
+
/* @__PURE__ */ jsx("p", { className: "mt-0.5 leading-snug text-white/45", children: verdict.detail })
|
|
1764
|
+
] }),
|
|
1765
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center justify-between border-t border-white/10 pt-1.5", children: [
|
|
1766
|
+
/* @__PURE__ */ jsxs("span", { className: "text-white/35", children: [
|
|
1767
|
+
"log ",
|
|
1768
|
+
logSize,
|
|
1769
|
+
" \xB7 ",
|
|
1770
|
+
loggedSeconds < 60 ? `${loggedSeconds}s` : `${Math.round(loggedSeconds / 60)}m`
|
|
1771
|
+
] }),
|
|
1772
|
+
/* @__PURE__ */ jsxs("span", { className: "flex gap-1", children: [
|
|
1773
|
+
/* @__PURE__ */ jsx(
|
|
1774
|
+
"button",
|
|
1775
|
+
{
|
|
1776
|
+
onClick: exportJson,
|
|
1777
|
+
disabled: !logSize,
|
|
1778
|
+
className: "rounded border border-white/15 px-1.5 py-0.5 text-white/60 transition-colors hover:border-white/30 hover:text-white disabled:cursor-not-allowed disabled:opacity-40",
|
|
1779
|
+
children: "JSON"
|
|
1780
|
+
}
|
|
1781
|
+
),
|
|
1782
|
+
/* @__PURE__ */ jsx(
|
|
1783
|
+
"button",
|
|
1784
|
+
{
|
|
1785
|
+
onClick: exportCsv,
|
|
1786
|
+
disabled: !logSize,
|
|
1787
|
+
className: "rounded border border-white/15 px-1.5 py-0.5 text-white/60 transition-colors hover:border-white/30 hover:text-white disabled:cursor-not-allowed disabled:opacity-40",
|
|
1788
|
+
children: "CSV"
|
|
1789
|
+
}
|
|
1790
|
+
)
|
|
1791
|
+
] })
|
|
1792
|
+
] })
|
|
1793
|
+
]
|
|
1794
|
+
}
|
|
1795
|
+
);
|
|
1796
|
+
}
|
|
1797
|
+
function Row({ label, value }) {
|
|
1798
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [
|
|
1799
|
+
/* @__PURE__ */ jsx("dt", { className: "text-white/40", children: label }),
|
|
1800
|
+
/* @__PURE__ */ jsx("dd", { className: "tabular-nums text-white/70", children: value })
|
|
1801
|
+
] });
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1373
1804
|
// src/changelog.ts
|
|
1374
1805
|
var changelog = [];
|
|
1375
1806
|
var changelog_default = changelog;
|
|
@@ -2539,6 +2970,13 @@ function Desktop({ profile }) {
|
|
|
2539
2970
|
] })
|
|
2540
2971
|
] }) });
|
|
2541
2972
|
})(),
|
|
2973
|
+
prefs.show_desktop_stats === true && (() => {
|
|
2974
|
+
const versionShowing = prefs.show_desktop_version === true && !!(host.productVersion ?? APP_VERSION);
|
|
2975
|
+
const taskbarAtBottom = prefs.taskbar_position !== "top" && prefs.taskbar_position !== "left" && prefs.taskbar_position !== "right";
|
|
2976
|
+
const side = prefs.taskbar_position === "right" ? "left-3" : "right-3";
|
|
2977
|
+
const bottom = taskbarAtBottom ? versionShowing ? "bottom-24" : "bottom-16" : versionShowing ? "bottom-8" : "bottom-3";
|
|
2978
|
+
return /* @__PURE__ */ jsx("div", { className: `absolute z-[240] ${side} ${bottom}`, children: /* @__PURE__ */ jsx(PerfStats, { onClose: () => saveShellPrefs({ show_desktop_stats: false }) }) });
|
|
2979
|
+
})(),
|
|
2542
2980
|
prefs.show_desktop_version === true && (host.productVersion ?? APP_VERSION) && /* @__PURE__ */ jsx(
|
|
2543
2981
|
"button",
|
|
2544
2982
|
{
|
|
@@ -4063,7 +4501,7 @@ function previewColor(resolved, light, dark, pink, green, grey, blue) {
|
|
|
4063
4501
|
}
|
|
4064
4502
|
function Customization({ omit, section } = {}) {
|
|
4065
4503
|
const omitSet = new Set(omit ?? []);
|
|
4066
|
-
const inSection = (
|
|
4504
|
+
const inSection = (group2) => section === void 0 || section === group2;
|
|
4067
4505
|
const host = useDesktopHost();
|
|
4068
4506
|
const WALLPAPERS = host.wallpapers && host.wallpapers.length > 0 ? host.wallpapers : DEFAULT_WALLPAPERS;
|
|
4069
4507
|
const { prefs, save } = useShellPrefs();
|
|
@@ -4375,6 +4813,25 @@ function Customization({ omit, section } = {}) {
|
|
|
4375
4813
|
})
|
|
4376
4814
|
}
|
|
4377
4815
|
)
|
|
4816
|
+
] }),
|
|
4817
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
4818
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-900 mb-3", children: "Diagnostics" }),
|
|
4819
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-start gap-2 cursor-pointer", children: [
|
|
4820
|
+
/* @__PURE__ */ jsx(
|
|
4821
|
+
"input",
|
|
4822
|
+
{
|
|
4823
|
+
type: "checkbox",
|
|
4824
|
+
checked: !!pref("show_desktop_stats"),
|
|
4825
|
+
onChange: (e) => savePref("show_desktop_stats", e.target.checked),
|
|
4826
|
+
className: "h-4 w-4 mt-0.5 rounded border-gray-300 text-blue-600"
|
|
4827
|
+
}
|
|
4828
|
+
),
|
|
4829
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm text-gray-700", children: [
|
|
4830
|
+
"Show performance stats",
|
|
4831
|
+
/* @__PURE__ */ jsx("span", { className: "block text-xs text-gray-400", children: "Overlays frame rate, worst frame time and a bottleneck reading on the desktop, beside the version number. Turn it on to check whether Reduce transparency actually helps on this machine \u2014 or whether the slowness is coming from somewhere else." }),
|
|
4832
|
+
/* @__PURE__ */ jsx("span", { className: "block text-xs text-gray-400 mt-1", children: "While it is on, each reading is recorded with what was happening at the time \u2014 how many windows were open, which one was on top, and counts of clicks, keystrokes, scrolls and window drags. No page content or keystroke text is recorded. The log stays on this device and is only shared if you export it with the JSON or CSV button on the overlay." })
|
|
4833
|
+
] })
|
|
4834
|
+
] })
|
|
4378
4835
|
] })
|
|
4379
4836
|
] }),
|
|
4380
4837
|
inSection("layout") && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -4614,7 +5071,7 @@ function BehaviorPanel() {
|
|
|
4614
5071
|
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-900 mb-3", children: "Behavior" }),
|
|
4615
5072
|
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
4616
5073
|
/* @__PURE__ */ jsx(
|
|
4617
|
-
|
|
5074
|
+
Row2,
|
|
4618
5075
|
{
|
|
4619
5076
|
label: "New window position",
|
|
4620
5077
|
options: WINDOW_POSITIONS,
|
|
@@ -4623,7 +5080,7 @@ function BehaviorPanel() {
|
|
|
4623
5080
|
}
|
|
4624
5081
|
),
|
|
4625
5082
|
/* @__PURE__ */ jsx(
|
|
4626
|
-
|
|
5083
|
+
Row2,
|
|
4627
5084
|
{
|
|
4628
5085
|
label: "Double-click desktop",
|
|
4629
5086
|
options: DESKTOP_DBLCLICK,
|
|
@@ -4632,7 +5089,7 @@ function BehaviorPanel() {
|
|
|
4632
5089
|
}
|
|
4633
5090
|
),
|
|
4634
5091
|
/* @__PURE__ */ jsx(
|
|
4635
|
-
|
|
5092
|
+
Row2,
|
|
4636
5093
|
{
|
|
4637
5094
|
label: "Default window size",
|
|
4638
5095
|
options: WINDOW_SIZES,
|
|
@@ -4673,7 +5130,7 @@ function BehaviorPanel() {
|
|
|
4673
5130
|
/* @__PURE__ */ jsx(ModalActions, { children: /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400", children: "Changes are saved automatically" }) })
|
|
4674
5131
|
] });
|
|
4675
5132
|
}
|
|
4676
|
-
function
|
|
5133
|
+
function Row2({
|
|
4677
5134
|
label,
|
|
4678
5135
|
options,
|
|
4679
5136
|
value,
|
|
@@ -10003,6 +10460,6 @@ function useEditHotkey(callback) {
|
|
|
10003
10460
|
}, [callback, isActive]);
|
|
10004
10461
|
}
|
|
10005
10462
|
|
|
10006
|
-
export { ALT, ALT_SHIFT_D, ALT_SHIFT_E, ALT_SHIFT_N, Accordion, AuthScreen, Avatar, AvatarGroup, Banner, BarChart, BehaviorPanel, BulkImportGrid, Button_default as Button, CMD_A, CMD_DOT, CMD_ENTER, CMD_K, CMD_S, Card, ChangePasswordForm, ChatTemplate, Checkbox_default as Checkbox, CheckoutTemplate, ColoredBadge, ContainerFillChart, Customization, DEV_BANNER_TEXT, DashboardTemplate, DataTablePage, DateRangePicker, Desktop, DesktopHostProvider, DevIndicator, DonutChart, ENTER, EmailTemplate, EmptyState, EntityList, ErrorPage, FilterBar, FormField, FormLayoutPage, GalleryTemplate, GlobalSearch, HelpCenter, INPUT_BASE, Input_default as Input, Kanban, Label, Layout, ListFooter, ListLoadError, LoadingSpinner2 as LoadingSpinner, MOD, Markdown, MediaUploadField, MediaUploadGrid, MetricBar, MilestoneTimeline, NativeSelect, NotificationBell, PageHeader, Pagination, PdfActionButton, Radio_default as Radio, ResizableTable, SHIFT, SearchableSelect, Select_default as Select, ServerStatusIndicator, ShellEntityFetcherProvider, ShortcutHelp, SidebarActionButton, SidebarGroupLabel, SidebarNavItem, SoundsPanel, Sparkline, StartMenu, StatCard, StatusBadge, StatusBadgeProvider, SystemPreferences, Tabs, Textarea_default as Textarea, Tooltip, TopNav, WidgetManager, applyDevTitle, createWindowRegistry, findDuplicateKeys, formatDate, inputClasses, isDevEnv, isMac, isSeverityTone, mediaFileName, mergeBulkItems, severityOf, toISODate2 as toISODate, useClickOutside, useColumnConfig, useDesktopHost, useEditHotkey, useFilters, useInfiniteScroll, useNewHotkey, useShellEntityFetcher, useSort, useTableNav };
|
|
10463
|
+
export { ALT, ALT_SHIFT_D, ALT_SHIFT_E, ALT_SHIFT_N, Accordion, AuthScreen, Avatar, AvatarGroup, BLOCKED_PCT_CPU, Banner, BarChart, BehaviorPanel, BulkImportGrid, Button_default as Button, CMD_A, CMD_DOT, CMD_ENTER, CMD_K, CMD_S, Card, ChangePasswordForm, ChatTemplate, Checkbox_default as Checkbox, CheckoutTemplate, ColoredBadge, ContainerFillChart, Customization, DEV_BANNER_TEXT, DashboardTemplate, DataTablePage, DateRangePicker, Desktop, DesktopHostProvider, DevIndicator, DonutChart, ENTER, EmailTemplate, EmptyState, EntityList, ErrorPage, FilterBar, FormField, FormLayoutPage, GalleryTemplate, GlobalSearch, HelpCenter, INPUT_BASE, Input_default as Input, Kanban, LOG_CAP, Label, Layout, ListFooter, ListLoadError, LoadingSpinner2 as LoadingSpinner, MIN_GROUP_SAMPLES, MOD, Markdown, MediaUploadField, MediaUploadGrid, MetricBar, MilestoneTimeline, NativeSelect, NotificationBell, PageHeader, Pagination, PdfActionButton, PerfStats, Radio_default as Radio, ResizableTable, SHIFT, SMOOTH_FPS, SearchableSelect, Select_default as Select, ServerStatusIndicator, ShellEntityFetcherProvider, ShortcutHelp, SidebarActionButton, SidebarGroupLabel, SidebarNavItem, SoundsPanel, Sparkline, StartMenu, StatCard, StatusBadge, StatusBadgeProvider, SystemPreferences, Tabs, Textarea_default as Textarea, Tooltip, TopNav, WidgetManager, appendRecord as appendPerfRecord, applyDevTitle, classify as classifyPerf, createWindowRegistry, findDuplicateKeys, formatDate, inputClasses, isDevEnv, isInteracting, isMac, isSeverityTone, mediaFileName, mergeBulkItems, toCsv as perfLogToCsv, severityOf, summariseFrames, summariseLog as summarisePerfLog, toISODate2 as toISODate, useClickOutside, useColumnConfig, useDesktopHost, useEditHotkey, useFilters, useInfiniteScroll, useNewHotkey, useShellEntityFetcher, useSort, useTableNav };
|
|
10007
10464
|
//# sourceMappingURL=index.js.map
|
|
10008
10465
|
//# sourceMappingURL=index.js.map
|