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

@@ -116,12 +116,19 @@ export function BlockFields({
{block.type === 'photo' && (
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
<div
{/* A button, not a clickable div, so the keyboard reaches it. */}
<button
type="button"
aria-label={block.assetId ? 'Change photo' : 'Pick a photo'}
style={{
width: '140px',
height: '96px',
flexShrink: 0,
background: 'rgba(0,0,0,0.3)',
border: 'none',
padding: 0,
color: 'inherit',
font: 'inherit',
borderRadius: '6px',
overflow: 'hidden',
display: 'flex',
@@ -140,13 +147,13 @@ export function BlockFields({
// eslint-disable-next-line @next/next/no-img-element
<img
src={`/api/admin/thumbnail/${block.assetId}`}
alt="Thumb"
alt=""
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
/>
) : (
<span style={{ fontSize: '0.8rem', opacity: 0.6 }}>+ Pick</span>
)}
</div>
</button>
<div
style={{
@@ -213,10 +220,16 @@ export function BlockFields({
}}
>
{[0, 1].map((pIdx) => (
<div
<button
type="button"
key={pIdx}
aria-label={`${block.assetIds[pIdx] ? 'Change' : 'Pick'} photo ${pIdx + 1} of the pair`}
style={{
height: '90px',
width: '100%',
padding: 0,
color: 'inherit',
font: 'inherit',
background: 'rgba(0,0,0,0.3)',
borderRadius: '6px',
overflow: 'hidden',
@@ -241,13 +254,13 @@ export function BlockFields({
// eslint-disable-next-line @next/next/no-img-element
<img
src={`/api/admin/thumbnail/${block.assetIds[pIdx]}`}
alt="Thumb"
alt=""
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
/>
) : (
<span style={{ fontSize: '0.8rem', opacity: 0.6 }}>+ Pick Photo #{pIdx + 1}</span>
)}
</div>
</button>
))}
</div>
<div>

View File

@@ -401,25 +401,26 @@ export function JournalEditor({ slug, mapEnabled, onBack, kind = 'journal' }: Jo
onChange={(e) => handleFrontmatterChange({ title: e.target.value })}
/>
{/* The admin's segmented control (QA A-18). As `admin-btn-xs` buttons the
active one never showed: `.admin-btn-xs` sets its own background
after `.admin-btn-primary`, so both modes looked identical. */}
<div
style={{
display: 'flex',
gap: '4px',
background: 'rgba(255,255,255,0.06)',
borderRadius: '6px',
padding: '2px',
}}
className="segmented-control segmented-control--sm"
role="group"
aria-label="Editor mode"
>
<button
type="button"
className={`admin-btn admin-btn-xs ${editorMode === 'blocks' ? 'admin-btn-primary' : ''}`}
className={`segment-btn ${editorMode === 'blocks' ? 'active' : ''}`}
aria-pressed={editorMode === 'blocks'}
onClick={() => setEditorMode('blocks')}
>
Visual Blocks
</button>
<button
type="button"
className={`admin-btn admin-btn-xs ${editorMode === 'markdown' ? 'admin-btn-primary' : ''}`}
className={`segment-btn ${editorMode === 'markdown' ? 'active' : ''}`}
aria-pressed={editorMode === 'markdown'}
onClick={() => setEditorMode('markdown')}
>
Raw Markdown

View File

@@ -92,17 +92,24 @@ export function JournalPreview({ parsed, albumAssets }: JournalPreviewProps) {
<span className="journal-preview-bar-label">
<IconEye size={13} /> Realtime Theme Preview
</span>
<div style={{ display: 'flex', gap: '4px' }}>
{/* Same control and same fix as the editor's mode toggle. */}
<div
className="segmented-control segmented-control--sm"
role="group"
aria-label="Preview width"
>
<button
type="button"
className={`admin-btn admin-btn-xs ${viewport === 'desktop' ? 'admin-btn-primary' : ''}`}
className={`segment-btn ${viewport === 'desktop' ? 'active' : ''}`}
aria-pressed={viewport === 'desktop'}
onClick={() => setViewport('desktop')}
>
Desktop
</button>
<button
type="button"
className={`admin-btn admin-btn-xs ${viewport === 'mobile' ? 'admin-btn-primary' : ''}`}
className={`segment-btn ${viewport === 'mobile' ? 'active' : ''}`}
aria-pressed={viewport === 'mobile'}
onClick={() => setViewport('mobile')}
>
Mobile

View File

@@ -110,11 +110,12 @@ export function JournalStudio({ slug: activeSlug, mapEnabled }: JournalStudioPro
}
};
const handleDelete = async (slug: string) => {
const handleDelete = async (slug: string, title?: string) => {
// Named by title, as the page delete is; the slug only when there is none.
const ok = await confirm({
title: `Delete “${slug}”?`,
title: `Delete “${title || slug}”?`,
message: 'A copy is kept and can be restored under Backups.',
confirmLabel: 'Delete',
confirmLabel: 'Delete entry',
danger: true,
});
if (!ok) return;
@@ -244,7 +245,7 @@ export function JournalStudio({ slug: activeSlug, mapEnabled }: JournalStudioPro
<button
type="button"
className="admin-btn admin-btn-sm admin-btn-danger"
onClick={() => handleDelete(entry.slug)}
onClick={() => handleDelete(entry.slug, entry.frontmatter.title)}
title="Delete entry"
style={{ marginLeft: 'auto' }}
>

View File

@@ -1,8 +1,9 @@
/* ── Journal Studio & Editor CSS ──────────────────────────────────── */
/*
* The studio is a full-width workspace: it breaks out of the 1120px
* `.admin-main` column so the split view has room for editor *and* preview.
* The studio is a full-width workspace: it breaks out of the shared
* `--admin-content-width` column so the split view has room for editor *and*
* preview.
*/
.admin-main:has(> .journal-studio),
.admin-main:has(> .journal-editor-container) {