diff --git a/packages/studio/src/components/StudioRightPanel.tsx b/packages/studio/src/components/StudioRightPanel.tsx
index 90b72920e..f009e6426 100644
--- a/packages/studio/src/components/StudioRightPanel.tsx
+++ b/packages/studio/src/components/StudioRightPanel.tsx
@@ -1,3 +1,4 @@
+import { Tooltip } from "./ui";
import { PropertyPanel } from "./editor/PropertyPanel";
import { MotionPanel } from "./editor/MotionPanel";
import { LayersPanel } from "./editor/LayersPanel";
@@ -106,58 +107,62 @@ export function StudioRightPanel({
{STUDIO_INSPECTOR_PANELS_ENABLED && (
<>
-
-
- {STUDIO_MOTION_PANEL_ENABLED && (
+
+
+
+
+
+ {STUDIO_MOTION_PANEL_ENABLED && (
+
+
+
)}
>
)}
-
+
+
+
{rightPanelTab === "block-params" && activeBlockParams ? (
diff --git a/packages/studio/src/components/sidebar/LeftSidebar.tsx b/packages/studio/src/components/sidebar/LeftSidebar.tsx
index a7b9fe167..28ea6ccae 100644
--- a/packages/studio/src/components/sidebar/LeftSidebar.tsx
+++ b/packages/studio/src/components/sidebar/LeftSidebar.tsx
@@ -13,6 +13,7 @@ import { trackStudioEvent } from "../../utils/studioTelemetry";
import { BlocksTab, type BlockPreviewInfo } from "./BlocksTab";
import { FileTree } from "../editor/FileTree";
import { STUDIO_BLOCKS_PANEL_ENABLED } from "../editor/manualEditingAvailability";
+import { Tooltip } from "../ui";
export type SidebarTab = "compositions" | "assets" | "code" | "blocks";
@@ -124,55 +125,59 @@ export const LeftSidebar = memo(
: "1fr 1fr 1fr",
}}
>
-
-
-
- {STUDIO_BLOCKS_PANEL_ENABLED && (
+
+
+
+
+
+
+
+
+ {STUDIO_BLOCKS_PANEL_ENABLED && (
+
+
+
)}
{onToggleCollapse && (
diff --git a/packages/studio/src/components/ui/Tooltip.tsx b/packages/studio/src/components/ui/Tooltip.tsx
new file mode 100644
index 000000000..f5ab4739d
--- /dev/null
+++ b/packages/studio/src/components/ui/Tooltip.tsx
@@ -0,0 +1,61 @@
+import { useState, useRef, useCallback, type ReactNode } from "react";
+import { createPortal } from "react-dom";
+
+interface TooltipProps {
+ label: string;
+ children: ReactNode;
+ delay?: number;
+ side?: "top" | "bottom";
+}
+
+export function Tooltip({ label, children, delay = 400, side = "top" }: TooltipProps) {
+ const [visible, setVisible] = useState(false);
+ const [pos, setPos] = useState({ x: 0, y: 0 });
+ const timerRef = useRef | null>(null);
+ const triggerRef = useRef(null);
+
+ const show = useCallback(() => {
+ timerRef.current = setTimeout(() => {
+ const el = triggerRef.current;
+ if (!el) return;
+ const rect = el.getBoundingClientRect();
+ setPos({
+ x: rect.left + rect.width / 2,
+ y: side === "top" ? rect.top - 6 : rect.bottom + 6,
+ });
+ setVisible(true);
+ }, delay);
+ }, [delay, side]);
+
+ const hide = useCallback(() => {
+ if (timerRef.current) {
+ clearTimeout(timerRef.current);
+ timerRef.current = null;
+ }
+ setVisible(false);
+ }, []);
+
+ return (
+ <>
+
+ {children}
+
+ {visible &&
+ createPortal(
+ ,
+ document.body,
+ )}
+ >
+ );
+}
diff --git a/packages/studio/src/components/ui/index.ts b/packages/studio/src/components/ui/index.ts
index 9623c7362..0a6987e69 100644
--- a/packages/studio/src/components/ui/index.ts
+++ b/packages/studio/src/components/ui/index.ts
@@ -2,3 +2,4 @@
export { Button, IconButton } from "./Button";
export { HyperframesLoader, StatusFrame } from "./HyperframesLoader";
export type { HyperframesLoaderProps } from "./HyperframesLoader";
+export { Tooltip } from "./Tooltip";