From 909c2db13628ef9f93d4247768d7be3ebf78f8dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Mon, 30 Mar 2026 23:47:13 +0200 Subject: [PATCH 1/3] feat(studio): timeline hidden by default with toggle in header + player controls Adds timelineVisible state (defaults false) with synced toggle buttons in both the header toolbar and the PlayerControls bar. Toggle icon turns teal when timeline is visible. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- packages/studio/src/App.tsx | 26 ++++++ .../studio/src/components/nle/NLELayout.tsx | 93 +++++++++++-------- .../src/player/components/PlayerControls.tsx | 31 +++++++ 3 files changed, 111 insertions(+), 39 deletions(-) diff --git a/packages/studio/src/App.tsx b/packages/studio/src/App.tsx index bc9504808..f8ffb5699 100644 --- a/packages/studio/src/App.tsx +++ b/packages/studio/src/App.tsx @@ -286,6 +286,7 @@ export function StudioApp() { const [rightWidth, setRightWidth] = useState(400); const [leftCollapsed, setLeftCollapsed] = useState(false); const [rightCollapsed, setRightCollapsed] = useState(true); + const [timelineVisible, setTimelineVisible] = useState(false); const panelDragRef = useRef<{ side: "left" | "right"; startX: number; @@ -575,6 +576,29 @@ export function StudioApp() { + + )} ); }); From eca7a2caec29530bff97e3800bebc7da520f601a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Tue, 31 Mar 2026 00:22:34 +0200 Subject: [PATCH 2/3] fix(studio): move player controls into preview area so they stay visible when timeline is hidden --- .../studio/src/components/nle/NLELayout.tsx | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/packages/studio/src/components/nle/NLELayout.tsx b/packages/studio/src/components/nle/NLELayout.tsx index 07b1d2d15..b29d267a8 100644 --- a/packages/studio/src/components/nle/NLELayout.tsx +++ b/packages/studio/src/components/nle/NLELayout.tsx @@ -317,17 +317,34 @@ export const NLELayout = memo(function NLELayout({ onKeyDown={handleKeyDown} tabIndex={-1} > - {/* Preview — takes remaining space above timeline */} -
- - {previewOverlay} + {/* Preview + player controls — takes remaining space above timeline */} +
+
+ + {previewOverlay} +
+ {/* Player controls always visible, regardless of timeline state */} +
+ {compositionStack.length > 1 && ( + + )} + +
{(timelineVisible ?? true) && ( @@ -343,22 +360,6 @@ export const NLELayout = memo(function NLELayout({ {/* Timeline section — fixed height, resizable */}
- {/* Breadcrumb + Player controls */} -
- {compositionStack.length > 1 && ( - - )} - -
- {/* Timeline tracks */}
Date: Tue, 31 Mar 2026 00:38:15 +0200 Subject: [PATCH 3/3] feat(studio): use phosphor duotone file-type icons in file tree --- .../studio/src/components/editor/FileTree.tsx | 83 +++++++------------ 1 file changed, 30 insertions(+), 53 deletions(-) diff --git a/packages/studio/src/components/editor/FileTree.tsx b/packages/studio/src/components/editor/FileTree.tsx index 31a0977ee..18a43cfaf 100644 --- a/packages/studio/src/components/editor/FileTree.tsx +++ b/packages/studio/src/components/editor/FileTree.tsx @@ -1,5 +1,19 @@ import { memo, useState, useCallback } from "react"; -import { Film, Music, Image, ChevronDown, ChevronRight } from "../../icons/SystemIcons"; +import { + FileHtml, + FileCss, + FileJs, + FileJsx, + FileTs, + FileTsx, + FileTxt, + FileCode, + File, + FilmStrip, + MusicNote, + Image as PhImage, +} from "@phosphor-icons/react"; +import { ChevronDown, ChevronRight } from "../../icons/SystemIcons"; interface FileTreeProps { files: string[]; @@ -7,65 +21,28 @@ interface FileTreeProps { onSelectFile: (path: string) => void; } -/** VS Code–style language badge: colored rounded rect with a 2–3 letter label. */ -function Badge({ label, bg, text = "#fff" }: { label: string; bg: string; text?: string }) { - return ( - - {label} - - ); -} +const SZ = 14; -/** Render a file-type icon for a given file path. */ function FileIcon({ path }: { path: string }) { const ext = path.split(".").pop()?.toLowerCase() ?? ""; - // Language badges - if (ext === "html") return ; - if (ext === "js" || ext === "mjs" || ext === "cjs") - return ; - if (ext === "ts" || ext === "mts") return ; - if (ext === "css") return ; - if (ext === "json") return ; - if (ext === "md" || ext === "mdx") return ; - if (ext === "svg") return ; + const d = { size: SZ, weight: "duotone" as const, className: "flex-shrink-0" }; + if (ext === "html") return ; + if (ext === "css") return ; + if (ext === "js" || ext === "mjs" || ext === "cjs") return ; + if (ext === "jsx") return ; + if (ext === "ts" || ext === "mts") return ; + if (ext === "tsx") return ; + if (ext === "txt" || ext === "md" || ext === "mdx") return ; + if (ext === "json" || ext === "svg") return ; if (ext === "wav" || ext === "mp3" || ext === "ogg" || ext === "m4a") - return ; + return ; if (ext === "mp4" || ext === "webm" || ext === "mov") - return ; + return ; if (ext === "png" || ext === "jpg" || ext === "jpeg" || ext === "webp" || ext === "gif") - return ; + return ; if (ext === "woff" || ext === "woff2" || ext === "ttf" || ext === "otf") - return ; - if (ext === "txt") return ; - // Generic document - return ( - - - - - ); + return ; + return ; } interface TreeNode {