@@ -14,6 +14,7 @@ import {
1414 onMount ,
1515 onCleanup ,
1616 Show ,
17+ For ,
1718 lazy ,
1819 Suspense ,
1920} from "solid-js" ;
@@ -25,9 +26,6 @@ import FigmaActivityBar from "./FigmaActivityBar";
2526import FigmaChatPanel , { ChatPanelState , ChatMessage } from "./FigmaChatPanel" ;
2627import FigmaStatusBar from "./FigmaStatusBar" ;
2728
28- // Existing Components
29- import { MenuBar } from "@/components/MenuBar" ;
30-
3129// Existing Contexts
3230import { useEditor } from "@/context/EditorContext" ;
3331import { useSDK } from "@/context/SDKContext" ;
@@ -119,6 +117,301 @@ function SidebarSkeleton() {
119117 ) ;
120118}
121119
120+ // ============================================================================
121+ // Figma Menu Dropdown - Hamburger menu with Figma theme styling
122+ // ============================================================================
123+
124+ interface FigmaMenuDropdownProps {
125+ onClose : ( ) => void ;
126+ }
127+
128+ interface MenuItem {
129+ label : string ;
130+ shortcut ?: string ;
131+ action ?: ( ) => void ;
132+ separator ?: boolean ;
133+ icon ?: string ;
134+ }
135+
136+ function FigmaMenuDropdown ( props : FigmaMenuDropdownProps ) {
137+ const [ activeMenu , setActiveMenu ] = createSignal < string | null > ( null ) ;
138+
139+ // Menu structure matching VS Code/IDE layout
140+ const menus : { label : string ; items : MenuItem [ ] } [ ] = [
141+ {
142+ label : "File" ,
143+ items : [
144+ { label : "New File" , shortcut : "Ctrl+N" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "file:new" ) ) } ,
145+ { label : "New Window" , shortcut : "Ctrl+Shift+N" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "window:new" ) ) } ,
146+ { separator : true , label : "" } ,
147+ { label : "Open File..." , shortcut : "Ctrl+O" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "file:open" ) ) } ,
148+ { label : "Open Folder..." , shortcut : "Ctrl+K Ctrl+O" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "folder:open" ) ) } ,
149+ { separator : true , label : "" } ,
150+ { label : "Save" , shortcut : "Ctrl+S" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "file:save" ) ) } ,
151+ { label : "Save As..." , shortcut : "Ctrl+Shift+S" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "file:save-as" ) ) } ,
152+ { label : "Save All" , shortcut : "Ctrl+K S" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "file:save-all" ) ) } ,
153+ { separator : true , label : "" } ,
154+ { label : "Close" , shortcut : "Ctrl+W" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "file:close" ) ) } ,
155+ { label : "Close Folder" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "folder:close" ) ) } ,
156+ ] ,
157+ } ,
158+ {
159+ label : "Edit" ,
160+ items : [
161+ { label : "Undo" , shortcut : "Ctrl+Z" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "edit:undo" ) ) } ,
162+ { label : "Redo" , shortcut : "Ctrl+Shift+Z" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "edit:redo" ) ) } ,
163+ { separator : true , label : "" } ,
164+ { label : "Cut" , shortcut : "Ctrl+X" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "edit:cut" ) ) } ,
165+ { label : "Copy" , shortcut : "Ctrl+C" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "edit:copy" ) ) } ,
166+ { label : "Paste" , shortcut : "Ctrl+V" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "edit:paste" ) ) } ,
167+ { separator : true , label : "" } ,
168+ { label : "Find" , shortcut : "Ctrl+F" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "edit:find" ) ) } ,
169+ { label : "Replace" , shortcut : "Ctrl+H" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "edit:replace" ) ) } ,
170+ { label : "Find in Files" , shortcut : "Ctrl+Shift+F" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "search:find-in-files" ) ) } ,
171+ ] ,
172+ } ,
173+ {
174+ label : "Selection" ,
175+ items : [
176+ { label : "Select All" , shortcut : "Ctrl+A" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "selection:select-all" ) ) } ,
177+ { label : "Expand Selection" , shortcut : "Shift+Alt+→" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "selection:expand" ) ) } ,
178+ { label : "Shrink Selection" , shortcut : "Shift+Alt+←" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "selection:shrink" ) ) } ,
179+ { separator : true , label : "" } ,
180+ { label : "Copy Line Up" , shortcut : "Shift+Alt+↑" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "selection:copy-line-up" ) ) } ,
181+ { label : "Copy Line Down" , shortcut : "Shift+Alt+↓" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "selection:copy-line-down" ) ) } ,
182+ { label : "Move Line Up" , shortcut : "Alt+↑" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "selection:move-line-up" ) ) } ,
183+ { label : "Move Line Down" , shortcut : "Alt+↓" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "selection:move-line-down" ) ) } ,
184+ ] ,
185+ } ,
186+ {
187+ label : "View" ,
188+ items : [
189+ { label : "Command Palette..." , shortcut : "Ctrl+Shift+P" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "command-palette:open" ) ) } ,
190+ { label : "Quick Open..." , shortcut : "Ctrl+P" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "quick-open:show" ) ) } ,
191+ { separator : true , label : "" } ,
192+ { label : "Explorer" , shortcut : "Ctrl+Shift+E" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "view:explorer" ) ) } ,
193+ { label : "Search" , shortcut : "Ctrl+Shift+F" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "view:search" ) ) } ,
194+ { label : "Source Control" , shortcut : "Ctrl+Shift+G" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "view:git" ) ) } ,
195+ { label : "Extensions" , shortcut : "Ctrl+Shift+X" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "view:extensions" ) ) } ,
196+ { separator : true , label : "" } ,
197+ { label : "Terminal" , shortcut : "Ctrl+`" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "terminal:toggle" ) ) } ,
198+ { label : "Toggle Sidebar" , shortcut : "Ctrl+B" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "sidebar:toggle" ) ) } ,
199+ ] ,
200+ } ,
201+ {
202+ label : "Go" ,
203+ items : [
204+ { label : "Go to File..." , shortcut : "Ctrl+P" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "goto:file" ) ) } ,
205+ { label : "Go to Symbol..." , shortcut : "Ctrl+Shift+O" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "goto:symbol" ) ) } ,
206+ { label : "Go to Line..." , shortcut : "Ctrl+G" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "goto:line" ) ) } ,
207+ { separator : true , label : "" } ,
208+ { label : "Go to Definition" , shortcut : "F12" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "goto:definition" ) ) } ,
209+ { label : "Go to References" , shortcut : "Shift+F12" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "goto:references" ) ) } ,
210+ { separator : true , label : "" } ,
211+ { label : "Go Back" , shortcut : "Alt+←" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "goto:back" ) ) } ,
212+ { label : "Go Forward" , shortcut : "Alt+→" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "goto:forward" ) ) } ,
213+ ] ,
214+ } ,
215+ {
216+ label : "Terminal" ,
217+ items : [
218+ { label : "New Terminal" , shortcut : "Ctrl+Shift+`" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "terminal:new" ) ) } ,
219+ { label : "Split Terminal" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "terminal:split" ) ) } ,
220+ { separator : true , label : "" } ,
221+ { label : "Run Task..." , action : ( ) => window . dispatchEvent ( new CustomEvent ( "task:run" ) ) } ,
222+ { label : "Run Build Task" , shortcut : "Ctrl+Shift+B" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "task:build" ) ) } ,
223+ ] ,
224+ } ,
225+ {
226+ label : "Help" ,
227+ items : [
228+ { label : "Welcome" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "help:welcome" ) ) } ,
229+ { label : "Documentation" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "help:docs" ) ) } ,
230+ { label : "Release Notes" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "help:release-notes" ) ) } ,
231+ { separator : true , label : "" } ,
232+ { label : "Keyboard Shortcuts" , shortcut : "Ctrl+K Ctrl+S" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "help:keybindings" ) ) } ,
233+ { separator : true , label : "" } ,
234+ { label : "About" , action : ( ) => window . dispatchEvent ( new CustomEvent ( "help:about" ) ) } ,
235+ ] ,
236+ } ,
237+ ] ;
238+
239+ const handleMenuClick = ( label : string ) => {
240+ if ( activeMenu ( ) === label ) {
241+ setActiveMenu ( null ) ;
242+ } else {
243+ setActiveMenu ( label ) ;
244+ }
245+ } ;
246+
247+ const handleItemClick = ( item : MenuItem ) => {
248+ if ( item . action ) {
249+ item . action ( ) ;
250+ props . onClose ( ) ;
251+ }
252+ } ;
253+
254+ return (
255+ < >
256+ { /* Backdrop to close menu */ }
257+ < div
258+ style = { {
259+ position : "fixed" ,
260+ top : "0" ,
261+ left : "0" ,
262+ right : "0" ,
263+ bottom : "0" ,
264+ "z-index" : "2400" ,
265+ } }
266+ onClick = { props . onClose }
267+ />
268+
269+ { /* Menu Container - styled with Figma theme */ }
270+ < div
271+ style = { {
272+ position : "fixed" ,
273+ top : "53px" ,
274+ left : "143px" , // Aligned with hamburger menu position
275+ "z-index" : "2500" ,
276+ display : "flex" ,
277+ "align-items" : "flex-start" ,
278+ gap : "2px" ,
279+ background : "var(--figma-bg-secondary, #1A1A1A)" ,
280+ "border-radius" : "var(--figma-radius-md, 8px)" ,
281+ border : "1px solid var(--figma-border-default, rgba(255,255,255,0.1))" ,
282+ padding : "6px" ,
283+ "box-shadow" : "0 4px 24px rgba(0,0,0,0.5)" ,
284+ } }
285+ onClick = { ( e ) => e . stopPropagation ( ) }
286+ >
287+ { /* Menu Bar Items */ }
288+ < For each = { menus } >
289+ { ( menu ) => (
290+ < div style = { { position : "relative" } } >
291+ < button
292+ onClick = { ( ) => handleMenuClick ( menu . label ) }
293+ style = { {
294+ height : "28px" ,
295+ padding : "0 12px" ,
296+ "font-size" : "13px" ,
297+ "font-family" : "var(--figma-font-sans, Inter, sans-serif)" ,
298+ "font-weight" : "400" ,
299+ color : activeMenu ( ) === menu . label
300+ ? "var(--figma-accent-primary, #BFFF00)"
301+ : "var(--figma-text-secondary, #A0A0A0)" ,
302+ background : activeMenu ( ) === menu . label
303+ ? "rgba(191, 255, 0, 0.1)"
304+ : "transparent" ,
305+ border : "none" ,
306+ "border-radius" : "var(--figma-radius-sm, 6px)" ,
307+ cursor : "pointer" ,
308+ transition : "all 100ms ease" ,
309+ "white-space" : "nowrap" ,
310+ } }
311+ onMouseEnter = { ( e ) => {
312+ if ( activeMenu ( ) !== menu . label ) {
313+ ( e . currentTarget as HTMLElement ) . style . color = "var(--figma-text-primary, #FFFFFF)" ;
314+ ( e . currentTarget as HTMLElement ) . style . background = "rgba(255,255,255,0.05)" ;
315+ }
316+ // If any menu is open, switch to this one on hover
317+ if ( activeMenu ( ) !== null ) {
318+ setActiveMenu ( menu . label ) ;
319+ }
320+ } }
321+ onMouseLeave = { ( e ) => {
322+ if ( activeMenu ( ) !== menu . label ) {
323+ ( e . currentTarget as HTMLElement ) . style . color = "var(--figma-text-secondary, #A0A0A0)" ;
324+ ( e . currentTarget as HTMLElement ) . style . background = "transparent" ;
325+ }
326+ } }
327+ >
328+ { menu . label }
329+ </ button >
330+
331+ { /* Dropdown Menu */ }
332+ < Show when = { activeMenu ( ) === menu . label } >
333+ < div
334+ style = { {
335+ position : "absolute" ,
336+ top : "calc(100% + 4px)" ,
337+ left : "0" ,
338+ "min-width" : "220px" ,
339+ background : "var(--figma-bg-secondary, #1A1A1A)" ,
340+ "border-radius" : "var(--figma-radius-md, 8px)" ,
341+ border : "1px solid var(--figma-border-default, rgba(255,255,255,0.1))" ,
342+ padding : "6px 0" ,
343+ "box-shadow" : "0 8px 32px rgba(0,0,0,0.5)" ,
344+ "z-index" : "1" ,
345+ } }
346+ >
347+ < For each = { menu . items } >
348+ { ( item ) => (
349+ < Show
350+ when = { ! item . separator }
351+ fallback = {
352+ < div
353+ style = { {
354+ height : "1px" ,
355+ background : "var(--figma-border-default, rgba(255,255,255,0.1))" ,
356+ margin : "6px 0" ,
357+ } }
358+ />
359+ }
360+ >
361+ < button
362+ onClick = { ( ) => handleItemClick ( item ) }
363+ style = { {
364+ width : "100%" ,
365+ display : "flex" ,
366+ "align-items" : "center" ,
367+ "justify-content" : "space-between" ,
368+ height : "28px" ,
369+ padding : "0 12px" ,
370+ "font-size" : "13px" ,
371+ "font-family" : "var(--figma-font-sans, Inter, sans-serif)" ,
372+ "font-weight" : "400" ,
373+ color : "var(--figma-text-secondary, #A0A0A0)" ,
374+ background : "transparent" ,
375+ border : "none" ,
376+ cursor : "pointer" ,
377+ transition : "all 100ms ease" ,
378+ "text-align" : "left" ,
379+ } }
380+ onMouseEnter = { ( e ) => {
381+ ( e . currentTarget as HTMLElement ) . style . color = "var(--figma-text-primary, #FFFFFF)" ;
382+ ( e . currentTarget as HTMLElement ) . style . background = "rgba(191, 255, 0, 0.1)" ;
383+ } }
384+ onMouseLeave = { ( e ) => {
385+ ( e . currentTarget as HTMLElement ) . style . color = "var(--figma-text-secondary, #A0A0A0)" ;
386+ ( e . currentTarget as HTMLElement ) . style . background = "transparent" ;
387+ } }
388+ >
389+ < span > { item . label } </ span >
390+ < Show when = { item . shortcut } >
391+ < span
392+ style = { {
393+ "font-size" : "11px" ,
394+ color : "var(--figma-text-muted, #808080)" ,
395+ "font-family" : "ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco, Consolas, monospace" ,
396+ } }
397+ >
398+ { item . shortcut }
399+ </ span >
400+ </ Show >
401+ </ button >
402+ </ Show >
403+ ) }
404+ </ For >
405+ </ div >
406+ </ Show >
407+ </ div >
408+ ) }
409+ </ For >
410+ </ div >
411+ </ >
412+ ) ;
413+ }
414+
122415// ============================================================================
123416// Main Layout Component
124417// ============================================================================
@@ -346,36 +639,9 @@ export function FigmaDesktopLayout(props: ParentProps) {
346639 onMenuClick = { ( ) => setIsMenuOpen ( ! isMenuOpen ( ) ) }
347640 />
348641
349- { /* MenuBar - Shows when hamburger menu is clicked */ }
642+ { /* Figma-styled Menu Dropdown - Shows when hamburger menu is clicked */ }
350643 < Show when = { isMenuOpen ( ) } >
351- < div
352- style = { {
353- position : "fixed" ,
354- top : "53px" ,
355- left : "179px" , // Aligned with breadcrumbs
356- "z-index" : "2500" ,
357- background : "#1A1A1A" ,
358- "border-radius" : "8px" ,
359- border : "1px solid rgba(255,255,255,0.1)" ,
360- padding : "8px 0" ,
361- "box-shadow" : "0 4px 16px rgba(0,0,0,0.4)" ,
362- } }
363- onClick = { ( e ) => e . stopPropagation ( ) }
364- >
365- < MenuBar />
366- </ div >
367- { /* Backdrop to close menu */ }
368- < div
369- style = { {
370- position : "fixed" ,
371- top : "0" ,
372- left : "0" ,
373- right : "0" ,
374- bottom : "0" ,
375- "z-index" : "2400" ,
376- } }
377- onClick = { ( ) => setIsMenuOpen ( false ) }
378- />
644+ < FigmaMenuDropdown onClose = { ( ) => setIsMenuOpen ( false ) } />
379645 </ Show >
380646
381647 { /* Main Content Area - Activity Bar + Content + StatusBar always visible */ }
0 commit comments