Skip to content
9 changes: 8 additions & 1 deletion apps/editor/components/scene-loader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ function isLightPreviewQuery(searchParams: URLSearchParams): boolean {

export function SceneLoader(props: SceneLoaderProps) {
const webXRInstalled = useWebXRInstalled()
// The editor follows its host's `.dark` class, and this app keeps the editor
// dark: the page root carries it from the first paint, and <body> for the
// menus and dialogs the editor portals there.
useEffect(() => {
document.body.classList.add('dark')
return () => document.body.classList.remove('dark')
}, [])
Comment thread
cursor[bot] marked this conversation as resolved.
return (
<WebXRFeatureRuntime enabled={webXRInstalled}>
<SceneEditor {...props} />
Expand Down Expand Up @@ -331,7 +338,7 @@ function SceneEditor({ initialScene, meta: initialMeta }: SceneLoaderProps) {
)

return (
<div className="relative h-screen w-screen">
<div className="dark relative h-screen w-screen">
{conflict && (
<div className="dark pointer-events-auto absolute top-14 left-1/2 z-50 w-full max-w-md -translate-x-1/2 rounded-lg border border-border bg-background p-4 text-foreground shadow-xl">
<h2 className="font-semibold text-sm">Another session saved first — refresh?</h2>
Expand Down
2 changes: 1 addition & 1 deletion apps/editor/components/viewer-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ function DisplayMenu() {
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<SwatchBook className="h-4 w-4" />
<span>Theme</span>
<span>Scene theme</span>
<span className="ml-auto truncate text-muted-foreground text-xs">
{activeTheme.name}
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ export function EditorLayoutMobile({
}, [panelSheetHeight, committedSheetH])

return (
<div className="dark flex h-full w-full flex-col bg-sidebar text-foreground">
<div className="flex h-full w-full flex-col bg-sidebar text-foreground">
{navbarSlot}

<div
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/src/components/editor/editor-layout-v2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ export function EditorLayoutV2({
}

return (
<div className="dark flex h-full w-full flex-col bg-sidebar text-foreground">
<div className="flex h-full w-full flex-col bg-sidebar text-foreground">
{/* Top navbar */}
{navbarSlot}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function FloorplanModeCoordinator() {
<span>{notice.message}</span>
{notice.kind === 'switch-to-expert' ? (
<button
className="shrink-0 rounded-md bg-cyan-500/15 px-2.5 py-1 font-medium text-cyan-400 hover:bg-cyan-500/25"
className="shrink-0 rounded-md bg-cyan-500/15 px-2.5 py-1 font-medium text-cyan-700 dark:text-cyan-400 hover:bg-cyan-500/25"
onClick={() => {
setFloorplanMode('expert')
dismissNotice()
Expand All @@ -71,7 +71,7 @@ export function FloorplanModeCoordinator() {
) : null}
<button
aria-label="Dismiss"
className="shrink-0 rounded p-1 text-muted-foreground hover:bg-white/10 hover:text-foreground"
className="shrink-0 rounded p-1 text-muted-foreground hover:bg-black/6 dark:hover:bg-white/10 hover:text-foreground"
onClick={dismissNotice}
type="button"
>
Expand Down
6 changes: 3 additions & 3 deletions packages/editor/src/components/editor/floorplan-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11265,7 +11265,7 @@ export function FloorplanPanel({
}}
>
<div className="mb-3 flex items-start gap-2.5">
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-xl border border-border bg-white/5">
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-xl border border-border bg-black/4 dark:bg-white/5">
<Ruler className="h-4 w-4 text-foreground/80" />
</div>
<div className="min-w-0">
Expand All @@ -11277,7 +11277,7 @@ export function FloorplanPanel({
</div>
</div>

<div className="mb-3 rounded-xl border border-border/70 bg-white/5 px-3 py-2">
<div className="mb-3 rounded-xl border border-border/70 bg-black/4 dark:bg-white/5 px-3 py-2">
<div className="text-[11px] text-muted-foreground uppercase tracking-wide">
Drawn line
</div>
Expand Down Expand Up @@ -11340,7 +11340,7 @@ export function FloorplanPanel({

<div className="mt-4 flex justify-end gap-2">
<button
className="h-8 rounded-lg border border-border px-3 font-medium text-muted-foreground text-xs transition hover:bg-white/8 hover:text-foreground"
className="h-8 rounded-lg border border-border px-3 font-medium text-muted-foreground text-xs transition hover:bg-black/5 dark:hover:bg-white/8 hover:text-foreground"
onClick={() => setPendingReferenceScale(null)}
type="button"
>
Expand Down
9 changes: 1 addition & 8 deletions packages/editor/src/components/editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1566,13 +1566,6 @@ function EditorContent({
if (!isPreviewMode) setPreviewStageMode('3d')
}, [isPreviewMode])

useEffect(() => {
document.body.classList.add('dark')
return () => {
document.body.classList.remove('dark')
}
}, [])

const handleSceneReadyChange = useCallback((ready: boolean) => {
setIsViewerSceneReady(ready)
}, [])
Expand Down Expand Up @@ -1824,7 +1817,7 @@ function EditorContent({
const overlayLeft = LAYOUT_PADDING + (isSidebarCollapsed ? 8 : sidebarWidth) + LAYOUT_GAP

return (
<div className="dark flex h-full w-full gap-3 bg-neutral-100 p-3 text-foreground">
<div className="flex h-full w-full gap-3 bg-neutral-100 p-3 text-foreground">
<FloorplanModeCoordinator />
{visibleLoader && (
<div className="fixed inset-0 z-60">
Expand Down
21 changes: 14 additions & 7 deletions packages/editor/src/components/editor/riser-diagram-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import { X } from 'lucide-react'
import { useMemo } from 'react'
import useEditor from '../../store/use-editor'

const WASTE_COLOR = '#0ea5e9'
const VENT_COLOR = '#a855f7'
// Set per theme on the panel root; the light values are darker so the 9-unit
// diameter labels read on white.
const WASTE_COLOR = 'var(--riser-waste)'
const VENT_COLOR = 'var(--riser-vent)'
const MARKER_COLOR = '#1e293b'
const PADDING = 32
/** Meters → SVG units. The iso projection is in meters; scale up so a
Expand Down Expand Up @@ -45,14 +47,14 @@ function RiserDiagramContent() {
const ty = diagram ? -diagram.bounds.minY * SCALE + PADDING : 0

return (
<div className="dark pointer-events-auto absolute top-4 right-4 z-30 flex max-h-[80vh] w-[26rem] flex-col overflow-hidden rounded-2xl border border-border/40 bg-background/95 text-foreground shadow-lg backdrop-blur-xl">
<div className="pointer-events-auto absolute top-4 right-4 z-30 flex max-h-[80vh] w-[26rem] flex-col overflow-hidden rounded-2xl border border-border/40 bg-background/95 text-foreground shadow-lg backdrop-blur-xl [--riser-vent:#9333ea] [--riser-waste:#0369a1] dark:[--riser-vent:#a855f7] dark:[--riser-waste:#0ea5e9]">
<div className="flex items-center justify-between border-border/40 border-b px-4 py-2.5">
<div className="flex flex-col">
<span className="font-medium text-sm">Riser Diagram</span>
<span className="text-muted-foreground text-xs">DWV plumbing isometric</span>
</div>
<button
className="flex h-7 w-7 items-center justify-center rounded-md transition-colors hover:bg-white/10"
className="flex h-7 w-7 items-center justify-center rounded-md transition-colors hover:bg-black/6 dark:hover:bg-white/10"
onClick={() => setRiserOpen(false)}
>
<X className="h-4 w-4 text-muted-foreground" />
Expand Down Expand Up @@ -87,18 +89,18 @@ function RiserDiagramContent() {
<line
className="cursor-pointer"
onClick={() => select(line.nodeId)}
stroke={color}
strokeDasharray={line.system === 'vent' ? '5 4' : undefined}
strokeLinecap="round"
strokeWidth={(line.vertical ? 3.5 : 2.5) + (isSel ? 2 : 0)}
style={{ stroke: color }}
x1={line.from[0] * SCALE}
x2={line.to[0] * SCALE}
y1={line.from[1] * SCALE}
y2={line.to[1] * SCALE}
/>
<text
fill={color}
fontSize={9}
style={{ fill: color }}
x={((line.from[0] + line.to[0]) / 2) * SCALE + 4}
y={((line.from[1] + line.to[1]) / 2) * SCALE - 3}
>
Expand All @@ -115,7 +117,12 @@ function RiserDiagramContent() {
transform={`translate(${marker.point[0] * SCALE}, ${marker.point[1] * SCALE})`}
>
{marker.kind === 'vent-termination' ? (
<path d="M -5 0 L 0 -7 L 5 0" fill="none" stroke={VENT_COLOR} strokeWidth={2} />
<path
d="M -5 0 L 0 -7 L 5 0"
fill="none"
strokeWidth={2}
style={{ stroke: VENT_COLOR }}
/>
) : (
<circle fill={MARKER_COLOR} r={3} stroke={MARKER_COLOR} strokeWidth={1.5} />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ export function SnapshotCaptureOverlay({ projectId }: { projectId: string }) {
const captureDisabled = captureState !== 'idle' || (mode === 'area' && !hasSelection)

return (
<div className="pointer-events-none absolute inset-0 z-40" ref={overlayRef}>
<div className="dark pointer-events-none absolute inset-0 z-40" ref={overlayRef}>
{/* Walk / drone keep the walkthrough's centered pointer (the ring means
E opens the door / window under it) — the capture overlay replaces
the walkthrough HUD, so the crosshair rides along here. */}
Expand Down
7 changes: 4 additions & 3 deletions packages/editor/src/components/editor/views/view-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ const GROUP = cn(
)
const SEGMENT =
'flex items-center justify-center gap-1.5 px-2.5 font-medium text-xs transition-colors'
const ACTIVE = 'bg-white/10 text-foreground'
const IDLE = 'text-muted-foreground/70 hover:bg-white/8 hover:text-muted-foreground'
const ACTIVE = 'bg-black/6 dark:bg-white/10 text-foreground'
const IDLE =
'text-muted-foreground/70 hover:bg-black/5 dark:hover:bg-white/8 hover:text-muted-foreground'

/** The views of one pane: its pinned views, the one it shows, and a menu with the rest. */
export function ViewTabs({ pane, className }: { pane: ViewPaneIndex; className?: string }) {
Expand Down Expand Up @@ -184,7 +185,7 @@ function MoreViewsMenu({
aria-label={pinned ? `Unpin ${view.label}` : `Pin ${view.label}`}
aria-pressed={pinned}
className={cn(
'flex h-6 w-6 items-center justify-center rounded-md transition-colors hover:bg-white/10',
'flex h-6 w-6 items-center justify-center rounded-md transition-colors hover:bg-black/6 dark:hover:bg-white/10',
pinned ? 'text-foreground' : 'text-muted-foreground/60',
)}
onClick={(event) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ interface ActionButtonProps extends React.ComponentProps<typeof Button> {
isActive?: boolean
tooltipContent?: React.ReactNode
tooltipSide?: 'top' | 'right' | 'bottom' | 'left'
/** The tooltip portals to `<body>`, so a button in a themed scope passes the scope here. */
tooltipClassName?: string
}

export const ActionButton = React.forwardRef<HTMLButtonElement, ActionButtonProps>(
Expand All @@ -26,6 +28,7 @@ export const ActionButton = React.forwardRef<HTMLButtonElement, ActionButtonProp
isActive,
tooltipContent,
tooltipSide,
tooltipClassName,
onClick,
onMouseEnter,
...props
Expand Down Expand Up @@ -68,7 +71,7 @@ export const ActionButton = React.forwardRef<HTMLButtonElement, ActionButtonProp
)}
</Button>
</TooltipTrigger>
<TooltipContent side={tooltipSide}>
<TooltipContent className={tooltipClassName} side={tooltipSide}>
{tooltipContent || (
<p>
{label} {shortcut && `(${shortcut})`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function CameraActions({ hideOrbit = false }: { hideOrbit?: boolean }) {
<>
{/* Orbit CCW */}
<ActionButton
className="group hover:bg-white/5"
className="group hover:bg-black/4"
label="Orbit Left"
onClick={orbitCCW}
size="icon"
Expand All @@ -47,7 +47,7 @@ export function CameraActions({ hideOrbit = false }: { hideOrbit?: boolean }) {

{/* Orbit CW */}
<ActionButton
className="group hover:bg-white/5"
className="group hover:bg-black/4"
label="Orbit Right"
onClick={orbitCW}
size="icon"
Expand All @@ -70,7 +70,7 @@ export function CameraActions({ hideOrbit = false }: { hideOrbit?: boolean }) {
{/* Top View */}
{!is2dOnly && (
<ActionButton
className="group hover:bg-white/5"
className="group hover:bg-black/4"
label="Top View"
onClick={goToTopView}
size="icon"
Expand Down
12 changes: 6 additions & 6 deletions packages/editor/src/components/ui/action-menu/control-modes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ const controls: ControlConfig[] = [
imageSrc: '/icons/select.webp',
label: 'Select',
shortcut: 'V',
color: 'hover:bg-blue-500/20 hover:text-blue-400',
activeColor: 'bg-blue-500/20 text-blue-400',
color: 'hover:bg-blue-500/20 hover:text-blue-600 dark:hover:text-blue-400',
activeColor: 'bg-blue-500/20 text-blue-600 dark:text-blue-400 dark:hover:text-accent-foreground',
},
{
id: 'delete',
icon: Trash2,
label: 'Delete',
shortcut: 'X',
color: 'hover:bg-red-500/20 hover:text-red-400',
activeColor: 'bg-red-500/20 text-red-400',
color: 'hover:bg-red-500/20 hover:text-red-600 dark:hover:text-red-400',
activeColor: 'bg-red-500/20 text-red-600 dark:text-red-400 dark:hover:text-accent-foreground',
},
]

Expand Down Expand Up @@ -87,8 +87,8 @@ export function ControlModes() {
'group text-muted-foreground',
!(isImageMode || isActive) && c.color,
!isImageMode && isActive && c.activeColor,
isImageMode && isActive && 'bg-white/10 hover:bg-white/10',
isImageMode && !isActive && 'hover:bg-white/5',
isImageMode && isActive && 'bg-black/6 hover:bg-black/6 dark:bg-white/10',
isImageMode && !isActive && 'hover:bg-black/4',
)}
// A static hook for a host app that wants to point a first-run
// tour at this button. Nothing here reads it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function FollowPascalPill() {
{shows ? (
<motion.button
animate={{ opacity: 1, y: 0, scale: 1 }}
className="-translate-x-1/2 fixed bottom-24 left-1/2 z-50 rounded-full border border-violet-400/40 bg-background/90 px-4 py-2 font-medium text-sm text-violet-200 shadow-xl backdrop-blur-md active:scale-[0.97]"
className="-translate-x-1/2 fixed bottom-24 left-1/2 z-50 rounded-full border border-violet-400/40 bg-background/90 px-4 py-2 font-medium text-sm text-violet-700 shadow-xl dark:text-violet-200 backdrop-blur-md active:scale-[0.97]"
exit={{ opacity: 0, y: reduced ? 0 : 8, scale: reduced ? 1 : 0.96 }}
initial={{ opacity: 0, y: reduced ? 0 : 8, scale: reduced ? 1 : 0.96 }}
onClick={resume}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,28 @@ import useFollowPascal from '../../../store/use-follow-pascal'
import { ActionButton } from './action-button'

/** The toolbar's switch for Follow Pascal: the camera follows what the agent builds. */
export function FollowPascalToggle({ className }: { className?: string }) {
export function FollowPascalToggle({
className,
tooltipClassName,
}: {
className?: string
tooltipClassName?: string
}) {
const on = useFollowPascal((state) => state.on)
const setOn = useFollowPascal((state) => state.setOn)
return (
<ActionButton
aria-pressed={on}
className={cn(
on ? 'bg-violet-500/20 text-violet-300' : 'hover:bg-white/5 hover:text-violet-300',
on
? 'bg-violet-500/20 text-violet-600 dark:text-violet-300 dark:hover:text-accent-foreground'
: 'hover:bg-black/4 hover:text-violet-600 dark:hover:text-violet-300',
className,
)}
label={`Follow Pascal: ${on ? 'On' : 'Off'}`}
onClick={() => setOn(!on)}
size="icon"
tooltipClassName={tooltipClassName}
variant="ghost"
>
<Video className="h-6 w-6" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ export function MeasurementControl() {
className={cn(
'rounded-r-none p-0 text-muted-foreground',
isControlActive
? 'bg-cyan-500/20 text-cyan-400 hover:bg-cyan-500/20'
: 'hover:bg-cyan-500/15 hover:text-cyan-400',
? 'bg-cyan-500/20 text-cyan-700 hover:bg-cyan-500/20 dark:text-cyan-400 dark:hover:text-accent-foreground'
: 'hover:bg-cyan-500/15 hover:text-cyan-700 dark:hover:text-cyan-400',
)}
label={`Measure: ${selectedLabel}`}
onClick={handlePrimaryClick}
Expand All @@ -166,8 +166,8 @@ export function MeasurementControl() {
className={cn(
'flex h-11 w-6 items-center justify-center rounded-r-lg text-muted-foreground transition-colors',
isOpen
? 'bg-cyan-500/15 text-cyan-400'
: 'hover:bg-cyan-500/10 hover:text-cyan-400',
? 'bg-cyan-500/15 text-cyan-700 dark:text-cyan-400'
: 'hover:bg-cyan-500/10 hover:text-cyan-700 dark:hover:text-cyan-400',
)}
type="button"
>
Expand Down Expand Up @@ -198,8 +198,8 @@ export function MeasurementControl() {
className={cn(
'flex h-9 w-full items-center gap-2 rounded-md px-2.5 text-left text-sm transition-colors',
isSelected
? 'bg-white/10 text-foreground'
: 'text-muted-foreground hover:bg-white/8 hover:text-foreground',
? 'bg-black/6 dark:bg-white/10 text-foreground'
: 'text-muted-foreground hover:bg-black/5 dark:hover:bg-white/8 hover:text-foreground',
)}
key={option.kind}
onClick={() => {
Expand Down Expand Up @@ -234,8 +234,8 @@ export function MeasurementControl() {
className={cn(
'flex h-9 w-full items-center gap-2 rounded-md px-2.5 text-left text-sm transition-colors',
isSelected
? 'bg-white/10 text-foreground'
: 'text-muted-foreground hover:bg-white/8 hover:text-foreground',
? 'bg-black/6 dark:bg-white/10 text-foreground'
: 'text-muted-foreground hover:bg-black/5 dark:hover:bg-white/8 hover:text-foreground',
)}
key={`${option.mode}-${option.chainMode}`}
onClick={() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ export function PointAskButton() {
<ActionButton
className={cn(
'group text-muted-foreground',
active ? 'bg-[#a99bff]/20 text-[#c9c1ff]' : 'hover:bg-[#a99bff]/15 hover:text-[#c9c1ff]',
active
? 'bg-[#a99bff]/20 text-[#5d49df] dark:text-[#c9c1ff] dark:hover:text-accent-foreground'
: 'hover:bg-[#a99bff]/15 hover:text-[#5d49df] dark:hover:text-[#c9c1ff]',
)}
data-guide-target="mode-point-ask"
disabled={unavailable}
Expand Down
Loading
Loading