figma-mcp-cli 1.0.6 → 1.0.7

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.
Files changed (43) hide show
  1. package/askCopilot.js +51 -0
  2. package/index.js +10 -3
  3. package/my-new-project/.env +1 -1
  4. package/my-new-project/.vscode/settings.json +1 -1
  5. package/my-new-project/my-app/.env +1 -0
  6. package/my-new-project/my-app/README.md +73 -0
  7. package/my-new-project/my-app/eslint.config.js +23 -0
  8. package/my-new-project/my-app/index.html +13 -0
  9. package/my-new-project/my-app/package-lock.json +3212 -0
  10. package/my-new-project/my-app/package.json +30 -0
  11. package/my-new-project/my-app/public/vite.svg +1 -0
  12. package/my-new-project/my-app/src/App.css +42 -0
  13. package/my-new-project/my-app/src/App.tsx +28 -0
  14. package/my-new-project/my-app/src/FigmaSidebarComponent.css +99 -0
  15. package/my-new-project/my-app/src/FigmaSidebarComponent.tsx +111 -0
  16. package/my-new-project/my-app/src/React19AllFeaturesDemo.jsx +111 -0
  17. package/my-new-project/my-app/src/RenderRightSidebar.css +32 -0
  18. package/my-new-project/my-app/src/RenderRightSidebar.tsx +73 -0
  19. package/my-new-project/my-app/src/assets/react.svg +1 -0
  20. package/my-new-project/my-app/src/components/AccordionSection.tsx +33 -0
  21. package/my-new-project/my-app/src/components/ChevronIcon.tsx +24 -0
  22. package/my-new-project/my-app/src/components/EnvironmentScenePanel.tsx +57 -0
  23. package/my-new-project/my-app/src/components/LightingPanel.tsx +57 -0
  24. package/my-new-project/my-app/src/components/RenderScalePanel.tsx +34 -0
  25. package/my-new-project/my-app/src/components/ResolutionPanel.tsx +27 -0
  26. package/my-new-project/my-app/src/components/SaturationPanel.tsx +27 -0
  27. package/my-new-project/my-app/src/components/SidebarButton.tsx +27 -0
  28. package/my-new-project/my-app/src/components/SidebarPreview.tsx +77 -0
  29. package/my-new-project/my-app/src/index.css +68 -0
  30. package/my-new-project/my-app/src/main.tsx +10 -0
  31. package/my-new-project/my-app/src/styles/AccordionSection.css +70 -0
  32. package/my-new-project/my-app/src/styles/EnvironmentScenePanel.css +104 -0
  33. package/my-new-project/my-app/src/styles/LightingPanel.css +111 -0
  34. package/my-new-project/my-app/src/styles/RenderScalePanel.css +67 -0
  35. package/my-new-project/my-app/src/styles/ResolutionPanel.css +60 -0
  36. package/my-new-project/my-app/src/styles/SaturationPanel.css +56 -0
  37. package/my-new-project/my-app/src/styles/SidebarButton.css +81 -0
  38. package/my-new-project/my-app/src/styles/SidebarPreview.css +92 -0
  39. package/my-new-project/my-app/tsconfig.app.json +28 -0
  40. package/my-new-project/my-app/tsconfig.json +7 -0
  41. package/my-new-project/my-app/tsconfig.node.json +26 -0
  42. package/my-new-project/my-app/vite.config.ts +7 -0
  43. package/package.json +2 -1
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "my-app",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc -b && vite build",
9
+ "lint": "eslint .",
10
+ "preview": "vite preview"
11
+ },
12
+ "dependencies": {
13
+ "react": "^19.2.0",
14
+ "react-dom": "^19.2.0"
15
+ },
16
+ "devDependencies": {
17
+ "@eslint/js": "^9.39.1",
18
+ "@types/node": "^24.10.1",
19
+ "@types/react": "^19.2.5",
20
+ "@types/react-dom": "^19.2.3",
21
+ "@vitejs/plugin-react": "^5.1.1",
22
+ "eslint": "^9.39.1",
23
+ "eslint-plugin-react-hooks": "^7.0.1",
24
+ "eslint-plugin-react-refresh": "^0.4.24",
25
+ "globals": "^16.5.0",
26
+ "typescript": "~5.9.3",
27
+ "typescript-eslint": "^8.46.4",
28
+ "vite": "^7.2.4"
29
+ }
30
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
@@ -0,0 +1,42 @@
1
+ #root {
2
+ max-width: 1280px;
3
+ margin: 0 auto;
4
+ padding: 2rem;
5
+ text-align: center;
6
+ }
7
+
8
+ .logo {
9
+ height: 6em;
10
+ padding: 1.5em;
11
+ will-change: filter;
12
+ transition: filter 300ms;
13
+ }
14
+ .logo:hover {
15
+ filter: drop-shadow(0 0 2em #646cffaa);
16
+ }
17
+ .logo.react:hover {
18
+ filter: drop-shadow(0 0 2em #61dafbaa);
19
+ }
20
+
21
+ @keyframes logo-spin {
22
+ from {
23
+ transform: rotate(0deg);
24
+ }
25
+ to {
26
+ transform: rotate(360deg);
27
+ }
28
+ }
29
+
30
+ @media (prefers-reduced-motion: no-preference) {
31
+ a:nth-of-type(2) .logo {
32
+ animation: logo-spin infinite 20s linear;
33
+ }
34
+ }
35
+
36
+ .card {
37
+ padding: 2em;
38
+ }
39
+
40
+ .read-the-docs {
41
+ color: #888;
42
+ }
@@ -0,0 +1,28 @@
1
+ import { useState } from "react";
2
+ import "./App.css";
3
+ import FigmaSidebarComponent from "./FigmaSidebarComponent.tsx";
4
+ import RenderRightSidebar from "./RenderRightSidebar";
5
+ function App() {
6
+ const [count, setCount] = useState(0);
7
+
8
+ return (
9
+ <>
10
+ <RenderRightSidebar />
11
+ {/* <FigmaSidebarComponent /> */}
12
+ <h1>Vite + React</h1>
13
+ <div className="card">
14
+ <button onClick={() => setCount((count) => count + 1)}>
15
+ count is {count}
16
+ </button>
17
+ <p>
18
+ Edit <code>src/App.tsx</code> and save to test HMR
19
+ </p>
20
+ </div>
21
+ <p className="read-the-docs">
22
+ Click on the Vite and React logos to learn more
23
+ </p>
24
+ </>
25
+ );
26
+ }
27
+
28
+ export default App;
@@ -0,0 +1,99 @@
1
+ .figma-sidebar-container {
2
+ display: flex;
3
+ width: 100%;
4
+ height: 100vh;
5
+ background: #f5f5f5;
6
+ font-family: 'Noto IKEA Latin', 'Noto IKEA Simplified Chinese', sans-serif;
7
+ }
8
+
9
+ .figma-sidebar {
10
+ position: relative;
11
+ width: 76px;
12
+ height: 100%;
13
+ background: white;
14
+ border-right: 1px solid #e0e0e0;
15
+ display: flex;
16
+ flex-direction: column;
17
+ align-items: center;
18
+ padding: 58px 8px 16px 8px;
19
+ box-sizing: border-box;
20
+ transition: width 0.3s ease;
21
+ overflow-y: auto;
22
+ z-index: 10;
23
+ }
24
+
25
+ .figma-sidebar.expanded {
26
+ width: 120px;
27
+ }
28
+
29
+ .sidebar-background {
30
+ position: absolute;
31
+ left: 0;
32
+ top: 58px;
33
+ width: 76px;
34
+ height: 842px;
35
+ background: linear-gradient(to bottom, #f5f5f5 0%, #ffffff 100%);
36
+ z-index: 0;
37
+ }
38
+
39
+ .sidebar-buttons {
40
+ display: flex;
41
+ flex-direction: column;
42
+ gap: 12px;
43
+ z-index: 1;
44
+ position: relative;
45
+ }
46
+
47
+ .sidebar-toggle {
48
+ position: absolute;
49
+ bottom: 16px;
50
+ left: 8px;
51
+ width: 60px;
52
+ height: 40px;
53
+ background: #f5f5f5;
54
+ border: 1px solid #e0e0e0;
55
+ border-radius: 4px;
56
+ cursor: pointer;
57
+ font-size: 16px;
58
+ display: flex;
59
+ align-items: center;
60
+ justify-content: center;
61
+ transition: all 0.3s ease;
62
+ color: #111;
63
+ }
64
+
65
+ .sidebar-toggle:hover {
66
+ background: #ffffff;
67
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
68
+ }
69
+
70
+ .sidebar-main-content {
71
+ flex: 1;
72
+ display: flex;
73
+ align-items: center;
74
+ justify-content: center;
75
+ padding: 32px;
76
+ overflow: auto;
77
+ }
78
+
79
+ /* 响应式设计 */
80
+ @media (max-width: 768px) {
81
+ .figma-sidebar {
82
+ width: 60px;
83
+ padding: 40px 4px 8px 4px;
84
+ }
85
+
86
+ .sidebar-background {
87
+ width: 60px;
88
+ }
89
+
90
+ .sidebar-buttons {
91
+ gap: 8px;
92
+ }
93
+
94
+ .sidebar-toggle {
95
+ width: 50px;
96
+ height: 36px;
97
+ font-size: 14px;
98
+ }
99
+ }
@@ -0,0 +1,111 @@
1
+ import React, { useState } from "react";
2
+ import "./FigmaSidebarComponent.css";
3
+ import SidebarButton from "./components/SidebarButton";
4
+ import SidebarPreview from "./components/SidebarPreview.tsx";
5
+
6
+ const img2022120818101 =
7
+ "https://www.figma.com/api/mcp/asset/15899701-5b58-4ebf-aebd-ecf1777bf9a9";
8
+
9
+ interface SidebarItem {
10
+ id: string;
11
+ icon: React.ReactNode;
12
+ label: string;
13
+ onClick: () => void;
14
+ }
15
+
16
+ export default function FigmaSidebarComponent() {
17
+ const [activeItem, setActiveItem] = useState<string>("draw-plan");
18
+ const [isExpanded, setIsExpanded] = useState(true);
19
+
20
+ const sidebarItems: SidebarItem[] = [
21
+ {
22
+ id: "draw-plan",
23
+ icon: "✏️",
24
+ label: "Draw\nPlan",
25
+ onClick: () => setActiveItem("draw-plan"),
26
+ },
27
+ {
28
+ id: "furniture-model",
29
+ icon: "📦",
30
+ label: "Furniture\nModel",
31
+ onClick: () => setActiveItem("furniture-model"),
32
+ },
33
+ {
34
+ id: "generic-model",
35
+ icon: "⬜",
36
+ label: "Generic\nModel",
37
+ onClick: () => setActiveItem("generic-model"),
38
+ },
39
+ {
40
+ id: "idea",
41
+ icon: "⭐",
42
+ label: "Idea",
43
+ onClick: () => setActiveItem("idea"),
44
+ },
45
+ {
46
+ id: "sharing-idea",
47
+ icon: "✨",
48
+ label: "Sharing\nIdea",
49
+ onClick: () => setActiveItem("sharing-idea"),
50
+ },
51
+ {
52
+ id: "function-bubble",
53
+ icon: "👥",
54
+ label: "Function\nBubble",
55
+ onClick: () => setActiveItem("function-bubble"),
56
+ },
57
+ {
58
+ id: "upload",
59
+ icon: "📤",
60
+ label: "Upload",
61
+ onClick: () => setActiveItem("upload"),
62
+ },
63
+ {
64
+ id: "showroom",
65
+ icon: "🏛️",
66
+ label: "Showroom",
67
+ onClick: () => setActiveItem("showroom"),
68
+ },
69
+ ];
70
+
71
+ return (
72
+ <div className="figma-sidebar-container">
73
+ <aside
74
+ className={`figma-sidebar ${isExpanded ? "expanded" : "collapsed"}`}
75
+ >
76
+ {/* 侧边栏背景 */}
77
+ <div className="sidebar-background" />
78
+
79
+ {/* 按钮列表 */}
80
+ <div className="sidebar-buttons">
81
+ {sidebarItems.map((item) => (
82
+ <SidebarButton
83
+ key={item.id}
84
+ icon={item.icon}
85
+ label={item.label}
86
+ isActive={activeItem === item.id}
87
+ onClick={item.onClick}
88
+ />
89
+ ))}
90
+ </div>
91
+
92
+ {/* 切换侧边栏按钮 */}
93
+ <button
94
+ className="sidebar-toggle"
95
+ onClick={() => setIsExpanded(!isExpanded)}
96
+ title={isExpanded ? "Collapse sidebar" : "Expand sidebar"}
97
+ >
98
+ {isExpanded ? "◄" : "►"}
99
+ </button>
100
+ </aside>
101
+
102
+ {/* 预览区域 */}
103
+ <main className="sidebar-main-content">
104
+ <SidebarPreview
105
+ activeItem={activeItem}
106
+ previewImage={img2022120818101}
107
+ />
108
+ </main>
109
+ </div>
110
+ );
111
+ }
@@ -0,0 +1,111 @@
1
+ import React, {
2
+ useState,
3
+ useEffect,
4
+ useOptimistic,
5
+ use,
6
+ useRef,
7
+ useCallback,
8
+ useTransition,
9
+ } from "react";
10
+
11
+ // React 19 新特性综合演示
12
+ export default function React19AllFeaturesDemo() {
13
+ // 1. useOptimistic
14
+ const [messages, setMessages] = useState(["Hello"]);
15
+ const [optimisticMessages, addOptimisticMessage] = useOptimistic(
16
+ messages,
17
+ (state, newMsg) => [...state, newMsg]
18
+ );
19
+
20
+ // 2. useEffect 支持同步清理
21
+ useEffect(() => {
22
+ document.title = "React 19 Demo";
23
+ return () => {
24
+ document.title = "";
25
+ };
26
+ }, []);
27
+
28
+ // 3. useTransition 新增 API
29
+ const [isPending, startTransition] = useTransition();
30
+ const [count, setCount] = useState(0);
31
+ const handleTransition = () => {
32
+ startTransition(() => {
33
+ setCount((c) => c + 1);
34
+ });
35
+ };
36
+
37
+ // 4. use 新钩子(Suspense for Data Fetching)
38
+ // 这里只做静态演示,实际需配合 Suspense
39
+ function fetchData() {
40
+ return new Promise((resolve) =>
41
+ setTimeout(() => resolve("数据加载完成"), 1000)
42
+ );
43
+ }
44
+ // 伪代码演示 use
45
+ // const data = use(fetchData());
46
+
47
+ // 5. 新事件系统(PointerDown 冒泡)
48
+ function handlePointerDown() {
49
+ alert("PointerDown 事件冒泡!");
50
+ }
51
+
52
+ // 6. useRef 支持回调 ref
53
+ const cbRef = useCallback((node) => {
54
+ if (node) node.style.background = "#e0f7fa";
55
+ }, []);
56
+
57
+ return (
58
+ <div className="p-6 max-w-md mx-auto bg-white rounded-xl shadow-md space-y-4">
59
+ <h2 className="text-xl font-bold">React 19 全新特性 Demo</h2>
60
+ <div>
61
+ <span className="font-semibold">useOptimistic:</span>
62
+ <ul className="list-disc ml-6">
63
+ {optimisticMessages.map((msg, idx) => (
64
+ <li key={idx}>{msg}</li>
65
+ ))}
66
+ </ul>
67
+ <button
68
+ className="mt-2 px-4 py-1 bg-blue-500 text-white rounded"
69
+ onClick={() =>
70
+ addOptimisticMessage("新消息" + Math.random().toFixed(2))
71
+ }
72
+ >
73
+ 添加乐观消息
74
+ </button>
75
+ </div>
76
+ <div
77
+ onPointerDown={handlePointerDown}
78
+ className="p-2 border cursor-pointer"
79
+ >
80
+ PointerDown 事件测试(React 19 支持冒泡)
81
+ </div>
82
+ <div>
83
+ <span className="font-semibold">useEffect:</span>{" "}
84
+ 页面标题会随组件挂载/卸载变化
85
+ </div>
86
+ <div>
87
+ <span className="font-semibold">useTransition:</span>
88
+ <button
89
+ className="ml-2 px-4 py-1 bg-green-500 text-white rounded"
90
+ onClick={handleTransition}
91
+ >
92
+ +1 (Transition)
93
+ </button>
94
+ <span className="ml-2">
95
+ 计数:{count}{" "}
96
+ {isPending && <span className="text-gray-400">(pending...)</span>}
97
+ </span>
98
+ </div>
99
+ <div>
100
+ <span className="font-semibold">useRef 回调:</span>
101
+ <div ref={cbRef} className="p-2 border">
102
+ 回调 ref 设置背景色
103
+ </div>
104
+ </div>
105
+ <div>
106
+ <span className="font-semibold">use:</span> Suspense for Data
107
+ Fetching(需配合 Suspense,见源码注释)
108
+ </div>
109
+ </div>
110
+ );
111
+ }
@@ -0,0 +1,32 @@
1
+ .render-right-sidebar {
2
+ width: 100%;
3
+ max-width: 280px;
4
+ background: white;
5
+ border-radius: 8px;
6
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
7
+ padding: 16px 0;
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: 16px;
11
+ font-family: 'Noto IKEA Latin', 'Noto IKEA Simplified Chinese', sans-serif;
12
+ }
13
+
14
+ .sidebar-section-content {
15
+ display: flex;
16
+ flex-direction: column;
17
+ gap: 16px;
18
+ padding: 0 16px;
19
+ }
20
+
21
+ .sidebar-divider {
22
+ height: 1px;
23
+ background: #f5f5f5;
24
+ margin: 0 16px;
25
+ }
26
+
27
+ /* Responsive */
28
+ @media (max-width: 768px) {
29
+ .render-right-sidebar {
30
+ max-width: 100%;
31
+ }
32
+ }
@@ -0,0 +1,73 @@
1
+ import React, { useState } from "react";
2
+ import "./RenderRightSidebar.css";
3
+ import AccordionSection from "./components/AccordionSection.tsx";
4
+ import ResolutionPanel from "./components/ResolutionPanel.tsx";
5
+ import RenderScalePanel from "./components/RenderScalePanel.tsx";
6
+ import SaturationPanel from "./components/SaturationPanel.tsx";
7
+ import LightingPanel from "./components/LightingPanel.tsx";
8
+ import EnvironmentScenePanel from "./components/EnvironmentScenePanel.tsx";
9
+
10
+ export default function RenderRightSidebar() {
11
+ const [expandedSections, setExpandedSections] = useState<
12
+ Record<string, boolean>
13
+ >({
14
+ rendering: true,
15
+ lighting: true,
16
+ environment: true,
17
+ });
18
+
19
+ const toggleSection = (sectionId: string) => {
20
+ setExpandedSections((prev) => ({
21
+ ...prev,
22
+ [sectionId]: !prev[sectionId],
23
+ }));
24
+ };
25
+
26
+ return (
27
+ <div className="render-right-sidebar">
28
+ {/* Rendering Parameters */}
29
+ <AccordionSection
30
+ title="Rendering Parameters"
31
+ sectionId="rendering"
32
+ isExpanded={expandedSections.rendering}
33
+ onToggle={toggleSection}
34
+ >
35
+ <div className="sidebar-section-content">
36
+ <ResolutionPanel />
37
+ <RenderScalePanel />
38
+ <SaturationPanel />
39
+ </div>
40
+ </AccordionSection>
41
+
42
+ {/* Divider */}
43
+ <div className="sidebar-divider" />
44
+
45
+ {/* Lighting */}
46
+ <AccordionSection
47
+ title="Lighting"
48
+ sectionId="lighting"
49
+ isExpanded={expandedSections.lighting}
50
+ onToggle={toggleSection}
51
+ >
52
+ <div className="sidebar-section-content">
53
+ <LightingPanel />
54
+ </div>
55
+ </AccordionSection>
56
+
57
+ {/* Divider */}
58
+ <div className="sidebar-divider" />
59
+
60
+ {/* Environment Scene */}
61
+ <AccordionSection
62
+ title="Environment scene"
63
+ sectionId="environment"
64
+ isExpanded={expandedSections.environment}
65
+ onToggle={toggleSection}
66
+ >
67
+ <div className="sidebar-section-content">
68
+ <EnvironmentScenePanel />
69
+ </div>
70
+ </AccordionSection>
71
+ </div>
72
+ );
73
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
@@ -0,0 +1,33 @@
1
+ import React from "react";
2
+ import "../styles/AccordionSection.css";
3
+ import ChevronIcon from "./ChevronIcon";
4
+
5
+ interface AccordionSectionProps {
6
+ title: string;
7
+ sectionId: string;
8
+ isExpanded: boolean;
9
+ onToggle: (sectionId: string) => void;
10
+ children: React.ReactNode;
11
+ }
12
+
13
+ export default function AccordionSection({
14
+ title,
15
+ sectionId,
16
+ isExpanded,
17
+ onToggle,
18
+ children,
19
+ }: AccordionSectionProps) {
20
+ return (
21
+ <div className="accordion-section">
22
+ <button
23
+ className="accordion-header"
24
+ onClick={() => onToggle(sectionId)}
25
+ aria-expanded={isExpanded}
26
+ >
27
+ <div className="accordion-title">{title}</div>
28
+ <ChevronIcon isExpanded={isExpanded} />
29
+ </button>
30
+ {isExpanded && <div className="accordion-content">{children}</div>}
31
+ </div>
32
+ );
33
+ }
@@ -0,0 +1,24 @@
1
+ import React from "react";
2
+
3
+ interface ChevronIconProps {
4
+ isExpanded: boolean;
5
+ }
6
+
7
+ export default function ChevronIcon({ isExpanded }: ChevronIconProps) {
8
+ return (
9
+ <div
10
+ className="chevron-icon"
11
+ style={{ transform: isExpanded ? "rotate(0deg)" : "rotate(180deg)" }}
12
+ >
13
+ <svg width="12" height="7" viewBox="0 0 12 7" fill="none">
14
+ <path
15
+ d="M1 1L6 6L11 1"
16
+ stroke="#111"
17
+ strokeWidth="1.5"
18
+ strokeLinecap="round"
19
+ strokeLinejoin="round"
20
+ />
21
+ </svg>
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,57 @@
1
+ import React, { useState } from "react";
2
+ import "../styles/EnvironmentScenePanel.css";
3
+
4
+ interface Scene {
5
+ id: string;
6
+ name: string;
7
+ image: string;
8
+ isSelected?: boolean;
9
+ }
10
+
11
+ export default function EnvironmentScenePanel() {
12
+ const [scenes, setScenes] = useState<Scene[]>([
13
+ { id: "1", name: "Scene 1", image: "🏞️", isSelected: true },
14
+ { id: "2", name: "Scene 2", image: "🏖️", isSelected: false },
15
+ { id: "3", name: "Scene 2", image: "🌳", isSelected: false },
16
+ { id: "4", name: "Scene 2", image: "🏠", isSelected: false },
17
+ { id: "5", name: "Scene 2", image: "🌲", isSelected: false },
18
+ { id: "6", name: "Scene 2", image: "🌇", isSelected: false },
19
+ { id: "7", name: "Scene 2", image: "🏡", isSelected: false },
20
+ { id: "8", name: "White", image: "⚪", isSelected: false },
21
+ ]);
22
+
23
+ const toggleScene = (sceneId: string) => {
24
+ setScenes(
25
+ scenes.map((scene) =>
26
+ scene.id === sceneId
27
+ ? { ...scene, isSelected: !scene.isSelected }
28
+ : { ...scene, isSelected: false }
29
+ )
30
+ );
31
+ };
32
+
33
+ const uploadScene = () => {
34
+ console.log("Upload scene clicked");
35
+ };
36
+
37
+ return (
38
+ <div className="environment-scene-panel">
39
+ <div className="scenes-grid">
40
+ {scenes.map((scene) => (
41
+ <button
42
+ key={scene.id}
43
+ className={`scene-card ${scene.isSelected ? "selected" : ""}`}
44
+ onClick={() => toggleScene(scene.id)}
45
+ >
46
+ <div className="scene-image">{scene.image}</div>
47
+ <div className="scene-name">{scene.name}</div>
48
+ </button>
49
+ ))}
50
+ </div>
51
+ <button className="upload-scene-btn" onClick={uploadScene}>
52
+ <span className="plus-icon">+</span>
53
+ Upload Scene
54
+ </button>
55
+ </div>
56
+ );
57
+ }