-
{t("workspaceSongStructureLabel")}
-
{t("workspaceRehearsalTimelineLabel")}
+
+
+
+
{t("workspaceSongStructureLabel")}
+ {t("workspaceRehearsalTimelineLabel")}
+
+
{cueSummary}
+
+ {focusSection ? (
+
+ ) : (
+
+ )}
- {sections.map((section) => (
-
-
- {section.label} · {formatTimelineTime(section.timeRange.start)}–{formatTimelineTime(section.timeRange.end)}
-
-
{section.groove}
-
- ))}
+ {sections.map((section) => {
+ const isCued = section.id === cuedSectionId;
+ return (
+
+
+ {section.label} · {formatTimelineTime(section.timeRange.start)}–{formatTimelineTime(section.timeRange.end)}
+
+
{section.groove}
+
+ );
+ })}
@@ -113,13 +228,19 @@ const SongStructure = memo(function SongStructure({ sections, t }: { sections: R
+ {cuedSection ? (
+
+ {cueStatus}
+
+ ) : null}
);
});
-/** Documented. */
+/** Render the rehearsal workspace, including role planning and a focusable song-structure cue timeline. */
export function Workspace({ song, sourceBootstrap = null, onSongUpdate }: WorkspaceProps) {
const [activeRole, setActiveRole] = useState
(null);
+ const [cuedTimelineSectionId, setCuedTimelineSectionId] = useState(null);
const t = useMemo(() => createTranslator(detectPreferredLocale()), []);
// Extract all unique roles from the song's sections
@@ -212,6 +333,16 @@ export function Workspace({ song, sourceBootstrap = null, onSongUpdate }: Worksp
const roleTranspositionPlan =
nonBlankText(activeRoleDetails?.transpositionPlan) ??
nonBlankText(activeRoleDetails?.simplification);
+ const focusSection = firstFocusSection(song);
+
+ /** Cue tonight's first lock-in on the song-structure timeline without inventing playback. */
+ const cueTonightFocus = (): void => {
+ if (!focusSection) {
+ return;
+ }
+ setCuedTimelineSectionId(focusSection.id);
+ focusTimelineSection(focusSection.id);
+ };
/** Documented. */
const handleExportCueSheet = () => {
@@ -331,7 +462,13 @@ export function Workspace({ song, sourceBootstrap = null, onSongUpdate }: Worksp
-