react-os-shell 4.4.0 → 4.6.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/index.js CHANGED
@@ -15,8 +15,8 @@ export { setShellAuthBridge } from './chunk-3NZSR3ZG.js';
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-GDOCYHAG.js';
19
- export { VERSION } from './chunk-GDOCYHAG.js';
18
+ import { APP_VERSION, VERSION } from './chunk-3DIOUFVR.js';
19
+ export { VERSION } from './chunk-3DIOUFVR.js';
20
20
  import { registerModalEscapeInterceptor, useIsMobile, useWindowManager, PopupMenu, PopupMenuLabel, PopupMenuDivider, PopupMenuItem, WINDOW_REGISTRY, isPageEntry, Modal, setPerfCollecting, drainPerfEvents, useShellPrefs, markMenuOpen, SIDEBAR_STRIP_W, forgetMaximizedWindowBoxes, ModalActions, useModalActive, client_default, LoadingSpinner, isShellApiClientConfigured, useUndo, CancelButton, setWindowPosition } from './chunk-EF5W3EXL.js';
21
21
  export { CancelButton, ConfirmProvider, CopyButton, DocFavStar, Modal, ModalActions, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, ShellPrefsProvider, UndoProvider, WindowCrashedFallback, WindowErrorBoundary, WindowManagerProvider, WindowTitle, beginWindowGesture, commitExposeHighlight, confirm, confirmDestructive, exitExposeMode, getActiveWindowRoute, getExposeHighlight, getWindowPosition, isEntityEntry, isPageEntry, markMenuOpen, prompt, registerModalEscapeInterceptor, setExposeHighlight, setShellApiClient, setShellWindowRegistry, setWindowDefaultPosition, setWindowPosition, subscribeExposeHighlight, toggleExposeMode, useIsActiveWindow, useLocalStoragePrefs, useModalActive, useShellPrefs, useUndo, useUndoable, useUndoableState, useWidgetSettings, useWindowManager, useWindowMenuItem, useWindowTitle } from './chunk-EF5W3EXL.js';
22
22
  import { glassStyle, startMenuCategories, navSections, isSection, GLASS_INPUT_BG, navIcons, isReachable, sectionIcons, useShellAuth, visibleChildren } from './chunk-DKDD5KXG.js';
@@ -466,9 +466,9 @@ function HelpCenter({
466
466
  (a, b) => (rank.get(a.key) ?? Infinity) - (rank.get(b.key) ?? Infinity)
467
467
  );
468
468
  }, [docs, categoryOrder]);
469
- const matches = (doc) => !q || doc.title.toLowerCase().includes(q) || doc.body.toLowerCase().includes(q) || doc.category_label.toLowerCase().includes(q);
469
+ const matches2 = (doc) => !q || doc.title.toLowerCase().includes(q) || doc.body.toLowerCase().includes(q) || doc.category_label.toLowerCase().includes(q);
470
470
  const visibleGroups = useMemo(
471
- () => groups.map((g) => ({ ...g, docs: g.docs.filter(matches) })).filter((g) => g.docs.length > 0),
471
+ () => groups.map((g) => ({ ...g, docs: g.docs.filter(matches2) })).filter((g) => g.docs.length > 0),
472
472
  [groups, q]
473
473
  );
474
474
  const visibleDocs = useMemo(() => visibleGroups.flatMap((g) => g.docs), [visibleGroups]);
@@ -1399,13 +1399,13 @@ function summariseFrames(timestamps) {
1399
1399
  };
1400
1400
  }
1401
1401
  function classify(reading) {
1402
- const { fps, blockedPct } = reading;
1402
+ const { fps: fps2, blockedPct } = reading;
1403
1403
  const threadBlocked = blockedPct !== null && blockedPct >= BLOCKED_PCT_CPU;
1404
- if (!Number.isFinite(fps) || fps <= 0) {
1404
+ if (!Number.isFinite(fps2) || fps2 <= 0) {
1405
1405
  if (threadBlocked) return CPU_BOUND;
1406
1406
  return { kind: "unknown", label: "Measuring\u2026", detail: "Collecting frame timings." };
1407
1407
  }
1408
- if (fps >= SMOOTH_FPS) {
1408
+ if (fps2 >= SMOOTH_FPS) {
1409
1409
  return {
1410
1410
  kind: "smooth",
1411
1411
  label: "Smooth",
@@ -1550,6 +1550,138 @@ function toCsv(log) {
1550
1550
  ...log.map((r) => CSV_COLUMNS.map((c) => cell(r[c])).join(","))
1551
1551
  ].join("\n");
1552
1552
  }
1553
+
1554
+ // src/shell/perfEnvironment.ts
1555
+ function pickBrand(brands) {
1556
+ if (!brands?.length) return null;
1557
+ const real = brands.filter((b) => !/not[^a-z]*a[^a-z]*brand/i.test(b.brand));
1558
+ const named = real.find((b) => b.brand !== "Chromium") ?? real[0];
1559
+ return named ? `${named.brand} ${named.version}` : null;
1560
+ }
1561
+ function asString(value) {
1562
+ return typeof value === "string" && value ? value : null;
1563
+ }
1564
+ async function requestAsyncEnvironment() {
1565
+ return { hints: await readHints(), battery: await readBattery() };
1566
+ }
1567
+ async function readHints() {
1568
+ const uaData = navigator.userAgentData;
1569
+ if (!uaData) return null;
1570
+ const base = {
1571
+ platform: uaData.platform ?? null,
1572
+ platformVersion: null,
1573
+ architecture: null,
1574
+ model: null,
1575
+ browser: pickBrand(uaData.brands)
1576
+ };
1577
+ if (!uaData.getHighEntropyValues) return base;
1578
+ try {
1579
+ const high = await uaData.getHighEntropyValues([
1580
+ "platform",
1581
+ "platformVersion",
1582
+ "architecture",
1583
+ "model",
1584
+ "fullVersionList"
1585
+ ]);
1586
+ const full = high.fullVersionList;
1587
+ return {
1588
+ platform: asString(high.platform) ?? base.platform,
1589
+ platformVersion: asString(high.platformVersion),
1590
+ architecture: asString(high.architecture),
1591
+ model: asString(high.model),
1592
+ // The full list carries the build number; the low-entropy brands only
1593
+ // carry the major, which is not enough to match against a known bug.
1594
+ browser: pickBrand(full) ?? base.browser
1595
+ };
1596
+ } catch {
1597
+ return base;
1598
+ }
1599
+ }
1600
+ async function readBattery() {
1601
+ const getBattery = navigator.getBattery;
1602
+ if (!getBattery) return null;
1603
+ try {
1604
+ const battery = await getBattery.call(navigator);
1605
+ return { charging: battery.charging, level: battery.level };
1606
+ } catch {
1607
+ return null;
1608
+ }
1609
+ }
1610
+ function readGpu() {
1611
+ if (typeof document === "undefined") return { renderer: null, vendor: null, available: false };
1612
+ try {
1613
+ const gl = document.createElement("canvas").getContext("webgl");
1614
+ if (!gl) return { renderer: null, vendor: null, available: false };
1615
+ const debug = gl.getExtension("WEBGL_debug_renderer_info");
1616
+ const renderer = debug ? gl.getParameter(debug.UNMASKED_RENDERER_WEBGL) : gl.getParameter(gl.RENDERER);
1617
+ const vendor = debug ? gl.getParameter(debug.UNMASKED_VENDOR_WEBGL) : gl.getParameter(gl.VENDOR);
1618
+ gl.getExtension("WEBGL_lose_context")?.loseContext();
1619
+ return { renderer: asString(renderer), vendor: asString(vendor), available: true };
1620
+ } catch {
1621
+ return { renderer: null, vendor: null, available: false };
1622
+ }
1623
+ }
1624
+ function matches(query) {
1625
+ try {
1626
+ return window.matchMedia(query).matches;
1627
+ } catch {
1628
+ return false;
1629
+ }
1630
+ }
1631
+ function readEnvironment(async_) {
1632
+ const nav = navigator;
1633
+ const memory = performance.memory;
1634
+ const hints = async_?.hints ?? null;
1635
+ const gpu = readGpu();
1636
+ const conn = nav.connection;
1637
+ return {
1638
+ userAgent: nav.userAgent,
1639
+ browser: hints?.browser ?? null,
1640
+ platform: hints?.platform ?? null,
1641
+ platformVersion: hints?.platformVersion ?? null,
1642
+ architecture: hints?.architecture ?? null,
1643
+ model: hints?.model ?? null,
1644
+ cpuCores: typeof nav.hardwareConcurrency === "number" ? nav.hardwareConcurrency : null,
1645
+ deviceMemoryGB: typeof nav.deviceMemory === "number" ? nav.deviceMemory : null,
1646
+ gpu: gpu.renderer,
1647
+ gpuVendor: gpu.vendor,
1648
+ webglAvailable: gpu.available,
1649
+ screen: {
1650
+ width: screen.width,
1651
+ height: screen.height,
1652
+ dpr: window.devicePixelRatio,
1653
+ colorDepth: typeof screen.colorDepth === "number" ? screen.colorDepth : null
1654
+ },
1655
+ viewport: { width: window.innerWidth, height: window.innerHeight },
1656
+ heapLimitMB: memory ? Math.round(memory.jsHeapSizeLimit / 1048576) : null,
1657
+ network: conn ? {
1658
+ effectiveType: asString(conn.effectiveType),
1659
+ downlinkMbps: typeof conn.downlink === "number" ? conn.downlink : null,
1660
+ rttMs: typeof conn.rtt === "number" ? conn.rtt : null,
1661
+ saveData: conn.saveData === true
1662
+ } : null,
1663
+ battery: async_?.battery ?? null,
1664
+ prefersReducedMotion: matches("(prefers-reduced-motion: reduce)"),
1665
+ forcedColors: matches("(forced-colors: active)"),
1666
+ reducedTransparency: document.documentElement.classList.contains("rosh-reduce-transparency"),
1667
+ touchPoints: typeof nav.maxTouchPoints === "number" ? nav.maxTouchPoints : null
1668
+ };
1669
+ }
1670
+ function describeMachine(env) {
1671
+ const parts = [];
1672
+ if (env.browser) parts.push(env.browser);
1673
+ const os = [env.platform, env.platformVersion].filter(Boolean).join(" ");
1674
+ if (os) parts.push(env.architecture ? `${os} (${env.architecture})` : os);
1675
+ if (env.gpu) parts.push(env.gpu);
1676
+ else if (!env.webglAvailable) parts.push("no WebGL (software rendering)");
1677
+ if (env.cpuCores) parts.push(`${env.cpuCores} cores`);
1678
+ if (env.deviceMemoryGB) parts.push(`${env.deviceMemoryGB}GB+ RAM`);
1679
+ parts.push(`${env.screen.width}\xD7${env.screen.height} @${env.screen.dpr}x`);
1680
+ if (env.battery && !env.battery.charging) parts.push(`on battery ${Math.round(env.battery.level * 100)}%`);
1681
+ if (env.reducedTransparency) parts.push("reduce transparency on");
1682
+ if (env.prefersReducedMotion) parts.push("reduced motion");
1683
+ return parts.join(" \xB7 ");
1684
+ }
1553
1685
  var FLUSH_MS = 500;
1554
1686
  var WINDOW_MS = 1e3;
1555
1687
  var HISTORY = 40;
@@ -1586,6 +1718,7 @@ function PerfStats({ onClose, onSubmit, className = "" }) {
1586
1718
  const startedAtRef = useRef(0);
1587
1719
  const persistedAtRef = useRef(0);
1588
1720
  const snapshotRef = useRef(null);
1721
+ const envRef = useRef(null);
1589
1722
  const [longTaskSupported, setLongTaskSupported] = useState(false);
1590
1723
  const [reading, setReading] = useState({ fps: 0, frameMs: 0, worstMs: 0, blockedPct: null });
1591
1724
  const [history, setHistory] = useState([]);
@@ -1599,6 +1732,15 @@ function PerfStats({ onClose, onSubmit, className = "" }) {
1599
1732
  setPerfCollecting(true);
1600
1733
  return () => setPerfCollecting(false);
1601
1734
  }, []);
1735
+ useEffect(() => {
1736
+ let live = true;
1737
+ requestAsyncEnvironment().then((env) => {
1738
+ if (live) envRef.current = env;
1739
+ });
1740
+ return () => {
1741
+ live = false;
1742
+ };
1743
+ }, []);
1602
1744
  useEffect(() => {
1603
1745
  let raf = 0;
1604
1746
  const tick = (now) => {
@@ -1733,20 +1875,21 @@ function PerfStats({ onClose, onSubmit, className = "" }) {
1733
1875
  const buildReport = useCallback(() => {
1734
1876
  const records = snapshotRef.current ?? logRef.current;
1735
1877
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
1878
+ const summary = summariseLog(records);
1879
+ const environment = readEnvironment(envRef.current);
1736
1880
  return {
1737
1881
  message,
1738
1882
  filename: `perf-log-${stamp}.json`,
1739
- summary: summariseLog(records),
1883
+ summary,
1884
+ environment,
1740
1885
  verdict: verdict.label,
1741
1886
  json: JSON.stringify(
1742
1887
  {
1743
1888
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1744
1889
  message,
1745
1890
  verdict: verdict.label,
1746
- userAgent: navigator.userAgent,
1747
- screen: { width: window.screen.width, height: window.screen.height, dpr: window.devicePixelRatio },
1748
- reducedTransparency: document.documentElement.classList.contains("rosh-reduce-transparency"),
1749
- summary: summariseLog(records),
1891
+ environment,
1892
+ summary,
1750
1893
  records
1751
1894
  },
1752
1895
  null,
@@ -4973,7 +5116,7 @@ function Customization({ omit, section } = {}) {
4973
5116
  /* @__PURE__ */ jsxs("span", { className: "text-sm text-gray-700", children: [
4974
5117
  "Show performance stats",
4975
5118
  /* @__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." }),
4976
- /* @__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, which menu or submenu you opened, how long you spent moving or resizing a window, and counts of clicks, keystrokes and scrolls. No page content or keystroke text is recorded. The log stays on this device unless you use the overlay's Report button, which sends it \u2014 with whatever you type in the box \u2014 to the team as a bug report." })
5119
+ /* @__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, which menu or submenu you opened, how long you spent moving or resizing a window, and counts of clicks, keystrokes and scrolls. No page content or keystroke text is recorded. The log stays on this device unless you use the overlay's Report button, which sends it \u2014 with whatever you type in the box, and a description of this computer (browser, operating system, processor cores, memory, graphics card, screen, connection speed and whether you are on battery) \u2014 to the team as a bug report." })
4977
5120
  ] })
4978
5121
  ] })
4979
5122
  ] })
@@ -8521,6 +8664,46 @@ function MetricBar({
8521
8664
  ] });
8522
8665
  }
8523
8666
 
8667
+ // src/shell/perfDescribe.ts
8668
+ var TOP_GROUPS = 3;
8669
+ var fps = (n) => n > 0 ? `${n.toFixed(0)} fps` : "stalled";
8670
+ var duration = (ms) => {
8671
+ const seconds = Math.round(ms / 1e3);
8672
+ return seconds < 60 ? `${seconds}s` : `${Math.round(seconds / 60)}m`;
8673
+ };
8674
+ function describePerfReport(report) {
8675
+ const { summary, message, verdict, environment } = report;
8676
+ const lines = [`Performance report \u2014 ${verdict}`, ""];
8677
+ lines.push(message.trim() || "(No description given.)", "");
8678
+ lines.push(`Machine: ${describeMachine(environment)}`);
8679
+ const net = environment.network;
8680
+ if (net?.effectiveType && net.effectiveType !== "4g") {
8681
+ lines.push(`Connection: ${net.effectiveType}${net.rttMs ? ` \xB7 ${net.rttMs} ms round trip` : ""}`);
8682
+ }
8683
+ lines.push("");
8684
+ lines.push(
8685
+ `Median ${fps(summary.medianFps)} over ${duration(summary.durationMs)} (${summary.samples} samples), worst frame ${summary.worstFrameMs.toFixed(0)} ms.`
8686
+ );
8687
+ const activity = summary.byActivity.slice(0, TOP_GROUPS);
8688
+ if (activity.length) {
8689
+ lines.push(
8690
+ `Slowest while: ${activity.map((a) => `${a.kind} ${fps(a.medianFps)} (worst ${a.worstMs.toFixed(0)} ms)`).join(" \xB7 ")}`
8691
+ );
8692
+ }
8693
+ const menu = summary.worstMenus[0];
8694
+ if (menu) lines.push(`Slowest menu: ${menu.key} \u2014 ${fps(menu.medianFps)}`);
8695
+ const win = summary.worstWindows[0];
8696
+ if (win) lines.push(`Slowest window: ${win.key} \u2014 ${fps(win.medianFps)}`);
8697
+ if (summary.idle && summary.interacting) {
8698
+ lines.push(`At rest ${fps(summary.idle.medianFps)} vs in use ${fps(summary.interacting.medianFps)}.`);
8699
+ }
8700
+ lines.push("", `Full log attached as ${report.filename}.`);
8701
+ return lines.join("\n");
8702
+ }
8703
+ function perfReportFile(report) {
8704
+ return new File([report.json], report.filename, { type: "application/json" });
8705
+ }
8706
+
8524
8707
  // src/utils/mergeBulkItems.ts
8525
8708
  function findDuplicateKeys(rows, keyRowKey) {
8526
8709
  const groups = /* @__PURE__ */ new Map();
@@ -10637,6 +10820,6 @@ function useEditHotkey(callback) {
10637
10820
  }, [callback, isActive]);
10638
10821
  }
10639
10822
 
10640
- 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_EVENT_SAMPLES, 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, UndoControls, WidgetManager, appendRecord as appendPerfRecord, applyDevTitle, classifyActivity, 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 };
10823
+ 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_EVENT_SAMPLES, 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, UndoControls, WidgetManager, appendRecord as appendPerfRecord, applyDevTitle, classifyActivity, classify as classifyPerf, createWindowRegistry, describeMachine, describePerfReport, findDuplicateKeys, formatDate, inputClasses, isDevEnv, isInteracting, isMac, isSeverityTone, mediaFileName, mergeBulkItems, toCsv as perfLogToCsv, perfReportFile, readEnvironment as readPerfEnvironment, requestAsyncEnvironment, severityOf, summariseFrames, summariseLog as summarisePerfLog, toISODate2 as toISODate, useClickOutside, useColumnConfig, useDesktopHost, useEditHotkey, useFilters, useInfiniteScroll, useNewHotkey, useShellEntityFetcher, useSort, useTableNav };
10641
10824
  //# sourceMappingURL=index.js.map
10642
10825
  //# sourceMappingURL=index.js.map