release: v0.20.0

This commit is contained in:
Ralksta
2026-10-02 18:14:23 +02:00
committed by GitHub
parent 81ce886b01
commit 5f831f719a
209 changed files with 13807 additions and 803 deletions

View File

@@ -30,6 +30,7 @@ import SeoSection from './settings/SeoSection';
import SecuritySection from './settings/SecuritySection';
import AboutSection, { useAboutEditor } from './settings/AboutSection';
import PageHeader from './PageHeader';
import { sameDraft } from './sameDraft';
const SETTINGS_SECTIONS = [
{ id: 'general', label: 'General' },
@@ -71,6 +72,8 @@ export default function SettingsEditor() {
} | null>(null);
/** Fields an environment variable overrides, path to variable name (#605). */
const [envLocks, setEnvLocks] = useState<EnvLocks>({});
/** SITE_TITLE, the first fallback for an empty title (QA A-15). */
const [siteTitleEnv, setSiteTitleEnv] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
/**
* Set when the settings could not be fetched. It blocks saving, because an
@@ -79,7 +82,8 @@ export default function SettingsEditor() {
*/
const [loadError, setLoadError] = useState<string | null>(null);
const [saving, setSaving] = useState(false);
const [dirty, setDirty] = useState(false);
/** Set by any edit; `dirty` below also asks whether the edits changed anything. */
const [edited, setDirty] = useState(false);
const [saveStatus, setSaveStatus] = useState<SaveStatus>(null);
const about = useAboutEditor(activeSection === 'about');
@@ -88,6 +92,8 @@ export default function SettingsEditor() {
// builder's and the journal's do (#592) — useUnsavedGuard only covers
// leaving the browser.
const [serverSettings, setServerSettings] = useState<Settings>({});
// Typing a value back to what was saved is not an unsaved change (QA A-19).
const dirty = edited && !sameDraft(settings, serverSettings);
// The draft never sees `sitePassword`: sessionStorage is readable by any
// script on the admin origin, and until it is saved (and hashed) the field
// holds the password as typed. A restored draft takes the password from the
@@ -160,7 +166,13 @@ export default function SettingsEditor() {
: `The server answered ${res.status}.`,
);
}
const { settings: data, siteUrl, envLocks: locks, version } = await res.json();
const {
settings: data,
siteUrl,
envLocks: locks,
version,
siteTitleEnv: titleEnv,
} = await res.json();
const loaded: Settings = data || {};
versionRef.current = typeof version === 'string' ? version : null;
setServerSettings(loaded);
@@ -170,6 +182,7 @@ export default function SettingsEditor() {
setDirty(restored !== null);
setSiteUrlInfo(siteUrl ?? null);
setEnvLocks(locks ?? {});
setSiteTitleEnv(typeof titleEnv === 'string' ? titleEnv : null);
} catch (err) {
console.error('Failed to load settings:', err);
setLoadError(err instanceof Error ? err.message : 'The settings could not be loaded.');
@@ -407,7 +420,7 @@ export default function SettingsEditor() {
{/* Content */}
<div className="settings-content">
{activeSection === 'general' && <GeneralSection {...props} />}
{activeSection === 'general' && <GeneralSection {...props} siteTitleEnv={siteTitleEnv} />}
{activeSection === 'theme' && <ThemeSection {...props} />}
{activeSection === 'grid' && <GridSection {...props} />}
{activeSection === 'footer' && <FooterSection {...props} />}