tfrere HF Staff commited on
Commit
b4825aa
·
1 Parent(s): d1ce089

feat: improve Release Notes display with cleaner parsing and anchor link

Browse files
Files changed (1) hide show
  1. src/pages/Download.jsx +112 -126
src/pages/Download.jsx CHANGED
@@ -88,30 +88,50 @@ function formatDate(dateString) {
88
  });
89
  }
90
 
91
- // Clean release body - filter out auto-generated GitHub content
92
- function cleanReleaseBody(body) {
93
- if (!body) return null;
94
 
95
- // Filter out lines that are auto-generated or not useful
96
- const filteredLines = body
97
- .split('\n')
98
- .filter(line => {
99
- const trimmed = line.trim();
100
- // Skip HTML comments
101
- if (trimmed.startsWith('<!--') || trimmed.endsWith('-->')) return false;
102
- // Skip "Full Changelog" links
103
- if (trimmed.startsWith('**Full Changelog**:')) return false;
104
- // Skip generic placeholder messages
105
- if (trimmed === 'See the assets to download this version and install.') return false;
106
- return true;
107
- })
108
- .join('\n')
109
- .trim();
110
 
111
- // If nothing meaningful left, return null
112
- if (!filteredLines || filteredLines.length < 10) return null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
- return filteredLines;
115
  }
116
 
117
  // Windows Icon
@@ -696,14 +716,16 @@ export default function Download() {
696
  </Button>
697
  </Box>
698
 
699
- {/* Release Notes - Temporarily hidden
700
  {allReleases.length > 0 && (
701
  <Box
 
702
  sx={{
703
  background: 'rgba(255, 255, 255, 0.02)',
704
  border: '1px solid rgba(255, 255, 255, 0.06)',
705
  borderRadius: 4,
706
  p: 4,
 
707
  }}
708
  >
709
  <Typography
@@ -713,131 +735,95 @@ export default function Download() {
713
  Release Notes
714
  </Typography>
715
 
716
- <Stack spacing={3}>
717
- {(showAllReleases ? allReleases : allReleases.slice(0, 3)).map((release) => (
718
- <Box
719
- key={release.id}
720
- sx={{
721
- borderLeft: '2px solid rgba(255, 149, 0, 0.4)',
722
- pl: 3,
723
- py: 1,
724
- }}
725
- >
726
- <Stack direction="row" spacing={2} alignItems="center" sx={{ mb: 1 }}>
727
- <Typography
728
- variant="subtitle1"
729
- sx={{ color: 'white', fontWeight: 600 }}
730
- >
731
- {release.name || release.tag_name}
732
- </Typography>
733
- <Chip
734
- label={formatDate(release.published_at)}
735
- size="small"
736
  sx={{
737
- backgroundColor: 'rgba(255, 255, 255, 0.05)',
738
- color: 'rgba(255,255,255,0.5)',
739
- fontSize: 11,
740
- height: 22,
741
  }}
742
- />
743
- {release.prerelease && (
744
- <Chip
745
- label="Pre-release"
746
- size="small"
747
- sx={{
748
- backgroundColor: 'rgba(255, 149, 0, 0.15)',
749
- color: '#FF9500',
750
- fontSize: 11,
751
- height: 22,
752
- }}
753
- />
754
- )}
755
- </Stack>
756
-
757
- {(() => {
758
- const cleanedBody = cleanReleaseBody(release.body);
759
- if (cleanedBody) {
760
- return (
761
  <Typography
762
- variant="body2"
763
- sx={{
764
- color: 'rgba(255,255,255,0.6)',
765
- whiteSpace: 'pre-line',
766
- lineHeight: 1.7,
767
- '& strong, & b': { color: 'rgba(255,255,255,0.8)' },
768
- }}
769
  >
770
- {cleanedBody.split('\n').map((line, i) => {
771
- // Simple markdown parsing for headers and lists
772
- if (line.startsWith('### ')) {
773
- return (
774
- <Box
775
- key={i}
776
- component="span"
777
- sx={{
778
- display: 'block',
779
- fontWeight: 600,
780
- color: 'rgba(255,255,255,0.8)',
781
- mt: i > 0 ? 1.5 : 0,
782
- mb: 0.5,
783
- }}
784
- >
785
- {line.replace('### ', '')}
786
- </Box>
787
- );
788
- }
789
- if (line.startsWith('- ')) {
790
- return (
791
- <Box key={i} component="span" sx={{ display: 'block', pl: 2 }}>
792
- • {line.replace('- ', '')}
793
- </Box>
794
- );
795
- }
796
- if (line.trim() === '') return null;
797
- return <Box key={i} component="span" sx={{ display: 'block' }}>{line}</Box>;
798
- })}
799
  </Typography>
800
- );
801
- }
802
- // No meaningful release notes - show link to changelog
803
- return (
804
- <Typography
805
- variant="body2"
806
- sx={{ color: 'rgba(255,255,255,0.5)' }}
807
- >
808
- Bug fixes and improvements.{' '}
809
- <Box
810
- component="a"
811
- href={`https://github.com/pollen-robotics/reachy-mini-desktop-app/compare/${release.tag_name.replace('v', 'v')}...${release.tag_name}`}
812
- target="_blank"
813
- sx={{ color: '#FF9500', textDecoration: 'none', '&:hover': { textDecoration: 'underline' } }}
814
- >
815
- View changes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
816
  </Box>
817
- </Typography>
818
- );
819
- })()}
820
- </Box>
821
- ))}
822
  </Stack>
823
 
824
- {allReleases.length > 3 && (
825
  <Button
826
  variant="text"
827
  onClick={() => setShowAllReleases(!showAllReleases)}
828
  endIcon={showAllReleases ? <ExpandLessIcon /> : <ExpandMoreIcon />}
829
  sx={{
830
- mt: 3,
831
  color: 'rgba(255,255,255,0.5)',
832
  '&:hover': { color: 'white' },
833
  }}
834
  >
835
- {showAllReleases ? 'Show less' : `Show ${allReleases.length - 3} more releases`}
836
  </Button>
837
  )}
838
  </Box>
839
  )}
840
- */}
841
  </Container>
842
  </Box>
843
  </Layout>
 
88
  });
89
  }
90
 
91
+ // Parse release body and extract clean changes
92
+ function parseReleaseChanges(body) {
93
+ if (!body) return [];
94
 
95
+ const changes = [];
96
+ const lines = body.split('\n');
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
+ for (const line of lines) {
99
+ const trimmed = line.trim();
100
+
101
+ // Skip empty lines, headers, and meta content
102
+ if (!trimmed) continue;
103
+ if (trimmed.startsWith('##')) continue; // Skip all headers (## What's Changed, etc.)
104
+ if (trimmed.startsWith('**Full Changelog**')) continue;
105
+ if (trimmed.startsWith('**New Contributors**')) continue;
106
+ if (trimmed.includes('made their first contribution')) continue;
107
+ if (trimmed.startsWith('<!--') || trimmed.endsWith('-->')) continue;
108
+ if (trimmed === 'See the assets to download this version and install.') continue;
109
+
110
+ // Parse change lines (starting with * or -)
111
+ if (trimmed.startsWith('*') || trimmed.startsWith('-')) {
112
+ let change = trimmed.replace(/^[\*\-]\s*/, '');
113
+
114
+ // Extract the description from markdown links: "fix: description by @user in https://..."
115
+ // We want to keep: "fix: description"
116
+ const byMatch = change.match(/^(.+?)\s+by\s+@\w+/i);
117
+ if (byMatch) {
118
+ change = byMatch[1].trim();
119
+ }
120
+
121
+ // Remove trailing "in https://..." links
122
+ change = change.replace(/\s+in\s+https:\/\/[^\s]+$/i, '');
123
+
124
+ // Clean up any remaining markdown link syntax [text](url) -> text
125
+ change = change.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1');
126
+
127
+ // Skip if it's just a contributor line or empty after cleaning
128
+ if (change && change.length > 3 && !change.includes('first contribution')) {
129
+ changes.push(change);
130
+ }
131
+ }
132
+ }
133
 
134
+ return changes;
135
  }
136
 
137
  // Windows Icon
 
716
  </Button>
717
  </Box>
718
 
719
+ {/* Release Notes */}
720
  {allReleases.length > 0 && (
721
  <Box
722
+ id="release-notes"
723
  sx={{
724
  background: 'rgba(255, 255, 255, 0.02)',
725
  border: '1px solid rgba(255, 255, 255, 0.06)',
726
  borderRadius: 4,
727
  p: 4,
728
+ scrollMarginTop: '100px', // Offset for fixed header
729
  }}
730
  >
731
  <Typography
 
735
  Release Notes
736
  </Typography>
737
 
738
+ <Stack spacing={2.5}>
739
+ {(showAllReleases ? allReleases : allReleases.slice(0, 5))
740
+ .map((release) => {
741
+ const changes = parseReleaseChanges(release.body);
742
+ return (
743
+ <Box
744
+ key={release.id}
 
 
 
 
 
 
 
 
 
 
 
 
 
745
  sx={{
746
+ borderLeft: '2px solid rgba(255, 149, 0, 0.4)',
747
+ pl: 3,
748
+ py: 0.5,
 
749
  }}
750
+ >
751
+ <Stack direction="row" spacing={2} alignItems="center" flexWrap="wrap" sx={{ mb: changes.length > 0 ? 1 : 0 }}>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
752
  <Typography
753
+ variant="subtitle2"
754
+ sx={{ color: 'white', fontWeight: 600 }}
 
 
 
 
 
755
  >
756
+ {release.tag_name}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
757
  </Typography>
758
+ <Chip
759
+ label={formatDate(release.published_at)}
760
+ size="small"
761
+ sx={{
762
+ backgroundColor: 'rgba(255, 255, 255, 0.05)',
763
+ color: 'rgba(255,255,255,0.5)',
764
+ fontSize: 10,
765
+ height: 20,
766
+ }}
767
+ />
768
+ {release.prerelease && (
769
+ <Chip
770
+ label="Pre-release"
771
+ size="small"
772
+ sx={{
773
+ backgroundColor: 'rgba(255, 149, 0, 0.15)',
774
+ color: '#FF9500',
775
+ fontSize: 10,
776
+ height: 20,
777
+ }}
778
+ />
779
+ )}
780
+ </Stack>
781
+
782
+ {changes.length > 0 && (
783
+ <Box component="ul" sx={{ m: 0, pl: 2.5, listStyle: 'none' }}>
784
+ {changes.map((change, i) => (
785
+ <Box
786
+ component="li"
787
+ key={i}
788
+ sx={{
789
+ color: 'rgba(255,255,255,0.6)',
790
+ fontSize: 13,
791
+ lineHeight: 1.6,
792
+ position: 'relative',
793
+ '&::before': {
794
+ content: '"•"',
795
+ position: 'absolute',
796
+ left: -14,
797
+ color: 'rgba(255, 149, 0, 0.6)',
798
+ }
799
+ }}
800
+ >
801
+ {change}
802
+ </Box>
803
+ ))}
804
  </Box>
805
+ )}
806
+ </Box>
807
+ );
808
+ })}
 
809
  </Stack>
810
 
811
+ {allReleases.length > 5 && (
812
  <Button
813
  variant="text"
814
  onClick={() => setShowAllReleases(!showAllReleases)}
815
  endIcon={showAllReleases ? <ExpandLessIcon /> : <ExpandMoreIcon />}
816
  sx={{
817
+ mt: 2,
818
  color: 'rgba(255,255,255,0.5)',
819
  '&:hover': { color: 'white' },
820
  }}
821
  >
822
+ {showAllReleases ? 'Show less' : 'Show older releases'}
823
  </Button>
824
  )}
825
  </Box>
826
  )}
 
827
  </Container>
828
  </Box>
829
  </Layout>