Repository: onlook-dev/onlook
Pull request #1913: update editor bar component
Author: login: spartan-vutrannguyen
Created: 2025-05-23T11:06:26Z | Merged: 2025-05-23T16:47:08Z

## Description

<!-- Provide a clear and concise description of your changes -->

## Related Issues

<!-- Link any related issues using GitHub keywords (e.g., "closes #123", "fixes #456", "related to #789") -->

## Type of Change

<!-- Put an `x` in the boxes that apply -->

- [ ] Bug fix
- [ ] New feature
- [ ] Documentation update
- [ ] Release
- [ ] Refactor
- [ ] Other (please describe):

## Testing

<!-- Describe the tests you ran or the steps to verify your changes -->

## Screenshots (if applicable)

<!-- Add screenshots to help explain your changes -->

## Additional Notes

<!-- Add any other context about the PR here -->

<!-- ELLIPSIS_HIDDEN -->


----

> [!IMPORTANT]
> Refactor and optimize the editor bar component by introducing new hooks, refactoring components, and improving state management and UI consistency.
> 
>   - **Components**:
>     - Refactor `DivSelected` and `TextSelected` components to use `useMeasureGroup` for dynamic visibility of toolbar groups.
>     - Convert `DivSelected` to a memoized component.
>     - Update `FontFamilySelector`, `FontSizeSelector`, `FontWeightSelector`, `TextAlignSelector`, and `TextColor` to use `useTextControl` for state management.
>     - Replace individual font components with a unified `FontFamily` component.
>   - **Hooks**:
>     - Add `useMeasureGroup` to calculate visible toolbar groups based on available width.
>     - Add `useInputControl` for debounced input handling.
>     - Update `useDimensionControl` to handle dimension changes with numeric values.
>   - **Dropdowns**:
>     - Refactor dropdown components (`border.tsx`, `color-background.tsx`, `display/index.tsx`, etc.) to use `observer` from `mobx-react-lite`.
>     - Simplify dropdown logic and improve UI consistency.
>   - **Inputs**:
>     - Refactor `InputDropdown` and `InputIcon` to use `useInputControl` for better input management.
>     - Update `SpacingInputs` to use `InputIcon` for individual side inputs.
>   - **Misc**:
>     - Rename `font-family.tsx` to `font/font-family-selector.tsx` and `font-size.tsx` to `font/font-size.tsx`.
>     - Remove unused `font-weight.tsx` and replace with new implementation in `font/font-weight.tsx`.
> 
> <sup>This description was created by </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=onlook-dev%2Fonlook&utm_source=github&utm_medium=referral)<sup> for 13c6990ef86fc9fd125c5e2f5a41aa7464bf0d1f. You can [customize](https://app.ellipsis.dev/onlook-dev/settings/summaries) this summary. It will automatically update as commits are pushed.</sup>


<!-- ELLIPSIS_HIDDEN -->

Linked issue #123: [FEAT] Add better onboarding docs
#### Describe the feature
Add framework documentation
- Next.js
- Remix
- Webpack
- Babel

Diff:
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/div-selected.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/div-selected.tsx
index 55b3c0efa7..e290ff1a91 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/div-selected.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/div-selected.tsx
@@ -3,7 +3,7 @@
 import { Button } from '@onlook/ui/button';
 import { Icons } from '@onlook/ui/icons';
 import { Popover, PopoverContent, PopoverTrigger } from '@onlook/ui/popover';
-import React, { useCallback, useEffect, useRef, useState } from 'react';
+import React, { memo, useState } from 'react';
 import { Border } from './dropdowns/border';
 import { ColorBackground } from './dropdowns/color-background';
 import { Display } from './dropdowns/display';
@@ -13,187 +13,94 @@ import { Opacity } from './dropdowns/opacity';
 import { Padding } from './dropdowns/padding';
 import { Radius } from './dropdowns/radius';
 import { Width } from './dropdowns/width';
+import { useMeasureGroup } from './hooks/use-measure-group';
 import { InputSeparator } from './separator';
-import { FontFamilySelector } from './text-inputs/font-family';
-import { FontSizeSelector } from './text-inputs/font-size';
-import { FontWeightSelector } from './text-inputs/font-weight';
+import { FontFamilySelector } from './text-inputs/font/font-family-selector';
+import { FontSizeSelector } from './text-inputs/font/font-size';
+import { FontWeightSelector } from './text-inputs/font/font-weight';
 
 // Group definitions for the div-selected toolbar
 export const DIV_SELECTED_GROUPS = [
     {
         key: 'dimensions',
         label: 'Dimensions',
-        components: [
-            <Width />,
-            <Height />,
-        ],
+        components: [<Width />, <Height />],
     },
     {
         key: 'base',
         label: 'Base',
-        components: [
-            <ColorBackground />,
-            <Border />,
-            <Radius />,
-        ]
+        components: [<ColorBackground />, <Border />, <Radius />],
     },
     {
         key: 'layout',
         label: 'Layout',
-        components: [
-            <Display />,
-            <Padding />,
-            <Margin />,
-        ],
+        components: [<Display />, <Padding />, <Margin />],
     },
     {
         key: 'typography',
         label: 'Typography',
         components: [
-            <FontFamilySelector fontFamily="Arial" />,
-            <FontWeightSelector fontWeight="normal" handleFontWeightChange={() => { }} />,
-            <FontSizeSelector fontSize={16} handleFontSizeChange={() => { }} />,
+            <FontFamilySelector />,
+            <FontWeightSelector />,
+            <FontSizeSelector />,
         ],
     },
     {
         key: 'opacity',
         label: 'Opacity',
-        components: [
-            <Opacity />,
-        ]
+        components: [<Opacity />],
     },
 ];
 
-export const DivSelected = ({ availableWidth = 0 }: { availableWidth?: number }) => {
-    const groupRefs = useRef<(HTMLDivElement | null)[]>([]);
-    const [groupWidths, setGroupWidths] = useState<number[]>([]);
+export const DivSelected = memo(({ availableWidth = 0 }: { availableWidth?: number }) => {
     const [overflowOpen, setOverflowOpen] = useState(false);
-    const [visibleCount, setVisibleCount] = useState(DIV_SELECTED_GROUPS.length);
-
-    // Calculate total width of a group including margins and padding
-    const calculateGroupWidth = useCallback((element: HTMLElement | null): number => {
-        if (!element) return 0;
-        const style = window.getComputedStyle(element);
-        const width = element.offsetWidth;
-        const marginLeft = parseFloat(style.marginLeft);
-        const marginRight = parseFloat(style.marginRight);
-        const paddingLeft = parseFloat(style.paddingLeft);
-        const paddingRight = parseFloat(style.paddingRight);
-        return width + marginLeft + marginRight + paddingLeft + paddingRight;
-    }, []);
-
-    // Measure all group widths
-    const measureGroups = useCallback(() => {
-        const widths = groupRefs.current.map(ref => calculateGroupWidth(ref));
-        setGroupWidths(widths);
-    }, [calculateGroupWidth]);
-
-    // Update visible count based on available width
-    const updateVisibleCount = useCallback(() => {
-        if (!groupWidths.length || !availableWidth) return;
-
-        const OVERFLOW_BUTTON_WIDTH = 32; // Reduced from 48px
-        const MIN_GROUP_WIDTH = 80; // Reduced from 100px
-        const SEPARATOR_WIDTH = 8; // Width of the InputSeparator
-        let used = 0;
-        let count = 0;
-
-        for (let i = 0; i < groupWidths.length; i++) {
-            const width = groupWidths[i] ?? 0;
-            if (width < MIN_GROUP_WIDTH) continue;
-
-            // Add separator width if this isn't the first group
-            const totalWidth = width + (count > 0 ? SEPARATOR_WIDTH : 0);
-
-            if (used + totalWidth <= availableWidth - OVERFLOW_BUTTON_WIDTH) {
-                used += totalWidth;
-                count++;
-            } else {
-                break;
-            }
-        }
-
-        setVisibleCount(count);
-    }, [groupWidths, availableWidth]);
-
-    // Measure group widths after mount and when groupRefs change
-    useEffect(() => {
-        measureGroups();
-    }, [measureGroups, availableWidth]);
-
-    // Update visible count when measurements change
-    useEffect(() => {
-        updateVisibleCount();
-    }, [updateVisibleCount]);
+    const { visibleCount } = useMeasureGroup({ availableWidth, count: DIV_SELECTED_GROUPS.length });
 
     const visibleGroups = DIV_SELECTED_GROUPS.slice(0, visibleCount);
     const overflowGroups = DIV_SELECTED_GROUPS.slice(visibleCount);
 
     return (
-        <>
-            {/* Hidden measurement container */}
-            <div style={{ position: 'absolute', visibility: 'hidden', height: 0, overflow: 'hidden', pointerEvents: 'none' }}>
-                {DIV_SELECTED_GROUPS.map((group, groupIdx) => (
-                    <div
-                        key={group.key}
-                        className="flex items-center justify-center gap-0.5"
-                        ref={el => { groupRefs.current[groupIdx] = el; }}
-                    >
+        <div className="flex items-center justify-center gap-0.5 w-full overflow-hidden">
+            {visibleGroups.map((group, groupIdx) => (
+                <React.Fragment key={group.key}>
+                    {groupIdx > 0 && <InputSeparator />}
+                    <div className="flex items-center justify-center gap-0.5">
                         {group.components.map((comp, idx) => (
-                            <React.Fragment key={idx}>
-                                {comp}
-                            </React.Fragment>
+                            <React.Fragment key={idx}>{comp}</React.Fragment>
                         ))}
                     </div>
-                ))}
-            </div>
-            <div
-                className="flex items-center justify-center gap-0.5 w-full overflow-hidden"
-            >
-                {DIV_SELECTED_GROUPS.map((group, groupIdx) => (
-                    groupIdx < visibleCount ? (
-                        <React.Fragment key={group.key}>
-                            {groupIdx > 0 && <InputSeparator />}
-                            <div className="flex items-center justify-center gap-0.5">
-                                {group.components.map((comp, idx) => (
-                                    <React.Fragment key={idx}>
-                                        {comp}
-                                    </React.Fragment>
-                                ))}
-                            </div>
-                        </React.Fragment>
-                    ) : null
-                ))}
-                {overflowGroups.length > 0 && visibleCount > 0 && <InputSeparator />}
-                {overflowGroups.length > 0 && (
-                    <Popover open={overflowOpen} onOpenChange={setOverflowOpen}>
-                        <PopoverTrigger asChild>
-                            <Button
-                                variant="ghost"
-                                size="toolbar"
-                                className="w-8 h-8 flex items-center justify-center"
-                                aria-label="Show more toolbar controls"
-                            >
-                                <Icons.DotsHorizontal className="w-5 h-5" />
-                            </Button>
-                        </PopoverTrigger>
-                        <PopoverContent align="end" className="flex flex-row gap-1 p-1 px-1 bg-background rounded-lg shadow-xl shadow-black/20 min-w-[fit-content] items-center w-[fit-content]">
-                            {overflowGroups.map((group, groupIdx) => (
-                                <React.Fragment key={group.key}>
-                                    {groupIdx > 0 && <InputSeparator />}
-                                    <div className="flex items-center gap-0.5">
-                                        {group.components.map((comp, idx) => (
-                                            <React.Fragment key={idx}>
-                                                {comp}
-                                            </React.Fragment>
-                                        ))}
-                                    </div>
-                                </React.Fragment>
-                            ))}
-                        </PopoverContent>
-                    </Popover>
-                )}
-            </div>
-        </>
+                </React.Fragment>
+            ))}
+            {overflowGroups.length > 0 && visibleCount > 0 && <InputSeparator />}
+            {overflowGroups.length > 0 && (
+                <Popover open={overflowOpen} onOpenChange={setOverflowOpen}>
+                    <PopoverTrigger asChild>
+                        <Button
+                            variant="ghost"
+                            size="toolbar"
+                            className="w-8 h-8 flex items-center justify-center"
+                            aria-label="Show more toolbar controls"
+                        >
+                            <Icons.DotsHorizontal className="w-5 h-5" />
+                        </Button>
+                    </PopoverTrigger>
+                    <PopoverContent
+                        align="end"
+                        className="flex flex-row gap-1 p-1 px-1 bg-background rounded-lg shadow-xl shadow-black/20 min-w-[fit-content] items-center w-[fit-content]"
+                    >
+                        {overflowGroups.map((group, groupIdx) => (
+                            <React.Fragment key={group.key}>
+                                {groupIdx > 0 && <InputSeparator />}
+                                <div className="flex items-center gap-0.5">
+                                    {group.components.map((comp, idx) => (
+                                        <React.Fragment key={idx}>{comp}</React.Fragment>
+                                    ))}
+                                </div>
+                            </React.Fragment>
+                        ))}
+                    </PopoverContent>
+                </Popover>
+            )}
+        </div>
     );
-};
+});
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/border.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/border.tsx
index cc9f6700e7..4aa563442d 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/border.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/border.tsx
@@ -15,8 +15,9 @@ import { HoverOnlyTooltip } from "../hover-tooltip";
 import { InputColor } from "../inputs/input-color";
 import { InputRange } from "../inputs/input-range";
 import { SpacingInputs } from "../inputs/spacing-inputs";
+import { observer } from "mobx-react-lite";
 
-export const Border = () => {
+export const Border = observer(() => {
     const editorEngine = useEditorEngine();
     const initialColor = editorEngine.style.selectedStyle?.styles.computed.borderColor;
     const [activeTab, setActiveTab] = useState('all');
@@ -133,4 +134,4 @@ export const Border = () => {
             </DropdownMenuContent>
         </DropdownMenu>
     );
-};
+});
\ No newline at end of file
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/color-background.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/color-background.tsx
index 202d597e39..cc2531ce2d 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/color-background.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/color-background.tsx
@@ -6,12 +6,13 @@ import { Popover, PopoverContent, PopoverTrigger } from '@onlook/ui/popover';
 import { useMemo } from 'react';
 import { ColorPickerContent } from '../inputs/color-picker';
 import { useColorUpdate } from '../hooks/use-color-update';
+import { observer } from 'mobx-react-lite';
 
 interface ColorBackgroundProps {
     className?: string;
 }
 
-export const ColorBackground = ({ className }: ColorBackgroundProps) => {
+export const ColorBackground = observer(({ className }: ColorBackgroundProps) => {
     const editorEngine = useEditorEngine();
     const initialColor = editorEngine.style.selectedStyle?.styles.computed.backgroundColor;
 
@@ -64,4 +65,4 @@ export const ColorBackground = ({ className }: ColorBackgroundProps) => {
             </Popover>
         </div>
     );
-};
+});
\ No newline at end of file
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/display/index.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/display/index.tsx
index b71e129344..2437e5fa17 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/display/index.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/display/index.tsx
@@ -10,6 +10,7 @@ import { HorizontalAlignInput, VerticalAlignInput } from './align';
 import { DirectionInput } from './direction';
 import { GapInput } from './gap';
 import { TypeInput } from './type';
+import { observer } from 'mobx-react-lite';
 
 export interface CssValue {
     value: string;
@@ -23,7 +24,7 @@ export const layoutTypeOptions: Record<string, CssValue> = {
     grid: { value: "grid", label: "Grid" },
 };
 
-export const Display = () => {
+export const Display = observer(() => {
     const editorEngine = useEditorEngine();
     const [layoutType, setLayoutType] = useState(
         editorEngine.style.selectedStyle?.styles.computed.display ?? 'block',
@@ -60,4 +61,4 @@ export const Display = () => {
             </DropdownMenuContent>
         </DropdownMenu>
     );
-};
+});
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/height.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/height.tsx
index 00741a2068..7649db712c 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/height.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/height.tsx
@@ -4,11 +4,12 @@ import { Button } from '@onlook/ui/button';
 import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from '@onlook/ui/dropdown-menu';
 import { Icons } from '@onlook/ui/icons';
 import { LayoutMode } from '@onlook/utility';
+import { observer } from 'mobx-react-lite';
 import { useDimensionControl } from '../hooks/use-dimension-control';
 import { HoverOnlyTooltip } from '../hover-tooltip';
 import { InputDropdown } from '../inputs/input-dropdown';
 
-export const Height = () => {
+export const Height = observer(() => {
     const { dimensionState, handleDimensionChange, handleUnitChange, handleLayoutChange } =
         useDimensionControl('height');
 
@@ -22,16 +23,9 @@ export const Height = () => {
                         className="text-muted-foreground border-border/0 hover:bg-background-tertiary/20 hover:border-border data-[state=open]:bg-background-tertiary/20 data-[state=open]:border-border flex cursor-pointer items-center gap-1 border hover:border hover:text-white focus:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:outline-none active:border-0 data-[state=open]:border data-[state=open]:text-white"
                     >
                         <Icons.Height className="h-4 min-h-4 w-4 min-w-4" />
-                        {(dimensionState.height.unit === 'px'
-                            ? dimensionState.height.num !== undefined
-                            : (dimensionState.height.value && dimensionState.height.value !== "auto")
-                        ) && (
-                                <span className="text-small">
-                                    {dimensionState.height.unit === 'px'
-                                        ? Math.round(dimensionState.height.num ?? 0)
-                                        : dimensionState.height.value}
-                                </span>
-                            )}
+                        <span className="text-small">
+                            {dimensionState.height.value}
+                        </span>
                     </Button>
                 </DropdownMenuTrigger>
             </HoverOnlyTooltip>
@@ -43,7 +37,7 @@ export const Height = () => {
                     <div className="flex items-center justify-between">
                         <span className="text-muted-white text-sm">Height</span>
                         <InputDropdown
-                            value={dimensionState.height.num?.toString() ?? '--'}
+                            value={dimensionState.height.num ?? 0}
                             unit={dimensionState.height.unit}
                             dropdownValue={dimensionState.height.dropdownValue}
                             dropdownOptions={Object.values(LayoutMode)}
@@ -55,7 +49,7 @@ export const Height = () => {
                     <div className="flex items-center justify-between">
                         <span className="text-muted-foreground text-sm">Min</span>
                         <InputDropdown
-                            value={dimensionState.minHeight.num?.toString() ?? '--'}
+                            value={dimensionState.minHeight.num ?? 0}
                             unit={dimensionState.minHeight.unit}
                             dropdownValue={dimensionState.minHeight.dropdownValue}
                             dropdownOptions={Object.values(LayoutMode)}
@@ -67,7 +61,7 @@ export const Height = () => {
                     <div className="flex items-center justify-between">
                         <span className="text-muted-foreground text-sm">Max</span>
                         <InputDropdown
-                            value={dimensionState.maxHeight.num?.toString() ?? '--'}
+                            value={dimensionState.maxHeight.num ?? 0}
                             unit={dimensionState.maxHeight.unit}
                             dropdownValue={dimensionState.maxHeight.dropdownValue}
                             dropdownOptions={Object.values(LayoutMode)}
@@ -80,4 +74,4 @@ export const Height = () => {
             </DropdownMenuContent>
         </DropdownMenu>
     );
-};
+});
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/margin.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/margin.tsx
index 8c518b6ca0..5d2070a9f9 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/margin.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/margin.tsx
@@ -12,10 +12,10 @@ import { useBoxControl } from "../hooks/use-box-control";
 import { HoverOnlyTooltip } from "../hover-tooltip";
 import { InputRange } from "../inputs/input-range";
 import { SpacingInputs } from "../inputs/spacing-inputs";
+import { observer } from "mobx-react-lite";
 
-export const Margin = () => {
+export const Margin = observer(() => {
     const [activeTab, setActiveTab] = useState("all");
-    const [hovered, setHovered] = useState(false);
     const { boxState, handleBoxChange, handleUnitChange, handleIndividualChange } = useBoxControl('margin');
 
     return (
@@ -83,4 +83,4 @@ export const Margin = () => {
             </DropdownMenuContent>
         </DropdownMenu>
     );
-};
+});
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/opacity.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/opacity.tsx
index fce799c21d..0eebdf96ee 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/opacity.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/opacity.tsx
@@ -5,6 +5,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigge
 import { Input } from "@onlook/ui/input";
 import { useEffect, useRef, useState } from "react";
 import { HoverOnlyTooltip } from "../hover-tooltip";
+import { observer } from "mobx-react-lite";
 
 const OPACITY_PRESETS = [100, 80, 75, 50, 25, 10, 0];
 
@@ -37,7 +38,7 @@ const useOpacityControl = () => {
     return { opacity, handleOpacityChange };
 };
 
-export const Opacity = () => {
+export const Opacity = observer(() => {
     const { opacity, handleOpacityChange } = useOpacityControl();
     const inputRef = useRef<HTMLInputElement>(null);
 
@@ -92,4 +93,4 @@ export const Opacity = () => {
             </DropdownMenuContent>
         </DropdownMenu>
     );
-};
+});
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/padding.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/padding.tsx
index 753dbf8a16..6d9a06c1df 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/padding.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/padding.tsx
@@ -12,8 +12,9 @@ import { useBoxControl } from "../hooks/use-box-control";
 import { HoverOnlyTooltip } from "../hover-tooltip";
 import { InputRange } from "../inputs/input-range";
 import { SpacingInputs } from "../inputs/spacing-inputs";
+import { observer } from "mobx-react-lite";
 
-export const Padding = () => {
+export const Padding = observer(() => {
     const [activeTab, setActiveTab] = useState('all');
     const { boxState, handleBoxChange, handleUnitChange, handleIndividualChange } = useBoxControl('padding');
 
@@ -79,4 +80,4 @@ export const Padding = () => {
             </DropdownMenuContent>
         </DropdownMenu>
     );
-};
+});
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/radius.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/radius.tsx
index 2649e55f66..54cb7674ba 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/radius.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/radius.tsx
@@ -7,13 +7,14 @@ import {
     DropdownMenuTrigger,
 } from "@onlook/ui/dropdown-menu";
 import { Icons } from "@onlook/ui/icons";
+import { observer } from "mobx-react-lite";
 import { useState } from "react";
 import { useBoxControl } from "../hooks/use-box-control";
 import { HoverOnlyTooltip } from "../hover-tooltip";
 import { InputRange } from "../inputs/input-range";
 import { SpacingInputs } from "../inputs/spacing-inputs";
 
-export const Radius = () => {
+export const Radius = observer(() => {
     const [activeTab, setActiveTab] = useState('all');
     const { boxState, handleBoxChange, handleUnitChange, handleIndividualChange } = useBoxControl('radius');
 
@@ -79,4 +80,4 @@ export const Radius = () => {
             </DropdownMenuContent>
         </DropdownMenu>
     );
-};
+});
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/width.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/width.tsx
index ab43728b53..e6341e465f 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/width.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/width.tsx
@@ -4,11 +4,12 @@ import { Button } from '@onlook/ui/button';
 import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from '@onlook/ui/dropdown-menu';
 import { Icons } from '@onlook/ui/icons';
 import { LayoutMode } from '@onlook/utility';
+import { observer } from 'mobx-react-lite';
 import { useDimensionControl } from '../hooks/use-dimension-control';
 import { HoverOnlyTooltip } from '../hover-tooltip';
 import { InputDropdown } from '../inputs/input-dropdown';
 
-export const Width = () => {
+export const Width = observer(() => {
     const { dimensionState, handleDimensionChange, handleUnitChange, handleLayoutChange } =
         useDimensionControl('width');
 
@@ -22,16 +23,9 @@ export const Width = () => {
                         className="text-muted-foreground border-border/0 hover:bg-background-tertiary/20 hover:border-border data-[state=open]:bg-background-tertiary/20 data-[state=open]:border-border flex cursor-pointer items-center gap-1 border hover:border hover:text-white focus:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:outline-none active:border-0 data-[state=open]:border data-[state=open]:text-white"
                     >
                         <Icons.Width className="h-4 w-4 min-h-4 min-w-4" />
-                        {(dimensionState.width.unit === 'px'
-                            ? dimensionState.width.num !== undefined
-                            : (dimensionState.width.value && dimensionState.width.value !== "auto")
-                        ) && (
-                                <span className="text-small">
-                                    {dimensionState.width.unit === 'px'
-                                        ? Math.round(dimensionState.width.num ?? 0)
-                                        : dimensionState.width.value}
-                                </span>
-                            )}
+                        <span className="text-small">
+                            {dimensionState.width.value}
+                        </span>
                     </Button>
                 </DropdownMenuTrigger>
             </HoverOnlyTooltip>
@@ -40,7 +34,7 @@ export const Width = () => {
                     <div className="flex items-center justify-between">
                         <span className="text-sm text-muted-white">Width</span>
                         <InputDropdown
-                            value={dimensionState.width.num?.toString() ?? '--'}
+                            value={dimensionState.width.num ?? 0}
                             unit={dimensionState.width.unit}
                             dropdownValue={dimensionState.width.dropdownValue}
                             dropdownOptions={Object.values(LayoutMode)}
@@ -52,7 +46,7 @@ export const Width = () => {
                     <div className="flex items-center justify-between">
                         <span className="text-sm text-muted-foreground">Min</span>
                         <InputDropdown
-                            value={dimensionState.minWidth.num?.toString() ?? '--'}
+                            value={dimensionState.minWidth.num ?? 0}
                             unit={dimensionState.minWidth.unit}
                             dropdownValue={dimensionState.minWidth.dropdownValue}
                             dropdownOptions={Object.values(LayoutMode)}
@@ -64,7 +58,7 @@ export const Width = () => {
                     <div className="flex items-center justify-between">
                         <span className="text-sm text-muted-foreground">Max</span>
                         <InputDropdown
-                            value={dimensionState.maxWidth.num?.toString() ?? '--'}
+                            value={dimensionState.maxWidth.num ?? 0}
                             unit={dimensionState.maxWidth.unit}
                             dropdownValue={dimensionState.maxWidth.dropdownValue}
                             dropdownOptions={Object.values(LayoutMode)}
@@ -77,4 +71,4 @@ export const Width = () => {
             </DropdownMenuContent>
         </DropdownMenu>
     );
-};
+})
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/hooks/use-dimension-control.ts b/apps/web/client/src/app/project/[id]/_components/editor-bar/hooks/use-dimension-control.ts
index 516c3bb70e..69deeb892d 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/hooks/use-dimension-control.ts
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/hooks/use-dimension-control.ts
@@ -113,8 +113,8 @@ export const useDimensionControl = <T extends DimensionType>(dimension: T) => {
         setDimensionState(getInitialState());
     }, [getInitialState]);
 
-    const handleDimensionChange = useCallback((property: DimensionProperty<T>, value: string) => {
-        const parsedValue = value === '--' ? undefined : value;
+    const handleDimensionChange = useCallback((property: DimensionProperty<T>, value: number) => {
+        const parsedValue =  value;
         const currentState = dimensionState[property];
 
         if (!currentState) return;
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/hooks/use-input-control.ts b/apps/web/client/src/app/project/[id]/_components/editor-bar/hooks/use-input-control.ts
new file mode 100644
index 0000000000..b7c6266a35
--- /dev/null
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/hooks/use-input-control.ts
@@ -0,0 +1,48 @@
+import { useEffect, useMemo, useState } from "react";
+import type { KeyboardEvent } from 'react';
+import { debounce } from "lodash";
+
+export const useInputControl = (value: number, onChange?: (value: number) => void) => {
+    const [localValue, setLocalValue] = useState<number>(value);
+
+    useEffect(() => {
+        setLocalValue(value);
+    }, [value]);
+
+    const handleIncrement = (step: number) => {
+        if (!isNaN(localValue)) {
+            const newValue = (localValue + step);
+            handleChange(newValue);
+        }
+    };
+
+    const handleChange = (value: number) => {
+        setLocalValue(value);
+        debouncedOnChange(value);
+    }
+
+    const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
+        if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
+            e.preventDefault();
+            const step = e.shiftKey ? 10 : 1;
+            const direction = e.key === 'ArrowUp' ? 1 : -1;
+            handleIncrement(step * direction);
+        }
+    };
+
+    const debouncedOnChange = useMemo(
+        () => debounce((newValue: number) => {
+            onChange?.(newValue);
+        }, 500),
+        [onChange]
+    );
+
+
+    useEffect(() => {
+        return () => {
+            debouncedOnChange.cancel();
+        };
+    }, [debouncedOnChange]);
+
+    return { localValue, handleKeyDown, handleChange };
+}
\ No newline at end of file
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/hooks/use-measure-group.ts b/apps/web/client/src/app/project/[id]/_components/editor-bar/hooks/use-measure-group.ts
new file mode 100644
index 0000000000..b5dab56157
--- /dev/null
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/hooks/use-measure-group.ts
@@ -0,0 +1,60 @@
+import { useCallback, useEffect, useState } from 'react';
+
+// Pre-calculated approximate widths for each group type
+const GROUP_WIDTHS = {
+    // Div groups
+    dimensions: 160, // Width + Height
+    base: 180, // Color + Border + Radius
+    layout: 180, // Display + Padding + Margin
+    typography: 240, // Font Family + Weight + Size
+    opacity: 80, // Opacity
+
+    // Text groups (wider due to more components)
+    'text-typography': 360, // Font Family + Weight + Size + Color + Align + Advanced
+    'text-dimensions': 160, // Width + Height
+    'text-base': 180, // Color + Border + Radius
+    'text-layout': 180, // Display + Padding + Margin
+    'text-opacity': 80, // Opacity
+};
+
+export const useMeasureGroup = ({ availableWidth = 0, count = 0 }: { availableWidth?: number, count?: number }) => {
+    const [visibleCount, setVisibleCount] = useState(count);
+
+    // Update visible count based on available width
+    const updateVisibleCount = useCallback(() => {
+        if (!availableWidth) return;
+
+        const OVERFLOW_BUTTON_WIDTH = 32;
+        const SEPARATOR_WIDTH = 8;
+        let used = 0;
+        let count = 0;
+
+        // Get all group keys in order
+        const groupKeys = Object.keys(GROUP_WIDTHS);
+
+        for (let i = 0; i < groupKeys.length; i++) {
+            const width = GROUP_WIDTHS[groupKeys[i] as keyof typeof GROUP_WIDTHS];
+            
+            // Add separator width if this isn't the first group
+            const totalWidth = width + (count > 0 ? SEPARATOR_WIDTH : 0);
+
+            if (used + totalWidth <= availableWidth - OVERFLOW_BUTTON_WIDTH) {
+                used += totalWidth;
+                count++;
+            } else {
+                break;
+            }
+        }
+
+        setVisibleCount(count);
+    }, [availableWidth]);
+
+    // Update visible count when available width changes
+    useEffect(() => {
+        updateVisibleCount();
+    }, [updateVisibleCount]);
+
+    return {
+        visibleCount,
+    };
+};
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/inputs/input-dropdown.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/inputs/input-dropdown.tsx
index 2489dd05ad..40057b6096 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/inputs/input-dropdown.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/inputs/input-dropdown.tsx
@@ -8,9 +8,7 @@ import {
     DropdownMenuTrigger,
 } from '@onlook/ui/dropdown-menu';
 import { Icons } from '@onlook/ui/icons';
-import { debounce } from 'lodash';
-import { useEffect, useMemo, useState } from 'react';
-import type { KeyboardEvent } from 'react';
+import { useInputControl } from '../hooks/use-input-control';
 
 const UNITS = ['PX', '%', 'EM', 'REM'];
 
@@ -20,11 +18,11 @@ const OPTION_OVERRIDES: Record<string, string | undefined> = {
 };
 
 interface InputDropdownProps {
-    value: string;
+    value: number;
     unit?: string;
     dropdownValue: string;
     dropdownOptions?: string[];
-    onChange?: (value: string) => void;
+    onChange?: (value: number) => void;
     onDropdownChange?: (value: string) => void;
     onUnitChange?: (value: string) => void;
 }
@@ -38,42 +36,7 @@ export const InputDropdown = ({
     onDropdownChange,
     onUnitChange,
 }: InputDropdownProps) => {
-    const [localValue, setLocalValue] = useState(value);
-
-    useEffect(() => {
-        setLocalValue(value);
-    }, [value]);
-
-    const debouncedOnChange = useMemo(
-        () => debounce((newValue: string) => {
-            onChange?.(newValue);
-        }, 500),
-        [onChange]
-    );
-
-    useEffect(() => {
-        return () => {
-            debouncedOnChange.cancel();
-        };
-    }, [debouncedOnChange]);
-
-    const handleIncrement = (step: number) => {
-        const currentValue = parseFloat(localValue);
-        if (!isNaN(currentValue)) {
-            const newValue = (currentValue + step).toString();
-            setLocalValue(newValue);
-            debouncedOnChange(newValue);
-        }
-    };
-
-    const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
-        if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
-            e.preventDefault();
-            const step = e.shiftKey ? 10 : 1;
-            const direction = e.key === 'ArrowUp' ? 1 : -1;
-            handleIncrement(step * direction);
-        }
-    };
+    const { localValue, handleKeyDown, handleChange } = useInputControl(value, onChange);
 
     return (
         <div className="flex items-center">
@@ -81,11 +44,7 @@ export const InputDropdown = ({
                 <input
                     type="text"
                     value={localValue}
-                    onChange={(e) => {
-                        const newValue = e.target.value;
-                        setLocalValue(newValue);
-                        debouncedOnChange(newValue);
-                    }}
+                    onChange={(e) => handleChange(Number(e.target.value))}
                     onKeyDown={handleKeyDown}
                     className="w-[32px] bg-transparent text-sm text-white focus:outline-none text-left"
                     aria-label="Value input"
@@ -114,7 +73,9 @@ export const InputDropdown = ({
                         className="h-[36px] bg-background-tertiary/50 hover:bg-background-tertiary/70 hover:text-white rounded-l-none rounded-r-md ml-[1px] px-2.5 flex items-center justify-between w-[72px] cursor-pointer transition-colors"
                     >
                         <div className="flex items-center gap-2">
-                            <span className="text-sm text-muted-foreground group-hover:text-white transition-colors">{OPTION_OVERRIDES[dropdownValue] ?? dropdownValue}</span>
+                            <span className="text-sm text-muted-foreground group-hover:text-white transition-colors">
+                                {OPTION_OVERRIDES[dropdownValue] ?? dropdownValue}
+                            </span>
                         </div>
                         <Icons.ChevronDown className="h-4 w-4 min-h-4 min-w-4 text-muted-foreground group-hover:text-white transition-colors" />
                     </Button>
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/inputs/input-icon.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/inputs/input-icon.tsx
index 0883f6449f..7146fffb72 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/inputs/input-icon.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/inputs/input-icon.tsx
@@ -6,6 +6,7 @@ import {
 } from '@onlook/ui/dropdown-menu';
 import { Icons } from '@onlook/ui/icons';
 import { useState } from 'react';
+import { useInputControl } from '../hooks/use-input-control';
 
 const UNITS = ['px', '%', 'rem', 'vw', 'vh'];
 type Unit = (typeof UNITS)[number];
@@ -29,25 +30,9 @@ interface InputIconProps {
 }
 
 export const InputIcon = ({ value, unit = 'px', icon, onChange, onUnitChange }: InputIconProps) => {
-    const [inputValue, setInputValue] = useState(value.toString());
     const [unitValue, setUnitValue] = useState(unit);
+    const { localValue, handleKeyDown, handleChange } = useInputControl(value, onChange);
 
-    const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
-        const newValue = e.target.value;
-        setInputValue(newValue);
-
-        const numericValue = parseFloat(newValue);
-        if (!isNaN(numericValue) && onChange) {
-            onChange(numericValue);
-        }
-    };
-
-    const handleBlur = () => {
-        const numericValue = parseFloat(inputValue);
-        if (isNaN(numericValue)) {
-            setInputValue(value.toString());
-        }
-    };
 
     const IconComponent = icon ? Icons[icon] : null;
 
@@ -61,9 +46,9 @@ export const InputIcon = ({ value, unit = 'px', icon, onChange, onUnitChange }:
                     type="text"
                     inputMode="numeric"
                     pattern="[0-9]*"
-                    value={inputValue}
-                    onChange={handleChange}
-                    onBlur={handleBlur}
+                    value={localValue}
+                    onChange={(e) => handleChange(Number(e.target.value))}
+                    onKeyDown={handleKeyDown}
                     className="w-full bg-transparent text-sm text-white focus:outline-none uppercase hover:text-white"
                 />
 
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/inputs/spacing-inputs.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/inputs/spacing-inputs.tsx
index 1aaba10d47..1c3c418fb2 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/inputs/spacing-inputs.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/inputs/spacing-inputs.tsx
@@ -1,4 +1,5 @@
 import type { BoxType } from '../hooks/use-box-control';
+import { useInputControl } from '../hooks/use-input-control';
 import { InputIcon } from './input-icon';
 
 type IconType =
@@ -47,8 +48,6 @@ const getIconNames = (type: BoxType): IconMap => {
 
 export const SpacingInputs = ({ type, values, onChange }: SpacingInputsProps) => {
     const icons = getIconNames(type);
-    console.log(values);
-    
     const positions =
         type === 'radius'
             ? ['topLeft', 'topRight', 'bottomLeft', 'bottomRight']
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/panels/layout-position.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/panels/layout-position.tsx
index 3c1cb648c1..8c92f01365 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/panels/layout-position.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/panels/layout-position.tsx
@@ -28,8 +28,8 @@ export function LayoutPosition({ className }: LayoutPositionProps) {
         'left' | 'center' | 'right' | 'space-between'
     >('center');
     const [gap, setGap] = useState('12');
-    const [width, setWidth] = useState('100');
-    const [height, setHeight] = useState('228');
+    const [width, setWidth] = useState(100);
+    const [height, setHeight] = useState(228);
     const [positionType, setPositionType] = useState('Relative');
     const [showMarginInputs, setShowMarginInputs] = useState(false);
     const [showPaddingInputs, setShowPaddingInputs] = useState(false);
@@ -254,7 +254,7 @@ export function LayoutPosition({ className }: LayoutPositionProps) {
                             <span className="text-sm text-muted-foreground w-24">Min</span>
                             <div className="flex-1">
                                 <InputDropdown
-                                    value="--"
+                                    value={0}
                                     unit="-"
                                     dropdownValue="Rel"
                                     dropdownOptions={['Rel', 'PX', 'VW']}
@@ -267,7 +267,7 @@ export function LayoutPosition({ className }: LayoutPositionProps) {
                             <span className="text-sm text-muted-foreground w-24">Max</span>
                             <div className="flex-1">
                                 <InputDropdown
-                                    value="--"
+                                    value={0}
                                     unit="-"
                                     dropdownValue="Rel"
                                     dropdownOptions={['Rel', 'PX', 'VW']}
@@ -294,7 +294,7 @@ export function LayoutPosition({ className }: LayoutPositionProps) {
                             <span className="text-sm text-muted-foreground w-24">Min</span>
                             <div className="flex-1">
                                 <InputDropdown
-                                    value="--"
+                                    value={0}
                                     unit="-"
                                     dropdownValue="Rel"
                                     dropdownOptions={['Rel', 'PX', 'VH']}
@@ -307,7 +307,7 @@ export function LayoutPosition({ className }: LayoutPositionProps) {
                             <span className="text-sm text-muted-foreground w-24">Max</span>
                             <div className="flex-1">
                                 <InputDropdown
-                                    value="--"
+                                    value={0}
                                     unit="-"
                                     dropdownValue="Rel"
                                     dropdownOptions={['Rel', 'PX', 'VH']}
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font-weight.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font-weight.tsx
deleted file mode 100644
index c3b033c945..0000000000
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font-weight.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-'use client';
-
-import { VARIANTS } from '@onlook/fonts';
-import { Button } from '@onlook/ui/button';
-import {
-    DropdownMenu,
-    DropdownMenuContent,
-    DropdownMenuItem,
-    DropdownMenuTrigger,
-} from '@onlook/ui/dropdown-menu';
-import { Icons } from '@onlook/ui/icons';
-import { Tooltip, TooltipContent, TooltipTrigger } from '@onlook/ui/tooltip';
-import { convertFontWeight } from '@onlook/utility';
-import { memo } from 'react';
-
-export const FontWeightSelector = memo(
-    ({
-        fontWeight,
-        handleFontWeightChange,
-    }: {
-        fontWeight: string;
-        handleFontWeightChange: (weight: string) => void;
-    }) => (
-        <DropdownMenu>
-            <Tooltip>
-                <div>
-                    <TooltipTrigger asChild>
-                        <DropdownMenuTrigger asChild>
-                            <Button
-                                variant="ghost"
-                                size="toolbar"
-                                className="text-muted-foreground border-border/0 hover:bg-background-tertiary/20 hover:border-border data-[state=open]:bg-background-tertiary/20 data-[state=open]:border-border flex w-24 cursor-pointer items-center justify-start gap-2 rounded-lg border px-3 hover:border hover:text-white focus:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:outline-none active:border-0 data-[state=open]:border data-[state=open]:text-white"
-                            >
-                                <span className="text-smallPlus">
-                                    {convertFontWeight(fontWeight)}
-                                </span>
-                            </Button>
-                        </DropdownMenuTrigger>
-                    </TooltipTrigger>
-                    <TooltipContent side="bottom" className="mt-1" hideArrow>
-                        Font Weight
-                    </TooltipContent>
-                </div>
-            </Tooltip>
-            <DropdownMenuContent align="center" className="mt-1 min-w-[120px] rounded-lg p-1">
-                {VARIANTS.map((weight) => (
-                    <DropdownMenuItem
-                        key={weight.value}
-                        onClick={() => handleFontWeightChange(weight.value)}
-                        className={`text-muted-foreground data-[highlighted]:bg-background-tertiary/10 border-border/0 data-[highlighted]:border-border flex items-center justify-between rounded-md border px-2 py-1.5 text-sm data-[highlighted]:text-white cursor-pointer transition-colors duration-150 hover:bg-background-tertiary/20 hover:text-foreground ${fontWeight === weight.value
-                            ? 'bg-background-tertiary/20 border-border border text-white'
-                            : ''
-                            }`}
-                    >
-                        {weight.name}
-                        {fontWeight === weight.value && (
-                            <Icons.Check className="ml-2 h-4 w-4 text-foreground-primary" />
-                        )}
-                    </DropdownMenuItem>
-                ))}
-            </DropdownMenuContent>
-        </DropdownMenu>
-    ),
-);
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font-family.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font/font-family-selector.tsx
similarity index 84%
rename from apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font-family.tsx
rename to apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font/font-family-selector.tsx
index 3c3f04d23d..ca286dc0bc 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font-family.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font/font-family-selector.tsx
@@ -1,7 +1,6 @@
 'use client';
 
 import { useEditorEngine } from '@/components/store/editor';
-import { VARIANTS } from '@onlook/fonts';
 import { BrandTabValue, LeftPanelTabValue } from '@onlook/models';
 import type { Font } from '@onlook/models/assets';
 import { Button } from '@onlook/ui/button';
@@ -9,16 +8,17 @@ import { Icons } from '@onlook/ui/icons';
 import { Popover, PopoverContent, PopoverTrigger } from '@onlook/ui/popover';
 import { Tooltip, TooltipContent, TooltipTrigger } from '@onlook/ui/tooltip';
 import { toNormalCase } from '@onlook/utility';
-import { memo, useEffect, useState } from 'react';
-import { FontFamily } from '../../left-panel/brand-tab/font-panel/font-family';
-import { useTextControl } from '../hooks/use-text-control';
+import { observer } from 'mobx-react-lite';
+import { useEffect, useState } from 'react';
+import { useTextControl } from '../../hooks/use-text-control';
+import { FontFamily } from './font-family';
 
-export const FontFamilySelector = memo(({ fontFamily }: { fontFamily: string }) => {
+export const FontFamilySelector = observer(() => {
     const editorEngine = useEditorEngine();
     const [fonts, setFonts] = useState<Font[]>([]);
     const [search, setSearch] = useState('');
     const [open, setOpen] = useState(false);
-    const { handleFontFamilyChange } = useTextControl();
+    const { handleFontFamilyChange, textState } = useTextControl();
 
     useEffect(() => {
         (async () => {
@@ -64,7 +64,9 @@ export const FontFamilySelector = memo(({ fontFamily }: { fontFamily: string })
                             tabIndex={0}
                             onClick={handleClose}
                         >
-                            <span className="truncate text-sm">{toNormalCase(fontFamily)}</span>
+                            <span className="truncate text-sm">
+                                {toNormalCase(textState.fontFamily)}
+                            </span>
                         </Button>
                     </PopoverTrigger>
                 </TooltipTrigger>
@@ -110,17 +112,8 @@ export const FontFamilySelector = memo(({ fontFamily }: { fontFamily: string })
                             <div key={font.id} className="py-1">
                                 <FontFamily
                                     name={font.family}
-                                    variants={
-                                        font.weight?.map(
-                                            (weight) =>
-                                                VARIANTS.find((v) => v.value === weight)?.name ||
-                                                weight,
-                                        ) as string[]
-                                    }
-                                    showDropdown={false}
-                                    showAddButton={false}
-                                    isDefault={fontFamily === font.family}
                                     onSetFont={() => handleFontFamilyChange(font)}
+                                    isActive={textState.fontFamily.toLowerCase() === font.id.toLowerCase()}
                                 />
                             </div>
                         ))
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font/font-family.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font/font-family.tsx
new file mode 100644
index 0000000000..15d9ced5ed
--- /dev/null
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font/font-family.tsx
@@ -0,0 +1,24 @@
+'use client';
+
+import { Icons } from '@onlook/ui/icons';
+import { memo } from 'react';
+
+export const FontFamily = memo(
+    ({ name, isActive, onSetFont }: { name: string; isActive: boolean; onSetFont: () => void }) => {
+        return (
+            <div
+                key={name}
+                onClick={onSetFont}
+                className={`text-muted-foreground data-[highlighted]:bg-background-tertiary/10 border-border/0 data-[highlighted]:border-border flex items-center justify-between rounded-md border px-2 py-1.5 text-sm data-[highlighted]:text-white cursor-pointer transition-colors duration-150 hover:bg-background-tertiary/20 hover:text-foreground ${isActive
+                    ? 'bg-background-tertiary/20 border-border border text-white'
+                    : ''
+                    }`}
+            >
+                <span className="font-medium" style={{ fontFamily: name }}>
+                    {name}
+                </span>
+                {isActive && <Icons.Check className="ml-2 h-4 w-4 text-foreground-primary" />}
+            </div>
+        );
+    },
+);
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font-size.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font/font-size.tsx
similarity index 92%
rename from apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font-size.tsx
rename to apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font/font-size.tsx
index cc590e361a..d079b68a06 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font-size.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font/font-size.tsx
@@ -4,23 +4,20 @@ import { Button } from '@onlook/ui/button';
 import { Icons } from '@onlook/ui/icons';
 import { Popover, PopoverContent, PopoverTrigger } from '@onlook/ui/popover';
 import { Tooltip, TooltipContent, TooltipTrigger } from '@onlook/ui/tooltip';
-import { memo, useRef, useState } from 'react';
+import { observer } from 'mobx-react-lite';
+import { useRef, useState } from 'react';
+import { useTextControl } from '../../hooks/use-text-control';
 
 const FONT_SIZES = [12, 14, 16, 18, 20, 24, 30, 36, 48, 60, 72, 96];
 
-export const FontSizeSelector = memo(
-    ({
-        fontSize,
-        handleFontSizeChange,
-    }: {
-        fontSize: number;
-        handleFontSizeChange: (size: number) => void;
-    }) => {
+export const FontSizeSelector = observer(
+    () => {
         const [open, setOpen] = useState(false);
         const inputRef = useRef<HTMLInputElement>(null);
+        const { handleFontSizeChange, textState } = useTextControl();
 
         const adjustFontSize = (amount: number) => {
-            handleFontSizeChange(Math.max(1, fontSize + amount));
+            handleFontSizeChange(Math.max(1, textState.fontSize + amount));
         };
 
         const handleInputClick = () => {
@@ -50,7 +47,7 @@ export const FontSizeSelector = memo(
                                     <input
                                         ref={inputRef}
                                         type="number"
-                                        value={fontSize}
+                                        value={textState.fontSize}
                                         onChange={(e) => {
                                             const value = parseInt(e.target.value);
                                             if (!isNaN(value) && value > 0) {
@@ -89,7 +86,7 @@ export const FontSizeSelector = memo(
                                     handleFontSizeChange(size);
                                     setOpen(false);
                                 }}
-                                className={`cursor-pointer text-muted-foreground data-[highlighted]:bg-background-tertiary/10 border-border/0 data-[highlighted]:border-border justify-center rounded-md border px-2 py-1 text-sm data-[highlighted]:text-white ${size === fontSize
+                                className={`cursor-pointer text-muted-foreground data-[highlighted]:bg-background-tertiary/10 border-border/0 data-[highlighted]:border-border justify-center rounded-md border px-2 py-1 text-sm data-[highlighted]:text-white ${size === textState.fontSize
                                     ? 'bg-background-tertiary/20 border-border border text-white'
                                     : ''
                                     }`}
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font/font-weight.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font/font-weight.tsx
new file mode 100644
index 0000000000..857ef1a0e6
--- /dev/null
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font/font-weight.tsx
@@ -0,0 +1,62 @@
+'use client';
+
+import { VARIANTS } from '@onlook/fonts';
+import { Button } from '@onlook/ui/button';
+import {
+    DropdownMenu,
+    DropdownMenuContent,
+    DropdownMenuItem,
+    DropdownMenuTrigger,
+} from '@onlook/ui/dropdown-menu';
+import { Icons } from '@onlook/ui/icons';
+import { Tooltip, TooltipContent, TooltipTrigger } from '@onlook/ui/tooltip';
+import { convertFontWeight } from '@onlook/utility';
+import { observer } from 'mobx-react-lite';
+import { useTextControl } from '../../hooks/use-text-control';
+
+export const FontWeightSelector = observer(
+    () => {
+        const { handleFontWeightChange, textState } = useTextControl();
+        return (
+            <DropdownMenu>
+                <Tooltip>
+                    <div>
+                        <TooltipTrigger asChild>
+                            <DropdownMenuTrigger asChild>
+                                <Button
+                                    variant="ghost"
+                                    size="toolbar"
+                                    className="text-muted-foreground border-border/0 hover:bg-background-tertiary/20 hover:border-border data-[state=open]:bg-background-tertiary/20 data-[state=open]:border-border flex w-24 cursor-pointer items-center justify-start gap-2 rounded-lg border px-3 hover:border hover:text-white focus:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:outline-none active:border-0 data-[state=open]:border data-[state=open]:text-white"
+                                >
+                                    <span className="text-smallPlus">
+                                        {convertFontWeight(textState.fontWeight)}
+                                    </span>
+                                </Button>
+                            </DropdownMenuTrigger>
+                        </TooltipTrigger>
+                        <TooltipContent side="bottom" className="mt-1" hideArrow>
+                            Font Weight
+                        </TooltipContent>
+                    </div>
+                </Tooltip>
+                <DropdownMenuContent align="center" className="mt-1 min-w-[120px] rounded-lg p-1">
+                    {VARIANTS.map((weight) => (
+                        <DropdownMenuItem
+                            key={weight.value}
+                            onClick={() => handleFontWeightChange(weight.value)}
+                            className={`text-muted-foreground data-[highlighted]:bg-background-tertiary/10 border-border/0 data-[highlighted]:border-border flex items-center justify-between rounded-md border px-2 py-1.5 text-sm data-[highlighted]:text-white cursor-pointer transition-colors duration-150 hover:bg-background-tertiary/20 hover:text-foreground ${textState.fontWeight === weight.value
+                                ? 'bg-background-tertiary/20 border-border border text-white'
+                                : ''
+                                }`}
+                        >
+                            {weight.name}
+                            {textState.fontWeight === weight.value && (
+                                <Icons.Check className="ml-2 h-4 w-4 text-foreground-primary" />
+                            )}
+                        </DropdownMenuItem>
+                    ))}
+                </DropdownMenuContent>
+            </DropdownMenu>
+        )
+    }
+);
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/text-align.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/text-align.tsx
index 3896111672..453f28b3c8 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/text-align.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/text-align.tsx
@@ -9,70 +9,67 @@ import {
 } from '@onlook/ui/dropdown-menu';
 import { Icons } from '@onlook/ui/icons';
 import { Tooltip, TooltipContent, TooltipTrigger } from '@onlook/ui/tooltip';
-import { memo } from 'react';
-import { type TextAlign } from '../hooks/use-text-control';
+import { observer } from 'mobx-react-lite';
+import { useTextControl, type TextAlign } from '../hooks/use-text-control';
 
-export const TextAlignSelector = memo(
-    ({
-        textAlign,
-        handleTextAlignChange,
-    }: {
-        textAlign: TextAlign;
-        handleTextAlignChange: (align: TextAlign) => void;
-    }) => (
-        <DropdownMenu>
-            <Tooltip>
-                <div>
-                    <TooltipTrigger asChild>
-                        <DropdownMenuTrigger asChild>
-                            <Button
-                                variant="ghost"
-                                size="toolbar"
-                                className="text-white border-border/0 hover:bg-background-tertiary/20 hover:border-border data-[state=open]:bg-background-tertiary/20 data-[state=open]:border-border flex max-w-9 min-w-9 cursor-pointer items-center justify-center gap-2 rounded-lg border px-2 hover:border hover:text-white focus:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:outline-none active:border-0 data-[state=open]:border data-[state=open]:text-white"
-                            >
-                                {(() => {
-                                    switch (textAlign) {
-                                        case 'center':
-                                            return <Icons.TextAlignCenter className="h-4 w-4" />;
-                                        case 'right':
-                                            return <Icons.TextAlignRight className="h-4 w-4" />;
-                                        case 'justify':
-                                            return <Icons.TextAlignJustified className="h-4 w-4" />;
-                                        case 'left':
-                                        default:
-                                            return <Icons.TextAlignLeft className="h-4 w-4" />;
-                                    }
-                                })()}
-                            </Button>
-                        </DropdownMenuTrigger>
-                    </TooltipTrigger>
-                    <TooltipContent side="bottom" className="mt-1" hideArrow>
-                        Text Align
-                    </TooltipContent>
-                </div>
-            </Tooltip>
-            <DropdownMenuContent
-                align="center"
-                className="mt-1 flex min-w-fit gap-1 rounded-lg p-1"
-            >
-                {[
-                    { value: 'left' as TextAlign, icon: Icons.TextAlignLeft },
-                    { value: 'center' as TextAlign, icon: Icons.TextAlignCenter },
-                    { value: 'right' as TextAlign, icon: Icons.TextAlignRight },
-                    { value: 'justify' as TextAlign, icon: Icons.TextAlignJustified },
-                ].map(({ value, icon: Icon }) => (
-                    <DropdownMenuItem
-                        key={value}
-                        onClick={() => handleTextAlignChange(value)}
-                        className={`text-muted-foreground data-[highlighted]:bg-background-tertiary/10 border-border/0 data-[highlighted]:border-border rounded-md border px-2 py-1.5 data-[highlighted]:text-foreground cursor-pointer transition-colors duration-150 hover:bg-background-tertiary/20 hover:text-foreground ${textAlign === value
-                            ? 'bg-background-tertiary/20 border-border border text-white'
-                            : ''
-                            }`}
-                    >
-                        <Icon className="h-4 w-4" />
-                    </DropdownMenuItem>
-                ))}
-            </DropdownMenuContent>
-        </DropdownMenu>
-    ),
+export const TextAlignSelector = observer(
+    () => {
+        const { handleTextAlignChange, textState } = useTextControl();
+        return (
+            <DropdownMenu>
+                <Tooltip>
+                    <div>
+                        <TooltipTrigger asChild>
+                            <DropdownMenuTrigger asChild>
+                                <Button
+                                    variant="ghost"
+                                    size="toolbar"
+                                    className="text-white border-border/0 hover:bg-background-tertiary/20 hover:border-border data-[state=open]:bg-background-tertiary/20 data-[state=open]:border-border flex max-w-9 min-w-9 cursor-pointer items-center justify-center gap-2 rounded-lg border px-2 hover:border hover:text-white focus:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:outline-none active:border-0 data-[state=open]:border data-[state=open]:text-white"
+                                >
+                                    {(() => {
+                                        switch (textState.textAlign) {
+                                            case 'center':
+                                                return <Icons.TextAlignCenter className="h-4 w-4" />;
+                                            case 'right':
+                                                return <Icons.TextAlignRight className="h-4 w-4" />;
+                                            case 'justify':
+                                                return <Icons.TextAlignJustified className="h-4 w-4" />;
+                                            case 'left':
+                                            default:
+                                                return <Icons.TextAlignLeft className="h-4 w-4" />;
+                                        }
+                                    })()}
+                                </Button>
+                            </DropdownMenuTrigger>
+                        </TooltipTrigger>
+                        <TooltipContent side="bottom" className="mt-1" hideArrow>
+                            Text Align
+                        </TooltipContent>
+                    </div>
+                </Tooltip>
+                <DropdownMenuContent
+                    align="center"
+                    className="mt-1 flex min-w-fit gap-1 rounded-lg p-1"
+                >
+                    {[
+                        { value: 'left' as TextAlign, icon: Icons.TextAlignLeft },
+                        { value: 'center' as TextAlign, icon: Icons.TextAlignCenter },
+                        { value: 'right' as TextAlign, icon: Icons.TextAlignRight },
+                        { value: 'justify' as TextAlign, icon: Icons.TextAlignJustified },
+                    ].map(({ value, icon: Icon }) => (
+                        <DropdownMenuItem
+                            key={value}
+                            onClick={() => handleTextAlignChange(value)}
+                            className={`text-muted-foreground data-[highlighted]:bg-background-tertiary/10 border-border/0 data-[highlighted]:border-border rounded-md border px-2 py-1.5 data-[highlighted]:text-foreground cursor-pointer transition-colors duration-150 hover:bg-background-tertiary/20 hover:text-foreground ${textState.textAlign === value
+                                ? 'bg-background-tertiary/20 border-border border text-white'
+                                : ''
+                                }`}
+                        >
+                            <Icon className="h-4 w-4" />
+                        </DropdownMenuItem>
+                    ))}
+                </DropdownMenuContent>
+            </DropdownMenu>
+        )
+    }
 );
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/text-color.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/text-color.tsx
index d6365d253c..8e7b27104c 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/text-color.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/text-color.tsx
@@ -3,22 +3,19 @@
 import { Icons } from '@onlook/ui/icons';
 import { Popover, PopoverContent, PopoverTrigger } from '@onlook/ui/popover';
 import { Tooltip, TooltipContent, TooltipTrigger } from '@onlook/ui/tooltip';
-import { memo } from 'react';
 import { useColorUpdate } from '../hooks/use-color-update';
 import { ColorPickerContent } from '../inputs/color-picker';
+import { useTextControl } from '../hooks/use-text-control';
+import { observer } from 'mobx-react-lite';
+
+export const TextColor = observer(
+    () => {
+        const { handleTextColorChange, textState } = useTextControl();
 
-export const TextColor = memo(
-    ({
-        handleTextColorChange,
-        textColor,
-    }: {
-        handleTextColorChange: (color: string) => void;
-        textColor: string;
-    }) => {
         const { handleColorUpdate, handleColorUpdateEnd, tempColor } = useColorUpdate({
             elementStyleKey: 'color',
             onValueChange: (_, value) => handleTextColorChange(value),
-            initialColor: textColor,
+            initialColor: textState.textColor,
         });
 
         return (
diff --git a/apps/web/client/src/app/project/[id]/_components/editor-bar/text-selected.tsx b/apps/web/client/src/app/project/[id]/_components/editor-bar/text-selected.tsx
index c0625601bc..9488d99dd0 100644
--- a/apps/web/client/src/app/project/[id]/_components/editor-bar/text-selected.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/editor-bar/text-selected.tsx
@@ -3,7 +3,7 @@
 import { Button } from '@onlook/ui/button';
 import { Icons } from '@onlook/ui/icons';
 import { Popover, PopoverContent, PopoverTrigger } from '@onlook/ui/popover';
-import React, { useCallback, useEffect, useRef, useState } from 'react';
+import React, { useState } from 'react';
 import { Border } from './dropdowns/border';
 import { ColorBackground } from './dropdowns/color-background';
 import { Display } from './dropdowns/display';
@@ -13,32 +13,31 @@ import { Opacity } from './dropdowns/opacity';
 import { Padding } from './dropdowns/padding';
 import { Radius } from './dropdowns/radius';
 import { Width } from './dropdowns/width';
-import { useTextControl } from './hooks/use-text-control';
-import { ViewButtons } from './panels/panel-bar/bar';
+import { useMeasureGroup } from './hooks/use-measure-group';
 import { InputSeparator } from './separator';
 import { AdvancedTypography } from './text-inputs/advanced-typography';
-import { FontFamilySelector } from './text-inputs/font-family';
-import { FontSizeSelector } from './text-inputs/font-size';
-import { FontWeightSelector } from './text-inputs/font-weight';
+import { FontFamilySelector } from './text-inputs/font/font-family-selector';
+import { FontSizeSelector } from './text-inputs/font/font-size';
+import { FontWeightSelector } from './text-inputs/font/font-weight';
 import { TextAlignSelector } from './text-inputs/text-align';
 import { TextColor } from './text-inputs/text-color';
 
 // Group definitions for the text-selected toolbar
 export const TEXT_SELECTED_GROUPS = [
     {
-        key: 'typography',
+        key: 'text-typography',
         label: 'Typography',
         components: [
-            <FontFamilySelector fontFamily="Arial" />,
-            <FontWeightSelector fontWeight="normal" handleFontWeightChange={() => { }} />,
-            <FontSizeSelector fontSize={16} handleFontSizeChange={() => { }} />,
-            <TextColor handleTextColorChange={() => { }} textColor="black" />,
-            <TextAlignSelector textAlign="left" handleTextAlignChange={() => { }} />,
+            <FontFamilySelector />,
+            <FontWeightSelector />,
+            <FontSizeSelector />,
+            <TextColor />,
+            <TextAlignSelector />,
             <AdvancedTypography />,
         ],
     },
     {
-        key: 'dimensions',
+        key: 'text-dimensions',
         label: 'Dimensions',
         components: [
             <Width />,
@@ -46,7 +45,7 @@ export const TEXT_SELECTED_GROUPS = [
         ],
     },
     {
-        key: 'base',
+        key: 'text-base',
         label: 'Base',
         components: [
             <ColorBackground />,
@@ -55,7 +54,7 @@ export const TEXT_SELECTED_GROUPS = [
         ],
     },
     {
-        key: 'layout',
+        key: 'text-layout',
         label: 'Layout',
         components: [
             <Display />,
@@ -64,7 +63,7 @@ export const TEXT_SELECTED_GROUPS = [
         ],
     },
     {
-        key: 'opacity',
+        key: 'text-opacity',
         label: 'Opacity',
         components: [
             <Opacity />,
@@ -73,181 +72,55 @@ export const TEXT_SELECTED_GROUPS = [
 ];
 
 export const TextSelected = ({ availableWidth = 0 }: { availableWidth?: number }) => {
-    const {
-        textState,
-        handleFontSizeChange,
-        handleFontWeightChange,
-        handleTextAlignChange,
-        handleTextColorChange,
-    } = useTextControl();
-
-    const groupRefs = useRef<(HTMLDivElement | null)[]>([]);
-    const [groupWidths, setGroupWidths] = useState<number[]>([]);
+    const { visibleCount } = useMeasureGroup({ availableWidth, count: TEXT_SELECTED_GROUPS.length });
     const [overflowOpen, setOverflowOpen] = useState(false);
-    const [visibleCount, setVisibleCount] = useState(TEXT_SELECTED_GROUPS.length);
-
-    // Calculate total width of a group including margins and padding
-    const calculateGroupWidth = useCallback((element: HTMLElement | null): number => {
-        if (!element) return 0;
-        const style = window.getComputedStyle(element);
-        const width = element.offsetWidth;
-        const marginLeft = parseFloat(style.marginLeft);
-        const marginRight = parseFloat(style.marginRight);
-        const paddingLeft = parseFloat(style.paddingLeft);
-        const paddingRight = parseFloat(style.paddingRight);
-        return width + marginLeft + marginRight + paddingLeft + paddingRight;
-    }, []);
-
-    // Measure all group widths
-    const measureGroups = useCallback(() => {
-        const widths = groupRefs.current.map(ref => calculateGroupWidth(ref));
-        setGroupWidths(widths);
-    }, [calculateGroupWidth]);
-
-    // Update visible count based on available width
-    const updateVisibleCount = useCallback(() => {
-        if (!groupWidths.length || !availableWidth) return;
-
-        const OVERFLOW_BUTTON_WIDTH = 32; // Reduced from 48px
-        const MIN_GROUP_WIDTH = 80; // Reduced from 100px
-        const SEPARATOR_WIDTH = 8; // Width of the InputSeparator
-        let used = 0;
-        let count = 0;
-
-        for (let i = 0; i < groupWidths.length; i++) {
-            const width = groupWidths[i] ?? 0;
-            if (width < MIN_GROUP_WIDTH) continue;
-
-            // Add separator width if this isn't the first group
-            const totalWidth = width + (count > 0 ? SEPARATOR_WIDTH : 0);
-
-            if (used + totalWidth <= availableWidth - OVERFLOW_BUTTON_WIDTH) {
-                used += totalWidth;
-                count++;
-            } else {
-                break;
-            }
-        }
-
-        setVisibleCount(count);
-    }, [groupWidths, availableWidth]);
-
-    // Measure group widths after mount and when groupRefs change
-    useEffect(() => {
-        measureGroups();
-    }, [measureGroups, availableWidth]);
-
-    // Update visible count when measurements change
-    useEffect(() => {
-        updateVisibleCount();
-    }, [updateVisibleCount]);
 
     const visibleGroups = TEXT_SELECTED_GROUPS.slice(0, visibleCount);
     const overflowGroups = TEXT_SELECTED_GROUPS.slice(visibleCount);
 
-    // Create component map with current textState and handlers
-    const COMPONENT_MAP: { [key: string]: React.ComponentType<any> } = {
-        Opacity,
-        Width,
-        Height,
-        FontFamily: () => <FontFamilySelector fontFamily={textState.fontFamily} />,
-        FontWeight: () => (
-            <FontWeightSelector
-                fontWeight={textState.fontWeight}
-                handleFontWeightChange={handleFontWeightChange}
-            />
-        ),
-        FontSize: () => (
-            <FontSizeSelector
-                fontSize={textState.fontSize}
-                handleFontSizeChange={handleFontSizeChange}
-            />
-        ),
-        TextColor: () => (
-            <TextColor
-                handleTextColorChange={handleTextColorChange}
-                textColor={textState.textColor}
-            />
-        ),
-        TextAlign: () => (
-            <TextAlignSelector
-                textAlign={textState.textAlign}
-                handleTextAlignChange={handleTextAlignChange}
-            />
-        ),
-        AdvancedTypography: () => <AdvancedTypography />,
-        Display,
-        Padding,
-        Margin,
-        Radius,
-        Border,
-        ColorBackground,
-        ViewButtons,
-    };
-
     return (
-        <>
-            {/* Hidden measurement container */}
-            <div style={{ position: 'absolute', visibility: 'hidden', height: 0, overflow: 'hidden', pointerEvents: 'none' }}>
-                {TEXT_SELECTED_GROUPS.map((group, groupIdx) => (
-                    <div
-                        key={group.key}
-                        className="flex items-center justify-center gap-0.5"
-                        ref={el => { groupRefs.current[groupIdx] = el; }}
-                    >
+        <div className="flex items-center justify-center gap-0.5 w-full overflow-hidden">
+            {visibleGroups.map((group, groupIdx) => (
+                <React.Fragment key={group.key}>
+                    {groupIdx > 0 && <InputSeparator />}
+                    <div className="flex items-center justify-center gap-0.5">
                         {group.components.map((comp, idx) => (
                             <React.Fragment key={idx}>
                                 {comp}
                             </React.Fragment>
                         ))}
                     </div>
-                ))}
-            </div>
-            <div className="flex items-center justify-center gap-0.5 w-full overflow-hidden">
-                {TEXT_SELECTED_GROUPS.map((group, groupIdx) => (
-                    groupIdx < visibleCount ? (
-                        <React.Fragment key={group.key}>
-                            {groupIdx > 0 && <InputSeparator />}
-                            <div className="flex items-center justify-center gap-0.5">
-                                {group.components.map((comp, idx) => (
-                                    <React.Fragment key={idx}>
-                                        {comp}
-                                    </React.Fragment>
-                                ))}
-                            </div>
-                        </React.Fragment>
-                    ) : null
-                ))}
-                <InputSeparator />
-                {overflowGroups.length > 0 && (
-                    <Popover open={overflowOpen} onOpenChange={setOverflowOpen}>
-                        <PopoverTrigger asChild>
-                            <Button
-                                variant="ghost"
-                                size="toolbar"
-                                className="w-8 h-8 flex items-center justify-center"
-                                aria-label="Show more toolbar controls"
-                            >
-                                <Icons.DotsHorizontal className="w-5 h-5" />
-                            </Button>
-                        </PopoverTrigger>
-                        <PopoverContent align="end" className="flex flex-row gap-1 p-1 px-1 bg-background rounded-lg shadow-xl shadow-black/20 min-w-[fit-content] items-center w-[fit-content]">
-                            {overflowGroups.map((group, groupIdx) => (
-                                <React.Fragment key={group.key}>
-                                    {groupIdx > 0 && <InputSeparator />}
-                                    <div className="flex items-center gap-0.5">
-                                        {group.components.map((comp, idx) => (
-                                            <React.Fragment key={idx}>
-                                                {comp}
-                                            </React.Fragment>
-                                        ))}
-                                    </div>
-                                </React.Fragment>
-                            ))}
-                        </PopoverContent>
-                    </Popover>
-                )}
-            </div>
-        </>
+                </React.Fragment>
+            ))}
+            {overflowGroups.length > 0 && visibleCount > 0 && <InputSeparator />}
+            {overflowGroups.length > 0 && (
+                <Popover open={overflowOpen} onOpenChange={setOverflowOpen}>
+                    <PopoverTrigger asChild>
+                        <Button
+                            variant="ghost"
+                            size="toolbar"
+                            className="w-8 h-8 flex items-center justify-center"
+                            aria-label="Show more toolbar controls"
+                        >
+                            <Icons.DotsHorizontal className="w-5 h-5" />
+                        </Button>
+                    </PopoverTrigger>
+                    <PopoverContent align="end" className="flex flex-row gap-1 p-1 px-1 bg-background rounded-lg shadow-xl shadow-black/20 min-w-[fit-content] items-center w-[fit-content]">
+                        {overflowGroups.map((group, groupIdx) => (
+                            <React.Fragment key={group.key}>
+                                {groupIdx > 0 && <InputSeparator />}
+                                <div className="flex items-center gap-0.5">
+                                    {group.components.map((comp, idx) => (
+                                        <React.Fragment key={idx}>
+                                            {comp}
+                                        </React.Fragment>
+                                    ))}
+                                </div>
+                            </React.Fragment>
+                        ))}
+                    </PopoverContent>
+                </Popover>
+            )}
+        </div>
     );
 };
diff --git a/apps/web/client/src/app/project/[id]/_components/left-panel/brand-tab/font-panel/font-family.tsx b/apps/web/client/src/app/project/[id]/_components/left-panel/brand-tab/font-panel/font-family.tsx
index 70ceed9cc1..000dcc1012 100644
--- a/apps/web/client/src/app/project/[id]/_components/left-panel/brand-tab/font-panel/font-family.tsx
+++ b/apps/web/client/src/app/project/[id]/_components/left-panel/brand-tab/font-panel/font-family.tsx
@@ -52,12 +52,7 @@ export const FontFamily = ({
     };
 
     return (
-        <div
-            className="w-full group"
-            style={{
-                fontFamily: name,
-            }}
-        >
+        <div className="w-full group">
             <div className="flex justify-between items-center py-3">
                 <div
                     className="flex flex-1 items-center cursor-pointer max-w-52"
@@ -68,7 +63,10 @@ export const FontFamily = ({
                     />
                     <Tooltip>
                         <TooltipTrigger asChild>
-                            <span className={`text-sm truncate transition-opacity duration-200`}>
+                            <span
+                                className={`text-sm truncate transition-opacity duration-200`}
+                                style={{ fontFamily: name }}
+                            >
                                 {name}
                             </span>
                         </TooltipTrigger>
@@ -132,7 +130,12 @@ export const FontFamily = ({
             </div>
 
             {expanded && variants.length > 0 && (
-                <div className="pl-7 flex flex-col gap-2 pb-6">
+                <div
+                    className="pl-7 flex flex-col gap-2 pb-6"
+                    style={{
+                        fontFamily: name,
+                    }}
+                >
                     {variants.map((variant) => (
                         <FontVariant key={`${name}-${variant}`} name={variant} />
                     ))}


Reviews:
user: login: ellipsis-dev[bot]
body: > [!CAUTION]
> **Changes requested  :x:**
> <details>
> <summary>Reviewed everything up to bf198b43e8562c84ca553b93f6965e679aa9b6cf in <ellipsistime>1 minute and 42 seconds</ellipsistime>. Click for details.</summary> 
> 
> - Reviewed `1384` lines of code in `24` files
> - Skipped `0` files when reviewing.
> - Skipped posting `13` draft comments. View those below.
> - Modify your [settings](https://app.ellipsis.dev/onlook-dev/settings/code-review) and [rules](https://app.ellipsis.dev/onlook-dev/code-review/rules) to customize what types of comments Ellipsis leaves. And don't forget to react with :+1: or :-1: to teach Ellipsis.
> 
> <details>
> <summary>1. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/text-color.tsx:31</code></summary>
> 
> - **Draft comment:** 
> Use optional chaining (tempColor?.toHex()) to avoid potential runtime errors if tempColor is null.
> - **Reason this comment was not posted:**
> Comment was not on a location in the diff, so it can't be submitted as a review comment.
> 
> 
> </details>
> 
> <details>
> <summary>2. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font-size.tsx:25</code></summary>
> 
> - **Draft comment:** 
> Consider replacing the setTimeout hack with useLayoutEffect to ensure the input is focused immediately after the popover opens.
> - **Reason this comment was not posted:**
> Comment was not on a location in the diff, so it can't be submitted as a review comment.
> 
> 
> </details>
> 
> <details>
> <summary>3. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/panels/layout-position.tsx:30</code></summary>
> 
> - **Draft comment:** 
> Consider using consistent data types (e.g., number instead of string) for layout metrics like gap to avoid type inconsistencies.
> - **Reason this comment was not posted:**
> Confidence changes required: `50%` <= threshold `50%` None
> 
> 
> </details>
> 
> <details>
> <summary>4. <code>PR:0</code></summary>
> 
> - **Draft comment:** 
> The PR description mentions adding onboarding documentation, but the diff only updates editor bar components. Please ensure that the required documentation changes are included.
> - **Reason this comment was not posted:**
> Comment was not on a location in the diff, so it can't be submitted as a review comment.
> 
> 
> </details>
> 
> <details>
> <summary>5. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/border.tsx:137</code></summary>
> 
> - **Draft comment:** 
> There seems to be an extra parenthesis on line 137. The component is defined as an arrow function starting with `{` and should likely close with `};` rather than `});`. Please verify if this change was intentional.
> - **Reason this comment was not posted:**
> Comment looked like it was already resolved.
> 
> 
> </details>
> 
> <details>
> <summary>6. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/color-background.tsx:68</code></summary>
> 
> - **Draft comment:** 
> It looks like there's an extraneous closing parenthesis at the end of the file. The file now ends with `});` whereas it should likely end with just `};` to properly close the arrow function. Could you double-check if this is intentional or a typo?
> - **Reason this comment was not posted:**
> Comment looked like it was already resolved.
> 
> 
> </details>
> 
> <details>
> <summary>7. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/display/index.tsx:64</code></summary>
> 
> - **Draft comment:** 
> Typographical error: The closing of the component function is incorrect. It currently uses `});` but should be `};` to properly close the arrow function without an extra parenthesis.
> - **Reason this comment was not posted:**
> Comment looked like it was already resolved.
> 
> 
> </details>
> 
> <details>
> <summary>8. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/margin.tsx:86</code></summary>
> 
> - **Draft comment:** 
> Typo: There is an extra closing parenthesis. The component is declared with an opening curly brace, so it should be closed with `};` rather than `});`.
> - **Reason this comment was not posted:**
> Comment looked like it was already resolved.
> 
> 
> </details>
> 
> <details>
> <summary>9. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/opacity.tsx:96</code></summary>
> 
> - **Draft comment:** 
> Typo: There's an extra closing parenthesis on line 96. It currently reads `});` but if the component function doesn't wrap its return value in an extra set of parentheses, it should likely be `};`.
> - **Reason this comment was not posted:**
> Comment looked like it was already resolved.
> 
> 
> </details>
> 
> <details>
> <summary>10. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/padding.tsx:83</code></summary>
> 
> - **Draft comment:** 
> Typographical error: The function closing appears to have an extra closing parenthesis. It should be `};` instead of `});`.
> - **Reason this comment was not posted:**
> Comment looked like it was already resolved.
> 
> 
> </details>
> 
> <details>
> <summary>11. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/radius.tsx:83</code></summary>
> 
> - **Draft comment:** 
> There appears to be a typographical error in the closing of the Radius component. The closing was changed from `};` to `});`, which introduces an extra parenthesis. This change does not seem intentional—please review and correct it if needed.
> - **Reason this comment was not posted:**
> Comment looked like it was already resolved.
> 
> 
> </details>
> 
> <details>
> <summary>12. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/width.tsx:80</code></summary>
> 
> - **Draft comment:** 
> Typographical error: The closing token is currently `})` but it appears it should be `};` to correctly close the arrow function. Please review the bracket/parenthesis usage.
> - **Reason this comment was not posted:**
> Comment looked like it was already resolved.
> 
> 
> </details>
> 
> <details>
> <summary>13. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/hooks/use-dimension-control.ts:117</code></summary>
> 
> - **Draft comment:** 
> There's an extra space in the assignment (`const parsedValue =  value;`). Consider removing the additional space to maintain consistency.
> - **Reason this comment was not posted:**
> Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% While technically correct about the extra space, this is an extremely minor formatting issue. Most modern IDEs and formatters would automatically fix this. The actual important changes in this diff are the type changes and logic simplification, not spacing. This kind of nitpick doesn't add value to the review. The comment is factually accurate - there is indeed an extra space. And consistency in code formatting can be important for readability. While accurate, this is too minor of an issue to warrant a comment. It's the kind of thing that should be handled by automated formatting tools, not manual review comments. Delete this comment as it focuses on an insignificant formatting detail rather than substantive code issues.
> 
> 
> </details>
> 
> 
> Workflow ID: <workflowid>`wflow_R910g7wtjTLSzH6G`</workflowid>
> </details>
> 
> <sup>You can customize </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=onlook-dev%2Fonlook&utm_source=github&utm_medium=referral)<sup> by changing your [verbosity settings](https://app.ellipsis.dev/onlook-dev/settings/code-review), reacting with :+1: or :-1:, [replying to comments](https://docs.ellipsis.dev/features/code-review#teaching-ellipsis), or adding [code review rules](https://app.ellipsis.dev/onlook-dev/code-review/rules).</sup>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T11:08:12Z
user: login: ellipsis-dev[bot]
body: > [!IMPORTANT]
> **Looks good to me!  :+1:**
> <details>
> <summary>Reviewed 7fffc45c0a4ab7b6516f90b788abe2d02d96a6fb in <ellipsistime>39 seconds</ellipsistime>. Click for details.</summary> 
> 
> - Reviewed `25` lines of code in `2` files
> - Skipped `0` files when reviewing.
> - Skipped posting `2` draft comments. View those below.
> - Modify your [settings](https://app.ellipsis.dev/onlook-dev/settings/code-review) and [rules](https://app.ellipsis.dev/onlook-dev/code-review/rules) to customize what types of comments Ellipsis leaves. And don't forget to react with :+1: or :-1: to teach Ellipsis.
> 
> <details>
> <summary>1. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/div-selected.tsx:59</code></summary>
> 
> - **Draft comment:** 
> Good removal of debug logging. Verify that no critical logs were used for issue monitoring.
> - **Reason this comment was not posted:**
> Confidence changes required: `0%` <= threshold `50%` None
> 
> 
> </details>
> 
> <details>
> <summary>2. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/index.tsx:35</code></summary>
> 
> - **Draft comment:** 
> Debug log removal is fine, but the PR description mentions onboarding docs improvements. Ensure that documentation updates are included if intended.
> - **Reason this comment was not posted:**
> Comment did not seem useful. Confidence is useful = `0%` <= threshold `50%` This comment is asking the PR author to ensure that documentation updates are included, which is similar to asking them to confirm their intention or ensure something is done. This violates the rule against asking the author to confirm their intention or ensure something is done.
> 
> 
> </details>
> 
> 
> Workflow ID: <workflowid>`wflow_y5CZRCme012AV2Fz`</workflowid>
> </details>
> 
> <sup>You can customize </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=onlook-dev%2Fonlook&utm_source=github&utm_medium=referral)<sup> by changing your [verbosity settings](https://app.ellipsis.dev/onlook-dev/settings/code-review), reacting with :+1: or :-1:, [replying to comments](https://docs.ellipsis.dev/features/code-review#teaching-ellipsis), or adding [code review rules](https://app.ellipsis.dev/onlook-dev/code-review/rules).</sup>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T11:10:25Z
user: login: ellipsis-dev[bot]
body: > [!CAUTION]
> **Changes requested  :x:**
> <details>
> <summary>Reviewed 56c822b9b7595e19acf48385a010289120584c04 in <ellipsistime>1 minute and 57 seconds</ellipsistime>. Click for details.</summary> 
> 
> - Reviewed `228` lines of code in `2` files
> - Skipped `0` files when reviewing.
> - Skipped posting `4` draft comments. View those below.
> - Modify your [settings](https://app.ellipsis.dev/onlook-dev/settings/code-review) and [rules](https://app.ellipsis.dev/onlook-dev/code-review/rules) to customize what types of comments Ellipsis leaves. And don't forget to react with :+1: or :-1: to teach Ellipsis.
> 
> <details>
> <summary>1. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/div-selected.tsx:55</code></summary>
> 
> - **Draft comment:** 
> PR description mismatch: The title/description mention onboarding docs improvements, but the changes update the editor bar component. Please update the PR description or issue reference accordingly.
> - **Reason this comment was not posted:**
> Comment did not seem useful. Confidence is useful = `0%` <= threshold `50%` This comment is asking the PR author to update the PR description, which is against the rules. The comment does not provide a specific code suggestion or ask for a test to be written. It is purely about the PR description, which should not be commented on.
> 
> 
> </details>
> 
> <details>
> <summary>2. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/hooks/use-measure-group.ts:4</code></summary>
> 
> - **Draft comment:** 
> The hook relies on the ordering of GROUP_WIDTHS keys to match DIV_SELECTED_GROUPS. Please document or enforce this dependency to avoid future mismatches.
> - **Reason this comment was not posted:**
> Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% The comment refers to DIV_SELECTED_GROUPS which was removed in this change. The new code is self-contained and only depends on GROUP_WIDTHS. The ordering is now determined by Object.keys(), which means it's derived directly from the GROUP_WIDTHS object. There's no external dependency to document. Could there be another file that still expects these groups to be in a specific order? Could DIV_SELECTED_GROUPS still exist elsewhere? Even if DIV_SELECTED_GROUPS exists elsewhere, this component now works independently. Cross-file dependencies are outside the scope of this review per the rules. The comment should be deleted as it refers to a dependency that was removed in this change, and any potential cross-file issues are out of scope.
> 
> 
> </details>
> 
> <details>
> <summary>3. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/div-selected.tsx:55</code></summary>
> 
> - **Draft comment:** 
> Consider setting a displayName on the memoized DivSelected component to ease debugging.
> - **Reason this comment was not posted:**
> Confidence changes required: `33%` <= threshold `50%` None
> 
> 
> </details>
> 
> <details>
> <summary>4. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/hooks/use-measure-group.ts:33</code></summary>
> 
> - **Draft comment:** 
> The visibleCount calculation always reserves OVERFLOW_BUTTON_WIDTH space, even if no overflow exists. Validate that this design decision fits all layout scenarios.
> - **Reason this comment was not posted:**
> Comment was not on a location in the diff, so it can't be submitted as a review comment.
> 
> 
> </details>
> 
> 
> Workflow ID: <workflowid>`wflow_Hw8d5rozyfpzccLT`</workflowid>
> </details>
> 
> <sup>You can customize </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=onlook-dev%2Fonlook&utm_source=github&utm_medium=referral)<sup> by changing your [verbosity settings](https://app.ellipsis.dev/onlook-dev/settings/code-review), reacting with :+1: or :-1:, [replying to comments](https://docs.ellipsis.dev/features/code-review#teaching-ellipsis), or adding [code review rules](https://app.ellipsis.dev/onlook-dev/code-review/rules).</sup>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T14:32:19Z
user: login: ellipsis-dev[bot]
body: > [!CAUTION]
> **Changes requested  :x:**
> <details>
> <summary>Reviewed f8d4691a50e3bdef9fa53ca3b0b0ac35790ce60b in <ellipsistime>2 minutes and 32 seconds</ellipsistime>. Click for details.</summary> 
> 
> - Reviewed `326` lines of code in `4` files
> - Skipped `0` files when reviewing.
> - Skipped posting `3` draft comments. View those below.
> - Modify your [settings](https://app.ellipsis.dev/onlook-dev/settings/code-review) and [rules](https://app.ellipsis.dev/onlook-dev/code-review/rules) to customize what types of comments Ellipsis leaves. And don't forget to react with :+1: or :-1: to teach Ellipsis.
> 
> <details>
> <summary>1. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/hooks/use-measure-group.ts:32</code></summary>
> 
> - **Draft comment:** 
> The measurement hook iterates over all keys in GROUP_WIDTHS (including non-text groups) without limiting to the intended subset. The provided 'count' parameter isn’t used to restrict iteration, which may lead to inaccurate visibleCount for text-selected toolbars. Consider filtering the keys (or iterating only up to 'count') to match the actual groups.
> - **Reason this comment was not posted:**
> Comment was not on a location in the diff, so it can't be submitted as a review comment.
> 
> 
> </details>
> 
> <details>
> <summary>2. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font-family.tsx:140</code></summary>
> 
> - **Draft comment:** 
> The active font check compares textState.fontFamily with font.id. This may be inconsistent if textState.fontFamily actually stores the font family name. Consider comparing with font.family instead.
> - **Reason this comment was not posted:**
> Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 20% vs. threshold = 50% The code shows font.family is used for display (name prop), but that doesn't necessarily mean it should be used for comparison. Without seeing how textState.fontFamily is set in handleFontFamilyChange or how font.id relates to font.family, I can't be certain which comparison is correct. The current code could be intentionally using ID for more reliable matching. I don't have visibility into how textState.fontFamily is populated or the relationship between font.id and font.family. The current implementation could be correct. While the comment raises a valid concern about potential inconsistency, without seeing more context about how textState.fontFamily is managed, I cannot be confident that this is actually a problem. Delete the comment since we don't have strong evidence that the current implementation is incorrect. The suggestion is speculative without more context.
> 
> 
> </details>
> 
> <details>
> <summary>3. <code>apps/web/client/src/app/project/[id]/_components/left-panel/brand-tab/font-panel/font-family.tsx:73</code></summary>
> 
> - **Draft comment:** 
> Ensure that the element with id 'style-panel' exists before using it in TooltipPortal to avoid potential null reference issues.
> - **Reason this comment was not posted:**
> Comment was not on a location in the diff, so it can't be submitted as a review comment.
> 
> 
> </details>
> 
> 
> Workflow ID: <workflowid>`wflow_Xei2hvgHsmLwayay`</workflowid>
> </details>
> 
> <sup>You can customize </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=onlook-dev%2Fonlook&utm_source=github&utm_medium=referral)<sup> by changing your [verbosity settings](https://app.ellipsis.dev/onlook-dev/settings/code-review), reacting with :+1: or :-1:, [replying to comments](https://docs.ellipsis.dev/features/code-review#teaching-ellipsis), or adding [code review rules](https://app.ellipsis.dev/onlook-dev/code-review/rules).</sup>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T15:26:21Z
user: login: ellipsis-dev[bot]
body: > [!IMPORTANT]
> **Looks good to me!  :+1:**
> <details>
> <summary>Reviewed 248e0319c43506cc6582625b20426c61787b5151 in <ellipsistime>48 seconds</ellipsistime>. Click for details.</summary> 
> 
> - Reviewed `21` lines of code in `1` files
> - Skipped `0` files when reviewing.
> - Skipped posting `2` draft comments. View those below.
> - Modify your [settings](https://app.ellipsis.dev/onlook-dev/settings/code-review) and [rules](https://app.ellipsis.dev/onlook-dev/code-review/rules) to customize what types of comments Ellipsis leaves. And don't forget to react with :+1: or :-1: to teach Ellipsis.
> 
> <details>
> <summary>1. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font-family.tsx:17</code></summary>
> 
> - **Draft comment:** 
> Removed console.log(name, isActive) from the FontFamily component. This cleanup prevents leftover debug output.
> - **Reason this comment was not posted:**
> Confidence changes required: `0%` <= threshold `50%` None
> 
> 
> </details>
> 
> <details>
> <summary>2. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font-family.tsx:67</code></summary>
> 
> - **Draft comment:** 
> Removed console.log(textState.fontFamily) from FontFamilySelector for cleaner production output.
> - **Reason this comment was not posted:**
> Confidence changes required: `0%` <= threshold `50%` None
> 
> 
> </details>
> 
> 
> Workflow ID: <workflowid>`wflow_g2idiUPEf1dTL7K5`</workflowid>
> </details>
> 
> <sup>You can customize </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=onlook-dev%2Fonlook&utm_source=github&utm_medium=referral)<sup> by changing your [verbosity settings](https://app.ellipsis.dev/onlook-dev/settings/code-review), reacting with :+1: or :-1:, [replying to comments](https://docs.ellipsis.dev/features/code-review#teaching-ellipsis), or adding [code review rules](https://app.ellipsis.dev/onlook-dev/code-review/rules).</sup>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T15:29:12Z
user: login: Kitenite
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T16:12:43Z
user: login: ellipsis-dev[bot]
body: > [!IMPORTANT]
> **Looks good to me!  :+1:**
> <details>
> <summary>Reviewed 19ec6349e105d6913ea05839b78cc7f949023db3 in <ellipsistime>1 minute and 37 seconds</ellipsistime>. Click for details.</summary> 
> 
> - Reviewed `77` lines of code in `2` files
> - Skipped `0` files when reviewing.
> - Skipped posting `2` draft comments. View those below.
> - Modify your [settings](https://app.ellipsis.dev/onlook-dev/settings/code-review) and [rules](https://app.ellipsis.dev/onlook-dev/code-review/rules) to customize what types of comments Ellipsis leaves. And don't forget to react with :+1: or :-1: to teach Ellipsis.
> 
> <details>
> <summary>1. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/height.tsx:26</code></summary>
> 
> - **Draft comment:** 
> The refactor now always renders dimensionState.height.value without the prior conditional logic (including rounding for 'px'). Ensure this is intentional and that value is properly formatted or has a fallback in cases where it might be undefined or 'auto'.
> - **Reason this comment was not posted:**
> Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% The comment is asking the author to "ensure" something, which violates our rules. It's speculative about potential issues rather than pointing out a definite problem. The InputDropdown component and handlers suggest the value formatting is handled elsewhere in the system. Without seeing the dimensionState implementation, we can't be certain there's an actual issue. I might be missing critical context about how dimensionState.height.value is formatted. The old code's special handling could have been important. Even if there were formatting concerns, asking the author to "ensure" something is not actionable. If there was a specific formatting issue, it should be stated directly. Delete this comment as it's speculative, asks for verification rather than pointing out a specific issue, and we lack evidence of an actual problem.
> 
> 
> </details>
> 
> <details>
> <summary>2. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/width.tsx:26</code></summary>
> 
> - **Draft comment:** 
> The conditional logic for formatting (and rounding for 'px') was removed to always display dimensionState.width.value. Confirm that this aligns with intended UX, and consider fallback handling if the value may be missing or 'auto'.
> - **Reason this comment was not posted:**
> Comment did not seem useful. Confidence is useful = `50%` <= threshold `50%` The comment is asking the author to confirm their intention regarding the removal of conditional logic for formatting and rounding. It also suggests considering fallback handling if the value may be missing or 'auto'. The first part of the comment violates the rule against asking the author to confirm their intention. However, the suggestion to consider fallback handling is a valid code suggestion.
> 
> 
> </details>
> 
> 
> Workflow ID: <workflowid>`wflow_UutGPg8wcMfL6nSb`</workflowid>
> </details>
> 
> <sup>You can customize </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=onlook-dev%2Fonlook&utm_source=github&utm_medium=referral)<sup> by changing your [verbosity settings](https://app.ellipsis.dev/onlook-dev/settings/code-review), reacting with :+1: or :-1:, [replying to comments](https://docs.ellipsis.dev/features/code-review#teaching-ellipsis), or adding [code review rules](https://app.ellipsis.dev/onlook-dev/code-review/rules).</sup>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T16:27:16Z
user: login: ellipsis-dev[bot]
body: > [!CAUTION]
> **Changes requested  :x:**
> <details>
> <summary>Reviewed 13c6990ef86fc9fd125c5e2f5a41aa7464bf0d1f in <ellipsistime>1 minute and 52 seconds</ellipsistime>. Click for details.</summary> 
> 
> - Reviewed `151` lines of code in `6` files
> - Skipped `0` files when reviewing.
> - Skipped posting `3` draft comments. View those below.
> - Modify your [settings](https://app.ellipsis.dev/onlook-dev/settings/code-review) and [rules](https://app.ellipsis.dev/onlook-dev/code-review/rules) to customize what types of comments Ellipsis leaves. And don't forget to react with :+1: or :-1: to teach Ellipsis.
> 
> <details>
> <summary>1. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/div-selected.tsx:17</code></summary>
> 
> - **Draft comment:** 
> Import paths for font selectors have been updated to the new directory structure. Verify that the new paths are consistent across the project.
> - **Reason this comment was not posted:**
> Confidence changes required: `0%` <= threshold `50%` None
> 
> 
> </details>
> 
> <details>
> <summary>2. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font/font-family-selector.tsx:23</code></summary>
> 
> - **Draft comment:** 
> Consider including 'editorEngine' in the useEffect dependency array if its reference might change, ensuring the font scan stays in sync.
> - **Reason this comment was not posted:**
> Comment was not on a location in the diff, so it can't be submitted as a review comment.
> 
> 
> </details>
> 
> <details>
> <summary>3. <code>apps/web/client/src/app/project/[id]/_components/editor-bar/div-selected.tsx:1</code></summary>
> 
> - **Draft comment:** 
> The PR description mentions onboarding docs improvements, but these changes focus on refactoring editor bar components. Update the PR description if documentation changes are not part of this update.
> - **Reason this comment was not posted:**
> Comment was not on a location in the diff, so it can't be submitted as a review comment.
> 
> 
> </details>
> 
> 
> Workflow ID: <workflowid>`wflow_NInsudJhgvRwCiVq`</workflowid>
> </details>
> 
> <sup>You can customize </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=onlook-dev%2Fonlook&utm_source=github&utm_medium=referral)<sup> by changing your [verbosity settings](https://app.ellipsis.dev/onlook-dev/settings/code-review), reacting with :+1: or :-1:, [replying to comments](https://docs.ellipsis.dev/features/code-review#teaching-ellipsis), or adding [code review rules](https://app.ellipsis.dev/onlook-dev/code-review/rules).</sup>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T16:47:15Z

Review comments:
diff_hunk: @@ -17,153 +17,89 @@ import { InputSeparator } from './separator';
 import { FontFamilySelector } from './text-inputs/font-family';
 import { FontSizeSelector } from './text-inputs/font-size';
 import { FontWeightSelector } from './text-inputs/font-weight';
+import { useMeasureGroup } from './hooks/use-measure-group';
 
 // Group definitions for the div-selected toolbar
 export const DIV_SELECTED_GROUPS = [
     {
         key: 'dimensions',
         label: 'Dimensions',
-        components: [
-            <Width />,
-            <Height />,
-        ],
+        components: [<Width />, <Height />],
     },
     {
         key: 'base',
         label: 'Base',
-        components: [
-            <ColorBackground />,
-            <Border />,
-            <Radius />,
-        ]
+        components: [<ColorBackground />, <Border />, <Radius />],
     },
     {
         key: 'layout',
         label: 'Layout',
-        components: [
-            <Display />,
-            <Padding />,
-            <Margin />,
-        ],
+        components: [<Display />, <Padding />, <Margin />],
     },
     {
         key: 'typography',
         label: 'Typography',
         components: [
-            <FontFamilySelector fontFamily="Arial" />,
-            <FontWeightSelector fontWeight="normal" handleFontWeightChange={() => { }} />,
-            <FontSizeSelector fontSize={16} handleFontSizeChange={() => { }} />,
+            <FontFamilySelector />,
+            <FontWeightSelector />,
+            <FontSizeSelector />,
         ],
     },
     {
         key: 'opacity',
         label: 'Opacity',
-        components: [
-            <Opacity />,
-        ]
+        components: [<Opacity />],
     },
 ];
 
 export const DivSelected = ({ availableWidth = 0 }: { availableWidth?: number }) => {
-    const groupRefs = useRef<(HTMLDivElement | null)[]>([]);
-    const [groupWidths, setGroupWidths] = useState<number[]>([]);
     const [overflowOpen, setOverflowOpen] = useState(false);
-    const [visibleCount, setVisibleCount] = useState(DIV_SELECTED_GROUPS.length);
-
-    // Calculate total width of a group including margins and padding
-    const calculateGroupWidth = useCallback((element: HTMLElement | null): number => {
-        if (!element) return 0;
-        const style = window.getComputedStyle(element);
-        const width = element.offsetWidth;
-        const marginLeft = parseFloat(style.marginLeft);
-        const marginRight = parseFloat(style.marginRight);
-        const paddingLeft = parseFloat(style.paddingLeft);
-        const paddingRight = parseFloat(style.paddingRight);
-        return width + marginLeft + marginRight + paddingLeft + paddingRight;
-    }, []);
-
-    // Measure all group widths
-    const measureGroups = useCallback(() => {
-        const widths = groupRefs.current.map(ref => calculateGroupWidth(ref));
-        setGroupWidths(widths);
-    }, [calculateGroupWidth]);
-
-    // Update visible count based on available width
-    const updateVisibleCount = useCallback(() => {
-        if (!groupWidths.length || !availableWidth) return;
-
-        const OVERFLOW_BUTTON_WIDTH = 32; // Reduced from 48px
-        const MIN_GROUP_WIDTH = 80; // Reduced from 100px
-        const SEPARATOR_WIDTH = 8; // Width of the InputSeparator
-        let used = 0;
-        let count = 0;
-
-        for (let i = 0; i < groupWidths.length; i++) {
-            const width = groupWidths[i] ?? 0;
-            if (width < MIN_GROUP_WIDTH) continue;
-
-            // Add separator width if this isn't the first group
-            const totalWidth = width + (count > 0 ? SEPARATOR_WIDTH : 0);
-
-            if (used + totalWidth <= availableWidth - OVERFLOW_BUTTON_WIDTH) {
-                used += totalWidth;
-                count++;
-            } else {
-                break;
-            }
-        }
-
-        setVisibleCount(count);
-    }, [groupWidths, availableWidth]);
-
-    // Measure group widths after mount and when groupRefs change
-    useEffect(() => {
-        measureGroups();
-    }, [measureGroups, availableWidth]);
-
-    // Update visible count when measurements change
-    useEffect(() => {
-        updateVisibleCount();
-    }, [updateVisibleCount]);
+    const { groupRefs, visibleCount } = useMeasureGroup({ availableWidth, count: DIV_SELECTED_GROUPS.length });
 
     const visibleGroups = DIV_SELECTED_GROUPS.slice(0, visibleCount);
     const overflowGroups = DIV_SELECTED_GROUPS.slice(visibleCount);
 
+    console.log('re-render', visibleCount, availableWidth);
path: apps/web/client/src/app/project/[id]/_components/editor-bar/div-selected.tsx
user: login: ellipsis-dev[bot]
body: Remove debug console.log before merge.
```suggestion

```

created_at: 2025-05-23T11:08:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -35,6 +35,7 @@ const getSelectedTag = (selected: DomElement[]): 'div' | 'text' | 'image' | 'vid
 export const EditorBar = observer(({ availableWidth }: { availableWidth?: number }) => {
     const editorEngine = useEditorEngine();
     const selectedTag = getSelectedTag(editorEngine.elements.selected);
+    console.log('re-render selected tag', availableWidth);
path: apps/web/client/src/app/project/[id]/_components/editor-bar/index.tsx
user: login: ellipsis-dev[bot]
body: Remove debug `console.log` to keep production logs clean.
```suggestion

```

created_at: 2025-05-23T11:08:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,52 @@
+import { useCallback, useEffect, useState } from 'react';
+
+// Pre-calculated approximate widths for each group type
+const GROUP_WIDTHS = {
+    dimensions: 160, // Width + Height
+    base: 180, // Color + Border + Radius
+    layout: 180, // Display + Padding + Margin
+    typography: 240, // Font Family + Weight + Size
+    opacity: 80, // Opacity
+};
+
+export const useMeasureGroup = ({ availableWidth = 0, count = 0 }: { availableWidth?: number, count?: number }) => {
path: apps/web/client/src/app/project/[id]/_components/editor-bar/hooks/use-measure-group.ts
user: login: ellipsis-dev[bot]
body: The 'count' parameter is used only to initialize `visibleCount` and isn’t used in subsequent calculations. Consider reviewing its necessity or removing it to prevent confusion.

created_at: 2025-05-23T14:32:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -10,15 +10,37 @@ import { Popover, PopoverContent, PopoverTrigger } from '@onlook/ui/popover';
 import { Tooltip, TooltipContent, TooltipTrigger } from '@onlook/ui/tooltip';
 import { toNormalCase } from '@onlook/utility';
 import { memo, useEffect, useState } from 'react';
-import { FontFamily } from '../../left-panel/brand-tab/font-panel/font-family';
 import { useTextControl } from '../hooks/use-text-control';
+import { observer } from 'mobx-react-lite';
 
-export const FontFamilySelector = memo(({ fontFamily }: { fontFamily: string }) => {
+const FontFamily = memo(
+    ({ name, isActive, onSetFont }: { name: string; isActive: boolean; onSetFont: () => void }) => {
+        console.log(name, isActive);
path: apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font-family.tsx
user: login: ellipsis-dev[bot]
body: Remove the debugging `console.log` (name, isActive) from the `FontFamily` component.
```suggestion

```

created_at: 2025-05-23T15:26:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -45,6 +67,8 @@ export const FontFamilySelector = memo(({ fontFamily }: { fontFamily: string })
         setSearch('');
     };
 
+    console.log(textState.fontFamily);
path: apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font-family.tsx
user: login: ellipsis-dev[bot]
body: Remove the debugging `console.log` for `textState.fontFamily`.
```suggestion

```

created_at: 2025-05-23T15:26:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,183 +17,90 @@ import { InputSeparator } from './separator';
 import { FontFamilySelector } from './text-inputs/font-family';
 import { FontSizeSelector } from './text-inputs/font-size';
 import { FontWeightSelector } from './text-inputs/font-weight';
+import { useMeasureGroup } from './hooks/use-measure-group';
 
 // Group definitions for the div-selected toolbar
 export const DIV_SELECTED_GROUPS = [
     {
         key: 'dimensions',
         label: 'Dimensions',
-        components: [
-            <Width />,
-            <Height />,
-        ],
+        components: [<Width />, <Height />],
     },
     {
         key: 'base',
         label: 'Base',
-        components: [
-            <ColorBackground />,
-            <Border />,
-            <Radius />,
-        ]
+        components: [<ColorBackground />, <Border />, <Radius />],
     },
     {
         key: 'layout',
         label: 'Layout',
-        components: [
-            <Display />,
-            <Padding />,
-            <Margin />,
-        ],
+        components: [<Display />, <Padding />, <Margin />],
     },
     {
         key: 'typography',
         label: 'Typography',
         components: [
-            <FontFamilySelector fontFamily="Arial" />,
-            <FontWeightSelector fontWeight="normal" handleFontWeightChange={() => { }} />,
-            <FontSizeSelector fontSize={16} handleFontSizeChange={() => { }} />,
+            <FontFamilySelector />,
+            <FontWeightSelector />,
+            <FontSizeSelector />,
         ],
     },
     {
         key: 'opacity',
         label: 'Opacity',
-        components: [
-            <Opacity />,
-        ]
+        components: [<Opacity />],
     },
 ];
 
-export const DivSelected = ({ availableWidth = 0 }: { availableWidth?: number }) => {
-    const groupRefs = useRef<(HTMLDivElement | null)[]>([]);
-    const [groupWidths, setGroupWidths] = useState<number[]>([]);
+export const DivSelected = memo(({ availableWidth = 0 }: { availableWidth?: number }) => {
     const [overflowOpen, setOverflowOpen] = useState(false);
-    const [visibleCount, setVisibleCount] = useState(DIV_SELECTED_GROUPS.length);
-
-    // Calculate total width of a group including margins and padding
-    const calculateGroupWidth = useCallback((element: HTMLElement | null): number => {
-        if (!element) return 0;
-        const style = window.getComputedStyle(element);
-        const width = element.offsetWidth;
-        const marginLeft = parseFloat(style.marginLeft);
-        const marginRight = parseFloat(style.marginRight);
-        const paddingLeft = parseFloat(style.paddingLeft);
-        const paddingRight = parseFloat(style.paddingRight);
-        return width + marginLeft + marginRight + paddingLeft + paddingRight;
-    }, []);
-
-    // Measure all group widths
-    const measureGroups = useCallback(() => {
-        const widths = groupRefs.current.map(ref => calculateGroupWidth(ref));
-        setGroupWidths(widths);
-    }, [calculateGroupWidth]);
-
-    // Update visible count based on available width
-    const updateVisibleCount = useCallback(() => {
-        if (!groupWidths.length || !availableWidth) return;
-
-        const OVERFLOW_BUTTON_WIDTH = 32; // Reduced from 48px
-        const MIN_GROUP_WIDTH = 80; // Reduced from 100px
-        const SEPARATOR_WIDTH = 8; // Width of the InputSeparator
-        let used = 0;
-        let count = 0;
-
-        for (let i = 0; i < groupWidths.length; i++) {
-            const width = groupWidths[i] ?? 0;
-            if (width < MIN_GROUP_WIDTH) continue;
-
-            // Add separator width if this isn't the first group
-            const totalWidth = width + (count > 0 ? SEPARATOR_WIDTH : 0);
-
-            if (used + totalWidth <= availableWidth - OVERFLOW_BUTTON_WIDTH) {
-                used += totalWidth;
-                count++;
-            } else {
-                break;
-            }
-        }
-
-        setVisibleCount(count);
-    }, [groupWidths, availableWidth]);
-
-    // Measure group widths after mount and when groupRefs change
-    useEffect(() => {
-        measureGroups();
-    }, [measureGroups, availableWidth]);
-
-    // Update visible count when measurements change
-    useEffect(() => {
-        updateVisibleCount();
-    }, [updateVisibleCount]);
+    const { visibleCount } = useMeasureGroup({ availableWidth, count: DIV_SELECTED_GROUPS.length });
path: apps/web/client/src/app/project/[id]/_components/editor-bar/div-selected.tsx
user: login: Kitenite
body: Thanks for refactoring all this.
created_at: 2025-05-23T16:12:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,24 @@
+'use client';
+
+import { Icons } from '@onlook/ui/icons';
+import { memo } from 'react';
+
+export const FontFamily = memo(
+    ({ name, isActive, onSetFont }: { name: string; isActive: boolean; onSetFont: () => void }) => {
+        return (
+            <div
+                key={name}
path: apps/web/client/src/app/project/[id]/_components/editor-bar/text-inputs/font/font-family.tsx
user: login: ellipsis-dev[bot]
body: Avoid setting a key on the root element inside a memoized component; keys should be applied in the parent mapping.
```suggestion

```

created_at: 2025-05-23T16:47:15Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-05-23T11:06:29Z
body: [vc]: #oOanWSvdUgRZDkhL3p+qFB81rn8Cund5q5kXw8rzry8=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJkb2NzIiwicm9vdERpcmVjdG9yeSI6ImRvY3MiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiZG9jcy1naXQtcmVmYWN0b3ItdXBkYXRlLWVkaXQtYmFyLWNvbXBvbmVudHMtb25sb29rLnZlcmNlbC5hcHAifSwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL29ubG9vay9kb2NzLzhkZEc1WTFBNWhZbTFrclh6MkJncER6VmtQMUciLCJwcmV2aWV3VXJsIjoiZG9jcy1naXQtcmVmYWN0b3ItdXBkYXRlLWVkaXQtYmFyLWNvbXBvbmVudHMtb25sb29rLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQifSx7Im5hbWUiOiJ3ZWIiLCJyb290RGlyZWN0b3J5IjoiYXBwcy93ZWIvY2xpZW50IiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL29ubG9vay93ZWIvRVdSYUtzdmJITFp1cnBjV3pzNlFwMjlIOXhlQyIsInByZXZpZXdVcmwiOiJ3ZWItZ2l0LXJlZmFjdG9yLXVwZGF0ZS1lZGl0LWJhci1jb21wb25lbnRzLW9ubG9vay52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6IndlYi1naXQtcmVmYWN0b3ItdXBkYXRlLWVkaXQtYmFyLWNvbXBvbmVudHMtb25sb29rLnZlcmNlbC5hcHAifX1dfQ==
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **docs** | ✅ Ready ([Inspect](https://vercel.com/onlook/docs/8ddG5Y1A5hYm1krXz2BgpDzVkP1G)) | [Visit Preview](https://docs-git-refactor-update-edit-bar-components-onlook.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/docs-git-refactor-update-edit-bar-components-onlook.vercel.app?via=pr-comment-feedback-link) | May 23, 2025 4:47pm |
| **web** | ✅ Ready ([Inspect](https://vercel.com/onlook/web/EWRaKsvbHLZurpcWzs6Qp29H9xeC)) | [Visit Preview](https://web-git-refactor-update-edit-bar-components-onlook.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/web-git-refactor-update-edit-bar-components-onlook.vercel.app?via=pr-comment-feedback-link) | May 23, 2025 4:47pm |



author_association: NONE
user: login: supabase[bot]
created_at: 2025-05-23T11:06:32Z
body: [supa]:wowaemfasoptxrdjhilu
This pull request has been ignored for the connected project `wowaemfasoptxrdjhilu` because there are no changes detected in `apps/backend/supabase` directory. You can change this behaviour in [Project Integrations Settings ↗︎](https://supabase.com/dashboard/project/wowaemfasoptxrdjhilu/settings/integrations).

<hr/>

Preview Branches by Supabase.
Learn more about [Supabase Branching ↗︎](https://supabase.com/docs/guides/platform/branching).
author_association: NONE