Files
InsanityLab-web/.superpowers/sdd/review-5f1d9f3..6f51aec.diff
Adriano b913a2bf90 registri dei piani in git (AI-OS#309)
Refs gitea:Adriano/AI-OS#310

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 10:36:26 +02:00

114 lines
4.4 KiB
Diff

# Review package: 5f1d9f3..6f51aec
## Commits
6f51aec feat: preset stili contenuto con whitelist
## Files changed
src/lib/style-presets.ts | 31 +++++++++++++++++++++++++++++++
src/styles/global.css | 19 +++++++++++++++++++
tests/style-presets.test.ts | 24 ++++++++++++++++++++++++
3 files changed, 74 insertions(+)
## Diff
diff --git a/src/lib/style-presets.ts b/src/lib/style-presets.ts
new file mode 100644
index 0000000..821ad38
--- /dev/null
+++ b/src/lib/style-presets.ts
@@ -0,0 +1,31 @@
+export const STYLE_GROUPS = {
+ size: ['s', 'm', 'l'],
+ color: ['accent', 'accent-dark', 'dark', 'heading', 'text', 'text-light', 'white'],
+ weight: ['normal', 'bold'],
+ style: ['normal', 'italic'],
+ align: ['left', 'center', 'right'],
+} as const;
+
+export type StyleGroup = keyof typeof STYLE_GROUPS;
+
+export function validateStyles(input: unknown):
+ | { ok: true; styles: Record<string, string> }
+ | { ok: false; error: string } {
+ if (typeof input !== 'object' || input === null || Array.isArray(input)) {
+ return { ok: false, error: 'Stili non validi.' };
+ }
+ const out: Record<string, string> = {};
+ for (const [k, v] of Object.entries(input)) {
+ const allowed = (STYLE_GROUPS as Record<string, readonly string[]>)[k];
+ if (!allowed) return { ok: false, error: `Gruppo stile sconosciuto: ${k}` };
+ if (typeof v !== 'string' || !allowed.includes(v)) {
+ return { ok: false, error: `Valore non ammesso per ${k}: ${String(v)}` };
+ }
+ out[k] = v;
+ }
+ return { ok: true, styles: out };
+}
+
+export function stylesToClasses(styles: Record<string, string>): string {
+ return Object.entries(styles).map(([k, v]) => `cs-${k}-${v}`).join(' ');
+}
diff --git a/src/styles/global.css b/src/styles/global.css
index 09dd32e..2b49c61 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -61,10 +61,29 @@ html.js [data-reveal="right"] { transform: translateX(60px); }
html.js [data-reveal="fade"] { transform: none; }
html.js [data-reveal].is-inview, html.js .section-heading.is-inview { opacity: 1; transform: none; }
.btn::after { transition: transform .3s ease; }
.btn:hover::after { transform: translateX(5px); }
@media (prefers-reduced-motion: reduce) {
html.js [data-reveal], html.js .section-heading { opacity: 1; transform: none; transition: none; }
*, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}
+
+/* === Stili contenuto (preset pannello /admin/content) === */
+.cs-size-s { font-size: .85em; }
+.cs-size-m { font-size: 1em; }
+.cs-size-l { font-size: 1.25em; }
+.cs-color-accent { color: var(--c-accent); }
+.cs-color-accent-dark { color: var(--c-accent-dark); }
+.cs-color-dark { color: var(--c-dark); }
+.cs-color-heading { color: var(--c-heading); }
+.cs-color-text { color: var(--c-text); }
+.cs-color-text-light { color: var(--c-text-light); }
+.cs-color-white { color: #fff; }
+.cs-weight-normal { font-weight: 400; }
+.cs-weight-bold { font-weight: 700; }
+.cs-style-normal { font-style: normal; }
+.cs-style-italic { font-style: italic; }
+.cs-align-left { text-align: left; }
+.cs-align-center { text-align: center; }
+.cs-align-right { text-align: right; }
diff --git a/tests/style-presets.test.ts b/tests/style-presets.test.ts
new file mode 100644
index 0000000..32b324f
--- /dev/null
+++ b/tests/style-presets.test.ts
@@ -0,0 +1,24 @@
+import { describe, it, expect } from 'vitest';
+import { validateStyles, stylesToClasses, STYLE_GROUPS } from '../src/lib/style-presets';
+
+describe('style presets', () => {
+ it('accetta chiavi e valori in whitelist', () => {
+ const r = validateStyles({ size: 'l', color: 'accent', align: 'center' });
+ expect(r).toEqual({ ok: true, styles: { size: 'l', color: 'accent', align: 'center' } });
+ });
+
+ it('rifiuta gruppo sconosciuto e valore fuori lista', () => {
+ expect(validateStyles({ font: 'comic' }).ok).toBe(false);
+ expect(validateStyles({ size: 'xxl' }).ok).toBe(false);
+ expect(validateStyles('non-oggetto').ok).toBe(false);
+ });
+
+ it('mappa stili in classi cs-*', () => {
+ expect(stylesToClasses({ size: 's', weight: 'bold' })).toBe('cs-size-s cs-weight-bold');
+ expect(stylesToClasses({})).toBe('');
+ });
+
+ it('espone i gruppi attesi', () => {
+ expect(Object.keys(STYLE_GROUPS)).toEqual(['size', 'color', 'weight', 'style', 'align']);
+ });
+});