11import "./index.css"
22import { Title , Meta , Link } from "@solidjs/meta"
3- import { createAsync , query } from "@solidjs/router"
3+ import { createAsync } from "@solidjs/router"
44import { Header } from "~/component/header"
55import { Footer } from "~/component/footer"
66import { Legal } from "~/component/legal"
77import { config } from "~/config"
88import { For , Show , createSignal } from "solid-js"
9-
10- type Release = {
11- tag_name : string
12- name : string
13- body : string
14- published_at : string
15- html_url : string
16- }
17-
18- const getReleases = query ( async ( ) => {
19- "use server"
20- const response = await fetch ( "https://api.github.com/repos/anomalyco/opencode/releases?per_page=20" , {
21- headers : {
22- Accept : "application/vnd.github.v3+json" ,
23- "User-Agent" : "OpenCode-Console" ,
24- } ,
25- cf : {
26- cacheTtl : 60 * 5 ,
27- cacheEverything : true ,
28- } ,
29- } as any )
30- if ( ! response . ok ) return [ ]
31- return response . json ( ) as Promise < Release [ ] >
32- } , "releases.get" )
33-
34- function formatDate ( dateString : string ) {
35- const date = new Date ( dateString )
36- return date . toLocaleDateString ( "en-US" , {
37- year : "numeric" ,
38- month : "short" ,
39- day : "numeric" ,
40- } )
41- }
9+ import { getRequestEvent } from "solid-js/web"
4210
4311type HighlightMedia = { type : "video" ; src : string } | { type : "image" ; src : string ; width : string ; height : string }
4412
@@ -54,68 +22,33 @@ type HighlightGroup = {
5422 items : HighlightItem [ ]
5523}
5624
57- function parseHighlights ( body : string ) : HighlightGroup [ ] {
58- const groups = new Map < string , HighlightItem [ ] > ( )
59- const regex = / < h i g h l i g h t \s + s o u r c e = " ( [ ^ " ] + ) " > ( [ \s \S ] * ?) < \/ h i g h l i g h t > / g
60- let match
61-
62- while ( ( match = regex . exec ( body ) ) !== null ) {
63- const source = match [ 1 ]
64- const content = match [ 2 ]
65-
66- const titleMatch = content . match ( / < h 2 > ( [ ^ < ] + ) < \/ h 2 > / )
67- const pMatch = content . match ( / < p (?: \s + s h o r t = " ( [ ^ " ] * ) " ) ? > ( [ ^ < ] + ) < \/ p > / )
68- const imgMatch = content . match ( / < i m g \s + w i d t h = " ( [ ^ " ] + ) " \s + h e i g h t = " ( [ ^ " ] + ) " \s + a l t = " [ ^ " ] * " \s + s r c = " ( [ ^ " ] + ) " / )
69- const videoMatch = content . match ( / ^ \s * ( h t t p s : \/ \/ g i t h u b \. c o m \/ u s e r - a t t a c h m e n t s \/ a s s e t s \/ [ a - f 0 - 9 - ] + ) \s * $ / m)
70-
71- let media : HighlightMedia | undefined
72- if ( videoMatch ) {
73- media = { type : "video" , src : videoMatch [ 1 ] }
74- } else if ( imgMatch ) {
75- media = { type : "image" , src : imgMatch [ 3 ] , width : imgMatch [ 1 ] , height : imgMatch [ 2 ] }
76- }
77-
78- if ( titleMatch && media ) {
79- const item : HighlightItem = {
80- title : titleMatch [ 1 ] ,
81- description : pMatch ?. [ 2 ] || "" ,
82- shortDescription : pMatch ?. [ 1 ] ,
83- media,
84- }
85-
86- if ( ! groups . has ( source ) ) {
87- groups . set ( source , [ ] )
88- }
89- groups . get ( source ) ! . push ( item )
90- }
91- }
92-
93- return Array . from ( groups . entries ( ) ) . map ( ( [ source , items ] ) => ( { source, items } ) )
25+ type ChangelogRelease = {
26+ tag : string
27+ name : string
28+ date : string
29+ url : string
30+ highlights : HighlightGroup [ ]
31+ sections : { title : string ; items : string [ ] } [ ]
9432}
9533
96- function parseMarkdown ( body : string ) {
97- const lines = body . split ( "\n" )
98- const sections : { title : string ; items : string [ ] } [ ] = [ ]
99- let current : { title : string ; items : string [ ] } | null = null
100- let skip = false
34+ async function getReleases ( ) {
35+ const event = getRequestEvent ( )
36+ const url = event ? new URL ( "/changelog.json" , event . request . url ) . toString ( ) : "/changelog.json"
10137
102- for ( const line of lines ) {
103- if ( line . startsWith ( "## " ) ) {
104- if ( current ) sections . push ( current )
105- const title = line . slice ( 3 ) . trim ( )
106- current = { title, items : [ ] }
107- skip = false
108- } else if ( line . startsWith ( "**Thank you" ) ) {
109- skip = true
110- } else if ( line . startsWith ( "- " ) && ! skip ) {
111- current ?. items . push ( line . slice ( 2 ) . trim ( ) )
112- }
113- }
114- if ( current ) sections . push ( current )
38+ const response = await fetch ( url ) . catch ( ( ) => undefined )
39+ if ( ! response ?. ok ) return [ ]
11540
116- const highlights = parseHighlights ( body )
41+ const json = await response . json ( ) . catch ( ( ) => undefined )
42+ return Array . isArray ( json ?. releases ) ? ( json . releases as ChangelogRelease [ ] ) : [ ]
43+ }
11744
118- return { sections, highlights }
45+ function formatDate ( dateString : string ) {
46+ const date = new Date ( dateString )
47+ return date . toLocaleDateString ( "en-US" , {
48+ year : "numeric" ,
49+ month : "short" ,
50+ day : "numeric" ,
51+ } )
11952}
12053
12154function ReleaseItem ( props : { item : string } ) {
@@ -217,28 +150,27 @@ export default function Changelog() {
217150 < section data-component = "releases" >
218151 < For each = { releases ( ) } >
219152 { ( release ) => {
220- const parsed = ( ) => parseMarkdown ( release . body || "" )
221153 return (
222154 < article data-component = "release" >
223155 < header >
224156 < div data-slot = "version" >
225- < a href = { release . html_url } target = "_blank" rel = "noopener noreferrer" >
226- { release . tag_name }
157+ < a href = { release . url } target = "_blank" rel = "noopener noreferrer" >
158+ { release . tag }
227159 </ a >
228160 </ div >
229- < time dateTime = { release . published_at } > { formatDate ( release . published_at ) } </ time >
161+ < time dateTime = { release . date } > { formatDate ( release . date ) } </ time >
230162 </ header >
231163 < div data-slot = "content" >
232- < Show when = { parsed ( ) . highlights . length > 0 } >
164+ < Show when = { release . highlights . length > 0 } >
233165 < div data-component = "highlights" >
234- < For each = { parsed ( ) . highlights } > { ( group ) => < HighlightSection group = { group } /> } </ For >
166+ < For each = { release . highlights } > { ( group ) => < HighlightSection group = { group } /> } </ For >
235167 </ div >
236168 </ Show >
237- < Show when = { parsed ( ) . highlights . length > 0 && parsed ( ) . sections . length > 0 } >
238- < CollapsibleSections sections = { parsed ( ) . sections } />
169+ < Show when = { release . highlights . length > 0 && release . sections . length > 0 } >
170+ < CollapsibleSections sections = { release . sections } />
239171 </ Show >
240- < Show when = { parsed ( ) . highlights . length === 0 } >
241- < For each = { parsed ( ) . sections } >
172+ < Show when = { release . highlights . length === 0 } >
173+ < For each = { release . sections } >
242174 { ( section ) => (
243175 < div data-component = "section" >
244176 < h3 > { section . title } </ h3 >
0 commit comments