@@ -84,19 +149,32 @@ const SongStructure = memo(function SongStructure({ sections, t }: { sections: R
role="region"
tabIndex={0}
className="overflow-x-auto rounded-2xl border border-white/10 bg-[linear-gradient(180deg,rgba(8,18,35,0.96),rgba(2,6,23,0.98))] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-300"
- aria-label="Scrollable song structure timeline"
+ aria-label={t("workspaceSongStructureTimelineRegionAria")}
>
- {sections.map((section) => (
-
-
- {section.label} · {formatTimelineTime(section.timeRange.start)}–{formatTimelineTime(section.timeRange.end)}
-
-
{section.groove}
+ {sections.map((section, sectionIndex) => (
+
+
))}
@@ -121,6 +199,7 @@ const SongStructure = memo(function SongStructure({ sections, t }: { sections: R
/** Documented. */
export function Workspace({ song, sourceBootstrap = null, onSongUpdate }: WorkspaceProps) {
const [activeRole, setActiveRole] = useState
(null);
+ const [loopedSectionIndex, setLoopedSectionIndex] = useState(null);
const t = useMemo(() => createTranslator(detectPreferredLocale()), []);
// Extract all unique roles from the song's sections
@@ -225,6 +304,18 @@ export function Workspace({ song, sourceBootstrap = null, onSongUpdate }: Worksp
const roleTranspositionPlan =
nonBlankText(activeRoleDetails?.transpositionPlan) ??
nonBlankText(activeRoleDetails?.simplification);
+ const loopSectionIndex = firstLoopSectionIndex(song, activeRole);
+ const loopSection = loopSectionIndex === undefined ? undefined : song.sections[loopSectionIndex];
+ const loopedSection = loopedSectionIndex === null ? null : (song.sections[loopedSectionIndex] ?? null);
+
+ /** Arm a rehearsal loop and move focus to the matching renderer-owned roadmap card. */
+ const armSectionLoop = (sectionIndex: number): void => {
+ if (!Number.isSafeInteger(sectionIndex) || sectionIndex < 0 || sectionIndex >= song.sections.length) {
+ return;
+ }
+ setLoopedSectionIndex(sectionIndex);
+ focusWorkspaceSection(sectionIndex);
+ };
/** Documented. */
const handleExportCueSheet = () => {
@@ -353,7 +444,17 @@ export function Workspace({ song, sourceBootstrap = null, onSongUpdate }: Worksp
@@ -376,8 +477,8 @@ export function Workspace({ song, sourceBootstrap = null, onSongUpdate }: Worksp