diff --git a/continue.md b/continue.md index b8b85b120..0e011a1b4 100644 --- a/continue.md +++ b/continue.md @@ -38,10 +38,12 @@ Actualizacion Eidos ecosystem 2026-05-16: - Grupos semanticos de color (`surface`, `content`, `border`, `focus`) quedan cerrados con key sets canonicos y valores no vacios antes de generar tokens de theme. +- Overrides de `semantics.color.roles.*.slots` quedan validados: solo slots + canonicos y pasos `1..12`. - Validado: - - `npx vitest run src/uix/eidos/active-eidos-config.test.ts` -> 32 tests OK. + - `npx vitest run src/uix/eidos/active-eidos-config.test.ts` -> 33 tests OK. - `npx vitest run src/uix/eidos/active-eidos.test.ts` -> 19 tests OK. - - `npx vitest run src/uix/eidos` -> 7 archivos, 72 tests OK. + - `npx vitest run src/uix/eidos` -> 7 archivos, 73 tests OK. - `npm run check` -> 0 errores, 0 warnings. Actualizacion permutation runner 2026-05-16: diff --git a/src/uix/eidos/active-eidos-config.test.ts b/src/uix/eidos/active-eidos-config.test.ts index 7d36ca609..fd5e5d07f 100644 --- a/src/uix/eidos/active-eidos-config.test.ts +++ b/src/uix/eidos/active-eidos-config.test.ts @@ -581,6 +581,28 @@ describe('ActiveEidos config', () => { ) }) + it('validates semantic color role slot overrides', () => { + const cfg = structuredClone(THEME_BASE_OPTIONS) + + ;(cfg.semantics.color.roles as unknown as Record).primary = { + scale: 'purple', + slots: { + solid: '13', + madeUp: '9' + } + } + + const report = createEidos(cfg).validate() + + expect(report.ok).toBe(false) + expect( + report.issues.some((issue) => issue.path === 'semantics.color.roles.primary.slots.solid') + ).toBe(true) + expect( + report.issues.some((issue) => issue.path === 'semantics.color.roles.primary.slots.madeUp') + ).toBe(true) + }) + it('validates CSS-facing theme and color scale keys', () => { const cfg = structuredClone(THEME_BASE_OPTIONS) diff --git a/src/uix/eidos/lib/config.ts b/src/uix/eidos/lib/config.ts index 4575d18b7..759445f08 100644 --- a/src/uix/eidos/lib/config.ts +++ b/src/uix/eidos/lib/config.ts @@ -4,6 +4,7 @@ import { BORDER_STYLE_KEYS, BORDER_WIDTH_KEYS, COLOR_ROLES, + COLOR_ROLE_SLOTS, COLOR_SCALE_STEPS, CONTAINER_WIDTH_KEYS, CONTENT_COLOR_KEYS, @@ -55,6 +56,8 @@ const BORDER_WIDTH_KEY_SET = new Set(BORDER_WIDTH_KEYS) const BORDER_STYLE_KEY_SET = new Set(BORDER_STYLE_KEYS) const BORDER_COLOR_KEY_SET = new Set(BORDER_COLOR_KEYS) const CONTENT_COLOR_KEY_SET = new Set(CONTENT_COLOR_KEYS) +const COLOR_ROLE_SLOT_SET = new Set(COLOR_ROLE_SLOTS) +const COLOR_SCALE_STEP_SET = new Set(COLOR_SCALE_STEPS) const CONTAINER_WIDTH_KEY_SET = new Set(CONTAINER_WIDTH_KEYS) const CONTENT_WIDTH_KEY_SET = new Set(CONTENT_WIDTH_KEYS) const DENSITY_KEY_SET = new Set(DENSITY_KEYS) @@ -454,6 +457,14 @@ function validateColorRole( return } + if (roleDef !== undefined && typeof roleDef !== 'string' && !isPlainRecord(roleDef)) { + issues.push({ + path, + message: 'color role definition must be a scale name or plain object' + }) + return + } + const scaleName = resolveRoleScaleName(roleDef) if (!scaleName) { issues.push({ @@ -463,6 +474,10 @@ function validateColorRole( return } + if (typeof roleDef !== 'string' && roleDef?.slots !== undefined) { + validateColorRoleSlots(`${path}.slots`, roleDef.slots, issues) + } + const themeEntries = Object.entries(themes) if (themeEntries.length === 0) { if (!hasOwn(globalScales, scaleName)) { @@ -484,6 +499,36 @@ function validateColorRole( } } +function validateColorRoleSlots( + path: string, + slots: unknown, + issues: EidosValidationIssue[] +): void { + if (!isPlainRecord(slots)) { + issues.push({ + path, + message: 'color role slots must be a plain object' + }) + return + } + + for (const [slot, step] of Object.entries(slots)) { + if (!COLOR_ROLE_SLOT_SET.has(slot)) { + issues.push({ + path: `${path}.${slot}`, + message: 'unknown color role slot' + }) + continue + } + if (typeof step !== 'string' || !COLOR_SCALE_STEP_SET.has(step)) { + issues.push({ + path: `${path}.${slot}`, + message: 'color role slot must point to a scale step from 1 to 12' + }) + } + } +} + function validateTypography(options: EidosConfig, issues: EidosValidationIssue[]): void { const typography = options.primitives.typography if (!typography) return