-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Simulating Onlook -> Agent implementation #3049
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,18 @@ | |
|
|
||
| import { useEditorEngine } from '@/components/store/editor'; | ||
| import { EditorAttributes } from '@onlook/constants'; | ||
| import { EditorMode } from '@onlook/models'; | ||
| import { EditorMode, LeftPanelTabValue } from '@onlook/models'; | ||
| import { | ||
| ContextMenu, | ||
| ContextMenuContent, | ||
| ContextMenuItem, | ||
| ContextMenuSeparator, | ||
| ContextMenuSub, | ||
| ContextMenuSubContent, | ||
| ContextMenuSubTrigger, | ||
| ContextMenuTrigger, | ||
| } from '@onlook/ui/context-menu'; | ||
| import { Icons } from '@onlook/ui/icons'; | ||
| import { throttle } from 'lodash'; | ||
| import { observer } from 'mobx-react-lite'; | ||
| import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; | ||
|
|
@@ -23,7 +34,11 @@ const MAX_Y = 10000; | |
| const MIN_X = -5000; | ||
| const MIN_Y = -5000; | ||
|
|
||
| export const Canvas = observer(() => { | ||
| interface CanvasProps { | ||
| onSidebarClick?: (tab: LeftPanelTabValue) => void; | ||
| } | ||
|
|
||
| export const Canvas = observer(({ onSidebarClick }: CanvasProps) => { | ||
| const editorEngine = useEditorEngine(); | ||
| const containerRef = useRef<HTMLDivElement>(null); | ||
| const scale = editorEngine.canvas.scale; | ||
|
|
@@ -272,41 +287,127 @@ export const Canvas = observer(() => { | |
|
|
||
| return ( | ||
| <HotkeysArea> | ||
| <div | ||
| ref={containerRef} | ||
| className="overflow-hidden bg-background-onlook flex flex-grow relative" | ||
| onMouseDown={handleCanvasMouseDown} | ||
| onMouseMove={handleCanvasMouseMove} | ||
| onMouseUp={handleCanvasMouseUp} | ||
| onMouseLeave={(e) => { | ||
| // Only terminate drag if no mouse button is pressed | ||
| // Note: The global mouseup listener will handle the actual cleanup | ||
| // This is just an additional safety check for when mouse leaves without buttons pressed | ||
| if (e.buttons === 0 && isDragSelecting) { | ||
| setIsDragSelecting(false); | ||
| setFramesInSelection(new Set()); | ||
| editorEngine.state.isDragSelecting = false; | ||
| } | ||
| }} | ||
| > | ||
| <div id={EditorAttributes.CANVAS_CONTAINER_ID} style={transformStyle}> | ||
| <Frames framesInDragSelection={framesInSelection} /> | ||
| </div> | ||
| <RecenterCanvasButton /> | ||
| <DragSelectOverlay | ||
| startX={dragSelectStart.x} | ||
| startY={dragSelectStart.y} | ||
| endX={dragSelectEnd.x} | ||
| endY={dragSelectEnd.y} | ||
| isSelecting={isDragSelecting} | ||
| /> | ||
| <Overlay /> | ||
| <PanOverlay | ||
| clampPosition={(position: { x: number; y: number }) => | ||
| clampPosition(position, scale) | ||
| } | ||
| /> | ||
| </div> | ||
| <ContextMenu> | ||
| <ContextMenuTrigger asChild> | ||
| <div | ||
| ref={containerRef} | ||
| className="overflow-hidden bg-background-onlook flex flex-grow relative" | ||
| onMouseDown={handleCanvasMouseDown} | ||
| onMouseMove={handleCanvasMouseMove} | ||
| onMouseUp={handleCanvasMouseUp} | ||
| onMouseLeave={(e) => { | ||
| // Only terminate drag if no mouse button is pressed | ||
| // Note: The global mouseup listener will handle the actual cleanup | ||
| // This is just an additional safety check for when mouse leaves without buttons pressed | ||
| if (e.buttons === 0 && isDragSelecting) { | ||
| setIsDragSelecting(false); | ||
| setFramesInSelection(new Set()); | ||
| editorEngine.state.isDragSelecting = false; | ||
| } | ||
| }} | ||
| > | ||
| <div id={EditorAttributes.CANVAS_CONTAINER_ID} style={transformStyle}> | ||
| <Frames framesInDragSelection={framesInSelection} /> | ||
| </div> | ||
| <RecenterCanvasButton /> | ||
| <DragSelectOverlay | ||
| startX={dragSelectStart.x} | ||
| startY={dragSelectStart.y} | ||
| endX={dragSelectEnd.x} | ||
| endY={dragSelectEnd.y} | ||
| isSelecting={isDragSelecting} | ||
| /> | ||
| <Overlay /> | ||
| <PanOverlay | ||
| clampPosition={(position: { x: number; y: number }) => | ||
| clampPosition(position, scale) | ||
| } | ||
| /> | ||
| </div> | ||
| </ContextMenuTrigger> | ||
| <ContextMenuContent className="w-64 bg-background/95 backdrop-blur-lg"> | ||
| <ContextMenuItem | ||
| onClick={() => { | ||
| // Add Element action | ||
| }} | ||
| > | ||
| <Icons.Plus className="mr-2 h-4 w-4" /> | ||
| Add Element | ||
| </ContextMenuItem> | ||
| <ContextMenuItem | ||
| onClick={() => { | ||
| // Add Component action | ||
| }} | ||
| > | ||
| <Icons.ComponentInstance className="mr-2 h-4 w-4" /> | ||
| Add Component | ||
| </ContextMenuItem> | ||
| <ContextMenuSeparator /> | ||
| <ContextMenuSub> | ||
| <ContextMenuSubTrigger> | ||
| <Icons.ViewGrid className="mr-2 h-4 w-4" /> | ||
| Panels | ||
| </ContextMenuSubTrigger> | ||
| <ContextMenuSubContent> | ||
| <ContextMenuItem | ||
| onClick={() => { | ||
| onSidebarClick?.(LeftPanelTabValue.LAYERS); | ||
| }} | ||
| > | ||
| Layers | ||
| </ContextMenuItem> | ||
| <ContextMenuItem | ||
| onClick={() => { | ||
| onSidebarClick?.(LeftPanelTabValue.BRAND); | ||
| }} | ||
| > | ||
| Brand | ||
| </ContextMenuItem> | ||
| <ContextMenuItem | ||
| onClick={() => { | ||
| onSidebarClick?.(LeftPanelTabValue.PAGES); | ||
| }} | ||
| > | ||
| Pages | ||
| </ContextMenuItem> | ||
| <ContextMenuItem | ||
| onClick={() => { | ||
| onSidebarClick?.(LeftPanelTabValue.IMAGES); | ||
| }} | ||
| > | ||
| Images | ||
| </ContextMenuItem> | ||
| </ContextMenuSubContent> | ||
| </ContextMenuSub> | ||
| <ContextMenuSeparator /> | ||
| <ContextMenuItem | ||
| onClick={() => { | ||
| onSidebarClick?.(LeftPanelTabValue.BRANCHES); | ||
| }} | ||
| > | ||
| Branches | ||
| </ContextMenuItem> | ||
| <ContextMenuSeparator /> | ||
| <ContextMenuItem | ||
| onClick={() => { | ||
| editorEngine.copy.copy(); | ||
| }} | ||
| > | ||
| <Icons.Clipboard className="mr-2 h-4 w-4" /> | ||
| Copy | ||
| <span className="ml-auto text-xs text-muted-foreground">⌘ C</span> | ||
| </ContextMenuItem> | ||
| <ContextMenuItem | ||
| onClick={() => { | ||
| editorEngine.copy.paste(); | ||
| }} | ||
| > | ||
| <Icons.ClipboardCopy className="mr-2 h-4 w-4" /> | ||
| Paste | ||
| <span className="ml-auto text-xs text-muted-foreground">⌘ V</span> | ||
| </ContextMenuItem> | ||
|
Comment on lines
+328
to
+408
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Internationalize hardcoded menu text. Lines 335, 343, 349, 357, 364, 371, 378, 388, 397, and 406 contain hardcoded user-facing text ("Add Element", "Add Component", "Panels", "Layers", "Brand", "Pages", "Images", "Branches", "Copy", "Paste"). This violates the coding guideline to use next-intl messages/hooks instead of hardcoded text. Based on coding guidelines. The DesignPanel component (apps/web/client/src/app/project/[id]/_components/left-panel/design-panel/index.tsx) already uses +import { useTranslations } from 'next-intl';
+import { transKeys } from '@/i18n/keys';
+
export const Canvas = observer(({ onSidebarClick }: CanvasProps) => {
const editorEngine = useEditorEngine();
+ const t = useTranslations();
// ... rest of component
<ContextMenuItem onClick={() => { /* ... */ }}>
<Icons.Plus className="mr-2 h-4 w-4" />
- Add Element
+ {t(transKeys.editor.contextMenu.addElement)}
</ContextMenuItem>Ensure corresponding translation keys exist in your i18n configuration.
Comment on lines
+391
to
+408
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chainVerify keyboard shortcut labels match actual hotkeys. The context menu displays "⌘ C" and "⌘ V" for Copy and Paste operations, but these might not reflect the actual keyboard shortcuts registered in the HotkeysArea or elsewhere in the application. Verify that the displayed shortcuts match the actual hotkey bindings: Additionally, consider making shortcuts cross-platform:
🏁 Script executed: #!/bin/bash
# Description: Search for keyboard shortcut definitions for copy/paste
echo "=== Searching for copy hotkey definitions ==="
rg -n "copy|Copy" --type=ts --type=tsx -B 2 -A 2 | rg -i "cmd|meta|ctrl|key"
echo -e "\n=== Searching for paste hotkey definitions ==="
rg -n "paste|Paste" --type=ts --type=tsx -B 2 -A 2 | rg -i "cmd|meta|ctrl|key"
echo -e "\n=== Searching in HotkeysArea component ==="
fd -e ts -e tsx "hotkey" --exec cat {}Length of output: 4228 🏁 Script executed: #!/bin/bash
# Search for context menu usage of Copy/Paste with keyboard shortcuts in canvas component
echo "=== Finding canvas component file ==="
fd -type f -path "*canvas/index.tsx"
echo -e "\n=== Showing lines 391-408 of canvas component ==="
fd -type f -path "*canvas/index.tsx" -exec sed -n '391,408p' {} \;
echo -e "\n=== Broader context: lines 380-420 to understand the component ==="
fd -type f -path "*canvas/index.tsx" -exec sed -n '380,420p' {} \;
echo -e "\n=== Search for Hotkey imports in canvas component ==="
fd -type f -path "*canvas/index.tsx" -exec grep -n "Hotkey\|import.*hotkey" {} \;Length of output: 895 🏁 Script executed: #!/bin/bash
# Search for canvas component with correct fd syntax
echo "=== Finding canvas component file ==="
fd canvas/index.tsx
echo -e "\n=== Showing lines 391-408 of canvas component ==="
fd canvas/index.tsx -exec sed -n '391,408p' {} \;
echo -e "\n=== Broader context: lines 380-420 ==="
fd canvas/index.tsx -exec sed -n '380,420p' {} \;
echo -e "\n=== Search for Hotkey imports in canvas component ==="
fd canvas/index.tsx -exec grep -n "Hotkey\|import.*hotkey" {} \;
echo -e "\n=== Search for hardcoded keyboard shortcuts ==="
fd canvas/index.tsx -exec grep -n "⌘\|text-muted-foreground" {} \;Length of output: 1156 🏁 Script executed: #!/bin/bash
# Search for the canvas component more broadly
echo "=== Finding all index.tsx files in canvas directories ==="
find . -type f -path "*canvas/index.tsx" 2>/dev/null
echo -e "\n=== List recent .tsx files ==="
find . -type f -name "index.tsx" -path "*canvas*" 2>/dev/null
echo -e "\n=== Search for files containing Copy/Paste context menu ==="
rg -l "ContextMenuItem.*[Cc]opy|ContextMenuItem.*[Pp]aste" -g "*.tsx" 2>/dev/null | head -5
echo -e "\n=== Look for the exact file from the review path ==="
find . -type f -path "*project*\[id\]*canvas*index.tsx" 2>/dev/nullLength of output: 1210 🏁 Script executed: #!/bin/bash
# Extract the exact lines from the canvas component
echo "=== Lines 391-408 from canvas/index.tsx ==="
sed -n '391,408p' ./apps/web/client/src/app/project/[id]/_components/canvas/index.tsx
echo -e "\n=== Broader context: lines 380-420 ==="
sed -n '380,420p' ./apps/web/client/src/app/project/[id]/_components/canvas/index.tsx
echo -e "\n=== Check for Hotkey imports in canvas/index.tsx ==="
grep -n "import.*Hotkey\|from.*hotkey" ./apps/web/client/src/app/project/[id]/_components/canvas/index.tsx
echo -e "\n=== Check if COPY or PASTE hotkeys are used ==="
grep -n "Hotkey\.COPY\|Hotkey\.PASTE\|\.COPY\|\.PASTE" ./apps/web/client/src/app/project/[id]/_components/canvas/index.tsx
echo -e "\n=== Search for ContextMenuItem usage ==="
grep -n "ContextMenuItem" ./apps/web/client/src/app/project/[id]/_components/canvas/index.tsx | head -10
echo -e "\n=== Search for keyboard shortcut display patterns ==="
grep -n "⌘\|Ctrl\|text-xs\|muted-foreground" ./apps/web/client/src/app/project/[id]/_components/canvas/index.tsxLength of output: 3154 🏁 Script executed: #!/bin/bash
# Check the HotkeysArea component to understand keyboard handling
echo "=== Content of hotkeys/index.tsx ==="
cat ./apps/web/client/src/app/project/[id]/_components/canvas/hotkeys/index.tsx
echo -e "\n=== Search for Hotkey class usage in canvas components ==="
grep -r "Hotkey\." ./apps/web/client/src/app/project/[id]/_components/canvas/ --include="*.tsx" | head -15
echo -e "\n=== Check imports at top of canvas/index.tsx ==="
head -30 ./apps/web/client/src/app/project/[id]/_components/canvas/index.tsxLength of output: 9112 Use the Hotkey system to display platform-appropriate keyboard shortcuts instead of hardcoded Mac-only labels. The context menu displays hardcoded "⌘ C" and "⌘ V" at lines 398 and 407, but the application's keyboard binding system (HotkeysArea in hotkeys/index.tsx) correctly supports cross-platform shortcuts via the Hotkey class, which dynamically converts Import the
This ensures the displayed shortcuts match the actual keyboard bindings on all platforms. 🤖 Prompt for AI Agents |
||
| </ContextMenuContent> | ||
| </ContextMenu> | ||
| </HotkeysArea> | ||
| ); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,6 +1,7 @@ | ||||||
| import { useEditorEngine } from '@/components/store/editor'; | ||||||
| import { transKeys } from '@/i18n/keys'; | ||||||
| import { LeftPanelTabValue } from '@onlook/models'; | ||||||
| import { Button } from '@onlook/ui/button'; | ||||||
| import { Icons } from '@onlook/ui/icons'; | ||||||
| import { cn } from '@onlook/ui/utils'; | ||||||
| import { observer } from 'mobx-react-lite'; | ||||||
|
|
@@ -47,11 +48,16 @@ const tabs: { | |||||
| }, | ||||||
| ]; | ||||||
|
|
||||||
| export const DesignPanel = observer(() => { | ||||||
| interface DesignPanelProps { | ||||||
| onClose?: () => void; | ||||||
| activeSection?: string | null; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Refine the type of the
Suggested change
|
||||||
| } | ||||||
|
|
||||||
| export const DesignPanel = observer(({ onClose, activeSection }: DesignPanelProps) => { | ||||||
| const editorEngine = useEditorEngine(); | ||||||
| const t = useTranslations(); | ||||||
| const isLocked = editorEngine.state.leftPanelLocked; | ||||||
| const selectedTab = editorEngine.state.leftPanelTab; | ||||||
| const selectedTab = (activeSection as LeftPanelTabValue) || editorEngine.state.leftPanelTab; | ||||||
|
|
||||||
| const handleMouseEnter = (tab: LeftPanelTabValue) => { | ||||||
| if (isLocked) { | ||||||
|
|
@@ -102,8 +108,8 @@ export const DesignPanel = observer(() => { | |||||
| className="flex h-full overflow-auto" | ||||||
| onMouseLeave={handleMouseLeave} | ||||||
| > | ||||||
| {/* Left sidebar with tabs */} | ||||||
| <div className="w-20 flex flex-col items-center py-0.5 gap-2 bg-background-onlook/60 backdrop-blur-xl"> | ||||||
| {/* Left sidebar with tabs - HIDDEN */} | ||||||
| {/* <div className="w-20 flex flex-col items-center py-0.5 gap-2 bg-background-onlook/60 backdrop-blur-xl"> | ||||||
| {tabs.map((tab) => ( | ||||||
| <button | ||||||
| key={tab.value} | ||||||
|
|
@@ -127,18 +133,47 @@ export const DesignPanel = observer(() => { | |||||
| <ZoomControls /> | ||||||
| <HelpButton /> | ||||||
| </div> | ||||||
| </div> | ||||||
| </div> */} | ||||||
|
|
||||||
| {/* Content panel */} | ||||||
| {editorEngine.state.leftPanelTab && ( | ||||||
| {selectedTab && ( | ||||||
| <> | ||||||
| <div className="flex-1 w-[280px] bg-background/95 rounded-xl"> | ||||||
| <div className="border backdrop-blur-xl h-full shadow overflow-auto p-0 rounded-xl"> | ||||||
| {selectedTab === LeftPanelTabValue.LAYERS && <LayersTab />} | ||||||
| {selectedTab === LeftPanelTabValue.BRAND && <BrandTab />} | ||||||
| {selectedTab === LeftPanelTabValue.PAGES && <PagesTab />} | ||||||
| {selectedTab === LeftPanelTabValue.IMAGES && <ImagesTab />} | ||||||
| {selectedTab === LeftPanelTabValue.BRANCHES && <BranchesTab />} | ||||||
| <div className="border backdrop-blur-xl h-full shadow overflow-auto p-0 rounded-xl flex flex-col"> | ||||||
| {/* Panel header with close button */} | ||||||
| <div className="flex items-center justify-between p-3 border-b border-border/50"> | ||||||
| <div className="flex items-center gap-2"> | ||||||
| {selectedTab === LeftPanelTabValue.LAYERS && <Icons.Layers className="w-4 h-4" />} | ||||||
| {selectedTab === LeftPanelTabValue.BRAND && <Icons.Brand className="w-4 h-4" />} | ||||||
| {selectedTab === LeftPanelTabValue.PAGES && <Icons.File className="w-4 h-4" />} | ||||||
| {selectedTab === LeftPanelTabValue.IMAGES && <Icons.Image className="w-4 h-4" />} | ||||||
| {selectedTab === LeftPanelTabValue.BRANCHES && <Icons.Branch className="w-4 h-4" />} | ||||||
| <span className="text-sm font-medium"> | ||||||
| {selectedTab === LeftPanelTabValue.LAYERS && t(transKeys.editor.panels.layers.tabs.layers)} | ||||||
| {selectedTab === LeftPanelTabValue.BRAND && t(transKeys.editor.panels.layers.tabs.brand)} | ||||||
| {selectedTab === LeftPanelTabValue.PAGES && t(transKeys.editor.panels.layers.tabs.pages)} | ||||||
| {selectedTab === LeftPanelTabValue.IMAGES && t(transKeys.editor.panels.layers.tabs.images)} | ||||||
| {selectedTab === LeftPanelTabValue.BRANCHES && t(transKeys.editor.panels.layers.tabs.branches)} | ||||||
| </span> | ||||||
| </div> | ||||||
| {onClose && ( | ||||||
| <Button | ||||||
| variant="ghost" | ||||||
| size="icon" | ||||||
| className="h-6 w-6" | ||||||
| onClick={onClose} | ||||||
| > | ||||||
| <Icons.CrossS className="h-4 w-4" /> | ||||||
| </Button> | ||||||
| )} | ||||||
| </div> | ||||||
| <div className="flex-1 overflow-auto"> | ||||||
| {selectedTab === LeftPanelTabValue.LAYERS && <LayersTab />} | ||||||
| {selectedTab === LeftPanelTabValue.BRAND && <BrandTab />} | ||||||
| {selectedTab === LeftPanelTabValue.PAGES && <PagesTab />} | ||||||
| {selectedTab === LeftPanelTabValue.IMAGES && <ImagesTab />} | ||||||
| {selectedTab === LeftPanelTabValue.BRANCHES && <BranchesTab />} | ||||||
| </div> | ||||||
| </div> | ||||||
| </div> | ||||||
|
|
||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -4,14 +4,14 @@ import { useEditorEngine } from '@/components/store/editor'; | |||||
| import { SubscriptionModal } from '@/components/ui/pricing-modal'; | ||||||
| import { SettingsModalWithProjects } from '@/components/ui/settings-modal/with-project'; | ||||||
| import { EditorAttributes } from '@onlook/constants'; | ||||||
| import { EditorMode } from '@onlook/models'; | ||||||
| import { EditorMode, LeftPanelTabValue } from '@onlook/models'; | ||||||
| import { Button } from '@onlook/ui/button'; | ||||||
| import { Icons } from '@onlook/ui/icons'; | ||||||
| import { TooltipProvider } from '@onlook/ui/tooltip'; | ||||||
| import { cn } from '@onlook/ui/utils'; | ||||||
| import { observer } from 'mobx-react-lite'; | ||||||
| import { useRouter } from 'next/navigation'; | ||||||
| import { useEffect, useRef } from 'react'; | ||||||
| import { useEffect, useRef, useState } from 'react'; | ||||||
| import { usePanelMeasurements } from '../_hooks/use-panel-measure'; | ||||||
| import { useStartProject } from '../_hooks/use-start-project'; | ||||||
| import { BottomBar } from './bottom-bar'; | ||||||
|
|
@@ -31,6 +31,19 @@ export const Main = observer(() => { | |||||
| leftPanelRef, | ||||||
| rightPanelRef, | ||||||
| ); | ||||||
| const [activeSection, setActiveSection] = useState<string | null>(null); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider typing the
Suggested change
|
||||||
|
|
||||||
| const handleSidebarClick = (tab: LeftPanelTabValue) => { | ||||||
| editorEngine.state.leftPanelTab = tab; | ||||||
| editorEngine.state.leftPanelLocked = true; | ||||||
| setActiveSection(tab); | ||||||
| }; | ||||||
|
|
||||||
| const handleClosePanel = () => { | ||||||
| editorEngine.state.leftPanelTab = null; | ||||||
| editorEngine.state.leftPanelLocked = false; | ||||||
| setActiveSection(null); | ||||||
| }; | ||||||
|
|
||||||
| useEffect(() => { | ||||||
| function handleGlobalWheel(event: WheelEvent) { | ||||||
|
|
@@ -82,7 +95,7 @@ export const Main = observer(() => { | |||||
| return ( | ||||||
| <TooltipProvider> | ||||||
| <div className="h-screen w-screen flex flex-row select-none relative overflow-hidden"> | ||||||
| <Canvas /> | ||||||
| <Canvas onSidebarClick={handleSidebarClick} /> | ||||||
|
|
||||||
| <div className="absolute top-0 w-full"> | ||||||
| <TopBar /> | ||||||
|
|
@@ -91,9 +104,9 @@ export const Main = observer(() => { | |||||
| {/* Left Panel */} | ||||||
| <div | ||||||
| ref={leftPanelRef} | ||||||
| className="absolute top-10 left-0 h-[calc(100%-40px)] z-50" | ||||||
| className="absolute top-10 left-2 h-[calc(100%-40px)] z-50" | ||||||
| > | ||||||
| <LeftPanel /> | ||||||
| <LeftPanel onClose={handleClosePanel} activeSection={activeSection} /> | ||||||
| </div> | ||||||
| {/* EditorBar anchored between panels */} | ||||||
| <div | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implement missing onClick handlers for Add Element and Add Component.
The context menu items for "Add Element" and "Add Component" have empty
onClickhandlers. These actions are non-functional and will confuse users if the menu is accessible.Either:
Do you want me to help search for existing add element/component functionality that could be wired up?
🤖 Prompt for AI Agents