+
+
+ Radio Pascal
+
+
+
{
+ e.stopPropagation()
+ handlePlayPause()
+ }}
+ className="rounded-sm p-1 transition-all cursor-pointer bg-accent/30 hover:bg-accent hover:text-accent-foreground hover:shadow-sm"
+ role="button"
+ tabIndex={0}
+ aria-label={isRadioPlaying ? 'Pause' : 'Play'}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault()
+ e.stopPropagation()
+ handlePlayPause()
+ }
+ }}
+ >
+ {isRadioPlaying ? : }
+
-
-
-
- {/* Current song info with prev/next */}
-
-
Now Playing
-
-
-
{currentTrack.title}
-
+
+
+
+
+ {isOpen && (
+
+
+
+ {/* Current song info with prev/next */}
+
+
Now Playing
+
+
+
+ {currentTrack.title}
+
+
+
+
+
+ {/* Volume control */}
+
+
+
+ {radioVolume}%
-
- {/* Volume control */}
-
-
-
- {radioVolume}%
-
-
-
-
-
+
+ )}
+
+
)
}
diff --git a/apps/editor/components/systems/zone/zone-system.tsx b/apps/editor/components/systems/zone/zone-system.tsx
index 1c5e2cf6..883123de 100644
--- a/apps/editor/components/systems/zone/zone-system.tsx
+++ b/apps/editor/components/systems/zone/zone-system.tsx
@@ -1,14 +1,14 @@
import { sceneRegistry, useScene, type ZoneNode } from '@pascal-app/core'
import { useViewer } from '@pascal-app/viewer'
-import { useEffect } from 'react'
+import { useFrame } from '@react-three/fiber'
import useEditor from '@/store/use-editor'
export const ZoneSystem = () => {
- const structureLayer = useEditor((state) => state.structureLayer)
- const levelMode = useViewer((state) => state.levelMode)
- const selectedLevelId = useViewer((state) => state.selection.levelId)
+ useFrame(() => {
+ const structureLayer = useEditor.getState().structureLayer
+ const levelMode = useViewer.getState().levelMode
+ const selectedLevelId = useViewer.getState().selection.levelId
- useEffect(() => {
const visible = structureLayer === 'zones'
const zones = sceneRegistry.byType.zone || new Set()
const nodes = useScene.getState().nodes
@@ -23,21 +23,27 @@ export const ZoneSystem = () => {
const isOnSelectedLevel = zone?.parentId === selectedLevelId
const hideInSoloMode = levelMode === 'solo' && selectedLevelId && !isOnSelectedLevel
- obj.visible = visible
+ if (obj.visible !== visible) {
+ obj.visible = visible
+ }
const label = obj.getObjectByName('label')
if (label) {
// Hide label if zone layer is off OR if in solo mode on a different level
const showLabel = visible && !hideInSoloMode;
const labelPosition = obj.userData.labelPosition as [number, number, number] | undefined
- if (showLabel && labelPosition) {
- label.position.set(...labelPosition)
- } else {
- label.position.set(-9999, -9999, -9999)
+
+ const targetX = showLabel && labelPosition ? labelPosition[0] : -9999
+ if (label.position.x !== targetX) {
+ if (showLabel && labelPosition) {
+ label.position.set(...labelPosition)
+ } else {
+ label.position.set(-9999, -9999, -9999)
+ }
}
}
})
- }, [structureLayer, levelMode, selectedLevelId])
+ })
return null
}
diff --git a/apps/editor/components/ui/action-menu/camera-actions.tsx b/apps/editor/components/ui/action-menu/camera-actions.tsx
index 929b557f..63309368 100644
--- a/apps/editor/components/ui/action-menu/camera-actions.tsx
+++ b/apps/editor/components/ui/action-menu/camera-actions.tsx
@@ -1,7 +1,7 @@
'use client'
import { emitter } from '@pascal-app/core'
-import { RotateCcw, RotateCw, Rotate3D } from 'lucide-react'
+import Image from 'next/image'
import { Button } from '@/components/ui/primitives/button'
import {
Tooltip,
@@ -28,12 +28,18 @@ export function CameraActions() {
@@ -45,12 +51,18 @@ export function CameraActions() {
@@ -62,12 +74,18 @@ export function CameraActions() {
diff --git a/apps/editor/components/ui/action-menu/control-modes.tsx b/apps/editor/components/ui/action-menu/control-modes.tsx
index c4d09097..15b97c62 100644
--- a/apps/editor/components/ui/action-menu/control-modes.tsx
+++ b/apps/editor/components/ui/action-menu/control-modes.tsx
@@ -1,19 +1,21 @@
"use client";
+import Image from "next/image";
import { Button } from "@/components/ui/primitives/button";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/primitives/tooltip";
-import { Hammer, MousePointer2, Pencil, Trash2 } from "lucide-react";
+import { Pencil, Trash2, type LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import useEditor, { Mode, Phase } from "@/store/use-editor";
type ModeConfig = {
id: Mode;
- icon: typeof MousePointer2;
+ icon?: LucideIcon;
+ imageSrc?: string;
label: string;
shortcut: string;
color: string;
@@ -24,7 +26,7 @@ type ModeConfig = {
const allModes: ModeConfig[] = [
{
id: "select",
- icon: MousePointer2,
+ imageSrc: "/icons/select.png",
label: "Select",
shortcut: "V",
color: "hover:bg-blue-500/20 hover:text-blue-400",
@@ -40,7 +42,7 @@ const allModes: ModeConfig[] = [
},
{
id: "build",
- icon: Hammer,
+ imageSrc: "/icons/build.png",
label: "Build",
shortcut: "B",
color: "hover:bg-green-500/20 hover:text-green-400",
@@ -98,22 +100,39 @@ export function ControlModes() {
{availableModes.map((m) => {
const Icon = m.icon;
const isActive = mode === m.id;
+ const isImageMode = Boolean(m.imageSrc);
return (
diff --git a/apps/editor/components/ui/action-menu/index.tsx b/apps/editor/components/ui/action-menu/index.tsx
index 461ab7d5..c5a14aff 100644
--- a/apps/editor/components/ui/action-menu/index.tsx
+++ b/apps/editor/components/ui/action-menu/index.tsx
@@ -30,7 +30,7 @@ export function ActionMenu({ className }: { className?: string }) {
transition={transition}
className={cn(
"-translate-x-1/2 fixed bottom-6 left-1/2 z-50",
- "rounded-2xl border border-zinc-800 bg-zinc-950/90 shadow-2xl backdrop-blur-md",
+ "rounded-2xl border border-border bg-background/90 shadow-2xl backdrop-blur-md",
"transition-colors duration-200 ease-out",
className,
)}
@@ -40,7 +40,7 @@ export function ActionMenu({ className }: { className?: string }) {
{mode === "build" && tool === "item" && catalogCategory && (
-
+
-
+