@@ -15,7 +15,7 @@ function stripFrontmatter(md: string): string {
1515const aboutHtml = DOMPurify .sanitize (marked (stripFrontmatter (aboutRaw )) as string )
1616const contactHtml = DOMPurify .sanitize (marked (stripFrontmatter (contactRaw )) as string )
1717
18- const tabOptions = [' About' , ' Contact' , ' SlideRule Releases ' , ' Web Client Releases ' ]
18+ const tabOptions = [' About' , ' Contact' , ' Release Notes ' ]
1919const selectedTab = ref (' About' )
2020
2121const panelHtml = computed (() => {
@@ -30,11 +30,6 @@ const panelHtml = computed(() => {
3030})
3131
3232// --- Release Notes ---
33- //
34- // Two release-note sources share the list/detail UI below:
35- // - 'remote' = the SlideRule platform notes scraped from the docs site
36- // - 'local' = this repo's web-client notes, bundled at build time (see the
37- // import.meta.glob of src/assets/content/release-notes/*.md)
3833
3934const RELEASE_NOTES_INDEX_URL = DOCS .releaseNotes .index
4035const RELEASE_NOTES_BASE_URL = DOCS .releaseNotes .base
@@ -53,11 +48,7 @@ const releaseHtml = ref('')
5348const releaseLoading = ref (false )
5449const releaseError = ref (' ' )
5550
56- const releaseMode = computed <' remote' | ' local' | null >(() => {
57- if (selectedTab .value === ' SlideRule Releases' ) return ' remote'
58- if (selectedTab .value === ' Web Client Releases' ) return ' local'
59- return null
60- })
51+ const showReleaseNotes = computed (() => selectedTab .value === ' Release Notes' )
6152
6253// --- Local web-client release notes (bundled markdown) ---
6354
@@ -67,24 +58,6 @@ const localNoteModules = import.meta.glob('@/assets/content/release-notes/*.md',
6758 eager: true
6859}) as Record <string , string >
6960
70- function versionKey(v : string ): number [] {
71- return v
72- .replace (/ ^ v/ , ' ' )
73- .split (' .' )
74- .map ((n ) => parseInt (n , 10 ) || 0 )
75- }
76-
77- // Sort newest-first by semantic version (v4.5.10 after v4.5.2).
78- function compareVersionDesc(a : string , b : string ): number {
79- const av = versionKey (a )
80- const bv = versionKey (b )
81- for (let i = 0 ; i < Math .max (av .length , bv .length ); i ++ ) {
82- const diff = (bv [i ] ?? 0 ) - (av [i ] ?? 0 )
83- if (diff !== 0 ) return diff
84- }
85- return 0
86- }
87-
8861const localReleaseNotes = computed <ReleaseNote []>(() => {
8962 const notes = Object .entries (localNoteModules ).map (([path , raw ]) => {
9063 const version = path .split (' /' ).pop ()?.replace (/ \. md$ / , ' ' ) ?? ' '
@@ -97,33 +70,38 @@ const localReleaseNotes = computed<ReleaseNote[]>(() => {
9770 const bullet = body .match (/ ^ \s * [-*] \s + (. + )$ / m )
9871 const snippet = bullet ?.[1 ] ?? ' '
9972 const html = DOMPurify .sanitize (marked (body ) as string )
100- return { title , date , url: DOCS .webClient .tags + version , snippet , html }
73+ return {
74+ title: ' Web Client - ' + title ,
75+ date ,
76+ url: DOCS .webClient .tags + version ,
77+ snippet ,
78+ html
79+ }
10180 })
102- return notes .sort ((a , b ) => compareVersionDesc ( a . title , b . title ) || b .date .localeCompare (a .date ))
81+ return notes .sort ((a , b ) => b .date .localeCompare (a .date ))
10382})
10483
10584const currentNotes = computed <ReleaseNote []>(() => {
106- if (releaseMode .value === ' local ' ) return localReleaseNotes . value
107- if ( releaseMode . value === ' remote ' ) return releaseNotes .value
108- return []
85+ if (! showReleaseNotes .value ) return []
86+ const combined = [ ... releaseNotes .value , ... localReleaseNotes . value ]
87+ return combined . sort (( a , b ) => b . date . localeCompare ( a . date ))
10988})
11089
11190const externalLink = computed (() => {
11291 if (! selectedRelease .value ) return ' '
113- return releaseMode .value === ' local '
92+ return selectedRelease .value . html
11493 ? selectedRelease .value .url
11594 : RELEASE_NOTES_BASE_URL + selectedRelease .value .url
11695})
11796
11897const externalLinkLabel = computed (() =>
119- releaseMode .value === ' local ' ? ' View on GitHub ↗' : ' View on docs site ↗'
98+ selectedRelease .value ?. html ? ' View on GitHub ↗' : ' View on docs site ↗'
12099)
121100
122101function openRelease(note : ReleaseNote ) {
123- if (releaseMode .value === ' local' ) {
124- // Local notes are already rendered — no network fetch needed.
102+ if (note .html ) {
125103 selectedRelease .value = note
126- releaseHtml .value = note .html ?? ' '
104+ releaseHtml .value = note .html
127105 } else {
128106 void fetchRelease (note )
129107 }
@@ -146,7 +124,7 @@ async function fetchReleaseNotesIndex() {
146124 // Matches "Release v5.4.x", "Release v5.4.1", "Web Client Release v4.5.0", etc.
147125 const match = text .match (/ ^ (?:Web Client )? Release v[\d. x] + $ / )
148126 if (match ) {
149- notes .push ({ date: ' ' , title: text , url: href })
127+ notes .push ({ date: ' ' , title: ' Server - ' + text , url: href })
150128 }
151129 })
152130 releaseNotes .value = notes
@@ -237,12 +215,10 @@ onMounted(() => {
237215})
238216
239217watch (selectedTab , (tab ) => {
240- // Reset the detail view whenever the tab changes; only the remote source needs
241- // an on-switch fetch (local notes are bundled and rendered eagerly).
242218 selectedRelease .value = null
243219 releaseHtml .value = ' '
244220 releaseError .value = ' '
245- if (tab === ' SlideRule Releases ' ) {
221+ if (tab === ' Release Notes ' ) {
246222 void fetchReleaseNotesIndex ()
247223 }
248224})
@@ -261,7 +237,7 @@ watch(selectedTab, (tab) => {
261237 </div >
262238 </div >
263239 <!-- About / Contact -->
264- <div v-if =" releaseMode === null " ref =" panelRef" class =" sr-landing-panel" >
240+ <div v-if =" !showReleaseNotes " ref =" panelRef" class =" sr-landing-panel" >
265241 <div v-if =" panelHtml" class =" sr-landing-panel-content" >
266242 <div v-html =" panelHtml" />
267243 </div >
@@ -270,7 +246,7 @@ watch(selectedTab, (tab) => {
270246 </div >
271247 </div >
272248
273- <!-- Release Notes (SlideRule platform = remote, Web Client = local ) -->
249+ <!-- Release Notes (combined: server + web client ) -->
274250 <div v-else class =" sr-landing-panel" >
275251 <div v-if =" releaseLoading" class =" sr-news-status" >Loading...</div >
276252 <div v-else-if =" releaseError" class =" sr-news-status sr-news-error" >{{ releaseError }}</div >
0 commit comments