Skip to content

Commit 791633b

Browse files
authored
refactor: window manager into frame manager (#1909)
* refactor: window manager * update name collision * remove window metadata * disable all but brand and windows tab
1 parent 1fb86d5 commit 791633b

File tree

17 files changed

+193
-284
lines changed

17 files changed

+193
-284
lines changed

apps/web/client/messages/en.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@
239239
"tabs": {
240240
"layers": "Layers",
241241
"pages": "Pages",
242-
"components": "Components",
242+
"components": "Elements",
243243
"images": "Images",
244244
"windows": {
245245
"name": "Windows",

apps/web/client/src/app/project/[id]/_components/canvas/frame/gesture.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export const GestureScreen = observer(({ frame }: { frame: WebFrame }) => {
6060
break;
6161
case MouseAction.MOUSE_DOWN:
6262
if (el.tagName.toLocaleLowerCase() === 'body') {
63-
editorEngine.frames.select(frame);
63+
editorEngine.frames.select([frame]);
6464
return;
6565
}
6666
// Ignore right-clicks
@@ -116,7 +116,7 @@ export const GestureScreen = observer(({ frame }: { frame: WebFrame }) => {
116116
const handleClick = useCallback(
117117
(e: React.MouseEvent<HTMLDivElement>) => {
118118
editorEngine.frames.deselectAll();
119-
editorEngine.frames.select(frame);
119+
editorEngine.frames.select([frame]);
120120
},
121121
[editorEngine.frames],
122122
);

apps/web/client/src/app/project/[id]/_components/canvas/frame/right-click.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,18 @@ export const RightClickMenu = observer(({ children }: RightClickMenuProps) => {
127127
const WINDOW_ITEMS: MenuItem[] = [
128128
{
129129
label: 'Duplicate',
130-
action: () => editorEngine.window.duplicate(),
130+
action: () => editorEngine.frames.duplicateSelected(),
131131
icon: <Icons.Copy className="mr-2 h-4 w-4" />,
132132
hotkey: Hotkey.DUPLICATE,
133+
disabled: !editorEngine.frames.canDuplicate(),
133134
},
134135
{
135136
label: 'Delete',
136-
action: () => editorEngine.window.delete(editorEngine.frames.selected[0]?.frame.id),
137+
action: () => editorEngine.frames.deleteSelected(),
137138
icon: <Icons.Trash className="mr-2 h-4 w-4" />,
138139
hotkey: Hotkey.DELETE,
139140
destructive: true,
141+
disabled: !editorEngine.frames.canDelete(),
140142
},
141143
];
142144

apps/web/client/src/app/project/[id]/_components/canvas/frame/top-bar.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ import { useEditorEngine } from '@/components/store/editor';
22
import type { WebFrame } from '@onlook/models';
33
import { Button } from '@onlook/ui/button';
44
import { Icons } from '@onlook/ui/icons/index';
5+
import { cn } from '@onlook/ui/utils';
56
import { observer } from 'mobx-react-lite';
67
import Link from 'next/link';
78

89
export const TopBar = observer(
9-
({ frame, children }: { frame: WebFrame; children?: React.ReactNode }) => {
10+
({ frame }: { frame: WebFrame }) => {
1011
const editorEngine = useEditorEngine();
12+
const isSelected = editorEngine.frames.isSelected(frame.id);
1113

1214
const handleMouseDown = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
1315
e.preventDefault();
@@ -52,15 +54,25 @@ export const TopBar = observer(
5254
editorEngine.frames.reload(frame.id);
5355
};
5456

57+
const handleClick = () => {
58+
editorEngine.frames.select([frame]);
59+
};
60+
5561
return (
5662
<div
57-
className="rounded-lg bg-background-primary/10 hover:shadow h-6 m-auto flex flex-row items-center backdrop-blur-lg overflow-hidden relative shadow-sm border-input text-foreground-secondary group-hover:text-foreground cursor-grab active:cursor-grabbing"
63+
className={
64+
cn(
65+
'rounded-lg bg-background-primary/10 hover:shadow h-6 m-auto flex flex-row items-center backdrop-blur-lg overflow-hidden relative shadow-sm border-input text-foreground-secondary group-hover:text-foreground cursor-grab active:cursor-grabbing',
66+
isSelected && 'text-teal-400 fill-teal-400',
67+
)
68+
}
5869
style={{
5970
height: `${28 / editorEngine.canvas.scale}px`,
6071
width: `${frame.dimension.width}px`,
6172
marginBottom: `${10 / editorEngine.canvas.scale}px`,
6273
}}
6374
onMouseDown={handleMouseDown}
75+
onClick={handleClick}
6476
>
6577
<div
6678
className="flex flex-row items-center justify-between gap-2 w-full"

apps/web/client/src/app/project/[id]/_components/canvas/hotkeys/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ export const HotkeysArea = ({ children }: { children: ReactNode }) => {
6666
useHotkeys(Hotkey.PASTE.command, () => editorEngine.copy.paste());
6767
useHotkeys(Hotkey.CUT.command, () => editorEngine.copy.cut());
6868
useHotkeys(Hotkey.DUPLICATE.command, () => {
69-
if (editorEngine.window.areAnyWindowsSelected) {
70-
editorEngine.window.duplicate();
69+
if (editorEngine.frames.canDuplicate()) {
70+
editorEngine.frames.duplicateSelected();
7171
} else {
7272
editorEngine.copy.duplicate();
7373
}

apps/web/client/src/app/project/[id]/_components/left-panel/index.tsx

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,43 +12,48 @@ import { PagesTab } from './page-tab';
1212
import { WindowsTab } from './windows-tab';
1313
import { ZoomControls } from './zoom-controls';
1414

15-
const tabs: { value: LeftPanelTabValue; icon: React.ReactNode; label: string; hidden: boolean }[] =
15+
const tabs: { value: LeftPanelTabValue; icon: React.ReactNode; label: string; disabled?: boolean }[] =
1616
[
17+
18+
{
19+
value: LeftPanelTabValue.BRAND,
20+
icon: <Icons.Brand className="w-5 h-5" />,
21+
label: 'editor.panels.layers.tabs.brand',
22+
},
23+
{
24+
value: LeftPanelTabValue.WINDOWS,
25+
icon: <Icons.Desktop className="w-5 h-5" />,
26+
label: 'editor.panels.layers.tabs.windows.name',
27+
},
1728
{
1829
value: LeftPanelTabValue.LAYERS,
1930
icon: <Icons.Layers className="w-5 h-5" />,
2031
label: 'editor.panels.layers.tabs.layers',
21-
hidden: false,
32+
disabled: true,
2233
},
2334
{
2435
value: LeftPanelTabValue.PAGES,
2536
icon: <Icons.File className="w-5 h-5" />,
2637
label: 'editor.panels.layers.tabs.pages',
27-
hidden: false,
38+
disabled: true,
2839
},
2940
{
3041
value: LeftPanelTabValue.IMAGES,
3142
icon: <Icons.Image className="w-5 h-5" />,
3243
label: 'editor.panels.layers.tabs.images',
33-
hidden: false,
34-
},
35-
{
36-
value: LeftPanelTabValue.WINDOWS,
37-
icon: <Icons.Desktop className="w-5 h-5" />,
38-
label: 'editor.panels.layers.tabs.windows.name',
39-
hidden: false,
40-
},
41-
{
42-
value: LeftPanelTabValue.BRAND,
43-
icon: <Icons.Brand className="w-5 h-5" />,
44-
label: 'editor.panels.layers.tabs.brand',
45-
hidden: false,
44+
disabled: true,
4645
},
4746
{
4847
value: LeftPanelTabValue.APPS,
4948
icon: <Icons.ViewGrid className="w-5 h-5" />,
5049
label: 'editor.panels.layers.tabs.apps',
51-
hidden: true,
50+
disabled: true,
51+
},
52+
{
53+
value: LeftPanelTabValue.COMPONENTS,
54+
icon: <Icons.Component className="w-5 h-5" />,
55+
label: 'editor.panels.layers.tabs.components',
56+
disabled: true,
5257
},
5358
];
5459

@@ -117,13 +122,14 @@ export const LeftPanel = observer(() => {
117122
key={tab.value}
118123
className={cn(
119124
'w-16 h-16 rounded-xl flex flex-col items-center justify-center gap-1.5 p-2',
120-
tab.hidden && 'hidden',
121125
selectedTab === tab.value && isLocked
122126
? 'bg-accent text-foreground border-[0.5px] border-foreground/20 '
123127
: 'text-muted-foreground hover:text-foreground hover:bg-accent/50',
128+
tab.disabled && 'opacity-50 cursor-not-allowed hover:bg-transparent hover:text-muted-foreground',
124129
)}
125-
onClick={() => handleClick(tab.value)}
126-
onMouseEnter={() => handleMouseEnter(tab.value)}
130+
disabled={tab.disabled}
131+
onClick={() => !tab.disabled && handleClick(tab.value)}
132+
onMouseEnter={() => !tab.disabled && handleMouseEnter(tab.value)}
127133
>
128134
{tab.icon}
129135
<span className="text-xs leading-tight">{t(tab.label)}</span>
@@ -142,12 +148,10 @@ export const LeftPanel = observer(() => {
142148
<div className="flex-1 w-[280px] bg-background/95 rounded-xl">
143149
<div className="border backdrop-blur-xl h-full shadow overflow-auto p-0 rounded-xl">
144150
{selectedTab === LeftPanelTabValue.LAYERS && <LayersTab />}
145-
{/* {selectedTab === LayersPanelTabValue.COMPONENTS && <ComponentsTab components={editorEngine.state.components} />} */}
146151
{selectedTab === LeftPanelTabValue.PAGES && <PagesTab />}
147152
{selectedTab === LeftPanelTabValue.IMAGES && <ImagesTab />}
148153
{selectedTab === LeftPanelTabValue.WINDOWS && <WindowsTab />}
149154
{selectedTab === LeftPanelTabValue.BRAND && <BrandTab />}
150-
{/* {selectedTab === LayersPanelTabValue.APPS && <AppsTab />} */}
151155
</div>
152156
</div>
153157

apps/web/client/src/app/project/[id]/_components/left-panel/windows-tab/frame-dimensions.tsx

Lines changed: 27 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useEditorEngine } from '@/components/store/editor';
22
import type { FrameImpl } from '@/components/store/editor/canvas/frame';
33
import { DefaultSettings, DEVICE_OPTIONS, Orientation } from '@onlook/constants';
4-
import type { Frame, FrameType } from '@onlook/models';
4+
import type { FrameType, WindowMetadata } from '@onlook/models';
55
import { Button } from '@onlook/ui/button';
66
import { Icons } from '@onlook/ui/icons/index';
77
import { Input } from '@onlook/ui/input';
@@ -15,23 +15,26 @@ import {
1515
SelectValue,
1616
} from '@onlook/ui/select';
1717
import { Separator } from '@onlook/ui/separator';
18+
import { computeWindowMetadata } from '@onlook/utility';
1819
import { Fragment, useEffect, useState } from 'react';
1920

2021
export const FrameDimensions = ({ frame }: { frame: FrameImpl }) => {
2122
const editorEngine = useEditorEngine();
22-
const [device, setDevice] = useState(frame.windowMetadata.device ?? DefaultSettings.DEVICE);
23+
24+
const settings = computeWindowMetadata(frame.dimension.width.toString(), frame.dimension.height.toString());
25+
const [device, setDevice] = useState(settings.device || DefaultSettings.DEVICE);
2326
const [orientation, setOrientation] = useState(
24-
frame.windowMetadata.orientation ?? DefaultSettings.ORIENTATION,
27+
settings.orientation || DefaultSettings.ORIENTATION,
2528
);
2629
const [width, setWidth] = useState(
27-
frame.dimension.width ?? DefaultSettings.FRAME_DIMENSION.width,
30+
settings.width || DefaultSettings.FRAME_DIMENSION.width,
2831
);
2932
const [height, setHeight] = useState(
30-
frame.dimension.height ?? DefaultSettings.FRAME_DIMENSION.height,
33+
settings.height || DefaultSettings.FRAME_DIMENSION.height,
3134
);
3235
// const [responsive, setResponsive] = useState('Closest Size');
3336
const [aspectRatioLocked, setAspectRatioLocked] = useState(
34-
frame.windowMetadata.aspectRatioLocked ?? DefaultSettings.ASPECT_RATIO_LOCKED,
37+
settings.aspectRatioLocked || DefaultSettings.ASPECT_RATIO_LOCKED,
3538
);
3639
const [aspectRatio, setAspectRatio] = useState(width / height);
3740
const [step, setStep] = useState(1);
@@ -40,39 +43,18 @@ export const FrameDimensions = ({ frame }: { frame: FrameImpl }) => {
4043
width: parseInt(DefaultSettings.MIN_DIMENSIONS.width),
4144
});
4245

43-
useEffect(() => {
44-
const observer = (newSettings: Frame) => {
45-
if (newSettings.dimension.width !== width) {
46-
setWidth(newSettings.dimension.width);
47-
}
48-
if (newSettings.dimension.height !== height) {
49-
setHeight(newSettings.dimension.height);
50-
}
51-
};
52-
53-
editorEngine.canvas.observeSettings(frame.id, observer);
54-
55-
return editorEngine.canvas.unobserveSettings(frame.id, observer);
56-
}, []);
57-
58-
useEffect(() => {
59-
setDevice(frame.windowMetadata.device || DefaultSettings.DEVICE);
60-
setOrientation(frame.windowMetadata.orientation || DefaultSettings.ORIENTATION);
61-
setWidth(frame.dimension.width || DefaultSettings.FRAME_DIMENSION.width);
62-
setHeight(frame.dimension.height || DefaultSettings.FRAME_DIMENSION.height);
63-
setAspectRatioLocked(frame.windowMetadata.aspectRatioLocked || DefaultSettings.ASPECT_RATIO_LOCKED);
64-
}, [frame.id]);
46+
const updateFrame = (metadata: Partial<WindowMetadata>) => {
47+
console.log(metadata);
48+
}
6549

6650
useEffect(() => {
6751
const [deviceCategory, deviceName] = device.split(':');
6852
if (deviceCategory && deviceName) {
6953

7054

7155
if (deviceName === 'Custom') {
72-
editorEngine.canvas.saveFrame(frame.id, {
73-
windowMetadata: {
74-
device: device as FrameType,
75-
}
56+
updateFrame({
57+
device: device as FrameType,
7658
});
7759
return;
7860
}
@@ -89,11 +71,10 @@ export const FrameDimensions = ({ frame }: { frame: FrameImpl }) => {
8971
} else {
9072
setWidth(parseInt(deviceWidth));
9173
setHeight(parseInt(deviceHeight));
92-
editorEngine.canvas.saveFrame(frame.id, {
93-
dimension: { width: parseInt(deviceWidth), height: parseInt(deviceHeight) },
94-
windowMetadata: {
95-
device: device as FrameType,
96-
}
74+
updateFrame({
75+
width: parseInt(deviceWidth),
76+
height: parseInt(deviceHeight),
77+
device: device as FrameType,
9778
});
9879
if (aspectRatioLocked) {
9980
setAspectRatio(parseInt(deviceWidth) / parseInt(deviceHeight));
@@ -132,8 +113,9 @@ export const FrameDimensions = ({ frame }: { frame: FrameImpl }) => {
132113
setOrientation(Orientation.Landscape);
133114
}
134115

135-
editorEngine.canvas.saveFrame(frame.id, {
136-
dimension: { width: width, height: height },
116+
updateFrame({
117+
width: width,
118+
height: height,
137119
});
138120
}
139121
}, [height, width]);
@@ -157,18 +139,14 @@ export const FrameDimensions = ({ frame }: { frame: FrameImpl }) => {
157139
width: parseInt(DefaultSettings.MIN_DIMENSIONS.width),
158140
});
159141
}
160-
editorEngine.canvas.saveFrame(frame.id, {
161-
windowMetadata: {
162-
aspectRatioLocked: aspectRatioLocked,
163-
}
142+
updateFrame({
143+
aspectRatioLocked: aspectRatioLocked,
164144
});
165145
}, [aspectRatioLocked]);
166146

167147
useEffect(() => {
168-
editorEngine.canvas.saveFrame(frame.id, {
169-
windowMetadata: {
170-
orientation: orientation,
171-
}
148+
updateFrame({
149+
orientation: orientation,
172150
});
173151
}, [orientation]);
174152

@@ -283,10 +261,8 @@ export const FrameDimensions = ({ frame }: { frame: FrameImpl }) => {
283261

284262
const handleAspectRatioLock = () => {
285263
setAspectRatioLocked((prev) => !prev);
286-
editorEngine.canvas.saveFrame(frame.id, {
287-
windowMetadata: {
288-
aspectRatioLocked: !aspectRatioLocked,
289-
}
264+
updateFrame({
265+
aspectRatioLocked: !aspectRatioLocked,
290266
});
291267
};
292268

0 commit comments

Comments
 (0)