diff --git a/apps/editor/components/ui/action-menu/structure-tools.tsx b/apps/editor/components/ui/action-menu/structure-tools.tsx
new file mode 100644
index 00000000..1c3a1c33
--- /dev/null
+++ b/apps/editor/components/ui/action-menu/structure-tools.tsx
@@ -0,0 +1,81 @@
+'use client'
+
+import NextImage from 'next/image'
+import { Button } from '@/components/ui/primitives/button'
+import { Tooltip, TooltipContent, TooltipTrigger, } from '@/components/ui/primitives/tooltip'
+
+import { cn } from '@/lib/utils'
+import useEditor, { CatalogCategory, StructureTool, Tool } from '@/store/use-editor'
+
+export type ToolConfig = {
+ id: StructureTool; iconSrc: string; label: string; catalogCategory?: CatalogCategory }
+
+export const tools: ToolConfig[] = [
+ { id: 'wall', iconSrc: '/icons/wall.png', label: 'Wall' },
+ { id: 'room', iconSrc: '/icons/room.png', label: 'Room' },
+ { id: 'custom-room', iconSrc: '/icons/custom-room.png', label: 'Custom Room' },
+ { id: 'slab', iconSrc: '/icons/floor.png', label: 'Slab' },
+ { id: 'ceiling', iconSrc: '/icons/ceiling.png', label: 'Ceiling' },
+ { id: 'roof', iconSrc: '/icons/roof.png', label: 'Gable Roof' },
+ { id: 'column', iconSrc: '/icons/column.png', label: 'Column' },
+ { id: 'item', iconSrc: '/icons/door.png', label: 'Door', catalogCategory: 'door' },
+ { id: 'item', iconSrc: '/icons/window.png', label: 'Window', catalogCategory: 'window' },
+ { id: 'zone', iconSrc: '/icons/zone.png', label: 'Zone' },
+]
+
+export function StructureTools() {
+ const activeTool = useEditor((state) => state.tool)
+ const catalogCategory = useEditor((state) => state.catalogCategory)
+ const setTool = useEditor((state) => state.setTool)
+ const setCatalogCategory = useEditor((state) => state.setCatalogCategory)
+
+ return (
+
+ {tools.map((tool, index) => {
+ // For item tools with catalog category, check both tool and category match
+ const isActive =
+ activeTool === tool.id &&
+ (tool.catalogCategory ? catalogCategory === tool.catalogCategory : true)
+
+ return (
+
+
+
+
+
+
+ {tool.label}
+ {isActive && ' (Click to deselect)'}
+
+
+
+ )
+ })}
+
+ )
+}
diff --git a/apps/editor/package.json b/apps/editor/package.json
index df2b2dd1..ed65769b 100644
--- a/apps/editor/package.json
+++ b/apps/editor/package.json
@@ -22,6 +22,7 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.562.0",
+ "motion": "^12.26.2",
"next": "16.1.0",
"postcss": "^8.5.6",
"react": "^19.2.0",
diff --git a/apps/editor/public/icons/appliance.png b/apps/editor/public/icons/appliance.png
new file mode 100644
index 00000000..c2bb0bae
Binary files /dev/null and b/apps/editor/public/icons/appliance.png differ
diff --git a/apps/editor/public/icons/bathroom.png b/apps/editor/public/icons/bathroom.png
new file mode 100644
index 00000000..fafec7cc
Binary files /dev/null and b/apps/editor/public/icons/bathroom.png differ
diff --git a/apps/editor/public/icons/building.png b/apps/editor/public/icons/building.png
new file mode 100644
index 00000000..1d377680
Binary files /dev/null and b/apps/editor/public/icons/building.png differ
diff --git a/apps/editor/public/icons/ceiling.png b/apps/editor/public/icons/ceiling.png
new file mode 100644
index 00000000..9e04d639
Binary files /dev/null and b/apps/editor/public/icons/ceiling.png differ
diff --git a/apps/editor/public/icons/collection.png b/apps/editor/public/icons/collection.png
new file mode 100644
index 00000000..32b7ccd0
Binary files /dev/null and b/apps/editor/public/icons/collection.png differ
diff --git a/apps/editor/public/icons/column.png b/apps/editor/public/icons/column.png
new file mode 100644
index 00000000..8e44f6d8
Binary files /dev/null and b/apps/editor/public/icons/column.png differ
diff --git a/apps/editor/public/icons/couch.png b/apps/editor/public/icons/couch.png
new file mode 100644
index 00000000..6509e005
Binary files /dev/null and b/apps/editor/public/icons/couch.png differ
diff --git a/apps/editor/public/icons/cube.png b/apps/editor/public/icons/cube.png
new file mode 100644
index 00000000..c279d41b
Binary files /dev/null and b/apps/editor/public/icons/cube.png differ
diff --git a/apps/editor/public/icons/custom-room.png b/apps/editor/public/icons/custom-room.png
new file mode 100644
index 00000000..14d40459
Binary files /dev/null and b/apps/editor/public/icons/custom-room.png differ
diff --git a/apps/editor/public/icons/door.png b/apps/editor/public/icons/door.png
new file mode 100644
index 00000000..8b045246
Binary files /dev/null and b/apps/editor/public/icons/door.png differ
diff --git a/apps/editor/public/icons/environment.png b/apps/editor/public/icons/environment.png
new file mode 100644
index 00000000..2bc71b11
Binary files /dev/null and b/apps/editor/public/icons/environment.png differ
diff --git a/apps/editor/public/icons/floor.png b/apps/editor/public/icons/floor.png
new file mode 100644
index 00000000..f2ce9f49
Binary files /dev/null and b/apps/editor/public/icons/floor.png differ
diff --git a/apps/editor/public/icons/floorplan.png b/apps/editor/public/icons/floorplan.png
new file mode 100644
index 00000000..456dce36
Binary files /dev/null and b/apps/editor/public/icons/floorplan.png differ
diff --git a/apps/editor/public/icons/item.png b/apps/editor/public/icons/item.png
new file mode 100644
index 00000000..ae3fdc25
Binary files /dev/null and b/apps/editor/public/icons/item.png differ
diff --git a/apps/editor/public/icons/kitchen.png b/apps/editor/public/icons/kitchen.png
new file mode 100644
index 00000000..1bffefe4
Binary files /dev/null and b/apps/editor/public/icons/kitchen.png differ
diff --git a/apps/editor/public/icons/level.png b/apps/editor/public/icons/level.png
new file mode 100644
index 00000000..4b931fd0
Binary files /dev/null and b/apps/editor/public/icons/level.png differ
diff --git a/apps/editor/public/icons/mesh.png b/apps/editor/public/icons/mesh.png
new file mode 100644
index 00000000..ecd7a21e
Binary files /dev/null and b/apps/editor/public/icons/mesh.png differ
diff --git a/apps/editor/public/icons/roof.png b/apps/editor/public/icons/roof.png
new file mode 100644
index 00000000..253c1d76
Binary files /dev/null and b/apps/editor/public/icons/roof.png differ
diff --git a/apps/editor/public/icons/room.png b/apps/editor/public/icons/room.png
new file mode 100644
index 00000000..85a179b4
Binary files /dev/null and b/apps/editor/public/icons/room.png differ
diff --git a/apps/editor/public/icons/site.png b/apps/editor/public/icons/site.png
new file mode 100644
index 00000000..0d37688a
Binary files /dev/null and b/apps/editor/public/icons/site.png differ
diff --git a/apps/editor/public/icons/stairs.png b/apps/editor/public/icons/stairs.png
new file mode 100644
index 00000000..6c9f1a65
Binary files /dev/null and b/apps/editor/public/icons/stairs.png differ
diff --git a/apps/editor/public/icons/tree.png b/apps/editor/public/icons/tree.png
new file mode 100644
index 00000000..ae3fdc25
Binary files /dev/null and b/apps/editor/public/icons/tree.png differ
diff --git a/apps/editor/public/icons/wall.png b/apps/editor/public/icons/wall.png
new file mode 100644
index 00000000..465e3e9d
Binary files /dev/null and b/apps/editor/public/icons/wall.png differ
diff --git a/apps/editor/public/icons/wallcut.png b/apps/editor/public/icons/wallcut.png
new file mode 100644
index 00000000..bedcf614
Binary files /dev/null and b/apps/editor/public/icons/wallcut.png differ
diff --git a/apps/editor/public/icons/walllow.png b/apps/editor/public/icons/walllow.png
new file mode 100644
index 00000000..7ec71abd
Binary files /dev/null and b/apps/editor/public/icons/walllow.png differ
diff --git a/apps/editor/public/icons/window.png b/apps/editor/public/icons/window.png
new file mode 100644
index 00000000..ec62d0a7
Binary files /dev/null and b/apps/editor/public/icons/window.png differ
diff --git a/apps/editor/public/icons/zone.png b/apps/editor/public/icons/zone.png
new file mode 100644
index 00000000..32b7ccd0
Binary files /dev/null and b/apps/editor/public/icons/zone.png differ
diff --git a/apps/editor/store/use-editor.tsx b/apps/editor/store/use-editor.tsx
index ceb439f8..390b468b 100644
--- a/apps/editor/store/use-editor.tsx
+++ b/apps/editor/store/use-editor.tsx
@@ -5,6 +5,8 @@ import { useViewer } from "@pascal-app/viewer";
import { create } from "zustand";
export type Phase = "site" | "structure" | "furnish";
+
+
export type Mode = "select" | "edit" | "delete" | "build";
// Structure mode tools (building elements)
@@ -17,22 +19,28 @@ export type StructureTool =
| "roof"
| "column"
| "stair"
- | "door"
- | "window"
+ | "item"
| "zone";
// Furnish mode tools (items and decoration)
export type FurnishTool =
- | "furniture"
- | "appliance"
- | "kitchen"
- | "bathroom"
- | "outdoor"
- | "painting";
+ | "item"
// Site mode tools
export type SiteTool = "property-line";
+
+// Catalog categories for furnish mode items
+export type CatalogCategory =
+ | 'furniture'
+ | 'appliance'
+ | 'bathroom'
+ | 'kitchen'
+ | 'outdoor'
+ | 'window'
+ | 'door'
+
+
// Combined tool type
export type Tool = SiteTool | StructureTool | FurnishTool;
@@ -43,6 +51,8 @@ type EditorState = {
setMode: (mode: Mode) => void;
tool: Tool | null;
setTool: (tool: Tool | null) => void;
+ catalogCategory: CatalogCategory | null;
+ setCatalogCategory: (category: CatalogCategory | null) => void;
};
const useEditor = create
()((set, get) => ({
@@ -94,6 +104,8 @@ const useEditor = create()((set, get) => ({
setMode: (mode) => set({ mode }),
tool: null,
setTool: (tool) => set({ tool }),
+ catalogCategory: null,
+ setCatalogCategory: (category) => set({ catalogCategory: category }),
}));
export default useEditor;
diff --git a/bun.lock b/bun.lock
index 4f8420b7..8f942691 100644
--- a/bun.lock
+++ b/bun.lock
@@ -31,6 +31,7 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.562.0",
+ "motion": "^12.26.2",
"next": "16.1.0",
"postcss": "^8.5.6",
"react": "^19.2.0",
@@ -625,6 +626,8 @@
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
+ "framer-motion": ["framer-motion@12.26.2", "", { "dependencies": { "motion-dom": "^12.26.2", "motion-utils": "^12.24.10", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-lflOQEdjquUi9sCg5Y1LrsZDlsjrHw7m0T9Yedvnk7Bnhqfkc89/Uha10J3CFhkL+TCZVCRw9eUGyM/lyYhXQA=="],
+
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="],
@@ -817,6 +820,12 @@
"mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
+ "motion": ["motion@12.26.2", "", { "dependencies": { "framer-motion": "^12.26.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-2Q6g0zK1gUJKhGT742DAe42LgietcdiJ3L3OcYAHCQaC1UkLnn6aC8S/obe4CxYTLAgid2asS1QdQ/blYfo5dw=="],
+
+ "motion-dom": ["motion-dom@12.26.2", "", { "dependencies": { "motion-utils": "^12.24.10" } }, "sha512-KLMT1BroY8oKNeliA3JMNJ+nbCIsTKg6hJpDb4jtRAJ7nCKnnpg/LTq/NGqG90Limitz3kdAnAVXecdFVGlWTw=="],
+
+ "motion-utils": ["motion-utils@12.24.10", "", {}, "sha512-x5TFgkCIP4pPsRLpKoI86jv/q8t8FQOiM/0E8QKBzfMozWHfkKap2gA1hOki+B5g3IsBNpxbUnfOum1+dgvYww=="],
+
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="],