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.
|
|
4 months ago | |
|---|---|---|
| .. | ||
| components | 4 months ago | |
| engine | 4 months ago | |
| extensions | 4 months ago | |
| test | 5 months ago | |
| ARCHITECTURE_PROPOSAL.md | 4 months ago | |
| README.md | 4 months ago | |
| exports.ts | 4 months ago | |
| index.ts | 5 months ago | |
| types.ts | 4 months ago | |
| words-content.svelte.test.ts | 5 months ago | |
| words-provider.svelte.test.ts | 4 months ago | |
| words-provider.svelte.ts | 4 months ago | |
README.md
Words Soma
Soma sera el propietario del motor headless de Words.
Este directorio esta creado como staging documental. La implementacion no debe empezar hasta cerrar contrato Morfo y alcance V1.
Estado
2026-05-24:
- Alcance V1 firmado.
- Slice 4 de motor puro implementado en
engine/. - Slice 5 implementa el primer Provider Soma real y el puente
contenteditable. - Slice 6 cierra wrapper Eidos, receta visual, pack Sema y demo UIX de 6 tabs.
- Tests focalizados en
engine/engine.test.ts,words-provider.svelte.test.tsywords-content.svelte.test.ts.
Responsabilidad
- Documento canonico JSON.
- Estado editable/readonly/disabled/focused/empty/invalid.
- Seleccion DOM <-> seleccion de documento.
- Pipeline de input:
beforeinput,input, composition/IME, paste, drop. - Comandos.
- History.
- Normalizacion de documento.
- Plugins internos.
- Serializacion JSON/HTML/Markdown.
- Integracion con logger, clipboard, format y events de ActiveUIX.
Submodulos propuestos
words/
README.md
types.ts
index.ts
exports.ts
words-provider.svelte.ts
words-provider.svelte.test.ts
engine/
document.ts
dom.ts
dom-selection.ts
selection.ts
commands.ts
code-highlight.ts
history.ts
input.ts
normalize.ts
operations.ts
render.ts
clipboard.ts
serialize-json.ts
serialize-html.ts
serialize-markdown.ts
logger.ts
plugins/
basic.ts
marks.ts
blocks.ts
lists.ts
links.ts
history.ts
markdown.ts
components/
words.svelte
words-content.svelte
words-placeholder.svelte
words-toolbar.svelte
words-toolbar-group.svelte
words-command-button.svelte
words-bubble-menu.svelte
words-slash-menu.svelte
words-link-editor.svelte
words-bubble-menu.svelte
words-link-popover.svelte
words-hidden-input.svelte
words-status.svelte
Modelo minimo V1
type WordsDocument = {
version: 1;
children: WordsBlock[];
};
type WordsBlock =
| { type: 'paragraph'; children: WordsInline[] }
| { type: 'heading'; level: 1 | 2 | 3; children: WordsInline[] }
| { type: 'quote'; children: WordsInline[] }
| { type: 'code'; language?: string; children: WordsInline[] }
| { type: 'list'; kind: 'ordered' | 'unordered' | 'check'; children: WordsListItem[] }
| { type: 'table'; striped?: boolean; compact?: boolean; children: WordsTableRow[] };
type WordsTableRow = { type: 'table-row'; children: WordsTableCell[] };
type WordsTableCell = {
type: 'table-cell';
header?: boolean;
textAlign?: 'left' | 'center' | 'right' | 'justify';
verticalAlign?: 'top' | 'middle' | 'bottom';
tone?: 'default' | 'muted' | 'accent';
children: WordsInline[];
};
type WordsInline =
| { type: 'text'; text: string; marks?: WordsMark[] }
| { type: 'link'; href: string; title?: string; children: WordsInline[] };
type WordsMark = 'bold' | 'italic' | 'underline' | 'strike' | 'code';
Comandos V1
focusblurundoredotoggleMarksetBlocktoggleListtoggleCheckIteminsertTextapplyMarkdownShortcutinsertLineBreakinsertTableinsertTableRowinsertTableColumndeleteTableRowdeleteTableColumnmoveTableCellinsertLinkunlinksetCodeLanguagecommitSlashCommandclearFormattingreplaceDocumentimportContentexportContent
Slice implementado
| Archivo | Estado |
|---|---|
engine/document.ts |
Modelo JSON V1, factories y helpers de marcas/texto/tablas. |
engine/dom.ts |
Contrato data-words-*, path encode/decode y selection targets. |
engine/dom-selection.ts |
Adaptador Selection API para leer/restaurar seleccion real del DOM. |
engine/normalize.ts |
Normaliza documentos, bloques, inline nodes, marks y links inseguros. |
engine/selection.ts |
Modelo de seleccion path + offset, rangos y text leaves. |
engine/path.ts |
Navegacion de arbol, paths profundos, replace/set helpers. |
engine/operations.ts |
Rangos, insert, marks, blocks, listas, tablas, links y teclado. |
engine/commands.ts |
Dispatcher tipado de comandos V1 parciales. |
engine/code-highlight.ts |
Tokenizer ligero para pintar code blocks en runtime sin dependencia. |
engine/history.ts |
Undo/redo por snapshots con batching y limite de stack. |
engine/input.ts |
Traduccion de beforeinput a comandos o accion de history. |
engine/render.ts |
Descriptor de render headless con tags y attrs para contenteditable. |
engine/serialize-json.ts |
Parse normalizado y serialize sin mutacion implicita. |
engine/serialize-html.ts |
Export HTML limpio sin data-words-* ni zero-width placeholders. |
engine/serialize-text.ts |
Import seguro de texto plano a parrafos canonicos. |
engine/engine.test.ts |
Cobertura de normalizacion, listas, rangos, links, history y teclado. |
Correcciones cerradas en slice 5
src/uix/morfo/components/words.tsdeclara contrato V1: Provider, Content, Placeholder, Toolbar, ToolbarGroup, CommandButton, LinkEditor, HiddenInput y Status.- Morfo incluye
data-focused,data-empty,data-dirty,data-disabled,data-readonly,data-required,data-invalid,data-composing,data-selection,data-block,data-command,data-stateydata-status. - Eventos declarados con familias Sema validas:
contact-focus,commit-content,commit-format,commit-link,commit-unlink,commit-set-replace,commit-slash-command,commit-check,commit-history,commit-clearysignal-invalid-input. - Provider Soma mantiene documento, seleccion, history, active marks,
focused,dirty,disabled,readonly,required,invalide integracion conFieldProvider. Contentrenderiza HTML seguro desde descriptores propios del motor, escuchabeforeinput,paste, shortcuts de teclado y composition events.HiddenInputserializa JSON canonico para formularios.CommandButtonejecuta comandos de formato, listas, bloques, undo, redo, clear y commit; expone estado activo/deshabilitado por Morfo.LinkEditoraplicainsertLinkyunlinksobre la seleccion estable del documento, exponeselectedLink,canUnlink,captureSelectiony feedback de URL invalida.FindReplacemantiene query, matches, regex validada, navegacion, find-in-selection, replace-current, replace-all yreplacePreserveCasepara adaptar el replacement al casing del match (word,Word,WORD).- Paste HTML sin texto plano se rechaza y se reporta por el logger UIX como
soma.words. - IME/composition queda conectado al Provider:
compositionendaplica el comando final. deleteWord*ydeleteLine*tienen comandos propios; losbeforeinputno soportados no se cancelan cuando el motor no los posee.- Las listas desindentan un nivel con Enter en item vacio anidado, salen de la
lista con Enter en item vacio raiz y Backspace al inicio del primer item;
partir y unir items conserva indentacion, y
setBlocksobre listas produce un bloque por item. - Las checklist renderizan un marcador interactivo; click sobre el marcador o
Ctrl/Cmd+Enteren el item actual alternacheckedmediante el comandotoggleCheckItemy emitecommit-check. - Las citas continuan como cita al pulsar Enter con contenido; Enter sobre una cita vacia sale a un parrafo normal.
- Los botones de lista se activan por
listKind, no por cualquier bloquelist. - Los links anidados se aplanan en normalizacion y
insertLinkno anida dentro de un link existente. exportContentexpone JSON, texto plano y HTML limpio;importContentacepta JSON, texto plano y HTML seguro conservador.exportContenttambien expone Markdown V1 sin dependencia externa;importContentacepta Markdown conservador para headings, quote, listas, links y marcas inline basicas.- Los bloques
codepreservanlanguagenormalizado en JSON/HTML/Markdown; el Provider exponecurrentCodeLanguageysetCodeLanguage()para toolbars o selectores externos sin acoplar Soma a una UI concreta. - El render runtime de bloques
codetokeniza lenguajes comunes (ts/js,css,html,json,bash,python) condata-words-code-token, mientrasserializeWordsHtmlmantiene un export limpio sin spans de highlighting. - La lectura de
Selection APIcalcula offsets dentro de descendientes anidados, de modo que seleccionar texto dentro demarko tokens de codigo no rompe el path canonico. Words.Contentrenderiza el HTML seguro del motor como contenido inicial de Svelte y usa un efecto posterior solo para restaurar la seleccion cuando el editor mantenia foco interno. La rama gestionada no monta snippets internos, de modo que wrappers Eidos/demos no apagan accidentalmente el render del documento. Tiene cobertura montando el componente Svelte real.WordsProvidersincroniza cambios externos devalueyselectionsin remount, normaliza el documento entrante y no emite callbacks de edicion de usuario para cambios controlados desde fuera.- El Provider usa
batch: 'typing'debeforeinputpara agrupar escritura, borrado e IME contiguos en una sola entrada de undo; seleccion externa, blur, commit, undo/redo o comandos de formato cierran el batch. - El espacio tras un marcador inicial aplica atajos Markdown de escritura:
#,##,###,>,-,*,1.,1),- [ ],- [x]y fences de tres backticks con lenguaje opcional se transforman en bloques canonicos sin insertar el espacio. - El mismo trigger de espacio aplica atajos Markdown inline al final del texto
actual:
**bold**,*italic*,_italic_,~~strike~~,`code`y[label](url)se convierten en marcas o links canonicos conservando el espacio para seguir escribiendo. - Las URLs desnudas con protocolo permitido (
http(s),mailto,tel) se autoconvierten a links al escribir el espacio siguiente; puntuacion final comun queda fuera del enlace. - Pegar una unica URL segura crea un link directamente. Si hay texto
seleccionado, la URL pegada se usa como
hrefy el texto seleccionado se conserva como etiqueta del enlace. - La seleccion de rango se sincroniza desde
selectionchange, teclado, puntero yselect;CommandButtonpreserva la seleccion del editor antes de aplicar comandos de toolbar sobre el rango seleccionado. - La resincronizacion de la misma seleccion colapsada conserva
activeMarks. Esto permite activar una marca desde toolbar y seguir escribiendo texto con esa marca, tambien dentro de celdas de tabla. Ctrl+AusaselectAll()dentro del Provider para seleccionar todo el documento en el modelo Soma y restaurar la Selection API condata-selection="range".BubbleMenucalcula su posicion desde el rectangulo deSelection API, exponedata-open,data-positioned,data-sideydata-selection, y evita que elpointerdowndel toolbar contextual colapse la seleccion antes de ejecutar comandos.SlashMenudetecta/queryen selecciones colapsadas fuera de code blocks, filtra comandos de bloque/lista, mantiene indice activo, permite navegar con flechas/Home/End y aplicaEnter/Tabborrando el rango del slash antes de ejecutar el comando en una sola entrada de history.- Las tablas tienen nodo canonico
type: 'table'con filas y celdas editables por paths profundos ([block,row,cell,text]). El comandoinsertTableinserta una tabla 2x2 y un parrafo posterior, el render DOM emitedata-words-node="table|table-row|table-cell", y HTML preserva tablas con headers, align, vertical align, tone, striped y compact. Markdown import/export conserva pipe tables simples.insertTableRow,insertTableColumn,deleteTableRow,deleteTableColumn,toggleTableHeaderRow,toggleTableHeaderColumn,setTableCellTone,setTableCellVerticalAlign,toggleTableStriped,toggleTableCompactymoveTableCellpermiten editar estructura y formato desde la celda activa;Tabavanza entre celdas y crea una fila nueva al llegar al final. - La sincronizacion de seleccion distingue
selectionchangeautomatico de senales explicitas (pointerup, teclado, input). Un colapso transitorio durante el cruce del contenido alBubbleMenuno sobrescribe el rango del modelo; los botones restauran la seleccion DOM antes de aplicar comandos. Words.Contentintercepta clicks sobre links en modo editable para evitar navegacion accidental fuera del editor; en modoreadonlyno cancela el comportamiento nativo.Words.LinkEditorrecibe el formulario real como target decommit-linkycommit-unlink, de forma que Sema puede proyectar los eventos sobre la parte declarada en Morfo.
Correcciones cerradas en slice 2
- Paths de profundidad variable para parrafos, listas y texto dentro de links.
getNodeAtPath,resolveTextAtPath,replaceInlineAtPathy helpers de hijos inline.- Operaciones de rango multi-inline y multi-bloque para reemplazo de texto.
- Edicion real dentro de list items.
insertParagraph,insertLineBreak,deleteBackward,deleteForwardydeleteRange.unlinkdesde el texto interior de un link.- Links relativos internos (
/ruta,?query,#anchor) permitidos. - History con
batchHistoryCommandsy limite por defecto de 200 snapshots. serializeWordsJsonya no normaliza ni muta silenciosamente.
Correcciones cerradas en slice 3
- Contrato DOM estable basado en
data-words-nodeydata-words-path. - Encode/decode de paths para no depender de indices inventados por el DOM.
- Mapping
WordsSelection<-> targets DOM serializables y clampados. - Render headless de bloques, listas, items, links y text leaves.
- Texto vacio renderizable mediante placeholder zero-width y
data-words-empty-text. - Traduccion de
beforeinputpara texto, Enter, line break, delete, formato, history undo/redo y composition guard.
Correcciones cerradas en slice 4
- Lectura de
Selection APIdesde DOM real mediantedata-words-path. - Soporte de rangos que cruzan texto normal y texto dentro de link.
- Restauracion de
Selection APIdesde targets DOM codificados. - Clamp de offsets DOM contra texto renderizado y placeholders vacios.
- Primitivas de paste seguro: texto plano con normalizacion de saltos de linea, HTML ignorado hasta tener importador seguro.
- Primitivas IME/composition: begin, update y commit final como comando explicito.
Pendiente despues de Provider
- Importador HTML avanzado para imagenes y HTML arbitrario completo.
- Drop/clipboard avanzado a traves de servicios ActiveUIX.
- Popover flotante de links resuelto en Eidos como
Words.ToolPopoverlocal para no depender delSoma/Popovercompartido mientras ese componente tiene bloqueo propio documentado ensrc/uix/PENDIENTES.md. Soma mantiene la seleccion estable y la validacion/comando deLinkEditor. - Cobertura de componentes Svelte renderizados para el resto de partes compuestas.
- Eidos recipe y demo bajo
web/routes/uix/components/wordsya existen; queda auditoria visual iterativa conforme crezcan BubbleMenu/import/export.
Reglas de seguridad
- HTML importado debe validarse y normalizarse antes de entrar al documento.
- Links solo con protocolos permitidos.
javascript:,data:y protocolos desconocidos deben rechazarse o convertirse en error logueado.- No aceptar nodos desconocidos silenciosamente.
- Paste desde HTML debe tener fallback a texto plano si la importacion falla.
Eventos
Soma dispara runtime.trigger(...) solo para eventos declarados en Morfo.
Cambios internos frecuentes deben agruparse y convertirse en commit-content
cuando proceda.