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>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
# 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']);
|
||||
+ });
|
||||
+});
|
||||
Reference in New Issue
Block a user