Freie Gestaltung statt Tokenlisten (Dokumentschema v10)
Der bisherige Editor bot drei Farben, keine Schriftwahl und elf Auswahllisten mit 44 festen Werten. ADR 008 hatte das mit Sicherheit begründet - der Schluss war falsch: die Alternative zu beliebigem CSS-Text ist nicht eine Auswahlliste, sondern ein typisiertes Designmodell. StyleSchema erklärt jede Eigenschaft einmal mit Typ, CSS-Entsprechung und Grenzen. Validator, CSS-Erzeugung und Editorpanel lesen dieselbe Definition, also kann der Editor nichts anbieten, was der Server ablehnt. Rund fünfzig Eigenschaften in sieben Gruppen, je Block, je Gerät und für den Hover-Zustand, mit Vererbung und einzelnem Zurücksetzen. Werte sind kurze Zeichenketten mit verankertem Muster, nie CSS-Text; zusammengesetzte Deklarationen baut der Compiler selbst. Der Server erzeugt daraus pro Seite ein blockgenaues Stylesheet, das über seinen eigenen SHA-256-Hash erlaubt wird statt über unsafe-inline. Theme unter /admin/theme: freie Farbpalette als @schlüssel, Schriften aus Systemschrift oder Google Fonts, Grundtypografie mit modularer Überschriftenskala. Google Fonts auf ausdrückliche Nutzerentscheidung; die Richtlinie öffnet die Hosts nur, wenn wirklich eine eingestellt ist. Alte Tokenwerte aus v2 bis v9 bleiben gültig und im Editor unter "Klassische Werte dieser Seite" bearbeitbar. Geprüft: 154 PHP-Tests / 563 Assertions unter PHP 8.2.12 und 8.4.22, 15 JavaScript-Modelltests, Lint/Format/Container. Im Browser wurde nur der Aufbau des Panels bestätigt, kein Gestaltungsvorgang von Hand ausgeführt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a40c2bb27e
commit
abf0bd2c6b
24 files changed
+1591
-21
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { EditorHistory, createBlock, locate, insertBlock, duplicateBlock, moveBlock, upgradeDocument, effectiveDesign, setDesign, insertPreset, blockPath, insertMedia, setEffect, isPluginBlock, blockLabel, blockIcon, canContain, isContainer } from '../public/assets/editor-model.js';
|
||||
import { EditorHistory, createBlock, locate, insertBlock, duplicateBlock, moveBlock, upgradeDocument, effectiveDesign, setDesign, insertPreset, blockPath, insertMedia, setEffect, isPluginBlock, blockLabel, blockIcon, canContain, isContainer, setStyle, effectiveStyle, hoverStyle, resetStyleDevice, clearStyle, copyStyle } from '../public/assets/editor-model.js';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { designCss } from '../scripts/build-design.mjs';
|
||||
|
||||
@@ -234,3 +234,37 @@ test('a new tab group starts with two tabs and accepts nothing but tabs', () =>
|
||||
history.undo();
|
||||
assert.deepEqual(history.document, fixture());
|
||||
});
|
||||
|
||||
test('free styles inherit from the wider device, reset individually and leave no skeleton', () => {
|
||||
const history = new EditorHistory(fixture());
|
||||
history.change(doc => setStyle(doc, 's1', 'desktop', 'fontSize', '42px'));
|
||||
history.change(doc => setStyle(doc, 's1', 'desktop', 'padding', '80px', { side: 'top' }));
|
||||
history.change(doc => setStyle(doc, 's1', 'desktop', 'color', '#ffffff', { hover: true }));
|
||||
history.change(doc => setStyle(doc, 's1', 'mobile', 'fontSize', '28px'));
|
||||
const block = () => locate(history.document.blocks, 's1').block;
|
||||
|
||||
assert.equal(history.document.schemaVersion, 10);
|
||||
// Tablet has no own value, so it inherits the desktop one; mobile keeps its own.
|
||||
assert.deepEqual(effectiveStyle(block(), 'tablet', 'fontSize'), { value: '42px', source: 'desktop' });
|
||||
assert.deepEqual(effectiveStyle(block(), 'mobile', 'fontSize'), { value: '28px', source: 'mobile' });
|
||||
assert.deepEqual(effectiveStyle(block(), 'desktop', 'padding', 'top'), { value: '80px', source: 'desktop' });
|
||||
assert.equal(hoverStyle(block(), 'desktop', 'color'), '#ffffff');
|
||||
|
||||
// Clearing one value makes it inherit again and removes the now empty container.
|
||||
history.change(doc => setStyle(doc, 's1', 'mobile', 'fontSize', ''));
|
||||
assert.equal(block().style.mobile, undefined);
|
||||
assert.deepEqual(effectiveStyle(block(), 'mobile', 'fontSize'), { value: '42px', source: 'desktop' });
|
||||
|
||||
// Copying carries the styling over without sharing objects.
|
||||
history.change(doc => copyStyle(doc, 's1', 's2'));
|
||||
const copy = locate(history.document.blocks, 's2').block;
|
||||
assert.deepEqual(copy.style, block().style);
|
||||
assert.notEqual(copy.style, block().style);
|
||||
|
||||
history.change(doc => clearStyle(doc, 's1'));
|
||||
assert.equal(block().style, undefined);
|
||||
|
||||
// Every step is one history entry, so undoing all of them restores the original document.
|
||||
for (let i = 0; i < 7; i++) history.undo();
|
||||
assert.deepEqual(history.document, fixture());
|
||||
});
|
||||
Reference in new issue
Block a user