+
{/* Left: logo + project name */}
-
+
|
- {projectId}
+ {projectId}
{/* Right: toolbar buttons */}
-
-
+ />
-
-
+ />
+ {/* A real download link, so it wears Button's recipe rather than being
+ one: `download` on an is what saves the frame, and no
-
+
-
+
diff --git a/packages/studio/src/components/StudioRightPanel.tsx b/packages/studio/src/components/StudioRightPanel.tsx
index 248ff383cb..75c1cb5dad 100644
--- a/packages/studio/src/components/StudioRightPanel.tsx
+++ b/packages/studio/src/components/StudioRightPanel.tsx
@@ -9,7 +9,7 @@ import { BlockParamsPanel } from "./editor/BlockParamsPanel";
import { RenderQueuePanel } from "./renders/RenderQueuePanel";
import { SlideshowPanel } from "./panels/SlideshowPanel";
import { VariablesPanel } from "./panels/VariablesPanel";
-import { PanelTabButton } from "./PanelTabButton";
+import { RightPanelTabs, type RightPanelTabDescriptor } from "./RightPanelTabs";
import type { RenderJob } from "./renders/useRenderQueue";
import { STUDIO_FLAT_INSPECTOR_ENABLED } from "./editor/manualEditingAvailability";
import { useSlideshowPersist } from "../hooks/useSlideshowPersist";
@@ -339,6 +339,51 @@ export function StudioRightPanel({
const renderQueuePanel =
;
+ // Slideshow appears only for a slideshow composition, so the strip is built
+ // rather than written out: a tab that is not in this list is not reachable by
+ // an arrow key either.
+ const inspectorTabs: RightPanelTabDescriptor[] = [
+ {
+ id: "design",
+ label: "Design",
+ tooltip: "Element styles and properties",
+ active: designPaneOpen,
+ onSelect: () => handleInspectorPaneButtonClick("design"),
+ },
+ {
+ id: "layers",
+ label: "Layers",
+ tooltip: "Composition layer stack",
+ active: layersPaneOpen,
+ onSelect: () => handleInspectorPaneButtonClick("layers"),
+ },
+ {
+ id: "renders",
+ label: renderJobs.length > 0 ? `Renders (${renderJobs.length})` : "Renders",
+ tooltip: "Render queue and exports",
+ active: rightPanelTab === "renders",
+ onSelect: () => setRightPanelTab("renders"),
+ },
+ ...(isSlideshowComposition
+ ? [
+ {
+ id: "slideshow",
+ label: "Slideshow",
+ tooltip: "Slideshow branching editor",
+ active: rightPanelTab === "slideshow",
+ onSelect: () => setRightPanelTab("slideshow"),
+ },
+ ]
+ : []),
+ {
+ id: "variables",
+ label: "Variables",
+ tooltip: "Template variables — declare, preview with values",
+ active: rightPanelTab === "variables",
+ onSelect: () => setRightPanelTab("variables"),
+ },
+ ];
+
return (
<>
{/* Vertical resize divider: 3px visible seam, 13px hit zone via the inner div. */}
@@ -376,40 +421,7 @@ export function StudioRightPanel({
) : (
<>
-
-
handleInspectorPaneButtonClick("design")}
- />
- handleInspectorPaneButtonClick("layers")}
- />
- 0 ? `Renders (${renderJobs.length})` : "Renders"}
- tooltip="Render queue and exports"
- active={rightPanelTab === "renders"}
- onClick={() => setRightPanelTab("renders")}
- />
- {isSlideshowComposition && (
- setRightPanelTab("slideshow")}
- />
- )}
- setRightPanelTab("variables")}
- />
-
+
{rightPanelTab === "block-params" && activeBlockParams ? (
= {
),
};
-/** The three control heights, 24 / 28 / 32 px, from `--spacing-ctl-*`. */
-const sizeStyles: Record = {
+/**
+ * The three control heights, 24 / 28 / 32 px, from `--spacing-ctl-*`.
+ * Exported so the Capture download `` can wear the same recipe as a Button.
+ */
+export const buttonSizes: Record = {
sm: "h-ctl-sm px-2 gap-1 rounded-sm text-step-11",
md: "h-ctl px-3 gap-1.5 rounded-md text-step-12",
lg: "h-ctl-lg px-4 gap-2 rounded-md text-step-13",
@@ -107,7 +110,7 @@ export const Button = forwardRef(
// `aria-disabled` as well as `disabled`: assistive tech announces the
// state even where the native attribute is filtered out of the tree.
aria-disabled={isDisabled || undefined}
- className={cn(buttonBase, buttonVariants[variant], sizeStyles[size], className)}
+ className={cn(buttonBase, buttonVariants[variant], buttonSizes[size], className)}
{...props}
>
{leading}
diff --git a/packages/studio/src/components/ui/index.ts b/packages/studio/src/components/ui/index.ts
index 1055707d7e..ad56b2dc81 100644
--- a/packages/studio/src/components/ui/index.ts
+++ b/packages/studio/src/components/ui/index.ts
@@ -1,7 +1,7 @@
// Studio's shared UI primitives. Every control in the app is meant to come
// from here, so a look or a keyboard behaviour is decided once.
export { cn } from "./cn";
-export { Button, buttonBase, buttonVariants } from "./Button";
+export { Button, buttonBase, buttonSizes, buttonVariants } from "./Button";
export type { ButtonSize, ButtonVariant, PreviewState } from "./Button";
export { IconButton } from "./IconButton";
export { Tab, TabPanel, Tabs, TabsList } from "./Tabs";
diff --git a/packages/studio/src/styles/hex-baseline.json b/packages/studio/src/styles/hex-baseline.json
index eb038c5e0d..752f99acb6 100644
--- a/packages/studio/src/styles/hex-baseline.json
+++ b/packages/studio/src/styles/hex-baseline.json
@@ -1,12 +1,11 @@
{
- "total": 423,
+ "total": 418,
"files": {
"packages/studio/src/captions/components/CaptionOverlay.tsx": 5,
"packages/studio/src/captions/components/CaptionTimeline.tsx": 11,
"packages/studio/src/captions/generator.ts": 1,
"packages/studio/src/captions/parser.ts": 1,
"packages/studio/src/captions/types.ts": 4,
- "packages/studio/src/components/StudioHeader.tsx": 5,
"packages/studio/src/components/StudioToast.tsx": 10,
"packages/studio/src/components/TimelineToolbar.tsx": 2,
"packages/studio/src/components/editor/ArcPathControls.tsx": 1,
diff --git a/packages/studio/src/styles/theme.css b/packages/studio/src/styles/theme.css
index 95520e9631..d40daf17e5 100644
--- a/packages/studio/src/styles/theme.css
+++ b/packages/studio/src/styles/theme.css
@@ -41,6 +41,11 @@
--color-media: #00e3ff;
--color-container: #f5a623;
+ /* The two stops of the HeyGen brand gradient, worn by the header logo. Fixed brand values
+ * rather than roles, so they are not reachable as a background or text colour by mistake. */
+ --color-brand-gradient-from: #06e3fa;
+ --color-brand-gradient-to: #4fdb5e;
+
/* -------------------------------------------------------------- geometry */
--radius-sm: 0.25rem;
diff --git a/scripts/check-no-main-deletions.mjs b/scripts/check-no-main-deletions.mjs
index 7f5975e731..691d0a554b 100644
--- a/scripts/check-no-main-deletions.mjs
+++ b/scripts/check-no-main-deletions.mjs
@@ -44,6 +44,10 @@ export const ALLOWED_DELETIONS = new Map([
"packages/studio/src/components/StudioGlobalDragOverlay.tsx",
"the full-screen drop-to-add box is replaced by a landing preview on the timeline; no remaining references",
],
+ [
+ "packages/studio/src/components/PanelTabButton.tsx",
+ "replaced by the shared Tabs primitive in RightPanelTabs; no remaining references",
+ ],
[
"packages/studio/postcss.config.js",
"Tailwind v4 migration: postcss replaced by @tailwindcss/vite",