You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/eidos/components/palabras/ESQUEMA.md

20 KiB

palabras — ESQUEMA del documento (spec para ratificación)

2026-07-14. El esquema es el contrato completo del documento: lo que la app le pasa a <Palabras> para gobernar CÓMO SE VE el documento y QUÉ SE PUEDE EDITAR en él — independiente del tema de la página anfitriona (el editor puede vivir en un backoffice cuyo look no tiene nada que ver con el documento que se redacta). Estado: RATIFICADA por el usuario (2026-07-14, «empieza») — este documento es el contrato que la implementación ejecuta. Las propuestas de las puertas ⚠️ del §12 quedan aceptadas tal como están escritas (temperature entra al modelo del motor; field-types custom → v2; ambos niveles de extensión; @nombre sí). Lo implementado antes de la firma (30a67533) era la base parcial del §11.


1. Las seis piezas del esquema

Pieza Qué declara A dónde proyecta
fonts Las fuentes ADMITIDAS en el documento (grupos con label; pesos disponibles por fuente) Dropdown Familia del panel (y validación del valor fontFamily de los overrides)
sizes La escala tipográfica: tamaño por elemento (text, h1…h6, quote, code, image.caption…) Var CSS por elemento + el valor "heredado" que muestra el panel
palette Los colores predefinidos del documento, CON NOMBRE (referenciables) Swatches del ColorPicker + referencias @nombre desde cualquier estilo
editable QUÉ puede editarse por elemento (lista blanca de campos del panel, con granularidad de sub-control) Gobierna el panel: campos ofrecidos, secciones visibles
styles El estilo base COMPLETO de cada elemento y sus variantes/sub-partes (§2–§3) Vars CSS + reglas por elemento en el lienzo; el «↺» del panel vuelve aquí
doc El lienzo del documento: medida, fondo, ritmo entre bloques, selección, placeholder, caret Vars CSS en la raíz del documento

Entrada: schemeStyle acepta nombre registrado ('default', registerPalabrasScheme), objeto PalabrasScheme inline, o string en notación compacta (§9 — se distingue de un nombre porque contiene =). Un esquema parcial rellena desde el default.


2. Dimensiones transversales (todo elemento las admite)

El estilo de CUALQUIER elemento puede declarar:

Grupo Propiedades Notas
Tipografía font (familia) · size · weight · style (normal/italic) · lh (interlineado) · ls (tracking) · ws (word-spacing) · transform (none/upper/lower/capitalize) · wrap (wrap/nowrap/balance/pretty) · tabular (cifras tabulares) El mismo vocabulario que el mega-control Tipografía del panel — esquema y panel hablan igual
Color color · bg Valores: hex/rgb/nombre CSS o @nombre de la paleta
Espaciado margin · pad — 1/2/4 valores CSS-like (pad=12, pad=8 12, pad=8 12 4 12) o por lado (margin-top=24) px por defecto; admite unidades (1.5em)
Borde border (3 solid #f00 o @acento) · por lado (border-left=…) · radius (1 o 4 valores)
Otros shadow (token sm/md/lg o valor CSS) · opacity · measure (max-width del texto, en bloques de texto)

3. Inventario por elemento (transversales + ESPECÍFICAS de cada uno)

Elemento / sub-parte Específicas además de §2 Ejemplo (notación §7)
doc (el lienzo) measure (ancho de línea del contenido, ej. 68ch) · gap (ritmo vertical entre bloques) · bg (fondo del documento) · pad (margen interior del lienzo) · selection (color/fondo del texto seleccionado) · caret (color) · placeholder (color/estilo del texto fantasma) doc{measure=68ch; gap=12; bg=#fff}
text (párrafo base) indent (sangría de primera línea) text{font=Georgia; lh=1.6; color=@tinta}
h1…h6 margin-top/margin-bottom propios (el ritmo de cada nivel) h1{weight=700; lh=1.2; margin-top=24}
list indent (por nivel de anidado) · item-gap (espacio entre items) · marker (carácter/estilo del bullet) · marker-color list{indent=24; item-gap=4; marker-color=@tinta3}
list.ordered numbering default (1/a/A/i/I) list.ordered{numbering=1}
list.check box (tamaño del checkbox) · box-color · box-radius · checked-color · tachado del texto marcado sí/no list.check{box=16; box-color=@acento}
quote border-left (la barra: grosor/estilo/color) — o el lado que sea · comillas decorativas sí/no quote{border-left=3 solid @acento; pad=12}
quote.cite (atribución/fuente) tipografía y color propios quote.cite{style=italic; size=14; color=@tinta3}
code (bloque) tab-size · wrap (ajustar vs scroll) · lines (numeración de líneas on/off + color) code{font=Courier; bg=#f6f8fa; radius=6; wrap=off}
code.tokens el tema del highlighter: color por token — keyword · string · number · comment · function · tag · attr · punct (los que emite el tokenizador) code.tokens{keyword=#cf222e; string=#0a3069; comment=#6e7781}
callout (base) title como sub-parte (tipografía propia) · icono sí/no ⚠️ callout{radius=8; pad=12}
callout.{intent} (×6: neutral/affirm/fulfill/risk/threat/loss) CADA intent declara su bg · border · color · title-color — aquí vive el mapa intent→píxeles callout.risk{bg=#fff1f0; border-left=3 solid #cf222e}
image shadow · radius · border · bg (letterbox del fit) · fit default (fill/fit/crop/tile) · full-width default · los AJUSTES del componente ImageAdjustments del framework (el panel lo COMPONE — regla #1): brightness · contrast · saturation · temperature · hue · blur · grayscale · sepia — el esquema declara sus valores por defecto (el punto de partida del documento; el usuario ajusta por bloque encima) y editable gobierna cuáles se ofrecen (granular adjustments.*, §6) ⚠️ image{radius=8; shadow=sm; brightness=1.05; saturation=1.1}
image.caption tipografía/color/align propios del pie image.caption{size=13; color=@tinta3; align=center}
table border exterior · border-inner (retícula interior) · radius table{border=1 solid #d0d7de; radius=6}
table.cell pad · valign default table.cell{pad=8 10}
table.header (fila Y columna de cabecera) bg · color · weight — su look propio table.header{bg=#f6f8fa; weight=600}
table.zebra fondo alterno de filas (off si no se declara) table.zebra{bg=#fafbfc}
columns gap default · rule (regla divisoria: grosor/estilo/color, off por defecto) · col-pad columns{gap=24; rule=1 solid #eee}
divider height (grosor) · style (solid/dashed/dotted) · color · gap (espacio vertical) · width (100% o menos, centrado) divider{height=1; color=#d0d7de; gap=24}
link (inline) color · underline (grosor offset, o off) · hover-color · visited-color ⚠️ link{color=@acento; underline=1 2}
mark.* (inline) mark.bold (el peso concreto) · mark.code (código inline: bg/color/radius/pad/tamaño relativo) · mark.highlight (fondo default del subrayador) · mark.underline/mark.strike (estilo/grosor/color) · mark.sub/mark.sup (tamaño/offset) mark.bold=700; mark.code{bg=#f6f8fa; radius=4}
embed (y bloques de PLUGIN) clave abierta: cualquier bloque registrado puede recibir estilo por su type (transversales §2 + aspect para embed) embed{radius=8; aspect=16/9}

⚠️ marcan las dudas concretas: icono de callout (¿lo gobierna el esquema o el chrome?), visited/hover de links (¿v1 o sobra?).


4. fonts — las fuentes admitidas

fonts{
  Georgia = Georgia, 'Times New Roman', serif;
  Helvetica = 'Helvetica Neue', Arial, sans-serif @ 400,700;   ← pesos disponibles
  Courier = 'Courier New', monospace
}
  • Cada entrada: label = stack CSS (+ opcional @ pesos). En objeto: los PalabrasFontGroup de hoy (con grupos con título).
  • Proyectan al dropdown Familia; la primera es la base si text{font} no dice otra.
  • El grupo «Tema» (var(--font-family-*)) DEJA de existir por defecto — doctrina backoffice. Una app puede añadirlo explícitamente si quiere.

5. palette — colores con nombre, referenciables

palette{tinta=#1f2328; tinta2=#57606a; acento=#0969da; papel=#ffffff; peligro=#cf222e}
  • Proyecta a los swatches del picker (el nombre = tooltip/aria del swatch).
  • Cualquier valor de color del esquema puede referenciar @nombre — cambiar acento re-tinta todo lo que lo usa. (En objeto: palette: [{name, value}]; se admite el string pelado sin nombre.)

6. editable — qué se puede editar (gobierna el panel)

editable{
  image = src, alt, adjustments.saturation, adjustments.blur;  ← imagen: SOLO esto
  heading = level, children, align;
  paragraph = children, typography.size, color;   ← granular: sub-claves con punto
  divider =                                       ← vacío: panel solo-lectura del tipo
}
  • Lista blanca por tipo: tipo no declarado → panel completo (como hoy).
  • Granularidad con punto: typography.size, typography.tracking… apagan sub-controles individuales del mega-control Tipografía (typography a secas = todo él); adjustments.saturation, adjustments.blur… hacen lo mismo con los sliders del componente ImageAdjustments (adjustments a secas = los 8).
  • Tipo declarado VACÍO = nada editable (el panel muestra el tipo pero sin campos).

6b. marks + blocks — qué puede APLICAR/INSERTAR (gobiernan bubble y slash)

editable gobierna el PANEL. Las otras dos superficies tienen su propia lista blanca top-level (misma doctrina «sin declarar = todo permitido»):

marks  = bold, italic, underline, strike, code, sub, sup, fontSize, fontFamily;
blocks = paragraph, heading, list, quote;   ← el slash / «+» solo ofrecen estos
  • marks — marcas inline que el usuario puede aplicar (bubble + atajos): booleanas (bold/italic/underline/strike/code/sub/sup) y el tamaño/familia paramétricos (fontSize/fontFamily). Sin declarar → todas; declarada VACÍA → ninguna. Los VALORES de familia/tamaño salen de fonts/sizes. (El COLOR se gobierna aparte, vía editable + la paleta — no por esta lista.)
  • blocks — tipos de bloque insertables (slash + menús «+»/Insertar), en vocabulario canónico (heading cubre h1–h6, list las tres listas). Sin declarar → todos los registrados; declarada VACÍA → ninguno. El slash se filtra en el MOTOR (opt insertableBlocks del provider) para que el índice de teclado no derive; los menús eidos filtran vía schemeAllowsBlock sobre insertableBlockTypes(scheme).

7. El panel: las TRES clases de propiedades

Formalización (hoy es convención por consts compartidas; pasa a contrato):

Clase Tab Qué es Ejemplos
Específicas semánticas del elemento Contenido Qué ES y qué dice el bloque level/texto/enlace (heading) · src/alt/caption (imagen) · intent/título (callout) · kind/start/numbering (lista) · cabeceras (tabla) · cite (cita) · language (código)
Comunes (transversales §2) Diseño — primero, secciones idénticas en todos los tipos El estilo que cualquier bloque admite Disposición (alineación/márgenes/relleno) · Tipografía (el mega-control) · Colores (texto/fondo)
Específicas de diseño del elemento Diseño — después, sección propia del tipo El estilo que SOLO ese elemento tiene «Imagen»: fit + los 8 ajustes (ImageAdjustments) + radius/sombra · «Tabla»: bordes/cabecera/zebra · «Separador»: grosor/estilo/color · «Columnas»: gap/regla
(Avanzadas) Avanzado Ancla + flags de export (spec de interacción L4) + extensiones (§8) anchor · hideOnExport (cuando el motor la tenga)

Reglas: las secciones comunes son LAS MISMAS consts para todos (nunca se duplican por tipo); la sección específica de diseño lleva el NOMBRE del elemento; editable (§6) y la notación (§9) respetan esta clasificación tal cual (las claves granulares apuntan a campos de cualquiera de las tres clases).


8. Extensión de los paneles — quitar y añadir (programador y diseñador)

Dos actores, dos niveles, orden de aplicación fijo:

catálogo del REGISTRY (app)  →  −editable (esquema)  →  +fields (esquema)

8a. Nivel APP — el programador (código, boot)

// Panel COMPLETO de un tipo nuevo (p. ej. el bloque embed, que es plugin):
registerPalabrasPanel({ type: 'embed', label: '…', contenido: […], diseño: […] });

// Modificar el panel de un tipo existente:
extendPalabrasPanel('heading', {
  contenido: {
    add: [{ key: 'cta', type: 'text', label: '…' }],  // añadir campo…
    after: 'level',                                    // …con posición (o append)
    remove: ['link']                                   // quitar del catálogo de la app
  },
  diseño: { addSection: { title: '…', fields: […] } },
  avanzado: { add: […] }
});
  • Mismo patrón que los plugins de bloque (defaultPalabrasSchema.register) y los esquemas (registerPalabrasScheme): registro en boot, sin tocar el core.
  • remove a nivel app ≠ editable del esquema: el programador define el CATÁLOGO de su app (global); el diseñador restringe POR DOCUMENTO.
  • ⚠️ Field-types custom (registrar un CONTROL nuevo — componente Svelte — bajo un type propio, para campos que los 12 tipos básicos no cubren): propuesto para v2, la puerta queda declarada.

8b. Nivel DOCUMENTO — el diseñador (el esquema, sin código)

  • Quitar: editable (§6) — lista blanca por tipo, granular.
  • Añadir: fields — campos declarativos con los tipos básicos (text/textarea/number/toggle/select/color/slider/length), sobre una prop libre del bloque. El motor YA persiste props desconocidas (updateBlockAtPath conserva y exporta), así que un campo declarado en el esquema funciona end-to-end sin una línea de código:
heading.fields{
  cta{type=text; label=Texto del CTA; tab=avanzado};
  urgente{type=toggle; label=Urgente; tab=contenido}
}
  • tab = contenido/diseño/avanzado (default avanzado); options para select; min/max/step para number/slider. En objeto: scheme.fields = { heading: [PalabrasFieldDef…] }.

9. La notación compacta (gramática) — mapeo 1:1 con el objeto

esquema  := decl (';' decl)*
decl     := escala | bloque | asignación
escala   := elemento '=' número                    → sizes.{elemento}     (h1=32)
bloque   := ruta '{' (prop | bloque) (';' …)* '}'  → styles.{ruta}        (code{padding=4})
prop     := nombre '=' valor                       — nombres cortos de §2/§3
ruta     := elemento ('.' subparte)*               — callout.risk · code.tokens · mark.bold
asignación := ruta '=' valor                       — atajo de un solo valor (mark.bold=700)
valor    := número | dimensión | color | '@'paleta | ident | valor ' ' valor
  • ; separa; los saltos de línea son equivalentes a ;. Espacios libres.
  • Números pelados = px (salvo props adimensionales: lh, weight, opacity).
  • fonts{…} palette{…} editable{…} doc{…} son bloques con las formas de §4–§6.
  • El parser emite EXACTAMENTE un PalabrasScheme — todo lo expresable en notación es expresable en objeto y viceversa. Errores de parse: se ignora la declaración inválida y se avisa por consola dev (el documento nunca revienta por un typo).

Ejemplo completo (el «default» expresado en notación)

palette{tinta=#1f2328; tinta2=#57606a; tinta3=#8c959f; acento=#0969da; papel=#ffffff}
fonts{Georgia=Georgia,'Times New Roman',serif; Helvetica='Helvetica Neue',Arial,sans-serif; Courier='Courier New',monospace}
doc{measure=68ch; gap=12; bg=@papel}
text=17; h1=32; h2=26; h3=22; h4=19; h5=17; h6=16; quote=18; code=14
text{font=Georgia; lh=1.6; color=@tinta}
h1{weight=700; lh=1.2}  h2{weight=700; lh=1.25}  h3{weight=600; lh=1.3}
quote{color=@tinta2; border-left=3 solid #d0d7de; pad=12}
quote.cite{style=italic; size=14; color=@tinta3}
code{font=Courier; bg=#f6f8fa; radius=6; pad=12}
code.tokens{keyword=#cf222e; string=#0a3069; comment=#6e7781; number=#0550ae}
callout{radius=8; pad=12}
callout.risk{bg=#fff1f0; border-left=3 solid #cf222e}
callout.fulfill{bg=#dafbe1; border-left=3 solid #1a7f37}
list{indent=24; item-gap=4}
image{radius=6}  image.caption{size=13; color=@tinta3; align=center}
table{border=1 solid #d0d7de}  table.cell{pad=8 10}  table.header{bg=#f6f8fa; weight=600}
divider{height=1; color=#d0d7de; gap=24}
link{color=@acento; underline=1 2}
mark.bold=700  mark.code{bg=#f6f8fa; radius=4}

10. Qué genera cada pieza (proyección)

Pieza Proyección
styles.{el} transversales Vars --palabras-scheme-{el}-{prop} en el doc root + regla por elemento en palabras.css — TODAS las props declaradas, no un subconjunto
styles de sub-partes/variantes Selectores existentes del render: callout.{intent} → [data-palabras-callout-intent='…'] · code.tokens.* → los spans de token del highlighter · table.header → th · image.caption → figcaption · mark.* → los tags/spans de marks
doc Vars en la raíz: measure → max-width del content; gap → margen entre bloques; selection → ::selection; placeholder/caret → sus reglas
sizes --palabras-scheme-{el}-size (+ el valor «heredado» del panel)
palette Presets del picker + resolución de @nombre en el parser/resolver (a valor concreto en las vars)
fonts Dropdown Familia (grupos) + primera fuente = base implícita
editable Filtrado de campos/secciones del panel + sub-controles de Tipografía y de ImageAdjustments (adjustments.*)
styles.image (ajustes) Defaults de los 8 sliders de ImageAdjustments en el panel + el filter CSS base del elemento imagen (el override por bloque compone encima)

Los overrides por bloque (block.visual) SIEMPRE ganan al esquema; el «↺» los limpia y el bloque vuelve al esquema. Cambiar de esquema re-tinta todo lo no-overrideado.


11. Estado actual vs esta spec (honesto)

Ya existe (base de 30a67533): el tipo con fonts/sizes/palette/editable/styles, nombre-u-objeto con relleno parcial, registro, doctrina backoffice (aislamiento del host), proyección de text/h1–h6/quote/code/callout-base, paleta→picker, whitelist de panel (sin granularidad), muerte del proxy getComputedStyle.

Falta TODO lo demás de esta spec: la notación compacta (el parser), doc (medida/ ritmo/selección/placeholder/caret), paleta con nombre + @referencias, granularidad de editable (sub-controles), y los estilos de list (+ordered/check), quote.cite, code.tokens, callout por intent, image(+caption), table (+cell/header/zebra), columns(+rule), divider, link, marks y bloques de plugin.

12. Ratificación (lo que es tuyo)

  1. El inventario §3: tacha dimensiones que sobren, añade las que falten.
  2. Los ⚠️: icono del callout en el esquema sí/no · hover/visited de link v1 sí/no.
  3. ⚠️ Ajustes de imagen — mapeo motor↔componente: el componente canónico (ImageAdjustments) habla saturation / hue / temperature; el modelo del MOTOR (ImageBlock) hoy tiene saturate / hueRotate y no tiene temperature. Propuesta: el panel COMPONE ImageAdjustments (vocabulario del componente), mapeado a las props existentes del modelo, y temperature se AÑADE al modelo del motor (validador + render con su aproximación CSS) para paridad completa. ¿OK, o temperature fuera del modelo en v1?
  4. Las tres clases del panel (§7): la asignación Contenido/Diseño-comunes/ Diseño-específicas/Avanzado — ¿así?
  5. Extensión de paneles (§8): registerPalabrasPanel + extendPalabrasPanel (nivel app) y fields declarativos del esquema (nivel documento, sin código) — ¿ambos niveles sí? · ⚠️ ¿la posición after/before al añadir importa, o basta append? · ⚠️ field-types CUSTOM (control propio como componente) → propuesto v2.
  6. Los nombres cortos de la notación (§2: lh/ls/ws/pad/bg…) — ¿te valen así?
  7. @nombre para referenciar la paleta — ¿sí?
  8. Con tu firma: se implementa TODO (tipo ampliado + parser + proyección completa + las tres clases + el mecanismo de extensión + granularidad del panel — incluido componer ImageAdjustments en el panel de imagen), con la demo ejercitando cada pieza y verificación por valores.

Powered by TurnKey Linux.