Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions packages/app/src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { lazy, Suspense, useEffect, useState } from "react";
import { BookOpen } from "@phosphor-icons/react/dist/ssr/BookOpen";
import { Mouse } from "@phosphor-icons/react/dist/ssr/Mouse";
import { Goggles } from "@phosphor-icons/react/dist/ssr/Goggles";
import { Sparkle } from "@phosphor-icons/react/dist/ssr/Sparkle";
import { CaretRight } from "@phosphor-icons/react/dist/ssr/CaretRight";
import { Export } from "@phosphor-icons/react/dist/ssr/Export";
Expand Down Expand Up @@ -37,6 +38,7 @@ import { useAppCommands } from "@/hooks/useAppCommands";
import { COMMAND_ICONS } from "@/lib/command-icons";
import { useCapabilities } from "@/lib/capabilities";
import { useNativeMenu } from "@/hooks/useNativeMenu";
import { xrStore, useXRPresenting, useXRSupportStore } from "@/stores/xr-store";

// Lazy so the prefs dialog (and its wasm-backed keybinding hook) doesn't
// bloat the Header bundle until the user opens it.
Expand Down Expand Up @@ -307,6 +309,36 @@ function RayTracingSubmenu() {
);
}

/** XR entries for the View menu. Always shown — disabled when the browser
* doesn't report support, so users can see the capability exists. */
function XRMenuItems() {
const vr = useXRSupportStore((s) => s.vr);
const ar = useXRSupportStore((s) => s.ar);
const presenting = useXRPresenting();

if (presenting) {
return (
<MenuItem
icon={Goggles}
onSelect={() => xrStore.getState().session?.end()}
>
Exit XR
</MenuItem>
);
}

return (
<>
<MenuItem icon={Goggles} disabled={!vr} onSelect={() => xrStore.enterVR()}>
Enter VR
</MenuItem>
<MenuItem icon={Goggles} disabled={!ar} onSelect={() => xrStore.enterAR()}>
Enter AR
</MenuItem>
</>
);
}

export function Header({ onAboutOpen, onProductOpen, onSave, onOpen, onShareOpen, onVersionHistoryOpen, children }: HeaderProps) {
const user = useAuthStore((s) => s.user);
const isAnonymous = useAuthStore((s) => s.isAnonymous);
Expand Down Expand Up @@ -565,6 +597,7 @@ export function Header({ onAboutOpen, onProductOpen, onSave, onOpen, onShareOpen
>
{t("menu.view.input_preferences")}
</MenuItem>
<XRMenuItems />
<MenuSeparator />
<CommandMenuItem id="cycle-theme" commands={commands} />
</Menubar.Content>
Expand Down
2 changes: 0 additions & 2 deletions packages/app/src/components/Viewport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { DrawingView } from "./DrawingView";
import { TriangleInspectionPanel } from "./TriangleInspector";
import { RayTracedViewportOverlay } from "./RayTracedViewport";
import { FollowModeToggle } from "./FollowModeToggle";
import { EnterXRButton } from "./xr/EnterXRButton";
import { xrStore, useXRPresenting } from "@/stores/xr-store";
import {
useUiStore,
Expand Down Expand Up @@ -350,7 +349,6 @@ export function Viewport() {
drags in the gaps; the toggle itself re-enables them. */}
{!electronicsActive && (
<div className="pointer-events-none absolute top-3 right-3 z-10 flex items-center gap-2">
<EnterXRButton />
<FollowModeToggle />
</div>
)}
Expand Down
55 changes: 0 additions & 55 deletions packages/app/src/components/xr/EnterXRButton.tsx

This file was deleted.

4 changes: 4 additions & 0 deletions packages/app/src/stores/xr-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export const xrStore = createXRStore({
hand: true,
controller: true,
foveation: 0,
// Disable the bundled iwer device emulator. It would otherwise auto-inject a
// floating "Enter XR" DOM button on localhost, which clashes with our
// in-menubar XR entry. Real headsets are unaffected.
emulate: false,
});

type SupportState = {
Expand Down