chore: release v1.1.2
Add a Changelog page that renders CHANGELOG.md from the header menu, switch the default color theme to the brand-accurate Stalwart theme, and document all changes since v1.1.1.
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import * as LucideIcons from 'lucide-react';
|
||||
const { User, LogOut, Check, Menu, Sparkles, Search, Palette } = LucideIcons;
|
||||
const { User, LogOut, Check, Menu, Sparkles, Search, Palette, ScrollText } = LucideIcons;
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { CommandPalette } from '@/components/common/CommandPalette';
|
||||
import {
|
||||
@@ -203,6 +203,11 @@ export function TopBar() {
|
||||
{t('appearance.label', 'Appearance')}
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem onClick={() => navigate('/Changelog')}>
|
||||
<ScrollText className="mr-2 h-4 w-4" />
|
||||
{t('changelog.label', 'Changelog')}
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem
|
||||
variant="destructive"
|
||||
onClick={() => {
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
import { ExternalLink } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import type { Components } from 'react-markdown';
|
||||
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import changelogSource from '../../../CHANGELOG.md?raw';
|
||||
|
||||
// Renders this fork's actual CHANGELOG.md — the single source of truth for
|
||||
// release notes, kept up to date after every release — instead of a
|
||||
// separately maintained copy that could drift from it.
|
||||
const components: Components = {
|
||||
h1: () => null,
|
||||
h2: ({ children }) => (
|
||||
<h2 className="mt-10 border-b pb-2 text-lg font-semibold tracking-tight first:mt-0">{children}</h2>
|
||||
),
|
||||
h3: ({ children }) => (
|
||||
<h3 className="mt-5 mb-2 text-xs font-semibold tracking-wide text-muted-foreground uppercase">{children}</h3>
|
||||
),
|
||||
p: ({ children }) => <p className="text-sm text-muted-foreground">{children}</p>,
|
||||
ul: ({ children }) => <ul className="mt-1 list-disc space-y-1.5 pl-5 text-sm">{children}</ul>,
|
||||
li: ({ children }) => <li className="leading-relaxed">{children}</li>,
|
||||
a: ({ children, href }) => (
|
||||
<a
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-primary underline underline-offset-2 hover:no-underline"
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
code: ({ children }) => <code className="rounded bg-muted px-1 py-0.5 font-mono text-xs">{children}</code>,
|
||||
};
|
||||
|
||||
export function ChangelogPage() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-3xl space-y-6">
|
||||
<Card>
|
||||
<CardContent className="flex items-start gap-3 p-4 sm:p-6">
|
||||
<ExternalLink className="mt-0.5 h-4 w-4 shrink-0 text-primary" aria-hidden />
|
||||
<p className="text-sm leading-relaxed">
|
||||
{t(
|
||||
'changelog.officialNotice',
|
||||
'The official WebUI is maintained by the Stalwart team and is available at',
|
||||
)}{' '}
|
||||
<a
|
||||
href="https://github.com/stalwartlabs/webui"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="font-medium text-primary underline underline-offset-2 hover:no-underline"
|
||||
>
|
||||
https://github.com/stalwartlabs/webui
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className="space-y-1">
|
||||
<h1 className="text-2xl font-semibold tracking-tight">{t('changelog.label', 'Changelog')}</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('changelog.description', "What's new in this fork, release by release.")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ReactMarkdown components={components}>{changelogSource}</ReactMarkdown>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -56,6 +56,11 @@
|
||||
"welcome": "Welcome to Stalwart",
|
||||
"welcomeSubtitle": "Let's get your server set up."
|
||||
},
|
||||
"changelog": {
|
||||
"description": "What's new in this fork, release by release.",
|
||||
"label": "Changelog",
|
||||
"officialNotice": "The official WebUI is maintained by the Stalwart team and is available at"
|
||||
},
|
||||
"common": {
|
||||
"apply": "Apply",
|
||||
"back": "Back",
|
||||
|
||||
+5
-2
@@ -24,8 +24,11 @@ import { getBasePath } from './lib/basePath';
|
||||
const theme = state?.theme;
|
||||
const dark = theme === 'dark' || (theme !== 'light' && !!window.matchMedia?.('(prefers-color-scheme: dark)').matches);
|
||||
document.documentElement.classList.toggle('dark', dark);
|
||||
const colorTheme = state?.colorTheme;
|
||||
// Match uiStore defaults (stalwart + square) so the first paint is correct
|
||||
// before zustand rehydrates — including brand-new sessions with no persisted state.
|
||||
const colorTheme = state?.colorTheme ?? 'stalwart';
|
||||
if (
|
||||
colorTheme === 'stalwart' ||
|
||||
colorTheme === 'ocean' ||
|
||||
colorTheme === 'forest' ||
|
||||
colorTheme === 'violet' ||
|
||||
@@ -35,7 +38,7 @@ import { getBasePath } from './lib/basePath';
|
||||
) {
|
||||
document.documentElement.dataset.theme = colorTheme;
|
||||
}
|
||||
if (state?.radius === 'square') {
|
||||
if ((state?.radius ?? 'square') === 'square') {
|
||||
document.documentElement.dataset.radius = 'square';
|
||||
}
|
||||
// eslint-disable-next-line no-empty
|
||||
|
||||
@@ -27,6 +27,7 @@ import { LoadingFallback } from '@/components/common/LoadingFallback';
|
||||
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
||||
import { friendlyName } from '@/hooks/useGlobalSearch';
|
||||
import { AppearancePage } from '@/features/appearance/AppearancePage';
|
||||
import { ChangelogPage } from '@/features/changelog/ChangelogPage';
|
||||
import {
|
||||
findFirstAccessibleLinkInLayout,
|
||||
findFirstVisibleLinkInLayout,
|
||||
@@ -101,6 +102,7 @@ export default function AdminPanel() {
|
||||
// what the user sees in the navigation, e.g. "MTA-STS · Settings".
|
||||
const pageTitle = useMemo(() => {
|
||||
if (section === 'Appearance') return t('appearance.label', 'Appearance');
|
||||
if (section === 'Changelog') return t('changelog.label', 'Changelog');
|
||||
if (!section) return t('dashboard.title', 'Dashboard');
|
||||
if (!viewName) return section;
|
||||
const entry =
|
||||
@@ -220,9 +222,9 @@ export default function AdminPanel() {
|
||||
}
|
||||
}
|
||||
|
||||
if (section === 'Appearance') {
|
||||
// Appearance is a client-side page outside the schema layouts: keep the
|
||||
// sidebar bound to a real section while it is open.
|
||||
if (section === 'Appearance' || section === 'Changelog') {
|
||||
// Appearance and Changelog are client-side pages outside the schema
|
||||
// layouts: keep the sidebar bound to a real section while open.
|
||||
if (!layouts.some((l) => l.name === activeSection)) {
|
||||
setActiveSection(layouts[0]?.name ?? '');
|
||||
}
|
||||
@@ -337,6 +339,8 @@ export default function AdminPanel() {
|
||||
<ErrorBoundary key={activeAccountId ?? 'none'}>
|
||||
{section === 'Appearance' ? (
|
||||
<AppearancePage />
|
||||
) : section === 'Changelog' ? (
|
||||
<ChangelogPage />
|
||||
) : (
|
||||
<MainContent viewName={viewName} id={id} section={section} />
|
||||
)}
|
||||
|
||||
@@ -73,7 +73,7 @@ export const useUIStore = create<UIState>()(
|
||||
(set, get) => ({
|
||||
theme:
|
||||
typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light',
|
||||
colorTheme: 'ocean',
|
||||
colorTheme: 'stalwart',
|
||||
radius: 'square',
|
||||
sidebarOpen: typeof window !== 'undefined' ? (window.matchMedia?.('(min-width: 768px)').matches ?? true) : true,
|
||||
activeSection: '',
|
||||
@@ -116,7 +116,7 @@ export const useUIStore = create<UIState>()(
|
||||
return (state) => {
|
||||
if (state) {
|
||||
applyThemeClass(state.theme);
|
||||
applyColorThemeAttribute(state.colorTheme ?? 'ocean');
|
||||
applyColorThemeAttribute(state.colorTheme ?? 'stalwart');
|
||||
applyRadiusAttribute(state.radius ?? 'square');
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user