mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-26 13:45:16 -07:00
- Rename Server tab to Settings with horizontal sub-tab navigation (General, Generation, GPU, Logs, Changelog) - All sub-tabs are proper routes under /settings/* with /server redirect for backwards compat - General: connection settings, link cards (docs + discord), API reference card, app updates - Generation: auto-chunking, crossfade, normalize, autoplay as SettingRow components - GPU: info card with platform-aware icons (Apple logo for MPS), CUDA management, explainer text - Logs: real-time server log viewer piped from Tauri sidecar via event system (Tauri-only) - Changelog: parsed from CHANGELOG.md at build time via Vite virtual module plugin - New reusable SettingRow/SettingSection components for consistent settings layout - New Toggle (switch) UI component replacing checkboxes in settings - Toast viewport now offsets when audio player is open - Sidebar stays active on settings sub-routes (fuzzy matching)
38 lines
1.2 KiB
TypeScript
38 lines
1.2 KiB
TypeScript
export interface ChangelogEntry {
|
|
version: string;
|
|
date: string | null;
|
|
body: string;
|
|
}
|
|
|
|
/**
|
|
* Parses a Keep-a-Changelog style markdown string into structured entries.
|
|
*
|
|
* Splits on `## [version]` headings and extracts the version + date from each.
|
|
* The body is the raw markdown between headings (trimmed), with the leading
|
|
* `# Changelog` title and trailing link references stripped.
|
|
*/
|
|
export function parseChangelog(raw: string): ChangelogEntry[] {
|
|
const entries: ChangelogEntry[] = [];
|
|
|
|
// Strip trailing link reference definitions (e.g. [0.1.0]: https://...)
|
|
const cleaned = raw.replace(/^\[[\w.]+\]:.*$/gm, '').trimEnd();
|
|
|
|
// Match `## [version]` or `## [version] - date`
|
|
const headingRe = /^## \[(.+?)\](?:\s*-\s*(.+))?$/gm;
|
|
const matches = [...cleaned.matchAll(headingRe)];
|
|
|
|
for (let i = 0; i < matches.length; i++) {
|
|
const match = matches[i];
|
|
const version = match[1];
|
|
const date = match[2]?.trim() || null;
|
|
|
|
const start = match.index! + match[0].length;
|
|
const end = i + 1 < matches.length ? matches[i + 1].index! : cleaned.length;
|
|
const body = cleaned.slice(start, end).trim();
|
|
|
|
entries.push({ version, date, body });
|
|
}
|
|
|
|
return entries;
|
|
}
|