@@ -132,38 +132,17 @@ const BeatGrid: React.FC<BeatGridProps> = ({ beats = [], totalBeats = 16, groupS
132132 } as Beat ) ;
133133 }
134134
135- // Group beats
136- const groups = [ ] ;
137- for ( let i = 0 ; i < totalBeats ; i += groupSize ) {
138- const group = beatsToRender . slice ( i , i + groupSize ) ;
139- groups . push ( group ) ;
140- }
141-
142135 return (
143- < div className = "grid grid-cols-4 gap-0 border border-gray-200 rounded-lg" >
144- { groups . map ( ( group , groupIndex ) => (
145- < div key = { groupIndex } className = "border-r border-gray-200 last:border-r-0" >
146- < div className = "grid grid-cols-4" >
147- { group . map ( ( beat , beatIndex ) => {
148- const renderedBeat = renderBeat ( beat ) ;
149- const isLyrics = typeof beat !== 'number' &&
150- beat ?. elements ?. some ( e => e ?. lyrics ) || false ;
151- const className = isLyrics ? 'text-blue-600 font-medium' : 'text-black' ;
152-
153- return (
154- < div
155- key = { `${ groupIndex } -${ beatIndex } ` }
156- className = "text-center p-1 border-r border-gray-100 last:border-r-0 relative group"
157- title = { `Beat ${ groupIndex * groupSize + beatIndex + 1 } ` }
158- >
159- < span className = { className } > { renderedBeat } </ span >
160- < div className = "absolute -top-8 left-1/2 transform -translate-x-1/2 bg-black text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none" >
161- Beat { groupIndex * groupSize + beatIndex + 1 }
162- </ div >
163- </ div >
164- ) ;
165- } ) }
166- </ div >
136+ < div className = "grid grid-cols-16 gap-1" >
137+ { beatsToRender . map ( ( beat , index ) => (
138+ < div
139+ key = { index }
140+ className = { `border p-2 text-center hover:bg-gray-50 transition-colors ${
141+ index % groupSize === 0 ? 'border-l-2' : ''
142+ } `}
143+ title = { `Beat ${ index + 1 } ` }
144+ >
145+ { typeof beat === 'number' ? beat : formatter . formatBeat ( beat ) }
167146 </ div >
168147 ) ) }
169148 </ div >
@@ -389,6 +368,71 @@ const SUREditor: React.FC<{ content: string; onChange: (content: string) => void
389368 ) ;
390369} ;
391370
371+ interface PreviewProps {
372+ document : SurDocument ;
373+ }
374+
375+ const formatter = new SurFormatter ( ) ;
376+
377+ export function Preview ( { document } : PreviewProps ) {
378+ if ( ! document ) return null ;
379+
380+ // Calculate total beats
381+ const totalBeats = document . composition . sections . reduce (
382+ ( sum , section ) => sum + section . beats . length ,
383+ 0
384+ ) ;
385+
386+ return (
387+ < div className = "preview-container" >
388+ { /* Title Section */ }
389+ < h1 className = "text-2xl font-bold mb-4" >
390+ { document . metadata . name || 'Untitled Composition' }
391+ </ h1 >
392+
393+ { /* Metadata Section */ }
394+ < div className = "metadata-section mb-4" >
395+ < div className = "grid grid-cols-2 gap-2" >
396+ < div > Raag:</ div >
397+ < div > { document . metadata . raag || 'Not specified' } </ div >
398+ < div > Taal:</ div >
399+ < div > { document . metadata . taal || 'Not specified' } </ div >
400+ < div > Tempo:</ div >
401+ < div > { document . metadata . tempo || 'Not specified' } </ div >
402+ </ div >
403+ </ div >
404+
405+ { /* Statistics */ }
406+ < div className = "stats-section mb-4" >
407+ < p > Total Beats: { totalBeats } </ p >
408+ </ div >
409+
410+ { /* Composition Sections */ }
411+ { document . composition . sections . map ( ( section , sectionIndex ) => (
412+ < div key = { sectionIndex } className = "section-container mb-6" >
413+ { /* Section Header */ }
414+ < h2 className = "text-xl font-semibold mb-2" >
415+ { section . title || 'Untitled Section' }
416+ </ h2 >
417+
418+ { /* Beats Grid */ }
419+ < div className = "grid grid-cols-8 gap-2" >
420+ { section . beats . map ( ( beat , beatIndex ) => (
421+ < div
422+ key = { beatIndex }
423+ className = "border p-2 text-center"
424+ title = { `Beat ${ beatIndex + 1 } ` }
425+ >
426+ { formatter . formatBeat ( beat ) }
427+ </ div >
428+ ) ) }
429+ </ div >
430+ </ div >
431+ ) ) }
432+ </ div >
433+ ) ;
434+ }
435+
392436const SUREditorViewer = ( ) => {
393437 const [ content , setContent ] = useState ( DEFAULT_SUR ) ;
394438 const [ hideControls , setHideControls ] = useState ( false ) ;
@@ -410,58 +454,155 @@ const SUREditorViewer = () => {
410454
411455 < TabsContent value = "preview" >
412456 < Card className = "w-full" >
413- < CardHeader className = "border-b border-gray-200" >
414- < div className = "flex justify-between items-start" >
415- < div onClick = { toggleControls } >
416- < CardTitle className = "text-2xl font-bold mb-2" >
417- Preview
418- </ CardTitle >
457+ < CardHeader className = "border-b border-gray-200 pb-6" >
458+ { /* First Row: Title and Download Button */ }
459+ < div className = "flex justify-between items-center mb-4" >
460+ < CardTitle className = "text-3xl font-bold" >
461+ { ( ( ) => {
462+ try {
463+ const surDoc = parseSURFile ( content ) ;
464+ return surDoc . metadata . name || 'Untitled Composition' ;
465+ } catch ( e ) {
466+ return 'Preview' ;
467+ }
468+ } ) ( ) }
469+ </ CardTitle >
470+ { ( ( ) => {
471+ try {
472+ const surDoc = parseSURFile ( content ) ;
473+ return (
474+ < PDFExporter
475+ config = { {
476+ name : surDoc . metadata . name || 'Untitled' ,
477+ tempo : surDoc . metadata . tempo ,
478+ beats_per_row : surDoc . metadata . beats_per_row
479+ } }
480+ composition = { surDoc . composition . sections . map ( section => ( {
481+ title : section . title ,
482+ lines : groupBeatsIntoLines ( section . beats ) . map ( line => ( {
483+ beats : line
484+ } ) )
485+ } ) ) }
486+ />
487+ ) ;
488+ } catch ( e ) {
489+ return null ;
490+ }
491+ } ) ( ) }
492+ </ div >
493+
494+ { /* Second Row: Metadata */ }
495+ < div className = "cursor-pointer" onClick = { toggleControls } >
496+ < div className = { `metadata-section ${ hideControls ? 'hidden' : '' } ` } >
497+ { ( ( ) => {
498+ try {
499+ const surDoc = parseSURFile ( content ) ;
500+ return (
501+ < div className = "bg-gray-50 rounded-lg p-4" >
502+ < dl className = "grid grid-cols-3 gap-6" >
503+ < div >
504+ < dt className = "text-sm font-medium text-gray-500 mb-1" > Raag</ dt >
505+ < dd className = "text-base font-semibold text-gray-900" >
506+ { surDoc . metadata . raag || 'Not specified' }
507+ </ dd >
508+ </ div >
509+ < div >
510+ < dt className = "text-sm font-medium text-gray-500 mb-1" > Taal</ dt >
511+ < dd className = "text-base font-semibold text-gray-900" >
512+ { surDoc . metadata . taal || 'Not specified' }
513+ </ dd >
514+ </ div >
515+ < div >
516+ < dt className = "text-sm font-medium text-gray-500 mb-1" > Tempo</ dt >
517+ < dd className = "text-base font-semibold text-gray-900" >
518+ { surDoc . metadata . tempo || 'Not specified' }
519+ </ dd >
520+ </ div >
521+ </ dl >
522+ </ div >
523+ ) ;
524+ } catch ( e ) {
525+ return null ;
526+ }
527+ } ) ( ) }
419528 </ div >
420529 </ div >
421530 </ CardHeader >
422-
531+
423532 < CardContent className = "p-6" >
424533 < div className = "space-y-6" >
425534 { /* Beat numbers row */ }
426535 < div className = "mb-3 font-mono text-sm" >
427536 < div className = "text-gray-600 mb-0.5" > Beat:</ div >
428- < BeatGrid
429- beats = { Array . from ( { length : 16 } , ( _ , i ) => i + 1 ) }
430- totalBeats = { 16 }
431- groupSize = { 4 }
432- />
537+ < div className = "grid grid-cols-4 gap-0 border border-gray-200 rounded-lg" >
538+ { [ 0 , 1 , 2 , 3 ] . map ( ( group ) => (
539+ < div key = { group } className = "border-r border-gray-200 last:border-r-0" >
540+ < div className = "grid grid-cols-4" >
541+ { [ 1 , 2 , 3 , 4 ] . map ( ( num ) => {
542+ const beatNum = group * 4 + num ;
543+ return (
544+ < div
545+ key = { beatNum }
546+ className = "text-center p-2 border-r border-gray-100 last:border-r-0"
547+ title = { `Beat ${ beatNum } ` }
548+ >
549+ { beatNum }
550+ </ div >
551+ ) ;
552+ } ) }
553+ </ div >
554+ </ div >
555+ ) ) }
556+ </ div >
433557 </ div >
434-
558+
435559 { /* Composition sections */ }
436560 { ( ( ) => {
437561 try {
438562 const surDoc = parseSURFile ( content ) ;
439- console . log ( 'Rendering document:' , surDoc ) ;
563+ const formatter = new SurFormatter ( ) ;
440564
441565 return surDoc . composition . sections . map ( ( section , sectionIdx ) => {
442- console . log ( 'Rendering section:' , section . title , 'beats:' , section . beats ) ;
443-
444- if ( ! Array . isArray ( section . beats ) ) {
445- console . error ( 'Section beats is not an array:' , section . beats ) ;
446- return null ;
447- }
448-
449566 // Group beats into lines
450567 const beatLines = groupBeatsIntoLines ( section . beats ) ;
451568
452569 return (
453570 < div key = { sectionIdx } className = "space-y-1.5" >
454- < h3 className = "text-lg font-semibold text-blue-600 mb-1 " >
571+ < h3 className = "text-lg font-semibold text-blue-600 mb-2 " >
455572 { section . title }
456573 </ h3 >
457574 < div className = "font-mono text-sm space-y-2" >
458575 { beatLines . map ( ( beatLine , lineIdx ) => (
459- < BeatGrid
460- key = { `${ sectionIdx } -${ lineIdx } ` }
461- beats = { beatLine }
462- totalBeats = { 16 }
463- groupSize = { 4 }
464- />
576+ < div key = { `${ sectionIdx } -${ lineIdx } ` }
577+ className = "grid grid-cols-4 gap-0 border border-gray-200 rounded-lg" >
578+ { [ 0 , 1 , 2 , 3 ] . map ( ( group ) => (
579+ < div key = { group } className = "border-r border-gray-200 last:border-r-0" >
580+ < div className = "grid grid-cols-4" >
581+ { [ 0 , 1 , 2 , 3 ] . map ( ( num ) => {
582+ const beatIndex = group * 4 + num ;
583+ const beat = beatLine [ beatIndex ] || {
584+ elements : [ { note : { pitch : NotePitch . SILENCE } } ] ,
585+ bracketed : false
586+ } ;
587+ return (
588+ < div
589+ key = { beatIndex }
590+ className = { `text-center p-2 border-r border-gray-100 last:border-r-0 relative group ${
591+ beat . elements . some ( e => e . lyrics ) ? 'text-blue-600' : ''
592+ } `}
593+ >
594+ { formatter . formatBeat ( beat ) }
595+ { /* Tooltip */ }
596+ < div className = "absolute -top-8 left-1/2 transform -translate-x-1/2 bg-black text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none" >
597+ Beat { lineIdx * 16 + beatIndex + 1 }
598+ </ div >
599+ </ div >
600+ ) ;
601+ } ) }
602+ </ div >
603+ </ div >
604+ ) ) }
605+ </ div >
465606 ) ) }
466607 </ div >
467608 </ div >
0 commit comments